diff --git a/.github/ISSUE_TEMPLATE/checklist-for-release.md b/.github/ISSUE_TEMPLATE/checklist-for-release.md index 8b5872891..af802cab3 100644 --- a/.github/ISSUE_TEMPLATE/checklist-for-release.md +++ b/.github/ISSUE_TEMPLATE/checklist-for-release.md @@ -9,7 +9,6 @@ assignees: doegox, iceman1001 # Checklist -- [ ] CHANGELOG.md: add title: `## [releasename][YYYY-MM-DD]` - [ ] `make style` - [ ] `make miscchecks` - [ ] `make clean; make client CC=clang CXX=clang++ LD=clang++` on recent Debian or Ubuntu @@ -44,14 +43,24 @@ Run `tools/release_tests.sh` on: # creating release -- [ ] `make release RELEASE_NAME="ice awesome"` - - last line of output, gives you next command to run. +- [ ] CHANGELOG.md: add title: `## [myreleasename][YYYY-MM-DD]` +- [ ] `make release RELEASE_NAME="myreleasename"` + - last line of output gives you next command to run. - Sample: `git push && git push origin v4.12345` -- [ ] CHANGELOG.md: edit title to add version info: `## [releasename.4.12345][YYYY-MM-DD]` +- [ ] CHANGELOG.md: edit title to add version info: `## [myreleasename.4.12345][YYYY-MM-DD]` ## Step Github releases - [ ] Go to Github releases, create release based on the new created tag and publish + - Choose a tag: v4.12345 + - Target: master + - Set as the latest release + - Title: `proxmark3-v4.12345` + - Description: +``` +Release v4.12345 +Nickname "myreleasename" +``` ## Step Homebrew updates diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0ec56e054..07d1832ce 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,7 +41,7 @@ jobs: 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 lua5.2 sed libssl-dev + 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 liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev - name: Install Python dependencies run: | @@ -70,7 +70,7 @@ jobs: env: V: 1 PLATFORM_EXTRAS: BTADDON - run: make + run: make -j$((`nproc` + 1)) - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index a632a5e37..d7726f6e0 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -35,7 +35,7 @@ jobs: run: brew tap RfidResearchGroup/proxmark3 - name: Install dependencies - run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl + run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl gd continue-on-error: true - name: Install Python dependencies @@ -50,14 +50,12 @@ jobs: - name: Build env: V: 1 - run: make + run: make -j$((`sysctl -n hw.ncpu` + 1)) - name: Test run: make check macos-make-btaddon: - if: always() - needs: [macos-make] runs-on: macos-latest steps: @@ -77,7 +75,7 @@ jobs: run: brew tap RfidResearchGroup/proxmark3 - name: Install dependencies - run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl + run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl gd continue-on-error: true - name: Install Python dependencies @@ -93,14 +91,12 @@ jobs: env: V: 1 PLATFORM_EXTRAS: BTADDON - run: make + run: make -j$((`sysctl -n hw.ncpu` + 1)) - name: Test run: make check macos-cmake: - if: always() - needs: [macos-make, macos-make-btaddon] runs-on: macos-latest steps: @@ -120,7 +116,7 @@ jobs: run: brew tap RfidResearchGroup/proxmark3 - name: Install dependencies - run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl + run: brew install readline coreutils qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc openssl gd continue-on-error: true - name: Install Python dependencies @@ -144,7 +140,7 @@ jobs: - name: Build env: VERBOSE: 1 - run: make + run: make -j$((`sysctl -n hw.ncpu` + 1)) working-directory: client/build/ - name: Test diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index acce10e3a..969fe2335 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -26,7 +26,7 @@ jobs: 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 lua5.2 sed libssl-dev + 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 liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev - name: Install Python dependencies run: | @@ -41,14 +41,12 @@ jobs: - name: Build env: V: 1 - run: make + run: make -j$((`nproc` + 1)) - name: Test run: make check ubuntu-make-btaddon: - if: always() - needs: [ubuntu-make] runs-on: ubuntu-latest steps: @@ -58,7 +56,7 @@ jobs: 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 lua5.2 sed libssl-dev + 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 liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev - name: Install Python dependencies run: | @@ -74,14 +72,12 @@ jobs: env: V: 1 PLATFORM_EXTRAS: BTADDON - run: make + run: make -j$((`nproc` + 1)) - name: Test run: make check ubuntu-cmake: - if: always() - needs: [ubuntu-make, ubuntu-make-btaddon] runs-on: ubuntu-latest steps: @@ -91,7 +87,7 @@ jobs: 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 lua5.2 sed libssl-dev + 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 liblz4-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0 lua5.2 sed libssl-dev libgd-dev - name: Install Python dependencies run: | @@ -115,7 +111,7 @@ jobs: - name: Build env: VERBOSE: 1 - run: make + run: make -j$((`nproc` + 1)) working-directory: client/build/ - name: Test diff --git a/.github/workflows/uniq.yaml b/.github/workflows/uniq.yaml index 89f845483..a770a4fde 100644 --- a/.github/workflows/uniq.yaml +++ b/.github/workflows/uniq.yaml @@ -18,6 +18,5 @@ jobs: - name: check unique keys in dic files shell: bash run: | - find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | grep -v '#' | sort | uniq -i -d -c | sort -n -r " - if [[ $(find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | grep -v '#' | sort | uniq -i -d -c | sort -n -r " | grep -v "./" | wc -l) -gt 0 ]]; then exit 1; fi - + find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sort | uniq -i -d -c | sort -n -r " + if [[ $(find . -type f -name "*.dic" | xargs -I {} sh -c "echo {} && cat {} | sed 's/ *#.*//;/^$/d' | sort | uniq -i -d -c | sort -n -r " | grep -v '^\./' | wc -l) -gt 0 ]]; then exit 1; fi diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 77c3dc70c..77b160edf 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -54,7 +54,7 @@ jobs: run: make clean - name: Build - run: make V=1 + run: make -j $([System.Environment]::ProcessorCount + 1) V=1 - name: Test run: make check @@ -63,7 +63,7 @@ jobs: run: make clean - name: Build btaddon - run: make V=1 PLATFORM_EXTRAS=BTADDON + run: make -j $([System.Environment]::ProcessorCount + 1) V=1 PLATFORM_EXTRAS=BTADDON - name: Test btaddon run: make check @@ -84,7 +84,7 @@ jobs: working-directory: client/build/ - name: Build cmake - run: make VERBOSE=1 + run: make -j $([System.Environment]::ProcessorCount + 1) VERBOSE=1 working-directory: client/build/ - name: Test cmake @@ -110,6 +110,7 @@ jobs: gcc-arm-none-eabi libnewlib-dev libbz2-dev + liblz4-dev qtbase5-dev cmake libpython3-dev @@ -118,6 +119,7 @@ jobs: python3-dev libpython3-all-dev libssl-dev + libgd-dev - name: Install Python dependencies run: | @@ -140,7 +142,7 @@ jobs: run: make clean - name: Build - run: make V=1 + run: make -j$((`nproc` + 1)) V=1 - name: Test run: make check @@ -149,7 +151,7 @@ jobs: run: make clean - name: Build btaddon - run: make V=1 PLATFORM_EXTRAS=BTADDON + run: make -j$((`nproc` + 1)) V=1 PLATFORM_EXTRAS=BTADDON - name: Test btaddon run: make check @@ -170,7 +172,7 @@ jobs: working-directory: client/build/ - name: Build cmake - run: make VERBOSE=1 + run: make -j$((`nproc` + 1)) VERBOSE=1 working-directory: client/build/ - name: Test cmake diff --git a/.gitignore b/.gitignore index 8469c8449..9c42779fa 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,7 @@ luac tools/fpga_compress/fpga_compress tools/mfkey/mfkey32 tools/mfkey/mfkey64 +tools/mfkey/staticnested tools/nonce2key/nonce2key tools/cryptorf/cm tools/cryptorf/sm @@ -79,18 +80,9 @@ tools/jtag_openocd/openocd_configuration tools/mfd_aes_brute/mfd_aes_brute tools/mfd_aes_brute/mfd_multi_brute tools/mfd_aes_brute/brute_key +!tools/lena.bmp -fpga/* -!fpga/tests -!fpga/fpga_lf.bit -!fpga/fpga_hf.bit -!fpga/*.v -!fpga/Makefile -!fpga/fpga.ucf -!fpga/xst_lf.scr -!fpga/xst_hf.scr -!fpga/go.bat -!fpga/sim.tcl +fpga/__build* # offcial dumps folder dumps/* diff --git a/.vscode/setup.sh b/.vscode/setup.sh index 70672b924..a319ab056 100755 --- a/.vscode/setup.sh +++ b/.vscode/setup.sh @@ -39,7 +39,7 @@ function print_config { function setup_serial_port { if [ -z "$SerialPort" ]; then pm3list=$($VSCODEPATH/../pm3 --list 2>/dev/null) - #Use first port listed + #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" @@ -129,17 +129,17 @@ fi HOSTOS=$(uname | awk '{print toupper($0)}') if [ "$HOSTOS" = "LINUX" ]; then - if uname -a|grep -q Microsoft; then + if uname -a|grep -q Microsoft; then setup_wsl - else + else setup_linux - fi + 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 + echo >&2 "[!!] Host OS not recognized, abort: $HOSTOS" + exit 1 fi \ No newline at end of file diff --git a/AUTHORS.md b/AUTHORS.md index c1424a8a1..ac8358c20 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -6,7 +6,7 @@ Copyright (C) 2005-2007 Jonathan Westhues Since then, each contribution is under the copyright of its respective author. -A few releases were done by the Proxmark community between 2007 and March 2009 before using version control. +A few releases were done by the Proxmark3 community between 2007 and March 2009 before using version control. The last release which served as basis for version control, under SVN then migrated to Git, was the `20090306_ela` release by Edouard Lafargue. See the first commit of this repository. Therefore, only the following copyright notices are left untouched in the corresponding files: diff --git a/CHANGELOG.md b/CHANGELOG.md index 85593f3b2..ceaea4f56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,191 @@ -# Change Log + Change Log 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] + - Changed `prefs set client.debug` - now also toggles the client side APDU logging (@iceman1001) + - Changed `hf 14b sriwrbl` - now supports --force to override block checks. Thanks @gentilkiwi for the idea! (@iceman1001) + - Changed `hf 14b sriwrbl` - now tries to verify the write (@iceman1001) + - Changed `hf 14b apdu` - now supports tearoff (@iceman1001) + - Fixed `hf 14b raw` - fixed a potential write out of bounds. (@iceman1001) + - Fixed `hf 14b` commands got some serious overhaul and love. Better return values and packet handling (@iceman1001) + - Changed `hf waveshare` - image is automatically scaled and cropped to match panel size (@socram8888) + - Changed `hf waveshare` - image loading and processing is now done using [GDlib](https://github.com/libgd/libgd) (@socram8888) + - Added trace of sniffed SEOS traffic between reader and card (@iceman1001) + - Change `hf seos list` - print crc and annotate better (@iceman1001) + - Added troubleshooting entry - ARM architecture error (@francis2054) + - Fixed `lf pyramid sim` - wrong parameter handling (@iceman1001) + - Fixed bootloader - Ignore jitters when pressing the button (@wh201906) + +## [Steamboat Willie.4.17768][2024-01-03] + - Changed `mem spiffs dump -t` - now supports downloading direct into trace buffer (@hazardousvoltage) + - Changed `hf search` - added hints for all HF protocols we search for (@iceman1001) + - Changed `hf search` - added hint for iClass (@bettse) + - Changed `hf 14a apdu` - It now uses the FWI and SGFI values from the ATS to determine an appropriate timeout (@nvx) + - Added a thread to check when device comes online again. It will connect and update prompt (@iceman1001) + - Changed CLI offline prompt - replaces the old prompt when offline is detected (@iceman100) + - Changed `hf mf info` - it now uses found keys to try identify Gen2 cards (@iceman1001) + - Changed `hf 14a info` - disabled debug logging while performing the magic tests (@iceman1001) + - Changed `hf 14a info` - magic detection to better detect different GDM/USCUID configurations and improved Gen2/CUID detection when default keys are used (@nvx) + - Changed `hf_cardhopper` - standalone mode to allow running over the internal Proxmark3 USB-CDC serial port (@nvx) + - Fixed CLI prompt - Update connection type prompt after running `hw connect` (@wh201906) + - Changed `uart_receive()` - Check if TCP connection is lost (@wh201906) + - Change `data detectclock` - now tries all clocks if called w/o any params (@iceman1001) + - Changed `lf search -1u` - improved the autocorrelation detection for unknown signals (@iceman1001) + - Fixed `hf emrtd dump` stack smashing on device side (@iceman1001) + - Changed `dbprint` on device side to use max 200 chars strings. (@iceman1001) + - Fixed bootloader to correctly clear bss segment on start. Fixes USB serial number sometimes not working in the bootloader (@nvx) + - Changed `notes on downgrade attacks` - reworked the original text follow repo style (@iceman1001) + - Added `hf mf info` command and static encrypted nonce detection (@merlokk) + - Added Saflok KDF - generate MFC keys (@h1kari) + - Changed `lf fdx demod` - now raw bytes shows all data (@iceman1001) + - Changed `data num` - now can print reversed and inverse (@iceman1001) + - Fixed `hf mf sim -ix` never returning console (@datatags) + - Added standalone mode `hf_unisniff` combining 14a/14b/15 sniffing with extra flash save options (@hazardousvoltage) + - Added encryption and other SL3 functions for Mifare Plus - more to come (@team-orangeBlue) + - Fixed the corrupted data in real-time sampling (@wh201906) + - Added a slider in the plot window for navigation (@wh201906) + - Fixed client build bug with Python 3.12 (@wh201906) + - Fixed `ExchangeAPDUSC()` in `cmdsmartcard.c` to prevent client crash (@wh201906) + - Added real-time LF sampling support (@wh201906) + - Changed `hf 14a info` - now reads and prints QL88 sigantures (@iceman1001) + - Fixed `hf iclass dump` truncating AA2 blocks and improve reliability (@nvx) + - Added some info about UMC in "doc/magic_cards_notes.md" (@temskiy) + - Added `hw bootloader` to reboot to the bootloader mode (@wh201906) + +## [Faraday.4.17511][2023-11-13] + - Fixed Python support of `experimental_client_with_swig` (@doegox) + - Use proxmark3 as a generic smartcard reader with other software with `smart relay` (@gm3197) + - Added `tools\mfkeys\staticnested` - program to recover static nested keys (@iceman1001) + - Added `pm3_gen_dictionary.py` - python script to extract and save all keys from MFC dump files. (@iceman1001) + - Changed `hf mfu info` - now detect MIFARE Ultralight AES (@iceman1001) + - Changed `hf mf autopwn` - now supports multiple user supplied keys (@iceman1001) + - Added `hf mf gchpwd` command for change Gen4 GTU card access password (@merlokk) + - Added `--ms` option in `hw status` to specify the timeout of connection speed test (@wh201906) + - Added `hf mf ginfo` command for get info about Gen4 GTU configuration (@merlokk) + - Added support for loading Flipper PICOPASS dump files (@iceman1001) + - Fixed unknown chip identification (@jmichelp) + - Fixed `nfc decode` - now properly handles MFU dump files (@iceman1001) + - Added support for loading Flipper MCT/MFU dump files (@iceman1001) + - Changed `data bmap` - now default `-m` is 8 (@iceman1001) + - Added support for NTAG424 cards. (@dankar) + - Additional fixes to configcard code for keyroll mode based on nfc-iclass output (@Antiklesys) + - Changed lf sampling - improved the performance (@yah01) + - Added `bind` option for network connections to specify the outbound address and port (@wh201906) + - Changed `lf em 4x05 dump` - now supports the `--ns` nosave parameter (@iceman1001) + - Fixed some wrong synchronization waits in usb_write() to increase the communication speed (@wh201906) + - Added new command `data bmap` - breaks down a hexvalue to a binary template (@iceman1001) + - Changed aid_desfire.json - added entreis from the Metrodroid project (@iceman1001) + - Changed mad.json - added entries from the Metrodroid project (@iceman1001) + - Changed `hf iclass dump` - now allow no save of dumped data (@iceman1001) + - Changed `hf iclass calcnewkey` - Added calculations for old key elite and new key non elite (@Antiklesys) + - Changed the CLI prompt to show tcp/udp if used (@iceman1001) + - Changed `hw ping` - now shows transfer time (@doegox) + - Added `hf mf encodehid` - writes HID legacy credential to a empty MFC (@iceman1001) + - Added `hf iclass sam` - Added support for HID SAM Picopass communications (@iceman1001) + - Add support for quoted arguments in the CLI, allowing spaces in them which + are removed automatically (@jmichelp) + - Added UDP support on Windows (@wh201906) + - Added client communication timeout to preferences (@iceman1001) + - Added IPv6 support (@wh201906) + - Fixed `lf hid clone --bin` - now correctly handles sentinel bits (@iceman1001) + - Experimental UDP support in linux (@iceman1001, @wh201906) + - Changed CI scripts to speed up the builds (@wh201906) + - Changed the timeout of local TCP connections (@wh201906) + - Finalized implementation of configcard generation for keyroll when cardhelper is not present (@Antiklesys) + - Added documentation for compiling on iOS (@The-SamminAter) + - Fixed `hf iclass wrbl` - pagemap bit map for secured is now handled better (@iceman1001) + - Changed `hf iclass view/decrypt` to detect SIO lengths better and show if legacy credentials are encrypted (@nvx) + - Changed the json file formats for mfc, 14b, 15, legic, cryptorf, ndef (@iceman1001) + - Deprecated the EML file format when saving dump files. (@iceman1001) + - Added `sim014.bin` - new sim module firmware v4.42 with improved ISO7816 Protocol T0 support (@gentilkiwi) + - Added datasheet for sim module (@iceman1001) + - Changed `smart raw --timeout` - allows for a custom timeout (@iceman1001) + - Changed `lf t55 detectp1` - now also accepts 0xE039 Silicon Craft Tech as valid card (@iceman1001) + - Fixed `utils.lua` library function "convertdectohex" wasn't working (@iceman1001) + - Added `hf iclass creditepurse` command to allow crediting the epurse debit value (@nvx) + - Modified `hf iclass configcard` to only support online mode (@Antiklesys) + - Modified `hf iclass configcard` command to generate config cards without a cardhelper module by porting the contents of blocks 6 & 7 from nfc-iclass (@Antiklesys) + - Fixed `hf iclass info` command showing incorrectly in offline mode (@Antiklesys) + - The "doc/magic_cards_notes.md" file has been rebuilt, filled up, and so on. (@team-orangeBlue) + +## [Raccoon.4.17140][2023-09-09] + - Changed text and adjust pm3_test case for mf_aes_brute (@doegox) + - Fix CPPChecker warnings (@doegox) + - Fix TubmleWeed docker setup (@doegox) + - Added default keys (@ernestask) (@craftbyte) + - Fixed MFU authentication to send PACK correctly (@shallax) + - Fixed list output when line has 16 bytes (@piru) + - Changed AIDlist w new entries (@kormax) + - Swapped to OE1 for Shallow modulation on RDV4. Thanks to @gentilkiwi for testing (@d18c7db) + - Changed iClass SIO and Legacy credential detection to be more reliable (@nvx) + - Add hf_cardhopper standalone mode for long-distance relay attacks (@startrekdude) + - Added `hf iclass esetblk` - set iClass emulator memory block data (@nvx) + - Added cryptorf regressiontests (@iceman1001) + - Fixed `cryptorf/sma_multi` - local state used in multithread (@iceman1001) + - Changed `fpga_compress` - better deallocation of memory and closing of file handles (@iceman1001) + - Changed `hf search` - less swaps of fpga images on device side (@iceman1001) + - Changed `mkversion.sh` - now regenerates version_pm3.c (and consequently the binaries) only when needed (@doegox) + - Added `data atr` - a command to lookup ATR (@iceman1001) + - Fixed bug in ATR lookup fct, thanks @DidierA (@iceman1001) + - Updated ATR list (@iceman1001) + - Changed `mem load -m` - now correctly erase all allocated flash memory (@iceman1001) + - Fixed emulator quick dump to handle MFC Ev1 extra sectors (@iceman100) + - Removed some empty dump files (@iceman1001) + - Added a fct to choose fpga mode (@iceman1001) + - Changed `hf mf eload/gsave` - fast uploading to emulator memory (@iceman1001) + - Added empty dump files with keys for easy simulation (@iceman1001) + - Added `hf 15 view` - view ISO15693 dump files (@iceman1001) + - Fixed `hf iclass config` - now loops correct in keyroll generation (@iceman1001) + - Added `hf iclass sam` - skeleton command (@iceman1001) + - Changed `lf cotag demo` - a new decoder (@iceman1001) + - Changed `hf legic view/eview/info` - now in verbose mode will print raw hex dump (@iceman1001) + - Added new test for cotag demod using data commands in pm3_test.sh (@iceman1001) + - Added new sample trace file for cotag w fc/272. Thanks s1acky! (@iceman1001) + - Fixed `hf legic eload` - now it doesn't crash client (@doegox) + - Changed `lf hitag *` - rework client side (@doegox) + - Changed data commands to handle ask/nrz clocks above 256 (@iceman1001) + - Added `data envelope` - almost acts like data askedgedetect (@iceman1001) + - Added `data cthreshold` - acts like an inverted dirtythreshold command. Remove center values (@iceman1001) + - Added `hf mfp list` - interprets MIFARE Plus commands in traces (@DidierA) + - Changed `hf legic sim` - loop and return codes on deviceside updated to DEFINES (@iceman1001) + - Changed `hf legic einfo` - now accepts the three different cardsizes as params (@iceman1001) + - Fixed `lf cotag reader -1` - now doesn't fail (@iceman1001) + - Added support for LZ4 compressed hardnested tables (@doegox) + - Changed `emv reader -v` - now tries to print found transactions logs (@iceman1001) + - Added ISO4217 currency lookup (@iceman1001) + - Fixed bad free in loadfilebinarykey fct. Thanks to @gentilkiwi + - Changed `emv reader -v` - now can decode track1/2 data if found (@iceman1001) + - Added `emv reader` - act as a EMV reader (@iceman1001) + - Added support for Apple Wallet NFC Passes with the Value Added Services protocol implementation (@gm3197) + - Fixed compiling liblua on iOS (@The-SamminAter) + - Changed `hf_mf_luxeo_dump.lua` - now have list of keys to iterate (@iceman1001) + - Fixed the timeout of TCP connections (@wh201906) + - Added `hw timeout` - make the connection timeout configurable (@wh201906) + +## [Seven.4.16717][2023-06-25] + - Change `hf 14a info` - now identifes QL88 tags (@iceman1001) + - Added support for compiling on iOS (@The-SamminAter) + - Fixed viewing MFC dump - border char is now white (@iceman1001) + - Changed `data diff` - to print filenames in header if it fits (@iceman1001) + - Changed viewing MFC dump files - it now colors ACL + GPB bytes (@iceman1001) + - Added `hf mf supercard --furui` - now supports key recovery from Furui detection card. Thanks foxushka! (@iceman1001) + - Added `hf topaz dump --ns` - now supports nosave param (@iceman1001) + - Changed `hf topaz rdbl` - unified output (@iceman1001) + - Fixed `hf topaz wrbl` - now supports tear off and write_nonerase command (@iceman1001) + - Fixed `hf mf` commands (@iceman1001) + - Fixed `hf mfp` commands (@iceman1001) + - Added more default keys (@iceman1001) Thanks anon! + - Fixed `pm3-flash-all` shell script now correctly identify the if running on outdated bootloader (@iceman1001) + - Fixed `hf 15693/iclass sniff` trace timings (@nvx) + - Fixed LegicCash segment handling in `hf_legic.lua` script (@jmichelp) + - Fixed `trace list` - now handles marking of crc bytes w color a bit better (@iceman1001) + - Changed `hf mfu pwdgen -r` - now generates pwd/pack for Philips Sonicare, thanks @ckuenzi, @atc1441 (@iceman1001) + - Changed `hf mfu info` - now detects Philips Sonicare devices (@iceman1001) + - Fixed truncated FPGA upload due to incorrect integer size variable (@d18c7db) + - Changed `usart btfactory` - handles the new BT board with version "BT SPP V3.0" (@iceman1001) + - Changed `hf mf eview --sk` - now can extract keys and save to file (@iceman1001) + - Changed `hf mf view --sk` - now can extract keys and save to file (@iceman1001) - Changed `hf mf sim` - reduce 6ms threshold to 4ms for reset to idle #1974 (@net147) - Rebuilt the Spartan-2 `fpga_*.bit` files to include the `hi_iso14443a.v` update (@d18c7db) - Added minor orphaned change from `hi_iso14443a.v` in `fpga-xc3s100e` to `hi_iso14443a.v` in `fpga-xc2s30` (@d18c7db) @@ -22,12 +205,12 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf mf supercard` - Support editing UID and recovery of keys from second generation card (@AloneLiberty) - Added iClass credit key to default iClass key table and reorganized key order (@GuruSteve) - Changed `hf mf value` - ability to use transfer on different block (@AloneLiberty) - - Change `hf mf dump --ns` - dump command now supports `no save` of MFC card memory (@iceman1001) + - Changed `hf mf dump --ns` - dump command now supports `no save` of MFC card memory (@iceman1001) - Added `hf mf gdmsetcfg` - Supprt Gen4 GDM write configuration block (@iceman1001) - Added `hf mf gdmcfg` - Support Gen4 GDM read configuration block (@iceman1001) - Changed magic note to include a section about GDM tags (@iceman1001) - Added `hf mf gdmsetblk` - Support Gen4 GDM write block (@iceman1001) - - Changed `hf 14a info` - detect Gen GDM magic tags (@iceman1001) + - Changed `hf 14a info` - detect Gen GDM magic tags (@iceman1001) - Changed CLI max string argument length limit from 512 to 4096 (@iceman1001) - Fixed `data asn1` - now handles bad input better (@iceman1001) - Added new public key for signature MIFARE Plus Troika (@iceman100) @@ -62,11 +245,13 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `hf legic info` command for other sources (@0xdeb) - Added `hf legic einfo` - views emulator menory (@0xdeb) - Changed `hf legic view` - now also print the decoded info of the dump file (@0xdeb) - - Now `script run hf_mf_ultimatecard.lua -u` supports 10bytes UID (@alejandro12120) - - Update documentation for installation on macOS with MacPorts (@linuxgemini) + - Changed `script run hf_mf_ultimatecard.lua -u` to support 10bytes UID (@alejandro12120) + - Updated documentation for installation on macOS with MacPorts (@linuxgemini) - Added possible Paxton id to hitag2 tag info output - Changed `hf mf sim` - reduce 50ms threshold to 6ms for reset to idle #1974 (@net147) - Update `amiibo_tools.lua` with new identifiers and create a python script `update_amiibo_tools_lua.py` to automate the process in the future. (@CorySolovewicz) + - Added `lf paradox sim --fc --cn` - Simulates Paradox fob from facility code and card number (jerji) + ## [Nitride.4.16191][2023-01-29] - Changed `build_all_firmwares.sh` to fit GENERIC 256kb firmware images (@doegox) @@ -83,11 +268,11 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf mfu info` - now also does a simple OTP fingerprinting (@iceman1001) - Changed `hf mf wrbl` - now checks for strict readonly ACL's in the data to write (@iceman1001) - Changed `hf mf view` - verbose printing if strict readonly ACL's exists in dump file (@iceman1001) - - Add command `piv authsign` to get a buffer signed by the selected key (@jmichelp) - - Add command `piv scan` which tries to read all known containers on PIV (@jmichelp) - - Add support for PIV commands, over wired and contactless interfaces (@jmichelp) - - Add `--shallow` option to `hf iclass` reader commands to do shallow (ASK) reader modulation instead of OOK (@nvx) - - Improved NXP SLI/SLIX series tag identification (@nvx) + - Added command `piv authsign` to get a buffer signed by the selected key (@jmichelp) + - Added command `piv scan` which tries to read all known containers on PIV (@jmichelp) + - Added support for PIV commands, over wired and contactless interfaces (@jmichelp) + - Added `--shallow` option to `hf iclass` reader commands to do shallow (ASK) reader modulation instead of OOK (@nvx) + - Change and improved NXP SLI/SLIX series tag identification (@nvx) - Fixed buffer overflow in "lf em 4x05 sniff" (@HeinrichsH) - Fixed potential NULL array printing (@jmichelp) - Added PIV aid to resource file (@jmichelp) @@ -95,14 +280,14 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Fixed length check in sim module communications (@jmichelp) - Changed timings in i2c.c when communicating with sim module (@iceman1001) - Moved to non-deprecated API to initialize Python interpreter (@jmichelp) - - Changed `sc upgrade` updated firmware v4.13 (RDV40) - frame buffer is now 384 bytes (@sentiprox) + - Changed `sc upgrade` updated firmware v4.13 (RDV40) - frame buffer is now 384 bytes (@sentiprox) - Fixed contact interface / smartcard APDU chaining logic and allow 256 bytes ADPU payload. Need SIM firmware 4.13 to work (@jmichelp) - Fixed `lf hitag dump` - Should now work as described in the command help (@natmchugh) - Fixed SPI flash overflow when loading dictionnaries into flash. Breaking change: added 1 more sector for Mifare - dictionnaries should be loaded again (@jmichelp) - Added `hf mf gload, gsave, ggetblk, gsetblk` for Gen4 GTU in mifare classic mode (@DidierA) - Fixed `trace list -r` (relative times) not working unless `-u` (microseconds) was specified, and made `--frame` respect `-u` and `-r` options (@nvx) - Added detection of magic Gen4 GTU (@DidierA) - - Added luascript `hf_i2c_plus_2k_utils` - Script for dumping/modifying user memory of sectors 0 and 1 (@flamebarke) + - Added luascript `hf_i2c_plus_2k_utils` - Script for dumping/modifying user memory of sectors 0 and 1 (@flamebarke) - Added `hf mfu esave` - saves emulator memory to mfu dump file (@DidierA) - Added luascript `hf_mfu_ntag` - Script for configuring NTAG216 configuration pages (@flamebarke) - Changed `hf mf hardnested` - a detection for static encrypted nonces (@iceman1001) @@ -216,6 +401,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added new standalone mode `lf_em4100rsww` (@zabszk) - Fixed `hf 15 slixdisable` wrong pass id (@r1ddl3rz) - Added `script run hf_mf_hid_sim.lua` (@micsen) + - Added flashmem support in `HF_14BSNIFF` standalone mode (@wh201906) + - Changed `HF_14ASNIFF` standalone mode - now supports Proxmark3 without flashmem (@wh201906) ## [Frostbit.4.14831][2022-01-11] - Changed Wiegand format lookup - now case-insensitive (@iceman1001) diff --git a/Makefile b/Makefile index be4af6682..c86e765a6 100644 --- a/Makefile +++ b/Makefile @@ -29,17 +29,17 @@ ifneq (,$(DESTDIR)) endif endif -all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfkey/% nonce2key/% mf_nonce_brute/% mfd_aes_brute/% fpga_compress/% +all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfkey/% nonce2key/% mf_nonce_brute/% mfd_aes_brute/% fpga_compress/% cryptorf/% # hitag2crack toolsuite is not yet integrated in "all", it must be called explicitly: "make hitag2crack" #all clean install uninstall check: %: hitag2crack/% INSTALLTOOLS=pm3_eml2lower.sh pm3_eml2upper.sh pm3_mfdread.py pm3_mfd2eml.py pm3_eml2mfd.py pm3_amii_bin2eml.pl pm3_reblay-emulating.py pm3_reblay-reading.py -INSTALLSIMFW=sim011.bin sim011.sha512.txt sim013.bin sim013.sha512.txt +INSTALLSIMFW=sim011.bin sim011.sha512.txt sim013.bin sim013.sha512.txt sim014.bin sim014.sha512.txt INSTALLSCRIPTS=pm3 pm3-flash pm3-flash-all pm3-flash-bootrom pm3-flash-fullimage INSTALLSHARES=tools/jtag_openocd traces INSTALLDOCS=doc/*.md doc/md -install: all common/install +install: common/install common/install: $(info [@] Installing common resources to $(MYDESTDIR)$(PREFIX)...) @@ -109,6 +109,9 @@ endif $(Q)-$(INSTALLSUDO) $(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) # tests +cryptorf/check: FORCE + $(info [*] CHECK $(patsubst %/check,%,$@)) + $(Q)$(BASH) tools/pm3_tests.sh $(CHECKARGS) $(patsubst %/check,%,$@) mfkey/check: FORCE $(info [*] CHECK $(patsubst %/check,%,$@)) $(Q)$(BASH) tools/pm3_tests.sh $(CHECKARGS) $(patsubst %/check,%,$@) @@ -145,6 +148,9 @@ common/check: FORCE check: common/check $(info [*] ALL CHECKS DONE) +cryptorf/%: FORCE + $(info [*] MAKE $@) + $(Q)$(MAKE) --no-print-directory -C tools/cryptorf $(patsubst cryptorf/%,%,$@) DESTDIR=$(MYDESTDIR) mfkey/%: FORCE $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C tools/mfkey $(patsubst mfkey/%,%,$@) DESTDIR=$(MYDESTDIR) @@ -170,7 +176,7 @@ client/%: FORCE cleanifplatformchanged $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C client $(patsubst client/%,%,$@) DESTDIR=$(MYDESTDIR) recovery/all: bootrom/all armsrc/all -recovery/install: bootrom/all armsrc/all +recovery/install: bootrom/install armsrc/install recovery/%: FORCE cleanifplatformchanged $(info [*] MAKE $@) $(Q)$(MAKE) --no-print-directory -C recovery $(patsubst recovery/%,%,$@) DESTDIR=$(MYDESTDIR) @@ -196,6 +202,7 @@ help: @echo "+ recovery - Make bootrom and fullimage files for JTAG flashing" @echo @echo "+ client - Make only the OS-specific host client" + @echo "+ cryptorf - Make tools/cryptorf" @echo "+ mfkey - Make tools/mfkey" @echo "+ nonce2key - Make tools/nonce2key" @echo "+ mf_nonce_brute - Make tools/mf_nonce_brute" @@ -232,6 +239,8 @@ fullimage/uninstall: armsrc/uninstall recovery: recovery/all +cryptorf: cryptorf/all + mfkey: mfkey/all nonce2key: nonce2key/all @@ -318,10 +327,10 @@ style: # Make sure python3 is installed @command -v python3 >/dev/null || ( echo "Please install 'python3' package first" ; exit 1 ) # Update commands.json, patch port in case it was run under Windows - [ -x client/proxmark3 ] && client/proxmark3 --fulltext | sed 's#com[0-9]#/dev/ttyacm0#'|python3 client/pyscripts/pm3_help2json.py - doc/commands.json + [ -x client/proxmark3 ] && client/proxmark3 --fulltext | sed 's#com[0-9]#/dev/ttyACM0#'|python3 client/pyscripts/pm3_help2json.py - doc/commands.json # Update the readline autocomplete autogenerated code - [ -x client/proxmark3 ] && client/proxmark3 --fulltext | python3 client/pyscripts/pm3_help2list.py - client/src/pm3line_vocabulory.h + [ -x client/proxmark3 ] && client/proxmark3 --fulltext | python3 client/pyscripts/pm3_help2list.py - client/src/pm3line_vocabulary.h # Detecting weird codepages and tabs. @@ -370,10 +379,10 @@ release: # - Tagging temporarily... @git tag -a -m "Release $(VERSION) - $(RELEASE_NAME)" $(VERSION) # - Changing default version information based on new tag - @$(SH) tools/mkversion.sh > common/default_version_pm3.c.tmp && $(MV) common/default_version_pm3.c.tmp common/default_version_pm3.c + @$(SH) tools/mkversion.sh --force common/default_version_pm3.c # - Removing mkversion calls @sed -i 's#^.*\.\./tools/mkversion.sh.*|| #\t$$(Q)#' client/Makefile bootrom/Makefile armsrc/Makefile - @sed -i '/COMMAND/s/sh .*|| //' client/CMakeLists.txt + @sed -i '/COMMAND/s/sh .*|| //' client/CMakeLists.txt client/experimental_lib/CMakeLists.txt # - Deleting tag... @git tag -d $(VERSION) # - Amending commit... diff --git a/Makefile.defs b/Makefile.defs index bcbbaa67e..1ef2aa09d 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -75,7 +75,15 @@ else endif ifeq ($(platform),Darwin) - USE_BREW ?= 1 + ifeq ($(shell uname -p),arm64) + # The platform is iOS + USE_BREW ?= 0 + # iOS refuses to compile unless this is set + export IPHONEOS_DEPLOYMENT_TARGET=11.0 + else + # M* macOS devices return arm + USE_BREW ?= 1 + endif USE_MACPORTS ?= 0 AR= /usr/bin/ar rcs RANLIB= /usr/bin/ranlib @@ -132,6 +140,10 @@ ifeq ($(shell expr $(CC_VERSION) \>= 10), 1) endif endif ifeq ($(platform),Darwin) + ifeq ($(shell uname -p),arm64) + # iOS will refuse to compile without the minimum target of iOS 11.0 + DEFCFLAGS += -mios-version-min=11.0 + endif # their readline has strict-prototype issues DEFCFLAGS += -Wno-strict-prototypes # some warnings about braced initializers on structs we want to ignore @@ -158,6 +170,8 @@ GCCEXTRACFLAGS += -Wold-style-declaration -Wno-error=old-style-declaration GCCEXTRACFLAGS += -Woverride-init GCCEXTRACFLAGS += -Wshift-negative-value GCCEXTRACFLAGS += -Wunused-but-set-parameter -Wno-error=unused-but-set-parameter +# enable gcc static analysis +GCCEXTRACFLAGS += -fanalyzer ifeq ($(GCCEXTRA),1) DEFCFLAGS += $(GCCEXTRACFLAGS) $(EXTRACFLAGS) endif diff --git a/Makefile.platform.sample b/Makefile.platform.sample index aeb541cee..c928ae893 100644 --- a/Makefile.platform.sample +++ b/Makefile.platform.sample @@ -6,6 +6,7 @@ PLATFORM=PM3RDV4 # If you want more than one PLATFORM_EXTRAS option, separate them by spaces: #PLATFORM_EXTRAS=BTADDON #PLATFORM_EXTRAS=FLASH +#PLATFORM_EXTRAS=SMARTCARD #PLATFORM_EXTRAS=BTADDON FLASH #STANDALONE=LF_SAMYRUN diff --git a/README.md b/README.md index 20416bdbb..a2b473e78 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Iceman Fork - Proxmark3 a RFID / NFC project. +# Iceman Fork - Proxmark3 The Proxmark3 is the swiss-army tool of RFID, allowing for interactions with the vast majority of RFID tags on a global scale. Originally built by Jonathan Westhues, the device is now the goto tool for RFID Analysis for the enthusiast. Iceman repository is considered to be the pinnacle of features and functionality, enabling a huge range of extremely useful and convenient commands and LUA scripts to automate chip identification, penetration testing, and programming @@ -10,24 +10,25 @@ The Proxmark3 is the swiss-army tool of RFID, allowing for interactions with the | Actions OSX CI | Actions Ubuntu CI | Actions 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) | [![Windows Build and Test](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/windows.yml/badge.svg?branch=master)](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/windows.yml) | - +| [![MacOS Build and Test](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/macos.yml/badge.svg?branch=master)](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/macos.yml) | [![Ubuntu Build and Test](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/ubuntu.yml/badge.svg?branch=master)](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/ubuntu.yml) | [![Windows Build and Test](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/windows.yml/badge.svg?branch=master)](https://github.com/RfidResearchGroup/proxmark3/actions/workflows/windows.yml) | # Table of Contents - 1. [PROXMARK3 INSTALLATION AND OVERVIEW](#proxmark3-installation-and-overview) - 2. [Notes / helpful documents](#notes--helpful-documents) - 3. [How to build?](#how-to-build) - 1. [Proxmark3 RDV4](#proxmark3-rdv4) - 2. [Generic Proxmark3 platforms](#generic-proxmark3-platforms) - 4. [What has changed?](#what-has-changed) - 5. [Development](#development) - 6. [Supported operative systems](#supported-operative-systems) - 7. [Precompiled binaries](#precompiled-binaries) - 8. [Proxmark3 GUI](#proxmark3-gui) - 9. [Official channels](#official-channels) -10. [Maintainers](#maintainers) -11. [Citation](#citation) -12. [Copyright and licensing terms](#copyright-and-licensing-terms) +- [Iceman Fork - Proxmark3](#iceman-fork---proxmark3) +- [Table of Contents](#table-of-contents) +- [PROXMARK3 INSTALLATION AND OVERVIEW](#proxmark3-installation-and-overview) + - [Notes / helpful documents](#notes--helpful-documents) +- [How to build?](#how-to-build) + - [Proxmark3 RDV4](#proxmark3-rdv4) + - [Generic Proxmark3 platforms](#generic-proxmark3-platforms) +- [What has changed?](#what-has-changed) +- [Development](#development) + - [Supported operative systems](#supported-operative-systems) + - [Precompiled binaries](#precompiled-binaries) + - [Proxmark3 GUI](#proxmark3-gui) + - [Official channels](#official-channels) + - [Maintainers](#maintainers) + - [Citation](#citation) + - [Copyright and licensing terms](#copyright-and-licensing-terms) # PROXMARK3 INSTALLATION AND OVERVIEW @@ -35,11 +36,12 @@ The Proxmark3 is the swiss-army tool of RFID, allowing for interactions with the | :------------------: | :------------------: | | [Linux - Setup and Build](/doc/md/Installation_Instructions/Linux-Installation-Instructions.md) | [Compilation Instructions](/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md)| | [Linux - Important notes on ModemManager](/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md) | [Validating Proxmark3 Client Functionality](/doc/md/Use_of_Proxmark/1_Validation.md)| -| [Mac OS X - Homebrew & Upgrading HomeBrew Tap Formula](/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md) | [First Use and Verification](/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md)| -| [Mac OS X - MacPorts](/doc/md/Installation_Instructions/Mac-OS-X-MacPorts-Installation-Instructions.md) | [Commands & Features](/doc/md/Use_of_Proxmark/3_Commands-and-Features.md)| -| [Mac OS X - Setup and Build](/doc/md/Installation_Instructions/Mac-OS-X-Compile-From-Source-Instructions.md) || +| [macOS - Homebrew & Upgrading HomeBrew Tap Formula](/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md) | [First Use and Verification](/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md)| +| [macOS - MacPorts](/doc/md/Installation_Instructions/macOS-MacPorts-Installation-Instructions.md) | [Commands & Features](/doc/md/Use_of_Proxmark/3_Commands-and-Features.md)| +| [macOS - Setup and Build](/doc/md/Installation_Instructions/macOS-Compile-From-Source-Instructions.md) || | [Windows - Setup and Build](/doc/md/Installation_Instructions/Windows-Installation-Instructions.md) || | [Termux / Android - Setup and Build](/doc/termux_notes.md) || +| [iOS - Setup and Build](/doc/md/Installation_Instructions/iOS-Installation-Instructions.md) | [Blue Shark Manual](/doc/bt_manual_v10.md) | [Command Cheat Sheet](/doc/cheatsheet.md)| | [Advanced Compilation Parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md) | [More Cheat Sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| | [Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md) | [Complete Client Command Set](/doc/commands.md) | @@ -58,7 +60,7 @@ The Proxmark3 is the swiss-army tool of RFID, allowing for interactions with the |[Developing standalone mode](/armsrc/Standalone/readme.md)|[Wiki about standalone mode](https://github.com/RfidResearchGroup/proxmark3/wiki/Standalone-mode)|[Notes on Magic UID cards](/doc/magic_cards_notes.md)| |[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)|[Notes on MIFARE DESFire](/doc/desfire.md)| -|[Notes on CIPURSE](/doc/cipurse.md)|[Notes on NDEF type4a](/doc/ndef_type4a.md)|| +|[Notes on CIPURSE](/doc/cipurse.md)|[Notes on NDEF type4a](/doc/ndef_type4a.md)|[Notes on downgrade attacks](/doc/hid_downgrade.md)| # How to build? @@ -77,6 +79,7 @@ We define generic Proxmark3 platforms as following devices. - Ryscorp green PCB version - Radiowar black PCB version - numerous Chinese adapted versions of the RDV3 easy (kkmoon, PiSwords etc) + - Proxmark3 SE (Second edition) (BLE enabled) **Not supported** - ⚠ Proxmark Evolution (EVO) @@ -96,8 +99,6 @@ We define generic Proxmark3 platforms as following devices. - **Note**: unknown device hw - ⚠ Proxmark3 Ultimate - **Note**: unknown device hw -- ⚠ Proxmark3 SE - - **Note**: unknown device hw When it comes to these new unknown models we are depending on the community to report in if this repo works and what they did to make it work. @@ -184,7 +185,8 @@ This repo compiles nicely on - Windows/MinGW environment - Ubuntu, ParrotOS, Gentoo, Pentoo, Kali, NetHunter, Arch Linux, Fedora, Debian, Raspbian - Android / Termux - - Mac OS X / Homebrew (or MacPorts, experimental) / Apple Silicon M1 + - macOS / Homebrew (or MacPorts, experimental) / Apple Silicon M1 + - iOS (Jailbroken, rootful) - Docker container - [ Iceman repo based ubuntu 18.04 container ](https://hub.docker.com/r/secopsconsult/proxmark3) - [ Iceman fork based container v1.7 ](https://hub.docker.com/r/iceman1001/proxmark3/) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index d685f0388..4f99ae756 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -69,8 +69,6 @@ static dmabuf8_t dma_8 = { .buf = NULL }; - - // trace related variables static uint32_t trace_len = 0; static bool tracing = true; @@ -99,6 +97,11 @@ uint8_t *BigBuf_get_EM_addr(void) { return emulator_memory; } + +uint32_t BigBuf_get_hi(void) { + return s_bigbuf_hi; +} + /* uint32_t BigBuf_get_EM_size(void) { return CARD_MEMORY_SIZE; @@ -129,7 +132,7 @@ void BigBuf_Clear_keep_EM(void) { // allocate a chunk of memory from BigBuf. We allocate high memory first. The unallocated memory // at the beginning of BigBuf is always for traces/samples uint8_t *BigBuf_malloc(uint16_t chunksize) { - if (s_bigbuf_hi < chunksize) + if (s_bigbuf_hi < (chunksize + 3)) return NULL; // no memory left chunksize = (chunksize + 3) & 0xfffc; // round to next multiple of 4 @@ -142,7 +145,7 @@ uint8_t *BigBuf_malloc(uint16_t chunksize) { uint8_t *BigBuf_calloc(uint16_t chunksize) { uint8_t *mem = BigBuf_malloc(chunksize); if (mem != NULL) { - memset(mem, 0x00, chunksize); + memset(mem, 0x00, ((chunksize + 3) & 0xfffc)); // round to next multiple of 4 } return mem; } @@ -233,7 +236,7 @@ uint32_t BigBuf_get_traceLen(void) { by 'hf list -t raw', alternatively 'hf list -t ' for protocol-specific annotation of commands/responses. **/ -bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool reader2tag) { +bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, const uint8_t *parity, bool reader2tag) { if (tracing == false) { return false; } @@ -290,7 +293,7 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ } // specific LogTrace function for ISO15693: the duration needs to be scaled because otherwise it won't fit into a uint16_t -bool LogTrace_ISO15693(const uint8_t *bytes, uint16_t len, uint32_t ts_start, uint32_t ts_end, uint8_t *parity, bool reader2tag) { +bool LogTrace_ISO15693(const uint8_t *bytes, uint16_t len, uint32_t ts_start, uint32_t ts_end, const uint8_t *parity, bool reader2tag) { uint32_t duration = ts_end - ts_start; duration /= 32; ts_end = ts_start + duration; @@ -306,24 +309,31 @@ bool RAMFUNC LogTraceBits(const uint8_t *btBytes, uint16_t bitLen, uint32_t time } // Emulator memory -uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length) { +uint8_t emlSet(const uint8_t *data, uint32_t offset, uint32_t length) { uint8_t *mem = BigBuf_get_EM_addr(); - if (offset + length < CARD_MEMORY_SIZE) { + if (offset + length <= CARD_MEMORY_SIZE) { memcpy(mem + offset, data, length); return 0; } - Dbprintf("Error, trying to set memory outside of bounds! %d > %d", (offset + length), CARD_MEMORY_SIZE); + Dbprintf("Error, trying to set memory outside of bounds! " _RED_("%d") " > %d", (offset + length), CARD_MEMORY_SIZE); + return 1; +} +uint8_t emlGet(uint8_t *out, uint32_t offset, uint32_t length) { + uint8_t *mem = BigBuf_get_EM_addr(); + if (offset + length <= CARD_MEMORY_SIZE) { + memcpy(out, mem + offset, length); + return 0; + } + Dbprintf("Error, trying to read memory outside of bounds! " _RED_("%d") " > %d", (offset + length), CARD_MEMORY_SIZE); return 1; } - - // get the address of the ToSend buffer. Allocate part of Bigbuf for it, if not yet done tosend_t *get_tosend(void) { - if (toSend.buf == NULL) + if (toSend.buf == NULL) { toSend.buf = BigBuf_malloc(TOSEND_BUFFER_SIZE); - + } return &toSend; } diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index b1003a4c1..e496ce2f7 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -35,6 +35,8 @@ uint8_t *BigBuf_get_addr(void); uint32_t BigBuf_get_size(void); uint8_t *BigBuf_get_EM_addr(void); uint16_t BigBuf_max_traceLen(void); +uint32_t BigBuf_get_hi(void); + void BigBuf_initialize(void); void BigBuf_Clear(void); void BigBuf_Clear_ext(bool verbose); @@ -51,11 +53,12 @@ void set_tracing(bool enable); void set_tracelen(uint32_t value); bool get_tracing(void); -bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool reader2tag); +bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, const uint8_t *parity, bool reader2tag); bool RAMFUNC LogTraceBits(const uint8_t *btBytes, uint16_t bitLen, uint32_t timestamp_start, uint32_t timestamp_end, bool reader2tag); -bool LogTrace_ISO15693(const uint8_t *bytes, uint16_t len, uint32_t ts_start, uint32_t ts_end, uint8_t *parity, bool reader2tag); +bool LogTrace_ISO15693(const uint8_t *bytes, uint16_t len, uint32_t ts_start, uint32_t ts_end, const uint8_t *parity, bool reader2tag); -uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length); +uint8_t emlSet(const uint8_t *data, uint32_t offset, uint32_t length); +uint8_t emlGet(uint8_t *out, uint32_t offset, uint32_t length); typedef struct { int max; diff --git a/armsrc/LCD_disabled.c b/armsrc/LCD_disabled.c index 1a4b253e2..67b0f3de9 100644 --- a/armsrc/LCD_disabled.c +++ b/armsrc/LCD_disabled.c @@ -53,7 +53,7 @@ void LCDFill(unsigned char xs, unsigned char ys, unsigned char width, unsigned c } } -void LCDString(char *lcd_string, const char *font_style, unsigned char x, unsigned char y, unsigned char fcolor, unsigned char bcolor) { +void LCDString(const char *lcd_string, const char *font_style, unsigned char x, unsigned char y, unsigned char fcolor, unsigned char bcolor) { unsigned int i; unsigned char mask = 0, px, py, xme, yme, offset; const char *data; diff --git a/armsrc/LCD_disabled.h b/armsrc/LCD_disabled.h index e2f57e02c..26900531a 100644 --- a/armsrc/LCD_disabled.h +++ b/armsrc/LCD_disabled.h @@ -130,7 +130,7 @@ void LCDInit(void); void LCDReset(void); void LCDSetXY(unsigned char x, unsigned char y); void LCDSetPixel(unsigned char x, unsigned char y, unsigned char color); -void LCDString(char *lcd_string, const char *font_style, unsigned char x, unsigned char y, unsigned char fcolor, unsigned char bcolor); +void LCDString(const char *lcd_string, const char *font_style, unsigned char x, unsigned char y, unsigned char fcolor, unsigned char bcolor); void LCDFill(unsigned char xs, unsigned char ys, unsigned char width, unsigned char height, unsigned char color); #endif diff --git a/armsrc/Makefile b/armsrc/Makefile index 5203b0292..5f641884a 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -37,13 +37,13 @@ APP_CFLAGS = $(PLATFORM_DEFS) \ SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c SRC_HF = hfops.c SRC_ISO15693 = iso15693.c iso15693tools.c -SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c +SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c sam_mfc.c sam_seos.c #UNUSED: mifaresniff.c SRC_ISO14443b = iso14443b.c SRC_FELICA = felica.c SRC_CRAPTO1 = crypto1.c des.c desfire_crypto.c mifaredesfire.c aes.c platform_util.c SRC_CRC = crc.c crc16.c crc32.c -SRC_ICLASS = iclass.c optimized_cipherutils.c optimized_ikeys.c optimized_elite.c optimized_cipher.c +SRC_ICLASS = iclass.c optimized_cipherutils.c optimized_ikeys.c optimized_elite.c optimized_cipher.c sam_picopass.c SRC_LEGIC = legicrf.c legicrfsim.c legic_prng.c SRC_NFCBARCODE = thinfilm.c @@ -103,9 +103,6 @@ endif # Generic standalone Mode injection of source code include Standalone/Makefile.inc -#the FPGA bitstream files. Note: order matters! -FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit fpga_felica.bit fpga_hf_15.bit - #the lz4 source files required for decompressing the fpga config at run time SRC_LZ4 = lz4.c #additional defines required to compile lz4 @@ -130,7 +127,7 @@ THUMBSRC = start.c \ $(SRC_SMARTCARD) \ $(SRC_FPC) \ $(SRC_HITAG) \ - $(SRC_EM4x50) \ + $(SRC_EM4x50) \ $(SRC_EM4x70) \ $(SRC_SPIFFS) \ $(SRC_HF) \ @@ -184,10 +181,10 @@ showinfo: .DELETE_ON_ERROR: -# version_pm3.c should be remade on every time fullimage.stage1.elf should be remade +# version_pm3.c should be checked on every time fullimage.stage1.elf should be remade version_pm3.c: default_version_pm3.c $(OBJDIR)/fpga_version_info.o $(OBJDIR)/fpga_all.o $(THUMBOBJ) $(ARMOBJ) .FORCE - $(info [-] GEN $@) - $(Q)$(SH) ../tools/mkversion.sh > $@ || $(CP) $< $@ + $(info [-] CHECK $@) + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ fpga_version_info.c: $(FPGA_BITSTREAMS) $(FPGA_COMPRESSOR) $(info [-] GEN $@) diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index da204eaca..62dd8813d 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -86,6 +86,9 @@ define KNOWN_STANDALONE_DEFINITIONS | HF_BOG | 14a sniff with ULC/ULEV1/NTAG auth | | (RDV4 only) | storing in flashmem - Bogito | +----------------------------------------------------------+ +| HF_CARDHOPPER | Relay 14a protocols over long distances| +| (RDV4 only) | (w/ IP backbone) - Sam Haskins | ++----------------------------------------------------------+ | HF_COLIN | Mifare ultra fast sniff/sim/clone | | (RDV4 only) | - Colin Brigato | +----------------------------------------------------------+ @@ -119,6 +122,9 @@ define KNOWN_STANDALONE_DEFINITIONS | HF_TMUDFORD | Read and emulate 15 tags | | | - Tim Mudford | +----------------------------------------------------------+ +| HF_UNISNIFF | Sniff 14a/14b/15 (optionally to flash) | +| | - hazardousvoltage | ++----------------------------------------------------------+ | HF_YOUNG | Mifare sniff/simulation | | | - Craig Young | +----------------------------------------------------------+ @@ -130,9 +136,9 @@ endef STANDALONE_MODES := LF_SKELETON STANDALONE_MODES += LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_MULTIHID LF_NEDAP_SIM LF_NEXID LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE -STANDALONE_MODES += HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_TCPRST HF_TMUDFORD HF_YOUNG +STANDALONE_MODES += HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_AVEFUL HF_BOG HF_CARDHOPPER HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_TCPRST HF_TMUDFORD HF_UNISNIFF HF_YOUNG STANDALONE_MODES += DANKARMULTI -STANDALONE_MODES_REQ_BT := HF_REBLAY +STANDALONE_MODES_REQ_BT := HF_CARDHOPPER HF_REBLAY STANDALONE_MODES_REQ_SMARTCARD := STANDALONE_MODES_REQ_FLASH := LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_LEGICSIM HF_MFCSIM ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index 6aeb163bb..6f9ec5465 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -97,6 +97,10 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_BOG,$(APP_CFLAGS))) SRC_STANDALONE = hf_bog.c endif +# WITH_STANDALONE_HF_CARDHOPPER +ifneq (,$(findstring WITH_STANDALONE_HF_CARDHOPPER,$(APP_CFLAGS))) + SRC_STANDALONE = hf_cardhopper.c +endif # WITH_STANDALONE_HF_COLIN ifneq (,$(findstring WITH_STANDALONE_HF_COLIN,$(APP_CFLAGS))) SRC_STANDALONE = vtsend.c hf_colin.c frozen.c nprintf.c @@ -141,6 +145,10 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_TMUDFORD,$(APP_CFLAGS))) SRC_STANDALONE = hf_tmudford.c endif +# WITH_STANDALONE_HF_UNISNIFF +ifneq (,$(findstring WITH_STANDALONE_HF_UNISNIFF,$(APP_CFLAGS))) + SRC_STANDALONE = hf_unisniff.c +endif # WITH_STANDALONE_HF_YOUNG ifneq (,$(findstring WITH_STANDALONE_HF_YOUNG,$(APP_CFLAGS))) SRC_STANDALONE = hf_young.c diff --git a/armsrc/Standalone/hf_14asniff.c b/armsrc/Standalone/hf_14asniff.c index 041a0b6f0..dc0c0905c 100644 --- a/armsrc/Standalone/hf_14asniff.c +++ b/armsrc/Standalone/hf_14asniff.c @@ -43,7 +43,7 @@ * 1. mem spiffs dump -s hf_14asniff.trace -d hf_14asniff.trace * Copies trace data file from flash to your PC. * - * 2. trace load hf_14asniff.trace + * 2. trace load -f hf_14asniff.trace * Loads trace data from a file into PC-side buffers. * * 3. For ISO14a: trace list -t 14a -1 @@ -55,6 +55,7 @@ * the lab connected to PM3 client before taking it into the field. * * To delete the trace data from flash: + * mem spiffs remove -f hf_14asniff.trace * * Caveats / notes: * - Trace buffer will be cleared on starting stand-alone mode. Data in flash diff --git a/armsrc/Standalone/hf_14bsniff.c b/armsrc/Standalone/hf_14bsniff.c old mode 100755 new mode 100644 index d342afa93..d3adaeb23 --- a/armsrc/Standalone/hf_14bsniff.c +++ b/armsrc/Standalone/hf_14bsniff.c @@ -14,9 +14,24 @@ * - LED3: sniffed reader command, turns off when finished sniffing tag command * - LED4: unmounting/sync'ing flash (normally < 100ms) * + * To retrieve trace data from flash: + * + * 1. mem spiffs dump -s hf_14bsniff.trace -d hf_14bsniff.trace + * Copies trace data file from flash to your PC. + * + * 2. trace load -f hf_14bsniff.trace + * Loads trace data from a file into PC-side buffers. + * + * 3. For ISO14a: trace list -t 14b -1 + * + * Lists trace data from buffer without requesting it from PM3. + * * 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 trace data from flash: + * mem spiffs remove -f hf_14bsniff.trace + * * Caveats / notes: * - Trace buffer will be cleared on starting stand-alone mode. * - This module will terminate if the trace buffer is full. diff --git a/armsrc/Standalone/hf_15sniff.c b/armsrc/Standalone/hf_15sniff.c index 24d068275..ad1d3f8b6 100644 --- a/armsrc/Standalone/hf_15sniff.c +++ b/armsrc/Standalone/hf_15sniff.c @@ -43,7 +43,7 @@ * 1. mem spiffs dump -s hf_15693sniff.trace -d hf_15693sniff.trace * Copies trace data file from flash to your PC. * - * 2. trace load hf_15693sniff.trace + * 2. trace load -f hf_15693sniff.trace * Loads trace data from a file into PC-side buffers. * * 3. For ISO15693: trace list -t 15 -1 @@ -54,6 +54,7 @@ * the lab connected to PM3 client before taking it into the field. * * To delete the trace data from flash: + * mem spiffs remove -f hf_15693sniff.trace * * Caveats / notes: * - Trace buffer will be cleared on starting stand-alone mode. Data in flash diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c index 7363a952e..a7d96a44e 100644 --- a/armsrc/Standalone/hf_aveful.c +++ b/armsrc/Standalone/hf_aveful.c @@ -250,7 +250,7 @@ void RunMod(void) { } else if (state == STATE_EMUL) { uint16_t flags = FLAG_7B_UID_IN_DATA; - Dbprintf("Starting simulation, press pm3-button to stop and go back to search state."); + Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); SimulateIso14443aTag(7, flags, card.uid, 0); // Go back to search state if user presses pm3-button diff --git a/armsrc/Standalone/hf_cardhopper.c b/armsrc/Standalone/hf_cardhopper.c new file mode 100644 index 000000000..b589542c4 --- /dev/null +++ b/armsrc/Standalone/hf_cardhopper.c @@ -0,0 +1,437 @@ +/* + * hf_cardhopper standalone mode by Sam Haskins + * + * A component of our (me + Trevor Stevado) research on long-range relay + * attacks against 14a-based protocols (as presented at DEF CON '31). + * Works with a CardHopper (recommended) or BlueShark add-on. + * + * If you're reading this, you're clearly a very interesting person--- + * do reach out if you get any fun results? [ sam AT loudmouth DOT io ] + * Good luck, and may the odds be ever in your favour! + * + * The companion Android app is available on our gitlab: gitlab.com/loudmouth-security + * + * For more information, see: https://media.defcon.org/DEF%20CON%2031/DEF%20CON%2031%20presentations/Trevor%20Stevado%20Sam%20Haskins%20-%20Unlocking%20Doors%20from%20Half%20a%20Continent%20Away.pdf + */ + +#include + +#include "appmain.h" +#include "BigBuf.h" +#include "dbprint.h" +#include "fpgaloader.h" +#include "iso14443a.h" +#include "protocols.h" +#include "proxmark3_arm.h" +#include "standalone.h" +#include "ticks.h" +#include "util.h" +#include "usart.h" +#include "cmd.h" +#include "usb_cdc.h" + +#ifdef CARDHOPPER_USB +#define cardhopper_write usb_write +#define cardhopper_read usb_read_ng +#define cardhopper_data_available usb_poll_validate_length +#else +#define cardhopper_write usart_writebuffer_sync +#define cardhopper_read usart_read_ng +#define cardhopper_data_available usart_rxdata_available +#endif + +void ModInfo(void) { + DbpString(" HF - Long-range relay 14a over serial<->IP - a.k.a. CardHopper (Sam Haskins)"); +} + + +typedef struct PACKED { + uint8_t len; + uint8_t dat[255]; +} packet_t; + +// Magic numbers +static const uint8_t magicREAD[4] = "READ"; +static const uint8_t magicCARD[4] = "CARD"; +static const uint8_t magicEND [4] = "\xff" "END"; +static const uint8_t magicRSRT[7] = "RESTART"; +static const uint8_t magicERR [4] = "\xff" "ERR"; +static uint8_t magicACK [1] = "\xfe"; // is constant, but must be passed to API that doesn't like that + +// Forward declarations +static void become_reader(void); +static void select_card(void); + +static void become_card(void); +static void prepare_emulation(uint8_t *, uint16_t *, uint8_t *, packet_t *); +static void cook_ats(packet_t *, uint8_t, uint8_t); +static bool try_use_canned_response(const uint8_t *, int, tag_response_info_t *); +static void reply_with_packet(packet_t *); + +static void read_packet(packet_t *); +static void write_packet(packet_t *); + +static bool GetIso14443aCommandFromReaderInterruptible(uint8_t *, uint8_t *, int *); + + +void RunMod(void) { + // Ensure debug logs don't polute stream +#ifdef CARDHOPPER_USB + g_reply_via_usb = false; +#else + g_reply_via_fpc = false; +#endif + + StandAloneMode(); + DbpString(_CYAN_("[@]") " CardHopper has started - waiting for mode"); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + clear_trace(); + set_tracing(true); + + // Indicate we are alive and in CardHopper + LEDsoff(); + LED_A_ON(); + LED_D_ON(); + + while (1) { + WDT_HIT(); + + packet_t modeRx = { 0 }; + read_packet(&modeRx); + + if (memcmp(magicREAD, modeRx.dat, sizeof(magicREAD)) == 0) { + DbpString(_CYAN_("[@]") " I am a READER. I talk to a CARD."); + become_reader(); + } else if (memcmp(magicCARD, modeRx.dat, sizeof(magicCARD)) == 0) { + DbpString(_CYAN_("[@]") " I am a CARD. I talk to a READER."); + become_card(); + } else if (memcmp(magicEND, modeRx.dat, sizeof(magicEND)) == 0) { + break; + } else { + DbpString(_YELLOW_("[!]") " unknown mode!"); + Dbhexdump(modeRx.len, modeRx.dat, true); + } + } + + DbpString(_CYAN_("[@]") " exiting ..."); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +} + + +static void become_reader(void) { + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); + select_card(); // also sends UID, ATS + + DbpString(_CYAN_("[@]") " entering reader main loop ..."); + packet_t packet = { 0 }; + packet_t *rx = &packet; + packet_t *tx = &packet; + uint8_t toCard[256] = { 0 }; + uint8_t parity[MAX_PARITY_SIZE] = { 0 }; + + while (1) { + WDT_HIT(); + + read_packet(rx); + if (memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) break; + + memcpy(toCard, rx->dat, rx->len); + AddCrc14A(toCard, rx->len); + ReaderTransmit(toCard, rx->len + 2, NULL); + + tx->len = ReaderReceive(tx->dat, parity); + if (tx->len == 0) { + tx->len = sizeof(magicERR); + memcpy(tx->dat, magicERR, sizeof(magicERR)); + } else tx->len -= 2; // cut off the CRC + + write_packet(tx); + } +} + + +static void select_card(void) { + iso14a_card_select_t card = { 0 }; + while (1) { + WDT_HIT(); + + int ret = iso14443a_select_card(NULL, &card, NULL, true, 0, false); + if (ret && ret != 1) + Dbprintf(_RED_("[!]") " Error selecting card: %d", ret); + if (ret == 1) break; + + SpinDelay(20); + } + + DbpString(_CYAN_("[@]") " UID:"); + Dbhexdump(card.uidlen, card.uid, false); + DbpString(_CYAN_("[@]") " ATS:"); + Dbhexdump(card.ats_len - 2 /* no CRC */, card.ats, false); + + packet_t tx = { 0 }; + tx.len = card.uidlen; + memcpy(tx.dat, card.uid, tx.len); + write_packet(&tx); + + tx.len = card.ats_len - 2; + memcpy(tx.dat, card.ats, tx.len); + write_packet(&tx); +} + + +static void become_card(void) { + iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); + + uint8_t tagType; + uint16_t flags; + uint8_t data[PM3_CMD_DATA_SIZE] = { 0 }; + packet_t ats = { 0 }; + prepare_emulation(&tagType, &flags, data, &ats); + + tag_response_info_t *canned; + uint32_t cuid; + uint32_t counters[3] = { 0 }; + uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; + uint8_t pages; + SimulateIso14443aInit(tagType, flags, data, &canned, &cuid, counters, tearings, &pages); + + DbpString(_CYAN_("[@]") " Setup done - entering emulation loop"); + int fromReaderLen; + uint8_t fromReaderDat[256] = { 0 }; + uint8_t parity[MAX_PARITY_SIZE] = { 0 }; + packet_t packet = { 0 }; + packet_t *tx = &packet; + packet_t *rx = &packet; + + while (1) { + WDT_HIT(); + + if (!GetIso14443aCommandFromReaderInterruptible(fromReaderDat, parity, &fromReaderLen)) { + if (cardhopper_data_available()) { + read_packet(rx); + if (memcmp(magicRSRT, rx->dat, sizeof(magicRSRT)) == 0) { + DbpString(_CYAN_("[@]") " Breaking from reader loop"); + break; + } + } + continue; + } + + // Option 1: Use a canned response + if (try_use_canned_response(fromReaderDat, fromReaderLen, canned)) continue; + + // Option 2: Reply with our cooked ATS + if (fromReaderDat[0] == ISO14443A_CMD_RATS && fromReaderLen == 4) { + reply_with_packet(&ats); + continue; + } + + // Option 3: Relay the message + tx->len = fromReaderLen - 2; // cut off the crc + memcpy(tx->dat, fromReaderDat, tx->len); + write_packet(tx); + + read_packet(rx); + reply_with_packet(rx); + } +} + + +static void prepare_emulation(uint8_t *tagType, uint16_t *flags, uint8_t *data, packet_t *ats) { + packet_t tagTypeRx = { 0 }; + read_packet(&tagTypeRx); + packet_t timeModeRx = { 0 }; + read_packet(&timeModeRx); + packet_t uidRx = { 0 }; + read_packet(&uidRx); + read_packet(ats); + + *tagType = tagTypeRx.dat[0]; + Dbprintf(_CYAN_("[@]") " Using tag type: %hhu", *tagType); + + DbpString(_CYAN_("[@]") " Time control parameters:"); + Dbhexdump(timeModeRx.len, timeModeRx.dat, false); + uint8_t fwi = timeModeRx.dat[0] & 0x0f; + uint8_t sfgi = timeModeRx.dat[1] & 0x0f; + Dbprintf(_CYAN_("[@]") " Parsed as fwi = %hhu, sfgi = %hhu", fwi, sfgi); + + if (fwi == 0xf) { + DbpString(_YELLOW_("[!]") " Refusing to use 15 as FWI - will use 14"); + fwi = 0xe; + } + if (sfgi == 0xf) { + DbpString(_YELLOW_("[!]") " Refusing to use 15 as SFGI - will use 14"); + sfgi = 0xe; + } + + memcpy(data, uidRx.dat, uidRx.len); + *flags = (uidRx.len == 10 ? FLAG_10B_UID_IN_DATA : (uidRx.len == 7 ? FLAG_7B_UID_IN_DATA : FLAG_4B_UID_IN_DATA)); + DbpString(_CYAN_("[@]") " UID:"); + Dbhexdump(uidRx.len, data, false); + Dbprintf(_CYAN_("[@]") " Flags: %hu", *flags); + + DbpString(_CYAN_("[@]") " Original ATS:"); + Dbhexdump(ats->len, ats->dat, false); + cook_ats(ats, fwi, sfgi); + DbpString(_CYAN_("[@]") " Cooked ATS:"); + Dbhexdump(ats->len, ats->dat, false); +} + + +static void cook_ats(packet_t *ats, uint8_t fwi, uint8_t sfgi) { + if (ats->len != ats->dat[0]) { + DbpString(_RED_("[!]") " Malformed ATS - unable to cook; things may go wrong!"); + return; + } + + // If the ATS is too short (unusual), pad it to length with hopefully-sensible data + // Might be better for the phone side to do this tbh + if (ats->len == 1) { + ats->len = 4; + ats->dat[0] = 0x04; + ats->dat[1] = 0x78; + ats->dat[2] = 0x77; + // ats->dat[3] = 0x80; + } else if (ats->len == 2) { + ats->len = 4; + ats->dat[0] = 0x04; + ats->dat[2] = 0x77; + // ats->dat[3] = 0x80; + } else if (ats->len == 3) { + ats->len = 4; + ats->dat[0] = 0x04; + // ats->dat[3] = 0x80; + } + + // Set the SFGI as well as the FWI - needed for some older readers (firmware revs?) + uint8_t cookedTB0 = (fwi << 4) | sfgi; + ats->dat[3] = cookedTB0; +} + + +static bool try_use_canned_response(const uint8_t *dat, int len, tag_response_info_t *canned) { + if ((dat[0] == ISO14443A_CMD_REQA || dat[0] == ISO14443A_CMD_WUPA) && len == 1) { + EmSendPrecompiledCmd(canned + RESP_INDEX_ATQA); + return true; + } + + if (dat[1] == 0x20 && len == 2) { + if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT) { + EmSendPrecompiledCmd(canned + RESP_INDEX_UIDC1); + return true; + } else if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2) { + EmSendPrecompiledCmd(canned + RESP_INDEX_UIDC2); + return true; + } else if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { + EmSendPrecompiledCmd(canned + RESP_INDEX_UIDC3); + return true; + } + } + + if (dat[1] == 0x70 && len == 9) { + if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT) { + EmSendPrecompiledCmd(canned + RESP_INDEX_SAKC1); + return true; + } else if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2) { + EmSendPrecompiledCmd(canned + RESP_INDEX_SAKC2); + return true; + } else if (dat[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { + EmSendPrecompiledCmd(canned + RESP_INDEX_SAKC3); + return true; + } + } + + if (dat[0] == ISO14443A_CMD_PPS) { + EmSendPrecompiledCmd(canned + RESP_INDEX_PPS); + return true; + } + + // No response is expected to these 14a commands + if ((dat[0] == 0xf2 && len == 4) || dat[0] == 0xfa) return true; + if (dat[0] == ISO14443A_CMD_HALT && len == 4) return true; + + // Ignore Apple ECP2 polling + if (dat[0] == 0x6a) return true; + + return false; +} + + +static uint8_t g_responseBuffer [512 ] = { 0 }; +static uint8_t g_modulationBuffer[1024] = { 0 }; + +static void reply_with_packet(packet_t *packet) { + tag_response_info_t response = { 0 }; + response.response = g_responseBuffer; + response.modulation = g_modulationBuffer; + + memcpy(response.response, packet->dat, packet->len); + AddCrc14A(response.response, packet->len); + response.response_n = packet->len + 2; + + prepare_tag_modulation(&response, sizeof(g_modulationBuffer)); + EmSendPrecompiledCmd(&response); +} + + +static void read_packet(packet_t *packet) { + while (!cardhopper_data_available()) { + WDT_HIT(); + SpinDelayUs(100); + } + + cardhopper_read((uint8_t *) &packet->len, 1); + + uint32_t dataReceived = 0; + while (dataReceived != packet->len) { + while (!cardhopper_data_available()) WDT_HIT(); + + dataReceived += cardhopper_read(packet->dat + dataReceived, packet->len - dataReceived); + } + cardhopper_write(magicACK, sizeof(magicACK)); +} + + +static void write_packet(packet_t *packet) { + cardhopper_write((uint8_t *) packet, packet->len + 1); +} + + +static bool GetIso14443aCommandFromReaderInterruptible(uint8_t *received, uint8_t *par, int *len) { + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); + + Uart14aInit(received, par); + + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + (void)b; + + uint8_t flip = 0; + uint16_t checker = 4000; + for (;;) { + WDT_HIT(); + + if (flip == 3) { + if (cardhopper_data_available()) + return false; + + flip = 0; + } + + if (checker-- == 0) { + flip++; + checker = 4000; + } + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (MillerDecoding(b, 0)) { + *len = GetUart14a()->len; + return true; + } + } + } + return false; +} diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index 2da9a582a..044d3aba3 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -293,7 +293,7 @@ static void ReadLastTagFromFlash(void) { rdv40_spiffs_read_as_filetype((char *)HFCOLIN_LASTTAG_SYMLINK, (uint8_t *)mem, len, RDV40_SPIFFS_SAFETY_SAFE); // copy 64blocks (16bytes) starting w block0, to emulator mem. - emlSetMem(mem, 0, 64); + emlSetMem_xt(mem, 0, 64, 16); DbprintfEx(FLAG_NEWLINE, "[OK] Last tag recovered from FLASHMEM set to emulator"); cjSetCursLeft(); @@ -650,7 +650,7 @@ failtag: for (uint8_t t = 0; t < 2; t++) { memcpy(mblock + t * 10, foundKey[t][sectorNo], 6); } - emlSetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); + emlSetMem_xt(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1, 16); } cjSetCursLeft(); @@ -821,24 +821,24 @@ int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) { } for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(s); blockNo++) { - if (isOK && mifare_classic_readblock(pcs, colin_cjcuid, FirstBlockOfSector(s) + blockNo, dataoutbuf)) { + if (isOK && mifare_classic_readblock(pcs, FirstBlockOfSector(s) + blockNo, dataoutbuf)) { isOK = false; break; }; if (isOK) { if (blockNo < NumBlocksPerSector(s) - 1) { - emlSetMem(dataoutbuf, FirstBlockOfSector(s) + blockNo, 1); + emlSetMem_xt(dataoutbuf, FirstBlockOfSector(s) + blockNo, 1, 16); } else { // sector trailer, keep the keys, set only the AC emlGetMem(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1); memcpy(&dataoutbuf2[6], &dataoutbuf[6], 4); - emlSetMem(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1); + emlSetMem_xt(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1, 16); } } } } - int res = mifare_classic_halt(pcs, colin_cjcuid); + int res = mifare_classic_halt(pcs); (void)res; crypto1_deinit(pcs); @@ -986,7 +986,7 @@ int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *data break; }; - if (mifare_classic_halt(NULL, colin_cjcuid)) { + if (mifare_classic_halt(NULL)) { DbprintfEx(FLAG_NEWLINE, "Halt error"); break; }; @@ -1006,7 +1006,7 @@ int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *data break; }; - if (mifare_classic_halt(NULL, colin_cjcuid)) { + if (mifare_classic_halt(NULL)) { DbprintfEx(FLAG_NEWLINE, "Halt error"); break; }; @@ -1043,7 +1043,7 @@ int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *data }; if (workFlags & 0x04) { - if (mifare_classic_halt(NULL, colin_cjcuid)) { + if (mifare_classic_halt(NULL)) { cjSetCursFRight(); DbprintfEx(FLAG_NEWLINE, "Halt error"); diff --git a/armsrc/Standalone/hf_craftbyte.c b/armsrc/Standalone/hf_craftbyte.c index 80733a3cc..41d9a3503 100644 --- a/armsrc/Standalone/hf_craftbyte.c +++ b/armsrc/Standalone/hf_craftbyte.c @@ -91,7 +91,7 @@ void RunMod(void) { continue; } - Dbprintf("Starting simulation, press pm3-button to stop and go back to search state."); + Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); if (card.sak == 0x08 && card.atqa[0] == 0x04 && card.atqa[1] == 0) { DbpString("Mifare Classic 1k"); SimulateIso14443aTag(1, flags, card.uid, 0); diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index 481475bbc..a47cf6517 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -69,7 +69,6 @@ #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" #define HF_ICLASS_ATTACK_BIN "iclass_mac_attack" #define HF_ICLASS_CC_A "iceclass_cc_a.bin" @@ -117,10 +116,6 @@ static bool have_aa2(void) { return memcmp(aa2_key, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8); } -static uint8_t get_pagemap(const picopass_hdr_t *hdr) { - return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; -} - static uint8_t csns[8 * NUM_CSNS] = { 0x01, 0x0A, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, 0x0C, 0x06, 0x0C, 0xFE, 0xF7, 0xFF, 0x12, 0xE0, diff --git a/armsrc/Standalone/hf_legicsim.c b/armsrc/Standalone/hf_legicsim.c index 7765672b0..849d2be3b 100644 --- a/armsrc/Standalone/hf_legicsim.c +++ b/armsrc/Standalone/hf_legicsim.c @@ -68,8 +68,10 @@ static bool fill_eml_from_file(char *dumpfile) { } //read and load dump file BigBuf_Clear(); - if (g_dbglevel >= DBG_INFO) - Dbprintf(_YELLOW_("Found dump file %s. Uploading to emulator memory..."), dumpfile); + if (g_dbglevel >= DBG_INFO) { + Dbprintf("Found dump file... `" _YELLOW_("%s") "`", dumpfile); + Dbprintf("Uploading to emulator memory..."); + } uint8_t *emCARD = BigBuf_get_EM_addr(); rdv40_spiffs_read_as_filetype(dumpfile, emCARD, size, RDV40_SPIFFS_SAFETY_SAFE); return true; diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c index 97a02a278..ed6f93a3b 100644 --- a/armsrc/Standalone/hf_mattyrun.c +++ b/armsrc/Standalone/hf_mattyrun.c @@ -109,7 +109,7 @@ static int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_ break; }; - if (mifare_classic_halt(NULL, mattyrun_cuid)) { + if (mifare_classic_halt(NULL)) { DbprintfEx(FLAG_NEWLINE, "Halt error"); break; }; @@ -129,7 +129,7 @@ static int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_ break; }; - if (mifare_classic_halt(NULL, mattyrun_cuid)) { + if (mifare_classic_halt(NULL)) { DbprintfEx(FLAG_NEWLINE, "Halt error"); break; }; @@ -165,7 +165,7 @@ static int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_ }; if (workFlags & 0x04) { - if (mifare_classic_halt(NULL, mattyrun_cuid)) { + if (mifare_classic_halt(NULL)) { DbprintfEx(FLAG_NEWLINE, "Halt error"); break; }; @@ -267,23 +267,23 @@ static int saMifareECardLoad(uint32_t numofsectors, uint8_t keytype) { // failure to read one block, skips to next sector. for (uint8_t blockNo = 0; blockNo < NumBlocksPerSector(s); blockNo++) { - if (mifare_classic_readblock(pcs, mattyrun_cuid, FirstBlockOfSector(s) + blockNo, dataoutbuf)) { + if (mifare_classic_readblock(pcs, FirstBlockOfSector(s) + blockNo, dataoutbuf)) { retval = PM3_ESOFT; break; }; if (blockNo < NumBlocksPerSector(s) - 1) { - emlSetMem(dataoutbuf, FirstBlockOfSector(s) + blockNo, 1); + emlSetMem_xt(dataoutbuf, FirstBlockOfSector(s) + blockNo, 1, 16); } else { // sector trailer, keep the keys, set only the AC emlGetMem(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1); memcpy(&dataoutbuf2[6], &dataoutbuf[6], 4); - emlSetMem(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1); + emlSetMem_xt(dataoutbuf2, FirstBlockOfSector(s) + blockNo, 1, 16); } } } - int res = mifare_classic_halt(pcs, mattyrun_cuid); + int res = mifare_classic_halt(pcs); (void)res; out: @@ -505,7 +505,7 @@ void RunMod(void) { memcpy(mblock + t * 10, foundKey[t][sectorNo], 6); } } - emlSetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); + emlSetMem_xt(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1, 16); } } diff --git a/armsrc/Standalone/hf_mfcsim.c b/armsrc/Standalone/hf_mfcsim.c index 86ff62f6c..bba01d472 100644 --- a/armsrc/Standalone/hf_mfcsim.c +++ b/armsrc/Standalone/hf_mfcsim.c @@ -71,7 +71,8 @@ static bool fill_eml_from_file(char *dumpfile) { //read and load dump file if (g_dbglevel >= DBG_INFO) { - Dbprintf(_YELLOW_("Found dump file %s. Uploading to emulator memory..."), dumpfile); + Dbprintf("Found dump file... `" _YELLOW_("%s") "`", dumpfile); + Dbprintf("Uploading to emulator memory..."); } emlClearMem(); diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index b0d81ff79..56be50db1 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -28,19 +28,20 @@ #include "iso14443a.h" #include "protocols.h" #include "cmd.h" +#include "commonutil.h" void ModInfo(void) { - DbpString(" HF - Reading Visa cards & Emulating a Visa MSD Transaction(ISO14443) - (Salvador Mendoza)"); + DbpString(" HF - Reading VISA cards & Emulating a VISA MSD Transaction(ISO14443) - (Salvador Mendoza)"); } /* This standalone implements two different modes: reading and emulating. * * The initial mode is reading with LED A as guide. -* In this mode, the Proxmark expects a Visa Card, +* In this mode, the Proxmark3 expects a Visa Card, * and will act as card reader. Trying to find track 2. * -* If the Proxmark found a track 2, it will change to emulation mode (LED C) automatically. -* During this mode the Proxmark will behave as card, emulating a Visa MSD transaction +* If the Proxmark3 found a track 2, it will change to emulation mode (LED C) automatically. +* During this mode the Proxmark3 will behave as card, emulating a Visa MSD transaction * using the pre-saved track2 from the previous reading. * * It is possible to jump from mode to another by simply pressing the button. @@ -132,9 +133,15 @@ static uint8_t treatPDOL(const uint8_t *apdu) { void RunMod(void) { StandAloneMode(); - DbpString(_YELLOW_(">>") "Reading Visa cards & Emulating a Visa MSD Transaction a.k.a. MSDSal Started " _YELLOW_("<<")); + DbpString(""); + DbpString(_YELLOW_(">>>") " Reading VISA cards & Emulating a VISA MSD Transaction a.k.a. MSDSal Started " _YELLOW_("<<<")); + DbpString(""); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + // free eventually allocated BigBuf memory but keep Emulator Memory + // also sets HIGH pointer of BigBuf enabling us to malloc w/o fiddling w the reserved emulator memory + BigBuf_free_keep_EM(); + //For reading process iso14a_card_select_t card_a_info; @@ -146,10 +153,32 @@ void RunMod(void) { 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0x00 }; - uint8_t visa[13] = { - 0x00, 0xA4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, - 0x00, 0x03, 0x10, 0x10, 0x00 + + uint8_t visa[] = { 0x00, 0xA4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x00 }; + + + /* + uint8_t select_aid_hdr[5] = { 0x00, 0xA4, 0x04, 0x00, 0x00 }; + static const char* aid_list [] = { + "A00000000305076010", // VISA ELO Credit + "A0000000031010", // VISA Debit/Credit (Classic) + "A000000003101001", // VISA Credit + "A000000003101002", // VISA Debit + "A0000000032010", // VISA Electron + "A0000000032020", // VISA + "A0000000033010", // VISA Interlink + "A0000000034010", // VISA Specific + "A0000000035010", // VISA Specific + "A0000000036010", // Domestic Visa Cash Stored Value + "A0000000036020", // International Visa Cash Stored Value + "A0000000038002", // VISA Auth, VisaRemAuthen EMV-CAP (DPA) + "A0000000038010", // VISA Plus + "A0000000039010", // VISA Loyalty + "A000000003999910", // VISA Proprietary ATM + "A000000098", // Visa USA Debit Card + "A0000000980848", // Visa USA Debit Cardv }; + */ uint8_t processing [8] = {0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00}; uint8_t sfi[5] = {0x00, 0xb2, 0x01, 0x0c, 0x00}; @@ -161,48 +190,49 @@ void RunMod(void) { bool existpdol; - // - MSD token card format - // - //Card number: 4412 3456 0578 1234 - //Expiration date: 17/11 - //Service code: 201 - //Discretionary data: 0000030000991 - //char token[19] = {0x44,0x12,0x34,0x56,0x05,0x78,0x12,0x34,0xd1,0x71,0x12,0x01,0x00,0x00,0x03,0x00,0x00,0x99,0x1f}; + // Card number.............. 4412 3456 0578 1234 + // Expiration date.......... 17/11 + // Service code............. 201 + // Discretionary data....... 0000030000991 + // Pin verification value... 0000 + // CVV / iCvv............... 030 + // Trailing................. 000991 + + // 44 12 34 56 05 78 12 34 D 1711 2 01 00 00 03 00 00 99 1 + // char token[19] = {0x44,0x12,0x34,0x56,0x05,0x78,0x12,0x34,0xd1,0x71,0x12,0x01,0x00,0x00,0x03,0x00,0x00,0x99,0x1f}; // // It is possible to initialize directly the emulation mode, having "token" with data and set "chktoken" = true ;) // char token[19] = {0x00}; bool chktoken = false; - // Allocate 512 bytes for the dynamic modulation, created when the reader queries for it - // Such a response is less time critical, so we can prepare them on the fly -#define DYNAMIC_RESPONSE_BUFFER_SIZE 64 -#define DYNAMIC_MODULATION_BUFFER_SIZE 512 - // UID 4 bytes(could be 7 bytes if needed it) uint8_t flags = FLAG_4B_UID_IN_DATA; // in case there is a read command received we shouldn't break uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; - uint8_t visauid[7] = {0x01, 0x02, 0x03, 0x04}; + uint8_t visauid[7] = {0xE9, 0x66, 0x5D, 0x20}; memcpy(data, visauid, 4); // to initialize the emulation - uint8_t tagType = 11; // 11 = ISO/IEC 14443-4 - javacard (JCOP) tag_response_info_t *responses; + uint32_t cuid = 0; - uint32_t counters[3] = { 0x00, 0x00, 0x00 }; - uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; - uint8_t pages = 0; // command buffers uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 }; uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 }; + + // Allocate 512 bytes for the dynamic modulation, created when the reader queries for it + // Such a response is less time critical, so we can prepare them on the fly +#define DYNAMIC_RESPONSE_BUFFER_SIZE 64 +#define DYNAMIC_MODULATION_BUFFER_SIZE 512 + uint8_t dynamic_response_buffer[DYNAMIC_RESPONSE_BUFFER_SIZE] = {0}; uint8_t dynamic_modulation_buffer[DYNAMIC_MODULATION_BUFFER_SIZE] = {0}; - // to know the transaction status uint8_t prevCmd = 0; @@ -223,11 +253,11 @@ void RunMod(void) { // Checking if the user wants to go directly to emulation mode using a hardcoded track 2 if (chktoken == true && token[0] != 0x00) { state = STATE_EMU; - DbpString(_YELLOW_("[ ") "Initialized emulation mode" _YELLOW_(" ]")); - DbpString("\n"_YELLOW_("!!") "Waiting for a card reader..."); + DbpString("Initialized [ " _BLUE_("emulation mode") " ]"); + DbpString("Waiting for a card reader..."); } else { - DbpString(_YELLOW_("[ ") "Initialized reading mode" _YELLOW_(" ]")); - DbpString("\n"_YELLOW_("!!") "Waiting for a Visa card..."); + DbpString("Initialized [ " _YELLOW_("reading mode") " ]"); + DbpString("Waiting for a VISA card..."); } for (;;) { @@ -240,20 +270,20 @@ void RunMod(void) { int button_pressed = BUTTON_HELD(1000); - if (button_pressed == BUTTON_HOLD) + if (button_pressed == BUTTON_HOLD) { break; - else if (button_pressed == BUTTON_SINGLE_CLICK) { + } else if (button_pressed == BUTTON_SINGLE_CLICK) { // pressing one time change between reading & emulation if (state == STATE_READ) { if (chktoken == true && token[0] != 0x00) { // only change to emulation if it saved a track 2 in memory state = STATE_EMU; - DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]")); + DbpString("[ " _BLUE_("Emulation mode") " ]"); } else - DbpString(_YELLOW_("!!") "Nothing in memory to emulate"); + DbpString(_YELLOW_("Nothing in memory to emulate")); } else { state = STATE_READ; - DbpString(_YELLOW_("[ ") "In reading mode" _YELLOW_(" ]")); + DbpString("[ " _YELLOW_("Reading mode") " ]"); } } @@ -261,35 +291,40 @@ void RunMod(void) { if (state == STATE_READ) { LED_A_ON(); - if (chktoken) + if (chktoken) { LED_C_ON(); + } iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); if (iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)) { - DbpString(_YELLOW_("+") "Found ISO 14443 Type A!"); - for (uint8_t i = 0; i < 4; i++) { chktoken = false; LED_C_OFF(); LED_B_ON(); + + // add loop visa + // for (int i = 0; i < ARRAYLEN(AIDlist); i ++) { +// hexstr_to_byte_array("a0da02631a440a44000000a012ad10a00e800200048108", sam_apdu, &sam_len); uint8_t apdulen = iso14_apdu(apdus[i], (uint16_t) apduslen[i], false, apdubuffer, NULL); if (apdulen > 0) { - DbpString(_YELLOW_("[ ") "Proxmark command" _YELLOW_(" ]")); + DbpString("[ " _YELLOW_("Proxmark command") " ]"); Dbhexdump(apduslen[i], apdus[i], false); - DbpString(_GREEN_("[ ") "Card answer" _GREEN_(" ]")); + DbpString("[ " _GREEN_("Card answer") " ]"); Dbhexdump(apdulen - 2, apdubuffer, false); - DbpString("----"); + DbpString("-------------------------------"); for (uint8_t u = 0; u < apdulen; u++) { if (i == 1) { // check for PDOL if (apdubuffer[u] == 0x9F && apdubuffer[u + 1] == 0x38) { - for (uint8_t e = 0; e <= apdubuffer[u + 2]; e++) + + for (uint8_t e = 0; e <= apdubuffer[u + 2]; e++) { pdol[e] = apdubuffer[u + e + 2]; + } // generate a challenge plen = treatPDOL(pdol); @@ -309,25 +344,27 @@ void RunMod(void) { } if (i == 1) { - DbpString(_GREEN_("[ ") "Challenge generated" _GREEN_(" ]")); + DbpString("[ "_GREEN_("Challenge generated") " ]"); Dbhexdump(plen, existpdol ? ppdol : processing, false); } } else { - DbpString(_YELLOW_("!!") "Error reading the card"); + DbpString(_RED_("Error reading the card")); } LED_B_OFF(); } if (chktoken) { - DbpString(_RED_("[ ") "Track 2" _RED_(" ]")); + DbpString("[ " _GREEN_("Track 2") " ]"); Dbhexdump(19, (uint8_t *)token, false); - DbpString(_YELLOW_("!!") "Card number"); + DbpString("[ " _GREEN_("Card Number") " ]"); Dbhexdump(8, (uint8_t *)token, false); - DbpString("---"); + DbpString("-------------------------------"); + DbpString(""); + DbpString(""); LED_C_ON(); state = STATE_EMU; - DbpString(_YELLOW_("[ ") "Initialized emulation mode" _YELLOW_(" ]")); - DbpString("\n"_YELLOW_("!!") "Waiting for a card reader..."); + DbpString("Initialized [ " _BLUE_("emulation mode") " ]"); + DbpString("Waiting for a card reader..."); } } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -340,14 +377,15 @@ void RunMod(void) { // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); - if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) { + // tag type: 11 = ISO/IEC 14443-4 - javacard (JCOP) + if (SimulateIso14443aInit(11, flags, data, &responses, &cuid, NULL, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); - DbpString(_YELLOW_("!!") "Error initializing the emulation process!"); + DbpString(_RED_("Error initializing the emulation process!")); SpinDelay(500); state = STATE_READ; - DbpString(_YELLOW_("[ ") "Initialized reading mode" _YELLOW_(" ]")); - DbpString("\n" _YELLOW_("!!") "Waiting for a Visa card..."); + DbpString("Initialized [ "_YELLOW_("reading mode") " ]"); + DbpString("Waiting for a VISA card..."); continue; } @@ -366,17 +404,18 @@ void RunMod(void) { for (;;) { LED_B_OFF(); // clean receive command buffer - if (!GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len)) { - DbpString(_YELLOW_("!!") "Emulator stopped"); + if (GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len) == false) { + DbpString("Emulator stopped"); retval = PM3_EOPABORTED; break; } + tag_response_info_t *p_response = NULL; LED_B_ON(); // dynamic_response_info will be in charge of responses dynamic_response_info.response_n = 0; - + //Dbprintf("receivedCmd: %02x\n", receivedCmd); // received a REQUEST if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { @@ -387,53 +426,49 @@ void RunMod(void) { // received a HALT } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { - //DbpString(_YELLOW_("+") "Received a HALT"); p_response = NULL; // received a WAKEUP } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { - //DbpString(_YELLOW_("+") "WAKEUP Received"); prevCmd = 0; p_response = &responses[RESP_INDEX_ATQA]; // received request for UID (cascade 1) } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { - //DbpString(_YELLOW_("+") "Request for UID C1"); - p_response = &responses[RESP_INDEX_UIDC1]; + p_response = &responses[RESP_INDEX_UIDC1]; // received a SELECT (cascade 1) } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { - //DbpString(_YELLOW_("+") "Request for SELECT S1"); - p_response = &responses[RESP_INDEX_SAKC1]; + p_response = &responses[RESP_INDEX_SAKC1]; // received a RATS request } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { - DbpString(_YELLOW_("+") "Request for RATS"); prevCmd = 0; - //p_response = &responses[RESP_INDEX_RATS]; - - static uint8_t rRATS[] = { 0x13, 0x78, 0x80, 0x72, 0x02, 0x80, 0x31, 0x80, 0x66, 0xb1, 0x84, 0x0c, 0x01, 0x6e, 0x01, 0x83, 0x00, 0x90, 0x00 }; - - memcpy(&dynamic_response_info.response[0], rRATS, sizeof(rRATS)); - dynamic_response_info.response_n = sizeof(rRATS); + p_response = &responses[RESP_INDEX_RATS]; } else { - DbpString(_YELLOW_("[ ") "Card reader command" _YELLOW_(" ]")); - Dbhexdump(len, receivedCmd, false); + if (g_dbglevel == DBG_DEBUG) { + DbpString("[ "_YELLOW_("Card reader command") " ]"); + Dbhexdump(len, receivedCmd, false); + } - // emulate a Visa MSD(Magnetic stripe data) card + // emulate a Visa MSD (Magnetic stripe data) card if (receivedCmd[0] == 0x02 || receivedCmd[0] == 0x03) { dynamic_response_info.response[0] = receivedCmd[0]; // depending on card reader commands, the Proxmark will answer to fool the reader // respond with PPSE if (receivedCmd[2] == 0xA4 && receivedCmd[6] == 0x32 && prevCmd == 0) { + // need to adapt lengths.. uint8_t ppsea[39] = { + // 0x23 = 35, skip two first bytes then the message - SW 2 is 35 = 0x23 0x6F, 0x23, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31, 0xA5, 0x11, 0xBF, 0x0C, 0x0E, 0x61, - 0x0C, 0x4F, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, - 0x10, 0x10, 0x87, 0x01, 0x01, 0x90, 0x00 + 0x0C, 0x4F, + // len aid0 aid1 aid2... + 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, + 0x87, 0x01, 0x01, 0x90, 0x00 }; memcpy(&dynamic_response_info.response[1], ppsea, sizeof(ppsea)); dynamic_response_info.response_n = sizeof(ppsea) + 1; @@ -442,10 +477,14 @@ void RunMod(void) { // respond Visa AID } else if (receivedCmd[2] == 0xA4 && receivedCmd[10] == 0x03 && receivedCmd[11] == 0x10 && prevCmd == 1) { uint8_t visauid_long[34] = { - 0x6F, 0x1E, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00, - 0x03, 0x10, 0x10, 0xA5, 0x13, 0x50, 0x0B, 0x56, - 0x49, 0x53, 0x41, 0x20, 0x43, 0x52, 0x45, 0x44, - 0x49, 0x54, 0x9F, 0x38, 0x03, 0x9F, 0x66, 0x02, + // 0x1E = 30, skip two first bytes then the message - SW 2 is 30 = 0x1E + 0x6F, 0x1E, 0x84, + // len aid0 aid1 aid2.... + 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, + 0xA5, 0x13, 0x50, + // len V I S A C R E D I T + 0x0B, 0x56, 0x49, 0x53, 0x41, 0x20, 0x43, 0x52, 0x45, 0x44, 0x49, 0x54, + 0x9F, 0x38, 0x03, 0x9F, 0x66, 0x02, 0x90, 0x00 }; memcpy(&dynamic_response_info.response[1], visauid_long, sizeof(visauid_long)); @@ -461,18 +500,21 @@ void RunMod(void) { // SFI } else if (receivedCmd[1] == 0x00 && receivedCmd[2] == 0xB2 && prevCmd == 3) { - uint8_t last[4] = {0x70, 0x15, 0x57, 0x13}; - uint8_t statusapdu[2] = {0x90, 0x00}; - uint8_t card[25]; - memcpy(&card[0], last, sizeof(last)); + uint8_t card[25] = { + 0x70, 0x15, 0x57, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x90, 0x00 + }; + // add token array == Track 2 found before memcpy(&card[4], token, sizeof(token)); - memcpy(&card[23], statusapdu, sizeof(statusapdu)); + memcpy(&dynamic_response_info.response[1], card, sizeof(card)); dynamic_response_info.response_n = sizeof(card) + 1; prevCmd++; } else { - uint8_t finished[2] = {0x6f, 0x00}; + uint8_t finished[2] = {0x6F, 0x00}; memcpy(&dynamic_response_info.response[1], finished, sizeof(finished)); dynamic_response_info.response_n = sizeof(finished) + 1; if (prevCmd == 5) { @@ -480,7 +522,7 @@ void RunMod(void) { } } } else { - DbpString(_YELLOW_("!!") "Received unknown command!"); + DbpString(_RED_("Received unknown command!")); if (prevCmd < 4) { memcpy(dynamic_response_info.response, receivedCmd, len); dynamic_response_info.response_n = len; @@ -491,7 +533,7 @@ void RunMod(void) { } if (dynamic_response_info.response_n > 0) { - DbpString(_GREEN_("[ ") "Proxmark3 answer" _GREEN_(" ]")); + DbpString("[ " _GREEN_("Proxmark3 answer") " ]"); Dbhexdump(dynamic_response_info.response_n, dynamic_response_info.response, false); DbpString("----"); @@ -501,7 +543,7 @@ void RunMod(void) { if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE) == false) { SpinDelay(500); - DbpString(_YELLOW_("!!") "Error preparing Proxmark to answer!"); + DbpString(_RED_("Error preparing Proxmark to answer!")); continue; } p_response = &dynamic_response_info; @@ -511,13 +553,15 @@ void RunMod(void) { EmSendPrecompiledCmd(p_response); } } - switch_off(); + switch_off(); set_tracing(false); BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, retval, NULL, 0); } } - DbpString(_YELLOW_("[=]") "exiting"); + DbpString("Exit standalone mode!"); + DbpString(""); + SpinErr(15, 200, 3); LEDsoff(); } diff --git a/armsrc/Standalone/hf_reblay.c b/armsrc/Standalone/hf_reblay.c index cf290fa31..5bd13ec3b 100644 --- a/armsrc/Standalone/hf_reblay.c +++ b/armsrc/Standalone/hf_reblay.c @@ -46,14 +46,14 @@ void ModInfo(void) { * standalone. * * For the reading mode: -* - Set up and run the other end first, to where the Proxmark will send the data. +* - Set up and run the other end first, to where the Proxmark3 will send the data. * - After the card is detected, Proxmark3 will send a package. The first byte will be the package * length, then, the card data. Use the first length byte to read the whole package. * - Proxmark3 will expect a raw APDU from the other end, then it will be sent to the card. * - The answer of the card will be sent back to the connection, repeating the cycle. * * For the emulation mode: -* - Set up and run the other end first, from where the Proxmark will receive the data. +* - Set up and run the other end first, from where the Proxmark3 will receive the data. * - When the Proxmark3 detected the terminal, it will send the command to the connection. * - The first byte will be the package length, then, the terminal command. Use the first * length byte to read the whole package. @@ -73,29 +73,25 @@ void ModInfo(void) { void RunMod() { StandAloneMode(); - Dbprintf(_YELLOW_(">>") "Relaying ISO/14443A data over Bluetooth a.k.a. reblay Started<<"); + DbpString(""); + Dbprintf(_YELLOW_(">>> ") " Relaying ISO/14443A data over Bluetooth a.k.a. reblay Started " _YELLOW_("<<<")); + DbpString(""); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); -// Allocate 512 bytes for the dynamic modulation, created when the reader queries for it -// Such a response is less time critical, so we can prepare them on the fly -#define DYNAMIC_RESPONSE_BUFFER_SIZE 512 -#define DYNAMIC_MODULATION_BUFFER_SIZE 1024 - uint8_t flags = FLAG_4B_UID_IN_DATA; //UID 4 bytes(could be 7 bytes if needed it) - uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; // in case there is a read command received we shouldn't break - uint8_t visauid[7] = {0x01, 0x02, 0x03, 0x04}; + // UID 4 bytes(could be 7 bytes if needed it) + uint8_t flags = FLAG_4B_UID_IN_DATA; + // in case there is a read command received we shouldn't break + uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; + + uint8_t visauid[7] = {0xE9, 0x66, 0x5D, 0x20}; memcpy(data, visauid, 4); // to initialize the emulation - uint8_t tagType = 4; // 4 = ISO/IEC 14443-4 - javacard (JCOP) tag_response_info_t *responses; uint32_t cuid = 0; - uint32_t counters[3] = { 0x00, 0x00, 0x00 }; - uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; - uint8_t pages = 0; - // For received Bluetooth package uint8_t rpacket[MAX_FRAME_SIZE] = { 0x00 }; @@ -126,6 +122,12 @@ void RunMod() { uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 }; uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 }; + +// Allocate 512 bytes for the dynamic modulation, created when the reader queries for it +// Such a response is less time critical, so we can prepare them on the fly +#define DYNAMIC_RESPONSE_BUFFER_SIZE 512 +#define DYNAMIC_MODULATION_BUFFER_SIZE 1024 + uint8_t dynamic_response_buffer[DYNAMIC_RESPONSE_BUFFER_SIZE] = {0}; uint8_t dynamic_modulation_buffer[DYNAMIC_MODULATION_BUFFER_SIZE] = {0}; @@ -143,9 +145,9 @@ void RunMod() { uint8_t state = STATE_READ; if (state == STATE_READ) { - DbpString(_YELLOW_("[ ") "In reading mode" _YELLOW_(" ]")); + DbpString("Initialized [ " _YELLOW_("reading mode") " ]"); } else { - DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]")); + DbpString("Initialized [ " _BLUE_("emulation mode") " ]"); } for (;;) { @@ -162,10 +164,10 @@ void RunMod() { else if (button_pressed == BUTTON_SINGLE_CLICK) { // Pressing one time change between reading & emulation if (state == STATE_READ) { state = STATE_EMU; - DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]")); + DbpString("[ " _BLUE_("Emulation mode") " ]"); } else { state = STATE_READ; - DbpString(_YELLOW_("[ ") "In reading mode" _YELLOW_(" ]")); + DbpString("[ " _YELLOW_("Reading mode") " ]"); } } @@ -178,6 +180,7 @@ void RunMod() { iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); if (iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)) { + LED_B_ON(); // Get data to send a ping with UID + ATQA + SAK @@ -208,7 +211,9 @@ void RunMod() { Dbhexdump(uidlen + 4, rdata, false); DbpString(_YELLOW_("[ ") "Sending ping" _YELLOW_(" ]")); + if (usart_writebuffer_sync(rdata, uidlen + 4) == PM3_SUCCESS) { + DbpString(_YELLOW_("[ ") "Sent!" _YELLOW_(" ]")); for (;;) { @@ -261,26 +266,31 @@ void RunMod() { // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); - if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) { + // 4 = ISO/IEC 14443-4 - javacard (JCOP) + if (SimulateIso14443aInit(4, flags, data, &responses, &cuid, NULL, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); - DbpString(_YELLOW_("!!") "Error initializing the emulation process!"); + DbpString(_RED_("Error initializing the emulation process!")); SpinDelay(500); state = STATE_READ; - DbpString(_YELLOW_("[ ") "Initialized reading mode" _YELLOW_(" ]")); + DbpString("Initialized [ "_YELLOW_("reading mode") " ]"); continue; } // We need to listen to the high-frequency, peak-detected path. iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); - int len = 0; // Command length - int retval = PM3_SUCCESS; // Check emulation status + // Command length + int len = 0; + // Check emulation status + int retval = PM3_SUCCESS; - uint8_t resp = 0; // Bluetooth response + // Bluetooth response + uint8_t resp = 0; lenpacket = 0; - uint8_t prevcmd = 0x00; // Keep track of last terminal type command + // Keep track of last terminal type command + uint8_t prevcmd = 0x00; clear_trace(); set_tracing(true); @@ -288,11 +298,12 @@ void RunMod() { for (;;) { LED_B_OFF(); // Clean receive command buffer - if (!GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len)) { - DbpString(_YELLOW_("!!") "Emulator stopped"); + if (GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len) == false) { + DbpString("Emulator stopped"); retval = PM3_EOPABORTED; break; } + tag_response_info_t *p_response = NULL; LED_B_ON(); @@ -314,46 +325,42 @@ void RunMod() { } } if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { // Received a REQUEST -// DbpString(_YELLOW_("+") "REQUEST Received"); p_response = &responses[RESP_INDEX_ATQA]; } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { // Received a HALT -// DbpString(_YELLOW_("+") "Received a HALT"); p_response = NULL; resp = 0; } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { // Received a WAKEUP -// DbpString(_YELLOW_("+") "WAKEUP Received"); p_response = &responses[RESP_INDEX_ATQA]; resp = 0; } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { // Received request for UID (cascade 1) -// DbpString(_YELLOW_("+") "Request for UID C1"); p_response = &responses[RESP_INDEX_UIDC1]; } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1) -// DbpString(_YELLOW_("+") "Request for SELECT S1"); p_response = &responses[RESP_INDEX_SAKC1]; } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request -// DbpString(_YELLOW_("+") "Request for RATS"); p_response = &responses[RESP_INDEX_RATS]; resp = 1; } else if (receivedCmd[0] == 0xf2 && len == 4) { // ACKed - Time extension - DbpString(_YELLOW_("!!") "Reader accepted time extension!"); + DbpString(_YELLOW_("!!") " Reader accepted time extension!"); p_response = NULL; } else if ((receivedCmd[0] == 0xb2 || receivedCmd[0] == 0xb3) && len == 3) { //NACK - Request more time WTX - DbpString(_YELLOW_("!!") "NACK - time extension request?"); + DbpString(_YELLOW_("!!") " NACK - time extension request?"); if (resp == 2 && lenpacket == 0) { - DbpString(_YELLOW_("!!") "Requesting more time - WTX"); + DbpString(_YELLOW_("!!") " Requesting more time - WTX"); dynamic_response_info.response_n = 2; dynamic_response_info.response[0] = 0xf2; dynamic_response_info.response[1] = 0x0b; // Requesting the maximum amount of time } else if (lenpacket == 0) { - DbpString(_YELLOW_("!!") "NACK - ACK - Resend last command!"); // To burn some time as well + DbpString(_YELLOW_("!!") " NACK - ACK - Resend last command!"); // To burn some time as well dynamic_response_info.response[0] = 0xa3; dynamic_response_info.response_n = 1; } else { - DbpString(_YELLOW_("!!") "Avoiding request - Bluetooth data already in memory!!"); + DbpString(_YELLOW_("!!") " Avoiding request - Bluetooth data already in memory!!"); } } else { - DbpString(_GREEN_("[ ") "Card reader command" _GREEN_(" ]")); - Dbhexdump(len - 2, &receivedCmd[1], false); + if (g_dbglevel == DBG_DEBUG) { + DbpString("[ "_YELLOW_("Card reader command") " ]"); + Dbhexdump(len - 2, &receivedCmd[1], false); + } if ((receivedCmd[0] == 0x02 || receivedCmd[0] == 0x03) && len > 3) { // Process reader commands @@ -379,16 +386,17 @@ void RunMod() { } else { if (lenpacket == 0) { - DbpString(_YELLOW_("!!") "Received unknown command!"); + DbpString(_RED_("Received unknown command!")); memcpy(dynamic_response_info.response, receivedCmd, len); dynamic_response_info.response_n = len; } else { - DbpString(_YELLOW_("!!") "Avoiding unknown command - Bluetooth data already in memory!!"); + DbpString(_YELLOW_("!!") " Avoiding unknown command - Bluetooth data already in memory !!"); } } } + if (dynamic_response_info.response_n > 0) { - DbpString(_GREEN_("[ ") "Proxmark3 answer" _GREEN_(" ]")); + DbpString("[ " _GREEN_("Proxmark3 answer") " ]"); Dbhexdump(dynamic_response_info.response_n, dynamic_response_info.response, false); DbpString("----"); if (lenpacket > 0) { @@ -402,7 +410,7 @@ void RunMod() { if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE) == false) { Dbprintf(_YELLOW_("[ ") "Buffer size: %d "_YELLOW_(" ]"), dynamic_response_info.response_n); SpinDelay(500); - DbpString(_YELLOW_("!!") "Error preparing Proxmark to answer!"); + DbpString(_RED_("Error preparing Proxmark to answer!")); continue; } p_response = &dynamic_response_info; @@ -412,13 +420,15 @@ void RunMod() { EmSendPrecompiledCmd(p_response); } } - switch_off(); + switch_off(); set_tracing(false); BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, retval, NULL, 0); } } - DbpString(_YELLOW_("[=]") "exiting"); + DbpString("Exit standalone mode!"); + DbpString(""); + SpinErr(15, 200, 3); LEDsoff(); } diff --git a/armsrc/Standalone/hf_tcprst.c b/armsrc/Standalone/hf_tcprst.c index 8527ba2f1..952815d60 100644 --- a/armsrc/Standalone/hf_tcprst.c +++ b/armsrc/Standalone/hf_tcprst.c @@ -36,19 +36,19 @@ void ModInfo(void) { /* This standalone implements four different modes: reading, simulating, dumping, & emulating. * * The initial mode is reading with LEDs A & D. -* In this mode, the Proxmark is looking for an ST25TA card like those used by the IKEA Rothult, +* In this mode, the Proxmark3 is looking for an ST25TA card like those used by the IKEA Rothult, * it will act as reader, and store the UID for simulation. * -* If the Proxmark gets an ST25TA UID, it will change to simulation mode (LEDs A & C) automatically. -* During this mode the Proxmark will pretend to be the IKEA Rothult ST25TA master key, upon presentation -* to an IKEA Rothult the Proxmark will steal the 16 byte Read Protection key used to authenticate to the card. +* If the Proxmark3 gets an ST25TA UID, it will change to simulation mode (LEDs A & C) automatically. +* During this mode the Proxmark3 will pretend to be the IKEA Rothult ST25TA master key, upon presentation +* to an IKEA Rothult the Proxmark3 will steal the 16 byte Read Protection key used to authenticate to the card. * -* Once it gets the key, it will switch to dump mode (LEDs C & D) automatically. During this mode the Proxmark +* Once it gets the key, it will switch to dump mode (LEDs C & D) automatically. During this mode the Proxmark3 * will act as a reader once again, but now we know the Read Protection key to authenticate to the card to dump * it's contents so we can achieve full emulation. * * Once it dumps the contents of the card, it will switch to emulation mode (LED C) automatically. -* During this mode the Proxmark should function as the original ST25TA IKEA Rothult Master Key +* During this mode the Proxmark3 should function as the original ST25TA IKEA Rothult Master Key * * Keep pressing the button down will quit the standalone cycle. * diff --git a/armsrc/Standalone/hf_tmudford.c b/armsrc/Standalone/hf_tmudford.c index a945a49ec..0f20a5b86 100644 --- a/armsrc/Standalone/hf_tmudford.c +++ b/armsrc/Standalone/hf_tmudford.c @@ -74,7 +74,7 @@ void RunMod(void) { } } else if (state == STATE_EMUL) { Iso15693InitTag(); - Dbprintf("Starting simulation, press pm3-button to stop and go back to search state."); + Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); // default block size is 4 SimTagIso15693(card.uid, 4); diff --git a/armsrc/Standalone/hf_unisniff.c b/armsrc/Standalone/hf_unisniff.c new file mode 100644 index 000000000..e1882ba8c --- /dev/null +++ b/armsrc/Standalone/hf_unisniff.c @@ -0,0 +1,340 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// HF_UNISNIFF: Integrated 14a/14b/15 sniffer +//----------------------------------------------------------------------------- + +/* + * 'hf_unisniff' integrates existing sniffer functionality for 14a/14b/15a into + * one standalone module. It can sniff to the RAM trace buffer, or if you have + * a PM3 with Flash it will (optionally) save traces to SPIFFS. + * + * You can select which protocol will be sniffed with compile-time flags, or at + * runtime via button presses or a config file in SPIFFS. You can also choose + * whether it will append to the trace file for each sniffing session + * or create new ones. + * + * If the protocol to sniff is configured at compile time or in config file: + * Once the module is launched, it will begin sniffing immediately. + * + * If configured for runtime selection: + * Flashing LED(s) indicate selected sniffer protocol: A=14a, B=14b, A+B=15 + * Short press cycles through options. Long press begins sniffing. + * + * Short-pressing the button again will stop sniffing, with the sniffed data in + * the trace buffer. If you have Flash, and have not set the 'save=none' + * option in the config file, trace data will be saved to SPIFFS. The default + * is to create a new file for each sniffing session, but you may configure it + * to append instead. + * + * Once the data is saved, standalone mode will exit. + * + * LEDs: + * - LED1: sniffing + * - LED2: sniffed tag command, turns off when finished sniffing reader command + * - LED3: sniffed reader command, turns off when finished sniffing tag command + * - LED4: unmounting/sync'ing flash (normally < 100ms) + * + * Config file: 'hf_unisniff.conf' is a plain text file, one option per line. + * Settings here will override the compile-time options. + * + * Currently available options: + * save = [new|append|none] + * new = create a new file with a numbered name for each session. + * append = append to existing file, create if not existing. + * none = do not save to SPIFFS, leave in trace buffer only. + * + * protocol = [14a|14b|15|user] + * which protocol to sniff. If you choose a protocol it will go directly + * to work. If you choose 'user' you may select the protocol at the start + * of each session. + * + * To retrieve trace data from flash: + * + * 1. mem spiffs dump -s hf_unisniff_[protocol]_[number].trace -d hf_unisniff.trace + * Copies trace data file from flash to your PC. + * + * 2. trace load -f hf_unisniff.trace + * Loads trace data from a file into PC-side buffers. + * + * 3. For ISO14a: trace list -t [protocol] -1 + * For MIFARE Classic: trace list -t mf -1 + * + * Lists trace data from buffer without requesting it from PM3. + * + * 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 trace data from flash: + * mem spiffs remove -f [filename] + * + * Caveats / notes: + * - Trace buffer will be cleared on starting stand-alone mode. Data in flash + * will remain unless explicitly deleted. + * - This module will terminate if the trace buffer is full (and save data to + * flash). + * - Like normal sniffing mode, timestamps overflow after 5 min 16 sec. + * However, the trace buffer is sequential, so will be in the correct order. + * + * Mostly this is based on existing code, i.e. the hf_1*sniff modules and dankarmulti. + * I find it handy to have multiprotocol sniffing on the go, and prefer separate trace + * files rather than appends, so here it is. + * + * If you really like navigating menus with one button and some LEDs, it also works + * with dankarmulti :) + * + * Enjoy! + */ + +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "iso14443a.h" +#include "iso14443b.h" +#include "iso15693.h" +#include "iso15.h" +#include "util.h" +#include "commonutil.h" +#include "spiffs.h" +#include "appmain.h" +#include "dbprint.h" +#include "ticks.h" +#include "BigBuf.h" +#include "string.h" + +#undef HF_UNISNIFF_VERBOSE_DEBUG +#define HF_UNISNIFF_PROTOCOL "14a" +#define HF_UNISNIFF_LOGFILE "hf_unisniff" +#define HF_UNISNIFF_LOGEXT ".trace" +#define HF_UNISNIFF_CONFIG "hf_unisniff.conf" +#define HF_UNISNIFF_CONFIG_SIZE 128 + +#define HF_UNISNIFF_PROTOCOLS {"14a","14b","15", "user"} // The logic requires USER be last. +#define HF_UNISNIFF_NUM_PROTOCOLS 4 +#define HF_UNISNIFF_PROTO_14a 0 +#define HF_UNISNIFF_PROTO_14b 1 +#define HF_UNISNIFF_PROTO_15 2 +#define HF_UNISNIFF_PROTO_USER HF_UNISNIFF_NUM_PROTOCOLS-1 + +#define HF_UNISNIFF_SAVE_MODE HF_UNISNIFF_SAVE_MODE_NEW // Default, override in .conf +#define HF_UNISNIFF_SAVE_MODE_NEW 0 +#define HF_UNISNIFF_SAVE_MODE_APPEND 1 +#define HF_UNISNIFF_SAVE_MODE_NONE 2 + +#ifdef WITH_FLASH +static void UniSniff_DownloadTraceInstructions(char *filename) { + Dbprintf(""); + Dbprintf("To get the trace from flash and display it:"); + Dbprintf("1. mem spiffs dump -s %s -d hf_unisniff.trace", filename); + Dbprintf("2. trace load -f hf_unisniff.trace"); + Dbprintf("3. trace list -t [protocol] -1"); +} +#endif + +void ModInfo(void) { + DbpString(" HF UNISNIFF, multimode HF sniffer with optional flashmem & runtime select (hazardousvoltage)"); + Dbprintf(" Compile-time default protocol: %s", HF_UNISNIFF_PROTOCOL); +#ifdef WITH_FLASH + DbpString(" WITH_FLASH support."); +#endif +} + +void RunMod(void) { + char *protocols[] = HF_UNISNIFF_PROTOCOLS; + uint8_t sniff_protocol, default_sniff_protocol; + StandAloneMode(); + + Dbprintf(_YELLOW_("HF UNISNIFF started")); + for (sniff_protocol = 0; sniff_protocol < HF_UNISNIFF_NUM_PROTOCOLS; sniff_protocol++) { + if (!strcmp(protocols[sniff_protocol], HF_UNISNIFF_PROTOCOL)) break; + } + default_sniff_protocol = sniff_protocol; +#ifdef HF_UNISNIFF_VERBOSE_DEBUG + Dbprintf("Compile-time configured protocol: %d", sniff_protocol); +#endif +#ifdef WITH_FLASH + uint8_t save_mode = HF_UNISNIFF_SAVE_MODE; + rdv40_spiffs_lazy_mount(); + // Allocate memory now for buffer for filename to save to. Who knows what'll be + // available after filling the trace buffer. + char *filename = (char *)BigBuf_malloc(64); + if (filename == NULL) { + Dbprintf("failed to allocate memory"); + return; + } + // Read the config file. Size is limited to defined value so as not to consume + // stupid amounts of stack + if (exists_in_spiffs(HF_UNISNIFF_CONFIG)) { + char config_buffer_array[HF_UNISNIFF_CONFIG_SIZE]; + char *config_buffer = &config_buffer_array[0]; + uint32_t config_size = size_in_spiffs(HF_UNISNIFF_CONFIG); + if (config_size > HF_UNISNIFF_CONFIG_SIZE) config_size = HF_UNISNIFF_CONFIG_SIZE; + rdv40_spiffs_read_as_filetype(HF_UNISNIFF_CONFIG, (uint8_t *)config_buffer, + config_size, RDV40_SPIFFS_SAFETY_SAFE); + // This parser is terrible but I think fairly memory efficient? Maybe better to use JSON? + char *x = config_buffer; + char *y = x; + // strip out all the whitespace and Windows line-endings + do { + while (*y == 0x20 || *y == 0x09 || *y == 0x0D) { + ++y; + } + } while ((*x++ = c_tolower(*y++))); + char *token = strchr(config_buffer, '\n'); + while (token != NULL) { + *token++ = '\0'; + char *tag = strtok(config_buffer, "="); + char *value = strtok(NULL, "\n"); + if (tag != NULL && value != NULL) { + if (!strcmp(tag, "protocol")) { + // If we got a selection here, override compile-time selection + uint8_t conf_protocol; + for (conf_protocol = 0; conf_protocol < HF_UNISNIFF_NUM_PROTOCOLS; conf_protocol++) { + if (!strcmp(protocols[conf_protocol], value)) { + sniff_protocol = conf_protocol; + break; + } + } +#ifdef HF_UNISNIFF_VERBOSE_DEBUG + Dbprintf("Run-time configured protocol: %d", conf_protocol); +#endif + } else if (!strcmp(tag, "save")) { + if (!strcmp(value, "append")) save_mode = HF_UNISNIFF_SAVE_MODE_APPEND; + else if (!strcmp(value, "none")) save_mode = HF_UNISNIFF_SAVE_MODE_NONE; + else save_mode = HF_UNISNIFF_SAVE_MODE_NEW; +#ifdef HF_UNISNIFF_VERBOSE_DEBUG + Dbprintf("Run-time configured save_mode: %d", save_mode); +#endif + } + } + config_buffer = token; + token = strchr(config_buffer, '\n'); + } + + } +#endif + + if (sniff_protocol >= HF_UNISNIFF_PROTO_USER) { + Dbprintf("[!] Protocol undefined, going to prompt loop"); + sniff_protocol = default_sniff_protocol; // Default to compile-time setting. + for (;;) { + WDT_HIT(); + if (data_available()) { + BigBuf_free(); + return; + } + if (GetTickCount() & 0x80) + LED(sniff_protocol + 1, 0); + else + LEDsoff(); + + // Was our button held down or pressed? + int button_pressed = BUTTON_HELD(1000); + if (button_pressed == BUTTON_SINGLE_CLICK) { + sniff_protocol++; + if (sniff_protocol >= HF_UNISNIFF_PROTO_USER) sniff_protocol = 0; + SpinDelay(100); + Dbprintf("Selected protocol: '%s'", protocols[sniff_protocol]); + } else if (button_pressed == BUTTON_HOLD) { + Dbprintf("Executing protocol %s", protocols[sniff_protocol]); + for (uint8_t i = 0; i < 4; i++) { + LED(15, 0); + SpinDelay(100); + LEDsoff(); + SpinDelay(100); + } + WAIT_BUTTON_RELEASED(); + SpinDelay(300); + LEDsoff(); + break; + } + } + } + + switch (sniff_protocol) { + case HF_UNISNIFF_PROTO_14a: + SniffIso14443a(0); + break; + case HF_UNISNIFF_PROTO_14b: + SniffIso14443b(); + break; + case HF_UNISNIFF_PROTO_15: + SniffIso15693(0, NULL, false); + break; + default: + Dbprintf("No protocol selected, exiting."); + BigBuf_free(); + LEDsoff(); + return; + } + + Dbprintf("Stopped sniffing"); + SpinDelay(200); + + uint32_t trace_len = BigBuf_get_traceLen(); +#ifndef WITH_FLASH + // Keep stuff in BigBuf for USB/BT dumping + if (trace_len > 0) + Dbprintf("[!] Trace length (bytes) = %u", trace_len); +#else + // Write stuff to spiffs logfile + if (trace_len == 0) { + Dbprintf("[!] Trace buffer is empty, nothing to write!"); + } else if (save_mode == HF_UNISNIFF_SAVE_MODE_NONE) { + Dbprintf("[!] Trace save to flash disabled in config!"); + } else { + Dbprintf("[!] Trace length (bytes) = %u", trace_len); + + uint8_t *trace_buffer = BigBuf_get_addr(); + + sprintf(filename, "%s_%s%s", HF_UNISNIFF_LOGFILE, protocols[sniff_protocol], HF_UNISNIFF_LOGEXT); + if (save_mode == HF_UNISNIFF_SAVE_MODE_NEW) { + uint16_t file_index = 0; + while (exists_in_spiffs(filename)) { + if (file_index++ == 1000) break; + sprintf(filename, "%s_%s-%03d%s", HF_UNISNIFF_LOGFILE, protocols[sniff_protocol], + file_index, HF_UNISNIFF_LOGEXT); + } + if (file_index > 999) { + Dbprintf("[!] Too many files! Trace not saved. Clean up your SPIFFS."); + } else { + rdv40_spiffs_write(filename, trace_buffer, trace_len, RDV40_SPIFFS_SAFETY_SAFE); + Dbprintf("[!] Wrote trace to %s", filename); + } + } else if (save_mode == HF_UNISNIFF_SAVE_MODE_APPEND) { + if (!exists_in_spiffs(filename)) { + rdv40_spiffs_write(filename, trace_buffer, trace_len, RDV40_SPIFFS_SAFETY_SAFE); + Dbprintf("[!] Wrote trace to %s", filename); + } else { + rdv40_spiffs_append(filename, trace_buffer, trace_len, RDV40_SPIFFS_SAFETY_SAFE); + Dbprintf("[!] Appended trace to %s", filename); + } + } + UniSniff_DownloadTraceInstructions(filename); + } + + LED_D_ON(); + rdv40_spiffs_lazy_unmount(); + LED_D_OFF(); + + SpinErr(LED_A, 200, 5); + SpinDelay(100); + BigBuf_free(); +#endif + + Dbprintf("-=[ exit ]=-"); + LEDsoff(); + + return; +} diff --git a/armsrc/appmain.c b/armsrc/appmain.c index f68739da2..520b445d6 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -60,7 +60,11 @@ #include "ticks.h" #include "commonutil.h" #include "crc16.h" - +#include "protocols.h" +#include "mifareutil.h" +#include "sam_picopass.h" +#include "sam_seos.h" +#include "sam_mfc.h" #ifdef WITH_LCD #include "LCD_disabled.h" @@ -365,11 +369,10 @@ static void print_debug_level(void) { // measure the Connection Speed by sending SpeedTestBufferSize bytes to client and measuring the elapsed time. // Note: this mimics GetFromBigbuf(), i.e. we have the overhead of the PacketCommandNG structure included. -static void printConnSpeed(void) { +static void printConnSpeed(uint32_t wait) { DbpString(_CYAN_("Transfer Speed")); Dbprintf(" Sending packets to client..."); -#define CONN_SPEED_TEST_MIN_TIME 500 // in milliseconds uint8_t *test_data = BigBuf_get_addr(); uint32_t start_time = GetTickCount(); uint32_t delta_time = 0; @@ -377,7 +380,7 @@ static void printConnSpeed(void) { LED_B_ON(); - while (delta_time < CONN_SPEED_TEST_MIN_TIME) { + while (delta_time < wait) { reply_ng(CMD_DOWNLOADED_BIGBUF, PM3_SUCCESS, test_data, PM3_CMD_DATA_SIZE); bytes_transferred += PM3_CMD_DATA_SIZE; delta_time = GetTickCountDelta(start_time); @@ -386,13 +389,15 @@ static void printConnSpeed(void) { Dbprintf(" Time elapsed................... %dms", delta_time); Dbprintf(" Bytes transferred.............. %d", bytes_transferred); - Dbprintf(" Transfer Speed PM3 -> Client... " _YELLOW_("%d") " bytes/s", 1000 * bytes_transferred / delta_time); + if (delta_time) { + Dbprintf(" Transfer Speed PM3 -> Client... " _YELLOW_("%llu") " bytes/s", 1000 * (uint64_t)bytes_transferred / delta_time); + } } /** * Prints runtime information about the PM3. **/ -static void SendStatus(void) { +static void SendStatus(uint32_t wait) { BigBuf_print_status(); Fpga_print_status(); #ifdef WITH_FLASH @@ -408,7 +413,7 @@ static void SendStatus(void) { #ifdef WITH_ISO14443a printHf14aConfig(); // HF 14a config #endif - printConnSpeed(); + printConnSpeed(wait); DbpString(_CYAN_("Various")); print_stack_usage(); @@ -427,7 +432,7 @@ static void SendStatus(void) { delta_time = GetTickCountDelta(start_time); if ((delta_time < SLCK_CHECK_MS - 1) || (delta_time > SLCK_CHECK_MS + 1)) { // error > 2% with SLCK_CHECK_MS=50 - Dbprintf(_RED_(" Slow Clock speed change detected, TIA needed")); + Dbprintf(_RED_(" Slow Clock speed change detected, run `hw tia`")); Dbprintf(_YELLOW_(" Slow Clock actual speed seems closer to %d kHz"), (16 * MAINCK / 1000) / mainf * delta_time / SLCK_CHECK_MS); } @@ -782,10 +787,20 @@ static void PacketReceived(PacketCommandNG *packet) { g_reply_via_usb = false; break; } + case CMD_SET_FPGAMODE: { + uint8_t mode = packet->data.asBytes[0]; + if (mode >= FPGA_BITSTREAM_LF && mode <= FPGA_BITSTREAM_HF_15) { + FpgaDownloadAndGo(mode); + reply_ng(CMD_SET_FPGAMODE, PM3_SUCCESS, NULL, 0); + } + reply_ng(CMD_SET_FPGAMODE, PM3_EINVARG, NULL, 0); + break; + } // emulator case CMD_SET_DBGMODE: { g_dbglevel = packet->data.asBytes[0]; - print_debug_level(); + if (packet->length == 1 || packet->data.asBytes[1] != 0) + print_debug_level(); reply_ng(CMD_SET_DBGMODE, PM3_SUCCESS, NULL, 0); break; } @@ -837,13 +852,13 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_LF_ACQ_RAW_ADC: { - struct p { - uint32_t samples : 31; - bool verbose : 1; - } PACKED; - struct p *payload = (struct p *)packet->data.asBytes; - uint32_t bits = SampleLF(payload->verbose, payload->samples, true); - reply_ng(CMD_LF_ACQ_RAW_ADC, PM3_SUCCESS, (uint8_t *)&bits, sizeof(bits)); + lf_sample_payload_t *payload = (lf_sample_payload_t *)packet->data.asBytes; + if (payload->realtime) { + ReadLF_realtime(true); + } else { + uint32_t bits = SampleLF(payload->verbose, payload->samples, true); + reply_ng(CMD_LF_ACQ_RAW_ADC, PM3_SUCCESS, (uint8_t *)&bits, sizeof(bits)); + } break; } case CMD_LF_MOD_THEN_ACQ_RAW_ADC: { @@ -866,14 +881,13 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_LF_SNIFF_RAW_ADC: { - struct p { - uint32_t samples : 31; - bool verbose : 1; - } PACKED; - struct p *payload = (struct p *)packet->data.asBytes; - - uint32_t bits = SniffLF(payload->verbose, payload->samples, true); - reply_ng(CMD_LF_SNIFF_RAW_ADC, PM3_SUCCESS, (uint8_t *)&bits, sizeof(bits)); + lf_sample_payload_t *payload = (lf_sample_payload_t *)packet->data.asBytes; + if (payload->realtime) { + ReadLF_realtime(false); + } else { + uint32_t bits = SniffLF(payload->verbose, payload->samples, true); + reply_ng(CMD_LF_SNIFF_RAW_ADC, PM3_SUCCESS, (uint8_t *)&bits, sizeof(bits)); + } break; } case CMD_LF_HID_WATCH: { @@ -1143,27 +1157,27 @@ static void PacketReceived(PacketCommandNG *packet) { #ifdef WITH_EM4x50 case CMD_LF_EM4X50_INFO: { - em4x50_info((em4x50_data_t *)packet->data.asBytes, true); + em4x50_info((const em4x50_data_t *)packet->data.asBytes, true); break; } case CMD_LF_EM4X50_WRITE: { - em4x50_write((em4x50_data_t *)packet->data.asBytes, true); + em4x50_write((const em4x50_data_t *)packet->data.asBytes, true); break; } case CMD_LF_EM4X50_WRITEPWD: { - em4x50_writepwd((em4x50_data_t *)packet->data.asBytes, true); + em4x50_writepwd((const em4x50_data_t *)packet->data.asBytes, true); break; } case CMD_LF_EM4X50_READ: { - em4x50_read((em4x50_data_t *)packet->data.asBytes, true); + em4x50_read((const em4x50_data_t *)packet->data.asBytes, true); break; } case CMD_LF_EM4X50_BRUTE: { - em4x50_brute((em4x50_data_t *)packet->data.asBytes, true); + em4x50_brute((const em4x50_data_t *)packet->data.asBytes, true); break; } case CMD_LF_EM4X50_LOGIN: { - em4x50_login((uint32_t *)packet->data.asBytes, true); + em4x50_login((const uint32_t *)packet->data.asBytes, true); break; } case CMD_LF_EM4X50_SIM: { @@ -1173,7 +1187,7 @@ static void PacketReceived(PacketCommandNG *packet) { // destroy the Emulator Memory. //----------------------------------------------------------------------------- FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - em4x50_sim((uint32_t *)packet->data.asBytes, true); + em4x50_sim((const uint32_t *)packet->data.asBytes, true); break; } case CMD_LF_EM4X50_READER: { @@ -1197,7 +1211,7 @@ static void PacketReceived(PacketCommandNG *packet) { // destroy the Emulator Memory. //----------------------------------------------------------------------------- FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - em4x50_chk((uint8_t *)packet->data.asBytes, true); + em4x50_chk((const char *)packet->data.asBytes, true); break; } #endif @@ -1277,7 +1291,7 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t data[]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - EmlSetMemIso15693(payload->count, payload->data, payload->offset); + emlSet(payload->data, payload->offset, payload->count); break; } case CMD_HF_ISO15693_SIMULATE: { @@ -1415,7 +1429,7 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t blockno; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - ReadSTBlock(payload->blockno); + read_14b_st_block(payload->blockno); break; } case CMD_HF_ISO14443B_SNIFF: { @@ -1556,7 +1570,9 @@ static void PacketReceived(PacketCommandNG *packet) { } case CMD_HF_MIFARE_READBL: { mf_readblock_t *payload = (mf_readblock_t *)packet->data.asBytes; - MifareReadBlock(payload->blockno, payload->keytype, payload->key); + uint8_t outbuf[16]; + int16_t retval = mifare_cmd_readblocks(MIFARE_AUTH_KEYA + (payload->keytype & 1), payload->key, ISO14443A_CMD_READBLOCK, payload->blockno, 1, outbuf); + reply_ng(CMD_HF_MIFARE_READBL, retval, outbuf, sizeof(outbuf)); break; } case CMD_HF_MIFAREU_READBL: { @@ -1580,7 +1596,19 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFARE_WRITEBL: { - MifareWriteBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); + uint8_t block_no = packet->oldarg[0]; + uint8_t key_type = packet->oldarg[1]; + uint8_t *key = packet->data.asBytes; + uint8_t *block_data = packet->data.asBytes + 10; + + int16_t retval = mifare_cmd_writeblocks(MIFARE_AUTH_KEYA + (key_type & 1), key, ISO14443A_CMD_WRITEBLOCK, block_no, 1, block_data); + + // convert ng style retval to old status + if (retval >= 0) { + retval = 1; + } + + reply_mix(CMD_ACK, retval, 0, 0, 0, 0); break; } case CMD_HF_MIFARE_VALUE: { @@ -1659,6 +1687,7 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_HF_MIFARE_EML_MEMCLR: { MifareEMemClr(); reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); break; } case CMD_HF_MIFARE_EML_MEMSET: { @@ -1669,7 +1698,14 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t data[]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - MifareEMemSet(payload->blockno, payload->blockcnt, payload->blockwidth, payload->data); + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // backwards compat... default bytewidth + if (payload->blockwidth == 0) + payload->blockwidth = 16; + + emlSetMem_xt(payload->data, payload->blockno, payload->blockcnt, payload->blockwidth); break; } case CMD_HF_MIFARE_EML_MEMGET: { @@ -1696,8 +1732,15 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFARE_CIDENT: { - bool is_mfc = packet->data.asBytes[0]; - MifareCIdent(is_mfc); + + + struct p { + uint8_t is_mfc; + uint8_t keytype; + uint8_t key[6]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareCIdent(payload->is_mfc, payload->keytype, payload->key); break; } // Gen 3 magic cards @@ -1740,7 +1783,9 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t key[6]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - MifareReadConfigBlockGDM(payload->key); + uint8_t outbuf[16]; + int16_t retval = mifare_cmd_readblocks(MIFARE_MAGIC_GDM_AUTH_KEY, payload->key, MIFARE_MAGIC_GDM_READ_CFG, 0, 1, outbuf); + reply_ng(CMD_HF_MIFARE_G4_GDM_CONFIG, retval, outbuf, sizeof(outbuf)); break; } case CMD_HF_MIFARE_G4_GDM_WRCFG: { @@ -1748,18 +1793,20 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t data[16]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - MifareWriteConfigBlockGDM(payload->data); + uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + int16_t retval = mifare_cmd_writeblocks(MIFARE_MAGIC_GDM_AUTH_KEY, key, MIFARE_MAGIC_GDM_WRITE_CFG, 0, 1, payload->data); + reply_ng(CMD_HF_MIFARE_G4_GDM_WRCFG, retval, NULL, 0); break; } case CMD_HF_MIFARE_G4_GDM_WRBL: { struct p { uint8_t blockno; - uint8_t keytype; uint8_t key[6]; uint8_t data[16]; // data to be written } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - MifareWriteBlockGDM(payload->blockno, payload->keytype, payload->key, payload->data); + int16_t retval = mifare_cmd_writeblocks(MIFARE_MAGIC_GDM_AUTH_KEY, payload->key, MIFARE_MAGIC_GDM_WRITEBLOCK, payload->blockno, 1, payload->data); + reply_ng(CMD_HF_MIFARE_G4_GDM_WRBL, retval, NULL, 0); break; } case CMD_HF_MIFARE_PERSONALIZE_UID: { @@ -1826,6 +1873,17 @@ static void PacketReceived(PacketCommandNG *packet) { MifareHasStaticNonce(); break; } + case CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE: { + struct p { + uint8_t block_no; + uint8_t key_type; + uint8_t key[6]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + + MifareHasStaticEncryptedNonce(payload->block_no, payload->key_type, payload->key); + break; + } #endif #ifdef WITH_NFCBARCODE @@ -1904,6 +1962,10 @@ static void PacketReceived(PacketCommandNG *packet) { iClass_Restore((iclass_restore_req_t *)packet->data.asBytes); break; } + case CMD_HF_ICLASS_CREDIT_EPURSE: { + iclass_credit_epurse((iclass_credit_epurse_t *)packet->data.asBytes); + break; + } #endif #ifdef WITH_HFSNIFF @@ -1998,6 +2060,21 @@ static void PacketReceived(PacketCommandNG *packet) { fwdata = NULL; break; } + + case CMD_HF_SAM_PICOPASS: { + sam_picopass_get_pacs(); + break; + } + case CMD_HF_SAM_SEOS: { +// sam_seos_get_pacs(); + break; + } + + case CMD_HF_SAM_MFC: { +// sam_mfc_get_pacs(); + break; + } + #endif #ifdef WITH_FPC_USART @@ -2014,11 +2091,16 @@ static void PacketReceived(PacketCommandNG *packet) { uint32_t waittime; } PACKED; struct p *payload = (struct p *) &packet->data.asBytes; + uint16_t available; uint16_t pre_available = 0; uint8_t *dest = BigBuf_malloc(USART_FIFOLEN); uint32_t wait = payload->waittime; + + StartTicks(); + uint32_t ti = GetTickCount(); + while (true) { WaitMS(50); available = usart_rxdata_available(); @@ -2039,6 +2121,8 @@ static void PacketReceived(PacketCommandNG *packet) { } else { reply_ng(CMD_USART_RX, PM3_ENODATA, NULL, 0); } + + StopTicks(); BigBuf_free(); LED_B_OFF(); break; @@ -2051,11 +2135,16 @@ static void PacketReceived(PacketCommandNG *packet) { } PACKED; struct p *payload = (struct p *) &packet->data.asBytes; usart_writebuffer_sync(payload->data, packet->length - sizeof(payload)); + uint16_t available; uint16_t pre_available = 0; uint8_t *dest = BigBuf_malloc(USART_FIFOLEN); uint32_t wait = payload->waittime; + + StartTicks(); + uint32_t ti = GetTickCount(); + while (true) { WaitMS(50); available = usart_rxdata_available(); @@ -2070,12 +2159,15 @@ static void PacketReceived(PacketCommandNG *packet) { if (GetTickCountDelta(ti) > wait) break; } + if (available > 0) { uint16_t len = usart_read_ng(dest, available); reply_ng(CMD_USART_TXRX, PM3_SUCCESS, dest, len); } else { reply_ng(CMD_USART_TXRX, PM3_ENODATA, NULL, 0); } + + StopTicks(); BigBuf_free(); LED_B_OFF(); break; @@ -2112,8 +2204,9 @@ static void PacketReceived(PacketCommandNG *packet) { reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_SUCCESS, NULL, 0); break; case 2: - if (button_status == BUTTON_SINGLE_CLICK) + if (button_status == BUTTON_SINGLE_CLICK) { reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_EOPABORTED, NULL, 0); + } uint16_t volt = MeasureAntennaTuningHfData(); reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_SUCCESS, (uint8_t *)&volt, sizeof(volt)); break; @@ -2140,8 +2233,9 @@ static void PacketReceived(PacketCommandNG *packet) { reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_SUCCESS, NULL, 0); break; case 2: - if (button_status == BUTTON_SINGLE_CLICK) + if (button_status == BUTTON_SINGLE_CLICK) { reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_EOPABORTED, NULL, 0); + } uint32_t volt = MeasureAntennaTuningLfData(); reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_SUCCESS, (uint8_t *)&volt, sizeof(volt)); @@ -2465,6 +2559,9 @@ static void PacketReceived(PacketCommandNG *packet) { Flash_WriteEnable(); Flash_Erase4k(3, 0xC); } else if (payload->startidx == DEFAULT_MF_KEYS_OFFSET) { + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); + Flash_Erase4k(3, 0x8); Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(3, 0x9); @@ -2587,7 +2684,10 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_STATUS: { - SendStatus(); + if (packet->length == 4) + SendStatus(packet->data.asDwords[0]); + else + SendStatus(CONN_SPEED_TEST_MIN_TIME_DEFAULT); break; } case CMD_TIA: { @@ -2669,6 +2769,7 @@ void __attribute__((noreturn)) AppMain(void) { SpinDelay(100); BigBuf_initialize(); + // Add stack canary for (uint32_t *p = _stack_start; p + 0x200 < _stack_end ; ++p) { *p = 0xdeadbeef; } @@ -2718,9 +2819,6 @@ void __attribute__((noreturn)) AppMain(void) { } #endif -#ifdef WITH_FPC_USART - usart_init(USART_BAUD_RATE, USART_PARITY); -#endif #ifdef WITH_FLASH // If flash is not present, BUSY_TIMEOUT kicks in, let's do it after USB @@ -2733,20 +2831,27 @@ void __attribute__((noreturn)) AppMain(void) { rdv40_spiffs_check(); #endif +#ifdef WITH_FPC_USART + usart_init(USART_BAUD_RATE, USART_PARITY); +#endif + + allow_send_wtx = true; + // This is made as late as possible to ensure enumeration without timeout // against device such as http://www.hobbytronics.co.uk/usb-host-board-v2 // In other words, keep the interval between usb_enable() and the main loop as short as possible. // (AT91F_CDC_Enumerate() will be called in the main loop) usb_disable(); usb_enable(); - allow_send_wtx = true; for (;;) { WDT_HIT(); if (*_stack_start != 0xdeadbeef) { - Dbprintf("Stack overflow detected! Please increase stack size, currently %d bytes", (uint32_t)_stack_end - (uint32_t)_stack_start); - Dbprintf("Unplug your device now."); + Dbprintf("DEBUG: increase stack size, currently " _YELLOW_("%d") " bytes", (uint32_t)_stack_end - (uint32_t)_stack_start); + Dbprintf("Stack overflow detected"); + Dbprintf("--> Unplug your device now! <--"); + hf_field_off(); while (1); } diff --git a/armsrc/appmain.h b/armsrc/appmain.h index 815245b04..fdc520f5f 100644 --- a/armsrc/appmain.h +++ b/armsrc/appmain.h @@ -37,6 +37,9 @@ int tearoff_hook(void); // ADC Vref = 3300mV, (240k-10M):240k voltage divider, 140800 mV #define MAX_ADC_LF_VOLTAGE 140800 +// Default connection speed test timeout, used in hw status +#define CONN_SPEED_TEST_MIN_TIME_DEFAULT 500 // in milliseconds + extern int ToSendMax; extern uint8_t ToSend[]; diff --git a/armsrc/cmd.c b/armsrc/cmd.c index c179ed2a9..ee2565cd2 100644 --- a/armsrc/cmd.c +++ b/armsrc/cmd.c @@ -18,6 +18,7 @@ #include "usart.h" #include "crc16.h" #include "string.h" +#include "BigBuf.h" // Flags to tell where to add CRC on sent replies bool g_reply_with_crc_on_usb = false; @@ -26,11 +27,11 @@ bool g_reply_with_crc_on_fpc = true; bool g_reply_via_fpc = false; bool g_reply_via_usb = false; -int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len) { +int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len) { PacketResponseOLD txcmd = {CMD_UNKNOWN, {0, 0, 0}, {{0}}}; -// for (size_t i = 0; i < sizeof(PacketResponseOLD); i++) -// ((uint8_t *)&txcmd)[i] = 0x00; + for (size_t i = 0; i < sizeof(PacketResponseOLD); i++) + ((uint8_t *)&txcmd)[i] = 0x00; // Compose the outgoing command frame txcmd.cmd = cmd; @@ -42,7 +43,7 @@ int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *d if (data && len) { len = MIN(len, PM3_CMD_DATA_SIZE); for (size_t i = 0; i < len; i++) { - txcmd.d.asBytes[i] = ((uint8_t *)data)[i]; + txcmd.d.asBytes[i] = ((const uint8_t *)data)[i]; } } @@ -135,7 +136,7 @@ int reply_ng(uint16_t cmd, int16_t status, const uint8_t *data, size_t len) { return reply_ng_internal(cmd, status, data, len, true); } -int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len) { +int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len) { int16_t status = PM3_SUCCESS; uint64_t arg[3] = {arg0, arg1, arg2}; if (len > PM3_CMD_DATA_SIZE - sizeof(arg)) { @@ -147,7 +148,8 @@ int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *d if (len && data) memcpy(cmddata + sizeof(arg), data, (int)len); - return reply_ng_internal((cmd & 0xFFFF), status, cmddata, len + sizeof(arg), false); + int res = reply_ng_internal((cmd & 0xFFFF), status, cmddata, len + sizeof(arg), false); + return res; } static int receive_ng_internal(PacketCommandNG *rx, uint32_t read_ng(uint8_t *data, size_t len), bool usb, bool fpc) { @@ -178,9 +180,10 @@ static int receive_ng_internal(PacketCommandNG *rx, uint32_t read_ng(uint8_t *da memcpy(rx->data.asBytes, rx_raw.data, length); rx->length = length; } else { - uint64_t arg[3]; - if (length < sizeof(arg)) + uint64_t arg[3] = {0}; + if (length < sizeof(arg)) { return PM3_EIO; + } memcpy(arg, rx_raw.data, sizeof(arg)); rx->oldarg[0] = arg[0]; diff --git a/armsrc/cmd.h b/armsrc/cmd.h index b578db8e8..22a79ce16 100644 --- a/armsrc/cmd.h +++ b/armsrc/cmd.h @@ -27,9 +27,9 @@ extern bool g_reply_with_crc_on_fpc; extern bool g_reply_via_fpc; extern bool g_reply_via_usb; -int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); +int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len); int reply_ng(uint16_t cmd, int16_t status, const uint8_t *data, size_t len); -int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); +int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, const void *data, size_t len); int receive_ng(PacketCommandNG *rx); #endif // _PROXMARK_CMD_H_ diff --git a/armsrc/dbprint.c b/armsrc/dbprint.c index 7be7bba4c..afb83da4c 100644 --- a/armsrc/dbprint.c +++ b/armsrc/dbprint.c @@ -22,7 +22,7 @@ #include "printf.h" #define DEBUG 1 - +#define DEBUG_MAX_MSG_SIZE 200 //============================================================================= // Debug print functions, to go out over USB, to the usual PC-side client. //============================================================================= @@ -31,7 +31,7 @@ void DbpStringEx(uint32_t flags, const char *src, size_t srclen) { #if DEBUG struct { uint16_t flag; - uint8_t buf[PM3_CMD_DATA_SIZE - sizeof(uint16_t)]; + uint8_t buf[DEBUG_MAX_MSG_SIZE]; } PACKED data; data.flag = flags; uint16_t len = MIN(srclen, sizeof(data.buf)); @@ -49,7 +49,7 @@ void DbpString(const char *str) { void DbprintfEx(uint32_t flags, const char *fmt, ...) { #if DEBUG // should probably limit size here; oh well, let's just use a big buffer - char s[PM3_CMD_DATA_SIZE] = {0x00}; + char s[DEBUG_MAX_MSG_SIZE] = {0x00}; va_list ap; va_start(ap, fmt); kvsprintf(fmt, s, 10, ap); @@ -62,7 +62,7 @@ void DbprintfEx(uint32_t flags, const char *fmt, ...) { void Dbprintf(const char *fmt, ...) { #if DEBUG // should probably limit size here; oh well, let's just use a big buffer - char output_string[PM3_CMD_DATA_SIZE] = {0x00}; + char output_string[DEBUG_MAX_MSG_SIZE] = {0x00}; va_list ap; va_start(ap, fmt); @@ -74,13 +74,13 @@ void Dbprintf(const char *fmt, ...) { } // prints HEX & ASCII -void Dbhexdump(int len, uint8_t *d, bool bAsci) { +void Dbhexdump(int len, const uint8_t *d, bool bAsci) { #if DEBUG - char ascii[9]; + char ascii[17]; while (len > 0) { - int l = (len > 8) ? 8 : len; + int l = (len > 16) ? 16 : len; memcpy(ascii, d, l); ascii[l] = 0; @@ -97,33 +97,41 @@ void Dbhexdump(int len, uint8_t *d, bool bAsci) { else Dbprintf("%*D", l, d, " "); - len -= 8; - d += 8; + len -= 16; + d += 16; } #endif } -void print_result(const char *name, uint8_t *buf, size_t len) { +void print_result(const char *name, const uint8_t *d, size_t n) { - uint8_t *p = buf; - uint16_t tmp = len & 0xFFF0; + const uint8_t *p = d; + uint16_t tmp = n & 0xFFF0; - for (; p - buf < tmp; p += 16) { + for (; p - d < tmp; p += 16) { Dbprintf("[%s: %02d/%02d] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", name, - p - buf, - len, + p - d, + n, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15] ); } - if (len % 16 != 0) { + + if (n % 16 != 0) { char s[46] = {0}; char *sp = s; - for (; p - buf < len; p++) { + for (; p - d < n; p++) { sprintf(sp, "%02x ", p[0]); sp += 3; } - Dbprintf("[%s: %02d/%02d] %s", name, p - buf, len, s); + Dbprintf("[%s: %02d/%02d] %s", name, p - d, n, s); + } +} + +// Prints message and hexdump +void print_dbg(char *msg, uint8_t *d, uint16_t n) { + if (g_dbglevel == DBG_DEBUG) { + print_result(msg, d, n); } } diff --git a/armsrc/dbprint.h b/armsrc/dbprint.h index b1a7753da..4bfa6c89f 100644 --- a/armsrc/dbprint.h +++ b/armsrc/dbprint.h @@ -22,40 +22,13 @@ #include "common.h" #include "ansi.h" -#define Dbprintf_usb(...) {\ - bool tmpfpc = g_reply_via_fpc;\ - bool tmpusb = g_reply_via_usb;\ - g_reply_via_fpc = false;\ - g_reply_via_usb = true;\ - Dbprintf(__VA_ARGS__);\ - g_reply_via_fpc = tmpfpc;\ - g_reply_via_usb = tmpusb;} - -#define Dbprintf_fpc(...) {\ - bool tmpfpc = g_reply_via_fpc;\ - bool tmpusb = g_reply_via_usb;\ - g_reply_via_fpc = true;\ - g_reply_via_usb = false;\ - Dbprintf(__VA_ARGS__);\ - g_reply_via_fpc = tmpfpc;\ - g_reply_via_usb = tmpusb;} - -#define Dbprintf_all(...) {\ - bool tmpfpc = g_reply_via_fpc;\ - bool tmpusb = g_reply_via_usb;\ - g_reply_via_fpc = true;\ - g_reply_via_usb = true;\ - Dbprintf(__VA_ARGS__);\ - g_reply_via_fpc = tmpfpc;\ - g_reply_via_usb = tmpusb;} - - void DbpString(const char *str); void DbpStringEx(uint32_t flags, const char *src, size_t srclen); void Dbprintf(const char *fmt, ...); void DbprintfEx(uint32_t flags, const char *fmt, ...); -void Dbhexdump(int len, uint8_t *d, bool bAsci); -void print_result(const char *name, uint8_t *buf, size_t len); +void Dbhexdump(int len, const uint8_t *d, bool bAsci); +void print_result(const char *name, const uint8_t *buf, size_t len); +void print_dbg(char *msg, uint8_t *d, uint16_t n); //void PrintToSendBuffer(void); #endif diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 84cae8aed..6b90b079b 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -634,7 +634,7 @@ static int login(uint32_t password) { } // searching for password using chosen bruteforce algorithm -static bool brute(em4x50_data_t *etd, uint32_t *pwd) { +static bool brute(const em4x50_data_t *etd, uint32_t *pwd) { generator_context_t ctx; bool pwd_found = false; @@ -698,7 +698,7 @@ static bool brute(em4x50_data_t *etd, uint32_t *pwd) { } // login into EM4x50 -void em4x50_login(uint32_t *password, bool ledcontrol) { +void em4x50_login(const uint32_t *password, bool ledcontrol) { em4x50_setup_read(); int status = PM3_EFAILED; @@ -717,7 +717,7 @@ void em4x50_login(uint32_t *password, bool ledcontrol) { } // invoke password search -void em4x50_brute(em4x50_data_t *etd, bool ledcontrol) { +void em4x50_brute(const em4x50_data_t *etd, bool ledcontrol) { em4x50_setup_read(); bool bsuccess = false; @@ -737,7 +737,7 @@ void em4x50_brute(em4x50_data_t *etd, bool ledcontrol) { } // check passwords from dictionary content in flash memory -void em4x50_chk(uint8_t *filename, bool ledcontrol) { +void em4x50_chk(const char *filename, bool ledcontrol) { int status = PM3_EFAILED; uint32_t pwd = 0x0; @@ -747,11 +747,11 @@ void em4x50_chk(uint8_t *filename, bool ledcontrol) { int changed = rdv40_spiffs_lazy_mount(); uint16_t pwd_count = 0; - uint32_t size = size_in_spiffs((char *)filename); + uint32_t size = size_in_spiffs(filename); pwd_count = size / 4; uint8_t *pwds = BigBuf_malloc(size); - rdv40_spiffs_read_as_filetype((char *)filename, pwds, size, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_read_as_filetype(filename, pwds, size, RDV40_SPIFFS_SAFETY_SAFE); if (changed) rdv40_spiffs_lazy_unmount(); @@ -877,7 +877,7 @@ 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 ledcontrol) { +void em4x50_read(const em4x50_data_t *etd, bool ledcontrol) { int status = PM3_EFAILED; uint32_t words[EM4X50_NO_WORDS] = {0x0}; @@ -910,7 +910,7 @@ void em4x50_read(em4x50_data_t *etd, bool ledcontrol) { } // collects as much information as possible via selective read mode -void em4x50_info(em4x50_data_t *etd, bool ledcontrol) { +void em4x50_info(const em4x50_data_t *etd, bool ledcontrol) { int status = PM3_EFAILED; uint32_t words[EM4X50_NO_WORDS] = {0x0}; @@ -1057,7 +1057,7 @@ static int write_password(uint32_t password, uint32_t new_password) { // write operation process for EM4x50 tag, // single word is written to given address, verified by selective read operation // wrong password -> return with PM3_EFAILED -void em4x50_write(em4x50_data_t *etd, bool ledcontrol) { +void em4x50_write(const em4x50_data_t *etd, bool ledcontrol) { int status = PM3_EFAILED; uint32_t words[EM4X50_NO_WORDS] = {0x0}; @@ -1117,7 +1117,7 @@ void em4x50_write(em4x50_data_t *etd, bool ledcontrol) { } // simple change of password -void em4x50_writepwd(em4x50_data_t *etd, bool ledcontrol) { +void em4x50_writepwd(const em4x50_data_t *etd, bool ledcontrol) { int status = PM3_EFAILED; em4x50_setup_read(); @@ -1362,7 +1362,7 @@ static bool em4x50_sim_read_word(uint32_t *word) { } // check if reader requests receive mode (rm) by sending two zeros -static int check_rm_request(uint32_t *tag, bool ledcontrol) { +static int check_rm_request(const uint32_t *tag, bool ledcontrol) { // look for first zero int bit = em4x50_sim_read_bit(); @@ -1391,7 +1391,7 @@ static int check_rm_request(uint32_t *tag, bool ledcontrol) { } // send single listen window in simulation mode -static int em4x50_sim_send_listen_window(uint32_t *tag, bool ledcontrol) { +static int em4x50_sim_send_listen_window(const uint32_t *tag, bool ledcontrol) { SHORT_COIL(); wait_cycles(EM4X50_T_TAG_HALF_PERIOD); @@ -1464,7 +1464,7 @@ static void em4x50_sim_send_nak(void) { } // standard read mode process (simulation mode) -static int em4x50_sim_handle_standard_read_command(uint32_t *tag, bool ledcontrol) { +static int em4x50_sim_handle_standard_read_command(const uint32_t *tag, bool ledcontrol) { // extract control data int fwr = reflect32(tag[EM4X50_CONTROL]) & 0xFF; // first word read @@ -1504,7 +1504,7 @@ static int em4x50_sim_handle_standard_read_command(uint32_t *tag, bool ledcontro } // selective read mode process (simulation mode) -static int em4x50_sim_handle_selective_read_command(uint32_t *tag, bool ledcontrol) { +static int em4x50_sim_handle_selective_read_command(const uint32_t *tag, bool ledcontrol) { // read password uint32_t address = 0; @@ -1559,7 +1559,7 @@ static int em4x50_sim_handle_selective_read_command(uint32_t *tag, bool ledcontr } // login process (simulation mode) -static int em4x50_sim_handle_login_command(uint32_t *tag, bool ledcontrol) { +static int em4x50_sim_handle_login_command(const uint32_t *tag, bool ledcontrol) { // read password uint32_t password = 0; @@ -1585,7 +1585,7 @@ static int em4x50_sim_handle_login_command(uint32_t *tag, bool ledcontrol) { } // reset process (simulation mode) -static int em4x50_sim_handle_reset_command(uint32_t *tag, bool ledcontrol) { +static int em4x50_sim_handle_reset_command(const uint32_t *tag, bool ledcontrol) { // processing pause time (corresponds to a "1" bit) em4x50_sim_send_bit(1); @@ -1810,7 +1810,7 @@ void em4x50_handle_commands(int *command, uint32_t *tag, bool ledcontrol) { // 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, bool ledcontrol) { +void em4x50_sim(const uint32_t *password, bool ledcontrol) { int command = PM3_ENODATA; diff --git a/armsrc/em4x50.h b/armsrc/em4x50.h index af3a7a8db..eb52c36e5 100644 --- a/armsrc/em4x50.h +++ b/armsrc/em4x50.h @@ -21,20 +21,21 @@ #include "../include/em4x50.h" +// used by standalone mode 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, bool ledcontrol); -void em4x50_info(em4x50_data_t *etd, bool ledcontrol); -void em4x50_write(em4x50_data_t *etd, bool ledcontrol); -void em4x50_writepwd(em4x50_data_t *etd, bool ledcontrol); -void em4x50_read(em4x50_data_t *etd, bool ledcontrol); -void em4x50_brute(em4x50_data_t *etd, bool ledcontrol); -void em4x50_login(uint32_t *password, bool ledcontrol); -void em4x50_sim(uint32_t *password, bool ledcontrol); +// dispatch functions (appmain.c) +void em4x50_info(const em4x50_data_t *etd, bool ledcontrol); +void em4x50_write(const em4x50_data_t *etd, bool ledcontrol); +void em4x50_writepwd(const em4x50_data_t *etd, bool ledcontrol); +void em4x50_read(const em4x50_data_t *etd, bool ledcontrol); +void em4x50_brute(const em4x50_data_t *etd, bool ledcontrol); +void em4x50_login(const uint32_t *password, bool ledcontrol); +void em4x50_sim(const uint32_t *password, bool ledcontrol); void em4x50_reader(bool ledcontrol); -void em4x50_chk(uint8_t *filename, bool ledcontrol); +void em4x50_chk(const char *filename, bool ledcontrol); #endif /* EM4X50_H */ diff --git a/armsrc/em4x70.c b/armsrc/em4x70.c index 806442f7f..40eadb890 100644 --- a/armsrc/em4x70.c +++ b/armsrc/em4x70.c @@ -310,6 +310,7 @@ static bool check_ack(void) { return false; } +// TODO: define and use structs for rnd, frnd, response static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *response) { if (find_listen_window(true)) { @@ -350,8 +351,10 @@ static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *respon return PM3_ESOFT; } -static int set_byte(uint8_t *target, int value) { - int c = value > 0xFF; +// Sets one (reflected) byte and returns carry bit +// (1 if `value` parameter was greater than 0xFF) +static int set_byte(uint8_t *target, uint16_t value) { + int c = value > 0xFF ? 1 : 0; // be explicit about carry bit values *target = reflect8(value); return c; } @@ -373,8 +376,8 @@ static int bruteforce(const uint8_t address, const uint8_t *rnd, const uint8_t * uint16_t rev_k = reflect16(k); switch (address) { case 9: - c = set_byte(&temp_rnd[0], rev_rnd[0] + (rev_k & 0xFF)); - c = set_byte(&temp_rnd[1], rev_rnd[1] + c + ((rev_k >> 8) & 0xFF)); + c = set_byte(&temp_rnd[0], rev_rnd[0] + ((rev_k) & 0xFFu)); + c = set_byte(&temp_rnd[1], rev_rnd[1] + c + ((rev_k >> 8) & 0xFFu)); c = set_byte(&temp_rnd[2], rev_rnd[2] + c); c = set_byte(&temp_rnd[3], rev_rnd[3] + c); c = set_byte(&temp_rnd[4], rev_rnd[4] + c); @@ -383,16 +386,16 @@ static int bruteforce(const uint8_t address, const uint8_t *rnd, const uint8_t * break; case 8: - c = set_byte(&temp_rnd[2], rev_rnd[2] + (rev_k & 0xFF)); - c = set_byte(&temp_rnd[3], rev_rnd[3] + c + ((rev_k >> 8) & 0xFF)); + c = set_byte(&temp_rnd[2], rev_rnd[2] + ((rev_k) & 0xFFu)); + c = set_byte(&temp_rnd[3], rev_rnd[3] + c + ((rev_k >> 8) & 0xFFu)); c = set_byte(&temp_rnd[4], rev_rnd[4] + c); c = set_byte(&temp_rnd[5], rev_rnd[5] + c); set_byte(&temp_rnd[6], rev_rnd[6] + c); break; case 7: - c = set_byte(&temp_rnd[4], rev_rnd[4] + (rev_k & 0xFF)); - c = set_byte(&temp_rnd[5], rev_rnd[5] + c + ((rev_k >> 8) & 0xFF)); + c = set_byte(&temp_rnd[4], rev_rnd[4] + ((rev_k) & 0xFFu)); + c = set_byte(&temp_rnd[5], rev_rnd[5] + c + ((rev_k >> 8) & 0xFFu)); set_byte(&temp_rnd[6], rev_rnd[6] + c); break; @@ -707,7 +710,7 @@ static int em4x70_receive(uint8_t *bits, size_t length) { return bit_pos; } -void em4x70_info(em4x70_data_t *etd, bool ledcontrol) { +void em4x70_info(const em4x70_data_t *etd, bool ledcontrol) { uint8_t status = 0; @@ -728,7 +731,7 @@ void em4x70_info(em4x70_data_t *etd, bool ledcontrol) { reply_ng(CMD_LF_EM4X70_INFO, status, tag.data, sizeof(tag.data)); } -void em4x70_write(em4x70_data_t *etd, bool ledcontrol) { +void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) { uint8_t status = 0; @@ -758,7 +761,7 @@ void em4x70_write(em4x70_data_t *etd, bool ledcontrol) { reply_ng(CMD_LF_EM4X70_WRITE, status, tag.data, sizeof(tag.data)); } -void em4x70_unlock(em4x70_data_t *etd, bool ledcontrol) { +void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) { uint8_t status = 0; @@ -791,7 +794,7 @@ void em4x70_unlock(em4x70_data_t *etd, bool ledcontrol) { reply_ng(CMD_LF_EM4X70_UNLOCK, status, tag.data, sizeof(tag.data)); } -void em4x70_auth(em4x70_data_t *etd, bool ledcontrol) { +void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) { uint8_t status = 0; uint8_t response[3] = {0}; @@ -813,7 +816,7 @@ void em4x70_auth(em4x70_data_t *etd, bool ledcontrol) { reply_ng(CMD_LF_EM4X70_AUTH, status, response, sizeof(response)); } -void em4x70_brute(em4x70_data_t *etd, bool ledcontrol) { +void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) { uint8_t status = 0; uint8_t response[2] = {0}; @@ -834,7 +837,7 @@ void em4x70_brute(em4x70_data_t *etd, bool ledcontrol) { reply_ng(CMD_LF_EM4X70_BRUTE, status, response, sizeof(response)); } -void em4x70_write_pin(em4x70_data_t *etd, bool ledcontrol) { +void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) { uint8_t status = 0; @@ -850,7 +853,7 @@ void em4x70_write_pin(em4x70_data_t *etd, bool ledcontrol) { if (em4x70_read_id()) { // Write new PIN - if ((write(etd->pin & 0xFFFF, EM4X70_PIN_WORD_UPPER) == PM3_SUCCESS) && + 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 @@ -874,7 +877,7 @@ void em4x70_write_pin(em4x70_data_t *etd, bool ledcontrol) { reply_ng(CMD_LF_EM4X70_WRITEPIN, status, tag.data, sizeof(tag.data)); } -void em4x70_write_key(em4x70_data_t *etd, bool ledcontrol) { +void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol) { uint8_t status = 0; diff --git a/armsrc/em4x70.h b/armsrc/em4x70.h index 0fd640f86..83a8237d7 100644 --- a/armsrc/em4x70.h +++ b/armsrc/em4x70.h @@ -30,12 +30,12 @@ typedef enum { FALLING_EDGE } edge_detection_t; -void em4x70_info(em4x70_data_t *etd, bool ledcontrol); -void em4x70_write(em4x70_data_t *etd, bool ledcontrol); -void em4x70_brute(em4x70_data_t *etd, bool ledcontrol); -void em4x70_unlock(em4x70_data_t *etd, bool ledcontrol); -void em4x70_auth(em4x70_data_t *etd, bool ledcontrol); -void em4x70_write_pin(em4x70_data_t *etd, bool ledcontrol); -void em4x70_write_key(em4x70_data_t *etd, bool ledcontrol); +void em4x70_info(const em4x70_data_t *etd, bool ledcontrol); +void em4x70_write(const em4x70_data_t *etd, bool ledcontrol); +void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol); +void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol); +void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol); +void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol); +void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol); #endif /* EM4x70_H */ diff --git a/armsrc/epa.c b/armsrc/epa.c index 7bd563676..8406f6168 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -37,6 +37,24 @@ static const uint8_t pps[] = {0xD0, 0x11, 0x00, 0x52, 0xA6}; #endif +// this struct is used by EPA_Parse_CardAccess and contains info about the +// PACE protocol supported by the chip +typedef struct { + uint8_t oid[10]; + uint8_t version; + uint8_t parameter_id; +} pace_version_info_t; + +// general functions +static void EPA_Finish(void); +static size_t EPA_Parse_CardAccess(const uint8_t *data, size_t length, pace_version_info_t *pace_info); +static int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length); +static int EPA_Setup(void); + +// PACE related functions +static int EPA_PACE_MSE_Set_AT(const pace_version_info_t pace_version_info, uint8_t password); +static int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce); + // APDUs for communication with German Identification Card // General Authenticate (request encrypted nonce) WITHOUT the Le at the end @@ -137,7 +155,7 @@ static int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response, uint16_t re #endif case 'b': #ifdef WITH_ISO14443b - return iso14443b_apdu(apdu, length, false, response, respmaxlen, NULL); + return iso14443b_apdu(apdu, length, false, response, respmaxlen, NULL, NULL); #else (void) apdu; (void) length; @@ -153,7 +171,7 @@ static int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response, uint16_t re //----------------------------------------------------------------------------- // Closes the communication channel and turns off the field //----------------------------------------------------------------------------- -void EPA_Finish(void) { +static void EPA_Finish(void) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); iso_type = 0; @@ -172,7 +190,7 @@ void EPA_Finish(void) { // TODO: Support elements with long tags (tag is longer than 1 byte) // TODO: Support proprietary PACE domain parameters //----------------------------------------------------------------------------- -size_t EPA_Parse_CardAccess(uint8_t *data, size_t length, pace_version_info_t *pace_info) { +static size_t EPA_Parse_CardAccess(const uint8_t *data, size_t length, pace_version_info_t *pace_info) { size_t index = 0; @@ -243,7 +261,7 @@ size_t EPA_Parse_CardAccess(uint8_t *data, size_t length, pace_version_info_t *p // Returns -1 on failure or the length of the data on success // TODO: for the moment this sends only 1 APDU regardless of the requested length //----------------------------------------------------------------------------- -int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) { +static int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) { // the response APDU of the card // since the card doesn't always care for the expected length we send it, // we reserve 262 bytes here just to be safe (256-byte APDU + SW + ISO frame) @@ -300,7 +318,7 @@ static void EPA_PACE_Collect_Nonce_Abort(uint32_t cmd, uint8_t step, int func_re //----------------------------------------------------------------------------- // Acquire one encrypted PACE nonce //----------------------------------------------------------------------------- -void EPA_PACE_Collect_Nonce(PacketCommandNG *c) { +void EPA_PACE_Collect_Nonce(const PacketCommandNG *c) { /* * ack layout: * arg: @@ -354,7 +372,7 @@ void EPA_PACE_Collect_Nonce(PacketCommandNG *c) { struct p { uint32_t m; } PACKED; - struct p *packet = (struct p *)c->data.asBytes; + const struct p *packet = (const struct p *)c->data.asBytes; func_return = EPA_PACE_Get_Nonce(packet->m, nonce); // check if the command succeeded @@ -377,7 +395,7 @@ void EPA_PACE_Collect_Nonce(PacketCommandNG *c) { // Returns the actual size of the nonce on success or a less-than-zero error // code on failure. //----------------------------------------------------------------------------- -int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) { +static int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) { // build the APDU uint8_t apdu[sizeof(apdu_general_authenticate_pace_get_nonce) + 1]; @@ -416,7 +434,7 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) { // Initializes the PACE protocol by performing the "MSE: Set AT" step // Returns 0 on success or a non-zero error code on failure //----------------------------------------------------------------------------- -int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password) { +static int EPA_PACE_MSE_Set_AT(const pace_version_info_t pace_version_info, uint8_t password) { // create the MSE: Set AT APDU uint8_t apdu[23]; @@ -479,7 +497,7 @@ int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password) //----------------------------------------------------------------------------- // Perform the PACE protocol by replaying given APDUs //----------------------------------------------------------------------------- -void EPA_PACE_Replay(PacketCommandNG *c) { +void EPA_PACE_Replay(const PacketCommandNG *c) { uint32_t timings[ARRAYLEN(apdu_lengths_replay)] = {0}; @@ -547,7 +565,7 @@ void EPA_PACE_Replay(PacketCommandNG *c) { // Set up a communication channel (Card Select, PPS) // Returns 0 on success or a non-zero error code on failure //----------------------------------------------------------------------------- -int EPA_Setup(void) { +static int EPA_Setup(void) { #ifdef WITH_ISO14443a { @@ -593,7 +611,7 @@ int EPA_Setup(void) { return 1; } -void EPA_PACE_Simulate(PacketCommandNG *c) { +void EPA_PACE_Simulate(const PacketCommandNG *c) { //---------Initializing--------- diff --git a/armsrc/epa.h b/armsrc/epa.h index 72524f836..62880eca3 100644 --- a/armsrc/epa.h +++ b/armsrc/epa.h @@ -22,26 +22,8 @@ #include "common.h" #include "pm3_cmd.h" -// this struct is used by EPA_Parse_CardAccess and contains info about the -// PACE protocol supported by the chip -typedef struct { - uint8_t oid[10]; - uint8_t version; - uint8_t parameter_id; -} pace_version_info_t; - -// general functions -void EPA_Finish(void); -size_t EPA_Parse_CardAccess(uint8_t *data, size_t length, pace_version_info_t *pace_info); -int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length); -int EPA_Setup(void); - -// PACE related functions -int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password); -int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce); - -void EPA_PACE_Collect_Nonce(PacketCommandNG *c); -void EPA_PACE_Replay(PacketCommandNG *c); -void EPA_PACE_Simulate(PacketCommandNG *c); +void EPA_PACE_Collect_Nonce(const PacketCommandNG *c); +void EPA_PACE_Replay(const PacketCommandNG *c); +void EPA_PACE_Simulate(const PacketCommandNG *c); #endif /* __EPA_H */ diff --git a/armsrc/felica.c b/armsrc/felica.c index 0a74a30cc..b575ad9c4 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -49,7 +49,7 @@ static uint32_t felica_lasttime_prox2air_start; static void iso18092_setup(uint8_t fpga_minor_mode); static uint8_t felica_select_card(felica_card_select_t *card); -static void TransmitFor18092_AsReader(uint8_t *frame, uint16_t len, uint32_t *timing, uint8_t power, uint8_t highspeed); +static void TransmitFor18092_AsReader(const uint8_t *frame, uint16_t len, const uint32_t *NYI_timing_NYI, uint8_t power, uint8_t highspeed); static bool WaitForFelicaReply(uint16_t maxbytes); static void iso18092_set_timeout(uint32_t timeout) { @@ -269,13 +269,13 @@ static uint8_t felica_select_card(felica_card_select_t *card) { // copy UID // idm 8 if (card) { - memcpy(card->IDm, FelicaFrame.framebytes + 4, 8); + memcpy(card->IDm, FelicaFrame.framebytes + 4, 8); memcpy(card->PMm, FelicaFrame.framebytes + 4 + 8, 8); //memcpy(card->servicecode, FelicaFrame.framebytes + 4 + 8 + 8, 2); - memcpy(card->code, card->IDm, 2); - memcpy(card->uid, card->IDm + 2, 6); - memcpy(card->iccode, card->PMm, 2); - memcpy(card->mrt, card->PMm + 2, 6); + memcpy(card->code, card->IDm, 2); + memcpy(card->uid, card->IDm + 2, 6); + memcpy(card->iccode, card->PMm, 2); + memcpy(card->mrt, card->PMm + 2, 6); if (g_dbglevel >= DBG_DEBUG) { Dbprintf("Received Frame: "); Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes, 0); @@ -350,7 +350,12 @@ static void BuildFliteRdblk(const uint8_t *idm, uint8_t blocknum, const uint16_t AddCrc(frameSpace + 2, c - 2); } -static void TransmitFor18092_AsReader(uint8_t *frame, uint16_t len, uint32_t *timing, uint8_t power, uint8_t highspeed) { +static void TransmitFor18092_AsReader(const uint8_t *frame, uint16_t len, const uint32_t *NYI_timing_NYI, uint8_t power, uint8_t highspeed) { + if (NYI_timing_NYI != NULL) { + Dbprintf("Error: TransmitFor18092_AsReader does not check or set parameter NYI_timing_NYI"); + return; + } + uint16_t flags = FPGA_MAJOR_MODE_HF_ISO18092; if (power) flags |= FPGA_HF_ISO18092_FLAG_READER; @@ -512,12 +517,12 @@ static void felica_reset_frame_mode(void) { // arg0 FeliCa flags // arg1 len of commandbytes // d.asBytes command bytes to send -void felica_sendraw(PacketCommandNG *c) { +void felica_sendraw(const PacketCommandNG *c) { if (g_dbglevel >= DBG_DEBUG) Dbprintf("FeliCa_sendraw Enter"); felica_command_t param = c->oldarg[0]; size_t len = c->oldarg[1] & 0xffff; - uint8_t *cmd = c->data.asBytes; + const uint8_t *cmd = c->data.asBytes; uint32_t arg0; felica_card_select_t card; @@ -675,7 +680,7 @@ void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) { #define R_READBLK_LEN 0x21 //simulate NFC Tag3 card - for now only poll response works // second half (4 bytes) of NDEF2 goes into nfcid2_0, first into nfcid2_1 -void felica_sim_lite(uint8_t *uid) { +void felica_sim_lite(const uint8_t *uid) { // prepare our 3 responses... uint8_t resp_poll0[R_POLL0_LEN] = { 0xb2, 0x4d, 0x12, FELICA_POLL_ACK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0xb3, 0x7f}; diff --git a/armsrc/felica.h b/armsrc/felica.h index 8f232b3e4..2677dbfb9 100644 --- a/armsrc/felica.h +++ b/armsrc/felica.h @@ -21,9 +21,9 @@ #include "common.h" #include "cmd.h" -void felica_sendraw(PacketCommandNG *c); +void felica_sendraw(const PacketCommandNG *c); void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip); -void felica_sim_lite(uint8_t *uid); +void felica_sim_lite(const uint8_t *uid); void felica_dump_lite_s(void); #endif diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 802c77558..7ac52a42a 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -95,40 +95,40 @@ void SetupSpi(int mode) { case SPI_FPGA_MODE: AT91C_BASE_SPI->SPI_MR = (0 << 24) | // Delay between chip selects (take default: 6 MCK periods) - (0xE << 16) | // Peripheral Chip Select (selects FPGA SPI_NCS0 or PA11) + (0xE << 16) | // Peripheral Chip Select (selects FPGA SPI_NCS0 or PA11) (0 << 7) | // Local Loopback Disabled - AT91C_SPI_MODFDIS | // Mode Fault Detection disabled + AT91C_SPI_MODFDIS | // Mode Fault Detection disabled (0 << 2) | // Chip selects connected directly to peripheral - AT91C_SPI_PS_FIXED | // Fixed Peripheral Select + AT91C_SPI_PS_FIXED | // Fixed Peripheral Select AT91C_SPI_MSTR; // Master Mode AT91C_BASE_SPI->SPI_CSR[0] = (1 << 24) | // Delay between Consecutive Transfers (32 MCK periods) (1 << 16) | // Delay Before SPCK (1 MCK period) (6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24MHz/6 = 4M baud - AT91C_SPI_BITS_16 | // Bits per Transfer (16 bits) + AT91C_SPI_BITS_16 | // Bits per Transfer (16 bits) (0 << 3) | // Chip Select inactive after transfer - AT91C_SPI_NCPHA | // Clock Phase data captured on leading edge, changes on following edge + AT91C_SPI_NCPHA | // Clock Phase data captured on leading edge, changes on following edge (0 << 0); // Clock Polarity inactive state is logic 0 break; /* case SPI_LCD_MODE: AT91C_BASE_SPI->SPI_MR = - ( 0 << 24) | // Delay between chip selects (take default: 6 MCK periods) - (0xB << 16) | // Peripheral Chip Select (selects LCD SPI_NCS2 or PA10) - ( 0 << 7) | // Local Loopback Disabled - ( 1 << 4) | // Mode Fault Detection disabled - ( 0 << 2) | // Chip selects connected directly to peripheral - ( 0 << 1) | // Fixed Peripheral Select + ( 0 << 24) | // Delay between chip selects (take default: 6 MCK periods) + (0xB << 16) | // Peripheral Chip Select (selects LCD SPI_NCS2 or PA10) + ( 0 << 7) | // Local Loopback Disabled + ( 1 << 4) | // Mode Fault Detection disabled + ( 0 << 2) | // Chip selects connected directly to peripheral + ( 0 << 1) | // Fixed Peripheral Select ( 1 << 0); // Master Mode AT91C_BASE_SPI->SPI_CSR[2] = - ( 1 << 24) | // Delay between Consecutive Transfers (32 MCK periods) - ( 1 << 16) | // Delay Before SPCK (1 MCK period) - ( 6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24MHz/6 = 4M baud - AT91C_SPI_BITS_9 | // Bits per Transfer (9 bits) - ( 0 << 3) | // Chip Select inactive after transfer - ( 1 << 1) | // Clock Phase data captured on leading edge, changes on following edge + ( 1 << 24) | // Delay between Consecutive Transfers (32 MCK periods) + ( 1 << 16) | // Delay Before SPCK (1 MCK period) + ( 6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24MHz/6 = 4M baud + AT91C_SPI_BITS_9 | // Bits per Transfer (9 bits) + ( 0 << 3) | // Chip Select inactive after transfer + ( 1 << 1) | // Clock Phase data captured on leading edge, changes on following edge ( 0 << 0); // Clock Polarity inactive state is logic 0 break; */ @@ -162,8 +162,7 @@ void FpgaSetupSsc(uint16_t fpga_mode) { // 8, 16 or 32 bits per transfer, no loopback, MSB first, 1 transfer per sync // pulse, no output sync - if (((fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_READER || - (fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_FSK_READER) && + if (((fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_READER) && (FpgaGetCurrent() == FPGA_BITSTREAM_HF || FpgaGetCurrent() == FPGA_BITSTREAM_HF_15)) { AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); } else { @@ -393,7 +392,7 @@ static int bitparse_find_section(int bitstream_version, char section_name, uint3 while (numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH) { char current_name = get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); numbytes++; - uint16_t current_length = 0; + uint32_t current_length = 0; if (current_name < 'a' || current_name > 'e') { /* Strange section name, abort */ break; @@ -404,16 +403,22 @@ static int bitparse_find_section(int bitstream_version, char section_name, uint3 /* Four byte length field */ current_length += get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer) << 24; current_length += get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer) << 16; - numbytes += 2; - default: /* Fall through, two byte length field */ + current_length += get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer) << 8; + current_length += get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer) << 0; + numbytes += 4; + if (current_length > 300 * 1024) { + /* section e should never exceed about 300KB, if the length is too big limit it but still send the bitstream just in case */ + current_length = 300 * 1024; + } + break; + default: /* Two byte length field */ current_length += get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer) << 8; current_length += get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer) << 0; numbytes += 2; - } - - if (current_name != 'e' && current_length > 255) { - /* Maybe a parse error */ - break; + if (current_length > 64) { + /* if text field is too long, keep it but truncate it */ + current_length = 64; + } } if (current_name == section_name) { @@ -423,7 +428,7 @@ static int bitparse_find_section(int bitstream_version, char section_name, uint3 break; } - for (uint16_t i = 0; i < current_length && numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH; i++) { + for (uint32_t i = 0; i < current_length && numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH; i++) { get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); numbytes++; } @@ -488,7 +493,7 @@ void FpgaDownloadAndGo(int bitstream_version) { #endif // Send waiting time extension request as this will take a while - send_wtx(1500); + send_wtx(FPGA_LOAD_WAIT_TIME); bool verbose = (g_dbglevel > 3); diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 6069d3257..27455507d 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -1,5 +1,4 @@ //----------------------------------------------------------------------------- -// Copyright (C) Jonathan Westhues, April 2006 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. // // This program is free software: you can redistribute it and/or modify @@ -25,83 +24,117 @@ #define FpgaDisableSscDma(void) AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; #define FpgaEnableSscDma(void) AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; -// definitions for multiple FPGA config files support -#define FPGA_BITSTREAM_LF 1 -#define FPGA_BITSTREAM_HF 2 -#define FPGA_BITSTREAM_HF_FELICA 3 -#define FPGA_BITSTREAM_HF_15 4 - /* - Communication between ARM / FPGA is done inside armsrc/fpgaloader.c (function FpgaSendCommand) - Send 16 bit command / data pair to FPGA - The bit format is: C3 C2 C1 C0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 - where - C is 4bit command - D is 12bit data + Communication between ARM / FPGA is done inside armsrc/fpgaloader.c see: function FpgaSendCommand() + Send 16 bit command / data pair to FPGA with the bit format: + ++------ frame layout circa 2020 ------------------+ +| 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | ++-------------------------------------------------+ +| C C C C M M M M P P P P P P P P | C = FPGA_CMD_SET_CONFREG, M = FPGA_MAJOR_MODE_*, P = FPGA_LF_* or FPGA_HF_* parameter +| C C C C D D D D D D D D | C = FPGA_CMD_SET_DIVISOR, D = divisor +| C C C C T T T T T T T T | C = FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, T = threshold +| C C C C E | C = FPGA_CMD_TRACE_ENABLE, E=0 off, E=1 on ++-------------------------------------------------+ + ++------ frame layout current ---------------------+ +| 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | ++-------------------------------------------------+ +| C C C C M M M P P P P P P | C = FPGA_CMD_SET_CONFREG, M = FPGA_MAJOR_MODE_*, P = FPGA_LF_* or FPGA_HF_* parameter +| C C C C D D D D D D D D | C = FPGA_CMD_SET_DIVISOR, D = divisor +| C C C C T T T T T T T T | C = FPGA_CMD_SET_EDGE_DETECT_THRESHOLD, T = threshold +| C C C C E | C = FPGA_CMD_TRACE_ENABLE, E=0 off, E=1 on ++-------------------------------------------------+ + + shift_reg receive this 16bit frame + + LF command + ---------- + shift_reg[15:12] == 4bit command + LF has three commands (FPGA_CMD_SET_CONFREG, FPGA_CMD_SET_DIVISOR, FPGA_CMD_SET_EDGE_DETECT_THRESHOLD) + Current commands uses only 2bits. We have room for up to 4bits of commands total (7). + + LF data + ------- + shift_reg[11:0] == 12bit data + lf data is divided into MAJOR MODES and configuration values. + + The major modes uses 3bits (0,1,2,3,7 | 000, 001, 010, 011, 111) + 000 FPGA_MAJOR_MODE_LF_READER = Act as LF reader (modulate) + 001 FPGA_MAJOR_MODE_LF_EDGE_DETECT = Simulate LF + 010 FPGA_MAJOR_MODE_LF_PASSTHRU = Passthrough mode, CROSS_LO line connected to SSP_DIN. SSP_DOUT logic level controls if we modulate / listening + 011 FPGA_MAJOR_MODE_LF_ADC = refactor hitag2, clear ADC sampling + 111 FPGA_MAJOR_MODE_OFF = turn off sampling. + + Each one of this major modes can have options. Currently these two major modes uses options. + - FPGA_MAJOR_MODE_LF_READER + - FPGA_MAJOR_MODE_LF_EDGE_DETECT + + FPGA_MAJOR_MODE_LF_READER + ------------------------------------- + lf_field = 1bit (FPGA_LF_ADC_READER_FIELD) + + You can send FPGA_CMD_SET_DIVISOR to set with FREQUENCY the fpga should sample at + divisor = 8bits shift_reg[7:0] + + FPGA_MAJOR_MODE_LF_EDGE_DETECT + ------------------------------------------ + lf_ed_toggle_mode = 1bits + lf_ed_threshold = 8bits threshold defaults to 127 + + You can send FPGA_CMD_SET_EDGE_DETECT_THRESHOLD to set a custom threshold + lf_ed_threshold = 8bits threshold value. + + conf_word 12bits + conf_word[7:5] = 3bit major mode. + conf_word[0] = 1bit lf_field + conf_word[1] = 1bit lf_ed_toggle_mode + conf_word[7:0] = 8bit divisor + conf_word[7:0] = 8bit threshold ------+--------- frame layout -------------------- -bit | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 ------+------------------------------------------- -cmd | x x x x -major| x x x -opt | x x -divi | x x x x x x x x -thres| x x x x x x x x ------+------------------------------------------- */ - -// Definitions for the FPGA commands. -// BOTH HF / LF -#define FPGA_CMD_SET_CONFREG (1<<12) // C - -// LF -#define FPGA_CMD_SET_DIVISOR (2<<12) // C -#define FPGA_CMD_SET_USER_BYTE1 (3<<12) // C - -// HF -#define FPGA_CMD_TRACE_ENABLE (2<<12) // C - -// Definitions for the FPGA configuration word. +// Defining commands, modes and options. This must be aligned to the definitions in fpga/define.v #define FPGA_MAJOR_MODE_MASK 0x01C0 #define FPGA_MINOR_MODE_MASK 0x003F -// LF +// Definitions for the FPGA commands. +#define FPGA_CMD_SET_CONFREG (1<<12) +#define FPGA_CMD_SET_DIVISOR (2<<12) +#define FPGA_CMD_SET_EDGE_DETECT_THRESHOLD (3<<12) +#define FPGA_CMD_TRACE_ENABLE (2<<12) + +// Major modes #define FPGA_MAJOR_MODE_LF_READER (0<<6) #define FPGA_MAJOR_MODE_LF_EDGE_DETECT (1<<6) #define FPGA_MAJOR_MODE_LF_PASSTHRU (2<<6) #define FPGA_MAJOR_MODE_LF_ADC (3<<6) -// HF -#define FPGA_MAJOR_MODE_HF_READER (0<<6) // D -#define FPGA_MAJOR_MODE_HF_SIMULATOR (1<<6) // D -#define FPGA_MAJOR_MODE_HF_ISO14443A (2<<6) // D -#define FPGA_MAJOR_MODE_HF_SNIFF (3<<6) // D -#define FPGA_MAJOR_MODE_HF_ISO18092 (4<<6) // D -#define FPGA_MAJOR_MODE_HF_GET_TRACE (5<<6) // D -#define FPGA_MAJOR_MODE_HF_FSK_READER (6<<6) // D - -// BOTH HF / LF -#define FPGA_MAJOR_MODE_OFF (7<<6) // D - +#define FPGA_MAJOR_MODE_HF_READER (0<<6) +#define FPGA_MAJOR_MODE_HF_SIMULATOR (1<<6) +#define FPGA_MAJOR_MODE_HF_ISO14443A (2<<6) +#define FPGA_MAJOR_MODE_HF_SNIFF (3<<6) +#define FPGA_MAJOR_MODE_HF_ISO18092 (4<<6) +#define FPGA_MAJOR_MODE_HF_GET_TRACE (5<<6) +#define FPGA_MAJOR_MODE_OFF (7<<6) // Options for LF_READER -#define FPGA_LF_ADC_READER_FIELD 0x1 +#define FPGA_LF_ADC_READER_FIELD ( 1 ) // Options for LF_EDGE_DETECT -#define FPGA_CMD_SET_EDGE_DETECT_THRESHOLD FPGA_CMD_SET_USER_BYTE1 -#define FPGA_LF_EDGE_DETECT_READER_FIELD 0x1 -#define FPGA_LF_EDGE_DETECT_TOGGLE_MODE 0x2 +#define FPGA_LF_EDGE_DETECT_READER_FIELD ( 1 ) +#define FPGA_LF_EDGE_DETECT_TOGGLE_MODE ( 2 ) -// Options for the HF reader -#define FPGA_HF_READER_MODE_RECEIVE_IQ (0<<0) -#define FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE (1<<0) -#define FPGA_HF_READER_MODE_RECEIVE_PHASE (2<<0) -#define FPGA_HF_READER_MODE_SEND_FULL_MOD (3<<0) -#define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD (4<<0) -#define FPGA_HF_READER_MODE_SNIFF_IQ (5<<0) -#define FPGA_HF_READER_MODE_SNIFF_AMPLITUDE (6<<0) -#define FPGA_HF_READER_MODE_SNIFF_PHASE (7<<0) -#define FPGA_HF_READER_MODE_SEND_JAM (8<<0) +// Options for the generic HF reader +#define FPGA_HF_READER_MODE_RECEIVE_IQ ( 0 ) +#define FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE ( 1 ) +#define FPGA_HF_READER_MODE_RECEIVE_PHASE ( 2 ) +#define FPGA_HF_READER_MODE_SEND_FULL_MOD ( 3 ) +#define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD ( 4 ) +#define FPGA_HF_READER_MODE_SNIFF_IQ ( 5 ) +#define FPGA_HF_READER_MODE_SNIFF_AMPLITUDE ( 6 ) +#define FPGA_HF_READER_MODE_SNIFF_PHASE ( 7 ) +#define FPGA_HF_READER_MODE_SEND_JAM ( 8 ) +#define FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4 ( 9 ) #define FPGA_HF_READER_SUBCARRIER_848_KHZ (0<<4) #define FPGA_HF_READER_SUBCARRIER_424_KHZ (1<<4) @@ -109,24 +142,23 @@ thres| x x x x x x x x #define FPGA_HF_READER_2SUBCARRIERS_424_484_KHZ (3<<4) // Options for the HF simulated tag, how to modulate -#define FPGA_HF_SIMULATOR_NO_MODULATION 0x0 // 0000 -#define FPGA_HF_SIMULATOR_MODULATE_BPSK 0x1 // 0001 -#define FPGA_HF_SIMULATOR_MODULATE_212K 0x2 // 0010 -#define FPGA_HF_SIMULATOR_MODULATE_424K 0x4 // 0100 -#define FPGA_HF_SIMULATOR_MODULATE_424K_8BIT 0x5 // 0101 -// no 848K +#define FPGA_HF_SIMULATOR_NO_MODULATION ( 0 ) +#define FPGA_HF_SIMULATOR_MODULATE_BPSK ( 1 ) +#define FPGA_HF_SIMULATOR_MODULATE_212K ( 2 ) +#define FPGA_HF_SIMULATOR_MODULATE_424K ( 4 ) +#define FPGA_HF_SIMULATOR_MODULATE_424K_8BIT ( 5 ) // Options for ISO14443A -#define FPGA_HF_ISO14443A_SNIFFER 0x0 -#define FPGA_HF_ISO14443A_TAGSIM_LISTEN 0x1 -#define FPGA_HF_ISO14443A_TAGSIM_MOD 0x2 -#define FPGA_HF_ISO14443A_READER_LISTEN 0x3 -#define FPGA_HF_ISO14443A_READER_MOD 0x4 +#define FPGA_HF_ISO14443A_SNIFFER ( 0 ) +#define FPGA_HF_ISO14443A_TAGSIM_LISTEN ( 1 ) +#define FPGA_HF_ISO14443A_TAGSIM_MOD ( 2 ) +#define FPGA_HF_ISO14443A_READER_LISTEN ( 3 ) +#define FPGA_HF_ISO14443A_READER_MOD ( 4 ) -//options for Felica. -#define FPGA_HF_ISO18092_FLAG_NOMOD 0x1 // 0001 disable modulation module -#define FPGA_HF_ISO18092_FLAG_424K 0x2 // 0010 should enable 414k mode (untested). No autodetect -#define FPGA_HF_ISO18092_FLAG_READER 0x4 // 0100 enables antenna power, to act as a reader instead of tag +// Options for ISO18092 / Felica +#define FPGA_HF_ISO18092_FLAG_NOMOD ( 1 ) // 0001 disable modulation module +#define FPGA_HF_ISO18092_FLAG_424K ( 2 ) // 0010 should enable 414k mode (untested). No autodetect +#define FPGA_HF_ISO18092_FLAG_READER ( 4 ) // 0100 enables antenna power, to act as a reader instead of tag void FpgaSendCommand(uint16_t cmd, uint16_t v); void FpgaWriteConfWord(uint16_t v); diff --git a/armsrc/hfops.c b/armsrc/hfops.c index 6a97be76a..df42c8685 100644 --- a/armsrc/hfops.c +++ b/armsrc/hfops.c @@ -200,7 +200,7 @@ static uint32_t HfEncodeTkm(const uint8_t *uid, uint8_t modulation, uint8_t *dat return len; } -int HfSimulateTkm(uint8_t *uid, uint8_t modulation, uint32_t timeout) { +int HfSimulateTkm(const uint8_t *uid, uint8_t modulation, uint32_t timeout) { // free eventually allocated BigBuf memory BigBuf_free_keep_EM(); diff --git a/armsrc/hfops.h b/armsrc/hfops.h index 352f9d2bc..c738887b4 100644 --- a/armsrc/hfops.h +++ b/armsrc/hfops.h @@ -22,6 +22,6 @@ #include "common.h" int HfReadADC(uint32_t samplesCount, bool ledcontrol); -int HfSimulateTkm(uint8_t *uid, uint8_t modulation, uint32_t timeout); +int HfSimulateTkm(const uint8_t *uid, uint8_t modulation, uint32_t timeout); #endif diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 90a667105..d4abe9ff1 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -281,7 +281,7 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_ } break; - // Received RWD authentication challenge and respnse + // Received RWD authentication challenge and response case 64: { // Store the authentication attempt if (auth_table_len < (AUTH_TABLE_LENGTH - 8)) { @@ -891,7 +891,7 @@ static bool hitag2_test_auth_attempts(uint8_t *rx, const size_t rxlen, uint8_t * if (bCrypto) { Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed, removed entry!", NrAr[0], NrAr[1], NrAr[2], NrAr[3], NrAr[4], NrAr[5], NrAr[6], NrAr[7]); - // Removing failed entry from authentiations table + // Removing failed entry from authentications table memcpy(auth_table + auth_table_pos, auth_table + auth_table_pos + 8, 8); auth_table_len -= 8; @@ -979,7 +979,7 @@ static bool hitag2_read_uid(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t return true; } -void EloadHitag(uint8_t *data, uint16_t len) { +void EloadHitag(const uint8_t *data, uint16_t len) { memcpy(tag.sectors, data, sizeof(tag.sectors)); } @@ -1532,7 +1532,7 @@ void SimulateHitag2(bool ledcontrol) { // reply_ng(CMD_LF_HITAG_SIMULATE, (checked == -1) ? PM3_EOPABORTED : PM3_SUCCESS, (uint8_t *)tag.sectors, tag_size); } -void ReaderHitag(hitag_function htf, hitag_data *htd, bool ledcontrol) { +void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { uint32_t command_start = 0, command_duration = 0; uint32_t response_start = 0, response_duration = 0; @@ -1928,7 +1928,7 @@ out: reply_mix(CMD_ACK, bSuccessful, 0, 0, 0, 0); } -void WriterHitag(hitag_function htf, hitag_data *htd, int page, bool ledcontrol) { +void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledcontrol) { uint32_t command_start = 0; uint32_t command_duration = 0; diff --git a/armsrc/hitag2.h b/armsrc/hitag2.h index dcad3b417..90aa8132b 100644 --- a/armsrc/hitag2.h +++ b/armsrc/hitag2.h @@ -24,7 +24,7 @@ void SniffHitag2(bool ledcontrol); void SimulateHitag2(bool ledcontrol); -void ReaderHitag(hitag_function htf, hitag_data *htd, bool ledcontrol); -void WriterHitag(hitag_function htf, hitag_data *htd, int page, bool ledcontrol); -void EloadHitag(uint8_t *data, uint16_t len); +void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol); +void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledcontrol); +void EloadHitag(const uint8_t *data, uint16_t len); #endif diff --git a/armsrc/hitag2_crypto.c b/armsrc/hitag2_crypto.c index a8a01dae4..931c17fa8 100644 --- a/armsrc/hitag2_crypto.c +++ b/armsrc/hitag2_crypto.c @@ -91,17 +91,17 @@ uint32_t _hitag2_byte(uint64_t *x) { void hitag2_cipher_reset(struct hitag2_tag *tag, const uint8_t *iv) { uint64_t key = ((uint64_t)tag->sectors[2][2]) | - ((uint64_t)tag->sectors[2][3] << 8) | + ((uint64_t)tag->sectors[2][3] << 8) | ((uint64_t)tag->sectors[1][0] << 16) | ((uint64_t)tag->sectors[1][1] << 24) | ((uint64_t)tag->sectors[1][2] << 32) | ((uint64_t)tag->sectors[1][3] << 40); uint32_t uid = ((uint32_t)tag->sectors[0][0]) | - ((uint32_t)tag->sectors[0][1] << 8) | + ((uint32_t)tag->sectors[0][1] << 8) | ((uint32_t)tag->sectors[0][2] << 16) | ((uint32_t)tag->sectors[0][3] << 24); uint32_t iv_ = (((uint32_t)(iv[0]))) | - (((uint32_t)(iv[1])) << 8) | + (((uint32_t)(iv[1])) << 8) | (((uint32_t)(iv[2])) << 16) | (((uint32_t)(iv[3])) << 24); tag->cs = _hitag2_init(REV64(key), REV32(uid), REV32(iv_)); diff --git a/armsrc/hitag2crack.c b/armsrc/hitag2crack.c index ed7daecd5..138fdf451 100644 --- a/armsrc/hitag2crack.c +++ b/armsrc/hitag2crack.c @@ -339,7 +339,7 @@ bool hitag2crack_read_page(uint8_t *responsestr, uint8_t pagenum, uint8_t *nrar, // decrypt response hitag2crack_xor(response, e_response, keybits + 10, 32); // convert to hexstring - binarraytohex(responsestr, response, 32); + binarray_2_hex(responsestr, response, 32); return true; } else { UserMessage("hitag2crack_read_page:\r\n hitag2crack_send_e_cmd returned ERROR_RESPONSE\r\n"); @@ -424,7 +424,7 @@ bool hitag2crack_tx_rx(uint8_t *responsestr, uint8_t *msg, int len, int state, b } // convert response to hexstring - binarraytohex(responsestr, tmp + 5, 32); + binarray_2_hex(responsestr, tmp + 5, 32); return true; } else { #ifdef RFIDLER_DEBUG @@ -506,8 +506,8 @@ bool hitag2crack_decrypt_hex(uint8_t *response, uint8_t *hex) { binulong = hextoulong(hex); ulongtobinarray(bin, hitag2_crypt(binulong, 32), 32); - binarraytobinstring(binstr, bin, 32); - binarraytohex(binhex, bin, 32); + binarray_2_binstr(binstr, bin, 32); + binarray_2_hex(binhex, bin, 32); // UserMessage("ar = %s\r\n", binstr); // UserMessage("arhex = %s\r\n", binhex); @@ -532,7 +532,7 @@ bool hitag2crack_decrypt_bin(uint8_t *response, uint8_t *e_binstr) { binulong = binarraytoulong(e_bin, len); ulongtobinarray(bin, hitag2_crypt(binulong, len), len); - binarraytobinstring(binstr, bin, len); + binarray_2_binstr(binstr, bin, len); strcpy(response, binstr); return true; } @@ -569,7 +569,7 @@ bool hitag2_keystream(uint8_t *response, uint8_t *nrarhex) { uint8_t *spaceptr = NULL; /* - keybits = malloc(2080); + keybits = calloc(2080, sizeof(uint8_t)); if (!keybits) { UserMessage("cannot malloc keybits\r\n"); return false; @@ -646,7 +646,7 @@ bool hitag2_keystream(uint8_t *response, uint8_t *nrarhex) { } for (i = 0; i < 2048; i += 256) { - binarraytohex(keybitshex, keybits + i, 256); + binarray_2_hex(keybitshex, keybits + i, 256); UserMessage("%s\r\n", keybitshex); } diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index db219bcf2..d304cb6b3 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -635,7 +635,7 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, /* * Emulates a Hitag S Tag with the given data from the .hts file */ -void SimulateHitagSTag(bool tag_mem_supplied, uint8_t *data, bool ledcontrol) { +void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) { StopTicks(); @@ -1052,7 +1052,7 @@ static size_t concatbits(uint8_t *dstbuf, size_t dstbufskip, const uint8_t *srcb return dstbufskip + srcbuflen; } -static int selectHitagS(hitag_function htf, hitag_data *htd, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx, int t_wait, bool ledcontrol) { +static int selectHitagS(hitag_function htf, const hitag_data *htd, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx, int t_wait, bool ledcontrol) { StopTicks(); FpgaDownloadAndGo(FPGA_BITSTREAM_LF); @@ -1263,7 +1263,7 @@ static int selectHitagS(hitag_function htf, hitag_data *htd, uint8_t *tx, size_t * If the key was given the password will be decrypted. * Reads every page of a hitag S transpoder. */ -void ReadHitagS(hitag_function htf, hitag_data *htd, bool ledcontrol) { +void ReadHitagS(hitag_function htf, const hitag_data *htd, bool ledcontrol) { uint8_t rx[HITAG_FRAME_LEN]; size_t rxlen = 0; @@ -1356,7 +1356,7 @@ void ReadHitagS(hitag_function htf, hitag_data *htd, bool ledcontrol) { * Authenticates to the Tag with the given Key or Challenge. * Writes the given 32Bit data into page_ */ -void WritePageHitagS(hitag_function htf, hitag_data *htd, int page, bool ledcontrol) { +void WritePageHitagS(hitag_function htf, const hitag_data *htd, int page, bool ledcontrol) { bool bSuccessful = false; //check for valid input @@ -1443,7 +1443,7 @@ write_end: * is not received correctly due to Antenna problems. This function * detects these challenges. */ -void Hitag_check_challenges(uint8_t *data, uint32_t datalen, bool ledcontrol) { +void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol) { //check for valid input if (datalen < 8) { Dbprintf("Error, need chals"); diff --git a/armsrc/hitagS.h b/armsrc/hitagS.h index 49a4f358f..cbb87722f 100644 --- a/armsrc/hitagS.h +++ b/armsrc/hitagS.h @@ -25,8 +25,8 @@ #include "hitag.h" -void SimulateHitagSTag(bool tag_mem_supplied, uint8_t *data, bool ledcontrol); -void ReadHitagS(hitag_function htf, hitag_data *htd, bool ledcontrol); -void WritePageHitagS(hitag_function htf, hitag_data *htd, int page, bool ledcontrol); -void Hitag_check_challenges(uint8_t *data, uint32_t datalen, bool ledcontrol); +void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol); +void ReadHitagS(hitag_function htf, const hitag_data *htd, bool ledcontrol); +void WritePageHitagS(hitag_function htf, const hitag_data *htd, int page, bool ledcontrol); +void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol); #endif diff --git a/armsrc/i2c.c b/armsrc/i2c.c index ca3cb46fd..2848509a1 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +// //----------------------------------------------------------------------------- // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. // // This program is free software: you can redistribute it and/or modify @@ -53,9 +53,6 @@ static void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { #define I2C_DELAY_2CLK I2CSpinDelayClk(2) #define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x)) -// The SIM module v4 supports up to 384 bytes for the length. -#define ISO7816_MAX_FRAME 270 - // try i2c bus recovery at 100kHz = 5us high, 5us low void I2C_recovery(void) { @@ -168,15 +165,15 @@ static bool WaitSCL_H_delay(uint32_t delay) { return false; } -// 5000 * 3.07us = 15350us. 15.35ms -// 15000 * 3.07us = 46050us. 46.05ms +// 5000 * 3.07us = 15350 us = 15.35 ms +// 15000 * 3.07us = 46050 us = 46.05 ms static bool WaitSCL_H(void) { return WaitSCL_H_delay(5000); } static bool WaitSCL_L_delay(uint32_t delay) { while (delay--) { - if (!SCL_read) { + if (SCL_read == false) { return true; } I2C_DELAY_1CLK; @@ -194,7 +191,7 @@ static bool WaitSCL_L(void) { // It timeout reading response from card // Which ever comes first static bool WaitSCL_L_timeout(void) { - volatile uint32_t delay = 200; + volatile uint32_t delay = 1200; while (delay--) { // exit on SCL LOW if (SCL_read == false) @@ -212,32 +209,41 @@ static bool I2C_Start(void) { SDA_H; I2C_DELAY_1CLK; SCL_H; - if (!WaitSCL_H()) + if (WaitSCL_H() == false) { return false; + } I2C_DELAY_2CLK; - if (!SCL_read) + if (SCL_read == false) { return false; - if (!SDA_read) + } + + if (SDA_read == false) { return false; + } SDA_L; I2C_DELAY_2CLK; return true; } -static bool I2C_WaitForSim(void) { +static bool I2C_WaitForSim(uint32_t wait) { // wait for data from card - if (!WaitSCL_L_timeout()) + if (WaitSCL_L_timeout() == false) { return false; + } // 8051 speaks with smart card. - // 1000*50*3.07 = 153.5ms - // 1000*110*3.07 = 337.7ms + // 1000*50*3.07 = 153.5ms + // 1000*110*3.07 = 337.7ms (337700) + // 4 560 000 * 3.07 = 13999,2ms (13999200) // 1byte transfer == 1ms with max frame being 256bytes - return WaitSCL_H_delay(1000 * 110); + + // fct WaitSCL_H_delay uses a I2C_DELAY_1CLK in the loop with "wait" as number of iterations. + // I2C_DELAY_1CLK == I2CSpinDelayClk(1) = 3.07us + return WaitSCL_H_delay(wait); } // send i2c STOP @@ -248,7 +254,11 @@ static void I2C_Stop(void) { I2C_DELAY_2CLK; SCL_H; I2C_DELAY_2CLK; - if (!WaitSCL_H()) return; + + if (WaitSCL_H() == false) { + return; + } + SDA_H; I2C_DELAY_2CLK; I2C_DELAY_2CLK; @@ -264,7 +274,11 @@ static void I2C_Ack(void) { I2C_DELAY_2CLK; SCL_H; I2C_DELAY_2CLK; - if (!WaitSCL_H()) return; + + if (WaitSCL_H() == false) { + return; + } + SCL_L; I2C_DELAY_2CLK; } @@ -277,7 +291,11 @@ static void I2C_NoAck(void) { I2C_DELAY_2CLK; SCL_H; I2C_DELAY_2CLK; - if (!WaitSCL_H()) return; + + if (WaitSCL_H() == false) { + return; + } + SCL_L; I2C_DELAY_2CLK; } @@ -288,8 +306,10 @@ static bool I2C_WaitAck(void) { SDA_H; I2C_DELAY_1CLK; SCL_H; - if (!WaitSCL_H()) + + if (WaitSCL_H() == false) { return false; + } I2C_DELAY_2CLK; I2C_DELAY_2CLK; @@ -302,6 +322,7 @@ static bool I2C_WaitAck(void) { } static void I2C_SendByte(uint8_t data) { + uint8_t bits = 8; while (bits--) { @@ -319,8 +340,9 @@ static void I2C_SendByte(uint8_t data) { I2C_DELAY_1CLK; SCL_H; - if (!WaitSCL_H()) + if (WaitSCL_H() == false) { return; + } I2C_DELAY_2CLK; } @@ -332,111 +354,131 @@ static int16_t I2C_ReadByte(void) { SDA_H; while (bits--) { + b <<= 1; SCL_L; - if (!WaitSCL_L()) return -2; + if (WaitSCL_L() == false) { + return -2; + } I2C_DELAY_1CLK; - SCL_H; - if (!WaitSCL_H()) return -1; + if (WaitSCL_H() == false) { + return -1; + } I2C_DELAY_1CLK; - if (SDA_read) + if (SDA_read) { b |= 0x01; + } } SCL_L; return b; } -// Sends one byte ( command to be written, SlaveDevice address) +// Sends one byte (command to be written, SlaveDevice address) bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) { - bool bBreak = true; + bool _break = true; do { - if (!I2C_Start()) + if (I2C_Start() == false) { return false; + } I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(device_cmd); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } - bBreak = false; + _break = false; } while (false); I2C_Stop(); - if (bBreak) { + + if (_break) { + if (g_dbglevel > 3) DbpString(I2C_ERROR); + return false; } return true; } -// Sends 1 byte data (Data to be written, command to be written , SlaveDevice address ). +// Sends 1 byte data (data to be written, command to be written , SlaveDevice address) bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) { - bool bBreak = true; + bool _break = true; do { - if (!I2C_Start()) + if (I2C_Start() == false) { return false; + } I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(device_cmd); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(data); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } - bBreak = false; + _break = false; } while (false); I2C_Stop(); - if (bBreak) { + if (_break) { if (g_dbglevel > 3) DbpString(I2C_ERROR); return false; } return true; } -//Sends array of data (Array, length, command to be written , SlaveDevice address ). +// Sends array of data (array, length, command to be written , SlaveDevice address) // len = uint16 because we need to write up to 256 bytes -bool I2C_BufferWrite(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address) { - bool bBreak = true; +bool I2C_BufferWrite(const uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address) { + bool _break = true; do { - if (!I2C_Start()) + if (I2C_Start() == false) { return false; + } I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(device_cmd); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } while (len) { I2C_SendByte(*data); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) break; len--; data++; } - if (len == 0) - bBreak = false; + if (len == 0) { + _break = false; + } + } while (false); I2C_Stop(); - if (bBreak) { + if (_break) { if (g_dbglevel > 3) DbpString(I2C_ERROR); return false; } @@ -447,132 +489,157 @@ bool I2C_BufferWrite(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t de // len = uint16 because we need to read up to 256bytes int16_t I2C_BufferRead(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address) { - if (!data || len == 0) + // sanity check + if (data == NULL || len == 0) { return 0; + } + +// uint8_t *pd = data; // extra wait 500us (514us measured) // 200us (xx measured) WaitUS(600); - bool bBreak = true; - uint16_t readcount = 0; - uint16_t recv_len = 0; + bool _break = true; do { - if (!I2C_Start()) + if (I2C_Start() == false) { return 0; + } // 0xB0 / 0xC0 == i2c write I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(device_cmd); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } // 0xB1 / 0xC1 == i2c read I2C_Start(); I2C_SendByte(device_address | 1); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } - bBreak = false; + _break = false; } while (false); - if (bBreak) { + if (_break) { I2C_Stop(); if (g_dbglevel > 3) DbpString(I2C_ERROR); return 0; } + uint16_t readcount = 0; + uint16_t recv_len = 0; + while (len) { int16_t tmp = I2C_ReadByte(); - if (tmp < 0) + if (tmp < 0) { return tmp; + } *data = (uint8_t)tmp & 0xFF; len--; // Starting firmware v4 the length is encoded on the first two bytes. - // This only applies if command is I2C_DEVICE_CMD_READ. - if (device_cmd == I2C_DEVICE_CMD_READ) { - switch (readcount) { - case 0: - // Length (MSB) - recv_len = (*data) << 8; - break; - case 1: - // Length (LSB) - recv_len += *data; - // Adjust len if needed - if (len > recv_len) { - len = recv_len; - } - break; - default: - // Data byte received - data++; - break; + switch (readcount) { + case 0: { + // Length (MSB) + recv_len = (*data) << 8; + break; } - } else { - // Length is encoded on 1 byte - if ((readcount == 0) && (len > *data)) { - len = *data; - } else { + case 1: { + // Length (LSB) + recv_len += *data; + + // old packages.. + if (recv_len > 0x0200) { + // [0] = len + // [1] = data + recv_len >>= 8; + data++; + } + + // Adjust len if needed + if (len > recv_len) { + len = recv_len; + } + break; + } + default: { + // Data byte received data++; + break; } } + readcount++; // acknowledgements. After last byte send NACK. - if (len == 0) + if (len == 0) { I2C_NoAck(); - else + } else { I2C_Ack(); + } } I2C_Stop(); +// Dbprintf("rec len... %u readcount... %u", recv_len, readcount); +// Dbhexdump(readcount, pd, false); + + if (readcount < 2) { + return 0; + } + // return bytecount - bytes encoding length - return readcount - (device_cmd == I2C_DEVICE_CMD_READ ? 2 : 1); + return readcount - 2; } int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { //START, 0xB0, 0x00, 0x00, START, 0xB1, xx, yy, zz, ......, STOP - bool bBreak = true; + bool _break = true; uint8_t readcount = 0; // sending do { - if (!I2C_Start()) + if (I2C_Start() == false) { return 0; + } // 0xB0 / 0xC0 i2c write I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) break; I2C_SendByte(msb); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(lsb); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } // 0xB1 / 0xC1 i2c read I2C_Start(); I2C_SendByte(device_address | 1); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } - bBreak = false; + _break = false; } while (false); - if (bBreak) { + if (_break) { I2C_Stop(); if (g_dbglevel > 3) DbpString(I2C_ERROR); return 0; @@ -582,8 +649,9 @@ int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t while (len) { int16_t tmp = I2C_ReadByte(); - if (tmp < 0) + if (tmp < 0) { return tmp; + } *data = (uint8_t)tmp & 0xFF; @@ -602,42 +670,49 @@ int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t return readcount; } -bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { +bool I2C_WriteFW(const uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { //START, 0xB0, 0x00, 0x00, xx, yy, zz, ......, STOP - bool bBreak = true; + bool _break = true; do { - if (!I2C_Start()) + if (I2C_Start() == false) { return false; + } // 0xB0 == i2c write I2C_SendByte(device_address & 0xFE); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(msb); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } I2C_SendByte(lsb); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; + } while (len) { I2C_SendByte(*data); - if (!I2C_WaitAck()) + if (I2C_WaitAck() == false) { break; - + } len--; data++; } - if (len == 0) - bBreak = false; + if (len == 0) { + _break = false; + } + } while (false); I2C_Stop(); - if (bBreak) { + + if (_break) { if (g_dbglevel > 3) DbpString(I2C_ERROR); return false; } @@ -646,37 +721,40 @@ bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t d void I2C_print_status(void) { DbpString(_CYAN_("Smart card module (ISO 7816)")); - uint8_t maj, min; - if (I2C_get_version(&maj, &min) == PM3_SUCCESS) { - Dbprintf(" version................. " _YELLOW_("v%x.%02d"), maj, min); - if (maj < 4) { - DbpString(" " _RED_("Outdated firmware.") " Please upgrade to v4.x or above."); - } + + uint8_t major, minor; + if (I2C_get_version(&major, &minor) == PM3_SUCCESS) { + + Dbprintf(" version................. v%d.%02d ( %s )" + , major + , minor + , ((major == 4) && (minor == 42)) ? _GREEN_("ok") : _RED_("Outdated") + ); } else { - DbpString(" version................. " _RED_("FAILED")); + DbpString(" version................. ( " _RED_("fail") " )"); } } -int I2C_get_version(uint8_t *maj, uint8_t *min) { +int I2C_get_version(uint8_t *major, uint8_t *minor) { uint8_t resp[] = {0, 0, 0, 0}; I2C_Reset_EnterMainProgram(); uint8_t len = I2C_BufferRead(resp, sizeof(resp), I2C_DEVICE_CMD_GETVERSION, I2C_DEVICE_ADDRESS_MAIN); - if (len > 0) { - *maj = resp[0]; - *min = resp[1]; + if (len > 1) { + *major = resp[0]; + *minor = resp[1]; return PM3_SUCCESS; } return PM3_EDEVNOTSUPP; } // Will read response from smart card module, retries 3 times to get the data. -bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen) { +bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen, uint32_t wait) { - uint8_t i = 5; + uint8_t i = 10; int16_t len = 0; while (i--) { - I2C_WaitForSim(); + I2C_WaitForSim(wait); len = I2C_BufferRead(dest, *destlen, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); @@ -691,9 +769,9 @@ bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen) { } } - // after three - if (len <= 1) + if (len < 1) { return false; + } *destlen = len; return true; @@ -701,8 +779,10 @@ bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen) { bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { - if (card_ptr == NULL) + if (card_ptr == NULL) { return false; + } + card_ptr->atr_len = 0; memset(card_ptr->atr, 0, sizeof(card_ptr->atr)); @@ -711,15 +791,17 @@ bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { // start [C0 01] stop start C1 len aa bb cc stop] I2C_WriteCmd(I2C_DEVICE_CMD_GENERATE_ATR, I2C_DEVICE_ADDRESS_MAIN); - //wait for sim card to answer. + // wait for sim card to answer. // 1byte = 1ms , max frame 256bytes. Should wait 256ms atleast just in case. - if (I2C_WaitForSim() == false) + if (I2C_WaitForSim(SIM_WAIT_DELAY) == false) { return false; + } // read bytes from module uint16_t len = sizeof(card_ptr->atr); - if (sc_rx_bytes(card_ptr->atr, &len) == false) + if (sc_rx_bytes(card_ptr->atr, &len, SIM_WAIT_DELAY) == false) { return false; + } if (len > sizeof(card_ptr->atr)) { len = sizeof(card_ptr->atr); @@ -772,7 +854,7 @@ void SmartCardAtr(void) { // StopTicks(); } -void SmartCardRaw(smart_card_raw_t *p) { +void SmartCardRaw(const smart_card_raw_t *p) { LED_D_ON(); uint16_t len = 0; @@ -803,16 +885,22 @@ void SmartCardRaw(smart_card_raw_t *p) { } } - if ((flags & SC_RAW) || (flags & SC_RAW_T0)) { + if (((flags & SC_RAW) == SC_RAW) || ((flags & SC_RAW_T0) == SC_RAW_T0)) { + + uint32_t wait = SIM_WAIT_DELAY; + if ((flags & SC_WAIT) == SC_WAIT) { + wait = (uint32_t)((p->wait_delay * 1000) / 3.07); + } LogTrace(p->data, p->len, 0, 0, NULL, true); bool res = I2C_BufferWrite( p->data, p->len, - ((flags & SC_RAW_T0) ? I2C_DEVICE_CMD_SEND_T0 : I2C_DEVICE_CMD_SEND), + (((flags & SC_RAW_T0) == SC_RAW_T0) ? I2C_DEVICE_CMD_SEND_T0 : I2C_DEVICE_CMD_SEND), I2C_DEVICE_ADDRESS_MAIN ); + if (res == false && g_dbglevel > 3) { DbpString(I2C_ERROR); reply_ng(CMD_SMART_RAW, PM3_ESOFT, NULL, 0); @@ -821,7 +909,7 @@ void SmartCardRaw(smart_card_raw_t *p) { // read bytes from module len = ISO7816_MAX_FRAME; - res = sc_rx_bytes(resp, &len); + res = sc_rx_bytes(resp, &len, wait); if (res) { LogTrace(resp, len, 0, 0, NULL, false); } else { diff --git a/armsrc/i2c.h b/armsrc/i2c.h index 972704c1b..52c96862e 100644 --- a/armsrc/i2c.h +++ b/armsrc/i2c.h @@ -30,6 +30,15 @@ #define I2C_DEVICE_CMD_GETVERSION 0x06 #define I2C_DEVICE_CMD_SEND_T0 0x07 +// The SIM module v4 supports up to 384 bytes for the length. +#define ISO7816_MAX_FRAME 270 + +// 8051 speaks with smart card. +// 1000*50*3.07 = 153.5ms +// 1 byte transfer == 1ms with max frame being 256 bytes +#define SIM_WAIT_DELAY 88000 // about 270ms delay // 109773 -- about 337.7ms delay + + void I2C_recovery(void); void I2C_init(bool has_ticks); void I2C_Reset(void); @@ -41,20 +50,20 @@ void I2C_Reset_EnterBootloader(void); bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address); bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address); -bool I2C_BufferWrite(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address); +bool I2C_BufferWrite(const uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address); int16_t I2C_BufferRead(uint8_t *data, uint16_t len, uint8_t device_cmd, uint8_t device_address); // for firmware int16_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); -bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); +bool I2C_WriteFW(const uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); -bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen); +bool sc_rx_bytes(uint8_t *dest, uint16_t *destlen, uint32_t wait); // bool GetATR(smart_card_atr_t *card_ptr, bool verbose); // generice functions void SmartCardAtr(void); -void SmartCardRaw(smart_card_raw_t *p); +void SmartCardRaw(const smart_card_raw_t *p); void SmartCardUpgrade(uint64_t arg0); void SmartCardSetBaud(uint64_t arg0); void SmartCardSetClock(uint64_t arg0); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 5a68a8207..283577e3a 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -36,9 +36,10 @@ #include "protocols.h" #include "ticks.h" #include "iso15693.h" -#include "iclass_cmd.h" /* iclass_card_select_t struct */ +#include "iclass_cmd.h" // iclass_card_select_t struct +#include "i2c.h" // i2c defines (SIM module access) -static uint8_t get_pagemap(const picopass_hdr_t *hdr) { +uint8_t get_pagemap(const picopass_hdr_t *hdr) { return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; } @@ -52,23 +53,6 @@ static uint8_t get_pagemap(const picopass_hdr_t *hdr) { #define ICLASS_16KS_SIZE 0x100 * 8 #endif -// iCLASS has a slightly different timing compared to ISO15693. According to the picopass data sheet the tag response is expected 330us after -// the reader command. This is measured from end of reader EOF to first modulation of the tag's SOF which starts with a 56,64us unmodulated period. -// 330us = 140 ssp_clk cycles @ 423,75kHz when simulating. -// 56,64us = 24 ssp_clk_cycles -#define DELAY_ICLASS_VCD_TO_VICC_SIM (140 - 26) // (140 - 24) - -// times in ssp_clk_cycles @ 3,3625MHz when acting as reader -#define DELAY_ICLASS_VICC_TO_VCD_READER DELAY_ISO15693_VICC_TO_VCD_READER - -// times in samples @ 212kHz when acting as reader -#define ICLASS_READER_TIMEOUT_ACTALL 330 // 1558us, nominal 330us + 7slots*160us = 1450us -#define ICLASS_READER_TIMEOUT_UPDATE 3390 // 16000us, nominal 4-15ms -#define ICLASS_READER_TIMEOUT_OTHERS 80 // 380us, nominal 330us - -#define AddCrc(data, len) compute_crc(CRC_ICLASS, (data), (len), (data)+(len), (data)+(len)+1) - - /* * CARD TO READER * in ISO15693-2 mode - Manchester @@ -1245,7 +1229,7 @@ send: } // THE READER CODE -static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod) { +void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod) { CodeIso15693AsReader(frame, len); tosend_t *ts = get_tosend(); TransmitTo15693Tag(ts->buf, ts->max, start_time, shallow_mod); @@ -1270,6 +1254,12 @@ static bool iclass_send_cmd_with_retries(uint8_t *cmd, size_t cmdsize, uint8_t * if (res == PM3_SUCCESS && expected_size == resp_len) { return true; } + + // Timed out waiting for the tag to reply, but perhaps the tag did hear the command and is attempting to reply + // So wait long enough for the tag to encode it's reply plus required frame delays on each side before retrying + // And then double it, because in practice it seems to make it much more likely to succeed + // Response time calculation from expected_size lifted from GetIso15693AnswerFromTag + *start_time = *eof_time + ((DELAY_ICLASS_VICC_TO_VCD_READER + DELAY_ISO15693_VCD_TO_VICC_READER + (expected_size * 8 * 8 * 16)) * 2); } return false; } @@ -1755,7 +1745,7 @@ void iClass_Dump(uint8_t *msg) { } PACKED response; response.isOK = dumpsuccess; - response.block_cnt = i; + response.block_cnt = i - cmd->start_block; response.bb_offset = dataout - BigBuf_get_addr(); reply_ng(CMD_HF_ICLASS_DUMP, PM3_SUCCESS, (uint8_t *)&response, sizeof(response)); } @@ -1784,7 +1774,6 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, return false; } - uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if (blockno == 2) { // check response. e-purse update swaps first and second half if (memcmp(data + 4, resp, 4) || memcmp(data, resp + 4, 4)) { @@ -1792,6 +1781,7 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, } } else if (blockno == 3 || blockno == 4) { // check response. Key updates always return 0xffffffffffffffff + uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if (memcmp(all_ff, resp, 8)) { return false; } @@ -1821,7 +1811,7 @@ void iClass_WriteBlock(uint8_t *msg) { // select tag. uint32_t eof_time = 0; picopass_hdr_t hdr = {0}; - uint8_t res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time, shallow_mod); + bool res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time, shallow_mod); if (res == false) { goto out; } @@ -1881,8 +1871,9 @@ void iClass_WriteBlock(uint8_t *msg) { if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred res = false; switch_off(); - if (payload->req.send_reply) - reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_ETEAROFF, (uint8_t *)&res, sizeof(uint8_t)); + if (payload->req.send_reply) { + reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_ETEAROFF, (uint8_t *)&res, sizeof(bool)); + } return; } else { @@ -1901,16 +1892,16 @@ void iClass_WriteBlock(uint8_t *msg) { } // verify write - uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - if (payload->req.blockno == 2) { + if ((pagemap != PICOPASS_NON_SECURE_PAGEMODE) && (payload->req.blockno == 2)) { // check response. e-purse update swaps first and second half if (memcmp(payload->data + 4, resp, 4) || memcmp(payload->data, resp + 4, 4)) { res = false; goto out; } - } else if (payload->req.blockno == 3 || payload->req.blockno == 4) { + } else if ((pagemap != PICOPASS_NON_SECURE_PAGEMODE) && (payload->req.blockno == 3 || payload->req.blockno == 4)) { // check response. Key updates always return 0xffffffffffffffff - if (memcmp(all_ff, resp, 8)) { + uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + if (memcmp(all_ff, resp, sizeof(all_ff))) { res = false; goto out; } @@ -1922,11 +1913,166 @@ void iClass_WriteBlock(uint8_t *msg) { } } +out: + switch_off(); + + if (payload->req.send_reply) { + reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&res, sizeof(bool)); + } +} + +void iclass_credit_epurse(iclass_credit_epurse_t *payload) { + + LED_A_ON(); + + bool shallow_mod = payload->req.shallow_mod; + + Iso15693InitReader(); + + // select tag. + uint32_t eof_time = 0; + picopass_hdr_t hdr = {0}; + uint8_t res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time, shallow_mod); + if (res == false) { + goto out; + } + + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + uint8_t mac[4] = {0}; + + // authenticate + if (payload->req.do_auth) { + + res = authenticate_iclass_tag(&payload->req, &hdr, &start_time, &eof_time, mac); + if (res == false) { + goto out; + } + } + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, payload->req.blockno, 0x00, 0x00}; + AddCrc(cmd_read + 1, 1); + + uint8_t epurse[10]; + res = iclass_send_cmd_with_retries(cmd_read, sizeof(cmd_read), epurse, sizeof(epurse), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, shallow_mod); + if (!res) { + switch_off(); + if (payload->req.send_reply) { + reply_ng(CMD_HF_ICLASS_CREDIT_EPURSE, PM3_ETIMEOUT, (uint8_t *)&res, sizeof(uint8_t)); + } + return; + } + + uint8_t write[14] = { 0x80 | ICLASS_CMD_UPDATE, payload->req.blockno }; + uint8_t write_len = 14; + + uint8_t epurse_offset = 0; + const uint8_t empty_epurse[] = {0xff, 0xff, 0xff, 0xff}; + if (memcmp(epurse, empty_epurse, 4) == 0) { + // epurse data in stage 2 + epurse_offset = 4; + } + + memcpy(epurse + epurse_offset, payload->epurse, 4); + + // blank out debiting value as per the first step of the crediting procedure + epurse[epurse_offset + 0] = 0xFF; + epurse[epurse_offset + 1] = 0xFF; + + // initial epurse write for credit + memcpy(write + 2, epurse, 8); + + doMAC_N(write + 1, 9, payload->req.use_credit_key ? hdr.key_c : hdr.key_d, mac); + memcpy(write + 10, mac, sizeof(mac)); + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + uint8_t resp[10] = {0}; + + uint8_t tries = 3; + while (tries-- > 0) { + + iclass_send_as_reader(write, write_len, &start_time, &eof_time, shallow_mod); + + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred + res = false; + switch_off(); + if (payload->req.send_reply) + reply_ng(CMD_HF_ICLASS_CREDIT_EPURSE, PM3_ETEAROFF, (uint8_t *)&res, sizeof(uint8_t)); + return; + } else { + + uint16_t resp_len = 0; + int res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_UPDATE, &eof_time, false, true, &resp_len); + if (res2 == PM3_SUCCESS && resp_len == 10) { + res = true; + break; + } + } + } + + if (tries == 0) { + res = false; + goto out; + } + + // check response. e-purse update swaps first and second half + if (memcmp(write + 2 + 4, resp, 4) || memcmp(write + 2, resp + 4, 4)) { + res = false; + goto out; + } + + // new epurse write + // epurse offset is now flipped after the first write + epurse_offset ^= 4; + memcpy(resp + epurse_offset, payload->epurse, 4); + memcpy(write + 2, resp, 8); + + doMAC_N(write + 1, 9, payload->req.use_credit_key ? hdr.key_c : hdr.key_d, mac); + memcpy(write + 10, mac, sizeof(mac)); + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + tries = 3; + while (tries-- > 0) { + + iclass_send_as_reader(write, write_len, &start_time, &eof_time, shallow_mod); + + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred + res = false; + switch_off(); + if (payload->req.send_reply) + reply_ng(CMD_HF_ICLASS_CREDIT_EPURSE, PM3_ETEAROFF, (uint8_t *)&res, sizeof(uint8_t)); + return; + } else { + + uint16_t resp_len = 0; + int res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_UPDATE, &eof_time, false, true, &resp_len); + if (res2 == PM3_SUCCESS && resp_len == 10) { + res = true; + break; + } + } + } + + if (tries == 0) { + res = false; + goto out; + } + + // check response. e-purse update swaps first and second half + if (memcmp(write + 2 + 4, resp, 4) || memcmp(write + 2, resp + 4, 4)) { + res = false; + goto out; + } + out: switch_off(); if (payload->req.send_reply) - reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&res, sizeof(uint8_t)); + reply_ng(CMD_HF_ICLASS_CREDIT_EPURSE, PM3_SUCCESS, (uint8_t *)&res, sizeof(uint8_t)); } void iClass_Restore(iclass_restore_req_t *msg) { diff --git a/armsrc/iclass.h b/armsrc/iclass.h index ebbda2e9f..30846ff36 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -21,10 +21,28 @@ #include "common.h" #include "iclass_cmd.h" +// iCLASS has a slightly different timing compared to ISO15693. According to the picopass data sheet the tag response is expected 330us after +// the reader command. This is measured from end of reader EOF to first modulation of the tag's SOF which starts with a 56,64us unmodulated period. +// 330us = 140 ssp_clk cycles @ 423,75kHz when simulating. +// 56,64us = 24 ssp_clk_cycles +#define DELAY_ICLASS_VCD_TO_VICC_SIM (140 - 26) // (140 - 24) + +// times in ssp_clk_cycles @ 3,3625MHz when acting as reader +#define DELAY_ICLASS_VICC_TO_VCD_READER DELAY_ISO15693_VICC_TO_VCD_READER + + +// times in samples @ 212kHz when acting as reader +#define ICLASS_READER_TIMEOUT_ACTALL 330 // 1558us, nominal 330us + 7slots*160us = 1450us +#define ICLASS_READER_TIMEOUT_UPDATE 3390 // 16000us, nominal 4-15ms +#define ICLASS_READER_TIMEOUT_OTHERS 80 // 380us, nominal 330us + +#define AddCrc(data, len) compute_crc(CRC_ICLASS, (data), (len), (data)+(len), (data)+(len)+1) + void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string); void ReaderIClass(uint8_t flags); void iClass_WriteBlock(uint8_t *msg); +void iclass_credit_epurse(iclass_credit_epurse_t *payload); void iClass_Dump(uint8_t *msg); void iClass_Restore(iclass_restore_req_t *msg); @@ -42,4 +60,7 @@ bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, ui bool select_iclass_tag(picopass_hdr_t *hdr, bool use_credit_key, uint32_t *eof_time, bool shallow_mod); bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr_t *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out); + +uint8_t get_pagemap(const picopass_hdr_t *hdr); +void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod); #endif diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index b4730b62d..c06e2af48 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -142,6 +142,17 @@ Default HF 14a config is set to: */ static hf14a_config hf14aconfig = { 0, 0, 0, 0, 0 } ; + +// Polling frames and configurations +static iso14a_polling_parameters_t WUPA_POLLING_PARAMETERS = { + .frames = { {{ 0x52 }, 1, 7, 0} }, + .frame_count = 1, + .extra_timeout = 0, +}; + +// parity isn't used much +static uint8_t parity_array[MAX_PARITY_SIZE] = {0}; + void printHf14aConfig(void) { DbpString(_CYAN_("HF 14a config")); Dbprintf(" [a] Anticol override.... %s%s%s", @@ -179,7 +190,7 @@ void printHf14aConfig(void) { * @brief setSamplingConfig * @param sc */ -void setHf14aConfig(hf14a_config *hc) { +void setHf14aConfig(const hf14a_config *hc) { if ((hc->forceanticol >= 0) && (hc->forceanticol <= 2)) hf14aconfig.forceanticol = hc->forceanticol; @@ -555,7 +566,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t } -// Thinfilm, Kovio mangels ISO14443A in the way that they don't use start bit nor parity bits. +// Thinfilm, Kovio mangles ISO14443A in the way that they don't use start bit nor parity bits. static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { Demod.twoBits = (Demod.twoBits << 8) | bit; @@ -862,9 +873,8 @@ static void CodeIso14443aAsTagPar(const uint8_t *cmd, uint16_t len, const uint8_ } static void CodeIso14443aAsTagEx(const uint8_t *cmd, uint16_t len, bool collision) { - uint8_t par[MAX_PARITY_SIZE] = {0}; - GetParity(cmd, len, par); - CodeIso14443aAsTagPar(cmd, len, par, collision); + GetParity(cmd, len, parity_array); + CodeIso14443aAsTagPar(cmd, len, parity_array, collision); } static void CodeIso14443aAsTag(const uint8_t *cmd, uint16_t len) { CodeIso14443aAsTagEx(cmd, len, false); @@ -1012,7 +1022,8 @@ bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_ } } -bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_response_info_t **responses, uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages) { +bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_response_info_t **responses, + uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages) { uint8_t sak = 0; // The first response contains the ATQA (note: bytes are transmitted in reverse order). static uint8_t rATQA[2] = { 0x00 }; @@ -1029,8 +1040,13 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // Prepare the optional third SAK (for 10 byte UID), drop the cascade bit static uint8_t rSAKc3[3] = { 0x00 }; // dummy ATS (pseudo-ATR), answer to RATS + // Format byte = 0x58: FSCI=0x08 (FSC=256), TA(1) and TC(1) present, + // TA(1) = 0x80: different divisors not supported, DR = 1, DS = 1 + // TB(1) = not present. Defaults: FWI = 4 (FWT = 256 * 16 * 2^4 * 1/fc = 4833us), SFGI = 0 (SFG = 256 * 16 * 2^0 * 1/fc = 302us) + // TC(1) = 0x02: CID supported, NAD not supported // static uint8_t rRATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 }; - static uint8_t rRATS[] = { 0x05, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 }; + static uint8_t rRATS[40] = { 0x05, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00, 0x00 }; + uint8_t rRATS_len = 8; // GET_VERSION response for EV1/NTAG static uint8_t rVERSION[10] = { 0x00 }; @@ -1045,8 +1061,8 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r case 1: { // MIFARE Classic 1k rATQA[0] = 0x04; sak = 0x08; + break; } - break; case 2: { // MIFARE Ultralight rATQA[0] = 0x44; sak = 0x00; @@ -1076,33 +1092,33 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // READ_SIG memcpy(rSIGN, mfu_header->signature, 32); AddCrc14A(rSIGN, sizeof(rSIGN) - 2); - + break; } - break; case 3: { // MIFARE DESFire rATQA[0] = 0x44; rATQA[1] = 0x03; sak = 0x20; memcpy(rRATS, "\x06\x75\x77\x81\x02\x80\x00\x00", 8); + rRATS_len = 8; + break; } - break; case 4: { // ISO/IEC 14443-4 - javacard (JCOP) rATQA[0] = 0x04; sak = 0x28; + break; } - break; case 5: { // MIFARE TNP3XXX rATQA[0] = 0x01; rATQA[1] = 0x0f; sak = 0x01; + break; } - break; case 6: { // MIFARE Mini 320b rATQA[0] = 0x44; sak = 0x09; + break; } - break; - case 7: { // NTAG + case 7: { // NTAG 215 rATQA[0] = 0x44; sak = 0x00; // some first pages of UL/NTAG dump is special data @@ -1131,37 +1147,42 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // READ_SIG memcpy(rSIGN, mfu_header->signature, 32); AddCrc14A(rSIGN, sizeof(rSIGN) - 2); - + break; } - break; case 8: { // MIFARE Classic 4k rATQA[0] = 0x02; sak = 0x18; + break; } - break; case 9: { // FM11RF005SH (Shanghai Metro) rATQA[0] = 0x03; rATQA[1] = 0x00; sak = 0x0A; + break; } - break; case 10: { // ST25TA IKEA Rothult rATQA[0] = 0x42; rATQA[1] = 0x00; sak = 0x20; + break; } - break; - case 11: { // ISO/IEC 14443-4 - javacard (JCOP) + case 11: { // ISO/IEC 14443-4 - javacard (JCOP) / EMV + + memcpy(rRATS, "\x13\x78\x80\x72\x02\x80\x31\x80\x66\xb1\x84\x0c\x01\x6e\x01\x83\x00\x90\x00", 19); + rRATS_len = 19; rATQA[0] = 0x04; sak = 0x20; + break; + } + case 12: { // HID Seos 4K card + rATQA[0] = 0x01; + sak = 0x20; + break; } - break; - default: { if (g_dbglevel >= DBG_ERROR) Dbprintf("Error: unknown tagtype (%d)", tagType); return false; } - break; } // if uid not supplied then get from emulator memory @@ -1189,12 +1210,12 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r // Configure the ATQA and SAK accordingly rATQA[0] &= 0xBF; - if(tagType == 11){ - rSAKc1[0] = sak & 0xFC & 0X70; - }else{ - rSAKc1[0] = sak & 0xFB; + if (tagType == 11) { + rSAKc1[0] = sak & 0xFC & 0X70; + } else { + rSAKc1[0] = sak & 0xFB; } - + AddCrc14A(rSAKc1, sizeof(rSAKc1) - 2); *cuid = bytes_to_num(data, 4); @@ -1256,25 +1277,24 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r return false; } - // Format byte = 0x58: FSCI=0x08 (FSC=256), TA(1) and TC(1) present, - // TA(1) = 0x80: different divisors not supported, DR = 1, DS = 1 - // TB(1) = not present. Defaults: FWI = 4 (FWT = 256 * 16 * 2^4 * 1/fc = 4833us), SFGI = 0 (SFG = 256 * 16 * 2^0 * 1/fc = 302us) - // TC(1) = 0x02: CID supported, NAD not supported - AddCrc14A(rRATS, sizeof(rRATS) - 2); + AddCrc14A(rRATS, rRATS_len - 2); AddCrc14A(rPPS, sizeof(rPPS) - 2); + // EV1/NTAG, set PWD w AMIIBO algo if all zero. if (tagType == 7) { - uint8_t pwd[4]; - uint8_t gen_pwd[4]; - uint16_t start = (*pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH; - emlGetMemBt(pwd, start, sizeof(pwd)); + uint8_t pwd[4] = {0, 0, 0, 0}; + uint8_t gen_pwd[4] = {0, 0, 0, 0}; + emlGetMemBt(pwd, (*pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(pwd)); + emlGetMemBt(rPACK, (*pages) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(rPACK)); + Uint4byteToMemBe(gen_pwd, ul_ev1_pwdgenB(data)); if (memcmp(pwd, gen_pwd, sizeof(pwd)) == 0) { rPACK[0] = 0x80; rPACK[1] = 0x80; } } + AddCrc14A(rPACK, sizeof(rPACK) - 2); static tag_response_info_t responses_init[] = { @@ -1292,14 +1312,23 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r { .response = rPACK, .response_n = sizeof(rPACK) } // PACK response }; - // "precompile" responses. There are 12 predefined responses with a total of 84 bytes data to transmit. + // since rats len is variable now. + responses_init[RESP_INDEX_RATS].response_n = rRATS_len; + // "precompiled" responses. + // These exist for speed reasons. There are no time in the anti collision phase to calculate responses. + // There are 12 predefined responses with a total of 84 bytes data to transmit. + // // Coded responses need one byte per bit to transfer (data, parity, start, stop, correction) // 85 * 8 data bits, 85 * 1 parity bits, 12 start bits, 12 stop bits, 12 correction bits // 85 * 8 + 85 + 12 + 12 + 12 == 801 -#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 801 + // CHG: + // 85 bytes normally (rats = 8 bytes) + // 77 bytes + ratslen, - uint8_t *free_buffer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); +#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE ( ((77 + rRATS_len) * 8) + 77 + rRATS_len + 12 + 12 + 12) + + uint8_t *free_buffer = BigBuf_calloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); // modulation buffer pointer and current buffer free space size uint8_t *free_buffer_pointer = free_buffer; size_t free_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE; @@ -1315,7 +1344,6 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, tag_r } *responses = responses_init; - return true; } @@ -1349,12 +1377,16 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 }; uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 }; + // free eventually allocated BigBuf memory but keep Emulator Memory + BigBuf_free_keep_EM(); + // Allocate 512 bytes for the dynamic modulation, created when the reader queries for it // Such a response is less time critical, so we can prepare them on the fly #define DYNAMIC_RESPONSE_BUFFER_SIZE 64 #define DYNAMIC_MODULATION_BUFFER_SIZE 512 - uint8_t dynamic_response_buffer[DYNAMIC_RESPONSE_BUFFER_SIZE] = {0}; - uint8_t dynamic_modulation_buffer[DYNAMIC_MODULATION_BUFFER_SIZE] = {0}; + + uint8_t *dynamic_response_buffer = BigBuf_calloc(DYNAMIC_RESPONSE_BUFFER_SIZE); + uint8_t *dynamic_modulation_buffer = BigBuf_calloc(DYNAMIC_MODULATION_BUFFER_SIZE); tag_response_info_t dynamic_response_info = { .response = dynamic_response_buffer, .response_n = 0, @@ -1362,9 +1394,6 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ .modulation_n = 0 }; - // free eventually allocated BigBuf memory but keep Emulator Memory - BigBuf_free_keep_EM(); - if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); @@ -1558,7 +1587,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ // block1 = 4byte UID. p_response = &responses[RESP_INDEX_UIDC1]; } else { // all other tags (16 byte block tags) - uint8_t emdata[MAX_MIFARE_FRAME_SIZE]; + uint8_t emdata[MAX_MIFARE_FRAME_SIZE] = {0}; emlGetMemBt(emdata, block, 16); AddCrc14A(emdata, 16); EmSendCmd(emdata, sizeof(emdata)); @@ -1572,7 +1601,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ // send NACK 0x0 == invalid argument EmSend4bit(CARD_NACK_IV); } else { - uint8_t emdata[MAX_FRAME_SIZE]; + uint8_t emdata[MAX_FRAME_SIZE] = {0}; // first blocks of emu are header int start = block1 * 4 + MFU_DUMP_PREFIX_LENGTH; len = (block2 - block1 + 1) * 4; @@ -1656,7 +1685,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ // send NACK 0x0 == invalid argument EmSend4bit(CARD_NACK_IV); } else { - uint8_t cmd[3]; + uint8_t cmd[3] = {0, 0, 0}; cmd[0] = tearings[index]; AddCrc14A(cmd, sizeof(cmd) - 2); EmSendCmd(cmd, sizeof(cmd)); @@ -1692,36 +1721,32 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_ } else if (receivedCmd[0] == MIFARE_ULC_AUTH_1) { // ULC authentication, or Desfire Authentication LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); p_response = NULL; - } else if (receivedCmd[0] == MIFARE_ULEV1_AUTH && len == 7 && tagType == 7) { // NTAG / EV-1 authentication - - /* - // PWD stored in dump now - uint8_t pwd[4]; + } else if (receivedCmd[0] == MIFARE_ULEV1_AUTH && len == 7 && tagType == 7) { // NTAG / EV-1 + uint8_t pwd[4] = {0, 0, 0, 0}; emlGetMemBt(pwd, (pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(pwd)); + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf("Reader sent password: "); + Dbhexdump(4, receivedCmd + 1, 0); + Dbprintf("Loaded password from memory: "); + Dbhexdump(4, pwd, 0); + } + if (memcmp(pwd, "\x00\x00\x00\x00", 4) == 0) { Uint4byteToMemLe(pwd, ul_ev1_pwdgenB(data)); - Dbprintf("Calc pwd... %02X %02X %02X %02X", pwd[0], pwd[1], pwd[2], pwd[3]); + if (g_dbglevel >= DBG_DEBUG) Dbprintf("Calc pwd... %02X %02X %02X %02X", pwd[0], pwd[1], pwd[2], pwd[3]); } if (memcmp(receivedCmd + 1, pwd, 4) == 0) { - - uint8_t pack[4]; - emlGetMemBt(pack, pages * 4 + MFU_DUMP_PREFIX_LENGTH, 2); - if (memcmp(pack, "\x00\x00\x00\x00", 4) == 0) { - pack[0] = 0x80; - pack[1] = 0x80; - } - AddCrc14A(pack, sizeof(pack) - 2); - EmSendCmd(pack, sizeof(pack)); + if (g_dbglevel >= DBG_DEBUG) Dbprintf("Password match, responding with PACK."); + p_response = &responses[RESP_INDEX_PACK]; } else { - EmSend4bit(CARD_NACK_NA); - if (g_dbglevel >= DBG_DEBUG) Dbprintf("Auth attempt: %08x", bytes_to_num(receivedCmd + 1, 4)); + if (g_dbglevel >= DBG_DEBUG) Dbprintf("Password did not match, NACK_IV."); + p_response = NULL; + EmSend4bit(CARD_NACK_IV); } - p_response = NULL; - */ - p_response = &responses[RESP_INDEX_PACK]; + } else if (receivedCmd[0] == MIFARE_ULEV1_VCSL && len == 23 && tagType == 7) { - uint8_t cmd[3]; + uint8_t cmd[3] = {0, 0, 0}; emlGetMemBt(cmd, (pages - 2) * 4 + 1 + MFU_DUMP_PREFIX_LENGTH, 1); AddCrc14A(cmd, sizeof(cmd) - 2); EmSendCmd(cmd, sizeof(cmd)); @@ -1907,7 +1932,7 @@ static void PrepareDelayedTransfer(uint16_t delay) { static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing) { if (g_hf_field_active == false) { - Dbprintf("Warning: HF field is off, ignoring TransmitFor14443a command"); + Dbprintf("Warning: HF field is off"); return; } FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); @@ -1918,8 +1943,6 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing else PrepareDelayedTransfer(*timing & 0x00000007); // Delay transfer (fine tuning - up to 7 MF clock ticks) - if (g_dbglevel >= DBG_EXTENDED && GetCountSspClk() >= (*timing & 0xfffffff8)) - Dbprintf("TransmitFor14443a: Missed timing"); while (GetCountSspClk() < (*timing & 0xfffffff8)) {}; // Delay transfer (multiple of 8 MF clock ticks) LastTimeProxToAirStart = *timing; } else { @@ -2220,17 +2243,15 @@ int EmSendCmd(uint8_t *resp, uint16_t respLen) { return EmSendCmdEx(resp, respLen, false); } int EmSendCmdEx(uint8_t *resp, uint16_t respLen, bool collision) { - uint8_t par[MAX_PARITY_SIZE] = {0x00}; - GetParity(resp, respLen, par); - return EmSendCmdParEx(resp, respLen, par, collision); + GetParity(resp, respLen, parity_array); + return EmSendCmdParEx(resp, respLen, parity_array, collision); } int EmSendPrecompiledCmd(tag_response_info_t *p_response) { if (p_response == NULL) return 0; int ret = EmSendCmd14443aRaw(p_response->modulation, p_response->modulation_n); // do the tracing for the previous reader request and this tag answer: - uint8_t par[MAX_PARITY_SIZE] = {0x00}; - GetParity(p_response->response, p_response->response_n, par); + GetParity(p_response->response, p_response->response_n, parity_array); EmLogTrace(Uart.output, Uart.len, @@ -2241,7 +2262,7 @@ int EmSendPrecompiledCmd(tag_response_info_t *p_response) { p_response->response_n, LastTimeProxToAirStart * 16 + DELAY_ARM2AIR_AS_TAG, (LastTimeProxToAirStart + p_response->ProxToAirDuration) * 16 + DELAY_ARM2AIR_AS_TAG, - par); + parity_array); return ret; } @@ -2274,7 +2295,7 @@ bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_Start //----------------------------------------------------------------------------- bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint8_t *received_len) { - if (!g_hf_field_active) { + if (g_hf_field_active == false) { Dbprintf("Warning: HF field is off, ignoring GetIso14443aAnswerFromTag_Thinfilm command"); return false; } @@ -2292,7 +2313,9 @@ bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint8_t *rec uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; (void)b; + uint32_t timeout = iso14a_get_timeout(); uint32_t receive_timer = GetTickCount(); + for (;;) { WDT_HIT(); @@ -2300,17 +2323,18 @@ bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint8_t *rec b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; if (ManchesterDecoding_Thinfilm(b)) { *received_len = Demod.len; - // log + LogTrace(receivedResponse, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, NULL, false); return true; } } - if (GetTickCountDelta(receive_timer) > 100) + if (GetTickCountDelta(receive_timer) > timeout + 100) break; } + *received_len = Demod.len; - // log + LogTrace(receivedResponse, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, NULL, false); return false; } @@ -2322,8 +2346,10 @@ bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint8_t *rec // If it takes too long return FALSE //----------------------------------------------------------------------------- static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receivedResponsePar, uint16_t offset) { - if (g_hf_field_active == false) + if (g_hf_field_active == false) { + Dbprintf("Warning: HF field is off"); return false; + } // Set FPGA mode to "reader listen mode", no modulation (listen // only, since we are receiving, not transmitting). @@ -2355,8 +2381,9 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receive } // timeout already in ms + 100ms guard time - if (GetTickCountDelta(receive_timer) > timeout + 100) + if (GetTickCountDelta(receive_timer) > timeout + 100) { break; + } } return false; } @@ -2378,28 +2405,28 @@ void ReaderTransmitPar(uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *tim static void ReaderTransmitBits(uint8_t *frame, uint16_t len, uint32_t *timing) { // Generate parity and redirect - uint8_t par[MAX_PARITY_SIZE] = {0x00}; - GetParity(frame, len / 8, par); - ReaderTransmitBitsPar(frame, len, par, timing); + GetParity(frame, len / 8, parity_array); + ReaderTransmitBitsPar(frame, len, parity_array, timing); } void ReaderTransmit(uint8_t *frame, uint16_t len, uint32_t *timing) { // Generate parity and redirect - uint8_t par[MAX_PARITY_SIZE] = {0x00}; - GetParity(frame, len, par); - ReaderTransmitBitsPar(frame, len * 8, par, timing); + GetParity(frame, len, parity_array); + ReaderTransmitBitsPar(frame, len * 8, parity_array, timing); } static uint16_t ReaderReceiveOffset(uint8_t *receivedAnswer, uint16_t offset, uint8_t *par) { - if (!GetIso14443aAnswerFromTag(receivedAnswer, par, offset)) + if (GetIso14443aAnswerFromTag(receivedAnswer, par, offset) == false) { return 0; + } LogTrace(receivedAnswer, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, par, false); return Demod.len; } uint16_t ReaderReceive(uint8_t *receivedAnswer, uint8_t *par) { - if (!GetIso14443aAnswerFromTag(receivedAnswer, par, 0)) + if (GetIso14443aAnswerFromTag(receivedAnswer, par, 0) == false) { return 0; + } LogTrace(receivedAnswer, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, par, false); return Demod.len; } @@ -2508,83 +2535,70 @@ static void iso14a_set_ATS_times(const uint8_t *ats) { } } -static int GetATQA(uint8_t *resp, uint8_t *resp_par, bool use_ecp, bool use_magsafe) { -#define ECP_DELAY 10 -#define ECP_RETRY_TIMEOUT 100 -#define WUPA_RETRY_TIMEOUT 10 // 10ms - - - // 0x26 - REQA - // 0x52 - WAKE-UP - // 0x7A - MAGESAFE WAKE UP - uint8_t wupa[] = { ISO14443A_CMD_WUPA }; - - // if magsafe, set it outofbounds - if (use_magsafe) { - wupa[0] = MAGSAFE_CMD_WUPA_4; - } - - if (use_ecp) { - // In case a device was already selected, we send a S-BLOCK deselect to bring it into an idle state so it can be selected again - uint8_t deselect_cmd[] = {0xc2, 0xe0, 0xb4}; - ReaderTransmit(deselect_cmd, sizeof(deselect_cmd), NULL); - // Read response if present - (void) ReaderReceive(resp, resp_par); - } +static int GetATQA(uint8_t *resp, uint8_t *resp_par, iso14a_polling_parameters_t *polling_parameters) { +#define WUPA_RETRY_TIMEOUT 10 uint32_t save_iso14a_timeout = iso14a_get_timeout(); iso14a_set_timeout(1236 / 128 + 1); // response to WUPA is expected at exactly 1236/fc. No need to wait longer. bool first_try = true; - uint32_t retry_timeout = use_ecp ? ECP_RETRY_TIMEOUT : WUPA_RETRY_TIMEOUT; - uint32_t start_time = GetTickCount(); + uint32_t retry_timeout = WUPA_RETRY_TIMEOUT * polling_parameters->frame_count + polling_parameters->extra_timeout; + uint32_t start_time = 0; int len; - // we may need several tries if we did send an unknown command or a wrong authentication before... + uint8_t current_frame = 0; + do { - if (use_ecp && !first_try) { - uint8_t ecp[] = { 0x6a, 0x02, 0xC8, 0x01, 0x00, 0x03, 0x00, 0x02, 0x79, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xD8}; - ReaderTransmit(ecp, sizeof(ecp), NULL); - SpinDelay(ECP_DELAY); + iso14a_polling_frame_t *frame_parameters = &polling_parameters->frames[current_frame]; + + if (frame_parameters->last_byte_bits == 8) { + ReaderTransmit(frame_parameters->frame, frame_parameters->frame_length, NULL); + } else { + ReaderTransmitBitsPar(frame_parameters->frame, frame_parameters->last_byte_bits, NULL, NULL); } - if (use_magsafe) { - if (wupa[0] == MAGSAFE_CMD_WUPA_4) { - wupa[0] = MAGSAFE_CMD_WUPA_1; - } else { - wupa[0]++; - } + if (frame_parameters->extra_delay) { + SpinDelay(frame_parameters->extra_delay); } - // Broadcast for a card, WUPA (0x52) will force response from all cards in the field - ReaderTransmitBitsPar(wupa, 7, NULL, NULL); // Receive the ATQA len = ReaderReceive(resp, resp_par); + // We set the start_time here otherwise in some cases we miss the window and only ever try once + if (first_try) { + start_time = GetTickCount(); + } + first_try = false; + + // Go over frame configurations, loop back when we reach the end + current_frame = current_frame < (polling_parameters->frame_count - 1) ? current_frame + 1 : 0; } while (len == 0 && GetTickCountDelta(start_time) <= retry_timeout); iso14a_set_timeout(save_iso14a_timeout); return len; } + int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats) { - return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, no_rats, false, false); + return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, no_rats, &WUPA_POLLING_PARAMETERS); } + // performs iso14443a anticollision (optional) and card select procedure // fills the uid and cuid pointer unless NULL // fills the card info record unless NULL // if anticollision is false, then the UID must be provided in uid_ptr[] // and num_cascades must be set (1: 4 Byte UID, 2: 7 Byte UID, 3: 10 Byte UID) // requests ATS unless no_rats is true -int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, bool use_ecp, bool use_magsafe) { +int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, + bool anticollision, uint8_t num_cascades, bool no_rats, + iso14a_polling_parameters_t *polling_parameters) { uint8_t resp[MAX_FRAME_SIZE] = {0}; // theoretically. A usual RATS will be much smaller - uint8_t resp_par[MAX_PARITY_SIZE] = {0}; - uint8_t sak; // cascade uid + uint8_t sak = 0; // cascade uid bool do_cascade = 1; int cascade_level = 0; @@ -2594,7 +2608,7 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint p_card->ats_len = 0; } - if (GetATQA(resp, resp_par, use_ecp, use_magsafe) == false) { + if (GetATQA(resp, parity_array, polling_parameters) == 0) { return 0; } @@ -2611,7 +2625,7 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint // Read real UID uint8_t fudan_read[] = { 0x30, 0x01, 0x8B, 0xB9}; ReaderTransmit(fudan_read, sizeof(fudan_read), NULL); - if (!ReaderReceive(resp, resp_par)) { + if (!ReaderReceive(resp, parity_array)) { if (g_dbglevel >= DBG_INFO) Dbprintf("Card didn't answer to select all"); return 0; } @@ -2619,11 +2633,11 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint memcpy(p_card->uid, resp, 4); // select again? - if (GetATQA(resp, resp_par, false, false) == false) { + if (GetATQA(resp, parity_array, &WUPA_POLLING_PARAMETERS) == 0) { return 0; } - if (GetATQA(resp, resp_par, false, false) == false) { + if (GetATQA(resp, parity_array, &WUPA_POLLING_PARAMETERS) == 0) { return 0; } @@ -2641,7 +2655,10 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint if (hf14aconfig.forceanticol == 0) { // check for proprietary anticollision: - if ((resp[0] & 0x1F) == 0) return 3; + if ((resp[0] & 0x1F) == 0) { + return 3; + } + } else if (hf14aconfig.forceanticol == 2) { return 3; // force skipping anticol } // else force executing @@ -2660,7 +2677,7 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint // SELECT_ALL ReaderTransmit(sel_all, sizeof(sel_all), NULL); - if (!ReaderReceive(resp, resp_par)) { + if (!ReaderReceive(resp, parity_array)) { if (g_dbglevel >= DBG_INFO) Dbprintf("Card didn't answer to CL%i select all", cascade_level + 1); return 0; } @@ -2673,10 +2690,12 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint // anti-collision-loop: while (Demod.collisionPos) { Dbprintf("Multiple tags detected. Collision after Bit %d", Demod.collisionPos); + for (uint16_t i = collision_answer_offset; i < Demod.collisionPos; i++, uid_resp_bits++) { // add valid UID bits before collision point uint16_t UIDbit = (resp[i / 8] >> (i % 8)) & 0x01; uid_resp[uid_resp_bits / 8] |= UIDbit << (uid_resp_bits % 8); } + uid_resp[uid_resp_bits / 8] |= 1 << (uid_resp_bits % 8); // next time select the card(s) with a 1 in the collision position uid_resp_bits++; // construct anticollision command: @@ -2684,9 +2703,13 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint for (uint16_t i = 0; i <= uid_resp_bits / 8; i++) { sel_uid[2 + i] = uid_resp[i]; } + collision_answer_offset = uid_resp_bits % 8; + ReaderTransmitBits(sel_uid, 16 + uid_resp_bits, NULL); - if (!ReaderReceiveOffset(resp, collision_answer_offset, resp_par)) return 0; + if (!ReaderReceiveOffset(resp, collision_answer_offset, parity_array)) { + return 0; + } } // finally, add the last bits and BCC of the UID @@ -2717,17 +2740,21 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC) if (anticollision) { + memcpy(sel_uid + 2, uid_resp, 5); // the UID received during anticollision with original BCC uint8_t bcc = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate BCC if (sel_uid[6] != bcc) { + Dbprintf("BCC%d incorrect, got 0x%02x, expected 0x%02x", cascade_level, sel_uid[6], bcc); + if (hf14aconfig.forcebcc == 0) { Dbprintf("Aborting"); return 0; } else if (hf14aconfig.forcebcc == 1) { sel_uid[6] = bcc; } // else use card BCC - Dbprintf("Using BCC%d=" _YELLOW_("0x%02x") " to perform anticollision", cascade_level, sel_uid[6]); + + Dbprintf("Using BCC%d =" _YELLOW_("0x%02x"), cascade_level, sel_uid[6]); } } else { memcpy(sel_uid + 2, uid_resp, 4); // the provided UID @@ -2738,7 +2765,7 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint ReaderTransmit(sel_uid, sizeof(sel_uid), NULL); // Receive the SAK - if (!ReaderReceive(resp, resp_par)) { + if (!ReaderReceive(resp, parity_array)) { if (g_dbglevel >= DBG_INFO) Dbprintf("Card didn't answer to select"); return 0; } @@ -2746,13 +2773,16 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint // Test if more parts of the uid are coming do_cascade = (((sak & 0x04) /* && uid_resp[0] == 0x88 */) > 0); + if (cascade_level == 0) { + if (hf14aconfig.forcecl2 == 2) { do_cascade = false; } else if (hf14aconfig.forcecl2 == 1) { do_cascade = true; } // else 0==auto } else if (cascade_level == 1) { + if (hf14aconfig.forcecl3 == 2) { do_cascade = false; } else if (hf14aconfig.forcecl3 == 1) { @@ -2783,7 +2813,10 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint if (hf14aconfig.forcerats == 0) { // PICC compliant with iso14443a-4 ---> (SAK & 0x20 != 0) - if ((sak & 0x20) == 0) return 2; + if ((sak & 0x20) == 0) { + return 2; + } + } else if (hf14aconfig.forcerats == 2) { if ((sak & 0x20) != 0) Dbprintf("Skipping RATS according to hf 14a config"); return 2; @@ -2793,12 +2826,13 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint // RATS, Request for answer to select if (no_rats == false) { - uint8_t rats[] = { ISO14443A_CMD_RATS, 0x80, 0x00, 0x00 }; // FSD=256, FSDI=8, CID=0 - AddCrc14A(rats, 2); + + uint8_t rats[] = { ISO14443A_CMD_RATS, 0x80, 0x31, 0x73 }; // FSD=256, FSDI=8, CID=0 ReaderTransmit(rats, sizeof(rats), NULL); - int len = ReaderReceive(resp, resp_par); - if (len == 0) + int len = ReaderReceive(resp, parity_array); + if (len == 0) { return 0; + } if (p_card) { memcpy(p_card->ats, resp, sizeof(p_card->ats)); @@ -2822,7 +2856,7 @@ int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) { uint8_t sak = 0x04; // cascade uid int cascade_level = 0; - if (!GetATQA(resp, resp_par, false, false)) { + if (GetATQA(resp, resp_par, &WUPA_POLLING_PARAMETERS) == 0) { return 0; } @@ -2830,10 +2864,9 @@ int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) { // which case we need to make a cascade 2 request and select - this is a long UID // While the UID is not complete, the 3nd bit (from the right) is set in the SAK. for (; sak & 0x04; cascade_level++) { - uint8_t sel_all[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT, 0x20 }; uint8_t sel_uid[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) - sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2; + sel_uid[0] = ISO14443A_CMD_ANTICOLL_OR_SELECT + cascade_level * 2; if (cascade_level < num_cascades - 1) { uid_resp[0] = 0x88; @@ -2850,7 +2883,9 @@ int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) { ReaderTransmit(sel_uid, sizeof(sel_uid), NULL); // Receive the SAK - if (!ReaderReceive(resp, resp_par)) return 0; + if (!ReaderReceive(resp, resp_par)) { + return 0; + } sak = resp[0]; @@ -2895,8 +2930,8 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { } /* Peter Fillmore 2015 -Added card id field to the function - info from ISO14443A standard +Added card id field to the function info from ISO14443A standard + b1 = Block Number b2 = RFU (always 1) b3 = depends on block @@ -2904,14 +2939,17 @@ b4 = Card ID following if set to 1 b5 = depends on block type b6 = depends on block type b7,b8 = block type. + Coding of I-BLOCK: b8 b7 b6 b5 b4 b3 b2 b1 0 0 0 x x x 1 x b5 = chaining bit + Coding of R-block: b8 b7 b6 b5 b4 b3 b2 b1 1 0 1 x x 0 1 x b5 = ACK/NACK + Coding of S-block: b8 b7 b6 b5 b4 b3 b2 b1 1 1 x x x 0 1 0 @@ -2919,8 +2957,7 @@ b5,b6 = 00 - DESELECT 11 - WTX */ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, uint8_t *res) { - uint8_t parity[MAX_PARITY_SIZE] = {0x00}; - uint8_t real_cmd[cmd_len + 4]; + uint8_t *real_cmd = BigBuf_calloc(cmd_len + 4); if (cmd_len) { // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 @@ -2940,11 +2977,12 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u ReaderTransmit(real_cmd, cmd_len + 3, NULL); - size_t len = ReaderReceive(data, parity); + size_t len = ReaderReceive(data, parity_array); uint8_t *data_bytes = (uint8_t *) data; if (!len) { - return 0; //DATA LINK ERROR + BigBuf_free(); + return 0; // DATA LINK ERROR } else { // S-Block WTX while (len && ((data_bytes[0] & 0xF2) == 0xF2)) { @@ -2959,7 +2997,7 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u // transmit S-Block ReaderTransmit(data_bytes, len, NULL); // retrieve the result again (with increased timeout) - len = ReaderReceive(data, parity); + len = ReaderReceive(data, parity_array); data_bytes = data; // restore timeout iso14a_set_timeout(save_iso14a_timeout); @@ -2975,11 +3013,13 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u } // if we received I-block with chaining we need to send ACK and receive another block of data - if (res) + if (res) { *res = data_bytes[0]; + } // crc check if (len >= 3 && !CheckCrc14A(data_bytes, len)) { + BigBuf_free(); return -1; } @@ -2989,10 +3029,12 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u // cut frame byte len -= 1; // memmove(data_bytes, data_bytes + 1, len); - for (int i = 0; i < len; i++) + for (int i = 0; i < len; i++) { data_bytes[i] = data_bytes[i + 1]; + } } + BigBuf_free(); return len; } @@ -3011,8 +3053,8 @@ void ReaderIso14443a(PacketCommandNG *c) { uint32_t timeout = c->oldarg[2]; uint8_t *cmd = c->data.asBytes; uint32_t arg0; - uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00}; - uint8_t par[MAX_PARITY_SIZE] = {0x00}; + + uint8_t buf[PM3_CMD_DATA_SIZE_MIX] = {0x00}; if ((param & ISO14A_CONNECT)) { iso14_pcb_blocknum = 0; @@ -3031,7 +3073,12 @@ void ReaderIso14443a(PacketCommandNG *c) { // if failed selecting, turn off antenna and quite. if (!(param & ISO14A_NO_SELECT)) { iso14a_card_select_t *card = (iso14a_card_select_t *)buf; - arg0 = iso14443a_select_cardEx(NULL, card, NULL, true, 0, (param & ISO14A_NO_RATS), (param & ISO14A_USE_ECP), (param & ISO14A_USE_MAGSAFE)); + + arg0 = iso14443a_select_cardEx( + NULL, card, NULL, true, 0, (param & ISO14A_NO_RATS), + (param & ISO14A_USE_CUSTOM_POLLING) ? (iso14a_polling_parameters_t *)cmd : &WUPA_POLLING_PARAMETERS + ); + // TODO: Improve by adding a cmd parser pointer and moving it by struct length to allow combining data with polling params FpgaDisableTracing(); reply_mix(CMD_ACK, arg0, card->uidlen, 0, buf, sizeof(iso14a_card_select_t)); @@ -3050,7 +3097,7 @@ void ReaderIso14443a(PacketCommandNG *c) { arg0 = iso14_apdu(cmd, len, (param & ISO14A_SEND_CHAINING), buf, &res); FpgaDisableTracing(); - reply_old(CMD_ACK, arg0, res, 0, buf, sizeof(buf)); + reply_mix(CMD_ACK, arg0, res, 0, buf, sizeof(buf)); } if ((param & ISO14A_RAW)) { @@ -3079,8 +3126,8 @@ void ReaderIso14443a(PacketCommandNG *c) { bits_to_send -= 8; } } else { - GetParity(cmd, lenbits / 8, par); - ReaderTransmitBitsPar(cmd, lenbits, par, NULL); // bytes are 8 bit with odd parity + GetParity(cmd, lenbits / 8, parity_array); + ReaderTransmitBitsPar(cmd, lenbits, parity_array, NULL); // bytes are 8 bit with odd parity } } else { // want to send complete bytes only if ((param & ISO14A_TOPAZMODE)) { @@ -3094,13 +3141,33 @@ void ReaderIso14443a(PacketCommandNG *c) { } } - if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred - FpgaDisableTracing(); - reply_mix(CMD_ACK, 0, 0, 0, NULL, 0); + if ((param & ISO14A_TOPAZMODE)) { + + if (cmd[0] == TOPAZ_WRITE_E8 || cmd[0] == TOPAZ_WRITE_NE8) { + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred + FpgaDisableTracing(); + reply_mix(CMD_ACK, 0, 0, 0, NULL, 0); + } else { + arg0 = ReaderReceive(buf, parity_array); + FpgaDisableTracing(); + reply_mix(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); + } + } else { + arg0 = ReaderReceive(buf, parity_array); + FpgaDisableTracing(); + reply_mix(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); + } + } else { - arg0 = ReaderReceive(buf, par); - FpgaDisableTracing(); - reply_old(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); + + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred + FpgaDisableTracing(); + reply_mix(CMD_ACK, 0, 0, 0, NULL, 0); + } else { + arg0 = ReaderReceive(buf, parity_array); + FpgaDisableTracing(); + reply_mix(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); + } } } @@ -3111,8 +3178,9 @@ void ReaderIso14443a(PacketCommandNG *c) { iso14a_set_timeout(save_iso14a_timeout); } - if ((param & ISO14A_NO_DISCONNECT)) + if ((param & ISO14A_NO_DISCONNECT)) { return; + } OUT: hf_field_off(); @@ -3160,11 +3228,11 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { clear_trace(); set_tracing(true); - uint8_t mf_auth[] = { keytype, block, 0x00, 0x00 }; - uint8_t mf_nr_ar[] = {0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t par_list[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t ks_list[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t mf_auth[4] = { keytype, block, 0x00, 0x00 }; + uint8_t mf_nr_ar[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t par_list[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t ks_list[8] = {0, 0, 0, 0, 0, 0, 0, 0}; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; uint8_t par[1] = {0}; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough @@ -3447,12 +3515,12 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { * Thanks to @doegox for the feedback and new approaches. */ void DetectNACKbug(void) { - uint8_t mf_auth[] = {0x60, 0x00, 0xF5, 0x7B}; - uint8_t mf_nr_ar[] = {0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - uint8_t par[1] = {0}; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough + uint8_t mf_auth[4] = { MIFARE_AUTH_KEYA, 0x00, 0xF5, 0x7B }; + uint8_t mf_nr_ar[8] = { 0x00 }; + uint8_t uid[10] = { 0x00 }; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = { 0x00 }; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = { 0x00 }; + uint8_t par[1] = {0x00 }; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough uint32_t nt = 0, previous_nt = 0, nt_attacked = 0, cuid = 0; int32_t catch_up_cycles = 0, last_catch_up = 0; @@ -3602,17 +3670,18 @@ void DetectNACKbug(void) { sync_cycles = (sync_cycles - nt_distance) / elapsed_prng_sequences; - if (sync_cycles <= 0) + if (sync_cycles <= 0) { sync_cycles += PRNG_SEQUENCE_LENGTH; + } if (sync_cycles > PRNG_SEQUENCE_LENGTH * 2) { isOK = 96; // Card's PRNG runs at an unexpected frequency or resets unexpectedly break; } - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n", i, nt_distance, elapsed_prng_sequences, sync_cycles); - + } continue; } } @@ -3644,7 +3713,7 @@ void DetectNACKbug(void) { sync_cycles += catch_up_cycles; if (g_dbglevel >= DBG_EXTENDED) { - Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d.\n", i, catch_up_cycles, sync_cycles); + Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d\n", i, catch_up_cycles, sync_cycles); Dbprintf("nt [%08x] attacted [%08x]", nt, nt_attacked); } last_catch_up = 0; @@ -3655,8 +3724,9 @@ void DetectNACKbug(void) { } // Receive answer. This will be a 4 Bit NACK when the 8 parity bits are OK after decoding - if (received_nack) + if (received_nack) { catch_up_cycles = 8; // the PRNG is delayed by 8 cycles due to the NAC (4Bits = 0x05 encrypted) transfer + } // we are testing all 256 possibilities. par[0]++; @@ -3664,8 +3734,9 @@ void DetectNACKbug(void) { // tried all 256 possible parities without success. if (par[0] == 0) { // did we get one NACK? - if (num_nacks == 1) + if (num_nacks == 1) { isOK = 1; + } break; } diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 104d6b680..7957e6f48 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -121,7 +121,7 @@ typedef enum { #endif void printHf14aConfig(void); -void setHf14aConfig(hf14a_config *hc); +void setHf14aConfig(const hf14a_config *hc); hf14a_config *getHf14aConfig(void); void iso14a_set_timeout(uint32_t timeout); uint32_t iso14a_get_timeout(void); @@ -151,7 +151,7 @@ uint16_t ReaderReceive(uint8_t *receivedAnswer, uint8_t *par); void iso14443a_setup(uint8_t fpga_minor_mode); int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, uint8_t *res); int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); -int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, bool use_ecp, bool use_magsafe); +int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, iso14a_polling_parameters_t *polling_parameters); int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades); void iso14a_set_trigger(bool enable); diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index e4f1e4e93..512976d30 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -39,9 +39,14 @@ * Current timing issues with ISO14443-b implementation * Proxmark3 * Carrier Frequency 13.56MHz +* 1 / 13 560 000 = 73.74 nano seconds ( 0.07374 µs ) + * SSP_CLK runs at 13.56MHz / 4 = 3,39MHz +* 1 / 3 390 000 = 294.98 nano seconds ( 0.2949 µs ) * -* +* 1 ETU = 9.4395 µs = 32 SSP_CLK = 128 FC +* 1 SSP_CLK = 4 FC +* 1 µs 3 SSP_CLK about 14 FC * PROBLEM 1. * ---------- * one way of calculating time, that relates both to PM3 ssp_clk 3.39MHz, ISO freq of 13.56Mhz and ETUs @@ -106,6 +111,9 @@ * */ + + + #ifndef MAX_14B_TIMEOUT // FWT(max) = 4949 ms or 4.95 seconds. // SSP_CLK = 4949000 * 3.39 = 16777120 @@ -122,8 +130,8 @@ // ETU 14 * 9.4395 µS = 132 µS == 0.132ms // TR2, counting from start of PICC EOF 14 ETU. -#define DELAY_ISO14443B_PICC_TO_PCD_READER ETU_TO_SSP(14) -#define DELAY_ISO14443B_PCD_TO_PICC_READER ETU_TO_SSP(15) +#define DELAY_ISO14443B_PICC_TO_PCD_READER HF14_ETU_TO_SSP(14) +#define DELAY_ISO14443B_PCD_TO_PICC_READER HF14_ETU_TO_SSP(15) /* Guard Time (per 14443-2) in ETU * @@ -138,41 +146,45 @@ * TR0 */ #ifndef ISO14B_TR0 -# define ISO14B_TR0 ETU_TO_SSP(32) +# define ISO14B_TR0 HF14_ETU_TO_SSP(16) #endif #ifndef ISO14B_TR0_MAX -# define ISO14B_TR0_MAX ETU_TO_SSP(32) +# define ISO14B_TR0_MAX HF14_ETU_TO_SSP(32) // * TR0 - 32 ETU's maximum for ATQB only // * TR0 - FWT for all other commands -// TR0 max is 151/fsc = 151/848kHz = 302us or 64 samples from FPGA -// 32 ETU * 9.4395 µS == 302 µS -// 32 * 8 = 256 sub carrier cycles, -// 256 / 4 = 64 I/Q pairs. +// TR0 max is 159 µS or 32 samples from FPGA +// 16 ETU * 9.4395 µS == 151 µS +// 16 * 8 = 128 sub carrier cycles, +// 128 / 4 = 32 I/Q pairs. // since 1 I/Q pair after 4 subcarrier cycles at 848kHz subcarrier #endif // 8 ETU = 75 µS == 256 SSP_CLK #ifndef ISO14B_TR0_MIN -# define ISO14B_TR0_MIN ETU_TO_SSP(8) +# define ISO14B_TR0_MIN HF14_ETU_TO_SSP(8) #endif // Synchronization time (per 14443-2) in ETU -// 10 ETU = 94,39 µS == 320 SSP_CLK +// 16 ETU = 151 µS == 512 SSP_CLK #ifndef ISO14B_TR1_MIN -# define ISO14B_TR1_MIN ETU_TO_SSP(10) +# define ISO14B_TR1_MIN HF14_ETU_TO_SSP(16) #endif // Synchronization time (per 14443-2) in ETU // 25 ETU == 236 µS == 800 SSP_CLK #ifndef ISO14B_TR1_MAX -# define ISO14B_TR1 ETU_TO_SSP(25) +# define ISO14B_TR1 HF14_ETU_TO_SSP(25) #endif // Frame Delay Time PICC to PCD (per 14443-3 Amendment 1) in ETU // 14 ETU == 132 µS == 448 SSP_CLK #ifndef ISO14B_TR2 -# define ISO14B_TR2 ETU_TO_SSP(14) +# define ISO14B_TR2 HF14_ETU_TO_SSP(14) +#endif + +#ifndef ISO14B_BLOCK_SIZE +# define ISO14B_BLOCK_SIZE 4 #endif // 4sample @@ -440,10 +452,11 @@ static void Uart14bInit(uint8_t *data) { // param timeout accepts ETU static void iso14b_set_timeout(uint32_t timeout_etu) { - uint32_t ssp = ETU_TO_SSP(timeout_etu); + uint32_t ssp = HF14_ETU_TO_SSP(timeout_etu); - if (ssp > MAX_14B_TIMEOUT) + if (ssp > MAX_14B_TIMEOUT) { ssp = MAX_14B_TIMEOUT; + } iso14b_timeout = ssp; if (g_dbglevel >= DBG_DEBUG) { @@ -458,11 +471,14 @@ static void iso14b_set_fwt(uint8_t fwt) { } static void iso14b_set_maxframesize(uint16_t size) { - if (size > 256) + if (size > 256) { size = MAX_FRAME_SIZE; + } Uart.byteCntMax = size; - if (g_dbglevel >= DBG_DEBUG) Dbprintf("ISO14443B Max frame size set to %d bytes", Uart.byteCntMax); + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf("ISO14443B Max frame size set to %d bytes", Uart.byteCntMax); + } } //----------------------------------------------------------------------------- @@ -652,7 +668,7 @@ static RAMFUNC int Handle14443bSampleFromReader(uint8_t bit) { // Assume that we're called with the SSC (to the FPGA) and ADC path set // correctly. //----------------------------------------------------------------------------- -static int GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) { +static bool GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) { // Set FPGA mode to "simulated ISO 14443B tag", no modulation (listen // only, since we are receiving, not transmitting). // Signal field is off with the appropriate LED @@ -701,7 +717,7 @@ static void TransmitFor14443b_AsTag(const uint8_t *response, uint16_t len) { // Main loop of simulated tag: receive commands from reader, decide what // response to send, and send it. //----------------------------------------------------------------------------- -void SimulateIso14443bTag(uint8_t *pupi) { +void SimulateIso14443bTag(const uint8_t *pupi) { LED_A_ON(); // the only commands we understand is WUPB, AFI=0, Select All, N=1: @@ -754,7 +770,7 @@ void SimulateIso14443bTag(uint8_t *pupi) { tosend_t *ts = get_tosend(); - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); // prepare "ATQB" tag answer (encoded): CodeIso14443bAsTag(respATQB, sizeof(respATQB)); @@ -787,10 +803,13 @@ void SimulateIso14443bTag(uint8_t *pupi) { LED_A_ON(); } } - if (cardSTATE == SIM_NOFIELD) continue; + + if (cardSTATE == SIM_NOFIELD) { + continue; + } // Get reader command - if (!GetIso14443bCommandFromReader(receivedCmd, &len)) { + if (GetIso14443bCommandFromReader(receivedCmd, &len) == false) { Dbprintf("button pressed, received %d commands", cmdsReceived); break; } @@ -800,9 +819,11 @@ void SimulateIso14443bTag(uint8_t *pupi) { // WUP in HALTED state if (len == 5) { if ((receivedCmd[0] == ISO14443B_REQB && (receivedCmd[2] & 0x8) == 0x8 && cardSTATE == SIM_HALTED) || - receivedCmd[0] == ISO14443B_REQB) { + receivedCmd[0] == ISO14443B_REQB) { + LogTrace(receivedCmd, len, 0, 0, NULL, true); cardSTATE = SIM_SELECTING; + } } @@ -851,13 +872,14 @@ void SimulateIso14443bTag(uint8_t *pupi) { // - SLOT MARKER // - ISO7816 // - emulate with a memory dump - if (g_dbglevel >= DBG_DEBUG) + if (g_dbglevel >= DBG_DEBUG) { Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsReceived); + } - // CRC Check - if (len >= 3) { // if crc exists + // CRC Check, if long enough + if (len >= 3) { - if (!check_crc(CRC_14443_B, receivedCmd, len)) { + if (check_crc(CRC_14443_B, receivedCmd, len) == false) { if (g_dbglevel >= DBG_DEBUG) { DbpString("CRC fail"); } @@ -990,7 +1012,7 @@ void Simulate_iso14443b_srx_tag(uint8_t *uid) { if (cardSTATE == SIM_NOFIELD) continue; // Get reader command - if (!GetIso14443bCommandFromReader(receivedCmd, &len)) { + if (GetIso14443bCommandFromReader(receivedCmd, &len) == false) { Dbprintf("button pressed, received %d commands", cmdsReceived); break; } @@ -1057,7 +1079,7 @@ void Simulate_iso14443b_srx_tag(uint8_t *uid) { // CRC Check if (len >= 3) { // if crc exists - if (!check_crc(CRC_14443_B, receivedCmd, len)) { + if (check_crc(CRC_14443_B, receivedCmd, len) == false) { if (g_dbglevel >= DBG_DEBUG) { DbpString("CRC fail"); } @@ -1294,16 +1316,17 @@ static RAMFUNC int Handle14443bSamplesFromTag(int ci, int cq) { /* * Demodulate the samples we received from the tag, also log to tracebuffer */ -static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t timeout, uint32_t *eof_time) { +static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t timeout, uint32_t *eof_time, uint16_t *retlen) { // Set up the demodulator for tag -> reader responses. Demod14bInit(response, max_len); // The DMA buffer, used to stream samples from the FPGA dmabuf16_t *dma = get_dma16(); + if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { if (g_dbglevel > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); - return -1; + return PM3_EMALLOC; } uint32_t dma_start_time = 0; @@ -1317,8 +1340,9 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t for (;;) { volatile uint16_t behindBy = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); - if (behindBy == 0) + if (behindBy == 0) { continue; + } samples++; @@ -1364,13 +1388,13 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t *eof_time = GetCountSspClkDelta(dma_start_time) - DELAY_TAG_TO_ARM; // end of EOF if (Demod.len > Demod.max_len) { - ret = -2; // overflow + ret = PM3_EOVFLOW; // overflow } break; } if (((GetCountSspClkDelta(dma_start_time)) > timeout) && Demod.state < DEMOD_PHASE_REF_TRAINING) { - ret = -1; + ret = PM3_ETIMEOUT; break; } } @@ -1381,14 +1405,18 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, uint32_t } if (Demod.len > 0) { - uint32_t sof_time = *eof_time - ETU_TO_SSP( + uint32_t sof_time = *eof_time - HF14_ETU_TO_SSP( (Demod.len * (8 + 2)) // time for byte transfers // + (10) // time for TR1 + (10 + 2) // time for SOF transfer + (10)); // time for EOF transfer LogTrace(Demod.output, Demod.len, sof_time, *eof_time, NULL, false); } - return Demod.len; + + if (retlen) { + *retlen = Demod.len; + } + return PM3_SUCCESS; } //----------------------------------------------------------------------------- @@ -1399,8 +1427,11 @@ static void TransmitFor14443b_AsReader(uint32_t *start_time) { tosend_t *ts = get_tosend(); +#ifdef RDV4 + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4); +#else FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); - +#endif // TR2 minimum 14 ETUs if (*start_time < ISO14B_TR0) { @@ -1544,7 +1575,7 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t // eof_time in ssp clocks, but bits was added here! // *eof_time = *start_time + (10 * ts->max) + 10 + 2 + 10; - *eof_time = *start_time + ETU_TO_SSP(8 * ts->max); + *eof_time = *start_time + HF14_ETU_TO_SSP(8 * ts->max); LogTrace(cmd, len, *start_time, *eof_time, NULL, true); } @@ -1552,16 +1583,18 @@ static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t /* Sends an APDU to the tag * TODO: check CRC and preamble */ -int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void *rxdata, uint16_t rxmaxlen, uint8_t *res) { +int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void *rxdata, uint16_t rxmaxlen, uint8_t *res, int *reponselen) { uint8_t real_cmd[msg_len + 4]; if (msg_len) { // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 real_cmd[0] = 0x02; // bnr, nad, cid, chn=0; i-block(0x00) + if (send_chaining) { real_cmd[0] |= 0x10; } + // put block number into the PCB real_cmd[0] |= iso14b_pcb_blocknum; memcpy(real_cmd + 1, msg, msg_len); @@ -1579,14 +1612,21 @@ 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, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - int len = Get14443bAnswerFromTag(rxdata, rxmaxlen, iso14b_timeout, &eof_time); + + // Activation frame waiting time + // 65536/fc == 4833 µS + // SSP_CLK = 4833 µS * 3.39 = 16384 + + uint16_t len = 0; + if (Get14443bAnswerFromTag(rxdata, rxmaxlen, iso14b_timeout, &eof_time, &len) != PM3_SUCCESS) { + return PM3_ECARDEXCHANGE; + } + FpgaDisableTracing(); uint8_t *data_bytes = (uint8_t *) rxdata; - if (len <= 0) { - return 0; //DATA LINK ERROR - } else { + if (len) { // S-Block WTX while (len && ((data_bytes[0] & 0xF2) == 0xF2)) { @@ -1613,7 +1653,11 @@ int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void // retrieve the result again (with increased timeout) eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - len = Get14443bAnswerFromTag(rxdata, rxmaxlen, iso14b_timeout, &eof_time); + + if (Get14443bAnswerFromTag(rxdata, rxmaxlen, iso14b_timeout, &eof_time, &len) != PM3_SUCCESS) { + FpgaDisableTracing(); + return PM3_ECARDEXCHANGE; + } FpgaDisableTracing(); data_bytes = rxdata; @@ -1624,32 +1668,37 @@ int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void // if we received an I- or R(ACK)-Block with a block number equal to the // current block number, toggle the current block number - if (len >= 3 // PCB + CRC = 3 bytes - && ((data_bytes[0] & 0xC0) == 0 // I-Block - || (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 - && (data_bytes[0] & 0x01) == iso14b_pcb_blocknum) { // equal block numbers + + if ((len >= 3) && // PCB + CRC = 3 bytes + (((data_bytes[0] & 0xC0) == 0) || (data_bytes[0] & 0xD0) == 0x80) && // I-Block OR R-Block with ACK bit set to 0 + ((data_bytes[0] & 0x01) == iso14b_pcb_blocknum)) { // equal block numbers + iso14b_pcb_blocknum ^= 1; + } // if we received I-block with chaining we need to send ACK and receive another block of data - if (res) + if (res) { *res = data_bytes[0]; + } // crc check - if (len >= 3 && !check_crc(CRC_14443_B, data_bytes, len)) { - return -1; + if (len >= 3 && (check_crc(CRC_14443_B, data_bytes, len) == false)) { + return PM3_ECRC; + } + + // cut frame byte + len -= 1; + + for (int i = 0; i < len; i++) { + data_bytes[i] = data_bytes[i + 1]; } } - if (len) { - // cut frame byte - len -= 1; - // memmove(data_bytes, data_bytes + 1, len); - for (int i = 0; i < len; i++) - data_bytes[i] = data_bytes[i + 1]; + if (reponselen) { + *reponselen = len; } - - return len; + return PM3_SUCCESS; } /** @@ -1661,24 +1710,29 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { uint8_t cmdMSBUID[] = {ASK_SELECT, 0xFF, 0xFF, 0x00, 0x00}; uint8_t cmdLSBUID[] = {0xC4, 0x00, 0x00}; +// iceman: todo static crc AddCrc14B(cmdMSBUID, 3); AddCrc14B(cmdLSBUID, 1); - uint8_t r[8]; + uint8_t r[8] = { 0x00 }; uint32_t start_time = 0; uint32_t eof_time = 0; CodeAndTransmit14443bAsReader(cmdINIT, sizeof(cmdINIT), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - int retlen = Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time); + uint16_t retlen = 0; + if (Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + goto out; + } + FpgaDisableTracing(); if (retlen != 4) { - return -1; + return PM3_ELENGTH; } if (check_crc(CRC_14443_B, r, retlen) == false) { - return -2; + return PM3_ECRC; } if (card) { @@ -1691,14 +1745,16 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { CodeAndTransmit14443bAsReader(cmdMSBUID, sizeof(cmdMSBUID), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - retlen = Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time); + if (Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + goto out; + } FpgaDisableTracing(); if (retlen != 4) { - return -1; + return PM3_ELENGTH; } if (check_crc(CRC_14443_B, r, retlen) == false) { - return -2; + return PM3_ECRC; } if (card) { @@ -1709,21 +1765,26 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { CodeAndTransmit14443bAsReader(cmdLSBUID, sizeof(cmdLSBUID), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - retlen = Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time); + if (Get14443bAnswerFromTag(r, sizeof(r), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + goto out; + } FpgaDisableTracing(); if (retlen != 4) { - return -1; + return PM3_ELENGTH; } if (check_crc(CRC_14443_B, r, retlen) == false) { - return -2; + return PM3_ECRC; } if (card) { memcpy(card->uid + 2, r, 2); } - return 0; + return PM3_SUCCESS; +out: + FpgaDisableTracing(); + return PM3_ECARDEXCHANGE; } /** * SRx Initialise. @@ -1731,21 +1792,21 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { static int iso14443b_select_srx_card(iso14b_card_select_t *card) { // INITIATE command: wake up the tag using the INITIATE static const uint8_t init_srx[] = { ISO14443B_INITIATE, 0x00, 0x97, 0x5b }; - uint8_t r_init[3] = {0x0}; - uint8_t r_select[3] = {0x0}; - uint8_t r_papid[10] = {0x0}; + uint8_t r_init[3] = { 0x00 }; + uint8_t r_select[3] = { 0x00 }; + uint8_t r_papid[10] = { 0x00 }; uint32_t start_time = 0; uint32_t eof_time = 0; CodeAndTransmit14443bAsReader(init_srx, sizeof(init_srx), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - int retlen = Get14443bAnswerFromTag(r_init, sizeof(r_init), iso14b_timeout, &eof_time); - FpgaDisableTracing(); - - if (retlen <= 0) { - return -1; + uint16_t retlen = 0; + if (Get14443bAnswerFromTag(r_init, sizeof(r_init), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + FpgaDisableTracing(); + return PM3_ECARDEXCHANGE; } + FpgaDisableTracing(); // Randomly generated Chip ID if (card) { @@ -1762,59 +1823,67 @@ static int iso14443b_select_srx_card(iso14b_card_select_t *card) { CodeAndTransmit14443bAsReader(select_srx, sizeof(select_srx), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - retlen = Get14443bAnswerFromTag(r_select, sizeof(r_select), iso14b_timeout, &eof_time); + if (Get14443bAnswerFromTag(r_select, sizeof(r_select), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + goto out; + } + FpgaDisableTracing(); if (retlen != 3) { - return -1; + return PM3_ELENGTH; } if (check_crc(CRC_14443_B, r_select, retlen) == false) { - return -2; + return PM3_ECRC; } // Check response from the tag: should be the same UID as the command we just sent: if (select_srx[1] != r_select[0]) { - return -3; + return PM3_EWRONGANSWER; } // First get the tag's UID: select_srx[0] = ISO14443B_GET_UID; - - AddCrc14B(select_srx, 1); + select_srx[1] = 0xAB; + select_srx[2] = 0x4E; start_time = eof_time + ISO14B_TR2; CodeAndTransmit14443bAsReader(select_srx, 3, &start_time, &eof_time, true); // Only first three bytes for this one eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - retlen = Get14443bAnswerFromTag(r_papid, sizeof(r_papid), iso14b_timeout, &eof_time); + if (Get14443bAnswerFromTag(r_papid, sizeof(r_papid), iso14b_timeout, &eof_time, & retlen) != PM3_SUCCESS) { + goto out; + } + FpgaDisableTracing(); if (retlen != 10) { - return -1; + return PM3_ELENGTH; } - if (!check_crc(CRC_14443_B, r_papid, retlen)) { - return -2; + + if (check_crc(CRC_14443_B, r_papid, retlen) == false) { + return PM3_ECRC; } if (card) { card->uidlen = 8; memcpy(card->uid, r_papid, 8); } - - return 0; + return PM3_SUCCESS; +out: + FpgaDisableTracing(); + return PM3_ECARDEXCHANGE; } // Xerox tag connect function: wup, anticoll, attrib, password // the original chips require all commands in this sequence // 0: OK, 1: select fail, 2: attrib fail, 3: crc fail, 4: password fail -int iso14443b_select_xrx_card(iso14b_card_select_t *card) { +static int iso14443b_select_xrx_card(iso14b_card_select_t *card) { // AFI static const uint8_t x_wup1[] = { 0x0D, 0x37, 0x21, 0x92, 0xf2 }; static const uint8_t x_wup2[] = { 0x5D, 0x37, 0x21, 0x71, 0x71 }; - uint8_t slot_mark[1]; - - uint8_t x_atqb[24] = {0x0}; // ATQB len = 18 + uint8_t slot_mark[1] = { 0x00 }; + uint8_t x_atqb[24] = { 0x00 }; // ATQB len = 18 uint32_t start_time = 0; uint32_t eof_time = 0; @@ -1830,22 +1899,20 @@ int iso14443b_select_xrx_card(iso14b_card_select_t *card) { CodeAndTransmit14443bAsReader(x_wup2, sizeof(x_wup2), &start_time, &eof_time, true); uint64_t uid = 0; - int retlen; + uint16_t retlen = 0; for (int uid_pos = 0; uid_pos < 64; uid_pos += 2) { int slot; for (slot = 0; slot < 4; slot++) { - start_time = eof_time + ETU_TO_SSP(30); //(24); // next slot after 24 ETU + start_time = eof_time + HF14_ETU_TO_SSP(30); //(24); // next slot after 24 ETU - retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); - - if (retlen > 0) { - FpgaDisableTracing(); - - Dbprintf("unexpected data %d", retlen); - Dbprintf("crc %s", check_crc(CRC_14443_B, x_atqb, retlen) ? "OK" : "BAD"); - return 1; + if (Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + if (retlen > 0) { + Dbprintf("unexpected data %d", retlen); + Dbprintf("crc %s", check_crc(CRC_14443_B, x_atqb, retlen) ? "OK" : "BAD"); + } + goto out; } // tx unframed slot-marker @@ -1868,11 +1935,13 @@ int iso14443b_select_xrx_card(iso14b_card_select_t *card) { if (g_dbglevel >= DBG_DEBUG) { DbpString("no answer to anticollision"); } - return 1; + return PM3_ESOFT; } } - retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); + if (Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + goto out; + } if (g_dbglevel >= DBG_DEBUG) { Dbprintf("anticollision uid %llx", uid); @@ -1880,17 +1949,16 @@ int iso14443b_select_xrx_card(iso14b_card_select_t *card) { // ATQB too short? if (retlen < 18) { - return 1; + return PM3_ELENGTH; } // VALIDATE CRC if (check_crc(CRC_14443_B, x_atqb, 18) == false) { // use fixed len because unstable EOF catch - return 3; + return PM3_ECRC; } if (x_atqb[0] != 0x50) { -// DbpString("aqtb bad"); - return 1; + return PM3_EWRONGANSWER; } if (card) { @@ -1899,8 +1967,6 @@ int iso14443b_select_xrx_card(iso14b_card_select_t *card) { memcpy(card->atqb, x_atqb + 9, 7); } -// DbpString("aqtb ok"); - // send ATTRIB command uint8_t txbuf[18]; @@ -1918,25 +1984,23 @@ int iso14443b_select_xrx_card(iso14b_card_select_t *card) { CodeAndTransmit14443bAsReader(txbuf + 1, 15, &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); + if (Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + goto out; + } FpgaDisableTracing(); if (retlen < 3) { -// DbpString("attrib failed"); - return 2; + return PM3_ELENGTH; } if (check_crc(CRC_14443_B, x_atqb, 3) == false) { - return 3; + return PM3_ECRC; } if (x_atqb[0] != 0) { -// DbpString("attrib failed"); - return 2; + return PM3_EWRONGANSWER; } -// DbpString("attrib ok"); - // apply PASSWORD command txbuf[0] = 2; @@ -1954,25 +2018,27 @@ int iso14443b_select_xrx_card(iso14b_card_select_t *card) { CodeAndTransmit14443bAsReader(txbuf, 17, &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - retlen = Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time); + if (Get14443bAnswerFromTag(x_atqb, sizeof(x_atqb), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + goto out; + } if (retlen < 4) { -// DbpString("passwd failed"); - return 4; + return PM3_ELENGTH; } if (check_crc(CRC_14443_B, x_atqb, 4) == false) { - return 3; + return PM3_ECRC; } if (x_atqb[0] != 2 || x_atqb[1] != 0) { -// DbpString("passwd failed"); - return 4; + return PM3_EWRONGANSWER; } -// DbpString("passwd ok"); + return PM3_SUCCESS; - return 0; +out: + FpgaDisableTracing(); + return PM3_ECARDEXCHANGE; } /* Perform the ISO 14443 B Card Selection procedure @@ -1989,10 +2055,9 @@ int iso14443b_select_card(iso14b_card_select_t *card) { static const uint8_t wupb[] = { ISO14443B_REQB, 0x00, 0x00, 0x71, 0xff }; // ATTRIB command (with space for CRC) - uint8_t attrib[] = { ISO14443B_ATTRIB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00}; - - uint8_t r_pupid[14] = {0x0}; - uint8_t r_attrib[3] = {0x0}; + uint8_t attrib[11] = { ISO14443B_ATTRIB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00}; + uint8_t r_pupid[14] = { 0x00 }; + uint8_t r_attrib[3] = { 0x00 }; // first, wake up the tag uint32_t start_time = 0; @@ -2000,17 +2065,21 @@ int iso14443b_select_card(iso14b_card_select_t *card) { CodeAndTransmit14443bAsReader(wupb, sizeof(wupb), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - int retlen = Get14443bAnswerFromTag(r_pupid, sizeof(r_pupid), iso14b_timeout, &eof_time); + uint16_t retlen = 0; + if (Get14443bAnswerFromTag(r_pupid, sizeof(r_pupid), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + goto out; + } + FpgaDisableTracing(); // ATQB too short? if (retlen < 14) { - return -1; + return PM3_ELENGTH; } // VALIDATE CRC if (check_crc(CRC_14443_B, r_pupid, retlen) == false) { - return -2; + return PM3_ECRC; } if (card) { @@ -2029,17 +2098,19 @@ int iso14443b_select_card(iso14b_card_select_t *card) { CodeAndTransmit14443bAsReader(attrib, sizeof(attrib), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - retlen = Get14443bAnswerFromTag(r_attrib, sizeof(r_attrib), iso14b_timeout, &eof_time); + if (Get14443bAnswerFromTag(r_attrib, sizeof(r_attrib), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + goto out; + } FpgaDisableTracing(); // Answer to ATTRIB too short? if (retlen < 3) { - return -1; + return PM3_ELENGTH; } // VALIDATE CRC if (check_crc(CRC_14443_B, r_attrib, retlen) == false) { - return -2; + return PM3_ECRC; } if (card) { @@ -2065,22 +2136,28 @@ int iso14443b_select_card(iso14b_card_select_t *card) { } // reset PCB block number iso14b_pcb_blocknum = 0; - return 0; + return PM3_SUCCESS; + +out: + FpgaDisableTracing(); + return PM3_ECARDEXCHANGE; } // Set up ISO 14443 Type B communication (similar to iso14443a_setup) // field is setup for "Sending as Reader" void iso14443b_setup(void) { - LEDsoff(); + + switch_off(); // disconnect raw + SpinDelay(20); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // allocate command receive buffer BigBuf_free(); - BigBuf_Clear_ext(false); // Initialize Demod and Uart structs - Demod14bInit(BigBuf_malloc(MAX_FRAME_SIZE), MAX_FRAME_SIZE); - Uart14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); + Demod14bInit(BigBuf_calloc(MAX_FRAME_SIZE), MAX_FRAME_SIZE); + Uart14bInit(BigBuf_calloc(MAX_FRAME_SIZE)); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -2089,7 +2166,12 @@ void iso14443b_setup(void) { FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // Signal field is on with the appropriate LED +#ifdef RDV4 + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4); +#else FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); +#endif + SpinDelay(100); // Start the timer @@ -2110,8 +2192,9 @@ void iso14443b_setup(void) { // // I tried to be systematic and check every answer of the tag, every CRC, etc... //----------------------------------------------------------------------------- -static int read_srx_block(uint8_t blocknr, uint8_t *block) { +static int read_14b_srx_block(uint8_t blocknr, uint8_t *block) { +// iceman: todo add static CRC uint8_t cmd[] = {ISO14443B_READ_BLK, blocknr, 0x00, 0x00}; AddCrc14B(cmd, 2); @@ -2122,12 +2205,16 @@ static int read_srx_block(uint8_t blocknr, uint8_t *block) { CodeAndTransmit14443bAsReader(cmd, sizeof(cmd), &start_time, &eof_time, true); eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - int retlen = Get14443bAnswerFromTag(r_block, sizeof(r_block), iso14b_timeout, &eof_time); + uint16_t retlen = 0; + if (Get14443bAnswerFromTag(r_block, sizeof(r_block), iso14b_timeout, &eof_time, &retlen) != PM3_SUCCESS) { + FpgaDisableTracing(); + return PM3_ECARDEXCHANGE; + } FpgaDisableTracing(); // Check if we got an answer from the tag if (retlen != 6) { - DbpString("[!] expected 6 bytes from tag, got less..."); + Dbprintf("expected 6 bytes from tag, got %u", retlen); return PM3_EWRONGANSWER; } // The check the CRC of the answer @@ -2137,7 +2224,7 @@ static int read_srx_block(uint8_t blocknr, uint8_t *block) { } if (block) { - memcpy(block, r_block, 4); + memcpy(block, r_block, ISO14B_BLOCK_SIZE); } if (g_dbglevel >= DBG_DEBUG) { @@ -2151,28 +2238,26 @@ static int read_srx_block(uint8_t blocknr, uint8_t *block) { return PM3_SUCCESS; } -void ReadSTBlock(uint8_t blocknr) { +void read_14b_st_block(uint8_t blocknr) { iso14443b_setup(); - iso14b_card_select_t card; - int res = iso14443b_select_srx_card(&card); - // 0: OK -1 wrong len, -2: attrib fail, -3:crc fail, - switch (res) { - case -1: - case -3: { - reply_ng(CMD_HF_SRI_READ, PM3_EWRONGANSWER, NULL, 0); - goto out; - } - case -2: { - reply_ng(CMD_HF_SRI_READ, PM3_ECRC, NULL, 0); - goto out; - } + + set_tracing(true); + + uint8_t *data = BigBuf_calloc(ISO14B_BLOCK_SIZE); + iso14b_card_select_t *card = (iso14b_card_select_t *) BigBuf_calloc(sizeof(iso14b_card_select_t)); + + int res = iso14443b_select_srx_card(card); + if (res != PM3_SUCCESS) { + reply_ng(CMD_HF_SRI_READ, res, NULL, 0); + goto out; } - uint8_t *data = BigBuf_malloc(4); - res = read_srx_block(blocknr, data); - reply_ng(CMD_HF_SRI_READ, res, data, 4); + + res = read_14b_srx_block(blocknr, data); + reply_ng(CMD_HF_SRI_READ, res, data, ISO14B_BLOCK_SIZE); out: - BigBuf_free(); + set_tracing(false); + BigBuf_free_keep_EM(); switch_off(); } @@ -2374,16 +2459,10 @@ static void iso14b_set_trigger(bool enable) { void SendRawCommand14443B_Ex(iso14b_raw_cmd_t *p) { - // receive buffer - uint8_t buf[PM3_CMD_DATA_SIZE]; - memset(buf, 0, sizeof(buf)); - if (g_dbglevel > DBG_DEBUG) { - Dbprintf("14b raw: param, %04x", p->flags); - } - // turn on trigger (LED_A) - if ((p->flags & ISO14B_REQUEST_TRIGGER) == ISO14B_REQUEST_TRIGGER) + if ((p->flags & ISO14B_REQUEST_TRIGGER) == ISO14B_REQUEST_TRIGGER) { iso14b_set_trigger(true); + } if ((p->flags & ISO14B_CONNECT) == ISO14B_CONNECT) { iso14443b_setup(); @@ -2395,84 +2474,109 @@ void SendRawCommand14443B_Ex(iso14b_raw_cmd_t *p) { if ((p->flags & ISO14B_CLEARTRACE) == ISO14B_CLEARTRACE) { clear_trace(); + BigBuf_Clear_ext(false); } set_tracing(true); - int status; + // receive buffer + uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00}; + + int status = 0; uint32_t sendlen = sizeof(iso14b_card_select_t); - iso14b_card_select_t card; - memset((void *)&card, 0x00, sizeof(card)); + iso14b_card_select_t *card = (iso14b_card_select_t *)buf; if ((p->flags & ISO14B_SELECT_STD) == ISO14B_SELECT_STD) { - status = iso14443b_select_card(&card); - reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, (uint8_t *)&card, sendlen); - // 0: OK -1: attrib fail, -2:crc fail, - if (status != 0) goto out; + status = iso14443b_select_card(card); + reply_ng(CMD_HF_ISO14443B_COMMAND, status, (uint8_t *)card, sendlen); + if (status != PM3_SUCCESS) goto out; } if ((p->flags & ISO14B_SELECT_SR) == ISO14B_SELECT_SR) { - status = iso14443b_select_srx_card(&card); - reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, (uint8_t *)&card, sendlen); - // 0: OK 2: demod fail, 3:crc fail, - if (status > 0) goto out; - } - - if ((p->flags & ISO14B_SELECT_CTS) == ISO14B_SELECT_CTS) { - iso14b_cts_card_select_t cts; - sendlen = sizeof(iso14b_cts_card_select_t); - status = iso14443b_select_cts_card(&cts); - reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, (uint8_t *)&cts, sendlen); - // 0: OK 2: demod fail, 3:crc fail, - if (status > 0) goto out; + memset(card, 0, sizeof(iso14b_card_select_t)); + status = iso14443b_select_srx_card(card); + reply_ng(CMD_HF_ISO14443B_COMMAND, status, (uint8_t *)card, sendlen); + if (status != PM3_SUCCESS) goto out; } if ((p->flags & ISO14B_SELECT_XRX) == ISO14B_SELECT_XRX) { - status = iso14443b_select_xrx_card(&card); - reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, (uint8_t *)&card, sendlen); + memset(card, 0, sizeof(iso14b_card_select_t)); + status = iso14443b_select_xrx_card(card); + reply_ng(CMD_HF_ISO14443B_COMMAND, status, (uint8_t *)card, sendlen); // 0: OK, 1: select fail, 2: attrib fail, 3: crc fail, 4: password fail - if (status != 0) goto out; + if (status != PM3_SUCCESS) goto out; + } + + if ((p->flags & ISO14B_SELECT_CTS) == ISO14B_SELECT_CTS) { + iso14b_cts_card_select_t *cts = (iso14b_cts_card_select_t *)buf; + memset(cts, 0, sizeof(iso14b_cts_card_select_t)); + sendlen = sizeof(iso14b_cts_card_select_t); + status = iso14443b_select_cts_card(cts); + reply_ng(CMD_HF_ISO14443B_COMMAND, status, (uint8_t *)cts, sendlen); + if (status > PM3_SUCCESS) goto out; } if ((p->flags & ISO14B_APDU) == ISO14B_APDU) { - uint8_t res; - status = iso14443b_apdu(p->raw, p->rawlen, (p->flags & ISO14B_SEND_CHAINING), buf, sizeof(buf), &res); - sendlen = MIN(Demod.len, PM3_CMD_DATA_SIZE); - reply_mix(CMD_HF_ISO14443B_COMMAND, status, res, 0, buf, sendlen); + + int responselen = 0; + uint8_t response_byte = 0; + status = iso14443b_apdu(p->raw, p->rawlen, (p->flags & ISO14B_SEND_CHAINING), buf, sizeof(buf), &response_byte, &responselen); + + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred + reply_ng(CMD_HF_ISO14443B_COMMAND, PM3_ETEAROFF, NULL, 0); + } else { + responselen = MIN(responselen, PM3_CMD_DATA_SIZE); + + iso14b_raw_apdu_response_t *payload = (iso14b_raw_apdu_response_t *)BigBuf_calloc( sizeof(iso14b_raw_apdu_response_t) + responselen); + payload->response_byte = response_byte; + payload->datalen = responselen; + memcpy(payload->data, buf, payload->datalen); + + reply_ng(CMD_HF_ISO14443B_COMMAND, status, (uint8_t*)payload, sizeof(iso14b_raw_apdu_response_t) + responselen); + BigBuf_free_keep_EM(); + } } if ((p->flags & ISO14B_RAW) == ISO14B_RAW) { - if ((p->flags & ISO14B_APPEND_CRC) == ISO14B_APPEND_CRC) { - if (p->rawlen > 0) { - AddCrc14B(p->raw, p->rawlen); - p->rawlen += 2; - } + + uint8_t *raw = BigBuf_calloc(p->rawlen + 2); + memcpy(raw, p->raw, p->rawlen); + if ( + ((p->flags & ISO14B_APPEND_CRC) == ISO14B_APPEND_CRC) && (p->rawlen)) { + AddCrc14B(raw, p->rawlen); + p->rawlen += 2; } + uint32_t start_time = 0; uint32_t eof_time = 0; - CodeAndTransmit14443bAsReader(p->raw, p->rawlen, &start_time, &eof_time, true); + CodeAndTransmit14443bAsReader(raw, p->rawlen, &start_time, &eof_time, true); + FpgaDisableTracing(); if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occurred - FpgaDisableTracing(); - reply_mix(CMD_HF_ISO14443B_COMMAND, -2, 0, 0, NULL, 0); + reply_ng(CMD_HF_ISO14443B_COMMAND, PM3_ETEAROFF, NULL, 0); } else { eof_time += DELAY_ISO14443B_PCD_TO_PICC_READER; - status = Get14443bAnswerFromTag(buf, sizeof(buf), iso14b_timeout, &eof_time); // raw - FpgaDisableTracing(); - - sendlen = MIN(Demod.len, PM3_CMD_DATA_SIZE); - reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, Demod.output, sendlen); + uint16_t retlen = 0; + status = Get14443bAnswerFromTag(buf, sizeof(buf), iso14b_timeout, &eof_time, &retlen); + if (status == PM3_SUCCESS) { + sendlen = MIN(retlen, PM3_CMD_DATA_SIZE); + reply_ng(CMD_HF_ISO14443B_COMMAND, status, Demod.output, sendlen); + } else { + reply_ng(CMD_HF_ISO14443B_COMMAND, status, NULL, 0); + } } } out: // turn off trigger (LED_A) - if ((p->flags & ISO14B_REQUEST_TRIGGER) == ISO14B_REQUEST_TRIGGER) + if ((p->flags & ISO14B_REQUEST_TRIGGER) == ISO14B_REQUEST_TRIGGER) { iso14b_set_trigger(false); + } // turn off antenna et al // we don't send a HALT command. if ((p->flags & ISO14B_DISCONNECT) == ISO14B_DISCONNECT) { switch_off(); // disconnect raw SpinDelay(20); + BigBuf_free_keep_EM(); } } diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index 2579e6f1c..14da377d3 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -35,15 +35,13 @@ #endif void iso14443b_setup(void); -int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void *rxdata, uint16_t rxmaxlen, uint8_t *res); +int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void *rxdata, uint16_t rxmaxlen, uint8_t *res, int * responselen); int iso14443b_select_card(iso14b_card_select_t *card); -int iso14443b_select_card_srx(iso14b_card_select_t *card); -int iso14443b_select_xrx_card(iso14b_card_select_t *card); -void SimulateIso14443bTag(uint8_t *pupi); +void SimulateIso14443bTag(const uint8_t *pupi); void AcquireRawAdcSamplesIso14443b(uint32_t parameter); -void ReadSTBlock(uint8_t blocknr); +void read_14b_st_block(uint8_t blocknr); void SniffIso14443b(void); void SendRawCommand14443B(uint32_t, uint32_t, uint8_t, uint8_t[]); void SendRawCommand14443B_Ex(iso14b_raw_cmd_t *p); diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index daa84f886..da2f9255e 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -294,7 +294,12 @@ void CodeIso15693AsTag(const uint8_t *cmd, size_t len) { // Transmit the command (to the tag) that was placed in cmd[]. void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time, bool shallow_mod) { +#ifdef RDV4 + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | (shallow_mod ? FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4 : FPGA_HF_READER_MODE_SEND_FULL_MOD)); +#else FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | (shallow_mod ? FPGA_HF_READER_MODE_SEND_SHALLOW_MOD : FPGA_HF_READER_MODE_SEND_FULL_MOD)); +#endif + if (*start_time < DELAY_ARM_TO_TAG) { *start_time = DELAY_ARM_TO_TAG; @@ -1055,7 +1060,7 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo if (dtf->len > dtf->max_len) { ret = PM3_EOVFLOW; - Dbprintf("overflow (%d > %d", dtf->len, dtf->max_len); + Dbprintf("overflow (%d > %d)", dtf->len, dtf->max_len); } break; } @@ -1078,7 +1083,7 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo if (dt->len > dt->max_len) { ret = PM3_EOVFLOW; - Dbprintf("overflow (%d > %d", dt->len, dt->max_len); + Dbprintf("overflow (%d > %d)", dt->len, dt->max_len); } break; } @@ -1736,14 +1741,19 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla // no need to try decoding reader data if the tag is sending if (!tag_is_active) { - if (Handle15693SampleFromReader((sniffdata & 0x02) >> 1, &dreader)) { + int extra_8s = 1; + if (Handle15693SampleFromReader((sniffdata & 0x02) >> 1, &dreader) || + (++extra_8s && Handle15693SampleFromReader(sniffdata & 0x01, &dreader))) { - uint32_t eof_time = dma_start_time + (samples * 16) + 8 - DELAY_READER_TO_ARM_SNIFF; // end of EOF if (dreader.byteCount > 0) { + // sof/eof_times are in ssp_clk, which is 13.56MHz / 4 + // not sure where the extra +8's on the EOF time comes from though, if someone knows update this comment + uint32_t eof_time = dma_start_time + (samples * 16) + (extra_8s * 8) - DELAY_READER_TO_ARM_SNIFF; // end of EOF uint32_t sof_time = eof_time - - dreader.byteCount * (dreader.Coding == CODING_1_OUT_OF_4 ? 128 * 16 : 2048 * 16) // time for byte transfers - - 32 * 16 // time for SOF transfer - - 16 * 16; // time for EOF transfer + - dreader.byteCount * (dreader.Coding == CODING_1_OUT_OF_4 ? 1024 : 16384) // time for byte transfers + - 256 // time for SOF transfer (1024/fc / 4) + - 128; // time for EOF transfer (512/fc / 4) + // sof/eof_times * 4 here to bring from ssp_clk freq to RF carrier freq LogTrace_ISO15693(dreader.output, dreader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); if (!iclass) { // Those flags don't exist in iClass @@ -1751,52 +1761,38 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla expect_fast_answer = dreader.output[0] & ISO15_REQ_DATARATE_HIGH; } } + // And ready to receive another command. //DecodeReaderReset(&dreader); // already reseted DecodeTagReset(&dtag); DecodeTagFSKReset(&dtagfsk); reader_is_active = false; expect_tag_answer = true; - } else if (Handle15693SampleFromReader(sniffdata & 0x01, &dreader)) { - - uint32_t eof_time = dma_start_time + (samples * 16) + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF - if (dreader.byteCount > 0) { - uint32_t sof_time = eof_time - - dreader.byteCount * (dreader.Coding == CODING_1_OUT_OF_4 ? 128 * 16 : 2048 * 16) // time for byte transfers - - 32 * 16 // time for SOF transfer - - 16 * 16; // time for EOF transfer - LogTrace_ISO15693(dreader.output, dreader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); - if (!iclass) { // Those flags don't exist in iClass - expect_fsk_answer = dreader.output[0] & ISO15_REQ_SUBCARRIER_TWO; - expect_fast_answer = dreader.output[0] & ISO15_REQ_DATARATE_HIGH; - } - } - // And ready to receive another command - //DecodeReaderReset(&dreader); // already reseted - DecodeTagReset(&dtag); - DecodeTagFSKReset(&dtagfsk); - reader_is_active = false; - expect_tag_answer = true; } else { reader_is_active = (dreader.state >= STATE_READER_RECEIVE_DATA_1_OUT_OF_4); } } - if (!reader_is_active && expect_tag_answer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet + // no need to try decoding tag data if the reader is currently sending or no answer expected yet + if (!reader_is_active && expect_tag_answer) { if (!expect_fsk_answer) { + // single subcarrier tag response if (Handle15693SamplesFromTag((sniffdata >> 4) << 2, &dtag, expect_fast_answer)) { + // sof/eof_times are in ssp_clk, which is 13.56MHz / 4 uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF if (dtag.lastBit == SOF_PART2) { eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) } uint32_t sof_time = eof_time - - dtag.len * 8 * 8 * 16 // time for byte transfers - - (32 * 16) // time for SOF transfer - - (dtag.lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer + - dtag.len * 1024 // time for byte transfers (4096/fc / 4) + - 512 // time for SOF transfer (2048/fc / 4) + - (dtag.lastBit != SOF_PART2 ? 512 : 0); // time for EOF transfer (2048/fc / 4) + // sof/eof_times * 4 here to bring from ssp_clk freq to RF carrier freq LogTrace_ISO15693(dtag.output, dtag.len, (sof_time * 4), (eof_time * 4), NULL, false); + // And ready to receive another response. DecodeTagReset(&dtag); DecodeTagFSKReset(&dtagfsk); @@ -1807,26 +1803,23 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla tag_is_active = (dtag.state >= STATE_TAG_RECEIVING_DATA); } } else { + // dual subcarrier tag response if (FREQ_IS_0((sniffdata >> 2) & 0x3)) // tolerate 1 00 sniffdata = sniffdata_prev; if (Handle15693FSKSamplesFromTag((sniffdata >> 2) & 0x3, &dtagfsk, expect_fast_answer)) { - expect_fsk_answer = false; - } else { - tag_is_active = (dtagfsk.state >= STATE_FSK_RECEIVING_DATA_484); - } - if (!expect_fsk_answer) { - // FSK answer no more expected: switch back to ASK if (dtagfsk.len > 0) { + // sof/eof_times are in ssp_clk, which is 13.56MHz / 4 uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF if (dtagfsk.lastBit == SOF) { eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) } uint32_t sof_time = eof_time - - dtagfsk.len * 8 * 8 * 16 // time for byte transfers - - (32 * 16) // time for SOF transfer - - (dtagfsk.lastBit != SOF ? (32 * 16) : 0); // time for EOF transfer + - dtagfsk.len * 1016 // time for byte transfers (4064/fc / 4) - FSK is slightly different + - 512 // time for SOF transfer (2048/fc / 4) + - (dtagfsk.lastBit != SOF ? 512 : 0); // time for EOF transfer (2048/fc / 4) + // sof/eof_times * 4 here to bring from ssp_clk freq to RF carrier freq LogTrace_ISO15693(dtagfsk.output, dtagfsk.len, (sof_time * 4), (eof_time * 4), NULL, false); } @@ -1834,6 +1827,10 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla DecodeReaderReset(&dreader); expect_tag_answer = false; tag_is_active = false; + // FSK answer no more expected: switch back to ASK + expect_fsk_answer = false; + } else { + tag_is_active = (dtagfsk.state >= STATE_FSK_RECEIVING_DATA_484); } } } @@ -1843,20 +1840,20 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string, bool icla switch_off(); DbpString(""); - DbpString(_CYAN_("Sniff statistics")); - DbpString("================================="); - Dbprintf(" DecodeTag State........%d", dtag.state); - Dbprintf(" DecodeTag byteCnt......%d", dtag.len); - Dbprintf(" DecodeTag posCount.....%d", dtag.posCount); - Dbprintf(" DecodeTagFSK State.....%d", dtagfsk.state); - Dbprintf(" DecodeTagFSK byteCnt...%d", dtagfsk.len); - Dbprintf(" DecodeTagFSK count.....%d", dtagfsk.count); - Dbprintf(" DecodeReader State.....%d", dreader.state); - Dbprintf(" DecodeReader byteCnt...%d", dreader.byteCount); - Dbprintf(" DecodeReader posCount..%d", dreader.posCount); - Dbprintf(" Trace length..........." _YELLOW_("%d"), BigBuf_get_traceLen()); - DbpString(""); - + if (g_dbglevel > DBG_ERROR) { + DbpString(_CYAN_("Sniff statistics")); + DbpString("================================="); + Dbprintf("DecodeTag State........ %d", dtag.state); + Dbprintf("DecodeTag byteCnt...... %d", dtag.len); + Dbprintf("DecodeTag posCount..... %d", dtag.posCount); + Dbprintf("DecodeTagFSK State..... %d", dtagfsk.state); + Dbprintf("DecodeTagFSK byteCnt... %d", dtagfsk.len); + Dbprintf("DecodeTagFSK count..... %d", dtagfsk.count); + Dbprintf("DecodeReader State..... %d", dreader.state); + Dbprintf("DecodeReader byteCnt... %d", dreader.byteCount); + Dbprintf("DecodeReader posCount.. %d", dreader.posCount); + } + Dbprintf("Trace length........... " _YELLOW_("%d"), BigBuf_get_traceLen()); } // Initialize Proxmark3 as ISO15693 reader @@ -2127,7 +2124,6 @@ void Iso15693InitTag(void) { StartCountSspClk(); } - void EmlClearIso15693(void) { // Resetting the bitstream also frees the BigBuf memory, so we do this here to prevent // an inconvenient reset in the future by Iso15693InitTag @@ -2136,16 +2132,6 @@ void EmlClearIso15693(void) { reply_ng(CMD_HF_ISO15693_EML_CLEAR, PM3_SUCCESS, NULL, 0); } -void EmlSetMemIso15693(uint8_t count, uint8_t *data, uint32_t offset) { - uint8_t *emCARD = BigBuf_get_EM_addr(); - memcpy(emCARD + offset, data, count); -} - -void EmlGetMemIso15693(uint8_t count, uint8_t *output, uint32_t offset) { - uint8_t *emCARD = BigBuf_get_EM_addr(); - memcpy(output, emCARD + offset, count); -} - // Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands // all demodulation performed in arm rather than host. - greg void SimTagIso15693(uint8_t *uid, uint8_t block_size) { @@ -2304,8 +2290,11 @@ void SimTagIso15693(uint8_t *uid, uint8_t block_size) { } // Block data if (block_size * (block_idx + j + 1) <= CARD_MEMORY_SIZE) { - EmlGetMemIso15693(block_size, resp_readblock + (work_offset + security_offset), - block_size * (block_idx + j)); + emlGet( + resp_readblock + (work_offset + security_offset), + block_size * (block_idx + j), + block_size + ); } else { memset(resp_readblock + work_offset + security_offset, 0, block_size); } @@ -2342,7 +2331,7 @@ void SimTagIso15693(uint8_t *uid, uint8_t block_size) { uint8_t *data = cmd + 3 + address_offset + multi_offset; // write data - EmlSetMemIso15693(block_count * block_size, data, block_idx * block_size); + emlSet(data, (block_idx * block_size), (block_count * block_size)); // Build WRITE_(MULTI_)BLOCK response int response_length = 3; @@ -2672,7 +2661,7 @@ void SetTag15693Uid(const uint8_t *uid) { switch_off(); } -static void init_password_15693_Slix(uint8_t *buffer, uint8_t *pwd, const uint8_t *rnd) { +static void init_password_15693_Slix(uint8_t *buffer, const uint8_t *pwd, const uint8_t *rnd) { memcpy(buffer, pwd, 4); if (rnd) { buffer[0] ^= rnd[0]; @@ -2700,7 +2689,7 @@ static bool get_rnd_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t return true; } -static uint32_t disable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, uint8_t *password) { +static uint32_t disable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password) { uint8_t rnd[2]; if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { @@ -2722,7 +2711,7 @@ static uint32_t disable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_ti return PM3_SUCCESS; } -static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, uint8_t *password, uint8_t *uid) { +static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pass_id, const uint8_t *password, uint8_t *uid) { uint8_t rnd[2]; @@ -2749,7 +2738,7 @@ static uint32_t set_pass_15693_Slix(uint32_t start_time, uint32_t *eof_time, uin return PM3_SUCCESS; } -static uint32_t set_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *password) { +static uint32_t set_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, const uint8_t *password) { uint8_t rnd[2]; if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { return PM3_ETIMEOUT; @@ -2770,7 +2759,7 @@ static uint32_t set_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, return PM3_SUCCESS; } -static uint32_t disable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *password, bool usepwd) { +static uint32_t disable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, const uint8_t *password, bool usepwd) { uint8_t uid[8]; get_uid_slix(start_time, eof_time, uid); @@ -2804,7 +2793,7 @@ static uint32_t disable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, } -static uint32_t enable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *password, bool usepwd) { +static uint32_t enable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, const uint8_t *password, bool usepwd) { uint8_t uid[8]; get_uid_slix(start_time, eof_time, uid); @@ -2836,7 +2825,7 @@ static uint32_t enable_eas_15693_Slix(uint32_t start_time, uint32_t *eof_time, u return PM3_SUCCESS; } -static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pwd_id, uint8_t *password, uint8_t *uid) { +static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t pwd_id, const uint8_t *password, uint8_t *uid) { uint8_t new_pwd_cmd[] = { (ISO15_REQ_DATARATE_HIGH | ISO15_REQ_ADDRESS), ISO15693_WRITE_PASSWORD, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, pwd_id, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -2857,7 +2846,7 @@ static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_tim return PM3_SUCCESS; } -static uint32_t pass_protect_EASAFI_15693_Slix(uint32_t start_time, uint32_t *eof_time, bool set_option_flag, uint8_t *password) { +static uint32_t pass_protect_EASAFI_15693_Slix(uint32_t start_time, uint32_t *eof_time, bool set_option_flag, const uint8_t *password) { uint8_t flags; @@ -2896,7 +2885,7 @@ static uint32_t pass_protect_EASAFI_15693_Slix(uint32_t start_time, uint32_t *eo return PM3_SUCCESS; } -static uint32_t write_afi_15693(uint32_t start_time, uint32_t *eof_time, uint8_t *password, bool usepwd, uint8_t *uid, bool use_uid, uint8_t afi) { +static uint32_t write_afi_15693(uint32_t start_time, uint32_t *eof_time, const uint8_t *password, bool usepwd, uint8_t *uid, bool use_uid, uint8_t afi) { if (!use_uid) { int res_getuid = get_uid_slix(start_time, eof_time, uid); @@ -2933,7 +2922,7 @@ static uint32_t write_afi_15693(uint32_t start_time, uint32_t *eof_time, uint8_t } /* -static uint32_t enable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, uint8_t pass_id, uint8_t *password) { +static uint32_t enable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, uint8_t pass_id, const uint8_t *password) { uint8_t rnd[2]; if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { return PM3_ETIMEOUT; @@ -2954,7 +2943,7 @@ static uint32_t enable_privacy_15693_Slix(uint32_t start_time, uint32_t *eof_tim return PM3_SUCCESS; } -static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, uint8_t pass_id, uint8_t *password) { +static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, uint8_t pass_id, const uint8_t *password) { uint8_t rnd[2]; if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { return PM3_ETIMEOUT; @@ -2977,7 +2966,7 @@ static uint32_t write_password_15693_Slix(uint32_t start_time, uint32_t *eof_tim return PM3_SUCCESS; } -static uint32_t destroy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, uint8_t *password) { +static uint32_t destroy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint8_t *uid, const uint8_t *password) { uint8_t rnd[2]; if (get_rnd_15693_Slix(start_time, eof_time, rnd) == false) { @@ -3001,7 +2990,7 @@ static uint32_t destroy_15693_Slix(uint32_t start_time, uint32_t *eof_time, uint */ -void WritePasswordSlixIso15693(uint8_t *old_password, uint8_t *new_password, uint8_t pwd_id) { +void WritePasswordSlixIso15693(const uint8_t *old_password, const uint8_t *new_password, uint8_t pwd_id) { LED_D_ON(); Iso15693InitReader(); StartCountSspClk(); @@ -3026,7 +3015,7 @@ void WritePasswordSlixIso15693(uint8_t *old_password, uint8_t *new_password, uin } -void DisablePrivacySlixIso15693(uint8_t *password) { +void DisablePrivacySlixIso15693(const uint8_t *password) { LED_D_ON(); Iso15693InitReader(); StartCountSspClk(); @@ -3041,7 +3030,7 @@ void DisablePrivacySlixIso15693(uint8_t *password) { switch_off(); } -void EnablePrivacySlixIso15693(uint8_t *password) { +void EnablePrivacySlixIso15693(const uint8_t *password) { LED_D_ON(); Iso15693InitReader(); StartCountSspClk(); @@ -3057,7 +3046,7 @@ void EnablePrivacySlixIso15693(uint8_t *password) { } -void DisableEAS_AFISlixIso15693(uint8_t *password, bool usepwd) { +void DisableEAS_AFISlixIso15693(const uint8_t *password, bool usepwd) { LED_D_ON(); Iso15693InitReader(); StartCountSspClk(); @@ -3075,7 +3064,7 @@ void DisableEAS_AFISlixIso15693(uint8_t *password, bool usepwd) { switch_off(); } -void EnableEAS_AFISlixIso15693(uint8_t *password, bool usepwd) { +void EnableEAS_AFISlixIso15693(const uint8_t *password, bool usepwd) { LED_D_ON(); Iso15693InitReader(); StartCountSspClk(); @@ -3090,7 +3079,7 @@ void EnableEAS_AFISlixIso15693(uint8_t *password, bool usepwd) { switch_off(); } -void PassProtextEASSlixIso15693(uint8_t *password) { +void PassProtextEASSlixIso15693(const uint8_t *password) { LED_D_ON(); Iso15693InitReader(); StartCountSspClk(); @@ -3099,7 +3088,7 @@ void PassProtextEASSlixIso15693(uint8_t *password) { reply_ng(CMD_HF_ISO15693_SLIX_PASS_PROTECT_EAS, res, NULL, 0); switch_off(); } -void PassProtectAFISlixIso15693(uint8_t *password) { +void PassProtectAFISlixIso15693(const uint8_t *password) { LED_D_ON(); Iso15693InitReader(); StartCountSspClk(); @@ -3109,7 +3098,7 @@ void PassProtectAFISlixIso15693(uint8_t *password) { switch_off(); } -void WriteAFIIso15693(uint8_t *password, bool use_pwd, uint8_t *uid, bool use_uid, uint8_t afi) { +void WriteAFIIso15693(const uint8_t *password, bool use_pwd, uint8_t *uid, bool use_uid, uint8_t afi) { LED_D_ON(); Iso15693InitReader(); StartCountSspClk(); diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index b087edd2e..8a9e2fe82 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -47,8 +47,6 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo void AcquireRawAdcSamplesIso15693(void); void ReaderIso15693(iso15_card_select_t *p_card); // ISO15693 reader void EmlClearIso15693(void); -void EmlSetMemIso15693(uint8_t count, uint8_t *data, uint32_t offset); -void EmlGetMemIso15693(uint8_t count, uint8_t *output, uint32_t offset); void SimTagIso15693(uint8_t *uid, uint8_t block_size); // simulate an ISO15693 tag void BruteforceIso15693Afi(uint32_t speed); // find an AFI of a tag void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t *data); // send arbitrary commands from CLI @@ -62,12 +60,12 @@ int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, ui void SetTag15693Uid(const uint8_t *uid); -void WritePasswordSlixIso15693(uint8_t *old_password, uint8_t *new_password, uint8_t pwd_id); -void DisablePrivacySlixIso15693(uint8_t *password); -void EnablePrivacySlixIso15693(uint8_t *password); -void DisableEAS_AFISlixIso15693(uint8_t *password, bool usepwd); -void EnableEAS_AFISlixIso15693(uint8_t *password, bool usepwd); -void PassProtextEASSlixIso15693(uint8_t *password); -void PassProtectAFISlixIso15693(uint8_t *password); -void WriteAFIIso15693(uint8_t *password, bool usepwd, uint8_t *uid, bool use_uid, uint8_t afi); +void WritePasswordSlixIso15693(const uint8_t *old_password, const uint8_t *new_password, uint8_t pwd_id); +void DisablePrivacySlixIso15693(const uint8_t *password); +void EnablePrivacySlixIso15693(const uint8_t *password); +void DisableEAS_AFISlixIso15693(const uint8_t *password, bool usepwd); +void EnableEAS_AFISlixIso15693(const uint8_t *password, bool usepwd); +void PassProtextEASSlixIso15693(const uint8_t *password); +void PassProtectAFISlixIso15693(const uint8_t *password); +void WriteAFIIso15693(const uint8_t *password, bool usepwd, uint8_t *uid, bool use_uid, uint8_t afi); #endif diff --git a/armsrc/ldscript b/armsrc/ldscript index 1dd09e937..0824205a2 100644 --- a/armsrc/ldscript +++ b/armsrc/ldscript @@ -53,7 +53,7 @@ SECTIONS .bss : { __bss_start__ = .; *(.bss) - *(.bss.*) + *(.bss.*) . = ALIGN(4); __bss_end__ = .; } >ram AT>ram :bss diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index c6af0dace..42d511875 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -528,7 +528,7 @@ OUT: StopTicks(); } -void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, uint8_t *data) { +void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, const uint8_t *data) { // configure ARM and FPGA init_reader(); diff --git a/armsrc/legicrf.h b/armsrc/legicrf.h index 670688aab..02f20b536 100644 --- a/armsrc/legicrf.h +++ b/armsrc/legicrf.h @@ -25,7 +25,7 @@ void LegicRfInfo(void); int LegicRfReaderEx(uint16_t offset, uint16_t len, uint8_t iv); void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv); -void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, uint8_t *data); +void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, const uint8_t *data); legic_card_select_t *getLegicCardInfo(void); #endif /* __LEGICRF_H */ diff --git a/armsrc/legicrfsim.c b/armsrc/legicrfsim.c index 58ca40320..1f6accf4a 100644 --- a/armsrc/legicrfsim.c +++ b/armsrc/legicrfsim.c @@ -91,18 +91,18 @@ static bool wait_for(bool value, const uint32_t timeout) { // - A bit length >80.2us is a 1 // - A bit length <80.2us is a 0 // - A bit length >148.6us is a code violation -static int8_t rx_bit(void) { +static int32_t rx_bit(void) { // backup ts for threshold calculation uint32_t bit_start = last_frame_end; // wait for pause to end - if (!wait_for(RWD_PULSE, bit_start + RWD_TIME_1 * 3 / 2)) { - return -1; + if (wait_for(RWD_PULSE, bit_start + RWD_TIME_1 * 3 / 2) == false) { + return PM3_ERFTRANS; } // wait for next pause - if (!wait_for(RWD_PAUSE, bit_start + RWD_TIME_1 * 3 / 2)) { - return -1; + if (wait_for(RWD_PAUSE, bit_start + RWD_TIME_1 * 3 / 2) == false) { + return PM3_ERFTRANS; } // update bit and frame end @@ -110,7 +110,7 @@ static int8_t rx_bit(void) { // check for code violation (bit to short) if (last_frame_end - bit_start < RWD_TIME_PAUSE) { - return -1; + return PM3_ERFTRANS; } // apply threshold (average of RWD_TIME_0 and ) @@ -215,7 +215,6 @@ static void tx_ack(void) { // - receive the frame // - detect end of frame (last pause) static int32_t rx_frame(uint8_t *len) { - int32_t frame = 0; // add 2 SSP clock cycles (1 for tx and 1 for rx pipeline delay) // those will be subtracted at the end of the rx phase @@ -235,23 +234,24 @@ static int32_t rx_frame(uint8_t *len) { // check for code violation if (i > RWD_CMD_TIMEOUT) { - return -1; + return PM3_ETIMEOUT; } } // backup ts for trace log uint32_t last_frame_start = last_frame_end; + int32_t frame = 0; // receive frame for (*len = 0; true; ++(*len)) { // receive next bit LED_B_ON(); - int8_t bit = rx_bit(); + int32_t bit = rx_bit(); LED_B_OFF(); // check for code violation and to short / long frame if ((bit < 0) && ((*len < RWD_MIN_FRAME_LEN) || (*len > RWD_MAX_FRAME_LEN))) { - return -1; + return PM3_ERFTRANS; } // check for code violation caused by end of frame @@ -353,7 +353,7 @@ static int32_t setup_phase(legic_card_select_t *p_card) { // wait for iv int32_t iv = rx_frame(&len); if ((len != 7) || (iv < 0)) { - return -1; + return PM3_ETIMEOUT; } // configure prng @@ -375,19 +375,19 @@ static int32_t setup_phase(legic_card_select_t *p_card) { // wait for ack int32_t ack = rx_frame(&len); if ((len != 6) || (ack < 0)) { - return -1; + return PM3_ETIMEOUT; } // validate data switch (p_card->tagtype) { case 0: - if (ack != 0x19) return -1; + if (ack != 0x19) return PM3_ERFTRANS; break; case 1: - if (ack != 0x39) return -1; + if (ack != 0x39) return PM3_ERFTRANS; break; case 2: - if (ack != 0x39) return -1; + if (ack != 0x39) return PM3_ERFTRANS; break; } @@ -399,7 +399,7 @@ static int32_t setup_phase(legic_card_select_t *p_card) { // the gap by one period. last_frame_end += TAG_BIT_PERIOD; - return 0; + return PM3_SUCCESS; } static uint8_t calc_crc4(uint16_t cmd, uint8_t cmd_sz, uint8_t value) { @@ -414,7 +414,7 @@ static int32_t connected_phase(legic_card_select_t *p_card) { // wait for command int32_t cmd = rx_frame(&len); if (cmd < 0) { - return -1; + return PM3_ETIMEOUT; } // check if command is LEGIC_READ @@ -425,8 +425,7 @@ static int32_t connected_phase(legic_card_select_t *p_card) { // transmit data tx_frame((crc << 8) | byte, 12); - - return 0; + return PM3_SUCCESS; } // check if command is LEGIC_WRITE @@ -441,7 +440,7 @@ static int32_t connected_phase(legic_card_select_t *p_card) { uint8_t calc_crc = calc_crc4(addr << 1, p_card->cmdsize, byte); if (calc_crc != crc) { Dbprintf("!!! crc mismatch: %x != %x !!!", calc_crc, crc); - return -1; + return PM3_ECRC; } // store data @@ -449,11 +448,10 @@ static int32_t connected_phase(legic_card_select_t *p_card) { // transmit ack tx_ack(); - - return 0; + return PM3_SUCCESS; } - return -1; + return PM3_ERFTRANS; } //----------------------------------------------------------------------------- @@ -466,26 +464,26 @@ void LegicRfSimulate(uint8_t tagtype, bool send_reply) { // configure ARM and FPGA init_tag(); - int res = PM3_SUCCESS; + int res = init_card(tagtype, &card); // verify command line input - if (init_card(tagtype, &card) != PM3_SUCCESS) { + if (res != PM3_SUCCESS) { DbpString("Unknown tagtype to simulate"); - res = PM3_ESOFT; goto OUT; } - uint16_t counter = 0; LED_A_ON(); - Dbprintf("Legic Prime, simulating uid: %02X%02X%02X%02X", legic_mem[0], legic_mem[1], legic_mem[2], legic_mem[3]); + Dbprintf("Legic Prime, simulating MCD... " _YELLOW_("%02X") " MSN... " _YELLOW_("%02X%02X%02X"), legic_mem[0], legic_mem[1], legic_mem[2], legic_mem[3]); + uint16_t counter = 0; while (BUTTON_PRESS() == false) { + WDT_HIT(); if (counter >= 2000) { if (data_available()) { res = PM3_EOPABORTED; - break; + goto OUT; } counter = 0; } @@ -497,12 +495,12 @@ void LegicRfSimulate(uint8_t tagtype, bool send_reply) { } // wait for connection, restart on error - if (setup_phase(&card)) { + if (setup_phase(&card) != PM3_SUCCESS) { continue; } // connection is established, process commands until one fails - while (connected_phase(&card) == false) { + while (connected_phase(&card) == PM3_SUCCESS) { WDT_HIT(); } } @@ -510,17 +508,19 @@ void LegicRfSimulate(uint8_t tagtype, bool send_reply) { OUT: if (g_dbglevel >= DBG_ERROR) { - Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", get_tracing(), BigBuf_get_traceLen()); + Dbprintf("Emulator stopped. Trace length... " _YELLOW_("%d"), BigBuf_get_traceLen()); } - if (res == PM3_EOPABORTED) - DbpString("aborted by user"); + if (res == PM3_EOPABORTED) { + DbpString("Aborted by user"); + } switch_off(); StopTicks(); - if (send_reply) + if (send_reply) { reply_ng(CMD_HF_LEGIC_SIMULATE, res, NULL, 0); + } BigBuf_free_keep_EM(); } diff --git a/armsrc/lfops.c b/armsrc/lfops.c index c130d78e8..9c5990fcc 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -44,7 +44,7 @@ Notes about EM4xxx timings. The timing values differs between cards, we got EM410x, EM43x5, EM445x etc. -We are trying to unify and enable the Proxmark to easily detect and select correct timings automatic. +We are trying to unify and enable the Proxmark3 to easily detect and select correct timings automatic. The measures from datasheets doesn't always match correct the hardware features of RDV4 antenans and we still wanted to let other devices with other custom antennas still benefit from this repo. This is why its configurable and we use to set these dynamic settings in device external flash memory. @@ -287,7 +287,7 @@ void printT55xxConfig(void) { DbpString(""); } -void setT55xxConfig(uint8_t arg0, t55xx_configurations_t *c) { +void setT55xxConfig(uint8_t arg0, const t55xx_configurations_t *c) { for (uint8_t i = 0; i < 4; i++) { if (c->m[i].start_gap != 0) T55xx_Timing.m[i].start_gap = c->m[i].start_gap; @@ -391,6 +391,8 @@ void loadT55xxConfig(void) { if (isok == T55XX_CONFIG_LEN) { if (g_dbglevel > 1) DbpString("T55XX Config load success"); } + + BigBuf_free(); #endif } @@ -1009,7 +1011,7 @@ void CmdHIDsimTAG(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool // prepare a waveform pattern in the buffer based on the ID given then // simulate a FSK tag until the button is pressed // arg1 contains fcHigh and fcLow, arg2 contains STT marker and clock -void CmdFSKsimTAGEx(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, uint8_t *bits, bool ledcontrol, int numcycles) { +void CmdFSKsimTAGEx(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, const uint8_t *bits, bool ledcontrol, int numcycles) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); @@ -1045,7 +1047,7 @@ void CmdFSKsimTAGEx(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t cl // prepare a waveform pattern in the buffer based on the ID given then // simulate a FSK tag until the button is pressed // arg1 contains fcHigh and fcLow, arg2 contains STT marker and clock -void CmdFSKsimTAG(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, uint8_t *bits, bool ledcontrol) { +void CmdFSKsimTAG(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, const uint8_t *bits, bool ledcontrol) { CmdFSKsimTAGEx(fchigh, fclow, separator, clk, bitslen, bits, ledcontrol, -1); reply_ng(CMD_LF_FSK_SIMULATE, PM3_EOPABORTED, NULL, 0); } @@ -1866,9 +1868,9 @@ void T55xxResetRead(uint8_t flags, bool ledcontrol) { if (ledcontrol) LED_A_OFF(); } -void T55xxDangerousRawTest(uint8_t *data, bool ledcontrol) { +void T55xxDangerousRawTest(const uint8_t *data, bool ledcontrol) { // supports only default downlink mode - t55xx_test_block_t *c = (t55xx_test_block_t *)data; + const t55xx_test_block_t *c = (const t55xx_test_block_t *)data; uint8_t start_wait = 4; uint8_t bs[128 / 8]; @@ -2320,7 +2322,7 @@ void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, boo } // clone viking tag to T55xx -void CopyVikingtoT55xx(uint8_t *blocks, bool q5, bool em, bool ledcontrol) { +void CopyVikingtoT55xx(const uint8_t *blocks, bool q5, bool em, bool ledcontrol) { uint32_t data[] = {T55x7_BITRATE_RF_32 | T55x7_MODULATION_MANCHESTER | (2 << T55x7_MAXBLOCK_SHIFT), 0, 0}; if (q5) { diff --git a/armsrc/lfops.h b/armsrc/lfops.h index f1bc88b64..755afa88f 100644 --- a/armsrc/lfops.h +++ b/armsrc/lfops.h @@ -38,9 +38,9 @@ void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, boo void CmdHIDsimTAG(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol); void CmdFSKsimTAGEx(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, - uint8_t *bits, bool ledcontrol, int numcycles); + const uint8_t *bits, bool ledcontrol, int numcycles); void CmdFSKsimTAG(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, - uint8_t *bits, bool ledcontrol); + const uint8_t *bits, bool ledcontrol); void CmdASKsimTAG(uint8_t encoding, uint8_t invert, uint8_t separator, uint8_t clk, uint16_t size, const uint8_t *bits, bool ledcontrol); void CmdPSKsimTAG(uint8_t carrier, uint8_t invert, uint8_t clk, uint16_t size, @@ -54,7 +54,7 @@ int lf_em410x_watch(int findone, uint32_t *high, uint64_t *low, bool ledcontrol) int lf_io_watch(int findone, uint32_t *high, uint32_t *low, bool ledcontrol); void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool q5, bool em, bool ledcontrol); // Clone an HID card to T5557/T5567 -void CopyVikingtoT55xx(uint8_t *blocks, bool q5, bool em, bool ledcontrol); +void CopyVikingtoT55xx(const uint8_t *blocks, bool q5, bool em, bool ledcontrol); int copy_em410x_to_t55xx(uint8_t card, uint8_t clock, uint32_t id_hi, uint32_t id_lo, bool ledcontrol); @@ -66,7 +66,7 @@ void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint8_t downlink_mode, bool ledcontrol); void T55xxWakeUp(uint32_t pwd, uint8_t flags, bool ledcontrol); void T55xx_ChkPwds(uint8_t flags, bool ledcontrol); -void T55xxDangerousRawTest(uint8_t *data, bool ledcontrol); +void T55xxDangerousRawTest(const uint8_t *data, bool ledcontrol); void turn_read_lf_on(uint32_t delay); void turn_read_lf_off(uint32_t delay); @@ -78,7 +78,7 @@ void EM4xWriteWord(uint8_t addr, uint32_t data, uint32_t pwd, uint8_t usepwd, bo void EM4xProtectWord(uint32_t data, uint32_t pwd, uint8_t usepwd, bool ledcontrol); void Cotag(uint32_t arg0, bool ledcontrol); -void setT55xxConfig(uint8_t arg0, t55xx_configurations_t *c); +void setT55xxConfig(uint8_t arg0, const t55xx_configurations_t *c); t55xx_configurations_t *getT55xxConfig(void); void printT55xxConfig(void); void loadT55xxConfig(void); diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index a56e013ee..ec79630eb 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -27,6 +27,7 @@ #include "lfdemod.h" #include "string.h" // memset #include "appmain.h" // print stack +#include "usb_cdc.h" // real-time sampling /* Default LF config is set to: @@ -94,7 +95,7 @@ void setDefaultSamplingConfig(void) { * @brief setSamplingConfig * @param sc */ -void setSamplingConfig(sample_config *sc) { +void setSamplingConfig(const sample_config *sc) { // decimation (1-8) how many bits of adc sample value to save if (sc->decimation > 0 && sc->decimation < 9) @@ -128,20 +129,6 @@ sample_config *getSamplingConfig(void) { return &config; } -/** - * @brief Pushes bit onto the stream - * @param stream - * @param bit - */ -static void pushBit(BitstreamOut_t *stream, uint8_t bit) { - int bytepos = stream->position >> 3; // divide by 8 - int bitpos = stream->position & 7; - *(stream->buffer + bytepos) &= ~(1 << (7 - bitpos)); - *(stream->buffer + bytepos) |= (bit > 0) << (7 - bitpos); - stream->position++; - stream->numbits++; -} - void initSampleBuffer(uint32_t *sample_size) { initSampleBufferEx(sample_size, false); } @@ -233,13 +220,20 @@ void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool data.numbits = samples.total_saved << 3; } else { - pushBit(&data, sample & 0x80); - if (bits_per_sample > 1) pushBit(&data, sample & 0x40); - if (bits_per_sample > 2) pushBit(&data, sample & 0x20); - if (bits_per_sample > 3) pushBit(&data, sample & 0x10); - if (bits_per_sample > 4) pushBit(&data, sample & 0x08); - if (bits_per_sample > 5) pushBit(&data, sample & 0x04); - if (bits_per_sample > 6) pushBit(&data, sample & 0x02); + // truncate trailing data + sample >>= 8 - bits_per_sample; + sample <<= 8 - bits_per_sample; + + uint8_t bits_offset = data.numbits & 0x7; + uint8_t bits_cap = 8 - bits_offset; + + // write the current byte + data.buffer[data.numbits >> 3] |= sample >> bits_offset; + uint32_t numbits = data.numbits + bits_cap; + + // write the remaining bits to the next byte + data.buffer[numbits >> 3] |= sample << (bits_cap); + data.numbits += bits_per_sample; } } @@ -312,7 +306,7 @@ uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, in // only every 4000th times, in order to save time when collecting samples. // interruptible only when logging not yet triggered - if ((checked >= 4000) && trigger_hit == false) { + if (trigger_hit == false && (checked >= 4000)) { if (data_available()) { checked = -1; break; @@ -331,7 +325,7 @@ uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, in if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - // Test point 8 (TP8) can be used to trigger oscilloscope + // (RDV4) Test point 8 (TP8) can be used to trigger oscilloscope if (ledcontrol) LED_D_OFF(); // threshold either high or low values 128 = center 0. if trigger = 178 @@ -344,10 +338,9 @@ uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, in } continue; } + trigger_hit = true; } - trigger_hit = true; - if (samples_to_skip > 0) { samples_to_skip--; continue; @@ -377,6 +370,7 @@ uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, in } return data.numbits; } + /** * @brief Does sample acquisition, ignoring the config values set in the sample_config. * This method is typically used by tag-specific readers who just wants to read the samples @@ -431,6 +425,123 @@ uint32_t SampleLF(bool verbose, uint32_t sample_size, bool ledcontrol) { BigBuf_Clear_ext(false); return ReadLF(true, verbose, sample_size, ledcontrol); } + +/** + * Do LF sampling and send samples to the USB + * + * Uses parameters in config. Only bits_per_sample = 8 is working now + * + * @param reader_field - true for reading tags, false for sniffing + * @return sampling result +**/ +int ReadLF_realtime(bool reader_field) { + // parameters from config and constants + const uint8_t bits_per_sample = config.bits_per_sample; + const int16_t trigger_threshold = config.trigger_threshold; + int32_t samples_to_skip = config.samples_to_skip; + const uint8_t decimation = config.decimation; + + const int8_t size_threshold_table[9] = {0, 64, 64, 60, 64, 60, 60, 56, 64}; + const int8_t size_threshold = size_threshold_table[bits_per_sample]; + + // DoAcquisition() start + uint8_t last_byte = 0; + uint8_t curr_byte = 0; + int return_value = PM3_SUCCESS; + + uint32_t sample_buffer_len = AT91C_USB_EP_IN_SIZE; + initSampleBuffer(&sample_buffer_len); + if (sample_buffer_len != AT91C_USB_EP_IN_SIZE) { + return PM3_EFAILED; + } + + bool trigger_hit = false; + int16_t checked = 0; + + return_value = async_usb_write_start(); + if (return_value != PM3_SUCCESS) { + return return_value; + } + + BigBuf_Clear_ext(false); + LFSetupFPGAForADC(config.divisor, reader_field); + + while (BUTTON_PRESS() == false) { + // only every 4000th times, in order to save time when collecting samples. + // interruptible only when logging not yet triggered + if (trigger_hit == false && (checked >= 4000)) { + if (data_available()) { + checked = -1; + break; + } else { + checked = 0; + } + } + ++checked; + + WDT_HIT(); + + if ((AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY)) { + LED_D_ON(); + } + + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + + // (RDV4) Test point 8 (TP8) can be used to trigger oscilloscope + LED_D_OFF(); + + // threshold either high or low values 128 = center 0. if trigger = 178 + if (trigger_hit == false) { + if ((trigger_threshold > 0) && (sample < (trigger_threshold + 128)) && (sample > (128 - trigger_threshold))) { + continue; + } + trigger_hit = true; + } + + if (samples_to_skip > 0) { + samples_to_skip--; + continue; + } + + logSample(sample, decimation, bits_per_sample, false); + + // Write to USB FIFO if byte changed + curr_byte = data.numbits >> 3; + if (curr_byte > last_byte) { + async_usb_write_pushByte(data.buffer[last_byte]); + } + last_byte = curr_byte; + + if (samples.total_saved == size_threshold) { + // Request USB transmission and change FIFO bank + if (async_usb_write_requestWrite() == false) { + return_value = PM3_EIO; + break; + } + + // Reset sample + last_byte = 0; + data.numbits = 0; + samples.counter = size_threshold; + samples.total_saved = 0; + + } else if (samples.total_saved == 1) { + // Check if there is any data from client + if (data_available_fast()) { + break; + } + } + } + } + LED_D_OFF(); + return_value = async_usb_write_stop(); + + // DoAcquisition() end + StopTicks(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + return return_value; +} /** * Initializes the FPGA for sniffer-mode (field off), and acquires the samples. * @return number of bits sampled diff --git a/armsrc/lfsampling.h b/armsrc/lfsampling.h index 11c03ca74..c96112385 100644 --- a/armsrc/lfsampling.h +++ b/armsrc/lfsampling.h @@ -51,6 +51,16 @@ void doT55x7Acquisition(size_t sample_size, bool ledcontrol); **/ uint32_t SampleLF(bool verbose, uint32_t sample_size, bool ledcontrol); +/** + * Do LF sampling and send samples to the USB + * + * Uses parameters in config. Only bits_per_sample = 8 is working now + * + * @param reader_field - true for reading tags, false for sniffing + * @return sampling result +**/ +int ReadLF_realtime(bool reader_field); + /** * Initializes the FPGA for sniff-mode (field off), and acquires the samples. * @return number of bits sampled @@ -110,7 +120,7 @@ void LFSetupFPGAForADC(int divisor, bool reader_field); * @brief setSamplingConfig * @param sc */ -void setSamplingConfig(sample_config *sc); +void setSamplingConfig(const sample_config *sc); void setDefaultSamplingConfig(void); sample_config *getSamplingConfig(void); diff --git a/armsrc/lfzx.c b/armsrc/lfzx.c index be09247ca..f95b2c2b7 100644 --- a/armsrc/lfzx.c +++ b/armsrc/lfzx.c @@ -115,7 +115,7 @@ static void zx8211_setup_read(void) { WDT_HIT(); } -static void zx_send(uint8_t *cmd, uint8_t clen) { +static void zx_send(const uint8_t *cmd, uint8_t clen) { if (clen == 0) return; @@ -153,7 +153,7 @@ static void zx_get(bool ledcontrol) { volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; (void)sample; - // Test point 8 (TP8) can be used to trigger oscilloscope + // (RDV4) Test point 8 (TP8) can be used to trigger oscilloscope if (ledcontrol) LED_D_OFF(); } diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 30558541d..7fd97ea80 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -58,21 +58,88 @@ static uint8_t dummy_answer = 0; //----------------------------------------------------------------------------- // Select, Authenticate, Read a MIFARE tag. -// read block +// key_auth_cmd is one of MIFARE_AUTH_KEYA, MIFARE_AUTH_KEYB, or MIFARE_MAGIC_GDM_AUTH_KEY +// read_cmd is one of ISO14443A_CMD_READBLOCK, MIFARE_MAGIC_GDM_READBLOCK, or MIFARE_MAGIC_GDM_READ_CFG +// block_data must be 16*count bytes large +// block_no through block_no+count-1 normally needs to be within the same sector //----------------------------------------------------------------------------- -void MifareReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *datain) { - // params - uint64_t ui64Key = 0; - ui64Key = bytes_to_num(datain, 6); +int16_t mifare_cmd_readblocks(uint8_t key_auth_cmd, uint8_t *key, uint8_t read_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data) { + + uint64_t ui64key = bytes_to_num(key, 6); - // variables - uint8_t dataoutbuf[16] = {0x00}; uint8_t uid[10] = {0x00}; - uint32_t cuid = 0, status = PM3_EOPABORTED; - + uint32_t cuid = 0; struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; + struct Crypto1State *pcs = &mpcs; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + uint32_t timeout = iso14a_get_timeout(); + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + int retval = PM3_SUCCESS; + + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); + retval = PM3_ESOFT; + goto OUT; + } + + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, NULL, NULL, NULL)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); + retval = PM3_ESOFT; + goto OUT; + }; + + // frame waiting time (FWT) in 1/fc + uint32_t fwt = 256 * 16 * (1 << 7); + iso14a_set_timeout(fwt / (8 * 16)); + + for (uint8_t i = 0; i < count; i++) { + if (mifare_classic_readblock_ex(pcs, block_no + i, block_data + (i * 16), read_cmd)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Read block error"); + retval = PM3_ESOFT; + goto OUT; + }; + } + + if (mifare_classic_halt(pcs)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); + retval = PM3_ESOFT; + goto OUT; + }; + +OUT: + crypto1_deinit(pcs); + + iso14a_set_timeout(timeout); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + BigBuf_free(); + return retval; +} + +//----------------------------------------------------------------------------- +// Select, Authenticate, Write a MIFARE tag. +// key_auth_cmd is one of MIFARE_AUTH_KEYA, MIFARE_AUTH_KEYB, or MIFARE_MAGIC_GDM_AUTH_KEY +// write_cmd is one of ISO14443A_CMD_WRITEBLOCK, MIFARE_MAGIC_GDM_WRITEBLOCK, or MIFARE_MAGIC_GDM_WRITE_CFG +// block_data must be 16*count bytes large +// block_no through block_no+count-1 normally needs to be within the same sector +//----------------------------------------------------------------------------- +int16_t mifare_cmd_writeblocks(uint8_t key_auth_cmd, uint8_t *key, uint8_t write_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data) { + + uint64_t ui64key = bytes_to_num(key, 6); + + uint8_t uid[10] = {0x00}; + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs = &mpcs; iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -83,41 +150,61 @@ void MifareReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *datain) { LED_B_OFF(); LED_C_OFF(); - while (true) { - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); - break; - }; + int retval = PM3_SUCCESS; - if (mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); - break; - }; + if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); + retval = PM3_ESOFT; + goto OUT; + }; - if (mifare_classic_readblock(pcs, cuid, blockNo, dataoutbuf)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Read block error"); - break; - }; + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, NULL, NULL, NULL)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); + retval = PM3_ESOFT; + goto OUT; + }; - if (mifare_classic_halt(pcs, cuid)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); - break; - }; - - status = PM3_SUCCESS; - break; + for (uint8_t i = 0; i < count; i++) { + int res = mifare_classic_writeblock_ex(pcs, block_no + i, block_data + (i * 16), write_cmd); + if (res == PM3_ETEAROFF) { + retval = PM3_ETEAROFF; + goto OUT; + } else if (res != PM3_SUCCESS) { + if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error"); + retval = PM3_ESOFT; + goto OUT; + } } + if (mifare_classic_halt(pcs)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); + retval = PM3_ESOFT; + goto OUT; + }; + +OUT: crypto1_deinit(pcs); - if (g_dbglevel >= 2) DbpString("READ BLOCK FINISHED"); - - LED_B_ON(); - reply_ng(CMD_HF_MIFARE_READBL, status, dataoutbuf, 16); - LED_B_OFF(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); + set_tracing(false); + BigBuf_free(); + + return retval; +} + +//----------------------------------------------------------------------------- +// Select, Authenticate, Read a MIFARE tag. +// read sector (data = 4 x 16 bytes = 64 bytes, or 16 x 16 bytes = 256 bytes) +//----------------------------------------------------------------------------- +void MifareReadSector(uint8_t sector_no, uint8_t key_type, uint8_t *key) { + uint8_t block_no = FirstBlockOfSector(sector_no); + uint8_t num_blocks = NumBlocksPerSector(sector_no); + + uint8_t outbuf[16 * 16]; + int16_t retval = mifare_cmd_readblocks(MIFARE_AUTH_KEYA + (key_type & 1), key, ISO14443A_CMD_READBLOCK, block_no, num_blocks, outbuf); + + reply_old(CMD_ACK, retval == PM3_SUCCESS, 0, 0, outbuf, 16 * num_blocks); } void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes) { @@ -214,131 +301,6 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { LEDsoff(); } -void MifareReadConfigBlockGDM(uint8_t *key) { - - int retval = PM3_SUCCESS; - - uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); - if (par == NULL) { - retval = PM3_EMALLOC; - goto OUT; - } - - uint8_t *uid = BigBuf_malloc(10); - if (uid == NULL) { - retval = PM3_EMALLOC; - goto OUT; - } - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - clear_trace(); - set_tracing(true); - - // variables - uint32_t cuid = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - uint64_t ui64key = bytes_to_num(key, 6); - uint8_t outbuf[16] = {0x00}; - - if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { - retval = PM3_ESOFT; - goto OUT; - } - - if (mifare_classic_authex_2(pcs, cuid, 0, 0, ui64key, AUTH_FIRST, NULL, NULL, true)) { - retval = PM3_ESOFT; - goto OUT; - }; - - if (mifare_classic_readblock_ex(pcs, cuid, 0, outbuf, MIFARE_MAGIC_GDM_READ_CFG)) { - retval = PM3_ESOFT; - goto OUT; - }; - - if (mifare_classic_halt(pcs, cuid)) { - retval = PM3_ESOFT; - goto OUT; - }; - -OUT: - crypto1_deinit(pcs); - - reply_ng(CMD_HF_MIFARE_G4_GDM_CONFIG, retval, outbuf, sizeof(outbuf)); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); - BigBuf_free(); -} - -//----------------------------------------------------------------------------- -// Select, Authenticate, Read a MIFARE tag. -// read sector (data = 4 x 16 bytes = 64 bytes, or 16 x 16 bytes = 256 bytes) -//----------------------------------------------------------------------------- -void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t *datain) { - // params - uint8_t sectorNo = arg0; - uint8_t keyType = arg1; - uint64_t ui64Key = 0; - ui64Key = bytes_to_num(datain, 6); - - // variables - uint8_t isOK = 0; - uint8_t dataoutbuf[16 * 16]; - uint8_t uid[10] = {0x00}; - uint32_t cuid = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - clear_trace(); - set_tracing(true); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - isOK = 1; - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - isOK = 0; - if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); - } - - - if (isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) { - isOK = 0; - if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); - } - - for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) { - if (mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf + 16 * blockNo)) { - isOK = 0; - if (g_dbglevel >= DBG_ERROR) Dbprintf("Read sector %2d block %2d error", sectorNo, blockNo); - break; - } - } - - if (mifare_classic_halt(pcs, cuid)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); - } - - if (g_dbglevel >= 2) DbpString("READ SECTOR FINISHED"); - - crypto1_deinit(pcs); - - LED_B_ON(); - reply_old(CMD_ACK, isOK, 0, 0, dataoutbuf, 16 * NumBlocksPerSector(sectorNo)); - LED_B_OFF(); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); -} - // arg0 = blockNo (start) // arg1 = Pages (number of blocks) // arg2 = useKey @@ -438,210 +400,6 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) set_tracing(false); } -//----------------------------------------------------------------------------- -// Select, Authenticate, Write a MIFARE tag. -// read block -//----------------------------------------------------------------------------- -void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { - // params - uint8_t blockNo = arg0; - uint8_t keyType = arg1; - uint64_t ui64Key = 0; - uint8_t blockdata[16] = {0x00}; - - ui64Key = bytes_to_num(datain, 6); - memcpy(blockdata, datain + 10, 16); - - // variables - uint8_t uid[10] = {0x00}; - uint32_t cuid = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - clear_trace(); - set_tracing(true); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - uint8_t retval = 0; - - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); - goto OUT; - }; - - if (mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); - goto OUT; - }; - - int res = mifare_classic_writeblock(pcs, cuid, blockNo, blockdata); - if (res == PM3_ETEAROFF) { - retval = PM3_ETEAROFF; - goto OUT; - } else if (res) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Write block error"); - retval = PM3_ESOFT; - goto OUT; - } - - if (mifare_classic_halt(pcs, cuid)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); - goto OUT; - }; - - retval = 1; - -OUT: - crypto1_deinit(pcs); - - reply_mix(CMD_ACK, retval, 0, 0, 0, 0); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); -} - -void MifareWriteBlockGDM(uint8_t blockno, uint8_t keytype, uint8_t *key, uint8_t *datain) { - - int retval = PM3_SUCCESS; - - // check args - if (datain == NULL) { - retval = PM3_EINVARG; - goto OUT; - } - - uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); - if (par == NULL) { - retval = PM3_EMALLOC; - goto OUT; - } - - uint8_t *uid = BigBuf_malloc(10); - if (uid == NULL) { - retval = PM3_EMALLOC; - goto OUT; - } - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - clear_trace(); - set_tracing(true); - - // variables - uint32_t cuid = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - uint64_t ui64key = bytes_to_num(key, 6); - - if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { - retval = PM3_ESOFT; - goto OUT; - } - - if (mifare_classic_authex_2(pcs, cuid, blockno, keytype, ui64key, AUTH_FIRST, NULL, NULL, true)) { - retval = PM3_ESOFT; - goto OUT; - }; - - int res = mifare_classic_writeblock_ex(pcs, cuid, blockno, datain, true); - if (res == PM3_ETEAROFF) { - retval = PM3_ETEAROFF; - goto OUT; - } else if (res) { - retval = PM3_ESOFT; - goto OUT; - } - - if (mifare_classic_halt(pcs, cuid)) { - retval = PM3_ESOFT; - goto OUT; - }; - -OUT: - crypto1_deinit(pcs); - - reply_ng(CMD_HF_MIFARE_G4_GDM_WRBL, retval, NULL, 0); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); - BigBuf_free(); -} - -void MifareWriteConfigBlockGDM(uint8_t *datain) { - - int retval = PM3_SUCCESS; - - // check args - if (datain == NULL) { - retval = PM3_EINVARG; - goto OUT; - } - - uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); - if (par == NULL) { - retval = PM3_EMALLOC; - goto OUT; - } - - uint8_t *uid = BigBuf_malloc(10); - if (uid == NULL) { - retval = PM3_EMALLOC; - goto OUT; - } - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - clear_trace(); - set_tracing(true); - - // variables - uint32_t cuid = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { - retval = PM3_ESOFT; - goto OUT; - } - - uint64_t key = 0; - if (mifare_classic_authex_2(pcs, cuid, 0, 0, key, AUTH_FIRST, NULL, NULL, true)) { - retval = PM3_ESOFT; - goto OUT; - }; - - int res = mifare_classic_write_cfg_block_gdm(pcs, cuid, datain); - if (res == PM3_ETEAROFF) { - retval = PM3_ETEAROFF; - goto OUT; - } else if (res) { - retval = PM3_ESOFT; - goto OUT; - } - - if (mifare_classic_halt(pcs, cuid)) { - retval = PM3_ESOFT; - goto OUT; - }; - -OUT: - crypto1_deinit(pcs); - - reply_ng(CMD_HF_MIFARE_G4_GDM_WRCFG, retval, NULL, 0); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); - BigBuf_free(); -} - - void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { // params uint8_t blockNo = arg0; @@ -688,8 +446,8 @@ void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { break; }; - if (mifare_classic_value(pcs, cuid, blockNo, blockdata, action)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Write block error"); + if (mifare_classic_value(pcs, blockNo, blockdata, action) != PM3_SUCCESS) { + if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error"); break; }; @@ -708,7 +466,7 @@ void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { break; } - if (mifare_classic_halt(pcs, cuid)) { + if (mifare_classic_halt(pcs)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); break; }; @@ -777,8 +535,8 @@ static void MifareUWriteBlockEx(uint8_t arg0, uint8_t arg1, uint8_t *datain, boo } } - if (mifare_ultra_writeblock(blockNo, blockdata)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Write block error"); + if (mifare_ultra_writeblock(blockNo, blockdata) != PM3_SUCCESS) { + if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error"); OnError(0); return; }; @@ -851,8 +609,8 @@ void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain) { } } - if (mifare_ultra_writeblock_compat(blockNo, blockdata)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Write block error"); + if (mifare_ultra_writeblock_compat(blockNo, blockdata) != PM3_SUCCESS) { + if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error"); OnError(0); return; }; @@ -896,8 +654,8 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { blockdata[1] = pwd[6]; blockdata[2] = pwd[5]; blockdata[3] = pwd[4]; - if (mifare_ultra_writeblock(44, blockdata)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Write block error"); + if (mifare_ultra_writeblock(44, blockdata) != PM3_SUCCESS) { + if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error"); OnError(44); return; }; @@ -906,8 +664,8 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { blockdata[1] = pwd[2]; blockdata[2] = pwd[1]; blockdata[3] = pwd[0]; - if (mifare_ultra_writeblock(45, blockdata)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Write block error"); + if (mifare_ultra_writeblock(45, blockdata) != PM3_SUCCESS) { + if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error"); OnError(45); return; }; @@ -916,8 +674,8 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { blockdata[1] = pwd[14]; blockdata[2] = pwd[13]; blockdata[3] = pwd[12]; - if (mifare_ultra_writeblock(46, blockdata)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Write block error"); + if (mifare_ultra_writeblock(46, blockdata) != PM3_SUCCESS) { + if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error"); OnError(46); return; }; @@ -926,8 +684,8 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { blockdata[1] = pwd[10]; blockdata[2] = pwd[9]; blockdata[3] = pwd[8]; - if (mifare_ultra_writeblock(47, blockdata)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Write block error"); + if (mifare_ultra_writeblock(47, blockdata) != PM3_SUCCESS) { + if (g_dbglevel >= DBG_INFO) Dbprintf("Write block error"); OnError(47); return; }; @@ -1145,7 +903,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, } // nested authentication - uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par_enc, NULL); + uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par_enc, NULL); // wait for the card to become ready again CHK_TIMEOUT(); @@ -1267,7 +1025,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 } // prepare next select. No need to power down the card. - if (mifare_classic_halt(pcs, cuid)) { + if (mifare_classic_halt(pcs)) { if (g_dbglevel >= DBG_INFO) Dbprintf("Nested: Halt error"); rtr--; continue; @@ -1294,14 +1052,9 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 }; // cards with fixed nonce - /* - if (nt1 == nt2) { - Dbprintf("Nested: %08x vs %08x", nt1, nt2); - break; - } - */ - - uint32_t nttmp = prng_successor(nt1, 100); //NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160 + // NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160 + + uint32_t nttmp = prng_successor(nt1, 100); for (i = 101; i < 1200; i++) { nttmp = prng_successor(nttmp, 1); if (nttmp == nt2) break; @@ -1313,12 +1066,14 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 dmin = MIN(dmin, i); dmax = MAX(dmax, i); } else { - delta_time = auth2_time - auth1_time + 32; // allow some slack for proper timing + // allow some slack for proper timing + delta_time = auth2_time - auth1_time + 32; } if (g_dbglevel >= DBG_DEBUG) Dbprintf("Nested: calibrating... ntdist=%d", i); } else { unsuccessful_tries++; - if (unsuccessful_tries > NESTED_MAX_TRIES) { // card isn't vulnerable to nested attack (random numbers are not predictable) + // card isn't vulnerable to nested attack (random numbers are not predictable) + if (unsuccessful_tries > NESTED_MAX_TRIES) { isOK = PM3_EFAILED; } } @@ -1354,7 +1109,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 LED_C_ON(); // get crypted nonces for target sector - for (i = 0; ((i < 2) && (isOK == PM3_SUCCESS)); i++) { + for (i = 0; ((i < 2) && (isOK == PM3_SUCCESS)); i++) { // look for exactly two different nonces @@ -1369,7 +1124,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 } // prepare next select. No need to power down the card. - if (mifare_classic_halt(pcs, cuid)) { + if (mifare_classic_halt(pcs)) { if (g_dbglevel >= DBG_INFO) Dbprintf("Nested: Halt error"); continue; } @@ -1388,7 +1143,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 // nested authentication auth2_time = auth1_time + delta_time; - len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, &auth2_time); + len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, &auth2_time); if (len != 4) { if (g_dbglevel >= DBG_INFO) Dbprintf("Nested: Auth2 error len=%d", len); continue; @@ -1491,7 +1246,7 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, // Main loop - get crypted nonces for target sector for (uint8_t rtr = 0; rtr < 2; rtr++) { - if (mifare_classic_halt(pcs, cuid)) { + if (mifare_classic_halt(pcs)) { continue; } @@ -1513,7 +1268,7 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, target_nt[1] = prng_successor(nt1, 320); } - len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, NULL); + len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, NULL); if (len != 4) { continue; }; @@ -1522,8 +1277,7 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, target_ks[0] = nt2 ^ target_nt[0]; // second collection - - if (mifare_classic_halt(pcs, cuid)) { + if (mifare_classic_halt(pcs)) { continue; } @@ -1539,10 +1293,11 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, continue; }; - len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, NULL); + len = mifare_sendcmd_short(pcs, AUTH_NESTED, MIFARE_AUTH_KEYA + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, NULL); if (len != 4) { continue; }; + nt3 = bytes_to_num(receivedAnswer, 4); target_ks[1] = nt3 ^ target_nt[1]; @@ -1619,7 +1374,7 @@ static uint8_t chkKey(struct chk_t *c) { // if successful auth, send HALT // if ( !res ) - // mifare_classic_halt_ex(c->pcs); + // mifare_classic_halt(c->pcs); break; } return res; @@ -1634,7 +1389,7 @@ static uint8_t chkKey_readb(struct chk_t *c, uint8_t *keyb) { return 1; uint8_t data[16] = {0x00}; - uint8_t res = mifare_classic_readblock(c->pcs, c->cuid, c->block, data); + uint8_t res = mifare_classic_readblock(c->pcs, c->block, data); // successful read if (!res) { @@ -1645,7 +1400,7 @@ static uint8_t chkKey_readb(struct chk_t *c, uint8_t *keyb) { } else { res = 3; } - mifare_classic_halt_ex(c->pcs); + mifare_classic_halt(c->pcs); } return res; } @@ -2053,8 +1808,8 @@ OUT: emlSetMem_xt(block, blockno, 1, sizeof(block)); } - MifareECardLoad(sectorcnt, 0); - MifareECardLoad(sectorcnt, 1); + MifareECardLoad(sectorcnt, MF_KEY_A); + MifareECardLoad(sectorcnt, MF_KEY_B); } } else { // partial/none keys found @@ -2230,7 +1985,7 @@ void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) { break; } - if (mifare_classic_halt(pcs, cuid)) { + if (mifare_classic_halt(pcs)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); break; } @@ -2262,15 +2017,6 @@ void MifareEMemClr(void) { emlClearMem(); } -void MifareEMemSet(uint8_t blockno, uint8_t blockcnt, uint8_t blockwidth, uint8_t *datain) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - - if (blockwidth == 0) - blockwidth = 16; // backwards compat... default bytewidth - - emlSetMem_xt(datain, blockno, blockcnt, blockwidth); // data, block num, blocks count, block byte width -} - void MifareEMemGet(uint8_t blockno, uint8_t blockcnt) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); @@ -2303,74 +2049,141 @@ int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype) { int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { - uint32_t cuid = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - // variables - uint8_t dataoutbuf[16] = {0x00}; - uint8_t dataoutbuf2[16] = {0x00}; - uint8_t uid[10] = {0x00}; - LED_A_ON(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); + // variables + bool have_uid = false; + uint8_t cascade_levels = 0; + uint32_t cuid = 0; + uint8_t uid[10] = {0x00}; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + int retval = PM3_SUCCESS; - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - retval = PM3_ESOFT; - if (g_dbglevel > DBG_ERROR) Dbprintf("Can't select card"); - goto out; - } + // increase time-out. Magic card etc are slow + uint32_t timeout = iso14a_get_timeout(); + // frame waiting time (FWT) in 1/fc + uint32_t fwt = 256 * 16 * (1 << 6); + iso14a_set_timeout(fwt / (8 * 16)); - for (uint8_t sectorNo = 0; sectorNo < sectorcnt; sectorNo++) { - uint64_t ui64Key = emlGetKey(sectorNo, keytype); - if (sectorNo == 0) { - if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keytype, ui64Key, AUTH_FIRST)) { - retval = PM3_EPARTIAL; - if (g_dbglevel > DBG_ERROR) Dbprintf("Sector[%2d]. Auth error", sectorNo); + for (uint8_t s = 0; s < sectorcnt; s++) { + + uint64_t ui64Key = emlGetKey(s, keytype); + + if (sectorcnt == 18) { + // MFC 1K EV1, skip sector 16 since its lockdown + if (s == 16) { + // unknown sector trailer, keep the keys, set only the AC + uint8_t st[16] = {0x00}; + emlGetMem(st, FirstBlockOfSector(s) + 3, 1); + memcpy(st + 6, "\x70\xF0\xF8\x69", 4); + emlSetMem_xt(st, FirstBlockOfSector(s) + 3, 1, 16); continue; } - } else { - if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keytype, ui64Key, AUTH_NESTED)) { - retval = PM3_EPARTIAL; - if (g_dbglevel > DBG_ERROR) Dbprintf("Sector[%2d]. Auth nested error", sectorNo); + + // ICEMAN: ugly hack, we don't want to trigger the partial load message + // MFC 1K EV1 sector 17 don't use key A. + // not mention we don't save signatures in our MFC dump files. + if (s == 17 && keytype == 0) { + ui64Key = 0x4B791BEA7BCC; + keytype = 1; + } + } + + // use fast select + if (have_uid == false) { // 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) == 0) { + continue; + } + + switch (card_info.uidlen) { + case 4 : + cascade_levels = 1; + break; + case 7 : + cascade_levels = 2; + break; + case 10: + cascade_levels = 3; + break; + default: + break; + } + have_uid = true; + } else { // no need for anticollision. We can directly select the card + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { continue; } } - for (uint8_t blockNo = 0; blockNo < NumBlocksPerSector(sectorNo); blockNo++) { - if (mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf)) { - retval = PM3_EPARTIAL; + // Auth + if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(s), keytype, ui64Key, AUTH_FIRST)) { + retval = PM3_EPARTIAL; + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Sector %2d - Auth error", s); + } + continue; + } - if (g_dbglevel > DBG_ERROR) Dbprintf("Error reading sector %2d block %2d", sectorNo, blockNo); - continue; + +#define MAX_RETRIES 2 + + uint8_t data[16] = {0x00}; + for (uint8_t b = 0; b < NumBlocksPerSector(s); b++) { + + memset(data, 0x00, sizeof(data)); + uint8_t tb = FirstBlockOfSector(s) + b; + uint8_t r = 0; + for (; r < MAX_RETRIES; r++) { + + int res = mifare_classic_readblock(pcs, tb, data); + if (res == 1) { + retval |= PM3_EPARTIAL; + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Error No rights reading sector %2d block %2d", s, b); + } + break; + } + // retry if wrong len. + if (res != 0) { + continue; + } + + // No need to copy empty + if (memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) == 0) { + break; + } + + if (IsSectorTrailer(b)) { + // sector trailer, keep the keys, set only the AC + uint8_t st[16] = {0x00}; + emlGetMem(st, tb, 1); + memcpy(st + 6, data + 6, 4); + emlSetMem_xt(st, tb, 1, 16); + } else { + emlSetMem_xt(data, tb, 1, 16); + } + break; } - if (memcmp(dataoutbuf, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16) == 0) { - continue; - } - - if (blockNo < NumBlocksPerSector(sectorNo) - 1) { - emlSetMem(dataoutbuf, FirstBlockOfSector(sectorNo) + blockNo, 1); - } else { // sector trailer, keep the keys, set only the AC - emlGetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1); - memcpy(dataoutbuf2 + 6, dataoutbuf + 6, 4); - emlSetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1); + // if we failed all retries, notify client + if (r == MAX_RETRIES) { + retval |= PM3_EPARTIAL; } } } - int res = mifare_classic_halt(pcs, cuid); + int res = mifare_classic_halt(pcs); (void)res; - if (g_dbglevel >= DBG_INFO) DbpString("Emulator fill sectors finished"); - -out: + iso14a_set_timeout(timeout); crypto1_deinit(pcs); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); @@ -2395,6 +2208,9 @@ out: static uint8_t wupC1[] = { MIFARE_MAGICWUPC1 }; static uint8_t wupC2[] = { MIFARE_MAGICWUPC2 }; static uint8_t wipeC[] = { MIFARE_MAGICWIPEC }; +// GDM alt magic wakeup +static uint8_t wupGDM1[] = { MIFARE_MAGIC_GDM_WUPC1 }; +//static uint8_t wupGDM2[] = { MIFARE_MAGIC_GDM_WUPC2 }; void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { @@ -2430,10 +2246,10 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); errormsg = MAGIC_UID; - mifare_classic_halt_ex(NULL); + mifare_classic_halt(NULL); break; } - mifare_classic_halt_ex(NULL); + mifare_classic_halt(NULL); } // wipe tag, fill it with zeros @@ -2459,7 +2275,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { } iso14a_set_timeout(old_timeout); - mifare_classic_halt_ex(NULL); + mifare_classic_halt(NULL); } // write block @@ -2498,7 +2314,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { } if (workFlags & MAGIC_HALT) - mifare_classic_halt_ex(NULL); + mifare_classic_halt(NULL); isOK = true; break; @@ -2539,6 +2355,12 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { set_tracing(true); } + // increase time-out. Magic card etc are slow + uint32_t timeout = iso14a_get_timeout(); + // frame waiting time (FWT) in 1/fc + uint32_t fwt = 256 * 16 * (1 << 7); + iso14a_set_timeout(fwt / (8 * 16)); + //loop doesn't loop just breaks out if error or done while (true) { if (workFlags & MAGIC_WUPC) { @@ -2560,7 +2382,7 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { } // read block - if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 18)) { + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != MAX_MIFARE_FRAME_SIZE)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("read block send command error"); errormsg = 0; break; @@ -2570,7 +2392,7 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { // send HALT if (workFlags & MAGIC_HALT) - mifare_classic_halt_ex(NULL); + mifare_classic_halt(NULL); isOK = true; break; @@ -2588,88 +2410,124 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { if (workFlags & MAGIC_OFF) OnSuccessMagic(); + + iso14a_set_timeout(timeout); } -void MifareCIdent(bool is_mfc) { +static void mf_reset_card(void) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(40); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); +} + +void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) { // variables - uint8_t isGen = 0; uint8_t rec[1] = {0x00}; uint8_t recpar[1] = {0x00}; uint8_t rats[4] = {ISO14443A_CMD_RATS, 0x80, 0x31, 0x73}; uint8_t rdblf0[4] = {ISO14443A_CMD_READBLOCK, 0xF0, 0x8D, 0x5f}; uint8_t rdbl00[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa8}; - uint8_t gen4gmd[4] = {MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92}; + uint8_t gen4gdmAuth[4] = {MIFARE_MAGIC_GDM_AUTH_KEY, 0x00, 0x6C, 0x92}; + uint8_t gen4gdmGetConf[4] = {MIFARE_MAGIC_GDM_READ_CFG, 0x00, 0x39, 0xF7}; uint8_t gen4GetConf[8] = {GEN_4GTU_CMD, 0x00, 0x00, 0x00, 0x00, GEN_4GTU_GETCNF, 0, 0}; uint8_t superGen1[9] = {0x0A, 0x00, 0x00, 0xA6, 0xB0, 0x00, 0x10, 0x14, 0x1D}; - uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); - uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); - uint8_t *uid = BigBuf_malloc(10); + bool isGen2 = false; + bool isGen1AGdm = false; - memset(par, 0x00, MAX_PARITY_SIZE); - memset(buf, 0x00, PM3_CMD_DATA_SIZE); - memset(uid, 0x00, 10); + uint8_t *par = BigBuf_calloc(MAX_PARITY_SIZE); + uint8_t *buf = BigBuf_calloc(PM3_CMD_DATA_SIZE); + uint8_t *uid = BigBuf_calloc(10); + uint8_t *data = BigBuf_calloc(16); uint32_t cuid = 0; - uint8_t data[1] = {0x00}; + size_t data_off = 0; iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); // Generation 1 test ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); if (ReaderReceive(rec, recpar) && (rec[0] == 0x0a)) { + uint8_t isGen = MAGIC_GEN_1A; ReaderTransmit(wupC2, sizeof(wupC2), NULL); if (!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) { isGen = MAGIC_GEN_1B; - goto OUT; }; - isGen = MAGIC_GEN_1A; - goto OUT; + data[data_off++] = isGen; + + // check for GDM config + ReaderTransmit(gen4gdmGetConf, sizeof(gen4gdmGetConf), NULL); + int res = ReaderReceive(buf, par); + if (res > 1) { + isGen1AGdm = true; + } } - // reset card - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(40); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + mf_reset_card(); int res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res == 2) { + if (res) { // Check for Magic Gen4 GTU with default password: // Get config should return 30 or 32 bytes AddCrc14A(gen4GetConf, sizeof(gen4GetConf) - 2); ReaderTransmit(gen4GetConf, sizeof(gen4GetConf), NULL); res = ReaderReceive(buf, par); if (res == 32 || res == 34) { - isGen = MAGIC_GEN_4GTU; - goto OUT; + data[data_off++] = MAGIC_GEN_4GTU; } } // reset card - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(40); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + mf_reset_card(); - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res == 2) { + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, false); + if (res) { if (cuid == 0xAA55C396) { - isGen = MAGIC_GEN_UNFUSED; - goto OUT; + data[data_off++] = MAGIC_GEN_UNFUSED; } ReaderTransmit(rats, sizeof(rats), NULL); res = ReaderReceive(buf, par); + if (res) { + if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10", 9) == 0) { + // test for some MFC gen2 + isGen2 = true; + data[data_off++] = MAGIC_GEN_2; + } else if (memcmp(buf, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) { + // test for some MFC 7b gen2 + isGen2 = true; + data[data_off++] = MAGIC_GEN_2; + } else if (memcmp(buf, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) { + // test for Ultralight magic gen2 + isGen2 = true; + data[data_off++] = MAGIC_GEN_2; + } else if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x41\xDF", 18) == 0) { + // test for Ultralight EV1 magic gen2 + isGen2 = true; + data[data_off++] = MAGIC_GEN_2; + } else if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x16\xD7", 18) == 0) { + // test for some other Ultralight EV1 magic gen2 + isGen2 = true; + data[data_off++] = MAGIC_GEN_2; + } else if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x18\x4D", 18) == 0) { + // test for some other Ultralight magic gen2 + isGen2 = true; + data[data_off++] = MAGIC_GEN_2; + } else if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xA5\x00\x04\x04\x02\x01\x00\x0F\x03\x79\x0C", 18) == 0) { + // test for NTAG213 magic gen2 + isGen2 = true; + data[data_off++] = MAGIC_GEN_2; + } + // test for super card ReaderTransmit(superGen1, sizeof(superGen1), NULL); res = ReaderReceive(buf, par); if (res == 22) { - isGen = MAGIC_SUPER_GEN1; + uint8_t isGen = MAGIC_SUPER_GEN1; // check for super card gen2 // not available after RATS, reset card before executing - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(40); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + mf_reset_card(); iso14443a_select_card(uid, NULL, &cuid, true, 0, true); ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); @@ -2678,93 +2536,99 @@ void MifareCIdent(bool is_mfc) { isGen = MAGIC_SUPER_GEN2; } - goto OUT; - } - // test for some MFC gen2 - if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10\xF0\x05", 11) == 0) { - isGen = MAGIC_GEN_2; - goto OUT; - } - // test for some MFC 7b gen2 - if (memcmp(buf, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) { - isGen = MAGIC_GEN_2; - goto OUT; - } - // test for Ultralight magic gen2 - if (memcmp(buf, "\x0A\x78\x00\x81\x02\xDB\xA0\xC1\x19\x40\x2A\xB5", 12) == 0) { - isGen = MAGIC_GEN_2; - goto OUT; - } - // test for Ultralight EV1 magic gen2 - if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x41\xDF", 18) == 0) { - isGen = MAGIC_GEN_2; - goto OUT; - } - // test for some other Ultralight EV1 magic gen2 - if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xC3\x00\x04\x03\x01\x01\x00\x0B\x03\x16\xD7", 18) == 0) { - isGen = MAGIC_GEN_2; - goto OUT; - } - // test for some other Ultralight magic gen2 - if (memcmp(buf, "\x85\x00\x00\xA0\x0A\x00\x0A\xB0\x00\x00\x00\x00\x00\x00\x00\x00\x18\x4D", 18) == 0) { - isGen = MAGIC_GEN_2; - goto OUT; - } - // test for NTAG213 magic gen2 - if (memcmp(buf, "\x85\x00\x00\xA0\x00\x00\x0A\xA5\x00\x04\x04\x02\x01\x00\x0F\x03\x79\x0C", 18) == 0) { - isGen = MAGIC_GEN_2; - goto OUT; + data[data_off++] = isGen; } } if (is_mfc == false) { // magic ntag test - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(40); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + mf_reset_card(); + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); if (res == 2) { ReaderTransmit(rdblf0, sizeof(rdblf0), NULL); res = ReaderReceive(buf, par); if (res == 18) { - isGen = MAGIC_NTAG21X; + data[data_off++] = MAGIC_NTAG21X; } } } else { - // magic MFC Gen3 test 1 - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(40); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res == 2) { - ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); - res = ReaderReceive(buf, par); - if (res == 18) { - isGen = MAGIC_GEN_3; + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + // CUID (with default sector 0 B key) test + // regular cards will NAK the WRITEBLOCK(0) command, while DirectWrite will ACK it + // if we do get an ACK, we immediately abort to ensure nothing is ever actually written + // only perform test if we haven't already identified Gen2. No need test if we have a positive identification already + if (!isGen2) { + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res) { + + uint64_t tmpkey = bytes_to_num(key, 6); + if (mifare_classic_authex(pcs, cuid, 0, keytype, tmpkey, AUTH_FIRST, NULL, NULL) == 0) { + + if ((mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, 0, buf, par, NULL) == 1) && (buf[0] == 0x0A)) { + data[data_off++] = MAGIC_GEN_2; + // turn off immediately to ensure nothing ever accidentally writes to the block + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + } + } + crypto1_deinit(pcs); } } - // magic MFC Gen4 GDM test - if (isGen != MAGIC_GEN_3) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(40); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); - if (res == 2) { - ReaderTransmit(gen4gmd, sizeof(gen4gmd), NULL); - res = ReaderReceive(buf, par); - if (res == 4) { - isGen = MAGIC_GEN_4GDM; - } + // magic MFC Gen3 test 1 + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res) { + ReaderTransmit(rdbl00, sizeof(rdbl00), NULL); + res = ReaderReceive(buf, par); + if (res == 18) { + data[data_off++] = MAGIC_GEN_3; } } + + // magic MFC Gen4 GDM magic auth test + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res) { + ReaderTransmit(gen4gdmAuth, sizeof(gen4gdmAuth), NULL); + res = ReaderReceive(buf, par); + if (res == 4) { + data[data_off++] = MAGIC_GDM_AUTH; + } + } + + // QL88 test + mf_reset_card(); + + res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); + if (res) { + if (mifare_classic_authex(pcs, cuid, 68, MF_KEY_B, 0x707B11FC1481, AUTH_FIRST, NULL, NULL) == 0) { + data[data_off++] = MAGIC_QL88; + } + crypto1_deinit(pcs); + } } }; -OUT: + if (isGen1AGdm == true) { + data[data_off++] = MAGIC_GDM_WUP_40; + } - data[0] = isGen; - reply_ng(CMD_HF_MIFARE_CIDENT, PM3_SUCCESS, data, sizeof(data)); + // GEM alt magic wakeup (20) + ReaderTransmitBitsPar(wupGDM1, 7, NULL, NULL); + if (ReaderReceive(rec, recpar) && (rec[0] == 0x0a)) { + data[data_off++] = MAGIC_GDM_WUP_20; + } + + reply_ng(CMD_HF_MIFARE_CIDENT, PM3_SUCCESS, data, data_off); // turns off OnSuccessMagic(); BigBuf_free(); @@ -2791,8 +2655,7 @@ void MifareHasStaticNonce(void) { uint8_t counter = 0; for (uint8_t i = 0; i < 3; i++) { - iso14a_card_select_t card_info; - if (!iso14443a_select_card(uid, &card_info, NULL, true, 0, true)) { + if (!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { retval = PM3_ESOFT; goto OUT; } @@ -2822,7 +2685,7 @@ void MifareHasStaticNonce(void) { } if (counter) { - Dbprintf("%u static nonce %08x", data[0], nt); + Dbprintf("Static nonce......... " _YELLOW_("%08x"), nt); data[0] = NONCE_STATIC; } else { data[0] = NONCE_NORMAL; @@ -2836,11 +2699,82 @@ OUT: crypto1_deinit(pcs); } +// FUDAN card w static encrypted nonces +// 2B F9 1C 1B D5 08 48 48 03 A4 B1 B1 75 FF 2D 90 +// ^^ ^^ + +void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *key) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + clear_trace(); + set_tracing(true); + + int retval = PM3_SUCCESS; + uint8_t *uid = BigBuf_calloc(10); + + uint64_t ui64key = bytes_to_num(key, 6); + uint8_t data[1] = { NONCE_FAIL }; + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + uint32_t cuid = 0; + if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + retval = PM3_ESOFT; + goto OUT; + } + + uint8_t key_auth_cmd = MIFARE_AUTH_KEYA + (key_type & 1); + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_FIRST, NULL, NULL, NULL)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); + retval = PM3_ESOFT; + goto OUT; + }; + + uint32_t nt = 0; + uint8_t enc_counter = 0; + uint32_t ntenc = 0; + uint32_t oldntenc = 0; + for (uint8_t i = 0; i < 3; i++) { + if (mifare_classic_authex_cmd(pcs, cuid, block_no, key_auth_cmd, ui64key, AUTH_NESTED, &nt, &ntenc, NULL)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("Auth error"); + retval = PM3_ESOFT; + goto OUT; + }; + + if (g_dbglevel >= DBG_INFO) + Dbprintf("nt: %x, nt encoded: %x", nt, ntenc); + + if (oldntenc == 0) + oldntenc = ntenc; + else if (ntenc == oldntenc) + enc_counter++; + } + + if (enc_counter) { + data[0] = NONCE_STATIC_ENC; + } else { + data[0] = NONCE_NORMAL; + } + +OUT: + crypto1_deinit(pcs); + + reply_ng(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, retval, data, sizeof(data)); + // turns off + OnSuccessMagic(); + BigBuf_free(); +} + void OnSuccessMagic(void) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); set_tracing(false); } + void OnErrorMagic(uint8_t reason) { // ACK, ISOK, reason,0,0,0 reply_mix(CMD_ACK, 0, reason, 0, 0, 0); @@ -2849,8 +2783,8 @@ void OnErrorMagic(uint8_t reason) { int DoGen3Cmd(uint8_t *cmd, uint8_t cmd_len) { int retval = PM3_SUCCESS; - uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); - uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); + uint8_t *par = BigBuf_calloc(MAX_PARITY_SIZE); + uint8_t *buf = BigBuf_calloc(PM3_CMD_DATA_SIZE); LED_B_ON(); uint32_t save_iso14a_timeout = iso14a_get_timeout(); @@ -2867,17 +2801,17 @@ int DoGen3Cmd(uint8_t *cmd, uint8_t cmd_len) { } iso14a_set_timeout(save_iso14a_timeout); LED_B_OFF(); - return retval; } void MifareGen3UID(uint8_t uidlen, uint8_t *uid) { int retval = PM3_SUCCESS; uint8_t uid_cmd[5] = { 0x90, 0xfb, 0xcc, 0xcc, 0x07 }; - uint8_t *old_uid = BigBuf_malloc(10); - uint8_t *cmd = BigBuf_malloc(sizeof(uid_cmd) + uidlen + 2); - iso14a_card_select_t *card_info = (iso14a_card_select_t *) BigBuf_malloc(sizeof(iso14a_card_select_t)); + uint8_t *old_uid = BigBuf_calloc(10); + uint8_t *cmd = BigBuf_calloc(sizeof(uid_cmd) + uidlen + 2); + iso14a_card_select_t *card_info = (iso14a_card_select_t *) BigBuf_calloc(sizeof(iso14a_card_select_t)); + LEDsoff(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); @@ -2906,18 +2840,18 @@ OUT: } void MifareGen3Blk(uint8_t block_len, uint8_t *block) { -#define MIFARE_BLOCK_SIZE (MAX_MIFARE_FRAME_SIZE - 2) + int retval = PM3_SUCCESS; uint8_t block_cmd[5] = { 0x90, 0xf0, 0xcc, 0xcc, 0x10 }; - uint8_t *uid = BigBuf_malloc(10); - uint8_t *cmd = BigBuf_malloc(sizeof(block_cmd) + MAX_MIFARE_FRAME_SIZE); - iso14a_card_select_t *card_info = (iso14a_card_select_t *) BigBuf_malloc(sizeof(iso14a_card_select_t)); + uint8_t *cmd = BigBuf_calloc(sizeof(block_cmd) + MAX_MIFARE_FRAME_SIZE); + iso14a_card_select_t *card_info = (iso14a_card_select_t *) BigBuf_calloc(sizeof(iso14a_card_select_t)); + LEDsoff(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); - if (iso14443a_select_card(uid, card_info, NULL, true, 0, true) == false) { + if (iso14443a_select_card(NULL, card_info, NULL, true, 0, true) == false) { retval = PM3_ESOFT; goto OUT; } @@ -2952,7 +2886,7 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { AddCrc14A(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE); if (doReselect) { - if (!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { + if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { retval = PM3_ESOFT; goto OUT; } @@ -2969,15 +2903,16 @@ OUT: } void MifareGen3Freez(void) { + + LEDsoff(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); int retval = PM3_SUCCESS; uint8_t freeze_cmd[7] = { 0x90, 0xfd, 0x11, 0x11, 0x00, 0xe7, 0x91 }; - uint8_t *uid = BigBuf_malloc(10); - if (iso14443a_select_card(uid, NULL, NULL, true, 0, true) == false) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == false) { retval = PM3_ESOFT; goto OUT; } @@ -2993,7 +2928,7 @@ OUT: void MifareG4ReadBlk(uint8_t blockno, uint8_t *pwd, uint8_t workFlags) { bool setup = ((workFlags & MAGIC_INIT) == MAGIC_INIT) ; - bool done = ((workFlags & MAGIC_OFF) == MAGIC_OFF) ; + bool done = ((workFlags & MAGIC_OFF) == MAGIC_OFF) ; int res = 0; int retval = PM3_SUCCESS; @@ -3011,16 +2946,12 @@ void MifareG4ReadBlk(uint8_t blockno, uint8_t *pwd, uint8_t workFlags) { } if (setup) { - uint8_t *uid = BigBuf_malloc(10); - if (uid == NULL) { - retval = PM3_EMALLOC; - goto OUT; - } + LEDsoff(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); - if (iso14443a_select_card(uid, NULL, NULL, true, 0, true) == false) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == false) { retval = PM3_ESOFT; goto OUT; } @@ -3049,21 +2980,31 @@ void MifareG4ReadBlk(uint8_t blockno, uint8_t *pwd, uint8_t workFlags) { retval = PM3_ESOFT; } - if (done || retval != 0) iso14a_set_timeout(save_iso14a_timeout); + if (done || retval != 0) { + iso14a_set_timeout(save_iso14a_timeout); + } + LED_B_OFF(); OUT: reply_ng(CMD_HF_MIFARE_G4_RDBL, retval, buf, res); + // turns off - if (done || retval != 0) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + if (done || retval != 0) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + } + LEDsoff(); - if (done || retval != 0) set_tracing(false); + if (done || retval != 0) { + set_tracing(false); + } + BigBuf_free(); } void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t workFlags) { bool setup = ((workFlags & MAGIC_INIT) == MAGIC_INIT) ; - bool done = ((workFlags & MAGIC_OFF) == MAGIC_OFF) ; + bool done = ((workFlags & MAGIC_OFF) == MAGIC_OFF) ; int res = 0; int retval = PM3_SUCCESS; @@ -3087,16 +3028,12 @@ void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t work } if (setup) { - uint8_t *uid = BigBuf_malloc(10); - if (uid == NULL) { - retval = PM3_EMALLOC; - goto OUT; - } + LEDsoff(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); - if (iso14443a_select_card(uid, NULL, NULL, true, 0, true) == false) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == false) { retval = PM3_ESOFT; goto OUT; } @@ -3128,15 +3065,23 @@ void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t work retval = PM3_ESOFT; } - if (done || retval != 0) iso14a_set_timeout(save_iso14a_timeout); + if (done || retval != 0) { + iso14a_set_timeout(save_iso14a_timeout); + } LED_B_OFF(); OUT: reply_ng(CMD_HF_MIFARE_G4_WRBL, retval, buf, res); // turns off - if (done || retval != 0) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + if (done || retval != 0) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + } + LEDsoff(); - if (done || retval != 0) set_tracing(false); + if (done || retval != 0) { + set_tracing(false); + } + BigBuf_free(); } @@ -3147,24 +3092,25 @@ void MifareSetMod(uint8_t *datain) { // variables uint16_t isOK = PM3_EUNDEF; - uint8_t uid[10] = {0}; + uint8_t *uid = BigBuf_calloc(10); + uint32_t cuid = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs = &mpcs; - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0}; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0}; + uint8_t *buf = BigBuf_calloc(MAX_MIFARE_FRAME_SIZE); + uint8_t *par = BigBuf_calloc(MAX_MIFARE_PARITY_SIZE); + + LEDsoff(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); while (true) { - if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (iso14443a_select_card(uid, NULL, &cuid, true, 0, true) == false) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); break; } @@ -3175,12 +3121,12 @@ void MifareSetMod(uint8_t *datain) { } int respLen; - if (((respLen = mifare_sendcmd_short(pcs, CRYPT_ALL, 0x43, mod, receivedAnswer, receivedAnswerPar, NULL)) != 1) || (receivedAnswer[0] != 0x0a)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("SetMod error; response[0]: %hhX, len: %d", receivedAnswer[0], respLen); + if (((respLen = mifare_sendcmd_short(pcs, CRYPT_ALL, MIFARE_EV1_SETMOD, mod, buf, par, NULL)) != 1) || (buf[0] != 0x0a)) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("SetMod error; response[0]: %hhX, len: %d", buf[0], respLen); break; } - if (mifare_classic_halt(pcs, cuid)) { + if (mifare_classic_halt(pcs)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Halt error"); break; } @@ -3197,6 +3143,7 @@ void MifareSetMod(uint8_t *datain) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); + BigBuf_free(); } // @@ -3204,21 +3151,20 @@ void MifareSetMod(uint8_t *datain) { // void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain) { uint8_t dataout[12] = {0x00}; - uint8_t uid[10] = {0x00}; uint32_t cuid = 0; iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); - int len = iso14443a_select_card(uid, NULL, &cuid, true, 0, false); + int len = iso14443a_select_card(NULL, NULL, &cuid, true, 0, false); if (!len) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); OnError(1); return; }; - if (mifare_desfire_des_auth1(cuid, dataout)) { + if (mifare_desfire_des_auth1(cuid, dataout) != PM3_SUCCESS) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication part1: Fail."); OnError(4); return; @@ -3238,7 +3184,7 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain) { isOK = mifare_desfire_des_auth2(cuid, key, dataout); - if (isOK) { + if (isOK != PM3_SUCCESS) { if (g_dbglevel >= DBG_EXTENDED) Dbprintf("Authentication part2: Failed"); OnError(4); return; @@ -3260,8 +3206,10 @@ void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *data_test if (g_dbglevel >= DBG_DEBUG) DbpString("Preparing OTP tear-off"); - if (tearoff_time > 43000) + if (tearoff_time > 43000) { tearoff_time = 43000; + } + g_tearoff_delay_us = tearoff_time; g_tearoff_enabled = true; @@ -3280,7 +3228,7 @@ void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *data_test AddCrc14A(cmd, sizeof(cmd) - 2); // anticollision / select card - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == false) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); OnError(1); reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_EFAILED, NULL, 0); @@ -3297,8 +3245,9 @@ void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *data_test // Tear-off attack against MFU counter void MifareU_Counter_Tearoff(uint8_t counter, uint32_t tearoff_time, uint8_t *datain) { - if (tearoff_time > 43000) + if (tearoff_time > 43000) { tearoff_time = 43000; + } LEDsoff(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -3319,7 +3268,7 @@ void MifareU_Counter_Tearoff(uint8_t counter, uint32_t tearoff_time, uint8_t *da AddCrc14A(cmd, sizeof(cmd) - 2); // anticollision / select card - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == false) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); OnError(1); switch_off(); diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index a6623c006..aefc4e760 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -19,19 +19,18 @@ #include "common.h" -void MifareReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *datain); +int16_t mifare_cmd_readblocks(uint8_t key_auth_cmd, uint8_t *key, uint8_t read_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data); +int16_t mifare_cmd_writeblocks(uint8_t key_auth_cmd, uint8_t *key, uint8_t write_cmd, uint8_t block_no, uint8_t count, uint8_t *block_data); +void MifareReadSector(uint8_t sector_no, uint8_t key_type, uint8_t *key); +void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes); void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain); -void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t *datain); -void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); -void MifareValue(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain); - void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); -void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, bool calibrate, uint8_t *key); +void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, bool calibrate, uint8_t *key); void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key); void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain); @@ -41,7 +40,6 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da void MifareChkKeys_file(uint8_t *fn); void MifareEMemClr(void); -void MifareEMemSet(uint8_t blockno, uint8_t blockcnt, uint8_t blockwidth, uint8_t *datain); void MifareEMemGet(uint8_t blockno, uint8_t blockcnt); int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype); int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype); @@ -49,8 +47,9 @@ int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype); // MFC GEN1a /1b void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain); // Work with "magic Chinese" card void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain); -void MifareCIdent(bool is_mfc); // is "magic chinese" card? +void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key); // is "magic chinese" card? void MifareHasStaticNonce(void); // Has the tag a static nonce? +void MifareHasStaticEncryptedNonce(uint8_t block_no, uint8_t key_type, uint8_t *key); // Has the tag a static encrypted nonce? // MFC GEN3 int DoGen3Cmd(uint8_t *cmd, uint8_t cmd_len); @@ -58,11 +57,6 @@ void MifareGen3UID(uint8_t uidlen, uint8_t *uid); // Gen 3 magic card set UID wi void MifareGen3Blk(uint8_t block_len, uint8_t *block); // Gen 3 magic card overwrite manufacturer block void MifareGen3Freez(void); // Gen 3 magic card lock further UID changes -// MFC GEN4 GDM -void MifareReadConfigBlockGDM(uint8_t *key); -void MifareWriteConfigBlockGDM(uint8_t *datain); -void MifareWriteBlockGDM(uint8_t blockno, uint8_t keytype, uint8_t *key, uint8_t *datain); - // MFC GEN4 GTU void MifareG4ReadBlk(uint8_t blockno, uint8_t *pwd, uint8_t workFlags); void MifareG4WriteBlk(uint8_t blockno, uint8_t *pwd, uint8_t *data, uint8_t workFlags); diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index c2f81df21..eddb59dc0 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -46,6 +46,15 @@ #include "dbprint.h" #include "ticks.h" +static bool IsKeyBReadable(uint8_t blockNo) { + uint8_t sector_trailer[16]; + emlGetMem(sector_trailer, SectorTrailer(blockNo), 1); + uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) + | ((sector_trailer[8] >> 2) & 0x02) + | ((sector_trailer[8] >> 7) & 0x01); + return (AC == 0x00 || AC == 0x01 || AC == 0x02); +} + static bool IsTrailerAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { uint8_t sector_trailer[16]; emlGetMem(sector_trailer, blockNo, 1); @@ -514,7 +523,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 uint8_t rAUTH_NT_keystream[4]; uint32_t nonce = 0; - tUart14a *uart = GetUart14a(); + const tUart14a *uart = GetUart14a(); // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); @@ -872,8 +881,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 break; } */ - - if (MifareBlockToSector(receivedCmd_dec[1]) != cardAUTHSC) { + blockNo = receivedCmd_dec[1]; + if (MifareBlockToSector(blockNo) != cardAUTHSC) { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); FpgaDisableTracing(); @@ -881,6 +890,18 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 Dbprintf("[MFEMUL_WORK] Reader tried to operate (0x%02x) on block (0x%02x) not authenticated for (0x%02x), nacking", receivedCmd_dec[0], receivedCmd_dec[1], cardAUTHSC); break; } + + // Compliance of MIFARE Classic EV1 1K Datasheet footnote of Table 8 + // If access bits show that key B is Readable, any subsequent memory access will be refused. + + if (cardAUTHKEY == AUTHKEYB && IsKeyBReadable(blockNo)) { + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + FpgaDisableTracing(); + + if (g_dbglevel >= DBG_ERROR) + Dbprintf("[MFEMUL_WORK] Access denied: Reader tried to access memory on authentication with key B while key B is readable in sector (0x%02x)", cardAUTHSC); + break; + } } // case MFEMUL_WORK => CMD READ block @@ -1251,7 +1272,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 memcpy(receivedCmd_dec, response, 16); // don't change anything } } - emlSetMem(receivedCmd_dec, cardWRBL, 1); + emlSetMem_xt(receivedCmd_dec, cardWRBL, 1, 16); EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); // always ACK? FpgaDisableTracing(); diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 472c2616f..3258007e3 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -116,7 +116,9 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t uint16_t len = ReaderReceive(answer, par); - if (answer_parity) *answer_parity = par[0]; + if (answer_parity) { + *answer_parity = par[0]; + } if (pcs && (crypted == CRYPT_ALL)) { if (len == 1) { @@ -127,8 +129,9 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 3)) << 3; answer[0] = res; } else { - for (pos = 0; pos < len; pos++) + for (pos = 0; pos < len; pos++) { answer[pos] = crypto1_byte(pcs, 0x00, 0) ^ answer[pos]; + } } } return len; @@ -139,9 +142,9 @@ int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, return mifare_classic_authex(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL, NULL); } int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing) { - return mifare_classic_authex_2(pcs, uid, blockNo, keyType, ui64Key, isNested, ntptr, timing, false); + return mifare_classic_authex_cmd(pcs, uid, blockNo, (keyType & 1) ? MIFARE_AUTH_KEYB : MIFARE_AUTH_KEYA, ui64Key, isNested, ntptr, NULL, timing); } -int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing, bool is_gdm) { +int mifare_classic_authex_cmd(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t cmd, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *ntencptr, uint32_t *timing) { // "random" reader nonce: uint8_t nr[4]; @@ -150,13 +153,14 @@ int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t bloc uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - // Transmit MIFARE_CLASSIC_AUTH 0x60, 0x61 or GDM 0x80 - uint8_t cmdbyte = (is_gdm) ? MIFARE_MAGIC_GDM_AUTH_KEY : MIFARE_AUTH_KEYA + (keyType & 0x01); - int len = mifare_sendcmd_short(pcs, isNested, cmdbyte, blockNo, receivedAnswer, receivedAnswerPar, timing); + // Transmit MIFARE_CLASSIC_AUTH, 0x60 for key A, 0x61 for key B, or 0x80 for GDM backdoor + int len = mifare_sendcmd_short(pcs, isNested, cmd, blockNo, receivedAnswer, receivedAnswerPar, timing); if (len != 4) return 1; // Save the tag nonce (nt) uint32_t nt = bytes_to_num(receivedAnswer, 4); + if (ntencptr) + *ntencptr = nt; // ----------------------------- crypto1 create if (isNested) @@ -207,8 +211,8 @@ int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t bloc uint32_t save_timeout = iso14a_get_timeout(); // set timeout for authentication response - if (save_timeout > 103) - iso14a_set_timeout(103); + if (save_timeout > 106) + iso14a_set_timeout(106); // Receive 4 byte tag answer len = ReaderReceive(receivedAnswer, receivedAnswerPar); @@ -229,21 +233,25 @@ int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t bloc return 0; } -int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { - return mifare_classic_readblock_ex(pcs, uid, blockNo, blockData, ISO14443A_CMD_READBLOCK); +int mifare_classic_readblock(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData) { + return mifare_classic_readblock_ex(pcs, blockNo, blockData, ISO14443A_CMD_READBLOCK); } -int mifare_classic_readblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t iso_byte) { +int mifare_classic_readblock_ex(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData, uint8_t iso_byte) { uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; uint16_t len = mifare_sendcmd_short(pcs, 1, iso_byte, blockNo, receivedAnswer, receivedAnswerPar, NULL); if (len == 1) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error %02x", receivedAnswer[0]); + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Block " _YELLOW_("%3d") " Cmd 0x%02x Cmd Error %02x", blockNo, iso_byte, receivedAnswer[0]); + } return 1; } if (len != 18) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("wrong response len %d (expected 18)", len); + if (g_dbglevel >= DBG_ERROR) { + Dbprintf("Block " _YELLOW_("%3d") " Cmd 0x%02x Wrong response len, expected 18 got " _RED_("%d"), blockNo, iso_byte, len); + } return 2; } @@ -415,41 +423,41 @@ int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData) { return res; } -int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { - return mifare_classic_writeblock_ex(pcs, uid, blockNo, blockData, false); +int mifare_classic_writeblock(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData) { + return mifare_classic_writeblock_ex(pcs, blockNo, blockData, ISO14443A_CMD_WRITEBLOCK); } -int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, bool is_gdm) { +int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData, uint8_t cmd) { // variables uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - // command MIFARE_MAGIC_GDM_WRITEBLOCK - uint16_t len; - if (is_gdm) { - len = mifare_sendcmd_short(pcs, 1, MIFARE_MAGIC_GDM_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); - } else { - len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); - } + // cmd is ISO14443A_CMD_WRITEBLOCK for normal tags, but could also be + // MIFARE_MAGIC_GDM_WRITEBLOCK or MIFARE_MAGIC_GDM_WRITE_CFG for certain magic tags + uint16_t len = mifare_sendcmd_short(pcs, 1, cmd, blockNo, receivedAnswer, receivedAnswerPar, NULL); if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; + if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + return PM3_EFAILED; } uint8_t d_block[18], d_block_enc[18]; memcpy(d_block, blockData, 16); AddCrc14A(d_block, 16); - // enough for 18 Bytes to send - uint8_t par[3] = {0x00, 0x00, 0x00}; - // crypto - for (uint32_t pos = 0; pos < 18; pos++) { - d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos]; - par[pos >> 3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos & 0x0007))); - } + if (pcs) { + // enough for 18 Bytes to send + uint8_t par[3] = {0x00, 0x00, 0x00}; + // crypto + for (uint32_t pos = 0; pos < 18; pos++) { + d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos]; + par[pos >> 3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos & 0x0007))); + } - ReaderTransmitPar(d_block_enc, sizeof(d_block_enc), par, NULL); + ReaderTransmitPar(d_block_enc, sizeof(d_block_enc), par, NULL); + } else { + ReaderTransmit(d_block, sizeof(d_block), NULL); + } // tearoff occurred if (tearoff_hook() == PM3_ETEAROFF) { @@ -459,67 +467,24 @@ int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t len = ReaderReceive(receivedAnswer, receivedAnswerPar); uint8_t res = 0; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3; + if (pcs) { + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3; + } else { + res = receivedAnswer[0]; + } if ((len != 1) || (res != 0x0A)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd send data2 Error: %02x", res); - return 2; + if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd send data2 Error: %02x", res); + return PM3_EFAILED; } } - return 0; + return PM3_SUCCESS; } -int mifare_classic_write_cfg_block_gdm(struct Crypto1State *pcs, uint32_t uid, uint8_t *blockData) { - - // variables - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - - uint16_t len = mifare_sendcmd_short(pcs, 1, MIFARE_MAGIC_GDM_WRITE_CFG, 0, receivedAnswer, receivedAnswerPar, NULL); - if ((len != 1) || (receivedAnswer[0] != 0x0A)) { - return 1; - } - - uint8_t d_block[18], d_block_enc[18]; - memcpy(d_block, blockData, 16); - AddCrc14A(d_block, 16); - - // enough for 18 Bytes to send - uint8_t par[3] = {0x00, 0x00, 0x00}; - // crypto - for (uint32_t pos = 0; pos < 18; pos++) { - d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos]; - par[pos >> 3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos & 0x0007))); - } - - ReaderTransmitPar(d_block_enc, sizeof(d_block_enc), par, NULL); - - // tearoff occurred - if (tearoff_hook() == PM3_ETEAROFF) { - return PM3_ETEAROFF; - } else { - // Receive the response - len = ReaderReceive(receivedAnswer, receivedAnswerPar); - - uint8_t res = 0; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3; - - if ((len != 1) || (res != 0x0A)) { - return 2; - } - } - return 0; -} - - - -int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t action) { +int mifare_classic_value(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData, uint8_t action) { // variables uint16_t len = 0; uint32_t pos = 0; @@ -540,8 +505,8 @@ int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo len = mifare_sendcmd_short(pcs, 1, command, blockNo, receivedAnswer, receivedAnswerPar, NULL); if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; + if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + return PM3_EFAILED; } memcpy(d_block, blockData, 4); @@ -566,12 +531,12 @@ int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3; if ((len != 1) || (res != 0x0A)) { - if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd send data2 Error: %02x", res); - return 2; + if (g_dbglevel >= DBG_INFO) Dbprintf("Cmd send data2 Error: %02x", res); + return PM3_EFAILED; } } - return 0; + return PM3_SUCCESS; } int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) { @@ -585,9 +550,10 @@ int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) { len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK - if (g_dbglevel >= DBG_ERROR) + if (g_dbglevel >= DBG_INFO) { Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0], len); - return 1; + } + return PM3_EFAILED; } memcpy(d_block, blockData, 16); @@ -599,11 +565,12 @@ int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) { len = ReaderReceive(receivedAnswer, receivedAnswerPar); if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK - if (g_dbglevel >= DBG_ERROR) + if (g_dbglevel >= DBG_INFO) { Dbprintf("Cmd Send Data Error: %02x %d", receivedAnswer[0], len); - return 2; + } + return PM3_EFAILED; } - return 0; + return PM3_SUCCESS; } int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData) { @@ -618,13 +585,15 @@ int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData) { len = mifare_sendcmd(MIFARE_ULC_WRITE, block, sizeof(block), receivedAnswer, receivedAnswerPar, NULL); if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK - if (g_dbglevel >= DBG_ERROR) + if (g_dbglevel >= DBG_INFO) { Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0], len); - return 1; + } + return PM3_EFAILED; } - return 0; + return PM3_SUCCESS; } -int mifare_classic_halt_ex(struct Crypto1State *pcs) { + +int mifare_classic_halt(struct Crypto1State *pcs) { uint8_t receivedAnswer[4] = {0x00, 0x00, 0x00, 0x00}; uint16_t len = mifare_sendcmd_short(pcs, (pcs == NULL) ? CRYPT_NONE : CRYPT_ALL, ISO14443A_CMD_HALT, 0x00, receivedAnswer, NULL, NULL); if (len != 0) { @@ -633,19 +602,9 @@ int mifare_classic_halt_ex(struct Crypto1State *pcs) { } return 0; } -int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid) { - return mifare_classic_halt_ex(pcs); -} int mifare_ultra_halt(void) { - uint16_t len = 0; - uint8_t receivedAnswer[4] = {0x00, 0x00, 0x00, 0x00}; - len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_HALT, 0x00, receivedAnswer, NULL, NULL); - if (len != 0) { - if (g_dbglevel >= DBG_EXTENDED) Dbprintf("halt warning. response len: %x", len); - return 1; - } - return 0; + return mifare_classic_halt(NULL); } @@ -660,32 +619,28 @@ uint8_t FirstBlockOfSector(uint8_t sectorNo) { return sectorNo * 4; else return 32 * 4 + (sectorNo - 32) * 16; - } // work with emulator memory -void emlSetMem(uint8_t *data, int blockNum, int blocksCount) { - emlSetMem_xt(data, blockNum, blocksCount, 16); -} - -void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { - uint8_t *emCARD = BigBuf_get_EM_addr(); - memcpy(emCARD + blockNum * blockBtWidth, data, blocksCount * blockBtWidth); +void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int block_width) { + uint32_t offset = blockNum * block_width; + uint32_t len = blocksCount * block_width; + emlSet(data, offset, len); } void emlGetMem(uint8_t *data, int blockNum, int blocksCount) { - uint8_t *emCARD = BigBuf_get_EM_addr(); - memcpy(data, emCARD + blockNum * 16, blocksCount * 16); + uint8_t *mem = BigBuf_get_EM_addr(); + memcpy(data, mem + blockNum * 16, blocksCount * 16); } void emlGetMemBt(uint8_t *data, int offset, int byteCount) { - uint8_t *emCARD = BigBuf_get_EM_addr(); - memcpy(data, emCARD + offset, byteCount); + uint8_t *mem = BigBuf_get_EM_addr(); + memcpy(data, mem + offset, byteCount); } int emlCheckValBl(int blockNum) { - uint8_t *emCARD = BigBuf_get_EM_addr(); - uint8_t *data = emCARD + blockNum * 16; + uint8_t *mem = BigBuf_get_EM_addr(); + uint8_t *data = mem + blockNum * 16; if ((data[0] != (data[4] ^ 0xff)) || (data[0] != data[8]) || (data[1] != (data[5] ^ 0xff)) || (data[1] != data[9]) || @@ -699,8 +654,8 @@ int emlCheckValBl(int blockNum) { } int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum) { - uint8_t *emCARD = BigBuf_get_EM_addr(); - uint8_t *data = emCARD + blockNum * 16; + uint8_t *mem = BigBuf_get_EM_addr(); + uint8_t *data = mem + blockNum * 16; if (emlCheckValBl(blockNum)) return 1; @@ -711,8 +666,8 @@ int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum) { } int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum) { - uint8_t *emCARD = BigBuf_get_EM_addr(); - uint8_t *data = emCARD + blockNum * 16; + uint8_t *mem = BigBuf_get_EM_addr(); + uint8_t *data = mem + blockNum * 16; memcpy(data + 0, &blReg, 4); memcpy(data + 8, &blReg, 4); @@ -723,41 +678,43 @@ int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum) { data[13] = blBlock ^ 0xff; data[14] = blBlock; data[15] = blBlock ^ 0xff; - return 0; } uint64_t emlGetKey(int sectorNum, int keyType) { uint8_t key[6] = {0x00}; - uint8_t *emCARD = BigBuf_get_EM_addr(); - memcpy(key, emCARD + 16 * (FirstBlockOfSector(sectorNum) + NumBlocksPerSector(sectorNum) - 1) + keyType * 10, 6); + uint8_t *mem = BigBuf_get_EM_addr(); + memcpy(key, mem + 16 * (FirstBlockOfSector(sectorNum) + NumBlocksPerSector(sectorNum) - 1) + keyType * 10, 6); return bytes_to_num(key, 6); } void emlClearMem(void) { const uint8_t trailer[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x80, 0x69, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; const uint8_t uid[] = {0xe6, 0x84, 0x87, 0xf3, 0x16, 0x88, 0x04, 0x00, 0x46, 0x8e, 0x45, 0x55, 0x4d, 0x70, 0x41, 0x04}; - uint8_t *emCARD = BigBuf_get_EM_addr(); - memset(emCARD, 0, CARD_MEMORY_SIZE); + uint8_t *mem = BigBuf_get_EM_addr(); + memset(mem, 0, CARD_MEMORY_SIZE); // fill sectors trailer data - for (uint16_t b = 3; b < MIFARE_4K_MAXBLOCK; ((b < MIFARE_2K_MAXBLOCK - 4) ? (b += 4) : (b += 16))) - emlSetMem((uint8_t *)trailer, b, 1); + for (uint16_t b = 3; b < MIFARE_4K_MAXBLOCK; ((b < MIFARE_2K_MAXBLOCK - 4) ? (b += 4) : (b += 16))) { + emlSetMem_xt((uint8_t *)trailer, b, 1, 16); + } // uid - emlSetMem((uint8_t *)uid, 0, 1); + emlSetMem_xt((uint8_t *)uid, 0, 1, 16); return; } uint8_t SectorTrailer(uint8_t blockNo) { if (blockNo <= MIFARE_2K_MAXBLOCK) { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("Sector Trailer for block %d : %d", blockNo, (blockNo | 0x03)); + } return (blockNo | 0x03); } else { - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("Sector Trailer for block %d : %d", blockNo, (blockNo | 0x0f)); - return (blockNo | 0x0f); + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("Sector Trailer for block %d : %d", blockNo, (blockNo | 0x0F)); + } + return (blockNo | 0x0F); } } @@ -804,9 +761,10 @@ int mifare_desfire_des_auth1(uint32_t uid, uint8_t *blockData) { len = mifare_sendcmd_special(NULL, 1, 0x02, data, receivedAnswer, receivedAnswerPar, NULL); if (len == 1) { - if (g_dbglevel >= DBG_ERROR) + if (g_dbglevel >= DBG_INFO) { Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; + } + return PM3_EFAILED; } if (len == 12) { @@ -817,9 +775,9 @@ int mifare_desfire_des_auth1(uint32_t uid, uint8_t *blockData) { receivedAnswer[10], receivedAnswer[11]); } memcpy(blockData, receivedAnswer, 12); - return 0; + return PM3_SUCCESS; } - return 1; + return PM3_EFAILED; } int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData) { @@ -834,9 +792,10 @@ int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData) { len = mifare_sendcmd_special2(NULL, 1, 0x03, data, receivedAnswer, receivedAnswerPar, NULL); if ((receivedAnswer[0] == 0x03) && (receivedAnswer[1] == 0xae)) { - if (g_dbglevel >= DBG_ERROR) + if (g_dbglevel >= DBG_ERROR) { Dbprintf("Auth Error: %02x %02x", receivedAnswer[0], receivedAnswer[1]); - return 1; + } + return PM3_EFAILED; } if (len == 12) { @@ -847,7 +806,7 @@ int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData) { receivedAnswer[10], receivedAnswer[11]); } memcpy(blockData, receivedAnswer, 12); - return 0; + return PM3_SUCCESS; } - return 1; + return PM3_EFAILED; } diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index e5731ce36..fd0749058 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -30,22 +30,24 @@ #define AUTH_FIRST 0 #define AUTH_NESTED 2 -#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) +#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) #define PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication // reader voltage field detector -#define MF_MINFIELDV 4000 +#define MF_MINFIELDV 4000 // Mifare 4k/2k/1k/mini Max Block / Max Sector -#define MIFARE_4K_MAXBLOCK 256 -#define MIFARE_2K_MAXBLOCK 128 -#define MIFARE_1K_MAXBLOCK 64 -#define MIFARE_MINI_MAXBLOCK 20 +#define MIFARE_4K_MAXBLOCK 256 +#define MIFARE_2K_MAXBLOCK 128 +#define MIFARE_1K_MAXBLOCK 64 +#define MIFARE_MINI_MAXBLOCK 20 -#define MIFARE_MINI_MAXSECTOR 5 -#define MIFARE_1K_MAXSECTOR 16 -#define MIFARE_2K_MAXSECTOR 32 -#define MIFARE_4K_MAXSECTOR 40 +#define MIFARE_MINI_MAXSECTOR 5 +#define MIFARE_1K_MAXSECTOR 16 +#define MIFARE_2K_MAXSECTOR 32 +#define MIFARE_4K_MAXSECTOR 40 + +#define MIFARE_BLOCK_SIZE 16 //mifare emulator states #define MFEMUL_NOFIELD 0 @@ -72,17 +74,15 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t // mifare classic int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested); int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing); -int mifare_classic_authex_2(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing, bool is_gdm); +int mifare_classic_authex_cmd(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t cmd, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *ntencptr, uint32_t *timing); -int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); -int mifare_classic_readblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t iso_byte); +int mifare_classic_readblock(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData); +int mifare_classic_readblock_ex(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData, uint8_t iso_byte); -int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); -int mifare_classic_halt_ex(struct Crypto1State *pcs); -int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); -int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, bool is_gdm); -int mifare_classic_value(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData, uint8_t action); -int mifare_classic_write_cfg_block_gdm(struct Crypto1State *pcs, uint32_t uid, uint8_t *blockData); +int mifare_classic_halt(struct Crypto1State *pcs); +int mifare_classic_writeblock(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData); +int mifare_classic_writeblock_ex(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData, uint8_t cmd); +int mifare_classic_value(struct Crypto1State *pcs, uint8_t blockNo, uint8_t *blockData, uint8_t action); // Ultralight/NTAG... int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack); @@ -115,8 +115,7 @@ uint8_t SectorTrailer(uint8_t blockNo); // emulator functions void emlClearMem(void); -void emlSetMem(uint8_t *data, int blockNum, int blocksCount); -void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); +void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int block_width); void emlGetMem(uint8_t *data, int blockNum, int blocksCount); void emlGetMemBt(uint8_t *data, int offset, int byteCount); uint64_t emlGetKey(int sectorNum, int keyType); diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c index 68b36af85..c2ffa5da9 100644 --- a/armsrc/optimized_cipher.c +++ b/armsrc/optimized_cipher.c @@ -115,12 +115,12 @@ static void init_opt_select_LUT(void) { print_result("", opt_select_LUT, 256); } ***********************************************************************************/ - +/* #define opt__select(x,y,r) (4 & (((r & (r << 2)) >> 5) ^ ((r & ~(r << 2)) >> 4) ^ ( (r | r << 2) >> 3)))\ |(2 & (((r | r << 2) >> 6) ^ ( (r | r << 2) >> 1) ^ (r >> 5) ^ r ^ ((x^y) << 1)))\ |(1 & (((r & ~(r << 2)) >> 4) ^ ((r & (r << 2)) >> 3) ^ r ^ x)) -/* + * Some background on the expression above can be found here... uint8_t xopt__select(bool x, bool y, uint8_t r) { @@ -201,7 +201,9 @@ static void opt_suc(const uint8_t *k, State_t *s, const uint8_t *in, uint8_t len } //For tag MAC, an additional 32 zeroes if (add32Zeroes) { - for (int i = 0; i < 16; i++) { + for (int i = 0; i < 8; i++) { + opt_successor(k, s, 0); + opt_successor(k, s, 0); opt_successor(k, s, 0); opt_successor(k, s, 0); } diff --git a/armsrc/optimized_cipherutils.c b/armsrc/optimized_cipherutils.c index 3afba7164..66a618291 100644 --- a/armsrc/optimized_cipherutils.c +++ b/armsrc/optimized_cipherutils.c @@ -114,7 +114,7 @@ uint64_t x_bytes_to_num(uint8_t *src, size_t len) { return num; } -uint8_t reversebytes(uint8_t b) { +uint8_t reversebyte(uint8_t b) { b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; b = (b & 0xCC) >> 2 | (b & 0x33) << 2; b = (b & 0xAA) >> 1 | (b & 0x55) << 1; @@ -124,14 +124,14 @@ uint8_t reversebytes(uint8_t b) { void reverse_arraybytes(uint8_t *arr, size_t len) { size_t i; for (i = 0; i < len ; i++) { - arr[i] = reversebytes(arr[i]); + arr[i] = reversebyte(arr[i]); } } void reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len) { size_t i; for (i = 0; i < len ; i++) { - dest[i] = reversebytes(arr[i]); + dest[i] = reversebyte(arr[i]); } } diff --git a/armsrc/optimized_cipherutils.h b/armsrc/optimized_cipherutils.h index 1bef017a2..8d8a75f8c 100644 --- a/armsrc/optimized_cipherutils.h +++ b/armsrc/optimized_cipherutils.h @@ -57,7 +57,7 @@ int bitsLeft(BitstreamIn_t *stream); void push6bits(BitstreamOut_t *stream, uint8_t bits); void x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest); uint64_t x_bytes_to_num(uint8_t *src, size_t len); -uint8_t reversebytes(uint8_t b); +uint8_t reversebyte(uint8_t b); void reverse_arraybytes(uint8_t *arr, size_t len); void reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len); #endif // CIPHERUTILS_H diff --git a/armsrc/sam_mfc.c b/armsrc/sam_mfc.c new file mode 100644 index 000000000..090f4a781 --- /dev/null +++ b/armsrc/sam_mfc.c @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Routines to support MFC <-> SAM communication +//----------------------------------------------------------------------------- +#include "sam_mfc.h" +#include "sam_seos.h" +#include "iclass.h" + +#include "proxmark3_arm.h" +#include "cmd.h" diff --git a/armsrc/sam_mfc.h b/armsrc/sam_mfc.h new file mode 100644 index 000000000..5cf55d711 --- /dev/null +++ b/armsrc/sam_mfc.h @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +#ifndef __SAM_MFC_H +#define __SAM_MFC_H + +#include "common.h" + +#endif diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c new file mode 100644 index 000000000..fd465c992 --- /dev/null +++ b/armsrc/sam_picopass.c @@ -0,0 +1,447 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Routines to support Picopass <-> SAM communication +//----------------------------------------------------------------------------- +#include "sam_picopass.h" +#include "iclass.h" +#include "crc16.h" +#include "proxmark3_arm.h" +#include "BigBuf.h" +#include "cmd.h" +#include "commonutil.h" +#include "ticks.h" +#include "dbprint.h" +#include "i2c.h" +#include "iso15693.h" +#include "protocols.h" +#include "optimized_cipher.h" +#include "fpgaloader.h" + +static int sam_rxtx(const uint8_t *data, uint16_t n, uint8_t *resp, uint16_t *resplen) { + + StartTicks(); + + bool res = I2C_BufferWrite(data, n, I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); + if (res == false) { + DbpString("failed to send to SIM CARD"); + goto out; + } + + *resplen = ISO7816_MAX_FRAME; + + res = sc_rx_bytes(resp, resplen, SIM_WAIT_DELAY); + if (res == false) { + DbpString("failed to receive from SIM CARD"); + goto out; + } + + if (*resplen < 2) { + DbpString("received too few bytes from SIM CARD"); + res = false; + goto out; + } + + uint16_t more_len = 0; + + if (resp[*resplen - 2] == 0x61 || resp[*resplen - 2] == 0x9F) { + more_len = resp[*resplen - 1]; + } else { + // we done, return + goto out; + } + + // Don't discard data we already received except the SW code. + // If we only received 1 byte, this is the echo of INS, we discard it. + *resplen -= 2; + if (*resplen == 1) { + *resplen = 0; + } + + uint8_t cmd_getresp[] = {0x00, ISO7816_GET_RESPONSE, 0x00, 0x00, more_len}; + + res = I2C_BufferWrite(cmd_getresp, sizeof(cmd_getresp), I2C_DEVICE_CMD_SEND_T0, I2C_DEVICE_ADDRESS_MAIN); + if (res == false) { + DbpString("failed to send to SIM CARD 2"); + goto out; + } + + more_len = 255 - *resplen; + + res = sc_rx_bytes(resp + *resplen, &more_len, SIM_WAIT_DELAY); + if (res == false) { + DbpString("failed to receive from SIM CARD 2"); + goto out; + } + + *resplen += more_len; + +out: + StopTicks(); + return res; +} + +// using HID SAM to authenticate w PICOPASS +int sam_picopass_get_pacs(void) { + + static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; + static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; + static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 }; + uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t read_aia[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; + uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; + + picopass_hdr_t hdr = {0}; + // Bit 4: K.If this bit equals to one, the READCHECK will use the Credit Key (Kc); if equals to zero, Debit Key (Kd) will be used + // bit 7: parity. + // if (use_credit_key) + // read_check_cc[0] = 0x10 | ICLASS_CMD_READCHECK; + + BigBuf_free_keep_EM(); + + clear_trace(); + + I2C_Reset_EnterMainProgram(); + StopTicks(); + + uint8_t *resp = BigBuf_calloc(ISO7816_MAX_FRAME); + + bool shallow_mod = false; + uint16_t resp_len = 0; + int res; + uint32_t eof_time = 0; + + // wakeup + Iso15693InitReader(); + + uint32_t start_time = GetCountSspClk(); + iclass_send_as_reader(act_all, 1, &start_time, &eof_time, shallow_mod); + + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_ACTALL, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // send Identify + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(identify, 1, &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 10) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // copy the Anti-collision CSN to our select-packet + memcpy(&select[1], resp, 8); + + // select the card + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(select, sizeof(select), &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here, 8 byte CSN and 2 byte CRC + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 10) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // store CSN + memcpy(hdr.csn, resp, sizeof(hdr.csn)); + + // card selected, now read config (block1) (only 8 bytes no CRC) + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_conf, sizeof(read_conf), &start_time, &eof_time, shallow_mod); + + // expect a 8-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 10) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // store CONFIG + memcpy((uint8_t *)&hdr.conf, resp, sizeof(hdr.conf)); + + uint8_t pagemap = get_pagemap(&hdr); + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + res = PM3_EWRONGANSWER; + goto out; + } + + // read App Issuer Area block 5 + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 10) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // store AIA + memcpy(hdr.app_issuer_area, resp, sizeof(hdr.app_issuer_area)); + + // card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); + + // expect a 8-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 8) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + // store EPURSE + memcpy(hdr.epurse, resp, sizeof(hdr.epurse)); + + // ----------------------------------------------------------------------------- + // SAM comms + // ----------------------------------------------------------------------------- + size_t sam_len = 0; + uint8_t *sam_apdu = BigBuf_calloc(ISO7816_MAX_FRAME); + + // ----------------------------------------------------------------------------- + // first + // a0 da 02 63 1a 44 0a 44 00 00 00 a0 12 ad 10 a0 0e 80 02 00 04 81 08 9b fc a4 00 fb ff 12 e0 + hexstr_to_byte_array("a0da02631a440a44000000a012ad10a00e800200048108", sam_apdu, &sam_len); + memcpy(sam_apdu + sam_len, hdr.csn, sizeof(hdr.csn)); + sam_len += sizeof(hdr.csn); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 1", resp, resp_len); + + // ----------------------------------------------------------------------------- + // second + // a0 da 02 63 0d 44 0a 44 00 00 00 a0 05 a1 03 80 01 04 + hexstr_to_byte_array("a0da02630d440a44000000a005a103800104", sam_apdu, &sam_len); + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 2", resp, resp_len); + + // TAG response + // -- 0c 05 de64 // read block 5 + // Tag|c00a140a000000a110a10e8004 0c05de64 8102 0004 820201f4 + + // ----------------------------------------------------------------------------- + // third AIA block 5 + // a0da02631c140a00000000bd14a012a010800a ffffff0006fffffff88e 81020000 + // picopass legacy is fixed. wants AIA and crc. ff ff ff ff ff ff ff ff ea f5 + // picpoasss SE ff ff ff 00 06 ff ff ff f8 8e + hexstr_to_byte_array("a0da02631c140a00000000bd14a012a010800affffff0006fffffff88e81020000", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, hdr.app_issuer_area, sizeof(hdr.app_issuer_area)); + AddCrc(sam_apdu + 19, 8); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 3", resp, resp_len); + + // 88 02 -- readcheck (block2 epurse, start of auth) + // Tag|c00a140a000000a10ea10c8002 8802 8102 0004 820201f4 9000 + // 61 16 f5 0a140a000000a10ea10c 8002 8802 8102 0004 820201f4 9000 + + // ----------------------------------------------------------------------------- + // forth EPURSE + // a0da02631a140a00000000bd12a010a00e8008 ffffffffedffffff 81020000 + hexstr_to_byte_array("a0da02631a140a00000000bd12a010a00e8008ffffffffedffffff81020000", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, hdr.epurse, sizeof(hdr.epurse)); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 4", resp, resp_len); + + uint8_t nr_mac[9] = {0}; + memcpy(nr_mac, resp + 11, sizeof(nr_mac)); + // resp here hold the whole NR/MAC + // 05 9bcd475e965ee20e // CHECK (w key) + print_dbg("NR/MAC", nr_mac, sizeof(nr_mac)); + + // c00a140a000000a115a1138009 059bcd475e965ee20e 8102 0004 820201f4 9000 + + // pre calc ourself? + // uint8_t cc_nr[] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0}; + uint8_t div_key[8] = {0}; + static uint8_t legacy_aa1_key[] = {0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78}; + iclass_calc_div_key(hdr.csn, legacy_aa1_key, div_key, false); + + uint8_t mac[4] = {0}; + if (g_dbglevel == DBG_DEBUG) { + uint8_t wb[16] = {0}; + memcpy(wb, hdr.epurse, sizeof(hdr.epurse)); + memcpy(wb + sizeof(hdr.epurse), nr_mac + 1, 4); + print_dbg("cc_nr...", wb, sizeof(wb)); + doMAC_N(wb, sizeof(wb), div_key, mac); + print_dbg("Calc MAC...", mac, sizeof(mac)); + } + + // start ssp clock again... + StartCountSspClk(); + + // NOW we auth against tag + uint8_t cmd_check[9] = { ICLASS_CMD_CHECK }; + memcpy(cmd_check + 1, nr_mac + 1, 8); + + start_time = GetCountSspClk(); + iclass_send_as_reader(cmd_check, sizeof(cmd_check), &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 4) { + res = PM3_ECARDEXCHANGE; + goto out; + } + // store MAC + memcpy(mac, resp, sizeof(mac)); + print_dbg("Got MAC", mac, sizeof(mac)); + + // ----------------------------------------------------------------------------- + // fifth send received MAC + // A0DA026316140A00000000BD0EA00CA00A8004 311E32E9 81020000 + hexstr_to_byte_array("A0DA026316140A00000000BD0EA00CA00A8004311E32E981020000", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, mac, sizeof(mac)); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 5", resp, resp_len); + + uint8_t tmp_p1[4] = {0}; + uint8_t tmp_p2[4] = {0}; + + // c161c10000a11aa118800e8702 ffffffff88ffffff 0a914eb981020004820236b09000 + + memcpy(tmp_p1, resp + 13, sizeof(tmp_p1)); + memcpy(tmp_p2, resp + 13 + 4, sizeof(tmp_p2)); + // ----------------------------------------------------------------------------- + // sixth send fake epurse update + // A0DA02631C140A00000000BD14A012A010800A 88FFFFFFFFFFFFFF9DE1 81020000 + hexstr_to_byte_array("A0DA02631C140A00000000BD14A012A010800A88FFFFFFFFFFFFFF9DE181020000", sam_apdu, &sam_len); + + memcpy(sam_apdu + 19, tmp_p2, sizeof(tmp_p1)); + memcpy(sam_apdu + 19 + 4, tmp_p1, sizeof(tmp_p1)); + AddCrc(sam_apdu + 19, 8); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 6", resp, resp_len); + // c1 61 c1 00 00 a1 10 a1 0e 80 04 0c 06 45 56 81 02 00 04 82 02 01 f4 90 00 + + // read block 6 + StartCountSspClk(); + start_time = GetCountSspClk(); + iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS || resp_len != 10) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("Block 6 from Picopass", resp, resp_len); + + // ----------------------------------------------------------------------------- + // eight send block 6 config to SAM + // A0DA02631C140A00000000BD14A012A010800A 030303030003E0174323 81020000 + hexstr_to_byte_array("A0DA02631C140A00000000BD14A012A010800A030303030003E017432381020000", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, resp, resp_len); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 7", resp, resp_len); + + // c161c10000a110a10e8004 0606455681020004820201f49000 + + // read the credential blocks + StartCountSspClk(); + start_time = GetCountSspClk(); + iclass_send_as_reader(resp + 11, 4, &start_time, &eof_time, shallow_mod); + + // expect a 10-byte response here + res = GetIso15693AnswerFromTag(resp, ISO7816_MAX_FRAME, ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); + if (res != PM3_SUCCESS) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("Block 6-9 from Picopass", resp, resp_len); + + // ----------------------------------------------------------------------------- + // nine send credential blocks to SAM + // A0DA026334140A00000000BD2CA02AA0288022 030303030003E017769CB4A198E0DEC82AD4C8211F9968712BE7393CF8E71D7E804C 81020000 + hexstr_to_byte_array("A0DA026334140A00000000BD2CA02AA0288022030303030003E017769CB4A198E0DEC82AD4C8211F9968712BE7393CF8E71D7E804C81020000", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, resp, resp_len); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + print_dbg("-- 8", resp, resp_len); + + + // ----------------------------------------------------------------------------- + // TEN ask for PACS data + // A0DA02630C440A00000000BD04A0028200 + hexstr_to_byte_array("A0DA02630C440A00000000BD04A0028200", sam_apdu, &sam_len); + memcpy(sam_apdu + 19, resp, resp_len); + + if (sam_rxtx(sam_apdu, sam_len, resp, &resp_len) == false) { + res = PM3_ECARDEXCHANGE; + goto out; + } + + print_dbg("-- 9 response", resp, resp_len); + if (memcmp(resp, "\xc1\x64\x00\x00\x00\xbd\x17\x8a\x15", 9) == 0) { + res = PM3_ENOPACS; + goto out; + } + + // c164000000bd098a07 030506951f9a00 9000 + uint8_t *pacs = BigBuf_calloc(resp[8]); + memcpy(pacs, resp + 9, resp[8]); + + print_dbg("-- 10 PACS data", pacs, resp[8]); + + reply_ng(CMD_HF_SAM_PICOPASS, PM3_SUCCESS, pacs, resp[8]); + res = PM3_SUCCESS; + goto off; + +out: + reply_ng(CMD_HF_SAM_PICOPASS, res, NULL, 0); + +off: + switch_off(); + BigBuf_free(); + return res; +} + +// HID SAM <-> MFC +// HID SAM <-> SEOS diff --git a/armsrc/sam_picopass.h b/armsrc/sam_picopass.h new file mode 100644 index 000000000..7feef0bde --- /dev/null +++ b/armsrc/sam_picopass.h @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +#ifndef __SAM_PICOPASS_H +#define __SAM_PICOPASS_H + +#include "common.h" + +int sam_picopass_get_pacs(void); + +#endif diff --git a/armsrc/sam_seos.c b/armsrc/sam_seos.c new file mode 100644 index 000000000..00e4da45b --- /dev/null +++ b/armsrc/sam_seos.c @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Routines to support SEOS <-> SAM communication +//----------------------------------------------------------------------------- +#include "sam_seos.h" +#include "iclass.h" + +#include "proxmark3_arm.h" +#include "cmd.h" diff --git a/armsrc/sam_seos.h b/armsrc/sam_seos.h new file mode 100644 index 000000000..c2100f07e --- /dev/null +++ b/armsrc/sam_seos.h @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +#ifndef __SAM_SEOS_H +#define __SAM_SEOS_H + +#include "common.h" + +#endif diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index e243b8ed1..3154bfc0f 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -196,17 +196,19 @@ int rdv40_spiffs_check(void) { ///// Base RDV40_SPIFFS_SAFETY_NORMAL operations//////////////////////////////// -void write_to_spiffs(const char *filename, uint8_t *src, uint32_t size) { +void write_to_spiffs(const char *filename, const uint8_t *src, uint32_t size) { spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0); - if (SPIFFS_write(&fs, fd, src, size) < 0) { + // Note: SPIFFS_write() doesn't declare third parameter as const (but should) + if (SPIFFS_write(&fs, fd, (void *)src, size) < 0) { Dbprintf("wr errno %i\n", SPIFFS_errno(&fs)); } SPIFFS_close(&fs, fd); } -void append_to_spiffs(const char *filename, uint8_t *src, uint32_t size) { +void append_to_spiffs(const char *filename, const uint8_t *src, uint32_t size) { spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_APPEND | SPIFFS_RDWR, 0); - if (SPIFFS_write(&fs, fd, src, size) < 0) { + // Note: SPIFFS_write() doesn't declare third parameter as const (but should) + if (SPIFFS_write(&fs, fd, (void *)src, size) < 0) { Dbprintf("errno %i\n", SPIFFS_errno(&fs)); } SPIFFS_close(&fs, fd); @@ -310,10 +312,10 @@ static int is_valid_filename(const char *filename) { } */ static void copy_in_spiffs(const char *src, const char *dst) { - uint32_t size = size_in_spiffs((char *)src); + uint32_t size = size_in_spiffs(src); uint8_t *mem = BigBuf_malloc(size); - read_from_spiffs((char *)src, (uint8_t *)mem, size); - write_to_spiffs((char *)dst, (uint8_t *)mem, size); + read_from_spiffs(src, (uint8_t *)mem, size); + write_to_spiffs(dst, (uint8_t *)mem, size); } //////////////////////////////////////////////////////////////////////////////// @@ -434,7 +436,7 @@ int rdv40_spiffs_lazy_mount_rollback(int changed) { // TODO : forbid writing to a filename which already exists as lnk ! // TODO : forbid writing to a filename.lnk which already exists without lnk ! // Note: Writing in SPIFFS_WRITE_CHUNK_SIZE (8192) byte chucks helps to ensure "free space" has been erased by GC (Garbage collection) -int rdv40_spiffs_write(const char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) { +int rdv40_spiffs_write(const char *filename, const uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( uint32_t idx; if (size <= SPIFFS_WRITE_CHUNK_SIZE) { @@ -457,7 +459,7 @@ int rdv40_spiffs_write(const char *filename, uint8_t *src, uint32_t size, RDV40S ) } -int rdv40_spiffs_append(const char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) { +int rdv40_spiffs_append(const char *filename, const uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( uint32_t idx; // Append any SPIFFS_WRITE_CHUNK_SIZE byte chunks @@ -480,26 +482,26 @@ int rdv40_spiffs_read(const char *filename, uint8_t *dst, uint32_t size, RDV40Sp // TODO : forbid writing to a filename which already exists as lnk ! // TODO : forbid writing to a filename.lnk which already exists without lnk ! -int rdv40_spiffs_rename(char *old_filename, char *new_filename, RDV40SpiFFSSafetyLevel level) { +int rdv40_spiffs_rename(const char *old_filename, const char *new_filename, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( // rename_in_spiffs(old_filename, new_filename); // ) } -int rdv40_spiffs_remove(char *filename, RDV40SpiFFSSafetyLevel level) { +int rdv40_spiffs_remove(const char *filename, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( // remove_from_spiffs(filename); // ) } -int rdv40_spiffs_copy(char *src, char *dst, RDV40SpiFFSSafetyLevel level) { +int rdv40_spiffs_copy(const char *src_filename, const char *dst_filename, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( // - copy_in_spiffs(src, dst); // + copy_in_spiffs(src_filename, dst_filename); // ) } -int rdv40_spiffs_stat(char *filename, uint32_t *buf, RDV40SpiFFSSafetyLevel level) { +int rdv40_spiffs_stat(const char *filename, uint32_t *size_in_bytes, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( // - *buf = size_in_spiffs(filename); // + *size_in_bytes = size_in_spiffs(filename); // ) } @@ -530,7 +532,7 @@ int rdv40_spiffs_is_symlink(const char *s) { // symlink ?") // ATTENTION : you must NOT provide the whole filename (so please do not include the .lnk extension) // TODO : integrate in read_function -int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) { +int rdv40_spiffs_read_as_symlink(const char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( char linkdest[SPIFFS_OBJ_NAME_LEN]; @@ -538,7 +540,7 @@ int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RD sprintf(linkfilename, "%s.lnk", filename); if (g_dbglevel >= DBG_DEBUG) - Dbprintf("Linkk real filename is " _YELLOW_("%s"), linkfilename); + Dbprintf("Link real filename is " _YELLOW_("%s"), linkfilename); read_from_spiffs((char *)linkfilename, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); @@ -561,11 +563,11 @@ int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RD // which you can then read back with : // rdv40_spiffs_read_as_symlink((uint8_t *)"world",(uint8_t *) buffer, orig_file_size, RDV40_SPIFFS_SAFETY_SAFE); // TODO : FORBID creating a symlink with a basename (before.lnk) which already exists as a file ! -int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyLevel level) { +int rdv40_spiffs_make_symlink(const char *linkdest, const char *filename, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( char linkfilename[SPIFFS_OBJ_NAME_LEN]; sprintf(linkfilename, "%s.lnk", filename); - write_to_spiffs((char *)linkfilename, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); + write_to_spiffs(linkfilename, (const uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); ) } @@ -575,15 +577,15 @@ int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyL // Still, this case won't happen when the write(s) functions will check for both symlink and real file // preexistence, avoiding a link being created if filename exists, or avoiding a file being created if // symlink exists with same name -int rdv40_spiffs_read_as_filetype(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) { +int rdv40_spiffs_read_as_filetype(const char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) { RDV40_SPIFFS_SAFE_FUNCTION( RDV40SpiFFSFileType filetype = filetype_in_spiffs((char *)filename); switch (filetype) { case RDV40_SPIFFS_FILETYPE_REAL: - rdv40_spiffs_read((char *)filename, (uint8_t *)dst, size, level); + rdv40_spiffs_read(filename, dst, size, level); break; case RDV40_SPIFFS_FILETYPE_SYMLINK: - rdv40_spiffs_read_as_symlink(filename, (uint8_t *)dst, size, level); + rdv40_spiffs_read_as_symlink(filename, dst, size, level); break; case RDV40_SPIFFS_FILETYPE_BOTH: case RDV40_SPIFFS_FILETYPE_UNKNOWN: diff --git a/armsrc/spiffs.h b/armsrc/spiffs.h index 5958e559b..7bbfb794b 100644 --- a/armsrc/spiffs.h +++ b/armsrc/spiffs.h @@ -46,18 +46,18 @@ typedef struct rdv40_spiffs_fsinfo { uint32_t usedPercent, freePercent; } rdv40_spiffs_fsinfo; -int rdv40_spiffs_read_as_filetype(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_read_as_filetype(const char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); int rdv40_spiffs_check(void); int rdv40_spiffs_lazy_unmount(void); int rdv40_spiffs_lazy_mount(void); int rdv40_spiffs_lazy_mount_rollback(int changed); -int rdv40_spiffs_write(const char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_write(const char *filename, const uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level); int rdv40_spiffs_read(const char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); -int rdv40_spiffs_rename(char *old_filename, char *new_filename, RDV40SpiFFSSafetyLevel level); -int rdv40_spiffs_remove(char *filename, RDV40SpiFFSSafetyLevel level); -int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); -void write_to_spiffs(const char *filename, uint8_t *src, uint32_t size); +int rdv40_spiffs_rename(const char *old_filename, const char *new_filename, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_remove(const char *filename, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_read_as_symlink(const char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); +void write_to_spiffs(const char *filename, const uint8_t *src, uint32_t size); void read_from_spiffs(const char *filename, uint8_t *dst, uint32_t size); void test_spiffs(void); void rdv40_spiffs_safe_print_tree(void); @@ -65,11 +65,11 @@ int rdv40_spiffs_unmount(void); int rdv40_spiffs_mount(void); int rdv40_spiffs_is_symlink(const char *s); void rdv40_spiffs_safe_print_fsinfo(void); -int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyLevel level); -void append_to_spiffs(const char *filename, uint8_t *src, uint32_t size); -int rdv40_spiffs_copy(char *src, char *dst, RDV40SpiFFSSafetyLevel level); -int rdv40_spiffs_append(const char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level); -int rdv40_spiffs_stat(char *filename, uint32_t *buf, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_make_symlink(const char *linkdest, const char *filename, RDV40SpiFFSSafetyLevel level); +void append_to_spiffs(const char *filename, const uint8_t *src, uint32_t size); +int rdv40_spiffs_copy(const char *src_filename, const char *dst_filename, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_append(const char *filename, const uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_stat(const char *filename, uint32_t *size_in_bytes, RDV40SpiFFSSafetyLevel level); uint32_t size_in_spiffs(const char *filename); int exists_in_spiffs(const char *filename); diff --git a/armsrc/thinfilm.c b/armsrc/thinfilm.c index 82db7c7cb..bdc4d7d11 100644 --- a/armsrc/thinfilm.c +++ b/armsrc/thinfilm.c @@ -101,7 +101,7 @@ static int EmSendCmdThinfilmRaw(const uint8_t *resp, uint16_t respLen) { } if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); + b = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); (void)b; } if (BUTTON_PRESS()) break; @@ -121,13 +121,12 @@ static int EmSendCmdThinfilmRaw(const uint8_t *resp, uint16_t respLen) { } void SimulateThinFilm(uint8_t *data, size_t len) { - Dbprintf("Simulate %i-bit Thinfilm tag", len * 8); - Dbhexdump(len, data, true); - int16_t status = PM3_SUCCESS; - CodeThinfilmAsTag(data, len); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + Dbprintf("Simulate " _YELLOW_("%i-bit Thinfilm") " tag", len * 8); + Dbhexdump(len, data, true); + // Set up the synchronous serial port FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); @@ -136,29 +135,38 @@ void SimulateThinFilm(uint8_t *data, size_t len) { FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_MOD); SpinDelay(100); + // Start the timer + StartCountSspClk(); uint16_t hf_baseline = ReadReaderField(); - tosend_t *ts = get_tosend(); + int16_t status = PM3_SUCCESS; + CodeThinfilmAsTag(data, len); - // Start the timer - StartCountSspClk(); + tosend_t *ts = get_tosend(); bool reader_detected = false; LED_A_ON(); for (;;) { + WDT_HIT(); + if (BUTTON_PRESS() || data_available()) { status = PM3_EOPABORTED; break; } + uint16_t hf_av = ReadReaderField(); - if (hf_av < hf_baseline) + + if (hf_av < hf_baseline) { hf_baseline = hf_av; + } + if (hf_av > hf_baseline + 10) { EmSendCmdThinfilmRaw(ts->buf, ts->max); - if (!reader_detected) { + + if (reader_detected == false) { LED_B_ON(); //Dbprintf("Reader detected, start beaming data"); reader_detected = true; @@ -166,7 +174,7 @@ void SimulateThinFilm(uint8_t *data, size_t len) { } else { if (reader_detected) { LED_B_OFF(); - //Dbprintf("Reader gone, stop beaming data"); + // Dbprintf("Reader gone, stop beaming data"); reader_detected = false; } } diff --git a/armsrc/usart.c b/armsrc/usart.c index f92e9c701..a205acb03 100644 --- a/armsrc/usart.c +++ b/armsrc/usart.c @@ -18,6 +18,34 @@ #include "usart.h" #include "proxmark3_arm.h" +#define Dbprintf_usb(...) {\ + bool tmpfpc = g_reply_via_fpc;\ + bool tmpusb = g_reply_via_usb;\ + g_reply_via_fpc = false;\ + g_reply_via_usb = true;\ + Dbprintf(__VA_ARGS__);\ + g_reply_via_fpc = tmpfpc;\ + g_reply_via_usb = tmpusb;} + +#define Dbprintf_fpc(...) {\ + bool tmpfpc = g_reply_via_fpc;\ + bool tmpusb = g_reply_via_usb;\ + g_reply_via_fpc = true;\ + g_reply_via_usb = false;\ + Dbprintf(__VA_ARGS__);\ + g_reply_via_fpc = tmpfpc;\ + g_reply_via_usb = tmpusb;} + +#define Dbprintf_all(...) {\ + bool tmpfpc = g_reply_via_fpc;\ + bool tmpusb = g_reply_via_usb;\ + g_reply_via_fpc = true;\ + g_reply_via_usb = true;\ + Dbprintf(__VA_ARGS__);\ + g_reply_via_fpc = tmpfpc;\ + g_reply_via_usb = tmpusb;} + + static volatile AT91PS_USART pUS1 = AT91C_BASE_US1; static volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; static volatile AT91PS_PDC pPDC = AT91C_BASE_PDC_US1; @@ -46,8 +74,8 @@ void usart_close(void) { } */ -static uint8_t us_inbuf1[USART_BUFFLEN]; -static uint8_t us_inbuf2[USART_BUFFLEN]; +static uint8_t us_in_a[USART_BUFFLEN]; +static uint8_t us_in_b[USART_BUFFLEN]; static uint8_t *usart_cur_inbuf = NULL; static uint16_t usart_cur_inbuf_off = 0; static uint8_t us_rxfifo[USART_FIFOLEN]; @@ -56,7 +84,9 @@ static size_t us_rxfifo_high = 0; static void usart_fill_rxfifo(void) { - uint16_t rxfifo_free ; + + uint16_t rxfifo_free = 0; + if (pUS1->US_RNCR == 0) { // One buffer got filled, backup buffer being used if (us_rxfifo_low > us_rxfifo_high) @@ -79,19 +109,22 @@ static void usart_fill_rxfifo(void) { pUS1->US_RNCR = USART_BUFFLEN; // Swap current buff - if (usart_cur_inbuf == us_inbuf1) - usart_cur_inbuf = us_inbuf2; + if (usart_cur_inbuf == us_in_a) + usart_cur_inbuf = us_in_b; else - usart_cur_inbuf = us_inbuf1; + usart_cur_inbuf = us_in_a; usart_cur_inbuf_off = 0; } else { // Take only what we have room for available = rxfifo_free; for (uint16_t i = 0; i < available; i++) { + us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i]; - if (us_rxfifo_high == sizeof(us_rxfifo)) + + if (us_rxfifo_high == sizeof(us_rxfifo)) { us_rxfifo_high = 0; + } } usart_cur_inbuf_off += available; return; @@ -101,18 +134,20 @@ static void usart_fill_rxfifo(void) { if (pUS1->US_RCR < USART_BUFFLEN - usart_cur_inbuf_off) { // Current buffer partially filled if (us_rxfifo_low > us_rxfifo_high) - rxfifo_free = us_rxfifo_low - us_rxfifo_high; + rxfifo_free = (us_rxfifo_low - us_rxfifo_high); else - rxfifo_free = sizeof(us_rxfifo) - us_rxfifo_high + us_rxfifo_low; + rxfifo_free = (sizeof(us_rxfifo) - us_rxfifo_high + us_rxfifo_low); - uint16_t available = USART_BUFFLEN - pUS1->US_RCR - usart_cur_inbuf_off; + uint16_t available = (USART_BUFFLEN - pUS1->US_RCR - usart_cur_inbuf_off); if (available > rxfifo_free) available = rxfifo_free; + for (uint16_t i = 0; i < available; i++) { us_rxfifo[us_rxfifo_high++] = usart_cur_inbuf[usart_cur_inbuf_off + i]; - if (us_rxfifo_high == sizeof(us_rxfifo)) + if (us_rxfifo_high == sizeof(us_rxfifo)) { us_rxfifo_high = 0; + } } usart_cur_inbuf_off += available; } @@ -121,9 +156,9 @@ static void usart_fill_rxfifo(void) { uint16_t usart_rxdata_available(void) { usart_fill_rxfifo(); if (us_rxfifo_low <= us_rxfifo_high) - return us_rxfifo_high - us_rxfifo_low; + return (us_rxfifo_high - us_rxfifo_low); else - return sizeof(us_rxfifo) - us_rxfifo_low + us_rxfifo_high; + return (sizeof(us_rxfifo) - us_rxfifo_low + us_rxfifo_high); } uint32_t usart_read_ng(uint8_t *data, size_t len) { @@ -143,9 +178,10 @@ uint32_t usart_read_ng(uint8_t *data, size_t len) { uint32_t maxtry = 10 * (3000000 / USART_BAUD_RATE) + tryconstant; while (len) { - uint32_t available = usart_rxdata_available(); + uint32_t available = usart_rxdata_available(); uint32_t packetSize = MIN(available, len); + if (available > 0) { // Dbprintf_usb("Dbg USART ask %d bytes, available %d bytes, packetsize %d bytes", len, available, packetSize); // highest_observed_try = MAX(highest_observed_try, try); @@ -153,8 +189,9 @@ uint32_t usart_read_ng(uint8_t *data, size_t len) { } len -= packetSize; while (packetSize--) { - if (us_rxfifo_low == sizeof(us_rxfifo)) + if (us_rxfifo_low == sizeof(us_rxfifo)) { us_rxfifo_low = 0; + } data[bytes_rcv++] = us_rxfifo[us_rxfifo_low++]; } if (try++ == maxtry) { @@ -183,10 +220,13 @@ int usart_writebuffer_sync(uint8_t *data, size_t len) { void usart_init(uint32_t baudrate, uint8_t parity) { - if (baudrate != 0) + if (baudrate != 0) { g_usart_baudrate = baudrate; - if ((parity == 'N') || (parity == 'O') || (parity == 'E')) + } + + if ((parity == 'N') || (parity == 'O') || (parity == 'E')) { g_usart_parity = parity; + } // For a nice detailed sample, interrupt driven but still relevant. // See https://www.sparkfun.com/datasheets/DevTools/SAM7/at91sam7%20serial%20communications.pdf @@ -262,11 +302,11 @@ void usart_init(uint32_t baudrate, uint8_t parity) { pUS1->US_TCR = 0; pUS1->US_TNPR = (uint32_t)0; pUS1->US_TNCR = 0; - pUS1->US_RPR = (uint32_t)us_inbuf1; + pUS1->US_RPR = (uint32_t)us_in_a; pUS1->US_RCR = USART_BUFFLEN; - usart_cur_inbuf = us_inbuf1; + usart_cur_inbuf = us_in_a; usart_cur_inbuf_off = 0; - pUS1->US_RNPR = (uint32_t)us_inbuf2; + pUS1->US_RNPR = (uint32_t)us_in_b; pUS1->US_RNCR = USART_BUFFLEN; // Initialize our fifo diff --git a/armsrc/util.c b/armsrc/util.c index fe40b6b43..0834e619d 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -268,8 +268,9 @@ int BUTTON_HELD(int ms) { int ticks = (48000 * (ms ? ms : 1000)) >> 10; // If we're not even pressed, forget about it! - if (BUTTON_PRESS() == false) + if (BUTTON_PRESS() == false) { return BUTTON_NO_CLICK; + } // Borrow a PWM unit for my real-time clock AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); @@ -284,12 +285,14 @@ int BUTTON_HELD(int ms) { uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; // As soon as our button let go, we didn't hold long enough - if (BUTTON_PRESS() == false) + if (BUTTON_PRESS() == false) { return BUTTON_SINGLE_CLICK; + } // Have we waited the full second? - else if (now == (uint16_t)(start + ticks)) + else if (now == (uint16_t)(start + ticks)) { return BUTTON_HOLD; + } WDT_HIT(); } @@ -298,6 +301,8 @@ int BUTTON_HELD(int ms) { return BUTTON_ERROR; } +// This function returns false if no data is available or +// the USB connection is invalid. bool data_available(void) { #ifdef WITH_FPC_USART_HOST return usb_poll_validate_length() || (usart_rxdata_available() > 0); @@ -305,3 +310,14 @@ bool data_available(void) { return usb_poll_validate_length(); #endif } + +// This function doesn't check if the USB connection is valid. +// In most of the cases, you should use data_available() unless +// the timing is critical. +bool data_available_fast(void) { +#ifdef WITH_FPC_USART_HOST + return usb_available_length() || (usart_rxdata_available() > 0); +#else + return usb_available_length(); +#endif +} diff --git a/armsrc/util.h b/armsrc/util.h index 65d24f026..da45219a7 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -101,5 +101,6 @@ void SpinUp(uint32_t speed); int BUTTON_CLICKED(int ms); int BUTTON_HELD(int ms); bool data_available(void); +bool data_available_fast(void); #endif diff --git a/bootrom/Makefile b/bootrom/Makefile index cad3e17d1..b6825530d 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -53,10 +53,10 @@ INSTALLFW = $(OBJDIR)/bootrom.elf OBJS = $(OBJDIR)/bootrom.s19 -# version_pm3.c should be remade on every compilation +# version_pm3.c should be checked on every compilation version_pm3.c: default_version_pm3.c .FORCE - $(info [=] GEN $@) - $(Q)$(SH) ../tools/mkversion.sh > $@ || $(PERL) ../tools/mkversion.pl > $@ || $(CP) $< $@ + $(info [=] CHECK $@) + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ all: showinfo $(OBJS) diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c index 4c87a65b0..e92b86e35 100644 --- a/bootrom/bootrom.c +++ b/bootrom/bootrom.c @@ -30,7 +30,7 @@ common_area_t g_common_area __attribute__((section(".commonarea"))); uint32_t start_addr, end_addr; bool bootrom_unlocked; -extern uint32_t _bootrom_start[], _bootrom_end[], _flash_start[], _flash_end[], _osimage_entry[]; +extern uint32_t _bootrom_start[], _bootrom_end[], _flash_start[], _flash_end[], _osimage_entry[], __bss_start__[], __bss_end__[]; static int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len) { PacketResponseOLD txcmd; @@ -212,6 +212,12 @@ static void UsbPacketReceived(uint8_t *packet) { reply_old(CMD_ACK, arg0, 0, 0, 0, 0); } +// delay_loop(1) = 3.07us +static volatile uint32_t c; +static void __attribute__((optimize("O0"))) delay_loop(uint32_t delay) { + for (c = delay * 2; c; c--) {}; +} + static void flash_mode(void) { start_addr = 0; end_addr = 0; @@ -234,7 +240,7 @@ static void flash_mode(void) { usb_enable(); // wait for reset to be complete? - for (volatile size_t i = 0; i < 0x100000; i++) {}; + delay_loop(100000); for (;;) { WDT_HIT(); @@ -246,10 +252,17 @@ static void flash_mode(void) { } } - if (g_common_area.flags.button_pressed && BUTTON_PRESS() == false) { + bool button_state = BUTTON_PRESS(); + // ~10ms, prevent jitter + delay_loop(3333); + if (button_state != BUTTON_PRESS()) { + // in jitter state, ignore + continue; + } + if (g_common_area.flags.button_pressed && button_state == false) { g_common_area.flags.button_pressed = 0; } - if (!g_common_area.flags.button_pressed && BUTTON_PRESS()) { + if (!g_common_area.flags.button_pressed && button_state) { /* Perform a reset to leave flash mode */ g_common_area.flags.button_pressed = 1; usb_disable(); @@ -262,6 +275,10 @@ static void flash_mode(void) { void BootROM(void); void BootROM(void) { + /* Set up (that is: clear) BSS. */ + uint32_t *bss_dst = __bss_start__; + while (bss_dst < __bss_end__) *bss_dst++ = 0; + //------------ // First set up all the I/O pins; GPIOs configured directly, other ones // just need to be assigned to the appropriate peripheral. diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 3fd255997..88bbb9ead 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -96,11 +96,13 @@ if (CMAKE_TOOLCHAIN_FILE) endif (ANDROID) set(EMBED_READLINE ON) set(EMBED_BZIP2 ON) + set(EMBED_LZ4 ON) + set(EMBED_GD ON) endif (CMAKE_TOOLCHAIN_FILE) -if (EMBED_READLINE OR EMBED_BZIP2) +if (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4 OR EMBED_GD) include(ExternalProject) -endif (EMBED_READLINE OR EMBED_BZIP2) +endif (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4 OR EMBED_GD) if (NOT SKIPREADLINE EQUAL 1) if (APPLE) @@ -162,6 +164,7 @@ if (NOT SKIPJANSSONSYSTEM EQUAL 1) endif (NOT SKIPJANSSONSYSTEM EQUAL 1) if(EMBED_BZIP2) + cmake_policy(SET CMP0114 NEW) set(BZIP2_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/bzip2/src/bzip2) # Specify SOURCE_DIR will cause some errors ExternalProject_Add(bzip2 @@ -183,6 +186,55 @@ else(EMBED_BZIP2) find_package (BZip2 REQUIRED) endif(EMBED_BZIP2) +if(EMBED_LZ4) + cmake_policy(SET CMP0114 NEW) + set(LZ4_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/lz4/src/lz4) + # Specify SOURCE_DIR will cause some errors + ExternalProject_Add(lz4 + GIT_REPOSITORY https://android.googlesource.com/platform/external/lz4 + GIT_TAG platform-tools-30.0.2 + PREFIX deps/lz4 + # SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/lz4 + CONFIGURE_COMMAND mkdir -p ${LZ4_BUILD_DIR} && git archive --format tar HEAD | tar -C ${LZ4_BUILD_DIR} -x + BUILD_IN_SOURCE ON + BUILD_COMMAND make -C ${LZ4_BUILD_DIR}/lib -j4 CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} LD=${CMAKE_C_COMPILER} AR=${CMAKE_AR} RANLIB=${CMAKE_RANLIB} ${CFLAGS_EXTERNAL_LIB} liblz4.a + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + ) + ExternalProject_Add_StepTargets(lz4 configure build install) + set(LZ4_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/deps/lz4/src/lz4/lib) + set(LZ4_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/deps/lz4/src/lz4/lib/liblz4.a) + set(LZ4_FOUND ON) +else(EMBED_LZ4) + find_path(LZ4_INCLUDE_DIRS lz4frame.h) + find_library(LZ4_LIBRARIES lz4) +endif(EMBED_LZ4) + +if (LZ4_INCLUDE_DIRS AND LZ4_LIBRARIES) + set(LZ4_FOUND ON) +endif (LZ4_INCLUDE_DIRS AND LZ4_LIBRARIES) + +if (NOT SKIPGD EQUAL 1) + if (EMBED_GD) + cmake_policy(SET CMP0114 NEW) + set(GD_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/gd) + # Specify SOURCE_DIR will cause some errors + ExternalProject_Add(gd + URL https://github.com/libgd/libgd/releases/download/gd-2.3.3/libgd-2.3.3.tar.gz + URL_HASH SHA256=dd3f1f0bb016edcc0b2d082e8229c822ad1d02223511997c80461481759b1ed2 + PREFIX deps/gd + CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DENABLE_CPP=0 -DCMAKE_INSTALL_PREFIX=. + PATCH_COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/deps/gd-static.patch | patch -p1 + ) + ExternalProject_Add_StepTargets(gd configure build install) + set(GD_INCLUDE_DIRS ${GD_BUILD_DIR}/src/gd-build/include) + set(GD_LIBRARIES ${GD_BUILD_DIR}/src/gd-build/lib/libgd.a) + set(GD_FOUND ON) + else (EMBED_GD) + pkg_search_module(GD QUIET gdlib) + endif (EMBED_GD) +endif (NOT SKIPGD EQUAL 1) + if (NOT SKIPWHEREAMISYSTEM EQUAL 1) find_path(WHEREAMI_INCLUDE_DIRS whereami.h) find_library(WHEREAMI_LIBRARIES whereami) @@ -249,6 +301,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/mifare/mifare4.c ${PM3_ROOT}/client/src/mifare/mifaredefault.c ${PM3_ROOT}/client/src/mifare/mifarehost.c + ${PM3_ROOT}/client/src/mifare/gen4.c ${PM3_ROOT}/client/src/nfc/ndef.c ${PM3_ROOT}/client/src/mifare/lrpcrypto.c ${PM3_ROOT}/client/src/mifare/desfirecrypto.c @@ -256,6 +309,8 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/mifare/desfirecore.c ${PM3_ROOT}/client/src/mifare/desfiretest.c ${PM3_ROOT}/client/src/mifare/gallaghercore.c + ${PM3_ROOT}/client/src/uart/ringbuffer.c + ${PM3_ROOT}/client/src/uart/uart_common.c ${PM3_ROOT}/client/src/uart/uart_posix.c ${PM3_ROOT}/client/src/uart/uart_win32.c ${PM3_ROOT}/client/src/ui/overlays.ui @@ -298,7 +353,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdhftexkom.c ${PM3_ROOT}/client/src/cmdhfthinfilm.c ${PM3_ROOT}/client/src/cmdhftopaz.c - ${PM3_ROOT}/client/src/cmdhfwaveshare.c + ${PM3_ROOT}/client/src/cmdhfvas.c ${PM3_ROOT}/client/src/cmdhfxerox.c ${PM3_ROOT}/client/src/cmdhw.c ${PM3_ROOT}/client/src/cmdlf.c @@ -348,6 +403,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/fileutils.c ${PM3_ROOT}/client/src/flash.c ${PM3_ROOT}/client/src/graph.c + ${PM3_ROOT}/client/src/iso4217.c ${PM3_ROOT}/client/src/jansson_path.c ${PM3_ROOT}/client/src/preferences.c ${PM3_ROOT}/client/src/pm3.c @@ -365,7 +421,7 @@ set (TARGET_SOURCES add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/version_pm3.c - COMMAND sh ${PM3_ROOT}/tools/mkversion.sh > ${CMAKE_BINARY_DIR}/version_pm3.c || ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c + COMMAND sh ${PM3_ROOT}/tools/mkversion.sh ${CMAKE_BINARY_DIR}/version_pm3.c || ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c DEPENDS ${PM3_ROOT}/common/default_version_pm3.c ) @@ -380,7 +436,23 @@ message(STATUS "CMAKE_SYSTEM_PROCESSOR := ${CMAKE_SYSTEM_PROCESSOR}") if (APPLE) message(STATUS "Apple device detected.") set(ADDITIONAL_SRC ${PM3_ROOT}/client/src/util_darwin.h ${PM3_ROOT}/client/src/util_darwin.m ${ADDITIONAL_SRC}) - set(ADDITIONAL_LNK "-framework Foundation" "-framework AppKit") + + find_library(UIKIT_LIBRARY UIKit) + if (NOT UIKIT_LIBRARY) + message(STATUS "UIKit.framework NOT found!") + else() + message(STATUS "UIKit.framework found! ${UIKIT_LIBRARY}") + set(ADDITIONAL_LNK "-framework Foundation" "-framework UIKit") + endif() + + find_library(APPKIT_LIBRARY AppKit) + if (NOT APPKIT_LIBRARY) + message(STATUS "AppKit.framework NOT found!") + else() + message(STATUS "AppKit.framework found! ${APPKIT_LIBRARY}") + set(ADDITIONAL_LNK "-framework Foundation" "-framework AppKit") + endif() + endif (APPLE) if ((NOT SKIPQT EQUAL 1) AND (Qt5_FOUND)) @@ -439,6 +511,22 @@ if (BZIP2_FOUND) set(ADDITIONAL_LNK ${BZIP2_LIBRARIES} ${ADDITIONAL_LNK}) endif (BZIP2_FOUND) +if (LZ4_FOUND) + set(ADDITIONAL_DIRS ${LZ4_INCLUDE_DIRS} ${ADDITIONAL_DIRS}) + set(ADDITIONAL_LNK ${LZ4_LIBRARIES} ${ADDITIONAL_LNK}) +endif (LZ4_FOUND) + +if (NOT SKIPGD EQUAL 1 AND GD_FOUND) + set(ADDITIONAL_DIRS ${GD_INCLUDE_DIRS} ${ADDITIONAL_DIRS}) + set(ADDITIONAL_LNK ${GD_LIBRARIES} ${ADDITIONAL_LNK}) + set(ADDITIONAL_LNKDIRS ${GD_LIBRARY_DIRS} ${ADDITIONAL_LNKDIRS}) + set(TARGET_SOURCES + ${PM3_ROOT}/client/src/imgutils.c + ${PM3_ROOT}/client/src/cmdhfwaveshare.c + ${TARGET_SOURCES}) + add_definitions("-DHAVE_GD") +endif (NOT SKIPGD EQUAL 1 AND GD_FOUND) + if (WHEREAMI_FOUND) set(ADDITIONAL_DIRS ${WHEREAMI_INCLUDE_DIRS} ${ADDITIONAL_DIRS}) set(ADDITIONAL_LNK ${WHEREAMI_LIBRARIES} ${ADDITIONAL_LNK}) @@ -471,11 +559,37 @@ else (SKIPBT EQUAL 1) endif (BLUEZ_FOUND) endif(SKIPBT EQUAL 1) -if (EMBED_BZIP2) - message(STATUS "Bzip2 library: embedded") -else (EMBED_BZIP2) - message(STATUS "Bzip2 library: system library found") -endif (EMBED_BZIP2) +if (BZIP2_FOUND) + if (EMBED_BZIP2) + message(STATUS "Bzip2 library: embedded") + else (EMBED_BZIP2) + message(STATUS "Bzip2 library: system library found") + endif (EMBED_BZIP2) +else (BZIP2_FOUND) + message(SEND_ERROR "Bzip2 library: Bzip2 not found") +endif (BZIP2_FOUND) + +if (LZ4_FOUND) + if (EMBED_LZ4) + message(STATUS "LZ4 library: embedded") + else (EMBED_LZ4) + message(STATUS "LZ4 library: system library found") + endif (EMBED_LZ4) +else (LZ4_FOUND) + message(SEND_ERROR "LZ4 library: LZ4 not found") +endif (LZ4_FOUND) + +if (SKIPGD EQUAL 1) + message(STATUS "GD library: skipped") +elseif (GD_FOUND) + if (EMBED_GD) + message(STATUS "GD library: embedded") + else (EMBED_GD) + message(STATUS "GD library: system library found") + endif (EMBED_GD) +else (SKIPGD EQUAL 1) + message(STATUS "GD library: GD not found, disabled") +endif (SKIPGD EQUAL 1) if (SKIPJANSSONSYSTEM EQUAL 1) message(STATUS "Jansson library: local library forced") @@ -575,6 +689,9 @@ endif (EMBED_READLINE) if (EMBED_BZIP2) add_dependencies(proxmark3 bzip2) endif (EMBED_BZIP2) +if (EMBED_LZ4) + add_dependencies(proxmark3 lz4) +endif (EMBED_LZ4) if (MINGW) # Mingw uses by default Microsoft printf, we want the GNU printf (e.g. for %z) @@ -586,21 +703,21 @@ if (MINGW) set(CMAKE_C_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_CXX_FLAGS}") - # GCC 10 has issues with false positives on stringop-overflow, - # let's disable them for now (cf https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92955, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94335) - # beware these flags didn't exist for GCC < 7 - if(CMAKE_COMPILER_IS_GNUCXX) - execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) - if (GCC_VERSION VERSION_GREATER 10.0 OR GCC_VERSION VERSION_EQUAL 10.0) - set(CMAKE_C_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_CXX_FLAGS}") - endif() - endif(CMAKE_COMPILER_IS_GNUCXX) - # link Winsock2 set(ADDITIONAL_LNK ws2_32 ${ADDITIONAL_LNK}) endif (MINGW) +# GCC 10 has issues with false positives on stringop-overflow, +# let's disable them for now (cf https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92955, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94335) +# beware these flags didn't exist for GCC < 7 +if(CMAKE_COMPILER_IS_GNUCXX) + execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + if (GCC_VERSION VERSION_GREATER 10.0 OR GCC_VERSION VERSION_EQUAL 10.0) + set(CMAKE_C_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_CXX_FLAGS}") + endif() +endif(CMAKE_COMPILER_IS_GNUCXX) + target_include_directories(proxmark3 PRIVATE ${PM3_ROOT}/common ${PM3_ROOT}/common_fpga @@ -648,14 +765,14 @@ endif (NOT SKIPPTHREAD EQUAL 1) if (NOT SKIPPYTHON EQUAL 1) # OSX have a hard time compiling python3 dependency with older cmake. if (PYTHON3EMBED_FOUND OR PYTHON3_FOUND) - if (NOT CMAKE_VERSION VERSION_LESS 3.13) - target_link_directories(proxmark3 PRIVATE ${ADDITIONAL_LNKDIRS}) - elseif (APPLE) - message( SEND_ERROR "Your CMAKE version is too old for Apple platform, please update to a version >=3.13" ) - endif (NOT CMAKE_VERSION VERSION_LESS 3.13) + if (CMAKE_VERSION VERSION_LESS 3.13) + message( SEND_ERROR "Your CMAKE version is too old for Apple platform, please update to a version >=3.13" ) + endif (CMAKE_VERSION VERSION_LESS 3.13) endif (PYTHON3EMBED_FOUND OR PYTHON3_FOUND) endif (NOT SKIPPYTHON EQUAL 1) +target_link_directories(proxmark3 PRIVATE ${ADDITIONAL_LNKDIRS}) + install(TARGETS proxmark3 DESTINATION "bin") install(DIRECTORY cmdscripts lualibs luascripts pyscripts resources dictionaries DESTINATION "share/proxmark3") diff --git a/client/Makefile b/client/Makefile index d0cee355e..adbd2c0ce 100644 --- a/client/Makefile +++ b/client/Makefile @@ -40,7 +40,7 @@ ifeq ($(USE_BREW),1) PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/lib/pkgconfig:$(BREW_PREFIX)/opt/qt/lib/pkgconfig:$(BREW_PREFIX)/opt/qt5/lib/pkgconfig endif -ifdef ($(USE_MACPORTS),1) +ifeq ($(USE_MACPORTS),1) INCLUDES += -I$(MACPORTS_PREFIX)/include LDLIBS += -L$(MACPORTS_PREFIX)/lib PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(MACPORTS_PREFIX)/lib/pkgconfig @@ -242,6 +242,9 @@ endif ## BZIP2 LDLIBS += -lbz2 +## LZ4 +LDLIBS += -llz4 + ## Bluez (optional) ifneq ($(SKIPBT),1) BTINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags bluez 2>/dev/null) @@ -270,7 +273,7 @@ ifneq ($(SKIPPYTHON),1) PYTHONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs python3 2>/dev/null) ifneq ($(PYTHONLDLIBS),) PYTHONLIBLD = $(PYTHONLDLIBS) - PYTHONLIBINC = $(PYTHONINCLUDES) + PYTHONLIBINC = $(subst -I,-isystem ,$(PYTHONINCLUDES)) PYTHON_FOUND = 1 else # since python3.8, applications willing to embed python must use -embed: @@ -278,7 +281,7 @@ ifneq ($(SKIPPYTHON),1) PYTHONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs python3-embed 2>/dev/null) ifneq ($(PYTHONLDLIBS),) PYTHONLIBLD = $(PYTHONLDLIBS) - PYTHONLIBINC = $(PYTHONINCLUDES) + PYTHONLIBINC = $(subst -I,-isystem ,$(PYTHONINCLUDES)) PYTHON_FOUND = 1 endif endif @@ -329,6 +332,17 @@ endif LDLIBS += $(QTLDLIBS) CXXINCLUDES += $(QTINCLUDES) +## GD (optional) +ifneq ($(SKIPGD),1) + GDINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags gdlib 2>/dev/null) + GDLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs gdlib 2>/dev/null) + ifneq ($(GDLDLIBS),) + LDLIBS += $(GDLDLIBS) + PM3INCLUDES += $(GDINCLUDES) + GD_FOUND = 1 + endif +endif + ## Readline ifneq ($(SKIPREADLINE),1) ifeq ($(USE_BREW),1) @@ -368,12 +382,16 @@ ifeq ($(PYTHON_FOUND),1) endif ####################################################################################################### +# macOS doesn't like this params +#MYCFLAGS += --param max-completely-peeled-insns=1000 --param max-completely-peel-times=10000 +MYCFLAGS += -O3 + CFLAGS ?= $(DEFCFLAGS) CFLAGS += $(MYDEFS) $(MYCFLAGS) $(MYINCLUDES) # We cannot just use CFLAGS+=... because it has impact on sub-makes if CFLAGS is defined in env: PM3CFLAGS = $(CFLAGS) -PM3CFLAGS += -g -I./src -I./include -I../include -I../common -I../common_fpga $(PM3INCLUDES) $(INCLUDES) +PM3CFLAGS += -I./src -I./include -I../include -I../common -I../common_fpga $(PM3INCLUDES) $(INCLUDES) # WIP Testing #PM3CFLAGS += -std=c11 -pedantic @@ -403,6 +421,10 @@ ifeq ($(PYTHON_FOUND),1) PM3CFLAGS += -DHAVE_PYTHON endif +ifeq ($(GD_FOUND),1) + PM3CFLAGS += -DHAVE_GD +endif + ifeq ($(SWIG_LUA_FOUND),1) PM3CFLAGS += -DHAVE_LUA_SWIG endif @@ -412,7 +434,7 @@ endif PM3CFLAGS += -DHAVE_SNPRINTF -CXXFLAGS ?= -Wall -Werror -O3 +CXXFLAGS ?= -Wall -Werror CXXFLAGS += $(MYDEFS) $(MYCXXFLAGS) $(MYINCLUDES) PM3CXXFLAGS = $(CXXFLAGS) @@ -434,7 +456,13 @@ LDFLAGS += $(MYLDFLAGS) PM3LDFLAGS = $(LDFLAGS) ifeq ($(platform),Darwin) - PM3LDFLAGS += -framework Foundation -framework AppKit + ifeq ($(shell uname -p),arm64) + # The platform is iOS + PM3LDFLAGS += -framework Foundation -framework UIKit + else + # M* macOS devices return arm + PM3LDFLAGS += -framework Foundation -framework AppKit + endif endif ################### @@ -497,6 +525,16 @@ else endif endif +ifeq ($(SKIPGD),1) + $(info GD library: skipped) +else + ifeq ($(GD_FOUND),1) + $(info GD library: GD v$(shell $(PKG_CONFIG_ENV) pkg-config --modversion gdlib) found, enabled) + else + $(info GD library: GD not found, disabled) + endif +endif + ifeq ($(SKIPREADLINE),1) $(info Readline library: skipped) else @@ -591,7 +629,7 @@ SRCS = mifare/aiddesfire.c \ cmdhfthinfilm.c \ cmdhftopaz.c \ cmdhftexkom.c \ - cmdhfwaveshare.c \ + cmdhfvas.c \ cmdhfxerox.c \ cmdhw.c \ cmdlf.c \ @@ -671,6 +709,7 @@ SRCS = mifare/aiddesfire.c \ generator.c \ graph.c \ jansson_path.c \ + iso4217.c \ iso7816/apduinfo.c \ iso7816/iso7816core.c \ loclass/cipher.c \ @@ -688,6 +727,7 @@ SRCS = mifare/aiddesfire.c \ mifare/mifare4.c \ mifare/mifaredefault.c \ mifare/mifarehost.c \ + mifare/gen4.c \ nfc/ndef.c \ pm3.c \ pm3_binlib.c \ @@ -696,6 +736,8 @@ SRCS = mifare/aiddesfire.c \ pm3line.c \ proxmark3.c \ scandir.c \ + uart/ringbuffer.c \ + uart/uart_common.c \ uart/uart_posix.c \ uart/uart_win32.c \ scripting.c \ @@ -720,6 +762,12 @@ SRCS += bucketsort.c \ lfdemod.c \ util_posix.c +ifeq ($(GD_FOUND),1) + # electronic shelf labels + SRCS += imgutils.c \ + cmdhfwaveshare.c +endif + # swig SWIGSRCS = @@ -770,10 +818,14 @@ proxmark3: $(OBJS) $(STATICLIBS) lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lu src/proxgui.cpp: src/ui/ui_overlays.h src/ui/ui_image.h +src/proxguiqt.cpp: src/proxguiqt.h + src/proxguiqt.moc.cpp: src/proxguiqt.h $(info [-] MOC $@) $(Q)$(MOC) -o$@ $^ +src/proxguiqt.h: src/ui/ui_overlays.h src/ui/ui_image.h + src/ui/ui_overlays.h: src/ui/overlays.ui $(info [-] UIC $@) $(Q)$(UIC) $^ > $@ @@ -816,7 +868,6 @@ endif ifneq (,$(INSTALLSHARE)) $(Q)$(INSTALLSUDO) $(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) $(Q)$(INSTALLSUDO) $(CP) $(INSTALLSHARE) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) - $(Q)$(INSTALLSUDO) $(CP) src/pm3.py $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)/pyscripts endif @true @@ -903,10 +954,10 @@ src/pm3_pywrap.c: pm3.i .PHONY: all clean install uninstall tarbin .FORCE -# version_pm3.c should be remade on every compilation +# version_pm3.c should be checked on every compilation src/version_pm3.c: default_version_pm3.c .FORCE - $(info [=] GEN $@) - $(Q)$(SH) ../tools/mkversion.sh > $@ || $(CP) $< $@ + $(info [=] CHECK $@) + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ # easy printing of MAKE VARIABLES print-%: ; @echo $* = $($*) diff --git a/client/atr_scrap_pcsctools.py b/client/atr_scrap_pcsctools.py index 9c4d1b4be..1f0ab3948 100755 --- a/client/atr_scrap_pcsctools.py +++ b/client/atr_scrap_pcsctools.py @@ -59,6 +59,7 @@ const char *getAtrInfo(const char *atr_str); // atr_t array is expected to be NULL terminated const static atr_t AtrTable[] = { { "3BDF18FFC080B1FE751F033078464646462026204963656D616E1D", "Cardhelper by 0xFFFF and Iceman" }, + { "3B90969181B1FE551FC7D4", "IClass SE Processor (Other) https://www.hidglobal.com/products/embedded-modules/iclass-se/sio-processor"}, """ C_FOOTER=""" {NULL, "N/A"} diff --git a/client/deps/amiitool/amiitool.c b/client/deps/amiitool/amiitool.c index 61f4abd81..aa51b44d5 100644 --- a/client/deps/amiitool/amiitool.c +++ b/client/deps/amiitool/amiitool.c @@ -43,7 +43,7 @@ int main(int argc, char **argv) { char op = '\0'; bool lenient = false; - char c; + int c; while ((c = getopt(argc, argv, "edci:s:o:k:l")) != -1) { switch (c) { case 'e': diff --git a/client/deps/cliparser/cliparser.c b/client/deps/cliparser/cliparser.c index e5e8f946c..0ea0bd33b 100644 --- a/client/deps/cliparser/cliparser.c +++ b/client/deps/cliparser/cliparser.c @@ -147,6 +147,7 @@ enum ParserState { PS_FIRST, PS_ARGUMENT, PS_OPTION, + PS_QUOTE, }; #define isSpace(c)(c == ' ' || c == '\t') @@ -195,6 +196,10 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab case PS_ARGUMENT: if (state == PS_FIRST) state = PS_ARGUMENT; + if (str[i] == '"') { + state = PS_QUOTE; + break; + } if (isSpace(str[i])) { spaceptr = bufptr; state = PS_FIRST; @@ -215,6 +220,16 @@ int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtab *bufptr = str[i]; bufptr++; break; + case PS_QUOTE: + if (str[i] == '"') { + *bufptr++ = 0x00; + state = PS_FIRST; + } else { + if (isSpace(str[i]) == false) { + *bufptr++ = str[i]; + } + } + break; } if (bufptr > bufptrend) { PrintAndLogEx(ERR, "ERROR: Line too long\n"); diff --git a/client/deps/gd-static.patch b/client/deps/gd-static.patch new file mode 100644 index 000000000..c8447c845 --- /dev/null +++ b/client/deps/gd-static.patch @@ -0,0 +1,11 @@ +--- a/src/gd.h 2024-01-07 16:51:43.749223000 +0100 ++++ b/src/gd.h 2024-01-07 16:52:34.162291600 +0100 +@@ -45,7 +45,7 @@ + the gd sources in a project. */ + + /* http://gcc.gnu.org/wiki/Visibility */ +-#if defined(_WIN32) || defined(CYGWIN) || defined(_WIN32_WCE) ++#if 0 // Disable DLL annotations when building statically. Needed for embedding under MinGW. + # ifdef BGDWIN32 + # ifdef NONDLL + # define BGD_EXPORT_DATA_PROT diff --git a/client/deps/hardnested/Makefile b/client/deps/hardnested/Makefile index 1667e036f..badace7e5 100644 --- a/client/deps/hardnested/Makefile +++ b/client/deps/hardnested/Makefile @@ -22,6 +22,9 @@ endif ifneq ($(findstring aarch64, $(cpu_arch)), ) IS_SIMD_ARCH=arm64 endif +ifneq ($(findstring iP, $(cpu_arch)), ) + IS_SIMD_ARCH=arm64 +endif ifneq ($(IS_SIMD_ARCH), ) MULTIARCHSRCS = hardnested_bf_core.c hardnested_bitarray_core.c diff --git a/client/deps/hardnested/hardnested_bf_core.c b/client/deps/hardnested/hardnested_bf_core.c index 63eba8eba..a84ce0df9 100644 --- a/client/deps/hardnested/hardnested_bf_core.c +++ b/client/deps/hardnested/hardnested_bf_core.c @@ -371,7 +371,7 @@ uint64_t CRACK_STATES_BITSLICED(uint32_t cuid, uint8_t *best_first_bytes, statel for (uint32_t tests = 0; tests < nonces_to_bruteforce; ++tests) { // common bits with preceding test nonce uint32_t common_bits = next_common_bits; //tests ? trailing_zeros(bf_test_nonce_2nd_byte[tests] ^ bf_test_nonce_2nd_byte[tests-1]) : 0; - next_common_bits = tests < nonces_to_bruteforce - 1 ? trailing_zeros(bf_test_nonce_2nd_byte[tests] ^ bf_test_nonce_2nd_byte[tests + 1]) : 0; + next_common_bits = (tests < nonces_to_bruteforce - 1) ? trailing_zeros(bf_test_nonce_2nd_byte[tests] ^ bf_test_nonce_2nd_byte[tests + 1]) : 0; uint32_t parity_bit_idx = 1; // start checking with the parity of second nonce byte bitslice_value_t fb_bits = fbb[common_bits]; // start with precomputed feedback bits from previous nonce bitslice_value_t ks_bits = ksb[common_bits]; // dito for first keystream bits diff --git a/client/deps/hardnested/hardnested_bruteforce.c b/client/deps/hardnested/hardnested_bruteforce.c index 7766b28d3..f55d8885a 100644 --- a/client/deps/hardnested/hardnested_bruteforce.c +++ b/client/deps/hardnested/hardnested_bruteforce.c @@ -78,6 +78,8 @@ THE SOFTWARE. #define DEBUG_KEY_ELIMINATION 1 // #define DEBUG_BRUTE_FORCE +#define MIN_BUCKETS_SIZE 128 + typedef enum { EVEN_STATE = 0, ODD_STATE = 1 @@ -88,12 +90,13 @@ static uint32_t bf_test_nonce[256]; static uint8_t bf_test_nonce_2nd_byte[256]; static uint8_t bf_test_nonce_par[256]; static uint32_t bucket_count = 0; -static statelist_t *buckets[128]; +static size_t buckets_allocated = 0; +static statelist_t **buckets = NULL; static uint32_t keys_found = 0; static uint64_t num_keys_tested; static uint64_t found_bs_key = 0; -inline uint8_t trailing_zeros(uint8_t byte) { +uint8_t trailing_zeros(uint8_t byte) { static const uint8_t trailing_zeros_LUT[256] = { 8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, @@ -157,6 +160,7 @@ crack_states_thread(void *x) { uint8_t *best_first_bytes; } *thread_arg; + const int num_brute_force_threads = NUM_BRUTE_FORCE_THREADS; thread_arg = (struct arg *)x; const int thread_id = thread_arg->thread_ID; uint32_t current_bucket = thread_id; @@ -188,7 +192,7 @@ crack_states_thread(void *x) { } } } - current_bucket += NUM_BRUTE_FORCE_THREADS; + current_bucket += num_brute_force_threads; } return NULL; } @@ -294,12 +298,35 @@ static void write_benchfile(statelist_t *candidates) { #endif +static bool ensure_buckets_alloc(size_t need_buckets) { + if (need_buckets > buckets_allocated) { + size_t alloc_sz = ((buckets_allocated == 0) ? MIN_BUCKETS_SIZE : (buckets_allocated * 2)); + while (need_buckets > alloc_sz) { + alloc_sz *= 2; + } + statelist_t **new_buckets = realloc(buckets, sizeof(statelist_t *) * alloc_sz); + if (new_buckets == NULL) { + free(buckets); + buckets_allocated = 0; + return false; + } + buckets = new_buckets; + memset(buckets + buckets_allocated, 0, (alloc_sz - buckets_allocated) * sizeof(statelist_t *)); + buckets_allocated = alloc_sz; + } + + return true; +} + + bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint32_t num_acquired_nonces, uint64_t maximum_states, noncelist_t *nonces, uint8_t *best_first_bytes, uint64_t *found_key) { #if defined (WRITE_BENCH_FILE) write_benchfile(candidates); #endif bool silent = (bf_rate != NULL); + const int num_brute_force_threads = NUM_BRUTE_FORCE_THREADS; + keys_found = 0; num_keys_tested = 0; found_bs_key = 0; @@ -310,6 +337,11 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint bucket_count = 0; for (statelist_t *p = candidates; p != NULL; p = p->next) { if (p->states[ODD_STATE] != NULL && p->states[EVEN_STATE] != NULL) { + if (!ensure_buckets_alloc(bucket_count + 1)) { + PrintAndLogEx(ERR, "Can't allocate buckets, abort!"); + return false; + } + buckets[bucket_count] = p; bucket_count++; } @@ -322,7 +354,7 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint return false; #endif - pthread_t threads[NUM_BRUTE_FORCE_THREADS]; + pthread_t threads[num_brute_force_threads]; struct args { bool silent; int thread_ID; @@ -331,9 +363,9 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint uint64_t maximum_states; noncelist_t *nonces; uint8_t *best_first_bytes; - } thread_args[NUM_BRUTE_FORCE_THREADS]; + } thread_args[num_brute_force_threads]; - for (uint32_t i = 0; i < NUM_BRUTE_FORCE_THREADS; i++) { + for (uint32_t i = 0; i < num_brute_force_threads; i++) { thread_args[i].thread_ID = i; thread_args[i].silent = silent; thread_args[i].cuid = cuid; @@ -343,10 +375,14 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint thread_args[i].best_first_bytes = best_first_bytes; pthread_create(&threads[i], NULL, crack_states_thread, (void *)&thread_args[i]); } - for (uint32_t i = 0; i < NUM_BRUTE_FORCE_THREADS; i++) { + for (uint32_t i = 0; i < num_brute_force_threads; i++) { pthread_join(threads[i], 0); } + free(buckets); + buckets = NULL; + buckets_allocated = 0; + uint64_t elapsed_time = msclock() - start_time; if (bf_rate != NULL) @@ -377,11 +413,14 @@ static bool read_bench_data(statelist_t *test_candidates) { return false; } free(path); - bytes_read = fread(&nonces_to_bruteforce, 1, sizeof(nonces_to_bruteforce), benchfile); - if (bytes_read != sizeof(nonces_to_bruteforce)) { + + // read 4 bytes of data ? + bytes_read = fread(&nonces_to_bruteforce, 1, sizeof(uint32_t), benchfile); + if (bytes_read != sizeof(uint32_t) || (nonces_to_bruteforce >= 256)) { fclose(benchfile); return false; } + for (uint32_t i = 0; i < nonces_to_bruteforce && i < 256; i++) { bytes_read = fread(&bf_test_nonce[i], 1, sizeof(uint32_t), benchfile); if (bytes_read != sizeof(uint32_t)) { @@ -395,11 +434,13 @@ static bool read_bench_data(statelist_t *test_candidates) { return false; } } + bytes_read = fread(&num_states, 1, sizeof(uint32_t), benchfile); if (bytes_read != sizeof(uint32_t)) { fclose(benchfile); return false; } + for (states_read = 0; states_read < MIN(num_states, TEST_BENCH_SIZE); states_read++) { bytes_read = fread(test_candidates->states[EVEN_STATE] + states_read, 1, sizeof(uint32_t), benchfile); if (bytes_read != sizeof(uint32_t)) { @@ -407,9 +448,11 @@ static bool read_bench_data(statelist_t *test_candidates) { return false; } } + for (uint32_t i = states_read; i < TEST_BENCH_SIZE; i++) { test_candidates->states[EVEN_STATE][i] = test_candidates->states[EVEN_STATE][i - states_read]; } + for (uint32_t i = states_read; i < num_states; i++) { bytes_read = fread(&temp, 1, sizeof(uint32_t), benchfile); if (bytes_read != sizeof(uint32_t)) { @@ -417,6 +460,7 @@ static bool read_bench_data(statelist_t *test_candidates) { return false; } } + for (states_read = 0; states_read < MIN(num_states, TEST_BENCH_SIZE); states_read++) { bytes_read = fread(test_candidates->states[ODD_STATE] + states_read, 1, sizeof(uint32_t), benchfile); if (bytes_read != sizeof(uint32_t)) { @@ -424,6 +468,7 @@ static bool read_bench_data(statelist_t *test_candidates) { return false; } } + for (uint32_t i = states_read; i < TEST_BENCH_SIZE; i++) { test_candidates->states[ODD_STATE][i] = test_candidates->states[ODD_STATE][i - states_read]; } @@ -434,30 +479,31 @@ static bool read_bench_data(statelist_t *test_candidates) { float brute_force_benchmark(void) { - statelist_t test_candidates[NUM_BRUTE_FORCE_THREADS]; + const int num_brute_force_threads = NUM_BRUTE_FORCE_THREADS; + statelist_t test_candidates[num_brute_force_threads]; test_candidates[0].states[ODD_STATE] = calloc(1, (TEST_BENCH_SIZE + 1) * sizeof(uint32_t)); test_candidates[0].states[EVEN_STATE] = calloc(1, (TEST_BENCH_SIZE + 1) * sizeof(uint32_t)); - for (uint32_t i = 0; i < NUM_BRUTE_FORCE_THREADS - 1; i++) { + for (uint32_t i = 0; i < num_brute_force_threads - 1; i++) { test_candidates[i].next = test_candidates + i + 1; test_candidates[i + 1].states[ODD_STATE] = test_candidates[0].states[ODD_STATE]; test_candidates[i + 1].states[EVEN_STATE] = test_candidates[0].states[EVEN_STATE]; } - test_candidates[NUM_BRUTE_FORCE_THREADS - 1].next = NULL; + test_candidates[num_brute_force_threads - 1].next = NULL; if (!read_bench_data(test_candidates)) { PrintAndLogEx(NORMAL, "Couldn't read benchmark data. Assuming brute force rate of %1.0f states per second", DEFAULT_BRUTE_FORCE_RATE); return DEFAULT_BRUTE_FORCE_RATE; } - for (uint32_t i = 0; i < NUM_BRUTE_FORCE_THREADS; i++) { + for (uint32_t i = 0; i < num_brute_force_threads; i++) { test_candidates[i].len[ODD_STATE] = TEST_BENCH_SIZE; test_candidates[i].len[EVEN_STATE] = TEST_BENCH_SIZE; test_candidates[i].states[ODD_STATE][TEST_BENCH_SIZE] = -1; test_candidates[i].states[EVEN_STATE][TEST_BENCH_SIZE] = -1; } - uint64_t maximum_states = TEST_BENCH_SIZE * TEST_BENCH_SIZE * (uint64_t)NUM_BRUTE_FORCE_THREADS; + uint64_t maximum_states = TEST_BENCH_SIZE * TEST_BENCH_SIZE * (uint64_t)num_brute_force_threads; float bf_rate; uint64_t found_key = 0; diff --git a/client/deps/liblua/luaconf.h b/client/deps/liblua/luaconf.h index 77445383f..d0b657361 100644 --- a/client/deps/liblua/luaconf.h +++ b/client/deps/liblua/luaconf.h @@ -10,9 +10,6 @@ #if defined(__APPLE__) #include "TargetConditionals.h" -#if TARGET_OS_IOS || TARGET_OS_WATCH || TARGET_OS_TV -#define system(s) ((s)==NULL ? 0 : -1) -#endif // end iOS #endif #include diff --git a/client/deps/mbedtls.cmake b/client/deps/mbedtls.cmake index 40929e1ea..c1ab8d880 100644 --- a/client/deps/mbedtls.cmake +++ b/client/deps/mbedtls.cmake @@ -9,6 +9,9 @@ add_library(pm3rrg_rdv4_mbedtls STATIC ../../common/mbedtls/entropy.c ../../common/mbedtls/error.c ../../common/mbedtls/ecp.c + ../../common/mbedtls/ecdh.c + ../../common/mbedtls/ecc_point_compression.c + ../../common/mbedtls/gcm.c ../../common/mbedtls/ecp_curves.c ../../common/mbedtls/certs.c ../../common/mbedtls/camellia.c @@ -40,6 +43,7 @@ add_library(pm3rrg_rdv4_mbedtls STATIC ../../common/mbedtls/x509.c ../../common/mbedtls/x509_crl.c ../../common/mbedtls/x509_crt.c + ../../common/mbedtls/net_sockets.c ) target_include_directories(pm3rrg_rdv4_mbedtls PRIVATE ../../common) diff --git a/client/deps/tinycbor/cborencoder.c b/client/deps/tinycbor/cborencoder.c index 570319e67..ae034d727 100644 --- a/client/deps/tinycbor/cborencoder.c +++ b/client/deps/tinycbor/cborencoder.c @@ -232,21 +232,21 @@ static inline void put64(void *where, uint64_t v) { memcpy(where, &v, sizeof(v)); } -static inline bool would_overflow(CborEncoder *encoder, size_t len) { +static bool would_overflow(CborEncoder *encoder, size_t len) { ptrdiff_t remaining = (ptrdiff_t)encoder->end; remaining -= remaining ? (ptrdiff_t)encoder->data.ptr : encoder->data.bytes_needed; remaining -= (ptrdiff_t)len; return unlikely(remaining < 0); } -static inline void advance_ptr(CborEncoder *encoder, size_t n) { +static void advance_ptr(CborEncoder *encoder, size_t n) { if (encoder->end) encoder->data.ptr += n; else encoder->data.bytes_needed += n; } -static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len) { +static CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len) { if (would_overflow(encoder, len)) { if (encoder->end != NULL) { len -= encoder->end - encoder->data.ptr; @@ -263,11 +263,11 @@ static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, return CborNoError; } -static inline CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte) { +static CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte) { return append_to_buffer(encoder, &byte, 1); } -static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType) { +static CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType) { /* Little-endian would have been so much more convenient here: * We could just write at the beginning of buf but append_to_buffer * only the necessary bytes. diff --git a/client/deps/tinycbor/cborparser.c b/client/deps/tinycbor/cborparser.c index 2dade3a0d..468fe5da2 100644 --- a/client/deps/tinycbor/cborparser.c +++ b/client/deps/tinycbor/cborparser.c @@ -142,19 +142,19 @@ * \endif */ -static inline uint16_t get16(const uint8_t *ptr) { +static uint16_t get16(const uint8_t *ptr) { uint16_t result; memcpy(&result, ptr, sizeof(result)); return cbor_ntohs(result); } -static inline uint32_t get32(const uint8_t *ptr) { +static uint32_t get32(const uint8_t *ptr) { uint32_t result; memcpy(&result, ptr, sizeof(result)); return cbor_ntohl(result); } -static inline uint64_t get64(const uint8_t *ptr) { +static uint64_t get64(const uint8_t *ptr) { uint64_t result; memcpy(&result, ptr, sizeof(result)); return cbor_ntohll(result); @@ -949,7 +949,7 @@ CborError cbor_value_calculate_string_length(const CborValue *value, size_t *len return _cbor_value_copy_string(value, NULL, len, NULL); } -static inline void prepare_string_iteration(CborValue *it) { +static void prepare_string_iteration(CborValue *it) { if (!cbor_value_is_length_known(it)) { /* chunked string: we're before the first chunk; * advance to the first chunk */ diff --git a/client/dictionaries/iclass_default_keys.dic b/client/dictionaries/iclass_default_keys.dic index d2d6489b2..c717cd88f 100644 --- a/client/dictionaries/iclass_default_keys.dic +++ b/client/dictionaries/iclass_default_keys.dic @@ -37,5 +37,5 @@ F41DAF58B20C8B91 66584C91EE80D5E5 C1B74D7478053AE2 # -# default iCLASS RFIDeas +# default iCLASS RFIDeas 6B65797374726B72 diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 63598f78b..6616a92ee 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -31,6 +31,14 @@ D01AFEEB890A # 17 B 4B791BEA7BCC # +# QL88 keys +# 17 A/B +2612C6DE84CA +707B11FC1481 +# +# QL88 diversifed +03F9067646AE +2352C5B56D85 # B0B1B2B3B4B5 C0C1C2C3C4C5 @@ -63,8 +71,8 @@ B27CCAB30DBD D2ECE8B9395E # NSCP default key 1494E81663D7 -# -# NFC tools +# +# NFC tools 7c9fb8474242 # # Kiev keys @@ -128,6 +136,19 @@ F1D83F964314 199404281970 199404281998 # +# Data from http://www.proxmark.org/forum/viewtopic.php?pid=25925#p25925 +# Tengo Cards Key A +FFF011223358 +FF9F11223358 +# +# Elevator system Kherson, Ukraine +AC37E76385F5 +576DCFFF2F25 +# +# Car wash system +1EE38419EF39 +26578719DCD9 +# # more Keys from mfc_default_keys.lua 000000000001 000000000002 @@ -145,6 +166,9 @@ F1D83F964314 222222222222 27DD91F1FCF1 # +# Hotel system +505209016A1F +# # Directory and eventlog KeyB 2BA9621E0A36 # @@ -288,7 +312,7 @@ AAFB06045877 25094DF6F148 # # -# https://mattionline.de/fitnessstudio-armband-reverse-engineering/ +# https://mattionline.de/fitnessstudio-armband-reverse-engineering/ # https://mattionline.de/milazycracker/ # gym wistband A, same as Fysiken A # gym wistband B @@ -299,7 +323,7 @@ AAFB06045877 A05DBD98E0FC # # GoFit -AA4DDA458EBB +AA4DDA458EBB EAB8066C7479 # # Nordic Wellness A, same as Fysiken A @@ -319,6 +343,9 @@ AFBECD121004 6E7747394E63 763958704B78 # +# Onity S1 A/B +8A19D40CF2B5 +# # 24-7 D21762B2DE3B 0E83A374B513 @@ -589,6 +616,7 @@ BFB6796A11DB # Data from Salto A/B 6A1987C40A21 7F33625BC129 +6BE9314930D8 # # Data from forum 2338B4913111 @@ -628,6 +656,12 @@ A56C2DF9A26D # # Smart Rider. Western Australian Public Transport Cards 568C9083F71C +117E5C165B10 +24BB421C7973 +3E3A546650EA +41F262D3AB66 +514956AB3142 +863933AE8388 # # Bangkok metro key 97F5DA640B18 @@ -860,7 +894,7 @@ AD4FB33388BF # # HID MIFARE Classic 1k Key 484944204953 -204752454154 +204752454154 # HID MIFARE SO 3B7E4FD575AD 11496F97752A @@ -927,9 +961,9 @@ FBF225DC5D58 # Data https://pastebin.com/BEm6bdAE # vingcard.txt # Note: most likely diversified +96A301BCE267 4708111C8604 3D50D902EA48 -96A301BCE267 6700F10FEC09 7A09CC1DB70A 560F7CFF2D81 @@ -949,8 +983,7 @@ D58660D1ACDE C01FC822C6E5 0854BF31111E # -# More keys -8A19D40CF2B5 +# More keys - Found 8A at Sebel Hotel in Canberra, Australia AE8587108640 # # SafLock standalone door locks @@ -1012,6 +1045,10 @@ F8493407799D 6B8BD9860763 D3A297DC2698 # +# Data from reddit +34635A313344 +593367486137 +# # Keys from Mifare Classic Tool project 044CE1872BC3 045CECA15535 @@ -1113,6 +1150,33 @@ A2B2C9D187FB # Hotel Adina 9EBC3EB37130 # +# Misc. keys from hotels & library cards in Germany +914f57280ce3 +324a82200018 +370aee95cd69 +2e032ad6850d +1feda39d38ec +288b7a34dbf8 +0965e3193497 +18c628493f7f +064d9423938a +995fd2a2351e +7c7d672bc62e +217250fb7014 +ae7478ccaee7 +abbf6d116eaf +05862c58edfb +e43b7f185460 +6a59aa9a959b +b79e5b175227 +7bc9ebb8274b +b2afbf2331d4 +223e5847dd79 +640524d2a39b +aee297cb2fd6 +3da5dfa54604 +0cf1a2aa1f8d +# # most likely diversifed individual keys. # data from https://github.com/korsehindi/proxmark3/commit/24fdbfa9a1d5c996aaa5c192bc07e4ab28db4c5c 491CDC863104 @@ -1812,6 +1876,43 @@ E19504C39461 FA1FBB3F0F1F FF16014FEFC7 # +# Food GEM +6686FADE5566 +# +# Samsung Data Systems (SDS) — Electronic Locks +# Gen 1 S10 KA/KB is FFFFFFFFFFFF, incompatible with Gen 2 locks +# +# SDS Gen 2 S10 KB +C22E04247D9A +# +# Data from Discord, French pool +# SDS Gen 2 S10 KA +9B7C25052FC3 +494446555455 +# +# Data from Discord, seems to be related to ASSA +427553754D47 +# Keys found on Edith Cowan University Smart Riders +9A677289564D +186C59E6AFC9 +DDDAA35A9749 +9D0D0A829F49 +# Mercator Pika Card, Slovenia +97D77FAE77D3 +5AF445D2B87A +# +# Vilniečio/JUDU kortelė, Lithuania +# A +16901CB400BC +F0FE56621A42 +8C187E78EE9C +FE2A42E85CA8 +# B +6A6C80423226 +F4CE4AF888AE +307448829EBC +C2A0105EB028 +# # Keys from Flipper Zero Community # Last update: Aug 13, 2022 # @@ -2042,9 +2143,54 @@ D144BD193063 8627C10A7014 453857395635 # +# Unknown hotel system Sec 0 / A +353038383134 +# +# Brazil transport Sec 8 / A +50d4c54fcdf5 +# +# Bandai Namco Passport [fka Banapassport] / Sega Aime Card +# Dumped on the Flipper Devices Discord Server +6090D00632F5 +019761AA8082 +574343467632 +A99164400748 +62742819AD7C +CC5075E42BA1 +B9DF35A0814C +8AF9C718F23D +58CD5C3673CB +FC80E88EB88C +7A3CDAD7C023 +30424C029001 +024E4E44001F +ECBBFA57C6AD +4757698143BD +1D30972E6485 +F8526D1A8D6D +1300EC8C7E80 +F80A65A87FFA +DEB06ED4AF8E +4AD96BF28190 +000390014D41 +0800F9917CB0 +730050555253 +4146D4A956C4 +131157FBB126 +E69DD9015A43 +337237F254D5 +9A8389F32FBF +7B8FB4A7100B +C8382A233993 +7B304F2A12A6 +FC9418BF788B +# # Data from "the more the marriott" mifare project (colonelborkmundus) # aka The Horde # +# These keys seem to be from Vingcard / Saflok system which means they are diversified +# and not static default keys. To verify this, the UID from such a card is needed. +# # 20230125-01, Elite Member Marriott Rewards 43012BD9EB87 # 20230125-02, Elite Member Marriott Rewards @@ -2125,7 +2271,6 @@ D23C1CB1216E # 20230413-78, Caesars A1D92F808CAF # 20230413-79, The Cosmopolitan, Vegas -# 96A301BCE267 # 20230413-80, Aria 1153C319B4F8 # 20230413-81, Aria @@ -2156,14 +2301,39 @@ D201DBB6AB6E 09074A146605 151F3E85EC46 # +# Travelodge by Wyndham Berkeley +0000FFFFFFFF +4663ACD2FFFF +EDC317193709 +# Hotel Santa Cruz +75FAB77E2E5B +# saflok brand HOTEL key +32F093536677 +# A WaterFront Hotel in Oakland +3351916B5A77 +# Ballys (2018) +336E34CC2177 +# Random Hawaiian Hotel +A1670589B2AF +# SF Hotel (SoMa area) +2E0F00700000 # -# Food GEM -6686FADE5566 +# Unknown PACS from Western Australia +CA80E51FA52B +A71E80EA35E1 +05597810D63D # -# Data from Discord, French pool -9B7C25052FC3 -494446555455 +# Hotel Key from Las Vegas +EA0CA627FD06 +80BB8436024C +5044068C5183 # -# Data from Discord, seems to be related to ASSA -427553754D47 - +# Key from Hotel M Montreal (probably diversified) +7E5E05866ED6 +661ABF99AFAD +# +# Key from evo Montreal (probably diversified) +1064BA5D6DF8 +# Hotel key +CE0F4F15E909 +D60DE9436219 diff --git a/client/dictionaries/mfdes_default_keys.dic b/client/dictionaries/mfdes_default_keys.dic index fbf3a459f..3989054f9 100644 --- a/client/dictionaries/mfdes_default_keys.dic +++ b/client/dictionaries/mfdes_default_keys.dic @@ -15,7 +15,7 @@ ffffffffffffffffffffffffffffffffffffffffffffffff 6AC292FAA1315B4D858AB3A3D7D5933A 404142434445464748494a4b4c4d4e4f 3112B738D8862CCD34302EB299AAB456 # Gallagher AES (https://pastebin.com/GkbGLz8r) -47454D5850524553534F53414D504C45 # Gemalto +47454D5850524553534F53414D504C45 # Gemalto 2b7e151628aed2a6abf7158809cf4f3c fbeed618357133667c85e08f7236a8de f7ddac306ae266ccf90bc11ee46d513b diff --git a/client/dictionaries/mfulc_default_keys.dic b/client/dictionaries/mfulc_default_keys.dic index 51b4b9365..09dea8caf 100644 --- a/client/dictionaries/mfulc_default_keys.dic +++ b/client/dictionaries/mfulc_default_keys.dic @@ -1,5 +1,5 @@ # -# Mifare Ultralight Default Keys +# Mifare Ultralight C Default Keys # -- iceman fork version -- # -- contribute to this list, sharing is caring -- # diff --git a/client/dictionaries/t55xx_default_pwds.dic b/client/dictionaries/t55xx_default_pwds.dic index e56904ea4..fc9c6bb8b 100644 --- a/client/dictionaries/t55xx_default_pwds.dic +++ b/client/dictionaries/t55xx_default_pwds.dic @@ -37,7 +37,7 @@ A5B4C3D2 E9920427 # paxton bullit? 575F4F4B -# +# Hotel system 50520901 # iCopy-X 20206666 @@ -52,6 +52,8 @@ C0F5009A # prefered pwds of members in the community FEEDBEEF DEADC0DE +# derived from BCARD key B +A9EF2AFC # Default pwd, simple: 00000000 11111111 diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index 2ac10f871..efa334216 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -97,11 +97,13 @@ if (CMAKE_TOOLCHAIN_FILE) endif (ANDROID) set(EMBED_READLINE ON) set(EMBED_BZIP2 ON) + set(EMBED_LZ4 ON) + set(EMBED_GD ON) endif (CMAKE_TOOLCHAIN_FILE) -if (EMBED_READLINE OR EMBED_BZIP2) +if (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4 OR EMBED_GD) include(ExternalProject) -endif (EMBED_READLINE OR EMBED_BZIP2) +endif (EMBED_READLINE OR EMBED_BZIP2 OR EMBED_LZ4 OR EMBED_GD) if (NOT SKIPREADLINE EQUAL 1) if (APPLE) @@ -163,12 +165,14 @@ if (NOT SKIPJANSSONSYSTEM EQUAL 1) endif (NOT SKIPJANSSONSYSTEM EQUAL 1) if(EMBED_BZIP2) + cmake_policy(SET CMP0114 NEW) set(BZIP2_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/bzip2/src/bzip2) + # Specify SOURCE_DIR will cause some errors ExternalProject_Add(bzip2 GIT_REPOSITORY https://android.googlesource.com/platform/external/bzip2 GIT_TAG platform-tools-30.0.2 PREFIX deps/bzip2 - SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/bzip2 + # SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/bzip2 CONFIGURE_COMMAND mkdir -p ${BZIP2_BUILD_DIR} && git archive --format tar HEAD | tar -C ${BZIP2_BUILD_DIR} -x BUILD_IN_SOURCE ON BUILD_COMMAND make -C ${BZIP2_BUILD_DIR} -j4 CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} LD=${CMAKE_C_COMPILER} AR=${CMAKE_AR} RANLIB=${CMAKE_RANLIB} ${CFLAGS_EXTERNAL_LIB} libbz2.a @@ -183,6 +187,55 @@ else(EMBED_BZIP2) find_package (BZip2 REQUIRED) endif(EMBED_BZIP2) +if(EMBED_LZ4) + cmake_policy(SET CMP0114 NEW) + set(LZ4_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/lz4/src/lz4) + # Specify SOURCE_DIR will cause some errors + ExternalProject_Add(lz4 + GIT_REPOSITORY https://android.googlesource.com/platform/external/lz4 + GIT_TAG platform-tools-30.0.2 + PREFIX deps/lz4 + # SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/lz4 + CONFIGURE_COMMAND mkdir -p ${LZ4_BUILD_DIR} && git archive --format tar HEAD | tar -C ${LZ4_BUILD_DIR} -x + BUILD_IN_SOURCE ON + BUILD_COMMAND make -C ${LZ4_BUILD_DIR}/lib -j4 CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} LD=${CMAKE_C_COMPILER} AR=${CMAKE_AR} RANLIB=${CMAKE_RANLIB} ${CFLAGS_EXTERNAL_LIB} liblz4.a + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + ) + ExternalProject_Add_StepTargets(lz4 configure build install) + set(LZ4_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}/deps/lz4/src/lz4/lib) + set(LZ4_LIBRARIES ${CMAKE_CURRENT_BINARY_DIR}/deps/lz4/src/lz4/lib/liblz4.a) + set(LZ4_FOUND ON) +else(EMBED_LZ4) + find_path(LZ4_INCLUDE_DIRS lz4frame.h) + find_library(LZ4_LIBRARIES lz4) +endif(EMBED_LZ4) + +if (LZ4_INCLUDE_DIRS AND LZ4_LIBRARIES) + set(LZ4_FOUND ON) +endif (LZ4_INCLUDE_DIRS AND LZ4_LIBRARIES) + +if (NOT SKIPGD EQUAL 1) + if (EMBED_GD) + cmake_policy(SET CMP0114 NEW) + set(GD_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/gd) + # Specify SOURCE_DIR will cause some errors + ExternalProject_Add(gd + URL https://github.com/libgd/libgd/releases/download/gd-2.3.3/libgd-2.3.3.tar.gz + URL_HASH SHA256=dd3f1f0bb016edcc0b2d082e8229c822ad1d02223511997c80461481759b1ed2 + PREFIX deps/gd + CMAKE_ARGS -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DENABLE_CPP=0 -DCMAKE_INSTALL_PREFIX=. + PATCH_COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/deps/gd-static.patch | patch -p1 + ) + ExternalProject_Add_StepTargets(gd configure build install) + set(GD_INCLUDE_DIRS ${GD_BUILD_DIR}/src/gd-build/include) + set(GD_LIBRARIES ${GD_BUILD_DIR}/src/gd-build/lib/libgd.a) + set(GD_FOUND ON) + else (EMBED_GD) + pkg_search_module(GD QUIET gdlib) + endif (EMBED_GD) +endif (NOT SKIPGD EQUAL 1) + if (NOT SKIPWHEREAMISYSTEM EQUAL 1) find_path(WHEREAMI_INCLUDE_DIRS whereami.h) find_library(WHEREAMI_LIBRARIES whereami) @@ -249,6 +302,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/mifare/mifare4.c ${PM3_ROOT}/client/src/mifare/mifaredefault.c ${PM3_ROOT}/client/src/mifare/mifarehost.c + ${PM3_ROOT}/client/src/mifare/gen4.c ${PM3_ROOT}/client/src/nfc/ndef.c ${PM3_ROOT}/client/src/mifare/lrpcrypto.c ${PM3_ROOT}/client/src/mifare/desfirecrypto.c @@ -256,6 +310,8 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/mifare/desfirecore.c ${PM3_ROOT}/client/src/mifare/desfiretest.c ${PM3_ROOT}/client/src/mifare/gallaghercore.c + ${PM3_ROOT}/client/src/uart/ringbuffer.c + ${PM3_ROOT}/client/src/uart/uart_common.c ${PM3_ROOT}/client/src/uart/uart_posix.c ${PM3_ROOT}/client/src/uart/uart_win32.c ${PM3_ROOT}/client/src/ui/overlays.ui @@ -298,7 +354,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdhftexkom.c ${PM3_ROOT}/client/src/cmdhfthinfilm.c ${PM3_ROOT}/client/src/cmdhftopaz.c - ${PM3_ROOT}/client/src/cmdhfwaveshare.c + ${PM3_ROOT}/client/src/cmdhfvas.c ${PM3_ROOT}/client/src/cmdhfxerox.c ${PM3_ROOT}/client/src/cmdhw.c ${PM3_ROOT}/client/src/cmdlf.c @@ -348,6 +404,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/fileutils.c ${PM3_ROOT}/client/src/flash.c ${PM3_ROOT}/client/src/graph.c + ${PM3_ROOT}/client/src/iso4217.c ${PM3_ROOT}/client/src/jansson_path.c ${PM3_ROOT}/client/src/preferences.c ${PM3_ROOT}/client/src/pm3.c @@ -365,7 +422,7 @@ set (TARGET_SOURCES add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/version_pm3.c - COMMAND sh ${PM3_ROOT}/tools/mkversion.sh > ${CMAKE_BINARY_DIR}/version_pm3.c || ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c + COMMAND sh ${PM3_ROOT}/tools/mkversion.sh ${CMAKE_BINARY_DIR}/version_pm3.c || ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c DEPENDS ${PM3_ROOT}/common/default_version_pm3.c ) @@ -380,7 +437,23 @@ message(STATUS "CMAKE_SYSTEM_PROCESSOR := ${CMAKE_SYSTEM_PROCESSOR}") if (APPLE) message(STATUS "Apple device detected.") set(ADDITIONAL_SRC ${PM3_ROOT}/client/src/util_darwin.h ${PM3_ROOT}/client/src/util_darwin.m ${ADDITIONAL_SRC}) - set(ADDITIONAL_LNK "-framework Foundation" "-framework AppKit") + + find_library(UIKIT_LIBRARY UIKit) + if (NOT UIKIT_LIBRARY) + message(STATUS "UIKit.framework NOT found!") + else() + message(STATUS "UIKit.framework found! ${UIKIT_LIBRARY}") + set(ADDITIONAL_LNK "-framework Foundation" "-framework UIKit") + endif() + + find_library(APPKIT_LIBRARY AppKit) + if (NOT APPKIT_LIBRARY) + message(STATUS "AppKit.framework NOT found!") + else() + message(STATUS "AppKit.framework found! ${APPKIT_LIBRARY}") + set(ADDITIONAL_LNK "-framework Foundation" "-framework AppKit") + endif() + endif (APPLE) if ((NOT SKIPQT EQUAL 1) AND (Qt5_FOUND)) @@ -439,6 +512,22 @@ if (BZIP2_FOUND) set(ADDITIONAL_LNK ${BZIP2_LIBRARIES} ${ADDITIONAL_LNK}) endif (BZIP2_FOUND) +if (LZ4_FOUND) + set(ADDITIONAL_DIRS ${LZ4_INCLUDE_DIRS} ${ADDITIONAL_DIRS}) + set(ADDITIONAL_LNK ${LZ4_LIBRARIES} ${ADDITIONAL_LNK}) +endif (LZ4_FOUND) + +if (NOT SKIPGD EQUAL 1 AND GD_FOUND) + set(ADDITIONAL_DIRS ${GD_INCLUDE_DIRS} ${ADDITIONAL_DIRS}) + set(ADDITIONAL_LNK ${GD_LIBRARIES} ${ADDITIONAL_LNK}) + set(ADDITIONAL_LNKDIRS ${GD_LIBRARY_DIRS} ${ADDITIONAL_LNKDIRS}) + set(TARGET_SOURCES + ${PM3_ROOT}/client/src/imgutils.c + ${PM3_ROOT}/client/src/cmdhfwaveshare.c + ${TARGET_SOURCES}) + add_definitions("-DHAVE_GD") +endif (NOT SKIPGD EQUAL 1 AND GD_FOUND) + if (WHEREAMI_FOUND) set(ADDITIONAL_DIRS ${WHEREAMI_INCLUDE_DIRS} ${ADDITIONAL_DIRS}) set(ADDITIONAL_LNK ${WHEREAMI_LIBRARIES} ${ADDITIONAL_LNK}) @@ -471,11 +560,37 @@ else (SKIPBT EQUAL 1) endif (BLUEZ_FOUND) endif(SKIPBT EQUAL 1) -if (EMBED_BZIP2) - message(STATUS "Bzip2 library: embedded") -else (EMBED_BZIP2) - message(STATUS "Bzip2 library: system library found") -endif (EMBED_BZIP2) +if (BZIP2_FOUND) + if (EMBED_BZIP2) + message(STATUS "Bzip2 library: embedded") + else (EMBED_BZIP2) + message(STATUS "Bzip2 library: system library found") + endif (EMBED_BZIP2) +else (BZIP2_FOUND) + message(SEND_ERROR "Bzip2 library: Bzip2 not found") +endif (BZIP2_FOUND) + +if (LZ4_FOUND) + if (EMBED_LZ4) + message(STATUS "LZ4 library: embedded") + else (EMBED_LZ4) + message(STATUS "LZ4 library: system library found") + endif (EMBED_LZ4) +else (LZ4_FOUND) + message(SEND_ERROR "LZ4 library: LZ4 not found") +endif (LZ4_FOUND) + +if (SKIPGD EQUAL 1) + message(STATUS "GD library: skipped") +elseif (GD_FOUND) + if (EMBED_GD) + message(STATUS "GD library: embedded") + else (EMBED_GD) + message(STATUS "GD library: system library found") + endif (EMBED_GD) +else (SKIPGD EQUAL 1) + message(STATUS "GD library: GD not found, disabled") +endif (SKIPGD EQUAL 1) if (SKIPJANSSONSYSTEM EQUAL 1) message(STATUS "Jansson library: local library forced") @@ -565,7 +680,7 @@ add_library(pm3rrg_rdv4 SHARED ) target_compile_definitions(pm3rrg_rdv4 PRIVATE LIBPM3) - + target_compile_options(pm3rrg_rdv4 PUBLIC -Wall -Werror -O3) if (EMBED_READLINE) if (NOT SKIPREADLINE EQUAL 1) @@ -575,6 +690,9 @@ endif (EMBED_READLINE) if (EMBED_BZIP2) add_dependencies(pm3rrg_rdv4 bzip2) endif (EMBED_BZIP2) +if (EMBED_LZ4) + add_dependencies(pm3rrg_rdv4 lz4) +endif (EMBED_LZ4) if (MINGW) # Mingw uses by default Microsoft printf, we want the GNU printf (e.g. for %z) @@ -586,19 +704,21 @@ if (MINGW) set(CMAKE_C_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_C_FLAGS}") set(CMAKE_CXX_FLAGS "-mno-ms-bitfields -fexec-charset=cp850 ${CMAKE_CXX_FLAGS}") - # GCC 10 has issues with false positives on stringop-overflow, - # let's disable them for now (cf https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92955, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94335) - # beware these flags didn't exist for GCC < 7 - if(CMAKE_COMPILER_IS_GNUCXX) - execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) - if (GCC_VERSION VERSION_GREATER 10.0 OR GCC_VERSION VERSION_EQUAL 10.0) - set(CMAKE_C_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_CXX_FLAGS}") - endif() - endif(CMAKE_COMPILER_IS_GNUCXX) - + # link Winsock2 + set(ADDITIONAL_LNK ws2_32 ${ADDITIONAL_LNK}) endif (MINGW) +# GCC 10 has issues with false positives on stringop-overflow, +# let's disable them for now (cf https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92955, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94335) +# beware these flags didn't exist for GCC < 7 +if(CMAKE_COMPILER_IS_GNUCXX) + execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) + if (GCC_VERSION VERSION_GREATER 10.0 OR GCC_VERSION VERSION_EQUAL 10.0) + set(CMAKE_C_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "-Wno-stringop-overflow -Wno-error=stringop-overflow ${CMAKE_CXX_FLAGS}") + endif() +endif(CMAKE_COMPILER_IS_GNUCXX) + target_include_directories(pm3rrg_rdv4 PRIVATE ${PM3_ROOT}/common ${PM3_ROOT}/common_fpga @@ -646,10 +766,10 @@ endif (NOT SKIPPTHREAD EQUAL 1) if (NOT SKIPPYTHON EQUAL 1) # OSX have a hard time compiling python3 dependency with older cmake. if (PYTHON3EMBED_FOUND OR PYTHON3_FOUND) - if (NOT CMAKE_VERSION VERSION_LESS 3.13) - target_link_directories(pm3rrg_rdv4 PRIVATE ${ADDITIONAL_LNKDIRS}) - elseif (APPLE) - message( SEND_ERROR "Your CMAKE version is too old for Apple platform, please update to a version >=3.13" ) - endif (NOT CMAKE_VERSION VERSION_LESS 3.13) + if (CMAKE_VERSION VERSION_LESS 3.13) + message( SEND_ERROR "Your CMAKE version is too old for Apple platform, please update to a version >=3.13" ) + endif (CMAKE_VERSION VERSION_LESS 3.13) endif (PYTHON3EMBED_FOUND OR PYTHON3_FOUND) endif (NOT SKIPPYTHON EQUAL 1) + +target_link_directories(pm3rrg_rdv4 PRIVATE ${ADDITIONAL_LNKDIRS}) diff --git a/client/experimental_lib/example_c/01make_test.sh b/client/experimental_lib/example_c/01make_test.sh index 1ce6357ff..7bda5aa37 100755 --- a/client/experimental_lib/example_c/01make_test.sh +++ b/client/experimental_lib/example_c/01make_test.sh @@ -1,3 +1,4 @@ #!/bin/bash gcc -o test test.c -I../../include -lpm3rrg_rdv4 -L../build -lpthread +gcc -o test_grab test_grab.c -I../../include -lpm3rrg_rdv4 -L../build -lpthread diff --git a/client/experimental_lib/example_c/02run_test.sh b/client/experimental_lib/example_c/02run_test.sh index a3e4ec057..7da0e3700 100755 --- a/client/experimental_lib/example_c/02run_test.sh +++ b/client/experimental_lib/example_c/02run_test.sh @@ -1,3 +1,3 @@ #!/bin/bash -LD_LIBRARY_PATH=../build ./test +LD_LIBRARY_PATH=../build ./test /dev/ttyACM0 diff --git a/client/experimental_lib/example_c/02run_test_grab.sh b/client/experimental_lib/example_c/02run_test_grab.sh new file mode 100755 index 000000000..93545c422 --- /dev/null +++ b/client/experimental_lib/example_c/02run_test_grab.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +LD_LIBRARY_PATH=../build ./test_grab /dev/ttyACM0 diff --git a/client/experimental_lib/example_c/test.c b/client/experimental_lib/example_c/test.c index a3cf212dc..319b6b08e 100644 --- a/client/experimental_lib/example_c/test.c +++ b/client/experimental_lib/example_c/test.c @@ -1,8 +1,14 @@ +#include +#include #include "pm3.h" int main(int argc, char *argv[]) { + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + exit(-1); + } pm3 *p; - p = pm3_open("/dev/ttyACM0"); + p = pm3_open(argv[1]); pm3_console(p, "hw status"); pm3_close(p); } diff --git a/client/experimental_lib/example_c/test_grab.c b/client/experimental_lib/example_c/test_grab.c new file mode 100644 index 000000000..c785361ba --- /dev/null +++ b/client/experimental_lib/example_c/test_grab.c @@ -0,0 +1,74 @@ +#include "pm3.h" +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + + int pipefd[2]; + char buf[8196 + 1]; + size_t n; + + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + exit(-1); + } + + if (pipe(pipefd) == -1) { + exit(-1); + } + + int pid = fork(); + if (pid == -1) { + perror("fork"); + exit(-1); + } + + // child + if (pid == 0) { + printf("[INFO] inside child\n"); + + // Redirect stdout to the write end of the pipe + dup2(pipefd[1], STDOUT_FILENO); + + close(pipefd[0]); // Child: close read end of the pipe + close(pipefd[1]); // Close original write end + + pm3 *p; + p = pm3_open(argv[1]); + + // Execute the command + pm3_console(p, "hw status"); + pm3_close(p); + _exit(-1); + } else { + + printf("[INFO] inside parent\n"); + // Parent: close write end of the pipe + close(pipefd[1]); + + // Read from the pipe + while (1) { + n = read(pipefd[0], buf, sizeof(buf)); + if (n == -1) { + continue; + } + if (n == 0) { + break; + } else { + // null termination + buf[n] = 0; + if (strstr(buf, "ERROR") != NULL) { + printf("%s", buf); + } + if (strstr(buf, "Unique ID") != NULL) { + printf("%s", buf); + } + } + } + + // Close read end + close(pipefd[0]); + } +} diff --git a/client/experimental_lib/example_py/02run_test.sh b/client/experimental_lib/example_py/02run_test.sh index 67ab20525..f881b459d 100755 --- a/client/experimental_lib/example_py/02run_test.sh +++ b/client/experimental_lib/example_py/02run_test.sh @@ -8,4 +8,4 @@ #/usr/lib/python3/dist-packages/pm3.py # need access to pm3.py -PYTHONPATH=../../src ./test.py +PYTHONPATH=../../pyscripts ./test.py diff --git a/client/experimental_lib/example_py/02run_test_grab_interactive.sh b/client/experimental_lib/example_py/02run_test_grab_interactive.sh index d7dd7dd16..5cfa7a038 100755 --- a/client/experimental_lib/example_py/02run_test_grab_interactive.sh +++ b/client/experimental_lib/example_py/02run_test_grab_interactive.sh @@ -1,3 +1,3 @@ #!/bin/bash -PYTHONPATH=../../src ipython3 -i ./test_grab.py +PYTHONPATH=../../pyscripts ipython3 -i ./test_grab.py diff --git a/client/experimental_lib/example_py/02run_test_interactive.sh b/client/experimental_lib/example_py/02run_test_interactive.sh index 701dbb5cf..f7d70a5d4 100755 --- a/client/experimental_lib/example_py/02run_test_interactive.sh +++ b/client/experimental_lib/example_py/02run_test_interactive.sh @@ -1,3 +1,3 @@ #!/bin/bash -PYTHONPATH=../../src ipython3 -i ./test.py +PYTHONPATH=../../pyscripts ipython3 -i ./test.py diff --git a/client/lualibs/hf_reader.lua b/client/lualibs/hf_reader.lua index f49d587f2..894a0d8ef 100644 --- a/client/lualibs/hf_reader.lua +++ b/client/lualibs/hf_reader.lua @@ -15,7 +15,7 @@ local reader15693 = require('read15') -- @return if successful: an table containing card info -- @return if unsuccessful : nil, error local function waitForTag() - print("Waiting for card... press Enter to quit") + print("Waiting for card... press to quit") local readers = {reader14443A, reader14443B, reader15693} local i = 0; while not core.kbd_enter_pressed() do diff --git a/client/lualibs/read14a.lua b/client/lualibs/read14a.lua index c4ef3caa7..782fca6d5 100644 --- a/client/lualibs/read14a.lua +++ b/client/lualibs/read14a.lua @@ -124,7 +124,7 @@ end -- @return if successful: an table containing card info -- @return if unsuccessful : nil, error local function waitFor14443a() - print('Waiting for card... press Enter to quit') + print('Waiting for card... press to quit') while not core.kbd_enter_pressed() do res, err = read14443a() if res then return res end diff --git a/client/lualibs/read15.lua b/client/lualibs/read15.lua index feb35d3eb..9d4ca589d 100644 --- a/client/lualibs/read15.lua +++ b/client/lualibs/read15.lua @@ -133,7 +133,7 @@ end -- @return if successful: an table containing card info -- @return if unsuccessful : nil, error local function waitFor15693() - print('Waiting for card... press Enter to quit') + print('Waiting for card... press to quit') while not core.kbd_enter_pressed() do res, err = read15693() if res then return res end diff --git a/client/lualibs/utils.lua b/client/lualibs/utils.lua index 00103d6b4..03862ac29 100644 --- a/client/lualibs/utils.lua +++ b/client/lualibs/utils.lua @@ -262,14 +262,19 @@ local Utils = -- -- Converts DECIMAL to HEX - ConvertDecToHex = function(IN) - local B,K,OUT,I,D = 16, "0123456789ABCDEF", "", 0 - while IN > 0 do - I = I+1 - IN, D = math.floor(IN/B), math.modf(IN, B) + 1 - OUT = string.sub(K, D, D)..OUT + ConvertDecToHex = function(decimal) + if decimal == 0 then + return "0" end - return OUT + + local B,DIGITS,hex = 16, "0123456789ABCDEF", "" + + while decimal > 0 do + local remainder = math.fmod(decimal, B) + hex = string.sub(DIGITS, remainder + 1, remainder + 1) .. hex + decimal = math.floor(decimal / B) + end + return hex end, --- -- Convert Byte array to string of hex diff --git a/client/luascripts/hf_14b_calypso.lua b/client/luascripts/hf_14b_calypso.lua index 61aa92dde..e9cc8c8f5 100644 --- a/client/luascripts/hf_14b_calypso.lua +++ b/client/luascripts/hf_14b_calypso.lua @@ -29,6 +29,9 @@ Check there for details about data format and how commands are interpreted on th device-side. ]] +local PM3_SUCCESS = 0 + +-- iceman, todo: return payload from ISO14b APDU is a struct now. iso14b_raw_apdu_response_t local function calypso_parse(result) if result.Oldarg0 >= 0 then local len = result.Oldarg0 * 2 @@ -112,9 +115,7 @@ end local function calypso_send_cmd_raw(data, ignoreresponse ) local flags = lib14b.ISO14B_COMMAND.ISO14B_APDU --- flags = lib14b.ISO14B_COMMAND.ISO14B_RAW + --- lib14b.ISO14B_COMMAND.ISO14B_APPEND_CRC - local flags = lib14b.ISO14B_COMMAND.ISO14B_APDU + data = data or "" -- LEN of data, half the length of the ASCII-string hex string -- 2 bytes flags @@ -129,7 +130,7 @@ local function calypso_send_cmd_raw(data, ignoreresponse ) local c = Command:newNG{cmd = cmds.CMD_HF_ISO14443B_COMMAND, data = senddata} local result, err = c:sendNG(ignoreresponse, 2000) if result then - if result.Oldarg0 >= 0 then + if result.status == PM3_SUCCESS then return calypso_parse(result) else err = 'card response failed' @@ -144,7 +145,7 @@ end -- writes it in the tree in decimal format. local function calypso_card_num(card) if not card then return end - local card_num = tonumber( card.uid:sub(1,8),16 ) + local card_num = tonumber( card.uid:sub(1, 8), 16) print('') print('Card UID ' ..ansicolors.green..card.uid:format('%x')..ansicolors.reset) print('Card Number ' ..ansicolors.green..string.format('%u', card_num)..ansicolors.reset) @@ -156,7 +157,7 @@ local function calypso_apdu_status(apdu) -- last two is CRC -- next two is APDU status bytes. local mess = 'FAIL' - local sw = apdu:sub( #apdu-7, #apdu-4) + local sw = apdu:sub( #apdu - 7 , #apdu - 4) desc, err = iso7816.tostring(sw) --print ('SW', sw, desc, err ) local status = ( sw == '9000' ) @@ -250,7 +251,7 @@ function main(args) for i, apdu in spairs(_calypso_cmds) do print('>> '..ansicolors.yellow..i..ansicolors.reset) apdu = apdu:gsub('%s+', '') - data, err = calypso_send_cmd_raw(apdu , false) + data, err = calypso_send_cmd_raw(apdu, false) if err then print('<< '..err) else diff --git a/client/luascripts/hf_14b_mobib.lua b/client/luascripts/hf_14b_mobib.lua index 85cd832a8..e267e7412 100644 --- a/client/luascripts/hf_14b_mobib.lua +++ b/client/luascripts/hf_14b_mobib.lua @@ -30,6 +30,7 @@ Check there for details about data format and how commands are interpreted on th device-side. ]] +-- iceman, todo: return payload from ISO14b APDU is a struct now. iso14b_raw_apdu_response_t local function mobib_parse(result) if result.Oldarg0 >= 0 then local len = result.Oldarg0 * 2 @@ -126,7 +127,7 @@ local function mobib_send_cmd_raw(data, ignoreresponse ) local result, err = c:sendNG(ignoreresponse, 2000) if result then - if result.Oldarg0 >= 0 then + if result.status == PM3_SUCCESS then return mobib_parse(result) else err = 'card response failed' diff --git a/client/luascripts/hf_legic.lua b/client/luascripts/hf_legic.lua index d6260ac88..fb98b777e 100644 --- a/client/luascripts/hf_legic.lua +++ b/client/luascripts/hf_legic.lua @@ -96,78 +96,6 @@ Known issues; needs to be fixed: * last byte in last segment is handled incorrectly when it is the last bytes on the card itself (MIM256: => byte 256) --]] -example = "script run hf_legic" -author = "Mosci, uhei" -version = "1.0.4" - -desc = -[[ - -This script helps you to read, create and modify Legic Prime Tags (MIM22, MIM256, MIM1024) -The virtual tag (and therefore the file to be saved) is always a MIM1024 tag. -it's kinda interactive with following commands in three categories: - - Data I/O Segment Manipulation Token-Data - ----------------- -------------------- ----------------- - rt => read Tag as => add Segment mt => make Token - wt => write Tag es => edit Segment Header et => edit Token data - ed => edit Segment Data tk => toggle KGH-Flag - File I/O rs => remove Segment - ----------------- cc => check Segment-CRC - lf => load bin File ck => check KGH - sf => save eml/bin File ds => dump Segments - xf => xor to File - - - (partially) known Segments Virtual Tags Script Output - --------------------------- ------------------------------- ------------------------ - dlc => dump Legic-Cash ct => copy mainTag to backupTag tac => toggle ansicolors - elc => edit Legic-Cash tc => copy backupTag to mainTag - d3p => dump 3rd-Party-Cash tt => switch mainTag & backupTag - e3p => edit 3rd-Party-Cash di => dump mainTag - do => dump backupTag - - - - rt: 'read tag' - reads a tag placed near to the PM3 - wt: 'write tag' - writes the content of the 'virtual inTag' to a tag placed near to th PM3 - without the need of changing anything - MCD,MSN,MCC will be read from the tag - before and applied to the output. - - lf: 'load file' - load a (xored) binary file (*.bin) from the local Filesystem into the 'virtual inTag' - sf: 'save file' - saves the 'virtual inTag' to the local Filesystem as eml and bin (xored with Tag-MCC) - xf: 'xor file' - saves the 'virtual inTag' to the local Filesystem (xored with chosen MCC - use '00' for plain values) - - ct: 'copy tag' - copy the 'virtual Tag' to a second 'virtual TAG' - not useful yet, but inernally needed - tc: 'copy tag' - copy the 'second virtual Tag' to 'virtual TAG' - not useful yet, but inernally needed - tt: 'toggle tag' - copy mainTag to BackupTag and backupTag to mainTag - - di: 'dump mainTag' - shows the current content of the 'virtual Tag' - do: 'dump backupTag' - shows the current content of the 'virtual outTag' - ds: 'dump Segments' - will show the content of a selected Segment - as: 'add Segment' - will add a 'empty' Segment to the inTag - es: 'edit Segment' - edit the Segment-Header of a selected Segment (len, WRP, WRC, RD, valid) - all other Segment-Header-Values are either calculated or not needed to edit (yet) - ed: 'edit data' - edit the Data of a Segment (ADF-Aera / Stamp & Payload specific Data) - et: 'edit Token' - edit Data of a Token (CDF-Area / SAM, SAM64, SAM63, IAM, GAM specific Data) - mt: 'make Token' - create a Token 'from scratch' (guided) - rs: 'remove segment' - removes a Segment (except Segment 00, but this can be set to valid=0 for Master-Token) - cc: 'check Segment-CRC'- checks & calculates (if check failed) the Segment-CRC of all Segments - ck: 'check KGH-CRC' - checks the and calculates a 'Kaba Group Header' if one was detected - 'Kaba Group Header CRC calculation' - tk: 'toggle KGH' - toggle the (script-internal) flag for kgh-calculation for a segment - xc: 'etra c' - show string that was used to calculate the kgh-crc of a segment - -dlc: 'dump Legic-Cash' - show balance and checksums of a Legic-Cash Segment -elc: 'edit Legic-Cash' - edit values of a Legic-Cash Segment - -d3p: 'dump 3rd Party' - show balance, history and checksums of a (yet) unknown 3rd-Party Cash Segment -e3p: 'edit 3rd Party' - edit Data in 3rd-Party Cash Segment - -tac: 'toggle ansicolors'- switch on and off the colored text-output of this script - default can be changed by setting the variable 'colored_output' to false -]] -currentTag="inTAG" --- -- requirements @@ -193,6 +121,10 @@ local acyellow = "" local acblue = "" local acmagenta = "" +local acy = ansicolors.yellow +local acc = ansicolors.cyan +local acr = ansicolors.reset + --- Helper --- --- -- default colors (change to whatever you want) @@ -206,6 +138,10 @@ function load_colors(onoff) acblue = ansicolors.blue acmagenta= ansicolors.magenta acoff = ansicolors.reset + + acy = ansicolors.yellow + acc = ansicolors.cyan + acr = ansicolors.reset else -- 'no color' acgreen = "" @@ -215,13 +151,88 @@ function load_colors(onoff) acblue = "" acmagenta= "" acoff = "" + + acy = "" + acc = "" + acr = "" end end + +example = "script run hf_legic" +author = "Mosci, uhei" +version = "1.0.5" +desc = +[[ + +This script helps you to read, create and modify Legic Prime Tags ( MIM22, MIM256, MIM1024 ) +The virtual tag (and therefore the file to be saved) is always a MIM1024 tag. +it's kinda interactive with following commands in three categories: + + Data I/O Segment Manipulation Token-Data + ----------------- -------------------- ----------------- + ]]..acy..[[rt]]..acr..[[ -> read Tag ]]..acy..[[as]]..acr..[[ -> add Segment ]]..acy..[[mt]]..acr..[[ -> make Token + ]]..acy..[[wt]]..acr..[[ -> write Tag ]]..acy..[[es]]..acr..[[ -> edit Segment Header ]]..acy..[[et]]..acr..[[ -> edit Token data + ]]..acy..[[ed]]..acr..[[ => edit Segment Data ]]..acy..[[tk]]..acr..[[ => toggle KGH-Flag + File I/O ]]..acy..[[rs]]..acr..[[ => remove Segment + ----------------- ]]..acy..[[cc]]..acr..[[ -> check Segment-CRC + ]]..acy..[[lf]]..acr..[[ -> load bin File ]]..acy..[[ck]]..acr..[[ -> check KGH + ]]..acy..[[sf]]..acr..[[ -> save eml/bin File ]]..acy..[[ds]]..acr..[[ -> dump Segments + ]]..acy..[[xf]]..acr..[[ -> xor to File + + + (partially) known Segments Virtual Tags Script Output + --------------------------- ------------------------------- ------------------------ + ]]..acy..[[dlc]]..acr..[[ -> dump Legic-Cash ]]..acy..[[ct]]..acr..[[ -> copy mainTag to backupTag ]]..acy..[[tac]]..acr..[[ -> toggle ansicolors + ]]..acy..[[elc]]..acr..[[ -> edit Legic-Cash ]]..acy..[[tc]]..acr..[[ -> copy backupTag to mainTag + ]]..acy..[[d3p]]..acr..[[ -> dump 3rd-Party-Cash ]]..acy..[[tt]]..acr..[[ -> switch mainTag & backupTag + ]]..acy..[[e3p]]..acr..[[ -> edit 3rd-Party-Cash ]]..acy..[[di]]..acr..[[ -> dump mainTag + ]]..acy..[[do]]..acr..[[ => dump backupTag + + rt: 'read tag' - reads a tag placed near to the PM3 + wt: 'write tag' - writes the content of the 'virtual inTag' to a tag placed near to th PM3 + without the need of changing anything - MCD,MSN,MCC will be read from the tag + before and applied to the output. + + lf: 'load file' - load a (xored) binary file (*.bin) from the local Filesystem into the 'virtual inTag' + sf: 'save file' - saves the 'virtual inTag' to the local Filesystem as eml and bin (xored with Tag-MCC) + xf: 'xor file' - saves the 'virtual inTag' to the local Filesystem (xored with chosen MCC - use '00' for plain values) + + ct: 'copy tag' - copy the 'virtual Tag' to a second 'virtual TAG' - not useful yet, but inernally needed + tc: 'copy tag' - copy the 'second virtual Tag' to 'virtual TAG' - not useful yet, but inernally needed + tt: 'toggle tag' - copy mainTag to BackupTag and backupTag to mainTag + + di: 'dump mainTag' - shows the current content of the 'virtual Tag' + do: 'dump backupTag' - shows the current content of the 'virtual outTag' + ds: 'dump Segments' - will show the content of a selected Segment + as: 'add Segment' - will add a 'empty' Segment to the inTag + es: 'edit Segment' - edit the Segment-Header of a selected Segment (len, WRP, WRC, RD, valid) + all other Segment-Header-Values are either calculated or not needed to edit (yet) + ed: 'edit data' - edit the Data of a Segment (ADF-Aera / Stamp & Payload specific Data) + et: 'edit Token' - edit Data of a Token (CDF-Area / SAM, SAM64, SAM63, IAM, GAM specific Data) + mt: 'make Token' - create a Token 'from scratch' (guided) + rs: 'remove segment' - removes a Segment (except Segment 00, but this can be set to valid=0 for Master-Token) + cc: 'check Segment-CRC' - checks & calculates (if check failed) the Segment-CRC of all Segments + ck: 'check KGH-CRC' - checks the and calculates a 'Kaba Group Header' if one was detected + 'Kaba Group Header CRC calculation' + tk: 'toggle KGH' - toggle the (script-internal) flag for kgh-calculation for a segment + xc: 'etra c' - show string that was used to calculate the kgh-crc of a segment + +dlc: 'dump Legic-Cash' - show balance and checksums of a Legic-Cash Segment +elc: 'edit Legic-Cash' - edit values of a Legic-Cash Segment + +d3p: 'dump 3rd Party' - show balance, history and checksums of a (yet) unknown 3rd-Party Cash Segment +e3p: 'edit 3rd Party' - edit Data in 3rd-Party Cash Segment + +tac: 'toggle ansicolors' - switch on and off the colored text-output of this script + default can be changed by setting the variable 'colored_output' to false +]] +currentTag="inTAG" + --- -- curency-codes for Legic-Cash-Segments (ISO 4217) local currency = { - ["03d2"]="EUR", + ["03D2"]="EUR", ["0348"]="USD", ["033A"]="GBP", ["02F4"]="CHF" @@ -237,7 +248,11 @@ end --- -- Usage help function help() - print(desc) + -- the proxmark3 client can't handle such long strings + -- by breaking up at specific points it still looks good. + print(string.sub(desc, 0, 1961)) + print(string.sub(desc, 1962, 3925)) + print(string.sub(desc, 3926, #desc)) print("Version: "..version) print("Example usage: "..example) end @@ -254,7 +269,6 @@ local function padString(str) if (#str == 1) then return '0'..str end - return str end @@ -305,18 +319,6 @@ function xorBytes(inBytes, crc) end end ---- --- check availability of file -function file_check(file_name) - local file_found = io.open(file_name, "r") - if file_found == nil then - return false - else - file_found:close() - return true - end -end - --- -- split csv-string into table local function split(str, sep) @@ -330,6 +332,24 @@ local function split(str, sep) return fields end +--- +-- check availability of file +function file_check(file_name) + if not file_name then return false, "" end + + local arr = split(file_name, ".") + local path = core.search_file(arr[1], "."..arr[2]) + if (path == nil) then return false end + + local file_found = io.open(path, "r") + if file_found == nil then + return false, "" + else + file_found:close() + return true, path + end +end + --- -- put a string into a bytes-table function str2bytes(s) @@ -348,7 +368,7 @@ end function bytesToTable(bytes, bstart, bend) local t={} for i=0, (bend-bstart) do - t[i]=bytes[bstart+i] + t[i]=padString(bytes[bstart+i]) end return t end @@ -358,8 +378,13 @@ end function getInputBytes(infile) local line local bytes = {} - local fhi,err = io.open(infile,"rb") - if err then oops("failed to read from file ".. infile); return false; end + + local arr = split(infile, ".") + local path = core.search_file(arr[1], "."..arr[2]) + if (path == nil) then oops("failed to read from file ".. infile); return false; end + + local fhi,err = io.open(path,"rb") + if err then oops("failed to read from file ".. path); return false; end file_data = fhi:read("*a"); for i = 1, #file_data do @@ -367,7 +392,7 @@ function getInputBytes(infile) end fhi:close() if (bytes[7]=='00') then return false end - print(#bytes .. " bytes from "..infile.." loaded") + print(#bytes .. " bytes from "..path.." loaded") return bytes end @@ -613,12 +638,13 @@ local function readFile(filename) print(accyan) local bytes = {} local tag = {} - if file_check(filename) == false then + + local res, path = file_check(filename) + if not res then return oops("input file: "..acyellow..filename..acoff.." not found") end - bytes = getInputBytes(filename) - + bytes = getInputBytes(path) if bytes == false then return oops('couldnt get input bytes') end -- make plain bytes @@ -640,12 +666,14 @@ local function save_BIN(data, filename) local fn = filename..ext -- Make sure we don't overwrite a file - while file_check(fn) do + local res, path = file_check(fn) + while res == false do fn = filename..ext:gsub(ext, "-"..tostring(counter)..ext) counter = counter + 1 + res, path = file_check(fn) end - outfile = io.open(fn, 'wb') + outfile = io.open(path, 'wb') local i = 1 while data[i] do @@ -660,17 +688,19 @@ end -- write bytes to file function writeFile(bytes, filename) local emlext = ".eml" + local res, path if (filename ~= 'MyLegicClone') then - if (file_check(filename..emlext)) then - local answer = confirm("\nthe output-file "..filename..emlext.." already exists!\nthis will delete the previous content!\ncontinue?") + res, path = file_check(filename..emlext) + if res then + local answer = confirm("\nthe output-file "..path.." already exists!\nthis will delete the previous content!\ncontinue?") if not answer then return print("user abort") end end end local line local bcnt = 0 - local fho, err = io.open(filename..emlext, "w") + local fho, err = io.open(path, "w") if err then - return oops("OOps ... failed to open output-file ".. filename..emlext) + return oops("OOps ... failed to open output-file ".. path) end bytes = xorBytes(bytes, bytes[5]) @@ -692,11 +722,10 @@ function writeFile(bytes, filename) end fho:close() - -- save binary - local fn_bin, fn_bin_num = save_BIN(bytes, filename) - print("\nwrote "..acyellow..(#bytes * 3)..acoff.." bytes to " ..acyellow..filename..emlext..acoff) + -- save binary + local fn_bin, fn_bin_num = save_BIN(bytes, filename) if fn_bin and fn_bin_num then print("\nwrote "..acyellow..fn_bin_num..acoff.." bytes to BINARY file "..acyellow..fn_bin..acoff) end @@ -731,10 +760,16 @@ function readFromPM3() infile=getRandomTempName() core.console("hf legic dump -f "..infile) tag=readFile(infile..".bin") - os.remove(infile) - os.remove(infile..".bin") - os.remove(infile..".eml") - os.remove(infile..".json") + + res, path = file_check(infile..".bin") + if not res then return nil end + os.remove(path) + + res, path = file_check(infile..".eml") + os.remove(path) + + res, path = file_check(infile..".json") + os.remove(path) return tag end @@ -758,16 +793,20 @@ end --- -- save mapping to file local function saveTagMap(map, filename) + + local res, path + if #filename > 0 then - if file_check(filename) then - local answer = confirm("\nthe output-file "..acyellow..filename..acoff.." alredy exists!\nthis will delete the previous content!\ncontinue?") + res, path = file_check(filename) + if res then + local answer = confirm("\nthe output-file "..acyellow..path..acoff.." alredy exists!\nthis will delete the previous content!\ncontinue?") if not answer then return print("user abort") end end end local line - local fho,err = io.open(filename, "w") - if err then oops("OOps ... failed to open output-file "..acyellow..filename..acoff) end + local fho,err = io.open(path, "w") + if err then oops("OOps ... failed to open output-file "..acyellow..path..acoff) end -- write line to new file for k, v in pairs(map) do @@ -842,10 +881,13 @@ function loadTagMap(filename) local line, fields local temp={} local offset=0 - if not file_check(filename) then - return oops("input file: "..acyellow..filename..acoff.." not found") - else - local fhi,err = io.open(filename) + + local res, path = file_check(filename) + if not res then + return oops("input file: "..acyellow..filename..acoff.." not found") + else + + local fhi,err = io.open(path) while true do line = fhi:read() if line == nil then @@ -907,20 +949,21 @@ function dumpTagMap(tag, tagMap) end if (isPosCrc8(tagMap, v['start'])>0) then if ( checkMapCrc8(tagMap, bytes, isPosCrc8(tagMap, v['start']) ) ) then - io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..acgreen..v['name']..acoff..":") + io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..acgreen..v['name']..acoff) else - io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..acred..v['name']..acoff..":") + io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..acred..v['name']..acoff) end else - io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..((v['highlight']) and acmagenta or acyellow)..v['name']..acoff..":") - end - temp="" - for i=((string.len(v['name']))/10), 2 do - temp=temp.."\t" + io.write("("..("%04d"):format(v['start']).."-"..("%04d"):format(v['end'])..") "..((v['highlight']) and acmagenta or acyellow)..v['name']..acoff) end + + temp = "" + while (#v['name'] + temp:len()) < 20 do temp = temp.." " end + for i=v['start'], v['end'] do temp=temp..bytes[i].." " end + print(temp) lastend=v['end'] end @@ -966,11 +1009,23 @@ end -- edit existing Map function editTagMap(tag, tagMap) local t = [[ - Data: dm = show dr = dump raw -Mappings: im = insert am = add rm = remove - CRC8: ac8 = add sc8 = show rc8 = remove - : q = exit h = Help +]]..acc..[[Data]]..acr..[[ + + ]]..acy..[[dm]]..acr..[[ - show ]]..acy..[[dr]]..acr..[[ - dump raw + +]]..acc..[[Mappings]]..acr..[[ + + ]]..acy..[[im]]..acr..[[ - insert ]]..acy..[[am]]..acr..[[ - add + ]]..acy..[[rm]]..acr..[[ - remove ]]..acy..[[mas]]..acr..[[ - map all segments + +]]..acc..[[CRC8]]..acr..[[ + + ]]..acy..[[ac8]]..acr..[[ - add ]]..acy..[[sc8]]..acr..[[ - show + ]]..acy..[[rc8]]..acr..[[ - remove + + ]]..acy..[[q]]..acr..[[ - exit ]]..acy..[[h]]..acr..[[ - Help ]] + --if(#tagMap.mappings==0) then oops("no mappings in tagMap"); return tagMap end print("tagMap edit-mode submenu") repeat @@ -1447,7 +1502,10 @@ function dumpLegicCash(tag, x) print("--------------------------------\n\tLegic-Cash Values\n--------------------------------") local limit, curr, balance, rid, tcv -- currency of balance & limit - curr=currency[tag.SEG[x].data[8]..tag.SEG[x].data[9]] + curr=string.upper(tag.SEG[x].data[8]..tag.SEG[x].data[9]) + if currency[curr] ~= nil then + curr = currency[curr] + end -- maximum balance limit=string.format("%4.2f", tonumber(tag.SEG[x].data[10]..tag.SEG[x].data[11]..tag.SEG[x].data[12], 16)/100) -- current balance @@ -1784,17 +1842,17 @@ end --- -- edit Segment Data -function editSegmentData(data) +function editSegmentData(data, uid) io.write("\n") if istable(data) == false then print("no Segment-Data found") end - local lc = check4LegicCash(data) + local lc = check4LegicCash(data, uid) for i=0, #data-1 do data[i]=input(accyan.."Data"..i..acoff..": ", data[i]) end if (lc) then - data = fixLegicCash(data) + data = fixLegicCash(data, uid) end return data end @@ -1917,7 +1975,7 @@ function autoSelectSegment(tag, s) repeat io.write(". ") x=x-1 - res=check4LegicCash(tag.SEG[x].data) + res=check4LegicCash(tag.SEG[x].data, uid) until ( res or x==0 ) end --- @@ -2011,7 +2069,7 @@ end --- -- edit Legic Cash -function editLegicCash(data) +function editLegicCash(data, uid) local limit, curr, balance, rid, tcv -- currency of balance & limit curr=currency[data[8]..data[9]] @@ -2064,12 +2122,12 @@ function editLegicCash(data) data[20]=string.sub(rid, 5, 6) end - return fixLegicCash(data) + return fixLegicCash(data, uid) end --- -- chack for signature of a 'Legic-Cash-Segment' -function check4LegicCash(data) +function check4LegicCash(data, uid) if(#data==32) then local stamp_len=(#data-25) local stamp="" @@ -2077,9 +2135,9 @@ function check4LegicCash(data) stamp=stamp..data[i].." " end if (data[7]=="01") then - if (("%04x"):format(utils.Crc16(dumpTable(data, "", 0, 12))) == data[13]..data[14]) then - if (("%04x"):format(utils.Crc16(dumpTable(data, "", 15, 20))) == data[21]..data[22]) then - if (("%04x"):format(utils.Crc16(dumpTable(data, "", 23, 29))) == data[30]..data[31]) then + if (("%04x"):format(utils.Crc16Legic(dumpTable(data, "", 0, 12), uid)) == data[13]..data[14]) then + if (("%04x"):format(utils.Crc16Legic(dumpTable(data, "", 15, 20), uid)) == data[21]..data[22]) then + if (("%04x"):format(utils.Crc16Legic(dumpTable(data, "", 23, 29), uid)) == data[30]..data[31]) then io.write(accyan.."Legic-Cash Segment detected "..acoff) return true end @@ -2156,7 +2214,7 @@ end --- -- repair / fix crc's of a 'Legic-Cash-Segment' -function fixLegicCash(data) +function fixLegicCash(data, uid) if(#data==32 and data[7]=="01") then local crc1, crc2, crc3 -- set shadow-balance equal to balance @@ -2168,9 +2226,9 @@ function fixLegicCash(data) data[27]=data[19] data[28]=data[20] -- calculate all crc's - crc1=("%04x"):format(utils.Crc16(dumpTable(data, "", 0, 12))) - crc2=("%04x"):format(utils.Crc16(dumpTable(data, "", 15, 20))) - crc3=("%04x"):format(utils.Crc16(dumpTable(data, "", 23, 29))) + crc1=("%04x"):format(utils.Crc16Legic(dumpTable(data, "", 0, 12), uid)) + crc2=("%04x"):format(utils.Crc16Legic(dumpTable(data, "", 15, 20), uid)) + crc3=("%04x"):format(utils.Crc16Legic(dumpTable(data, "", 23, 29), uid)) -- set crc's data[13]=string.sub(crc1, 1, 2) data[14]=string.sub(crc1, 3, 4) @@ -2290,25 +2348,25 @@ function modifyHelp() Data I/O Segment Manipulation Token-Data ----------------- -------------------- --------------------- - rt => read Tag as => add Segment mt => make Token - wt => write Tag es => edit Segment Header et => edit Token data - ed => edit Segment Data tk => toggle KGH-Flag - File I/O rs => remove Segment - ----------------- cc => check Segment-CRC - lf => load bin File ck => check KGH - sf => save eml/bin File ds => dump Segments - xf => xor to File + ]]..acy..[[rt]]..acr..[[ => read Tag ]]..acy..[[as]]..acr..[[ => add Segment ]]..acy..[[mt]]..acr..[[ => make Token + ]]..acy..[[wt]]..acr..[[ => write Tag ]]..acy..[[es]]..acr..[[ => edit Segment Header ]]..acy..[[et]]..acr..[[ => edit Token data + ]]..acy..[[ed]]..acr..[[ => edit Segment Data ]]..acy..[[tk]]..acr..[[ => toggle KGH-Flag + File I/O ]]..acy..[[rs]]..acr..[[ => remove Segment + ----------------- ]]..acy..[[cc]]..acr..[[ => check Segment-CRC + ]]..acy..[[lf]]..acr..[[ => load bin File ]]..acy..[[ck]]..acr..[[ => check KGH + ]]..acy..[[sf]]..acr..[[ => save eml/bin File ]]..acy..[[ds]]..acr..[[ => dump Segments + ]]..acy..[[xf]]..acr..[[ => xor to File Virtual Tags tagMap (partial) known Segments -------------------------------- --------------------- --------------------------- - ct => copy mainTag to backupTag mm => make (new) Map dlc => dump Legic-Cash - tc => copy backupTag to mainTag em => edit Map submenu elc => edit Legic-Cash - tt => switch mainTag & backupTag lm => load map from file d3p => dump 3rd-Party-Cash - di => dump mainTag sm => save map to file e3p => edit 3rd-Party-Cash - do => dump backupTag + ]]..acy..[[ct]]..acr..[[ => copy mainTag to backupTag ]]..acy..[[mm]]..acr..[[ => make (new) Map ]]..acy..[[dlc]]..acr..[[ => dump Legic-Cash + ]]..acy..[[tc]]..acr..[[ => copy backupTag to mainTag ]]..acy..[[em]]..acr..[[ => edit Map submenu ]]..acy..[[elc]]..acr..[[ => edit Legic-Cash + ]]..acy..[[tt]]..acr..[[ => switch mainTag & backupTag ]]..acy..[[lm]]..acr..[[ => load map from file ]]..acy..[[d3p]]..acr..[[ => dump 3rd-Party-Cash + ]]..acy..[[di]]..acr..[[ => dump mainTag ]]..acy..[[sm]]..acr..[[ => save map to file ]]..acy..[[e3p]]..acr..[[ => edit 3rd-Party-Cash + ]]..acy..[[do]]..acr..[[ => dump backupTag - h => this help q => quit + ]]..acy..[[h]]..acr..[[ => this help ]]..acy..[[q]]..acr..[[ => quit ]] return t end @@ -2322,8 +2380,10 @@ function modifyMode() --- -- helptext ["h"] = function(x) - print(" Version: "..version); - print(modifyHelp().."\n".."tags im Memory: "..(istable(inTAG) and ((currentTag=='inTAG') and acgreen.."*mainTAG"..acoff or "mainTAG") or "").." "..(istable(backupTAG) and ((currentTag=='backupTAG') and acgreen.."*backupTAG"..acoff or "backupTAG") or "")) + print(" Version: "..acgreen..version..acr); + print(modifyHelp()) + print("\n".."tags im Memory: "..(istable(inTAG) and ((currentTag=='inTAG') and acgreen.."*mainTAG"..acoff or "mainTAG") or "").." "..(istable(backupTAG) and ((currentTag=='backupTAG') and acgreen.."*backupTAG"..acoff or "backupTAG") or "")) + print("") end, --- -- read real Tag with PM3 into virtual 'mainTAG' @@ -2364,16 +2424,16 @@ function modifyMode() --- -- load file into mainTAG ["lf"] = function(x) - - if (type(x)=='string' and file_check(x)) then + if (x and not x=="" and type(x)=='string' and file_check(x)) then filename = x else filename = input("enter filename: ", "legic.temp") end inTAG=readFile(filename) -- check for existing tagMap - if (file_check(filename..".map")) then - if(confirm(accyan.."Mapping-File for "..acoff..filename..accyan.." found - load it also?"..acoff)) then + local res, path = file_check(filename..".map") + if res then + if(confirm(accyan.."Mapping-File for "..acoff..path..accyan.." found - load it also?"..acoff)) then tagMap=loadTagMap(filename..".map") end end @@ -2413,7 +2473,7 @@ function modifyMode() for i=0, #inTAG.SEG do if(check43rdPartyCash1(uid, inTAG.SEG[i].data)) then io.write(accyan.."in Segment index: "..inTAG.SEG[i].index ..acoff.. "\n") - elseif(check4LegicCash(inTAG.SEG[i].data)) then + elseif(check4LegicCash(inTAG.SEG[i].data, uid)) then io.write(accyan.."in Segment index: "..inTAG.SEG[i].index..acoff.."\n") lc=true; lci=inTAG.SEG[i].index; @@ -2517,10 +2577,11 @@ function modifyMode() --- -- edit data-portion of single segment ["ed"] = function(x) - if (type(x)=="string" and string.len(x)>0) then sel=tonumber(x,10) - else sel=selectSegment(inTAG) end + if (type(x) == "string" and string.len(x)>0) then sel=tonumber(x,10) + else sel = selectSegment(inTAG) end if (istable(inTAG.SEG[sel])) then - inTAG.SEG[sel].data=editSegmentData(inTAG.SEG[sel].data) + local uid = inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2 + inTAG.SEG[sel].data = editSegmentData(inTAG.SEG[sel].data, uid) end end, --- @@ -2587,13 +2648,15 @@ function modifyMode() else x = selectSegment(inTAG) end - inTAG.SEG[x].data=fixLegicCash(inTAG.SEG[x].data) + local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2 + inTAG.SEG[x].data=fixLegicCash(inTAG.SEG[x].data, uid) end, --- - -- edit legic-cash values fixLegicCash(data) + -- edit legic-cash values fixLegicCash(data, uid) ["elc"] = function(x) x=autoSelectSegment(inTAG, "legiccash") - inTAG.SEG[x].data=editLegicCash(inTAG.SEG[x].data) + local uid=inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2 + inTAG.SEG[x].data=editLegicCash(inTAG.SEG[x].data, uid) end, --- -- dump legic-cash human-readable @@ -2718,7 +2781,7 @@ function modifyMode() } repeat -- default message / prompt - ic=input("Legic command? ('h' for help - 'q' for quit)", "h") + ic=input("Legic command? ('"..acy.."h"..acr.."' for help - '"..acy.."q"..acr.."' for quit)", acy.."h"..acr) -- command actions decisions (first match, longer commands before shorter) if (type(actions[string.lower(string.sub(ic,0,3))])=='function') then actions[string.lower(string.sub(ic,0,3))](string.sub(ic,5)) @@ -2737,10 +2800,13 @@ end function main(args) -- set init colors/switch (can be toggled with 'tac' => 'toggle ansicolors') load_colors(colored_output) - if (#args == 0 ) then modifyMode() end + if (#args == 0 ) then modifyMode() end --- variables - local inTAG, backupTAG, outTAG, outfile, interactive, crc, ofs, cfs, dfs - -- just a spacer for better readability + local inTAG, backupTAG, outTAG, outfile, interactive, crc + local ofs=false + local cfs=false + local dfs=false + -- just a spacer for better readability print() --- parse arguments for o, a in getopt.getopt(args, 'hrmi:do:c:') do @@ -2749,7 +2815,7 @@ function main(args) -- read tag from PM3 if o == "r" then inTAG=readFromPM3() end -- input file - if o == "i" then inTAG=readFile(a) end + if o == "i" then inTAG=readFile(a) end -- dump virtual-Tag if o == "d" then dfs=true end -- interacive modifying @@ -2787,7 +2853,10 @@ function main(args) -- write to outfile if (bytes) then - writeFile(bytes, outfile) + + if (outfile) then + writeFile(bytes, outfile) + end --- read real tag into virtual tag -- inTAG=readFromPM3() end --- or simply use the bytes that where wriiten diff --git a/client/luascripts/hf_legic_clone.lua b/client/luascripts/hf_legic_clone.lua index d41046d65..d9a86dc81 100644 --- a/client/luascripts/hf_legic_clone.lua +++ b/client/luascripts/hf_legic_clone.lua @@ -168,7 +168,7 @@ local function help() print(example) end -- read LEGIC data -local function readlegicdata(offset, length, iv) +local function readlegicdata(offset, len, iv) -- Read data local d0 = ('%04X%04X%02X'):format(offset, len, iv) local c = Command:newNG{cmd = cmds.CMD_HF_LEGIC_READER, data = d0} diff --git a/client/luascripts/hf_mf_dump_luxeo.lua b/client/luascripts/hf_mf_dump_luxeo.lua index aaf7d572f..f645d3d7b 100644 --- a/client/luascripts/hf_mf_dump_luxeo.lua +++ b/client/luascripts/hf_mf_dump_luxeo.lua @@ -231,13 +231,26 @@ local function main(args) print(acblue.."UID: "..tag.uid..acoff) print(acblue..string.format("XTEA key: %08X %08X %08X %08X", xteakey[0], xteakey[1], xteakey[2], xteakey[3])..acoff) - edata, cdata = readtag("415A54454B4D", xteakey) + local keys = { + "415A54454B4D", + "4B6A43059B64", + "C8BE6250C9C5", + } + + for i, key in ipairs(keys) do + edata, cdata = readtag(key, xteakey) + if edata and cdata then + goto continue + end + end if edata == nil or cdata == nil then print("ERROR Reading tag!") return nil end + ::continue:: + print("Ciphered data:") for key,value in ipairs(edata) do print(value) diff --git a/client/luascripts/hf_mf_sim_hid.lua b/client/luascripts/hf_mf_sim_hid.lua index 4091a10b5..3f3933872 100644 --- a/client/luascripts/hf_mf_sim_hid.lua +++ b/client/luascripts/hf_mf_sim_hid.lua @@ -7,7 +7,7 @@ local ansicolors = require('ansicolors') copyright = '' author = "Michael Micsen" -version = 'v0.0.1' +version = 'v0.0.2' desc = [[ Perform simulation of Mifare credentials with HID encoding This script only supports: H10301 @@ -17,12 +17,12 @@ example = [[ script run hf_mf_sim_hid.lua -f 1 -c 10000 ]] usage = [[ -script run hf_mf_sim_hid.lua -f facility -c card_number +script run hf_mf_sim_hid.lua -f -c ]] arguments = [[ -h : this help - -f : facility id - -c : starting card id + -f : facility code + -c : card number ]] local DEBUG = true --local bxor = bit32.bxor @@ -126,7 +126,6 @@ local function cardHex(i, f) sentinel = lshift(1, 26) bits = bor(bits, sentinel) - return ('%08x'):format(bits) end --- @@ -146,15 +145,14 @@ local function main(args) if o == 'h' then return help() end if o == 'f' then if isempty(a) then - print('You did not supply a facility code, using 0') + print('Defaulting to facility code 0') facility = 0 else facility = a end end if o == 'c' then - print(a) - if isempty(a) then return oops('You must supply the flag -c (card number)1') end + if isempty(a) then return oops('You must supply a card number') end cardnum = a end end @@ -162,23 +160,27 @@ local function main(args) --Due to my earlier complaints about how this specific getopt library --works, specifying ':' does not enforce supplying a value, thus we --need to do these checks all over again. - if isempty(cardnum) then return oops('You must supply the flag -c (card number)2') end + if isempty(cardnum) then return oops('You must supply a card number') end + --If the facility ID is non specified, ensure we code it as zero if isempty(facility) then - print('Using 0 for the facility code as -f was not supplied') + print('Defaulting to facility code 0') facility = 0 end -- Write the MAD to read for a Mifare HID credential - core.console('hf mf esetblk -b 1 -d 1B014D48000000000000000000000000') - core.console('hf mf esetblk -b 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A') + core.console('hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000') + core.console('hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A') --Write the sector trailer for the credential sector - core.console('hf mf esetblk -b 7 -d 484944204953787788AA204752454154') + core.console('hf mf esetblk --blk 7 -d 484944204953787788AA204752454154') local cardh = cardHex(cardnum, facility) - print('Hex') - print(cardh) - core.console( ('hf mf esetblk -b 5 -d 020000000000000000000000%s'):format(cardh) ) + print('Facility Code... ' .. facility) + print('Card number..... ' .. cardnum) + print('Hex............. ' .. cardh) + print('') + + core.console( ('hf mf esetblk --blk 5 -d 020000000000000000000000%s'):format(cardh) ) core.console('hf mf sim --1k -i') end diff --git a/client/luascripts/hf_mf_tnp3_sim.lua b/client/luascripts/hf_mf_tnp3_sim.lua index d44a883fd..f04c8d7fa 100644 --- a/client/luascripts/hf_mf_tnp3_sim.lua +++ b/client/luascripts/hf_mf_tnp3_sim.lua @@ -87,12 +87,6 @@ local function ExitMsg(msg) print() end -local function writedumpfile(infile) - t = infile:read('*all') - len = string.len(t) - local len,hex = bin.unpack(('H%d'):format(len),t) - return hex -end -- blocks with data -- there are two dataareas, in block 8 or block 36, ( 1==8 , -- checksum type = 0, 1, 2, 3 diff --git a/client/luascripts/hf_mf_uid_downgrade.lua b/client/luascripts/hf_mf_uid_downgrade.lua new file mode 100644 index 000000000..a8d243888 --- /dev/null +++ b/client/luascripts/hf_mf_uid_downgrade.lua @@ -0,0 +1,131 @@ +-- +-- hf_mf_uid_downgrade.lua - Downgrading to UID-based Mifare Classic +-- Adapted from hf_mf_sim_hid.lua +-- Created 29.11.2023 + +local getopt = require('getopt') +local ansicolors = require('ansicolors') + +copyright = '' +author = "Adam Foster (evildaemond)" +version = 'v0.0.1' +desc = [[ +Convert a facility code and card number to a Mifare Classic UID, which can be used as part of a downgrade attack. + +Working on HID Readers with any of the following enabled +- Generic 14333A +- Mifare Classic SIO + CSN +- Mifare Classic CSN +]] +example = [[ + -- Downgrade a card with the facility code of 146 and card number of 5 + script run hf_mf_uid_downgrade.lua -f 146 -c 5 +]] +usage = [[ +script run hf_mf_uid_downgrade.lua -f -c +]] +arguments = [[ + -h : this help + -f : facility code + -c : card number +]] + +--local bxor = bit32.bxor +local bor = bit32.bor +local lshift = bit32.lshift +--- +--- +-- Usage help +local function help() + print(copyright) + 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 +--- +-- Exit message +local function exitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end + +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, errr +end + + +local function isempty(s) + return s == nil or s == '' +end + +-- Function to combine two hexadecimal strings +local function convertToUID(hex_str1, hex_str2) + local hex1 = string.format('%04x', hex_str1) + local hex2 = string.format('%04x', hex_str2) + + local combined_hex = hex1 .. hex2 + local reversed_hex = '' + for i = #combined_hex, 1, -2 do + reversed_hex = reversed_hex .. string.sub(combined_hex, i - 1, i) + end + return reversed_hex +end + +--- +-- main +local function main(args) + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + if #args == 0 then return help() end + + --I really wish a better getopt function would be brought in supporting + --long arguments, but it seems this library was chosen for BSD style + --compatibility + for o, a in getopt.getopt(args, 'f:c:h') do + if o == 'h' then return help() end + if o == 'f' then + if isempty(a) then return oops('You must supply a facility code') end + facility = a + end + if o == 'c' then + if isempty(a) then return oops('You must supply a card number') end + cardnum = a + end + end + + --Due to my earlier complaints about how this specific getopt library + --works, specifying ':' does not enforce supplying a value, thus we + --need to do these checks all over again. + if isempty(facility) then return oops('You must supply a facility code') end + if isempty(cardnum) then return oops('You must supply a card number') end + + local cardh = convertToUID(facility, cardnum) + + print('Facility Code... ' .. facility) + print('Card number..... ' .. cardnum) + print('UID............. ' .. cardh) + print('') + + -- Print emulation or writing string based on flags + print('Emulate via PM3:') + print('hf mf sim --1k -u', cardh, '\n') + + print('Write to Mifare Classic Card (Gen1a or Above):') + print('hf mf csetuid -u', cardh) + +end + +main(args) diff --git a/client/luascripts/hf_mf_uidbruteforce.lua b/client/luascripts/hf_mf_uidbruteforce.lua index 4835ba03b..88dca4273 100644 --- a/client/luascripts/hf_mf_uidbruteforce.lua +++ b/client/luascripts/hf_mf_uidbruteforce.lua @@ -124,7 +124,7 @@ local function main(args) local c = string.format( command, n ) print('Running: "'..c..'"') core.console(c) - core.console('msleep '..timeout); + core.console('msleep -t'..timeout); core.console('hw ping') end diff --git a/client/luascripts/hf_mf_ultimatecard.lua b/client/luascripts/hf_mf_ultimatecard.lua index 09b53937f..819f68b40 100644 --- a/client/luascripts/hf_mf_ultimatecard.lua +++ b/client/luascripts/hf_mf_ultimatecard.lua @@ -50,20 +50,20 @@ arguments = [[ -c read magic configuration -u UID (8-20 hexsymbols), set UID on tag -t tag type to impersonate - 1 = Mifare Mini S20 4-byte + 1 = Mifare Mini S20 4-byte 2 = Mifare Mini S20 7-byte 15 = NTAG 210 3 = Mifare Mini S20 10-byte 16 = NTAG 212 4 = Mifare 1k S50 4-byte 17 = NTAG 213 5 = Mifare 1k S50 7-byte 18 = NTAG 215 - 6 = Mifare 1k S50 10-byte 19 = NTAG 216 + 6 = Mifare 1k S50 10-byte 19 = NTAG 216 7 = Mifare 4k S70 4-byte 20 = NTAG I2C 1K 8 = Mifare 4k S70 7-byte 21 = NTAG I2C 2K 9 = Mifare 4k S70 10-byte 22 = NTAG I2C 1K PLUS *** 10 = UL - NOT WORKING FULLY 23 = NTAG I2C 2K PLUS *** 11 = UL-C - NOT WORKING FULLY 24 = NTAG 213F 12 = UL EV1 48b 25 = NTAG 216F - 13 = UL EV1 128b - *** 14 = UL Plus - NOT WORKING YET + 13 = UL EV1 128b + *** 14 = UL Plus - NOT WORKING YET -p NTAG password (8 hexsymbols), set NTAG password on tag. -a NTAG pack ( 4 hexsymbols), set NTAG pack on tag. @@ -297,7 +297,7 @@ return true, 'Ok' end --- -- calculate block0 -local function calculate_block0(useruid) +local function calculate_block0(useruid) local uidbytes = utils.ConvertHexToBytes(useruid) local i = 1 local bcc = bxor(uidbytes[i], uidbytes[i+1]); @@ -737,7 +737,7 @@ local function set_type(tagtype) write_uid('04112233445566') write_otp('00000000') -- Setting OTP to default 00 00 00 00 write_version('0004030101000b03') -- UL-EV1 (48) 00 04 03 01 01 00 0b 03 - elseif tagtype == 12 then + elseif tagtype == 13 then print('Setting: Ultimate Magic card to UL-EV1 128') connect() send("CF".._key.."F001010000000003000978009102DABC19101011121314151644000000") diff --git a/client/luascripts/hf_mfp_raw.lua b/client/luascripts/hf_mfp_raw.lua index e28c274a8..28fec1474 100644 --- a/client/luascripts/hf_mfp_raw.lua +++ b/client/luascripts/hf_mfp_raw.lua @@ -348,7 +348,7 @@ function main(args) -- commandString = AUTH_FIRST .. "0190" .. "00" -- response = sendRaw(commandString, true, true) - -- Power off the Proxmark + -- Power off the Proxmark3 sendRaw(POWEROFF, false, false) lib14a.disconnect() diff --git a/client/luascripts/lf_em_tearoff.lua b/client/luascripts/lf_em_tearoff.lua index 2aa0dd5ee..c26979d9a 100644 --- a/client/luascripts/lf_em_tearoff.lua +++ b/client/luascripts/lf_em_tearoff.lua @@ -82,16 +82,16 @@ local function main(args) sd = sd or 2000 ed = ed or 2100 - if #password ~= 8 then - password = '' + if password ~= '' and #password ~= 8 then + return oops('password must be 4 hex bytes') end if #wr_value ~= 8 then - wr_value = 'FFFFFFFF' + return oops('write value must be 4 hex bytes') end if #rd_value ~= 8 then - rd_value = 'FFFFFFFF' + return oops('read value must be 4 hex bytes') end if sd > ed then @@ -114,7 +114,7 @@ local function main(args) local set_tearoff_delay = 'hw tearoff --delay %d' local enable_tearoff = 'hw tearoff --on' - local wr_template = 'lf em 4x05_write %s %s %s' + local wr_template = 'lf em 4x05 write --addr %s --data %s --pwd %s' -- init addr to value core.console(wr_template:format(addr, wr_value, password)) diff --git a/client/luascripts/lf_ident_json.lua b/client/luascripts/lf_ident_json.lua index 73794547c..11657910d 100644 --- a/client/luascripts/lf_ident_json.lua +++ b/client/luascripts/lf_ident_json.lua @@ -113,7 +113,7 @@ local function getDefault(block0) block0 = block0:upper() - local T55X7_DEFAULT_CONFIG_BLOCK = '000880E8' --// compat mode, RF/32, manchester, STT, 7 data blocks + local T55X7_DEFAULT_CONFIG_BLOCK = '000880E8' --// compat mode, RF/32, manchester, STT, 7 data blocks local T55X7_RAW_CONFIG_BLOCK = '000880E0' --// compat mode, RF/32, manchester, 7 data blocks local T55X7_EM_UNIQUE_CONFIG_BLOCK = '00148040' --// emulate em4x02/unique - compat mode, manchester, RF/64, 2 data blocks -- FDXB requires data inversion and BiPhase 57 is simply BipHase 50 inverted, so we can either do it using the modulation scheme or the inversion flag diff --git a/client/src/pm3.py b/client/pyscripts/pm3.py similarity index 100% rename from client/src/pm3.py rename to client/pyscripts/pm3.py diff --git a/client/pyscripts/pm3_help2json.py b/client/pyscripts/pm3_help2json.py index 8099b5d7c..34faedec5 100755 --- a/client/pyscripts/pm3_help2json.py +++ b/client/pyscripts/pm3_help2json.py @@ -62,21 +62,19 @@ def build_arg_parser(): def build_help_regex(): - """The regex uses to parse the full text output of help data from the pm3 client.""" - # Reads the divider followed by the command itself re_command = r'-{87}\n(?P.+)\n' # Reads if the command is available offline re_offline = r'available offline: (?Pyes|no)\n+' # Reads the description lines - re_description = r'(?P(?:.+\n)+)\n+' + re_description = r'(?P\n[\s\S]*?(?=usage:))' # Reads the usage string re_usage = r'(?:usage:\n(?P(?:.+\n)+)\n+)?' # Reads the options and there individual descriptions re_options = r'(?:options:\n(?P(?:.+\n)+)\n+)?' # Reads the notes and examples - re_notes = r'(?:examples\/notes:\n(?P(?:.+\n)+)\n+)?' + re_notes = r'(?:examples\/notes:\n(?P[\s\S]*?(?=(===|---|\n\n))))' # Combine them into a single regex object - re_full = re.compile(re_command+re_offline+re_description+re_usage+re_options+re_notes, re.MULTILINE); + re_full = re.compile(re_command+re_offline+re_description+re_usage+re_options+re_notes, re.MULTILINE) return re_full diff --git a/client/pyscripts/pm3_help2list.py b/client/pyscripts/pm3_help2list.py index 7c27d23fe..a896e5546 100755 --- a/client/pyscripts/pm3_help2list.py +++ b/client/pyscripts/pm3_help2list.py @@ -12,7 +12,7 @@ This version - Iceman Note: - This script is used as a helper script to generate the pm3line_vocabulory.h file. + This script is used as a helper script to generate the pm3line_vocabulary.h file. It need a working proxmark3 client to extract the help text. Ie: this script can't be used inside the normal build sequence. @@ -65,8 +65,8 @@ def main(): // readline auto complete utilities //----------------------------------------------------------------------------- -#ifndef PM3LINE_VOCABULORY_H__ -#define PM3LINE_VOCABULORY_H__ +#ifndef PM3LINE_VOCABULARY_H__ +#define PM3LINE_VOCABULARY_H__ #ifdef __cplusplus extern "C" { @@ -74,12 +74,12 @@ extern "C" { #include -typedef struct vocabulory_s { +typedef struct vocabulary_s { bool offline; const char *name; -} vocabulory_t; +} vocabulary_t; -const static vocabulory_t vocabulory[] = {\n""") +const static vocabulary_t vocabulary[] = {\n""") for key, values in command_data.items(): offline = 0 @@ -88,7 +88,7 @@ const static vocabulory_t vocabulory[] = {\n""") cmd = values['command'] - args.output_file.write(' {{ {}, "{}" }}, \n'.format(offline, cmd)) + args.output_file.write(' {{ {}, "{}" }},\n'.format(offline, cmd)) args.output_file.write(""" {0, NULL}\n}; diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index 45df737e5..c90edb126 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -49,7 +49,7 @@ }, { "AID": "F21190", - "Vendor": "Metropolitan Transportation Commission", + "Vendor": "Metropolitan Transportation Commission / Cubic", "Country": "US", "Name": "Clipper Card", "Description": "", @@ -81,7 +81,7 @@ }, { "AID": "784000", - "Vendor": "NO1", + "Vendor": "NOL", "Country": "UAE", "Name": "Nol Card/Dubai", "Description": "Nol Card/Dubai", @@ -121,7 +121,7 @@ }, { "AID": "F21030", - "Vendor": "ORCA Card", + "Vendor": "ORCA (VUX/ERG)", "Country": "", "Name": "ORCA Card", "Description": "(FIDs 02: Trip History; 04: current balance)", @@ -367,5 +367,181 @@ "Name": "University of Ljubljana Student ID", "Description": "", "Type": "student" + }, + { + "AID": "27E178", + "Vendor": "Disney", + "Country": "US", + "Name": "Disney MagicBand", + "Description": "", + "Type": "payment system" + }, + { + "AID": "78E127", + "Vendor": "Disney", + "Country": "US", + "Name": "Disney MagicBand", + "Description": "", + "Type": "payment system" + }, + { + "AID": "44434C", + "Vendor": "Disney", + "Country": "US", + "Name": "Disney MagicBand", + "Description": "AID found on MagicBand desfire cards", + "Type": "payment system" + }, + { + "AID": "F21100", + "Vendor": "MyKI", + "Country": "AUS", + "Name": "Myki", + "Description": "AID found on Myki ticket cards", + "Type": "transport" + }, + { + "AID": "F210F0", + "Vendor": "MyKI", + "Country": "AUS", + "Name": "Myki", + "Description": "AID found on Myki ticket cards", + "Type": "transport" + }, + { + "AID": "F206B0", + "Vendor": "ACS", + "Country": "AUS", + "Name": "Metrocard / ACS", + "Description": "", + "Type": "transport" + }, + { + "AID": "F21050", + "Vendor": "INIT", + "Country": "NZ", + "Name": "Metrocard / Christchurch", + "Description": "", + "Type": "transport" + }, + { + "AID": "F21150", + "Vendor": "HAGUESS", + "Country": "CZ", + "Name": "Lítačka / Prague", + "Description": "", + "Type": "transport" + }, + { + "AID": "F21360", + "Vendor": "INIT", + "Country": "CZ", + "Name": "HOLO", + "Description": "", + "Type": "transport" + }, + { + "AID": "F21381", + "Vendor": "Cubic", + "Country": "US", + "Name": "Ventra", + "Description": "", + "Type": "transport" + }, + { + "AID": "F213A0", + "Vendor": "INIT", + "Country": "US", + "Name": "WAVE / Rhode Island", + "Description": "", + "Type": "transport" + }, + { + "AID": "F210E0", + "Vendor": "Hop Fastpass", + "Country": "", + "Name": "Hop Fastpass", + "Description": "", + "Type": "transport" + }, + { + "AID": "EF2011", + "Vendor": "HSL", + "Country": "FI", + "Name": "HSL / Helsinki", + "Description": "", + "Type": "transport" + }, + { + "AID": "A00216", + "Vendor": "ITSO", + "Country": "", + "Name": "ITSO", + "Description": "", + "Type": "transport" + }, + { + "AID": "554000", + "Vendor": "AT HOP", + "Country": "", + "Name": "AT HOP", + "Description": "", + "Type": "transport" + }, + { + "AID": "534531", + "Vendor": "OPAL", + "Country": "AUS", + "Name": "OPAL", + "Description": "", + "Type": "transport" + }, + { + "AID": "2211AF", + "Vendor": "Leap", + "Country": "", + "Name": "Leap", + "Description": "", + "Type": "transport" + }, + { + "AID": "015342", + "Vendor": "BEM", + "Country": "TH", + "Name": "BEM / Bangkok", + "Description": "", + "Type": "transport" + }, + { + "AID": "012242", + "Vendor": "Istanbulkart", + "Country": "TR", + "Name": "Istanbulkart / Istanbul", + "Description": "", + "Type": "transport" + }, + { + "AID": "010000", + "Vendor": "Madrid Public Transit Card", + "Country": "ES", + "Name": "Madrid Public Transit Card", + "Description": "", + "Type": "transport" + }, + { + "AID": "000001", + "Vendor": "Invalid / reserved", + "Country": "", + "Name": "Invalid / reserved", + "Description": "used by Compass DESFire and Breeze DESFire", + "Type": "transport" + }, + { + "AID": "FFFFFF", + "Vendor": "Reserved for future use", + "Country": "", + "Name": "Reserved for future use", + "Description": "used by AT HOP, Nol, ORCA", + "Type": "transport" } ] diff --git a/client/resources/aidlist.json b/client/resources/aidlist.json index ff574dd6a..6eeba5e28 100644 --- a/client/resources/aidlist.json +++ b/client/resources/aidlist.json @@ -2255,13 +2255,21 @@ "Description": "Student ID cards", "Type": "identity" }, + { + "AID": "D2760000254D010200", + "Vendor": "Zentraler Kreditausschuss (ZKA)", + "Country": "Germany", + "Name": "Girocard Jugendschutz", + "Description": "Age verification", + "Type": "identity" + }, { "AID": "A000000809434343444B467631", "Vendor": "Car Connectivity Consortium (CCC)", "Country": "", "Name": "Digital Car Key Framework", "Description": "Used during key provisioning and configuration", - "Type": "access" + "Type": "" }, { "AID": "A000000809434343444B417631", @@ -2295,13 +2303,69 @@ "Description": "AID prefix used by MIFARE 2GO-based cards", "Type": "" }, + { + "AID": "A00000039656434103F1216000000000", + "Vendor": "LV Monorail", + "Country": "United States", + "Name": "Las Vegas Monorail", + "Description": "", + "Type": "transport" + }, + { + "AID": "A00000039656434103F8852200000000", + "Vendor": "Ubian", + "Country": "Slovakia", + "Name": "Ubian digital transit card", + "Description": "DESFire-based virtual transit card", + "Type": "transport" + }, + { + "AID": "DE5C0D1F1CADA5", + "Vendor": "CRTM", + "Country": "Spain", + "Name": "Madrid transit card", + "Description": "DESFire-based transit card", + "Type": "transport" + }, + { + "AID": "A00000F21100", + "Vendor": "PTV", + "Country": "Australia", + "Name": "Myki transit card", + "Description": "DESFire-based transit card", + "Type": "transport" + }, + { + "AID": "637001ff4c41", + "Vendor": "Cubic", + "Country": "United States", + "Name": "LA Tap", + "Description": "DESFire-based transit card (ASCII cp\\x01\\xffLA)", + "Type": "transport" + }, + { + "AID": "637001ff574d415441", + "Vendor": "Cubic", + "Country": "United States", + "Name": "Smart Trip", + "Description": "DESFire-based transit card (ASCII cp\\x01\\xffWMATA)", + "Type": "transport" + }, + { + "AID": "637001ff434c4950504552", + "Vendor": "Cubic", + "Country": "United States", + "Name": "Clipper", + "Description": "DESFire-based transit card (ASCII cp\\x01\\xffCLIPPER)", + "Type": "transport" + }, { "AID": "A0000002164954534F2D31", "Vendor": "ITSO", "Country": "United Kingdom", "Name": "ITSO CMD2", "Description": "AID used by ITSO for smartcard/phone-based transit cards", - "Type": "transit" + "Type": "transport" }, { "AID": "A000000632010105", @@ -2309,22 +2373,54 @@ "Country": "China", "Name": "China T-Union", "Description": "Universal transit card used by many big public transit operators", - "Type": "transit" - }, - { - "AID": "D2760000254D010200", - "Vendor": "Zentraler Kreditausschuss (ZKA)", - "Country": "Germany", - "Name": "Girocard Jugendschutz", - "Description": "Age verification", - "Type": "" + "Type": "transport" }, { "AID": "A00000000491", "Vendor": "MasterCard International", "Country": "", "Name": "Mastercard Private Label Transit", - "Description": "AID prefix used by transit cards that use private label mastercards (E.g. Ventra and HOP)", - "Type": "transit" + "Description": "AID prefix used by transit cards that use private label mastercard", + "Type": "transport" + }, + { + "AID": "A0000000049100", + "Vendor": "MasterCard International", + "Country": "United States", + "Name": "HOP Fastpass", + "Description": "", + "Type": "transport" + }, + { + "AID": "A0000000049101", + "Vendor": "MasterCard International", + "Country": "United States", + "Name": "Ventra", + "Description": "", + "Type": "transport" + }, + { + "AID": "A000000858044F53452E4348", + "Vendor": "Apple", + "Country": "", + "Name": "AirDrop connection negotiation", + "Description": "Used by NFC-based AirDrop negotiation added in IOS17", + "Type": "" + }, + { + "AID": "a0000004400001010001", + "Vendor": "HID Global", + "Country": "", + "Name": "SEOS", + "Description": "Used by both by physical cards and mobile implementations", + "Type": "access" + }, + { + "AID": "A00000054000060100010000FF", + "Vendor": "HID Global", + "Country": "", + "Name": "SEOS Mobile", + "Description": "Declared by some SEOS-compatible applications for HCE", + "Type": "access" } ] diff --git a/client/resources/ecplist.json b/client/resources/ecplist.json new file mode 100644 index 000000000..27db827f1 --- /dev/null +++ b/client/resources/ecplist.json @@ -0,0 +1,130 @@ +[ + { + "value": "6a01000000", + "name": "VAS or payment", + "description": "Used when a reader needs a pass or a payment card. Sometimes called VAS over Payment" + }, + { + "value": "6a01000001", + "name": "VAS and payment", + "description": "Also called single tap mode. Displays pass info under a payment card. Allows reading multiple passes with different ids in one tap" + }, + { + "value": "6a01000002", + "name": "VAS only", + "description": "Used when a reader requests passes only" + }, + { + "value": "6a01000003", + "name": "Payment only", + "description": "Used when a reader requests payment cards only. Also disables express mode for chinese transit cards" + }, + { + "value": "6a01cf0000", + "name": "Ignore", + "description": "iPhones before IOS17 emit this frame so that other Apple devices don't react to the field during background reading. Also emitted during NFCReaderSession subtypes" + }, + + { + "value": "6a02c801000300000000000000", + "name": "Transit: Ventra", + "description": "" + }, + { + "value": "6a02c801000304000000000000", + "name": "Transit: HOP Fastpass", + "description": "" + }, + { + "value": "6a02c801000300010000000000", + "name": "Transit: WMATA", + "description": "" + }, + { + "value": "6a02c801000300027900000000", + "name": "Transit: TFL", + "description": "Found by a member of Proxmark community. Data bytes define a mask of supported EMV payment networks for fallback. In this case: Amex, Visa, Mastercard, Maestro, Vpay" + }, + { + "value": "6a02c801000300050000000000", + "name": "Transit: LA TAP", + "description": "" + }, + { + "value": "6a02c801000300070000000000", + "name": "Transit: Clipper", + "description": "" + }, + + { + "value": "6a02c3020002ffff", + "name": "Access: Hotel: Hilton", + "description": "TCI might be a wildcard before a reservation is made" + }, + + { + "value": "6a02cb0206021100deadbeefdeadbeef", + "name": "Access: Home Key", + "description": "Last 8 bytes refer to reader group identifier, common for all readers in same home installation, allows to differentiate between keys for different homes" + }, + + { + "value": "6a02c30209010001", + "name": "Access: Car Pairing: BMW", + "description": "Device does not respond to poll after this frame. Displays a car pairing popup for BMW" + }, + { + "value": "6a02c30209010201", + "name": "Access: Car Pairing: Mercedes", + "description": "Device does not respond to poll after this frame. Displays a car pairing popup for Mercedes" + }, + { + "value": "6a02c30209010051", + "name": "Access: Car Pairing: Genesis", + "description": "Device does not respond to poll after this frame. Displays a car pairing popup for Genesis" + }, + { + "value": "6a02c30209010041", + "name": "Access: Car Pairing: KIA", + "description": "Device does not respond to poll after this frame. Displays a car pairing popup for KIA" + }, + { + "value": "6a02c30209010301", + "name": "Access: Car Pairing: Hyundai", + "description": "Device does not respond to poll after this frame. Displays a car pairing popup for Hyundai" + }, + { + "value": "6a02c30209010701", + "name": "Access: Car Pairing: BYD", + "description": "Device does not respond to poll after this frame. Displays a car pairing popup for BYD" + }, + { + "value": "6a02c30209010241", + "name": "Access: Car Pairing: Denza", + "description": "Device does not respond to poll after this frame. Displays a car pairing popup for Denza" + }, + { + "value": "6a02c30209010091", + "name": "Access: Car Pairing: Lotus", + "description": "Device does not respond to poll after this frame. Displays a car pairing popup for Lotus" + }, + + + { + "value": "6a0281030000", + "name": "Identity", + "description": "Used for reading ISO18013 digital IDs" + }, + + + { + "value": "6a02890500010001deadbeef6969", + "name": "NameDrop", + "description": "Triggers a warp animation. Device does not respond to poll after this frame, but it serves as a trigger for AirDrop frame. Data part contains BLE MAC address" + }, + { + "value": "6a02890500010000000000000000", + "name": "AirDrop", + "description": "Only sent if sees a NameDrop frame of other device. Device responds to poll with UID 00010203" + } +] diff --git a/client/resources/hardnested_tables/bitflip_0_001_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_001_states.bin.bz2 deleted file mode 100644 index e65f22471..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_001_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_001_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_001_states.bin.lz4 new file mode 100644 index 000000000..b343718e7 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_001_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_003_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_003_states.bin.bz2 deleted file mode 100644 index 5ae156039..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_003_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_003_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_003_states.bin.lz4 new file mode 100644 index 000000000..61f52563f Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_003_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_005_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_005_states.bin.bz2 deleted file mode 100644 index 473c32fca..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_005_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_005_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_005_states.bin.lz4 new file mode 100644 index 000000000..5e6ab2676 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_005_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_007_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_007_states.bin.bz2 deleted file mode 100644 index 0cfda87cb..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_007_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_007_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_007_states.bin.lz4 new file mode 100644 index 000000000..8864efd2d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_007_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_009_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_009_states.bin.bz2 deleted file mode 100644 index 1103e29bc..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_009_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_009_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_009_states.bin.lz4 new file mode 100644 index 000000000..187763ee6 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_009_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_00b_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_00b_states.bin.bz2 deleted file mode 100644 index 2e1c29d87..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_00b_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_00b_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_00b_states.bin.lz4 new file mode 100644 index 000000000..085a4301c Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_00b_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_00d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_00d_states.bin.bz2 deleted file mode 100644 index c76301638..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_00d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_00d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_00d_states.bin.lz4 new file mode 100644 index 000000000..f88c681b5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_00d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_00f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_00f_states.bin.bz2 deleted file mode 100644 index 39e7afc47..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_00f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_00f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_00f_states.bin.lz4 new file mode 100644 index 000000000..65e43f158 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_00f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_010_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_010_states.bin.bz2 deleted file mode 100644 index 74890f1ff..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_010_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_010_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_010_states.bin.lz4 new file mode 100644 index 000000000..8309c3f1d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_010_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_014_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_014_states.bin.bz2 deleted file mode 100644 index b7fb646d1..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_014_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_014_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_014_states.bin.lz4 new file mode 100644 index 000000000..44acd9e89 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_014_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_01c_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_01c_states.bin.bz2 deleted file mode 100644 index c865be04e..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_01c_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_01c_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_01c_states.bin.lz4 new file mode 100644 index 000000000..81d4d9de3 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_01c_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_021_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_021_states.bin.bz2 deleted file mode 100644 index b2fef5781..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_021_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_021_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_021_states.bin.lz4 new file mode 100644 index 000000000..fd5d540d7 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_021_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_023_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_023_states.bin.bz2 deleted file mode 100644 index 32c7adfd5..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_023_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_023_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_023_states.bin.lz4 new file mode 100644 index 000000000..d3fd78ebb Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_023_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_025_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_025_states.bin.bz2 deleted file mode 100644 index e21a5ee93..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_025_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_025_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_025_states.bin.lz4 new file mode 100644 index 000000000..d337d7303 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_025_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_027_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_027_states.bin.bz2 deleted file mode 100644 index 7b51fd6b1..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_027_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_027_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_027_states.bin.lz4 new file mode 100644 index 000000000..b1816f47e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_027_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_029_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_029_states.bin.bz2 deleted file mode 100644 index b55556692..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_029_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_029_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_029_states.bin.lz4 new file mode 100644 index 000000000..c5599605a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_029_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_02b_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_02b_states.bin.bz2 deleted file mode 100644 index f22242565..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_02b_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_02b_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_02b_states.bin.lz4 new file mode 100644 index 000000000..d0906edb9 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_02b_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_02d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_02d_states.bin.bz2 deleted file mode 100644 index 54170b83b..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_02d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_02d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_02d_states.bin.lz4 new file mode 100644 index 000000000..3c1e1727b Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_02d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_02f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_02f_states.bin.bz2 deleted file mode 100644 index e20b10f3d..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_02f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_02f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_02f_states.bin.lz4 new file mode 100644 index 000000000..883c4851e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_02f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_030_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_030_states.bin.bz2 deleted file mode 100644 index 8042c6366..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_030_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_030_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_030_states.bin.lz4 new file mode 100644 index 000000000..0abd4785a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_030_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_034_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_034_states.bin.bz2 deleted file mode 100644 index 06fc562c4..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_034_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_034_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_034_states.bin.lz4 new file mode 100644 index 000000000..f6270b397 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_034_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_03c_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_03c_states.bin.bz2 deleted file mode 100644 index 1a354ec8a..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_03c_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_03c_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_03c_states.bin.lz4 new file mode 100644 index 000000000..f89025de1 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_03c_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_040_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_040_states.bin.bz2 deleted file mode 100644 index 54b71e2ce..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_040_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_040_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_040_states.bin.lz4 new file mode 100644 index 000000000..016ecfe67 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_040_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_044_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_044_states.bin.bz2 deleted file mode 100644 index b268ec935..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_044_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_044_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_044_states.bin.lz4 new file mode 100644 index 000000000..a0513f75e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_044_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_04c_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_04c_states.bin.bz2 deleted file mode 100644 index 26977703d..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_04c_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_04c_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_04c_states.bin.lz4 new file mode 100644 index 000000000..0ee52357e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_04c_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_051_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_051_states.bin.bz2 deleted file mode 100644 index 3c889d07c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_051_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_051_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_051_states.bin.lz4 new file mode 100644 index 000000000..ae0d8a050 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_051_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_053_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_053_states.bin.bz2 deleted file mode 100644 index 751724d3d..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_053_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_053_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_053_states.bin.lz4 new file mode 100644 index 000000000..a1d8359ae Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_053_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_055_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_055_states.bin.bz2 deleted file mode 100644 index 1cf3f2ad9..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_055_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_055_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_055_states.bin.lz4 new file mode 100644 index 000000000..a7e1e3522 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_055_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_057_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_057_states.bin.bz2 deleted file mode 100644 index 099cc0e38..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_057_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_057_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_057_states.bin.lz4 new file mode 100644 index 000000000..525db3d7e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_057_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_059_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_059_states.bin.bz2 deleted file mode 100644 index fea5cf3e8..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_059_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_059_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_059_states.bin.lz4 new file mode 100644 index 000000000..3fb7f1f60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_059_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_05b_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_05b_states.bin.bz2 deleted file mode 100644 index 4dd885ec3..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_05b_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_05b_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_05b_states.bin.lz4 new file mode 100644 index 000000000..a7928ed6e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_05b_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_05d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_05d_states.bin.bz2 deleted file mode 100644 index 7ea71a9c6..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_05d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_05d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_05d_states.bin.lz4 new file mode 100644 index 000000000..f149660c5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_05d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_05f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_05f_states.bin.bz2 deleted file mode 100644 index bda71a64e..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_05f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_05f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_05f_states.bin.lz4 new file mode 100644 index 000000000..472f2b854 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_05f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_064_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_064_states.bin.bz2 deleted file mode 100644 index 3bec7c36b..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_064_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_064_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_064_states.bin.lz4 new file mode 100644 index 000000000..a596bd59c Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_064_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_06c_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_06c_states.bin.bz2 deleted file mode 100644 index ff80c2233..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_06c_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_06c_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_06c_states.bin.lz4 new file mode 100644 index 000000000..45cda4314 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_06c_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_071_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_071_states.bin.bz2 deleted file mode 100644 index 6073d4e52..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_071_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_071_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_071_states.bin.lz4 new file mode 100644 index 000000000..15ebf5ea0 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_071_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_073_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_073_states.bin.bz2 deleted file mode 100644 index d1ae56fec..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_073_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_073_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_073_states.bin.lz4 new file mode 100644 index 000000000..bb38d5cb4 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_073_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_075_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_075_states.bin.bz2 deleted file mode 100644 index 6c8258ae3..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_075_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_075_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_075_states.bin.lz4 new file mode 100644 index 000000000..47dae45cf Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_075_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_077_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_077_states.bin.bz2 deleted file mode 100644 index acaf1e210..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_077_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_077_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_077_states.bin.lz4 new file mode 100644 index 000000000..4e9d451b5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_077_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_079_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_079_states.bin.bz2 deleted file mode 100644 index 427506dd8..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_079_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_079_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_079_states.bin.lz4 new file mode 100644 index 000000000..3b71ddcd2 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_079_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_07b_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_07b_states.bin.bz2 deleted file mode 100644 index bcc281932..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_07b_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_07b_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_07b_states.bin.lz4 new file mode 100644 index 000000000..e8f500c82 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_07b_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_07f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_07f_states.bin.bz2 deleted file mode 100644 index b56e0f7a0..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_07f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_07f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_07f_states.bin.lz4 new file mode 100644 index 000000000..38fe391be Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_07f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_081_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_081_states.bin.bz2 deleted file mode 100644 index c5426ace6..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_081_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_081_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_081_states.bin.lz4 new file mode 100644 index 000000000..6cc5d4be6 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_081_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_083_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_083_states.bin.bz2 deleted file mode 100644 index 3421417ed..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_083_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_083_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_083_states.bin.lz4 new file mode 100644 index 000000000..b9e06b240 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_083_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_085_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_085_states.bin.bz2 deleted file mode 100644 index 17f1aaeed..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_085_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_085_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_085_states.bin.lz4 new file mode 100644 index 000000000..33f0bdfef Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_085_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_087_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_087_states.bin.bz2 deleted file mode 100644 index 5b9302627..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_087_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_087_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_087_states.bin.lz4 new file mode 100644 index 000000000..859ab9b29 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_087_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_089_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_089_states.bin.bz2 deleted file mode 100644 index 9f37987c7..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_089_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_089_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_089_states.bin.lz4 new file mode 100644 index 000000000..046fef513 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_089_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_08b_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_08b_states.bin.bz2 deleted file mode 100644 index 0f42f9246..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_08b_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_08b_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_08b_states.bin.lz4 new file mode 100644 index 000000000..693b2164a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_08b_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_08d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_08d_states.bin.bz2 deleted file mode 100644 index f7e2d1674..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_08d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_08d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_08d_states.bin.lz4 new file mode 100644 index 000000000..a77eade5e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_08d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_08f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_08f_states.bin.bz2 deleted file mode 100644 index f66b803eb..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_08f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_08f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_08f_states.bin.lz4 new file mode 100644 index 000000000..fb7224627 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_08f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_090_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_090_states.bin.bz2 deleted file mode 100644 index 7f559b40c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_090_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_090_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_090_states.bin.lz4 new file mode 100644 index 000000000..ad7aab465 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_090_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_094_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_094_states.bin.bz2 deleted file mode 100644 index bcdcbd26f..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_094_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_094_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_094_states.bin.lz4 new file mode 100644 index 000000000..eb996434c Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_094_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_09c_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_09c_states.bin.bz2 deleted file mode 100644 index 4b0316dc3..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_09c_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_09c_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_09c_states.bin.lz4 new file mode 100644 index 000000000..ffe57e55e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_09c_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0a1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0a1_states.bin.bz2 deleted file mode 100644 index daa61d3b7..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0a1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0a1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0a1_states.bin.lz4 new file mode 100644 index 000000000..716e2ec82 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0a1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0a3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0a3_states.bin.bz2 deleted file mode 100644 index d7171c287..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0a3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0a3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0a3_states.bin.lz4 new file mode 100644 index 000000000..7f045d83f Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0a3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0a5_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0a5_states.bin.bz2 deleted file mode 100644 index 958b8ed3d..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0a5_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0a5_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0a5_states.bin.lz4 new file mode 100644 index 000000000..bb045bb43 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0a5_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0a7_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0a7_states.bin.bz2 deleted file mode 100644 index 9992d1259..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0a7_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0a7_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0a7_states.bin.lz4 new file mode 100644 index 000000000..ba9fb4f9c Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0a7_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0a9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0a9_states.bin.bz2 deleted file mode 100644 index 5893a4938..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0a9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0a9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0a9_states.bin.lz4 new file mode 100644 index 000000000..dab0fdca9 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0a9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0ab_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0ab_states.bin.bz2 deleted file mode 100644 index 7deaece19..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0ab_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0ab_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0ab_states.bin.lz4 new file mode 100644 index 000000000..ef2177e62 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0ab_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0ad_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0ad_states.bin.bz2 deleted file mode 100644 index 19c36b526..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0ad_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0ad_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0ad_states.bin.lz4 new file mode 100644 index 000000000..171ee0312 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0ad_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0af_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0af_states.bin.bz2 deleted file mode 100644 index 3e99de95c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0af_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0af_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0af_states.bin.lz4 new file mode 100644 index 000000000..cb3b19e44 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0af_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0b0_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0b0_states.bin.bz2 deleted file mode 100644 index cb2ac918b..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0b0_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0b0_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0b0_states.bin.lz4 new file mode 100644 index 000000000..4bf9b403e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0b0_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0b4_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0b4_states.bin.bz2 deleted file mode 100644 index f9c8c8183..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0b4_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0b4_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0b4_states.bin.lz4 new file mode 100644 index 000000000..8de773b3f Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0b4_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0bc_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0bc_states.bin.bz2 deleted file mode 100644 index 19e1fe89a..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0bc_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0bc_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0bc_states.bin.lz4 new file mode 100644 index 000000000..60ed53332 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0bc_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0c0_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0c0_states.bin.bz2 deleted file mode 100644 index 87d8693df..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0c0_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0c0_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0c0_states.bin.lz4 new file mode 100644 index 000000000..2d542516a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0c0_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0c4_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0c4_states.bin.bz2 deleted file mode 100644 index 57823403b..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0c4_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0c4_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0c4_states.bin.lz4 new file mode 100644 index 000000000..690f7419e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0c4_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0cc_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0cc_states.bin.bz2 deleted file mode 100644 index 38fc1e0ae..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0cc_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0cc_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0cc_states.bin.lz4 new file mode 100644 index 000000000..9880ccf01 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0cc_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0d1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0d1_states.bin.bz2 deleted file mode 100644 index 4d1b27593..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0d1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0d1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0d1_states.bin.lz4 new file mode 100644 index 000000000..0c9ca6ebd Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0d1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0d3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0d3_states.bin.bz2 deleted file mode 100644 index cb9afb002..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0d3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0d3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0d3_states.bin.lz4 new file mode 100644 index 000000000..dcf6b9deb Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0d3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0d5_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0d5_states.bin.bz2 deleted file mode 100644 index ae6e859a8..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0d5_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0d5_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0d5_states.bin.lz4 new file mode 100644 index 000000000..17d19e8e9 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0d5_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0d7_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0d7_states.bin.bz2 deleted file mode 100644 index 90c562613..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0d7_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0d7_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0d7_states.bin.lz4 new file mode 100644 index 000000000..400120c8f Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0d7_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0d9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0d9_states.bin.bz2 deleted file mode 100644 index ad2df6b55..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0d9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0d9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0d9_states.bin.lz4 new file mode 100644 index 000000000..79d6d49b6 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0d9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0db_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0db_states.bin.bz2 deleted file mode 100644 index 353c48c88..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0db_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0db_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0db_states.bin.lz4 new file mode 100644 index 000000000..d89176694 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0db_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0dd_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0dd_states.bin.bz2 deleted file mode 100644 index 2d2e19707..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0dd_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0dd_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0dd_states.bin.lz4 new file mode 100644 index 000000000..9e7ae0925 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0dd_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0df_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0df_states.bin.bz2 deleted file mode 100644 index 14ca3fcda..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0df_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0df_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0df_states.bin.lz4 new file mode 100644 index 000000000..b8cbfb9d6 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0df_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0e4_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0e4_states.bin.bz2 deleted file mode 100644 index 19e2e6109..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0e4_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0e4_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0e4_states.bin.lz4 new file mode 100644 index 000000000..07135ccf2 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0e4_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0ec_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0ec_states.bin.bz2 deleted file mode 100644 index f376b1cd1..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0ec_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0ec_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0ec_states.bin.lz4 new file mode 100644 index 000000000..a2ca8131c Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0ec_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0f1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0f1_states.bin.bz2 deleted file mode 100644 index b263afc60..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0f1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0f1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0f1_states.bin.lz4 new file mode 100644 index 000000000..f54d02f01 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0f1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0f3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0f3_states.bin.bz2 deleted file mode 100644 index d18971ec8..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0f3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0f3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0f3_states.bin.lz4 new file mode 100644 index 000000000..f9141026d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0f3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0f5_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0f5_states.bin.bz2 deleted file mode 100644 index 7102292c4..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0f5_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0f5_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0f5_states.bin.lz4 new file mode 100644 index 000000000..daee63044 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0f5_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0f7_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0f7_states.bin.bz2 deleted file mode 100644 index 4da9f8558..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0f7_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0f7_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0f7_states.bin.lz4 new file mode 100644 index 000000000..c1ff0c76b Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0f7_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0f9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0f9_states.bin.bz2 deleted file mode 100644 index 476895cd1..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0f9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0f9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0f9_states.bin.lz4 new file mode 100644 index 000000000..383b8a8f2 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0f9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0fb_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0fb_states.bin.bz2 deleted file mode 100644 index 39a913d64..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0fb_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0fb_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0fb_states.bin.lz4 new file mode 100644 index 000000000..157d035f2 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0fb_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0fd_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0fd_states.bin.bz2 deleted file mode 100644 index ffc3cf539..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0fd_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0fd_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0fd_states.bin.lz4 new file mode 100644 index 000000000..d760dd764 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0fd_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_0ff_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_0ff_states.bin.bz2 deleted file mode 100644 index 0c96f7625..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_0ff_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_0ff_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_0ff_states.bin.lz4 new file mode 100644 index 000000000..86670f0b9 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_0ff_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_104_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_104_states.bin.bz2 deleted file mode 100644 index a5c2ad9bf..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_104_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_104_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_104_states.bin.lz4 new file mode 100644 index 000000000..a52ca9770 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_104_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_10c_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_10c_states.bin.bz2 deleted file mode 100644 index e3aa0c2b5..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_10c_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_10c_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_10c_states.bin.lz4 new file mode 100644 index 000000000..8c633bac5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_10c_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_111_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_111_states.bin.bz2 deleted file mode 100644 index 8cd2e0ba0..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_111_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_111_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_111_states.bin.lz4 new file mode 100644 index 000000000..199eb6377 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_111_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_113_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_113_states.bin.bz2 deleted file mode 100644 index c1b73e1cf..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_113_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_113_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_113_states.bin.lz4 new file mode 100644 index 000000000..9b91cc4e2 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_113_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_115_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_115_states.bin.bz2 deleted file mode 100644 index 464b2e5d6..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_115_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_115_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_115_states.bin.lz4 new file mode 100644 index 000000000..062d0f4b8 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_115_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_117_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_117_states.bin.bz2 deleted file mode 100644 index 87f7542d3..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_117_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_117_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_117_states.bin.lz4 new file mode 100644 index 000000000..8a5df1fc3 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_117_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_119_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_119_states.bin.bz2 deleted file mode 100644 index 05620bba0..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_119_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_119_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_119_states.bin.lz4 new file mode 100644 index 000000000..4730414ad Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_119_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_11b_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_11b_states.bin.bz2 deleted file mode 100644 index cce41a523..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_11b_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_11b_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_11b_states.bin.lz4 new file mode 100644 index 000000000..f147874a3 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_11b_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_11d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_11d_states.bin.bz2 deleted file mode 100644 index 6aa3f4638..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_11d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_11d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_11d_states.bin.lz4 new file mode 100644 index 000000000..b03da0135 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_11d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_11f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_11f_states.bin.bz2 deleted file mode 100644 index 5892009d2..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_11f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_11f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_11f_states.bin.lz4 new file mode 100644 index 000000000..cfa31da6f Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_11f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_124_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_124_states.bin.bz2 deleted file mode 100644 index d3601defe..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_124_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_124_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_124_states.bin.lz4 new file mode 100644 index 000000000..5fe6d13ba Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_124_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_12c_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_12c_states.bin.bz2 deleted file mode 100644 index 38dbdf227..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_12c_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_12c_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_12c_states.bin.lz4 new file mode 100644 index 000000000..18d8d20f2 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_12c_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_131_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_131_states.bin.bz2 deleted file mode 100644 index 0afa8f655..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_131_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_131_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_131_states.bin.lz4 new file mode 100644 index 000000000..f3993558a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_131_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_133_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_133_states.bin.bz2 deleted file mode 100644 index 400272bcc..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_133_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_133_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_133_states.bin.lz4 new file mode 100644 index 000000000..03c54fd52 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_133_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_135_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_135_states.bin.bz2 deleted file mode 100644 index 242ba30a6..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_135_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_135_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_135_states.bin.lz4 new file mode 100644 index 000000000..84a6ba3af Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_135_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_137_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_137_states.bin.bz2 deleted file mode 100644 index 293c1f93f..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_137_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_137_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_137_states.bin.lz4 new file mode 100644 index 000000000..e5a76bf33 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_137_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_139_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_139_states.bin.bz2 deleted file mode 100644 index 97761432b..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_139_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_139_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_139_states.bin.lz4 new file mode 100644 index 000000000..e52835eaf Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_139_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_13b_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_13b_states.bin.bz2 deleted file mode 100644 index fb869e7cb..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_13b_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_13b_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_13b_states.bin.lz4 new file mode 100644 index 000000000..8bffa0f2e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_13b_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_13d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_13d_states.bin.bz2 deleted file mode 100644 index 46b67bf83..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_13d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_13d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_13d_states.bin.lz4 new file mode 100644 index 000000000..79899c9f6 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_13d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_13f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_13f_states.bin.bz2 deleted file mode 100644 index 33b2abfc2..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_13f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_13f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_13f_states.bin.lz4 new file mode 100644 index 000000000..29de4add8 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_13f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_141_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_141_states.bin.bz2 deleted file mode 100644 index 3be41189c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_141_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_141_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_141_states.bin.lz4 new file mode 100644 index 000000000..7df6d080b Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_141_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_143_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_143_states.bin.bz2 deleted file mode 100644 index 99328786c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_143_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_143_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_143_states.bin.lz4 new file mode 100644 index 000000000..d6fcf319d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_143_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_145_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_145_states.bin.bz2 deleted file mode 100644 index b31bd720a..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_145_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_145_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_145_states.bin.lz4 new file mode 100644 index 000000000..aabd5fa31 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_145_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_147_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_147_states.bin.bz2 deleted file mode 100644 index fab2780c7..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_147_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_147_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_147_states.bin.lz4 new file mode 100644 index 000000000..ac70a06f5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_147_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_149_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_149_states.bin.bz2 deleted file mode 100644 index eda7650e5..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_149_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_149_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_149_states.bin.lz4 new file mode 100644 index 000000000..d22b15e0e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_149_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_14b_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_14b_states.bin.bz2 deleted file mode 100644 index c3c3dfaf3..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_14b_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_14b_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_14b_states.bin.lz4 new file mode 100644 index 000000000..139d6cbb6 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_14b_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_14d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_14d_states.bin.bz2 deleted file mode 100644 index adf20a10f..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_14d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_14d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_14d_states.bin.lz4 new file mode 100644 index 000000000..ed8fe08c6 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_14d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_14f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_14f_states.bin.bz2 deleted file mode 100644 index f61babefa..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_14f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_14f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_14f_states.bin.lz4 new file mode 100644 index 000000000..3ce07c452 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_14f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_150_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_150_states.bin.bz2 deleted file mode 100644 index 612d6295e..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_150_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_150_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_150_states.bin.lz4 new file mode 100644 index 000000000..798da8c6f Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_150_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_154_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_154_states.bin.bz2 deleted file mode 100644 index 9efc40fd9..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_154_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_154_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_154_states.bin.lz4 new file mode 100644 index 000000000..0ec51234e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_154_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_15c_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_15c_states.bin.bz2 deleted file mode 100644 index 0180aa901..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_15c_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_15c_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_15c_states.bin.lz4 new file mode 100644 index 000000000..aea4d7a5f Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_15c_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_161_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_161_states.bin.bz2 deleted file mode 100644 index 5c12567e4..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_161_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_161_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_161_states.bin.lz4 new file mode 100644 index 000000000..c0d488db2 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_161_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_163_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_163_states.bin.bz2 deleted file mode 100644 index b68ad5995..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_163_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_163_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_163_states.bin.lz4 new file mode 100644 index 000000000..34af1d6f7 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_163_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_165_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_165_states.bin.bz2 deleted file mode 100644 index 69e051fbf..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_165_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_165_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_165_states.bin.lz4 new file mode 100644 index 000000000..fc5eab951 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_165_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_167_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_167_states.bin.bz2 deleted file mode 100644 index bb2c8cc48..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_167_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_167_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_167_states.bin.lz4 new file mode 100644 index 000000000..438806c68 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_167_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_169_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_169_states.bin.bz2 deleted file mode 100644 index bbec52e26..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_169_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_169_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_169_states.bin.lz4 new file mode 100644 index 000000000..2ab3d7fe6 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_169_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_16b_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_16b_states.bin.bz2 deleted file mode 100644 index 0a34da2f6..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_16b_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_16b_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_16b_states.bin.lz4 new file mode 100644 index 000000000..9b731604a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_16b_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_16d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_16d_states.bin.bz2 deleted file mode 100644 index c6758b6a2..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_16d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_16d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_16d_states.bin.lz4 new file mode 100644 index 000000000..7df5b243e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_16d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_16f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_16f_states.bin.bz2 deleted file mode 100644 index fe936f3bf..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_16f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_16f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_16f_states.bin.lz4 new file mode 100644 index 000000000..0ad58f032 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_16f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_170_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_170_states.bin.bz2 deleted file mode 100644 index 5180aae93..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_170_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_170_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_170_states.bin.lz4 new file mode 100644 index 000000000..871544b77 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_170_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_174_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_174_states.bin.bz2 deleted file mode 100644 index 00e4f95b4..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_174_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_174_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_174_states.bin.lz4 new file mode 100644 index 000000000..5db556ff5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_174_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_17c_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_17c_states.bin.bz2 deleted file mode 100644 index 01c02bb2b..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_17c_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_17c_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_17c_states.bin.lz4 new file mode 100644 index 000000000..08843ca7b Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_17c_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_184_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_184_states.bin.bz2 deleted file mode 100644 index 5da967674..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_184_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_184_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_184_states.bin.lz4 new file mode 100644 index 000000000..8369142ee Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_184_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_18c_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_18c_states.bin.bz2 deleted file mode 100644 index 88f05ef65..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_18c_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_18c_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_18c_states.bin.lz4 new file mode 100644 index 000000000..b38580ae3 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_18c_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_191_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_191_states.bin.bz2 deleted file mode 100644 index 7303065d9..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_191_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_191_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_191_states.bin.lz4 new file mode 100644 index 000000000..3563685d6 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_191_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_193_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_193_states.bin.bz2 deleted file mode 100644 index b9e3297c5..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_193_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_193_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_193_states.bin.lz4 new file mode 100644 index 000000000..10d9b90fa Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_193_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_195_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_195_states.bin.bz2 deleted file mode 100644 index fa5060af8..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_195_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_195_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_195_states.bin.lz4 new file mode 100644 index 000000000..b649fd709 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_195_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_197_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_197_states.bin.bz2 deleted file mode 100644 index 5de558643..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_197_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_197_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_197_states.bin.lz4 new file mode 100644 index 000000000..de5d1d9cb Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_197_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_199_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_199_states.bin.bz2 deleted file mode 100644 index c4f58b53d..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_199_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_199_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_199_states.bin.lz4 new file mode 100644 index 000000000..83cbb61fb Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_199_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_19b_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_19b_states.bin.bz2 deleted file mode 100644 index 9ad820f8c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_19b_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_19b_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_19b_states.bin.lz4 new file mode 100644 index 000000000..9be17ac56 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_19b_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_19d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_19d_states.bin.bz2 deleted file mode 100644 index 54371d652..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_19d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_19d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_19d_states.bin.lz4 new file mode 100644 index 000000000..2ea815311 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_19d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_19f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_19f_states.bin.bz2 deleted file mode 100644 index b4a434889..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_19f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_19f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_19f_states.bin.lz4 new file mode 100644 index 000000000..0abb61eb4 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_19f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1a4_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1a4_states.bin.bz2 deleted file mode 100644 index ca79aa9b0..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1a4_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1a4_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1a4_states.bin.lz4 new file mode 100644 index 000000000..a8bc77435 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1a4_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1ac_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1ac_states.bin.bz2 deleted file mode 100644 index 944d52e07..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1ac_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1ac_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1ac_states.bin.lz4 new file mode 100644 index 000000000..bdee2aa73 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1ac_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1b1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1b1_states.bin.bz2 deleted file mode 100644 index 860f486bf..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1b1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1b1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1b1_states.bin.lz4 new file mode 100644 index 000000000..0c1bbe06d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1b1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1b3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1b3_states.bin.bz2 deleted file mode 100644 index 8afe0ffb9..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1b3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1b3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1b3_states.bin.lz4 new file mode 100644 index 000000000..a6ed5254c Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1b3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1b5_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1b5_states.bin.bz2 deleted file mode 100644 index 804b581f8..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1b5_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1b5_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1b5_states.bin.lz4 new file mode 100644 index 000000000..04723ba94 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1b5_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1b7_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1b7_states.bin.bz2 deleted file mode 100644 index 8d541ff7d..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1b7_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1b7_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1b7_states.bin.lz4 new file mode 100644 index 000000000..864a67df5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1b7_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1b9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1b9_states.bin.bz2 deleted file mode 100644 index 42269c116..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1b9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1b9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1b9_states.bin.lz4 new file mode 100644 index 000000000..a80815ec5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1b9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1bb_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1bb_states.bin.bz2 deleted file mode 100644 index f1f353e41..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1bb_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1bb_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1bb_states.bin.lz4 new file mode 100644 index 000000000..026830879 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1bb_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1bd_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1bd_states.bin.bz2 deleted file mode 100644 index ce30db7be..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1bd_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1bd_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1bd_states.bin.lz4 new file mode 100644 index 000000000..5195b6cb0 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1bd_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1bf_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1bf_states.bin.bz2 deleted file mode 100644 index 43164354d..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1bf_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1bf_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1bf_states.bin.lz4 new file mode 100644 index 000000000..0136c6877 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1bf_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1c1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1c1_states.bin.bz2 deleted file mode 100644 index a805bf22a..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1c1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1c1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1c1_states.bin.lz4 new file mode 100644 index 000000000..5c6f1d6ab Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1c1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1c3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1c3_states.bin.bz2 deleted file mode 100644 index 5ef2b8efa..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1c3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1c3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1c3_states.bin.lz4 new file mode 100644 index 000000000..4c8bc7add Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1c3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1c5_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1c5_states.bin.bz2 deleted file mode 100644 index abc9ef501..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1c5_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1c5_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1c5_states.bin.lz4 new file mode 100644 index 000000000..7a03cca13 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1c5_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1c9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1c9_states.bin.bz2 deleted file mode 100644 index 6d8d002e6..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1c9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1c9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1c9_states.bin.lz4 new file mode 100644 index 000000000..10be732e1 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1c9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1cb_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1cb_states.bin.bz2 deleted file mode 100644 index e9caa94a6..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1cb_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1cb_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1cb_states.bin.lz4 new file mode 100644 index 000000000..df2ced6e2 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1cb_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1d0_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1d0_states.bin.bz2 deleted file mode 100644 index 26a43e303..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1d0_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1d0_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1d0_states.bin.lz4 new file mode 100644 index 000000000..ae0fab92d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1d0_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1d4_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1d4_states.bin.bz2 deleted file mode 100644 index 15911b954..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1d4_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1d4_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1d4_states.bin.lz4 new file mode 100644 index 000000000..5734c0aa7 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1d4_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1dc_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1dc_states.bin.bz2 deleted file mode 100644 index ab5149491..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1dc_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1dc_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1dc_states.bin.lz4 new file mode 100644 index 000000000..0226241f9 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1dc_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1e1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1e1_states.bin.bz2 deleted file mode 100644 index 7e4c4e5a8..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1e1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1e1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1e1_states.bin.lz4 new file mode 100644 index 000000000..c3b856a36 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1e1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1e3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1e3_states.bin.bz2 deleted file mode 100644 index 887da78ed..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1e3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1e3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1e3_states.bin.lz4 new file mode 100644 index 000000000..49d5e69d4 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1e3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1e5_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1e5_states.bin.bz2 deleted file mode 100644 index 43b2cee21..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1e5_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1e5_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1e5_states.bin.lz4 new file mode 100644 index 000000000..6f2431a30 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1e5_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1e7_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1e7_states.bin.bz2 deleted file mode 100644 index 89ebe72a9..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1e7_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1e7_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1e7_states.bin.lz4 new file mode 100644 index 000000000..c7f8fad60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1e7_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1e9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1e9_states.bin.bz2 deleted file mode 100644 index 64ab8f4ce..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1e9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1e9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1e9_states.bin.lz4 new file mode 100644 index 000000000..1d1a9d20e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1e9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1eb_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1eb_states.bin.bz2 deleted file mode 100644 index d76993719..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1eb_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1eb_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1eb_states.bin.lz4 new file mode 100644 index 000000000..cebb8c05e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1eb_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1ed_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1ed_states.bin.bz2 deleted file mode 100644 index 8574c08f7..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1ed_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1ed_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1ed_states.bin.lz4 new file mode 100644 index 000000000..7f18cb77d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1ed_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1ef_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1ef_states.bin.bz2 deleted file mode 100644 index 7a9012f47..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1ef_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1ef_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1ef_states.bin.lz4 new file mode 100644 index 000000000..e534c2013 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1ef_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1f0_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1f0_states.bin.bz2 deleted file mode 100644 index 058cbfd82..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1f0_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1f0_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1f0_states.bin.lz4 new file mode 100644 index 000000000..63b4270d1 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1f0_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1f4_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1f4_states.bin.bz2 deleted file mode 100644 index 69262a53f..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1f4_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1f4_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1f4_states.bin.lz4 new file mode 100644 index 000000000..52ec12172 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1f4_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_1fc_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_1fc_states.bin.bz2 deleted file mode 100644 index c3d550787..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_1fc_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_1fc_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_1fc_states.bin.lz4 new file mode 100644 index 000000000..3fd553268 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_1fc_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_210_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_210_states.bin.bz2 deleted file mode 100644 index fe85775bf..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_210_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_210_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_210_states.bin.lz4 new file mode 100644 index 000000000..35817b77d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_210_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_225_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_225_states.bin.bz2 deleted file mode 100644 index 15195d213..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_225_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_225_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_225_states.bin.lz4 new file mode 100644 index 000000000..a5259b624 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_225_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_227_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_227_states.bin.bz2 deleted file mode 100644 index 18a6d4987..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_227_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_227_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_227_states.bin.lz4 new file mode 100644 index 000000000..08c311533 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_227_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_22d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_22d_states.bin.bz2 deleted file mode 100644 index fa1be58fd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_22d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_22d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_22d_states.bin.lz4 new file mode 100644 index 000000000..b315cfd60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_22d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_22f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_22f_states.bin.bz2 deleted file mode 100644 index b6c1eb54c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_22f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_22f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_22f_states.bin.lz4 new file mode 100644 index 000000000..c7380444a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_22f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_240_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_240_states.bin.bz2 deleted file mode 100644 index 6d1908e79..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_240_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_240_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_240_states.bin.lz4 new file mode 100644 index 000000000..e835ce417 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_240_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_275_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_275_states.bin.bz2 deleted file mode 100644 index 15195d213..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_275_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_275_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_275_states.bin.lz4 new file mode 100644 index 000000000..a5259b624 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_275_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_277_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_277_states.bin.bz2 deleted file mode 100644 index 18a6d4987..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_277_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_277_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_277_states.bin.lz4 new file mode 100644 index 000000000..08c311533 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_277_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_27f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_27f_states.bin.bz2 deleted file mode 100644 index b6c1eb54c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_27f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_27f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_27f_states.bin.lz4 new file mode 100644 index 000000000..c7380444a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_27f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_294_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_294_states.bin.bz2 deleted file mode 100644 index b01f02208..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_294_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_294_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_294_states.bin.lz4 new file mode 100644 index 000000000..e4074f984 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_294_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_2a1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_2a1_states.bin.bz2 deleted file mode 100644 index 15195d213..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_2a1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_2a1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_2a1_states.bin.lz4 new file mode 100644 index 000000000..a5259b624 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_2a1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_2a3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_2a3_states.bin.bz2 deleted file mode 100644 index 18a6d4987..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_2a3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_2a3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_2a3_states.bin.lz4 new file mode 100644 index 000000000..08c311533 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_2a3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_2a9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_2a9_states.bin.bz2 deleted file mode 100644 index fa1be58fd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_2a9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_2a9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_2a9_states.bin.lz4 new file mode 100644 index 000000000..b315cfd60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_2a9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_2ab_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_2ab_states.bin.bz2 deleted file mode 100644 index b6c1eb54c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_2ab_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_2ab_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_2ab_states.bin.lz4 new file mode 100644 index 000000000..c7380444a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_2ab_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_2c4_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_2c4_states.bin.bz2 deleted file mode 100644 index b01f02208..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_2c4_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_2c4_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_2c4_states.bin.lz4 new file mode 100644 index 000000000..e4074f984 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_2c4_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_2f1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_2f1_states.bin.bz2 deleted file mode 100644 index 15195d213..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_2f1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_2f1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_2f1_states.bin.lz4 new file mode 100644 index 000000000..a5259b624 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_2f1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_2f3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_2f3_states.bin.bz2 deleted file mode 100644 index 18a6d4987..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_2f3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_2f3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_2f3_states.bin.lz4 new file mode 100644 index 000000000..08c311533 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_2f3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_2f9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_2f9_states.bin.bz2 deleted file mode 100644 index fa1be58fd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_2f9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_2f9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_2f9_states.bin.lz4 new file mode 100644 index 000000000..b315cfd60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_2f9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_2fb_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_2fb_states.bin.bz2 deleted file mode 100644 index b6c1eb54c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_2fb_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_2fb_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_2fb_states.bin.lz4 new file mode 100644 index 000000000..c7380444a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_2fb_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_335_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_335_states.bin.bz2 deleted file mode 100644 index 15195d213..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_335_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_335_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_335_states.bin.lz4 new file mode 100644 index 000000000..a5259b624 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_335_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_337_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_337_states.bin.bz2 deleted file mode 100644 index 18a6d4987..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_337_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_337_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_337_states.bin.lz4 new file mode 100644 index 000000000..08c311533 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_337_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_33d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_33d_states.bin.bz2 deleted file mode 100644 index fa1be58fd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_33d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_33d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_33d_states.bin.lz4 new file mode 100644 index 000000000..b315cfd60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_33d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_33f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_33f_states.bin.bz2 deleted file mode 100644 index b6c1eb54c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_33f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_33f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_33f_states.bin.lz4 new file mode 100644 index 000000000..c7380444a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_33f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_350_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_350_states.bin.bz2 deleted file mode 100644 index fe85775bf..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_350_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_350_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_350_states.bin.lz4 new file mode 100644 index 000000000..35817b77d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_350_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_365_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_365_states.bin.bz2 deleted file mode 100644 index 15195d213..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_365_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_365_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_365_states.bin.lz4 new file mode 100644 index 000000000..a5259b624 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_365_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_367_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_367_states.bin.bz2 deleted file mode 100644 index 18a6d4987..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_367_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_367_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_367_states.bin.lz4 new file mode 100644 index 000000000..08c311533 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_367_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_36d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_36d_states.bin.bz2 deleted file mode 100644 index fa1be58fd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_36d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_36d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_36d_states.bin.lz4 new file mode 100644 index 000000000..b315cfd60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_36d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_36f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_36f_states.bin.bz2 deleted file mode 100644 index b6c1eb54c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_36f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_36f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_36f_states.bin.lz4 new file mode 100644 index 000000000..c7380444a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_36f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_384_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_384_states.bin.bz2 deleted file mode 100644 index b01f02208..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_384_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_384_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_384_states.bin.lz4 new file mode 100644 index 000000000..e4074f984 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_384_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_3b1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_3b1_states.bin.bz2 deleted file mode 100644 index 15195d213..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_3b1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_3b1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_3b1_states.bin.lz4 new file mode 100644 index 000000000..a5259b624 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_3b1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_3b3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_3b3_states.bin.bz2 deleted file mode 100644 index 18a6d4987..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_3b3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_3b3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_3b3_states.bin.lz4 new file mode 100644 index 000000000..08c311533 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_3b3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_3b9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_3b9_states.bin.bz2 deleted file mode 100644 index fa1be58fd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_3b9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_3b9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_3b9_states.bin.lz4 new file mode 100644 index 000000000..b315cfd60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_3b9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_3bb_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_3bb_states.bin.bz2 deleted file mode 100644 index b6c1eb54c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_3bb_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_3bb_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_3bb_states.bin.lz4 new file mode 100644 index 000000000..c7380444a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_3bb_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_3d4_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_3d4_states.bin.bz2 deleted file mode 100644 index b01f02208..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_3d4_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_3d4_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_3d4_states.bin.lz4 new file mode 100644 index 000000000..e4074f984 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_3d4_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_3e1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_3e1_states.bin.bz2 deleted file mode 100644 index 15195d213..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_3e1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_3e1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_3e1_states.bin.lz4 new file mode 100644 index 000000000..a5259b624 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_3e1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_3e3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_3e3_states.bin.bz2 deleted file mode 100644 index 18a6d4987..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_3e3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_3e3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_3e3_states.bin.lz4 new file mode 100644 index 000000000..08c311533 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_3e3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_3e9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_3e9_states.bin.bz2 deleted file mode 100644 index fa1be58fd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_3e9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_3e9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_3e9_states.bin.lz4 new file mode 100644 index 000000000..b315cfd60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_3e9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_0_3eb_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_0_3eb_states.bin.bz2 deleted file mode 100644 index b6c1eb54c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_0_3eb_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_0_3eb_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_0_3eb_states.bin.lz4 new file mode 100644 index 000000000..c7380444a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_0_3eb_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_002_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_002_states.bin.bz2 deleted file mode 100644 index 4c9093b38..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_002_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_002_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_002_states.bin.lz4 new file mode 100644 index 000000000..067f3cc33 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_002_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_008_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_008_states.bin.bz2 deleted file mode 100644 index 89d88b572..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_008_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_008_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_008_states.bin.lz4 new file mode 100644 index 000000000..9230a93a4 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_008_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_00a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_00a_states.bin.bz2 deleted file mode 100644 index 38af88dff..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_00a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_00a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_00a_states.bin.lz4 new file mode 100644 index 000000000..826825600 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_00a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_012_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_012_states.bin.bz2 deleted file mode 100644 index 492261923..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_012_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_012_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_012_states.bin.lz4 new file mode 100644 index 000000000..ead8e206e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_012_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_018_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_018_states.bin.bz2 deleted file mode 100644 index c0a592378..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_018_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_018_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_018_states.bin.lz4 new file mode 100644 index 000000000..03b98ebf8 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_018_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_01a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_01a_states.bin.bz2 deleted file mode 100644 index 98093a999..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_01a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_01a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_01a_states.bin.lz4 new file mode 100644 index 000000000..f9216c396 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_01a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_020_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_020_states.bin.bz2 deleted file mode 100644 index 333dab4c5..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_020_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_020_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_020_states.bin.lz4 new file mode 100644 index 000000000..7b0d648cb Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_020_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_028_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_028_states.bin.bz2 deleted file mode 100644 index 7f2e66c32..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_028_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_028_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_028_states.bin.lz4 new file mode 100644 index 000000000..e0e9538ca Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_028_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_02a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_02a_states.bin.bz2 deleted file mode 100644 index 18c04fa47..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_02a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_02a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_02a_states.bin.lz4 new file mode 100644 index 000000000..a1347c788 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_02a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_02e_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_02e_states.bin.bz2 deleted file mode 100644 index 2ecb621b3..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_02e_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_02e_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_02e_states.bin.lz4 new file mode 100644 index 000000000..16254a7e4 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_02e_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_032_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_032_states.bin.bz2 deleted file mode 100644 index 083f1f0a5..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_032_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_032_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_032_states.bin.lz4 new file mode 100644 index 000000000..b3f9ccbfa Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_032_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_036_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_036_states.bin.bz2 deleted file mode 100644 index 7fa414bd8..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_036_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_036_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_036_states.bin.lz4 new file mode 100644 index 000000000..843e1adee Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_036_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_038_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_038_states.bin.bz2 deleted file mode 100644 index 15f739f1b..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_038_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_038_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_038_states.bin.lz4 new file mode 100644 index 000000000..7bb6d9648 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_038_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_03a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_03a_states.bin.bz2 deleted file mode 100644 index 61ef02268..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_03a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_03a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_03a_states.bin.lz4 new file mode 100644 index 000000000..0f3512d21 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_03a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_03e_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_03e_states.bin.bz2 deleted file mode 100644 index 7dd251575..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_03e_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_03e_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_03e_states.bin.lz4 new file mode 100644 index 000000000..7d66dd7eb Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_03e_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_040_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_040_states.bin.bz2 deleted file mode 100644 index 87d8693df..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_040_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_040_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_040_states.bin.lz4 new file mode 100644 index 000000000..2d542516a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_040_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_042_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_042_states.bin.bz2 deleted file mode 100644 index b757bce02..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_042_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_042_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_042_states.bin.lz4 new file mode 100644 index 000000000..ad1bd544c Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_042_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_046_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_046_states.bin.bz2 deleted file mode 100644 index b1454b4e8..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_046_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_046_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_046_states.bin.lz4 new file mode 100644 index 000000000..d6bf32445 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_046_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_048_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_048_states.bin.bz2 deleted file mode 100644 index 99563f5db..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_048_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_048_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_048_states.bin.lz4 new file mode 100644 index 000000000..d8d050de5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_048_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_04a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_04a_states.bin.bz2 deleted file mode 100644 index b31851187..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_04a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_04a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_04a_states.bin.lz4 new file mode 100644 index 000000000..557f84698 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_04a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_04e_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_04e_states.bin.bz2 deleted file mode 100644 index d0a946c4e..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_04e_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_04e_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_04e_states.bin.lz4 new file mode 100644 index 000000000..6fa29a650 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_04e_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_052_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_052_states.bin.bz2 deleted file mode 100644 index 95d0d7af7..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_052_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_052_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_052_states.bin.lz4 new file mode 100644 index 000000000..b5c4b3812 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_052_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_056_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_056_states.bin.bz2 deleted file mode 100644 index 92dee0efa..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_056_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_056_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_056_states.bin.lz4 new file mode 100644 index 000000000..609c9acc4 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_056_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_058_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_058_states.bin.bz2 deleted file mode 100644 index ca2fe1a7a..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_058_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_058_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_058_states.bin.lz4 new file mode 100644 index 000000000..88aee3f16 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_058_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_05a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_05a_states.bin.bz2 deleted file mode 100644 index 2771dced1..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_05a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_05a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_05a_states.bin.lz4 new file mode 100644 index 000000000..18eedc09b Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_05a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_05e_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_05e_states.bin.bz2 deleted file mode 100644 index 94c8612fd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_05e_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_05e_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_05e_states.bin.lz4 new file mode 100644 index 000000000..c30a74316 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_05e_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_060_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_060_states.bin.bz2 deleted file mode 100644 index edc274c4b..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_060_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_060_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_060_states.bin.lz4 new file mode 100644 index 000000000..c440016cf Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_060_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_062_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_062_states.bin.bz2 deleted file mode 100644 index 5b50711b3..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_062_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_062_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_062_states.bin.lz4 new file mode 100644 index 000000000..de48057f6 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_062_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_066_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_066_states.bin.bz2 deleted file mode 100644 index e599d5318..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_066_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_066_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_066_states.bin.lz4 new file mode 100644 index 000000000..dfd8b4e9e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_066_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_068_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_068_states.bin.bz2 deleted file mode 100644 index d52f1573d..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_068_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_068_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_068_states.bin.lz4 new file mode 100644 index 000000000..8ed807ea3 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_068_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_06a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_06a_states.bin.bz2 deleted file mode 100644 index 05cdc3556..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_06a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_06a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_06a_states.bin.lz4 new file mode 100644 index 000000000..ccf2cf30a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_06a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_06e_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_06e_states.bin.bz2 deleted file mode 100644 index f1087952d..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_06e_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_06e_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_06e_states.bin.lz4 new file mode 100644 index 000000000..6adc3eead Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_06e_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_072_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_072_states.bin.bz2 deleted file mode 100644 index 138c17c0f..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_072_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_072_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_072_states.bin.lz4 new file mode 100644 index 000000000..8b2991446 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_072_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_076_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_076_states.bin.bz2 deleted file mode 100644 index db0df90a6..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_076_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_076_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_076_states.bin.lz4 new file mode 100644 index 000000000..08f0dfa3a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_076_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_078_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_078_states.bin.bz2 deleted file mode 100644 index 2c6f9cd4d..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_078_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_078_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_078_states.bin.lz4 new file mode 100644 index 000000000..d63ed136a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_078_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_07a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_07a_states.bin.bz2 deleted file mode 100644 index ea2f8386b..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_07a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_07a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_07a_states.bin.lz4 new file mode 100644 index 000000000..ddd8741b0 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_07a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_07e_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_07e_states.bin.bz2 deleted file mode 100644 index 00d59bf75..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_07e_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_07e_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_07e_states.bin.lz4 new file mode 100644 index 000000000..0d50053bc Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_07e_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_080_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_080_states.bin.bz2 deleted file mode 100644 index 54b71e2ce..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_080_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_080_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_080_states.bin.lz4 new file mode 100644 index 000000000..016ecfe67 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_080_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_082_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_082_states.bin.bz2 deleted file mode 100644 index 9937ecd2a..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_082_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_082_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_082_states.bin.lz4 new file mode 100644 index 000000000..3ebd8d8d5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_082_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_086_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_086_states.bin.bz2 deleted file mode 100644 index 30d232f69..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_086_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_086_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_086_states.bin.lz4 new file mode 100644 index 000000000..56a80b7e3 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_086_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_088_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_088_states.bin.bz2 deleted file mode 100644 index cf826e416..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_088_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_088_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_088_states.bin.lz4 new file mode 100644 index 000000000..1dde249a5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_088_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_08a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_08a_states.bin.bz2 deleted file mode 100644 index b708bd851..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_08a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_08a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_08a_states.bin.lz4 new file mode 100644 index 000000000..2a3ee1493 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_08a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_08e_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_08e_states.bin.bz2 deleted file mode 100644 index 79c5eec83..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_08e_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_08e_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_08e_states.bin.lz4 new file mode 100644 index 000000000..8cbee39df Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_08e_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_092_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_092_states.bin.bz2 deleted file mode 100644 index 917b8cc2f..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_092_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_092_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_092_states.bin.lz4 new file mode 100644 index 000000000..bf6423f2f Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_092_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_096_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_096_states.bin.bz2 deleted file mode 100644 index f5b0d1e39..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_096_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_096_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_096_states.bin.lz4 new file mode 100644 index 000000000..ac2ec4cd5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_096_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_098_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_098_states.bin.bz2 deleted file mode 100644 index 648c9f2b9..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_098_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_098_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_098_states.bin.lz4 new file mode 100644 index 000000000..ed3e88627 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_098_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_09a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_09a_states.bin.bz2 deleted file mode 100644 index 261c672de..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_09a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_09a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_09a_states.bin.lz4 new file mode 100644 index 000000000..0377fddff Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_09a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_09e_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_09e_states.bin.bz2 deleted file mode 100644 index bddf5c91f..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_09e_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_09e_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_09e_states.bin.lz4 new file mode 100644 index 000000000..8fc08c44e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_09e_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0a0_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0a0_states.bin.bz2 deleted file mode 100644 index fe09d19cb..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0a0_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0a0_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0a0_states.bin.lz4 new file mode 100644 index 000000000..1feeb89e2 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0a0_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0a2_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0a2_states.bin.bz2 deleted file mode 100644 index 8aa9c2542..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0a2_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0a2_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0a2_states.bin.lz4 new file mode 100644 index 000000000..0150dbd78 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0a2_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0a6_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0a6_states.bin.bz2 deleted file mode 100644 index aa6ec2408..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0a6_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0a6_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0a6_states.bin.lz4 new file mode 100644 index 000000000..b120016e2 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0a6_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0a8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0a8_states.bin.bz2 deleted file mode 100644 index 6d8970c2e..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0a8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0a8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0a8_states.bin.lz4 new file mode 100644 index 000000000..592af17d4 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0a8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0aa_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0aa_states.bin.bz2 deleted file mode 100644 index e23808553..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0aa_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0aa_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0aa_states.bin.lz4 new file mode 100644 index 000000000..21b477241 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0aa_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0ae_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0ae_states.bin.bz2 deleted file mode 100644 index bfc6848dd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0ae_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0ae_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0ae_states.bin.lz4 new file mode 100644 index 000000000..ab5ece8cb Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0ae_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0b2_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0b2_states.bin.bz2 deleted file mode 100644 index 05a4d0fd5..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0b2_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0b2_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0b2_states.bin.lz4 new file mode 100644 index 000000000..e63a9862d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0b2_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0b6_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0b6_states.bin.bz2 deleted file mode 100644 index 34fd067ec..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0b6_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0b6_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0b6_states.bin.lz4 new file mode 100644 index 000000000..5f3d72c5e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0b6_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0b8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0b8_states.bin.bz2 deleted file mode 100644 index f638e4e1b..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0b8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0b8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0b8_states.bin.lz4 new file mode 100644 index 000000000..df41059c8 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0b8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0ba_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0ba_states.bin.bz2 deleted file mode 100644 index 7be7169f4..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0ba_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0ba_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0ba_states.bin.lz4 new file mode 100644 index 000000000..3467ef92e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0ba_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0be_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0be_states.bin.bz2 deleted file mode 100644 index 91e544e4c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0be_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0be_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0be_states.bin.lz4 new file mode 100644 index 000000000..6833be254 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0be_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0c0_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0c0_states.bin.bz2 deleted file mode 100644 index 87d8693df..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0c0_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0c0_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0c0_states.bin.lz4 new file mode 100644 index 000000000..2d542516a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0c0_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0c2_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0c2_states.bin.bz2 deleted file mode 100644 index 05f424f30..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0c2_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0c2_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0c2_states.bin.lz4 new file mode 100644 index 000000000..8bd7ee05c Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0c2_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0c6_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0c6_states.bin.bz2 deleted file mode 100644 index c104fb1de..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0c6_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0c6_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0c6_states.bin.lz4 new file mode 100644 index 000000000..0906007e9 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0c6_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0c8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0c8_states.bin.bz2 deleted file mode 100644 index 6548e5270..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0c8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0c8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0c8_states.bin.lz4 new file mode 100644 index 000000000..b303d2141 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0c8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0ca_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0ca_states.bin.bz2 deleted file mode 100644 index 8ceab191a..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0ca_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0ca_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0ca_states.bin.lz4 new file mode 100644 index 000000000..a9f7fd045 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0ca_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0ce_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0ce_states.bin.bz2 deleted file mode 100644 index 26fc3dcd4..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0ce_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0ce_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0ce_states.bin.lz4 new file mode 100644 index 000000000..45d798574 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0ce_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0d2_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0d2_states.bin.bz2 deleted file mode 100644 index 9d6eaefdd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0d2_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0d2_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0d2_states.bin.lz4 new file mode 100644 index 000000000..db3b33de4 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0d2_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0d6_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0d6_states.bin.bz2 deleted file mode 100644 index d6b205973..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0d6_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0d6_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0d6_states.bin.lz4 new file mode 100644 index 000000000..d42480484 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0d6_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0d8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0d8_states.bin.bz2 deleted file mode 100644 index 3a4e9b14c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0d8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0d8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0d8_states.bin.lz4 new file mode 100644 index 000000000..d52ee181d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0d8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0da_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0da_states.bin.bz2 deleted file mode 100644 index f59ffec93..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0da_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0da_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0da_states.bin.lz4 new file mode 100644 index 000000000..255006b82 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0da_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0de_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0de_states.bin.bz2 deleted file mode 100644 index 62759b5d1..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0de_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0de_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0de_states.bin.lz4 new file mode 100644 index 000000000..ff79d1fd6 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0de_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0e0_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0e0_states.bin.bz2 deleted file mode 100644 index 904ba404c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0e0_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0e0_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0e0_states.bin.lz4 new file mode 100644 index 000000000..a8232844a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0e0_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0e8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0e8_states.bin.bz2 deleted file mode 100644 index e969ed97c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0e8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0e8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0e8_states.bin.lz4 new file mode 100644 index 000000000..f99bdf117 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0e8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_0f8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_0f8_states.bin.bz2 deleted file mode 100644 index ebb53232e..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_0f8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_0f8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_0f8_states.bin.lz4 new file mode 100644 index 000000000..2c9de9e5d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_0f8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_108_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_108_states.bin.bz2 deleted file mode 100644 index 2b87ca8f2..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_108_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_108_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_108_states.bin.lz4 new file mode 100644 index 000000000..8882c1f20 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_108_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_111_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_111_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_111_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_111_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_111_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_111_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_113_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_113_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_113_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_113_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_113_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_113_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_115_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_115_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_115_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_115_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_115_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_115_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_117_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_117_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_117_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_117_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_117_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_117_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_118_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_118_states.bin.bz2 deleted file mode 100644 index 2b2bf5915..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_118_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_118_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_118_states.bin.lz4 new file mode 100644 index 000000000..c1794bc03 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_118_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_11a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_11a_states.bin.bz2 deleted file mode 100644 index f7e2d1674..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_11a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_11a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_11a_states.bin.lz4 new file mode 100644 index 000000000..a77eade5e Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_11a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_11b_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_11b_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_11b_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_11b_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_11b_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_11b_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_120_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_120_states.bin.bz2 deleted file mode 100644 index fed958a7f..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_120_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_120_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_120_states.bin.lz4 new file mode 100644 index 000000000..746496cf3 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_120_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_122_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_122_states.bin.bz2 deleted file mode 100644 index 0e406a019..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_122_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_122_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_122_states.bin.lz4 new file mode 100644 index 000000000..7b1d7eee7 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_122_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_128_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_128_states.bin.bz2 deleted file mode 100644 index e02d0729a..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_128_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_128_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_128_states.bin.lz4 new file mode 100644 index 000000000..4c620265d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_128_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_131_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_131_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_131_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_131_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_131_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_131_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_135_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_135_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_135_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_135_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_135_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_135_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_138_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_138_states.bin.bz2 deleted file mode 100644 index c12c2219a..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_138_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_138_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_138_states.bin.lz4 new file mode 100644 index 000000000..87e962761 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_138_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_145_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_145_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_145_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_145_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_145_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_145_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_147_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_147_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_147_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_147_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_147_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_147_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_148_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_148_states.bin.bz2 deleted file mode 100644 index f9760f6b2..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_148_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_148_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_148_states.bin.lz4 new file mode 100644 index 000000000..eeac62486 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_148_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_158_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_158_states.bin.bz2 deleted file mode 100644 index ceb8298d8..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_158_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_158_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_158_states.bin.lz4 new file mode 100644 index 000000000..8ee5757b9 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_158_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_160_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_160_states.bin.bz2 deleted file mode 100644 index 6264a1abb..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_160_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_160_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_160_states.bin.lz4 new file mode 100644 index 000000000..089422698 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_160_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_161_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_161_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_161_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_161_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_161_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_161_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_163_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_163_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_163_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_163_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_163_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_163_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_165_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_165_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_165_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_165_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_165_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_165_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_168_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_168_states.bin.bz2 deleted file mode 100644 index e4ee8cdbb..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_168_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_168_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_168_states.bin.lz4 new file mode 100644 index 000000000..151d6c198 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_168_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_178_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_178_states.bin.bz2 deleted file mode 100644 index ce03d40d3..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_178_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_178_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_178_states.bin.lz4 new file mode 100644 index 000000000..05128e5d5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_178_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_180_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_180_states.bin.bz2 deleted file mode 100644 index 87d8693df..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_180_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_180_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_180_states.bin.lz4 new file mode 100644 index 000000000..2d542516a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_180_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_188_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_188_states.bin.bz2 deleted file mode 100644 index e6392a033..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_188_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_188_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_188_states.bin.lz4 new file mode 100644 index 000000000..01dacce88 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_188_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_191_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_191_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_191_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_191_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_191_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_191_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_198_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_198_states.bin.bz2 deleted file mode 100644 index f8e066f82..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_198_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_198_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_198_states.bin.lz4 new file mode 100644 index 000000000..1b64ec6b5 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_198_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_199_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_199_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_199_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_199_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_199_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_199_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_19d_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_19d_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_19d_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_19d_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_19d_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_19d_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_19f_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_19f_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_19f_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_19f_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_19f_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_19f_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1a0_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1a0_states.bin.bz2 deleted file mode 100644 index da20250ec..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1a0_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1a0_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1a0_states.bin.lz4 new file mode 100644 index 000000000..18008b8bb Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1a0_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1a8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1a8_states.bin.bz2 deleted file mode 100644 index 034c96abd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1a8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1a8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1a8_states.bin.lz4 new file mode 100644 index 000000000..483e921ff Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1a8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1b3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1b3_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1b3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1b3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1b3_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1b3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1b5_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1b5_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1b5_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1b5_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1b5_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1b5_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1b7_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1b7_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1b7_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1b7_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1b7_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1b7_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1b8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1b8_states.bin.bz2 deleted file mode 100644 index c57b7929b..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1b8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1b8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1b8_states.bin.lz4 new file mode 100644 index 000000000..77cf4f142 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1b8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1b9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1b9_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1b9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1b9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1b9_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1b9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1bd_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1bd_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1bd_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1bd_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1bd_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1bd_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1c1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1c1_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1c1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1c1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1c1_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1c1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1c3_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1c3_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1c3_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1c3_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1c3_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1c3_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1c8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1c8_states.bin.bz2 deleted file mode 100644 index 6ced4a199..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1c8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1c8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1c8_states.bin.lz4 new file mode 100644 index 000000000..3f7dd0f90 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1c8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1c9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1c9_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1c9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1c9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1c9_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1c9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1cd_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1cd_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1cd_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1cd_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1cd_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1cd_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1cf_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1cf_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1cf_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1cf_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1cf_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1cf_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1d8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1d8_states.bin.bz2 deleted file mode 100644 index bbc7f1d82..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1d8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1d8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1d8_states.bin.lz4 new file mode 100644 index 000000000..b77d70585 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1d8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e0_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1e0_states.bin.bz2 deleted file mode 100644 index d701fc285..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1e0_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e0_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1e0_states.bin.lz4 new file mode 100644 index 000000000..c6e2f9438 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1e0_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e1_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1e1_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1e1_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e1_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1e1_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1e1_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e5_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1e5_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1e5_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e5_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1e5_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1e5_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e7_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1e7_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1e7_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e7_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1e7_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1e7_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1e8_states.bin.bz2 deleted file mode 100644 index 19bb1cdac..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1e8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1e8_states.bin.lz4 new file mode 100644 index 000000000..42fb9d45d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1e8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e9_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1e9_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1e9_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1e9_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1e9_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1e9_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1eb_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1eb_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1eb_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1eb_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1eb_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1eb_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1ed_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1ed_states.bin.bz2 deleted file mode 100644 index 646198900..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1ed_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1ed_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1ed_states.bin.lz4 new file mode 100644 index 000000000..7ec52a440 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1ed_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_1f8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_1f8_states.bin.bz2 deleted file mode 100644 index b3ff47017..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_1f8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_1f8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_1f8_states.bin.lz4 new file mode 100644 index 000000000..87fbc9d30 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_1f8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_208_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_208_states.bin.bz2 deleted file mode 100644 index b01f02208..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_208_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_208_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_208_states.bin.lz4 new file mode 100644 index 000000000..e4074f984 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_208_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_220_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_220_states.bin.bz2 deleted file mode 100644 index fe85775bf..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_220_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_220_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_220_states.bin.lz4 new file mode 100644 index 000000000..35817b77d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_220_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_24a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_24a_states.bin.bz2 deleted file mode 100644 index 15195d213..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_24a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_24a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_24a_states.bin.lz4 new file mode 100644 index 000000000..a5259b624 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_24a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_24e_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_24e_states.bin.bz2 deleted file mode 100644 index 18a6d4987..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_24e_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_24e_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_24e_states.bin.lz4 new file mode 100644 index 000000000..08c311533 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_24e_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_25a_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_25a_states.bin.bz2 deleted file mode 100644 index fa1be58fd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_25a_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_25a_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_25a_states.bin.lz4 new file mode 100644 index 000000000..b315cfd60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_25a_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_25e_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_25e_states.bin.bz2 deleted file mode 100644 index b6c1eb54c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_25e_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_25e_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_25e_states.bin.lz4 new file mode 100644 index 000000000..c7380444a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_25e_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_262_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_262_states.bin.bz2 deleted file mode 100644 index 15195d213..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_262_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_262_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_262_states.bin.lz4 new file mode 100644 index 000000000..a5259b624 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_262_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_266_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_266_states.bin.bz2 deleted file mode 100644 index 18a6d4987..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_266_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_266_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_266_states.bin.lz4 new file mode 100644 index 000000000..08c311533 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_266_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_272_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_272_states.bin.bz2 deleted file mode 100644 index fa1be58fd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_272_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_272_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_272_states.bin.lz4 new file mode 100644 index 000000000..b315cfd60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_272_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_276_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_276_states.bin.bz2 deleted file mode 100644 index b6c1eb54c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_276_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_276_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_276_states.bin.lz4 new file mode 100644 index 000000000..c7380444a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_276_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_280_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_280_states.bin.bz2 deleted file mode 100644 index 6d1908e79..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_280_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_280_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_280_states.bin.lz4 new file mode 100644 index 000000000..e835ce417 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_280_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_2a8_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_2a8_states.bin.bz2 deleted file mode 100644 index b01f02208..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_2a8_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_2a8_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_2a8_states.bin.lz4 new file mode 100644 index 000000000..e4074f984 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_2a8_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_2c2_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_2c2_states.bin.bz2 deleted file mode 100644 index 15195d213..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_2c2_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_2c2_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_2c2_states.bin.lz4 new file mode 100644 index 000000000..a5259b624 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_2c2_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_2c6_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_2c6_states.bin.bz2 deleted file mode 100644 index 18a6d4987..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_2c6_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_2c6_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_2c6_states.bin.lz4 new file mode 100644 index 000000000..08c311533 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_2c6_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_2d2_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_2d2_states.bin.bz2 deleted file mode 100644 index fa1be58fd..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_2d2_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_2d2_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_2d2_states.bin.lz4 new file mode 100644 index 000000000..b315cfd60 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_2d2_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_2d6_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_2d6_states.bin.bz2 deleted file mode 100644 index b6c1eb54c..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_2d6_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_2d6_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_2d6_states.bin.lz4 new file mode 100644 index 000000000..c7380444a Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_2d6_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_328_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_328_states.bin.bz2 deleted file mode 100644 index b01f02208..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_328_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_328_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_328_states.bin.lz4 new file mode 100644 index 000000000..e4074f984 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_328_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_388_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_388_states.bin.bz2 deleted file mode 100644 index b01f02208..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_388_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_388_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_388_states.bin.lz4 new file mode 100644 index 000000000..e4074f984 Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_388_states.bin.lz4 differ diff --git a/client/resources/hardnested_tables/bitflip_1_3a0_states.bin.bz2 b/client/resources/hardnested_tables/bitflip_1_3a0_states.bin.bz2 deleted file mode 100644 index fe85775bf..000000000 Binary files a/client/resources/hardnested_tables/bitflip_1_3a0_states.bin.bz2 and /dev/null differ diff --git a/client/resources/hardnested_tables/bitflip_1_3a0_states.bin.lz4 b/client/resources/hardnested_tables/bitflip_1_3a0_states.bin.lz4 new file mode 100644 index 000000000..35817b77d Binary files /dev/null and b/client/resources/hardnested_tables/bitflip_1_3a0_states.bin.lz4 differ diff --git a/client/resources/mad.json b/client/resources/mad.json index cfa6294d3..a11236189 100644 --- a/client/resources/mad.json +++ b/client/resources/mad.json @@ -2940,7 +2940,7 @@ "system_integrator": "A.I.I." }, { - "application": "Multi-Modal Transit", + "application": "Multi-Modal Transit (Vix/ERG)", "company": "ERG Transit Systems", "mad": "0x2103", "service_provider": "ERG Transit Systems", @@ -2953,6 +2953,13 @@ "service_provider": "ERG Transit Systems", "system_integrator": "ERG Transit Systems" }, + { + "application": "Transit (Metrocard / Christchurch)", + "company": "INIT Transit Systems", + "mad": "0x2105", + "service_provider": "INIT Transit Systems", + "system_integrator": "INIT Transit Systems" + }, { "application": "Mass Transportation Ticketing", "company": "Omnifare", @@ -2995,6 +3002,13 @@ "service_provider": "Emcard", "system_integrator": "Emtest" }, + { + "application": "Integrated multi-modal transport ticketing system", + "company": "HOP Fastpass", + "mad": "0x210E", + "service_provider": "HOP Fastpass", + "system_integrator": "HOP Fastpass" + }, { "application": "Integrated multi-modal transport ticketing system", "company": "Keane Australia PTY Limited", @@ -3038,7 +3052,7 @@ "system_integrator": "T-Systems GEI" }, { - "application": "Transportation ticket", + "application": "Transportation ticket (Lítačka)", "company": "Haguess s.r.o.", "mad": "0x2115", "service_provider": "HAGUESS, a.s.", @@ -3135,6 +3149,35 @@ "service_provider": "RTD", "system_integrator": "ACS" }, + { + "application": "Bus and rail fare collection (HOLO)", + "company": "INIT", + "mad": "0x2136", + "service_provider": "INIT", + "system_integrator": "INIT" + }, + { + "application": "Bus and rail fare collection (Ventra)", + "company": "Cubic", + "mad": "0x2138", + "service_provider": "Cubic", + "system_integrator": "Cubic" + }, + { + "application": "Bus and rail fare collection (Clipper)", + "company": "Cubic", + "mad": "0x2139", + "service_provider": "Cubic", + "system_integrator": "Cubic" + }, + { + "application": "Bus and rail fare collection (WAVE)", + "company": "INIT Rhode Island", + "mad": "0x213A", + "service_provider": "INIT Rhode Island", + "system_integrator": "INIT Rhode Island" + }, + { "application": "Bus services with extension to rail and taxi", "company": "Questek Marketing", @@ -4073,6 +4116,20 @@ "service_provider": "HID Corporation", "system_integrator": "HID Corporation" }, + { + "application": "City transport, prepaid ticket, cardholder, servicespass", + "company": "Ridango AS", + "mad": "0x3C56", + "service_provider": "Pilet.ee ekaart", + "system_integrator": "Pilet.ee ekaart" + }, + { + "application": "City transport, prepaid ticket, cardholder, servicespass", + "company": "Ridango AS", + "mad": "0x3D56", + "service_provider": "Pilet.ee ekaart", + "system_integrator": "Pilet.ee ekaart" + }, { "application": "City transport bus, ferry, administration", "company": "VFJ Technology Pty Ltd", @@ -13607,5 +13664,12 @@ "mad": "0xF001", "service_provider": "Tech ID", "system_integrator": "Tech ID" + }, + { + "application": "Miscellaneous applications", + "company": "Reserved For future Use", + "mad": "0xFFFF", + "service_provider": "RFU", + "system_integrator": "RFU" } ] diff --git a/client/resources/sim014.bin b/client/resources/sim014.bin new file mode 100644 index 000000000..d4f91b0c3 Binary files /dev/null and b/client/resources/sim014.bin differ diff --git a/client/resources/sim014.sha512.txt b/client/resources/sim014.sha512.txt new file mode 100644 index 000000000..2daf74b65 --- /dev/null +++ b/client/resources/sim014.sha512.txt @@ -0,0 +1 @@ +8b754191cec19a8172ff77443eee67490f61c53161af2e831ff4e8c7909b788802f5c0b68cecdc5decabe7852479740a0122832803af26b68beab814ced543b8 *client/resources/sim014.bin diff --git a/client/resources/vas_privkey.der b/client/resources/vas_privkey.der new file mode 100644 index 000000000..318a651c7 Binary files /dev/null and b/client/resources/vas_privkey.der differ diff --git a/client/src/atrs.c b/client/src/atrs.c index 0b22eb79b..d8f11587f 100644 --- a/client/src/atrs.c +++ b/client/src/atrs.c @@ -18,8 +18,8 @@ #include "atrs.h" #include #include -#include "commonutil.h" // ARRAYLEN -#include "ui.h" // PrintAndLogEx +#include "commonutil.h" // ARRAYLEN +#include "ui.h" // PrintAndLogEx // get a ATR description based on the atr bytes // returns description of the best match @@ -32,7 +32,7 @@ const char *getAtrInfo(const char *atr_str) { if (strlen(AtrTable[i].bytes) != slen) continue; - if (strstr(AtrTable[i].bytes, "..") != NULL) { + if (strstr(AtrTable[i].bytes, ".") != NULL) { char *tmp_atr = calloc(slen, sizeof(uint8_t)); if (tmp_atr == NULL) { PrintAndLogEx(FAILED, "failed to allocate memory"); @@ -40,7 +40,7 @@ const char *getAtrInfo(const char *atr_str) { } for (int j = 0; j < slen; j++) { - tmp_atr[j] = AtrTable[i].bytes[j] == '.' ? '.' : atr_str[j]; + tmp_atr[j] = (AtrTable[i].bytes[j] == '.') ? '.' : atr_str[j]; } if (strncmp(tmp_atr, AtrTable[i].bytes, slen) == 0) { @@ -50,9 +50,12 @@ const char *getAtrInfo(const char *atr_str) { free(tmp_atr); } else { - if (strncmp(atr_str, AtrTable[i].bytes, slen) == 0) return AtrTable[i].desc; + if (strncmp(atr_str, AtrTable[i].bytes, slen) == 0) { + return AtrTable[i].desc; + } } } + if (match >= 0) { return AtrTable[match].desc; } else { diff --git a/client/src/atrs.h b/client/src/atrs.h index 87171c523..134e74d77 100644 --- a/client/src/atrs.h +++ b/client/src/atrs.h @@ -34,6 +34,7 @@ const char *getAtrInfo(const char *atr_str); // atr_t array is expected to be NULL terminated const static atr_t AtrTable[] = { { "3BDF18FFC080B1FE751F033078464646462026204963656D616E1D", "Cardhelper by 0xFFFF and Iceman" }, + { "3B90969181B1FE551FC7D4", "IClass SE Processor (Other) https://www.hidglobal.com/products/embedded-modules/iclass-se/sio-processor"}, { "3B..............0031B8640000000073......829000", "MultiApp ID IAS ECC 72K CC (with IAS XL / IAS ECC Applet) IAS ECC Type 3" }, { "3B..............0031B8640000000073......829000..", "MultiApp ID IAS ECC 72K CC (with IAS XL / IAS ECC Applet) IAS ECC Type 4" }, { "3B........0031B864........73......829000", "IDClassic IAS (old name: IAS TPC) IAS ECC Type 1\nMultiApp ID IAS ECC 72K CC (with IAS XL / IAS ECC Applet) IAS ECC Type 1" }, @@ -43,6 +44,7 @@ const static atr_t AtrTable[] = { { "3B003B28003441454130323030", "Cryptoguard card used for pay tv Plustelka (DVB-T2 - Slovak) (Pay TV)\nhttps://www.plustelka.sk/" }, { "3B021050", "Visa (Bank)" }, { "3B02141C", "UAE (United Arab Emirates) (eID)" }, + { "3B021435", "cartao cidadao (eID)" }, { "3B021450", "Schlumberger Multiflex 3k" }, { "3B02145011", "Maste visa card (Bank)" }, { "3B021451", "8A (Bank)" }, @@ -54,7 +56,7 @@ const static atr_t AtrTable[] = { { "3B026F33", "Polish national PKI equivalent to national id card issued by Eurocert - 1 of 2 Polish authorized issuers (PKI)\nhttps://eurocert.pl/index.php/en-us/" }, { "3B0400000000", "Laundromat payment card\nSpanish ID ('DNIe: Documento Nacional de Identidad electronico).\nhttp://www.dnie.es/" }, { "3B0400000073", "ASE M2 Card (I2C) (Other)" }, - { "3B0400040000", "Italian healtcare card (TS) National Service Card (CNS) - Regione Lazio - (HealthCare)\nhttp://cns.regione.lazio.it" }, + { "3B0400040000", "Italian healthcare card (TS) National Service Card (CNS) - Regione Lazio - (HealthCare)\nhttp://cns.regione.lazio.it" }, { "3B0401020304", "SLE4442 - memory card (256) with write protection PIN. (Other)" }, { "3B0405D486C8", "Telstra (Telecom Australia) Pay Phone Phonecard (Telecommunication)\nhttps://www.telstra.com.au/home-phone/calling-cards" }, { "3B04073C8592", "OLD Spanish Prepaid phone cards (pesetas) provided by Telefonica" }, @@ -70,15 +72,18 @@ const static atr_t AtrTable[] = { { "3B0417D43000", "Gemalto BONZA game smart-card. (Other)" }, { "3B0417D46608", "Prepaid public telephone card from Antel, Uruguay. (Telecommunication)" }, { "3B0417D4C882", "prepaid payphone card, issued by Lietuvos Telekomas (now Teo LT)\nhttps://www.teo.lt/" }, + { "3B04190CF600", "USWest TeleCard for Millennium Payphones (Other)" }, { "3B041BF43F54", "German TelefonKarte 50DM. Memory card with Solaic chip (Other)" }, { "3B042AFF3200", "Mac Gray Intelligent Laundry Systems laundry card" }, + { "3B0437E409EC", "prepaid payphone card, issued by Deutsche Telekom 'Telefonkarte Comfort als klassische Karte' (Other)\nhttps://tkc.telekom-dienste.de/" }, { "3B0441117781", "Sample Mifare DESFire contactless smartcard from Phillips" }, { "3B04411177B1", "IBM JCOP 30 contactless" }, { "3B044932432E", "German Health Insurance Card\n'LogCard' from concept2.com (a indoor rower manufacturer)\nI2C card" }, - { "3B0450D7F30E", "Ukranian memory card for public phones. Manufacturer: 'Kvazar' (Other)" }, + { "3B0450D7F30E", "Ukrainian memory card for public phones. Manufacturer: 'Kvazar' (Other)" }, { "3B046089", "Smartcard 3.1 (JavaCard)" }, { "3B04696C2065", "ASE M2 Card 2Kbit (Other)" }, { "3B047F010000", "Russian memory card for payphones. Manufacturer: 'Exiton' (Other)" }, + { "3B0482231091", "VISA DEBIT (Bank)" }, { "3B0492231091", "Siemens SLE 4432/42 card" }, { "3B04989400C4", "Windsor and Maidenhead Advantage Card\nhttp://www.rbwm.gov.uk/web/advantage_index.htm" }, { "3B0499FFFF90", "Madrid prepaid parkimeters parking card" }, @@ -103,6 +108,7 @@ const static atr_t AtrTable[] = { { "3B06010022018227", "HID ProxCard II (Other)" }, { "3B06010100001073", "HID Prox 26-bit (eID)" }, { "3B06010102000001", "HID0009P (eID)" }, + { "3B06010118038209", "HID ProxKey III (125kHz) (eID)\nhttps://www.hidglobal.com/products/cards-and-credentials/hid-proximity/1346" }, { "3B06010210......", "ISOProx II Card:\nhttp://www.hidglobal.com/documents/isoprox_ds_en.pdf" }, { "3B0602..........", "HID Prox H10302 Format\n37 bit (CN)\nhttps://www.hidglobal.com/sites/default/files/omnikey_contactless_developer_guide.pdf" }, { "3B0604..........", "HID Prox H10304 Format\n37 bit (FAC+CN)\nhttps://www.hidglobal.com/sites/default/files/omnikey_contactless_developer_guide.pdf" }, @@ -136,6 +142,7 @@ const static atr_t AtrTable[] = { { "3B12953608", "EMTG56 0.8 (sub-version 0.1)" }, { "3B12953609", "EMTG56 0.9 (sub-version 0.1)" }, { "3B1396130917", "Teacher Card (Other)" }, + { "3B1411CA4DDEAD", "Tricolor (Telecommunication)" }, { "3B151112CA0700DB", "Tricolor TV center card (Pay TV)" }, { "3B151112CA0711CA", "DRE Crypt (Pay TV)" }, { "3B151112CA0714CF", "PlatformaHD (Russia) DRE Crypt 4AE1:14" }, @@ -165,12 +172,14 @@ const static atr_t AtrTable[] = { { "3B16959B0007011803", "Thai GSM UICC (Telecommunication)" }, { "3B1695D00045F70100", "Telefonica O2 Czech Republic, a.s. - O2 sim card - 173285 / SIM64ND.GO0\nhttp://www.o2.cz" }, { "3B1695D0016CFD0D00", "Virgin Mobile SIM card (SIM)" }, + { "3B1695D0017B010E00", "Vivo Brasil - SIM Card (Telecommunication)" }, { "3B1695D0017BDA0D00", "Verizon GSM SIM (Telecommunication)" }, { "3B1696417374726964", "Gemalto IDPrime v2+ .NET" }, { "3B1696770006010403", "AIS One-2-Call GSM UICC (Telecommunication)\nhttp://www.ais.co.th" }, { "3B1696BA000E010603", "Vinaphone Vietnam SIM" }, { "3B1696BA000E020A03", "Vietel (Telecommunication)" }, { "3B1696D000D4072C00", "Vodafone India Postpaid / Local Operator 3G" }, + { "3B1696D0016CA70D00", "Ukrainian Telecommunications Operator MTS (old simcard, now rebranded to Vodafone UA) (Telecommunication)\nhttps://www.vodafone.ua" }, { "3B17118065AF034200D8", "TELE2 SPB SIM Russia" }, { "3B17139C120201010740", "Schlumberger Cyberflex Access Developer 32k" }, { "3B179418010101014193", "T-Mobile SIM card" }, @@ -196,6 +205,7 @@ const static atr_t AtrTable[] = { { "3B1894451543DA0100FF02", "Airtel Tanzania Prepaid SIM Card (Telecommunication)" }, { "3B1894530D06772407FF02", "GSM SIM Tele2 Estonia, prepaid (Telecommunication)" }, { "3B1894532007AD0A05FF02", "GSM SIM Beeline Kazakhstan (Telecommunication)\nhttp://beeline.kz/" }, + { "3B18962621550401030001", "OBILedit Forensic SIM Cloning card (Telecommunication)\nhttps://www.mobiledit.com/connection-kit" }, { "3B19145590010101000508B0", "Schlumberger Multiflex 8k" }, { "3B19145590010201000504B0", "Schlumberger Multiflex 4k" }, { "3B19145901010F01000508B0", "Schlumberger Multiflex 8k" }, @@ -203,6 +213,7 @@ const static atr_t AtrTable[] = { { "3B19943303013145410602B6", "BASE Twin simcard (Telecommunication)" }, { "3B1994806794080125010101", "Mobiphone (Telecommunication)" }, { "3B1996806793270102020401", "Orange SIM card (Telecommunication)" }, + { "3B1996806794090101040104", "Morpho EIRENE GSM-R Card (Telecommunication)" }, { "3B1996806794160203010101", "Movistar Argentina Usim Card (Telecommunication)\nwww.movistar.com.ar" }, { "3B1996806798040101010101", "PLUS gsm (Telecommunication)" }, { "3B1A9692015493320103022501", "BEELINE (Telecommunication)" }, @@ -216,6 +227,7 @@ const static atr_t AtrTable[] = { { "3B1D9600230700000000000000009000", "emulator card prototype from ST" }, { "3B1D97434C5F53414D00143800009000", "CLSAM (Transport)\nhttp://www.planeta.inf.br" }, { "3B1E130069454D4345663228F86B009000", "ECHS (Ex-servicemen Contributory Health Scheme) Card (HealthCare)\nhttp://echs.gov.in/" }, + { "3B1E95806721544D4D04120E7132829F00", "SIM card of old Ukrainian Telecommunications Operator Ace&Base (deriving from Kyivstar) (Telecommunication)\nhttps://kyivstar.ua/uk/mm" }, { "3B1F110067424146495345535266FF819000", "Finnish student id card" }, { "3B1F110067804246495345105266FF819000", "Nokia branded SC (Setec)" }, { "3B1F11006A013846495345108C02FF079000", "GSM-SIM Saunalahti (from 2004)\nFinnish cell phone operator 'Sonera' SIM card (from 2002)" }, @@ -240,7 +252,7 @@ const static atr_t AtrTable[] = { { "3B23003513FF", "Schlumberger MicroPayflex" }, { "3B2300354180", "PayflexHID (idenfitied by Sun Ray Services)" }, { "3B2400......45", "Conax" }, - { "3B240000000000", "MultiChoice satelite TV Card Eutelsat 36B at 35.9degE\nhttp://www.lyngsat.com/Eutelsat-36B.html" }, + { "3B240000000000", "MultiChoice satellite TV Card Eutelsat 36B at 35.9degE\nhttp://www.lyngsat.com/Eutelsat-36B.html" }, { "3B240000E29267", "Health care (HealthCare)" }, { "3B240030423030", "ComHem Digital-TV smartcard (Sweden)\nTNK Telewizja Na Karte (Poland) - Conditional access system : Conax (Norway)\nhttp://telewizjanakarte.pl/" }, { "3B240080729443", "MPCOS-3DES 64K \\ EMV Filter (Gemplus)" }, @@ -258,6 +270,7 @@ const static atr_t AtrTable[] = { { "3B2600311A45039000", "Tajeta Sanitaria Individual (Spanish Insurance Card)\nfrom the 'Junta de Andalucia(Consejeria de Salud)'" }, { "3B2600311A51039000", "Andalusian Health Service identification card (HealthCare)" }, { "3B270011140003029000", "ITTAS CNA Smart Card Reader (PKI)\nhttps://www.ittas.by/en/solutions/cna-cryptographic-protection-of-information" }, + { "3B27002A2A4352595054", "PAY TV (Pay TV)" }, { "3B27008065A2..010137", "Gemplus GemSAFE Smart Card (4K)" }, { "3B27008065A200010137", "Gemplus GemSAFE Card CSP v1.0" }, { "3B27008065A202028237", "Gemplus GPK2000s" }, @@ -317,7 +330,6 @@ const static atr_t AtrTable[] = { { "3B3B13008066120401040158829000", "Schaffhauser Kantonalbank Switzerland (Bank)\nhttp://www.shkb.ch" }, { "3B3B7F380000006A444E496510024C", "dnie (eID)" }, { "3B3B7F380000006A444E496520024C", "DNI (eID)" }, - { "3B3B8E800180318066B1840C016E01830090001C021450", "all transport travel pass (Transport)\nhttp://www.metro.spb.ru/cnblt41.html" }, { "3B3B94000064053E030F31800E9000", "Slovenian national health insurance card" }, { "3B3B940000640E3E02F031800E9000", "HPC card 2\nZZZS Health care professional card- Slovenia (Healthcard) (HealthCare)\nhttps://partner.zzzs.si/wps/portal/portali/aizv/e-poslovanje/kartice_in_citalniki/prof_kartica/" }, { "3B3B940000640E3E030F31800E9000", "HIC card\nZZZS Health care card- Slovenia (HealthCare)\nhttps://partner.zzzs.si/wps/portal/portali/aizv/e-poslovanje/kartice_in_citalniki/kartica_zdravstvenega_zavarovanja/!ut/p/z1/04_Sj9CPykssy0xPLMnMz0vMAfIjo8ziTQxdPd2N_Q08LSyCDQ0cjZzMzXz8XQ3cXU30C7IdFQEohG-V/" }, @@ -351,6 +363,7 @@ const static atr_t AtrTable[] = { { "3B3C110042AF20A32007002283829000", "Orange Mobicarte (SIM card old generation)" }, { "3B3C110044AF11F7200504FB83819000", "itineris (Old French Mobile Operator SIM card) (Telecommunication)" }, { "3B3C9400423111A21202095183809000", "Omnitel IT 16K GSM SIM card" }, + { "3B3C9400443111F000002CAE83839000", "Movistar Spain (Telecommunication)" }, { "3B3C94004B3125A21013144783839000", "GSM SFR" }, { "3B3C94004C3125A7201B001583839000", "GSM-SIM (900MHz) card of the carrier vodafone for their cellular\nnetwork (phase 2+ with 3V)" }, { "3B3C9400633112F00000464083839000", "Old russian 'beeline' sim" }, @@ -400,8 +413,9 @@ const static atr_t AtrTable[] = { { "3B4F004932435F436172643D4E6F5F515452", "AT24C (Bank)" }, { "3B4F00536C653434322D34343DA2131091", "Debit card (Bank)" }, { "3B4F00536C65343433322D34323DA2131091", "VISA (Bank)" }, + { "3B501100", "JAVA (JavaCard)" }, { "3B57180293020101019000", "Easyflex FastOS 2.0 / Schlumberger" }, - { "3B5B96000031C064BAFC10000F9000", "SERGAS - Galician Healtcare Service (Spain) (HealthCare)\nhttps://www.sergas.gal" }, + { "3B5B96000031C064BAFC10000F9000", "SERGAS - Galician Healthcare Service (Spain) (HealthCare)\nhttps://www.sergas.gal" }, { "3B5B96000031C064C6FC1000019000", "Banking card (Oberthur C.S. 06 18710-04-10)" }, { "3B5B96000031C064C7FC1000019000", "EBanking card (Oberthur C.S. 03 1146821)" }, { "3B5E11FF4573744549442076657220312E30", "Estonian Identity Card (EstEID v1.0 2006 warm)" }, @@ -411,11 +425,17 @@ const static atr_t AtrTable[] = { { "3B5F9500807300010059434C5AC506117C9000", "Smart Card C5 68K CardLogix Corp (Other)\nhttps://www.cardlogix.com/products/most-card-c-series-microprocessor-smart-cards/" }, { "3B5F9500807300010059434C5AC50612C09000", "Republic of Liberia Civil Service Identification Card (eID)" }, { "3B5F9600805A2C1100101000FFFFFFFF829000", "Calypso (Transport)" }, + { "3B5F9600805A3F0608140101C546DEDC829000", "Navegante(r) Personalizado (Lisbon public transportation card) (Transport)\nhttps://www.navegante.pt/viajar/cartoes" }, + { "3B5F9600805A3F0608140101C546EBDC829000", "Multi-Transport Pass (Navegante) (Metro, Bus, Electric, Boat) Carris Metropolitano, PT (Transport)\nhttps://www.navegante.pt/" }, + { "3B5F9600805A3F0608201223C4325FDE829000", "Montreal metropolitan area and Quebec city area OPUS card (Transport)\nhttps://www.carteopus.info/" }, + { "3B600000", "Meano (Bank)" }, { "3B61000080", "blank A40CR card (JavaCard)" }, { "3B630000364180", "Schlumberger Payflex 4k User" }, { "3B64..FF8062..A2", "JCOP20" }, { "3B64000080620.51", "Setec SetCOS 5.1.0 EMV" }, { "3B6400FF806202A2", "VISA credit card (Nordea bank)" }, + { "3B6500002063CB3020", "CB / VISA La Banque Postale (IDEMIA) (Bank)" }, + { "3B6500002063CB3040", "Credit Mutuel Debit card (Bank)" }, { "3B6500002063CB4700", "Orga SmartyPlus DATA STORE issued by MORPHO CARDS PERU" }, { "3B6500002063CB6300", "Bank card from Societe Generale (Oberthur)" }, { "3B6500002063CB6400", "Bank card Caisse d'Epargne" }, @@ -460,21 +480,28 @@ const static atr_t AtrTable[] = { { "3B6500002063CBB020", "Credit Mutuel Arkea paycard (Bank)" }, { "3B6500002063CBB120", "Bank card 'Credit Agricole' (Bank)" }, { "3B6500002063CBB280", "Visa card distributed by 'Societe Generale' (French Bank) (Bank)\nVisa card distributed by 'Boursorama Banque' (French Bank) (Bank)\nhttps://www.visa.fr/" }, + { "3B6500002063CBB600", "Maestro Bank Card with chip (Bank)" }, { "3B6500002063CBB620", "MasterCard credit card, issued by Swedbank LT (Bank)" }, { "3B6500002063CBB680", "Visa bank card (Bank)" }, { "3B6500002063CBB700", "Lloyds Bank (UK) 'Avios Duo' Mastercard credit card (Bank)" }, - { "3B6500002063CBB720", "Lloyds Bank Platinum Mastercard Credit (Bank)" }, + { "3B6500002063CBB720", "Lloyds Bank Platinum Mastercard Credit (Bank)\nCredit Agricole Prepaid - Mastercard" }, { "3B6500002063CBB780", "VISA Credit Card (Bank)" }, { "3B6500002063CBB900", "Mastercard (Bank)" }, + { "3B6500002063CBBC", "jcop040 (JavaCard)" }, { "3B6500002063CBBC00", "Credit Agricole MasterCard Societaire (France) (Bank)\nhttps://www.ca-centrefrance.fr/moyens-de-paiement/carte-bancaire-societaire.html" }, { "3B6500002063CBBC10", "French 'Ticket Restaurant' Edenred payment card (Other)\nhttps://www.myedenred.fr/" }, { "3B6500002063CBBC80", "CB / Visa La Banque Postale (Gemalto SP)" }, { "3B6500002063CBBD00", "Credit Mutuel Arkea (Bank)" }, + { "3B6500002063CBBD80", "CARD ONEY BANQUE VISA CREDIT (Bank)\nhttps://www.oney.fr/" }, + { "3B6500002063CBBF00", "Credit Agricole SA (france) (Bank)" }, { "3B6500002063CBC000", "Nickel Credit Card (Bank)\nhttps://nickel.eu" }, { "3B6500002063CBC080", "VISA debit card (Bank)\nhttps://www.cic.fr/fr/banques/professionnels/gestion-courante/cartes-paiement-professionnelles.html" }, { "3B6500002063CBC100", "CIC (Bank)\nhttps://www.cic.fr/" }, + { "3B6500002063CBC300", "CIC Credit Card (Bank)" }, { "3B6500002063CBC310", "Pass Restaurant Sodexo Pass France (Bank)\nhttps://moncompte.sodexopass.fr/" }, { "3B6500002063CBC380", "Apetiz restaurant card (French luncheon vouchers) (Bank)\nhttps://www.apetiz.com/faq/?thematic=achats-apetiz-quoi-quand-combien" }, + { "3B6500002063CBC600", "AuMax / Credit Mutuel (Bank)\nhttps://www.aumaxpourmoi.fr/" }, + { "3B6500002063CBC680", "BNP Paribas Visa Classic Card (Bank)\nhttps://mabanque.bnpparibas/en/managing-your-accounts/cards-and-payment-methods/all-cards/visa-classic-card" }, { "3B6500002063CBFF00", "Credit Agricole business mastercard (Bank)" }, { "3B6500002640009000", "Alacard Petroltech (Transport)\nhttp://www.alacard.kz/" }, { "3B6500002905010201", "ActivCard (Schlumberger) CyberFlex 64K V1 SM 2.1" }, @@ -490,6 +517,7 @@ const static atr_t AtrTable[] = { { "3B660000009C11010103", "Axalto Cyberflex Access 32K V4 SM 1.3" }, { "3B660000009C11010301", "Axalto Cyberflex Access 64K V1 Bio SM 3.1" }, { "3B6600000512010101B1", "Lyon1 student card (Moneo compatible)" }, + { "3B660000112233445566", "Sodexo Restaurant Pass Card (Turkey) (Loyalty)\nhttps://www.sodexoavantaj.com/yemek-karti" }, { "3B660000314B01010080", "VISA credit card (Skandiabanken)" }, { "3B66000032D000300201", "Kazakhstan Kazkom Onlinebank (Homebank) PKI client key-card\nhttps://www.homebank.kz/" }, { "3B66000062C901010000", "safeview (Pay TV)\nhttps://safeviewmedia.com/" }, @@ -563,10 +591,10 @@ const static atr_t AtrTable[] = { { "3B6800000073C84010009000", "Icelandic Banking scheme Issued by the Ministry of Treasure in Iceland\nhttp://www.islandsrot.is/" }, { "3B6800000073C84011009000", "Woolworths Everyday Money prepaid Mastercard\nNordea Bank Norway Visa + national debet card [BankAxept]\nVISA Classic - Nordlandsbanken (Norway)\nCiti Double Cash MasterCard\nWescom Credit Union Visa Debit Card (Bank)\nhttps://www.wescom.org/CHECKING/CHECK-CARD.ASP" }, { "3B6800000073C84012009000", "Brazilian 'e-CPF' card" }, - { "3B6800000073C84013009000", "MASTERCARD issued by MLP (Marschollek, Lautenschlager and Partner)\nG&D 12696-GDM-10/11 DEBIT CARD issued by BANCO DE CREDITO DEL PERU\nVisa from Caisse populaire Desjardins (Canada) (Bank)\nhttps://www.desjardins.com/\nMasterCard issued by President's Choice Bank (Canada)\nhttp://pcfinancial.ca/mastercard\nMasterCard issued by CIBC (Canada)\nhttp://www.cibc.com/\nMasterCard issued by The Bank of Nova Scotia (Canada)\nhttp://www.scotiabank.com/\nMasterCard issued by JPMorgan Chase Bank, N.A.\nhttps://www.chase.com/\nCaixaBank Visa Electron (Bank)\nhttps://www.lacaixa.cat/\nAmerican Express Canada Credit Card (Bank)" }, + { "3B6800000073C84013009000", "MASTERCARD issued by MLP (Marschollek, Lautenschlager and Partner)\nG&D 12696-GDM-10/11 DEBIT CARD issued by BANCO DE CREDITO DEL PERU\nVisa from Caisse populaire Desjardins (Canada) (Bank)\nhttps://www.desjardins.com/\nMasterCard issued by President's Choice Bank (Canada)\nhttp://pcfinancial.ca/mastercard\nMasterCard issued by CIBC (Canada)\nhttp://www.cibc.com/\nMasterCard issued by The Bank of Nova Scotia (Canada)\nhttp://www.scotiabank.com/\nMasterCard issued by JPMorgan Chase Bank, N.A.\nhttps://www.chase.com/\nCaixaBank Visa Electron (Bank)\nhttps://www.lacaixa.cat/\nAmerican Express Canada Credit Card (Bank)\nAlfa-bank Russia Visa" }, { "3B6800000073C8401300907D", "NextCard - Mastercard Debit card - Intesa Sanpaolo Bank (Italy) (Bank)\nhttps://www.intesasanpaolo.com/it/persone-e-famiglie/prodotti/carte/carte-di-debito/next-card.html" }, { "3B68000000DE511001019000", "Itau Bank Mastercard Debit Card (Brazil) (Bank)\nhttps://www.itau.com.br" }, - { "3B6800000101309600009000", "Edenred - French Restauration e-Ticket card (2013) (Other)\nhttps://www.edenred.fr/ticket-restaurant" }, + { "3B6800000101309600009000", "Edenred - French Restoration e-Ticket card (2013) (Other)\nhttps://www.edenred.fr/ticket-restaurant" }, { "3B6800000101310000009000", "VISA ELECTRON Sberbank card (Bank)\nSubmarino MasterCard credit card (Brazil) (Bank)\nhttps://www.submarino.com.br/landingpage/ofertas-cartao-submarino" }, { "3B6800000102109600009000", "Italian BancoPostaClick Postamat Card (Poste Italiane) -\nMasterCard debit card\nmbna PLATINUM MasterCard\nFlash (Banca Fideuram) Italy prepaid Mastercard" }, { "3B6800000105200000009000", "Air Bank a.s. MasterCard debit card with PayPass (Bank)" }, @@ -589,7 +617,8 @@ const static atr_t AtrTable[] = { { "3B6800008066B00701010707", "Java Gemalto R7 (Bank)\nGemalto Santander Optelio TUI R7 with WG10 using Contact interface" }, { "3B6800008066B00701017707", "Tecnico Lisboa Student Card (Bank)\nhttp://www.tecnico.ulisboa.pt/\nOther Optelio Card (Santander)\nSantander Totta Student Card - Portugal (Bank)\nhttps://www.santandertotta.pt" }, { "3B68000081000350010F9000", "Holvi Payment Master Card (Bank)\nhttp://www.holvi.com" }, - { "3B680000990200D103042201", "N26 business debit card (Bank)\nhttps://n26.com/en-de/business-account" }, + { "3B680000990100C103042201", "Robinhood Debit (Bank)\nhttps://robinhood.com/us/en/" }, + { "3B680000990200D103042201", "N26 business debit card (Bank)\nhttps://n26.com/en-de/business-account\nVisa cards from Inecobank, Armenia" }, { "3B6800009D03020101564953", "Visa Card - SberBank / Russia" }, { "3B6800009D08010201564953", "Visa Card - bonus - DenizBank / Turkey" }, { "3B6800009D080103014F5453", "MasterCard Card - bonus - Garanti Bank / Turkey" }, @@ -605,7 +634,7 @@ const static atr_t AtrTable[] = { { "3B6900002494010000000001A9", "Kazakhstan Helios gas station debit card\nhttp://helios.kz/" }, { "3B6900002494010201000101A9", "Chipcard from SUN to be used in SunRay's\n370-4328-01 (31091)" }, { "3B6900002494010301000100A9", "Schlumberger MicroPayflex S card" }, - { "3B6900004944353056312E....", "eID Card to user authenticate and save paswords in the Card. Product ID50 Password manager from IDENTOS GmbH (eID)\nhttps://identos.com/id50-password-manager/" }, + { "3B6900004944353056312E....", "eID Card to user authenticate and save passwords in the Card. Product ID50 Password manager from IDENTOS GmbH (eID)\nhttps://identos.com/id50-password-manager/" }, { "3B6900004944363056312E....", "token appidkey ID60-USB (Other)\nhttps://identsmart.com/en/products/id60-datasafe/" }, { "3B6900004A434F503331563232", "Visa Europe Sample Card / Axalto" }, { "3B6900005001010401000101A9", "Sample card given to all attendees of the CTST 2004 SmartCard Conference" }, @@ -624,7 +653,7 @@ const static atr_t AtrTable[] = { { "3B690000F704010601800102A9", "Azeriqaz Smart Payment (eID)\nhttp://azeriqaz104.az/" }, { "3B690000F704010801800102A9", "Multinet (Other)" }, { "3B69000241434F534A76313031", "ACOSJ 40K Dual Interface (JavaCard)" }, - { "3B69000241434F534A76323033", "ACS ACOSJ Java Card (JavaCard)\nhttps://www.acs.com.hk/en/products/405/acosj-java-card-combi/" }, + { "3B69000241434F534A7632303.", "ACS ACOSJ Java Card (JavaCard)\nhttps://www.acs.com.hk/en/products/405/acosj-java-card-combi/" }, { "3B6900FF00644A100432059000", "NXP JCOP 20 V2.1 16K" }, { "3B6900FF3131313054434F5350", "Seven-Eleven Value Card / Smart Purse - Thailand (Other)\nhttp://www.7eleven.co.th/about7card.php" }, { "3B6900FF323332435343533336", "CSCS smart card. Must be read using CSCS go smart software.\nhttps://download.cscsreader.co.uk/" }, @@ -658,8 +687,10 @@ const static atr_t AtrTable[] = { { "3B6B00000031806443B002008C6127", "Bull Odyssey 1.2 (Javacard 2.0)" }, { "3B6B00000031C06400273400079000", "American Express Gold Air France - KLM (Bank)" }, { "3B6B00000031C064002734000F9000", "American Express Chip and Signature Card (Contact) ()\nhttps://americanexpress.com/chipandsignature" }, + { "3B6B00000031C06408046105079000", "TBC Bank Mastercard (Georgia) (Bank)\nhttps://www.tbcbank.ge/web/en/cards-and-subscription-plans" }, { "3B6B00000031C06408046112079000", "Visa Electron. BSB Bank. Belarus (Bank)" }, { "3B6B00000031C064080461120F9000", "Portuguese National Identity Card (eID)\nhttps://www.autenticacao.gov.pt/o-cartao-de-cidadao" }, + { "3B6B00000031C06408046114079000", "Belarusbank VISA credit card (Belarus) (Bank)" }, { "3B6B00000031C06408046120079000", "AMEX Bank DI Card (Bank)" }, { "3B6B00000031C06408046176079000", "Alliance Bank Debit Card (Bank)" }, { "3B6B00000031C064080461770F9000", "'Strelka' Russian Federation Travelcard (Transport)\nhttp://strelkacard.ru/" }, @@ -674,20 +705,24 @@ const static atr_t AtrTable[] = { { "3B6B00000031C064083981040F9000", "Visa Debit (Bank)" }, { "3B6B00000031C064083991080F9000", "MasterCard Bank Card (Bank)\nhttps://www.creditonebank.com/" }, { "3B6B00000031C064084403040F9000", "VISA debit card (ActivoBank, Portugal) (Bank)\nhttps://www.activobank.pt" }, + { "3B6B00000031C06408440305019000", "mastercard (Bank)" }, { "3B6B00000031C064084403050F9000", "Visa (Bank)" }, { "3B6B00000031C06408440312079000", "VISA (Bank)" }, { "3B6B00000031C064084403120F9000", "Debit Card/MasterCard shipped by electronic Bank N26 (Number26) (Bank)\nhttps://n26.com/de-de/\nBBVA (Spain) (MasterCard) debit card (Bank)\nhttps://www.bbva.es/eng/productos/ficha/tarjeta-ahora-bbva/0000009719" }, { "3B6B00000031C06408440313079000", "MasterCard, Alfa Bank (Russia) (Bank)\nhttps://alfabank.ru/everyday/debit-cards/perekrestok-prepaid/" }, { "3B6B00000031C064084403680F9000", "Credit card emitted by KBC Brussels, Belgium (Bank)\nAdvanzia Credit Card (Master Card gold), Germany (IDK if there is a difference between the german one and the other ones) (Bank)\nhttps://mastercard.gebuhrenfrei.com/\nItalian Intesa Sanpaolo Superflash Prepaid Mastercard (Bank)" }, { "3B6B00000031C06408440376079000", "Blu American Express Credit Card (Bank)\nhttps://www.americanexpress.com/it/content/carta-blu/" }, - { "3B6B00000031C064084403760F9000", "Maestro debit card, issued by Caixa Geral de Depositos (Bank)\nhttps://www.cgd.pt/Particulares/Cartoes/Cartoes-de-Debito/Pages/Cartao-Debito-Cauxautomatica-Maestro.aspx" }, + { "3B6B00000031C064084403760F9000", "Maestro debit card, issued by Caixa Geral de Depositos (Bank)\nhttps://www.cgd.pt/Particulares/Cartoes/Cartoes-de-Debito/Pages/Cartao-Debito-Cauxautomatica-Maestro.aspx\nCredit Agricole Bank Card - Bancomat / Maestro\nDanske Bank Mastercard Debit" }, + { "3B6B00000031C06408440377079000", "Sber bank VISA with Novacard chip (Bank)" }, + { "3B6B00000031C064084403930F9000", "MIR card (Bank)" }, { "3B6B00000031C064084841080F9000", "Monizze card. Card use to pay only meal. (Other)\nhttp://www.monizze.be" }, { "3B6B00000031C064131402000F9000", "UBA Card PREPAID (Bank)" }, { "3B6B00000031C0641F1801000F9000", "Portuguese Medical Association eID card (Cedula Profissional, Ordem dos Medicos, Portugal) (eID)\nhttp://www.omsul.pt/tabid/242/Default.aspx" }, { "3B6B00000031C0641F270100079000", "SOCCARDRT (HealthCare)" }, - { "3B6B00000031C0641F2701150F9000", "VISA Debit card for HSBC, Australia" }, + { "3B6B00000031C0641F2701150F9000", "VISA Debit card for HSBC, Australia\nDanske Bank Mastercard Debit" }, { "3B6B00000031C0641F27011C079000", "Raiffeizen Bank, Russia, MasterCard paypass card (Bank)" }, { "3B6B00000031C0641F2701350F9000", "V PAY (Bank)\nhttps://www.unicreditbulbank.bg/en/individual-clients/everyday-banking/payments/debit-cards/" }, + { "3B6B00000031C0643F680100079000", "JCOP empty card (JavaCard)" }, { "3B6B00000031C0643F6801020F9000", "dinacard (Bank)" }, { "3B6B00000031C0643F680103079000", "American Express Blue Cash (Bank)\nhttps://www.americanexpress.com/us/credit-cards/" }, { "3B6B00000031C0643F6801030F9000", "HSBC Credit Gold Card from VISA (Bank)" }, @@ -700,15 +735,25 @@ const static atr_t AtrTable[] = { { "3B6B00000031C064BE1B01030F9000", "dsafas (Telecommunication)" }, { "3B6B00000031C064D0100100079000", "Alfa-Bank (Russia) Master Card (Aeroflot bonus) (Bank)" }, { "3B6B00000031C16408603206079000", "HSBC (Bank)" }, + { "3B6B00000031C164086032060F90", "VISA (National Bank) (eID)" }, { "3B6B00000031C164086032060F9000", "Nationwide Building Society VISA Debit card (Bank)" }, + { "3B6B00000031C164086032090F9000", "Swedbank Estonia ISIC debit card (Bank)\nhttps://www.swedbank.ee/private/cards/debit/isic" }, { "3B6B00000031C164086032100F9000", "ICA Bank, Mastercard (Bank)\nhttps://www.icabanken.se/kort-och-betala/bankkort/" }, { "3B6B00000031C1640860321F079000", "NMRC Card (Transport)" }, { "3B6B00000031C1640860321F0F9000", "Fio bank, Mastercard, credit (Bank)\nhttps://www.fio.cz/bankovni-sluzby/platebni-karty/kreditni-karty" }, { "3B6B00000031C164086032200F9000", "Twisto (Bank)" }, { "3B6B00000031C16408603221079000", "DSK Bank Debit Mastercard (Bank)" }, + { "3B6B00000031C164086032220F9000", "Universidade de Aveiro (ID Card) (eID)\nhttps://www.ua.pt/pt/sas/cartao" }, + { "3B6B00000031C164086032420F9000", "Westpac Handybank EFTPOS/ATM Card (Bank)" }, { "3B6B00000031C164087771300F9000", "Apple Card (from launch) (Bank)\nhttps://www.apple.com/apple-card/" }, { "3B6B00000031C16408777156079000", "American Express UK Euro ICC charge card (Bank)\nhttps://www.americanexpress.com/icc/cards/the-basic-international-currency-card.html" }, + { "3B6B00000031C16408986200079000", "EquaBank Master Card (Bank)\nhttps://equabank.cz" }, + { "3B6B00000031C164089862000F9000", "NovaKBM Visa Debit (Bank)" }, { "3B6B00000031C164089862010F9000", "Sodexo Czech Gastro/Multipass (Other)\nhttps://www.sodexo.cz/" }, + { "3B6B00000031C1640924331E0F9000", "Cembra Money Bank - Certo! Mastercard credit card (Bank)\nhttps://certo-card.ch/certo/de/" }, + { "3B6B00000031C164092962250F9000", "SANTANDER BASIC CASH CARD (Bank)\nhttps://www.santander.co.uk/assets/s3fs-public/2018-09/Basic%20Current%20Account%20KFD.pdf" }, + { "3B6B00000031C16409644136079000", "HSBC UK Visa Debit Card (Bank)\nhttps://www.hsbc.co.uk/content/dam/hsbc/gb/pdf/help/hsbc-visa-debit-card-guide.pdf" }, + { "3B6B00000031C164096441360F9000", "VISA Business Folio AS /Norway (Sparebanken Vest) [IDEMIA] (Bank)\nhttps://folio.no/" }, { "3B6B000020900000000000B0A2BD69", "Fudan FM1280 (JavaCard)" }, { "3B6B0000426C756361726420344B42", "Blutronics Blucard 4K (Loyalty)\nhttp://blucard.blutronics.com" }, { "3B6B00004348495031342056312E30", "Swiss PostFinance Card (Bank)\nhttps://www.postfinance.ch/en/priv/prod/card/pfcard.html" }, @@ -739,7 +784,10 @@ const static atr_t AtrTable[] = { { "3B6C00004E544943339051024A030000", "Topas TV (Pay TV)" }, { "3B6C000080641134014873F741C08107", "Universal Electronic Card (UEC Russia) (eID)" }, { "3B6C0000806411650190730000008107", "Universal Electronic Card (UEC Russia) (eID)" }, + { "3B6C00008066B1A30401110B83009000", "NAVY F.C.U. (JavaCard)" }, + { "3B6C00008066B1A330401110B8300900", "Visa debit (Bank)" }, { "3B6C0002366186384B8C13046203598A", "Nagravision, Swiss mode" }, + { "3B6C00FF50564A434F50323156323331", "Al Etihad (Bank)" }, { "3B6C00FF8073C8211366010611590001", "Visa Crypto Business Electron by neyvabank.ru (Bank)\nhttps://neyvabank.ru/uploads/files/download/bankdirectvisacryptocardreader.pdf" }, { "3B6D0000", "PVT (Bank)" }, { "3B6D0000003180642DA0040C71968C6129", "UK Co-operative Bank Visa Debit (produced 2002)" }, @@ -756,6 +804,7 @@ const static atr_t AtrTable[] = { { "3B6D00000031C071D664A0010400849000", "Swiss Mastercard M-Budget (Bank)" }, { "3B6D00000031C071D66511223301839000", "UK Co-operative Bank Visa Debit (produced 2006)" }, { "3B6D000000664D453153050653036104F0", "FutureCard Ind. LLC / Feb-18 | Blank card for programming (JavaCard)" }, + { "3B6D00000073C800136447374237009000", "VISA DEBIT CLASSIC BANK OF AMERICA (Bank)" }, { "3B6D00000073C800136447374532009000", "Canada TD Bank Visa Debit/Interac Debit Card (Bank)" }, { "3B6D00000073C80013644A374237009000", "Sberbank Visa card (Bank)" }, { "3B6D00000073C800136454334433009000", "FirstBankcard issued for overstock.com (Bank)\nhttp://www.firstbankcard.com/overstock/" }, @@ -795,9 +844,10 @@ const static atr_t AtrTable[] = { { "3B6D000080318065B0872701BC83089000", "Kazcommertsbank\nhttp://en.kkb.kz/page/WhoWeAre\none of the biggest banks in Republic of Kazakhstan\nNordea (Finland) + Finnair MasterCard (credit)\nGXP7 T=0" }, { "3B6D000080318065B0873401D783009000", "Fidelity Investment Rewards (AMEX) (Bank)\nhttps://www.fidelity.com/cash-management/american-express-cards" }, { "3B6D000080318065B0893501F183009000", "Bank of America BankAmericard Travel Visa Chip Card (Gemalto)\nhttps://www.bankofamerica.com/credit-cards/products/bankamericard-travel-rewards-credit-card.go\nCredito Trevigiano - Banca di Credito Cooperativo - Carta BCC\nVISA Signature issued by RBC Bank (Georgia), N.A.\nhttps://www.rbcbank.com/\nBanca Popolare di Novara - (Bancomat Maestro)" }, - { "3B6D000080318065B0894001F283009000", "PSAM Card" }, + { "3B6D000080318065B0894001F283009000", "PSAM Card\nNordea Mastercard Card" }, { "3B6D00008067A1110101640855830E9000", "Italian Intesa SanPaolo Maestro" }, { "3B6D00FF003180718E6448D50200829000", "Blue for Business, American Express@Business" }, + { "3B6D00FF0031C173C8400052A1D8009000", "Discover it Credit Card (Bank)\nhttps://www.discover.com/credit-cards/cash-back/it-card.html" }, { "3B6D00FF80655343010D067394211B810[15]", "Giesecke & Devrient CardToken 350 (ICCD)" }, { "3B6D00FF8073002113574A544861314700", "ActiveKey SIM" }, { "3B6D00FF8073002113574A544861314800", "Spanish Medical College Card" }, @@ -824,7 +874,7 @@ const static atr_t AtrTable[] = { { "3B6E00000031C0657CB501018071D68C6121", "MUFG CARD (AMEX .jp) (Bank)\nhttp://www.cr.mufg.jp/mufgcard/support/webs/" }, { "3B6E00000031C065BCD002010671D68C612E", "Lloyds TSB Mastercard Credit Card" }, { "3B6E00000031C065BCD002010671D68C6133", "Lloyds TSB Visa Debit\nBarclays Visa DEBIT" }, - { "3B6E00000031C065BCD002010671D68C6143", "S-pankki (Finland) Visa Debit" }, + { "3B6E00000031C065BCD002010671D68C6143", "S-Pankki (Finland) Visa Debit" }, { "3B6E00000031C065BCD102010171D68C6133", "Travelex Cash Passport - Prepaid MasterCard Currency Card" }, { "3B6E00000031C065BCD102010171D68C6135", "MasterCard Card, issues by Rabobank in the Netherlands" }, { "3B6E00000031C065D3C102012871D68C6122", "Visa debit card" }, @@ -910,10 +960,11 @@ const static atr_t AtrTable[] = { { "3B6E0000626C756361726420344B422F7634", "eID Blutronics Blucard 4K\nhttp://blucard.blutronics.com" }, { "3B6E0000626C756361726420384B422F7634", "eID Blutronics Blucard 8K\nhttp://blucard.blutronics.com" }, { "3B6E00008025A00000002856801024000111", "Opencard, new card in Prague, Czech Republic (transport card)" }, + { "3B6E0000803108721422574458544B4E3031", "DX-Token (eID)\nhttps://www.dexon.ind.br/produtos-dxtoken" }, { "3B6E000080318065B00301015E8300009000", "FirstUSA Visa" }, { "3B6E000080318065B00302015E8300009000", "Gemplus GemXpresso 211is" }, { "3B6E000080318066B0070300AC0183009000", "e-payment card with topup system, propreteary by local bank\nhttp://www.klikbca.com/individual/silver/product.html?s=69" }, - { "3B6E000080318066B0840C016E0183009000", "Optelio Cards (D72 R4 WR)\nNordea (a Skandinavian bank) eID card\nhttp://linux.fi/wiki/Nordea_eID\nRBC Royal Bank Client Card (bank in Canada)\nBanco Santander TUI/USC R7\nGemalto Optelio/Desineo D72 (JavaCard) with WG10 and Maestro (JavaCard) (Bank)\nCarte Ticket Restaurant with MasterCard\nCitigold VISA Debit for Citibank, Australia\nPlatinum VISA card for Citibank, Australia\nVISA Infinite issued by RBC Royal Bank (Canada)\nhttp://www.rbc.com/\nPostepay Evolution - Poste Italiane (mastercard)\n'la Caixa' (Spain) (VISA Electron) debit card (Bank)\nhttps://www.lacaixa.es/\nItalian Webank.it BPM Banca Popolare di Milano Bancomat & Maestro Card (Bank)" }, + { "3B6E000080318066B0840C016E0183009000", "Optelio Cards (D72 R4 WR)\nNordea (a Skandinavian bank) eID card\nhttp://linux.fi/wiki/Nordea_eID\nNordea Mastercard card\nNordea Visa card\nRBC Royal Bank Client Card (bank in Canada)\nBanco Santander TUI/USC R7\nGemalto Optelio/Desineo D72 (JavaCard) with WG10 and Maestro (JavaCard) (Bank)\nCarte Ticket Restaurant with MasterCard\nCitigold VISA Debit for Citibank, Australia\nPlatinum VISA card for Citibank, Australia\nVISA Infinite issued by RBC Royal Bank (Canada)\nhttp://www.rbc.com/\nPostepay Evolution - Poste Italiane (mastercard)\n'la Caixa' (Spain) (VISA Electron) debit card (Bank)\nhttps://www.lacaixa.es/\nItalian Webank.it BPM Banca Popolare di Milano Bancomat & Maestro Card (Bank)\nSberbank of Russia MIR debit card (Bank)\nMasterCard bank card by OTP Bank (Hungary)" }, { "3B6E000080318066B08412016E0183009000", "Barclaycard Platinum VISA\nInteligo debit card\nVISA issued by ING (Poland)\nVISA Debit card for ING Direct, Australia\nVISA Gold issued by RBC Royal Bank (Canada)\nhttp://www.rbc.com/\nGas Natural Fenosa Visa (issued by CaixaBank) (Bank)\nhttp://www.clubfenosa.gasnaturalfenosa.es/ca/1285341160257/targeta+gas+natural+fenosa.html" }, { "3B6E000080318066B08416016E0183009000", "UK 'Barclaycard Gold VISA' with RFID" }, { "3B6E000080318066B0870C016E0183009000", "Banco Santander TUI/USC R7 - Gemalto Optelio/Desineo D72 (JavaCard)\nhttp://www.observatoriotui.com/home" }, @@ -927,17 +978,24 @@ const static atr_t AtrTable[] = { { "3B6E00FF00620000574156414E5410819000", "debit card (Visa Electron) issued by Nordea bank" }, { "3B6E00FF4573744549442076657220312E30", "Estonian Identity Card (EstEID v1.0 warm)" }, { "3B6E00FF47442D47502D333256342D444553", "Mastercard Ourocard Platinum from Banco do Brasil" }, + { "3B6F000000004A300A86454A5A231044433330", "Personal Card (eID)" }, { "3B6F00000031C068435350454D560300079000", "SOCCARDRT (HealthCare)" }, { "3B6F00000031C173C821106441443231009000", "Debit Card for Santander Universidades PT (Bank)\nhttps://www.santander.pt/pt_PT/Particulares/Universitarios.html" }, + { "3B6F00000031C173C821106441443530009000", "Discover Cashback Debit (Bank)\nhttps://www.discover.com/online-banking/" }, + { "3B6F00000031C173C8211064414D3347", "CHASE DEBIT VISA (JavaCard)" }, + { "3B6F00000031C173C8211064414D3347079000", "Java (JavaCard)" }, { "3B6F00000031C173C8211064474D3131009000", "Beutsche Bank Business mastercard credit (Bank)\nhttps://www.deutsche-bank.de/gk/zahlungsverkehr/zahlungsverkehr-im-ueberblick/kreditkarte.html" }, { "3B6F00000031C173C8211064474D3133009000", "Bank card from the Commonwealth Bank of Australia (Bank)\nhttps://www.commbank.com.au/business/pds/cbb127_eftpos_int_v7.pdf" }, { "3B6F00000031C173C8211064474D3134009000", "bancomat Banca Popolare Milano + maestro debit card + rfid (Bank)" }, + { "3B6F00000031C173C8211064474D3136009000", "Highest level of DNB AS (Norway (Bank)" }, { "3B6F00000031C173C8211064474D3331009000", "BBVA Antes Card (Spain) (VISA) prepaid card (Bank)\nhttps://www.bbva.es/eng/productos/ficha/tarjeta-antes-bbva/0T00000225" }, { "3B6F00000031C173C8211064474D3332009000", "td debit visa card (Bank)\nScotiabank ScotiaCard Interac/Visa debit card (Bank)\nhttps://www.scotiabank.com/ca/en/personal/ways-to-bank/debit-credit-prepaid-cards/debit-cards/scotiacards.html" }, { "3B6F00000031C173C8211064474D3338009000", "ING Direct Debit Card (Bank)" }, { "3B6F00000031C173C8211064474D3341009000", "Fineco debit card (Bancomat, Visa Debit) (Bank)\nhttps://finecobank.com/it/online/conto-e-carte/carte-e-bancomat/" }, - { "3B6F00000031C173C8211064474D3435009000", "VISA card, issued by OP Financial Group, Finland (Bank)" }, + { "3B6F00000031C173C8211064474D3435009000", "VISA card, issued by OP Financial Group, Finland (Bank)\nVISA card, issued by S-Pankki, Finland (Bank)" }, { "3B6F00000031C173C8211064474D3437009000", "Visa credit card, issued by Klarna (Bank) (Bank)\nhttps://www.klarna.com/se/kort/" }, + { "3B6F00000031C173C8211064474D3533009000", "American Express Platinum Card Mexico (Bank)\nhttps://www.americanexpress.com/mx/tarjetas-de-credito/the-platinum-credit-card/" }, + { "3B6F00000031C173C821106457493035009000", "Discover EMV Card (Bank)" }, { "3B6F00000031C173C821106457493036009000", "neat MasterCard (Bank)\nhttps://www.neatcommerce.com/" }, { "3B6F00000031C173C821106457493131009000", "Banca Popolare Di Sondrio (IT) - Ateneo+ Card for University of Brescia (Bank)\nhttps://www.ateneopiu.it/home" }, { "3B6F00000031C173C8211064574B3037009000", "NatWest VISA Debit card (Bank)\nhttps://personal.natwest.com/personal/current-accounts/select_account.html" }, @@ -945,7 +1003,7 @@ const static atr_t AtrTable[] = { { "3B6F00000031C173C821106457533430009000", "PNC Debit (Bank)" }, { "3B6F000000664D453161030153086104F09000", "Spanish University of Murcia smart ID card - Old version (M.Mar OS) - Also used by many others spanish universities" }, { "3B6F000000664D4531B1030153086104F09000", "Spanish UCAM University smart ID card - (M.Mar OS) - Also used by many others spanish universities" }, - { "3B6F000000664D45668003..53066103F09000", "Millenium Card (www.coruna.es/millennium) municipal city card for A Coruna, Spain.\nelectronic wallet for paying services like parking meter or public service (bus) transportation." }, + { "3B6F000000664D45668003..53066103F09000", "Millennium Card (www.coruna.es/millennium) municipal city card for A Coruna, Spain.\nelectronic wallet for paying services like parking meter or public service (bus) transportation." }, { "3B6F000000664D456680030C53066105F09000", "Transporte Metropolitano de Galicia (Transport)\nhttp://tmg.xunta.gal/" }, { "3B6F000000664D456680031453036104F09000", "Paypal Prepaid Card (YoUnique Money E.D.E.)" }, { "3B6F000000664D456680031653066105F09000", "Transport Metropolitano de Galicia (Transport)\nhttps://tmg.xunta.gal/" }, @@ -956,6 +1014,7 @@ const static atr_t AtrTable[] = { { "3B6F000000B854211004900000000000000000", "VIABUY Credit card (Bank)\nhttp://www.viabuy.de" }, { "3B6F000000B854311007900000000000000000", "Revolut Premium Mastercard (Bank)\nhttps://www.revolut.com/revolut-premium\nMonzo Bank (UK) Debit Mastercard\nhttps://monzo.com/\nLansforsakringar (SE) Debit Mastercard\nhttps://www.lansforsakringar.se/\nING Direct MasterCard (Bank)\nhttps://www.ing.es/tarjetas-ing" }, { "3B6F00000231B813FF000000000000000F9000", "Coverflex | Flexible Benefits Platform (Other)\nhttps://www.coverflex.com/" }, + { "3B6F00003101F1564011001900000000000000", "Postepay Evolution (Bank)\nhttps://postepay.poste.it/prodotti/postepay-evolution.html" }, { "3B6F0000626C75636172642031364B422F7634", "eID Blutronics Blucard 16K\nhttp://blucard.blutronics.com" }, { "3B6F00008031C0520083640219083283839000", "Bancomer Mexican Bank" }, { "3B6F00008031C05205B5640200647183839000", "Read Card in USB, used for application in Java. (JavaCard)" }, @@ -966,6 +1025,7 @@ const static atr_t AtrTable[] = { { "3B6F00008031C05211F46402B3023783839000", "VISA (Bank)" }, { "3B6F00008031C052132F640219083283839000", "Cajamadrid-UCM (Madrid, Spain) Cash/Visa Electron" }, { "3B6F00008031C05216B9640566803283839000", "Banorte Mexican Bank" }, + { "3B6F00008031C05216F5640569937083839000", "BANCO INTERAMERICANO DE FINANZAS, S.A.E.M.A. (Bank)" }, { "3B6F00008031C05220E2640562178083839000", "VISA MedicusMundi (VISA Classic Affinity credit card, issued by Laboral Kutxa) (Bank)\nhttps://www.medicusmundi.es/es/que-puedes-hacer-tu/tarjetas-affinity" }, { "3B6F00008031E05B4555520000000000070200", "Pre-loaded wallet card. 'Mondex is a smart card electronic cash system, implemented as a stored-value card' (Bank)\nhttps://en.wikipedia.org/wiki/Mondex" }, { "3B6F00008031E05B4E4F4B0000000000000200", "Norsk-Tipping (Buypass) Monodex card" }, @@ -994,6 +1054,7 @@ const static atr_t AtrTable[] = { { "3B6F00008031E06B04520502BB555555555555", "AlfaBROU - Mastercard (prepaid card emitted by Banco Republica - Uruguay) (Bank)\nhttps://www.brou.com.uy/personas/tarjetas/prepaga-alfabrou" }, { "3B6F00008031E06B04520502FD555555555555", "C6 Bank Mastercard Global Account (Bank)\nhttps://www.c6bank.com.br/conta-global\nC6 Bank Mastercard Brazil Account (Bank)\nhttps://www.c6bank.com.br/nossos-produtos" }, { "3B6F00008031E06B04520502FE555555555555", "CITIBanamex 'Perfiles' debit card (Bank)\nhttps://www.banamex.com/es/personas/cuentas/cuenta-perfiles.html" }, + { "3B6F00008031E06B04546B026D555555555555", "Mastercard Credit/Debit Card (Bank)" }, { "3B6F00008031E06B0505050280555555555555", "Banamex cuenta perfiles (Bank)\nhttp://banamex.com" }, { "3B6F00008031E06B0508050283555555555555", "Bank of Montreal debit card" }, { "3B6F00008031E06B0512050287555555555555", "American Express credit card" }, @@ -1047,8 +1108,11 @@ const static atr_t AtrTable[] = { { "3B6F0000805A080608201223C202795D829000", "OPUS is card for storing public transit passes and tickets (Transport)\nhttps://opusenligne.ca/" }, { "3B6F0000805A080608201223C2036347829000", "OPUS Card RTC Quebec's Bus system (Canada) (Transport)\nhttps://opusenligne.ca" }, { "3B6F0000805A080608201223C204D07F829000", "OPUS Card (Transport)\nhttps://en.wikipedia.org/wiki/OPUS_card" }, + { "3B6F0000805A080608201223C210E956829000", "Montreal Public transport Card (OPUS) (Transport)\nhttps://www.stm.info/en/info/fares/opus-cards-and-other-fare-media/opus-card" }, { "3B6F0000805A080608201223C21113A6829000", "OPUS (Montreal and Quebec transport card) (Transport) (Transport)\nhttps://www.stm.info/en/info/fares/opus-cards-and-other-fare-media/opus-card" }, { "3B6F0000805A080608201223C21290FD829000", "OPUS Montreal Societe des Transports de Montreal Metro and Bus card. (Transport)\nhttps://www.stm.info/en/info/fares/opus-cards-and-other-fare-media" }, + { "3B6F0000805A080608201223C215C411829000", "Montreal OPUS Transportation Card for the STM, STL, RTM and RTL (Transport) (Transport)" }, + { "3B6F0000805A080608201223C21BAFB4829000", "OPUS card (Transport)\nhttps://www.stm.info/en/info/fares/opus-cards-and-other-fare-media/opus-card" }, { "3B6F0000805A0A010120031103DCB31B829000", "Card Transport 'Lignes d'Azur' of 'Metropole Nice Cote d'Azur'" }, { "3B6F0000805A0A01012003119361D7D7829000", "Carte Tecely (Reseau TCL: metro, tramway, bus et funiculaire de Lyon) (Idemia)" }, { "3B6F0000805A0A0102200311........829000", "Card << Oura ! >> of region Rhone-Alpes, Calypso standard (TER, Transisere)" }, @@ -1072,10 +1136,16 @@ const static atr_t AtrTable[] = { { "3B6F0000805A0A070620042C031B899B829000", "Israeli public transport 'RavKav' card (ASK variant) (Transport)\nhttp://www.dannorth.co.il/" }, { "3B6F0000805A0A070620042C031C112A829000", "Peronalized RavKav (Transport)\nhttps://ravkavonline.co.il/he/" }, { "3B6F0000805A0A070620042C03288444829000", "rav-kav (Transport)\nhttp://nohal.mot.gov.il/%d7%94%d7%92%d7%93%d7%a8%d7%95%d7%aa%20%d7%91%d7%a0%d7%95%d7%a9%d7%90%20%d7%9b%d7%a8%d7%98%d7%95%d7%a1%20%d7%97%d7%9b%d7%9d.aspx" }, + { "3B6F0000805A0A070620042C0490EFCB829000", "Rav-Kav Israel (Transport)\nhttps://ravkavonline.co.il" }, { "3B6F0000805A0A070620042DC1660B73829000", "Rav-Kav: Israel's Travel Card (Transport)" }, { "3B6F0000805A0A070620042DC169517A829000", "Card for bus (Transport)" }, + { "3B6F0000805A0A070620042DC2E8E270829000", "ravkav (Transport)" }, + { "3B6F0000805A0A070620042DC3229072829000", "Israelian public transportation smartcard for all bus and rail services, called a Rav-Kav, RavKav, or Rav Kav. This variant is NFC capable. (Transport)\nhttps://ravkavonline.co.il/" }, { "3B6F0000805A28010220122103346577829000", "ONYGO! pass\nhttp://www.region-basse-normandie.fr/pass-onygo" }, + { "3B6F0000805A280102201221C12AD1C8829000", "SIMPLI CITES (Transport)\nhttps://www.fluo.eu/" }, + { "3B6F0000805A280102201221C365AF0F829000", "SIMPLI CITE Stan (France) (Transport)\nhttps://www.reseau-stan.com/" }, { "3B6F0000805A2811421010122B260CD45A829000", "French transport card Navigo (Transport)\nhttps://www.iledefrance-mobilites.fr/je-gere-ma-carte" }, + { "3B6F0000805A28114210122B03791BB9829000", "RATP Navigo Easy Paris France (Transport)\nhttps://www.transilien.com/fr/page-tarifs/navigo-easy" }, { "3B6F0000805A28114210122B037A89AA829000", "Ile-de-France Mobilites (Navigo Easy) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-easy" }, { "3B6F0000805A28114210122B037AED59829000", "Navigo Easy (Paris) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-easy" }, { "3B6F0000805A28114210122B23A9E229829000", "Navigo(French transport) (Transport)\nhttp://www.navigo.fr/je-gere-ma-carte/" }, @@ -1120,6 +1190,9 @@ const static atr_t AtrTable[] = { { "3B6F0000805A28130210122B03F80BC3829000", "Korrigo Card (Transport in Rennes Area in France ) (Transport)\nhttps://www.star.fr/titres-tarifs/carte-korrigo/" }, { "3B6F0000805A28130210122B7500559A829000", "Contact card to handle personal travel tickets in the Britany area. Used by several travel operators like SNCF TER Bretagne, STAR in Rennes, BIBUS in Brest,.... (Transport)" }, { "3B6F0000805A28130210122B75021B8A829000", "KorriGo, smart transport card in France region Brittany (Transport)\nhttps://www.ter.sncf.com/bretagne/offres/carte-korrigo" }, + { "3B6F0000805A28130210122B7503FB01829000", "origo (France) (Transport)\nhttps://www.breizhgo.bzh/se-deplacer-en-bretagne/KorriGo" }, + { "3B6F0000805A28130210122B750C7E79829000", "Transportation card delivered by STAR (Transportation service from the city of Rennes, France) (Transport)\nhttps://www.star.fr/titres-et-tarifs/carte-korrigo/" }, + { "3B6F0000805A28130210122B750DD382829000", "ZOU! for Region Sud (Transport)\nhttps://zou.maregionsud.fr/ma-carte-zou/" }, { "3B6F0000805A28130210122B9292E642829000", "Transport card in cote d'or France (mobigo)" }, { "3B6F0000805A28130210122B9292E829829000", "French transport card of the city of Dijon and Cote d'or department. (Transport)\nhttps://www.viamobigo.fr/fr/acheter-mes-titres-de-transport-mobigo-en-cote-dor/176" }, { "3B6F0000805A28130210122B92D663FC829000", "Card 'Korrigo' region Bretagne, France, Bibus Brest Metropole public transport (Transport)\nhttps://fr.wikipedia.org/wiki/KorriGo" }, @@ -1132,6 +1205,7 @@ const static atr_t AtrTable[] = { { "3B6F0000805A2C06081010059316213D829000", "Portuguese Viva Card (Transport)\nhttp://www.portalviva.pt" }, { "3B6F0000805A2C0608101005932268D0829000", "Public transport card lisbon (Transport)\nhttp://www.portalviva.pt" }, { "3B6F0000805A2C0608101005932346E2829000", "Lisboa Viva (Transport)" }, + { "3B6F0000805A2C060810100593237A11829000", "Portuguese Lisboa Viva Viagem Transport Card (Transport)" }, { "3B6F0000805A2C11C31010057B01004B829000", "French Military Circulation card (Transport)" }, { "3B6F0000805A2C11C31010057B0A1DBA829000", "French military discount on SNCF trains card (Transport)" }, { "3B6F0000805A2C11C31010057B120D53829000", "French military transport card (Transport)" }, @@ -1156,11 +1230,17 @@ const static atr_t AtrTable[] = { { "3B6F0000805A2D060810100278324D6F829000", "Lisboa Viva card, public transport card of Lisbon (Portugal) (Transport)\nhttps://www.portalviva.pt" }, { "3B6F0000805A2D06081010027835EDCE829000", "Lisbon Transportation SmartCard (Transport)" }, { "3B6F0000805A2D06081010027848BBCC829000", "Lisbon Metro Monthly Student Pass (Transport)\nhttps://www.metrolisboa.pt/" }, + { "3B6F0000805A2D0608101005935C42FB829000", "Comboios de Portugal Transit Card (Transport)\nhttps://www.cp.pt/passageiros/pt/consultar-horarios/precos/cartao-cp" }, + { "3B6F0000805A2E130200010104EF8342829000", "Oura Auvergne-Rhone-Alpes (Transport)\nhttps://www.oura.com" }, { "3B6F0000805A3407061500017917A7E2829000", "Rav-Kav multi-line travel ticket used in the public transportation system in Israel (Transport)\nhttps://www.gov.il/en/departments/guides/multi_line_card" }, + { "3B6F0000805A340706150001792A4B5C829000", "Rav Kav Transit Payment Card (Israel) (Transport)\nhttps://ravkavonline.co.il/" }, { "3B6F0000805A3B0706150101793E797B829000", "Rav Kav (Transport)\nhttp://alhakav.mot.gov.il/he/rav-kav" }, { "3B6F0000805A3B070615010279537211829000", "Israeli public transport card ('RavKav') (Transport)\nhttps://ravkavonline.co.il" }, { "3B6F0000805A3B07061501027956C5F4829000", "RAVKAV - Israel dual interface transport card (Calypso standard) (Transport)\nhttps://en.wikipedia.org/wiki/Rav-Kav" }, + { "3B6F0000805A3B070615010279C3C331829000", "Rav-Kav Israel (Transport)\nhttps://ravkavonline.co.il" }, { "3B6F0000805A3C0608140101C3805E38829000", "Lisboa VIVA - Lisbon public transport card (Transport)\nhttps://www.portalviva.pt/" }, + { "3B6F0000805A3C0608140101C4D4FEC4829000", "Metropolitan Transports of Lisbon NAVEGANTE Card (Transport)\nhttps://www.navegante.pt/viajar/cartoes" }, + { "3B6F0000805A3C0608140101C4D522FB829000", "Navegant Perdonal Card - Transportes Metropolitanos de Lisboa (Transport)\nhttps://www.navegante.pt/" }, { "3B6F0000805A3C1142141001274AC890829000", "Gemalto Celego G1 (Transport)" }, { "3B6F0000805A3C114214100127A46D02829000", "Navigo decouverte Paris (Transport)\nhttp://www.navigo.fr/titres/le-forfait-navigo-semaine-presentation/" }, { "3B6F0000805A3C114214100127B3C81B829000", "Transport card for Paris (France) and its region. Market name is Navigo (Transport)" }, @@ -1172,8 +1252,10 @@ const static atr_t AtrTable[] = { { "3B6F0000805A3C1142141001C17BC993829000", "Navigo Easy (Transport)\nhttps://www.ratp.fr/titres-et-tarifs/passe-navigo-easy" }, { "3B6F0000805A3C1142141001C17DA8CA829000", "Navigo Easy (France) transport card (Transport)\nhttps://www.ratp.fr/titres-et-tarifs/passe-navigo-easy" }, { "3B6F0000805A3C1142141001C185D47B829000", "Navigo Liberte+ -- French (Paris region) transport card (Transport)" }, + { "3B6F0000805A3C1142141001C2A40D5F829000", "'Pass Navigo Decouverte' - Parisian transport card (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-decouverte" }, { "3B6F0000805A3C1142141001C2BF7B46829000", "Navigo RATP (Transport)" }, { "3B6F0000805A3C1330141001C3568397829000", "'oura' card for leman express and ter sncf rhone-alpes (Transport)" }, + { "3B6F0000805A3C1330141001C4F81130829000", "Oura card which allows to travel across many transport networks of France region: Auvergne-Rhone-Alpes (Transport)\nhttps://www.oura.com/" }, { "3B6F0000805A3C23C4141001C02EF7F8829000", "MOBIB basic Brussels (with NFC) (Transport)\nhttps://mobib.be/en.html" }, { "3B6F0000805A3C23C4141001C02EFA97829000", "Brussels STIB MOBIB Classic (Transport)\nhttps://www.stib-mivb.be/mobib.html" }, { "3B6F0000805A3C23C4141001C02FDEFA829000", "MOBIB public transport card (Belgium); contacted IC (Transport)" }, @@ -1187,7 +1269,9 @@ const static atr_t AtrTable[] = { { "3B6F0000805A3D0706150101792CB636829000", "RavKav (Transport)" }, { "3B6F0000805A3D23C41501027937D7AE829000", "MOBIB - Brussels (Transport)\nhttps://www.stib-mivb.be/article.html?_guid=d02c7fb6-3e9c-3810-248e-eec4ee5ebc8c&l=fr" }, { "3B6F0000805A3D23C41501027949789C829000", "MOBIB basic (Transport)\nhttp://www.stib-mivb.be/article.html?_guid=30af0085-2483-3410-5394-a71daf08acd1&l=en#contentBodyList1" }, + { "3B6F0000805A3D23C4150102795A863C829000", "mobib transportation card (Transport)" }, { "3B6F0000805A3D23C415010279748A25829000", "Mobib (Brussels transport card) for the STIB-MIVB network (Transport)\nhttps://www.stib-mivb.be/article.html?_guid=d02c7fb6-3e9c-3810-248e-eec4ee5ebc8c&l=fr" }, + { "3B6F0000805A3D23C415010279A9E567829000", "MoBIB card, a medium for the transport tickets of the four Belgian public transport operators (Transport)\nhttps://mobib.be/" }, { "3B6F0000805A434F4C44000000000000829000", "LBB Berlin, MasterCard (ADAC branding?)" }, { "3B6F0000805A4880C1205001AEC00295829000", "Calypso SAM C1 (Transport)" }, { "3B6F0000806645460138180353023110829000", "Fabrica Nacional de Moneda y Timbre FNMT WG10\nhttp://www.fnmt.es/es/html/tage/fichaTarjeta/fp1_ta_01.asp" }, @@ -1198,7 +1282,7 @@ const static atr_t AtrTable[] = { { "3B6F00008066A20302023D0753023110829000", "Electronic purse of the Universidad Politecnica of Madrid (provided by Banco Santander)" }, { "3B6F00008066B007010107............9000", "Gemalto Santander Optelio TUI R7 with WG10 customized using Contact interface" }, { "3B6F00008066B0070101070753023110829000", "Banco Santander TUI/USC R7 - Gemalto Optelio/Desineo D72 (JavaCard) with WG10 (JavaCard)" }, - { "3B6F00008066B0070101070753023124829000", "Banco Santander TUI/USC R7 - Gemalto Optelio/Desineo D72 (JavaCard) with WG10 and Maestro (JavaCard)" }, + { "3B6F00008066B0070101070753023124829000", "Banco Santander TUI/USC R7 - Gemalto Optelio/Desineo D72 (JavaCard) with WG10 and Maestro (JavaCard)\nClient bizness card (Bank)" }, { "3B6F00008066B007010177............9000", "Other Optelio Card (Santander)" }, { "3B6F00008066B0070101770753023110829000", "University ID card (issued by Banco Santander Central Hispano)\n.\nUniversidad Nacional de Educacion a Distancia (UNED, Spain)\nhttp://www.uned.es/tarjeta\n.\nUniversitat Politecnica de Catalunya (UPC.edu)\nhttps://www.upc.edu/identitatdigital\n.\nUniversitat Ramon Llull (URL)\nhttp://www.url.edu/cont/url/carnet.php" }, { "3B6F00008066B0070101770753023124829000", "Santander 4B Maestro\nUniversity of Santiago de Compostela. Spain\nPolytechnical University of Madrid, Spain" }, @@ -1227,7 +1311,7 @@ const static atr_t AtrTable[] = { { "3B76130000806207418181", "TransLink card (discontinued San Francisco Bay Area transit card)" }, { "3B76980000009C11010102", "CyberFlex Access 32" }, { "3B771800004B4153414B4944", "Identification Kazakhstan Republic (passport)\nhttps://egov.kz/" }, - { "3B7812000047C403008FF19000", "Sattelite bg 23.5 East cryptoworks card" }, + { "3B7812000047C403008FF19000", "Satellite bg 23.5 East cryptoworks card" }, { "3B7812000054C401078FF19000", "XtraMusic listening card (Pay TV)" }, { "3B7812000054C40204FFFF6F04", "UPC Direct Satellite TV Card (Pay TV)\nhttp://www.upcdirect.com/" }, { "3B7812000054C402078FF19000", "Polish WIZJA TV pay-tv card for satellite receivers (Pay TV)" }, @@ -1247,11 +1331,14 @@ const static atr_t AtrTable[] = { { "3B781400000073C8400000", "e-tazkira (eID)" }, { "3B781800000073C80140009000", "The Kyrgyz Republic eID (eID)" }, { "3B781800000073C840000000009000", "VERISOFT REWARDO LOYALTY CARD and CUSTOM PERSONALIZATION PROJECTS FOR EXTERNAL ENTITIES (Loyalty)\nhttp://www.verisoft.com" }, + { "3B781800000073C84013009000", "Mastercard (Bank)" }, { "3B781800005448204E4944203.", "Thailand National ID (eID)" }, { "3B7818000100000000C31E6919", "Storage for passwords (Bank)" }, { "3B781800FF0073C84000009000", "NAB Visa Debit - Contact I/F (Bank)" }, + { "3B789400008684044930310604", "bank of china Debit Card (Bank)" }, { "3B789600000073C84000009000", "SAM card for acquirer module by lanit.ru (Bank)" }, { "3B789600005343066001079000", "Bank of America Travel Rewards Credit Card (Bank)\nhttps://www.bankofamerica.com/credit-cards/products/travel-rewards-credit-card/" }, + { "3B789600005343066201079000", "ATM Card (Bank)" }, { "3B789600008100035001079000", "RMA BMCE BANK CARD (Bank)" }, { "3B791100008054434F4C44829000", "amazon.de / VISA / LBB Debit Card (Bank)" }, { "3B79130000806416030183829000", "Raiffeisen VPay Debit Card (Bank)\nhttp://raiffeisen.ch" }, @@ -1262,11 +1349,13 @@ const static atr_t AtrTable[] = { { "3B791800008054434F4C44829000", "LBB VISA Card" }, { "3B7918000080634B560283079000", "Pay charge of South Korean Highway Tollgate, It named hi-pass. This card is sales by 'SM hiplus', and card name is 'Greencar Postpaid hi-pass Card'. In this name, 'Greencar' means South Korea's Car Sharing Service. This card from Rented car. (Transport)\nhttps://www.lottecard.co.kr/" }, { "3B79180000806355500183079000", "TEST SAM Card (espark) (Other)" }, + { "3B79180000806355561183039000", "Hi-Pass (Other)" }, { "3B79180000806355561283079000", "'hi-pass+ Prepaid hi-pass Card' pay charge of South Korean Highway Tollgate. Sold by 'SM hiplus' (Transport)\nhttp://www.hipluscard.co.kr/" }, { "3B799400005901010E016B0102A9", "Vending machine payment card ('Necta' brand)" }, { "3B79940000EB03010102700102A9", "Sodexo - Spanish vending machines (Other)\nhttp://es.sodexo.com/home.html" }, { "3B79940000F704010103800102A9", "Pelican Rouge, vending machine (Other)\nhttp://colnect.com/es/functional_cards/functional_card/26065-Maquinas_Cafe_-_Pelican_Rouge-Pelican_Rouge-Vending-Espa%C3%B1a\nhttp://www.pelicanrouge.co.uk/en-uk/about-us" }, { "3B7995000054454C454D10211010", "Israeli Identity Card (eID)\nhttp://smartid.gov.il/English/Pages/default.aspx" }, + { "3B799600002001010601000100E9", "Casino (Other)" }, { "3B799600005448204E494420313.", "Thai National ID Card (eID)" }, { "3B799800005001010401000101A9", "Gemalto PayFlex used in Aristocrat System 7000 Casino Management System (South Africa only)" }, { "3B79980000EB03010000700101A9", "Casino Rio Patras, Greece" }, @@ -1287,16 +1376,17 @@ const static atr_t AtrTable[] = { { "3B7A9600008065A201200100303D72D641", "Oman eID (eID)" }, { "3B7A9600008065A20120013D72D641", "Oman eID (eID)" }, { "3B7A9700008065B08520040272D64.", "OMAN EID CARD (eID)" }, + { "3B7A9700008065B08521040272D641", "Oman eID (eID) (eID)\nhttps://idp.pki.ita.gov.om/" }, { "3B7B..000080620.515646696E454944", "Setec SetCOS 5.1.0" }, { "3B7B..00008065B083......83009000", "IDClassic 3XX Cards (without MPCOS Applet - FIPS)" }, - { "3B7B11000031FE45436F6D624F532000", "Debit card emmited by Societe Generale Morocco (Bank)" }, + { "3B7B11000031FE45436F6D624F532000", "Debit card emitted by Societe Generale Morocco (Bank)" }, { "3B7B1800000031C06477E30300829000", "Oberthur Cosmopolic 64K v5.2 D" }, { "3B7B1800000031C06477E91000019000", "Oberthur Card Systems: Cosmo 64 RSA V5.4 (ISK Key Set: 404142 .. 4E4F)" }, { "3B7B1800000031C06490E31100829000", "oberthur card in the middle http://postarca.posta.si/downloadfile.aspx?fileid=16918 (eID)\nhttp://postarca.posta.si/" }, { "3B7B1800000031C064C6FC1000079000", "Sberbank of the RUSSIAN federation" }, { "3B7B1800000031E85427E60000019000", "Oberthur ID-One COSMO 64k v.5" }, - { "3B7B180000806201545646696E454944", "FineID identity card for organizations\nhttp://fineid.fi/default.aspx?id=491\nhttp://www.opensc-project.org/opensc/wiki/FinnishEid" }, - { "3B7B94000080621[12]515646696E454944", "Finnish Electronic ID card (fineid card www.fineid.fi)" }, + { "3B7B180000806201545646696E454944", "FineID identity card for organizations\nhttps://dvv.fi/en/organisation-cards\nhttps://github.com/OpenSC/OpenSC/wiki/Finnish-FINEID" }, + { "3B7B94000080621[12]515646696E454944", "Finnish Electronic ID card for persons (Former FINeID card)\nhttps://dvv.fi/en/citizen-certificate-and-electronic-identity" }, { "3B7B9400008065521607865383009000", "Truemove H Thailand (Telecommunication)\nhttp://truemoveh.truecorp.co.th/" }, { "3B7B9400008065B083010[13]7483009000", "Gemplus GemXpresso Pro R3 (E64 PK)" }, { "3B7B9500008065B08301047483009000", "Gemplus GemXpresso Pro 64K R3 FIPS v2" }, @@ -1341,8 +1431,9 @@ const static atr_t AtrTable[] = { { "3B7D94000080318065B08311C0A983009000", "GemXpresso R4 72K\nNational ID card of Republic of Lithuania (2007-2012)" }, { "3B7D95000080318065B08302047E83009000", "Gemalto's Classic TPC HM CC Mifare 1K White PVC\n(Old name GemSafeXpresso 64K)" }, { "3B7D95000080318065B08311....83009000", "Portuguese ID Card (eID)\nhttp://www.cartaodecidadao.pt/" }, + { "3B7D96000080318065B07549170F83009000", "DoD CAC card issued ~October 2020, Gemalto TOP DL V2.1 144K, Geneva Conventions Identification Card (PKI)\nhttps://www.cac.mil/" }, { "3B7D96000080318065B0830201F383009000", "Gemalto IDClassic 340" }, - { "3B7D96000080318065B0831100C883009000", "GEMALTO Clasic TPC IM CC" }, + { "3B7D96000080318065B0831100C883009000", "GEMALTO Classic TPC IM CC" }, { "3B7D96000080318065B0831111AC83009000", "GEMALTO WM GX4 72 DHS TSA" }, { "3B7D96000080318065B0831111E583009000", "Gemalto TOP DL v2 StdR\neCPF (Cadastro de Pessoas Fisicas) from Imprensa Oficial do Brasil\nIdentidade digital (e-CPF) from Caixa\nhttp://www.caixa.gov.br/\nIngenico Sign/Kit Telium TETRA (Developer kit signature card)\nhttps://developer.ingenico.com/hc/en-gb\nBrazilian 'e-CNPJ' card, issued by Certisign (Safesign)" }, { "3B7D96000080318065B0831113AC83009000", "CAC card (GEMALTO GCX4 72K DI)" }, @@ -1375,14 +1466,14 @@ const static atr_t AtrTable[] = { { "3B7E9400008025A00000002856801021000114", "CryptoPlus ProID, students ISIC card at University of Zilina, Slovakia\nTOP GX4 72k contact chip using JavaCard v2.2.1. and GlobalPlatform 2.1.1" }, { "3B7E9400008025A00000002856801024000111", "'OpenCard' - card issued by Prague local authority\ndual card: chip used for electronic certificates" }, { "3B7E9400008025D20310010056000000010100", "Personal identity card (ID card) of Czech Republic" }, - { "3B7E9400008025D20310010056000000020200", "Official Czech Republic identity card issued since 7/2018 (eID)\nhttps://www.eidentita.cz/" }, + { "3B7E9400008025D20310010056000000020200", "Official Czech Republic identity card issued since 7/2018 (eID)\nhttps://info.identitaobcana.cz/eop/" }, { "3B7E94000080318066475091450313830F9000", "JTOP Trusted Logic" }, { "3B7E940000803180664750A4450511830F9000", "JTOP Trusted Logic" }, { "3B7E9500008031807334118082900000000000", "IAS (Identification, Authentication, and electronic Signature) Premium, profil DGME from Gemalto" }, { "3B7F..0000006A4345524553022C3402..039000", "Ceres ST v2" }, { "3B7F..0000006A4345524553022C3403..039000", "Ceres ST v3" }, { "3B7F..00008031..65B000000000......8290..", "MultiApp V2.1 (with IAS XL / IAS ECC and IAS Classic Applet V3) [MultiApp V2.1 Type 2]" }, - { "3B7F..000080318065B0........120FFE829000", "IDPrime MD 8840, 3840, 3810, 840 and 830 Cards T=0" }, + { "3B7F..000080318065B0........120FFE829000", "IDPrime MD 8840, 3840, 3810, 840 and 830 Cards T=0\nIDPrime 930 (JavaCard)" }, { "3B7F0100FE58434F53763235312863295046424D", "XCOS is an Experimental Card Operating System for Atmel based smartcards (Funcard, etc..) (Other)\nhttp://runningserver.com/?page=runningserver.content.download.xcos" }, { "3B7F04000080318071906754454D44412E309000", "public key (PKI)" }, { "3B7F1100000031C053CAC4016452D90400829000", "DoD CAC, Oberthur CosmopolIC 32K V4" }, @@ -1410,17 +1501,19 @@ const static atr_t AtrTable[] = { { "3B7F1300008031C0520B71640566983683839000", "OuroCard VISA International - Banco do Brasil" }, { "3B7F1300008031C0520BBB640566983683839000", "VISA OUROCARD Banco do Brasil S.A." }, { "3B7F1300008031C0520D1E640566946183839000", "Universitat Politecnica de Valencia ID Visa Electron Card\nhttp://www.upv.es" }, - { "3B7F1300008031C0520DEA640566946183839000", "Universitat Rovira i Virgili Identification Card\nThis card enables his/her propietary to sign documents and to access to rooms, laboratories and classes." }, + { "3B7F1300008031C0520DEA640566946183839000", "Universitat Rovira i Virgili Identification Card\nThis card enables his/her proprietary to sign documents and to access to rooms, laboratories and classes." }, { "3B7F1300008031C0520ED4640566966183839000", "BBVA Blue Card (VISA)\nwww.bluebbva.com" }, { "3B7F1300008031C05210496402B3027083839000", "Bank of Brazil (Bank)\nhttp://www.bb.com.br/pbb/pagina-inicial" }, { "3B7F1300008031C05210686402B3027083839000", "Santander VISA (Brazil) (Bank)" }, { "3B7F1300008031C05210A46402B3027083839000", "EMV Smartcard Reader (Other)\nSaraiva (Banco do Brasil) - VISA (Bank)\nhttps://www.bb.com.br/pbb/pagina-inicial/voce/produtos-e-servicos/cartoes#/" }, { "3B7F1300008031C052112F640569937083839000", "Banco do Brasil (Bank)\nhttp://www.bb.com.br/" }, { "3B7F1300008031C05212936402B3027083839000", "Visa Infinite issued by Banco do Brasil (www.bb.com.br)\nManufactured by Giesecke & Devrient (G&D www.gi-de.com)" }, + { "3B7F1300008031C05213076402B3023783839000", "Banco do Brasil ELO Debito (Brazil) (Bank)" }, { "3B7F1300008031C052130B6402B3023783839000", "Visa Ourocard Platinum from Banco do Brasil" }, { "3B7F1300008031C0521366640566983683839000", "VISA Platinum from Banco do Brasil" }, { "3B7F1300008031C05214D5640566966183839000", "Visa Electron Debit Card issued by Spain Caja Madrid (www.cajamadrid.es)" }, { "3B7F1300008031C0521554640566983683839000", "VISA Card from Banc Sabadell in Spain" }, + { "3B7F1300008031C0521563640200647183839000", "VISA CREDIT (Bank)" }, { "3B7F1300008031C05215B8640200647183839000", "Debit classic card banorte 'banco mercantilmdel norte' (Bank)\nhttps://www.banorte.com/" }, { "3B7F1300008031C05215C7640569937083839000", "Brazil Bank Debit Cart - Visa Electron (Banco do Brasil) (Bank)" }, { "3B7F1300008031C05215CB640566966183839000", "Bank, Spanish group BANKIA (www.bankia.es)\nVisa, Servired\nManufactured by Saetic\nhttp://www.saetic.es/" }, @@ -1488,13 +1581,16 @@ const static atr_t AtrTable[] = { { "3B7F960000006A444E4965200101550421039000", "DNI electronico (Spanish electronic ID card) (eID)\nhttp://www.dnielectronico.es" }, { "3B7F960000006A444E4965200177980311039000", "Spanish ID card (DNIe) (eID)\nwww.dnielectronico.es/" }, { "3B7F960000006A464E4D54030411430430039000", "CERES Spanish SmartCard from the 'Fabrica Nacional de Moneda y Timbre' (FNMT) (eID)\nhttp://www.cert.fnmt.es/" }, + { "3B7F960000006A54494633000101550422039000", "DNIe Spain (eID)\nhttps://www.dnielectronico.es/PortalDNIe/" }, { "3B7F96000000B854311007900000000000000000", "Mastercard (Bank)" }, { "3B7F96000000B854311107900000000000000000", "STPay-Gold (JavaCard)\nhttp://www.st.com/en/secure-mcus/stpay-gold.html" }, { "3B7F9600003100DE525001001500000000000000", "VALES (Bank)" }, + { "3B7F9600008031805843657274756D3031829000", "Certum Electronic Seal (PKI)" }, { "3B7F9600008031806555850300EF124140829000", "Civil ID (eID)" }, { "3B7F96000080318065B0842327E5120FFE829000", "Gemalto IDPrime MD 3810 dual interface smartcard ISO 7816, ISO 14443 and NFC (PKI)\nhttp://www.smartcardfocus.com/shop/ilp/id~672/gemalto-idprime-md-3810-dual-interface-smartcard/p/index.shtml" }, { "3B7F96000080318065B084413DF612004C829000", "Serasa Experian (Other)" }, { "3B7F96000080318065B084413DF6120FFE829000", "Gemalto IDPrime MD" }, + { "3B7F96000080318065B084413DF612FFFE829000", "THALES IDCore 30 B (JavaCard)\nhttps://cpl.thalesgroup.com/access-management/idcore-java-card" }, { "3B7F96000080318065B084534C0F12037B829000", "Serbian International Student Identity Card (ISIC) (eID)\nhttps://www.gpa.rs/kartice" }, { "3B7F96000080318065B0846160FB120FFD829000", "IDPrime 930/3930 FIPS Level 2 or Level 3 (T=0 CT=96) (BAI1, BAI2, BAI3, BAI5, BAI7) (PKI)" }, { "3B7F96000080318065B0850201F3120FFF829000", "Swedish National Identity Card (eID)\nhttps://polisen.se/tjanster-tillstand/pass-och-nationellt-id-kort/" }, @@ -1510,18 +1606,26 @@ const static atr_t AtrTable[] = { { "3B7F96000080318065B085040120120FFF829000", "Belgian eID v1.8 (eID)\nhttps://github.com/Fedict/eid-mw" }, { "3B7F96000080318065B085040120F20001829000", "Health Insurance Institute of Slovenia - Health Insurance Card Gen. 3 (HealthCare)" }, { "3B7F96000080318065B085040120F20002829000", "Health Insurance Institute of Slovenia - Professional Card Gen. 3 (HealthCare)" }, + { "3B7F96000080318065B085050011120FFF829000", "LuxTrust card (Luxembourg qualified electronic signature / authentication system) (Other)\nhttps://www.luxtrust.com/en/professionals/smartcard" }, + { "3B7F96000080318065B0855956FB120268829000", "qualified certificate (eID)\nhttps://www.elektronicznypodpis.pl/en/offer/qualified-certificates/" }, + { "3B7F96000080318065B0855956FB1202C1829000", "Gemalto USB (eID)" }, + { "3B7F96000080318065B0855956FB120FFE829000", "Thales (Gemalto) IDPrime 941 (PKI)\nhttps://cpl.thalesgroup.com/de/access-management/idprime-md-pki-smart-cards\nThales SafeNet IDPrime 940B (PKI)\nhttps://cpl.thalesgroup.com/resources/access-management/idprime-940-product-brief" }, { "3B7F96000080318065B0855956FB12FFFE829000", "IDCORE 3140 (JavaCard)" }, { "3B7F9600008031B865B0850300EF1200F6829000", "Finnish identity card (eID)\nhttp://vrk.fi/en/citizen-certificate" }, { "3B7F9600008031B865B08504021B1200F6829000", "Finnish ID-card v5.0(?) (eID)\nhttps://dvv.fi/en/fineid-specifications" }, + { "3B7F9600008031B865B085050011122460829000", "wasim finnish id (eID)" }, { "3B7F960000EA5CBDF07AEB6541894B0400000000", "Gemalto TOP IM GX4 (Other)" }, + { "3B7F960080318065B084413DF612004C829000", "Black (PKI)\nhttps://certificadodigital.imprensaoficial.com.br" }, { "3B7F9700000031C173C821106457533430009000", "PNC Bank VISA card (Bank)" }, + { "3B7F97000080318065B08466693912FFFE829000", "IDCore3230 build 6.8, test APDU applet (JavaCard)" }, { "3B7F980000805A070404000102028458F2829000", "Mobili'carte (Angouleme mobility services card) (Transport)\nhttp://www.mobilite-grandangouleme.fr/" }, { "3B800181", "NXP Semiconductors VNG OpenPGP Card (Other)\nhttps://www.vng.com.vn/" }, - { "3B80800101", "ISO 14443 Type B without historical bytes\nElectronic Passport\nSpanish passport (2012)\nCanadian Passport\nVenez_Prox" }, + { "3B801FC78031E073FE211163407163830790009A", "Intertelecom UA RUIM card (Telecommunication)\nhttps://www.intertelecom.ua/" }, + { "3B80800101", "ISO 14443 Type B without historical bytes\nElectronic Passport\nSpanish passport (2012)\nCanadian Passport\nVenez_Prox\nCarta nazionale dei servizi\nhttps://www.agid.gov.it/it/piattaforme/carta-nazionale-servizi" }, { "3B810020", "Old MobilCOM GSM (Telecom D1) (Telecommunication)" }, { "3B81010080", "Rompetrol Romania Fill&Go Fuel Card (Other)" }, { "3B811F00CC52", "eToken R2 2242" }, - { "3B8180018080", "RFID - ISO 14443 Type A - NXP DESFire or DESFire EV1 or EV2\n'Reiner LoginCard' (or 'OWOK', how they name it) - they have been distributed by a german computer magazine ('Computer BILD')\nhttps://cardlogin.reiner-sct.com/\nBelgium A-kaart (Antwerp citycard)\nOyster card - Transport for London (second-gen 'D')\nhttps://en.wikipedia.org/wiki/Oyster_card\nKaba Legic Advant 4k\nSydney Opal card public transport ticket (Transport)\nhttps://www.opal.com.au\nTH Koln (University of Applied Sciences Cologne) - Student Identity Card\nhttps://www.th-koeln.de/en/academics/multica_5893.php\nGerman red cross blood donation service\nhttp://www.blutspende-nordost.de/\nGreater Toronto/Hamilton/Ottawa PRESTO contactless fare card\nhttp://en.wikipedia.org/wiki/Presto_card\nElectic vehicle charging card of the EMSP EnBW Energie Baden-Wurttemberg AG, Tarif ADAC e-Charge, Germany" }, + { "3B8180018080", "RFID - ISO 14443 Type A - NXP DESFire or DESFire EV1 or EV2\n'Reiner LoginCard' (or 'OWOK', how they name it) - they have been distributed by a german computer magazine ('Computer BILD')\nhttps://cardlogin.reiner-sct.com/\nBelgium A-kaart (Antwerp citycard)\nOyster card - Transport for London (second-gen 'D')\nhttps://en.wikipedia.org/wiki/Oyster_card\nKaba Legic Advant 4k\nSydney Opal card public transport ticket (Transport)\nhttps://www.opal.com.au\nTH Koln (University of Applied Sciences Cologne) - Student Identity Card\nhttps://www.th-koeln.de/en/academics/multica_5893.php\nGerman red cross blood donation service\nhttp://www.blutspende-nordost.de/\nGreater Toronto/Hamilton/Ottawa PRESTO contactless fare card\nhttp://en.wikipedia.org/wiki/Presto_card\nElectric vehicle charging card of the EMSP EnBW Energie Baden-Wurttemberg AG, Tarif ADAC e-Charge, Germany" }, { "3B8180018181", "Ticket (Transport)" }, { "3B82005518", "GSM SIM of TIM ITalian mobile company" }, { "3B82005519", "GSM card 'Hi' (KPN brand)" }, @@ -1575,9 +1679,12 @@ const static atr_t AtrTable[] = { { "3B8580012063CBAD0021", "NUMBER26 MasterCard (Bank)\nhttps://number26.eu" }, { "3B8580012063CBAD2001", "KBC Ireland MasterCard Debit Card (Bank)\nhttp://www.kbc.ie/" }, { "3B8580012063CBAD80A1", "VISA Caisse d'Epargne (Bank)\nVISA BNP Paribas (NFC) (Bank)" }, - { "3B8580012063CBB7201B", "Halifax Clarity Mastercard (Bank)" }, + { "3B8580012063CBB7003B", "Visa card issued by OTP Bank (Hungary) (Bank)\nhttps://www.otpbank.hu/portal/en/Retail/Bankcards" }, + { "3B8580012063CBB7201B", "Halifax Clarity Mastercard (Bank)\nCredit Agricole Prepaid - Mastercard" }, { "3B8580012063CBB780BB", "First Direct Debit (Bank)" }, { "3B8580012063CBB880B4", "VISA Cleo LCL (Bank)\nhttps://particuliers.lcl.fr/quotidien/cartes/carte-visa-cleo/" }, + { "3B8580012063CBC6004A", "Gold Mastercard Credit Agricole (Bank)" }, + { "3B858001300101301014", "German public health insurance card (,,Gesundheitskarte'), 2.1 generation (G2.1), issuer Techniker Krankenkasse (HealthCare) (HealthCare)\nhttps://www.gematik.de/telematikinfrastruktur/egk" }, { "3B858001300101303034", "German Health Insurance Card 'elektronische Gesundheitskarte' (eGK) / European Health Insurance Card (EHIC) (HealthCare)\nhttps://fachportal.gematik.de/karten-und-identitaeten/elektronische-gesundheitskarte" }, { "3B8580014A4D52544441", "JMRTD - Java Machine Readable Travel Document (ePassport emulator\nhttp://jmrtd.org/)" }, { "3B8580014D7945494478", "MyEID" }, @@ -1588,21 +1695,28 @@ const static atr_t AtrTable[] = { { "3B858001A000000000A4", "Italian Passport (passport)" }, { "3B85C0FA216380630101051B", "Public Distribution System\nhttp://cg.nic.in/pdsonline/corepds/" }, { "3B8640206801010204AC", "Activcard Gold, SchlumbergerSema Cryptoflex 8k" }, + { "3B8640FA808101520301", "SCOSTA (JavaCard)" }, { "3B86800100049AEE00CABD", "ASK CPL 528" }, { "3B86800103020211000015", "cash bee card. transportation and payment in south korea (seoul) (Transport)\nhttp://cashbee.co.kr" }, { "3B86800106757781028000", "NXP Mifare DESFire EV1 8K / MF3ICD81\n'OpenCard' - card issued by Prague local authority\ndual card: RFID (1k Mirfare) used for parkimeters and as public traffic ticket" }, { "3B86800106757781028F00", "Desfire (eID)" }, { "3B8680013930433032317E", "Chase Freedom VISA card" }, + { "3B86800143686970313733", "PostFinance Switzerland (Bank)\nhttp://www.postfinance.ch" }, { "3B86800144492030324D65", "Lufthansa Miles & More Gold MasterCard PayPass\nRaiffeizen Bank, Russia, MasterCard paypass card" }, { "3B8680014449203032567E", "DKB VISA paywave (Bank)\nhttp://www.visaeurope.com/en/cardholders/visa_paywave.aspx" }, { "3B8680014A434F50333012", "Mifare ProX T=CL" }, { "3B8680014A434F50333113", "JCOP BIO 31 Contactless Card" }, { "3B8680014B4F4E4112203E", "Citibank Russia, Mastercard paypass" }, + { "3B8680014B4F4E4113213E", "Vivid Money Visa Debit (Bank)\nhttps://vivid.money" }, { "3B8680014B4F4E41141109", "Mastercard paypass enabled credit card" }, { "3B8680015741524930310B", "Gusto Karta (Bank)\nhttps://www.gustokarta.cz" }, { "3B86800157575061737336", "WWPass Passkey (eID)\nhttps://www.wwpass.com/passkey" }, + { "3B868001801434373000A0", "Malta eID Identity Card (eID)\nhttps://www.identitymalta.com/" }, + { "3B8680018031C15211182C", "IDEMIA Cosmo V8.0 with a PIV applet (contactless) (PKI)" }, + { "3B8680018031C152411A7E", "IDEMIA Cosmo V8.1 with a PIV applet (contactless) (PKI)" }, { "3B86800180540410010FC9", "Nickel.eu prepaid account (Bank)\nhttps://nickel.eu" }, { "3B868001C1052F2F01BC7E", "Contactless interface to St. Petersburg unified card, Russia (Edinaia karta peterburzhtsa) (Other)\nhttps://ekp.spb.ru/" }, + { "3B868001F04938DE0C3064", "blyt mtrw (Transport)\nhttps://ezpay.ir/" }, { "3B868131703445504120454B08", "Austrian Quick E-purse 'Einreichkarte' (transfer card)\nhttp://www.quick.at/" }, { "3B8780014D49464152452B39", "PEKA CARD (JavaCard)" }, { "3B8780014D525444312E3026", "Russian Foreign Passport (passport)" }, @@ -1619,21 +1733,26 @@ const static atr_t AtrTable[] = { { "3B8780017743495002000128", "CIPURSE (transport)\nhttp://www.osptalliance.org/" }, { "3B8780018031807396128040", "Public transport: VRS Verkehrsverbund Rhein-Sieg (Germany, North_Rhine-Westphalia)\nhttp://www.vrsinfo.de/englisch/the-vrs/vrs-about-us.html" }, { "3B878001803198738401E039", "Austrian Passport" }, + { "3B878001803198738601E03B", "German passport (passport)" }, { "3B8780018031B8738401E019", "Personalausweis (German Identity Card) (eID)" }, - { "3B8780018031B973842160B8", "Hungarian eID (2016) (eID)\nhttp://kekkh.gov.hu/Eszemelyi/" }, + { "3B8780018031B8738601E01B", "Personalausweis (German Identity Card) (eID)\nhttps://www.personalausweisportal.de/" }, + { "3B8780018031B973842160B8", "Hungarian eID (2016) (eID)\nhttps://eszemelyi.hu/en/" }, { "3B8780018031C073D620C032", "RFID GeldKarte (girogo)" }, { "3B8780018031C073D621C033", "Sparkasse Hannover - German contactless GeldKarte (RFID, NFC, girogo)\nhttps://www.geldkarte.de/_www/en/pub/geldkarte/service_navigation/about_us.php" }, { "3B8780018031C073D631C023", "girocard contactless (Bank)\nhttps://www.girocard.eu/english.html" }, + { "3B8780018073842140900080", "Queensland drivers licence (eID)\nhttps://www.qld.gov.au/transport/licensing/driver-licensing" }, { "3B878001C10521300077C165", "Mifare Plus (Other)\nhttp://www.Mifare.net" }, { "3B878001C1052F2F0035C730", "MiFare Plus 2K 'S' (Other)" }, { "3B878001C1052F2F01BCD6A9", "RFID - ISO 14443 Type A - NXP Mifare Plus" }, { "3B87813140434D4643203133316F", "Telekom Paycard" }, { "3B88010003050668D06080D1", "125 Khz HID Proximity card (eID)\nhttps://www.hidglobal.com/product-display/cards-and-credentials/hid-proximity" }, { "3B88014B415A544F4B454E82", "Kaztoken (eID)\nhttp://kaztoken.kz/" }, + { "3B88018056536F6C6F203272", "SoloKeys Solo 2 Security Key (PKI)\nhttps://github.com/solokeys/solo2" }, { "3B888001000000000000000009", "Personalausweis (German Identity Card) (eID)" }, { "3B8880010000000000419100D9", "EffiTIC (Transport)\nwww.effitic.com" }, { "3B888001000000000071710009", "OPUS Public Transport card (Montreal, Quebec, Canada) - Oberthur based\nhttp://carteopus.info/\nACTV (Italy) transport card (RFID)" }, { "3B8880010000000000718100F9", "Navigo Decouverte (RFID interface) (Transport)" }, + { "3B8880010000000000817000F8", "Qantas Frequent Flyer Loyalty Card - Contactless (Loyalty)" }, { "3B8880010000000000817700FF", "KBC Maestro card (Bank)" }, { "3B88800100000000008187000F", "ING Bank Card (Bank)\nhttps://www.ing.nl/particulier/betalen/passen/betaalpas/contactloos-betalen-met-uw-betaalpas/index.html" }, { "3B888001000000000081911009", "Trenitalia (Italy) fidelity card 'CartaFreccia' (RFID)" }, @@ -1656,8 +1775,10 @@ const static atr_t AtrTable[] = { { "3B888001000005E0B381A1007F", "Japanese JPKI card (aka JINC card) (eID)\nhttps://github.com/jpki/myna" }, { "3B888001000014E0B38191005E", "'JUKICARD', the Basic Resident Registration Card in Japan (eID)" }, { "3B888001000041E0B381A1003B", "ID card issued by Japan government (eID)\nhttps://www.kojinbango-card.go.jp/mynumber/index.html" }, + { "3B8880010000C9047781730041", "D-TRUST Card 4.1, qualified signature card (eID)\nhttps://www.d-trust.net" }, { "3B8880010073C8400000900062", "NXP JCOP 31 V2.2 36K - RFID I/F\nBarclaycard Visa Wave & Pay - RFID I/F\nCIBC Visa" }, { "3B8880010073C8401300900071", "Nokia 6131 NFC phone\nhttp://wiki.forum.nokia.com/index.php/Nokia_6131_NFC_-_FAQs\nGiesecke & Devrient's (G&D) Sm@rtCafe Expert 3.1\nAmex Bank of Canada American Express\nTD Canada Trust Visa\nTD Canada Trust Access Card (Visa Debit)" }, + { "3B88800100883C1F77819500C1", "Polish Passport (passport)" }, { "3B88800100DDA611F771850060", " 'Pyrelis' card, PAU (France) public transport card. (Calypso card). (Transport)\n http://www.reseau-idelis.com/930-Billettique-IDELIS.html" }, { "3B888001040200200071C140DF", "Seoul Citypass+ T-Money Card" }, { "3B8880011000000000817000E8", "Tap&Go MasterCard Sim Card (Bank)\nhttps://www.tapngo.com.hk" }, @@ -1671,7 +1792,8 @@ const static atr_t AtrTable[] = { { "3B8880011CF0E111F771850016", "CEPAS Card (Concession card issued by Land Transport Authority Singapore) (Transport)" }, { "3B88800130415654000000077D", "Avtor ID Key (eID)\nhttp://avtor.ua/" }, { "3B888001304C47127783D50001", "elesec - TCOS 3.0 Signature Card (eID)\nhttps://www.telesec.de/de/tcos/support/downloadbereich/category/28-leistungsbeschreibung" }, - { "3B88800130ABAB017781B70079", "Italian healtcare card (TS) National Service Card (CNS) (HealthCare)" }, + { "3B88800130ABAB017781B70079", "Italian healthcare card (TS) National Service Card (CNS) (HealthCare)" }, + { "3B888001310109000000000030", "Indonesian Electronic Driving License (e-SIM) (Other)\nhttps://polri.go.id/sim, https://www.digitalkorlantas.id/sim/" }, { "3B88800131CCCC017781C1000E", "Ideal v 1.4 (Transport)" }, { "3B88800131CCCC017783A1006C", "Chile RUT (eID)" }, { "3B88800131F35E110081950090", "Venez_Omnikey" }, @@ -1681,6 +1803,7 @@ const static atr_t AtrTable[] = { { "3B8880014241454944312E316C", "BiH eID card (eID)" }, { "3B88800143433169AA200000DB", "PostFinance Switzerland (Bank)\nhttp://www.postfinance.ch" }, { "3B888001434C6169726520360F", "VISA credit card with NFC payment function (Bank)\nhttp://www.visa.ca/en/personal/visa-paywave/index.jsp" }, + { "3B8880014431314352322E3070", "javacard.pro card (JavaCard)\nhttps://javacard.pro/" }, { "3B88800146494445534D4F3167", "Fidesmo card (install or uninstall JavaCard applets or Mifare-based services on the field, using the Fidesmo Android App. (JavaCard))\nhttps://developer.fidesmo.com/" }, { "3B8880014A434F50763234315E", "RFID - ISO 14443 Type A - NXP JCOP\nNXP J3A081 JavaCard (contactless interface)" }, { "3B88800150FFFF117783D50069", "Gematik TSYS eHBA G2.1 (HealthCare)" }, @@ -1694,19 +1817,23 @@ const static atr_t AtrTable[] = { { "3B888001990200D10304220167", "Debit card (Bank)" }, { "3B888001C91207520200811014", "electronic Tickes from the german Transport Association VGN (Verkehrsgemeinschaft Niederrhein)" }, { "3B888001D10386050080800058", "Resident Identity Card of People Republic of China (Second Generation with RF Feature) (eID)\nhttp://www.gov.cn/banshi/2005-08/02/content_19457.htm" }, + { "3B888001E1686C17778395009A", "Algerian ID card (eID)" }, { "3B888001E1E1F35E1377830043", "ePerso - German ID card (issued 2011) (eID)" }, + { "3B888001E1F35E113381970071", "DK passport (passport)" }, { "3B888001E1F35E117381A50003", "US passport (2007)" }, - { "3B888001E1F35E117781950037", "Belgian Passport (passport)" }, + { "3B888001E1F35E117781950037", "Belgian Passport (passport)\nFinnish Passport (passport)" }, { "3B888001E1F35E117781A10003", "Spanish ID know as DNIe (eID)\nhttp://www.dnielectronico.es" }, { "3B888001E1F35E117781A50007", "US passport (2012)" }, { "3B888001E1F35E117781C72045", "French passport (2007-2008)" }, { "3B888001E1F35E117781E10043", "DNIE Spain (eID) Contactless (eID)\nhttp://www.dnielectronico.es/PortalDNIe/" }, + { "3B888001E1F35E117781E16023", "DNIe v4 (Spanish eID) - contactless interface (PC/SC wrapped 14443 Type B ATR) (eID)\nhttps://www.dnielectronico.es" }, { "3B888001E1F35E1177830000A0", "Residence permit (eID)\nhttp://de.wikipedia.org/wiki/Aufenthaltstitel" }, { "3B888001E1F35E117783950035", "French biometric ePassport (issued in 2012)" }, { "3B888001E1F35E117783D50075", "German Passport (ePass) (issued June 2009)" }, { "3B888001E1F35E117783D70077", "Spanish Electronic Passport 2.0 (passport)\nhttps://www.dnielectronico.es/PortalDNIe/PRF1_Cons02.action?pag=REF_1080&id_menu=56" }, { "3B888001E1F35E117787950031", "Dutch Government Pilot E-rijbewijs (eID)\nhttps://www.digid.nl/over-digid/kaartlezer-pilot" }, { "3B888001E1F35E1180879500C6", "Irish Driver Learner Permit (Other)" }, + { "3B888001E1F35E11B381A500C3", "Australian Passport (passport)\nhttps://www.passports.gov.au/" }, { "3B888001E1F35E1377830000A2", "ePerso - German ID card (issued 2013)" }, { "3B888001E1F35E137783D50077", "ePerso - German ID card (issued 2011)" }, { "3B88813120550057696E4361726429", "SmartCard for Windows 1.0" }, @@ -1717,6 +1844,7 @@ const static atr_t AtrTable[] = { { "3B894014474732364D35323830", "GSM-SIM e-plus (1800MHz)" }, { "3B898001006404150102009000EE", "German Passport (issued Apr 2007)" }, { "3B898001006404280302009000D1", "TCOS 3.0 release 2 on Philips P5CD080 (PKI)\nhttps://www.digchip.com/datasheets/parts/datasheet/1019/P5CD080.php" }, + { "3B898001024D4B4D574B534B5411", "Polish National ID (eID)\nhttps://www.gov.pl/web/e-dowod/" }, { "3B8980013131313054434F535052", "Seven-Eleven Value Card / Smart Purse - Thailand (Contactless) (Other)\nhttp://www.7eleven.co.th/about7card.php" }, { "3B8980013233324353435333363E", "CSCS Smartcard (passport)\nhttp://getgosmart.io" }, { "3B89800141434F534A763130311A", "ACS ACOSJ (Combi) (JavaCard)\nhttp://www.acs.com.hk/en/products/405/acosj-java-card-combi/" }, @@ -1737,13 +1865,15 @@ const static atr_t AtrTable[] = { { "3B89800150565F4A33413034305D", "Java Card J3A040 (JavaCard)\nhttp://smartcardsource.com/contents/en-ca/d9_JCOP-NXP-cards.html" }, { "3B898001535049564B4559373044", "Taglio PIVkey C980 smart card (Other)\nhttps://pivkey.com/" }, { "3B89800153504B323544499000DA", "SPK 2.5 D1" }, + { "3B89800153776973735061737374", "SwissPass - key to mobility and leisure in Switzerland (Transport)\nhttps://www.swisspass.ch\nhttps://www.allianceswisspass.ch/de/informationen-ov-nutzende/SwissPass" }, { "3B89800166494658425332476F32", "Blockchain Security 2Go (JavaCard)\nhttps://github.com/Infineon/Blockchain" }, { "3B898001664A41434F53322E3016", "MasterCard, Credit Card by TargoBank, Germany -- (Bank)" }, - { "3B898001665257453250524F4D1C", "NFC enabled SIM card. (Telecommunication)\nElectic vehicle charging card of the EMSP Chargepoint" }, + { "3B898001665257453250524F4D1C", "NFC enabled SIM card. (Telecommunication)\nElectric vehicle charging card of the EMSP Chargepoint" }, { "3B8980016653434F4E454432307C", "Alioth 98886 chip (Bank)" }, { "3B8980018057454D5650524F438F", "BNP Paribas Visa Classic Card (NFC) (Bank)\nhttps://mabanque.bnpparibas/fr/notre-offre/comptes-cartes-et-services/cartes-et-moyens-de-paiement/decouvrir-toutes-les-cartes/carte-visa-classic" }, { "3B89800180574A4D5650726F33F0", "PayPal UK MasterCard Contacless" }, { "3B89800180574A4D5676352E30D3", "PayPal Access Business MasterCard (contactless) (Bank)\nCoinbase Card, Visa Debit (Paysafe / TCT FCU) (Bank)\nhttps://coinbase.com/card" }, + { "3B8980018064160401868290006B", "LUKB (Bank)\nhttps://www.lukb.ch" }, { "3B8980018064160402828290006C", "Maestro card (from Mastercard) used by BCGE (switzerland) bank (Bank)" }, { "3B89800180670412B0030501024C", "Austrian Passport" }, { "3B8A0091010016000116010096", "GSM-SIM T-Mobil D1 (900MHz)" }, @@ -1761,13 +1891,20 @@ const static atr_t AtrTable[] = { { "3B8A80010031C173C8400000900090", "NXP PN65o's Internal Secure Element in card emulation mode. (Other)" }, { "3B8A80010064055C02033180900016", "T-System Contactless Netkey Card" }, { "3B8A8001006405760203318090003C", "T-System Contactless TCOS Min" }, + { "3B8A80013037393134315F3030316F", "Electronic Citizen Identity Card (e-ID Card) (eID)\nhttps://www.e-ktp.com/" }, + { "3B8A800143323330302D4649505368", "Crescendo 2300 FIPS (contactless interface) (PKI)" }, { "3B8A80014A3341303831563234316B", "NXP JCOP CJ3A081 (NFC) (JavaCard)\nNXP JCOP 80K - J3A081V241" }, + { "3B8A80014A33523138302D32353506", "Cardlogix J3R180 NXP JCOP 4 Java Card 3.0.5 Classic Dual Interface (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic/" }, { "3B8A80014A434F503331563233327A", "Snapper New Zealand (JCOP)" }, { "3B8A80014A434F503431563232317F", "JCOP41 Cards (not supported, but recognized by Classic Client)\nNXP JCOP 41 v2.2.1 72k RFID I/F" }, + { "3B8A800150564A434F5033454D5676", "NXP JCOP3 J3H082 Java Card 3.0.4 Dual-Interface (JavaCard) (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop3-j3h082-java-card-3-0-4-j3h081-dual-interface/" }, + { "3B8A800150564A434F503453494471", "J3R180 via ifdnfc (JavaCard)" }, + { "3B8A80015345204445534669726557", "NXP-Mifare DESFire EV1 2k (used as a company ID card) (eID)" }, { "3B8A8001534F535345020325010374", "Super SIM X-SIM 16-in-1 (Telecommunication)\nhttps://multi-com.eu/,details,id_pr,14881,key,super-sim-16-in-1-card,smenu,gsm.html" }, { "3B8A8001546963546F6B20332E3008", "Cryptas TicTok v3 (PKI)\nhttps://www.cryptas.com/en/products/tictok-card" }, { "3B8A80018031B8738401E082900006", "German ID Card - Personalausweis" }, { "3B8A80018031F873F741E082900075", "ePerso - German ID card" }, + { "3B8A800180641211111073C0C1801F", "Belarussian ID card (eID)" }, { "3B8A80018065A20101013D72D64397", "Gemplus GemXpresso Pro R3 E32 PK (combi)\nMultiApp ID Dual Citizen EAC 80K CC / IDClassic 3340 (old name: Classic TPC DM) (with MPCOS Applet installed by default) - Contactless Mode with Prox DU" }, { "3B8A80018065A20131013D72D641A5", "Resident Identity Card (eID)" }, { "3B8B005275746F6B656E6C745344E3", "Aktiv Rutoken Lite SD\nhttps://www.rutoken.ru/products/catalogue/info_52.html" }, @@ -1777,12 +1914,13 @@ const static atr_t AtrTable[] = { { "3B8B015275746F6B656E44534254D7", "Rutoken ECP Bluetooth (eID)\nhttp://www.rutoken.ru" }, { "3B8B015275746F6B656E6C697465C2", "Aktiv Rutoken Lite\nhttps://www.rutoken.ru/products/all/rutoken-lite/" }, { "3B8B80010012233F536549440F9000A0", "Estonian Identity Card (eID)\nhttp://id.ee/" }, + { "3B8B80010012428F536549440F900071", "Latvia eID card (eID)\nhttps://www.pmlp.gov.lv/en/identity-card-eid" }, { "3B8B80010031C0640804610000900062", "NUMBER26 Maestro Card (Bank)\nhttps://number26.eu\nAmerican Express credit card for corporations" }, { "3B8B80010031C0640844030400900044", "Credit Card Visa (Other)" }, { "3B8B80010031C0640844031200900052", "Tomorrow Debit Mastercard issued by solarisBank AG (Bank)\nhttps://www.tomorrow.one/" }, { "3B8B80010031C0640844031300900053", "Swedbank ISIC (eID)\nhttps://www.swedbank.ee/private/cards/debit/isic?language=EST" }, { "3B8B80010031C0640844036800900028", "Advanzia / Gebuhrenfrei.com Mastercard Gold (Bank)\nhttps://www.gebuhrenfrei.com/Home/" }, - { "3B8B80010031C0640844037600900036", "American Express Blue Card (Germany) (Bank)" }, + { "3B8B80010031C0640844037600900036", "American Express Blue Card (Germany) (Bank)\nMastercard issued by OTP Bank (Hungary) (Bank)\nhttps://www.otpbank.hu/portal/en/Retail/Bankcards\nCredit Agricole Bank Card - Bancomat / Maestro" }, { "3B8B80010031C06408440393009000D3", "Novacard pp0815-04/20 chip (Bank)" }, { "3B8B80010031C0641F18010000900009", "Student ID card in Poland (eID)" }, { "3B8B80010031C0641F27010000900036", "American Express Platinum (AU Issued) (Bank)" }, @@ -1792,10 +1930,15 @@ const static atr_t AtrTable[] = { { "3B8B80010031C1640840223000900054", "IDEMIA Cosmo v8.1-n (Other)" }, { "3B8B80010031C1640860320600900052", "Banco CTT (Portugal) contactless VISA Debit card (Bank) (Bank)\nhttps://www.bancoctt.pt/home/abrir-conta.html" }, { "3B8B80010031C1640860321200900046", "AMEX Silver Credit (Bank)" }, + { "3B8B80010031C1640860321F0090004B", "Hanseatic Bank Visa Card (Bank)\nhttps://www.hanseaticbank.de/kreditkarte/genialcard" }, { "3B8B80010031C1640860322000900074", "IDEMIA (Other)" }, - { "3B8B80010031C16408923354009000F3", "Italian healtcare card (TS) National Service Card (CNS) (HealthCare)\nhttp://www.salute.gov.it/portale/lea/dettaglioContenutiLea.jsp?lingua=italiano&id=4693&area=Lea&menu=leaEssn" }, + { "3B8B80010031C1640860324200900016", "Westpac Handybank EFTPOS/ATM Card - Contactless (Bank)" }, + { "3B8B80010031C16408923354009000F3", "Italian healthcare card (TS) National Service Card (CNS) (HealthCare)\nhttp://www.salute.gov.it/portale/lea/dettaglioContenutiLea.jsp?lingua=italiano&id=4693&area=Lea&menu=leaEssn" }, + { "3B8B80010031C16408986200009000FC", "ERSTE Bank creditcard (mastercard) (Bank)" }, { "3B8B80010031C1640911213000900007", "SmartMX (Other)" }, + { "3B8B80010031C1640924331E0090000E", "Cumulus Mastercard (Bank)\nhttps://www.migros.ch/cumulus/mastercard" }, { "3B8B80010031C1640937721300900051", "French ID Card 2021 (contactless interface) (eID)\nhttps://ants.gouv.fr/Les-titres/Carte-nationale-d-identite/La-puce-de-la-nouvelle-carte-nationale-d-identite" }, + { "3B8B80010031C1640964413600900014", "Monzo (Bank)\nhttps://monzo.com/" }, { "3B8B800100640411010131800090005A", "German Passport (issued Nov 2006)\nUnited Kingdom e-Passport\nLuxembourg passport (2007)" }, { "3B8B80010B7880820244492030324D1B", "Mastercard Debit issued by Raiffeisen bank in Czech Republic" }, { "3B8B80012085008B030FE09AA0E04052", "Shanghai Public Transportation Card (Transport)\nhttp://www.sptcc.com/" }, @@ -1809,21 +1952,29 @@ const static atr_t AtrTable[] = { { "3B8B8001654B5450304432654B54504C", "NXP smart eID - Indonesia ektp (eID)" }, { "3B8B80018031C06305107F8300900061", "UAB Bank - Prepaid VISA Card (Bank)\nhttps://www.uab.com.mm/cards/" }, { "3B8B80018066475000B8007F8290002E", "Italian Card Identity (eID)\nhttps://www.cartaidentita.interno.gov.it/" }, + { "3B8B80018066475000B80094829000C5", "Italian Electronic Contactless Identity Card v. 3.0 (CIE 3.0) (eID)\nhttps://www.cartaidentita.interno.gov.it/" }, { "3B8B800180F9A00000030800001000C8", "Probably the same as 'JCOP3 SecID P60 CS (JavaCard)' but mated with a contactless Identiv reader (JavaCard)" }, { "3B8B80018688FF6F391E743C200800D3", "Chinese ICBC (bank)" }, { "3B8B81314034534D41525453434F5045316D", "Zeeland kaart (Telecommunication)" }, { "3B8C014D79536D6172744C6F676F6EA5", "EIDVirtual (USB key emulated as a virtual smart card) (PKI)\nhttp://www.mysmartlogon.com/eidvirtual/" }, + { "3B8C01805A4E6974726F6B657920337D", "'Nitrokey 3C NFC' USB authentication and security token (Other)\nhttps://shop.nitrokey.com/shop/product/nk3cn-nitrokey-3c-nfc-148" }, { "3B8C40FA808105520101A00000015001", "Algerian Postal Services Current Account Card (Bank)" }, { "3B8C80010443FD....................", "RFID - NFC Forum tag type 3 (FeliCa)" }, + { "3B8C80010C75778002C1052F2F0035C7B7", "Mifare Plus S (Other)\nhttps://www.nxp.com/products/no-longer-manufactured/mifare-plus-s-2k-4k:MIFARE_PLUS_S_2K_4K" }, { "3B8C80014F5449442894B3C00100900045", "Belgian passport (2009-2013)" }, { "3B8C80014F5449442894F7C00000900000", "French passport (2010-2013)" }, { "3B8C800150........E1F35E117781E1..", "Spanish eID, Documento Nacional de Identidad (DNIe) (eID)\nhttp://www.dnielectronico.es/" }, + { "3B8C800150000000000000000080814519", "a12 (Telecommunication)" }, { "3B8C80015000000000000000009181A0ED", "Belimo VAV-Compact NFC (Other)\nhttp://www.belimo.ch/pdf/e/flyer_nfc_en.pdf" }, { "3B8C8001500005022D0206000000819163", "Local Transport card for Trentino region (Italy) trentino trasporti esercizio (Transport)\nhttp://www.ttesercizio.it/" }, + { "3B8C80015000CA464D00000000808171EC", "Visa Classic (Bank)" }, + { "3B8C8001500197E9BE00000000808171EC", "VISA card from bank LA BANQUE POSTALE (Bank)" }, { "3B8C80015001A5CE19000000008081715E", "VISA Card from Bank BANQUE POPULAIRE (Bank)" }, { "3B8C80015001BA137E00000000B3717138", "Mobib (Brussels public transportation card)" }, + { "3B8C80015001C62E3900000000808171FD", "edenred (Loyalty)" }, { "3B8C80015003A129CF0000000011818108", "rav-kav, Israel transport card (Transport)\nhttp://www.egged.co.il/Article-786-Rav-Kav-Card.aspx" }, { "3B8C80015004463EC3E1F35E117781A1E8", "Spanish ID (eID)" }, + { "3B8C8001500509CE35000000003371A149", "Libertan transport card from Nantes (France), based on Calypso (Transport)\nhttps://www.tan.fr/fr/abonnements-libertan-1" }, { "3B8C80015005A966960000000080817171", "Hello bank! VISA CLASSIC Card (Bank)\nhttp://www.hellobank.fr/" }, { "3B8C80015006DCA8590000000080817106", "VISA Hello bank! (NFC) (Bank)\nhttps://www.hellobank.fr/" }, { "3B8C800150107331B400000000B3717108", "MiMuovo public transport card used in Italian region Emilia Romagna (Transport)" }, @@ -1831,6 +1982,7 @@ const static atr_t AtrTable[] = { { "3B8C80015010B9F98900000000B3717137", "Toulouse (France) city public transport card called TISSEO Pastel (Transport)\nhttps://www.tisseo.fr/les-tarifs/obtenir-une-carte-pastel" }, { "3B8C80015010D632A000000000B37171BA", "MOBIB (STIB) (Transport)\nhttps://www.stib-mivb.be/article.html?_guid=30af0085-2483-3410-5394-a71daf08acd1&l=en" }, { "3B8C800150186188CEE1F35E117781C70E", "French passport (2007-2008)" }, + { "3B8C800150191B6CC20000000000817707", "Rabobank bankcard (dutch) (Bank)\nhttps://www.rabobank.nl/particulieren/betalen/betaalpas" }, { "3B8C80015019E3BB3D00000000008177D7", "ING/Maestro bank card (Bank)\nhttps://www.ing.nl/particulier/betalen/passen/index.html" }, { "3B8C80015024AD64E10000000033819172", "Italian healthcare + public administration card (HealthCare)\nhttp://sistemats1.sanita.finanze.it/wps/portal" }, { "3B8C800150260DBB150000000000718128", "Navigo Card (Transport)\nhttp://www.navigo.fr/" }, @@ -1839,13 +1991,17 @@ const static atr_t AtrTable[] = { { "3B8C8001502724266400000000007181EC", "French Transport Card (Navigo Card) (Transport)\nhttps://fr.wikipedia.org/wiki/Passe_Navigo" }, { "3B8C8001502747D205000000000071811A", "Pass Navigo Decouverte (Transport)\nhttps://fr.wikipedia.org/wiki/Passe_Navigo" }, { "3B8C800150275038CE000000000071812C", "Paris transportation card (Pass Navigo) (Transport)\nhttps://fr.wikipedia.org/wiki/Passe_Navigo" }, + { "3B8C8001502752318100000000007181", "Calypso (Transport)\nhttps://www.innovatron.fr/CalypsoFuncSpecification.pdf" }, { "3B8C80015027B1F76B0000000000717157", "Navigo (public transportation in Ile-de-France), IDF Mobilites (Transport)\nhttps://www.iledefrance-mobilites.fr/l-innovation/navigo/" }, { "3B8C8001502C8D214B23180100778197CD", "Indonesian Driver License" }, + { "3B8C8001502FE0AFA1000000000081776A", "Rabo Wereldpas (Maestro) (Bank)\nhttps://www.rabobank.nl/" }, { "3B8C80015035A45B650000000080817182", "Trenitalia cartafreccia (Transport)\nhttp://www.trenitalia.com/tcom/Cartafreccia" }, { "3B8C800150370B16BD310106017781777C", "Driver License of Indonesia - SIM (Surat Izin Mengemudi) (Transport)" }, { "3B8C800150380EB25B00000000B3717131", "BIP (Biglietto Integrato Piemonte) Card, GTT (Gruppo Trasporti Torinese) (Transport)\nhttp://bip.piemonte.it/" }, { "3B8C8001503A88EC29E1F35E117781E160", "DNI electronico (Spanish electronic ID card) (eID)\nhttp://www.dnielectronico.es" }, { "3B8C8001503A964B0000000000008180BB", "Ventra Transit Pass (Transport)\nhttps://www.ventrachicago.com/how-to/ventra-cards/" }, + { "3B8C8001503BF0330B30ABAB017781B7DE", "Italian health card - national service card (HealthCare)\nhttps://sistemats1.sanita.finanze.it/portale/tessera-sanitaria" }, + { "3B8C800150438EAE7B30ABAB017781B735", "Italian healthcare card (TS) National Service Card (CNS) (HealthCare) - Regione Liguria (HealthCare)" }, { "3B8C80015043E0F5F43101060077817748", "Indonesian Driver License" }, { "3B8C8001504553FFC6E1F35E117781A178", "Electronic DNI (eID)" }, { "3B8C80015046E570EC2318010077819739", "Indonesian Driver License" }, @@ -1853,9 +2009,11 @@ const static atr_t AtrTable[] = { { "3B8C8001504B75AF41E1F35E117781A583", "USA PASSPORT BOOK ICAO (passport)\nhttps://www.icao.int/publications/pages/publication.aspx?docnum=9303" }, { "3B8C8001505381573630AAAA017781D7FE", "Indonesian ektp (eID)" }, { "3B8C8001505467137900000000F781C1B3", "Tessera Sanitaria italian health care card (HealthCare)" }, + { "3B8C8001505C4B71A152745343778183D9", "RUTOKEN ECP 3.0 (PKI)\nhttps://www.rutoken.ru/products/catalogue/id_114.html" }, { "3B8C8001505CF5A94530AAAA017781D708", "Indonesia ektp (eID)" }, { "3B8C800150605F2EFA00000000778191D1", "Residence Permit (Switzerland) (eID)" }, { "3B8C80015064E65B000000000000818085", "Chicago CTA Ventra Transit card\nhttps://www.ventrachicago.com/" }, + { "3B8C80015068DDA5B11C00001177818582", "EZ-Link Card (issued by Land Transport Authority Singapore) (Transport)\nhttps://www.ezlink.com.sg/ez-link-faqs/ez-link-card/" }, { "3B8C800150710CF3C800000000B37171A8", "MOBIB CARD BELGIUM (Transport)" }, { "3B8C800150773B2DBD0000001100818594", "Texas Instruments Dynamic NFC Interface Transponder (RF430CL330H)" }, { "3B8C800150784B2CCB00000000B371713A", "... (Transport)\nhttps://www.portalviva.pt/" }, @@ -1863,9 +2021,12 @@ const static atr_t AtrTable[] = { { "3B8C8001507919600100DDA611F7718535", "Transport Traway Montpellier France (Transport)" }, { "3B8C8001507AA44007231801007781979F", "Indonesian Driver License" }, { "3B8C8001507E0071D20000000000717180", "Unica (= Just one) joined train and public transport card for Italian region Emilia Romagna (evolution of MiMuovo) (Transport)" }, + { "3B8C8001507E0A66C2000000000071718D", "'UNICA' card for the subscription to trains and buses of the Trenitalia TPER company (Transport)\nhttps://www.trenitalia.com/it/treni_regionali/smart-card-unica.html" }, + { "3B8C8001507EDE1E0600883C1F7781952D", "Bahrain CPR (eID)" }, { "3B8C8001507F9C668A0000000000818754", "NFC Mastercard issued by CSOB bank Czech Republic, first 4 digits 5168" }, { "3B8C80015089DCA96E00000000808171BF", "Cartafreccia VISA (Italian Railways Prepaid + Loyalty Card) (Bank)" }, { "3B8C800150915D7129E1F35E117781A1C3", "Spanish eID, Documento Nacional de Identidad (DNIe) (eID)\nhttp://www.dnielectronico.es/" }, + { "3B8C80015092BF3BC0000000000071718B", "Venice Public Transport Card (Transport)\nhttps://www.veneziaunica.it/" }, { "3B8C80015093195E3000000000007171B9", "OPUS STM CANADA (Transport)\nhttp://stm.info/" }, { "3B8C800150933CCC05000000000071713B", "OPUS Card, public transport (subway) of Montreal (Transport)\nhttp://www.stm.info/fr/infos/titres-et-tarifs/carte-opus-et-autres-supports/carte-opus" }, { "3B8C800150A221FAA031CCCC017781C183", "Morpho (eID)\nhttp://www.morpho.com/" }, @@ -1878,6 +2039,7 @@ const static atr_t AtrTable[] = { { "3B8C800150C0E450CA0000000091717172", "Transportation card for Lille (France) (Transport)" }, { "3B8C800150C0EA9CBB00000000B37171E3", "Tisseo (Toulouse) (Transport)\nhttp://www.tisseo.fr/les-tarifs/obtenir-une-carte-pastel" }, { "3B8C800150C0F1DE700000000091717153", "isodep, NfcB (Transport)" }, + { "3B8C800150C10D7D5E00000000B3717101", "Pastel transport card from Toulouse (France), based on Calypso (Transport)\nhttps://www.tisseo.fr/les-tarifs/ou-acheter/la-carte-pastel" }, { "3B8C800150C110BBA9000000009171710F", "Divia card (passport)" }, { "3B8C800150C11BA3F10000000091717144", "Tramway (PKI)" }, { "3B8C800150C14164E3000000000071715A", "Navigo Annuel (France) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/detail/forfait-navigo-annuel" }, @@ -1886,6 +2048,9 @@ const static atr_t AtrTable[] = { { "3B8C800150C17B55BA0000000000717108", "Navigo Easy (Paris public transportation) (Transport)\nhttps://parisbytrain.com/navigo-easy/" }, { "3B8C800150C17DA8CA0000000000717183", "Navigo Easy (France) transport card (Transport)\nhttps://www.ratp.fr/titres-et-tarifs/passe-navigo-easy" }, { "3B8C800150C2B936860000000000717196", "Passe Navigo (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-decouverte" }, + { "3B8C800150C326AF0C000000000071711B", "Navigo Mobility Paris transport's Card (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/liste?d=forfaits" }, + { "3B8C800150C41466C30000000000717128", "Navigo easy (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-easy" }, + { "3B8C800150C51770F0000000008081717F", "SumUp Limited Apple Pay Virtual Card (Bank)\nhttps://sumup.co.uk" }, { "3B8C800150C7251C5A00000011F781811F", "MOBIB Belgian public transport Card (Transport)\nhttps://mobib.be/fr.html" }, { "3B8C800150CA24513E00000011F781813A", "belgian mobib transportation card (Transport)" }, { "3B8C800150CBFB077E000000008081E1F4", "Samsung Digital Center in Seul Access ID card for guests (eID)" }, @@ -1915,6 +2080,10 @@ const static atr_t AtrTable[] = { { "3B8C80019067464A0100360600000000C7", "A40CR (eID)" }, { "3B8D0180FBA000000397425446590401", "Microsoft Virtual Smart Card 0 (PKI)" }, { "3B8D0180FBA000000397425446590401CF", "Microsoft Virtual Smartcard Provider shared through VMWare Workstation. (Other)\nhttps://technet.microsoft.com/en-us/library/dn593708%28v=ws.11%29.aspx" }, + { "3B8D80010004D0842186640000000000009F", "ABC UnionPay card with QuickPass contactless interface (Bank)" }, + { "3B8D8001003180718E6452D90400829000BB", "AwesomeCard (Bank)" }, + { "3B8D80010031C173C8400052A51000900070", "NXP P71 SmartMX3 (JavaCard)\nhttps://www.nxp.com/products/security-and-authentication/security-controllers/smartmx3-p71d321-secure-and-flexible-microcontroller:SMARTMX3-P71D321" }, + { "3B8D80010031C173C8400052A5100090FF8F", "NXP Smart MX3 P71D321 (JavaCard)" }, { "3B8D80010073C80013644737423700900055", "TD Canada Trust Visa" }, { "3B8D80010073C8001364543741380090004A", "Visa Debit (ING-DiBa) (Bank)\nhttps://www.ing-diba.de/\nVisa Debit Card with NFC payment function (Consorsbank)\nhttps://www.consorsbank.de/" }, { "3B8D80010073C80013645437423700900046", "pay (Bank)" }, @@ -1940,6 +2109,7 @@ const static atr_t AtrTable[] = { { "3B8E8001005131631F5901739F20C0C0900017", "Identity card (eID) Republic of Latvia (eID)\nhttp://www.pmlp.gov.lv/en/home/services/personal-certificates-%28eid%29/\nFrench driving license (eID) (contactless)" }, { "3B8E80010E7833C4020064041501020090FF95", "Spanish Passport" }, { "3B8E8001103877A78091E165D0004200008272", "Czech Republic e-Passport (issued Feb 2009)" }, + { "3B8E8001107833D4020064041101013180FFBD", "Second generation German passport (passport)\nhttps://www.cs.bham.ac.uk/~tpc/Papers/PassportTrace.pdf" }, { "3B8E80011178B3C0024F5449442894B3FFFF0E", "Civilian passport NFC tag (passport)" }, { "3B8E8001137880720280318066B1840C01FF04", "Optelio Contactless Gemalto (Bank)" }, { "3B8E8001137880800246494F4D4B5F3030314E", "MasterCard/PayPass Card issued by Czech FIO Banka a.s. (contactless chip)\nnote the ASCII string 'FIOK_001N' embedded in ATR" }, @@ -1953,7 +2123,9 @@ const static atr_t AtrTable[] = { { "3B8E800180318066B08412016E018300900003", "Contactless Barclaycard Visa\nMyCiti Transport Card MasterCard PayPass (Cape Town, South Africa)" }, { "3B8E800180318066B08416016E018300900007", "Barclaycard Platinum VISA (Bank)" }, { "3B8E800180318066B1840C016E0183009000..", "Gemalto Santander Optelio TUI R7 using Contactless interface" }, - { "3B8E800180318066B1840C016E01830090001C", "UK Lloyds Bank Gold Visa Debit (Contact & Contactless)\nbPay by barclaycard - contactless pre-paid Visa\nBPP Digital pre-paid VISA card (Bank)\nhttps://bpp.com.br/" }, + { "3B8E800180318066B1840C016E01830090001C", "UK Lloyds Bank Gold Visa Debit (Contact & Contactless)\nbPay by barclaycard - contactless pre-paid Visa\nBPP Digital pre-paid VISA card (Bank)\nhttps://bpp.com.br/\nMastercard credit card for Standard Chartered Bank, Pakistan also Mastercard debit card for JS Bank, Pakistan (Bank)" }, + { "3B8E800180318066B1840C016E01830090001C021450", "all transport travel pass (Transport)\nhttp://www.metro.spb.ru/cnblt41.html" }, + { "3B8E800180318066B1C5240100ED83009000F7", "Mastercard (Bank)" }, { "3B8E80018031815448534D3173802140810718", "Smartcard-HSM (Contactless Interface) (PKI)\nhttp://www.cardcontact.de/products/sc-hsm.html" }, { "3B8E80018031815448534D3173802140FFFF9E", "SmartCard-HSM RFID (PKI)\nhttps://www.smartcard-hsm.com/" }, { "3B8E800180919131C06477E30300838290001C", "Belgian Passport (2005)\nThai Passport 2005" }, @@ -1963,19 +2135,35 @@ const static atr_t AtrTable[] = { { "3B8F01415642000000000000000001000000DA", "AVEST-SYSTEMS AvBign (PKI)" }, { "3B8F0145464B000000000000000004000000C2", "EfitKey (PKI)" }, { "3B8F018025A00000005657444B3430300600B7", "SafeNet IKey4000" }, + { "3B8F01805D4E6974726F6B657900000000006A", "Nitrokey Nitrokey 3 Mini (eID)\nhttps://shop.nitrokey.com/shop/product/nk3am-nitrokey-3a-mini-149" }, { "3B8F80010000000000000000000000000000000E", "Tangem card (Other)\nhttps://tangem.com/" }, { "3B8F80010000000000000000000000FF829000E3", "CIE (eID)" }, { "3B8F80010031B86404B0ECC1739401808290000E", "Contactless CPS v3 Card (Carte de Professionnel de Sante)" }, + { "3B8F80010031B86404B0ECC173940180FFFFFFE3", "Card used in public French hospital (Other)" }, + { "3B8F80010031B96409377213738401E0000000AB", "National Identity Card of Slovakia (NFC interface) (eID)\nhttps://en.wikipedia.org/wiki/Slovak_identity_card" }, { "3B8F80010031C173C800106457494943009000B5", "ICC Solutions Card for Certification (Other)\nhttps://www.iccsolutions.com/" }, + { "3B8F80010031C173C8211064414D31300790008A", "master card (Bank)" }, + { "3B8F80010031C173C8211064414D313307900089", "CheBanca! nexi debit card (Bank)\nhttps://www.chebanca.it/" }, + { "3B8F80010031C173C8211064414D31370790008D", "AirPlus International Mastercard (Bank)\nhttps://www.airplus.com/" }, { "3B8F80010031C173C8211064414D333007900088", "ING-VISA-Card (Bank)\nhttps://www.ing.de/girokonto/karten-bargeld/" }, + { "3B8F80010031C173C8211064414D333107900089", "NAB VISA Debit (Bank)\nhttps://www.nab.com.au/" }, + { "3B8F80010031C173C8211064414D3341079000F9", "Ukrainian International resere bank (ex Sberbak RF) debit card (Bank)\nhttps://www.sbrf.com.ua/" }, + { "3B8F80010031C173C8211064474D34350090008B", "S-Etukortti Visa (Bank)\nhttps://www.s-pankki.fi/fi/s-etukortti-visa" }, { "3B8F80010031C173C8211064474D343700900089", "SpareBank Visa Card, Norway (Bank)" }, + { "3B8F80010031C173C8211064574B31330090009E", "TIM Pay - HYPE (Mastercard debit card) (Bank)\nhttps://www.tim.it/fisso-e-mobile/mobile/servizi/tim-pay" }, { "3B8F80010031C173C8211064574B313400900099", "G&D Sm@rtCafe Card embedded in a wristband (Bank)\nhttps://www.gi-de.com/en/au/mobile-security/industries/financial-institutions/wearables/" }, { "3B8F800100664653051000FF71DF000000000039", "JavaCOS A40 dual interface Java card - 64K (JavaCard)" }, { "3B8F800100B85421000090000000000000000053", "netbank Germany, Mastercard (Bank)\nhttps://netbank.de" }, { "3B8F800100B85431000090000000000000000043", "Monzo MasterCard Contactless (Bank)\nhttps://monzo.com/" }, { "3B8F800100B854310000900000000000FFFFFFBC", "VISA Debit issued by WIREX. card made by CONTIS / TAG SYSTEMS (Bank)\nhttps://wirexapp.com/" }, + { "3B8F80013101F1564011001900000000000000D1", "Revolut Mastercard (Bank)" }, + { "3B8F80013101F1564011001900000000FFFFFF2E", "BVG Guthabenkarte (Prepaid Payment Card for Berlin/Brandenburg Public Transport) (Transport)\nhttps://www.bvg.de/de/service-und-kontakt/guthabenkarte" }, { "3B8F800141434F53204449616E6131204C63365B", "DKB-VISA-Card (Bank)\nhttps://produkte.dkb.de/?&page=girokonto#kreditkarte" }, { "3B8F800141434F53204449616E613120FFFFFFBD", "DKB Visa Credit Card (Bank)\nhttps://www.dkb.de" }, + { "3B8F800141434F53204449616E6132204C633658", "comdirect Visa debit (Bank)\nhttps://www.comdirect.de" }, + { "3B8F800141434F53204449616E613220FFFFFFBE", "comdirect, VISA Debit (Bank)\nhttps://www.comdirect.de/konto/karten.html#bankkarte" }, + { "3B8F800141434F532046696F6E6131204C633677", "DKB Visa (Debit) (Bank)\nhttps://www.dkb.de/" }, + { "3B8F800143433169A920000020202020202020FF", "Reka (Bank)\nhttps://reka.ch" }, { "3B8F800143443269AB41202020202020FFFFFF47", "BKB VISA Card Switzerland (Bank)\nhttps://www.bkb.ch/de/privatkunden/konten-und-karten/kreditkarten/world-mastercard-silber-oder-visa-classic/factsheet" }, { "3B8F800143553269AA20202020202020202020E9", "UBS Access Card (Mobile Online Banking, NFC, Switzerland)" }, { "3B8F800145504100000000........00........", "Austrian Quick E-purse contactless\nhttp://www.quick.at/" }, @@ -1984,6 +2172,8 @@ const static atr_t AtrTable[] = { { "3B8F800152464944494F74204A434F5020373276", "RFIDIOt JCOP 72K Blank\nhttp://rfidiot.org" }, { "3B8F800152464944494F74204A434F5037327224", "RFIDIOt JCOP 72K RANDOM_UID Blank\nhttp://rfidiot.org" }, { "3B8F800156696E5061795379732050757273652F", "JCOP (Other)" }, + { "3B8F80018031806549544A3442120FFF829000", "Italian Identity Card CIE (eID) (eID)\nhttps://www.cartaidentita.interno.gov.it/en/home/" }, + { "3B8F80018031806549544A3442120FFF8290008B", "Identity Card (eID)" }, { "3B8F800180318065B0........120FFE829000..", "IDPrime MD 3810 T=Contactless (Prox DU)" }, { "3B8F800180318065B0842327E5120FFE8290007E", "Gemalto IDPrime MD 3810 Blank Card (Other)\nhttp://www.gemalto.com/products/IDPrime_MD/index.html" }, { "3B8F800180318065B0846160FB120FFD82900066", "IDPrime 3930 FIPS Level 2 (Contactless TypeA) (BAI5, BAI7) (PKI)" }, @@ -1991,8 +2181,13 @@ const static atr_t AtrTable[] = { { "3B8F800180318065B0850300EF120FFF82900073", "Grand Duchy of Luxembourg / Identity card with LuxTrust certificate (eID)\nhttp://www.guichet.public.lu/citoyens/fr/citoyennete/papiers-identite/carte-identite/nouv-carte-identite-adulte-EN/index.html" }, { "3B8F800180318065B085040011120FFF8290008A", "Dutch driver licence (eID)\nItalian electronic identity card (eID)\nhttps://en.wikipedia.org/wiki/Italian_electronic_identity_card" }, { "3B8F80018031B865B0850300EF1200F68290004D", "Finnish electronic identity card (eID)\nhttps://vrk.fi/en/electronic-identity-and-certificates" }, + { "3B8F80018031B865B08504021B1200F6829000BC", "Finnish identity card (eID)" }, + { "3B8F80018031D865B08505001112752082900076", "Swedish national ID card (eID)\nhttps://polisen.se/en/services-and-permits/passport-and-national-id-card/check-validity-online-for-passports-and-national-id-cards/" }, { "3B8F80018031E06B042105027255555555555564", "Banamex/Citi Bank Bsmart MasterCard Paypass (Bank)\nhttps://www.banamex.com/es/personas/tarjetas_credito/ver_tarjetas/puntos_efectivo/bsmart.htm" }, { "3B8F80018031E06B04310502D1555555........", "Revolut Prepaid Mastercard (Contactless) (Bank)\nhttps://revolut.com/" }, + { "3B8F80018031E06B04520502FD555555FFFFFF32", "C6 Bank Card (Bank)" }, + { "3B8F80018031E06B0453F5025B555555555555CF", "Mastercard debit card for Askari Bank, Pakistan (Bank)" }, + { "3B8F80018031E06B04546B026D55555555555560", "Mastercard Credit/Debit Card - Contactless (Bank)" }, { "3B8F80018031E06B061605028C555555555555AF", "Air Miles American Express card (contactless) (Bank)" }, { "3B8F8001804F0CA000000306..000000000000..", "Card name not given (as per PCSC std part3)" }, { "3B8F8001804F0CA000000306..000100000000..", "MIFARE Classic 1K (as per PCSC std part3)" }, @@ -2060,10 +2255,10 @@ const static atr_t AtrTable[] = { { "3B8F8001804F0CA00000030601....00000000..", "RFID - ISO 14443 Type A Part 1 (as per PCSC std part3)" }, { "3B8F8001804F0CA00000030602....00000000..", "RFID - ISO 14443 Type A Part 2 (as per PCSC std part3)" }, { "3B8F8001804F0CA00000030603....00000000..", "RFID - ISO 14443 Type A Part 3 (as per PCSC std part3)" }, - { "3B8F8001804F0CA000000306030000000000006B", "buss/train pass for use with Skanetrafiken (www.skanetrafiken.se) busses and trains.\npublic library of Dusseldorf\nhttp://www.duesseldorf.de/stadtbuechereien/\nspecialized Mifare Ultralight card" }, - { "3B8F8001804F0CA000000306030001000000006A", "NXP/Philips MIFARE Classic 1K (as per PCSC std part3)\nhttp://www.nxp.com/#/pip/pip=[pfp=41863]|pp=[t=pfp,i=41863]\nOyster card - Transport for London (first-gen)\nhttps://en.wikipedia.org/wiki/Oyster_card\nACOS5/1k Mirfare\nvivotech ViVOcard Contactless Test Card\nBangkok BTS Sky SmartPass\nMifare Classic 1K (block 0 re-writeable)\nElectic vehicle charging card of the German Telekom, acting as EMSP GetCharge\nElectic vehicle charging card of the EMSP Stadtwerke Muenchen (SWM), ladenetz.de, Germany\nElectic vehicle charging card of the EMSP EinfachStromLaden of Maingau-Energie, Germany\nScouter carsharing customer card in Germany\nhttps://scouter.de/" }, - { "3B8F8001804F0CA0000003060300020000000069", "RFID - ISO 14443 Type A - NXP Mifare card with 4k EEPROM" }, - { "3B8F8001804F0CA0000003060300030000000068", "RFID - ISO 14443 Type A - NXP Mifare Ultralight or UltralightC" }, + { "3B8F8001804F0CA000000306030000000000006B", "bus/train pass for use with Skanetrafiken (www.skanetrafiken.se) buses and trains.\npublic library of Dusseldorf\nhttp://www.duesseldorf.de/stadtbuechereien/\nspecialized Mifare Ultralight card" }, + { "3B8F8001804F0CA000000306030001000000006A", "NXP/Philips MIFARE Classic 1K (as per PCSC std part3)\nhttp://www.nxp.com/#/pip/pip=[pfp=41863]|pp=[t=pfp,i=41863]\nOyster card - Transport for London (first-gen)\nhttps://en.wikipedia.org/wiki/Oyster_card\nACOS5/1k Mirfare\nvivotech ViVOcard Contactless Test Card\nBangkok BTS Sky SmartPass\nMifare Classic 1K (block 0 re-writeable)\nElectric vehicle charging card of the German Telekom, acting as EMSP GetCharge\nElectric vehicle charging card of the EMSP Stadtwerke Muenchen (SWM), ladenetz.de, Germany\nElectric vehicle charging card of the EMSP EinfachStromLaden of Maingau-Energie, Germany\nScouter carsharing customer card in Germany\nhttps://scouter.de/\nDKV Euro Service +charge (Transport)\nhttps://www.dkv-mobility.com/en/fuelling/fuel-card/dkv-card-charge/\nAndante (Transport)\nKazakhstan 'Onay' transport card (Transport)\nhttps://onay.kz" }, + { "3B8F8001804F0CA0000003060300020000000069", "RFID - ISO 14443 Type A - NXP Mifare card with 4k EEPROM\nOV Chipkaart\nhttps://www.ov-chipkaart.nl/home.htm" }, + { "3B8F8001804F0CA0000003060300030000000068", "RFID - ISO 14443 Type A - NXP Mifare Ultralight or UltralightC\nTempmate S1 Data Logger (Other)\nhttps://www.tempmate.com/\nprepaid bus card (Transport)\nhttps://www.t-l.ch/abos-billets/billets/carte-prepayee\nGamestate rechargable play card (Other)\nhttps://shop.gamestate.com/" }, { "3B8F8001804F0CA0000003060300FF0000000094", "ACTV (Italy) prepaid transport ticket\n'NFC Tag' -- Sony's 'Smart Tags'" }, { "3B8F8001804F0CA00000030603F004000000009F", "NFC FORUM TYPE 1 TAG\nwww.inovision-group.com/topaz\nISO/IEC 14443A - 96 Bytes read/write NFC/RFID IC mandated by NFC Forum as the Type 1 NFC Forum Tag Format." }, { "3B8F8001804F0CA00000030603F011000000008A", "Bangkok Metro (MRT)\nHTC One X Android phone (European edition 'endaevoru')" }, @@ -2074,8 +2269,10 @@ const static atr_t AtrTable[] = { { "3B8F8001804F0CA00000030607....00000000..", "RFID - ISO 14443 Type B Part 3 (as per PCSC std part3)" }, { "3B8F8001804F0CA000000306070000000000006F", "Atmel AT88RF04C CryptoRF" }, { "3B8F8001804F0CA000000306074344600201E4EF", "'Andante' Card, Porto (Portugal) metro card. (ISO14443 B CTS/CTM512B) (Transport)\nhttp://www.transportespublicos.pt/en/glossary/andante-ticket-system/" }, + { "3B8F8001804F0CA00000030607FFB00000000020", "Chinese ID Card (eID)\nhttps://en.wikipedia.org/wiki/Resident_Identity_Card" }, { "3B8F8001804F0CA00000030609....00000000..", "RFID - ISO 15693 Part 1 (as per PCSC std part3)" }, { "3B8F8001804F0CA0000003060A....00000000..", "RFID - ISO 15693 Part 2 (as per PCSC std part3)" }, + { "3B8F8001804F0CA0000003060A0018000000007A", "HID ICLASS DL (eID)" }, { "3B8F8001804F0CA0000003060A001C000000007E", "RFID - HID iCLASS 16K CL" }, { "3B8F8001804F0CA0000003060B....00000000..", "RFID - ISO 15693 Part 3 (as per PCSC std part3)" }, { "3B8F8001804F0CA0000003060B00000000000063", "RFID - ISO 15693 - EM Microelectronic-Marin SA" }, @@ -2088,7 +2285,7 @@ const static atr_t AtrTable[] = { { "3B8F8001804F0CA0000003060E....00000000..", "Contact (7816-10) Extended I2C (as per PCSC std part3)" }, { "3B8F8001804F0CA0000003060F....00000000..", "Contact (7816-10) 2WBP (as per PCSC std part3)" }, { "3B8F8001804F0CA00000030610....00000000..", "RFID - FeliCa compatible (as per PCSC std part3)\nContact (7816-10) 3WBP (as per PCSC std part3)" }, - { "3B8F8001804F0CA00000030611003B0000000042", "RFID - FeliCa (generic) (as per PCSC std part3)\nSuica public transit card (Japan IC system)\n(also: Hayakaken, ICOCA, Kitaca, manaca, nimoca, PASMO, PiTaPa, SUGOCA, TOICA)\nhttps://en.wikipedia.org/wiki/Suica\nOctopus, MTR network from Hong Kong, 2014" }, + { "3B8F8001804F0CA00000030611003B0000000042", "RFID - FeliCa (generic) (as per PCSC std part3)\nSuica public transit card (Japan IC system)\n(also: Hayakaken, ICOCA, Kitaca, manaca, nimoca, PASMO, PiTaPa, SUGOCA, TOICA)\nhttps://en.wikipedia.org/wiki/Suica\nOctopus, MTR network from Hong Kong, 2014\ne-Amusement card (Other)\nhttps://en.wikipedia.org/wiki/E-Amusement" }, { "3B8F8001804F0CA00000030640....00000000..", "RFID - Low Frequency < 135 kHz (as per PCSC std part3)" }, { "3B8F8001804F0CA0000003064000000000000028", "HID Proximity. Used to access buildings. Reference on the card 'HID0008P'.\nhttp://www.hidglobal.com/product-display/cards-and-credentials/hid-proximity" }, { "3B8F8001804F0CA0001A0000000078", "iclass 16k cl (eID)" }, @@ -2098,6 +2295,7 @@ const static atr_t AtrTable[] = { { "3B8F8001805A08030400020024E279788290000C", "Paris Navigo card 14443 B' (Innovatron - Calypso)" }, { "3B8F80018066B007010107............9000..", "Gemalto Santander Optelio TUI R7 with WG10 customized using Contactless interface" }, { "3B8F80018073CC91CBF9A0000003080000100029", "NIST demo PIV card (eID)" }, + { "3B8F8001809100318065B08405002583019000CD", "Contactless IDCore3230 build 6.8, test APDU applet (JavaCard)" }, { "3B8F80018091E1318065B0831100AC83009000B7", "TOP DM GX4 Cards [TOP DM GX4 - contact interface]\nGemCombiXpresso R4 72K (contactless interface)" }, { "3B8F80018091E1318065B0831111AC83009000A6", "TOP DM GX4 Cards [TOP DM GX4 on Omnikey]\nGEMALTO WM GX4 72 DHS TSA (contactless interface)" }, { "3B8F80018091E1318065B0831111E583009000EF", "TOP DL V2 Cards" }, @@ -2115,6 +2313,7 @@ const static atr_t AtrTable[] = { { "3B90958011FE6A", "JC30M48CR JavaCard (JavaCard)" }, { "3B9095801FC359", "Dai Nippon Printing Co., DNP Standard-J T3.1" }, { "3B90968111FE68", "Xirka (eID)" }, + { "3B90969181B1FE551FC7D4", "iClass SE Processor (Other)\nhttps://www.hidglobal.com/products/embedded-modules/iclass-se/sio-processor" }, { "3B9194801F0323BA", "China black(COS) SIM, does not support clock stop" }, { "3B931100003200", "01-3936b2013 (Pay TV)\nhttp://www.lripl.com" }, { "3B941881B1807D1F0319C80050DC", "GoldKey Security PIV Token" }, @@ -2140,19 +2339,22 @@ const static atr_t AtrTable[] = { { "3B959680B1FE551FC7477261636513", "IClass SE Processor (Other)\nhttps://www.hidglobal.com/products/embedded-modules/iclass-se/sio-processor" }, { "3B9596C0F01FC20F100A0A16", "viettel (Telecommunication)" }, { "3B959740F01A160A1941", "SG50 (Samsung Chip) (Telecommunication)" }, + { "3B96004121920000622433339000", "Ukrainian Telecommunications Operator Kyivstar (old simcard) (Telecommunication)\nhttps://kyivstar.ua/uk/mm" }, { "3B961880018051006110309F006110309E", "Atmel/Athena T0 PC/SC Compliance Test Card No. 1 (warm reset)" }, + { "3B9694801FC6D0019FCB1000CE", "jawwal (Telecommunication)" }, { "3B9695801FC3D007830254005D", "au IC-card ver.001 (for CDMA2000) (Telecommunication)\nhttps://ja.wikipedia.org/wiki/Au_IC%E3%82%AB%E3%83%BC%E3%83%89" }, { "3B97110002020422003F03", "China Unicom (Telecommunication)" }, { "3B9711801F418031A073BE2100A6", "Comprion UT3 simulator SIM (Other)" }, { "3B9711801F428031A073BE2100A6", "ETSI 102 230 Test case 5.2.5.3. Comprion IT3 SIM emulator (Telecommunication)" }, - { "3B9711C0FFB1FE351F83A505010102A3015F", "digital chronotachygraphe card: conducteur/driver, entreprise, controleur/controler et atelier/workshop, 2006-2010\nmanufactured by Imprimerie Nationale, distributed by Chronoservices" }, - { "3B9713C0FFB1FE351F83A505010102A3015D", "digital chronotachygraphe card: conducteur/driver, entreprise, controleur/controler et atelier/workshop, 2005-2006\nmanufactured by Imprimerie Nationale, distributed by Chronoservices" }, + { "3B9711C0FFB1FE351F83A505010102A3015F", "digital chronotachygraphe card: conducteur/driver, entreprise, controleur/controller et atelier/workshop, 2006-2010\nmanufactured by Imprimerie Nationale, distributed by Chronoservices" }, + { "3B9713C0FFB1FE351F83A505010102A3015D", "digital chronotachygraphe card: conducteur/driver, entreprise, controleur/controller et atelier/workshop, 2005-2006\nmanufactured by Imprimerie Nationale, distributed by Chronoservices" }, { "3B9794801F438031E073FE211B39", "Telenor SIM card (Hungary) (Telecommunication)\nhttps://www.telenor.hu" }, { "3B9794803F44908031A073BE210095", "Comprion Simulated card that indicate Low Impedance support: TB(3) = 0x90 (Telecommunication)" }, { "3B9795801F438031E073FE211B38", "JIO (Telecommunication)" }, { "3B9795801F478031E073FE211B3C", "iFree MOGO SIM Card for test (Telecommunication)" }, { "3B9795801FC78031A073B6211BB4", "DNP Local5G (Telecommunication)" }, - { "3B9795C02A31FE35D000480105A3113C", "digital chronotachygraphe card: conducteur/driver, entreprise, controleur/controler et atelier/workshop, 2010-2015\nmanufactured by Imprimerie Nationale, distributed by Chronoservices" }, + { "3B9795C02A31FE35D000480105A3113C", "digital chronotachygraphe card: conducteur/driver, entreprise, controleur/controller et atelier/workshop, 2010-2015\nmanufactured by Imprimerie Nationale, distributed by Chronoservices" }, + { "3B97978171FE24007743534D01020300", "Infineon CIPURSE SAM SLF 9630 (Other)" }, { "3B9811000205013242030500", "Sino 3G Nano-sim ordered off Alie Express (Telecommunication)\nhttp://goo.gl/rSEi4N" }, { "3B9813400AA503010101AD1311", "Belgium Electronic ID card (eID)" }, { "3B981801524D4D2076312E32A8", "Ericsson Mini-Link RYS 110 243/1 (Other)" }, @@ -2176,6 +2378,7 @@ const static atr_t AtrTable[] = { { "3B999400910891060001060600", "GSM-SIM Orange-UK (1800)" }, { "3B999400911691080001250100", "GSM SIM E-plus, Germany (Telecommunication)" }, { "3B999400919993120001160200", "ORGA test systems - GSM Phase 2+ Test SIM" }, + { "3B999500001506030105040100", "Skylink CDMA R-UIM card (Telecommunication)\nhttps://en.wikipedia.org/wiki/Sky_Link_(Russia)" }, { "3B9A180082627593110001020200", "Supersim 6 in 1 (Telecommunication)" }, { "3B9A18801FC731425243442E312E3538C0", "HP StorageWorks (Other)\nhttps://hpe.com" }, { "3B9A940091010017000123100096", "GSM-SIM Victorvox D1 (900MHz)" }, @@ -2183,6 +2386,7 @@ const static atr_t AtrTable[] = { { "3B9A940091010017000126050096", "GSM-SIM T-D1 prepaid (Xtra) (Telecommunication)" }, { "3B9A940091010017000126060096", "T D1 GSM card" }, { "3B9A9400920275931100010202..", "SuperSIM (X-sim)" }, + { "3B9A940092029093110001340200", "Orange Spain (Telecommunication)" }, { "3B9A960092013693170002040300", "GSM-SIM EMT 'Simpel' (prepaid, Estonia)\nhttps://www.emt.ee/web/simpel" }, { "3B9A960092014893170002120400", "SIM Card C*******r Mobile (Belgium)" }, { "3B9A960092016693170002120400", "GSM SIM Bite.lv prepaid 'Toxic'; 2008" }, @@ -2200,22 +2404,29 @@ const static atr_t AtrTable[] = { { "3B9C110068868D0C86980256408B0500", "China Mobile (Telecommunication)" }, { "3B9C131181647265616D6372797074000408", "XPlusTV & INXCT Access Card-9 (FIRECrypt)" }, { "3B9C131181647265616D6372797074900599", "FireCrypt, access card 9 (Pay TV)" }, + { "3B9C188121455A43332E333420524556204467", "MS-Protect safecard for access system (eID)" }, + { "3B9C188121755A43332E333420524556204457", "ZC3.34 (Other)\nhttps://www.zeitcontrol.de/Smart-card-BasicCard-Enhanced-ZC334-SIM-cut" }, { "3B9C940068868D0A86980256C2000500", "G3 & GSM & Blank SIM card: to be programmed for OpenBTS with pySim-prog (Telecommunication)" }, { "3B9C940068868D0C86980245A1000500", "China Mobile (Telecommunication)" }, { "3B9C940068868D0C86980256408B0500", "mobiledit (Telecommunication)" }, { "3B9C948011405275746F6B656E4543507363C3", "Aktiv Rutoken ECP SC T1\nhttps://www.rutoken.ru/products/all/rutoken-ecp-sc/" }, + { "3B9C94801F478031E073FE211B545015740152", "VI india (Telecommunication)" }, + { "3B9C94801F478031E073FE211B545015745003", "SIM Card (Telecommunication)" }, { "3B9C94801F478031E073FE211B5450845450B2", "cambodia (Telecommunication)\nhttps://www.smart.com.kh/" }, + { "3B9C94801F478031E073FE211B545085745291", "Airtel india (Telecommunication)" }, { "3B9C94801F478031E073FE211B54538974509C", "China Unicom 128K M2M Card (Telecommunication)" }, { "3B9C94801F478031E073FE211B545983795091", "Vodafone Ukraine (Telecommunication)" }, { "3B9C95801FC78031E073FE211B6457444946CF", "MTC (Moscow) phone SIM card (Telecommunication)\nhttps://moskva.mts.ru/personal" }, { "3B9C9580811F039067464A01005404F272FE00C0", "Feitian Technologies Java Card A22CR (JavaCard)\nhttps://www.javacardos.com/store/javacard-a22cr.php" }, { "3B9C9580811F039067464A01011706F2727E0000", "A40CR (JavaCard)" }, + { "3B9C958131FE9F9067464A010253050172FE00FB", "Feitian Biopass K27 (PKI)\nhttps://www.ftsafe.com/Products/FIDO/Bio" }, { "3B9C96005275746F6B656E4543507363", "Aktiv Rutoken ECP SC T0\nhttps://www.rutoken.ru/products/all/rutoken-ecp-sc/" }, { "3B9C960058442403020020010A009005", "Ticket Restaurant Card (Other)\nhttp://www.edenred.it/buoni-pasto-welfare-benefit/ticket-restaurant-card/" }, { "3B9C978011405275746F6B656E4543507363C0", "Aktiv Rutoken ECP 3.0 NFC (PKI)\nhttps://www.rutoken.ru/products/all/rutoken-ecp-nfc/" }, { "3B9D114023006810114D696F434F53009000", "MioCOS 1.0" }, { "3B9D13813160378031C0694D54434F537302020440", "DPI Card Guatemala (eID)\nhttp://www.masktech.de/" }, { "3B9D13813160378031C0694D54434F537302020541", "MTCOS (eID)\nhttp://www.masktech.com/Products/MTCOS-Professional/11/en" }, + { "3B9D188131FC358031C0694D54434F5373020505D3", "Lithuanian e-ID Card (eID)\nhttps://www.nsc.vrm.lt/default_en.htm" }, { "3B9D944023006820014D696F434F53009000", "Miotec smartcard running Miocos 2.0 on an Atmel AT90SC646\nhttp://www.miotec.fi" }, { "3B9D95801FC38031E0524B5462110373FE211B8F", "KT WiBro UICC (2.3 GHz mobile WiMAX in South Korea)" }, { "3B9D95801FC38031E073FE211B65D00057026230", "Gemalto NFC enabled (acquired through the Simagine contest)" }, @@ -2228,16 +2439,21 @@ const static atr_t AtrTable[] = { { "3B9D95801FC780731A211B63AF09A9830F9000F3", "Estonian GSM operator TELE2 (WPKI eID support)" }, { "3B9D95803FC7A08031A073BE21135105830590007C", "NTT docomo Xi(LTE) DN05(DNP) Pink SIM (Telecommunication)" }, { "3B9D960053492303030020000400F59000", "shell (Transport)" }, + { "3B9D96801FC78031E073FE2113654C0404020096", "China umion 5G SIM (Telecommunication)" }, + { "3B9D96813160378031C0694D54434F5373020204C5", "Mozambique ID Card (eID)" }, { "3B9E94801F4280310073BE211066545953044C25CF", "MCI (Mobile Communication Company of Iran) SIM" }, + { "3B9E94801F478031A073BE21106686880212204027", "americamobile (Telecommunication)" }, { "3B9E94801F478031A073BE21136686880210421014", "Airspan USIM (Telecommunication)" }, { "3B9E94801F478031A073BE21136686880210731025", "module (JavaCard)" }, { "3B9E94801F478031E073BE211366868882183942F5", "China Unicom 128K Mini USIM (Telecommunication)" }, + { "3B9E94801F478031E073FE21136686880212653213", "MTS Moscow (Russia) SIM card (Telecommunication)\nhttps://moskva.mts.ru/personal" }, { "3B9E94801FC38031E073FE211B66D00017B40000A5", "Vodafone Ireland SIM card" }, { "3B9E94801FC78031E073FE211366425454423031DB", "XLB.5G CTC Test Card (Telecommunication)\nhttps://xielibo.com" }, { "3B9E94801FC78031E073FE211B66D0006C025F0033", "Vivo Brasil SIM Card" }, { "3B9E94801FC78031E073FE211B66D0006C0634005C", "SIM card SFR 250 128ko" }, { "3B9E94801FC78031E073FE211B66D0007A00000078", "GaduAIR (Poland) - Subscriber Identity Module (SIM)" }, { "3B9E94801FC78031E073FE211B66D0016DE80C008A", "Mobile TeleSystems (MTS) GSM SIM\nhttp://www.mts.ru/" }, + { "3B9E94801FC78031E073FE211B66D001A16B0F00C6", "Proximus (Belgium) SIM, unactivated (Telecommunication)" }, { "3B9E94801FC78031E073FE211B66D001A18D0F0020", "StarHub Happy Prepaid SIM (Telecommunication)\nhttp://www.starhub.com/personal/mobile/mobile-phones-plans/prepaid-cards.html" }, { "3B9E94801FC78031E073FE211B66D00217F71300F3", "VIETTEL Telecom 4G SIM 128KB (Vietnam) (Telecommunication)\nhttp://vietteltelecom.vn/" }, { "3B9E94801FC78031E073FE211B66D002195C130056", "SIM Card of the French mobile carrier 'La Poste Mobile' (Telecommunication)" }, @@ -2273,6 +2489,7 @@ const static atr_t AtrTable[] = { { "3B9E95801FC78031E073FE211B66D001A0C3120073", "3G MTN IRANCELL Sim Card. (Telecommunication)" }, { "3B9E95801FC78031E073FE211B66D001A1141100A6", "Globe (Telecommunication)" }, { "3B9E95801FC78031E073FE211B66D001A1721000C1", "Vodafone Hungary SIM Card (Telecommunication)\nhttp://vodafone.hu" }, + { "3B9E95801FC78031E073FE211B66D001A17C0F00D0", "Cricket SIM card (Telecommunication)" }, { "3B9E95801FC78031E073FE211B66D001A18C12003D", "Telkomsel SIM card (Telecommunication)\nhttps://www.telkomsel.com/en/kartu-as" }, { "3B9E95801FC78031E073FE211B66D001A1B810000B", "abc mobile prepaid sim card (serviced by CSL) (Telecommunication)\nhttps://www.hkcsl.com/en/abc-Mobile-Prepaid-SIM-Cards/" }, { "3B9E95801FC78031E073FE211B66D0021702130007", "AT&T prepaid nano iphone/android sim card (Telecommunication)" }, @@ -2283,6 +2500,7 @@ const static atr_t AtrTable[] = { { "3B9E95801FC78031E073FE211B66D00217C71100C0", "3 UK SIM card (Telecommunication)" }, { "3B9E95801FC78031E073FE211B66D00217F61100F1", "Ziggo SmartCard Television Subscription (Pay TV)" }, { "3B9E95801FC78031E073FE211B66D00219151300", "ABS-CBN (Telecommunication)" }, + { "3B9E95801FC78031E073FE211B66D0022A861300BE", "KPN, Netherlands (Telecommunication)" }, { "3B9E95C00A1FC68031E073FE211B66D001830D58811E", "Mobile Paypass G199 NFC" }, { "3B9E96008031C0654D4700000072F7418107", "Russian Magistra (UTF8: Magistra) smart card (eID)\nhttp://www.smart-park.ru/index.php/products/smartcards.html" }, { "3B9E96008031C0654D5300000072F7418107", "Smart-key for bank-client of MDM Bank (RU) with faktura.ru service (Bank)" }, @@ -2299,6 +2517,7 @@ const static atr_t AtrTable[] = { { "3B9E96801FC68031E073FE211B66D0019F8F120002", "Russian 'Megafon' gsm sim card (Telecommunication)\nhttp://www.megafon.ru/" }, { "3B9E96801FC68031E073FE211B66D0019FBC100033", "sim beeline russia krasnodar (Telecommunication)" }, { "3B9E96801FC68031E073FE211B66D0019FEF100060", "Infineon SLU14MCO480K2 (JavaCard)" }, + { "3B9E96801FC68031E073FE211B66D0024010150046", "Kyivstar USIM-card (Telecommunication)" }, { "3B9E96801FC78031E073FE21136200498381900007", "Claro Brazil SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D000282401000D", "United Mobile SIM" }, { "3B9E96801FC78031E073FE211B66D0002B2D020004", "KTF SHOW GE-B1400 WCDMA USIM (Telecommunication)" }, @@ -2333,15 +2552,20 @@ const static atr_t AtrTable[] = { { "3B9E96801FC78031E073FE211B66D00199FE0E0068", "Finnish Sonera SIM-card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A0041100B4", "Aldi Talk SIM card, Germany (Telecommunication)\nhttps://www.alditalk.de/talk" }, { "3B9E96801FC78031E073FE211B66D001A01E1100AE", "Zevvle SIM Card (Telecommunication)\nhttps://zevvle.com" }, + { "3B9E96801FC78031E073FE211B66D001A0741000C5", "USIM of Vodafone Germany (MCC 262, MNC 2) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A0DD12006E", "USIM (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A0EC11005C", "Boost Mobile Prepaid Micro SIM Card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1121000A2", "OpenAirInerface (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D001A1181100A9", "WinEMP NRI License Card (Other)" }, + { "3B9E96801FC78031E073FE211B66D001A122110093", "Lebara (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1581100E9", "Vodafone CZ: SIMPLUS V128 LTE (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1680F00C7", "Free Mobile SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1721000C2", "Vodafone (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1731000C3", "Prepaid public telephone card from Lidl Connect, Germany (Telecommunication)\nhttps://www.lidl.de/de/lidl-connect/s7373597" }, + { "3B9E96801FC78031E073FE211B66D001A1771000C7", "SIM Card Model X1 for Ting Mobile Carrier (Telecommunication)\nhttps://tingmobile.com/" }, { "3B9E96801FC78031E073FE211B66D001A1A70F0008", "Spanish Movistar Mobile phone SIM card (Telecommunication)\nhttp://www.movistar.es/" }, { "3B9E96801FC78031E073FE211B66D001A1E60F0049", "SIM O2 CZ (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D002170912000E", "SIM/USIM (SPAIN) - ORANGE ESPAGNE VIRTUAL, S.A.U. (Orange / Jazztel / SIMYO) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0021759140058", "USIM card of of Ukrainian Telecommunications Operator Kyivstar, emitted after 2016 (Kyivstar GSM) (GSM/UMTS/LTE services) (Telecommunication) (Telecommunication)\nhttps://kyivstar.ua/uk/4g" }, { "3B9E96801FC78031E073FE211B66D002175B12005C", "Cellcom Israel USIM (micro FF) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0021760130066", "Vodafone spain barcelona (Telecommunication)" }, @@ -2353,12 +2577,22 @@ const static atr_t AtrTable[] = { { "3B9E96801FC78031E073FE211B66D00217D91200DE", "telenor (swedish mobile provider) SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D00217F41200F3", "SIM card for Swedish operator Vimla! (Telecommunication)\nhttps://www.vimla.se" }, { "3B9E96801FC78031E073FE211B66D002194B120042", "halebop (swedish mobile provider) SIM card (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D0022436140004", "SIM (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D0022A6D140051", "Mobitel SIM from Sri Lanka (Telecommunication)\nhttps://mobitel.lk/" }, { "3B9E96801FC78031E073FE211B66D0022A72130049", "SIM Card OI (Brazil) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022A861300BD", "Mobile Vikings SIM Card (Telecommunication)\nhttps://mobilevikings.com" }, + { "3B9E96801FC78031E073FE211B66D0022A8F1400B3", "Telefonica USIM (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022AD31300E8", "sim card from Mobilcom Debitel Telefonica (Telecommunication)\nhttps://md.de" }, { "3B9E96801FC78031E073FE211B66D0022AD91300E2", "sim (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D0022AE81300D3", "Twilio Super SIM card (Telecommunication)\nhttps://www.twilio.com/iot/super-sim-card" }, + { "3B9E96801FC78031E073FE211B66D0022AF21400CE", "Tello (Telecommunication)\nhttps://tello.com/" }, { "3B9E96801FC78031E073FE211B66D00233AD140088", "4G-LTE (Telecommunication)" }, { "3B9E96803FC3A08031E073FE211B630801140F9000D3", "KT Olleh LTE Warp SA-L 1670 (Telecommunication)" }, + { "3B9E97801FC68031E073FE211B66D0019F7A1200F6", "Spectrun USA Sim Card (Telecommunication)" }, + { "3B9E97801FC68031E073FE211B66D0024011150046", "SIM Card for French mobile provider Unyc (Telecommunication)\nhttps://www.unyc.io/" }, + { "3B9E97801FC68031E073FE211B66D002401D15004A", "Telia Sim card for IoT (Telecommunication)" }, + { "3B9E97801FC68031E073FE211B66D0024027150070", "verymobile wind 3 (Telecommunication)\nhttps://verymobile.it/" }, + { "3B9E97801FC68031E073FE211B66D0025E7315003A", "Twilio Super SIM (Telecommunication)\nhttps://www.twilio.com/iot/super-sim-card" }, { "3B9E97801FC78031E073FE211B66D0006B951100EE", "TracFone SIM Verizon Wireless LTE supported (Telecommunication)\nhttps://www.tracfone.com" }, { "3B9E97801FC78031E073FE211B66D0022AB3130089", "SIM T-MObile (Telecommunication)" }, { "3B9F..801FC300681.4405014649534531C8..9000..", "Setec SetCOS 4.4.1" }, @@ -2406,6 +2640,7 @@ const static atr_t AtrTable[] = { { "3B9F94401E0067114346495345105266FF819000", "Setec / FINEID\nSETEC Instant EID" }, { "3B9F94401E0067164346495345105266FF819000", "RSA SecurID 3100 or Utimaco Safeware Smartcard\nSetCOS 4.3.1 Revision Unknown" }, { "3B9F94801FC30068104405014649534531C807900018", "SetCOS 4.3.0 32K RSA\nInstant EID IP2\nSETEC SetCard 32K PKI Evaluated SetCOS 4.4.1a2" }, + { "3B9F94801FC38031A07320210805500940095E010274", "Yemen Mobile SIM Card Yemen (YE) (Telecommunication)" }, { "3B9F94801FC38031A073B6A10067CF3251CC9E6EC015", "OYEITIMES 2G GSM Blank SIM Card (Other)\nhttps://www.aliexpress.com/item/32972705510.html?spm=a2g0s.9042311.0.0.5a564c4daKCdcQ" }, { "3B9F94801FC38031E073FE21136301030283079000CE", "Programmable SIM card as one of the requirement for free5GC testing procedure (Telecommunication)" }, { "3B9F94801FC78031E073FE2100644090610082900069", "softsim (Telecommunication)" }, @@ -2420,13 +2655,16 @@ const static atr_t AtrTable[] = { { "3B9F94801FC78031E073FE21135749050C86986018CC", "China Unicom USIM 128K 6131H" }, { "3B9F94801FC78031E073FE21135786810586984018AE", "China Unicom USIM Card (Telecommunication)" }, { "3B9F94801FC78031E073FE211357868107869862188E", "OYEITIMES MCR3516 SIM Card (Telecommunication)" }, + { "3B9F94801FC78031E073FE2113578681098698621880", "Oyeitimes 5G ISIM R16 (Telecommunication)\nhttps://www.oyeitimes.com/detail.php?id=535&ids=565&idt=&ide=&dd=1410" }, { "3B9F94801FC78031E073FE21135786850686984218AB", "4G LTE blank USIM Green Card (Telecommunication)\nhttp://grcard.en.alibaba.com/product/60076835567-209365843/4G_LTE_blank_usim_card_for_4G_network.html" }, { "3B9F94801FC78031E073FE21136321150683079000F8", "Maroc Telecom 4G+ (Telecommunication)" }, + { "3B9F94801FC78031E073FE21136700000000000070AA", "OMANTEL (Telecommunication)\nhttps://portal.omantel.om/Personal/mobile/hayyak" }, { "3B9F94801FC78031E073FE21136761210D10000070F7", "Celcom XPAX (Telecommunication)\nhttps://www.celcom.com.my/personal/prepaid" }, { "3B9F94801FC78031E073FE2119573C8660CFB902A0EE", "Gotanet SE USIM (Telecommunication)" }, { "3B9F94801FC78031E073FE2119573C8660CFBA02A0ED", "Gotanet DK USIM (Telecommunication)" }, { "3B9F94801FC78031E073FE211B573786609B3289B2A1", "China Unicom Travel SIM (Telecommunication)\nhttps://www.cuniq.com/hk/data-card/asia/asia-12days.html" }, { "3B9F94801FC78031E073FE211B573786609BA182109B", "U Mobile POWER Prepaid (Telecommunication)\nhttp://www.u.com.my/prepaid" }, + { "3B9F94801FC78031E073FE211B573C8660CDA1001246", "Beeline SIM card (RUS) (Telecommunication)\nhttps://beeline.ru" }, { "3B9F94801FC78031E073FE211B573F86604D03000075", "Prepaid SIM card MOCHE (Portugal) (Telecommunication)" }, { "3B9F94801FC78031E073FE211B573F866083020000BA", "GSM-SIM Beeline RU (Telecommunication)\nhttp://beeline.ru" }, { "3B9F94801FC78031E073FE211B573F866096A100000C", "Lycamobile UICC/SIM card. (Telecommunication)\nhttps://www.lycamobile.com.au" }, @@ -2436,6 +2674,7 @@ const static atr_t AtrTable[] = { { "3B9F94801FC78031E073FE211B640681010082900045", "Mobicarte Orange" }, { "3B9F94801FC78031E073FE211B6407564200829000D0", "SIM mobi orange" }, { "3B9F94801FC78031E073FE211B649F4F002082900033", "USIM (3G DF) Orange (French Mobile Network) (Telecommunication)" }, + { "3B9F94801FC78031E073FE211B674B004A5503060083", "CU esim thd89 (Telecommunication)" }, { "3B9F95801F038031A073B6A10067CF97F9E063689957", "Telestial OneRate International SIM card (Telecommunication)\nhttp://www.amazon.com/gp/product/B004GV13VY/ref=oh_aui_detailpage_o02_s00?ie=UTF8&psc=1" }, { "3B9F95801F438031E073362113574A330E09314100A9", "GSM-SIM Elisa (Estonia, WPKI eID support)" }, { "3B9F95801F438031E073362113574A330E0C314100AC", "Vodafone SIM (mobile phone)" }, @@ -2449,6 +2688,7 @@ const static atr_t AtrTable[] = { { "3B9F95801FC38031A073BE21005302E232830590003C", "NTT docomo FOMA(W-CDMA) GE02(Japan Gemplus) Green SIM (Telecommunication)\nhttps://ja.wikipedia.org/wiki/%E3%83%89%E3%82%B3%E3%83%A2UIM%E3%82%AB%E3%83%BC%E3%83%89" }, { "3B9F95801FC38031A073BE2113674701030102000099", "T-Mobile (UK) SIM\nFresh Mobile (UK) SIM" }, { "3B9F95801FC38031A073BE2113674701050104000099", "T-Mobile Nederlands\nhttp://www.tmobile.nl" }, + { "3B9F95801FC38031A073BE2113674701050904000091", "SIM card (Telecommunication)" }, { "3B9F95801FC38031A073BE2113674701090901000098", "German SIM card (T-Mobile) (Telecommunication)" }, { "3B9F95801FC38031A073BE211367D002030901000006", "GSM, T-Mobile (Germany)" }, { "3B9F95801FC38031A073BE211367D002031901000016", "GSM SIM card from congstar (a no-frills service provider of Deutsche Telekom)" }, @@ -2464,11 +2704,14 @@ const static atr_t AtrTable[] = { { "3B9F95801FC38031E073FE211B649B4D011182900000", "Singular (now AT&T) 3G GSM SIM Card" }, { "3B9F95801FC38031E073FE211BB3E20174830F900088", "Gemplus GemXplore 3G USIM" }, { "3B9F95801FC78031A073B6A10067CF1713C41D2D3649", "Idemia card (Telecommunication)" }, - { "3B9F95801FC78031A073B6A10067CF1794AA28993F28", "Glaier Programable LTE USIM Card (Telecommunication)" }, + { "3B9F95801FC78031A073B6A10067CF1794AA28993F28", "Glaier Programmable LTE USIM Card (Telecommunication)" }, { "3B9F95801FC78031A073B6A10067CF3211B252C679F3", "open5gs (Telecommunication)" }, { "3B9F95801FC78031A073B6A10067CF3215A98FD70950", "LTE Card (Telecommunication)" }, + { "3B9F95801FC78031A073B6A10067CF3215CA9CD70920", "Gialer LTE USIM (Telecommunication)\nhttps://www.gialer.com/collections/writable-sim-card/products/gialer-writable-programmable-sim-card-4g-lte-wcdma-gsm-nano-micro-2ff-3ff-4ff-blank-usim-card-for-telecom-operator?variant=40982030090394" }, + { "3B9F95801FC78031A073B6A10067CF3216C3F1484998", "LTE USIM as used for Private LTE Networks. Manufacturer 'Huahong' in China (Telecommunication)" }, { "3B9F95801FC78031A073BE21005304D08083059000BE", "NTT_DoCoMo (Telecommunication)" }, { "3B9F95801FC78031A073BE211367D002040901000005", "T-Mobile SIM card issued in Germany 2012" }, + { "3B9F95801FC78031E073F62100000086538300900061", "Zong (Telecommunication)" }, { "3B9F95801FC78031E073F62113674D451500390102F4", "Vivo 4G LTE SIM Card (Telecommunication)" }, { "3B9F95801FC78031E073F62113674D4516004301008F", "VALID USIM (Telecommunication)" }, { "3B9F95801FC78031E073FE21135712291102010000C2", "sysmocom sysmoUSIM-GR1\nhttp://sysmocom.de/" }, @@ -2481,11 +2724,13 @@ const static atr_t AtrTable[] = { { "3B9F95801FC78031E073FE2113574A330E1932330084", "WIND Mobile SIM Card" }, { "3B9F95801FC78031E073FE2113574A330E1A32330087", "Rohde and Schwarz CMW-Z04. Mini-UICC Test Card (Telecommunication)" }, { "3B9F95801FC78031E073FE2113574A330E1A32360082", "TELUS 3G SIM Card" }, + { "3B9F95801FC78031E073FE2113574A330E1E32360086", "SIM Enreach (Telecommunication)" }, { "3B9F95801FC78031E073FE2113635510888307900006", "SHOW or UPlus USIM (Telecommunication)" }, { "3B9F95801FC78031E073FE2113635510AA8307900024", "2degrees NFC (Telecommunication)\nhttp://www.2degreesmobile.co.nz/home" }, { "3B9F95801FC78031E073FE2113672228004001000191", "Lycamobile Prepaid SIM-Card (Telecommunication)\nhttp://lycamobile.at" }, { "3B9F95801FC78031E073FE211367940311000002035C", "AT&T (US) GSM SIM" }, { "3B9F95801FC78031E073FE211B573C8660BEB7002010", "Kyivstar SIM (Telecommunication)\nhttps://kyivstar.ua/" }, + { "3B9F95801FC78031E073FE211B573F866096AE000002", "Kazakhstan 'Beeline' 4G SIM card (Telecommunication)\nhttps://beeline.kz" }, { "3B9F95801FC78031E073FE211B63E20394830F9000BE", "Telenet N.V. Walk & Talk SIM Card Belgium" }, { "3B9F95801FC78031E073FE211B63E204A5830F900088", "Cingular '64Ksmartchip' GSM SIM\nTelia GSM/3G (Swedish operator)" }, { "3B9F95801FC78031E073FE211B63E206A6830F900089", "TracFone (US) Net10 GSM SIM card\nSimyo (Germany) GSM SIM card\nH3G (Italy) UMTS USIM card" }, @@ -2498,6 +2743,7 @@ const static atr_t AtrTable[] = { { "3B9F95801FC78031E073FE211B640752930282900006", "AT&T GO PHONE 2G (Telecommunication)\nhttps://www.att.com/shop/wireless/gophone.html" }, { "3B9F95801FC78031E073FE211B6407536301829000F4", "AT&T UICC with USIM and ISIM (Telecommunication)" }, { "3B9F95801FC78031E073FE211B6440614100829000A2", "SIM card (Telecommunication)" }, + { "3B9F95801FC78031E073FE211B644067620082900087", "ATT SIM card (Telecommunication)" }, { "3B9F95801FC78031E073FE211B644112010082900090", "MTS SIM card (Russia) (Telecommunication)\nhttp://www.mts.ru/" }, { "3B9F95801FC78031E073FE211B6441132100829000B1", "AT&T SIM card (Telecommunication)" }, { "3B9F95801FC78031E073FE211B644132310082900080", "Telefonica | Vivo Brazil M2M SIM Card (Telecommunication)" }, @@ -2505,6 +2751,8 @@ const static atr_t AtrTable[] = { { "3B9F95801FC78031E073FE211B659F010902048105C4", "2degrees mobile SIM card\nhttp://www.2degreesmobile.co.nz/" }, { "3B9F95801FC78031E073FE211B65B4010E01028105ED", "USIM Card by Maroc Telecom Morocco (Telecommunication)" }, { "3B9F95801FC78031E073FE211B675348434F5F01019B", "baicells sim card (Telecommunication)\nhttp://www.baicells.com/" }, + { "3B9F95801FC78031E073FE211B675744534C45393794", "SIM card MTS Russia (Telecommunication)\nhttps://moskva.mts.ru/personal" }, + { "3B9F95801FC78031E073FEA11F6441805100829000D6", "AT&T SIM Card (Telecommunication)" }, { "3B9F95803FC3A08031E073FE2113638D434283F0900034", "Taisys Java Csrd (Telecommunication)" }, { "3B9F95803FC7A08031A073BE211B5305D0808305900024", "NTT docomo Xi(LTE) AX05(Gemalto) Pink SIM (Telecommunication)\nhttps://ja.wikipedia.org/wiki/%E3%83%89%E3%82%B3%E3%83%A2UIM%E3%82%AB%E3%83%BC%E3%83%89" }, { "3B9F95803FC7A08031E073FE211363554022830790007C", "Ubivelox (JavaCard)" }, @@ -2515,6 +2763,8 @@ const static atr_t AtrTable[] = { { "3B9F958131FE9F006646530510043171DF000000000026", "ePass2003 (PKI)" }, { "3B9F958131FE9F006646530510043171DF000006000020", "FEITIAN ePASS 2003 Auto (PKI)" }, { "3B9F958131FE9F006646530510043171DF00003900001F", "Feitian ePass2003 (PKI)" }, + { "3B9F958131FE9F006646530510063171DF000000000024", "FT ePass2003Auto USB Token (PKI)" }, + { "3B9F958131FE9F006646530510323871DF00000600001F", "token (PKI)" }, { "3B9F958131FE9F006646530520032571DF000000000005", "Feitian ePass2003 token" }, { "3B9F958131FE9F006646530520032571DF000003900096", "Feitian ePass2003 token (PKI)" }, { "3B9F958131FE9F006646530523002571C39F0000000086", "Feitian ePass2003 token" }, @@ -2522,6 +2772,10 @@ const static atr_t AtrTable[] = { { "3B9F958131FE9F006646530523002571DF000003900096", "Feitian USB Cryptographic token (FIPS 140-2 Level 3) (PKI)\nhttp://www.ftsafe.com/product/epass/epass2003" }, { "3B9F958131FE9F006646530532022571DF000006000010", "ePass 3003 Auto (PKI)\nhttps://www.ftsafe.com/products/PKI/Standard/Specification" }, { "3B9F958131FE9F006646530534002571DF0000036A82F9", "Feitian ePass2003Auto (PKI)\nhttps://www.ftsafe.com/Products/PKI/Standard/Specification" }, + { "3B9F958131FE9F006646530551003371DF000000000061", "FT ePass2003Auto 00 00 (PKI)" }, + { "3B9F958131FE9F006646530551003371DF0000036A828A", "Feitian epass2003 Auto (PKI)" }, + { "3B9F958131FE9F006646530551003371DF0000039000F2", "HyperPKI USB Token (PKI)\nhttps://www.hypersecu.com/hyperpki" }, + { "3B9F958131FE9F006646530551003371DF000006000067", "Epass2003 (PKI)" }, { "3B9F95C00A1F078031E073FE211B63F100AD830F90001D", "EMT WPKI USIM (2014, Estonia, JavaCard) (Telecommunication)" }, { "3B9F95C00A1FC78031E073FE211B63F100AD830F9000DD", "Telenor SIM card (Norway)" }, { "3B9F96400A8031E06B0420050258555555555555", "BuyPass identification card. It can also possibly be used to hold e-currency." }, @@ -2532,31 +2786,41 @@ const static atr_t AtrTable[] = { { "3B9F96801F438031E073362113574A330E0C314100AF", "GSM, Vodafone (Germany)" }, { "3B9F96801F478031E073362113574A330E0C314100AB", "NATEL SIM-Card swisscom\nVodafone Germany Micro-SIM from 2010" }, { "3B9F96801F478031E073FE2113574A330E253332003B", "SMART prepaid card, Estonia (Telecommunication)" }, + { "3B9F96801F878031A073BE2111630000018305900089", "jio sim card (Telecommunication)" }, { "3B9F96801F878031E073FE2119674A555473300948DB", "nano sim/usim card (Telecommunication)" }, { "3B9F96801F878031E073FE2119674A555475300662F8", "SIM Card (Telecommunication)" }, { "3B9F96801F878031E073FE2119674A557330310746BE", "Hologram Developer Global IoT SIM Card (Telecommunication)\nhttps://hologram.io/store/global-iot-sim-card/17" }, + { "3B9F96801F878031E073FE2119674A55753031136288", "Zimbabwe sim card provider: ECONET (Telecommunication)" }, { "3B9F96801F878031E073FE211B674A4C5275310451D5", "Test card provided with 4G/5G network from Amarisoft (Telecommunication)" }, { "3B9F96801F878031E073FE211B674A4C7530300248A9", "Cardcentrics (Telecommunication)" }, - { "3B9F96801F878031E073FE211B674A4C753034054BA9", "Test Card (Telecommunication)" }, + { "3B9F96801F878031E073FE211B674A4C753034054BA9", "sysmoISIM-SJA2 (Telecommunication)\nhttps://osmocom.org/projects/cellular-infrastructure/wiki/SysmoISIM-SJA2" }, + { "3B9F96801F878031E073FE211B674A55527531054BD7", "Softbank (C2) USIM card (Telecommunication)" }, + { "3B9F96801F878031E073FE211B674A55733031094BBF", "NB-IoT SIM (Telecommunication)" }, { "3B9F96801FC30068104405014649534531C80790001A", "SETEC SetCard 32K PKI Evaluated SetCOS 4.4.1" }, { "3B9F96801FC38031E073FA21006331602383109000BE", "TAISYS SIMoME JAR (JavaCard)\nhttp://www.taisys.com/p-detail?id=39beTne-hf4u2Y5DnWghweplyFR-dUUf1-xpy55cWQ" }, + { "3B9F96801FC38031E073FE2100671101674006007789", "Beni's UICC 4G LTE (Telecommunication)\nhttps://cslgroup.co.id/" }, { "3B9F96801FC38031E073FE211BB3E2027E830F900082", "Vodafone SIM Card (D2, 1800Mhz, Germany, Twincard, possibly others too?), manufactured by Gemplus (See stamp on the chip)\nMobileOne (Singaporean 3G/GSM operator) SIM card 'II(3G-64) M1 3G '" }, { "3B9F96801FC68031E073F62113674D45220079020080", "TMobile (Telecommunication)" }, { "3B9F96801FC68031E073F62113674D4522008001007A", "ORANGE SPAIN GSM 4G SIM (Telecommunication)" }, { "3B9F96801FC68031E073F62113675602220080010127", "giffgaff USIM (Telecommunication)" }, { "3B9F96801FC68031E073F62113675603220121010087", "Rain 4G 5G Internet Service provider (Telecommunication)\nhttps://www.rain.co.za" }, + { "3B9F96801FC68031E073F62113675603220121020084", "T-Mobile Sim Card (Telecommunication)" }, { "3B9F96801FC68031E073FE2111633F011183079000E4", "VR-Bank VRNetworld HBCI Card (Bank)" }, { "3B9F96801FC68031E073FE2113574A330577333300E2", "USIM issued by Moldcell mobile operator in Moldova (Telecommunication)\nhttp://www.moldcell.md/" }, + { "3B9F96801FC68031E073FE211B6300850083819000C2", "Vodafone IoT USIM (Telecommunication)" }, { "3B9F96801FC68031E073FE211B6441024100829000C2", "Empire Bank ltd (Bank)\nFinnish Telia SIM-card (Telecommunication)" }, { "3B9F96801FC68031E073FE211B644104810082900004", "SAP Leonardo IoT Connect 365 SIM card (Telecommunication)\nhttps://www.sap.com/uk/products/enterprise-iot-connect.html" }, { "3B9F96801FC68031E073FE211B644115010082900095", "Telia LT prepaid Ezys USIM (Telecommunication)\nhttps://www.ezys.lt" }, { "3B9F96801FC68031E073FE211B6441177300829000E5", "Prixtel SIM card over OrangeF network (Telecommunication)\nhttps://www.prixtel.com" }, { "3B9F96801FC68031E073FE211B6441193100829000A9", "Taiwan Star Telecom Corporation Limited (Telecommunication)\nhttps://www.tstartel.com/CWS/" }, + { "3B9F96801FC68031E073FE211B6441247100829000D4", "RWG Mobile SIM Card (Telecommunication)\nhttps://www.rwgmobile.wales/rates-plans-eng-per/" }, { "3B9F96801FC68031E073FE211B644124910082900034", "Fido Canada (Telecommunication)" }, { "3B9F96801FC68031E073FE211B6441255100829000F5", "R&S CMW-Z06 Multi Test SIM (Telecommunication)" }, { "3B9F96801FC68031E073FE211B644130320082900083", "SFR (Telecommunication)" }, { "3B9F96801FC68031E073FE211B6441346100829000D4", "Ting X1 GSM SIM Card (4G, T-Mobile network) (Telecommunication)\nhttps://ting.com/shop/gsmSIM" }, + { "3B9F96801FC68031E073FE211B65CA010E0209810599", "Liquid Telecom Zambia (Telecommunication)\nhttps://zm.liquidhome.tech/" }, { "3B9F96801FC68031E073FE211B66D00206131218031E", "SIM card (Telecommunication)" }, + { "3B9F96801FC68031E073FE211B66D002061312180419", "T-Mobile NL SIM only (Telecommunication)" }, { "3B9F96801FC68031E073FE211B66D00206E20F1801F0", "Telekom Sim card (Telecommunication)" }, { "3B9F96801FC68031E073FE211B66D00206E20F1803F2", "Vulcan 0502 T-Mobile SIM (Telecommunication)" }, { "3B9F96801FC68031E073FE211B66D00206E20F1804F5", "T-Mobile Nederlands (Telecommunication)\nhttps://t-mobile.nl/" }, @@ -2565,6 +2829,7 @@ const static atr_t AtrTable[] = { { "3B9F96801FC68031E073FE211B66D00221AB11180180", "Telekom Germany Triple SIM issued in 2018 (Telecommunication)" }, { "3B9F96801FC68031E073FE211B66D00221AB11180786", "telekom HU nano sim card (Telecommunication)\nhttps://www.telekom.hu" }, { "3B9F96801FC68031E073FE211C6441193100829000AE", "SORACOM SIM (plan unknown) (Telecommunication)" }, + { "3B9F96801FC78031A073BE21136742470111000001CC", "Bank of Hawai'i (Bank)\nhttps://www.boh.com/" }, { "3B9F96801FC78031A073BE2113674320071800000100", "sysmoUSIM-SJS1 (Telecommunication) (Telecommunication)\nhttp://www.sysmocom.de/products/sysmousim-sjs1-sim-usim" }, { "3B9F96801FC78031A073BE21136743200718000001A5", "sysmoUSIM-SJS1 (Telecommunication)\nhttp://www.sysmocom.de/products/sysmousim-sjs1-sim-usim" }, { "3B9F96801FC78031A073BE21136744220610000001A9", "Fairwaves test SIM card (Telecommunication)\nhttp://fairwaves.co" }, @@ -2576,6 +2841,9 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073F62113675603270089010228", "TMobile Prepaid USIM (Telecommunication)" }, { "3B9F96801FC78031E073F621136756032701010203A3", "USIM LTE MOVISTAR (Telecommunication)" }, { "3B9F96801FC78031E073F621136756032701020100A0", "vivo (Other)" }, + { "3B9F96801FC78031E073F62113675603270120020081", "VALID brand SIM card from T-Mobile (Telecommunication)" }, + { "3B9F96801FC78031E073F6211367560346014901008A", "T-Mobile Poland pre-paid SIM card (Telecommunication)" }, + { "3B9F96801FC78031E073F6A157574A4D020B6110005B", "G+D Mobile Security for private 5G USIM (Telecommunication)\nhttps://www.gi-de.com/en/connectivity/mobile-network-operator/5g#5g-sim" }, { "3B9F96801FC78031E073FE21..63......83..9000..", "TIM (Italy) 128KB GSM SIM\nH3G (Italy) UMTS USIM\nVodafone (UK) SIM" }, { "3B9F96801FC78031E073FE211163407262830790009A", "GSM SIM / LTE UICC card issued by Omnitel, Lithuania (Telecomunications)" }, { "3B9F96801FC78031E073FE2111634082918307900099", "Chunghwa Telecom (Taiwan) 3G/4G PrePaid card\nhttps://www.twgate.net/prepaidcard/product_en.html" }, @@ -2591,6 +2859,8 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073FE2113574A330E25333200BB", "Sintel hi! Tourist prepaid (Telecommunication)\nhttp://www1.singtel.com/personal/phones-plans/mobile/prepaid/hitouristsimcards.html" }, { "3B9F96801FC78031E073FE2113574A330E26333200B8", "Vodafone sim card (Telecommunication)" }, { "3B9F96801FC78031E073FE2113574A330E3F333400A7", "SIM Card (Telecommunication)" }, + { "3B9F96801FC78031E073FE2113574A338101330017", "Telefonica M2M Global SIM (Telecommunication)" }, + { "3B9F96801FC78031E073FE2113574A33810433330014", "PureTalk SIM Card (Telecommunication)" }, { "3B9F96801FC78031E073FE2113679118020406060651", "USIM (3G PF) Orange" }, { "3B9F96801FC78031E073FE211367931801010401095F", "Telstra 128K Universal Orange SIM Card (Telecommunication)" }, { "3B9F96801FC78031E073FE2113679319010401010750", "Telefonica m2m simcard, Movistar o2 vivo (Telecommunication)" }, @@ -2600,6 +2870,7 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073FE211367933001030301047F", "T-Mobile US SIM card (Telecommunication)" }, { "3B9F96801FC78031E073FE211367933001030403027C", "Comprion test U(SIM): model 128K/J LTE Test (U)SIM - Mini-UICC, product number Prod. No.: 10432006 (Telecommunication)\nhttp://www.comprion.com/en/products/test__u_sims/3g_test_usim_cards" }, { "3B9F96801FC78031E073FE211367933001060201027D", "Morpho USIM (Telecommunication)" }, + { "3B9F96801FC78031E073FE2113679330010802010273", "Bouygues Telecom SIM (Telecommunication)" }, { "3B9F96801FC78031E073FE211367933001110201026A", "R&S CMW-Z04 Mini-UICC Test Card (Telecommunication)\nhttp://www.rhode-schwartz.com" }, { "3B9F96801FC78031E073FE2113679331010801010172", "Morpho (Telecommunication)" }, { "3B9F96801FC78031E073FE2113679403030A02020244", "Digi mobil Romania (old sim card version) (Telecommunication)" }, @@ -2609,6 +2880,7 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073FE211367940401070202024C", "Orange Mobicarte (France)" }, { "3B9F96801FC78031E073FE211367940404030505054A", "Claro operator SIM" }, { "3B9F96801FC78031E073FE211367941602070502025A", "CSL Mobile / PCCW SIM Card (Telecommunication)\nhttp://www.hkcsl.com/" }, + { "3B9F96801FC78031E073FE211367941602120101014B", "Airtel India Sim Card (Telecommunication)" }, { "3B9F96801FC78031E073FE2113679801010202030142", "Telenor SIM card (Hungary) (Telecommunication)\nhttps://www.telenor.hu" }, { "3B9F96801FC78031E073FE2113679801010402030247", "ReadySIM / T-Mobile USA USIM (2013) (Telecommunication)\nhttp://www.readysim.com/" }, { "3B9F96801FC78031E073FE2113679801010803010349", "Yesss! SIM card (Austria) (Telecommunication)\nhttps://www.yesss.at" }, @@ -2629,8 +2901,11 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073FE2115574A330E3B333400A5", "O2 Spain phone SIM (Telecommunication)\nhttps://o2online.es/" }, { "3B9F96801FC78031E073FE2115574A330E3F333500A0", "Telefonica (Germany) SIM Card (Telecommunication)" }, { "3B9F96801FC78031E073FE2117574A330E3F333400A3", "Telecommunication (SIM or USIM card) T-Mobile Communications (Telecommunication)" }, + { "3B9F96801FC78031E073FE2117574A330E3F333500A2", "Megafon (RUS) SIM Card (Telecommunication)\nhttps://megafon.ru" }, { "3B9F96801FC78031E073FE2117574A330E3F333600A1", "Bell Mobility MULTI SIM (Telecommunication)\nhttps://bell.ca" }, { "3B9F96801FC78031E073FE2117574A33810433360015", "HoT Austria SIM Card (Telecommunication)\nhttps://hot.at/" }, + { "3B9F96801FC78031E073FE2117574A3382033339001E", "Sm@rtSIM Zeta (Giesecke & Devrient SIM) (Telecommunication)" }, + { "3B9F96801FC78031E073FE2117574A33820433390019", "NB-IoT SIM (Telecommunication)" }, { "3B9F96801FC78031E073FE211B57318660B21A0C0C9F", "SIM CARD (JavaCard)" }, { "3B9F96801FC78031E073FE211B630057008381900011", "3 (3g microUSIM) Italian subsidiary H3G" }, { "3B9F96801FC78031E073FE211B634041918307900050", "giffgaff UK 3G SIM\nnetzclub sponsored mobile\no2 Loop GSM SIM Germany (PREPAID) from 2013" }, @@ -2655,7 +2930,10 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073FE211B6441447300829000B7", "Bouygues Telecom (French Mobile Provider SIM card) (Telecommunication)\nhttps://www.bouyguestelecom.fr" }, { "3B9F96801FC78031E073FE211B6441503100829000E1", "telcel sim card (Telecommunication)" }, { "3B9F96801FC78031E073FE211B6441725100829000A3", "Free Mobile (French wireless service provider) SIM card (mini-SIM with micro-SIM cutout) received in 2020-09 (Telecommunication)\nhttps://mobile.free.fr/" }, + { "3B9F96801FC78031E073FE211B6441725200829000A0", "Woolworths Mobile Prepaid SIM (Telecommunication)\nhttps://mobile.woolworths.com.au/" }, { "3B9F96801FC78031E073FE211B6441763200829000C4", "T-Mobile SIM card issued April 2021 (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B6450040100826A82EC", "SIM (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B6450055100829000C5", "Sim card Team Telecom Armenia (Telecommunication)\nhttps://www.telecomarmenia.am/hy/" }, { "3B9F96801FC78031E073FE211B65240109010081057B", "Lycamobile (UK) GSM SIM card" }, { "3B9F96801FC78031E073FE211B65260109000781057F", "EMT WPKI 2015 (ECC) subscription (Telecommunication)\nhttps://www.emt.ee/en/pakkumised/mobiil-id" }, { "3B9F96801FC78031E073FE211B65270109010381057B", "slarmy (Telecommunication)" }, @@ -2663,8 +2941,12 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073FE211B652A010A0102810574", "Thailand AIS SIM Card (Telecommunication)" }, { "3B9F96801FC78031E073FE211B652A010E0201810570", "gsm (Telecommunication)" }, { "3B9F96801FC78031E073FE211B652D010C0400810572", "Tri Indonesia Bima+ SIM Card (Telecommunication)\nhttps://beta.tri.co.id/3digiworld/Bimaplus" }, + { "3B9F96801FC78031E073FE211B652F0109020A810579", "cellcom israel sim (Telecommunication)" }, { "3B9F96801FC78031E073FE211B652F010A0301810570", "SMART Prepaid SIM (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B652F010A0304810575", "Smart Telecom (PH) (Telecommunication)\nhttps://smart.com.ph/Prepaid/sim-and-phones/lte-prepaid-sim" }, + { "3B9F96801FC78031E073FE211B652F010D0100810574", "vodacom_sim (Pay TV)" }, { "3B9F96801FC78031E073FE211B6556544C017481056D", "Viettel LTE SIM (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B6556544C017681056F", "Viettel Telecom (Telecommunication)" }, { "3B9F96801FC78031E073FE211B65630109020181053E", "2degrees Mobile SIM Card New Zealand (Telecommunication)\nhttp://2degreesmobile.co.nz" }, { "3B9F96801FC78031E073FE211B65BA010904048100E1", "Blank SIM (Telecommunication)" }, { "3B9F96801FC78031E073FE211B66D0016B820E1801FE", "Telekom Deutschland GmbH - Xtra Data Karte (3G SIM)" }, @@ -2678,11 +2960,18 @@ const static atr_t AtrTable[] = { { "3B9F96803FC7008031E073FE211F6441262100829000A3", "Smartjac SMAOT100A234FF (Telecommunication)\nhttps://smartjac.com" }, { "3B9F96803FC7828031E073F62157574A330581053000CE", "COMPRION M2M eUICC (Telecommunication)" }, { "3B9F96803FC7828031E073F62157574A4D020B60010069", "eSIM GSMA Card (Telecommunication)\nhttps://www.gsma.com/newsroom/wp-content/uploads/SGP.22_v2.2.pdf" }, + { "3B9F96803FC7828031E073F62157574A4D020B60610009", "ting (Telecommunication)" }, + { "3B9F96803FC7828031E073FE211B57AA8660F0010004FB", "The eSIM.me Card (Telecommunication)\nhttps://esim.me/" }, + { "3B9F96803FC7828031E073FE211B57AA8660F0010011EE", "eSIM.me pluggable eSIM (Telecommunication)\nhttps://esim.me/" }, + { "3B9F96803FC7828031E073FE211B633A204E8300900031", "eSIM (Telecommunication)" }, + { "3B9F96803FC7828031E075F62157210355020B60500019", "st33g1m2 (Telecommunication)\nhttps://www.st.com/en/secure-mcus/st33g1m2.html" }, + { "3B9F96803FC7A08031E073F62116574A4D020233456377", "ISIS-Ready T-Mobile Sim Card (Telecommunication)" }, { "3B9F96803FC7A08031E073F62156574A4D020B3444005B", "Norwegian telenor (Telecommunication)\nhttp://www.telenor.no" }, { "3B9F96803FC7A08031E073F62157574A4D020B34546329", "Orange FR - opa (Telecommunication)" }, { "3B9F96803FC7A08031E073FE211B63F100E8830090005E", "UICC CARD (Telecommunication)" }, { "3B9F96803FC7A08031E073FE211B6407689A00829000B4", "Orange SIM Card (Telecommunication)" }, { "3B9F96803FC7A08031E073FE211B64080503008290004F", "NFC-enabled SIM card of MTS Russia. (Telecommunication)" }, + { "3B9F96803FC7A08031E073FE211F6300690083819000AB", "GSM file system and SWP sample supplied with STMicro development kit (Other)" }, { "3B9F96803FC7A08031E073FE211F6441269100829000B3", "LTE Lab SIM Ver 1.3 (Telecommunication)" }, { "3B9F968131FE458065544312210831C073F6218081059A", "Scientific and Technological Research Council of Turkey (test card) (eID)" }, { "3B9F968131FE45806755454B41451212318073B3A180EA", "AKiS v1.2 on nxp chip" }, @@ -2691,10 +2980,12 @@ const static atr_t AtrTable[] = { { "3B9F968131FE45806755454B41451292318073B3A1806A", "AKiS v1.2.2 on infineon chip" }, { "3B9F968131FE45806755454B41451293318073B3A1806B", "AKiS v1.3 on infineon chip" }, { "3B9F968131FE5D00640428010231C073F701D000900065", "German eTicketing SAM (Transport)\nhttps://www.eticket-deutschland.de/" }, + { "3B9F968131FE6D00640428010231C073F701D000900055", "VDV-KA Secure Access Module (German Public Transport) (Transport)\nhttps://www.eticket-deutschland.de/" }, { "3B9F968131FE9D006405A0030431C073F701D000900028", "Deutsche Telekom AG, TeleSec PKS ECC Signature Card (PKI)\nhttps://www.telesec.de/en/signaturecard" }, { "3B9F9681B1FE451F070064051EB20031B0739621DB0590005C", "SignTrust (www.signtrust.de)\nInfinion SLE66CX680PE with Starcos 3.2\nhttp://www.deutschepost.de/dpag?xmlFile=link1015459_49595" }, { "3B9F9681B1FE451F070064051EB20331B0739621C005900044", "German Dentist ID (eID)" }, { "3B9F96C00A1FC38031E073FE211B63F100AD830F9000DA", "SIM SFR Pro (French Mobile Operator)" }, + { "3B9F96C00A1FC68031E073FE211F65D00233131B810FFA", "Tinkoff SIM card (Telecommunication)" }, { "3B9F96C00A1FC78031E073FE211B63F100AD830F9000DE", "H3G (Italy) UMTS USIM card" }, { "3B9F96C00A1FC78031E073FE211B65D0011009228100F3", "Verizon 4G LTE SIM Card (Telecommunication)\nhttp://www.verizonwireless.com/support/information/4gsim.html" }, { "3B9F96C00A1FC78031E073FE211B65D0018E0E3281007A", "Rogers 3G SIM card" }, @@ -2702,6 +2993,7 @@ const static atr_t AtrTable[] = { { "3B9F96C00A1FC78031E073FE211F65D0020B11A4810F7E", "Telia Finland Oyj, network operator LTE (Telecommunication)" }, { "3B9F96C00A1FC78031E073FE211F65D013370F3B810FD2", "Gemalto Security Element (PKI)" }, { "3B9F96C00A31FE45435431690B010001000000000000000D", "EU smart tachograph card (driver/company/control/workshop)\nhttps://dtc.jrc.ec.europa.eu/" }, + { "3B9F96C00A31FE45754A6176656C696E2D4F5320312E3051", "Vietnam ID Card (eID)" }, { "3B9F96C00A3FC6A08031E073FE211B65D001740EEB810FD7", "Verizon '4G LTE' USIM (Telecommunication)" }, { "3B9F96C00A3FC6A08031E073FE211B65D001740F50810F6D", "5G (Telecommunication)" }, { "3B9F96C00A3FC6A08031E073FE211F65D001900F3B810FE6", "Verizon US USIM card (Telecommunication)" }, @@ -2715,6 +3007,7 @@ const static atr_t AtrTable[] = { { "3B9F97801FC68031E073FE211B6441442100829000E5", "SAKURA Internet SIM (Telecommunication)\nhttps://www.sakura.ad.jp/services/sim/" }, { "3B9F97801FC68031E073FE211B65CA010E060B81059E", "rain Networks - R15 4G Sim Card (Telecommunication)\nhttp://www.rain.co.za" }, { "3B9F97801FC78031E073FE211367980801120401045B", "Tmobile Sim card (Telecommunication)" }, + { "3B9F97801FC78031E073FE2113679814010202010257", "Orange SIM from Egypt (Telecommunication)" }, { "3B9F97803FC7828031E073FE211F640856210082900019", "eSIM card (Telecommunication)" }, { "3B9F97803FC7828031E073FE211F6409069200829000FB", "Ubigi Transatel esim (Telecommunication)" }, { "3B9F978131FE458065544312210831C073F621808105", "Republic of Turkey Identity Card (eID)\nhttps://bilgem.tubitak.gov.tr/en/icerik/national-identity-card-tr-nc-identity-card" }, @@ -2761,6 +3054,7 @@ const static atr_t AtrTable[] = { { "3BB7110081319043A5..............", "Siemens CardOS/M V1.4 (SLE44C80S)" }, { "3BB7110081319043C517B09D19221E1F", "CryptoTech blank access/authentication card (Other)\nhttp://www.cryptotech.com.pl/" }, { "3BB7110081319053B5..............", "CardOS EM/V1.4 (SLE44CR80S)" }, + { "3BB711008131FE432025854702202866", "Research Institute of Applied Information Technologies (Other)\nhttp://dodatok.osvita.net/" }, { "3BB711008131FE4320283FB50320313B", "Research Institute of Applied Information Technologies (Other)\nhttp://dodatok.osvita.net/" }, { "3BB718008131704310434E5452563253", "Avtor SecureToken (PKI)" }, { "3BB718008131FE6553504B323490005A", "Giesecke & Devrient Starcos 2.4" }, @@ -2770,6 +3064,9 @@ const static atr_t AtrTable[] = { { "3BB794008131FE6553504B32339000D1", "Giesecke & Devrient Starcos 2.3\nDeutsche Bank WebSign (RSA-Card)\nG&D StarSign Token\nOsakidetza ONA (eID)\nhttp://www.osakidetza.euskadi.eus/r85-ckserv01/es/contenidos/nota_prensa/ruedasanidad35/es_rs/ruedasanidad35_c.html" }, { "3BB813008131205D0057696E4361726402", "SmartCard for Windows 1.1" }, { "3BB813008131FA524348544D4F494341A5", "citizen digital certificate (PKI)\nhttp://moica.nat.gov.tw/" }, + { "3BB897008131FE45FFFF148230502300F1", "UAE Emirates ID (eID)\nhttps://www.icp.gov.ae" }, + { "3BB89700813FE45FFFF148230502300F", "UAE Emirates ID (eID)" }, + { "3BB89700C00831FE45FFFF148230502300B8", "Infineon SECORA ID X (JavaCard)" }, { "3BB918008131FE9E8073FF614083000000DF", "Serbian Identity Card\nThis is the new Serbian biometric identity card (every adult cityzen\nmust have). The chip contains owners picture, name, date and place\nof birth, current address, unique ID number and fingerprint." }, { "3BB9940040144747334D4838353330", "T D1 GSM card (Telecommunication)" }, { "3BB9940040144747334E4838363430", "GSM-SIM card of the Austrian mobile phone provider One\nhttp://www.one.at\nProximus SIM - Belgium (SetCOS?)\no2 GSM-SIM card Germany 2003" }, @@ -2790,6 +3087,7 @@ const static atr_t AtrTable[] = { { "3BBA96008131865D0064056002033180900066", "Telesec TCOS 2 (SLE66P)\nTCOS 2.0 (on CX320P)\nTeleSec Netkey Card" }, { "3BBA96008131865D00640560020331809000667001040530C9", "TeleSec Netkey E4 Card" }, { "3BBA96008131865D0064057B0203318090007D", "TeleSec NetKey Card\nDeutsche Post card (tcos)" }, + { "3BBB18008131FE4580670518B1020184008105E0", "STARCOS Smart Card (Other)\nhttps://www.gi-de.com/de/identities/unternehmenssicherheit/signaturkarte" }, { "3BBB1800C01031FE4580670412B003030000810138", "Giesecke & Devrient Star Sign Card, STARCOS 3.0 DI, 72 KB, RSA2048 bit\nGiesecke & Devrient Smartc@fe Expert 32K v2.0" }, { "3BBB1800C01031FE4580670412B00303000081053C", "Philips Smart MX\nSzczecin University of Technology in Poland student identity card (Elektroniczna Legitymacja Studencka = student identity card)\nCSOB bank, Czech Republic\nCATCert (Agencia Catalana de Certificacio) catalan government workers identity card" }, { "3BBB1800C03E31FE654726442054534D20312E30B7", "Italian Tachograph Driver Card" }, @@ -2873,9 +3171,9 @@ const static atr_t AtrTable[] = { { "3BD0A8FF81F1FB24001FC3F4", "Philips DESFire SAM" }, { "3BD218008131FE450101C1", "Dutch License Plate Card (RDW)\nhttps://commons.wikimedia.org/wiki/File:Kentekencard_voorzijde_1_december_2013.jpg" }, { "3BD218008131FE58C90114", "Atos CardOS5 (PKI)\nhttp://atos.net/NR/rdonlyres/17C7BDD0-225B-4A58-B9A4-438EA3F3238A/0/74743_20120830_160149_cardos_v5_0__datenblatt_en.pdf" }, - { "3BD218008131FE58C90217", "WVDP (West Valley Demonstration Project?) (PKI)" }, + { "3BD218008131FE58C90217", "Atos CardOS 5.3 (PKI)\nhttp://www.atos.net/cardos" }, { "3BD218008131FE58C90316", "Austrian 'RKS-Card' issued by GlobalTrust (PKI)\nhttps://secure.globaltrust.eu/info-rksv.html" }, - { "3BD218008131FE58C90411", "Identity Card in Slovakia with security chip and e-signature issued after 2021-06-21 (eID)" }, + { "3BD218008131FE58C90411", "Identity Card in Slovakia with security chip and e-signature issued after 2021-06-21 (eID)\nD-TRUST Card 4.1 Standard, qualified signature card" }, { "3BD218008131FE58CA6076", "CardOS IoT V5.4 (PKI)\nhttps://atos.net/wp-content/uploads/2018/11/ct_181127_lpm_cardos_iot_v5-4_fs_en4_web.pdf" }, { "3BD21802C10A31FE58C80D51", "Siemens Card CardOS M4.4" }, { "3BD396FF81B1FE451F078081052D", "German public health insurance card (Elektronische Gesundheitskarte eGK), 2nd generation (G2) (HealthCare)" }, @@ -2886,6 +3184,7 @@ const static atr_t AtrTable[] = { { "3BD518FF8191FE1FC38073C8211309", "Athena IDProtect Key (v2)\nhttp://www.athena-scs.com/product.asp?pid=33" }, { "3BD5950400AE01020101", "Axalto Cyberflex Access 64K v2b SM 1.1" }, { "3BD595FF8091FE1FC38073C8211385", "Athena IDProtect - Cryptographic Java Card\nhttp://www.athena-scs.com/product.asp?pid=32" }, + { "3BD596028031FE654F734549441F", "AVR128DA32 microcontroller based open source EID smartcard with RSA and ECC. (eID)\nhttps://oseid.sourceforge.io/" }, { "3BD596FF8191FE1FC34332333030CC", "HID Global - Crescendo C2300 (PKI)\nhttps://www.hidglobal.com/products/cards-and-credentials/crescendo/c2300" }, { "3BD6180080B1806D1F038051006110309E", "Atmel/Athena T0 PC/SC Compliance Test Card No. 1" }, { "3BD618008131FE7D415350204F5383", "ASP FIXED CHAL1, 2, 3 and 4 (Other)" }, @@ -2893,14 +3192,17 @@ const static atr_t AtrTable[] = { { "3BD6960081B1FE451F078031C1521118F8", "smart card from NASA, 2019 (PKI)" }, { "3BD6960081B1FE451F878031C152211949", "DHS CAC card (PKI)" }, { "3BD6960081B1FE451F878031C152411A2A", "Identiv SCR3310 v2.0 (eID)" }, - { "3BD6970081B1FE451F078031C1521118F9", "NASA Personal Identity Verification (PIV) card (eID)" }, + { "3BD6970081B1FE451F078031C1521118F9", "NASA Personal Identity Verification (PIV) card (eID)\nIDEMIA Cosmo V8.0 with a PIV applet" }, { "3BD6970081B1FE451F078031C1521119F8", "Secure badge (PKI)" }, { "3BD6970081B1FE451F878031C152211948", "DOS PIV (PKI)" }, { "3BD6970081B1FE451F878031C152211A4B", "ID-One PIV 2.4 (P/N 1501381) from IDEMIA (Other)" }, - { "3BD6970081B1FE451F878031C152411A2B", "Oberthur Technologies ID-One PIV/CIV on V8 Device (eID)\nhttps://csrc.nist.gov/csrc/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp2986.pdf" }, + { "3BD6970081B1FE451F878031C152411A2B", "Oberthur Technologies ID-One PIV/CIV on V8 Device (eID)\nhttps://csrc.nist.gov/csrc/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp2986.pdf\nIDEMIA Cosmo V8.1 with a PIV applet" }, + { "3BD81800801F078031C1640806920F", "US Government CAC (PKI) / IDEMIA Cosmo v8 (PKI)\nhttps://www.idemia.com/id-one-piv-card" }, + { "3BD81800801F078031C1640806920FDF", "US DoD Common Access Card (IDEMIA Cosmo v8) (PKI)" }, { "3BD8180080B1FE451F078031C1640806920FD5", "Oberthur Cosmo v8 (PKI)" }, { "3BD818FF8131FE458064041BB42A8105D5", "Schweizerische Krankenversicherungskarte KVG (HealthCare)" }, { "3BD818FF81B1FE451F038064041AB403810561", "D-Trust multicard advanced 3.1\nGerman public health insurance card ('Gesundheitskarte'), issuer SBK 'Siemens Betriebskrankenkasse'" }, + { "3BD896008031FE4400531200840F90001F", "Cape Verde National Identity Card (CNI) (eID)\nhttps://sniac.cv/cartao-nacional-de-identificacao/" }, { "3BD8960081B1FE451F0743485447504B494ADD", "Citizen Digital Certificate, Taiwan (PKI)\nhttp://moica.nat.gov.tw/" }, { "3BD896FF8131FE458064041BB42A81055B", "Swiss LAMal health insurance card" }, { "3BD911008131FE8D0000464F4D53312E3132", "TFOMS (eID)\nhttp://www.samtfoms.ru" }, @@ -2919,13 +3221,17 @@ const static atr_t AtrTable[] = { { "3BDA13FF8131FB468012392F31C173C601C03B", "My Number Card (The Social Security and Tax Number System in JAPAN) (eID)\nhttp://www.cas.go.jp/jp/seisaku/bangoseido/english.html" }, { "3BDA1802C10A31FE584B53776973735369676EA9", "SuisseID Post - ATOS CardOS 4.x (eID)\nhttps://postsuisseid.ch/" }, { "3BDA18FF8191FE1FC350564A434F503353494472", "J3H145 (P6 SecID) purchased from FUTAKO Ltd., Taiwan (JavaCard)\nhttp://www.javacardsdk.com" }, + { "3BDA18FF8191FE1FC380641211111073C0C1801B", "Belarus national identity card (passport)\nhttps://eng.belta.by/infographica/view/id-cards-in-belarus-7095/" }, { "3BDA18FF81B1FE451FC3546963546F6B20322E3068", "TikTok 2.0 (PKI)" }, { "3BDA18FF81B1FE451FC3546963546F6B20332E3069", "TicTok 3.0 (PKI) (PKI)\nhttps://en.cryptoshop.com/products/cryptas-tictok-v3-basisartikel.html" }, { "3BDA18FF81B1FE751F030031C573C001400090000C", "OpenPGP Card V2" }, { "3BDA18FF81B1FE751F030031F573C001600090001C", "OpenPGP Card V3" }, { "3BDA9400004D4D41524A2B33399000", "SIM card from Vodafone Spain" }, { "3BDA9500400A2508004053434F535441", "Card used for issuing commodity to benificiary for rice, wheat and more (Other)" }, + { "3BDA96FF8031FE454D696E694C6F61646572AB", "iClass SE Processor (Miniloader Mode) (Other)\nhttps://www.hidglobal.com/products/embedded-modules/iclass-se/sio-processor" }, + { "3BDA96FF8031FE45536E6D704C6F61646572A8", "iClass SE Processor (SNMP Loader Mode) (Other)\nhttps://www.hidglobal.com/products/embedded-modules/iclass-se/sio-processor" }, { "3BDA96FF8131FE45805631B85349434181057B", "STARCOS 3.5 ID ECC C1R (PKI)\nhttps://www.gi-de.com/fileadmin/user_upload/MS/Certificates/STARCOS35_ID_ECC_TABLES.pdf" }, + { "3BDA96FF8191FE1FC343323330302D46495053E2", "Crescendo 2300 FIPS (JavaCard)" }, { "3BDA96FF81B1FE451F0780584943412056322E30E9", "Starcos chip card from Giesecke & Devrient (PKI)\nhttps://ica.cz/functionality-smart-card" }, { "3BDB11FF5000FF0000000000000007921603", "NEC V-WAY64 v2.1" }, { "3BDB1800801F030031C06477E303008290004F", "Oberthur ID-One Cosmo 64K V5.2" }, @@ -2948,9 +3254,11 @@ const static atr_t AtrTable[] = { { "3BDB960080B1FE451F830031C064102301000F900063", "Extremenian Health Service target (HealthCare)\nhttps://saludextremadura.ses.es/web/preguntas-frecuentes" }, { "3BDB960080B1FE451F830031C0641A1801000790005A", "Ercom CRYPTOSMART\nhttp://www.ssi.gouv.fr/entreprise/qualification/gamme-cryptosmart-pour-la-securisation-des-smartphones-et-des-tablettes/" }, { "3BDB960080B1FE451F830031C0641A1801000F900052", "Serbian Car registration ID card\nhttp://blog.goranrakic.com/archives/2011/07/citanje_saobracajne_dozvole_sa_cipom.html" }, - { "3BDB960080B1FE451F830031C0641A71010007900033", "ChamberSign Gemalto USB Shell Token V2 - Certificat Audacio ** (eID)\nhttp://www.chambersign.fr/certificat-rgs-audacio/" }, + { "3BDB960080B1FE451F830031C0641A71010007900033", "ChamberSign Gemalto USB Shell Token V2 - Certificate Audacio ** (eID)\nhttp://www.chambersign.fr/certificat-rgs-audacio/" }, { "3BDB960080B1FE451F830031C064B0FC100007900005", "Oberthur Cosmo V7 debug card (SDK)" }, { "3BDB960080B1FE451F830031C064B0FC10000F90000D", "ID-One PIV (that's the only non-numeric identifying mark) (PKI)" }, + { "3BDB960080B1FE451F830031C064BAFC10000790000F", "Oberthur ID-One Cosmo v7.0 80K (eID)\nhttps://www.ssi.gouv.fr/uploads/IMG/certificat/ANSSI-CC-cible_2011-64en.pdf" }, + { "3BDB960080B1FE451F830031C064BAFC10000F900007", "Oberthur ID-One Cosmo v7.0 (PKI)\nhttps://csrc.nist.rip/groups/STM/cmvp/documents/140-1/140sp/140sp1236.pdf" }, { "3BDB960080B1FE451F830031C064BE1B0100019000FB", "Bank card" }, { "3BDB960080B1FE451F830031C064C30801000F90009B", "SIM Aruba (Italian provider)" }, { "3BDB960080B1FE451F830031C064C7FC100001900074", "Oberthur Cosmo (eID)\nhttp://www.stampit.org" }, @@ -2959,10 +3267,12 @@ const static atr_t AtrTable[] = { { "3BDB960080B1FE451F830031E85427E6040007900084", "Polish encard (eID)" }, { "3BDB960080B1FE451F830031E85427E604000F90008C", "Token card from iBRE CompanyNet (mbank) (Bank)" }, { "3BDB960080B1FE451F834553544F4E49412D65494455", "Estonian Identity Card (ID-One Cosmo v8.1) (eID)" }, + { "3BDB960080B1FE451F870031C1640958223607900019", "Idemia Solvo Fly 40 (JavaCard)" }, { "3BDB960081B1FE451F0380F9A0000003080000100018", "Oberthur CS PIV End Point v1.08 FIPS201 Certified" }, { "3BDB960081B1FE451F0380F9A0000003480000000149", "Fly Clear card" }, { "3BDB960081B1FE451F8380F9A0000003080000100098", "Oberthur Cosmo v7 128K with PIV applet\nhttp://www.smartcardfocus.com/shop/ilp/id~410/p/index.shtml" }, - { "3BDB96FF80B1FE451F870031C164093772130F9000F4", "French ID Card 2021 (eID)\nhttps://ants.gouv.fr/Les-titres/Carte-nationale-d-identite/La-puce-de-la-nouvelle-carte-nationale-d-identite" }, + { "3BDB96FF80B1FE451F870031C164093364490F9000BC", "cnie Carte Nationale d'Identite Electronique (eID)" }, + { "3BDB96FF80B1FE451F870031C164093772130F9000F4", "French ID Card 2021 (eID)\nhttps://ants.gouv.fr/nos-missions/les-titres-produits-par-l-ants/les-documents-d-identite/la-puce-de-la-nouvelle-carte-nationale-didentite" }, { "3BDB96FF8131FE4580670534B50201064081051B", "SINA STARCOS 3.5 BX-CombiCard+HSB (Other)" }, { "3BDB96FFC01031FE4580671501B403000900810521", "Digital Tachograph Card for Professional Driver\nolish driver card for digital tachograph" }, { "3BDC1802C10A31FE588031A873B0019B2460071320AA", "Public Services Card | Ireland (Other)\nhttps://psc.gov.ie/" }, @@ -2972,6 +3282,7 @@ const static atr_t AtrTable[] = { { "3BDC18FF8111C18073C821136605036351000233", "GoTrust Idem Key (Other)\nhttps://www.gotrustid.com/idem-key" }, { "3BDC18FF8111FE8073C82113660106013080018D", "Aladdin R.D. JaCarta LT (eID)" }, { "3BDC18FF8191FE1FC3060A2B06010401E910050203D1", "Caregiver card for Dutch Medical System called UZI (Unieke Zorgverlener Identificatie, Caring Unique Identification) (HealthCare)\nhttps://www.uziregister.nl/" }, + { "3BDC18FF8191FE1FC3060A2B06010401E910050204D6", "UZI (HealthCare)" }, { "3BDC18FF8191FE1FC38073C821136601060130040155", "Athena IDProtect Key Laser" }, { "3BDC18FF8191FE1FC38073C821136601061159000128", "JaCarta (PKI)\nhttp://www.aladdin-rd.ru" }, { "3BDC18FF8191FE1FC38073C8211366010B0352000538", "Athena IDProtect Smart Card Logon Card" }, @@ -2979,10 +3290,14 @@ const static atr_t AtrTable[] = { { "3BDC18FF8191FE1FC38073C821136605036057000255", "NXP IDProtect (X) (JavaCard)" }, { "3BDC18FF8191FE1FC38073C821136605036351000250", "JCOP3 SecID P60 CS (JavaCard)" }, { "3BDC96FF8111FE8031C8544356300573FFA1C03B", "NXP Javacard with Athena SCS OS (JavaCard)" }, + { "3BDC96FF8111FE8031C8544356350773FFA1C03C", "NXP JCOP 4, J3R200P0X3U/0ZA16CP NXD6.2 (JavaCard)" }, { "3BDC96FF81B1FE431FC30B46415245474F53414D5632CC", "Multismartcard SAM (used in proprietary Scheidt&Bachmann Smartcard Reader v2)" }, { "3BDD18008131FE4580F9A0000000770100700A90008B", "National ID Card of Peru issued by RENIEC from Oberthur" }, { "3BDD18008131FE4580F9A000000077010800079000FE", "Oberthur Cosmo v7 IAS ECC\nBrazilian 'e-CNPJ' card, issued by Certisign (Oberthur)" }, { "3BDD18008131FE45904C41545649412D65494490008C", "Identity card (eID) Republic of Latvia\nhttp://www.pmlp.gov.lv/lv/pakalpojumi/passes/eid.html" }, + { "3BDD18008191FE1FC3006646530803003671DF00008097", "Feitian K9Plus - ePass FIDO-NFC with PIV (Other)\nhttps://ftsafe.com/products/FIDO/NFC" }, + { "3BDD18FF8191FE1FC3006646530803003671DF00008068", "Feitian FIDO NFC Plus K9 Security Key (Other)\nhttps://www.ftsafe.com/products/FIDO/NFC" }, + { "3BDD18FF8191FE1FC3FF4F70656E506879736963616CF6", "Open Physical PIV-Compatible NXP SECID P60 (eID)\nhttps://openphysical.org/" }, { "3BDD18FFC080B1FE451FC30068D276000028040411009000C9", "Russian Federation driver card for the digital tachograph\nPolish driver card for digital tachograph" }, { "3BDD18FFC080B1FE451FC30068D276000028040971009000A4", "Worktime/driving style monitoring card (Transport)\nhttp://www.paetronics.fi/en/" }, { "3BDD96008010FE8031806301FFC073B3211B8105", "BIFIT iBank 2 USB Key (Bank)\nhttp://bifit.ua" }, @@ -3012,10 +3327,11 @@ const static atr_t AtrTable[] = { { "3BDF18008131FE588031B05202046405C903AC73B7B1D422", "Austrian 'e-Card' (=Health Card) of the 4th generation. (HealthCare)" }, { "3BDF18008131FE58AC31B05202046405C903AC73B7B1D422", "e-Card Austria (HealthCare)" }, { "3BDF18008131FE67005C49434DD49147D276000038330058", "Infineon SICRYPT Card Module D4 PC/SC Compliance Test Card" }, - { "3BDF18008131FE7D006B020C0182011101434E53103180FC", "Italian healtcare card (TS) National Service Card (CNS) (HealthCare)" }, - { "3BDF18008131FE7D006B040C0184011101434E53103180FC", "Italian healthcare card (TS) National Service Card (CNS) (HealthCare)\nhttp://www.regione.toscana.it/cartasanitaria" }, + { "3BDF18008131FE7D006B020C0182011101434E53103180FC", "Italian healthcare card (TS) National Service Card (CNS) (HealthCare)" }, + { "3BDF18008131FE7D006B040C0184011101434E53103180FC", "Italian healthcare card (TS) National Service Card (CNS) (HealthCare)\nhttp://www.regione.toscana.it/cartasanitaria\nhttps://www.agid.gov.it/it/piattaforme/carta-nazionale-servizi" }, { "3BDF18008131FE7D006B150C0180011101434E53103180E9", "Provider: Actalis S.p.A.\ncode: AT00006181\nhttp://www.actalis.it" }, { "3BDF18008131FE7D006B150C0181011101434E53103180E8", "Italian healthcare card (TS) National Service Card (CNS)\nCarta Regionale dei Servizi - Regione Lombardia\nTuscany TS-CNS\nhttp://www.regione.toscana.it/cartasanitaria" }, + { "3BDF18FF8091FE1FC3003138644790ECC273140150829000BB", "Ministry of Interior - France 'Agent Card' (Other)" }, { "3BDF18FF8131FE4580590180484944433730307300011B33", "Crescendo C700 + MiFare 4K\nhttp://www.smartcardfocus.com/shop/ilp/id~265/p/index.shtml" }, { "3BDF18FF8191FE1FC3003138644790ECC273140150829000BA", "Card store authentication and signature keys (JavaCard)\nhttps://ants.gouv.fr/Les-titres/Cartes-Agents/Adhesion/Telechargement-et-support" }, { "3BDF18FF8191FE1FC30031B8640000EC0073940000829000FE", "NXP Jcop3 P60 ChipDoc v7b4 (JavaCard)\nhttps://www.javacardos.com/store/products/10029" }, @@ -3025,6 +3341,7 @@ const static atr_t AtrTable[] = { { "3BDF18FF81F1FE43003F03834D494641524520506C75732053414D3B", "NXP SAM AV2 module" }, { "3BDF18FF81F1FE43003F03834D4946506C75732053414D3B53414D3B", "Mifare SAM -AV2 (PKI)" }, { "3BDF18FF81F1FE43003F07834D49464152452053414D204156333011", "NXP SAM AV3 module" }, + { "3BDF18FF910131FE4680319052410264050200AC73D622C099", "Acos-ID (AUSTRIACARD's Operating System) (Other)\nhttps://www.austriacard.com/digital-security/solutions/card-solutions/acos-id/" }, { "3BDF94FFC080B1FE451F03006AD2760000280415FA10040090006B", "UK Digital Tacho card (Other)" }, { "3BDF94FFC080B1FE451F03006AD2760000280415FA40040090003B", "DVLA Company Card (Transport)" }, { "3BDF9500801F878031A073FF21006345B105830F900060", "FUTURE CARD Normal ISO SIM (Telecommunication)" }, @@ -3038,13 +3355,17 @@ const static atr_t AtrTable[] = { { "3BDF960080B1FE451F838073BC9180F9A00000007780080201A4", "Latvian eSignature card (eID)\nhttps://www.lvrtc.lv/e-signature.html" }, { "3BDF960080B1FE451F870031C16408923201738421E0059000C5", "Company Card for Transport companies (Transport)" }, { "3BDF96008131FE4541434F532D4944303032382E3031366F", "Sri Lankan driving license [ web: motortraffic.gov.lk ] (eID)\nhttp://www.motortraffic.gov.lk/web/index.php?option=com_content&view=article&id=83&Itemid=140&lang=en" }, + { "3BDF96008131FE588031B05202056405A100AC73D622C020", "Austrian health insurance card 'e-card' (HealthCare)\nhttps://de.wikipedia.org/wiki/E-card_(Chipkarte)" }, { "3BDF960081B1FE451F838073CC91CBF9A0000003080000100079", "Test PIV Cards available for sale from NIST\nhttp://csrc.nist.gov/groups/SNS/piv/testcards.html" }, { "3BDF96FF8131FE455A018048494443313158587300011B09", "HID Crescendo iCLASS Px G8H" }, { "3BDF96FF8131FE4580590180504956434C41537300011BDE", "HID Global pivCLASS v1.0 (PKI)\nhttp://www.hidglobal.com/products/cards-and-credentials/pivclass/pivclass-smart-card" }, { "3BDF96FF8131FE45805B44452E42415F53433335328105B5", "Employee ID card from the Federal Employment Agency (Germany) (PKI)\nhttps://www.pki.arbeitsagentur.de/" }, { "3BDF96FF8131FE45805B44452E424E4F544B3130308105A0", "BeA - Certification Card for German Solicitors (Other)\nhttps://bea.bnotk.de/" }, + { "3BDF96FF81B1FE451F870031B96409377213738401E000000000", "National Identity Card of Slovakia (eID) (eID)\nhttps://en.wikipedia.org/wiki/Slovak_identity_card" }, + { "3BDF96FF910131FE4680319052410264050200AC73D622C017", "Acos-ID (AUSTRIACARD's Operating System) (Other)\nhttps://www.austriacard.com/digital-security/solutions/card-solutions/acos-id/" }, { "3BDF97008131FE588031B05202056405A100AC73D622C021", "Austrian healthcare insurance identification card (HealthCare)\nhttps://www.chipkarte.at" }, { "3BDF970081B1FE451F838073CC91CBF9A0000003080000100078", "NASA PIV Card (Other)" }, + { "3BE000008131104505", "Emoney indonesia (Bank)" }, { "3BE000008131204030", "SmarTEC" }, { "3BE000FF8131FE4514", "'JUKICARD', digitally sign tax documents in Japan" }, { "3BE20000402049..", "Schlumberger Cryptoflex 4k" }, @@ -3056,6 +3377,7 @@ const static atr_t AtrTable[] = { { "3BE500008121459C100100800D", "BIN 470132 -- BANK OF AMERICA VISA DEBIT -- GEMALTO MGY 0 U1090788B - 12/14 F8 00 89 (Bank)" }, { "3BE500008131FE45D00037008089", "ATM card for Standard Chartered, Taiwan" }, { "3BE500FF8131FE458073C601082D", "MUFG (.jp) (Bank)" }, + { "3BE6000080318066B1A30401110B83", "Java Jcop J2A040 (JavaCard)" }, { "3BE6000080318066B1A30401110B83009000", "VISA Credit Card (Bank)" }, { "3BE60000812145324B010101017A", "Axalto Cyberflex Palmera V5" }, { "3BE600FF8131FE4544492030324D70", "Alior Sync (Poland) - MasterCard Debit Card with PayPass (Bank)\nMasterCard Contactless Debit Card issued by Raiffeisen Bank in Czech Republic\nDebit MasterCard with paypass issued by Bank Zachodni WBK (Poland)\nDebit MasterCard with paypass issued by AliorSync" }, @@ -3075,6 +3397,7 @@ const static atr_t AtrTable[] = { { "3BE700FF8131FE458031C073C62148BE", "Japanese ETC (Electronic Toll Collection System) card (Transport) and Credit card (Issuer: Toyota) (Transport)" }, { "3BE700FF81B1FE451F018031C073C6214820", "Japanese ETC (Electronic Toll Collection System) card (Transport)" }, { "3BE8000040FA0073C84011009000", "KEBTechnology KONA USB SmartCard (Other)" }, + { "3BE800008031FE450073C8401300907DE7", "National Health System of Spain - Consejeria de Sanidad y Servicios Sociales - JUNTA DE EXTREMADURA (HealthCare)\nhttps://www.juntaex.es/es/lajunta/consejo-de-gobierno/vicepresidencia-segunda-y-consejeria-de-sanidad-y-servicios-sociales/servicio-extremeno-de-salud" }, { "3BE80000813120450073C8400000900056", "Visa credit card for Standard Chartered, Taiwan" }, { "3BE800008131FE00506572736F53696DAA", "Simulated virtual smartcard, from project PersoSim (eID)\nhttps://persosim.de/?q=node/33" }, { "3BE800008131FE450073C8400000900088", "VISA Card (Skandinaviska Enskilda Banken) with Swedish BankID\nVISA card (Chinatrust Bank (Taiwan), dual-interface card with a Taipei Metro e-purse function)" }, @@ -3102,7 +3425,7 @@ const static atr_t AtrTable[] = { { "3BE900FFC11031FE55C80120504E34303132AD", "Siemens CardOS/M 3.0 (SLE66CX160S)" }, { "3BEA0000813120438065A2........72D6....", "IDClassic 3XX Cards (with MPCOS Applet)" }, { "3BEA0000813120438065A20101013D72D643A5", "GemXpresso Pro R3 32PK (MPCOS, T=1) (warn reset)" }, - { "3BEA00008131FE450031C173C840000090007A", "Nigerian eID Card (cold reset)\nChip is NXP JCOP 2.4.1R3" }, + { "3BEA00008131FE450031C173C840000090007A", "Nigerian eID Card (cold reset)\nChip is NXP JCOP 2.4.1R3\nDual BCR Signum Mastercard (bank) + Digital Signature Costa Rica (eID)\nhttps://bancobcr.com/wps/portal/bcr/bancobcr/personas/tarjetas/signum_firma_digital/" }, { "3BEA00008131FE45436F6D624F5320494900FE", "UBS VISA Gold Card\nMasterCard from lhv.ee\nNordea Bank Finland PLC Estonian Branch (ABnote)" }, { "3BEA00008131FE454A434F5033315632333290", "NAB VISA Debit card" }, { "3BEA00008131FE454A434F5034315632323195", "HID Crescendo C700\nhttps://www.hidglobal.com/products/cards-and-credentials/crescendo/c700" }, @@ -3131,6 +3454,7 @@ const static atr_t AtrTable[] = { { "3BEE00008131804280318066B0840C016E01830090008E", "MultiApp Cards (Easy 72K Type B and Combi 72K Type B)\nE.SUN Commercial bank debit master card (Bank)\nTaiwan EasyCard (Transport)\nhttps://www.easycard.com.tw/english/index.asp" }, { "3BEE00008131804380318066B1A1110100F683009000", "Optelio/Desineo Cards (D72 FXR1)" }, { "3BEE00008131804380318066B1A11101A0F683009000", "Optelio D72 FXR1 (MD) T=1" }, + { "3BEE00008131804380318066B1A30401110B83009000D4", "Japan Post Bank cash card (Bank)\nhttps://www.jp-bank.japanpost.jp/kojin/chokin/sogou/kj_cho_sg_iccard.html" }, { "3BEE00008131FE45003180718665016702A00A8390001B", "IBM JCOP 'Java Card 2.1.1' et 'Open Platform 2.0.1'" }, { "3BEE00008131FE4580318066409093060F1783019000FD", "Health insurance (HealthCare)" }, { "3BEE00008131FE4580318066409093060F17830F9000F3", "IC card for the National Health Insurance, Taiwan" }, @@ -3173,6 +3497,7 @@ const static atr_t AtrTable[] = { { "3BEF00FF813166456563202049424D20332E3120202020", "IBM eCash" }, { "3BEF00FF813166456563202049424D20332E3120202020CF", "IBM eCash" }, { "3BEF00FF8131864549424D204D4643343030303038333143", "ComCard MFC 4.1" }, + { "3BEF00FF8131FE4141434F532046696F6E6131204C633666", "EUROBANK GR (Bank)\nNBG BANK (Bank)" }, { "3BEF00FF8131FE4541434F53204449616E6131204C63364E", "comdirect VISA card (Bank)\nhttps://www.comdirect.de/konto/karten.html#Visa-Karte" }, { "3BEF00FF8131FE4541434F53204449616E6132204C63364D", "Alior Bank SA (Bank)\nhttps://www.aliorbank.pl/" }, { "3BEF00FF8131FE4565630D12810156001F00006686080122", "Commerzbank signature card SECCOS (6 or 7) providing RAH security profile (Bank)\nhttps://www.chipkartenleser-shop.de/commerzbank/electronic-banking-chipkarten/commerzbank-signaturkarte-2710050006" }, @@ -3219,11 +3544,12 @@ const static atr_t AtrTable[] = { { "3BEF00FF8131FE458031C06B49424D204A65745A204D3239", "UBS Internet Card (IBM JetZ M2)" }, { "3BEF00FF8131FE458031E06B042105026B55555555555568", "MasterCard credit card for Mega International Commercial Bank, Taiwan (Bank)\nhttps://www.megabank.com.tw/creditcard/index.asp" }, { "3BEF00FF8131FF6549424D204D4643393232393238393017", "IBM MFC 4.22 (University of Cambridge smartchip card)" }, + { "3BF01100FF01", "Not a physical smart card. But a JavaCard simulator ATR with default configuration. (JavaCard)" }, { "3BF01200FF9181B17C451F019B", "Japanese Chijou Digital B-CAS Card (pay TV) (Pay TV)" }, { "3BF01200FF9181B17C451F0399", "Japanese Chijou Digital B-CAS Card (pay TV)" }, { "3BF01200FF9181B1EF451F030A", "Japanese Digital CATV C-CAS card" }, { "3BF01300001000", "MasterCard ETEC InterOp 27. This is an dual-app Maestro/MasterCard Credit EMV test card" }, - { "3BF01300008131FE45E8", "Healt care card Romania (HealthCare)\nhttp://www.cnas.ro/casmb/national-page/cardul-national-de-asigurari-de-sanatate-2.html" }, + { "3BF01300008131FE45E8", "Healthcare card Romania (HealthCare)\nhttp://www.cnas.ro/casmb/national-page/cardul-national-de-asigurari-de-sanatate-2.html" }, { "3BF01300FF9181B1FE461F0319", "Japan BS/CS 4K Satellite Broadcasting A-CAS Card (Pay TV)\nhttp://www.acas.or.jp/index.html" }, { "3BF2180000C10A31FE55C80675", "HID iCLASS P16K C4H\nproximity card used for both door locks and keystore" }, { "3BF2180002C10A31FE55C80776", "Siemens CardOS V4.3" }, @@ -3276,8 +3602,10 @@ const static atr_t AtrTable[] = { { "3BF711000140967070370E6CB6D6", "Carte pour decodeur cable numerique (fourni par www.voo.be et\nwww.ledecodeur.be)" }, { "3BF711000140967070670E6CB6D6", "UK TopUp TV" }, { "3BF711000140967071090E6CB6D6", "Carte pour decodeur tele de Neuf Telecom TV" }, + { "3BF71300008131FE4500C08AC80C658185", "NXP JCop (JavaCard)" }, { "3BF71300008131FE45464F4D534F4D53A9", "Health card Russian Federation" }, { "3BF71300008131FE454A434F503234....", "NXP JCOP v2.4.x (see hist bytes for more info)" }, + { "3BF71300008131FE4580654A5030310415", "Nichizeiren Denshi-shomei (eID)\nhttps://www.nichizeiren.or.jp/taxaccount/auth/fifth/" }, { "3BF71800008031FE45736674652D6E66C4", "SmartCafe Expert 3.2 72K" }, { "3BF71800008031FE45FE42475265494424", "Bulgarian eID PKI card pilot on IFX SLE78 jTOP (PKI)" }, { "3BF71800008131FE458055433776706B28", "Only labeled 'J35110', dual interface (JavaCard)" }, @@ -3292,13 +3620,15 @@ const static atr_t AtrTable[] = { { "3BF81300008131FE15597562696B657934D4", "Yubico Yubikey 4 OTP+CCID" }, { "3BF81300008131FE4546494445534D4F318E", "Fidesmo Card with Dual Interface (JavaCard)\nhttp://shop.fidesmo.com/product/fidesmo-card-dual-interface" }, { "3BF81300008131FE454A434F50763234....", "NXP JCOP v2.4.x (see hist bytes for more info)" }, - { "3BF81300008131FE454A434F5076323431B7", "Nigerian eID Card (blank card)\nChip is NXP JCOP 2.4.1R3" }, + { "3BF81300008131FE454A434F5076323431B7", "Nigerian eID Card (blank card)\nChip is NXP JCOP 2.4.1R3\nBank of Hawaii (Bank)\nhttps://www.boh.com/\nLA BANQUE POSTALE (Bank)\nhttps://www.labanquepostale.fr/\nbnpparibas (Bank)\nhttp://www.bnpparibas.com/\nJcop040 (JavaCard)\nJcop21 (JavaCard)\nVisa debit classic (Bank)\nhttp://www.jpmorganchase.com/\nJPMorgan Chase (Bank)\nVisa (Bank)\nhttps://unitedfcu.com/" }, { "3BF81300008131FE454A4F5076323431B7", "Nigerian eID card (eID)" }, { "3BF81300008131FE455049564B45593730FF", "PIVKey CP70 (PKI)\nhttps://pivkey.com/" }, { "3BF81300008131FE455241414B43327635CB", "Raak C2 Smart Card (PKI)\nhttp://www.raaktechnologies.com/software-downloads-documentation/" }, { "3BF81300008131FE45534B555001000000FC", "Silesian Card of Public Services (Transport)\nhttps://portal.kartaskup.pl/" }, { "3BF81300008131FE45536D617274417070F8", "national Lithuania ID card" }, { "3BF81300008131FE45FF4A32413034300012", "MIDAS Card Diversification Key JavaCard (J2A040) (Bank)\nhttps://github.com/kategray/midas" }, + { "3BF81300FF108053430663010F900000", "Affinity CUIA Debit (JavaCard)\nhttps://www.affinitycuia.org" }, + { "3BF81300FF910131FE41534C4A01305023100D", "Walden Mutual Bank (Bank)\nhttps://www.waldenmutual.com/sustainable-bank-for-individuals#footnote-s0-4" }, { "3BF81800008031FE450073C8401300900092", "G&D StarSign Token" }, { "3BF81800008131FE450073C8400000900080", "NXP JCOP 31 V2.2 36K - S/C I/F" }, { "3BF81800008131FE450073C8401300900093", "Giesecke & Devrient Sm@rtCafe Expert 3.0" }, @@ -3306,6 +3636,7 @@ const static atr_t AtrTable[] = { { "3BF81800008131FE454A434F5076323431BC", "NXP J2A080 JavaCard" }, { "3BF81800FF8131FE450073C840000090007F", "NXP JCOP 10\nNXP JCOP 31 (contact interface)" }, { "3BF81800FF8131FE454A434F507632343143", "VIVOtech SAM\nNXP JCOP V241\nNXP J3A081 JavaCard (contact interface)" }, + { "3BF89600008031FE470073C840000090000D", "Italian driver tachograph smartcard (Transport)\nhttps://www.to.camcom.it/cartatachigrafica" }, { "3BF89600008131FE4400739401C00F9000DD", "fourth-generation Hong Kong permanent identity card (Other)\nhttps://en.wikipedia.org/wiki/Hong_Kong_identity_card" }, { "3BF89600008131FE454A434F507632343132", "NXP JCOP 2.4.1 (JavaCard)" }, { "3BF91100008131FE45436F6D624F53205600AA", "VISA Card (Bank)" }, @@ -3334,6 +3665,9 @@ const static atr_t AtrTable[] = { { "3BF99600008031FE4553434537200000202027", "G&D SmartCafe Expert 7 (JavaCard)" }, { "3BF99600008031FE4553434537200300204642", "ActivIdentity Activkey Sim (PKI)\nhttps://www.hidglobal.com/products/cards-and-credentials/activid/usb-tokens" }, { "3BF99600008031FE4553434537200F0020464E", "Giesecke & Devrient (DoD Alternate Token) G+D Sm@rtCafe Expert v7.0 144K DI #3 (PKI)" }, + { "3BF99600008031FE4553434537202000202007", "Serbian Identity Card (eID) (eID)" }, + { "3BF99600008031FE45534345372047434E335E", "Serbian Identity Card (eID)" }, + { "3BF99600008031FE455343453720474E335E", "Serbian Identity Card (eID)" }, { "3BF99600008131FE45454F4E43617264563173", "eONCard V1 (PKI)" }, { "3BF99600008131FE4553434537200E00202028", "Giesecke & Devrient GmbH StarSign CUT S" }, { "3BF99600008131FE45535049564B4559373028", "Taglio PIVKey C980 (PKI)\nhttps://www.pivkey.com" }, @@ -3348,25 +3682,32 @@ const static atr_t AtrTable[] = { { "3BFA1300008131FE450031C173C8400000900079", "Nigerian eID Card (warm reset)\nChip is NXP JCOP 2.4.1R3" }, { "3BFA1300008131FE454465786120434620763198", "Dexa Systems Crossfire Card (PKI)\nhttp://www.dexasystems.com/products-services/products/dexa-smartcards-credential-tokens-peripherals" }, { "3BFA1300008131FE454A434F503.3.56323332..", "JCOPxx/yy v2.3.2 (see hist bytes for more info)" }, + { "3BFA1300008131FE454A434F50323156323331", "J2A040 JCOP (JavaCard)" }, { "3BFA1300008131FE454A434F5032315632333191", "NXP JCOP 21 V2.3.1 36K" }, { "3BFA1300008131FE454A434F5032315632343196", "NXP JCOP 2.1 V 2.4.1 (JavaCard)" }, { "3BFA1300008131FE454A434F50343156", "JCOP41 V221" }, { "3BFA1300008131FE454A434F5034315632333197", "JCOP41 /72K (eID)" }, { "3BFA1300008131FE454A434F50763234........", "NXP JCOP v2.4.x (see hist bytes for more info)" }, + { "3BFA1300008131FE54A434F503233191", "Jcop (JavaCard)" }, { "3BFA1300FF813180450031C173C00100009000B1", "OpenPGP" }, { "3BFA1800008031FE45FE654944202F20504B4903", "Estonian Identity Card (EstEID v3.5 (10.2014) cold) (eID)\nhttp://id.ee/" }, { "3BFA1800008131FE4506082A841001876E0805BC", "Dutch Rijkspas (eID)" }, - { "3BFA1800008131FE4506082A841001876E0807BE", "Rijkspas (identification card dutch government employees) (eID)\nhttps://nl.wikipedia.org/wiki/Rijkspas\nDutch goverment multifunctional smartcard (Other)\nhttps://nl.wikipedia.org/wiki/Rijkspas" }, - { "3BFA1800008131FE45060860841001876F0602FE", "Card used by the Dutch health insurers to give medical personell access to patient insurance information" }, + { "3BFA1800008131FE4506082A841001876E0807BE", "Rijkspas (identification card dutch government employees) (eID)\nhttps://nl.wikipedia.org/wiki/Rijkspas\nDutch government multifunctional smartcard (Other)\nhttps://nl.wikipedia.org/wiki/Rijkspas" }, + { "3BFA1800008131FE45060860841001876F0602FE", "Card used by the Dutch health insurers to give medical personnel access to patient insurance information" }, + { "3BFA1800008131FE4546534A434F503453494480", "NXP Java Card JCOP4 P71 GP2.3 JC3.0.5 (JavaCard)\nhttps://www.javacardsdk.com/product/j3r180sim/" }, { "3BFA1800008131FE454A33413034305632343184", "NXP J3A 40K\nJava Card v2.2.2 - Global Platform v2.2.1\nDual-interface functionality (features 1K Mifare emulation)" }, { "3BFA1800008131FE454A33413038315632343189", "NXP JCOP CJ3A081\nhttp://www.usmartcards.com/media/downloads/492/NXP%20P5CX012%2002X%2040%2073%2080%20144%20%20%202011.pdf" }, + { "3BFA1800008131FE454A3344303831563234328F", "AustriaCard Dual Interface Unpersonalized EMV Cards (Bank)\nhttps://www.austriacard.com" }, { "3BFA1800008131FE454A434F503431563232319D", "NXP JCOP 41 v2.2.1 72k SmartCard I/F" }, { "3BFA1800008131FE454A546178436F72655631B2", "Taxpayer Portal Authentication for Fiji Revenue & Customs Service taxpayer portal (PKI)" }, { "3BFA1800008131FE454D4F54494F4E0000900760", "SIM card (Telecommunication)" }, { "3BFA1800008131FE4550564A434F5033454D5694", "NXP JCOP3 J3H082 Java Card 3.0.4 Dual-Interface (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop3-j3h082-java-card-3-0-4-j3h081-dual-interface/" }, + { "3BFA1800008131FE4550564A434F503453494493", "National Health Insurance (Taiwan) (HealthCare)" }, { "3BFA180000910131FE4550564A434F503453494482", "Supposed P71 SecID purchased from a Chinese manufacturer (JavaCard)" }, + { "3BFA180000910131FE456BD1936AC2F28547E164CC", "J3R180, NXP JCOP4 JC3.0.5 Classic, GP2.3, SECID (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic/" }, { "3BFA180002C10A31FE584B53776973735369676E89", "SuisseId card (used for qualified signatures)\nhttp://postsuisseid.ch/de/suisseid\nhttp://www.suisseid.ch/" }, { "3BFA1800FF10004A546178436F72655631", "NXP JCOP4 J3R200 P71 (JavaCard)" }, + { "3BFA1800FF8031FE450031807394410000900090", "Kazakhstan Identity Card 2022 (eID)" }, { "3BFA1800FF8131FE454A434F5032315632333165", "TrubDemax healthcare card\nJCOP 21 / 72k" }, { "3BFA1800FF8131FE454A434F5034314332303074", "HID Crescendo C200\nhttps://www.hidglobal.com/sites/hidglobal.com/files/resource_files/crescendo-c200-c700-smart-card-ds-en.pdf" }, { "3BFA1800FF8131FE454A434F5034315632323162", "JCOP41\nHID Crescendo C700\nhttp://www.hidcorp.com/\nShould be compatible to RAAK\nhttp://www.raaktechnologies.com/\nMarx CrypToken MX2048-JCOP USB Token" }, @@ -3385,6 +3726,9 @@ const static atr_t AtrTable[] = { { "3BFB1300FF10800031C164086032060F9000", "Stripe Issuing Card (Bank)" }, { "3BFB1300FF10800031C164086032100F9000", "Varo (Bank)" }, { "3BFB1300FF10800031C164089862210F9000", "Visa Debit (Bank)" }, + { "3BFB1300FF10800031C164089862290F9000", "Bank Card (Bank)" }, + { "3BFB1300FF10800031C1640924331E0F9000", "TransferWise Debit Card (Bank)\nhttps://wise.com/" }, + { "3BFB1300FF10800031C164096441360F9000", "Truist Business Debit (Bank)" }, { "3BFB1300FF813180755A43352E3520524556204763", "ZeitControl BasicCard 5.5" }, { "3BFB1300FF813180755A43352E3620524556204D6A", "ZeitControl BasicCard ZC5.6 user-programmable smart card\nhttp://www.basiccard.com/index.html?overview.htm" }, { "3BFB1300FF813180755A43362E3520524556204364", "ZeitControl BasicCard 6.5, multiapplication with 30 kByte EEPROM" }, @@ -3423,7 +3767,9 @@ const static atr_t AtrTable[] = { { "3BFD1300008131FE4541373030364347203234325231D6", "YubiKey NEO (token)" }, { "3BFD1300008131FE454A434F50323176323331474454E1", "National Health Insurance Card, Taiwan" }, { "3BFD1300008131FE45543D314A323133364B56323331DC", "ic Card (JavaCard)" }, + { "3BFD1300008131FE4580318153534431738421C0810730", "Personal Info Card (eID)" }, { "3BFD1300FF10000031C173C8400052A1C5009000", "IBKR Prepaid MasterCard, Issued by Peoples Trust Company (Bank)\nhttps://www.interactivebrokers.com/en/index.php?f=26451" }, + { "3BFD1300FF10000031C173C8400052A1D5009000", "PayPal Business Debit Mastercard (Bank)\nhttps://www.paypal.com/merchantapps/appcenter/makepayments/bdmc" }, { "3BFD1800008031FE45003180718E6452D904008190005B", "Oberthur Card Systems, authentIC" }, { "3BFD1800008031FE4553434536302D43443038312D46C4", "Panama Electronic Signature (JavaCard)" }, { "3BFD1800008031FE45736674652063643134342D6E66D8", "SmartCafe Expert 3.2 144K Dual is a contact and contactless technology Java card from G&D with 144K on-board EEPROM for application and data storage. Certified to FIPS 140-2 Level 3 and Common Criteria EAL 5+. Supports specifications ISO 14443A T=CL and ISO 7816 T=1/0. (PKI)\nhttp://www.smartcardfocus.us/shop/ilp/id~523/smartcafe-expert-3-2-144k-dual/p/index.shtml" }, @@ -3449,7 +3795,10 @@ const static atr_t AtrTable[] = { { "3BFD9600008131FE4500000152332980000000000000A9", "DPI Guatemala (eID)\nhttp://www.renap.gob.gt/" }, { "3BFD9600008131FE450000015233672000000000000047", "DPI Guatemala (eID)\nhttps://www.renap.gob.gt/servicios/que-es-el-dpi" }, { "3BFD9600008131FE4500000161638620000000000000C5", "Renap Guatemala (eID)" }, + { "3BFD9600008131FE450000016687038000000000000003", "DPI (Documento Personal de Identificacion) Guatemala (eID)" }, { "3BFD9600008131FE45534C4A353247444C313238435257", "Universal JCard (Contact) with Infineon SLE78 (JavaCard)\nhttp://www.usmartcards.co.uk/cards/universal-jcard-contact-with-infineon-sle78-white-gloss-pvc-card.html" }, + { "3BFD9600008131FE45534C4A35324778787979797A5224", "J3R180 (JavaCard)" }, + { "3BFE130000108080318066B0840C016E0183009000", "Sberbank of Russia MIR debit card (Bank)" }, { "3BFE1300008131FE454A434F5076323431204C4F542057B1", "LOT test card (JavaCard)\nwww.lotgroup.eu" }, { "3BFE1800008031FE454573744549442076657220312E30A8", "Estonian Identity Card (EstEID 3.0 'JavaCard' cold)" }, { "3BFE1800008031FE4553434536302D43443038312D6E46A9", "G&D Sm@rtCafe Expert 6.0 (JavaCard)\nhttp://www.smartcardfocus.com/shop/ilp/id~684/smartcafe-expert-6-0-80k-dual-/p/index.shtml" }, @@ -3484,7 +3833,8 @@ const static atr_t AtrTable[] = { { "3BFF1100008171404200002101314252000[05]63........9000.*", "Smart Card 'The Smart Way to Login'\nUsed on Acer TravelMate to secure boot" }, { "3BFF11000240648069A2070103570000FF0083009000", "Gemplus GemXpresso" }, { "3BFF1100FF8131FE9580F9D2760000255444010083000000A0", "GiroCard Haspa Hamburger Sparkasse (Bank)\nhttps://www.haspa.de/privatkunden/ihr-online-banking/unser-angebot/haspa-digital-services-96198/" }, - { "3BFF13000010003101F1564011001900000000000000", "BVG Guthabenkarte (Prepaid Payment Card for Berlin/Brandenburg Public Transport) (Transport)\nhttps://www.bvg.de/de/service-und-kontakt/guthabenkarte" }, + { "3BFF13000010003100DE525001001500000000000000", "wisely debit (Bank)\nhttps://www.mywisely.com/" }, + { "3BFF13000010003101F1564011001900000000000000", "BVG Guthabenkarte (Prepaid Payment Card for Berlin/Brandenburg Public Transport) (Transport)\nhttps://www.bvg.de/de/service-und-kontakt/guthabenkarte\nRevolut Visa Glow-in-the-dark\nhttps://revolut.com/\nMasterCard debit - PayCenter - Corporate Benefit 'Sachbezugskarte' (Bank)\nhttps://paycenter.de/sachbezugskarte/" }, { "3BFF13000010003101F1564011001D00000000000000", "albo (Bank)\nhttps://www.albo.mx/" }, { "3BFF1300008131FE450031B9640404ECC17394018082900052", "AKD kID (eID)\nhttps://www.id.hr" }, { "3BFF1300008131FE450031B9640444ECC17394018082900012", "Croation personal ID card (eID)\nhttp://eid.hr/" }, @@ -3492,13 +3842,24 @@ const static atr_t AtrTable[] = { { "3BFF1300008131FE45434433690940000020202020202000F3", "VISA credit card (LBBW/Payback VISA) (Bank)" }, { "3BFF1300008131FE454F574F4B31302D4A................", "OWOK (One Web, One Key) login card\nhttp://www.reiner-sct.com/owok/\nReiner SCT loginCard\nhttps://cardlogin.reiner-sct.com/" }, { "3BFF1300008131FE4D8025A00000005657444B3333300600D2", "Datakey DCOS model 330 (DKCCOS 6.0 token)" }, + { "3BFF1300918131FE4141434F532046696F6E6131204C6336F4", "TURKEY A101 HADI APP CARD (Bank)\nhttps://a101hadi.a101.com.tr/" }, + { "3BFF1300FF10000031C1738211064414D33470779000", "Visa Debit (Bank)\nhttps://www.chase.com/" }, { "3BFF1300FF10000031C173C821106441443533079000", "BRADESCO-CONTA SALARIO (Bank)" }, { "3BFF1300FF10000031C173C8211064414D3130079000", "Tangerine MasterCard (Bank)\nhttps://www.tangerine.ca/en/products/spending/creditcard/money-back/" }, + { "3BFF1300FF10000031C173C8211064414D3137079000", "PayPal Business Debit mastercard (Bank)\nhttps://www.paypal.com/merchantapps/appcenter/makepayments/bdmc" }, { "3BFF1300FF10000031C173C8211064414D3330079000", "VISA card issued by ING-DiBa AG (Germany) (Bank)" }, + { "3BFF1300FF10000031C173C8211064414D3331079000", "NAB VISA Debit (contact interface) (Bank)\nhttps://www.nab.com.au/" }, { "3BFF1300FF10000031C173C8211064414D3337079000", "VISA Credit Card (Postbank), Germany (Bank)" }, { "3BFF1300FF10000031C173C8211064414D3341079000", "BBVA debit card Uruguay. MIFRE Plus compatible. (Bank)\nScotiabank Passport Visa Infinite credit card (Bank)\nhttps://www.scotiabank.com/ca/en/personal/credit-cards/visa/passport-infinite-card.html" }, + { "3BFF1300FF10000031C173C8211064414D3343079000", "MasterCard issued by President's Choice Bank (Canada) (Bank)\nhttp://pcfinancial.ca/mastercard" }, { "3BFF1300FF10000031C173C8211064414D3347079000", "Chase Visa Debit Card (Bank)\nhttps://www.chase.com/bankinghelp" }, { "3BFF1300FF10000031C173C8211064414D3348079000", "BBVA blue VISA Debit Card (Bank)\nhttps://www.bbva.es/en/personas/productos/tarjetas/tarjeta-joven-ahora.html\nDesjardins Bonus Visa credit card (Bank)\nhttps://www.desjardins.com/ca/personal/loans-credit/credit-cards/bonus-visa/index.jsp" }, + { "3BFF1300FF10000031C173C8211064414D3430079000", "PNC BUSINESS VISA DEBIT (Bank)\nhttps://www.pnc.com/en/small-business/payments-and-processing/payment-cards/pnc-bank-visa-business-debit-card.html" }, + { "3BFF1300FF10000031C173C82110644930424E079000", "National Bank Debit Card with expiration date and cvv code (Bank)" }, + { "3BFF1300FF10000031C173C82110644932424E079000", "Interact, Visa Debit Bank of Novia Scotia (Bank)\nhttps://www.scotiabank.com/global/en/credit-card-terms-and-conditions.html" }, + { "3BFF1300FF10000031C173C82110644D30424E079000", "Debit payment card (Rabobank NL) (Bank)\nhttps://www.rabobank.nl/en/business/making-and-receiving-payments/payments/paying-with-your-bank-card" }, + { "3BFF1300FF10000031C173C82110645631424E079000", "Portuguese 'BancoCTT' Bank Card (Bank)\nhttps://www.bancoctt.pt/o-seu-dia-a-dia/cartao-de-credito-banco-ctt" }, + { "3BFF1300FF10000031C173C82110645631434E079000", "Chase Freedom Unlimited Credit Card (Bank)" }, { "3BFF1300FF10808031E06B04310502AF555555555555", "USAA EMV Visa Debit Card (Bank)" }, { "3BFF1300FF10808031E06B071405028A555555555555", "Tangerine Debit Card (Bank)\nhttps://www.tangerine.ca" }, { "3BFF1300FF10808031E06B08240502B5555555555555", "Tangerine Canada Interac debit card (Bank)\nhttps://www.tangerine.ca/" }, @@ -3508,7 +3869,9 @@ const static atr_t AtrTable[] = { { "3BFF1300FF8131FE45656311045002800008390004020502E9", "German 'Geldkarte' supplied by the Deutsche Bank in Karlsruhe,\nBaden-Wurttemberg, Germany." }, { "3BFF1300FF8131FE45656311045002800008540004230502A5", "Maestrocard/Geldkarte (Stadtsparkasse Haltern, Germany)" }, { "3BFF1300FF8131FE5D8025A00000005657444B33323005003F", "Datakey DCOS model 320" }, - { "3BFF1300FF918131FE4541434F53204449616E6132204C6336DF", "Alior Bank MasterCard debit (Bank)" }, + { "3BFF1300FF910131FE210031C173C82110644D30434E07900094", "AirPlus MasterCard Commercial (Bank)\nhttps://www.airplus.com/us/en/products-solutions/products/corporate-cards/corporate-cards.html" }, + { "3BFF1300FF918131FE4141434F532046696F6E6131204C6336F4", "Deutsche Kreditbank Debit (Bank)" }, + { "3BFF1300FF918131FE4541434F53204449616E6132204C6336DF", "Alior Bank MasterCard debit (Bank)\nComdirect (Deutsch Bank) debit VISA (AUSTRIACARD 56015/001) (Bank)" }, { "3BFF1400FF8131FE458025A000000056575343363530010039", "SafeNet SC650 (PKI)\nhttp://www.safenet-inc.com/data-protection/authentication/smartcard-650/" }, { "3BFF1400FF8131FE458025A000000056575343363530030239", "SafeNet SC650 v3.2 (PKI)\nhttp://www.safenetat.com/products-solutions/high-assurance-authentication/sc650/" }, { "3BFF1400FF8131FE458025A00000005657534336353004003C", "SafeNet AT SC650 V4.0 02/2018 (PKI)\nhttps://www.safenetat.com/Solutions/Enterprise-Security/high-assurance-authentication/sc650/" }, @@ -3518,6 +3881,8 @@ const static atr_t AtrTable[] = { { "3BFF1800008131FE45006B04050100012101434E5310318059", "CNS - Carta Nazionale dei Servizi (Italia)\nPA emittente: Regione Autonoma della Sardegna\nCarta del Servizio Sanitario Regionale - Emilia Romagna" }, { "3BFF1800008131FE45006B05051017012101434E531031805E", "Regional Card - Regione Liguria, Veneto - Italy (eID)\nTessera Sanitaria - Carta Regionale dei Servizi" }, { "3BFF1800008131FE45006B05052000012101434E5310318079", "health card (HealthCare)\nhttps://tscns.regione.sardegna.it/" }, + { "3BFF1800008131FE45006B0505200001F101434E53103180A9", "national health service card (HealthCare)\nhttps://ca.arubapec.it/downloads/MU_LINUX.zip" }, + { "3BFF1800008131FE45006B0505912001F101434E5310318038", "Italian Health Card (TS) and Citizen's Card (CNS) based on IDEMIA ID-One CNS v2 on Cosmo 9.1 (HealthCare)" }, { "3BFF1800008131FE45006B11050700011101434E531131807B", "Italian National Fire Corps -special identification card (eID)" }, { "3BFF1800008131FE45006B11050700012101434E531031804A", "Oberthur ID-One Cosmo V7-n it's a java card 2.2.2\nIzenpe Certificado Ciudadano (eID)\nhttps://www.izenpe.eus/informacion/certificado-ciudadano/s15-content/es/" }, { "3BFF1800008131FE4D8025A00000005657444B3430300600DD", "DataKey 400 (DK400)" }, @@ -3525,7 +3890,9 @@ const static atr_t AtrTable[] = { { "3BFF1800008131FE55006B0209040301010144534410318068", "ACA (Lawyer Identifier Card) (eID)" }, { "3BFF1800008131FE55006B02090503010101434E5310318064", "Bit4id J-SIGN 2048 (L) (PKI)\nhttps://www.bit4id.com/en/j-sign/" }, { "3BFF1800008131FE55006B02090603010101434E5310318067", "ST microelettronics JSign3 (HealthCare)" }, + { "3BFF1800008131FE55006B02090703010101434E5310318066", "Aruba digital signing card (eID)\nhttps://www.aruba.it" }, { "3BFF1800008131FE55006B02091403010101434E5310318075", "Smart Card INFOCERT digital key CNS from CST PADOVA (eID)" }, + { "3BFF1800008131FE55006B02091717010101434E5310318062", "Carta Nazionale dei Servizi (CNS) Centro Servizi Territoriali (CST) (PKI)\nhttp://cst.provincia.padova.it/category/faq/firma-digitale" }, { "3BFF1800FF8031FE45534653452D43583332322D561803087C", "Giesecke & Devrient Sm@rtCafe Expert 2.0" }, { "3BFF1800FF8031FE45536D4072744361666545787065727465", "Giesecke & Devrient SmartCafe 32K v1" }, { "3BFF1800FF8131..456563............................", "Geldkarte (generic ATR)" }, @@ -3539,7 +3906,10 @@ const static atr_t AtrTable[] = { { "3BFF1800FF81313C4565630D0231025000109055700004100A", "EC-Card from DKB (Deutsche Kreditbank AG)" }, { "3BFF1800FF81313C4565630D02310280001224300020041059", "Geldkarte (Germany)" }, { "3BFF1800FF813150456563............................", "GeldKarte v3 (Germany)" }, + { "3BFF1800FF8131FE4165630608710156000FB85073204712D8", "Commerzbank maestro (Bank)\nhttps://www.commerzbank.de/konten-zahlungsverkehr/produkte/girokonten/kostenloses-girokonto/" }, { "3BFF1800FF8131FE4165630608710156000FB8602AA0471231", "Debit card (Germany): Postbank - GeldKarte (EUR), girocard, V-PAY (Bank)\nhttps://www.postbank.de/" }, + { "3BFF1800FF8131FE41656306087102500023B8907360471271", "Debit card (Germany): Deutsche Kreditbank (DKB), ec-cash, (Bank)\nhttps://www.dkb.de/privatkunden/karten/girocard" }, + { "3BFF1800FF8131FE4165631116710156000F0902904E5711AC", "German Bank Card IDEMIA 9 Maestro/Girocard (Sparkasse S-Payment TPY 1974693D) (Bank)" }, { "3BFF1800FF8131FE450031C573C00180547615020105900074", "SIGILANCE NFC OpenPGP Smart Card (JavaCard)\nhttps://www.sigilance.com/" }, { "3BFF1800FF8131FE455448434331305445434F4744484E3224", "National Health Insurance Card, Taiwan" }, { "3BFF1800FF8131FE455448434331305445434F4744494E3126", "National Health Insurance Card, Taiwan" }, @@ -3593,6 +3963,7 @@ const static atr_t AtrTable[] = { { "3BFF1800FF8131FE55006B02091303010101434E531031808D", "Aruba Digital Signature (Other)\nhttps://www.pec.it/offerta-firma-digitale.aspx" }, { "3BFF1800FF8131FE55006B02091303011101434E531131809C", "Politecnico di Torino Student Card (eID)\nhttp://www.polito.it/" }, { "3BFF1800FF8131FE55006B02091617011101434E531131808D", "Carta Regionale dei Servizi - Regione Autonoma Friuli Venezia Giulia (HealthCare)\nhttps://www.regione.fvg.it/rafvg/cms/RAFVG/GEN/carta-regionale-servizi/" }, + { "3BFF1800FF8131FE55006B02091717011101434E531131808C", "european health insurance card and Regional (ItalY - Provincia Autonoma di Trento) Service Card (CPS) (eID)\nhttps://www.provincia.tn.it/Servizi/Attivare-la-Carta-Provinciale-dei-Servizi-CPS#cos_e" }, { "3BFF1800FF8131FE55006B42495434494420312E3000900091", "Touch&Sign 2048 (PKI)" }, { "3BFF1800FF8131FE55006B42495434494420322E3000900092", "Izenpe Green Card (Citizen Certificate) (eID)\nhttp://www.izenpe.com/s15-12020/en/contenidos/informacion/ciudadano/en_def/index.shtml" }, { "3BFF1800FFC10A31FE55006B0508C805011101434E531031800C", "Carta Regionale dei Servizi - Regione Lombardia" }, @@ -3622,6 +3993,7 @@ const static atr_t AtrTable[] = { { "3BFF9500008031FE4380318067B0850201F3A3138301900057", "Driver's Card (Tachograf card) issued by pwpw Poland (Transport)\nhttps://www.pwpw.pl/en/Products/Cards/Cards.html" }, { "3BFF9500008031FE4380318067B0850201F3A3138301F83BFF", "UK Drivers Tachograph Card (Transport)" }, { "3BFF950000C00A1F438031E073362113574A330E0231410088", "'BASE' SIM card; BASE is a german mobile phone operator, which is a brand of E-Plus, Germany." }, + { "3BFF95000150801C444E41535034323020526576533430", "Nagra card Canal+ (Polish TV provider) (Pay TV)\nhttps://pl.canalplus.com/" }, { "3BFF95000150801C444E41535034323020526576533430F1", "NC+ Polland (Pay TV)\nhttp://www.flysat.com/ncplus.php" }, { "3BFF95000150801C444E41535034323020526576533430F15D", "NC+ Polland (Pay TV)\nhttp://www.flysat.com/ncplus.php" }, { "3BFF95000150801C444E41535034323020526576533441", "Platforma Canal+ Polska, cayman card (Pay TV)\nhttps://www.flysat.com/canalplus-pl.php" }, @@ -3633,6 +4005,7 @@ const static atr_t AtrTable[] = { { "3BFF9500FF50801C444E41535034303020526576493441", "Pay TV - NC+ in Poland (Pay TV)\nhttp://ncplus.pl/" }, { "3BFF9500FF50801C444E41535034303020526576493447", "Platforma Canal+ Polska, cameleon card (Pay TV)\nhttps://www.flysat.com/canalplus-pl.php" }, { "3BFF9500FF50801C444E41535034303020526576493548", "Canal+ France Nagra3 (Pay TV)\nhttps://www.canalplus.com/" }, + { "3BFF9500FF50801C444E41535034383220526576523038", "CANALSAT, mediaguard key (Pay TV)" }, { "3BFF9500FFC00A1F438031E073362113574A3320073341411F", "Swisscom 3G SIM card" }, { "3BFF9500FFC00A1F438031E073F62113574A334857314141E5", "MTNL 3G USIM (India)" }, { "3BFF9500FFC00A1F438031E073F62113574A334861324147D6", "GSM SIM (issued by e-plus, Germany)" }, @@ -3646,10 +4019,21 @@ const static atr_t AtrTable[] = { { "3BFF9600008131FE4380318065B0845651101201788290006A", "SafeNet eToken 5300 (PKI)" }, { "3BFF9600008131FE4380318065B08456511012021082900001", "Nedap NexS N:Secure (eID)\nhttps://www.nsecure.nl/nl/" }, { "3BFF9600008131FE4380318065B0846160FB120FFD8290000C", "IDPrime 930 FIPS Level 2 (T=1 CT=96) (BAI3.1) (PKI)" }, + { "3BFF9600008131FE4380318065B0846566FB12017882900085", "eToken 5110+ FIPS 140-2 Level 2 (JavaCard)" }, + { "3BFF9600008131FE4380318065B0846566FB120FFC8290000F", "SmartID 3930 FIDO Contact and Contactless card (PKI)\nhttps://www.smartcardfocus.com/shop/ilp/id~962/safenet-idprime-3930-fido-dual-interface-fips-l2/p/index.shtml" }, + { "3BFF9600008131FE4380318065B0846669FB12FFFE829000F1", "IDCore3230 build 6.8, test APDU applet (JavaCard)" }, { "3BFF9600008131FE4380318065B085040011120FFF829000E0", "Pakistan National identity card (eID)" }, { "3BFF9600008131FE4380318065B085040120120FFF829000D0", "Portuguese National Identity Card (eID) (eID)\nhttps://www.autenticacao.gov.pt/o-cartao-de-cidadao" }, + { "3BFF9600008131FE4380318065B0855956FB120FFC82900002", "THALES SafeNet IDPrime 3940 Fido (PKI)\nhttps://cpl.thalesgroup.com/fr/resources/access-management/idprime-3940-product-brief" }, { "3BFF9600008131FE4580F9A0000003080000100053454E54AC", "cac (eID)" }, { "3BFF9600008131FE55006B02090403010101434E53103180EB", "Aruba PEC SpA digital signature card made by Incard (eID)\nhttps://www.pec.it/download-software-driver.aspx" }, + { "3BFF960000C00A31FE4380318065B085040011120FFF829000AB", "French National Identity Card (eID) (eID)\nhttps://www.interieur.gouv.fr/actualites/actu-du-ministere/nouvelle-carte-nationale-didentite" }, + { "3BFF9600FF8131FE406563111562025000100A0190A90730BF", "girocard Sparkasse Ansbach, Germany BLZ 76550000 (Bank)" }, + { "3BFF9600FF8131FE4065631D02840156001F190850E10200EF", "Raiffeiesenbank Girocard Maestro (Bank)" }, + { "3BFF9600FF8131FE4065631D0284025000230308C0B702000A", "German Debitcard from Sparkasse (Bank)" }, + { "3BFF9600FF8131FE4065631D0284025000230709E0F9020061", "Sparkasse Ingolstadt (Bank)" }, + { "3BFF9600FF8131FE4065631D028402500023140710B80200CD", "Sparkasse Aachen - german Maestro/Girocard (S-Payment TGI 50380969) (Bank)" }, + { "3BFF9600FF8131FE4065631D028402500023160BB0C102001A", "debit card (Bank)" }, { "3BFF9600FF8131FE4565630D09710764000D00035450070181", "Commerzbank ServiceCard / Maestro / GeldKarte / Cirrus / girocard / CashGroup / electronic cash" }, { "3BFF9600FF8131FE4565631901500280000F002B0046501172", "Sparkasse Bremen Germany HBCI DDV" }, { "3BFF9600FF8131FE4565631901500280000F002F0025501115", "German Postbank Giro card with electronic cash, Maestro, GeldKarte features" }, @@ -3659,6 +4043,7 @@ const static atr_t AtrTable[] = { { "3BFF9600FFC00A1F438031E073362113574A43491C3130321E", "Giesecke & Devrient - UniverSIM Pegasus" }, { "3BFF9600FFC00A31FE4D8031E06B04310502A85555555555557E", "Multos (Other)" }, { "3BFF9700008131FE4380318065B0846160FB120FFD8290000D", "IDPrime 3930 FIPS Level 3 (T=1 CT=97) (BAI6) (PKI)" }, + { "3BFF9700008131FE4380318065B08466693912FFFE82900032", "IDCore3230 build 6.8, test APDU applet (JavaCard)" }, { "3F05DC20FC0001", "DigiCash Facility Card" }, { "3F28000011140003689000", "SIMEMU - a DIY GSM SIM card\nhttp://simemu.cjb.net/" }, { "3F2D0027A051827D00000052000C9000", "Porta Moedas Multibanco (Portugeese electronic purse)" }, @@ -3666,6 +4051,7 @@ const static atr_t AtrTable[] = { { "3F2F008059AF0201013000000A0E83069F12", "Gemplus GemXplore" }, { "3F2F008059AF02010230000C0A0E831E9F16", "GSM-SIM (900MHz) card of the carrier 'Mannesmann Mobilfunk' for\ntheir network 'D2-Privat' - now known as Vodafone Mobilfunk\nhttp://www.vodafone.de/" }, { "3F2F008069AE0202013600000A0E833E9F16", "GSM-SIM e-plus (1800MHz)" }, + { "3F2F008069AF0204013600000A0E833E9F16", "Telia Mobitel GSM (Telecommunication)" }, { "3F2F008069AF0204013600020A0E833E9F16", "GSM-SIM D2 CallYa (900MHz)" }, { "3F2F008069AF0307015900000A0E833E9F16", "Nokia SIM Ph2 16K Ver2.0" }, { "3F2F008069AF0307015900130A0E833E9F16", "Old Spanish Telefonica Movistar GSM SIM card manufactured by Gemplus" }, @@ -3710,6 +4096,7 @@ const static atr_t AtrTable[] = { { "3F672F0011140003689000", "D2MAC/Eurocrypt (Pay TV)" }, { "3F672F0411200000689000", "BULL HN ITALIA 06/92 - 100.000 - 64MP\nLa Sapienza - Universita' di Roma" }, { "3F69000024AF01700101FF9000", "French GSM SIM card (900MHz)" }, + { "3F69000025AF01700103FF9000", "French Gift Card (Loyalty)" }, { "3F6A000000640150010C820101A9", "Credit Card cafe Selecta" }, { "3F6B150002A007906F4D59000C9000", "Sky Viewing Card (Gen 1) from 1990s (Pay TV)" }, { "3F6C000024A03000FF00000100049000", "Gemplus MCOS 16K DES Sample Card" }, @@ -3721,6 +4108,7 @@ const static atr_t AtrTable[] = { { "3F6C00003DA030BE4100370100049000", "Sberbank (Bank)" }, { "3F6D000080318065B00501025E83009000", "Gemplus GemXpresso 211PK or 211PK-IS" }, { "3F6D000080318065B00501025E92009000", "Gemplus GemXpresso 32K" }, + { "3F7613250421B0114A5003", "DSS/DTV F (P1; first generation access card) (Pay TV)" }, { "3F77130000C11400A2689000", "Boxer DTV Sweden (Pay TV)\nhttp://www.boxer.se" }, { "3F77180000C11400A2689000", "Viacess card HRT (Hrvatska Radio Televizija)" }, { "3F77180000C11401A2689000", "VIA 2.6 XXX (Pay TV)" }, @@ -3730,7 +4118,7 @@ const static atr_t AtrTable[] = { { "3F77180000C27A4202689000", "SCT (Via Access)" }, { "3F77180000C27A4302689000", "DORCEL (Via Access)" }, { "3F77180000C27A4402689000", "XXX Redlight_HD (Viaccess)" }, - { "3F77180000C2EB41026C9000", "Elite HD10+ (Pay TV)\nSattelite cryptoworks card - Smart card Viaccess (Telesat - belgium) (Pay TV)" }, + { "3F77180000C2EB41026C9000", "Elite HD10+ (Pay TV)\nSatellite cryptoworks card - Smart card Viaccess (Telesat - belgium) (Pay TV)" }, { "3F77180000C2EB45026C9000", "facetv (Other)" }, { "3F77180000D38A4001649000", "Skylink Viaccess 5.0 (Pay TV)\nhttp://www.skylink.sk/" }, { "3F77180000D38A4201649000", "Satellite decoder card for TV Vlaanderen (Other)\nhttps://www.tv-vlaanderen.be" }, @@ -3765,12 +4153,14 @@ const static atr_t AtrTable[] = { { "3FFD11250250000333B01569FF4A50F080034B4C03", "Kabel Deutschland G02 (Pay TV)" }, { "3FFD11250250800F41B00A69FF4A507080005A4503", "Buypass smart card (Bank)\nhttps://www.buypass.no/bruker/buypass-id/buypass-smartkort" }, { "3FFD11250250800F41B00D69FF4A50F08000565403", "Viasat (Pay TV)" }, + { "3FFD11250250800F41B00F69FF4A50F080005A4A03", "Telekom Romania Communications (DVB-C) (Pay TV)\nhttps://www.telekom.ro/" }, { "3FFD13250250000F33B00F69FF4A50D00000535902", "Sky Digital (DSS satellite TV card) 2009 issue" }, { "3FFD13250250000F33B01669FF4A50D08000535903", "Sky TV Multiroom (Pay TV)" }, { "3FFD13250250800F..B0..69FF4A50D08000495403", "Sky (Italy) VideoGuard CAM card" }, { "3FFD13250250800F33B008FFFF4A50900000474C01", "Sky (Brasil) VideoGuard CAM card" }, { "3FFD13250250800F33B008FFFF4A50900000545601", "NDS Videoguard TV CAM card (Sky Mexico 0905) (Pay TV)\nhttps://en.wikipedia.org/wiki/VideoGuard" }, { "3FFD13250250800F41B00A69FF4A50F00000503103", "Sky Germany V14 NDS card (Pay TV)\nhttp://www.wikipedia.org/wiki/Sky_Deutschland" }, + { "3FFD13250250800F41B00F69FF4A50F080005A4A03", "Orange Romania (DVB-C) (Pay TV)\nhttps://www.orange.ro/" }, { "3FFD13250250800F55B00269FF4A50F08000503103", "SKY DE V15 (Pay TV)" }, { "3FFD14250150000F33B00BFFFF4A50800000475801", "DirecTV card" }, { "3FFD14250250800F41B00A69FF4A507080004E5A03", "Sky Network Televisiton Limited (New Zealand) card for new (2016) decoder. Reportedly, this is a Kaon NS1120-500 box. (Pay TV)\nhttp://www.sky.co.nz" }, @@ -3809,7 +4199,7 @@ const static atr_t AtrTable[] = { { "3FFF9500FF918171644700444E41535030303320526576333233FF", "Satellite TV Card 'Via Digital' (Nagra)" }, { "3FFF9500FF918171A04700444E4153503031302052657641323048", "DSS/DISH ROM10" }, { "3FFF9500FF918171A04700444E4153503031302052657641323149", "PayTV card for DishNetwork Sat receiver\nhttp://www.dishnetwork.com/" }, - { "3FFF9500FF918171A04700444E4153503031312052657642", "NTL digial TV card (Nagravision)" }, + { "3FFF9500FF918171A04700444E4153503031312052657642", "NTL digital TV card (Nagravision)" }, { "3FFF9500FF918171A04700444E415350303131205265764230364E", "Telewest Broadband (Nagravision)" }, { "3FFF9500FF918171A04700444E415350303131205265764230423A", "NagraVision card for StarHub Digital Cable DVB-C Singapore" }, { "3FFF9500FF918171A04700444E415350303131205265764230443C", "NagraVision card for Virgin Media in the UK" }, @@ -3822,7 +4212,7 @@ const static atr_t AtrTable[] = { { "3FFF9500FF918171FE4700444E4153503134322052657647303216", "Polsat Nagra3\nBrazil - Claro TV Nagra3 Red" }, { "3FFF9500FF918171FE4700444E4153503134322052657647303410", "Nagra 3 Card - Telefonica Brazil Green" }, { "3FFF9500FF918171FE4700444E4153503134322052657647303612", "UM02 card from German Unitymedia cable TV provider" }, - { "3FFF9500FF918171FE4700444E4153503134322052657647433463", "HD+ card used by the satelite company astra for decryption of the HDTV channels of RTL, VOX, Sat1 and ProSieben. Nagravision V3 is used for the encryption." }, + { "3FFF9500FF918171FE4700444E4153503134322052657647433463", "HD+ card used by the satellite company astra for decryption of the HDTV channels of RTL, VOX, Sat1 and ProSieben. Nagravision V3 is used for the encryption." }, { "3FFF9500FF918171FE4700444E415350313830204D65724A30320E", "Nagra 3 Digital Plus Spain" }, { "3FFF9500FF918171FE4700444E41535032343120447368", "DISH Network G3 (Pay TV)" }, { "3FFF9500FF918171FE4700444E415350323431204473684830390C", "Dish Network Smart Card (Pay TV)" }, @@ -3836,6 +4226,7 @@ const static atr_t AtrTable[] = { { "3FFF9500FF918171FE5700444E4153503431302052657651324260", "Nagravision Kudelski Generation 7 card Rom410 MerQ2B (Pay TV)" }, { "3FFF9500FF918171FE5700444E4153503431302052657651325371", "Slovak and Czech pay TV provider Slovak Telecom (Pay TV)\nhttp://www.flysat.com/novadigi-sk.php" }, { "3FFF9500FF918171FE5700444E4153503432302052657653363017", "HD+ HD04b Card (Pay TV)" }, + { "3FFF9500FF918171FE5700444E4153503432302052657653363413", "claro card honduras central america 'NAGRA' (Pay TV)" }, { "3FFF9500FF918171FE5700444E4153503432302052657653364265", "Nagra Kudelski / Canalsat Reunion (Pay TV)" }, { "3FFF9500FF918171FE5700444E4153503435302052657657363014", "HD+ HD05 Paytv smartcard (Pay TV)" }, { "3FFF9500FF918171FE5700444E415350343832205265765232361C", "Max Tv Croatia (Pay TV)\nhttps://www.lyngsat.com/packages/Max-TV.html" }, diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c index 563fda14f..ff75cc20e 100644 --- a/client/src/cipurse/cipursecore.c +++ b/client/src/cipurse/cipursecore.c @@ -104,9 +104,7 @@ static int CIPURSEExchangeEx(bool activate_field, bool leave_field_on, sAPDU_t a memcpy(result, securedata, rlen); } - if (result_len != NULL) { - *result_len = rlen; - } + *result_len = rlen; if (sw != NULL) { *sw = isw; @@ -628,7 +626,7 @@ void CIPURSEPrintFileAttrEx(uint8_t *attr, size_t len, bool isDGI) { } void CIPURSEPrintFileAttr(uint8_t *attr, size_t len) { - return CIPURSEPrintFileAttrEx(attr, len, false); + CIPURSEPrintFileAttrEx(attr, len, false); } void CIPURSEPrintFileUpdateAttr(uint8_t *attr, size_t len) { diff --git a/client/src/cmdanalyse.c b/client/src/cmdanalyse.c index a2ebf9d23..780107953 100644 --- a/client/src/cmdanalyse.c +++ b/client/src/cmdanalyse.c @@ -311,7 +311,7 @@ static int CmdAnalyseCRC(const char *Cmd) { PrintAndLogEx(INFO, " reflect8(0x80) is %02X == 0x01", reflect8(0x80)); PrintAndLogEx(INFO, " reflect16(0x8000) is %04X == 0x0001", reflect16(0xc6c6)); - uint8_t b1, b2; + uint8_t b1 = 0, b2 = 0; // ISO14443 crc B compute_crc(CRC_14443_B, data, (size_t)dlen, &b1, &b2); uint16_t crcBB_1 = (uint16_t)(b1 << 8 | b2); @@ -969,6 +969,11 @@ static int CmdAnalyseFreq(const char *Cmd) { 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); + PrintAndLogEx(INFO, "Antenna lengths"); + PrintAndLogEx(INFO, " 125 kHz 1/2 = %f m, 1/4 = %f m", (len_125 / 2), (len_125 / 4)); + PrintAndLogEx(INFO, " 134 kHz 1/2 = %f m, 1/4 = %f m", (len_134 / 2), (len_134 / 4)); + PrintAndLogEx(INFO, " 13.56 mHz 1/2 = %f m, 1/4 = %f m", (len_1356 / 2), (len_1356 / 4)); + if (F == 0 && C == 0 && L == 0) return PM3_SUCCESS; @@ -1079,71 +1084,86 @@ static int CmdAnalyseUnits(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "analyse units", "experiments of unit conversions found in HF. ETU (1/13.56mhz), US or SSP_CLK (1/3.39MHz)", - "analyse uints --etu 10" - "analyse uints --us 100" + "analyse uints --etu 10\n" + "analyse uints --us 100\n" ); void *argtable[] = { arg_param_begin, arg_int0(NULL, "etu", "", "number in ETU"), arg_int0(NULL, "us", "", "number in micro seconds (us)"), + arg_lit0("t", "selftest", "self tests"), arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIExecWithReturn(ctx, Cmd, argtable, true); int etu = arg_get_int_def(ctx, 1, -1); int us = arg_get_int_def(ctx, 2, -1); + bool selftest = arg_get_lit(ctx, 3); CLIParserFree(ctx); - if (etu == -1 && us == -1) { + if (selftest) { PrintAndLogEx(INFO, "US to ETU conversions"); - PrintAndLogEx(INFO, " 9 US = %u ETU (expect 1) " _GREEN_("ok"), US_TO_ETU(9)); - PrintAndLogEx(INFO, " 10 US = %u ETU (expect 1) " _GREEN_("ok"), US_TO_ETU(10)); - PrintAndLogEx(INFO, " 94 US = %u ETU (expect 10) " _GREEN_("ok"), US_TO_ETU(94)); - PrintAndLogEx(INFO, " 95 US = %u ETU (expect 10) " _GREEN_("ok"), US_TO_ETU(95)); - PrintAndLogEx(INFO, " 302 US = %u ETU (expect 32) " _GREEN_("ok"), US_TO_ETU(302)); + + int32_t test = US_TO_ETU(9); + PrintAndLogEx(INFO, " 9 US = %i ETU (expect 1) %s", test, (test == 1) ? _GREEN_("ok") : _RED_("fail")); + + test = US_TO_ETU(10); + PrintAndLogEx(INFO, " 10 US = %i ETU (expect 1) %s", test, (test == 1) ? _GREEN_("ok") : _RED_("fail")); + + test = US_TO_ETU(94); + PrintAndLogEx(INFO, " 94 US = %i ETU (expect 10) %s", test, (test == 10) ? _GREEN_("ok") : _RED_("fail")); + + test = US_TO_ETU(95); + PrintAndLogEx(INFO, " 95 US = %i ETU (expect 10) %s", test, (test == 10) ? _GREEN_("ok") : _RED_("fail")); + + test = US_TO_ETU(302); + PrintAndLogEx(INFO, " 302 US = %i ETU (expect 32) %s", test, (test == 10) ? _GREEN_("ok") : _RED_("fail")); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "ETU to US conversions"); - PrintAndLogEx(INFO, " 1 ETU = %u US (expect 9.43) " _GREEN_("ok"), ETU_TO_US(1)); - PrintAndLogEx(INFO, " 10 ETU = %u US (expect 94.39) " _GREEN_("ok"), ETU_TO_US(10)); - PrintAndLogEx(INFO, " 32 ETU = %u US (expect 302) " _GREEN_("ok"), ETU_TO_US(32)); + PrintAndLogEx(INFO, "ETU to Micro seconds (µS) conversions"); + double test_us = HF14_ETU_TO_US(1); + PrintAndLogEx(INFO, " 1 ETU = %3.2f US (expect 9.44) %s", test_us, (test_us == 9.44) ? _GREEN_("ok") : _RED_("fail")); + test_us = HF14_ETU_TO_US(10); + PrintAndLogEx(INFO, " 10 ETU = %4.2f US (expect 94.40) %s", test_us, (test_us == 94.40) ? _GREEN_("ok") : _RED_("fail")); + test_us = HF14_ETU_TO_US(32); + PrintAndLogEx(INFO, " 32 ETU = %5.2f US (expect 302.06) %s", test_us, (test_us == 320.06) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "US to SSP CLK 3.39MHz conversions"); - PrintAndLogEx(INFO, " 9 US = %u SSP (expect 32) ", US_TO_SSP(9)); - PrintAndLogEx(INFO, " 10 US = %u SSP (expect 32 or 48) ", US_TO_SSP(10)); - PrintAndLogEx(INFO, " 94 US = %u SSP (expect 320) ", US_TO_SSP(94)); - PrintAndLogEx(INFO, " 95 US = %u SSP (expect 320 or 336) ", US_TO_SSP(95)); - PrintAndLogEx(INFO, " 302 US = %u SSP (expect 1024) ", US_TO_SSP(302)); + PrintAndLogEx(INFO, "Microseconds (µS) to SSP CLK 3.39MHz conversions"); + PrintAndLogEx(INFO, " 9 µS = %i SSP (expect 32) ", US_TO_SSP(9)); + PrintAndLogEx(INFO, " 10 µS = %i SSP (expect 32 or 48) ", US_TO_SSP(10)); + PrintAndLogEx(INFO, " 94 µS = %i SSP (expect 320) ", US_TO_SSP(94)); + PrintAndLogEx(INFO, " 95 µS = %i SSP (expect 320 or 336) ", US_TO_SSP(95)); + PrintAndLogEx(INFO, " 302 µS = %i SSP (expect 1024) ", US_TO_SSP(302)); - PrintAndLogEx(INFO, " 4949000 US = %u SSP ", US_TO_SSP(4949000)); + PrintAndLogEx(INFO, " 4949000 µS = %i SSP ", US_TO_SSP(4949000)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "SSP CLK 3.39MHz to US conversions"); - PrintAndLogEx(INFO, " 32 SSP = %u US (expect 9 or 10) " _GREEN_("ok"), SSP_TO_US(32)); - PrintAndLogEx(INFO, " 320 SSP = %u US (expect 94 or 95) " _GREEN_("ok"), SSP_TO_US(320)); - PrintAndLogEx(INFO, "1024 SSP = %u US (expect 302) " _GREEN_("ok"), SSP_TO_US(1024)); + PrintAndLogEx(INFO, " 32 SSP = %i US (expect 9 or 10) " _GREEN_("ok"), SSP_TO_US(32)); + PrintAndLogEx(INFO, " 320 SSP = %i US (expect 94 or 95) " _GREEN_("ok"), SSP_TO_US(320)); + PrintAndLogEx(INFO, "1024 SSP = %i US (expect 302) " _GREEN_("ok"), SSP_TO_US(1024)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "ETU to SSP CLK 3.39MHz conversions"); - PrintAndLogEx(INFO, " 1 ETU = %u SSP (expect 32) " _GREEN_("ok"), ETU_TO_SSP(1)); - PrintAndLogEx(INFO, " 10 ETU = %u SSP (expect 320) " _GREEN_("ok"), ETU_TO_SSP(10)); - PrintAndLogEx(INFO, " 32 ETU = %u SSP (expect 1024) " _GREEN_("ok"), ETU_TO_SSP(32)); + PrintAndLogEx(INFO, " 1 ETU = %i SSP (expect 32) " _GREEN_("ok"), HF14_ETU_TO_SSP(1)); + PrintAndLogEx(INFO, " 10 ETU = %i SSP (expect 320) " _GREEN_("ok"), HF14_ETU_TO_SSP(10)); + PrintAndLogEx(INFO, " 32 ETU = %i SSP (expect 1024) " _GREEN_("ok"), HF14_ETU_TO_SSP(32)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "SSP CLK 3.39MHz to ETU conversions"); - PrintAndLogEx(INFO, "1024 SSP = %u ETU (expect 32) " _GREEN_("ok"), SSP_TO_ETU(1024)); - PrintAndLogEx(INFO, " 320 SSP = %u ETU (expect 10) " _GREEN_("ok"), SSP_TO_ETU(320)); - PrintAndLogEx(INFO, " 32 SSP = %u ETU (expect 1) " _GREEN_("ok"), SSP_TO_ETU(32)); - } else if (etu) { + PrintAndLogEx(INFO, "1024 SSP = %i ETU (expect 32) " _GREEN_("ok"), HF14_SSP_TO_ETU(1024)); + PrintAndLogEx(INFO, " 320 SSP = %i ETU (expect 10) " _GREEN_("ok"), HF14_SSP_TO_ETU(320)); + PrintAndLogEx(INFO, " 32 SSP = %i ETU (expect 1) " _GREEN_("ok"), HF14_SSP_TO_ETU(32)); + } else if (etu > -1) { - PrintAndLogEx(INFO, " %d ETU = %u us ", ETU_TO_US(etu), 0); - PrintAndLogEx(INFO, " %d ETU = %u SSP ", ETU_TO_SSP(etu), 0); - } else if (us) { - PrintAndLogEx(INFO, " %d us = %u ETU ", US_TO_ETU(us), 0); - PrintAndLogEx(INFO, " %d us = %u SSP ", US_TO_SSP(us), 0); + PrintAndLogEx(INFO, " %i ETU = %3.2f µS", etu, HF14_ETU_TO_US(etu)); + PrintAndLogEx(INFO, " %i ETU = %i SSP", etu, HF14_ETU_TO_SSP(etu)); + } else if (us > -1) { + PrintAndLogEx(INFO, " %i µS = %3.2f ETU = %u SSP", us, US_TO_ETU(us), US_TO_SSP(us)); } return PM3_SUCCESS; diff --git a/client/src/cmddata.c b/client/src/cmddata.c index 9524cbc63..8654c8476 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -38,8 +38,9 @@ #include "mbedtls/bignum.h" // big num #include "mbedtls/entropy.h" // #include "mbedtls/ctr_drbg.h" // random generator +#include "atrs.h" // ATR lookup -uint8_t g_DemodBuffer[MAX_DEMOD_BUF_LEN]; +uint8_t g_DemodBuffer[MAX_DEMOD_BUF_LEN] = { 0x00 }; size_t g_DemodBufferLen = 0; int32_t g_DemodStartIdx = 0; int g_DemodClock = 0; @@ -221,7 +222,7 @@ static int CmdSetDebugMode(const char *Cmd) { return PM3_SUCCESS; } -// max output to 512 bits if we have more +// max output to MAX_DEMODULATION_BITS bits if we have more // doesn't take inconsideration where the demod offset or bitlen found. int printDemodBuff(uint8_t offset, bool strip_leading, bool invert, bool print_hex) { size_t len = g_DemodBufferLen; @@ -232,7 +233,7 @@ int printDemodBuff(uint8_t offset, bool strip_leading, bool invert, bool print_h uint8_t *buf = calloc(len, sizeof(uint8_t)); if (buf == NULL) { - PrintAndLogEx(WARNING, "dail, cannot allocate memory"); + PrintAndLogEx(WARNING, "fail, cannot allocate memory"); return PM3_EMALLOC; } memcpy(buf, g_DemodBuffer, len); @@ -256,8 +257,8 @@ int printDemodBuff(uint8_t offset, bool strip_leading, bool invert, bool print_h len = (g_DemodBufferLen - offset); } - if (len > 512) { - len = 512; + if (len > MAX_DEMODULATION_BITS) { + len = MAX_DEMODULATION_BITS; } if (invert) { @@ -274,8 +275,8 @@ int printDemodBuff(uint8_t offset, bool strip_leading, bool invert, bool print_h if (print_hex) { p = (buf + offset); - char hex[512] = {0x00}; - int num_bits = binarraytohex(hex, sizeof(hex), (char *)p, len); + char hex[MAX_DEMODULATION_BITS + 1] = {0x00}; + int num_bits = binarray_2_hex(hex, sizeof(hex), (char *)p, len); if (num_bits == 0) { p = NULL; free(buf); @@ -440,7 +441,7 @@ int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxlen, bool amplify, b clk /= 2; } if (errCnt < 0 || bitlen < 16) { //if fatal error (or -1) - PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) No data found errors:%d, %s bitlen:%zu, clock:%d" + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) No data found errors:%d, %s bitlen:%zu, clock:%i" , errCnt , (invert) ? "inverted," : "" , bitlen @@ -451,7 +452,7 @@ int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxlen, bool amplify, b } if (errCnt > maxErr) { - PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%zu, clock:%d" + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%zu, clock:%i" , errCnt , bitlen , clk @@ -461,7 +462,7 @@ int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxlen, bool amplify, b } if (verbose) { - PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) using clock:%d, %sbits found:%zu, start index %d" + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) using clock:%i, %sbits found:%zu, start index %d" , clk , (invert) ? "inverted, " : "" , bitlen @@ -475,14 +476,14 @@ int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxlen, bool amplify, b if (verbose) { if (errCnt > 0) - PrintAndLogEx(DEBUG, "# Errors during demoding (shown as 7 in bit stream): %d", errCnt); + PrintAndLogEx(DEBUG, "# Errors during demoding (shown as 7 in bit stream)... " _RED_("%d"), errCnt); if (askType) { - PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Manchester") " - clock %d - decoded bitstream", clk); - PrintAndLogEx(INFO, "---------------------------------------------"); + PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Manchester") " - clock " _YELLOW_("%i") " - decoded bitstream", clk); + PrintAndLogEx(INFO, "-----------------------------------------------"); } else { - PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Raw") " - clock %d - decoded bitstream", clk); - PrintAndLogEx(INFO, "--------------------------------------"); + PrintAndLogEx(SUCCESS, _YELLOW_("ASK/Raw") " - clock " _YELLOW_("%i") " - decoded bitstream", clk); + PrintAndLogEx(INFO, "----------------------------------------"); } printDemodBuff(0, false, false, false); @@ -527,7 +528,7 @@ static int Cmdaskmandemod(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); bool amplify = arg_get_lit(ctx, 1); - uint8_t clk = (uint8_t)arg_get_int_def(ctx, 2, 0) & 0xFF; + uint16_t clk = (uint16_t)arg_get_int_def(ctx, 2, 0); bool invert = arg_get_lit(ctx, 3); bool st = arg_get_lit(ctx, 4); uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 5, 100) & 0xFF; @@ -563,7 +564,11 @@ static int Cmdmandecoderaw(const char *Cmd) { return PM3_ESOFT; } - uint8_t bits[MAX_DEMOD_BUF_LEN] = {0}; + uint8_t *bits = calloc(MAX_DEMOD_BUF_LEN, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } // make sure its just binary data 0|1|7 in buffer int high = 0, low = 0; @@ -578,6 +583,7 @@ static int Cmdmandecoderaw(const char *Cmd) { if (high > 7 || low < 0) { PrintAndLogEx(ERR, "Error: please first raw demod then manchester raw decode"); + free(bits); return PM3_ESOFT; } @@ -586,6 +592,7 @@ static int Cmdmandecoderaw(const char *Cmd) { uint16_t err_cnt = manrawdecode(bits, &size, invert, &offset); if (err_cnt > max_err) { PrintAndLogEx(ERR, "Too many errors attempting to decode " _RED_("%i"), err_cnt); + free(bits); return PM3_ESOFT; } @@ -610,6 +617,7 @@ static int Cmdmandecoderaw(const char *Cmd) { } setDemodBuff(bits, size, 0); setClockGrid(g_DemodClock * 2, g_DemodStartIdx); + free(bits); return PM3_SUCCESS; } @@ -650,17 +658,27 @@ static int CmdBiphaseDecodeRaw(const char *Cmd) { return PM3_ESOFT; } - uint8_t bits[MAX_DEMOD_BUF_LEN] = {0}; - size_t size = sizeof(bits); - if (!getDemodBuff(bits, &size)) return PM3_ESOFT; + uint8_t *bits = calloc(MAX_DEMOD_BUF_LEN, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + + size_t size = MAX_DEMOD_BUF_LEN; + if (!getDemodBuff(bits, &size)) { + free(bits); + return PM3_ESOFT; + } int err_cnt = BiphaseRawDecode(bits, &size, &offset, invert); if (err_cnt < 0) { PrintAndLogEx(ERR, "Error during decode " _RED_("%i"), err_cnt); + free(bits); return PM3_ESOFT; } if (err_cnt > max_err) { PrintAndLogEx(ERR, "Too many errors attempting to decode " _RED_("%i"), err_cnt); + free(bits); return PM3_ESOFT; } @@ -673,6 +691,7 @@ static int CmdBiphaseDecodeRaw(const char *Cmd) { setDemodBuff(bits, size, 0); setClockGrid(g_DemodClock * 2, g_DemodStartIdx + g_DemodClock * offset); + free(bits); return PM3_SUCCESS; } @@ -680,10 +699,16 @@ static int CmdBiphaseDecodeRaw(const char *Cmd) { int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) { //ask raw demod g_GraphBuffer first - uint8_t bs[MAX_DEMOD_BUF_LEN]; - size_t size = getFromGraphBuf(bs); + uint8_t *bs = calloc(MAX_DEMOD_BUF_LEN, sizeof(uint8_t)); + if (bs == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + + size_t size = getFromGraphBufEx(bs, MAX_DEMOD_BUF_LEN); if (size == 0) { PrintAndLogEx(DEBUG, "DEBUG: no data in graphbuf"); + free(bs); return PM3_ESOFT; } int startIdx = 0; @@ -691,6 +716,7 @@ int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) { 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); + free(bs); return PM3_ESOFT; } @@ -698,10 +724,12 @@ int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) { errCnt = BiphaseRawDecode(bs, &size, &offset, invert); if (errCnt < 0) { if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: Error BiphaseRawDecode: %d", errCnt); + free(bs); return PM3_ESOFT; } if (errCnt > maxErr) { if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: Error BiphaseRawDecode too many errors: %d", errCnt); + free(bs); return PM3_ESOFT; } @@ -715,6 +743,7 @@ int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) { PrintAndLogEx(DEBUG, "Biphase Decoded using offset %d | clock %d | #errors %d | start index %d\ndata\n", offset, clk, errCnt, (startIdx + clk * offset / 2)); printDemodBuff(offset, false, false, false); } + free(bs); return PM3_SUCCESS; } @@ -740,7 +769,7 @@ static int Cmdaskbiphdemod(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF; + uint16_t clk = (uint16_t)arg_get_int_def(ctx, 1, 0); bool invert = arg_get_lit(ctx, 2); int offset = arg_get_int_def(ctx, 3, 0); uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 4, 50) & 0xFF; @@ -773,7 +802,7 @@ static int Cmdaskrawdemod(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); bool amplify = arg_get_lit(ctx, 1); - uint8_t clk = (uint8_t)arg_get_int_def(ctx, 2, 0) & 0xFF; + uint16_t clk = (uint16_t)arg_get_int_def(ctx, 2, 0); bool invert = arg_get_lit(ctx, 3); bool st = arg_get_lit(ctx, 4); uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 5, 100) & 0xFF; @@ -785,9 +814,9 @@ static int Cmdaskrawdemod(const char *Cmd) { int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveGrph, bool verbose) { // sanity check - if (window > len) window = len; - - if (verbose) PrintAndLogEx(INFO, "performing " _YELLOW_("%zu") " correlations", g_GraphTraceLen - window); + if (window > len) { + window = len; + } //test double autocv = 0.0; // Autocovariance value @@ -801,6 +830,9 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG int *correl_buf = calloc(MAX_GRAPH_TRACE_LEN, sizeof(int)); + uint8_t peak_cnt = 0; + size_t peaks[10] = {0}; + for (size_t i = 0; i < len - window; ++i) { for (size_t j = 0; j < (len - i); j++) { @@ -815,58 +847,55 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG double ac_value = autocv / variance; // keep track of which distance is repeating. - if (ac_value > 1) { + // A value near 1.0 or more indicates a correlation in the signal + if (ac_value > 0.95f) { correlation = i - lastmax; lastmax = i; + + if ((correlation > 1) && peak_cnt < ARRAYLEN(peaks)) { + peaks[peak_cnt++] = correlation; + } } } - // - int hi = 0, idx = 0; - int distance = 0, hi_1 = 0, idx_1 = 0; - for (size_t i = 0; i <= len; ++i) { - if (correl_buf[i] > hi) { - hi = correl_buf[i]; - idx = i; + // Find shorts distance between peaks + int distance = -1; + for (size_t i = 0; i < ARRAYLEN(peaks); ++i) { + + PrintAndLogEx(DEBUG, "%zu | %zu", i, peaks[i]); + if (peaks[i] < 128) { + continue; + } + + if (distance == -1) { + distance = peaks[i]; + continue; + } + + if (peaks[i] < distance) { + distance = peaks[i]; } } - for (size_t i = idx + 1; i <= window; ++i) { - if (correl_buf[i] > hi_1) { - hi_1 = correl_buf[i]; - idx_1 = i; + if (distance > -1) { + if (verbose) { + PrintAndLogEx(SUCCESS, "Possible correlation at "_YELLOW_("%4d") " samples", distance); } - } - - int foo = ABS(hi - hi_1); - int bar = (int)((int)((hi + hi_1) / 2) * 0.04); - - if (verbose && foo < bar) { - distance = idx_1 - idx; - PrintAndLogEx(SUCCESS, "possible visible correlation "_YELLOW_("%4d") " samples", distance); - } else if (verbose && (correlation > 1)) { - PrintAndLogEx(SUCCESS, "possible correlation " _YELLOW_("%4zu") " samples", correlation); } else { - PrintAndLogEx(FAILED, "no repeating pattern found, try increasing window size"); + PrintAndLogEx(HINT, "No repeating pattern found, try increasing window size"); + // return value -1, indication to increase window size + return -1; } - int retval = correlation; if (SaveGrph) { //g_GraphTraceLen = g_GraphTraceLen - window; memcpy(out, correl_buf, len * sizeof(int)); - if (distance > 0) { - setClockGrid(distance, idx); - retval = distance; - } else - setClockGrid(correlation, idx); - - g_CursorCPos = idx_1; - g_CursorDPos = idx_1 + retval; + setClockGrid(distance, 0); g_DemodBufferLen = 0; RepaintGraphWindow(); } free(correl_buf); - return retval; + return distance; } static int CmdAutoCorr(const char *Cmd) { @@ -1010,7 +1039,11 @@ static int CmdUndecimate(const char *Cmd) { CLIParserFree(ctx); //We have memory, don't we? - int swap[MAX_GRAPH_TRACE_LEN] = {0}; + int *swap = calloc(MAX_GRAPH_TRACE_LEN, sizeof(int)); + if (swap == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } uint32_t g_index = 0, s_index = 0; while (g_index < g_GraphTraceLen && s_index + factor < MAX_GRAPH_TRACE_LEN) { int count = 0; @@ -1027,6 +1060,7 @@ static int CmdUndecimate(const char *Cmd) { memcpy(g_GraphBuffer, swap, s_index * sizeof(int)); g_GraphTraceLen = s_index; RepaintGraphWindow(); + free(swap); return PM3_SUCCESS; } @@ -1106,20 +1140,18 @@ static int CmdDetectClockRate(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "data detectclock", "Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer", - "data detectclock -A --> detect clock of an ask wave in GraphBuffer\n" - "data detectclock -F --> detect clock of an fsk wave in GraphBuffer\n" - "data detectclock -N --> detect clock of an psk wave in GraphBuffer\n" - "data detectclock -P --> detect clock of an nrz/direct wave in GraphBuffer" + "data detectclock --ask\n" + "data detectclock --nzr --> detect clock of an nrz/direct wave in GraphBuffer\n" ); void *argtable[] = { arg_param_begin, - arg_lit0("A", "ASK", "specify ASK modulation clock detection"), - arg_lit0("F", "FSK", "specify FSK modulation clock detection"), - arg_lit0("N", "NZR", "specify NZR/DIRECT modulation clock detection"), - arg_lit0("P", "PSK", "specify PSK modulation clock detection"), + arg_lit0(NULL, "ask", "specify ASK modulation clock detection"), + arg_lit0(NULL, "fsk", "specify FSK modulation clock detection"), + arg_lit0(NULL, "nzr", "specify NZR/DIRECT modulation clock detection"), + arg_lit0(NULL, "psk", "specify PSK modulation clock detection"), arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIExecWithReturn(ctx, Cmd, argtable, true); bool a = arg_get_lit(ctx, 1); bool f = arg_get_lit(ctx, 2); bool n = arg_get_lit(ctx, 3); @@ -1130,6 +1162,25 @@ static int CmdDetectClockRate(const char *Cmd) { if (tmp > 1) { PrintAndLogEx(WARNING, "Only specify one modulation"); return PM3_EINVARG; + } else if (tmp == 0) { + + int clock = GetFskClock("", false); + if (clock > 0) { + PrintAndLogEx(SUCCESS, "FSK Clock... %d", clock); + } + clock = GetAskClock("", false); + if (clock > 0) { + PrintAndLogEx(SUCCESS, "ASK Clock... %d", clock); + } + clock = GetNrzClock("", false); + if (clock > 0) { + PrintAndLogEx(SUCCESS, "NRZ Clock... %d", clock); + } + clock = GetPskClock("", false); + if (clock > 0) { + PrintAndLogEx(SUCCESS, "PSK Clock... %d", clock); + } + return PM3_SUCCESS; } if (a) @@ -1371,7 +1422,8 @@ int NRZrawDemod(int clk, int invert, int maxErr, bool verbose) { if (errCnt > 0 && (verbose || g_debugMode)) PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Errors during Demoding (shown as 7 in bit stream): %d", errCnt); if (verbose || g_debugMode) { - PrintAndLogEx(NORMAL, "NRZ demoded bitstream:"); + PrintAndLogEx(SUCCESS, "NRZ demoded bitstream"); + PrintAndLogEx(INFO, "---------------------"); // Now output the bitstream to the scrollback by line of 16 bits printDemodBuff(0, false, invert, false); } @@ -1399,7 +1451,7 @@ static int CmdNRZrawDemod(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF; + uint16_t clk = (uint16_t)arg_get_int_def(ctx, 1, 0); bool invert = arg_get_lit(ctx, 2); uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 3, 100) & 0xFF; CLIParserFree(ctx); @@ -1429,7 +1481,7 @@ int CmdPSK1rawDemod(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t clk = (uint8_t)arg_get_int_def(ctx, 1, 0) & 0xFF; + uint16_t clk = (uint16_t)arg_get_int_def(ctx, 1, 0); bool invert = arg_get_lit(ctx, 2); uint8_t max_err = (uint8_t)arg_get_int_def(ctx, 3, 100) & 0xFF; CLIParserFree(ctx); @@ -1603,7 +1655,8 @@ int CmdGrid(const char *Cmd) { g_PlotGridX = arg_get_dbl_def(ctx, 1, 0); g_PlotGridY = arg_get_dbl_def(ctx, 2, 0); CLIParserFree(ctx); - PrintAndLogEx(INFO, "Setting X %.0f Y %.0f", g_PlotGridX, g_PlotGridY); + + PrintAndLogEx(DEBUG, "Setting X %.0f Y %.0f", g_PlotGridX, g_PlotGridY); g_PlotGridXdefault = g_PlotGridX; g_PlotGridYdefault = g_PlotGridY; RepaintGraphWindow(); @@ -1707,7 +1760,11 @@ int CmdHpf(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); - uint8_t bits[g_GraphTraceLen]; + uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t size = getFromGraphBuf(bits); removeSignalOffset(bits, size); // push it back to graph @@ -1716,6 +1773,7 @@ int CmdHpf(const char *Cmd) { computeSignalProperties(bits, size); RepaintGraphWindow(); + free(bits); return PM3_SUCCESS; } @@ -1753,58 +1811,77 @@ int getSamplesEx(uint32_t start, uint32_t end, bool verbose, bool ignore_lf_conf uint32_t n = end - start; - if (n == 0 || n > g_pm3_capabilities.bigbuf_size - 1) + if (n == 0 || n > g_pm3_capabilities.bigbuf_size - 1) { n = g_pm3_capabilities.bigbuf_size - 1; + } - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Reading " _YELLOW_("%u") " bytes from device memory", n); + } - PacketResponseNG response; - if (!GetFromDevice(BIG_BUF, got, n, start, NULL, 0, &response, 10000, true)) { + PacketResponseNG resp; + if (GetFromDevice(BIG_BUF, got, n, start, NULL, 0, &resp, 10000, true) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } - if (verbose) PrintAndLogEx(SUCCESS, "Data fetched"); + if (verbose) { + PrintAndLogEx(SUCCESS, "Data fetched"); + } uint8_t bits_per_sample = 8; // Old devices without this feature would send 0 at arg[0] - if (response.oldarg[0] > 0 && (ignore_lf_config == false)) { - sample_config *sc = (sample_config *) response.data.asBytes; - if (verbose) PrintAndLogEx(INFO, "Samples @ " _YELLOW_("%d") " bits/smpl, decimation 1:%d ", sc->bits_per_sample, sc->decimation); + if (resp.oldarg[0] > 0 && (ignore_lf_config == false)) { + sample_config *sc = (sample_config *) resp.data.asBytes; + if (verbose) { + PrintAndLogEx(INFO, "Samples @ " _YELLOW_("%d") " bits/smpl, decimation 1:%d ", sc->bits_per_sample, sc->decimation); + } bits_per_sample = sc->bits_per_sample; } + return getSamplesFromBufEx(got, n, bits_per_sample, verbose);; +} + +int getSamplesFromBufEx(uint8_t *data, size_t sample_num, uint8_t bits_per_sample, bool verbose) { + + size_t max_num = MIN(sample_num, MAX_GRAPH_TRACE_LEN); + if (bits_per_sample < 8) { if (verbose) PrintAndLogEx(INFO, "Unpacking..."); - BitstreamOut_t bout = { got, bits_per_sample * n, 0}; - uint32_t j = 0; - for (j = 0; j * bits_per_sample < n * 8 && j * bits_per_sample < MAX_GRAPH_TRACE_LEN * 8; j++) { + BitstreamOut_t bout = {data, bits_per_sample * sample_num, 0}; + size_t j = 0; + for (j = 0; j < max_num; j++) { uint8_t sample = getByte(bits_per_sample, &bout); g_GraphBuffer[j] = ((int) sample) - 127; } g_GraphTraceLen = j; - if (verbose) PrintAndLogEx(INFO, "Unpacked %d samples", j); + if (verbose) PrintAndLogEx(INFO, "Unpacked %zu samples", j); } else { - for (uint32_t j = 0; j < n; j++) { - g_GraphBuffer[j] = ((int)got[j]) - 127; + for (size_t j = 0; j < max_num; j++) { + g_GraphBuffer[j] = ((int)data[j]) - 127; } - g_GraphTraceLen = n; + g_GraphTraceLen = max_num; } - uint8_t bits[g_GraphTraceLen]; + uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t size = getFromGraphBuf(bits); // set signal properties low/high/mean/amplitude and is_noise detection computeSignalProperties(bits, size); + free(bits); setClockGrid(0, 0); g_DemodBufferLen = 0; RepaintGraphWindow(); + return PM3_SUCCESS; } @@ -1901,16 +1978,16 @@ int CmdTuneSamples(const char *Cmd) { struct p *package = (struct p *)resp.data.asBytes; if (package->v_lf125 > NON_VOLTAGE) - PrintAndLogEx(SUCCESS, "LF antenna: %5.2f V - %.2f kHz", (package->v_lf125 * ANTENNA_ERROR) / 1000.0, LF_DIV2FREQ(LF_DIVISOR_125)); + PrintAndLogEx(SUCCESS, "At %.2f kHz .......... " _YELLOW_("%5.2f") " V", LF_DIV2FREQ(LF_DIVISOR_125), (package->v_lf125 * ANTENNA_ERROR) / 1000.0); if (package->v_lf134 > NON_VOLTAGE) - PrintAndLogEx(SUCCESS, "LF antenna: %5.2f V - %.2f kHz", (package->v_lf134 * ANTENNA_ERROR) / 1000.0, LF_DIV2FREQ(LF_DIVISOR_134)); + PrintAndLogEx(SUCCESS, "At %.2f kHz .......... " _YELLOW_("%5.2f") " V", LF_DIV2FREQ(LF_DIVISOR_134), (package->v_lf134 * ANTENNA_ERROR) / 1000.0); if (package->v_lfconf > NON_VOLTAGE && package->divisor > 0 && package->divisor != LF_DIVISOR_125 && package->divisor != LF_DIVISOR_134) - PrintAndLogEx(SUCCESS, "LF antenna: %5.2f V - %.2f kHz", (package->v_lfconf * ANTENNA_ERROR) / 1000.0, LF_DIV2FREQ(package->divisor)); + PrintAndLogEx(SUCCESS, "At %.2f kHz .......... " _YELLOW_("%5.2f") " V", LF_DIV2FREQ(package->divisor), (package->v_lfconf * ANTENNA_ERROR) / 1000.0); if (package->peak_v > NON_VOLTAGE && package->peak_f > 0) - PrintAndLogEx(SUCCESS, "LF optimal: %5.2f V - %6.2f kHz", (package->peak_v * ANTENNA_ERROR) / 1000.0, LF_DIV2FREQ(package->peak_f)); + PrintAndLogEx(SUCCESS, "At %.2f kHz optimal... " _YELLOW_("%5.2f") " V", LF_DIV2FREQ(package->peak_f), (package->peak_v * ANTENNA_ERROR) / 1000.0); // Empirical measures in mV const double vdd_rdv4 = 9000; @@ -1931,6 +2008,9 @@ int CmdTuneSamples(const char *Cmd) { break; } } + + PrintAndLogEx(SUCCESS, ""); + PrintAndLogEx(SUCCESS, "Approx. Q factor measurement (*)"); double lfq1 = 0; if (s4 != 0) { // we got all our points of interest double a = package->results[s2 - 1]; @@ -1940,12 +2020,12 @@ int CmdTuneSamples(const char *Cmd) { double d = package->results[s4]; double f2 = LF_DIV2FREQ(s4 - 1 + (c - v_3db_scaled) / (c - d)); lfq1 = LF_DIV2FREQ(package->peak_f) / (f1 - f2); - PrintAndLogEx(SUCCESS, "Approx. Q factor (*): %.1lf by frequency bandwidth measurement", lfq1); + PrintAndLogEx(SUCCESS, "Frequency bandwidth..... " _YELLOW_("%.1lf"), lfq1); } // Q measure with Vlr=Q*(2*Vdd/pi) double lfq2 = (double)package->peak_v * 3.14 / 2 / vdd; - PrintAndLogEx(SUCCESS, "Approx. Q factor (*): %.1lf by peak voltage measurement", lfq2); + PrintAndLogEx(SUCCESS, "Peak voltage............ " _YELLOW_("%.1lf") , lfq2); // cross-check results if (lfq1 > 3) { double approx_vdd = (double)package->peak_v * 3.14 / 2 / lfq1; @@ -1970,34 +2050,40 @@ int CmdTuneSamples(const char *Cmd) { memset(judgement, 0, sizeof(judgement)); // LF evaluation if (package->peak_v < LF_UNUSABLE_V) - snprintf(judgement, sizeof(judgement), _RED_("UNUSABLE")); + snprintf(judgement, sizeof(judgement), _RED_("unusable")); else if (package->peak_v < LF_MARGINAL_V) - snprintf(judgement, sizeof(judgement), _YELLOW_("MARGINAL")); + snprintf(judgement, sizeof(judgement), _YELLOW_("marginal")); else - snprintf(judgement, sizeof(judgement), _GREEN_("OK")); + snprintf(judgement, sizeof(judgement), _GREEN_("ok")); - PrintAndLogEx((package->peak_v < LF_UNUSABLE_V) ? WARNING : SUCCESS, "LF antenna is %s", judgement); + PrintAndLogEx((package->peak_v < LF_UNUSABLE_V) ? WARNING : SUCCESS, "LF antenna ( %s )", judgement); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "---------- " _CYAN_("HF Antenna") " ----------"); // HF evaluation - if (package->v_hf > NON_VOLTAGE) - PrintAndLogEx(SUCCESS, "HF antenna: %5.2f V - 13.56 MHz", (package->v_hf * ANTENNA_ERROR) / 1000.0); + if (package->v_hf > NON_VOLTAGE) { + PrintAndLogEx(SUCCESS, "13.56 MHz............... " _YELLOW_("%5.2f") " V", (package->v_hf * ANTENNA_ERROR) / 1000.0); + } memset(judgement, 0, sizeof(judgement)); + PrintAndLogEx(SUCCESS, ""); + PrintAndLogEx(SUCCESS, "Approx. Q factor measurement (*)"); + if (package->v_hf >= HF_UNUSABLE_V) { // Q measure with Vlr=Q*(2*Vdd/pi) double hfq = (double)package->v_hf * 3.14 / 2 / vdd; - PrintAndLogEx(SUCCESS, "Approx. Q factor (*): %.1lf by peak voltage measurement", hfq); + PrintAndLogEx(SUCCESS, "peak voltage............ " _YELLOW_("%.1lf") , hfq); } - if (package->v_hf < HF_UNUSABLE_V) - snprintf(judgement, sizeof(judgement), _RED_("UNUSABLE")); - else if (package->v_hf < HF_MARGINAL_V) - snprintf(judgement, sizeof(judgement), _YELLOW_("MARGINAL")); - else - snprintf(judgement, sizeof(judgement), _GREEN_("OK")); - PrintAndLogEx((package->v_hf < HF_UNUSABLE_V) ? WARNING : SUCCESS, "HF antenna is %s", judgement); + if (package->v_hf < HF_UNUSABLE_V) + snprintf(judgement, sizeof(judgement), _RED_("unusable")); + else if (package->v_hf < HF_MARGINAL_V) + snprintf(judgement, sizeof(judgement), _YELLOW_("marginal")); + else + snprintf(judgement, sizeof(judgement), _GREEN_("ok")); + + PrintAndLogEx((package->v_hf < HF_UNUSABLE_V) ? WARNING : SUCCESS, "HF antenna ( %s )", judgement); PrintAndLogEx(NORMAL, "\n(*) Q factor must be measured without tag on the antenna"); // graph LF measurements @@ -2009,8 +2095,16 @@ int CmdTuneSamples(const char *Cmd) { } if (test1 > 0) { - PrintAndLogEx(SUCCESS, "\nDisplaying LF tuning graph. Divisor %d (blue) is %.2f kHz, %d (red) is %.2f kHz.\n\n", - LF_DIVISOR_134, LF_DIV2FREQ(LF_DIVISOR_134), LF_DIVISOR_125, LF_DIV2FREQ(LF_DIVISOR_125)); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-------- " _CYAN_("LF tuning graph") " ---------"); + PrintAndLogEx(SUCCESS, "Blue line Divisor %d / %.2f kHz" + , LF_DIVISOR_134 + , LF_DIV2FREQ(LF_DIVISOR_134) + ); + PrintAndLogEx(SUCCESS, "Red line Divisor %d / %.2f kHz\n\n" + , LF_DIVISOR_125 + , LF_DIV2FREQ(LF_DIVISOR_125) + ); g_GraphTraceLen = 256; g_CursorCPos = LF_DIVISOR_125; g_CursorDPos = LF_DIVISOR_134; @@ -2094,12 +2188,17 @@ static int CmdLoad(const char *Cmd) { PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " samples", g_GraphTraceLen); if (nofix == false) { - uint8_t bits[g_GraphTraceLen]; + uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t size = getFromGraphBuf(bits); removeSignalOffset(bits, size); setGraphBuf(bits, size); computeSignalProperties(bits, size); + free(bits); } setClockGrid(0, 0); @@ -2224,19 +2323,24 @@ int CmdNorm(const char *Cmd) { if (g_GraphBuffer[i] < min) min = g_GraphBuffer[i]; } - if (max != min) { + if ((g_GraphTraceLen > 10) && (max != min)) { for (uint32_t i = 0; i < g_GraphTraceLen; ++i) { g_GraphBuffer[i] = ((long)(g_GraphBuffer[i] - ((max + min) / 2)) * 256) / (max - min); //marshmelow: adjusted *1000 to *256 to make +/- 128 so demod commands still work } } - uint8_t bits[g_GraphTraceLen]; + uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t size = getFromGraphBuf(bits); // set signal properties low/high/mean/amplitude and is_noise detection computeSignalProperties(bits, size); RepaintGraphWindow(); + free(bits); return PM3_SUCCESS; } @@ -2377,12 +2481,17 @@ static int CmdDirectionalThreshold(const char *Cmd) { directionalThreshold(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen, up, down); // set signal properties low/high/mean/amplitude and isnoice detection - uint8_t bits[g_GraphTraceLen]; + uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t size = getFromGraphBuf(bits); // set signal properties low/high/mean/amplitude and is_noice detection computeSignalProperties(bits, size); RepaintGraphWindow(); + free(bits); return PM3_SUCCESS; } @@ -2420,11 +2529,16 @@ static int CmdZerocrossings(const char *Cmd) { } } - uint8_t bits[g_GraphTraceLen]; + uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t size = getFromGraphBuf(bits); // set signal properties low/high/mean/amplitude and is_noise detection computeSignalProperties(bits, size); RepaintGraphWindow(); + free(bits); return PM3_SUCCESS; } @@ -2733,11 +2847,16 @@ static int CmdDataIIR(const char *Cmd) { iceSimple_Filter(g_GraphBuffer, g_GraphTraceLen, k); - uint8_t bits[g_GraphTraceLen]; + uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t size = getFromGraphBuf(bits); // set signal properties low/high/mean/amplitude and is_noise detection computeSignalProperties(bits, size); RepaintGraphWindow(); + free(bits); return PM3_SUCCESS; } @@ -2911,14 +3030,19 @@ static int CmdAsn1Decoder(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("d", NULL, "", "ASN1 encoded byte array"), + arg_str0("d", NULL, "", "ASN1 encoded byte array"), + arg_lit0("t", "test", "perform selftest"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int dlen = 2048; uint8_t data[2048]; CLIGetHexWithReturn(ctx, 1, data, &dlen); + bool selftest = arg_get_lit(ctx, 2); CLIParserFree(ctx); + if (selftest) { + return asn1_selftest(); + } // print ASN1 decoded array in TLV view PrintAndLogEx(INFO, "---------------- " _CYAN_("ASN1 TLV") " -----------------"); @@ -2939,7 +3063,7 @@ static int CmdDiff(const char *Cmd) { // "data diff -a fileA --cb\n" "data diff --fa fileA -b fileB\n" "data diff --fa fileA --fb fileB\n" - "data diff --ea --cb\n" +// "data diff --ea --cb\n" ); void *argtable[] = { @@ -3083,10 +3207,19 @@ static int CmdDiff(const char *Cmd) { PrintAndLogEx(INFO, "inB null"); int hdr_sln = (width * 4) + 2; + char hdr0[300] = {0}; - char hdr0[200] = " # | " _CYAN_("a"); - memset(hdr0 + strlen(hdr0), ' ', hdr_sln - 2); - strcat(hdr0 + strlen(hdr0), "| " _CYAN_("b")); + int max_fn_space = (width * 5); + + if (fnlenA && fnlenB && (max_fn_space > fnlenA) && (max_fn_space > fnlenB)) { + snprintf(hdr0, sizeof(hdr0) - 1, " # | " _CYAN_("%.*s"), max_fn_space, filenameA); + memset(hdr0 + strlen(hdr0), ' ', hdr_sln - strlen(filenameA) - 1); + snprintf(hdr0 + strlen(hdr0), sizeof(hdr0) - 1 - strlen(hdr0), "| " _CYAN_("%.*s"), max_fn_space, filenameB); + } else { + strcat(hdr0, " # | " _CYAN_("a")); + memset(hdr0 + strlen(hdr0), ' ', hdr_sln - 2); + strcat(hdr0 + strlen(hdr0), "| " _CYAN_("b")); + } char hdr1[200] = "----+"; memset(hdr1 + strlen(hdr1), '-', hdr_sln); @@ -3191,6 +3324,7 @@ static int CmdNumCon(const char *Cmd) { arg_str0(NULL, "hex", "", "hexadecimal value"), arg_str0(NULL, "bin", "", "binary value"), arg_lit0("i", NULL, "print inverted value"), + arg_lit0("r", NULL, "print reversed value"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -3204,14 +3338,15 @@ static int CmdNumCon(const char *Cmd) { int hlen = 256; char hex[256]; memset(hex, 0, sizeof(hex)); - res = CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)hex, sizeof(hex), &hlen); + res |= CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)hex, sizeof(hex), &hlen); int blen = 256; char bin[256]; memset(bin, 0, sizeof(bin)); - res = CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)bin, sizeof(bin), &blen); + res |= CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)bin, sizeof(bin), &blen); bool shall_invert = arg_get_lit(ctx, 4); + bool shall_reverse = arg_get_lit(ctx, 5); CLIParserFree(ctx); // sanity checks @@ -3222,6 +3357,7 @@ static int CmdNumCon(const char *Cmd) { // results for MPI actions bool ret = false; + (void) ret; // container of big number mbedtls_mpi N; @@ -3252,11 +3388,6 @@ static int CmdNumCon(const char *Cmd) { mbedtls_mpi_init(&base); mbedtls_mpi_add_int(&base, &base, 10); - if (shall_invert) { - PrintAndLogEx(INFO, "should invert"); - MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&N, &N, &base)); - } - // printing typedef struct { const char *desc; @@ -3264,9 +3395,9 @@ static int CmdNumCon(const char *Cmd) { } radix_t; radix_t radix[] = { - {"dec..... ", 10}, - {"hex..... 0x", 16}, - {"bin..... 0b", 2} + {"dec... ", 10}, + {"hex... ", 16}, + {"bin... ", 2} }; char s[600] = {0}; @@ -3274,11 +3405,53 @@ static int CmdNumCon(const char *Cmd) { for (uint8_t i = 0; i < ARRAYLEN(radix); i++) { MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, radix[i].radix, s, sizeof(s), &slen)); - if (slen > 0) { - PrintAndLogEx(INFO, "%s%s", radix[i].desc, s); + if (slen) { + PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, s); } } + // reverse + if (shall_reverse) { + PrintAndLogEx(SUCCESS, _CYAN_("Reversed")); + for (uint8_t i = 0; i < ARRAYLEN(radix); i++) { + MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, radix[i].radix, s, sizeof(s), &slen)); + + str_reverse(s, strlen(s)); + + if (slen) { + PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, s); + } + } + } + + // invert + if (shall_invert) { + PrintAndLogEx(SUCCESS, _CYAN_("Inverted")); + for (uint8_t i = 0; i < ARRAYLEN(radix); i++) { + MBEDTLS_MPI_CHK(mbedtls_mpi_write_string(&N, radix[i].radix, s, sizeof(s), &slen)); + if (slen == 0) { + continue; + } + + switch (i) { + case 0: +// MBEDTLS_MPI_CHK(mbedtls_mpi_inv_mod(&N, &N, &base)); + break; + case 1: + str_inverse_hex(s, strlen(s)); + PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, s); + break; + case 2: + str_inverse_bin(s, strlen(s)); + PrintAndLogEx(SUCCESS, "%s%s", radix[i].desc, s); + break; + default: + break; + } + } + } + + // check if number is a prime mbedtls_entropy_context entropy; mbedtls_ctr_drbg_context ctr_drbg; @@ -3300,6 +3473,218 @@ cleanup: return PM3_SUCCESS; } +int centerThreshold(const int *in, int *out, size_t len, int8_t up, int8_t down) { + if (len < 5) { + return PM3_EINVARG; + } + + for (size_t i = 0; i < len; ++i) { + if ((in[i] <= up) && (in[i] >= down)) { + out[i] = 0; + } + } + + // clean out spikes. + for (size_t i = 2; i < len - 2; ++i) { + + int a = out[i - 2] + out[i - 1]; + int b = out[i + 2] + out[i + 1]; + if (a == 0 && b == 0) { + out[i] = 0; + } + } + return PM3_SUCCESS; +} + +static int CmdCenterThreshold(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "data cthreshold", + "Inverse of dirty threshold command, all values between up and down will be average out", + "data cthreshold -u 10 -d -10" + ); + void *argtable[] = { + arg_param_begin, + arg_int1("d", "down", "", "threshold down"), + arg_int1("u", "up", "", "threshold up"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int8_t down = arg_get_int(ctx, 1); + int8_t up = arg_get_int(ctx, 2); + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "Applying up threshold: " _YELLOW_("%i") ", down threshold: " _YELLOW_("%i") "\n", up, down); + + centerThreshold(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen, up, down); + + // set signal properties low/high/mean/amplitude and isnoice detection + uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + size_t size = getFromGraphBuf(bits); + // set signal properties low/high/mean/amplitude and is_noice detection + computeSignalProperties(bits, size); + RepaintGraphWindow(); + free(bits); + return PM3_SUCCESS; +} + +static int envelope_square(const int *in, int *out, size_t len) { + if (len < 10) { + return PM3_EINVARG; + } + + + size_t i = 0; + while (i < len - 8) { + + if (in[i] == 0 && in[i + 1] == 0 && in[i + 2] == 0 && in[i + 3] == 0 && + in[i + 4] == 0 && in[i + 5] == 0 && in[i + 6] == 0 && in[i + 7] == 0) { + + i += 8; + continue; + } + + out[i] = 255; + i++; + } + return PM3_SUCCESS; +} + +static int CmdEnvelope(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "data envelop", + "Create an square envelop of the samples", + "data envelop" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + envelope_square(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen); + + uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + size_t size = getFromGraphBuf(bits); + // set signal properties low/high/mean/amplitude and is_noice detection + computeSignalProperties(bits, size); + RepaintGraphWindow(); + free(bits); + return PM3_SUCCESS; +} + +static int CmdAtrLookup(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "data atr", + "look up ATR record from bytearray\n" + "", + "data atr -d 3B6B00000031C064BE1B0100079000\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("d", NULL, "", "ASN1 encoded byte array"), +// arg_lit0("t", "test", "perform selftest"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int dlen = 128; + uint8_t data[128 + 1]; + CLIGetStrWithReturn(ctx, 1, data, &dlen); + +// bool selftest = arg_get_lit(ctx, 2); + CLIParserFree(ctx); +// if (selftest) { +// return atr_selftest(); +// } + PrintAndLogEx(INFO, "ISO7816-3 ATR... " _YELLOW_("%s"), data); + PrintAndLogEx(INFO, "Fingerprint..."); + + char *copy = str_dup(getAtrInfo((char *)data)); + + char *token = strtok(copy, "\n"); + while (token != NULL) { + PrintAndLogEx(INFO, " %s", token); + token = strtok(NULL, "\n"); + } + free(copy); + return PM3_SUCCESS; +} + +static int CmdBinaryMap(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "data bmap", + "Breaks down a hex value to binary according a template\n" + " data bmap -d 16 -m 4,4\n" + "This will give two rows each with four bits", + "data bmap -d 3B\n" + "data bmap -d 3B -m 2,5,1\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("d", NULL, "", "hex string"), + arg_str0("m", NULL, "", "binary template"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int hlen = 5; + uint8_t hex[5 + 1]; + CLIGetStrWithReturn(ctx, 1, hex, &hlen); + + int tlen = 40; + uint8_t template[40 + 1]; + CLIGetStrWithReturn(ctx, 2, template, &tlen); + CLIParserFree(ctx); + + char bits[(8 * 4) + 1] = {0}; + hextobinstring_n(bits, (char *)hex, hlen); + + if (tlen == 0) { + template[0] = '8'; + template[1] = 0; + } + + char *token = strtok((char *)template, ","); + + // header + PrintAndLogEx(INFO, "---+---------------------------"); + PrintAndLogEx(INFO, " | b0 b1 b2 b3 b4 b5 b6 b7"); + PrintAndLogEx(INFO, "---+---------------------------"); + + uint8_t i = 0; + uint8_t cnt = 1; + int x = 0; + while (token != NULL) { + sscanf(token, "%d", &x); + + PrintAndLogEx(INFO, " %d | %*.s" NOLF, cnt, i * 3, " "); + + // incease with previous offset + x += i; + + for (; i < (uint8_t)x; i++) { + PrintAndLogEx(NORMAL, "%c " NOLF, bits[7 - i]); + } + + PrintAndLogEx(NORMAL, ""); + token = strtok(NULL, ","); + cnt++; + } + + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, @@ -3312,10 +3697,11 @@ static command_t CommandTable[] = { {"rawdemod", CmdRawDemod, AlwaysAvailable, "Demodulate the data in the GraphBuffer and output binary"}, {"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("Graph") "-------------------------"}, - {"askedgedetect", CmdAskEdgeDetect, AlwaysAvailable, "Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave"}, + {"askedgedetect", CmdAskEdgeDetect, AlwaysAvailable, "Adjust Graph for manual ASK demod"}, {"autocorr", CmdAutoCorr, AlwaysAvailable, "Autocorrelation over window"}, - {"dirthreshold", CmdDirectionalThreshold, AlwaysAvailable, "Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev."}, + {"dirthreshold", CmdDirectionalThreshold, AlwaysAvailable, "Max rising higher up-thres/ Min falling lower down-thres"}, {"decimate", CmdDecimate, AlwaysAvailable, "Decimate samples"}, + {"envelope", CmdEnvelope, AlwaysAvailable, "Generate square envelope of samples"}, {"undecimate", CmdUndecimate, AlwaysAvailable, "Un-decimate samples"}, {"hide", CmdHide, AlwaysAvailable, "Hide graph window"}, {"hpf", CmdHpf, AlwaysAvailable, "Remove DC offset from trace"}, @@ -3325,20 +3711,25 @@ static command_t CommandTable[] = { {"mtrim", CmdMtrim, AlwaysAvailable, "Trim out samples from the specified start to the specified stop"}, {"norm", CmdNorm, AlwaysAvailable, "Normalize max/min to +/-128"}, {"plot", CmdPlot, AlwaysAvailable, "Show graph window"}, + + {"cthreshold", CmdCenterThreshold, AlwaysAvailable, "Average out all values between"}, + {"rtrim", CmdRtrim, AlwaysAvailable, "Trim samples from right of trace"}, {"setgraphmarkers", CmdSetGraphMarkers, AlwaysAvailable, "Set blue and orange marker in graph window"}, {"shiftgraphzero", CmdGraphShiftZero, AlwaysAvailable, "Shift 0 for Graphed wave + or - shift value"}, - {"timescale", CmdTimeScale, AlwaysAvailable, "Set a timescale to get a differential reading between the yellow and purple markers as time duration"}, + {"timescale", CmdTimeScale, AlwaysAvailable, "Set cursor display timescale"}, {"zerocrossings", CmdZerocrossings, AlwaysAvailable, "Count time between zero-crossings"}, {"convertbitstream", CmdConvertBitStream, AlwaysAvailable, "Convert GraphBuffer's 0/1 values to 127 / -127"}, {"getbitstream", CmdGetBitStream, AlwaysAvailable, "Convert GraphBuffer's >=1 values to 1 and <1 to 0"}, {"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("General") "-------------------------"}, - {"asn1", CmdAsn1Decoder, AlwaysAvailable, "asn1 decoder"}, + {"asn1", CmdAsn1Decoder, AlwaysAvailable, "ASN1 decoder"}, + {"atr", CmdAtrLookup, AlwaysAvailable, "ATR lookup"}, {"bin2hex", Cmdbin2hex, AlwaysAvailable, "Converts binary to hexadecimal"}, {"bitsamples", CmdBitsamples, IfPm3Present, "Get raw samples as bitstring"}, + {"bmap", CmdBinaryMap, AlwaysAvailable, "Convert hex value according a binary template"}, {"clear", CmdBuffClear, AlwaysAvailable, "Clears bigbuf on deviceside and graph window"}, - {"diff", CmdDiff, AlwaysAvailable, "diff of input files"}, + {"diff", CmdDiff, AlwaysAvailable, "Diff of input files"}, {"hexsamples", CmdHexsamples, IfPm3Present, "Dump big buffer as hex bytes"}, {"hex2bin", Cmdhex2bin, AlwaysAvailable, "Converts hexadecimal to binary"}, {"load", CmdLoad, AlwaysAvailable, "Load contents of file into graph window"}, @@ -3361,4 +3752,3 @@ int CmdData(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } - diff --git a/client/src/cmddata.h b/client/src/cmddata.h index aaeae864a..0767412cb 100644 --- a/client/src/cmddata.h +++ b/client/src/cmddata.h @@ -86,9 +86,11 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG int getSamples(uint32_t n, bool verbose); int getSamplesEx(uint32_t start, uint32_t end, bool verbose, bool ignore_lf_config); +int getSamplesFromBufEx(uint8_t *data, size_t sample_num, uint8_t bits_per_sample, bool verbose); void setClockGrid(uint32_t clk, int offset); int directionalThreshold(const int *in, int *out, size_t len, int8_t up, int8_t down); +int centerThreshold(const int *in, int *out, size_t len, int8_t up, int8_t down); int AskEdgeDetect(const int *in, int *out, int len, int threshold); #define MAX_DEMOD_BUF_LEN (1024*128) diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index e8db64885..9d1f5605f 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -275,7 +275,8 @@ static int CmdFlashMemLoad(const char *Cmd) { } break; } -// not needed when we transite to loadxxxx_safe methods.(iceman) + + // ICEMAN: not needed when we transite to loadxxxx_safe methods uint8_t *newdata = realloc(data, datalen); if (newdata == NULL) { free(data); @@ -378,8 +379,7 @@ static int CmdFlashMemDump(const char *Cmd) { } if (filename[0] != '\0') { - saveFile(filename, ".bin", dump, len); - saveFileEML(filename, dump, len, 16); + pm3_save_dump(filename, dump, len, jsfRaw); } free(dump); diff --git a/client/src/cmdflashmemspiffs.c b/client/src/cmdflashmemspiffs.c index a1923d588..2f5c94324 100644 --- a/client/src/cmdflashmemspiffs.c +++ b/client/src/cmdflashmemspiffs.c @@ -16,6 +16,7 @@ // Proxmark3 RDV40 Flash memory commands //----------------------------------------------------------------------------- #include "cmdflashmemspiffs.h" +#include "cmdtrace.h" #include #include "cmdparser.h" // command_t #include "pmflash.h" @@ -369,19 +370,19 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { CLIParserInit(&ctx, "mem spiffs dump", "Dumps device SPIFFS file to a local file\n" "Size is handled by first sending a STAT command against file to verify existence", - "mem spiffs dump -s tag.bin --> download binary file from device\n" - "mem spiffs dump -s tag.bin -d aaa -e --> download tag.bin, save as aaa.eml format" + "mem spiffs dump -s tag.bin --> download binary file from device, saved as `tag.bin`\n" + "mem spiffs dump -s tag.bin -d a001 --> download tag.bin, save as `a001.bin`\n" + "mem spiffs dump -s tag.bin -t --> download tag.bin into trace buffer" ); void *argtable[] = { arg_param_begin, arg_str1("s", "src", "", "SPIFFS file to save"), arg_str0("d", "dest", "", "file name to save to "), - arg_lit0("e", "eml", "also save in EML format"), + arg_lit0("t", "trace", "download into trace buffer"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int slen = 0; char src[32] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)src, 32, &slen); @@ -390,7 +391,7 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { char dest[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)dest, FILE_PATH_SIZE, &dlen); - bool eml = arg_get_lit(ctx, 3); + bool to_trace = arg_get_lit(ctx, 3); CLIParserFree(ctx); // get size from spiffs itself ! @@ -404,7 +405,7 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { uint32_t len = resp.data.asDwords[0]; uint8_t *dump = calloc(len, sizeof(uint8_t)); - if (!dump) { + if (dump == NULL) { PrintAndLogEx(ERR, "error, cannot allocate memory "); return PM3_EMALLOC; } @@ -412,35 +413,37 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { // download from device uint32_t start_index = 0; PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") " bytes from `" _YELLOW_("%s") "` (spiffs)", len, src); - if (!GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)src, slen, NULL, -1, true)) { + if (GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)src, slen, NULL, -1, true) == false) { PrintAndLogEx(FAILED, "error, downloading from spiffs"); free(dump); return PM3_EFLASH; } - // save to file - char fn[FILE_PATH_SIZE] = {0}; - if (dlen == 0) { - strncpy(fn, src, slen); + if (to_trace) { + // copy to client trace buffer + if (ImportTraceBuffer(dump, len) == false) { + PrintAndLogEx(FAILED, "error, copying to trace buffer"); + free(dump); + return PM3_EMALLOC; + } + PrintAndLogEx(HINT, "Use 'trace list -1 -t ...' to view, 'trace save -f ...' to save"); } else { - strncpy(fn, dest, dlen); - } - // set file extension - char *suffix = strchr(fn, '.'); - if (suffix) - saveFile(fn, suffix, dump, len); - else - saveFile(fn, ".bin", dump, len); // default + // save to file + char fn[FILE_PATH_SIZE] = {0}; + if (dlen) { + strncpy(fn, dest, dlen); + } else { + strncpy(fn, src, slen); + } - if (eml) { - uint8_t eml_len = 16; - if (strstr(fn, "class") != NULL) - eml_len = 8; - else if (strstr(fn, "mfu") != NULL) - eml_len = 4; - - saveFileEML(fn, dump, len, eml_len); + // set file extension + char *suffix = strchr(fn, '.'); + if (suffix) { + saveFile(fn, suffix, dump, len); + } else { + saveFile(fn, ".bin", dump, len); + } } free(dump); return PM3_SUCCESS; @@ -524,14 +527,14 @@ static int CmdFlashMemSpiFFSView(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "mem spiffs view", - "View a file on flash memory on devicer in console", + "View a file on flash memory on device in console", "mem spiffs view -f tag.bin" ); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "SPIFFS file to view"), - arg_int0("c", "cols", "", "column breaks (def 32)"), + arg_int0("c", "cols", "", "column breaks (def 16)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -540,7 +543,7 @@ static int CmdFlashMemSpiFFSView(const char *Cmd) { char fn[32] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)fn, 32, &fnlen); - int breaks = arg_get_int_def(ctx, 2, 32); + int breaks = arg_get_int_def(ctx, 2, 16); CLIParserFree(ctx); uint8_t *dump = NULL; diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 97f7df3c7..2de94680e 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -38,6 +38,7 @@ #include "cmdhftopaz.h" // TOPAZ #include "cmdhffelica.h" // ISO18092 / FeliCa #include "cmdhffido.h" // FIDO authenticators +#include "cmdhffudan.h" // Fudan cards #include "cmdhfgallagher.h" // Gallagher DESFire cards #include "cmdhfksx6924.h" // KS X 6924 #include "cmdhfcipurse.h" // CIPURSE transport cards @@ -46,11 +47,11 @@ #include "cmdhfcryptorf.h" // CryptoRF #include "cmdhfseos.h" // SEOS #include "cmdhfst25ta.h" // ST25TA -#include "cmdhfwaveshare.h" // Waveshare -#include "cmdhftexkom.h" // Texkom -#include "cmdhfxerox.h" // Xerox -#include "cmdhffudan.h" // Fudan cards #include "cmdhftesla.h" // Tesla +#include "cmdhftexkom.h" // Texkom +#include "cmdhfvas.h" // Value added services +#include "cmdhfwaveshare.h" // Waveshare +#include "cmdhfxerox.h" // Xerox #include "cmdtrace.h" // trace list #include "ui.h" #include "proxgui.h" @@ -81,11 +82,14 @@ int CmdHFSearch(const char *Cmd) { int res = PM3_ESOFT; + uint8_t success[20] = {0}; + PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for ThinFilm tag..."); if (IfPm3NfcBarcode()) { if (infoThinFilm(false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Thinfilm tag") " found\n"); + success[THINFILM] = true; res = PM3_SUCCESS; } } @@ -95,6 +99,7 @@ int CmdHFSearch(const char *Cmd) { if (IfPm3Iso14443a()) { if (reader_lto(false, false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("LTO-CM tag") " found\n"); + success[LTO] = true; res = PM3_SUCCESS; } } @@ -105,6 +110,7 @@ int CmdHFSearch(const char *Cmd) { int sel_state = infoHF14A(false, false, false); if (sel_state > 0) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 14443-A tag") " found\n"); + success[ISO_14443A] = true; res = PM3_SUCCESS; if (sel_state == 1) @@ -112,29 +118,12 @@ int CmdHFSearch(const char *Cmd) { } } - PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for ISO15693 tag..."); - if (IfPm3Iso15693()) { - if (readHF15Uid(false, false)) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 15693 tag") " found\n"); - res = PM3_SUCCESS; - } - } - - PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for iCLASS / PicoPass tag..."); - if (IfPm3Iclass()) { - if (read_iclass_csn(false, false, false) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("iCLASS tag / PicoPass tag") " found\n"); - res = PM3_SUCCESS; - } - } - PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for LEGIC tag..."); if (IfPm3Legicrf()) { if (readLegicUid(false, false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("LEGIC Prime tag") " found\n"); + success[LEGIC] = true; res = PM3_SUCCESS; } } @@ -144,6 +133,27 @@ int CmdHFSearch(const char *Cmd) { if (IfPm3Iso14443a()) { if (readTopazUid(false, false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Topaz tag") " found\n"); + success[TOPAZ] = true; + res = PM3_SUCCESS; + } + } + + // texkom + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, " Searching for TEXKOM tag..."); + if (read_texkom_uid(false, false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("TEXKOM tag") " found\n"); + success[PROTO_TEXKOM] = true; + res = PM3_SUCCESS; + } + + // xerox + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, " Searching for Fuji/Xerox tag..."); + if (IfPm3Iso14443b()) { + if (read_xerox_uid(false, false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Fuji/Xerox tag") " found\n"); + success[PROTO_XEROX] = true; res = PM3_SUCCESS; } } @@ -154,33 +164,41 @@ int CmdHFSearch(const char *Cmd) { if (IfPm3Iso14443b()) { if (readHF14B(false, false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 14443-B tag") " found\n"); + success[ISO_14443B] = true; res = PM3_SUCCESS; } } + // OBS! This triggers a swap to FPGA_BITSTREAM_HF_15 == 1.5sec delay + + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, " Searching for ISO15693 tag..."); + if (IfPm3Iso15693()) { + if (readHF15Uid(false, false)) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 15693 tag") " found\n"); + success[ISO_15693] = true; + res = PM3_SUCCESS; + } + } + + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, " Searching for iCLASS / PicoPass tag..."); + if (IfPm3Iclass()) { + if (read_iclass_csn(false, false, false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("iCLASS tag / PicoPass tag") " found\n"); + success[ICLASS] = true; + res = PM3_SUCCESS; + } + } + + // OBS! This triggers a swap to FPGA_BITSTREAM_HF_FELICA == 1.5sec delay + PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for FeliCa tag..."); if (IfPm3Felica()) { if (read_felica_uid(false, false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 18092 / FeliCa tag") " found\n"); - res = PM3_SUCCESS; - } - } - - // texkom - PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for TEXKOM tag..."); - if (read_texkom_uid(false, false) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("TEXKOM tag") " found\n"); - res = PM3_SUCCESS; - } - - // xerox - PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for Fuji/Xerox tag..."); - if (IfPm3Iso14443b()) { - if (read_xerox_uid(false, false) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Fuji/Xerox tag") " found\n"); + success[FELICA] = true; res = PM3_SUCCESS; } } @@ -191,6 +209,7 @@ int CmdHFSearch(const char *Cmd) { if (IfPm3Iso14443b()) { if (readHFCryptoRF(false, false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("CryptoRF tag") " found\n"); + success[CRYPTORF] = true; res = PM3_SUCCESS; } } @@ -200,6 +219,53 @@ int CmdHFSearch(const char *Cmd) { if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, _RED_("No known/supported 13.56 MHz tags found")); res = PM3_ESOFT; + } else { + + // no need to print 14A hints, since it will print itself + + if (success[THINFILM]) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf thinfilm`") " commands\n"); + } + + if (success[LTO]) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf lto`") " commands\n"); + } + + if (success[LEGIC]) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf legic`") " commands\n"); + } + + if (success[TOPAZ]) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf topaz`") " commands\n"); + } + + if (success[PROTO_TEXKOM]) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf texkom`") " commands\n"); + } + + if (success[PROTO_XEROX]) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf xerox`") " commands\n"); + } + + if (success[ISO_14443B]) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf 14b`") " commands\n"); + } + + if (success[ISO_15693]) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf 15`") " commands\n"); + } + + if (success[ICLASS]) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf iclass`") " commands\n"); + } + + if (success[FELICA]) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf felica`") " commands\n"); + } + + if (success[PROTO_CRYPTORF]) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf cryptorf`") " commands\n"); + } } DropField(); @@ -211,7 +277,7 @@ int CmdHFTune(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf tune", "Continuously measure HF antenna tuning.\n" - "Press button or to interrupt.", + "Press pm3 button or to interrupt.", "hf tune\n" "hf tune --mix" ); @@ -244,7 +310,8 @@ int CmdHFTune(const char *Cmd) { if (is_value) style = STYLE_VALUE; - PrintAndLogEx(INFO, "Measuring HF antenna, click " _GREEN_("pm3 button") " or press " _GREEN_("Enter") " to exit"); + PrintAndLogEx(INFO, "Measuring HF antenna"); + PrintAndLogEx(INFO, "click " _GREEN_("pm3 button") " or press " _GREEN_("") " to exit"); PacketResponseNG resp; clearCommandBuffer(); @@ -425,8 +492,8 @@ int handle_hf_plot(void) { uint8_t buf[FPGA_TRACE_SIZE] = {0}; - PacketResponseNG response; - if (GetFromDevice(FPGA_MEM, buf, FPGA_TRACE_SIZE, 0, NULL, 0, &response, 4000, true) == false) { + PacketResponseNG resp; + if (GetFromDevice(FPGA_MEM, buf, FPGA_TRACE_SIZE, 0, NULL, 0, &resp, 4000, true) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -498,8 +565,11 @@ static command_t CommandTable[] = { {"texkom", CmdHFTexkom, AlwaysAvailable, "{ Texkom RFIDs... }"}, {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, - {"xerox", CmdHFXerox, AlwaysAvailable, "{ Fuji/Xerox cartridge RFIDs... }"}, + {"vas", CmdHFVAS, AlwaysAvailable, "{ Apple Value Added Service }"}, +#ifdef HAVE_GD {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, +#endif + {"xerox", CmdHFXerox, AlwaysAvailable, "{ Fuji/Xerox cartridge RFIDs... }"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHFList, AlwaysAvailable, "List protocol data in trace buffer"}, diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 0a9a23578..361e4deae 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -42,6 +42,7 @@ #include "desfire.h" // desfire enums #include "mifare/desfirecore.h" // desfire context #include "mifare/mifaredefault.h" +#include "preferences.h" // get/set device debug level static bool g_apdu_in_framing_enable = true; bool Get_apdu_in_framing(void) { @@ -54,6 +55,35 @@ void Set_apdu_in_framing(bool v) { static int CmdHelp(const char *Cmd); static int waitCmd(bool i_select, uint32_t timeout, bool verbose); + +static const iso14a_polling_frame_t WUPA_FRAME = { + { 0x52 }, 1, 7, 0, +}; + +static const iso14a_polling_frame_t MAGWUPA1_FRAME = { + { 0x7A }, 1, 7, 0 +}; + +static const iso14a_polling_frame_t MAGWUPA2_FRAME = { + { 0x7B }, 1, 7, 0 +}; + +static const iso14a_polling_frame_t MAGWUPA3_FRAME = { + { 0x7C }, 1, 7, 0 +}; + +static const iso14a_polling_frame_t MAGWUPA4_FRAME = { + { 0x7D }, 1, 7, 0 +}; + +static const iso14a_polling_frame_t ECP_FRAME = { + .frame = { 0x6a, 0x02, 0xC8, 0x01, 0x00, 0x03, 0x00, 0x02, 0x79, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xD8}, + .frame_length = 15, + .last_byte_bits = 8, + .extra_delay = 0 +}; + + static const manufactureName_t manufactureMapping[] = { // ID, "Vendor Country" { 0x01, "Motorola UK" }, @@ -194,6 +224,7 @@ static const hintAIDList_t hintAIDList[] = { { "\x32\x50\x41\x59\x2E\x53\x59\x53\x2E\x44\x44\x46\x30\x31", 14, "EMV (ppse)", "emv" }, { "\x41\x44\x20\x46\x31", 5, "CIPURSE", "hf cipurse" }, { "\xd2\x76\x00\x00\x85\x01\x00", 7, "desfire", "hf mfdes" }, + { "\x4F\x53\x45\x2E\x56\x41\x53\x2E\x30\x31", 10, "Apple VAS", "hf vas"}, }; // iso14a apdu input frame length @@ -202,7 +233,7 @@ static uint8_t gs_frames_num = 0; static uint16_t atsFSC[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; static int CmdHF14AList(const char *Cmd) { - return CmdTraceListAlias(Cmd, "hf 14a", "14a"); + return CmdTraceListAlias(Cmd, "hf 14a", "14a -c"); } int hf14a_getconfig(hf14a_config *config) { @@ -254,7 +285,7 @@ static int hf_14a_config_example(void) { PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 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 14a config --atqa force --bcc ignore --cl2 force --cl3 skip --rats skip")); PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl --blk 0 -k FFFFFFFFFFFF -d 04112233445566184200626364656667")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); PrintAndLogEx(NORMAL, _CYAN_(" MFUL ")"/" _CYAN_(" MFUL EV1 ")"/" _CYAN_(" MFULC")":"); @@ -388,11 +419,10 @@ static int CmdHf14AConfig(const char *Cmd) { } int Hf14443_4aGetCardData(iso14a_card_select_t *card) { - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; WaitForResponse(CMD_ACK, &resp); - memcpy(card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision @@ -433,6 +463,41 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) { return 0; } +iso14a_polling_parameters_t iso14a_get_polling_parameters(bool use_ecp, bool use_magsafe) { + // Extra 100ms give enough time for Apple (ECP) devices to proccess field info and make a decision + + if (use_ecp && use_magsafe) { + iso14a_polling_parameters_t full_polling_parameters = { + .frames = { WUPA_FRAME, ECP_FRAME, MAGWUPA1_FRAME, MAGWUPA2_FRAME, MAGWUPA3_FRAME, MAGWUPA4_FRAME }, + .frame_count = 6, + .extra_timeout = 100 + }; + return full_polling_parameters; + } else if (use_ecp) { + iso14a_polling_parameters_t ecp_polling_parameters = { + .frames = { WUPA_FRAME, ECP_FRAME }, + .frame_count = 2, + .extra_timeout = 100 + }; + return ecp_polling_parameters; + } else if (use_magsafe) { + iso14a_polling_parameters_t magsafe_polling_parameters = { + .frames = { WUPA_FRAME, MAGWUPA1_FRAME, MAGWUPA2_FRAME, MAGWUPA3_FRAME, MAGWUPA4_FRAME }, + .frame_count = 5, + .extra_timeout = 0 + }; + return magsafe_polling_parameters; + } + + iso14a_polling_parameters_t wupa_polling_parameters = { + .frames = { WUPA_FRAME }, + .frame_count = 1, + .extra_timeout = 0, + }; + return wupa_polling_parameters; +} + + static int CmdHF14AReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a reader", @@ -472,33 +537,40 @@ static int CmdHF14AReader(const char *Cmd) { cm |= ISO14A_NO_RATS; } - if (arg_get_lit(ctx, 5)) { - cm |= ISO14A_USE_ECP; - } + bool use_ecp = arg_get_lit(ctx, 5); + bool use_magsafe = arg_get_lit(ctx, 6); - if (arg_get_lit(ctx, 6)) { - cm |= ISO14A_USE_MAGSAFE; + iso14a_polling_parameters_t *polling_parameters = NULL; + iso14a_polling_parameters_t parameters = iso14a_get_polling_parameters(use_ecp, use_magsafe); + if (use_ecp || use_magsafe) { + cm |= ISO14A_USE_CUSTOM_POLLING; + polling_parameters = ¶meters; } bool continuous = arg_get_lit(ctx, 7); - CLIParserFree(ctx); - int res = PM3_SUCCESS; - - if (!disconnectAfter) + if (disconnectAfter == false) { cm |= ISO14A_NO_DISCONNECT; - if (continuous) { - PrintAndLogEx(INFO, "Press " _GREEN_("Enter") " to exit"); } + + if (continuous) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + int res = PM3_SUCCESS; do { clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, cm, 0, 0, NULL, 0); + + if (cm & ISO14A_USE_CUSTOM_POLLING) { + SendCommandMIX(CMD_HF_ISO14443A_READER, cm, 0, 0, (uint8_t *)polling_parameters, sizeof(iso14a_polling_parameters_t)); + } else { + SendCommandMIX(CMD_HF_ISO14443A_READER, cm, 0, 0, NULL, 0); + } if (ISO14A_CONNECT & cm) { PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { - if (!silent) PrintAndLogEx(WARNING, "iso14443a card select failed"); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { DropField(); res = PM3_ESOFT; goto plot; @@ -516,7 +588,6 @@ static int CmdHF14AReader(const char *Cmd) { uint64_t select_status = resp.oldarg[0]; if (select_status == 0) { - if (!silent) PrintAndLogEx(WARNING, "iso14443a card select failed"); DropField(); res = PM3_ESOFT; goto plot; @@ -532,7 +603,7 @@ static int CmdHF14AReader(const char *Cmd) { } else { PrintAndLogEx(SUCCESS, "ATQA: %02X %02X", card.atqa[1], card.atqa[0]); } - + PrintAndLogEx(NORMAL, ""); } DropField(); res = PM3_ESOFT; @@ -550,9 +621,10 @@ static int CmdHF14AReader(const char *Cmd) { PrintAndLogEx(SUCCESS, " ATS: [%d] " _GREEN_("%s"), card.ats_len, sprint_hex(card.ats, card.ats_len)); } } + PrintAndLogEx(NORMAL, ""); } - if (!disconnectAfter) { - if (!silent) PrintAndLogEx(SUCCESS, "Card is selected. You can now start sending commands"); + if ((disconnectAfter == false) && (silent == false)) { + PrintAndLogEx(SUCCESS, "Card is selected. You can now start sending commands"); } } plot: @@ -680,11 +752,14 @@ int CmdHF14ASim(const char *Cmd) { "hf 14a sim -t 7 -> MFU EV1 / NTAG 215 Amiibo\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"); + "hf 14a sim -t 10 -> ST25TA IKEA Rothult\n" + "hf 14a sim -t 11 -> Javacard (JCOP)\n" + "hf 14a sim -t 12 -> 4K Seos card\n" + ); void *argtable[] = { arg_param_begin, - arg_int1("t", "type", "<1-10> ", "Simulation type to use"), + arg_int1("t", "type", "<1-12> ", "Simulation type to use"), arg_str0("u", "uid", "", "<4|7|10> hex bytes UID"), arg_int0("n", "num", "", "Exit simulation after blocks have been read by reader. 0 = infinite"), arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"), @@ -734,7 +809,7 @@ int CmdHF14ASim(const char *Cmd) { CLIParserFree(ctx); - if (tagtype > 10) { + if (tagtype > 12) { PrintAndLogEx(ERR, "Undefined tag %d", tagtype); return PM3_EINVARG; } @@ -762,7 +837,7 @@ int CmdHF14ASim(const char *Cmd) { sector_t *k_sector = NULL; size_t k_sectors_cnt = MIFARE_4K_MAXSECTOR; - PrintAndLogEx(INFO, "Press pm3-button to abort simulation"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort simulation"); bool keypress = kbd_enter_pressed(); while (keypress == false) { @@ -847,8 +922,10 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav uint8_t data[PM3_CMD_DATA_SIZE] = { 0x0a | gs_frames_num, 0x00}; gs_frames_num ^= 1; - memcpy(&data[2], datain, datainlen & 0xFFFF); - SendCommandOLD(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | cmdc, (datainlen & 0xFFFF) + 2, 0, data, (datainlen & 0xFFFF) + 2); + + int min = MIN((PM3_CMD_DATA_SIZE - 2), (datainlen & 0x1FF)); + memcpy(&data[2], datain, min); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | cmdc, (datainlen & 0xFFFF) + 2, 0, data, min + 2); uint8_t *recv; PacketResponseNG resp; @@ -892,8 +969,7 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav return 0; } -int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card) { - +int SelectCard14443A_4_WithParameters(bool disconnect, bool verbose, iso14a_card_select_t *card, iso14a_polling_parameters_t *polling_parameters) { // global vars should be prefixed with g_ gs_frame_len = 0; gs_frames_num = 0; @@ -906,7 +982,12 @@ int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card // Anticollision + SELECT card PacketResponseNG resp; - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + if (polling_parameters != NULL) { + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT | ISO14A_USE_CUSTOM_POLLING, 0, 0, (uint8_t *)polling_parameters, sizeof(iso14a_polling_parameters_t)); + } else { + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + } + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; @@ -915,7 +996,7 @@ int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card // check result if (resp.oldarg[0] == 0) { if (verbose) { - PrintAndLogEx(FAILED, "No card in field"); + PrintAndLogEx(WARNING, "No ISO1443-A Card in field"); } return PM3_ECARDEXCHANGE; } @@ -925,6 +1006,11 @@ int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card return PM3_ECARDEXCHANGE; } + iso14a_card_select_t *vcard = (iso14a_card_select_t *) resp.data.asBytes; + if (card) { + memcpy(card, vcard, sizeof(iso14a_card_select_t)); + } + if (resp.oldarg[0] == 2) { // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision // get ATS uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 @@ -948,19 +1034,19 @@ int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card gs_frame_len = atsFSC[fsci]; } } + + if (card) { + card->ats_len = resp.oldarg[0]; + memcpy(card->ats, resp.data.asBytes, card->ats_len); + } } else { // get frame length from ATS in card data structure - iso14a_card_select_t *vcard = (iso14a_card_select_t *) resp.data.asBytes; if (vcard->ats_len > 1) { uint8_t fsci = vcard->ats[1] & 0x0f; if (fsci < ARRAYLEN(atsFSC)) { gs_frame_len = atsFSC[fsci]; } } - - if (card) { - memcpy(card, vcard, sizeof(iso14a_card_select_t)); - } } SetISODEPState(ISODEP_NFCA); @@ -972,14 +1058,51 @@ int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card return PM3_SUCCESS; } +int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card) { + return SelectCard14443A_4_WithParameters(disconnect, verbose, card, NULL); +} + static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool activateField, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool *chainingout) { *chainingout = false; + size_t timeout = 1500; if (activateField) { // select with no disconnect and set gs_frame_len - int selres = SelectCard14443A_4(false, true, NULL); - if (selres != PM3_SUCCESS) + iso14a_card_select_t card; + int selres = SelectCard14443A_4(false, true, &card); + if (selres != PM3_SUCCESS) { return selres; + } + + // Extract FWI and SFGI from ATS and increase timeout by the indicated values + // for most cards these values are trivially small so will make no practical + // difference but some "cards" like hf_cardhopper overwrite these to their + // maximum values resulting in ~5 seconds each which can cause timeouts if we + // just ignore it + if (((card.ats[1] & 0x20) == 0x20) && card.ats_len > 2) { + // TB is present in ATS + + uint8_t tb; + if ((card.ats[1] & 0x10) == 0x10 && card.ats_len > 3) { + // TA is also present, so TB at ats[3] + tb = card.ats[3]; + } else { + // TA is not present, so TB is at ats[2] + tb = card.ats[2]; + } + + uint8_t fwi = (tb & 0xF0) >> 4; + if (fwi != 0x0F) { + uint32_t fwt = 256 * 16 * (1 << fwi); + timeout += fwt; + } + + uint8_t sfgi = tb & 0x0F; + if (sfgi != 0x0F) { + uint32_t sgft = 256 * 16 * (1 << sfgi); + timeout += sgft; + } + } } uint16_t cmdc = 0; @@ -991,13 +1114,13 @@ static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool // here length PM3_CMD_DATA_SIZE=512 // timeout must be authomatically set by "get ATS" if (datain) - SendCommandOLD(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0x1FF), 0, datain, datainlen & 0x1FF); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0x1FF), 0, datain, datainlen & 0x1FF); else SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, 0, 0, NULL, 0); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, timeout)) { uint8_t *recv = resp.data.asBytes; int iLen = resp.oldarg[0]; uint8_t res = resp.oldarg[1]; @@ -1008,7 +1131,7 @@ static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool *dataoutlen += dlen; if (maxdataoutlen && *dataoutlen > maxdataoutlen) { - PrintAndLogEx(ERR, "APDU: Buffer too small(%d), needs %d bytes", *dataoutlen, maxdataoutlen); + PrintAndLogEx(DEBUG, "ERR: APDU: Buffer too small(%d), needs %d bytes", *dataoutlen, maxdataoutlen); return PM3_EAPDU_FAIL; } @@ -1020,19 +1143,19 @@ static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool } if (!iLen) { - PrintAndLogEx(ERR, "APDU: No APDU response"); + PrintAndLogEx(DEBUG, "ERR: APDU: No APDU response"); return PM3_EAPDU_FAIL; } // check apdu length if (iLen < 2 && iLen >= 0) { - PrintAndLogEx(ERR, "APDU: Small APDU response, len %d", iLen); + PrintAndLogEx(DEBUG, "ERR: APDU: Small APDU response, len %d", iLen); return PM3_EAPDU_FAIL; } // check block TODO if (iLen == -2) { - PrintAndLogEx(ERR, "APDU: Block type mismatch"); + PrintAndLogEx(DEBUG, "ERR: APDU: Block type mismatch"); return PM3_EAPDU_FAIL; } @@ -1045,11 +1168,11 @@ static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool // CRC Check if (iLen == -1) { - PrintAndLogEx(ERR, "APDU: ISO 14443A CRC error"); + PrintAndLogEx(DEBUG, "ERR: APDU: ISO 14443A CRC error"); return PM3_EAPDU_FAIL; } } else { - PrintAndLogEx(ERR, "APDU: Reply timeout"); + PrintAndLogEx(DEBUG, "ERR: APDU: Reply timeout"); return PM3_EAPDU_FAIL; } @@ -1083,7 +1206,7 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea } // check R-block ACK -//TODO check this one... + // TODO check this one... if ((*dataoutlen == 0) && (chaining != chainBlockNotLast)) { if (leaveSignalON == false) DropField(); @@ -1294,7 +1417,7 @@ static int CmdHF14ACmdRaw(const char *Cmd) { bool use_magsafe = arg_get_lit(ctx, 12); int datalen = 0; - uint8_t data[PM3_CMD_DATA_SIZE]; + uint8_t data[PM3_CMD_DATA_SIZE_MIX] = {0}; CLIGetHexWithReturn(ctx, 13, data, &datalen); CLIParserFree(ctx); @@ -1353,19 +1476,18 @@ static int CmdHF14ACmdRaw(const char *Cmd) { flags |= ISO14A_NO_RATS; } - if (use_ecp) { - flags |= ISO14A_USE_ECP; + // TODO: allow to use reader command with both data and polling configuration + if (use_ecp | use_magsafe) { + PrintAndLogEx(WARNING, "ECP and Magsafe not supported with this command at this moment. Instead use 'hf 14a reader -sk --ecp/--mag'"); + // flags |= ISO14A_USE_MAGSAFE; + // flags |= ISO14A_USE_ECP; } - if (use_magsafe) { - flags |= ISO14A_USE_MAGSAFE; - } - - // Max buffer is PM3_CMD_DATA_SIZE - datalen = (datalen > PM3_CMD_DATA_SIZE) ? PM3_CMD_DATA_SIZE : datalen; + // Max buffer is PM3_CMD_DATA_SIZE_MIX + datalen = (datalen > PM3_CMD_DATA_SIZE_MIX) ? PM3_CMD_DATA_SIZE_MIX : datalen; clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO14443A_READER, flags, (datalen & 0xFFFF) | ((uint32_t)(numbits << 16)), argtimeout, data, datalen & 0xFFFF); + SendCommandMIX(CMD_HF_ISO14443A_READER, flags, (datalen & 0x1FF) | ((uint32_t)(numbits << 16)), argtimeout, data, datalen); if (reply) { int res = 0; @@ -1513,6 +1635,7 @@ typedef enum { MTEMV = 128, MTFUDAN = 256, MTISO18092 = 512, + MT424 = 1024, } nxp_mifare_type_t; // Based on NXP AN10833 Rev 3.6 and NXP AN10834 Rev 4.1 @@ -1641,7 +1764,7 @@ static int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status) { } printTag("NTAG 4xx"); - type |= MTDESFIRE; + type |= (MTDESFIRE | MT424); } } else if ((sak & 0x04) == 0x04) { printTag("Any MIFARE CL1"); @@ -1778,11 +1901,17 @@ static void get_compact_tlv(uint8_t *d, uint8_t n) { } int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { + + uint8_t dbg_curr = DBG_NONE; + if (getDeviceDebugLevel(&dbg_curr) != PM3_SUCCESS) { + return PM3_EFAILED; + } + clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { - if (verbose) PrintAndLogEx(WARNING, "iso14443a card select failed"); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); DropField(); return 0; } @@ -1799,7 +1928,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { uint64_t select_status = resp.oldarg[0]; if (select_status == 0) { - if (verbose) PrintAndLogEx(WARNING, "iso14443a card select failed"); + PrintAndLogEx(DEBUG, "iso14443a card select failed"); DropField(); return select_status; } @@ -1838,6 +1967,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { bool isEMV = false; bool isFUDAN = false; bool isISO18092 = false; + bool isNTAG424 = false; int nxptype = MTNONE; if (card.uidlen <= 4) { @@ -1847,6 +1977,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { isMifareDESFire = ((nxptype & MTDESFIRE) == MTDESFIRE); isMifarePlus = ((nxptype & MTPLUS) == MTPLUS); isMifareUltralight = ((nxptype & MTULTRALIGHT) == MTULTRALIGHT); + isNTAG424 = ((nxptype & MT424) == MT424); if ((nxptype & MTOTHER) == MTOTHER) isMifareClassic = true; @@ -1876,6 +2007,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { isMifareDESFire = ((nxptype & MTDESFIRE) == MTDESFIRE); isMifarePlus = ((nxptype & MTPLUS) == MTPLUS); isMifareUltralight = ((nxptype & MTULTRALIGHT) == MTULTRALIGHT); + isNTAG424 = ((nxptype & MT424) == MT424); if ((nxptype & MTOTHER) == MTOTHER) isMifareClassic = true; @@ -1928,8 +2060,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { // ******** is card of the MFU type (UL/ULC/NTAG/ etc etc) DropField(); - uint32_t tagT = GetHF14AMfU_Type(); - if (tagT != UL_ERROR) { + uint64_t tagT = GetHF14AMfU_Type(); + if (tagT != MFU_TT_UL_ERROR) { ul_print_type(tagT, 0); isMifareUltralight = true; printTag("MIFARE Ultralight/C/NTAG Compatible"); @@ -2310,31 +2442,35 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { select_status = 2; } + if (setDeviceDebugLevel(verbose ? DBG_INFO : DBG_NONE, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + int isMagic = 0; if (isMifareClassic) { - isMagic = detect_mf_magic(true); + isMagic = detect_mf_magic(true, MF_KEY_B, 0xFFFFFFFFFFFF); } if (isMifareUltralight) { - isMagic = (detect_mf_magic(false) == MAGIC_NTAG21X); + isMagic = (detect_mf_magic(false, MF_KEY_A, 0) == MAGIC_NTAG21X); } if (isMifareClassic) { int res = detect_classic_static_nonce(); if (res == NONCE_STATIC) - PrintAndLogEx(SUCCESS, "Static nonce: " _YELLOW_("yes")); + PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); if (res == NONCE_FAIL && verbose) - PrintAndLogEx(SUCCESS, "Static nonce: " _RED_("read failed")); + PrintAndLogEx(SUCCESS, "Static nonce......... " _RED_("read failed")); if (res == NONCE_NORMAL) { // not static res = detect_classic_prng(); if (res == 1) - PrintAndLogEx(SUCCESS, "Prng detection: " _GREEN_("weak")); + PrintAndLogEx(SUCCESS, "Prng detection....... " _GREEN_("weak")); else if (res == 0) - PrintAndLogEx(SUCCESS, "Prng detection: " _YELLOW_("hard")); + PrintAndLogEx(SUCCESS, "Prng detection....... " _YELLOW_("hard")); else - PrintAndLogEx(FAILED, "Prng detection: " _RED_("fail")); + PrintAndLogEx(FAILED, "Prng detection........ " _RED_("fail")); if (do_nack_test) detect_classic_nackbug(false); @@ -2349,23 +2485,27 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mf`") " commands"); } + if (setDeviceDebugLevel(dbg_curr, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + if (isMifareUltralight) - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu info`")); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfu info") "`"); if (isMifarePlus && isMagic == 0 && isEMV == false) - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfp info`")); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfp info") "`"); if (isMifareDESFire && isMagic == 0 && isEMV == false) - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfdes info`")); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfdes info") "`"); if (isST) - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf st info`")); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf st info") "`"); if (isEMV) - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`emv search -s`")); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("emv reader") "`"); if (isFUDAN) { - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf fudan dump`")); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf fudan dump") "`"); /* PrintAndLogEx(HINT, " hf 14a raw -a -b 7 -k 26"); PrintAndLogEx(HINT, " hf 14a raw -k -c 3000"); @@ -2379,6 +2519,11 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { */ } + if (isNTAG424) { + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf ntag424 info") "`"); + } + + PrintAndLogEx(NORMAL, ""); DropField(); return select_status; @@ -2416,17 +2561,16 @@ int infoHF14A4Applications(bool verbose) { } if (found) { - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "---------------------------------------------------"); - else - PrintAndLogEx(INFO, "Short AID search:"); + } if (found >= ARRAYLEN(hintAIDList) - 1) { PrintAndLogEx(HINT, "Hint: card answers to all AID. It maybe the latest revision of plus/desfire/ultralight card."); } else { for (int i = 0; i < ARRAYLEN(hintAIDList); i++) { if (cardFound[i] && strlen(hintAIDList[i].hint)) - PrintAndLogEx(HINT, "Hint: try " _YELLOW_("%s") " commands", hintAIDList[i].hint); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("%s") "` commands", hintAIDList[i].hint); } } } @@ -2667,6 +2811,7 @@ int CmdHF14ANdefRead(const char *Cmd) { CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool verbose = arg_get_lit(ctx, 2); + bool verbose2 = arg_get_lit(ctx, 2) > 1; CLIParserFree(ctx); bool activate_field = true; @@ -2853,11 +2998,24 @@ int CmdHF14ANdefRead(const char *Cmd) { memcpy(ndef_file + (i - offset), response, segment_size); } - if (fnlen != 0) { - saveFile(filename, ".bin", ndef_file, ndef_size); + if (verbose2) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("NDEF raw") " ----------------"); + print_buffer(ndef_file, ndef_size, 1); } NDEFRecordsDecodeAndPrint(ndef_file, ndef_size, verbose); + + pm3_save_dump(filename, ndef_file, ndef_size, jsfNDEF); + + if (verbose == false) { + PrintAndLogEx(HINT, "Try " _YELLOW_("`hf 14a ndefread -v`") " for more details"); + } else { + if (verbose2 == false) { + PrintAndLogEx(HINT, "Try " _YELLOW_("`hf 14a ndefread -vv`") " for more details"); + } + } + free(ndef_file); return PM3_SUCCESS; } diff --git a/client/src/cmdhf14a.h b/client/src/cmdhf14a.h index a811db66b..d7304501f 100644 --- a/client/src/cmdhf14a.h +++ b/client/src/cmdhf14a.h @@ -52,7 +52,9 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card); int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode); +iso14a_polling_parameters_t iso14a_get_polling_parameters(bool use_ecp, bool use_magsafe); int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card); +int SelectCard14443A_4_WithParameters(bool disconnect, bool verbose, iso14a_card_select_t *card, iso14a_polling_parameters_t *polling_parameters); bool Get_apdu_in_framing(void); void Set_apdu_in_framing(bool v); diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index d0083fbb5..7c416fb86 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -45,6 +45,18 @@ // for static arrays #define ST25TB_SR_BLOCK_SIZE 4 + +// SR memory sizes +#define SR_SIZE_512 1 +#define SR_SIZE_4K 2 +// ST235 memory sizes +#define ST25_SIZE_512 3 +#define ST25_SIZE_2K 4 +#define ST25_SIZE_4K 5 + + + + // iso14b apdu input frame length static uint16_t apdu_frame_length = 0; //static uint16_t ats_fsc[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; @@ -86,8 +98,10 @@ static void hf14b_aid_search(bool verbose) { json_t *data = AIDSearchGetElm(root, elmindx); uint8_t vaid[200] = {0}; int vaidlen = 0; - if (!AIDGetFromElm(data, vaid, sizeof(vaid), &vaidlen) || !vaidlen) + + if ((AIDGetFromElm(data, vaid, sizeof(vaid), &vaidlen) == false) || (vaidlen == 0 )) { continue; + } // COMPUTE APDU @@ -96,7 +110,7 @@ static void hf14b_aid_search(bool verbose) { sAPDU_t apdu = (sAPDU_t) {0x00, 0xa4, 0x04, 0x00, vaidlen, vaid}; if (APDUEncodeS(&apdu, false, 0x00, apdu_data, &apdu_len)) { - PrintAndLogEx(ERR, "APDU encoding error."); + PrintAndLogEx(ERR, "APDU encoding error"); return; } @@ -106,8 +120,9 @@ static void hf14b_aid_search(bool verbose) { 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) + if (res) { continue; + } uint16_t sw = get_sw(result, resultlen); @@ -128,9 +143,13 @@ static void hf14b_aid_search(bool verbose) { if (sw == ISO7816_OK || sw == ISO7816_INVALID_DF || sw == ISO7816_FILE_TERMINATED) { if (sw == ISO7816_OK) { - if (verbose) PrintAndLogEx(SUCCESS, "Application ( " _GREEN_("ok") " )"); + if (verbose) { + PrintAndLogEx(SUCCESS, "Application ( " _GREEN_("ok") " )"); + } } else { - if (verbose) PrintAndLogEx(WARNING, "Application ( " _RED_("blocked") " )"); + if (verbose) { + PrintAndLogEx(WARNING, "Application ( " _RED_("blocked") " )"); + } } PrintAIDDescriptionBuf(root, vaid, vaidlen, verbose); @@ -138,7 +157,9 @@ static void hf14b_aid_search(bool verbose) { if (dfnamelen) { if (dfnamelen == vaidlen) { if (memcmp(dfname, vaid, vaidlen) == 0) { - if (verbose) PrintAndLogEx(INFO, "(DF) Name found and equal to AID"); + 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); @@ -148,16 +169,79 @@ static void hf14b_aid_search(bool verbose) { PrintAIDDescriptionBuf(root, dfname, dfnamelen, verbose); } } else { - if (verbose) PrintAndLogEx(INFO, "(DF) Name not found"); + if (verbose) { + PrintAndLogEx(INFO, "(DF) Name not found"); + } } - if (verbose) PrintAndLogEx(SUCCESS, "----------------------------------------------------"); + if (verbose) { + PrintAndLogEx(SUCCESS, "----------------------------------------------------"); + } found = true; } } switch_off_field_14b(); - if (verbose == false && found) + if (verbose == false && found) { PrintAndLogEx(INFO, "----------------------------------------------------"); + } +} + +static bool wait_14b_response(bool only_first, uint8_t *datalen, uint8_t *data) { + + /* We have scenarios. + A - only select + B - only normal respose + C - both select and response + */ + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply (first)"); + return false; + } + + if (resp.status == PM3_ETEAROFF) { + PrintAndLogEx(INFO, "Writing tear off triggered"); + return true; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "first response failed... %d", resp.status); + return false; + } + + // treat first reponse as same. + if (only_first) { + + if (datalen) { + *datalen = resp.length; + } + + if (data) { + memcpy(data, resp.data.asBytes, resp.length); + } + return true; + } + + // wait a second time. + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return false; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "second response failed... %d", resp.status); + return false; + } + + if (datalen) { + *datalen = resp.length; + } + if (data) { + memcpy(data, resp.data.asBytes, resp.length); + } + + return true; } static bool wait_cmd_14b(bool verbose, bool is_select, uint32_t timeout) { @@ -168,33 +252,35 @@ static bool wait_cmd_14b(bool verbose, bool is_select, uint32_t timeout) { return false; } - uint16_t len = (resp.oldarg[1] & 0xFFFF); - uint8_t *data = resp.data.asBytes; + if (resp.status == PM3_ETEAROFF) { + PrintAndLogEx(INFO, "Writing tear off triggered"); + return true; + } - // handle select responses if (is_select) { - - // 0: OK; -1: attrib fail; -2:crc fail - int status = (int)resp.oldarg[0]; - if (status == 0) { - - if (verbose) { - PrintAndLogEx(SUCCESS, "received " _YELLOW_("%u") " bytes", len); - PrintAndLogEx(SUCCESS, "%s", sprint_hex(data, len)); - } - return true; - } else { + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(INFO, "failed status value... %d", resp.status); return false; } } + uint16_t len = resp.length; + uint8_t *data = resp.data.asBytes; + + // handle select responses OK + if (is_select && verbose) { + PrintAndLogEx(SUCCESS, "received " _YELLOW_("%u") " bytes", len); + PrintAndLogEx(SUCCESS, "%s", sprint_hex(data, len)); + return true; + } + // handle raw bytes responses if (verbose) { if (len >= 3) { bool crc = check_crc(CRC_14443_B, data, len); PrintAndLogEx(SUCCESS, "received " _YELLOW_("%u") " bytes", len); - PrintAndLogEx(SUCCESS, "%s[%02X %02X] ( %s )", + PrintAndLogEx(SUCCESS, "%s[ " _YELLOW_("%02X %02X") " ] ( %s )", sprint_hex(data, len - 2), data[len - 2], data[len - 1], @@ -210,7 +296,7 @@ static bool wait_cmd_14b(bool verbose, bool is_select, uint32_t timeout) { } static int CmdHF14BList(const char *Cmd) { - return CmdTraceListAlias(Cmd, "hf 14b", "14b"); + return CmdTraceListAlias(Cmd, "hf 14b", "14b -c"); } static int CmdHF14BSim(const char *Cmd) { @@ -239,7 +325,7 @@ static int CmdHF14BSim(const char *Cmd) { } PrintAndLogEx(INFO, "Simulate with PUPI : " _GREEN_("%s"), sprint_hex_inrow(pupi, sizeof(pupi))); - PrintAndLogEx(INFO, "Press pm3-button to abort simulation"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort simulation"); clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443B_SIMULATE, pupi, sizeof(pupi)); return PM3_SUCCESS; @@ -306,29 +392,6 @@ static int CmdHF14BCmdRaw(const char *Cmd) { int user_timeout = arg_get_int_def(ctx, 8, -1); bool verbose = arg_get_lit(ctx, 9); - uint32_t flags = ISO14B_CONNECT; - if (add_crc) { - flags |= ISO14B_APPEND_CRC; - } - - if (select_std) { - flags |= (ISO14B_SELECT_STD | ISO14B_CLEARTRACE); - if (verbose) - PrintAndLogEx(INFO, "using ISO14443-B select"); - } else if (select_sr) { - flags |= (ISO14B_SELECT_SR | ISO14B_CLEARTRACE); - if (verbose) - PrintAndLogEx(INFO, "using ST/SRx select"); - } else if (select_cts) { - flags |= (ISO14B_SELECT_CTS | ISO14B_CLEARTRACE); - if (verbose) - PrintAndLogEx(INFO, "using ASK/C-ticket select"); - } else if (select_xrx) { - flags |= (ISO14B_SELECT_XRX | ISO14B_CLEARTRACE); - if (verbose) - PrintAndLogEx(INFO, "using Fuji/Xerox select"); - } - uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; int datalen = 0; int res = CLIParamHexToBuf(arg_get_str(ctx, 10), data, sizeof(data), &datalen); @@ -337,6 +400,33 @@ static int CmdHF14BCmdRaw(const char *Cmd) { } CLIParserFree(ctx); + // FLAGS for device side + uint32_t flags = ISO14B_CONNECT; + if (add_crc) { + flags |= ISO14B_APPEND_CRC; + } + + if (select_std) { + flags |= (ISO14B_SELECT_STD | ISO14B_CLEARTRACE); + if (verbose) { + PrintAndLogEx(INFO, "using ISO14443-B select"); + } + } else if (select_sr) { + flags |= (ISO14B_SELECT_SR | ISO14B_CLEARTRACE); + if (verbose) { + PrintAndLogEx(INFO, "using ST/SRx select"); + } + } else if (select_cts) { + flags |= (ISO14B_SELECT_CTS | ISO14B_CLEARTRACE); + if (verbose) { + PrintAndLogEx(INFO, "using ASK/C-ticket select"); + } + } else if (select_xrx) { + flags |= (ISO14B_SELECT_XRX | ISO14B_CLEARTRACE); + if (verbose) { + PrintAndLogEx(INFO, "using Fuji/Xerox select"); + } + } uint32_t time_wait = 0; if (user_timeout > 0) { @@ -354,11 +444,13 @@ static int CmdHF14BCmdRaw(const char *Cmd) { PrintAndLogEx(INFO, " new raw timeout : %u ETU ( %u ms )", time_wait, user_timeout); } - if (keep_field_on == 0) + if (keep_field_on == false) { flags |= ISO14B_DISCONNECT; + } - if (datalen > 0) + if (datalen > 0) { flags |= ISO14B_RAW; + } // Max buffer is PM3_CMD_DATA_SIZE datalen = (datalen > PM3_CMD_DATA_SIZE) ? PM3_CMD_DATA_SIZE : datalen; @@ -369,6 +461,7 @@ static int CmdHF14BCmdRaw(const char *Cmd) { PrintAndLogEx(FAILED, "failed to allocate memory"); return PM3_EMALLOC; } + packet->flags = flags; packet->timeout = time_wait; packet->rawlen = datalen; @@ -388,26 +481,30 @@ static int CmdHF14BCmdRaw(const char *Cmd) { // Select, device will send back iso14b_card_select_t, don't print it. if (select_std) { success = wait_cmd_14b(verbose, true, user_timeout); - if (verbose && success) + if (verbose && success) { PrintAndLogEx(SUCCESS, "Got response for standard select"); + } } if (select_sr) { success = wait_cmd_14b(verbose, true, user_timeout); - if (verbose && success) + if (verbose && success) { PrintAndLogEx(SUCCESS, "Got response for ST/SRx select"); + } } if (select_cts) { success = wait_cmd_14b(verbose, true, user_timeout); - if (verbose && success) + if (verbose && success) { PrintAndLogEx(SUCCESS, "Got response for ASK/C-ticket select"); + } } if (select_xrx) { success = wait_cmd_14b(verbose, true, user_timeout); - if (verbose && success) + if (verbose && success) { PrintAndLogEx(SUCCESS, "Got response for Fuji/Xerox select"); + } } // get back response from the raw bytes you sent. @@ -438,8 +535,14 @@ static bool get_14b_UID(uint8_t *d, iso14b_type_t *found_type) { SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { - if (resp.oldarg[0] == 0) { + if (resp.status == PM3_SUCCESS) { memcpy(d, resp.data.asBytes, sizeof(iso14b_card_select_t)); + + iso14b_card_select_t *card = (iso14b_card_select_t *)d; + uint8_t empty[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if (memcmp(card->uid, empty, card->uidlen) == 0) { + return false; + } *found_type = ISO14B_SR; return true; } @@ -447,11 +550,12 @@ static bool get_14b_UID(uint8_t *d, iso14b_type_t *found_type) { // test 14b standard packet.flags = (ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT); + clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { - if (resp.oldarg[0] == 0) { + if (resp.status == PM3_SUCCESS) { memcpy(d, resp.data.asBytes, sizeof(iso14b_card_select_t)); *found_type = ISO14B_STANDARD; return true; @@ -464,7 +568,7 @@ static bool get_14b_UID(uint8_t *d, iso14b_type_t *found_type) { SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { - if (resp.oldarg[0] == 0) { + if (resp.status == PM3_SUCCESS) { memcpy(d, resp.data.asBytes, sizeof(iso14b_cts_card_select_t)); *found_type = ISO14B_CT; return true; @@ -527,8 +631,8 @@ static int print_atqb_resp(uint8_t *data, uint8_t cid) { } // get SRx chip model (from UID) // from ST Microelectronics -static const char *get_st_chip_model(uint8_t data) { - switch (data) { +static const char *get_st_chip_model(uint8_t id) { + switch (id) { case 0x0: return "SRIX4K (Special)"; case 0x2: @@ -544,10 +648,27 @@ static const char *get_st_chip_model(uint8_t data) { case 0xC: return "SRT512"; default : - return "Unknown"; + return ""; } } +/* +static const char *get_st25_chip_model(uint8_t id) { + switch (id) { + case 0x1B: + return "ST25TB512-AC"; + case 0x33: + return "ST25TB512-AT"; + case 0x3F: + return "ST25TB02K"; + case 0x1F: + return "ST25TB04K"; + default: + return ""; + } +} +*/ + #define ST_LOCK_INFO_EMPTY " " static const char *get_st_lock_info(uint8_t model, const uint8_t *lockbytes, uint8_t blk) { if (blk > 15) { @@ -714,23 +835,45 @@ static uint8_t get_st_chipid(const uint8_t *uid) { return uid[5] >> 2; } +/* +static uint8_t get_st25_chipid(const uint8_t *uid) { + return uid[5]; +} +*/ + static uint8_t get_st_cardsize(const uint8_t *uid) { uint8_t chipid = get_st_chipid(uid); switch (chipid) { case 0x0: case 0x3: - case 0x7: - return 1; + case 0x7: + return SR_SIZE_4K; case 0x4: case 0x6: case 0xC: - return 2; + return SR_SIZE_512; default: return 0; } - return 0; } +/* +static uint8_t get_st25_cardsize(const uint8_t *uid) { + uint8_t chipid = get_st25_chipid(uid); + switch (chipid) { + case 0x1B: + case 0x33: + return ST25_SIZE_512; + case 0x1F: + return ST25_SIZE_4K; + case 0x3F: + return ST25_SIZE_2K; + default: + return 0; + } +} +*/ + // print UID info from SRx chips (ST Microelectronics) static void print_st_general_info(uint8_t *data, uint8_t len) { //uid = first 8 bytes in data @@ -757,12 +900,13 @@ static void print_ct_general_info(void *vcard) { } static void print_hdr(void) { - PrintAndLogEx(INFO, " block# | data |lck| ascii"); - PrintAndLogEx(INFO, "---------+--------------+---+----------"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, " block# | data |lck| ascii"); + PrintAndLogEx(INFO, "---------+-------------+---+------"); } static void print_footer(void) { - PrintAndLogEx(INFO, "---------+--------------+---+----------"); + PrintAndLogEx(INFO, "---------+-------------+---+------"); PrintAndLogEx(NORMAL, ""); } @@ -792,16 +936,17 @@ static void print_sr_blocks(uint8_t *data, size_t len, const uint8_t *uid) { size_t blocks = (len / ST25TB_SR_BLOCK_SIZE) - 1 ; uint8_t *systemblock = data + blocks * ST25TB_SR_BLOCK_SIZE ; uint8_t chipid = get_st_chipid(uid); - PrintAndLogEx(SUCCESS, _GREEN_("%s") " tag", get_st_chip_model(chipid)); - PrintAndLogEx(DEBUG, "systemblock : %s", sprint_hex(systemblock, ST25TB_SR_BLOCK_SIZE)); - PrintAndLogEx(DEBUG, " otp lock : %02x %02x", *systemblock, *(systemblock + 1)); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-------- " _CYAN_("%s tag memory") " ---------", get_st_chip_model(chipid)); + PrintAndLogEx(DEBUG, "systemblock... " _YELLOW_("%s"), sprint_hex(systemblock, ST25TB_SR_BLOCK_SIZE)); + PrintAndLogEx(DEBUG, " otp lock... " _YELLOW_("%02x %02x"), *systemblock, *(systemblock + 1)); print_hdr(); for (int i = 0; i < blocks; i++) { PrintAndLogEx(INFO, - "%3d/0x%02X | %s | %s | %s", + "%3d/0x%02X | %s| %s | %s", i, i, sprint_hex(data + (i * ST25TB_SR_BLOCK_SIZE), ST25TB_SR_BLOCK_SIZE), @@ -811,7 +956,7 @@ static void print_sr_blocks(uint8_t *data, size_t len, const uint8_t *uid) { } PrintAndLogEx(INFO, - "%3d/0x%02X | %s | %s | %s", + "%3d/0x%02X | %s| %s | %s", 0xFF, 0xFF, sprint_hex(systemblock, ST25TB_SR_BLOCK_SIZE), @@ -854,14 +999,14 @@ static bool HF14B_Std_Info(bool verbose, bool do_aid_search) { return false; } - iso14b_card_select_t card; - memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + switch (resp.status) { + case PM3_SUCCESS: { + + iso14b_card_select_t card; + memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - int status = resp.oldarg[0]; - switch (status) { - case 0: { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "-------------------- " _CYAN_("Tag information") " --------------------"); + 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); @@ -873,10 +1018,10 @@ static bool HF14B_Std_Info(bool verbose, bool do_aid_search) { return true; } - case -1: + case PM3_ELENGTH: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 STD ATTRIB fail"); break; - case -2: + case PM3_ECRC: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 STD CRC fail"); break; default: @@ -906,12 +1051,17 @@ static bool HF14B_ST_Info(bool verbose, bool do_aid_search) { return false; } + if (resp.status != PM3_SUCCESS) { + return false; + } + iso14b_card_select_t card; memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - int status = resp.oldarg[0]; - if (status < 0) + uint8_t empty[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if ((card.uidlen != 8) || (memcmp(card.uid, empty, card.uidlen) == 0)) { return false; + } print_st_general_info(card.uid, card.uidlen); @@ -942,6 +1092,57 @@ static int CmdHF14Binfo(const char *Cmd) { return infoHF14B(verbose, do_aid_search); } +static int read_sr_block(uint8_t blockno, uint8_t *out) { + struct { + uint8_t blockno; + } PACKED payload; + + payload.blockno = blockno; + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_SRI_READ, (uint8_t *)&payload, sizeof(payload)); + if (WaitForResponseTimeout(CMD_HF_SRI_READ, &resp, TIMEOUT) == false) { + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_SUCCESS && out) { + memcpy(out, resp.data.asBytes, resp.length); + } + return resp.status; +} + +static int write_sr_block(uint8_t blockno, uint8_t datalen, uint8_t *data) { + + uint8_t psize = sizeof(iso14b_raw_cmd_t) + datalen + 2; + iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, psize); + if (packet == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } + + packet->flags = (ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_RAW | ISO14B_APPEND_CRC | ISO14B_DISCONNECT); + packet->timeout = 0; + packet->rawlen = 6; + packet->raw[0] = ISO14443B_WRITE_BLK; + packet->raw[1] = blockno; + packet->raw[2] = data[0]; + packet->raw[3] = data[1]; + packet->raw[4] = data[2]; + packet->raw[5] = data[3]; + + // SRx get and print general info about SRx chip from UID + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, psize); + free(packet); + + if (wait_14b_response(true, NULL, NULL) == false) { + PrintAndLogEx(FAILED, "SRx write block ( " _RED_("failed") " )" ); + return PM3_ESOFT; + } + return PM3_SUCCESS; +} + static bool HF14B_st_reader(bool verbose) { iso14b_raw_cmd_t packet = { @@ -961,21 +1162,25 @@ static bool HF14B_st_reader(bool verbose) { return false; } - iso14b_card_select_t card; - memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + switch (resp.status) { + case PM3_SUCCESS:{ + iso14b_card_select_t card; + memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - int status = resp.oldarg[0]; - switch (status) { - case 0: + uint8_t empty[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if ((card.uidlen != 8) || (memcmp(card.uid, empty, card.uidlen) == 0)) { + return false; + } print_st_general_info(card.uid, card.uidlen); return true; - case -1: + } + case PM3_ELENGTH: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ST ATTRIB fail"); break; - case -2: + case PM3_ECRC: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ST CRC fail"); break; - case -3: + case PM3_EWRONGANSWER: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ST random chip id fail"); break; default: @@ -1003,11 +1208,15 @@ static bool HF14B_std_reader(bool verbose) { return false; } - int status = resp.oldarg[0]; - switch (status) { - case 0: { + switch (resp.status) { + case PM3_SUCCESS: { iso14b_card_select_t card; memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + + uint8_t empty[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + if (memcmp(card.uid, empty, card.uidlen) == 0) { + return false; + } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, " UID : " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); @@ -1015,11 +1224,11 @@ static bool HF14B_std_reader(bool verbose) { print_atqb_resp(card.atqb, card.cid); return true; } - case -1: { + case PM3_ELENGTH: { if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); break; } - case -2: { + case PM3_ECRC: { if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); break; } @@ -1048,18 +1257,16 @@ static bool HF14B_ask_ct_reader(bool verbose) { return false; } - int status = resp.oldarg[0]; - - switch (status) { - case 0: { + switch (resp.status) { + case PM3_SUCCESS: { print_ct_general_info(resp.data.asBytes); return true; } - case -1: { + case PM3_ELENGTH: { if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CTS wrong length"); break; } - case -2: { + case PM3_ECRC: { if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CTS CRC fail"); break; } @@ -1087,29 +1294,12 @@ static bool HF14B_other_reader(bool verbose) { // 14b get and print UID only (general info) clearCommandBuffer(); - PacketResponseNG resp; SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + packet->rawlen); - if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { - if (verbose) { - PrintAndLogEx(WARNING, "timeout while waiting for reply"); - } - free(packet); - switch_off_field_14b(); - return false; - } - int status = resp.oldarg[0]; - PrintAndLogEx(DEBUG, "status %d", status); - if (status == 0) { + // wait for the select message and wait for response + if (wait_14b_response(false, NULL, NULL) ) { PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); - PrintAndLogEx(SUCCESS, "unknown tag type answered to a 0x000b3f80 command ans:"); - switch_off_field_14b(); - free(packet); - return true; - } else if (status > 0) { - PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); - PrintAndLogEx(SUCCESS, "unknown tag type answered to a 0x000b3f80 command ans:"); - PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp.data.asBytes, status)); + PrintAndLogEx(SUCCESS, "unknown tag type answered to a " _YELLOW_("0x000b3f80") " command"); switch_off_field_14b(); free(packet); return true; @@ -1119,27 +1309,10 @@ static bool HF14B_other_reader(bool verbose) { packet->raw[0] = ISO14443B_AUTHENTICATE; clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + packet->rawlen); - if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { - if (verbose) { - PrintAndLogEx(WARNING, "timeout while waiting for reply"); - } - switch_off_field_14b(); - free(packet); - return false; - } - status = resp.oldarg[0]; - PrintAndLogEx(DEBUG, "status %d", status); - if (status == 0) { + if (wait_14b_response(false, NULL, NULL)) { PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); - PrintAndLogEx(SUCCESS, "Unknown tag type answered to a 0x0A command ans:"); - switch_off_field_14b(); - free(packet); - return true; - } else if (status > 0) { - PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); - PrintAndLogEx(SUCCESS, "unknown tag type answered to a 0x0A command ans:"); - PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp.data.asBytes, status)); + PrintAndLogEx(SUCCESS, "Unknown tag type answered to a " _YELLOW_("0x0A") " command"); switch_off_field_14b(); free(packet); return true; @@ -1149,25 +1322,9 @@ static bool HF14B_other_reader(bool verbose) { clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + packet->rawlen); free(packet); - if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { - if (verbose) { - PrintAndLogEx(WARNING, "timeout while waiting for reply"); - } - switch_off_field_14b(); - return false; - } - status = resp.oldarg[0]; - PrintAndLogEx(DEBUG, "status %d", status); - - if (status == 0) { + if (wait_14b_response(false, NULL, NULL)) { PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); - PrintAndLogEx(SUCCESS, "Unknown tag type answered to a 0x0C command ans:"); - switch_off_field_14b(); - return true; - } else if (status > 0) { - PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); - PrintAndLogEx(SUCCESS, "unknown tag type answered to a 0x0C command ans:"); - PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp.data.asBytes, status)); + PrintAndLogEx(SUCCESS, "Unknown tag type answered to a " _YELLOW_("0x0C") " command"); switch_off_field_14b(); return true; } @@ -1239,22 +1396,13 @@ static int CmdHF14BSriRdBl(const char *Cmd) { uint8_t cardtype = get_st_cardsize(card.uid); uint8_t blocks = (cardtype == 1) ? 0x7F : 0x0F; */ - struct { - uint8_t blockno; - } PACKED payload; - payload.blockno = blockno; - - PacketResponseNG resp; - clearCommandBuffer(); - SendCommandNG(CMD_HF_SRI_READ, (uint8_t *)&payload, sizeof(payload)); - if (WaitForResponseTimeout(CMD_HF_SRI_READ, &resp, TIMEOUT) == false) { - return PM3_ETIMEOUT; + uint8_t out[4] = {0}; + int status = read_sr_block(blockno, out); + if (status == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "block %02u... " _GREEN_("%s") " | " _GREEN_("%s"), blockno, sprint_hex(out, sizeof(out)), sprint_ascii(out, sizeof(out))); } - if (resp.status == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "block %02u : " _GREEN_("%s") " | " _GREEN_("%s"), blockno, sprint_hex(resp.data.asBytes, resp.length), sprint_ascii(resp.data.asBytes, resp.length)); - } - return resp.status; + return status; } // New command to write a SRI512/SRIX4K tag. @@ -1272,7 +1420,8 @@ static int CmdHF14BWriteSri(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14b sriwrite", - "Write data to a SRI512 or SRIX4K block", + "Write data to a SRI512 or SRIX4K block\n" + "If writing to a block out-of-range, use --force to override checks", "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" @@ -1286,6 +1435,7 @@ static int CmdHF14BWriteSri(const char *Cmd) { 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_lit0(NULL, "force", "overrides block range checks"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -1301,6 +1451,7 @@ static int CmdHF14BWriteSri(const char *Cmd) { bool use_sri512 = arg_get_lit(ctx, 3); bool use_srix4k = arg_get_lit(ctx, 4); bool special = arg_get_lit(ctx, 5); + bool override = arg_get_lit(ctx, 6); CLIParserFree(ctx); if (dlen != sizeof(data)) { @@ -1314,35 +1465,55 @@ static int CmdHF14BWriteSri(const char *Cmd) { } if (use_srix4k && blockno > 0x7F) { - PrintAndLogEx(FAILED, "block number out of range, max 127 (0x7F)"); - return PM3_EINVARG; + PrintAndLogEx(FAILED, "block number out of range, max 127 (0x7F), got " _RED_("%u"), blockno); + if (override) { + PrintAndLogEx(INFO, "overriding block check"); + } else { + return PM3_EINVARG; + } } if (use_sri512 && blockno > 0x0F) { - PrintAndLogEx(FAILED, "block number out of range, max 15 (0x0F)"); - return PM3_EINVARG; + PrintAndLogEx(FAILED, "block number out of range, max 15 (0x0F), got " _RED_("%u"), blockno); + if (override) { + PrintAndLogEx(INFO, "overriding block check"); + } else { + return PM3_EINVARG; + } } // special block at end of memory if (special) { blockno = 0xFF; - PrintAndLogEx(SUCCESS, "[%s] Write special block %02X [ " _YELLOW_("%s")" ]", + PrintAndLogEx(SUCCESS, _YELLOW_("%s") " Write special block %02X - " _YELLOW_("%s"), (use_srix4k) ? "SRIX4K" : "SRI512", blockno, sprint_hex(data, sizeof(data)) ); } else { - PrintAndLogEx(SUCCESS, "[%s] Write block %02X [ " _YELLOW_("%s")" ]", + PrintAndLogEx(SUCCESS, _YELLOW_("%s") " Write block %02X - " _YELLOW_("%s"), (use_srix4k) ? "SRIX4K" : "SRI512", blockno, sprint_hex(data, sizeof(data)) ); } - char str[36]; - memset(str, 0x00, sizeof(str)); - snprintf(str, sizeof(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); + int status = write_sr_block(blockno, 4, data); + if (status != PM3_SUCCESS) { + return status; + } + + // verify + uint8_t out[4] = {0}; + status = read_sr_block(blockno, out); + if (status == PM3_SUCCESS) { + if (memcmp(data, out, 4) == 0) { + PrintAndLogEx(SUCCESS, "SRx write block ( " _GREEN_("ok") " )" ); + } + } else { + PrintAndLogEx(INFO, "Verifying block ( " _RED_("failed") " )"); + } + return status; } // need to write to file @@ -1399,7 +1570,6 @@ static int CmdHF14BDump(const char *Cmd) { return switch_off_field_14b(); } - if (select_cardtype == ISO14B_SR) { iso14b_card_select_t card; memcpy(&card, (iso14b_card_select_t *)&select, sizeof(iso14b_card_select_t)); @@ -1412,11 +1582,11 @@ static int CmdHF14BDump(const char *Cmd) { uint16_t cardsize = 0; switch (cardtype) { - case 2: + case SR_SIZE_512: cardsize = (512 / 8) + ST25TB_SR_BLOCK_SIZE; lastblock = 0x0F; break; - case 1: + case SR_SIZE_4K: default: cardsize = (4096 / 8) + ST25TB_SR_BLOCK_SIZE; lastblock = 0x7F; @@ -1427,7 +1597,8 @@ static int CmdHF14BDump(const char *Cmd) { PrintAndLogEx(SUCCESS, "found a " _GREEN_("%s") " tag", get_st_chip_model(chipid)); // detect blocksize from card :) - PrintAndLogEx(INFO, "reading tag memory from UID " _GREEN_("%s"), sprint_hex_inrow(SwapEndian64(card.uid, card.uidlen, 8), card.uidlen)); + PrintAndLogEx(INFO, "reading tag memory"); + iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 2); if (packet == NULL) { PrintAndLogEx(FAILED, "failed to allocate memory"); @@ -1442,11 +1613,9 @@ static int CmdHF14BDump(const char *Cmd) { PacketResponseNG resp; // select SR tag - int status; if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { - status = resp.oldarg[0]; - if (status < 0) { - PrintAndLogEx(FAILED, "failed to select arg0[%" PRId64 "]", resp.oldarg[0]); + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "failed to select ( " _RED_("%d") " )", resp.status); free(packet); return switch_off_field_14b(); } @@ -1458,7 +1627,7 @@ static int CmdHF14BDump(const char *Cmd) { memset(data, 0, sizeof(data)); uint16_t blocknum = 0; - for (int retry = 0; retry < 5; retry++) { + for (int retry = 0; retry < 3; retry++) { // set up the read command packet->flags = (ISO14B_APPEND_CRC | ISO14B_RAW); @@ -1470,21 +1639,18 @@ static int CmdHF14BDump(const char *Cmd) { SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)packet, sizeof(iso14b_raw_cmd_t) + 2); if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { - status = resp.oldarg[0]; - if (status < 0) { + if (resp.status != PM3_SUCCESS) { PrintAndLogEx(FAILED, "retrying one more time"); continue; } - uint16_t len = (resp.oldarg[1] & 0xFFFF); uint8_t *recv = resp.data.asBytes; - if (check_crc(CRC_14443_B, recv, len) == false) { + if (check_crc(CRC_14443_B, recv, resp.length) == false) { PrintAndLogEx(FAILED, "crc fail, retrying one more time"); continue; } - // last read if (blocknum == 0xFF) { // we reserved space for this block after 0x0F and 0x7F, ie 0x10, 0x80 @@ -1508,28 +1674,33 @@ static int CmdHF14BDump(const char *Cmd) { free(packet); PrintAndLogEx(NORMAL, ""); + switch_off_field_14b(); if (blocknum != 0xFF) { PrintAndLogEx(FAILED, "dump failed"); - return switch_off_field_14b(); + return PM3_ESOFT; } print_sr_blocks(data, cardsize, card.uid); - if (nosave == false) { - // save to file - if (fnlen < 1) { - PrintAndLogEx(INFO, "using UID as filename"); - char *fptr = filename + snprintf(filename, sizeof(filename), "hf-14b-"); - FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), "-dump", card.uidlen); - } - - size_t datalen = (lastblock + 2) * ST25TB_SR_BLOCK_SIZE; - pm3_save_dump(filename, data, datalen, jsf14b, ST25TB_SR_BLOCK_SIZE); + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; } + + // save to file + if (fnlen < 1) { + PrintAndLogEx(INFO, "using UID as filename"); + char *fptr = filename + snprintf(filename, sizeof(filename), "hf-14b-"); + FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), "-dump", card.uidlen); + } + + size_t datalen = (lastblock + 2) * ST25TB_SR_BLOCK_SIZE; + pm3_save_dump(filename, data, datalen, jsf14b_v2); } - return switch_off_field_14b(); + return PM3_ESOFT; } /* @@ -1640,13 +1811,15 @@ static int srix4kValid(const char *Cmd) { */ int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card) { - if (card) + if (card) { memset(card, 0, sizeof(iso14b_card_select_t)); + } + SetAPDULogging(true); switch_off_field_14b(); iso14b_raw_cmd_t packet = { - .flags = (ISO14B_CONNECT | ISO14B_SELECT_STD), + .flags = (ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_CLEARTRACE), .timeout = 0, .rawlen = 0, }; @@ -1657,13 +1830,13 @@ int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card) { PrintAndLogEx(INFO, "Trying 14B Select SRx"); // Anticollision + SELECT SR card - packet.flags = (ISO14B_CONNECT | ISO14B_SELECT_SR); + packet.flags = (ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_CLEARTRACE); SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { PrintAndLogEx(INFO, "Trying 14B Select CTS"); // Anticollision + SELECT ASK C-Ticket card - packet.flags = (ISO14B_CONNECT | ISO14B_SELECT_CTS); + packet.flags = (ISO14B_CONNECT | ISO14B_SELECT_CTS | ISO14B_CLEARTRACE); SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { PrintAndLogEx(ERR, "connection timeout"); @@ -1674,12 +1847,12 @@ int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card) { } // check result - int status = resp.oldarg[0]; - if (status < 0) { - PrintAndLogEx(ERR, "No card in field."); + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "No ISO14443-B Card in field"); switch_off_field_14b(); return PM3_ESOFT; } + SetISODEPState(ISODEP_NFCB); apdu_frame_length = 0; // get frame length from ATS in card data structure @@ -1708,8 +1881,9 @@ static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, if (activateField) { // select with no disconnect and set frameLength int selres = select_card_14443b_4(false, NULL); - if (selres != PM3_SUCCESS) + if (selres != PM3_SUCCESS) { return selres; + } } iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + datainlen); @@ -1721,8 +1895,9 @@ static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, packet->timeout = 0; packet->rawlen = 0; - if (chainingin) + if (chainingin) { packet->flags = (ISO14B_SEND_CHAINING | ISO14B_APDU); + } if (user_timeout > 0) { packet->flags |= ISO14B_SET_TIMEOUT; @@ -1752,8 +1927,15 @@ static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, return PM3_ETIMEOUT; } - int rlen = resp.oldarg[0]; - int dlen = rlen - 2; + if ( resp.status != PM3_SUCCESS) { + PrintAndLogEx(ERR, "APDU: no APDU response"); + return resp.status; + } + + iso14b_raw_apdu_response_t *apdu = (iso14b_raw_apdu_response_t *)resp.data.asBytes; + + // remove crc bytes + int dlen = apdu->datalen - 2; if (dlen < 0) { dlen = 0; } @@ -1761,33 +1943,28 @@ static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, *dataoutlen += dlen; if (maxdataoutlen && *dataoutlen > maxdataoutlen) { - PrintAndLogEx(ERR, "APDU: buffer too small(%d), needs %d bytes", maxdataoutlen, *dataoutlen); + PrintAndLogEx(ERR, "APDU: buffer too small ( " _RED_("%d") " ), needs " _YELLOW_("%d") " bytes", maxdataoutlen, *dataoutlen); return PM3_ESOFT; } // I-block ACK - uint8_t res = resp.oldarg[1]; - if ((res & 0xF2) == 0xA2) { + if ((apdu->response_byte & 0xF2) == 0xA2) { *dataoutlen = 0; *chainingout = true; return PM3_SUCCESS; } - if (rlen < 0) { - PrintAndLogEx(ERR, "APDU: no APDU response"); - return PM3_ESOFT; - } - // check apdu length - if (rlen == 0 || rlen == 1) { - PrintAndLogEx(ERR, "APDU: small APDU response, len %d", rlen); + if (apdu->datalen < 2) { + PrintAndLogEx(ERR, "APDU: small APDU response, len " _RED_("%d"), apdu->datalen); return PM3_ESOFT; } - - memcpy(dataout, resp.data.asBytes, dlen); + + // copy to output array + memcpy(dataout, apdu->data, dlen); // chaining - if ((res & 0x10) != 0) { + if ((apdu->response_byte & 0x10) != 0) { *chainingout = true; } return PM3_SUCCESS; @@ -1815,8 +1992,9 @@ int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, *dataoutlen = 0; res = handle_14b_apdu(chainBlockNotLast, &datain[clen], vlen, v_activate_field, dataout, maxdataoutlen, dataoutlen, &chaining, user_timeout); if (res) { - if (leave_signal_on == false) + if (leave_signal_on == false) { switch_off_field_14b(); + } return 200; } @@ -1834,8 +2012,9 @@ int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, clen += vlen; v_activate_field = false; if (*dataoutlen) { - if (clen != datainlen) + if (clen != datainlen) { PrintAndLogEx(ERR, "APDU: I-block/R-block sequence error. Data len=%d, Sent=%d, Last packet len=%d", datainlen, clen, *dataoutlen); + } break; } } while (clen < datainlen); @@ -2100,9 +2279,13 @@ int CmdHF14BNdefRead(const char *Cmd) { goto out; } - if (fnlen != 0) { - saveFile(filename, ".bin", response + 2, resplen - 4); - } + // get total NDEF length before save. If fails, we save it all + size_t n = 0; + if (NDEFGetTotalLength(response + 2, resplen - 4, &n) != PM3_SUCCESS) + n = resplen - 4; + + pm3_save_dump(filename, response + 2, n, jsfNDEF); + res = NDEFRecordsDecodeAndPrint(response + 2, resplen - 4, verbose); out: @@ -2138,12 +2321,15 @@ static int CmdHF14BView(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14b view", - "Print a ISO14443-B dump file (bin/eml/json)", + "Print a ISO14443-B dump file (bin/eml/json)\n" + "note:\n" + " - command expects the filename to contain a UID\n" + " which is needed to determine card memory type", "hf 14b view -f hf-14b-01020304-dump.bin" ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; @@ -2179,7 +2365,6 @@ static int CmdHF14BView(const char *Cmd) { return PM3_SUCCESS; } - static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"apdu", CmdHF14BAPDU, IfPm3Iso14443b, "Send ISO 14443-4 APDU to tag"}, @@ -2229,33 +2414,43 @@ int infoHF14B(bool verbose, bool do_aid_search) { // get and print general info about all known 14b chips int readHF14B(bool loop, bool verbose) { + bool found = false; do { + found = false; + // try std 14b (atqb) - if (HF14B_std_reader(verbose)) - if (loop) - continue; + found |= HF14B_std_reader(verbose); + if (found && loop) + continue; + else if (found) + return PM3_SUCCESS; // try ST Microelectronics 14b - if (HF14B_st_reader(verbose)) - if (loop) - continue; + found |= HF14B_st_reader(verbose); + if (found && loop) + continue; + else if (found) + return PM3_SUCCESS; // try ASK CT 14b - if (HF14B_ask_ct_reader(verbose)) - if (loop) - continue; + found |= HF14B_ask_ct_reader(verbose); + if (found && loop) + continue; + else if (found) + return PM3_SUCCESS; // try unknown 14b read commands (to be identified later) // could be read of calypso, CEPAS, moneo, or pico pass. - if (HF14B_other_reader(verbose)) - if (loop) - continue; - + found |= HF14B_other_reader(verbose); + if (found && loop) + continue; + else if (found) + return PM3_SUCCESS; } while (loop && kbd_enter_pressed() == false); - if (verbose) { + if (verbose && found == false) { PrintAndLogEx(FAILED, "no ISO 14443-B tag found"); } - return PM3_EOPABORTED; + return (found) ? PM3_SUCCESS : PM3_EOPABORTED; } diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index c80f8cac4..6072c57b7 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -48,7 +48,7 @@ #define Logic1 Iso15693Logic1 #define FrameEOF Iso15693FrameEOF #define CARD_MEMORY_SIZE 4096 -#define HF15_UID_LENGTH 8 +#define HF15_UID_LENGTH 8 #ifndef Crc15 # define Crc15(data, len) Crc16ex(CRC_15693, (data), (len)) @@ -62,7 +62,7 @@ typedef struct { uint8_t lock; - uint8_t block[4]; + uint8_t block[8]; } t15memory_t; // structure and database for uid -> tagtype lookups @@ -99,15 +99,15 @@ static const productName_t uidmapping[] = { //I-Code SLI-L [IC id = 03 + bit36 set to 0] //I-Code SLIX-L [IC id = 03 + bit36 set to 1] { 0xE004000000000000LL, 16, "NXP Semiconductors Germany (Philips)" }, - { 0xE004010000000000LL, 24, "NXP(Philips); IC SL2 ICS20/ICS21(SLI) ICS2002/ICS2102(SLIX) ICS2602(SLIX2)" }, - { 0xE004011800000000LL, 0xFFFFFF1800000000LL, "NXP(Philips); IC NTP53x2/NTP5210/NTA5332(NTAG 5)" }, - { 0xE004010000000000LL, 0xFFFFFF1800000000LL, "NXP(Philips); IC SL2 ICS20/ICS21(SLI)" }, - { 0xE004011000000000LL, 0xFFFFFF1800000000LL, "NXP(Philips); IC SL2 ICS2002/ICS2102(SLIX)" }, - { 0xE004010800000000LL, 0xFFFFFF1800000000LL, "NXP(Philips); IC SL2 ICS2602(SLIX2)" }, - { 0xE004020000000000LL, 0xFFFFFF1000000000LL, "NXP(Philips); IC SL2 ICS53/ICS54(SLI-S)" }, - { 0xE004021000000000LL, 0xFFFFFF1000000000LL, "NXP(Philips); ICS5302/ICS5402(SLIX-S)" }, - { 0xE004030000000000LL, 0xFFFFFF1000000000LL, "NXP(Philips); IC SL2 ICS50/ICS51(SLI-L)" }, - { 0xE004031000000000LL, 0xFFFFFF1000000000LL, "NXP(Philips); ICS5002/ICS5102(SLIX-L)" }, + { 0xE004010000000000LL, 24, "NXP (Philips); IC SL2 ICS20/ICS21 (SLI) ICS2002/ICS2102 (SLIX) ICS2602 (SLIX2)" }, + { 0xE004011800000000LL, 0xFFFFFF1800000000LL, "NXP (Philips); IC NTP53x2/NTP5210/NTA5332 " AEND "( " _CYAN_("NTAG 5") " )" }, + { 0xE004010000000000LL, 0xFFFFFF1800000000LL, "NXP (Philips); IC SL2 ICS20/ICS21 " AEND "( " _CYAN_("SLI") " )" }, + { 0xE004011000000000LL, 0xFFFFFF1800000000LL, "NXP (Philips); IC SL2 ICS2002/ICS2102 " AEND "( " _CYAN_("SLIX") " )" }, + { 0xE004010800000000LL, 0xFFFFFF1800000000LL, "NXP (Philips); IC SL2 ICS2602 " AEND "( " _CYAN_("SLIX2") " )" }, + { 0xE004020000000000LL, 0xFFFFFF1000000000LL, "NXP (Philips); IC SL2 ICS53/ICS54 " AEND "( " _CYAN_("SLI-S") " )" }, + { 0xE004021000000000LL, 0xFFFFFF1000000000LL, "NXP (Philips); ICS5302/ICS5402 " AEND "( " _CYAN_("SLIX-S") " )" }, + { 0xE004030000000000LL, 0xFFFFFF1000000000LL, "NXP (Philips); IC SL2 ICS50/ICS51 " AEND "( " _CYAN_("SLI-L") " )" }, + { 0xE004031000000000LL, 0xFFFFFF1000000000LL, "NXP (Philips); ICS5002/ICS5102 " AEND "( " _CYAN_("SLIX-L") " )" }, // E0 05 XX .. .. .. // 05 = Manufacturer code (Infineon) @@ -115,10 +115,10 @@ static const productName_t uidmapping[] = { { 0xE005000000000000LL, 16, "Infineon Technologies AG Germany" }, { 0xE005A10000000000LL, 24, "Infineon; SRF55V01P [IC id = 161] plain mode 1kBit"}, { 0xE005A80000000000LL, 24, "Infineon; SRF55V01P [IC id = 168] pilot series 1kBit"}, - { 0xE005400000000000LL, 24, "Infineon; SRF55V02P [IC id = 64] plain mode 2kBit"}, - { 0xE005000000000000LL, 24, "Infineon; SRF55V10P [IC id = 00] plain mode 10KBit"}, - { 0xE005500000000000LL, 24, "Infineon; SRF55V02S [IC id = 80] secure mode 2kBit"}, - { 0xE005100000000000LL, 24, "Infineon; SRF55V10S [IC id = 16] secure mode 10KBit"}, + { 0xE005400000000000LL, 24, "Infineon; SRF55V02P [IC id = 64] plain mode 2kBit"}, + { 0xE005000000000000LL, 24, "Infineon; SRF55V10P [IC id = 00] plain mode 10KBit"}, + { 0xE005500000000000LL, 24, "Infineon; SRF55V02S [IC id = 80] secure mode 2kBit"}, + { 0xE005100000000000LL, 24, "Infineon; SRF55V10S [IC id = 16] secure mode 10KBit"}, { 0xE0051E0000000000LL, 23, "Infineon; SLE66r01P [IC id = 3x = My-d Move or My-d move NFC]"}, { 0xE005200000000000LL, 21, "Infineon; SLE66r01P [IC id = 3x = My-d Move or My-d move NFC]"}, @@ -229,14 +229,14 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { #define PUBLIC_ECDA_KEYLEN 33 const ecdsa_publickey_t nxp_15693_public_keys[] = { - {"NXP Mifare Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, - {"Manufacturer Mifare Classic MFC1C14_x", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, - {"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, - {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, - {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, - {"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"}, - {"MIKRON Public key", "04f971eda742a4a80d32dcf6a814a707cc3dc396d35902f72929fdcd698b3468f2"}, - {"VivoKey Spark1 Public key", "04d64bb732c0d214e7ec580736acf847284b502c25c0f7f2fa86aace1dada4387a"}, + {"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, + {"Manufacturer MIFARE Classic / QL88", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, + {"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, + {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, + {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, + {"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"}, + {"MIKRON Public key", "04f971eda742a4a80d32dcf6a814a707cc3dc396d35902f72929fdcd698b3468f2"}, + {"VivoKey Spark1 Public key", "04d64bb732c0d214e7ec580736acf847284b502c25c0f7f2fa86aace1dada4387a"}, }; /* uint8_t nxp_15693_public_keys[][PUBLIC_ECDA_KEYLEN] = { @@ -335,7 +335,7 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { return PM3_ESOFT; } - PrintAndLogEx(INFO, " IC signature public key name: %s", nxp_15693_public_keys[i].desc); + PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_15693_public_keys[i].desc); PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_15693_public_keys[i].value); PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); @@ -671,11 +671,9 @@ static int NxpTestEAS(uint8_t *uid) { SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "iso15693 timeout"); + PrintAndLogEx(DEBUG, "iso15693 timeout"); } else { - PrintAndLogEx(NORMAL, ""); - - + PrintAndLogEx(INFO, ""); if (resp.length < 2) { PrintAndLogEx(INFO, " EAS (Electronic Article Surveillance) is not active"); } else { @@ -687,7 +685,6 @@ static int NxpTestEAS(uint8_t *uid) { } } } - return PM3_SUCCESS; } @@ -712,7 +709,7 @@ static int NxpCheckSig(uint8_t *uid) { SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "iso15693 timeout"); + PrintAndLogEx(DEBUG, "iso15693 timeout"); DropField(); return PM3_ETIMEOUT; } @@ -732,10 +729,9 @@ static int NxpCheckSig(uint8_t *uid) { } uint8_t signature[32] = {0x00}; - memcpy(signature, recv + 1, 32); + memcpy(signature, recv + 1, sizeof(signature)); nxp_15693_print_signature(uid, signature); - return PM3_SUCCESS; } @@ -764,7 +760,7 @@ static int NxpSysInfo(uint8_t *uid) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); if (WaitForResponseTimeout(CMD_HF_ISO15693_COMMAND, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "iso15693 timeout"); + PrintAndLogEx(DEBUG, "iso15693 timeout"); DropField(); return PM3_ETIMEOUT; } @@ -790,21 +786,29 @@ static int NxpSysInfo(uint8_t *uid) { bool support_signature = (recv[5] & 0x01); bool support_easmode = (recv[4] & 0x04); - PrintAndLogEx(INFO, "--------- " _CYAN_("NXP Sysinfo") " ---------"); - PrintAndLogEx(INFO, " raw : %s", sprint_hex(recv, 8)); - PrintAndLogEx(INFO, " Password protection configuration:"); - PrintAndLogEx(INFO, " * Page L read%s password protected", ((recv[2] & 0x01) ? "" : " not")); - PrintAndLogEx(INFO, " * Page L write%s password protected", ((recv[2] & 0x02) ? "" : " not")); - PrintAndLogEx(INFO, " * Page H read%s password protected", ((recv[2] & 0x10) ? "" : " not")); - PrintAndLogEx(INFO, " * Page H write%s password protected", ((recv[2] & 0x20) ? "" : " not")); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("NXP Sysinfo")); + PrintAndLogEx(INFO, " raw... %s", sprint_hex(recv, 8)); + PrintAndLogEx(INFO, " " _CYAN_("Password protection configuration:")); + PrintAndLogEx(INFO, " * Page L read%s password protected", ((recv[2] & 0x01) ? "" : _GREEN_(" not"))); + PrintAndLogEx(INFO, " * Page L write%s password protected", ((recv[2] & 0x02) ? "" : _GREEN_(" not"))); + PrintAndLogEx(INFO, " * Page H read%s password protected", ((recv[2] & 0x10) ? "" : _GREEN_(" not"))); + PrintAndLogEx(INFO, " * Page H write%s password protected", ((recv[2] & 0x20) ? "" : _GREEN_(" not"))); - PrintAndLogEx(INFO, " Lock bits:"); - PrintAndLogEx(INFO, " * AFI%s locked", ((recv[3] & 0x01) ? "" : " not")); // AFI lock bit - PrintAndLogEx(INFO, " * EAS%s locked", ((recv[3] & 0x02) ? "" : " not")); // EAS lock bit - PrintAndLogEx(INFO, " * DSFID%s locked", ((recv[3] & 0x03) ? "" : " not")); // DSFID lock bit - PrintAndLogEx(INFO, " * Password protection configuration%s locked", ((recv[3] & 0x04) ? "" : " not")); // Password protection pointer address and access conditions lock bit + PrintAndLogEx(INFO, " " _CYAN_("Lock bits:")); + // AFI lock bit + PrintAndLogEx(INFO, " * AFI%s locked", ((recv[3] & 0x01) ? "" : _GREEN_(" not"))); - PrintAndLogEx(INFO, " Features:"); + // EAS lock bit + PrintAndLogEx(INFO, " * EAS%s locked", ((recv[3] & 0x02) ? "" : _GREEN_(" not"))); + + // DSFID lock bit + PrintAndLogEx(INFO, " * DSFID%s locked", ((recv[3] & 0x03) ? "" : _GREEN_(" not"))); + + // Password protection pointer address and access conditions lock bit + PrintAndLogEx(INFO, " * Password protection configuration%s locked", ((recv[3] & 0x04) ? "" : _GREEN_(" not"))); + + PrintAndLogEx(INFO, " " _CYAN_("Features:")); PrintAndLogEx(INFO, " * User memory password protection%s supported", ((recv[4] & 0x01) ? "" : " not")); PrintAndLogEx(INFO, " * Counter feature%s supported", ((recv[4] & 0x02) ? "" : " not")); PrintAndLogEx(INFO, " * EAS ID%s supported by EAS ALARM command", support_easmode ? "" : " not"); @@ -827,6 +831,7 @@ static int NxpSysInfo(uint8_t *uid) { NxpCheckSig(uid); } + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -844,7 +849,7 @@ static int CmdHF15Info(const char *Cmd) { "hf 15 info -u E011223344556677" ); - void *argtable[6 + 1] = {}; + void *argtable[6 + 1] = {0}; uint8_t arglen = arg_add_default(argtable); argtable[arglen++] = arg_param_end; @@ -925,7 +930,6 @@ static int CmdHF15Info(const char *Cmd) { memcpy(uid, data + 2, sizeof(uid)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); PrintAndLogEx(SUCCESS, " TYPE: " _YELLOW_("%s"), getTagInfo_15(data + 2)); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), iso15693_sprintUID(NULL, uid)); PrintAndLogEx(SUCCESS, " SYSINFO: %s", sprint_hex(data, resp.length - 2)); @@ -1020,7 +1024,7 @@ static int CmdHF15Reader(const char *Cmd) { CLIParserFree(ctx); if (cm) { - PrintAndLogEx(INFO, "press " _GREEN_("`Enter`") " to exit"); + PrintAndLogEx(INFO, "press " _GREEN_("") " to exit"); } readHF15Uid(cm, true); return PM3_SUCCESS; @@ -1129,6 +1133,7 @@ static int CmdHF15ELoad(const char *Cmd) { } free(data); PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%zu") " bytes to emulator memory", offset); PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf 15 sim -h`")); PrintAndLogEx(INFO, "Done!"); @@ -1138,14 +1143,14 @@ static int CmdHF15ELoad(const char *Cmd) { static int CmdHF15ESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 15 esave", - "Save emulator memory into three files (BIN/EML/JSON) ", + "Save emulator memory into two files (bin/json) ", "hf 15 esave -f hf-15-01020304" "hf 15 esave -b 8 -c 42 -f hf-15-01020304" ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), - arg_int0("b", "blocksize", "", "block size, defaults to 4"), + arg_str1("f", "file", "", "Specify a filename for dump file"), + arg_int0(NULL, "bsize", "", "block size, defaults to 4"), arg_int0("c", "count", "", "number of blocks to export, defaults to all"), arg_param_end }; @@ -1171,13 +1176,18 @@ static int CmdHF15ESave(const char *Cmd) { } PrintAndLogEx(INFO, "Downloading %u bytes from emulator memory", bytes); - if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; } - pm3_save_dump(filename, dump, bytes, jsf15, blocksize); + if (blocksize == 8) { + pm3_save_dump(filename, dump, bytes, jsf15_v3); + } else { + pm3_save_dump(filename, dump, bytes, jsf15_v2); + } + free(dump); return PM3_SUCCESS; } @@ -1239,7 +1249,7 @@ static int CmdHF15EView(const char *Cmd) { } PrintAndLogEx(INFO, "Downloading %u bytes from emulator memory", bytes); - if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; @@ -1284,12 +1294,13 @@ static int CmdHF15Sim(const char *Cmd) { CLIParserFree(ctx); PrintAndLogEx(SUCCESS, "Starting simulating UID " _YELLOW_("%s"), iso15693_sprintUID(NULL, payload.uid)); - PrintAndLogEx(INFO, "press " _YELLOW_("`Pm3 button`") " to cancel"); + PrintAndLogEx(INFO, "Press " _YELLOW_("`pm3-button`") " to abort simulation"); PacketResponseNG resp; clearCommandBuffer(); SendCommandNG(CMD_HF_ISO15693_SIMULATE, (uint8_t *)&payload, sizeof(payload)); WaitForResponse(CMD_HF_ISO15693_SIMULATE, &resp); + PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -1310,7 +1321,7 @@ static int CmdHF15FindAfi(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); - PrintAndLogEx(INFO, "click " _GREEN_("pm3 button") " or press " _GREEN_("Enter") " to exit"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to exit"); clearCommandBuffer(); PacketResponseNG resp; SendCommandMIX(CMD_HF_ISO15693_FINDAFI, strtol(Cmd, NULL, 0), 0, 0, NULL, 0); @@ -1354,12 +1365,13 @@ static int CmdHF15WriteAfi(const char *Cmd) { "hf 15 writeafi -u E011223344556677 --afi 12 -p 0F0F0F0F" ); - void *argtable[5] = {}; - argtable[0] = arg_param_begin; - argtable[1] = arg_str0("u", "uid", "", "full UID, 8 bytes"); - argtable[2] = arg_int1(NULL, "afi", "", "AFI number (0-255)"); - argtable[3] = arg_str0("p", "pwd", "", "optional AFI/EAS password"); - argtable[4] = arg_param_end; + void *argtable[] = { + arg_param_begin, + arg_str0("u", "uid", "", "full UID, 8 bytes"), + arg_int1(NULL, "afi", "", "AFI number (0-255)"), + arg_str0("p", "pwd", "", "optional AFI/EAS password"), + arg_param_end + }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1437,7 +1449,7 @@ static int CmdHF15WriteDsfid(const char *Cmd) { "hf 15 writedsfid -u E011223344556677 --dsfid 12" ); - void *argtable[6 + 2] = {}; + void *argtable[6 + 2] = {0}; uint8_t arglen = arg_add_default(argtable); argtable[arglen++] = arg_int1(NULL, "dsfid", "", "DSFID number (0-255)"); argtable[arglen++] = arg_param_end; @@ -1531,15 +1543,15 @@ static int CmdHF15WriteDsfid(const char *Cmd) { static int CmdHF15Dump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 15 dump", - "This command dumps the contents of a ISO-15693 tag and save it to file", + "This command dumps the contents of a ISO-15693 tag and save to file (bin/json)", "hf 15 dump\n" "hf 15 dump -*\n" "hf 15 dump -u E011223344556677 -f hf-15-my-dump.bin" ); - void *argtable[6 + 2] = {}; + void *argtable[6 + 2] = {0}; uint8_t arglen = arg_add_default(argtable); - argtable[arglen++] = arg_str0("f", "file", "", "filename of dump"), + argtable[arglen++] = arg_str0("f", "file", "", "Specify a filename for dump file"), argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1598,9 +1610,12 @@ static int CmdHF15Dump(const char *Cmd) { // memory. t15memory_t mem[256]; - uint8_t data[256 * 4] = {0}; + uint8_t data[256 * 8] = {0}; memset(data, 0, sizeof(data)); + // keep track of which block length tag returned? + uint8_t blklen = 4; + for (int retry = 0; (retry < 5 && blocknum < 0x100); retry++) { req[10] = blocknum; @@ -1640,9 +1655,15 @@ static int CmdHF15Dump(const char *Cmd) { break; } + // lock byte value mem[blocknum].lock = resp.data.asBytes[0]; - memcpy(mem[blocknum].block, resp.data.asBytes + 1, 4); - memcpy(data + (blocknum * 4), resp.data.asBytes + 1, 4); + + // is tag responding with 4 or 8 bytes? + if (resp.length == 11) { + blklen = 8; + } + memcpy(mem[blocknum].block, resp.data.asBytes + 1, blklen); + memcpy(data + (blocknum * 4), resp.data.asBytes + 1, blklen); retry = 0; blocknum++; @@ -1653,6 +1674,10 @@ static int CmdHF15Dump(const char *Cmd) { DropField(); + if (blklen == 8) { + PrintAndLogEx(INFO, "8 byte block length detected"); + } + PrintAndLogEx(NORMAL, "\n"); PrintAndLogEx(INFO, "block# | data |lck| ascii"); PrintAndLogEx(INFO, "---------+--------------+---+----------"); @@ -1666,9 +1691,9 @@ static int CmdHF15Dump(const char *Cmd) { PrintAndLogEx(INFO, "%3d/0x%02X | %s | %s | %s" , i , i - , sprint_hex(mem[i].block, 4) + , sprint_hex(mem[i].block, blklen) , lck - , sprint_ascii(mem[i].block, 4) + , sprint_ascii(mem[i].block, blklen) ); } PrintAndLogEx(NORMAL, ""); @@ -1681,8 +1706,11 @@ static int CmdHF15Dump(const char *Cmd) { FillFileNameByUID(fptr, SwapEndian64(uid, sizeof(uid), 8), "-dump", sizeof(uid)); } - size_t datalen = blocknum * 4; - pm3_save_dump(filename, data, datalen, jsf15, 4); + if (blklen == 8) { + pm3_save_dump(filename, data, (size_t)(blocknum * blklen), jsf15_v3); + } else { + pm3_save_dump(filename, data, (size_t)(blocknum * blklen), jsf15_v2); + } return PM3_SUCCESS; } @@ -1765,7 +1793,7 @@ static int CmdHF15Readmulti(const char *Cmd) { "hf 15 rdmulti -u E011223344556677 -b 12 --cnt 3 -> read three blocks" ); - void *argtable[6 + 3] = {}; + void *argtable[6 + 3] = {0}; uint8_t arglen = arg_add_default(argtable); argtable[arglen++] = arg_int1("b", NULL, "", "first page number (0-255)"); argtable[arglen++] = arg_int1(NULL, "cnt", "", "number of pages (1-6)"); @@ -1901,7 +1929,7 @@ static int CmdHF15Readblock(const char *Cmd) { "hf 15 rdbl -u E011223344556677 -b 12" ); - void *argtable[6 + 2] = {}; + void *argtable[6 + 2] = {0}; uint8_t arglen = arg_add_default(argtable); argtable[arglen++] = arg_int1("b", "blk", "", "page number (0-255)"); argtable[arglen++] = arg_param_end; @@ -1994,15 +2022,16 @@ static int CmdHF15Readblock(const char *Cmd) { return PM3_EWRONGANSWER; } + // print response char lck[16] = {0}; if (data[1]) { - snprintf(lck, sizeof(lck), _RED_("%d"), data[1]); + snprintf(lck, sizeof(lck), _RED_("%02X"), data[1]); } else { - snprintf(lck, sizeof(lck), "%d", data[1]); + snprintf(lck, sizeof(lck), "%02X", data[1]); } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, " #%3d |lck| ascii", block); + PrintAndLogEx(INFO, "#%3d |lck| ascii", block); PrintAndLogEx(INFO, "------------+---+------"); PrintAndLogEx(INFO, "%s| %s | %s", sprint_hex(data + 2, resp.length - 4), lck, sprint_ascii(data + 2, resp.length - 4)); PrintAndLogEx(NORMAL, ""); @@ -2062,7 +2091,7 @@ static int CmdHF15Write(const char *Cmd) { "hf 15 wrbl -u E011223344556677 -b 12 -d AABBCCDD" ); - void *argtable[6 + 4] = {}; + void *argtable[6 + 4] = {0}; uint8_t arglen = arg_add_default(argtable); argtable[arglen++] = arg_int1("b", "blk", "", "page number (0-255)"); argtable[arglen++] = arg_str1("d", "data", "", "data, 4 bytes"); @@ -2147,15 +2176,15 @@ static int CmdHF15Write(const char *Cmd) { static int CmdHF15Restore(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 15 restore", - "This command restore the contents of a dump file onto a ISO-15693 tag", + "This command restore the contents of a dump file (bin/eml/json) onto a ISO-15693 tag", "hf 15 restore\n" "hf 15 restore -*\n" "hf 15 restore -u E011223344556677 -f hf-15-my-dump.bin" ); - void *argtable[6 + 5] = {}; + void *argtable[6 + 5] = {0}; uint8_t arglen = arg_add_default(argtable); - argtable[arglen++] = arg_str0("f", "file", "", "filename of dump"), + argtable[arglen++] = arg_str0("f", "file", "", "Specify a filename for dump file"), argtable[arglen++] = arg_int0("r", "retry", "", "number of retries (def 3)"), argtable[arglen++] = arg_int0(NULL, "bs", "", "block size (def 4)"), argtable[arglen++] = arg_lit0("v", "verbose", "verbose output"); @@ -2498,7 +2527,7 @@ static int CmdHF15SlixDisable(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 15 slixprivacydisable", "Disable privacy mode on SLIX ISO-15693 tag", - "hf 15 slixdisable -p 0F0F0F0F"); + "hf 15 slixprivacydisable -p 0F0F0F0F"); void *argtable[] = { arg_param_begin, @@ -2548,7 +2577,7 @@ static int CmdHF15SlixEnable(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 15 slixprivacyenable", "Enable privacy mode on SLIX ISO-15693 tag", - "hf 15 slixenable -p 0F0F0F0F"); + "hf 15 slixprivacyenable -p 0F0F0F0F"); void *argtable[] = { arg_param_begin, @@ -2820,10 +2849,45 @@ static int CmdHF15EASPassProtect(const char *Cmd) { return resp.status; } +static int CmdHF15View(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 15 view", + "Print a ISO-15693 tag dump file (bin/eml/json)", + "hf 15 view -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" + ); + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "Specify a filename for dump file"), +// arg_lit0("z", "dense", "dense dump output style"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fnlen = 0; + char filename[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); +// bool dense_output = g_session.dense_output || arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + // read dump file + uint8_t *dump = NULL; + size_t bytes_read = CARD_MEMORY_SIZE; + int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, CARD_MEMORY_SIZE); + if (res != PM3_SUCCESS) { + return res; + } + + PrintAndLogEx(NORMAL, ""); + print_blocks_15693(dump, bytes_read, 4); + + free(dump); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { - {"-----------", CmdHF15Help, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdHF15Help, AlwaysAvailable, "This help"}, {"list", CmdHF15List, AlwaysAvailable, "List ISO-15693 history"}, + {"-----------", CmdHF15Help, AlwaysAvailable, "----------------------- " _CYAN_("general") " -----------------------"}, {"demod", CmdHF15Demod, AlwaysAvailable, "Demodulate ISO-15693 from tag"}, {"dump", CmdHF15Dump, IfPm3Iso15693, "Read all memory pages of an ISO-15693 tag, save to file"}, {"info", CmdHF15Info, IfPm3Iso15693, "Tag information"}, @@ -2834,10 +2898,14 @@ static command_t CommandTable[] = { {"reader", CmdHF15Reader, IfPm3Iso15693, "Act like an ISO-15693 reader"}, {"restore", CmdHF15Restore, IfPm3Iso15693, "Restore from file to all memory pages of an ISO-15693 tag"}, {"samples", CmdHF15Samples, IfPm3Iso15693, "Acquire samples as reader (enables carrier, sends inquiry)"}, + {"view", CmdHF15View, AlwaysAvailable, "Display content from tag dump file"}, + {"wrbl", CmdHF15Write, IfPm3Iso15693, "Write a block"}, + {"-----------", CmdHF15Help, IfPm3Iso15693, "--------------------- " _CYAN_("simulation") " ----------------------"}, + {"sim", CmdHF15Sim, IfPm3Iso15693, "Fake an ISO-15693 tag"}, {"eload", CmdHF15ELoad, IfPm3Iso15693, "Load image file into emulator to be used by 'sim' command"}, {"esave", CmdHF15ESave, IfPm3Iso15693, "Save emulator memory into image file"}, {"eview", CmdHF15EView, IfPm3Iso15693, "View emulator memory"}, - {"sim", CmdHF15Sim, IfPm3Iso15693, "Fake an ISO-15693 tag"}, + {"-----------", CmdHF15Help, IfPm3Iso15693, "------------------------ " _CYAN_("SLIX") " -------------------------"}, {"slixwritepwd", CmdHF15SlixWritePassword, IfPm3Iso15693, "Writes a password on a SLIX ISO-15693 tag"}, {"slixeasdisable", CmdHF15SlixEASDisable, IfPm3Iso15693, "Disable EAS mode on SLIX ISO-15693 tag"}, {"slixeasenable", CmdHF15SlixEASEnable, IfPm3Iso15693, "Enable EAS mode on SLIX ISO-15693 tag"}, @@ -2845,12 +2913,11 @@ static command_t CommandTable[] = { {"slixprivacyenable", CmdHF15SlixEnable, IfPm3Iso15693, "Enable privacy mode on SLIX ISO-15693 tag"}, {"passprotectafi", CmdHF15AFIPassProtect, IfPm3Iso15693, "Password protect AFI - Cannot be undone"}, {"passprotecteas", CmdHF15EASPassProtect, IfPm3Iso15693, "Password protect EAS - Cannot be undone"}, - {"wrbl", CmdHF15Write, IfPm3Iso15693, "Write a block"}, - {"-----------", CmdHF15Help, IfPm3Iso15693, "----------------------- " _CYAN_("afi") " -----------------------"}, + {"-----------", CmdHF15Help, IfPm3Iso15693, "-------------------------- " _CYAN_("afi") " ------------------------"}, {"findafi", CmdHF15FindAfi, IfPm3Iso15693, "Brute force AFI of an ISO-15693 tag"}, {"writeafi", CmdHF15WriteAfi, IfPm3Iso15693, "Writes the AFI on an ISO-15693 tag"}, {"writedsfid", CmdHF15WriteDsfid, IfPm3Iso15693, "Writes the DSFID on an ISO-15693 tag"}, - {"-----------", CmdHF15Help, IfPm3Iso15693, "----------------------- " _CYAN_("magic") " -----------------------"}, + {"-----------", CmdHF15Help, IfPm3Iso15693, "------------------------- " _CYAN_("magic") " -----------------------"}, {"csetuid", CmdHF15CSetUID, IfPm3Iso15693, "Set UID for magic card"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfcryptorf.c b/client/src/cmdhfcryptorf.c index 552c25590..1f2a18229 100644 --- a/client/src/cmdhfcryptorf.c +++ b/client/src/cmdhfcryptorf.c @@ -116,15 +116,16 @@ static bool get_14b_UID(iso14b_card_select_t *card) { SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { - if (resp.oldarg[0] == 0) { + if (resp.status == PM3_SUCCESS) { memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); return true; } } } // retry - if (retry <= 0) + if (retry <= 0) { PrintAndLogEx(FAILED, "command execution timeout"); + } return false; } @@ -148,22 +149,20 @@ static int infoHFCryptoRF(bool verbose) { return false; } - iso14b_card_select_t card; - memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - - uint64_t status = resp.oldarg[0]; - - switch (status) { - case 0: + switch (resp.status) { + case PM3_SUCCESS: { + iso14b_card_select_t card; + memcpy(&card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid); return PM3_SUCCESS; - case 2: + } + case PM3_ELENGTH: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); break; - case 3: + case PM3_ECRC: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); break; default: @@ -209,16 +208,16 @@ int readHFCryptoRF(bool loop, bool verbose) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - uint8_t status = resp.oldarg[0] & 0xFF; - if (loop) { - if (status != 0) { + if (resp.status != PM3_SUCCESS) { continue; } } else { // when not in continuous mode - if (status != 0) { - if (verbose) PrintAndLogEx(WARNING, "cryptoRF / ISO14443-b card select failed"); + if (resp.status != PM3_SUCCESS) { + if (verbose) { + PrintAndLogEx(WARNING, "cryptoRF / ISO14443-b card select failed"); + } res = PM3_EOPABORTED; break; } @@ -322,11 +321,9 @@ static int CmdHFCryptoRFDump(const char *Cmd) { PacketResponseNG resp; // select - int status; if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { - status = resp.oldarg[0]; - if (status < 0) { - PrintAndLogEx(FAILED, "failed to select %" PRId64 "]", resp.oldarg[0]); + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "failed to select %d]", resp.status); free(packet); return switch_off_field_cryptorf(); } @@ -350,13 +347,12 @@ static int CmdHFCryptoRFDump(const char *Cmd) { SendCommandNG(CMD_HF_ISO14443B_COMMAND, (uint8_t *)&packet, sizeof(iso14b_raw_cmd_t) + 2); if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { - status = resp.oldarg[0]; - if (status < 0) { + if (resp.status != PM3_SUCCESS) { PrintAndLogEx(FAILED, "retrying one more time"); continue; } - uint16_t len = (resp.oldarg[1] & 0xFFFF); + uint16_t len = resp.length; uint8_t *recv = resp.data.asBytes; if (check_crc(CRC_14443_B, recv, len) == false) { @@ -414,9 +410,8 @@ static int CmdHFCryptoRFDump(const char *Cmd) { FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); } - saveFileEML(filename, data, datalen, 4); - saveFile(filename, ".bin", data, datalen); - // json? + pm3_save_dump(filename, data, datalen, jsfCryptorf); + return switch_off_field_cryptorf(); } @@ -424,13 +419,13 @@ static int CmdHFCryptoRFELoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf cryptorf eload", - "Loads CryptoRF tag dump into emulator memory on device", + "Loads CryptoRF tag dump (bin/eml/json) into emulator memory on device", "hf cryptorf eload -f hf-cryptorf-0102030405-dump.bin\n" ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -447,7 +442,7 @@ static int CmdHFCryptoRFELoad(const char *Cmd) { size_t datalen = CRYPTORF_MEM_SIZE; // set up buffer uint8_t *data = calloc(datalen, sizeof(uint8_t)); - if (!data) { + if (data == NULL) { PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); return PM3_EMALLOC; } @@ -478,7 +473,7 @@ static int CmdHFCryptoRFELoad(const char *Cmd) { } */ free(data); - PrintAndLogEx(SUCCESS, "sent %d bytes of data to device emulator memory", bytes_sent); + PrintAndLogEx(SUCCESS, "sent " _YELLOW_("%d") " bytes of data to device emulator memory", bytes_sent); return PM3_SUCCESS; } @@ -486,14 +481,14 @@ static int CmdHFCryptoRFESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf cryptorf esave", - "Save emulator memory to bin/eml/json file\n" + "Save emulator memory to file (bin/json)\n" "if filename is not supplied, UID will be used.", "hf cryptorf esave\n" "hf cryptorf esave -f filename" ); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dumpfile"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -527,11 +522,7 @@ static int CmdHFCryptoRFESave(const char *Cmd) { FillFileNameByUID(fptr, data, "-dump", 4); } - saveFile(filename, ".bin", data, numofbytes); - //needs to change - saveFileEML(filename, data, numofbytes, 8); - //needs to change - saveFileJSON(filename, jsfRaw, data, numofbytes, NULL); + pm3_save_dump(filename, data, numofbytes, jsfCryptorf); free(data); return PM3_SUCCESS; } diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index c8edd2054..0933217cd 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -42,17 +42,15 @@ #define EMRTD_MAX_FILE_SIZE 35000 // ISO7816 commands -#define EMRTD_SELECT 0xA4 -#define EMRTD_EXTERNAL_AUTHENTICATE 0x82 -#define EMRTD_GET_CHALLENGE 0x84 -#define EMRTD_READ_BINARY 0xB0 -#define EMRTD_P1_SELECT_BY_EF 0x02 -#define EMRTD_P1_SELECT_BY_NAME 0x04 -#define EMRTD_P2_PROPRIETARY 0x0C +#define EMRTD_P1_SELECT_BY_EF 0x02 +#define EMRTD_P1_SELECT_BY_NAME 0x04 +#define EMRTD_P2_PROPRIETARY 0x0C // App IDs #define EMRTD_AID_MRTD {0xA0, 0x00, 0x00, 0x02, 0x47, 0x10, 0x01} +#define EMRTD_KMAC_LEN 16 + // DESKey Types static const uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; static const uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; @@ -124,7 +122,7 @@ static emrtd_hashalg_t hashalg_table[] = { {"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, {}} + {NULL, NULL, 0, 0, {0}} }; static emrtd_pacealg_t pacealg_table[] = { @@ -145,7 +143,7 @@ static emrtd_pacealg_t pacealg_table[] = { {"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, {}} + {NULL, NULL, {0}} }; static emrtd_pacesdp_t pacesdp_table[] = { @@ -383,25 +381,25 @@ static void _emrtd_convert_fileid(uint16_t file, uint8_t *dataout) { } static int emrtd_select_file_by_name(uint8_t namelen, uint8_t *name) { - return emrtd_exchange_commands_noout((sAPDU_t) {0, EMRTD_SELECT, EMRTD_P1_SELECT_BY_NAME, 0x0C, namelen, name}, false, true); + return emrtd_exchange_commands_noout((sAPDU_t) {0, ISO7816_SELECT_FILE, EMRTD_P1_SELECT_BY_NAME, 0x0C, namelen, name}, false, true); } static int emrtd_select_file_by_ef(uint16_t file_id) { uint8_t data[2]; _emrtd_convert_fileid(file_id, data); - return emrtd_exchange_commands_noout((sAPDU_t) {0, EMRTD_SELECT, EMRTD_P1_SELECT_BY_EF, 0x0C, sizeof(data), data}, false, true); + return emrtd_exchange_commands_noout((sAPDU_t) {0, ISO7816_SELECT_FILE, EMRTD_P1_SELECT_BY_EF, 0x0C, sizeof(data), data}, false, true); } static int emrtd_get_challenge(int length, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) { - return emrtd_exchange_commands((sAPDU_t) {0, EMRTD_GET_CHALLENGE, 0, 0, 0, NULL}, true, length, dataout, maxdataoutlen, dataoutlen, false, true); + return emrtd_exchange_commands((sAPDU_t) {0, ISO7816_GET_CHALLENGE, 0, 0, 0, NULL}, true, length, dataout, maxdataoutlen, dataoutlen, false, true); } static int emrtd_external_authenticate(uint8_t *data, int length, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) { - return emrtd_exchange_commands((sAPDU_t) {0, EMRTD_EXTERNAL_AUTHENTICATE, 0, 0, length, data}, true, length, dataout, maxdataoutlen, dataoutlen, false, true); + return emrtd_exchange_commands((sAPDU_t) {0, ISO7816_EXTERNAL_AUTHENTICATION, 0, 0, length, data}, true, length, dataout, maxdataoutlen, dataoutlen, false, true); } static int _emrtd_read_binary(int offset, int bytes_to_read, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) { - return emrtd_exchange_commands((sAPDU_t) {0, EMRTD_READ_BINARY, offset >> 8, offset & 0xFF, 0, NULL}, true, bytes_to_read, dataout, maxdataoutlen, dataoutlen, false, true); + return emrtd_exchange_commands((sAPDU_t) {0, ISO7816_READ_BINARY, offset >> 8, offset & 0xFF, 0, NULL}, true, bytes_to_read, dataout, maxdataoutlen, dataoutlen, false, true); } static void emrtd_bump_ssc(uint8_t *ssc) { @@ -420,8 +418,8 @@ static void emrtd_bump_ssc(uint8_t *ssc) { 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]; + uint8_t k[500] = { 0x00 }; + uint8_t cc[500] = { 0x00 }; emrtd_bump_ssc(ssc); @@ -453,16 +451,16 @@ static bool emrtd_check_cc(uint8_t *ssc, uint8_t *key, uint8_t *rapdu, int rapdu } static bool emrtd_secure_select_file_by_ef(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, uint16_t file) { - uint8_t response[PM3_CMD_DATA_SIZE]; + uint8_t response[PM3_CMD_DATA_SIZE] = { 0x00 }; size_t resplen = 0; // convert fileid to bytes - uint8_t file_id[2]; + uint8_t file_id[2] = { 0x00 }; _emrtd_convert_fileid(file, file_id); uint8_t iv[8] = { 0x00 }; - uint8_t cmd[8]; - uint8_t data[21]; + uint8_t cmd[8] = { 0x00 }; + uint8_t data[21] = { 0x00 }; uint8_t temp[8] = {0x0c, 0xa4, EMRTD_P1_SELECT_BY_EF, 0x0c}; int cmdlen = pad_block(temp, 4, cmd); @@ -503,7 +501,7 @@ static bool emrtd_secure_select_file_by_ef(uint8_t *kenc, uint8_t *kmac, uint8_t memcpy(data + (datalen + 3), do8e, 10); PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc)); - if (emrtd_exchange_commands((sAPDU_t) {0x0C, EMRTD_SELECT, EMRTD_P1_SELECT_BY_EF, 0x0C, lc, data}, true, 0, response, sizeof(response), &resplen, false, true) == false) { + if (emrtd_exchange_commands((sAPDU_t) {0x0C, ISO7816_SELECT_FILE, EMRTD_P1_SELECT_BY_EF, 0x0C, lc, data}, true, 0, response, sizeof(response), &resplen, false, true) == false) { return false; } @@ -511,11 +509,11 @@ static bool emrtd_secure_select_file_by_ef(uint8_t *kenc, uint8_t *kmac, uint8_t } static bool _emrtd_secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, size_t maxdataoutlen, size_t *dataoutlen) { - uint8_t cmd[8]; - uint8_t data[21]; + uint8_t cmd[8] = { 0x00 }; + uint8_t data[21] = { 0x00 }; uint8_t temp[8] = {0x0c, 0xb0}; - PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 20)); + PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, EMRTD_KMAC_LEN)); // Set p1 and p2 temp[2] = (uint8_t)(offset >> 8); @@ -526,24 +524,24 @@ static bool _emrtd_secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, i uint8_t do97[3] = {0x97, 0x01, bytes_to_read}; - uint8_t m[11]; + uint8_t m[11] = { 0x00 }; memcpy(m, cmd, 8); memcpy(m + 8, do97, 3); emrtd_bump_ssc(ssc); - uint8_t n[19]; + uint8_t n[19] = { 0x00 }; memcpy(n, ssc, 8); memcpy(n + 8, m, 11); - PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, 19)); + PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, sizeof(n))); - uint8_t cc[8]; + uint8_t cc[8] = { 0x00 }; retail_mac(kmac, n, 19, cc); - PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8)); + PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, sizeof(cc))); uint8_t do8e[10] = {0x8E, 0x08}; memcpy(do8e + 2, cc, 8); - PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, 10)); + PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, sizeof(do8e))); int lc = 13; PrintAndLogEx(DEBUG, "lc: %i", lc); @@ -552,7 +550,7 @@ static bool _emrtd_secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, i memcpy(data + 3, do8e, 10); PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc)); - if (emrtd_exchange_commands((sAPDU_t) {0x0C, EMRTD_READ_BINARY, offset >> 8, offset & 0xFF, lc, data}, true, 0, dataout, maxdataoutlen, dataoutlen, false, true) == false) { + if (emrtd_exchange_commands((sAPDU_t) {0x0C, ISO7816_READ_BINARY, offset >> 8, offset & 0xFF, lc, data}, true, 0, dataout, maxdataoutlen, dataoutlen, false, true) == false) { return false; } @@ -560,8 +558,8 @@ static bool _emrtd_secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, i } 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, size_t *dataoutlen) { - uint8_t response[500]; - uint8_t temp[500]; + uint8_t response[500] = { 0x00 }; + uint8_t temp[500] = { 0x00 }; size_t resplen, cutat = 0; uint8_t iv[8] = { 0x00 }; @@ -582,9 +580,9 @@ static bool _emrtd_secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint } static int emrtd_read_file(uint8_t *dataout, size_t *dataoutlen, uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, bool use_secure) { - uint8_t response[EMRTD_MAX_FILE_SIZE]; + uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; size_t resplen = 0; - uint8_t tempresponse[500]; + uint8_t tempresponse[500] = { 0x00 }; size_t tempresplen = 0; int toread = 4; int offset = 0; @@ -751,7 +749,7 @@ static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const c } static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const char *path) { - uint8_t data[EMRTD_MAX_FILE_SIZE]; + uint8_t data[EMRTD_MAX_FILE_SIZE] = { 0x00 }; size_t datalen = 0; // If we can't find image in EF_DG5, return false. @@ -761,8 +759,9 @@ static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const c if (datalen < EMRTD_MAX_FILE_SIZE) { char *filepath = calloc(strlen(path) + 100, sizeof(char)); - if (filepath == NULL) + if (filepath == NULL) { return PM3_EMALLOC; + } strcpy(filepath, path); strncat(filepath, PATHSEP, 2); strcat(filepath, dg_table[EF_DG5].filename); @@ -778,7 +777,7 @@ static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const c } static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path) { - uint8_t data[EMRTD_MAX_FILE_SIZE]; + uint8_t data[EMRTD_MAX_FILE_SIZE] = { 0x00 }; size_t datalen = 0; // If we can't find image in EF_DG7, return false. @@ -788,8 +787,9 @@ static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const c if (datalen < EMRTD_MAX_FILE_SIZE) { char *filepath = calloc(strlen(path) + 100, sizeof(char)); - if (filepath == NULL) + if (filepath == NULL) { return PM3_EMALLOC; + } strcpy(filepath, path); strncat(filepath, PATHSEP, 2); strcat(filepath, dg_table[EF_DG7].filename); @@ -814,8 +814,9 @@ static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const c } char *filepath = calloc(strlen(path) + 100, sizeof(char)); - if (filepath == NULL) + if (filepath == NULL) { return PM3_EMALLOC; + } strcpy(filepath, path); strncat(filepath, PATHSEP, 2); @@ -827,7 +828,7 @@ static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const c } static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, uint16_t file, const char *name, bool use_secure, const char *path) { - uint8_t response[EMRTD_MAX_FILE_SIZE]; + uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; size_t resplen = 0; if (emrtd_select_and_read(response, &resplen, file, ks_enc, ks_mac, ssc, use_secure) == false) { @@ -835,8 +836,9 @@ static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, uint } char *filepath = calloc(strlen(path) + 100, sizeof(char)); - if (filepath == NULL) + if (filepath == NULL) { return false; + } strcpy(filepath, path); strncat(filepath, PATHSEP, 2); @@ -1021,8 +1023,8 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; size_t resplen = 0; uint8_t ssc[8] = { 0x00 }; - uint8_t ks_enc[16] = { 0x00 }; - uint8_t ks_mac[16] = { 0x00 }; + uint8_t ks_enc[EMRTD_KMAC_LEN] = { 0x00 }; + uint8_t ks_mac[EMRTD_KMAC_LEN] = { 0x00 }; bool BAC = false; // Select the eMRTD @@ -1052,8 +1054,9 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab char *filepath = calloc(strlen(path) + 100, sizeof(char)); - if (filepath == NULL) + if (filepath == NULL) { return PM3_EMALLOC; + } strcpy(filepath, path); strncat(filepath, PATHSEP, 2); @@ -1281,18 +1284,40 @@ static void emrtd_print_issuance(char *data, bool ascii) { PrintAndLogEx(SUCCESS, "Date of issue.........: " _YELLOW_("%s"), final_date); } -static void emrtd_print_personalization_timestamp(uint8_t *data) { +static void emrtd_print_personalization_timestamp(uint8_t *data, size_t datalen) { + if (datalen < 7) { + return; + } + char str_date[0x0F] = { 0x00 }; strncpy(str_date, sprint_hex_inrow(data, 0x07), sizeof(str_date) - 1); + char final_date[20] = { 0x00 }; - snprintf(final_date, sizeof(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); + snprintf(final_date, sizeof(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) { +static void emrtd_print_unknown_timestamp_5f85(uint8_t *data, size_t datalen) { + if (datalen < 14) { + return; + } char final_date[20] = { 0x00 }; - snprintf(final_date, sizeof(final_date), "%.4s-%.2s-%.2s %.2s:%.2s:%.2s", data, data + 4, data + 6, data + 8, data + 10, data + 12); + snprintf(final_date, sizeof(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."); @@ -1587,13 +1612,13 @@ static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { saveFile("BackOfDocument", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen); break; case 0x55: - emrtd_print_personalization_timestamp(tagdata); + emrtd_print_personalization_timestamp(tagdata, tagdatalen); break; case 0x56: PrintAndLogEx(SUCCESS, "Serial of Personalization System: " _YELLOW_("%.*s"), (int)tagdatalen, tagdata); break; case 0x85: - emrtd_print_unknown_timestamp_5f85(tagdata); + emrtd_print_unknown_timestamp_5f85(tagdata, tagdatalen); break; default: PrintAndLogEx(SUCCESS, "Unknown Field %02X%02X....: %s", taglist[i], taglist[i + 1], sprint_hex_inrow(tagdata, tagdatalen)); @@ -2086,13 +2111,13 @@ static int CmdHFeMRTDDump(const char *Cmd) { "Dump all files on an eMRTD", "hf emrtd dump\n" "hf emrtd dump --dir ../dump\n" - "hf emrtd dump -n 123456789 -d 19890101 -e 20250401" + "hf emrtd dump -n 123456789 -d 890101 -e 250401" ); 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("n", "doc", "", "document number, up to 9 chars"), + arg_str0("d", "date", "", "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, "dir", "", "save dump to the given dirpath"), @@ -2186,14 +2211,14 @@ static int CmdHFeMRTDInfo(const char *Cmd) { "Display info about an eMRTD", "hf emrtd info\n" "hf emrtd info --dir ../dumps\n" - "hf emrtd info -n 123456789 -d 19890101 -e 20250401\n" - "hf emrtd info -n 123456789 -d 19890101 -e 20250401 -i" + "hf emrtd info -n 123456789 -d 890101 -e 250401\n" + "hf emrtd info -n 123456789 -d 890101 -e 250401 -i" ); 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("n", "doc", "", "document number, up to 9 chars"), + arg_str0("d", "date", "", "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, "dir", "", "display info from offline dump stored in dirpath"), diff --git a/client/src/cmdhfepa.c b/client/src/cmdhfepa.c index 66ce37d6b..4c6c67f72 100644 --- a/client/src/cmdhfepa.c +++ b/client/src/cmdhfepa.c @@ -148,7 +148,7 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { // Proxmark response PacketResponseNG resp; - // transfer the APDUs to the Proxmark + // transfer the APDUs to the Proxmark3 uint8_t data[PM3_CMD_DATA_SIZE]; // fast push mode g_conn.block_after_ACK = true; @@ -172,7 +172,7 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { clearCommandBuffer(); // arg0: APDU number // arg1: offset into the APDU - SendCommandOLD(CMD_HF_EPA_REPLAY, i + 1, j * sizeof(data), packet_length, data, packet_length); + SendCommandMIX(CMD_HF_EPA_REPLAY, i + 1, j * sizeof(data), packet_length, data, packet_length); if (WaitForResponseTimeout(CMD_HF_EPA_REPLAY, &resp, 2500) == false) { PrintAndLogEx(WARNING, "command time out"); return PM3_ETIMEOUT; diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index 191a481de..5ba3e8116 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -566,7 +566,7 @@ static void reverse_3des_key(const uint8_t *master_key, int length, uint8_t *rev for (int i = 0; i < length; i++) { reverse_master_key[i] = master_key[(length - 1) - i]; } -}; +} /** * Command parser for auth1 @@ -1783,7 +1783,7 @@ static int CmdHFFelicaSniff(const char *Cmd) { PrintAndLogEx(INFO, "Sniff Felica, getting first %" PRIu32 " frames, skipping after %" PRIu32 " triggers", payload.samples, payload.triggers); - PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort sniffing"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to abort sniffing"); clearCommandBuffer(); SendCommandNG(CMD_HF_FELICA_SNIFF, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; @@ -1830,7 +1830,7 @@ static int CmdHFFelicaSimLite(const char *Cmd) { CLIParserFree(ctx); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to abort simulation"); clearCommandBuffer(); SendCommandNG(CMD_HF_FELICALITE_SIMULATE, payload.uid, sizeof(payload)); @@ -2035,7 +2035,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { SendCommandNG(CMD_HF_FELICALITE_DUMP, NULL, 0); PacketResponseNG resp; - PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort dumping"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to abort dumping"); uint8_t timeout = 0; while (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { diff --git a/client/src/cmdhffido.c b/client/src/cmdhffido.c index 64cc8a43f..d3ab66fc6 100644 --- a/client/src/cmdhffido.c +++ b/client/src/cmdhffido.c @@ -78,33 +78,36 @@ static int CmdHFFidoInfo(const char *Cmd) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; - int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw); + int res = FIDOSelect(true, true, buf, sizeof(buf), &len, &sw); if (res) { DropField(); return res; } if (sw != ISO7816_OK) { - if (sw) - PrintAndLogEx(INFO, "Not a FIDO card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - else - PrintAndLogEx(ERR, "APDU exchange error. Card returns 0x0000."); - + if (sw) { + PrintAndLogEx(INFO, "Not a FIDO card. APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + } else { + PrintAndLogEx(ERR, "APDU exchange error. Card returns 0x0000"); + } DropField(); return PM3_SUCCESS; } - if (!strncmp((char *)buf, "U2F_V2", 7)) { - if (!strncmp((char *)buf, "FIDO_2_0", 8)) { - PrintAndLogEx(INFO, "FIDO2 authenticator detected. Version: %.*s", (int)len, buf); + if (strncmp((char *)buf, "U2F_V2", 7) == 0) { + if (strncmp((char *)buf, "FIDO_2_0", 8) == 0) { + PrintAndLogEx(INFO, "FIDO2 authenticator"); + PrintAndLogEx(INFO, "Version... " _YELLOW_("%.*s"), (int)len, buf); } else { - PrintAndLogEx(INFO, "FIDO authenticator detected (not standard U2F)."); - PrintAndLogEx(INFO, "Non U2F authenticator version:"); + PrintAndLogEx(INFO, "FIDO authenticator (not standard U2F)"); + PrintAndLogEx(INFO, "Non U2F authenticator"); + PrintAndLogEx(INFO, "version... "); print_buffer((const unsigned char *)buf, len, 1); } } else { - PrintAndLogEx(INFO, "FIDO U2F authenticator detected. Version: %.*s", (int)len, buf); + PrintAndLogEx(INFO, "FIDO U2F authenticator detected"); + PrintAndLogEx(INFO, "Version... " _YELLOW_("%.*s"), (int)len, buf); } res = FIDO2GetInfo(buf, sizeof(buf), &len, &sw); @@ -112,6 +115,7 @@ static int CmdHFFidoInfo(const char *Cmd) { if (res) { return res; } + if (sw != ISO7816_OK) { PrintAndLogEx(ERR, "FIDO2 version doesn't exist (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); return PM3_SUCCESS; @@ -409,8 +413,8 @@ static int CmdHFFidoAuthenticate(const char *cmd) { "hf fido auth --kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n" "hf fido auth \n" "--kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f\n" - "--cp 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f \n" - "--ap 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"); + "--cpx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f \n" + "--apx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"); void *argtable[] = { arg_param_begin, @@ -441,7 +445,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { controlByte = 0x07; uint8_t data[512] = {0}; - uint8_t hdata[250] = {0}; + uint8_t hdata[256] = {0}; bool public_key_loaded = false; uint8_t public_key[65] = {0}; int hdatalen = 0; diff --git a/client/src/cmdhffudan.c b/client/src/cmdhffudan.c index 502425f83..b0dc8b08d 100644 --- a/client/src/cmdhffudan.c +++ b/client/src/cmdhffudan.c @@ -110,7 +110,7 @@ static int fudan_get_type(iso14a_card_select_t *card, bool verbose) { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { - PrintAndLogEx(DEBUG, "iso14443a card select failed"); + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); return PM3_ESOFT; } @@ -235,14 +235,14 @@ static int CmdHFFudanReader(const char *Cmd) { static int CmdHFFudanDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf fudan dump", - "Dump FUDAN tag to binary file\n" + "Dump FUDAN tag to file (bin/json)\n" "If no given, UID will be used as filename", "hf fudan dump -f mydump --> dump using filename\n" ); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -304,7 +304,7 @@ static int CmdHFFudanDump(const char *Cmd) { clearCommandBuffer(); PacketResponseNG resp; - SendCommandOLD(CMD_HF_ISO14443A_READER, flags, sizeof(cmd) | ((uint32_t)(numbits << 16)), argtimeout, cmd, sizeof(cmd)); + SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(cmd) | ((uint32_t)(numbits << 16)), argtimeout, cmd, sizeof(cmd)); if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { if (resp.status == PM3_SUCCESS) { @@ -339,14 +339,7 @@ static int CmdHFFudanDump(const char *Cmd) { free(fptr); } - saveFile(dataFilename, ".bin", (uint8_t *)carddata, sizeof(carddata)); - saveFileEML(dataFilename, (uint8_t *)carddata, sizeof(carddata), MAX_FUDAN_BLOCK_SIZE); - - iso14a_mf_extdump_t xdump; - xdump.card_info = card; - xdump.dump = (uint8_t *)carddata; - xdump.dumplen = sizeof(carddata); - saveFileJSON(dataFilename, jsfFudan, (uint8_t *)&xdump, sizeof(xdump), NULL); + pm3_save_dump(dataFilename, (uint8_t *)carddata, sizeof(carddata), jsfFudan); return PM3_SUCCESS; } @@ -355,7 +348,7 @@ static int CmdHFFudanWrBl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf fudan wrbl", "Write fudan block with 4 hex bytes of data\n", - "hf mf wrbl --blk 1 -k FFFFFFFFFFFF -d 01020304" + "hf fudan wrbl --blk 1 -k FFFFFFFFFFFF -d 01020304" ); void *argtable[] = { arg_param_begin, @@ -460,7 +453,7 @@ static int CmdHFFudanView(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index ba6d984ad..340af551a 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -19,8 +19,8 @@ #include "cmdhficlass.h" #include #include "cliparser.h" -#include "cmdparser.h" // command_t -#include "commonutil.h" // ARRAYLEN +#include "cmdparser.h" // command_t +#include "commonutil.h" // ARRAYLEN #include "cmdtrace.h" #include "util_posix.h" #include "comms.h" @@ -34,17 +34,18 @@ #include "cardhelper.h" #include "wiegand_formats.h" #include "wiegand_formatutils.h" -#include "cmdsmartcard.h" // smart select fct +#include "cmdsmartcard.h" // smart select fct #include "proxendian.h" #include "iclass_cmd.h" -#include "crypto/asn1utils.h" // ASN1 decoder +#include "crypto/asn1utils.h" // ASN1 decoder #include "preferences.h" -#define PICOPASS_BLOCK_SIZE 8 #define NUM_CSNS 9 +#define MAC_ITEM_SIZE 24 // csn(8) + epurse(8) + nr(4) + mac(4) = 24 bytes #define ICLASS_KEYS_MAX 8 #define ICLASS_AUTH_RETRY 10 +#define ICLASS_CFG_BLK_SR_BIT 0xA0 // indicates SIO present when set in block6[0] (legacy tags) #define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin" static void print_picopass_info(const picopass_hdr_t *hdr); @@ -55,13 +56,13 @@ static void iclass_set_last_known_card(picopass_hdr_t *card) { memcpy(&iclass_last_known_card, card, sizeof(picopass_hdr_t)); } -static uint8_t empty[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -static uint8_t zeros[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static uint8_t empty[PICOPASS_BLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +static uint8_t zeros[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static int CmdHelp(const char *Cmd); -static void printIclassSIO(uint8_t *iclass_dump); +static void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len); -static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = { +static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][PICOPASS_BLOCK_SIZE] = { { 0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78 }, { 0xFD, 0xCB, 0x5A, 0x52, 0xEA, 0x8F, 0x30, 0x90 }, { 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87 }, @@ -130,7 +131,7 @@ static inline uint32_t leadingzeros(uint64_t a) { #endif } -static void iclass_upload_emul(uint8_t *d, uint16_t n, uint16_t *bytes_sent) { +static void iclass_upload_emul(uint8_t *d, uint16_t n, uint16_t offset, uint16_t *bytes_sent) { struct p { uint16_t offset; @@ -145,25 +146,32 @@ static void iclass_upload_emul(uint8_t *d, uint16_t n, uint16_t *bytes_sent) { *bytes_sent = 0; uint16_t bytes_remaining = n; + PrintAndLogEx(INFO, "Uploading to emulator memory"); + PrintAndLogEx(INFO, "." NOLF); + while (bytes_remaining > 0) { uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE - 4, bytes_remaining); if (bytes_in_packet == bytes_remaining) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } - clearCommandBuffer(); struct p *payload = calloc(4 + bytes_in_packet, sizeof(uint8_t)); - payload->offset = *bytes_sent; + payload->offset = offset + *bytes_sent; payload->len = bytes_in_packet; memcpy(payload->data, d + *bytes_sent, bytes_in_packet); + clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_EML_MEMSET, (uint8_t *)payload, 4 + bytes_in_packet); free(payload); bytes_remaining -= bytes_in_packet; *bytes_sent += bytes_in_packet; + + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); } + PrintAndLogEx(NORMAL, ""); } static const char *card_types[] = { @@ -188,22 +196,20 @@ static uint8_t card_app2_limit[] = { 0xff, }; -static iclass_config_card_item_t iclass_config_types[14] = { - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - {"", ""}, - // must be the last entry - {"no config card info available", ""} +static iclass_config_card_item_t iclass_config_types[13] = { + {"Audio/Visual #1 - Beep ON, LED Off, Flash GREEN on read", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x00, 0xA8, 0x8F, 0xA7, 0x80, 0xA9, 0x01}}, + {"Audio/Visual #2 - Beep ON, LED RED, Host must flash GREEN", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x18, 0xAC, 0x00, 0xA8, 0x1F, 0xA7, 0x80, 0xA9, 0x01}}, + {"Audio/Visual #3 - Beep ON, LED Off, Host must flash RED and/or GREEN", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x00, 0xA8, 0x0F, 0xA9, 0x03, 0xA7, 0x80}}, + {"Keypad Output #1 - Buffer ONE key (8 bit Dorado)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"Keypad Output #2 - Buffer ONE to FIVE keys (standard 26 bit)", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAE, 0x0B, 0xAF, 0xFF, 0xAD, 0x15, 0xB3, 0x03}}, + {"Keypad Output #3 - Local PIN verify", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAD, 0x6D, 0xB3, 0x03, 0x00, 0x00, 0x00, 0x00}}, + {"Mifare CSN #1 - 32 bit reverse output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x01, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, + {"Mifare CSN #2 - 16 bit output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x02, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, + {"Mifare CSN #3 - 34 bit output", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x18, 0xAC, 0x03, 0xA7, 0x80, 0xA8, 0x9F, 0xA9, 0x01}}, + {"Keyroll DISABLE - Set ELITE Key and DISABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {"Keyroll ENABLE - Set ELITE Key and ENABLE Keyrolling", {0x0C, 0x00, 0x00, 0x01, 0x00, 0x00, 0xBF, 0x18, 0xBF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}}, + {"Reset READER - Reset READER to defaults", {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {"Reset ENROLLER - Reset ENROLLER to defaults", {0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF}} }; static bool check_config_card(const iclass_config_card_item_t *o) { @@ -252,7 +258,7 @@ static const iclass_config_card_item_t *get_config_card_item(int idx) { static void print_config_cards(void) { if (check_config_card(&iclass_config_types[0])) { PrintAndLogEx(INFO, "---- " _CYAN_("Config cards available") " ------------"); - for (int i = 0; i < ARRAYLEN(iclass_config_types) - 1 ; ++i) { + for (int i = 0; i < ARRAYLEN(iclass_config_types) ; ++i) { PrintAndLogEx(INFO, "%2d, %s", i, iclass_config_types[i].desc); } PrintAndLogEx(NORMAL, ""); @@ -266,6 +272,16 @@ static void print_config_card(const iclass_config_card_item_t *o) { } } +static void iclass_encrypt_block_data(uint8_t *blk_data, uint8_t *key) { + uint8_t encrypted_data[16]; + uint8_t *encrypted = encrypted_data; + mbedtls_des3_context ctx; + mbedtls_des3_set2key_enc(&ctx, key); + mbedtls_des3_crypt_ecb(&ctx, blk_data, encrypted); + memcpy(blk_data, encrypted, 8); + mbedtls_des3_free(&ctx); +} + static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *key, bool got_kr) { if (check_config_card(o) == false) { return PM3_EINVARG; @@ -345,11 +361,37 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke SetFlushAfterWrite(true); // KEYROLL need to encrypt + uint8_t key_en[16] = {0}; + uint8_t *keyptr_en = NULL; + if (IsCardHelperPresent(false) == false) { + size_t keylen = 0; + int res_key = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr_en, &keylen); + if (res_key != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to find iclass_decryptionkey.bin"); + free(data); + return PM3_EINVARG; + } + + if (keylen != 16) { + PrintAndLogEx(ERR, "Failed to load transport key from file"); + free(keyptr_en); + free(data); + return PM3_EINVARG; + } + memcpy(key_en, keyptr_en, sizeof(key_en)); + free(keyptr_en); + } + PrintAndLogEx(INFO, "Setting up encryption... " NOLF); uint8_t ffs[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - if (Encrypt(ffs, ffs) == false) { - PrintAndLogEx(WARNING, "failed to encrypt FF"); + if (IsCardHelperPresent(false) != false) { + if (Encrypt(ffs, ffs) == false) { + PrintAndLogEx(WARNING, "failed to encrypt FF"); + } else { + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + } } else { + iclass_encrypt_block_data(ffs, key_en); PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); } @@ -358,9 +400,14 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke uint8_t lkey[8]; memcpy(lkey, key, sizeof(lkey)); uint8_t enckey1[8]; - if (Encrypt(lkey, enckey1) == false) { - PrintAndLogEx(WARNING, "failed to encrypt key1"); + if (IsCardHelperPresent(false) != false) { + if (Encrypt(lkey, enckey1) == false) { + PrintAndLogEx(WARNING, "failed to encrypt key1"); + } else { + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + } } else { + iclass_encrypt_block_data(lkey, key_en); PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); } @@ -369,9 +416,13 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke memcpy(data + (6 * 8), o->data, sizeof(o->data)); // encrypted keyroll key 0D - memcpy(data + (0xD * 8), enckey1, sizeof(enckey1)); + if (IsCardHelperPresent(false) != false) { + memcpy(data + (0x0D * 8), enckey1, sizeof(enckey1)); + } else { + memcpy(data + (0x0D * 8), lkey, sizeof(enckey1)); + } // encrypted 0xFF - for (uint8_t i = 0xD; i < 0x14; i++) { + for (uint8_t i = 0x0E; i < 0x14; i++) { memcpy(data + (i * 8), ffs, sizeof(ffs)); } PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); @@ -379,30 +430,44 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke // encrypted partial keyroll key 14 PrintAndLogEx(INFO, "Setting encrypted partial key14... " NOLF); uint8_t foo[8] = {0x15}; - memcpy(foo + 1, lkey, 7); + memcpy(foo + 1, key, 7); uint8_t enckey2[8]; - if (Encrypt(foo, enckey2) == false) { - PrintAndLogEx(WARNING, "failed to encrypt partial 1"); + if (IsCardHelperPresent(false) != false) { + if (Encrypt(foo, enckey2) == false) { + PrintAndLogEx(WARNING, "failed to encrypt partial 1"); + } else { + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + memcpy(data + (0x14 * 8), enckey2, sizeof(enckey2)); + } + } else { + iclass_encrypt_block_data(foo, key_en); + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + memcpy(data + (0x14 * 8), foo, sizeof(enckey2)); } - memcpy(data + (0x14 * 8), enckey2, sizeof(enckey2)); - PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); - // encrypted partial keyroll key 15 PrintAndLogEx(INFO, "Setting encrypted partial key15... " NOLF); memset(foo, 0xFF, sizeof(foo)); - foo[0] = lkey[7]; - if (Encrypt(foo, enckey2) == false) { - PrintAndLogEx(WARNING, "failed to encrypt partial 2"); + foo[0] = key[7]; + if (IsCardHelperPresent(false) != false) { + if (Encrypt(foo, enckey2) == false) { + PrintAndLogEx(WARNING, "failed to encrypt partial 2"); + } else { + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + memcpy(data + (0x15 * 8), enckey2, sizeof(enckey2)); + } + } else { + iclass_encrypt_block_data(foo, key_en); + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + memcpy(data + (0x15 * 8), foo, sizeof(enckey2)); } - memcpy(data + (0x15 * 8), enckey2, sizeof(enckey2)); - PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); // encrypted 0xFF PrintAndLogEx(INFO, "Setting 0xFF's... " NOLF); - for (uint8_t i = 0x16; i <= app1_limit; i++) { + for (uint16_t i = 0x16; i < (app1_limit + 1); i++) { memcpy(data + (i * 8), ffs, sizeof(ffs)); } + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); // revert potential modified app1_limit @@ -417,7 +482,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke //Send to device PrintAndLogEx(INFO, "Uploading to device... "); uint16_t bytes_sent = 0; - iclass_upload_emul(data, tot_bytes, &bytes_sent); + iclass_upload_emul(data, tot_bytes, 0, &bytes_sent); free(data); PrintAndLogEx(NORMAL, ""); @@ -443,14 +508,14 @@ static void fuse_config(const picopass_hdr_t *hdr) { uint16_t otp = (hdr->conf.otp[1] << 8 | hdr->conf.otp[0]); - PrintAndLogEx(INFO, " Raw: " _YELLOW_("%s"), sprint_hex((uint8_t *)&hdr->conf, 8)); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "..................... app limit", hdr->conf.app_limit); - PrintAndLogEx(INFO, " " _YELLOW_("%04X") " ( %5u )...... OTP", otp, otp); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "............ block write lock", hdr->conf.block_writelock); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "......... chip", hdr->conf.chip_config); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "...... mem", hdr->conf.mem_config); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "... EAS", hdr->conf.eas); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") " fuses", hdr->conf.fuses); + PrintAndLogEx(INFO, " Raw... " _YELLOW_("%s"), sprint_hex((uint8_t *)&hdr->conf, 8)); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") " ( %3u )............. app limit", hdr->conf.app_limit, hdr->conf.app_limit); + PrintAndLogEx(INFO, " " _YELLOW_("%04X") " ( %5u )...... OTP", otp, otp); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "............ block write lock", hdr->conf.block_writelock); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "......... chip", hdr->conf.chip_config); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "...... mem", hdr->conf.mem_config); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "... EAS", hdr->conf.eas); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") " fuses", hdr->conf.fuses); uint8_t fuses = hdr->conf.fuses; @@ -594,51 +659,51 @@ static void mem_app_config(const picopass_hdr_t *hdr) { PrintAndLogEx(INFO, " * Kd, Debit key, AA1 Kc, Credit key, AA2 *"); uint8_t keyAccess = isset(mem, 0x01); if (keyAccess) { - PrintAndLogEx(INFO, " Read AA1....... debit"); - PrintAndLogEx(INFO, " Write AA1...... debit"); - PrintAndLogEx(INFO, " Read AA2....... credit"); - PrintAndLogEx(INFO, " Write AA2...... credit"); + PrintAndLogEx(INFO, " Read AA1..... debit"); + PrintAndLogEx(INFO, " Write AA1.... debit"); + PrintAndLogEx(INFO, " Read AA2..... credit"); + PrintAndLogEx(INFO, " Write AA2.... credit"); PrintAndLogEx(INFO, " Debit........ debit or credit"); PrintAndLogEx(INFO, " Credit....... credit"); } else { - PrintAndLogEx(INFO, " Read AA1....... debit or credit"); - PrintAndLogEx(INFO, " Write AA1...... credit"); - PrintAndLogEx(INFO, " Read AA2....... debit or credit"); - PrintAndLogEx(INFO, " Write AA2...... credit"); + PrintAndLogEx(INFO, " Read AA1..... debit or credit"); + PrintAndLogEx(INFO, " Write AA1.... credit"); + PrintAndLogEx(INFO, " Read AA2..... debit or credit"); + PrintAndLogEx(INFO, " Write AA2.... credit"); PrintAndLogEx(INFO, " Debit........ debit or credit"); PrintAndLogEx(INFO, " Credit....... credit"); } } void print_picopass_info(const picopass_hdr_t *hdr) { - PrintAndLogEx(INFO, "-------------------- " _CYAN_("card configuration") " --------------------"); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("Card configuration") " --------------------"); fuse_config(hdr); mem_app_config(hdr); } void print_picopass_header(const picopass_hdr_t *hdr) { - PrintAndLogEx(INFO, "--------------------------- " _CYAN_("card") " ---------------------------"); - PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); - PrintAndLogEx(SUCCESS, " Config: %s Card configuration", sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf))); - PrintAndLogEx(SUCCESS, "E-purse: %s Card challenge, CC", sprint_hex(hdr->epurse, sizeof(hdr->epurse))); + PrintAndLogEx(INFO, "--------------------------- " _CYAN_("Card") " ---------------------------"); + PrintAndLogEx(SUCCESS, " CSN... " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); + PrintAndLogEx(SUCCESS, " Config... %s card configuration", sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf))); + PrintAndLogEx(SUCCESS, "E-purse... %s card challenge, CC", sprint_hex(hdr->epurse, sizeof(hdr->epurse))); if (memcmp(hdr->key_d, zeros, sizeof(zeros)) && memcmp(hdr->key_d, empty, sizeof(empty))) { - PrintAndLogEx(SUCCESS, " Kd: " _YELLOW_("%s") " debit key", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); + PrintAndLogEx(SUCCESS, " Kd... " _YELLOW_("%s") " debit key", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); } else { - PrintAndLogEx(SUCCESS, " Kd: %s debit key ( hidden )", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); + PrintAndLogEx(SUCCESS, " Kd... %s debit key ( hidden )", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); } if (memcmp(hdr->key_c, zeros, sizeof(zeros)) && memcmp(hdr->key_c, empty, sizeof(empty))) { - PrintAndLogEx(SUCCESS, " Kc: " _YELLOW_("%s") " credit key", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); + PrintAndLogEx(SUCCESS, " Kc... " _YELLOW_("%s") " credit key", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); } else { - PrintAndLogEx(SUCCESS, " Kc: %s credit key ( hidden )", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); + PrintAndLogEx(SUCCESS, " Kc... %s credit key ( hidden )", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); } - PrintAndLogEx(SUCCESS, " AIA: %s Application Issuer area", sprint_hex(hdr->app_issuer_area, sizeof(hdr->app_issuer_area))); + PrintAndLogEx(SUCCESS, " AIA... %s application issuer area", sprint_hex(hdr->app_issuer_area, sizeof(hdr->app_issuer_area))); } static int CmdHFiClassList(const char *Cmd) { - return CmdTraceListAlias(Cmd, "hf iclass", "iclass"); + return CmdTraceListAlias(Cmd, "hf iclass", "iclass -c"); } static int CmdHFiClassSniff(const char *Cmd) { @@ -683,8 +748,13 @@ static int CmdHFiClassSniff(const char *Cmd) { WaitForResponse(CMD_HF_ICLASS_SNIFF, &resp); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass list") "` to view captured tracelog"); PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save -f hf_iclass_mytrace") "` to save tracelog for later analysing"); + if (jam_epurse_update) { + PrintAndLogEx(HINT, "Verify if the jam worked by comparing value in trace and block 2"); + } + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -735,7 +805,7 @@ static int CmdHFiClassSim(const char *Cmd) { // remember to change the define NUM_CSNS to match. // pre-defined 9 CSN by iceman - uint8_t csns[8 * NUM_CSNS] = { + uint8_t csns[NUM_CSNS * PICOPASS_BLOCK_SIZE] = { 0x01, 0x0A, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, 0x0C, 0x06, 0x0C, 0xFE, 0xF7, 0xFF, 0x12, 0xE0, 0x10, 0x97, 0x83, 0x7B, 0xF7, 0xFF, 0x12, 0xE0, @@ -767,7 +837,7 @@ static int CmdHFiClassSim(const char *Cmd) { PrintAndLogEx(INFO, "press " _YELLOW_("`enter`") " to cancel"); PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, NUM_CSNS, 1, csns, 8 * NUM_CSNS); + SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, NUM_CSNS, 1, csns, NUM_CSNS * PICOPASS_BLOCK_SIZE); while (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { tries++; @@ -787,7 +857,7 @@ static int CmdHFiClassSim(const char *Cmd) { if (num_mac == 0) break; - size_t datalen = NUM_CSNS * 24; + size_t datalen = NUM_CSNS * MAC_ITEM_SIZE; uint8_t *dump = calloc(datalen, sizeof(uint8_t)); if (!dump) { PrintAndLogEx(WARNING, "Failed to allocate memory"); @@ -799,11 +869,11 @@ static int CmdHFiClassSim(const char *Cmd) { uint8_t i = 0; for (i = 0 ; i < NUM_CSNS ; i++) { //copy CSN - memcpy(dump + i * 24, csns + i * 8, 8); + memcpy(dump + (i * MAC_ITEM_SIZE), csns + i * 8, 8); //copy epurse - memcpy(dump + i * 24 + 8, resp.data.asBytes + i * 16, 8); + memcpy(dump + (i * MAC_ITEM_SIZE) + 8, resp.data.asBytes + i * 16, 8); // NR_MAC (eight bytes from the response) ( 8b csn + 8b epurse == 16) - memcpy(dump + i * 24 + 16, resp.data.asBytes + i * 16 + 8, 8); + memcpy(dump + (i * MAC_ITEM_SIZE) + 16, resp.data.asBytes + i * 16 + 8, 8); } /** Now, save to dumpfile **/ saveFile("iclass_mac_attack", ".bin", dump, datalen); @@ -818,7 +888,7 @@ static int CmdHFiClassSim(const char *Cmd) { PrintAndLogEx(INFO, "press Enter to cancel"); PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, NUM_CSNS, 1, csns, 8 * NUM_CSNS); + SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, NUM_CSNS, 1, csns, NUM_CSNS * PICOPASS_BLOCK_SIZE); while (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { tries++; @@ -838,25 +908,23 @@ static int CmdHFiClassSim(const char *Cmd) { if (num_mac == 0) break; - size_t datalen = NUM_CSNS * 24; + size_t datalen = NUM_CSNS * MAC_ITEM_SIZE; uint8_t *dump = calloc(datalen, sizeof(uint8_t)); if (!dump) { PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } -#define MAC_ITEM_SIZE 24 - //KEYROLL 1 //Need zeroes for the CC-field memset(dump, 0, datalen); for (uint8_t i = 0; i < NUM_CSNS ; i++) { // copy CSN - memcpy(dump + i * MAC_ITEM_SIZE, csns + i * 8, 8); //CSN + memcpy(dump + (i * MAC_ITEM_SIZE), csns + i * 8, 8); //CSN // copy EPURSE - memcpy(dump + i * MAC_ITEM_SIZE + 8, resp.data.asBytes + i * 16, 8); + memcpy(dump + (i * MAC_ITEM_SIZE) + 8, resp.data.asBytes + i * 16, 8); // copy NR_MAC (eight bytes from the response) ( 8b csn + 8b epurse == 16) - memcpy(dump + i * MAC_ITEM_SIZE + 16, resp.data.asBytes + i * 16 + 8, 8); + memcpy(dump + (i * MAC_ITEM_SIZE) + 16, resp.data.asBytes + i * 16 + 8, 8); } saveFile("iclass_mac_attack_keyroll_A", ".bin", dump, datalen); @@ -865,11 +933,11 @@ static int CmdHFiClassSim(const char *Cmd) { for (uint8_t i = 0; i < NUM_CSNS; i++) { uint8_t resp_index = (i + NUM_CSNS) * 16; // Copy CSN - memcpy(dump + i * MAC_ITEM_SIZE, csns + i * 8, 8); + memcpy(dump + (i * MAC_ITEM_SIZE), csns + i * 8, 8); // copy EPURSE - memcpy(dump + i * MAC_ITEM_SIZE + 8, resp.data.asBytes + resp_index, 8); + memcpy(dump + (i * MAC_ITEM_SIZE) + 8, resp.data.asBytes + resp_index, 8); // copy NR_MAC (eight bytes from the response) ( 8b csn + 8 epurse == 16) - memcpy(dump + i * MAC_ITEM_SIZE + 16, resp.data.asBytes + resp_index + 8, 8); + memcpy(dump + (i * MAC_ITEM_SIZE) + 16, resp.data.asBytes + resp_index + 8, 8); resp_index++; } saveFile("iclass_mac_attack_keyroll_B", ".bin", dump, datalen); @@ -997,15 +1065,16 @@ static int CmdHFiClassReader(const char *Cmd) { static int CmdHFiClassELoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass eload", - "Load emulator memory with data from (bin/eml/json) iCLASS dump file", - "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.eml\n" + "Load emulator memory with data from (bin/json) iCLASS dump file", + "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.json\n" "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin -m\n" ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_lit0("m", "mem", "use RDV4 spiffs"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -1021,6 +1090,7 @@ static int CmdHFiClassELoad(const char *Cmd) { } bool use_spiffs = arg_get_lit(ctx, 2); + bool verbose = arg_get_lit(ctx, 3); CLIParserFree(ctx); // use RDV4 spiffs @@ -1069,21 +1139,27 @@ static int CmdHFiClassELoad(const char *Cmd) { dump = newdump; } - print_picopass_header((picopass_hdr_t *) dump); - print_picopass_info((picopass_hdr_t *) dump); + if (verbose) { + print_picopass_header((picopass_hdr_t *) dump); + print_picopass_info((picopass_hdr_t *) dump); + } + + PrintAndLogEx(NORMAL, ""); //Send to device uint16_t bytes_sent = 0; - iclass_upload_emul(dump, bytes_read, &bytes_sent); + iclass_upload_emul(dump, bytes_read, 0, &bytes_sent); free(dump); - PrintAndLogEx(SUCCESS, "sent %u bytes of data to device emulator memory", bytes_sent); + PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", bytes_sent); + PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf iclass sim -h`")); + PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } static int CmdHFiClassESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass esave", - "Save emulator memory to file.\n" + "Save emulator memory to file (bin/json)\n" "if filename is not supplied, CSN will be used.", "hf iclass esave\n" "hf iclass esave -f hf-iclass-dump\n" @@ -1091,7 +1167,7 @@ static int CmdHFiClassESave(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump file"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_int0("s", "size", "<256|2048>", "number of bytes to save (default 256)"), arg_param_end }; @@ -1129,7 +1205,7 @@ static int CmdHFiClassESave(const char *Cmd) { FillFileNameByUID(fptr, dump, "-dump", 8); } - pm3_save_dump(filename, dump, bytes, jsfIclass, PICOPASS_BLOCK_SIZE); + pm3_save_dump(filename, dump, bytes, jsfIclass); free(dump); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); @@ -1195,39 +1271,156 @@ static int CmdHFiClassEView(const char *Cmd) { printIclassDumpContents(dump, 1, blocks, bytes, dense_output); if (verbose) { - printIclassSIO(dump); + print_iclass_sio(dump, bytes); } free(dump); return PM3_SUCCESS; } +static int CmdHFiClassESetBlk(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass esetblk", + "Sets an individual block in emulator memory.", + "hf iclass esetblk --blk 7 -d 0000000000000000"); + + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "blk", "", "block number"), + arg_str0("d", "data", "", "bytes to write, 8 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int blk = arg_get_int_def(ctx, 1, 0); + + if (blk > 255 || blk < 0) { + PrintAndLogEx(WARNING, "block number must be between 0 and 255. Got " _RED_("%i"), blk); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t data[PICOPASS_BLOCK_SIZE] = {0x00}; + int datalen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), data, sizeof(data), &datalen); + CLIParserFree(ctx); + + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + + if (datalen != PICOPASS_BLOCK_SIZE) { + PrintAndLogEx(WARNING, "block data must include 8 HEX bytes. Got " _RED_("%i"), datalen); + return PM3_EINVARG; + } + + uint16_t bytes_sent = 0; + iclass_upload_emul(data, sizeof(data), blk * PICOPASS_BLOCK_SIZE, &bytes_sent); + + return PM3_SUCCESS; +} + +static bool iclass_detect_new_pacs(uint8_t *d) { + uint8_t n = 0; + while (n++ < (PICOPASS_BLOCK_SIZE / 2)) { + if (d[n] && d[n + 1] == 0xA6) { + return true; + } + } + return false; +} + +// block 7 decoder for PACS +static int iclass_decode_credentials_new_pacs(uint8_t *d) { + + uint8_t offset = 0; + while (d[offset] == 0 && (offset < PICOPASS_BLOCK_SIZE / 2)) { + offset++; + } + + uint8_t pad = d[offset]; + + PrintAndLogEx(INFO, "%u , %u", offset, pad); + + char *binstr = (char *)calloc((PICOPASS_BLOCK_SIZE * 8) + 1, sizeof(uint8_t)); + if (binstr == NULL) { + return PM3_EMALLOC; + } + + uint8_t n = PICOPASS_BLOCK_SIZE - offset - 2; + bytes_2_binstr(binstr, d + offset + 2, n); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + offset + 2, n)); + PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + binstr[strlen(binstr) - pad] = '\0'; + PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + size_t hexlen = 0; + uint8_t hex[16] = {0}; + binstr_2_bytes(hex, &hexlen, binstr); + PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); + + uint32_t top = 0, mid = 0, bot = 0; + if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { + PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); + free(binstr); + return PM3_EINVARG; + } + + free(binstr); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Wiegand decode"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); + HIDTryUnpack(&packed); + + return PM3_SUCCESS; +} + static void iclass_decode_credentials(uint8_t *data) { - BLOCK79ENCRYPTION encryption = (data[(6 * 8) + 7] & 0x03); - bool has_values = (memcmp(data + (8 * 7), empty, 8) != 0) && (memcmp(data + (8 * 7), zeros, 8) != 0); + picopass_hdr_t *hdr = (picopass_hdr_t *)data; + if (memcmp(hdr->app_issuer_area, empty, PICOPASS_BLOCK_SIZE)) { + // Not a Legacy or SR card, nothing to do here. + return; + } + + BLOCK79ENCRYPTION encryption = (data[(6 * PICOPASS_BLOCK_SIZE) + 7] & 0x03); + + uint8_t *b7 = data + (PICOPASS_BLOCK_SIZE * 7); + + bool has_new_pacs = iclass_detect_new_pacs(b7); + bool has_values = (memcmp(b7, empty, PICOPASS_BLOCK_SIZE) != 0) && (memcmp(b7, zeros, PICOPASS_BLOCK_SIZE) != 0); if (has_values && encryption == None) { - //todo: remove preamble/sentinel - uint32_t top = 0, mid = 0, bot = 0; - + // todo: remove preamble/sentinel PrintAndLogEx(INFO, "Block 7 decoder"); - char hexstr[16 + 1] = {0}; - hex_to_buffer((uint8_t *)hexstr, data + (8 * 7), 8, sizeof(hexstr) - 1, 0, 0, true); - hexstring_to_u96(&top, &mid, &bot, hexstr); + if (has_new_pacs) { + iclass_decode_credentials_new_pacs(b7); + } else { + char hexstr[16 + 1] = {0}; + hex_to_buffer((uint8_t *)hexstr, b7, PICOPASS_BLOCK_SIZE, sizeof(hexstr) - 1, 0, 0, true); - char binstr[64 + 1]; - hextobinstring(binstr, hexstr); - char *pbin = binstr; - while (strlen(pbin) && *(++pbin) == '0'); + uint32_t top = 0, mid = 0, bot = 0; + hexstring_to_u96(&top, &mid, &bot, hexstr); - PrintAndLogEx(SUCCESS, "Binary..................... " _GREEN_("%s"), pbin); + char binstr[64 + 1]; + hextobinstring(binstr, hexstr); + char *pbin = binstr; + while (strlen(pbin) && *(++pbin) == '0'); + + PrintAndLogEx(SUCCESS, "Binary..................... " _GREEN_("%s"), pbin); + + PrintAndLogEx(INFO, "Wiegand decode"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); + HIDTryUnpack(&packed); + } - PrintAndLogEx(INFO, "Wiegand decode"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); } else { - PrintAndLogEx(INFO, "No credential found"); + PrintAndLogEx(INFO, "No unencrypted legacy credential found"); } } @@ -1240,7 +1433,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { "which is defined by the configuration block.\n" "\nOBS!\n" "In order to use this function, the file `iclass_decryptionkey.bin` must reside\n" - "in the resources directory. The file should be 16 bytes binary data\n" + "in the resources directory. The file must be 16 bytes binary data\n" "or...\n" "make sure your cardhelper is placed in the sim module", "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" @@ -1249,7 +1442,7 @@ static int CmdHFiClassDecrypt(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump file (bin/eml/json)"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_str0("d", "data", "", "3DES encrypted data"), arg_str0("k", "key", "", "3DES transport key"), arg_lit0("v", "verbose", "verbose output"), @@ -1429,12 +1622,12 @@ static int CmdHFiClassDecrypt(const char *Cmd) { strcat(fptr, "hf-iclass-"); FillFileNameByUID(fptr, hdr->csn, "-dump-decrypted", sizeof(hdr->csn)); - pm3_save_dump(fptr, decrypted, decryptedlen, jsfIclass, PICOPASS_BLOCK_SIZE); + pm3_save_dump(fptr, decrypted, decryptedlen, jsfIclass); printIclassDumpContents(decrypted, 1, (decryptedlen / 8), decryptedlen, dense_output); if (verbose) { - printIclassSIO(decrypted); + print_iclass_sio(decrypted, decryptedlen); } PrintAndLogEx(NORMAL, ""); @@ -1452,23 +1645,19 @@ static int CmdHFiClassDecrypt(const char *Cmd) { // decode block 9 has_values = (memcmp(decrypted + (8 * 9), empty, 8) != 0) && (memcmp(decrypted + (8 * 9), zeros, 8) != 0); - if (has_values) { - + if (has_values && use_sc) { uint8_t usr_blk_len = GetNumberBlocksForUserId(decrypted + (8 * 6)); if (usr_blk_len < 3) { - if (use_sc) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Block 9 decoder"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Block 9 decoder"); + uint8_t pinsize = GetPinSize(decrypted + (8 * 6)); + if (pinsize > 0) { - uint8_t pinsize = GetPinSize(decrypted + (8 * 6)); - if (pinsize > 0) { - - uint64_t pin = bytes_to_num(decrypted + (8 * 9), 5); - char tmp[17] = {0}; - snprintf(tmp, sizeof(tmp), "%."PRIu64, BCD2DEC(pin)); - PrintAndLogEx(INFO, "PIN........................ " _GREEN_("%.*s"), pinsize, tmp); - } + uint64_t pin = bytes_to_num(decrypted + (8 * 9), 5); + char tmp[17] = {0}; + snprintf(tmp, sizeof(tmp), "%."PRIu64, BCD2DEC(pin)); + PrintAndLogEx(INFO, "PIN........................ " _GREEN_("%.*s"), pinsize, tmp); } } } @@ -1482,16 +1671,6 @@ static int CmdHFiClassDecrypt(const char *Cmd) { return PM3_SUCCESS; } -static void iclass_encrypt_block_data(uint8_t *blk_data, uint8_t *key) { - uint8_t encrypted_data[16]; - uint8_t *encrypted = encrypted_data; - mbedtls_des3_context ctx; - mbedtls_des3_set2key_enc(&ctx, key); - mbedtls_des3_crypt_ecb(&ctx, blk_data, encrypted); - memcpy(blk_data, encrypted, 8); - mbedtls_des3_free(&ctx); -} - static int CmdHFiClassEncryptBlk(const char *Cmd) { CLIParserContext *clictx; CLIParserInit(&clictx, "hf iclass encrypt", @@ -1641,6 +1820,7 @@ static int CmdHFiClassDump(const char *Cmd) { arg_lit0("z", "dense", "dense dump output style"), arg_lit0(NULL, "force", "force unsecure card read"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), + arg_lit0(NULL, "ns", "no save to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1727,6 +1907,7 @@ static int CmdHFiClassDump(const char *Cmd) { bool dense_output = g_session.dense_output || arg_get_lit(ctx, 9); bool force = arg_get_lit(ctx, 10); bool shallow_mod = arg_get_lit(ctx, 11); + bool nosave = arg_get_lit(ctx, 12); CLIParserFree(ctx); @@ -1741,7 +1922,6 @@ static int CmdHFiClassDump(const char *Cmd) { uint8_t tag_data[0x100 * 8]; memset(tag_data, 0xFF, sizeof(tag_data)); - iclass_card_select_t payload_rdr = { .flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE) }; @@ -1887,17 +2067,15 @@ static int CmdHFiClassDump(const char *Cmd) { return PM3_ETIMEOUT; } - if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { - // all memory available - memcpy(tag_data + (8 * 3), tempbuf + (8 * 3), (blocks_read * 8)); - } else { + if (pagemap != PICOPASS_NON_SECURE_PAGEMODE) { // div key KD - memcpy(tag_data + (8 * 3), tempbuf + (8 * 3), 8); - // AIA data - memcpy(tag_data + (8 * 5), tempbuf + (8 * 5), 8); - // AA1 data - memcpy(tag_data + (8 * 6), tempbuf + (8 * 6), ((blocks_read - 6) * 8)); + memcpy(tag_data + (PICOPASS_BLOCK_SIZE * 3), + tempbuf + (PICOPASS_BLOCK_SIZE * 3), PICOPASS_BLOCK_SIZE); } + // all memory available + memcpy(tag_data + (PICOPASS_BLOCK_SIZE * payload.start_block), + tempbuf + (PICOPASS_BLOCK_SIZE * payload.start_block), + blocks_read * PICOPASS_BLOCK_SIZE); uint16_t bytes_got = (app_limit1 + 1) * 8; @@ -1955,12 +2133,14 @@ static int CmdHFiClassDump(const char *Cmd) { } // div key KC - memcpy(tag_data + (8 * 4), tempbuf + (8 * 4), 8); + memcpy(tag_data + (PICOPASS_BLOCK_SIZE * 4), tempbuf + (PICOPASS_BLOCK_SIZE * 4), PICOPASS_BLOCK_SIZE); // AA2 data - memcpy(tag_data + (8 * (app_limit1 + 1)), tempbuf + (8 * (app_limit1 + 1)), (blocks_read * 8)); + memcpy(tag_data + (PICOPASS_BLOCK_SIZE * payload.start_block), + tempbuf + (PICOPASS_BLOCK_SIZE * payload.start_block), + blocks_read * PICOPASS_BLOCK_SIZE); - bytes_got = (blocks_read * 8); + bytes_got += (blocks_read * PICOPASS_BLOCK_SIZE); aa2_success = true; } @@ -1973,6 +2153,12 @@ write_dump: // print the dump printIclassDumpContents(tag_data, 1, (bytes_got / 8), bytes_got, dense_output); + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + // use CSN as filename if (filename[0] == 0) { strcat(filename, "hf-iclass-"); @@ -1982,7 +2168,7 @@ write_dump: // save the dump to .bin file PrintAndLogEx(SUCCESS, "saving dump file - %u blocks read", bytes_got / 8); - pm3_save_dump(filename, tag_data, bytes_got, jsfIclass, PICOPASS_BLOCK_SIZE); + pm3_save_dump(filename, tag_data, bytes_got, jsfIclass); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass decrypt -f") "` to decrypt dump file"); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass view -f") "` to view dump file"); @@ -2022,6 +2208,7 @@ static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *macdata if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); return resp.status; } + return (resp.data.asBytes[0] == 1) ? PM3_SUCCESS : PM3_ESOFT; } @@ -2029,15 +2216,15 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass wrbl", "Write data to an iCLASS tag", - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B\n" - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit\n" - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA --ki 0"); + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B\n" + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit\n" + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA --ki 0"); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "Access key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_int1("b", "block", "", "The block number to read"), + arg_int1(NULL, "blk", "", "block number"), arg_str1("d", "data", "", "data to write as 8 hex bytes"), arg_str0("m", "mac", "", "replay mac data (4 hex bytes)"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), @@ -2126,7 +2313,131 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { int isok = iclass_write_block(blockno, data, mac, key, use_credit_key, elite, rawkey, use_replay, verbose, auth, shallow_mod); switch (isok) { case PM3_SUCCESS: - PrintAndLogEx(SUCCESS, "Wrote block %3d/0x%02X successful", blockno, blockno); + PrintAndLogEx(SUCCESS, "Wrote block " _YELLOW_("%d") " / " _YELLOW_("0x%02X") " ( " _GREEN_("ok") " )", blockno, blockno); + break; + case PM3_ETEAROFF: + if (verbose) + PrintAndLogEx(INFO, "Writing tear off triggered"); + break; + default: + PrintAndLogEx(FAILED, "Writing failed"); + break; + } + PrintAndLogEx(NORMAL, ""); + return isok; +} + +static int CmdHFiClassCreditEpurse(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass creditepurse", + "Credit the epurse on an iCLASS tag. The provided key must be the credit key.\n" + "The first two bytes of the epurse are the debit value (big endian) and may be any value except FFFF.\n" + "The remaining two bytes of the epurse are the credit value and must be smaller than the previous value.", + "hf iclass creditepurse -d FEFFFFFF -k 001122334455667B\n" + "hf iclass creditepurse -d FEFFFFFF --ki 0"); + + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "Credit key as 8 hex bytes"), + arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_str1("d", "data", "", "data to write as 8 hex bytes"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "no computations applied to key"), + arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int key_len = 0; + uint8_t key[8] = {0}; + + CLIGetHexWithReturn(ctx, 1, key, &key_len); + + int key_nr = arg_get_int_def(ctx, 2, -1); + + if (key_len > 0 && key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify key or index, not both"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (key_len > 0) { + if (key_len != 8) { + PrintAndLogEx(ERR, "Key is incorrect length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + 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; + } + } else { + PrintAndLogEx(ERR, "Key or key number must be provided"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int blockno = 2; + + int data_len = 0; + uint8_t data[4] = {0}; + CLIGetHexWithReturn(ctx, 3, data, &data_len); + + if (data_len != 4) { + PrintAndLogEx(ERR, "Data must be 4 hex bytes (8 hex symbols)"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool elite = arg_get_lit(ctx, 4); + bool rawkey = arg_get_lit(ctx, 5); + bool verbose = arg_get_lit(ctx, 6); + bool shallow_mod = arg_get_lit(ctx, 7); + + CLIParserFree(ctx); + + if ((rawkey + elite) > 1) { + PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw'"); + return PM3_EINVARG; + } + + iclass_credit_epurse_t payload = { + .req.use_raw = rawkey, + .req.use_elite = elite, + .req.use_credit_key = true, + .req.use_replay = false, + .req.blockno = blockno, + .req.send_reply = true, + .req.do_auth = true, + .req.shallow_mod = shallow_mod, + }; + memcpy(payload.req.key, key, 8); + memcpy(payload.epurse, data, sizeof(payload.epurse)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ICLASS_CREDIT_EPURSE, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + + int isok; + if (WaitForResponseTimeout(CMD_HF_ICLASS_CREDIT_EPURSE, &resp, 2000) == 0) { + if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); + isok = PM3_ETIMEOUT; + } else if (resp.status != PM3_SUCCESS) { + if (verbose) PrintAndLogEx(ERR, "failed to communicate with card"); + isok = resp.status; + } else { + isok = (resp.data.asBytes[0] == 1) ? PM3_SUCCESS : PM3_ESOFT; + } + + switch (isok) { + case PM3_SUCCESS: + PrintAndLogEx(SUCCESS, "Credited epurse successfully"); break; case PM3_ETEAROFF: if (verbose) @@ -2142,7 +2453,7 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { static int CmdHFiClassRestore(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass restore", - "Restore data from dumpfile onto a iCLASS tag", + "Restore data from dumpfile (bin/eml/json) onto a iCLASS tag", "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" @@ -2150,7 +2461,7 @@ static int CmdHFiClassRestore(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "specify a filename to restore (bin/eml/json)"), + arg_str1("f", "file", "", "specify a filename to restore"), arg_str0("k", "key", "", "Access key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), arg_int1(NULL, "first", "", "The first block number to restore"), @@ -2364,15 +2675,15 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass rdbl", "Read a iCLASS block from tag", - "hf iclass rdbl -b 6 -k 0011223344556677\n" - "hf iclass rdbl -b 27 -k 0011223344556677 --credit\n" - "hf iclass rdbl -b 10 --ki 0"); + "hf iclass rdbl --blk 6 -k 0011223344556677\n" + "hf iclass rdbl --blk 27 -k 0011223344556677 --credit\n" + "hf iclass rdbl --blk 10 --ki 0"); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "Access key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_int1("b", "block", "", "The block number to read"), + arg_int1(NULL, "blk", "", "Block number"), 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"), @@ -2464,7 +2775,7 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { return PM3_SUCCESS; // crypto helper available. - PrintAndLogEx(INFO, "----------------------------- " _CYAN_("cardhelper") " -----------------------------"); + PrintAndLogEx(INFO, "----------------------------- " _CYAN_("Cardhelper") " -----------------------------"); switch (blockno) { case 6: { @@ -2473,9 +2784,9 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { } case 7: { - uint8_t dec_data[8]; + uint8_t dec_data[PICOPASS_BLOCK_SIZE]; - uint64_t a = bytes_to_num(data, 8); + uint64_t a = bytes_to_num(data, PICOPASS_BLOCK_SIZE); bool starts = (leadingzeros(a) < 12); bool ones = (bitcount64(a) > 16 && bitcount64(a) < 48); @@ -2488,25 +2799,32 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { PrintAndLogEx(INFO, "data looks unencrypted, trying to decode"); } - if (memcmp(dec_data, empty, 8) != 0) { + bool has_new_pacs = iclass_detect_new_pacs(dec_data); + bool has_values = (memcmp(dec_data, empty, PICOPASS_BLOCK_SIZE) != 0) && (memcmp(dec_data, zeros, PICOPASS_BLOCK_SIZE) != 0); - //todo: remove preamble/sentinel - uint32_t top = 0, mid = 0, bot = 0; + if (has_values) { - char hexstr[16 + 1] = {0}; - hex_to_buffer((uint8_t *)hexstr, dec_data, 8, sizeof(hexstr) - 1, 0, 0, true); - hexstring_to_u96(&top, &mid, &bot, hexstr); + if (has_new_pacs) { + iclass_decode_credentials_new_pacs(dec_data); + } else { + //todo: remove preamble/sentinel + uint32_t top = 0, mid = 0, bot = 0; - char binstr[64 + 1]; - hextobinstring(binstr, hexstr); - char *pbin = binstr; - while (strlen(pbin) && *(++pbin) == '0'); + char hexstr[16 + 1] = {0}; + hex_to_buffer((uint8_t *)hexstr, dec_data, PICOPASS_BLOCK_SIZE, sizeof(hexstr) - 1, 0, 0, true); + hexstring_to_u96(&top, &mid, &bot, hexstr); - PrintAndLogEx(SUCCESS, " bin : %s", pbin); - PrintAndLogEx(INFO, ""); - PrintAndLogEx(INFO, "------------------------------ " _CYAN_("wiegand") " -------------------------------"); - wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); - HIDTryUnpack(&packed); + char binstr[64 + 1]; + hextobinstring(binstr, hexstr); + char *pbin = binstr; + while (strlen(pbin) && *(++pbin) == '0'); + + PrintAndLogEx(SUCCESS, " bin : %s", pbin); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "------------------------------ " _CYAN_("Wiegand") " -------------------------------"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); + HIDTryUnpack(&packed); + } } else { PrintAndLogEx(INFO, "no credential found"); } @@ -2564,57 +2882,99 @@ static int CmdHFiClass_loclass(const char *Cmd) { return bruteforceFileNoKeys(filename); } -static void detect_credential(uint8_t *data, bool *legacy, bool *se, bool *sr) { - bool r1 = !memcmp(data + (5 * 8), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8); +static void detect_credential(uint8_t *iclass_dump, size_t dump_len, bool *is_legacy, bool *is_se, bool *is_sr, uint8_t **sio_start_ptr, size_t *sio_length) { + *is_legacy = false; + *is_sr = false; + *is_se = false; + if (sio_start_ptr != NULL) { + *sio_start_ptr = NULL; + } + if (sio_length != NULL) { + *sio_length = 0; + } - uint8_t pattern_se[] = {0x05, 0x00}; - bool r2 = byte_strstr(data + (6 * 8), 6 * 8, pattern_se, sizeof(pattern_se)) != -1; + if (dump_len < sizeof(picopass_hdr_t)) { + // Can't really do anything with a dump that doesn't include the header + return; + } - uint8_t pattern_sr[] = {0x05, 0x00, 0x05, 0x00}; - bool r3 = byte_strstr(data + (11 * 8), 6 * 8, pattern_sr, sizeof(pattern_sr)) != -1; + picopass_hdr_t *hdr = (picopass_hdr_t *)iclass_dump; - *legacy = (r1) && (data[6 * 8] != 0x30); - *se = (r2) && (data[6 * 8] == 0x30); - *sr = (r3) && (data[10 * 8] == 0x30); - r1 = NULL, r2 = NULL, r3 = NULL; + if (!memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + // Legacy AIA + *is_legacy = true; + + if (dump_len < 11 * PICOPASS_BLOCK_SIZE) { + // Can't reliably detect if the card is SR without checking + // blocks 6 and 10 + return; + } + + // SR bit set in legacy config block + if ((iclass_dump[6 * PICOPASS_BLOCK_SIZE] & ICLASS_CFG_BLK_SR_BIT) == ICLASS_CFG_BLK_SR_BIT) { + // If the card is blank (all FF's) then we'll reach here too, so check for an empty block 10 + // to avoid false positivies + if (memcmp(iclass_dump + (10 * PICOPASS_BLOCK_SIZE), "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + *is_sr = true; + if (sio_start_ptr != NULL) { + // SR SIO starts at block 10 + *sio_start_ptr = iclass_dump + (10 * PICOPASS_BLOCK_SIZE); + } + } + } + } else if (!memcmp(hdr->app_issuer_area, "\xFF\xFF\xFF\x00\x06\xFF\xFF\xFF", PICOPASS_BLOCK_SIZE)) { + // SE AIA + *is_se = true; + + if (sio_start_ptr != NULL) { + // SE SIO starts at block 6 + *sio_start_ptr = iclass_dump + (6 * PICOPASS_BLOCK_SIZE); + } + } + + if (sio_length == NULL || sio_start_ptr == NULL || *sio_start_ptr == NULL) { + // No need to calculate length + return; + } + + uint8_t *sio_start = *sio_start_ptr; + + if (sio_start[0] != 0x30) { + // SIOs always start with a SEQUENCE(P), if this is missing then bail + return; + } + + if (sio_start[1] >= 0x80 || sio_start[1] == 0x00) { + // We only support definite short form lengths + return; + } + + // Length of bytes within the SEQUENCE, plus tag and length bytes for the SEQUENCE tag + *sio_length = sio_start[1] + 2; } // print ASN1 decoded array in TLV view -static void printIclassSIO(uint8_t *iclass_dump) { - - bool isLegacy, isSE, isSR; - detect_credential(iclass_dump, &isLegacy, &isSE, &isSR); - - int dlen = 0; +static void print_iclass_sio(uint8_t *iclass_dump, size_t dump_len) { + bool is_legacy, is_se, is_sr; uint8_t *sio_start; - if (isSE) { + size_t sio_length; + detect_credential(iclass_dump, dump_len, &is_legacy, &is_se, &is_sr, &sio_start, &sio_length); - sio_start = iclass_dump + (6 * 8); - uint8_t pattern_se[] = {0x05, 0x00}; - dlen = byte_strstr(sio_start, 8 * 8, pattern_se, sizeof(pattern_se)); - if (dlen == -1) { - return; - } - dlen += sizeof(pattern_se); - } else if (isSR) { + if (sio_start == NULL) { + return; + } - sio_start = iclass_dump + (10 * 8); - uint8_t pattern_sr[] = {0x05, 0x00, 0x05, 0x00}; - dlen = byte_strstr(sio_start, 8 * 8, pattern_sr, sizeof(pattern_sr)); - if (dlen == -1) { - return; - } - dlen += sizeof(pattern_sr); - } else { + if (dump_len < sio_length + (sio_start - iclass_dump)) { + // SIO length exceeds the size of the dump we have, bail return; } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "---------------------------- " _CYAN_("SIO - RAW") " ----------------------------"); - print_hex_noascii_break(sio_start, dlen, 32); + print_hex_noascii_break(sio_start, sio_length, 32); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "------------------------- " _CYAN_("SIO - ASN1 TLV") " --------------------------"); - asn1_print(sio_start, dlen, " "); + asn1_print(sio_start, sio_length, " "); PrintAndLogEx(NORMAL, ""); } @@ -2639,8 +2999,15 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e else maxmemcount = 31; - if (startblock == 0) - startblock = 6; + uint8_t pagemap = get_pagemap(hdr); + + if (startblock == 0) { + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + startblock = 3; + } else { + startblock = 6; + } + } if ((endblock > maxmemcount) || (endblock == 0)) endblock = maxmemcount; @@ -2658,11 +3025,18 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e , filemaxblock ); */ - uint8_t pagemap = get_pagemap(hdr); - bool isLegacy = false, isSE = false, isSR = false; - if (filemaxblock >= 17) { - detect_credential(iclass_dump, &isLegacy, &isSE, &isSR); + bool is_legacy, is_se, is_sr; + uint8_t *sio_start; + size_t sio_length; + detect_credential(iclass_dump, endblock * 8, &is_legacy, &is_se, &is_sr, &sio_start, &sio_length); + + bool is_legacy_decrypted = is_legacy && (iclass_dump[(6 * PICOPASS_BLOCK_SIZE) + 7] & 0x03) == 0x00; + + int sio_start_block = 0, sio_end_block = 0; + if (sio_start && sio_length > 0) { + sio_start_block = (sio_start - iclass_dump) / PICOPASS_BLOCK_SIZE; + sio_end_block = sio_start_block + ((sio_length + PICOPASS_BLOCK_SIZE - 1) / PICOPASS_BLOCK_SIZE) - 1; } int i = startblock; @@ -2733,38 +3107,33 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e regular_print_block = true; } else { - const char *info_ks[] = {"CSN", "Config", "E-purse", "Debit", "Credit", "AIA", "User"}; + const char *info_ks[] = {"CSN", "Config", "E-purse", "Debit", "Credit", "AIA", "User", "User AA2"}; - if (i >= 6 && i <= 9 && isLegacy && isSE == false) { + if (i >= 6 && i <= 9 && is_legacy) { // legacy credential - PrintAndLogEx(INFO, "%3d/0x%02X | " _YELLOW_("%s") "| " _YELLOW_("%s") " | %s | User / Cred " + PrintAndLogEx(INFO, "%3d/0x%02X | " _YELLOW_("%s") "| " _YELLOW_("%s") " | %s | User / %s " , i , i , sprint_hex(blk, 8) , sprint_ascii(blk, 8) , lockstr + , i == 6 ? "HID CFG" : (is_legacy_decrypted ? "Cred" : "Enc Cred") ); - } else if (i >= 6 && i <= 12 && isSE) { + } else if (sio_start_block != 0 && i >= sio_start_block && i <= sio_end_block) { // SIO credential - PrintAndLogEx(INFO, "%3d/0x%02X | " _CYAN_("%s") "| " _CYAN_("%s") " | %s | User / SIO / SE" - , i - , i - , sprint_hex(blk, 8) - , sprint_ascii(blk, 8) - , lockstr - ); - } else if (i >= 10 && i <= 16 && isSR) { - // SIO credential - PrintAndLogEx(INFO, "%3d/0x%02X | " _CYAN_("%s") "| " _CYAN_("%s") " | %s | User / SIO / SR" + PrintAndLogEx(INFO, "%3d/0x%02X | " _CYAN_("%s") "| " _CYAN_("%s") " | %s | User / SIO / %s" , i , i , sprint_hex(blk, 8) , sprint_ascii(blk, 8) , lockstr + , is_se ? "SE" : "SR" ); } else { if (i < 6) { block_info = info_ks[i]; + } else if (i > hdr->conf.app_limit) { + block_info = info_ks[7]; } else { block_info = info_ks[6]; } @@ -2775,7 +3144,7 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e if (regular_print_block) { // suppress repeating blocks, truncate as such that the first and last block with the same data is shown - // but the blocks in between are replaced with a single line of "*" if dense_output is enabled + // but the blocks in between are replaced with a single line of "......" if dense_output is enabled if (dense_output && i > 6 && i < (endblock - 1) && !in_repeated_block && !memcmp(blk, blk - 8, 8) && !memcmp(blk, blk + 8, 8) && !memcmp(blk, blk + 16, 8)) { // we're in a user block that isn't the first user block nor last two user blocks, @@ -2789,7 +3158,7 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e if (in_repeated_block == false) { PrintAndLogEx(INFO, - "%3d/0x%02X | %s | %s | %s ", + "%3d/0x%02X | %s | %s | %s", i, i, sprint_hex_ascii(blk, 8), @@ -2801,13 +3170,13 @@ void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t e i++; } PrintAndLogEx(INFO, "---------+-------------------------+----------+---+----------------"); - if (isLegacy) + if (is_legacy) PrintAndLogEx(HINT, _YELLOW_("yellow") " = legacy credential"); - if (isSE) + if (is_se) PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SE credential"); - if (isSR) + if (is_sr) PrintAndLogEx(HINT, _CYAN_("cyan") " = SIO / SR credential"); PrintAndLogEx(NORMAL, ""); @@ -2818,12 +3187,14 @@ static int CmdHFiClassView(const char *Cmd) { CLIParserInit(&ctx, "hf iclass view", "Print a iCLASS tag dump file (bin/eml/json)", "hf iclass view -f hf-iclass-AA162D30F8FF12F1-dump.bin\n" - "hf iclass view --first 1 -f hf-iclass-AA162D30F8FF12F1-dump.bin\n"); + "hf iclass view --first 1 -f hf-iclass-AA162D30F8FF12F1-dump.bin\n\n" + "If --first is not specified it will default to the first user block\n" + "which is block 6 for secured chips or block 3 for non-secured chips"); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), - arg_int0(NULL, "first", "", "Begin printing from this block (default block 6)"), + arg_str1("f", "file", "", "Specify a filename for dump file"), + arg_int0(NULL, "first", "", "Begin printing from this block (default first user block)"), arg_int0(NULL, "last", "", "End printing at this block (default 0, ALL)"), arg_lit0("v", "verbose", "verbose output"), arg_lit0("z", "dense", "dense dump output style"), @@ -2863,7 +3234,7 @@ static int CmdHFiClassView(const char *Cmd) { iclass_decode_credentials(dump); if (verbose) { - printIclassSIO(dump); + print_iclass_sio(dump, bytes_read); } free(dump); @@ -2927,6 +3298,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { arg_str0(NULL, "csn", "", "Specify a Card Serial Number (CSN) to diversify the key (if omitted will attempt to read a CSN)"), arg_lit0(NULL, "elite", "Elite computations applied to new key"), arg_lit0(NULL, "elite2", "Elite computations applied to both old and new key"), + arg_lit0(NULL, "oldelite", "Elite computations applied only to old key"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -3019,6 +3391,11 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { old_elite = true; } + if (arg_get_lit(ctx, 8)) { + elite = false; + old_elite = true; + } + CLIParserFree(ctx); uint8_t xor_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -3036,7 +3413,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { return PM3_SUCCESS; } -static int loadKeys(char *filename) { +static int iclass_load_keys(char *filename) { uint8_t *dump = NULL; size_t bytes_read = 0; @@ -3045,46 +3422,30 @@ static int loadKeys(char *filename) { return PM3_EFILE; } - if (bytes_read > ICLASS_KEYS_MAX * 8) { + if (bytes_read > ICLASS_KEYS_MAX * PICOPASS_BLOCK_SIZE) { PrintAndLogEx(WARNING, "File is too long to load - bytes: %zu", bytes_read); free(dump); return PM3_EFILE; } size_t i = 0; - for (; i < bytes_read / 8; i++) - memcpy(iClass_Key_Table[i], dump + (i * 8), 8); + for (; i < bytes_read / PICOPASS_BLOCK_SIZE; i++) { + memcpy(iClass_Key_Table[i], dump + (i * PICOPASS_BLOCK_SIZE), PICOPASS_BLOCK_SIZE); + } free(dump); PrintAndLogEx(SUCCESS, "Loaded " _GREEN_("%2zd") " keys from %s", i, filename); return PM3_SUCCESS; } -static int saveKeys(char *filename) { - FILE *f; - f = fopen(filename, "wb"); - if (!f) { - PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", filename); - return PM3_EFILE; - } - for (uint8_t i = 0; i < ICLASS_KEYS_MAX; i++) { - if (fwrite(iClass_Key_Table[i], 8, 1, f) != 1) { - PrintAndLogEx(WARNING, "save key failed to write to file:" _YELLOW_("%s"), filename); - break; - } - } - fclose(f); - return PM3_SUCCESS; -} - -static int printKeys(void) { +static int iclass_print_keys(void) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "idx| key"); PrintAndLogEx(INFO, "---+------------------------"); for (uint8_t i = 0; i < ICLASS_KEYS_MAX; i++) { - if (memcmp(iClass_Key_Table[i], "\x00\x00\x00\x00\x00\x00\x00\x00", 8) == 0) + if (memcmp(iClass_Key_Table[i], zeros, sizeof(zeros)) == 0) PrintAndLogEx(INFO, " %u |", i); else - PrintAndLogEx(INFO, " %u | " _YELLOW_("%s"), i, sprint_hex(iClass_Key_Table[i], 8)); + PrintAndLogEx(INFO, " %u | " _YELLOW_("%s"), i, sprint_hex(iClass_Key_Table[i], PICOPASS_BLOCK_SIZE)); } PrintAndLogEx(INFO, "---+------------------------"); PrintAndLogEx(NORMAL, ""); @@ -3177,11 +3538,15 @@ static int CmdHFiClassManageKeys(const char *Cmd) { PrintAndLogEx(SUCCESS, " New key[%d] " _GREEN_("%s"), key_nr, sprint_hex_inrow(iClass_Key_Table[key_nr], 8)); return PM3_SUCCESS; case 4: - return printKeys(); + return iclass_print_keys(); case 5: - return loadKeys(filename); - case 6: - return saveKeys(filename); + return iclass_load_keys(filename); + case 6: { + bool isOK = saveFile(filename, ".bin", iClass_Key_Table, sizeof(iClass_Key_Table)); + if (isOK == false) { + return PM3_EFILE; + } + } } return PM3_SUCCESS; } @@ -4073,11 +4438,11 @@ static int CmdHFiClassAutopwn(const char *Cmd) { static int CmdHFiClassConfigCard(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass configcard", - "Manage reader configuration card via Cardhelper,\n" + "Manage reader configuration card via Cardhelper or internal database,\n" "The generated config card will be uploaded to device emulator memory.\n" "You can start simulating `hf iclass sim -t 3` or use the emul commands", - "hf iclass configcard -l --> download config card settings\n" - "hf iclass configcard -p --> print all config cards\n" + "hf iclass configcard -l --> download config card settings from cardhelper\n" + "hf iclass configcard -p --> print all config cards in the database\n" "hf iclass configcard --ci 1 --> view config card setting in slot 1\n" "hf iclass configcard -g --ci 0 --> generate config file from slot 0" ); @@ -4123,12 +4488,14 @@ static int CmdHFiClassConfigCard(const char *Cmd) { print_config_cards(); } - if (ccidx > -1 && ccidx < 14) { + if (ccidx > -1 && ccidx < ARRAYLEN(iclass_config_types)) { const iclass_config_card_item_t *item = get_config_card_item(ccidx); print_config_card(item); + } else { + PrintAndLogEx(ERR, "Please specify a valid configuration number!"); } - if (do_generate) { + if (do_generate && (ccidx > -1 && ccidx < ARRAYLEN(iclass_config_types))) { const iclass_config_card_item_t *item = get_config_card_item(ccidx); if (strstr(item->desc, "Keyroll") != NULL) { if (got_kr == false) { @@ -4142,39 +4509,159 @@ static int CmdHFiClassConfigCard(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHFiClassSAM(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass sam", + "Extract PACS via a HID SAM\n", + "hf iclass sam\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + if (IsHIDSamPresent(verbose) == false) { + return PM3_ESOFT; + } + + clearCommandBuffer(); + SendCommandNG(CMD_HF_SAM_PICOPASS, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_SAM_PICOPASS, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "SAM timeout"); + return PM3_ETIMEOUT; + } + + switch (resp.status) { + case PM3_SUCCESS: + break; + case PM3_ENOPACS: + PrintAndLogEx(SUCCESS, "No PACS data found. Card empty?"); + return resp.status; + default: + PrintAndLogEx(WARNING, "SAM select failed"); + return resp.status; + } + + // CSN, config, epurse, NR/MAC, AIA + // PACS + // first byte skip + // second byte length + // third padded + // fourth .. + uint8_t *d = resp.data.asBytes; + uint8_t n = d[1] - 1; // skip length byte + uint8_t pad = d[2]; + char *binstr = (char *)calloc((n * 8) + 1, sizeof(uint8_t)); + if (binstr == NULL) { + return PM3_EMALLOC; + } + + bytes_2_binstr(binstr, d + 3, n); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "PACS......... " _GREEN_("%s"), sprint_hex_inrow(d + 2, resp.length - 2)); + PrintAndLogEx(SUCCESS, "padded bin... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + binstr[strlen(binstr) - pad] = '\0'; + PrintAndLogEx(SUCCESS, "bin.......... " _GREEN_("%s") " ( %zu )", binstr, strlen(binstr)); + + size_t hexlen = 0; + uint8_t hex[16] = {0}; + binstr_2_bytes(hex, &hexlen, binstr); + PrintAndLogEx(SUCCESS, "hex.......... " _GREEN_("%s"), sprint_hex_inrow(hex, hexlen)); + + uint32_t top = 0, mid = 0, bot = 0; + if (binstring_to_u96(&top, &mid, &bot, binstr) != strlen(binstr)) { + PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); + free(binstr); + return PM3_EINVARG; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Wiegand decode"); + wiegand_message_t packed = initialize_message_object(top, mid, bot, 0); + HIDTryUnpack(&packed); + + PrintAndLogEx(NORMAL, ""); + + if (strlen(binstr) >= 26 && verbose) { + + // iCLASS Legacy + PrintAndLogEx(INFO, "Clone to " _YELLOW_("iCLASS Legacy")); + PrintAndLogEx(SUCCESS, " hf iclass encode --ki 0 --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + + // HID Prox II + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("HID Prox II")); + PrintAndLogEx(SUCCESS, " lf hid clone -w H10301 --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + + // MIFARE Classic + char mfcbin[28] = {0}; + mfcbin[0] = '1'; + memcpy(mfcbin + 1, binstr, strlen(binstr)); + binstr_2_bytes(hex, &hexlen, mfcbin); + + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic") " (Pm3 simulation)"); + PrintAndLogEx(SUCCESS, " hf mf eclr;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 0 -d 049DBA42A23E80884400C82000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 1 -d 1B014D48000000000000000000000000;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 3 -d A0A1A2A3A4A5787788C189ECA97F8C2A;"); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 5 -d 020000000000000000000000%s;", sprint_hex_inrow(hex, hexlen)); + PrintAndLogEx(SUCCESS, " hf mf esetblk --blk 7 -d 484944204953787788AA204752454154;"); + PrintAndLogEx(SUCCESS, " hf mf sim --1k -i;"); + PrintAndLogEx(NORMAL, ""); + + PrintAndLogEx(INFO, "Downgrade to " _YELLOW_("MIFARE Classic 1K")); + PrintAndLogEx(SUCCESS, " hf mf encodehid --bin %s", binstr); + PrintAndLogEx(NORMAL, ""); + } + free(binstr); + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { - {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdHFiClassList, AlwaysAvailable, "List iclass history"}, + {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("general") " ---------------------"}, // {"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"}, + {"info", CmdHFiClassInfo, IfPm3Iclass, "Tag information"}, {"rdbl", CmdHFiClass_ReadBlock, IfPm3Iclass, "Read Picopass / iCLASS block"}, {"reader", CmdHFiClassReader, IfPm3Iclass, "Act like a Picopass / iCLASS reader"}, - {"restore", CmdHFiClassRestore, IfPm3Iclass, "Restore a dump file onto a Picopass / iCLASS tag"}, + {"restore", CmdHFiClassRestore, IfPm3Iclass, "Restore a dump file onto a Picopass / iCLASS tag"}, {"sniff", CmdHFiClassSniff, IfPm3Iclass, "Eavesdrop Picopass / iCLASS communication"}, + {"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"}, {"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "Write Picopass / iCLASS block"}, - - {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("recovery") " ---------------------"}, + {"creditepurse", CmdHFiClassCreditEpurse, IfPm3Iclass, "Credit epurse value"}, + {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("recovery") " --------------------"}, // {"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") " ---------------------"}, + {"-----------", CmdHelp, IfPm3Iclass, "-------------------- " _CYAN_("simulation") " -------------------"}, {"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"}, + {"esetblk", CmdHFiClassESetBlk, IfPm3Iclass, "Set emulator memory block data"}, {"eview", CmdHFiClassEView, IfPm3Iclass, "View emulator memory"}, - - {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("utils") " ---------------------"}, - {"configcard", CmdHFiClassConfigCard, AlwaysAvailable, "Reader configuration card"}, + {"-----------", CmdHelp, AlwaysAvailable, "---------------------- " _CYAN_("utils") " ----------------------"}, + {"configcard", CmdHFiClassConfigCard, IfPm3Iclass, "Reader configuration card"}, {"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, AlwaysAvailable, "Permute function from 'heart of darkness' paper"}, - {"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"}, + {"-----------", CmdHelp, IfPm3Smartcard, "----------------------- " _CYAN_("SAM") " -----------------------"}, + {"sam", CmdHFiClassSAM, IfPm3Smartcard, "SAM tests"}, {NULL, NULL, NULL, NULL} }; @@ -4189,7 +4676,7 @@ int CmdHFiClass(const char *Cmd) { return CmdsParse(CommandTable, Cmd); } -//static void test_credential_type(void) { +// static void test_credential_type(void) { // need AA1 key // Block 5 -> tells if its a legacy or SIO, also tells which key to use. @@ -4235,7 +4722,7 @@ int info_iclass(bool shallow_mod) { picopass_ns_hdr_t *ns_hdr = &r->header.ns_hdr; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--------------------- " _CYAN_("Tag Information") " ----------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ----------------------------------------"); if ((r->status & FLAG_ICLASS_CSN) == FLAG_ICLASS_CSN) { PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); @@ -4287,13 +4774,13 @@ int info_iclass(bool shallow_mod) { memcpy(aia, hdr->app_issuer_area, sizeof(aia)); } - // if CSN ends with FF12E0, it's inside HID CSN range. - bool isHidRange = (memcmp(hdr->csn + 5, "\xFF\x12\xE0", 3) == 0); + // if CSN starts with E012FFF (big endian), it's inside HID CSN range. + bool is_hid_range = (hdr->csn[4] & 0xF0) == 0xF0 && (memcmp(hdr->csn + 5, "\xFF\x12\xE0", 3) == 0); - bool legacy = (memcmp(aia, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); - bool se_enabled = (memcmp(aia, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); + if (is_hid_range) { + bool legacy = (memcmp(aia, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0); + bool se_enabled = (memcmp(aia, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0); - if (isHidRange) { PrintAndLogEx(SUCCESS, " CSN.......... " _YELLOW_("HID range")); if (legacy) PrintAndLogEx(SUCCESS, " Credential... " _GREEN_("iCLASS legacy")); diff --git a/client/src/cmdhfjooki.c b/client/src/cmdhfjooki.c index 93ca6a00a..261a6194a 100644 --- a/client/src/cmdhfjooki.c +++ b/client/src/cmdhfjooki.c @@ -200,7 +200,7 @@ static int jooki_create_ndef(uint8_t *b64ndef, uint8_t *ndefrecord) { 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, "Encoded URL.. %s ( " _YELLOW_("%s") " )", sprint_hex(b64, 12), b64); PrintAndLogEx(INFO, "Figurine..... %02x %02x - " _GREEN_("%s, %s") , tid , fid @@ -525,29 +525,36 @@ static int CmdHF14AJookiSim(const char *Cmd) { // upload to emulator memory PrintAndLogEx(INFO, "Uploading to emulator memory"); - PrintAndLogEx(INFO, "." NOLF); + // fast push mode g_conn.block_after_ACK = true; uint8_t blockwidth = 4, counter = 0, blockno = 0; + + // 12 is the size of the struct the fct mfEmlSetMem_xt uses to transfer to device + uint16_t max_avail_blocks = ((PM3_CMD_DATA_SIZE - 12) / blockwidth) * blockwidth; + while (datalen) { if (datalen == blockwidth) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } + uint16_t chunk_size = MIN(max_avail_blocks, datalen); + uint16_t blocks_to_send = chunk_size / blockwidth; - if (mfEmlSetMem_xt(data + counter, blockno, 1, blockwidth) != PM3_SUCCESS) { + if (mfEmlSetMem_xt(data + counter, blockno, blocks_to_send, blockwidth) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Cant set emul block: %3d", blockno); free(data); return PM3_ESOFT; } + blockno += blocks_to_send; + counter += chunk_size; + datalen -= chunk_size; PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); - blockno++; - counter += blockwidth; - datalen -= blockwidth; } - PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", counter); struct { uint8_t tagtype; @@ -566,7 +573,9 @@ static int CmdHF14AJookiSim(const char *Cmd) { SendCommandNG(CMD_HF_ISO14443A_SIMULATE, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Starting simulating"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to abort simulation"); for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); @@ -574,15 +583,15 @@ static int CmdHF14AJookiSim(const char *Cmd) { break; } - if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false) continue; if (resp.status != PM3_SUCCESS) break; } free(data); - PrintAndLogEx(INFO, "Done"); PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 14a list") "` to view trace log"); + PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index 02a43e0aa..f7c44ecbc 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -58,34 +58,35 @@ static bool legic_xor(uint8_t *data, uint16_t cardsize) { } static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buffer) { - int i = 0, k = 0, segmentNum = 0, segment_len = 0, segment_flag = 0; - int crc = 0, wrp = 0, wrc = 0; - uint8_t stamp_len = 0; - char token_type[6] = {0, 0, 0, 0, 0, 0}; - int dcf = 0; - int bIsSegmented = 0; - int return_value = PM3_SUCCESS; if (!(card_size == LEGIC_PRIME_MIM22 || card_size == LEGIC_PRIME_MIM256 || card_size == LEGIC_PRIME_MIM1024)) { PrintAndLogEx(FAILED, "Bytebuffer is not any known legic card size! (MIM22, MIM256, MIM1024)"); - return_value = PM3_EFAILED; return PM3_EFAILED; } // copy input buffer into newly allocated buffer, because the existing code mutates the data inside. uint8_t *data = calloc(card_size, sizeof(uint8_t)); - if (!data) { + if (data == NULL) { PrintAndLogEx(WARNING, "Cannot allocate memory"); return PM3_EMALLOC; } memcpy(data, input_buffer, card_size); + int i = 0, k = 0, segmentNum = 0, segment_len = 0, segment_flag = 0; + int wrp = 0, wrc = 0, dcf = 0; + uint8_t stamp_len = 0; + char token_type[6] = {0, 0, 0, 0, 0, 0}; + int bIsSegmented = 0; + int return_value = PM3_SUCCESS; + // Output CDF System area (9 bytes) plus remaining header area (12 bytes) - crc = data[4]; + int crc = data[4]; uint32_t calc_crc = CRC8Legic(data, 4); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ----------------------------------------"); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, " " _CYAN_("CDF: System Area")); - PrintAndLogEx(NORMAL, "------------------------------------------------------"); + PrintAndLogEx(INFO, "------------------------------------------------------"); PrintAndLogEx(SUCCESS, "MCD: " _GREEN_("%02X") " MSN: " _GREEN_("%s") " MCC: " _GREEN_("%02X") " ( %s )", data[0], sprint_hex(data + 1, 3), @@ -111,30 +112,28 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff int fl = 0; - if (data[6] == 0xec) { + if (data[6] == 0xEC) { strncpy(token_type, "XAM", sizeof(token_type) - 1); fl = 1; stamp_len = 0x0c - (data[5] >> 4); } else { - switch (data[5] & 0x7f) { - case 0x00 ... 0x2f: - strncpy(token_type, "IAM", sizeof(token_type) - 1); - fl = (0x2f - (data[5] & 0x7f)) + 1; - break; - case 0x30 ... 0x6f: - strncpy(token_type, "SAM", sizeof(token_type) - 1); - fl = (0x6f - (data[5] & 0x7f)) + 1; - break; - case 0x70 ... 0x7f: - strncpy(token_type, "GAM", sizeof(token_type) - 1); - fl = (0x7f - (data[5] & 0x7f)) + 1; - break; + + uint8_t tmp = data[5] & 0x7F; + if (tmp <= 0x2F) { + strncpy(token_type, "IAM", sizeof(token_type) - 1); + fl = (0x2F - tmp) + 1; + } else if (tmp >= 0x30 && tmp <= 0x6F) { + strncpy(token_type, "SAM", sizeof(token_type) - 1); + fl = (0x6F - tmp) + 1; + } else if (tmp >= 0x70 && tmp <= 0x7F) { + strncpy(token_type, "GAM", sizeof(token_type) - 1); + fl = (0x7F - tmp) + 1; } - stamp_len = 0xfc - data[6]; + stamp_len = 0xFC - data[6]; } - PrintAndLogEx(SUCCESS, "DCF: %d (%02x %02x), Token Type=" _YELLOW_("%s") " (OLE=%01u), OL=%02u, FL=%02u", + PrintAndLogEx(SUCCESS, "DCF: %d (%02x %02x) Token Type=" _YELLOW_("%s") " (OLE=%01u) OL=%02u FL=%02u", dcf, data[5], data[6], @@ -153,7 +152,7 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff strncpy(token_type, "IM", sizeof(token_type) - 1); } - PrintAndLogEx(SUCCESS, "DCF: %d (%02x %02x), Token Type = %s (OLE = %01u)", + PrintAndLogEx(SUCCESS, "DCF: %d (%02x %02x) Token Type = %s (OLE = %01u)", dcf, data[5], data[6], @@ -166,7 +165,7 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff if (dcf != 0xFFFF) { if (bIsSegmented) { - PrintAndLogEx(SUCCESS, "WRP = %02u, WRC = %01u, RD = %01u, SSC = %02X", + PrintAndLogEx(SUCCESS, "WRP = %02u WRC = %01u RD = %01u SSC = %02X", data[7] & 0x0f, (data[7] & 0x70) >> 4, (data[7] & 0x80) >> 7, @@ -185,7 +184,7 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff } } } - PrintAndLogEx(NORMAL, "------------------------------------------------------"); + PrintAndLogEx(INFO, "------------------------------------------------------"); uint8_t segCrcBytes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; uint32_t segCalcCRC = 0; @@ -196,8 +195,9 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff goto out; } + PrintAndLogEx(INFO, ""); PrintAndLogEx(SUCCESS, _CYAN_("ADF: User Area")); - PrintAndLogEx(NORMAL, "------------------------------------------------------"); + PrintAndLogEx(INFO, "------------------------------------------------------"); if (bIsSegmented) { @@ -208,8 +208,8 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff for (segmentNum = 1; segmentNum < 128; segmentNum++) { // for decoding the segment header we need at least 4 bytes left in buffer if ((i + 4) > card_size) { - PrintAndLogEx(FAILED, "Cannot read segment header, because the input buffer is too small. " - "Please check that the data is correct and properly aligned. "); + PrintAndLogEx(FAILED, "Cannot read segment header, because the input buffer is too small."); + PrintAndLogEx(FAILED, "Please check that the data is correct and properly aligned"); return_value = PM3_EOUTOFBOUND; goto out; } @@ -236,20 +236,20 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff segCalcCRC = CRC8Legic(segCrcBytes, 8); segCRC = data[i + 4] ^ crc; - PrintAndLogEx(SUCCESS, "Segment | " _YELLOW_("%02u"), segmentNum); - PrintAndLogEx(SUCCESS, "raw header | 0x%02X 0x%02X 0x%02X 0x%02X", + PrintAndLogEx(SUCCESS, "Segment....... " _YELLOW_("%02u"), segmentNum); + PrintAndLogEx(SUCCESS, "Raw header.... 0x%02X 0x%02X 0x%02X 0x%02X", data[i] ^ crc, data[i + 1] ^ crc, data[i + 2] ^ crc, data[i + 3] ^ crc ); - PrintAndLogEx(SUCCESS, "Segment len | %u, Flag: 0x%X (valid:%01u, last:%01u)", + PrintAndLogEx(SUCCESS, "Segment len... %u Flag: 0x%X (valid:%01u last:%01u)", segment_len, segment_flag, (segment_flag & 0x4) >> 2, (segment_flag & 0x8) >> 3 ); - PrintAndLogEx(SUCCESS, " | WRP: %02u, WRC: %02u, RD: %01u, CRC: 0x%02X ( %s )", + PrintAndLogEx(SUCCESS, " WRP: %02u WRC: %02u RD: %01u CRC: 0x%02X ( %s )", wrp, wrc, ((data[i + 3] ^ crc) & 0x80) >> 7, @@ -268,28 +268,33 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff } if (hasWRC) { - PrintAndLogEx(SUCCESS, "\nWRC protected area: (I %d | K %d| WRC %d)", i, k, wrc); - PrintAndLogEx(NORMAL, "\nrow | data"); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------"); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(SUCCESS, _CYAN_("WRC protected area:") " (I %d | K %d| WRC %d)", i, k, wrc); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "## | data | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); for (k = i; k < (i + wrc); ++k) data[k] ^= crc; print_hex_break(data + i, wrc, 16); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------\n"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); + PrintAndLogEx(INFO, ""); i += wrc; } if (hasWRP) { - PrintAndLogEx(SUCCESS, "Remaining write protected area: (I %d | K %d | WRC %d | WRP %d WRP_LEN %d)", i, k, wrc, wrp, wrp_len); - PrintAndLogEx(NORMAL, "\nrow | data"); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------"); + PrintAndLogEx(SUCCESS, _CYAN_("Remaining write protected area:") " (I %d | K %d | WRC %d | WRP %d WRP_LEN %d)", i, k, wrc, wrp, wrp_len); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "## | data | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); for (k = i; k < (i + wrp_len); ++k) data[k] ^= crc; print_hex_break(data + i, wrp_len, 16); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------\n"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); + PrintAndLogEx(INFO, ""); i += wrp_len; // does this one work? (Answer: Only if KGH/BGH is used with BCD encoded card number! So maybe this will show just garbage...) @@ -302,15 +307,16 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff } } if (remain_seg_payload_len > 0) { - PrintAndLogEx(SUCCESS, "Remaining segment payload: (I %d | K %d | Remain LEN %d)", i, k, remain_seg_payload_len); - PrintAndLogEx(NORMAL, "\nrow | data"); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------"); + PrintAndLogEx(SUCCESS, _CYAN_("Remaining segment payload:") " (I %d | K %d | Remain LEN %d)", i, k, remain_seg_payload_len); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "## | data | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); for (k = i; k < (i + remain_seg_payload_len); ++k) data[k] ^= crc; print_hex_break(data + i, remain_seg_payload_len, 16); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------\n"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------\n"); i += remain_seg_payload_len; } // end with last segment @@ -331,7 +337,7 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff int wrp_len = (wrp - wrc); int remain_seg_payload_len = (card_size - 22 - wrp); - PrintAndLogEx(SUCCESS, "Unsegmented card - WRP: %02u, WRC: %02u, RD: %01u", + PrintAndLogEx(SUCCESS, "Unsegmented card - WRP: %02u WRC: %02u RD: %01u", wrp, wrc, (data[7] & 0x80) >> 7 @@ -346,20 +352,24 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff } if (hasWRC) { - PrintAndLogEx(SUCCESS, "WRC protected area: (I %d | WRC %d)", i, wrc); - PrintAndLogEx(NORMAL, "\nrow | data"); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------"); + PrintAndLogEx(SUCCESS, _CYAN_("WRC protected area:") " (I %d | WRC %d)", i, wrc); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "## | data | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); print_hex_break(data + i, wrc, 16); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------\n"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); + PrintAndLogEx(INFO, ""); i += wrc; } if (hasWRP) { - PrintAndLogEx(SUCCESS, "Remaining write protected area: (I %d | WRC %d | WRP %d | WRP_LEN %d)", i, wrc, wrp, wrp_len); - PrintAndLogEx(NORMAL, "\nrow | data"); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------"); + PrintAndLogEx(SUCCESS, _CYAN_("Remaining write protected area:") " (I %d | WRC %d | WRP %d | WRP_LEN %d)", i, wrc, wrp, wrp_len); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "## | data | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); print_hex_break(data + i, wrp_len, 16); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------\n"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); + PrintAndLogEx(INFO, ""); i += wrp_len; // Q: does this one work? @@ -374,11 +384,12 @@ static int decode_and_print_memory(uint16_t card_size, const uint8_t *input_buff } if (remain_seg_payload_len > 0) { - PrintAndLogEx(SUCCESS, "Remaining segment payload: (I %d | Remain LEN %d)", i, remain_seg_payload_len); - PrintAndLogEx(NORMAL, "\nrow | data"); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------"); + PrintAndLogEx(SUCCESS, _CYAN_("Remaining segment payload:") " (I %d | Remain LEN %d)", i, remain_seg_payload_len); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "## | data | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); print_hex_break(data + i, remain_seg_payload_len, 16); - PrintAndLogEx(NORMAL, "-----+------------------------------------------------\n"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------\n"); } } @@ -400,9 +411,11 @@ static int CmdLegicInfo(const char *Cmd) { void *argtable[] = { arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); CLIParserFree(ctx); uint16_t datalen = 0; @@ -418,7 +431,7 @@ static int CmdLegicInfo(const char *Cmd) { // allocate receiver buffer uint8_t *data = calloc(card.cardsize, sizeof(uint8_t)); - if (!data) { + if (data == NULL) { PrintAndLogEx(WARNING, "Cannot allocate memory"); return PM3_EMALLOC; } @@ -430,8 +443,15 @@ static int CmdLegicInfo(const char *Cmd) { return status; } - decode_and_print_memory(card.cardsize, data); + if (verbose) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); + print_hex_break(data, datalen, 16); + } + PrintAndLogEx(NORMAL, ""); + decode_and_print_memory(card.cardsize, data); free(data); return PM3_SUCCESS; } @@ -536,20 +556,21 @@ static int CmdLegicSim(const char *Cmd) { SendCommandNG(CMD_HF_LEGIC_SIMULATE, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - PrintAndLogEx(INFO, "Press pm3-button to abort simulation"); - bool keypress = kbd_enter_pressed(); - while (keypress == false) { - keypress = kbd_enter_pressed(); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to abort simulation"); + for (;;) { + if (kbd_enter_pressed()) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + PrintAndLogEx(DEBUG, "User aborted"); + break; + } if (WaitForResponseTimeout(CMD_HF_LEGIC_SIMULATE, &resp, 1500)) { break; } - } - if (keypress) - SendCommandNG(CMD_BREAK_LOOP, NULL, 0); PrintAndLogEx(INFO, "Done"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf legic list") "` to view trace log"); return PM3_SUCCESS; } @@ -682,7 +703,7 @@ static int CmdLegicCalcCrc(const char *Cmd) { switch (type) { case 16: - init_table(CRC_LEGIC); + init_table(CRC_LEGIC_16); PrintAndLogEx(SUCCESS, "Legic crc16: %X", crc16_legic(data, data_len, mcc[0])); break; default: @@ -782,6 +803,9 @@ void legic_chk_iv(uint32_t *iv) { void legic_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) { + PrintAndLogEx(INFO, "Uploading to emulator memory"); + PrintAndLogEx(INFO, "." NOLF); + // fast push mode g_conn.block_after_ACK = true; for (size_t i = offset; i < numofbytes; i += LEGIC_PACKET_SIZE) { @@ -800,7 +824,11 @@ void legic_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) { clearCommandBuffer(); SendCommandNG(CMD_HF_LEGIC_ESET, (uint8_t *)payload, sizeof(legic_packet_t) + len); free(payload); + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", numofbytes); } static int CmdLegicReader(const char *Cmd) { @@ -828,7 +856,7 @@ static int CmdLegicReader(const char *Cmd) { static int CmdLegicDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic dump", - "Read all memory from LEGIC Prime tags and saves to (bin/eml/json) dump file\n" + "Read all memory from LEGIC Prime tags and saves to (bin/json) dump file\n" "It autodetects card type (MIM22, MIM256, MIM1024)", "hf legic dump --> use UID as filename\n" "hf legic dump -f myfile \n" @@ -858,7 +886,7 @@ static int CmdLegicDump(const char *Cmd) { uint16_t dumplen = card.cardsize; legic_print_type(dumplen, 0); - PrintAndLogEx(SUCCESS, "Reading tag memory %d b...", dumplen); + PrintAndLogEx(SUCCESS, "Reading tag memory." NOLF); legic_packet_t *payload = calloc(1, sizeof(legic_packet_t)); payload->offset = 0; @@ -921,7 +949,7 @@ static int CmdLegicDump(const char *Cmd) { FillFileNameByUID(filename, data, "-dump", 4); } - pm3_save_dump(filename, data, readlen, jsfLegic, LEGIC_BLOCK_SIZE); + pm3_save_dump(filename, data, readlen, jsfLegic_v2); free(data); return PM3_SUCCESS; } @@ -936,7 +964,7 @@ static int CmdLegicRestore(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Filename to restore"), + arg_str1("f", "file", "", "Specify a filename to restore"), arg_lit0(NULL, "ob", "obfuscate dump data (xor with MCC)"), arg_param_end }; @@ -1077,10 +1105,11 @@ static int CmdLegicELoad(const char *Cmd) { legic_xor(dump, bytes_read); } - PrintAndLogEx(SUCCESS, "Uploading to emulator memory"); legic_seteml(dump, 0, bytes_read); free(dump); + + PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf legic sim -h`")); PrintAndLogEx(SUCCESS, "Done!"); return PM3_SUCCESS; } @@ -1088,7 +1117,7 @@ static int CmdLegicELoad(const char *Cmd) { static int CmdLegicESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf legic esave", - "Saves a (bin/eml/json) dump file of emulator memory", + "Saves a (bin/json) dump file of emulator memory", "hf legic esave --> uses UID as filename\n" "hf legic esave -f myfile --22\n" "hf legic esave -f myfile --22 --de\n" @@ -1157,7 +1186,7 @@ static int CmdLegicESave(const char *Cmd) { legic_xor(data, numofbytes); } - pm3_save_dump(filename, data, numofbytes, jsfLegic, LEGIC_BLOCK_SIZE); + pm3_save_dump(filename, data, numofbytes, jsfLegic_v2); return PM3_SUCCESS; } @@ -1174,6 +1203,7 @@ static int CmdLegicEView(const char *Cmd) { arg_lit0(NULL, "22", "LEGIC Prime MIM22"), arg_lit0(NULL, "256", "LEGIC Prime MIM256 (def)"), arg_lit0(NULL, "1024", "LEGIC Prime MIM1024"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1181,6 +1211,7 @@ static int CmdLegicEView(const char *Cmd) { bool m1 = arg_get_lit(ctx, 1); bool m2 = arg_get_lit(ctx, 2); bool m3 = arg_get_lit(ctx, 3); + bool verbose = arg_get_lit(ctx, 4); CLIParserFree(ctx); // validations @@ -1212,10 +1243,16 @@ static int CmdLegicEView(const char *Cmd) { return PM3_ETIMEOUT; } + if (verbose) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); + print_hex_break(dump, bytes, 16); + } + PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii"); - PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); - print_hex_break(dump, bytes, 16); + decode_and_print_memory(bytes, dump); + free(dump); return PM3_SUCCESS; } @@ -1226,15 +1263,36 @@ static int CmdLegicEInfo(const char *Cmd) { CLIParserInit(&ctx, "hf legic einfo", "It decodes and displays emulator memory", "hf legic einfo\n" + "hf legic eview --22\n" ); void *argtable[] = { arg_param_begin, + arg_lit0(NULL, "22", "LEGIC Prime MIM22"), + arg_lit0(NULL, "256", "LEGIC Prime MIM256 (def)"), + arg_lit0(NULL, "1024", "LEGIC Prime MIM1024"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + bool m1 = arg_get_lit(ctx, 1); + bool m2 = arg_get_lit(ctx, 2); + bool m3 = arg_get_lit(ctx, 3); CLIParserFree(ctx); + // validations + if (m1 + m2 + m3 > 1) { + PrintAndLogEx(WARNING, "Only specify one LEGIC Prime Type"); + return PM3_EINVARG; + } else if (m1 + m2 + m3 == 0) { + m2 = true; + } + size_t card_size = LEGIC_PRIME_MIM256; + if (m1) + card_size = LEGIC_PRIME_MIM22; + else if (m2) + card_size = LEGIC_PRIME_MIM256; + else if (m3) + card_size = LEGIC_PRIME_MIM1024; uint8_t *dump = calloc(card_size, sizeof(uint8_t)); if (dump == NULL) { @@ -1346,13 +1404,15 @@ static int CmdLegicView(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int fnlen = 0; char filename[FILE_PATH_SIZE]; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); // read dump file @@ -1363,14 +1423,15 @@ static int CmdLegicView(const char *Cmd) { return res; } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii"); - PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); - print_hex_break(dump, bytes_read, 16); + if (verbose) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "## | 0 1 2 3 4 5 6 7 8 9 A B C D E F | ascii"); + PrintAndLogEx(INFO, "---+-------------------------------------------------+-----------------"); + print_hex_break(dump, bytes_read, 16); + } PrintAndLogEx(NORMAL, ""); decode_and_print_memory(bytes_read, dump); - free(dump); return PM3_SUCCESS; } diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index 7207e50f9..2765363fd 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -70,7 +70,7 @@ static uint8_t *gs_mfuc_key = NULL; 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 (isResponse && (n == 5)) return 2; if (d[1] == 0x50 && d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT && d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { @@ -189,7 +189,7 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i if (cmd[1] == 0x01 && cmdsize == 7) { snprintf(exp, size, "ECP1"); return PM3_SUCCESS; - } else if (cmd[1] == 0x02 && cmdsize == (cmd[2] & 0x0f) + 7) { + } else if (cmd[1] == 0x02 && cmdsize == (cmd[2] & 0x0F) + 7) { // Byte 3 is the reader type switch (cmd[3]) { case 0x01: @@ -201,6 +201,9 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i case 0x03: snprintf(exp, size, "ECP2 (Identity)"); break; + case 0x05: + snprintf(exp, size, "ECP2 (AirDrop)"); + break; default: snprintf(exp, size, "ECP2"); break; @@ -270,11 +273,13 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i MifareAuthState = masNone; break; case ISO14443A_CMD_RATS: - snprintf(exp, size, "RATS"); + snprintf(exp, size, "RATS - FSDI=%x, CID=%x", (cmd[1] & 0xF0) >> 4, (cmd[1] & 0x0F)); break; + /* Actually, PPSS is Dx case ISO14443A_CMD_PPS: snprintf(exp, size, "PPS"); break; + */ case ISO14443A_CMD_OPTS: snprintf(exp, size, "OPTIONAL TIMESLOT"); break; @@ -420,10 +425,14 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool i snprintf(exp, size, "FAST WRITE (" _MAGENTA_("%d-%d") ")", cmd[1], cmd[2]); else snprintf(exp, size, "?"); - break; + default: - return PM3_ESOFT; + if ((cmd[0] & 0xF0) == 0xD0 && (cmdsize == 4 || cmdsize == 5)) { + snprintf(exp, size, "PPS - CID=%x", cmd[0] & 0x0F) ; + } else { + return PM3_ESOFT; + } } } else { if (gs_mfuc_state == 1) { @@ -496,7 +505,7 @@ void annotateIclass(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool uint8_t key[8]; if (check_known_default(csn, epurse, rmac, tmac, key)) { - snprintf(exp, size, "CHECK ( %s )", sprint_hex_inrow(key, 8)); + snprintf(exp, size, "CHECK ( " _GREEN_("%s") " )", sprint_hex_inrow(key, 8)); } else { snprintf(exp, size, "CHECK"); } @@ -1014,6 +1023,9 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { snprintf(exp, size, "CLEAR RECORD FILE"); } break; + case MFDES_NOTIFY_TRANSACTION_SUCCESS: + snprintf(exp, size, "NOTIFY TRANSACTION SUCCESS (ECP)"); + break; case MFDES_COMMIT_TRANSACTION: snprintf(exp, size, "COMMIT TRANSACTION"); break; @@ -1194,6 +1206,361 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { } } +// MIFARE Plus + +// returns the message to print for a given opcode. +const char *mfpGetAnnotationForCode(uint8_t code) { + struct mfp_code_msg { + uint8_t code; + const char *annotation; + } messages[] = { + { MFP_GETVERSION, "GET VERSION"}, + { MFP_ADDITIONALFRAME, "NEXT FRAME"}, + { MFP_AUTHENTICATENONFIRST, "FOLLOWING AUTH"}, + { MFP_AUTHENTICATECONTINUE, "SECOND AUTH STEP"}, + { MFP_RESETAUTH, "RESET AUTH"}, + { MFP_COMMITPERSO, "COMMIT PERSO"}, + { MFP_VCSUPPORTLASTISOL3, "CHECK VIRTUAL CARD"}, + { MFP_ISOSELECT, "SELECT VIRTUAL CARD"}, + { MFP_SETCONFIGSL1, "SET CONFIG SL1"}, + { MFP_MF_PERSONALIZEUIDUSAGE, "PERSONALIZE UID USAGE"}, + { MFP_READ_SIG, "READ SIGNATURE"}, + { MFDES_PREPARE_PC, "PREPARE PROXIMITY CHECK"}, + { MFDES_PROXIMITY_CHECK, "PROXIMITY CHECK"}, + { MFDES_VERIFY_PC, "VERIFY PROXIMITY CHECK"}, + { MFDES_COMMIT_READER_ID, "COMMIT READER ID"}, + { MFP_INCREMENTNOMAC, "INCREMENT"}, + { MFP_INCREMENTMAC, "INCREMENT"}, + { MFP_DECREMENTMAC, "DECREMENT"}, + { MFP_DECREMENTNOMAC, "DECREMENT"}, + { MFP_TRANSFERNOMAC, "TRANSFER"}, + { MFP_TRANSFERMAC, "TRANSFER"}, + { MFP_INCREMENTTRANSFERNOMAC, "INCREMENT, TRANSFER"}, + { MFP_INCREMENTTRANSFERMAC, "INCREMENT, TRANSFER"}, + { MFP_DECREMENTTRANSFERNOMAC, "DECREMENT, TRANSFER"}, + { MFP_DECREMENTTRANSFERMAC, "DECREMENT, TRANSFER"}, + { MFP_RESTORENOMAC, "RESTORE"}, + { MFP_RESTOREMAC, "RESTORE"}, + { 0, NULL} + } ; + + for (struct mfp_code_msg *p = messages ; p->annotation != NULL ; p++) { + if (p->code == code) { + return p->annotation ; + } + } + return NULL ; +} + +const char *mfpGetEncryptedForCode(uint8_t code) { + /* + encrypted |plain : bit 1 + 30 A0 0000 32 A2 0010 + 31 A1 0001 33 A3 0011 + 34 0100 36 0110 + 35 0101 37 0111 + */ + if ((code & 0x02) == 2) { + return "PLAIN" ; + } + return "ENCRYPTED" ; +} + +/* + response |command + NOMAC MAC UnMACed MACed + 30 31 34 30,A0 + 32 33 35 31,A1 + A0 A1 36 32,A2 + A2 A3 37 33,A3 + bit 0 is response: NOMAC if 0, MAC if 1 + bit 2 is command: UNMACed if 1, MACed if 0 +*/ +const char *mfpGetResponseMacedForCode(uint8_t code) { + if ((code & 0x01) == 0x00) { + return "NoMAC" ; + } + return "MAC" ; +} + +const char *mfpGetCommandMacedForCode(uint8_t code) { + if ((code & 0x04) == 0x04) { + return "UnMACed" ; + } + return "MACed" ; +} + +void annotateMfPlus(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { + + // If we are in Mifare Classic Authenticated mode, all the work has already be done elsewhere + if ((MifareAuthState != masNone) && (MifareAuthState != masError)) { + return ; + } + + // it's basically a ISO14443a tag, so try annotation from there + if (applyIso14443a(exp, size, cmd, cmdsize, false) == PM3_SUCCESS) { + return ; + } + + // ok this part is copy paste from annotateMfDesfire, it seems to work for MIFARE Plus also + if (((cmd[0] & 0xC0) == 0x00) && (cmdsize > 2)) { + + // PCB [CID] [NAD] [INF] CRC CRC + int pos = 1; + if ((cmd[0] & 0x08) == 0x08) // cid byte following + pos++; + + if ((cmd[0] & 0x04) == 0x04) // nad byte following + pos++; + + for (uint8_t i = 0; i < 2; i++, pos++) { + bool found_annotation = true; + + uint8_t *data = cmd + pos + 1; + // if the byte prior to the command is 90 the command is wrapped, so data starts 3 bytes later + if (i > 0 && cmd[pos - 1] == 0x90) { + data += 3; + } + uint8_t data_size = 0; + if (cmdsize > (data - cmd)) { + data_size = cmdsize - (data - cmd); + } + uint8_t opcode = cmd[pos] ; + switch (opcode) { + case MFP_AUTHENTICATEFIRST: + case MFP_AUTHENTICATEFIRST_VARIANT: + if (data_size > 1) { + // key : uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); + uint16_t uKeyNum = MemLeToUint2byte(data); + switch (uKeyNum & 0xf000) { + const char *stringdata; + default: + stringdata = "FIRST AUTH (Keynr 0x%04X: Key not identified)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uKeyNum); + break; + case 0x4000: + snprintf(exp, size, "FIRST AUTH (Keynr 0x%04X: %c sector %d)", uKeyNum, uKeyNum & 0x0001 ? 'B' : 'A', (uKeyNum - 0x4000) / 2); + break; + case 0xA000: // There are virtual card encryption and MACing keys, but this is NOT their place! + stringdata = "FIRST AUTH(Keynr 0x%04X: Proximity Check Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uKeyNum); + break; + case 0x9000: + switch (uKeyNum & 0xf) { + case 0x0: + stringdata = "FIRST AUTH (Keynr 0x%04X: Card Master Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uKeyNum); + break; + case 0x1: + stringdata = "FIRST AUTH (Keynr 0x%04X: Card Configuration Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uKeyNum); + break; + case 0x2: + stringdata = "FIRST AUTH(Keynr 0x%04X: SL2 Switch Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uKeyNum); + break; + case 0x3: + stringdata = "FIRST AUTH(Keynr 0x%04X: SL3 Switch Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uKeyNum); + break; + case 0x4: + stringdata = "FIRST AUTH(Keynr 0x%04X: SL1 Additional Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uKeyNum); + break; + case 0x6: + stringdata = "FIRST AUTH(Keynr 0x%04X: SL3 Sector Switch Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uKeyNum); + break; + case 0x7: + stringdata = "FIRST AUTH(Keynr 0x%04X: SL1SL3Mix Sector Switch Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uKeyNum); + break; + default: + stringdata = "FIRST AUTH(Keynr 0x%04X: Management Key not identified)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uKeyNum); + break; + } + } + } else { + snprintf(exp, size, "FIRST AUTH") ; + } + break; + + case MFP_WRITEPERSO: + if (data_size > 1) { + uint16_t uKeyNum = MemLeToUint2byte(data) ; + snprintf(exp, size, "WRITE PERSO (Addr 0x%04X)", uKeyNum); + } else { + snprintf(exp, size, "WRITE PERSO"); + } + break; + + case MFP_READENCRYPTEDNOMAC_MACED: + case MFP_READENCRYPTEDMAC_MACED: + case MFP_READENCRYPTEDNOMAC_UNMACED: + case MFP_READENCRYPTEDMAC_UNMACED: + case MFP_READPLAINNOMAC_MACED: + case MFP_READPLAINMAC_MACED: + case MFP_READPLAINNOMAC_UNMACED: + case MFP_READPLAINMAC_UNMACED: { + const char *encrypted = mfpGetEncryptedForCode(opcode) ; + const char *responseMaced = mfpGetResponseMacedForCode(opcode) ; + const char *commandMaced = mfpGetCommandMacedForCode(opcode) ; + + if (data_size > 2) { + uint16_t uBlockNum = MemLeToUint2byte(data) ; + uint8_t uQty = data[2] ; + if (uQty > 1) { + snprintf(exp, size, "READ %s(%u-%i) %s_%s", encrypted, uBlockNum, uBlockNum + uQty - 1, responseMaced, commandMaced); + } else { + snprintf(exp, size, "READ %s(%u) %s_%s", encrypted, uBlockNum, responseMaced, commandMaced); + } + } else { + snprintf(exp, size, "READ %s %s_%s ?", encrypted, responseMaced, commandMaced); + } + break; + } + + case MFP_WRITEPLAINNOMAC : + case MFP_WRITEPLAINMAC : + case MFP_WRITEENCRYPTEDNOMAC: + case MFP_WRITEENCRYPTEDMAC : { + const char *encrypted = mfpGetEncryptedForCode(opcode) ; + const char *responseMaced = mfpGetResponseMacedForCode(opcode) ; + + if (data_size > 1) { + uint16_t uBlockNum = MemLeToUint2byte(data); + switch (uBlockNum & 0xF000) { + const char *stringdata; + default: + stringdata = "WRITE %s(%u) %s"; + snprintf(exp, size, stringdata, encrypted, uBlockNum, responseMaced); + break; + case 0x4000: + snprintf(exp, size, "WRITE (Keynr 0x%04X: %c sector %d)", uBlockNum, uBlockNum & 0x0001 ? 'B' : 'A', (uBlockNum - 0x4000) / 2); + break; + case 0xA000: // There are virtual card encryption and MACing keys, but this is NOT their place! + stringdata = "WRITE(Keynr 0x%04X: Proximity Check Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0xB000: + case 0x9000: + if ((uBlockNum & 0x2000) == 0x2000) { + switch (uBlockNum & 0xf) { + default: + stringdata = "WRITE(Config %04X: Unidentified)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0x0: + stringdata = "WRITE(Config %04X: Config)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0x1: + stringdata = "WRITE(Config %04X: Virtual Card Installation ID)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0x2: + stringdata = "WRITE(Config %04X: ATS)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0x3: + stringdata = "WRITE(Config %04X: Field configuration)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + } + } else { + switch (uBlockNum & 0xf) { + default: + stringdata = "WRITE(Keynr 0x%04X: Management Key not identified)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0x0: + stringdata = "WRITE(Keynr 0x%04X: Card Master Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0x1: + stringdata = "WRITE(Keynr 0x%04X: Card Configuration Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0x2: + stringdata = "WRITE(Keynr 0x%04X: SL2 Switch Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0x3: + stringdata = "WRITE(Keynr 0x%04X: SL3 Switch Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0x4: + stringdata = "WRITE(Keynr 0x%04X: SL1 Additional Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0x6: + stringdata = "WRITE(Keynr 0x%04X: SL3 Sector Switch Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + case 0x7: + stringdata = "WRITE(Keynr 0x%04X: SL1SL3Mix Sector Switch Key)"; + snprintf(exp, strlen(stringdata) + 1, stringdata, uBlockNum); + break; + } + } + } + } else { + snprintf(exp, size, "WRITE %s %s ?", encrypted, responseMaced); + } + break; + } + + case MFP_INCREMENTNOMAC : + case MFP_INCREMENTMAC : + case MFP_DECREMENTNOMAC : + case MFP_DECREMENTMAC : + case MFP_TRANSFERNOMAC : + case MFP_TRANSFERMAC : + case MFP_INCREMENTTRANSFERNOMAC: + case MFP_INCREMENTTRANSFERMAC : + case MFP_DECREMENTTRANSFERNOMAC: + case MFP_DECREMENTTRANSFERMAC : + case MFP_RESTORENOMAC : + case MFP_RESTOREMAC : { + const char *responseMaced = mfpGetResponseMacedForCode(opcode) ; + const char *annotation = mfpGetAnnotationForCode(opcode) ; + if (annotation == NULL) { + //should not happen outside of default case: it means an entry is mising in mfpGetAnnotationForCode() + annotation = "?? MISSING OPCODE" ; + } + + if (data_size > 1) { + uint16_t uBlockNum = MemLeToUint2byte(data) ; + snprintf(exp, size, "%s(%u) %s", annotation, uBlockNum, responseMaced); + } else { + snprintf(exp, size, "%s %s ?", annotation, responseMaced); + } + break; + } + + default: { + // Messages for commands that do not need args are treated here + const char *annotation = mfpGetAnnotationForCode(opcode) ; + if (annotation != NULL) { + snprintf(exp, size, "%s", annotation) ; + } else { + found_annotation = false; + } + break; + } + } + if (found_annotation) { + break; + } + } + } else { + // anything else + snprintf(exp, size, "?"); + } +} + + /** 06 00 = INITIATE 0E xx = SELECT ID (xx = Chip-ID) @@ -1318,10 +1685,39 @@ void annotateSeos(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { // it's basically a ISO14443a tag, so try annotation from there if (applyIso14443a(exp, size, cmd, cmdsize, false) != PM3_SUCCESS) { -// switch (cmd[0]) { -// default: -// break; -// }; + int pos = 0; + switch (cmd[0]) { + case 2: + case 3: + pos = 2; + break; + case 0: + pos = 1; + break; + default: + pos = 2; + break; + } + + if (memcmp(cmd + pos, "\x00\xa4\x04\x00\x0a", 5) == 0) { + snprintf(exp, size, "SELECT AID"); + } + + if (memcmp(cmd + pos, "\x80\xA5\x04\x00", 4) == 0) { + snprintf(exp, size, "SELECT ADF / OID"); + } + + if (memcmp(cmd + pos, "\x00\x87\x00\x01\x04\x7c\x02\x81\x00", 9) == 0) { + snprintf(exp, size, "GET CHALLENGE"); + } + + if (memcmp(cmd + pos, "\x00\x87\x00\x01\x2c", 5) == 0) { + snprintf(exp, size, "MUTUAL AUTHENTICATION"); + } + + if (memcmp(cmd + pos, "\x0c\xcb\x3f\xff", 4) == 0) { + snprintf(exp, size, "GET DATA"); + } // apply ISO7816 annotations? // if (annotateIso7816(exp, size, cmd, cmdsize) == 0) { diff --git a/client/src/cmdhflist.h b/client/src/cmdhflist.h index 6499938eb..ab544a86f 100644 --- a/client/src/cmdhflist.h +++ b/client/src/cmdhflist.h @@ -56,6 +56,11 @@ void annotateIso7816(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); void annotateIso14443b(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); void annotateIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, bool is_response); void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); +const char *mfpGetAnnotationForCode(uint8_t code); +const char *mfpGetEncryptedForCode(uint8_t code); +const char *mfpGetResponseMacedForCode(uint8_t code); +const char *mfpGetCommandMacedForCode(uint8_t code); +void annotateMfPlus(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, const uint8_t *parity, uint8_t paritysize, bool isResponse); void annotateLTO(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index b5a8d1206..6ffe115c0 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -526,7 +526,7 @@ int infoLTO(bool verbose) { } static int CmdHfLTOList(const char *Cmd) { - return CmdTraceListAlias(Cmd, "hf lto", "lto"); + return CmdTraceListAlias(Cmd, "hf lto", "lto -c"); } int rdblLTO(uint8_t st_blk, uint8_t end_blk, bool verbose) { @@ -655,12 +655,12 @@ static int CmdHfLTOWriteBlock(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf lto wrbl", "Write data to block on LTO tag", - "hf lto wrbl --block 128 -d 0001020304050607080910111213141516171819202122232425262728293031"); + "hf lto wrbl --blk 128 -d 0001020304050607080910111213141516171819202122232425262728293031"); void *argtable[] = { arg_param_begin, arg_str1("d", "data", "", "32 bytes of data to write (64 hex symbols, no spaces)"), - arg_int1(NULL, "block", "", "The block number to write to as an integer"), + arg_int1(NULL, "blk", "", "The block number to write to as an integer"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -771,8 +771,7 @@ static int CmdHfLTODump(const char *Cmd) { char *fptr = filename + snprintf(filename, sizeof(filename), "hf-lto-"); FillFileNameByUID(fptr, dump, "-dump", 5); } - saveFile(filename, ".bin", dump, dump_len); - saveFileEML(filename, dump, dump_len, 32); + pm3_save_dump(filename, dump, dump_len, jsfLto); free(dump); return PM3_SUCCESS; } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index aebce5737..8a158f3b8 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -36,6 +36,11 @@ #include "crypto/libpcrypto.h" #include "wiegand_formats.h" #include "wiegand_formatutils.h" +#include "cmdhw.h" // set_fpga_mode +#include "loclass/cipherutils.h" // BitstreamOut_t +#include "proxendian.h" +#include "preferences.h" +#include "mifare/gen4.h" static int CmdHelp(const char *Cmd); @@ -62,7 +67,8 @@ int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, in // 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"}, + {"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, + {"Manufacturer MIFARE Classic / QL88", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, }; uint8_t i; @@ -89,7 +95,7 @@ int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, in return PM3_ESOFT; } - PrintAndLogEx(INFO, " IC signature public key name: %s", nxp_mfc_public_keys[i].desc); + PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%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)); @@ -101,8 +107,8 @@ static int GetHFMF14AUID(uint8_t *uid, int *uidlen) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(DEBUG, "iso14443a card select failed"); DropField(); return PM3_ERFTRANS; } @@ -153,26 +159,37 @@ static int initSectorTable(sector_t **src, size_t items) { static void decode_print_st(uint16_t blockno, uint8_t *data) { if (mfIsSectorTrailer(blockno)) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "----------------------- " _CYAN_("Sector trailer decoder") " -----------------------"); + PrintAndLogEx(INFO, "-------------------------- " _CYAN_("Sector trailer decoder") " --------------------------"); PrintAndLogEx(INFO, "key A........ " _GREEN_("%s"), sprint_hex_inrow(data, 6)); PrintAndLogEx(INFO, "acr.......... " _GREEN_("%s"), sprint_hex_inrow(data + 6, 3)); PrintAndLogEx(INFO, "user / gpb... " _GREEN_("%02x"), data[9]); PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, " # | Access rights"); - PrintAndLogEx(INFO, "----+-----------------------------------------------------------------"); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, " # | access rights"); + PrintAndLogEx(INFO, "----+-----------------------------------------------------------------------"); if (mfValidateAccessConditions(&data[6]) == false) { PrintAndLogEx(WARNING, _RED_("Invalid Access Conditions")); } + int bln = mfFirstBlockOfSector(mfSectorNum(blockno)); int blinc = (mfNumBlocksPerSector(mfSectorNum(blockno)) > 4) ? 5 : 1; for (int i = 0; i < 4; i++) { PrintAndLogEx(INFO, "%3d%c| " _YELLOW_("%s"), bln, ((blinc > 1) && (i < 3) ? '+' : ' '), mfGetAccessConditionsDesc(i, &data[6])); bln += blinc; + + if (i == 3) { + uint8_t cond = mf_get_accesscondition(i, &data[6]); + if (cond == 0 || cond == 1 || cond == 2) { + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "OBS! Key B is readable, it SHALL NOT be able to authenticate on original MFC"); + } + } } - PrintAndLogEx(INFO, "----------------------------------------------------------------------"); + + + PrintAndLogEx(INFO, "----------------------------------------------------------------------------"); PrintAndLogEx(NORMAL, ""); } } @@ -207,7 +224,7 @@ static char GetFormatFromSector(uint8_t sectors) { } } -static bool mfc_value(const uint8_t *d, int32_t *val) { +bool mfc_value(const uint8_t *d, int32_t *val) { // values int32_t a = (int32_t)MemLeToUint4byte(d); uint32_t a_inv = MemLeToUint4byte(d + 4); @@ -225,11 +242,39 @@ static bool mfc_value(const uint8_t *d, int32_t *val) { return val_checks; } -static void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose) { +void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose) { + if (blockno == 0) { - PrintAndLogEx(INFO, "%3d | " _RED_("%s"), blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + char ascii[24] = {0}; + ascii_to_buffer((uint8_t *)ascii, d, MFBLOCK_SIZE, sizeof(ascii) - 1, 1); + PrintAndLogEx(INFO, "%3d | " _RED_("%s") "| " _RED_("%s"), + blockno, + sprint_hex(d, MFBLOCK_SIZE), + ascii + ); } else if (mfIsSectorTrailer(blockno)) { - PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s"), blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + + char keya[26] = {0}; + hex_to_buffer((uint8_t *)keya, d, MIFARE_KEY_SIZE, sizeof(keya) - 1, 0, 1, true); + + char acl[20] = {0}; + hex_to_buffer((uint8_t *)acl, d + MIFARE_KEY_SIZE, 3, sizeof(acl) - 1, 0, 1, true); + + char keyb[26] = {0}; + hex_to_buffer((uint8_t *)keyb, d + 10, MIFARE_KEY_SIZE, sizeof(keyb) - 1, 0, 1, true); + + char ascii[24] = {0}; + ascii_to_buffer((uint8_t *)ascii, d, MFBLOCK_SIZE, sizeof(ascii) - 1, 1); + + PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s") _MAGENTA_("%s") "%02X " _YELLOW_("%s") "| " _YELLOW_("%s"), + blockno, + keya, + acl, + d[9], + keyb, + ascii + ); + } else { int32_t value = 0; if (verbose && mfc_value(d, &value)) { @@ -249,9 +294,38 @@ static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) { } if (blockno == 0) { - PrintAndLogEx(INFO, "%s| %3d | " _RED_("%s"), secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + char ascii[24] = {0}; + ascii_to_buffer((uint8_t *)ascii, d, MFBLOCK_SIZE, sizeof(ascii) - 1, 1); + PrintAndLogEx(INFO, "%s| %3d | " _RED_("%s") "| " _RED_("%s"), + secstr, + blockno, + sprint_hex(d, MFBLOCK_SIZE), + ascii + ); + } else if (mfIsSectorTrailer(blockno)) { - PrintAndLogEx(INFO, "%s| %3d | " _YELLOW_("%s"), secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); + + char keya[26] = {0}; + hex_to_buffer((uint8_t *)keya, d, MIFARE_KEY_SIZE, sizeof(keya) - 1, 0, 1, true); + + char acl[20] = {0}; + hex_to_buffer((uint8_t *)acl, d + MIFARE_KEY_SIZE, 3, sizeof(acl) - 1, 0, 1, true); + + char keyb[26] = {0}; + hex_to_buffer((uint8_t *)keyb, d + 10, MIFARE_KEY_SIZE, sizeof(keyb) - 1, 0, 1, true); + + char ascii[24] = {0}; + ascii_to_buffer((uint8_t *)ascii, d, MFBLOCK_SIZE, sizeof(ascii) - 1, 1); + + PrintAndLogEx(INFO, "%s| %3d | " _YELLOW_("%s") _MAGENTA_("%s") "%02X " _YELLOW_("%s") "| " _YELLOW_("%s"), + secstr, + blockno, + keya, + acl, + d[9], + keyb, + ascii + ); } else { int32_t value = 0; if (verbose && mfc_value(d, &value)) { @@ -280,8 +354,8 @@ static void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose) { PrintAndLogEx(NORMAL, ""); } +// assumes n is in number of blocks 0..255 static int mf_print_keys(uint16_t n, uint8_t *d) { - uint8_t sectors = 0; switch (n) { case MIFARE_MINI_MAXBLOCK: @@ -308,18 +382,69 @@ static int mf_print_keys(uint16_t n, uint8_t *d) { } for (uint16_t i = 0; i < n; i++) { - if (mfIsSectorTrailer(i)) { - e_sector[mfSectorNum(i)].foundKey[0] = 1; - e_sector[mfSectorNum(i)].Key[0] = bytes_to_num(d + (i * MFBLOCK_SIZE), MIFARE_KEY_SIZE); - e_sector[mfSectorNum(i)].foundKey[1] = 1; - e_sector[mfSectorNum(i)].Key[1] = bytes_to_num(d + (i * MFBLOCK_SIZE) + 10, MIFARE_KEY_SIZE); + if (mfIsSectorTrailer(i) == false) { + continue; } + // zero based index... + uint8_t lookup = mfSectorNum(i); + uint8_t sec = MIN(sectors - 1, lookup); + e_sector[sec].foundKey[0] = 1; + e_sector[sec].Key[0] = bytes_to_num(d + (i * MFBLOCK_SIZE), MIFARE_KEY_SIZE); + e_sector[sec].foundKey[1] = 1; + e_sector[sec].Key[1] = bytes_to_num(d + (i * MFBLOCK_SIZE) + 10, MIFARE_KEY_SIZE); } printKeyTable(sectors, e_sector); free(e_sector); return PM3_SUCCESS; } +// MFC dump , extract and save the keys to key file +// assumes n is in number of blocks 0..255 +static int mf_save_keys_from_arr(uint16_t n, uint8_t *d) { + uint8_t sectors = 0; + switch (n) { + case MIFARE_MINI_MAXBLOCK: + sectors = MIFARE_MINI_MAXSECTOR; + break; + case MIFARE_2K_MAXBLOCK: + sectors = MIFARE_2K_MAXSECTOR; + break; + case MIFARE_4K_MAXBLOCK: + sectors = MIFARE_4K_MAXSECTOR; + break; + case MIFARE_1K_MAXBLOCK: + default: + sectors = MIFARE_1K_MAXSECTOR; + break; + } + + uint16_t keysize = 2 * MIFARE_KEY_SIZE * sectors; + + uint8_t *keys = calloc(keysize, sizeof(uint8_t)); + if (keys == NULL) { + return PM3_EMALLOC; + } + + uint8_t sector = 0; + for (uint16_t i = 0; i < n; i++) { + if (mfIsSectorTrailer(i)) { + // key A offset in ST block + memcpy(keys + (MIFARE_KEY_SIZE * sector), d + (i * MFBLOCK_SIZE), MIFARE_KEY_SIZE); + + // key B offset in ST block + memcpy(keys + (MIFARE_KEY_SIZE * sectors) + (MIFARE_KEY_SIZE * sector), d + (i * MFBLOCK_SIZE) + 10, MIFARE_KEY_SIZE); + + sector++; + } + } + + char fn[FILE_PATH_SIZE] = {0}; + snprintf(fn, sizeof(fn), "hf-mf-%s-key", sprint_hex_inrow(d, 4)); + saveFile(fn, ".bin", keys, keysize); + free(keys); + return PM3_SUCCESS; +} + /* static void mf_print_values(uint16_t n, uint8_t *d) { @@ -343,7 +468,7 @@ static void mf_print_values(uint16_t n, uint8_t *d) { } */ -static void mf_print_sector_hdr(uint8_t sector) { +void mf_print_sector_hdr(uint8_t sector) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, " # | sector " _GREEN_("%02d") " / " _GREEN_("0x%02X") " | ascii", sector, sector); PrintAndLogEx(INFO, "----+-------------------------------------------------+-----------------"); @@ -352,7 +477,7 @@ static void mf_print_sector_hdr(uint8_t sector) { static bool mf_write_block(const uint8_t *key, uint8_t keytype, uint8_t blockno, uint8_t *block) { uint8_t data[26]; - memcpy(data, key, MFKEY_SIZE); + memcpy(data, key, MIFARE_KEY_SIZE); memcpy(data + 10, block, MFBLOCK_SIZE); clearCommandBuffer(); @@ -363,9 +488,10 @@ static bool mf_write_block(const uint8_t *key, uint8_t keytype, uint8_t blockno, return false; } - return (resp.oldarg[0] & 0xff); + return ((resp.oldarg[0] & 0xff) == 1); } +// assumes n is in number of blocks 0..255 static void mf_analyse_acl(uint16_t n, uint8_t *d) { for (uint16_t b = 3; b < n; b++) { @@ -426,7 +552,7 @@ static int mf_analyse_st_block(uint8_t blockno, uint8_t *block, bool force) { return PM3_EINVARG; } } else { - PrintAndLogEx(SUCCESS, "ST passed checks, continuing..."); + PrintAndLogEx(SUCCESS, "ST checks ( " _GREEN_("ok") " )"); } return PM3_SUCCESS; @@ -438,20 +564,20 @@ static int mf_analyse_st_block(uint8_t blockno, uint8_t *block, bool force) { * @param numSectors: size of the card * @param keyFileName: filename containing keys or NULL. */ -static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t numSectors, char *keyfn){ +static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t numSectors, char *keyfn) { // Select card to get UID/UIDLEN/ATQA/SAK information clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "iso14443a card select timeout"); + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); return PM3_ETIMEOUT; } uint64_t select_status = resp.oldarg[0]; if (select_status == 0) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); + PrintAndLogEx(DEBUG, "iso14443a card select failed"); return PM3_SUCCESS; } @@ -471,10 +597,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n size_t alen = 0, blen = 0; uint8_t *keyA, *keyB; - if (loadFileBinaryKey(keyfn, "", (void**)&keyA, (void**)&keyB, &alen, &blen) != PM3_SUCCESS) { - if (keyA) { - free(keyA); - } + if (loadFileBinaryKey(keyfn, "", (void **)&keyA, (void **)&keyB, &alen, &blen) != PM3_SUCCESS) { free(fptr); return PM3_ESOFT; } @@ -544,12 +667,13 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n current_key = MF_KEY_A; uint8_t data_area = (sectorNo < 32) ? blockNo : blockNo / 5; if (rights[sectorNo][data_area] == 0x07) { // no key would work - PrintAndLogEx(WARNING, "access rights do not allow reading of sector %2d block %3d, skipping", sectorNo, blockNo); + PrintAndLogEx(WARNING, "access rights do not allow reading of sector " _YELLOW_("%2d") " block " _YELLOW_("%3d") ", skipping", sectorNo, blockNo); continue; } for (uint8_t tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) { - if (mfIsSectorTrailer(blockNo)) { + + if (mfIsSectorTrailerBasedOnBlocks(sectorNo, blockNo)) { // sector trailer. At least the Access Conditions can always be read with key A. payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo; @@ -561,7 +685,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); } else { // data block. Check if it can be read with key A or key B - if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) { + if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) { // only key B would work payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo; payload.keytype = MF_KEY_B; @@ -571,7 +695,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); } else { - // key A would work + // key A would work payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo; payload.keytype = current_key; memcpy(payload.key, (current_key == MF_KEY_A) ? keyA + (sectorNo * MIFARE_KEY_SIZE) : keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); @@ -603,16 +727,16 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n uint8_t *data = resp.data.asBytes; - if (mfIsSectorTrailer(blockNo)) { + if (mfIsSectorTrailerBasedOnBlocks(sectorNo, blockNo)) { // sector trailer. Fill in the keys. - memcpy(data , keyA + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + memcpy(data, keyA + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); memcpy(data + 10, keyB + (sectorNo * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); } memcpy(carddata + (MFBLOCK_SIZE * (mfFirstBlockOfSector(sectorNo) + blockNo)), data, MFBLOCK_SIZE); - PrintAndLogEx(SUCCESS, "successfully read block %2d of sector %2d.", blockNo, sectorNo); + PrintAndLogEx(INPLACE, "successfully read block " _YELLOW_("%2d") " of sector " _YELLOW_("%2d"), blockNo, sectorNo); } else { - PrintAndLogEx(FAILED, "could not read block %2d of sector %2d", blockNo, sectorNo); + PrintAndLogEx(FAILED, "\ncould not read block %2d of sector %2d", blockNo, sectorNo); } } else { PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d of sector %2d.", blockNo, sectorNo); @@ -628,6 +752,77 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n return PM3_SUCCESS ; } +static int mfLoadKeys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey, int userkeylen, const char *filename, int fnlen) { + // Handle Keys + *pkeycnt = 0; + *pkeyBlock = NULL; + uint8_t *p; + // Handle user supplied key + // (it considers *pkeycnt and *pkeyBlock as possibly non-null so logic can be easily reordered) + if (userkeylen >= MIFARE_KEY_SIZE) { + int numKeys = userkeylen / MIFARE_KEY_SIZE; + p = realloc(*pkeyBlock, numKeys * MIFARE_KEY_SIZE); + if (!p) { + PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + + memcpy(*pkeyBlock, userkey, numKeys * MIFARE_KEY_SIZE); + + for (int i = 0; i < numKeys; i++) { + PrintAndLogEx(INFO, "[" _YELLOW_("%d") "] key %s", i, sprint_hex(*pkeyBlock + i * MIFARE_KEY_SIZE, MIFARE_KEY_SIZE)); + } + *pkeycnt += numKeys; + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " keys supplied by user ", numKeys); + } + + // Handle default keys + p = realloc(*pkeyBlock, (*pkeycnt + ARRAYLEN(g_mifare_default_keys)) * MIFARE_KEY_SIZE); + if (!p) { + PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + // Copy default keys to list + for (int i = 0; i < ARRAYLEN(g_mifare_default_keys); i++) { + num_to_bytes(g_mifare_default_keys[i], MIFARE_KEY_SIZE, (uint8_t *)(*pkeyBlock + (*pkeycnt + i) * MIFARE_KEY_SIZE)); + PrintAndLogEx(DEBUG, "[" _YELLOW_("%d") "] key %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * MIFARE_KEY_SIZE, MIFARE_KEY_SIZE)); + } + *pkeycnt += ARRAYLEN(g_mifare_default_keys); + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%zu") " keys from hardcoded default array", ARRAYLEN(g_mifare_default_keys)); + + + // Handle user supplied dictionary file + if (fnlen > 0) { + uint32_t loaded_numKeys = 0; + uint8_t *keyBlock_tmp = NULL; + int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock_tmp, MIFARE_KEY_SIZE, &loaded_numKeys); + if (res != PM3_SUCCESS || loaded_numKeys == 0 || *pkeyBlock == NULL) { + PrintAndLogEx(FAILED, "An error occurred while loading the dictionary!"); + free(keyBlock_tmp); + free(*pkeyBlock); + return PM3_EFILE; + } else { + p = realloc(*pkeyBlock, (*pkeycnt + loaded_numKeys) * MIFARE_KEY_SIZE); + if (!p) { + PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + free(keyBlock_tmp); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + memcpy(*pkeyBlock + *pkeycnt * MIFARE_KEY_SIZE, keyBlock_tmp, loaded_numKeys * MIFARE_KEY_SIZE); + *pkeycnt += loaded_numKeys; + free(keyBlock_tmp); + } + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%u") " keys from dictionary", loaded_numKeys); + } + return PM3_SUCCESS; +} + static int CmdHF14AMfAcl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf acl", @@ -739,7 +934,8 @@ static int CmdHF14AMfWrBl(const char *Cmd) { " \n" "`--force` param is used to override warnings like bad ACL and BLOCK 0 writes.\n" " if not specified, it will exit if detected", - "hf mf wrbl --blk 1 -k FFFFFFFFFFFF -d 000102030405060708090a0b0c0d0e0f" + "hf mf wrbl --blk 1 -d 000102030405060708090a0b0c0d0e0f\n" + "hf mf wrbl --blk 1 -k A0A1A2A3A4A5 -d 000102030405060708090a0b0c0d0e0f\n" ); void *argtable[] = { arg_param_begin, @@ -767,7 +963,7 @@ static int CmdHF14AMfWrBl(const char *Cmd) { bool force = arg_get_lit(ctx, 4); int keylen = 0; - uint8_t key[6] = {0}; + uint8_t key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; CLIGetHexWithReturn(ctx, 5, key, &keylen); uint8_t block[MFBLOCK_SIZE] = {0x00}; @@ -775,6 +971,11 @@ static int CmdHF14AMfWrBl(const char *Cmd) { CLIGetHexWithReturn(ctx, 6, block, &blen); CLIParserFree(ctx); + if (keylen && keylen != 6) { + PrintAndLogEx(WARNING, "Key must be 12 hex digits. Got %d", keylen); + return PM3_EINVARG; + } + if (blen != MFBLOCK_SIZE) { PrintAndLogEx(WARNING, "block data must include 16 HEX bytes. Got %i", blen); return PM3_EINVARG; @@ -833,7 +1034,8 @@ static int CmdHF14AMfRdBl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf rdbl", "Read MIFARE Classic block", - "hf mf rdbl --blk 0 -k FFFFFFFFFFFF\n" + "hf mf rdbl --blk 0\n" + "hf mf rdbl --blk 0 -k A0A1A2A3A4A5\n" "hf mf rdbl --blk 3 -v -> get block 3, decode sector trailer\n" ); void *argtable[] = { @@ -858,11 +1060,16 @@ static int CmdHF14AMfRdBl(const char *Cmd) { } int keylen = 0; - uint8_t key[6] = {0}; + uint8_t key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; CLIGetHexWithReturn(ctx, 4, key, &keylen); bool verbose = arg_get_lit(ctx, 5); CLIParserFree(ctx); + if (keylen && keylen != 6) { + PrintAndLogEx(WARNING, "Key must be 12 hex digits. Got %d", keylen); + return PM3_EINVARG; + } + if (b > 255) { return PM3_EINVARG; } @@ -888,7 +1095,8 @@ static int CmdHF14AMfRdSc(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf rdsc", "Read MIFARE Classic sector", - "hf mf rdsc -s 0 -k FFFFFFFFFFFF\n" + "hf mf rdsc -s 0\n" + "hf mf rdsc -s 0 -k A0A1A2A3A4A5\n" ); void *argtable[] = { arg_param_begin, @@ -910,17 +1118,23 @@ static int CmdHF14AMfRdSc(const char *Cmd) { } int keylen = 0; - uint8_t key[6] = {0}; + uint8_t key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; CLIGetHexWithReturn(ctx, 3, key, &keylen); int s = arg_get_int_def(ctx, 4, 0); bool verbose = arg_get_lit(ctx, 5); CLIParserFree(ctx); - if (s > MIFARE_4K_MAXSECTOR) { + if (keylen && keylen != 6) { + PrintAndLogEx(WARNING, "Key must be 12 hex digits. Got %d", keylen); + return PM3_EINVARG; + } + + if (s >= MIFARE_4K_MAXSECTOR) { PrintAndLogEx(WARNING, "Sector number must be less then 40"); return PM3_EINVARG; } + uint8_t sector = (uint8_t)s; uint16_t sc_size = mfNumBlocksPerSector(sector) * MFBLOCK_SIZE; @@ -968,7 +1182,7 @@ static int FastDumpWithEcFill(uint8_t numsectors) { } if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(INFO, "fast dump reported back failure w KEY A, swapping to KEY B"); + PrintAndLogEx(FAILED, "fast dump reported back failure w KEY A, swapping to KEY B"); // ecfill key B payload.keytype = MF_KEY_B; @@ -982,8 +1196,8 @@ static int FastDumpWithEcFill(uint8_t numsectors) { } if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(INFO, "fast dump reported back failure w KEY B"); - PrintAndLogEx(INFO, "Dump file is " _RED_("PARTIAL") " complete"); + PrintAndLogEx(FAILED, "fast dump reported back failure w KEY B"); + PrintAndLogEx(FAILED, "Dump file is " _RED_("PARTIAL") " complete"); } } return PM3_SUCCESS; @@ -992,7 +1206,7 @@ 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" + "Dump MIFARE Classic tag to file (bin/json)\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" @@ -1002,7 +1216,7 @@ static int CmdHF14AMfDump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_str0("k", "keys", "", "filename of keys"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), @@ -1050,7 +1264,7 @@ static int CmdHF14AMfDump(const char *Cmd) { } else if (m2) { numSectors = MIFARE_2K_MAXSECTOR; bytes = MIFARE_2K_MAX_BYTES; - } else if (m4) { + } else if (m4) { numSectors = MIFARE_4K_MAXSECTOR; bytes = MIFARE_4K_MAX_BYTES; } else { @@ -1060,7 +1274,7 @@ static int CmdHF14AMfDump(const char *Cmd) { // read card iso14a_card_select_t card ; - uint8_t *mem = calloc(MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, sizeof(uint8_t)); + uint8_t *mem = calloc(MIFARE_4K_MAX_BYTES, sizeof(uint8_t)); if (mem == NULL) { PrintAndLogEx(ERR, "failed to allocate memory"); return PM3_EMALLOC; @@ -1092,14 +1306,7 @@ static int CmdHF14AMfDump(const char *Cmd) { free(fptr); } - saveFile(dataFilename, ".bin", mem, bytes); - saveFileEML(dataFilename, mem, bytes, MFBLOCK_SIZE); - - iso14a_mf_extdump_t xdump; - xdump.card_info = card; - xdump.dump = mem; - xdump.dumplen = bytes; - saveFileJSON(dataFilename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); + pm3_save_mf_dump(dataFilename, mem, bytes, jsfCardMemory); free(mem); return PM3_SUCCESS; } @@ -1133,7 +1340,7 @@ static int CmdHF14AMfRestore(const char *Cmd) { arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_str0("u", "uid", "", "uid, (4|7|10 hex bytes)"), - arg_str0("f", "file", "", "specify dump filename (bin/eml/json)"), + arg_str0("f", "file", "", "specify a filename for dump file"), arg_str0("k", "kfn", "", "key filename"), arg_lit0(NULL, "ka", "use specified keyfile to authenticate"), arg_lit0(NULL, "force", "override warnings"), @@ -1210,59 +1417,47 @@ static int CmdHF14AMfRestore(const char *Cmd) { free(fptr); } - FILE *f; - if ((f = fopen(keyfilename, "rb")) == NULL) { - PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyfilename); - return PM3_EFILE; + // + size_t alen = 0, blen = 0; + uint8_t *keyA, *keyB; + if (loadFileBinaryKey(keyfilename, "", (void **)&keyA, (void **)&keyB, &alen, &blen) != PM3_SUCCESS) { + return PM3_ESOFT; } - // key arrays - uint8_t keyA[40][6]; - uint8_t keyB[40][6]; - - // read key file - size_t bytes_read; - for (uint8_t s = 0; s < sectors; s++) { - bytes_read = fread(keyA[s], 1, 6, f); - if (bytes_read != 6) { - PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyfilename); - fclose(f); - return PM3_EFILE; - } - } - - for (uint8_t s = 0; s < sectors; s++) { - bytes_read = fread(keyB[s], 1, 6, f); - if (bytes_read != 6) { - PrintAndLogEx(ERR, "File reading error " _YELLOW_("%s"), keyfilename); - fclose(f); - return PM3_EFILE; - } - } - fclose(f); + PrintAndLogEx(INFO, "Using key file `" _YELLOW_("%s") "`", keyfilename); // try reading card uid and create filename if (datafnlen == 0) { char *fptr = GenerateFilename("hf-mf-", "-dump.bin"); - if (fptr == NULL) + if (fptr == NULL) { + if (keyA) { + free(keyA); + } + if (keyB) { + free(keyB); + } return PM3_ESOFT; - + } strcpy(datafilename, fptr); free(fptr); } // read dump file uint8_t *dump = NULL; - bytes_read = 0; + size_t bytes_read = 0; int res = pm3_load_dump(datafilename, (void **)&dump, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK)); if (res != PM3_SUCCESS) { + free(keyA); + free(keyB); return res; } // default authentication key uint8_t default_key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - PrintAndLogEx(INFO, "Restoring " _YELLOW_("%s")" to card", datafilename); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, " blk | data | status"); + PrintAndLogEx(INFO, "-----+-------------------------------------------------+----------------"); // main loop for restoring. // a bit more complicated than needed @@ -1277,22 +1472,15 @@ static int CmdHF14AMfRestore(const char *Cmd) { memcpy(bldata, dump, MFBLOCK_SIZE); // if sector trailer - if (mfNumBlocksPerSector(s) - 1 == b) { + if (mfIsSectorTrailerBasedOnBlocks(s, b)) { + + // keep the current keys on the card if (use_keyfile_for_auth == false) { // replace KEY A - bldata[0] = (keyA[s][0]); - bldata[1] = (keyA[s][1]); - bldata[2] = (keyA[s][2]); - bldata[3] = (keyA[s][3]); - bldata[4] = (keyA[s][4]); - bldata[5] = (keyA[s][5]); + memcpy(bldata, keyA + (s * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + // replace KEY B - bldata[10] = (keyB[s][0]); - bldata[11] = (keyB[s][1]); - bldata[12] = (keyB[s][2]); - bldata[13] = (keyB[s][3]); - bldata[14] = (keyB[s][4]); - bldata[15] = (keyB[s][5]); + memcpy(bldata + 10, keyB + (s * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); } // ensure access right isn't messed up. @@ -1325,39 +1513,55 @@ static int CmdHF14AMfRestore(const char *Cmd) { for (int8_t kt = MF_KEY_B; kt > -1; kt--) { if (use_keyfile_for_auth) { - if (kt == MF_KEY_A) - memcpy(wdata, keyA[s], 6); - else - memcpy(wdata, keyB[s], 6); + + if (kt == MF_KEY_A) { + memcpy(wdata, keyA + (s * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + } else { + memcpy(wdata, keyB + (s * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + } + } else { // use default key to authenticate for the write command - memcpy(wdata, default_key, 6); + memcpy(wdata, default_key, MIFARE_KEY_SIZE); } - PrintAndLogEx(INFO, "block %3d: %s", mfFirstBlockOfSector(s) + b, sprint_hex(bldata, sizeof(bldata))); + + uint16_t blockno = (mfFirstBlockOfSector(s) + b); clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_WRITEBL, mfFirstBlockOfSector(s) + b, kt, 0, wdata, sizeof(wdata)); + SendCommandMIX(CMD_HF_MIFARE_WRITEBL, blockno, kt, 0, wdata, sizeof(wdata)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t isOK = resp.oldarg[0] & 0xff; - if (isOK == 0) { - if (b == 0) { - PrintAndLogEx(INFO, "Writing to manufacture block w key %c ( " _RED_("fail") " )", (kt == MF_KEY_A) ? 'A' : 'B'); - } else { - PrintAndLogEx(FAILED, "Write to block %u w key %c ( " _RED_("fail") " ) ", b, (kt == MF_KEY_A) ? 'A' : 'B'); - } - } else { - // if success, skip to next block - break; - } - } else { + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(WARNING, "Command execute timeout"); + continue; } - } + + int isOK = resp.oldarg[0] & 0xff; + if (isOK == 1) { + // if success, skip to next block + PrintAndLogEx(INFO, " %3d | %s| ( " _GREEN_("ok") " )", blockno, sprint_hex(bldata, sizeof(bldata))); + break; + } + // write somehow failed. Lets determine why. + if (isOK == PM3_ETEAROFF) { + PrintAndLogEx(INFO, "Tear off triggerd. Recommendation is not to use tear-off with restore command"); + goto out; + } + + PrintAndLogEx(INFO, " %3d | %s| ( " _RED_("fail") " ) key " _YELLOW_("%c"), + blockno, + sprint_hex(bldata, sizeof(bldata)), + (kt == MF_KEY_A) ? 'A' : 'B' + ); + } // end loop key types } // end loop B } // end loop S +out: free(ref_dump); + free(keyA); + free(keyB); + PrintAndLogEx(INFO, "-----+-------------------------------------------------+----------------"); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -1563,7 +1767,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt); - int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, use_flashmemory); + int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, use_flashmemory, false); if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Fast check found all keys"); goto jumptoend; @@ -1605,7 +1809,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't e_sector[sectorNo].foundKey[trgKeyType] = 1; e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); - mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false); + mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); continue; default : PrintAndLogEx(ERR, "Unknown error\n"); @@ -1703,7 +1907,7 @@ jumptoend: static int CmdHF14AMfNestedStatic(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf staticnested", - "Execute Nested attack against MIFARE Classic card with static nonce for key recovery.\n" + "Execute static nested attack against MIFARE Classic card with static nonce for key recovery.\n" "Supply a known key from one block to recover all keys", "hf mf staticnested --mini --blk 0 -a -k FFFFFFFFFFFF\n" "hf mf staticnested --1k --blk 0 -a -k FFFFFFFFFFFF\n" @@ -1819,7 +2023,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { } PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt); - int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, false); + int res = mfCheckKeys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, false, false); if (res == PM3_SUCCESS) { // all keys found PrintAndLogEx(SUCCESS, "Fast check found all keys"); @@ -1852,7 +2056,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { e_sector[sectorNo].foundKey[trgKeyType] = 1; e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); - // mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false); + // mfCheckKeys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); continue; default : PrintAndLogEx(ERR, "unknown error.\n"); @@ -2116,28 +2320,29 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { snprintf(filename, FILE_PATH_SIZE, "hf-mf-%s-nonces.bin", uid); } - // detect MFC EV1 Signature - if (detect_mfc_ev1_signature() && keylen == 0) { - PrintAndLogEx(INFO, "MIFARE Classic EV1 card detected"); - blockno = 69; - keytype = MF_KEY_B; - memcpy(key, g_mifare_signature_key_b, sizeof(g_mifare_signature_key_b)); - } - - if (known_target_key == false && nonce_file_read == false) { - - // check if tag doesn't have static nonce - if (detect_classic_static_nonce() == NONCE_STATIC) { - PrintAndLogEx(WARNING, "Static nonce detected. Quitting..."); - PrintAndLogEx(HINT, "\tTry use `" _YELLOW_("hf mf staticnested") "`"); - return PM3_EOPABORTED; + if (g_session.pm3_present && !tests) { + // detect MFC EV1 Signature + if (detect_mfc_ev1_signature() && keylen == 0) { + PrintAndLogEx(INFO, "MIFARE Classic EV1 card detected"); + blockno = 69; + keytype = MF_KEY_B; + memcpy(key, g_mifare_signature_key_b, sizeof(g_mifare_signature_key_b)); } - uint64_t key64 = 0; - // check if we can authenticate to sector - if (mfCheckKeys(blockno, keytype, true, 1, key, &key64) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Key is wrong. Can't authenticate to block: %3d key type: %c", blockno, (keytype == MF_KEY_B) ? 'B' : 'A'); - return PM3_EWRONGANSWER; + if (known_target_key == false && nonce_file_read == false) { + // check if tag doesn't have static nonce + if (detect_classic_static_nonce() == NONCE_STATIC) { + PrintAndLogEx(WARNING, "Static nonce detected. Quitting..."); + PrintAndLogEx(HINT, "\tTry use `" _YELLOW_("hf mf staticnested") "`"); + return PM3_EOPABORTED; + } + + uint64_t key64 = 0; + // check if we can authenticate to sector + if (mfCheckKeys(blockno, keytype, true, 1, key, &key64) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Key is wrong. Can't authenticate to block: %3d key type: %c", blockno, (keytype == MF_KEY_B) ? 'B' : 'A'); + return PM3_EWRONGANSWER; + } } } @@ -2188,12 +2393,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { "hf mf autopwn\n" "hf mf autopwn -s 0 -a -k FFFFFFFFFFFF --> target MFC 1K card, Sector 0 with known key A 'FFFFFFFFFFFF'\n" "hf mf autopwn --1k -f mfc_default_keys --> target MFC 1K card, default dictionary\n" - "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -f mfc_default_keys --> combo of the two above samples" + "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -f mfc_default_keys --> combo of the two above samples\n" + "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -k a0a1a2a3a4a5 --> multiple user supplied keys" ); void *argtable[] = { arg_param_begin, - arg_str0("k", "key", "", "Known key, 12 hex bytes"), + arg_strx0("k", "key", "", "Known key, 12 hex bytes"), arg_int0("s", "sector", "", "Input sector number"), arg_lit0("a", NULL, "Input key A (def)"), arg_lit0("b", NULL, "Input key B"), @@ -2224,16 +2430,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); - int keylen = 0; - uint8_t key[6] = {0}; - int32_t res = CLIParamHexToBuf(arg_get_str(ctx, 1), key, sizeof(key), &keylen); - if (res) { - CLIParserFree(ctx); - PrintAndLogEx(FAILED, "Error parsing key bytes"); - return PM3_EINVARG; - } - - bool known_key = (keylen == 6); + int in_keys_len = 0; + uint8_t in_keys[100 * MIFARE_KEY_SIZE] = {0}; + CLIGetHexWithReturn(ctx, 1, in_keys, &in_keys_len); uint8_t sectorno = arg_get_u32_def(ctx, 2, 0); @@ -2249,7 +2448,6 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - bool has_filename = (fnlen > 0); bool slow = arg_get_lit(ctx, 6); bool legacy_mfchk = arg_get_lit(ctx, 7); @@ -2332,40 +2530,49 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (in) SetSIMDInstr(SIMD_NONE); - // Nested and Hardnested parameter uint64_t key64 = 0; bool calibrate = true; + // Attack key storage variables uint8_t *keyBlock = NULL; uint32_t key_cnt = 0; - uint8_t tmp_key[6] = {0}; + uint8_t tmp_key[MIFARE_KEY_SIZE] = {0}; // Nested and Hardnested returned status uint64_t foundkey = 0; - int isOK = 0; int current_sector_i = 0, current_key_type_i = 0; + // Dumping and transfere to simulater memory - uint8_t block[16] = {0x00}; + uint8_t block[MFBLOCK_SIZE] = {0x00}; int bytes; + // Settings int prng_type = PM3_EUNDEF; - uint8_t num_found_keys = 0; - + int isOK = 0; // ------------------------------ + uint64_t tagT = GetHF14AMfU_Type(); + if (tagT != MFU_TT_UL_ERROR) { + PrintAndLogEx(ERR, "Detected a MIFARE Ultralight/C/NTAG Compatible card."); + PrintAndLogEx(ERR, "This command targets " _YELLOW_("MIFARE Classic")); + return PM3_ESOFT; + } + // Select card to get UID/UIDLEN/ATQA/SAK information clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "iso14443a card select timeout"); + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); return PM3_ETIMEOUT; } uint64_t select_status = resp.oldarg[0]; if (select_status == 0) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); + // iso14443a card select failed + PrintAndLogEx(FAILED, "No tag detected or other tag communication error"); + PrintAndLogEx(HINT, "Hint: Try some distance or position of the card"); return PM3_ECARDEXCHANGE; } @@ -2373,6 +2580,12 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + bool known_key = (in_keys_len > 5); + uint8_t key[MIFARE_KEY_SIZE] = {0}; + if (known_key) { + memcpy(key, in_keys, sizeof(key)); + } + // detect MFC EV1 Signature bool is_ev1 = detect_mfc_ev1_signature(); if (is_ev1) { @@ -2409,7 +2622,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (has_staticnonce == NONCE_NORMAL) { prng_type = detect_classic_prng(); if (prng_type < 0) { - PrintAndLogEx(FAILED, "\nNo tag detected or other tag communication error (%u)", prng_type); + PrintAndLogEx(FAILED, "\nNo tag detected or other tag communication error (%i)", prng_type); free(e_sector); free(fptr); return PM3_ESOFT; @@ -2438,106 +2651,21 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(INFO, "========================================================================"); } - // Start the timer - uint64_t t1 = msclock(); - // check the user supplied key if (known_key == false) { PrintAndLogEx(WARNING, "no known key was supplied, key recovery might fail"); - } else { - if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START KNOWN KEY ATTACK") " ======================="); - } - - if (mfCheckKeys(mfFirstBlockOfSector(sectorno), keytype, true, 1, key, &key64) == PM3_SUCCESS) { - PrintAndLogEx(INFO, "target sector %3u key type %c -- using valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", - sectorno, - (keytype == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(key, sizeof(key)) - ); - - // Store the key for the nested / hardnested attack (if supplied by the user) - e_sector[sectorno].Key[keytype] = key64; - e_sector[sectorno].foundKey[keytype] = 'U'; - - ++num_found_keys; - } else { - known_key = false; - PrintAndLogEx(FAILED, "Key is wrong. Can't authenticate to sector"_RED_("%3d") " key type "_RED_("%c") " key " _RED_("%s"), - sectorno, - (keytype == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(key, sizeof(key)) - ); - PrintAndLogEx(WARNING, "falling back to dictionary"); - } - - // Check if the user supplied key is used by other sectors - for (int i = 0; i < sector_cnt; i++) { - for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { - - if (e_sector[i].foundKey[j]) { - continue; - } - - if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, key, &key64) == PM3_SUCCESS) { - e_sector[i].Key[j] = bytes_to_num(key, 6); - e_sector[i].foundKey[j] = 'U'; - - // If the user supplied secctor / keytype was wrong --> just be nice and correct it ;) - if (known_key == false) { - num_to_bytes(e_sector[i].Key[j], 6, key); - known_key = true; - sectorno = i; - keytype = j; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", - i, - (j == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(key, sizeof(key)) - ); - } else { - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", - i, - (j == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(key, sizeof(key)) - ); - } - ++num_found_keys; - } - } - } - - if (num_found_keys == sector_cnt * 2) { - goto all_found; - } } - bool load_success = true; - // Load the dictionary - if (has_filename) { - res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 6, &key_cnt); - if (res != PM3_SUCCESS || key_cnt == 0 || keyBlock == NULL) { - PrintAndLogEx(FAILED, "An error occurred while loading the dictionary! (we will use the default keys now)"); - if (keyBlock != NULL) { - free(keyBlock); - } - load_success = false; - } + // Start the timer + uint64_t t1 = msclock(); + + int ret = mfLoadKeys(&keyBlock, &key_cnt, in_keys, in_keys_len, filename, fnlen); + if (ret != PM3_SUCCESS) { + free(e_sector); + return ret; } - if (has_filename == false || load_success == false) { - keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); - if (keyBlock == NULL) { - free(e_sector); - free(fptr); - return PM3_EMALLOC; - } - - for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) { - num_to_bytes(g_mifare_default_keys[cnt], 6, keyBlock + cnt * 6); - } - key_cnt = ARRAYLEN(g_mifare_default_keys); - PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%2d") " keys from hardcoded default array", key_cnt); - } + int32_t res = PM3_SUCCESS; // Use the dictionary to find sector keys on the card if (verbose) PrintAndLogEx(INFO, "======================= " _YELLOW_("START DICTIONARY ATTACK") " ======================="); @@ -2553,10 +2681,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); - if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, (keyBlock + (6 * k)), &key64) == PM3_SUCCESS) { - e_sector[i].Key[j] = bytes_to_num((keyBlock + (6 * k)), 6); + if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, (keyBlock + (MIFARE_KEY_SIZE * k)), &key64) == PM3_SUCCESS) { + e_sector[i].Key[j] = bytes_to_num((keyBlock + (MIFARE_KEY_SIZE * k)), MIFARE_KEY_SIZE); e_sector[i].foundKey[j] = 'D'; - ++num_found_keys; break; } } @@ -2566,7 +2693,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { PrintAndLogEx(NORMAL, ""); } else { - uint32_t chunksize = key_cnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : key_cnt; + uint32_t chunksize = key_cnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : key_cnt; bool firstChunk = true, lastChunk = false; for (uint8_t strategy = 1; strategy < 3; strategy++) { @@ -2582,12 +2709,14 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } uint32_t size = ((key_cnt - i) > chunksize) ? chunksize : key_cnt - i; // last chunk? - if (size == key_cnt - i) + if (size == key_cnt - i) { lastChunk = true; + } - res = mfCheckKeys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); - if (firstChunk) + res = mfCheckKeys_fast(sector_cnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, verbose); + if (firstChunk) { firstChunk = false; + } // all keys, aborted if (res == PM3_SUCCESS) { i = key_cnt; @@ -2601,36 +2730,43 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // Analyse the dictionary attack + uint8_t num_found_keys = 0; for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { if (e_sector[i].foundKey[j] != 1) { continue; } + ++num_found_keys; + e_sector[i].foundKey[j] = 'D'; - num_to_bytes(e_sector[i].Key[j], 6, tmp_key); + num_to_bytes(e_sector[i].Key[j], MIFARE_KEY_SIZE, tmp_key); // Store valid credentials for the nested / hardnested attack if none exist if (known_key == false) { - num_to_bytes(e_sector[i].Key[j], 6, key); + num_to_bytes(e_sector[i].Key[j], MIFARE_KEY_SIZE, key); known_key = true; sectorno = i; keytype = j; PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", - i, - (j == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(tmp_key, sizeof(tmp_key)) - ); + i, + (j == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(tmp_key, sizeof(tmp_key)) + ); } else { PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", - i, - (j == MF_KEY_B) ? 'B' : 'A', - sprint_hex_inrow(tmp_key, sizeof(tmp_key)) - ); + i, + (j == MF_KEY_B) ? 'B' : 'A', + sprint_hex_inrow(tmp_key, sizeof(tmp_key)) + ); } } } + if (num_found_keys == sector_cnt * 2) { + goto all_found; + } + // Check if at least one sector key was found if (known_key == false) { @@ -2639,7 +2775,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "======================= " _YELLOW_("START DARKSIDE ATTACK") " ======================="); } - isOK = mfDarkside(mfFirstBlockOfSector(sectorno), keytype + 0x60, &key64); + isOK = mfDarkside(mfFirstBlockOfSector(sectorno), MIFARE_AUTH_KEYA + keytype, &key64); switch (isOK) { case PM3_EOPABORTED : @@ -2661,7 +2797,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { } // Store the keys - num_to_bytes(key64, 6, key); + num_to_bytes(key64, MIFARE_KEY_SIZE, key); e_sector[sectorno].Key[keytype] = key64; e_sector[sectorno].foundKey[keytype] = 'S'; PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%012" PRIx64) " ] (used for nested / hardnested attack)", @@ -2682,7 +2818,7 @@ noValidKeyFound: free(keyBlock); // Clear the needed variables - num_to_bytes(0, 6, tmp_key); + num_to_bytes(0, MIFARE_KEY_SIZE, tmp_key); bool nested_failed = false; // Iterate over each sector and key(A/B) @@ -2697,8 +2833,8 @@ noValidKeyFound: goto tryStaticnested; // Try the found keys are reused - if (bytes_to_num(tmp_key, 6) != 0) { - // The fast check --> mfCheckKeys_fast(sector_cnt, true, true, 2, 1, tmp_key, e_sector, false); + if (bytes_to_num(tmp_key, MIFARE_KEY_SIZE) != 0) { + // The fast check --> mfCheckKeys_fast(sector_cnt, true, true, 2, 1, tmp_key, e_sector, false, verbose); // Returns false keys, so we just stick to the slower mfchk. for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { @@ -2708,7 +2844,7 @@ noValidKeyFound: // Check if the key works if (mfCheckKeys(mfFirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) { - e_sector[i].Key[j] = bytes_to_num(tmp_key, 6); + e_sector[i].Key[j] = bytes_to_num(tmp_key, MIFARE_KEY_SIZE); e_sector[i].foundKey[j] = 'R'; PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", i, @@ -2720,7 +2856,7 @@ noValidKeyFound: } } // Clear the last found key - num_to_bytes(0, 6, tmp_key); + num_to_bytes(0, MIFARE_KEY_SIZE, tmp_key); if (current_key_type_i == MF_KEY_B) { if (e_sector[current_sector_i].foundKey[0] && !e_sector[current_sector_i].foundKey[1]) { @@ -2736,7 +2872,7 @@ noValidKeyFound: payload.blockno = sectrail; payload.keytype = MF_KEY_A; - num_to_bytes(e_sector[current_sector_i].Key[0], 6, payload.key); // KEY A + num_to_bytes(e_sector[current_sector_i].Key[0], MIFARE_KEY_SIZE, payload.key); // KEY A clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); @@ -2746,11 +2882,11 @@ noValidKeyFound: if (resp.status != PM3_SUCCESS) goto skipReadBKey; uint8_t *data = resp.data.asBytes; - key64 = bytes_to_num(data + 10, 6); + key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); if (key64) { e_sector[current_sector_i].foundKey[current_key_type_i] = 'A'; e_sector[current_sector_i].Key[current_key_type_i] = key64; - num_to_bytes(key64, 6, tmp_key); + num_to_bytes(key64, MIFARE_KEY_SIZE, tmp_key); PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', @@ -2836,7 +2972,7 @@ tryNested: } case PM3_SUCCESS: { calibrate = false; - e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6); + e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, MIFARE_KEY_SIZE); e_sector[current_sector_i].foundKey[current_key_type_i] = 'N'; break; } @@ -2898,7 +3034,7 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack } // Copy the found key to the tmp_key variale (for the following print statement, and the mfCheckKeys above) - num_to_bytes(foundkey, 6, tmp_key); + num_to_bytes(foundkey, MIFARE_KEY_SIZE, tmp_key); e_sector[current_sector_i].Key[current_key_type_i] = foundkey; e_sector[current_sector_i].foundKey[current_key_type_i] = 'H'; } @@ -2928,7 +3064,7 @@ tryStaticnested: return isOK; } case PM3_SUCCESS: { - e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, 6); + e_sector[current_sector_i].Key[current_key_type_i] = bytes_to_num(tmp_key, MIFARE_KEY_SIZE); e_sector[current_sector_i].foundKey[current_key_type_i] = 'C'; break; } @@ -2970,17 +3106,21 @@ all_found: clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_MEMCLR, NULL, 0); - PrintAndLogEx(SUCCESS, "transferring keys to simulator memory (Cmd Error: 04 can occur)"); + PrintAndLogEx(INFO, "transferring keys to simulator memory " NOLF); + bool transfer_status = true; for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) { mfEmlGetMem(block, current_sector_i, 1); if (e_sector[current_sector_i].foundKey[0]) - num_to_bytes(e_sector[current_sector_i].Key[0], 6, block); + num_to_bytes(e_sector[current_sector_i].Key[0], MIFARE_KEY_SIZE, block); if (e_sector[current_sector_i].foundKey[1]) - num_to_bytes(e_sector[current_sector_i].Key[1], 6, block + 10); + num_to_bytes(e_sector[current_sector_i].Key[1], MIFARE_KEY_SIZE, block + 10); - mfEmlSetMem(block, mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1, 1); + transfer_status |= mfEmlSetMem(block, mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1, 1); } + PrintAndLogEx(NORMAL, "( %s )", (transfer_status) ? _GREEN_("ok") : _RED_("fail")); + + PrintAndLogEx(INFO, "dumping card content to emulator memory (Cmd Error: 04 can occur)"); // use ecfill trick FastDumpWithEcFill(sector_cnt); @@ -2994,8 +3134,8 @@ all_found: return PM3_EMALLOC; } - PrintAndLogEx(INFO, "downloading the card content from emulator memory"); - if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + PrintAndLogEx(INFO, "downloading card content from emulator memory"); + if (GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(ERR, "Fail, transfer from device time-out"); free(e_sector); free(dump); @@ -3015,13 +3155,7 @@ all_found: strncpy(filename, fptr, sizeof(filename) - 1); free(fptr); - saveFile(filename, ".bin", dump, bytes); - saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); - iso14a_mf_extdump_t xdump; - xdump.card_info = card; - xdump.dump = dump; - xdump.dumplen = bytes; - saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); + pm3_save_mf_dump(filename, dump, bytes, jsfCardMemory); // Generate and show statistics t1 = msclock() - t1; @@ -3032,73 +3166,6 @@ all_found: return PM3_SUCCESS; } -static int mfLoadKeys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey, int userkeylen, const char *filename, int fnlen) { - // Handle Keys - *pkeycnt = 0; - *pkeyBlock = NULL; - uint8_t *p; - // Handle user supplied key - // (it considers *pkeycnt and *pkeyBlock as possibly non-null so logic can be easily reordered) - if (userkeylen >= 6) { - int numKeys = userkeylen / 6; - p = realloc(*pkeyBlock, (*pkeycnt + numKeys) * 6); - if (!p) { - PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); - free(*pkeyBlock); - return PM3_EMALLOC; - } - *pkeyBlock = p; - - memcpy(*pkeyBlock + *pkeycnt * 6, userkey, numKeys * 6); - - for (int i = 0; i < numKeys; i++) { - PrintAndLogEx(INFO, "[%2d] key %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * 6, 6)); - } - *pkeycnt += numKeys; - } - - // Handle default keys - p = realloc(*pkeyBlock, (*pkeycnt + ARRAYLEN(g_mifare_default_keys)) * 6); - if (!p) { - PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); - free(*pkeyBlock); - return PM3_EMALLOC; - } - *pkeyBlock = p; - // Copy default keys to list - for (int i = 0; i < ARRAYLEN(g_mifare_default_keys); i++) { - num_to_bytes(g_mifare_default_keys[i], 6, (uint8_t *)(*pkeyBlock + (*pkeycnt + i) * 6)); - PrintAndLogEx(DEBUG, "[%2d] key %s", *pkeycnt + i, sprint_hex(*pkeyBlock + (*pkeycnt + i) * 6, 6)); - } - *pkeycnt += ARRAYLEN(g_mifare_default_keys); - - // Handle user supplied dictionary file - if (fnlen > 0) { - uint32_t loaded_numKeys = 0; - uint8_t *keyBlock_tmp = NULL; - int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock_tmp, 6, &loaded_numKeys); - if (res != PM3_SUCCESS || loaded_numKeys == 0 || *pkeyBlock == NULL) { - PrintAndLogEx(FAILED, "An error occurred while loading the dictionary!"); - free(keyBlock_tmp); - free(*pkeyBlock); - return PM3_EFILE; - } else { - p = realloc(*pkeyBlock, (*pkeycnt + loaded_numKeys) * 6); - if (!p) { - PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); - free(keyBlock_tmp); - free(*pkeyBlock); - return PM3_EMALLOC; - } - *pkeyBlock = p; - memcpy(*pkeyBlock + *pkeycnt * 6, keyBlock_tmp, loaded_numKeys * 6); - *pkeycnt += loaded_numKeys; - free(keyBlock_tmp); - } - } - return PM3_SUCCESS; -} - static int CmdHF14AMfChk_fast(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf fchk", @@ -3128,7 +3195,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); int keylen = 0; - uint8_t key[255 * 6] = {0}; + uint8_t key[100 * MIFARE_KEY_SIZE] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); bool m0 = arg_get_lit(ctx, 2); @@ -3183,16 +3250,17 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { return PM3_EMALLOC; } - uint32_t chunksize = keycnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : keycnt; + uint32_t chunksize = keycnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : keycnt; bool firstChunk = true, lastChunk = false; int i = 0; + // time uint64_t t1 = msclock(); if (use_flashmemory) { PrintAndLogEx(SUCCESS, "Using dictionary in flash memory"); - mfCheckKeys_fast(sectorsCnt, true, true, 1, 0, keyBlock, e_sector, use_flashmemory); + mfCheckKeys_fast(sectorsCnt, true, true, 1, 0, keyBlock, e_sector, use_flashmemory, false); } else { // strategys. 1= deep first on sector 0 AB, 2= width first on all sectors @@ -3213,7 +3281,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { if (size == keycnt - i) lastChunk = true; - int res = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); + int res = mfCheckKeys_fast(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, false); if (firstChunk) firstChunk = false; @@ -3221,6 +3289,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { // all keys, aborted if (res == PM3_SUCCESS || res == 2) goto out; + } // end chunks of keys firstChunk = true; lastChunk = false; @@ -3260,16 +3329,16 @@ out: if (transferToEml) { // fast push mode g_conn.block_after_ACK = true; - uint8_t block[16] = {0x00}; + uint8_t block[MFBLOCK_SIZE] = {0x00}; for (i = 0; i < sectorsCnt; ++i) { uint8_t b = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; mfEmlGetMem(block, b, 1); if (e_sector[i].foundKey[0]) - num_to_bytes(e_sector[i].Key[0], 6, block); + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); if (e_sector[i].foundKey[1]) - num_to_bytes(e_sector[i].Key[1], 6, block + 10); + num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, block + 10); if (i == sectorsCnt - 1) { // Disable fast mode on last packet @@ -3331,7 +3400,7 @@ static int CmdHF14AMfChk(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); int keylen = 0; - uint8_t key[255 * 6] = {0}; + uint8_t key[100 * MIFARE_KEY_SIZE] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); int blockNo = arg_get_int_def(ctx, 2, -1); @@ -3359,8 +3428,9 @@ static int CmdHF14AMfChk(const char *Cmd) { CLIParamStrToBuf(arg_get_str(ctx, 12), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); - bool singleSector = blockNo > -1; - if (! singleSector) { + + bool singleSector = (blockNo > -1); + if (singleSector == false) { // start from first trailer block blockNo = 3; } @@ -3383,9 +3453,11 @@ static int CmdHF14AMfChk(const char *Cmd) { } if (singleSector) { - size_t min_sectors_cnt = 0; + // find a MIFARE type that can accommodate the provided block number + size_t min_sectors_cnt = 0; uint8_t s = mfSectorNum(blockNo); + if (s < MIFARE_MINI_MAXSECTOR) { min_sectors_cnt = MIFARE_MINI_MAXSECTOR; } else if (s < MIFARE_1K_MAXSECTOR) { @@ -3398,6 +3470,7 @@ static int CmdHF14AMfChk(const char *Cmd) { PrintAndLogEx(WARNING, "Provided block out of possible MIFARE Type memory map"); return PM3_EINVARG; } + if (sectors_cnt == 1) { sectors_cnt = min_sectors_cnt; } else if (sectors_cnt < min_sectors_cnt) { @@ -3405,6 +3478,7 @@ static int CmdHF14AMfChk(const char *Cmd) { return PM3_EINVARG; } } + if (sectors_cnt == 1) { sectors_cnt = MIFARE_1K_MAXSECTOR; } @@ -3462,7 +3536,7 @@ static int CmdHF14AMfChk(const char *Cmd) { uint32_t size = keycnt - c > max_keys ? max_keys : keycnt - c; - if (mfCheckKeys(b, trgKeyType, clearLog, size, &keyBlock[6 * c], &key64) == PM3_SUCCESS) { + if (mfCheckKeys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64) == PM3_SUCCESS) { e_sector[i].Key[trgKeyType] = key64; e_sector[i].foundKey[trgKeyType] = true; clearLog = false; @@ -3472,6 +3546,7 @@ static int CmdHF14AMfChk(const char *Cmd) { } if (singleSector) break; + b < 127 ? (b += 4) : (b += 16); } } @@ -3498,7 +3573,7 @@ static int CmdHF14AMfChk(const char *Cmd) { payload.keytype = MF_KEY_A; // Use key A - num_to_bytes(e_sector[i].Key[0], 6, payload.key); + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, payload.key); clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); @@ -3509,9 +3584,9 @@ static int CmdHF14AMfChk(const char *Cmd) { if (resp.status != PM3_SUCCESS) continue; uint8_t *data = resp.data.asBytes; - key64 = bytes_to_num(data + 10, 6); + key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); if (key64) { - PrintAndLogEx(NORMAL, "Data:%s", sprint_hex(data + 10, 6)); + PrintAndLogEx(NORMAL, "Data:%s", sprint_hex(data + 10, MIFARE_KEY_SIZE)); e_sector[i].foundKey[1] = 1; e_sector[i].Key[1] = key64; } @@ -3527,24 +3602,24 @@ out: PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); //print keys - if (singleSector) - printKeyTableEx(1, e_sector, mfSectorNum(blockNo)); - else - printKeyTable(sectors_cnt, e_sector); +// if (singleSector) +// printKeyTableEx(1, e_sector, mfSectorNum(blockNo)); +// else + printKeyTable(sectors_cnt, e_sector); if (transferToEml) { // fast push mode g_conn.block_after_ACK = true; - uint8_t block[16] = {0x00}; + uint8_t block[MFBLOCK_SIZE] = {0x00}; for (int i = 0; i < sectors_cnt; ++i) { uint8_t blockno = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; mfEmlGetMem(block, blockno, 1); if (e_sector[i].foundKey[0]) - num_to_bytes(e_sector[i].Key[0], 6, block); + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); if (e_sector[i].foundKey[1]) - num_to_bytes(e_sector[i].Key[1], 6, block + 10); + num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, block + 10); if (i == sectors_cnt - 1) { // Disable fast mode on last packet @@ -3800,7 +3875,7 @@ static int CmdHF14AMfSim(const char *Cmd) { PacketResponseNG resp; if (flags & FLAG_INTERACTIVE) { - PrintAndLogEx(INFO, "Press pm3-button or send another cmd to abort simulation"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or send another cmd to abort simulation"); sector_t *k_sector = NULL; @@ -3811,19 +3886,20 @@ static int CmdHF14AMfSim(const char *Cmd) { if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) break; - + if ((resp.oldarg[0] & 0xffff) != CMD_HF_MIFARE_SIMULATE) break; nonces_t data[1]; memcpy(data, resp.data.asBytes, sizeof(data)); readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose); + break; } //iceman: readerAttack call frees k_sector. this call below is useless. showSectorTable(k_sector, k_sectors_cnt); } else { - PrintAndLogEx(INFO, "Press pm3-button to abort simulation"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort simulation"); } return PM3_SUCCESS; } @@ -3846,7 +3922,10 @@ static int CmdHF14AMfKeyBrute(const char *Cmd) { if (cmdp == 'b') keytype = MF_KEY_B; // key - if (param_gethex(Cmd, 2, key, 12)) return usage_hf14_keybrute(); + int keylen = 0; + if (param_gethex_ex(Cmd, 2, key, &keylen) && (keylen != 12)) { + return usage_hf14_keybrute(); + } uint64_t t1 = msclock(); @@ -3862,7 +3941,7 @@ static int CmdHF14AMfKeyBrute(const char *Cmd) { */ void printKeyTable(size_t sectorscnt, sector_t *e_sector) { - return printKeyTableEx(sectorscnt, e_sector, 0); + printKeyTableEx(sectorscnt, e_sector, 0); } void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector) { @@ -3871,14 +3950,15 @@ void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector char resA[20 + 1] = {0}; char resB[20 + 1] = {0}; + uint64_t ndef_key = 0xD3F7D3F7D3F7; + bool has_ndef_key = false; + bool extended_legend = false; + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "-----+-----+--------------+---+--------------+----"); PrintAndLogEx(SUCCESS, " Sec | Blk | key A |res| key B |res"); PrintAndLogEx(SUCCESS, "-----+-----+--------------+---+--------------+----"); - uint64_t ndef_key = 0xD3F7D3F7D3F7; - bool has_ndef_key = false; - bool extended_legend = false; for (size_t i = 0; i < sectorscnt; i++) { if ((e_sector[i].foundKey[0] > 1) || (e_sector[i].foundKey[1] > 1)) { @@ -3921,11 +4001,12 @@ void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector } PrintAndLogEx(SUCCESS, " " _YELLOW_("%03d") " | %03d | %s | %s | %s | %s" - , s - , mfSectorTrailerOfSector(s) - , strA, resA - , strB, resB - ); + , s + , mfSectorTrailerOfSector(s) + , strA, resA + , strB, resB + ); + } PrintAndLogEx(SUCCESS, "-----+-----+--------------+---+--------------+----"); @@ -4014,7 +4095,7 @@ static int CmdHF14AMfEGetSc(const char *Cmd) { bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); - if (s > 39) { + if (s >= MIFARE_4K_MAXSECTOR) { PrintAndLogEx(WARNING, "Sector number must be less then 40"); return PM3_EINVARG; } @@ -4106,7 +4187,7 @@ int CmdHF14AMfELoad(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), @@ -4114,6 +4195,7 @@ int CmdHF14AMfELoad(const char *Cmd) { arg_lit0(NULL, "ul", "MIFARE Ultralight family"), arg_lit0("m", "mem", "use RDV4 spiffs"), arg_int0("q", "qty", "", "manually set number of blocks (overrides)"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -4130,7 +4212,7 @@ int CmdHF14AMfELoad(const char *Cmd) { bool use_spiffs = arg_get_lit(ctx, 7); int numblks = arg_get_int_def(ctx, 8, -1); - + bool verbose = arg_get_lit(ctx, 9); CLIParserFree(ctx); // validations @@ -4162,13 +4244,17 @@ int CmdHF14AMfELoad(const char *Cmd) { return PM3_EINVARG; } - PrintAndLogEx(INFO, "%d blocks ( %u bytes ) to upload", block_cnt, block_cnt * block_width); + PrintAndLogEx(INFO, "Upload " _YELLOW_("%u") " blocks " _YELLOW_("%u") " bytes", block_cnt, block_cnt * block_width); if (numblks > 0) { block_cnt = MIN(numblks, block_cnt); - PrintAndLogEx(INFO, "overriding number of blocks, will use %d blocks ( %u bytes )", block_cnt, block_cnt * block_width); + PrintAndLogEx(INFO, "overriding number of blocks, will use " _YELLOW_("%u") " blocks " _YELLOW_("%u") " bytes", block_cnt, block_cnt * block_width); } + // ICEMAN: bug. if device has been using ICLASS commands, + // the device needs to load the HF fpga image. It takes 1.5 second. + set_fpga_mode(FPGA_BITSTREAM_HF); + // use RDV4 spiffs if (use_spiffs && IfPm3Flash() == false) { PrintAndLogEx(WARNING, "Device not compiled to support spiffs"); @@ -4222,8 +4308,10 @@ int CmdHF14AMfELoad(const char *Cmd) { return res; } - mfu_dump_t *mfu_dump = (mfu_dump_t *)data; - printMFUdumpEx(mfu_dump, mfu_dump->pages + 1, 0); + if (verbose) { + mfu_dump_t *mfu_dump = (mfu_dump_t *)data; + printMFUdumpEx(mfu_dump, mfu_dump->pages + 1, 0); + } // update expected blocks to match converted data. block_cnt = bytes_read / MFU_BLOCK_SIZE; @@ -4239,23 +4327,28 @@ int CmdHF14AMfELoad(const char *Cmd) { size_t offset = 0; int cnt = 0; + // 12 is the size of the struct the fct mfEmlSetMem_xt uses to transfer to device + uint16_t max_avail_blocks = ((PM3_CMD_DATA_SIZE - 12) / block_width) * block_width; + while (bytes_read && cnt < block_cnt) { if (bytes_read == block_width) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } - if (mfEmlSetMem_xt(data + offset, cnt, 1, block_width) != PM3_SUCCESS) { + uint16_t chunk_size = MIN(max_avail_blocks, bytes_read); + uint16_t blocks_to_send = chunk_size / block_width; + + if (mfEmlSetMem_xt(data + offset, cnt, blocks_to_send, block_width) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Can't set emulator mem at block: %3d", cnt); free(data); return PM3_ESOFT; } + cnt += blocks_to_send; + offset += chunk_size; + bytes_read -= chunk_size; PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); - - cnt++; - offset += block_width; - bytes_read -= block_width; } free(data); PrintAndLogEx(NORMAL, ""); @@ -4274,8 +4367,8 @@ int CmdHF14AMfELoad(const char *Cmd) { PrintAndLogEx(WARNING, "Error, file content, Only loaded %d blocks, must be %d blocks into emulator memory", cnt, block_cnt); return PM3_SUCCESS; } + PrintAndLogEx(INFO, "Done!"); } - PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -4283,14 +4376,14 @@ static int CmdHF14AMfESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf esave", - "Save emulator memory into three files (BIN/EML/JSON) ", + "Save emulator memory to file (bin/json) ", "hf mf esave\n" "hf mf esave --4k\n" "hf mf esave --4k -f hf-mf-01020304.eml" ); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), @@ -4353,30 +4446,7 @@ static int CmdHF14AMfESave(const char *Cmd) { FillFileNameByUID(fptr, dump, "-dump", 4); } - saveFile(filename, ".bin", dump, bytes); - saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); - - iso14a_mf_extdump_t xdump = {0}; - xdump.card_info.ats_len = 0; - // Check for 4 bytes uid: bcc corrected and single size uid bits in ATQA - if ((dump[0] ^ dump[1] ^ dump[2] ^ dump[3]) == dump[4] && (dump[6] & 0xc0) == 0) { - xdump.card_info.uidlen = 4; - memcpy(xdump.card_info.uid, dump, xdump.card_info.uidlen); - xdump.card_info.sak = dump[5]; - memcpy(xdump.card_info.atqa, &dump[6], sizeof(xdump.card_info.atqa)); - } - // Check for 7 bytes UID: double size uid bits in ATQA - else if ((dump[8] & 0xc0) == 0x40) { - xdump.card_info.uidlen = 7; - memcpy(xdump.card_info.uid, dump, xdump.card_info.uidlen); - xdump.card_info.sak = dump[7]; - memcpy(xdump.card_info.atqa, &dump[8], sizeof(xdump.card_info.atqa)); - } else { - PrintAndLogEx(WARNING, "Invalid dump. UID/SAK/ATQA not found"); - } - xdump.dump = dump; - xdump.dumplen = bytes; - saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); + pm3_save_mf_dump(filename, dump, bytes, jsfCardMemory); free(dump); return PM3_SUCCESS; } @@ -4396,6 +4466,7 @@ static int CmdHF14AMfEView(const char *Cmd) { arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "sk", "Save extracted keys to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -4404,6 +4475,7 @@ static int CmdHF14AMfEView(const char *Cmd) { bool m2 = arg_get_lit(ctx, 3); bool m4 = arg_get_lit(ctx, 4); bool verbose = arg_get_lit(ctx, 5); + bool save_keys = arg_get_lit(ctx, 6); CLIParserFree(ctx); // validations @@ -4449,6 +4521,11 @@ static int CmdHF14AMfEView(const char *Cmd) { if (verbose) { mf_print_keys(block_cnt, dump); } + + if (save_keys) { + mf_save_keys_from_arr(block_cnt, dump); + } + free(dump); return PM3_SUCCESS; } @@ -4812,7 +4889,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "emu", "from emulator memory"), arg_param_end }; @@ -5001,7 +5078,8 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { int s = arg_get_int_def(ctx, 1, 0); bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); - if (s > 39) { + + if (s >= MIFARE_4K_MAXSECTOR) { PrintAndLogEx(WARNING, "Sector number must be less then 40"); return PM3_EINVARG; } @@ -5040,14 +5118,14 @@ static int CmdHF14AMfCGetSc(const char *Cmd) { static int CmdHF14AMfCSave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf csave", - "Save magic gen1a card memory into three files (BIN/EML/JSON)" + "Save magic gen1a card memory to file (bin/json)" "or into emulator memory", "hf mf csave\n" "hf mf csave --4k" ); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), @@ -5104,7 +5182,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "iso14443a card select timeout"); + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); return PM3_ETIMEOUT; } @@ -5116,7 +5194,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { */ uint64_t select_status = resp.oldarg[0]; if (select_status == 0) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); + PrintAndLogEx(DEBUG, "iso14443a card select failed"); return PM3_SUCCESS; } @@ -5169,6 +5247,10 @@ static int CmdHF14AMfCSave(const char *Cmd) { if (mfEmlSetMem(dump + (i * MFBLOCK_SIZE), i, 5) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "Can't set emul block: " _YELLOW_("%d"), i); } + if (i % 64 == 0) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "" NOLF) ; + } PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); } @@ -5183,13 +5265,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); } - saveFile(filename, ".bin", dump, bytes); - saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); - iso14a_mf_extdump_t xdump; - xdump.card_info = card; - xdump.dump = dump; - xdump.dumplen = bytes; - saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); + pm3_save_mf_dump(filename, dump, bytes, jsfCardMemory); free(dump); return PM3_SUCCESS; } @@ -5254,7 +5330,7 @@ static int CmdHF14AMfCView(const char *Cmd) { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "iso14443a card select timeout"); + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); return PM3_ETIMEOUT; } @@ -5267,7 +5343,7 @@ static int CmdHF14AMfCView(const char *Cmd) { uint64_t select_status = resp.oldarg[0]; if (select_status == 0) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); + PrintAndLogEx(DEBUG, "iso14443a card select failed"); return PM3_ERFTRANS; } @@ -5559,28 +5635,27 @@ static int CmdHF14AMfAuth4(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf auth4", "Executes AES authentication command in ISO14443-4", - "hf mf auth4 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" - "hf mf auth4 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> executes authentication\n"); + "hf mf auth4 -n 4000 -k 000102030405060708090a0b0c0d0e0f -> executes authentication\n" + "hf mf auth4 -n 9003 -k FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> executes authentication\n"); void *argtable[] = { arg_param_begin, - arg_str1(NULL, NULL, "", NULL), - arg_str1(NULL, NULL, "", NULL), + arg_str1("n", NULL, "", "key num, 2 hex bytes"), + arg_str1("k", "key", "", "key, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - CLIGetHexWithReturn(ctx, 1, keyn, &keynlen); CLIGetHexWithReturn(ctx, 2, key, &keylen); CLIParserFree(ctx); if (keynlen != 2) { - PrintAndLogEx(ERR, " must be 2 bytes long instead of: %d", keynlen); + PrintAndLogEx(ERR, "Key number must be 2 bytes. Got... %d", keynlen); return PM3_ESOFT; } if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); + PrintAndLogEx(ERR, "Key must be 16 bytes. Got... %d", keylen); return PM3_ESOFT; } @@ -5600,12 +5675,12 @@ static int CmdHF14AMfMAD(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_lit0("v", "verbose", "show technical data"), - arg_str0(NULL, "aid", "", "print all sectors with specified aid"), - arg_str0("k", "key", "", "key for printing sectors"), + arg_str0(NULL, "aid", "", "print all sectors with specified aid"), + arg_str0("k", "key", "", "key for printing sectors"), arg_lit0("b", "keyb", "use key B for access printing sectors (by default: key A)"), arg_lit0(NULL, "be", "(optional, BigEndian)"), arg_lit0(NULL, "dch", "decode Card Holder information"), - arg_str0("f", "file", "", "load dump file and decode MAD"), + arg_str0("f", "file", "", "load dump file and decode MAD"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -5630,21 +5705,20 @@ static int CmdHF14AMfMAD(const char *Cmd) { // read dump file uint8_t *dump = NULL; size_t bytes_read = 0; - int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK)); + int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, MIFARE_4K_MAX_BYTES); if (res != PM3_SUCCESS) { return res; } uint16_t block_cnt = MIN(MIFARE_1K_MAXBLOCK, (bytes_read / MFBLOCK_SIZE)); - if (bytes_read == 320) + if (bytes_read == MIFARE_MINI_MAX_BYTES) block_cnt = MIFARE_MINI_MAXBLOCK; - else if (bytes_read == 2048) + else if (bytes_read == MIFARE_2K_MAX_BYTES) block_cnt = MIFARE_2K_MAXBLOCK; - else if (bytes_read == 4096) + else if (bytes_read == MIFARE_4K_MAX_BYTES) block_cnt = MIFARE_4K_MAXBLOCK; if (verbose) { - PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt); } @@ -5698,6 +5772,30 @@ static int CmdHF14AMfMAD(const char *Cmd) { PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, _CYAN_("VIGIK PACS detected")); } + + if (haveMAD2) { + MAD2DecodeAndPrint(dump + (MIFARE_1K_MAXBLOCK * MF_MAD2_SECTOR), swapmad, verbose); + } + + if (aidlen == 2 || decodeholder) { + uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; + size_t madlen = 0; + if (MADDecode(dump, dump + (0x10 * MIFARE_1K_MAXBLOCK), mad, &madlen, swapmad)) { + PrintAndLogEx(ERR, "can't decode MAD"); + free(dump); + return PM3_ESOFT; + } + + uint16_t aaid = 0x0004; + if (aidlen == 2) { + aaid = (aid[0] << 8) + aid[1]; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-------- " _CYAN_("Card Holder Info 0x%04x") " --------", aaid); + + MADCardHolderInfoDecode(dump, bytes_read, verbose); + } free(dump); return PM3_SUCCESS; } @@ -5706,8 +5804,8 @@ static int CmdHF14AMfMAD(const char *Cmd) { return PM3_ENOTTY; - uint8_t sector0[16 * 4] = {0}; - uint8_t sector10[16 * 4] = {0}; + uint8_t sector0[MFBLOCK_SIZE * 4] = {0}; + uint8_t sector10[MFBLOCK_SIZE * 4] = {0}; bool got_first = true; if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0) != PM3_SUCCESS) { @@ -5733,17 +5831,34 @@ static int CmdHF14AMfMAD(const char *Cmd) { return PM3_ESOFT; } + got_first = true; + if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10) != PM3_SUCCESS) { + if (verbose) { + PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD 2 or doesn't have MAD 2 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_MAD2_SECTOR, MF_KEY_A, userkey, sector10) != PM3_SUCCESS) { + if (verbose) { + PrintAndLogEx(ERR, "error, read sector 10. card doesn't have MAD 2 or the custom key is wrong"); + } + } else { + PrintAndLogEx(INFO, "Authentication ( " _GREEN_("ok") " )"); + } + } + MADPrintHeader(); bool haveMAD2 = false; MAD1DecodeAndPrint(sector0, swapmad, verbose, &haveMAD2); if (haveMAD2) { - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10)) { - PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD or doesn't have MAD on default keys"); - return PM3_ESOFT; - } - MAD2DecodeAndPrint(sector10, swapmad, verbose); } @@ -5761,7 +5876,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { // user specified key if (keylen == 6) { - memcpy(akey, userkey, 6); + memcpy(akey, userkey, sizeof(akey)); } uint16_t aaid = 0x0004; @@ -5774,7 +5889,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { for (int i = 0; i < madlen; i++) { if (aaid == mad[i]) { - uint8_t vsector[16 * 4] = {0}; + uint8_t vsector[MFBLOCK_SIZE * 4] = {0}; if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector %d", i + 1); @@ -5782,7 +5897,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { } for (int j = 0; j < (verbose ? 4 : 3); j ++) - PrintAndLogEx(NORMAL, " [%03d] %s", (i + 1) * 4 + j, sprint_hex(&vsector[j * 16], 16)); + PrintAndLogEx(NORMAL, " [%03d] %s", (i + 1) * 4 + j, sprint_hex(&vsector[j * MFBLOCK_SIZE], MFBLOCK_SIZE)); } } } @@ -5792,21 +5907,22 @@ static int CmdHF14AMfMAD(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-------- " _CYAN_("Card Holder Info 0x%04x") " --------", aaid); - uint8_t data[4096] = {0}; + uint8_t data[MIFARE_4K_MAX_BYTES] = {0}; int datalen = 0; for (int i = 0; i < madlen; i++) { if (aaid == mad[i]) { - uint8_t vsector[16 * 4] = {0}; + uint8_t vsector[MFBLOCK_SIZE * 4] = {0}; if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(ERR, "error, read sector %d", i + 1); return PM3_ESOFT; } - memcpy(&data[datalen], vsector, 16 * 3); - datalen += 16 * 3; + // skip ST block hence only 3 blocks copy + memcpy(&data[datalen], vsector, MFBLOCK_SIZE * 3); + datalen += MFBLOCK_SIZE * 3; } } @@ -5820,9 +5936,16 @@ static int CmdHF14AMfMAD(const char *Cmd) { if (verbose) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "------------ " _CYAN_("MAD sector raw") " -------------"); - for (int i = 0; i < 4; i ++) - PrintAndLogEx(INFO, "[%d] %s", i, sprint_hex(§or0[i * 16], 16)); + PrintAndLogEx(INFO, "------------ " _CYAN_("MAD v1 sector raw") " -------------"); + for (int i = 0; i < 4; i ++) { + PrintAndLogEx(INFO, "[%d] %s", i, sprint_hex(§or0[i * MFBLOCK_SIZE], MFBLOCK_SIZE)); + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "------------ " _CYAN_("MAD v2 sector raw") " -------------"); + for (int i = 0; i < 4; i ++) { + PrintAndLogEx(INFO, "[%d] %s", i, sprint_hex(§or10[i * MFBLOCK_SIZE], MFBLOCK_SIZE)); + } } return PM3_SUCCESS; @@ -5853,12 +5976,15 @@ int CmdHFMFNDEFRead(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); bool verbose2 = arg_get_lit(ctx, 1) > 1; uint8_t aid[2] = {0}; + int aidlen; CLIGetHexWithReturn(ctx, 2, aid, &aidlen); uint8_t key[6] = {0}; + int keylen; CLIGetHexWithReturn(ctx, 3, key, &keylen); bool keyB = arg_get_lit(ctx, 4); + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); @@ -5866,8 +5992,9 @@ int CmdHFMFNDEFRead(const char *Cmd) { CLIParserFree(ctx); uint16_t ndef_aid = NDEF_MFC_AID; - if (aidlen == 2) + if (aidlen == 2) { ndef_aid = (aid[0] << 8) + aid[1]; + } uint8_t ndefkey[6] = {0}; memcpy(ndefkey, g_mifare_ndef_key, 6); @@ -5880,8 +6007,9 @@ int CmdHFMFNDEFRead(const char *Cmd) { uint8_t data[4096] = {0}; int datalen = 0; - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "reading MAD v1 sector"); + } if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, g_mifare_mad_key, sector0)) { PrintAndLogEx(ERR, "error, read sector 0. card doesn't have MAD or doesn't have MAD on default keys"); @@ -5889,37 +6017,37 @@ int CmdHFMFNDEFRead(const char *Cmd) { return PM3_ESOFT; } + if (verbose) { + PrintAndLogEx(INFO, "reading MAD v2 sector"); + } + + if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) { + if (verbose) { + PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD 2 or doesn't have MAD 2 on default keys"); + PrintAndLogEx(INFO, "Skipping MAD 2"); + } + } + bool haveMAD2 = false; - int res = MADCheck(sector0, NULL, verbose, &haveMAD2); + int res = MADCheck(sector0, sector10, verbose, &haveMAD2); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "MAD error %d", res); return res; } - if (haveMAD2) { - if (verbose) - PrintAndLogEx(INFO, "reading MAD v2 sector"); - - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, g_mifare_mad_key, sector10)) { - PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD or doesn't have MAD on default keys"); - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -k `") " with your custom key"); - return PM3_ESOFT; - } - } - uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; - res = MADDecode(sector0, (haveMAD2 ? sector10 : NULL), mad, &madlen, false); + res = MADDecode(sector0, sector10, mad, &madlen, false); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "can't decode MAD"); return res; } PrintAndLogEx(INFO, "reading data from tag"); - for (int i = 1; i <= madlen; i++) { + for (int i = 0; i < madlen; i++) { if (ndef_aid == mad[i]) { uint8_t vsector[MFBLOCK_SIZE * 4] = {0}; - if (mfReadSector(i, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) { + if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) { PrintAndLogEx(ERR, "error, reading sector %d ", i + 1); return PM3_ESOFT; } @@ -5943,16 +6071,19 @@ int CmdHFMFNDEFRead(const char *Cmd) { print_buffer(data, datalen, 1); } - if (fnlen != 0) { - saveFile(filename, ".bin", data, datalen); - } - res = NDEFDecodeAndPrint(data, datalen, verbose); if (res != PM3_SUCCESS) { PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header"); res = NDEFRecordsDecodeAndPrint(data, datalen, verbose); } + // get total NDEF length before save. If fails, we save it all + size_t n = 0; + if (NDEFGetTotalLength(data, datalen, &n) != PM3_SUCCESS) + n = datalen; + + pm3_save_dump(filename, data, n, jsfNDEF); + if (verbose == false) { PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mf ndefread -v`") " for more details"); } else { @@ -6035,13 +6166,13 @@ int CmdHFMFNDEFFormat(const char *Cmd) { SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "iso14443a card select timeout"); + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); return PM3_ETIMEOUT; } uint64_t select_status = resp.oldarg[0]; if (select_status == 0) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); + PrintAndLogEx(DEBUG, "iso14443a card select failed"); return PM3_SUCCESS; } @@ -6049,8 +6180,8 @@ int CmdHFMFNDEFFormat(const char *Cmd) { // init keys to default key - uint8_t keyA[MIFARE_4K_MAXSECTOR][MFKEY_SIZE]; - uint8_t keyB[MIFARE_4K_MAXSECTOR][MFKEY_SIZE]; + uint8_t keyA[MIFARE_4K_MAXSECTOR][MIFARE_KEY_SIZE]; + uint8_t keyB[MIFARE_4K_MAXSECTOR][MIFARE_KEY_SIZE]; for (uint8_t i = 0; i < MIFARE_4K_MAXSECTOR; i++) { memcpy(keyA[i], g_mifare_default_key, sizeof(g_mifare_default_key)); @@ -6083,37 +6214,21 @@ int CmdHFMFNDEFFormat(const char *Cmd) { // load key file if exist if (strlen(keyFilename)) { - - FILE *f; - if ((f = fopen(keyFilename, "rb")) == NULL) { - // PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), keyFilename); + // + size_t alen = 0, blen = 0; + uint8_t *tmpA, *tmpB; + if (loadFileBinaryKey(keyFilename, "", (void **)&tmpA, (void **)&tmpB, &alen, &blen) != PM3_SUCCESS) { goto skipfile; } PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyFilename); - // Read keys A from file - size_t bytes_read; - for (uint8_t i = 0; i < numSectors; i++) { - bytes_read = fread(keyA[i], 1, MFKEY_SIZE, f); - if (bytes_read != MFKEY_SIZE) { - PrintAndLogEx(ERR, "File reading error."); - fclose(f); - return PM3_EFILE; - } + for (int i = 0; i < numSectors; i++) { + memcpy(keyA[i], tmpA + (i * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); + memcpy(keyB[i], tmpB + (i * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); } - - // Read keys B from file - for (uint8_t i = 0; i < numSectors; i++) { - bytes_read = fread(keyB[i], 1, MFKEY_SIZE, f); - if (bytes_read != MFKEY_SIZE) { - PrintAndLogEx(ERR, "File reading error."); - fclose(f); - return PM3_EFILE; - } - } - - fclose(f); + free(tmpA); + free(tmpB); } skipfile: @@ -6150,13 +6265,12 @@ skipfile: memcpy(block, firstblocks[b], MFBLOCK_SIZE); break; default: { - if (mfIsSectorTrailer(j)) { + if (mfIsSectorTrailerBasedOnBlocks(i, j)) { // ST NDEF memcpy(block, firstblocks[7], MFBLOCK_SIZE); } break; } - } // write to card, try B key first @@ -6171,7 +6285,6 @@ skipfile: } PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; } @@ -6339,7 +6452,7 @@ int CmdHFMFNDEFWrite(const char *Cmd) { return PM3_ESOFT; } - // decode MAD + // decode MAD v1 uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; res = MADDecode(sector0, NULL, mad, &madlen, false); @@ -6522,7 +6635,7 @@ static int CmdHFMFPersonalize(const char *Cmd) { } static int CmdHF14AMfList(const char *Cmd) { - return CmdTraceListAlias(Cmd, "hf mf", "mf"); + return CmdTraceListAlias(Cmd, "hf mf", "mf -c"); } static int CmdHf14AGen3UID(const char *Cmd) { @@ -6632,6 +6745,102 @@ static int CmdHf14AGen3Freeze(const char *Cmd) { return res; } +#define FURUI_MAX_TRACES 8 +static int mfc_furui_recovery(uint8_t items, uint8_t tracedata[FURUI_MAX_TRACES][18]) { + // recover key from collected traces + // outer loop + for (uint8_t i = 0; i < items; i++) { + + // first + nonces_t data; + data.cuid = bytes_to_num(tracedata[i], 4); + data.nonce = bytes_to_num(tracedata[i] + 6, 4); + data.nr = bytes_to_num(tracedata[i] + 10, 4); + data.ar = bytes_to_num(tracedata[i] + 14, 4); + data.at = 0; + + // inner loop + for (uint8_t j = i + 1; j < items; j++) { + + uint8_t *p = tracedata[j]; + PrintAndLogEx(INFO, "%u... %s", i, sprint_hex_inrow(p, 18)); + + // since data stored as block number but its the same key for all blocks in one sector + // we compare with sector number here + uint8_t s = mfSectorNum(tracedata[i][4]); + if (mfSectorNum(p[4]) == s) { + + data.nonce2 = bytes_to_num(p + 6, 4); + data.nr2 = bytes_to_num(p + 10, 4); + data.ar2 = bytes_to_num(p + 14, 4); + data.sector = s; + data.keytype = tracedata[i][5]; + data.state = FIRST; + + uint64_t key64 = -1; + if (mfkey32_moebius(&data, &key64)) { + PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ "_GREEN_("%012" PRIX64) " ]", + sprint_hex_inrow(tracedata[i], 4), + data.sector, + (data.keytype == 0x60) ? 'A' : 'B', + key64 + ); + break; + } + } + } + } + return PM3_SUCCESS; +} + +static int mfc_supercard_gen2_recovery(uint8_t items, uint8_t tracedata[FURUI_MAX_TRACES][18]) { + for (uint8_t i = 0; i < items; i++) { + uint8_t *tmp = tracedata[i]; + + // first + uint16_t NT0 = (tmp[6] << 8) | tmp[7]; + + nonces_t data; + data.cuid = bytes_to_num(tmp, 4); + data.nonce = prng_successor(NT0, 31); + data.nr = bytes_to_num(tmp + 8, 4); + data.ar = bytes_to_num(tmp + 12, 4); + data.at = 0; + + // second + for (uint8_t j = i + 1; j < items; j++) { + uint8_t *p = tracedata[j]; + + // since data stored as block number but its the same key for all blocks in one sector + // we compare with sector number here + uint8_t s = mfSectorNum(tmp[5]); + if (mfSectorNum(p[5]) == s) { + + NT0 = (p[6] << 8) | p[7]; + + data.nonce2 = prng_successor(NT0, 31); + data.nr2 = bytes_to_num(p + 8, 4); + data.ar2 = bytes_to_num(p + 12, 4); + data.sector = s; + data.keytype = tmp[4]; + data.state = FIRST; + + uint64_t key64 = -1; + if (mfkey32_moebius(&data, &key64)) { + PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ "_GREEN_("%012" PRIX64) " ]", + sprint_hex_inrow(tmp, 4), + data.sector, + (data.keytype == 0x60) ? 'A' : 'B', + key64 + ); + break; + } + } + } + } + return PM3_SUCCESS; +} + static int CmdHf14AMfSuperCard(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf supercard", @@ -6644,6 +6853,7 @@ static int CmdHf14AMfSuperCard(const char *Cmd) { arg_param_begin, arg_lit0("r", "reset", "Reset card"), arg_str0("u", "uid", "", "New UID (4 hex bytes)"), + arg_lit0(NULL, "furui", "Furui detection card"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -6651,6 +6861,8 @@ static int CmdHf14AMfSuperCard(const char *Cmd) { uint8_t uid[4]; int uidlen = 0; int res = CLIParamHexToBuf(arg_get_str(ctx, 2), uid, sizeof(uid), &uidlen); + bool is_furui = arg_get_lit(ctx, 3); + CLIParserFree(ctx); if (res || (!res && uidlen && uidlen != sizeof(uid))) { @@ -6658,15 +6870,53 @@ static int CmdHf14AMfSuperCard(const char *Cmd) { return PM3_EINVARG; } - #define SUPER_MAX_TRACES 7 + uint8_t tracedata[FURUI_MAX_TRACES][18]; - uint8_t trace = 0; - uint8_t traces[SUPER_MAX_TRACES][16]; + // Super card FURUI + if (is_furui) { - // read 7 traces from super card - for (trace = 0; trace < SUPER_MAX_TRACES; trace++) { + // no reset on super card FURUI + if (uidlen || reset_card) { + PrintAndLogEx(FAILED, "Not supported on this card"); + return PM3_SUCCESS; + } - uint8_t data[] = {0x30, 0x00 + trace}; + // read 8 traces + uint8_t i; + for (i = 0; i < FURUI_MAX_TRACES; i++) { + + uint8_t data[] = {0xAA, 0xA8, 0x00, i}; + uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(data), 0, data, sizeof(data)); + if (WaitForResponseTimeout(CMD_ACK, NULL, 1500) == false) { + break; + } + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + break; + } + + uint16_t len = resp.oldarg[0] & 0xFFFF; + if (len != 20) { + break; // Not trace data + } + + PrintAndLogEx(DEBUG, ">>> %s", sprint_hex_inrow(resp.data.asBytes, len)); + memcpy(&tracedata[i], resp.data.asBytes, len - 2); + } + + return mfc_furui_recovery(i, tracedata); + } + +#define SUPER_MAX_TRACES 7 + + // read 7 traces from super card generation 1,2 + uint8_t i = 0; + for (i = 0; i < SUPER_MAX_TRACES; i++) { + + uint8_t data[] = {0x30, i}; uint32_t flags = ISO14A_CONNECT | ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_RATS; clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(data), 0, data, sizeof(data)); @@ -6684,11 +6934,12 @@ static int CmdHf14AMfSuperCard(const char *Cmd) { break; // Not trace data } - memcpy(&traces[trace], resp.data.asBytes, len - 2); + PrintAndLogEx(DEBUG, ">>> %s", sprint_hex_inrow(resp.data.asBytes, len)); + memcpy(&tracedata[i], resp.data.asBytes, len - 2); } // Super card generation 2 - if (trace == SUPER_MAX_TRACES) { + if (i == SUPER_MAX_TRACES) { // no reset on super card generation 2. if (uidlen || reset_card) { @@ -6697,174 +6948,157 @@ static int CmdHf14AMfSuperCard(const char *Cmd) { } // recover key from collected traces - for (trace = 0; trace < SUPER_MAX_TRACES; trace++) { - uint8_t *trace_data = traces[trace]; - nonces_t data; + return mfc_supercard_gen2_recovery(i, tracedata); + } - // first - uint16_t NT0 = (trace_data[6] << 8) | trace_data[7]; - data.cuid = bytes_to_num(trace_data, 4); - data.nonce = prng_successor(NT0, 31); - data.nr = bytes_to_num(trace_data + 8, 4); - data.ar = bytes_to_num(trace_data + 12, 4); - data.at = 0; + // Super card generation 1 - // second - for (uint8_t s_strace = trace + 1; s_strace < 7; s_strace++) { - uint8_t *s_trace_data = traces[s_strace]; - if (mfSectorNum(s_trace_data[5]) == mfSectorNum(trace_data[5])) { - NT0 = (s_trace_data[6] << 8) | s_trace_data[7]; - data.nonce2 = prng_successor(NT0, 31); - data.nr2 = bytes_to_num(s_trace_data + 8, 4); - data.ar2 = bytes_to_num(s_trace_data + 12, 4); - data.sector = mfSectorNum(trace_data[5]); - data.keytype = trace_data[4]; - data.state = FIRST; + // Commands: + // a0 - set UID + // b0 - read traces + // c0 - clear card + bool activate_field = true; + bool keep_field_on = true; - uint64_t key64 = -1; - if (mfkey32_moebius(&data, &key64)) { - PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ "_GREEN_("%012" PRIX64) " ]", sprint_hex_inrow(trace_data, 4), data.sector, (data.keytype == 0x60) ? 'A' : 'B', key64); - break; - } - } - } - } - - } else { - - // Super card generation 1 - - // Commands: - // a0 - set UID - // b0 - read traces - // c0 - clear card - bool activate_field = true; - bool keep_field_on = true; - - // change UID on a super card generation 1 - if (uidlen) { - keep_field_on = false; - uint8_t response[6]; - int resplen = 0; - - // --------------- CHANGE UID ---------------- - uint8_t aCHANGE[] = {0x00, 0xa6, 0xa0, 0x00, 0x05, 0xff, 0xff, 0xff, 0xff, 0x00}; - memcpy(aCHANGE + 5, uid, uidlen); - res = ExchangeAPDU14a(aCHANGE, sizeof(aCHANGE), activate_field, keep_field_on, response, sizeof(response), - &resplen); - if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Super card UID change [ " _RED_("fail") " ]"); - DropField(); - return res; - } - - PrintAndLogEx(SUCCESS, "Super card UID change ( " _GREEN_("ok") " )"); - return PM3_SUCCESS; - } - - // reset a super card generation 1 - if (reset_card) { - keep_field_on = false; - uint8_t response[6]; - int resplen = 0; - - // --------------- RESET CARD ---------------- - uint8_t aRESET[] = {0x00, 0xa6, 0xc0, 0x00}; - res = ExchangeAPDU14a(aRESET, sizeof(aRESET), activate_field, keep_field_on, response, sizeof(response), - &resplen); - if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Super card reset [ " _RED_("fail") " ]"); - DropField(); - return res; - } - PrintAndLogEx(SUCCESS, "Super card reset ( " _GREEN_("ok") " )"); - return PM3_SUCCESS; - } - - - uint8_t responseA[22]; - uint8_t responseB[22]; - int respAlen = 0; - int respBlen = 0; - - // --------------- First ---------------- - uint8_t aFIRST[] = {0x00, 0xa6, 0xb0, 0x00, 0x10}; - res = ExchangeAPDU14a(aFIRST, sizeof(aFIRST), activate_field, keep_field_on, responseA, sizeof(responseA), &respAlen); - if (res != PM3_SUCCESS) { - DropField(); - return res; - } - - // --------------- Second ---------------- - activate_field = false; + // change UID on a super card generation 1 + if (uidlen) { keep_field_on = false; + uint8_t response[6]; + int resplen = 0; + + // --------------- CHANGE UID ---------------- + uint8_t aCHANGE[] = {0x00, 0xa6, 0xa0, 0x00, 0x05, 0xff, 0xff, 0xff, 0xff, 0x00}; + memcpy(aCHANGE + 5, uid, uidlen); + res = ExchangeAPDU14a( + aCHANGE, sizeof(aCHANGE), + activate_field, + keep_field_on, + response, sizeof(response), + &resplen + ); - uint8_t aSECOND[] = {0x00, 0xa6, 0xb0, 0x01, 0x10}; - res = ExchangeAPDU14a(aSECOND, sizeof(aSECOND), activate_field, keep_field_on, responseB, sizeof(responseB), &respBlen); if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Super card UID change [ " _RED_("fail") " ]"); DropField(); return res; } - uint8_t outA[16] = {0}; - uint8_t outB[16] = {0}; + PrintAndLogEx(SUCCESS, "Super card UID change ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; + } - uint8_t key[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; - for (uint8_t i = 0; i < 16; i += 8) { - des_decrypt(outA + i, responseA + i, key); - des_decrypt(outB + i, responseB + i, key); + // reset a super card generation 1 + if (reset_card) { + keep_field_on = false; + uint8_t response[6]; + int resplen = 0; + + // --------------- RESET CARD ---------------- + uint8_t aRESET[] = {0x00, 0xa6, 0xc0, 0x00}; + res = ExchangeAPDU14a( + aRESET, sizeof(aRESET), + activate_field, + keep_field_on, + response, sizeof(response), + &resplen + ); + + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Super card reset [ " _RED_("fail") " ]"); + DropField(); + return res; } + PrintAndLogEx(SUCCESS, "Super card reset ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; + } - PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseA, respAlen)); - PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outA, sizeof(outA))); - PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseB, respAlen)); - PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outB, sizeof(outB))); + uint8_t responseA[22]; + uint8_t responseB[22]; + int respAlen = 0; + int respBlen = 0; - if (memcmp(outA, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) { - PrintAndLogEx(INFO, "No trace recorded"); - return PM3_SUCCESS; - } + // --------------- First ---------------- + uint8_t aFIRST[] = {0x00, 0xa6, 0xb0, 0x00, 0x10}; + res = ExchangeAPDU14a(aFIRST, sizeof(aFIRST), activate_field, keep_field_on, responseA, sizeof(responseA), &respAlen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } - // second trace? - if (memcmp(outB, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) { - PrintAndLogEx(INFO, "Only one trace recorded"); - return PM3_SUCCESS; - } + // --------------- Second ---------------- + activate_field = false; + keep_field_on = false; - nonces_t data; + uint8_t aSECOND[] = {0x00, 0xa6, 0xb0, 0x01, 0x10}; + res = ExchangeAPDU14a(aSECOND, sizeof(aSECOND), activate_field, keep_field_on, responseB, sizeof(responseB), &respBlen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } - // first - uint16_t NT0 = (outA[6] << 8) | outA[7]; - data.cuid = bytes_to_num(outA, 4); - data.nonce = prng_successor(NT0, 31); - data.nr = bytes_to_num(outA + 8, 4); - data.ar = bytes_to_num(outA + 12, 4); - data.at = 0; + uint8_t outA[16] = {0}; + uint8_t outB[16] = {0}; - // second - NT0 = (outB[6] << 8) | outB[7]; - data.nonce2 = prng_successor(NT0, 31); - data.nr2 = bytes_to_num(outB + 8, 4); - data.ar2 = bytes_to_num(outB + 12, 4); - data.sector = mfSectorNum(outA[5]); - data.keytype = outA[4]; - data.state = FIRST; + uint8_t key[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + for (i = 0; i < 16; i += 8) { + des_decrypt(outA + i, responseA + i, key); + des_decrypt(outB + i, responseB + i, key); + } - PrintAndLogEx(DEBUG, "A Sector %02x", data.sector); - PrintAndLogEx(DEBUG, "A NT %08x", data.nonce); - PrintAndLogEx(DEBUG, "A NR %08x", data.nr); - PrintAndLogEx(DEBUG, "A AR %08x", data.ar); - PrintAndLogEx(DEBUG, ""); - PrintAndLogEx(DEBUG, "B NT %08x", data.nonce2); - PrintAndLogEx(DEBUG, "B NR %08x", data.nr2); - PrintAndLogEx(DEBUG, "B AR %08x", data.ar2); + PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseA, respAlen)); + PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outA, sizeof(outA))); + PrintAndLogEx(DEBUG, " in : %s", sprint_hex_inrow(responseB, respAlen)); + PrintAndLogEx(DEBUG, "out : %s", sprint_hex_inrow(outB, sizeof(outB))); - uint64_t key64 = -1; - if (mfkey32_moebius(&data, &key64)) { - PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ " _GREEN_("%012" PRIX64) " ]", sprint_hex_inrow(outA, 4), data.sector, (data.keytype == 0x60) ? 'A' : 'B', key64); - } else { - PrintAndLogEx(FAILED, "failed to recover any key"); - } + if (memcmp(outA, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) { + PrintAndLogEx(INFO, "No trace recorded"); + return PM3_SUCCESS; + } + + // second trace? + if (memcmp(outB, "\x01\x01\x01\x01\x01\x01\x01\x01", 8) == 0) { + PrintAndLogEx(INFO, "Only one trace recorded"); + return PM3_SUCCESS; + } + + nonces_t data; + + // first + uint16_t NT0 = (outA[6] << 8) | outA[7]; + data.cuid = bytes_to_num(outA, 4); + data.nonce = prng_successor(NT0, 31); + data.nr = bytes_to_num(outA + 8, 4); + data.ar = bytes_to_num(outA + 12, 4); + data.at = 0; + + // second + NT0 = (outB[6] << 8) | outB[7]; + data.nonce2 = prng_successor(NT0, 31); + data.nr2 = bytes_to_num(outB + 8, 4); + data.ar2 = bytes_to_num(outB + 12, 4); + data.sector = mfSectorNum(outA[5]); + data.keytype = outA[4]; + data.state = FIRST; + + PrintAndLogEx(DEBUG, "A Sector %02x", data.sector); + PrintAndLogEx(DEBUG, "A NT %08x", data.nonce); + PrintAndLogEx(DEBUG, "A NR %08x", data.nr); + PrintAndLogEx(DEBUG, "A AR %08x", data.ar); + PrintAndLogEx(DEBUG, ""); + PrintAndLogEx(DEBUG, "B NT %08x", data.nonce2); + PrintAndLogEx(DEBUG, "B NR %08x", data.nr2); + PrintAndLogEx(DEBUG, "B AR %08x", data.ar2); + + uint64_t key64 = -1; + if (mfkey32_moebius(&data, &key64)) { + PrintAndLogEx(SUCCESS, "UID: %s Sector %02x key %c [ " _GREEN_("%012" PRIX64) " ]", + sprint_hex_inrow(outA, 4), + data.sector, + (data.keytype == 0x60) ? 'A' : 'B', + key64 + ); + } else { + PrintAndLogEx(FAILED, "failed to recover any key"); } return PM3_SUCCESS; } @@ -6913,33 +7147,33 @@ static int CmdHF14AMfWipe(const char *Cmd) { return PM3_ESOFT; } - uint8_t keyA[MIFARE_4K_MAXSECTOR * 6]; - uint8_t keyB[MIFARE_4K_MAXSECTOR * 6]; + uint8_t keyA[MIFARE_4K_MAXSECTOR * MIFARE_KEY_SIZE]; + uint8_t keyB[MIFARE_4K_MAXSECTOR * MIFARE_KEY_SIZE]; uint8_t num_sectors = 0; uint8_t mf[MFBLOCK_SIZE]; switch (keyslen) { - case (MIFARE_MINI_MAXSECTOR * 2 * 6): { + case (MIFARE_MINI_MAX_KEY_SIZE): { PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic Mini 320b"); - memcpy(keyA, keys, (MIFARE_MINI_MAXSECTOR * 6)); - memcpy(keyB, keys + (MIFARE_MINI_MAXSECTOR * 6), (MIFARE_MINI_MAXSECTOR * 6)); + memcpy(keyA, keys, (MIFARE_MINI_MAXSECTOR * MIFARE_KEY_SIZE)); + memcpy(keyB, keys + (MIFARE_MINI_MAXSECTOR * MIFARE_KEY_SIZE), (MIFARE_MINI_MAXSECTOR * MIFARE_KEY_SIZE)); num_sectors = NumOfSectors('0'); memcpy(mf, "\x11\x22\x33\x44\x44\x09\x04\x00\x62\x63\x64\x65\x66\x67\x68\x69", MFBLOCK_SIZE); break; } - case (MIFARE_1K_MAXSECTOR * 2 * 6): { + case (MIFARE_1K_MAX_KEY_SIZE): { PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic 1K"); - memcpy(keyA, keys, (MIFARE_1K_MAXSECTOR * 6)); - memcpy(keyB, keys + (MIFARE_1K_MAXSECTOR * 6), (MIFARE_1K_MAXSECTOR * 6)); + memcpy(keyA, keys, (MIFARE_1K_MAXSECTOR * MIFARE_KEY_SIZE)); + memcpy(keyB, keys + (MIFARE_1K_MAXSECTOR * MIFARE_KEY_SIZE), (MIFARE_1K_MAXSECTOR * MIFARE_KEY_SIZE)); num_sectors = NumOfSectors('1'); memcpy(mf, "\x11\x22\x33\x44\x44\x08\x04\x00\x62\x63\x64\x65\x66\x67\x68\x69", MFBLOCK_SIZE); break; } - case (MIFARE_4K_MAXSECTOR * 2 * 6): { + case (MIFARE_4K_MAX_KEY_SIZE): { PrintAndLogEx(INFO, "Loaded keys matching MIFARE Classic 4K"); - memcpy(keyA, keys, (MIFARE_4K_MAXSECTOR * 6)); - memcpy(keyB, keys + (MIFARE_4K_MAXSECTOR * 6), (MIFARE_4K_MAXSECTOR * 6)); + memcpy(keyA, keys, (MIFARE_4K_MAXSECTOR * MIFARE_KEY_SIZE)); + memcpy(keyB, keys + (MIFARE_4K_MAXSECTOR * MIFARE_KEY_SIZE), (MIFARE_4K_MAXSECTOR * MIFARE_KEY_SIZE)); num_sectors = NumOfSectors('4'); memcpy(mf, "\x11\x22\x33\x44\x44\x18\x02\x00\x62\x63\x64\x65\x66\x67\x68\x69", MFBLOCK_SIZE); break; @@ -6954,25 +7188,33 @@ static int CmdHF14AMfWipe(const char *Cmd) { PrintAndLogEx(INFO, "Forcing overwrite of sector 0 / block 0 "); else PrintAndLogEx(INFO, "Skipping sector 0 / block 0"); + PrintAndLogEx(NORMAL, ""); uint8_t zeros[MFBLOCK_SIZE] = {0}; memset(zeros, 0x00, sizeof(zeros)); uint8_t st[MFBLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + PrintAndLogEx(INFO, " blk | "); + PrintAndLogEx(INFO, "-----+------------------------------------------------------------"); // time to wipe card for (uint8_t s = 0; s < num_sectors; s++) { for (uint8_t b = 0; b < mfNumBlocksPerSector(s); b++) { - // Skipp write to manufacture block if not enforced + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); + goto out; + } + + // Skip write to manufacture block if not enforced if (s == 0 && b == 0 && gen2 == false) { continue; } uint8_t data[26]; memset(data, 0, sizeof(data)); - if (mfIsSectorTrailer(b)) { + if (mfIsSectorTrailerBasedOnBlocks(s, b)) { memcpy(data + 10, st, sizeof(st)); } else { memcpy(data + 10, zeros, sizeof(zeros)); @@ -6987,11 +7229,11 @@ static int CmdHF14AMfWipe(const char *Cmd) { for (int8_t kt = MF_KEY_B; kt > -1; kt--) { if (kt == MF_KEY_A) - memcpy(data, keyA + (s * 6), 6); + memcpy(data, keyA + (s * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); else - memcpy(data, keyB + (s * 6), 6); + memcpy(data, keyB + (s * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); - PrintAndLogEx(INFO, "block %3d: %s" NOLF, mfFirstBlockOfSector(s) + b, sprint_hex(data + 10, MFBLOCK_SIZE)); + PrintAndLogEx(INFO, " %3d | %s" NOLF, mfFirstBlockOfSector(s) + b, sprint_hex(data + 10, MFBLOCK_SIZE)); clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_WRITEBL, mfFirstBlockOfSector(s) + b, kt, 0, data, sizeof(data)); PacketResponseNG resp; @@ -7010,6 +7252,7 @@ static int CmdHF14AMfWipe(const char *Cmd) { } } + PrintAndLogEx(INFO, "-----+------------------------------------------------------------"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Done!"); out: @@ -7026,8 +7269,9 @@ static int CmdHF14AMfView(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "sk", "Save extracted keys to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -7035,12 +7279,13 @@ static int CmdHF14AMfView(const char *Cmd) { char filename[FILE_PATH_SIZE]; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool verbose = arg_get_lit(ctx, 2); + bool save_keys = arg_get_lit(ctx, 3); CLIParserFree(ctx); // read dump file uint8_t *dump = NULL; size_t bytes_read = 0; - int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, (MFBLOCK_SIZE * MIFARE_4K_MAXBLOCK)); + int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, MIFARE_4K_MAX_BYTES); if (res != PM3_SUCCESS) { return res; } @@ -7054,7 +7299,6 @@ static int CmdHF14AMfView(const char *Cmd) { block_cnt = MIFARE_4K_MAXBLOCK; if (verbose) { - PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, block_cnt, block_cnt); } @@ -7065,13 +7309,17 @@ static int CmdHF14AMfView(const char *Cmd) { mf_analyse_acl(block_cnt, dump); } + if (save_keys) { + mf_save_keys_from_arr(block_cnt, dump); + } + int sector = DetectHID(dump, 0x4910); if (sector > -1) { // decode it PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, _CYAN_("VIGIK PACS detected")); - // decode MAD + // decode MAD v1 uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; size_t madlen = 0; res = MADDecode(dump, NULL, mad, &madlen, false); @@ -7118,6 +7366,188 @@ static int CmdHF14AMfView(const char *Cmd) { return PM3_SUCCESS; } +// info about Gen4 GTU card +static int CmdHF14AGen4Info(const char *cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf ginfo", + "Read info about magic gen4 GTU card.", + "hf mf ginfo --> get info with default password 00000000\n" + "hf mf ginfo --pwd 01020304 --> get info with password\n" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), + arg_str0("p", "pwd", "", "password 4bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + + int pwd_len = 0; + uint8_t pwd[4] = {0}; + CLIGetHexWithReturn(ctx, 2, pwd, &pwd_len); + CLIParserFree(ctx); + + if (pwd_len != 0 && pwd_len != 4) { + PrintAndLogEx(FAILED, "Password must be 4 bytes length, got " _YELLOW_("%u"), pwd_len); + return PM3_EINVARG; + } + + uint8_t resp[40] = {0}; + size_t resplen = 0; + int res = mfG4GetConfig(pwd, resp, &resplen, verbose); + if (res != PM3_SUCCESS || resplen == 0) { + if (res == PM3_ETIMEOUT) + PrintAndLogEx(ERR, "No card in the field or card command timeout."); + else + PrintAndLogEx(ERR, "Error get config. Maybe not a Gen4 card?. error=%d rlen=%zu", res, resplen); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "---------- Gen4 configuration ----------"); + if (resplen != 30 && resplen != 32) { + PrintAndLogEx(INFO, "Raw config [%02zu] %s", resplen, sprint_hex_inrow(resp, resplen)); + PrintAndLogEx(WARNING, "Unknown config format"); + return PM3_SUCCESS; + } + if (verbose) + PrintAndLogEx(INFO, "Raw config [%02zu]..... %s", resplen, sprint_hex_inrow(resp, resplen)); + + PrintAndLogEx(INFO, "UL protocol......... %02x" NOLF, resp[0]); + switch (resp[0]) { + case 0x00: + PrintAndLogEx(NORMAL, " (MIFARE Classic mode)"); + break; + case 0x01: + PrintAndLogEx(NORMAL, " (MIFARE Ultralight/NTAG mode)"); + break; + default: + PrintAndLogEx(NORMAL, " (unknown %02x)", resp[0]); + break; + } + + uint8_t uid_len = resp[1]; + PrintAndLogEx(INFO, "UID length.......... %02x" NOLF, resp[1]); + switch (resp[1]) { + case 0x00: + PrintAndLogEx(NORMAL, " (4 byte)"); + break; + case 0x01: + PrintAndLogEx(NORMAL, " (7 byte)"); + break; + case 0x02: + PrintAndLogEx(NORMAL, " (10 byte)"); + break; + default: + PrintAndLogEx(NORMAL, " (unknown %02x)", resp[1]); + break; + } + + PrintAndLogEx(INFO, "Password............ %s", sprint_hex_inrow(&resp[2], 4)); + + PrintAndLogEx(INFO, "GTU mode............ %02x" NOLF, resp[6]); + switch (resp[6]) { + case 0x00: + PrintAndLogEx(NORMAL, " (pre-write, shadow data can be written)"); + break; + case 0x01: + PrintAndLogEx(NORMAL, " (restore mode)"); + break; + case 0x02: + PrintAndLogEx(NORMAL, " (disabled)"); + break; + case 0x03: + PrintAndLogEx(NORMAL, " (disabled, high speed R/W mode for Ultralight?)"); + break; + default: + PrintAndLogEx(NORMAL, " (unknown %02x)", resp[6]); + break; + } + + PrintAndLogEx(INFO, "ATS [%02d]............ %s", resp[7], sprint_hex_inrow(&resp[8], resp[7])); + PrintAndLogEx(INFO, "ATQA................ %02x%02x", resp[25], resp[24]); + PrintAndLogEx(INFO, "SAK................. %02x", resp[26]); + + PrintAndLogEx(INFO, "UL mode............. %02x" NOLF, resp[27]); + switch (resp[27]) { + case 0x00: + PrintAndLogEx(NORMAL, " (UL EV1)"); + break; + case 0x01: + PrintAndLogEx(NORMAL, " (NTAG)"); + break; + case 0x02: + PrintAndLogEx(NORMAL, " (UL-C)"); + break; + case 0x03: + PrintAndLogEx(NORMAL, " (UL)"); + break; + default: + PrintAndLogEx(NORMAL, " (unknown %02x)", resp[27]); + break; + } + + PrintAndLogEx(INFO, "max rd/wr sectors... %02x", resp[28]); + PrintAndLogEx(INFO, "block0 direct wr.... %02x" NOLF, resp[29]); + switch (resp[29]) { + case 0x00: + PrintAndLogEx(NORMAL, " (Activate direct write to block 0 (Same behaviour of Gen2 cards. Some readers may identify the card as magic))"); + break; + case 0x01: + PrintAndLogEx(NORMAL, " (Deactivate direct write to block 0 (Same behaviour of vanilla cards))"); + break; + case 0x02: + PrintAndLogEx(NORMAL, " (Default value. Same behaviour as 00?"); + break; + default: + PrintAndLogEx(NORMAL, " (unknown %02x)", resp[29]); + break; + } + + res = mfG4GetFactoryTest(pwd, resp, &resplen, false); + if (res == PM3_SUCCESS && resplen > 2) { + if (verbose) { + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "Raw test [%02zu]....... %s", resplen, sprint_hex_inrow(resp, resplen)); + } + + if (resp[resplen - 2] == 0x66 && resp[resplen - 1] == 0x66) + PrintAndLogEx(INFO, "Card type........... generic"); + else if (resp[resplen - 2] == 0x02 && resp[resplen - 1] == 0xaa) + PrintAndLogEx(INFO, "Card type........... limited functionality"); + else if (resp[resplen - 2] == 0x03 && resp[resplen - 1] == 0xa0) + PrintAndLogEx(INFO, "Card type........... old card version"); + else if (resp[resplen - 2] == 0x06 && resp[resplen - 1] == 0xa0) + PrintAndLogEx(INFO, "Card type........... new card version"); + else + PrintAndLogEx(INFO, "Card type........... unknown %02x%02x", resp[resplen - 2], resp[resplen - 1]); + } + + // read block 0 + res = mfG4GetBlock(pwd, 0, resp, MAGIC_INIT | MAGIC_OFF); + if (res == PM3_SUCCESS) { + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "Block 0............. %s", sprint_hex_inrow(resp, 16)); + + switch (uid_len) { + case 0x00: + PrintAndLogEx(INFO, "UID [4]............. %s", sprint_hex(resp, 4)); + break; + case 0x01: + PrintAndLogEx(INFO, "UID [7]............. %s", sprint_hex(resp, 7)); + break; + case 0x02: + PrintAndLogEx(INFO, "UID [10]............ %s", sprint_hex(resp, 10)); + break; + default: + break; + } + } + + + return PM3_SUCCESS; +} + // Read block from Gen4 GTU card static int CmdHF14AGen4GetBlk(const char *cmd) { CLIParserContext *ctx; @@ -7198,7 +7628,7 @@ static int CmdHF14AGen4Load(const char *cmd) { arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_str0("p", "pwd", "", "password 4bytes"), arg_lit0("v", "verbose", "verbose output"), - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "emu", "from emulator memory"), arg_int0(NULL, "start", "", "index of block to start writing (default 0)"), arg_int0(NULL, "end", "", "index of block to end writing (default last block)"), @@ -7507,14 +7937,6 @@ static int CmdHF14AGen4View(const char *Cmd) { for (uint16_t i = 0; i < block_cnt; i++) { - // 4k READs can be long, so we split status each 64 blocks. - if (i % 64 == 0) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "" NOLF) ; - } - PrintAndLogEx(NORMAL, "." NOLF); - fflush(stdout); - uint8_t flags = 0 ; if (i == 0) flags |= MAGIC_INIT ; if (i + 1 == block_cnt) flags |= MAGIC_OFF ; @@ -7526,6 +7948,14 @@ static int CmdHF14AGen4View(const char *Cmd) { free(dump); return PM3_ESOFT; } + + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + // 4k READs can be long, so we split status each 64 blocks. + if (i % 64 == 0) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "" NOLF) ; + } } PrintAndLogEx(NORMAL, ""); @@ -7544,7 +7974,7 @@ static int CmdHF14AGen4Save(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf gsave", - "Save `magic gen4 gtu` card memory into three files (BIN/EML/JSON)" + "Save `magic gen4 gtu` card memory to file (bin/json)" "or into emulator memory", "hf mf gsave\n" "hf mf gsave --4k\n" @@ -7556,8 +7986,8 @@ static int CmdHF14AGen4Save(const char *Cmd) { arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), - arg_str0("p", "pwd", "", "password 4bytes"), - arg_str0("f", "file", "", "filename of dump"), + arg_str0("p", "pwd", "", "password 4 bytes"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_lit0(NULL, "emu", "to emulator memory"), arg_param_end }; @@ -7578,6 +8008,10 @@ static int CmdHF14AGen4Save(const char *Cmd) { bool fill_emulator = arg_get_lit(ctx, 7); CLIParserFree(ctx); + // ICEMAN: bug. if device has been using ICLASS commands, + // the device needs to load the HF fpga image. It takes 1.5 second. + set_fpga_mode(FPGA_BITSTREAM_HF); + // validations if (pwd_len != 4 && pwd_len != 0) { PrintAndLogEx(FAILED, "Must specify 4 bytes, got " _YELLOW_("%u"), pwd_len); @@ -7610,15 +8044,13 @@ static int CmdHF14AGen4Save(const char *Cmd) { PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); return PM3_EINVARG; } - PrintAndLogEx(SUCCESS, "Dumping magic gen4 GTU MIFARE Classic " _GREEN_("%s") " card memory", s); - PrintAndLogEx(INFO, "." NOLF); // Select card to get UID/UIDLEN information clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "iso14443a card select timeout"); + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); return PM3_ETIMEOUT; } @@ -7630,7 +8062,7 @@ static int CmdHF14AGen4Save(const char *Cmd) { */ uint64_t select_status = resp.oldarg[0]; if (select_status == 0) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); + PrintAndLogEx(DEBUG, "iso14443a card select failed"); return PM3_SUCCESS; } @@ -7646,63 +8078,72 @@ static int CmdHF14AGen4Save(const char *Cmd) { return PM3_EMALLOC; } + PrintAndLogEx(SUCCESS, "Dumping magic gen4 GTU MIFARE Classic " _GREEN_("%s") " card memory", s); + PrintAndLogEx(INFO, "." NOLF); + for (uint16_t i = 0; i < block_cnt; i++) { - - // 4k READs can be long, so we split status each 64 blocks. - if (i % 64 == 0) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "" NOLF) ; - } - PrintAndLogEx(NORMAL, "." NOLF); - fflush(stdout); - uint8_t flags = 0 ; - if (i == 0) flags |= MAGIC_INIT ; - if (i + 1 == block_cnt) flags |= MAGIC_OFF ; + if (i == 0) { + flags |= MAGIC_INIT; + } + if (i + 1 == block_cnt) { + flags |= MAGIC_OFF; + } int res = mfG4GetBlock(pwd, i, dump + (i * MFBLOCK_SIZE), flags); if (res != PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(WARNING, "Can't get magic card block: %u. error=%d", i, res); PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); free(dump); return PM3_ESOFT; } + + + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + // 4k READs can be long, so we split status each 64 blocks. + if (i % 64 == 0 && i != 0) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "" NOLF) ; + } } PrintAndLogEx(NORMAL, ""); if (fill_emulator) { - PrintAndLogEx(INFO, "uploading to emulator memory" NOLF); + PrintAndLogEx(INFO, "uploading to emulator memory"); + PrintAndLogEx(INFO, "." NOLF); // fast push mode g_conn.block_after_ACK = true; size_t offset = 0; - int cnt = 0; + int cnt = 0; uint16_t bytes_left = bytes ; - while (bytes_left > 0 && cnt < block_cnt) { - // 4k writes can be long, so we split status each 64 blocks. - if (cnt % 64 == 0) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "" NOLF) ; - } - PrintAndLogEx(NORMAL, "." NOLF); - fflush(stdout); + // 12 is the size of the struct the fct mfEmlSetMem_xt uses to transfer to device + uint16_t max_avail_blocks = ((PM3_CMD_DATA_SIZE - 12) / MFBLOCK_SIZE) * MFBLOCK_SIZE; + while (bytes_left > 0 && cnt < block_cnt) { if (bytes_left == MFBLOCK_SIZE) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } - if (mfEmlSetMem_xt(dump + offset, cnt, 1, MFBLOCK_SIZE) != PM3_SUCCESS) { + uint16_t chunk_size = MIN(max_avail_blocks, bytes_left); + uint16_t blocks_to_send = chunk_size / MFBLOCK_SIZE; + + if (mfEmlSetMem_xt(dump + offset, cnt, blocks_to_send, MFBLOCK_SIZE) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "Can't set emulator mem at block: %3d", cnt); free(dump); return PM3_ESOFT; } - cnt++; - offset += MFBLOCK_SIZE; - bytes_left -= MFBLOCK_SIZE; + cnt += blocks_to_send; + offset += chunk_size; + bytes_left -= chunk_size; + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); } PrintAndLogEx(NORMAL, ""); @@ -7716,18 +8157,60 @@ static int CmdHF14AGen4Save(const char *Cmd) { FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); } - saveFile(filename, ".bin", dump, bytes); - saveFileEML(filename, dump, bytes, MFBLOCK_SIZE); - iso14a_mf_extdump_t xdump; - xdump.card_info = card; - xdump.dump = dump; - xdump.dumplen = bytes; - saveFileJSON(filename, jsfCardMemory, (uint8_t *)&xdump, sizeof(xdump), NULL); - + pm3_save_mf_dump(filename, dump, bytes, jsfCardMemory); free(dump); return PM3_SUCCESS; } +// change Gent4 GTU card access password +static int CmdHF14AGen4ChangePwd(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gchpwd", + "Change access password for Gen4 GTU card. WARNING! If you dont KNOW the password - you CAN'T access it!!!", + "hf mf gchpwd --pwd 00000000 --newpwd 01020304" + ); + void *argtable[] = { + arg_param_begin, + arg_str0("p", "pwd", "", "password 4 bytes"), + arg_str0("n", "newpwd", "", "new password 4 bytes"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int pwd_len = 0; + uint8_t pwd[4] = {0}; + CLIGetHexWithReturn(ctx, 1, pwd, &pwd_len); + + int new_pwd_len = 0; + uint8_t new_pwd[4] = {0}; + CLIGetHexWithReturn(ctx, 2, new_pwd, &new_pwd_len); + + bool verbose = arg_get_lit(ctx, 3); + + CLIParserFree(ctx); + + if (pwd_len != 4) { + PrintAndLogEx(FAILED, "Old password must be 4 bytes long, got " _YELLOW_("%u"), pwd_len); + return PM3_EINVARG; + } + + if (new_pwd_len != 4) { + PrintAndLogEx(FAILED, "New password must be 4 bytes long, got " _YELLOW_("%u"), new_pwd_len); + return PM3_EINVARG; + } + + int res = mfG4ChangePassword(pwd, new_pwd, verbose); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Change password error"); + return res; + } + + PrintAndLogEx(SUCCESS, "Change password ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; +} + static int CmdHF14AGen4_GDM_Cfg(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf gdmcfg", @@ -7832,8 +8315,6 @@ static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_int1(NULL, "blk", "", "block number"), - arg_lit0("a", NULL, "input key type is key A (def)"), - arg_lit0("b", NULL, "input key type is key B"), arg_str0("d", "data", "", "bytes to write, 16 hex bytes"), arg_str0("k", "key", "", "key, 6 hex bytes"), arg_lit0(NULL, "force", "override warnings"), @@ -7843,24 +8324,15 @@ static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) { int b = arg_get_int_def(ctx, 1, 1); - uint8_t keytype = MF_KEY_A; - if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) { - CLIParserFree(ctx); - PrintAndLogEx(WARNING, "Input key type must be A or B"); - return PM3_EINVARG; - } else if (arg_get_lit(ctx, 3)) { - keytype = MF_KEY_B;; - } - uint8_t block[MFBLOCK_SIZE] = {0x00}; int blen = 0; - CLIGetHexWithReturn(ctx, 4, block, &blen); + CLIGetHexWithReturn(ctx, 2, block, &blen); int keylen = 0; uint8_t key[6] = {0}; - CLIGetHexWithReturn(ctx, 5, key, &keylen); + CLIGetHexWithReturn(ctx, 3, key, &keylen); - bool force = arg_get_lit(ctx, 6); + bool force = arg_get_lit(ctx, 4); CLIParserFree(ctx); if (blen != MFBLOCK_SIZE) { @@ -7884,18 +8356,16 @@ static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) { return PM3_EINVARG; } - PrintAndLogEx(INFO, "Writing block no %d, key %c - %s", blockno, (keytype == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(INFO, "Writing block no %d, key %s", blockno, sprint_hex_inrow(key, sizeof(key))); PrintAndLogEx(INFO, "data: %s", sprint_hex(block, sizeof(block))); struct p { uint8_t blockno; - uint8_t keytype; uint8_t key[6]; uint8_t data[MFBLOCK_SIZE]; // data to be written } PACKED payload; payload.blockno = blockno; - payload.keytype = keytype; memcpy(payload.key, key, sizeof(payload.key)); memcpy(payload.data, block, sizeof(payload.data)); @@ -7914,7 +8384,6 @@ static int CmdHF14AGen4_GDM_SetBlk(const char *Cmd) { return resp.status; } else { PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); - PrintAndLogEx(HINT, "Maybe access rights? Try specify keytype `" _YELLOW_("hf mf gdmsetblk -%c ...") "` instead", (keytype == MF_KEY_A) ? 'b' : 'a'); } return PM3_SUCCESS; } @@ -7930,24 +8399,24 @@ static int CmdHF14AMfValue(const char *Cmd) { "hf mf value --blk 16 -k FFFFFFFFFFFF -b --get\n" "hf mf value --blk 16 -k FFFFFFFFFFFF --res --transfer 30 --tk FFFFFFFFFFFF --> transfer block 16 value to block 30 (even if block can't be incremented by ACL)\n" "hf mf value --get -d 87D612007829EDFF87D6120011EE11EE\n" - ); + ); void *argtable[] = { - arg_param_begin, - arg_str0("k", "key", "", "key, 6 hex bytes"), - arg_lit0("a", NULL, "input key type is key A (def)"), - arg_lit0("b", NULL, "input key type is key B"), - arg_u64_0(NULL, "inc", "", "Increment value by X (0 - 2147483647)"), - arg_u64_0(NULL, "dec", "", "Decrement value by X (0 - 2147483647)"), - arg_u64_0(NULL, "set", "", "Set value to X (-2147483647 - 2147483647)"), - arg_u64_0(NULL, "transfer", "", "Transfer value to other block (after inc/dec/restore)"), - arg_str0(NULL, "tkey", "", "transfer key, 6 hex bytes (if transfer is preformed to other sector)"), - arg_lit0(NULL, "ta", "transfer key type is key A (def)"), - arg_lit0(NULL, "tb", "transfer key type is key B"), - arg_lit0(NULL, "get", "Get value from block"), - arg_lit0(NULL, "res", "Restore (copy value to card buffer, should be used with --transfer)"), - arg_int0(NULL, "blk", "", "block number"), - arg_str0("d", "data", "", "block data to extract values from (16 hex bytes)"), - arg_param_end + arg_param_begin, + arg_str0("k", "key", "", "key, 6 hex bytes"), + arg_lit0("a", NULL, "input key type is key A (def)"), + arg_lit0("b", NULL, "input key type is key B"), + arg_u64_0(NULL, "inc", "", "Increment value by X (0 - 2147483647)"), + arg_u64_0(NULL, "dec", "", "Decrement value by X (0 - 2147483647)"), + arg_u64_0(NULL, "set", "", "Set value to X (-2147483647 - 2147483647)"), + arg_u64_0(NULL, "transfer", "", "Transfer value to other block (after inc/dec/restore)"), + arg_str0(NULL, "tkey", "", "transfer key, 6 hex bytes (if transfer is preformed to other sector)"), + arg_lit0(NULL, "ta", "transfer key type is key A (def)"), + arg_lit0(NULL, "tb", "transfer key type is key B"), + arg_lit0(NULL, "get", "Get value from block"), + arg_lit0(NULL, "res", "Restore (copy value to card buffer, should be used with --transfer)"), + arg_int0(NULL, "blk", "", "block number"), + arg_str0("d", "data", "", "block data to extract values from (16 hex bytes)"), + arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -8004,7 +8473,7 @@ static int CmdHF14AMfValue(const char *Cmd) { // Action: 0 Increment, 1 - Decrement, 2 - Restore, 3 - Set, 4 - Get, 5 - Decode from data // iceman: TODO - should be enum - uint8_t action = 4; + uint8_t action = 4; uint32_t value = 0; // Need to check we only have 1 of inc/dec/set and get the value from the selected option @@ -8075,10 +8544,10 @@ static int CmdHF14AMfValue(const char *Cmd) { } // don't want to write value data and break something - if ((blockno == 0) || - (mfIsSectorTrailer(blockno)) || - (trnval == 0) || - (trnval != -1 && mfIsSectorTrailer(trnval))) { + if ((blockno == 0) || + (mfIsSectorTrailer(blockno)) || + (trnval == 0) || + (trnval != -1 && mfIsSectorTrailer(trnval))) { PrintAndLogEx(WARNING, "invalid block number, should be a data block"); return PM3_EINVARG; } @@ -8096,7 +8565,7 @@ static int CmdHF14AMfValue(const char *Cmd) { memcpy(block, (uint8_t *)&value, 4); uint8_t cmddata[34]; - memcpy(cmddata, key, sizeof(key)); + memcpy(cmddata, key, sizeof(key)); // Key == 6 data went to 10, so lets offset 9 for inc/dec if (action == 0) { @@ -8107,13 +8576,13 @@ static int CmdHF14AMfValue(const char *Cmd) { } // 00 if increment, 01 if decrement, 02 if restore - cmddata[9] = action; - + cmddata[9] = action; + if (trnval != -1) { // transfer to block - cmddata[10] = trnval; - + cmddata[10] = trnval; + memcpy(cmddata + 27, transferkey, sizeof(transferkey)); if (mfSectorNum(trnval) != mfSectorNum(blockno)) { cmddata[33] = 1; // should send nested auth @@ -8166,7 +8635,7 @@ static int CmdHF14AMfValue(const char *Cmd) { if (isok) { PrintAndLogEx(SUCCESS, "Update ... : " _GREEN_("success")); - getval = true; + getval = true; // all ok so set flag to read current value } else { PrintAndLogEx(FAILED, "Update ... : " _RED_("failed")); @@ -8207,10 +8676,380 @@ static int CmdHF14AMfValue(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHFMFHidEncode(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf encodehid", + "Encode binary wiegand to card\n" + "Use either --bin or --wiegand/--fc/--cn", + "hf mf encodehid --bin 10001111100000001010100011 -> FC 31 CN 337 (H10301)\n" + "hf mf encodehid -w H10301 --fc 31 --cn 337\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "bin", "", "Binary string i.e 0001001001"), + arg_u64_0(NULL, "fc", "", "facility code"), + arg_u64_0(NULL, "cn", "", "card number"), + arg_str0("w", "wiegand", "", "see " _YELLOW_("`wiegand list`") " for available formats"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int bin_len = 120; + uint8_t bin[121] = {0}; + CLIGetStrWithReturn(ctx, 1, bin, &bin_len); + + wiegand_card_t card; + memset(&card, 0, sizeof(wiegand_card_t)); + card.FacilityCode = arg_get_u32_def(ctx, 2, 0); + card.CardNumber = arg_get_u32_def(ctx, 3, 0); + + char format[16] = {0}; + int format_len = 0; + CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)format, sizeof(format), &format_len); + + bool verbose = arg_get_lit(ctx, 5); + CLIParserFree(ctx); + + // santity checks + if (bin_len > 120) { + PrintAndLogEx(ERR, "Binary wiegand string must be less than 120 bits"); + return PM3_EINVARG; + } + + if (bin_len == 0 && card.FacilityCode == 0 && card.CardNumber == 0) { + PrintAndLogEx(ERR, "Must provide either --cn/--fc or --bin"); + return PM3_EINVARG; + } + + uint8_t blocks[] = { + 0x1B, 0x01, 0x4D, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0x78, 0x77, 0x88, 0xC1, 0x89, 0xEC, 0xA9, 0x7F, 0x8C, 0x2A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x49, 0x44, 0x20, 0x49, 0x53, 0x78, 0x77, 0x88, 0xAA, 0x20, 0x47, 0x52, 0x45, 0x41, 0x54, + }; + + if (bin_len) { + char mfcbin[121] = {0}; + mfcbin[0] = '1'; + memcpy(mfcbin + 1, bin, bin_len); + + size_t hexlen = 0; + uint8_t hex[15] = {0}; + binstr_2_bytes(hex, &hexlen, mfcbin); + + memcpy(blocks + (MFBLOCK_SIZE * 4) + 1 + (15 - hexlen), hex, hexlen); + } else { + wiegand_message_t packed; + memset(&packed, 0, sizeof(wiegand_message_t)); + + int format_idx = HIDFindCardFormat(format); + if (format_idx == -1) { + PrintAndLogEx(WARNING, "Unknown format: " _YELLOW_("%s"), format); + return PM3_EINVARG; + } + + if (HIDPack(format_idx, &card, &packed, false) == false) { + PrintAndLogEx(WARNING, "The card data could not be encoded in the selected format."); + return PM3_ESOFT; + } + + // iceman: only for formats w length smaller than 37. + // Needs a check. + + // increase length to allow setting bit just above real data + packed.Length++; + // Set sentinel bit + set_bit_by_position(&packed, true, 0); + +#ifdef HOST_LITTLE_ENDIAN + packed.Mid = BSWAP_32(packed.Mid); + packed.Bot = BSWAP_32(packed.Bot); +#endif + + memcpy(blocks + (MFBLOCK_SIZE * 4) + 8, &packed.Mid, sizeof(packed.Mid)); + memcpy(blocks + (MFBLOCK_SIZE * 4) + 12, &packed.Bot, sizeof(packed.Bot)); + } + + uint8_t empty[MIFARE_KEY_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + bool res = true; + for (uint8_t i = 0; i < (sizeof(blocks) / MFBLOCK_SIZE); i++) { + + if (verbose) { + PrintAndLogEx(INFO, "Writing %u - %s", (i + 1), sprint_hex_inrow(blocks + (i * MFBLOCK_SIZE), MFBLOCK_SIZE)); + } + + if (mf_write_block(empty, MF_KEY_A, (i + 1), blocks + (i * MFBLOCK_SIZE)) == false) { + if (mf_write_block(empty, MF_KEY_B, (i + 1), blocks + (i * MFBLOCK_SIZE)) == false) { + PrintAndLogEx(WARNING, "failed writing block %d using default empty key", (i + 1)); + res = false; + break; + } + } + } + if (res == false) { + PrintAndLogEx(WARNING, "Make sure card is wiped before running this command"); + } + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + +static int CmdHF14AMfInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf info", + "Information and check vulnerabilities in a MIFARE Classic card\n" + "Some cards in order to extract information you need to specify key\n" + "and/or specific keys in the command line", + "hf mf info\n" + "hf mf info -k FFFFFFFFFFFF -n -v\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0(NULL, "blk", "", "block number"), + arg_lit0("a", NULL, "input key type is key A (def)"), + arg_lit0("b", NULL, "input key type is key B"), + arg_str0("k", "key", "", "key, 6 hex bytes"), + arg_lit0("n", "nack", "do nack test"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int blockn = arg_get_int_def(ctx, 1, 0); + + uint8_t keytype = MF_KEY_A; + if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 3)) { + keytype = MF_KEY_B; + } + + int keylen = 0; + uint8_t key[MIFARE_KEY_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + CLIGetHexWithReturn(ctx, 4, key, &keylen); + + bool do_nack_test = arg_get_lit(ctx, 5); + bool verbose = arg_get_lit(ctx, 6); + CLIParserFree(ctx); + + uint8_t dbg_curr = DBG_NONE; + if (getDeviceDebugLevel(&dbg_curr) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + if (keylen != 0 && keylen != MIFARE_KEY_SIZE) { + PrintAndLogEx(ERR, "Key length must be %u bytes", MIFARE_KEY_SIZE); + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); + return 0; + } + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + + /* + 0: couldn't read + 1: OK, with ATS + 2: OK, no ATS + 3: proprietary Anticollision + */ + uint64_t select_status = resp.oldarg[0]; + + if (select_status == 0) { + PrintAndLogEx(DEBUG, "iso14443a card select failed"); + return select_status; + } + + if (select_status == 3) { + PrintAndLogEx(INFO, "Card doesn't support standard iso14443-3 anticollision"); + + if (verbose) { + PrintAndLogEx(SUCCESS, "ATQA: %02X %02X", card.atqa[1], card.atqa[0]); + } + + return select_status; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " ---------------------"); + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); + PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]); + + if (setDeviceDebugLevel(verbose ? DBG_INFO : DBG_NONE, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + uint8_t signature[32] = {0}; + int res = read_mfc_ev1_signature(signature); + if (res == PM3_SUCCESS) { + mfc_ev1_print_signature(card.uid, card.uidlen, signature, sizeof(signature)); + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Keys Information")); + + /* + 1. fast check for different KDF here + 2. mew command "hf mf keygen" + + " Vingcard algo"); + PrintAndLogEx(INFO, " Saflok algo"); + PrintAndLogEx(INFO, " SALTO algo"); + uint64_t key = 0; + mfc_algo_saflok_one(uid, 1, 0, &key); + PrintAndLogEx(INFO, " Dorma Kaba algo | %012X" PRIX64, key); + PrintAndLogEx(INFO, " STiD algo"); + PrintAndLogEx(INFO, "-------------------------------------"); + */ + + + uint8_t fkey[MIFARE_KEY_SIZE] = {0}; + uint8_t fKeyType = 0xff; + + int sectorsCnt = 1; + uint8_t *keyBlock = NULL; + uint32_t keycnt = 0; + res = mfLoadKeys(&keyBlock, &keycnt, key, MIFARE_KEY_SIZE, NULL, 0); + if (res != PM3_SUCCESS) { + return res; + } + + // create/initialize key storage structure + sector_t *e_sector = NULL; + if (initSectorTable(&e_sector, sectorsCnt) != PM3_SUCCESS) { + free(keyBlock); + return PM3_EMALLOC; + } + + res = mfCheckKeys_fast(sectorsCnt, true, true, 1, keycnt, keyBlock, e_sector, false, verbose); + if (res == PM3_SUCCESS || res == PM3_EPARTIAL) { + uint8_t blockdata[MFBLOCK_SIZE] = {0}; + + if (e_sector[0].foundKey[MF_KEY_A]) { + PrintAndLogEx(SUCCESS, "Sector 0 key A... " _GREEN_("%012" PRIX64), e_sector[0].Key[MF_KEY_A]); + + num_to_bytes(e_sector[0].Key[MF_KEY_A], MIFARE_KEY_SIZE, fkey); + if (mfReadBlock(0, MF_KEY_A, key, blockdata) == PM3_SUCCESS) { + fKeyType = MF_KEY_A; + } + } + + if (e_sector[0].foundKey[MF_KEY_B]) { + PrintAndLogEx(SUCCESS, "Sector 0 key B... " _GREEN_("%012" PRIX64), e_sector[0].Key[MF_KEY_B]); + + if (fKeyType == 0xFF) { + num_to_bytes(e_sector[0].Key[MF_KEY_B], MIFARE_KEY_SIZE, fkey); + if (mfReadBlock(0, MF_KEY_B, key, blockdata) == PM3_SUCCESS) { + fKeyType = MF_KEY_B; + } + } + } + + if (fKeyType != 0xFF) { + PrintAndLogEx(SUCCESS, "Block 0.......... %s", sprint_hex(blockdata, MFBLOCK_SIZE)); + } + + if ( + (blockdata[8] == 0x03 && blockdata[15] == 0x90) || + (blockdata[9] == 0x02 && blockdata[14] == 0x1D) || + (blockdata[8] == 0x04 && blockdata[15] == 0x90) || + (memcmp(blockdata + 8, "\x62\x63\x64\x65\x66\x67\x68\x69", 8) == 0) + ) { + PrintAndLogEx(SUCCESS, " Fudan tag detected"); + } + + } else { + PrintAndLogEx(INFO, ""); + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Magic Tag Information")); + if (detect_mf_magic(true, MF_KEY_B, e_sector[0].Key[MF_KEY_B]) == 0) { + if (detect_mf_magic(true, MF_KEY_A, e_sector[0].Key[MF_KEY_A]) == 0) { + PrintAndLogEx(INFO, ""); + } + } + + + free(keyBlock); + free(e_sector); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("PRNG Information")); + + res = detect_classic_static_nonce(); + if (res == NONCE_STATIC) { + PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); + } + + if (res == NONCE_FAIL && verbose) { + PrintAndLogEx(SUCCESS, "Static nonce......... " _RED_("read failed")); + } + + if (res == NONCE_NORMAL) { + // not static + res = detect_classic_prng(); + if (res == 1) + PrintAndLogEx(SUCCESS, "Prng................. " _GREEN_("weak")); + else if (res == 0) + PrintAndLogEx(SUCCESS, "Prng................. " _YELLOW_("hard")); + else + PrintAndLogEx(FAILED, "Prng................. " _RED_("fail")); + + + // detect static encrypted nonce + if (keylen == MIFARE_KEY_SIZE) { + res = detect_classic_static_encrypted_nonce(blockn, keytype, key); + if (res == NONCE_STATIC) { + PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); + fKeyType = 0xFF; // dont detect twice + } + if (res == NONCE_STATIC_ENC) { + PrintAndLogEx(SUCCESS, "Static enc nonce..... " _RED_("yes")); + fKeyType = 0xFF; // dont detect twice + } + } + + if (fKeyType != 0xFF) { + res = detect_classic_static_encrypted_nonce(0, fKeyType, fkey); + if (res == NONCE_STATIC) + PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); + if (res == NONCE_STATIC_ENC) + PrintAndLogEx(SUCCESS, "Static enc nonce..... " _RED_("yes")); + } + + if (do_nack_test) { + detect_classic_nackbug(verbose); + } + } + + if (setDeviceDebugLevel(dbg_curr, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("recovery") " -----------------------"}, + {"info", CmdHF14AMfInfo, IfPm3Iso14443a, "mfc card Info"}, {"darkside", CmdHF14AMfDarkside, IfPm3Iso14443a, "Darkside attack"}, {"nested", CmdHF14AMfNested, IfPm3Iso14443a, "Nested attack"}, {"hardnested", CmdHF14AMfNestedHard, AlwaysAvailable, "Nested attack for hardened MIFARE Classic cards"}, @@ -8220,7 +9059,7 @@ static command_t CommandTable[] = { {"nack", CmdHf14AMfNack, IfPm3Iso14443a, "Test for MIFARE NACK bug"}, {"chk", CmdHF14AMfChk, IfPm3Iso14443a, "Check keys"}, {"fchk", CmdHF14AMfChk_fast, IfPm3Iso14443a, "Check keys fast, targets all keys on card"}, - {"decrypt", CmdHf14AMfDecryptBytes, AlwaysAvailable, "[nt] [ar_enc] [at_enc] [data] - to decrypt sniff or trace"}, + {"decrypt", CmdHf14AMfDecryptBytes, AlwaysAvailable, "Decrypt Crypto1 data from sniff or trace"}, {"supercard", CmdHf14AMfSuperCard, IfPm3Iso14443a, "Extract info from a `super card`"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, {"auth4", CmdHF14AMfAuth4, IfPm3Iso14443a, "ISO14443-4 AES authentication"}, @@ -8261,11 +9100,13 @@ static command_t CommandTable[] = { {"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite manufacturer block"}, {"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock UID changes. irreversible"}, {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GTU") " --------------------------"}, + {"ginfo", CmdHF14AGen4Info, IfPm3Iso14443a, "Info about configuration of the card"}, {"ggetblk", CmdHF14AGen4GetBlk, IfPm3Iso14443a, "Read block from card"}, {"gload", CmdHF14AGen4Load, IfPm3Iso14443a, "Load dump to card"}, {"gsave", CmdHF14AGen4Save, IfPm3Iso14443a, "Save dump from card into file or emulator"}, {"gsetblk", CmdHF14AGen4SetBlk, IfPm3Iso14443a, "Write block to card"}, {"gview", CmdHF14AGen4View, IfPm3Iso14443a, "View card"}, + {"gchpwd", CmdHF14AGen4ChangePwd, IfPm3Iso14443a, "Change card access password. Warning!"}, {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("magic gen4 GDM") " --------------------------"}, {"gdmcfg", CmdHF14AGen4_GDM_Cfg, IfPm3Iso14443a, "Read config block from card"}, {"gdmsetcfg", CmdHF14AGen4_GDM_SetCfg, IfPm3Iso14443a, "Write config block to card"}, @@ -8275,6 +9116,7 @@ static command_t CommandTable[] = { {"ndefformat", CmdHFMFNDEFFormat, IfPm3Iso14443a, "Format MIFARE Classic Tag as NFC Tag"}, {"ndefread", CmdHFMFNDEFRead, IfPm3Iso14443a, "Read and print NDEF records from card"}, {"ndefwrite", CmdHFMFNDEFWrite, IfPm3Iso14443a, "Write NDEF records to card"}, + {"encodehid", CmdHFMFHidEncode, IfPm3Iso14443a, "Encode a HID Credential / NDEF record to card"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfmf.h b/client/src/cmdhfmf.h index 5c21c7fb3..4e92bca70 100644 --- a/client/src/cmdhfmf.h +++ b/client/src/cmdhfmf.h @@ -34,6 +34,11 @@ void showSectorTable(sector_t *k_sector, size_t k_sectors_cnt); void readerAttack(sector_t *k_sector, size_t k_sectors_cnt, nonces_t data, bool setEmulatorMem, bool verbose); void printKeyTable(size_t sectorscnt, sector_t *e_sector); void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector); +// void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector, bool singel_sector); + +bool mfc_value(const uint8_t *d, int32_t *val); +void mf_print_sector_hdr(uint8_t sector); +void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose); 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 b2cd1f7a5..88166a8f2 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -372,8 +372,7 @@ static int mfdes_get_info(mfdes_info_res_t *info) { } // --- GET SIGNATURE -static int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, size_t signature_len, nxp_cardtype_t card_type) { - (void)card_type; +int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, size_t signature_len) { if (uid == NULL) { PrintAndLogEx(DEBUG, "UID=NULL"); @@ -445,7 +444,7 @@ static void swap24(uint8_t *data) { uint8_t tmp = data[0]; data[0] = data[2]; data[2] = tmp; -}; +} // default parameters static uint8_t defaultKeyNum = 0; @@ -742,7 +741,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { res = DesfireReadSignature(&dctx, 0x00, signature, &signature_len); if (res == PM3_SUCCESS) { if (signature_len == 56) - desfire_print_signature(info.uid, info.uidlen, signature, signature_len, cardtype); + desfire_print_signature(info.uid, info.uidlen, signature, signature_len); else PrintAndLogEx(WARNING, "--- GetSignature returned wrong signature length: %zu", signature_len); } else { @@ -808,9 +807,9 @@ static int CmdHF14ADesInfo(const char *Cmd) { iso14a_card_select_t card; res = SelectCard14443A_4(true, false, &card); if (res == PM3_SUCCESS) { - static const char STANDALONE_DESFIRE[] = { 0x75, 0x77, 0x81, 0x02 }; - static const char JCOP_DESFIRE[] = { 0x75, 0xf7, 0xb1, 0x02 }; - static const char JCOP3_DESFIRE[] = { 0x78, 0x77, 0x71, 0x02 }; + static const uint8_t STANDALONE_DESFIRE[] = { 0x75, 0x77, 0x81, 0x02 }; + static const uint8_t JCOP_DESFIRE[] = { 0x75, 0xf7, 0xb1, 0x02 }; + static const uint8_t JCOP3_DESFIRE[] = { 0x78, 0x77, 0x71, 0x02 }; if (card.sak == 0x20) { @@ -1126,9 +1125,9 @@ static int CmdHF14aDesChk(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes chk", "Checks keys with MIFARE DESFire card.", "hf mfdes chk --aid 123456 -k 000102030405060708090a0b0c0d0e0f -> check key on aid 0x123456\n" - "hf mfdes chk -d mfdes_default_keys -> check keys from dictionary against all existing aid on card\n" - "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys from dictionary against aid 0x123456\n" - "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to json\n" + "hf mfdes chk -d mfdes_default_keys -> check keys against all existing aid on card\n" + "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys against aid 0x123456\n" + "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to `keys.json`\n" "hf mfdes chk --aid 123456 --pattern2b --startp2b FA00 -> check all 2-byte keys pattern on aid 0x123456. Start from key FA00FA00...FA00"); void *argtable[] = { @@ -1411,7 +1410,7 @@ static int CmdHF14aDesChk(const char *Cmd) { } static int CmdHF14ADesList(const char *Cmd) { - return CmdTraceListAlias(Cmd, "hf mfdes", "des"); + return CmdTraceListAlias(Cmd, "hf mfdes", "des -c"); } static int DesfireAuthCheck(DesfireContext_t *dctx, DesfireISOSelectWay way, uint32_t appID, DesfireSecureChannel secureChannel, uint8_t *key) { diff --git a/client/src/cmdhfmfdes.h b/client/src/cmdhfmfdes.h index 88d2cdb32..80b3e8c93 100644 --- a/client/src/cmdhfmfdes.h +++ b/client/src/cmdhfmfdes.h @@ -28,6 +28,8 @@ char *getVersionStr(uint8_t major, uint8_t minor); int getKeySettings(uint8_t *aid); */ +int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, size_t signature_len); + // Ev1 card limits #define MAX_NUM_KEYS 0x0F #define MAX_APPLICATION_COUNT 28 diff --git a/client/src/cmdhfmfhard.c b/client/src/cmdhfmfhard.c index acbf32ea5..f8f894e3c 100644 --- a/client/src/cmdhfmfhard.c +++ b/client/src/cmdhfmfhard.c @@ -30,6 +30,7 @@ #include #include #include // MingW +#include #include #include "commonutil.h" // ARRAYLEN @@ -51,7 +52,9 @@ #define IGNORE_BITFLIP_THRESHOLD 0.9901 #define STATE_FILES_DIRECTORY "hardnested_tables/" -#define STATE_FILE_TEMPLATE "bitflip_%d_%03" PRIx16 "_states.bin.bz2" +#define STATE_FILE_TEMPLATE_RAW "bitflip_%d_%03" PRIx16 "_states.bin" +#define STATE_FILE_TEMPLATE_LZ4 "bitflip_%d_%03" PRIx16 "_states.bin.lz4" +#define STATE_FILE_TEMPLATE_BZ2 "bitflip_%d_%03" PRIx16 "_states.bin.bz2" #define DEBUG_KEY_ELIMINATION // #define DEBUG_REDUCTION @@ -134,7 +137,7 @@ static void print_progress_header(void) { void hardnested_print_progress(uint32_t nonces, const char *activity, float brute_force, uint64_t min_diff_print_time) { static uint64_t last_print_time = 0; - if (msclock() - last_print_time > min_diff_print_time) { + if (msclock() - last_print_time >= min_diff_print_time) { last_print_time = msclock(); uint64_t total_time = msclock() - start_time; float brute_force_time = brute_force / brute_force_per_second; @@ -255,51 +258,193 @@ static void init_bitflip_bitarrays(void) { #if defined (DEBUG_REDUCTION) uint8_t line = 0; #endif + uint64_t init_bitflip_bitarrays_starttime = msclock(); - bz_stream compressed_stream; - - char state_files_path[strlen(get_my_executable_directory()) + strlen(STATE_FILES_DIRECTORY) + strlen(STATE_FILE_TEMPLATE) + 1]; - char state_file_name[strlen(STATE_FILE_TEMPLATE) + 1]; - + char state_file_name[MAX(strlen(STATE_FILE_TEMPLATE_RAW), MAX(strlen(STATE_FILE_TEMPLATE_LZ4), strlen(STATE_FILE_TEMPLATE_BZ2))) + 1]; + char state_files_path[strlen(get_my_executable_directory()) + strlen(STATE_FILES_DIRECTORY) + sizeof(state_file_name)]; + uint16_t nraw = 0, nlz4 = 0, nbz2 = 0; for (odd_even_t odd_even = EVEN_STATE; odd_even <= ODD_STATE; odd_even++) { num_effective_bitflips[odd_even] = 0; for (uint16_t bitflip = 0x001; bitflip < 0x400; bitflip++) { + bool open_uncompressed = false; + bool open_lz4compressed = false; + bool open_bz2compressed = false; + bitflip_bitarrays[odd_even][bitflip] = NULL; count_bitflip_bitarrays[odd_even][bitflip] = 1 << 24; - snprintf(state_file_name, sizeof(state_file_name), STATE_FILE_TEMPLATE, odd_even, bitflip); + char *path; + snprintf(state_file_name, sizeof(state_file_name), STATE_FILE_TEMPLATE_RAW, odd_even, bitflip); strncpy(state_files_path, STATE_FILES_DIRECTORY, sizeof(state_files_path) - 1); strncat(state_files_path, state_file_name, sizeof(state_files_path) - (strlen(STATE_FILES_DIRECTORY) + 1)); - - char *path; - if (searchFile(&path, RESOURCES_SUBDIR, state_files_path, "", true) != PM3_SUCCESS) { - continue; + if (searchFile(&path, RESOURCES_SUBDIR, state_files_path, "", true) == PM3_SUCCESS) { + open_uncompressed = true; + } else { + snprintf(state_file_name, sizeof(state_file_name), STATE_FILE_TEMPLATE_LZ4, odd_even, bitflip); + strncpy(state_files_path, STATE_FILES_DIRECTORY, sizeof(state_files_path) - 1); + strncat(state_files_path, state_file_name, sizeof(state_files_path) - (strlen(STATE_FILES_DIRECTORY) + 1)); + if (searchFile(&path, RESOURCES_SUBDIR, state_files_path, "", true) == PM3_SUCCESS) { + open_lz4compressed = true; + } else { + snprintf(state_file_name, sizeof(state_file_name), STATE_FILE_TEMPLATE_BZ2, odd_even, bitflip); + strncpy(state_files_path, STATE_FILES_DIRECTORY, sizeof(state_files_path) - 1); + strncat(state_files_path, state_file_name, sizeof(state_files_path) - (strlen(STATE_FILES_DIRECTORY) + 1)); + if (searchFile(&path, RESOURCES_SUBDIR, state_files_path, "", true) == PM3_SUCCESS) { + open_bz2compressed = true; + } else { + continue; + } + } } FILE *statesfile = fopen(path, "rb"); free(path); if (statesfile == NULL) { continue; - } else { - fseek(statesfile, 0, SEEK_END); - int fsize = ftell(statesfile); - if (fsize == -1) { + } + + fseek(statesfile, 0, SEEK_END); + int fsize = ftell(statesfile); + if (fsize == -1) { + PrintAndLogEx(ERR, "File read error with %s. Aborting...\n", state_file_name); + fclose(statesfile); + exit(5); + } + uint32_t filesize = (uint32_t)fsize; + rewind(statesfile); + + if (open_uncompressed) { + + uint32_t count = 0; + size_t bytesread = fread(&count, 1, sizeof(count), statesfile); + if (bytesread != 4) { PrintAndLogEx(ERR, "File read error with %s. Aborting...\n", state_file_name); fclose(statesfile); exit(5); } - uint32_t filesize = (uint32_t)fsize; - rewind(statesfile); + + if ((float)count / (1 << 24) < IGNORE_BITFLIP_THRESHOLD) { + uint32_t *bitset = (uint32_t *)malloc_bitarray(sizeof(uint32_t) * (1 << 19)); + if (bitset == NULL) { + PrintAndLogEx(ERR, "Out of memory error in init_bitflip_statelists(). Aborting...\n"); + fclose(statesfile); + exit(4); + } + + bytesread = fread(bitset, 1, filesize - sizeof(count), statesfile); + if (bytesread != filesize - sizeof(count)) { + PrintAndLogEx(ERR, "File read error with %s. Aborting...\n", state_file_name); + fclose(statesfile); + exit(5); + } + + effective_bitflip[odd_even][num_effective_bitflips[odd_even]++] = bitflip; + bitflip_bitarrays[odd_even][bitflip] = bitset; + count_bitflip_bitarrays[odd_even][bitflip] = count; +#if defined (DEBUG_REDUCTION) + PrintAndLogEx(INFO, "(%03" PRIx16 " %s:%5.1f%%) ", bitflip, odd_even ? "odd " : "even", (float)count / (1 << 24) * 100.0); + line++; + if (line == 8) { + PrintAndLogEx(NORMAL, ""); + line = 0; + } +#endif + } + fclose(statesfile); + nraw++; + continue; + + } else if (open_lz4compressed) { + + char *compressed_data = calloc(filesize, sizeof(uint8_t)); + if (compressed_data == NULL) { + PrintAndLogEx(ERR, "Out of memory error in init_bitflip_statelists(). Aborting...\n"); + fclose(statesfile); + exit(4); + } + size_t bytesread = fread(compressed_data, 1, filesize, statesfile); + if (bytesread != filesize) { + PrintAndLogEx(ERR, "File read error with %s (2). Aborting...\n", state_file_name); + free(compressed_data); + fclose(statesfile); + exit(5); + } + fclose(statesfile); + + char *uncompressed_data = calloc((sizeof(uint32_t) * (1 << 19)) + sizeof(uint32_t), sizeof(uint8_t)); + if (uncompressed_data == NULL) { + PrintAndLogEx(ERR, "Out of memory error in init_bitflip_statelists(). Aborting...\n"); + free(compressed_data); + exit(4); + } + + LZ4F_decompressionContext_t ctx; + LZ4F_errorCode_t result = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); + if (LZ4F_isError(result)) { + PrintAndLogEx(ERR, "File read error with %s (3) Failed to create decompression context: %s. Aborting...\n", state_file_name, LZ4F_getErrorName(result)); + free(compressed_data); + free(uncompressed_data); + exit(5); + } + + size_t expected_output_size = (sizeof(uint32_t) * (1 << 19)) + sizeof(uint32_t); + size_t consumed_input_size = filesize; + size_t generated_output_size = expected_output_size; + result = LZ4F_decompress(ctx, uncompressed_data, &generated_output_size, compressed_data, &consumed_input_size, NULL); + + LZ4F_freeDecompressionContext(ctx); + free(compressed_data); + + if (LZ4F_isError(result)) { + PrintAndLogEx(ERR, "File read error with %s (3) %s. Aborting...\n", state_file_name, LZ4F_getErrorName(result)); + free(uncompressed_data); + exit(5); + } + if (generated_output_size != expected_output_size) { + PrintAndLogEx(ERR, "File read error with %s (3) got %lu instead of %lu bytes. Aborting...\n", state_file_name, generated_output_size, expected_output_size); + free(uncompressed_data); + exit(5); + } + + uint32_t count; + memcpy(&count, uncompressed_data, sizeof(uint32_t)); + + if ((float)count / (1 << 24) < IGNORE_BITFLIP_THRESHOLD) { + uint32_t *bitset = (uint32_t *)malloc_bitarray(sizeof(uint32_t) * (1 << 19)); + if (bitset == NULL) { + PrintAndLogEx(ERR, "Out of memory error in init_bitflip_statelists(). Aborting...\n"); + free(uncompressed_data); + exit(4); + } + memcpy(bitset, uncompressed_data + sizeof(uint32_t), sizeof(uint32_t) * (1 << 19)); + effective_bitflip[odd_even][num_effective_bitflips[odd_even]++] = bitflip; + bitflip_bitarrays[odd_even][bitflip] = bitset; + count_bitflip_bitarrays[odd_even][bitflip] = count; +#if defined (DEBUG_REDUCTION) + PrintAndLogEx(INFO, "(%03" PRIx16 " %s:%5.1f%%) ", bitflip, odd_even ? "odd " : "even", (float)count / (1 << 24) * 100.0); + line++; + if (line == 8) { + PrintAndLogEx(NORMAL, ""); + line = 0; + } +#endif + } + free(uncompressed_data); + nlz4++; + continue; + } else if (open_bz2compressed) { + char input_buffer[filesize]; size_t bytesread = fread(input_buffer, 1, filesize, statesfile); if (bytesread != filesize) { PrintAndLogEx(ERR, "File read error with %s. Aborting...\n", state_file_name); fclose(statesfile); - //BZ2_bzDecompressEnd(&compressed_stream); exit(5); } fclose(statesfile); + uint32_t count = 0; + bz_stream compressed_stream; init_bunzip2(&compressed_stream, input_buffer, filesize, (char *)&count, sizeof(count)); int res = BZ2_bzDecompress(&compressed_stream); if (res != BZ_OK) { @@ -335,11 +480,16 @@ static void init_bitflip_bitarrays(void) { #endif } BZ2_bzDecompressEnd(&compressed_stream); + nbz2++; } } effective_bitflip[odd_even][num_effective_bitflips[odd_even]] = 0x400; // EndOfList marker } - + { + char progress_text[80]; + snprintf(progress_text, sizeof(progress_text), "Loaded %u RAW / %u LZ4 / %u BZ2 in %"PRIu64" ms", nraw, nlz4, nbz2, msclock() - init_bitflip_bitarrays_starttime); + hardnested_print_progress(0, progress_text, (float)(1LL << 47), 0); + } uint16_t i = 0; uint16_t j = 0; num_all_effective_bitflips = 0; @@ -374,9 +524,11 @@ static void init_bitflip_bitarrays(void) { PrintAndLogEx(INFO, "%03x ", all_effective_bitflip[i]); } #endif - char progress_text[80]; - snprintf(progress_text, sizeof(progress_text), "Using %d precalculated bitflip state tables", num_all_effective_bitflips); - hardnested_print_progress(0, progress_text, (float)(1LL << 47), 0); + { + char progress_text[80]; + snprintf(progress_text, sizeof(progress_text), "Using %d precalculated bitflip state tables", num_all_effective_bitflips); + hardnested_print_progress(0, progress_text, (float)(1LL << 47), 0); + } } static void free_bitflip_bitarrays(void) { @@ -510,7 +662,7 @@ static char failstr[250] = ""; #endif // the probability that a random nonce has a Sum Property K -static const float p_K0[NUM_SUMS] = { +static const float p_K0[NUM_SUMS] = { 0.0290, 0.0083, 0.0006, 0.0339, 0.0048, 0.0934, 0.0119, 0.0489, 0.0602, 0.4180, 0.0602, 0.0489, 0.0119, 0.0934, 0.0048, 0.0339, 0.0006, 0.0083, 0.0290 @@ -1223,31 +1375,32 @@ __attribute__((force_align_arg_pointer)) static void check_for_BitFlipProperties(bool time_budget) { // create and run worker threads - pthread_t thread_id[NUM_CHECK_BITFLIPS_THREADS]; + const size_t num_check_bitflip_threads = NUM_CHECK_BITFLIPS_THREADS; + pthread_t thread_id[num_check_bitflip_threads]; - uint8_t args[NUM_CHECK_BITFLIPS_THREADS][3]; - uint16_t bytes_per_thread = (256 + (NUM_CHECK_BITFLIPS_THREADS / 2)) / NUM_CHECK_BITFLIPS_THREADS; - for (uint32_t i = 0; i < NUM_CHECK_BITFLIPS_THREADS; i++) { + uint8_t args[num_check_bitflip_threads][3]; + uint16_t bytes_per_thread = (256 + (num_check_bitflip_threads / 2)) / num_check_bitflip_threads; + for (uint32_t i = 0; i < num_check_bitflip_threads; i++) { args[i][0] = i * bytes_per_thread; args[i][1] = MIN(args[i][0] + bytes_per_thread - 1, 255); args[i][2] = time_budget; } // args[][] is uint8_t so max 255, no need to check it - // args[NUM_CHECK_BITFLIPS_THREADS - 1][1] = MAX(args[NUM_CHECK_BITFLIPS_THREADS - 1][1], 255); + // args[num_check_bitflip_threads - 1][1] = MAX(args[num_check_bitflip_threads - 1][1], 255); // start threads - for (uint32_t i = 0; i < NUM_CHECK_BITFLIPS_THREADS; i++) { + for (uint32_t i = 0; i < num_check_bitflip_threads; i++) { pthread_create(&thread_id[i], NULL, check_for_BitFlipProperties_thread, args[i]); } // wait for threads to terminate: - for (uint32_t i = 0; i < NUM_CHECK_BITFLIPS_THREADS; i++) { + for (uint32_t i = 0; i < num_check_bitflip_threads; i++) { pthread_join(thread_id[i], NULL); } if (hardnested_stage & CHECK_2ND_BYTES) { hardnested_stage &= ~CHECK_1ST_BYTES; // we are done with 1st stage, except... - for (uint32_t i = 0; i < NUM_CHECK_BITFLIPS_THREADS; i++) { + for (uint32_t i = 0; i < num_check_bitflip_threads; i++) { if (args[i][1] != 0) { hardnested_stage |= CHECK_1ST_BYTES; // ... when any of the threads didn't complete in time break; @@ -1410,7 +1563,20 @@ static int acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ float brute_force_depth; FILE *fnonces = NULL; - PacketResponseNG resp; + + // init to ZERO + PacketResponseNG resp = { + .cmd = 0, + .length = 0, + .magic = 0, + .status = 0, + .crc = 0, + .ng = false, + }; + resp.oldarg[0] = 0; + resp.oldarg[1] = 0; + resp.oldarg[2] = 0; + memset(resp.data.asBytes, 0, PM3_CMD_DATA_SIZE); uint8_t write_buf[9]; char progress_text[80]; @@ -1764,7 +1930,7 @@ static void add_matching_states(statelist_t *cands, uint8_t part_sum_a0, uint8_t const uint32_t worstcase_size = 1 << 20; - cands->states[odd_even] = (uint32_t *)malloc(sizeof(uint32_t) * worstcase_size); + cands->states[odd_even] = (uint32_t *)calloc(sizeof(uint32_t) * worstcase_size, sizeof(uint8_t)); if (cands->states[odd_even] == NULL) { PrintAndLogEx(ERR, "Out of memory error in add_matching_states() - statelist.\n"); exit(4); @@ -2057,10 +2223,11 @@ static void generate_candidates(uint8_t sum_a0_idx, uint8_t sum_a8_idx) { init_book_of_work(); // create and run worker threads - pthread_t thread_id[NUM_REDUCTION_WORKING_THREADS]; + const size_t num_reduction_working_threads = NUM_REDUCTION_WORKING_THREADS; + pthread_t thread_id[num_reduction_working_threads]; - uint16_t sums1[NUM_REDUCTION_WORKING_THREADS][3]; - for (uint32_t i = 0; i < NUM_REDUCTION_WORKING_THREADS; i++) { + uint16_t sums1[num_reduction_working_threads][3]; + for (uint32_t i = 0; i < num_reduction_working_threads; i++) { sums1[i][0] = sum_a0_idx; sums1[i][1] = sum_a8_idx; sums1[i][2] = i + 1; @@ -2068,7 +2235,7 @@ static void generate_candidates(uint8_t sum_a0_idx, uint8_t sum_a8_idx) { } // wait for threads to terminate: - for (uint32_t i = 0; i < NUM_REDUCTION_WORKING_THREADS; i++) { + for (uint32_t i = 0; i < num_reduction_working_threads; i++) { pthread_join(thread_id[i], NULL); } diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index b079e15c1..6c047f06c 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -33,19 +33,13 @@ #include "fileutils.h" #include "protocols.h" #include "crypto/libpcrypto.h" +#include "cmdhfmf.h" // printblock, header +#include "cmdtrace.h" -static const uint8_t DefaultKey[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -static uint16_t CardAddresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001}; +static const uint8_t mfp_default_key[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +static uint16_t mfp_card_adresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0x9006, 0x9007, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001}; -typedef enum { - MFP_UNKNOWN = 0, - DESFIRE_MF3ICD40, - DESFIRE_EV1, - DESFIRE_EV2, - DESFIRE_EV3, - DESFIRE_LIGHT, - PLUS_EV1, -} nxp_cardtype_t; +#define MFP_KEY_FILE_SIZE 14 + (2 * 64 * (AES_KEY_LEN + 1)) static int CmdHelp(const char *Cmd); @@ -172,7 +166,7 @@ static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature const ecdsa_publickey_t nxp_plus_public_keys[] = { {"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"}, {"MIFARE Plus Ev_x", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"}, - {"MIFARE Plus Trojka", "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"} + {"MIFARE Plus Troika", "040F732E0EA7DF2B38F791BF89425BF7DCDF3EE4D976669E3831F324FF15751BD52AFF1782F72FF2731EEAD5F63ABE7D126E03C856FFB942AF"} }; uint8_t i; @@ -232,9 +226,10 @@ static int get_plus_signature(uint8_t *signature, int *signature_len) { *signature_len = 0; retval = PM3_ESOFT; } - mfpSetVerboseMode(false); + return retval; } + // GET VERSION static int plus_print_version(uint8_t *version) { PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(version + 14, 7)); @@ -260,12 +255,12 @@ static int plus_print_version(uint8_t *version) { PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(version[13], false)); return PM3_SUCCESS; } + static int get_plus_version(uint8_t *version, int *version_len) { int resplen = 0, retval = PM3_SUCCESS; mfpSetVerboseMode(false); MFPGetVersion(true, false, version, *version_len, &resplen); - mfpSetVerboseMode(false); *version_len = resplen; if (resplen != 28) { @@ -288,7 +283,6 @@ static int CmdHFMFPInfo(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); // Mifare Plus info SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); @@ -395,7 +389,7 @@ static int CmdHFMFPInfo(const char *Cmd) { // DESFire answers 0x1C or 67 00 // Plus answers 0x0B, 0x09, 0x06 - // Which tag answers 6D 00 ?? + // 6D00 is "INS code not supported" in APDU if (data[0] != 0x0b && data[0] != 0x09 && data[0] != 0x1C && data[0] != 0x67 && data[0] != 0x6d) { PrintAndLogEx(INFO, _RED_("Send copy to iceman of this command output!")); PrintAndLogEx(INFO, "data: %s", sprint_hex(data, datalen)); @@ -457,56 +451,66 @@ static int CmdHFMFPWritePerso(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp wrp", "Executes Write Perso command. Can be used in SL0 mode only.", - "hf mfp wrp --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n" - "hf mfp wrp --ki 4000 -> write default key(0xff..0xff) to key number 4000"); + "Use this command to program AES keys, as well as personalize other data on the tag.\n" + "You can program:\n" + "* Address 00 [00-FF]: Memory blocks (as well as ACLs and Crypto1 keys)\n" + "* Address 40 [00-40]: AES sector keys\n" + "* Address 90 [00-04]: AES administrative keys\n" + "* Address A0 [00, 01, 80, 81]: Virtual Card keys\n" + "* Address B0 [00-03]: Configuration data (DO NOT TOUCH B003)\n" + "Examples:\n" + "hf mfp wrp --adr 4000 --data 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000 \n" + "hf mfp wrp --adr 4000 -> write default key(0xff..0xff) to key number 4000\n" + "hf mfp wrp --adr b000 -d FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> allow 255 commands without MAC in configuration block (B000)\n" + "hf mfp wrp --adr 0003 -d 1234561234567F078869B0B1B2B3B4B5 -> write crypto1 keys A: 123456123456 and B: B0B1B2B3B4B5 to block 3\n"); void *argtable[] = { arg_param_begin, arg_lit0("v", "verbose", "Verbose output"), - arg_str1(NULL, "ki", "", " Key number, 2 hex bytes"), - arg_str0(NULL, "key", "", " Key, 16 hex bytes"), + arg_str1("a", "adr", "", "Address, 2 hex bytes"), + arg_str0("d", "data", "", "Data, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool verbose = arg_get_lit(ctx, 1); - uint8_t keyNum[64] = {0}; - int keyNumLen = 0; - CLIGetHexWithReturn(ctx, 2, keyNum, &keyNumLen); + uint8_t addr[64] = {0}; + int addrLen = 0; + CLIGetHexWithReturn(ctx, 2, addr, &addrLen); - uint8_t key[64] = {0}; - int keyLen = 0; - CLIGetHexWithReturn(ctx, 3, key, &keyLen); + uint8_t datain[64] = {0}; + int datainLen = 0; + CLIGetHexWithReturn(ctx, 3, datain, &datainLen); CLIParserFree(ctx); mfpSetVerboseMode(verbose); - if (!keyLen) { - memmove(key, DefaultKey, 16); - keyLen = 16; + if (!datainLen) { + memmove(datain, mfp_default_key, 16); + datainLen = 16; } - if (keyNumLen != 2) { - PrintAndLogEx(ERR, "Key number length must be 2 bytes instead of: %d", keyNumLen); + if (addrLen != 2) { + PrintAndLogEx(ERR, "Address length must be 2 bytes. Got %d", addrLen); return PM3_EINVARG; } - if (keyLen != 16) { - PrintAndLogEx(ERR, "Key length must be 16 bytes instead of: %d", keyLen); + if (datainLen != 16) { + PrintAndLogEx(ERR, "Data length must be 16 bytes. Got %d", datainLen); return PM3_EINVARG; } uint8_t data[250] = {0}; int datalen = 0; - int res = MFPWritePerso(keyNum, key, true, false, data, sizeof(data), &datalen); + int res = MFPWritePerso(addr, datain, true, false, data, sizeof(data), &datalen); if (res) { PrintAndLogEx(ERR, "Exchange error: %d", res); return res; } if (datalen != 3) { - PrintAndLogEx(ERR, "Command must return 3 bytes instead of: %d", datalen); + PrintAndLogEx(ERR, "Command must return 3 bytes. Got %d", datalen); return PM3_ESOFT; } @@ -538,17 +542,18 @@ static int CmdHFMFPInitPerso(const char *Cmd) { bool verbose2 = arg_get_lit(ctx, 1) > 1; uint8_t key[256] = {0}; - int keyLen = 0; - CLIGetHexWithReturn(ctx, 2, key, &keyLen); + int keylen = 0; + CLIGetHexWithReturn(ctx, 2, key, &keylen); CLIParserFree(ctx); - if (keyLen && keyLen != 16) { - PrintAndLogEx(ERR, "Key length must be 16 bytes instead of: %d", keyLen); + if (keylen && keylen != 16) { + PrintAndLogEx(FAILED, "Key length must be 16 bytes. Got %d", keylen); return PM3_EINVARG; } - if (!keyLen) - memmove(key, DefaultKey, 16); + if (keylen == 0) { + memmove(key, mfp_default_key, sizeof(mfp_default_key)); + } uint8_t keyNum[2] = {0}; uint8_t data[250] = {0}; @@ -571,15 +576,15 @@ static int CmdHFMFPInitPerso(const char *Cmd) { } mfpSetVerboseMode(verbose); - for (int i = 0; i < ARRAYLEN(CardAddresses); i++) { - keyNum[0] = CardAddresses[i] >> 8; - keyNum[1] = CardAddresses[i] & 0xff; + for (int i = 0; i < ARRAYLEN(mfp_card_adresses); i++) { + keyNum[0] = mfp_card_adresses[i] >> 8; + keyNum[1] = mfp_card_adresses[i] & 0xff; res = MFPWritePerso(keyNum, key, false, true, data, sizeof(data), &datalen); if (!res && (datalen == 3) && data[0] == 0x09) { - PrintAndLogEx(WARNING, "Skipped[%04x]...", CardAddresses[i]); + PrintAndLogEx(WARNING, "Skipped[%04x]...", mfp_card_adresses[i]); } else { if (res || (datalen != 3) || data[0] != 0x90) { - PrintAndLogEx(ERR, "Write error on address %04x", CardAddresses[i]); + PrintAndLogEx(ERR, "Write error on address %04x", mfp_card_adresses[i]); break; } } @@ -596,7 +601,9 @@ static int CmdHFMFPInitPerso(const char *Cmd) { static int CmdHFMFPCommitPerso(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp commitp", - "Executes Commit Perso command. Can be used in SL0 mode only.\nOBS! This command will not be executed if CardConfigKey, CardMasterKey and L3SwitchKey AES keys are not written.", + "Executes Commit Perso command. Can be used in SL0 mode only.\n" + "OBS! This command will not be executed if \n" + "CardConfigKey, CardMasterKey and L3SwitchKey AES keys are not written.", "hf mfp commitp\n" // "hf mfp commitp --sl 1" ); @@ -624,7 +631,7 @@ static int CmdHFMFPCommitPerso(const char *Cmd) { } if (datalen != 3) { - PrintAndLogEx(ERR, "Command must return 3 bytes instead of: %d", datalen); + PrintAndLogEx(ERR, "Command must return 3 bytes. Got %d", datalen); return PM3_EINVARG; } @@ -632,7 +639,7 @@ static int CmdHFMFPCommitPerso(const char *Cmd) { PrintAndLogEx(ERR, "Command error: %02x %s", data[0], mfpGetErrorDescription(data[0])); return PM3_EINVARG; } - PrintAndLogEx(INFO, "Switch level ( " _GREEN_("ok") " )"); + PrintAndLogEx(INFO, "Switched security level ( " _GREEN_("ok") " )"); return PM3_SUCCESS; } @@ -644,7 +651,7 @@ static int CmdHFMFPAuth(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp auth", - "Executes AES authentication command for Mifare Plus card", + "Executes AES authentication command for MIFARE Plus card", "hf mfp auth --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> executes authentication\n" "hf mfp auth --ki 9003 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data"); @@ -663,22 +670,44 @@ static int CmdHFMFPAuth(const char *Cmd) { CLIParserFree(ctx); if (keynlen != 2) { - PrintAndLogEx(ERR, "ERROR: must be 2 bytes long instead of: %d", keynlen); + PrintAndLogEx(ERR, "ERROR: must be 2 bytes. Got %d", keynlen); return PM3_EINVARG; } if (keylen != 16) { - PrintAndLogEx(ERR, "ERROR: must be 16 bytes long instead of: %d", keylen); + PrintAndLogEx(ERR, "ERROR: must be 16 bytes. Got %d", keylen); return PM3_EINVARG; } return MifareAuth4(NULL, keyn, key, true, false, true, verbose, false); } - +static int data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev) { + uint8_t kenc[16]; + memcpy(kenc, mf4session->Kenc, 16); + uint8_t ti[4]; + memcpy(ti, mf4session->TI, 4); + uint8_t ctr[1]; + uint8_t IV[16] = {0, 0, 0x00, 0x00, 0x00, 0, 0x00, 0x00, 0x00, 0}; + if (rev) { + ctr[0] = (uint8_t)(mf4session->R_Ctr & 0xff); + for (int i = 0; i < 9; i += 4) {memcpy(&IV[i], ctr, 1);} + memcpy(&IV[12], ti, 4); // For reads TI is LS + } else { + ctr[0] = (uint8_t)(mf4session->W_Ctr & 0xff); + for (int i = 3; i < 16; i += 4) {memcpy(&IV[i], ctr, 1);} + memcpy(&IV[0], ti, 4); // For writes TI is MS + } + if (rev) { + aes_decode(IV, kenc, dati, dato, 16); + } else { + aes_encode(IV, kenc, dati, dato, 16); + } + return 0; +} static int CmdHFMFPRdbl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp rdbl", - "Reads several blocks from Mifare Plus card", + "Reads blocks from MIFARE Plus card", "hf mfp rdbl --blk 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n" "hf mfp rdbl --blk 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF"); @@ -687,7 +716,9 @@ static int CmdHFMFPRdbl(const char *Cmd) { arg_lit0("v", "verbose", "Verbose mode"), arg_int0("n", "count", "", "Blocks count (def: 1)"), arg_lit0("b", "keyb", "Use key B (def: keyA)"), - arg_lit0("p", "plain", "Plain communication mode between reader and card"), + arg_lit0("p", "plain", "Do not use encrypted communication mode between reader and card"), + arg_lit0(NULL, "nmc", "Do not append MAC to command"), + arg_lit0(NULL, "nmr", "Do not expect MAC in reply"), arg_int1(NULL, "blk", "<0..255>", "Block number"), arg_str0("k", "key", "", "Key, 16 hex bytes"), arg_param_end @@ -697,35 +728,37 @@ static int CmdHFMFPRdbl(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); int blocksCount = arg_get_int_def(ctx, 2, 1); bool keyB = arg_get_lit(ctx, 3); - int plain = arg_get_lit(ctx, 4); - uint32_t blockn = arg_get_int(ctx, 5); + bool plain = arg_get_lit(ctx, 4); + bool nomaccmd = arg_get_lit(ctx, 5); + bool nomacres = arg_get_lit(ctx, 6); + uint32_t blockn = arg_get_int(ctx, 7); uint8_t keyn[2] = {0}; uint8_t key[250] = {0}; int keylen = 0; - CLIGetHexWithReturn(ctx, 6, key, &keylen); + CLIGetHexWithReturn(ctx, 8, key, &keylen); CLIParserFree(ctx); mfpSetVerboseMode(verbose); if (!keylen) { - memmove(key, DefaultKey, 16); + memmove(key, mfp_default_key, 16); keylen = 16; } if (blockn > 255) { - PrintAndLogEx(ERR, " must be in range [0..255] got: %d", blockn); + PrintAndLogEx(ERR, " must be in range [0..255]. got %d", blockn); return PM3_EINVARG; } if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long. got: %d", keylen); + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", keylen); return PM3_EINVARG; } // 3 blocks - wo iso14443-4 chaining if (blocksCount > 3) { - PrintAndLogEx(ERR, "blocks count must be less than 3. got: %d", blocksCount); + PrintAndLogEx(ERR, "blocks count must be less than 3. Got %d", blocksCount); return PM3_EINVARG; } @@ -750,7 +783,7 @@ static int CmdHFMFPRdbl(const char *Cmd) { uint8_t data[250] = {0}; int datalen = 0; uint8_t mac[8] = {0}; - res = MFPReadBlock(&mf4session, plain, blockn & 0xff, blocksCount, false, false, data, sizeof(data), &datalen, mac); + res = MFPReadBlock(&mf4session, plain, nomaccmd, nomacres, blockn & 0xff, blocksCount, false, false, data, sizeof(data), &datalen, mac); if (res) { PrintAndLogEx(ERR, "Read error: %d", res); return res; @@ -760,31 +793,32 @@ static int CmdHFMFPRdbl(const char *Cmd) { PrintAndLogEx(ERR, "Card read error: %02x %s", data[0], mfpGetErrorDescription(data[0])); return PM3_ESOFT; } - - if (datalen != 1 + blocksCount * 16 + 8 + 2) { - PrintAndLogEx(ERR, "Error return length:%d", datalen); + //PrintAndLogEx(INFO, "%i", 8 && (!macres || 0xff)); + if (datalen != 1 + blocksCount * 16 + (nomacres ? 0 : 8) + 2) { + PrintAndLogEx(ERR, "Error return length: %d", datalen); return PM3_ESOFT; } + if (!plain) data_crypt(&mf4session, &data[1], &data[1], true); + uint8_t sector = mfSectorNum(blockn); + mf_print_sector_hdr(sector); + int indx = blockn; for (int i = 0; i < blocksCount; i++) { - PrintAndLogEx(INFO, "data[%03d]: %s", indx, sprint_hex(&data[1 + i * 16], 16)); + mf_print_block_one(indx, data + 1 + (i * MFBLOCK_SIZE), verbose); indx++; - if (mfIsSectorTrailer(indx) && i != blocksCount - 1) { - PrintAndLogEx(INFO, "data[%03d]: ------------------- trailer -------------------", indx); - indx++; + } + + if (memcmp(&data[(blocksCount * 16) + 1], mac, 8) && !nomacres) { + PrintAndLogEx(WARNING, "WARNING: mac not equal..."); + PrintAndLogEx(WARNING, "MAC card... " _YELLOW_("%s"), sprint_hex_inrow(&data[1 + (blocksCount * MFBLOCK_SIZE)], 8)); + PrintAndLogEx(WARNING, "MAC reader... " _YELLOW_("%s"), sprint_hex_inrow(mac, sizeof(mac))); + } else if (!nomacres) { + if (verbose) { + PrintAndLogEx(INFO, "MAC... " _YELLOW_("%s"), sprint_hex_inrow(&data[1 + (blocksCount * MFBLOCK_SIZE)], 8)); } } - - if (memcmp(&data[blocksCount * 16 + 1], mac, 8)) { - PrintAndLogEx(WARNING, "WARNING: mac not equal..."); - PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[blocksCount * 16 + 1], 8)); - PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); - } else { - if (verbose) - PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[blocksCount * 16 + 1], 8)); - } - + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -799,7 +833,9 @@ static int CmdHFMFPRdsc(const char *Cmd) { arg_param_begin, arg_lit0("v", "verbose", "Verbose mode"), arg_lit0("b", "keyb", "Use key B (def: keyA)"), - arg_lit0("p", "plain", "Plain communication mode between reader and card"), + arg_lit0("p", "plain", "Do not use encrypted communication mode between reader and card"), + arg_lit0(NULL, "nmc", "Do not append MAC to command"), + arg_lit0(NULL, "nmr", "Do not expect MAC in reply"), arg_int1("s", "sn", "<0..255>", "Sector number"), arg_str0("k", "key", "", "Key, 16 hex bytes"), arg_param_end @@ -809,27 +845,29 @@ static int CmdHFMFPRdsc(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); bool keyB = arg_get_lit(ctx, 2); bool plain = arg_get_lit(ctx, 3); - uint32_t sectorNum = arg_get_int(ctx, 4); + bool nomaccmd = arg_get_lit(ctx, 4); + bool nomacres = arg_get_lit(ctx, 5); + uint32_t sectorNum = arg_get_int(ctx, 6); uint8_t keyn[2] = {0}; uint8_t key[250] = {0}; int keylen = 0; - CLIGetHexWithReturn(ctx, 5, key, &keylen); + CLIGetHexWithReturn(ctx, 7, key, &keylen); CLIParserFree(ctx); mfpSetVerboseMode(verbose); if (!keylen) { - memmove(key, DefaultKey, 16); + memmove(key, mfp_default_key, 16); keylen = 16; } if (sectorNum > 39) { - PrintAndLogEx(ERR, " must be in range [0..39] got: %d", sectorNum); + PrintAndLogEx(ERR, " must be in range [0..39]. Got %d", sectorNum); return PM3_EINVARG; } if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long. got: %d", keylen); + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", keylen); return PM3_EINVARG; } @@ -849,8 +887,12 @@ static int CmdHFMFPRdsc(const char *Cmd) { uint8_t data[250] = {0}; int datalen = 0; uint8_t mac[8] = {0}; - for (int n = mfFirstBlockOfSector(sectorNum); n < mfFirstBlockOfSector(sectorNum) + mfNumBlocksPerSector(sectorNum); n++) { - res = MFPReadBlock(&mf4session, plain, n & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); + + mf_print_sector_hdr(sectorNum); + + for (int blockno = mfFirstBlockOfSector(sectorNum); blockno < mfFirstBlockOfSector(sectorNum) + mfNumBlocksPerSector(sectorNum); blockno++) { + + res = MFPReadBlock(&mf4session, plain, nomaccmd, nomacres, blockno & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); if (res) { PrintAndLogEx(ERR, "Read error: %d", res); DropField(); @@ -862,32 +904,34 @@ static int CmdHFMFPRdsc(const char *Cmd) { DropField(); return PM3_ESOFT; } - if (datalen != 1 + 16 + 8 + 2) { + + if (datalen != 1 + MFBLOCK_SIZE + (nomacres ? 0 : 8) + 2) { PrintAndLogEx(ERR, "Error return length:%d", datalen); DropField(); return PM3_ESOFT; } + if (!plain) data_crypt(&mf4session, &data[1], &data[1], true); + mf_print_block_one(blockno, data + 1, verbose); - PrintAndLogEx(INFO, "data[%03d]: %s", n, sprint_hex(&data[1], 16)); - - if (memcmp(&data[1 + 16], mac, 8)) { - PrintAndLogEx(WARNING, "WARNING: mac on block %d not equal...", n); - PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1 + 16], 8)); - PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); - } else { - if (verbose) - PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1 + 16], 8)); + if (memcmp(&data[1 + 16], mac, 8) && !nomacres) { + PrintAndLogEx(WARNING, "WARNING: mac on block %d not equal...", blockno); + PrintAndLogEx(WARNING, "MAC card... " _YELLOW_("%s"), sprint_hex_inrow(&data[1 + MFBLOCK_SIZE], 8)); + PrintAndLogEx(WARNING, "MAC reader... " _YELLOW_("%s"), sprint_hex_inrow(mac, sizeof(mac))); + } else if (!nomacres) { + if (verbose) { + PrintAndLogEx(INFO, "MAC... " _YELLOW_("%s"), sprint_hex_inrow(&data[1 + MFBLOCK_SIZE], 8)); + } } } + PrintAndLogEx(NORMAL, ""); DropField(); - return PM3_SUCCESS; } static int CmdHFMFPWrbl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp wrbl", - "Writes one block to Mifare Plus card", + "Writes one block to MIFARE Plus card", "hf mfp wrbl --blk 1 -d ff0000000000000000000000000000ff --key 000102030405060708090a0b0c0d0e0f -> write block 1 data\n" "hf mfp wrbl --blk 2 -d ff0000000000000000000000000000ff -v -> write block 2 data with default key 0xFF..0xFF" ); @@ -897,6 +941,8 @@ static int CmdHFMFPWrbl(const char *Cmd) { arg_lit0("v", "verbose", "Verbose mode"), arg_lit0("b", "keyb", "Use key B (def: keyA)"), arg_int1(NULL, "blk", "<0..255>", "Block number"), + arg_lit0("p", "plain", "Do not use encrypted transmission"), + arg_lit0(NULL, "nmr", "Do not expect MAC in response"), arg_str1("d", "data", "", "Data, 16 hex bytes"), arg_str0("k", "key", "", "Key, 16 hex bytes"), arg_param_end @@ -906,14 +952,16 @@ static int CmdHFMFPWrbl(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); bool keyB = arg_get_lit(ctx, 2); uint32_t blockNum = arg_get_int(ctx, 3); + bool plain = arg_get_lit(ctx, 4); + bool nomacres = arg_get_lit(ctx, 5); uint8_t datain[250] = {0}; int datainlen = 0; - CLIGetHexWithReturn(ctx, 4, datain, &datainlen); + CLIGetHexWithReturn(ctx, 6, datain, &datainlen); uint8_t key[250] = {0}; int keylen = 0; - CLIGetHexWithReturn(ctx, 5, key, &keylen); + CLIGetHexWithReturn(ctx, 7, key, &keylen); CLIParserFree(ctx); uint8_t keyn[2] = {0}; @@ -921,22 +969,22 @@ static int CmdHFMFPWrbl(const char *Cmd) { mfpSetVerboseMode(verbose); if (!keylen) { - memmove(key, DefaultKey, 16); + memmove(key, mfp_default_key, 16); keylen = 16; } if (blockNum > 255) { - PrintAndLogEx(ERR, " must be in range [0..255] got: %d", blockNum); + PrintAndLogEx(ERR, " must be in range [0..255]. Got %d", blockNum); return PM3_EINVARG; } if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long. got: %d", keylen); + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", keylen); return PM3_EINVARG; } if (datainlen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long. got: %d", datainlen); + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", datainlen); return PM3_EINVARG; } @@ -953,18 +1001,18 @@ static int CmdHFMFPWrbl(const char *Cmd) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; } - + if (!plain) data_crypt(&mf4session, &datain[0], &datain[0], false); uint8_t data[250] = {0}; int datalen = 0; uint8_t mac[8] = {0}; - res = MFPWriteBlock(&mf4session, blockNum & 0xff, datain, false, false, data, sizeof(data), &datalen, mac); + res = MFPWriteBlock(&mf4session, plain, nomacres, blockNum & 0xff, 0x00, datain, false, false, data, sizeof(data), &datalen, mac); if (res) { PrintAndLogEx(ERR, "Write error: %d", res); DropField(); return res; } - if (datalen != 3 && (datalen != 3 + 8)) { + if (datalen != 3 && (datalen != 3 + (nomacres ? 0 : 8))) { PrintAndLogEx(ERR, "Error return length:%d", datalen); DropField(); return PM3_ESOFT; @@ -976,11 +1024,11 @@ static int CmdHFMFPWrbl(const char *Cmd) { return PM3_ESOFT; } - if (memcmp(&data[1], mac, 8)) { + if (memcmp(&data[1], mac, 8) && !nomacres) { PrintAndLogEx(WARNING, "WARNING: mac not equal..."); PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8)); PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); - } else { + } else if (!nomacres) { if (verbose) PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8)); } @@ -990,12 +1038,232 @@ static int CmdHFMFPWrbl(const char *Cmd) { return PM3_SUCCESS; } -#define AES_KEY_LEN 16 -#define MAX_KEYS_LIST_LEN 1024 +static int CmdHFMFPChKey(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfp chkey", + "Change the keys on a Mifare Plus tag", + "This requires the key that can update the key that you are trying to update.\n" + "hf mfp chkey --ki 401f -d FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF --key A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7 -> Change key B for Sector 15 from MAD to default\n" + "hf mfp chkey --ki 9000 -d 32F9351A1C02B35FF97E0CA943F814F6 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> Change card master key to custom from default" + ); -static int MFPKeyCheck(uint8_t startSector, uint8_t endSector, uint8_t startKeyAB, uint8_t endKeyAB, - uint8_t keyList[MAX_KEYS_LIST_LEN][AES_KEY_LEN], size_t keyListLen, uint8_t foundKeys[2][64][AES_KEY_LEN + 1], - bool verbose) { + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "Verbose mode"), + arg_lit0(NULL, "nmr", "Do not expect MAC in response"), + arg_str1(NULL, "ki", "", "Key Index, 2 hex bytes"), + arg_str0("k", "key", "", "Current sector key, 16 hex bytes"), + arg_lit0("b", "typeb", "Sector key is key B"), + arg_str1("d", "data", "", "New key, 16 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool verbose = arg_get_lit(ctx, 1); + bool nomacres = arg_get_lit(ctx, 2); + + uint8_t keyn[250] = {0}; + + uint8_t ki[250] = {0}; + int kilen = 0; + CLIGetHexWithReturn(ctx, 3, ki, &kilen); + + uint8_t key[250] = {0}; + int keylen = 0; + CLIGetHexWithReturn(ctx, 4, key, &keylen); + + bool usekeyb = arg_get_lit(ctx, 5); + uint8_t datain[250] = {0}; + int datainlen = 0; + CLIGetHexWithReturn(ctx, 6, datain, &datainlen); + + CLIParserFree(ctx); + + mfpSetVerboseMode(verbose); + + if (!keylen) { + memmove(key, mfp_default_key, 16); + keylen = 16; + } + + if (keylen != 16) { + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", keylen); + return PM3_EINVARG; + } + + if (datainlen != 16) { + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", datainlen); + return PM3_EINVARG; + } + mf4Session_t mf4session; + keyn[0] = ki[0]; + if (ki[0] == 0x40) { // Only if we are working with sector keys + if (usekeyb) { + keyn[1] = (ki[1] % 2 == 0) ? ki[1] + 1 : ki[1]; // If we change using key B, check if KI is key A + } else { + keyn[1] = (ki[1] % 2 == 0) ? ki[1] : ki[1] - 1; // If we change using key A, check if KI is key A + } + } else {keyn[1] = ki[1];} + if (verbose) { + PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2)); + } + int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); + if (res) { + PrintAndLogEx(ERR, "Authentication error: %d", res); + return res; + } + data_crypt(&mf4session, &datain[0], &datain[0], false); + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + res = MFPWriteBlock(&mf4session, false, nomacres, ki[1], ki[0], datain, false, false, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLogEx(ERR, "Write error: %d", res); + DropField(); + return res; + } + + if (datalen != 3 && (datalen != 3 + (nomacres ? 0 : 8))) { + PrintAndLogEx(ERR, "Error return length:%d", datalen); + DropField(); + return PM3_ESOFT; + } + + if (datalen && data[0] != 0x90) { + PrintAndLogEx(ERR, "Card write error: %02x %s", data[0], mfpGetErrorDescription(data[0])); + DropField(); + return PM3_ESOFT; + } + + if (memcmp(&data[1], mac, 8) && !nomacres) { + PrintAndLogEx(WARNING, "WARNING: mac not equal..."); + PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8)); + PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); + } else if (!nomacres) { + if (verbose) + PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8)); + } + + DropField(); + PrintAndLogEx(INFO, "Key update ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; +} + +static int CmdHFMFPChConf(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfp chconf", + "Change the configuration on a Mifare Plus tag. DANGER!", + "This requires Card Master Key (9000) or Card Configuration Key (9001).\n" + "Configuration block info can be found below.\n" + "* Block B000 (00; CMK): Max amount of commands without MAC (byte 0), as well as plain mode access (unknown).\n" + "* Block B001 (01; CCK): Installation identifier for Virtual Card. Please consult NXP for data.\n" + "* Block B002 (02; CCK): ATS data.\n" + "* Block B003 (03; CCK): Use Random ID in SL3, decide whether proximity check is mandatory.\n * DO NOT WRITE THIS BLOCK UNDER ANY CIRCUMSTANCES! Risk of bricking.\n" + "More configuration tips to follow. Check JMY600 Series IC Card Module.\n" + "hf mfp chconf -c 00 -d 10ffffffffffffffffffffffffffffff --key A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7 -> Allow 16 commands without MAC in a single transaction." + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "Verbose mode"), + arg_lit0(NULL, "nmr", "Do not expect MAC in response"), + arg_int1("c", "conf", "", "Config block number, 0-3"), + arg_str0("k", "key", "", "Card key, 16 hex bytes"), + arg_lit0(NULL, "cck", "Auth as Card Configuration key instead of Card Master Key"), + arg_str1("d", "data", "", "New configuration data, 16 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool verbose = arg_get_lit(ctx, 1); + bool nomacres = arg_get_lit(ctx, 2); + + uint8_t keyn[250] = {0}; + uint32_t blockNum = arg_get_int(ctx, 3); + + uint8_t key[250] = {0}; + int keylen = 0; + CLIGetHexWithReturn(ctx, 4, key, &keylen); + bool usecck = arg_get_lit(ctx, 5); + + uint8_t datain[250] = {0}; + int datainlen = 0; + CLIGetHexWithReturn(ctx, 6, datain, &datainlen); + + CLIParserFree(ctx); + + mfpSetVerboseMode(verbose); + + if (!keylen) { + memmove(key, mfp_default_key, 16); + keylen = 16; + } + + if (keylen != 16) { + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", keylen); + return PM3_EINVARG; + } + + if (datainlen != 16) { + PrintAndLogEx(ERR, " must be 16 bytes. Got %d", datainlen); + return PM3_EINVARG; + } + + if (blockNum > 3) { + PrintAndLogEx(ERR, " must be in range [0..3]. Got %d", blockNum); + return PM3_EINVARG; + } + mf4Session_t mf4session; + keyn[0] = 0x90; + keyn[1] = usecck ? 0x01 : 0x00; + if (verbose) { + PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2)); + } + int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); + if (res) { + PrintAndLogEx(ERR, "Authentication error: %d", res); + return res; + } + data_crypt(&mf4session, &datain[0], &datain[0], false); + uint8_t data[250] = {0}; + int datalen = 0; + uint8_t mac[8] = {0}; + res = MFPWriteBlock(&mf4session, false, nomacres, blockNum & 0xff, 0xb0, datain, false, false, data, sizeof(data), &datalen, mac); + if (res) { + PrintAndLogEx(ERR, "Write error: %d", res); + DropField(); + return res; + } + + if (datalen != 3 && (datalen != 3 + (nomacres ? 0 : 8))) { + PrintAndLogEx(ERR, "Error return length:%d", datalen); + DropField(); + return PM3_ESOFT; + } + + if (datalen && data[0] != 0x90) { + PrintAndLogEx(ERR, "Card write error: %02x %s", data[0], mfpGetErrorDescription(data[0])); + DropField(); + return PM3_ESOFT; + } + + if (memcmp(&data[1], mac, 8) && !nomacres) { + PrintAndLogEx(WARNING, "WARNING: mac not equal..."); + PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8)); + PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); + } else if (!nomacres) { + if (verbose) + PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8)); + } + + DropField(); + PrintAndLogEx(INFO, "Write config ( " _GREEN_("ok") " )"); + return PM3_SUCCESS; +} + +static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startKeyAB, uint8_t endKeyAB, + uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], size_t keyListLen, uint8_t foundKeys[2][64][AES_KEY_LEN + 1], + bool verbose) { int res; bool selectCard = true; uint8_t keyn[2] = {0}; @@ -1006,7 +1274,7 @@ static int MFPKeyCheck(uint8_t startSector, uint8_t endSector, uint8_t startKeyA for (uint8_t keyAB = startKeyAB; keyAB <= endKeyAB; keyAB++) { // main cycle with key check for (int i = 0; i < keyListLen; i++) { - + // allow client abort every iteration if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); @@ -1052,14 +1320,14 @@ static int MFPKeyCheck(uint8_t startSector, uint8_t endSector, uint8_t startKeyA selectCard = true; msleep(50); - // break out from keylist check loop, + // break out from keylist check loop, break; } if (verbose) PrintAndLogEx(WARNING, "\nsector %02d key %d [%s] res: %d", sector, keyAB, sprint_hex_inrow(keyList[i], 16), res); - // RES can be: + // RES can be: // PM3_ERFTRANS -7 // PM3_EWRONGANSWER -16 if (res == PM3_ERFTRANS) { @@ -1081,7 +1349,7 @@ static int MFPKeyCheck(uint8_t startSector, uint8_t endSector, uint8_t startKeyA return PM3_SUCCESS; } -static void Fill2bPattern(uint8_t keyList[MAX_KEYS_LIST_LEN][AES_KEY_LEN], uint32_t *keyListLen, uint32_t *startPattern) { +static void Fill2bPattern(uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], uint32_t *keyListLen, uint32_t *startPattern) { for (uint32_t pt = *startPattern; pt < 0x10000; pt++) { keyList[*keyListLen][0] = (pt >> 8) & 0xff; keyList[*keyListLen][1] = pt & 0xff; @@ -1090,25 +1358,21 @@ static void Fill2bPattern(uint8_t keyList[MAX_KEYS_LIST_LEN][AES_KEY_LEN], uint3 memcpy(&keyList[*keyListLen][8], &keyList[*keyListLen][0], 8); (*keyListLen)++; *startPattern = pt; - if (*keyListLen == MAX_KEYS_LIST_LEN) + if (*keyListLen == MAX_AES_KEYS_LIST_LEN) break; } (*startPattern)++; } static int CmdHFMFPChk(const char *Cmd) { - int res; - uint8_t keyList[MAX_KEYS_LIST_LEN][AES_KEY_LEN] = {{0}}; - uint32_t keyListLen = 0; - uint8_t foundKeys[2][64][AES_KEY_LEN + 1] = {{{0}}}; CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp chk", "Checks keys on MIFARE Plus card", "hf mfp chk -k 000102030405060708090a0b0c0d0e0f -> check key on sector 0 as key A and B\n" - "hf mfp chk -s 2 -a -> check default key list on sector 2, key A\n" + "hf mfp chk -s 2 -a -> check default key list on sector 2, only key A\n" "hf mfp chk -d mfp_default_keys -s0 -e6 -> check keys from dictionary against sectors 0-6\n" - "hf mfp chk --pattern1b -j keys -> check all 1-byte keys pattern and save found keys to json\n" + "hf mfp chk --pattern1b --dump -> check all 1-byte keys pattern and save found keys to file\n" "hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00"); void *argtable[] = { @@ -1122,7 +1386,7 @@ static int CmdHFMFPChk(const char *Cmd) { arg_lit0(NULL, "pattern1b", "Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)"), arg_lit0(NULL, "pattern2b", "Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)"), arg_str0(NULL, "startp2b", "", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"), - arg_str0("j", "json", "", "Json filename to save keys"), + arg_lit0(NULL, "dump", "Dump found keys to JSON file"), arg_lit0("v", "verbose", "Verbose mode"), arg_param_end }; @@ -1133,6 +1397,10 @@ static int CmdHFMFPChk(const char *Cmd) { uint8_t startSector = arg_get_int_def(ctx, 3, 0); uint8_t endSector = arg_get_int_def(ctx, 4, 0); + uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN] = {{0}}; + uint32_t keyListLen = 0; + uint8_t foundKeys[2][64][AES_KEY_LEN + 1] = {{{0}}}; + uint8_t vkey[16] = {0}; int vkeylen = 0; CLIGetHexWithReturn(ctx, 5, vkey, &vkeylen); @@ -1141,7 +1409,7 @@ static int CmdHFMFPChk(const char *Cmd) { memcpy(&keyList[keyListLen], vkey, 16); keyListLen++; } else { - PrintAndLogEx(ERR, "Specified key must have 16 bytes length."); + PrintAndLogEx(ERR, "Specified key must have 16 bytes. Got %d", vkeylen); CLIParserFree(ctx); return PM3_EINVARG; } @@ -1178,7 +1446,7 @@ static int CmdHFMFPChk(const char *Cmd) { if (vpatternlen <= 2) { startPattern = (vpattern[0] << 8) + vpattern[1]; } else { - PrintAndLogEx(ERR, "Pattern must be 2-byte length."); + PrintAndLogEx(ERR, "Pattern must be 2-bytes. Got %d", vpatternlen); CLIParserFree(ctx); return PM3_EINVARG; } @@ -1186,25 +1454,16 @@ static int CmdHFMFPChk(const char *Cmd) { PrintAndLogEx(WARNING, "Pattern entered, but search mode not is 2-byte search."); } - uint8_t jsonname[250] = {0}; - int jsonnamelen = 0; - if (CLIParamStrToBuf(arg_get_str(ctx, 10), jsonname, sizeof(jsonname), &jsonnamelen)) { - PrintAndLogEx(ERR, "Invalid json name."); - CLIParserFree(ctx); - return PM3_EINVARG; - } - jsonname[jsonnamelen] = 0; - + bool create_dumpfile = arg_get_lit(ctx, 10); bool verbose = arg_get_lit(ctx, 11); - CLIParserFree(ctx); uint8_t startKeyAB = 0; uint8_t endKeyAB = 1; - if (keyA && !keyB) + if (keyA && (keyB == false)) endKeyAB = 0; - if (!keyA && keyB) + if ((keyA == false) && keyB) startKeyAB = 1; if (endSector < startSector) @@ -1212,15 +1471,19 @@ static int CmdHFMFPChk(const char *Cmd) { // 1-byte pattern search mode if (pattern1b) { - for (int i = 0; i < 0x100; i++) + for (int i = 0; i < 0x100; i++) { memset(keyList[i], i, 16); + } keyListLen = 0x100; } // 2-byte pattern search mode - if (pattern2b) + if (pattern2b) { Fill2bPattern(keyList, &keyListLen, &startPattern); + } + + int res = PM3_SUCCESS; // dictionary mode size_t endFilePosition = 0; @@ -1236,8 +1499,9 @@ static int CmdHFMFPChk(const char *Cmd) { if (keyListLen == 0) { for (int i = 0; i < g_mifare_plus_default_keys_len; i++) { - if (hex_to_bytes(g_mifare_plus_default_keys[i], keyList[keyListLen], 16) != 16) + if (hex_to_bytes(g_mifare_plus_default_keys[i], keyList[keyListLen], 16) != 16) { break; + } keyListLen++; } @@ -1250,21 +1514,26 @@ static int CmdHFMFPChk(const char *Cmd) { PrintAndLogEx(INFO, "Loaded " _YELLOW_("%"PRIu32) " keys", keyListLen); } - if (verbose == false) + if (verbose == false) { PrintAndLogEx(INFO, "Search keys"); + } while (true) { - res = MFPKeyCheck(startSector, endSector, startKeyAB, endKeyAB, keyList, keyListLen, foundKeys, verbose); - if (res == PM3_EOPABORTED) + res = plus_key_check(startSector, endSector, startKeyAB, endKeyAB, keyList, keyListLen, foundKeys, verbose); + if (res == PM3_EOPABORTED) { break; + } + if (pattern2b && startPattern < 0x10000) { - if (verbose == false) + if (verbose == false) { PrintAndLogEx(NORMAL, "p" NOLF); + } keyListLen = 0; Fill2bPattern(keyList, &keyListLen, &startPattern); continue; } + if (dict_filenamelen && endFilePosition) { if (verbose == false) PrintAndLogEx(NORMAL, "d" NOLF); @@ -1274,37 +1543,66 @@ static int CmdHFMFPChk(const char *Cmd) { if (res == PM3_SUCCESS && endFilePosition) { keyListLen = keycnt; } + continue; } break; } - if (verbose == false) + + if (verbose == false) { PrintAndLogEx(NORMAL, ""); + } // print result + char strA[46 + 1] = {0}; + char strB[46 + 1] = {0}; + + uint8_t ndef_key[] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7}; + bool has_ndef_key = false; bool printedHeader = false; - for (uint8_t sector = startSector; sector <= endSector; sector++) { - if (foundKeys[0][sector][0] || foundKeys[1][sector][0]) { - if (!printedHeader) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "-------+--------------------------------+---------------------------------"); - PrintAndLogEx(INFO, "|sector| key A | key B |"); - PrintAndLogEx(INFO, "|------+--------------------------------+--------------------------------|"); - printedHeader = true; - } - PrintAndLogEx(INFO, "| %02d |%32s|%32s|", - sector, - (foundKeys[0][sector][0] == 0) ? "------ " : sprint_hex_inrow(&foundKeys[0][sector][1], AES_KEY_LEN), - (foundKeys[1][sector][0] == 0) ? "------ " : sprint_hex_inrow(&foundKeys[1][sector][1], AES_KEY_LEN)); + for (uint8_t s = startSector; s <= endSector; s++) { + + if ((memcmp(&foundKeys[0][s][1], ndef_key, AES_KEY_LEN) == 0) || + (memcmp(&foundKeys[1][s][1], ndef_key, AES_KEY_LEN) == 0)) { + has_ndef_key = true; } + + if (printedHeader == false) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-----+----------------------------------+----------------------------------"); + PrintAndLogEx(INFO, " Sec | key A | key B"); + PrintAndLogEx(INFO, "-----+----------------------------------+----------------------------------"); + printedHeader = true; + } + + if (foundKeys[0][s][0]) { + snprintf(strA, sizeof(strA), _GREEN_("%s"), sprint_hex_inrow(&foundKeys[0][s][1], AES_KEY_LEN)); + } else { + snprintf(strA, sizeof(strA), _RED_("%s"), "--------------------------------"); + } + + if (foundKeys[1][s][0]) { + snprintf(strB, sizeof(strB), _GREEN_("%s"), sprint_hex_inrow(&foundKeys[1][s][1], AES_KEY_LEN)); + } else { + snprintf(strB, sizeof(strB), _RED_("%s"), "--------------------------------"); + } + + PrintAndLogEx(INFO, " " _YELLOW_("%03d") " | %s | %s", s, strA, strB); } - if (!printedHeader) + + if (printedHeader == false) PrintAndLogEx(INFO, "No keys found("); else - PrintAndLogEx(INFO, "'------+--------------------------------+--------------------------------'\n"); + PrintAndLogEx(INFO, "-----+----------------------------------+----------------------------------\n"); // save keys to json - if ((jsonnamelen > 0) && printedHeader) { + if (create_dumpfile && printedHeader) { + + size_t keys_len = (2 * 64 * (AES_KEY_LEN + 1)); + + uint8_t data[10 + 1 + 2 + 1 + 256 + keys_len]; + memset(data, 0, sizeof(data)); + // Mifare Plus info SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); @@ -1315,8 +1613,6 @@ static int CmdHFMFPChk(const char *Cmd) { memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision - - uint8_t data[10 + 1 + 2 + 1 + 256 + 2 * 64 * (AES_KEY_LEN + 1)] = {0}; uint8_t atslen = 0; if (select_status == 1 || select_status == 2) { memcpy(data, card.uid, card.uidlen); @@ -1328,19 +1624,112 @@ static int CmdHFMFPChk(const char *Cmd) { memcpy(&data[14], card.ats, atslen); } + char *fptr = calloc(sizeof(char) * (strlen("hf-mfp-") + strlen("-key")) + card.uidlen * 2 + 1, sizeof(uint8_t)); + strcpy(fptr, "hf-mfp-"); + + FillFileNameByUID(fptr, card.uid, "-key", card.uidlen); + // length: UID(10b)+SAK(1b)+ATQA(2b)+ATSlen(1b)+ATS(atslen)+foundKeys[2][64][AES_KEY_LEN + 1] - memcpy(&data[14 + atslen], foundKeys, 2 * 64 * (AES_KEY_LEN + 1)); - saveFileJSON((char *)jsonname, jsfMfPlusKeys, data, 64, NULL); + memcpy(&data[14 + atslen], foundKeys, keys_len); + // 64 here is for how many "rows" there is in the data array. A bit confusing + saveFileJSON(fptr, jsfMfPlusKeys, data, 64, NULL); + free(fptr); } + // MAD detection + if ((memcmp(&foundKeys[0][0][1], "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7", AES_KEY_LEN) == 0)) { + PrintAndLogEx(HINT, "MAD key detected. Try " _YELLOW_("`hf mfp mad`") " for more details"); + } + + // NDEF detection + if (has_ndef_key) { + PrintAndLogEx(HINT, "NDEF key detected. Try " _YELLOW_("`hf mfp ndefread -h`") " for more details"); + } + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } +static int CmdHFMFPDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfp dump", + "Dump MIFARE Plus tag to file (bin/json)\n" + "If no given, UID will be used as filename", + "hf mfp dump\n" + "hf mfp dump --keys hf-mf-066C8B78-key.bin --> MIFARE Plus with keys from specified file\n"); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "Specify a filename for dump file"), + arg_str0("k", "keys", "", "Specify a filename for keys file"), + arg_lit0(NULL, "ns", "no save to file"), + arg_lit0("v", "verbose", "Verbose mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int datafnlen = 0; + char data_fn[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)data_fn, FILE_PATH_SIZE, &datafnlen); + + int keyfnlen = 0; + char key_fn[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)key_fn, FILE_PATH_SIZE, &keyfnlen); + +// bool nosave = arg_get_lit(ctx, 3); +// bool verbose = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + PrintAndLogEx(INFO, " To be implemented, feel free to contribute!"); + return PM3_ENOTIMPL; + + /* + mfpSetVerboseMode(verbose); + + // read card + uint8_t *mem = calloc(MIFARE_4K_MAXBLOCK * MFBLOCK_SIZE, sizeof(uint8_t)); + if (mem == NULL) { + PrintAndLogEx(ERR, "failed to allocate memory"); + return PM3_EMALLOC; + } + + + // iso14a_card_select_t card ; + // int res = mfp_read_tag(&card, mem, key_fn); + // if (res != PM3_SUCCESS) { + // free(mem); + // return res; + // } + + + // Skip saving card data to file + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + free(mem); + return PM3_SUCCESS; + } + + // Save to file + // if (strlen(data_fn) < 1) { + // char *fptr = calloc(sizeof(char) * (strlen("hf-mfp-") + strlen("-dump")) + card.uidlen * 2 + 1, sizeof(uint8_t)); + // strcpy(fptr, "hf-mfp-"); + // FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); + // strcpy(data_fn, fptr); + // free(fptr); + // } + + // pm3_save_mf_dump(filename, dump, MIFARE_4K_MAX_BYTES, jsfCardMemory); + + free(mem); + return PM3_SUCCESS; + */ +} + + static int CmdHFMFPMAD(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp mad", - "Checks and prints Mifare Application Directory (MAD)", + "Checks and prints MIFARE Application Directory (MAD)", "hf mfp mad\n" "hf mfp mad --aid e103 -k d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 -> read and print NDEF data from MAD aid"); @@ -1382,9 +1771,7 @@ static int CmdHFMFPMAD(const char *Cmd) { return PM3_ESOFT; } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Mifare App Directory Information") " ----------------"); - PrintAndLogEx(INFO, "-----------------------------------------------------"); + MADPrintHeader(); if (verbose) { PrintAndLogEx(SUCCESS, "Raw:"); @@ -1398,7 +1785,7 @@ static int CmdHFMFPMAD(const char *Cmd) { if (haveMAD2) { if (mfpReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifarep_mad_key, sector10, verbose)) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(ERR, "error, read sector 0x10. card doesn't have MAD or doesn't have MAD on default keys"); + PrintAndLogEx(ERR, "error, read sector " _YELLOW_("0x10") ". Card doesn't have MAD or doesn't have MAD on default keys"); return PM3_ESOFT; } @@ -1476,6 +1863,36 @@ static int CmdHFMFPMAD(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHFMFPNDEFFormat(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfp ndefformat", + "format MIFARE Plus Tag as a NFC tag with Data Exchange Format (NDEF)\n" + "If no given, UID will be used as filename. \n" + "It will try default keys and MAD keys to detect if tag is already formatted in order to write.\n" + "\n" + "If not, it will try finding a key file based on your UID. ie, if you ran autopwn before", + "hf mfp ndefformat\n" + "hf mfp ndefformat --keys hf-mf-01020304-key.bin --> with keys from specified file\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("k", "keys", "", "filename of keys"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int keyfnlen = 0; + char keyFilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)keyFilename, FILE_PATH_SIZE, &keyfnlen); + + CLIParserFree(ctx); + + PrintAndLogEx(SUCCESS, "Not implemented yet. Feel free to contribute!"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + int CmdHFMFPNDEFRead(const char *Cmd) { CLIParserContext *ctx; @@ -1581,7 +1998,7 @@ int CmdHFMFPNDEFRead(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - if (!datalen) { + if (datalen == 0) { PrintAndLogEx(ERR, "no NDEF data"); return PM3_SUCCESS; } @@ -1592,28 +2009,99 @@ int CmdHFMFPNDEFRead(const char *Cmd) { print_buffer(data, datalen, 1); } - if (fnlen != 0) { - saveFile(filename, ".bin", data, datalen); + res = NDEFDecodeAndPrint(data, datalen, verbose); + if (res != PM3_SUCCESS) { + PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header"); + res = NDEFRecordsDecodeAndPrint(data, datalen, verbose); + } + + // get total NDEF length before save. If fails, we save it all + size_t n = 0; + if (NDEFGetTotalLength(data, datalen, &n) != PM3_SUCCESS) + n = datalen; + + pm3_save_dump(filename, data, n, jsfNDEF); + + if (verbose == false) { + PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfp ndefread -v`") " for more details"); + } else { + if (verbose2 == false) { + PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfp ndefread -vv`") " for more details"); + } } - NDEFDecodeAndPrint(data, datalen, verbose); - PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfp ndefread -vv`") " for more details"); return PM3_SUCCESS; } +static int CmdHFMFPNDEFWrite(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfp ndefwrite", + "Write raw NDEF hex bytes to tag. This commands assumes tag already been NFC/NDEF formatted.\n", + "hf mfp ndefwrite -d 0300FE -> write empty record to tag\n" + "hf mfp ndefwrite -f myfilename\n" + "hf mfp ndefwrite -d 033fd1023a53709101195405656e2d55534963656d616e2054776974746572206c696e6b5101195502747769747465722e636f6d2f686572726d616e6e31303031\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("d", NULL, "", "raw NDEF hex bytes"), + arg_str0("f", "file", "", "write raw NDEF file to tag"), + arg_lit0("p", NULL, "fix NDEF record headers / terminator block if missing"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint8_t raw[4096] = {0}; + int rawlen; + CLIGetHexWithReturn(ctx, 1, raw, &rawlen); + + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool fix_msg = arg_get_lit(ctx, 3); + bool verbose = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if (fix_msg) { + PrintAndLogEx(NORMAL, "called with fix NDEF message param"); + } + + if (verbose) { + PrintAndLogEx(NORMAL, ""); + } + PrintAndLogEx(SUCCESS, "Not implemented yet. Feel free to contribute!"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + +static int CmdHFMFPList(const char *Cmd) { + return CmdTraceListAlias(Cmd, "hf mfp", "mfp -c"); +} + static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"info", CmdHFMFPInfo, IfPm3Iso14443a, "Info about Mifare Plus tag"}, - {"wrp", CmdHFMFPWritePerso, IfPm3Iso14443a, "Write Perso command"}, - {"initp", CmdHFMFPInitPerso, IfPm3Iso14443a, "Fill all the card's keys in SL0 mode"}, - {"commitp", CmdHFMFPCommitPerso, IfPm3Iso14443a, "Move card to SL1 or SL3 mode"}, - {"auth", CmdHFMFPAuth, IfPm3Iso14443a, "Authentication"}, - {"rdbl", CmdHFMFPRdbl, IfPm3Iso14443a, "Read blocks"}, - {"rdsc", CmdHFMFPRdsc, IfPm3Iso14443a, "Read sectors"}, - {"wrbl", CmdHFMFPWrbl, IfPm3Iso14443a, "Write blocks"}, - {"chk", CmdHFMFPChk, IfPm3Iso14443a, "Check keys"}, - {"mad", CmdHFMFPMAD, IfPm3Iso14443a, "Check and print MAD"}, - {"ndefread", CmdHFMFPNDEFRead, IfPm3Iso14443a, "Read and print NDEF records from card"}, - {NULL, NULL, 0, NULL} + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdHFMFPList, AlwaysAvailable, "List MIFARE Plus history"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "------------------- " _CYAN_("operations") " ---------------------"}, + {"auth", CmdHFMFPAuth, IfPm3Iso14443a, "Authentication"}, + {"chk", CmdHFMFPChk, IfPm3Iso14443a, "Check keys"}, + {"dump", CmdHFMFPDump, IfPm3Iso14443a, "Dump MIFARE Plus tag to binary file"}, + {"info", CmdHFMFPInfo, IfPm3Iso14443a, "Info about MIFARE Plus tag"}, + {"mad", CmdHFMFPMAD, IfPm3Iso14443a, "Check and print MAD"}, + {"rdbl", CmdHFMFPRdbl, IfPm3Iso14443a, "Read blocks from card"}, + {"rdsc", CmdHFMFPRdsc, IfPm3Iso14443a, "Read sectors from card"}, + {"wrbl", CmdHFMFPWrbl, IfPm3Iso14443a, "Write block to card"}, + {"chkey", CmdHFMFPChKey, IfPm3Iso14443a, "Change key on card"}, + {"chconf", CmdHFMFPChConf, IfPm3Iso14443a, "Change config on card"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "---------------- " _CYAN_("personalization") " -------------------"}, + {"commitp", CmdHFMFPCommitPerso, IfPm3Iso14443a, "Configure security layer (SL1/SL3 mode)"}, + {"initp", CmdHFMFPInitPerso, IfPm3Iso14443a, "Fill all the card's keys in SL0 mode"}, + {"wrp", CmdHFMFPWritePerso, IfPm3Iso14443a, "Write Perso command"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("ndef") " ------------------------"}, + {"ndefformat", CmdHFMFPNDEFFormat, IfPm3Iso14443a, "Format MIFARE Plus Tag as NFC Tag"}, + {"ndefread", CmdHFMFPNDEFRead, IfPm3Iso14443a, "Read and print NDEF records from card"}, + {"ndefwrite", CmdHFMFPNDEFWrite, IfPm3Iso14443a, "Write NDEF records to card"}, + {NULL, NULL, 0, NULL} }; static int CmdHelp(const char *Cmd) { diff --git a/client/src/cmdhfmfp.h b/client/src/cmdhfmfp.h index 7381f87a2..e8be12e07 100644 --- a/client/src/cmdhfmfp.h +++ b/client/src/cmdhfmfp.h @@ -20,6 +20,26 @@ #include "common.h" +typedef enum { + MFP_UNKNOWN = 0, + DESFIRE_MF3ICD40, + DESFIRE_EV1, + DESFIRE_EV2, + DESFIRE_EV3, + DESFIRE_LIGHT, + PLUS_EV1, +} nxp_cardtype_t; + +typedef struct mfp_key_item { + uint8_t a[16]; + uint8_t b[16]; +} mfp_key_item_t; + +typedef struct mfp_keys { + uint8_t success; + mfp_key_item_t *keys; +} mfp_keys_t; + int CmdHFMFP(const char *Cmd); int CmdHFMFPNDEFRead(const char *Cmd); diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index f720d8599..60a7130fe 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -30,9 +30,10 @@ #include "nfc/ndef.h" #include "cliparser.h" #include "cmdmain.h" -#include "amiibo.h" // amiiboo fcts +#include "amiibo.h" // amiiboo fcts #include "base64.h" -#include "fileutils.h" // saveFile +#include "fileutils.h" // saveFile +#include "cmdtrace.h" // trace list #define MAX_UL_BLOCKS 0x0F #define MAX_ULC_BLOCKS 0x2F @@ -50,6 +51,7 @@ #define MAX_MY_D_MOVE 0x25 #define MAX_MY_D_MOVE_LEAN 0x0F #define MAX_UL_NANO_40 0x0A +#define MAX_UL_AES 0x37 static int CmdHelp(const char *Cmd); @@ -68,16 +70,25 @@ static uint8_t default_pwd_pack[][4] = { {0x4E, 0x45, 0x78, 0x54}, // NExT }; -static uint32_t UL_TYPES_ARRAY[] = { - UNKNOWN, UL, UL_C, UL_EV1_48, UL_EV1_128, - NTAG, NTAG_203, NTAG_210, NTAG_212, - NTAG_213, NTAG_215, NTAG_216, - MY_D, MY_D_NFC, MY_D_MOVE, MY_D_MOVE_NFC, MY_D_MOVE_LEAN, - NTAG_I2C_1K, NTAG_I2C_2K, NTAG_I2C_1K_PLUS, NTAG_I2C_2K_PLUS, - FUDAN_UL, NTAG_213_F, NTAG_216_F, UL_EV1, UL_NANO_40, - NTAG_213_TT, NTAG_213_C, - MAGIC_1A, MAGIC_1B, MAGIC_NTAG, - NTAG_210u, UL_MAGIC, UL_C_MAGIC +static uint64_t UL_TYPES_ARRAY[] = { + MFU_TT_UNKNOWN, MFU_TT_UL, + MFU_TT_UL_C, MFU_TT_UL_EV1_48, + MFU_TT_UL_EV1_128, MFU_TT_NTAG, + MFU_TT_NTAG_203, MFU_TT_NTAG_210, + MFU_TT_NTAG_212, MFU_TT_NTAG_213, + MFU_TT_NTAG_215, MFU_TT_NTAG_216, + MFU_TT_MY_D, MFU_TT_MY_D_NFC, + MFU_TT_MY_D_MOVE, MFU_TT_MY_D_MOVE_NFC, + MFU_TT_MY_D_MOVE_LEAN, MFU_TT_NTAG_I2C_1K, + MFU_TT_NTAG_I2C_2K, MFU_TT_NTAG_I2C_1K_PLUS, + MFU_TT_NTAG_I2C_2K_PLUS, MFU_TT_FUDAN_UL, + MFU_TT_NTAG_213_F, MFU_TT_NTAG_216_F, + MFU_TT_UL_EV1, MFU_TT_UL_NANO_40, + MFU_TT_NTAG_213_TT, MFU_TT_NTAG_213_C, + MFU_TT_MAGIC_1A, MFU_TT_MAGIC_1B, + MFU_TT_MAGIC_NTAG, MFU_TT_NTAG_210u, + MFU_TT_UL_MAGIC, MFU_TT_UL_C_MAGIC, + MFU_TT_UL_AES }; static uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = { @@ -98,7 +109,7 @@ static uint8_t UL_MEMORY_ARRAY[ARRAYLEN(UL_TYPES_ARRAY)] = { // MAGIC_1A, MAGIC_1B, MAGIC_NTAG, MAX_UL_BLOCKS, MAX_UL_BLOCKS, MAX_NTAG_216, // NTAG_210u, UL_MAGIC, UL_C_MAGIC - MAX_NTAG_210, MAX_UL_BLOCKS, MAX_ULC_BLOCKS + MAX_NTAG_210, MAX_UL_BLOCKS, MAX_ULC_BLOCKS, MAX_UL_AES }; //------------------------------------ @@ -190,7 +201,7 @@ int ul_read_uid(uint8_t *uid) { // 2: OK, no ATS // 3: proprietary Anticollision if (select_status == 0) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); + PrintAndLogEx(DEBUG, "iso14443a card select failed"); return PM3_ESOFT; } if (card.uidlen != 7) { @@ -224,14 +235,14 @@ static bool ul_select(iso14a_card_select_t *card) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); DropField(); return false; } else { uint16_t len = (resp.oldarg[1] & 0xFFFF); if (len == 0) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); + PrintAndLogEx(DEBUG, "iso14443a card select failed"); DropField(); return false; } @@ -371,8 +382,8 @@ static int ulev1_requestAuthentication(uint8_t *pwd, uint8_t *pack, uint16_t pac return len; } -static int ul_auth_select(iso14a_card_select_t *card, TagTypeUL_t tagtype, bool hasAuthKey, uint8_t *authkey, uint8_t *pack, uint8_t packSize) { - if (hasAuthKey && (tagtype & UL_C)) { +static int ul_auth_select(iso14a_card_select_t *card, uint64_t tagtype, bool hasAuthKey, uint8_t *authkey, uint8_t *pack, uint8_t packSize) { + if (hasAuthKey && (tagtype & MFU_TT_UL_C)) { //will select card automatically and close connection on error if (!ulc_authentication(authkey, false)) { PrintAndLogEx(WARNING, "Authentication Failed UL-C"); @@ -419,7 +430,6 @@ static int ulev1_readTearing(uint8_t counter, uint8_t *response, uint16_t respon } static int ulev1_readSignature(uint8_t *response, uint16_t responseLength) { - uint8_t cmd[] = {MIFARE_ULEV1_READSIG, 0x00}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), response, responseLength); return len; @@ -441,16 +451,16 @@ static int ulev1_readSignature(uint8_t *response, uint16_t responseLength) { static int ul_fudan_check(void) { iso14a_card_select_t card; if (!ul_select(&card)) - return UL_ERROR; + return MFU_TT_UL_ERROR; uint8_t cmd[4] = {ISO14443A_CMD_READBLOCK, 0x00, 0x02, 0xa7}; //wrong crc on purpose should be 0xa8 clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_NO_RATS, 4, 0, cmd, sizeof(cmd)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return UL_ERROR; - if (resp.oldarg[0] != 1) return UL_ERROR; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) return MFU_TT_UL_ERROR; + if (resp.oldarg[0] != 1) return MFU_TT_UL_ERROR; - return (!resp.data.asBytes[0]) ? FUDAN_UL : UL; //if response == 0x00 then Fudan, else Genuine NXP + return (!resp.data.asBytes[0]) ? MFU_TT_FUDAN_UL : MFU_TT_UL; //if response == 0x00 then Fudan, else Genuine NXP } static int ul_print_default(uint8_t *data, uint8_t *real_uid) { @@ -622,7 +632,7 @@ static int ndef_print_CC(uint8_t *data) { return PM3_SUCCESS; } -int ul_print_type(uint32_t tagtype, uint8_t spaces) { +int ul_print_type(uint64_t tagtype, uint8_t spaces) { if (spaces > 10) spaces = 10; @@ -630,73 +640,73 @@ int ul_print_type(uint32_t tagtype, uint8_t spaces) { char typestr[100]; memset(typestr, 0x00, sizeof(typestr)); - if (tagtype & UL) + if (tagtype & MFU_TT_UL) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight (MF0ICU1)"), spaces, ""); - else if (tagtype & UL_C) + else if (tagtype & MFU_TT_UL_C) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight C (MF0ULC)"), spaces, ""); - else if (tagtype & UL_NANO_40) + else if (tagtype & MFU_TT_UL_NANO_40) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight Nano 40bytes (MF0UNH00)"), spaces, ""); - else if (tagtype & UL_EV1_48) + else if (tagtype & MFU_TT_UL_EV1_48) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight EV1 48bytes (MF0UL1101)"), spaces, ""); - else if (tagtype & UL_EV1_128) + else if (tagtype & MFU_TT_UL_EV1_128) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight EV1 128bytes (MF0UL2101)"), spaces, ""); - else if (tagtype & UL_EV1) + else if (tagtype & MFU_TT_UL_EV1) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("MIFARE Ultralight EV1 UNKNOWN"), spaces, ""); - else if (tagtype & NTAG) + else if (tagtype & MFU_TT_NTAG) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG UNKNOWN"), spaces, ""); - else if (tagtype & NTAG_203) + else if (tagtype & MFU_TT_NTAG_203) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 203 144bytes (NT2H0301F0DT)"), spaces, ""); - else if (tagtype & NTAG_210u) + else if (tagtype & MFU_TT_NTAG_210u) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 210u (micro) 48bytes (NT2L1001G0DU)"), spaces, ""); - else if (tagtype & NTAG_210) + else if (tagtype & MFU_TT_NTAG_210) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 210 48bytes (NT2L1011G0DU)"), spaces, ""); - else if (tagtype & NTAG_212) + else if (tagtype & MFU_TT_NTAG_212) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 212 128bytes (NT2L1211G0DU)"), spaces, ""); - else if (tagtype & NTAG_213) + else if (tagtype & MFU_TT_NTAG_213) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213 144bytes (NT2H1311G0DU)"), spaces, ""); - else if (tagtype & NTAG_213_F) + else if (tagtype & MFU_TT_NTAG_213_F) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213F 144bytes (NT2H1311F0DTL)"), spaces, ""); - else if (tagtype & NTAG_213_C) + else if (tagtype & MFU_TT_NTAG_213_C) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213C 144bytes (NT2H1311C1DTL)"), spaces, ""); - else if (tagtype & NTAG_213_TT) + else if (tagtype & MFU_TT_NTAG_213_TT) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 213TT 144bytes (NT2H1311TTDU)"), spaces, ""); - else if (tagtype & NTAG_215) + else if (tagtype & MFU_TT_NTAG_215) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 215 504bytes (NT2H1511G0DU)"), spaces, ""); - else if (tagtype & NTAG_216) + else if (tagtype & MFU_TT_NTAG_216) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 216 888bytes (NT2H1611G0DU)"), spaces, ""); - else if (tagtype & NTAG_216_F) + else if (tagtype & MFU_TT_NTAG_216_F) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG 216F 888bytes (NT2H1611F0DTL)"), spaces, ""); - else if (tagtype & NTAG_I2C_1K) + else if (tagtype & MFU_TT_NTAG_I2C_1K) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C 888bytes (NT3H1101FHK)"), spaces, ""); - else if (tagtype & NTAG_I2C_2K) + else if (tagtype & MFU_TT_NTAG_I2C_2K) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C 1904bytes (NT3H1201FHK)"), spaces, ""); - else if (tagtype & NTAG_I2C_1K_PLUS) + else if (tagtype & MFU_TT_NTAG_I2C_1K_PLUS) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C plus 888bytes (NT3H2111FHK)"), spaces, ""); - else if (tagtype & NTAG_I2C_2K_PLUS) + else if (tagtype & MFU_TT_NTAG_I2C_2K_PLUS) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("NTAG I2C plus 1912bytes (NT3H2211FHK)"), spaces, ""); - else if (tagtype & MY_D) + else if (tagtype & MFU_TT_MY_D) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 (SLE 66RxxS)"), spaces, ""); - else if (tagtype & MY_D_NFC) + else if (tagtype & MFU_TT_MY_D_NFC) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 NFC (SLE 66RxxP)"), spaces, ""); - else if (tagtype & MY_D_MOVE) + else if (tagtype & MFU_TT_MY_D_MOVE) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move (SLE 66R01P)"), spaces, ""); - else if (tagtype & MY_D_MOVE_NFC) + else if (tagtype & MFU_TT_MY_D_MOVE_NFC) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move NFC (SLE 66R01P)"), spaces, ""); - else if (tagtype & MY_D_MOVE_LEAN) + else if (tagtype & MFU_TT_MY_D_MOVE_LEAN) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("INFINEON my-d\x99 move lean (SLE 66R01L)"), spaces, ""); - else if (tagtype & FUDAN_UL) + else if (tagtype & MFU_TT_FUDAN_UL) snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("FUDAN Ultralight Compatible (or other compatible)"), spaces, ""); else - snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("Unknown %06x"), spaces, "", tagtype); + snprintf(typestr, sizeof(typestr), "%*sTYPE: " _YELLOW_("Unknown %06" PRIx64), spaces, "", tagtype); - bool ismagic = ((tagtype & MAGIC) == MAGIC); + bool ismagic = ((tagtype & MFU_TT_MAGIC) == MFU_TT_MAGIC); if (ismagic) snprintf(typestr + strlen(typestr), 4, " ("); - snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), " %s ", (tagtype & MAGIC) ? _GREEN_("magic") : ""); - tagtype &= ~(MAGIC); - snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), "%s", (tagtype & MAGIC_1A) ? _GREEN_("Gen 1a") : ""); - snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), "%s", (tagtype & MAGIC_1B) ? _GREEN_("Gen 1b") : ""); + snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), " %s ", (tagtype & MFU_TT_MAGIC) ? _GREEN_("magic") : ""); + tagtype &= ~(MFU_TT_MAGIC); + snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), "%s", (tagtype & MFU_TT_MAGIC_1A) ? _GREEN_("Gen 1a") : ""); + snprintf(typestr + strlen(typestr), sizeof(typestr) - strlen(typestr), "%s", (tagtype & MFU_TT_MAGIC_1B) ? _GREEN_("Gen 1b") : ""); if (ismagic) snprintf(typestr + strlen(typestr), 4, " )"); @@ -737,7 +747,7 @@ static int ulc_print_configuration(uint8_t *data) { return PM3_SUCCESS; } -static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t startPage) { +static int ulev1_print_configuration(uint64_t tagtype, uint8_t *data, uint8_t startPage) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Configuration")); @@ -755,7 +765,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st PrintAndLogEx(INFO, " cfg0 [%u/0x%02X]: %s", startPage, startPage, sprint_hex(data, 4)); //NTAG213TT has different ASCII mirroring options and config bytes interpretation from other ulev1 class tags - if (tagtype & NTAG_213_TT) { + if (tagtype & MFU_TT_NTAG_213_TT) { uint8_t mirror_conf = ((data[0] & 0xE0) >> 5); uint8_t mirror_byte = ((data[0] & 0x18) >> 3); uint8_t mirror_page = data[2]; @@ -818,10 +828,13 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st default: break; } - PrintAndLogEx(INFO, " mirror start page %02X | byte pos %02X - %s", mirror_page, mirror_byte, (mirror_page >= 0x4 && ((mirror_user_mem_start_byte + bytes_required_for_mirror_data) <= 144)) ? _GREEN_("OK") : _YELLOW_("Invalid value")); + PrintAndLogEx(INFO, " mirror start page %02X | byte pos %02X - %s" + , mirror_page, mirror_byte + , (mirror_page >= 0x4 && ((mirror_user_mem_start_byte + bytes_required_for_mirror_data) <= 144)) ? _GREEN_("ok") : _YELLOW_("Invalid value") + ); } - } else if (tagtype & (NTAG_213_F | NTAG_216_F)) { + } else if (tagtype & (MFU_TT_NTAG_213_F | MFU_TT_NTAG_216_F)) { uint8_t mirror_conf = ((data[0] & 0xC0) >> 6); uint8_t mirror_byte = (data[0] & 0x30); bool sleep_en = (data[0] & 0x08); @@ -864,7 +877,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st break; } // valid mirror start page and byte position within start page. - if (tagtype & NTAG_213_F) { + if (tagtype & MFU_TT_NTAG_213_F) { switch (mirror_conf) { case 1: { PrintAndLogEx(INFO, " mirror start block %02X | byte pos %02X - %s", data[2], mirror_byte, (data[2] >= 0x4 && data[2] <= 0x24) ? "OK" : "Invalid value"); break;} @@ -875,7 +888,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st default: break; } - } else if (tagtype & NTAG_216_F) { + } else if (tagtype & MFU_TT_NTAG_216_F) { switch (mirror_conf) { case 1: { PrintAndLogEx(INFO, " mirror start block %02X | byte pos %02X - %s", data[2], mirror_byte, (data[2] >= 0x4 && data[2] <= 0xDE) ? "OK" : "Invalid value"); break;} @@ -900,7 +913,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st uint8_t tt_msg_resp_len = 0; uint8_t tt_status_resp[5] = {0x00}; - if (tagtype & NTAG_213_TT) { + if (tagtype & MFU_TT_NTAG_213_TT) { tt_enabled = (data[1] & 0x02); tt_msg_resp_len = ul_read(45, tt_message, 4); @@ -940,7 +953,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st PrintAndLogEx(INFO, " PACK [%u/0x%02X]: %s - (cannot be read)", startPage + 3, startPage + 3, sprint_hex(data + 12, 2)); PrintAndLogEx(INFO, " RFU [%u/0x%02X]: %s- (cannot be read)", startPage + 3, startPage + 3, sprint_hex(data + 14, 2)); - if (tagtype & NTAG_213_TT) { + if (tagtype & MFU_TT_NTAG_213_TT) { if (data[1] & 0x06) { PrintAndLogEx(INFO, "TT_MSG [45/0x2D]: %s- (cannot be read)", sprint_hex(tt_message, tt_msg_resp_len)); PrintAndLogEx(INFO, " - tamper message is masked in memory"); @@ -951,7 +964,7 @@ static int ulev1_print_configuration(uint32_t tagtype, uint8_t *data, uint8_t st } //The NTAG213TT only returns meaningful information for the fields below if the tamper feature is enabled - if ((tagtype & NTAG_213_TT) && tt_enabled) { + if ((tagtype & MFU_TT_NTAG_213_TT) && tt_enabled) { uint8_t tt_status_len = ntagtt_getTamperStatus(tt_status_resp, 5); @@ -1011,20 +1024,26 @@ static int ulev1_print_counters(void) { return len; } -static int ulev1_print_signature(TagTypeUL_t tagtype, uint8_t *uid, uint8_t *signature, size_t signature_len) { +static int ulev1_print_signature(uint64_t tagtype, uint8_t *uid, uint8_t *signature, size_t signature_len) { #define PUBLIC_ECDA_KEYLEN 33 +#define PUBLIC_ECDA_192_KEYLEN 49 // known public keys for the originality check (source: https://github.com/alexbatalov/node-nxp-originality-verifier) // ref: AN11350 NTAG 21x Originality Signature Validation // ref: AN11341 MIFARE Ultralight EV1 Originality Signature Validation const ecdsa_publickey_t nxp_mfu_public_keys[] = { - {"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, - {"Manufacturer MIFARE Classic MFC1C14_x", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, - {"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, - {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, - {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, - {"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"}, - {"MIKRON Public key", "04f971eda742a4a80d32dcf6a814a707cc3dc396d35902f72929fdcd698b3468f2"}, + {"NXP MIFARE Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, + {"Manufacturer MIFARE Classic / QL88", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, + {"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, + {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, + {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, + {"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"}, + {"MIKRON Public key", "04f971eda742a4a80d32dcf6a814a707cc3dc396d35902f72929fdcd698b3468f2"}, + }; + + // https://www.nxp.com/docs/en/application-note/AN13452.pdf + const ecdsa_publickey_t nxp_mfu_192_public_keys[] = { + {"NXP Ultralight AES", "0453BF8C49B7BD9FE3207A91513B9C1D238ECAB07186B772104AB535F7D3AE63CF7C7F3DD0D169DA3E99E43C6399621A86"}, }; /* @@ -1075,34 +1094,60 @@ static int ulev1_print_signature(TagTypeUL_t tagtype, uint8_t *uid, uint8_t *sig */ uint8_t i; bool is_valid = false; - for (i = 0; i < ARRAYLEN(nxp_mfu_public_keys); i++) { + if (signature_len == 32) { + for (i = 0; i < ARRAYLEN(nxp_mfu_public_keys); i++) { - int dl = 0; - uint8_t key[PUBLIC_ECDA_KEYLEN] = {0}; - param_gethex_to_eol(nxp_mfu_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl); + int dl = 0; + uint8_t key[PUBLIC_ECDA_KEYLEN] = {0}; + param_gethex_to_eol(nxp_mfu_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl); - int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 7, signature, signature_len, false); + int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 7, signature, signature_len, false); - is_valid = (res == 0); - if (is_valid) - break; + is_valid = (res == 0); + if (is_valid) + break; + } + } + + bool is_192_valid = false; + if (signature_len == 48) { + for (i = 0; i < ARRAYLEN(nxp_mfu_192_public_keys); i++) { + int dl = 0; + uint8_t key[PUBLIC_ECDA_192_KEYLEN] = {0}; + param_gethex_to_eol(nxp_mfu_192_public_keys[i].value, 0, key, PUBLIC_ECDA_192_KEYLEN, &dl); + + int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP192R1, key, uid, 7, signature, signature_len, false); + + is_192_valid = (res == 0); + if (is_192_valid) + break; + } } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); - if (is_valid == false || i == ARRAYLEN(nxp_mfu_public_keys)) { - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); + if (is_192_valid) { + PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfu_192_public_keys[i].desc); + PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_192_public_keys[i].value); + PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp192r1"); PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len)); - PrintAndLogEx(SUCCESS, " Signature verification ( " _RED_("fail") " )"); - return PM3_ESOFT; + PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )"); + return PM3_SUCCESS; } - PrintAndLogEx(INFO, " IC signature public key name: %s", nxp_mfu_public_keys[i].desc); - PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_public_keys[i].value); - PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); + if (is_valid) { + PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_mfu_public_keys[i].desc); + PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfu_public_keys[i].value); + PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); + PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len)); + PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )"); + return PM3_SUCCESS; + } + + PrintAndLogEx(INFO, " Elliptic curve parameters: %s", (signature_len == 48) ? "NID_secp192r1" : "NID_secp128r1"); PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, signature_len)); - PrintAndLogEx(SUCCESS, " Signature verification ( " _GREEN_("successful") " )"); - return PM3_SUCCESS; + PrintAndLogEx(SUCCESS, " Signature verification ( " _RED_("fail") " )"); + return PM3_ESOFT; } static int ulev1_print_version(uint8_t *data) { @@ -1151,14 +1196,14 @@ static int ulc_magic_test(){ uint8_t nonce1[11] = {0x00}; uint8_t nonce2[11] = {0x00}; if ( !ul_select(&card) ){ - return UL_ERROR; + return MFU_TT_UL_ERROR; } int status = ulc_requestAuthentication(nonce1, sizeof(nonce1)); if ( status > 0 ) { status = ulc_requestAuthentication(nonce2, sizeof(nonce2)); - returnValue = ( !memcmp(nonce1, nonce2, 11) ) ? UL_C_MAGIC : UL_C; + returnValue = ( !memcmp(nonce1, nonce2, 11) ) ? MFU_TT_UL_C_MAGIC : MFU_TT_UL_C; } else { - returnValue = UL; + returnValue = MFU_TT_UL; } DropField(); return returnValue; @@ -1171,12 +1216,12 @@ static int ul_magic_test(void) { iso14a_card_select_t card; if (ul_select(&card) == false) - return UL_ERROR; + return MFU_TT_UL_ERROR; int status = ul_comp_write(0, NULL, 0); DropField(); if (status == 0) - return MAGIC; + return MFU_TT_MAGIC; // check for GEN1A, GEN1B and NTAG21x uint8_t is_generation = 0; @@ -1190,11 +1235,11 @@ static int ul_magic_test(void) { } switch (is_generation) { case MAGIC_GEN_1A: - return MAGIC_1A; + return MFU_TT_MAGIC_1A; case MAGIC_GEN_1B: - return MAGIC_1B; + return MFU_TT_MAGIC_1B; case MAGIC_NTAG21X: - return MAGIC_NTAG; + return MFU_TT_MAGIC_NTAG; default: break; } @@ -1305,10 +1350,10 @@ typedef struct { } mfu_otp_identify_t; static mfu_otp_identify_t mfu_otp_ident_table[] = { - { "SALTO", 12, 4, "534C544F", ul_c_otpgenA, NULL }, -// { "SAFLOK", 12, 4, NULL, ul_c_otpgenB, NULL }, -// { "VINGCARD", 12, 4, NULL, ul_c_otpgenC, NULL }, -// { "DORMA KABA", 12, 4, NULL, ul_c_otpgenD, NULL }, + { "SALTO tag", 12, 4, "534C544F", ul_c_otpgenA, NULL }, +// { "SAFLOK tag", 12, 4, NULL, ul_c_otpgenB, NULL }, +// { "VINGCARD tag", 12, 4, NULL, ul_c_otpgenC, NULL }, +// { "DORMA KABA tag", 12, 4, NULL, ul_c_otpgenD, NULL }, { NULL, 0, 0, NULL, NULL, NULL } }; @@ -1351,7 +1396,7 @@ static mfu_identify_t mfu_ident_table[] = { "Jooki", "0004040201000F03", 12, 32, "E11012000103A00C340329D101255504732E6A6F6F6B692E726F636B732F732F", ul_ev1_pwdgen_def, ul_ev1_packgen_def, - "hf jooki decode -r" + "hf mfu ndefread" }, { "Lego Dimensions", "0004040201000F03", @@ -1391,6 +1436,18 @@ static mfu_identify_t mfu_ident_table[] = { "hf mfu dump -k %08x" }, */ + { + "Philips Toothbrush", "0004040201010F03", + 16, 20, "0310D1010C55027068696C6970732E636F6DFE00", + ul_ev1_pwdgen_def, ul_ev1_packgen_def, + "hf mfu pwdgen -r" + }, + { + "Philips Toothbrush", "0004040201010F03", + 16, 36, "0320D1011C55027068696C6970732E636F6D2F6E6663627275736868656164746170FE00", + ul_ev1_pwdgen_def, ul_ev1_packgen_def, + "hf mfu pwdgen -r" + }, {NULL, NULL, 0, 0, NULL, NULL, NULL, NULL} }; @@ -1448,7 +1505,7 @@ static int mfu_get_version_uid(uint8_t *version, uint8_t *uid) { return PM3_SUCCESS; } -static int mfu_fingerprint(TagTypeUL_t tagtype, bool hasAuthKey, uint8_t *authkey, int ak_len) { +static int mfu_fingerprint(uint64_t tagtype, bool hasAuthKey, uint8_t *authkey, int ak_len) { uint8_t *data = NULL; int res = PM3_SUCCESS; @@ -1473,7 +1530,7 @@ static int mfu_fingerprint(TagTypeUL_t tagtype, bool hasAuthKey, uint8_t *authke uint8_t keytype = 0; if (hasAuthKey) { - if (tagtype & UL_C) + if (tagtype & MFU_TT_UL_C) keytype = 1; //UL_C auth else keytype = 2; //UL_EV1/NTAG auth @@ -1554,17 +1611,17 @@ out: uint32_t GetHF14AMfU_Type(void) { - TagTypeUL_t tagtype = UNKNOWN; + uint64_t tagtype = MFU_TT_UNKNOWN; iso14a_card_select_t card; if (ul_select(&card) == false) - return UL_ERROR; + return MFU_TT_UL_ERROR; // Ultralight - ATQA / SAK if (card.atqa[1] != 0x00 || card.atqa[0] != 0x44 || card.sak != 0x00) { //PrintAndLogEx(NORMAL, "Tag is not Ultralight | NTAG | MY-D [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak); DropField(); - return UL_ERROR; + return MFU_TT_UL_ERROR; } if (card.uid[0] != 0x05) { @@ -1615,79 +1672,80 @@ uint32_t GetHF14AMfU_Type(void) { Feiju NTAG 0053040201000F03 */ - if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0B", 7) == 0) { tagtype = UL_EV1_48; break; } - else if (memcmp(version, "\x00\x04\x03\x01\x02\x00\x0B", 7) == 0) { tagtype = UL_NANO_40; break; } - else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0B", 7) == 0) { tagtype = UL_EV1_48; break; } - else if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0E", 7) == 0) { tagtype = UL_EV1_128; break; } - else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0E", 7) == 0) { tagtype = UL_EV1_128; break; } - else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { tagtype = UL_EV1_128; break; } // Mikron JSC Russia EV1 41 pages tag - else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0B", 7) == 0) { tagtype = NTAG_210; break; } - else if (memcmp(version, "\x00\x04\x04\x01\x02\x00\x0B", 7) == 0) { tagtype = NTAG_210u; break; } - else if (memcmp(version, "\x00\x04\x04\x02\x02\x00\x0B", 7) == 0) { tagtype = NTAG_210u; break; } - else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0E", 7) == 0) { tagtype = NTAG_212; break; } - else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213; break; } - else if (memcmp(version, "\x00\x53\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213; break; } //Shanghai Feiju Microelectronics Co. Ltd. China (Xiaomi Air Purifier filter) - else if (memcmp(version, "\x00\x04\x04\x02\x01\x01\x0F", 7) == 0) { tagtype = NTAG_213_C; break; } - else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x11", 7) == 0) { tagtype = NTAG_215; break; } - else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x13", 7) == 0) { tagtype = NTAG_216; break; } - else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x0F", 7) == 0) { tagtype = NTAG_213_F; break; } - else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x13", 7) == 0) { tagtype = NTAG_216_F; break; } - else if (memcmp(version, "\x00\x04\x04\x02\x03\x00\x0F", 7) == 0) { tagtype = NTAG_213_TT; break; } - else if (memcmp(version, "\x00\x04\x04\x05\x02\x01\x13", 7) == 0) { tagtype = NTAG_I2C_1K; break; } - else if (memcmp(version, "\x00\x04\x04\x05\x02\x01\x15", 7) == 0) { tagtype = NTAG_I2C_2K; break; } - else if (memcmp(version, "\x00\x04\x04\x05\x02\x02\x13", 7) == 0) { tagtype = NTAG_I2C_1K_PLUS; break; } - else if (memcmp(version, "\x00\x04\x04\x05\x02\x02\x15", 7) == 0) { tagtype = NTAG_I2C_2K_PLUS; break; } - else if (version[2] == 0x04) { tagtype = NTAG; break; } - else if (version[2] == 0x03) { tagtype = UL_EV1; } + if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0B", 7) == 0) { tagtype = MFU_TT_UL_EV1_48; break; } + else if (memcmp(version, "\x00\x04\x03\x01\x02\x00\x0B", 7) == 0) { tagtype = MFU_TT_UL_NANO_40; break; } + else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0B", 7) == 0) { tagtype = MFU_TT_UL_EV1_48; break; } + else if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0E", 7) == 0) { tagtype = MFU_TT_UL_EV1_128; break; } + else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0E", 7) == 0) { tagtype = MFU_TT_UL_EV1_128; break; } + else if (memcmp(version, "\x00\x04\x03\x01\x04\x00\x0F\x03", 8) == 0) { tagtype = MFU_TT_UL_AES; break; } + else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { tagtype = MFU_TT_UL_EV1_128; break; } // Mikron JSC Russia EV1 41 pages tag + else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0B", 7) == 0) { tagtype = MFU_TT_NTAG_210; break; } + else if (memcmp(version, "\x00\x04\x04\x01\x02\x00\x0B", 7) == 0) { tagtype = MFU_TT_NTAG_210u; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x02\x00\x0B", 7) == 0) { tagtype = MFU_TT_NTAG_210u; break; } + else if (memcmp(version, "\x00\x04\x04\x01\x01\x00\x0E", 7) == 0) { tagtype = MFU_TT_NTAG_212; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213; break; } + else if (memcmp(version, "\x00\x53\x04\x02\x01\x00\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213; break; } // Shanghai Feiju Microelectronics Co. Ltd. China (Xiaomi Air Purifier filter) + else if (memcmp(version, "\x00\x04\x04\x02\x01\x01\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213_C; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x11", 7) == 0) { tagtype = MFU_TT_NTAG_215; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x01\x00\x13", 7) == 0) { tagtype = MFU_TT_NTAG_216; break; } + else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213_F; break; } + else if (memcmp(version, "\x00\x04\x04\x04\x01\x00\x13", 7) == 0) { tagtype = MFU_TT_NTAG_216_F; break; } + else if (memcmp(version, "\x00\x04\x04\x02\x03\x00\x0F", 7) == 0) { tagtype = MFU_TT_NTAG_213_TT; break; } + else if (memcmp(version, "\x00\x04\x04\x05\x02\x01\x13", 7) == 0) { tagtype = MFU_TT_NTAG_I2C_1K; break; } + else if (memcmp(version, "\x00\x04\x04\x05\x02\x01\x15", 7) == 0) { tagtype = MFU_TT_NTAG_I2C_2K; break; } + else if (memcmp(version, "\x00\x04\x04\x05\x02\x02\x13", 7) == 0) { tagtype = MFU_TT_NTAG_I2C_1K_PLUS; break; } + else if (memcmp(version, "\x00\x04\x04\x05\x02\x02\x15", 7) == 0) { tagtype = MFU_TT_NTAG_I2C_2K_PLUS; break; } + else if (version[2] == 0x04) { tagtype = MFU_TT_NTAG; break; } + else if (version[2] == 0x03) { tagtype = MFU_TT_UL_EV1; } break; } case 0x01: - tagtype = UL_C; + tagtype = MFU_TT_UL_C; break; case 0x00: - tagtype = UL; + tagtype = MFU_TT_UL; break; case -1 : - tagtype = (UL | UL_C | NTAG_203); + tagtype = (MFU_TT_UL | MFU_TT_UL_C | MFU_TT_NTAG_203); break; // could be UL | UL_C magic tags default : - tagtype = UNKNOWN; + tagtype = MFU_TT_UNKNOWN; break; } // UL vs UL-C vs ntag203 test - if (tagtype & (UL | UL_C | NTAG_203)) { - if (!ul_select(&card)) return UL_ERROR; + if (tagtype & (MFU_TT_UL | MFU_TT_UL_C | MFU_TT_NTAG_203)) { + if (!ul_select(&card)) return MFU_TT_UL_ERROR; // do UL_C check first... uint8_t nonce[11] = {0x00}; int status = ulc_requestAuthentication(nonce, sizeof(nonce)); DropField(); if (status > 1) { - tagtype = UL_C; + tagtype = MFU_TT_UL_C; } else { // need to re-select after authentication error if (ul_select(&card) == false) - return UL_ERROR; + return MFU_TT_UL_ERROR; uint8_t data[16] = {0x00}; // read page 0x26-0x29 (last valid ntag203 page) status = ul_read(0x26, data, sizeof(data)); if (status <= 1) { - tagtype = UL; + tagtype = MFU_TT_UL; } else { // read page 0x30 (should error if it is a ntag203) status = ul_read(0x30, data, sizeof(data)); if (status <= 1) { - tagtype = NTAG_203; + tagtype = MFU_TT_NTAG_203; } else { - tagtype = UNKNOWN; + tagtype = MFU_TT_UNKNOWN; } } DropField(); } } - if (tagtype & UL) { + if (tagtype & MFU_TT_UL) { tagtype = ul_fudan_check(); DropField(); } @@ -1698,23 +1756,23 @@ uint32_t GetHF14AMfU_Type(void) { switch (nib) { // case 0: tagtype = SLE66R35E7; break; //or SLE 66R35E7 - mifare compat... should have different sak/atqa for mf 1k case 1: - tagtype = MY_D; + tagtype = MFU_TT_MY_D; break; // or SLE 66RxxS ... up to 512 pages of 8 user bytes... case 2: - tagtype = (MY_D_NFC); + tagtype = MFU_TT_MY_D_NFC; break; // or SLE 66RxxP ... up to 512 pages of 8 user bytes... (or in nfc mode FF pages of 4 bytes) case 3: - tagtype = (MY_D_MOVE | MY_D_MOVE_NFC); + tagtype = (MFU_TT_MY_D_MOVE | MFU_TT_MY_D_MOVE_NFC); break; // or SLE 66R01P // 38 pages of 4 bytes //notice: we can not currently distinguish between these two case 7: - tagtype = MY_D_MOVE_LEAN; + tagtype = MFU_TT_MY_D_MOVE_LEAN; break; // or SLE 66R01L // 16 pages of 4 bytes } } tagtype |= ul_magic_test(); - if (tagtype == (UNKNOWN | MAGIC)) { - tagtype = (UL_MAGIC); + if (tagtype == (MFU_TT_UNKNOWN | MFU_TT_MAGIC)) { + tagtype = (MFU_TT_UL_MAGIC); } return tagtype; } @@ -1767,13 +1825,12 @@ static int CmdHF14AMfUInfo(const char *Cmd) { uint8_t pack[4] = {0, 0, 0, 0}; int len; - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) return PM3_ESOFT; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " --------------------------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); ul_print_type(tagtype, 6); // Swap endianness @@ -1800,7 +1857,7 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } // UL_C Specific - if ((tagtype & UL_C)) { + if ((tagtype & MFU_TT_UL_C)) { // read pages 0x28, 0x29, 0x2A, 0x2B uint8_t ulc_conf[16] = {0x00}; @@ -1815,7 +1872,9 @@ static int CmdHF14AMfUInfo(const char *Cmd) { else locked = true; - if ((tagtype & MAGIC)) { + mfu_fingerprint(tagtype, has_auth_key, authkeyptr, ak_len); + + if ((tagtype & MFU_TT_MAGIC)) { //just read key uint8_t ulc_deskey[16] = {0x00}; status = ul_read(0x2C, ulc_deskey, sizeof(ulc_deskey)); @@ -1849,7 +1908,7 @@ static int CmdHF14AMfUInfo(const char *Cmd) { // do counters and signature first (don't neet auth) // ul counters are different than ntag counters - if ((tagtype & (UL_EV1_48 | UL_EV1_128 | UL_EV1))) { + if ((tagtype & (MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1))) { if (ulev1_print_counters() != 3) { // failed - re-select if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { @@ -1859,7 +1918,7 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } // NTAG counters? - if ((tagtype & (NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216))) { + if ((tagtype & (MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216))) { if (ntag_print_counter()) { // failed - re-select if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { @@ -1869,8 +1928,12 @@ static int CmdHF14AMfUInfo(const char *Cmd) { } // Read signature - if ((tagtype & (UL_EV1_48 | UL_EV1_128 | UL_EV1 | UL_NANO_40 | NTAG_210u | NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216 | NTAG_216_F | NTAG_I2C_1K | NTAG_I2C_2K | NTAG_I2C_1K_PLUS | NTAG_I2C_2K_PLUS))) { - uint8_t ulev1_signature[32] = {0x00}; + if ((tagtype & (MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1 | MFU_TT_UL_NANO_40 | + MFU_TT_NTAG_210u | MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | + MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216 | MFU_TT_NTAG_216_F | + MFU_TT_NTAG_I2C_1K | MFU_TT_NTAG_I2C_2K | MFU_TT_NTAG_I2C_1K_PLUS | MFU_TT_NTAG_I2C_2K_PLUS | + MFU_TT_UL_AES))) { + uint8_t ulev1_signature[49] = {0x00}; status = ulev1_readSignature(ulev1_signature, sizeof(ulev1_signature)); if (status == -1) { PrintAndLogEx(ERR, "Error: tag didn't answer to READ SIGNATURE"); @@ -1878,7 +1941,9 @@ static int CmdHF14AMfUInfo(const char *Cmd) { return PM3_ESOFT; } if (status == 32) { - ulev1_print_signature(tagtype, card.uid, ulev1_signature, sizeof(ulev1_signature)); + ulev1_print_signature(tagtype, card.uid, ulev1_signature, 32); + } else if (status == 48) { + ulev1_print_signature(tagtype, card.uid, ulev1_signature, 48); } else { // re-select if (ul_auth_select(&card, tagtype, has_auth_key, authkeyptr, pack, sizeof(pack)) == PM3_ESOFT) { @@ -2082,8 +2147,8 @@ static int CmdHF14AMfUWrBl(const char *Cmd) { uint8_t *authKeyPtr = authenticationkey; // starting with getting tagtype - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) return PM3_ESOFT; uint8_t maxblockno = 0; @@ -2203,8 +2268,8 @@ static int CmdHF14AMfURdBl(const char *Cmd) { uint8_t *authKeyPtr = authenticationkey; // start with getting tagtype - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) return PM3_ESOFT; uint8_t maxblockno = 0; @@ -2314,7 +2379,12 @@ void printMFUdumpEx(mfu_dump_t *card, uint16_t pages, uint8_t startpage) { for (uint16_t i = 0; i < pages; ++i) { if (i < 3) { - PrintAndLogEx(INFO, "%3d/0x%02X | %s| | %s", i + startpage, i + startpage, sprint_hex(data + i * 4, 4), sprint_ascii(data + i * 4, 4)); + PrintAndLogEx(INFO, "%3d/0x%02X | " _RED_("%s")"| | %s", + i + startpage, + i + startpage, + sprint_hex(data + i * 4, 4), + sprint_ascii(data + i * 4, 4) + ); continue; } switch (i) { @@ -2420,7 +2490,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu dump", - "Dump MIFARE Ultralight/NTAG tag to binary/eml/json files.\n" + "Dump MIFARE Ultralight/NTAG tag to files (bin/json)\n" "It autodetects card type." "Supports:\n" "Ultralight, Ultralight-C, Ultralight EV1\n" @@ -2487,8 +2557,8 @@ static int CmdHF14AMfUDump(const char *Cmd) { authKeyPtr = SwapEndian64(authenticationkey, ak_len, 4); } - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) return PM3_ESOFT; //get number of pages to read @@ -2505,7 +2575,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { PrintAndLogEx(SUCCESS, "Reading tag memory..."); uint8_t keytype = 0; if (has_auth_key || has_pwd) { - if (tagtype & UL_C) + if (tagtype & MFU_TT_UL_C) keytype = 1; //UL_C auth else keytype = 2; //UL_EV1/NTAG auth @@ -2555,7 +2625,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { // not ul_c and not std ul then attempt to collect info like // VERSION, SIGNATURE, COUNTERS, TEARING, PACK, - if (!(tagtype & UL_C || tagtype & UL || tagtype & MY_D_MOVE || tagtype & MY_D_MOVE_LEAN)) { + if (!(tagtype & MFU_TT_UL_C || tagtype & MFU_TT_UL || tagtype & MFU_TT_MY_D_MOVE || tagtype & MFU_TT_MY_D_MOVE_LEAN)) { //attempt to read pack uint8_t get_pack[] = {0, 0}; if (ul_auth_select(&card, tagtype, true, authKeyPtr, get_pack, sizeof(get_pack)) != PM3_SUCCESS) { @@ -2585,7 +2655,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { uint8_t n = 0; // NTAG has 1 counter, at 0x02 - if ((tagtype & (NTAG_213 | NTAG_213_F | NTAG_213_C | NTAG_213_TT | NTAG_215 | NTAG_216))) { + if ((tagtype & (MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216))) { n = 2; } @@ -2633,7 +2703,7 @@ static int CmdHF14AMfUDump(const char *Cmd) { authKeyPtr = authenticationkey; } - if (tagtype & UL_C) { //add 4 pages + if (tagtype & MFU_TT_UL_C) { //add 4 pages memcpy(data + pages * 4, authKeyPtr, ak_len); pages += ak_len / 4; } else { // 2nd page from end @@ -2651,23 +2721,27 @@ static int CmdHF14AMfUDump(const char *Cmd) { printMFUdumpEx(&dump_file_data, pages, start_page); - if (nosave == false) { - // user supplied filename? - if (fnlen < 1) { - PrintAndLogEx(INFO, "Using UID as filename"); - uint8_t uid[7] = {0}; - memcpy(uid, (uint8_t *)&dump_file_data.data, 3); - memcpy(uid + 3, (uint8_t *)&dump_file_data.data + 4, 4); - strcat(filename, "hf-mfu-"); - FillFileNameByUID(filename, uid, "-dump", sizeof(uid)); - } + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } - uint16_t datalen = pages * MFU_BLOCK_SIZE + MFU_DUMP_PREFIX_LENGTH; - pm3_save_dump(filename, (uint8_t *)&dump_file_data, datalen, jsfMfuMemory, MFU_BLOCK_SIZE); + // user supplied filename? + if (fnlen < 1) { + PrintAndLogEx(INFO, "Using UID as filename"); + uint8_t uid[7] = {0}; + memcpy(uid, (uint8_t *)&dump_file_data.data, 3); + memcpy(uid + 3, (uint8_t *)&dump_file_data.data + 4, 4); + strcat(filename, "hf-mfu-"); + FillFileNameByUID(filename, uid, "-dump", sizeof(uid)); + } - if (is_partial) { - PrintAndLogEx(WARNING, "Partial dump created. (%d of %d blocks)", pages, card_mem_size); - } + uint16_t datalen = pages * MFU_BLOCK_SIZE + MFU_DUMP_PREFIX_LENGTH; + pm3_save_dump(filename, (uint8_t *)&dump_file_data, datalen, jsfMfuMemory); + + if (is_partial) { + PrintAndLogEx(WARNING, "Partial dump created. (%d of %d blocks)", pages, card_mem_size); } return PM3_SUCCESS; @@ -2728,13 +2802,13 @@ int CmdHF14MfUTamper(const char *Cmd) { bool disable = arg_get_lit(ctx, 2); CLIParserFree(ctx); - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) { + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) { PrintAndLogEx(WARNING, "Tag type not detected"); DropField(); return PM3_ESOFT; } - if (tagtype != NTAG_213_TT) { + if (tagtype != MFU_TT_NTAG_213_TT) { PrintAndLogEx(WARNING, "Tag type not NTAG 213TT"); DropField(); return PM3_ESOFT; @@ -2752,7 +2826,7 @@ int CmdHF14MfUTamper(const char *Cmd) { if (use_msg) { if (ul_select(&card) == false) { DropField(); - return UL_ERROR; + return MFU_TT_UL_ERROR; } PrintAndLogEx(INFO, "Trying to write tamper message\n"); SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, tt_msg_page, 0, 0, msg_data, 4); @@ -2775,7 +2849,7 @@ int CmdHF14MfUTamper(const char *Cmd) { if (ul_select(&card) == false) { PrintAndLogEx(ERR, "Unable to select tag"); DropField(); - return UL_ERROR; + return MFU_TT_UL_ERROR; } uint8_t cfg_page[4] = {0x00}; @@ -2826,7 +2900,7 @@ int CmdHF14MfUTamper(const char *Cmd) { static int CmdHF14AMfURestore(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu restore", - "Restore MIFARE Ultralight/NTAG dump file to tag.\n", + "Restore MIFARE Ultralight/NTAG dump file (bin/eml/json) to tag.\n", "hf mfu restore -f myfile -s -> special write\n" "hf mfu restore -f myfile -k AABBCCDD -s -> special write, use key\n" "hf mfu restore -f myfile -k AABBCCDD -ser -> special write, use key, write dump pwd, ..." @@ -2834,7 +2908,7 @@ static int CmdHF14AMfURestore(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "specify dump filename (bin/eml/json)"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_str0("k", "key", "", "key for authentication (UL-C 16 bytes, EV1/NTAG 4 bytes)"), arg_lit0("l", NULL, "swap entered key's endianness"), arg_lit0("s", NULL, "enable special write UID -MAGIC TAG ONLY-"), @@ -3051,8 +3125,9 @@ static int CmdHF14AMfUeLoad(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_int0("q", "qty", "", "Number of blocks to load from eml file"), + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -3069,6 +3144,7 @@ static int CmdHF14AMfUeLoad(const char *Cmd) { free(nc); PrintAndLogEx(HINT, "Try " _YELLOW_("`hf mfu sim -t 7`") " to simulate an Amiibo."); + PrintAndLogEx(INFO, "Done!"); return res; } @@ -3090,7 +3166,7 @@ static int CmdHF14AMfUSim(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_int1("t", "type", "<1..10> ", "Simulation type to use"), + arg_int1("t", "type", "<1..12> ", "Simulation type to use"), arg_str0("u", "uid", "", "<4|7|10> hex bytes UID"), arg_int0("n", "num", "", "Exit simulation after blocks. 0 = infinite"), arg_lit0("v", "verbose", "Verbose output"), @@ -3581,6 +3657,8 @@ static int CmdHF14AMfUPwdGen(const char *Cmd) { if (selftest) return generator_selftest(); + uint8_t philips_mfg[10] = {0}; + if (use_tag) { // read uid from tag int res = ul_read_uid(uid); @@ -3588,6 +3666,19 @@ static int CmdHF14AMfUPwdGen(const char *Cmd) { return res; } + iso14a_card_select_t card; + if (ul_select(&card)) { + // Philips toothbrush needs page 0x21-0x23 + uint8_t data[16] = {0x00}; + int status = ul_read(0x21, data, sizeof(data)); + if (status == -1) { + PrintAndLogEx(DEBUG, "Error: tag didn't answer to READ"); + } else if (status == 16) { + memcpy(philips_mfg, data + 2, sizeof(philips_mfg)); + } + DropField(); + } + } else { if (u_len != 7) { PrintAndLogEx(WARNING, "Key must be 7 hex bytes"); @@ -3595,24 +3686,30 @@ static int CmdHF14AMfUPwdGen(const char *Cmd) { } } - PrintAndLogEx(INFO, "------------------.---------------"); + PrintAndLogEx(INFO, "------------------.------------------"); PrintAndLogEx(INFO, " Using UID 4b: " _YELLOW_("%s"), sprint_hex(uid, 4)); PrintAndLogEx(INFO, " Using UID 7b: " _YELLOW_("%s"), sprint_hex(uid, 7)); - PrintAndLogEx(INFO, "----------------------------------"); - PrintAndLogEx(INFO, " algo | pwd | pack"); - PrintAndLogEx(INFO, "-----------------+----------+-----"); - PrintAndLogEx(INFO, " Transport EV1 | %08X | %04X", ul_ev1_pwdgenA(uid), ul_ev1_packgenA(uid)); - PrintAndLogEx(INFO, " Amiibo | %08X | %04X", ul_ev1_pwdgenB(uid), ul_ev1_packgenB(uid)); - PrintAndLogEx(INFO, " Lego Dimension | %08X | %04X", ul_ev1_pwdgenC(uid), ul_ev1_packgenC(uid)); - PrintAndLogEx(INFO, " XYZ 3D printer | %08X | %04X", ul_ev1_pwdgenD(uid), ul_ev1_packgenD(uid)); - PrintAndLogEx(INFO, " Xiaomi purifier | %08X | %04X", ul_ev1_pwdgenE(uid), ul_ev1_packgenE(uid)); - PrintAndLogEx(INFO, " NTAG tools | %08X | %04X", ul_ev1_pwdgenF(uid), ul_ev1_packgen_def(uid)); - PrintAndLogEx(INFO, "-----------------+----------+-----"); + PrintAndLogEx(INFO, "-------------------------------------"); + PrintAndLogEx(INFO, " algo | pwd | pack"); + PrintAndLogEx(INFO, "--------------------+----------+-----"); + PrintAndLogEx(INFO, " Transport EV1 | %08X | %04X", ul_ev1_pwdgenA(uid), ul_ev1_packgenA(uid)); + PrintAndLogEx(INFO, " Amiibo | %08X | %04X", ul_ev1_pwdgenB(uid), ul_ev1_packgenB(uid)); + PrintAndLogEx(INFO, " Lego Dimension | %08X | %04X", ul_ev1_pwdgenC(uid), ul_ev1_packgenC(uid)); + PrintAndLogEx(INFO, " XYZ 3D printer | %08X | %04X", ul_ev1_pwdgenD(uid), ul_ev1_packgenD(uid)); + PrintAndLogEx(INFO, " Xiaomi purifier | %08X | %04X", ul_ev1_pwdgenE(uid), ul_ev1_packgenE(uid)); + PrintAndLogEx(INFO, " NTAG tools | %08X | %04X", ul_ev1_pwdgenF(uid), ul_ev1_packgen_def(uid)); + if (philips_mfg[0] != 0) { + PrintAndLogEx(INFO, " Philips Toothbrush | %08X | %04X", ul_ev1_pwdgenG(uid, philips_mfg), ul_ev1_packgenG(uid, philips_mfg)); + } + PrintAndLogEx(INFO, "--------------------+----------+-----"); PrintAndLogEx(INFO, " Vingcard algo"); - PrintAndLogEx(INFO, " Saflok algo"); + uint64_t key = 0; + mfc_algo_saflok_one(uid, 0, 0, &key); + PrintAndLogEx(INFO, " Saflok algo | %012" PRIX64, key); PrintAndLogEx(INFO, " SALTO algo"); PrintAndLogEx(INFO, " Dorma Kaba algo"); - PrintAndLogEx(INFO, "----------------------------------"); + PrintAndLogEx(INFO, " STiD algo"); + PrintAndLogEx(INFO, "-------------------------------------"); return PM3_SUCCESS; } @@ -4340,8 +4437,8 @@ int CmdHF14MfuNDEFRead(const char *Cmd) { } // Get tag type - TagTypeUL_t tagtype = GetHF14AMfU_Type(); - if (tagtype == UL_ERROR) { + uint64_t tagtype = GetHF14AMfU_Type(); + if (tagtype == MFU_TT_UL_ERROR) { PrintAndLogEx(WARNING, "No Ultralight / NTAG based tag found"); return PM3_ESOFT; } @@ -4408,14 +4505,20 @@ int CmdHF14MfuNDEFRead(const char *Cmd) { } DropField(); - if (fnlen != 0) { - saveFile(filename, ".bin", records, (size_t)maxsize); - } + status = NDEFRecordsDecodeAndPrint(records, (size_t)maxsize, verbose); if (status != PM3_SUCCESS) { status = NDEFDecodeAndPrint(records, (size_t)maxsize, verbose); } + // get total NDEF length before save. If fails, we save it all + size_t n = 0; + if (NDEFGetTotalLength(records, maxsize, &n) != PM3_SUCCESS) + n = maxsize; + + pm3_save_dump(filename, records, n, jsfNDEF); + + char *jooki = strstr((char *)records, "s.jooki.rocks/s/?s="); if (jooki) { jooki += 17; @@ -4522,18 +4625,18 @@ static int CmdHF14AMfuEView(const char *Cmd) { static int CmdHF14AMfuESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu esave", - "Saves emulator memory to a MIFARE Ultralight/NTAG dump file (bin/eml/json)\n" + "Saves emulator memory to a MIFARE Ultralight/NTAG dump file (bin/json)\n" "By default number of pages saved depends on defined tag type.\n" "You can override this with option --end.", "hf mfu esave\n" "hf mfu esave --end 255 -> saves whole memory\n" - "hf mfu esave -f hf-mfu-04010203040506-dump.json" + "hf mfu esave -f hf-mfu-04010203040506-dump" ); void *argtable[] = { arg_param_begin, arg_int0("e", "end", "", "index of last block"), - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_param_end }; @@ -4578,7 +4681,7 @@ static int CmdHF14AMfuESave(const char *Cmd) { // save dump. Last block contains PACK + RFU uint16_t datalen = (end + 1) * MFU_BLOCK_SIZE + MFU_DUMP_PREFIX_LENGTH; - res = pm3_save_dump(filename, (uint8_t *)dump, datalen, jsfMfuMemory, MFU_BLOCK_SIZE); + res = pm3_save_dump(filename, (uint8_t *)dump, datalen, jsfMfuMemory); free(dump); return res; @@ -4593,7 +4696,7 @@ static int CmdHF14AMfuView(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_lit0("v", "verbose", "Verbose output"), arg_param_end }; @@ -4637,6 +4740,10 @@ static int CmdHF14AMfuView(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14AMfuList(const char *Cmd) { + return CmdTraceListAlias(Cmd, "hf 14a", "14a -c"); +} + /* static int CmdHF14AMfUCDecryptAmiibo(const char *Cmd){ @@ -4683,6 +4790,7 @@ static int CmdHF14AMfUCDecryptAmiibo(const char *Cmd){ //------------------------------------ static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdHF14AMfuList, AlwaysAvailable, "List MIFARE Ultralight / NTAG history"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("recovery") " -------------------------"}, {"keygen", CmdHF14AMfUGenDiverseKeys, AlwaysAvailable, "Generate 3DES MIFARE diversified keys"}, {"pwdgen", CmdHF14AMfUPwdGen, AlwaysAvailable, "Generate pwd from known algos"}, diff --git a/client/src/cmdhfmfu.h b/client/src/cmdhfmfu.h index 1ed652e79..7b1acf27a 100644 --- a/client/src/cmdhfmfu.h +++ b/client/src/cmdhfmfu.h @@ -43,7 +43,7 @@ typedef struct { } PACKED old_mfu_dump_t; uint32_t GetHF14AMfU_Type(void); -int ul_print_type(uint32_t tagtype, uint8_t spaces); +int ul_print_type(uint64_t tagtype, uint8_t spaces); void printMFUdumpEx(mfu_dump_t *card, uint16_t pages, uint8_t startpage); int ul_read_uid(uint8_t *uid); int trace_mfuc_try_default_3des_keys(uint8_t **correct_key, int state, uint8_t (*authdata)[16]); @@ -55,44 +55,44 @@ int CmdHF14MfUTamper(const char *Cmd); uint16_t ul_ev1_packgen_VCNEW(uint8_t *uid, uint32_t pwd); uint32_t ul_ev1_otpgenA(uint8_t *uid); -typedef enum TAGTYPE_UL { - UNKNOWN = 0x000000, - UL = 0x1, - UL_C = 0x2, - UL_EV1_48 = 0x4, - UL_EV1_128 = 0x8, - NTAG = 0x10, - NTAG_203 = 0x20, - NTAG_210 = 0x40, - NTAG_212 = 0x80, - NTAG_213 = 0x100, - NTAG_215 = 0x200, - NTAG_216 = 0x400, - MY_D = 0x800, - MY_D_NFC = 0x1000, - MY_D_MOVE = 0x2000, - MY_D_MOVE_NFC = 0x4000, - MY_D_MOVE_LEAN = 0x8000, - NTAG_I2C_1K = 0x10000, - NTAG_I2C_2K = 0x20000, - NTAG_I2C_1K_PLUS = 0x40000, - NTAG_I2C_2K_PLUS = 0x80000, - FUDAN_UL = 0x100000, - MAGIC = 0x200000, - NTAG_213_F = 0x400000, - NTAG_216_F = 0x800000, - UL_EV1 = 0x1000000, - UL_NANO_40 = 0x2000000, - NTAG_213_TT = 0x4000000, - NTAG_213_C = 0x8000000, - MAGIC_1A = 0x10000000 | MAGIC, - MAGIC_1B = 0x20000000 | MAGIC, - MAGIC_NTAG = 0x40000000 | MAGIC, - NTAG_210u = 0x80000000, - UL_MAGIC = UL | MAGIC, - UL_C_MAGIC = UL_C | MAGIC, - // Don't forget to fill UL_TYPES_ARRAY and UL_MEMORY_ARRAY if new types are added - UL_ERROR = 0xFFFFFF, -} TagTypeUL_t; +#define MFU_TT_UNKNOWN 0x0ULL +#define MFU_TT_UL 0x1ULL +#define MFU_TT_UL_C 0x2ULL +#define MFU_TT_UL_EV1_48 0x4ULL +#define MFU_TT_UL_EV1_128 0x8ULL +#define MFU_TT_NTAG 0x10ULL +#define MFU_TT_NTAG_203 0x20ULL +#define MFU_TT_NTAG_210 0x40ULL +#define MFU_TT_NTAG_212 0x80ULL +#define MFU_TT_NTAG_213 0x100ULL +#define MFU_TT_NTAG_215 0x200ULL +#define MFU_TT_NTAG_216 0x400ULL +#define MFU_TT_MY_D 0x800ULL +#define MFU_TT_MY_D_NFC 0x1000ULL +#define MFU_TT_MY_D_MOVE 0x2000ULL +#define MFU_TT_MY_D_MOVE_NFC 0x4000ULL +#define MFU_TT_MY_D_MOVE_LEAN 0x8000ULL +#define MFU_TT_NTAG_I2C_1K 0x10000ULL +#define MFU_TT_NTAG_I2C_2K 0x20000ULL +#define MFU_TT_NTAG_I2C_1K_PLUS 0x40000ULL +#define MFU_TT_NTAG_I2C_2K_PLUS 0x80000ULL +#define MFU_TT_FUDAN_UL 0x100000ULL +#define MFU_TT_MAGIC 0x200000ULL +#define MFU_TT_NTAG_213_F 0x400000ULL +#define MFU_TT_NTAG_216_F 0x800000ULL +#define MFU_TT_UL_EV1 0x1000000ULL +#define MFU_TT_UL_NANO_40 0x2000000ULL +#define MFU_TT_NTAG_213_TT 0x4000000ULL +#define MFU_TT_NTAG_213_C 0x8000000ULL +#define MFU_TT_MAGIC_1A (0x10000000ULL | MFU_TT_MAGIC) +#define MFU_TT_MAGIC_1B (0x20000000ULL | MFU_TT_MAGIC) +#define MFU_TT_MAGIC_NTAG (0x40000000ULL | MFU_TT_MAGIC) +#define MFU_TT_NTAG_210u 0x80000000ULL +#define MFU_TT_UL_AES 0x100000000ULL +#define MFU_TT_UL_MAGIC (MFU_TT_UL | MFU_TT_MAGIC) +#define MFU_TT_UL_C_MAGIC (MFU_TT_UL_C | MFU_TT_MAGIC) +// Don't forget to fill UL_TYPES_ARRAY and UL_MEMORY_ARRAY if new types are added +#define MFU_TT_UL_ERROR 0x7FFFFFFFULL + #endif diff --git a/client/src/cmdhfntag424.c b/client/src/cmdhfntag424.c index fa6170c94..e1593500b 100644 --- a/client/src/cmdhfntag424.c +++ b/client/src/cmdhfntag424.c @@ -20,15 +20,832 @@ #include #include "cmdparser.h" #include "commonutil.h" +#include "comms.h" +#include "iso7816/apduinfo.h" #include "protocols.h" #include "cliparser.h" #include "cmdmain.h" #include "fileutils.h" // saveFile #include "crypto/libpcrypto.h" // aes_decode #include "cmac.h" +#include "cmdhf14a.h" +#include "ui.h" +#include "util.h" +#include "crc32.h" +#include "cmdhfmfdes.h" + +#define NTAG424_MAX_BYTES 412 -#define NTAG424_MAX_BYTES 412 +// NTAG424 commands currently implemented +#define NTAG424_CMD_GET_FILE_SETTINGS 0xF5 +#define NTAG424_CMD_CHANGE_FILE_SETTINGS 0x5F +#define NTAG424_CMD_CHANGE_KEY 0xC4 +#define NTAG424_CMD_READ_DATA 0xAD +#define NTAG424_CMD_WRITE_DATA 0x8D +#define NTAG424_CMD_AUTHENTICATE_EV2_FIRST 0x71 +#define NTAG424_CMD_MORE_DATA 0xAF +#define NTAG424_CMD_GET_VERSION 0x60 +#define NTAG424_CMD_GET_SIGNATURE 0x3C + +// +// Original from https://github.com/rfidhacking/node-sdm/ +// +typedef struct sdm_picc_s { + uint8_t tag; + uint8_t uid[7]; + uint8_t cnt[3]; + uint32_t cnt_int; +} sdm_picc_t; + +// -------------- Encryption structs --------------------------- +typedef struct { + uint8_t ti[4]; + uint8_t rnd_a[16]; + uint8_t pd_cap2[6]; + uint8_t pcd_cap2[6]; +} ntag424_ev2_response_t; + +typedef struct { + uint16_t command_counter; + uint8_t ti[4]; + uint8_t encryption[16]; + uint8_t mac[16]; +} ntag424_session_keys_t; + +typedef enum { + COMM_PLAIN, + COMM_MAC, + COMM_FULL +} ntag424_communication_mode_t; + +const CLIParserOption ntag424_communication_mode_options[] = { + {COMM_PLAIN, "plain"}, + {COMM_MAC, "mac"}, + {COMM_FULL, "encrypt"}, + {0, NULL}, +}; + +// -------------- File settings structs ------------------------- +// Enabling this bit in the settings will also reset the read counter to 0 +#define FILE_SETTINGS_OPTIONS_SDM_AND_MIRRORING (1 << 6) + +#define FILE_SETTINGS_SDM_OPTIONS_UID (1 << 7) +#define FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER (1 << 6) +#define FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER_LIMIT (1 << 5) +#define FILE_SETTINGS_SDM_OPTIONS_SDM_ENC_FILE_DATA (1 << 4) +#define FILE_SETTINGS_SDM_OPTIONS_ENCODING_MODE_ASCII (1 << 0) + +typedef struct { + uint8_t sdm_options; + uint8_t sdm_access[2]; + uint8_t sdm_data[8][3]; +} ntag424_file_sdm_settings_t; + +typedef struct { + uint8_t type; + uint8_t options; + uint8_t access[2]; + uint8_t size[3]; + ntag424_file_sdm_settings_t optional_sdm_settings; +} ntag424_file_settings_t; + +#define SETTINGS_WITHOUT_SDM_DATA_SIZE (1+1+2+3+1+2) + +// A different struct is used when actually writing the settings back, +// since we obviously can't change the size or type of a static file. +typedef struct { + uint8_t options; + uint8_t access[2]; + ntag424_file_sdm_settings_t optional_sdm_settings; +} file_settings_write_t; + +// -------------- Version information structs ------------------------- +typedef struct { + uint8_t vendor_id; + uint8_t type; + uint8_t sub_type; + uint8_t major_version; + uint8_t minor_version; + uint8_t storage_size; + uint8_t protocol; +} PACKED ntag424_version_information_t; + +typedef struct { + uint8_t uid[7]; + uint8_t batch[4]; + uint8_t fab_key_high : 4; + uint8_t batchno : 4; + uint8_t week_prod : 7; + uint8_t fab_key_low : 1; + uint8_t year_prod; +} PACKED ntag424_production_information_t; + +typedef struct { + ntag424_version_information_t hardware; + ntag424_version_information_t software; + ntag424_production_information_t production; +} ntag424_full_version_information_t; + + +static void ntag424_print_version_information(ntag424_version_information_t *version) { + PrintAndLogEx(INFO, " vendor id: " _GREEN_("%02X"), version->vendor_id); + PrintAndLogEx(INFO, " type: " _GREEN_("%02X"), version->type); + PrintAndLogEx(INFO, " sub type: " _GREEN_("%02X"), version->sub_type); + PrintAndLogEx(INFO, " version: " _GREEN_("%d.%d"), version->major_version, version->minor_version); + PrintAndLogEx(INFO, "storage size: " _GREEN_("%02X"), version->storage_size); + PrintAndLogEx(INFO, " protocol: " _GREEN_("%02X"), version->protocol); +} + +static void ntag424_print_production_information(ntag424_production_information_t *version) { + PrintAndLogEx(INFO, " uid: " _GREEN_("%s"), sprint_hex(version->uid, sizeof(version->uid))); + PrintAndLogEx(INFO, " batch: " _GREEN_("%s"), sprint_hex(version->batch, sizeof(version->batch))); + PrintAndLogEx(INFO, " batchno: " _GREEN_("%02X"), version->batchno); + PrintAndLogEx(INFO, " fab key: " _GREEN_("%02X"), (version->fab_key_high << 1) | version->fab_key_low); + PrintAndLogEx(INFO, " date: week " _GREEN_("%02X") " / " _GREEN_("20%02X"), version->week_prod, version->year_prod); +} + +static void ntag424_print_full_version_information(ntag424_full_version_information_t *version) { + PrintAndLogEx(INFO, "--- " _CYAN_("Hardware version information:")); + ntag424_print_version_information(&version->hardware); + + PrintAndLogEx(INFO, "--- " _CYAN_("Software version information:")); + ntag424_print_version_information(&version->software); + + PrintAndLogEx(INFO, "--- " _CYAN_("Production information:")); + ntag424_print_production_information(&version->production); +} + +// Currently unused functions, commented out due to -Wunused-function +/*static void ntag424_file_settings_set_access_rights(ntag424_file_settings_t *settings, + uint8_t read_write_key, uint8_t change_key, + uint8_t read_key, uint8_t write_key) + +{ + settings->access[0] = read_write_key << 4 | change_key; + settings->access[1] = read_key << 4 | write_key; +}*/ + +// Currently unused functions, commented out due to -Wunused-function +/*static void ntag424_file_settings_set_sdm_access_rights(ntag424_file_settings_t *settings, + uint8_t sdm_meta_read, uint8_t sdm_file_read, uint8_t sdm_ctr_ret) +{ + settings->optional_sdm_settings.sdm_access[1] = sdm_meta_read << 4 | sdm_file_read; + settings->optional_sdm_settings.sdm_access[0] = 0xf << 4 | sdm_ctr_ret; // (0xf is due to reserved for future use) +}*/ + + +static uint8_t ntag424_file_settings_get_sdm_meta_read(const ntag424_file_settings_t *settings) { + return settings->optional_sdm_settings.sdm_access[1] >> 4; +} + +static uint8_t ntag424_file_settings_get_sdm_file_read(const ntag424_file_settings_t *settings) { + return settings->optional_sdm_settings.sdm_access[1] & 0xf; +} + +// Currently unused functions, commented out due to -Wunused-function +/*static uint8_t ntag424_file_settings_get_sdm_ctr_ret(const ntag424_file_settings_t *settings) { + return settings->optional_sdm_settings.sdm_access[0] & 4; +}*/ + +// Calculate the actual size of a file settings struct. A variable number of data is attached +// at the end depending on settings. +static int ntag424_calc_file_settings_size(const ntag424_file_settings_t *settings) { + int size = 7; + + if (settings->options & FILE_SETTINGS_OPTIONS_SDM_AND_MIRRORING) { + size += 3; // sdm_options and sdm_access must be present + + if (settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_UID && + ntag424_file_settings_get_sdm_meta_read(settings) == 0xe) { + size += 3; // UIDOffset + } + + if (settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER && + ntag424_file_settings_get_sdm_meta_read(settings) == 0xe) { + size += 3; // SDMReadCtrOffset + } + + if (ntag424_file_settings_get_sdm_meta_read(settings) <= 0x04) { + size += 3; // PICCDataOffset + } + + if (ntag424_file_settings_get_sdm_file_read(settings) != 0x0f) { + size += 3; // SDMMacInputOffset + } + + if (ntag424_file_settings_get_sdm_file_read(settings) != 0x0f && + settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_ENC_FILE_DATA) { + size += 3; // SDMEncOffset + size += 3; // SDMEncLength + } + + if (ntag424_file_settings_get_sdm_file_read(settings) != 0x0f) { + // Warning, this value has different offsets depending on + // FILE_SETTINGS_SDM_OPTIONS_SDM_ENC_FILE_DATA + size += 3; // SDMMacOffset + } + + if (settings->optional_sdm_settings.sdm_options & FILE_SETTINGS_SDM_OPTIONS_SDM_READ_COUNTER_LIMIT) { + size += 3; // SDMReadCtrLimit + } + } + + return size; +} + +static int ntag424_calc_file_write_settings_size(const ntag424_file_settings_t *settings) { + return ntag424_calc_file_settings_size(settings) - 4; +} + +static void ntag424_calc_send_iv(ntag424_session_keys_t *session_keys, uint8_t *out_ivc) { + uint8_t iv_clear[] = { 0xa5, 0x5a, + session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3], + (uint8_t)(session_keys->command_counter), (uint8_t)(session_keys->command_counter >> 8), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + uint8_t zero_iv[16] = {0}; + aes_encode(zero_iv, session_keys->encryption, iv_clear, out_ivc, 16); +} + +static void ntag424_calc_receive_iv(ntag424_session_keys_t *session_keys, uint8_t *out_ivc) { + uint8_t iv_clear[] = { 0x5a, 0xa5, + session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3], + (uint8_t)(session_keys->command_counter), (uint8_t)(session_keys->command_counter >> 8), + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + uint8_t zero_iv[16] = {0}; + aes_encode(zero_iv, session_keys->encryption, iv_clear, out_ivc, 16); +} + +static void ntag424_calc_mac(ntag424_session_keys_t *session_keys, uint8_t command, uint8_t *data, uint8_t datalen, uint8_t *out_mac) { + uint8_t mac_input_header[] = { command, + (uint8_t)session_keys->command_counter, (uint8_t)(session_keys->command_counter >> 8), + session_keys->ti[0], session_keys->ti[1], session_keys->ti[2], session_keys->ti[3] + }; + + int mac_input_len = sizeof(mac_input_header) + datalen; + + uint8_t *mac_input = (uint8_t *)calloc(mac_input_len, sizeof(uint8_t)); + memcpy(mac_input, mac_input_header, sizeof(mac_input_header)); + memcpy(&mac_input[sizeof(mac_input_header)], data, datalen); + uint8_t mac[16] = {0}; + mbedtls_aes_cmac_prf_128(session_keys->mac, 16, mac_input, sizeof(mac_input_header) + datalen, mac); + + for (int i = 0; i < 8; i++) { + out_mac[i] = mac[i * 2 + 1]; + } + + free(mac_input); +} + +static int ntag424_comm_mac_apdu(APDU_t *apdu, int command_header_length, int apdu_max_data_size, ntag424_session_keys_t *session_keys) { + + int size = apdu->lc; + + if (size + 8 > apdu_max_data_size) { + return PM3_EOVFLOW; + } + + ntag424_calc_mac(session_keys, apdu->ins, apdu->data, size, &apdu->data[size]); + session_keys->command_counter++; // CmdCtr should be incremented each time a MAC is calculated + apdu->lc = size + 8; + + return PM3_SUCCESS; +} + +static int ntag424_comm_encrypt_apdu(APDU_t *apdu, int command_header_length, int apdu_max_data_size, ntag424_session_keys_t *session_keys) { + // ------- Calculate IV + uint8_t ivc[16]; + ntag424_calc_send_iv(session_keys, ivc); + + int size = apdu->lc; + + size_t encrypt_data_size = size - command_header_length; + size_t padded_data_size = encrypt_data_size + 16 - (encrypt_data_size % 16); // pad up to 16 byte blocks + uint8_t temp_buffer[256] = {0}; + + if (!encrypt_data_size) { + return PM3_SUCCESS; + } + + if (padded_data_size + command_header_length > apdu_max_data_size) { + return PM3_EOVFLOW; + } + + // ------ Pad data + memcpy(temp_buffer, &apdu->data[command_header_length], encrypt_data_size); // We encrypt everything except the CmdHdr (first byte in data) + temp_buffer[encrypt_data_size] = 0x80; + + // ------ Encrypt it + aes_encode(ivc, session_keys->encryption, temp_buffer, &apdu->data[command_header_length], padded_data_size); + + apdu->lc = (uint8_t)(command_header_length + padded_data_size); // Set size to CmdHdr + padded data + + return PM3_SUCCESS; +} + +static int ntag424_exchange_apdu(APDU_t apdu, int command_header_length, uint8_t *response, int *response_length, ntag424_communication_mode_t comm_mode, ntag424_session_keys_t *session_keys, uint8_t sw1_expected, uint8_t sw2_expected) { + + int res; + + // New buffer since we might need to expand the data in the apdu + int buffer_length = 256; + uint8_t tmp_apdu_buffer[256] = {0}; + + if (comm_mode != COMM_PLAIN) { + if (session_keys == NULL) { + PrintAndLogEx(ERR, "Non-plain communications mode requested but no session keys supplied"); + return PM3_EINVARG; + } + memcpy(tmp_apdu_buffer, apdu.data, apdu.lc); + apdu.data = tmp_apdu_buffer; + } + + if (comm_mode == COMM_FULL) { + res = ntag424_comm_encrypt_apdu(&apdu, command_header_length, buffer_length, session_keys); + if (res != PM3_SUCCESS) { + return res; + } + } + + if (comm_mode == COMM_MAC || comm_mode == COMM_FULL) { + res = ntag424_comm_mac_apdu(&apdu, command_header_length, buffer_length, session_keys); + if (res != PM3_SUCCESS) { + return res; + } + } + + uint8_t cmd[256] = {0}; + int apdu_length = 256; + + if (APDUEncode(&apdu, cmd, &apdu_length) != 0) { + return PM3_EINVARG; + } + + res = ExchangeAPDU14a(cmd, apdu_length + 1, false, true, response, *response_length, response_length); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to exchange APDU: %d", res); + return res; + } + + if (*response_length < 2) { + PrintAndLogEx(ERR, "No response"); + return PM3_ESOFT; + } + + uint8_t sw1 = response[*response_length - 2]; + uint8_t sw2 = response[*response_length - 1]; + + if (sw1 != sw1_expected || sw2 != sw2_expected) { + PrintAndLogEx(ERR, "Error from card: %02X %02X (%s)", sw1, sw2, GetAPDUCodeDescription(sw1, sw2)); + return PM3_ESOFT; + } + + // Decrypt data if we are in full communications mode. If we want to verify MAC, this + // should also be done here + if (comm_mode == COMM_FULL) { + uint8_t iv[16] = {0}; + ntag424_calc_receive_iv(session_keys, iv); + + uint8_t tmp[256]; + memcpy(tmp, response, *response_length); + aes_decode(iv, session_keys->encryption, response, tmp, *response_length - 10); + + memcpy(response, tmp, *response_length); + } + + return PM3_SUCCESS; +} + + +static int ntag424_get_file_settings(uint8_t fileno, ntag424_file_settings_t *settings_out) { + int response_length = sizeof(ntag424_file_settings_t) + 2; + uint8_t response[response_length]; + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_GET_FILE_SETTINGS, + .lc = 1, + .data = &fileno, + .extended_apdu = false + }; + + int res = ntag424_exchange_apdu(apdu, 1, response, &response_length, COMM_PLAIN, NULL, 0x91, 0x00); + if (res != PM3_SUCCESS) { + return res; + } + + if (settings_out) { + memcpy(settings_out, response, response_length); + } + + return PM3_SUCCESS; +} + +static int ntag424_write_file_settings(uint8_t fileno, ntag424_file_settings_t *settings, ntag424_session_keys_t *session_keys) { + + // ------- Convert file settings to the format for writing + file_settings_write_t write_settings = { + .options = settings->options, + .access[0] = settings->access[0], + .access[1] = settings->access[1], + .optional_sdm_settings = settings->optional_sdm_settings, + }; + + size_t settings_size = ntag424_calc_file_write_settings_size(settings); + + uint8_t cmd_buffer[256]; + cmd_buffer[0] = fileno; + memcpy(&cmd_buffer[1], &write_settings, settings_size); + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_CHANGE_FILE_SETTINGS, + .lc = 1 + settings_size, + .data = cmd_buffer + }; + + + // ------- Actually send the APDU + int response_length = 8 + 2; + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, 1, response, &response_length, COMM_FULL, session_keys, 0x91, 0x00); + return res; +} + +static void ntag424_print_file_settings(uint8_t fileno, const ntag424_file_settings_t *settings) { + + int num_sdm_data = (ntag424_calc_file_settings_size(settings) - SETTINGS_WITHOUT_SDM_DATA_SIZE) / 3; + + PrintAndLogEx(SUCCESS, "--- " _CYAN_("File %d settings:"), fileno); + + PrintAndLogEx(SUCCESS, " type: " _GREEN_("%02X"), settings->type); + PrintAndLogEx(SUCCESS, " options: " _GREEN_("%02X"), settings->options); + PrintAndLogEx(SUCCESS, " access: " _GREEN_("%02X%02X (RW, C, R, W)"), settings->access[0], settings->access[1]); + PrintAndLogEx(SUCCESS, " size: " _GREEN_("%02X%02X%02X"), settings->size[2], settings->size[1], settings->size[0]); + + if (settings->options & FILE_SETTINGS_OPTIONS_SDM_AND_MIRRORING) { + PrintAndLogEx(SUCCESS, "--- " _CYAN_("SDM settings: ")); + PrintAndLogEx(SUCCESS, " options: " _GREEN_("%02X"), settings->optional_sdm_settings.sdm_options); + PrintAndLogEx(SUCCESS, " sdm access: " _GREEN_("%02X%02X"), settings->optional_sdm_settings.sdm_access[0], settings->optional_sdm_settings.sdm_access[1]); + + if (num_sdm_data > 0) { + PrintAndLogEx(SUCCESS, "--- " _CYAN_("SDM data: ")); + for (int i = 0; i < num_sdm_data; i++) { + PrintAndLogEx(SUCCESS, " %d: %02X%02X%02X", i, + settings->optional_sdm_settings.sdm_data[i][2], + settings->optional_sdm_settings.sdm_data[i][1], + settings->optional_sdm_settings.sdm_data[i][0]); + } + } + } +} + +// NTAG424 only have one static application, so we select it here +static int ntag424_select_application(void) { + const size_t RESPONSE_LENGTH = 2; + uint8_t cmd[] = {0x00, 0xA4, 0x04, 0x0C, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00 }; + uint8_t resp[RESPONSE_LENGTH]; + int outlen = 0; + int res; + + res = ExchangeAPDU14a(cmd, sizeof(cmd), false, true, resp, RESPONSE_LENGTH, &outlen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to send apdu"); + return res; + } + + if (outlen != RESPONSE_LENGTH || resp[RESPONSE_LENGTH - 2] != 0x90 || resp[RESPONSE_LENGTH - 1] != 0x00) { + PrintAndLogEx(ERR, "Failed to select application"); + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + +static int ntag424_auth_first_step(uint8_t keyno, uint8_t *key, uint8_t *out) { + uint8_t key_number[2] = { keyno, 0x00 }; + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_AUTHENTICATE_EV2_FIRST, + .lc = 0x02, + .data = key_number + }; + + int response_length = 16 + 2; + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, 2, response, &response_length, COMM_PLAIN, NULL, 0x91, 0xAF); + if (res != PM3_SUCCESS) { + return res; + } + + if (response_length != 16 + 2) { + PrintAndLogEx(ERR, "Failed to get RndB (invalid key number?)"); + return PM3_ESOFT; + } + + uint8_t iv[16] = {0}; + aes_decode(iv, key, response, out, 16); + + return PM3_SUCCESS; +} + +static int ntag424_auth_second_step(uint8_t *challenge, uint8_t *response_out) { + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_MORE_DATA, + .lc = 0x20, + .data = challenge, + }; + int response_length = 256; + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, 0x20, response, &response_length, COMM_PLAIN, NULL, 0x91, 0x00); + if (res != PM3_SUCCESS) { + return res; + } + + memcpy(response_out, response, response_length - 2); + + return PM3_SUCCESS; +} + +// Authenticate against a key number and optionally get session keys out +static int ntag424_authenticate_ev2_first(uint8_t keyno, uint8_t *key, ntag424_session_keys_t *session_keys_out) { + // -------- Get first challenge from card + uint8_t rnd_b_clear[16] = {0}; + + int res = ntag424_auth_first_step(keyno, key, rnd_b_clear); + if (res != PM3_SUCCESS) { + return res; + } + + // -------- Concatenate RndA and RndB and encrypt it with the key + uint8_t concat_clear[32] = {0}; + uint8_t concat_enc[32] = {0}; + // This should of course be completely random, if we cared + // about security + uint8_t rnd_a_clear[16] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, + 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf + }; + + uint8_t iv[16] = {0}; + memcpy(&concat_clear[16], &rnd_b_clear[1], 15); + concat_clear[31] = rnd_b_clear[0]; + memcpy(concat_clear, rnd_a_clear, 16); + + aes_encode(iv, key, concat_clear, concat_enc, 32); + + // -------- Do second step with our concatenated encrypted RndA || RndB + uint8_t resp[4 + 16 + 6 + 6]; + res = ntag424_auth_second_step(concat_enc, resp); + if (res != PM3_SUCCESS) { + return res; + } + + ntag424_ev2_response_t response; + aes_decode(iv, key, resp, (uint8_t *)&response, sizeof(ntag424_ev2_response_t)); + + // -------- Verify that the response we got contains the RndA that we supplied (rotated one byte) + if (memcmp(response.rnd_a, &rnd_a_clear[1], 15) != 0 || + response.rnd_a[15] != rnd_a_clear[0]) { + PrintAndLogEx(ERR, "Incorrect response from card\n" + "expected: %s\n" + "got: %s" + , sprint_hex(rnd_a_clear, 16), + sprint_hex(response.rnd_a, 16)); + return PM3_ESOFT; + } + + // -------- Optionally calculate session keys + if (session_keys_out) { + memset(session_keys_out, 0, sizeof(ntag424_session_keys_t)); + memcpy(session_keys_out->ti, response.ti, sizeof(response.ti)); + + // SV 1 = [0xA5][0x5A][0x00][0x01] + // [0x00][0x80][RndA[15:14] || + // [ (RndA[13:8] ⊕ RndB[15:10]) ] || + // [RndB[9:0] || RndA[7:0] + + uint8_t sv1[] = { 0xa5, 0x5a, 0x00, 0x01, 0x00, 0x80, rnd_a_clear[0], rnd_a_clear[1], + rnd_a_clear[2] ^rnd_b_clear[0], + rnd_a_clear[3] ^rnd_b_clear[1], + rnd_a_clear[4] ^rnd_b_clear[2], + rnd_a_clear[5] ^rnd_b_clear[3], + rnd_a_clear[6] ^rnd_b_clear[4], + rnd_a_clear[7] ^rnd_b_clear[5], + rnd_b_clear[6], rnd_b_clear[7], rnd_b_clear[8], rnd_b_clear[9], rnd_b_clear[10], + rnd_b_clear[11], rnd_b_clear[12], rnd_b_clear[13], rnd_b_clear[14], rnd_b_clear[15], + rnd_a_clear[8], rnd_a_clear[9], rnd_a_clear[10], + rnd_a_clear[11], rnd_a_clear[12], rnd_a_clear[13], rnd_a_clear[14], rnd_a_clear[15] + }; + + // SV 2 = [0x5A][0xA5][0x00][0x01] + // [0x00][0x80][RndA[15:14] || + // [ (RndA[13:8] ⊕ RndB[15:10]) ] || + // [RndB[9:0] || RndA[7:0] + + uint8_t sv2[] = { 0x5a, 0xa5, 0x00, 0x01, 0x00, 0x80, rnd_a_clear[0], rnd_a_clear[1], + rnd_a_clear[2] ^rnd_b_clear[0], + rnd_a_clear[3] ^rnd_b_clear[1], + rnd_a_clear[4] ^rnd_b_clear[2], + rnd_a_clear[5] ^rnd_b_clear[3], + rnd_a_clear[6] ^rnd_b_clear[4], + rnd_a_clear[7] ^rnd_b_clear[5], + rnd_b_clear[6], rnd_b_clear[7], rnd_b_clear[8], rnd_b_clear[9], rnd_b_clear[10], + rnd_b_clear[11], rnd_b_clear[12], rnd_b_clear[13], rnd_b_clear[14], rnd_b_clear[15], + rnd_a_clear[8], rnd_a_clear[9], rnd_a_clear[10], + rnd_a_clear[11], rnd_a_clear[12], rnd_a_clear[13], rnd_a_clear[14], rnd_a_clear[15] + }; + + mbedtls_aes_cmac_prf_128(key, 16, sv1, sizeof(sv1), session_keys_out->encryption); + mbedtls_aes_cmac_prf_128(key, 16, sv2, sizeof(sv2), session_keys_out->mac); + } + + return PM3_SUCCESS; +} + +#define MAX_WRITE_APDU (200) + +// Write file to card. Only supports plain communications mode. Authentication must be done +// first unless file has free write access. +static int ntag424_write_data(uint8_t fileno, uint32_t offset, uint32_t num_bytes, uint8_t *in, ntag424_communication_mode_t comm_mode, ntag424_session_keys_t *session_keys) { + size_t remainder = 0; + + // Split writes that are too large for one APDU + if (num_bytes > MAX_WRITE_APDU) { + remainder = num_bytes - MAX_WRITE_APDU; + num_bytes = MAX_WRITE_APDU; + } + + uint8_t cmd_header[] = { + fileno, + (uint8_t)offset, + (uint8_t)(offset << 8), + (uint8_t)(offset << 16), // offset + (uint8_t)num_bytes, + (uint8_t)(num_bytes >> 8), + (uint8_t)(num_bytes >> 16) // size + }; + + uint8_t cmd[256] = {0}; + + memcpy(cmd, cmd_header, sizeof(cmd_header)); + memcpy(&cmd[sizeof(cmd_header)], in, num_bytes); + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_WRITE_DATA, + .lc = sizeof(cmd_header) + num_bytes, + .data = cmd, + }; + + int response_length = 8 + 2; // potential MAC and result + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, sizeof(cmd_header), response, &response_length, comm_mode, session_keys, 0x91, 0x00); + if (res != PM3_SUCCESS) { + return res; + } + + if (remainder > 0) { + return ntag424_write_data(fileno, offset + num_bytes, remainder, &in[num_bytes], comm_mode, session_keys); + } + + return PM3_SUCCESS; +} + +// Read file from card. Only supports plain communications mode. Authentication must be done +// first unless file has free read access. +static int ntag424_read_data(uint8_t fileno, uint16_t offset, uint16_t num_bytes, uint8_t *out, ntag424_communication_mode_t comm_mode, ntag424_session_keys_t *session_keys) { + uint8_t cmd_header[] = { + fileno, + (uint8_t)offset, (uint8_t)(offset << 8), (uint8_t)(offset << 16), // offset + (uint8_t)num_bytes, (uint8_t)(num_bytes >> 8), 0x00 + }; + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_READ_DATA, + .lc = sizeof(cmd_header), + .data = cmd_header, + }; + + int response_length = num_bytes + 4 + 2 + 20; // number of bytes to read + mac + result + potential padding + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, sizeof(cmd_header), response, &response_length, comm_mode, session_keys, 0x91, 0x00); + if (res != PM3_SUCCESS) { + return res; + } + + memcpy(out, response, num_bytes); + return PM3_SUCCESS; +} + +static int ntag424_get_version(ntag424_full_version_information_t *version) { + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_GET_VERSION, + }; + + + uint8_t response[256]; + + int response_length = sizeof(ntag424_version_information_t) + 2; + if (ntag424_exchange_apdu(apdu, 0, response, &response_length, COMM_PLAIN, NULL, 0x91, 0xAF) != PM3_SUCCESS) { + return PM3_ESOFT; + } + memcpy(&version->hardware, response, sizeof(ntag424_version_information_t)); + + APDU_t continue_apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_MORE_DATA, + }; + + response_length = sizeof(ntag424_version_information_t) + 2; + if (ntag424_exchange_apdu(continue_apdu, 0, response, &response_length, COMM_PLAIN, NULL, 0x91, 0xAF) != PM3_SUCCESS) { + return PM3_ESOFT; + } + memcpy(&version->software, response, sizeof(ntag424_version_information_t)); + + response_length = sizeof(ntag424_production_information_t) + 2; + if (ntag424_exchange_apdu(continue_apdu, 0, response, &response_length, COMM_PLAIN, NULL, 0x91, 0x00) != PM3_SUCCESS) { + return PM3_ESOFT; + } + memcpy(&version->production, response, sizeof(ntag424_production_information_t)); + + return PM3_SUCCESS; +} + +#define NXP_SIGNATURE_LENGTH 56 +#define NXP_SIGNATURE_ID 0x00 + +static int ntag424_get_signature(uint8_t *signature_out) { + uint8_t signature_id = NXP_SIGNATURE_ID; + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_GET_SIGNATURE, + .lc = 1, + .data = &signature_id, + }; + + int response_length = NXP_SIGNATURE_LENGTH + 2; + // This is a weird one. Datasheet claims this command should result in 91 00, but cards, and the AN12196 + // document shows 91 90 on success. + if (ntag424_exchange_apdu(apdu, 1, signature_out, &response_length, COMM_PLAIN, NULL, 0x91, 0x90) != PM3_SUCCESS) { + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + +static int ntag424_change_key(uint8_t keyno, uint8_t *new_key, uint8_t *old_key, uint8_t version, ntag424_session_keys_t *session_keys) { + // -------- Calculate xor and crc + uint8_t key[16] = {0}; + uint8_t crc[4] = {0}; + if (keyno != 0) { + for (int i = 0; i < 16; i++) { + key[i] = old_key[i] ^ new_key[i]; + } + crc32_ex(new_key, 16, crc); + } else { + memcpy(key, new_key, 16); + } + + // ------- Assemble KeyData command + uint8_t key_cmd_data[32] = {0}; + key_cmd_data[0] = keyno; + memcpy(&key_cmd_data[1], key, 16); + key_cmd_data[17] = version; + int key_data_len; + if (keyno != 0) { + memcpy(&key_cmd_data[18], crc, sizeof(crc)); + key_data_len = sizeof(keyno) + sizeof(key) + sizeof(version) + sizeof(crc); + } else { + key_data_len = sizeof(keyno) + sizeof(key) + sizeof(version); + } + + APDU_t apdu = { + .cla = 0x90, + .ins = NTAG424_CMD_CHANGE_KEY, + .lc = key_data_len, + .data = key_cmd_data + }; + + int response_length = 8 + 2; + uint8_t response[response_length]; + + int res = ntag424_exchange_apdu(apdu, 1, response, &response_length, COMM_FULL, session_keys, 0x91, 0x00); + return res; +} static int CmdHelp(const char *Cmd); @@ -41,123 +858,38 @@ static int CmdHF_ntag424_view(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Filename of dump"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_lit0("v", "verbose", "Verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int fnlen = 0; - char filename[FILE_PATH_SIZE]; - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + char fn[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)fn, FILE_PATH_SIZE, &fnlen); bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); // read dump file uint8_t *dump = NULL; size_t bytes_read = NTAG424_MAX_BYTES; - int res = pm3_load_dump(filename, (void **)&dump, &bytes_read, NTAG424_MAX_BYTES); + int res = pm3_load_dump(fn, (void **)&dump, &bytes_read, NTAG424_MAX_BYTES); if (res != PM3_SUCCESS) { return res; } if (verbose) { - PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); + PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), fn); PrintAndLogEx(INFO, "File size %zu bytes", bytes_read); } + // to be implemented... + PrintAndLogEx(INFO, "not implemented yet"); + PrintAndLogEx(INFO, "Feel free to contribute!"); + free(dump); return PM3_SUCCESS; } -// -// Original from https://github.com/rfidhacking/node-sdm/ -// -typedef struct sdm_picc_s { - uint8_t tag; - uint8_t uid[7]; - uint8_t cnt[3]; - uint32_t cnt_int; -} sdm_picc_t; - -static int sdm_generator(void) { - - // NXP Secure Dynamic Messaging (SDM) with Secure Unique NFC message (SUN) - // Where do they come up with these names? - // - // ref: - // https://www.nxp.com/docs/en/application-note/AN12196.pdf - - // SMD / CMAC - uint8_t iv[16] = {0}; - uint8_t aeskey[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - // uint8_t enc_txt[16] = {0xEF, 0x96, 0x3F, 0xF7, 0x82, 0x86, 0x58, 0xA5, 0x99, 0xF3, 0x04, 0x15, 0x10, 0x67, 0x1E, 0x88}; - uint8_t enc_txt[16] = {0xe6, 0x45, 0xb6, 0x15, 0x4e, 0x8f, 0x32, 0x7d, 0xfb, 0xab, 0x93, 0x4d, 0x4c, 0x66, 0x46, 0x14}; - uint8_t dec_txt[16] = {0}; - - aes_decode(iv, aeskey, enc_txt, dec_txt, sizeof(enc_txt)); - - PrintAndLogEx(INFO, "Ntag424 SUN message validation and encryption"); - PrintAndLogEx(INFO, "Enc text... %s", sprint_hex(enc_txt, sizeof(enc_txt))); - PrintAndLogEx(INFO, "Dec text... %s", sprint_hex(dec_txt, sizeof(dec_txt))); - - sdm_picc_t o = {0}; - o.tag = dec_txt[0]; - memcpy(o.uid, dec_txt + 1, sizeof(o.uid)); - memcpy(o.cnt, dec_txt + 8, sizeof(o.cnt)); - o.cnt_int = MemLeToUint3byte(o.cnt); - - PrintAndLogEx(INFO, "Decypted text"); - PrintAndLogEx(INFO, " Tag........... 0x%02X", o.tag); - PrintAndLogEx(INFO, " UID........... %s", sprint_hex(o.uid, sizeof(o.uid))); - PrintAndLogEx(INFO, " Count bytes... %s", sprint_hex(o.cnt, sizeof(o.cnt))); - PrintAndLogEx(INFO, " Count value... 0x%X ( %u )", o.cnt_int, o.cnt_int); - - // SV2 as per NXP DS465430 (NT4H2421Gx Data sheet) - uint8_t sv2data[16] = {0x3C, 0xC3, 0x00, 0x01, 0x00, 0x80}; - - memcpy(sv2data + 6, o.uid, sizeof(o.uid)); - memcpy(sv2data + 6 + sizeof(o.uid), o.cnt, sizeof(o.cnt)); - - uint8_t cmackey[16] = {0}; - mbedtls_aes_cmac_prf_128(aeskey, 16, sv2data, sizeof(sv2data), cmackey); - - uint8_t zero[16] = {0}; - uint8_t full_cmac[16] = {0}; - mbedtls_aes_cmac_prf_128(cmackey, 16, zero, 0, full_cmac); - - uint8_t cmac[8] = {0}; - for (int i = 0, j = 1; i < 8; ++i, j += 2) { - cmac[i] = full_cmac[j]; - } - - //uint8_t veri[] = {0x94, 0xee, 0xd9, 0xee, 0x65, 0x33, 0x70, 0x86}; - uint8_t veri[] = {0x8b, 0xa1, 0xfb, 0x47, 0x0d, 0x63, 0x39, 0xe8 }; - uint8_t is_ok = (memcmp(cmac, veri, 8) == 0); - - PrintAndLogEx(INFO, "SDM cmac... %s ( %s )", - sprint_hex(cmac, sizeof(cmac)), - (is_ok) ? _GREEN_("ok") : _RED_("fail") - ); - - return PM3_SUCCESS; -} -static int CmdHF_ntag424_sdm(const char *Cmd) { - - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf ntag424 sdm", - "Validate a SDM message", - "hf ntag424 sdm" - ); - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, false); - CLIParserFree(ctx); - - return sdm_generator(); -} - static int CmdHF_ntag424_info(const char *Cmd) { CLIParserContext *ctx; @@ -169,40 +901,583 @@ static int CmdHF_ntag424_info(const char *Cmd) { arg_param_begin, arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); - PrintAndLogEx(INFO, "not implemented yet"); - PrintAndLogEx(INFO, "Feel free to contribute!"); + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to select card"); + DropField(); + return PM3_ERFTRANS; + } + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } - // has hardcoded application and three files. + ntag424_full_version_information_t version = {0}; + if (ntag424_get_version(&version) != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + ntag424_print_full_version_information(&version); + uint8_t signature[NXP_SIGNATURE_LENGTH]; + int res = ntag424_get_signature(signature); + DropField(); - /* - // 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, true, false, response, sizeof(response), &response_n); - if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Tag did not respond to a test APDU (select file command). Aborting..."); - return res; - } - */ + if (res == PM3_SUCCESS) { + PrintAndLogEx(INFO, "--- " _CYAN_("NXP originality signature:")); + desfire_print_signature(version.production.uid, 7, signature, NXP_SIGNATURE_LENGTH); + } + return res; +} + +static int ntag424_cli_get_auth_information(CLIParserContext *ctx, int keyno_index, int key_index, int *keyno, uint8_t *key_out) { + + if (keyno) { + *keyno = arg_get_int(ctx, keyno_index); + } + + int keylen = 16; + uint8_t key[16] = {0}; + + if (CLIParamHexToBuf(arg_get_str(ctx, key_index), key, sizeof(key), &keylen) || (keylen != 16)) { + return PM3_ESOFT; + } + + memcpy(key_out, key, 16); return PM3_SUCCESS; } -//------------------------------------ -// Menu Stuff -//------------------------------------ +static int CmdHF_ntag424_auth(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 auth", + "Authenticate with selected key against NTAG424.", + "hf ntag424 auth --keyno 0 -k 00000000000000000000000000000000"); + + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "keyno", "", "Key number"), + arg_str1("k", "key", "", "Key for authenticate (HEX 16 bytes)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int keyno; + uint8_t key[16] = {0}; + if (ntag424_cli_get_auth_information(ctx, 1, 2, &keyno, key) != PM3_SUCCESS) { + CLIParserFree(ctx); + return PM3_ESOFT; + } + + CLIParserFree(ctx); + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to select card"); + DropField(); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + int res = ntag424_authenticate_ev2_first(keyno, key, NULL); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Auth key %d ( " _RED_("fail") " )", keyno); + } else { + PrintAndLogEx(SUCCESS, "Auth key %d ( " _GREEN_("ok") " )", keyno); + } + + DropField(); + return PM3_SUCCESS; +} + +// Read can only read files with plain communication mode! +static int CmdHF_ntag424_read(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 read", + "Read and print data from file on NTAG424 tag. Will authenticate if key information is provided.", + "hf ntag424 read --fileno 1 --keyno 0 -k 00000000000000000000000000000000 -o 0 -l 32\n" + "hf ntag424 read --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 0 -l 256\n" + "hf ntag424 read --fileno 3 --keyno 3 -k 00000000000000000000000000000000 -o 0 -l 128 -m encrypt"); + + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "fileno", "<1|2|3>", "File number"), + arg_int0(NULL, "keyno", "", "Key number"), + arg_str0("k", "key", "", "Key for authentication (HEX 16 bytes)"), + arg_int0("o", "offset", "", "Offset to read in file (def 0)"), + arg_int1("l", "length", "", "Number of bytes to read"), + arg_str0("m", "cmode", "", "Communication mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fileno = arg_get_int(ctx, 1); + + int keyno; + uint8_t key[16] = {0}; + bool auth = (ntag424_cli_get_auth_information(ctx, 2, 3, &keyno, key) == PM3_SUCCESS); + + int offset = arg_get_int_def(ctx, 4, 0); + int read_length = arg_get_int(ctx, 5); + + ntag424_communication_mode_t comm_mode; + int comm_out = 0; + if (CLIGetOptionList(arg_get_str(ctx, 6), ntag424_communication_mode_options, &comm_out)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); + + comm_mode = comm_out; + + if (comm_mode != COMM_PLAIN && auth == false) { + PrintAndLogEx(ERR, "Only plain communication mode can be used without a key specified"); + return PM3_EINVARG; + } + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + int res = PM3_SUCCESS; + ntag424_session_keys_t session_keys; + if (auth) { + res = ntag424_authenticate_ev2_first(keyno, key, &session_keys); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Auth key %d ( " _RED_("fail") " )", keyno); + DropField(); + return res; + } else { + PrintAndLogEx(SUCCESS, "Auth key %d ( " _GREEN_("ok") " )", keyno); + } + } + + uint8_t data[512] = {0}; + res = ntag424_read_data(fileno, offset, read_length, data, comm_mode, &session_keys); + DropField(); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, " -------- Read file " _YELLOW_("%d") " contents ------------ ", fileno); + print_hex_break(data, read_length, 16); + } + return res; +} + +static int CmdHF_ntag424_write(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 write", + "Write data to file on NTAG424 tag. Will authenticate if key information is provided.", + "hf ntag424 write --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 0 -d 1122334455667788\n" + "hf ntag424 write --fileno 3 --keyno 3 -k 00000000000000000000000000000000 -o 0 -d 1122334455667788 -m encrypt"); + + void *argtable[] = { + arg_param_begin, + arg_u64_1(NULL, "fileno", "<1|2|3>", "File number (def 2)"), + arg_int0(NULL, "keyno", "", "Key number"), + arg_str0("k", "key", "", "Key for authentication (HEX 16 bytes)"), + arg_int0("o", "offset", "", "Offset to write in file (def 0)"), + arg_str1("d", "data", "", "Data to write"), + arg_str0("m", "cmode", "", "Communication mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int fileno = arg_get_int(ctx, 1); + + int keyno = 0; + uint8_t key[16] = {0}; + bool auth = (ntag424_cli_get_auth_information(ctx, 2, 3, &keyno, key) == PM3_SUCCESS); + + uint32_t offset = arg_get_u32_def(ctx, 4, 0); + + uint8_t data[512] = {0}; + int datalen = 512; + CLIGetHexWithReturn(ctx, 5, data, &datalen); + + ntag424_communication_mode_t comm_mode; + int comm_out = 0; + if (CLIGetOptionList(arg_get_str(ctx, 6), ntag424_communication_mode_options, &comm_out)) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); + + comm_mode = comm_out; + + if (comm_mode != COMM_PLAIN && auth == false) { + PrintAndLogEx(ERR, "Only plain communication mode can be used without a key specified"); + return PM3_EINVARG; + } + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + int res = PM3_SUCCESS; + ntag424_session_keys_t session_keys = {0}; + if (auth) { + res = ntag424_authenticate_ev2_first(keyno, key, &session_keys); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Auth key %d ( " _RED_("fail") " )", keyno); + DropField(); + return res; + } else { + PrintAndLogEx(SUCCESS, "Auth key %d ( " _GREEN_("ok") " )", keyno); + } + } + + res = ntag424_write_data(fileno, offset, (uint32_t)datalen, data, comm_mode, &session_keys); + DropField(); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Wrote " _YELLOW_("%d") " bytes ( " _GREEN_("ok") " )", datalen); + } else { + PrintAndLogEx(ERR, "Wrote " _YELLOW_("%d") " bytes ( " _RED_("fail") " )", datalen); + } + return res; +} + +static int CmdHF_ntag424_getfilesettings(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 getfs", + "Read and print file settings for file", + "hf ntag424 getfs --fileno 2"); + + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "fileno", "", "File number"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fileno = arg_get_int(ctx, 1); + + CLIParserFree(ctx); + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + ntag424_file_settings_t settings = {0}; + int res = ntag424_get_file_settings(fileno, &settings); + DropField(); + if (res == PM3_SUCCESS) { + ntag424_print_file_settings(fileno, &settings); + } + return res; +} + +static int CmdHF_ntag424_changefilesettings(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 changefs", + "Updates file settings for file, must be authenticated.\n" + "This is a short explanation of the settings. See AN12196 for more information:\n" + "options: byte with bit flags\n" + " Bit: Setting:\n" + " 6 Enable SDM and mirroring\n\n" + + "access: two byte access rights.\n" + "Each nibble is a key number, or E for free access.\n" + "Order is key for readwrite, change, read and write\n\n" + + "sdmoptions: byte with bit flags\n" + " Bit: Setting:\n" + " 0 ASCII encoding\n" + " 4 SDMEncFileData\n" + " 5 SDMReadCtrLimit\n" + " 6 SDMReadCtr\n" + " 7 SDMOptionsUID\n\n" + + "sdmaccess: two byte access rights.\n" + "Each nibble is a key, or E for plain mirror and F for no mirroring\n" + "Order is Reserved, SDMCtrRet, SDMMetaRead and SDMFileRead\n\n" + + "sdm_data: Three bytes of data used to control SDM settings. Can be specified multiple times.\n" + "Data means different things depending on settings.\n\n" + + "Note: Not all of these settings will be written. It depends on the option byte, and the keys set. See AN12196 for more information.\n" + "You must also start with sdmdata1, then sdmdata2, up to the number of sdm_data you want to write", + + "hf ntag424 changefs --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 40 -a 00E0 -s C1 -c F000 --data1 000020 --data2 000043 --data3 000043" + ); + + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "fileno", "", "File number"), + arg_int1(NULL, "keyno", "", "Key number"), + arg_str1("k", "key", "", "Key for authentication (HEX 16 bytes)"), + arg_str0("o", "options", "", "File options byte (HEX 1 byte)"), + arg_str0("a", "access", "", "File access settings (HEX 2 bytes)"), + arg_str0("s", "sdmoptions", "", "SDM options (HEX 1 byte)"), + arg_str0("c", "sdmaccess", "", "SDM access settings (HEX 2 bytes)"), + arg_str0(NULL, "data1", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data2", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data3", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data4", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data5", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data6", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data7", "", "SDM data (HEX 3 bytes)"), + arg_str0(NULL, "data8", "", "SDM data (HEX 3 bytes)"), + // Sorry, couldn't figure out how to work with arg_strn... + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fileno = arg_get_int(ctx, 1); + + int keyno; + uint8_t key[16] = {0}; + + uint8_t has_options = 0; + uint8_t options[1]; + uint8_t has_access = 0; + uint8_t access[2]; + uint8_t has_sdmoptions = 0; + uint8_t sdmoptions[1]; + uint8_t has_sdmaccess = 0; + uint8_t sdmaccess[2]; + uint8_t num_sdm_data = 0; + uint8_t sdm_data[8][3]; + + if (ntag424_cli_get_auth_information(ctx, 2, 3, &keyno, key) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not get key settings"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int len = 1; + if (arg_get_str(ctx, 4)->count == 1) { + has_options = 1; + CLIGetHexWithReturn(ctx, 4, options, &len); + if (len != 1) { + PrintAndLogEx(ERR, "Options must be 1 byte, got ( %d )", len); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + len = 2; + if (arg_get_str(ctx, 5)->count == 1) { + has_access = 1; + CLIGetHexWithReturn(ctx, 5, access, &len); + if (len != 2) { + PrintAndLogEx(ERR, "Access must be 2 bytes, got ( %d )", len); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + len = 1; + if (arg_get_str(ctx, 6)->count == 1) { + has_sdmoptions = 1; + CLIGetHexWithReturn(ctx, 6, sdmoptions, &len); + if (len != 1) { + PrintAndLogEx(ERR, "SDM Options must be 1 byte, got ( %d )", len); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + len = 2; + if (arg_get_str(ctx, 7)->count == 1) { + has_sdmaccess = 1; + CLIGetHexWithReturn(ctx, 7, sdmaccess, &len); + if (len != 2) { + PrintAndLogEx(ERR, "SDM Access must be 2 bytes, got ( %d )", len); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + for (int i = 0; i < 8; i++) { + if (arg_get_str(ctx, 8 + i)->count == 1) { + len = 3; + num_sdm_data++; + CLIGetHexWithReturn(ctx, 8 + i, sdm_data[i], &len); + if (len != 3) { + PrintAndLogEx(ERR, "sdmdata must be 3 bytes, got ( %d )", len); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } else { + break; + } + } + + CLIParserFree(ctx); + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + ntag424_file_settings_t settings = {0}; + if (ntag424_get_file_settings(fileno, &settings) != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + int res = PM3_SUCCESS; + ntag424_session_keys_t session = {0}; + res = ntag424_authenticate_ev2_first(keyno, key, &session); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Auth key %d ( " _RED_("fail") " )", keyno); + DropField(); + return res; + } else { + PrintAndLogEx(SUCCESS, "Auth key %d ( " _GREEN_("ok") " )", keyno); + } + + if (has_options) { + settings.options = options[0]; + } + + if (has_access) { + memcpy(settings.access, access, 2); + } + + if (has_sdmoptions) { + settings.optional_sdm_settings.sdm_options = sdmoptions[0]; + } + + if (has_sdmaccess) { + memcpy(settings.optional_sdm_settings.sdm_access, sdmaccess, 2); + } + + for (int i = 0; i < num_sdm_data; i++) { + settings.optional_sdm_settings.sdm_data[i][2] = sdm_data[i][0]; + settings.optional_sdm_settings.sdm_data[i][1] = sdm_data[i][1]; + settings.optional_sdm_settings.sdm_data[i][0] = sdm_data[i][2]; + } + + res = ntag424_write_file_settings(fileno, &settings, &session); + DropField(); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Write settings ( " _GREEN_("ok") " )"); + ntag424_print_file_settings(fileno, &settings); + } else { + PrintAndLogEx(ERR, "Write settings (" _RED_("fail") " )"); + } + return res; +} + +static int CmdHF_ntag424_changekey(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf ntag424 changekey", + "Change a key.\n" + "Authentication key must currently be different to the one we want to change.\n", + "hf ntag424 changekey --keyno 1 --oldkey 00000000000000000000000000000000 --newkey 11111111111111111111111111111111 --key0 00000000000000000000000000000000 --kv 1\n" + "hf ntag424 changekey --keyno 0 --newkey 11111111111111111111111111111111 --key0 00000000000000000000000000000000 --kv 1\n" + ); + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "keyno", "", "Key number to change"), + arg_str0(NULL, "oldkey", "", "Old key (only needed when changing key 1-4, HEX 16 bytes)"), + arg_str1(NULL, "newkey", "", "New key (HEX 16 bytes)"), + arg_str1(NULL, "key0", "", "Authentication key (must be key 0, HEX 16 bytes)"), + arg_int1(NULL, "kv", "", "New key version number"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + uint8_t version = arg_get_int(ctx, 6); + int keyno = arg_get_int(ctx, 1); + + uint8_t oldkey[16] = {0}; + if (keyno != 0) { + if (ntag424_cli_get_auth_information(ctx, 0, 2, NULL, oldkey) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not get keyno or old key"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + uint8_t newkey[16] = {0}; + if (ntag424_cli_get_auth_information(ctx, 0, 3, NULL, newkey) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not get new key"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t authkey[16] = {0}; + if (ntag424_cli_get_auth_information(ctx, 0, 4, NULL, authkey) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not get authentication key"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); + + if (SelectCard14443A_4(false, true, NULL) != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Failed to select card"); + return PM3_ERFTRANS; + } + + if (ntag424_select_application() != PM3_SUCCESS) { + DropField(); + return PM3_ESOFT; + } + + int res = PM3_SUCCESS; + ntag424_session_keys_t session = {0}; + res = ntag424_authenticate_ev2_first(0, authkey, &session); + if (res != PM3_SUCCESS) { + DropField(); + PrintAndLogEx(ERR, "Auth ( " _RED_("fail") " )"); + return PM3_ESOFT; + } else { + PrintAndLogEx(SUCCESS, "Auth ( " _GREEN_("ok") " )"); + } + + res = ntag424_change_key(keyno, newkey, oldkey, version, &session); + DropField(); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Change key %d ( " _GREEN_("ok") " )", keyno); + } else { + PrintAndLogEx(ERR, "Change key %d ( "_RED_("fail") " )", keyno); + } + + return res; +} + static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, - {"info", CmdHF_ntag424_info, IfPm3Iso14443a, "Tag information"}, -// {"ndefread", CmdHF_ntag424_sdm, IfPm3Iso14443a, "Prints NDEF records from card"}, - {"sdm", CmdHF_ntag424_sdm, IfPm3Iso14443a, "Prints NDEF records from card"}, - {"view", CmdHF_ntag424_view, AlwaysAvailable, "Display content from tag dump file"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, + {"info", CmdHF_ntag424_info, IfPm3Iso14443a, "Tag information"}, + {"view", CmdHF_ntag424_view, AlwaysAvailable, "Display content from tag dump file"}, + {"auth", CmdHF_ntag424_auth, IfPm3Iso14443a, "Test authentication with key"}, + {"read", CmdHF_ntag424_read, IfPm3Iso14443a, "Read file"}, + {"write", CmdHF_ntag424_write, IfPm3Iso14443a, "Write file"}, + {"getfs", CmdHF_ntag424_getfilesettings, IfPm3Iso14443a, "Get file settings"}, + {"changefs", CmdHF_ntag424_changefilesettings, IfPm3Iso14443a, "Change file settings"}, + {"changekey", CmdHF_ntag424_changekey, IfPm3Iso14443a, "Change key"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index 896157be4..4ab7c1b15 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -112,7 +112,7 @@ static int CmdHfSeosInfo(const char *Cmd) { } static int CmdHfSeosList(const char *Cmd) { - return CmdTraceListAlias(Cmd, "hf seos", "7816"); + return CmdTraceListAlias(Cmd, "hf seos", "seos -c"); } static command_t CommandTable[] = { diff --git a/client/src/cmdhfst25ta.c b/client/src/cmdhfst25ta.c index 86dfe0584..9a2f84e7c 100644 --- a/client/src/cmdhfst25ta.c +++ b/client/src/cmdhfst25ta.c @@ -404,10 +404,15 @@ int CmdHFST25TANdefRead(const char *Cmd) { return PM3_ESOFT; } - if (fnlen != 0) { - saveFile(filename, ".bin", response + 2, resplen - 4); - } NDEFRecordsDecodeAndPrint(response + 2, resplen - 4, verbose); + + // get total NDEF length before save. If fails, we save it all + size_t n = 0; + if (NDEFGetTotalLength(response, resplen, &n) != PM3_SUCCESS) + n = resplen; + + pm3_save_dump(filename, response + 2, n, jsfNDEF); + return PM3_SUCCESS; } diff --git a/client/src/cmdhftexkom.c b/client/src/cmdhftexkom.c index 2b54480ca..0fa08edc7 100644 --- a/client/src/cmdhftexkom.c +++ b/client/src/cmdhftexkom.c @@ -253,7 +253,7 @@ static bool TexcomTK13Decode(uint32_t *implengths, uint32_t implengthslen, char return false; if (verbose) - PrintAndLogEx(INFO, "raw bit string [%zu]: %s", strlen(bitstring), bitstring); + PrintAndLogEx(INFO, "raw bit string [%3zu]... %s", strlen(bitstring), bitstring); // add trailing impulse (some tags just ignore it) if (strlen(bitstring) % 2 != 0) { @@ -280,7 +280,7 @@ static bool TexcomTK13Decode(uint32_t *implengths, uint32_t implengthslen, char return false; if (verbose) - PrintAndLogEx(INFO, "bit string [%zu]: %s", strlen(cbitstring), cbitstring); + PrintAndLogEx(INFO, "bit string [%3zu].... %s", strlen(cbitstring), cbitstring); return ((strlen(cbitstring) == 64) && (strncmp(cbitstring, "1111111111111111", 16) == 0)); } @@ -324,8 +324,8 @@ static bool TexcomTK15Decode(uint32_t *implengths, uint32_t implengthslen, char return false; if (verbose) { - PrintAndLogEx(INFO, "raw bit string [%zu]: %s", strlen(bitstring), bitstring); - PrintAndLogEx(INFO, "bit string [%zu]: %s", strlen(cbitstring), cbitstring); + PrintAndLogEx(INFO, "raw bit string [%3zu]... %s", strlen(bitstring), bitstring); + PrintAndLogEx(INFO, "bit string [%3zu]....... %s", strlen(cbitstring), cbitstring); } return ((strlen(cbitstring) == 64) && (strncmp(cbitstring, "1111111111111111", 16) == 0)); @@ -420,7 +420,7 @@ static bool TexcomGeneralDecode(uint32_t *implengths, uint32_t implengthslen, ch } } if (verbose) - PrintAndLogEx(INFO, "General raw bit string [%zu]: %s", strlen(bitstring), bitstring); + PrintAndLogEx(INFO, "General raw bit string [%zu]... %s", strlen(bitstring), bitstring); return (!biterror && strlen(bitstring) > 0); } @@ -429,7 +429,7 @@ static void TexcomReverseCode(const uint8_t *code, int length, uint8_t *reverse_ for (int i = 0; i < length; i++) { reverse_code[i] = code[(length - 1) - i]; } -}; +} static int texkom_get_type(texkom_card_select_t *card, bool verbose) { @@ -493,7 +493,7 @@ static int texkom_get_type(texkom_card_select_t *card, bool verbose) { noiselvl = TEXKOM_NOISE_THRESHOLD; } - uint32_t implengths[256] = {}; + uint32_t implengths[256] = { 0 }; uint32_t implengthslen = 0; uint32_t impulseindx = 0; uint32_t impulsecnt = 0; @@ -588,30 +588,38 @@ int read_texkom_uid(bool loop, bool verbose) { } bool crc = (TexcomTK13CRC(&card.tcode[3]) == card.tcode[7]); + bool printed = false; if (card.tcode[2] == 0x63) { - PrintAndLogEx(INFO, "TYPE..... TK13"); + PrintAndLogEx(INFO, "TYPE..... " _YELLOW_("TK13")); PrintAndLogEx(INFO, "UID...... " _GREEN_("%s"), sprint_hex(&card.tcode[3], 4)); if (verbose) { PrintAndLogEx(INFO, "CRC...... %s", (crc) ? _GREEN_("ok") : _RED_("fail")); } + printed = true; } else if (card.tcode[2] == 0xCA) { - PrintAndLogEx(INFO, "TYPE..... TK17"); + PrintAndLogEx(INFO, "TYPE..... " _YELLOW_("TK17")); PrintAndLogEx(INFO, "UID...... " _GREEN_("%s"), sprint_hex(&card.tcode[3], 4)); if (verbose) { PrintAndLogEx(INFO, "CRC...... %s", (crc) ? _GREEN_("ok") : _RED_("fail")); } + printed = true; } else if (card.tcode[2] == 0xFF && card.tcode[3] == 0xFF) { - PrintAndLogEx(INFO, "TYPE..... MMBIT"); + PrintAndLogEx(INFO, "TYPE..... " _YELLOW_("MMBIT")); PrintAndLogEx(INFO, "UID...... " _GREEN_("%s"), sprint_hex(&card.tcode[4], 3)); - crc = (MMBITCRC(&card.tcode[4]) == card.tcode[7] >> 4); if (verbose) { + crc = (MMBITCRC(&card.tcode[4]) == card.tcode[7] >> 4); PrintAndLogEx(INFO, "CRC...... %s", (crc) ? _GREEN_("ok") : _RED_("fail")); } + printed = true; } + if (verbose) { - PrintAndLogEx(INFO, "Raw... %s", sprint_hex(card.tcode, 8)); - PrintAndLogEx(INFO, "Raw Reversed... %s", sprint_hex(card.rtcode, 8)); + PrintAndLogEx(INFO, "Raw....... " _YELLOW_("%s"), sprint_hex(card.tcode, 8)); + PrintAndLogEx(INFO, "Raw rev... " _YELLOW_("%s"), sprint_hex(card.rtcode, 8)); + } + if (printed && loop) { + PrintAndLogEx(NORMAL, ""); } } @@ -630,7 +638,7 @@ static int CmdHFTexkomReader(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("1", NULL, "Use data from Graphbuffer"), + arg_lit0("1", NULL, "Use data from Graphbuffer (offline mode)"), arg_lit0("v", "verbose", "Verbose scan and output"), arg_lit0("@", NULL, "optional - continuous reader mode"), arg_param_end @@ -699,7 +707,7 @@ static int CmdHFTexkomReader(const char *Cmd) { //PrintAndLogEx(WARNING, "--- indx: %d, len: %d, max: %d, noise: %d", sindx, slen, maxlvl, noiselvl); - uint32_t implengths[256] = {}; + uint32_t implengths[256] = { 0 }; uint32_t implengthslen = 0; uint32_t impulseindx = 0; uint32_t impulsecnt = 0; @@ -753,82 +761,84 @@ static int CmdHFTexkomReader(const char *Cmd) { TexcomReverseCode(tcode, 8, rtcode); if (verbose) { - PrintAndLogEx(INFO, "Hex code: %s", sprint_hex(tcode, 8)); - PrintAndLogEx(INFO, "Hex code reversed: %s", sprint_hex(rtcode, 8)); + PrintAndLogEx(INFO, "Hex code............ %s", sprint_hex(tcode, 8)); + PrintAndLogEx(INFO, "Hex code rev........ %s", sprint_hex(rtcode, 8)); } if (tcode[0] == 0xff && tcode[1] == 0xff) { // decoding code if (verbose == false) { - PrintAndLogEx(INFO, "Texkom: %s", sprint_hex(tcode, 8)); - PrintAndLogEx(INFO, "Texkom duplicator: %s", sprint_hex(rtcode, 8)); + PrintAndLogEx(SUCCESS, "Texkom.............. %s", sprint_hex(tcode, 8)); + PrintAndLogEx(SUCCESS, "Texkom duplicator... %s", sprint_hex(rtcode, 8)); } if (codefound == TexkomModTK13) - PrintAndLogEx(INFO, "modulation: TK13"); + PrintAndLogEx(SUCCESS, "Modulation.......... " _YELLOW_("TK13")); else if (codefound == TexkomModTK15) - PrintAndLogEx(INFO, "modulation: TK15"); + PrintAndLogEx(SUCCESS, "Modulation.......... " _YELLOW_("TK15")); else if (codefound == TexkomModTK17) - PrintAndLogEx(INFO, "modulation: TK17"); + PrintAndLogEx(SUCCESS, "Modulation.......... " _YELLOW_("TK17")); else - PrintAndLogEx(INFO, "modulation: unknown"); + PrintAndLogEx(INFO, "Modulation.......... " _YELLOW_("unknown")); if (tcode[2] == 0x63) { // TK13 and TK15. differs only by timings. TK15 has impulse 0 and 1 lengths very close to each other. if (codefound == TexkomModTK13) - PrintAndLogEx(INFO, "type : TK13"); + PrintAndLogEx(SUCCESS, "Type................ " _YELLOW_("TK13")); else if (codefound == TexkomModTK15) - PrintAndLogEx(INFO, "type : TK15"); + PrintAndLogEx(SUCCESS, "Type................ " _YELLOW_("TK15")); else - PrintAndLogEx(WARNING, " mod type: WRONG"); - - PrintAndLogEx(INFO, "uid : %s", sprint_hex(&tcode[3], 4)); + PrintAndLogEx(WARNING, "Type................ " _RED_("fail")); + PrintAndLogEx(SUCCESS, "UID................. " _YELLOW_("%s"), sprint_hex(&tcode[3], 4)); + PrintAndLogEx(INFO, "CRC................ " NOLF); if (TexcomTK13CRC(&tcode[3]) == tcode[7]) - PrintAndLogEx(INFO, "crc : OK"); + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); else - PrintAndLogEx(WARNING, "crc : WRONG"); + PrintAndLogEx(NORMAL, "( " _RED_("fail") " )"); } else if (tcode[2] == 0xFF && tcode[3] == 0xFF) { // MMBIT if (codefound != TexkomModTK13 && codefound != TexkomModTK15) { - PrintAndLogEx(WARNING, " mod type: WRONG"); + PrintAndLogEx(WARNING, "Mod type............ " _RED_("fail")); } - PrintAndLogEx(INFO, "type : MMBIT"); - PrintAndLogEx(INFO, "uid : %s", sprint_hex(&tcode[4], 3)); - + PrintAndLogEx(SUCCESS, "Type................ " _YELLOW_("MMBIT")); + PrintAndLogEx(SUCCESS, "UID................. " _YELLOW_("%s"), sprint_hex(&tcode[4], 3)); + PrintAndLogEx(INFO, "CRC................ " NOLF); if (MMBITCRC(&tcode[4]) == tcode[7] >> 4) - PrintAndLogEx(INFO, "crc : OK"); + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); else - PrintAndLogEx(WARNING, "crc : WRONG"); + PrintAndLogEx(NORMAL, "( " _RED_("fail") " )"); + + } else if (tcode[2] == 0xCA) { // TK17 if (codefound != TexkomModTK17) { - PrintAndLogEx(WARNING, " mod type: WRONG"); + PrintAndLogEx(WARNING, "Mod type............ " _RED_("fail")); } - PrintAndLogEx(INFO, "type : TK17"); - PrintAndLogEx(INFO, "uid : %s", sprint_hex(&tcode[3], 4)); - + PrintAndLogEx(SUCCESS, "Type............... " _YELLOW_("TK17")); + PrintAndLogEx(SUCCESS, "UID................ " _YELLOW_("%s"), sprint_hex(&tcode[3], 4)); + PrintAndLogEx(INFO, "CRC................ " NOLF); if (TexcomTK17CRC(&tcode[3]) == tcode[7]) - PrintAndLogEx(INFO, "crc : OK"); + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); else - PrintAndLogEx(WARNING, "crc : WRONG"); + PrintAndLogEx(NORMAL, "( " _RED_("fail") " )"); } else { - PrintAndLogEx(INFO, "type : unknown"); - PrintAndLogEx(INFO, "uid : %s (maybe)", sprint_hex(&tcode[3], 4)); + PrintAndLogEx(INFO, "Type............... unknown"); + PrintAndLogEx(INFO, "UID................ %s (maybe)", sprint_hex(&tcode[3], 4)); } } else { - PrintAndLogEx(ERR, "Code have no preamble FFFF: %s", sprint_hex(tcode, 8)); + PrintAndLogEx(ERR, "Code have no preamble FFFF... %s", sprint_hex(tcode, 8)); } } else { if (strlen(genbitstring) > 0) - PrintAndLogEx(INFO, "General decoding bitstring: %s", genbitstring); + PrintAndLogEx(INFO, "General decoding bitstring... %s", genbitstring); if (strlen(bitstring) > 0) - PrintAndLogEx(INFO, "last raw bit string [%zu]: %s", strlen(bitstring), bitstring); + PrintAndLogEx(INFO, "last raw bit string [%zu].... %s", strlen(bitstring), bitstring); if (strlen(cbitstring) > 0) - PrintAndLogEx(INFO, "last bit string [%zu]: %s", strlen(cbitstring), cbitstring); + PrintAndLogEx(INFO, "last bit string [%zu]........ %s", strlen(cbitstring), cbitstring); PrintAndLogEx(ERR, "Texkom card is not found"); } @@ -840,10 +850,10 @@ static int CmdHFTexkomSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf texkom sim", "Simulate a texkom tag", - "hf texkom sim \r\n" - "hf texkom sim --raw FFFF638C7DC45553 -> simulate TK13 tag with id 8C7DC455\r\n" - "hf texkom sim --tk17 --raw FFFFCA17F31EC512 -> simulate TK17 tag with id 17F31EC5\r\n" - "hf texkom sim --id 8C7DC455 -> simulate TK13 tag with id 8C7DC455\r\n" + "hf texkom sim \n" + "hf texkom sim --raw FFFF638C7DC45553 -> simulate TK13 tag with id 8C7DC455\n" + "hf texkom sim --tk17 --raw FFFFCA17F31EC512 -> simulate TK17 tag with id 17F31EC5\n" + "hf texkom sim --id 8C7DC455 -> simulate TK13 tag with id 8C7DC455\n" "hf texkom sim --id 8C7DC455 --tk17 -> simulate TK17 tag with id 17F31EC5"); void *argtable[] = { @@ -862,7 +872,7 @@ static int CmdHFTexkomSim(const char *Cmd) { uint8_t data[8]; uint8_t modulation; uint32_t timeout; - } PACKED payload = {}; + } PACKED payload = {0}; bool verbose = arg_get_lit(ctx, 1); payload.modulation = 0; // tk-13 diff --git a/client/src/cmdhfthinfilm.c b/client/src/cmdhfthinfilm.c index 92c82f3f3..40e43519e 100644 --- a/client/src/cmdhfthinfilm.c +++ b/client/src/cmdhfthinfilm.c @@ -43,7 +43,7 @@ static int print_barcode(uint8_t *barcode, const size_t barcode_len, bool verbos if (verbose) { PrintAndLogEx(SUCCESS, " Data format : "_YELLOW_("%02X"), barcode[1]); if (barcode_len > 2) { - uint8_t b1, b2; + uint8_t b1 = 0, b2 = 0; compute_crc(CRC_14443_A, barcode, barcode_len - 2, &b1, &b2); bool isok = (barcode[barcode_len - 1] == b1 && barcode[barcode_len - 2] == b2); @@ -173,7 +173,7 @@ int CmdHfThinFilmSim(const char *Cmd) { CLIParserFree(ctx); if (addcrc && data_len <= 510) { - uint8_t b1, b2; + uint8_t b1 = 0, b2 = 0; compute_crc(CRC_14443_A, data, data_len, &b1, &b2); data[data_len++] = b2; data[data_len++] = b1; @@ -182,7 +182,7 @@ int CmdHfThinFilmSim(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_HF_THINFILM_SIMULATE, (uint8_t *)&data, data_len); PacketResponseNG resp; - PrintAndLogEx(SUCCESS, "press pm3-button to abort simulation"); + PrintAndLogEx(SUCCESS, "press " _GREEN_("pm3 button") " to abort simulation"); int ret; while (!(ret = kbd_enter_pressed())) { diff --git a/client/src/cmdhftopaz.c b/client/src/cmdhftopaz.c index 6311b50ed..5b8ab2677 100644 --- a/client/src/cmdhftopaz.c +++ b/client/src/cmdhftopaz.c @@ -224,6 +224,8 @@ static int topaz_write_erase8_block(uint8_t blockno, uint8_t *block_data) { uint16_t resp_len = 11; uint8_t response[11] = {0}; + // + if (topaz_send_cmd(wr8_cmd, sizeof(wr8_cmd), response, &resp_len, true) == PM3_ETIMEOUT) { topaz_switch_off_field(); return PM3_ESOFT; // WriteErase 8bytes failed @@ -243,6 +245,61 @@ static int topaz_write_erase8_block(uint8_t blockno, uint8_t *block_data) { return PM3_ESOFT; } +// write a block (8 Bytes) of a selected Topaz tag. +static int topaz_write_nonerase8_block(uint8_t blockno, uint8_t *block_data) { + + uint8_t atqa[2] = {0}; + uint8_t rid_response[8] = {0}; + int res = topaz_select(atqa, sizeof(atqa), rid_response, sizeof(rid_response), true); + if (res != PM3_SUCCESS) { + return res; + } + + if (atqa[1] != 0x0c && atqa[0] != 0x00) { + return res; + } + + uint8_t *uid_echo = &rid_response[2]; + uint8_t rall_response[124] = {0}; + + res = topaz_rall(uid_echo, rall_response); + if (res == PM3_ESOFT) { + return res; + } + + // ADD + // 7 6 5 4 3 2 1 0 + // b b b --- Byte 0 - 7 + // B B B B --------- BLOCK + // r ----------------- 0 + // + + uint8_t wr8_cmd[] = {TOPAZ_WRITE_NE8, blockno, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + memcpy(wr8_cmd + 10, uid_echo, 4); + memcpy(wr8_cmd + 2, block_data, 8); + + uint16_t resp_len = 11; + uint8_t response[11] = {0}; + + // + if (topaz_send_cmd(wr8_cmd, sizeof(wr8_cmd), response, &resp_len, true) == PM3_ETIMEOUT) { + topaz_switch_off_field(); + return PM3_ESOFT; + } + + if (resp_len != 11) { + return PM3_EFAILED; + } + + if (blockno != response[0]) { + return PM3_EFAILED; + } + + if (memcmp(block_data, response + 1, 8) == 0) { + return PM3_SUCCESS; + } + return PM3_ESOFT; +} // search for the lock area descriptor for the lockable area including byteno static dynamic_lock_area_t *get_dynamic_lock_area(uint16_t byteno) { @@ -402,6 +459,12 @@ static int topaz_print_CC(uint8_t *data) { return PM3_SUCCESS; } +static void topaz_print_hdr(uint8_t blockno) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, " # | block " _GREEN_("0x%02X") " | ascii", blockno); + PrintAndLogEx(INFO, "----+-------------------------+---------"); +} + // return type, length and value of a TLV, starting at memory position *TLV_ptr static void get_TLV(uint8_t **TLV_ptr, uint8_t *TLV_type, uint16_t *TLV_length, uint8_t **TLV_value) { *TLV_length = 0; @@ -749,7 +812,7 @@ static int CmdHFTopazRaw(const char *Cmd) { } static int CmdHFTopazList(const char *Cmd) { - return CmdTraceListAlias(Cmd, "hf topaz", "topaz"); + return CmdTraceListAlias(Cmd, "hf topaz", "topaz -c"); } static int CmdHFTopazSniff(const char *Cmd) { @@ -775,13 +838,14 @@ static int CmdHFTopazDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf topaz dump", - "Dump TOPAZ tag to binary file\n" + "Dump TOPAZ tag to file (bin/json)\n" "If no given, UID will be used as filename", "hf topaz dump\n"); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump"), + arg_str0("f", "file", "", "Specify a filename for dump file"), + arg_lit0(NULL, "ns", "no save to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -789,6 +853,8 @@ static int CmdHFTopazDump(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool nosave = arg_get_lit(ctx, 2); + CLIParserFree(ctx); int status = readTopazUid(false, false); @@ -811,9 +877,18 @@ static int CmdHFTopazDump(const char *Cmd) { ); } - PrintAndLogEx(INFO, "-------------------------------------------------------------"); topaz_switch_off_field(); + // Skip saving card data to file + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + if (set_dynamic) { + free(topaz_tag.dynamic_memory); + } + return PM3_SUCCESS; + } + + // user supplied filename? if (fnlen < 1) { PrintAndLogEx(INFO, "Using UID as filename"); @@ -822,9 +897,9 @@ static int CmdHFTopazDump(const char *Cmd) { } if (topaz_tag.size) - pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t) + topaz_tag.size, jsfTopaz, TOPAZ_BLOCK_SIZE); + pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t) + topaz_tag.size, jsfTopaz); else - pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t), jsfTopaz, TOPAZ_BLOCK_SIZE); + pm3_save_dump(filename, (uint8_t *)&topaz_tag, sizeof(topaz_tag_t), jsfTopaz); if (set_dynamic) { free(topaz_tag.dynamic_memory); @@ -841,7 +916,7 @@ static int CmdHFTopazView(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "filename of dump (bin/eml/json)"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -888,13 +963,13 @@ static int CmdHFTopazRdBl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf topaz rdbl", - "Read a block", - "hf topaz rdbl -b 7\n" + "Read Topaz block", + "hf topaz rdbl --blk 7\n" ); void *argtable[] = { arg_param_begin, - arg_int1("b", "block", "", "Block number to write"), + arg_int1(NULL, "blk", "", "Block number"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -910,7 +985,11 @@ static int CmdHFTopazRdBl(const char *Cmd) { uint8_t data[8] = {0}; int res = topaz_read_block(blockno, data); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Block: %0d (0x%02X) [ %s]", blockno, blockno, sprint_hex(data, sizeof(data))); + + topaz_print_hdr(blockno); + + PrintAndLogEx(INFO, " %2d | %s", blockno, sprint_hex_ascii(data, sizeof(data))); + PrintAndLogEx(NORMAL, ""); } topaz_switch_off_field(); @@ -922,13 +1001,13 @@ static int CmdHFTopazWrBl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf topaz wrbl", - "Write a block", - "hf topaz wrbl -b 7 -d 1122334455667788\n" + "Write Topaz block with 8 hex bytes of data", + "hf topaz wrbl --blk 7 -d 1122334455667788\n" ); void *argtable[] = { arg_param_begin, - arg_int1("b", "block", "", "Block number to write"), + arg_int1(NULL, "blk", "", "Block number"), arg_str1("d", "data", "", "Block data (8 hex bytes)"), arg_param_end }; @@ -954,14 +1033,23 @@ static int CmdHFTopazWrBl(const char *Cmd) { PrintAndLogEx(INFO, "Block: %0d (0x%02X) [ %s]", blockno, blockno, sprint_hex(data, dlen)); - // send write Block - int res = topaz_write_erase8_block(blockno, data); + int res; + if (blockno != 13 && blockno != 14) { + // send write/erase block + res = topaz_write_erase8_block(blockno, data); + } else { + // send write/non erase block + res = topaz_write_nonerase8_block(blockno, data); + } if (res == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); + PrintAndLogEx(HINT, "try `" _YELLOW_("hf topaz rdbl --blk %u") "` to verify", blockno); + } else { PrintAndLogEx(WARNING, "Write ( " _RED_("fail") " )"); } + PrintAndLogEx(NORMAL, ""); topaz_switch_off_field(); return res; @@ -970,18 +1058,24 @@ static int CmdHFTopazWrBl(const char *Cmd) { static int CmdHelp(const char *Cmd); static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"dump", CmdHFTopazDump, IfPm3Iso14443a, "Dump TOPAZ family tag to file"}, - {"list", CmdHFTopazList, AlwaysAvailable, "List Topaz history"}, - {"info", CmdHFTopazInfo, IfPm3Iso14443a, "Tag information"}, - {"reader", CmdHFTopazReader, IfPm3Iso14443a, "Act like a Topaz reader"}, - {"sim", CmdHFTopazSim, IfPm3Iso14443a, "Simulate Topaz tag"}, - {"sniff", CmdHFTopazSniff, IfPm3Iso14443a, "Sniff Topaz reader-tag communication"}, - {"raw", CmdHFTopazRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, - {"rdbl", CmdHFTopazRdBl, IfPm3Iso14443a, "Read block"}, - {"view", CmdHFTopazView, AlwaysAvailable, "Display content from tag dump file"}, - {"wrbl", CmdHFTopazWrBl, IfPm3Iso14443a, "Write block"}, - {NULL, NULL, 0, NULL} + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdHFTopazList, AlwaysAvailable, "List Topaz history"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "------------------- " _CYAN_("operations") " ---------------------"}, + {"dump", CmdHFTopazDump, IfPm3Iso14443a, "Dump TOPAZ family tag to file"}, + {"info", CmdHFTopazInfo, IfPm3Iso14443a, "Tag information"}, + {"raw", CmdHFTopazRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, + {"rdbl", CmdHFTopazRdBl, IfPm3Iso14443a, "Read block"}, + {"reader", CmdHFTopazReader, IfPm3Iso14443a, "Act like a Topaz reader"}, + {"sim", CmdHFTopazSim, IfPm3Iso14443a, "Simulate Topaz tag"}, + {"sniff", CmdHFTopazSniff, IfPm3Iso14443a, "Sniff Topaz reader-tag communication"}, + {"view", CmdHFTopazView, AlwaysAvailable, "Display content from tag dump file"}, + {"wrbl", CmdHFTopazWrBl, IfPm3Iso14443a, "Write block"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("ndef") " -----------------------"}, +// {"ndefformat", CmdHFTopazNDEFFormat, IfPm3Iso14443a, "Format Topaz Tag as NFC Tag"}, +// {"ndefread", CmdHFTopazNDEFRead, IfPm3Iso14443a, "Read and print NDEF records from card"}, +// {"ndefwrite", CmdHFTopazNDEFWrite, IfPm3Iso14443a, "Write NDEF records to card"}, + + {NULL, NULL, 0, NULL} }; static int CmdHelp(const char *Cmd) { diff --git a/client/src/cmdhfvas.c b/client/src/cmdhfvas.c new file mode 100644 index 000000000..9d4abbb6b --- /dev/null +++ b/client/src/cmdhfvas.c @@ -0,0 +1,595 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// An implementation of the Value Added Service protocol +//----------------------------------------------------------------------------- + +#include "cmdhfvas.h" +#include "cliparser.h" +#include "cmdparser.h" +#include "comms.h" +#include "ansi.h" +#include "cmdhf14a.h" +#include "emv/tlv.h" +#include "iso7816/apduinfo.h" +#include "ui.h" +#include "util.h" +#include "util_posix.h" +#include "iso7816/iso7816core.h" +#include +#include +#include "mifare.h" +#include +#include +#include "crypto/libpcrypto.h" +#include "fileutils.h" +#include "mbedtls/ecp.h" +#include "mbedtls/bignum.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/ecc_point_compression.h" +#include "mbedtls/gcm.h" + +static const iso14a_polling_frame_t WUPA_FRAME = { + .frame = { 0x52 }, + .frame_length = 1, + .last_byte_bits = 7, + .extra_delay = 0, +}; + +static const iso14a_polling_frame_t ECP_VAS_ONLY_FRAME = { + .frame = {0x6a, 0x01, 0x00, 0x00, 0x02, 0xe4, 0xd2}, + .frame_length = 7, + .last_byte_bits = 8, + .extra_delay = 0, +}; + +uint8_t aid[] = { 0x4f, 0x53, 0x45, 0x2e, 0x56, 0x41, 0x53, 0x2e, 0x30, 0x31 }; +uint8_t getVasUrlOnlyP2 = 0x00; +uint8_t getVasFullReqP2 = 0x01; + +static int ParseSelectVASResponse(const uint8_t *response, size_t resLen, bool verbose) { + struct tlvdb *tlvRoot = tlvdb_parse_multi(response, resLen); + + const struct tlvdb *versionTlv = tlvdb_find_full(tlvRoot, 0x9F21); + if (versionTlv == NULL) { + tlvdb_free(tlvRoot); + return PM3_ECARDEXCHANGE; + } + const struct tlv *version = tlvdb_get_tlv(versionTlv); + if (version->len != 2) { + tlvdb_free(tlvRoot); + return PM3_ECARDEXCHANGE; + } + if (verbose) { + PrintAndLogEx(INFO, "Mobile VAS application version: %d.%d", version->value[0], version->value[1]); + } + if (version->value[0] != 0x01 || version->value[1] != 0x00) { + tlvdb_free(tlvRoot); + return PM3_ECARDEXCHANGE; + } + + const struct tlvdb *capabilitiesTlv = tlvdb_find_full(tlvRoot, 0x9F23); + if (capabilitiesTlv == NULL) { + tlvdb_free(tlvRoot); + return PM3_ECARDEXCHANGE; + } + const struct tlv *capabilities = tlvdb_get_tlv(capabilitiesTlv); + if (capabilities->len != 4 + || capabilities->value[0] != 0x00 + || capabilities->value[1] != 0x00 + || capabilities->value[2] != 0x00 + || (capabilities->value[3] & 8) == 0) { + tlvdb_free(tlvRoot); + return PM3_ECARDEXCHANGE; + } + + tlvdb_free(tlvRoot); + return PM3_SUCCESS; +} + +static int CreateGetVASDataCommand(const uint8_t *pidHash, const char *url, size_t urlLen, uint8_t *out, int *outLen) { + if (pidHash == NULL && url == NULL) { + PrintAndLogEx(FAILED, "Must provide a Pass Type ID or a URL"); + return PM3_EINVARG; + } + + if (url != NULL && urlLen > 256) { + PrintAndLogEx(FAILED, "URL must be less than 256 characters"); + return PM3_EINVARG; + } + + uint8_t p2 = pidHash == NULL ? getVasUrlOnlyP2 : getVasFullReqP2; + + size_t reqTlvLen = 19 + (pidHash != NULL ? 35 : 0) + (url != NULL ? 3 + urlLen : 0); + uint8_t *reqTlv = calloc(reqTlvLen, sizeof(uint8_t)); + + uint8_t version[] = {0x9F, 0x22, 0x02, 0x01, 0x00}; + memcpy(reqTlv, version, sizeof(version)); + + uint8_t unknown[] = {0x9F, 0x28, 0x04, 0x00, 0x00, 0x00, 0x00}; + memcpy(reqTlv + sizeof(version), unknown, sizeof(unknown)); + + uint8_t terminalCapabilities[] = {0x9F, 0x26, 0x04, 0x00, 0x00, 0x00, 0x02}; + memcpy(reqTlv + sizeof(version) + sizeof(unknown), terminalCapabilities, sizeof(terminalCapabilities)); + + if (pidHash != NULL) { + size_t offset = sizeof(version) + sizeof(unknown) + sizeof(terminalCapabilities); + reqTlv[offset] = 0x9F; + reqTlv[offset + 1] = 0x25; + reqTlv[offset + 2] = 32; + memcpy(reqTlv + offset + 3, pidHash, 32); + } + + if (url != NULL) { + size_t offset = sizeof(version) + sizeof(unknown) + sizeof(terminalCapabilities) + (pidHash != NULL ? 35 : 0); + reqTlv[offset] = 0x9F; + reqTlv[offset + 1] = 0x29; + reqTlv[offset + 2] = urlLen; + memcpy(reqTlv + offset + 3, url, urlLen); + } + + out[0] = 0x80; + out[1] = 0xCA; + out[2] = 0x01; + out[3] = p2; + out[4] = reqTlvLen; + memcpy(out + 5, reqTlv, reqTlvLen); + out[5 + reqTlvLen] = 0x00; + + *outLen = 6 + reqTlvLen; + + free(reqTlv); + return PM3_SUCCESS; +} + +static int ParseGetVASDataResponse(const uint8_t *res, size_t resLen, uint8_t *cryptogram, size_t *cryptogramLen) { + struct tlvdb *tlvRoot = tlvdb_parse_multi(res, resLen); + + const struct tlvdb *cryptogramTlvdb = tlvdb_find_full(tlvRoot, 0x9F27); + if (cryptogramTlvdb == NULL) { + tlvdb_free(tlvRoot); + return PM3_ECARDEXCHANGE; + } + const struct tlv *cryptogramTlv = tlvdb_get_tlv(cryptogramTlvdb); + + memcpy(cryptogram, cryptogramTlv->value, cryptogramTlv->len); + *cryptogramLen = cryptogramTlv->len; + + tlvdb_free(tlvRoot); + return PM3_SUCCESS; +} + +static int LoadReaderPrivateKey(const uint8_t *buf, size_t bufLen, mbedtls_ecp_keypair *privKey) { + struct tlvdb *derRoot = tlvdb_parse_multi(buf, bufLen); + + const struct tlvdb *privkeyTlvdb = tlvdb_find_full(derRoot, 0x04); + if (privkeyTlvdb == NULL) { + tlvdb_free(derRoot); + return PM3_EINVARG; + } + const struct tlv *privkeyTlv = tlvdb_get_tlv(privkeyTlvdb); + + if (mbedtls_ecp_read_key(MBEDTLS_ECP_DP_SECP256R1, privKey, privkeyTlv->value, privkeyTlv->len)) { + tlvdb_free(derRoot); + PrintAndLogEx(FAILED, "Unable to parse private key file. Should be DER encoded ASN1"); + return PM3_EINVARG; + } + + const struct tlvdb *pubkeyCoordsTlvdb = tlvdb_find_full(derRoot, 0x03); + if (pubkeyCoordsTlvdb == NULL) { + tlvdb_free(derRoot); + PrintAndLogEx(FAILED, "Private key file should include public key component"); + return PM3_EINVARG; + } + const struct tlv *pubkeyCoordsTlv = tlvdb_get_tlv(pubkeyCoordsTlvdb); + if (pubkeyCoordsTlv->len != 66 || pubkeyCoordsTlv->value[0] != 0x00 || pubkeyCoordsTlv->value[1] != 0x04) { + tlvdb_free(derRoot); + PrintAndLogEx(FAILED, "Invalid public key data"); + return PM3_EINVARG; + } + + if (mbedtls_ecp_point_read_binary(&privKey->grp, &privKey->Q, pubkeyCoordsTlv->value + 1, 65)) { + PrintAndLogEx(FAILED, "Failed to read in public key coordinates"); + tlvdb_free(derRoot); + return PM3_EINVARG; + } + + if (mbedtls_ecp_check_pubkey(&privKey->grp, &privKey->Q)) { + PrintAndLogEx(FAILED, "VAS protocol requires an elliptic key on the P-256 curve"); + tlvdb_free(derRoot); + return PM3_EINVARG; + } + + tlvdb_free(derRoot); + return PM3_SUCCESS; +} + +static int GetPrivateKeyHint(mbedtls_ecp_keypair *privKey, uint8_t *keyHint) { + uint8_t xcoord[32] = {0}; + if (mbedtls_mpi_write_binary(&privKey->Q.X, xcoord, sizeof(xcoord))) { + return PM3_EINVARG; + } + + uint8_t hash[32] = {0}; + sha256hash(xcoord, 32, hash); + + memcpy(keyHint, hash, 4); + return PM3_SUCCESS; +} + +static int LoadMobileEphemeralKey(const uint8_t *xcoordBuf, mbedtls_ecp_keypair *pubKey) { + uint8_t compressedEcKey[33] = {0}; + compressedEcKey[0] = 0x02; + memcpy(compressedEcKey + 1, xcoordBuf, 32); + + uint8_t decompressedEcKey[65] = {0}; + size_t decompressedEcKeyLen = 0; + if (mbedtls_ecp_decompress(&pubKey->grp, compressedEcKey, sizeof(compressedEcKey), decompressedEcKey, &decompressedEcKeyLen, sizeof(decompressedEcKey))) { + return PM3_EINVARG; + } + + if (mbedtls_ecp_point_read_binary(&pubKey->grp, &pubKey->Q, decompressedEcKey, decompressedEcKeyLen)) { + return PM3_EINVARG; + } + + return PM3_SUCCESS; +} + +static int internalVasDecrypt(uint8_t *cipherText, size_t cipherTextLen, uint8_t *sharedSecret, + uint8_t *ansiSharedInfo, size_t ansiSharedInfoLen, + const uint8_t *gcmAad, size_t gcmAadLen, uint8_t *out, size_t *outLen) { + uint8_t key[32] = {0}; + if (ansi_x963_sha256(sharedSecret, 32, ansiSharedInfo, ansiSharedInfoLen, sizeof(key), key)) { + PrintAndLogEx(FAILED, "ANSI X9.63 key derivation failed"); + return PM3_EINVARG; + } + + uint8_t iv[16] = {0}; + + mbedtls_gcm_context gcmCtx; + mbedtls_gcm_init(&gcmCtx); + if (mbedtls_gcm_setkey(&gcmCtx, MBEDTLS_CIPHER_ID_AES, key, sizeof(key) * 8)) { + PrintAndLogEx(FAILED, "Unable to use key in GCM context"); + return PM3_EINVARG; + } + + if (mbedtls_gcm_auth_decrypt(&gcmCtx, cipherTextLen - 16, iv, sizeof(iv), gcmAad, gcmAadLen, cipherText + cipherTextLen - 16, 16, cipherText, out)) { + PrintAndLogEx(FAILED, "Failed to perform GCM decryption"); + return PM3_EINVARG; + } + + mbedtls_gcm_free(&gcmCtx); + + *outLen = cipherTextLen - 16; + + return PM3_SUCCESS; +} + +static int DecryptVASCryptogram(uint8_t *pidHash, uint8_t *cryptogram, size_t cryptogramLen, mbedtls_ecp_keypair *privKey, uint8_t *out, size_t *outLen, uint32_t *timestamp) { + uint8_t keyHint[4] = {0}; + if (GetPrivateKeyHint(privKey, keyHint) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Unable to generate key hint"); + return PM3_EINVARG; + } + + if (memcmp(keyHint, cryptogram, 4) != 0) { + PrintAndLogEx(FAILED, "Private key does not match cryptogram"); + return PM3_EINVARG; + } + + mbedtls_ecp_keypair mobilePubKey; + mbedtls_ecp_keypair_init(&mobilePubKey); + mobilePubKey.grp = privKey->grp; + + if (LoadMobileEphemeralKey(cryptogram + 4, &mobilePubKey) != PM3_SUCCESS) { + mbedtls_ecp_keypair_free(&mobilePubKey); + PrintAndLogEx(FAILED, "Unable to parse mobile ephemeral key from cryptogram"); + return PM3_EINVARG; + } + + mbedtls_mpi sharedSecret; + mbedtls_mpi_init(&sharedSecret); + + if (mbedtls_ecdh_compute_shared(&privKey->grp, &sharedSecret, &mobilePubKey.Q, &privKey->d, NULL, NULL)) { + mbedtls_mpi_free(&sharedSecret); + mbedtls_ecp_keypair_free(&mobilePubKey); + PrintAndLogEx(FAILED, "Failed to generate ECDH shared secret"); + return PM3_EINVARG; + } + mbedtls_ecp_keypair_free(&mobilePubKey); + + uint8_t sharedSecretBytes[32] = {0}; + if (mbedtls_mpi_write_binary(&sharedSecret, sharedSecretBytes, sizeof(sharedSecretBytes))) { + mbedtls_mpi_free(&sharedSecret); + PrintAndLogEx(FAILED, "Failed to generate ECDH shared secret"); + return PM3_EINVARG; + } + mbedtls_mpi_free(&sharedSecret); + + uint8_t string1[27] = "ApplePay encrypted VAS data"; + uint8_t string2[13] = "id-aes256-GCM"; + + uint8_t method1SharedInfo[73] = {0}; + method1SharedInfo[0] = 13; + memcpy(method1SharedInfo + 1, string2, sizeof(string2)); + memcpy(method1SharedInfo + 1 + sizeof(string2), string1, sizeof(string1)); + memcpy(method1SharedInfo + 1 + sizeof(string2) + sizeof(string1), pidHash, 32); + + uint8_t decryptedData[68] = {0}; + size_t decryptedDataLen = 0; + if (internalVasDecrypt(cryptogram + 4 + 32, cryptogramLen - 4 - 32, sharedSecretBytes, method1SharedInfo, sizeof(method1SharedInfo), NULL, 0, decryptedData, &decryptedDataLen)) { + if (internalVasDecrypt(cryptogram + 4 + 32, cryptogramLen - 4 - 32, sharedSecretBytes, string1, sizeof(string1), pidHash, 32, decryptedData, &decryptedDataLen)) { + return PM3_EINVARG; + } + } + + memcpy(out, decryptedData + 4, decryptedDataLen - 4); + *outLen = decryptedDataLen - 4; + + *timestamp = 0; + for (int i = 0; i < 4; ++i) { + *timestamp = (*timestamp << 8) | decryptedData[i]; + } + + return PM3_SUCCESS; +} + +static int VASReader(uint8_t *pidHash, const char *url, size_t urlLen, uint8_t *cryptogram, size_t *cryptogramLen, bool verbose) { + clearCommandBuffer(); + + iso14a_polling_parameters_t polling_parameters = { + .frames = { WUPA_FRAME, ECP_VAS_ONLY_FRAME }, + .frame_count = 2, + .extra_timeout = 250 + }; + + if (SelectCard14443A_4_WithParameters(false, false, NULL, &polling_parameters) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "No ISO14443-A Card in field"); + return PM3_ECARDEXCHANGE; + } + + uint16_t status = 0; + size_t resLen = 0; + uint8_t selectResponse[APDU_RES_LEN] = {0}; + Iso7816Select(CC_CONTACTLESS, false, true, aid, sizeof(aid), selectResponse, APDU_RES_LEN, &resLen, &status); + + if (status != 0x9000) { + PrintAndLogEx(FAILED, "Card doesn't support VAS"); + return PM3_ECARDEXCHANGE; + } + + if (ParseSelectVASResponse(selectResponse, resLen, verbose) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Card doesn't support VAS"); + return PM3_ECARDEXCHANGE; + } + + uint8_t getVasApdu[PM3_CMD_DATA_SIZE]; + int getVasApduLen = 0; + + int s = CreateGetVASDataCommand(pidHash, url, urlLen, getVasApdu, &getVasApduLen); + if (s != PM3_SUCCESS) { + return s; + } + + uint8_t apduRes[APDU_RES_LEN] = {0}; + int apduResLen = 0; + + s = ExchangeAPDU14a(getVasApdu, getVasApduLen, false, false, apduRes, APDU_RES_LEN, &apduResLen); + if (s != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Failed to send APDU"); + return s; + } + + if (apduResLen == 2 && apduRes[0] == 0x62 && apduRes[1] == 0x87) { + PrintAndLogEx(WARNING, "Device returned error on GET VAS DATA. Either doesn't have pass with matching id, or requires user authentication."); + return PM3_ECARDEXCHANGE; + } + + if (apduResLen == 0 || apduRes[0] != 0x70) { + PrintAndLogEx(FAILED, "Invalid response from peer"); + } + + return ParseGetVASDataResponse(apduRes, apduResLen, cryptogram, cryptogramLen); +} + +static int CmdVASReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf vas reader", + "Read and decrypt Value Added Services (VAS) message", + "hf vas reader --url https://example.com -> URL Only mode\n" + "hf vas reader --pid pass.com.passkit.pksamples.nfcdemo -f vas_privkey.der -@\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "pid", "", "PID, pass type id"), + arg_str0("f", "file", "", "path to terminal private key file"), + arg_str0(NULL, "url", "", "a URL to provide to the mobile device"), + arg_lit0("@", NULL, "continuous mode"), + arg_lit0("v", "verbose", "log additional information"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int pidlen = 0; + char pid[512] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)pid, 512, &pidlen); + + int keyfnlen = 0; + char keyfn[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)keyfn, FILE_PATH_SIZE, &keyfnlen); + + if (keyfnlen == 0 && pidlen > 0) { + PrintAndLogEx(FAILED, "Must provide path to terminal private key if a pass type id is provided"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int urllen = 0; + char url[512] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)url, 512, &urllen); + + bool continuous = arg_get_lit(ctx, 4); + bool verbose = arg_get_lit(ctx, 5); + CLIParserFree(ctx); + + // santity checks + uint8_t *key_data = NULL; + size_t key_datalen = 0; + if (loadFile_safe(keyfn, "", (void **)&key_data, &key_datalen) != PM3_SUCCESS) { + return PM3_EFILE; + } + + mbedtls_ecp_keypair privKey; + mbedtls_ecp_keypair_init(&privKey); + + if (LoadReaderPrivateKey(key_data, key_datalen, &privKey) != PM3_SUCCESS) { + free(key_data); + mbedtls_ecp_keypair_free(&privKey); + return PM3_ESOFT; + } + free(key_data); + + PrintAndLogEx(INFO, "Requesting pass type id... " _GREEN_("%s"), sprint_ascii((uint8_t *) pid, pidlen)); + + if (continuous) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + uint8_t pidhash[32] = {0}; + sha256hash((uint8_t *) pid, pidlen, pidhash); + + size_t clen = 0; + size_t mlen = 0; + uint8_t cryptogram[120] = {0}; + uint8_t msg[64] = {0}; + uint32_t timestamp = 0; + int res = PM3_SUCCESS; + + do { + if (continuous && kbd_enter_pressed()) { + break; + } + + res = VASReader((pidlen > 0) ? pidhash : NULL, url, urllen, cryptogram, &clen, verbose); + if (res == PM3_SUCCESS) { + + res = DecryptVASCryptogram(pidhash, cryptogram, clen, &privKey, msg, &mlen, ×tamp); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Timestamp... " _YELLOW_("%d") " (secs since Jan 1, 2001)", timestamp); + PrintAndLogEx(SUCCESS, "Message..... " _YELLOW_("%s"), sprint_ascii(msg, mlen)); + // extra sleep after successfull read + if (continuous) { + msleep(3000); + } + } + } + msleep(300); + } while (continuous); + + mbedtls_ecp_keypair_free(&privKey); + return res; +} + +static int CmdVASDecrypt(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf vas decrypt", + "Decrypt a previously captured cryptogram", + "hf vas decrypt --pid pass.com.passkit.pksamples.nfcdemo -f vas_privkey.der -d c0b77375eae416b79449347f9fe838c05cdb57dc7470b97b93b806cb348771d9bfbe29d58538c7c7d7c3d015fa205b68bfccd726058a62f7f44085ac98dbf877120fd9059f1507b956e0a6d56d0a\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "pid", "", "PID, pass type id"), + arg_str0("f", "file", "", "path to terminal private key file"), + arg_str0("d", "data", "", "cryptogram to decrypt"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int pidlen = 0; + char pid[512] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)pid, 512, &pidlen); + + int keyfnlen = 0; + char keyfn[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)keyfn, FILE_PATH_SIZE, &keyfnlen); + + if (keyfnlen == 0 && pidlen > 0) { + PrintAndLogEx(FAILED, "Must provide path to terminal private key if a pass type id is provided"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + int clen = 0; + uint8_t cryptogram[120] = {0}; + CLIGetHexWithReturn(ctx, 3, cryptogram, &clen); + CLIParserFree(ctx); + + // santity checks + uint8_t *key_data = NULL; + size_t key_datalen = 0; + if (loadFile_safe(keyfn, "", (void **)&key_data, &key_datalen) != PM3_SUCCESS) { + return PM3_EFILE; + } + + mbedtls_ecp_keypair privKey; + mbedtls_ecp_keypair_init(&privKey); + + if (LoadReaderPrivateKey(key_data, key_datalen, &privKey) != PM3_SUCCESS) { + free(key_data); + mbedtls_ecp_keypair_free(&privKey); + return PM3_EFILE; + } + free(key_data); + + uint8_t pidhash[32] = {0}; + sha256hash((uint8_t *) pid, pidlen, pidhash); + + size_t mlen = 0; + uint8_t msg[64] = {0}; + uint32_t timestamp = 0; + + int res = DecryptVASCryptogram(pidhash, cryptogram, clen, &privKey, msg, &mlen, ×tamp); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Timestamp... " _YELLOW_("%d") " (secs since Jan 1, 2001)", timestamp); + PrintAndLogEx(SUCCESS, "Message..... " _YELLOW_("%s"), sprint_ascii(msg, mlen)); + } + + mbedtls_ecp_keypair_free(&privKey); + return res; +} + +static int CmdHelp(const char *Cmd); + +static command_t CommandTable[] = { + {"--------", CmdHelp, AlwaysAvailable, "----------- " _CYAN_("Value Added Service") " -----------"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"--------", CmdHelp, AlwaysAvailable, "----------------- " _CYAN_("General") " -----------------"}, + {"reader", CmdVASReader, IfPm3Iso14443a, "Read and decrypt VAS message"}, + {"decrypt", CmdVASDecrypt, AlwaysAvailable, "Decrypt a previously captured VAS cryptogram"}, + {NULL, NULL, NULL, NULL} +}; + +int CmdHFVAS(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} diff --git a/client/src/cmdhfvas.h b/client/src/cmdhfvas.h new file mode 100644 index 000000000..50189e227 --- /dev/null +++ b/client/src/cmdhfvas.h @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// An implementation of the Value Added Service protocol +//----------------------------------------------------------------------------- + +#ifndef CMDHFVAS_H__ +#define CMDHFVAS_H__ + +#include "common.h" + +int CmdHFVAS(const char *Cmd); + +#endif diff --git a/client/src/cmdhfwaveshare.c b/client/src/cmdhfwaveshare.c index 8df62bcc1..cc220b55d 100644 --- a/client/src/cmdhfwaveshare.c +++ b/client/src/cmdhfwaveshare.c @@ -28,31 +28,7 @@ #include "fileutils.h" #include "util_posix.h" // msleep #include "cliparser.h" - -// Currently the largest pixel 880*528 only needs 58.08K bytes -#define WSMAPSIZE 60000 - -typedef struct { - uint8_t B; - uint8_t M; - uint32_t fsize; - uint16_t res1; - uint16_t res2; - uint32_t offset; - uint32_t Bit_Pixel; - uint32_t BMP_Width; - uint32_t BMP_Height; - uint16_t planes; - uint16_t bpp; - uint32_t ctype; - uint32_t dsize; - uint32_t hppm; - uint32_t vppm; - uint32_t colorsused; - uint32_t colorreq; - uint32_t Color_1; //Color palette - uint32_t Color_2; -} PACKED bmp_header_t; +#include "imgutils.h" #define EPD_1IN54B 0 #define EPD_1IN54C 1 @@ -105,254 +81,24 @@ static model_t models[] = { static int CmdHelp(const char *Cmd); -static int picture_bit_depth(const uint8_t *bmp, const size_t bmpsize, const uint8_t model_nr) { - if (bmpsize < sizeof(bmp_header_t)) { - return PM3_ESOFT; +static uint8_t * map8to1(gdImagePtr img, int color) { + // Calculate width rounding up + uint16_t width8 = (gdImageSX(img) + 7) / 8; + + uint8_t * colormap8 = calloc(width8 * gdImageSY(img), sizeof(uint8_t)); + if (!colormap8) { + return NULL; } - bmp_header_t *pbmpheader = (bmp_header_t *)bmp; - PrintAndLogEx(DEBUG, "colorsused = %d", pbmpheader->colorsused); - PrintAndLogEx(DEBUG, "pbmpheader->bpp = %d", pbmpheader->bpp); - if ((pbmpheader->BMP_Width != models[model_nr].width) || (pbmpheader->BMP_Height != models[model_nr].height)) { - PrintAndLogEx(WARNING, "Invalid BMP size, expected %ix%i, got %ix%i", models[model_nr].width, models[model_nr].height, pbmpheader->BMP_Width, pbmpheader->BMP_Height); - } - return pbmpheader->bpp; -} - -static int read_bmp_bitmap(const uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, uint8_t **black, uint8_t **red) { - bmp_header_t *pbmpheader = (bmp_header_t *)bmp; - // check file is bitmap - if (pbmpheader->bpp != 1) { - return PM3_ESOFT; - } - if (pbmpheader->B == 'M' || pbmpheader->M == 'B') { //0x4d42 - PrintAndLogEx(WARNING, "The file is not a BMP!"); - return PM3_ESOFT; - } - PrintAndLogEx(DEBUG, "file size = %d", pbmpheader->fsize); - PrintAndLogEx(DEBUG, "file offset = %d", pbmpheader->offset); - if (pbmpheader->fsize > bmpsize) { - PrintAndLogEx(WARNING, "The file is truncated!"); - return PM3_ESOFT; - } - uint8_t color_flag = pbmpheader->Color_1; - // Get BMP file data pointer - uint32_t offset = pbmpheader->offset; - uint16_t width = pbmpheader->BMP_Width; - uint16_t height = pbmpheader->BMP_Height; - if ((width + 8) * height > WSMAPSIZE * 8) { - PrintAndLogEx(WARNING, "The file is too large, aborting!"); - return PM3_ESOFT; - } - - uint16_t X, Y; - uint16_t Image_Width_Byte = (width % 8 == 0) ? (width / 8) : (width / 8 + 1); - uint16_t Bmp_Width_Byte = (Image_Width_Byte % 4 == 0) ? Image_Width_Byte : ((Image_Width_Byte / 4 + 1) * 4); - - *black = calloc(WSMAPSIZE, sizeof(uint8_t)); - if (*black == NULL) { - return PM3_EMALLOC; - } - // Write data into RAM - for (Y = 0; Y < height; Y++) { // columns - for (X = 0; X < Bmp_Width_Byte; X++) { // lines - if ((X < Image_Width_Byte) && ((X + (height - Y - 1) * Image_Width_Byte) < WSMAPSIZE)) { - (*black)[X + (height - Y - 1) * Image_Width_Byte] = color_flag ? bmp[offset] : ~bmp[offset]; - } - offset++; - } - } - if ((model_nr == M1in54B) || (model_nr == M2in13B)) { - // for BW+Red screens: - *red = calloc(WSMAPSIZE, sizeof(uint8_t)); - if (*red == NULL) { - free(*black); - return PM3_EMALLOC; - } - } - return PM3_SUCCESS; -} - -static void rgb_to_gray(const int16_t *chanR, const int16_t *chanG, const int16_t *chanB, - uint16_t width, uint16_t height, int16_t *chanGrey) { - for (uint16_t Y = 0; Y < height; Y++) { - for (uint16_t X = 0; X < width; X++) { - // greyscale conversion - float Clinear = 0.2126 * chanR[X + Y * width] + 0.7152 * chanG[X + Y * width] + 0.0722 * chanB[X + Y * width]; - // Csrgb = 12.92 Clinear when Clinear <= 0.0031308 - // Csrgb = 1.055 Clinear1/2.4 - 0.055 when Clinear > 0.0031308 - chanGrey[X + Y * width] = Clinear; - } - } -} - -// Floyd-Steinberg dithering -static void dither_chan_inplace(int16_t *chan, uint16_t width, uint16_t height) { - for (uint16_t Y = 0; Y < height; Y++) { - for (uint16_t X = 0; X < width; X++) { - int16_t oldp = chan[X + Y * width]; - int16_t newp = oldp > 127 ? 255 : 0; - chan[X + Y * width] = newp; - int16_t err = oldp - newp; - const float m[] = {7, 3, 5, 1}; - if (X < width - 1) { - chan[X + 1 + Y * width] = chan[X + 1 + Y * width] + m[0] / 16 * err; - } - if (Y < height - 1) { - chan[X - 1 + (Y + 1) * width] = chan[X - 1 + (Y + 1) * width] + m[1] / 16 * err; - chan[X + (Y + 1) * width] = chan[X + (Y + 1) * width] + m[2] / 16 * err; - } - if ((X < width - 1) && (Y < height - 1)) { - chan[X + 1 + (Y + 1) * width] = chan[X + 1 + (Y + 1) * width] + m[3] / 16 * err; - } - } - } -} - -static uint32_t color_compare(int16_t r1, int16_t g1, int16_t b1, int16_t r2, int16_t g2, int16_t b2) { - // Compute (square of) distance from oldR/G/B to this color - int16_t inR = r1 - r2; - int16_t inG = g1 - g2; - int16_t inB = b1 - b2; - // use RGB-to-grey weighting - float dist = 0.2126 * inR * inR + 0.7152 * inG * inG + 0.0722 * inB * inB; - return dist; -} - -static void nearest_color(int16_t oldR, int16_t oldG, int16_t oldB, const uint8_t *palette, - uint16_t palettelen, uint8_t *newR, uint8_t *newG, uint8_t *newB) { - uint32_t bestdist = 0x7FFFFFFF; - for (uint16_t i = 0; i < palettelen; i++) { - uint8_t R = palette[i * 3 + 0]; - uint8_t G = palette[i * 3 + 1]; - uint8_t B = palette[i * 3 + 2]; - uint32_t dist = color_compare(oldR, oldG, oldB, R, G, B); - if (dist < bestdist) { - bestdist = dist; - *newR = R; - *newG = G; - *newB = B; - } - } -} - -static void dither_rgb_inplace(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height, uint8_t *palette, uint16_t palettelen) { - for (uint16_t Y = 0; Y < height; Y++) { - for (uint16_t X = 0; X < width; X++) { - // scan odd lines in the opposite direction - uint16_t XX = X; - if (Y % 2) { - XX = width - X - 1; - } - int16_t oldR = chanR[XX + Y * width]; - int16_t oldG = chanG[XX + Y * width]; - int16_t oldB = chanB[XX + Y * width]; - uint8_t newR = 0, newG = 0, newB = 0; - nearest_color(oldR, oldG, oldB, palette, palettelen, &newR, &newG, &newB); - chanR[XX + Y * width] = newR; - chanG[XX + Y * width] = newG; - chanB[XX + Y * width] = newB; - int16_t errR = oldR - newR; - int16_t errG = oldG - newG; - int16_t errB = oldB - newB; - const float m[] = {7, 3, 5, 1}; - if (Y % 2) { - if (XX > 0) { - chanR[XX - 1 + Y * width] = (chanR[XX - 1 + Y * width] + m[0] / 16 * errR); - chanG[XX - 1 + Y * width] = (chanG[XX - 1 + Y * width] + m[0] / 16 * errG); - chanB[XX - 1 + Y * width] = (chanB[XX - 1 + Y * width] + m[0] / 16 * errB); - } - if (Y < height - 1) { - chanR[XX - 1 + (Y + 1) * width] = (chanR[XX - 1 + (Y + 1) * width] + m[3] / 16 * errR); - chanG[XX - 1 + (Y + 1) * width] = (chanG[XX - 1 + (Y + 1) * width] + m[3] / 16 * errG); - chanB[XX - 1 + (Y + 1) * width] = (chanB[XX - 1 + (Y + 1) * width] + m[3] / 16 * errB); - chanR[XX + (Y + 1) * width] = (chanR[XX + (Y + 1) * width] + m[2] / 16 * errR); - chanG[XX + (Y + 1) * width] = (chanG[XX + (Y + 1) * width] + m[2] / 16 * errG); - chanB[XX + (Y + 1) * width] = (chanB[XX + (Y + 1) * width] + m[2] / 16 * errB); - } - if ((XX < width - 1) && (Y < height - 1)) { - chanR[XX + 1 + (Y + 1) * width] = (chanR[XX + 1 + (Y + 1) * width] + m[1] / 16 * errR); - chanG[XX + 1 + (Y + 1) * width] = (chanG[XX + 1 + (Y + 1) * width] + m[1] / 16 * errG); - chanB[XX + 1 + (Y + 1) * width] = (chanB[XX + 1 + (Y + 1) * width] + m[1] / 16 * errB); - } - } else { - if (XX < width - 1) { - chanR[XX + 1 + Y * width] = (chanR[XX + 1 + Y * width] + m[0] / 16 * errR); - chanG[XX + 1 + Y * width] = (chanG[XX + 1 + Y * width] + m[0] / 16 * errG); - chanB[XX + 1 + Y * width] = (chanB[XX + 1 + Y * width] + m[0] / 16 * errB); - } - if (Y < height - 1) { - chanR[XX - 1 + (Y + 1) * width] = (chanR[XX - 1 + (Y + 1) * width] + m[1] / 16 * errR); - chanG[XX - 1 + (Y + 1) * width] = (chanG[XX - 1 + (Y + 1) * width] + m[1] / 16 * errG); - chanB[XX - 1 + (Y + 1) * width] = (chanB[XX - 1 + (Y + 1) * width] + m[1] / 16 * errB); - chanR[XX + (Y + 1) * width] = (chanR[XX + (Y + 1) * width] + m[2] / 16 * errR); - chanG[XX + (Y + 1) * width] = (chanG[XX + (Y + 1) * width] + m[2] / 16 * errG); - chanB[XX + (Y + 1) * width] = (chanB[XX + (Y + 1) * width] + m[2] / 16 * errB); - } - if ((XX < width - 1) && (Y < height - 1)) { - chanR[XX + 1 + (Y + 1) * width] = (chanR[XX + 1 + (Y + 1) * width] + m[3] / 16 * errR); - chanG[XX + 1 + (Y + 1) * width] = (chanG[XX + 1 + (Y + 1) * width] + m[3] / 16 * errG); - chanB[XX + 1 + (Y + 1) * width] = (chanB[XX + 1 + (Y + 1) * width] + m[3] / 16 * errB); - } - } - } - } -} - -static void rgb_to_gray_red_inplace(int16_t *chanR, int16_t *chanG, int16_t *chanB, uint16_t width, uint16_t height) { - for (uint16_t Y = 0; Y < height; Y++) { - for (uint16_t X = 0; X < width; X++) { - float Clinear = 0.2126 * chanR[X + Y * width] + 0.7152 * chanG[X + Y * width] + 0.0722 * chanB[X + Y * width]; - if ((chanR[X + Y * width] < chanG[X + Y * width] && chanR[X + Y * width] < chanB[X + Y * width])) { - chanR[X + Y * width] = Clinear; - chanG[X + Y * width] = Clinear; - chanB[X + Y * width] = Clinear; - } - } - } -} - -static void threshold_chan(const int16_t *colorchan, uint16_t width, uint16_t height, uint8_t threshold, uint8_t *colormap) { - for (uint16_t Y = 0; Y < height; Y++) { - for (uint16_t X = 0; X < width; X++) { - colormap[X + Y * width] = colorchan[X + Y * width] < threshold; - } - } -} - -static void threshold_rgb_black_red(const int16_t *chanR, const int16_t *chanG, const int16_t *chanB, - uint16_t width, uint16_t height, uint8_t threshold_black, - uint8_t threshold_red, uint8_t *blackmap, uint8_t *redmap) { - for (uint16_t Y = 0; Y < height; Y++) { - for (uint16_t X = 0; X < width; X++) { - if ((chanR[X + Y * width] < threshold_black) && (chanG[X + Y * width] < threshold_black) && (chanB[X + Y * width] < threshold_black)) { - blackmap[X + Y * width] = 1; - redmap[X + Y * width] = 0; - } else if ((chanR[X + Y * width] > threshold_red) && (chanG[X + Y * width] < threshold_black) && (chanB[X + Y * width] < threshold_black)) { - blackmap[X + Y * width] = 0; - redmap[X + Y * width] = 1; - } else { - blackmap[X + Y * width] = 0; - redmap[X + Y * width] = 0; - } - } - } -} - -static void map8to1(const uint8_t *colormap, uint16_t width, uint16_t height, uint8_t *colormap8) { - uint16_t width8; - if (width % 8 == 0) { - width8 = width / 8; - } else { - width8 = width / 8 + 1; - } uint8_t data = 0; uint8_t count = 0; - for (uint16_t Y = 0; Y < height; Y++) { - for (uint16_t X = 0; X < width; X++) { - data = data | colormap[X + Y * width]; + for (uint16_t Y = 0; Y < gdImageSY(img); Y++) { + for (uint16_t X = 0; X < gdImageSX(img); X++) { + if (gdImageGetPixel(img, X, Y) == color) { + data |= 1; + } count += 1; - if ((count >= 8) || (X == width - 1)) { + if ((count >= 8) || (X == gdImageSX(img) - 1)) { colormap8[X / 8 + Y * width8] = (~data) & 0xFF; count = 0; data = 0; @@ -360,194 +106,8 @@ static void map8to1(const uint8_t *colormap, uint16_t width, uint16_t height, ui data = (data << 1) & 0xFF; } } -} -static int read_bmp_rgb(uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, uint8_t **black, uint8_t **red, char *filename, bool save_conversions) { - bmp_header_t *pbmpheader = (bmp_header_t *)bmp; - // check file is full color - if ((pbmpheader->bpp != 24) && (pbmpheader->bpp != 32)) { - return PM3_ESOFT; - } - - if (pbmpheader->B == 'M' || pbmpheader->M == 'B') { //0x4d42 - PrintAndLogEx(WARNING, "The file is not a BMP!"); - return PM3_ESOFT; - } - - PrintAndLogEx(DEBUG, "file size = %d", pbmpheader->fsize); - PrintAndLogEx(DEBUG, "file offset = %d", pbmpheader->offset); - if (pbmpheader->fsize > bmpsize) { - PrintAndLogEx(WARNING, "The file is truncated!"); - return PM3_ESOFT; - } - - // Get BMP file data pointer - uint32_t offset = pbmpheader->offset; - uint16_t width = pbmpheader->BMP_Width; - uint16_t height = pbmpheader->BMP_Height; - if ((width + 8) * height > WSMAPSIZE * 8) { - PrintAndLogEx(WARNING, "The file is too large, aborting!"); - return PM3_ESOFT; - } - - int16_t *chanR = calloc(((size_t)width) * height, sizeof(int16_t)); - if (chanR == NULL) { - return PM3_EMALLOC; - } - - int16_t *chanG = calloc(((size_t)width) * height, sizeof(int16_t)); - if (chanG == NULL) { - free(chanR); - return PM3_EMALLOC; - } - - int16_t *chanB = calloc(((size_t)width) * height, sizeof(int16_t)); - if (chanB == NULL) { - free(chanR); - free(chanG); - return PM3_EMALLOC; - } - - // Extracting BMP chans - for (uint16_t Y = 0; Y < height; Y++) { - for (uint16_t X = 0; X < width; X++) { - chanB[X + (height - Y - 1) * width] = bmp[offset++]; - chanG[X + (height - Y - 1) * width] = bmp[offset++]; - chanR[X + (height - Y - 1) * width] = bmp[offset++]; - if (pbmpheader->bpp == 32) // Skip Alpha chan - offset++; - } - // Skip line padding - offset += width % 4; - } - - if ((model_nr == M1in54B) || (model_nr == M2in13B)) { - // for BW+Red screens: - uint8_t *mapBlack = calloc(((size_t)width) * height, sizeof(uint8_t)); - if (mapBlack == NULL) { - free(chanR); - free(chanG); - free(chanB); - return PM3_EMALLOC; - } - uint8_t *mapRed = calloc(((size_t)width) * height, sizeof(uint8_t)); - if (mapRed == NULL) { - free(chanR); - free(chanG); - free(chanB); - free(mapBlack); - return PM3_EMALLOC; - } - rgb_to_gray_red_inplace(chanR, chanG, chanB, width, height); - - uint8_t palette[] = {0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; // black, white, red - dither_rgb_inplace(chanR, chanG, chanB, width, height, palette, sizeof(palette) / 3); - - threshold_rgb_black_red(chanR, chanG, chanB, width, height, 128, 128, mapBlack, mapRed); - if (save_conversions) { - // fill BMP chans - offset = pbmpheader->offset; - for (uint16_t Y = 0; Y < height; Y++) { - for (uint16_t X = 0; X < width; X++) { - bmp[offset++] = chanB[X + (height - Y - 1) * width] & 0xFF; - bmp[offset++] = chanG[X + (height - Y - 1) * width] & 0xFF; - bmp[offset++] = chanR[X + (height - Y - 1) * width] & 0xFF; - if (pbmpheader->bpp == 32) // Fill Alpha chan - bmp[offset++] = 0xFF; - } - // Skip line padding - offset += width % 4; - } - PrintAndLogEx(INFO, "Saving red+black dithered version..."); - if (saveFile(filename, ".bmp", bmp, offset) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Could not save file " _YELLOW_("%s"), filename); - free(chanR); - free(chanG); - free(chanB); - free(mapBlack); - free(mapRed); - return PM3_EIO; - } - } - free(chanR); - free(chanG); - free(chanB); - *black = calloc(WSMAPSIZE, sizeof(uint8_t)); - if (*black == NULL) { - free(mapBlack); - free(mapRed); - return PM3_EMALLOC; - } - map8to1(mapBlack, width, height, *black); - free(mapBlack); - *red = calloc(WSMAPSIZE, sizeof(uint8_t)); - if (*red == NULL) { - free(mapRed); - free(*black); - return PM3_EMALLOC; - } - map8to1(mapRed, width, height, *red); - free(mapRed); - } else { - // for BW-only screens: - int16_t *chanGrey = calloc(((size_t)width) * height, sizeof(int16_t)); - if (chanGrey == NULL) { - free(chanR); - free(chanG); - free(chanB); - return PM3_EMALLOC; - } - rgb_to_gray(chanR, chanG, chanB, width, height, chanGrey); - dither_chan_inplace(chanGrey, width, height); - - uint8_t *mapBlack = calloc(((size_t)width) * height, sizeof(uint8_t)); - if (mapBlack == NULL) { - free(chanR); - free(chanG); - free(chanB); - free(chanGrey); - return PM3_EMALLOC; - } - threshold_chan(chanGrey, width, height, 128, mapBlack); - - if (save_conversions) { - // fill BMP chans - offset = pbmpheader->offset; - for (uint16_t Y = 0; Y < height; Y++) { - for (uint16_t X = 0; X < width; X++) { - bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF; - bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF; - bmp[offset++] = chanGrey[X + (height - Y - 1) * width] & 0xFF; - if (pbmpheader->bpp == 32) // Fill Alpha chan - bmp[offset++] = 0xFF; - } - // Skip line padding - offset += width % 4; - } - PrintAndLogEx(INFO, "Saving black dithered version..."); - if (saveFile(filename, ".bmp", bmp, offset) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Could not save file " _YELLOW_("%s"), filename); - free(chanGrey); - free(chanR); - free(chanG); - free(chanB); - free(mapBlack); - return PM3_EIO; - } - } - free(chanGrey); - free(chanR); - free(chanG); - free(chanB); - *black = calloc(WSMAPSIZE, sizeof(uint8_t)); - if (*black == NULL) { - free(mapBlack); - return PM3_EMALLOC; - } - map8to1(mapBlack, width, height, *black); - free(mapBlack); - } - return PM3_SUCCESS; + return colormap8; } static void read_black(uint32_t i, uint8_t *l, uint8_t model_nr, const uint8_t *black) { @@ -671,7 +231,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { uint8_t step4[2] = {0xcd, 0x03}; // e-paper power on uint8_t step5[2] = {0xcd, 0x05}; // e-paper config2 uint8_t step6[2] = {0xcd, 0x06}; // EDP load to main - uint8_t step7[2] = {0xcd, 0x07}; // Data preparation + uint8_t step7[3] = {0xcd, 0x07, 0}; // Data preparation uint8_t step8[123] = {0xcd, 0x08, 0x64}; // Data start command // 2.13inch(0x10:Send 16 data at a time) @@ -825,7 +385,7 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { } msleep(100); PrintAndLogEx(DEBUG, "Step7: Data preparation"); - ret = transceive_blocking(step7, 2, rx, 20, actrxlen, true); // cd 07 + ret = transceive_blocking(step7, 3, rx, 20, actrxlen, true); // cd 07 if (ret != PM3_SUCCESS) { return ret; } @@ -1003,13 +563,13 @@ static int start_drawing(uint8_t model_nr, uint8_t *black, uint8_t *red) { return PM3_SUCCESS; } -static int CmdHF14AWSLoadBmp(const char *Cmd) { +static int CmdHF14AWSLoad(const char *Cmd) { char desc[800] = {0}; for (uint8_t i = 0; i < MEND; i++) { snprintf(desc + strlen(desc), sizeof(desc) - strlen(desc), - "hf waveshare loadbmp -f myfile -m %2u -> %s ( %u, %u )\n", + "hf waveshare load -f myfile -m %2u -> %s ( %u, %u )\n", i, models[i].desc, models[i].width, @@ -1018,8 +578,8 @@ static int CmdHF14AWSLoadBmp(const char *Cmd) { } CLIParserContext *ctx; - CLIParserInit(&ctx, "hf waveshare loadbmp", - "Load BMP file to Waveshare NFC ePaper.", + CLIParserInit(&ctx, "hf waveshare load", + "Load image file to Waveshare NFC ePaper", desc ); @@ -1029,24 +589,25 @@ static int CmdHF14AWSLoadBmp(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_int1("m", NULL, "", modeldesc), - arg_lit0("s", "save", "save dithered version in filename-[n].bmp, only for RGB BMP"), - arg_str1("f", "file", "", "specify filename[.bmp] to upload to tag"), + arg_str1("f", "file", "", "specify image to upload to tag"), + arg_str0("s", "save", "", "save paletized version in file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int model_nr = arg_get_int_def(ctx, 1, -1); - bool save_conversions = arg_get_lit(ctx, 2); - int fnlen = 0; - char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + int infilelen, outfilelen; + char infile[FILE_PATH_SIZE]; + char outfile[FILE_PATH_SIZE]; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)infile, FILE_PATH_SIZE, &infilelen); + CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)outfile, FILE_PATH_SIZE, &outfilelen); CLIParserFree(ctx); //Validations - if (fnlen < 1) { - PrintAndLogEx(WARNING, "Missing filename"); + if (infilelen < 1) { + PrintAndLogEx(WARNING, "Missing input file"); return PM3_EINVARG; } if (model_nr == -1) { @@ -1057,57 +618,84 @@ static int CmdHF14AWSLoadBmp(const char *Cmd) { PrintAndLogEx(WARNING, "Unknown model"); return PM3_EINVARG; } - - uint8_t *bmp = NULL; - uint8_t *black = NULL; - uint8_t *red = NULL; - size_t bytes_read = 0; - if (loadFile_safe(filename, ".bmp", (void **)&bmp, &bytes_read) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), filename); - return PM3_EIO; + if (!g_session.pm3_present && !outfilelen) { + PrintAndLogEx(WARNING, "Offline - can only perform image conversion"); + return PM3_ENOTTY; } - int depth = picture_bit_depth(bmp, bytes_read, model_nr); - if (depth == PM3_ESOFT) { - PrintAndLogEx(ERR, "Error, BMP file is too small"); - free(bmp); - return PM3_ESOFT; - } else if (depth == 1) { - PrintAndLogEx(DEBUG, "BMP file is a bitmap"); - if (read_bmp_bitmap(bmp, bytes_read, model_nr, &black, &red) != PM3_SUCCESS) { - free(bmp); - return PM3_ESOFT; - } - } else if (depth == 24) { - PrintAndLogEx(DEBUG, "BMP file is a RGB"); - if (read_bmp_rgb(bmp, bytes_read, model_nr, &black, &red, filename, save_conversions) != PM3_SUCCESS) { - free(bmp); - return PM3_ESOFT; - } - } else if (depth == 32) { - PrintAndLogEx(DEBUG, "BMP file is a RGBA, we will ignore the Alpha channel"); - if (read_bmp_rgb(bmp, bytes_read, model_nr, &black, &red, filename, save_conversions) != PM3_SUCCESS) { - free(bmp); - return PM3_ESOFT; - } - } else { - PrintAndLogEx(ERR, "Error, BMP color depth %i not supported. Must be 1 (BW), 24 (RGB) or 32 (RGBA)", depth); - free(bmp); - return PM3_ESOFT; - } - free(bmp); + bool model_has_red = model_nr == M1in54B || model_nr == M2in13B; - start_drawing(model_nr, black, red); - free(black); - if ((model_nr == M1in54B) || (model_nr == M2in13B)) { - free(red); + gdImagePtr rgb_img = gdImageCreateFromFile(infile); + if (!rgb_img) { + PrintAndLogEx(WARNING, "Could not load image from " _YELLOW_("%s"), infile); + return PM3_EFILE; } - return PM3_SUCCESS; + + gdImagePtr scaled_img = img_crop_to_fit(rgb_img, models[model_nr].width, models[model_nr].height); + if (scaled_img == NULL) { + PrintAndLogEx(WARNING, "Failed to scale input image"); + gdImageDestroy(rgb_img); + return PM3_EFILE; + } + gdImageDestroy(rgb_img); + + int pal_len = 2; + int pal[3]; + pal[0] = gdTrueColorAlpha(0xFF, 0xFF, 0xFF, 0); // White + pal[1] = gdTrueColorAlpha(0x00, 0x00, 0x00, 0); // Black + if (model_has_red) { + pal_len = 3; + pal[2] = gdTrueColorAlpha(0xFF, 0x00, 0x00, 0); // Red + } + + gdImagePtr pal_img = img_palettize(scaled_img, pal, pal_len); + gdImageDestroy(scaled_img); + + if (!pal_img) { + PrintAndLogEx(WARNING, "Could not convert image"); + return PM3_EMALLOC; + } + + if (outfilelen) { + if (gdImageFile(pal_img, outfile)) { + PrintAndLogEx(INFO, "Save converted image to " _YELLOW_("%s"), outfile); + gdImageDestroy(pal_img); + return PM3_SUCCESS; + } else { + PrintAndLogEx(WARNING, "Could not save converted image", outfile); + gdImageDestroy(pal_img); + return PM3_EFILE; + } + } + + uint8_t * black_plane = map8to1(pal_img, 1); + if (!black_plane) { + PrintAndLogEx(WARNING, "Could not convert image to bit plane"); + gdImageDestroy(pal_img); + return PM3_EMALLOC; + } + + uint8_t * red_plane = NULL; + if (model_has_red) { + red_plane = map8to1(pal_img, 2); + if (!red_plane) { + PrintAndLogEx(WARNING, "Could not convert image to bit plane"); + free(black_plane); + gdImageDestroy(pal_img); + return PM3_EMALLOC; + } + } + gdImageDestroy(pal_img); + + int res = start_drawing(model_nr, black_plane, red_plane); + free(black_plane); + free(red_plane); + return res; } static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"loadbmp", CmdHF14AWSLoadBmp, IfPm3Iso14443a, "Load BMP file to Waveshare NFC ePaper"}, + {"load", CmdHF14AWSLoad, AlwaysAvailable, "Load image file to Waveshare NFC ePaper"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfxerox.c b/client/src/cmdhfxerox.c index fec69855e..88f910dfa 100644 --- a/client/src/cmdhfxerox.c +++ b/client/src/cmdhfxerox.c @@ -383,8 +383,9 @@ static int switch_off_field(void) { static int findXerox(iso14b_card_select_t *card, bool disconnect) { - if (card == NULL) + if (card == NULL) { return PM3_EINVARG; + } int8_t retry = 3; while (retry--) { @@ -399,10 +400,10 @@ static int findXerox(iso14b_card_select_t *card, bool disconnect) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { - if (resp.oldarg[0] == 0) { + if (resp.status == PM3_SUCCESS) { memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); } - return resp.oldarg[0]; + return resp.length; } } // retry @@ -513,7 +514,7 @@ static int CmdHFXeroxInfo(const char *Cmd) { if (verbose) { PrintAndLogEx(FAILED, "Fuji/Xerox tag select failed"); } - return PM3_ERFTRANS; + return status; } PrintAndLogEx(NORMAL, ""); @@ -551,7 +552,7 @@ static int CmdHFXeroxInfo(const char *Cmd) { */ // 14b raw command send data_len instead of status - if (/*resp.status != 0 ||*/ resp.length < 7) { + if (resp.length < 7) { PrintAndLogEx(FAILED, "retrying one more time"); continue; } @@ -611,9 +612,7 @@ static int CmdHFXeroxDump(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - bool decrypt = arg_get_lit(ctx, 2); - CLIParserFree(ctx); iso14b_raw_cmd_t *packet = (iso14b_raw_cmd_t *)calloc(1, sizeof(iso14b_raw_cmd_t) + 11); @@ -657,7 +656,8 @@ static int CmdHFXeroxDump(const char *Cmd) { resp.cmd, resp.length, resp.magic, resp.status, resp.crc, resp.oldarg[0], resp.oldarg[1], resp.oldarg[2], resp.data.asBytes[0], resp.data.asBytes[1], resp.data.asBytes[2], resp.ng ? 't' : 'f'); */ - if (/*resp.status != 0 ||*/ resp.length < 7) { // 14b raw command send data_len instead of status + + if (resp.length < 7) { PrintAndLogEx(FAILED, "retrying one more time"); continue; } @@ -682,7 +682,6 @@ static int CmdHFXeroxDump(const char *Cmd) { PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); -// PrintAndLogEx(INPLACE, "blk %3d", blocknum); } } @@ -692,8 +691,9 @@ static int CmdHFXeroxDump(const char *Cmd) { PrintAndLogEx(NORMAL, ""); - if (blocknum != 0x100) + if (blocknum != 0x100) { PrintAndLogEx(FAILED, "dump failed at block %d", blocknum); + } if (decrypt) { PrintAndLogEx(INFO, "Decrypting secret blocks..."); @@ -747,12 +747,14 @@ static int CmdHFXeroxDump(const char *Cmd) { uint16_t cs, csd; // calc checksum - for (b = 0, cs = 0; b < sizeof(decr) - 2; b += 2) cs += decr[b] | (decr[b + 1] << 8); + for (b = 0, cs = 0; b < sizeof(decr) - 2; b += 2) { + cs += decr[b] | (decr[b + 1] << 8); + } cs = ~cs; csd = (decr[7] << 8) | decr[6]; if (cs != csd) { - PrintAndLogEx(FAILED, "secret block %02X checksum failed.", dadr); + PrintAndLogEx(FAILED, "Secret block %02X checksum " _RED_("failed"), dadr); } } } @@ -773,23 +775,13 @@ static int CmdHFXeroxDump(const char *Cmd) { PrintAndLogEx(NORMAL, ""); if (0 == filename[0]) { // generate filename from uid - /* - PrintAndLogEx(INFO, "Using UID as filename"); - - sprintf(filename, "hf-xerox-%02X%02X%02X%02X%02X%02X%02X%02X-dump%s", - card.uid[7],card.uid[6],card.uid[5],card.uid[4],card.uid[3],card.uid[2],card.uid[1],card.uid[0], - decrypt ? "-dec" : ""); - */ char *fptr = filename; PrintAndLogEx(INFO, "Using UID as filename"); fptr += snprintf(fptr, sizeof(filename), "hf-xerox-"); FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), decrypt ? "-dump-dec" : "-dump", card.uidlen); } - size_t datalen = blocknum * 4; - saveFile(filename, ".bin", data, datalen); - saveFileEML(filename, data, datalen, 4); -// saveFileJSON(filename, jsf15, data, datalen, NULL); + pm3_save_dump(filename, data, blocknum * 4, jsf14b_v2); return PM3_SUCCESS; } diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index 3ddd98189..8c2757249 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -27,12 +27,17 @@ #include "comms.h" #include "usart_defs.h" #include "ui.h" +#include "fpga.h" #include "cmdhw.h" #include "cmddata.h" #include "commonutil.h" +#include "preferences.h" #include "pm3_cmd.h" #include "pmflash.h" // rdv40validation_t #include "cmdflashmem.h" // get_signature.. +#include "uart/uart.h" // configure timeout +#include "util_posix.h" +#include "flash.h" // reboot to bootloader mode static int CmdHelp(const char *Cmd); @@ -42,6 +47,7 @@ static void lookup_chipid_short(uint32_t iChipID, uint32_t mem_used) { case 0x270B0A40: asBuff = "AT91SAM7S512 Rev A"; break; + case 0x270B0A4E: case 0x270B0A4F: asBuff = "AT91SAM7S512 Rev B"; break; @@ -150,6 +156,7 @@ static void lookupChipID(uint32_t iChipID, uint32_t mem_used) { case 0x270B0A40: asBuff = "AT91SAM7S512 Rev A"; break; + case 0x270B0A4E: case 0x270B0A4F: asBuff = "AT91SAM7S512 Rev B"; break; @@ -471,14 +478,9 @@ static int CmdDbg(const char *Cmd) { return PM3_EINVARG; } - clearCommandBuffer(); - SendCommandNG(CMD_GET_DBGMODE, NULL, 0); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_GET_DBGMODE, &resp, 2000) == false) { - PrintAndLogEx(WARNING, "Failed to get current device debug level"); - return PM3_ETIMEOUT; - } - uint8_t curr = resp.data.asBytes[0]; + uint8_t curr = DBG_NONE; + if (getDeviceDebugLevel(&curr) != PM3_SUCCESS) + return PM3_EFAILED; const char *dbglvlstr; switch (curr) { @@ -517,8 +519,8 @@ static int CmdDbg(const char *Cmd) { else if (lv4) dbg = 4; - clearCommandBuffer(); - SendCommandNG(CMD_SET_DBGMODE, &dbg, sizeof(dbg)); + if (setDeviceDebugLevel(dbg, true) != PM3_SUCCESS) + return PM3_EFAILED; } return PM3_SUCCESS; } @@ -689,7 +691,7 @@ static int CmdSetDivisor(const char *Cmd) { CLIParserFree(ctx); if (arg < 19) { - PrintAndLogEx(ERR, "Divisor must be between" _YELLOW_("19") " and " _YELLOW_("255")); + PrintAndLogEx(ERR, "Divisor must be between " _YELLOW_("19") " and " _YELLOW_("255")); return PM3_EINVARG; } // 12 000 000 (12MHz) @@ -807,19 +809,29 @@ static int CmdStatus(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hw status", "Show runtime status information about the connected Proxmark3", - "hw status" + "hw status\n" + "hw status --ms 1000 -> Test connection speed with 1000ms timeout\n" ); void *argtable[] = { arg_param_begin, + arg_int0("m", "ms", "", "speed test timeout in micro seconds"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + int32_t speedTestTimeout = arg_get_int_def(ctx, 1, -1); CLIParserFree(ctx); + clearCommandBuffer(); PacketResponseNG resp; - SendCommandNG(CMD_STATUS, NULL, 0); - if (WaitForResponseTimeout(CMD_STATUS, &resp, 2000) == false) { + if (speedTestTimeout < 0) { + speedTestTimeout = 0; + SendCommandNG(CMD_STATUS, NULL, 0); + } else { + SendCommandNG(CMD_STATUS, (uint8_t *)&speedTestTimeout, sizeof(speedTestTimeout)); + } + + if (WaitForResponseTimeout(CMD_STATUS, &resp, 2000 + speedTestTimeout) == false) { PrintAndLogEx(WARNING, "Status command timeout. Communication speed test timed out"); return PM3_ETIMEOUT; } @@ -924,6 +936,46 @@ static int CmdTia(const char *Cmd) { return PM3_SUCCESS; } +static int CmdTimeout(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hw timeout", + "Set the communication timeout on the client side", + "hw timeout --> Show current timeout\n" + "hw timeout -m 20 --> Set the timeout to 20ms\n" + "hw timeout --ms 500 --> Set the timeout to 500ms\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("m", "ms", "", "timeout in micro seconds"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int32_t arg = arg_get_int_def(ctx, 1, -1); + CLIParserFree(ctx); + + uint32_t oldTimeout = uart_get_timeouts(); + + // timeout is not given/invalid, just show the current timeout then return + if (arg < 0) { + PrintAndLogEx(INFO, "Current communication timeout... " _GREEN_("%u") " ms", oldTimeout); + return PM3_SUCCESS; + } + + uint32_t newTimeout = arg; + // UART_USB_CLIENT_RX_TIMEOUT_MS is considered as the minimum required timeout. + if (newTimeout < UART_USB_CLIENT_RX_TIMEOUT_MS) { + PrintAndLogEx(WARNING, "Timeout less than %u ms might cause errors.", UART_USB_CLIENT_RX_TIMEOUT_MS); + } else if (newTimeout > 5000) { + PrintAndLogEx(WARNING, "Timeout greater than 5000 ms makes the client unresponsive."); + } + uart_reconfigure_timeouts(newTimeout); + PrintAndLogEx(INFO, "Old communication timeout... %u ms", oldTimeout); + PrintAndLogEx(INFO, "New communication timeout... " _GREEN_("%u") " ms", newTimeout); + return PM3_SUCCESS; +} + static int CmdPing(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hw ping", @@ -937,6 +989,7 @@ static int CmdPing(const char *Cmd) { arg_u64_0("l", "len", "", "length of payload to send"), arg_param_end }; + CLIExecWithReturn(ctx, Cmd, argtable, true); uint32_t len = arg_get_u32_def(ctx, 1, 32); CLIParserFree(ctx); @@ -945,7 +998,7 @@ static int CmdPing(const char *Cmd) { len = PM3_CMD_DATA_SIZE; if (len) { - PrintAndLogEx(INFO, "Ping sent with payload len " _YELLOW_("%d"), len); + PrintAndLogEx(INFO, "Ping sent with payload len... " _YELLOW_("%d"), len); } else { PrintAndLogEx(INFO, "Ping sent"); } @@ -954,16 +1007,22 @@ static int CmdPing(const char *Cmd) { PacketResponseNG resp; uint8_t data[PM3_CMD_DATA_SIZE] = {0}; - for (uint16_t i = 0; i < len; i++) + for (uint16_t i = 0; i < len; i++) { data[i] = i & 0xFF; + } + uint64_t tms = msclock(); SendCommandNG(CMD_PING, data, len); if (WaitForResponseTimeout(CMD_PING, &resp, 1000)) { + tms = msclock() - tms; if (len) { bool error = (memcmp(data, resp.data.asBytes, len) != 0); - PrintAndLogEx((error) ? ERR : SUCCESS, "Ping response " _GREEN_("received") " and content () %s )", error ? _RED_("fail") : _GREEN_("ok")); + PrintAndLogEx((error) ? ERR : SUCCESS, "Ping response " _GREEN_("received") + " in " _YELLOW_("%" PRIu64) " ms and content ( %s )", + tms, error ? _RED_("fail") : _GREEN_("ok")); } else { - PrintAndLogEx(SUCCESS, "Ping response " _GREEN_("received")); + PrintAndLogEx(SUCCESS, "Ping response " _GREEN_("received") + " in " _YELLOW_("%" PRIu64) " ms", tms); } } else PrintAndLogEx(WARNING, "Ping response " _RED_("timeout")); @@ -1042,11 +1101,48 @@ static int CmdBreak(const char *Cmd) { return PM3_SUCCESS; } +static int CmdBootloader(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hw bootloader", + "Reboot Proxmark3 into bootloader mode", + "hw bootloader\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + clearCommandBuffer(); + flash_reboot_bootloader(g_conn.serial_port_name, false); + return PM3_SUCCESS; +} + +int set_fpga_mode(uint8_t mode) { + if (mode < FPGA_BITSTREAM_LF || mode > FPGA_BITSTREAM_HF_15) { + return PM3_EINVARG; + } + uint8_t d[] = {mode}; + clearCommandBuffer(); + SendCommandNG(CMD_SET_FPGAMODE, d, sizeof(d)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_SET_FPGAMODE, &resp, 1000) == false) { + PrintAndLogEx(WARNING, "command execution timeout"); + return PM3_ETIMEOUT; + } + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to set FPGA mode"); + } + return resp.status; +} static command_t CommandTable[] = { {"-------------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Hardware") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, {"break", CmdBreak, IfPm3Present, "Send break loop usb command"}, + {"bootloader", CmdBootloader, IfPm3Present, "Reboot Proxmark3 into bootloader mode"}, {"connect", CmdConnect, AlwaysAvailable, "Connect Proxmark3 to serial port"}, {"dbg", CmdDbg, IfPm3Present, "Set Proxmark3 debug level"}, {"detectreader", CmdDetectReader, IfPm3Present, "Detect external reader field"}, @@ -1062,6 +1158,7 @@ static command_t CommandTable[] = { {"status", CmdStatus, IfPm3Present, "Show runtime status information about the connected Proxmark3"}, {"tearoff", CmdTearoff, IfPm3Present, "Program a tearoff hook for the next command supporting tearoff"}, {"tia", CmdTia, IfPm3Present, "Trigger a Timing Interval Acquisition to re-adjust the RealTimeCounter divider"}, + {"timeout", CmdTimeout, AlwaysAvailable, "Set the communication timeout on the client side"}, {"tune", CmdTune, IfPm3Present, "Measure antenna tuning"}, {"version", CmdVersion, AlwaysAvailable, "Show version information about the client and the connected Proxmark3, if any"}, {NULL, NULL, NULL, NULL} @@ -1234,7 +1331,7 @@ void pm3_version(bool verbose, bool oneliner) { return; PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("Proxmark3 RFID instrument") " ]"); - PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("CLIENT") " ]"); + PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("Client") " ]"); FormatVersionInformation(temp, sizeof(temp), " ", &g_version_information); PrintAndLogEx(NORMAL, "%s", temp); PrintAndLogEx(NORMAL, " compiled with............. " PM3CLIENTCOMPILER __VERSION__); @@ -1273,7 +1370,7 @@ void pm3_version(bool verbose, bool oneliner) { #endif if (g_session.pm3_present) { - PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("PROXMARK3") " ]"); + PrintAndLogEx(NORMAL, "\n [ " _YELLOW_("Proxmark3") " ]"); PacketResponseNG resp; clearCommandBuffer(); @@ -1333,7 +1430,7 @@ void pm3_version(bool verbose, bool oneliner) { } } PrintAndLogEx(NORMAL, payload->versionstr); - if (strstr(payload->versionstr, "2s30vq100") == NULL) { + if (strstr(payload->versionstr, FPGA_TYPE) == NULL) { PrintAndLogEx(NORMAL, " FPGA firmware... %s", _RED_("chip mismatch")); } diff --git a/client/src/cmdhw.h b/client/src/cmdhw.h index 4756a7cb0..a2b4f96e1 100644 --- a/client/src/cmdhw.h +++ b/client/src/cmdhw.h @@ -27,5 +27,5 @@ int CmdHW(const char *Cmd); int handle_tearoff(tearoff_params_t *params, bool verbose); void pm3_version(bool verbose, bool oneliner); void pm3_version_short(void); - +int set_fpga_mode(uint8_t mode); #endif diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index 848f6cab0..11b48d6b7 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +// //----------------------------------------------------------------------------- // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. // // This program is free software: you can redistribute it and/or modify @@ -21,6 +21,7 @@ #include #include #include +#include #include "cmdparser.h" // command_t #include "comms.h" #include "commonutil.h" // ARRAYLEN @@ -30,6 +31,7 @@ #include "cliparser.h" // args parsing #include "graph.h" // for graph data #include "cmddata.h" // for `lf search` +#include "cmdhw.h" // for setting FPGA image #include "cmdlfawid.h" // for awid menu #include "cmdlfem.h" // for em menu #include "cmdlfem410x.h" // for em4x menu @@ -66,8 +68,6 @@ #include "crc.h" #include "pm3_cmd.h" // for LF_CMDREAD_MAX_EXTRA_SYMBOLS -static bool gs_lf_threshold_set = false; - static int CmdHelp(const char *Cmd); // Informative user function. @@ -75,7 +75,7 @@ static int CmdHelp(const char *Cmd); // if key event, send break loop cmd to Pm3 int lfsim_wait_check(uint32_t cmd) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to abort simulation"); for (;;) { if (kbd_enter_pressed()) { @@ -160,7 +160,8 @@ static int CmdLFTune(const char *Cmd) { if (is_value) style = STYLE_VALUE; - PrintAndLogEx(INFO, "Measuring LF antenna at " _YELLOW_("%.2f") " kHz, click " _GREEN_("pm3 button") " or press " _GREEN_("Enter") " to exit", LF_DIV2FREQ(divisor)); + PrintAndLogEx(INFO, "Measuring LF antenna at " _YELLOW_("%.2f") " kHz", LF_DIV2FREQ(divisor)); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to exit"); uint8_t params[] = {1, 0}; params[1] = divisor; @@ -352,18 +353,21 @@ int CmdLFCommandRead(const char *Cmd) { } } - PrintAndLogEx(DEBUG, "Cmd read - settings"); + PrintAndLogEx(DEBUG, _CYAN_("Cmd read - settings")); PrintAndLogEx(DEBUG, "-------------------"); - PrintAndLogEx(DEBUG, "delay: %u , zero %u , one %u , samples %u", payload.delay, payload.period_0, payload.period_1, payload.samples); - PrintAndLogEx(DEBUG, "Extra symbols"); + PrintAndLogEx(DEBUG, "delay... " _YELLOW_("%u")" zero... " _YELLOW_("%u") " one... " _YELLOW_("%u")" samples... %u", payload.delay, payload.period_0, payload.period_1, payload.samples); + PrintAndLogEx(DEBUG, ""); + PrintAndLogEx(DEBUG, _CYAN_("Extra symbols")); PrintAndLogEx(DEBUG, "-------------"); for (i = 0; i < LF_CMDREAD_MAX_EXTRA_SYMBOLS; i++) { if (payload.symbol_extra[i] == 0x00) continue; - PrintAndLogEx(DEBUG, " %c - %u", payload.symbol_extra[i], payload.period_extra[i]); + PrintAndLogEx(DEBUG, " %c ... " _YELLOW_("%u"), payload.symbol_extra[i], payload.period_extra[i]); } - PrintAndLogEx(DEBUG, "data: %s", payload.data); + PrintAndLogEx(DEBUG, ""); + PrintAndLogEx(DEBUG, "data... " _YELLOW_("%s"), payload.data); + PrintAndLogEx(DEBUG, ""); if (cm) { PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); @@ -379,6 +383,17 @@ int CmdLFCommandRead(const char *Cmd) { SendCommandNG(CMD_LF_MOD_THEN_ACQ_RAW_ADC, (uint8_t *)&payload, PAYLOAD_HEADER_SIZE + cmd_len); PacketResponseNG resp; + // init to ZERO + resp.cmd = 0, + resp.length = 0, + resp.magic = 0, + resp.status = 0, + resp.crc = 0, + resp.ng = false, + resp.oldarg[0] = 0; + resp.oldarg[1] = 0; + resp.oldarg[2] = 0; + memset(resp.data.asBytes, 0, PM3_CMD_DATA_SIZE); i = 10; // 20sec wait loop @@ -419,7 +434,11 @@ int CmdFlexdemod(const char *Cmd) { #endif int i, j, start, bit, sum; - int data[g_GraphTraceLen]; + int *data = calloc(g_GraphTraceLen, sizeof(int)); + if (data == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } memcpy(data, g_GraphBuffer, g_GraphTraceLen); size_t size = g_GraphTraceLen; @@ -440,6 +459,7 @@ int CmdFlexdemod(const char *Cmd) { if (start == size - LONG_WAIT) { PrintAndLogEx(WARNING, "nothing to wait for"); + free(data); return PM3_ENODATA; } @@ -455,7 +475,7 @@ int CmdFlexdemod(const char *Cmd) { sum += data[i++]; } bits[bit] = (sum > 0) ? 1 : 0; - PrintAndLogEx(NORMAL, "bit %d sum %d", bit, sum); + // PrintAndLogEx(NORMAL, "bit %d sum %d", bit, sum); } for (bit = 0; bit < 64; bit++) { @@ -483,6 +503,7 @@ int CmdFlexdemod(const char *Cmd) { } } RepaintGraphWindow(); + free(data); return PM3_SUCCESS; } @@ -623,16 +644,6 @@ int CmdLFConfig(const char *Cmd) { .verbose = true }; - if (reset) { - config.decimation = 1; - config.bits_per_sample = 8; - config.averaging = 1, - config.divisor = LF_DIVISOR_125; - config.samples_to_skip = 0; - config.trigger_threshold = 0; - gs_lf_threshold_set = false; - } - if (use_125) config.divisor = LF_DIVISOR_125; @@ -675,49 +686,106 @@ int CmdLFConfig(const char *Cmd) { if (trigg > -1) { config.trigger_threshold = trigg; - gs_lf_threshold_set = (config.trigger_threshold > 0); } config.samples_to_skip = skip; + + if (reset) { + config.decimation = 1; + config.bits_per_sample = 8; + config.averaging = 1, + config.divisor = LF_DIVISOR_125; + config.samples_to_skip = 0; + config.trigger_threshold = 0; + } + return lf_config(&config); } -int lf_read(bool verbose, uint32_t samples) { +static int lf_read_internal(bool realtime, bool verbose, uint64_t samples) { if (!g_session.pm3_present) return PM3_ENOTTY; - struct p { - uint32_t samples : 31; - bool verbose : 1; - } PACKED; - - struct p payload; + lf_sample_payload_t payload = {0}; + payload.realtime = realtime; payload.verbose = verbose; - payload.samples = samples; + sample_config current_config; + int retval = lf_getconfig(¤t_config); + if (retval != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to get current device config"); + return retval; + } clearCommandBuffer(); - SendCommandNG(CMD_LF_ACQ_RAW_ADC, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (gs_lf_threshold_set) { - WaitForResponse(CMD_LF_ACQ_RAW_ADC, &resp); - } else { - if (!WaitForResponseTimeout(CMD_LF_ACQ_RAW_ADC, &resp, 2500)) { - PrintAndLogEx(WARNING, "(lf_read) command execution time out"); - return PM3_ETIMEOUT; + const uint8_t bits_per_sample = current_config.bits_per_sample; + const bool is_trigger_threshold_set = (current_config.trigger_threshold > 0); + + if (realtime) { + uint8_t *realtimeBuf = calloc(samples, sizeof(uint8_t)); + if (realtimeBuf == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; } + + size_t sample_bytes = samples * bits_per_sample; + sample_bytes = (sample_bytes / 8) + (sample_bytes % 8 != 0); + + // In real-time mode, the LF bitstream should be loaded before receiving raw data. + // Otherwise, the first batch of raw data might contain the response of CMD_WTX. + int result = set_fpga_mode(FPGA_BITSTREAM_LF); + if (result != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "failed to load LF bitstream to FPGA"); + return result; + } + + SendCommandNG(CMD_LF_ACQ_RAW_ADC, (uint8_t *)&payload, sizeof(payload)); + if (is_trigger_threshold_set) { + size_t first_receive_len = 32; + // Wait until a bunch of data arrives + first_receive_len = WaitForRawDataTimeout(realtimeBuf, first_receive_len, -1, false); + sample_bytes = WaitForRawDataTimeout(realtimeBuf + first_receive_len, sample_bytes - first_receive_len, 1000, true); + sample_bytes += first_receive_len; + } else { + sample_bytes = WaitForRawDataTimeout(realtimeBuf, sample_bytes, 1000, true); + } + samples = sample_bytes * 8 / bits_per_sample; + PrintAndLogEx(INFO, "Done: %" PRIu64 " samples (%zu bytes)", samples, sample_bytes); + if (samples != 0) { + getSamplesFromBufEx(realtimeBuf, samples, bits_per_sample, verbose); + } + + free(realtimeBuf); + } else { + payload.samples = (samples > MAX_LF_SAMPLES) ? MAX_LF_SAMPLES : samples; + SendCommandNG(CMD_LF_ACQ_RAW_ADC, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (is_trigger_threshold_set) { + WaitForResponse(CMD_LF_ACQ_RAW_ADC, &resp); + } else { + if (!WaitForResponseTimeout(CMD_LF_ACQ_RAW_ADC, &resp, 2500)) { + PrintAndLogEx(WARNING, "(lf_read) command execution time out"); + return PM3_ETIMEOUT; + } + } + // response is number of bits read + uint32_t size = (resp.data.asDwords[0] / bits_per_sample); + getSamples(size, verbose); } - // response is number of bits read - uint32_t size = (resp.data.asDwords[0] / 8); - getSamples(size, verbose); return PM3_SUCCESS; } +int lf_read(bool verbose, uint64_t samples) { + return lf_read_internal(false, verbose, samples); +} + int CmdLFRead(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf read", "Sniff low frequency signal.\n" " - use " _YELLOW_("`lf config`") _CYAN_(" to set parameters.\n") - _CYAN_(" - use ") _YELLOW_("`data plot`") _CYAN_(" to look at it"), + _CYAN_(" - use ") _YELLOW_("`data plot`") _CYAN_(" to look at it.\n") + _CYAN_("If the number of samples is more than the device memory limit (40000 now), ") + _CYAN_("it will try to use the real-time sampling mode."), "lf read -v -s 12000 --> collect 12000 samples\n" "lf read -s 3000 -@ --> oscilloscope style \n" ); @@ -730,50 +798,105 @@ int CmdLFRead(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint32_t samples = arg_get_u32_def(ctx, 1, 0); + uint64_t samples = arg_get_u64_def(ctx, 1, 0); bool verbose = arg_get_lit(ctx, 2); bool cm = arg_get_lit(ctx, 3); CLIParserFree(ctx); + // the 40000 there should be the result of BigBuf_max_traceLen(), + // but IDK how to get it. + bool realtime = samples > 40000; + if (g_session.pm3_present == false) return PM3_ENOTTY; - if (cm) { + if (cm || realtime) { PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); } int ret = PM3_SUCCESS; do { - ret = lf_read(verbose, samples); + ret = lf_read_internal(realtime, verbose, samples); } while (cm && kbd_enter_pressed() == false); + + if (ret == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "Got " _YELLOW_("%zu") " samples", g_GraphTraceLen); + + if (getSignalProperties()->isnoise) { + PrintAndLogEx(INFO, "signal looks like noise"); + } + } return ret; } -int lf_sniff(bool verbose, uint32_t samples) { +int lf_sniff(bool realtime, bool verbose, uint64_t samples) { if (!g_session.pm3_present) return PM3_ENOTTY; - struct p { - uint32_t samples : 31; - bool verbose : 1; - } PACKED payload; - - payload.samples = (samples & 0xFFFF); + lf_sample_payload_t payload = {0}; + payload.realtime = realtime; payload.verbose = verbose; + sample_config current_config; + int retval = lf_getconfig(¤t_config); + if (retval != PM3_SUCCESS) { + PrintAndLogEx(ERR, "failed to get current device config"); + return retval; + } clearCommandBuffer(); - SendCommandNG(CMD_LF_SNIFF_RAW_ADC, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (gs_lf_threshold_set) { - WaitForResponse(CMD_LF_SNIFF_RAW_ADC, &resp); - } else { - if (WaitForResponseTimeout(CMD_LF_SNIFF_RAW_ADC, &resp, 2500) == false) { - PrintAndLogEx(WARNING, "(lf_read) command execution time out"); - return PM3_ETIMEOUT; + const uint8_t bits_per_sample = current_config.bits_per_sample; + const bool is_trigger_threshold_set = (current_config.trigger_threshold > 0); + + if (realtime) { + uint8_t *realtimeBuf = calloc(samples, sizeof(uint8_t)); + if (realtimeBuf == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; } + + size_t sample_bytes = samples * bits_per_sample; + sample_bytes = (sample_bytes / 8) + (sample_bytes % 8 != 0); + + // In real-time mode, the LF bitstream should be loaded before receiving raw data. + // Otherwise, the first batch of raw data might contain the response of CMD_WTX. + int result = set_fpga_mode(FPGA_BITSTREAM_LF); + if (result != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "failed to load LF bitstream to FPGA"); + return result; + } + + SendCommandNG(CMD_LF_SNIFF_RAW_ADC, (uint8_t *)&payload, sizeof(payload)); + if (is_trigger_threshold_set) { + size_t first_receive_len = 32; + // Wait until a bunch of data arrives + first_receive_len = WaitForRawDataTimeout(realtimeBuf, first_receive_len, -1, false); + sample_bytes = WaitForRawDataTimeout(realtimeBuf + first_receive_len, sample_bytes - first_receive_len, 1000, true); + sample_bytes += first_receive_len; + } else { + sample_bytes = WaitForRawDataTimeout(realtimeBuf, sample_bytes, 1000, true); + } + samples = sample_bytes * 8 / bits_per_sample; + PrintAndLogEx(INFO, "Done: %" PRIu64 " samples (%zu bytes)", samples, sample_bytes); + if (samples != 0) { + getSamplesFromBufEx(realtimeBuf, samples, bits_per_sample, verbose); + } + + free(realtimeBuf); + } else { + payload.samples = (samples > MAX_LF_SAMPLES) ? MAX_LF_SAMPLES : samples; + SendCommandNG(CMD_LF_SNIFF_RAW_ADC, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (is_trigger_threshold_set) { + WaitForResponse(CMD_LF_SNIFF_RAW_ADC, &resp); + } else { + if (WaitForResponseTimeout(CMD_LF_SNIFF_RAW_ADC, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "(lf_read) command execution time out"); + return PM3_ETIMEOUT; + } + } + // response is number of bits read + uint32_t size = (resp.data.asDwords[0] / bits_per_sample); + getSamples(size, verbose); } - // response is number of bits read - uint32_t size = (resp.data.asDwords[0] / 8); - getSamples(size, verbose); return PM3_SUCCESS; } @@ -785,7 +908,9 @@ int CmdLFSniff(const char *Cmd) { "\n" " - use " _YELLOW_("`lf config`") _CYAN_(" to set parameters.\n") _CYAN_(" - use ") _YELLOW_("`data plot`") _CYAN_(" to look at sniff signal.\n") - _CYAN_(" - use ") _YELLOW_("`lf search -1`") _CYAN_(" to see if signal can be automatic decoded\n"), + _CYAN_(" - use ") _YELLOW_("`lf search -1`") _CYAN_(" to see if signal can be automatic decoded.\n") + _CYAN_("If the number of samples is more than the device memory limit (40000 now), ") + _CYAN_("it will try to use the real-time sampling mode."), "lf sniff -v\n" "lf sniff -s 3000 -@ --> oscilloscope style \n" ); @@ -798,21 +923,25 @@ int CmdLFSniff(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint32_t samples = (arg_get_u32_def(ctx, 1, 0) & 0xFFFF); + uint64_t samples = arg_get_u64_def(ctx, 1, 0); bool verbose = arg_get_lit(ctx, 2); bool cm = arg_get_lit(ctx, 3); CLIParserFree(ctx); + // the 40000 there should be the result of BigBuf_max_traceLen(), + // but IDK how to get it. + bool realtime = samples > 40000; + if (g_session.pm3_present == false) return PM3_ENOTTY; - if (cm) { + if (cm || realtime) { PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); } int ret = PM3_SUCCESS; do { - ret = lf_sniff(verbose, samples); - } while (cm && !kbd_enter_pressed()); + ret = lf_sniff(realtime, verbose, samples); + } while (cm && kbd_enter_pressed() == false); return ret; } @@ -1377,7 +1506,7 @@ int CmdVchDemod(const char *Cmd) { } */ -static bool CheckChipType(bool getDeviceData) { +static bool check_chiptype(bool getDeviceData) { bool retval = false; @@ -1428,22 +1557,61 @@ out: return retval; } +static int check_autocorrelate(const char *prefix, int clock) { + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, _CYAN_("%s - auto correlations"), prefix); + for (int win = 2000; win < 30000; win += 2000) { + int samples = AutoCorrelate(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen, win, false, false); + if (samples == -1) { + continue; + } + + if ((samples > 1) && (clock > 0)) { + + // If we got a field clock / bit rate from before + // we can use it for predict number of repeating bytes within + + int bytes = (samples / (8 * clock)); + int mod = (bytes % 4); + if (mod) + bytes++; + + int blocks = (bytes / 4); + PrintAndLogEx(SUCCESS, " " _YELLOW_("%d") " samples / 8 bits / " _YELLOW_("%d") " clock", samples, clock); + PrintAndLogEx(SUCCESS, " " _YELLOW_("%d") " bytes repeating", bytes); + + if (blocks < 7) { + PrintAndLogEx(SUCCESS, " " _YELLOW_("%d") " block%c", blocks, (blocks == 1) ? ' ' : 's'); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } else { + PrintAndLogEx(INFO, " " _YELLOW_("%d") " blocks ( too large for T5577 )", blocks); + PrintAndLogEx(NORMAL, ""); + return PM3_EFAILED; + } + } + } + PrintAndLogEx(NORMAL, ""); + return PM3_EFAILED; +} + int CmdLFfind(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf search", "Read and search for valid known tag. For offline mode, you can `data load` first then search.", "lf search -> try reading data from tag & search for known tag\n" - "lf search -1 -> use data from the GraphBuffer & search for known tag\n" "lf search -u -> try reading data from tag & search for known and unknown tag\n" - "lf search -1u -> use data from the GraphBuffer & search for known and unknown tag\n" + "lf search -1 -> use data from the GraphBuffer & search for known tag\n" + "lf search -1uc -> use data from the GraphBuffer & search for known and unknown tag\n" ); void *argtable[] = { arg_param_begin, - arg_lit0("1", NULL, "Use data from Graphbuffer to search"), - arg_lit0("c", NULL, "Continue searching even after a first hit"), - arg_lit0("u", NULL, "Search for unknown tags. If not set, reads only known tags"), + arg_lit0("1", NULL, "Use data from Graphbuffer to search (offline mode)"), + arg_lit0("c", NULL, "Continue searching after successful match"), + arg_lit0("u", NULL, "Search for unknown tags"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1463,15 +1631,13 @@ int CmdLFfind(const char *Cmd) { } if (search_cont) { - PrintAndLogEx(INFO, "Continuous search enabled"); + PrintAndLogEx(INFO, "Continue searching after successful match"); } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "NOTE: some demods output possible binary"); - PrintAndLogEx(INFO, "if it finds something that looks like a tag"); - PrintAndLogEx(INFO, "False Positives " _YELLOW_("ARE") " possible"); + PrintAndLogEx(INFO, "Note: False Positives " _YELLOW_("ARE") " possible"); PrintAndLogEx(INFO, ""); - PrintAndLogEx(INFO, "Checking for known tags..."); + PrintAndLogEx(INFO, _CYAN_("Checking for known tags...")); PrintAndLogEx(INFO, ""); // only run these tests if device is online @@ -1680,7 +1846,7 @@ int CmdLFfind(const char *Cmd) { goto out; } } - if (demodParadox(true) == PM3_SUCCESS) { + if (demodParadox(true, false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Paradox ID") " found!"); if (search_cont) { found++; @@ -1745,54 +1911,97 @@ int CmdLFfind(const char *Cmd) { } if (search_unk) { - //test unknown tag formats (raw mode) - PrintAndLogEx(INFO, "\nChecking for unknown tags:\n"); - int ans = AutoCorrelate(g_GraphBuffer, g_GraphBuffer, g_GraphTraceLen, 8000, false, false); - if (ans > 0) { - PrintAndLogEx(INFO, "Possible auto correlation of %d repeating samples", ans); + // test unknown tag formats (raw mode) + PrintAndLogEx(INFO, _CYAN_("Checking for unknown tags...") "\n"); - if (ans % 8 == 0) - PrintAndLogEx(INFO, "Possible %d bytes", (ans / 8)); + uint8_t ones[] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + }; + + // FSK + PrintAndLogEx(INFO, "FSK clock.......... " NOLF); + int clock = GetFskClock("", false); + if (clock) { + PrintAndLogEx(NORMAL, _GREEN_("detected")); + if (FSKrawDemod(0, 0, 0, 0, true) == PM3_SUCCESS) { + check_autocorrelate("FSK", clock); + found++; + } else { + PrintAndLogEx(INFO, "FSK demodulation... " _RED_("failed")); + } + } else { + PrintAndLogEx(NORMAL, _RED_("no")); } - //fsk - if (GetFskClock("", false)) { - if (FSKrawDemod(0, 0, 0, 0, true) == PM3_SUCCESS) { - PrintAndLogEx(INFO, "Unknown FSK Modulated Tag found!"); - if (search_cont) { + // ASK + PrintAndLogEx(INFO, "ASK clock.......... " NOLF); + clock = GetAskClock("", false); + if (clock && clock > 8) { + PrintAndLogEx(NORMAL, _GREEN_("detected")); + bool st = true; + if (ASKDemod_ext(0, 0, 0, 0, false, true, false, 1, &st) == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, _GREEN_("ASK") " modulation / Manchester encoding detected!"); + PrintAndLogEx(INFO, " could also be ASK/Biphase - try " _YELLOW_("'data rawdemod --ab'")); + check_autocorrelate("ASK", clock); + found++; + } else { + PrintAndLogEx(INFO, "ASK demodulation... " _RED_("failed")); + } + } else { + PrintAndLogEx(NORMAL, _RED_("no")); + } + + // NZR + PrintAndLogEx(INFO, "NRZ clock.......... " NOLF); + clock = GetNrzClock("", false); + if (clock && clock > 8) { + PrintAndLogEx(NORMAL, _GREEN_("detected")); + if (NRZrawDemod(0, 0, 0, true) == PM3_SUCCESS) { + + int min = MIN(g_DemodBufferLen, sizeof(ones)); + // if demodulated binary is only 1, skip autocorrect + if (memcmp(g_DemodBuffer, ones, min) != 0) { + check_autocorrelate("NRZ", clock); found++; } else { - goto out; + PrintAndLogEx(INFO, "NRZ ............... " _RED_("false positive")); + PrintAndLogEx(NORMAL, ""); } + } else { + PrintAndLogEx(INFO, "NRZ demodulation... " _RED_("failed")); } + } else { + PrintAndLogEx(NORMAL, _RED_("no")); } - bool st = true; - if (ASKDemod_ext(0, 0, 0, 0, false, true, false, 1, &st) == PM3_SUCCESS) { - PrintAndLogEx(INFO, "Unknown ASK Modulated and Manchester encoded Tag found!"); - PrintAndLogEx(INFO, "if it does not look right it could instead be ASK/Biphase - try " _YELLOW_("'data rawdemod --ab'")); - if (search_cont) { + // PSK + PrintAndLogEx(INFO, "PSK clock.......... " NOLF); + clock = GetPskClock("", false); + if (clock) { + PrintAndLogEx(NORMAL, _GREEN_("detected")); + if (CmdPSK1rawDemod("") == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Possible " _GREEN_("PSK1") " modulation detected!"); + PrintAndLogEx(INFO, " Could also be PSK2 - try " _YELLOW_("'data rawdemod --p2'")); + PrintAndLogEx(INFO, " Could also be PSK3 - [currently not supported]"); + PrintAndLogEx(INFO, " Could also be NRZ - try " _YELLOW_("'data rawdemod --nr")); + check_autocorrelate("PSK", clock); found++; } else { - goto out; - } - } - - if (CmdPSK1rawDemod("") == PM3_SUCCESS) { - PrintAndLogEx(INFO, "Possible unknown PSK1 Modulated Tag found above!"); - PrintAndLogEx(INFO, " Could also be PSK2 - try " _YELLOW_("'data rawdemod --p2'")); - PrintAndLogEx(INFO, " Could also be PSK3 - [currently not supported]"); - PrintAndLogEx(INFO, " Could also be NRZ - try " _YELLOW_("'data rawdemod --nr")); - if (search_cont) { - found++; - } else { - goto out; + PrintAndLogEx(INFO, "PSK demodulation... " _RED_("failed")); } + } else { + PrintAndLogEx(NORMAL, _RED_("no")); } if (found == 0) { - PrintAndLogEx(FAILED, _RED_("No data found!")); + PrintAndLogEx(FAILED, _RED_("Failed to demodulated signal")); } } @@ -1802,7 +2011,7 @@ int CmdLFfind(const char *Cmd) { out: // identify chipset - if (CheckChipType(is_online) == false) { + if (check_chiptype(is_online) == false) { PrintAndLogEx(DEBUG, "Automatic chip type detection " _RED_("failed")); } return retval; @@ -1825,7 +2034,7 @@ static command_t CommandTable[] = { {"io", CmdLFIO, AlwaysAvailable, "{ ioProx RFIDs... }"}, {"jablotron", CmdLFJablotron, AlwaysAvailable, "{ Jablotron RFIDs... }"}, {"keri", CmdLFKeri, AlwaysAvailable, "{ KERI RFIDs... }"}, - {"motorola", CmdLFMotorola, AlwaysAvailable, "{ Motorola RFIDs... }"}, + {"motorola", CmdLFMotorola, AlwaysAvailable, "{ Motorola Flexpass RFIDs... }"}, {"nedap", CmdLFNedap, AlwaysAvailable, "{ Nedap RFIDs... }"}, {"nexwatch", CmdLFNEXWATCH, AlwaysAvailable, "{ NexWatch RFIDs... }"}, {"noralsy", CmdLFNoralsy, AlwaysAvailable, "{ Noralsy RFIDs... }"}, diff --git a/client/src/cmdlf.h b/client/src/cmdlf.h index 3bcd3ecf2..b29fd137f 100644 --- a/client/src/cmdlf.h +++ b/client/src/cmdlf.h @@ -40,8 +40,8 @@ int CmdLFSniff(const char *Cmd); int CmdVchDemod(const char *Cmd); int CmdLFfind(const char *Cmd); -int lf_read(bool verbose, uint32_t samples); -int lf_sniff(bool verbose, uint32_t samples); +int lf_read(bool verbose, uint64_t samples); +int lf_sniff(bool realtime, bool verbose, uint64_t samples); int lf_config(sample_config *config); int lf_getconfig(sample_config *config); int lfsim_upload_gb(void); diff --git a/client/src/cmdlfawid.c b/client/src/cmdlfawid.c index 5d735a650..bdb8d008e 100644 --- a/client/src/cmdlfawid.c +++ b/client/src/cmdlfawid.c @@ -132,7 +132,7 @@ static int CmdAWIDWatch(const char *Cmd) { CLIParserFree(ctx); PrintAndLogEx(SUCCESS, "Watching for AWID cards - place tag on antenna"); - PrintAndLogEx(INFO, "Press pm3-button to stop reading cards"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to stop reading cards"); clearCommandBuffer(); SendCommandNG(CMD_LF_AWID_WATCH, NULL, 0); return lfsim_wait_check(CMD_LF_AWID_WATCH); @@ -524,7 +524,7 @@ static int CmdAWIDBrute(const char *Cmd) { } PrintAndLogEx(SUCCESS, "Bruteforceing AWID %d reader", fmtlen); - PrintAndLogEx(SUCCESS, "Press pm3-button to abort simulation or press Enter"); + PrintAndLogEx(SUCCESS, "Press " _GREEN_("pm3 button") " to abort simulation or press " _GREEN_("")); uint16_t up = cn; uint16_t down = cn; diff --git a/client/src/cmdlfcotag.c b/client/src/cmdlfcotag.c index 7c4bf1541..2306b0024 100644 --- a/client/src/cmdlfcotag.c +++ b/client/src/cmdlfcotag.c @@ -25,6 +25,7 @@ #include "ui.h" // PrintAndLog #include "ctype.h" // tolower #include "cliparser.h" +#include "commonutil.h" // reflect32 static int CmdHelp(const char *Cmd); @@ -37,6 +38,9 @@ int demodCOTAG(bool verbose) { size_t bitlen = COTAG_BITS; memcpy(bits, g_DemodBuffer, COTAG_BITS); + uint8_t inv_bits[COTAG_BITS] = {0}; + memcpy(inv_bits, g_DemodBuffer, COTAG_BITS); + uint8_t alignPos = 0; uint16_t err = manrawdecode(bits, &bitlen, 1, &alignPos); if (err > 50) { @@ -55,14 +59,38 @@ int demodCOTAG(bool verbose) { uint32_t raw3 = bytebits_to_byteLSBF(bits + 64, 32); uint32_t raw4 = bytebits_to_byteLSBF(bits + 96, 32); + /* fc 161: 1010 0001 -> LSB 1000 0101 cn 33593 1000 0011 0011 1001 -> LSB 1001 1100 1100 0001 cccc cccc cccc cccc ffffffff 0 1001 1100 1100 0001 1000 0101 0000 0000 100001010000000001111011100000011010000010000000000000000000000000000000000000000000000000000000100111001100000110000101000 1001 1100 1100 0001 10000101 + + COTAG FC/272 + 1 7 7 D E 2 0 0 8 0 0 0 3 9 2 0 D 0 4 0000000000000 + 0001 0111 0111 1101 1110 0010 0000 0000 1000 0000 0000 0000 0011 1001 0010 0000 1101 0000 0100 0000000000000000000000000000000000000000000000000000000 + 0001 0111 0111 1101 1110 001 0010 1001 0011 1000 0110 0100 + */ - PrintAndLogEx(SUCCESS, "COTAG Found: FC %u, CN: %u Raw: %08X%08X%08X%08X", fc, cn, raw1, raw2, raw3, raw4); + PrintAndLogEx(SUCCESS, "COTAG Found: FC " _GREEN_("%u")", CN: " _GREEN_("%u")" Raw: %08X%08X%08X%08X", fc, cn, raw1, raw2, raw3, raw4); + + bitlen = COTAG_BITS; + err = manrawdecode(inv_bits, &bitlen, 0, &alignPos); + if (err < 50) { + uint32_t cn_large = bytebits_to_byte(inv_bits + 1, 23); + cn_large = reflect32(cn_large) >> 9; + uint8_t a = bytebits_to_byte(inv_bits + 48, 4); + uint8_t b = bytebits_to_byte(inv_bits + 52, 4); + uint8_t c = bytebits_to_byte(inv_bits + 56, 4); + uint16_t fc_large = NIBBLE_LOW(c) << 8 | NIBBLE_LOW(b) << 4 | NIBBLE_LOW(a); + + raw1 = bytebits_to_byte(inv_bits, 32); + raw2 = bytebits_to_byte(inv_bits + 32, 32); + raw3 = bytebits_to_byte(inv_bits + 64, 32); + raw4 = bytebits_to_byte(inv_bits + 96, 32); + PrintAndLogEx(SUCCESS, " FC " _GREEN_("%u")", CN: " _GREEN_("%u")" Raw: %08X%08X%08X%08X", fc_large, cn_large, raw1, raw2, raw3, raw4); + } return PM3_SUCCESS; } @@ -75,11 +103,13 @@ static int CmdCOTAGDemod(const char *Cmd) { void *argtable[] = { arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); CLIParserFree(ctx); - return demodCOTAG(true); + return demodCOTAG(verbose); } // When reading a COTAG. @@ -152,7 +182,7 @@ static int CmdCOTAGReader(const char *Cmd) { case 0: case 2: { CmdPlot(""); - CmdGrid("384"); + CmdGrid("-x 384"); getSamples(0, false); break; } diff --git a/client/src/cmdlfcotag.h b/client/src/cmdlfcotag.h index 9577dabdb..11f0b28ce 100644 --- a/client/src/cmdlfcotag.h +++ b/client/src/cmdlfcotag.h @@ -20,6 +20,7 @@ #define CMDLFCOTAG_H__ #include "common.h" +#include #ifndef COTAG_BITS #define COTAG_BITS 264 diff --git a/client/src/cmdlfdestron.c b/client/src/cmdlfdestron.c index aaca79ff8..0f0b782c5 100644 --- a/client/src/cmdlfdestron.c +++ b/client/src/cmdlfdestron.c @@ -231,7 +231,7 @@ static int CmdDestronSim(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); CLIParserFree(ctx); PrintAndLogEx(INFO, " To be implemented, feel free to contribute!"); - return PM3_SUCCESS; + return PM3_ENOTIMPL; } static command_t CommandTable[] = { diff --git a/client/src/cmdlfem410x.c b/client/src/cmdlfem410x.c index 29ad61bc9..a8e70fe36 100644 --- a/client/src/cmdlfem410x.c +++ b/client/src/cmdlfem410x.c @@ -528,8 +528,9 @@ static int CmdEM410xBrute(const char *Cmd) { //The line start with # is comment, skip if (buf[0] == '#') continue; - if (param_gethex(buf, 0, uid, 10)) { - PrintAndLogEx(FAILED, "EM Tag IDs must include 5 hex bytes (10 hex symbols)"); + int uidlen = 0; + if (param_gethex_ex(buf, 0, uid, &uidlen) && (uidlen != 10)) { + PrintAndLogEx(FAILED, "EM Tag IDs must include 5 hex bytes (10 hex symbols), got ( " _RED_("%d") " )", uidlen); free(uidblock); fclose(f); return PM3_ESOFT; diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index c21540f45..f93db58d3 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -514,6 +514,7 @@ int CmdEM4x05Dump(const char *Cmd) { arg_param_begin, arg_str0("p", "pwd", "", "password (00000000)"), arg_str0("f", "file", "", "override filename prefix (optional). Default is based on UID"), + arg_lit0(NULL, "ns", "no save to file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -522,6 +523,7 @@ int CmdEM4x05Dump(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool nosave = arg_get_lit(ctx, 3); CLIParserFree(ctx); uint8_t addr = 0; @@ -693,6 +695,13 @@ int CmdEM4x05Dump(const char *Cmd) { } else { } + if (nosave) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + // all ok save dump to file if (success == PM3_SUCCESS) { @@ -709,9 +718,9 @@ int CmdEM4x05Dump(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); if (card_type == EM_4369 || card_type == EM_4469) - pm3_save_dump(filename, (uint8_t *)data, sizeof(data), jsfEM4x69, 4); + pm3_save_dump(filename, (uint8_t *)data, sizeof(data), jsfEM4x69); else - pm3_save_dump(filename, (uint8_t *)data, sizeof(data), jsfEM4x05, 4); + pm3_save_dump(filename, (uint8_t *)data, sizeof(data), jsfEM4x05); } PrintAndLogEx(NORMAL, ""); return success; @@ -741,16 +750,16 @@ int CmdEM4x05Read(const char *Cmd) { bool use_pwd = false; if (addr > 15) { - PrintAndLogEx(ERR, "Address must be between 0 and 15"); + PrintAndLogEx(ERR, "Address must be between 0 and 15, got " _RED_("%d"), addr); return PM3_EINVARG; } if (inputpwd == 0xFFFFFFFFFFFFFFFF) { - PrintAndLogEx(INFO, "Reading address %02u", addr); + PrintAndLogEx(INFO, "Reading address " _YELLOW_("%02u"), addr); } else { pwd = (inputpwd & 0xFFFFFFFF); use_pwd = true; - PrintAndLogEx(INFO, "Reading address %02u using password %08X", addr, pwd); + PrintAndLogEx(INFO, "Reading address " _YELLOW_("%02u") " using password " _YELLOW_("%08X"), addr, pwd); } uint32_t word = 0; @@ -818,14 +827,14 @@ int CmdEM4x05Write(const char *Cmd) { if (use_pwd) { if (protect_operation) - PrintAndLogEx(INFO, "Writing protection words data %08X using password %08X", data, pwd); + PrintAndLogEx(INFO, "Writing protection words data " _YELLOW_("%08X") " using password " _YELLOW_("%08X"), data, pwd); else - PrintAndLogEx(INFO, "Writing address %d data %08X using password %08X", addr, data, pwd); + PrintAndLogEx(INFO, "Writing address " _YELLOW_("%d") " data " _YELLOW_("%08X") " using password " _YELLOW_("%08X"), addr, data, pwd); } else { if (protect_operation) - PrintAndLogEx(INFO, "Writing protection words data %08X", data); + PrintAndLogEx(INFO, "Writing protection words data " _YELLOW_("%08X"), data); else - PrintAndLogEx(INFO, "Writing address %d data %08X", addr, data); + PrintAndLogEx(INFO, "Writing address " _YELLOW_("%d") " data " _YELLOW_("%08X"), addr, data); } res = PM3_SUCCESS; @@ -850,7 +859,7 @@ int CmdEM4x05Write(const char *Cmd) { if (status == PM3_SUCCESS) PrintAndLogEx(SUCCESS, "Data written and verified"); else if (status == PM3_EFAILED) - PrintAndLogEx(ERR, "Tag denied %s operation", protect_operation ? "Protect" : "Write"); + PrintAndLogEx(ERR, "Tag denied " _RED_("%s") " operation", protect_operation ? "Protect" : "Write"); else PrintAndLogEx(DEBUG, "No answer from tag"); @@ -2020,7 +2029,7 @@ int CmdEM4x05Sniff(const char *Cmd) { PrintAndLogEx(SUCCESS, "-------+-------------+----------+-----+------------------------------------------------------------"); smartbuf bits = { 0 }; - bits.ptr = malloc(EM4X05_BITS_BUFSIZE); + bits.ptr = calloc(EM4X05_BITS_BUFSIZE, sizeof(uint8_t)); bits.size = EM4X05_BITS_BUFSIZE; bits.idx = 0; size_t idx = 0; @@ -2162,14 +2171,14 @@ static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"brute", CmdEM4x05Brute, IfPm3Lf, "Bruteforce password"}, {"chk", CmdEM4x05Chk, IfPm3Lf, "Check passwords from dictionary"}, - {"demod", CmdEM4x05Demod, AlwaysAvailable, "demodulate a EM4x05/EM4x69 tag from the GraphBuffer"}, - {"dump", CmdEM4x05Dump, IfPm3Lf, "dump EM4x05/EM4x69 tag"}, - {"info", CmdEM4x05Info, IfPm3Lf, "tag information EM4x05/EM4x69"}, - {"read", CmdEM4x05Read, IfPm3Lf, "read word data from EM4x05/EM4x69"}, + {"demod", CmdEM4x05Demod, AlwaysAvailable, "Demodulate a EM4x05/EM4x69 tag from the GraphBuffer"}, + {"dump", CmdEM4x05Dump, IfPm3Lf, "Dump EM4x05/EM4x69 tag"}, + {"info", CmdEM4x05Info, IfPm3Lf, "Tag information"}, + {"read", CmdEM4x05Read, IfPm3Lf, "Read word data from EM4x05/EM4x69"}, {"sniff", CmdEM4x05Sniff, AlwaysAvailable, "Attempt to recover em4x05 commands from sample buffer"}, - {"unlock", CmdEM4x05Unlock, IfPm3Lf, "execute tear off against EM4x05/EM4x69"}, - {"wipe", CmdEM4x05Wipe, IfPm3Lf, "wipe EM4x05/EM4x69 tag"}, - {"write", CmdEM4x05Write, IfPm3Lf, "write word data to EM4x05/EM4x69"}, + {"unlock", CmdEM4x05Unlock, IfPm3Lf, "Execute tear off against EM4x05/EM4x69"}, + {"wipe", CmdEM4x05Wipe, IfPm3Lf, "Wipe EM4x05/EM4x69 tag"}, + {"write", CmdEM4x05Write, IfPm3Lf, "Write word data to EM4x05/EM4x69"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index b210ec73c..2efaa1f0b 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -165,18 +165,24 @@ static int em4x50_load_file(const char *filename, uint8_t *data, size_t data_len static void em4x50_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) { + PrintAndLogEx(INFO, "uploading to emulator memory"); + PrintAndLogEx(INFO, "." NOLF); // fast push mode g_conn.block_after_ACK = true; - for (size_t i = offset; i < numofbytes; i += PM3_CMD_DATA_SIZE) { + for (size_t i = offset; i < numofbytes; i += PM3_CMD_DATA_SIZE_MIX) { - size_t len = MIN((numofbytes - i), PM3_CMD_DATA_SIZE); + size_t len = MIN((numofbytes - i), PM3_CMD_DATA_SIZE_MIX); if (len == numofbytes - i) { // Disable fast mode on last packet g_conn.block_after_ACK = false; } clearCommandBuffer(); - SendCommandOLD(CMD_LF_EM4X50_ESET, i, len, 0, src + i, len); + SendCommandMIX(CMD_LF_EM4X50_ESET, i, len, 0, src + i, len); + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "uploaded " _YELLOW_("%d") " bytes to emulator memory", numofbytes); } int CmdEM4x50ELoad(const char *Cmd) { @@ -188,7 +194,7 @@ int CmdEM4x50ELoad(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "dump filename (bin/eml/json)"), + arg_str1("f", "file", "", "Specify a filename for dump file"), arg_param_end }; @@ -208,9 +214,8 @@ int CmdEM4x50ELoad(const char *Cmd) { } // upload to emulator memory - PrintAndLogEx(INFO, "Uploading to emulator memory contents of " _YELLOW_("%s"), filename); em4x50_seteml(data, 0, DUMP_FILESIZE); - + PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`lf em 4x50 sim -h`")); PrintAndLogEx(INFO, "Done!"); return PM3_SUCCESS; } @@ -218,7 +223,7 @@ int CmdEM4x50ELoad(const char *Cmd) { int CmdEM4x50ESave(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 esave", - "Saves bin/eml/json dump file of emulator memory.", + "Saves bin/json dump file of emulator memory.", "lf em 4x50 esave -> use UID as filename\n" "lf em 4x50 esave -f mydump\n" ); @@ -259,7 +264,7 @@ int CmdEM4x50ESave(const char *Cmd) { FillFileNameByUID(fptr, (uint8_t *)&data[4 * EM4X50_DEVICE_ID], "-dump", 4); } - pm3_save_dump(filename, data, DUMP_FILESIZE, jsfEM4x50, 4); + pm3_save_dump(filename, data, DUMP_FILESIZE, jsfEM4x50); return PM3_SUCCESS; } @@ -386,6 +391,7 @@ int CmdEM4x50Brute(const char *Cmd) { } else { PrintAndLogEx(FAILED, "Unknown bruteforce mode: %s", mode); + CLIParserFree(ctx); return PM3_EINVARG; } @@ -400,11 +406,13 @@ int CmdEM4x50Brute(const char *Cmd) { if (begin_len != 4) { PrintAndLogEx(FAILED, "'begin' parameter must be 4 bytes"); + CLIParserFree(ctx); return PM3_EINVARG; } if (end_len != 4) { PrintAndLogEx(FAILED, "'end' parameter must be 4 bytes"); + CLIParserFree(ctx); return PM3_EINVARG; } @@ -421,6 +429,7 @@ int CmdEM4x50Brute(const char *Cmd) { if (etd.bruteforce_charset == 0) { PrintAndLogEx(FAILED, "Please enable at least one charset when using charset bruteforce mode."); + CLIParserFree(ctx); return PM3_EINVARG; } @@ -794,7 +803,7 @@ int CmdEM4x50Reader(const char *Cmd) { int CmdEM4x50Dump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 dump", - "Reads all blocks/words from EM4x50 tag and saves dump in bin/eml/json format", + "Reads all blocks/words from EM4x50 tag and saves dump in (bin/json) format", "lf em 4x50 dump\n" "lf em 4x50 dump -f mydump\n" "lf em 4x50 dump -p 12345678\n" @@ -803,7 +812,7 @@ int CmdEM4x50Dump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "specify dump filename (bin/eml/json)"), + arg_str0("f", "file", "", "specify dump filename"), arg_str0("p", "pwd", "", "password, 4 hex bytes, lsb"), arg_param_end }; @@ -865,7 +874,7 @@ int CmdEM4x50Dump(const char *Cmd) { memcpy(data + (i * 4), words[i].byte, 4); } - pm3_save_dump(filename, data, sizeof(data), jsfEM4x50, 4); + pm3_save_dump(filename, data, sizeof(data), jsfEM4x50); return PM3_SUCCESS; } @@ -1111,7 +1120,7 @@ int CmdEM4x50Restore(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("u", "uid", "", "uid, 4 hex bytes, msb"), - arg_str0("f", "file", "", "specify dump filename (bin/eml/json)"), + arg_str0("f", "file", "", "specify a filename for dump file"), arg_str0("p", "pwd", "", "password, 4 hex bytes, lsb"), arg_param_end }; @@ -1224,13 +1233,26 @@ int CmdEM4x50Sim(const char *Cmd) { } int status = PM3_EFAILED; - PrintAndLogEx(INFO, "Simulating data from emulator memory"); + PrintAndLogEx(INFO, "Starting simulating"); clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X50_SIM, (uint8_t *)&password, sizeof(password)); - PacketResponseNG resp; - PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to abort simulation"); + + PacketResponseNG resp; + // init to ZERO + resp.cmd = 0, + resp.length = 0, + resp.magic = 0, + resp.status = 0, + resp.crc = 0, + resp.ng = false, + resp.oldarg[0] = 0; + resp.oldarg[1] = 0; + resp.oldarg[2] = 0; + memset(resp.data.asBytes, 0, PM3_CMD_DATA_SIZE); + bool keypress; do { keypress = kbd_enter_pressed(); diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index d7bc3811a..ecead7cf3 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -35,8 +35,6 @@ static void print_info_result(const uint8_t *data) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - PrintAndLogEx(INFO, "-----------------------------------------------"); - PrintAndLogEx(INFO, "Block | data | info"); PrintAndLogEx(INFO, "------+----------+-----------------------------"); @@ -284,7 +282,7 @@ int CmdEM4x70Brute(const char *Cmd) { return PM3_EINVARG; } - PrintAndLogEx(INFO, "click " _GREEN_("pm3 button") " or press " _GREEN_("Enter") " to exit"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to exit"); clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_LF_EM4X70_BRUTE, (uint8_t *)&etd, sizeof(etd)); @@ -490,11 +488,11 @@ int CmdEM4x70WritePIN(const char *Cmd) { if (resp.status) { print_info_result(resp.data.asBytes); - PrintAndLogEx(INFO, "Writing new PIN: " _GREEN_("SUCCESS")); + PrintAndLogEx(INFO, "Writing new PIN: " _GREEN_("ok")); return PM3_SUCCESS; } - PrintAndLogEx(FAILED, "Writing new PIN: " _RED_("FAILED")); + PrintAndLogEx(FAILED, "Writing new PIN: " _RED_("failed")); return PM3_ESOFT; } @@ -541,11 +539,11 @@ int CmdEM4x70WriteKey(const char *Cmd) { } if (resp.status) { - PrintAndLogEx(INFO, "Writing new crypt key: " _GREEN_("SUCCESS")); + PrintAndLogEx(INFO, "Writing new crypt key: " _GREEN_("ok")); return PM3_SUCCESS; } - PrintAndLogEx(FAILED, "Writing new crypt key: " _RED_("FAILED")); + PrintAndLogEx(FAILED, "Writing new crypt key: " _RED_("failed")); return PM3_ESOFT; } diff --git a/client/src/cmdlffdxb.c b/client/src/cmdlffdxb.c index e30d4f108..0c3330739 100644 --- a/client/src/cmdlffdxb.c +++ b/client/src/cmdlffdxb.c @@ -488,12 +488,13 @@ static const char *mapFDBX(uint16_t countryCode) { //see ASKDemod for what args are accepted //almost the same demod as cmddata.c/CmdFDXBdemodBI int demodFDXB(bool verbose) { - //Differential Biphase / di-phase (inverted biphase) - //get binary from ask wave + // Differential Biphase / di-phase (inverted biphase) + // get binary from ask wave if (ASKbiphaseDemod(0, 32, 1, 100, false) != PM3_SUCCESS) { PrintAndLogEx(DEBUG, "DEBUG: Error - FDX-B ASKbiphaseDemod failed"); return PM3_ESOFT; } + size_t size = g_DemodBufferLen; int preambleIndex = detectFDXB(g_DemodBuffer, &size); if (preambleIndex < 0) { @@ -513,7 +514,6 @@ int demodFDXB(bool verbose) { setDemodBuff(g_DemodBuffer, 128, preambleIndex); setClockGrid(g_DemodClock, g_DemodStartIdx + (preambleIndex * g_DemodClock)); - // remove marker bits (1's every 9th digit after preamble) (pType = 2) size = removeParity(g_DemodBuffer, 11, 9, 2, 117); if (size != 104) { @@ -521,7 +521,7 @@ int demodFDXB(bool verbose) { return PM3_ESOFT; } - //got a good demod + // got a good demod uint8_t offset; // ISO: bits 27..64 uint64_t NationalCode = ((uint64_t)(bytebits_to_byteLSBF(g_DemodBuffer + 32, 6)) << 32) | bytebits_to_byteLSBF(g_DemodBuffer, 32); @@ -559,31 +559,35 @@ int demodFDXB(bool verbose) { offset += 16; uint32_t extended = bytebits_to_byteLSBF(g_DemodBuffer + offset, 24); - uint64_t rawid = (uint64_t)(bytebits_to_byte(g_DemodBuffer, 32)) << 32 | bytebits_to_byte(g_DemodBuffer + 32, 32); - uint8_t raw[8]; - num_to_bytes(rawid, 8, raw); + uint8_t raw[13] = {0}; + for (int i = 0; i < sizeof(raw); i++) { + raw[i] = bytebits_to_byte(g_DemodBuffer + (i * 8), 8); + } - if (!verbose) { + if (verbose == false) { PROMPT_CLEARLINE; - PrintAndLogEx(SUCCESS, "Animal ID " _GREEN_("%04u-%012"PRIu64), countryCode, NationalCode); + PrintAndLogEx(SUCCESS, "Animal ID........... " _GREEN_("%04u-%012"PRIu64), countryCode, NationalCode); return PM3_SUCCESS; } - PrintAndLogEx(SUCCESS, "FDX-B / ISO 11784/5 Animal"); - PrintAndLogEx(SUCCESS, "Animal ID " _GREEN_("%03u-%012"PRIu64), countryCode, NationalCode); - PrintAndLogEx(SUCCESS, "National Code " _GREEN_("%012" PRIu64) " (0x%" PRIX64 ")", NationalCode, NationalCode); - PrintAndLogEx(SUCCESS, "Country Code " _GREEN_("%03u") " - %s", countryCode, mapFDBX(countryCode)); - PrintAndLogEx(SUCCESS, "Reserved/RFU %u (0x%04X)", reservedCode, reservedCode); - PrintAndLogEx(SUCCESS, " Animal bit set? %s", animalBit ? _YELLOW_("True") : "False"); - PrintAndLogEx(SUCCESS, " Data block? %s [value 0x%X]", dataBlockBit ? _YELLOW_("True") : "False", extended); - PrintAndLogEx(SUCCESS, " RUDI bit? %s", rudiBit ? _YELLOW_("True") " (advanced transponder)" : "False"); - PrintAndLogEx(SUCCESS, " User Info? %u %s", userInfo, userInfo == 0 ? "(RFU)" : ""); - PrintAndLogEx(SUCCESS, " Replacement No? %u %s", replacementNr, replacementNr == 0 ? "(RFU)" : ""); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, _CYAN_("FDX-B / ISO 11784/5 Animal")); + PrintAndLogEx(SUCCESS, "Animal ID......... " _GREEN_("%03u-%012"PRIu64), countryCode, NationalCode); + PrintAndLogEx(SUCCESS, "National Code..... " _GREEN_("%012" PRIu64) " ( 0x%" PRIX64 " )", NationalCode, NationalCode); + PrintAndLogEx(SUCCESS, "Country Code...... " _GREEN_("%03u") " - %s", countryCode, mapFDBX(countryCode)); + PrintAndLogEx(SUCCESS, "Reserved/RFU...... %u (0x%04X)", reservedCode, reservedCode); + PrintAndLogEx(SUCCESS, "Animal bit set?... %s", animalBit ? _YELLOW_("True") : "False"); + PrintAndLogEx(SUCCESS, "Data block?....... %s ( 0x%X )", dataBlockBit ? _YELLOW_("True") : "False", extended); + PrintAndLogEx(SUCCESS, "RUDI bit?......... %s", rudiBit ? _YELLOW_("True") " ( advanced transponder )" : "False"); + PrintAndLogEx(SUCCESS, "User Info?........ %u %s", userInfo, (userInfo == 0) ? "( RFU )" : ""); + PrintAndLogEx(SUCCESS, "Replacement No?... %u %s", replacementNr, replacementNr == 0 ? "( RFU )" : ""); + + // crc only calculated over NORMAL data (8 bytes) uint8_t c[] = {0, 0}; - compute_crc(CRC_11784, raw, sizeof(raw), &c[0], &c[1]); - PrintAndLogEx(SUCCESS, "CRC-16 0x%04X ( %s )", crc, (crc == (c[1] << 8 | c[0])) ? _GREEN_("ok") : _RED_("fail")); + compute_crc(CRC_11784, raw, 8, &c[0], &c[1]); + PrintAndLogEx(SUCCESS, "CRC-16............ 0x%04X ( %s )", crc, (crc == (c[1] << 8 | c[0])) ? _GREEN_("ok") : _RED_("fail")); // iceman: crc doesn't protect the extended data? - PrintAndLogEx(SUCCESS, "Raw " _GREEN_("%s"), sprint_hex(raw, 8)); + PrintAndLogEx(SUCCESS, "Raw............... " _GREEN_("%s"), sprint_hex(raw, sizeof(raw))); if (g_debugMode) { PrintAndLogEx(DEBUG, "Start marker %d; Size %zu", preambleIndex, size); @@ -601,12 +605,13 @@ int demodFDXB(bool verbose) { float bt_C = (bt_F - 32) / 1.8; PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "Bio-Thermo detected"); - PrintAndLogEx(INFO, " temperature " _GREEN_("%.1f")" F / " _GREEN_("%.1f") " C", bt_F, bt_C); + PrintAndLogEx(INFO, " temperature... " _GREEN_("%.1f")" F / " _GREEN_("%.1f") " C", bt_F, bt_C); } // set block 0 for later //g_DemodConfig = T55x7_MODULATION_DIPHASE | T55x7_BITRATE_RF_32 | 4 << T55x7_MAXBLOCK_SHIFT; + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } diff --git a/client/src/cmdlfhid.c b/client/src/cmdlfhid.c old mode 100755 new mode 100644 index d197a6dcb..150df44c1 --- a/client/src/cmdlfhid.c +++ b/client/src/cmdlfhid.c @@ -44,6 +44,7 @@ #include "wiegand_formats.h" #include "wiegand_formatutils.h" #include "cmdlfem4x05.h" // EM defines +#include "loclass/cipherutils.h" // bitstreamout #ifndef BITS # define BITS 96 @@ -116,10 +117,15 @@ int demodHID(bool verbose) { //raw fsk demod no manchester decoding no start bit finding just get binary from wave uint32_t hi2 = 0, hi = 0, lo = 0; - uint8_t bits[g_GraphTraceLen]; + uint8_t *bits = calloc(g_GraphTraceLen, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t size = getFromGraphBuf(bits); if (size == 0) { PrintAndLogEx(DEBUG, "DEBUG: Error - " _RED_("HID not enough samples")); + free(bits); return PM3_ESOFT; } //get binary from fsk wave @@ -140,11 +146,13 @@ int demodHID(bool verbose) { else PrintAndLogEx(DEBUG, "DEBUG: Error - " _RED_("HID error demoding fsk %d"), idx); + free(bits); return PM3_ESOFT; } setDemodBuff(bits, size, idx); setClockGrid(50, waveIdx + (idx * 50)); + free(bits); if (hi2 == 0 && hi == 0 && lo == 0) { PrintAndLogEx(DEBUG, "DEBUG: Error - " _RED_("HID no values found")); @@ -231,7 +239,7 @@ static int CmdHIDWatch(const char *Cmd) { CLIParserFree(ctx); PrintAndLogEx(SUCCESS, "Watching for HID Prox cards - place tag on antenna"); - PrintAndLogEx(INFO, "Press pm3-button to stop reading cards"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to stop reading cards"); clearCommandBuffer(); SendCommandNG(CMD_LF_HID_WATCH, NULL, 0); return lfsim_wait_check(CMD_LF_HID_WATCH); @@ -399,14 +407,32 @@ static int CmdHIDClone(const char *Cmd) { packed.Mid = mid; packed.Bot = bot; } else if (bin_len) { - int res = binstring_to_u96(&top, &mid, &bot, (const char *)bin); - if (res != bin_len) { - PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); - return PM3_EINVARG; + + uint8_t hex[12]; + memset(hex, 0, sizeof(hex)); + BitstreamOut_t bout = {hex, 0, 0 }; + + for (int i = 0; i < 96 - bin_len - 1; i++) { + pushBit(&bout, 0); } - packed.Top = top; - packed.Mid = mid; - packed.Bot = bot; + // 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); + } + + packed.Length = bin_len; + packed.Top = bytes_to_num(hex, 4); + packed.Mid = bytes_to_num(hex + 4, 4); + packed.Bot = bytes_to_num(hex + 8, 4); + add_HID_header(&packed); + } else { if (HIDPack(format_idx, &card, &packed, true) == false) { PrintAndLogEx(WARNING, "The card data could not be encoded in the selected format."); @@ -566,7 +592,7 @@ static int CmdHIDBrute(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Started bruteforcing HID Prox reader"); - PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to abort simulation"); PrintAndLogEx(NORMAL, ""); // copy values to low. card_low = card_hi; diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 23b8ac235..45206750f 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -72,14 +72,14 @@ static int CmdLFHitagList(const char *Cmd) { } // Query for the actual size of the trace - PacketResponseNG response; - if (!GetFromDevice(BIG_BUF, got, PM3_CMD_DATA_SIZE, 0, NULL, 0, &response, 2500, false)) { + PacketResponseNG resp; + if (!GetFromDevice(BIG_BUF, got, PM3_CMD_DATA_SIZE, 0, NULL, 0, &resp, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); free(got); return PM3_ETIMEOUT; } - uint16_t traceLen = response.arg[2]; + uint16_t traceLen = resp.arg[2]; if (traceLen > PM3_CMD_DATA_SIZE) { uint8_t *p = realloc(got, traceLen); if (p == NULL) { @@ -224,11 +224,11 @@ static int CmdLFHitagEload(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str1("f", "file", "", "Specfiy dump filename"), - arg_lit0("1", NULL, "Card type Hitag1"), - arg_lit0("2", NULL, "Card type Hitag2"), - arg_lit0("s", NULL, "Card type HitagS"), - arg_lit0("m", NULL, "Card type HitagM"), + arg_str1("f", "file", "", "Specify dump filename"), + arg_lit0("1", "ht1", "Card type Hitag 1"), + arg_lit0("2", "ht2", "Card type Hitag 2"), + arg_lit0("s", "hts", "Card type Hitag S"), + arg_lit0("m", "htm", "Card type Hitag \xce\xbc"), // μ arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -243,9 +243,12 @@ static int CmdLFHitagEload(const char *Cmd) { bool use_htm = arg_get_lit(ctx, 5); CLIParserFree(ctx); - uint8_t n = (use_ht1 + use_ht2 + use_hts + use_htm); - if (n != 1) { - PrintAndLogEx(ERR, "error, only specify one Hitag type"); + if ((use_ht1 + use_ht2 + use_hts + use_htm) > 1) { + PrintAndLogEx(ERR, "error, specify only one Hitag type"); + return PM3_EINVARG; + } + if ((use_ht1 + use_ht2 + use_hts + use_htm) == 0) { + PrintAndLogEx(ERR, "error, specify one Hitag type"); return PM3_EINVARG; } @@ -288,16 +291,16 @@ static int CmdLFHitagEload(const char *Cmd) { static int CmdLFHitagSim(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag sim", - "Simulate Hitag2 / HitagS transponder\n" + "Simulate Hitag transponder\n" "You need to `lf hitag eload` first", "lf hitag sim -2" ); void *argtable[] = { arg_param_begin, - arg_lit0("1", NULL, "simulate Hitag1"), - arg_lit0("2", NULL, "simulate Hitag2"), - arg_lit0("s", NULL, "simulate HitagS"), + arg_lit0("1", "ht1", "simulate Hitag 1"), + arg_lit0("2", "ht2", "simulate Hitag 2"), + arg_lit0("s", "hts", "simulate Hitag S"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -305,10 +308,15 @@ static int CmdLFHitagSim(const char *Cmd) { bool use_ht1 = arg_get_lit(ctx, 1); bool use_ht2 = arg_get_lit(ctx, 2); bool use_hts = arg_get_lit(ctx, 3); + bool use_htm = false; // not implemented yet CLIParserFree(ctx); - if ((use_ht1 + use_ht2 + use_hts) > 1) { - PrintAndLogEx(ERR, "error, Only specify one Hitag type"); + if ((use_ht1 + use_ht2 + use_hts + use_htm) > 1) { + PrintAndLogEx(ERR, "error, specify only one Hitag type"); + return PM3_EINVARG; + } + if ((use_ht1 + use_ht2 + use_hts + use_htm) == 0) { + PrintAndLogEx(ERR, "error, specify one Hitag type"); return PM3_EINVARG; } @@ -338,7 +346,7 @@ static void printHitag2PaxtonDowngrade(const uint8_t *data) { bytes = (bytes * 0x100) + data[i]; } - for (int j = 0; j< 8; j++) { + for (int j = 0; j < 8; j++) { num = bytes & mask; skip -= 5; mask = mask >> 5; @@ -506,7 +514,6 @@ static int CmdLFHitagInfo(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%08X"), uid); PrintAndLogEx(SUCCESS, " TYPE: " _GREEN_("%s"), getHitagTypeStr(uid)); @@ -523,67 +530,85 @@ static int CmdLFHitagInfo(const char *Cmd) { return PM3_SUCCESS; } -// TODO: iceman -// Hitag2 reader, problem is that this command mixes up stuff. So 26 give uid. 21 etc will also give you a memory dump !? -// static int CmdLFHitagReader(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf hitag reader", - "Act like a Hitag Reader", - "Hitag S\n" - " lf hitag reader --01 --nrar 0102030411223344\n" - " lf hitag reader --02 -k 4F4E4D494B52\n" - "Hitag 2\n" - " lf hitag reader --21 -k 4D494B52\n" - " lf hitag reader --22 --nrar 0102030411223344\n" - " lf hitag reader --23 -k 4F4E4D494B52\n" - " lf hitag reader --26\n" + CLIParserInit(&ctx, "lf hitag read", + "Read Hitag memory\n" + "Crypto mode key format: ISK high + ISK low", + "Hitag S, plain mode\n" + " lf hitag read --hts\n" + "Hitag S, challenge mode\n" + " lf hitag read --hts --nrar 0102030411223344\n" + "Hitag S, crypto mode => use default key 4F4E4D494B52 (ONMIKR)\n" + " lf hitag read --hts --crypto\n" + "Hitag S, long key = crypto mode\n" + " lf hitag read --hts -k 4F4E4D494B52\n\n" + + "Hitag 2, password mode => use default key 4D494B52 (MIKR)\n" + " lf hitag read --ht2 --pwd\n" + "Hitag 2, providing a short key = password mode\n" + " lf hitag read --ht2 -k 4D494B52\n" + "Hitag 2, challenge mode\n" + " lf hitag read --ht2 --nrar 0102030411223344\n" + "Hitag 2, crypto mode => use default key 4F4E4D494B52 (ONMIKR)\n" + " lf hitag read --ht2 --crypto\n" + "Hitag 2, providing a long key = crypto mode\n" + " lf hitag read --ht2 -k 4F4E4D494B52\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "01", "HitagS, read all pages, challenge mode"), - arg_lit0(NULL, "02", "HitagS, read all pages, crypto mode. Set key=0 for no auth"), - arg_lit0(NULL, "21", "Hitag2, read all pages, password mode. def 4D494B52 (MIKR)"), - arg_lit0(NULL, "22", "Hitag2, read all pages, challenge mode"), - arg_lit0(NULL, "23", "Hitag2, read all pages, crypto mode. Key ISK high + ISK low. def 4F4E4D494B52 (ONMIKR)"), - arg_lit0(NULL, "25", "Hitag2, test recorded authentications (replay?)"), - arg_lit0(NULL, "26", "Hitag2, read UID"), + arg_lit0("s", "hts", "Hitag S"), + arg_lit0("2", "ht2", "Hitag 2"), + arg_lit0(NULL, "pwd", "password mode"), + arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), + arg_lit0(NULL, "crypto", "crypto mode"), arg_str0("k", "key", "", "key, 4 or 6 hex bytes"), - arg_str0(NULL, "nrar", "", "nonce / answer reader, 8 hex bytes"), +// currently pm3 fw reads all the memory anyway +// arg_int1("p", "page", "", "page address to write to"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - // Hitag S - bool s01 = arg_get_lit(ctx, 1); - bool s02 = arg_get_lit(ctx, 2); + bool use_ht1 = false; // not yet implemented + bool use_hts = arg_get_lit(ctx, 1); + bool use_ht2 = arg_get_lit(ctx, 2); + bool use_htm = false; // not yet implemented - // Hitag 2 - bool h21 = arg_get_lit(ctx, 3); - bool h22 = arg_get_lit(ctx, 4); - bool h23 = arg_get_lit(ctx, 5); - bool h25 = arg_get_lit(ctx, 6); - bool h26 = arg_get_lit(ctx, 7); - - uint8_t key[6]; - int keylen = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, 8), key, sizeof(key), &keylen); + bool use_plain = false; + bool use_pwd = arg_get_lit(ctx, 3); + uint8_t nrar[8]; + int nalen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 4), nrar, sizeof(nrar), &nalen); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; } + bool use_nrar = nalen > 0; + bool use_crypto = arg_get_lit(ctx, 5); - uint8_t nrar[8]; - int nalen = 0; - res = CLIParamHexToBuf(arg_get_str(ctx, 9), nrar, sizeof(nrar), &nalen); - CLIParserFree(ctx); + uint8_t key[6]; + int keylen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 6), key, sizeof(key), &keylen); if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } +// uint32_t page = arg_get_u32_def(ctx, 6, 0); + + CLIParserFree(ctx); + + // sanity checks + if ((use_ht1 + use_ht2 + use_hts + use_htm) > 1) { + PrintAndLogEx(ERR, "error, specify only one Hitag type"); + return PM3_EINVARG; + } + if ((use_ht1 + use_ht2 + use_hts + use_htm) == 0) { + PrintAndLogEx(ERR, "error, specify one Hitag type"); return PM3_EINVARG; } - // sanity checks if (keylen != 0 && keylen != 4 && keylen != 6) { PrintAndLogEx(WARNING, "Wrong KEY len expected 0, 4 or 6, got %d", keylen); return PM3_EINVARG; @@ -594,48 +619,73 @@ static int CmdLFHitagReader(const char *Cmd) { return PM3_EINVARG; } - uint8_t foo = (s01 + s02 + h21 + h22 + h23 + h25 + h26); + // complete options + if (keylen == 4) { + use_pwd = true; + } + if (keylen == 6) { + use_crypto = true; + } + if ((keylen == 0) && use_pwd) { + memcpy(key, "MIKR", 4); + keylen = 4; + } + if ((keylen == 0) && use_crypto) { + memcpy(key, "ONMIKR", 6); + keylen = 6; + } + + // check coherence + uint8_t foo = (use_plain + use_pwd + use_nrar + use_crypto); if (foo > 1) { - PrintAndLogEx(WARNING, "Only specify one HITAG reader cmd"); + PrintAndLogEx(WARNING, "Specify only one authentication mode"); return PM3_EINVARG; } else if (foo == 0) { - PrintAndLogEx(WARNING, "Specify one HITAG reader cms"); + if (use_hts) { + use_plain = true; + } else { + PrintAndLogEx(WARNING, "Specify one authentication mode"); + return PM3_EINVARG; + } + } + + if (use_hts && use_pwd) { // not sure for the other types... + PrintAndLogEx(WARNING, "Chosen Hitag type does not have Password mode"); + return PM3_EINVARG; + } + + if (use_ht2 && use_plain) { // not sure for the other types... + PrintAndLogEx(WARNING, "Chosen Hitag type does not have Plain mode"); return PM3_EINVARG; } hitag_function htf; hitag_data htd; memset(&htd, 0, sizeof(htd)); - - - uint16_t cmd = CMD_LF_HITAG_READER; - if (s01) { + uint16_t cmd; + if (use_hts && use_nrar) { cmd = CMD_LF_HITAGS_READ; htf = RHTSF_CHALLENGE; memcpy(htd.auth.NrAr, nrar, sizeof(htd.auth.NrAr)); - } - if (s02) { + } else if (use_hts && use_crypto) { cmd = CMD_LF_HITAGS_READ; htf = RHTSF_KEY; memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); - } - if (h21) { + } else if (use_ht2 && use_pwd) { + cmd = CMD_LF_HITAG_READER; htf = RHT2F_PASSWORD; memcpy(htd.pwd.password, key, sizeof(htd.pwd.password)); - } - if (h22) { + } else if (use_ht2 && use_nrar) { + cmd = CMD_LF_HITAG_READER; htf = RHT2F_AUTHENTICATE; memcpy(htd.auth.NrAr, nrar, sizeof(htd.auth.NrAr)); - } - if (h23) { + } else if (use_ht2 && use_crypto) { htf = RHT2F_CRYPTO; + cmd = CMD_LF_HITAG_READER; memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); - } - if (h25) { - htf = RHT2F_TEST_AUTH_ATTEMPTS; - } - if (h26) { - htf = RHT2F_UID_ONLY; + } else { + PrintAndLogEx(WARNING, "Sorry, not yet implemented"); + return PM3_ENOTIMPL; } clearCommandBuffer(); @@ -653,21 +703,13 @@ static int CmdLFHitagReader(const char *Cmd) { uint32_t id = bytes_to_num(resp.data.asBytes, 4); uint8_t *data = resp.data.asBytes; PrintAndLogEx(SUCCESS, " UID: " _YELLOW_("%08x"), id); - - if (htf != RHT2F_UID_ONLY) { - - // block3, 1 byte - printHitag2Configuration(data[4 * 3]); - - // print data - print_hex_break(data, 48, 4); - - printHitag2PaxtonDowngrade(data); - } + printHitag2Configuration(data[4 * 3]); + print_hex_break(data, 48, 4); + printHitag2PaxtonDowngrade(data); return PM3_SUCCESS; } -static int CmdLFHitagCheckChallenges(const char *Cmd) { +static int CmdLFHitagSCheckChallenges(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag cc", @@ -686,7 +728,6 @@ static int CmdLFHitagCheckChallenges(const char *Cmd) { int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - CLIParserFree(ctx); clearCommandBuffer(); @@ -708,92 +749,170 @@ static int CmdLFHitagCheckChallenges(const char *Cmd) { return PM3_SUCCESS; } +static int CmdLFHitag2CheckChallenges(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag ta", + "Test recorded authentications (replay?)", + "lf hitag ta" + ); + CLIParserFree(ctx); + + clearCommandBuffer(); + SendCommandMIX(CMD_LF_HITAG_READER, RHT2F_TEST_AUTH_ATTEMPTS, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + if (resp.oldarg[0] == false) { + PrintAndLogEx(DEBUG, "DEBUG: Error - hitag failed"); + return PM3_ESOFT; + } + + // FIXME: doegox: not sure what this fct does and what it returns... + return PM3_SUCCESS; +} + + static int CmdLFHitagWriter(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf hitag writer", - "Act like a Hitag writer" - "In password mode the default key is 4D494B52 (MIKR)\n" - "In crypto mode the default key is 4F4E4D494B52 (ONMIKR) format: ISK high + ISK low.", - "Hitag S\n" - " lf hitag writer --03 --nrar 0102030411223344 -p 3 -d 01020304\n" - " lf hitag writer --04 -k 4F4E4D494B52 -p 3 -d 01020304\n" - "Hitag 2\n" - " lf hitag writer --24 -k 4F4E4D494B52 -p 3 -d 01020304\n" - " lf hitag writer --27 -k 4D494B52 -p 3 -d 01020304\n" + CLIParserInit(&ctx, "lf hitag wrbl", + "Write a page in Hitag memory\n" + "Crypto mode key format: ISK high + ISK low", + "Hitag S, plain mode\n" + " lf hitag wrbl --hts -p 6 -d 01020304\n" + "Hitag S, challenge mode\n" + " lf hitag wrbl --hts --nrar 0102030411223344 -p 6 -d 01020304\n" + "Hitag S, crypto mode => use default key 4F4E4D494B52 (ONMIKR)\n" + " lf hitag wrbl --hts --crypto -p 6 -d 01020304\n" + "Hitag S, long key = crypto mode\n" + " lf hitag wrbl --hts -k 4F4E4D494B52 -p 6 -d 01020304\n\n" + + "Hitag 2, password mode => use default key 4D494B52 (MIKR)\n" + " lf hitag wrbl --ht2 --pwd -p 6 -d 01020304\n" + "Hitag 2, providing a short key = password mode\n" + " lf hitag wrbl --ht2 -k 4D494B52 -p 6 -d 01020304\n" + "Hitag 2, challenge mode\n" + " lf hitag wrbl --ht2 --nrar 0102030411223344 -p 6 -d 01020304\n" + "Hitag 2, crypto mode => use default key 4F4E4D494B52 (ONMIKR)\n" + " lf hitag wrbl --ht2 --crypto -p 6 -d 01020304\n" + "Hitag 2, providing a long key = crypto mode\n" + " lf hitag wrbl --ht2 -k 4F4E4D494B52 -p 6 -d 01020304\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "03", "HitagS, write page, challenge mode"), - arg_lit0(NULL, "04", "HitagS, write page, crypto mode. Set key=0 for no auth"), - arg_lit0(NULL, "24", "Hitag2, write page, crypto mode."), - arg_lit0(NULL, "27", "Hitag2, write page, password mode"), - arg_int1("p", "page", "", "page address to write to"), - arg_str0("d", "data", "", "data, 4 hex bytes"), - arg_str0("k", "key", "", "key, 4 or 6 hex bytes"), + arg_lit0("s", "hts", "Hitag S"), + arg_lit0("2", "ht2", "Hitag 2"), + arg_lit0(NULL, "pwd", "password mode"), arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), + arg_lit0(NULL, "crypto", "crypto mode"), + arg_str0("k", "key", "", "key, 4 or 6 hex bytes"), + arg_int1("p", "page", "", "page address to write to"), + arg_str1("d", "data", "", "data, 4 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - // Hitag S - bool s03 = arg_get_lit(ctx, 1); - bool s04 = arg_get_lit(ctx, 2); + bool use_ht1 = false; // not yet implemented + bool use_hts = arg_get_lit(ctx, 1); + bool use_ht2 = arg_get_lit(ctx, 2); + bool use_htm = false; // not yet implemented - // Hitag 2 - bool h24 = arg_get_lit(ctx, 3); - bool h27 = arg_get_lit(ctx, 4); - - uint32_t page = arg_get_u32_def(ctx, 5, 0); - - uint8_t data[4]; - int dlen = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, 6), data, sizeof(data), &dlen); + bool use_plain = false; + bool use_pwd = arg_get_lit(ctx, 3); + uint8_t nrar[8]; + int nalen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 4), nrar, sizeof(nrar), &nalen); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; } + bool use_nrar = nalen > 0; + bool use_crypto = arg_get_lit(ctx, 5); uint8_t key[6]; int keylen = 0; - res = CLIParamHexToBuf(arg_get_str(ctx, 7), key, sizeof(key), &keylen); + res = CLIParamHexToBuf(arg_get_str(ctx, 6), key, sizeof(key), &keylen); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; } - uint8_t nrar[8]; - int nalen = 0; - res = CLIParamHexToBuf(arg_get_str(ctx, 8), nrar, sizeof(nrar), &nalen); + uint32_t page = arg_get_u32_def(ctx, 7, 0); + + uint8_t data[4]; + int dlen = 0; + res = CLIParamHexToBuf(arg_get_str(ctx, 8), data, sizeof(data), &dlen); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } CLIParserFree(ctx); - if (res != 0) { + // sanity checks + if ((use_ht1 + use_ht2 + use_hts + use_htm) > 1) { + PrintAndLogEx(ERR, "error, specify only one Hitag type"); + return PM3_EINVARG; + } + if ((use_ht1 + use_ht2 + use_hts + use_htm) == 0) { + PrintAndLogEx(ERR, "error, specify one Hitag type"); + return PM3_EINVARG; + } + + if (keylen != 0 && keylen != 4 && keylen != 6) { + PrintAndLogEx(WARNING, "Wrong KEY len expected 0, 4 or 6, got %d", keylen); return PM3_EINVARG; } - // sanity checks if (dlen != sizeof(data)) { PrintAndLogEx(WARNING, "Wrong DATA len expected 4, got %d", dlen); return PM3_EINVARG; } - if (keylen != 0 && keylen != 6 && keylen != 4) { - PrintAndLogEx(WARNING, "Wrong KEY len expected 0, 4 or 6, got %d", keylen); - return PM3_EINVARG; - } - if (nalen != 0 && nalen != 8) { PrintAndLogEx(WARNING, "Wrong NR/AR len expected 0 or 8, got %d", nalen); return PM3_EINVARG; } - uint8_t foo = (s03 + s04 + h24 + h27); + // complete options + if (keylen == 4) { + use_pwd = true; + } + if (keylen == 6) { + use_crypto = true; + } + if ((keylen == 0) && use_pwd) { + memcpy(key, "MIKR", 4); + keylen = 4; + } + if ((keylen == 0) && use_crypto) { + memcpy(key, "ONMIKR", 6); + keylen = 6; + } + + // check coherence + uint8_t foo = (use_plain + use_pwd + use_nrar + use_crypto); if (foo > 1) { - PrintAndLogEx(WARNING, "Only specify one HITAG write cmd"); + PrintAndLogEx(WARNING, "Specify only one authentication mode"); return PM3_EINVARG; } else if (foo == 0) { - PrintAndLogEx(WARNING, "Specify one HITAG write cmd"); + if (use_hts) { + use_plain = true; + } else { + PrintAndLogEx(WARNING, "Specify one authentication mode"); + return PM3_EINVARG; + } + } + + if (use_hts && use_pwd) { // not sure for the other types... + PrintAndLogEx(WARNING, "Chosen Hitag type does not have Password mode"); + return PM3_EINVARG; + } + + if (use_ht2 && use_plain) { // not sure for the other types... + PrintAndLogEx(WARNING, "Chosen Hitag type does not have Plain mode"); return PM3_EINVARG; } @@ -801,29 +920,33 @@ static int CmdLFHitagWriter(const char *Cmd) { hitag_data htd; memset(&htd, 0, sizeof(htd)); - if (s03) { + if (use_hts && use_nrar) { htf = WHTSF_CHALLENGE; memcpy(htd.auth.NrAr, nrar, sizeof(htd.auth.NrAr)); memcpy(htd.auth.data, data, sizeof(data)); - } - if (s04) { + PrintAndLogEx(INFO, "Authenticating to Hitag S in Challenge mode"); + } else if (use_hts && use_crypto) { htf = WHTSF_KEY; memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); memcpy(htd.crypto.data, data, sizeof(data)); - } - if (h24) { - htf = WHT2F_CRYPTO; - memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); - memcpy(htd.crypto.data, data, sizeof(data)); - } - if (h27) { + PrintAndLogEx(INFO, "Authenticating to Hitag S in Crypto mode"); + } else if (use_ht2 && use_pwd) { htf = WHT2F_PASSWORD; memcpy(htd.pwd.password, key, sizeof(htd.pwd.password)); memcpy(htd.crypto.data, data, sizeof(data)); + PrintAndLogEx(INFO, "Authenticating to Hitag 2 in Password mode"); + } else if (use_ht2 && use_crypto) { + htf = WHT2F_CRYPTO; + memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); + memcpy(htd.crypto.data, data, sizeof(data)); + PrintAndLogEx(INFO, "Authenticating to Hitag 2 in Crypto mode"); + } else { + PrintAndLogEx(WARNING, "Sorry, not yet implemented"); + return PM3_ENOTIMPL; } - + uint16_t cmd = CMD_LF_HITAGS_WRITE; clearCommandBuffer(); - SendCommandMIX(CMD_LF_HITAGS_WRITE, htf, 0, page, &htd, sizeof(htd)); + SendCommandMIX(cmd, htf, 0, page, &htd, sizeof(htd)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 4000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); @@ -841,56 +964,132 @@ static int CmdLFHitag2Dump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag dump", - "Read all card memory and save to file" - "In password mode the default key is 4D494B52 (MIKR)\n" - "In crypto mode the default key is 4F4E4D494B52 (ONMIKR) format: ISK high + ISK low.", - "lf hitag dump -k 4F4E4D494B52\n" - "lf hitag dump -k 4D494B52\n" + "Read all Hitag 2 card memory and save to file\n" + "Crypto mode key format: ISK high + ISK low", + "Password mode => use default key 4D494B52 (MIKR)\n" + " lf hitag dump --pwd\n" + "Short key = password mode\n" + " lf hitag dump -k 4D494B52\n" + "Challenge mode\n" + " lf hitag dump --nrar 0102030411223344\n" + "Crypto mode => use default key 4F4E4D494B52 (ONMIKR)\n" + " lf hitag dump --crypto\n" + "Long key = crypto mode\n" + " lf hitag dump -k 4F4E4D494B52\n" ); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "specify file name"), - arg_str0("k", "key", "", "key, 4 or 6 hex bytes"), + arg_lit0(NULL, "pwd", "password mode"), arg_str0(NULL, "nrar", "", "nonce / answer reader, 8 hex bytes"), + arg_lit0(NULL, "crypto", "crypto mode"), + arg_str0("k", "key", "", "key, 4 or 6 hex bytes"), + arg_str0("f", "file", "", "specify file name"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); - int fnlen = 0; - char filename[FILE_PATH_SIZE] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + bool use_ht1 = false; // not yet implemented + bool use_hts = false; // not yet implemented + bool use_ht2 = true; + bool use_htm = false; // not yet implemented + + bool use_plain = false; + bool use_pwd = arg_get_lit(ctx, 1); + uint8_t nrar[8]; + int nalen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), nrar, sizeof(nrar), &nalen); + if (res != 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + bool use_nrar = nalen > 0; + bool use_crypto = arg_get_lit(ctx, 3); uint8_t key[6]; int keylen = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, 2), key, sizeof(key), &keylen); + res = CLIParamHexToBuf(arg_get_str(ctx, 4), key, sizeof(key), &keylen); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; } - uint8_t nrar[8]; - int nalen = 0; - res = CLIParamHexToBuf(arg_get_str(ctx, 3), nrar, sizeof(nrar), &nalen); + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); - if (res != 0) { + + // sanity checks + if ((use_ht1 + use_ht2 + use_hts + use_htm) > 1) { + PrintAndLogEx(ERR, "error, specify only one Hitag type"); + return PM3_EINVARG; + } + if ((use_ht1 + use_ht2 + use_hts + use_htm) == 0) { + PrintAndLogEx(ERR, "error, specify one Hitag type"); + return PM3_EINVARG; + } + + if (keylen != 0 && keylen != 4 && keylen != 6) { + PrintAndLogEx(WARNING, "Wrong KEY len expected 0, 4 or 6, got %d", keylen); + return PM3_EINVARG; + } + + // complete options + if (keylen == 4) { + use_pwd = true; + } + if (keylen == 6) { + use_crypto = true; + } + if ((keylen == 0) && use_pwd) { + memcpy(key, "MIKR", 4); + keylen = 4; + } + if ((keylen == 0) && use_crypto) { + memcpy(key, "ONMIKR", 6); + keylen = 6; + } + + // check coherence + uint8_t foo = (use_plain + use_pwd + use_nrar + use_crypto); + if (foo > 1) { + PrintAndLogEx(WARNING, "Specify only one authentication mode"); + return PM3_EINVARG; + } else if (foo == 0) { + if (use_hts) { + use_plain = true; + } else { + PrintAndLogEx(WARNING, "Specify one authentication mode"); + return PM3_EINVARG; + } + } + + if (use_hts && use_pwd) { // not sure for the other types... + PrintAndLogEx(WARNING, "Chosen Hitag type does not have Password mode"); + return PM3_EINVARG; + } + + if (use_ht2 && use_plain) { // not sure for the other types... + PrintAndLogEx(WARNING, "Chosen Hitag type does not have Plain mode"); return PM3_EINVARG; } hitag_function htf; hitag_data htd; memset(&htd, 0, sizeof(htd)); - - if (keylen == 6) { - htf = RHT2F_CRYPTO; - memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); - PrintAndLogEx(INFO, "Authenticating in crypto mode"); - } else { + if (use_ht2 && use_pwd) { htf = RHT2F_PASSWORD; memcpy(htd.pwd.password, key, sizeof(htd.pwd.password)); - PrintAndLogEx(INFO, "Authenticating in password mode"); + PrintAndLogEx(INFO, "Authenticating to Hitag 2 in Password mode"); + } else if (use_ht2 && use_crypto) { + htf = RHT2F_CRYPTO; + memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); + PrintAndLogEx(INFO, "Authenticating to Hitag 2 in Crypto mode"); + } else { + PrintAndLogEx(WARNING, "Sorry, not yet implemented"); + return PM3_ENOTIMPL; } - uint16_t cmd = CMD_LF_HITAG_READER; clearCommandBuffer(); SendCommandMIX(cmd, htf, 0, 0, &htd, sizeof(htd)); @@ -909,7 +1108,8 @@ static int CmdLFHitag2Dump(const char *Cmd) { if (data == NULL) return PM3_ESOFT; - PrintAndLogEx(SUCCESS, "Dumping tag memory..."); + uint32_t id = bytes_to_num(resp.data.asBytes, 4); + PrintAndLogEx(SUCCESS, " UID: " _YELLOW_("%08x"), id); if (fnlen < 1) { char *fptr = filename; @@ -917,9 +1117,17 @@ static int CmdLFHitag2Dump(const char *Cmd) { FillFileNameByUID(fptr, data, "-dump", 4); } + // block3, 1 byte + printHitag2Configuration(data[4 * 3]); + + // print data print_hex_break(data, 48, 4); - pm3_save_dump(filename, data, 48, jsfHitag, 4); + printHitag2PaxtonDowngrade(data); + + PrintAndLogEx(SUCCESS, "Dumping tag memory..."); + + pm3_save_dump(filename, data, 48, jsfHitag); return PM3_SUCCESS; } @@ -982,16 +1190,20 @@ void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, } static command_t CommandTable[] = { + {"-----------", CmdHelp, IfPm3Hitag, "------------------------ " _CYAN_("General") " ------------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"eload", CmdLFHitagEload, IfPm3Hitag, "Load Hitag dump file into emulator memory"}, {"list", CmdLFHitagList, AlwaysAvailable, "List Hitag trace history"}, - {"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag2 tag information"}, - {"reader", CmdLFHitagReader, IfPm3Hitag, "Act like a Hitag reader"}, - {"sim", CmdLFHitagSim, IfPm3Hitag, "Simulate Hitag transponder"}, + {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("operations") " -----------------------"}, + {"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag 2 tag information"}, + {"dump", CmdLFHitag2Dump, IfPm3Hitag, "Dump Hitag 2 tag"}, + {"read", CmdLFHitagReader, IfPm3Hitag, "Read Hitag memory"}, + {"wrbl", CmdLFHitagWriter, IfPm3Hitag, "Write a block (page) in Hitag memory"}, {"sniff", CmdLFHitagSniff, IfPm3Hitag, "Eavesdrop Hitag communication"}, - {"writer", CmdLFHitagWriter, IfPm3Hitag, "Act like a Hitag writer"}, - {"dump", CmdLFHitag2Dump, IfPm3Hitag, "Dump Hitag2 tag"}, - {"cc", CmdLFHitagCheckChallenges, IfPm3Hitag, "Test all challenges"}, + {"cc", CmdLFHitagSCheckChallenges, IfPm3Hitag, "Hitag S: test all provided challenges"}, + {"ta", CmdLFHitag2CheckChallenges, IfPm3Hitag, "Hitag 2: test all recorded authentications"}, + {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("simulation") " -----------------------"}, + {"eload", CmdLFHitagEload, IfPm3Hitag, "Load Hitag dump file into emulator memory"}, + {"sim", CmdLFHitagSim, IfPm3Hitag, "Simulate Hitag transponder"}, { NULL, NULL, 0, NULL } }; @@ -1007,7 +1219,7 @@ int CmdLFHitag(const char *Cmd) { } int readHitagUid(void) { - return (CmdLFHitagReader("--26") == PM3_SUCCESS); + return (CmdLFHitagReader("--ht2") == PM3_SUCCESS); } uint8_t hitag1_CRC_check(uint8_t *d, uint32_t nbit) { diff --git a/client/src/cmdlfidteck.c b/client/src/cmdlfidteck.c index 3dae4f0d1..e39354ef8 100644 --- a/client/src/cmdlfidteck.c +++ b/client/src/cmdlfidteck.c @@ -214,7 +214,7 @@ static int CmdIdteckSim(const char *Cmd) { } PrintAndLogEx(SUCCESS, "Simulating Idteck - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, raw_len)); - PrintAndLogEx(SUCCESS, "Press pm3-button to abort simulation or run another command"); + PrintAndLogEx(SUCCESS, "Press " _GREEN_("pm3 button") " to abort simulation or run another command"); PrintAndLogEx(NORMAL, ""); lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + sizeof(bs)); diff --git a/client/src/cmdlfindala.c b/client/src/cmdlfindala.c index fa59894e1..ec61b938a 100644 --- a/client/src/cmdlfindala.c +++ b/client/src/cmdlfindala.c @@ -120,7 +120,7 @@ static void decodeHeden2L(uint8_t *bits) { if (bits[offset + 7]) cardnumber += 16384; if (bits[offset + 23]) cardnumber += 32768; - PrintAndLogEx(SUCCESS, " Heden-2L | %u", cardnumber); + PrintAndLogEx(SUCCESS, " Heden-2L...... %u", cardnumber); } // sending three times. Didn't seem to break the previous sim? @@ -301,10 +301,11 @@ int demodIndalaEx(int clk, int invert, int maxErr, bool verbose) { ); PrintAndLogEx(DEBUG, "two bit checksum... " _GREEN_("%1d%1d"), checksum >> 1 & 0x01, checksum & 0x01); + PrintAndLogEx(INFO, ""); PrintAndLogEx(SUCCESS, "Possible de-scramble patterns"); // This doesn't seem to line up with the hot-stamp numbers on any HID cards I have seen, but, leaving it alone since I do not know how those work. -MS - PrintAndLogEx(SUCCESS, " Printed | __%04d__ [0x%X]", p1, p1); - PrintAndLogEx(SUCCESS, " Internal ID | %" PRIu64, foo); + PrintAndLogEx(SUCCESS, " Printed....... __%04d__ ( 0x%X )", p1, p1); + PrintAndLogEx(SUCCESS, " Internal ID... %" PRIu64, foo); decodeHeden2L(g_DemodBuffer); } else { @@ -336,6 +337,7 @@ int demodIndalaEx(int clk, int invert, int maxErr, bool verbose) { PrintAndLogEx(DEBUG, "DEBUG: Indala - printing DemodBuffer"); printDemodBuff(0, false, false, false); } + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -401,7 +403,11 @@ static int CmdIndalaDemodAlt(const char *Cmd) { // worst case with g_GraphTraceLen=40000 is < 4096 // under normal conditions it's < 2048 - uint8_t data[MAX_GRAPH_TRACE_LEN] = {0}; + uint8_t *data = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); + if (data == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t datasize = getFromGraphBuf(data); uint8_t rawbits[4096] = {0}; @@ -444,6 +450,7 @@ static int CmdIndalaDemodAlt(const char *Cmd) { count = 0; } } + free(data); if (rawbit > 0) { PrintAndLogEx(INFO, "Recovered %d raw bits, expected: %zu", rawbit, g_GraphTraceLen / 32); @@ -503,7 +510,7 @@ static int CmdIndalaDemodAlt(const char *Cmd) { showbits[bit] = '.' + bits[bit]; } showbits[bit + 1] = '\0'; - PrintAndLogEx(SUCCESS, "Partial UID | %s", showbits); + PrintAndLogEx(SUCCESS, "Partial UID... %s", showbits); return PM3_SUCCESS; } else { for (bit = 0; bit < uidlen; bit++) { @@ -528,7 +535,7 @@ static int CmdIndalaDemodAlt(const char *Cmd) { uid2 = (uid2 << 1) | 1; } } - PrintAndLogEx(SUCCESS, "UID | %s (%x%08x)", showbits, uid1, uid2); + PrintAndLogEx(SUCCESS, "UID... %s ( %x%08x )", showbits, uid1, uid2); } else { uint32_t uid3 = 0; uint32_t uid4 = 0; @@ -549,7 +556,7 @@ static int CmdIndalaDemodAlt(const char *Cmd) { else uid7 = (uid7 << 1) | 1; } - PrintAndLogEx(SUCCESS, "UID | %s (%x%08x%08x%08x%08x%08x%08x)", showbits, uid1, uid2, uid3, uid4, uid5, uid6, uid7); + PrintAndLogEx(SUCCESS, "UID... %s (%x%08x%08x%08x%08x%08x%08x)", showbits, uid1, uid2, uid3, uid4, uid5, uid6, uid7); } // Checking UID against next occurrences @@ -661,7 +668,7 @@ static int CmdIndalaSim(const char *Cmd) { bool fmt4041x = arg_get_lit(ctx, 5); - int32_t cardnumber; + int32_t cardnumber = 0; uint8_t fc = 0; uint16_t cn = 0; bool got_cn = false, got_26 = false; @@ -745,7 +752,7 @@ static int CmdIndalaSim(const char *Cmd) { // lf simpsk -1 -c 32 --fc 2 -d 0102030405060708 - PrintAndLogEx(SUCCESS, "Press pm3-button to abort simulation or run another command"); + PrintAndLogEx(SUCCESS, "Press " _GREEN_("pm3 button") " to abort simulation or run another command"); // indala PSK, clock 32, carrier 0 lf_psksim_t *payload = calloc(1, sizeof(lf_psksim_t) + sizeof(bs)); @@ -1008,7 +1015,7 @@ static int CmdIndalaBrute(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Started brute-forcing INDALA Prox reader"); - PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " or press " _GREEN_("") " to abort simulation"); PrintAndLogEx(NORMAL, ""); // main loop diff --git a/client/src/cmdlfio.c b/client/src/cmdlfio.c index 454d01a54..11027266b 100644 --- a/client/src/cmdlfio.c +++ b/client/src/cmdlfio.c @@ -55,7 +55,7 @@ static int CmdIOProxWatch(const char *Cmd) { CLIParserFree(ctx); PrintAndLogEx(SUCCESS, "Watching for IO Prox cards - place tag on antenna"); - PrintAndLogEx(INFO, "Press pm3-button to stop reading cards"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to stop reading cards"); clearCommandBuffer(); SendCommandNG(CMD_LF_IO_WATCH, NULL, 0); return lfsim_wait_check(CMD_LF_IO_WATCH); @@ -66,10 +66,15 @@ static int CmdIOProxWatch(const char *Cmd) { int demodIOProx(bool verbose) { (void) verbose; // unused so far int idx = 0, retval = PM3_SUCCESS; - uint8_t bits[MAX_GRAPH_TRACE_LEN] = {0}; + uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t size = getFromGraphBuf(bits); if (size < 65) { PrintAndLogEx(DEBUG, "DEBUG: Error - IO prox not enough samples in GraphBuffer"); + free(bits); return PM3_ESOFT; } //get binary from fsk wave @@ -93,6 +98,7 @@ int demodIOProx(bool verbose) { PrintAndLogEx(DEBUG, "DEBUG: Error - IO prox error demoding fsk %d", idx); } } + free(bits); return PM3_ESOFT; } setDemodBuff(bits, size, idx); @@ -103,6 +109,7 @@ int demodIOProx(bool verbose) { PrintAndLogEx(DEBUG, "DEBUG: Error - IO prox data not found - FSK Bits: %zu", size); if (size > 92) PrintAndLogEx(DEBUG, "%s", sprint_bytebits_bin_break(bits, 92, 16)); } + free(bits); return PM3_ESOFT; } @@ -156,6 +163,7 @@ int demodIOProx(bool verbose) { printDemodBuff(0, false, false, true); printDemodBuff(0, false, false, false); } + free(bits); return retval; } @@ -229,7 +237,7 @@ static int CmdIOProxSim(const char *Cmd) { } PrintAndLogEx(SUCCESS, "Simulating ioProx version: " _YELLOW_("%u") " FC: " _YELLOW_("%u (0x%02x)") " CN: " _YELLOW_("%u"), version, fc, fc, cn); - PrintAndLogEx(SUCCESS, "Press pm3-button to abort simulation or run another command"); + PrintAndLogEx(SUCCESS, "Press " _GREEN_("pm3 button") " to abort simulation or run another command"); uint8_t bs[64]; memset(bs, 0x00, sizeof(bs)); @@ -441,4 +449,3 @@ int getIOProxBits(uint8_t version, uint8_t fc, uint16_t cn, uint8_t *bits) { PrintAndLogEx(SUCCESS, "IO raw bits:\n %s \n", sprint_bytebits_bin(bits, 64)); return PM3_SUCCESS; } - diff --git a/client/src/cmdlfmotorola.c b/client/src/cmdlfmotorola.c index 90394be54..cd0c91864 100644 --- a/client/src/cmdlfmotorola.c +++ b/client/src/cmdlfmotorola.c @@ -128,7 +128,7 @@ int demodMotorola(bool verbose) { static int CmdMotorolaDemod(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf motorola demod", - "Try to find Motorola preamble, if found decode / descramble data", + "Try to find Motorola Flexpass preamble, if found decode / descramble data", "lf motorola demod" ); @@ -144,7 +144,7 @@ static int CmdMotorolaDemod(const char *Cmd) { static int CmdMotorolaReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf motorola reader", - "read a Motorola tag", + "read a Motorola Flexpass tag", "lf motorola reader -@ -> continuous reader mode" ); diff --git a/client/src/cmdlfparadox.c b/client/src/cmdlfparadox.c index a2fa50f11..daa8a350c 100644 --- a/client/src/cmdlfparadox.c +++ b/client/src/cmdlfparadox.c @@ -20,7 +20,6 @@ #include #include #include -#include #include "commonutil.h" // ARRAYLEN #include "cmdparser.h" // command_t #include "comms.h" @@ -53,13 +52,66 @@ static const uint8_t paradox_lut[] = { // Paradox Prox demod - FSK2a RF/50 with preamble of 00001111 (then manchester encoded) // print full Paradox Prox ID and some bit format details if found -int demodParadox(bool verbose) { +// This function will calculate the bitstream for a paradox card and place the result in bs. +// It returns the calculated CRC from the fc and cn. +// CRC calculation by mwalker33 +static uint8_t GetParadoxBits(const uint32_t fc, const uint32_t cn, unsigned int *bs) { + + uint8_t manchester[13] = { 0x00 }; // check size needed + uint32_t t1; + + manchester[0] = 0x0F; // preamble + manchester[1] = 0x05; // Leading zeros - Note: from this byte on, is part of the CRC calculation + manchester[2] = 0x55; // Leading zeros its 4 bits out for the CRC, so we need to move + manchester[3] = 0x55; // Leading zeros back 4 bits once we have the crc (done below) + + // add FC + t1 = manchesterEncode2Bytes(fc); + manchester[4] = (t1 >> 8) & 0xFF; + manchester[5] = t1 & 0xFF; + + // add cn + t1 = manchesterEncode2Bytes(cn); + manchester[6] = (t1 >> 24) & 0xFF; + manchester[7] = (t1 >> 16) & 0xFF; + manchester[8] = (t1 >> 8) & 0xFF; + manchester[9] = t1 & 0xFF; + + uint8_t crc = (CRC8Maxim(manchester + 1, 9) ^ 0x6) & 0xFF; + + // add crc + t1 = manchesterEncode2Bytes(crc); + manchester[10] = (t1 >> 8) & 0xFF; + manchester[11] = t1 & 0xFF; + + // move left 4 bits left 4 bits - Now that we have the CRC we need to re-align the data. + for (int i = 1; i < 12; i++) + manchester[i] = (manchester[i] << 4) + (manchester[i + 1] >> 4); + + // Add trailing 1010 (11) + manchester[11] |= (1 << 3); + manchester[11] |= (1 << 1); + + // move into tag blocks + + for (int i = 0; i < 12; i++) + bs[1 + (i / 4)] += (manchester[i] << (8 * (3 - i % 4))); + + return crc; +} + +int demodParadox(bool verbose, bool oldChksum) { (void) verbose; // unused so far //raw fsk demod no manchester decoding no start bit finding just get binary from wave - uint8_t bits[MAX_GRAPH_TRACE_LEN] = {0}; + uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t size = getFromGraphBuf(bits); if (size == 0) { PrintAndLogEx(DEBUG, "DEBUG: Error - Paradox not enough samples"); + free(bits); return PM3_ESOFT; } @@ -78,6 +130,7 @@ int demodParadox(bool verbose) { else PrintAndLogEx(DEBUG, "DEBUG: Error - Paradox error demoding fsk %d", idx); + free(bits); return PM3_ESOFT; } @@ -106,7 +159,7 @@ int demodParadox(bool verbose) { // not manchester data if (bits[i] == bits[i + 1]) { - PrintAndLogEx(WARNING, "Error Manchester at %u", i); + PrintAndLogEx(DEBUG, "Error Manchester at %u", i); errors++; } @@ -120,7 +173,7 @@ int demodParadox(bool verbose) { } if (errors) { - PrintAndLogEx(WARNING, "Total Manchester Errors... %u", errors); + PrintAndLogEx(DEBUG, "Total Manchester Errors... %u", errors); } setDemodBuff(bits, size, idx); @@ -128,40 +181,46 @@ int demodParadox(bool verbose) { if (hi2 == 0 && hi == 0 && lo == 0) { PrintAndLogEx(DEBUG, "DEBUG: Error - Paradox no value found"); + free(bits); return PM3_ESOFT; } uint32_t fc = ((hi & 0x3) << 6) | (lo >> 26); uint32_t cardnum = (lo >> 10) & 0xFFFF; uint8_t chksum = (lo >> 2) & 0xFF; + if (oldChksum) { + // Calc CRC & Checksum + // 000088f0b - FC: 8 - Card: 36619 - Checksum: 05 - RAW: 0f55555559595aa559a5566a + // checksum? + uint8_t calc_chksum = 0x47; + uint8_t pos = 0; + for (uint8_t i = 0; i < 8; i++) { + uint8_t ice = rawhex[i + 1]; + for (uint8_t j = 0x80; j > 0; j >>= 2) { - // Calc CRC & Checksum - // 000088f0b - FC: 8 - Card: 36619 - Checksum: 05 - RAW: 0f55555559595aa559a5566a - // checksum? - uint8_t calc_chksum = 0x47; - uint8_t pos = 0; - for (uint8_t i = 0; i < 8; i++) { - - uint8_t ice = rawhex[i + 1]; - for (uint8_t j = 0x80; j > 0; j >>= 2) { - - if (ice & j) { - calc_chksum ^= paradox_lut[pos]; + if (ice & j) { + calc_chksum ^= paradox_lut[pos]; + } + pos++; } - pos++; } + uint32_t crc = CRC8Maxim(rawhex + 1, 8); + PrintAndLogEx(INFO, " FSK/MAN raw : %s", sprint_hex(rawhex, sizeof(rawhex))); + PrintAndLogEx(INFO, " raw : %s = (maxim crc8) %02x == %02x", sprint_hex(rawhex + 1, 8), crc, + calc_chksum); + // PrintAndLogEx(DEBUG, " OTHER sample CRC-8/MAXIM : 55 55 69 A5 55 6A 59 5A = FC"); } - - uint32_t crc = CRC8Maxim(rawhex + 1, 8); - PrintAndLogEx(DEBUG, " FSK/MAN raw : %s", sprint_hex(rawhex, sizeof(rawhex))); - PrintAndLogEx(DEBUG, " raw : %s = (maxim crc8) %02x == %02x", sprint_hex(rawhex + 1, 8), crc, calc_chksum); -// PrintAndLogEx(DEBUG, " OTHER sample CRC-8/MAXIM : 55 55 69 A5 55 6A 59 5A = FC"); - uint32_t rawLo = bytebits_to_byte(bits + idx + 64, 32); uint32_t rawHi = bytebits_to_byte(bits + idx + 32, 32); uint32_t rawHi2 = bytebits_to_byte(bits + idx, 32); + uint32_t blocks[4] = {0}; + uint8_t crc = GetParadoxBits(fc, cardnum, blocks); + if (chksum != crc) + PrintAndLogEx(ERR, "CRC Error! Calculated CRC is " _GREEN_("%d") " but card CRC is " _RED_("%d") ".", crc, chksum); + + PrintAndLogEx(INFO, "Paradox - ID: " _GREEN_("%x%08x") " FC: " _GREEN_("%d") " Card: " _GREEN_("%d") ", Checksum: %02x, Raw: %08x%08x%08x", hi >> 10, (hi & 0x3) << 26 | (lo >> 10), @@ -178,6 +237,7 @@ int demodParadox(bool verbose) { printDemodBuff(0, false, false, false); } + free(bits); return PM3_SUCCESS; } @@ -185,32 +245,37 @@ static int CmdParadoxDemod(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf paradox demod", "Try to find Paradox preamble, if found decode / descramble data", - "lf paradox demod" + "lf paradox demod --old -> Display previous checksum version" ); void *argtable[] = { arg_param_begin, + arg_lit0(NULL, "old", "optional - Display previous checksum version"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + bool old = arg_get_lit(ctx, 1); CLIParserFree(ctx); - return demodParadox(true); + return demodParadox(true, old); } static int CmdParadoxReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf paradox reader", "read a Paradox tag", - "lf Paradox reader -@ -> continuous reader mode" + "lf paradox reader -@ -> continuous reader mode\n" + "lf paradox reader --old -> Display previous checksum version" ); void *argtable[] = { arg_param_begin, arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_lit0(NULL, "old", "optional - Display previous checksum version"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool cm = arg_get_lit(ctx, 1); + bool old = arg_get_lit(ctx, 2); CLIParserFree(ctx); if (cm) { @@ -219,7 +284,7 @@ static int CmdParadoxReader(const char *Cmd) { do { lf_read(false, 10000); - demodParadox(!cm); + demodParadox(!cm, old); } while (cm && !kbd_enter_pressed()); return PM3_SUCCESS; @@ -230,7 +295,7 @@ static int CmdParadoxClone(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf paradox clone", "clone a paradox tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", - "lf paradox clone --fc 96 --cn 40426 -> encode for T55x7 tag with fc and cn\n" + "lf paradox clone --fc 96 --cn 40426 -> encode for T55x7 tag with fc and cn\n" "lf paradox clone --raw 0f55555695596a6a9999a59a -> encode for T55x7 tag\n" "lf paradox clone --raw 0f55555695596a6a9999a59a --q5 -> encode for Q5/T5555 tag\n" "lf paradox clone --raw 0f55555695596a6a9999a59a --em -> encode for EM4305/4469" @@ -263,6 +328,16 @@ static int CmdParadoxClone(const char *Cmd) { return PM3_EINVARG; } + if ((fc || cn) && raw_len != 0) { + PrintAndLogEx(FAILED, "Can't specify both FC/CN and RAW at the same time"); + return PM3_EINVARG; + } + + if (fc > 999 || cn > 99999) { + PrintAndLogEx(FAILED, "FC has a max value of 999 and CN has a max value of 99999"); + return PM3_EINVARG; + } + uint32_t blocks[4] = {0}; if (raw_len != 0) { @@ -275,44 +350,8 @@ static int CmdParadoxClone(const char *Cmd) { blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); } } else { - uint8_t manchester[13] = { 0x00 }; // check size needed - uint32_t t1; - - manchester[0] = 0x0F; // preamble - manchester[1] = 0x05; // Leading zeros - Note: from this byte on, is part of the CRC calculation - manchester[2] = 0x55; // Leading zeros its 4 bits out for the CRC, so we need too move - manchester[3] = 0x55; // Leading zeros back 4 bits once we have the crc (done below) - - // add FC - t1 = manchesterEncode2Bytes(fc); - manchester[4] = (t1 >> 8) & 0xFF; - manchester[5] = t1 & 0xFF; - - // add cn - t1 = manchesterEncode2Bytes(cn); - manchester[6] = (t1 >> 24) & 0xFF; - manchester[7] = (t1 >> 16) & 0xFF; - manchester[8] = (t1 >> 8) & 0xFF; - manchester[9] = t1 & 0xFF; - - uint8_t crc = (CRC8Maxim(manchester + 1, 9) ^ 0x6) & 0xFF; - - // add crc - t1 = manchesterEncode2Bytes(crc); - manchester[10] = (t1 >> 8) & 0xFF; - manchester[11] = t1 & 0xFF; - - // move left 4 bits left 4 bits - Now that we have the CRC we need to re-align the data. - for (int i = 1; i < 12; i++) - manchester[i] = (manchester[i] << 4) + (manchester[i + 1] >> 4); - - // Add trailing 1010 (11) - manchester[11] |= (1 << 3); - manchester[11] |= (1 << 1); - - // move into tag blocks - for (int i = 0; i < 12; i++) - blocks[1 + (i / 4)] += (manchester[i] << (8 * (3 - i % 4))); + //This function generates the bitstream and puts it in blocks. it returns the crc, but we don't need it here + GetParadoxBits(fc, cn, blocks); } // Paradox - FSK2a, data rate 50, 3 data blocks @@ -355,12 +394,15 @@ static int CmdParadoxSim(const char *Cmd) { CLIParserInit(&ctx, "lf paradox sim", "Enables simulation of paradox card with specified card number.\n" "Simulation runs until the button is pressed or another USB command is issued.", - "lf paradox sim --raw 0f55555695596a6a9999a59a" + "lf paradox sim --raw 0f55555695596a6a9999a59a -> simulate tag\n" + "lf paradox sim --fc 96 --cn 40426 -> simulate tag with fc and cn\n" ); void *argtable[] = { arg_param_begin, - arg_str0("r", "raw", "", " raw hex data. 12 bytes"), + arg_str0("r", "raw", "", "raw hex data. 12 bytes"), + arg_u64_0(NULL, "fc", "", "facility code"), + arg_u64_0(NULL, "cn", "", "card number"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -369,13 +411,32 @@ static int CmdParadoxSim(const char *Cmd) { // skip first block, 3*4 = 12 bytes left uint8_t raw[12] = {0}; CLIGetHexWithReturn(ctx, 1, raw, &raw_len); + + uint32_t fc = arg_get_u32_def(ctx, 2, 0); + uint32_t cn = arg_get_u32_def(ctx, 3, 0); CLIParserFree(ctx); - if (raw_len != 12) { - PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); + if ((fc || cn) && raw_len != 0) { + PrintAndLogEx(FAILED, "Can't specify both FC/CN and RAW at the same time"); return PM3_EINVARG; } + if (fc > 999 || cn > 99999) { + PrintAndLogEx(FAILED, "FC has a max value of 999 and CN has a max value of 99999"); + return PM3_EINVARG; + } + if (raw_len != 0) { + if (raw_len != 12) { + PrintAndLogEx(ERR, "Data must be 12 bytes (24 HEX characters) %d", raw_len); + return PM3_EINVARG; + } + } else { + uint32_t blocks[4] = {0}; + GetParadoxBits(fc, cn, blocks); + for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { + num_to_bytes(blocks[i], sizeof(uint32_t), raw + ((i - 1) * 4)); + } + } PrintAndLogEx(SUCCESS, "Simulating Paradox - raw " _YELLOW_("%s"), sprint_hex_inrow(raw, sizeof(raw))); uint8_t bs[sizeof(raw) * 8]; @@ -404,21 +465,8 @@ static int CmdParadoxSim(const char *Cmd) { return PM3_SUCCESS; } -/* - if (sscanf(Cmd, "%u %u", &fc, &cn) != 2) return usage_lf_paradox_sim(); - facilitycode = (fc & 0x000000FF); - cardnumber = (cn & 0x0000FFFF); - - // if ( GetParadoxBits(facilitycode, cardnumber, bs) != PM3_SUCCESS) { - // PrintAndLogEx(ERR, "Error with tag bitstream generation."); - // return 1; - // } - - PrintAndLogEx(NORMAL, "Simulating Paradox - Facility Code: %u, CardNumber: %u", facilitycode, cardnumber); - -*/ static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"demod", CmdParadoxDemod, AlwaysAvailable, "demodulate a Paradox FSK tag from the GraphBuffer"}, @@ -460,5 +508,3 @@ int detectParadox(uint8_t *dest, size_t *size, int *wave_start_idx) { return (int)idx; } - - diff --git a/client/src/cmdlfparadox.h b/client/src/cmdlfparadox.h index 010f08301..142ee19a7 100644 --- a/client/src/cmdlfparadox.h +++ b/client/src/cmdlfparadox.h @@ -22,6 +22,6 @@ int CmdLFParadox(const char *Cmd); -int demodParadox(bool verbose); +int demodParadox(bool verbose, bool oldChksum); int detectParadox(uint8_t *dest, size_t *size, int *wave_start_idx); #endif diff --git a/client/src/cmdlfpyramid.c b/client/src/cmdlfpyramid.c index 80eeb251c..2ec65657c 100644 --- a/client/src/cmdlfpyramid.c +++ b/client/src/cmdlfpyramid.c @@ -43,10 +43,15 @@ static int CmdHelp(const char *Cmd); int demodPyramid(bool verbose) { (void) verbose; // unused so far //raw fsk demod no manchester decoding no start bit finding just get binary from wave - uint8_t bits[MAX_GRAPH_TRACE_LEN] = {0}; + uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(FAILED, "failed to allocate memory"); + return PM3_EMALLOC; + } size_t size = getFromGraphBuf(bits); if (size == 0) { PrintAndLogEx(DEBUG, "DEBUG: Error - Pyramid not enough samples"); + free(bits); return PM3_ESOFT; } //get binary from fsk wave @@ -65,6 +70,7 @@ int demodPyramid(bool verbose) { PrintAndLogEx(DEBUG, "DEBUG: Error - Pyramid: size not correct: %zu", size); else PrintAndLogEx(DEBUG, "DEBUG: Error - Pyramid: error demoding fsk idx: %d", idx); + free(bits); return PM3_ESOFT; } setDemodBuff(bits, size, idx); @@ -113,6 +119,7 @@ int demodPyramid(bool verbose) { PrintAndLogEx(DEBUG, "DEBUG: Error - Pyramid: parity check failed - IDX: %d, hi3: %08X", idx, rawHi3); else PrintAndLogEx(DEBUG, "DEBUG: Error - Pyramid: at parity check - tag size does not match Pyramid format, SIZE: %zu, IDX: %d, hi3: %08X", size, idx, rawHi3); + free(bits); return PM3_ESOFT; } @@ -181,6 +188,7 @@ int demodPyramid(bool verbose) { printDemodBuff(0, false, false, false); } + free(bits); return PM3_SUCCESS; } @@ -360,7 +368,7 @@ static int CmdPyramidSim(const char *Cmd) { "The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated.\n" "Currently work only on 26bit", "lf pyramid sim --fc 123 --cn 1337\n" - "lf pyramid clone --raw 0001010101010101010440013223921c" + "lf pyramid sim --raw 0001010101010101010440013223921c" ); void *argtable[] = { @@ -377,7 +385,7 @@ static int CmdPyramidSim(const char *Cmd) { int raw_len = 0; // skip first block, 4*4 = 16 bytes left uint8_t raw[16] = {0}; - int res = CLIParamHexToBuf(arg_get_str(ctx, 5), raw, sizeof raw, &raw_len); + int res = CLIParamHexToBuf(arg_get_str(ctx, 3), raw, sizeof raw, &raw_len); if (res) { CLIParserFree(ctx); return PM3_EINVARG; @@ -507,4 +515,3 @@ int detectPyramid(uint8_t *dest, size_t *size, int *waveStartIdx) { return (int)startIdx; } - diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 554658c9f..894ad3b5d 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -1677,7 +1677,7 @@ static int CmdT55xxDangerousRaw(const char *Cmd) { return PM3_EINVARG; } - int bs_len = binstring2binarray(ng.data, (char *)bin, bin_len); + int bs_len = binstr_2_binarray(ng.data, (char *)bin, bin_len); if (bs_len == 0) { return PM3_EINVARG; } @@ -2217,7 +2217,7 @@ static int CmdT55xxDump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf t55xx dump", "This command dumps a T55xx card Page 0 block 0-7.\n" - "It will create three files (bin/eml/json)", + "It will create two files (bin/json)", "lf t55xx dump\n" "lf t55xx dump -p aabbccdd --override\n" "lf t55xx dump -f my_lf_dump" @@ -2298,8 +2298,14 @@ static int CmdT55xxDump(const char *Cmd) { } } + if (nosave) { + PrintAndLogEx(INFO, "Called with no save option"); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + // all ok, save dump to file - if (success && nosave == false) { + if (success) { // set default filename, if not set by user if (strlen(filename) == 0) { @@ -2315,17 +2321,13 @@ static int CmdT55xxDump(const char *Cmd) { } // Swap endian so the files match the txt display - uint32_t data[T55x7_BLOCK_COUNT]; + uint32_t data[T55x7_BLOCK_COUNT] = {0}; for (int i = 0; i < T55x7_BLOCK_COUNT; i++) { data[i] = BSWAP_32(cardmem[i].blockdata); } - // saveFileEML will add .eml extension to filename - // saveFile (binary) passes in the .bin extension. - saveFileJSON(filename, jsfT55x7, (uint8_t *)data, T55x7_BLOCK_COUNT * sizeof(uint32_t), NULL); - saveFileEML(filename, (uint8_t *)data, T55x7_BLOCK_COUNT * sizeof(uint32_t), sizeof(uint32_t)); - saveFile(filename, ".bin", data, sizeof(data)); + pm3_save_dump(filename, (uint8_t *)data, (T55x7_BLOCK_COUNT * sizeof(uint32_t)), jsfT55x7); } return PM3_SUCCESS; @@ -2340,7 +2342,7 @@ static int CmdT55xxRestore(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dump file"), + arg_str0("f", "file", "", "Specify a filename for dump file"), arg_str0("p", "pwd", "", "password if target card has password set (4 hex bytes)"), arg_param_end }; @@ -3498,11 +3500,12 @@ out: // some return all page 1 (64 bits) and others return just that block (32 bits) // unfortunately the 64 bits makes this more likely to get a false positive... bool tryDetectP1(bool getData) { - uint8_t preamble[] = {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1}; - size_t startIdx = 0; - uint8_t fc1 = 0, fc2 = 0, ans = 0; - int clk = 0, firstClockEdge = 0; - bool st = true; + uint8_t preamble_atmel[] = {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1}; + uint8_t preamble_silicon[] = {1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1}; + size_t startIdx = 0; + uint8_t fc1 = 0, fc2 = 0, ans = 0; + int clk = 0, firstClockEdge = 0; + bool st = true; if (getData) { if (!AcquireData(T55x7_PAGE1, T55x7_TRACE_BLOCK1, false, 0, 0)) @@ -3512,15 +3515,29 @@ bool tryDetectP1(bool getData) { // try fsk clock detect. if successful it cannot be any other type of modulation... (in theory...) ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, &firstClockEdge); if (ans && ((fc1 == 10 && fc2 == 8) || (fc1 == 8 && fc2 == 5))) { - if ((FSKrawDemod(0, 0, 0, 0, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + + if (FSKrawDemod(0, 0, 0, 0, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } - if ((FSKrawDemod(0, 1, 0, 0, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + + if (FSKrawDemod(0, 1, 0, 0, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } return false; } @@ -3528,44 +3545,82 @@ bool tryDetectP1(bool getData) { // try ask clock detect. it could be another type even if successful. clk = GetAskClock("", false); if (clk > 0) { - if ((ASKDemod_ext(0, 0, 1, 0, false, false, false, 1, &st) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + if (ASKDemod_ext(0, 0, 1, 0, false, false, false, 1, &st) == PM3_SUCCESS) { + + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } st = true; - if ((ASKDemod_ext(0, 1, 1, 0, false, false, false, 1, &st) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + if (ASKDemod_ext(0, 1, 1, 0, false, false, false, 1, &st) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } - if ((ASKbiphaseDemod(0, 0, 0, 2, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + if (ASKbiphaseDemod(0, 0, 0, 2, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } - if ((ASKbiphaseDemod(0, 0, 1, 2, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + if (ASKbiphaseDemod(0, 0, 1, 2, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } } // try NRZ clock detect. it could be another type even if successful. clk = GetNrzClock("", false); //has the most false positives :( if (clk > 0) { - if ((NRZrawDemod(0, 0, 1, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + if (NRZrawDemod(0, 0, 1, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } - if ((NRZrawDemod(0, 1, 1, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - return true; + + if (NRZrawDemod(0, 1, 1, false) == PM3_SUCCESS) { + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } } @@ -3577,33 +3632,54 @@ bool tryDetectP1(bool getData) { // save_restoreGB(GRAPH_SAVE); // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) //CmdLtrim("-i 160"); - if ((PSKDemod(0, 0, 6, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + if (PSKDemod(0, 0, 6, false) == PM3_SUCCESS) { //save_restoreGB(GRAPH_RESTORE); - return true; + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } - if ((PSKDemod(0, 1, 6, false) == PM3_SUCCESS) && - preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && - (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + + if (PSKDemod(0, 1, 6, false) == PM3_SUCCESS) { //save_restoreGB(GRAPH_RESTORE); - return true; + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } } + // PSK2 - needs a call to psk1TOpsk2. if (PSKDemod(0, 0, 6, false) == PM3_SUCCESS) { psk1TOpsk2(g_DemodBuffer, g_DemodBufferLen); - if (preambleSearchEx(g_DemodBuffer, preamble, sizeof(preamble), &g_DemodBufferLen, &startIdx, false) && + + //save_restoreGB(GRAPH_RESTORE); + if (preambleSearchEx(g_DemodBuffer, preamble_atmel, sizeof(preamble_atmel), &g_DemodBufferLen, &startIdx, false) && + (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { + return true; + } + + if (preambleSearchEx(g_DemodBuffer, preamble_silicon, sizeof(preamble_silicon), &g_DemodBufferLen, &startIdx, false) && (g_DemodBufferLen == 32 || g_DemodBufferLen == 64)) { - //save_restoreGB(GRAPH_RESTORE); return true; } } // inverse waves does not affect PSK2 demod //undo trim samples //save_restoreGB(GRAPH_RESTORE); // no other modulation clocks = 2 or 4 so quit searching - if (fc1 != 8) return false; + if (fc1 != 8) { + return false; + } } - return false; } // does this need to be a callable command? @@ -4028,7 +4104,7 @@ static int CmdT55xxSniff(const char *Cmd) { if (use_graphbuf == false) { // make loop to call sniff with skip samples.. - // then build it up by adding + // then build it up by adding CmdLFSniff(""); } @@ -4139,8 +4215,9 @@ static int CmdT55xxSniff(const char *Cmd) { blockAddr = 0; for (uint8_t i = 3; i < 6; i++) { blockAddr <<= 1; - if (data[i] == '1') + if (data[i] == '1') { blockAddr |= 1; + } } blockData = 0; have_data = true; @@ -4155,21 +4232,26 @@ static int CmdT55xxSniff(const char *Cmd) { usedPassword = 0; for (uint8_t i = 2; i <= 33; i++) { usedPassword <<= 1; - if (data[i] == '1') + if (data[i] == '1') { usedPassword |= 1; + } } + // Lock bit 34 blockData = 0; for (uint8_t i = 35; i <= 66; i++) { blockData <<= 1; - if (data[i] == '1') + if (data[i] == '1') { blockData |= 1; + } } + blockAddr = 0; for (uint8_t i = 67; i <= 69; i++) { blockAddr <<= 1; - if (data[i] == '1') + if (data[i] == '1') { blockAddr |= 1; + } } have_data = true; modeText = "Default pwd write"; @@ -4190,20 +4272,24 @@ static int CmdT55xxSniff(const char *Cmd) { blockData = 0; for (uint8_t i = 3; i <= 34; i++) { blockData <<= 1; - if (data[i] == '1') + if (data[i] == '1') { blockData |= 1; + } } + for (uint8_t i = 2; i <= 33; i++) { usedPassword <<= 1; if (data[i] == '1') { usedPassword |= 1; } } + blockAddr = 0; for (uint8_t i = 35; i <= 37; i++) { blockAddr <<= 1; - if (data[i] == '1') + if (data[i] == '1') { blockAddr |= 1; + } } have_data = true; modeText = "Default write/pwd read"; @@ -4227,21 +4313,27 @@ static int CmdT55xxSniff(const char *Cmd) { usedPassword = 0; for (uint8_t i = 5; i <= 36; i++) { usedPassword <<= 1; - if (data[i] == '1') + if (data[i] == '1') { usedPassword |= 1; + } } + blockData = 0; for (uint8_t i = 38; i <= 69; i++) { blockData <<= 1; - if (data[i] == '1') + if (data[i] == '1') { blockData |= 1; + } } + blockAddr = 0; for (uint8_t i = 70; i <= 72; i++) { blockAddr <<= 1; - if (data[i] == '1') + if (data[i] == '1') { blockAddr |= 1; + } } + have_data = true; modeText = "Leading 0 pwd write"; snprintf(pwdText, sizeof(pwdText), " %08X", usedPassword); @@ -4253,10 +4345,29 @@ static int CmdT55xxSniff(const char *Cmd) { // Print results if (have_data) { - if (blockAddr == 7) - PrintAndLogEx(SUCCESS, "%-22s | "_GREEN_("%10s")" | "_YELLOW_("%8s")" | "_YELLOW_("%d")" | "_GREEN_("%d")" | %3d | %3d | %s", modeText, pwdText, dataText, blockAddr, page, minWidth, maxWidth, data); - else - PrintAndLogEx(SUCCESS, "%-22s | "_GREEN_("%10s")" | "_GREEN_("%8s")" | "_GREEN_("%d")" | "_GREEN_("%d")" | %3d | %3d | %s", modeText, pwdText, dataText, blockAddr, page, minWidth, maxWidth, data); + if (blockAddr == 7) { + PrintAndLogEx(SUCCESS, "%-22s | "_GREEN_("%10s")" | "_YELLOW_("%8s")" | "_YELLOW_("%d")" | "_GREEN_("%d")" | %3d | %3d | %s" + , modeText + , pwdText + , dataText + , blockAddr + , page + , minWidth + , maxWidth + , data + ); + } else { + PrintAndLogEx(SUCCESS, "%-22s | "_GREEN_("%10s")" | "_GREEN_("%8s")" | "_GREEN_("%d")" | "_GREEN_("%d")" | %3d | %3d | %s" + , modeText + , pwdText + , dataText + , blockAddr + , page + , minWidth + , maxWidth + , data + ); + } } } diff --git a/client/src/cmdlft55xx.h b/client/src/cmdlft55xx.h index def6a5be6..6dbab378c 100644 --- a/client/src/cmdlft55xx.h +++ b/client/src/cmdlft55xx.h @@ -21,15 +21,15 @@ #include "common.h" -#define T55x7_CONFIGURATION_BLOCK 0x00 -#define T55x7_PWD_BLOCK 0x07 -#define T55x7_TRACE_BLOCK1 0x01 -#define T55x7_TRACE_BLOCK2 0x02 -#define T55x7_PAGE0 0x00 -#define T55x7_PAGE1 0x01 -#define T55x7_PWD 0x00000010 -#define REGULAR_READ_MODE_BLOCK 0xFF -#define T55x7_BLOCK_COUNT 12 +#define T55x7_CONFIGURATION_BLOCK 0x00 +#define T55x7_PWD_BLOCK 0x07 +#define T55x7_TRACE_BLOCK1 0x01 +#define T55x7_TRACE_BLOCK2 0x02 +#define T55x7_PAGE0 0x00 +#define T55x7_PAGE1 0x01 +#define T55x7_PWD 0x00000010 +#define REGULAR_READ_MODE_BLOCK 0xFF +#define T55x7_BLOCK_COUNT 12 // config blocks #define T55X7_DEFAULT_CONFIG_BLOCK 0x000880E8 // ASK, compat mode, data rate 32, manchester, STT, 7 data blocks @@ -70,6 +70,7 @@ #define T55X7_PAC_CONFIG_BLOCK 0x00080080 // NRZ, data rate 32, 4 data blocks #define T55X7_VERICHIP_CONFIG_BLOCK 0x000C0080 // NRZ, data rate 40, 4 data blocks + #define T55X7_bin 0b0010 // Q5 / Termic / T5555 diff --git a/client/src/cmdnfc.c b/client/src/cmdnfc.c index eadfa7161..06ddf0f10 100644 --- a/client/src/cmdnfc.c +++ b/client/src/cmdnfc.c @@ -109,30 +109,52 @@ static int CmdNfcDecode(const char *Cmd) { uint8_t *dump = NULL; size_t bytes_read = 4096; res = pm3_load_dump(filename, (void **)&dump, &bytes_read, 4096); - if (res != PM3_SUCCESS || dump == NULL) { + if (res != PM3_SUCCESS || dump == NULL || bytes_read > 4096) { return res; } - // convert from MFC dump file to a pure NDEF byte array - if (HasMADKey(dump)) { - PrintAndLogEx(SUCCESS, "MFC dump file detected. Converting..."); - uint8_t ndef[4096] = {0}; - uint16_t ndeflen = 0; + uint8_t *tmp = dump; - if (convert_mad_to_arr(dump, bytes_read, ndef, &ndeflen) != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Failed converting, aborting..."); - free(dump); - return PM3_ESOFT; + // if not MIFARE Classic default sizes, assume its Ultralight/NTAG + if (bytes_read != MIFARE_4K_MAX_BYTES + && bytes_read != MIFARE_2K_MAX_BYTES + && bytes_read != MIFARE_1K_MAX_BYTES + && bytes_read != MIFARE_MINI_MAX_BYTES) { + + uint8_t **pd = &tmp; + mfu_df_e df = detect_mfu_dump_format(pd, verbose); + if (df == MFU_DF_OLDBIN) { + tmp += OLD_MFU_DUMP_PREFIX_LENGTH + (4 * 4); + bytes_read -= OLD_MFU_DUMP_PREFIX_LENGTH + (4 * 4); + } else if (df == MFU_DF_NEWBIN) { + tmp += MFU_DUMP_PREFIX_LENGTH + (4 * 4); + bytes_read -= MFU_DUMP_PREFIX_LENGTH + (4 * 4); } + pd = NULL; - memcpy(dump, ndef, ndeflen); - bytes_read = ndeflen; + } else { + + // convert from MFC dump file to a pure NDEF byte array + if (HasMADKey(tmp)) { + PrintAndLogEx(SUCCESS, "MFC dump file detected. Converting..."); + uint8_t ndef[4096] = {0}; + uint16_t ndeflen = 0; + + if (convert_mad_to_arr(tmp, bytes_read, ndef, &ndeflen) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Failed converting, aborting..."); + free(dump); + return PM3_ESOFT; + } + + memcpy(tmp, ndef, ndeflen); + bytes_read = ndeflen; + } } - res = NDEFDecodeAndPrint(dump, bytes_read, verbose); + res = NDEFDecodeAndPrint(tmp, bytes_read, verbose); if (res != PM3_SUCCESS) { PrintAndLogEx(INFO, "Trying to parse NDEF records w/o NDEF header"); - res = NDEFRecordsDecodeAndPrint(dump, bytes_read, verbose); + res = NDEFRecordsDecodeAndPrint(tmp, bytes_read, verbose); } free(dump); diff --git a/client/src/cmdscript.c b/client/src/cmdscript.c index 0abab9b01..91c1ce50f 100644 --- a/client/src/cmdscript.c +++ b/client/src/cmdscript.c @@ -22,6 +22,7 @@ #ifdef HAVE_PYTHON //#define PY_SSIZE_T_CLEAN #include +#include #include #endif diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index 9c273739d..b8fbef3a7 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -20,6 +20,7 @@ #include #include "cmdparser.h" // command_t #include "commonutil.h" // ARRAYLEN +#include "iso7816/iso7816core.h" #include "protocols.h" #include "cmdtrace.h" #include "proxmark3.h" @@ -33,6 +34,16 @@ #include "cliparser.h" // cliparsing #include "atrs.h" // ATR lookup +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch-enum" +#include "mbedtls/net_sockets.h" +#pragma GCC diagnostic pop + +#include "mifare.h" +#include "util_posix.h" +#include "cmdhf14a.h" +#include "cmdhf14b.h" + static int CmdHelp(const char *Cmd); static int smart_loadjson(const char *preferredName, json_t **root) { @@ -174,7 +185,7 @@ static void PrintATR(uint8_t *atr, size_t atrlen) { if (T0 & 0x80) { uint8_t TD1 = atr[2 + T1len]; - PrintAndLogEx(INFO, " - TD1 (First offered transmission protocol, presence of TA2..TD2) [ 0x%02x ] Protocol T%d", TD1, TD1 & 0x0f); + PrintAndLogEx(INFO, " - TD1 (First offered transmission protocol, presence of TA2..TD2) [ 0x%02x ] Protocol " _GREEN_("T%d"), TD1, TD1 & 0x0f); protocol_T0_present = false; if ((TD1 & 0x0f) == 0) { protocol_T0_present = true; @@ -199,7 +210,7 @@ static void PrintATR(uint8_t *atr, size_t atrlen) { } if (TD1 & 0x80) { uint8_t TDi = atr[2 + T1len + TD1len]; - PrintAndLogEx(INFO, " - TD2 (A supported protocol or more global parameters, presence of TA3..TD3) [ 0x%02x ] Protocol T%d", TDi, TDi & 0x0f); + PrintAndLogEx(INFO, " - TD2 (A supported protocol or more global parameters, presence of TA3..TD3) [ 0x%02x ] Protocol " _GREEN_("T%d"), TDi, TDi & 0x0f); if ((TDi & 0x0f) == 0) { protocol_T0_present = true; } @@ -328,7 +339,7 @@ static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { needGetData = true; } - if (needGetData == true) { + if (needGetData) { // Don't discard data we already received except the SW code. // If we only received 1 byte, this is the echo of INS, we discard it. totallen -= 2; @@ -355,6 +366,7 @@ static int smart_responseEx(uint8_t *out, int maxoutlen, bool verbose) { smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + sizeof(cmd_getresp)); payload->flags = SC_RAW | SC_LOG; payload->len = sizeof(cmd_getresp); + payload->wait_delay = 0; memcpy(payload->data, cmd_getresp, sizeof(cmd_getresp)); clearCommandBuffer(); @@ -416,6 +428,7 @@ static int CmdSmartRaw(const char *Cmd) { arg_lit0("s", NULL, "active smartcard with select (get ATR)"), arg_lit0("t", "tlv", "executes TLV decoder if it possible"), arg_lit0("0", NULL, "use protocol T=0"), + arg_int0(NULL, "timeout", "", "Timeout in MS waiting for SIM to respond. (def 337ms)"), arg_str1("d", "data", "", "bytes to send"), arg_param_end }; @@ -426,10 +439,11 @@ static int CmdSmartRaw(const char *Cmd) { bool active_select = arg_get_lit(ctx, 3); bool decode_tlv = arg_get_lit(ctx, 4); bool use_t0 = arg_get_lit(ctx, 5); + int timeout = arg_get_int_def(ctx, 6, -1); int dlen = 0; uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; - int res = CLIParamHexToBuf(arg_get_str(ctx, 6), data, sizeof(data), &dlen); + int res = CLIParamHexToBuf(arg_get_str(ctx, 7), data, sizeof(data), &dlen); CLIParserFree(ctx); if (res) { @@ -453,6 +467,13 @@ static int CmdSmartRaw(const char *Cmd) { payload->flags |= SC_SELECT; } + payload->wait_delay = 0; + if (timeout > -1) { + payload->flags |= SC_WAIT; + payload->wait_delay = timeout; + } + PrintAndLogEx(DEBUG, "SIM Card timeout... %u ms", payload->wait_delay); + if (dlen > 0) { if (use_t0) payload->flags |= SC_RAW_T0; @@ -495,9 +516,9 @@ static int CmdSmartRaw(const char *Cmd) { data[4] = 0; } - if (decode_tlv && len > 4) + if (decode_tlv && len > 4) { TLVPrintFromBuffer(buf, len - 2); - else { + } else { if (len > 2) { PrintAndLogEx(INFO, "Response data:"); PrintAndLogEx(INFO, " # | bytes | ascii"); @@ -513,16 +534,16 @@ out: } static int CmdSmartUpgrade(const char *Cmd) { - PrintAndLogEx(INFO, "-------------------------------------------------------------------"); + PrintAndLogEx(INFO, "--------------------------------------------------------------------"); PrintAndLogEx(WARNING, _RED_("WARNING") " - sim module firmware upgrade"); PrintAndLogEx(WARNING, _RED_("A dangerous command, do wrong and you could brick the sim module")); - PrintAndLogEx(INFO, "-------------------------------------------------------------------"); + PrintAndLogEx(INFO, "--------------------------------------------------------------------"); PrintAndLogEx(NORMAL, ""); CLIParserContext *ctx; CLIParserInit(&ctx, "smart upgrade", "Upgrade RDV4 sim module firmware", - "smart upgrade -f sim013.bin" + "smart upgrade -f sim014.bin" ); void *argtable[] = { @@ -580,9 +601,10 @@ static int CmdSmartUpgrade(const char *Cmd) { } hashstring[128] = '\0'; + int hash1n = 0; uint8_t hash_1[64]; - if (param_gethex(hashstring, 0, hash_1, 128)) { - PrintAndLogEx(FAILED, "Couldn't read SHA-512 file"); + if (param_gethex_ex(hashstring, 0, hash_1, &hash1n) && hash1n != 128) { + PrintAndLogEx(FAILED, "Couldn't read SHA-512 file. expect 128 hex bytes, got ( "_RED_("%d") " )", hash1n); free(hashstring); free(firmware); return PM3_ESOFT; @@ -898,6 +920,7 @@ static void smart_brute_prim(void) { smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + 5); payload->flags = SC_RAW_T0; payload->len = 5; + payload->wait_delay = 0; memcpy(payload->data, get_card_data + i, 5); clearCommandBuffer(); @@ -941,6 +964,7 @@ static int smart_brute_sfi(bool decodeTLV) { smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + sizeof(READ_RECORD)); payload->flags = SC_RAW_T0; payload->len = sizeof(READ_RECORD); + payload->wait_delay = 0; memcpy(payload->data, READ_RECORD, sizeof(READ_RECORD)); clearCommandBuffer(); @@ -1094,7 +1118,7 @@ static int CmdSmartBruteforceSFI(const char *Cmd) { smart_card_raw_t *payload = calloc(1, sizeof(smart_card_raw_t) + hexlen); payload->flags = SC_RAW_T0; payload->len = hexlen; - + payload->wait_delay = 0; memcpy(payload->data, cmddata, hexlen); clearCommandBuffer(); SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + hexlen); @@ -1145,10 +1169,202 @@ static int CmdSmartBruteforceSFI(const char *Cmd) { return PM3_SUCCESS; } +static void atsToEmulatedAtr(uint8_t *ats, uint8_t *atr, int *atrLen) { + int historicalLen = 0; + int offset = 2; + + if (ats[0] < 2) { + historicalLen = 0; + } else { + + if ((ats[1] & 64) != 0) { + offset++; + } + if ((ats[1] & 32) != 0) { + offset++; + } + if ((ats[1] & 16) != 0) { + offset++; + } + + if (offset >= ats[0]) { + historicalLen = 0; + } else { + historicalLen = ats[0] - offset; + } + } + + atr[0] = 0x3B; + atr[1] = 0x80 | historicalLen; + atr[2] = 0x80; + atr[3] = 0x01; + + uint8_t tck = atr[1] ^ atr[2] ^ atr[3]; + for (int i = 0; i < historicalLen; ++i) { + atr[4 + i] = ats[offset + i]; + tck = tck ^ ats[offset + i]; + } + atr[4 + historicalLen] = tck; + + *atrLen = 5 + historicalLen; +} + +static void atqbToEmulatedAtr(uint8_t *atqb, uint8_t cid, uint8_t *atr, int *atrLen) { + atr[0] = 0x3B; + atr[1] = 0x80 | 8; + atr[2] = 0x80; + atr[3] = 0x01; + + memcpy(atr + 4, atqb, 7); + atr[11] = cid >> 4; + + uint8_t tck = 0; + for (int i = 1; i < 12; ++i) { + tck = tck ^ atr[i]; + } + atr[12] = tck; + + *atrLen = 13; +} + +static int CmdRelay(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "smart relay", + "Make pm3 available to host OS smartcard driver via vpcd to enable use with other software such as GlobalPlatform Pro", + "Requires the virtual smartcard daemon to be installed and running, see https://frankmorgner.github.io/vsmartcard/virtualsmartcard/README.html" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "host", "", "vpcd socket host (default: localhost)"), + arg_str0("p", "port", "", "vpcd socket port (default: 35963)"), + arg_lit0("v", "verbose", "display APDU transactions between OS and card"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t host[100] = {0}; + int hostLen = sizeof(host); + CLIGetStrWithReturn(ctx, 1, host, &hostLen); + if (hostLen == 0) { + strcpy((char *) host, "localhost"); + } + + uint8_t port[6] = {0}; + int portLen = sizeof(port); + CLIGetStrWithReturn(ctx, 2, port, &portLen); + if (portLen == 0) { + strcpy((char *) port, "35963"); + } + + bool verbose = arg_get_lit(ctx, 3); + + CLIParserFree(ctx); + + mbedtls_net_context netCtx; + mbedtls_net_init(&netCtx); + + PrintAndLogEx(INFO, "Relaying PM3 to host OS pcsc daemon"); + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + + uint8_t cmdbuf[512] = {0}; + iso14a_card_select_t selectedCard14a; + iso14b_card_select_t selectedCard14b; + isodep_state_t cardType = ISODEP_INACTIVE; + bool fieldActivated = false; + + do { + if (cardType != ISODEP_INACTIVE) { + int bytesRead = mbedtls_net_recv_timeout(&netCtx, cmdbuf, sizeof(cmdbuf), 100); + + if (bytesRead == MBEDTLS_ERR_SSL_TIMEOUT || bytesRead == MBEDTLS_ERR_SSL_WANT_READ) { + continue; + } + + if (bytesRead > 0) { + if (cmdbuf[1] == 0x01 && cmdbuf[2] == 0x04) { // vpcd GET ATR + uint8_t atr[20] = {0}; + int atrLen = 0; + + if (cardType == ISODEP_NFCA) { + atsToEmulatedAtr(selectedCard14a.ats, atr, &atrLen); + } else if (cardType == ISODEP_NFCB) { + atqbToEmulatedAtr(selectedCard14b.atqb, selectedCard14b.cid, atr, &atrLen); + } + + uint8_t res[22] = {0}; + res[1] = atrLen; + memcpy(res + 2, atr, atrLen); + mbedtls_net_send(&netCtx, res, 2 + atrLen); + } else if (cmdbuf[1] != 0x01) { // vpcd APDU + int apduLen = (cmdbuf[0] << 8) + cmdbuf[1]; + + uint8_t apduRes[APDU_RES_LEN] = {0}; + int apduResLen = 0; + + if (verbose) { + PrintAndLogEx(INFO, ">> %s", sprint_hex(cmdbuf + 2, apduLen)); + } + + if (cardType == ISODEP_NFCA) { + if (ExchangeAPDU14a(cmdbuf + 2, apduLen, !fieldActivated, true, apduRes, sizeof(apduRes), &apduResLen) != PM3_SUCCESS) { + cardType = ISODEP_INACTIVE; + mbedtls_net_close(&netCtx); + continue; + } + } else if (cardType == ISODEP_NFCB) { + if (exchange_14b_apdu(cmdbuf + 2, apduLen, !fieldActivated, true, apduRes, sizeof(apduRes), &apduResLen, 0)) { + cardType = ISODEP_INACTIVE; + mbedtls_net_close(&netCtx); + continue; + } + } + + fieldActivated = true; + + if (verbose) { + PrintAndLogEx(INFO, "<< %s", sprint_hex(apduRes, apduResLen)); + } + + uint8_t res[APDU_RES_LEN + 2] = {0}; + res[0] = (apduResLen >> 8) & 0xFF; + res[1] = apduResLen & 0xFF; + memcpy(res + 2, apduRes, apduResLen); + mbedtls_net_send(&netCtx, res, 2 + apduResLen); + } + } + } else { + if (IfPm3Iso14443a() && SelectCard14443A_4(false, false, &selectedCard14a) == PM3_SUCCESS) { + cardType = ISODEP_NFCA; + } else if (IfPm3Iso14443b() && select_card_14443b_4(false, &selectedCard14b) == PM3_SUCCESS) { + cardType = ISODEP_NFCB; + } + if (cardType != ISODEP_INACTIVE) { + fieldActivated = false; + if (mbedtls_net_connect(&netCtx, (char *) host, (char *) port, MBEDTLS_NET_PROTO_TCP)) { + PrintAndLogEx(FAILED, "Failed to connect to vpcd socket. Ensure you have vpcd installed and running"); + mbedtls_net_close(&netCtx); + mbedtls_net_free(&netCtx); + DropField(); + return PM3_EINVARG; + } + } + msleep(300); + } + } while (!kbd_enter_pressed()); + + mbedtls_net_close(&netCtx); + mbedtls_net_free(&netCtx); + DropField(); + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdSmartList, AlwaysAvailable, "List ISO 7816 history"}, {"info", CmdSmartInfo, IfPm3Smartcard, "Tag information"}, + {"relay", CmdRelay, IfPm3Iso14443a, "Turn pm3 into pcsc reader and relay to host OS via vpcd"}, {"reader", CmdSmartReader, IfPm3Smartcard, "Act like an IS07816 reader"}, {"raw", CmdSmartRaw, IfPm3Smartcard, "Send raw hex data to tag"}, {"upgrade", CmdSmartUpgrade, AlwaysAvailable, "Upgrade sim module firmware"}, @@ -1178,6 +1394,7 @@ int ExchangeAPDUSC(bool verbose, uint8_t *datain, int datainlen, bool activateCa payload->flags |= (SC_SELECT | SC_CONNECT); } payload->len = datainlen; + payload->wait_delay = 0; memcpy(payload->data, datain, datainlen); clearCommandBuffer(); @@ -1186,7 +1403,7 @@ int ExchangeAPDUSC(bool verbose, uint8_t *datain, int datainlen, bool activateCa int len = smart_responseEx(dataout, maxdataoutlen, verbose); if (len < 0) { free(payload); - return 1; + return PM3_ESOFT; } // retry @@ -1201,16 +1418,21 @@ int ExchangeAPDUSC(bool verbose, uint8_t *datain, int datainlen, bool activateCa SendCommandNG(CMD_SMART_RAW, (uint8_t *)payload, sizeof(smart_card_raw_t) + 5); datain[4] = 0; len = smart_responseEx(dataout, maxdataoutlen, verbose); + if (len < 0) { + free(payload); + return PM3_ESOFT; + } } free(payload); *dataoutlen = len; - return 0; + return PM3_SUCCESS; } bool smart_select(bool verbose, smart_card_atr_t *atr) { - if (atr) + if (atr) { memset(atr, 0, sizeof(smart_card_atr_t)); + } clearCommandBuffer(); SendCommandNG(CMD_SMART_ATR, NULL, 0); @@ -1228,8 +1450,9 @@ bool smart_select(bool verbose, smart_card_atr_t *atr) { smart_card_atr_t card; memcpy(&card, (smart_card_atr_t *)resp.data.asBytes, sizeof(smart_card_atr_t)); - if (atr) + if (atr) { memcpy(atr, &card, sizeof(smart_card_atr_t)); + } if (verbose) PrintAndLogEx(INFO, "ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len)); diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index d3096307a..9ee7b256a 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -91,6 +91,23 @@ static uint8_t calc_pos(const uint8_t *d) { return pos; } +// Copy an existing buffer into client trace buffer +// I think this is cleaner than further globalizing gs_trace, and may lend itself to more modularity later? +bool ImportTraceBuffer(uint8_t *trace_src, uint16_t trace_len) { + if (trace_len == 0 || trace_src == NULL) return (false); + if (gs_trace) { + free(gs_trace); + gs_traceLen = 0; + } + gs_trace = calloc(trace_len, sizeof(uint8_t)); + if (gs_trace == NULL) { + return (false); + } + memcpy(gs_trace, trace_src, trace_len); + gs_traceLen = trace_len; + return (true); +} + static uint8_t extract_uid[10] = {0}; static uint8_t extract_uidlen = 0; static uint8_t extract_epurse[8] = {0}; @@ -533,6 +550,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr crcStatus = !felica_CRC_check(frame + 2, data_len - 4); break; case PROTO_MIFARE: + case PROTO_MFPLUS: crcStatus = mifare_CRC_check(hdr->isResponse, frame, data_len); break; case ISO_14443A: @@ -570,8 +588,12 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr //1 CRC-command, CRC ok //2 Not crc-command - //--- Draw the data column - char line[18][160] = {{0}}; + // Draw the data column +#define TRACE_MAX_LINES 36 + // number of hex bytes to be printed per row (16 data + 2 crc) +#define TRACE_MAX_HEX_BYTES 18 + + char line[TRACE_MAX_LINES][160] = {{0}}; if (data_len == 0) { if (protocol == ICLASS && duration == 2048) { @@ -582,9 +604,10 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr snprintf(line[0], sizeof(line[0]), ""); } } + uint8_t partialbytebuff = 0; uint8_t offset = 0; - for (int j = 0; j < data_len && j / 18 < 18; j++) { + for (int j = 0; j < data_len && (j / TRACE_MAX_HEX_BYTES) < TRACE_MAX_HEX_BYTES; j++) { uint8_t parityBits = parityBytes[j >> 3]; if (protocol != LEGIC && protocol != ISO_14443B @@ -598,15 +621,18 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr && protocol != FELICA && protocol != LTO && protocol != PROTO_CRYPTORF - && (hdr->isResponse || protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == SEOS) + && (hdr->isResponse || protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS || protocol == SEOS) && (oddparity8(frame[j]) != ((parityBits >> (7 - (j & 0x0007))) & 0x01))) { snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x! ", frame[j]); + } else if (protocol == ICLASS && hdr->isResponse == false) { + uint8_t parity = 0; for (int i = 0; i < 6; i++) { parity ^= ((frame[0] >> i) & 1); } + if (parity == ((frame[0] >> 7) & 1)) { snprintf(line[j / 18] + ((j % 18) * 4), 120, "%02x ", frame[j]); } else { @@ -631,10 +657,8 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } - - uint8_t crc_format_string_offset = 0; if (markCRCBytes && data_len > 2) { - //CRC-command + // CRC-command if (((protocol == PROTO_HITAG1) || (protocol == PROTO_HITAGS)) && (data_len > 1)) { // Note that UID REQUEST response has no CRC, but we don't know // if the response we see is a UID @@ -647,36 +671,43 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr if (crcStatus == 0 || crcStatus == 1) { - char *pos1 = line[(data_len - 2) / 18]; - pos1 += (((data_len - 2) % 18) * 4) - 1; + char *pos1 = line[(data_len - 2) / TRACE_MAX_HEX_BYTES]; + int delta = (data_len - 2) % TRACE_MAX_HEX_BYTES ? 1 : 0; + pos1 += (((data_len - 2) % TRACE_MAX_HEX_BYTES) * 4) - delta; - (*(pos1 + 6 + 1)) = '\0'; + (*(pos1 + 6 + delta)) = '\0'; - char *cb_str = str_dup(pos1 + 1); + char *cb_str = str_dup(pos1 + delta); - if (hdr->isResponse) { - if (g_session.supports_colors) { - if (crcStatus == 0) { - snprintf(pos1, 24, " " _RED_("%s") " ", cb_str); - } else { - snprintf(pos1, 24, " " _GREEN_("%s") " ", cb_str); - } - crc_format_string_offset = 9; + if (g_session.supports_colors) { + if (crcStatus == 0) { + snprintf(pos1, 24, AEND " " _RED_("%s"), cb_str); } else { - snprintf(pos1, 9, "[%s]", cb_str); + snprintf(pos1, 24, AEND " " _GREEN_("%s"), cb_str); } } else { + snprintf(pos1, 9, "[%s]", cb_str); + } + + // odd case of second crc byte is alone in a new line + if (strlen(cb_str) < 5) { + + free(cb_str); + + pos1 = line[((data_len - 2) / TRACE_MAX_HEX_BYTES) + 1]; + cb_str = str_dup(pos1); + if (g_session.supports_colors) { if (crcStatus == 0) { - snprintf(pos1, 24, AEND " " _RED_("%s") " ", cb_str); + snprintf(pos1, 24, _RED_("%s"), cb_str); } else { - snprintf(pos1, 24, AEND " " _GREEN_("%s") " ", cb_str); + snprintf(pos1, 24, _GREEN_("%s"), cb_str); } - crc_format_string_offset = 13; } else { snprintf(pos1, 9, "[%s]", cb_str); } } + free(cb_str); } } @@ -689,6 +720,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr // mark short bytes (less than 8 Bit + Parity) if (protocol == ISO_14443A || protocol == PROTO_MIFARE || + protocol == PROTO_MFPLUS || protocol == THINFILM) { // approximated with 128 * (9 * data_len); @@ -735,6 +767,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr annotateIso14443a(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); break; case PROTO_MIFARE: + case PROTO_MFPLUS: annotateMifare(explanation, sizeof(explanation), frame, data_len, parityBytes, TRACELOG_PARITY_LEN(hdr), hdr->isResponse); break; case PROTO_HITAG1: @@ -754,7 +787,6 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } if (hdr->isResponse == false) { - switch (protocol) { case LEGIC: annotateLegic(explanation, sizeof(explanation), frame, data_len); @@ -762,6 +794,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr case MFDES: annotateMfDesfire(explanation, sizeof(explanation), frame, data_len); break; + case PROTO_MFPLUS: + annotateMfPlus(explanation, sizeof(explanation), frame, data_len); + break; case ISO_14443B: annotateIso14443b(explanation, sizeof(explanation), frame, data_len); break; @@ -791,8 +826,14 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } } - int num_lines = MIN((data_len - 1) / 18 + 1, 18); + int str_padder = 72; + int num_lines = MIN((data_len - 1) / TRACE_MAX_HEX_BYTES + 1, TRACE_MAX_HEX_BYTES); + for (int j = 0; j < num_lines ; j++) { + + bool last_line = (j == num_lines - 1); + str_padder = 72; + if (j == 0) { uint32_t time1 = hdr->timestamp - first_hdr->timestamp; @@ -802,25 +843,30 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr time2 = duration; } + // ansi codes addes extra chars that needs to be taken in consideration. + if (last_line && (memcmp(crc, "\x20\x20\x20\x20", 4) != 0) && g_session.supports_colors && markCRCBytes) { + str_padder = 85; + } + if (hdr->isResponse) { // tag row if (use_us) { PrintAndLogEx(NORMAL, " %10.1f | %10.1f | Tag |%-*s | %s| %s", (float)time1 / 13.56, (float)time2 / 13.56, - 72 + crc_format_string_offset, + str_padder, line[j], - (j == num_lines - 1) ? crc : " ", - (j == num_lines - 1) ? explanation : "" + (last_line) ? crc : " ", + (last_line) ? explanation : "" ); } else { PrintAndLogEx(NORMAL, " %10u | %10u | Tag |%-*s | %s| %s", time1, time2, - 72 + crc_format_string_offset, + str_padder, line[j], - (j == num_lines - 1) ? crc : " ", - (j == num_lines - 1) ? explanation : "" + (last_line) ? crc : " ", + (last_line) ? explanation : "" ); } } else { @@ -830,56 +876,86 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr _YELLOW_(" %10.1f") " | " _YELLOW_("%10.1f") " | " _YELLOW_("Rdr") " |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), (float)time1 / 13.56, (float)time2 / 13.56, - 72 + crc_format_string_offset, + str_padder, line[j], - (j == num_lines - 1) ? crc : " ", - (j == num_lines - 1) ? explanation : "" + (last_line) ? crc : " ", + (last_line) ? explanation : "" ); } else { PrintAndLogEx(NORMAL, _YELLOW_(" %10u") " | " _YELLOW_("%10u") " | " _YELLOW_("Rdr") " |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), time1, time2, - 72 + crc_format_string_offset, + str_padder, line[j], - (j == num_lines - 1) ? crc : " ", - (j == num_lines - 1) ? explanation : "" + (last_line) ? crc : " ", + (last_line) ? explanation : "" ); } - } } else { + + + if (last_line && (memcmp(crc, "\x20\x20\x20\x20", 4) != 0) && g_session.supports_colors && markCRCBytes) { + str_padder = 85; + // odd case of multiline, and last single byte on empty row has been colorised... + if (strlen(line[j]) < 14) { + str_padder = 81; + } + } + if (hdr->isResponse) { PrintAndLogEx(NORMAL, " | | |%-*s | %s| %s", - 72 + crc_format_string_offset, + str_padder, line[j], - (j == num_lines - 1) ? crc : " ", - (j == num_lines - 1) ? explanation : "" + last_line ? crc : " ", + last_line ? explanation : "" ); } else { PrintAndLogEx(NORMAL, " | | |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), - 72 + crc_format_string_offset, + str_padder, line[j], - (j == num_lines - 1) ? crc : " ", - (j == num_lines - 1) ? explanation : "" + last_line ? crc : " ", + last_line ? explanation : "" ); } + } } - if (protocol == PROTO_MIFARE) { + if (protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS) { uint8_t mfData[32] = {0}; size_t mfDataLen = 0; if (DecodeMifareData(frame, data_len, parityBytes, hdr->isResponse, mfData, &mfDataLen, mfDicKeys, mfDicKeysCount)) { memset(explanation, 0x00, sizeof(explanation)); - annotateIso14443a(explanation, sizeof(explanation), mfData, mfDataLen, hdr->isResponse); + + if (protocol == PROTO_MFPLUS) { + annotateMfPlus(explanation, sizeof(explanation), mfData, mfDataLen); + } else { + annotateIso14443a(explanation, sizeof(explanation), mfData, mfDataLen, hdr->isResponse); + } uint8_t crcc = iso14443A_CRC_check(hdr->isResponse, mfData, mfDataLen); - PrintAndLogEx(NORMAL, " | | * |%-*s | %-4s| %s", - 72 + crc_format_string_offset, - sprint_hex_inrow_spaces(mfData, mfDataLen, 2), - (crcc == 0 ? _RED_(" !! ") : (crcc == 1 ? _GREEN_(" ok ") : " ")), - explanation); + + // iceman: colorise crc bytes here will need a refactor of code from above. + for (int j = 0; j < mfDataLen; j += TRACE_MAX_HEX_BYTES) { + + int plen = MIN((mfDataLen - j), TRACE_MAX_HEX_BYTES); + + if (hdr->isResponse) { + PrintAndLogEx(NORMAL, " | | * |%-*s | %-4s| %s", + str_padder, + sprint_hex_inrow_spaces(mfData + j, plen, 2), + (crcc == 0 ? _RED_(" !! ") : (crcc == 1 ? _GREEN_(" ok ") : " ")), + explanation); + } else { + PrintAndLogEx(NORMAL, " | | * |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), + str_padder, + sprint_hex_inrow_spaces(mfData + j, plen, 2), + (crcc == 0 ? _RED_(" !! ") : (crcc == 1 ? _GREEN_(" ok ") : " ")), + explanation); + } + } } } @@ -938,15 +1014,15 @@ static int download_trace(void) { PrintAndLogEx(INFO, "downloading tracelog data from device"); // Query for the size of the trace, downloading PM3_CMD_DATA_SIZE - PacketResponseNG response; - if (!GetFromDevice(BIG_BUF, gs_trace, PM3_CMD_DATA_SIZE, 0, NULL, 0, &response, 4000, true)) { + PacketResponseNG resp; + if (!GetFromDevice(BIG_BUF, gs_trace, PM3_CMD_DATA_SIZE, 0, NULL, 0, &resp, 4000, true)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); free(gs_trace); gs_trace = NULL; return PM3_ETIMEOUT; } - gs_traceLen = response.oldarg[2]; + gs_traceLen = resp.oldarg[2]; // if tracelog buffer was larger and we need to download more. if (gs_traceLen > PM3_CMD_DATA_SIZE) { @@ -1045,8 +1121,9 @@ static int CmdTraceLoad(const char *Cmd) { CLIParserFree(ctx); if (gs_trace) { - free(gs_trace); + free(gs_trace); // maybe better to not clobber this until we have successful load? gs_trace = NULL; + gs_traceLen = 0; } size_t len = 0; @@ -1128,7 +1205,7 @@ int CmdTraceListAlias(const char *Cmd, const char *alias, const char *protocol) CLIParserFree(ctx); char args[128] = {0}; - snprintf(args, sizeof(args), "-c -t %s ", protocol); + snprintf(args, sizeof(args), "-t %s ", protocol); strncat(args, Cmd, sizeof(args) - strlen(args) - 1); return CmdTraceList(args); } @@ -1157,6 +1234,7 @@ int CmdTraceList(const char *Cmd) { "trace list -t seos -> interpret as " _YELLOW_("SEOS") "\n" "trace list -t thinfilm -> interpret as " _YELLOW_("Thinfilm") "\n" "trace list -t topaz -> interpret as " _YELLOW_("Topaz") "\n" + "trace list -t mfp -> interpret as " _YELLOW_("MIFARE Plus") "\n" "\n" "trace list -t mf -f mfc_default_keys.dic -> use default dictionary file\n" "trace list -t 14a --frame -> show frame delay times\n" @@ -1223,6 +1301,7 @@ int CmdTraceList(const char *Cmd) { else if (strcmp(type, "seos") == 0) protocol = SEOS; else if (strcmp(type, "thinfilm") == 0) protocol = THINFILM; else if (strcmp(type, "topaz") == 0) protocol = TOPAZ; + else if (strcmp(type, "mfp") == 0) protocol = PROTO_MFPLUS; else if (strcmp(type, "") == 0) protocol = -1; else { PrintAndLogEx(FAILED, "Unknown protocol \"%s\"", type); @@ -1231,7 +1310,7 @@ int CmdTraceList(const char *Cmd) { if (use_buffer == false) { download_trace(); - } else if (gs_traceLen == 0) { + } else if (gs_traceLen == 0 || gs_trace == NULL) { PrintAndLogEx(FAILED, "You requested a trace list in offline mode but there is no trace."); PrintAndLogEx(FAILED, "Consider using " _YELLOW_("`trace load`") " or removing parameter " _YELLOW_("`-1`")); return PM3_EINVARG; @@ -1261,7 +1340,7 @@ int CmdTraceList(const char *Cmd) { PrintAndLogEx(INFO, _YELLOW_("start") " = start of start frame " _YELLOW_("end") " = end of frame. " _YELLOW_("src") " = source of transfer"); } - if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == MFDES || protocol == TOPAZ || protocol == LTO) { + if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == MFDES || protocol == PROTO_MFPLUS || protocol == TOPAZ || protocol == LTO) { if (use_us) PrintAndLogEx(INFO, _YELLOW_("ISO14443A") " - all times are in microseconds"); else @@ -1311,7 +1390,7 @@ int CmdTraceList(const char *Cmd) { uint32_t dicKeysCount = 0; bool dictionaryLoad = false; - if (protocol == PROTO_MIFARE) { + if (protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS) { if (diclen > 0) { uint8_t *keyBlock = NULL; int res = loadFileDICTIONARY_safe(dictionary, (void **) &keyBlock, 6, &dicKeysCount); @@ -1344,7 +1423,7 @@ int CmdTraceList(const char *Cmd) { PrintAndLogEx(NORMAL, "------------+------------+-----+-------------------------------------------------------------------------+-----+--------------------"); // clean authentication data used with the mifare classic decrypt fct - if (protocol == ISO_14443A || protocol == PROTO_MIFARE) + if (protocol == ISO_14443A || protocol == PROTO_MIFARE || protocol == PROTO_MFPLUS) ClearAuthData(); uint32_t previous_EOT = 0; diff --git a/client/src/cmdtrace.h b/client/src/cmdtrace.h index 6f2967d44..2be583a06 100644 --- a/client/src/cmdtrace.h +++ b/client/src/cmdtrace.h @@ -24,5 +24,6 @@ int CmdTrace(const char *Cmd); int CmdTraceList(const char *Cmd); int CmdTraceListAlias(const char *Cmd, const char *alias, const char *protocol); +bool ImportTraceBuffer(uint8_t *trace_src, uint16_t trace_len); #endif diff --git a/client/src/cmdusart.c b/client/src/cmdusart.c index d67ae9bf1..c5fee5599 100644 --- a/client/src/cmdusart.c +++ b/client/src/cmdusart.c @@ -68,15 +68,20 @@ static int usart_txrx(uint8_t *srcdata, size_t srclen, uint8_t *dstdata, size_t struct payload_header header; uint8_t data[PM3_CMD_DATA_SIZE - sizeof(uint32_t)]; } PACKED payload; + payload.header.waittime = waittime; - if (srclen >= sizeof(payload.data)) + + if (srclen >= sizeof(payload.data)) { return PM3_EOVFLOW; + } + memcpy(payload.data, srcdata, srclen); SendCommandNG(CMD_USART_TXRX, (uint8_t *)&payload, srclen + sizeof(payload.header)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_USART_TXRX, &resp, waittime + 500)) { + if (WaitForResponseTimeout(CMD_USART_TXRX, &resp, waittime + 500) == false) { return PM3_ETIMEOUT; } + if (resp.status == PM3_SUCCESS) { *dstlen = resp.length; memcpy(dstdata, resp.data.asBytes, resp.length); @@ -143,6 +148,10 @@ static int CmdUsartConfig(const char *Cmd) { return set_usart_config(baudrate, parity); } +// module command not universal so specific commands needed if anyone DIY'd their own Blueshark. +bool BT_EXTENSION_HC04 = false; +bool BT_EXTENSION_HC05_BLUESHARK = false; + static int usart_bt_testcomm(uint32_t baudrate, uint8_t parity) { int ret = set_usart_config(baudrate, parity); if (ret != PM3_SUCCESS) @@ -154,11 +163,27 @@ static int usart_bt_testcomm(uint32_t baudrate, uint8_t parity) { PrintAndLogEx(SUCCESS, "TX (%3zu):%.*s at %u 8%c1", strlen(string), (int)strlen(string), string, baudrate, parity); - ret = usart_txrx((uint8_t *)string, strlen(string), data, &len, 1000); // such large timeout needed + // 1000, such large timeout needed + ret = usart_txrx((uint8_t *)string, strlen(string), data, &len, 1000); if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "RX (%3zu):%.*s", len, (int)len, data); - if (strcmp((char *)data, "hc01.comV2.0") == 0) { + if (str_startswith((char *)data, "hc01.comV2.0") || + str_startswith((char *)data, "www.hc01.com") || + str_startswith((char *)data, "BT SPP V4.0")) { + PrintAndLogEx(SUCCESS, "Add-on " _GREEN_("found!")); + + // if it fully match HC-04's attribute + if (str_startswith((char *)data, "www.hc01.com V2.5, 2022-04-26")) { + BT_EXTENSION_HC04 = true; + PrintAndLogEx(INFO, "Bluetooth module identified as HC-04."); + } + + // if it fully match Blueshark HC-05's attribute + if (str_startswith((char *)data, "hc01.comV2.0")) { + BT_EXTENSION_HC05_BLUESHARK = true; + PrintAndLogEx(INFO, "Bluetooth module identified as Blueshark HC-05."); + } return PM3_SUCCESS; } } @@ -230,7 +255,7 @@ static int CmdUsartBtFactory(const char *Cmd) { } if (!found) { - PrintAndLogEx(FAILED, "Sorry, add-on not found. Abort."); + PrintAndLogEx(FAILED, "Sorry, add-on not found. Abort. If you DIY'd your own, please report your model and manual to us."); return PM3_ESOFT; } @@ -240,13 +265,18 @@ static int CmdUsartBtFactory(const char *Cmd) { size_t len = 0; memset(data, 0, sizeof(data)); - string = "AT+NAMEPM3_RDV4.0"; + if (BT_EXTENSION_HC04 == true) { + string = "AT+NAME=PM3_RDV4.0"; + } else { + string = "AT+NAMEPM3_RDV4.0"; + } + PrintAndLogEx(SUCCESS, "TX (%3zu):%.*s", strlen(string), (int)strlen(string), string); int ret = usart_txrx((uint8_t *)string, strlen(string), data, &len, 1000); if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "RX (%3zu):%.*s", len, (int)len, data); - if (strcmp((char *)data, "OKsetname") == 0) { + if (strstr((char *)data, "OK")) { PrintAndLogEx(SUCCESS, "Name set to " _GREEN_("PM3_RDV4.0")); } else { PrintAndLogEx(WARNING, "Unexpected response to AT+NAME: " _YELLOW_("%.*s"), (int)len, data); @@ -256,6 +286,8 @@ static int CmdUsartBtFactory(const char *Cmd) { return PM3_ESOFT; } + msleep(500); + memset(data, 0, sizeof(data)); len = 0; string = "AT+ROLE=S"; @@ -264,7 +296,7 @@ static int CmdUsartBtFactory(const char *Cmd) { ret = usart_txrx((uint8_t *)string, strlen(string), data, &len, 1000); if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "RX (%3zu):%.*s", len, (int)len, data); - if (strcmp((char *)data, "OK+ROLE:S") == 0) { + if (strstr((char *)data, "OK")) { PrintAndLogEx(SUCCESS, "Role set to " _GREEN_("Slave")); } else { PrintAndLogEx(WARNING, "Unexpected response to AT+ROLE=S: " _YELLOW_("%.*s"), (int)len, data); @@ -274,15 +306,23 @@ static int CmdUsartBtFactory(const char *Cmd) { return PM3_ESOFT; } + msleep(500); + memset(data, 0, sizeof(data)); len = 0; - string = "AT+PIN1234"; + + if (BT_EXTENSION_HC04 == true) { + string = "AT+PIN=1234"; + } else { + string = "AT+PIN1234"; + } + PrintAndLogEx(SUCCESS, "TX (%3zu):%.*s", strlen(string), (int)strlen(string), string); ret = usart_txrx((uint8_t *)string, strlen(string), data, &len, 1000); if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "RX (%3zu):%.*s", len, (int)len, data); - if (strcmp((char *)data, "OKsetPIN") == 0) { + if (strstr((char *)data, "OK")) { PrintAndLogEx(SUCCESS, "PIN set to " _GREEN_("1234")); } else { PrintAndLogEx(WARNING, "Unexpected response to AT+PIN: " _YELLOW_("%.*s"), (int)len, data); @@ -292,38 +332,61 @@ static int CmdUsartBtFactory(const char *Cmd) { return PM3_ESOFT; } - // parity must be changed before baudrate - if (parity != USART_PARITY) { - memset(data, 0, sizeof(data)); - len = 0; - string = "AT+PN"; - PrintAndLogEx(SUCCESS, "TX (%3zu):%.*s", strlen(string), (int)strlen(string), string); + msleep(500); - ret = usart_txrx((uint8_t *)string, strlen(string), data, &len, 1000); - if (ret == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "RX (%3zu):%.*s", len, (int)len, data); - if (strcmp((char *)data, "OK None") == 0) { - PrintAndLogEx(SUCCESS, "Parity set to " _GREEN_("None")); + if (BT_EXTENSION_HC04 != true) { + // parity must be changed before baudrate + if (parity != USART_PARITY) { + memset(data, 0, sizeof(data)); + len = 0; + string = "AT+PN"; + PrintAndLogEx(SUCCESS, "TX (%3zu):%.*s", strlen(string), (int)strlen(string), string); + + ret = usart_txrx((uint8_t *)string, strlen(string), data, &len, 1000); + if (ret == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "RX (%3zu):%.*s", len, (int)len, data); + if (strcmp((char *)data, "OK None") == 0) { + PrintAndLogEx(SUCCESS, "Parity set to " _GREEN_("None")); + } else { + PrintAndLogEx(WARNING, "Unexpected response to AT+P: " _YELLOW_("%.*s"), (int)len, data); + } } else { - PrintAndLogEx(WARNING, "Unexpected response to AT+P: " _YELLOW_("%.*s"), (int)len, data); + PrintAndLogEx(WARNING, "Lost contact with add-on, please try again"); + return PM3_ESOFT; } - } else { - PrintAndLogEx(WARNING, "Lost contact with add-on, please try again"); - return PM3_ESOFT; } - } - if (baudrate != USART_BAUD_RATE) { + if (baudrate != USART_BAUD_RATE) { + memset(data, 0, sizeof(data)); + len = 0; + string = BTADDON_BAUD_AT; + PrintAndLogEx(SUCCESS, "TX (%3zu):%.*s", strlen(string), (int)strlen(string), string); + + ret = usart_txrx((uint8_t *)string, strlen(string), data, &len, 1000); + if (ret == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "RX (%3zu):%.*s", len, (int)len, data); + if (strcmp((char *)data, "OK" BTADDON_BAUD_NUM) == 0) { + PrintAndLogEx(SUCCESS, "Baudrate set to " _GREEN_(BTADDON_BAUD_NUM)); + } else { + PrintAndLogEx(WARNING, "Unexpected response to AT+BAUD: " _YELLOW_("%.*s"), (int)len, data); + } + } else { + PrintAndLogEx(WARNING, "Lost contact with add-on, please try again"); + return PM3_ESOFT; + } + } + } else { + memset(data, 0, sizeof(data)); len = 0; - string = BTADDON_BAUD_AT; + string = "AT+BAUD=115200,N"; PrintAndLogEx(SUCCESS, "TX (%3zu):%.*s", strlen(string), (int)strlen(string), string); ret = usart_txrx((uint8_t *)string, strlen(string), data, &len, 1000); if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "RX (%3zu):%.*s", len, (int)len, data); - if (strcmp((char *)data, "OK" BTADDON_BAUD_NUM) == 0) { - PrintAndLogEx(SUCCESS, "Baudrate set to " _GREEN_(BTADDON_BAUD_NUM)); + if (strstr((char *)data, "OK") != NULL) { + PrintAndLogEx(SUCCESS, "Parity set to " _GREEN_("None") " and Baudrate set to " _GREEN_("115200")); } else { PrintAndLogEx(WARNING, "Unexpected response to AT+BAUD: " _YELLOW_("%.*s"), (int)len, data); } diff --git a/client/src/comms.c b/client/src/comms.c index a8ed8be4c..efd395917 100644 --- a/client/src/comms.c +++ b/client/src/comms.c @@ -29,9 +29,10 @@ #include "util.h" // g_pendingPrompt #include "util_posix.h" // msclock #include "util_darwin.h" // en/dis-ableNapp(); +#include "usart_defs.h" -//#define COMMS_DEBUG -//#define COMMS_DEBUG_RAW +// #define COMMS_DEBUG +// #define COMMS_DEBUG_RAW // Serial port that we are communicating with the PM3 on. static serial_port sp = NULL; @@ -40,7 +41,15 @@ communication_arg_t g_conn; capabilities_t g_pm3_capabilities; static pthread_t communication_thread; +static pthread_t reconnect_thread; + +static bool reconnect_ok = false; + static bool comm_thread_dead = false; +static bool comm_raw_mode = false; +static uint8_t *comm_raw_data = NULL; +static size_t comm_raw_len = 0; +static size_t comm_raw_pos = 0; // Transmit buffer. static PacketCommandOLD txBuffer; @@ -97,7 +106,7 @@ void SendCommandOLD(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, v #endif if (!g_session.pm3_present) { - PrintAndLogEx(WARNING, "Sending bytes to Proxmark3 failed." _YELLOW_("offline")); + PrintAndLogEx(WARNING, "Sending bytes to Proxmark3 failed ( " _RED_("offline") " )"); return; } @@ -156,7 +165,7 @@ static void SendCommandNG_internal(uint16_t cmd, uint8_t *data, size_t len, bool memcpy(&txBufferNG.data, data, len); if ((g_conn.send_via_fpc_usart && g_conn.send_with_crc_on_fpc) || ((!g_conn.send_via_fpc_usart) && g_conn.send_with_crc_on_usb)) { - uint8_t first, second; + uint8_t first = 0, second = 0; compute_crc(CRC_14443_A, (uint8_t *)&txBufferNG, sizeof(PacketCommandNGPreamble) + len, &first, &second); tx_post->crc = (first << 8) + second; } else { @@ -330,6 +339,54 @@ static void PacketResponseReceived(PacketResponseNG *packet) { } } +// The reconnect device thread. +// When communication thread is dead, start up and try to start it again +void *uart_reconnect(void *targ) { + + communication_arg_t *connection = (communication_arg_t *)targ; + +#if defined(__MACH__) && defined(__APPLE__) + disableAppNap("Proxmark3 polling UART"); +#endif + + uint32_t speed = USART_BAUD_RATE; + if (connection->uart_speed) { + speed = connection->uart_speed; + } + + while (1) { + // throttle + msleep(200); + if (OpenProxmarkSilent(&g_session.current_device, connection->serial_port_name, speed) == false) { + continue; + } + + if (g_session.pm3_present && (TestProxmark(g_session.current_device) != PM3_SUCCESS)) { + CloseProxmark(g_session.current_device); + } else { + break; + } + } + + +#if defined(__MACH__) && defined(__APPLE__) + enableAppNap(); +#endif + + __atomic_test_and_set(&reconnect_ok, __ATOMIC_SEQ_CST); + + pthread_exit(NULL); + return NULL; +} + +void StartReconnectProxmark(void) { + pthread_create(&reconnect_thread, NULL, &uart_reconnect, &g_conn); +} + +bool IsReconnectedOk(void) { + bool ret = __atomic_load_n(&reconnect_ok, __ATOMIC_SEQ_CST); + return ret; +} // The communications thread. // signals to main thread when a response is ready to process. @@ -346,6 +403,8 @@ __attribute__((force_align_arg_pointer)) bool commfailed = false; PacketResponseNG rx; PacketResponseNGRaw rx_raw; + // Stash the last state of is_receiving_raw, to detect if state changed + bool is_receiving_raw_last = false; #if defined(__MACH__) && defined(__APPLE__) disableAppNap("Proxmark3 polling UART"); @@ -368,133 +427,175 @@ __attribute__((force_align_arg_pointer)) break; } - res = uart_receive(sp, (uint8_t *)&rx_raw.pre, sizeof(PacketResponseNGPreamble), &rxlen); - if ((res == PM3_SUCCESS) && (rxlen == sizeof(PacketResponseNGPreamble))) { - rx.magic = rx_raw.pre.magic; - uint16_t length = rx_raw.pre.length; - rx.ng = rx_raw.pre.ng; - rx.status = rx_raw.pre.status; - rx.cmd = rx_raw.pre.cmd; - if (rx.magic == RESPONSENG_PREAMBLE_MAGIC) { // New style NG reply - if (length > PM3_CMD_DATA_SIZE) { - PrintAndLogEx(WARNING, "Received packet frame with incompatible length: 0x%04x", length); + bool is_receiving_raw = __atomic_load_n(&comm_raw_mode, __ATOMIC_SEQ_CST); + + if (is_receiving_raw) { + uint8_t *bufferData = __atomic_load_n(&comm_raw_data, __ATOMIC_SEQ_CST); // read only + size_t bufferLen = __atomic_load_n(&comm_raw_len, __ATOMIC_SEQ_CST); // read only + size_t bufferPos = __atomic_load_n(&comm_raw_pos, __ATOMIC_SEQ_CST); // read and write + if (bufferPos < bufferLen) { + size_t rxMaxLen = bufferLen - bufferPos; + + rxMaxLen = MIN(COMM_RAW_RECEIVE_LEN, rxMaxLen); + + res = uart_receive(sp, bufferData + bufferPos, rxMaxLen, &rxlen); + if (res == PM3_SUCCESS) { + uint64_t clk = msclock(); + __atomic_store_n(&timeout_start_time, clk, __ATOMIC_SEQ_CST); + __atomic_store_n(&comm_raw_pos, bufferPos + rxlen, __ATOMIC_SEQ_CST); + } else if (res != PM3_ENODATA) { + PrintAndLogEx(WARNING, "Error when reading raw data: %zu/%zu, %d", bufferPos, bufferLen, res); error = true; + if (res == PM3_ENOTTY) { + commfailed = true; + } } - if ((!error) && (length > 0)) { // Get the variable length payload + } else { + // Ignore data when bufferPos >= bufferLen and is_receiving_raw has not been set to false + uint8_t dummyData[64]; + uint32_t dummyLen; + uart_receive(sp, dummyData, sizeof(dummyData), &dummyLen); + } + } else { + if (is_receiving_raw_last) { + // is_receiving_raw changed from true to false - res = uart_receive(sp, (uint8_t *)&rx_raw.data, length, &rxlen); - if ((res != PM3_SUCCESS) || (rxlen != length)) { - PrintAndLogEx(WARNING, "Received packet frame with variable part too short? %d/%d", rxlen, length); + // Set the buffer as undefined + // comm_raw_data == NULL is used in SetCommunicationReceiveMode() + __atomic_store_n(&comm_raw_data, NULL, __ATOMIC_SEQ_CST); + } + res = uart_receive(sp, (uint8_t *)&rx_raw.pre, sizeof(PacketResponseNGPreamble), &rxlen); + + if ((res == PM3_SUCCESS) && (rxlen == sizeof(PacketResponseNGPreamble))) { + rx.magic = rx_raw.pre.magic; + uint16_t length = rx_raw.pre.length; + rx.ng = rx_raw.pre.ng; + rx.status = rx_raw.pre.status; + rx.cmd = rx_raw.pre.cmd; + if (rx.magic == RESPONSENG_PREAMBLE_MAGIC) { // New style NG reply + if (length > PM3_CMD_DATA_SIZE) { + PrintAndLogEx(WARNING, "Received packet frame with incompatible length: 0x%04x", length); error = true; - } else { + } - if (rx.ng) { // Received a valid NG frame - memcpy(&rx.data, &rx_raw.data, length); - rx.length = length; - if ((rx.cmd == g_conn.last_command) && (rx.status == PM3_SUCCESS)) { - ACK_received = true; - } + if ((!error) && (length > 0)) { // Get the variable length payload + + res = uart_receive(sp, (uint8_t *)&rx_raw.data, length, &rxlen); + if ((res != PM3_SUCCESS) || (rxlen != length)) { + PrintAndLogEx(WARNING, "Received packet frame with variable part too short? %d/%d", rxlen, length); + error = true; } else { - uint64_t arg[3]; - if (length < sizeof(arg)) { - PrintAndLogEx(WARNING, "Received MIX packet frame with incompatible length: 0x%04x", length); - error = true; - } - if (!error) { // Received a valid MIX frame - memcpy(arg, &rx_raw.data, sizeof(arg)); - rx.oldarg[0] = arg[0]; - rx.oldarg[1] = arg[1]; - rx.oldarg[2] = arg[2]; - memcpy(&rx.data, ((uint8_t *)&rx_raw.data) + sizeof(arg), length - sizeof(arg)); - rx.length = length - sizeof(arg); - if (rx.cmd == CMD_ACK) { + + if (rx.ng) { // Received a valid NG frame + memcpy(&rx.data, &rx_raw.data, length); + rx.length = length; + if ((rx.cmd == g_conn.last_command) && (rx.status == PM3_SUCCESS)) { ACK_received = true; } + } else { + uint64_t arg[3]; + if (length < sizeof(arg)) { + PrintAndLogEx(WARNING, "Received MIX packet frame with incompatible length: 0x%04x", length); + error = true; + } + if (!error) { // Received a valid MIX frame + memcpy(arg, &rx_raw.data, sizeof(arg)); + rx.oldarg[0] = arg[0]; + rx.oldarg[1] = arg[1]; + rx.oldarg[2] = arg[2]; + memcpy(&rx.data, ((uint8_t *)&rx_raw.data) + sizeof(arg), length - sizeof(arg)); + rx.length = length - sizeof(arg); + if (rx.cmd == CMD_ACK) { + ACK_received = true; + } + } } } - } - } else if ((!error) && (length == 0)) { // we received an empty frame - if (rx.ng) - rx.length = 0; // set received length to 0 - else { // old frames can't be empty - PrintAndLogEx(WARNING, "Received empty MIX packet frame (length: 0x00)"); - - error = true; - } - } - if (!error) { // Get the postamble - res = uart_receive(sp, (uint8_t *)&rx_raw.foopost, sizeof(PacketResponseNGPostamble), &rxlen); - if ((res != PM3_SUCCESS) || (rxlen != sizeof(PacketResponseNGPostamble))) { - PrintAndLogEx(WARNING, "Received packet frame without postamble"); - error = true; - } - } - if (!error) { // Check CRC, accept MAGIC as placeholder - rx.crc = rx_raw.foopost.crc; - if (rx.crc != RESPONSENG_POSTAMBLE_MAGIC) { - uint8_t first, second; - compute_crc(CRC_14443_A, (uint8_t *)&rx_raw, sizeof(PacketResponseNGPreamble) + length, &first, &second); - if ((first << 8) + second != rx.crc) { - PrintAndLogEx(WARNING, "Received packet frame with invalid CRC %02X%02X <> %04X", first, second, rx.crc); + } else if ((!error) && (length == 0)) { // we received an empty frame + if (rx.ng) + rx.length = 0; // set received length to 0 + else { // old frames can't be empty + PrintAndLogEx(WARNING, "Received empty MIX packet frame (length: 0x00)"); error = true; } } - } - if (!error) { // Received a valid OLD frame -#ifdef COMMS_DEBUG - PrintAndLogEx(NORMAL, "Receiving %s:", rx.ng ? "NG" : "MIX"); -#endif -#ifdef COMMS_DEBUG_RAW - print_hex_break((uint8_t *)&rx_raw.pre, sizeof(PacketResponseNGPreamble), 32); - print_hex_break((uint8_t *)&rx_raw.data, rx_raw.pre.length, 32); - print_hex_break((uint8_t *)&rx_raw.foopost, sizeof(PacketResponseNGPostamble), 32); -#endif - PacketResponseReceived(&rx); - } - } else { // Old style reply - PacketResponseOLD rx_old; - memcpy(&rx_old, &rx_raw.pre, sizeof(PacketResponseNGPreamble)); - res = uart_receive(sp, ((uint8_t *)&rx_old) + sizeof(PacketResponseNGPreamble), sizeof(PacketResponseOLD) - sizeof(PacketResponseNGPreamble), &rxlen); - if ((res != PM3_SUCCESS) || (rxlen != sizeof(PacketResponseOLD) - sizeof(PacketResponseNGPreamble))) { - PrintAndLogEx(WARNING, "Received packet OLD frame with payload too short? %d/%zu", rxlen, sizeof(PacketResponseOLD) - sizeof(PacketResponseNGPreamble)); - error = true; - } - if (!error) { + if (!error) { // Get the postamble + res = uart_receive(sp, (uint8_t *)&rx_raw.foopost, sizeof(PacketResponseNGPostamble), &rxlen); + if ((res != PM3_SUCCESS) || (rxlen != sizeof(PacketResponseNGPostamble))) { + PrintAndLogEx(WARNING, "Received packet frame without postamble"); + error = true; + } + } + + if (!error) { // Check CRC, accept MAGIC as placeholder + rx.crc = rx_raw.foopost.crc; + if (rx.crc != RESPONSENG_POSTAMBLE_MAGIC) { + uint8_t first, second; + compute_crc(CRC_14443_A, (uint8_t *)&rx_raw, sizeof(PacketResponseNGPreamble) + length, &first, &second); + if ((first << 8) + second != rx.crc) { + PrintAndLogEx(WARNING, "Received packet frame with invalid CRC %02X%02X <> %04X", first, second, rx.crc); + error = true; + } + } + } + if (!error) { // Received a valid OLD frame #ifdef COMMS_DEBUG - PrintAndLogEx(NORMAL, "Receiving OLD:"); + PrintAndLogEx(NORMAL, "Receiving %s:", rx.ng ? "NG" : "MIX"); #endif #ifdef COMMS_DEBUG_RAW - print_hex_break((uint8_t *)&rx_old.cmd, sizeof(rx_old.cmd), 32); - print_hex_break((uint8_t *)&rx_old.arg, sizeof(rx_old.arg), 32); - print_hex_break((uint8_t *)&rx_old.d, sizeof(rx_old.d), 32); + print_hex_break((uint8_t *)&rx_raw.pre, sizeof(PacketResponseNGPreamble), 32); + print_hex_break((uint8_t *)&rx_raw.data, rx_raw.pre.length, 32); + print_hex_break((uint8_t *)&rx_raw.foopost, sizeof(PacketResponseNGPostamble), 32); #endif - rx.ng = false; - rx.magic = 0; - rx.status = 0; - rx.crc = 0; - rx.cmd = rx_old.cmd; - rx.oldarg[0] = rx_old.arg[0]; - rx.oldarg[1] = rx_old.arg[1]; - rx.oldarg[2] = rx_old.arg[2]; - rx.length = PM3_CMD_DATA_SIZE; - memcpy(&rx.data, &rx_old.d, rx.length); - PacketResponseReceived(&rx); - if (rx.cmd == CMD_ACK) { - ACK_received = true; + PacketResponseReceived(&rx); + } + } else { // Old style reply + PacketResponseOLD rx_old; + memcpy(&rx_old, &rx_raw.pre, sizeof(PacketResponseNGPreamble)); + + res = uart_receive(sp, ((uint8_t *)&rx_old) + sizeof(PacketResponseNGPreamble), sizeof(PacketResponseOLD) - sizeof(PacketResponseNGPreamble), &rxlen); + if ((res != PM3_SUCCESS) || (rxlen != sizeof(PacketResponseOLD) - sizeof(PacketResponseNGPreamble))) { + PrintAndLogEx(WARNING, "Received packet OLD frame with payload too short? %d/%zu", rxlen, sizeof(PacketResponseOLD) - sizeof(PacketResponseNGPreamble)); + error = true; + } + if (!error) { +#ifdef COMMS_DEBUG + PrintAndLogEx(NORMAL, "Receiving OLD:"); +#endif +#ifdef COMMS_DEBUG_RAW + print_hex_break((uint8_t *)&rx_old.cmd, sizeof(rx_old.cmd), 32); + print_hex_break((uint8_t *)&rx_old.arg, sizeof(rx_old.arg), 32); + print_hex_break((uint8_t *)&rx_old.d, sizeof(rx_old.d), 32); +#endif + rx.ng = false; + rx.magic = 0; + rx.status = 0; + rx.crc = 0; + rx.cmd = rx_old.cmd; + rx.oldarg[0] = rx_old.arg[0]; + rx.oldarg[1] = rx_old.arg[1]; + rx.oldarg[2] = rx_old.arg[2]; + rx.length = PM3_CMD_DATA_SIZE; + memcpy(&rx.data, &rx_old.d, rx.length); + PacketResponseReceived(&rx); + if (rx.cmd == CMD_ACK) { + ACK_received = true; + } } } - } - } else { - if (rxlen > 0) { - PrintAndLogEx(WARNING, "Received packet frame preamble too short: %d/%zu", rxlen, sizeof(PacketResponseNGPreamble)); - error = true; - } - if (res == PM3_ENOTTY) { - commfailed = true; + } else { + if (rxlen > 0) { + PrintAndLogEx(WARNING, "Received packet frame preamble too short: %d/%zu", rxlen, sizeof(PacketResponseNGPreamble)); + error = true; + } + if (res == PM3_ENOTTY) { + commfailed = true; + } } } + is_receiving_raw_last = is_receiving_raw; // TODO if error, shall we resync ? pthread_mutex_lock(&txBufferMutex); @@ -557,18 +658,100 @@ bool IsCommunicationThreadDead(void) { return ret; } + + +// To start raw receive mode: +// 1. Call SetCommunicationRawReceiveBuffer(...) +// 2. Call SetCommunicationReceiveMode(true) +// +// To stop raw receive mode: +// Call SetCommunicationReceiveMode(false) +// +// Note: +// 1. The receiving thread won't accept any normal packets after calling +// SetCommunicationReceiveMode(true). You need to call +// SetCommunicationReceiveMode(false) to stop the raw receiving process. +// 2. If the received size >= len used in SetCommunicationRawReceiveBuffer(), +// The receiving thread will ignore the incoming data to prevent overflow. +// 3. Normally you only need WaitForRawDataTimeout() rather than the +// low level functions like SetCommunicationReceiveMode(), +// SetCommunicationRawReceiveBuffer() and GetCommunicationRawReceiveNum() + +bool SetCommunicationReceiveMode(bool isRawMode) { + if (isRawMode) { + uint8_t *buffer = __atomic_load_n(&comm_raw_data, __ATOMIC_SEQ_CST); + if (buffer == NULL) { + PrintAndLogEx(ERR, "Buffer for raw data is not set"); + return false; + } + } + __atomic_store_n(&comm_raw_mode, isRawMode, __ATOMIC_SEQ_CST); + return true; +} + +void SetCommunicationRawReceiveBuffer(uint8_t *buffer, size_t len) { + __atomic_store_n(&comm_raw_data, buffer, __ATOMIC_SEQ_CST); + __atomic_store_n(&comm_raw_len, len, __ATOMIC_SEQ_CST); + __atomic_store_n(&comm_raw_pos, 0, __ATOMIC_SEQ_CST); +} + +size_t GetCommunicationRawReceiveNum(void) { + return __atomic_load_n(&comm_raw_pos, __ATOMIC_SEQ_CST); +} + +bool OpenProxmarkSilent(pm3_device_t **dev, const char *port, uint32_t speed) { + + sp = uart_open(port, speed, true); + + // check result of uart opening + if (sp == INVALID_SERIAL_PORT) { + sp = NULL; + return false; + } else if (sp == CLAIMED_SERIAL_PORT) { + sp = NULL; + return false; + } else { + // start the communication thread + if (port != g_conn.serial_port_name) { + uint16_t len = MIN(strlen(port), FILE_PATH_SIZE - 1); + memset(g_conn.serial_port_name, 0, FILE_PATH_SIZE); + memcpy(g_conn.serial_port_name, port, len); + } + g_conn.run = true; + g_conn.block_after_ACK = false; + // Flags to tell where to add CRC on sent replies + g_conn.send_with_crc_on_usb = false; + g_conn.send_with_crc_on_fpc = true; + // "Session" flag, to tell via which interface next msgs should be sent: USB or FPC USART + g_conn.send_via_fpc_usart = false; + + pthread_create(&communication_thread, NULL, &uart_communication, &g_conn); + __atomic_clear(&comm_thread_dead, __ATOMIC_SEQ_CST); + __atomic_clear(&reconnect_ok, __ATOMIC_SEQ_CST); + + g_session.pm3_present = true; // TODO support for multiple devices + + fflush(stdout); + if (*dev == NULL) { + *dev = calloc(sizeof(pm3_device_t), sizeof(uint8_t)); + } + (*dev)->g_conn = &g_conn; // TODO g_conn shouldn't be global + return true; + } +} + bool OpenProxmark(pm3_device_t **dev, const char *port, bool wait_for_port, int timeout, bool flash_mode, uint32_t speed) { - if (!wait_for_port) { - PrintAndLogEx(INFO, "Using UART port " _YELLOW_("%s"), port); - sp = uart_open(port, speed); + if (wait_for_port == false) { + PrintAndLogEx(SUCCESS, "Using UART port " _GREEN_("%s"), port); + sp = uart_open(port, speed, false); } else { PrintAndLogEx(SUCCESS, "Waiting for Proxmark3 to appear on " _YELLOW_("%s"), port); fflush(stdout); int openCount = 0; PrintAndLogEx(INPLACE, "% 3i", timeout); do { - sp = uart_open(port, speed); + sp = uart_open(port, speed, false); msleep(500); PrintAndLogEx(INPLACE, "% 3i", timeout - openCount - 1); @@ -618,11 +801,11 @@ bool OpenProxmark(pm3_device_t **dev, const char *port, bool wait_for_port, int // check if we can communicate with Pm3 int TestProxmark(pm3_device_t *dev) { - PacketResponseNG resp; uint16_t len = 32; uint8_t data[len]; - for (uint16_t i = 0; i < len; i++) + for (uint16_t i = 0; i < len; i++) { data[i] = i & 0xFF; + } __atomic_store_n(&last_packet_time, msclock(), __ATOMIC_SEQ_CST); clearCommandBuffer(); @@ -639,6 +822,7 @@ int TestProxmark(pm3_device_t *dev) { timeout = 1000; #endif + PacketResponseNG resp; if (WaitForResponseTimeoutW(CMD_PING, &resp, timeout, false) == 0) { return PM3_ETIMEOUT; } @@ -655,7 +839,7 @@ int TestProxmark(pm3_device_t *dev) { if ((resp.length != sizeof(g_pm3_capabilities)) || (resp.data.asBytes[0] != CAPABILITIES_VERSION)) { PrintAndLogEx(ERR, _RED_("Capabilities structure version sent by Proxmark3 is not the same as the one used by the client!")); - PrintAndLogEx(ERR, _RED_("Please flash the Proxmark with the same version as the client.")); + PrintAndLogEx(ERR, _RED_("Please flash the Proxmark3 with the same version as the client.")); return PM3_EDEVNOTSUPP; } @@ -663,15 +847,28 @@ int TestProxmark(pm3_device_t *dev) { g_conn.send_via_fpc_usart = g_pm3_capabilities.via_fpc; g_conn.uart_speed = g_pm3_capabilities.baudrate; - PrintAndLogEx(INFO, "Communicating with PM3 over %s%s%s", - g_conn.send_via_fpc_usart ? _YELLOW_("FPC UART") : _YELLOW_("USB-CDC"), - memcmp(g_conn.serial_port_name, "tcp:", 4) == 0 ? " over " _YELLOW_("TCP") : "", - memcmp(g_conn.serial_port_name, "bt:", 3) == 0 ? " over " _YELLOW_("BT") : ""); + bool is_tcp_conn = (g_conn.send_via_ip == PM3_TCPv4 || g_conn.send_via_ip == PM3_TCPv6); + bool is_bt_conn = (memcmp(g_conn.serial_port_name, "bt:", 3) == 0); + bool is_udp_conn = (g_conn.send_via_ip == PM3_UDPv4 || g_conn.send_via_ip == PM3_UDPv6); + PrintAndLogEx(SUCCESS, "Communicating with PM3 over %s%s%s%s", + (g_conn.send_via_fpc_usart) ? _GREEN_("FPC UART") : _GREEN_("USB-CDC"), + (is_tcp_conn) ? " over " _GREEN_("TCP") : "", + (is_bt_conn) ? " over " _GREEN_("BT") : "", + (is_udp_conn) ? " over " _GREEN_("UDP") : "" + ); if (g_conn.send_via_fpc_usart) { - PrintAndLogEx(INFO, "PM3 UART serial baudrate: " _YELLOW_("%u") "\n", g_conn.uart_speed); + PrintAndLogEx(SUCCESS, "PM3 UART serial baudrate: " _GREEN_("%u") "\n", g_conn.uart_speed); } else { - int res = uart_reconfigure_timeouts(UART_USB_CLIENT_RX_TIMEOUT_MS); + int res; + if (g_conn.send_via_local_ip) { + // (g_conn.send_via_local_ip == true) -> ((is_tcp_conn || is_udp_conn) == true) + res = uart_reconfigure_timeouts(is_tcp_conn ? UART_TCP_LOCAL_CLIENT_RX_TIMEOUT_MS : UART_UDP_LOCAL_CLIENT_RX_TIMEOUT_MS); + } else if (is_tcp_conn || is_udp_conn) { + res = uart_reconfigure_timeouts(UART_NET_CLIENT_RX_TIMEOUT_MS); + } else { + res = uart_reconfigure_timeouts(UART_USB_CLIENT_RX_TIMEOUT_MS); + } if (res != PM3_SUCCESS) { return res; } @@ -717,11 +914,85 @@ void CloseProxmark(pm3_device_t *dev) { // ~ = 12000000 / USART_BAUD_RATE // Let's take 2x (maybe we need more for BT link?) static size_t communication_delay(void) { - if (g_conn.send_via_fpc_usart) // needed also for Windows USB USART?? + // needed also for Windows USB USART?? + if (g_conn.send_via_fpc_usart) { return 2 * (12000000 / g_conn.uart_speed); + } return 0; } + +/** + * @brief Wait for receiving a specified amount of bytes + * + * @param buffer The receive buffer + * @param len The maximum receive byte size + * @param ms_timeout the maximum timeout + * @param show_process print how many bytes are received + * @return the number of received bytes + */ +size_t WaitForRawDataTimeout(uint8_t *buffer, size_t len, size_t ms_timeout, bool show_process) { + uint8_t print_counter = 0; + size_t last_pos = 0; + + // Add delay depending on the communication channel & speed + if (ms_timeout != (size_t) - 1) { + ms_timeout += communication_delay(); + } + __atomic_store_n(&timeout_start_time, msclock(), __ATOMIC_SEQ_CST); + + SetCommunicationRawReceiveBuffer(buffer, len); + SetCommunicationReceiveMode(true); + + size_t pos = 0; + while (pos < len) { + + if (kbd_enter_pressed()) { + // Send anything to stop the transfer + PrintAndLogEx(INFO, "Stopping"); + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + + // For ms_timeout == -1, pos < len might always be true + // so user need a spectial way to break this loop + if (ms_timeout == (size_t) - 1) { + break; + } + } + + pos = __atomic_load_n(&comm_raw_pos, __ATOMIC_SEQ_CST); + + // Check the timeout if pos is not updated + if (last_pos == pos) { + uint64_t tmp_clk = __atomic_load_n(&timeout_start_time, __ATOMIC_SEQ_CST); + // If ms_timeout == -1, the loop can only be breaked by pressing Enter or receiving enough data + if ((ms_timeout != (size_t) - 1) && (msclock() - tmp_clk > ms_timeout)) { + break; + } + } else { + // Print process when (print_counter % 64) == 0 + if (show_process && (print_counter & 0x3F) == 0) { + PrintAndLogEx(INFO, "[%zu/%zu]", pos, len); + } + } + + print_counter++; + last_pos = pos; + msleep(10); + } + if (pos == len && (ms_timeout != (size_t) - 1)) { + // If ms_timeout != -1, when the desired data is received, tell the arm side + // to stop the current process, and wait for some time to make sure the process + // has been stopped. + // If ms_timeout == -1, the user might not want to break the existing process + // on the arm side. + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + msleep(ms_timeout); + } + SetCommunicationReceiveMode(false); + pos = __atomic_load_n(&comm_raw_pos, __ATOMIC_SEQ_CST); + return pos; +} + /** * @brief Waits for a certain response type. This method waits for a maximum of * ms_timeout milliseconds for a specified response command. @@ -735,9 +1006,21 @@ static size_t communication_delay(void) { bool WaitForResponseTimeoutW(uint32_t cmd, PacketResponseNG *response, size_t ms_timeout, bool show_warning) { PacketResponseNG resp; + // init to ZERO + resp.cmd = 0, + resp.length = 0, + resp.magic = 0, + resp.status = 0, + resp.crc = 0, + resp.ng = false, + resp.oldarg[0] = 0; + resp.oldarg[1] = 0; + resp.oldarg[2] = 0; + memset(resp.data.asBytes, 0, PM3_CMD_DATA_SIZE); - if (response == NULL) + if (response == NULL) { response = &resp; + } // Add delay depending on the communication channel & speed if (ms_timeout != (size_t) - 1) @@ -766,12 +1049,11 @@ bool WaitForResponseTimeoutW(uint32_t cmd, PacketResponseNG *response, size_t ms if (msclock() - tmp_clk > 3000 && show_warning) { // 3 seconds elapsed (but this doesn't mean the timeout was exceeded) -// PrintAndLogEx(INFO, "Waiting for a response from the Proxmark3..."); PrintAndLogEx(INFO, "You can cancel this operation by pressing the pm3 button"); show_warning = false; } // just to avoid CPU busy loop: - msleep(10); + msleep(1); } return false; } @@ -802,11 +1084,26 @@ bool WaitForResponse(uint32_t cmd, PacketResponseNG *response) { bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint32_t start_index, uint8_t *data, uint32_t datalen, PacketResponseNG *response, size_t ms_timeout, bool show_warning) { if (dest == NULL) return false; - if (bytes == 0) return true; PacketResponseNG resp; - if (response == NULL) + if (response == NULL) { response = &resp; + } + + // init to ZERO + resp.cmd = 0, + resp.length = 0, + resp.magic = 0, + resp.status = 0, + resp.crc = 0, + resp.ng = false, + resp.oldarg[0] = 0; + resp.oldarg[1] = 0; + resp.oldarg[2] = 0; + memset(resp.data.asBytes, 0, PM3_CMD_DATA_SIZE); + + if (bytes == 0) return true; + // clear clearCommandBuffer(); diff --git a/client/src/comms.h b/client/src/comms.h index 0c1329cda..63b1af989 100644 --- a/client/src/comms.h +++ b/client/src/comms.h @@ -45,6 +45,8 @@ extern "C" { #define CMD_BUFFER_SIZE 100 #endif +#define COMM_RAW_RECEIVE_LEN (1024) + typedef enum { BIG_BUF, BIG_BUF_EML, @@ -54,6 +56,14 @@ typedef enum { FPGA_MEM, } DeviceMemType_t; +typedef enum { + PM3_TCPv4, + PM3_TCPv6, + PM3_UDPv4, + PM3_UDPv6, + PM3_NONE, +} CommunicationProtocol_t; + typedef struct { bool run; // If TRUE, continue running the uart_communication thread bool block_after_ACK; // if true, block after receiving an ACK package @@ -62,6 +72,10 @@ typedef struct { bool send_with_crc_on_fpc; // "Session" flag, to tell via which interface next msgs are sent: USB or FPC USART bool send_via_fpc_usart; + // to tell if we are using TCP/UDP/TCP(IPv6)/UDP(IPv6) + CommunicationProtocol_t send_via_ip; + // to tell if the target address is local address(127.0.0.1/localhost/::1) + bool send_via_local_ip; // To memorise baudrate uint32_t uart_speed; uint16_t last_command; @@ -75,6 +89,9 @@ typedef struct pm3_device { int script_embedded; } pm3_device_t; + +void *uart_reconnect(void *targ); + void *uart_receiver(void *targ); void SendCommandBL(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); void SendCommandOLD(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); @@ -83,11 +100,20 @@ void SendCommandMIX(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, v void clearCommandBuffer(void); #define FLASHMODE_SPEED 460800 + +bool IsReconnectedOk(void); bool IsCommunicationThreadDead(void); +bool SetCommunicationReceiveMode(bool isRawMode); +void SetCommunicationRawReceiveBuffer(uint8_t *buffer, size_t len); +size_t GetCommunicationRawReceiveNum(void); + +bool OpenProxmarkSilent(pm3_device_t **dev, const char *port, uint32_t speed); bool OpenProxmark(pm3_device_t **dev, const char *port, bool wait_for_port, int timeout, bool flash_mode, uint32_t speed); int TestProxmark(pm3_device_t *dev); void CloseProxmark(pm3_device_t *dev); +void StartReconnectProxmark(void); +size_t WaitForRawDataTimeout(uint8_t *buffer, size_t len, size_t ms_timeout, bool show_process); bool WaitForResponseTimeoutW(uint32_t cmd, PacketResponseNG *response, size_t ms_timeout, bool show_warning); bool WaitForResponseTimeout(uint32_t cmd, PacketResponseNG *response, size_t ms_timeout); bool WaitForResponse(uint32_t cmd, PacketResponseNG *response); @@ -99,5 +125,3 @@ bool GetFromDevice(DeviceMemType_t memtype, uint8_t *dest, uint32_t bytes, uint3 } #endif #endif - - diff --git a/client/src/crypto/asn1dump.c b/client/src/crypto/asn1dump.c index 5fcabe20a..32dc8f051 100644 --- a/client/src/crypto/asn1dump.c +++ b/client/src/crypto/asn1dump.c @@ -15,7 +15,7 @@ //----------------------------------------------------------------------------- // asn.1 dumping //----------------------------------------------------------------------------- -#define _POSIX_C_SOURCE 200809L // need for strnlen() + #include "asn1dump.h" #include "commonutil.h" // ARRAYLEN @@ -342,19 +342,19 @@ static void asn1_tag_dump_object_id(const struct tlv *tlv, const struct asn1_tag if (jsondesc) { PrintAndLogEx(NORMAL, " - %s" NOLF, jsondesc); } else { - const char *ppstr; + const char *ppstr = NULL; mbedtls_oid_get_attr_short_name(&asn1_buf, &ppstr); - if (ppstr && strnlen(ppstr, 1)) { + if (ppstr && str_nlen(ppstr, 1)) { PrintAndLogEx(NORMAL, " (%s)", ppstr); return; } mbedtls_oid_get_sig_alg_desc(&asn1_buf, &ppstr); - if (ppstr && strnlen(ppstr, 1)) { + if (ppstr && str_nlen(ppstr, 1)) { PrintAndLogEx(NORMAL, " (%s)", ppstr); return; } mbedtls_oid_get_extended_key_usage(&asn1_buf, &ppstr); - if (ppstr && strnlen(ppstr, 1)) { + if (ppstr && str_nlen(ppstr, 1)) { PrintAndLogEx(NORMAL, " (%s)", ppstr); return; } diff --git a/client/src/crypto/asn1utils.c b/client/src/crypto/asn1utils.c index 4618c5028..1fa839a7f 100644 --- a/client/src/crypto/asn1utils.c +++ b/client/src/crypto/asn1utils.c @@ -20,8 +20,9 @@ #include #include #include -#include // memcpy -#include "ui.h" // Print... +#include // memcpy +#include "ui.h" // Print... +#include "commonutil.h" // ARRAYLEN #include "emv/tlv.h" #include "asn1dump.h" #include "util.h" @@ -103,8 +104,152 @@ int asn1_print(uint8_t *asn1buf, size_t asn1buflen, const char *indent) { PrintAndLogEx(ERR, "Can't parse data as TLV tree"); return PM3_ESOFT; } - return PM3_SUCCESS; } +typedef struct { + const char *hex; + const char *expected; + const char *desc; +} asn1_test; + +int asn1_selftest(void) { + + PrintAndLogEx(INFO, "to be implemented. Feel free to contribute!"); + + /* + + ICEMAN: + Problem to be solved, how to extract data back from our asn1 decoder to compare with the expected text found in the following test cases. + Thanks @Mistial for the suggestion and links. + + These test cases are from the project lapo-luchini's asn1js (ISC license which is like MIT license) + https://github.com/lapo-luchini/asn1js/blob/trunk/test.js + + + const asn1_test tests[] = { + // RSA Laboratories technical notes from https://luca.ntop.org/Teaching/Appunti/asn1.html + {"0304066E5DC0", "(18 bit)\n011011100101110111", "ntop, bit string: DER encoding"}, + {"0304066E5DE0", "(18 bit)\n011011100101110111", "ntop, bit string: padded with `100000`"}, + {"038104066E5DC0", "(18 bit)\n011011100101110111", "ntop, bit string: long form of length octets"}, + {"23090303006E5D030206C0", "(18 bit)\n011011100101110111", "ntop, bit string (constructed encoding): `0110111001011101` + `11`"}, + {"160D7465737431407273612E636F6D", "test1@rsa.com", "ntop, ia5string: DER encoding"}, + {"16810D7465737431407273612E636F6D", "test1@rsa.com", "ntop, ia5string: long form of length octets"}, + {"36131605746573743116014016077273612E636F6D", "test1@rsa.com", "ntop, ia5string: constructed encoding: `test1` + `@` + `rsa.com`"}, + {"020100", "0", "ntop, integer: 0"}, + {"02017F", "127", "ntop, integer: 127"}, + {"02020080", "128", "ntop, integer: 128"}, + {"02020100", "256", "ntop, integer: 256"}, + {"020180", "-128", "ntop, integer: -128"}, + {"0202FF7F", "-129", "ntop, integer: -129"}, + {"0500", "", "ntop, null: DER"}, + {"058100", "", "ntop, null: long form of length octets"}, + {"06062A864886F70D", "1.2.840.113549", "ntop, object identifier"}, + {"04080123456789ABCDEF", "(8 byte)\n0123456789ABCDEF", "ntop, octet string: DER encoding"}, + {"0481080123456789ABCDEF", "(8 byte)\n0123456789ABCDEF", "ntop, octet string: long form of length octets"}, + {"240C040401234567040489ABCDEF", "(8 byte)\n0123456789ABCDEF", "ntop, octet string (constructed encoding): 01…67 + 89…ef"}, + {"130B5465737420557365722031", "Test User 1", "ntop, printable string: DER encoding"}, + {"13810B5465737420557365722031", "Test User 1", "ntop, printable string: long form of length octets"}, + {"330F130554657374201306557365722031", "Test User 1", "ntop, printable string: constructed encoding: `Test ` + `User 1`"}, + {"140F636CC26573207075626C6971756573", "clés publiques", "ntop, t61string: DER encoding"}, + {"14810F636CC26573207075626C6971756573", "clés publiques", "ntop, t61string: long form of length octets"}, + {"34151405636CC2657314012014097075626C6971756573", "clés publiques", "ntop, t61string: constructed encoding: `clés` + ` ` + `publiques`"}, + {"170D3931303530363233343534305A", "1991-05-06 23:45:40 UTC", "ntop, utc time: UTC"}, + {"17113931303530363136343534302D30373030", "1991-05-06 16:45:40 UTC-07:00", "ntop, utc time: PDT"}, + // inspired by http://luca.ntop.org/Teaching/Appunti/asn1.html + {"0304086E5DC0", "Exception:\nInvalid BitString with unusedBits=8", "bit string: invalid unusedBits"}, + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa379076(v=vs.85).aspx + {"30820319308202820201003023310F300D0603550403130654657374434E3110300E060355040A1307546573744F726730819F300D06092A864886F70D010101050003818D00308189028181008FE2412A08E851A88CB3E853E7D54950B3278A2BCBEAB54273EA0257CC6533EE882061A11756C12418E3A808D3BED931F3370B94B8CC43080B7024F79CB18D5DD66D82D0540984F89F970175059C89D4D5C91EC913D72A6B309119D6D442E0C49D7C9271E1B22F5C8DEEF0F1171ED25F315BB19CBC2055BF3A37424575DC90650203010001A08201B4301A060A2B0601040182370D0203310C160A362E302E353336312E323042060A2B0601040182370D0201313430321E260043006500720074006900660069006300610074006500540065006D0070006C0061007400651E080055007300650072305706092B0601040182371514314A30480201090C237669636833642E6A646F6D6373632E6E74746573742E6D6963726F736F66742E636F6D0C154A444F4D4353435C61646D696E6973747261746F720C07636572747265713074060A2B0601040182370D0202316630640201011E5C004D006900630072006F0073006F0066007400200045006E00680061006E006300650064002000430072007900700074006F0067007200610070006800690063002000500072006F00760069006400650072002000760031002E003003010030818206092A864886F70D01090E31753073301706092B0601040182371402040A1E08005500730065007230290603551D2504223020060A2B0601040182370A030406082B0601050507030406082B06010505070302300E0603551D0F0101FF0404030205A0301D0603551D0E041604143C0F73DAF8EF41D83AEABE922A5D2C966A7B9454300D06092A864886F70D01010505000381810047EB995ADF9E700DFBA73132C15F5C24C2E0BFC624AF15660EB86A2EAB2BC4971FE3CBDC63A525ECC7B428616636A1311BBFDDD0FCBF1794901DE55EC7115EC9559FEBA33E14C799A6CBBAA1460F39D444C4C84B760E205D6DA9349ED4D58742EB2426511490B40F065E5288327A9520A0FDF7E57D60DD72689BF57B058F6D1E", + "(3 elem)", "PKCS#10 request"}, + // Int10 + {"02102FA176B36EE9F049F444B40099661945", "(126 bit)\n63312083136615639753586560173617846597", "Big integer (126 bit)"}, + {"028181008953097086EE6147C5F4D5FFAF1B498A3D11EC5518E964DC52126B2614F743883F64CA51377ABB530DFD20464A48BD67CD27E7B29AEC685C5D10825E605C056E4AB8EEA460FA27E55AA62C498B02D7247A249838A12ECDF37C6011CF4F0EDEA9CEE687C1CB4A51C6AE62B2EFDB000723A01C99D6C23F834880BA8B42D5414E6F", + "(1024 bit)\n96432446964907009840023644401994013457468837455140331578268642517697945390319089463541388080569398374873228752921897678940332050406994011437231634303608704223145390228074087922901239478374991949372306413157758278029522534299413919735715864599284769202556071242381348472464716517735026291259010477833523908207", + "Big integer (1024 bit)"}, + {"02820201009BA9ABBF614A97AF2F97669A745FD0D996FDCFE2E466EF1F1F4733C244A3DF9ADE1FB554DD157C6935116FBBC80C8E6A181ED88FD916BC1048365CF063B3905A5C2437D7A3D6CB0971B9F1017284B07DDB4D80CDFCD36FC9F8DAB60E82D24585A81B68A83DE8F4446CBDA1C2CB03BE8C3E130084DF4A48C0E3220AE8E937A7184CB1090D23567F044DD9178418A5C8DA409473EBCE0E573C03813A9D0AA1574369AC576D799078E5B5B43BD8BC4C8D28A1A7A3A7BA024E25D12AAEEDAE0322B86B200F302854957FE0EECE0A669DD1402D6E22AF9D1AC10519D26FC0F29FF87BB30242FB50A91D2D930F23ABC6C10F92FFD0A215F55309711CFF451384E6265EF8E0881C0AFC16B6A87306B8F0638402A0C65AECE774DF70AEA38325EAD6C7978793A7C68A8A33976037103E973E6E2915D6A10FD1882C129F6FAAA4C642EB41A2E39543D301856D8EBB3BF32336C7FE3BE0A1250748ABC98974FF088F80BFC09665F3EEEC4B68BD9D88C331B340F1E8CFF638BB9CE4D17FD4E5589B7CFAD4F30E9B7591E4BA522E197ED1F5CD5A19FCBA06F6FB52A84B9904DDF8F9B48B50A34E6289F08724FA8342C187FAD52D292A5A717A646AD72760630DDBCE49F58D1F90893217F87343B8D25A938661D6E1750AEA796676884F71EB0425D60A5A7A93E5B94B17400FB1B6B9F5DE4FDCE0B3AC3B117060844A436E9920C029710AC065", + "(4096 bit)\n635048724432704421127930570668665246853305382538324205739741643121831497295424070220821366244137115920753022123888627038218427491681054376713237422498116573180444839575827154645186734602336866679804832661256616738257119870932328599495025506292424741581222812593482590762754785441060866630543522468678295806775919446210955958208696766307578451905771148370694894591388931494246786732600822191966681852303750991082312180670980233382216493445574638820565887523903118457551295241540793541306271618874366356869335229283992012581380459991160410157116077111142487644315609688092841697874946124416376553323373756926990721842430477982958383467149331238064901788481621489266725616974850293388387825359434286033332681714766010619113405542747712973535303497912234899589502990216128180823653963322406352206636893824027962569732222882297210841939793442415179615290739900774082858983244011679281115202763730964934473392333219986431517733237277686866318351054204026883453068392486990840498271719737813876367239342153571643327128417739316281558041652406500713712661305061745568036561978158652008943224271511931676512028205883718704503533046383542018858616087454820845906934069146870330990447993387221061968484774662499598623702280426558025111180066917", + "Big integer (4096 bit)"}, + {"0202007F", "127", "Padded 127"}, + {"0202FF7F", "-129", "Negative 129"}, + {"0202FC18", "-1000", "Negative 1000 (2)"}, + {"0204FFFFFC18", "-1000", "Negative 1000 (4)"}, + {"0208FFFFFFFFFFFFFC18", "-1000", "Negative 1000 (8)"}, + {"0210FFFFFFFFFFFFFFFFFFFFFFFFFFFFFC18", "-1000", "Negative 1000 (16)"}, + {"0203800001", "-8388607", "Negative 8388607"}, + {"02020000", "0", "Zero (2)"}, + {"0204FFFFFFFF", "-1", "Negative 1 (4)"}, + // OID + {"060C69C7C79AB78084C289F9870D", "2.25.84478768945400492475277", "Big OID arc"}, + {"06146982968D8D889BCCA8C7B3BDD4C080AAAED78A1B", "2.25.184830721219540099336690027854602552603", "Bigger OID arc"}, + {"060488378952", "2.999.1234", "OID arc > 2.47"}, + {"060782A384F3CAC00A", "2.9999999999930", "OID with Int10 corner case (1)"}, + {"060881E3AFEAA69A800A", "2.999999999999930", "OID with Int10 corner case (2)"}, + {"06092A864886F70D010105", "1.2.840.113549.1.1.5\nsha1WithRSAEncryption\nPKCS #1", "known OID from Peter Gutmann list"}, + // OID corner case from https://misc.daniel-marschall.de/asn.1/oid-sizecheck/oid_size_test.txt + {"060A81FFFFFFFFFFFFFFFF7F", "2.18446744073709551535", "OID root 64 bit - 1"}, + {"060A82808080808080808000", "2.18446744073709551536", "OID root 64 bit"}, + {"060A82808080808080808001", "2.18446744073709551537", "OID root 64 bit + 1"}, + {"0620FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7F", "2.26959946667150639794667015087019630673637144422540572481103610249135", "OID derLen20c"}, + {"0621818080808080808080808080808080808080808080808080808080808080808000", "2.26959946667150639794667015087019630673637144422540572481103610249136", "OID derLen21c"}, + // relative OID + {"0D0A0102030405060708090A","1.2.3.4.5.6.7.8.9.10", "Relative OID from GitHub PR 56"}, + {"0D04C27B0302","8571.3.2", "Relative OID from ISO/IEC 8825-1:2002 8.20.5"}, + // UTF-8 + {"0C0E4C61706FE280997320F09F9A972E", "Lapo’s 🚗.", "UTF-8 4-byte sequence"}, + // T-REC-X.690-201508 + {"0307040A3B5F291CD0", "(44 bit)\n00001010001110110101111100101001000111001101", "Example 8.6.4.2: bit string (primitive encoding)"}, + {"23800303000A3B0305045F291CD00000", "(44 bit)\n00001010001110110101111100101001000111001101", "Example 8.6.4.2: bit string (constructed encoding)"}, + // avoid past bugs + {"23800303000A3B230A0302005F030404291CD00000", "(44 bit)\n00001010001110110101111100101001000111001101", "Bit string (recursive constructed)"}, + {"0348003045022100DE601E573DAFB59BC551D58E3E7B9EDA0612DD0112805A2217B734759B884417022067C3FDE60780D41C1D7A3B90291F3D39C4DC2F206DCCBA2F982C06B67C09B232", "(568 bit)\n0011000001000101000000100010000100000000110111100110000000011110010101110011110110101111101101011001101111000101010100011101010110001110001111100111101110011110110110100000011000010010110111010000000100010010100000000101101000100010000101111011011100110100011101011001101110001000010001000001011100000010001000000110011111000011111111011110011000000111100000001101010000011100000111010111101000111011100100000010100100011111001111010011100111000100110111000010111100100000011011011100110010111010001011111001100000101100000001101011011001111100000010011011001000110010", "not constructed, but contains structures"}, + {"040731323334353637", "(7 byte)\n1234567", "Octet string with ASCII content"}, + {"0407312E3233E282AC", "(7 byte)\n1.23€", "Octet string with UTF-8 content"}, + // GitHub issue #47 + {"0420041EE4E3B7ED350CC24D034E436D9A1CB15BB1E328D37062FB82E84618AB0A3C", "(32 byte)\n041EE4E3B7ED350CC24D034E436D9A1CB15BB1E328D37062FB82E84618AB0A3C", "Do not mix encapsulated and structured octet strings"}, + // GitHub issue #54 + {"181531393835313130363231303632372E332D31323334", "1985-11-06 21:06:27.3 UTC-12:34", "UTC offsets with minutes"}, + // GitHub issue #54 + {"181331393835313130363231303632372E332B3134", "1985-11-06 21:06:27.3 UTC+14:00", "UTC offset +13 and +14"}, + }; + + int tot = ARRAYLEN(tests); + + PrintAndLogEx(INFO,"ASN1 decoder selftest. {%d tests}", tot); + int count = 0; + for (int i=0; i< ARRAYLEN(tests); i++) { + size_t n = strlen(tests[i].hex) * 2; + + uint8_t *d = calloc(n, sizeof(uint8_t)); + if (d == NULL) { + return PM3_EMALLOC; + } + int len = 0; + param_gethex_to_eol(tests[i].hex, 0, d, n, &len); + if (len == 0) { + free(d); + continue; + } + + PrintAndLogEx(INFO, "%s [%d: %s]", tests[i].desc, len, sprint_hex_inrow(d, len)); + + + struct tlvdb *t = tlvdb_parse_multi((const unsigned char*)n, len); + if (t) { + bool candump = false; + if (asn1_tag_dump(&t->tag, 0, &candump)) { + count++; + } + tlvdb_free(t); + } + free(d); + } + + + PrintAndLogEx(SUCCESS, "Pass... %s", (count == tot) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx(NORMAL, ""); + + */ + + return PM3_SUCCESS; +} diff --git a/client/src/crypto/asn1utils.h b/client/src/crypto/asn1utils.h index 691e574b2..e55a2926f 100644 --- a/client/src/crypto/asn1utils.h +++ b/client/src/crypto/asn1utils.h @@ -25,5 +25,6 @@ int asn1_print(uint8_t *asn1buf, size_t asn1buflen, const char *indent); int ecdsa_asn1_get_signature(uint8_t *signature, size_t signaturelen, uint8_t *rval, uint8_t *sval); +int asn1_selftest(void); #endif /* asn1utils.h */ diff --git a/client/src/crypto/libpcrypto.c b/client/src/crypto/libpcrypto.c index d2ac7a324..3634e9e4d 100644 --- a/client/src/crypto/libpcrypto.c +++ b/client/src/crypto/libpcrypto.c @@ -18,6 +18,7 @@ #include "crypto/libpcrypto.h" #include "crypto/asn1utils.h" +#include #include #include #include @@ -34,8 +35,10 @@ #include #include #include +#include "libpcrypto.h" #include "util.h" #include "ui.h" +#include "math.h" void des_encrypt(void *out, const void *in, const void *key) { mbedtls_des_context ctx; @@ -626,9 +629,42 @@ int blowfish_decrypt(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, mbedtls_blowfish_init(&blow); if (mbedtls_blowfish_setkey(&blow, key, 64)) return 1; + if (mbedtls_blowfish_crypt_cbc(&blow, MBEDTLS_BLOWFISH_DECRYPT, length, iiv, input, output)) return 2; + mbedtls_blowfish_free(&blow); return 0; } + +// Implementation from http://www.secg.org/sec1-v2.pdf#subsubsection.3.6.1 +int ansi_x963_sha256(uint8_t *sharedSecret, size_t sharedSecretLen, uint8_t *sharedInfo, size_t sharedInfoLen, size_t keyDataLen, uint8_t *keyData) { + // sha256 hash has (practically) no max input len, so skipping that step + + if (keyDataLen >= 32 * (pow(2, 32) - 1)) { + return 1; + } + + uint32_t counter = 0x00000001; + + for (int i = 0; i < (keyDataLen / 32); ++i) { + uint8_t *hashMaterial = calloc(4 + sharedSecretLen + sharedInfoLen, sizeof(uint8_t)); + memcpy(hashMaterial, sharedSecret, sharedSecretLen); + hashMaterial[sharedSecretLen] = (counter >> 24); + hashMaterial[sharedSecretLen + 1] = (counter >> 16) & 0xFF; + hashMaterial[sharedSecretLen + 2] = (counter >> 8) & 0xFF; + hashMaterial[sharedSecretLen + 3] = counter & 0xFF; + memcpy(hashMaterial + sharedSecretLen + 4, sharedInfo, sharedInfoLen); + + uint8_t hash[32] = {0}; + sha256hash(hashMaterial, 4 + sharedSecretLen + sharedInfoLen, hash); + free(hashMaterial); + + memcpy(keyData + (32 * i), hash, 32); + + counter++; + } + + return 0; +} diff --git a/client/src/crypto/libpcrypto.h b/client/src/crypto/libpcrypto.h index 301633fde..5d10b10ee 100644 --- a/client/src/crypto/libpcrypto.h +++ b/client/src/crypto/libpcrypto.h @@ -64,4 +64,7 @@ size_t FindISO9797M2PaddingDataLen(const uint8_t *data, size_t datalen); // BLOWFISH int blowfish_decrypt(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); +// KDF +int ansi_x963_sha256(uint8_t *sharedSecret, size_t sharedSecretLen, uint8_t *sharedInfo, size_t sharedInfoLen, size_t keyDataLen, uint8_t *keyData); + #endif /* libpcrypto.h */ diff --git a/client/src/emv/cmdemv.c b/client/src/emv/cmdemv.c index 71277d87b..af7255575 100644 --- a/client/src/emv/cmdemv.c +++ b/client/src/emv/cmdemv.c @@ -34,33 +34,40 @@ #include "emv_tags.h" #include "fileutils.h" #include "protocols.h" // ISO7816 APDU return codes +#include "commonutil.h" // MemBeToUint2byte +#include // DES +#include "crypto/libpcrypto.h" +#include "iso4217.h" // currency lookup + static int CmdHelp(const char *Cmd); #define TLV_ADD(tag, value)( tlvdb_change_or_add_node(tlvRoot, tag, sizeof(value) - 1, (const unsigned char *)value) ) static void ParamLoadDefaults(struct tlvdb *tlvRoot) { - //9F02:(Amount, authorized (Numeric)) len:6 + // 9F02:(Amount, authorized (Numeric)) len:6 TLV_ADD(0x9F02, "\x00\x00\x00\x00\x01\x00"); - //9F1A:(Terminal Country Code) len:2 + // 9F1A:(Terminal Country Code) len:2 TLV_ADD(0x9F1A, "ru"); - //5F2A:(Transaction Currency Code) len:2 + // 5F2A:(Transaction Currency Code) len:2 // USD 840, EUR 978, RUR 810, RUB 643, RUR 810(old), UAH 980, AZN 031, n/a 999 - TLV_ADD(0x5F2A, "\x09\x80"); - //9A:(Transaction Date) len:3 + TLV_ADD(0x5F2A, "\x090\x78"); + // 9A:(Transaction Date) len:3 TLV_ADD(0x9A, "\x00\x00\x00"); - //9C:(Transaction Type) len:1 | 00 => Goods and service #01 => Cash + // 9C:(Transaction Type) len:1 + // | 00 => Goods and Service + // | 01 => Cash TLV_ADD(0x9C, "\x00"); - // 9F37 Unpredictable Number len:4 + // 9F37 Unpredictable Number (UN) len:4 TLV_ADD(0x9F37, "\x01\x02\x03\x04"); // 9F6A Unpredictable Number (MSD for UDOL) len:4 TLV_ADD(0x9F6A, "\x01\x02\x03\x04"); - //9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 + // 9F66:(Terminal Transaction Qualifiers (TTQ)) len:4 TLV_ADD(0x9F66, "\x26\x00\x00\x00"); // qVSDC - //95:(Terminal Verification Results) len:5 + // 95:(Terminal Verification Results) len:5 // all OK TVR TLV_ADD(0x95, "\x00\x00\x00\x00\x00"); // 9F4E Merchant Name and Location len:x - TLV_ADD(0x9F4E, "proxmrk3rdv\x00"); + TLV_ADD(0x9F4E, "proxmark3rdv4\x00"); } static void PrintChannel(Iso7816CommandChannel channel) { @@ -74,6 +81,501 @@ static void PrintChannel(Iso7816CommandChannel channel) { } } +/* +static int emv_calc_cvv(const uint8_t *pan, size_t panlen, const uint8_t *expiry, const uint8_t *servicecode, const uint8_t *atc) { + + uint8_t key[16] = {0}; + memset(key, 0x30, sizeof(key)); + + uint8_t d[32] = {0}; + uint8_t *pd = d; + + memcpy(pd, pan, panlen); + pd += panlen; + memcpy(pd, expiry, 4); + pd += 4; + + // cvv/cvc + memcpy(pd, servicecode, 3); + pd += 3; + + // atc + memcpy(pd, atc, 4); + + uint8_t encrypted[16] = {0}; + + // zero padding?!? + + mbedtls_des_context ctx; + mbedtls_des_setkey_enc(&ctx, key); + mbedtls_des_crypt_ecb(&ctx, d, encrypted); + mbedtls_des_crypt_ecb(&ctx, d + 6, encrypted + 6); + + // xor + for (size_t i = 16; i < 32; i++) { + d[i] ^= encrypted[i - 16]; + } + + mbedtls_des_free(&ctx); + + PrintAndLogEx(INFO, "key... %s", sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(INFO, "d..... %s", sprint_hex_inrow(d, sizeof(d))); + + + mbedtls_des3_context ctx3; + mbedtls_des3_init(&ctx3); + mbedtls_des3_set2key_enc(&ctx3, key); + mbedtls_des3_set2key_dec(&ctx3, key); + mbedtls_des3_crypt_ecb(&ctx3, d, encrypted); + mbedtls_des3_free(&ctx3); + + PrintAndLogEx(INFO, "enc... %s", sprint_hex_inrow(encrypted, sizeof(encrypted))); + + memset(encrypted, 0, sizeof(encrypted)); + des3_encrypt(encrypted, d, key, 2); + PrintAndLogEx(INFO, "enc... %s", sprint_hex_inrow(encrypted, sizeof(encrypted))); + + return PM3_SUCCESS; +} +*/ + + +static size_t logtemplate_calculate_len(const struct tlv *tlv, size_t data_len) { + if (!tlv) + return 0; + + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + size_t count = 0; + + while (left) { + struct tlv cur_tlv; + if (!tlv_parse_tl(&buf, &left, &cur_tlv)) + return 0; + + count += cur_tlv.len; + + /* Last tag can be of variable length */ + if (cur_tlv.len == 0 && left == 0) + count = data_len; + } + + return count; +} + +static struct tlvdb *emv_logtemplate_parse(const struct tlv *tlv, const unsigned char *data, size_t data_len) { + if (!tlv) + return NULL; + + const unsigned char *buf = tlv->value; + size_t left = tlv->len; + size_t res_len = logtemplate_calculate_len(tlv, data_len); + size_t pos = 0; + struct tlvdb *db = NULL; + + while (left) { + struct tlv cur_tlv; + if (!tlv_parse_tl(&buf, &left, &cur_tlv) || pos + cur_tlv.len > res_len) { + tlvdb_free(db); + return NULL; + } + + /* Last tag can be of variable length */ + if (cur_tlv.len == 0 && left == 0) + cur_tlv.len = res_len - pos; + + struct tlvdb *tag_db = tlvdb_fixed(cur_tlv.tag, cur_tlv.len, data + pos); + if (!db) + db = tag_db; + else + tlvdb_add(db, tag_db); + + pos += cur_tlv.len; + } + + return db; +} + +static int emv_parse_log(struct tlvdb *ttdb, const uint8_t *d, size_t n) { + /* + The Log Format (9F4F) is a list in tag and length format (i.e., "TL" instead of TLV) See description in Table 33 on page 141. + + In your example, "9F 27 01 9F 02 06 5F 2A 02 9A 03 9F 36 02 9F 52 06 DF 3E 01 9F 21 03 9F 7C 14" means: + + 9F27 01 (Cryptogram Information Data) + 9F02 06 (Amount, Authorised) + 5F2A 02 (Transaction Currency Code) + 9A 03 (Transaction Date) + 9F36 02 (Application Transaction Counter) + 9F52 06 (Terminal Compatibility Indicator) + DF3E 01 + 9F21 03 (Transaction Time) + 9F7C 14 (Visa Customer Exclusive Data) + + */ + int pos = 0; + struct tlvdb *tp = ttdb; + while (tp) { + const struct tlv *tpitem = tlvdb_get_tlv(tp); + + const char *s = emv_get_tag_name(tpitem); + + switch (tpitem->tag) { + case 0x5F2A: + if (tpitem->len == 2) { + + char tmp[5] = {0}; + snprintf(tmp, sizeof(tmp), "%x%02x", d[pos], d[pos + 1]); + const char *cn = getCurrencyInfo(tmp); + PrintAndLogEx(INFO, "%-30s... " _YELLOW_("%s") " ( %x%02x )", s, cn, d[pos], d[pos + 1]); + } + break; + case 0x9A: + if (tpitem->len == 3) { + PrintAndLogEx(INFO, "%-30s... " _YELLOW_("20%02x-%02x-%02x"), s, d[pos], d[pos + 1], d[pos + 2]); + } + break; + case 0x9F21: + if (tpitem->len == 3) { + PrintAndLogEx(INFO, "%-30s... " _YELLOW_("%02x:%02x:%02x"), s, d[pos], d[pos + 1], d[pos + 2]); + } + break; + default: + PrintAndLogEx(INFO, "%-30s... " _YELLOW_("%s"), s, sprint_hex_inrow(d + pos, tpitem->len)); + break; + } + + pos += tpitem->len; + + tp = tlvdb_elm_get_next(tp); + } + return PM3_SUCCESS; +} + +static int emv_extract_log_info(uint8_t *response, size_t reslen, uint8_t *lid, uint8_t *lrecs) { + + struct tlvdb *t = tlvdb_parse_multi(response, reslen); + if (t == NULL) { + PrintAndLogEx(INFO, "root null"); + return PM3_EINVARG; + } + + int res = PM3_ESOFT; + struct tlvdb *logs = tlvdb_find_full(t, 0x9F4D); + if (logs != NULL) { + const struct tlv *tlv = tlvdb_get_tlv(logs); + if (tlv->len == 2) { + *lid = tlv->value[0]; + *lrecs = tlv->value[1]; + PrintAndLogEx(DEBUG, "Logs EMV... SFI %u Records # %u", *lid, *lrecs); + res = PM3_SUCCESS; + } + } + + tlvdb_free(t); + return res; +} + +static int emv_parse_track1(const uint8_t *d, size_t n, bool verbose) { + if (d == NULL || n < 10) { + return PM3_EINVARG; + } + if (verbose == false) { + return PM3_SUCCESS; + } + + // sanity checks + if (d[0] != 'B') { + return PM3_EINVARG; + } + + // decoder + char *tmp = str_ndup((const char *)d, n); + uint8_t i = 0; + char delim[2] = "^"; + char *token = strtok(tmp, delim); + while (token != NULL) { + + switch (i) { + case 0: { + size_t a = strlen(token); + if (a == 16) { + PrintAndLogEx(INFO, "PAN...................... " _GREEN_("%c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c"), + token[1], token[2], token[3], token[4], + token[5], token[6], token[7], token[8], + token[9], token[10], token[11], token[12], + token[13], token[14], token[15], token[16] + ); + } else if (a == 19) { + PrintAndLogEx(INFO, "PAN...................... " _GREEN_("%c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c %c%c%c"), + token[1], token[2], token[3], token[4], + token[5], token[6], token[7], token[8], + token[9], token[10], token[11], token[12], + token[13], token[14], token[15], token[16], + token[17], token[18], token[19] + ); + } + break; + } + case 1: + PrintAndLogEx(INFO, "CardHolder............... %s", token); + break; + case 2: + if (strlen(token) < 14) { + break; + } + PrintAndLogEx(INFO, "Expiry date.............. %.*s ( %c%c/%c%c )", 4, token, token[2], token[3], token[0], token[1]); + token += 4; + + PrintAndLogEx(INFO, "Service code............. %.*s", 3, token); + token += 3; + + PrintAndLogEx(INFO, "Unknown.................. %.*s", 4, token); + token += 4; + + PrintAndLogEx(INFO, "CVV / iCvv............... %.*s", 3, token); + token += 3; + + PrintAndLogEx(INFO, "Trailing................. %s", token); + break; + default: + break; + } + token = strtok(0, delim); + i++; + } + free(tmp); + return PM3_SUCCESS; +} + +static int emv_parse_track2(const uint8_t *d, size_t n, bool verbose) { + if (d == NULL || n < 10) { + return PM3_EINVARG; + } + if (verbose == false) { + return PM3_SUCCESS; + } + + // decoder + uint8_t s[80] = {0}; + hex_to_buffer(s, d, n, n, 0, 0, true); + uint8_t *tmp = s; + + if (tmp[0] == ';') + tmp++; + + PrintAndLogEx(INFO, "PAN...................... "_GREEN_("%c%c%c%c %c%c%c%c %c%c%c%c %c%c%c%c"), + tmp[0], tmp[1], tmp[2], tmp[3], + tmp[4], tmp[5], tmp[6], tmp[7], + tmp[8], tmp[9], tmp[10], tmp[11], + tmp[12], tmp[13], tmp[14], tmp[15] + ); + tmp += 16; + + if (tmp[0] == '=' || tmp[0] == 'D') + tmp++; + + PrintAndLogEx(INFO, "Expiry date.............. %.*s ( %c%c/%c%c )", 4, tmp, tmp[2], tmp[3], tmp[0], tmp[1]); + tmp += 4; + + PrintAndLogEx(INFO, "Service code............. %.*s", 3, tmp); + tmp += 3; + + PrintAndLogEx(INFO, "Pin verification value... %.*s", 4, tmp); + tmp += 4; + + PrintAndLogEx(INFO, "CVV / iCvv............... %.*s", 3, tmp); + tmp += 3; + + PrintAndLogEx(INFO, "Trailing................. %s", tmp); + + return PM3_SUCCESS; +} + +static int emv_parse_card_details(uint8_t *response, size_t reslen, bool verbose) { + + struct tlvdb *root = tlvdb_parse_multi(response, reslen); + if (root == NULL) { + return PM3_EINVARG; + } + + // extract application preferred name + struct tlvdb *prefname_full = tlvdb_find_full(root, 0x9F12); + if (prefname_full != NULL) { + const struct tlv *prefname_tlv = tlvdb_get_tlv(prefname_full); + if (prefname_tlv->len) { + char name[64] = {0}; + size_t n = MIN(sizeof(name), prefname_tlv->len); + memcpy(name, prefname_tlv->value, n); + PrintAndLogEx(INFO, "Application.......... " _YELLOW_("%s"), name); + } + } + + // extract application label + struct tlvdb *alabel = tlvdb_find_full(root, 0x50); + if (alabel != NULL) { + const struct tlv *alabel_tlv = tlvdb_get_tlv(alabel); + if (alabel_tlv->len) { + char name[64] = {0}; + size_t n = MIN(sizeof(name), alabel_tlv->len); + memcpy(name, alabel_tlv->value, n); + PrintAndLogEx(INFO, "Label................ " _YELLOW_("%s"), name); + } + } + + // extract language preference + struct tlvdb *lang_full = tlvdb_find_full(root, 0x5F2D); + if (lang_full != NULL) { + const struct tlv *lang_tlv = tlvdb_get_tlv(lang_full); + if (lang_tlv->len) { + char lang[16] = {0}; + size_t n = MIN(sizeof(lang), lang_tlv->len); + memcpy(lang, lang_tlv->value, n); + PrintAndLogEx(INFO, "Language............. " _YELLOW_("%s"), lang); + } + } + + // Application Currency Code + struct tlvdb *acc_full = tlvdb_find_full(root, 0x9F42); + if (acc_full != NULL) { + const struct tlv *acc_tlv = tlvdb_get_tlv(acc_full); + if (acc_tlv->len == 2) { + uint16_t acc = MemBeToUint2byte((const uint8_t *)acc_tlv->value); + + char tmp[5] = {0}; + snprintf(tmp, sizeof(tmp), "%x%02x", acc_tlv->value[0], acc_tlv->value[1]); + const char *cn = getCurrencyInfo(tmp); + + PrintAndLogEx(INFO, "Currency Code........ " _YELLOW_("%s") " ( %x )", cn, acc); + } + } + + // Application Effective Date + struct tlvdb *aeffect_full = tlvdb_find_full(root, 0x5F25); + if (aeffect_full != NULL) { + const struct tlv *aaeffect_tlv = tlvdb_get_tlv(aeffect_full); + if (aaeffect_tlv->len == 3) { + PrintAndLogEx(INFO, "Effective date....... " _YELLOW_("20%02x-%02x-%02x"), + aaeffect_tlv->value[0], + aaeffect_tlv->value[1], + aaeffect_tlv->value[2] + ); + } + } + + // Application Expiration Date + struct tlvdb *aexd_full = tlvdb_find_full(root, 0x5F24); + if (aexd_full != NULL) { + const struct tlv *aexd_tlv = tlvdb_get_tlv(aexd_full); + if (aexd_tlv->len == 3) { + PrintAndLogEx(INFO, "Expiration date...... " _YELLOW_("20%02x-%02x-%02x"), + aexd_tlv->value[0], + aexd_tlv->value[1], + aexd_tlv->value[2] + ); + } + } + + // Application Primary Account Number (PAN) + struct tlvdb *apan_full = tlvdb_find_full(root, 0x5A); + if (apan_full != NULL) { + const struct tlv *apan_tlv = tlvdb_get_tlv(apan_full); + if (apan_tlv->len == 8) { + PrintAndLogEx(INFO, "PAN.................. " _GREEN_("%02x%02x %02x%02x %02x%02x %02x%02x"), + apan_tlv->value[0], + apan_tlv->value[1], + apan_tlv->value[2], + apan_tlv->value[3], + apan_tlv->value[4], + apan_tlv->value[5], + apan_tlv->value[6], + apan_tlv->value[7] + ); + } + } + + // Application Primary Account Number (PAN) sequence number + struct tlvdb *apansq_full = tlvdb_find_full(root, 0x5F34); + if (apansq_full != NULL) { + const struct tlv *apansq_tlv = tlvdb_get_tlv(apansq_full); + if (apansq_tlv->len == 1) { + PrintAndLogEx(INFO, "PAN Sequence......... " _YELLOW_("%u"), apansq_tlv->value[0]); + } + } + + // Cardholder Name + struct tlvdb *chm_full = tlvdb_find_full(root, 0x5F20); + if (chm_full != NULL) { + const struct tlv *chm_tlv = tlvdb_get_tlv(chm_full); + if (chm_tlv->len) { + PrintAndLogEx(INFO, "Cardhold Name........ " _YELLOW_("%s"), sprint_ascii(chm_tlv->value, chm_tlv->len)); + } + } + + // Track 1 Data + struct tlvdb *track1_full = tlvdb_find_full(root, 0x56); + if (track1_full != NULL) { + const struct tlv *track1_tlv = tlvdb_get_tlv(track1_full); + if (track1_tlv->len) { + PrintAndLogEx(INFO, "Track 1.............. " _YELLOW_("%s"), sprint_ascii(track1_tlv->value, track1_tlv->len)); + emv_parse_track1(track1_tlv->value, track1_tlv->len, verbose); + } + } + + // Track 2 Data + struct tlvdb *track2_full = tlvdb_find_full(root, 0x9F6B); + if (track2_full != NULL) { + const struct tlv *track2_tlv = tlvdb_get_tlv(track2_full); + if (track2_tlv->len) { + PrintAndLogEx(INFO, "Track 2.............. " _YELLOW_("%s"), sprint_hex_inrow(track2_tlv->value, track2_tlv->len)); + emv_parse_track2(track2_tlv->value, track2_tlv->len, verbose); + } + } + + // Track 2 Equivalent Data + struct tlvdb *track2_eq_full = tlvdb_find_full(root, 0x57); + if (track2_eq_full != NULL) { + const struct tlv *track2_eq_tlv = tlvdb_get_tlv(track2_eq_full); + if (track2_eq_tlv->len) { + PrintAndLogEx(INFO, "Track 2 equivalent... " _YELLOW_("%s"), sprint_hex_inrow(track2_eq_tlv->value, track2_eq_tlv->len)); + emv_parse_track2(track2_eq_tlv->value, track2_eq_tlv->len, verbose); + } + } + + // Track 3 Data + // to be impl. + + // Unpredicable Number (UN) + struct tlvdb *un1_full = tlvdb_find_full(root, 0x9f37); + if (un1_full != NULL) { + const struct tlv *un1_tlv = tlvdb_get_tlv(un1_full); + if (un1_tlv->len) { + PrintAndLogEx(INFO, "9F37 Unpredicable Number... " _YELLOW_("%s"), sprint_hex_inrow(un1_tlv->value, un1_tlv->len)); + } + } + + // Unpredicable Number (UN) + struct tlvdb *un_full = tlvdb_find_full(root, 0x9f6a); + if (un_full != NULL) { + const struct tlv *un_tlv = tlvdb_get_tlv(un_full); + if (un_tlv->len) { + PrintAndLogEx(INFO, "9F6A Unpredicable Number... " _YELLOW_("%s"), sprint_hex_inrow(un_tlv->value, un_tlv->len)); + emv_parse_track2(un_tlv->value, un_tlv->len, verbose); + } + } + + struct tlvdb *merch_full = tlvdb_find_full(root, 0x9f4e); + if (merch_full != NULL) { + const struct tlv *merch_tlv = tlvdb_get_tlv(merch_full); + if (merch_tlv->len) { + PrintAndLogEx(INFO, "Merchant Name and Location... " _YELLOW_("%s"), sprint_hex_inrow(merch_tlv->value, merch_tlv->len)); + } + } + + tlvdb_free(root); + return PM3_SUCCESS; +} + static int CmdEMVSelect(const char *Cmd) { uint8_t data[APDU_AID_LEN] = {0}; int datalen = 0; @@ -81,16 +583,16 @@ static int CmdEMVSelect(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "emv select", "Executes select applet command", - "emv select -s a00000000101 -> select card, select applet\n" + "emv select -s a00000000101 -> select card, select applet\n" "emv select -st a00000000101 -> select card, select applet, show result in TLV\n"); void *argtable[] = { arg_param_begin, - arg_lit0("sS", "select", "Activate field and select card"), - arg_lit0("kK", "keep", "Keep field for next command"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("s", "select", "Activate field and select card"), + arg_lit0("k", "keep", "Keep field for next command"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_str1(NULL, NULL, "", "Applet AID"), arg_param_end }; @@ -98,7 +600,7 @@ static int CmdEMVSelect(const char *Cmd) { bool activateField = arg_get_lit(ctx, 1); bool leaveSignalON = arg_get_lit(ctx, 2); - bool APDULogging = arg_get_lit(ctx, 3); + bool show_apdu = arg_get_lit(ctx, 3); bool decodeTLV = arg_get_lit(ctx, 4); Iso7816CommandChannel channel = CC_CONTACTLESS; if (arg_get_lit(ctx, 5)) @@ -107,7 +609,7 @@ static int CmdEMVSelect(const char *Cmd) { CLIGetHexWithReturn(ctx, 6, data, &datalen); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -124,6 +626,7 @@ static int CmdEMVSelect(const char *Cmd) { if (decodeTLV) TLVPrintFromBuffer(buf, len); + SetAPDULogging(false); return PM3_SUCCESS; } @@ -137,18 +640,18 @@ static int CmdEMVSearch(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("sS", "select", "Activate field and select card"), - arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("s", "select", "Activate field and select card"), + arg_lit0("k", "keep", "Keep field ON for next command"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results of selected applets"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool activateField = arg_get_lit(ctx, 1); bool leaveSignalON = arg_get_lit(ctx, 2); - bool APDULogging = arg_get_lit(ctx, 3); + bool show_apdu = arg_get_lit(ctx, 3); bool decodeTLV = arg_get_lit(ctx, 4); Iso7816CommandChannel channel = CC_CONTACTLESS; @@ -159,13 +662,14 @@ static int CmdEMVSearch(const char *Cmd) { PrintChannel(channel); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); const char *al = "Applets list"; struct tlvdb *t = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); - if (EMVSearch(channel, activateField, leaveSignalON, decodeTLV, t)) { + if (EMVSearch(channel, activateField, leaveSignalON, decodeTLV, t, false)) { tlvdb_free(t); + SetAPDULogging(false); return PM3_ERFTRANS; } @@ -178,6 +682,7 @@ static int CmdEMVSearch(const char *Cmd) { tlvdb_free(t); + SetAPDULogging(false); return PM3_SUCCESS; } @@ -191,13 +696,13 @@ static int CmdEMVPPSE(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("sS", "select", "Activate field and select card"), - arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("1", "pse", "PSE (1PAY.SYS.DDF01) mode"), - arg_lit0("2", "ppse", "PPSE (2PAY.SYS.DDF01) mode (def)"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("s", "select", "Activate field and select card"), + arg_lit0("k", "keep", "Keep field ON for next command"), + arg_lit0("1", "pse", "PSE (1PAY.SYS.DDF01) mode"), + arg_lit0("2", "ppse", "PPSE (2PAY.SYS.DDF01) mode (def)"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results of selected applets"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -205,19 +710,23 @@ static int CmdEMVPPSE(const char *Cmd) { bool activateField = arg_get_lit(ctx, 1); bool leaveSignalON = arg_get_lit(ctx, 2); uint8_t PSENum = 2; - if (arg_get_lit(ctx, 3)) + if (arg_get_lit(ctx, 3)) { PSENum = 1; - if (arg_get_lit(ctx, 4)) + } + if (arg_get_lit(ctx, 4)) { PSENum = 2; - bool APDULogging = arg_get_lit(ctx, 5); + } + bool show_apdu = arg_get_lit(ctx, 5); bool decodeTLV = arg_get_lit(ctx, 6); + Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 7)) + if (arg_get_lit(ctx, 7)) { channel = CC_CONTACT; + } PrintChannel(channel); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // exec uint8_t buf[APDU_RES_LEN] = {0}; @@ -234,6 +743,7 @@ static int CmdEMVPPSE(const char *Cmd) { if (decodeTLV) TLVPrintFromBuffer(buf, len); + SetAPDULogging(false); return PM3_SUCCESS; } @@ -251,13 +761,13 @@ static int CmdEMVGPO(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("pP", "params", "Load parameters from `emv_defparams.json` file for PDOLdata making from PDOL and parameters"), - arg_lit0("mM", "make", "Make PDOLdata from PDOL (tag 9F38) and parameters (def: uses default parameters)"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), - arg_strx0(NULL, NULL, "", "PDOLdata/PDOL"), + arg_lit0("k", "keep", "Keep field ON for next command"), + arg_lit0("p", "params", "Load parameters from `emv_defparams.json` file for PDOLdata making from PDOL and parameters"), + arg_lit0("m", "make", "Make PDOLdata from PDOL (tag 9F38) and parameters (def: uses default parameters)"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results of selected applets"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_strx0(NULL, NULL, "", "PDOLdata/PDOL"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -265,16 +775,17 @@ static int CmdEMVGPO(const char *Cmd) { bool leaveSignalON = arg_get_lit(ctx, 1); bool paramsLoadFromFile = arg_get_lit(ctx, 2); bool dataMakeFromPDOL = arg_get_lit(ctx, 3); - bool APDULogging = arg_get_lit(ctx, 4); + bool show_apdu = arg_get_lit(ctx, 4); bool decodeTLV = arg_get_lit(ctx, 5); Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 6)) + if (arg_get_lit(ctx, 6)) { channel = CC_CONTACT; + } PrintChannel(channel); CLIGetHexWithReturn(ctx, 7, data, &datalen); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // Init TLV tree const char *alr = "Root terminal TLV tree"; @@ -302,6 +813,7 @@ static int CmdEMVGPO(const char *Cmd) { PrintAndLogEx(ERR, "Can't create PDOL TLV."); tlvdb_free(tmp_ext); tlvdb_free(tlvRoot); + SetAPDULogging(false); return PM3_ESOFT; } } else { @@ -317,8 +829,10 @@ static int CmdEMVGPO(const char *Cmd) { PrintAndLogEx(ERR, "Can't create PDOL data."); tlvdb_free(tmp_ext); tlvdb_free(tlvRoot); - if (pdol_data_tlv != &data_tlv) + if (pdol_data_tlv != &data_tlv) { free(pdol_data_tlv); + } + SetAPDULogging(false); return PM3_ESOFT; } PrintAndLogEx(INFO, "PDOL data[%zu]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); @@ -344,6 +858,7 @@ static int CmdEMVGPO(const char *Cmd) { if (decodeTLV) TLVPrintFromBuffer(buf, len); + SetAPDULogging(false); return PM3_SUCCESS; } @@ -360,21 +875,22 @@ static int CmdEMVReadRecord(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), - arg_strx1(NULL, NULL, "", "", "> 8, sw & 0xff)); @@ -420,15 +937,15 @@ static int CmdEMVAC(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("cC", "cda", "Executes CDA transaction. Needs to get SDAD in results."), - arg_str0("dD", "decision", "", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"), - arg_lit0("pP", "params", "Load parameters from `emv_defparams.json` file for CDOLdata making from CDOL and parameters"), - arg_lit0("mM", "make", "Make CDOLdata from CDOL (tag 8C and 8D) and parameters (def: use default parameters)"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), - arg_strx1(NULL, NULL, "", "CDOLdata/CDOL"), + arg_lit0("k", "keep", "Keep field ON for next command"), + arg_lit0("c", "cda", "Executes CDA transaction. Needs to get SDAD in results."), + arg_str0("d", "decision", "", "Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested"), + arg_lit0("p", "params", "Load parameters from `emv_defparams.json` file for CDOLdata making from CDOL and parameters"), + arg_lit0("m", "make", "Make CDOLdata from CDOL (tag 8C and 8D) and parameters (def: use default parameters)"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results of selected applets"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_strx1(NULL, NULL, "", "CDOLdata/CDOL"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -437,14 +954,17 @@ static int CmdEMVAC(const char *Cmd) { bool trTypeCDA = arg_get_lit(ctx, 2); uint8_t termDecision = 0xff; if (arg_get_str_len(ctx, 3)) { - if (!strncmp(arg_get_str(ctx, 3)->sval[0], "aac", 4)) + if (!strncmp(arg_get_str(ctx, 3)->sval[0], "aac", 4)) { termDecision = EMVAC_AAC; - if (!strncmp(arg_get_str(ctx, 3)->sval[0], "tc", 4)) + } + if (!strncmp(arg_get_str(ctx, 3)->sval[0], "tc", 4)) { termDecision = EMVAC_TC; - if (!strncmp(arg_get_str(ctx, 3)->sval[0], "arqc", 4)) + } + if (!strncmp(arg_get_str(ctx, 3)->sval[0], "arqc", 4)) { termDecision = EMVAC_ARQC; + } - if (termDecision == 0xff) { + if (termDecision == 0xFF) { PrintAndLogEx(ERR, "ERROR: can't find terminal decision '%s'", arg_get_str(ctx, 3)->sval[0]); CLIParserFree(ctx); return PM3_EINVARG; @@ -452,22 +972,25 @@ static int CmdEMVAC(const char *Cmd) { } else { termDecision = EMVAC_TC; } - if (trTypeCDA) + + if (trTypeCDA) { termDecision = termDecision | EMVAC_CDAREQ; + } bool paramsLoadFromFile = arg_get_lit(ctx, 4); bool dataMakeFromCDOL = arg_get_lit(ctx, 5); - bool APDULogging = arg_get_lit(ctx, 6); + bool show_apdu = arg_get_lit(ctx, 6); bool decodeTLV = arg_get_lit(ctx, 7); Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 8)) + if (arg_get_lit(ctx, 8)) { channel = CC_CONTACT; + } PrintChannel(channel); CLIGetHexWithReturn(ctx, 9, data, &datalen); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // Init TLV tree const char *alr = "Root terminal TLV tree"; @@ -496,6 +1019,7 @@ static int CmdEMVAC(const char *Cmd) { PrintAndLogEx(ERR, "Can't create CDOL TLV."); tlvdb_free(tmp_ext); tlvdb_free(tlvRoot); + SetAPDULogging(false); return PM3_ESOFT; } } else { @@ -512,6 +1036,7 @@ static int CmdEMVAC(const char *Cmd) { size_t len = 0; uint16_t sw = 0; int res = EMVAC(channel, leaveSignalON, termDecision, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); + SetAPDULogging(false); if (cdol_data_tlv != &data_tlv) free(cdol_data_tlv); @@ -542,28 +1067,30 @@ static int CmdEMVGenerateChallenge(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("k", "keep", "Keep field ON for next command"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool leaveSignalON = arg_get_lit(ctx, 1); - bool APDULogging = arg_get_lit(ctx, 2); + bool show_apdu = arg_get_lit(ctx, 2); Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 3)) + if (arg_get_lit(ctx, 3)) { channel = CC_CONTACT; + } PrintChannel(channel); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // exec uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; int res = EMVGenerateChallenge(channel, leaveSignalON, buf, sizeof(buf), &len, &sw, NULL); + SetAPDULogging(false); if (sw) PrintAndLogEx(INFO, "APDU response status: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); @@ -573,8 +1100,9 @@ static int CmdEMVGenerateChallenge(const char *Cmd) { PrintAndLogEx(SUCCESS, "Challenge: %s", sprint_hex(buf, len)); - if (len != 4 && len != 8) - PrintAndLogEx(WARNING, "Length of challenge must be 4 or 8, but it %zu", len); + if (len != 4 && len != 8) { + PrintAndLogEx(WARNING, "Length of challenge must be 4 or 8, got " _YELLOW_("%zu"), len); + } return PM3_SUCCESS; } @@ -594,13 +1122,13 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("kK", "keep", "Keep field ON for next command"), - arg_lit0("pP", "params", "Load parameters from `emv_defparams.json` file for DDOLdata making from DDOL and parameters"), - arg_lit0("mM", "make", "Make DDOLdata from DDOL (tag 9F49) and parameters (def: use default parameters)"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results of selected applets"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), - arg_strx1(NULL, NULL, "", "DDOLdata/DDOL"), + arg_lit0("k", "keep", "Keep field ON for next command"), + arg_lit0("p", "params", "Load parameters from `emv_defparams.json` file for DDOLdata making from DDOL and parameters"), + arg_lit0("m", "make", "Make DDOLdata from DDOL (tag 9F49) and parameters (def: use default parameters)"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results of selected applets"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_strx1(NULL, NULL, "", "DDOLdata/DDOL"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -608,16 +1136,17 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { bool leaveSignalON = arg_get_lit(ctx, 1); bool paramsLoadFromFile = arg_get_lit(ctx, 2); bool dataMakeFromDDOL = arg_get_lit(ctx, 3); - bool APDULogging = arg_get_lit(ctx, 4); + bool show_apdu = arg_get_lit(ctx, 4); bool decodeTLV = arg_get_lit(ctx, 5); Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 6)) + if (arg_get_lit(ctx, 6)) { channel = CC_CONTACT; + } PrintChannel(channel); CLIGetHexWithReturn(ctx, 7, data, &datalen); CLIParserFree(ctx); - SetAPDULogging(APDULogging); + SetAPDULogging(show_apdu); // Init TLV tree const char *alr = "Root terminal TLV tree"; @@ -646,6 +1175,7 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { PrintAndLogEx(ERR, "Can't create DDOL TLV."); tlvdb_free(tmp_ext); tlvdb_free(tlvRoot); + SetAPDULogging(false); return PM3_ESOFT; } } else { @@ -662,6 +1192,7 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { size_t len = 0; uint16_t sw = 0; int res = EMVInternalAuthenticate(channel, leaveSignalON, data, datalen, buf, sizeof(buf), &len, &sw, NULL); + SetAPDULogging(false); if (ddol_data_tlv != &data_tlv) free(ddol_data_tlv); @@ -681,7 +1212,14 @@ static int CmdEMVInternalAuthenticate(const char *Cmd) { return PM3_SUCCESS; } -#define dreturn(n) {free(pdol_data_tlv); tlvdb_free(tlvSelect); tlvdb_free(tlvRoot); DropFieldEx( channel ); return n;} +#define dreturn(n) { \ + free(pdol_data_tlv); \ + tlvdb_free(tlvSelect); \ + tlvdb_free(tlvRoot); \ + DropFieldEx( channel ); \ + SetAPDULogging(false); \ + return n; \ + } static void InitTransactionParameters(struct tlvdb *tlvRoot, bool paramLoadJSON, enum TransactionType TrType, bool GenACGPO) { @@ -718,6 +1256,7 @@ static void InitTransactionParameters(struct tlvdb *tlvRoot, bool paramLoadJSON, TLV_ADD(0x9F66, "\x26\x00\x00\x00"); } break; + case TT_END: default: break; } @@ -799,6 +1338,69 @@ static void ProcessACResponseFormat1(struct tlvdb *tlvRoot, uint8_t *buf, size_t } static int CmdEMVExec(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "emv exec", + "Executes EMV contactless transaction", + "emv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" + "emv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("s", "select", "Activate field and select card"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results"), + arg_lit0("j", "jload", "Load transaction parameters from `emv_defparams.json` file"), + arg_lit0(NULL, "force", "Force search AID. Search AID instead of execute PPSE"), + arg_rem("By default:", "Transaction type - MSD"), + arg_lit0("v", "qvsdc", "Transaction type - qVSDC or M/Chip"), + arg_lit0("c", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)"), + arg_lit0("x", "vsdc", "Transaction type - VSDC. For test only. Not a standard behavior"), + arg_lit0("g", "acgpo", "VISA. generate AC from GPO"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool activateField = arg_get_lit(ctx, 1); + bool show_apdu = arg_get_lit(ctx, 2); + bool decodeTLV = arg_get_lit(ctx, 3); + bool paramLoadJSON = arg_get_lit(ctx, 4); + bool forceSearch = arg_get_lit(ctx, 5); + + enum TransactionType TrType = TT_MSD; + + if (arg_get_lit(ctx, 7)) { + TrType = TT_QVSDCMCHIP; + } + + if (arg_get_lit(ctx, 8)) { + TrType = TT_CDA; + } + + if (arg_get_lit(ctx, 9)) { + TrType = TT_VSDC; + } + + bool GenACGPO = arg_get_lit(ctx, 10); + + Iso7816CommandChannel channel = CC_CONTACTLESS; + if (arg_get_lit(ctx, 11)) { + channel = CC_CONTACT; + } + + PrintChannel(channel); + uint8_t psenum = (channel == CC_CONTACT) ? 1 : 2; + CLIParserFree(ctx); + + if (IfPm3Smartcard() == false) { + if (channel == CC_CONTACT) { + PrintAndLogEx(WARNING, "PM3 does not have SMARTCARD support. Exiting."); + return PM3_EDEVNOTSUPP; + } + } + + SetAPDULogging(show_apdu); + uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; @@ -813,66 +1415,6 @@ static int CmdEMVExec(const char *Cmd) { struct tlvdb *tlvRoot = NULL; struct tlv *pdol_data_tlv = NULL; - CLIParserContext *ctx; - CLIParserInit(&ctx, "emv exec", - "Executes EMV contactless transaction", - "emv exec -sat -> select card, execute MSD transaction, show APDU and TLV\n" - "emv exec -satc -> select card, execute CDA transaction, show APDU and TLV\n"); - - void *argtable[] = { - arg_param_begin, - arg_lit0("sS", "select", "Activate field and select card"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results"), - arg_lit0("jJ", "jload", "Load transaction parameters from `emv_defparams.json` file"), - arg_lit0("fF", "forceaid", "Force search AID. Search AID instead of execute PPSE"), - arg_rem("By default:", "Transaction type - MSD"), - arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip"), - arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)"), - arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standard behavior"), - arg_lit0("gG", "acgpo", "VISA. generate AC from GPO"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, true); - - bool activateField = arg_get_lit(ctx, 1); - bool showAPDU = arg_get_lit(ctx, 2); - bool decodeTLV = arg_get_lit(ctx, 3); - bool paramLoadJSON = arg_get_lit(ctx, 4); - bool forceSearch = arg_get_lit(ctx, 5); - - enum TransactionType TrType = TT_MSD; - - if (arg_get_lit(ctx, 7)) - TrType = TT_QVSDCMCHIP; - - if (arg_get_lit(ctx, 8)) - TrType = TT_CDA; - - if (arg_get_lit(ctx, 9)) - TrType = TT_VSDC; - - bool GenACGPO = arg_get_lit(ctx, 10); - - Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 11)) { - channel = CC_CONTACT; - } - - PrintChannel(channel); - uint8_t psenum = (channel == CC_CONTACT) ? 1 : 2; - CLIParserFree(ctx); - - if (!IfPm3Smartcard()) { - if (channel == CC_CONTACT) { - PrintAndLogEx(WARNING, "PM3 does not have SMARTCARD support. Exiting."); - return PM3_EDEVNOTSUPP; - } - } - - SetAPDULogging(showAPDU); - // init applets list tree const char *al = "Applets list"; tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); @@ -881,13 +1423,14 @@ static int CmdEMVExec(const char *Cmd) { // https://www.openscdp.org/scripts/tutorial/emv/applicationselection.html if (!forceSearch) { // PPSE - PrintAndLogEx(NORMAL, "\n* PPSE."); - SetAPDULogging(showAPDU); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "* PPSE."); + SetAPDULogging(show_apdu); res = EMVSearchPSE(channel, activateField, true, psenum, decodeTLV, tlvSelect); // check PPSE instead of PSE and vice versa if (res) { - PrintAndLogEx(NORMAL, "Check PPSE instead of PSE and vice versa..."); + PrintAndLogEx(INFO, "Check PPSE instead of PSE and vice versa..."); res = EMVSearchPSE(channel, false, true, psenum == 1 ? 2 : 1, decodeTLV, tlvSelect); } @@ -900,9 +1443,9 @@ static int CmdEMVExec(const char *Cmd) { // Search if (!AIDlen) { - PrintAndLogEx(NORMAL, "\n* Search AID in list."); + PrintAndLogEx(INFO, "\n* Search AID in list."); SetAPDULogging(false); - if (EMVSearch(channel, activateField, true, decodeTLV, tlvSelect)) { + if (EMVSearch(channel, activateField, true, decodeTLV, tlvSelect, false)) { dreturn(PM3_ERFTRANS); } @@ -922,8 +1465,8 @@ static int CmdEMVExec(const char *Cmd) { } // Select - PrintAndLogEx(NORMAL, "\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); - SetAPDULogging(showAPDU); + PrintAndLogEx(INFO, "\n* Selecting AID:%s", sprint_hex_inrow(AID, AIDlen)); + SetAPDULogging(show_apdu); res = EMVSelect(channel, false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { @@ -931,15 +1474,16 @@ static int CmdEMVExec(const char *Cmd) { dreturn(PM3_ERFTRANS); } - if (decodeTLV) + if (decodeTLV) { TLVPrintFromBuffer(buf, len); - PrintAndLogEx(NORMAL, "* Selected."); + } + PrintAndLogEx(INFO, "* Selected."); - PrintAndLogEx(NORMAL, "\n* Init transaction parameters."); + PrintAndLogEx(INFO, "\n* Init transaction parameters."); InitTransactionParameters(tlvRoot, paramLoadJSON, TrType, GenACGPO); TLVPrintFromTLV(tlvRoot); // TODO delete!!! - PrintAndLogEx(NORMAL, "\n* Calc PDOL."); + PrintAndLogEx(INFO, "\n* Calc PDOL."); pdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x9f38, NULL), tlvRoot, 0x83); if (!pdol_data_tlv) { PrintAndLogEx(ERR, "Error: can't create PDOL TLV."); @@ -952,9 +1496,9 @@ static int CmdEMVExec(const char *Cmd) { PrintAndLogEx(ERR, "Error: can't create PDOL data."); dreturn(PM3_ESOFT); } - PrintAndLogEx(NORMAL, "PDOL data[%zu]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); + PrintAndLogEx(INFO, "PDOL data[%zu]: %s", pdol_data_tlv_data_len, sprint_hex(pdol_data_tlv_data, pdol_data_tlv_data_len)); - PrintAndLogEx(NORMAL, "\n* GPO."); + PrintAndLogEx(INFO, "\n* GPO."); res = EMVGPO(channel, true, pdol_data_tlv_data, pdol_data_tlv_data_len, buf, sizeof(buf), &len, &sw, tlvRoot); free(pdol_data_tlv_data); @@ -977,18 +1521,19 @@ static int CmdEMVExec(const char *Cmd) { tlvdb_add(tlvRoot, pan); const struct tlv *pantlv = tlvdb_get(tlvRoot, 0x5a, NULL); - PrintAndLogEx(NORMAL, "\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len)); + PrintAndLogEx(INFO, "\n* * Extracted PAN from track2: %s", sprint_hex(pantlv->value, pantlv->len)); } else { PrintAndLogEx(WARNING, "\n* * WARNING: Can't extract PAN from track2."); } } } - PrintAndLogEx(NORMAL, "\n* Read records from AFL."); + PrintAndLogEx(INFO, "\n* Read records from AFL."); const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); - if (!AFL || !AFL->len) + if (!AFL || !AFL->len) { PrintAndLogEx(WARNING, "WARNING: AFL not found."); + } while (AFL && AFL->len) { if (AFL->len % 4) { @@ -1002,14 +1547,14 @@ static int CmdEMVExec(const char *Cmd) { uint8_t SFIend = AFL->value[i * 4 + 2]; uint8_t SFIoffline = AFL->value[i * 4 + 3]; - PrintAndLogEx(NORMAL, "* * SFI[%02x] start:%02x end:%02x offline count:%02x", SFI, SFIstart, SFIend, SFIoffline); + PrintAndLogEx(INFO, "* * SFI[%02x] start:%02x end:%02x offline count:%02x", SFI, SFIstart, SFIend, SFIoffline); if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { - PrintAndLogEx(NORMAL, "SFI ERROR! Skipped..."); + PrintAndLogEx(WARNING, "SFI ERROR! Skipped..."); continue; } for (int n = SFIstart; n <= SFIend; n++) { - PrintAndLogEx(NORMAL, "* * * SFI[%02x] %d", SFI, n); + PrintAndLogEx(INFO, "* * * SFI[%02x] %d", SFI, n); res = EMVReadRecord(channel, true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { @@ -1052,7 +1597,7 @@ static int CmdEMVExec(const char *Cmd) { if (ODAiListLen) { struct tlvdb *oda = tlvdb_fixed(0x21, ODAiListLen, ODAiList); // not a standard tag tlvdb_add(tlvRoot, oda); - PrintAndLogEx(NORMAL, "* Input list for Offline Data Authentication added to TLV. len=%zu \n", ODAiListLen); + PrintAndLogEx(INFO, "* Input list for Offline Data Authentication added to TLV. len=%zu \n", ODAiListLen); } // get AIP @@ -1060,20 +1605,20 @@ static int CmdEMVExec(const char *Cmd) { const struct tlv *AIPtlv = tlvdb_get(tlvRoot, 0x82, NULL); if (AIPtlv) { AIP = AIPtlv->value[0] + AIPtlv->value[1] * 0x100; - PrintAndLogEx(NORMAL, "* * AIP=%04x", AIP); + PrintAndLogEx(INFO, "* * AIP=%04x", AIP); } else { PrintAndLogEx(ERR, "Can't find AIP."); } // SDA if (AIP & 0x0040) { - PrintAndLogEx(NORMAL, "\n* SDA"); + PrintAndLogEx(INFO, "\n* SDA"); trSDA(tlvRoot); } // DDA if (AIP & 0x0020) { - PrintAndLogEx(NORMAL, "\n* DDA"); + PrintAndLogEx(INFO, "\n* DDA"); trDDA(channel, decodeTLV, tlvRoot); } @@ -1084,8 +1629,8 @@ static int CmdEMVExec(const char *Cmd) { // 9F26: Application Cryptogram const struct tlv *AC = tlvdb_get(tlvRoot, 0x9F26, NULL); if (AC) { - PrintAndLogEx(NORMAL, "\n--> qVSDC transaction."); - PrintAndLogEx(NORMAL, "* AC path"); + PrintAndLogEx(INFO, "\n--> qVSDC transaction."); + PrintAndLogEx(INFO, "* AC path"); // 9F36: Application Transaction Counter (ATC) const struct tlv *ATC = tlvdb_get(tlvRoot, 0x9F36, NULL); @@ -1095,33 +1640,38 @@ static int CmdEMVExec(const char *Cmd) { const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9F10, NULL); // print AC data - PrintAndLogEx(NORMAL, "ATC: %s", sprint_hex(ATC->value, ATC->len)); - PrintAndLogEx(NORMAL, "AC: %s", sprint_hex(AC->value, AC->len)); + PrintAndLogEx(INFO, "ATC: %s", sprint_hex(ATC->value, ATC->len)); + PrintAndLogEx(INFO, "AC: %s", sprint_hex(AC->value, AC->len)); if (IAD) { - PrintAndLogEx(NORMAL, "IAD: %s", sprint_hex(IAD->value, IAD->len)); + PrintAndLogEx(INFO, "IAD: %s", sprint_hex(IAD->value, IAD->len)); // https://mst-company.ru/blog/ekvajring-emv-tranzaktsiya-emv-transaction-flow-chast-4-pdol-i-beskontaktnye-karty-osobennosti-qvsdc-i-quics if (IAD->value[0] == 0x1f) { - PrintAndLogEx(NORMAL, " Key index: 0x%02x", IAD->value[2]); - PrintAndLogEx(NORMAL, " Crypto ver: 0x%02x(%03d)", IAD->value[1], IAD->value[1]); - PrintAndLogEx(NORMAL, " CVR: %s", sprint_hex(&IAD->value[3], 5)); + PrintAndLogEx(INFO, " Key index: 0x%02x", IAD->value[2]); + PrintAndLogEx(INFO, " Crypto ver: 0x%02x(%03d)", IAD->value[1], IAD->value[1]); + PrintAndLogEx(INFO, " CVR: %s", sprint_hex(&IAD->value[3], 5)); struct tlvdb *cvr = tlvdb_fixed(0x20, 5, &IAD->value[3]); TLVPrintFromTLVLev(cvr, 1); - PrintAndLogEx(NORMAL, " IDD option id: 0x%02x", IAD->value[8]); - PrintAndLogEx(NORMAL, " IDD: %s", sprint_hex(&IAD->value[9], 23)); + PrintAndLogEx(INFO, " IDD option id: 0x%02x", IAD->value[8]); + PrintAndLogEx(INFO, " IDD: %s", sprint_hex(&IAD->value[9], 23)); + } else if (IAD->len >= IAD->value[0] + 1) { - PrintAndLogEx(NORMAL, " Key index: 0x%02x", IAD->value[1]); - PrintAndLogEx(NORMAL, " Crypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); - PrintAndLogEx(NORMAL, " CVR: %s", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); + PrintAndLogEx(INFO, " Key index: 0x%02x", IAD->value[1]); + PrintAndLogEx(INFO, " Crypto ver: 0x%02x(%03d)", IAD->value[2], IAD->value[2]); + PrintAndLogEx(INFO, " CVR: %s", sprint_hex(&IAD->value[3], IAD->value[0] - 2)); struct tlvdb *cvr = tlvdb_fixed(0x20, IAD->value[0] - 2, &IAD->value[3]); TLVPrintFromTLVLev(cvr, 1); + if (IAD->len >= 8) { int iddLen = IAD->value[7]; PrintAndLogEx(NORMAL, " IDD length: %d", iddLen); - if (iddLen >= 1) + if (iddLen >= 1) { PrintAndLogEx(NORMAL, " IDD option id: 0x%02x", IAD->value[8]); - if (iddLen >= 2) + } + + if (iddLen >= 2) { PrintAndLogEx(NORMAL, " IDD: %s", sprint_hex(&IAD->value[9], iddLen - 1)); + } } } } else { @@ -1136,16 +1686,18 @@ static int CmdEMVExec(const char *Cmd) { // Mastercard M/CHIP if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD && (TrType == TT_QVSDCMCHIP || TrType == TT_CDA)) { + const struct tlv *CDOL1 = tlvdb_get(tlvRoot, 0x8c, NULL); if (CDOL1 && GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { // and m/chip transaction flag - PrintAndLogEx(NORMAL, "\n--> Mastercard M/Chip transaction."); + PrintAndLogEx(INFO, "\n--> Mastercard M/Chip transaction."); - PrintAndLogEx(NORMAL, "* * Generate challenge"); + PrintAndLogEx(INFO, "* * Generate challenge"); res = EMVGenerateChallenge(channel, true, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { PrintAndLogEx(ERR, "Error GetChallenge. APDU error %4x", sw); dreturn(PM3_ERFTRANS); } + if (len < 4) { PrintAndLogEx(ERR, "Error GetChallenge. Wrong challenge length %zu", len); dreturn(PM3_ESOFT); @@ -1155,62 +1707,63 @@ static int CmdEMVExec(const char *Cmd) { struct tlvdb *ICCDynN = tlvdb_fixed(0x9f4c, len, buf); tlvdb_add(tlvRoot, ICCDynN); if (decodeTLV) { - PrintAndLogEx(NORMAL, "\n* * ICC Dynamic Number:"); + PrintAndLogEx(INFO, "\n* * ICC Dynamic Number:"); TLVPrintFromTLV(ICCDynN); } - PrintAndLogEx(NORMAL, "* * Calc CDOL1"); + PrintAndLogEx(INFO, "* * Calc CDOL1"); struct tlv *cdol_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag if (!cdol_data_tlv) { PrintAndLogEx(ERR, "Error: can't create CDOL1 TLV."); dreturn(PM3_ESOFT); } - PrintAndLogEx(NORMAL, "CDOL1 data[%zu]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); + PrintAndLogEx(INFO, "CDOL1 data[%zu]: %s", cdol_data_tlv->len, sprint_hex(cdol_data_tlv->value, cdol_data_tlv->len)); - PrintAndLogEx(NORMAL, "* * AC1"); + PrintAndLogEx(INFO, "* * AC1"); // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD res = EMVAC(channel, true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol_data_tlv->value, cdol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); - if (res) { PrintAndLogEx(ERR, "AC1 error(%d): %4x. Exit...", res, sw); dreturn(PM3_ERFTRANS); } - if (decodeTLV) + if (decodeTLV) { TLVPrintFromBuffer(buf, len); + } // CDA - PrintAndLogEx(NORMAL, "\n* CDA:"); + PrintAndLogEx(INFO, "\n* CDA:"); struct tlvdb *ac_tlv = tlvdb_parse_multi(buf, len); if (tlvdb_get(ac_tlv, 0x9f4b, NULL)) { res = trCDA(tlvRoot, ac_tlv, pdol_data_tlv, cdol_data_tlv); if (res) { - PrintAndLogEx(NORMAL, "CDA error (%d)", res); + PrintAndLogEx(WARNING, "CDA error (%d)", res); } } else { - PrintAndLogEx(NORMAL, "\n* Signed Dynamic Application Data (0x9f4b) not present"); + PrintAndLogEx(INFO, "\n* Signed Dynamic Application Data (0x9f4b) not present"); } free(ac_tlv); free(cdol_data_tlv); - PrintAndLogEx(NORMAL, "\n* M/Chip transaction result:"); + PrintAndLogEx(INFO, "\n* M/Chip transaction result:"); // 9F27: Cryptogram Information Data (CID) const struct tlv *CID = tlvdb_get(tlvRoot, 0x9F27, NULL); if (CID) { emv_tag_dump(CID, 1); - PrintAndLogEx(NORMAL, "------------------------------"); - if (CID->len > 0) { + PrintAndLogEx(INFO, "------------------------------"); + + if (CID->len) { switch (CID->value[0] & EMVAC_AC_MASK) { case EMVAC_AAC: - PrintAndLogEx(NORMAL, "Transaction DECLINED."); + PrintAndLogEx(INFO, "Transaction DECLINED."); break; case EMVAC_TC: - PrintAndLogEx(NORMAL, "Transaction approved OFFLINE."); + PrintAndLogEx(INFO, "Transaction approved OFFLINE."); break; case EMVAC_ARQC: - PrintAndLogEx(NORMAL, "Transaction approved ONLINE."); + PrintAndLogEx(INFO, "Transaction approved ONLINE."); break; default: PrintAndLogEx(WARNING, "Warning: CID transaction code error %2x", CID->value[0] & EMVAC_AC_MASK); @@ -1228,20 +1781,20 @@ static int CmdEMVExec(const char *Cmd) { // MSD if (AIP & 0x8000 && TrType == TT_MSD) { - PrintAndLogEx(NORMAL, "\n--> MSD transaction."); + PrintAndLogEx(INFO, "\n--> MSD transaction."); - PrintAndLogEx(NORMAL, "* MSD dCVV path. Check dCVV"); + PrintAndLogEx(INFO, "* MSD dCVV path. Check dCVV"); const struct tlv *track2 = tlvdb_get(tlvRoot, 0x57, NULL); if (track2) { - PrintAndLogEx(NORMAL, "Track2: %s", sprint_hex(track2->value, track2->len)); + PrintAndLogEx(INFO, "Track2: %s", sprint_hex(track2->value, track2->len)); struct tlvdb *dCVV = GetdCVVRawFromTrack2(track2); - PrintAndLogEx(NORMAL, "dCVV raw data:"); + PrintAndLogEx(INFO, "dCVV raw data:"); TLVPrintFromTLV(dCVV); if (GetCardPSVendor(AID, AIDlen) == CV_MASTERCARD) { - PrintAndLogEx(NORMAL, "\n* Mastercard calculate UDOL"); + PrintAndLogEx(INFO, "\n* Mastercard calculate UDOL"); // UDOL (9F69) const struct tlv *UDOL = tlvdb_get(tlvRoot, 0x9F69, NULL); @@ -1251,8 +1804,9 @@ static int CmdEMVExec(const char *Cmd) { .len = 3, .value = (uint8_t *)"\x9f\x6a\x04", }; - if (!UDOL) - PrintAndLogEx(NORMAL, "Use default UDOL."); + if (!UDOL) { + PrintAndLogEx(INFO, "Use default UDOL."); + } struct tlv *udol_data_tlv = dol_process(UDOL ? UDOL : &defUDOL, tlvRoot, 0x01); // 0x01 - dummy tag if (!udol_data_tlv) { @@ -1260,9 +1814,9 @@ static int CmdEMVExec(const char *Cmd) { dreturn(PM3_ESOFT); } - PrintAndLogEx(NORMAL, "UDOL data[%zu]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len)); + PrintAndLogEx(INFO, "UDOL data[%zu]: %s", udol_data_tlv->len, sprint_hex(udol_data_tlv->value, udol_data_tlv->len)); - PrintAndLogEx(NORMAL, "\n* Mastercard compute cryptographic checksum(UDOL)"); + PrintAndLogEx(INFO, "\n* Mastercard compute cryptographic checksum(UDOL)"); res = MSCComputeCryptoChecksum(channel, true, (uint8_t *)udol_data_tlv->value, udol_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { @@ -1285,18 +1839,18 @@ static int CmdEMVExec(const char *Cmd) { // VSDC if (GetCardPSVendor(AID, AIDlen) == CV_VISA && (TrType == TT_VSDC || TrType == TT_CDA)) { - PrintAndLogEx(NORMAL, "\n--> VSDC transaction."); + PrintAndLogEx(INFO, "\n--> VSDC transaction."); - PrintAndLogEx(NORMAL, "* * Calc CDOL1"); + PrintAndLogEx(INFO, "* * Calc CDOL1"); struct tlv *cdol1_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8c, NULL), tlvRoot, 0x01); // 0x01 - dummy tag if (!cdol1_data_tlv) { PrintAndLogEx(ERR, "Error: can't create CDOL1 TLV."); dreturn(PM3_ESOFT); } - PrintAndLogEx(NORMAL, "CDOL1 data[%zu]: %s", cdol1_data_tlv->len, sprint_hex(cdol1_data_tlv->value, cdol1_data_tlv->len)); + PrintAndLogEx(INFO, "CDOL1 data[%zu]: %s", cdol1_data_tlv->len, sprint_hex(cdol1_data_tlv->value, cdol1_data_tlv->len)); - PrintAndLogEx(NORMAL, "* * AC1"); + PrintAndLogEx(INFO, "* * AC1"); // EMVAC_TC + EMVAC_CDAREQ --- to get SDAD res = EMVAC(channel, true, (TrType == TT_CDA) ? EMVAC_TC + EMVAC_CDAREQ : EMVAC_TC, (uint8_t *)cdol1_data_tlv->value, cdol1_data_tlv->len, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { @@ -1322,56 +1876,59 @@ static int CmdEMVExec(const char *Cmd) { uint8_t CryptoVersion = 0; const struct tlv *IAD = tlvdb_get(tlvRoot, 0x9f10, NULL); if (IAD && (IAD->len > 1)) { - PrintAndLogEx(NORMAL, "\n* * Issuer Application Data (IAD):"); + PrintAndLogEx(INFO, "\n* * Issuer Application Data (IAD):"); uint8_t VDDlen = IAD->value[0]; // Visa discretionary data length uint8_t IDDlen = 0; // Issuer discretionary data length - PrintAndLogEx(NORMAL, "IAD length: %zu", IAD->len); - PrintAndLogEx(NORMAL, "VDDlen: %d", VDDlen); + PrintAndLogEx(INFO, "IAD length: %zu", IAD->len); + PrintAndLogEx(INFO, "VDDlen: %d", VDDlen); + if (VDDlen < IAD->len - 1) { IDDlen = IAD->value[VDDlen + 1]; } - PrintAndLogEx(NORMAL, "IDDlen: %d", IDDlen); + PrintAndLogEx(INFO, "IDDlen: %d", IDDlen); uint8_t DerivKeyIndex = IAD->value[1]; CryptoVersion = IAD->value[2]; - PrintAndLogEx(NORMAL, "CryptoVersion: %d", CryptoVersion); - PrintAndLogEx(NORMAL, "DerivKeyIndex: %d", DerivKeyIndex); + PrintAndLogEx(INFO, "CryptoVersion: %d", CryptoVersion); + PrintAndLogEx(INFO, "DerivKeyIndex: %d", DerivKeyIndex); // Card Verification Results (CVR) decode if ((VDDlen - 2) > 0) { uint8_t CVRlen = IAD->value[3]; if (CVRlen == (VDDlen - 2 - 1)) { - PrintAndLogEx(NORMAL, "CVR length: %d", CVRlen); - PrintAndLogEx(NORMAL, "CVR: %s", sprint_hex(&IAD->value[4], CVRlen)); + PrintAndLogEx(INFO, "CVR length: %d", CVRlen); + PrintAndLogEx(INFO, "CVR: %s", sprint_hex(&IAD->value[4], CVRlen)); } else { PrintAndLogEx(WARNING, "Wrong CVR length! CVR: %s", sprint_hex(&IAD->value[3], VDDlen - 2)); } } + if (IDDlen) { - PrintAndLogEx(NORMAL, "IDD: %s", sprint_hex(&IAD->value[VDDlen + 1], IDDlen)); + PrintAndLogEx(INFO, "IDD: %s", sprint_hex(&IAD->value[VDDlen + 1], IDDlen)); } + } else { PrintAndLogEx(WARNING, "Issuer Application Data (IAD) not found."); } - PrintAndLogEx(NORMAL, "\n* * Processing online request"); + PrintAndLogEx(INFO, "\n* * Processing online request"); // authorization response code from acquirer const char HostResponse[] = "00"; // 0x3030 size_t HostResponseLen = sizeof(HostResponse) - 1; - PrintAndLogEx(NORMAL, "Host Response: `%s`", HostResponse); + PrintAndLogEx(INFO, "Host Response: `%s`", HostResponse); tlvdb_change_or_add_node(tlvRoot, 0x8a, HostResponseLen, (const unsigned char *)HostResponse); if (CryptoVersion == 10) { - PrintAndLogEx(NORMAL, "\n* * Generate ARPC"); + PrintAndLogEx(INFO, "\n* * Generate ARPC"); // Application Cryptogram (AC) const struct tlv *AC = tlvdb_get(tlvRoot, 0x9f26, NULL); if (AC && (AC->len > 0)) { - PrintAndLogEx(NORMAL, "AC: %s", sprint_hex(AC->value, AC->len)); + PrintAndLogEx(INFO, "AC: %s", sprint_hex(AC->value, AC->len)); size_t rawARPClen = AC->len; uint8_t rawARPC[rawARPClen]; @@ -1379,10 +1936,10 @@ static int CmdEMVExec(const char *Cmd) { for (int i = 0; (i < HostResponseLen) && (i < rawARPClen); i++) { rawARPC[i] ^= HostResponse[i]; } - PrintAndLogEx(NORMAL, "raw ARPC: %s", sprint_hex(rawARPC, rawARPClen)); + PrintAndLogEx(INFO, "raw ARPC: %s", sprint_hex(rawARPC, rawARPClen)); // here must be calculation of ARPC, but we don't know a bank keys. - PrintAndLogEx(NORMAL, "ARPC: n/a"); + PrintAndLogEx(INFO, "ARPC: n/a"); } else { PrintAndLogEx(WARNING, "Application Cryptogram (AC) not found."); @@ -1392,7 +1949,7 @@ static int CmdEMVExec(const char *Cmd) { // needs to send AC2 command (res == ARQC) if ((CID & EMVAC_AC_MASK) == EMVAC_ARQC) { - PrintAndLogEx(NORMAL, "\n* * Calc CDOL2"); + PrintAndLogEx(INFO, "\n* * Calc CDOL2"); struct tlv *cdol2_data_tlv = dol_process(tlvdb_get(tlvRoot, 0x8d, NULL), tlvRoot, 0x01); // 0x01 - dummy tag if (!cdol2_data_tlv) { PrintAndLogEx(ERR, "Error: can't create CDOL2 TLV."); @@ -1400,15 +1957,15 @@ static int CmdEMVExec(const char *Cmd) { dreturn(PM3_ESOFT); } - PrintAndLogEx(NORMAL, "CDOL2 data[%zu]: %s", cdol2_data_tlv->len, sprint_hex(cdol2_data_tlv->value, cdol2_data_tlv->len)); - //PrintAndLogEx(NORMAL, "* * AC2"); + PrintAndLogEx(INFO, "CDOL2 data[%zu]: %s", cdol2_data_tlv->len, sprint_hex(cdol2_data_tlv->value, cdol2_data_tlv->len)); + //PrintAndLogEx(INFO, "* * AC2"); // here must be AC2, but we don't make external authenticate ( /* // AC2 PRINT_INDENT(level); - if ((CID & EMVAC_AC2_MASK) == EMVAC_AAC2) PrintAndLogEx(NORMAL, "\tAC2: AAC (Transaction declined)"); - if ((CID & EMVAC_AC2_MASK) == EMVAC_TC2) PrintAndLogEx(NORMAL, "\tAC2: TC (Transaction approved)"); - if ((CID & EMVAC_AC2_MASK) == EMVAC_ARQC2) PrintAndLogEx(NORMAL, "\tAC2: not requested (ARQC)"); - if ((CID & EMVAC_AC2_MASK) == EMVAC_AC2_MASK) PrintAndLogEx(NORMAL, "\tAC2: RFU"); + if ((CID & EMVAC_AC2_MASK) == EMVAC_AAC2) PrintAndLogEx(INFO, "\tAC2: AAC (Transaction declined)"); + if ((CID & EMVAC_AC2_MASK) == EMVAC_TC2) PrintAndLogEx(INFO, "\tAC2: TC (Transaction approved)"); + if ((CID & EMVAC_AC2_MASK) == EMVAC_ARQC2) PrintAndLogEx(INFO, "\tAC2: not requested (ARQC)"); + if ((CID & EMVAC_AC2_MASK) == EMVAC_AC2_MASK) PrintAndLogEx(INFO, "\tAC2: RFU"); */ free(cdol2_data_tlv); } @@ -1423,20 +1980,12 @@ static int CmdEMVExec(const char *Cmd) { tlvdb_free(tlvSelect); tlvdb_free(tlvRoot); - PrintAndLogEx(NORMAL, "\n* Transaction completed."); + PrintAndLogEx(SUCCESS, "\n* Transaction completed."); + SetAPDULogging(false); return PM3_SUCCESS; } static int CmdEMVScan(const char *Cmd) { - uint8_t AID[APDU_AID_LEN] = {0}; - size_t AIDlen = 0; - uint8_t buf[APDU_RES_LEN] = {0}; - size_t len = 0; - uint8_t ODAI_list[4096]; - size_t ODAI_listlen = 0; - uint16_t sw = 0; - int res; - CLIParserContext *ctx; CLIParserInit(&ctx, "emv scan", "Scan EMV card and save it contents to a file.\n" @@ -1447,41 +1996,45 @@ static int CmdEMVScan(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("tT", "tlv", "TLV decode results"), - arg_lit0("eE", "extract", "Extract TLV elements and fill Application Data"), - arg_lit0("jJ", "jload", "Load transaction parameters from `emv_defparams.json` file"), - arg_rem("By default:", "Transaction type - MSD"), - arg_lit0("vV", "qvsdc", "Transaction type - qVSDC or M/Chip"), - arg_lit0("cC", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)"), - arg_lit0("xX", "vsdc", "Transaction type - VSDC. For test only. Not a standard behavior"), - arg_lit0("gG", "acgpo", "VISA. generate AC from GPO"), - arg_lit0("mM", "merge", "Merge output file with card's data. (warning: the file may be corrupted!)"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), - arg_str1(NULL, NULL, "", "JSON output filename"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("t", "tlv", "TLV decode results"), + arg_lit0("e", "extract", "Extract TLV elements and fill Application Data"), + arg_lit0("j", "jload", "Load transaction parameters from `emv_defparams.json` file"), + arg_rem("By default:", "Transaction type - MSD"), + arg_lit0("v", "qvsdc", "Transaction type - qVSDC or M/Chip"), + arg_lit0("c", "qvsdccda", "Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)"), + arg_lit0("x", "vsdc", "Transaction type - VSDC. For test only. Not a standard behavior"), + arg_lit0("g", "acgpo", "VISA. generate AC from GPO"), + arg_lit0("m", "merge", "Merge output file with card's data. (warning: the file may be corrupted!)"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_str1(NULL, NULL, "", "JSON output file name"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool showAPDU = arg_get_lit(ctx, 1); + bool show_apdu = arg_get_lit(ctx, 1); bool decodeTLV = arg_get_lit(ctx, 2); bool extractTLVElements = arg_get_lit(ctx, 3); bool paramLoadJSON = arg_get_lit(ctx, 4); enum TransactionType TrType = TT_MSD; - if (arg_get_lit(ctx, 6)) + if (arg_get_lit(ctx, 6)) { TrType = TT_QVSDCMCHIP; - if (arg_get_lit(ctx, 7)) + } + if (arg_get_lit(ctx, 7)) { TrType = TT_CDA; - if (arg_get_lit(ctx, 8)) + } + if (arg_get_lit(ctx, 8)) { TrType = TT_VSDC; + } bool GenACGPO = arg_get_lit(ctx, 9); bool MergeJSON = arg_get_lit(ctx, 10); Iso7816CommandChannel channel = CC_CONTACTLESS; - if (arg_get_lit(ctx, 11)) + if (arg_get_lit(ctx, 11)) { channel = CC_CONTACT; + } PrintChannel(channel); @@ -1493,14 +2046,21 @@ static int CmdEMVScan(const char *Cmd) { CLIParserFree(ctx); - if (!IfPm3Smartcard()) { + if (IfPm3Smartcard() == false) { if (channel == CC_CONTACT) { PrintAndLogEx(WARNING, "PM3 does not have SMARTCARD support, exiting"); return PM3_EDEVNOTSUPP; } } - SetAPDULogging(showAPDU); + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint8_t ODAI_list[4096]; + size_t ODAI_listlen = 0; + uint16_t sw = 0; + int res; json_t *root; json_error_t error; @@ -1522,6 +2082,8 @@ static int CmdEMVScan(const char *Cmd) { root = json_object(); } + SetAPDULogging(show_apdu); + // drop field at start DropFieldEx(channel); @@ -1587,7 +2149,7 @@ static int CmdEMVScan(const char *Cmd) { // EMV SEARCH with AID list SetAPDULogging(false); PrintAndLogEx(INFO, "AID search."); - if (EMVSearch(channel, false, true, decodeTLV, tlvSelect)) { + if (EMVSearch(channel, false, true, decodeTLV, tlvSelect, false)) { PrintAndLogEx(ERR, "Can't found any of EMV AID, exiting..."); tlvdb_free(tlvSelect); DropFieldEx(channel); @@ -1599,7 +2161,7 @@ static int CmdEMVScan(const char *Cmd) { } // EMV SELECT application - SetAPDULogging(showAPDU); + SetAPDULogging(show_apdu); EMVSelectApplication(tlvSelect, AID, &AIDlen); tlvdb_free(tlvSelect); @@ -1619,7 +2181,7 @@ static int CmdEMVScan(const char *Cmd) { // EMV SELECT applet PrintAndLogEx(INFO, "Selecting AID: " _GREEN_("%s"), sprint_hex_inrow(AID, AIDlen)); - SetAPDULogging(showAPDU); + SetAPDULogging(show_apdu); res = EMVSelect(channel, false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); if (res) { @@ -1710,10 +2272,12 @@ static int CmdEMVScan(const char *Cmd) { sfijson = json_path_get(root, "$.Application.Records"); } + if (!json_is_array(sfijson)) { PrintAndLogEx(ERR, "Internal logic error. `$.Application.Records` is not an array."); break; } + for (int i = 0; i < AFL->len / 4; i++) { uint8_t SFI = AFL->value[i * 4 + 0] >> 3; uint8_t SFIstart = AFL->value[i * 4 + 1]; @@ -1769,11 +2333,11 @@ static int CmdEMVScan(const char *Cmd) { JsonSaveHex(jsonelm, "Offline", SFIoffline, 1); struct tlvdb *rsfi = tlvdb_parse_multi(buf, len); - if (extractTLVElements) + if (extractTLVElements) { JsonSaveTLVTree(root, jsonelm, "$.Data", rsfi); - else + } else { JsonSaveTLVTreeElm(jsonelm, "$.Data", rsfi, true, true, false); - + } tlvdb_free(rsfi); } } @@ -1799,7 +2363,7 @@ static int CmdEMVScan(const char *Cmd) { tlvdb_free(tlvRoot); DropFieldEx(channel); - + SetAPDULogging(false); if (MergeJSON == false) { // create unique new name @@ -1852,15 +2416,6 @@ static int CmdEMVTest(const char *Cmd) { } static int CmdEMVRoca(const char *Cmd) { - uint8_t AID[APDU_AID_LEN] = {0}; - size_t AIDlen = 0; - uint8_t buf[APDU_RES_LEN] = {0}; - size_t len = 0; - uint16_t sw = 0; - uint8_t ODAI_list[4096]; - size_t ODAI_listlen = 0; - int res; - CLIParserContext *ctx; CLIParserInit(&ctx, "emv roca", "Tries to extract public keys and run the ROCA test against them.\n", @@ -1870,9 +2425,9 @@ static int CmdEMVRoca(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0("tT", "selftest", "Self test"), - arg_lit0("aA", "apdu", "Show APDU requests and responses"), - arg_lit0("wW", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("t", "selftest", "Self test"), + arg_lit0("a", "apdu", "Show APDU requests and responses"), + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -1891,7 +2446,7 @@ static int CmdEMVRoca(const char *Cmd) { CLIParserFree(ctx); PrintChannel(channel); - if (!IfPm3Smartcard()) { + if (IfPm3Smartcard() == false) { if (channel == CC_CONTACT) { PrintAndLogEx(WARNING, "PM3 does not have SMARTCARD support, exiting"); return PM3_EDEVNOTSUPP; @@ -1903,6 +2458,15 @@ static int CmdEMVRoca(const char *Cmd) { SetAPDULogging(show_apdu); + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + uint8_t ODAI_list[4096]; + size_t ODAI_listlen = 0; + int res; + // init applets list tree const char *al = "Applets list"; struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); @@ -1917,10 +2481,11 @@ static int CmdEMVRoca(const char *Cmd) { } else { // EMV SEARCH with AID list PrintAndLogEx(INFO, "starting AID search"); - if (EMVSearch(channel, false, true, false, tlvSelect)) { + if (EMVSearch(channel, false, true, false, tlvSelect, false)) { PrintAndLogEx(ERR, "Can't found any of EMV AID, exiting"); tlvdb_free(tlvSelect); DropFieldEx(channel); + SetAPDULogging(false); return PM3_ERFTRANS; } @@ -2116,33 +2681,246 @@ static int CmdEMVRoca(const char *Cmd) { } out: + SetAPDULogging(false); tlvdb_free(tlvRoot); DropFieldEx(channel); return ret; } +static int CmdEMVReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "emv reader", + "Act as a EMV reader to identify tag. Look for EMV tags until Enter or the pm3 button is pressed\n" + "In `verbose` mode it will also try to extract and decode the transaction logs stored on card in either channel.\n", + "emv reader\n" + "emv reader -v\n" + "emv reader -@ -> Continuous mode\n" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0("w", "wired", "Send data via contact (iso7816) interface. (def: Contactless interface)"), + arg_lit0("v", "verbose", "verbose"), + arg_lit0("@", NULL, "continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + Iso7816CommandChannel channel = CC_CONTACTLESS; + if (arg_get_lit(ctx, 1)) { + channel = CC_CONTACT; + } + + uint8_t psenum = (channel == CC_CONTACT) ? 1 : 2; + bool verbose = arg_get_lit(ctx, 2); + bool continuous = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (continuous) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + bool old_logging = GetAPDULogging(); + SetAPDULogging(verbose); + + uint8_t AID[APDU_AID_LEN] = {0}; + size_t AIDlen = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + int res = 0; + uint16_t sw = 0; + + do { + if (continuous && kbd_enter_pressed()) { + break; + } + + // init applets list tree + const char *al = "Applets"; + struct tlvdb *tlvSelect = tlvdb_fixed(1, strlen(al), (const unsigned char *)al); + + res = EMVSelectPSE(channel, true, true, 2, buf, sizeof(buf), &len, &sw); + + // search PSE / PPSE + res |= EMVSearchPSE(channel, false, true, psenum, false, tlvSelect); + if (res) { + // EMV SEARCH with AID list + DropFieldEx(channel); + if (EMVSearch(channel, true, true, false, tlvSelect, false)) { + tlvdb_free(tlvSelect); + DropFieldEx(channel); + continue; + } + } + + // select application + EMVSelectApplication(tlvSelect, AID, &AIDlen); + tlvdb_free(tlvSelect); + + if (AIDlen == 0) { + DropFieldEx(channel); + continue; + } + + // Init TLV tree + const char *alr = "Root"; + struct tlvdb *tlvRoot = tlvdb_fixed(1, strlen(alr), (const unsigned char *)alr); + + // EMV SELECT applet + res = EMVSelect(channel, false, true, AID, AIDlen, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + DropFieldEx(channel); + continue; + } + + // decode application parts + emv_parse_card_details(buf, len, verbose); + + // transaction log information + uint8_t log_file_id = 0x0B; + uint8_t log_file_records = 31; + struct tlvdb *tlogDB = NULL; + + // try getting the LOG TEMPLATE. + bool log_found = false; + bool log_template_found = false; + if (emv_extract_log_info(buf, len, &log_file_id, &log_file_records) == PM3_SUCCESS) { + log_found = true; + } + + uint16_t extra_data[] = { 0x9F36, 0x9F13, 0x9F17, 0x9F4D, 0x9F4F }; + for (int i = 0; i < ARRAYLEN(extra_data); i++) { + if (EMVGetData(channel, true, extra_data[i], buf, sizeof(buf), &len, &sw, tlvRoot)) { + continue; + } + // Log template tag + if (extra_data[i] == 0x9F4F) { + struct tlvdb *ttdb = tlvdb_find_full(tlvRoot, extra_data[i]); + const struct tlv *ttag = tlvdb_get_tlv(ttdb); + tlogDB = emv_logtemplate_parse(ttag, buf, len); + log_template_found = true; + } + } + + for (TransactionType_t tt = TT_MSD; tt < TT_END; tt++) { + + // create transaction parameters + bool gen_acgpo = false; + InitTransactionParameters(tlvRoot, false, tt, gen_acgpo); + + struct tlv *pdol_tlv = dol_process(tlvdb_get(tlvRoot, 0x9F38, NULL), tlvRoot, 0x83); + if (!pdol_tlv) { + continue; + } + + size_t pdtd_len = 0; + unsigned char *pdol_data_tlv = tlv_encode(pdol_tlv, &pdtd_len); + if (!pdol_data_tlv) { + free(pdol_tlv); + continue; + } + + res = EMVGPO(channel, true, pdol_data_tlv, pdtd_len, buf, sizeof(buf), &len, &sw, tlvRoot); + free(pdol_data_tlv); + free(pdol_tlv); + + if (res) { + continue; + } + + ProcessGPOResponseFormat1(tlvRoot, buf, len, false); + + emv_parse_card_details(buf, len, verbose); + + if (tlvdb_get(tlvRoot, 0x77, NULL)) { + break; + } + } + + const struct tlv *AFL = tlvdb_get(tlvRoot, 0x94, NULL); + if (AFL && AFL->len) { + + if (AFL->len % 4) { + continue; + } + + for (int i = 0; i < AFL->len / 4; i++) { + uint8_t SFI = AFL->value[i * 4 + 0] >> 3; + uint8_t SFIstart = AFL->value[i * 4 + 1]; + uint8_t SFIend = AFL->value[i * 4 + 2]; + + if (SFI == 0 || SFI == 31 || SFIstart == 0 || SFIstart > SFIend) { + continue; + } + + for (int n = SFIstart; n <= SFIend; n++) { + res = EMVReadRecord(channel, true, SFI, n, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + continue; + } + emv_parse_card_details(buf, len, verbose); + } + } + } + + // only check for logs file if we found 0x9F4D + if (verbose && log_found && log_template_found) { + + for (int i = 1; i <= log_file_records; i++) { + res = EMVReadRecord(channel, true, log_file_id, i, buf, sizeof(buf), &len, &sw, tlvRoot); + if (res) { + continue; + } + + if (sw == 0x6A83) { + break; + } + + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "Transaction log # " _YELLOW_("%u"), i); + PrintAndLogEx(INFO, "---------------------"); + emv_parse_log(tlogDB, buf, len); + PrintAndLogEx(INFO, ""); + } + tlvdb_free(tlogDB); + } + + // free tlv object + tlvdb_free(tlvRoot); + PrintAndLogEx(INFO, ""); + } while (continuous); + + DropFieldEx(channel); + + SetAPDULogging(old_logging); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { + {"-----------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("general") " -----------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdEMVList, AlwaysAvailable, "List ISO7816 history"}, + {"test", CmdEMVTest, AlwaysAvailable, "Crypto logic test"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("operations") " ---------------------"}, + {"challenge", CmdEMVGenerateChallenge, IfPm3Iso14443, "Generate challenge"}, {"exec", CmdEMVExec, IfPm3Iso14443, "Executes EMV contactless transaction"}, + {"genac", CmdEMVAC, IfPm3Iso14443, "Generate ApplicationCryptogram"}, + {"gpo", CmdEMVGPO, IfPm3Iso14443, "Execute GetProcessingOptions"}, + {"intauth", CmdEMVInternalAuthenticate, IfPm3Iso14443, "Internal authentication"}, {"pse", CmdEMVPPSE, IfPm3Iso14443, "Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory"}, + {"reader", CmdEMVReader, IfPm3Iso14443a, "Act like an EMV reader"}, + {"readrec", CmdEMVReadRecord, IfPm3Iso14443, "Read files from card"}, + {"roca", CmdEMVRoca, IfPm3Iso14443, "Extract public keys and run ROCA test"}, + {"scan", CmdEMVScan, IfPm3Iso14443, "Scan EMV card and save it contents to json file for emulator"}, {"search", CmdEMVSearch, IfPm3Iso14443, "Try to select all applets from applets list and print installed applets"}, {"select", CmdEMVSelect, IfPm3Iso14443, "Select applet"}, - {"gpo", CmdEMVGPO, IfPm3Iso14443, "Execute GetProcessingOptions"}, - {"readrec", CmdEMVReadRecord, IfPm3Iso14443, "Read files from card"}, - {"genac", CmdEMVAC, IfPm3Iso14443, "Generate ApplicationCryptogram"}, - {"challenge", CmdEMVGenerateChallenge, IfPm3Iso14443, "Generate challenge"}, - {"intauth", CmdEMVInternalAuthenticate, IfPm3Iso14443, "Internal authentication"}, - {"scan", CmdEMVScan, IfPm3Iso14443, "Scan EMV card and save it contents to json file for emulator"}, - {"test", CmdEMVTest, AlwaysAvailable, "Crypto logic test"}, /* + {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("simulation") " ---------------------"}, {"getrng", CmdEMVGetrng, IfPm3Iso14443, "get random number from terminal"}, {"eload", CmdEmvELoad, IfPm3Iso14443, "load EMV tag into device"}, {"dump", CmdEmvDump, IfPm3Iso14443, "dump EMV tag values"}, {"sim", CmdEmvSim, IfPm3Iso14443, "simulate EMV tag"}, {"clone", CmdEmvClone, IfPm3Iso14443, "clone an EMV tag"}, */ - {"list", CmdEMVList, AlwaysAvailable, "List ISO7816 history"}, - {"roca", CmdEMVRoca, IfPm3Iso14443, "Extract public keys and run ROCA test"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/emv/emv_pki.c b/client/src/emv/emv_pki.c index 17eb7e0ea..8f430e290 100644 --- a/client/src/emv/emv_pki.c +++ b/client/src/emv/emv_pki.c @@ -37,7 +37,7 @@ void PKISetStrictExecution(bool se) { strictExecution = se; } -static const unsigned char empty_tlv_value[] = {}; +static const unsigned char empty_tlv_value[] = {0}; static const struct tlv empty_tlv = {.tag = 0x0, .len = 0, .value = empty_tlv_value}; static size_t emv_pki_hash_psn[256] = { 0, 0, 11, 2, 17, 2, }; diff --git a/client/src/emv/emv_tags.c b/client/src/emv/emv_tags.c index 0d5ddbc31..9536ce66a 100644 --- a/client/src/emv/emv_tags.c +++ b/client/src/emv/emv_tags.c @@ -373,6 +373,7 @@ static const struct emv_tag emv_tags[] = { { 0xa5, "File Control Information (FCI) Proprietary Template", EMV_TAG_GENERIC, NULL }, { 0xbf0c, "File Control Information (FCI) Issuer Discretionary Data", EMV_TAG_GENERIC, NULL }, { 0xdf20, "Issuer Proprietary Bitmap (IPB)", EMV_TAG_GENERIC, NULL }, + { 0xdf3e, "?", EMV_TAG_BITMASK, NULL }, { 0xdf4b, "POS Cardholder Interaction Information", EMV_TAG_GENERIC, NULL }, { 0xdf60, "VISA Log Entry", EMV_TAG_GENERIC, NULL }, { 0xdf61, "DS Digest H", EMV_TAG_GENERIC, NULL }, @@ -821,9 +822,11 @@ bool emv_tag_dump(const struct tlv *tlv, int level) { emv_tag_dump_string(tlv, tag, level); break; case EMV_TAG_NUMERIC: + PrintAndLogEx(NORMAL, ""); emv_tag_dump_numeric(tlv, tag, level); break; case EMV_TAG_YYMMDD: + PrintAndLogEx(NORMAL, ""); emv_tag_dump_yymmdd(tlv, tag, level); break; case EMV_TAG_CVR: diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index 0628fcf76..b2aa524ac 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -204,7 +204,7 @@ void TLVPrintAIDlistFromSelectTLV(struct tlvdb *tlv) { break; } PrintAndLogEx(INFO, "| %s| %s | %s|", - sprint_hex_inrow_ex(tgAID->value, tgAID->len, 17), + sprint_hex_inrow_ex(tgAID->value, tgAID->len, 16), (tgPrio) ? sprint_hex(tgPrio->value, 1) : " ", (tgName) ? sprint_ascii_ex(tgName->value, tgName->len, 24) : " "); @@ -473,7 +473,7 @@ int EMVSearchPSE(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFi return res; } -int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv) { +int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv, bool verbose) { uint8_t aidbuf[APDU_AID_LEN] = {0}; int aidlen = 0; uint8_t data[APDU_RES_LEN] = {0}; @@ -500,12 +500,16 @@ int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveField if (LeaveFieldON == false) DropFieldEx(channel); - PrintAndLogEx(WARNING, "exiting..."); + if (verbose) { + PrintAndLogEx(WARNING, "exiting..."); + } return 1; } retrycnt = 0; - PrintAndLogEx(FAILED, "Retry failed [%s]. Skipped...", AIDlist[i].aid); + if (verbose) { + PrintAndLogEx(FAILED, "Retry failed [%s]. Skipped...", AIDlist[i].aid); + } } continue; } @@ -582,6 +586,10 @@ int EMVReadRecord(Iso7816CommandChannel channel, bool LeaveFieldON, uint8_t SFI, return res; } +int EMVGetData(Iso7816CommandChannel channel, bool LeaveFieldON, uint16_t foo, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { + return EMVExchangeEx(channel, false, LeaveFieldON, (sAPDU_t) {0x80, 0xCA, ((foo >> 8) & 0xFF), (foo & 0xFF), 0, NULL}, true, Result, MaxResultLen, ResultLen, sw, tlv); +} + int EMVAC(Iso7816CommandChannel channel, bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv) { return EMVExchange(channel, LeaveFieldON, (sAPDU_t) {0x80, 0xae, RefControl, 0x00, CDOLLen, CDOL}, Result, MaxResultLen, ResultLen, sw, tlv); } diff --git a/client/src/emv/emvcore.h b/client/src/emv/emvcore.h index bb76aeb2f..6ff2b6ef9 100644 --- a/client/src/emv/emvcore.h +++ b/client/src/emv/emvcore.h @@ -28,12 +28,13 @@ #include "iso7816/iso7816core.h" #include "emv_pki.h" -enum TransactionType { +typedef enum TransactionType { TT_MSD, TT_VSDC, // contact only. not standard for contactless TT_QVSDCMCHIP, TT_CDA, -}; + TT_END, +} TransactionType_t; extern const char *TransactionTypeStr[]; enum CardPSVendor { @@ -62,7 +63,7 @@ int EMVExchange(Iso7816CommandChannel channel, bool LeaveFieldON, sAPDU_t apdu, // search application int EMVSearchPSE(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, bool decodeTLV, struct tlvdb *tlv); -int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv); +int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, bool decodeTLV, struct tlvdb *tlv, bool verbose); int EMVSelectPSE(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t PSENum, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); int EMVSelect(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // select application @@ -70,6 +71,9 @@ int EMVSelectApplication(struct tlvdb *tlv, uint8_t *AID, size_t *AIDlen); // Get Processing Options int EMVGPO(Iso7816CommandChannel channel, bool LeaveFieldON, uint8_t *PDOL, size_t PDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); int EMVReadRecord(Iso7816CommandChannel channel, bool LeaveFieldON, uint8_t SFI, uint8_t SFIrec, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); + +// Emv override get data +int EMVGetData(Iso7816CommandChannel channel, bool LeaveFieldON, uint16_t foo, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); // AC int EMVGenerateChallenge(Iso7816CommandChannel channel, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); int EMVAC(Iso7816CommandChannel channel, bool LeaveFieldON, uint8_t RefControl, uint8_t *CDOL, size_t CDOLLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw, struct tlvdb *tlv); diff --git a/client/src/emv/tlv.h b/client/src/emv/tlv.h index 39f1bcacd..2582066a9 100644 --- a/client/src/emv/tlv.h +++ b/client/src/emv/tlv.h @@ -22,6 +22,7 @@ #define TLV_H #include "common.h" +#include typedef uint32_t tlv_tag_t; @@ -41,7 +42,7 @@ struct tlvdb { struct tlvdb_root { struct tlvdb db; size_t len; - unsigned char buf[0]; + unsigned char buf[]; }; typedef void (*tlv_cb)(void *data, const struct tlv *tlv, int level, bool is_leaf); diff --git a/client/src/fileutils.c b/client/src/fileutils.c index d26470ed5..c4e915509 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -27,8 +27,7 @@ #include "proxmark3.h" #include "util.h" #include "cmdhficlass.h" // pagemap -#include "protocols.h" // iclass defines -#include "cmdhftopaz.h" // TOPAZ defines +#include "iclass_cmd.h" #ifdef _WIN32 #include "scandir.h" @@ -87,6 +86,10 @@ DumpFileType_t getfiletype(const char *filename) { o = DICTIONARY; } else if (str_endswith(s, "mct")) { o = MCT; + } else if (str_endswith(s, "nfc")) { + o = FLIPPER; + } else if (str_endswith(s, "picopass")) { + o = FLIPPER; } else { // mfd, trc, trace is binary o = BIN; @@ -174,7 +177,7 @@ static char *filenamemcopy(const char *preferredName, const char *suffix) { } static size_t path_size(savePaths_t a) { - if (a == spItemCount) { + if (a >= spItemCount) { return 0; } return strlen(g_session.defaultPaths[a]); @@ -189,17 +192,11 @@ char *newfilenamemcopyEx(const char *preferredName, const char *suffix, savePath return NULL; } - uint16_t p_namelen = strlen(preferredName); - if (str_endswith(preferredName, suffix)) { - p_namelen -= strlen(suffix); - } - - int save_path_len = path_size(e_save_path); - // 1: null terminator // 16: room for filenum to ensure new filename // save_path_len + strlen(PATHSEP): the user preference save paths - const size_t len = p_namelen + strlen(suffix) + 1 + 16 + save_path_len + strlen(PATHSEP); + //const size_t len = p_namelen + strlen(suffix) + 1 + 16 + save_path_len + strlen(PATHSEP); + size_t len = FILE_PATH_SIZE; char *fileName = (char *) calloc(len, sizeof(uint8_t)); if (fileName == NULL) { @@ -209,20 +206,33 @@ char *newfilenamemcopyEx(const char *preferredName, const char *suffix, savePath char *pfn = fileName; // user preference save paths - if (save_path_len) { - snprintf(pfn, save_path_len + strlen(PATHSEP) + 1, "%s%s", g_session.defaultPaths[e_save_path], PATHSEP); + size_t save_path_len = path_size(e_save_path); + if (save_path_len && save_path_len < (FILE_PATH_SIZE - strlen(PATHSEP))) { + snprintf(pfn, len, "%s%s", g_session.defaultPaths[e_save_path], PATHSEP); pfn += save_path_len + strlen(PATHSEP); + len -= save_path_len + strlen(PATHSEP); } - int num = 1; + // remove file extension if exist in name + size_t p_namelen = strlen(preferredName); + if (str_endswith(preferredName, suffix)) { + p_namelen -= strlen(suffix); + } + + len -= strlen(suffix) + 1; + len -= p_namelen; // modify filename - snprintf(pfn, len, "%.*s%s", p_namelen, preferredName, suffix); + snprintf(pfn, len, "%.*s%s", (int)p_namelen, preferredName, suffix); + // "-001" + len -= 4; + + int num = 1; // check complete path/filename if exists while (fileExists(fileName)) { // modify filename - snprintf(pfn, len, "%.*s-%03d%s", p_namelen, preferredName, num, suffix); + snprintf(pfn, len, "%.*s-%03d%s", (int)p_namelen, preferredName, num, suffix); num++; } @@ -258,57 +268,6 @@ int saveFile(const char *preferredName, const char *suffix, const void *data, si return PM3_SUCCESS; } -// dump file -int saveFileEML(const char *preferredName, uint8_t *data, size_t datalen, size_t blocksize) { - - if (data == NULL || datalen == 0) { - return PM3_EINVARG; - } - - char *fileName = newfilenamemcopyEx(preferredName, ".eml", spDump); - if (fileName == NULL) { - return PM3_EMALLOC; - } - - int retval = PM3_SUCCESS; - int blocks = datalen / blocksize; - uint16_t currblock = 1; - - // We should have a valid filename now, e.g. dumpdata-3.bin - - // Opening file for writing in text mode - FILE *f = fopen(fileName, "w+"); - if (!f) { - PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", fileName); - retval = PM3_EFILE; - goto out; - } - - for (size_t i = 0; i < datalen; i++) { - fprintf(f, "%02X", data[i]); - - // no extra line in the end - if ((i + 1) % blocksize == 0 && currblock != blocks) { - fprintf(f, "\n"); - currblock++; - } - } - // left overs - if (datalen % blocksize != 0) { - int index = blocks * blocksize; - for (size_t j = 0; j < datalen % blocksize; j++) { - fprintf(f, "%02X", data[index + j]); - } - } - fflush(f); - fclose(f); - PrintAndLogEx(SUCCESS, "saved " _YELLOW_("%" PRId32) " blocks to text file " _YELLOW_("%s"), blocks, fileName); - -out: - free(fileName); - return retval; -} - // dump file (normally, we also got preference file, etc) int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, void (*callback)(json_t *)) { return saveFileJSONex(preferredName, ftype, data, datalen, true, callback, spDump); @@ -316,17 +275,13 @@ int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, s int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, size_t datalen, bool verbose, void (*callback)(json_t *), savePaths_t e_save_path) { if (ftype != jsfCustom) { - if (data == NULL || datalen == 0) { - return PM3_EINVARG; - } - } - - char *fileName = newfilenamemcopyEx(preferredName, ".json", e_save_path); - if (fileName == NULL) { - return PM3_EMALLOC; + if (data == NULL || datalen == 0) { + return PM3_EINVARG; + } } int retval = PM3_SUCCESS; + char path[PATH_MAX_LENGTH] = {0}; json_t *root = json_object(); JsonSaveStr(root, "Created", "proxmark3"); @@ -336,26 +291,74 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "raw", data, datalen); break; } - case jsfCardMemory: { - iso14a_mf_extdump_t *xdump = (iso14a_mf_extdump_t *)(void *) data; - JsonSaveStr(root, "FileType", "mfcard"); - JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump->card_info.uid, xdump->card_info.uidlen); - JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump->card_info.atqa, 2); - JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump->card_info.sak), 1); - for (size_t i = 0; i < (xdump->dumplen / 16); i++) { - char path[PATH_MAX_LENGTH] = {0}; + case jsfMfc_v2: { + + iso14a_mf_extdump_t xdump; + memcpy(&xdump, data, sizeof(iso14a_mf_extdump_t)); + + JsonSaveStr(root, "FileType", "mfc v2"); + JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump.card_info.uid, xdump.card_info.uidlen); + JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump.card_info.atqa, 2); + JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump.card_info.sak), 1); + for (size_t i = 0; i < (xdump.dumplen / MFBLOCK_SIZE); i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); - JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16], 16); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE], MFBLOCK_SIZE); if (mfIsSectorTrailer(i)) { snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyA", mfSectorNum(i)); - JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16], 6); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE], 6); snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyB", mfSectorNum(i)); - JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16 + 10], 6); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE + 10], 6); - uint8_t *adata = &xdump->dump[i * 16 + 6]; + uint8_t *adata = &xdump.dump[i * MFBLOCK_SIZE + 6]; snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditions", mfSectorNum(i)); - JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 16 + 6], 4); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE + 6], 4); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.block%zu", mfSectorNum(i), i - 3); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(0, adata)); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.block%zu", mfSectorNum(i), i - 2); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(1, adata)); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.block%zu", mfSectorNum(i), i - 1); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(2, adata)); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.block%zu", mfSectorNum(i), i); + JsonSaveStr(root, path, mfGetAccessConditionsDesc(3, adata)); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.UserData", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, path, &adata[3], 1); + } + } + break; + } + case jsfMfc_v3: { + + iso14a_mf_dump_ev1_t xdump; + memcpy(&xdump, data, sizeof(iso14a_mf_dump_ev1_t)); + + JsonSaveStr(root, "FileType", "mfc v3"); + JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump.card.ev1.uid, xdump.card.ev1.uidlen); + JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump.card.ev1.atqa, 2); + JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump.card.ev1.sak), 1); + JsonSaveBufAsHexCompact(root, "$.Card.ATS", xdump.card.ev1.ats, sizeof(xdump.card.ev1.ats_len)); + JsonSaveBufAsHexCompact(root, "$.Card.SIGNATURE", xdump.card.ev1.signature, sizeof(xdump.card.ev1.signature)); + + for (size_t i = 0; i < (xdump.dumplen / MFBLOCK_SIZE); i++) { + + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE], MFBLOCK_SIZE); + if (mfIsSectorTrailer(i)) { + snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyA", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE], 6); + + snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyB", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE + 10], 6); + + uint8_t *adata = &xdump.dump[i * MFBLOCK_SIZE + 6]; + snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditions", mfSectorNum(i)); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * MFBLOCK_SIZE + 6], 4); snprintf(path, sizeof(path), "$.SectorKeys.%d.AccessConditionsText.block%zu", mfSectorNum(i), i - 3); JsonSaveStr(root, path, mfGetAccessConditionsDesc(0, adata)); @@ -376,86 +379,87 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, break; } case jsfFudan: { - iso14a_mf_extdump_t *xdump = (iso14a_mf_extdump_t *)(void *) data; + iso14a_mf_extdump_t xdump; + memcpy(&xdump, data, sizeof(iso14a_mf_extdump_t)); + JsonSaveStr(root, "FileType", "fudan"); - JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump->card_info.uid, xdump->card_info.uidlen); - JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump->card_info.atqa, 2); - JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump->card_info.sak), 1); - for (size_t i = 0; i < (xdump->dumplen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; + JsonSaveBufAsHexCompact(root, "$.Card.UID", xdump.card_info.uid, xdump.card_info.uidlen); + JsonSaveBufAsHexCompact(root, "$.Card.ATQA", xdump.card_info.atqa, 2); + JsonSaveBufAsHexCompact(root, "$.Card.SAK", &(xdump.card_info.sak), 1); + for (size_t i = 0; i < (xdump.dumplen / 4); i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); - JsonSaveBufAsHexCompact(root, path, &xdump->dump[i * 4], 4); + JsonSaveBufAsHexCompact(root, path, &xdump.dump[i * 4], 4); } break; } case jsfMfuMemory: { - JsonSaveStr(root, "FileType", "mfu"); - - mfu_dump_t *tmp = (mfu_dump_t *)data; + mfu_dump_t tmp; + memcpy(&tmp, data, sizeof(mfu_dump_t)); uint8_t uid[7] = {0}; - memcpy(uid, tmp->data, 3); - memcpy(uid + 3, tmp->data + 4, 4); - - char path[PATH_MAX_LENGTH] = {0}; + memcpy(uid, tmp.data, 3); + memcpy(uid + 3, tmp.data + 4, 4); + JsonSaveStr(root, "FileType", "mfu"); JsonSaveBufAsHexCompact(root, "$.Card.UID", uid, sizeof(uid)); - JsonSaveBufAsHexCompact(root, "$.Card.Version", tmp->version, sizeof(tmp->version)); - JsonSaveBufAsHexCompact(root, "$.Card.TBO_0", tmp->tbo, sizeof(tmp->tbo)); - JsonSaveBufAsHexCompact(root, "$.Card.TBO_1", tmp->tbo1, sizeof(tmp->tbo1)); - JsonSaveBufAsHexCompact(root, "$.Card.Signature", tmp->signature, sizeof(tmp->signature)); + JsonSaveBufAsHexCompact(root, "$.Card.Version", tmp.version, sizeof(tmp.version)); + JsonSaveBufAsHexCompact(root, "$.Card.TBO_0", tmp.tbo, sizeof(tmp.tbo)); + JsonSaveBufAsHexCompact(root, "$.Card.TBO_1", tmp.tbo1, sizeof(tmp.tbo1)); + JsonSaveBufAsHexCompact(root, "$.Card.Signature", tmp.signature, sizeof(tmp.signature)); for (uint8_t i = 0; i < 3; i ++) { snprintf(path, sizeof(path), "$.Card.Counter%d", i); - JsonSaveBufAsHexCompact(root, path, tmp->counter_tearing[i], 3); + JsonSaveBufAsHexCompact(root, path, tmp.counter_tearing[i], 3); snprintf(path, sizeof(path), "$.Card.Tearing%d", i); - JsonSaveBufAsHexCompact(root, path, tmp->counter_tearing[i] + 3, 1); + JsonSaveBufAsHexCompact(root, path, tmp.counter_tearing[i] + 3, 1); } // size of header 56b - size_t len = (datalen - MFU_DUMP_PREFIX_LENGTH) / 4; + + size_t len = (datalen - MFU_DUMP_PREFIX_LENGTH) / MFU_BLOCK_SIZE; for (size_t i = 0; i < len; i++) { snprintf(path, sizeof(path), "$.blocks.%zu", i); - JsonSaveBufAsHexCompact(root, path, tmp->data + (i * 4), 4); + JsonSaveBufAsHexCompact(root, path, tmp.data + (i * MFU_BLOCK_SIZE), MFU_BLOCK_SIZE); } break; } case jsfHitag: { - JsonSaveStr(root, "FileType", "hitag"); uint8_t uid[4] = {0}; memcpy(uid, data, 4); - + JsonSaveStr(root, "FileType", "hitag"); JsonSaveBufAsHexCompact(root, "$.Card.UID", uid, sizeof(uid)); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } break; } case jsfIclass: { + + picopass_hdr_t hdr; + memcpy(&hdr, data, sizeof(picopass_hdr_t)); + JsonSaveStr(root, "FileType", "iclass"); + JsonSaveBufAsHexCompact(root, "$.Card.CSN", hdr.csn, sizeof(hdr.csn)); + JsonSaveBufAsHexCompact(root, "$.Card.Configuration", (uint8_t *)&hdr.conf, sizeof(hdr.conf)); - picopass_hdr_t *hdr = (picopass_hdr_t *)data; - JsonSaveBufAsHexCompact(root, "$.Card.CSN", hdr->csn, sizeof(hdr->csn)); - JsonSaveBufAsHexCompact(root, "$.Card.Configuration", (uint8_t *)&hdr->conf, sizeof(hdr->conf)); - - uint8_t pagemap = get_pagemap(hdr); + uint8_t pagemap = get_pagemap(&hdr); if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { - picopass_ns_hdr_t *ns_hdr = (picopass_ns_hdr_t *)data; - JsonSaveBufAsHexCompact(root, "$.Card.AIA", ns_hdr->app_issuer_area, sizeof(ns_hdr->app_issuer_area)); + picopass_ns_hdr_t ns_hdr; + memcpy(&ns_hdr, data, sizeof(picopass_ns_hdr_t)); + JsonSaveBufAsHexCompact(root, "$.Card.AIA", ns_hdr.app_issuer_area, sizeof(ns_hdr.app_issuer_area)); } else { - JsonSaveBufAsHexCompact(root, "$.Card.Epurse", hdr->epurse, sizeof(hdr->epurse)); - JsonSaveBufAsHexCompact(root, "$.Card.Kd", hdr->key_d, sizeof(hdr->key_d)); - JsonSaveBufAsHexCompact(root, "$.Card.Kc", hdr->key_c, sizeof(hdr->key_c)); - JsonSaveBufAsHexCompact(root, "$.Card.AIA", hdr->app_issuer_area, sizeof(hdr->app_issuer_area)); + JsonSaveBufAsHexCompact(root, "$.Card.Epurse", hdr.epurse, sizeof(hdr.epurse)); + JsonSaveBufAsHexCompact(root, "$.Card.Kd", hdr.key_d, sizeof(hdr.key_d)); + JsonSaveBufAsHexCompact(root, "$.Card.Kc", hdr.key_c, sizeof(hdr.key_c)); + JsonSaveBufAsHexCompact(root, "$.Card.AIA", hdr.app_issuer_area, sizeof(hdr.app_issuer_area)); } - for (size_t i = 0; i < (datalen / 8); i++) { - char path[PATH_MAX_LENGTH] = {0}; + for (size_t i = 0; i < (datalen / PICOPASS_BLOCK_SIZE); i++) { snprintf(path, sizeof(path), "$.blocks.%zu", i); - JsonSaveBufAsHexCompact(root, path, data + (i * 8), 8); + JsonSaveBufAsHexCompact(root, path, data + (i * PICOPASS_BLOCK_SIZE), PICOPASS_BLOCK_SIZE); } break; @@ -467,25 +471,49 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.ConfigBlock", conf, sizeof(conf)); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } break; } - case jsf14b: { - JsonSaveStr(root, "FileType", "14b"); - JsonSaveBufAsHexCompact(root, "raw", data, datalen); + case jsf14b_v2: { + JsonSaveStr(root, "FileType", "14b v2"); + for (size_t i = 0; i < datalen / 4; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 4], 4); + } break; } - case jsf15: { - JsonSaveStr(root, "FileType", "15693"); - JsonSaveBufAsHexCompact(root, "raw", data, datalen); + // handles ISO15693 w blocksize of 4 bytes + case jsf15_v2: { + JsonSaveStr(root, "FileType", "15693 v2"); + for (size_t i = 0; i < datalen / 4; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 4], 4); + } break; } - case jsfLegic: { - JsonSaveStr(root, "FileType", "legic"); - JsonSaveBufAsHexCompact(root, "raw", data, datalen); + // handles ISO15693 w blocksize of 8 bytes + case jsf15_v3: { + JsonSaveStr(root, "FileType", "15693 v3"); + for (size_t i = 0; i < datalen / 8; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 8], 8); + } + break; + } + case jsfLegic_v2: { + JsonSaveStr(root, "FileType", "legic v2"); + JsonSaveBufAsHexCompact(root, "$.Card.UID", data, 4); + size_t i = 0; + for (; i < datalen / 16; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 16], 16); + } + if (datalen % 16) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 16], (datalen % 16)); + } break; } case jsfT5555: { @@ -495,7 +523,6 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.ConfigBlock", conf, sizeof(conf)); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } @@ -509,7 +536,6 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.Protection2", data + (15 * 4), 4); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } @@ -522,7 +548,6 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.Config", data + (4 * 4), 4); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } @@ -536,35 +561,33 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.UID", data + (33 * 4), 4); for (size_t i = 0; i < (datalen / 4); i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, data + (i * 4), 4); } break; } case jsfMfPlusKeys: { - JsonSaveStr(root, "FileType", "mfp"); + JsonSaveStr(root, "FileType", "mfpkeys"); JsonSaveBufAsHexCompact(root, "$.Card.UID", &data[0], 7); JsonSaveBufAsHexCompact(root, "$.Card.SAK", &data[10], 1); JsonSaveBufAsHexCompact(root, "$.Card.ATQA", &data[11], 2); uint8_t atslen = data[13]; - if (atslen > 0) + if (atslen > 0) { JsonSaveBufAsHexCompact(root, "$.Card.ATS", &data[14], atslen); + } - uint8_t vdata[2][64][16 + 1] = {{{0}}}; - memcpy(vdata, &data[14 + atslen], 2 * 64 * 17); + uint8_t vdata[2][64][17] = {{{0}}}; + memcpy(vdata, data + (14 + atslen), 2 * 64 * 17); for (size_t i = 0; i < datalen; i++) { - char path[PATH_MAX_LENGTH] = {0}; - if (vdata[0][i][0]) { - snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyA", mfSectorNum(i)); - JsonSaveBufAsHexCompact(root, path, &vdata[0][i][1], 16); + snprintf(path, sizeof(path), "$.SectorKeys.%zu.KeyA", i); + JsonSaveBufAsHexCompact(root, path, &vdata[0][i][1], AES_KEY_LEN); } if (vdata[1][i][0]) { - snprintf(path, sizeof(path), "$.SectorKeys.%d.KeyB", mfSectorNum(i)); - JsonSaveBufAsHexCompact(root, path, &vdata[1][i][1], 16); + snprintf(path, sizeof(path), "$.SectorKeys.%zu.KeyB", i); + JsonSaveBufAsHexCompact(root, path, &vdata[1][i][1], AES_KEY_LEN); } } break; @@ -582,31 +605,27 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, memcpy(dvdata, &data[14 + datslen], 4 * 0xE * (24 + 1)); for (int i = 0; i < (int)datalen; i++) { - char path[PATH_MAX_LENGTH] = {0}; if (dvdata[0][i][0]) { snprintf(path, sizeof(path), "$.DES.%d.Key", i); - JsonSaveBufAsHexCompact(root, path, &dvdata[0][i][1], 8); + JsonSaveBufAsHexCompact(root, path, &dvdata[0][i][1], DES_KEY_LEN); } if (dvdata[1][i][0]) { snprintf(path, sizeof(path), "$.3DES.%d.Key", i); - JsonSaveBufAsHexCompact(root, path, &dvdata[1][i][1], 16); + JsonSaveBufAsHexCompact(root, path, &dvdata[1][i][1], T2DES_KEY_LEN); } if (dvdata[2][i][0]) { snprintf(path, sizeof(path), "$.AES.%d.Key", i); - JsonSaveBufAsHexCompact(root, path, &dvdata[2][i][1], 16); + JsonSaveBufAsHexCompact(root, path, &dvdata[2][i][1], AES_KEY_LEN); } if (dvdata[3][i][0]) { snprintf(path, sizeof(path), "$.K3KDES.%d.Key", i); - JsonSaveBufAsHexCompact(root, path, &dvdata[3][i][1], 24); + JsonSaveBufAsHexCompact(root, path, &dvdata[3][i][1], T3DES_KEY_LEN); } } break; } - case jsfFido: { - break; - } case jsfCustom: { (*callback)(root); break; @@ -617,8 +636,8 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, JsonSaveBufAsHexCompact(root, "$.Card.UID", tag->uid, sizeof(tag->uid)); JsonSaveBufAsHexCompact(root, "$.Card.H0R1", tag->HR01, sizeof(tag->HR01)); JsonSaveBufAsHexCompact(root, "$.Card.Size", (uint8_t *) & (tag->size), 2); + for (size_t i = 0; i < TOPAZ_STATIC_MEMORY / 8; i++) { - char path[PATH_MAX_LENGTH] = {0}; snprintf(path, sizeof(path), "$.blocks.%zu", i); JsonSaveBufAsHexCompact(root, path, &tag->data_blocks[i][0], TOPAZ_BLOCK_SIZE); } @@ -629,24 +648,67 @@ int saveFileJSONex(const char *preferredName, JSONFileType ftype, uint8_t *data, break; } + case jsfLto: { + JsonSaveStr(root, "FileType", "lto"); + for (size_t i = 0; i < datalen / 32; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 32], 32); + } + break; + } + case jsfCryptorf: { + JsonSaveStr(root, "FileType", "cryptorf"); + for (size_t i = 0; i < datalen / 8; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 8], 8); + } + break; + } + case jsfNDEF: { + JsonSaveStr(root, "FileType", "ndef"); + JsonSaveInt(root, "Ndef.Size", datalen); + size_t i = 0; + for (; i < datalen / 16; i++) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 16], 16); + } + if (datalen % 16) { + snprintf(path, sizeof(path), "$.blocks.%zu", i); + JsonSaveBufAsHexCompact(root, path, &data[i * 16], (datalen % 16)); + } + break; + } + // no action + case jsfFido: + break; + // depricated + case jsfCardMemory: + case jsf14b: + case jsf15: + case jsfLegic: default: break; } - int res = json_dump_file(root, fileName, JSON_INDENT(2)); - if (res) { - PrintAndLogEx(FAILED, "error: can't save the file: " _YELLOW_("%s"), fileName); + char *fn = newfilenamemcopyEx(preferredName, ".json", e_save_path); + if (fn == NULL) { + return PM3_EMALLOC; + } + + if (json_dump_file(root, fn, JSON_INDENT(2))) { + PrintAndLogEx(FAILED, "error: can't save the file: " _YELLOW_("%s"), fn); retval = 200; + free(fn); goto out; } if (verbose) { - PrintAndLogEx(SUCCESS, "saved to json file " _YELLOW_("%s"), fileName); + PrintAndLogEx(SUCCESS, "saved to json file " _YELLOW_("%s"), fn); } + free(fn); out: json_decref(root); - free(fileName); return retval; } int saveFileJSONroot(const char *preferredName, void *root, size_t flags, bool verbose) { @@ -805,7 +867,7 @@ int createMfcKeyDump(const char *preferredName, uint8_t sectorsCnt, sector_t *e_ fflush(f); fclose(f); PrintAndLogEx(SUCCESS, "Found keys have been dumped to " _YELLOW_("%s"), fileName); - PrintAndLogEx(INFO, "FYI! --> " _YELLOW_("0xFFFFFFFFFFFF") " <-- has been inserted for unknown keys where " _YELLOW_("res") " is " _RED_("0")); + PrintAndLogEx(INFO, "--[ " _YELLOW_("FFFFFFFFFFFF") " ]-- has been inserted for unknown keys where " _YELLOW_("res") " is " _RED_("0")); free(fileName); return PM3_SUCCESS; } @@ -861,7 +923,7 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, *datalen = bytes_read; if (verbose) { - PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from binary file " _YELLOW_("%s"), bytes_read, preferredName); + PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from binary file `" _YELLOW_("%s") "`", bytes_read, preferredName); } return PM3_SUCCESS; } @@ -924,7 +986,7 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { if (line[0] == '#') continue; - strcleanrn(line, sizeof(line)); + str_cleanrn(line, sizeof(line)); res = param_gethex_to_eol(line, 0, buf, sizeof(buf), &hexlen); if (res == 0) { @@ -935,7 +997,7 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { } } fclose(f); - PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from text file " _YELLOW_("%s"), counter, preferredName); + PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from text file `" _YELLOW_("%s") "`", counter, preferredName); uint8_t *newdump = realloc(*pdata, counter); @@ -952,6 +1014,239 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { return retval; } +int loadFileNFC_safe(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, nfc_df_e ft) { + + if (data == NULL) return PM3_EINVARG; + + *datalen = 0; + int retval = PM3_SUCCESS; + + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (!f) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + free(path); + + // 256 + 2 newline chars + 1 null terminator + char line[256 + 2 + 1]; + memset(line, 0, sizeof(line)); + + udata_t udata = (udata_t)data; + int n = 0; + uint32_t counter = 0; + + while (!feof(f)) { + + memset(line, 0, sizeof(line)); + + if (fgets(line, sizeof(line), f) == NULL) { + if (feof(f)) + break; + + fclose(f); + PrintAndLogEx(FAILED, "File reading error."); + return PM3_EFILE; + } + + if (line[0] == '#') + continue; + + str_cleanrn(line, sizeof(line)); + str_lower(line); + + if (str_startswith(line, "uid:")) { + if (ft == NFC_DF_MFC) { +// param_gethex_to_eol(line + 4, 0, udata.mfc->card_info.uid, sizeof(udata.mfc->card_info.uid), &n); + } + continue; + } + + if (str_startswith(line, "atqa:")) { + if (ft == NFC_DF_MFC) { +// param_gethex_to_eol(line + 5, 0, udata.mfc->card_info.atqa, sizeof(udata.mfc->card_info.atqa), &n); + } + continue; + } + + if (str_startswith(line, "sak:")) { + if (ft == NFC_DF_MFC) { + int sak = 0; + sscanf(line, "sak: %d", &sak); +// udata.mfc->card_info.sak = sak & 0xFF; + } + continue; + } + + if (str_startswith(line, "signature:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + param_gethex_to_eol(line + 11, 0, udata.mfu->signature, sizeof(udata.mfu->signature), &n); + } + continue; + } + + if (str_startswith(line, "mifare version:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + param_gethex_to_eol(line + 16, 0, udata.mfu->version, sizeof(udata.mfu->version), &n); + } + continue; + } + + if (str_startswith(line, "counter 0:")) { + int no = 0; + sscanf(line, "counter 0: %d", &no); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->counter_tearing[0][0] = no & 0xFF; + udata.mfu->counter_tearing[0][1] = no & 0xFF; + udata.mfu->counter_tearing[0][2] = no & 0xFF; + } + continue; + } + + if (str_startswith(line, "tearing 0:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + uint32_t b = 0; + sscanf(line, "tearing 0: %02x", &b); + udata.mfu->counter_tearing[0][3] = b & 0xFF; + } + continue; + } + + if (str_startswith(line, "counter 1:")) { + int no = 0; + sscanf(line, "counter 1: %d", &no); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->counter_tearing[1][0] = no & 0xFF; + udata.mfu->counter_tearing[1][1] = no & 0xFF; + udata.mfu->counter_tearing[1][2] = no & 0xFF; + } + continue; + } + + if (str_startswith(line, "tearing 1:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + uint32_t b = 0; + sscanf(line, "tearing 1: %02x", &b); + udata.mfu->counter_tearing[1][3] = b & 0xFF; + } + continue; + } + + if (str_startswith(line, "counter 2:")) { + int no = 0; + sscanf(line, "counter 2: %d", &no); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->counter_tearing[2][0] = no & 0xFF; + udata.mfu->counter_tearing[2][1] = no & 0xFF; + udata.mfu->counter_tearing[2][2] = no & 0xFF; + } + continue; + } + + if (str_startswith(line, "tearing 2:")) { + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + uint32_t b = 0; + sscanf(line, "tearing 2: %02x", &b); + udata.mfu->counter_tearing[2][3] = b & 0xFF; + } + continue; + } + + if (str_startswith(line, "pages total:")) { + sscanf(line, "pages total: %d", &n); + if (ft == NFC_DF_MFC) { + } else if (ft == NFC_DF_MFU) { + udata.mfu->pages = n; + } + continue; + } + + // Page 0: 04 10 56 CA + if (str_startswith(line, "page ")) { + int pageno = 0; + sscanf(line, "page %d:", &pageno); + + if (strcmp(line, "??") == 0) { + PrintAndLogEx(INFO, "Missing data detected in page %i, skipping...", pageno); + continue; + } + + if (((pageno * MFU_BLOCK_SIZE) + MFU_BLOCK_SIZE) >= maxdatalen) { + continue; + } + + char *p = line; + while (*p++ != ':') {}; + p++; + + if (ft == NFC_DF_MFU) { + n = 0; + param_gethex_to_eol(p, 0, udata.mfu->data + (pageno * MFU_BLOCK_SIZE), MFU_BLOCK_SIZE, &n); + *datalen += MFU_BLOCK_SIZE; + } + continue; + } + + // Block 0: 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF + if (str_startswith(line, "block ")) { + int blockno = 0; + sscanf(line, "block %d:", &blockno); + + if (strcmp(line, "??") == 0) { + PrintAndLogEx(INFO, "Missing data detected in block %i, skipping...", blockno); + continue; + } + + if (((blockno * MFBLOCK_SIZE) + MFBLOCK_SIZE) >= maxdatalen) { + continue; + } + + char *p = line; + while (*p++ != ':') {}; + p++; + + if (ft == NFC_DF_MFC) { + uint8_t block[MFBLOCK_SIZE] = {0}; + param_gethex_to_eol(p, 0, block, MFBLOCK_SIZE, &n); + memcpy(&udata.bytes[(blockno * MFBLOCK_SIZE)], block, MFBLOCK_SIZE); + counter += MFBLOCK_SIZE; + } else if (ft == NFC_DF_PICOPASS) { + uint8_t block[PICOPASS_BLOCK_SIZE] = {0}; + param_gethex_to_eol(p, 0, block, PICOPASS_BLOCK_SIZE, &n); + memcpy(&udata.bytes[(blockno * PICOPASS_BLOCK_SIZE)], block, PICOPASS_BLOCK_SIZE); + counter += PICOPASS_BLOCK_SIZE; + } + continue; + } + } + + // add header length + if (ft == NFC_DF_MFC || ft == NFC_DF_PICOPASS) { + *datalen = counter; + } else if (ft == NFC_DF_MFU) { + *datalen += MFU_DUMP_PREFIX_LENGTH; + } + + fclose(f); + PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from NFC file `" _YELLOW_("%s") "`", *datalen, preferredName); + return retval; +} + int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { char *path; int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); @@ -1011,7 +1306,7 @@ int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { if (line[0] == '+') continue; - strcleanrn(line, sizeof(line)); + str_cleanrn(line, sizeof(line)); res = param_gethex_to_eol(line, 0, buf, sizeof(buf), &hexlen); if (res == 0) { @@ -1022,7 +1317,7 @@ int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { } } fclose(f); - PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from MCT file " _YELLOW_("%s"), counter, preferredName); + PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " bytes from MCT file `" _YELLOW_("%s") "`", counter, preferredName); uint8_t *newdump = realloc(*pdata, counter); @@ -1039,6 +1334,17 @@ int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { return retval; } +static int load_file_sanity(char *s, uint32_t datalen, int i, size_t len) { + if (len == 0) { + PrintAndLogEx(WARNING, "WARNING: json %s block %d has zero-length data", s, i); + PrintAndLogEx(INFO, "file parsing stopped"); + return false; + } else if (len != datalen) { + PrintAndLogEx(WARNING, "WARNING: json %s block %d only has %zu bytes, expected %d (will fill with zero data)", s, i, len, datalen); + } + return true; +} + int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, void (*callback)(json_t *)) { return loadFileJSONex(preferredName, data, maxdatalen, datalen, true, callback); } @@ -1057,8 +1363,9 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz json_error_t error; json_t *root = json_load_file(path, 0, &error); - if (verbose) - PrintAndLogEx(SUCCESS, "loaded from JSON file " _YELLOW_("%s"), path); + if (verbose) { + PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%s"), path); + } free(path); @@ -1074,64 +1381,105 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz goto out; } - typedef union UDATA { - void *v; - uint8_t *bytes; - mfu_dump_t *mfu; - topaz_tag_t *topaz; - } UDATA; - UDATA udata = (UDATA)data; char ctype[100] = {0}; JsonLoadStr(root, "$.FileType", ctype); - if (!strcmp(ctype, "raw")) { - JsonLoadBufAsHex(root, "$.raw", udata.bytes, maxdatalen, datalen); + // Proxmark3 settings file. Nothing to do except call the callback function + if (!strcmp(ctype, "settings")) { + goto out; } - if (!strcmp(ctype, "mfcard")) { + udata_t udata = (udata_t)data; + + size_t len = 0; + char blocks[PATH_MAX_LENGTH] = {0}; + + if (!strcmp(ctype, "raw")) { + JsonLoadBufAsHex(root, "$.raw", udata.bytes, maxdatalen, datalen); + goto out; + } + + // depricated mfcard + if (!strcmp(ctype, "mfcard") || !strcmp(ctype, "mfc v2")) { size_t sptr = 0; - for (int i = 0; i < 256; i++) { - char blocks[30] = {0}; - snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); - - size_t len = 0; - uint8_t block[16]; - JsonLoadBufAsHex(root, blocks, block, 16, &len); - if (!len) - break; - - if (sptr + 16 > maxdatalen) { + // load blocks (i) from 0..N, but check sptr against total data length, not `i` + for (int i = 0; sptr < maxdatalen; i++) { + if (sptr + MFBLOCK_SIZE > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - memcpy(&udata.bytes[sptr], block, 16); - sptr += len; + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + uint8_t block[MFBLOCK_SIZE] = {0}; // ensure zero-filled when partial block of data read + JsonLoadBufAsHex(root, blocks, block, MFBLOCK_SIZE, &len); + if (load_file_sanity(ctype, MFBLOCK_SIZE, i, len) == false) { + break; + } + + memcpy(&udata.bytes[sptr], block, MFBLOCK_SIZE); + sptr += MFBLOCK_SIZE; // always increment pointer by the full block size, even if only partial data read from dump file } *datalen = sptr; + goto out; + } + + if (!strcmp(ctype, "mfc v3")) { + + JsonLoadBufAsHex(root, "$.Card.UID", udata.mfc_ev1->card.ev1.uid, udata.mfc_ev1->card.ev1.uidlen, datalen); + JsonLoadBufAsHex(root, "$.Card.ATQA", udata.mfc_ev1->card.ev1.atqa, 2, datalen); + JsonLoadBufAsHex(root, "$.Card.SAK", &(udata.mfc_ev1->card.ev1.sak), 1, datalen); + JsonLoadBufAsHex(root, "$.Card.ATS", udata.mfc_ev1->card.ev1.ats, sizeof(udata.mfc_ev1->card.ev1.ats_len), datalen); + JsonLoadBufAsHex(root, "$.Card.SIGNATURE", udata.mfc_ev1->card.ev1.signature, sizeof(udata.mfc_ev1->card.ev1.signature), datalen); + + *datalen = MFU_DUMP_PREFIX_LENGTH; + + size_t sptr = 0; + // load blocks (i) from 0..N, but check sptr against total data length, not `i` + for (int i = 0; sptr < maxdatalen; i++) { + if (sptr + MFBLOCK_SIZE > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + uint8_t block[MFBLOCK_SIZE] = {0}; // ensure zero-filled when partial block of data read + JsonLoadBufAsHex(root, blocks, block, MFBLOCK_SIZE, &len); + + if (load_file_sanity(ctype, MFBLOCK_SIZE, i, len) == false) { + break; + } + + memcpy(&udata.bytes[sptr], block, MFBLOCK_SIZE); + sptr += MFBLOCK_SIZE; // always increment pointer by the full block size, even if only partial data read from dump file + } + + *datalen = sptr; + goto out; } if (!strcmp(ctype, "fudan")) { size_t sptr = 0; - for (int i = 0; i < 256; i++) { + for (int i = 0; i < maxdatalen; i++) { if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); - - size_t len = 0; JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); - if (!len) - break; + if (load_file_sanity(ctype, 4, i, len) == false) { + break; + } sptr += len; } *datalen = sptr; + goto out; } if (!strcmp(ctype, "mfu")) { @@ -1151,17 +1499,17 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz size_t sptr = 0; for (int i = 0; i < 256; i++) { if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); - - size_t len = 0; JsonLoadBufAsHex(root, blocks, &udata.mfu->data[sptr], MFU_BLOCK_SIZE, &len); - if (!len) + + if (load_file_sanity(ctype, MFU_BLOCK_SIZE, i, len) == false) { break; + } sptr += len; udata.mfu->pages++; @@ -1170,99 +1518,169 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz --udata.mfu->pages; *datalen += sptr; + goto out; } if (!strcmp(ctype, "hitag")) { size_t sptr = 0; - for (size_t i = 0; i < (maxdatalen / 4); i++) { + for (int i = 0; i < (maxdatalen / 4); i++) { if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; - snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); - - size_t len = 0; + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); - if (!len) + if (load_file_sanity(ctype, 4, i, len) == false) { break; + } sptr += len; } *datalen = sptr; + goto out; } if (!strcmp(ctype, "iclass")) { size_t sptr = 0; - for (size_t i = 0; i < (maxdatalen / 8); i++) { - if (sptr + 8 > maxdatalen) { + for (int i = 0; i < (maxdatalen / PICOPASS_BLOCK_SIZE); i++) { + if (sptr + PICOPASS_BLOCK_SIZE > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; - snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); - - size_t len = 0; - JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 8, &len); - if (!len) + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], PICOPASS_BLOCK_SIZE, &len); + if (load_file_sanity(ctype, PICOPASS_BLOCK_SIZE, i, len) == false) { break; + } sptr += len; } *datalen = sptr; + goto out; } if (!strcmp(ctype, "t55x7")) { size_t sptr = 0; - for (size_t i = 0; i < (maxdatalen / 4); i++) { + for (int i = 0; i < (maxdatalen / 4); i++) { if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; - snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); - - size_t len = 0; + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); - if (!len) + if (load_file_sanity(ctype, 4, i, len) == false) { break; + } sptr += len; } *datalen = sptr; + goto out; } if (!strcmp(ctype, "EM4X50")) { size_t sptr = 0; - for (size_t i = 0; i < (maxdatalen / 4); i++) { + for (int i = 0; i < (maxdatalen / 4); i++) { if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; - snprintf(blocks, sizeof(blocks), "$.blocks.%zu", i); - - size_t len = 0; + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); - if (!len) + if (load_file_sanity(ctype, 4, i, len) == false) { break; + } sptr += len; } *datalen = sptr; + goto out; } + // depricated if (!strcmp(ctype, "15693")) { JsonLoadBufAsHex(root, "$.raw", udata.bytes, maxdatalen, datalen); + goto out; } + // handles ISO15693 w blocksize of 4 bytes. + if (!strcmp(ctype, "15693 v2")) { + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 4); i++) { + if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); + if (load_file_sanity(ctype, 4, i, len) == false) { + break; + } + + sptr += len; + } + + *datalen = sptr; + goto out; + } + // handles ISO15693 w blocksize of 8 bytes. + if (!strcmp(ctype, "15693 v3")) { + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 8); i++) { + if (sptr + 8 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 8, &len); + if (load_file_sanity(ctype, 8, i, len) == false) { + break; + } + sptr += len; + } + + *datalen = sptr; + goto out; + } + + if (!strcmp(ctype, "legic v2")) { + size_t sptr = 0; + for (int i = 0; i < 64; i++) { + if (sptr + 16 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 16, &len); + if (load_file_sanity(ctype, 16, i, len) == false) { + break; + } + sptr += len; + } + + *datalen = sptr; + goto out; + } + + // depricated if (!strcmp(ctype, "legic")) { JsonLoadBufAsHex(root, "$.raw", udata.bytes, maxdatalen, datalen); + goto out; } if (!strcmp(ctype, "topaz")) { @@ -1275,29 +1693,200 @@ int loadFileJSONex(const char *preferredName, void *data, size_t maxdatalen, siz for (int i = 0; i < (TOPAZ_STATIC_MEMORY / 8); i++) { if (sptr + TOPAZ_BLOCK_SIZE > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); retval = PM3_EMALLOC; goto out; } - char blocks[30] = {0}; snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); - - size_t len = 0; JsonLoadBufAsHex(root, blocks, &udata.topaz->data_blocks[sptr][0], TOPAZ_BLOCK_SIZE, &len); - if (!len) + if (load_file_sanity(ctype, TOPAZ_BLOCK_SIZE, i, len) == false) { break; + } sptr += len; - // ICEMAN todo: add dynamic memory. // uint16_z Size // uint8_t *dynamic_memory; } *datalen += sptr; + goto out; } + + if (!strcmp(ctype, "mfpkeys")) { + + JsonLoadBufAsHex(root, "$.Card.UID", udata.bytes, 7, datalen); + JsonLoadBufAsHex(root, "$.Card.SAK", udata.bytes + 10, 1, datalen); + JsonLoadBufAsHex(root, "$.Card.ATQA", udata.bytes + 11, 2, datalen); + uint8_t atslen = udata.bytes[13]; + if (atslen > 0) { + JsonLoadBufAsHex(root, "$.Card.ATS", udata.bytes + 14, atslen, datalen); + } + + size_t sptr = (14 + atslen); + + // memcpy(vdata, udata.bytes + (14 + atslen), 2 * 64 * 17); + for (int i = 0; i < 64; i++) { + + if ((sptr + (AES_KEY_LEN * 2)) > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + break; + } + + size_t offset = (14 + atslen) + (i * 2 * AES_KEY_LEN); + + snprintf(blocks, sizeof(blocks), "$.SectorKeys.%d.KeyA", i); + JsonLoadBufAsHex(root, blocks, udata.bytes + offset, AES_KEY_LEN, datalen); + + snprintf(blocks, sizeof(blocks), "$.SectorKeys.%d.KeyB", i); + JsonLoadBufAsHex(root, blocks, udata.bytes + offset + AES_KEY_LEN, AES_KEY_LEN, datalen); + + sptr += (2 * AES_KEY_LEN); + } + *datalen += sptr; + goto out; + } + + if (!strcmp(ctype, "mfdes")) { + JsonLoadBufAsHex(root, "$.Card.UID", udata.bytes, 7, datalen); + JsonLoadBufAsHex(root, "$.Card.SAK", udata.bytes + 10, 1, datalen); + JsonLoadBufAsHex(root, "$.Card.ATQA", udata.bytes + 11, 2, datalen); + uint8_t atslen = udata.bytes[13]; + if (atslen > 0) { + JsonLoadBufAsHex(root, "$.Card.ATS", udata.bytes + 14, atslen, datalen); + } + +// size_t sptr = (14 + atslen); +// uint8_t dvdata[4][0xE][24 + 1] = {{{0}}}; + + /* + for (int i = 0; i < (int)datalen; i++) { + char path[PATH_MAX_LENGTH] = {0}; + + if (dvdata[0][i][0]) { + snprintf(path, sizeof(path), "$.DES.%d.Key", i); + JsonSaveBufAsHexCompact(root, path, &dvdata[0][i][1], DES_KEY_LEN); + } + + if (dvdata[1][i][0]) { + snprintf(path, sizeof(path), "$.3DES.%d.Key", i); + JsonSaveBufAsHexCompact(root, path, &dvdata[1][i][1], T2DES_KEY_LEN); + } + if (dvdata[2][i][0]) { + snprintf(path, sizeof(path), "$.AES.%d.Key", i); + JsonSaveBufAsHexCompact(root, path, &dvdata[2][i][1], AES_KEY_LEN); + } + if (dvdata[3][i][0]) { + snprintf(path, sizeof(path), "$.K3KDES.%d.Key", i); + JsonSaveBufAsHexCompact(root, path, &dvdata[3][i][1], T3DES_KEY_LEN); + } + } + */ +// memcpy(&data[14 + atslen], dvdata, 4 * 0xE * (24 + 1)); + + goto out; + } + + if (!strcmp(ctype, "14b v2")) { + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 4); i++) { + if (sptr + 4 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 4, &len); + if (load_file_sanity(ctype, 4, i, len) == false) { + break; + } + sptr += len; + } + + *datalen = sptr; + goto out; + } + + if (!strcmp(ctype, "lto")) { + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 32); i++) { + if (sptr + 32 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 32, &len); + if (load_file_sanity(ctype, 32, i, len) == false) { + break; + } + sptr += len; + } + + *datalen = sptr; + goto out; + } + + if (!strcmp(ctype, "cryptorf")) { + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 8); i++) { + if (sptr + 8 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 8, &len); + if (load_file_sanity(ctype, 8, i, len) == false) { + break; + } + sptr += len; + } + + *datalen = sptr; + goto out; + } + + if (!strcmp(ctype, "ndef")) { + + /* + // when we will read and return extra values from NDEF json + json_error_t up_error = {0}; + int i1 = 0; + size_t ndefsize = 0; + if (json_unpack_ex(root, &up_error, 0, "{s:i}", "Ndef.Size", &i1) == 0) { + ndefsize = i1; + } + */ + + size_t sptr = 0; + for (int i = 0; i < (maxdatalen / 16); i++) { + if (sptr + 16 > maxdatalen) { + PrintAndLogEx(ERR, "loadFileJSONex: maxdatalen=%zu (%04zx) block (i)=%4d (%04x) sptr=%zu (%04zx) -- exceeded maxdatalen", maxdatalen, maxdatalen, i, i, sptr, sptr); + retval = PM3_EMALLOC; + goto out; + } + + snprintf(blocks, sizeof(blocks), "$.blocks.%d", i); + JsonLoadBufAsHex(root, blocks, &udata.bytes[sptr], 16, &len); + if (load_file_sanity(ctype, 16, i, len) == false) { + break; + } + + sptr += len; + } + + *datalen = sptr; + goto out; + } + out: + if (callback != NULL) { (*callback)(root); } @@ -1315,8 +1904,9 @@ int loadFileJSONroot(const char *preferredName, void **proot, bool verbose) { json_error_t error; json_t *root = json_load_file(path, 0, &error); - if (verbose) - PrintAndLogEx(SUCCESS, "loaded from JSON file " _YELLOW_("%s"), path); + if (verbose) { + PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%s"), path); + } free(path); @@ -1432,7 +2022,7 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale } fclose(f); if (verbose) - PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%2d") " keys from dictionary file " _YELLOW_("%s"), vkeycnt, path); + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%2d") " keys from dictionary file `" _YELLOW_("%s") "`", vkeycnt, path); if (datalen) *datalen = counter; @@ -1575,6 +2165,7 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya if (*keyb == NULL) { PrintAndLogEx(FAILED, "error, cannot allocate memory"); fclose(f); + free(*keya); return PM3_EMALLOC; } @@ -1583,7 +2174,7 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya return PM3_SUCCESS; } -mfu_df_e detect_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) { +mfu_df_e detect_mfu_dump_format(uint8_t **dump, bool verbose) { mfu_df_e retval = MFU_DF_UNKNOWN; uint8_t bcc0, bcc1; @@ -1636,6 +2227,107 @@ mfu_df_e detect_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) { return retval; } +nfc_df_e detect_nfc_dump_format(const char *preferredName, bool verbose) { + + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, "", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (!f) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + free(path); + + nfc_df_e retval = NFC_DF_UNKNOWN; + + char line[256]; + memset(line, 0, sizeof(line)); + + while (!feof(f)) { + + memset(line, 0, sizeof(line)); + + if (fgets(line, sizeof(line), f) == NULL) { + if (feof(f)) { + break; + } + + fclose(f); + PrintAndLogEx(FAILED, "File reading error."); + return PM3_EFILE; + } + + str_cleanrn(line, sizeof(line)); + str_lower(line); + + if (str_startswith(line, "device type: ntag")) { + retval = NFC_DF_MFU; + break; + } + if (str_startswith(line, "device type: mifare classic")) { + retval = NFC_DF_MFC; + break; + } + if (str_startswith(line, "device type: mifare desfire")) { + retval = NFC_DF_MFDES; + break; + } + if (str_startswith(line, "device type: iso14443-3a")) { + retval = NFC_DF_14_3A; + break; + } + if (str_startswith(line, "device type: iso14443-3b")) { + retval = NFC_DF_14_3B; + break; + } + if (str_startswith(line, "device type: iso14443-4a")) { + retval = NFC_DF_14_4A; + break; + } + if (str_startswith(line, "filetype: flipper picopass device")) { + retval = NFC_DF_PICOPASS; + break; + } + + } + fclose(f); + + if (verbose) { + switch (retval) { + case NFC_DF_MFU: + PrintAndLogEx(INFO, "detected MIFARE Ultralight / NTAG based dump format"); + break; + case NFC_DF_MFC: + PrintAndLogEx(INFO, "detected MIFARE Classic based dump format"); + break; + case NFC_DF_MFDES: + PrintAndLogEx(INFO, "detected MIFARE DESFire based dump format"); + break; + case NFC_DF_14_3A: + PrintAndLogEx(INFO, "detected ISO14443-3A based dump format. No data available"); + break; + case NFC_DF_14_3B: + PrintAndLogEx(INFO, "detected ISO14443-3B based dump format. No data available"); + break; + case NFC_DF_14_4A: + PrintAndLogEx(INFO, "detected ISO14443-4A based dump format. No data available"); + break; + case NFC_DF_PICOPASS: + PrintAndLogEx(INFO, "detected PICOPASS based dump format"); + break; + case NFC_DF_UNKNOWN: + PrintAndLogEx(WARNING, "failed to detected dump format"); + break; + } + } + return retval; +} + static int convert_plain_mfu_dump(uint8_t **dump, size_t *dumplen, bool verbose) { mfu_dump_t *mfu = (mfu_dump_t *) calloc(sizeof(mfu_dump_t), sizeof(uint8_t)); @@ -1712,7 +2404,7 @@ int convert_mfu_dump_format(uint8_t **dump, size_t *dumplen, bool verbose) { return PM3_EINVARG; } - mfu_df_e res = detect_mfu_dump_format(dump, dumplen, verbose); + mfu_df_e res = detect_mfu_dump_format(dump, verbose); switch (res) { case MFU_DF_NEWBIN: @@ -2032,21 +2724,27 @@ int searchFile(char **foundpath, const char *pm3dir, const char *searchname, con PrintAndLogEx(FAILED, "Error - can't find `" _YELLOW_("%s") "`", filename); } } - free(filename); - return res; + free(filename); + return res; } int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumplen) { - int res = 0; + int res = PM3_SUCCESS; DumpFileType_t dftype = getfiletype(fn); switch (dftype) { case BIN: { res = loadFile_safe(fn, ".bin", pdump, dumplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "File IO failed"); + } break; } case EML: { res = loadFileEML_safe(fn, pdump, dumplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "File IO failed"); + } break; } case JSON: { @@ -2056,38 +2754,98 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl return PM3_EMALLOC; } res = loadFileJSON(fn, *pdump, maxdumplen, dumplen, NULL); - if (res != PM3_SUCCESS) { - free(*pdump); + if (res == PM3_SUCCESS) { + return res; + } + + free(*pdump); + + if (res == PM3_ESOFT) { + PrintAndLogEx(WARNING, "JSON objects failed to load"); + } else if (res == PM3_EMALLOC) { + PrintAndLogEx(WARNING, "Wrong size of allocated memory. Check your parameters"); } break; } case DICTIONARY: { - PrintAndLogEx(ERR, "Error: Only BIN/EML/JSON formats allowed"); + PrintAndLogEx(ERR, "Only elf = calloc(fsize, sizeof(uint8_t)); + ctx->elf = calloc(fsize + 1, sizeof(uint8_t)); if (!ctx->elf) { PrintAndLogEx(ERR, "Error, cannot allocate memory"); res = PM3_EMALLOC; @@ -349,7 +350,8 @@ int flash_load(flash_file_t *ctx, bool force) { } if (strcmp(((char *)shstr) + shdrs[i].sh_name, ".bootphase1") == 0) { - uint32_t offset = *(uint32_t *)(ctx->elf + le32(shdrs[i].sh_offset) + le32(shdrs[i].sh_size) - 4); + uint32_t offset; + memcpy(&offset, ctx->elf + le32(shdrs[i].sh_offset) + le32(shdrs[i].sh_size) - 4, sizeof(uint32_t)); if (offset >= le32(shdrs[i].sh_addr)) { offset -= le32(shdrs[i].sh_addr); if (offset < le32(shdrs[i].sh_size)) { @@ -425,7 +427,7 @@ static int get_proxmark_state(uint32_t *state) { } // Enter the bootloader to be able to start flashing -static int enter_bootloader(char *serial_port_name) { +static int enter_bootloader(char *serial_port_name, bool wait_appear) { uint32_t state; int ret; @@ -450,12 +452,14 @@ static int enter_bootloader(char *serial_port_name) { SendCommandBL(CMD_HARDWARE_RESET, 0, 0, 0, NULL, 0); PrintAndLogEx(SUCCESS, "Press and hold down button NOW if your bootloader requires it."); } - msleep(100); + msleep(200); CloseProxmark(g_session.current_device); // Let time to OS to make the port disappear msleep(1000); - if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED)) { + if (wait_appear == false) { + return PM3_SUCCESS; + } else if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED)) { PrintAndLogEx(NORMAL, _GREEN_(" found")); return PM3_SUCCESS; } else { @@ -510,7 +514,7 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t uint32_t chipinfo = 0; int ret; - ret = enter_bootloader(serial_port_name); + ret = enter_bootloader(serial_port_name, true); if (ret != PM3_SUCCESS) return ret; @@ -599,8 +603,8 @@ int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t } // Reboot into bootloader -int flash_reboot_bootloader(char *serial_port_name) { - return enter_bootloader(serial_port_name); +int flash_reboot_bootloader(char *serial_port_name, bool wait_appear) { + return enter_bootloader(serial_port_name, wait_appear); } static int write_block(uint32_t address, uint8_t *data, uint32_t length) { diff --git a/client/src/flash.h b/client/src/flash.h index fa5694dbd..8f66a2025 100644 --- a/client/src/flash.h +++ b/client/src/flash.h @@ -44,7 +44,7 @@ typedef struct { int flash_load(flash_file_t *ctx, bool force); int flash_prepare(flash_file_t *ctx, int can_write_bl, int flash_size); int flash_start_flashing(int enable_bl_writes, char *serial_port_name, uint32_t *max_allowed); -int flash_reboot_bootloader(char *serial_port_name); +int flash_reboot_bootloader(char *serial_port_name, bool wait_appear); int flash_write(flash_file_t *ctx); void flash_free(flash_file_t *ctx); int flash_stop_flashing(void); diff --git a/client/src/graph.c b/client/src/graph.c index 851e49d5a..8b97dcfad 100644 --- a/client/src/graph.c +++ b/client/src/graph.c @@ -113,12 +113,19 @@ void setGraphBuf(const uint8_t *src, size_t size) { RepaintGraphWindow(); } +// This function assumes that the length of dest array >= g_GraphTraceLen. +// If the length of dest array is less than g_GraphTraceLen, use getFromGraphBufEx(dest, maxLen) instead. size_t getFromGraphBuf(uint8_t *dest) { + return getFromGraphBufEx(dest, g_GraphTraceLen); +} + +size_t getFromGraphBufEx(uint8_t *dest, size_t maxLen) { if (dest == NULL) return 0; if (g_GraphTraceLen == 0) return 0; size_t i; - for (i = 0; i < g_GraphTraceLen; ++i) { + maxLen = (maxLen < g_GraphTraceLen) ? maxLen : g_GraphTraceLen; + for (i = 0; i < maxLen; ++i) { //trim if (g_GraphBuffer[i] > 127) g_GraphBuffer[i] = 127; if (g_GraphBuffer[i] < -127) g_GraphBuffer[i] = -127; @@ -387,4 +394,3 @@ bool fskClocks(uint8_t *fc1, uint8_t *fc2, uint8_t *rf1, int *firstClockEdge) { } return true; } - diff --git a/client/src/graph.h b/client/src/graph.h index 3be6c9852..3a05dd8eb 100644 --- a/client/src/graph.h +++ b/client/src/graph.h @@ -31,6 +31,7 @@ bool HasGraphData(void); void setGraphBuf(const uint8_t *src, size_t size); void save_restoreGB(uint8_t saveOpt); size_t getFromGraphBuf(uint8_t *dest); +size_t getFromGraphBufEx(uint8_t *dest, size_t maxLen); void convertGraphFromBitstream(void); void convertGraphFromBitstreamEx(int hi, int low); bool isGraphBitstream(void); @@ -42,7 +43,7 @@ int GetNrzClock(const char *str, bool verbose); int GetFskClock(const char *str, bool verbose); bool fskClocks(uint8_t *fc1, uint8_t *fc2, uint8_t *rf1, int *firstClockEdge); -#define MAX_GRAPH_TRACE_LEN (40000 * 8) +#define MAX_GRAPH_TRACE_LEN (40000 * 32) #define GRAPH_SAVE 1 #define GRAPH_RESTORE 0 diff --git a/client/src/guidummy.cpp b/client/src/guidummy.cpp index e54c1d94e..2882fd41d 100644 --- a/client/src/guidummy.cpp +++ b/client/src/guidummy.cpp @@ -30,7 +30,7 @@ extern "C" void ShowGraphWindow(void) { extern "C" void HideGraphWindow(void) {} extern "C" void RepaintGraphWindow(void) {} -extern "C" void ShowPictureWindow(char *fn) { +extern "C" void ShowPictureWindow(char *fn, int len) { static int warned = 0; if (!warned) { diff --git a/client/src/imgutils.c b/client/src/imgutils.c new file mode 100644 index 000000000..4b396c0ac --- /dev/null +++ b/client/src/imgutils.c @@ -0,0 +1,206 @@ + +#include +#include "imgutils.h" + +struct ycbcr_t { + int y; + int cb; + int cr; +}; + +static void rgb_to_ycbcr(int rgb, struct ycbcr_t * ycbcr) { + int r = gdTrueColorGetRed(rgb); + int g = gdTrueColorGetGreen(rgb); + int b = gdTrueColorGetBlue(rgb); + + /* + * Below is a fixed-point version of the following code: + * ycbcr->y = r * 0.29900 + g * 0.58700 + b * 0.11400; + * ycbcr->cb = r * -0.16874 + g * -0.33126 + b * 0.50000 + 128; + * ycbcr->cr = r * 0.50000 + g * -0.41869 + b * -0.08131 + 128; + */ + + ycbcr->y = (r * 19595 + g * 38470 + b * 7471) / 65536; + ycbcr->cb = (r * -11059 + g * -21709 + b * 32768) / 65536 + 128; + ycbcr->cr = (r * 32768 + g * -27439 + b * -5329) / 65536 + 128; +} + +static inline void cap_comp(int * x) { + if (*x < 0) { + *x = 0; + } else if (*x > 255) { + *x = 255; + } +} + +gdImagePtr img_palettize(gdImagePtr rgb, int * palette, int palette_size) { + assert(rgb != NULL); + assert(palette != NULL); + assert(palette_size >= 2 && palette_size < 256); + + // Create paletized image + gdImagePtr res = gdImageCreate(gdImageSX(rgb), gdImageSY(rgb)); + if (!res) { + return NULL; + } + + // Allocate space for palette in YCbCr + struct ycbcr_t * pal_ycbcr = calloc(palette_size, sizeof(struct ycbcr_t)); + if (!pal_ycbcr) { + gdImageDestroy(res); + return NULL; + } + + /* + * Initialize the column's error array. + * + * Note that we are storing two extra values so we don't have to do boundary checking at + * the left and right edges of the image. + * + * To reduce shifts and increase accuracy, each entry is stored with 16x times the error, + * and gets divided by that amount when it is read. + */ + struct ycbcr_t * forward = calloc(gdImageSX(rgb) + 2, sizeof(struct ycbcr_t)); + if (!forward) { + free(pal_ycbcr); + gdImageDestroy(res); + return NULL; + } + + // Convert palette to YCbCr and allocate in image + for (int i = 0; i < palette_size; i++) { + int c = palette[i]; + rgb_to_ycbcr(c, pal_ycbcr + i); + gdImageColorAllocate(res, gdTrueColorGetRed(c), gdTrueColorGetGreen(c), gdTrueColorGetBlue(c)); + } + + for (int y = 0; y < gdImageSY(rgb); y++) { + // Load current row error and reset its storage + struct ycbcr_t row_err = forward[1]; + forward[1].y = forward[1].cb = forward[1].cr = 0; + + for (int x = 0; x < gdImageSX(rgb); x++) { + struct ycbcr_t pix; + rgb_to_ycbcr(gdImageGetTrueColorPixel(rgb, x, y), &pix); + + // Add error for current pixel + pix.y += row_err.y / 16; + pix.cb += row_err.cb / 16; + pix.cr += row_err.cr / 16; + + // Cap in case it went to imaginary color territory + cap_comp(&pix.y); + cap_comp(&pix.cb); + cap_comp(&pix.cr); + + /* + * Iterate through all candidate colors and find the nearest one using the + * squared Euclidean distance. + */ + int best_idx = 0; + struct ycbcr_t best_err = { 0 }; + int best_score = 0x7FFFFFFF; + for (int can_idx = 0; can_idx < palette_size; can_idx++) { + struct ycbcr_t can_err = { + .y = pix.y - pal_ycbcr[can_idx].y, + .cb = pix.cb - pal_ycbcr[can_idx].cb, + .cr = pix.cr - pal_ycbcr[can_idx].cr, + }; + + int can_score = ( + can_err.y * can_err.y + + can_err.cb * can_err.cb + + can_err.cr * can_err.cr + ); + + if (can_score < best_score) { + best_idx = can_idx; + best_score = can_score; + best_err = can_err; + } + } + + // Set current pixel + gdImageSetPixel(res, x, y, best_idx); + + // Propagate error within the current row, to the pixel to the right + row_err.y = best_err.y * 7 + forward[x + 2].y; + row_err.cb = best_err.cb * 7 + forward[x + 2].cb; + row_err.cr = best_err.cr * 7 + forward[x + 2].cr; + + // Add error to bottom left + forward[x + 0].y += best_err.y * 3; + forward[x + 0].cb += best_err.cb * 3; + forward[x + 0].cr += best_err.cr * 3; + + // Add error to bottom center + forward[x + 1].y += best_err.y * 5; + forward[x + 1].cb += best_err.cb * 5; + forward[x + 1].cr += best_err.cr * 5; + + // Set error to bottom right + forward[x + 2].y = best_err.y * 1; + forward[x + 2].cb = best_err.cb * 1; + forward[x + 2].cr = best_err.cr * 1; + } + } + + free(forward); + free(pal_ycbcr); + return res; +} + +gdImagePtr img_crop_to_fit(gdImagePtr orig, int width, int height) { + assert(orig != NULL); + assert(width >= 1); + assert(height >= 1); + + gdImagePtr res; + if (gdImageTrueColor(orig)) { + res = gdImageCreateTrueColor(width, height); + } else { + res = gdImageCreate(width, height); + } + + if (!res) { + return NULL; + } + + if (gdImageSY(orig) * width <= gdImageSX(orig) * height) { + // Image is wider than expected, so we will crop the left and right sides + + int crop_width = gdImageSY(orig) * width / height; + int crop_sx = gdImageSX(orig) / 2 - crop_width / 2; + gdImageCopyResampled( + res, // Dest img + orig, // Src image + 0, // Dest X + 0, // Dest Y + crop_sx, // Src X + 0, // Src Y + width, // Dest width + height, // Dest height + crop_width, // Src width + gdImageSY(orig) // Src height + ); + } else { + // Image is taller than expected, so we will crop the top and bottom sides + + int crop_height = gdImageSX(orig) * height / width; + int crop_sy = gdImageSY(orig) / 2 - crop_height / 2; + gdImageCopyResampled( + res, // Dest img + orig, // Src image + 0, // Dest X + 0, // Dest Y + 0, // Src X + crop_sy, // Src Y + width, // Dest width + height, // Dest height + gdImageSX(orig), // Src width + crop_height // Src height + ); + } + + return res; +} diff --git a/client/src/imgutils.h b/client/src/imgutils.h new file mode 100644 index 000000000..ff619a5e1 --- /dev/null +++ b/client/src/imgutils.h @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Image utilities +//----------------------------------------------------------------------------- +#ifndef IMGUTILS_H__ +#define IMGUTILS_H__ + +#include + +/* + * Converts a true color image to a palette image, using Floyd-Steinberg dithering. + * + * For color matching, this function uses the Euclidean distance between colors in the + * YCbCr color space, which yields to better results than using sRGB directly. + * + * A comparison can be found at https://twitter.com/Socram4x8/status/1733157380097995205/photo/1. + */ +gdImagePtr img_palettize(gdImagePtr rgb, int * palette, int palette_size); + +/* + * This function scales and crops the image to the given size. + * Think of "background-size: cover" in CSS. + */ +gdImagePtr img_crop_to_fit(gdImagePtr orig, int width, int height); + +#endif diff --git a/client/src/iso4217.c b/client/src/iso4217.c new file mode 100644 index 000000000..a385f69ec --- /dev/null +++ b/client/src/iso4217.c @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// ISO4217 Currency information lookup +//----------------------------------------------------------------------------- +#include "iso4217.h" +#include +#include +#include "commonutil.h" // ARRAYLEN +#include "ui.h" // PrintAndLogEx + +// get a Currency description based on the currency number string +const char *getCurrencyInfo(const char *cn_str) { + size_t slen = strlen(cn_str); + + // skip last element of AtrTable + for (int i = 0; i < ARRAYLEN(Iso4217Table) - 1; ++i) { + if (strncmp(cn_str, Iso4217Table[i].code, slen) == 0) + return Iso4217Table[i].desc; + } + return Iso4217Table[ARRAYLEN(Iso4217Table) - 1].desc; +} + diff --git a/client/src/iso4217.h b/client/src/iso4217.h new file mode 100644 index 000000000..594a0bb38 --- /dev/null +++ b/client/src/iso4217.h @@ -0,0 +1,222 @@ +//----------------------------------------------------------------------------- +// Borrowed initially from +// https://github.com/L1L1/cardpeek/blob/master/dot_cardpeek_dir/scripts/lib/currency_codes.lua +// +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +#ifndef ISO4217_H__ + +#define ISO4217_H__ +#include + +typedef struct iso4217_s { + const char *code; + const char *desc; + int digits; +} iso4217_t; + +const char *getCurrencyInfo(const char *cn_str); + +// atr_t array is expected to be NULL terminated +const static iso4217_t Iso4217Table[] = { + {"008", "Lek", 2}, + {"012", "Algerian Dinar", 2}, + {"032", "Argentine Peso", 2}, + {"036", "Australian Dollar", 2}, + {"044", "Bahamian Dollar", 2}, + {"048", "Bahraini Dinar", 3}, + {"050", "Taka", 2}, + {"051", "Armenian Dram", 2}, + {"052", "Barbados Dollar", 2}, + {"060", "Bermudian Dollar", 2}, + {"064", "Ngultrum", 2}, + {"068", "Boliviano", 2}, + {"072", "Pula", 2}, + {"084", "Belize Dollar", 2}, + {"090", "Solomon Islands Dollar", 2}, + {"096", "Brunei Dollar", 2}, + {"104", "Kyat", 2}, + {"108", "Burundi Franc", 0}, + {"116", "Riel", 2}, + {"124", "Canadian Dollar", 2}, + {"132", "Cabo Verde Escudo", 2}, + {"136", "Cayman Islands Dollar", 2}, + {"144", "Sri Lanka Rupee", 2}, + {"152", "Chilean Peso", 0}, + {"156", "Yuan Renminbi", 2}, + {"170", "Colombian Peso", 2}, + {"174", "Comoro Franc", 0}, + {"188", "Costa Rican Colon", 2}, + {"191", "Croatian Kuna", 2}, + {"192", "Cuban Peso", 2}, + {"203", "Czech Koruna", 2}, + {"208", "Danish Krone", 2}, + {"214", "Dominican Peso", 2}, + {"222", "El Salvador Colon", 2}, + {"230", "Ethiopian Birr", 2}, + {"232", "Nakfa", 2}, + {"233", "Kroon", 2}, + {"238", "Falkland Islands Pound", 2}, + {"242", "Fiji Dollar", 2}, + {"262", "Djibouti Franc", 0}, + {"270", "Dalasi", 2}, + {"292", "Gibraltar Pound", 2}, + {"320", "Quetzal", 2}, + {"324", "Guinea Franc", 0}, + {"328", "Guyana Dollar", 2}, + {"332", "Gourde", 2}, + {"340", "Lempira", 2}, + {"344", "Hong Kong Dollar", 2}, + {"348", "Forint", 2}, + {"352", "Iceland Krona", 0}, + {"356", "Indian Rupee", 2}, + {"360", "Rupiah", 2}, + {"364", "Iranian Rial", 2}, + {"368", "Iraqi Dinar", 3}, + {"376", "New Israeli Sheqel", 2}, + {"388", "Jamaican Dollar", 2}, + {"392", "Yen", 0}, + {"398", "Tenge", 2}, + {"400", "Jordanian Dinar", 3}, + {"404", "Kenyan Shilling", 2}, + {"408", "North Korean Won", 2}, + {"410", "Won", 0}, + {"414", "Kuwaiti Dinar", 3}, + {"417", "Som", 2}, + {"418", "Kip", 2}, + {"422", "Lebanese Pound", 2}, + {"426", "Loti", 2}, + {"428", "Latvian Lats", 2}, + {"430", "Liberian Dollar", 2}, + {"434", "Libyan Dinar", 3}, + {"440", "Lithuanian Litas", 2}, + {"446", "Pataca", 2}, + {"454", "Kwacha", 2}, + {"458", "Malaysian Ringgit", 2}, + {"462", "Rufiyaa", 2}, + {"478", "Ouguiya", 2}, + {"480", "Mauritius Rupee", 2}, + {"484", "Mexican Peso", 2}, + {"496", "Tugrik", 2}, + {"498", "Moldovan Leu", 2}, + {"504", "Moroccan Dirham", 2}, + {"512", "Rial Omani", 3}, + {"516", "Namibia Dollar", 2}, + {"524", "Nepalese Rupee", 2}, + {"532", "Netherlands Antillean Guilder", 2}, + {"533", "Aruban Florin", 2}, + {"548", "Vatu", 0}, + {"554", "New Zealand Dollar", 2}, + {"558", "Cordoba Oro", 2}, + {"566", "Naira", 2}, + {"578", "Norwegian Krone", 2}, + {"586", "Pakistan Rupee", 2}, + {"590", "Balboa", 2}, + {"598", "Kina", 2}, + {"600", "Guarani", 0}, + {"604", "Nuevo Sol", 2}, + {"608", "Philippine Peso", 2}, + {"624", "Guinea-Bissau Peso", -1}, + {"634", "Qatari Rial", 2}, + {"643", "Russian Ruble", 2}, + {"646", "Rwanda Franc", 0}, + {"654", "Saint Helena Pound", 2}, + {"678", "Dobra", 2}, + {"682", "Saudi Riyal", 2}, + {"690", "Seychelles Rupee", 2}, + {"694", "Leone", 2}, + {"702", "Singapore Dollar", 2}, + {"704", "Dong", 0}, + {"706", "Somali Shilling", 2}, + {"710", "Rand", 2}, + {"728", "South Sudanese Pound", 2}, + {"748", "Lilangeni", 2}, + {"752", "Swedish Krona", 2}, + {"756", "Swiss Franc", 2}, + {"760", "Syrian Pound", 2}, + {"764", "Baht", 2}, + {"776", "Pa\'anga", 2}, + {"780", "Trinidad and Tobago Dollar", 2}, + {"784", "UAE Dirham", 2}, + {"788", "Tunisian Dinar", 3}, + {"800", "Uganda Shilling", 0}, + {"807", "Denar", 2}, + {"818", "Egyptian Pound", 2}, + {"826", "Pound Sterling", 2}, + {"834", "Tanzanian Shilling", 2}, + {"840", "US Dollar", 2}, + {"858", "Peso Uruguayo", 2}, + {"860", "Uzbekistan Sum", 2}, + {"882", "Tala", 2}, + {"886", "Yemeni Rial", 2}, + {"894", "Zambian Kwacha", 2}, + {"901", "New Taiwan Dollar", 2}, + {"931", "Peso Convertible", 2}, + {"932", "Zimbabwe Dollar", 2}, + {"934", "Turkmenistan New Manat", 2}, + {"936", "Ghana Cedi", 2}, + {"937", "Bolivar", 2}, + {"938", "Sudanese Pound", 2}, + {"940", "Uruguay Peso en Unidades Indexadas (URUIURUI)", 0}, + {"941", "Serbian Dinar", 2}, + {"943", "Mozambique Metical", 2}, + {"944", "Azerbaijanian Manat", 2}, + {"946", "New Romanian Leu", 2}, + {"947", "WIR Euro", 2}, + {"948", "WIR Franc", 2}, + {"949", "Turkish Lira", 2}, + {"950", "CFA Franc BEAC", 0}, + {"951", "East Caribbean Dollar", 2}, + {"952", "CFA Franc BCEAO", 0}, + {"953", "CFP Franc", 0}, + {"955", "Bond Markets Unit European Composite Unit (EURCO)", 0}, + {"956", "Bond Markets Unit European Monetary Unit (E.M.U.-6)", 0}, + {"957", "Bond Markets Unit European Unit of Account 9 (E.U.A.-9)", 0}, + {"958", "Bond Markets Unit European Unit of Account 17 (E.U.A.-17)", 0}, + {"959", "Gold", 0}, + {"960", "SDR (Special Drawing Right)", 0}, + {"961", "Silver", 0}, + {"962", "Platinum", 0}, + {"963", "Codes specifically reserved for testing purposes", -1}, + {"964", "Palladium", -1}, + {"965", "ADB Unit of Account", -1}, + {"967", "Zambian Kwacha", 2}, + {"968", "Surinam Dollar", 2}, + {"969", "Malagasy Ariary", 1}, + {"969", "Malagasy Ariary", 2}, + {"970", "Unidad de Valor Real", 2}, + {"971", "Afghani", 2}, + {"972", "Somoni", 2}, + {"973", "Kwanza", 2}, + {"974", "Belarussian Ruble", 0}, + {"975", "Bulgarian Lev", 2}, + {"976", "Congolese Franc", 2}, + {"977", "Convertible Mark", 2}, + {"978", "Euro", 2}, + {"979", "Mexican Unidad de Inversion (UDI)", 2}, + {"980", "Hryvnia", 2}, + {"981", "Lari", 2}, + {"984", "Mvdol", 2}, + {"985", "Zloty", 2}, + {"986", "Brazilian Real", 2}, + {"990", "Unidad de Fomento", 4}, + {"994", "Sucre", -1}, + {"997", "US Dollar (Next day)", 2}, + {"998", "US Dollar (Same day)", 2}, + {"999", "No currency is involved", -1}, + {"NULL", "N/A", -1} +}; + +#endif diff --git a/client/src/iso7816/iso7816core.c b/client/src/iso7816/iso7816core.c index 7e0dd2c7f..71f2a8e49 100644 --- a/client/src/iso7816/iso7816core.c +++ b/client/src/iso7816/iso7816core.c @@ -133,6 +133,11 @@ int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool l res = ExchangeAPDU14a(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len); if (res != PM3_SUCCESS) { res = exchange_14b_apdu(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len, 4000); + if (res == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Testing ISO14443-B... ( " _GREEN_("ok") " )"); + } else { + PrintAndLogEx(INFO, "Testing ISO14443-B... ( " _RED_("fail") " )"); + } } break; } @@ -148,7 +153,7 @@ int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool l res = ExchangeAPDUSC(false, data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len); } - if (res) { + if (res != PM3_SUCCESS) { return res; } break; diff --git a/client/src/ksx6924/ksx6924core.c b/client/src/ksx6924/ksx6924core.c index 307573f74..fa943eeba 100644 --- a/client/src/ksx6924/ksx6924core.c +++ b/client/src/ksx6924/ksx6924core.c @@ -108,20 +108,20 @@ typedef struct { return r->value; \ } -MAKE_ENUM_TYPE(uint8_t); +MAKE_ENUM_TYPE(uint8_t) // KSX6924LookupCardType MAKE_ENUM_CONST(CardType, uint8_t, { 0x00, "Pre-paid" }, { 0x10, "Post-pay" }, { 0x20, "Mobile post-pay" }, - ); + ) // KSX6924LookupAlg MAKE_ENUM_CONST(Alg, uint8_t, { 0x00, "SEED" }, { 0x10, "3DES" }, - ); + ) // KSX6924LookupTMoneyIDCenter MAKE_ENUM_CONST(TMoneyIDCenter, uint8_t, @@ -139,20 +139,17 @@ MAKE_ENUM_CONST(TMoneyIDCenter, uint8_t, { 0x0b, "EB Card Corporation" }, { 0x0c, "Seoul Bus Transport Association" }, { 0x0d, "Cardnet" }, - ); + ) // KSX6924LookupTMoneyUserCode MAKE_ENUM_CONST(TMoneyUserCode, uint8_t, { 0x01, "Regular/normal" }, { 0x02, "Child" }, - { 0x04, "Youth" }, - { 0x06, "elderly" }, - { 0x0f, "Test" }, { 0xff, "Inactive" }, - ); + ) // KSX6924LookupTMoneyDisRate MAKE_ENUM_CONST(TMoneyDisRate, uint8_t, @@ -163,7 +160,7 @@ MAKE_ENUM_CONST(TMoneyDisRate, uint8_t, { 0x20, "Merit, basic" }, { 0x21, "Merit, companion" }, - ); + ) // KSX6924LookupTMoneyTCode MAKE_ENUM_CONST(TMoneyTCode, uint8_t, @@ -171,7 +168,7 @@ MAKE_ENUM_CONST(TMoneyTCode, uint8_t, { 0x01, "SK Telecom" }, { 0x02, "Korea Telecom" }, { 0x03, "LG Uplus" }, - ); + ) // KSX6924LookupTMoneyCCode MAKE_ENUM_CONST(TMoneyCCode, uint8_t, @@ -187,7 +184,7 @@ MAKE_ENUM_CONST(TMoneyCCode, uint8_t, { 0x09, "Woori Card" }, { 0x0a, "Hana SK Card" }, { 0x0b, "Hyundai Card" }, - ); + ) static const char *KSX6924_UNKNOWN = "Unknown"; @@ -320,7 +317,7 @@ bool KSX6924ParsePurseInfo(const uint8_t *purseInfo, size_t purseLen, struct ksx // TODO return true; -}; +} /** * Prints out a ksx6924_purse_info @@ -523,7 +520,7 @@ bool KSX6924ParseInitializeCardResponse(const uint8_t *initCardResponse, size_t // TODO return true; -}; +} /** * Prints out a Initialize Card response diff --git a/client/src/loclass/cipherutils.h b/client/src/loclass/cipherutils.h index 160945f0d..f3dc9d5f3 100644 --- a/client/src/loclass/cipherutils.h +++ b/client/src/loclass/cipherutils.h @@ -63,7 +63,7 @@ void push6bits(BitstreamOut_t *stream, uint8_t bits); void EncryptDES(bool key[56], bool outBlk[64], bool inBlk[64], int verbose) ; void x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest); uint64_t x_bytes_to_num(uint8_t *src, size_t len); -uint8_t reversebytes(uint8_t b); +uint8_t reversebyte(uint8_t b); void reverse_arraybytes(uint8_t *arr, size_t len); void reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len); void printarr(const char *name, uint8_t *arr, int len); diff --git a/client/src/loclass/elite_crack.c b/client/src/loclass/elite_crack.c index 5d2be50e6..c5fd2c534 100644 --- a/client/src/loclass/elite_crack.c +++ b/client/src/loclass/elite_crack.c @@ -365,7 +365,7 @@ static void *bf_thread(void *thread_arg) { // success if (memcmp(calculated_MAC, mac, 4) == 0) { - loclass_thread_ret_t *r = (loclass_thread_ret_t *)malloc(sizeof(loclass_thread_ret_t)); + loclass_thread_ret_t *r = (loclass_thread_ret_t *)calloc(sizeof(loclass_thread_ret_t), sizeof(uint8_t)); for (uint8_t i = 0 ; i < numbytes_to_recover; i++) { r->values[i] = keytable[bytes_to_recover[i]] & 0xFF; diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 9c3326f83..077414c86 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -43,6 +43,7 @@ #include "mifare/mad.h" #include "mifare/aiddesfire.h" + const CLIParserOption DesfireAlgoOpts[] = { {T_DES, "des"}, {T_3DES, "2tdea"}, @@ -1749,7 +1750,7 @@ int DesfireFillAppList(DesfireContext_t *dctx, PICCInfo_t *PICCInfo, AppListS ap memcpy( appList[indx].appDFName, &buf[i * 24 + 1 + 5], - // strnlen((char *)&buf[i * 24 + 1 + 5], 16) + // str_nlen((char *)&buf[i * 24 + 1 + 5], 16) 16 ); } @@ -2879,7 +2880,7 @@ int DesfireISOSelect(DesfireContext_t *dctx, DesfireISOSelectControl cntr, uint8 } int DesfireISOSelectDF(DesfireContext_t *dctx, char *dfname, uint8_t *resp, size_t *resplen) { - return DesfireISOSelect(dctx, ISSDFName, (uint8_t *)dfname, strnlen(dfname, 16), resp, resplen); + return DesfireISOSelect(dctx, ISSDFName, (uint8_t *)dfname, str_nlen(dfname, 16), resp, resplen); } int DesfireISOGetChallenge(DesfireContext_t *dctx, DesfireCryptoAlgorithm keytype, uint8_t *resp, size_t *resplen) { diff --git a/client/src/mifare/desfirecrypto.c b/client/src/mifare/desfirecrypto.c index 48126ae50..750723dab 100644 --- a/client/src/mifare/desfirecrypto.c +++ b/client/src/mifare/desfirecrypto.c @@ -788,11 +788,11 @@ bool desfire_crc32_check(uint8_t *data, const size_t len, uint8_t *crc) { } void iso14443a_crc_append(uint8_t *data, size_t len) { - return compute_crc(CRC_14443_A, data, len, data + len, data + len + 1); + compute_crc(CRC_14443_A, data, len, data + len, data + len + 1); } void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc) { - return compute_crc(CRC_14443_A, data, len, pbtCrc, pbtCrc + 1); + compute_crc(CRC_14443_A, data, len, pbtCrc, pbtCrc + 1); } bool iso14443a_crc_check(uint8_t *data, const size_t len, uint8_t *crc) { diff --git a/client/src/mifare/gen4.c b/client/src/mifare/gen4.c new file mode 100644 index 000000000..f3a135b1d --- /dev/null +++ b/client/src/mifare/gen4.c @@ -0,0 +1,212 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Common functionality for low/high-frequency GALLAGHER tag encoding & decoding. +//----------------------------------------------------------------------------- +#include "gen4.h" + +#include +#include +#include +#include + +#include "commonutil.h" +#include "util.h" +#include "ui.h" +#include "mifaredefault.h" +#include "comms.h" +#include "cmdhf14a.h" +#include "protocols.h" +#include "mfkey.h" +#include "util_posix.h" +#include "cmdparser.h" + +static int mfG4ExCommand(uint8_t cmd, uint8_t *pwd, uint8_t *data, size_t datalen, uint8_t *response, size_t *responselen, bool verbose) { + struct p { + uint8_t cmdheader; + uint8_t pwd[4]; + uint8_t command; + uint8_t data[32]; + } PACKED payload; + memset(&payload, 0, sizeof(payload)); + + if (datalen > sizeof(payload.data)) { + return PM3_EINVARG; + } + + payload.cmdheader = 0xCF; + payload.command = cmd; + if (pwd != NULL) { + memcpy(payload.pwd, pwd, sizeof(payload.pwd)); + } + if (data != NULL && datalen > 0) { + memcpy(payload.data, data, datalen); + } + + int resplen = 0; + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_RAW | ISO14A_NO_RATS | ISO14A_APPEND_CRC, 6 + datalen, 0, (uint8_t *)&payload, 6 + datalen); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (resp.oldarg[0] != 2) { + if (verbose) PrintAndLogEx(ERR, "No card in the field."); + return PM3_ETIMEOUT; + } + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + if (verbose) { + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); + PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]); + } + } else { + if (verbose) PrintAndLogEx(ERR, "No card in the field."); + return PM3_ETIMEOUT; + } + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + resplen = resp.oldarg[0]; + + if (!resplen) { + if (verbose) PrintAndLogEx(ERR, "No card response."); + return PM3_EFAILED; + } + + resplen = resplen - 2; // 14A CRC + if (resplen < 0) + resplen = 0; + + if (resplen > 40) { + if (verbose) PrintAndLogEx(ERR, "Buffer too small(%d).", resplen); + return PM3_EOVFLOW; + } + + if (response != NULL) + memcpy(response, resp.data.asBytes, resplen); + + if (responselen != NULL) + *responselen = resplen; + + return PM3_SUCCESS; + } else { + if (verbose) PrintAndLogEx(ERR, "Reply timeout."); + return PM3_ETIMEOUT; + } +} + +int mfG4GetConfig(uint8_t *pwd, uint8_t *data, size_t *datalen, bool verbose) { + uint8_t resp[40] = {0}; + size_t resplen = 0; + + int res = mfG4ExCommand(GEN4_CMD_DUMP_CONFIG, pwd, NULL, 0, resp, &resplen, verbose); + if (res != PM3_SUCCESS) { + return res; + } + + if (data != NULL) + memcpy(data, resp, resplen); + + if (datalen != NULL) + *datalen = resplen; + + return PM3_SUCCESS; +} + +int mfG4GetFactoryTest(uint8_t *pwd, uint8_t *data, size_t *datalen, bool verbose) { + uint8_t resp[40] = {0}; + size_t resplen = 0; + + int res = mfG4ExCommand(GEN4_CMD_FACTORY_TEST, pwd, NULL, 0, resp, &resplen, verbose); + if (res != PM3_SUCCESS) { + return res; + } + + if (data != NULL) + memcpy(data, resp, resplen); + + if (datalen != NULL) + *datalen = resplen; + + return PM3_SUCCESS; +} + +int mfG4ChangePassword(uint8_t *pwd, uint8_t *newpwd, bool verbose) { + uint8_t resp[40] = {0}; + size_t resplen = 0; + + int res = mfG4ExCommand(GEN4_CMD_CHANGE_PASSWORD, pwd, newpwd, 4, resp, &resplen, verbose); + if (res != PM3_SUCCESS) { + return res; + } + + if (resplen != 2 || resp[0] != 0x90 || resp[1] != 0x00) + return PM3_EAPDU_FAIL; + + return PM3_SUCCESS; +} + +int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags) { + struct p { + uint8_t blockno; + uint8_t pwd[4]; + uint8_t workFlags; + } PACKED payload; + payload.blockno = blockno; + memcpy(payload.pwd, pwd, sizeof(payload.pwd)); + payload.workFlags = workFlags; + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_RDBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_RDBL, &resp, 1500)) { + if (resp.status != PM3_SUCCESS) { + return PM3_EUNDEF; + } + memcpy(data, resp.data.asBytes, MFBLOCK_SIZE); + } else { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + return PM3_SUCCESS; +} + +int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags) { + struct p { + uint8_t blockno; + uint8_t pwd[4]; + uint8_t data[MFBLOCK_SIZE]; + uint8_t workFlags; + } PACKED payload; + payload.blockno = blockno; + memcpy(payload.pwd, pwd, sizeof(payload.pwd)); + memcpy(payload.data, data, sizeof(payload.data)); + payload.workFlags = workFlags; + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_WRBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_WRBL, &resp, 1500)) { + if (resp.status != PM3_SUCCESS) { + return PM3_EUNDEF; + } + } else { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + return PM3_SUCCESS; +} diff --git a/client/src/mifare/gen4.h b/client/src/mifare/gen4.h new file mode 100644 index 000000000..b4e22fb98 --- /dev/null +++ b/client/src/mifare/gen4.h @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// High frequency ISO14443A commands +//----------------------------------------------------------------------------- +#ifndef __GEN4_H +#define __GEN4_H + +#include "common.h" + +#define GEN4_CMD_CONFIG_GTU 0x32 +#define GEN4_CMD_CONFIG_ATS 0x34 +#define GEN4_CMD_CONFIG_ATQA_SAK 0x35 +#define GEN4_CMD_CONFIG_UID_LEN 0x68 +#define GEN4_CMD_CONFIG_UL_ENABLE 0x69 +#define GEN4_CMD_CONFIG_UL_MODE 0x6A +#define GEN4_CMD_CONFIG_UL_SECTOR_COUNT 0x6A +#define GEN4_CMD_DUMP_CONFIG 0xC6 +#define GEN4_CMD_FACTORY_TEST 0xCC +#define GEN4_CMD_WRITE_BLOCK 0xCD +#define GEN4_CMD_READ_BLOCK 0xCE +#define GEN4_CMD_BL0_DIRECT_WRITE_EN 0xCF +#define GEN4_CMD_SET_CONFIG 0xF0 +#define GEN4_CMD_SET_CONFIG_PERMANENT 0xF1 +#define GEN4_CMD_CHANGE_PASSWORD 0xFE + +int mfG4GetConfig(uint8_t *pwd, uint8_t *data, size_t *datalen, bool verbose); +int mfG4GetFactoryTest(uint8_t *pwd, uint8_t *data, size_t *datalen, bool verbose); + +int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags); +int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags); + +int mfG4ChangePassword(uint8_t *pwd, uint8_t *newpwd, bool verbose); + +#endif diff --git a/client/src/mifare/mad.c b/client/src/mifare/mad.c index 49d3de065..54f67a6a4 100644 --- a/client/src/mifare/mad.c +++ b/client/src/mifare/mad.c @@ -190,7 +190,7 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) uint8_t GPB = sector0[(3 * 16) + 9]; if (verbose) - PrintAndLogEx(SUCCESS, "%14s " _GREEN_("0x%02x"), "GPB", GPB); + PrintAndLogEx(SUCCESS, "GPB....... " _GREEN_("0x%02X"), GPB); // DA (MAD available) if (!(GPB & 0x80)) { @@ -200,21 +200,22 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) uint8_t mad_ver = GPB & 0x03; if (verbose) - PrintAndLogEx(SUCCESS, "%14s " _GREEN_("%d"), "MAD version", mad_ver); + PrintAndLogEx(SUCCESS, "Version... " _GREEN_("%d"), mad_ver); // MAD version if ((mad_ver != 0x01) && (mad_ver != 0x02)) { - PrintAndLogEx(ERR, "Wrong MAD version " _RED_("0x%02x"), mad_ver); + PrintAndLogEx(ERR, "Wrong MAD version " _RED_("0x%02X"), mad_ver); return PM3_ESOFT; }; - if (haveMAD2) + if (haveMAD2) { *haveMAD2 = (mad_ver == 2); + } int res = madCRCCheck(sector0, true, 1); - - if (verbose && res == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "%14s " _GREEN_("0x%02x") " ( %s )", "CRC8", sector0[16], _GREEN_("ok")); + if (verbose && res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( %s )", sector0[16], _GREEN_("ok")); + } if (mad_ver == 2 && sector10) { int res2 = madCRCCheck(sector10, true, 2); @@ -222,7 +223,7 @@ int MADCheck(uint8_t *sector0, uint8_t *sector10, bool verbose, bool *haveMAD2) res = res2; if (verbose && !res2) - PrintAndLogEx(SUCCESS, "%14s " _GREEN_("0x%02x") " ( %s )", "CRC8", sector10[0], _GREEN_("ok")); + PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( %s )", sector10[0], _GREEN_("ok")); } // MA (multi-application card) @@ -284,9 +285,10 @@ static int MADInfoByteDecode(const uint8_t *sector, bool swapmad, int mad_ver, b info = sector[16 + 1] & 0x3f; if (info >= 0xF) { PrintAndLogEx(WARNING, "Invalid Info byte (MAD1) value " _YELLOW_("0x%02x"), info); - if (verbose) + if (verbose) { // I understand the spec in a way that MAD1 InfoByte should not point into MAD2 sectors, @lukaskuzmiak PrintAndLogEx(WARNING, "MAD1 Info byte points outside of MAD1 sector space (0x%02x), report a bug?", info); + } return PM3_ESOFT; } } else { @@ -318,7 +320,7 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMA int ibs = MADInfoByteDecode(sector, swapmad, 1, verbose); if (ibs > 0) { - PrintAndLogEx(SUCCESS, "Card publisher sector " _MAGENTA_("0x%02x"), ibs); + PrintAndLogEx(SUCCESS, "Card publisher sector " _MAGENTA_("0x%02X"), ibs); } else { PrintAndLogEx(WARNING, "Card publisher " _RED_("not") " present " _YELLOW_("0x%02x"), ibs); } @@ -331,12 +333,22 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMA for (int i = 1; i < 16; i++) { uint16_t aid = madGetAID(sector, swapmad, 1, i); if (aid < 6) { - PrintAndLogEx(INFO, (ibs == i) ? _MAGENTA_(" %02d [%04X] (%s)") : " %02d [%04X] (%s)", i, aid, aid_admin[aid]); + PrintAndLogEx(INFO, + (ibs == i) ? _MAGENTA_(" %02d [%04X] %s") : " %02d [" _GREEN_("%04X") "] %s", + i, + aid, + aid_admin[aid] + ); + } else if (prev_aid == aid) { - PrintAndLogEx(INFO, (ibs == i) ? _MAGENTA_(" %02d [%04X] (continuation)") : " %02d [%04X] (continuation)", i, aid); + PrintAndLogEx(INFO, + (ibs == i) ? _MAGENTA_(" %02d [%04X] continuation") : " %02d [" _YELLOW_("%04X") "] continuation", + i, + aid + ); } else { - char fmt[30]; - snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [%04X]%s", i, aid, "%s"); + char fmt[60]; + snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i, aid, "%s"); print_aid_description(mad_known_aids, aid, fmt, verbose); prev_aid = aid; } @@ -346,7 +358,7 @@ int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMA } int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose) { - open_mad_file(&mad_known_aids, verbose); + open_mad_file(&mad_known_aids, false); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "------------ " _CYAN_("MAD v2 details") " -------------"); @@ -354,14 +366,14 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose) { int res = madCRCCheck(sector, true, 2); if (verbose) { if (res == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "CRC8 ( %s )", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( " _GREEN_("%s") " )", sector[0], "ok"); else - PrintAndLogEx(WARNING, "CRC8 ( %s )", _RED_("fail")); + PrintAndLogEx(SUCCESS, "CRC8...... 0x%02X ( " _RED_("%s") " )", sector[0], "fail"); } int ibs = MADInfoByteDecode(sector, swapmad, 2, verbose); if (ibs > 0) { - PrintAndLogEx(SUCCESS, "Card publisher sector " _MAGENTA_("0x%02x"), ibs); + PrintAndLogEx(SUCCESS, "Card publisher sector " _MAGENTA_("0x%02X"), ibs); } else { PrintAndLogEx(WARNING, "Card publisher " _RED_("not") " present " _YELLOW_("0x%02x"), ibs); } @@ -375,12 +387,21 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose) { for (int i = 1; i < 8 + 8 + 7 + 1; i++) { uint16_t aid = madGetAID(sector, swapmad, 2, i); if (aid < 6) { - PrintAndLogEx(INFO, (ibs == i) ? _MAGENTA_(" %02d [%04X] (%s)") : " %02d [%04X] (%s)", i + 16, aid, aid_admin[aid]); + PrintAndLogEx(INFO, + (ibs == i) ? _MAGENTA_(" %02d [%04X] %s") : " %02d [" _GREEN_("%04X") "] %s", + i + 16, + aid, + aid_admin[aid] + ); } else if (prev_aid == aid) { - PrintAndLogEx(INFO, (ibs == i) ? _MAGENTA_(" %02d [%04X] (continuation)") : " %02d [%04X] (continuation)", i + 16, aid); + PrintAndLogEx(INFO, + (ibs == i) ? _MAGENTA_(" %02d [%04X] continuation") : " %02d [" _YELLOW_("%04X") "] continuation", + i + 16, + aid + ); } else { - char fmt[30]; - snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [%04X]%s", i + 16, aid, "%s"); + char fmt[60]; + snprintf(fmt, sizeof(fmt), (ibs == i) ? _MAGENTA_(" %02d [%04X]%s") : " %02d [" _GREEN_("%04X") "]%s", i + 16, aid, "%s"); print_aid_description(mad_known_aids, aid, fmt, verbose); prev_aid = aid; } @@ -424,8 +445,8 @@ int DetectHID(uint8_t *d, uint16_t manufacture) { int convert_mad_to_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen) { - if (in == NULL || out == NULL || ilen == 0 ) { - return PM3_EINVARG; + if (in == NULL || out == NULL || ilen == 0) { + return PM3_EINVARG; } // MAD detection @@ -464,8 +485,8 @@ int convert_mad_to_arr(uint8_t *in, uint16_t ilen, uint8_t *out, uint16_t *olen) // copy to out (skip ST) memcpy(out, tmp, sizeof(tmp) - MFBLOCK_SIZE); out += sizeof(tmp) - MFBLOCK_SIZE; - *olen += sizeof(tmp) -MFBLOCK_SIZE; + *olen += sizeof(tmp) - MFBLOCK_SIZE; } } return PM3_SUCCESS; -} \ No newline at end of file +} diff --git a/client/src/mifare/mifare4.c b/client/src/mifare/mifare4.c index ec8d1a6bf..f7d9980a9 100644 --- a/client/src/mifare/mifare4.c +++ b/client/src/mifare/mifare4.c @@ -24,9 +24,9 @@ #include "ui.h" #include "crypto/libpcrypto.h" -static bool VerboseMode = false; +static bool g_verbose_mode = false; void mfpSetVerboseMode(bool verbose) { - VerboseMode = verbose; + g_verbose_mode = verbose; } static const PlusErrorsElm_t PlusErrors[] = { @@ -83,6 +83,7 @@ bool mfValidateAccessConditions(const uint8_t *data) { return ((nd1 == (d1 ^ 0xF)) && (nd2 == (d2 ^ 0xF)) && (nd3 == (d3 ^ 0xF))); } + bool mfReadOnlyAccessConditions(uint8_t blockn, const uint8_t *data) { uint8_t d1 = NIBBLE_HIGH(data[1]) >> blockn; @@ -98,7 +99,6 @@ bool mfReadOnlyAccessConditions(uint8_t blockn, const uint8_t *data) { return false; } - const char *mfGetAccessConditionsDesc(uint8_t blockn, const uint8_t *data) { uint8_t d1 = NIBBLE_HIGH(data[1]) >> blockn; uint8_t d2 = NIBBLE_LOW(data[2]) >> blockn; @@ -118,9 +118,17 @@ const char *mfGetAccessConditionsDesc(uint8_t blockn, const uint8_t *data) { } }; - static char StaticNone[] = "none"; - return StaticNone; + static char none[] = "none"; + return none; } + +uint8_t mf_get_accesscondition(uint8_t blockn, const uint8_t *data) { + uint8_t d1 = NIBBLE_HIGH(data[1]) >> blockn; + uint8_t d2 = NIBBLE_LOW(data[2]) >> blockn; + uint8_t d3 = NIBBLE_HIGH(data[2]) >> blockn; + return (d1 & 0x01) << 2 | (d2 & 0x01) << 1 | (d3 & 0x01); +} + /* static int CalculateEncIVCommand(mf4Session_t *mf4session, uint8_t *iv, bool verbose) { memcpy(&iv[0], &mf4session->TI, 4); @@ -250,9 +258,9 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti memmove(&raw[16], &RndB[1], 16); aes_encode(NULL, key, raw, &cmd2[1], 32); - if (verbose) + if (verbose) { PrintAndLogEx(INFO, ">phase2: %s", sprint_hex(cmd2, 33)); - + } res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen, silentMode); if (res) { if (!silentMode) PrintAndLogEx(ERR, "Exchange raw error: %d", res); @@ -340,12 +348,12 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti } static int intExchangeRAW14aPlus(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { - if (VerboseMode) + if (g_verbose_mode) PrintAndLogEx(INFO, ">>> %s", sprint_hex(datain, datainlen)); int res = ExchangeRAW14a(datain, datainlen, activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen, false); - if (VerboseMode) + if (g_verbose_mode) PrintAndLogEx(INFO, "<<< %s", sprint_hex(dataout, *dataoutlen)); return res; @@ -364,29 +372,37 @@ int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int return intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); } -int MFPReadBlock(mf4Session_t *mf4session, bool plain, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { - uint8_t rcmd[4 + 8] = {(plain ? (0x37) : (0x33)), blockNum, 0x00, blockCount}; - if (!plain && mf4session) - CalculateMAC(mf4session, mtypReadCmd, blockNum, blockCount, rcmd, 4, &rcmd[4], VerboseMode); - - int res = intExchangeRAW14aPlus(rcmd, plain ? 4 : sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); +int MFPReadBlock(mf4Session_t *mf4session, bool plain, bool nomaccmd, bool nomacres, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { + int cmdb = 0x31; + if (nomacres) {cmdb = cmdb ^ 0x01;} // If we do not want MAC in reply, remove 0x01 + if (plain) {cmdb = cmdb ^ 0x02;} // If we do not need an encrypted transmission, add 0x02 + if (nomaccmd) {cmdb = cmdb ^ 0x04;} // If we do not want to send a MAC, remove 0x04 + uint8_t rcmd1[4] = {cmdb, blockNum, 0x00, blockCount}; + uint8_t maccmddat[8] = {0}; + uint8_t rcmd[nomaccmd ? 4 : 12]; + if (!nomaccmd && mf4session) + CalculateMAC(mf4session, mtypReadCmd, blockNum, blockCount, rcmd1, 4, &maccmddat[0], g_verbose_mode); + memmove(rcmd, rcmd1, 4); + if (!nomaccmd) {memmove(&rcmd[4], maccmddat, 8);} + int res = intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); if (res) return res; - if (mf4session) mf4session->R_Ctr++; - - if (mf4session && mac && *dataoutlen > 11) - CalculateMAC(mf4session, mtypReadResp, blockNum, blockCount, dataout, *dataoutlen - 8 - 2, mac, VerboseMode); + if (mf4session && !nomacres && *dataoutlen > 11) + CalculateMAC(mf4session, mtypReadResp, blockNum, blockCount, dataout, *dataoutlen - 8 - 2, mac, g_verbose_mode); return 0; } -int MFPWriteBlock(mf4Session_t *mf4session, uint8_t blockNum, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { - uint8_t rcmd[1 + 2 + 16 + 8] = {0xA3, blockNum, 0x00}; +int MFPWriteBlock(mf4Session_t *mf4session, bool plain, bool nomacres, uint8_t blockNum, uint8_t blockHdr, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac) { + int cmdb = 0xA1; + if (nomacres) {cmdb = cmdb ^ 0x01;} // If we do not want MAC in reply, remove 0x01 + if (plain) {cmdb = cmdb ^ 0x02;} // If we do not need an encrypted transmission, add 0x02 + uint8_t rcmd[1 + 2 + 16 + 8] = {cmdb, blockNum, blockHdr}; memmove(&rcmd[3], data, 16); if (mf4session) - CalculateMAC(mf4session, mtypWriteCmd, blockNum, 1, rcmd, 19, &rcmd[19], VerboseMode); + CalculateMAC(mf4session, mtypWriteCmd, blockNum, 1, rcmd, 19, &rcmd[19], g_verbose_mode); int res = intExchangeRAW14aPlus(rcmd, sizeof(rcmd), activateField, leaveSignalON, dataout, maxdataoutlen, dataoutlen); if (res) @@ -395,8 +411,8 @@ int MFPWriteBlock(mf4Session_t *mf4session, uint8_t blockNum, uint8_t *data, boo if (mf4session) mf4session->W_Ctr++; - if (mf4session && mac && *dataoutlen > 3) - CalculateMAC(mf4session, mtypWriteResp, blockNum, 1, dataout, *dataoutlen, mac, VerboseMode); + if (mf4session && mac && *dataoutlen > 3 && !nomacres) + CalculateMAC(mf4session, mtypWriteResp, blockNum, 1, dataout, *dataoutlen, mac, g_verbose_mode); return 0; } @@ -423,7 +439,7 @@ int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *data uint8_t mac[8] = {0}; uint8_t firstBlockNo = mfFirstBlockOfSector(sectorNo); for (int n = firstBlockNo; n < firstBlockNo + mfNumBlocksPerSector(sectorNo); n++) { - res = MFPReadBlock(&_session, plain, n & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); + res = MFPReadBlock(&_session, plain, false, false, n & 0xff, 1, false, true, data, sizeof(data), &datalen, mac); if (res) { PrintAndLogEx(ERR, "Sector %u read error: %d", sectorNo, res); DropField(); @@ -534,7 +550,7 @@ uint8_t mfSectorTrailer(uint16_t blockNo) { if (blockNo < 32 * 4) { return (blockNo | 0x03); } else { - return (blockNo | 0x0f); + return (blockNo | 0x0F); } } @@ -551,3 +567,11 @@ uint8_t mfSectorNum(uint16_t blockNo) { return (32 + (blockNo - 32 * 4) / 16); } + +bool mfIsSectorTrailerBasedOnBlocks(uint8_t sectorno, uint16_t blockno) { + if (sectorno < 32) { + return ((blockno | 0x03) == blockno); + } else { + return ((blockno | 0x0F) == blockno); + } +} diff --git a/client/src/mifare/mifare4.h b/client/src/mifare/mifare4.h index 80d55db7b..b5449ab44 100644 --- a/client/src/mifare/mifare4.h +++ b/client/src/mifare/mifare4.h @@ -63,8 +63,8 @@ int MifareAuth4(mf4Session_t *mf4session, uint8_t *keyn, uint8_t *key, bool acti int MFPWritePerso(uint8_t *keyNum, uint8_t *key, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); int MFPCommitPerso(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); -int MFPReadBlock(mf4Session_t *mf4session, bool plain, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); -int MFPWriteBlock(mf4Session_t *mf4session, uint8_t blockNum, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); +int MFPReadBlock(mf4Session_t *mf4session, bool plain, bool maccmd, bool macres, uint8_t blockNum, uint8_t blockCount, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); +int MFPWriteBlock(mf4Session_t *mf4session, bool plain, bool nomacres, uint8_t blockNum, uint8_t blockHdr, uint8_t *data, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, uint8_t *mac); int mfpReadSector(uint8_t sectorNo, uint8_t keyType, uint8_t *key, uint8_t *dataout, bool verbose); int MFPGetSignature(bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); @@ -73,12 +73,14 @@ int MFPGetVersion(bool activateField, bool leaveSignalON, uint8_t *dataout, int bool mfValidateAccessConditions(const uint8_t *data); bool mfReadOnlyAccessConditions(uint8_t blockn, const uint8_t *data); const char *mfGetAccessConditionsDesc(uint8_t blockn, const uint8_t *data); +uint8_t mf_get_accesscondition(uint8_t blockn, const uint8_t *data); uint8_t mfNumBlocksPerSector(uint8_t sectorNo); uint8_t mfFirstBlockOfSector(uint8_t sectorNo); uint8_t mfSectorTrailerOfSector(uint8_t sectorNo); uint8_t mfSectorTrailer(uint16_t blockNo); bool mfIsSectorTrailer(uint16_t blockNo); +bool mfIsSectorTrailerBasedOnBlocks(uint8_t sectorno, uint16_t blockno); uint8_t mfSectorNum(uint16_t blockNo); diff --git a/client/src/mifare/mifaredefault.h b/client/src/mifare/mifaredefault.h index e3590329d..d94aae269 100644 --- a/client/src/mifare/mifaredefault.h +++ b/client/src/mifare/mifaredefault.h @@ -21,7 +21,13 @@ #include "common.h" -#define MFKEY_SIZE 6 +#define DES_KEY_LEN 8 +#define AES_KEY_LEN 16 +#define T2DES_KEY_LEN 16 +#define T3DES_KEY_LEN 24 + +#define MAX_AES_KEYS_LIST_LEN 1024 + #define MFBLOCK_SIZE 16 #define MIFARE_4K_MAXBLOCK 256 @@ -41,14 +47,24 @@ #define MIFARE_KEY_SIZE 6 +#define MIFARE_MINI_MAX_KEY_SIZE (MIFARE_MINI_MAXSECTOR * 2 * MIFARE_KEY_SIZE) +#define MIFARE_1K_MAX_KEY_SIZE (MIFARE_1K_MAXSECTOR * 2 * MIFARE_KEY_SIZE) +#define MIFARE_2K_MAX_KEY_SIZE (MIFARE_2K_MAXSECTOR * 2 * MIFARE_KEY_SIZE) +#define MIFARE_4K_MAX_KEY_SIZE (MIFARE_4K_MAXSECTOR * 2 * MIFARE_KEY_SIZE) + + static const uint64_t g_mifare_default_keys[] = { 0xffffffffffff, // Default key (first key used by program if no user defined key) - 0xa0a1a2a3a4a5, // NFCForum MAD key + 0xa0a1a2a3a4a5, // NFCForum MAD key A + 0xb0b1b2b3b4b5, // NFCForum MAD key B + 0x89ECA97F8C2A, // NFCForum MAD key B 0xd3f7d3f7d3f7, // NDEF public key 0x4b791bea7bcc, // MFC EV1 Signature 17 B 0x5C8FF9990DA2, // MFC EV1 Signature 16 A 0xD01AFEEB890A, // MFC EV1 Signature 16 B 0x75CCB59C9BED, // MFC EV1 Signature 17 A + 0x707B11FC1481, // MFC QL88 Signature 17 B + 0x2612C6DE84CA, // MFC QL88 Signature 17 A 0xfc00018778f7, // Public Transport 0x6471a5ef2d1a, // SimonsVoss 0x4E3552426B32, // ID06 @@ -81,13 +97,12 @@ static const uint64_t g_mifare_default_keys[] = { 0x11496F97752A, // HID 0x3E65E4FB65B3, // Gym 0x000000000000, // Blank key - 0xb0b1b2b3b4b5, - 0xaabbccddeeff, + 0x010203040506, 0x1a2b3c4d5e6f, 0x123456789abc, - 0x010203040506, 0x123456abcdef, 0xabcdef123456, + 0xaabbccddeeff, 0x4d3a99c351dd, 0x1a982c7e459a, 0x714c5c886e97, @@ -108,6 +123,9 @@ static const uint8_t g_mifare_mad_key_b[] = {0x89, 0xEC, 0xA9, 0x7F, 0x8C, 0x2A} static const uint8_t g_mifare_signature_key_a[] = {0x5C, 0x8F, 0xF9, 0x99, 0x0D, 0xA2}; static const uint8_t g_mifare_signature_key_b[] = {0x4b, 0x79, 0x1b, 0xea, 0x7b, 0xcc}; +// Manufacture MFC / QL88 (S17 / B) +static const uint8_t g_mifare_ql88_signature_key_b[] = {0x70, 0x7B, 0x11, 0xFC, 0x14, 0x81}; + static const uint8_t g_mifare_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; static const uint8_t g_mifarep_mad_key[] = {0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7}; static const uint8_t g_mifarep_ndef_key[] = {0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7}; diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 3b37182fe..56313aa55 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -40,6 +40,8 @@ #include "crypto/libpcrypto.h" #include "util.h" // xor #include "mbedtls/sha1.h" // SHA1 +#include "cmdhf14a.h" +#include "gen4.h" int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { uint32_t uid = 0; @@ -50,7 +52,7 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { // message PrintAndLogEx(INFO, "Expected execution time is about 25seconds on average"); - PrintAndLogEx(INFO, "Press pm3-button to abort"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort"); while (true) { clearCommandBuffer(); @@ -195,15 +197,22 @@ int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keyc SendCommandNG(CMD_HF_MIFARE_CHKKEYS, data, (5 + 6 * keycnt)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_HF_MIFARE_CHKKEYS, &resp, 2500)) return PM3_ETIMEOUT; - if (resp.status != PM3_SUCCESS) return resp.status; + if (!WaitForResponseTimeout(CMD_HF_MIFARE_CHKKEYS, &resp, 2500)) { + return PM3_ETIMEOUT; + } + if (resp.status != PM3_SUCCESS) { + return resp.status; + } struct kr { uint8_t key[6]; bool found; } PACKED; struct kr *keyresult = (struct kr *)&resp.data.asBytes; - if (!keyresult->found) return PM3_ESOFT; + if (!keyresult->found) { + return PM3_ESOFT; + } + *key = bytes_to_num(keyresult->key, sizeof(keyresult->key)); return PM3_SUCCESS; } @@ -213,7 +222,7 @@ int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keyc // 1 == // 2 == Time-out, aborting int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint8_t strategy, - uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory) { + uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory, bool verbose) { uint64_t t2 = msclock(); @@ -247,7 +256,9 @@ int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, // time to convert the returned data. uint8_t curr_keys = resp.oldarg[0]; - PrintAndLogEx(INFO, "Chunk %.1fs | found %u/%u keys (%u)", (float)(t2 / 1000.0), curr_keys, (sectorsCnt << 1), size); + if (verbose) { + PrintAndLogEx(INFO, "Chunk %.1fs | found %u/%u keys (%u)", (float)(t2 / 1000.0), curr_keys, (sectorsCnt << 1), size); + } // all keys? if (curr_keys == sectorsCnt * 2 || lastChunk) { @@ -259,16 +270,19 @@ int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, foo = bytes_to_num(resp.data.asBytes + 480, 8); bar = (resp.data.asBytes[489] << 8 | resp.data.asBytes[488]); - for (uint8_t i = 0; i < 64; i++) + for (uint8_t i = 0; i < 64; i++) { arr[i] = (foo >> i) & 0x1; + } - for (uint8_t i = 0; i < 16; i++) + for (uint8_t i = 0; i < 16; i++) { arr[i + 64] = (bar >> i) & 0x1; + } // initialize storage for found keys icesector_t *tmp = calloc(sectorsCnt, sizeof(icesector_t)); - if (tmp == NULL) + if (tmp == NULL) { return PM3_EMALLOC; + } memcpy(tmp, resp.data.asBytes, sectorsCnt * sizeof(icesector_t)); @@ -286,10 +300,19 @@ int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, } free(tmp); - if (curr_keys == sectorsCnt * 2) + // if all keys where found + if (curr_keys == sectorsCnt * 2) { return PM3_SUCCESS; - if (lastChunk) + } + + // if some keys was found + if (curr_keys > 0) { + return PM3_EPARTIAL; + } + + if (lastChunk) { return PM3_ESOFT; + } } return PM3_ESOFT; } @@ -585,12 +608,11 @@ out: return PM3_ESOFT; } - int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey) { uint32_t uid; StateList_t statelists[2]; - struct Crypto1State *p1, * p2, * p3, * p4; + struct Crypto1State *p1, *p2, *p3, *p4; struct { uint8_t block; @@ -781,7 +803,7 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl p_keyblock = mem; } - uint8_t fn[26] = "static_nested_000.bin"; + uint8_t fn[32] = "static_nested_000.bin"; uint64_t start_time = msclock(); for (uint32_t i = 0; i < keycnt; i += max_keys_chunk) { @@ -867,13 +889,13 @@ out: int mfReadSector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data) { clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, (uint8_t *)key, 6); + SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, (uint8_t *)key, MIFARE_KEY_SIZE); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t isOK = resp.oldarg[0] & 0xff; + uint8_t isOK = resp.oldarg[0] & 0xFF; if (isOK) { - memcpy(data, resp.data.asBytes, mfNumBlocksPerSector(sectorNo) * 16); + memcpy(data, resp.data.asBytes, mfNumBlocksPerSector(sectorNo) * MFBLOCK_SIZE); return PM3_SUCCESS; } else { return PM3_EUNDEF; @@ -896,7 +918,7 @@ int mfReadBlock(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *d SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) { - memcpy(data, resp.data.asBytes, 16); + memcpy(data, resp.data.asBytes, MFBLOCK_SIZE); if (resp.status != PM3_SUCCESS) { PrintAndLogEx(DEBUG, "failed reading block"); @@ -912,7 +934,7 @@ int mfReadBlock(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *d // EMULATOR int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) { - size_t size = blocksCount * 16; + size_t size = blocksCount * MFBLOCK_SIZE; if (size > PM3_CMD_DATA_SIZE) { return PM3_ESOFT; } @@ -941,7 +963,7 @@ int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount) { } int mfEmlSetMem(uint8_t *data, int blockNum, int blocksCount) { - return mfEmlSetMem_xt(data, blockNum, blocksCount, 16); + return mfEmlSetMem_xt(data, blockNum, blocksCount, MFBLOCK_SIZE); } int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { @@ -955,7 +977,7 @@ int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidt size_t size = ((size_t) blocksCount) * blockBtWidth; if (size > (PM3_CMD_DATA_SIZE - sizeof(struct p))) { - return PM3_ESOFT; + return PM3_EINVARG; } size_t paylen = sizeof(struct p) + size; @@ -976,7 +998,7 @@ int mfEmlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidt int mfCSetUID(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t *sak, uint8_t *old_uid, uint8_t *verifed_uid, uint8_t wipecard) { uint8_t params = MAGIC_SINGLE; - uint8_t block0[16]; + uint8_t block0[MFBLOCK_SIZE]; memset(block0, 0x00, sizeof(block0)); int res = mfCGetBlock(0, block0, params); @@ -1039,11 +1061,11 @@ int mfCSetUID(uint8_t *uid, uint8_t uidlen, const uint8_t *atqa, const uint8_t * } int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak) { - uint8_t block0[16] = {0x00, 0x56, 0x78, 0xBB, 0x95, 0x08, 0x04, 0x00, 0x02, 0xB2, 0x1E, 0x24, 0x23, 0x27, 0x1E, 0x1D}; - // uint8_t block0[16] = {0x04, 0x03, 0x02, 0x01, 0x04, 0x08, 0x04, 0x00, 0x64, 0xB9, 0x95, 0x11, 0x4D, 0x20, 0x42, 0x09}; - uint8_t blockD[16] = {0x00}; + uint8_t block0[MFBLOCK_SIZE] = {0x00, 0x56, 0x78, 0xBB, 0x95, 0x08, 0x04, 0x00, 0x02, 0xB2, 0x1E, 0x24, 0x23, 0x27, 0x1E, 0x1D}; + // uint8_t block0[MFBLOCK_SIZE] = {0x04, 0x03, 0x02, 0x01, 0x04, 0x08, 0x04, 0x00, 0x64, 0xB9, 0x95, 0x11, 0x4D, 0x20, 0x42, 0x09}; + uint8_t blockD[MFBLOCK_SIZE] = {0x00}; // default transport ACL - uint8_t blockK[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + uint8_t blockK[MFBLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0x80, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; uint8_t params = MAGIC_SINGLE; if (uid != NULL) { @@ -1089,16 +1111,18 @@ int mfCWipe(uint8_t *uid, const uint8_t *atqa, const uint8_t *sak) { } int mfCSetBlock(uint8_t blockNo, uint8_t *data, uint8_t *uid, uint8_t params) { - clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_CSETBL, params, blockNo, 0, data, 16); + SendCommandMIX(CMD_HF_MIFARE_CSETBL, params, blockNo, 0, data, MFBLOCK_SIZE); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 3500)) { uint8_t isOK = resp.oldarg[0] & 0xff; - if (uid != NULL) + if (uid != NULL) { memcpy(uid, resp.data.asBytes, 4); - if (!isOK) + } + + if (!isOK) { return PM3_EUNDEF; + } } else { PrintAndLogEx(WARNING, "command execute timeout"); return PM3_ETIMEOUT; @@ -1112,9 +1136,10 @@ int mfCGetBlock(uint8_t blockNo, uint8_t *data, uint8_t params) { PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { uint8_t isOK = resp.oldarg[0] & 0xff; - if (!isOK) + if (!isOK) { return PM3_EUNDEF; - memcpy(data, resp.data.asBytes, 16); + } + memcpy(data, resp.data.asBytes, MFBLOCK_SIZE); } else { PrintAndLogEx(WARNING, "command execute timeout"); return PM3_ETIMEOUT; @@ -1139,11 +1164,11 @@ int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid) { int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock) { clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_GEN3BLK, blockLen, 0, 0, block, 16); + SendCommandMIX(CMD_HF_MIFARE_GEN3BLK, blockLen, 0, 0, block, MFBLOCK_SIZE); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_HF_MIFARE_GEN3BLK, &resp, 3500)) { if (resp.status == PM3_SUCCESS && newBlock) { - memcpy(newBlock, resp.data.asBytes, 16); + memcpy(newBlock, resp.data.asBytes, MFBLOCK_SIZE); } return resp.status; } else { @@ -1164,65 +1189,14 @@ int mfGen3Freeze(void) { } } -int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags) { - struct p { - uint8_t blockno; - uint8_t pwd[4]; - uint8_t workFlags; - } PACKED payload; - payload.blockno = blockno; - memcpy(payload.pwd, pwd, sizeof(payload.pwd)); - payload.workFlags = workFlags; - - clearCommandBuffer(); - SendCommandNG(CMD_HF_MIFARE_G4_RDBL, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_RDBL, &resp, 1500)) { - if (resp.status != PM3_SUCCESS) { - return PM3_EUNDEF; - } - memcpy(data, resp.data.asBytes, 16); - } else { - PrintAndLogEx(WARNING, "command execute timeout"); - return PM3_ETIMEOUT; - } - return PM3_SUCCESS; -} - -int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags) { - struct p { - uint8_t blockno; - uint8_t pwd[4]; - uint8_t data[16]; - uint8_t workFlags; - } PACKED payload; - payload.blockno = blockno; - memcpy(payload.pwd, pwd, sizeof(payload.pwd)); - memcpy(payload.data, data, sizeof(payload.data)); - payload.workFlags = workFlags; - - clearCommandBuffer(); - SendCommandNG(CMD_HF_MIFARE_G4_WRBL, (uint8_t *)&payload, sizeof(payload)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_WRBL, &resp, 1500)) { - if (resp.status != PM3_SUCCESS) { - return PM3_EUNDEF; - } - } else { - PrintAndLogEx(WARNING, "command execute timeout"); - return PM3_ETIMEOUT; - } - return PM3_SUCCESS; -} - - // variables uint32_t cuid = 0; // uid part used for crypto1. void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted) { if (len != 1) { - for (int i = 0; i < len; i++) + for (int i = 0; i < len; i++) { data[i] = crypto1_byte(pcs, 0x00, isEncrypted) ^ data[i]; + } } else { uint8_t bt = 0; bt |= (crypto1_bit(pcs, 0, isEncrypted) ^ BIT(data[0], 0)) << 0; @@ -1234,11 +1208,12 @@ void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool i } int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len) { + PrintAndLogEx(SUCCESS, "encrypted data... %s", sprint_hex(data, len)); - struct Crypto1State *s; uint32_t ks2 = ar_enc ^ prng_successor(nt, 64); uint32_t ks3 = at_enc ^ prng_successor(nt, 96); - s = lfsr_recovery64(ks2, ks3); + + struct Crypto1State *s = lfsr_recovery64(ks2, ks3); mf_crypto1_decrypt(s, data, len, false); PrintAndLogEx(SUCCESS, "decrypted data... " _YELLOW_("%s"), sprint_hex(data, len)); PrintAndLogEx(NORMAL, ""); @@ -1262,7 +1237,7 @@ int detect_classic_prng(void) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, flags, sizeof(cmd), 0, cmd, sizeof(cmd)); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(WARNING, "PRNG UID: Reply timeout."); return PM3_ETIMEOUT; } @@ -1272,7 +1247,7 @@ int detect_classic_prng(void) { PrintAndLogEx(ERR, "error: selecting tag failed, can't detect prng\n"); return PM3_ERFTRANS; } - if (!WaitForResponseTimeout(CMD_ACK, &respA, 2500)) { + if (WaitForResponseTimeout(CMD_ACK, &respA, 2500) == false) { PrintAndLogEx(WARNING, "PRNG data: Reply timeout."); return PM3_ETIMEOUT; } @@ -1302,8 +1277,9 @@ int detect_classic_nackbug(bool verbose) { PrintAndLogEx(INFO, "Checking for NACK bug"); - if (verbose) - PrintAndLogEx(SUCCESS, "press pm3-button on the Proxmark3 device to abort both Proxmark3 and client.\n"); + if (verbose) { + PrintAndLogEx(SUCCESS, "press " _GREEN_("pm3 button") " to abort both Proxmark3 and client\n"); + } PrintAndLogEx(INFO, "." NOLF); @@ -1387,54 +1363,94 @@ int detect_classic_static_nonce(void) { return NONCE_FAIL; } -/* try to see if card responses to "Chinese magic backdoor" commands. */ -int detect_mf_magic(bool is_mfc) { +/* Detect Mifare Classic static encrypted nonce +detects special magic cards that has a static / fixed nonce +returns: +0 = nonce ok +1 = has static/fixed nonce +2 = cmd failed +3 = has encrypted nonce +*/ +int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, uint8_t *key) { + clearCommandBuffer(); + uint8_t cdata[1 + 1 + MIFARE_KEY_SIZE] = { 0 }; + cdata[0] = block_no; + cdata[1] = key_type; + memcpy(&cdata[2], key, MIFARE_KEY_SIZE); + SendCommandNG(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, cdata, sizeof(cdata)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) { - uint8_t isGeneration = 0; + if (resp.status == PM3_ESOFT) { + return NONCE_FAIL; + } + return resp.data.asBytes[0]; + } + return NONCE_FAIL; +} + +/* try to see if card responses to "Chinese magic backdoor" commands. */ +int detect_mf_magic(bool is_mfc, uint8_t key_type, uint64_t key) { + + uint8_t isMagic = 0; PacketResponseNG resp; clearCommandBuffer(); - uint8_t payload[] = { is_mfc }; + uint8_t payload[1 + 1 + MIFARE_KEY_SIZE] = { is_mfc, key_type }; + num_to_bytes(key, MIFARE_KEY_SIZE, payload + 2); + SendCommandNG(CMD_HF_MIFARE_CIDENT, payload, sizeof(payload)); if (WaitForResponseTimeout(CMD_HF_MIFARE_CIDENT, &resp, 1500)) { - if (resp.status == PM3_SUCCESS) - isGeneration = resp.data.asBytes[0]; + if (resp.status != PM3_SUCCESS) { + return 0; + } } - switch (isGeneration) { - case MAGIC_GEN_1A: - PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 1a")); - break; - case MAGIC_GEN_1B: - PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 1b")); - break; - case MAGIC_GEN_2: - PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 2 / CUID")); - break; - case MAGIC_GEN_3: - PrintAndLogEx(SUCCESS, "Magic capabilities : possibly " _GREEN_("Gen 3 / APDU")); - break; - case MAGIC_GEN_4GTU: - PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GTU")); - break; - case MAGIC_GEN_4GDM: - PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Gen 4 GDM")); - break; - case MAGIC_GEN_UNFUSED: - PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Write Once / FUID")); - break; - case MAGIC_SUPER_GEN1: - PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Super card (") _CYAN_("Gen 1") _GREEN_(")")); - break; - case MAGIC_SUPER_GEN2: - PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("Super card (") _CYAN_("Gen 2") _GREEN_(")")); - break; - case MAGIC_NTAG21X: - PrintAndLogEx(SUCCESS, "Magic capabilities : " _GREEN_("NTAG21x")); - break; - default: - break; + for (size_t i = 0; i < resp.length; i++) { + isMagic = 1; + switch (resp.data.asBytes[i]) { + case MAGIC_GEN_1A: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 1a")); + break; + case MAGIC_GEN_1B: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 1b")); + break; + case MAGIC_GEN_2: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 2 / CUID")); + break; + case MAGIC_GEN_3: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 3 / APDU") " ( possibly )"); + break; + case MAGIC_GEN_4GTU: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GTU")); + break; + case MAGIC_GDM_AUTH: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GDM / USCUID") " ( Magic Auth )"); + break; + case MAGIC_GDM_WUP_20: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GDM / USCUID") " ( Alt Magic Wakeup )"); + break; + case MAGIC_GDM_WUP_40: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Gen 4 GDM / USCUID") " ( Gen1 Magic Wakeup )"); + break; + case MAGIC_GEN_UNFUSED: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Write Once / FUID")); + break; + case MAGIC_SUPER_GEN1: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Super card ( ") _CYAN_("Gen 1") _GREEN_(" )")); + break; + case MAGIC_SUPER_GEN2: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("Super card ( ") _CYAN_("Gen 2") _GREEN_(" )")); + break; + case MAGIC_NTAG21X: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("NTAG21x")); + break; + case MAGIC_QL88: + PrintAndLogEx(SUCCESS, "Magic capabilities... " _GREEN_("QL88")); + default: + break; + } } - return isGeneration; + return isMagic; } bool detect_mfc_ev1_signature(void) { @@ -1454,6 +1470,15 @@ int read_mfc_ev1_signature(uint8_t *signature) { if (res == PM3_SUCCESS) { memcpy(signature, sign, sizeof(sign)); } + } else { + // try QL88 + res = mfReadBlock(69, MF_KEY_B, g_mifare_ql88_signature_key_b, sign); + if (res == PM3_SUCCESS) { + res = mfReadBlock(70, MF_KEY_B, g_mifare_ql88_signature_key_b, sign + 16); + if (res == PM3_SUCCESS) { + memcpy(signature, sign, sizeof(sign)); + } + } } return res; } diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index ccefbbd40..f71a9436d 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -74,7 +74,8 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *resultKey); int mfCheckKeys(uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t *keyBlock, uint64_t *key); int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, - uint8_t strategy, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory); + uint8_t strategy, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, + bool use_flashmemory, bool verbose); int mfCheckKeys_file(uint8_t *destfn, uint64_t *key); @@ -96,15 +97,13 @@ int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid); int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock); int mfGen3Freeze(void); -int mfG4GetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags); -int mfG4SetBlock(uint8_t *pwd, uint8_t blockno, uint8_t *data, uint8_t workFlags); - int tryDecryptWord(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); int detect_classic_prng(void); int detect_classic_nackbug(bool verbose); -int detect_mf_magic(bool is_mfc); +int detect_mf_magic(bool is_mfc, uint8_t key_type, uint64_t key); int detect_classic_static_nonce(void); +int detect_classic_static_encrypted_nonce(uint8_t block_no, uint8_t key_type, uint8_t *key); bool detect_mfc_ev1_signature(void); int read_mfc_ev1_signature(uint8_t *signature); diff --git a/client/src/nfc/ndef.c b/client/src/nfc/ndef.c index 311615c2a..a8227ddc7 100644 --- a/client/src/nfc/ndef.c +++ b/client/src/nfc/ndef.c @@ -32,12 +32,16 @@ #define STRBOOL(p) ((p) ? "1" : "0") -#define NDEF_WIFIAPPL_WSC "application/vnd.wfa.wsc" -#define NDEF_WIFIAPPL_P2P "application/vnd.wfa.p2p" -#define NDEF_BLUEAPPL "application/vnd.bluetooth" -#define NDEF_JSONAPPL "application/json" -#define NDEF_VCARDTEXT "text/vcard" -#define NDEF_XVCARDTEXT "text/x-vcard" +#define NDEF_WIFIAPPL_WSC "application/vnd.wfa.wsc" +#define NDEF_WIFIAPPL_P2P "application/vnd.wfa.p2p" +#define NDEF_JSONAPPL "application/json" +#define NDEF_VCARDTEXT "text/vcard" +#define NDEF_XVCARDTEXT "text/x-vcard" + +#define NDEF_BLUEAPPL_EP "application/vnd.bluetooth.ep.oob" +#define NDEF_BLUEAPPL_LE "application/vnd.bluetooth.le.oob" +#define NDEF_BLUEAPPL_SECURE_LE "application/vnd.bluetooth.secure.le.oob" + static const char *TypeNameFormat_s[] = { "Empty Record", @@ -117,7 +121,7 @@ static int ndefDecodePayload(NDEFHeader_t *ndef, bool verbose); static uint16_t ndefTLVGetLength(const uint8_t *data, size_t *indx) { uint16_t len = 0; if (data[0] == 0xFF) { - len = (data[1] << 8) + data[2]; + len = MemBeToUint2byte(data + 1); *indx += 3; } else { len = data[0]; @@ -270,7 +274,7 @@ static int ndef_print_signature(uint8_t *data, uint8_t data_len, uint8_t *signat return PM3_ESOFT; } - PrintAndLogEx(INFO, " IC signature public key name: %s", ndef_public_keys[i].desc); + PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), ndef_public_keys[i].desc); PrintAndLogEx(INFO, "IC signature public key value: %s", ndef_public_keys[i].value); PrintAndLogEx(INFO, " Elliptic curve parameters: %s", get_curve_name(ndef_public_keys[i].grp_id)); PrintAndLogEx(INFO, " NDEF Signature: %s", sprint_hex_inrow(signature, 32)); @@ -313,7 +317,7 @@ static int ndefDecodeSig1(uint8_t *sig, size_t siglen) { if (sigType == stECDSA_P256) { slen = 32; } - + PrintAndLogEx(SUCCESS, "\tSignature [%u]...", intsiglen); print_hex_noascii_break(&sig[indx], intsiglen, 32); @@ -508,7 +512,7 @@ static int ndefDecodePayloadHandoverRequest(uint8_t *payload, size_t len) { PrintAndLogEx(INFO, _CYAN_("Handover Request")); uint8_t *p = payload; uint8_t major = (*(p) >> 4) & 0x0F; - uint8_t minor = *(p) & 0x0F; + uint8_t minor = *(p) & 0x0F; p++; PrintAndLogEx(INFO, "Version....... " _YELLOW_("%u.%u"), major, minor); @@ -630,6 +634,7 @@ static const char *ndef_wifi_auth_lookup(uint8_t *d) { return ""; } + static int ndefDecodeMime_wifi_wsc(NDEFHeader_t *ndef) { if (ndef->PayloadLen == 0) { PrintAndLogEx(INFO, "no payload"); @@ -645,7 +650,7 @@ static int ndefDecodeMime_wifi_wsc(NDEFHeader_t *ndef) { if (ndef->Payload[pos] != 0x10) { n -= 1; - pos -= 1; + pos += 1; continue; } @@ -709,7 +714,7 @@ static int ndefDecodeMime_wifi_wsc(NDEFHeader_t *ndef) { pos += len; } - // NETWORK_IDX + // NETWORK_IDX - always set to 1, deprecated if (memcmp(&ndef->Payload[pos], "\x10\x26", 2) == 0) { // 10 26 00 01 01 uint8_t len = 3; @@ -771,19 +776,34 @@ static int ndefDecodeMime_wifi_wsc(NDEFHeader_t *ndef) { pos += 2; pos += len; } + + // rf-bands + if (memcmp(&ndef->Payload[pos], "\x10\x3C", 2) == 0) { + uint8_t len = 3; + + if (ndef->Payload[pos + 2 + 2] == 0x01) { + PrintAndLogEx(INFO, "RF Bands........ %s ( " _YELLOW_("2.4 GHZ")" )", sprint_hex(&ndef->Payload[pos + 2], len)); + } else if (ndef->Payload[pos + 2 + 2] == 0x02) { + PrintAndLogEx(INFO, "RF Bands........ %s ( " _YELLOW_("5.0 GHZ")" )", sprint_hex(&ndef->Payload[pos + 2], len)); + } + + n -= 2; + n -= len; + pos += 2; + pos += len; + } } /* ap-channel 0, 6 + credential device-name - mac-address + manufacturer model-name model-number + oob-password primary-device-type - rf-bands secondary-device-type-list serial-number ssid @@ -854,6 +874,26 @@ static int ndefDecodeMime_json(NDEFHeader_t *ndef) { return PM3_SUCCESS; } +static int ndefDecodeMime_bt_secure_le_oob(NDEFHeader_t *ndef) { + if (ndef->PayloadLen == 0) { + PrintAndLogEx(INFO, "no payload"); + return PM3_SUCCESS; + } + PrintAndLogEx(INFO, "Type............ " _YELLOW_("%.*s"), (int)ndef->TypeLen, ndef->Type); + PrintAndLogEx(INFO, "To be implemented. Feel free to contribute!"); + return PM3_SUCCESS; +} + +static int ndefDecodeMime_bt_le_oob(NDEFHeader_t *ndef) { + if (ndef->PayloadLen == 0) { + PrintAndLogEx(INFO, "no payload"); + return PM3_SUCCESS; + } + PrintAndLogEx(INFO, "Type............ " _YELLOW_("%.*s"), (int)ndef->TypeLen, ndef->Type); + PrintAndLogEx(INFO, "To be implemented. Feel free to contribute!"); + return PM3_SUCCESS; +} + static int ndefDecodeMime_bt(NDEFHeader_t *ndef) { if (ndef->PayloadLen == 0) { PrintAndLogEx(INFO, "no payload"); @@ -866,7 +906,7 @@ static int ndefDecodeMime_bt(NDEFHeader_t *ndef) { uint8_t rev[6] = {0}; reverse_array_copy(ndef->Payload + 2, 6, rev); PrintAndLogEx(INFO, "BT MAC.......... " _YELLOW_("%s"), sprint_hex(rev, sizeof(rev))); - + // Let's check payload[8]. Tells us a bit about the UUID's. If 0x07 then it tells us a service UUID is 128bit switch (ndef->Payload[8]) { case 0x02: @@ -903,31 +943,31 @@ static int ndefDecodeMime_bt(NDEFHeader_t *ndef) { return PM3_SUCCESS; } -// https://raw.githubusercontent.com/haldean/ndef/master/docs/NFCForum-TS-RTD_1.0.pdf +// https://raw.githubusercontent.com/haldean/ndef/master/docs/NFCForum-TS-RTD_1.0.pdf static int ndefDecodeExternal_record(NDEFHeader_t *ndef) { - + if (ndef->TypeLen == 0) { PrintAndLogEx(INFO, "no type"); return PM3_SUCCESS; } - + if (ndef->PayloadLen == 0) { PrintAndLogEx(INFO, "no payload"); return PM3_SUCCESS; } PrintAndLogEx(INFO - , " URN... " _GREEN_("urn:nfc:ext:%.*s") - , (int)ndef->TypeLen - , ndef->Type - ); + , " URN... " _GREEN_("urn:nfc:ext:%.*s") + , (int)ndef->TypeLen + , ndef->Type + ); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Payload [%zu]...", ndef->PayloadLen); print_hex_noascii_break(ndef->Payload, ndef->PayloadLen, 32); - + // do a character check? - if (!strncmp((char *)ndef->Type, "pilet.ee:ekaart:2", ndef->TypeLen)) { + if (!strncmp((char *)ndef->Type, "pilet.ee:ekaart:", ndef->TypeLen - 1)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, _GREEN_("Ekaart detected") " - Trying ASN1 decode..."); asn1_print(ndef->Payload, ndef->PayloadLen, " "); @@ -1023,9 +1063,18 @@ static int ndefDecodePayload(NDEFHeader_t *ndef, bool verbose) { if (str_startswith(begin, NDEF_VCARDTEXT) || str_startswith(begin, NDEF_XVCARDTEXT)) { ndefDecodeMime_vcard(ndef); } - if (str_startswith(begin, NDEF_BLUEAPPL)) { + + + if (str_startswith(begin, NDEF_BLUEAPPL_EP)) { ndefDecodeMime_bt(ndef); } + if (str_startswith(begin, NDEF_BLUEAPPL_SECURE_LE)) { + ndefDecodeMime_bt_secure_le_oob(ndef); + } + if (str_startswith(begin, NDEF_BLUEAPPL_LE)) { + ndefDecodeMime_bt_le_oob(ndef); + } + if (str_startswith(begin, NDEF_JSONAPPL)) { ndefDecodeMime_json(ndef); } @@ -1075,10 +1124,11 @@ static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen, b return res; ndefPrintHeader(&NDEFHeader, verbose); - PrintAndLogEx(INFO, ""); - PrintAndLogEx(INFO, _CYAN_("Payload info")); if (verbose) { + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, _CYAN_("Payload info")); + if (NDEFHeader.TypeLen) { PrintAndLogEx(INFO, "Type data"); print_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, 1); @@ -1148,6 +1198,7 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("NDEF parsing") " ----------------"); while (indx < ndefLen) { + switch (ndef[indx]) { case 0x00: { indx++; @@ -1212,8 +1263,9 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { PrintAndLogEx(SUCCESS, "Found NDEF message ( " _YELLOW_("%u") " bytes )", len); int res = NDEFRecordsDecodeAndPrint(&ndef[indx], len, verbose); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { return res; + } } indx += len; @@ -1229,16 +1281,48 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { break; } case 0xFE: { - PrintAndLogEx(SUCCESS, "NDEF Terminator detected"); + if (verbose) { + PrintAndLogEx(SUCCESS, "NDEF Terminator detected"); + } return PM3_SUCCESS; } default: { - if (verbose) + if (verbose) { PrintAndLogEx(ERR, "unknown tag 0x%02x", ndef[indx]); - + } return PM3_ESOFT; } } } return PM3_SUCCESS; } + + +int NDEFGetTotalLength(uint8_t *ndef, size_t ndeflen, size_t *outlen) { + + size_t idx = 0; + while (idx < ndeflen) { + + if (ndef[idx] == 0x00 || + ndef[idx] == 0x01 || + ndef[idx] == 0x02 || + ndef[idx] == 0x03 || + ndef[idx] == 0xFD) { + idx++; + idx += ndefTLVGetLength(&ndef[idx], &idx); + continue; + } + + if (ndef[idx] == 0xFE) { + idx++; + break; + } + + // invalid NDEF + *outlen = 0; + return PM3_ESOFT; + } + + *outlen = idx; + return PM3_SUCCESS; +} diff --git a/client/src/nfc/ndef.h b/client/src/nfc/ndef.h index 643bb70a7..31967c61a 100644 --- a/client/src/nfc/ndef.h +++ b/client/src/nfc/ndef.h @@ -76,5 +76,5 @@ typedef struct { int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose); int NDEFRecordsDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen, bool verbose); - +int NDEFGetTotalLength(uint8_t *ndef, size_t ndeflen, size_t *outlen); #endif // _NDEF_H_ diff --git a/client/src/pm3.c b/client/src/pm3.c index 1c0667ff2..7639ecc6d 100644 --- a/client/src/pm3.c +++ b/client/src/pm3.c @@ -31,15 +31,16 @@ pm3_device_t *pm3_open(const char *port) { pm3_init(); OpenProxmark(&g_session.current_device, port, false, 20, false, USART_BAUD_RATE); if (g_session.pm3_present && (TestProxmark(g_session.current_device) != PM3_SUCCESS)) { - PrintAndLogEx(ERR, _RED_("ERROR:") " cannot communicate with the Proxmark\n"); + PrintAndLogEx(ERR, _RED_("ERROR:") " cannot communicate with the Proxmark3\n"); CloseProxmark(g_session.current_device); } if ((port != NULL) && (!g_session.pm3_present)) exit(EXIT_FAILURE); - if (!g_session.pm3_present) - PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") " mode"); + if (!g_session.pm3_present) { + PrintAndLogEx(INFO, _RED_("OFFLINE") " mode"); + } // For now, there is no real device context: return g_session.current_device; } diff --git a/client/src/pm3line.c b/client/src/pm3line.c index 1bf4476e0..5c09c6135 100644 --- a/client/src/pm3line.c +++ b/client/src/pm3line.c @@ -27,7 +27,7 @@ #elif defined(HAVE_LINENOISE) #include "linenoise.h" #endif -#include "pm3line_vocabulory.h" +#include "pm3line_vocabulary.h" #include "pm3_cmd.h" #include "ui.h" // g_session #include "util.h" // str_ndup @@ -45,12 +45,12 @@ static char *rl_command_generator(const char *text, int state) { len = strlen(text); } - while ((command = vocabulory[index].name)) { + while ((command = vocabulary[index].name)) { // When no pm3 device present // and the command is not available offline, // we skip it. - if ((g_session.pm3_present == false) && (vocabulory[index].offline == false)) { + if ((g_session.pm3_present == false) && (vocabulary[index].offline == false)) { index++; continue; } @@ -82,12 +82,12 @@ static void ln_command_completion(const char *text, linenoiseCompletions *lc) { size_t prev_match_len = 0; size_t len = strlen(text); const char *command; - while ((command = vocabulory[index].name)) { + while ((command = vocabulary[index].name)) { // When no pm3 device present // and the command is not available offline, // we skip it. - if ((g_session.pm3_present == false) && (vocabulory[index].offline == false)) { + if ((g_session.pm3_present == false) && (vocabulary[index].offline == false)) { index++; continue; } diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h new file mode 100644 index 000000000..17c9c03e7 --- /dev/null +++ b/client/src/pm3line_vocabulary.h @@ -0,0 +1,832 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// readline auto complete utilities +//----------------------------------------------------------------------------- + +#ifndef PM3LINE_VOCABULARY_H__ +#define PM3LINE_VOCABULARY_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef struct vocabulary_s { + bool offline; + const char *name; +} vocabulary_t; + +const static vocabulary_t vocabulary[] = { + { 1, "help" }, + { 0, "auto" }, + { 1, "clear" }, + { 1, "hints" }, + { 1, "msleep" }, + { 1, "rem" }, + { 1, "quit" }, + { 1, "exit" }, + { 1, "prefs help" }, + { 1, "prefs show" }, + { 1, "prefs get barmode" }, + { 1, "prefs get client.debug" }, + { 1, "prefs get client.delay" }, + { 1, "prefs get client.timeout" }, + { 1, "prefs get color" }, + { 1, "prefs get savepaths" }, + { 1, "prefs get emoji" }, + { 1, "prefs get hints" }, + { 1, "prefs get output" }, + { 1, "prefs get plotsliders" }, + { 1, "prefs set help" }, + { 1, "prefs set barmode" }, + { 1, "prefs set client.debug" }, + { 1, "prefs set client.delay" }, + { 1, "prefs set client.timeout" }, + { 1, "prefs set color" }, + { 1, "prefs set emoji" }, + { 1, "prefs set hints" }, + { 1, "prefs set savepaths" }, + { 1, "prefs set output" }, + { 1, "prefs set plotsliders" }, + { 1, "analyse help" }, + { 1, "analyse lcr" }, + { 1, "analyse crc" }, + { 1, "analyse chksum" }, + { 1, "analyse dates" }, + { 1, "analyse lfsr" }, + { 1, "analyse a" }, + { 1, "analyse nuid" }, + { 1, "analyse demodbuff" }, + { 1, "analyse freq" }, + { 1, "analyse foo" }, + { 1, "analyse units" }, + { 1, "data help" }, + { 1, "data biphaserawdecode" }, + { 1, "data detectclock" }, + { 1, "data fsktonrz" }, + { 1, "data manrawdecode" }, + { 1, "data modulation" }, + { 1, "data rawdemod" }, + { 1, "data askedgedetect" }, + { 1, "data autocorr" }, + { 1, "data dirthreshold" }, + { 1, "data decimate" }, + { 1, "data envelope" }, + { 1, "data undecimate" }, + { 1, "data hide" }, + { 1, "data hpf" }, + { 1, "data iir" }, + { 1, "data grid" }, + { 1, "data ltrim" }, + { 1, "data mtrim" }, + { 1, "data norm" }, + { 1, "data plot" }, + { 1, "data cthreshold" }, + { 1, "data rtrim" }, + { 1, "data setgraphmarkers" }, + { 1, "data shiftgraphzero" }, + { 1, "data timescale" }, + { 1, "data zerocrossings" }, + { 1, "data convertbitstream" }, + { 1, "data getbitstream" }, + { 1, "data asn1" }, + { 1, "data atr" }, + { 1, "data bin2hex" }, + { 0, "data bitsamples" }, + { 1, "data bmap" }, + { 1, "data clear" }, + { 1, "data diff" }, + { 0, "data hexsamples" }, + { 1, "data hex2bin" }, + { 1, "data load" }, + { 1, "data num" }, + { 1, "data print" }, + { 0, "data samples" }, + { 1, "data save" }, + { 1, "data setdebugmode" }, + { 0, "data tune" }, + { 1, "emv help" }, + { 1, "emv list" }, + { 1, "emv test" }, + { 0, "emv challenge" }, + { 0, "emv exec" }, + { 0, "emv genac" }, + { 0, "emv gpo" }, + { 0, "emv intauth" }, + { 0, "emv pse" }, + { 0, "emv reader" }, + { 0, "emv readrec" }, + { 0, "emv roca" }, + { 0, "emv scan" }, + { 0, "emv search" }, + { 0, "emv select" }, + { 1, "hf help" }, + { 1, "hf list" }, + { 0, "hf plot" }, + { 0, "hf tune" }, + { 1, "hf search" }, + { 0, "hf sniff" }, + { 1, "hf 14a help" }, + { 1, "hf 14a list" }, + { 0, "hf 14a antifuzz" }, + { 0, "hf 14a config" }, + { 0, "hf 14a cuids" }, + { 0, "hf 14a info" }, + { 0, "hf 14a sim" }, + { 0, "hf 14a sniff" }, + { 0, "hf 14a raw" }, + { 0, "hf 14a reader" }, + { 0, "hf 14a apdu" }, + { 0, "hf 14a apdufind" }, + { 0, "hf 14a chaining" }, + { 0, "hf 14a ndefformat" }, + { 0, "hf 14a ndefread" }, + { 0, "hf 14a ndefwrite" }, + { 1, "hf 14b help" }, + { 0, "hf 14b apdu" }, + { 0, "hf 14b dump" }, + { 0, "hf 14b info" }, + { 1, "hf 14b list" }, + { 0, "hf 14b ndefread" }, + { 0, "hf 14b raw" }, + { 0, "hf 14b reader" }, + { 0, "hf 14b sim" }, + { 0, "hf 14b sniff" }, + { 0, "hf 14b rdbl" }, + { 0, "hf 14b sriwrite" }, + { 1, "hf 14b view" }, + { 1, "hf 15 help" }, + { 1, "hf 15 list" }, + { 1, "hf 15 demod" }, + { 0, "hf 15 dump" }, + { 0, "hf 15 info" }, + { 0, "hf 15 sniff" }, + { 0, "hf 15 raw" }, + { 0, "hf 15 rdbl" }, + { 0, "hf 15 rdmulti" }, + { 0, "hf 15 reader" }, + { 0, "hf 15 restore" }, + { 0, "hf 15 samples" }, + { 1, "hf 15 view" }, + { 0, "hf 15 wrbl" }, + { 0, "hf 15 sim" }, + { 0, "hf 15 eload" }, + { 0, "hf 15 esave" }, + { 0, "hf 15 eview" }, + { 0, "hf 15 slixwritepwd" }, + { 0, "hf 15 slixeasdisable" }, + { 0, "hf 15 slixeasenable" }, + { 0, "hf 15 slixprivacydisable" }, + { 0, "hf 15 slixprivacyenable" }, + { 0, "hf 15 passprotectafi" }, + { 0, "hf 15 passprotecteas" }, + { 0, "hf 15 findafi" }, + { 0, "hf 15 writeafi" }, + { 0, "hf 15 writedsfid" }, + { 0, "hf 15 csetuid" }, + { 1, "hf cipurse help" }, + { 0, "hf cipurse info" }, + { 0, "hf cipurse select" }, + { 0, "hf cipurse auth" }, + { 0, "hf cipurse read" }, + { 0, "hf cipurse write" }, + { 0, "hf cipurse aread" }, + { 0, "hf cipurse awrite" }, + { 0, "hf cipurse formatall" }, + { 0, "hf cipurse create" }, + { 0, "hf cipurse delete" }, + { 0, "hf cipurse updkey" }, + { 0, "hf cipurse updakey" }, + { 0, "hf cipurse default" }, + { 1, "hf cipurse test" }, + { 1, "hf epa help" }, + { 0, "hf epa cnonces" }, + { 0, "hf epa replay" }, + { 0, "hf epa sim" }, + { 1, "hf emrtd help" }, + { 0, "hf emrtd dump" }, + { 1, "hf emrtd info" }, + { 1, "hf emrtd list" }, + { 1, "hf felica help" }, + { 1, "hf felica list" }, + { 0, "hf felica reader" }, + { 0, "hf felica info" }, + { 0, "hf felica sniff" }, + { 0, "hf felica raw" }, + { 0, "hf felica rdbl" }, + { 0, "hf felica wrbl" }, + { 0, "hf felica rqservice" }, + { 0, "hf felica rqresponse" }, + { 0, "hf felica scsvcode" }, + { 0, "hf felica rqsyscode" }, + { 0, "hf felica auth1" }, + { 0, "hf felica auth2" }, + { 0, "hf felica rqspecver" }, + { 0, "hf felica resetmode" }, + { 0, "hf felica litesim" }, + { 0, "hf felica litedump" }, + { 1, "hf fido help" }, + { 1, "hf fido list" }, + { 0, "hf fido info" }, + { 0, "hf fido reg" }, + { 0, "hf fido auth" }, + { 0, "hf fido make" }, + { 0, "hf fido assert" }, + { 1, "hf fudan help" }, + { 0, "hf fudan reader" }, + { 0, "hf fudan dump" }, + { 0, "hf fudan rdbl" }, + { 1, "hf fudan view" }, + { 0, "hf fudan wrbl" }, + { 1, "hf gallagher help" }, + { 0, "hf gallagher reader" }, + { 0, "hf gallagher clone" }, + { 0, "hf gallagher delete" }, + { 1, "hf gallagher diversifykey" }, + { 1, "hf gallagher decode" }, + { 1, "hf ksx6924 help" }, + { 0, "hf ksx6924 select" }, + { 0, "hf ksx6924 info" }, + { 0, "hf ksx6924 balance" }, + { 0, "hf ksx6924 init" }, + { 0, "hf ksx6924 prec" }, + { 1, "hf jooki help" }, + { 0, "hf jooki clone" }, + { 1, "hf jooki decode" }, + { 1, "hf jooki encode" }, + { 0, "hf jooki sim" }, + { 1, "hf iclass help" }, + { 1, "hf iclass list" }, + { 0, "hf iclass dump" }, + { 0, "hf iclass info" }, + { 0, "hf iclass rdbl" }, + { 0, "hf iclass reader" }, + { 0, "hf iclass restore" }, + { 0, "hf iclass sniff" }, + { 1, "hf iclass view" }, + { 0, "hf iclass wrbl" }, + { 0, "hf iclass creditepurse" }, + { 0, "hf iclass chk" }, + { 1, "hf iclass loclass" }, + { 1, "hf iclass lookup" }, + { 0, "hf iclass sim" }, + { 0, "hf iclass eload" }, + { 0, "hf iclass esave" }, + { 0, "hf iclass esetblk" }, + { 0, "hf iclass eview" }, + { 0, "hf iclass configcard" }, + { 1, "hf iclass calcnewkey" }, + { 1, "hf iclass encode" }, + { 1, "hf iclass encrypt" }, + { 1, "hf iclass decrypt" }, + { 1, "hf iclass managekeys" }, + { 1, "hf iclass permutekey" }, + { 0, "hf iclass sam" }, + { 1, "hf legic help" }, + { 0, "hf legic dump" }, + { 0, "hf legic info" }, + { 1, "hf legic list" }, + { 0, "hf legic rdbl" }, + { 0, "hf legic reader" }, + { 0, "hf legic restore" }, + { 0, "hf legic wipe" }, + { 0, "hf legic wrbl" }, + { 0, "hf legic sim" }, + { 0, "hf legic eload" }, + { 0, "hf legic esave" }, + { 0, "hf legic eview" }, + { 0, "hf legic einfo" }, + { 1, "hf legic crc" }, + { 1, "hf legic view" }, + { 1, "hf lto help" }, + { 0, "hf lto dump" }, + { 0, "hf lto info" }, + { 1, "hf lto list" }, + { 0, "hf lto rdbl" }, + { 0, "hf lto reader" }, + { 0, "hf lto restore" }, + { 0, "hf lto wrbl" }, + { 1, "hf mf help" }, + { 1, "hf mf list" }, + { 0, "hf mf info" }, + { 0, "hf mf darkside" }, + { 0, "hf mf nested" }, + { 1, "hf mf hardnested" }, + { 0, "hf mf staticnested" }, + { 0, "hf mf autopwn" }, + { 0, "hf mf nack" }, + { 0, "hf mf chk" }, + { 0, "hf mf fchk" }, + { 1, "hf mf decrypt" }, + { 0, "hf mf supercard" }, + { 0, "hf mf auth4" }, + { 1, "hf mf acl" }, + { 0, "hf mf dump" }, + { 1, "hf mf mad" }, + { 0, "hf mf personalize" }, + { 0, "hf mf rdbl" }, + { 0, "hf mf rdsc" }, + { 0, "hf mf restore" }, + { 0, "hf mf setmod" }, + { 1, "hf mf value" }, + { 1, "hf mf view" }, + { 0, "hf mf wipe" }, + { 0, "hf mf wrbl" }, + { 0, "hf mf sim" }, + { 0, "hf mf ecfill" }, + { 0, "hf mf eclr" }, + { 0, "hf mf egetblk" }, + { 0, "hf mf egetsc" }, + { 0, "hf mf ekeyprn" }, + { 0, "hf mf eload" }, + { 0, "hf mf esave" }, + { 0, "hf mf esetblk" }, + { 0, "hf mf eview" }, + { 0, "hf mf cgetblk" }, + { 0, "hf mf cgetsc" }, + { 0, "hf mf cload" }, + { 0, "hf mf csave" }, + { 0, "hf mf csetblk" }, + { 0, "hf mf csetuid" }, + { 0, "hf mf cview" }, + { 0, "hf mf cwipe" }, + { 0, "hf mf gen3uid" }, + { 0, "hf mf gen3blk" }, + { 0, "hf mf gen3freeze" }, + { 0, "hf mf ginfo" }, + { 0, "hf mf ggetblk" }, + { 0, "hf mf gload" }, + { 0, "hf mf gsave" }, + { 0, "hf mf gsetblk" }, + { 0, "hf mf gview" }, + { 0, "hf mf gchpwd" }, + { 0, "hf mf gdmcfg" }, + { 0, "hf mf gdmsetcfg" }, + { 0, "hf mf gdmsetblk" }, + { 0, "hf mf ndefformat" }, + { 0, "hf mf ndefread" }, + { 0, "hf mf ndefwrite" }, + { 0, "hf mf encodehid" }, + { 1, "hf mfp help" }, + { 1, "hf mfp list" }, + { 0, "hf mfp auth" }, + { 0, "hf mfp chk" }, + { 0, "hf mfp dump" }, + { 0, "hf mfp info" }, + { 0, "hf mfp mad" }, + { 0, "hf mfp rdbl" }, + { 0, "hf mfp rdsc" }, + { 0, "hf mfp wrbl" }, + { 0, "hf mfp chkey" }, + { 0, "hf mfp chconf" }, + { 0, "hf mfp commitp" }, + { 0, "hf mfp initp" }, + { 0, "hf mfp wrp" }, + { 0, "hf mfp ndefformat" }, + { 0, "hf mfp ndefread" }, + { 0, "hf mfp ndefwrite" }, + { 1, "hf mfu help" }, + { 1, "hf mfu list" }, + { 1, "hf mfu keygen" }, + { 1, "hf mfu pwdgen" }, + { 0, "hf mfu otptear" }, + { 0, "hf mfu cauth" }, + { 0, "hf mfu dump" }, + { 0, "hf mfu info" }, + { 0, "hf mfu ndefread" }, + { 0, "hf mfu rdbl" }, + { 0, "hf mfu restore" }, + { 1, "hf mfu view" }, + { 0, "hf mfu wrbl" }, + { 0, "hf mfu tamper" }, + { 0, "hf mfu eload" }, + { 0, "hf mfu esave" }, + { 0, "hf mfu eview" }, + { 0, "hf mfu sim" }, + { 0, "hf mfu setpwd" }, + { 0, "hf mfu setuid" }, + { 1, "hf mfdes help" }, + { 0, "hf mfdes info" }, + { 0, "hf mfdes getuid" }, + { 0, "hf mfdes default" }, + { 0, "hf mfdes auth" }, + { 0, "hf mfdes chk" }, + { 0, "hf mfdes detect" }, + { 0, "hf mfdes freemem" }, + { 0, "hf mfdes setconfig" }, + { 0, "hf mfdes formatpicc" }, + { 1, "hf mfdes list" }, + { 0, "hf mfdes mad" }, + { 0, "hf mfdes lsapp" }, + { 0, "hf mfdes getaids" }, + { 0, "hf mfdes getappnames" }, + { 0, "hf mfdes bruteaid" }, + { 0, "hf mfdes createapp" }, + { 0, "hf mfdes deleteapp" }, + { 0, "hf mfdes selectapp" }, + { 0, "hf mfdes changekey" }, + { 0, "hf mfdes chkeysettings" }, + { 0, "hf mfdes getkeysettings" }, + { 0, "hf mfdes getkeyversions" }, + { 0, "hf mfdes getfileids" }, + { 0, "hf mfdes getfileisoids" }, + { 0, "hf mfdes lsfiles" }, + { 0, "hf mfdes dump" }, + { 0, "hf mfdes createfile" }, + { 0, "hf mfdes createvaluefile" }, + { 0, "hf mfdes createrecordfile" }, + { 0, "hf mfdes createmacfile" }, + { 0, "hf mfdes deletefile" }, + { 0, "hf mfdes getfilesettings" }, + { 0, "hf mfdes chfilesettings" }, + { 0, "hf mfdes read" }, + { 0, "hf mfdes write" }, + { 0, "hf mfdes value" }, + { 0, "hf mfdes clearrecfile" }, + { 1, "hf mfdes test" }, + { 1, "hf ntag424 help" }, + { 0, "hf ntag424 info" }, + { 1, "hf ntag424 view" }, + { 0, "hf ntag424 auth" }, + { 0, "hf ntag424 read" }, + { 0, "hf ntag424 write" }, + { 0, "hf ntag424 getfs" }, + { 0, "hf ntag424 changefs" }, + { 0, "hf ntag424 changekey" }, + { 1, "hf seos help" }, + { 0, "hf seos info" }, + { 1, "hf seos list" }, + { 1, "hf st25ta help" }, + { 0, "hf st25ta info" }, + { 1, "hf st25ta list" }, + { 1, "hf st25ta ndefread" }, + { 0, "hf st25ta protect" }, + { 0, "hf st25ta pwd" }, + { 0, "hf st25ta sim" }, + { 1, "hf tesla help" }, + { 0, "hf tesla info" }, + { 1, "hf tesla list" }, + { 1, "hf texkom help" }, + { 0, "hf texkom reader" }, + { 0, "hf texkom sim" }, + { 1, "hf thinfilm help" }, + { 0, "hf thinfilm info" }, + { 1, "hf thinfilm list" }, + { 0, "hf thinfilm sim" }, + { 1, "hf topaz help" }, + { 1, "hf topaz list" }, + { 0, "hf topaz dump" }, + { 0, "hf topaz info" }, + { 0, "hf topaz raw" }, + { 0, "hf topaz rdbl" }, + { 0, "hf topaz reader" }, + { 0, "hf topaz sim" }, + { 0, "hf topaz sniff" }, + { 1, "hf topaz view" }, + { 0, "hf topaz wrbl" }, + { 1, "hf vas help" }, + { 0, "hf vas reader" }, + { 1, "hf vas decrypt" }, + { 1, "hf waveshare help" }, + { 0, "hf waveshare loadbmp" }, + { 1, "hf xerox help" }, + { 0, "hf xerox info" }, + { 0, "hf xerox reader" }, + { 0, "hf xerox dump" }, + { 1, "hw help" }, + { 0, "hw break" }, + { 0, "hw bootloader" }, + { 1, "hw connect" }, + { 0, "hw dbg" }, + { 0, "hw detectreader" }, + { 0, "hw fpgaoff" }, + { 0, "hw lcd" }, + { 0, "hw lcdreset" }, + { 0, "hw ping" }, + { 0, "hw readmem" }, + { 0, "hw reset" }, + { 0, "hw setlfdivisor" }, + { 0, "hw setmux" }, + { 0, "hw standalone" }, + { 0, "hw status" }, + { 0, "hw tearoff" }, + { 0, "hw tia" }, + { 1, "hw timeout" }, + { 0, "hw tune" }, + { 1, "hw version" }, + { 1, "lf help" }, + { 0, "lf config" }, + { 0, "lf cmdread" }, + { 0, "lf read" }, + { 1, "lf search" }, + { 0, "lf sim" }, + { 0, "lf simask" }, + { 0, "lf simfsk" }, + { 0, "lf simpsk" }, + { 0, "lf simbidir" }, + { 0, "lf sniff" }, + { 0, "lf tune" }, + { 1, "lf awid help" }, + { 1, "lf awid demod" }, + { 0, "lf awid reader" }, + { 0, "lf awid clone" }, + { 0, "lf awid sim" }, + { 0, "lf awid brute" }, + { 0, "lf awid watch" }, + { 1, "lf cotag help" }, + { 1, "lf cotag demod" }, + { 0, "lf cotag reader" }, + { 1, "lf destron help" }, + { 1, "lf destron demod" }, + { 0, "lf destron reader" }, + { 0, "lf destron clone" }, + { 0, "lf destron sim" }, + { 1, "lf em help" }, + { 1, "lf em 410x help" }, + { 1, "lf em 410x demod" }, + { 0, "lf em 410x reader" }, + { 0, "lf em 410x sim" }, + { 0, "lf em 410x brute" }, + { 0, "lf em 410x watch" }, + { 0, "lf em 410x spoof" }, + { 0, "lf em 410x clone" }, + { 1, "lf em 4x05 help" }, + { 0, "lf em 4x05 brute" }, + { 0, "lf em 4x05 chk" }, + { 1, "lf em 4x05 demod" }, + { 0, "lf em 4x05 dump" }, + { 0, "lf em 4x05 info" }, + { 0, "lf em 4x05 read" }, + { 1, "lf em 4x05 sniff" }, + { 0, "lf em 4x05 unlock" }, + { 0, "lf em 4x05 wipe" }, + { 0, "lf em 4x05 write" }, + { 1, "lf em 4x50 help" }, + { 0, "lf em 4x50 brute" }, + { 0, "lf em 4x50 chk" }, + { 0, "lf em 4x50 dump" }, + { 0, "lf em 4x50 info" }, + { 0, "lf em 4x50 login" }, + { 0, "lf em 4x50 rdbl" }, + { 0, "lf em 4x50 reader" }, + { 0, "lf em 4x50 restore" }, + { 0, "lf em 4x50 wrbl" }, + { 0, "lf em 4x50 wrpwd" }, + { 0, "lf em 4x50 wipe" }, + { 0, "lf em 4x50 eload" }, + { 0, "lf em 4x50 esave" }, + { 0, "lf em 4x50 eview" }, + { 0, "lf em 4x50 sim" }, + { 1, "lf em 4x70 help" }, + { 0, "lf em 4x70 brute" }, + { 0, "lf em 4x70 info" }, + { 0, "lf em 4x70 write" }, + { 0, "lf em 4x70 unlock" }, + { 0, "lf em 4x70 auth" }, + { 0, "lf em 4x70 writepin" }, + { 0, "lf em 4x70 writekey" }, + { 1, "lf fdxb help" }, + { 1, "lf fdxb demod" }, + { 0, "lf fdxb reader" }, + { 0, "lf fdxb clone" }, + { 0, "lf fdxb sim" }, + { 1, "lf gallagher help" }, + { 1, "lf gallagher demod" }, + { 0, "lf gallagher reader" }, + { 0, "lf gallagher clone" }, + { 0, "lf gallagher sim" }, + { 1, "lf gproxii help" }, + { 1, "lf gproxii demod" }, + { 0, "lf gproxii reader" }, + { 0, "lf gproxii clone" }, + { 0, "lf gproxii sim" }, + { 1, "lf hid help" }, + { 1, "lf hid demod" }, + { 0, "lf hid reader" }, + { 0, "lf hid clone" }, + { 0, "lf hid sim" }, + { 0, "lf hid brute" }, + { 0, "lf hid watch" }, + { 1, "lf hitag help" }, + { 1, "lf hitag list" }, + { 0, "lf hitag info" }, + { 0, "lf hitag dump" }, + { 0, "lf hitag read" }, + { 0, "lf hitag wrbl" }, + { 0, "lf hitag sniff" }, + { 0, "lf hitag cc" }, + { 0, "lf hitag ta" }, + { 0, "lf hitag eload" }, + { 0, "lf hitag sim" }, + { 1, "lf idteck help" }, + { 1, "lf idteck demod" }, + { 0, "lf idteck reader" }, + { 0, "lf idteck clone" }, + { 0, "lf idteck sim" }, + { 1, "lf indala help" }, + { 0, "lf indala brute" }, + { 1, "lf indala demod" }, + { 1, "lf indala altdemod" }, + { 0, "lf indala reader" }, + { 0, "lf indala clone" }, + { 0, "lf indala sim" }, + { 1, "lf io help" }, + { 1, "lf io demod" }, + { 0, "lf io reader" }, + { 0, "lf io clone" }, + { 0, "lf io sim" }, + { 0, "lf io watch" }, + { 1, "lf jablotron help" }, + { 1, "lf jablotron demod" }, + { 0, "lf jablotron reader" }, + { 0, "lf jablotron clone" }, + { 0, "lf jablotron sim" }, + { 1, "lf keri help" }, + { 1, "lf keri demod" }, + { 0, "lf keri reader" }, + { 0, "lf keri clone" }, + { 0, "lf keri sim" }, + { 1, "lf motorola help" }, + { 1, "lf motorola demod" }, + { 0, "lf motorola reader" }, + { 0, "lf motorola clone" }, + { 0, "lf motorola sim" }, + { 1, "lf nedap help" }, + { 1, "lf nedap demod" }, + { 0, "lf nedap reader" }, + { 0, "lf nedap clone" }, + { 0, "lf nedap sim" }, + { 1, "lf nexwatch help" }, + { 1, "lf nexwatch demod" }, + { 0, "lf nexwatch reader" }, + { 0, "lf nexwatch clone" }, + { 0, "lf nexwatch sim" }, + { 1, "lf noralsy help" }, + { 1, "lf noralsy demod" }, + { 0, "lf noralsy reader" }, + { 0, "lf noralsy clone" }, + { 0, "lf noralsy sim" }, + { 1, "lf pac help" }, + { 1, "lf pac demod" }, + { 0, "lf pac reader" }, + { 0, "lf pac clone" }, + { 0, "lf pac sim" }, + { 1, "lf paradox help" }, + { 1, "lf paradox demod" }, + { 0, "lf paradox reader" }, + { 0, "lf paradox clone" }, + { 0, "lf paradox sim" }, + { 1, "lf pcf7931 help" }, + { 0, "lf pcf7931 reader" }, + { 0, "lf pcf7931 write" }, + { 1, "lf pcf7931 config" }, + { 1, "lf presco help" }, + { 1, "lf presco demod" }, + { 0, "lf presco reader" }, + { 0, "lf presco clone" }, + { 0, "lf presco sim" }, + { 1, "lf pyramid help" }, + { 1, "lf pyramid demod" }, + { 0, "lf pyramid reader" }, + { 0, "lf pyramid clone" }, + { 0, "lf pyramid sim" }, + { 1, "lf securakey help" }, + { 1, "lf securakey demod" }, + { 0, "lf securakey reader" }, + { 0, "lf securakey clone" }, + { 0, "lf securakey sim" }, + { 1, "lf ti help" }, + { 1, "lf ti demod" }, + { 0, "lf ti reader" }, + { 0, "lf ti write" }, + { 1, "lf t55xx help" }, + { 0, "lf t55xx clonehelp" }, + { 1, "lf t55xx config" }, + { 0, "lf t55xx dangerraw" }, + { 1, "lf t55xx detect" }, + { 0, "lf t55xx deviceconfig" }, + { 0, "lf t55xx dump" }, + { 1, "lf t55xx info" }, + { 0, "lf t55xx p1detect" }, + { 0, "lf t55xx read" }, + { 0, "lf t55xx resetread" }, + { 0, "lf t55xx restore" }, + { 1, "lf t55xx trace" }, + { 0, "lf t55xx wakeup" }, + { 0, "lf t55xx write" }, + { 0, "lf t55xx bruteforce" }, + { 0, "lf t55xx chk" }, + { 0, "lf t55xx protect" }, + { 0, "lf t55xx recoverpw" }, + { 1, "lf t55xx sniff" }, + { 0, "lf t55xx special" }, + { 0, "lf t55xx wipe" }, + { 1, "lf viking help" }, + { 1, "lf viking demod" }, + { 0, "lf viking reader" }, + { 0, "lf viking clone" }, + { 0, "lf viking sim" }, + { 1, "lf visa2000 help" }, + { 1, "lf visa2000 demod" }, + { 0, "lf visa2000 reader" }, + { 0, "lf visa2000 clone" }, + { 0, "lf visa2000 sim" }, + { 1, "mem help" }, + { 0, "mem baudrate" }, + { 0, "mem dump" }, + { 0, "mem info" }, + { 0, "mem load" }, + { 0, "mem wipe" }, + { 1, "mem spiffs help" }, + { 0, "mem spiffs copy" }, + { 0, "mem spiffs check" }, + { 0, "mem spiffs dump" }, + { 0, "mem spiffs info" }, + { 0, "mem spiffs mount" }, + { 0, "mem spiffs remove" }, + { 0, "mem spiffs rename" }, + { 0, "mem spiffs test" }, + { 0, "mem spiffs tree" }, + { 0, "mem spiffs unmount" }, + { 0, "mem spiffs upload" }, + { 0, "mem spiffs view" }, + { 0, "mem spiffs wipe" }, + { 1, "nfc help" }, + { 1, "nfc decode" }, + { 0, "nfc type1 read" }, + { 1, "nfc type1 help" }, + { 0, "nfc type2 read" }, + { 1, "nfc type2 help" }, + { 0, "nfc type4a format" }, + { 0, "nfc type4a read" }, + { 0, "nfc type4a write" }, + { 0, "nfc type4a st25taread" }, + { 1, "nfc type4a help" }, + { 0, "nfc type4b read" }, + { 1, "nfc type4b help" }, + { 0, "nfc mf cformat" }, + { 0, "nfc mf cread" }, + { 0, "nfc mf cwrite" }, + { 0, "nfc mf pread" }, + { 1, "nfc mf help" }, + { 0, "nfc barcode read" }, + { 0, "nfc barcode sim" }, + { 1, "nfc barcode help" }, + { 1, "piv help" }, + { 0, "piv select" }, + { 0, "piv getdata" }, + { 0, "piv authsign" }, + { 0, "piv scan" }, + { 1, "piv list" }, + { 1, "smart help" }, + { 1, "smart list" }, + { 0, "smart info" }, + { 0, "smart relay" }, + { 0, "smart reader" }, + { 0, "smart raw" }, + { 1, "smart upgrade" }, + { 0, "smart setclock" }, + { 0, "smart brute" }, + { 1, "script help" }, + { 1, "script list" }, + { 1, "script run" }, + { 1, "trace help" }, + { 1, "trace extract" }, + { 1, "trace list" }, + { 1, "trace load" }, + { 1, "trace save" }, + { 1, "usart help" }, + { 0, "usart btpin" }, + { 0, "usart btfactory" }, + { 0, "usart tx" }, + { 0, "usart rx" }, + { 0, "usart txrx" }, + { 0, "usart txhex" }, + { 0, "usart rxhex" }, + { 0, "usart config" }, + { 1, "wiegand help" }, + { 1, "wiegand list" }, + { 1, "wiegand encode" }, + { 1, "wiegand decode" }, + {0, NULL} +}; + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/client/src/pm3line_vocabulory.h b/client/src/pm3line_vocabulory.h deleted file mode 100644 index e6d6dc438..000000000 --- a/client/src/pm3line_vocabulory.h +++ /dev/null @@ -1,797 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// See LICENSE.txt for the text of the license. -//----------------------------------------------------------------------------- -// readline auto complete utilities -//----------------------------------------------------------------------------- - -#ifndef PM3LINE_VOCABULORY_H__ -#define PM3LINE_VOCABULORY_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef struct vocabulory_s { - bool offline; - const char *name; -} vocabulory_t; - -const static vocabulory_t vocabulory[] = { - { 1, "help" }, - { 0, "auto" }, - { 1, "clear" }, - { 1, "hints" }, - { 1, "msleep" }, - { 1, "rem" }, - { 1, "quit" }, - { 1, "exit" }, - { 1, "prefs help" }, - { 1, "prefs show" }, - { 1, "prefs get barmode" }, - { 1, "prefs get clientdebug" }, - { 1, "prefs get clientdelay" }, - { 1, "prefs get color" }, - { 1, "prefs get savepaths" }, - { 1, "prefs get emoji" }, - { 1, "prefs get hints" }, - { 1, "prefs get output" }, - { 1, "prefs get plotsliders" }, - { 1, "prefs set help" }, - { 1, "prefs set barmode" }, - { 1, "prefs set clientdebug" }, - { 1, "prefs set clientdelay" }, - { 1, "prefs set color" }, - { 1, "prefs set emoji" }, - { 1, "prefs set hints" }, - { 1, "prefs set savepaths" }, - { 1, "prefs set output" }, - { 1, "prefs set plotsliders" }, - { 1, "analyse help" }, - { 1, "analyse lcr" }, - { 1, "analyse crc" }, - { 1, "analyse chksum" }, - { 1, "analyse dates" }, - { 1, "analyse lfsr" }, - { 1, "analyse a" }, - { 1, "analyse nuid" }, - { 1, "analyse demodbuff" }, - { 1, "analyse freq" }, - { 1, "analyse foo" }, - { 1, "analyse units" }, - { 1, "data help" }, - { 1, "data biphaserawdecode" }, - { 1, "data detectclock" }, - { 1, "data fsktonrz" }, - { 1, "data manrawdecode" }, - { 1, "data modulation" }, - { 1, "data rawdemod" }, - { 1, "data askedgedetect" }, - { 1, "data autocorr" }, - { 1, "data dirthreshold" }, - { 1, "data decimate" }, - { 1, "data undecimate" }, - { 1, "data hide" }, - { 1, "data hpf" }, - { 1, "data iir" }, - { 1, "data grid" }, - { 1, "data ltrim" }, - { 1, "data mtrim" }, - { 1, "data norm" }, - { 1, "data plot" }, - { 1, "data rtrim" }, - { 1, "data setgraphmarkers" }, - { 1, "data shiftgraphzero" }, - { 1, "data timescale" }, - { 1, "data zerocrossings" }, - { 1, "data convertbitstream" }, - { 1, "data getbitstream" }, - { 1, "data asn1" }, - { 1, "data bin2hex" }, - { 0, "data bitsamples" }, - { 1, "data clear" }, - { 1, "data diff" }, - { 0, "data hexsamples" }, - { 1, "data hex2bin" }, - { 1, "data load" }, - { 1, "data num" }, - { 1, "data print" }, - { 0, "data samples" }, - { 1, "data save" }, - { 1, "data setdebugmode" }, - { 0, "data tune" }, - { 1, "emv help" }, - { 0, "emv exec" }, - { 0, "emv pse" }, - { 0, "emv search" }, - { 0, "emv select" }, - { 0, "emv gpo" }, - { 0, "emv readrec" }, - { 0, "emv genac" }, - { 0, "emv challenge" }, - { 0, "emv intauth" }, - { 0, "emv scan" }, - { 1, "emv test" }, - { 1, "emv list" }, - { 0, "emv roca" }, - { 1, "hf help" }, - { 1, "hf list" }, - { 0, "hf plot" }, - { 0, "hf tune" }, - { 1, "hf search" }, - { 0, "hf sniff" }, - { 1, "hf 14a help" }, - { 1, "hf 14a list" }, - { 0, "hf 14a antifuzz" }, - { 0, "hf 14a config" }, - { 0, "hf 14a cuids" }, - { 0, "hf 14a info" }, - { 0, "hf 14a sim" }, - { 0, "hf 14a sniff" }, - { 0, "hf 14a raw" }, - { 0, "hf 14a reader" }, - { 0, "hf 14a apdu" }, - { 0, "hf 14a apdufind" }, - { 0, "hf 14a chaining" }, - { 0, "hf 14a ndefformat" }, - { 0, "hf 14a ndefread" }, - { 0, "hf 14a ndefwrite" }, - { 1, "hf 14b help" }, - { 0, "hf 14b apdu" }, - { 0, "hf 14b dump" }, - { 0, "hf 14b info" }, - { 1, "hf 14b list" }, - { 0, "hf 14b ndefread" }, - { 0, "hf 14b raw" }, - { 0, "hf 14b reader" }, - { 0, "hf 14b sim" }, - { 0, "hf 14b sniff" }, - { 0, "hf 14b rdbl" }, - { 0, "hf 14b sriwrite" }, - { 1, "hf 14b view" }, - { 1, "hf 15 help" }, - { 1, "hf 15 list" }, - { 1, "hf 15 demod" }, - { 0, "hf 15 dump" }, - { 0, "hf 15 info" }, - { 0, "hf 15 sniff" }, - { 0, "hf 15 raw" }, - { 0, "hf 15 rdbl" }, - { 0, "hf 15 rdmulti" }, - { 0, "hf 15 reader" }, - { 0, "hf 15 restore" }, - { 0, "hf 15 samples" }, - { 0, "hf 15 eload" }, - { 0, "hf 15 esave" }, - { 0, "hf 15 eview" }, - { 0, "hf 15 sim" }, - { 0, "hf 15 slixwritepwd" }, - { 0, "hf 15 slixeasdisable" }, - { 0, "hf 15 slixeasenable" }, - { 0, "hf 15 slixprivacydisable" }, - { 0, "hf 15 slixprivacyenable" }, - { 0, "hf 15 passprotectafi" }, - { 0, "hf 15 passprotecteas" }, - { 0, "hf 15 wrbl" }, - { 0, "hf 15 findafi" }, - { 0, "hf 15 writeafi" }, - { 0, "hf 15 writedsfid" }, - { 0, "hf 15 csetuid" }, - { 1, "hf cipurse help" }, - { 0, "hf cipurse info" }, - { 0, "hf cipurse select" }, - { 0, "hf cipurse auth" }, - { 0, "hf cipurse read" }, - { 0, "hf cipurse write" }, - { 0, "hf cipurse aread" }, - { 0, "hf cipurse awrite" }, - { 0, "hf cipurse formatall" }, - { 0, "hf cipurse create" }, - { 0, "hf cipurse delete" }, - { 0, "hf cipurse updkey" }, - { 0, "hf cipurse updakey" }, - { 0, "hf cipurse default" }, - { 1, "hf cipurse test" }, - { 1, "hf epa help" }, - { 0, "hf epa cnonces" }, - { 0, "hf epa replay" }, - { 0, "hf epa sim" }, - { 1, "hf emrtd help" }, - { 0, "hf emrtd dump" }, - { 1, "hf emrtd info" }, - { 1, "hf emrtd list" }, - { 1, "hf felica help" }, - { 1, "hf felica list" }, - { 0, "hf felica reader" }, - { 0, "hf felica info" }, - { 0, "hf felica sniff" }, - { 0, "hf felica raw" }, - { 0, "hf felica rdbl" }, - { 0, "hf felica wrbl" }, - { 0, "hf felica rqservice" }, - { 0, "hf felica rqresponse" }, - { 0, "hf felica scsvcode" }, - { 0, "hf felica rqsyscode" }, - { 0, "hf felica auth1" }, - { 0, "hf felica auth2" }, - { 0, "hf felica rqspecver" }, - { 0, "hf felica resetmode" }, - { 0, "hf felica litesim" }, - { 0, "hf felica litedump" }, - { 1, "hf fido help" }, - { 1, "hf fido list" }, - { 0, "hf fido info" }, - { 0, "hf fido reg" }, - { 0, "hf fido auth" }, - { 0, "hf fido make" }, - { 0, "hf fido assert" }, - { 1, "hf fudan help" }, - { 0, "hf fudan reader" }, - { 0, "hf fudan dump" }, - { 0, "hf fudan rdbl" }, - { 1, "hf fudan view" }, - { 0, "hf fudan wrbl" }, - { 1, "hf gallagher help" }, - { 0, "hf gallagher reader" }, - { 0, "hf gallagher clone" }, - { 0, "hf gallagher delete" }, - { 1, "hf gallagher diversifykey" }, - { 1, "hf gallagher decode" }, - { 1, "hf ksx6924 help" }, - { 0, "hf ksx6924 select" }, - { 0, "hf ksx6924 info" }, - { 0, "hf ksx6924 balance" }, - { 0, "hf ksx6924 init" }, - { 0, "hf ksx6924 prec" }, - { 1, "hf jooki help" }, - { 0, "hf jooki clone" }, - { 1, "hf jooki decode" }, - { 1, "hf jooki encode" }, - { 0, "hf jooki sim" }, - { 1, "hf iclass help" }, - { 0, "hf iclass dump" }, - { 1, "hf iclass info" }, - { 1, "hf iclass list" }, - { 0, "hf iclass rdbl" }, - { 0, "hf iclass reader" }, - { 0, "hf iclass restore" }, - { 0, "hf iclass sniff" }, - { 0, "hf iclass wrbl" }, - { 0, "hf iclass chk" }, - { 1, "hf iclass loclass" }, - { 1, "hf iclass lookup" }, - { 0, "hf iclass sim" }, - { 0, "hf iclass eload" }, - { 0, "hf iclass esave" }, - { 0, "hf iclass eview" }, - { 1, "hf iclass configcard" }, - { 1, "hf iclass calcnewkey" }, - { 1, "hf iclass encode" }, - { 1, "hf iclass encrypt" }, - { 1, "hf iclass decrypt" }, - { 1, "hf iclass managekeys" }, - { 1, "hf iclass permutekey" }, - { 1, "hf iclass view" }, - { 1, "hf legic help" }, - { 0, "hf legic dump" }, - { 0, "hf legic info" }, - { 1, "hf legic list" }, - { 0, "hf legic rdbl" }, - { 0, "hf legic reader" }, - { 0, "hf legic restore" }, - { 0, "hf legic wipe" }, - { 0, "hf legic wrbl" }, - { 0, "hf legic sim" }, - { 0, "hf legic eload" }, - { 0, "hf legic esave" }, - { 0, "hf legic eview" }, - { 0, "hf legic einfo" }, - { 1, "hf legic crc" }, - { 1, "hf legic view" }, - { 1, "hf lto help" }, - { 0, "hf lto dump" }, - { 0, "hf lto info" }, - { 1, "hf lto list" }, - { 0, "hf lto rdbl" }, - { 0, "hf lto reader" }, - { 0, "hf lto restore" }, - { 0, "hf lto wrbl" }, - { 1, "hf mf help" }, - { 1, "hf mf list" }, - { 0, "hf mf darkside" }, - { 0, "hf mf nested" }, - { 1, "hf mf hardnested" }, - { 0, "hf mf staticnested" }, - { 0, "hf mf autopwn" }, - { 0, "hf mf nack" }, - { 0, "hf mf chk" }, - { 0, "hf mf fchk" }, - { 1, "hf mf decrypt" }, - { 0, "hf mf supercard" }, - { 0, "hf mf auth4" }, - { 1, "hf mf acl" }, - { 0, "hf mf dump" }, - { 1, "hf mf mad" }, - { 0, "hf mf personalize" }, - { 0, "hf mf rdbl" }, - { 0, "hf mf rdsc" }, - { 0, "hf mf restore" }, - { 0, "hf mf setmod" }, - { 1, "hf mf value" }, - { 1, "hf mf view" }, - { 0, "hf mf wipe" }, - { 0, "hf mf wrbl" }, - { 0, "hf mf sim" }, - { 0, "hf mf ecfill" }, - { 0, "hf mf eclr" }, - { 0, "hf mf egetblk" }, - { 0, "hf mf egetsc" }, - { 0, "hf mf ekeyprn" }, - { 0, "hf mf eload" }, - { 0, "hf mf esave" }, - { 0, "hf mf esetblk" }, - { 0, "hf mf eview" }, - { 0, "hf mf cgetblk" }, - { 0, "hf mf cgetsc" }, - { 0, "hf mf cload" }, - { 0, "hf mf csave" }, - { 0, "hf mf csetblk" }, - { 0, "hf mf csetuid" }, - { 0, "hf mf cview" }, - { 0, "hf mf cwipe" }, - { 0, "hf mf gen3uid" }, - { 0, "hf mf gen3blk" }, - { 0, "hf mf gen3freeze" }, - { 0, "hf mf ggetblk" }, - { 0, "hf mf gload" }, - { 0, "hf mf gsave" }, - { 0, "hf mf gsetblk" }, - { 0, "hf mf gview" }, - { 0, "hf mf gdmconfig" }, - { 0, "hf mf gdmsetblk" }, - { 0, "hf mf ndefformat" }, - { 0, "hf mf ndefread" }, - { 0, "hf mf ndefwrite" }, - { 1, "hf mfp help" }, - { 0, "hf mfp info" }, - { 0, "hf mfp wrp" }, - { 0, "hf mfp initp" }, - { 0, "hf mfp commitp" }, - { 0, "hf mfp auth" }, - { 0, "hf mfp rdbl" }, - { 0, "hf mfp rdsc" }, - { 0, "hf mfp wrbl" }, - { 0, "hf mfp chk" }, - { 0, "hf mfp mad" }, - { 0, "hf mfp ndefread" }, - { 1, "hf mfu help" }, - { 1, "hf mfu keygen" }, - { 1, "hf mfu pwdgen" }, - { 0, "hf mfu otptear" }, - { 0, "hf mfu cauth" }, - { 0, "hf mfu dump" }, - { 0, "hf mfu info" }, - { 0, "hf mfu ndefread" }, - { 0, "hf mfu rdbl" }, - { 0, "hf mfu restore" }, - { 1, "hf mfu view" }, - { 0, "hf mfu wrbl" }, - { 0, "hf mfu tamper" }, - { 0, "hf mfu eload" }, - { 0, "hf mfu esave" }, - { 0, "hf mfu eview" }, - { 0, "hf mfu sim" }, - { 0, "hf mfu setpwd" }, - { 0, "hf mfu setuid" }, - { 1, "hf mfdes help" }, - { 0, "hf mfdes info" }, - { 0, "hf mfdes getuid" }, - { 0, "hf mfdes default" }, - { 0, "hf mfdes auth" }, - { 0, "hf mfdes chk" }, - { 0, "hf mfdes detect" }, - { 0, "hf mfdes freemem" }, - { 0, "hf mfdes setconfig" }, - { 0, "hf mfdes formatpicc" }, - { 1, "hf mfdes list" }, - { 0, "hf mfdes mad" }, - { 0, "hf mfdes lsapp" }, - { 0, "hf mfdes getaids" }, - { 0, "hf mfdes getappnames" }, - { 0, "hf mfdes bruteaid" }, - { 0, "hf mfdes createapp" }, - { 0, "hf mfdes deleteapp" }, - { 0, "hf mfdes selectapp" }, - { 0, "hf mfdes changekey" }, - { 0, "hf mfdes chkeysettings" }, - { 0, "hf mfdes getkeysettings" }, - { 0, "hf mfdes getkeyversions" }, - { 0, "hf mfdes getfileids" }, - { 0, "hf mfdes getfileisoids" }, - { 0, "hf mfdes lsfiles" }, - { 0, "hf mfdes dump" }, - { 0, "hf mfdes createfile" }, - { 0, "hf mfdes createvaluefile" }, - { 0, "hf mfdes createrecordfile" }, - { 0, "hf mfdes createmacfile" }, - { 0, "hf mfdes deletefile" }, - { 0, "hf mfdes getfilesettings" }, - { 0, "hf mfdes chfilesettings" }, - { 0, "hf mfdes read" }, - { 0, "hf mfdes write" }, - { 0, "hf mfdes value" }, - { 0, "hf mfdes clearrecfile" }, - { 1, "hf mfdes test" }, - { 1, "hf ntag424 help" }, - { 0, "hf ntag424 info" }, - { 0, "hf ntag424 sdm" }, - { 1, "hf ntag424 view" }, - { 1, "hf seos help" }, - { 0, "hf seos info" }, - { 1, "hf seos list" }, - { 1, "hf st25ta help" }, - { 0, "hf st25ta info" }, - { 1, "hf st25ta list" }, - { 1, "hf st25ta ndefread" }, - { 0, "hf st25ta protect" }, - { 0, "hf st25ta pwd" }, - { 0, "hf st25ta sim" }, - { 1, "hf tesla help" }, - { 0, "hf tesla info" }, - { 1, "hf tesla list" }, - { 1, "hf texkom help" }, - { 0, "hf texkom reader" }, - { 0, "hf texkom sim" }, - { 1, "hf thinfilm help" }, - { 0, "hf thinfilm info" }, - { 1, "hf thinfilm list" }, - { 0, "hf thinfilm sim" }, - { 1, "hf topaz help" }, - { 0, "hf topaz dump" }, - { 1, "hf topaz list" }, - { 0, "hf topaz info" }, - { 0, "hf topaz reader" }, - { 0, "hf topaz sim" }, - { 0, "hf topaz sniff" }, - { 0, "hf topaz raw" }, - { 0, "hf topaz rdbl" }, - { 1, "hf topaz view" }, - { 0, "hf topaz wrbl" }, - { 1, "hf xerox help" }, - { 0, "hf xerox info" }, - { 0, "hf xerox reader" }, - { 0, "hf xerox dump" }, - { 1, "hf waveshare help" }, - { 0, "hf waveshare loadbmp" }, - { 1, "hw help" }, - { 0, "hw break" }, - { 1, "hw connect" }, - { 0, "hw dbg" }, - { 0, "hw detectreader" }, - { 0, "hw fpgaoff" }, - { 0, "hw lcd" }, - { 0, "hw lcdreset" }, - { 0, "hw ping" }, - { 0, "hw readmem" }, - { 0, "hw reset" }, - { 0, "hw setlfdivisor" }, - { 0, "hw setmux" }, - { 0, "hw standalone" }, - { 0, "hw status" }, - { 0, "hw tearoff" }, - { 0, "hw tia" }, - { 0, "hw tune" }, - { 1, "hw version" }, - { 1, "lf help" }, - { 0, "lf config" }, - { 0, "lf cmdread" }, - { 0, "lf read" }, - { 1, "lf search" }, - { 0, "lf sim" }, - { 0, "lf simask" }, - { 0, "lf simfsk" }, - { 0, "lf simpsk" }, - { 0, "lf simbidir" }, - { 0, "lf sniff" }, - { 0, "lf tune" }, - { 1, "lf awid help" }, - { 1, "lf awid demod" }, - { 0, "lf awid reader" }, - { 0, "lf awid clone" }, - { 0, "lf awid sim" }, - { 0, "lf awid brute" }, - { 0, "lf awid watch" }, - { 1, "lf cotag help" }, - { 1, "lf cotag demod" }, - { 0, "lf cotag reader" }, - { 1, "lf destron help" }, - { 1, "lf destron demod" }, - { 0, "lf destron reader" }, - { 0, "lf destron clone" }, - { 0, "lf destron sim" }, - { 1, "lf em help" }, - { 1, "lf em 410x help" }, - { 1, "lf em 410x demod" }, - { 0, "lf em 410x reader" }, - { 0, "lf em 410x sim" }, - { 0, "lf em 410x brute" }, - { 0, "lf em 410x watch" }, - { 0, "lf em 410x spoof" }, - { 0, "lf em 410x clone" }, - { 1, "lf em 4x05 help" }, - { 0, "lf em 4x05 brute" }, - { 0, "lf em 4x05 chk" }, - { 1, "lf em 4x05 demod" }, - { 0, "lf em 4x05 dump" }, - { 0, "lf em 4x05 info" }, - { 0, "lf em 4x05 read" }, - { 1, "lf em 4x05 sniff" }, - { 0, "lf em 4x05 unlock" }, - { 0, "lf em 4x05 wipe" }, - { 0, "lf em 4x05 write" }, - { 1, "lf em 4x50 help" }, - { 0, "lf em 4x50 brute" }, - { 0, "lf em 4x50 chk" }, - { 0, "lf em 4x50 dump" }, - { 0, "lf em 4x50 info" }, - { 0, "lf em 4x50 login" }, - { 0, "lf em 4x50 rdbl" }, - { 0, "lf em 4x50 reader" }, - { 0, "lf em 4x50 restore" }, - { 0, "lf em 4x50 wrbl" }, - { 0, "lf em 4x50 wrpwd" }, - { 0, "lf em 4x50 wipe" }, - { 0, "lf em 4x50 eload" }, - { 0, "lf em 4x50 esave" }, - { 0, "lf em 4x50 eview" }, - { 0, "lf em 4x50 sim" }, - { 1, "lf em 4x70 help" }, - { 0, "lf em 4x70 brute" }, - { 0, "lf em 4x70 info" }, - { 0, "lf em 4x70 write" }, - { 0, "lf em 4x70 unlock" }, - { 0, "lf em 4x70 auth" }, - { 0, "lf em 4x70 writepin" }, - { 0, "lf em 4x70 writekey" }, - { 1, "lf fdxb help" }, - { 1, "lf fdxb demod" }, - { 0, "lf fdxb reader" }, - { 0, "lf fdxb clone" }, - { 0, "lf fdxb sim" }, - { 1, "lf gallagher help" }, - { 1, "lf gallagher demod" }, - { 0, "lf gallagher reader" }, - { 0, "lf gallagher clone" }, - { 0, "lf gallagher sim" }, - { 1, "lf gproxii help" }, - { 1, "lf gproxii demod" }, - { 0, "lf gproxii reader" }, - { 0, "lf gproxii clone" }, - { 0, "lf gproxii sim" }, - { 1, "lf hid help" }, - { 1, "lf hid demod" }, - { 0, "lf hid reader" }, - { 0, "lf hid clone" }, - { 0, "lf hid sim" }, - { 0, "lf hid brute" }, - { 0, "lf hid watch" }, - { 1, "lf hitag help" }, - { 0, "lf hitag eload" }, - { 1, "lf hitag list" }, - { 0, "lf hitag info" }, - { 0, "lf hitag reader" }, - { 0, "lf hitag sim" }, - { 0, "lf hitag sniff" }, - { 0, "lf hitag writer" }, - { 0, "lf hitag dump" }, - { 0, "lf hitag cc" }, - { 1, "lf idteck help" }, - { 1, "lf idteck demod" }, - { 0, "lf idteck reader" }, - { 0, "lf idteck clone" }, - { 0, "lf idteck sim" }, - { 1, "lf indala help" }, - { 0, "lf indala brute" }, - { 1, "lf indala demod" }, - { 1, "lf indala altdemod" }, - { 0, "lf indala reader" }, - { 0, "lf indala clone" }, - { 0, "lf indala sim" }, - { 1, "lf io help" }, - { 1, "lf io demod" }, - { 0, "lf io reader" }, - { 0, "lf io clone" }, - { 0, "lf io sim" }, - { 0, "lf io watch" }, - { 1, "lf jablotron help" }, - { 1, "lf jablotron demod" }, - { 0, "lf jablotron reader" }, - { 0, "lf jablotron clone" }, - { 0, "lf jablotron sim" }, - { 1, "lf keri help" }, - { 1, "lf keri demod" }, - { 0, "lf keri reader" }, - { 0, "lf keri clone" }, - { 0, "lf keri sim" }, - { 1, "lf motorola help" }, - { 1, "lf motorola demod" }, - { 0, "lf motorola reader" }, - { 0, "lf motorola clone" }, - { 0, "lf motorola sim" }, - { 1, "lf nedap help" }, - { 1, "lf nedap demod" }, - { 0, "lf nedap reader" }, - { 0, "lf nedap clone" }, - { 0, "lf nedap sim" }, - { 1, "lf nexwatch help" }, - { 1, "lf nexwatch demod" }, - { 0, "lf nexwatch reader" }, - { 0, "lf nexwatch clone" }, - { 0, "lf nexwatch sim" }, - { 1, "lf noralsy help" }, - { 1, "lf noralsy demod" }, - { 0, "lf noralsy reader" }, - { 0, "lf noralsy clone" }, - { 0, "lf noralsy sim" }, - { 1, "lf pac help" }, - { 1, "lf pac demod" }, - { 0, "lf pac reader" }, - { 0, "lf pac clone" }, - { 0, "lf pac sim" }, - { 1, "lf paradox help" }, - { 1, "lf paradox demod" }, - { 0, "lf paradox reader" }, - { 0, "lf paradox clone" }, - { 0, "lf paradox sim" }, - { 1, "lf pcf7931 help" }, - { 0, "lf pcf7931 reader" }, - { 0, "lf pcf7931 write" }, - { 1, "lf pcf7931 config" }, - { 1, "lf presco help" }, - { 1, "lf presco demod" }, - { 0, "lf presco reader" }, - { 0, "lf presco clone" }, - { 0, "lf presco sim" }, - { 1, "lf pyramid help" }, - { 1, "lf pyramid demod" }, - { 0, "lf pyramid reader" }, - { 0, "lf pyramid clone" }, - { 0, "lf pyramid sim" }, - { 1, "lf securakey help" }, - { 1, "lf securakey demod" }, - { 0, "lf securakey reader" }, - { 0, "lf securakey clone" }, - { 0, "lf securakey sim" }, - { 1, "lf ti help" }, - { 1, "lf ti demod" }, - { 0, "lf ti reader" }, - { 0, "lf ti write" }, - { 1, "lf t55xx help" }, - { 0, "lf t55xx clonehelp" }, - { 1, "lf t55xx config" }, - { 0, "lf t55xx dangerraw" }, - { 1, "lf t55xx detect" }, - { 0, "lf t55xx deviceconfig" }, - { 0, "lf t55xx dump" }, - { 1, "lf t55xx info" }, - { 0, "lf t55xx p1detect" }, - { 0, "lf t55xx read" }, - { 0, "lf t55xx resetread" }, - { 0, "lf t55xx restore" }, - { 1, "lf t55xx trace" }, - { 0, "lf t55xx wakeup" }, - { 0, "lf t55xx write" }, - { 0, "lf t55xx bruteforce" }, - { 0, "lf t55xx chk" }, - { 0, "lf t55xx protect" }, - { 0, "lf t55xx recoverpw" }, - { 1, "lf t55xx sniff" }, - { 0, "lf t55xx special" }, - { 0, "lf t55xx wipe" }, - { 1, "lf viking help" }, - { 1, "lf viking demod" }, - { 0, "lf viking reader" }, - { 0, "lf viking clone" }, - { 0, "lf viking sim" }, - { 1, "lf visa2000 help" }, - { 1, "lf visa2000 demod" }, - { 0, "lf visa2000 reader" }, - { 0, "lf visa2000 clone" }, - { 0, "lf visa2000 sim" }, - { 1, "mem help" }, - { 0, "mem baudrate" }, - { 0, "mem dump" }, - { 0, "mem info" }, - { 0, "mem load" }, - { 0, "mem wipe" }, - { 1, "mem spiffs help" }, - { 0, "mem spiffs copy" }, - { 0, "mem spiffs check" }, - { 0, "mem spiffs dump" }, - { 0, "mem spiffs info" }, - { 0, "mem spiffs mount" }, - { 0, "mem spiffs remove" }, - { 0, "mem spiffs rename" }, - { 0, "mem spiffs test" }, - { 0, "mem spiffs tree" }, - { 0, "mem spiffs unmount" }, - { 0, "mem spiffs upload" }, - { 0, "mem spiffs view" }, - { 0, "mem spiffs wipe" }, - { 1, "nfc help" }, - { 1, "nfc decode" }, - { 0, "nfc type1 read" }, - { 1, "nfc type1 help" }, - { 0, "nfc type2 read" }, - { 1, "nfc type2 help" }, - { 0, "nfc type4a format" }, - { 0, "nfc type4a read" }, - { 0, "nfc type4a write" }, - { 0, "nfc type4a st25taread" }, - { 1, "nfc type4a help" }, - { 0, "nfc type4b read" }, - { 1, "nfc type4b help" }, - { 0, "nfc mf cformat" }, - { 0, "nfc mf cread" }, - { 0, "nfc mf cwrite" }, - { 0, "nfc mf pread" }, - { 1, "nfc mf help" }, - { 0, "nfc barcode read" }, - { 0, "nfc barcode sim" }, - { 1, "nfc barcode help" }, - { 1, "piv help" }, - { 0, "piv select" }, - { 0, "piv getdata" }, - { 0, "piv authsign" }, - { 0, "piv scan" }, - { 1, "piv list" }, - { 1, "smart help" }, - { 1, "smart list" }, - { 0, "smart info" }, - { 0, "smart reader" }, - { 0, "smart raw" }, - { 1, "smart upgrade" }, - { 0, "smart setclock" }, - { 0, "smart brute" }, - { 1, "script help" }, - { 1, "script list" }, - { 1, "script run" }, - { 1, "trace help" }, - { 1, "trace extract" }, - { 1, "trace list" }, - { 1, "trace load" }, - { 1, "trace save" }, - { 1, "usart help" }, - { 0, "usart btpin" }, - { 0, "usart btfactory" }, - { 0, "usart tx" }, - { 0, "usart rx" }, - { 0, "usart txrx" }, - { 0, "usart txhex" }, - { 0, "usart rxhex" }, - { 0, "usart config" }, - { 1, "wiegand help" }, - { 1, "wiegand list" }, - { 1, "wiegand encode" }, - { 1, "wiegand decode" }, - {0, NULL} -}; - -#ifdef __cplusplus -} -#endif - -#endif \ No newline at end of file diff --git a/client/src/preferences.c b/client/src/preferences.c index e3aef4ccb..09521cab4 100644 --- a/client/src/preferences.c +++ b/client/src/preferences.c @@ -26,14 +26,15 @@ //----------------------------------------------------------------------------- #include "preferences.h" -#include "comms.h" -#include "emv/emvjson.h" #include -#include "cmdparser.h" #include #include -#include +#include "proxmark3.h" +#include "comms.h" +#include "emv/emvjson.h" +#include "cmdparser.h" #include "cliparser.h" +#include "uart/uart.h" // uart_reconfigure_timeouts static int CmdHelp(const char *Cmd); static int setCmdHelp(const char *Cmd); @@ -44,7 +45,7 @@ static char *prefGetFilename(void) { if (searchHomeFilePath(&path, NULL, preferencesFilename, false) == PM3_SUCCESS) return path; else - return strdup(preferencesFilename); + return str_dup(preferencesFilename); } int preferences_load(void) { @@ -52,6 +53,8 @@ int preferences_load(void) { // Set all defaults g_session.client_debug_level = cdbOFF; // g_session.device_debug_level = ddbOFF; + g_session.timeout = uart_get_timeouts(); + g_session.window_changed = false; g_session.plot.x = 10; g_session.plot.y = 30; @@ -119,7 +122,7 @@ int preferences_save(void) { PrintAndLogEx(INFO, "No preferences file will be saved"); return PM3_SUCCESS; } - PrintAndLogEx(INFO, "Saving preferences..."); + PrintAndLogEx(DEBUG, "Saving preferences..."); char *fn = prefGetFilename(); int fn_len = strlen(fn) + 5; // .bak\0 @@ -260,7 +263,7 @@ void preferences_save_callback(json_t *root) { } */ JsonSaveInt(root, "client.exe.delay", g_session.client_exe_delay); - + JsonSaveInt(root, "client.timeout", g_session.timeout); } void preferences_load_callback(json_t *root) { json_error_t up_error = {0}; @@ -276,6 +279,12 @@ void preferences_load_callback(json_t *root) { if (strncmp(tempStr, "off", 3) == 0) g_session.client_debug_level = cdbOFF; if (strncmp(tempStr, "simple", 6) == 0) g_session.client_debug_level = cdbSIMPLE; if (strncmp(tempStr, "full", 4) == 0) g_session.client_debug_level = cdbFULL; + + if (g_session.client_debug_level == cdbOFF) { + SetAPDULogging(false); + } else { + SetAPDULogging(true); + } } // default save path @@ -355,68 +364,107 @@ void preferences_load_callback(json_t *root) { // client command execution delay if (json_unpack_ex(root, &up_error, 0, "{s:i}", "client.exe.delay", &i1) == 0) g_session.client_exe_delay = i1; + + // client command timeout + if (json_unpack_ex(root, &up_error, 0, "{s:i}", "client.timeout", &i1) == 0) + g_session.timeout = i1; } // Help Functions // Preference Processing Functions // typedef enum preferenceId {prefNONE,prefHELP,prefEMOJI,prefCOLOR,prefPLOT,prefOVERLAY,prefHINTS,prefCLIENTDEBUG} preferenceId_t; -typedef enum prefShowOpt {prefShowNone, prefShowOLD, prefShowNEW} prefShowOpt_t; +typedef enum prefShowOpt {prefShowNone, prefShowOLD, prefShowNEW, prefShowOff, prefShowUnknown} prefShowOpt_t; -static const char *prefShowMsg(prefShowOpt_t Opt) { - switch (Opt) { +static const char *pref_show_status_msg(prefShowOpt_t opt) { + switch (opt) { case prefShowOLD: - return "( " _YELLOW_("old") " )"; + return "( " _CYAN_("old") " )"; case prefShowNEW: return "( " _GREEN_("new") " )"; case prefShowNone: + case prefShowOff: + case prefShowUnknown: + default: + return ""; + + } +} + +static const char *pref_show_value(prefShowOpt_t opt, const char* msg) { + + static char s[128] = {0}; + switch (opt) { + case prefShowOLD: + sprintf(s, _CYAN_("%s"), msg); + return s; + + case prefShowNEW: + sprintf(s, _GREEN_("%s"), msg); + return s; + case prefShowNone: + if ((strncmp(msg, "off", 3) == 0) || (strncmp(msg, "normal", 6) ==0)) { + sprintf(s, _WHITE_("%s"), msg); + } else { + sprintf(s, _GREEN_("%s"), msg); + } + return s; + + case prefShowOff: + sprintf(s, _WHITE_("%s"), msg); + return s; + + case prefShowUnknown: + sprintf(s, _RED_("%s"), msg); + return s; + + default: return ""; } - return ""; } + static void showEmojiState(prefShowOpt_t opt) { switch (g_session.emoji_mode) { case EMO_ALIAS: - PrintAndLogEx(INFO, " %s emoji.................. "_GREEN_("alias"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s emoji................... %s", pref_show_status_msg(opt), pref_show_value(opt, "alias")); break; case EMO_EMOJI: - PrintAndLogEx(INFO, " %s emoji.................. "_GREEN_("emoji"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s emoji................... %s", pref_show_status_msg(opt), pref_show_value(opt, "emoji")); break; case EMO_ALTTEXT: - PrintAndLogEx(INFO, " %s emoji.................. "_GREEN_("alttext"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s emoji................... %s", pref_show_status_msg(opt), pref_show_value(opt, "alttext")); break; case EMO_NONE: - PrintAndLogEx(INFO, " %s emoji.................. "_GREEN_("none"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s emoji................... %s", pref_show_status_msg(opt), pref_show_value(opt, "none")); break; default: - PrintAndLogEx(INFO, " %s emoji.................. "_RED_("unknown"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s emoji................... %s", pref_show_status_msg(opt), pref_show_value(prefShowUnknown, "unknown")); } } static void showColorState(prefShowOpt_t opt) { - - if (g_session.supports_colors) - PrintAndLogEx(INFO, " %s color.................. "_GREEN_("ansi"), prefShowMsg(opt)); - else - PrintAndLogEx(INFO, " %s color.................. "_WHITE_("off"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s color................... %s " + , pref_show_status_msg(opt) + , (g_session.supports_colors) ? pref_show_value(opt, "ansi") : pref_show_value(opt, "off") + ); } static void showClientDebugState(prefShowOpt_t opt) { switch (g_session.client_debug_level) { case cdbOFF: - PrintAndLogEx(INFO, " %s client debug........... "_WHITE_("off"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s client debug............ %s", pref_show_status_msg(opt), pref_show_value(opt, "off")); break; case cdbSIMPLE: - PrintAndLogEx(INFO, " %s client debug........... "_GREEN_("simple"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s client debug............ %s", pref_show_status_msg(opt), pref_show_value(opt, "simple")); break; case cdbFULL: - PrintAndLogEx(INFO, " %s client debug........... "_GREEN_("full"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s client debug............ %s", pref_show_status_msg(opt), pref_show_value(opt,"full")); break; default: - PrintAndLogEx(INFO, " %s client debug........... "_RED_("unknown"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s client debug............ %s", pref_show_status_msg(opt), pref_show_value(prefShowUnknown, "unknown")); } } /* @@ -448,28 +496,28 @@ static void showSavePathState(savePaths_t path_index, prefShowOpt_t opt) { char s[50]; switch (path_index) { case spDefault: - strcpy(s, "default save path......"); + strcpy(s, "default save path......."); break; case spDump: - strcpy(s, "dump save path........."); + strcpy(s, "dump save path.........."); break; case spTrace: - strcpy(s, "trace save path........"); + strcpy(s, "trace save path........."); break; case spItemCount: default: - strcpy(s, _RED_("unknown")" save path......"); + strcpy(s, _RED_("unknown")" save path......."); } if (path_index < spItemCount) { if ((g_session.defaultPaths[path_index] == NULL) || (strcmp(g_session.defaultPaths[path_index], "") == 0)) { PrintAndLogEx(INFO, " %s %s "_WHITE_("not set"), - prefShowMsg(opt), + pref_show_status_msg(opt), s ); } else { PrintAndLogEx(INFO, " %s %s "_GREEN_("%s"), - prefShowMsg(opt), + pref_show_status_msg(opt), s, g_session.defaultPaths[path_index] ); @@ -478,7 +526,7 @@ static void showSavePathState(savePaths_t path_index, prefShowOpt_t opt) { } static void showPlotPosState(void) { - PrintAndLogEx(INFO, " Plot window............ X "_GREEN_("%4d")" Y "_GREEN_("%4d")" H "_GREEN_("%4d")" W "_GREEN_("%4d"), + PrintAndLogEx(INFO, " plot window............. X "_GREEN_("%4d")" Y "_GREEN_("%4d")" H "_GREEN_("%4d")" W "_GREEN_("%4d"), g_session.plot.x, g_session.plot.y, g_session.plot.h, @@ -487,7 +535,7 @@ static void showPlotPosState(void) { } static void showOverlayPosState(void) { - PrintAndLogEx(INFO, " Slider/Overlay window.. X "_GREEN_("%4d")" Y "_GREEN_("%4d")" H "_GREEN_("%4d")" W "_GREEN_("%4d"), + PrintAndLogEx(INFO, " slider/overlay window... X "_GREEN_("%4d")" Y "_GREEN_("%4d")" H "_GREEN_("%4d")" W "_GREEN_("%4d"), g_session.overlay.x, g_session.overlay.y, g_session.overlay.h, @@ -496,45 +544,50 @@ static void showOverlayPosState(void) { } static void showHintsState(prefShowOpt_t opt) { - if (g_session.show_hints) - PrintAndLogEx(INFO, " %s hints.................. "_GREEN_("on"), prefShowMsg(opt)); - else - PrintAndLogEx(INFO, " %s hints.................. "_WHITE_("off"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s hints................... %s" + , pref_show_status_msg(opt) + , (g_session.show_hints) ? pref_show_value(opt,"on") : pref_show_value(opt,"off") + ); } static void showPlotSliderState(prefShowOpt_t opt) { - if (g_session.overlay_sliders) - PrintAndLogEx(INFO, " %s show plot sliders...... "_GREEN_("on"), prefShowMsg(opt)); - else - PrintAndLogEx(INFO, " %s show plot sliders...... "_WHITE_("off"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s show plot sliders....... %s" + , pref_show_status_msg(opt) + , (g_session.overlay_sliders) ? pref_show_value(opt,"on") : pref_show_value(opt,"off") + ); } static void showBarModeState(prefShowOpt_t opt) { switch (g_session.bar_mode) { case STYLE_BAR: - PrintAndLogEx(INFO, " %s barmode................ "_GREEN_("bar"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s barmode................. %s", pref_show_status_msg(opt), pref_show_value(opt,"bar")); break; case STYLE_MIXED: - PrintAndLogEx(INFO, " %s barmode................ "_GREEN_("mixed"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s barmode................. %s", pref_show_status_msg(opt), pref_show_value(opt,"mixed")); break; case STYLE_VALUE: - PrintAndLogEx(INFO, " %s barmode................ "_GREEN_("value"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s barmode................. %s", pref_show_status_msg(opt), pref_show_value(opt,"value")); break; default: - PrintAndLogEx(INFO, " %s barmode............... "_RED_("unknown"), prefShowMsg(opt)); + PrintAndLogEx(INFO, " %s barmode................. %s", pref_show_status_msg(opt), pref_show_value(prefShowUnknown,"unknown")); } } static void showOutputState(prefShowOpt_t opt) { - PrintAndLogEx(INFO, " %s output................. %s", prefShowMsg(opt), - g_session.dense_output ? _GREEN_("dense") : _WHITE_("normal")); + PrintAndLogEx(INFO, " %s output.................. %s" + , pref_show_status_msg(opt) + , (g_session.dense_output) ? pref_show_value(opt,"dense") : pref_show_value(opt,"normal") + ); } static void showClientExeDelayState(void) { - PrintAndLogEx(INFO, " Cmd execution delay.... "_GREEN_("%u"), g_session.client_exe_delay); + PrintAndLogEx(INFO, " cmd execution delay..... "_GREEN_("%u"), g_session.client_exe_delay); } +static void showClientTimeoutState(void) { + PrintAndLogEx(INFO, " communication timeout... " _GREEN_("%u") " ms", g_session.timeout); +} static int setCmdEmoji(const char *Cmd) { CLIParserContext *ctx; @@ -636,9 +689,9 @@ static int setCmdColor(const char *Cmd) { static int setCmdDebug(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "prefs set clientdebug ", + CLIParserInit(&ctx, "prefs set client.debug ", "Set persistent preference of using clientside debug level", - "prefs set clientdebug --simple" + "prefs set client.debug --simple" ); void *argtable[] = { @@ -664,9 +717,11 @@ static int setCmdDebug(const char *Cmd) { if (use_off) { new_value = cdbOFF; } + if (use_simple) { new_value = cdbSIMPLE; } + if (use_full) { new_value = cdbFULL; } @@ -675,6 +730,13 @@ static int setCmdDebug(const char *Cmd) { showClientDebugState(prefShowOLD); g_session.client_debug_level = new_value; g_debugMode = new_value; + + if (new_value == cdbOFF) { + SetAPDULogging(false); + } else { + SetAPDULogging(true); + } + showClientDebugState(prefShowNEW); preferences_save(); } else { @@ -752,6 +814,44 @@ static int setCmdDeviceDebug (const char *Cmd) } */ +int getDeviceDebugLevel(uint8_t *debug_level) { + if (!g_session.pm3_present) + return PM3_EFAILED; + + clearCommandBuffer(); + SendCommandNG(CMD_GET_DBGMODE, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_GET_DBGMODE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "Failed to get current device debug level"); + return PM3_ETIMEOUT; + } + + if (debug_level) + *debug_level = resp.data.asBytes[0]; + + return PM3_SUCCESS; +} + +int setDeviceDebugLevel(uint8_t debug_level, bool verbose) { + if (!g_session.pm3_present) + return PM3_EFAILED; + + if (verbose) + PrintAndLogEx(INFO, "setting device debug loglevel to %u", debug_level); + + uint8_t cdata[] = {debug_level, verbose}; + clearCommandBuffer(); + SendCommandNG(CMD_SET_DBGMODE, cdata, sizeof(cdata)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_SET_DBGMODE, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "failed to set device debug loglevel"); + return PM3_EFAILED; + } + + return PM3_SUCCESS; +} + + static int setCmdOutput(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs set output", @@ -798,10 +898,10 @@ static int setCmdOutput(const char *Cmd) { static int setCmdExeDelay(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "prefs set clientdelay", + CLIParserInit(&ctx, "prefs set client.delay", "Set persistent preference of delay before executing a command in the client", - "prefs set clientdelay --ms 0 --> unsets any delay\n" - "prefs set clientdelay --ms 1000 --> sets 1000ms delay" + "prefs set client.delay --ms 0 --> unsets any delay\n" + "prefs set client.delay --ms 1000 --> sets 1000ms delay" ); void *argtable[] = { @@ -824,9 +924,47 @@ static int setCmdExeDelay(const char *Cmd) { return PM3_SUCCESS; } +static int setClientTimeout(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs set client.timeout", + "Set persistent preference of client communication timeout", + "prefs set client.timeout --ms 0 --> unsets any timeout\n" + "prefs set client.timeout -m 20 --> Set the timeout to 20ms\n" + "prefs set client.timeout --ms 500 --> Set the timeout to 500ms\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("m", "ms", "", "timeout in micro seconds"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + uint32_t new_value = (uint32_t)arg_get_int_def(ctx, 1, 0); + CLIParserFree(ctx); + + // UART_USB_CLIENT_RX_TIMEOUT_MS is considered as the minimum required timeout. + if (new_value < UART_USB_CLIENT_RX_TIMEOUT_MS) { + PrintAndLogEx(WARNING, "Timeout less than %u ms might cause errors.", UART_USB_CLIENT_RX_TIMEOUT_MS); + } else if (new_value > 5000) { + PrintAndLogEx(WARNING, "Timeout greater than 5000 ms makes the client unresponsive."); + } + + if (g_session.timeout != new_value) { + showClientTimeoutState(); + g_session.timeout = new_value; + uart_reconfigure_timeouts(new_value); + showClientTimeoutState(); + preferences_save(); + } else { + showClientTimeoutState(); + } + return PM3_SUCCESS; +} + + static int setCmdHint(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "prefs set hints ", + CLIParserInit(&ctx, "prefs set hints", "Set persistent preference of showing hint messages in the client", "prefs set hints --on" ); @@ -1087,9 +1225,9 @@ static int getCmdColor(const char *Cmd) { static int getCmdDebug(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "prefs get clientdebug", + CLIParserInit(&ctx, "prefs get client.debug", "Get preference of using clientside debug level", - "prefs get clientdebug" + "prefs get client.debug" ); void *argtable[] = { arg_param_begin, @@ -1169,9 +1307,9 @@ static int getCmdSavePaths(const char *Cmd) { static int getCmdExeDelay(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "prefs get clientdelay", + CLIParserInit(&ctx, "prefs get client.delay", "Get preference of delay time before execution of a command in the client", - "prefs get clientdelay" + "prefs get client.delay" ); void *argtable[] = { arg_param_begin, @@ -1183,10 +1321,27 @@ static int getCmdExeDelay(const char *Cmd) { return PM3_SUCCESS; } +static int getClientTimeout(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get client.timeout", + "Get preference of delay time before execution of a command in the client", + "prefs get client.timeout" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + showClientTimeoutState(); + return PM3_SUCCESS; +} + static command_t CommandTableGet[] = { {"barmode", getCmdBarMode, AlwaysAvailable, "Get bar mode preference"}, - {"clientdebug", getCmdDebug, AlwaysAvailable, "Get client debug level preference"}, - {"clientdelay", getCmdExeDelay, AlwaysAvailable, "Get client execution delay preference"}, + {"client.debug", getCmdDebug, AlwaysAvailable, "Get client debug level preference"}, + {"client.delay", getCmdExeDelay, AlwaysAvailable, "Get client execution delay preference"}, + {"client.timeout", getClientTimeout, AlwaysAvailable, "Get client execution delay preference"}, {"color", getCmdColor, AlwaysAvailable, "Get color support preference"}, {"savepaths", getCmdSavePaths, AlwaysAvailable, "Get file folder "}, // {"devicedebug", getCmdDeviceDebug, AlwaysAvailable, "Get device debug level"}, @@ -1200,8 +1355,10 @@ static command_t CommandTableGet[] = { static command_t CommandTableSet[] = { {"help", setCmdHelp, AlwaysAvailable, "This help"}, {"barmode", setCmdBarMode, AlwaysAvailable, "Set bar mode"}, - {"clientdebug", setCmdDebug, AlwaysAvailable, "Set client debug level"}, - {"clientdelay", setCmdExeDelay, AlwaysAvailable, "Set client execution delay"}, + {"client.debug", setCmdDebug, AlwaysAvailable, "Set client debug level"}, + {"client.delay", setCmdExeDelay, AlwaysAvailable, "Set client execution delay"}, + {"client.timeout", setClientTimeout, AlwaysAvailable, "Set client communication timeout"}, + {"color", setCmdColor, AlwaysAvailable, "Set color support"}, {"emoji", setCmdEmoji, AlwaysAvailable, "Set emoji display"}, {"hints", setCmdHint, AlwaysAvailable, "Set hint display"}, @@ -1265,6 +1422,7 @@ static int CmdPrefShow(const char *Cmd) { showBarModeState(prefShowNone); showClientExeDelayState(); showOutputState(prefShowNone); + showClientTimeoutState(); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; diff --git a/client/src/preferences.h b/client/src/preferences.h index 56ba72fa3..e49b64d9c 100644 --- a/client/src/preferences.h +++ b/client/src/preferences.h @@ -31,4 +31,7 @@ int preferences_save(void); void preferences_save_callback(json_t *root); void preferences_load_callback(json_t *root); +int getDeviceDebugLevel(uint8_t *debug_level); +int setDeviceDebugLevel(uint8_t debug_level, bool verbose); + #endif diff --git a/client/src/proxendian.h b/client/src/proxendian.h index 17003e75e..d258d2ce8 100644 --- a/client/src/proxendian.h +++ b/client/src/proxendian.h @@ -53,11 +53,15 @@ #else static inline uint16_t le16(uint16_t v) { - return (v >> 8) | (v << 8); + return (uint16_t)( + (v >> 8) | (v << 8) + ); } static inline uint32_t le32(uint32_t v) { - return (le16(v) << 16) | (le16(v >> 16)); + return (uint32_t)( + (le16(v) << 16) | (le16(v >> 16)) + ); } #endif // HOST_LITTLE_ENDIAN diff --git a/client/src/proxgui.h b/client/src/proxgui.h index 56d0d9574..99c69af94 100644 --- a/client/src/proxgui.h +++ b/client/src/proxgui.h @@ -44,7 +44,7 @@ void ExitGraphics(void); extern double g_CursorScaleFactor; extern char g_CursorScaleFactorUnit[11]; extern double g_PlotGridX, g_PlotGridY, g_PlotGridXdefault, g_PlotGridYdefault, g_GridOffset; -extern uint32_t g_CursorCPos, g_CursorDPos, g_GraphStart, g_GraphStop; +extern uint32_t g_CursorCPos, g_CursorDPos, g_GraphStart, g_GraphStart_old, g_GraphStop; extern int CommandFinished; extern int offline; extern bool g_GridLocked; diff --git a/client/src/proxguiqt.cpp b/client/src/proxguiqt.cpp index ff88bce1e..bce468394 100644 --- a/client/src/proxguiqt.cpp +++ b/client/src/proxguiqt.cpp @@ -49,6 +49,7 @@ static int s_Buff[MAX_GRAPH_TRACE_LEN]; static bool gs_useOverlays = false; static int gs_absVMax = 0; static uint32_t startMax; // Maximum offset in the graph (right side of graph) +static uint32_t startMaxOld; static uint32_t PageWidth; // How many samples are currently visible on this 'page' / graph static int unlockStart = 0; @@ -372,6 +373,18 @@ void ProxWidget::vchange_dthr_down(int v) { RepaintGraphWindow(); } +void ProxWidget::updateNavSlider() { + navSlider->blockSignals(true); + navSlider->setValue(g_GraphStart); + navSlider->setMaximum(startMax); + // for startMaxOld < g_GraphStart or startMax < g_GraphStart_old + navSlider->setValue(g_GraphStart); + navSlider->setMaximum(startMax); + // for click + navSlider->setPageStep(startMax / 10); + navSlider->blockSignals(false); +} + ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) { this->master = master; @@ -406,10 +419,20 @@ ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) { // Set up the plot widget, which does the actual plotting plot = new Plot(this); + navSlider = new QSlider(this); QVBoxLayout *layout = new QVBoxLayout; - layout->addWidget(plot); + layout->addWidget(plot, 1); + layout->addWidget(navSlider); setLayout(layout); + navSlider->setOrientation(Qt::Horizontal); + plot->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + navSlider->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + QObject::connect(navSlider, SIGNAL(valueChanged(int)), plot, SLOT(MoveTo(int))); + QObject::connect(plot, SIGNAL(startMaxChanged(uint32_t)), this, SLOT(updateNavSlider(void))); + QObject::connect(plot, SIGNAL(graphStartChanged(uint32_t)), this, SLOT(updateNavSlider(void))); + // plot window title QString pt = QString("[*]Plot [ %1 ]").arg(g_conn.serial_port_name); setWindowTitle(pt); @@ -794,7 +817,7 @@ void Plot::paintEvent(QPaintEvent *event) { snprintf(scalestr, sizeof(scalestr), "[%2.2f %s] ", ((int32_t)(CursorBPos - CursorAPos)) / g_CursorScaleFactor, g_CursorScaleFactorUnit); } } - snprintf(str, sizeof(str), "@%u..%u dt=%i %szoom=%2.2f CursorAPos=%u CursorBPos=%u GridX=%lf GridY=%lf (%s) GridXoffset=%lf", + snprintf(str, sizeof(str), "@%u..%u dt=%i %szoom=%2.3f CursorAPos=%u CursorBPos=%u GridX=%lf GridY=%lf (%s) GridXoffset=%lf", g_GraphStart, g_GraphStop, CursorBPos - CursorAPos, @@ -809,6 +832,16 @@ void Plot::paintEvent(QPaintEvent *event) { ); painter.setPen(WHITE); painter.drawText(20, infoRect.bottom() - 3, str); + + if (startMaxOld != startMax) { + emit startMaxChanged(startMax); + } + startMaxOld = startMax; + + if (g_GraphStart != g_GraphStart_old) { + emit graphStartChanged(g_GraphStart); + } + g_GraphStart_old = g_GraphStart; } Plot::Plot(QWidget *parent) : QWidget(parent), g_GraphPixelsPerPoint(1) { @@ -838,17 +871,25 @@ void Plot::closeEvent(QCloseEvent *event) { gs_useOverlays = false; } +// every 4 steps the zoom doubles (or halves) +#define ZOOM_STEP (1.189207) +// limit zoom to 32 times in either direction +#define ZOOM_LIMIT (32) + void Plot::Zoom(double factor, uint32_t refX) { + double g_GraphPixelsPerPointNew = g_GraphPixelsPerPoint * factor; + if (factor >= 1) { // Zoom in - if (g_GraphPixelsPerPoint <= 25 * factor) { - g_GraphPixelsPerPoint *= factor; + if (g_GraphPixelsPerPointNew <= ZOOM_LIMIT) { + g_GraphPixelsPerPoint = g_GraphPixelsPerPointNew; if (refX > g_GraphStart) { g_GraphStart += (refX - g_GraphStart) - ((refX - g_GraphStart) / factor); } } } else { // Zoom out - if (g_GraphPixelsPerPoint >= 0.01 / factor) { - g_GraphPixelsPerPoint *= factor; + if (g_GraphPixelsPerPointNew >= (1.0 / ZOOM_LIMIT)) { + g_GraphPixelsPerPoint = g_GraphPixelsPerPointNew; + // shift graph towards refX when zooming out if (refX > g_GraphStart) { if (g_GraphStart >= ((refX - g_GraphStart) / factor) - (refX - g_GraphStart)) { g_GraphStart -= ((refX - g_GraphStart) / factor) - (refX - g_GraphStart); @@ -860,6 +901,22 @@ void Plot::Zoom(double factor, uint32_t refX) { } } +void Plot::MoveTo(uint32_t pos) { + if (g_GraphTraceLen == 0) return; + g_GraphStart = pos; + + QObject *signalSender = sender(); + if (signalSender != nullptr && signalSender != this) { + // Update if it's triggered by a signal from other object + this->update(); + } +} + +void Plot::MoveTo(int pos) { + MoveTo((uint32_t)((pos >= 0) ? pos : 0)); + // sender() is still valid in the inner call +} + void Plot::Move(int offset) { if (g_GraphTraceLen == 0) return; if (offset > 0) { // Move right @@ -885,7 +942,6 @@ void Plot::Move(int offset) { void Plot::Trim(void) { uint32_t lref, rref; - const double zoom_offset = 1.148698354997035; // 2**(1/5) if ((CursorAPos == 0) || (CursorBPos == 0)) { // if we don't have both cursors set lref = g_GraphStart; rref = g_GraphStop; @@ -902,12 +958,12 @@ void Plot::Trim(void) { } else { lref = CursorAPos < CursorBPos ? CursorAPos : CursorBPos; rref = CursorAPos < CursorBPos ? CursorBPos : CursorAPos; - // g_GraphPixelsPerPoint mush remain a power of zoom_offset + // g_GraphPixelsPerPoint must remain a power of ZOOM_STEP double GPPPtarget = g_GraphPixelsPerPoint * (g_GraphStop - g_GraphStart) / (rref - lref); while (g_GraphPixelsPerPoint < GPPPtarget) { - g_GraphPixelsPerPoint *= zoom_offset; + g_GraphPixelsPerPoint *= ZOOM_STEP; } - g_GraphPixelsPerPoint /= zoom_offset; + g_GraphPixelsPerPoint /= ZOOM_STEP; CursorAPos -= lref; CursorBPos -= lref; } @@ -925,34 +981,26 @@ void Plot::wheelEvent(QWheelEvent *event) { const float move_offset = 0.05; // -120+shift => zoom in (5 times = *2) // 120+shift => zoom out (5 times = /2) - const double zoom_offset = 1.148698354997035; // 2**(1/5) - if (event->modifiers() & Qt::ShiftModifier) { -// event->position doesn't exist in QT5.12.8, both exist in 5.14.2 and event->x doesn't exist in 5.15.0 #if QT_VERSION >= 0x050d00 - uint32_t x = event->position().x(); + // event->position doesn't exist in QT5.12.8, both exist in 5.14.2 and event->x doesn't exist in 5.15.0 + uint32_t x = event->position().x(); + // event->angleDelta doesn't exist in QT4, both exist in 5.12.8 and 5.14.2 and event->delta doesn't exist in 5.15.0 + float delta = -event->angleDelta().y(); #else - uint32_t x = event->x(); + uint32_t x = event->x(); + float delta = -event->delta(); #endif + if (event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) { x -= WIDTH_AXES; x = (int)(x / g_GraphPixelsPerPoint); x += g_GraphStart; -// event->angleDelta doesn't exist in QT4, both exist in 5.12.8 and 5.14.2 and event->delta doesn't exist in 5.15.0 -#if QT_VERSION >= 0x050d00 - float delta = event->angleDelta().y(); -#else - float delta = event->delta(); -#endif if (delta < 0) { - Zoom(zoom_offset, x); + Zoom(ZOOM_STEP, x); } else { - Zoom(1.0 / zoom_offset, x); + Zoom(1.0 / ZOOM_STEP, x); } } else { -#if QT_VERSION >= 0x050d00 - Move(PageWidth * (-(float)event->angleDelta().y() / (120 / move_offset))); -#else - Move(PageWidth * (-(float)event->delta() / (120 / move_offset))); -#endif + Move(PageWidth * delta * move_offset / 120); } this->update(); } @@ -972,7 +1020,6 @@ void Plot::mouseMoveEvent(QMouseEvent *event) { void Plot::keyPressEvent(QKeyEvent *event) { uint32_t offset; // Left/right movement offset (in sample size) - const double zoom_offset = 1.148698354997035; // 2**(1/5) if (event->modifiers() & Qt::ShiftModifier) { if (g_PlotGridX) @@ -983,22 +1030,22 @@ void Plot::keyPressEvent(QKeyEvent *event) { if (event->modifiers() & Qt::ControlModifier) offset = 1; else - offset = (int)(20 / g_GraphPixelsPerPoint); + offset = int(ZOOM_LIMIT / g_GraphPixelsPerPoint); } switch (event->key()) { case Qt::Key_Down: if (event->modifiers() & Qt::ShiftModifier) { if (event->modifiers() & Qt::ControlModifier) { - Zoom(zoom_offset, CursorBPos); + Zoom(ZOOM_STEP, CursorBPos); } else { - Zoom(2, CursorBPos); + Zoom(ZOOM_STEP * 2, CursorBPos); } } else { if (event->modifiers() & Qt::ControlModifier) { - Zoom(zoom_offset, CursorAPos); + Zoom(ZOOM_STEP, CursorAPos); } else { - Zoom(2, CursorAPos); + Zoom(ZOOM_STEP * 2, CursorAPos); } } break; @@ -1006,15 +1053,15 @@ void Plot::keyPressEvent(QKeyEvent *event) { case Qt::Key_Up: if (event->modifiers() & Qt::ShiftModifier) { if (event->modifiers() & Qt::ControlModifier) { - Zoom(1.0 / zoom_offset, CursorBPos); + Zoom(1.0 / ZOOM_STEP, CursorBPos); } else { - Zoom(0.5, CursorBPos); + Zoom(1.0 / (ZOOM_STEP * 2), CursorBPos); } } else { if (event->modifiers() & Qt::ControlModifier) { - Zoom(1.0 / zoom_offset, CursorAPos); + Zoom(1.0 / ZOOM_STEP, CursorAPos); } else { - Zoom(0.5, CursorAPos); + Zoom(1.0 / (ZOOM_STEP * 2), CursorAPos); } } break; diff --git a/client/src/proxguiqt.h b/client/src/proxguiqt.h index ac19a99c9..244d083e3 100644 --- a/client/src/proxguiqt.h +++ b/client/src/proxguiqt.h @@ -38,6 +38,8 @@ class ProxWidget; * @brief The actual plot, black area were we paint the graph */ class Plot: public QWidget { + Q_OBJECT; //needed for slot/signal classes + private: QWidget *master; double g_GraphPixelsPerPoint; // How many visual pixels are between each sample point (x axis) @@ -55,16 +57,24 @@ class Plot: public QWidget { public: Plot(QWidget *parent = 0); + public slots: + void Zoom(double factor, uint32_t refX); + void MoveTo(uint32_t pos); + void MoveTo(int pos); + void Move(int offset); + protected: void paintEvent(QPaintEvent *event); void closeEvent(QCloseEvent *event); - void Zoom(double factor, uint32_t refX); - void Move(int offset); void Trim(void); void wheelEvent(QWheelEvent *event); void mouseMoveEvent(QMouseEvent *event); void mousePressEvent(QMouseEvent *event) { mouseMoveEvent(event); } void keyPressEvent(QKeyEvent *event); + + signals: + void startMaxChanged(uint32_t startMax); + void graphStartChanged(uint32_t graphStart); }; class ProxGuiQT; @@ -97,6 +107,7 @@ class ProxWidget : public QWidget { Plot *plot; Ui::Form *opsController; SliderWidget *controlWidget; + QSlider *navSlider; public: ProxWidget(QWidget *parent = 0, ProxGuiQT *master = NULL); @@ -120,6 +131,7 @@ class ProxWidget : public QWidget { void vchange_askedge(int v); void vchange_dthr_up(int v); void vchange_dthr_down(int v); + void updateNavSlider(void); }; class WorkerThread : public QThread { diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index 378b9f0dc..476b900a0 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -127,24 +127,84 @@ static void showBanner(void) { static const char *prompt_dev = ""; static const char *prompt_ctx = ""; +static const char *prompt_net = ""; -static void prompt_compose(char *buf, size_t buflen, const char *promptctx, const char *promptdev) { - snprintf(buf, buflen - 1, PROXPROMPT_COMPOSE, promptdev, promptctx); + +static void prompt_set(void) { + if (g_session.pm3_present) { + + switch (g_conn.send_via_ip) { + case PM3_TCPv4: + prompt_net = PROXPROMPT_NET_TCPV4; + break; + case PM3_TCPv6: + prompt_net = PROXPROMPT_NET_TCPV6; + break; + case PM3_UDPv4: + prompt_net = PROXPROMPT_NET_UDPV4; + break; + case PM3_UDPv6: + prompt_net = PROXPROMPT_NET_UDPV6; + break; + case PM3_NONE: + prompt_net = PROXPROMPT_NET_NONE; + break; + default: + break; + } + + if (g_conn.send_via_fpc_usart) + prompt_dev = PROXPROMPT_DEV_FPC; + else + prompt_dev = PROXPROMPT_DEV_USB; + + } else { + prompt_dev = PROXPROMPT_DEV_OFFLINE; + } } +static void prompt_compose(char *buf, size_t buflen, const char *promptctx, const char *promptdev, const char *promptnet, bool no_newline) { + if (no_newline) { + snprintf(buf, buflen - 1, PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx); + } else { + snprintf(buf, buflen - 1, "\r \r" PROXPROMPT_COMPOSE, promptdev, promptnet, promptctx); + } +} + +static bool c_update_reconnect_prompt = false; + +// This function is hooked via RL_EVENT_HOOK. static int check_comm(void) { // If communications thread goes down. Device disconnected then this should hook up PM3 again. if (IsCommunicationThreadDead() && g_session.pm3_present) { - PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") " mode. Use "_YELLOW_("\"hw connect\"") " to reconnect\n"); + +#ifndef HAVE_READLINE + PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Use "_YELLOW_("\"hw connect\"") " to reconnect\n"); +#endif prompt_dev = PROXPROMPT_DEV_OFFLINE; char prompt[PROXPROMPT_MAX_SIZE] = {0}; - prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev); + prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, false); char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0}; memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors); pm3line_update_prompt(prompt_filtered); CloseProxmark(g_session.current_device); + StartReconnectProxmark(); + c_update_reconnect_prompt = true; } - msleep(10); + // its alive again + if (c_update_reconnect_prompt && IsReconnectedOk() && g_session.pm3_present) { + + prompt_set(); + + char prompt[PROXPROMPT_MAX_SIZE] = {0}; + prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, false); + char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0}; + memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors); + pm3line_update_prompt(prompt_filtered); + c_update_reconnect_prompt = false; + } + + msleep(50); return 0; } @@ -222,7 +282,7 @@ main_loop(char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) { uint16_t script_cmd_len = 0; if (execCommand) { script_cmd_len = strlen(script_cmd); - strcreplace(script_cmd, script_cmd_len, ';', '\0'); + str_creplace(script_cmd, script_cmd_len, ';', '\0'); } bool stdinOnPipe = !isatty(STDIN_FILENO); char script_cmd_buf[256] = {0x00}; // iceman, needs lua script the same file_path_buffer as the rest @@ -250,16 +310,13 @@ main_loop(char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) { if (g_session.incognito) { PrintAndLogEx(INFO, "No history will be recorded"); } else { - bool loaded_history = false; if (searchHomeFilePath(&g_session.history_path, NULL, PROXHISTORY, true) != PM3_SUCCESS) { g_session.history_path = NULL; - } else { - loaded_history = (pm3line_load_history(g_session.history_path) == PM3_SUCCESS); - } - if (loaded_history) { - pm3line_install_signals(); - } else { PrintAndLogEx(ERR, "No history will be recorded"); + } else { + if (pm3line_load_history(g_session.history_path) != PM3_SUCCESS) { + PrintAndLogEx(INFO, "No previous history could be loaded"); + } } } @@ -267,14 +324,8 @@ main_loop(char *script_cmds_file, char *script_cmd, bool stayInCommandLoop) { while (1) { bool printprompt = false; - if (g_session.pm3_present) { - if (g_conn.send_via_fpc_usart == false) - prompt_dev = PROXPROMPT_DEV_USB; - else - prompt_dev = PROXPROMPT_DEV_FPC; - } else { - prompt_dev = PROXPROMPT_DEV_OFFLINE; - } + + prompt_set(); check_script: // If there is a script file @@ -285,19 +336,22 @@ check_script: // read script file if (fgets(script_cmd_buf, sizeof(script_cmd_buf), current_cmdscriptfile()) == NULL) { - if (!pop_cmdscriptfile()) + if (pop_cmdscriptfile() == false) { break; - + } goto check_script; - } else { - prompt_ctx = PROXPROMPT_CTX_SCRIPTFILE; - // remove linebreaks - strcleanrn(script_cmd_buf, sizeof(script_cmd_buf)); - - cmd = str_dup(script_cmd_buf); - if (cmd != NULL) - printprompt = true; } + + prompt_ctx = PROXPROMPT_CTX_SCRIPTFILE; + + // remove linebreaks + str_cleanrn(script_cmd_buf, sizeof(script_cmd_buf)); + + cmd = str_dup(script_cmd_buf); + if (cmd != NULL) { + printprompt = true; + } + } else { // If there is a script command if (execCommand) { @@ -333,15 +387,15 @@ check_script: fromInteractive = false; script_cmd = script_cmd_buf; script_cmd_len = strlen(script_cmd); - strcreplace(script_cmd, script_cmd_len, ';', '\0'); + str_creplace(script_cmd, script_cmd_len, ';', '\0'); // remove linebreaks - strcleanrn(script_cmd, script_cmd_len); + str_cleanrn(script_cmd, script_cmd_len); goto check_script; } else { pm3line_check(check_comm); prompt_ctx = PROXPROMPT_CTX_INTERACTIVE; char prompt[PROXPROMPT_MAX_SIZE] = {0}; - prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev); + prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, true); char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0}; memcpy_filter_ansi(prompt_filtered, prompt, sizeof(prompt_filtered), !g_session.supports_colors); g_pendingPrompt = true; @@ -355,9 +409,9 @@ check_script: stayInCommandLoop = true; fromInteractive = true; script_cmd_len = strlen(script_cmd); - strcreplace(script_cmd, script_cmd_len, ';', '\0'); + str_creplace(script_cmd, script_cmd_len, ';', '\0'); // remove linebreaks - strcleanrn(script_cmd, script_cmd_len); + str_cleanrn(script_cmd, script_cmd_len); goto check_script; } fflush(NULL); @@ -391,7 +445,7 @@ check_script: g_printAndLog &= PRINTANDLOG_LOG; } char prompt[PROXPROMPT_MAX_SIZE] = {0}; - prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev); + prompt_compose(prompt, sizeof(prompt), prompt_ctx, prompt_dev, prompt_net, true); // always filter RL magic separators if not using readline char prompt_filtered[PROXPROMPT_MAX_SIZE] = {0}; memcpy_filter_rlmarkers(prompt_filtered, prompt, sizeof(prompt_filtered)); @@ -432,8 +486,9 @@ check_script: msleep(100); // Make sure command is sent before killing client } - while (current_cmdscriptfile()) + while (current_cmdscriptfile()) { pop_cmdscriptfile(); + } pm3line_flush_history(); @@ -574,9 +629,10 @@ static void show_help(bool showFullHelp, char *exec_name) { PrintAndLogEx(NORMAL, " -s/--script-file script file with one Proxmark3 command per line"); PrintAndLogEx(NORMAL, " -i/--interactive enter interactive mode after executing the script or the command"); PrintAndLogEx(NORMAL, " --incognito do not use history, prefs file nor log files"); + PrintAndLogEx(NORMAL, " --ncpu override number of CPU cores"); PrintAndLogEx(NORMAL, "\nOptions in flasher mode:"); PrintAndLogEx(NORMAL, " --flash flash Proxmark3, requires at least one --image"); - PrintAndLogEx(NORMAL, " --reboot-bootloader reboot Proxmark3 into bootloader mode"); + PrintAndLogEx(NORMAL, " --reboot-to-bootloader reboot Proxmark3 into bootloader mode"); PrintAndLogEx(NORMAL, " --unlock-bootloader Enable flashing of bootloader area *DANGEROUS* (need --flash)"); PrintAndLogEx(NORMAL, " --force Enable flashing even if firmware seems to not match client version"); PrintAndLogEx(NORMAL, " --image image to flash. Can be specified several times."); @@ -604,7 +660,7 @@ static void show_help(bool showFullHelp, char *exec_name) { } } -static int flash_pm3(char *serial_port_name, uint8_t num_files, char *filenames[FLASH_MAX_FILES], bool can_write_bl, bool force) { +static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *filenames[FLASH_MAX_FILES], bool can_write_bl, bool force) { int ret = PM3_EUNDEF; flash_file_t files[FLASH_MAX_FILES]; @@ -646,6 +702,7 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, char *filenames[ if (OpenProxmark(&g_session.current_device, serial_port_name, true, 60, true, FLASHMODE_SPEED)) { PrintAndLogEx(NORMAL, _GREEN_(" found")); + msleep(200); } else { PrintAndLogEx(ERR, "Could not find Proxmark3 on " _RED_("%s") ".\n", serial_port_name); ret = PM3_ETIMEOUT; @@ -710,7 +767,7 @@ static int reboot_bootloader_pm3(char *serial_port_name) { } PrintAndLogEx(NORMAL, _GREEN_(" found")); - return flash_reboot_bootloader(serial_port_name); + return flash_reboot_bootloader(serial_port_name, true); } #endif //LIBPM3 @@ -754,7 +811,7 @@ int main(int argc, char *argv[]) { bool flash_force = false; bool debug_mode_forced = false; int flash_num_files = 0; - char *flash_filenames[FLASH_MAX_FILES]; + const char *flash_filenames[FLASH_MAX_FILES]; // color management: // 1. default = no color @@ -995,6 +1052,23 @@ int main(int argc, char *argv[]) { continue; } + if (strcmp(argv[i], "--ncpu") == 0) { + if (i + 1 == argc) { + PrintAndLogEx(ERR, _RED_("ERROR:") " missing CPU number specification after --ncpu\n"); + show_help(false, exec_name); + return 1; + } + long int ncpus = strtol(argv[i + 1], NULL, 10); + const int detected_cpus = detect_num_CPUs(); + if (ncpus < 0 || ncpus >= detected_cpus) { + PrintAndLogEx(ERR, _RED_("ERROR:") " invalid number of CPU cores: --ncpu " _YELLOW_("%s") " (available: %d)\n", argv[i + 1], detected_cpus); + return 1; + } + g_numCPUs = ncpus; + i++; + continue; + } + // We got an unknown parameter PrintAndLogEx(ERR, _RED_("ERROR:") " invalid parameter: " _YELLOW_("%s") "\n", argv[i]); show_help(false, exec_name); @@ -1061,19 +1135,22 @@ int main(int argc, char *argv[]) { } if (g_session.pm3_present && (TestProxmark(g_session.current_device) != PM3_SUCCESS)) { - PrintAndLogEx(ERR, _RED_("ERROR:") " cannot communicate with the Proxmark\n"); + PrintAndLogEx(ERR, _RED_("ERROR:") " cannot communicate with the Proxmark3\n"); CloseProxmark(g_session.current_device); } - if ((port != NULL) && (!g_session.pm3_present)) + if ((port != NULL) && (!g_session.pm3_present)) { exit(EXIT_FAILURE); + } - if (!g_session.pm3_present) - PrintAndLogEx(INFO, "Running in " _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name); + if (!g_session.pm3_present) { + PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name); + } // ascii art only in interactive client - if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !flash_mode && !reboot_bootloader_mode) + if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !flash_mode && !reboot_bootloader_mode) { showBanner(); + } // Save settings if not loaded from settings json file. // Doing this here will ensure other checks and updates are saved to over rule default diff --git a/client/src/proxmark3.h b/client/src/proxmark3.h index a9f80ae91..bc6dcb3e7 100644 --- a/client/src/proxmark3.h +++ b/client/src/proxmark3.h @@ -24,7 +24,7 @@ #define PROXPROMPT_MAX_SIZE 255 -#define PROXPROMPT_COMPOSE "[" "%s%s" "] pm3 --> " +#define PROXPROMPT_COMPOSE "[" "%s%s%s" "] pm3 --> " #define PROXPROMPT_CTX_SCRIPTFILE "|" _RL_GREEN_("script") #define PROXPROMPT_CTX_SCRIPTCMD "|" _RL_GREEN_("script") @@ -35,8 +35,15 @@ #define PROXPROMPT_DEV_FPC _RL_BOLD_GREEN_("fpc") #define PROXPROMPT_DEV_OFFLINE _RL_BOLD_RED_("offline") +#define PROXPROMPT_NET_TCPV4 "|" _RL_BOLD_GREEN_("tcp") +#define PROXPROMPT_NET_UDPV4 "|" _RL_BOLD_GREEN_("udp") +#define PROXPROMPT_NET_TCPV6 "|" _RL_BOLD_GREEN_("tcp v6") +#define PROXPROMPT_NET_UDPV6 "|" _RL_BOLD_GREEN_("udp v6") +#define PROXPROMPT_NET_NONE "" + + #define PROXHISTORY "history.txt" -#define PROXLOG "log_%Y%m%d.txt" +#define PROXLOG "log_%Y%m%d%H%M%S.txt" #define MAX_NESTED_CMDSCRIPT 10 #define MAX_NESTED_LUASCRIPT 10 diff --git a/client/src/scripting.c b/client/src/scripting.c index c96d593df..613562978 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -1269,8 +1269,9 @@ static int l_searchfile(lua_State *L) { size_t size; // data const char *filename = luaL_checklstring(L, 1, &size); - if (size == 0) + if (size == 0) { return returnToLuaWithError(L, "Must specify filename"); + } const char *suffix = luaL_checklstring(L, 2, &size); char *path; diff --git a/client/src/uart/ringbuffer.c b/client/src/uart/ringbuffer.c new file mode 100644 index 000000000..b0ed173e3 --- /dev/null +++ b/client/src/uart/ringbuffer.c @@ -0,0 +1,118 @@ +#include "ringbuffer.h" +#include + +RingBuffer *RingBuf_create(int capacity) { + RingBuffer *buffer = (RingBuffer *)calloc(sizeof(RingBuffer), sizeof(uint8_t)); + if (!buffer) { + return NULL; + } + + buffer->data = (uint8_t *)calloc(capacity, sizeof(uint8_t)); + if (!buffer->data) { + free(buffer); + return NULL; + } + + buffer->capacity = capacity; + buffer->size = 0; + buffer->front = 0; + buffer->rear = 0; + + return buffer; +} + +inline bool RingBuf_isFull(RingBuffer *buffer) { + return buffer->size == buffer->capacity; +} + +inline bool RingBuf_isEmpty(RingBuffer *buffer) { + return buffer->size == 0; +} + +bool RingBuf_enqueue(RingBuffer *buffer, uint8_t value) { + if (RingBuf_isFull(buffer)) { + return false; + } + + buffer->data[buffer->rear] = value; + buffer->rear = (buffer->rear + 1) % buffer->capacity; + buffer->size++; + return true; +} + +bool RingBuf_dequeue(RingBuffer *buffer, uint8_t *value) { + if (RingBuf_isEmpty(buffer)) { + return false; + } + + *value = buffer->data[buffer->front]; + buffer->front = (buffer->front + 1) % buffer->capacity; + buffer->size--; + return true; +} + +int RingBuf_enqueueBatch(RingBuffer *buffer, const uint8_t *values, int count) { + int processed = 0; + + if (RingBuf_getAvailableSize(buffer) < count) { + count = RingBuf_getAvailableSize(buffer); + } + + for (int i = 0; i < count; i++) { + buffer->data[buffer->rear] = values[i]; + buffer->rear = (buffer->rear + 1) % buffer->capacity; + processed++; + } + + buffer->size += processed; + + return processed; +} + +int RingBuf_dequeueBatch(RingBuffer *buffer, uint8_t *values, int count) { + int processed = 0; + + if (buffer->size < count) { + count = buffer->size; + } + + for (int i = 0; i < count; i++) { + values[i] = buffer->data[buffer->front]; + buffer->front = (buffer->front + 1) % buffer->capacity; + processed++; + } + + buffer->size -= processed; + + return processed; +} + +inline int RingBuf_getUsedSize(RingBuffer *buffer) { + return buffer->size; +} + +inline int RingBuf_getAvailableSize(RingBuffer *buffer) { + return (buffer->capacity) - (buffer->size); +} + +void RingBuf_destroy(RingBuffer *buffer) { + if (buffer != NULL) + free(buffer->data); + free(buffer); +} + +inline int RingBuf_getContinousAvailableSize(RingBuffer *buffer) { + const int availableSize = RingBuf_getAvailableSize(buffer); + const int continousSize = (buffer->capacity) - (buffer->rear); + return (availableSize < continousSize) ? availableSize : continousSize; +} + +inline void RingBuf_postEnqueueBatch(RingBuffer *buffer, int count) { + // no check there + buffer->rear = (buffer->rear + count) % buffer->capacity; + buffer->size += count; +} + +inline uint8_t *RingBuf_getRearPtr(RingBuffer *buffer) { + return buffer->data + buffer->rear; +} diff --git a/client/src/uart/ringbuffer.h b/client/src/uart/ringbuffer.h new file mode 100644 index 000000000..ba29ddd77 --- /dev/null +++ b/client/src/uart/ringbuffer.h @@ -0,0 +1,31 @@ +#ifndef _RINGBUFFER_H_ +#define _RINGBUFFER_H_ + +#include +#include + +typedef struct { + uint8_t *data; + int capacity; + int size; + int front; + int rear; +} RingBuffer; + +RingBuffer *RingBuf_create(int capacity); +bool RingBuf_isFull(RingBuffer *buffer); +bool RingBuf_isEmpty(RingBuffer *buffer); +bool RingBuf_enqueue(RingBuffer *buffer, uint8_t value); +bool RingBuf_dequeue(RingBuffer *buffer, uint8_t *value); +int RingBuf_enqueueBatch(RingBuffer *buffer, const uint8_t *values, int count); +int RingBuf_dequeueBatch(RingBuffer *buffer, uint8_t *values, int count); +int RingBuf_getUsedSize(RingBuffer *buffer); +int RingBuf_getAvailableSize(RingBuffer *buffer); +void RingBuf_destroy(RingBuffer *buffer); + +// for direct write +int RingBuf_getContinousAvailableSize(RingBuffer *buffer); +void RingBuf_postEnqueueBatch(RingBuffer *buffer, int count); +uint8_t *RingBuf_getRearPtr(RingBuffer *buffer); + +#endif diff --git a/client/src/uart/uart.h b/client/src/uart/uart.h index ba4537769..22aab5556 100644 --- a/client/src/uart/uart.h +++ b/client/src/uart/uart.h @@ -40,8 +40,10 @@ typedef void *serial_port; * used for future references to that port. * * On errors, this method returns INVALID_SERIAL_PORT or CLAIMED_SERIAL_PORT. + * If slient is set to false, this function will print the error information + * when error occurs. */ -serial_port uart_open(const char *pcPortName, uint32_t speed); +serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient); /* Closes the given port. */ @@ -74,8 +76,21 @@ bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed); */ uint32_t uart_get_speed(const serial_port sp); -/* Reconfigure timeouts +/* Reconfigure timeouts (ms) */ int uart_reconfigure_timeouts(uint32_t value); -#endif // _UART_H_ +/* Get timeouts (ms) + */ +uint32_t uart_get_timeouts(void); + +/* Specify the outbound address and port for TCP/UDP connections + */ +bool uart_bind(void *socket, const char *bindAddrStr, const char *bindPortStr, bool isBindingIPv6); + +/* Parse address and port from string. + This could change the addrPortStr + */ +int uart_parse_address_port(char *addrPortStr, const char **addrStr, const char **portStr, bool *isIPv6); + +#endif // _UART_H_ diff --git a/client/src/uart/uart_common.c b/client/src/uart/uart_common.c new file mode 100644 index 000000000..e10897326 --- /dev/null +++ b/client/src/uart/uart_common.c @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Generic uart / rs232/ serial port library +//----------------------------------------------------------------------------- + +#include "uart.h" + +#include +#include +#include + +#include "comms.h" +#include "ui.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#else +#include +#include +#include +#include +#include +#endif + +bool uart_bind(void *socket, const char *bindAddrStr, const char *bindPortStr, bool isBindingIPv6) { + if (bindAddrStr == NULL && bindPortStr == NULL) + return true; // no need to bind + + struct sockaddr_storage bindSockaddr; + memset(&bindSockaddr, 0, sizeof(bindSockaddr)); + int bindPort = 0; // 0: port unspecified + if (bindPortStr != NULL) + bindPort = atoi(bindPortStr); + + if (!isBindingIPv6) { + struct sockaddr_in *bindSockaddr4 = (struct sockaddr_in *)&bindSockaddr; + bindSockaddr4->sin_family = AF_INET; + bindSockaddr4->sin_port = htons(bindPort); + if (bindAddrStr == NULL) + bindSockaddr4->sin_addr.s_addr = INADDR_ANY; + else + bindSockaddr4->sin_addr.s_addr = inet_addr(bindAddrStr); + } else { + struct sockaddr_in6 *bindSockaddr6 = (struct sockaddr_in6 *)&bindSockaddr; + bindSockaddr6->sin6_family = AF_INET6; + bindSockaddr6->sin6_port = htons(bindPort); + if (bindAddrStr == NULL) + bindSockaddr6->sin6_addr = in6addr_any; + else + inet_pton(AF_INET6, bindAddrStr, &(bindSockaddr6->sin6_addr)); + } +#ifdef _WIN32 + int res = bind(*(SOCKET *)socket, (struct sockaddr *)&bindSockaddr, sizeof(bindSockaddr)); +#else + int res = bind(*(int *)socket, (struct sockaddr *)&bindSockaddr, sizeof(bindSockaddr)); +#endif + return (res >= 0); +} + +int uart_parse_address_port(char *addrPortStr, const char **addrStr, const char **portStr, bool *isIPv6) { + + if (addrPortStr == NULL || addrStr == NULL || portStr == NULL) { + return PM3_EINVARG; + } + + *addrStr = addrPortStr; + *portStr = NULL; + + // find the start of the address + char *endBracket = strrchr(addrPortStr, ']'); + if (addrPortStr[0] == '[') { + *addrStr += 1; + if (endBracket == NULL) { + // [] unmatched + return PM3_ESOFT; + } + } + + if (isIPv6 != NULL) { + // Assume v4 + *isIPv6 = false; + } + + // find the port + char *lColon = strchr(addrPortStr, ':'); + char *rColon = strrchr(addrPortStr, ':'); + if (rColon == NULL) { + // no colon + // "", "[]" + *portStr = NULL; + } else if (lColon == rColon) { + // only one colon + // ":", "[]:" + *portStr = rColon + 1; + } else { + // two or more colon, IPv6 address + // "[]:" + // "", "[]" + if (endBracket != NULL && rColon == endBracket + 1) { + *portStr = rColon + 1; + } else { + *portStr = NULL; + } + + if (isIPv6 != NULL) { + *isIPv6 = true; + } + } + + // handle the end of the address + if (endBracket != NULL) { + *endBracket = '\0'; + } else if (rColon != NULL && lColon == rColon) { + *rColon = '\0'; + } + + return PM3_SUCCESS; +} diff --git a/client/src/uart/uart_posix.c b/client/src/uart/uart_posix.c index b2a858e09..0863cc9b7 100644 --- a/client/src/uart/uart_posix.c +++ b/client/src/uart/uart_posix.c @@ -21,6 +21,7 @@ #define _DEFAULT_SOURCE #include "uart.h" +#include "ringbuffer.h" #include #include @@ -30,9 +31,11 @@ #include #include #include +#include #include #include #include +#include #ifdef HAVE_BLUEZ #include @@ -46,12 +49,16 @@ #ifndef SOL_TCP # define SOL_TCP IPPROTO_TCP #endif +#ifndef SOL_UDP +# define SOL_UDP IPPROTO_UDP +#endif typedef struct termios term_info; typedef struct { int fd; // Serial port file descriptor term_info tiOld; // Terminal info before using the port term_info tiNew; // Terminal info during the transaction + RingBuffer *udpBuffer; } serial_port_unix_t_t; // see pm3_cmd.h @@ -62,6 +69,7 @@ struct timeval timeout = { static uint32_t newtimeout_value = 0; static bool newtimeout_pending = false; +static uint8_t rx_empty_counter = 0; int uart_reconfigure_timeouts(uint32_t value) { newtimeout_value = value; @@ -69,7 +77,11 @@ int uart_reconfigure_timeouts(uint32_t value) { return PM3_SUCCESS; } -serial_port uart_open(const char *pcPortName, uint32_t speed) { +uint32_t uart_get_timeouts(void) { + return newtimeout_value; +} + +serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) { serial_port_unix_t_t *sp = calloc(sizeof(serial_port_unix_t_t), sizeof(uint8_t)); if (sp == 0) { @@ -77,56 +89,119 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { return INVALID_SERIAL_PORT; } + sp->udpBuffer = NULL; + rx_empty_counter = 0; // init timeouts timeout.tv_usec = UART_FPC_CLIENT_RX_TIMEOUT_MS * 1000; + g_conn.send_via_local_ip = false; + g_conn.send_via_ip = PM3_NONE; - char *prefix = strdup(pcPortName); + char *prefix = str_dup(pcPortName); if (prefix == NULL) { - PrintAndLogEx(ERR, "error: string duplication"); + PrintAndLogEx(ERR, "error: string duplication"); free(sp); return INVALID_SERIAL_PORT; } str_lower(prefix); - if (memcmp(prefix, "tcp:", 4) == 0) { - free(prefix); + bool isTCP = false; + bool isUDP = false; + bool isBluetooth = false; + bool isUnixSocket = false; + if (strlen(prefix) > 4) { + isTCP = (memcmp(prefix, "tcp:", 4) == 0); + isUDP = (memcmp(prefix, "udp:", 4) == 0); + } + if (strlen(prefix) > 3) { + isBluetooth = (memcmp(prefix, "bt:", 3) == 0); + } + if (strlen(prefix) > 7) { + isUnixSocket = (memcmp(prefix, "socket:", 7) == 0); + } - if (strlen(pcPortName) <= 4) { - free(sp); - return INVALID_SERIAL_PORT; - } + if (isTCP || isUDP) { + + free(prefix); struct addrinfo *addr = NULL, *rp; - char *addrstr = strdup(pcPortName + 4); - if (addrstr == NULL) { + char *addrPortStr = str_dup(pcPortName + 4); + if (addrPortStr == NULL) { PrintAndLogEx(ERR, "error: string duplication"); free(sp); return INVALID_SERIAL_PORT; } - timeout.tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000; + timeout.tv_usec = UART_NET_CLIENT_RX_TIMEOUT_MS * 1000; - char *colon = strrchr(addrstr, ':'); - const char *portstr; - if (colon) { - portstr = colon + 1; - *colon = '\0'; - } else { - portstr = "18888"; + // find the "bind" option + char *bindAddrPortStr = strstr(addrPortStr, ",bind="); + const char *bindAddrStr = NULL; + const char *bindPortStr = NULL; + bool isBindingIPv6 = false; + + if (bindAddrPortStr != NULL) { + *bindAddrPortStr = '\0'; // as the end of target address (and port) + bindAddrPortStr += 6; // strlen(",bind=") + + int result = uart_parse_address_port(bindAddrPortStr, &bindAddrStr, &bindPortStr, &isBindingIPv6); + if (result != PM3_SUCCESS) { + if (result == PM3_ESOFT) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched in bind option"); + } else { + PrintAndLogEx(ERR, "error: failed to parse address and port in bind option"); + } + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + + // for bind option, it's possible to only specify address or port + if (strlen(bindAddrStr) == 0) { + bindAddrStr = NULL; + } + if (bindPortStr != NULL && strlen(bindPortStr) == 0) { + bindPortStr = NULL; + } } + const char *addrStr = NULL; + const char *portStr = NULL; + bool isIPv6 = false; + + int result = uart_parse_address_port(addrPortStr, &addrStr, &portStr, &isIPv6); + if (result != PM3_SUCCESS) { + if (result == PM3_ESOFT) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched"); + } else { + PrintAndLogEx(ERR, "error: failed to parse address and port"); + } + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + + g_conn.send_via_ip = isIPv6 ? (isTCP ? PM3_TCPv6 : PM3_UDPv6) : (isTCP ? PM3_TCPv4 : PM3_UDPv4); + portStr = (portStr == NULL) ? "18888" : portStr; + struct addrinfo info; memset(&info, 0, sizeof(info)); - info.ai_socktype = SOCK_STREAM; + info.ai_family = PF_UNSPEC; + info.ai_socktype = isTCP ? SOCK_STREAM : SOCK_DGRAM; - int s = getaddrinfo(addrstr, portstr, &info, &addr); + if ((strstr(addrStr, "localhost") != NULL) || + (strstr(addrStr, "127.0.0.1") != NULL) || + (strstr(addrStr, "::1") != NULL)) { + g_conn.send_via_local_ip = true; + } + + int s = getaddrinfo(addrStr, portStr, &info, &addr); if (s != 0) { - PrintAndLogEx(ERR, "error: getaddrinfo: %s", gai_strerror(s)); + PrintAndLogEx(ERR, "error: getaddrinfo: %d: %s", s, gai_strerror(s)); freeaddrinfo(addr); - free(addrstr); + free(addrPortStr); free(sp); return INVALID_SERIAL_PORT; } @@ -135,40 +210,59 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { for (rp = addr; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (sfd == -1) + if (sfd == -1) { continue; + } - if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) + if (!uart_bind(&sfd, bindAddrStr, bindPortStr, isBindingIPv6)) { + PrintAndLogEx(ERR, "error: Could not bind. errno: %d", errno); + close(sfd); + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + + if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) { break; + } close(sfd); } freeaddrinfo(addr); - free(addrstr); + free(addrPortStr); if (rp == NULL) { /* No address succeeded */ - PrintAndLogEx(ERR, "error: Could not connect"); + if (slient == false) { + PrintAndLogEx(ERR, "error: Could not connect"); + } free(sp); return INVALID_SERIAL_PORT; } sp->fd = sfd; - int one = 1; - int res = setsockopt(sp->fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)); - if (res != 0) { - free(sp); - return INVALID_SERIAL_PORT; + if (isTCP) { + int one = 1; + int res = setsockopt(sp->fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)); + if (res != 0) { + free(sp); + return INVALID_SERIAL_PORT; + } + } else if (isUDP) { + sp->udpBuffer = RingBuf_create(MAX(sizeof(PacketResponseNGRaw), sizeof(PacketResponseOLD)) * 30); } + return sp; } - if (memcmp(prefix, "bt:", 3) == 0) { + if (isBluetooth) { free(prefix); #ifdef HAVE_BLUEZ if (strlen(pcPortName) != 20) { + PrintAndLogEx(ERR, "Error: wrong Bluetooth MAC address length"); free(sp); return INVALID_SERIAL_PORT; } @@ -189,6 +283,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { free(sp); return INVALID_SERIAL_PORT; } + int sfd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); if (sfd == -1) { PrintAndLogEx(ERR, "Error opening Bluetooth socket"); @@ -196,8 +291,11 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { free(sp); return INVALID_SERIAL_PORT; } + if (connect(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - PrintAndLogEx(ERR, "Error: cannot connect device " _YELLOW_("%s") " over Bluetooth", addrstr); + if (slient == false) { + PrintAndLogEx(ERR, "Error: cannot connect device " _YELLOW_("%s") " over Bluetooth", addrstr); + } close(sfd); free(addrstr); free(sp); @@ -205,6 +303,8 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } sp->fd = sfd; + + g_conn.send_via_ip = PM3_NONE; return sp; #else // HAVE_BLUEZ PrintAndLogEx(ERR, "Sorry, this client doesn't support native Bluetooth addresses"); @@ -216,16 +316,11 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { // Is local socket buffer, not a TCP or any net connection! // so, you can't connect with address like: 127.0.0.1, or any IP // see http://man7.org/linux/man-pages/man7/unix.7.html - if (memcmp(prefix, "socket:", 7) == 0) { + if (isUnixSocket) { free(prefix); - if (strlen(pcPortName) <= 7) { - free(sp); - return INVALID_SERIAL_PORT; - } - // we must use max timeout! - timeout.tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000; + timeout.tv_usec = UART_NET_CLIENT_RX_TIMEOUT_MS * 1000; size_t servernameLen = (strlen(pcPortName) - 7) + 1; char serverNameBuf[servernameLen]; @@ -256,6 +351,8 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } sp->fd = localsocket; + + g_conn.send_via_ip = PM3_NONE; return sp; } @@ -285,6 +382,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { // Try to retrieve the old (current) terminal info struct if (tcgetattr(sp->fd, &sp->tiOld) == -1) { + PrintAndLogEx(ERR, "error: UART get terminal info attribute"); uart_close(sp); return INVALID_SERIAL_PORT; } @@ -305,6 +403,8 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { // Try to set the new terminal info struct if (tcsetattr(sp->fd, TCSANOW, &sp->tiNew) == -1) { + PrintAndLogEx(ERR, "error: UART set terminal info attribute"); + perror("tcsetattr() error"); uart_close(sp); return INVALID_SERIAL_PORT; } @@ -322,6 +422,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } } g_conn.uart_speed = uart_get_speed(sp); + g_conn.send_via_ip = PM3_NONE; return sp; } @@ -342,6 +443,7 @@ void uart_close(const serial_port sp) { //silent error message as it can be called from uart_open failing modes, e.g. when waiting for port to appear //PrintAndLogEx(ERR, "UART error while closing port"); } + RingBuf_destroy(spu->udpBuffer); close(spu->fd); free(sp); } @@ -350,6 +452,7 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin uint32_t byteCount; // FIONREAD returns size on 32b fd_set rfds; struct timeval tv; + const serial_port_unix_t_t *spu = (serial_port_unix_t_t *)sp; if (newtimeout_pending) { timeout.tv_usec = newtimeout_value * 1000; @@ -358,11 +461,30 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin // Reset the output count *pszRxLen = 0; do { + int res; + if (spu->udpBuffer != NULL) { + // for UDP connection, try to use the data from the buffer + + byteCount = RingBuf_getAvailableSize(spu->udpBuffer); + // Cap the number of bytes, so we don't overrun the buffer + if (pszMaxRxLen - (*pszRxLen) < byteCount) { +// PrintAndLogEx(ERR, "UART:: RX prevent overrun (have %u, need %u)", pszMaxRxLen - (*pszRxLen), byteCount); + byteCount = pszMaxRxLen - (*pszRxLen); + } + res = RingBuf_dequeueBatch(spu->udpBuffer, pbtRx + (*pszRxLen), byteCount); + *pszRxLen += res; + + if (*pszRxLen == pszMaxRxLen) { + // We have all the data we wanted. + return PM3_SUCCESS; + } + } + // Reset file descriptor FD_ZERO(&rfds); - FD_SET(((serial_port_unix_t_t *)sp)->fd, &rfds); + FD_SET(spu->fd, &rfds); tv = timeout; - int res = select(((serial_port_unix_t_t *)sp)->fd + 1, &rfds, NULL, NULL, &tv); + res = select(spu->fd + 1, &rfds, NULL, NULL, &tv); // Read error if (res < 0) { @@ -381,9 +503,44 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin } // Retrieve the count of the incoming bytes - res = ioctl(((serial_port_unix_t_t *)sp)->fd, FIONREAD, &byteCount); + res = ioctl(spu->fd, FIONREAD, &byteCount); // PrintAndLogEx(ERR, "UART:: RX ioctl res %d byteCount %u", res, byteCount); - if (res < 0) return PM3_ENOTTY; + if (res < 0) { + // error occurred (maybe disconnected) + // This happens when USB-CDC connection is lost + return PM3_ENOTTY; + } else if (byteCount == 0) { + // select() > 0 && byteCount > 0 ===> data available + // select() > 0 && byteCount always equals to 0 ===> maybe disconnected + // This happens when TCP connection is lost + rx_empty_counter++; + if (rx_empty_counter > 3) { + return PM3_ENOTTY; + } + } else { + rx_empty_counter = 0; + } + + // For UDP connection, put the incoming data into the buffer and handle them in the next round + if (spu->udpBuffer != NULL) { + if (RingBuf_getContinousAvailableSize(spu->udpBuffer) >= byteCount) { + // write to the buffer directly + res = read(spu->fd, RingBuf_getRearPtr(spu->udpBuffer), RingBuf_getAvailableSize(spu->udpBuffer)); + if (res >= 0) { + RingBuf_postEnqueueBatch(spu->udpBuffer, res); + } + } else { + // use transit buffer + uint8_t transitBuf[MAX(sizeof(PacketResponseNGRaw), sizeof(PacketResponseOLD)) * 30]; + res = read(spu->fd, transitBuf, RingBuf_getAvailableSize(spu->udpBuffer)); + RingBuf_enqueueBatch(spu->udpBuffer, transitBuf, res); + } + // Stop if the OS has some troubles reading the data + if (res < 0) { + return PM3_EIO; + } + continue; + } // Cap the number of bytes, so we don't overrun the buffer if (pszMaxRxLen - (*pszRxLen) < byteCount) { @@ -392,7 +549,7 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin } // There is something available, read the data - res = read(((serial_port_unix_t_t *)sp)->fd, pbtRx + (*pszRxLen), byteCount); + res = read(spu->fd, pbtRx + (*pszRxLen), byteCount); // Stop if the OS has some troubles reading the data if (res <= 0) { @@ -414,13 +571,14 @@ int uart_send(const serial_port sp, const uint8_t *pbtTx, const uint32_t len) { uint32_t pos = 0; fd_set rfds; struct timeval tv; + const serial_port_unix_t_t *spu = (serial_port_unix_t_t *)sp; while (pos < len) { // Reset file descriptor FD_ZERO(&rfds); - FD_SET(((serial_port_unix_t_t *)sp)->fd, &rfds); + FD_SET(spu->fd, &rfds); tv = timeout; - int res = select(((serial_port_unix_t_t *)sp)->fd + 1, NULL, &rfds, NULL, &tv); + int res = select(spu->fd + 1, NULL, &rfds, NULL, &tv); // Write error if (res < 0) { @@ -435,7 +593,7 @@ int uart_send(const serial_port sp, const uint8_t *pbtTx, const uint32_t len) { } // Send away the bytes - res = write(((serial_port_unix_t_t *)sp)->fd, pbtTx + pos, len - pos); + res = write(spu->fd, pbtTx + pos, len - pos); // Stop if the OS has some troubles sending the data if (res <= 0) @@ -629,4 +787,5 @@ uint32_t uart_get_speed(const serial_port sp) { }; return uiPortSpeed; } + #endif diff --git a/client/src/uart/uart_win32.c b/client/src/uart/uart_win32.c index 9cdcc41d1..9f8e7bef6 100644 --- a/client/src/uart/uart_win32.c +++ b/client/src/uart/uart_win32.c @@ -17,6 +17,7 @@ //----------------------------------------------------------------------------- #include "uart.h" +#include "ringbuffer.h" #include #include @@ -33,20 +34,22 @@ #include typedef struct { - HANDLE hPort; // Serial port handle - DCB dcb; // Device control settings - COMMTIMEOUTS ct; // Serial port time-out configuration - SOCKET hSocket; // Socket handle + HANDLE hPort; // Serial port handle + DCB dcb; // Device control settings + COMMTIMEOUTS ct; // Serial port time-out configuration + SOCKET hSocket; // Socket handle + RingBuffer *udpBuffer; // Buffer for UDP } serial_port_windows_t; // this is for TCP connection struct timeval timeout = { .tv_sec = 0, // 0 second - .tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000 + .tv_usec = UART_NET_CLIENT_RX_TIMEOUT_MS * 1000 }; uint32_t newtimeout_value = 0; bool newtimeout_pending = false; +uint8_t rx_empty_counter = 0; int uart_reconfigure_timeouts(uint32_t value) { newtimeout_value = value; @@ -54,13 +57,16 @@ int uart_reconfigure_timeouts(uint32_t value) { return PM3_SUCCESS; } +uint32_t uart_get_timeouts(void) { + return newtimeout_value; +} + static int uart_reconfigure_timeouts_polling(serial_port sp) { if (newtimeout_pending == false) return PM3_SUCCESS; newtimeout_pending = false; - serial_port_windows_t *spw; - spw = (serial_port_windows_t *)sp; + serial_port_windows_t *spw = (serial_port_windows_t *)sp; spw->ct.ReadIntervalTimeout = newtimeout_value; spw->ct.ReadTotalTimeoutMultiplier = 0; spw->ct.ReadTotalTimeoutConstant = newtimeout_value; @@ -76,7 +82,7 @@ static int uart_reconfigure_timeouts_polling(serial_port sp) { return PM3_SUCCESS; } -serial_port uart_open(const char *pcPortName, uint32_t speed) { +serial_port uart_open(const char *pcPortName, uint32_t speed, bool slient) { char acPortName[255] = {0}; serial_port_windows_t *sp = calloc(sizeof(serial_port_windows_t), sizeof(uint8_t)); sp->hSocket = INVALID_SOCKET; // default: serial port @@ -86,42 +92,91 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { return INVALID_SERIAL_PORT; } - char *prefix = strdup(pcPortName); + sp->udpBuffer = NULL; + rx_empty_counter = 0; + g_conn.send_via_local_ip = false; + g_conn.send_via_ip = PM3_NONE; + + char *prefix = str_dup(pcPortName); if (prefix == NULL) { - PrintAndLogEx(ERR, "error: string duplication"); + PrintAndLogEx(ERR, "error: string duplication"); free(sp); return INVALID_SERIAL_PORT; } str_lower(prefix); - if (memcmp(prefix, "tcp:", 4) == 0) { - free(prefix); + bool isTCP = false; + bool isUDP = false; + if (strlen(prefix) > 4) { + isTCP = (memcmp(prefix, "tcp:", 4) == 0); + isUDP = (memcmp(prefix, "udp:", 4) == 0); + } - if (strlen(pcPortName) <= 4) { - free(sp); - return INVALID_SERIAL_PORT; - } + if (isTCP || isUDP) { + + free(prefix); struct addrinfo *addr = NULL, *rp; - char *addrstr = strdup(pcPortName + 4); - if (addrstr == NULL) { + char *addrPortStr = str_dup(pcPortName + 4); + if (addrPortStr == NULL) { PrintAndLogEx(ERR, "error: string duplication"); free(sp); return INVALID_SERIAL_PORT; } - timeout.tv_usec = UART_TCP_CLIENT_RX_TIMEOUT_MS * 1000; + timeout.tv_usec = UART_NET_CLIENT_RX_TIMEOUT_MS * 1000; - char *colon = strrchr(addrstr, ':'); - const char *portstr; - if (colon) { - portstr = colon + 1; - *colon = '\0'; - } else { - portstr = "18888"; + // find the "bind" option + char *bindAddrPortStr = strstr(addrPortStr, ",bind="); + const char *bindAddrStr = NULL; + const char *bindPortStr = NULL; + bool isBindingIPv6 = false; + + if (bindAddrPortStr != NULL) { + *bindAddrPortStr = '\0'; // as the end of target address (and port) + bindAddrPortStr += 6; // strlen(",bind=") + + int result = uart_parse_address_port(bindAddrPortStr, &bindAddrStr, &bindPortStr, &isBindingIPv6); + if (result != PM3_SUCCESS) { + if (result == PM3_ESOFT) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched in bind option"); + } else { + PrintAndLogEx(ERR, "error: failed to parse address and port in bind option"); + } + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + + // for bind option, it's possible to only specify address or port + if (strlen(bindAddrStr) == 0) { + bindAddrStr = NULL; + } + if (bindPortStr != NULL && strlen(bindPortStr) == 0) { + bindPortStr = NULL; + } } + const char *addrStr = NULL; + const char *portStr = NULL; + bool isIPv6 = false; + + int result = uart_parse_address_port(addrPortStr, &addrStr, &portStr, &isIPv6); + if (result != PM3_SUCCESS) { + if (result == PM3_ESOFT) { + PrintAndLogEx(ERR, "error: wrong address: [] unmatched"); + } else { + PrintAndLogEx(ERR, "error: failed to parse address and port"); + } + free(addrPortStr); + free(sp); + return INVALID_SERIAL_PORT; + } + + g_conn.send_via_ip = isIPv6 ? (isTCP ? PM3_TCPv6 : PM3_UDPv6) : (isTCP ? PM3_TCPv4 : PM3_UDPv4); + portStr = (portStr == NULL) ? "18888" : portStr; + WSADATA wsaData; struct addrinfo info; int iResult; @@ -129,19 +184,27 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { PrintAndLogEx(ERR, "error: WSAStartup failed with error: %d", iResult); + free(addrPortStr); free(sp); return INVALID_SERIAL_PORT; } memset(&info, 0, sizeof(info)); - info.ai_socktype = SOCK_STREAM; - info.ai_protocol = IPPROTO_TCP; + info.ai_family = AF_UNSPEC; + info.ai_socktype = isTCP ? SOCK_STREAM : SOCK_DGRAM; + info.ai_protocol = isTCP ? IPPROTO_TCP : IPPROTO_UDP; - int s = getaddrinfo(addrstr, portstr, &info, &addr); + if ((strstr(addrStr, "localhost") != NULL) || + (strstr(addrStr, "127.0.0.1") != NULL) || + (strstr(addrStr, "::1") != NULL)) { + g_conn.send_via_local_ip = true; + } + + int s = getaddrinfo(addrStr, portStr, &info, &addr); if (s != 0) { - PrintAndLogEx(ERR, "error: getaddrinfo: %s", gai_strerror(s)); + PrintAndLogEx(ERR, "error: getaddrinfo: %d: %s", s, gai_strerror(s)); freeaddrinfo(addr); - free(addrstr); + free(addrPortStr); free(sp); WSACleanup(); return INVALID_SERIAL_PORT; @@ -151,21 +214,36 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { for (rp = addr; rp != NULL; rp = rp->ai_next) { hSocket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); - if (hSocket == INVALID_SOCKET) + if (hSocket == INVALID_SOCKET) { continue; + } - if (connect(hSocket, rp->ai_addr, (int)rp->ai_addrlen) != INVALID_SOCKET) + if (!uart_bind(&hSocket, bindAddrStr, bindPortStr, isBindingIPv6)) { + PrintAndLogEx(ERR, "error: Could not bind. error: %u", WSAGetLastError()); + closesocket(hSocket); + hSocket = INVALID_SOCKET; + freeaddrinfo(addr); + free(addrPortStr); + free(sp); + WSACleanup(); + return INVALID_SERIAL_PORT; + } + + if (connect(hSocket, rp->ai_addr, (int)rp->ai_addrlen) != INVALID_SOCKET) { break; + } closesocket(hSocket); hSocket = INVALID_SOCKET; } freeaddrinfo(addr); - free(addrstr); + free(addrPortStr); if (rp == NULL) { /* No address succeeded */ - PrintAndLogEx(ERR, "error: Could not connect"); + if (slient == false) { + PrintAndLogEx(ERR, "error: Could not connect"); + } WSACleanup(); free(sp); return INVALID_SERIAL_PORT; @@ -173,14 +251,19 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { sp->hSocket = hSocket; - int one = 1; - int res = setsockopt(sp->hSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); - if (res != 0) { - closesocket(hSocket); - WSACleanup(); - free(sp); - return INVALID_SERIAL_PORT; + if (isTCP) { + int one = 1; + int res = setsockopt(sp->hSocket, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); + if (res != 0) { + closesocket(hSocket); + WSACleanup(); + free(sp); + return INVALID_SERIAL_PORT; + } + } else if (isUDP) { + sp->udpBuffer = RingBuf_create(MAX(sizeof(PacketResponseNGRaw), sizeof(PacketResponseOLD)) * 30); } + return sp; } @@ -226,6 +309,7 @@ serial_port uart_open(const char *pcPortName, uint32_t speed) { } } g_conn.uart_speed = uart_get_speed(sp); + g_conn.send_via_ip = PM3_NONE; return sp; } @@ -236,13 +320,14 @@ void uart_close(const serial_port sp) { closesocket(spw->hSocket); WSACleanup(); } + RingBuf_destroy(spw->udpBuffer); if (spw->hPort != INVALID_HANDLE_VALUE) CloseHandle(spw->hPort); free(sp); } bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) { - serial_port_windows_t *spw; + serial_port_windows_t *spw = (serial_port_windows_t *)sp; // Set port speed (Input and Output) switch (uiPortSpeed) { @@ -260,7 +345,6 @@ bool uart_set_speed(serial_port sp, const uint32_t uiPortSpeed) { return false; }; - spw = (serial_port_windows_t *)sp; spw->dcb.BaudRate = uiPortSpeed; bool result = SetCommState(spw->hPort, &spw->dcb); PurgeComm(spw->hPort, PURGE_RXABORT | PURGE_RXCLEAR); @@ -279,11 +363,12 @@ uint32_t uart_get_speed(const serial_port sp) { } int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uint32_t *pszRxLen) { - serial_port_windows_t *spw = (serial_port_windows_t *)sp; - if (spw->hSocket == INVALID_SOCKET) { // serial port + const serial_port_windows_t *spw = (serial_port_windows_t *)sp; + if (spw->hSocket == INVALID_SOCKET) { + // serial port uart_reconfigure_timeouts_polling(sp); - int res = ReadFile(((serial_port_windows_t *)sp)->hPort, pbtRx, pszMaxRxLen, (LPDWORD)pszRxLen, NULL); + int res = ReadFile(spw->hPort, pbtRx, pszMaxRxLen, (LPDWORD)pszRxLen, NULL); if (res) return PM3_SUCCESS; @@ -294,7 +379,8 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin } return PM3_ENOTTY; - } else { // TCP + } else { + // TCP or UDP uint32_t byteCount; // FIONREAD returns size on 32b fd_set rfds; struct timeval tv; @@ -306,12 +392,31 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin // Reset the output count *pszRxLen = 0; do { + int res; + if (spw->udpBuffer != NULL) { + // for UDP connection, try to use the data from the buffer + + byteCount = RingBuf_getAvailableSize(spw->udpBuffer); + // Cap the number of bytes, so we don't overrun the buffer + if (pszMaxRxLen - (*pszRxLen) < byteCount) { + // PrintAndLogEx(ERR, "UART:: RX prevent overrun (have %u, need %u)", pszMaxRxLen - (*pszRxLen), byteCount); + byteCount = pszMaxRxLen - (*pszRxLen); + } + res = RingBuf_dequeueBatch(spw->udpBuffer, pbtRx + (*pszRxLen), byteCount); + *pszRxLen += res; + + if (*pszRxLen == pszMaxRxLen) { + // We have all the data we wanted. + return PM3_SUCCESS; + } + } + // Reset file descriptor FD_ZERO(&rfds); FD_SET(spw->hSocket, &rfds); tv = timeout; // the first argument nfds is ignored in Windows - int res = select(0, &rfds, NULL, NULL, &tv); + res = select(0, &rfds, NULL, NULL, &tv); // Read error if (res == SOCKET_ERROR) { @@ -331,8 +436,43 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin // Retrieve the count of the incoming bytes res = ioctlsocket(spw->hSocket, FIONREAD, (u_long *)&byteCount); - // PrintAndLogEx(ERR, "UART:: RX ioctl res %d byteCount %u", res, byteCount); - if (res == SOCKET_ERROR) return PM3_ENOTTY; + // PrintAndLogEx(ERR, "UART:: RX ioctl res %d byteCount %u", res, byteCount); + if (res == SOCKET_ERROR) { + // error occurred (maybe disconnected) + // This happens when USB-CDC connection is lost + return PM3_ENOTTY; + } else if (byteCount == 0) { + // select() > 0 && byteCount > 0 ===> data available + // select() > 0 && byteCount always equals to 0 ===> maybe disconnected + // This happens when TCP connection is lost + rx_empty_counter++; + if (rx_empty_counter > 3) { + return PM3_ENOTTY; + } + } else { + rx_empty_counter = 0; + } + + // For UDP connection, put the incoming data into the buffer and handle them in the next round + if (spw->udpBuffer != NULL) { + if (RingBuf_getContinousAvailableSize(spw->udpBuffer) >= byteCount) { + // write to the buffer directly + res = recv(spw->hSocket, (char *)RingBuf_getRearPtr(spw->udpBuffer), RingBuf_getAvailableSize(spw->udpBuffer), 0); + if (res >= 0) { + RingBuf_postEnqueueBatch(spw->udpBuffer, res); + } + } else { + // use transit buffer + uint8_t transitBuf[MAX(sizeof(PacketResponseNGRaw), sizeof(PacketResponseOLD)) * 30]; + res = recv(spw->hSocket, (char *)transitBuf, RingBuf_getAvailableSize(spw->udpBuffer), 0); + RingBuf_enqueueBatch(spw->udpBuffer, transitBuf, res); + } + // Stop if the OS has some troubles reading the data + if (res < 0) { + return PM3_EIO; + } + continue; + } // Cap the number of bytes, so we don't overrun the buffer if (pszMaxRxLen - (*pszRxLen) < byteCount) { @@ -361,10 +501,10 @@ int uart_receive(const serial_port sp, uint8_t *pbtRx, uint32_t pszMaxRxLen, uin } int uart_send(const serial_port sp, const uint8_t *p_tx, const uint32_t len) { - serial_port_windows_t *spw = (serial_port_windows_t *)sp; + const serial_port_windows_t *spw = (serial_port_windows_t *)sp; if (spw->hSocket == INVALID_SOCKET) { // serial port DWORD txlen = 0; - int res = WriteFile(((serial_port_windows_t *)sp)->hPort, p_tx, len, &txlen, NULL); + int res = WriteFile(spw->hPort, p_tx, len, &txlen, NULL); if (res) return PM3_SUCCESS; diff --git a/client/src/ui.c b/client/src/ui.c index 2281497c1..fc3f7908a 100644 --- a/client/src/ui.c +++ b/client/src/ui.c @@ -52,6 +52,7 @@ char g_CursorScaleFactorUnit[11] = {0}; double g_PlotGridX = 0, g_PlotGridY = 0, g_PlotGridXdefault = 64, g_PlotGridYdefault = 64; uint32_t g_CursorCPos = 0, g_CursorDPos = 0, g_GraphStop = 0; uint32_t g_GraphStart = 0; // Starting point/offset for the left side of the graph +uint32_t g_GraphStart_old = 0; double g_GraphPixelsPerPoint = 1.f; // How many visual pixels are between each sample point (x axis) static bool flushAfterWrite = false; double g_GridOffset = 0; @@ -316,8 +317,7 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) { char buffer[MAX_PRINT_BUFFER] = {0}; char buffer2[MAX_PRINT_BUFFER] = {0}; char buffer3[MAX_PRINT_BUFFER] = {0}; - // lock this section to avoid interlacing prints from different threads - pthread_mutex_lock(&g_print_lock); + bool linefeed = true; if (logging && g_session.incognito) { @@ -352,6 +352,8 @@ static void fPrintAndLog(FILE *stream, const char *fmt, ...) { } } + // lock this section to avoid interlacing prints from different threads + pthread_mutex_lock(&g_print_lock); // If there is an incoming message from the hardware (eg: lf hid read) in // the background (while the prompt is displayed and accepting user input), diff --git a/client/src/ui.h b/client/src/ui.h index 4042550b3..4cfd75660 100644 --- a/client/src/ui.h +++ b/client/src/ui.h @@ -62,6 +62,7 @@ typedef struct { uint16_t client_exe_delay; char *history_path; pm3_device_t *current_device; + uint32_t timeout; } session_arg_t; extern session_arg_t g_session; diff --git a/client/src/util.c b/client/src/util.c index 51408a524..0e2e27151 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -40,6 +40,8 @@ uint8_t g_debugMode = 0; uint8_t g_printAndLog = PRINTANDLOG_PRINT | PRINTANDLOG_LOG; // global client tell if a pending prompt is present bool g_pendingPrompt = false; +// global CPU core count override +int g_numCPUs = 0; #ifdef _WIN32 #include @@ -90,6 +92,30 @@ int kbd_enter_pressed(void) { } #endif +static char inv_b2s(char v, bool uppercase) { + + if (isxdigit(v) == 0) { + return '.'; + } + + uint8_t lut[] = { + 'f', 'e', 'd', 'c', + 'b', 'a', '9', '8', + '7', '6', '5', '4', + '3', '2', '1', '0' + }; + + uint8_t tmp = (tolower(v) - 'a' + 10); + if (isdigit(v)) { + tmp = (v - 0x30); + } + + if (uppercase) + return toupper(lut[tmp]); + else + return lut[tmp]; +} + static char b2s(uint8_t v, bool uppercase) { // clear higher bits v &= 0xF; @@ -757,25 +783,6 @@ float param_getfloat(const char *line, int paramnum, float deflt) { return deflt; } -int param_gethex(const char *line, int paramnum, uint8_t *data, int hexcnt) { - int bg, en, i; - uint32_t temp; - - if (hexcnt & 1) return 1; - - if (param_getptr(line, &bg, &en, paramnum)) return 1; - - if (en - bg + 1 != hexcnt) return 1; - - for (i = 0; i < hexcnt; i += 2) { - if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) return 1; - - sscanf((char[]) {line[bg + i], line[bg + i + 1], 0}, "%X", &temp); - data[i / 2] = temp & 0xff; - } - - return 0; -} int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) { int bg, en, i; uint32_t temp; @@ -783,8 +790,11 @@ int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt) if (param_getptr(line, &bg, &en, paramnum)) return 1; *hexcnt = en - bg + 1; - if (*hexcnt % 2) //error if not complete hex bytes + + // error if not complete hex bytes + if (*hexcnt & 1) { return 1; + } for (i = 0; i < *hexcnt; i += 2) { if (!(isxdigit(line[bg + i]) && isxdigit(line[bg + i + 1]))) return 1; @@ -874,7 +884,7 @@ int param_getbin_to_eol(const char *line, int paramnum, uint8_t *data, int maxda if (strlen(buf) > 0) { uint32_t temp = 0; - sscanf(buf, "%d", &temp); + sscanf(buf, "%u", &temp); data[*datalen] = (uint8_t)(temp & 0xff); *buf = 0; (*datalen)++; @@ -942,23 +952,40 @@ int hextobinarray_n(char *target, char *source, int sourcelen) { return count; } -// convert hex to human readable binary string +// convert hexstring to human readable binary string int hextobinstring(char *target, char *source) { return hextobinstring_n(target, source, strlen(source)); } - +// convert hexstring to human readable binary string int hextobinstring_n(char *target, char *source, int sourcelen) { int length = hextobinarray_n(target, source, sourcelen); if (length == 0) { return 0; } - binarraytobinstring(target, target, length); + binarray_2_binstr(target, target, length); return length; } +// convert bytes to binary string +void bytes_2_binstr(char *target, const uint8_t *source, size_t sourcelen) { + //uint8_t *p = *source; + for (int i = 0 ; i < sourcelen; ++i) { + uint8_t b = *(source++); + *(target++) = ((b >> 7) & 0x1) + '0'; + *(target++) = ((b >> 6) & 0x1) + '0'; + *(target++) = ((b >> 5) & 0x1) + '0'; + *(target++) = ((b >> 4) & 0x1) + '0'; + *(target++) = ((b >> 3) & 0x1) + '0'; + *(target++) = ((b >> 2) & 0x1) + '0'; + *(target++) = ((b >> 1) & 0x1) + '0'; + *(target++) = (b & 0x1) + '0'; + } + *target = '\0'; +} + // convert binary array of 0x00/0x01 values to hex // return number of bits converted -int binarraytohex(char *target, const size_t targetlen, const char *source, size_t srclen) { +int binarray_2_hex(char *target, const size_t targetlen, const char *source, size_t srclen) { uint8_t i = 0, x = 0; uint32_t t = 0; // written target chars uint32_t r = 0; // consumed bits @@ -1004,13 +1031,14 @@ int binarraytohex(char *target, const size_t targetlen, const char *source, size } // convert binary array to human readable binary -void binarraytobinstring(char *target, char *source, int length) { - for (int i = 0 ; i < length; ++i) +void binarray_2_binstr(char *target, char *source, int length) { + for (int i = 0 ; i < length; ++i) { *(target++) = *(source++) + '0'; + } *target = '\0'; } -int binstring2binarray(uint8_t *target, char *source, int length) { +int binstr_2_binarray(uint8_t *target, char *source, int length) { int count = 0; char *start = source; while (length--) { @@ -1028,6 +1056,36 @@ int binstring2binarray(uint8_t *target, char *source, int length) { return count; } +void binstr_2_bytes(uint8_t *target, size_t *targetlen, const char *src) { + size_t binlen = strlen(src); + if (binlen == 0) { + *targetlen = 0; + return; + } + + // Calculate padding needed + size_t padding = (8 - (binlen % 8)) % 8; + + // Determine the size of the hexadecimal array + *targetlen = (binlen + padding) / 8; + + uint8_t b = 0; + size_t bit_cnt = padding; + size_t idx = 0; + + // Process binary string + for (size_t i = 0; i < binlen; ++i) { + b = (b << 1) | (src[i] == '1'); + ++bit_cnt; + + if (bit_cnt == 8) { + target[idx++] = b; + b = 0; + bit_cnt = 0; + } + } +} + // return parity bit required to match type uint8_t GetParity(const uint8_t *bits, uint8_t type, int length) { int x; @@ -1077,8 +1135,16 @@ uint64_t HornerScheme(uint64_t num, uint64_t divider, uint64_t factor) { return result; } -// determine number of logical CPU cores (use for multithreaded functions) int num_CPUs(void) { + if (g_numCPUs > 0) { + return g_numCPUs; + } + + return detect_num_CPUs(); +} + +// determine number of logical CPU cores (use for multithreaded functions) +int detect_num_CPUs(void) { #if defined(_WIN32) #include SYSTEM_INFO sysinfo; @@ -1129,13 +1195,13 @@ void clean_ascii(unsigned char *buf, size_t len) { } // replace \r \n to \0 -void strcleanrn(char *buf, size_t len) { - strcreplace(buf, len, '\n', '\0'); - strcreplace(buf, len, '\r', '\0'); +void str_cleanrn(char *buf, size_t len) { + str_creplace(buf, len, '\n', '\0'); + str_creplace(buf, len, '\r', '\0'); } // replace char in buffer -void strcreplace(char *buf, size_t len, char from, char to) { +void str_creplace(char *buf, size_t len, char from, char to) { for (size_t i = 0; i < len; i++) { if (buf[i] == from) buf[i] = to; @@ -1156,6 +1222,44 @@ char *str_ndup(const char *src, size_t len) { return dest; } +size_t str_nlen(const char *src, size_t maxlen) { + size_t len = 0; + if (src) { + for (char c = *src; (len < maxlen && c != '\0'); c = *++src) { + len++; + } + } + return len; +} + +void str_reverse(char *buf, size_t len) { + for (size_t i = 0; i < (len >> 1); i++) { + char tmp = buf[i]; + buf[i] = buf[len - i - 1]; + buf[len - i - 1] = tmp; + } +} + +void str_inverse_hex(char *buf, size_t len) { + for (size_t i = 0; i < len; i++) { + buf[i] = inv_b2s(buf[i], true); + } +} + +void str_inverse_bin(char *buf, size_t len) { + for (size_t i = 0; i < len; i++) { + + char c = buf[i]; + if (c == '1') + buf[i] = '0'; + else if (c == '0') + buf[i] = '1'; + else + buf[i] = '.'; + } +} + + /** * Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers * one nibble at a time. @@ -1256,6 +1360,8 @@ inline uint64_t leadingzeros64(uint64_t a) { } +// byte_strstr searches for the first occurrence of pattern in src +// returns the byte offset the pattern is found at, or -1 if not found int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen) { size_t max = srclen - plen + 1; @@ -1279,6 +1385,28 @@ int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_ return -1; } +// byte_strrstr is like byte_strstr except searches in reverse +// ie it returns the last occurrence of the pattern in src instead of the first +// returns the byte offset the pattern is found at, or -1 if not found +int byte_strrstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen) { + for (int i = srclen - plen; i >= 0; i--) { + // compare only first byte + if (src[i] != pattern[0]) + continue; + + // try to match rest of the pattern + for (int j = plen - 1; j >= 1; j--) { + + if (src[i + j] != pattern[j]) + break; + + if (j == 1) + return i; + } + } + return -1; +} + void sb_append_char(smartbuf *sb, unsigned char c) { if (sb->idx >= sb->size) { sb->size *= 2; diff --git a/client/src/util.h b/client/src/util.h index 4dab9ae06..1949c1d53 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -32,6 +32,7 @@ extern uint8_t g_debugMode; extern uint8_t g_printAndLog; extern bool g_pendingPrompt; +extern int g_numCPUs; #define PRINTANDLOG_PRINT 1 #define PRINTANDLOG_LOG 2 @@ -105,7 +106,6 @@ uint64_t param_get64ex(const char *line, int paramnum, int deflt, int base); float param_getfloat(const char *line, int paramnum, float deflt); uint8_t param_getdec(const char *line, int paramnum, uint8_t *destination); uint8_t param_isdec(const char *line, int paramnum); -int param_gethex(const char *line, int paramnum, uint8_t *data, int hexcnt); int param_gethex_ex(const char *line, int paramnum, uint8_t *data, int *hexcnt); int param_gethex_to_eol(const char *line, int paramnum, uint8_t *data, int maxdatalen, int *datalen); int param_getbin_to_eol(const char *line, int paramnum, uint8_t *data, int maxdatalen, int *datalen); @@ -117,9 +117,12 @@ int hextobinarray_n(char *target, char *source, int sourcelen); int hextobinstring(char *target, char *source); int hextobinstring_n(char *target, char *source, int sourcelen); -int binarraytohex(char *target, const size_t targetlen, const char *source, size_t srclen); -void binarraytobinstring(char *target, char *source, int length); -int binstring2binarray(uint8_t *target, char *source, int length); +int binarray_2_hex(char *target, const size_t targetlen, const char *source, size_t srclen); +void binarray_2_binstr(char *target, char *source, int length); +int binstr_2_binarray(uint8_t *target, char *source, int length); + +void bytes_2_binstr(char *target, const uint8_t *source, size_t sourcelen); +void binstr_2_bytes(uint8_t *target, size_t *targetlen, const char *src); uint8_t GetParity(const uint8_t *bits, uint8_t type, int length); void wiegand_add_parity(uint8_t *target, uint8_t *source, uint8_t length); @@ -130,7 +133,8 @@ void wiegand_add_parity_swapped(uint8_t *target, uint8_t *source, uint8_t length uint32_t PackBits(uint8_t start, uint8_t len, const uint8_t *bits); uint64_t HornerScheme(uint64_t num, uint64_t divider, uint64_t factor); -int num_CPUs(void); // number of logical CPUs +int num_CPUs(void); +int detect_num_CPUs(void); // number of logical CPUs void str_lower(char *s); // converts string to lower case void str_upper(char *s); // converts string to UPPER case @@ -139,10 +143,15 @@ void strn_upper(char *s, size_t n); bool str_startswith(const char *s, const char *pre); // check for prefix in string bool str_endswith(const char *s, const char *suffix); // check for suffix in string void clean_ascii(unsigned char *buf, size_t len); -void strcleanrn(char *buf, size_t len); -void strcreplace(char *buf, size_t len, char from, char to); +void str_cleanrn(char *buf, size_t len); +void str_creplace(char *buf, size_t len, char from, char to); +void str_reverse(char *buf, size_t len); +void str_inverse_hex(char *buf, size_t len); +void str_inverse_bin(char *buf, size_t len); + char *str_dup(const char *src); char *str_ndup(const char *src, size_t len); +size_t str_nlen(const char *src, size_t maxlen); int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); int binarray_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const uint8_t *arr, int arrlen); @@ -153,6 +162,7 @@ uint32_t leadingzeros32(uint32_t a); uint64_t leadingzeros64(uint64_t a); int byte_strstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen); +int byte_strrstr(const uint8_t *src, size_t srclen, const uint8_t *pattern, size_t plen); struct smartbuf { char *ptr; diff --git a/client/src/wiegand_formats.c b/client/src/wiegand_formats.c index e63040a19..fc14f2bb6 100644 --- a/client/src/wiegand_formats.c +++ b/client/src/wiegand_formats.c @@ -730,7 +730,9 @@ static bool Pack_C15001(wiegand_card_t *card, wiegand_message_t *packed, bool pr static bool Unpack_C15001(wiegand_message_t *packed, wiegand_card_t *card) { memset(card, 0, sizeof(wiegand_card_t)); - if (packed->Length != 36) return false; // Wrong length? Stop here. + + if (packed->Length != 36) + return false; // Wrong length? Stop here. card->OEM = get_linear_field(packed, 1, 10); card->FacilityCode = get_linear_field(packed, 11, 8); @@ -976,8 +978,10 @@ static bool Pack_C1k48s(wiegand_card_t *card, wiegand_message_t *packed, bool pr packed->Mid |= (evenparity32((packed->Mid & 0x00001B6D) ^ (packed->Bot & 0xB6DB6DB6))) << 14; packed->Bot |= (oddparity32((packed->Mid & 0x000036DB) ^ (packed->Bot & 0x6DB6DB6C))); packed->Mid |= (oddparity32((packed->Mid & 0x00007FFF) ^ (packed->Bot & 0xFFFFFFFF))) << 15; + if (preamble) return add_HID_header(packed); + return true; } @@ -1284,6 +1288,58 @@ static bool Unpack_bc40(wiegand_message_t *packed, wiegand_card_t *card) { return true; } + +static bool step_parity_check(wiegand_message_t *packed, int start, int length, bool even_parity) { + bool parity = even_parity; + for (int i = start; i < start + length; i += 2) { + // Extract 2 bits + bool bit1 = get_bit_by_position(packed, i); + bool bit2 = get_bit_by_position(packed, i + 1); + + // Calculate parity for these 2 bits + parity ^= (bit1 ^ bit2); + } + return parity; +} + +static bool Pack_Avig56(wiegand_card_t *card, wiegand_message_t *packed, bool preamble) { + memset(packed, 0, sizeof(wiegand_message_t)); + packed->Length = 56; + + if (card->FacilityCode > 0xFFFFF) return false; // Can't encode FC. + if (card->CardNumber > 0x3FFFFFFFF) return false; // Can't encode CN. + + set_linear_field(packed, card->FacilityCode, 1, 20); + set_linear_field(packed, card->CardNumber, 21, 34); + + bool even_parity_valid = step_parity_check(packed, 0, 28, true); + set_bit_by_position(packed, !even_parity_valid, 0); + + bool odd_parity_valid = step_parity_check(packed, 28, 28, false); + set_bit_by_position(packed, !odd_parity_valid, 55); + + if (preamble) + return add_HID_header(packed); + + return true; +} + +static bool Unpack_Avig56(wiegand_message_t *packed, wiegand_card_t *card) { + memset(card, 0, sizeof(wiegand_card_t)); + + if (packed->Length != 56) return false; + + card->FacilityCode = get_linear_field(packed, 1, 20); + card->CardNumber = get_linear_field(packed, 21, 34); + + // Check step parity for every 2 bits + bool even_parity_valid = step_parity_check(packed, 0, 28, true); + bool odd_parity_valid = step_parity_check(packed, 28, 28, false); + + card->ParityValid = even_parity_valid && odd_parity_valid; + return true; +} + // --------------------------------------------------------------------------------------------------- void print_desc_wiegand(cardformat_t *fmt, wiegand_message_t *packed) { @@ -1410,6 +1466,7 @@ static const cardformat_t FormatTable[] = { {"Casi40", Pack_CasiRusco40, Unpack_CasiRusco40, "Casi-Rusco 40-bit", {1, 0, 0, 0, 0}}, // from cardinfo.barkweb.com.au {"C1k48s", Pack_C1k48s, Unpack_C1k48s, "HID Corporate 1000 48-bit std", {1, 1, 0, 0, 1}}, // imported from old pack/unpack {"BC40", Pack_bc40, Unpack_bc40, "Bundy TimeClock 40-bit", {1, 1, 0, 1, 1}}, // from + {"Avig56", Pack_Avig56, Unpack_Avig56, "Avigilon 56-bit", {1, 1, 0, 0, 1}}, {NULL, NULL, NULL, NULL, {0, 0, 0, 0, 0}} // Must null terminate array }; diff --git a/client/src/wiegand_formatutils.c b/client/src/wiegand_formatutils.c index e55f999bd..75aa6ae2f 100644 --- a/client/src/wiegand_formatutils.c +++ b/client/src/wiegand_formatutils.c @@ -148,7 +148,7 @@ static uint8_t get_length_from_header(wiegand_message_t *data) { len = 32; } else { - printf("hid preamble detected\n"); + PrintAndLogEx(DEBUG, "hid preamble detected"); len = 32; if ((data->Mid ^ 0x20) == 0) { hfmt = data->Bot; len = 0; } diff --git a/client/update_amiibo_tools_lua.py b/client/update_amiibo_tools_lua.py index 873a6e18d..8a31b9d60 100644 --- a/client/update_amiibo_tools_lua.py +++ b/client/update_amiibo_tools_lua.py @@ -8,15 +8,15 @@ Author: Cory Solovewicz Description: This is a python script to automate what the updating of the amiibo_tools.lua -file which holds a lua table of all known amiibos. Previously updating the +file which holds a lua table of all known amiibos. Previously updating the amiibo_tools.lua was a very manual process. -This script automates the following original command: +This script automates the following original command: curl https://raw.githubusercontent.com/N3evin/AmiiboAPI/master/database/amiibo.json | jq 'del(.amiibos[].release)' | jq 'del(.characters)' | pbcopy --> transform to table -And outputs the formatted file as amiibo_tools.lua -If everything goes well, this should be an updated copy of amiibo_tools.lua -which can then be placed in the /lualibs/ directory. -The temporary amiibo.json file is then deleted +And outputs the formatted file as amiibo_tools.lua +If everything goes well, this should be an updated copy of amiibo_tools.lua +which can then be placed in the /lualibs/ directory. +The temporary amiibo.json file is then deleted Dependencies: python3 -m pip install jq @@ -25,7 +25,7 @@ How to run: python update_amiibo_tools_lua.py The script will create the file amiibo_tools.lua -After running, manually backup the original /lualibs/amiibo_tools.lua and move the +After running, manually backup the original /lualibs/amiibo_tools.lua and move the updated amiibo_tools.lua to the /lualibs/ directory. ----------------------------------------------------------------------------- """ diff --git a/common/cardhelper.c b/common/cardhelper.c index c8c8b6bd8..fc4fe2503 100644 --- a/common/cardhelper.c +++ b/common/cardhelper.c @@ -22,6 +22,7 @@ #include "cmdsmartcard.h" #include "ui.h" #include "util.h" +#include "commonutil.h" #define CARD_INS_DECRYPT 0x01 #define CARD_INS_ENCRYPT 0x02 @@ -35,26 +36,77 @@ // look for CardHelper bool IsCardHelperPresent(bool verbose) { - if (IfPm3Smartcard()) { - int resp_len = 0; - uint8_t version[] = {0x96, 0x69, 0x00, 0x00, 0x00}; - uint8_t resp[30] = {0}; - ExchangeAPDUSC(verbose, version, sizeof(version), true, true, resp, sizeof(resp), &resp_len); + if (IfPm3Smartcard() == false) { + return false; + } - if (resp_len < 8) { - return false; - } + int resp_len = 0; + uint8_t version[] = {0x96, 0x69, 0x00, 0x00, 0x00}; + uint8_t resp[30] = {0}; + ExchangeAPDUSC(verbose, version, sizeof(version), true, true, resp, sizeof(resp), &resp_len); - if (strstr("CryptoHelper", (char *)resp) == 0) { - if (verbose) { - PrintAndLogEx(INFO, "Found smart card helper"); - } - return true; + if (resp_len < 8) { + return false; + } + + if (strstr("CryptoHelper", (char *)resp) == 0) { + if (verbose) { + PrintAndLogEx(INFO, "Found smart card helper"); } + return true; } return false; } +bool IsHIDSamPresent(bool verbose) { + + if (IfPm3Smartcard() == false) { + PrintAndLogEx(WARNING, "Proxmark3 does not have SMARTCARD support enabled, exiting"); + return false; + } + + // detect SAM + smart_card_atr_t card; + smart_select(verbose, &card); + if (card.atr_len == 0) { + PrintAndLogEx(ERR, "Can't get ATR from a smart card"); + return false; + } + + // SAM identification + smart_card_atr_t supported[] = { + {15, {0x3B, 0x95, 0x96, 0x80, 0xB1, 0xFE, 0x55, 0x1F, 0xC7, 0x47, 0x72, 0x61, 0x63, 0x65, 0x13}}, + {11, {0x3b, 0x90, 0x96, 0x91, 0x81, 0xb1, 0xfe, 0x55, 0x1f, 0xc7, 0xd4}}, + }; + bool found = false; + for (int i = 0; i < ARRAYLEN(supported); i++) { + if ((card.atr_len == supported[i].atr_len) && + (memcmp(card.atr, supported[i].atr, supported[i].atr_len) == 0)) { + found = true; + break; + } + } + if (found == false) { + if (verbose) { + PrintAndLogEx(SUCCESS, "Not detecting a SAM"); + } + return false; + } + + // Suspect some SAMs has version name in the historical bytes + uint8_t T0 = card.atr[1]; + uint8_t K = T0 & 0x0F; // Number of historical bytes + if (K > 0 && (K < (card.atr_len - 3)) && verbose) { + // Last byte of ATR is CRC and before that we have K bytes of + // "historical bytes". + // By construction K can't go above 15 + char sam_name[16] = {0}; + memcpy(sam_name, &card.atr[card.atr_len - 1 - K], K); + PrintAndLogEx(SUCCESS, "SAM (%s) detected", sam_name); + } + return true; +} + static bool executeCrypto(uint8_t ins, uint8_t *src, uint8_t *dest) { uint8_t cmd[] = {0x96, ins, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; memcpy(cmd + 5, src, 8); diff --git a/common/cardhelper.h b/common/cardhelper.h index 9574397a1..b88ff1e16 100644 --- a/common/cardhelper.h +++ b/common/cardhelper.h @@ -22,6 +22,7 @@ #include #include "common.h" +bool IsHIDSamPresent(bool verbose); bool IsCardHelperPresent(bool verbose); bool Encrypt(uint8_t *src, uint8_t *dest); bool Decrypt(uint8_t *src, uint8_t *dest); diff --git a/common/commonutil.c b/common/commonutil.c index 4af16b3c0..c9ae5981c 100644 --- a/common/commonutil.c +++ b/common/commonutil.c @@ -23,8 +23,8 @@ * verifies the magic properties, then stores a formatted string, prefixed by * prefix in dst. */ -void FormatVersionInformation(char *dst, int len, const char *prefix, void *version_info) { - struct version_information_t *v = (struct version_information_t *)version_info; +void FormatVersionInformation(char *dst, int len, const char *prefix, const void *version_info) { + const struct version_information_t *v = (const struct version_information_t *)version_info; dst[0] = 0; strncat(dst, prefix, len - 1); if (v->magic != VERSION_INFORMATION_MAGIC) { @@ -53,8 +53,8 @@ void FormatVersionInformation(char *dst, int len, const char *prefix, void *vers strncat(dst, v->armsrc, len - strlen(dst) - 1); } -void format_version_information_short(char *dst, int len, void *version_info) { - struct version_information_t *v = (struct version_information_t *)version_info; +void format_version_information_short(char *dst, int len, const void *version_info) { + const struct version_information_t *v = (const struct version_information_t *)version_info; dst[0] = 0; if (v->magic != VERSION_INFORMATION_MAGIC) { strncat(dst, "Missing/Invalid version information", len - strlen(dst) - 1); @@ -151,7 +151,7 @@ void num_to_bytes(uint64_t n, size_t len, uint8_t *dest) { } } -uint64_t bytes_to_num(uint8_t *src, size_t len) { +uint64_t bytes_to_num(const uint8_t *src, size_t len) { uint64_t num = 0; while (len--) { num = (num << 8) | (*src); @@ -161,63 +161,255 @@ uint64_t bytes_to_num(uint8_t *src, size_t len) { } uint16_t MemLeToUint2byte(const uint8_t *data) { - return (data[1] << 8) + data[0]; + return (uint16_t)( + (((uint16_t)(data[1])) << (8 * 1)) + + (((uint16_t)(data[0])) << (8 * 0)) + ); } uint32_t MemLeToUint3byte(const uint8_t *data) { - return (data[2] << 16) + (data[1] << 8) + data[0]; + return (uint32_t)( + (((uint32_t)(data[2])) << (8 * 2)) + + (((uint32_t)(data[1])) << (8 * 1)) + + (((uint32_t)(data[0])) << (8 * 0)) + ); } uint32_t MemLeToUint4byte(const uint8_t *data) { - return (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0]; + return (uint32_t)( + (((uint32_t)(data[3])) << (8 * 3)) + + (((uint32_t)(data[2])) << (8 * 2)) + + (((uint32_t)(data[1])) << (8 * 1)) + + (((uint32_t)(data[0])) << (8 * 0)) + ); +} + +uint64_t MemLeToUint5byte(const uint8_t *data) { + return (uint64_t)( + (((uint64_t)(data[4])) << (8 * 4)) + + (((uint64_t)(data[3])) << (8 * 3)) + + (((uint64_t)(data[2])) << (8 * 2)) + + (((uint64_t)(data[1])) << (8 * 1)) + + (((uint64_t)(data[0])) << (8 * 0)) + ); +} + +uint64_t MemLeToUint6byte(const uint8_t *data) { + return (uint64_t)( + (((uint64_t)(data[5])) << (8 * 5)) + + (((uint64_t)(data[4])) << (8 * 4)) + + (((uint64_t)(data[3])) << (8 * 3)) + + (((uint64_t)(data[2])) << (8 * 2)) + + (((uint64_t)(data[1])) << (8 * 1)) + + (((uint64_t)(data[0])) << (8 * 0)) + ); +} + +uint64_t MemLeToUint7byte(const uint8_t *data) { + return (uint64_t)( + (((uint64_t)(data[6])) << (8 * 6)) + + (((uint64_t)(data[5])) << (8 * 5)) + + (((uint64_t)(data[4])) << (8 * 4)) + + (((uint64_t)(data[3])) << (8 * 3)) + + (((uint64_t)(data[2])) << (8 * 2)) + + (((uint64_t)(data[1])) << (8 * 1)) + + (((uint64_t)(data[0])) << (8 * 0)) + ); +} + +uint64_t MemLeToUint8byte(const uint8_t *data) { + return (uint64_t)( + (((uint64_t)(data[7])) << (8 * 7)) + + (((uint64_t)(data[6])) << (8 * 6)) + + (((uint64_t)(data[5])) << (8 * 5)) + + (((uint64_t)(data[4])) << (8 * 4)) + + (((uint64_t)(data[3])) << (8 * 3)) + + (((uint64_t)(data[2])) << (8 * 2)) + + (((uint64_t)(data[1])) << (8 * 1)) + + (((uint64_t)(data[0])) << (8 * 0)) + ); } uint16_t MemBeToUint2byte(const uint8_t *data) { - return (data[0] << 8) + data[1]; + return (uint16_t)( + (((uint16_t)(data[0])) << (8 * 1)) + + (((uint16_t)(data[1])) << (8 * 0)) + ); } uint32_t MemBeToUint3byte(const uint8_t *data) { - return (data[0] << 16) + (data[1] << 8) + data[2]; + return (uint32_t)( + (((uint32_t)(data[0])) << (8 * 2)) + + (((uint32_t)(data[1])) << (8 * 1)) + + (((uint32_t)(data[2])) << (8 * 0)) + ); } uint32_t MemBeToUint4byte(const uint8_t *data) { - return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]; + return (uint32_t)( + (((uint32_t)(data[0])) << (8 * 3)) + + (((uint32_t)(data[1])) << (8 * 2)) + + (((uint32_t)(data[2])) << (8 * 1)) + + (((uint32_t)(data[3])) << (8 * 0)) + ); +} + +uint64_t MemBeToUint5byte(const uint8_t *data) { + return (uint64_t)( + (((uint64_t)(data[0])) << (8 * 4)) + + (((uint64_t)(data[1])) << (8 * 3)) + + (((uint64_t)(data[2])) << (8 * 2)) + + (((uint64_t)(data[3])) << (8 * 1)) + + (((uint64_t)(data[4])) << (8 * 0)) + ); +} + +uint64_t MemBeToUint6byte(const uint8_t *data) { + return (uint64_t)( + (((uint64_t)(data[0])) << (8 * 5)) + + (((uint64_t)(data[1])) << (8 * 4)) + + (((uint64_t)(data[2])) << (8 * 3)) + + (((uint64_t)(data[3])) << (8 * 2)) + + (((uint64_t)(data[4])) << (8 * 1)) + + (((uint64_t)(data[5])) << (8 * 0)) + ); +} + +uint64_t MemBeToUint7byte(const uint8_t *data) { + return (uint64_t)( + (((uint64_t)(data[0])) << (8 * 6)) + + (((uint64_t)(data[1])) << (8 * 5)) + + (((uint64_t)(data[2])) << (8 * 4)) + + (((uint64_t)(data[3])) << (8 * 3)) + + (((uint64_t)(data[4])) << (8 * 2)) + + (((uint64_t)(data[5])) << (8 * 1)) + + (((uint64_t)(data[6])) << (8 * 0)) + ); +} + +uint64_t MemBeToUint8byte(const uint8_t *data) { + return (uint64_t)( + (((uint64_t)(data[0])) << (8 * 7)) + + (((uint64_t)(data[1])) << (8 * 6)) + + (((uint64_t)(data[2])) << (8 * 5)) + + (((uint64_t)(data[3])) << (8 * 4)) + + (((uint64_t)(data[4])) << (8 * 3)) + + (((uint64_t)(data[5])) << (8 * 2)) + + (((uint64_t)(data[6])) << (8 * 1)) + + (((uint64_t)(data[7])) << (8 * 0)) + ); } void Uint2byteToMemLe(uint8_t *data, uint16_t value) { - data[1] = (value >> 8) & 0xff; - data[0] = value & 0xff; + data[0] = (uint8_t)((value >> (8 * 0)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 1)) & 0xffu); } void Uint3byteToMemLe(uint8_t *data, uint32_t value) { - data[2] = (value >> 16) & 0xff; - data[1] = (value >> 8) & 0xff; - data[0] = value & 0xff; + data[0] = (uint8_t)((value >> (8 * 0)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 2)) & 0xffu); } void Uint4byteToMemLe(uint8_t *data, uint32_t value) { - data[3] = (value >> 24) & 0xff; - data[2] = (value >> 16) & 0xff; - data[1] = (value >> 8) & 0xff; - data[0] = value & 0xff; + data[0] = (uint8_t)((value >> (8 * 0)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 2)) & 0xffu); + data[3] = (uint8_t)((value >> (8 * 3)) & 0xffu); +} + +void Uint5byteToMemLe(uint8_t *data, uint64_t value) { + data[0] = (uint8_t)((value >> (8 * 0)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 2)) & 0xffu); + data[3] = (uint8_t)((value >> (8 * 3)) & 0xffu); + data[4] = (uint8_t)((value >> (8 * 4)) & 0xffu); +} + +void Uint6byteToMemLe(uint8_t *data, uint64_t value) { + data[0] = (uint8_t)((value >> (8 * 0)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 2)) & 0xffu); + data[3] = (uint8_t)((value >> (8 * 3)) & 0xffu); + data[4] = (uint8_t)((value >> (8 * 4)) & 0xffu); + data[5] = (uint8_t)((value >> (8 * 5)) & 0xffu); +} + +void Uint7byteToMemLe(uint8_t *data, uint64_t value) { + data[0] = (uint8_t)((value >> (8 * 0)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 2)) & 0xffu); + data[3] = (uint8_t)((value >> (8 * 3)) & 0xffu); + data[4] = (uint8_t)((value >> (8 * 4)) & 0xffu); + data[5] = (uint8_t)((value >> (8 * 5)) & 0xffu); + data[6] = (uint8_t)((value >> (8 * 6)) & 0xffu); +} + +void Uint8byteToMemLe(uint8_t *data, uint64_t value) { + data[0] = (uint8_t)((value >> (8 * 0)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 2)) & 0xffu); + data[3] = (uint8_t)((value >> (8 * 3)) & 0xffu); + data[4] = (uint8_t)((value >> (8 * 4)) & 0xffu); + data[5] = (uint8_t)((value >> (8 * 5)) & 0xffu); + data[6] = (uint8_t)((value >> (8 * 6)) & 0xffu); + data[7] = (uint8_t)((value >> (8 * 7)) & 0xffu); } void Uint2byteToMemBe(uint8_t *data, uint16_t value) { - data[0] = (value >> 8) & 0xff; - data[1] = value & 0xff; + data[0] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 0)) & 0xffu); } void Uint3byteToMemBe(uint8_t *data, uint32_t value) { - data[0] = (value >> 16) & 0xff; - data[1] = (value >> 8) & 0xff; - data[2] = value & 0xff; + data[0] = (uint8_t)((value >> (8 * 2)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 0)) & 0xffu); } void Uint4byteToMemBe(uint8_t *data, uint32_t value) { - data[0] = (value >> 24) & 0xff; - data[1] = (value >> 16) & 0xff; - data[2] = (value >> 8) & 0xff; - data[3] = value & 0xff; + data[0] = (uint8_t)((value >> (8 * 3)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 2)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[3] = (uint8_t)((value >> (8 * 0)) & 0xffu); +} + +void Uint5byteToMemBe(uint8_t *data, uint64_t value) { + data[0] = (uint8_t)((value >> (8 * 4)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 3)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 2)) & 0xffu); + data[3] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[4] = (uint8_t)((value >> (8 * 0)) & 0xffu); +} + +void Uint6byteToMemBe(uint8_t *data, uint64_t value) { + data[0] = (uint8_t)((value >> (8 * 5)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 4)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 3)) & 0xffu); + data[3] = (uint8_t)((value >> (8 * 2)) & 0xffu); + data[4] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[5] = (uint8_t)((value >> (8 * 0)) & 0xffu); +} + +void Uint7byteToMemBe(uint8_t *data, uint64_t value) { + data[0] = (uint8_t)((value >> (8 * 6)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 5)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 4)) & 0xffu); + data[3] = (uint8_t)((value >> (8 * 3)) & 0xffu); + data[4] = (uint8_t)((value >> (8 * 2)) & 0xffu); + data[5] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[6] = (uint8_t)((value >> (8 * 0)) & 0xffu); +} + +void Uint8byteToMemBe(uint8_t *data, uint64_t value) { + data[0] = (uint8_t)((value >> (8 * 7)) & 0xffu); + data[1] = (uint8_t)((value >> (8 * 6)) & 0xffu); + data[2] = (uint8_t)((value >> (8 * 5)) & 0xffu); + data[3] = (uint8_t)((value >> (8 * 4)) & 0xffu); + data[4] = (uint8_t)((value >> (8 * 3)) & 0xffu); + data[5] = (uint8_t)((value >> (8 * 2)) & 0xffu); + data[6] = (uint8_t)((value >> (8 * 1)) & 0xffu); + data[7] = (uint8_t)((value >> (8 * 0)) & 0xffu); } // RotateLeft - Ultralight, Desfire @@ -293,3 +485,45 @@ void reverse_array_copy(const uint8_t *src, int src_len, uint8_t *dest) { dest[i] = src[(src_len - 1) - i]; } } + +static int hexchar_to_dec(char ch) { + if (ch >= '0' && ch <= '9') { + return ch - '0'; + } + if (ch >= 'a' && ch <= 'f') { + return ch - 'a' + 10; + } + if (ch >= 'A' && ch <= 'F') { + return ch - 'A' + 10; + } + return -1; +} + +// no spaces allowed for input hex string +bool hexstr_to_byte_array(const char *hexstr, uint8_t *d, size_t *n) { + + size_t hexstr_len = strlen(hexstr); + if (hexstr_len & 1) { + return false; + } + + *n = (hexstr_len >> 1); + + for (int i = 0; i < *n; i++) { + + char c1 = *hexstr++; + char c2 = *hexstr++; + + if (c1 == '\0' || c2 == '\0') { + return false; + } + + int b = (hexchar_to_dec(c1) << 4) | hexchar_to_dec(c2); + if (b < 0) { + // Error: invalid hex character + return false; + } + d[i] = (uint8_t) b; + } + return true; +} diff --git a/common/commonutil.h b/common/commonutil.h index 7beb95355..f057486a2 100644 --- a/common/commonutil.h +++ b/common/commonutil.h @@ -50,8 +50,8 @@ #endif extern struct version_information_t g_version_information; -void FormatVersionInformation(char *dst, int len, const char *prefix, void *version_info); -void format_version_information_short(char *dst, int len, void *version_info); +void FormatVersionInformation(char *dst, int len, const char *prefix, const void *version_info); +void format_version_information_short(char *dst, int len, const void *version_info); uint32_t reflect(uint32_t v, int b); // used in crc.c ... uint8_t reflect8(uint8_t b); // dedicated 8bit reversal @@ -59,21 +59,40 @@ uint16_t reflect16(uint16_t b); // dedicated 16bit reversal uint32_t reflect32(uint32_t b); // dedicated 32bit reversal void num_to_bytes(uint64_t n, size_t len, uint8_t *dest); -uint64_t bytes_to_num(uint8_t *src, size_t len); +uint64_t bytes_to_num(const uint8_t *src, size_t len); // LE and BE to/from memory uint16_t MemLeToUint2byte(const uint8_t *data); uint32_t MemLeToUint3byte(const uint8_t *data); uint32_t MemLeToUint4byte(const uint8_t *data); +uint64_t MemLeToUint5byte(const uint8_t *data); +uint64_t MemLeToUint6byte(const uint8_t *data); +uint64_t MemLeToUint7byte(const uint8_t *data); +uint64_t MemLeToUint8byte(const uint8_t *data); + uint16_t MemBeToUint2byte(const uint8_t *data); uint32_t MemBeToUint3byte(const uint8_t *data); uint32_t MemBeToUint4byte(const uint8_t *data); +uint64_t MemBeToUint5byte(const uint8_t *data); +uint64_t MemBeToUint6byte(const uint8_t *data); +uint64_t MemBeToUint7byte(const uint8_t *data); +uint64_t MemBeToUint8byte(const uint8_t *data); + void Uint2byteToMemLe(uint8_t *data, uint16_t value); void Uint3byteToMemLe(uint8_t *data, uint32_t value); void Uint4byteToMemLe(uint8_t *data, uint32_t value); +void Uint5byteToMemLe(uint8_t *data, uint64_t value); +void Uint6byteToMemLe(uint8_t *data, uint64_t value); +void Uint7byteToMemLe(uint8_t *data, uint64_t value); +void Uint8byteToMemLe(uint8_t *data, uint64_t value); + void Uint2byteToMemBe(uint8_t *data, uint16_t value); void Uint3byteToMemBe(uint8_t *data, uint32_t value); void Uint4byteToMemBe(uint8_t *data, uint32_t value); +void Uint5byteToMemBe(uint8_t *data, uint64_t value); +void Uint6byteToMemBe(uint8_t *data, uint64_t value); +void Uint7byteToMemBe(uint8_t *data, uint64_t value); +void Uint8byteToMemBe(uint8_t *data, uint64_t value); // rotate left byte array void rol(uint8_t *data, const size_t len); @@ -90,4 +109,5 @@ uint16_t get_sw(const uint8_t *d, uint16_t n); void reverse_array(uint8_t *d, size_t n); void reverse_array_copy(const uint8_t *src, int src_len, uint8_t *dest); +bool hexstr_to_byte_array(const char *hexstr, uint8_t *d, size_t *n); #endif diff --git a/common/crapto1/readme b/common/crapto1/readme index d57fa3e08..3a979efad 100644 --- a/common/crapto1/readme +++ b/common/crapto1/readme @@ -3,10 +3,10 @@ CRAPTO1 Provides a set of library functions which aid the verification of crypto1 weaknesses. - In short a partial implementation of: + In short a partial implementation of: Dismantling MIFARE Classic URL: http://www.sos.cs.ru.nl/applications/rfid/2008-esorics.pdf - Flavio D. Garcia, Gerhard de Koning Gans, Ruben Muijrers, + Flavio D. Garcia, Gerhard de Koning Gans, Ruben Muijrers, Peter van Rossum, Roel Verdult, Ronny Wichers Schreur, Bart Jacobs Institute for Computing and Information Sciences, Radboud University Nijmegen, The Netherlands diff --git a/common/crc16.c b/common/crc16.c index f78fab5b4..812cd3481 100644 --- a/common/crc16.c +++ b/common/crc16.c @@ -42,10 +42,14 @@ void init_table(CrcType_t crctype) { case CRC_15693: case CRC_ICLASS: case CRC_CRYPTORF: + case CRC_KERMIT: generate_table(CRC16_POLY_CCITT, true); break; case CRC_FELICA: case CRC_XMODEM: + case CRC_CCITT: + case CRC_11784: + case CRC_PHILIPS: generate_table(CRC16_POLY_CCITT, false); break; case CRC_LEGIC: @@ -54,15 +58,6 @@ void init_table(CrcType_t crctype) { case CRC_LEGIC_16: generate_table(CRC16_POLY_LEGIC_16, true); break; - case CRC_CCITT: - generate_table(CRC16_POLY_CCITT, false); - break; - case CRC_KERMIT: - generate_table(CRC16_POLY_CCITT, true); - break; - case CRC_11784: - generate_table(CRC16_POLY_CCITT, false); - break; case CRC_NONE: crc_table_init = false; current_crc_type = CRC_NONE; @@ -210,6 +205,9 @@ void compute_crc(CrcType_t ct, const uint8_t *d, size_t n, uint8_t *first, uint8 case CRC_LEGIC_16: // TODO return; + case CRC_PHILIPS: + crc = crc16_philips(d, n); + break; case CRC_NONE: return; } @@ -244,6 +242,8 @@ uint16_t Crc16ex(CrcType_t ct, const uint8_t *d, size_t n) { case CRC_LEGIC_16: // TODO return 0; + case CRC_PHILIPS: + return crc16_philips(d, n); case CRC_NONE: default: break; @@ -290,6 +290,8 @@ bool check_crc(CrcType_t ct, const uint8_t *d, size_t n) { case CRC_LEGIC_16: // TODO return false; + case CRC_PHILIPS: + return (crc16_philips(d, n) == 0); case CRC_NONE: default: break; @@ -350,3 +352,6 @@ uint16_t crc16_legic(uint8_t const *d, size_t n, uint8_t uidcrc) { return crc16_fast(d, n, initial, true, false); } +uint16_t crc16_philips(uint8_t const *d, size_t n) { + return crc16_fast(d, n, 0x49A3, false, false); +} diff --git a/common/crc16.h b/common/crc16.h index d9631c43c..80758d95b 100644 --- a/common/crc16.h +++ b/common/crc16.h @@ -42,6 +42,7 @@ typedef enum { CRC_KERMIT, CRC_XMODEM, CRC_CRYPTORF, + CRC_PHILIPS, } CrcType_t; uint16_t update_crc16_ex(uint16_t crc, uint8_t c, uint16_t polynomial); @@ -78,6 +79,9 @@ uint16_t crc16_iclass(uint8_t const *d, size_t n); // ie: uidcrc = 0x78 then initial_value == 0x7878 uint16_t crc16_legic(uint8_t const *d, size_t n, uint8_t uidcrc); +// Calculate CRC-16/ Philips. +uint16_t crc16_philips(uint8_t const *d, size_t n); + // table implementation void init_table(CrcType_t crctype); void reset_table(void); diff --git a/common/default_version_pm3.c b/common/default_version_pm3.c index 46eac57c9..d93a7ef15 100644 --- a/common/default_version_pm3.c +++ b/common/default_version_pm3.c @@ -26,5 +26,7 @@ const struct version_information_t SECTVERSINFO g_version_information = { 1, /* version 1 */ 0, /* version information not present */ 2, /* cleanliness couldn't be determined */ - /* Remaining fields: zero */ + "Iceman/master/unknown", + "1970-01-01 00:00:00", + "no sha256" }; diff --git a/common/generator.c b/common/generator.c index 852ea2b61..25832710a 100644 --- a/common/generator.c +++ b/common/generator.c @@ -173,7 +173,7 @@ uint32_t ul_ev1_pwdgenD(const uint8_t *uid) { // AIR purifier Xiaomi uint32_t ul_ev1_pwdgenE(const uint8_t *uid) { - uint8_t hash[20]; + uint8_t hash[20] = {0}; mbedtls_sha1(uid, 7, hash); uint32_t pwd = 0; pwd |= (hash[ hash[0] % 20 ]) << 24 ; @@ -185,7 +185,7 @@ uint32_t ul_ev1_pwdgenE(const uint8_t *uid) { // NDEF tools format password generator uint32_t ul_ev1_pwdgenF(const uint8_t *uid) { - uint8_t hash[16]; + uint8_t hash[16] = {0};; mbedtls_md5(uid, 7, hash); uint32_t pwd = 0; pwd |= hash[0] << 24; @@ -195,6 +195,20 @@ uint32_t ul_ev1_pwdgenF(const uint8_t *uid) { return pwd; } +// Solution from @atc1441 +// https://gist.github.com/atc1441/41af75048e4c22af1f5f0d4c1d94bb56 +// Philips Sonicare toothbrush NFC head +uint32_t ul_ev1_pwdgenG(const uint8_t *uid, const uint8_t *mfg) { + + init_table(CRC_PHILIPS); + // UID + uint32_t crc1 = crc16_philips(uid, 7); + // MFG string + uint32_t crc2 = crc16_fast(mfg, 10, crc1, false, false); + + return (BSWAP_16(crc2) << 16 | BSWAP_16(crc1)); +} + // pack generation for algo 1-3 uint16_t ul_ev1_packgenA(const uint8_t *uid) { uint16_t pack = (uid[0] ^ uid[1] ^ uid[2]) << 8 | (uid[2] ^ 8); @@ -224,13 +238,26 @@ uint16_t ul_ev1_packgenD(const uint8_t *uid) { p ^= 0x5555; return BSWAP_16(p & 0xFFFF); } - uint16_t ul_ev1_packgenE(const uint8_t *uid) { uint32_t pwd = ul_ev1_pwdgenE(uid); return (0xAD << 8 | ((pwd >> 24) & 0xFF)); } +uint16_t ul_ev1_packgenG(const uint8_t *uid, const uint8_t *mfg) { + init_table(CRC_PHILIPS); + // UID + uint32_t crc1 = crc16_philips(uid, 7); + // MFG string + uint32_t crc2 = crc16_fast(mfg, 10, crc1, false, false); + // PWD + uint32_t pwd = (BSWAP_16(crc2) << 16 | BSWAP_16(crc1)); + + uint8_t pb[4]; + num_to_bytes(pwd, 4, pb); + return BSWAP_16(crc16_fast(pb, 4, crc2, false, false)); +} + // default shims uint32_t ul_ev1_pwdgen_def(const uint8_t *uid) { @@ -299,10 +326,41 @@ int mfc_algo_yale_all(uint8_t *uid, uint8_t *keys) { int mfc_algo_saflok_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t *key) { if (sector > 15) return PM3_EINVARG; if (key == NULL) return PM3_EINVARG; - if (keytype > 2) return PM3_EINVARG; - *key = 0; + + if (keytype == 0 && sector == 2) { + *key = 0xFFFFFFFFFFFF; + return PM3_SUCCESS; + } + + if (keytype == 0 && sector == 1) { + *key = 0x2a2c13cc242a; + return PM3_SUCCESS; + } + + if (keytype == 0) { + uint64_t lut[16] = { + 0xf057b39ee3d8ULL, 0x969d954ac157ULL, 0x8f43580d2c9dULL, 0xffcce0050c43ULL, + 0x341b15a690ccULL, 0x89585612e71bULL, 0xbb74b0953658ULL, 0xfb97f84b5b74ULL, + 0xc9d188359f92ULL, 0x8f92e97f5897ULL, 0x166ca2b09fd1ULL, 0x27dd93101c6cULL, + 0xda3e3fd649ddULL, 0x58dded078e3eULL, 0x5cd005cfd907ULL, 0x118dd00187d0ULL + }; + + uint8_t h = ((uid[3] >> 4) & 0xF); + h += ((uid[2] >> 4) & 0xF); + h += uid[0] & 0xF; + + uint64_t m = lut[h & 0xF]; + + uint64_t id = (bytes_to_num(uid, 4) << 8); + + *key = (h + (id + m + ((uint64_t)h << 40ULL))) & 0xFFFFFFFFFFFFULL; + + } else { + *key = 0xFFFFFFFFFFFF; + } return PM3_SUCCESS; } + int mfc_algo_saflok_all(uint8_t *uid, uint8_t *keys) { if (keys == NULL) return PM3_EINVARG; @@ -522,7 +580,7 @@ int mfdes_kdf_input_gallagher(uint8_t *uid, uint8_t uidLen, uint8_t keyNo, uint3 int mfc_generate4b_nuid(uint8_t *uid, uint8_t *nuid) { uint16_t crc; - uint8_t b1, b2; + uint8_t b1 = 0, b2 = 0; compute_crc(CRC_14443_A, uid, 3, &b1, &b2); nuid[0] = (b2 & 0xE0) | 0xF; @@ -556,7 +614,7 @@ int mfc_algo_touch_one(uint8_t *uid, uint8_t sector, uint8_t keytype, uint64_t * int generator_selftest(void) { #ifndef ON_DEVICE -#define NUM_OF_TEST 8 +#define NUM_OF_TEST 10 PrintAndLogEx(INFO, "PWD / KEY generator selftest"); PrintAndLogEx(INFO, "----------------------------"); @@ -569,64 +627,81 @@ int generator_selftest(void) { if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid1, 7), pwd1, success ? "OK" : "->8432EB17<-"); + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid1, 7), pwd1, success ? _GREEN_("ok") : "->8432EB17<-"); uint8_t uid2[] = {0x04, 0x1f, 0x98, 0xea, 0x1e, 0x3e, 0x81}; uint32_t pwd2 = ul_ev1_pwdgenB(uid2); success = (pwd2 == 0x5fd37eca); if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid2, 7), pwd2, success ? "OK" : "->5fd37eca<--"); + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid2, 7), pwd2, success ? _GREEN_("ok") : "->5fd37eca<--"); uint8_t uid3[] = {0x04, 0x62, 0xB6, 0x8A, 0xB4, 0x42, 0x80}; uint32_t pwd3 = ul_ev1_pwdgenC(uid3); success = (pwd3 == 0x5a349515); if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid3, 7), pwd3, success ? "OK" : "->5a349515<--"); + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid3, 7), pwd3, success ? _GREEN_("ok") : "->5a349515<--"); uint8_t uid4[] = {0x04, 0xC5, 0xDF, 0x4A, 0x6D, 0x51, 0x80}; uint32_t pwd4 = ul_ev1_pwdgenD(uid4); success = (pwd4 == 0x72B1EC61); if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid4, 7), pwd4, success ? "OK" : "->72B1EC61<--"); + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid4, 7), pwd4, success ? _GREEN_("ok") : "->72B1EC61<--"); uint8_t uid5[] = {0x04, 0xA0, 0x3C, 0xAA, 0x1E, 0x70, 0x80}; uint32_t pwd5 = ul_ev1_pwdgenE(uid5); success = (pwd5 == 0xCD91AFCC); if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid5, 7), pwd5, success ? "OK" : "->CD91AFCC<--"); + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid5, 7), pwd5, success ? _GREEN_("ok") : "->CD91AFCC<--"); uint8_t uid6[] = {0x04, 0x77, 0x42, 0xAB, 0xEF, 0x42, 0x70}; uint32_t pwd6 = ul_ev1_pwdgenF(uid6); success = (pwd6 == 0xA9C4C3C0); if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid6, 7), pwd6, success ? "OK" : "->A9C4C3C0<--"); + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid6, 7), pwd6, success ? _GREEN_("ok") : "->A9C4C3C0<--"); + + uint8_t uid7[] = {0x04, 0x0D, 0x4B, 0x5A, 0xC5, 0x71, 0x81}; + uint8_t mfg[] = {0x32, 0x31, 0x30, 0x36, 0x32, 0x38, 0x20, 0x35, 0x32, 0x4D}; + uint32_t pwd7 = ul_ev1_pwdgenG(uid7, mfg); + success = (pwd7 == 0xFBCFACC1); + if (success) + testresult++; + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %08X - %s", sprint_hex(uid7, 7), pwd7, success ? _GREEN_("ok") : "->FBCFACC1<--"); + // uint8_t uid5[] = {0x11, 0x22, 0x33, 0x44}; // uint64_t key1 = mfc_algo_a(uid5); // success = (key1 == 0xD1E2AA68E39A); -// PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %"PRIx64" - %s", sprint_hex(uid5, 4), key1, success ? "OK" : "->D1E2AA68E39A<--"); +// PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %"PRIx64" - %s", sprint_hex(uid5, 4), key1, success ? _GREEN_("ok") : "->D1E2AA68E39A<--"); - uint8_t uid7[] = {0x74, 0x57, 0xCA, 0xA9}; - uint64_t key7 = 0; - mfc_algo_sky_one(uid7, 15, 0, &key7); - success = (key7 == 0x82c7e64bc565); + uint8_t uid8[] = {0x74, 0x57, 0xCA, 0xA9}; + uint64_t key8 = 0; + mfc_algo_sky_one(uid8, 15, 0, &key8); + success = (key8 == 0x82c7e64bc565); if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %"PRIx64" - %s", sprint_hex(uid7, 4), key7, success ? "OK" : "->82C7E64BC565<--"); + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %"PRIx64" - %s", sprint_hex(uid8, 4), key8, success ? _GREEN_("ok") : "->82C7E64BC565<--"); + // MFC SAFLOK + uint8_t uid9[] = {0x11, 0x22, 0x33, 0x44}; + uint64_t key9 = 0; + mfc_algo_saflok_one(uid9, 0, 0, &key9); + success = (key9 == 0xD1E2AA68E39A); + if (success) + testresult++; + PrintAndLogEx(success ? SUCCESS : WARNING, "UID | %s | %"PRIX64" - %s", sprint_hex(uid9, 4), key9, success ? _GREEN_("ok") : _RED_(">> D1E2AA68E39A <<")); uint32_t lf_id = lf_t55xx_white_pwdgen(0x00000080); success = (lf_id == 0x00018383); if (success) testresult++; - PrintAndLogEx(success ? SUCCESS : WARNING, "ID | 0x00000080 | %08"PRIx32 " - %s", lf_id, success ? "OK" : "->00018383<--"); + PrintAndLogEx(success ? SUCCESS : WARNING, "ID | 0x00000080 | %08"PRIx32 " - %s", lf_id, success ? _GREEN_("ok") : "->00018383<--"); - PrintAndLogEx(SUCCESS, "------------------- Selftest %s", (testresult == NUM_OF_TEST) ? "OK" : "fail"); + PrintAndLogEx(SUCCESS, "------------------- Selftest %s", (testresult == NUM_OF_TEST) ? _GREEN_("ok") : _RED_("fail")); #endif return PM3_SUCCESS; diff --git a/common/generator.h b/common/generator.h index 1008d01fa..ce2601f26 100644 --- a/common/generator.h +++ b/common/generator.h @@ -28,6 +28,7 @@ uint32_t ul_ev1_pwdgenC(const uint8_t *uid); uint32_t ul_ev1_pwdgenD(const uint8_t *uid); uint32_t ul_ev1_pwdgenE(const uint8_t *uid); uint32_t ul_ev1_pwdgenF(const uint8_t *uid); +uint32_t ul_ev1_pwdgenG(const uint8_t *uid, const uint8_t *mfg); uint16_t ul_ev1_packgen_def(const uint8_t *uid); uint16_t ul_ev1_packgenA(const uint8_t *uid); @@ -35,6 +36,7 @@ uint16_t ul_ev1_packgenB(const uint8_t *uid); uint16_t ul_ev1_packgenC(const uint8_t *uid); uint16_t ul_ev1_packgenD(const uint8_t *uid); uint16_t ul_ev1_packgenE(const uint8_t *uid); +uint16_t ul_ev1_packgenG(const uint8_t *uid, const uint8_t *mfg); uint32_t ul_c_otpgenA(const uint8_t *uid); diff --git a/common/lfdemod.c b/common/lfdemod.c index bf0bfd4c6..1255396c9 100644 --- a/common/lfdemod.c +++ b/common/lfdemod.c @@ -53,8 +53,8 @@ // ********************************************************************************************** // ---------------------------------Utilities Section-------------------------------------------- // ********************************************************************************************** -#define LOWEST_DEFAULT_CLOCK 32 -#define FSK_PSK_THRESHOLD 123 +#define LOWEST_DEFAULT_CLOCK 32 +#define FSK_PSK_THRESHOLD 123 //to allow debug print calls when used not on dev @@ -268,12 +268,20 @@ static size_t removeEm410xParity(uint8_t *bits, size_t startIdx, bool isLong, bo *validLong = false; uint8_t bLen = isLong ? 110 : 55; uint16_t parityCol[4] = { 0, 0, 0, 0 }; + for (int word = 0; word < bLen; word += 5) { for (int bit = 0; bit < 5; bit++) { - if (word + bit >= bLen) break; + + if (word + bit >= bLen) { + break; + } + parityWd = (parityWd << 1) | bits[startIdx + word + bit]; - if ((word <= 50) && (bit < 4)) + + if ((word <= 50) && (bit < 4)) { parityCol[bit] = (parityCol[bit] << 1) | bits[startIdx + word + bit]; + } + bits[bitCnt++] = (bits[startIdx + word + bit]); } if (word + 5 > bLen) break; @@ -293,12 +301,15 @@ static size_t removeEm410xParity(uint8_t *bits, size_t startIdx, bool isLong, bo if (!isLong && validRowParitySkipColP && validColParity) { *validShort = true; } + if (isLong && validRowParity) { *validLong = true; } + if (isLong && validRowParitySkipColP && validColParity) { *validShortExtended = true; } + if (*validShort || *validShortExtended || *validLong) { return bitCnt; } else { @@ -425,10 +436,10 @@ static size_t findModStart(const uint8_t *src, size_t size, uint8_t expWaveSize) } static int getClosestClock(int testclk) { - const uint16_t clocks[] = {8, 16, 32, 40, 50, 64, 100, 128, 256, 384}; - const uint8_t limit[] = {1, 2, 4, 4, 5, 8, 8, 8, 8, 8}; + const uint16_t clocks[] = {8, 16, 32, 40, 50, 64, 100, 128, 256, 272, 384}; + const uint8_t limit[] = {1, 2, 4, 4, 5, 8, 8, 8, 8, 24, 24}; - for (uint8_t i = 0; i < 10; i++) { + for (uint8_t i = 0; i < ARRAYLEN(clocks); i++) { if (testclk >= clocks[i] - limit[i] && testclk <= clocks[i] + limit[i]) return clocks[i]; } @@ -602,7 +613,7 @@ bool DetectCleanAskWave(const uint8_t *dest, size_t size, uint8_t high, uint8_t // based on count of low to low int DetectStrongAskClock(uint8_t *dest, size_t size, int high, int low, int *clock) { size_t i = 100; - size_t minClk = 512; + size_t minClk = 768; uint16_t shortestWaveIdx = 0; // get to first full low to prime loop and skip incomplete first pulse @@ -611,11 +622,11 @@ int DetectStrongAskClock(uint8_t *dest, size_t size, int high, int low, int *clo if (i == size) return -1; - if (size < 512) + if (size < 768) return -2; // clock, numoftimes, first idx - uint16_t tmpclk[10][3] = { + uint16_t tmpclk[11][3] = { {8, 0, 0}, {16, 0, 0}, {32, 0, 0}, @@ -625,11 +636,12 @@ int DetectStrongAskClock(uint8_t *dest, size_t size, int high, int low, int *clo {100, 0, 0}, {128, 0, 0}, {256, 0, 0}, + {272, 0, 0}, {384, 0, 0}, }; // loop through all samples (well, we don't want to go out-of-bounds) - while (i < (size - 512)) { + while (i < (size - 768)) { // measure from low to low size_t startwave = i; @@ -644,7 +656,7 @@ int DetectStrongAskClock(uint8_t *dest, size_t size, int high, int low, int *clo int foo = getClosestClock(minClk); if (foo > 0) { - for (uint8_t j = 0; j < 10; j++) { + for (uint8_t j = 0; j < 11; j++) { if (tmpclk[j][0] == foo) { tmpclk[j][1]++; @@ -658,8 +670,17 @@ int DetectStrongAskClock(uint8_t *dest, size_t size, int high, int low, int *clo } // find the clock with most hits and it the first index it was encountered. + int possible_clks = 0; + for (uint8_t j = 0; j < 11; j++) { + if (tmpclk[j][1] > 0) { + possible_clks++; + } + } + + uint16_t second_shortest = 0; + int second = 0; int max = 0; - for (uint8_t j = 0; j < 10; j++) { + for (int j = 10; j > -1; j--) { if (g_debugMode == 2) { prnt("DEBUG, ASK, clocks %u | hits %u | idx %u" , tmpclk[j][0] @@ -667,13 +688,23 @@ int DetectStrongAskClock(uint8_t *dest, size_t size, int high, int low, int *clo , tmpclk[j][2] ); } + if (max < tmpclk[j][1]) { + second = *clock; + second_shortest = shortestWaveIdx; + *clock = tmpclk[j][0]; shortestWaveIdx = tmpclk[j][2]; max = tmpclk[j][1]; } } + // ASK clock 8 is very rare and usually gives us false positives + if (possible_clks > 1 && *clock == 8) { + *clock = second; + shortestWaveIdx = second_shortest; + } + if (*clock == 0) return -1; @@ -701,9 +732,9 @@ int DetectASKClock(uint8_t *dest, size_t size, int *clock, int maxErr) { } size_t i = 1; - uint8_t num_clks = 9; + uint8_t num_clks = 10; // first 255 value pos0 is placeholder for user inputed clock. - uint16_t clk[] = {255, 8, 16, 32, 40, 50, 64, 100, 128, 255}; + uint16_t clk[] = {255, 8, 16, 32, 40, 50, 64, 100, 128, 255, 272}; // sometimes there is a strange end wave - filter out this size -= 60; @@ -744,8 +775,8 @@ int DetectASKClock(uint8_t *dest, size_t size, int *clock, int maxErr) { uint8_t clkCnt, tol; size_t j = 0; - uint16_t bestErr[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000}; - uint8_t bestStart[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint16_t bestErr[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000}; + uint8_t bestStart[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; size_t errCnt, arrLoc, loopEnd; if (found_clk) { @@ -884,11 +915,11 @@ int DetectStrongNRZClk(const uint8_t *dest, size_t size, int peak, int low, bool // detect nrz clock by reading #peaks vs no peaks(or errors) int DetectNRZClock(uint8_t *dest, size_t size, int clock, size_t *clockStartIdx) { size_t i = 0; - uint8_t clk[] = {8, 16, 32, 40, 50, 64, 100, 128, 255}; + uint16_t clk[] = {8, 16, 32, 40, 50, 64, 100, 128, 255, 272, 384}; size_t loopCnt = 4096; //don't need to loop through entire array... //if we already have a valid clock quit - for (; i < 8; ++i) + for (; i < ARRAYLEN(clk); ++i) if (clk[i] == clock) return clock; if (size < 20) return 0; @@ -915,7 +946,7 @@ int DetectNRZClock(uint8_t *dest, size_t size, int clock, size_t *clockStartIdx) uint8_t tol = 0; uint16_t smplCnt = 0; int16_t peakcnt = 0; - int16_t peaksdet[] = {0, 0, 0, 0, 0, 0, 0, 0}; + int16_t peaksdet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint16_t minPeak = 255; bool firstpeak = true; //test for large clipped waves - ignore first peak @@ -938,10 +969,10 @@ int DetectNRZClock(uint8_t *dest, size_t size, int clock, size_t *clockStartIdx) bool errBitHigh = 0, bitHigh = 0, lastPeakHigh = 0; uint8_t ignoreCnt = 0, ignoreWindow = 4; int lastBit = 0; - size_t bestStart[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + size_t bestStart[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; peakcnt = 0; //test each valid clock from smallest to greatest to see which lines up - for (clkCnt = 0; clkCnt < 8; ++clkCnt) { + for (clkCnt = 0; clkCnt < ARRAYLEN(bestStart); ++clkCnt) { //ignore clocks smaller than smallest peak if (clk[clkCnt] < minPeak - (clk[clkCnt] / 4)) continue; //try lining up the peaks by moving starting point (try first 256) @@ -994,7 +1025,7 @@ int DetectNRZClock(uint8_t *dest, size_t size, int clock, size_t *clockStartIdx) } uint8_t best = 0; - for (int m = 7; m > 0; m--) { + for (int m = ARRAYLEN(peaksdet) - 1; m >= 0; m--) { if ((peaksdet[m] >= (peaksdet[best] - 1)) && (peaksdet[m] <= peaksdet[best] + 1) && lowestTransition) { if (clk[m] > (lowestTransition - (clk[m] / 8)) && clk[m] < (lowestTransition + (clk[m] / 8))) { best = m; @@ -1087,10 +1118,12 @@ uint16_t countFC(const uint8_t *bits, size_t size, bool fskAdj) { fcH = fcLens[best2]; fcL = fcLens[best1]; } + /* if ((size - 180) / fcH / 3 > fcCnts[best1] + fcCnts[best2]) { if (g_debugMode == 2) prnt("DEBUG countfc: fc is too large: %zu > %u. Not psk or fsk", (size - 180) / fcH / 3, fcCnts[best1] + fcCnts[best2]); return 0; //lots of waves not psk or fsk } + */ // TODO: take top 3 answers and compare to known Field clocks to get top 2 uint16_t fcs = (((uint16_t)fcH) << 8) | fcL; @@ -1101,8 +1134,8 @@ uint16_t countFC(const uint8_t *bits, size_t size, bool fskAdj) { // detect psk clock by reading each phase shift // a phase shift is determined by measuring the sample length of each wave int DetectPSKClock(uint8_t *dest, size_t size, int clock, size_t *firstPhaseShift, uint8_t *curPhase, uint8_t *fc) { - uint8_t clk[] = {255, 16, 32, 40, 50, 64, 100, 128, 255}; //255 is not a valid clock - uint16_t loopCnt = 4096; //don't need to loop through entire array... + uint16_t clk[] = {255, 16, 32, 40, 50, 64, 100, 128, 256, 272, 384}; // 255 is not a valid clock + uint16_t loopCnt = 4096; // don't need to loop through entire array... if (size < 160 + 20) return 0; // size must be larger than 20 here, and 160 later on. @@ -1123,8 +1156,8 @@ int DetectPSKClock(uint8_t *dest, size_t size, int clock, size_t *firstPhaseShif uint8_t clkCnt; uint16_t waveLenCnt, fullWaveLen = 0; - uint16_t bestErr[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000}; - uint16_t peaksdet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint16_t bestErr[] = {1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000}; + uint16_t peaksdet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //find start of modulating data in trace size_t i = findModStart(dest, size, *fc); @@ -1146,7 +1179,7 @@ int DetectPSKClock(uint8_t *dest, size_t size, int clock, size_t *firstPhaseShif } //test each valid clock from greatest to smallest to see which lines up - for (clkCnt = 7; clkCnt >= 1 ; clkCnt--) { + for (clkCnt = 9; clkCnt >= 1 ; clkCnt--) { uint8_t tol = *fc / 2; size_t lastClkBit = firstFullWave; //set end of wave as clock align size_t waveStart = 0; @@ -1186,8 +1219,8 @@ int DetectPSKClock(uint8_t *dest, size_t size, int clock, size_t *firstPhaseShif } //all tested with errors //return the highest clk with the most peaks found - uint8_t best = 7; - for (i = 7; i >= 1; i--) { + uint8_t best = 9; + for (i = 9; i >= 1; i--) { if (peaksdet[i] > peaksdet[best]) best = i; @@ -1463,7 +1496,7 @@ bool DetectST(uint8_t *buffer, size_t *size, int *foundclock, size_t *ststart, s static int millerRawDecode(uint8_t *bits, size_t *size, int invert) { if (*size < 16) return -1; - uint16_t MaxBits = 512, errCnt = 0; + uint16_t MaxBits = MAX_DEMODULATION_BITS, errCnt = 0; size_t i, bitCnt = 0; uint8_t alignCnt = 0, curBit = bits[0], alignedIdx = 0, halfClkErr = 0; @@ -1507,7 +1540,7 @@ int BiphaseRawDecode(uint8_t *bits, size_t *size, int *offset, int invert) { uint16_t bitnum = 0; uint16_t errCnt = 0; size_t i = *offset; - uint16_t maxbits = 512; + uint16_t maxbits = MAX_DEMODULATION_BITS; //check for phase change faults - skip one sample if faulty bool offsetA = true, offsetB = true; @@ -1547,7 +1580,7 @@ uint16_t manrawdecode(uint8_t *bits, size_t *size, uint8_t invert, uint8_t *alig if (*size < 16) return 0xFFFF; int errCnt = 0, bestErr = 1000; - uint16_t bitnum = 0, maxBits = 512, bestRun = 0; + uint16_t bitnum = 0, maxBits = MAX_DEMODULATION_BITS, bestRun = 0; size_t i; //find correct start position [alignment] diff --git a/common/lfdemod.h b/common/lfdemod.h index c8ca0cfea..a33752b31 100644 --- a/common/lfdemod.h +++ b/common/lfdemod.h @@ -30,6 +30,9 @@ // ignore first x samples of the buffer #define SIGNAL_IGNORE_FIRST_SAMPLES 10 +// Max number of bits when demodulating a signal +#define MAX_DEMODULATION_BITS 4096 + // generic typedef struct { int low; diff --git a/common/mbedtls/Makefile b/common/mbedtls/Makefile index 142ac6b12..0413bdff7 100644 --- a/common/mbedtls/Makefile +++ b/common/mbedtls/Makefile @@ -21,7 +21,10 @@ MYSRCS = \ cipher.c \ cmac.c \ des.c \ + ecc_point_compression.c \ + ecdh.c \ ecdsa.c \ + gcm.c \ md.c \ md5.c \ oid.c \ @@ -43,7 +46,8 @@ MYSRCS = \ threading.c \ x509.c \ x509_crl.c \ - x509_crt.c + x509_crt.c \ + net_sockets.c LIB_A = libmbedtls.a diff --git a/common/mbedtls/config.h b/common/mbedtls/config.h index e6138bc6b..a3faef146 100644 --- a/common/mbedtls/config.h +++ b/common/mbedtls/config.h @@ -2811,7 +2811,7 @@ * This module enables the AES-GCM and CAMELLIA-GCM ciphersuites, if other * requisites are enabled as well. */ -//#define MBEDTLS_GCM_C +#define MBEDTLS_GCM_C /** * \def MBEDTLS_HAVEGE_C @@ -2979,7 +2979,7 @@ * * This module provides networking routines. */ -//#define MBEDTLS_NET_C +#define MBEDTLS_NET_C /** * \def MBEDTLS_OID_C diff --git a/common/mbedtls/ecc_point_compression.c b/common/mbedtls/ecc_point_compression.c new file mode 100644 index 000000000..7216f3662 --- /dev/null +++ b/common/mbedtls/ecc_point_compression.c @@ -0,0 +1,130 @@ +/* +* Not original to the mbedtls library. Taken from +* https://github.com/mwarning/mbedtls_ecp_compression +* to solve mbedtls' lack of support for elliptic point +* compression and decompression +* +* Released under CC0 1.0 Universal License +*/ + +/* +* This is all about mbedtls_ecp_decompress() and mbedtls_ecp_compress() +* +* Perform X25519 / Curve25519 point compression and decompression for mbedtls. +* As of mbedtls 2.5.1, mbedtls does not support decompression yet. +* +*/ + +#define MBEDTLS_ALLOW_PRIVATE_ACCESS + +#include "ecc_point_compression.h" + +int mbedtls_ecp_decompress( + const mbedtls_ecp_group *grp, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize +) { + int ret; + size_t plen; + mbedtls_mpi r; + mbedtls_mpi x; + mbedtls_mpi n; + + plen = mbedtls_mpi_size(&grp->P); + + *olen = 2 * plen + 1; + + if (osize < *olen) + return (MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL); + + if (ilen != plen + 1) + return (MBEDTLS_ERR_ECP_BAD_INPUT_DATA); + + if (input[0] != 0x02 && input[0] != 0x03) + return (MBEDTLS_ERR_ECP_BAD_INPUT_DATA); + + // output will consist of 0x04|X|Y + memcpy(output, input, ilen); + output[0] = 0x04; + + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&x); + mbedtls_mpi_init(&n); + + // x <= input + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&x, input + 1, plen)); + + // r = x^2 + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&r, &x, &x)); + + // r = x^2 + a + if (grp->A.p == NULL) { + // Special case where a is -3 + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&r, &r, 3)); + } else { + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&r, &r, &grp->A)); + } + + // r = x^3 + ax + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&r, &r, &x)); + + // r = x^3 + ax + b + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&r, &r, &grp->B)); + + // Calculate square root of r over finite field P: + // r = sqrt(x^3 + ax + b) = (x^3 + ax + b) ^ ((P + 1) / 4) (mod P) + + // n = P + 1 + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&n, &grp->P, 1)); + + // n = (P + 1) / 4 + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&n, 2)); + + // r ^ ((P + 1) / 4) (mod p) + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&r, &r, &n, &grp->P, NULL)); + + // Select solution that has the correct "sign" (equals odd/even solution in finite group) + if ((input[0] == 0x03) != mbedtls_mpi_get_bit(&r, 0)) { + // r = p - r + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&r, &grp->P, &r)); + } + + // y => output + ret = mbedtls_mpi_write_binary(&r, output + 1 + plen, plen); + +cleanup: + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&x); + mbedtls_mpi_free(&n); + + return (ret); +} + +int mbedtls_ecp_compress( + const mbedtls_ecp_group *grp, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize +) { + size_t plen; + + plen = mbedtls_mpi_size(&grp->P); + + *olen = plen + 1; + + if (osize < *olen) + return (MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL); + + if (ilen != 2 * plen + 1) + return (MBEDTLS_ERR_ECP_BAD_INPUT_DATA); + + if (input[0] != 0x04) + return (MBEDTLS_ERR_ECP_BAD_INPUT_DATA); + + // output will consist of 0x0?|X + memcpy(output, input, *olen); + + // Encode even/odd of Y into first byte (either 0x02 or 0x03) + output[0] = 0x02 + (input[2 * plen] & 1); + + return (0); +} diff --git a/common/mbedtls/ecc_point_compression.h b/common/mbedtls/ecc_point_compression.h new file mode 100644 index 000000000..fc691f8a4 --- /dev/null +++ b/common/mbedtls/ecc_point_compression.h @@ -0,0 +1,32 @@ +/* +* Not original to the mbedtls library. Taken from +* https://github.com/mwarning/mbedtls_ecp_compression +* to solve mbedtls' lack of support for elliptic point +* compression and decompression +* +* Released under CC0 1.0 Universal License +*/ + +/* +* This is all about mbedtls_ecp_decompress() and mbedtls_ecp_compress() +* +* Perform X25519 / Curve25519 point compression and decompression for mbedtls. +* As of mbedtls 2.5.1, mbedtls does not support decompression yet. +* +*/ + +#include + +#include "mbedtls/ecp.h" + +int mbedtls_ecp_decompress( + const mbedtls_ecp_group *grp, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize +); + +int mbedtls_ecp_compress( + const mbedtls_ecp_group *grp, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize +); diff --git a/common_arm/Makefile.common b/common_arm/Makefile.common index 99f4d9ecd..18d931b46 100644 --- a/common_arm/Makefile.common +++ b/common_arm/Makefile.common @@ -44,7 +44,7 @@ OBJDIR = obj INCLUDE = -I../include -I../common_arm -I../common_fpga -I../common -I. # Also search prerequisites in the common directory (for usb.c), the fpga directory (for fpga.bit), and the lz4 directory -VPATH = . ../common_arm ../common ../common/crapto1 ../common/mbedtls ../common/lz4 ../fpga-$(PLATFORM_FPGA) ../armsrc/Standalone +VPATH = . ../common_arm ../common ../common/crapto1 ../common/mbedtls ../common/lz4 ../fpga ../armsrc/Standalone INCLUDES = ../include/proxmark3_arm.h ../include/at91sam7s512.h ../include/config_gpio.h ../include/pm3_cmd.h diff --git a/common_arm/Makefile.hal b/common_arm/Makefile.hal index a65514784..bbc0af74e 100644 --- a/common_arm/Makefile.hal +++ b/common_arm/Makefile.hal @@ -48,7 +48,9 @@ Known definitions: endef define HELP_DEFINITIONS + Options to define platform, platform extras and/or standalone mode: + (1) Run make with your PLATFORM, PLATFORM_EXTRAS and/or STANDALONE choices as follows: make PLATFORM=PM3GENERIC STANDALONE=$(HELP_EXAMPLE_STANDALONE) @@ -65,6 +67,7 @@ STANDALONE= For Proxmarks with only 256k, you can define PLATFORM_SIZE=256 + to be warned if the image is too big for your device and you can specify which parts to skip in order to reduce the size: SKIP_LF=1 @@ -80,6 +83,7 @@ SKIP_NFCBARCODE=1 SKIP_HFSNIFF=1 SKIP_HFPLOT=1 SKIP_ZX8211=1 + endef define KNOWN_DEFINITIONS @@ -92,18 +96,26 @@ PLTNAME = Unknown Platform PLATFORM_FPGA = fpga-undefined ifeq ($(PLATFORM),PM3RDV4) + # FPGA bitstream files, the order matters! + FPGA_BITSTREAMS = fpga_pm3_lf.bit fpga_pm3_hf.bit fpga_pm3_felica.bit fpga_pm3_hf_15.bit PLATFORM_DEFS = -DWITH_SMARTCARD -DWITH_FLASH -DRDV4 PLTNAME = Proxmark3 RDV4 PLATFORM_FPGA = xc2s30 RDV4 = yes else ifeq ($(PLATFORM),PM3OTHER) $(warning PLATFORM=PM3OTHER is deprecated, please use PLATFORM=PM3GENERIC) + # FPGA bitstream files, the order matters! + FPGA_BITSTREAMS = fpga_pm3_lf.bit fpga_pm3_hf.bit fpga_pm3_felica.bit fpga_pm3_hf_15.bit PLTNAME = Proxmark3 generic target PLATFORM_FPGA = xc2s30 else ifeq ($(PLATFORM),PM3GENERIC) + # FPGA bitstream files, the order matters! + FPGA_BITSTREAMS = fpga_pm3_lf.bit fpga_pm3_hf.bit fpga_pm3_felica.bit fpga_pm3_hf_15.bit PLTNAME = Proxmark3 generic target PLATFORM_FPGA = xc2s30 else ifeq ($(PLATFORM),PM3ICOPYX) + # FPGA bitstream files, the order matters - only hf has a bitstream, the other 3 files are 0 bytes + FPGA_BITSTREAMS = fpga_icopyx_lf.bit fpga_icopyx_hf.bit fpga_icopyx_felica.bit fpga_icopyx_hf_15.bit PLATFORM_DEFS = -DWITH_FLASH -DICOPYX -DXC3 PLTNAME = iCopy-X with XC3S100E PLATFORM_FPGA = xc3s100e @@ -114,6 +126,10 @@ endif # parsing additional PLATFORM_EXTRAS tokens PLATFORM_EXTRAS_TMP:=$(PLATFORM_EXTRAS) +ifneq (,$(findstring SMARTCARD,$(PLATFORM_EXTRAS_TMP))) + PLATFORM_DEFS += -DWITH_SMARTCARD + PLATFORM_EXTRAS_TMP := $(strip $(filter-out SMARTCARD,$(PLATFORM_EXTRAS_TMP))) +endif ifneq (,$(findstring FLASH,$(PLATFORM_EXTRAS_TMP))) PLATFORM_DEFS += -DWITH_FLASH PLATFORM_EXTRAS_TMP := $(strip $(filter-out FLASH,$(PLATFORM_EXTRAS_TMP))) @@ -246,6 +262,7 @@ export PLATFORM_DEFS export PLATFORM_DEFS_INFO export PLATFORM_DEFS_INFO_STANDALONE export PLATFORM_CHANGED +export FPGA_BITSTREAMS $(info ===================================================================) $(info Version info: $(shell tools/mkversion.sh --short 2>/dev/null || ../tools/mkversion.sh --short 2>/dev/null)) diff --git a/common_arm/flashmem.c b/common_arm/flashmem.c index 94dcf1fd3..2cb54ed39 100644 --- a/common_arm/flashmem.c +++ b/common_arm/flashmem.c @@ -339,10 +339,10 @@ void Flashmem_print_status(void) { Dbprintf(" Baudrate................ " _GREEN_("%d MHz"), FLASHMEM_SPIBAUDRATE / 1000000); if (!FlashInit()) { - DbpString(" Init.................... " _RED_("FAILED")); + DbpString(" Init.................... " _RED_("failed")); return; } - DbpString(" Init.................... " _GREEN_("OK")); + DbpString(" Init.................... " _GREEN_("ok")); // NOTE: It would likely be more useful to use JDEC ID command 9F, // as it provides a third byte indicative of capacity. @@ -402,7 +402,7 @@ void Flashmem_print_info(void) { if (isok == 2) { num = ((keysum[1] << 8) | keysum[0]); if (num != 0xFFFF && num != 0x0) - Dbprintf(" Mifare.................. "_YELLOW_("%d")" / "_GREEN_("%d")" keys", num, DEFAULT_MF_KEYS_MAX); + Dbprintf(" Mifare.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_MF_KEYS_MAX); } Flash_CheckBusy(BUSY_TIMEOUT); @@ -410,7 +410,7 @@ void Flashmem_print_info(void) { if (isok == 2) { num = ((keysum[1] << 8) | keysum[0]); if (num != 0xFFFF && num != 0x0) - Dbprintf(" T55x7................... "_YELLOW_("%d")" / "_GREEN_("%d")" keys", num, DEFAULT_T55XX_KEYS_MAX); + Dbprintf(" T55x7................... "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_T55XX_KEYS_MAX); } Flash_CheckBusy(BUSY_TIMEOUT); @@ -418,7 +418,7 @@ void Flashmem_print_info(void) { if (isok == 2) { num = ((keysum[1] << 8) | keysum[0]); if (num != 0xFFFF && num != 0x0) - Dbprintf(" iClass.................. "_YELLOW_("%d")" / "_GREEN_("%d")" keys", num, DEFAULT_ICLASS_KEYS_MAX); + Dbprintf(" iClass.................. "_YELLOW_("%u")" / "_GREEN_("%u")" keys", num, DEFAULT_ICLASS_KEYS_MAX); } FlashStop(); diff --git a/common_arm/usb_cdc.c b/common_arm/usb_cdc.c index cb2d0c64a..f0050bd90 100644 --- a/common_arm/usb_cdc.c +++ b/common_arm/usb_cdc.c @@ -37,10 +37,7 @@ AT91SAM7S256 USB Device Port #define AT91C_EP_IN 2 // cfg bulk in #define AT91C_EP_NOTIFY 3 // cfg cdc notification interrup -#define AT91C_EP_CONTROL_SIZE 8 -#define AT91C_EP_OUT_SIZE 64 -#define AT91C_EP_IN_SIZE 64 - +// The endpoint size is defined in usb_cdc.h // Section: USB Descriptors #define USB_DESCRIPTOR_DEVICE 0x01 // DescriptorType for a Device Descriptor. @@ -114,6 +111,7 @@ AT91SAM7S256 USB Device Port #define SET_LINE_CODING 0x2021 #define SET_CONTROL_LINE_STATE 0x2221 +static bool isAsyncRequestFinished = false; static AT91PS_UDP pUdp = AT91C_BASE_UDP; static uint8_t btConfiguration = 0; static uint8_t btConnection = 0; @@ -127,7 +125,7 @@ static const char devDescriptor[] = { 2, // Device Class: Communication Device Class 0, // Device Subclass: CDC class sub code ACM [ice 0x02 = win10 virtual comport ] 0, // Device Protocol: CDC Device protocol (unused) - AT91C_EP_CONTROL_SIZE, // MaxPacketSize0 + AT91C_USB_EP_CONTROL_SIZE, // MaxPacketSize0 0xc4, 0x9a, // Vendor ID [0x9ac4 = J. Westhues] 0x8f, 0x4b, // Product ID [0x4b8f = Proxmark-3 RFID Instrument] 0x00, 0x01, // BCD Device release number (1.00) @@ -217,7 +215,7 @@ static const char cfgDescriptor[] = { USB_DESCRIPTOR_ENDPOINT, // Descriptor Type _EP03_IN, // EndpointAddress: Endpoint 03 - IN _INTERRUPT, // Attributes - AT91C_EP_CONTROL_SIZE, 0x00, // MaxPacket Size: EP0 - 8 + AT91C_USB_EP_CONTROL_SIZE, 0x00, // MaxPacket Size: EP0 - 8 0xFF, // Interval polling @@ -238,7 +236,7 @@ static const char cfgDescriptor[] = { USB_DESCRIPTOR_ENDPOINT, // Descriptor Type _EP01_OUT, // Endpoint Address: Endpoint 01 - OUT _BULK, // Attributes: BULK - AT91C_EP_OUT_SIZE, 0x00, // MaxPacket Size: 64 bytes + AT91C_USB_EP_OUT_SIZE, 0x00, // MaxPacket Size: 64 bytes 0, // Interval: ignored for bulk /* Endpoint descriptor */ @@ -246,7 +244,7 @@ static const char cfgDescriptor[] = { USB_DESCRIPTOR_ENDPOINT, // Descriptor Type _EP02_IN, // Endpoint Address: Endpoint 02 - IN _BULK, // Attribute: BULK - AT91C_EP_IN_SIZE, 0x00, // MaxPacket Size: 64 bytes + AT91C_USB_EP_IN_SIZE, 0x00, // MaxPacket Size: 64 bytes 0 // Interval: ignored for bulk }; @@ -638,6 +636,10 @@ bool usb_poll(void) { return (pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank); } +inline uint16_t usb_available_length(void) { + return ((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16); +} + /** In github PR #129, some users appears to get a false positive from usb_poll, which returns true, but the usb_read operation @@ -649,7 +651,7 @@ bool usb_poll(void) { bool usb_poll_validate_length(void) { if (!usb_check()) return false; if (!(pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank)) return false; - return ((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) > 0; + return ((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) > 0; } /* @@ -667,15 +669,17 @@ uint32_t usb_read(uint8_t *data, size_t len) { uint32_t time_out = 0; while (len) { - if (!usb_check()) break; + if (!usb_check()) + break; if (pUdp->UDP_CSR[AT91C_EP_OUT] & bank) { - packetSize = (pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16; + packetSize = ((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16); packetSize = MIN(packetSize, len); len -= packetSize; - while (packetSize--) + while (packetSize--) { data[nbBytesRcv++] = pUdp->UDP_FDR[AT91C_EP_OUT]; + } // flip bank UDP_CLEAR_EP_FLAGS(AT91C_EP_OUT, bank) @@ -685,20 +689,23 @@ uint32_t usb_read(uint8_t *data, size_t len) { else bank = AT91C_UDP_RX_DATA_BK0; } - if (time_out++ == 0x1fff) break; + + if (time_out++ == 0x1fff) + break; } btReceiveBank = bank; return nbBytesRcv; } -static uint8_t usb_read_ng_buffer[64]; +static uint8_t usb_read_ng_buffer[64] = {0}; static size_t usb_read_ng_bufoff = 0; static size_t usb_read_ng_buflen = 0; uint32_t usb_read_ng(uint8_t *data, size_t len) { - if (len == 0) return 0; + if (len == 0) + return 0; uint8_t bank = btReceiveBank; uint32_t packetSize, nbBytesRcv = 0; @@ -706,36 +713,44 @@ uint32_t usb_read_ng(uint8_t *data, size_t len) { // take first from local buffer if (len <= usb_read_ng_buflen) { - for (uint32_t i = 0; i < len; i++) + + for (uint32_t i = 0; i < len; i++) { data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoff + i]; + } + usb_read_ng_buflen -= len; if (usb_read_ng_buflen == 0) usb_read_ng_bufoff = 0; else usb_read_ng_bufoff += len; + return nbBytesRcv; } else { - for (uint32_t i = 0; i < usb_read_ng_buflen; i++) + for (uint32_t i = 0; i < usb_read_ng_buflen; i++) { data[nbBytesRcv++] = usb_read_ng_buffer[usb_read_ng_bufoff + i]; + } len -= usb_read_ng_buflen; usb_read_ng_buflen = 0; usb_read_ng_bufoff = 0; } while (len) { - if (!usb_check()) break; + if (!usb_check()) + break; if ((pUdp->UDP_CSR[AT91C_EP_OUT] & bank)) { - uint32_t available = (pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16; + uint32_t available = ((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16); packetSize = MIN(available, len); available -= packetSize; len -= packetSize; - while (packetSize--) + while (packetSize--) { data[nbBytesRcv++] = pUdp->UDP_FDR[AT91C_EP_OUT]; + } // fill the local buffer with the remaining bytes - for (uint32_t i = 0; i < available; i++) + for (uint32_t i = 0; i < available; i++) { usb_read_ng_buffer[i] = pUdp->UDP_FDR[AT91C_EP_OUT]; + } usb_read_ng_buflen = available; // flip bank UDP_CLEAR_EP_FLAGS(AT91C_EP_OUT, bank) @@ -744,7 +759,8 @@ uint32_t usb_read_ng(uint8_t *data, size_t len) { else bank = AT91C_UDP_RX_DATA_BK0; } - if (time_out++ == 0x1fff) break; + if (time_out++ == 0x1fff) + break; } btReceiveBank = bank; @@ -770,18 +786,18 @@ int usb_write(const uint8_t *data, const size_t len) { // send first chunk - cpt = MIN(length, AT91C_EP_IN_SIZE); + cpt = MIN(length, AT91C_USB_EP_IN_SIZE); length -= cpt; while (cpt--) { pUdp->UDP_FDR[AT91C_EP_IN] = *data++; } UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); - while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) {}; + while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY)) {}; while (length) { // Send next chunk - cpt = MIN(length, AT91C_EP_IN_SIZE); + cpt = MIN(length, AT91C_USB_EP_IN_SIZE); length -= cpt; while (cpt--) { pUdp->UDP_FDR[AT91C_EP_IN] = *data++; @@ -797,7 +813,7 @@ int usb_write(const uint8_t *data, const size_t len) { while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP) {}; UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); - while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) {}; + while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY)) {}; } // Wait for the end of transfer @@ -809,8 +825,8 @@ int usb_write(const uint8_t *data, const size_t len) { while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP) {}; - if (len % AT91C_EP_IN_SIZE == 0) { - + if (len % AT91C_USB_EP_IN_SIZE == 0) { + // like AT91F_USB_SendZlp(), in non ping-pong mode UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) {}; @@ -821,6 +837,117 @@ int usb_write(const uint8_t *data, const size_t len) { return PM3_SUCCESS; } +/* + *---------------------------------------------------------------------------- + * \fn async_usb_write_start + * \brief Start async write process + * \return PM3_EIO if USB is invalid, PM3_SUCCESS if it is ready for write + * + * This function checks if the USB is connected, and wait until the FIFO + * is ready to be filled. + * + * Warning: usb_write() should not be called between + * async_usb_write_start() and async_usb_write_stop(). + *---------------------------------------------------------------------------- +*/ +int async_usb_write_start(void) { + + if (!usb_check()) return PM3_EIO; + + while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) { + if (!usb_check()) return PM3_EIO; + } + + isAsyncRequestFinished = false; + return PM3_SUCCESS; +} + +/* + *---------------------------------------------------------------------------- + * \fn async_usb_write_pushByte + * \brief Push one byte to the FIFO of IN endpoint (time-critical) + * + * This function simply push a byte to the FIFO of IN endpoint. + * The FIFO size is AT91C_USB_EP_IN_SIZE. Make sure this function is not called + * over AT91C_USB_EP_IN_SIZE times between each async_usb_write_requestWrite(). + *---------------------------------------------------------------------------- +*/ +inline void async_usb_write_pushByte(uint8_t data) { + pUdp->UDP_FDR[AT91C_EP_IN] = data; + isAsyncRequestFinished = false; +} + +/* + *---------------------------------------------------------------------------- + * \fn async_usb_write_requestWrite + * \brief Request a write operation (time-critical) + * \return false if the last write request is not finished, true if success + * + * This function requests a write operation from FIFO to the USB bus, + * and switch the internal banks of FIFO. It doesn't wait for the end of + * transmission from FIFO to the USB bus. + * + * Note: This function doesn't check if the usb is valid, as it is + * time-critical. + *---------------------------------------------------------------------------- +*/ +inline bool async_usb_write_requestWrite(void) { + + // check if last request is finished + if (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) { + return false; + } + + // clear transmission completed flag + UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP); + while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP) {}; + + // start of transmission + UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); + + // hack: no need to wait if UDP_CSR and UDP_FDR are not used immediately. + // while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY)) {}; + isAsyncRequestFinished = true; + return true; +} + +/* + *---------------------------------------------------------------------------- + * \fn async_usb_write_stop + * \brief Stop async write process + * \return PM3_EIO if USB is invalid, PM3_SUCCESS if data is written + * + * This function makes sure the data left in the FIFO is written to the + * USB bus. + * + * Warning: usb_write() should not be called between + * async_usb_write_start() and async_usb_write_stop(). + *---------------------------------------------------------------------------- +*/ +int async_usb_write_stop(void) { + // Wait for the end of transfer + while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) { + if (!usb_check()) return PM3_EIO; + } + + // clear transmission completed flag + UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP); + while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP) {}; + + // FIFO is not empty, request a write in non-ping-pong mode + if (isAsyncRequestFinished == false) { + UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); + + while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) { + if (!usb_check()) return PM3_EIO; + } + + UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP); + while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP) {}; + } + return PM3_SUCCESS; +} + /* *---------------------------------------------------------------------------- * \fn AT91F_USB_SendData @@ -831,7 +958,7 @@ void AT91F_USB_SendData(AT91PS_UDP pudp, const char *pData, uint32_t length) { AT91_REG csr; do { - uint32_t cpt = MIN(length, AT91C_EP_CONTROL_SIZE); + uint32_t cpt = MIN(length, AT91C_USB_EP_CONTROL_SIZE); length -= cpt; while (cpt--) @@ -869,6 +996,8 @@ void AT91F_USB_SendData(AT91PS_UDP pudp, const char *pData, uint32_t length) { //*---------------------------------------------------------------------------- void AT91F_USB_SendZlp(AT91PS_UDP pudp) { UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXPKTRDY); + // for non ping-pong operation, wait until the FIFO is released + // the flag for FIFO released is AT91C_UDP_TXCOMP rather than AT91C_UDP_TXPKTRDY while (!(pudp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP)) {}; UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXCOMP); while (pudp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) {}; diff --git a/common_arm/usb_cdc.h b/common_arm/usb_cdc.h index e7f6f3d09..9c7cb7299 100644 --- a/common_arm/usb_cdc.h +++ b/common_arm/usb_cdc.h @@ -23,13 +23,22 @@ #include "common.h" #include "at91sam7s512.h" +#define AT91C_USB_EP_CONTROL_SIZE 8 +#define AT91C_USB_EP_OUT_SIZE 64 +#define AT91C_USB_EP_IN_SIZE 64 + void usb_disable(void); void usb_enable(void); bool usb_check(void); bool usb_poll(void); +uint16_t usb_available_length(void); bool usb_poll_validate_length(void); uint32_t usb_read(uint8_t *data, size_t len); int usb_write(const uint8_t *data, const size_t len); +int async_usb_write_start(void); +void async_usb_write_pushByte(uint8_t data); +bool async_usb_write_requestWrite(void); +int async_usb_write_stop(void); uint32_t usb_read_ng(uint8_t *data, size_t len); void usb_update_serial(uint64_t newSerialNumber); diff --git a/common_fpga/fpga.h b/common_fpga/fpga.h index b7e017d68..35143ec6f 100644 --- a/common_fpga/fpga.h +++ b/common_fpga/fpga.h @@ -23,9 +23,11 @@ #define FPGA_BITSTREAM_FIXED_HEADER_SIZE sizeof(bitparse_fixed_header) #define FPGA_INTERLEAVE_SIZE 288 #if defined XC3 -#define FPGA_CONFIG_SIZE 72864L // our current fpga_[lh]f.bit files are 72742 bytes. Rounded up to next multiple of FPGA_INTERLEAVE_SIZE +#define FPGA_TYPE "3s100evq100" +#define FPGA_CONFIG_SIZE 72864L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE #else -#define FPGA_CONFIG_SIZE 42336L // our current fpga_[lh]f.bit files are 42175 bytes. Rounded up to next multiple of FPGA_INTERLEAVE_SIZE +#define FPGA_TYPE "2s30vq100" +#define FPGA_CONFIG_SIZE 42336L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE #endif #define FPGA_RING_BUFFER_BYTES (1024 * 30) #define FPGA_TRACE_SIZE 3072 diff --git a/covsubmit.sh b/covsubmit.sh index 61ca853f1..579caf61d 100755 --- a/covsubmit.sh +++ b/covsubmit.sh @@ -5,6 +5,37 @@ set -e pre_submit_hook +echo "Checking upload permissions..." + +if ! up_perm="$(wget https://scan.coverity.com/api/upload_permitted --post-data "token=${COVTOKEN}&project=${COVPROJECT}" -q -O -)"; then + echo "Coverity Scan API access denied: bad token?" + exit 1 +fi + +# Really up_perm is a JSON response with either +# {upload_permitted:true} or {next_upload_permitted_at:} +# We do some hacky string parsing instead of properly parsing it. +case "$up_perm" in + *upload_permitted*true*) + echo "Coverity Scan: upload permitted" + ;; + *next_upload_permitted_at*) + if [ -z "$COVERITY_DRYRUN" ]; then + echo "Coverity Scan: upload quota reached; stopping here" + # Exit success as this isn't a build error. + exit 0 + else + echo "Coverity Scan: upload quota reached, continuing dry run" + fi + ;; + *) + echo "Coverity Scan upload check: unexpected result $up_perm" + exit 1 + ;; +esac + + + ## delete all previous tarballs rm -f proxmark3.all.*.tgz proxmark3.all.*.log @@ -25,7 +56,7 @@ curl --progress-bar --fail \ --form file="@$FILENAME" \ --form version="$VERSION" \ --form description="$DESCNAME" \ - https://scan.coverity.com/builds?project=Proxmark3+RRG+Iceman+repo | tee -a "${LOGFILENAME}" ; test "${PIPESTATUS[0]}" -eq 0 || exit $? + https://scan.coverity.com/builds?project="${COVPROJECT}" | tee -a "${LOGFILENAME}" ; test "${PIPESTATUS[0]}" -eq 0 || exit $? echo "tarball uploaded to Coverity for analyse" post_submit_hook diff --git a/doc/T5577_Guide.md b/doc/T5577_Guide.md index 6c4eceaf5..ded308676 100644 --- a/doc/T5577_Guide.md +++ b/doc/T5577_Guide.md @@ -142,7 +142,7 @@ developers have done a great job and gave us commands. What we need to know is that with the T5577, data is read/written one complete block at a time. Each block holds 32 bits of data (hence the binary output shown) -Since we know that the card has data and configuration blocks, lets say +Since we know that the card has data and configuration blocks, lets stay away from those while we learn how to read and write. I suggest you follow along and perform each command and check the results as we go. diff --git a/doc/cheatsheet.md b/doc/cheatsheet.md index 22e47b3f1..0dd86c2a0 100644 --- a/doc/cheatsheet.md +++ b/doc/cheatsheet.md @@ -79,7 +79,7 @@ Read iCLASS Block Options --- -k, --key Access key as 16 hex symbols --b, --block The block number to read as an integer + --blk The block number to read as an integer --ki Key index to select key from memory 'hf iclass managekeys' --credit key is assumed to be the credit key --elite elite computations applied to key @@ -94,7 +94,7 @@ Write to iCLASS Block Options --- -k, --key Access key as 16 hex symbols --b, --block The block number to read as an integer + --blk The block number to read as an integer -d, --data data to write as 16 hex symbols --ki Key index to select key from memory 'hf iclass managekeys' --credit key is assumed to be the credit key @@ -140,7 +140,7 @@ Decrypt iCLASS Block / file ``` Options --- --f, --file filename of dumpfile +-f, --file Specify a filename for dump file -d, --data 3DES encrypted data -k, --key 3DES transport key -v, --verbose verbose output @@ -153,7 +153,7 @@ Load iCLASS dump into memory for simulation ``` Options --- --f, --file filename of dump +-f, --file Specify a filename for dump file --json load JSON type dump --eml load EML type dump @@ -261,8 +261,8 @@ Dump MIFARE Classic card contents ``` Options: --- --f, --file filename of dump --k, --keys filename of keys +-f, --file Specify a filename for dump file +-k, --keys Specify a filename for keys file --mini MIFARE Classic Mini / S20 --1k MIFARE Classic 1k / S50 (default) --2k MIFARE Classic/Plus 2k @@ -335,7 +335,7 @@ Accepts (BIN/EML/JSON) ``` Options --- --f, --file filename of dump +-f, --file Specify a filename for dump file --mini MIFARE Classic Mini / S20 --1k MIFARE Classic 1k / S50 (def) --2k MIFARE Classic/Plus 2k @@ -514,24 +514,22 @@ Read Hitag information pm3 --> lf hitag info ``` -Act as Hitag reader +Read Hitag memory +Crypto mode key format: ISK high + ISK low ``` Options --- - --01 HitagS, read all pages, challenge mode - --02 HitagS, read all pages, crypto mode. Set key=0 for no auth - - --21 Hitag2, read all pages, password mode. def 4D494B52 (MIKR) - --22 Hitag2, read all pages, challenge mode - --23 Hitag2, read all pages, crypto mode. Key ISK high + ISK low. def 4F4E4D494B52 (ONMIKR) - --25 Hitag2, test recorded authentications (replay?) - --26 Hitag2, read UID --k, --key key, 4 or 6 hex bytes - --nrar nonce / answer reader, 8 hex bytes + -h, --help This help + -s, --hts Hitag S + -2, --ht2 Hitag 2 + --pwd password mode + --nrar nonce / answer writer, 8 hex bytes + --crypto crypto mode + -k, --key key, 4 or 6 hex bytes -pm3 --> lf hitag --26 -pm3 --> lf hitag --21 -k 4D494B52 -pm3 --> lf hitag reader --23 -k 4F4E4D494B52 +pm3 --> lf hitag read --ht2 +pm3 --> lf hitag read --ht2 -k 4D494B52 +pm3 --> lf hitag read --ht2 -k 4F4E4D494B52 ``` Sniff Hitag traffic @@ -545,26 +543,27 @@ Simulate Hitag2 pm3 --> lf hitag sim -2 ``` -Write to Hitag block +Write a page in Hitag memory +Crypto mode key format: ISK high + ISK low ``` Options --- - --03 HitagS, write page, challenge mode - --04 HitagS, write page, crypto mode. Set key=0 for no auth + -h, --help This help + -s, --hts Hitag S + -2, --ht2 Hitag 2 + --pwd password mode + --nrar nonce / answer writer, 8 hex bytes + --crypto crypto mode + -k, --key key, 4 or 6 hex bytes + -p, --page page address to write to + -d, --data data, 4 hex bytes - --24 Hitag2, write page, crypto mode. - --27 Hitag2, write page, password mode --p, --page page address to write to --d, --data data, 4 hex bytes --k, --key key, 4 or 6 hex bytes - --nrar nonce / answer writer, 8 hex bytes - -pm3 --> lf hitag writer --24 -k 499602D2 -p 1 -d 00000000 +pm3 --> lf hitag wrbl --ht2 -k 499602D2 -p 1 -d 00000000 ``` Simulate Hitag2 sequence ``` -pm3 --> lf hitag reader --21 -k 56713368 +pm3 --> lf hitag read --ht2 -k 56713368 pm3 --> lf hitag sim -2 ``` @@ -713,7 +712,7 @@ pm3 --> mem load -f iclass_default_keys --iclass Upgrade Sim Module firmware ``` -pm3 --> smart upgrade -f sim013.bin +pm3 --> smart upgrade -f sim014.bin ``` ## Smart Card diff --git a/doc/clocks.md b/doc/clocks.md index 161d680fb..9e0384d23 100644 --- a/doc/clocks.md +++ b/doc/clocks.md @@ -7,6 +7,7 @@ The device side firmware uses a range of different clocks. Here is an attempt t # Table of Contents - [Notes on device side clocks](#notes-on-device-side-clocks) - [Table of Contents](#table-of-contents) + - [Units](#units) - [Slow clock](#slow-clock) - [Main Oscillator / MAINCK](#main-oscillator--mainck) - [PLL clock](#pll-clock) @@ -15,9 +16,48 @@ The device side firmware uses a range of different clocks. Here is an attempt t - [1 kHz RTC: TickCount functions](#1-khz-rtc-tickcount-functions) - [Occasional PWM timer](#occasional-pwm-timer) - [Occasional TC0+TC1 / CountUS functions](#occasional-tc0tc1--countus-functions) - - [Occasional TC0+TC1+TC2 SSP_CLK from FPGA / CountSspClk functions](#occasional-tc0tc1tc2-ssp_clk-from-fpga--countsspclk-functions) + - [Occasional TC0+TC1+TC2 SSP\_CLK from FPGA / CountSspClk functions](#occasional-tc0tc1tc2-ssp_clk-from-fpga--countsspclk-functions) - [Occasional TC0+TC1 / Ticks functions](#occasional-tc0tc1--ticks-functions) + +## Units +^[Top](#top) + +Good calculator +https://www.unitjuggler.com/convert-frequency-from-MHz-to-ns(p).html?val=3.39 + +Basic units of time measurment or how long in time is a Hertz? + +``` +1 hertz = 1 second = 1000 milli seconds +10 Hertz = 1 / 10 = 0,1 = 100 milli seconds +100 Hertz = 1 / 100 = 0,01 = 10 milli seconds +1 kHz = 1 / 1000 = 0,001 = 1 milli seconds +10 kHz = 1 / 10 000 = 0,000 1 = 100 micro seconds +100 kHz = 1 / 100 000 = 0,000 01 = 10 micro seconds +1 MHZ = 1 / 1 000 000 = 0,000 001 = 1 micro seconds +``` + +- kHz, Kilo Hertz, 1000 Hertz +- MHz, Mega Hertz, 1000 000 Hertz + + +Basic units of time you will run into in the RFID world. + +``` +13.56 MHz = 1 / 13 560 000 = 73,74 nano seconds = 0,07374 micro seconds +125 kHz = 1/ 125 000 = 8 micro seconds +``` + +Given these units the following clocks used by Proxmark3 will make more sense. + +Like the SSP Clock running at 3.39 MHz. +3.39 MHz = 1 / 3 390 000 = 294,98 nano seconds = 0,2949 micro seconds + +1 tick at 3.39 MHz is 294.98 nano seconds. + + + ## Slow clock ^[Top](#top) diff --git a/doc/colors_notes.md b/doc/colors_notes.md index dac993585..06df0d224 100644 --- a/doc/colors_notes.md +++ b/doc/colors_notes.md @@ -16,7 +16,7 @@ The client should autodetect color support when starting. You can also use the command `pref show` to see and set your personal setting. -Why use colors in the Proxmark client? When everything is white it is hard to extract the important information fast. You also need new-lines for extra space to be easier to read. +Why use colors in the Proxmark3 client? When everything is white it is hard to extract the important information fast. You also need new-lines for extra space to be easier to read. We have gradually been introducing this color scheme into the client since we got decent color support on all systems: OSX, Linux, WSL, Proxspace. @@ -43,7 +43,6 @@ The following definition has be crystallized out from these experiments. Its no ``` PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); ``` For more examples, see also all **-h** helptext now in the LUA scripts. For the command help texts using _YELLOW_ for the example makes it very easy to see what is the command vs the description. diff --git a/doc/commands.json b/doc/commands.json index f8712fc47..a4c474801 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -95,14 +95,6 @@ ], "usage": "analyse freq [-h] [-F ] [-L ] [-C ]" }, - "analyse help": { - "command": "analyse help", - "description": "help This help lcr Generate final byte for XOR LRC crc Stub method for CRC evaluations chksum Checksum with adding, masking and one's complement dates Look for datestamps in a given array of bytes lfsr LFSR tests a num bits test nuid create NUID from 7byte UID demodbuff Load binary string to DemodBuffer freq Calc wave lengths foo muxer units convert ETU <> US <> SSP_CLK (3.39MHz)", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "analyse lcr": { "command": "analyse lcr", "description": "Specifying the bytes of a UID with a known LRC will find the last byte value needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX.", @@ -148,15 +140,17 @@ "command": "analyse units", "description": "experiments of unit conversions found in HF. ETU (1/13.56mhz), US or SSP_CLK (1/3.39MHz)", "notes": [ - "analyse uints --etu 10analyse uints --us 100" + "analyse uints --etu 10", + "analyse uints --us 100" ], "offline": true, "options": [ "-h, --help This help", "--etu number in ETU", - "--us number in micro seconds (us)" + "--us number in micro seconds (us)", + "-t, --selftest self tests" ], - "usage": "analyse units [-h] [--etu ] [--us ]" + "usage": "analyse units [-ht] [--etu ] [--us ]" }, "auto": { "command": "auto", @@ -205,11 +199,25 @@ "data asn1 -d 303381050186922305a5020500a6088101010403030008a7188516eeee4facacf4fbde5e5c49d95e55bfbca74267b02407a9020500" ], "offline": true, + "options": [ + "-h, --help This help", + "-d ASN1 encoded byte array", + "-t, --test perform selftest" + ], + "usage": "data asn1 [-ht] [-d ]" + }, + "data atr": { + "command": "data atr", + "description": "look up ATR record from bytearray", + "notes": [ + "data atr -d 3B6B00000031C064BE1B0100079000" + ], + "offline": true, "options": [ "-h, --help This help", "-d ASN1 encoded byte array" ], - "usage": "data asn1 [-h] -d " + "usage": "data atr [-h] [-d ]" }, "data autocorr": { "command": "data autocorr", @@ -267,6 +275,21 @@ ], "usage": "data bitsamples [-h]" }, + "data bmap": { + "command": "data bmap", + "description": "Breaks down a hex value to binary according a template data bmap -d 16 -m 4,4 This will give two rows each with four bits", + "notes": [ + "data bmap -d 3B", + "data bmap -d 3B -m 2,5,1" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-d hex string", + "-m binary template" + ], + "usage": "data bmap [-h] [-d ] [-m ]" + }, "data clear": { "command": "data clear", "description": "This function clears the bigbuff on deviceside and graph window", @@ -291,6 +314,20 @@ ], "usage": "data convertbitstream [-h]" }, + "data cthreshold": { + "command": "data cthreshold", + "description": "Inverse of dirty threshold command, all values between up and down will be average out", + "notes": [ + "data cthreshold -u 10 -d -10" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-d, --down threshold down", + "-u, --up threshold up" + ], + "usage": "data cthreshold [-h] -d -u " + }, "data decimate": { "command": "data decimate", "description": "Performs decimation, by reducing samples N times in the grapbuf. Good for PSK", @@ -309,20 +346,18 @@ "command": "data detectclock", "description": "Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer", "notes": [ - "data detectclock -A -> detect clock of an ask wave in GraphBuffer", - "data detectclock -F -> detect clock of an fsk wave in GraphBuffer", - "data detectclock -N -> detect clock of an psk wave in GraphBuffer", - "data detectclock -P -> detect clock of an nrz/direct wave in GraphBuffer" + "data detectclock --ask", + "data detectclock --nzr -> detect clock of an nrz/direct wave in GraphBuffer" ], "offline": true, "options": [ "-h, --help This help", - "-A, --ASK specify ASK modulation clock detection", - "-F, --FSK specify FSK modulation clock detection", - "-N, --NZR specify NZR/DIRECT modulation clock detection", - "-P, --PSK specify PSK modulation clock detection" + "--ask specify ASK modulation clock detection", + "--fsk specify FSK modulation clock detection", + "--nzr specify NZR/DIRECT modulation clock detection", + "--psk specify PSK modulation clock detection" ], - "usage": "data detectclock [-hAFNP]" + "usage": "data detectclock [-h] [--ask] [--fsk] [--nzr] [--psk]" }, "data diff": { "command": "data diff", @@ -332,8 +367,7 @@ "data diff -a fileA -b fileB", "data diff -a fileA --eb", "data diff --fa fileA -b fileB", - "data diff --fa fileA --fb fileB", - "data diff --ea --cb" + "data diff --fa fileA --fb fileB" ], "offline": true, "options": [ @@ -361,6 +395,18 @@ ], "usage": "data dirthreshold [-h] -d -u " }, + "data envelope": { + "command": "data envelope", + "description": "Create an square envelop of the samples", + "notes": [ + "data envelop" + ], + "offline": true, + "options": [ + "-h, --help This help" + ], + "usage": "data envelop [-h]" + }, "data fsktonrz": { "command": "data fsktonrz", "description": "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk) Omitted values are autodetect instead", @@ -404,14 +450,6 @@ ], "usage": "data grid [-h] [-x ] [-y ]" }, - "data help": { - "command": "data help", - "description": "help This help ----------- ------------------------- Modulation------------------------- biphaserawdecode Biphase decode bin stream in DemodBuffer detectclock Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer fsktonrz Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk) manrawdecode Manchester decode binary stream in DemodBuffer modulation Identify LF signal for clock and modulation rawdemod Demodulate the data in the GraphBuffer and output binary ----------- ------------------------- Graph------------------------- askedgedetect Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave autocorr Autocorrelation over window dirthreshold Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev. decimate Decimate samples undecimate Un-decimate samples hide Hide graph window hpf Remove DC offset from trace iir Apply IIR buttersworth filter on plot data grid overlay grid on graph window ltrim Trim samples from left of trace mtrim Trim out samples from the specified start to the specified stop norm Normalize max/min to +/-128 plot Show graph window rtrim Trim samples from right of trace setgraphmarkers Set blue and orange marker in graph window shiftgraphzero Shift 0 for Graphed wave + or - shift value timescale Set a timescale to get a differential reading between the yellow and purple markers as time duration zerocrossings Count time between zero-crossings convertbitstream Convert GraphBuffer's 0/1 values to 127 / -127 getbitstream Convert GraphBuffer's >=1 values to 1 and <1 to 0 ----------- ------------------------- General------------------------- asn1 asn1 decoder bin2hex Converts binary to hexadecimal clear Clears bigbuf on deviceside and graph window diff diff of input files hex2bin Converts hexadecimal to binary load Load contents of file into graph window num Converts dec/hex/bin print Print the data in the DemodBuffer save Save signal trace data (from graph window) setdebugmode Set Debugging Level on client side", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "data hex2bin": { "command": "data hex2bin", "description": "This function converts hexadecimal to binary. It will ignore all non-hexadecimal characters but stop reading on whitespace", @@ -570,9 +608,10 @@ "--dec decimal value", "--hex hexadecimal value", "--bin binary value", - "-i print inverted value" + "-i print inverted value", + "-r print reversed value" ], - "usage": "data num [-hi] [--dec ] [--hex ] [--bin ]" + "usage": "data num [-hir] [--dec ] [--hex ] [--bin ]" }, "data plot": { "command": "data plot", @@ -779,9 +818,9 @@ "offline": false, "options": [ "-h, --help This help", - "-k, -K, --keep Keep field ON for next command", - "-a, -A, --apdu Show APDU requests and responses", - "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" + "-k, --keep Keep field ON for next command", + "-a, --apdu Show APDU requests and responses", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" ], "usage": "emv challenge [-hkaw]" }, @@ -795,19 +834,19 @@ "offline": false, "options": [ "-h, --help This help", - "-s, -S, --select Activate field and select card", - "-a, -A, --apdu Show APDU requests and responses", - "-t, -T, --tlv TLV decode results", - "-j, -J, --jload Load transaction parameters from `emv_defparams.json` file", - "-f, -F, --forceaid Force search AID. Search AID instead of execute PPSE", + "-s, --select Activate field and select card", + "-a, --apdu Show APDU requests and responses", + "-t, --tlv TLV decode results", + "-j, --jload Load transaction parameters from `emv_defparams.json` file", + "--force Force search AID. Search AID instead of execute PPSE", "By default: Transaction type - MSD", - "-v, -V, --qvsdc Transaction type - qVSDC or M/Chip", - "-c, -C, --qvsdccda Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)", - "-x, -X, --vsdc Transaction type - VSDC. For test only. Not a standard behavior", - "-g, -G, --acgpo VISA. generate AC from GPO", - "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" + "-v, --qvsdc Transaction type - qVSDC or M/Chip", + "-c, --qvsdccda Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)", + "-x, --vsdc Transaction type - VSDC. For test only. Not a standard behavior", + "-g, --acgpo VISA. generate AC from GPO", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" ], - "usage": "emv exec [-hsatjfvcxgw] By default:" + "usage": "emv exec [-hsatjvcxgw] [--force] By default:" }, "emv genac": { "command": "emv genac", @@ -821,14 +860,14 @@ "offline": false, "options": [ "-h, --help This help", - "-k, -K, --keep Keep field ON for next command", - "-c, -C, --cda Executes CDA transaction. Needs to get SDAD in results.", - "-d, -D, --decision Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested", - "-p, -P, --params Load parameters from `emv_defparams.json` file for CDOLdata making from CDOL and parameters", - "-m, -M, --make Make CDOLdata from CDOL (tag 8C and 8D) and parameters (def: use default parameters)", - "-a, -A, --apdu Show APDU requests and responses", - "-t, -T, --tlv TLV decode results of selected applets", - "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", + "-k, --keep Keep field ON for next command", + "-c, --cda Executes CDA transaction. Needs to get SDAD in results.", + "-d, --decision Terminal decision. aac - declined, tc - approved, arqc - online authorisation requested", + "-p, --params Load parameters from `emv_defparams.json` file for CDOLdata making from CDOL and parameters", + "-m, --make Make CDOLdata from CDOL (tag 8C and 8D) and parameters (def: use default parameters)", + "-a, --apdu Show APDU requests and responses", + "-t, --tlv TLV decode results of selected applets", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", " CDOLdata/CDOL" ], "usage": "emv genac [-hkcpmatw] [-d ] []..." @@ -844,24 +883,16 @@ "offline": false, "options": [ "-h, --help This help", - "-k, -K, --keep Keep field ON for next command", - "-p, -P, --params Load parameters from `emv_defparams.json` file for PDOLdata making from PDOL and parameters", - "-m, -M, --make Make PDOLdata from PDOL (tag 9F38) and parameters (def: uses default parameters)", - "-a, -A, --apdu Show APDU requests and responses", - "-t, -T, --tlv TLV decode results of selected applets", - "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", + "-k, --keep Keep field ON for next command", + "-p, --params Load parameters from `emv_defparams.json` file for PDOLdata making from PDOL and parameters", + "-m, --make Make PDOLdata from PDOL (tag 9F38) and parameters (def: uses default parameters)", + "-a, --apdu Show APDU requests and responses", + "-t, --tlv TLV decode results of selected applets", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", " PDOLdata/PDOL" ], "usage": "emv gpo [-hkpmatw] []..." }, - "emv help": { - "command": "emv help", - "description": "help This help test Crypto logic test list List ISO7816 history", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "emv intauth": { "command": "emv intauth", "description": "Generate Internal Authenticate command. Usually needs 4-byte random number. It returns data in TLV format . Needs a EMV applet to be selected and GPO to be executed.", @@ -873,12 +904,12 @@ "offline": false, "options": [ "-h, --help This help", - "-k, -K, --keep Keep field ON for next command", - "-p, -P, --params Load parameters from `emv_defparams.json` file for DDOLdata making from DDOL and parameters", - "-m, -M, --make Make DDOLdata from DDOL (tag 9F49) and parameters (def: use default parameters)", - "-a, -A, --apdu Show APDU requests and responses", - "-t, -T, --tlv TLV decode results of selected applets", - "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", + "-k, --keep Keep field ON for next command", + "-p, --params Load parameters from `emv_defparams.json` file for DDOLdata making from DDOL and parameters", + "-m, --make Make DDOLdata from DDOL (tag 9F49) and parameters (def: use default parameters)", + "-a, --apdu Show APDU requests and responses", + "-t, --tlv TLV decode results of selected applets", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", " DDOLdata/DDOL" ], "usage": "emv intauth [-hkpmatw] []..." @@ -914,16 +945,33 @@ "offline": false, "options": [ "-h, --help This help", - "-s, -S, --select Activate field and select card", - "-k, -K, --keep Keep field ON for next command", + "-s, --select Activate field and select card", + "-k, --keep Keep field ON for next command", "-1, --pse PSE (1PAY.SYS.DDF01) mode", "-2, --ppse PPSE (2PAY.SYS.DDF01) mode (def)", - "-a, -A, --apdu Show APDU requests and responses", - "-t, -T, --tlv TLV decode results of selected applets", - "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" + "-a, --apdu Show APDU requests and responses", + "-t, --tlv TLV decode results of selected applets", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" ], "usage": "emv pse [-hsk12atw]" }, + "emv reader": { + "command": "emv reader", + "description": "Act as a EMV reader to identify tag. Look for EMV tags until Enter or the pm3 button is pressed In `verbose` mode it will also try to extract and decode the transaction logs stored on card in either channel.", + "notes": [ + "emv reader", + "emv reader -v", + "emv reader -@ -> Continuous mode" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", + "-v, --verbose verbose", + "-@ continuous reader mode" + ], + "usage": "emv reader [-hwv@]" + }, "emv readrec": { "command": "emv readrec", "description": "Executes Read Record command. It returns data in TLV format. Needs a bank applet to be selected and sometimes needs GPO to be executed.", @@ -934,10 +982,10 @@ "offline": false, "options": [ "-h, --help This help", - "-k, -K, --keep Keep field ON for next command", - "-a, -A, --apdu Show APDU requests and responses", - "-t, -T, --tlv TLV decode results of selected applets", - "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", + "-k, --keep Keep field ON for next command", + "-a, --apdu Show APDU requests and responses", + "-t, --tlv TLV decode results of selected applets", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", " []..." @@ -952,9 +1000,9 @@ "offline": false, "options": [ "-h, --help This help", - "-t, -T, --selftest Self test", - "-a, -A, --apdu Show APDU requests and responses", - "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" + "-t, --selftest Self test", + "-a, --apdu Show APDU requests and responses", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" ], "usage": "emv roca [-htaw]" }, @@ -968,18 +1016,18 @@ "offline": false, "options": [ "-h, --help This help", - "-a, -A, --apdu Show APDU requests and responses", - "-t, -T, --tlv TLV decode results", - "-e, -E, --extract Extract TLV elements and fill Application Data", - "-j, -J, --jload Load transaction parameters from `emv_defparams.json` file", + "-a, --apdu Show APDU requests and responses", + "-t, --tlv TLV decode results", + "-e, --extract Extract TLV elements and fill Application Data", + "-j, --jload Load transaction parameters from `emv_defparams.json` file", "By default: Transaction type - MSD", - "-v, -V, --qvsdc Transaction type - qVSDC or M/Chip", - "-c, -C, --qvsdccda Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)", - "-x, -X, --vsdc Transaction type - VSDC. For test only. Not a standard behavior", - "-g, -G, --acgpo VISA. generate AC from GPO", - "-m, -M, --merge Merge output file with card's data. (warning: the file may be corrupted!)", - "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", - " JSON output filename" + "-v, --qvsdc Transaction type - qVSDC or M/Chip", + "-c, --qvsdccda Transaction type - qVSDC or M/Chip plus CDA (SDAD generation)", + "-x, --vsdc Transaction type - VSDC. For test only. Not a standard behavior", + "-g, --acgpo VISA. generate AC from GPO", + "-m, --merge Merge output file with card's data. (warning: the file may be corrupted!)", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", + " JSON output file name" ], "usage": "emv scan [-hatejvcxgmw] By default: " }, @@ -993,11 +1041,11 @@ "offline": false, "options": [ "-h, --help This help", - "-s, -S, --select Activate field and select card", - "-k, -K, --keep Keep field ON for next command", - "-a, -A, --apdu Show APDU requests and responses", - "-t, -T, --tlv TLV decode results of selected applets", - "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" + "-s, --select Activate field and select card", + "-k, --keep Keep field ON for next command", + "-a, --apdu Show APDU requests and responses", + "-t, --tlv TLV decode results of selected applets", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)" ], "usage": "emv search [-hskatw]" }, @@ -1011,11 +1059,11 @@ "offline": false, "options": [ "-h, --help This help", - "-s, -S, --select Activate field and select card", - "-k, -K, --keep Keep field for next command", - "-a, -A, --apdu Show APDU requests and responses", - "-t, -T, --tlv TLV decode results", - "-w, -W, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", + "-s, --select Activate field and select card", + "-k, --keep Keep field for next command", + "-a, --apdu Show APDU requests and responses", + "-t, --tlv TLV decode results", + "-w, --wired Send data via contact (iso7816) interface. (def: Contactless interface)", " Applet AID" ], "usage": "emv select [-hskatw] " @@ -1047,14 +1095,6 @@ ], "usage": "quit [-h]" }, - "help": { - "command": "help", - "description": "help Use ` help` for details of a command prefs { Edit client/device preferences... } -------- ----------------------- Technology ----------------------- analyse { Analyse utils... } data { Plot window / data buffer manipulation... } emv { EMV ISO-14443 / ISO-7816... } hf { High frequency commands... } hw { Hardware commands... } lf { Low frequency commands... } nfc { NFC commands... } piv { PIV commands... } reveng { CRC calculations from RevEng software... } smart { Smart card ISO-7816 commands... } script { Scripting commands... } trace { Trace manipulation... } wiegand { Wiegand format manipulation... } -------- ----------------------- General ----------------------- clear Clear screen hints Turn hints on / off msleep Add a pause in milliseconds rem Add a text line in log file quit exit Exit program", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf 14a antifuzz": { "command": "hf 14a antifuzz", "description": "Tries to fuzz the ISO14443a anticollision phase", @@ -1131,21 +1171,18 @@ ], "usage": "hf 14a chaining [-h10]" }, - "hf 14a config": { - "command": "hf 14a config", - "description": "--------------------------------------------------------------------------------------- hf 14a cuids available offline: no", - "notes": [], + "hf 14a cuids": { + "command": "hf 14a cuids", + "description": "Collect n>0 ISO14443-a UIDs in one go", + "notes": [ + "hf 14a cuids -n 5 -> Collect 5 UIDs" + ], "offline": false, - "options": [], - "usage": "" - }, - "hf 14a help": { - "command": "hf 14a help", - "description": "----------- ----------------------- General ----------------------- help This help list List ISO 14443-a history", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-n, --num Number of UIDs to collect" + ], + "usage": "hf 14a cuids [-h] [-n ]" }, "hf 14a info": { "command": "hf 14a info", @@ -1164,7 +1201,7 @@ }, "hf 14a list": { "command": "hf 14a list", - "description": "Alias of `trace list -t 14a` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "Alias of `trace list -t 14a -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf 14a list --frame -> show frame delay times", "hf 14a list -1 -> use trace buffer" @@ -1291,19 +1328,21 @@ "hf 14a sim -t 7 -> MFU EV1 / NTAG 215 Amiibo", "hf 14a sim -t 8 -> MIFARE Classic 4k", "hf 14a sim -t 9 -> FM11RF005SH Shanghai Metro", - "hf 14a sim -t 10 -> ST25TA IKEA Rothult" + "hf 14a sim -t 10 -> ST25TA IKEA Rothult", + "hf 14a sim -t 11 -> Javacard (JCOP)", + "hf 14a sim -t 12 -> 4K Seos card" ], "offline": false, "options": [ "-h, --help This help", - "-t, --type <1-10> Simulation type to use", + "-t, --type <1-12> Simulation type to use", "-u, --uid <4|7|10> hex bytes UID", "-n, --num Exit simulation after blocks have been read by reader. 0 = infinite", "-x Performs the 'reader attack', nr/ar attack against a reader", "--sk Fill simulator keys from found keys", "-v, --verbose verbose output" ], - "usage": "hf 14a sim [-hxv] -t <1-10> [-u ] [-n ] [--sk]" + "usage": "hf 14a sim [-hxv] -t <1-12> [-u ] [-n ] [--sk]" }, "hf 14a sniff": { "command": "hf 14a sniff", @@ -1354,17 +1393,10 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file (optional) filename, if no UID will be used as filename" + "-f, --file (optional) filename, if no UID will be used as filename", + "--ns no save to file" ], - "usage": "hf 14b dump [-h] [-f ]" - }, - "hf 14b help": { - "command": "hf 14b help", - "description": "help This help list List ISO-14443-B history view Display content from tag dump file", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "usage": "hf 14b dump [-h] [-f ] [--ns]" }, "hf 14b info": { "command": "hf 14b info", @@ -1522,7 +1554,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "-v, --verbose verbose output" ], "usage": "hf 14b view [-hv] -f " @@ -1554,7 +1586,7 @@ }, "hf 15 dump": { "command": "hf 15 dump", - "description": "This command dumps the contents of a ISO-15693 tag and save it to file", + "description": "This command dumps the contents of a ISO-15693 tag and save to file (bin/json)", "notes": [ "hf 15 dump", "hf 15 dump -*", @@ -1568,7 +1600,7 @@ "-* scan for tag", "-2 use slower '1 out of 256' mode", "-o, --opt set OPTION Flag (needed for TI)", - "-f, --file filename of dump" + "-f, --file Specify a filename for dump file" ], "usage": "hf 15 dump [-h*2o] [-u ] [--ua] [-f ]" }, @@ -1587,18 +1619,18 @@ }, "hf 15 esave": { "command": "hf 15 esave", - "description": "Save emulator memory into three files (BIN/EML/JSON)", + "description": "Save emulator memory into two files (bin/json)", "notes": [ "hf 15 esave -f hf-15-01020304hf 15 esave -b 8 -c 42 -f hf-15-01020304" ], "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", - "-b, --blocksize block size, defaults to 4", + "-f, --file Specify a filename for dump file", + "--bsize block size, defaults to 4", "-c, --count number of blocks to export, defaults to all" ], - "usage": "hf 15 esave [-h] -f [-b ] [-c ]" + "usage": "hf 15 esave [-h] -f [--bsize ] [-c ]" }, "hf 15 eview": { "command": "hf 15 eview", @@ -1627,14 +1659,6 @@ ], "usage": "hf 15 findafi [-h]" }, - "hf 15 help": { - "command": "hf 15 help", - "description": "----------- --------------------- General --------------------- help This help list List ISO-15693 history demod Demodulate ISO-15693 from tag", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf 15 info": { "command": "hf 15 info", "description": "Uses the optional command `get_systeminfo` 0x2B to try and extract information", @@ -1776,7 +1800,7 @@ }, "hf 15 restore": { "command": "hf 15 restore", - "description": "This command restore the contents of a dump file onto a ISO-15693 tag", + "description": "This command restore the contents of a dump file (bin/eml/json) onto a ISO-15693 tag", "notes": [ "hf 15 restore", "hf 15 restore -*", @@ -1790,7 +1814,7 @@ "-* scan for tag", "-2 use slower '1 out of 256' mode", "-o, --opt set OPTION Flag (needed for TI)", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "-r, --retry number of retries (def 3)", "--bs block size (def 4)", "-v, --verbose verbose output" @@ -1902,6 +1926,19 @@ ], "usage": "hf 15 sniff [-h]" }, + "hf 15 view": { + "command": "hf 15 view", + "description": "Print a ISO-15693 tag dump file (bin/eml/json)", + "notes": [ + "hf 15 view -f hf-iclass-AA162D30F8FF12F1-dump.bin" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-f, --file Specify a filename for dump file" + ], + "usage": "hf 15 view [-h] -f " + }, "hf 15 wrbl": { "command": "hf 15 wrbl", "description": "Write block on ISO-15693 tag", @@ -2125,14 +2162,6 @@ ], "usage": "hf cipurse formatall [-hav] [-n ] [-k ] [--sreq ] [--sresp ] [--no-auth]" }, - "hf cipurse help": { - "command": "hf cipurse help", - "description": "help This help. test Regression tests", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf cipurse info": { "command": "hf cipurse info", "description": "Get info from CIPURSE tags", @@ -2291,41 +2320,33 @@ "notes": [ "hf emrtd dump", "hf emrtd dump --dir ../dump", - "hf emrtd dump -n 123456789 -d 19890101 -e 20250401" + "hf emrtd dump -n 123456789 -d 890101 -e 250401" ], "offline": false, "options": [ "-h, --help This help", - "-n, --documentnumber document number, up to 9 chars", - "-d, --dateofbirth date of birth in YYMMDD format", + "-n, --doc document number, up to 9 chars", + "-d, --date date of birth in YYMMDD format", "-e, --expiry expiry in YYMMDD format", "-m, --mrz <[0-9A-Z<]> 2nd line of MRZ, 44 chars", "--dir save dump to the given dirpath" ], "usage": "hf emrtd dump [-h] [-n ] [-d ] [-e ] [-m <[0-9A-Z<]>] [--dir ]" }, - "hf emrtd help": { - "command": "hf emrtd help", - "description": "help This help info Display info about an eMRTD list List ISO 14443A/7816 history", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf emrtd info": { "command": "hf emrtd info", "description": "Display info about an eMRTD", "notes": [ "hf emrtd info", "hf emrtd info --dir ../dumps", - "hf emrtd info -n 123456789 -d 19890101 -e 20250401", - "hf emrtd info -n 123456789 -d 19890101 -e 20250401 -i" + "hf emrtd info -n 123456789 -d 890101 -e 250401", + "hf emrtd info -n 123456789 -d 890101 -e 250401 -i" ], "offline": true, "options": [ "-h, --help This help", - "-n, --documentnumber document number, up to 9 chars", - "-d, --dateofbirth date of birth in YYMMDD format", + "-n, --doc document number, up to 9 chars", + "-d, --date date of birth in YYMMDD format", "-e, --expiry expiry in YYMMDD format", "-m, --mrz <[0-9A-Z<]> 2nd line of MRZ, 44 chars (passports only)", "--dir display info from offline dump stored in dirpath", @@ -2369,14 +2390,6 @@ ], "usage": "hf epa cnonces [-h] --size --num -d " }, - "hf epa help": { - "command": "hf epa help", - "description": "help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf epa replay": { "command": "hf epa replay", "description": "Perform PACE protocol by replaying given APDUs", @@ -2448,14 +2461,6 @@ ], "usage": "hf felica auth2 [-hv] [-i ] [-c ] [-k ]" }, - "hf felica help": { - "command": "hf felica help", - "description": "help This help ----------- ----------------------- General ----------------------- list List ISO 18092/FeliCa history ----------- ----------------------- FeliCa Standard ----------------------- ----------- ----------------------- FeliCa Light -----------------------", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf felica info": { "command": "hf felica info", "description": "Reader for FeliCa based tags", @@ -2536,11 +2541,25 @@ }, "hf felica rdbl": { "command": "hf felica rdbl", - "description": "Use this command to read block data from authentication-not-required Service.", - "notes": [], + "description": "Use this command to read block data from authentication-not-required Service. - Mode shall be Mode0. - Successful == block data - Unsuccessful == Status Flag1 and Flag2", + "notes": [ + "hf felica rdbl --sn 01 --scl 8B00 --bn 01 --ble 8000", + "hf felica rdbl --sn 01 --scl 4B18 --bn 01 --ble 8000 -b", + "hf felica rdbl -i 01100910c11bc407 --sn 01 --scl 8B00 --bn 01 --ble 8000" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-b get all block list elements 00 -> FF", + "-i set custom IDm", + "-l, --long use 3 byte block list element block number", + "--sn number of service", + "--scl service code list", + "--bn number of block", + "--ble block list element (def 2|3 bytes)", + "-v, --verbose verbose output" + ], + "usage": "hf felica rdbl [-hblv] [-i ] [--sn ] [--scl ] [--bn ] [--ble ]" }, "hf felica reader": { "command": "hf felica reader", @@ -2664,11 +2683,23 @@ }, "hf felica wrbl": { "command": "hf felica wrbl", - "description": "Use this command to write block data to authentication-not-required Service.", - "notes": [], + "description": "Use this command to write block data to authentication-not-required Service. - Mode shall be Mode0. - Un-/Ssuccessful == Status Flag1 and Flag2", + "notes": [ + "hf felica wrbl --sn 01 --scl CB10 --bn 01 --ble 8001 -d 0102030405060708090A0B0C0D0E0F10", + "hf felica wrbl -i 01100910c11bc407 --sn 01 --scl CB10 --bn 01 --ble 8001 -d 0102030405060708090A0B0C0D0E0F10" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-d, --data data, 16 hex bytes", + "-i set custom IDm", + "--sn number of service", + "--scl service code list", + "--bn number of block", + "--ble block list element (def 2|3 bytes)", + "-v, --verbose verbose output" + ], + "usage": "hf felica wrbl [-hv] [-d ] [-i ] [--sn ] [--scl ] [--bn ] [--ble ]" }, "hf fido assert": { "command": "hf fido assert", @@ -2695,8 +2726,8 @@ "hf fido auth --kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle", "hf fido auth", "--kh 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", - "--cp 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", - "--ap 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters" + "--cpx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", + "--apx 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters" ], "offline": false, "options": [ @@ -2716,14 +2747,6 @@ ], "usage": "hf fido auth [-havuc] default mode: [-f ] [-k ] [--kh ] [--cp ] [--ap ] [--cpx ] [--apx ]" }, - "hf fido help": { - "command": "hf fido help", - "description": "help This help. list List ISO 14443A history", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf fido info": { "command": "hf fido info", "description": "Get info from Fido tags", @@ -2800,25 +2823,17 @@ }, "hf fudan dump": { "command": "hf fudan dump", - "description": "Dump FUDAN tag to binary file If no given, UID will be used as filename", + "description": "Dump FUDAN tag to file (bin/json) If no given, UID will be used as filename", "notes": [ "hf fudan dump -f mydump -> dump using filename" ], "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump" + "-f, --file Specify a filename for dump file" ], "usage": "hf fudan dump [-h] [-f ]" }, - "hf fudan help": { - "command": "hf fudan help", - "description": "help This help view Display content from tag dump file", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf fudan rdbl": { "command": "hf fudan rdbl", "description": "Read fudan block", @@ -2859,7 +2874,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump" + "-f, --file Specify a filename for dump file" ], "usage": "hf fudan view [-h] -f " }, @@ -2867,7 +2882,7 @@ "command": "hf fudan wrbl", "description": "Write fudan block with 4 hex bytes of data", "notes": [ - "hf mf wrbl --blk 1 -k FFFFFFFFFFFF -d 01020304" + "hf fudan wrbl --blk 1 -k FFFFFFFFFFFF -d 01020304" ], "offline": false, "options": [ @@ -2953,14 +2968,6 @@ ], "usage": "hf gallagher diversify [-h] --aid [--keynum ] [--uid ] [--sitekey ] [--apdu]" }, - "hf gallagher help": { - "command": "hf gallagher help", - "description": "help This help diversifykey Diversify Gallagher key decode Decode Gallagher credential block", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf gallagher reader": { "command": "hf gallagher reader", "description": "Read a Gallagher DESFire tag from the Card Application Directory, CAD Specify site key is required if using non-default key", @@ -2979,14 +2986,6 @@ ], "usage": "hf gallagher reader [-h@v] [--aid ] [--sitekey ] [--apdu]" }, - "hf help": { - "command": "hf help", - "description": "-------- ----------------------- High Frequency ----------------------- 14a { ISO14443A RFIDs... } 14b { ISO14443B RFIDs... } 15 { ISO15693 RFIDs... } cipurse { Cipurse transport Cards... } epa { German Identification Card... } emrtd { Machine Readable Travel Document... } felica { ISO18092 / FeliCa RFIDs... } fido { FIDO and FIDO2 authenticators... } fudan { Fudan RFIDs... } gallagher { Gallagher DESFire RFIDs... } ksx6924 { KS X 6924 (T-Money, Snapper+) RFIDs } jooki { Jooki RFIDs... } iclass { ICLASS RFIDs... } legic { LEGIC RFIDs... } lto { LTO Cartridge Memory RFIDs... } mf { MIFARE RFIDs... } mfp { MIFARE Plus RFIDs... } mfu { MIFARE Ultralight RFIDs... } mfdes { MIFARE Desfire RFIDs... } ntag424 { NXP NTAG 4242 DNA RFIDs... } seos { SEOS RFIDs... } st25ta { ST25TA RFIDs... } tesla { TESLA Cards... } texkom { Texkom RFIDs... } thinfilm { Thinfilm RFIDs... } topaz { TOPAZ (NFC Type 1) RFIDs... } xerox { Fuji/Xerox cartridge RFIDs... } waveshare { Waveshare NFC ePaper... } ----------- --------------------- General --------------------- help This help list List protocol data in trace buffer search Search for known HF tags", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf iclass calcnewkey": { "command": "hf iclass calcnewkey", "description": "Calculate new keys for updating (blocks 3 & 4)", @@ -3004,9 +3003,10 @@ "--nki New key index to select key from memory 'hf iclass managekeys'", "--csn Specify a Card Serial Number (CSN) to diversify the key (if omitted will attempt to read a CSN)", "--elite Elite computations applied to new key", - "--elite2 Elite computations applied to both old and new key" + "--elite2 Elite computations applied to both old and new key", + "--oldelite Elite computations applied only to old key" ], - "usage": "hf iclass calcnewkey [-h] [--old ] [--oki ] [--new ] [--nki ] [--csn ] [--elite] [--elite2]" + "usage": "hf iclass calcnewkey [-h] [--old ] [--oki ] [--new ] [--nki ] [--csn ] [--elite] [--elite2] [--oldelite]" }, "hf iclass chk": { "command": "hf iclass chk", @@ -3028,14 +3028,14 @@ }, "hf iclass configcard": { "command": "hf iclass configcard", - "description": "Manage reader configuration card via Cardhelper, The generated config card will be uploaded to device emulator memory. You can start simulating `hf iclass sim -t 3` or use the emul commands", + "description": "Manage reader configuration card via Cardhelper or internal database, The generated config card will be uploaded to device emulator memory. You can start simulating `hf iclass sim -t 3` or use the emul commands", "notes": [ - "hf iclass configcard -l -> download config card settings", - "hf iclass configcard -p -> print all config cards", + "hf iclass configcard -l -> download config card settings from cardhelper", + "hf iclass configcard -p -> print all config cards in the database", "hf iclass configcard --ci 1 -> view config card setting in slot 1", "hf iclass configcard -g --ci 0 -> generate config file from slot 0" ], - "offline": true, + "offline": false, "options": [ "-h, --help This help", "--ci use config slot at index", @@ -3046,13 +3046,45 @@ ], "usage": "hf iclass configcard [-hglp] [--ci ] [--ki ]" }, + "hf iclass creditepurse": { + "command": "hf iclass creditepurse", + "description": "Credit the epurse on an iCLASS tag. The provided key must be the credit key. The first two bytes of the epurse are the debit value (big endian) and may be any value except FFFF. The remaining two bytes of the epurse are the credit value and must be smaller than the previous value.", + "notes": [ + "hf iclass creditepurse -d FEFFFFFF -k 001122334455667B", + "hf iclass creditepurse -d FEFFFFFF --ki 0" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-k, --key Credit key as 8 hex bytes", + "--ki Key index to select key from memory 'hf iclass managekeys'", + "-d, --data data to write as 8 hex bytes", + "--elite elite computations applied to key", + "--raw no computations applied to key", + "-v, --verbose verbose output", + "--shallow use shallow (ASK) reader modulation instead of OOK" + ], + "usage": "hf iclass creditepurse [-hv] [-k ] [--ki ] -d [--elite] [--raw] [--shallow]" + }, "hf iclass decrypt": { "command": "hf iclass decrypt", - "description": "3DES decrypt data This is a naive implementation, it tries to decrypt every block after block 6. Correct behaviour would be to decrypt only the application areas where the key is valid, which is defined by the configuration block.", - "notes": [], + "description": "3DES decrypt data This is a naive implementation, it tries to decrypt every block after block 6. Correct behaviour would be to decrypt only the application areas where the key is valid, which is defined by the configuration block. OBS! In order to use this function, the file `iclass_decryptionkey.bin` must reside in the resources directory. The file must be 16 bytes binary data or... make sure your cardhelper is placed in the sim module", + "notes": [ + "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin", + "hf iclass decrypt -f hf-iclass-AA162D30F8FF12F1-dump.bin -k 000102030405060708090a0b0c0d0e0f", + "hf iclass decrypt -d 1122334455667788 -k 000102030405060708090a0b0c0d0e0f" + ], "offline": true, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-f, --file Specify a filename for dump file", + "-d, --data 3DES encrypted data", + "-k, --key 3DES transport key", + "-v, --verbose verbose output", + "--d6 decode as block 6", + "-z, --dense dense dump output style" + ], + "usage": "hf iclass decrypt [-hvz] [-f ] [-d ] [-k ] [--d6]" }, "hf iclass dump": { "command": "hf iclass dump", @@ -3077,24 +3109,26 @@ "--nr replay of NR/MAC", "-z, --dense dense dump output style", "--force force unsecure card read", - "--shallow use shallow (ASK) reader modulation instead of OOK" + "--shallow use shallow (ASK) reader modulation instead of OOK", + "--ns no save to file" ], - "usage": "hf iclass dump [-hz] [-f ] [-k ] [--ki ] [--credit ] [--ci ] [--elite] [--raw] [--nr] [--force] [--shallow]" + "usage": "hf iclass dump [-hz] [-f ] [-k ] [--ki ] [--credit ] [--ci ] [--elite] [--raw] [--nr] [--force] [--shallow] [--ns]" }, "hf iclass eload": { "command": "hf iclass eload", - "description": "Load emulator memory with data from (bin/eml/json) iCLASS dump file", + "description": "Load emulator memory with data from (bin/json) iCLASS dump file", "notes": [ - "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.eml", + "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.json", "hf iclass eload -f hf-iclass-AA162D30F8FF12F1-dump.bin -m" ], "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump (bin/eml/json)", - "-m, --mem use RDV4 spiffs" + "-f, --file Specify a filename for dump file", + "-m, --mem use RDV4 spiffs", + "-v, --verbose verbose output" ], - "usage": "hf iclass eload [-hm] -f " + "usage": "hf iclass eload [-hmv] -f " }, "hf iclass encode": { "command": "hf iclass encode", @@ -3139,7 +3173,7 @@ }, "hf iclass esave": { "command": "hf iclass esave", - "description": "Save emulator memory to file. if filename is not supplied, CSN will be used.", + "description": "Save emulator memory to file (bin/json) if filename is not supplied, CSN will be used.", "notes": [ "hf iclass esave", "hf iclass esave -f hf-iclass-dump", @@ -3148,11 +3182,25 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump file", + "-f, --file Specify a filename for dump file", "-s, --size <256|2048> number of bytes to save (default 256)" ], "usage": "hf iclass esave [-h] [-f ] [-s <256|2048>]" }, + "hf iclass esetblk": { + "command": "hf iclass esetblk", + "description": "Sets an individual block in emulator memory.", + "notes": [ + "hf iclass esetblk --blk 7 -d 0000000000000000" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--blk block number", + "-d, --data bytes to write, 8 hex bytes" + ], + "usage": "hf iclass esetblk [-h] --blk [-d ]" + }, "hf iclass eview": { "command": "hf iclass eview", "description": "Display emulator memory. Number of bytes to download defaults to 256. Other value is 2048.", @@ -3170,21 +3218,13 @@ ], "usage": "hf iclass eview [-hvz] [-s <256|2048>]" }, - "hf iclass help": { - "command": "hf iclass help", - "description": "----------- --------------------- operations --------------------- help This help info Tag information list List iclass history ----------- --------------------- recovery --------------------- loclass Use loclass to perform bruteforce reader attack lookup Uses authentication trace to check for key in dictionary file ----------- --------------------- simulation --------------------- ----------- --------------------- utils --------------------- configcard Reader configuration card calcnewkey Calc diversified keys (blocks 3 & 4) to write new keys encode Encode binary wiegand to block 7 encrypt Encrypt given block data decrypt Decrypt given block data or tag dump file managekeys Manage keys to use with iclass commands permutekey Permute function from 'heart of darkness' paper view Display content from tag dump file", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf iclass info": { "command": "hf iclass info", "description": "Act as a iCLASS reader. Reads / fingerprints a iCLASS tag.", "notes": [ "hf iclass info" ], - "offline": true, + "offline": false, "options": [ "-h, --help This help", "--shallow use shallow (ASK) reader modulation instead of OOK" @@ -3193,7 +3233,7 @@ }, "hf iclass list": { "command": "hf iclass list", - "description": "Alias of `trace list -t iclass` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "Alias of `trace list -t iclass -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf iclass list --frame -> show frame delay times", "hf iclass list -1 -> use trace buffer" @@ -3287,16 +3327,16 @@ "command": "hf iclass rdbl", "description": "Read a iCLASS block from tag", "notes": [ - "hf iclass rdbl -b 6 -k 0011223344556677", - "hf iclass rdbl -b 27 -k 0011223344556677 --credit", - "hf iclass rdbl -b 10 --ki 0" + "hf iclass rdbl --blk 6 -k 0011223344556677", + "hf iclass rdbl --blk 27 -k 0011223344556677 --credit", + "hf iclass rdbl --blk 10 --ki 0" ], "offline": false, "options": [ "-h, --help This help", "-k, --key Access key as 8 hex bytes", "--ki Key index to select key from memory 'hf iclass managekeys'", - "-b, --block The block number to read", + "--blk Block number", "--credit key is assumed to be the credit key", "--elite elite computations applied to key", "--raw no computations applied to key", @@ -3304,7 +3344,7 @@ "-v, --verbose verbose output", "--shallow use shallow (ASK) reader modulation instead of OOK" ], - "usage": "hf iclass rdbl [-hv] [-k ] [--ki ] -b [--credit] [--elite] [--raw] [--nr] [--shallow]" + "usage": "hf iclass rdbl [-hv] [-k ] [--ki ] --blk [--credit] [--elite] [--raw] [--nr] [--shallow]" }, "hf iclass reader": { "command": "hf iclass reader", @@ -3322,7 +3362,7 @@ }, "hf iclass restore": { "command": "hf iclass restore", - "description": "Restore data from dumpfile onto a iCLASS tag", + "description": "Restore data from dumpfile (bin/eml/json) onto a iCLASS tag", "notes": [ "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0", "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0 --elite", @@ -3331,7 +3371,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file specify a filename to restore (bin/eml/json)", + "-f, --file specify a filename to restore", "-k, --key Access key as 8 hex bytes", "--ki Key index to select key from memory 'hf iclass managekeys'", "--first The first block number to restore", @@ -3344,6 +3384,19 @@ ], "usage": "hf iclass restore [-hv] -f [-k ] [--ki ] --first --last [--credit] [--elite] [--raw] [--shallow]" }, + "hf iclass sam": { + "command": "hf iclass sam", + "description": "Extract PACS via a HID SAM", + "notes": [ + "hf iclass sam" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose verbose output" + ], + "usage": "hf iclass sam [-hv]" + }, "hf iclass sim": { "command": "hf iclass sim", "description": "Simulate a iCLASS legacy/standard tag", @@ -3381,13 +3434,16 @@ "description": "Print a iCLASS tag dump file (bin/eml/json)", "notes": [ "hf iclass view -f hf-iclass-AA162D30F8FF12F1-dump.bin", - "hf iclass view --first 1 -f hf-iclass-AA162D30F8FF12F1-dump.bin" + "hf iclass view --first 1 -f hf-iclass-AA162D30F8FF12F1-dump.bin", + "", + "If --first is not specified it will default to the first user block", + "which is block 6 for secured chips or block 3 for non-secured chips" ], "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump (bin/eml/json)", - "--first Begin printing from this block (default block 6)", + "-f, --file Specify a filename for dump file", + "--first Begin printing from this block (default first user block)", "--last End printing at this block (default 0, ALL)", "-v, --verbose verbose output", "-z, --dense dense dump output style" @@ -3398,16 +3454,16 @@ "command": "hf iclass wrbl", "description": "Write data to an iCLASS tag", "notes": [ - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B", - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit", - "hf iclass wrbl -b 10 -d AAAAAAAAAAAAAAAA --ki 0" + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B", + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --credit", + "hf iclass wrbl --blk 10 -d AAAAAAAAAAAAAAAA --ki 0" ], "offline": false, "options": [ "-h, --help This help", "-k, --key Access key as 8 hex bytes", "--ki Key index to select key from memory 'hf iclass managekeys'", - "-b, --block The block number to read", + "--blk block number", "-d, --data data to write as 8 hex bytes", "-m, --mac replay mac data (4 hex bytes)", "--credit key is assumed to be the credit key", @@ -3417,7 +3473,7 @@ "-v, --verbose verbose output", "--shallow use shallow (ASK) reader modulation instead of OOK" ], - "usage": "hf iclass wrbl [-hv] [-k ] [--ki ] -b -d [-m ] [--credit] [--elite] [--raw] [--nr] [--shallow]" + "usage": "hf iclass wrbl [-hv] [-k ] [--ki ] --blk -d [-m ] [--credit] [--elite] [--raw] [--nr] [--shallow]" }, "hf jooki clone": { "command": "hf jooki clone", @@ -3483,14 +3539,6 @@ ], "usage": "hf jooki encode [-hrtv] [-u ] [--dragon] [--fox] [--ghost] [--knight] [--whale] [--blackdragon] [--blackfox] [--blackknight] [--blackwhale] [--whitedragon] [--whitefox] [--whiteknight] [--whitewhale] [--tid ] [--fid ]" }, - "hf jooki help": { - "command": "hf jooki help", - "description": "help This help decode Decode Jooki token encode Encode Jooki token", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf jooki sim": { "command": "hf jooki sim", "description": "Simulate a Jooki token. Either `hf mfu eload` before or use `-d` param", @@ -3519,14 +3567,6 @@ ], "usage": "hf ksx6924 balance [-hka]" }, - "hf ksx6924 help": { - "command": "hf ksx6924 help", - "description": "help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf ksx6924 info": { "command": "hf ksx6924 info", "description": "Get info about a KS X 6924 transit card. This application is used by T-Money (South Korea) and Snapper+ (Wellington, New Zealand).", @@ -3600,7 +3640,7 @@ }, "hf legic dump": { "command": "hf legic dump", - "description": "Read all memory from LEGIC Prime tags and saves to (bin/eml/json) dump file It autodetects card type (MIM22, MIM256, MIM1024)", + "description": "Read all memory from LEGIC Prime tags and saves to (bin/json) dump file It autodetects card type (MIM22, MIM256, MIM1024)", "notes": [ "hf legic dump -> use UID as filename", "hf legic dump -f myfile", @@ -3618,13 +3658,17 @@ "command": "hf legic einfo", "description": "It decodes and displays emulator memory", "notes": [ - "hf legic einfo" + "hf legic einfo", + "hf legic eview --22" ], "offline": false, "options": [ - "-h, --help This help" + "-h, --help This help", + "--22 LEGIC Prime MIM22", + "--256 LEGIC Prime MIM256 (def)", + "--1024 LEGIC Prime MIM1024" ], - "usage": "hf legic einfo [-h]" + "usage": "hf legic einfo [-h] [--22] [--256] [--1024]" }, "hf legic eload": { "command": "hf legic eload", @@ -3643,7 +3687,7 @@ }, "hf legic esave": { "command": "hf legic esave", - "description": "Saves a (bin/eml/json) dump file of emulator memory", + "description": "Saves a (bin/json) dump file of emulator memory", "notes": [ "hf legic esave -> uses UID as filename", "hf legic esave -f myfile --22", @@ -3672,17 +3716,10 @@ "-h, --help This help", "--22 LEGIC Prime MIM22", "--256 LEGIC Prime MIM256 (def)", - "--1024 LEGIC Prime MIM1024" + "--1024 LEGIC Prime MIM1024", + "-v, --verbose verbose output" ], - "usage": "hf legic eview [-h] [--22] [--256] [--1024]" - }, - "hf legic help": { - "command": "hf legic help", - "description": "----------- --------------------- operations --------------------- help This help list List LEGIC history ----------- --------------------- simulation --------------------- ----------- --------------------- utils --------------------- crc Calculate Legic CRC over given bytes view Display deobfuscated and decoded content from tag dump file", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "usage": "hf legic eview [-hv] [--22] [--256] [--1024]" }, "hf legic info": { "command": "hf legic info", @@ -3692,9 +3729,10 @@ ], "offline": false, "options": [ - "-h, --help This help" + "-h, --help This help", + "-v, --verbose verbose output" ], - "usage": "hf legic info [-h]" + "usage": "hf legic info [-hv]" }, "hf legic list": { "command": "hf legic list", @@ -3757,7 +3795,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file Filename to restore", + "-f, --file Specify a filename to restore", "--ob obfuscate dump data (xor with MCC)" ], "usage": "hf legic restore [-h] -f [--ob]" @@ -3786,9 +3824,10 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file Filename of dump" + "-f, --file Specify a filename for dump file", + "-v, --verbose verbose output" ], - "usage": "hf legic view [-h] -f " + "usage": "hf legic view [-hv] -f " }, "hf legic wipe": { "command": "hf legic wipe", @@ -3852,14 +3891,6 @@ ], "usage": "hf lto dump [-h] [-f ]" }, - "hf lto help": { - "command": "hf lto help", - "description": "help This help list List LTO-CM history", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf lto info": { "command": "hf lto info", "description": "Get info from LTO tags", @@ -3874,7 +3905,7 @@ }, "hf lto list": { "command": "hf lto list", - "description": "Alias of `trace list -t lto` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "Alias of `trace list -t lto -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf lto list --frame -> show frame delay times", "hf lto list -1 -> use trace buffer" @@ -3937,15 +3968,15 @@ "command": "hf lto wrbl", "description": "Write data to block on LTO tag", "notes": [ - "hf lto wrbl --block 128 -d 0001020304050607080910111213141516171819202122232425262728293031" + "hf lto wrbl --blk 128 -d 0001020304050607080910111213141516171819202122232425262728293031" ], "offline": false, "options": [ "-h, --help This help", "-d, --data 32 bytes of data to write (64 hex symbols, no spaces)", - "--block The block number to write to as an integer" + "--blk The block number to write to as an integer" ], - "usage": "hf lto wrbl [-h] -d --block " + "usage": "hf lto wrbl [-h] -d --blk " }, "hf mf acl": { "command": "hf mf acl", @@ -3965,14 +3996,16 @@ "command": "hf mf auth4", "description": "Executes AES authentication command in ISO14443-4", "notes": [ - "hf mf auth4 4000 000102030405060708090a0b0c0d0e0f -> executes authentication", - "hf mf auth4 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> executes authentication" + "hf mf auth4 -n 4000 -k 000102030405060708090a0b0c0d0e0f -> executes authentication", + "hf mf auth4 -n 9003 -k FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> executes authentication" ], "offline": false, "options": [ - "-h, --help This help" + "-h, --help This help", + "-n key num, 2 hex bytes", + "-k, --key key, 16 hex bytes" ], - "usage": "hf mf auth4 [-h] " + "usage": "hf mf auth4 [-h] -n -k " }, "hf mf autopwn": { "command": "hf mf autopwn", @@ -3981,7 +4014,8 @@ "hf mf autopwn", "hf mf autopwn -s 0 -a -k FFFFFFFFFFFF -> target MFC 1K card, Sector 0 with known key A 'FFFFFFFFFFFF'", "hf mf autopwn --1k -f mfc_default_keys -> target MFC 1K card, default dictionary", - "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -f mfc_default_keys -> combo of the two above samples" + "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -f mfc_default_keys -> combo of the two above samples", + "hf mf autopwn --1k -s 0 -a -k FFFFFFFFFFFF -k a0a1a2a3a4a5 -> multiple user supplied keys" ], "offline": false, "options": [ @@ -4005,7 +4039,7 @@ "--i2 AVX2", "--i5 AVX512" ], - "usage": "hf mf autopwn [-hablv] [-k ] [-s ] [-f ] [--slow] [--mini] [--1k] [--2k] [--4k] [--in] [--im] [--is] [--ia] [--i2] [--i5]" + "usage": "hf mf autopwn [-hablv] [-k ]... [-s ] [-f ] [--slow] [--mini] [--1k] [--2k] [--4k] [--in] [--im] [--is] [--ia] [--i2] [--i5]" }, "hf mf cgetblk": { "command": "hf mf cgetblk", @@ -4076,14 +4110,14 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "--emu from emulator memory" ], "usage": "hf mf cload [-h] [-f ] [--emu]" }, "hf mf csave": { "command": "hf mf csave", - "description": "Save magic gen1a card memory into three files (BIN/EML/JSON)or into emulator memory", + "description": "Save magic gen1a card memory to file (bin/json)or into emulator memory", "notes": [ "hf mf csave", "hf mf csave --4k" @@ -4091,7 +4125,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", @@ -4202,7 +4236,7 @@ }, "hf mf dump": { "command": "hf mf dump", - "description": "Dump MIFARE Classic tag to binary file If no given, UID will be used as filename", + "description": "Dump MIFARE Classic tag to file (bin/json) If no given, UID will be used as filename", "notes": [ "hf mf dump --mini -> MIFARE Mini", "hf mf dump --1k -> MIFARE Classic 1k", @@ -4213,14 +4247,15 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "-k, --keys filename of keys", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", - "--4k MIFARE Classic 4k / S70" + "--4k MIFARE Classic 4k / S70", + "--ns no save to file" ], - "usage": "hf mf dump [-h] [-f ] [-k ] [--mini] [--1k] [--2k] [--4k]" + "usage": "hf mf dump [-h] [-f ] [-k ] [--mini] [--1k] [--2k] [--4k] [--ns]" }, "hf mf ecfill": { "command": "hf mf ecfill", @@ -4310,20 +4345,39 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", "--ul MIFARE Ultralight family", "-m, --mem use RDV4 spiffs", - "-q, --qty manually set number of blocks (overrides)" + "-q, --qty manually set number of blocks (overrides)", + "-v, --verbose verbose output" ], - "usage": "hf mf eload [-hm] -f [--mini] [--1k] [--2k] [--4k] [--ul] [-q ]" + "usage": "hf mf eload [-hmv] -f [--mini] [--1k] [--2k] [--4k] [--ul] [-q ]" + }, + "hf mf encodehid": { + "command": "hf mf encodehid", + "description": "Encode binary wiegand to card Use either --bin or --wiegand/--fc/--cn", + "notes": [ + "hf mf encodehid --bin 10001111100000001010100011 -> FC 31 CN 337 (H10301)", + "hf mf encodehid -w H10301 --fc 31 --cn 337" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--bin Binary string i.e 0001001001", + "--fc facility code", + "--cn card number", + "-w, --wiegand see `wiegand list` for available formats", + "-v, --verbose verbose output" + ], + "usage": "hf mf encodehid [-hv] [--bin ] [--fc ] [--cn ] [-w ]" }, "hf mf esave": { "command": "hf mf esave", - "description": "Save emulator memory into three files (BIN/EML/JSON)", + "description": "Save emulator memory to file (bin/json)", "notes": [ "hf mf esave", "hf mf esave --4k", @@ -4332,7 +4386,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "--mini MIFARE Classic Mini / S20", "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", @@ -4368,9 +4422,10 @@ "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--sk Save extracted keys to file" ], - "usage": "hf mf eview [-hv] [--mini] [--1k] [--2k] [--4k]" + "usage": "hf mf eview [-hv] [--mini] [--1k] [--2k] [--4k] [--sk]" }, "hf mf fchk": { "command": "hf mf fchk", @@ -4400,18 +4455,33 @@ ], "usage": "hf mf fchk [-h] [-k ]... [--mini] [--1k] [--2k] [--4k] [--emu] [--dump] [--mem] [-f ]" }, - "hf mf gdmconfig": { - "command": "hf mf gdmconfig", + "hf mf gchpwd": { + "command": "hf mf gchpwd", + "description": "Change access password for Gen4 GTU card. WARNING! If you dont KNOW the password - you CAN'T access it!!!", + "notes": [ + "hf mf gchpwd --pwd 00000000 --newpwd 01020304" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-p, --pwd password 4 bytes", + "-n, --newpwd new password 4 bytes", + "-v, --verbose verbose output" + ], + "usage": "hf mf gchpwd [-hv] [-p ] [-n ]" + }, + "hf mf gdmcfg": { + "command": "hf mf gdmcfg", "description": "Get configuration data from magic gen4 GDM card.", "notes": [ - "hf mf gdmconfig" + "hf mf gdmcfg" ], "offline": false, "options": [ "-h, --help This help", "-k, --key key 6 bytes" ], - "usage": "hf mf gdmconfig [-h] [-k ]" + "usage": "hf mf gdmcfg [-h] [-k ]" }, "hf mf gdmsetblk": { "command": "hf mf gdmsetblk", @@ -4423,29 +4493,53 @@ "options": [ "-h, --help This help", "--blk block number", - "-a input key type is key A (def)", - "-b input key type is key B", "-d, --data bytes to write, 16 hex bytes", "-k, --key key, 6 hex bytes", "--force override warnings" ], - "usage": "hf mf gdmsetblk [-hab] --blk [-d ] [-k ] [--force]" + "usage": "hf mf gdmsetblk [-h] --blk [-d ] [-k ] [--force]" + }, + "hf mf gdmsetcfg": { + "command": "hf mf gdmsetcfg", + "description": "Set configuration data on a magic gen4 GDM card", + "notes": [ + "hf mf gdmsetcfg -d 850000000000000000005A5A00000008" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-d, --data bytes to write, 16 hex bytes" + ], + "usage": "hf mf gdmsetcfg [-h] -d " }, "hf mf gen3blk": { "command": "hf mf gen3blk", - "description": "Overwrite full manufacturer block for magic Gen3 card - You can specify part of manufacturer block as 4/7-bytes for UID change only", - "notes": [], + "description": "Overwrite full manufacturer block for magic Gen3 card - You can specify part of manufacturer block as 4/7-bytes for UID change only NOTE: BCC, SAK, ATQA will be calculated automatically", + "notes": [ + "hf mf gen3blk -> print current data", + "hf mf gen3blk -d 01020304 -> set 4 byte uid", + "hf mf gen3blk -d 01020304050607 -> set 7 byte uid", + "hf mf gen3blk -d 01020304FFFFFFFF0102030405060708" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-d, --data manufacturer block data up to 16 hex bytes" + ], + "usage": "hf mf gen3blk [-h] [-d ]" }, "hf mf gen3freeze": { "command": "hf mf gen3freeze", - "description": "Perma lock further UID changes. No more UID changes available after operation completed", - "notes": [], + "description": "Perma lock further UID changes. No more UID changes available after operation completed Note: operation is ! irreversible !", + "notes": [ + "hf mf gen3freeze -y" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-y, --yes confirm UID lock operation" + ], + "usage": "hf mf gen3freeze -y[h]" }, "hf mf gen3uid": { "command": "hf mf gen3uid", @@ -4477,6 +4571,21 @@ ], "usage": "hf mf ggetblk [-hv] -b [-p ]" }, + "hf mf ginfo": { + "command": "hf mf ginfo", + "description": "Read info about magic gen4 GTU card.", + "notes": [ + "hf mf ginfo -> get info with default password 00000000", + "hf mf ginfo --pwd 01020304 -> get info with password" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose verbose output", + "-p, --pwd password 4bytes" + ], + "usage": "hf mf ginfo [-hv] [-p ]" + }, "hf mf gload": { "command": "hf mf gload", "description": "Load magic gen4 gtu card with data from (bin/eml/json) dump file or from emulator memory.", @@ -4497,7 +4606,7 @@ "--4k MIFARE Classic 4k / S70", "-p, --pwd password 4bytes", "-v, --verbose verbose output", - "-f, --file filename of dump", + "-f, --file Specify a filename for dump file", "--emu from emulator memory", "--start index of block to start writing (default 0)", "--end index of block to end writing (default last block)" @@ -4506,7 +4615,7 @@ }, "hf mf gsave": { "command": "hf mf gsave", - "description": "Save `magic gen4 gtu` card memory into three files (BIN/EML/JSON)or into emulator memory", + "description": "Save `magic gen4 gtu` card memory to file (bin/json)or into emulator memory", "notes": [ "hf mf gsave", "hf mf gsave --4k", @@ -4519,8 +4628,8 @@ "--1k MIFARE Classic 1k / S50 (def)", "--2k MIFARE Classic/Plus 2k", "--4k MIFARE Classic 4k / S70", - "-p, --pwd password 4bytes", - "-f, --file filename of dump", + "-p, --pwd password 4 bytes", + "-f, --file Specify a filename for dump file", "--emu to emulator memory" ], "usage": "hf mf gsave [-h] [--mini] [--1k] [--2k] [--4k] [-p ] [-f ] [--emu]" @@ -4598,17 +4707,28 @@ ], "usage": "hf mf hardnested [-habrstw] [-k ] [--blk ] [--tblk ] [--ta] [--tb] [--tk ] [-u ] [-f ] [--in] [--im] [--is] [--ia] [--i2] [--i5]" }, - "hf mf help": { - "command": "hf mf help", - "description": "help This help list List MIFARE history hardnested Nested attack for hardened MIFARE Classic cards decrypt [nt] [ar_enc] [at_enc] [data] - to decrypt sniff or trace acl Decode and print MIFARE Classic access rights bytes mad Checks and prints MAD value Value blocks view Display content from tag dump file", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "hf mf info": { + "command": "hf mf info", + "description": "Information and check vulnerabilities in a MIFARE Classic card Some cards in order to extract information you need to specify key and/or specific keys in the command line", + "notes": [ + "hf mf info", + "hf mf info -k FFFFFFFFFFFF -n -v" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--blk block number", + "-a input key type is key A (def)", + "-b input key type is key B", + "-k, --key key, 6 hex bytes", + "-n, --nack do nack test", + "-v, --verbose verbose output" + ], + "usage": "hf mf info [-habnv] [--blk ] [-k ]" }, "hf mf list": { "command": "hf mf list", - "description": "Alias of `trace list -t mf` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "Alias of `trace list -t mf -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf mf list --frame -> show frame delay times", "hf mf list -1 -> use trace buffer" @@ -4639,14 +4759,14 @@ "options": [ "-h, --help This help", "-v, --verbose show technical data", - "--aid print all sectors with specified aid", - "-k, --key key for printing sectors", + "--aid print all sectors with specified aid", + "-k, --key key for printing sectors", "-b, --keyb use key B for access printing sectors (by default: key A)", "--be (optional, BigEndian)", "--dch decode Card Holder information", "-f, --file load dump file and decode MAD" ], - "usage": "hf mf mad [-hvb] [--aid ] [-k ] [--be] [--dch] [-f ]" + "usage": "hf mf mad [-hvb] [--aid ] [-k ] [--be] [--dch] [-f ]" }, "hf mf nack": { "command": "hf mf nack", @@ -4663,11 +4783,22 @@ }, "hf mf ndefformat": { "command": "hf mf ndefformat", - "description": "format MIFARE Classic Tag as a NFC tag with Data Exchange Format (NDEF) If no given, UID will be used as filename. It will try default keys and MAD keys to detect if tag is already formatted in order to write.", - "notes": [], + "description": "format MIFARE Classic Tag as a NFC tag with Data Exchange Format (NDEF) If no given, UID will be used as filename. It will try default keys and MAD keys to detect if tag is already formatted in order to write. If not, it will try finding a key file based on your UID. ie, if you ran autopwn before", + "notes": [ + "hf mf ndefformat", + "hf mf ndefformat --1k -> MIFARE Classic 1k", + "hf mf ndefformat --keys hf-mf-01020304-key.bin -> MIFARE 1k with keys from specified file" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-k, --keys filename of keys", + "--mini MIFARE Classic Mini / S20", + "--1k MIFARE Classic 1k / S50 (def)", + "--2k MIFARE Classic/Plus 2k", + "--4k MIFARE Classic 4k / S70" + ], + "usage": "hf mf ndefformat [-h] [-k ] [--mini] [--1k] [--2k] [--4k]" }, "hf mf ndefread": { "command": "hf mf ndefread", @@ -4768,7 +4899,8 @@ "command": "hf mf rdbl", "description": "Read MIFARE Classic block", "notes": [ - "hf mf rdbl --blk 0 -k FFFFFFFFFFFF", + "hf mf rdbl --blk 0", + "hf mf rdbl --blk 0 -k A0A1A2A3A4A5", "hf mf rdbl --blk 3 -v -> get block 3, decode sector trailer" ], "offline": false, @@ -4786,7 +4918,8 @@ "command": "hf mf rdsc", "description": "Read MIFARE Classic sector", "notes": [ - "hf mf rdsc -s 0 -k FFFFFFFFFFFF" + "hf mf rdsc -s 0", + "hf mf rdsc -s 0 -k A0A1A2A3A4A5" ], "offline": false, "options": [ @@ -4801,11 +4934,27 @@ }, "hf mf restore": { "command": "hf mf restore", - "description": "Restore MIFARE Classic dump file to tag.", - "notes": [], + "description": "Restore MIFARE Classic dump file to tag. The key file and dump file will program the card sector trailers. By default we authenticate to card with key 0xFFFFFFFFFFFF. If access rights in dump file is all zeros, it will be replaced with default values `--uid` param is used for filename templates `hf-mf--dump.bin` and `hf-mf--key.bin. if not specified, it will read the card uid instead. `--ka` param you can indicate that the key file should be used for authentication instead. if so we also try both B/A keys `--force` param is used to override warnings and allow bad ACL block writes. if not specified, it will skip blocks with bad ACL.", + "notes": [ + "hf mf restore", + "hf mf restore --1k --uid 04010203", + "hf mf restore --1k --uid 04010203 -k hf-mf-AABBCCDD-key.bin", + "hf mf restore --4k" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "--mini MIFARE Classic Mini / S20", + "--1k MIFARE Classic 1k / S50 (def)", + "--2k MIFARE Classic/Plus 2k", + "--4k MIFARE Classic 4k / S70", + "-u, --uid uid, (4|7|10 hex bytes)", + "-f, --file specify a filename for dump file", + "-k, --kfn key filename", + "--ka use specified keyfile to authenticate", + "--force override warnings" + ], + "usage": "hf mf restore [-h] [--mini] [--1k] [--2k] [--4k] [-u ] [-f ] [-k ] [--ka] [--force]" }, "hf mf setmod": { "command": "hf mf setmod", @@ -4855,7 +5004,7 @@ }, "hf mf staticnested": { "command": "hf mf staticnested", - "description": "Execute Nested attack against MIFARE Classic card with static nonce for key recovery. Supply a known key from one block to recover all keys", + "description": "Execute static nested attack against MIFARE Classic card with static nonce for key recovery. Supply a known key from one block to recover all keys", "notes": [ "hf mf staticnested --mini --blk 0 -a -k FFFFFFFFFFFF", "hf mf staticnested --1k --blk 0 -a -k FFFFFFFFFFFF", @@ -4890,9 +5039,10 @@ "options": [ "-h, --help This help", "-r, --reset Reset card", - "-u, --uid New UID (4 hex bytes)" + "-u, --uid New UID (4 hex bytes)", + "--furui Furui detection card" ], - "usage": "hf mf supercard [-hr] [-u ]" + "usage": "hf mf supercard [-hr] [-u ] [--furui]" }, "hf mf value": { "command": "hf mf value", @@ -4902,6 +5052,7 @@ "hf mf value --blk 16 -k FFFFFFFFFFFF --inc 10", "hf mf value --blk 16 -k FFFFFFFFFFFF -b --dec 10", "hf mf value --blk 16 -k FFFFFFFFFFFF -b --get", + "hf mf value --blk 16 -k FFFFFFFFFFFF --res --transfer 30 --tk FFFFFFFFFFFF -> transfer block 16 value to block 30 (even if block can't be incremented by ACL)", "hf mf value --get -d 87D612007829EDFF87D6120011EE11EE" ], "offline": true, @@ -4910,14 +5061,19 @@ "-k, --key key, 6 hex bytes", "-a input key type is key A (def)", "-b input key type is key B", - "--inc Incremenet value by X (0 - 2147483647)", - "--dec Dcrement value by X (0 - 2147483647)", + "--inc Increment value by X (0 - 2147483647)", + "--dec Decrement value by X (0 - 2147483647)", "--set Set value to X (-2147483647 - 2147483647)", + "--transfer Transfer value to other block (after inc/dec/restore)", + "--tkey transfer key, 6 hex bytes (if transfer is preformed to other sector)", + "--ta transfer key type is key A (def)", + "--tb transfer key type is key B", "--get Get value from block", + "--res Restore (copy value to card buffer, should be used with --transfer)", "--blk block number", "-d, --data block data to extract values from (16 hex bytes)" ], - "usage": "hf mf value [-hab] [-k ] [--inc ] [--dec ] [--set ] [--get] [--blk ] [-d ]" + "usage": "hf mf value [-hab] [-k ] [--inc ] [--dec ] [--set ] [--transfer ] [--tkey ] [--ta] [--tb] [--get] [--res] [--blk ] [-d ]" }, "hf mf view": { "command": "hf mf view", @@ -4928,10 +5084,11 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump", - "-v, --verbose verbose output" + "-f, --file Specify a filename for dump file", + "-v, --verbose verbose output", + "--sk Save extracted keys to file" ], - "usage": "hf mf view [-hv] -f " + "usage": "hf mf view [-hv] -f [--sk]" }, "hf mf wipe": { "command": "hf mf wipe", @@ -4953,7 +5110,8 @@ "command": "hf mf wrbl", "description": "Write MIFARE Classic block with 16 hex bytes of data Sector 0 / Block 0 - Manufacturer block When writing to block 0 you must use a VALID block 0 data (UID, BCC, SAK, ATQA) Writing an invalid block 0 means rendering your Magic GEN2 card undetectable. Look in the magic_cards_notes.md file for help to resolve it. `--force` param is used to override warnings like bad ACL and BLOCK 0 writes. if not specified, it will exit if detected", "notes": [ - "hf mf wrbl --blk 1 -k FFFFFFFFFFFF -d 000102030405060708090a0b0c0d0e0f" + "hf mf wrbl --blk 1 -d 000102030405060708090a0b0c0d0e0f", + "hf mf wrbl --blk 1 -k A0A1A2A3A4A5 -d 000102030405060708090a0b0c0d0e0f" ], "offline": false, "options": [ @@ -5090,9 +5248,9 @@ "description": "Checks keys with MIFARE DESFire card.", "notes": [ "hf mfdes chk --aid 123456 -k 000102030405060708090a0b0c0d0e0f -> check key on aid 0x123456", - "hf mfdes chk -d mfdes_default_keys -> check keys from dictionary against all existing aid on card", - "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys from dictionary against aid 0x123456", - "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to json", + "hf mfdes chk -d mfdes_default_keys -> check keys against all existing aid on card", + "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys against aid 0x123456", + "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to `keys.json`", "hf mfdes chk --aid 123456 --pattern2b --startp2b FA00 -> check all 2-byte keys pattern on aid 0x123456. Start from key FA00FA00...FA00" ], "offline": false, @@ -5768,14 +5926,6 @@ ], "usage": "hf mfdes getuid [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ]" }, - "hf mfdes help": { - "command": "hf mfdes help", - "description": "help This help list List DESFire (ISO 14443A) history test Regression crypto tests", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf mfdes info": { "command": "hf mfdes info", "description": "Get info from MIFARE DESfire tags", @@ -5790,7 +5940,7 @@ }, "hf mfdes list": { "command": "hf mfdes list", - "description": "Alias of `trace list -t des` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "Alias of `trace list -t des -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf mfdes list --frame -> show frame delay times", "hf mfdes list -1 -> use trace buffer" @@ -6103,7 +6253,7 @@ }, "hf mfp auth": { "command": "hf mfp auth", - "description": "Executes AES authentication command for Mifare Plus card", + "description": "Executes AES authentication command for MIFARE Plus card", "notes": [ "hf mfp auth --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> executes authentication", "hf mfp auth --ki 9003 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data" @@ -6117,14 +6267,40 @@ ], "usage": "hf mfp auth [-hv] --ki --key " }, + "hf mfp chconf": { + "command": "hf mfp chconf", + "description": "Change the configuration on a Mifare Plus tag. DANGER!", + "notes": [ + "This requires Card Master Key (9000) or Card Configuration Key (9001).", + "Configuration block info can be found below.", + "* Block B000 (00; CMK): Max amount of commands without MAC (byte 0), as well as plain mode access (unknown).", + "* Block B001 (01; CCK): Installation identifier for Virtual Card. Please consult NXP for data.", + "* Block B002 (02; CCK): ATS data.", + "* Block B003 (03; CCK): Use Random ID in SL3, decide whether proximity check is mandatory.", + "* DO NOT WRITE THIS BLOCK UNDER ANY CIRCUMSTANCES! Risk of bricking.", + "More configuration tips to follow. Check JMY600 Series IC Card Module.", + "hf mfp chconf -c 00 -d 10ffffffffffffffffffffffffffffff --key A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7 -> Allow 16 commands without MAC in a single transaction." + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose Verbose mode", + "--nmr Do not expect MAC in response", + "-c, --conf Config block number, 0-3", + "-k, --key Card key, 16 hex bytes", + "--cck Auth as Card Configuration key instead of Card Master Key", + "-d, --data New configuration data, 16 hex bytes" + ], + "usage": "hf mfp chconf [-hv] [--nmr] -c [-k ] [--cck] -d " + }, "hf mfp chk": { "command": "hf mfp chk", "description": "Checks keys on MIFARE Plus card", "notes": [ "hf mfp chk -k 000102030405060708090a0b0c0d0e0f -> check key on sector 0 as key A and B", - "hf mfp chk -s 2 -a -> check default key list on sector 2, key A", + "hf mfp chk -s 2 -a -> check default key list on sector 2, only key A", "hf mfp chk -d mfp_default_keys -s0 -e6 -> check keys from dictionary against sectors 0-6", - "hf mfp chk --pattern1b -j keys -> check all 1-byte keys pattern and save found keys to json", + "hf mfp chk --pattern1b --dump -> check all 1-byte keys pattern and save found keys to file", "hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00" ], "offline": false, @@ -6139,14 +6315,34 @@ "--pattern1b Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)", "--pattern2b Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)", "--startp2b Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)", - "-j, --json Json filename to save keys", + "--dump Dump found keys to JSON file", "-v, --verbose Verbose mode" ], - "usage": "hf mfp chk [-habv] [-s <0..255>] [-e <0..255>] [-k ] [-d ] [--pattern1b] [--pattern2b] [--startp2b ] [-j ]" + "usage": "hf mfp chk [-habv] [-s <0..255>] [-e <0..255>] [-k ] [-d ] [--pattern1b] [--pattern2b] [--startp2b ] [--dump]" + }, + "hf mfp chkey": { + "command": "hf mfp chkey", + "description": "Change the keys on a Mifare Plus tag", + "notes": [ + "This requires the key that can update the key that you are trying to update.", + "hf mfp chkey --ki 401f -d FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF --key A0A1A2A3A4A5A6A7A0A1A2A3A4A5A6A7 -> Change key B for Sector 15 from MAD to default", + "hf mfp chkey --ki 9000 -d 32F9351A1C02B35FF97E0CA943F814F6 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> Change card master key to custom from default" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-v, --verbose Verbose mode", + "--nmr Do not expect MAC in response", + "--ki Key Index, 2 hex bytes", + "-k, --key Current sector key, 16 hex bytes", + "-b, --typeb Sector key is key B", + "-d, --data New key, 16 hex bytes" + ], + "usage": "hf mfp chkey [-hvb] [--nmr] --ki [-k ] -d " }, "hf mfp commitp": { "command": "hf mfp commitp", - "description": "Executes Commit Perso command. Can be used in SL0 mode only.", + "description": "Executes Commit Perso command. Can be used in SL0 mode only. OBS! This command will not be executed if CardConfigKey, CardMasterKey and L3SwitchKey AES keys are not written.", "notes": [ "hf mfp commitp" ], @@ -6157,13 +6353,22 @@ ], "usage": "hf mfp commitp [-hv]" }, - "hf mfp help": { - "command": "hf mfp help", - "description": "help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "hf mfp dump": { + "command": "hf mfp dump", + "description": "Dump MIFARE Plus tag to file (bin/json) If no given, UID will be used as filename", + "notes": [ + "hf mfp dump", + "hf mfp dump --keys hf-mf-066C8B78-key.bin -> MIFARE Plus with keys from specified file" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-f, --file Specify a filename for dump file", + "-k, --keys Specify a filename for keys file", + "--ns no save to file", + "-v, --verbose Verbose mode" + ], + "usage": "hf mfp dump [-hv] [-f ] [-k ] [--ns]" }, "hf mfp info": { "command": "hf mfp info", @@ -6192,9 +6397,30 @@ ], "usage": "hf mfp initp [-hv] [-k ]" }, + "hf mfp list": { + "command": "hf mfp list", + "description": "Alias of `trace list -t mfp -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "notes": [ + "hf mfp list --frame -> show frame delay times", + "hf mfp list -1 -> use trace buffer" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-1, --buffer use data from trace buffer", + "--frame show frame delay times", + "-c mark CRC bytes", + "-r show relative times (gap and duration)", + "-u display times in microseconds instead of clock cycles", + "-x show hexdump to convert to pcap(ng)", + "or to import into Wireshark using encapsulation type \"ISO 14443\"", + "-f, --file filename of dictionary" + ], + "usage": "hf mfp list [-h1crux] [--frame] [-f ]" + }, "hf mfp mad": { "command": "hf mfp mad", - "description": "Checks and prints Mifare Application Directory (MAD)", + "description": "Checks and prints MIFARE Application Directory (MAD)", "notes": [ "hf mfp mad", "hf mfp mad --aid e103 -k d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 -> read and print NDEF data from MAD aid" @@ -6211,6 +6437,20 @@ ], "usage": "hf mfp mad [-hvb] [--aid ] [-k ] [--be] [--dch]" }, + "hf mfp ndefformat": { + "command": "hf mfp ndefformat", + "description": "format MIFARE Plus Tag as a NFC tag with Data Exchange Format (NDEF) If no given, UID will be used as filename. It will try default keys and MAD keys to detect if tag is already formatted in order to write. If not, it will try finding a key file based on your UID. ie, if you ran autopwn before", + "notes": [ + "hf mfp ndefformat", + "hf mfp ndefformat --keys hf-mf-01020304-key.bin -> with keys from specified file" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-k, --keys filename of keys" + ], + "usage": "hf mfp ndefformat [-h] [-k ]" + }, "hf mfp ndefread": { "command": "hf mfp ndefread", "description": "Prints NFC Data Exchange Format (NDEF)", @@ -6231,9 +6471,27 @@ ], "usage": "hf mfp ndefread [-hvb] [--aid ] [-k ] [-f ]" }, + "hf mfp ndefwrite": { + "command": "hf mfp ndefwrite", + "description": "Write raw NDEF hex bytes to tag. This commands assumes tag already been NFC/NDEF formatted.", + "notes": [ + "hf mfp ndefwrite -d 0300FE -> write empty record to tag", + "hf mfp ndefwrite -f myfilename", + "hf mfp ndefwrite -d 033fd1023a53709101195405656e2d55534963656d616e2054776974746572206c696e6b5101195502747769747465722e636f6d2f686572726d616e6e31303031" + ], + "offline": false, + "options": [ + "-h, --help This help", + "-d raw NDEF hex bytes", + "-f, --file write raw NDEF file to tag", + "-p fix NDEF record headers / terminator block if missing", + "-v, --verbose verbose output" + ], + "usage": "hf mfp ndefwrite [-hpv] [-d ] [-f ]" + }, "hf mfp rdbl": { "command": "hf mfp rdbl", - "description": "Reads several blocks from Mifare Plus card", + "description": "Reads blocks from MIFARE Plus card", "notes": [ "hf mfp rdbl --blk 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data", "hf mfp rdbl --blk 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF" @@ -6244,11 +6502,13 @@ "-v, --verbose Verbose mode", "-n, --count Blocks count (def: 1)", "-b, --keyb Use key B (def: keyA)", - "-p, --plain Plain communication mode between reader and card", + "-p, --plain Do not use encrypted communication mode between reader and card", + "--nmc Do not append MAC to command", + "--nmr Do not expect MAC in reply", "--blk <0..255> Block number", - "--key Key, 16 hex bytes" + "-k, --key Key, 16 hex bytes" ], - "usage": "hf mfp rdbl [-hvbp] [-n ] --blk <0..255> [--key ]" + "usage": "hf mfp rdbl [-hvbp] [-n ] [--nmc] [--nmr] --blk <0..255> [-k ]" }, "hf mfp rdsc": { "command": "hf mfp rdsc", @@ -6262,15 +6522,17 @@ "-h, --help This help", "-v, --verbose Verbose mode", "-b, --keyb Use key B (def: keyA)", - "-p, --plain Plain communication mode between reader and card", + "-p, --plain Do not use encrypted communication mode between reader and card", + "--nmc Do not append MAC to command", + "--nmr Do not expect MAC in reply", "-s, --sn <0..255> Sector number", "-k, --key Key, 16 hex bytes" ], - "usage": "hf mfp rdsc [-hvbp] -s <0..255> [-k ]" + "usage": "hf mfp rdsc [-hvbp] [--nmc] [--nmr] -s <0..255> [-k ]" }, "hf mfp wrbl": { "command": "hf mfp wrbl", - "description": "Writes one block to Mifare Plus card", + "description": "Writes one block to MIFARE Plus card", "notes": [ "hf mfp wrbl --blk 1 -d ff0000000000000000000000000000ff --key 000102030405060708090a0b0c0d0e0f -> write block 1 data", "hf mfp wrbl --blk 2 -d ff0000000000000000000000000000ff -v -> write block 2 data with default key 0xFF..0xFF" @@ -6281,26 +6543,38 @@ "-v, --verbose Verbose mode", "-b, --keyb Use key B (def: keyA)", "--blk <0..255> Block number", + "-p, --plain Do not use encrypted transmission", + "--nmr Do not expect MAC in response", "-d, --data Data, 16 hex bytes", "-k, --key Key, 16 hex bytes" ], - "usage": "hf mfp wrbl [-hvb] --blk <0..255> -d [-k ]" + "usage": "hf mfp wrbl [-hvbp] --blk <0..255> [--nmr] -d [-k ]" }, "hf mfp wrp": { "command": "hf mfp wrp", "description": "Executes Write Perso command. Can be used in SL0 mode only.", "notes": [ - "hf mfp wrp --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000", - "hf mfp wrp --ki 4000 -> write default key(0xff..0xff) to key number 4000" + "Use this command to program AES keys, as well as personalize other data on the tag.", + "You can program:", + "* Address 00 [00-FF]: Memory blocks (as well as ACLs and Crypto1 keys)", + "* Address 40 [00-40]: AES sector keys", + "* Address 90 [00-04]: AES administrative keys", + "* Address A0 [00, 01, 80, 81]: Virtual Card keys", + "* Address B0 [00-03]: Configuration data (DO NOT TOUCH B003)", + "Examples:", + "hf mfp wrp --adr 4000 --data 000102030405060708090a0b0c0d0e0f -> write key (00..0f) to key number 4000", + "hf mfp wrp --adr 4000 -> write default key(0xff..0xff) to key number 4000", + "hf mfp wrp --adr b000 -d FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> allow 255 commands without MAC in configuration block (B000)", + "hf mfp wrp --adr 0003 -d 1234561234567F078869B0B1B2B3B4B5 -> write crypto1 keys A: 123456123456 and B: B0B1B2B3B4B5 to block 3" ], "offline": false, "options": [ "-h, --help This help", "-v, --verbose Verbose output", - "--ki Key number, 2 hex bytes", - "--key Key, 16 hex bytes" + "-a, --adr Address, 2 hex bytes", + "-d, --data Data, 16 hex bytes" ], - "usage": "hf mfp wrp [-hv] --ki [--key ]" + "usage": "hf mfp wrp [-hv] -a [-d ]" }, "hf mfu cauth": { "command": "hf mfu cauth", @@ -6320,7 +6594,7 @@ }, "hf mfu dump": { "command": "hf mfu dump", - "description": "Dump MIFARE Ultralight/NTAG tag to binary/eml/json files. It autodetects card type.Supports: Ultralight, Ultralight-C, Ultralight EV1 NTAG 203, NTAG 210, NTAG 212, NTAG 213, NTAG 215, NTAG 216", + "description": "Dump MIFARE Ultralight/NTAG tag to files (bin/json) It autodetects card type.Supports: Ultralight, Ultralight-C, Ultralight EV1 NTAG 203, NTAG 210, NTAG 212, NTAG 213, NTAG 215, NTAG 216", "notes": [ "hf mfu dump -f myfile", "hf mfu dump -k AABBCCDD -> dump whole tag using pwd AABBCCDD", @@ -6335,9 +6609,10 @@ "-k, --key Key for authentication (UL-C 16 bytes, EV1/NTAG 4 bytes)", "-l Swap entered key's endianness", "-p, --page Manually set start page number to start from", - "-q, --qty Manually set number of pages to dump" + "-q, --qty Manually set number of pages to dump", + "--ns no save to file" ], - "usage": "hf mfu dump [-hl] [-f ] [-k ] [-p ] [-q ]" + "usage": "hf mfu dump [-hl] [-f ] [-k ] [-p ] [-q ] [--ns]" }, "hf mfu eload": { "command": "hf mfu eload", @@ -6349,24 +6624,25 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file Filename of dump", - "-q, --qty Number of blocks to load from eml file" + "-f, --file Specify a filename for dump file", + "-q, --qty Number of blocks to load from eml file", + "-v, --verbose verbose output" ], - "usage": "hf mfu eload [-h] -f [-q ]" + "usage": "hf mfu eload [-hv] -f [-q ]" }, "hf mfu esave": { "command": "hf mfu esave", - "description": "Saves emulator memory to a MIFARE Ultralight/NTAG dump file (bin/eml/json) By default number of pages saved depends on defined tag type. You can override this with option --end.", + "description": "Saves emulator memory to a MIFARE Ultralight/NTAG dump file (bin/json) By default number of pages saved depends on defined tag type. You can override this with option --end.", "notes": [ "hf mfu esave", "hf mfu esave --end 255 -> saves whole memory", - "hf mfu esave -f hf-mfu-04010203040506-dump.json" + "hf mfu esave -f hf-mfu-04010203040506-dump" ], "offline": false, "options": [ "-h, --help This help", "-e, --end index of last block", - "-f, --file filename of dump" + "-f, --file Specify a filename for dump file" ], "usage": "hf mfu esave [-h] [-e ] [-f ]" }, @@ -6384,14 +6660,6 @@ ], "usage": "hf mfu eview [-h] [-e ]" }, - "hf mfu help": { - "command": "hf mfu help", - "description": "help This help keygen Generate 3DES MIFARE diversified keys pwdgen Generate pwd from known algos view Display content from tag dump file", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf mfu info": { "command": "hf mfu info", "description": "Get info about MIFARE Ultralight Family styled tag. Sometimes the tags are locked down, and you may need a key to be able to read the information", @@ -6423,6 +6691,27 @@ ], "usage": "hf mfu keygen [-hr] [-u ]" }, + "hf mfu list": { + "command": "hf mfu list", + "description": "Alias of `trace list -t 14a -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "notes": [ + "hf 14a list --frame -> show frame delay times", + "hf 14a list -1 -> use trace buffer" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-1, --buffer use data from trace buffer", + "--frame show frame delay times", + "-c mark CRC bytes", + "-r show relative times (gap and duration)", + "-u display times in microseconds instead of clock cycles", + "-x show hexdump to convert to pcap(ng)", + "or to import into Wireshark using encapsulation type \"ISO 14443\"", + "-f, --file filename of dictionary" + ], + "usage": "hf 14a list [-h1crux] [--frame] [-f ]" + }, "hf mfu ndefread": { "command": "hf mfu ndefread", "description": "Prints NFC Data Exchange Format (NDEF)", @@ -6500,7 +6789,7 @@ }, "hf mfu restore": { "command": "hf mfu restore", - "description": "Restore MIFARE Ultralight/NTAG dump file to tag.", + "description": "Restore MIFARE Ultralight/NTAG dump file (bin/eml/json) to tag.", "notes": [ "hf mfu restore -f myfile -s -> special write", "hf mfu restore -f myfile -k AABBCCDD -s -> special write, use key", @@ -6509,7 +6798,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file specify dump filename (bin/eml/json)", + "-f, --file Specify a filename for dump file", "-k, --key key for authentication (UL-C 16 bytes, EV1/NTAG 4 bytes)", "-l swap entered key's endianness", "-s enable special write UID -MAGIC TAG ONLY-", @@ -6556,16 +6845,16 @@ "offline": false, "options": [ "-h, --help This help", - "-t, --type <1..10> Simulation type to use", + "-t, --type <1..12> Simulation type to use", "-u, --uid <4|7|10> hex bytes UID", "-n, --num Exit simulation after blocks. 0 = infinite", "-v, --verbose Verbose output" ], - "usage": "hf mfu sim [-hv] -t <1..10> [-u ] [-n ]" + "usage": "hf mfu sim [-hv] -t <1..12> [-u ] [-n ]" }, "hf mfu tamper": { "command": "hf mfu tamper", - "description": "Set the congiguration of the NTAG 213TT tamper feature Supports: NTAG 213TT", + "description": "Set the configuration of the NTAG 213TT tamper feature Supports: NTAG 213TT", "notes": [ "hf mfu tamper -e -> enable tamper feature", "hf mfu tamper -d -> disable tamper feature", @@ -6591,7 +6880,7 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file Filename of dump", + "-f, --file Specify a filename for dump file", "-v, --verbose Verbose output" ], "usage": "hf mfu view [-hv] -f " @@ -6615,13 +6904,77 @@ ], "usage": "hf mfu wrbl [-hl] [-k ] -b -d [--force]" }, - "hf ntag424 help": { - "command": "hf ntag424 help", - "description": "help This help view Display content from tag dump file", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "hf ntag424 auth": { + "command": "hf ntag424 auth", + "description": "Authenticate with selected key against NTAG424.", + "notes": [ + "hf ntag424 auth --keyno 0 -k 00000000000000000000000000000000" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--keyno Key number", + "-k, --key Key for authenticate (HEX 16 bytes)" + ], + "usage": "hf ntag424 auth [-h] --keyno -k " + }, + "hf ntag424 changefs": { + "command": "hf ntag424 changefs", + "description": "Updates file settings for file, must be authenticated. This is a short explanation of the settings. See AN12196 for more information: options: byte with bit flags Bit: Setting: 6 Enable SDM and mirroring access: two byte access rights. Each nibble is a key number, or E for free access. Order is key for readwrite, change, read and write sdmoptions: byte with bit flags Bit: Setting: 0 ASCII encoding 4 SDMEncFileData 5 SDMReadCtrLimit 6 SDMReadCtr 7 SDMOptionsUID sdmaccess: two byte access rights. Each nibble is a key, or E for plain mirror and F for no mirroring Order is Reserved, SDMCtrRet, SDMMetaRead and SDMFileRead sdm_data: Three bytes of data used to control SDM settings. Can be specified multiple times. Data means different things depending on settings. Note: Not all of these settings will be written. It depends on the option byte, and the keys set. See AN12196 for more information. You must also start with sdmdata1, then sdmdata2, up to the number of sdm_data you want to write", + "notes": [ + "hf ntag424 changefs --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 40 -a 00E0 -s C1 -c F000 --data1 000020 --data2 000043 --data3 000043" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--fileno File number", + "--keyno Key number", + "-k, --key Key for authentication (HEX 16 bytes)", + "-o, --options File options byte (HEX 1 byte)", + "-a, --access File access settings (HEX 2 bytes)", + "-s, --sdmoptions SDM options (HEX 1 byte)", + "-c, --sdmaccess SDM access settings (HEX 2 bytes)", + "--data1 SDM data (HEX 3 bytes)", + "--data2 SDM data (HEX 3 bytes)", + "--data3 SDM data (HEX 3 bytes)", + "--data4 SDM data (HEX 3 bytes)", + "--data5 SDM data (HEX 3 bytes)", + "--data6 SDM data (HEX 3 bytes)", + "--data7 SDM data (HEX 3 bytes)", + "--data8 SDM data (HEX 3 bytes)" + ], + "usage": "hf ntag424 changefs [-h] --fileno --keyno -k [-o ] [-a ] [-s ] [-c ] [--data1 ] [--data2 ] [--data3 ] [--data4 ] [--data5 ] [--data6 ] [--data7 ] [--data8 ]" + }, + "hf ntag424 changekey": { + "command": "hf ntag424 changekey", + "description": "Change a key. Authentication key must currently be different to the one we want to change.", + "notes": [ + "hf ntag424 changekey --keyno 1 --oldkey 00000000000000000000000000000000 --newkey 11111111111111111111111111111111 --key0 00000000000000000000000000000000 --kv 1", + "hf ntag424 changekey --keyno 0 --newkey 11111111111111111111111111111111 --key0 00000000000000000000000000000000 --kv 1" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--keyno Key number to change", + "--oldkey Old key (only needed when changing key 1-4, HEX 16 bytes)", + "--newkey New key (HEX 16 bytes)", + "--key0 Authentication key (must be key 0, HEX 16 bytes)", + "--kv New key version number" + ], + "usage": "hf ntag424 changekey [-h] --keyno [--oldkey ] --newkey --key0 --kv " + }, + "hf ntag424 getfs": { + "command": "hf ntag424 getfs", + "description": "Read and print file settings for file", + "notes": [ + "hf ntag424 getfs --fileno 2" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--fileno File number" + ], + "usage": "hf ntag424 getfs [-h] --fileno " }, "hf ntag424 info": { "command": "hf ntag424 info", @@ -6635,17 +6988,25 @@ ], "usage": "hf ntag424 info [-h]" }, - "hf ntag424 sdm": { - "command": "hf ntag424 sdm", - "description": "Validate a SDM message", + "hf ntag424 read": { + "command": "hf ntag424 read", + "description": "Read and print data from file on NTAG424 tag. Will authenticate if key information is provided.", "notes": [ - "hf ntag424 sdm" + "hf ntag424 read --fileno 1 --keyno 0 -k 00000000000000000000000000000000 -o 0 -l 32", + "hf ntag424 read --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 0 -l 256", + "hf ntag424 read --fileno 3 --keyno 3 -k 00000000000000000000000000000000 -o 0 -l 128 -m encrypt" ], "offline": false, "options": [ - "-h, --help This help" + "-h, --help This help", + "--fileno <1|2|3> File number", + "--keyno Key number", + "-k, --key Key for authentication (HEX 16 bytes)", + "-o, --offset Offset to read in file (def 0)", + "-l, --length Number of bytes to read", + "-m, --cmode Communication mode" ], - "usage": "hf ntag424 sdm [-h]" + "usage": "hf ntag424 read [-h] --fileno <1|2|3> [--keyno ] [-k ] [-o ] -l [-m ]" }, "hf ntag424 view": { "command": "hf ntag424 view", @@ -6656,11 +7017,30 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file Filename of dump", + "-f, --file Specify a filename for dump file", "-v, --verbose Verbose output" ], "usage": "hf ntag424 view [-hv] -f " }, + "hf ntag424 write": { + "command": "hf ntag424 write", + "description": "Write data to file on NTAG424 tag. Will authenticate if key information is provided.", + "notes": [ + "hf ntag424 write --fileno 2 --keyno 0 -k 00000000000000000000000000000000 -o 0 -d 1122334455667788", + "hf ntag424 write --fileno 3 --keyno 3 -k 00000000000000000000000000000000 -o 0 -d 1122334455667788 -m encrypt" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--fileno <1|2|3> File number (def 2)", + "--keyno Key number", + "-k, --key Key for authentication (HEX 16 bytes)", + "-o, --offset Offset to write in file (def 0)", + "-d, --data Data to write", + "-m, --cmode Communication mode" + ], + "usage": "hf ntag424 write [-h] --fileno <1|2|3> [--keyno ] [-k ] [-o ] -d [-m ]" + }, "hf plot": { "command": "hf plot", "description": "Plots HF signal after RF signal path and A/D conversion.", @@ -6687,14 +7067,6 @@ ], "usage": "hf search [-hv]" }, - "hf seos help": { - "command": "hf seos help", - "description": "help This help list List SEOS history", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf seos info": { "command": "hf seos info", "description": "Get info from SEOS tags", @@ -6745,14 +7117,6 @@ ], "usage": "hf sniff [-h] [--sp ] [--st ] [--smode [none|drop|min|max|avg]] [--sratio ]" }, - "hf st25ta help": { - "command": "hf st25ta help", - "description": "help This help list List ISO 14443A/7816 history ndefread read NDEF file on tag", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf st25ta info": { "command": "hf st25ta info", "description": "Get info about ST25TA tag", @@ -6850,14 +7214,6 @@ ], "usage": "hf st25ta sim [-h] -u " }, - "hf tesla help": { - "command": "hf tesla help", - "description": "help This help list List ISO 14443A/7816 history", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf tesla info": { "command": "hf tesla info", "description": "Get info about TESLA Key tag", @@ -6891,14 +7247,6 @@ ], "usage": "hf tesla list [-h1crux] [--frame] [-f ]" }, - "hf texkom help": { - "command": "hf texkom help", - "description": "help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf texkom reader": { "command": "hf texkom reader", "description": "Read a texkom tag", @@ -6909,7 +7257,7 @@ "offline": false, "options": [ "-h, --help This help", - "-1 Use data from Graphbuffer", + "-1 Use data from Graphbuffer (offline mode)", "-v, --verbose Verbose scan and output", "-@ optional - continuous reader mode" ], @@ -6936,14 +7284,6 @@ ], "usage": "hf texkom sim [-hvt] [--raw ] [--id ] [--timeout ]" }, - "hf thinfilm help": { - "command": "hf thinfilm help", - "description": "help This help list List NFC Barcode / Thinfilm history - not correct", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf thinfilm info": { "command": "hf thinfilm info", "description": "Get info from Thinfilm tags", @@ -6993,24 +7333,17 @@ }, "hf topaz dump": { "command": "hf topaz dump", - "description": "Dump TOPAZ tag to binary file If no given, UID will be used as filename", + "description": "Dump TOPAZ tag to file (bin/json) If no given, UID will be used as filename", "notes": [ "hf topaz dump" ], "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump" + "-f, --file Specify a filename for dump file", + "--ns no save to file" ], - "usage": "hf topaz dump [-h] [-f ]" - }, - "hf topaz help": { - "command": "hf topaz help", - "description": "help This help list List Topaz history view Display content from tag dump file", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "usage": "hf topaz dump [-h] [-f ] [--ns]" }, "hf topaz info": { "command": "hf topaz info", @@ -7029,7 +7362,7 @@ }, "hf topaz list": { "command": "hf topaz list", - "description": "Alias of `trace list -t topaz` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "Alias of `trace list -t topaz -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf topaz list --frame -> show frame delay times", "hf topaz list -1 -> use trace buffer" @@ -7062,16 +7395,16 @@ }, "hf topaz rdbl": { "command": "hf topaz rdbl", - "description": "Read a block", + "description": "Read Topaz block", "notes": [ - "hf topaz rdbl -b 7" + "hf topaz rdbl --blk 7" ], "offline": false, "options": [ "-h, --help This help", - "-b, --block Block number to write" + "--blk Block number" ], - "usage": "hf topaz rdbl [-h] -b " + "usage": "hf topaz rdbl [-h] --blk " }, "hf topaz reader": { "command": "hf topaz reader", @@ -7121,27 +7454,27 @@ "offline": true, "options": [ "-h, --help This help", - "-f, --file filename of dump (bin/eml/json)" + "-f, --file Specify a filename for dump file" ], "usage": "hf topaz view [-h] -f " }, "hf topaz wrbl": { "command": "hf topaz wrbl", - "description": "Write a block", + "description": "Write Topaz block with 8 hex bytes of data", "notes": [ - "hf topaz wrbl -b 7 -d 1122334455667788" + "hf topaz wrbl --blk 7 -d 1122334455667788" ], "offline": false, "options": [ "-h, --help This help", - "-b, --block Block number to write", + "--blk Block number", "-d, --data Block data (8 hex bytes)" ], - "usage": "hf topaz wrbl [-h] -b -d " + "usage": "hf topaz wrbl [-h] --blk -d " }, "hf tune": { "command": "hf tune", - "description": "Continuously measure HF antenna tuning. Press button or to interrupt.", + "description": "Continuously measure HF antenna tuning. Press pm3 button or to interrupt.", "notes": [ "hf tune", "hf tune --mix" @@ -7156,13 +7489,38 @@ ], "usage": "hf tune [-h] [-n ] [--bar] [--mix] [--value]" }, - "hf waveshare help": { - "command": "hf waveshare help", - "description": "help This help", - "notes": [], + "hf vas decrypt": { + "command": "hf vas decrypt", + "description": "Decrypt a previously captured cryptogram", + "notes": [ + "hf vas decrypt --pid pass.com.passkit.pksamples.nfcdemo -f vas_privkey.der -d c0b77375eae416b79449347f9fe838c05cdb57dc7470b97b93b806cb348771d9bfbe29d58538c7c7d7c3d015fa205b68bfccd726058a62f7f44085ac98dbf877120fd9059f1507b956e0a6d56d0a" + ], "offline": true, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "--pid PID, pass type id", + "-f, --file path to terminal private key file", + "-d, --data cryptogram to decrypt" + ], + "usage": "hf vas decrypt [-h] [--pid ] [-f ] [-d ]" + }, + "hf vas reader": { + "command": "hf vas reader", + "description": "Read and decrypt Value Added Services (VAS) message", + "notes": [ + "hf vas reader --url https://example.com -> URL Only mode", + "hf vas reader --pid pass.com.passkit.pksamples.nfcdemo -f vas_privkey.der -@" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--pid PID, pass type id", + "-f, --file path to terminal private key file", + "--url a URL to provide to the mobile device", + "-@ continuous mode", + "-v, --verbose log additional information" + ], + "usage": "hf vas reader [-h@v] [--pid ] [-f ] [--url ]" }, "hf waveshare loadbmp": { "command": "hf waveshare loadbmp", @@ -7200,14 +7558,6 @@ ], "usage": "hf xerox dump [-hd] [-f ]" }, - "hf xerox help": { - "command": "hf xerox help", - "description": "help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hf xerox info": { "command": "hf xerox info", "description": "Tag information for ISO/IEC 14443 type B / XEROX based tags", @@ -7251,6 +7601,18 @@ ], "usage": "hints [-h10]" }, + "hw bootloader": { + "command": "hw bootloader", + "description": "Reboot Proxmark3 into bootloader mode", + "notes": [ + "hw bootloader" + ], + "offline": false, + "options": [ + "-h, --help This help" + ], + "usage": "hw bootloader [-h]" + }, "hw break": { "command": "hw break", "description": "send break loop package", @@ -7322,14 +7684,6 @@ ], "usage": "hw fpgaoff [-h]" }, - "hw help": { - "command": "hw help", - "description": "------------- ----------------------- Hardware ----------------------- help This help connect Connect Proxmark3 to serial port version Show version information about the client and the connected Proxmark3, if any", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "hw lcd": { "command": "hw lcd", "description": "Send command/data to LCD", @@ -7442,13 +7796,15 @@ "command": "hw status", "description": "Show runtime status information about the connected Proxmark3", "notes": [ - "hw status" + "hw status", + "hw status --ms 1000 -> Test connection speed with 1000ms timeout" ], "offline": false, "options": [ - "-h, --help This help" + "-h, --help This help", + "-m, --ms speed test timeout in micro seconds" ], - "usage": "hw status [-h]" + "usage": "hw status [-h] [-m ]" }, "hw tearoff": { "command": "hw tearoff", @@ -7480,6 +7836,21 @@ ], "usage": "hw tia [-h]" }, + "hw timeout": { + "command": "hw timeout", + "description": "Set the communication timeout on the client side", + "notes": [ + "hw timeout -> Show current timeout", + "hw timeout -m 20 -> Set the timeout to 20ms", + "hw timeout --ms 500 -> Set the timeout to 500ms" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-m, --ms timeout in micro seconds" + ], + "usage": "hw timeout [-h] [-m ]" + }, "hw tune": { "command": "hw tune", "description": "Measure antenna tuning", @@ -7555,14 +7926,6 @@ ], "usage": "lf awid demod [-h]" }, - "lf awid help": { - "command": "lf awid help", - "description": "help this help demod demodulate an AWID FSK tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf awid reader": { "command": "lf awid reader", "description": "read a AWID Prox tag", @@ -7631,11 +7994,29 @@ }, "lf config": { "command": "lf config", - "description": "Get/Set config for LF sampling, bit/sample, decimation, frequency These changes are temporary, will be reset after a power cycle.", - "notes": [], + "description": "Get/Set config for LF sampling, bit/sample, decimation, frequency These changes are temporary, will be reset after a power cycle. - use `lf read` performs a read (active field) - use `lf sniff` performs a sniff (no active field)", + "notes": [ + "lf config -> shows current config", + "lf config -b 8 --125 -> samples at 125 kHz, 8 bps", + "lf config -b 4 --134 --dec 3 -> samples at 134 kHz, averages three samples into one, stored with a resolution of 4 bits per sample", + "lf config --trig 20 -s 10000 -> trigger sampling when above 20, skip 10 000 first samples after triggered", + "lf config --reset -> reset back to default values" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "--125 125 kHz frequency", + "--134 134 kHz frequency", + "-a, --avg <0|1> averaging - if set, will average the stored sample value when decimating (default 1)", + "-b, --bps <1-8> sets resolution of bits per sample (default 8)", + "--dec <1-8> sets decimation. A value of N saves only 1 in N samples (default 1)", + "--divisor <19-255> Manually set freq divisor. 88 -> 134 kHz, 95 -> 125 kHz", + "-f, --freq <47-600> manually set frequency in kHz", + "-r, --reset reset values to defaults", + "-s, --skip sets a number of samples to skip before capture (default 0)", + "-t, --trig <0-128> sets trigger threshold. 0 means no threshold" + ], + "usage": "lf config [-hr] [--125] [--134] [-a <0|1>] [-b <1-8>] [--dec <1-8>] [--divisor <19-255>] [-f <47-600>] [-s ] [-t <0-128>]" }, "lf cotag demod": { "command": "lf cotag demod", @@ -7645,17 +8026,10 @@ ], "offline": true, "options": [ - "-h, --help This help" + "-h, --help This help", + "-v, --verbose verbose output" ], - "usage": "lf cotag demod [-h]" - }, - "lf cotag help": { - "command": "lf cotag help", - "description": "help This help demod demodulate an COTAG tag", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "usage": "lf cotag demod [-hv]" }, "lf cotag reader": { "command": "lf cotag reader", @@ -7701,14 +8075,6 @@ ], "usage": "lf destron demod [-h]" }, - "lf destron help": { - "command": "lf destron help", - "description": "help This help demod demodulate an Destron tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf destron reader": { "command": "lf destron reader", "description": "read a Destron tag", @@ -7792,14 +8158,6 @@ ], "usage": "lf em 410x demod [-hia] [--clk ] [--err ] [--len ]" }, - "lf em 410x help": { - "command": "lf em 410x help", - "description": "help This help demod demodulate a EM410x tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf em 410x reader": { "command": "lf em 410x reader", "description": "read EM 410x tag", @@ -7922,17 +8280,10 @@ "options": [ "-h, --help This help", "-p, --pwd password (00000000)", - "-f, --file override filename prefix (optional). Default is based on UID" + "-f, --file override filename prefix (optional). Default is based on UID", + "--ns no save to file" ], - "usage": "lf em 4x05 dump [-h] [-p ] [-f ]" - }, - "lf em 4x05 help": { - "command": "lf em 4x05 help", - "description": "help This help demod demodulate a EM4x05/EM4x69 tag from the GraphBuffer sniff Attempt to recover em4x05 commands from sample buffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "usage": "lf em 4x05 dump [-h] [-p ] [-f ] [--ns]" }, "lf em 4x05 info": { "command": "lf em 4x05 info", @@ -8039,15 +8390,19 @@ "command": "lf em 4x50 brute", "description": "Tries to bruteforce the password of a EM4x50 card. Function can be stopped by pressing pm3 button.", "notes": [ - "lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000" + "lf em 4x50 brute --mode range --begin 12330000 --end 12340000 -> tries pwds from 0x12330000 to 0x12340000", + "lf em 4x50 brute --mode charset --digits --uppercase -> tries all combinations of ASCII codes for digits and uppercase letters" ], "offline": false, "options": [ "-h, --help This help", - "--first first password (start), 4 bytes, lsb", - "--last last password (stop), 4 bytes, lsb" + "--mode Bruteforce mode (range|charset)", + "--begin Range mode - start of the key range", + "--end Range mode - end of the key range", + "--digits Charset mode - include ASCII codes for digits", + "--uppercase Charset mode - include ASCII codes for uppercase letters" ], - "usage": "lf em 4x50 brute [-h] --first --last " + "usage": "lf em 4x50 brute [-h] --mode [--begin ] [--end ] [--digits] [--uppercase]" }, "lf em 4x50 chk": { "command": "lf em 4x50 chk", @@ -8065,7 +8420,7 @@ }, "lf em 4x50 dump": { "command": "lf em 4x50 dump", - "description": "Reads all blocks/words from EM4x50 tag and saves dump in bin/eml/json format", + "description": "Reads all blocks/words from EM4x50 tag and saves dump in (bin/json) format", "notes": [ "lf em 4x50 dump", "lf em 4x50 dump -f mydump", @@ -8075,7 +8430,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file specify dump filename (bin/eml/json)", + "-f, --file specify dump filename", "-p, --pwd password, 4 hex bytes, lsb" ], "usage": "lf em 4x50 dump [-h] [-f ] [-p ]" @@ -8089,13 +8444,13 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file dump filename (bin/eml/json)" + "-f, --file Specify a filename for dump file" ], "usage": "lf em 4x50 eload [-h] -f " }, "lf em 4x50 esave": { "command": "lf em 4x50 esave", - "description": "Saves bin/eml/json dump file of emulator memory.", + "description": "Saves bin/json dump file of emulator memory.", "notes": [ "lf em 4x50 esave -> use UID as filename", "lf em 4x50 esave -f mydump" @@ -8119,14 +8474,6 @@ ], "usage": "lf em 4x50 eview [-h]" }, - "lf em 4x50 help": { - "command": "lf em 4x50 help", - "description": "help This help ----------- --------------------- operations --------------------- ----------- --------------------- simulation ---------------------", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf em 4x50 info": { "command": "lf em 4x50 info", "description": "Tag information EM4x50.", @@ -8198,7 +8545,7 @@ "options": [ "-h, --help This help", "-u, --uid uid, 4 hex bytes, msb", - "-f, --file specify dump filename (bin/eml/json)", + "-f, --file specify a filename for dump file", "-p, --pwd password, 4 hex bytes, lsb" ], "usage": "lf em 4x50 restore [-h] [-u ] [-f ] [-p ]" @@ -8292,14 +8639,6 @@ ], "usage": "lf em 4x70 brute [-h] [--par] -b --rnd --frn [-s ]" }, - "lf em 4x70 help": { - "command": "lf em 4x70 help", - "description": "help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf em 4x70 info": { "command": "lf em 4x70 info", "description": "Tag Information EM4x70 Tag variants include ID48 automotive transponder. ID48 does not use command parity (default). V4070 and EM4170 do require parity bit.", @@ -8374,14 +8713,6 @@ ], "usage": "lf em 4x70 writepin [-h] [--par] -p " }, - "lf em help": { - "command": "lf em help", - "description": "help This help 410x { EM 4102 commands... } 4x05 { EM 4205 / 4305 / 4369 / 4469 commands... } 4x50 { EM 4350 / 4450 commands... } 4x70 { EM 4070 / 4170 commands... }", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf fdxb clone": { "command": "lf fdxb clone", "description": "clone a FDX-B tag to a T55x7, Q5/T5555 or EM4305/4469 tag.", @@ -8415,14 +8746,6 @@ ], "usage": "lf fdxb demod [-h]" }, - "lf fdxb help": { - "command": "lf fdxb help", - "description": "help this help demod demodulate a FDX-B ISO11784/85 tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf fdxb reader": { "command": "lf fdxb reader", "description": "read a FDX-B animal tag Note that the continuous mode is less verbose", @@ -8487,14 +8810,6 @@ ], "usage": "lf gallagher demod [-h]" }, - "lf gallagher help": { - "command": "lf gallagher help", - "description": "help This help demod demodulate an GALLAGHER tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf gallagher reader": { "command": "lf gallagher reader", "description": "read a GALLAGHER tag", @@ -8560,14 +8875,6 @@ ], "usage": "lf gproxii demod [-h] [-r ]" }, - "lf gproxii help": { - "command": "lf gproxii help", - "description": "help this help demod demodulate a G Prox II tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf gproxii reader": { "command": "lf gproxii reader", "description": "read a Guardall tag", @@ -8597,14 +8904,6 @@ ], "usage": "lf gproxii sim [-h] --xor --fmt --fc --cn " }, - "lf help": { - "command": "lf help", - "description": "help This help ----------- -------------- Low Frequency -------------- awid { AWID RFIDs... } cotag { COTAG CHIPs... } destron { FDX-A Destron RFIDs... } em { EM CHIPs & RFIDs... } fdxb { FDX-B RFIDs... } gallagher { GALLAGHER RFIDs... } gproxii { Guardall Prox II RFIDs... } hid { HID Prox RFIDs... } hitag { Hitag CHIPs... } idteck { Idteck RFIDs... } indala { Indala RFIDs... } io { ioProx RFIDs... } jablotron { Jablotron RFIDs... } keri { KERI RFIDs... } motorola { Motorola RFIDs... } nedap { Nedap RFIDs... } nexwatch { NexWatch RFIDs... } noralsy { Noralsy RFIDs... } pac { PAC/Stanley RFIDs... } paradox { Paradox RFIDs... } pcf7931 { PCF7931 CHIPs... } presco { Presco RFIDs... } pyramid { Farpointe/Pyramid RFIDs... } securakey { Securakey RFIDs... } ti { TI CHIPs... } t55xx { T55xx CHIPs... } viking { Viking RFIDs... } visa2000 { Visa2000 RFIDs... } ----------- --------------------- General --------------------- search Read and Search for valid known tag", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf hid brute": { "command": "lf hid brute", "description": "Enables bruteforce of HID readers with specified facility code or card number. This is an attack against the reader. If the field being bruteforced is provided, it starts with it and goes up / down one step while maintaining other supplied values. If the field being bruteforced is not provided, it will iterate through the full range while maintaining other supplied values.", @@ -8669,14 +8968,6 @@ ], "usage": "lf hid demod [-h]" }, - "lf hid help": { - "command": "lf hid help", - "description": "help this help demod demodulate HID Prox tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf hid reader": { "command": "lf hid reader", "description": "read a HID Prox tag", @@ -8739,19 +9030,29 @@ }, "lf hitag dump": { "command": "lf hitag dump", - "description": "Read all card memory and save to fileIn password mode the default key is 4D494B52 (MIKR) In crypto mode the default key is 4F4E4D494B52 (ONMIKR) format: ISK high + ISK low.", + "description": "Read all Hitag 2 card memory and save to file Crypto mode key format: ISK high + ISK low", "notes": [ - "lf hitag dump -k 4F4E4D494B52", - "lf hitag dump -k 4D494B52" + "Password mode => use default key 4D494B52 (MIKR)", + "lf hitag dump --pwd", + "Short key = password mode", + "lf hitag dump -k 4D494B52", + "Challenge mode", + "lf hitag dump --nrar 0102030411223344", + "Crypto mode => use default key 4F4E4D494B52 (ONMIKR)", + "lf hitag dump --crypto", + "Long key = crypto mode", + "lf hitag dump -k 4F4E4D494B52" ], "offline": false, "options": [ "-h, --help This help", - "-f, --file specify file name", + "--pwd password mode", + "--nrar nonce / answer reader, 8 hex bytes", + "--crypto crypto mode", "-k, --key key, 4 or 6 hex bytes", - "--nrar nonce / answer reader, 8 hex bytes" + "-f, --file specify file name" ], - "usage": "lf hitag dump [-h] [-f ] [-k ] [--nrar ]" + "usage": "lf hitag dump [-h] [--pwd] [--nrar ] [--crypto] [-k ] [-f ]" }, "lf hitag eload": { "command": "lf hitag eload", @@ -8762,22 +9063,14 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file Specfiy dump filename", - "-1 Card type Hitag1", - "-2 Card type Hitag2", - "-s Card type HitagS", - "-m Card type HitagM" + "-f, --file Specify dump filename", + "-1, --ht1 Card type Hitag 1", + "-2, --ht2 Card type Hitag 2", + "-s, --hts Card type Hitag S", + "-m, --htm Card type Hitag \u03bc" ], "usage": "lf hitag eload [-h12sm] -f " }, - "lf hitag help": { - "command": "lf hitag help", - "description": "help This help list List Hitag trace history", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf hitag info": { "command": "lf hitag info", "description": "Hitag2 tag information", @@ -8811,46 +9104,54 @@ ], "usage": "lf hitag list [-h1crux] [--frame] [-f ]" }, - "lf hitag reader": { - "command": "lf hitag reader", - "description": "Act like a Hitag Reader", + "lf hitag read": { + "command": "lf hitag read", + "description": "Read Hitag memory Crypto mode key format: ISK high + ISK low", "notes": [ - "Hitag S", - "lf hitag reader --01 --nrar 0102030411223344", - "lf hitag reader --02 -k 4F4E4D494B52", - "Hitag 2", - "lf hitag reader --21 -k 4D494B52", - "lf hitag reader --22 --nrar 0102030411223344", - "lf hitag reader --23 -k 4F4E4D494B52", - "lf hitag reader --26" + "Hitag S, plain mode", + "lf hitag read --hts", + "Hitag S, challenge mode", + "lf hitag read --hts --nrar 0102030411223344", + "Hitag S, crypto mode => use default key 4F4E4D494B52 (ONMIKR)", + "lf hitag read --hts --crypto", + "Hitag S, long key = crypto mode", + "lf hitag read --hts -k 4F4E4D494B52", + "", + "Hitag 2, password mode => use default key 4D494B52 (MIKR)", + "lf hitag read --ht2 --pwd", + "Hitag 2, providing a short key = password mode", + "lf hitag read --ht2 -k 4D494B52", + "Hitag 2, challenge mode", + "lf hitag read --ht2 --nrar 0102030411223344", + "Hitag 2, crypto mode => use default key 4F4E4D494B52 (ONMIKR)", + "lf hitag read --ht2 --crypto", + "Hitag 2, providing a long key = crypto mode", + "lf hitag read --ht2 -k 4F4E4D494B52" ], "offline": false, "options": [ "-h, --help This help", - "--01 HitagS, read all pages, challenge mode", - "--02 HitagS, read all pages, crypto mode. Set key=0 for no auth", - "--21 Hitag2, read all pages, password mode. def 4D494B52 (MIKR)", - "--22 Hitag2, read all pages, challenge mode", - "--23 Hitag2, read all pages, crypto mode. Key ISK high + ISK low. def 4F4E4D494B52 (ONMIKR)", - "--25 Hitag2, test recorded authentications (replay?)", - "--26 Hitag2, read UID", - "-k, --key key, 4 or 6 hex bytes", - "--nrar nonce / answer reader, 8 hex bytes" + "-s, --hts Hitag S", + "-2, --ht2 Hitag 2", + "--pwd password mode", + "--nrar nonce / answer writer, 8 hex bytes", + "--crypto crypto mode", + "-k, --key key, 4 or 6 hex bytes" ], - "usage": "lf hitag reader [-h] [--01] [--02] [--21] [--22] [--23] [--25] [--26] [-k ] [--nrar ]" + "usage": "lf hitag read [-hs2] [--pwd] [--nrar ] [--crypto] [-k ]" }, "lf hitag sim": { "command": "lf hitag sim", - "description": "Simulate Hitag2 / HitagS transponder You need to `lf hitag eload` first", + "description": "Simulate Hitag transponder You need to `lf hitag eload` first", "notes": [ "lf hitag sim -2" ], "offline": false, "options": [ "-h, --help This help", - "-1 simulate Hitag1", - "-2 simulate Hitag2", - "-s simulate HitagS" + "-1, --ht1 simulate Hitag 1", + "-2, --ht2 simulate Hitag 2", + "-s, --hts simulate Hitag S" ], "usage": "lf hitag sim [-h12s]" }, @@ -8866,30 +9167,43 @@ ], "usage": "lf hitag sniff [-h]" }, - "lf hitag writer": { - "command": "lf hitag writer", - "description": "Act like a Hitag writerIn password mode the default key is 4D494B52 (MIKR) In crypto mode the default key is 4F4E4D494B52 (ONMIKR) format: ISK high + ISK low.", + "lf hitag wrbl": { + "command": "lf hitag wrbl", + "description": "Write a page in Hitag memory Crypto mode key format: ISK high + ISK low", "notes": [ - "Hitag S", - "lf hitag writer --03 --nrar 0102030411223344 -p 3 -d 01020304", - "lf hitag writer --04 -k 4F4E4D494B52 -p 3 -d 01020304", - "Hitag 2", - "lf hitag writer --24 -k 4F4E4D494B52 -p 3 -d 01020304", - "lf hitag writer --27 -k 4D494B52 -p 3 -d 01020304" + "Hitag S, plain mode", + "lf hitag wrbl --hts -p 6 -d 01020304", + "Hitag S, challenge mode", + "lf hitag wrbl --hts --nrar 0102030411223344 -p 6 -d 01020304", + "Hitag S, crypto mode => use default key 4F4E4D494B52 (ONMIKR)", + "lf hitag wrbl --hts --crypto -p 6 -d 01020304", + "Hitag S, long key = crypto mode", + "lf hitag wrbl --hts -k 4F4E4D494B52 -p 6 -d 01020304", + "", + "Hitag 2, password mode => use default key 4D494B52 (MIKR)", + "lf hitag wrbl --ht2 --pwd -p 6 -d 01020304", + "Hitag 2, providing a short key = password mode", + "lf hitag wrbl --ht2 -k 4D494B52 -p 6 -d 01020304", + "Hitag 2, challenge mode", + "lf hitag wrbl --ht2 --nrar 0102030411223344 -p 6 -d 01020304", + "Hitag 2, crypto mode => use default key 4F4E4D494B52 (ONMIKR)", + "lf hitag wrbl --ht2 --crypto -p 6 -d 01020304", + "Hitag 2, providing a long key = crypto mode", + "lf hitag wrbl --ht2 -k 4F4E4D494B52 -p 6 -d 01020304" ], "offline": false, "options": [ "-h, --help This help", - "--03 HitagS, write page, challenge mode", - "--04 HitagS, write page, crypto mode. Set key=0 for no auth", - "--24 Hitag2, write page, crypto mode.", - "--27 Hitag2, write page, password mode", - "-p, --page page address to write to", - "-d, --data data, 4 hex bytes", + "-s, --hts Hitag S", + "-2, --ht2 Hitag 2", + "--pwd password mode", + "--nrar nonce / answer writer, 8 hex bytes", + "--crypto crypto mode", "-k, --key key, 4 or 6 hex bytes", - "--nrar nonce / answer writer, 8 hex bytes" + "-p, --page page address to write to", + "-d, --data data, 4 hex bytes" ], - "usage": "lf hitag writer [-h] [--03] [--04] [--24] [--27] -p [-d ] [-k ] [--nrar ]" + "usage": "lf hitag wrbl [-hs2] [--pwd] [--nrar ] [--crypto] [-k ] -p -d " }, "lf idteck clone": { "command": "lf idteck clone", @@ -8918,14 +9232,6 @@ ], "usage": "lf idteck demod [-h]" }, - "lf idteck help": { - "command": "lf idteck help", - "description": "help This help demod demodulate an Idteck tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf idteck reader": { "command": "lf idteck reader", "description": "read a Idteck tag", @@ -8990,11 +9296,26 @@ }, "lf indala clone": { "command": "lf indala clone", - "description": "clone Indala UID to T55x7 or Q5/T5555 tag using different known formats", - "notes": [], + "description": "clone Indala UID to T55x7 or Q5/T5555 tag using different known formats Warning, encoding with FC/CN doesn't always work", + "notes": [ + "lf indala clone --heden 888", + "lf indala clone --fc 123 --cn 1337", + "lf indala clone --fc 123 --cn 1337 --4041x", + "lf indala clone -r a0000000a0002021", + "lf indala clone -r 80000001b23523a6c2e31eba3cbee4afb3c6ad1fcf649393928c14e5" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-r, --raw raw bytes", + "--heden Card number for Heden 2L format", + "--fc Facility code (26 bit H10301 format)", + "--cn Card number (26 bit H10301 format)", + "--q5 Optional - specify writing to Q5/T5555 tag", + "--em Optional - specify writing to EM4305/4469 tag", + "--4041x Optional - specify Indala 4041X format, must use with fc and cn" + ], + "usage": "lf indala clone [-h] [-r ] [--heden ] [--fc ] [--cn ] [--q5] [--em] [--4041x]" }, "lf indala demod": { "command": "lf indala demod", @@ -9014,14 +9335,6 @@ ], "usage": "lf indala demod [-hi] [--clock ] [--maxerr ]" }, - "lf indala help": { - "command": "lf indala help", - "description": "help This help demod Demodulate an Indala tag (PSK1) from the GraphBuffer altdemod Alternative method to demodulate samples for Indala 64 bit UID (option '224' for 224 bit)", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf indala reader": { "command": "lf indala reader", "description": "read a Indala tag", @@ -9088,14 +9401,6 @@ ], "usage": "lf io demod [-h]" }, - "lf io help": { - "command": "lf io help", - "description": "help this help demod demodulate an ioProx tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf io reader": { "command": "lf io reader", "description": "read a ioProx tag", @@ -9165,14 +9470,6 @@ ], "usage": "lf jablotron demod [-h]" }, - "lf jablotron help": { - "command": "lf jablotron help", - "description": "help This help demod demodulate an Jablotron tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf jablotron reader": { "command": "lf jablotron reader", "description": "read a jablotron tag", @@ -9229,14 +9526,6 @@ ], "usage": "lf keri demod [-h]" }, - "lf keri help": { - "command": "lf keri help", - "description": "help This help demod demodulate an KERI tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf keri reader": { "command": "lf keri reader", "description": "read a keri tag", @@ -9282,7 +9571,7 @@ }, "lf motorola demod": { "command": "lf motorola demod", - "description": "Try to find Motorola preamble, if found decode / descramble data", + "description": "Try to find Motorola Flexpass preamble, if found decode / descramble data", "notes": [ "lf motorola demod" ], @@ -9292,17 +9581,9 @@ ], "usage": "lf motorola demod [-h]" }, - "lf motorola help": { - "command": "lf motorola help", - "description": "help This help demod demodulate an MOTOROLA tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf motorola reader": { "command": "lf motorola reader", - "description": "read a Motorola tag", + "description": "read a Motorola Flexpass tag", "notes": [ "lf motorola reader -@ -> continuous reader mode" ], @@ -9355,14 +9636,6 @@ ], "usage": "lf nedap demod [-h]" }, - "lf nedap help": { - "command": "lf nedap help", - "description": "help This help demod demodulate Nedap tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf nedap reader": { "command": "lf nedap reader", "description": "read a Nedap tag", @@ -9429,14 +9702,6 @@ ], "usage": "lf nexwatch demod [-h]" }, - "lf nexwatch help": { - "command": "lf nexwatch help", - "description": "help This help demod demodulate a NexWatch tag (nexkey, quadrakey) from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf nexwatch reader": { "command": "lf nexwatch reader", "description": "read a Nexwatch tag", @@ -9503,14 +9768,6 @@ ], "usage": "lf noralsy demod [-h]" }, - "lf noralsy help": { - "command": "lf noralsy help", - "description": "help This help demod demodulate an Noralsy tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf noralsy reader": { "command": "lf noralsy reader", "description": "read a Noralsy tag", @@ -9570,14 +9827,6 @@ ], "usage": "lf pac demod [-h]" }, - "lf pac help": { - "command": "lf pac help", - "description": "help This help demod demodulate a PAC tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf pac reader": { "command": "lf pac reader", "description": "read a PAC/Stanley tag", @@ -9630,47 +9879,45 @@ "command": "lf paradox demod", "description": "Try to find Paradox preamble, if found decode / descramble data", "notes": [ - "lf paradox demod" + "lf paradox demod --old -> Display previous checksum version" ], "offline": true, "options": [ - "-h, --help This help" + "-h, --help This help", + "--old optional - Display previous checksum version" ], - "usage": "lf paradox demod [-h]" - }, - "lf paradox help": { - "command": "lf paradox help", - "description": "help This help demod demodulate a Paradox FSK tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "usage": "lf paradox demod [-h] [--old]" }, "lf paradox reader": { "command": "lf paradox reader", "description": "read a Paradox tag", "notes": [ - "lf Paradox reader -@ -> continuous reader mode" + "lf paradox reader -@ -> continuous reader mode", + "lf paradox reader --old -> Display previous checksum version" ], "offline": false, "options": [ "-h, --help This help", - "-@ optional - continuous reader mode" + "-@ optional - continuous reader mode", + "--old optional - Display previous checksum version" ], - "usage": "lf paradox reader [-h@]" + "usage": "lf paradox reader [-h@] [--old]" }, "lf paradox sim": { "command": "lf paradox sim", "description": "Enables simulation of paradox card with specified card number. Simulation runs until the button is pressed or another USB command is issued.", "notes": [ - "lf paradox sim --raw 0f55555695596a6a9999a59a" + "lf paradox sim --raw 0f55555695596a6a9999a59a -> simulate tag", + "lf paradox sim --fc 96 --cn 40426 -> simulate tag with fc and cn" ], "offline": false, "options": [ "-h, --help This help", - "-r, --raw raw hex data. 12 bytes" + "-r, --raw raw hex data. 12 bytes", + "--fc facility code", + "--cn card number" ], - "usage": "lf paradox sim [-h] [-r ]" + "usage": "lf paradox sim [-h] [-r ] [--fc ] [--cn ]" }, "lf pcf7931 config": { "command": "lf pcf7931 config", @@ -9691,14 +9938,6 @@ ], "usage": "lf pcf7931 config [-hr] [-p ] [-d ] [--lw ] [--lp ]" }, - "lf pcf7931 help": { - "command": "lf pcf7931 help", - "description": "help This help config Configure the password, the tags initialization delay and time offsets (optional)", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf pcf7931 reader": { "command": "lf pcf7931 reader", "description": "read a PCF7931 tag", @@ -9757,14 +9996,6 @@ ], "usage": "lf presco demod [-h]" }, - "lf presco help": { - "command": "lf presco help", - "description": "help This help demod demodulate Presco tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf presco reader": { "command": "lf presco reader", "description": "read a presco tag", @@ -9824,14 +10055,6 @@ ], "usage": "lf pyramid demod [-h]" }, - "lf pyramid help": { - "command": "lf pyramid help", - "description": "help this help demod demodulate a Pyramid FSK tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf pyramid reader": { "command": "lf pyramid reader", "description": "read a Farpointe/Pyramid tag", @@ -9850,7 +10073,7 @@ "description": "Enables simulation of Farpointe/Pyramid card with specified card number. Simulation runs until the button is pressed or another USB command is issued. The facility-code is 8-bit and the card number is 16-bit. Larger values are truncated. Currently work only on 26bit", "notes": [ "lf pyramid sim --fc 123 --cn 1337", - "lf pyramid clone --raw 0001010101010101010440013223921c" + "lf pyramid sim --raw 0001010101010101010440013223921c" ], "offline": false, "options": [ @@ -9863,7 +10086,7 @@ }, "lf read": { "command": "lf read", - "description": "Sniff low frequency signal. - use `lf config` to set parameters. - use `data plot` to look at it", + "description": "Sniff low frequency signal. - use `lf config` to set parameters. - use `data plot` to look at it. If the number of samples is more than the device memory limit (40000 now), it will try to use the real-time sampling mode.", "notes": [ "lf read -v -s 12000 -> collect 12000 samples", "lf read -s 3000 -@ -> oscilloscope style" @@ -9882,16 +10105,16 @@ "description": "Read and search for valid known tag. For offline mode, you can `data load` first then search.", "notes": [ "lf search -> try reading data from tag & search for known tag", - "lf search -1 -> use data from the GraphBuffer & search for known tag", "lf search -u -> try reading data from tag & search for known and unknown tag", - "lf search -1u -> use data from the GraphBuffer & search for known and unknown tag" + "lf search -1 -> use data from the GraphBuffer & search for known tag", + "lf search -1uc -> use data from the GraphBuffer & search for known and unknown tag" ], "offline": true, "options": [ "-h, --help This help", - "-1 Use data from Graphbuffer to search", - "-c Continue searching even after a first hit", - "-u Search for unknown tags. If not set, reads only known tags" + "-1 Use data from Graphbuffer to search (offline mode)", + "-c Continue searching after successful match", + "-u Search for unknown tags" ], "usage": "lf search [-h1cu]" }, @@ -9924,14 +10147,6 @@ ], "usage": "lf securakey demod [-h]" }, - "lf securakey help": { - "command": "lf securakey help", - "description": "help This help demod demodulate an Securakey tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf securakey reader": { "command": "lf securakey reader", "description": "read a Securakey tag", @@ -10010,11 +10225,27 @@ }, "lf simfsk": { "command": "lf simfsk", - "description": "Simulate FSK tag from DemodBuffer or input. There are about four FSK modulations to know of. FSK1 - where fc/8 = high and fc/5 = low FSK1a - is inverted FSK1, ie: fc/5 = high and fc/8 = low FSK2 - where fc/10 = high and fc/8 = low FSK2a - is inverted FSK2, ie: fc/10 = high and fc/8 = low", - "notes": [], + "description": "Simulate FSK tag from DemodBuffer or input. There are about four FSK modulations to know of. FSK1 - where fc/8 = high and fc/5 = low FSK1a - is inverted FSK1, ie: fc/5 = high and fc/8 = low FSK2 - where fc/10 = high and fc/8 = low FSK2a - is inverted FSK2, ie: fc/10 = high and fc/8 = low NOTE: if you set one clock manually set them all manually", + "notes": [ + "lf simfsk -c 40 --high 8 --low 5 -d 010203 -> FSK1 rf/40 data 010203", + "lf simfsk -c 40 --high 5 --low 8 -d 010203 -> FSK1a rf/40 data 010203", + "lf simfsk -c 64 --high 10 --low 8 -d 010203 -> FSK2 rf/64 data 010203", + "lf simfsk -c 64 --high 8 --low 10 -d 010203 -> FSK2a rf/64 data 010203", + "", + "lf simfsk -c 50 --high 10 --low 8 -d 1D5559555569A9A555A59569 -> simulate HID Prox tag manually", + "lf simfsk -c 50 --high 10 --low 8 --stt -d 011DB2487E8D811111111111 -> simulate AWID tag manually" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-c, --clk manually set clock - can autodetect if using DemodBuffer (default 64)", + "--low manually set larger Field Clock", + "--high manually set smaller Field Clock", + "--stt TBD! - STT to enable a gap between playback repetitions (default: no gap)", + "-d, --data data to sim - omit to use DemodBuffer", + "-v, --verbose verbose output" + ], + "usage": "lf simfsk [-hv] [-c ] [--low ] [--high ] [--stt] [-d ]" }, "lf simpsk": { "command": "lf simpsk", @@ -10040,19 +10271,38 @@ }, "lf sniff": { "command": "lf sniff", - "description": "Sniff low frequency signal. You need to configure the LF part on the Proxmark3 device manually. Usually a trigger and skip samples is a good thing to set before doing a low frequency sniff.", - "notes": [], + "description": "Sniff low frequency signal. You need to configure the LF part on the Proxmark3 device manually. Usually a trigger and skip samples is a good thing to set before doing a low frequency sniff. - use `lf config` to set parameters. - use `data plot` to look at sniff signal. - use `lf search -1` to see if signal can be automatic decoded. If the number of samples is more than the device memory limit (40000 now), it will try to use the real-time sampling mode.", + "notes": [ + "lf sniff -v", + "lf sniff -s 3000 -@ -> oscilloscope style" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-s, --samples number of samples to collect", + "-v, --verbose verbose output", + "-@ continuous sniffing mode" + ], + "usage": "lf sniff [-hv@] [-s ]" }, "lf t55xx bruteforce": { "command": "lf t55xx bruteforce", - "description": "This command uses bruteforce to scan a number range. Try reading Page 0, block 7 before.", - "notes": [], + "description": "This command uses bruteforce to scan a number range. Try reading Page 0, block 7 before. WARNING this may brick non-password protected chips!", + "notes": [ + "lf t55xx bruteforce --r2 -s aaaaaa77 -e aaaaaa99" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-s, --start search start password (4 hex bytes)", + "-e, --end search end password (4 hex bytes)", + "--r0 downlink - fixed bit length", + "--r1 downlink - long leading reference", + "--r2 downlink - leading zero", + "--r3 downlink - 1 of 4 coding reference", + "--all try all downlink modes (def)" + ], + "usage": "lf t55xx bruteforce [-h] -s -e [--r0] [--r1] [--r2] [--r3] [--all]" }, "lf t55xx chk": { "command": "lf t55xx chk", @@ -10187,7 +10437,7 @@ }, "lf t55xx dump": { "command": "lf t55xx dump", - "description": "This command dumps a T55xx card Page 0 block 0-7. It will create three files (bin/eml/json)", + "description": "This command dumps a T55xx card Page 0 block 0-7. It will create two files (bin/json)", "notes": [ "lf t55xx dump", "lf t55xx dump -p aabbccdd --override", @@ -10199,20 +10449,13 @@ "-f, --file filename (default is generated on blk 0)", "-o, --override override, force pwd read despite danger to card", "-p, --pwd password (4 hex bytes)", + "--ns no save", "--r0 downlink - fixed bit length", "--r1 downlink - long leading reference", "--r2 downlink - leading zero", "--r3 downlink - 1 of 4 coding reference" ], - "usage": "lf t55xx dump [-ho] [-f ] [-p ] [--r0] [--r1] [--r2] [--r3]" - }, - "lf t55xx help": { - "command": "lf t55xx help", - "description": "----------- ---------------------------- notice ----------------------------- Remember to run `lf t55xx detect` first whenever a new card is placed on the Proxmark3 or the config block changed. help This help ----------- --------------------- operations --------------------- config Set/Get T55XX configuration (modulation, inverted, offset, rate) detect Try detecting the tag modulation from reading the configuration block info Show T55x7 configuration data (page 0/ blk 0) trace Show T55x7 traceability data (page 1/ blk 0-1) ----------- --------------------- recovery --------------------- sniff Attempt to recover T55xx commands from sample buffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "usage": "lf t55xx dump [-ho] [-f ] [-p ] [--ns] [--r0] [--r1] [--r2] [--r3]" }, "lf t55xx info": { "command": "lf t55xx info", @@ -10280,19 +10523,45 @@ }, "lf t55xx read": { "command": "lf t55xx read", - "description": "Read T55xx block data. This commands defaults to page 0.", - "notes": [], + "description": "Read T55xx block data. This commands defaults to page 0. * * * WARNING * * * Use of read with password on a tag not configured for a password can damage the tag * * * * * * * * * *", + "notes": [ + "lf t55xx read -b 0 -> read data from block 0", + "lf t55xx read -b 0 --pwd 01020304 -> read data from block 0, pwd 01020304", + "lf t55xx read -b 0 --pwd 01020304 -o -> read data from block 0, pwd 01020304, override" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-b, --blk <0-7> block number to read", + "-p, --pwd password (4 hex bytes)", + "-o, --override override safety check", + "--pg1 read page 1", + "--r0 downlink - fixed bit length (detected def)", + "--r1 downlink - long leading reference", + "--r2 downlink - leading zero", + "--r3 downlink - 1 of 4 coding reference" + ], + "usage": "lf t55xx read [-ho] -b <0-7> [-p ] [--pg1] [--r0] [--r1] [--r2] [--r3]" }, "lf t55xx recoverpw": { "command": "lf t55xx recoverpw", - "description": "This command uses a few tricks to try to recover mangled password. Try reading Page 0, block 7 before.", - "notes": [], + "description": "This command uses a few tricks to try to recover mangled password. Try reading Page 0, block 7 before. WARNING this may brick non-password protected chips!", + "notes": [ + "lf t55xx recoverpw", + "lf t55xx recoverpw -p 11223344", + "lf t55xx recoverpw -p 11223344 --r3" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-p, --pwd password (4 hex bytes)", + "--r0 downlink - fixed bit length", + "--r1 downlink - long leading reference", + "--r2 downlink - leading zero", + "--r3 downlink - 1 of 4 coding reference", + "--all try all downlink modes (def)" + ], + "usage": "lf t55xx recoverpw [-h] [-p ] [--r0] [--r1] [--r2] [--r3] [--all]" }, "lf t55xx resetread": { "command": "lf t55xx resetread", @@ -10320,7 +10589,7 @@ "offline": false, "options": [ "-h, --help This help", - "-f, --file filename of dump file", + "-f, --file Specify a filename for dump file", "-p, --pwd password if target card has password set (4 hex bytes)" ], "usage": "lf t55xx restore [-h] [-f ] [-p ]" @@ -10448,14 +10717,6 @@ ], "usage": "lf ti demod [-h]" }, - "lf ti help": { - "command": "lf ti help", - "description": "help This help demod Demodulate raw bits for TI LF tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf ti reader": { "command": "lf ti reader", "description": "read a TI tag", @@ -10532,14 +10793,6 @@ ], "usage": "lf viking demod [-h]" }, - "lf viking help": { - "command": "lf viking help", - "description": "help This help demod demodulate a Viking tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf viking reader": { "command": "lf viking reader", "description": "read a Viking AM tag", @@ -10595,14 +10848,6 @@ ], "usage": "lf visa2000 demod [-h]" }, - "lf visa2000 help": { - "command": "lf visa2000 help", - "description": "help This help demod demodulate an VISA2000 tag from the GraphBuffer", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "lf visa2000 reader": { "command": "lf visa2000 reader", "description": "read a visa2000 tag", @@ -10661,14 +10906,6 @@ ], "usage": "mem dump [-hv] [-o ] [-l ] [-f ] [-c ]" }, - "mem help": { - "command": "mem help", - "description": "help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "mem info": { "command": "mem info", "description": "Collect signature and verify it from flash memory", @@ -10736,25 +10973,18 @@ "command": "mem spiffs dump", "description": "Dumps device SPIFFS file to a local file Size is handled by first sending a STAT command against file to verify existence", "notes": [ - "mem spiffs dump -s tag.bin -> download binary file from device", - "mem spiffs dump -s tag.bin -d aaa -e -> download tag.bin, save as aaa.eml format" + "mem spiffs dump -s tag.bin -> download binary file from device, saved as `tag.bin`", + "mem spiffs dump -s tag.bin -d a001 -> download tag.bin, save as `a001.bin`", + "mem spiffs dump -s tag.bin -t -> download tag.bin into trace buffer" ], "offline": false, "options": [ "-h, --help This help", "-s, --src SPIFFS file to save", "-d, --dest file name to save to ", - "-e, --eml also save in EML format" + "-t, --trace download into trace buffer" ], - "usage": "mem spiffs dump [-he] -s [-d ]" - }, - "mem spiffs help": { - "command": "mem spiffs help", - "description": "help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" + "usage": "mem spiffs dump [-ht] -s [-d ]" }, "mem spiffs info": { "command": "mem spiffs info", @@ -10859,7 +11089,7 @@ }, "mem spiffs view": { "command": "mem spiffs view", - "description": "View a file on flash memory on devicer in console", + "description": "View a file on flash memory on device in console", "notes": [ "mem spiffs view -f tag.bin" ], @@ -10867,7 +11097,7 @@ "options": [ "-h, --help This help", "-f, --file SPIFFS file to view", - "-c, --cols column breaks (def 32)" + "-c, --cols column breaks (def 16)" ], "usage": "mem spiffs view [-h] -f [-c ]" }, @@ -10909,14 +11139,6 @@ ], "usage": "msleep [-h] [-t ]" }, - "nfc barcode help": { - "command": "nfc barcode help", - "description": "-------- ------------------ NFC Barcode -------------------- -------- --------------------- General --------------------- help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "nfc barcode read": { "command": "nfc barcode read", "description": "Get info from Thinfilm tags", @@ -10960,21 +11182,24 @@ ], "usage": "nfc decode [-hv] [-d ] [-f ]" }, - "nfc help": { - "command": "nfc help", - "description": "-------- --------------------- NFC Tags -------------------- type1 { NFC Forum Tag Type 1... } type2 { NFC Forum Tag Type 2... } type4a { NFC Forum Tag Type 4 ISO14443A... } type4b { NFC Forum Tag Type 4 ISO14443B... } mf { NFC Type MIFARE Classic/Plus Tag... } barcode { NFC Barcode Tag... } -------- --------------------- General --------------------- help This help decode Decode NDEF records", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "nfc mf cformat": { "command": "nfc mf cformat", - "description": "format MIFARE Classic Tag as a NFC tag with Data Exchange Format (NDEF) If no given, UID will be used as filename. It will try default keys and MAD keys to detect if tag is already formatted in order to write.", - "notes": [], + "description": "format MIFARE Classic Tag as a NFC tag with Data Exchange Format (NDEF) If no given, UID will be used as filename. It will try default keys and MAD keys to detect if tag is already formatted in order to write. If not, it will try finding a key file based on your UID. ie, if you ran autopwn before", + "notes": [ + "hf mf ndefformat", + "hf mf ndefformat --1k -> MIFARE Classic 1k", + "hf mf ndefformat --keys hf-mf-01020304-key.bin -> MIFARE 1k with keys from specified file" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help", + "-k, --keys filename of keys", + "--mini MIFARE Classic Mini / S20", + "--1k MIFARE Classic 1k / S50 (def)", + "--2k MIFARE Classic/Plus 2k", + "--4k MIFARE Classic 4k / S70" + ], + "usage": "hf mf ndefformat [-h] [-k ] [--mini] [--1k] [--2k] [--4k]" }, "nfc mf cread": { "command": "nfc mf cread", @@ -11018,14 +11243,6 @@ ], "usage": "hf mf ndefwrite [-hpv] [-d ] [-f ] [--mini] [--1k] [--2k] [--4k]" }, - "nfc mf help": { - "command": "nfc mf help", - "description": "-------- --------- NFC Type MIFARE Classic/Plus Tag -------- -------- --------------------- General --------------------- help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "nfc mf pread": { "command": "nfc mf pread", "description": "Prints NFC Data Exchange Format (NDEF)", @@ -11046,14 +11263,6 @@ ], "usage": "hf mfp ndefread [-hvb] [--aid ] [-k ] [-f ]" }, - "nfc type1 help": { - "command": "nfc type1 help", - "description": "-------- -------------- NFC Forum Tag Type 1 --------------- -------- --------------------- General --------------------- help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "nfc type1 read": { "command": "nfc type1 read", "description": "Get info from Topaz tags", @@ -11069,14 +11278,6 @@ ], "usage": "hf topaz info [-hv] [-f ]" }, - "nfc type2 help": { - "command": "nfc type2 help", - "description": "-------- -------------- NFC Forum Tag Type 2 --------------- -------- --------------------- General --------------------- help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "nfc type2 read": { "command": "nfc type2 read", "description": "Prints NFC Data Exchange Format (NDEF)", @@ -11107,14 +11308,6 @@ ], "usage": "hf 14a ndefformat [-hv]" }, - "nfc type4a help": { - "command": "nfc type4a help", - "description": "-------- --------- NFC Forum Tag Type 4 ISO14443A ---------- -------- --------------------- General --------------------- help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "nfc type4a read": { "command": "nfc type4a read", "description": "Read NFC Data Exchange Format (NDEF) file on Type 4 NDEF tag", @@ -11164,14 +11357,6 @@ ], "usage": "hf 14a ndefwrite [-hpv] [-d ] [-f ]" }, - "nfc type4b help": { - "command": "nfc type4b help", - "description": "-------- --------- NFC Forum Tag Type 4 ISO14443B ------------- -------- --------------------- General --------------------- help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "nfc type4b read": { "command": "nfc type4b read", "description": "Print NFC Data Exchange Format (NDEF)", @@ -11228,14 +11413,6 @@ ], "usage": "piv getdata [-hskatw] [--aid ] " }, - "piv help": { - "command": "piv help", - "description": "help This help list List ISO7816 history", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "piv list": { "command": "piv list", "description": "Alias of `trace list -t 7816` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", @@ -11307,29 +11484,41 @@ ], "usage": "prefs get barmode [-h]" }, - "prefs get clientdebug": { - "command": "prefs get clientdebug", + "prefs get client.debug": { + "command": "prefs get client.debug", "description": "Get preference of using clientside debug level", "notes": [ - "prefs get clientdebug" + "prefs get client.debug" ], "offline": true, "options": [ "-h, --help This help" ], - "usage": "prefs get clientdebug [-h]" + "usage": "prefs get client.debug [-h]" }, - "prefs get clientdelay": { - "command": "prefs get clientdelay", + "prefs get client.delay": { + "command": "prefs get client.delay", "description": "Get preference of delay time before execution of a command in the client", "notes": [ - "prefs get clientdelay" + "prefs get client.delay" ], "offline": true, "options": [ "-h, --help This help" ], - "usage": "prefs get clientdelay [-h]" + "usage": "prefs get client.delay [-h]" + }, + "prefs get client.timeout": { + "command": "prefs get client.timeout", + "description": "Get preference of delay time before execution of a command in the client", + "notes": [ + "prefs get client.timeout" + ], + "offline": true, + "options": [ + "-h, --help This help" + ], + "usage": "prefs get client.timeout [-h]" }, "prefs get color": { "command": "prefs get color", @@ -11403,14 +11592,6 @@ ], "usage": "prefs get savepaths [-h]" }, - "prefs help": { - "command": "prefs help", - "description": "help This help get { Get a preference } set { Set a preference } show Show all preferences", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "prefs set barmode": { "command": "prefs set barmode", "description": "Set persistent preference of HF/LF tune command styled output in the client", @@ -11426,11 +11607,11 @@ ], "usage": "prefs set barmode [-h] [--bar] [--mix] [--val]" }, - "prefs set clientdebug": { - "command": "prefs set clientdebug", + "prefs set client.debug": { + "command": "prefs set client.debug", "description": "Set persistent preference of using clientside debug level", "notes": [ - "prefs set clientdebug --simple" + "prefs set client.debug --simple" ], "offline": true, "options": [ @@ -11439,21 +11620,36 @@ "--simple simple debug messages", "--full full debug messages" ], - "usage": "prefs set clientdebug [-h] [--off] [--simple] [--full]" + "usage": "prefs set client.debug [-h] [--off] [--simple] [--full]" }, - "prefs set clientdelay": { - "command": "prefs set clientdelay", + "prefs set client.delay": { + "command": "prefs set client.delay", "description": "Set persistent preference of delay before executing a command in the client", "notes": [ - "prefs set clientdelay --ms 0 -> unsets any delay", - "prefs set clientdelay --ms 1000 -> sets 1000ms delay" + "prefs set client.delay --ms 0 -> unsets any delay", + "prefs set client.delay --ms 1000 -> sets 1000ms delay" ], "offline": true, "options": [ "-h, --help This help", "--ms delay in micro seconds" ], - "usage": "prefs set clientdelay [-h] [--ms ]" + "usage": "prefs set client.delay [-h] [--ms ]" + }, + "prefs set client.timeout": { + "command": "prefs set client.timeout", + "description": "Set persistent preference of client communication timeout", + "notes": [ + "prefs set client.timeout --ms 0 -> unsets any timeout", + "prefs set client.timeout -m 20 -> Set the timeout to 20ms", + "prefs set client.timeout --ms 500 -> Set the timeout to 500ms" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-m, --ms timeout in micro seconds" + ], + "usage": "prefs set client.timeout [-h] [-m ]" }, "prefs set color": { "command": "prefs set color", @@ -11485,14 +11681,6 @@ ], "usage": "prefs set emoji [-h] [--alias] [--emoji] [--alttext] [--none]" }, - "prefs set help": { - "command": "prefs set help", - "description": "help This help barmode Set bar mode clientdebug Set client debug level clientdelay Set client execution delay color Set color support emoji Set emoji display hints Set hint display savepaths ... to be adjusted next ... output Set dump output style plotsliders Set plot slider display", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "prefs set hints": { "command": "prefs set hints", "description": "Set persistent preference of showing hint messages in the client", @@ -11590,13 +11778,17 @@ ], "usage": "rem [-h] []..." }, - "script help": { - "command": "script help", - "description": "This is a feature to run Lua/Cmd/Python scripts. You can place scripts within the luascripts/cmdscripts/pyscripts folders. --------------------------------------------------------------------------------------- script list available offline: yes", - "notes": [], + "script list": { + "command": "script list", + "description": "List available Lua, Cmd and Python scripts", + "notes": [ + "script list" + ], "offline": true, - "options": [], - "usage": "" + "options": [ + "-h, --help This help" + ], + "usage": "script list [-h]" }, "script run": { "command": "script run", @@ -11625,14 +11817,6 @@ ], "usage": "smart brute [-ht]" }, - "smart help": { - "command": "smart help", - "description": "help This help list List ISO 7816 history upgrade Upgrade sim module firmware", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "smart info": { "command": "smart info", "description": "Extract more detailed information from smart card.", @@ -11684,9 +11868,10 @@ "-s active smartcard with select (get ATR)", "-t, --tlv executes TLV decoder if it possible", "-0 use protocol T=0", + "--timeout Timeout in MS waiting for SIM to respond. (def 337ms)", "-d, --data bytes to send" ], - "usage": "smart raw [-hrast0] -d " + "usage": "smart raw [-hrast0] [--timeout ] -d " }, "smart reader": { "command": "smart reader", @@ -11701,6 +11886,21 @@ ], "usage": "smart reader [-hv]" }, + "smart relay": { + "command": "smart relay", + "description": "Make pm3 available to host OS smartcard driver via vpcd to enable use with other software such as GlobalPlatform Pro", + "notes": [ + "Requires the virtual smartcard daemon to be installed and running, see https://frankmorgner.github.io/vsmartcard/virtualsmartcard/README.html" + ], + "offline": false, + "options": [ + "-h, --help This help", + "--host vpcd socket host (default: localhost)", + "-p, --port vpcd socket port (default: 35963)", + "-v, --verbose display APDU transactions between OS and card" + ], + "usage": "smart relay [-hv] [--host ] [-p ]" + }, "smart setclock": { "command": "smart setclock", "description": "Set clock speed for smart card interface.", @@ -11717,14 +11917,6 @@ ], "usage": "smart setclock [-h] [--16mhz] [--8mhz] [--4mhz]" }, - "smart upgrade": { - "command": "smart upgrade", - "description": "[=] ------------------------------------------------------------------- [!] WARNING - sim module firmware upgrade [!] A dangerous command, do wrong and you could brick the sim module [=] -------------------------------------------------------------------", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "trace extract": { "command": "trace extract", "description": "Extracts protocol authentication challenges from trace buffer", @@ -11739,14 +11931,6 @@ ], "usage": "trace extract [-h1]" }, - "trace help": { - "command": "trace help", - "description": "help This help extract Extract authentication challenges found in trace list List protocol data in trace buffer load Load trace from file save Save trace buffer to file", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "trace list": { "command": "trace list", "description": "Annotate trace buffer with selected protocol data You can load a trace from file (see `trace load -h`) or it be downloaded from device by default", @@ -11771,6 +11955,7 @@ "trace list -t seos -> interpret as SEOS", "trace list -t thinfilm -> interpret as Thinfilm", "trace list -t topaz -> interpret as Topaz", + "trace list -t mfp -> interpret as MIFARE Plus", "", "trace list -t mf -f mfc_default_keys.dic -> use default dictionary file", "trace list -t 14a --frame -> show frame delay times", @@ -11819,11 +12004,15 @@ }, "usart btfactory": { "command": "usart btfactory", - "description": "Reset BT add-on to factory settings This requires 1) BTpower to be turned ON 2) BT add-on to NOT be connected => the add-on blue LED must blink", - "notes": [], + "description": "Reset BT add-on to factory settings This requires 1) BTpower to be turned ON 2) BT add-on to NOT be connected => the add-on blue LED must blink WARNING: process only if strictly needed!", + "notes": [ + "usart btfactory" + ], "offline": false, - "options": [], - "usage": "" + "options": [ + "-h, --help This help" + ], + "usage": "usart btfactory [-h]" }, "usart btpin": { "command": "usart btpin", @@ -11856,14 +12045,6 @@ ], "usage": "usart config [-hNEO] [-b ]" }, - "usart help": { - "command": "usart help", - "description": "help This help", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "usart rx": { "command": "usart rx", "description": "Receive string over USART. WARNING: it will have side-effects if used in USART HOST mode!", @@ -11965,14 +12146,6 @@ ], "usage": "wiegand encode [-h] [--fc ] --cn [--issue ] [--oem ] [-w ] [--pre]" }, - "wiegand help": { - "command": "wiegand help", - "description": "help This help list List available wiegand formats encode Encode to wiegand raw hex (currently for HID Prox) decode Convert raw hex to decoded wiegand format (currently for HID Prox)", - "notes": [], - "offline": true, - "options": [], - "usage": "" - }, "wiegand list": { "command": "wiegand list", "description": "List available wiegand formats", @@ -11987,8 +12160,8 @@ } }, "metadata": { - "commands_extracted": 754, + "commands_extracted": 703, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2023-03-26T15:04:49" + "extracted_on": "2024-01-04T12:13:24" } -} +} \ No newline at end of file diff --git a/doc/commands.md b/doc/commands.md index 487cea0c9..8478e3205 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -37,8 +37,9 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`prefs get barmode `|Y |`Get bar mode preference` -|`prefs get clientdebug `|Y |`Get client debug level preference` -|`prefs get clientdelay `|Y |`Get client execution delay preference` +|`prefs get client.debug `|Y |`Get client debug level preference` +|`prefs get client.delay `|Y |`Get client execution delay preference` +|`prefs get client.timeout`|Y |`Get client execution delay preference` |`prefs get color `|Y |`Get color support preference` |`prefs get savepaths `|Y |`Get file folder ` |`prefs get emoji `|Y |`Get emoji display preference` @@ -55,8 +56,9 @@ Check column "offline" for their availability. |------- |------- |----------- |`prefs set help `|Y |`This help` |`prefs set barmode `|Y |`Set bar mode` -|`prefs set clientdebug `|Y |`Set client debug level` -|`prefs set clientdelay `|Y |`Set client execution delay` +|`prefs set client.debug `|Y |`Set client debug level` +|`prefs set client.delay `|Y |`Set client execution delay` +|`prefs set client.timeout`|Y |`Set client communication timeout` |`prefs set color `|Y |`Set color support` |`prefs set emoji `|Y |`Set emoji display` |`prefs set hints `|Y |`Set hint display` @@ -98,10 +100,11 @@ Check column "offline" for their availability. |`data manrawdecode `|Y |`Manchester decode binary stream in DemodBuffer` |`data modulation `|Y |`Identify LF signal for clock and modulation` |`data rawdemod `|Y |`Demodulate the data in the GraphBuffer and output binary` -|`data askedgedetect `|Y |`Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave` +|`data askedgedetect `|Y |`Adjust Graph for manual ASK demod` |`data autocorr `|Y |`Autocorrelation over window` -|`data dirthreshold `|Y |`Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev.` +|`data dirthreshold `|Y |`Max rising higher up-thres/ Min falling lower down-thres` |`data decimate `|Y |`Decimate samples` +|`data envelope `|Y |`Generate square envelope of samples` |`data undecimate `|Y |`Un-decimate samples` |`data hide `|Y |`Hide graph window` |`data hpf `|Y |`Remove DC offset from trace` @@ -111,18 +114,21 @@ Check column "offline" for their availability. |`data mtrim `|Y |`Trim out samples from the specified start to the specified stop` |`data norm `|Y |`Normalize max/min to +/-128` |`data plot `|Y |`Show graph window` +|`data cthreshold `|Y |`Average out all values between` |`data rtrim `|Y |`Trim samples from right of trace` |`data setgraphmarkers `|Y |`Set blue and orange marker in graph window` |`data shiftgraphzero `|Y |`Shift 0 for Graphed wave + or - shift value` -|`data timescale `|Y |`Set a timescale to get a differential reading between the yellow and purple markers as time duration` +|`data timescale `|Y |`Set cursor display timescale` |`data zerocrossings `|Y |`Count time between zero-crossings` |`data convertbitstream `|Y |`Convert GraphBuffer's 0/1 values to 127 / -127` |`data getbitstream `|Y |`Convert GraphBuffer's >=1 values to 1 and <1 to 0` -|`data asn1 `|Y |`asn1 decoder` +|`data asn1 `|Y |`ASN1 decoder` +|`data atr `|Y |`ATR lookup` |`data bin2hex `|Y |`Converts binary to hexadecimal` |`data bitsamples `|N |`Get raw samples as bitstring` +|`data bmap `|Y |`Convert hex value according a binary template` |`data clear `|Y |`Clears bigbuf on deviceside and graph window` -|`data diff `|Y |`diff of input files` +|`data diff `|Y |`Diff of input files` |`data hexsamples `|N |`Dump big buffer as hex bytes` |`data hex2bin `|Y |`Converts hexadecimal to binary` |`data load `|Y |`Load contents of file into graph window` @@ -141,19 +147,20 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`emv help `|Y |`This help` +|`emv list `|Y |`List ISO7816 history` +|`emv test `|Y |`Crypto logic test` +|`emv challenge `|N |`Generate challenge` |`emv exec `|N |`Executes EMV contactless transaction` +|`emv genac `|N |`Generate ApplicationCryptogram` +|`emv gpo `|N |`Execute GetProcessingOptions` +|`emv intauth `|N |`Internal authentication` |`emv pse `|N |`Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory` +|`emv reader `|N |`Act like an EMV reader` +|`emv readrec `|N |`Read files from card` +|`emv roca `|N |`Extract public keys and run ROCA test` +|`emv scan `|N |`Scan EMV card and save it contents to json file for emulator` |`emv search `|N |`Try to select all applets from applets list and print installed applets` |`emv select `|N |`Select applet` -|`emv gpo `|N |`Execute GetProcessingOptions` -|`emv readrec `|N |`Read files from card` -|`emv genac `|N |`Generate ApplicationCryptogram` -|`emv challenge `|N |`Generate challenge` -|`emv intauth `|N |`Internal authentication` -|`emv scan `|N |`Scan EMV card and save it contents to json file for emulator` -|`emv test `|Y |`Crypto logic test` -|`emv list `|Y |`List ISO7816 history` -|`emv roca `|N |`Extract public keys and run ROCA test` ### hf @@ -233,10 +240,12 @@ Check column "offline" for their availability. |`hf 15 reader `|N |`Act like an ISO-15693 reader` |`hf 15 restore `|N |`Restore from file to all memory pages of an ISO-15693 tag` |`hf 15 samples `|N |`Acquire samples as reader (enables carrier, sends inquiry)` +|`hf 15 view `|Y |`Display content from tag dump file` +|`hf 15 wrbl `|N |`Write a block` +|`hf 15 sim `|N |`Fake an ISO-15693 tag` |`hf 15 eload `|N |`Load image file into emulator to be used by 'sim' command` |`hf 15 esave `|N |`Save emulator memory into image file` |`hf 15 eview `|N |`View emulator memory` -|`hf 15 sim `|N |`Fake an ISO-15693 tag` |`hf 15 slixwritepwd `|N |`Writes a password on a SLIX ISO-15693 tag` |`hf 15 slixeasdisable `|N |`Disable EAS mode on SLIX ISO-15693 tag` |`hf 15 slixeasenable `|N |`Enable EAS mode on SLIX ISO-15693 tag` @@ -244,7 +253,6 @@ Check column "offline" for their availability. |`hf 15 slixprivacyenable`|N |`Enable privacy mode on SLIX ISO-15693 tag` |`hf 15 passprotectafi `|N |`Password protect AFI - Cannot be undone` |`hf 15 passprotecteas `|N |`Password protect EAS - Cannot be undone` -|`hf 15 wrbl `|N |`Write a block` |`hf 15 findafi `|N |`Brute force AFI of an ISO-15693 tag` |`hf 15 writeafi `|N |`Writes the AFI on an ISO-15693 tag` |`hf 15 writedsfid `|N |`Writes the DSFID on an ISO-15693 tag` @@ -401,29 +409,32 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf iclass help `|Y |`This help` -|`hf iclass dump `|N |`Dump Picopass / iCLASS tag to file` -|`hf iclass info `|Y |`Tag information` |`hf iclass list `|Y |`List iclass history` +|`hf iclass dump `|N |`Dump Picopass / iCLASS tag to file` +|`hf iclass info `|N |`Tag information` |`hf iclass rdbl `|N |`Read Picopass / iCLASS block` |`hf iclass reader `|N |`Act like a Picopass / iCLASS reader` |`hf iclass restore `|N |`Restore a dump file onto a Picopass / iCLASS tag` |`hf iclass sniff `|N |`Eavesdrop Picopass / iCLASS communication` +|`hf iclass view `|Y |`Display content from tag dump file` |`hf iclass wrbl `|N |`Write Picopass / iCLASS block` +|`hf iclass creditepurse `|N |`Credit epurse value` |`hf iclass chk `|N |`Check keys` |`hf iclass loclass `|Y |`Use loclass to perform bruteforce reader attack` |`hf iclass lookup `|Y |`Uses authentication trace to check for key in dictionary file` |`hf iclass sim `|N |`Simulate iCLASS tag` |`hf iclass eload `|N |`Load Picopass / iCLASS dump file into emulator memory` |`hf iclass esave `|N |`Save emulator memory to file` +|`hf iclass esetblk `|N |`Set emulator memory block data` |`hf iclass eview `|N |`View emulator memory` -|`hf iclass configcard `|Y |`Reader configuration card` +|`hf iclass configcard `|N |`Reader configuration card` |`hf iclass calcnewkey `|Y |`Calc diversified keys (blocks 3 & 4) to write new keys` |`hf iclass encode `|Y |`Encode binary wiegand to block 7` |`hf iclass encrypt `|Y |`Encrypt given block data` |`hf iclass decrypt `|Y |`Decrypt given block data or tag dump file` |`hf iclass managekeys `|Y |`Manage keys to use with iclass commands` |`hf iclass permutekey `|Y |`Permute function from 'heart of darkness' paper` -|`hf iclass view `|Y |`Display content from tag dump file` +|`hf iclass sam `|N |`SAM tests` ### hf legic @@ -474,6 +485,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf mf help `|Y |`This help` |`hf mf list `|Y |`List MIFARE history` +|`hf mf info `|N |`mfc card Info` |`hf mf darkside `|N |`Darkside attack` |`hf mf nested `|N |`Nested attack` |`hf mf hardnested `|Y |`Nested attack for hardened MIFARE Classic cards` @@ -482,7 +494,7 @@ Check column "offline" for their availability. |`hf mf nack `|N |`Test for MIFARE NACK bug` |`hf mf chk `|N |`Check keys` |`hf mf fchk `|N |`Check keys fast, targets all keys on card` -|`hf mf decrypt `|Y |`[nt] [ar_enc] [at_enc] [data] - to decrypt sniff or trace` +|`hf mf decrypt `|Y |`Decrypt Crypto1 data from sniff or trace` |`hf mf supercard `|N |`Extract info from a `super card`` |`hf mf auth4 `|N |`ISO14443-4 AES authentication` |`hf mf acl `|Y |`Decode and print MIFARE Classic access rights bytes` @@ -518,16 +530,20 @@ Check column "offline" for their availability. |`hf mf gen3uid `|N |`Set UID without changing manufacturer block` |`hf mf gen3blk `|N |`Overwrite manufacturer block` |`hf mf gen3freeze `|N |`Perma lock UID changes. irreversible` +|`hf mf ginfo `|N |`Info about configuration of the card` |`hf mf ggetblk `|N |`Read block from card` |`hf mf gload `|N |`Load dump to card` |`hf mf gsave `|N |`Save dump from card into file or emulator` |`hf mf gsetblk `|N |`Write block to card` |`hf mf gview `|N |`View card` -|`hf mf gdmconfig `|N |`Read config block from card` +|`hf mf gchpwd `|N |`Change card access password. Warning!` +|`hf mf gdmcfg `|N |`Read config block from card` +|`hf mf gdmsetcfg `|N |`Write config block to card` |`hf mf gdmsetblk `|N |`Write block to card` |`hf mf ndefformat `|N |`Format MIFARE Classic Tag as NFC Tag` |`hf mf ndefread `|N |`Read and print NDEF records from card` |`hf mf ndefwrite `|N |`Write NDEF records to card` +|`hf mf encodehid `|N |`Encode a HID Credential / NDEF record to card` ### hf mfp @@ -537,17 +553,23 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf mfp help `|Y |`This help` -|`hf mfp info `|N |`Info about Mifare Plus tag` -|`hf mfp wrp `|N |`Write Perso command` -|`hf mfp initp `|N |`Fill all the card's keys in SL0 mode` -|`hf mfp commitp `|N |`Move card to SL1 or SL3 mode` +|`hf mfp list `|Y |`List MIFARE Plus history` |`hf mfp auth `|N |`Authentication` -|`hf mfp rdbl `|N |`Read blocks` -|`hf mfp rdsc `|N |`Read sectors` -|`hf mfp wrbl `|N |`Write blocks` |`hf mfp chk `|N |`Check keys` +|`hf mfp dump `|N |`Dump MIFARE Plus tag to binary file` +|`hf mfp info `|N |`Info about MIFARE Plus tag` |`hf mfp mad `|N |`Check and print MAD` +|`hf mfp rdbl `|N |`Read blocks from card` +|`hf mfp rdsc `|N |`Read sectors from card` +|`hf mfp wrbl `|N |`Write block to card` +|`hf mfp chkey `|N |`Change key on card` +|`hf mfp chconf `|N |`Change config on card` +|`hf mfp commitp `|N |`Configure security layer (SL1/SL3 mode)` +|`hf mfp initp `|N |`Fill all the card's keys in SL0 mode` +|`hf mfp wrp `|N |`Write Perso command` +|`hf mfp ndefformat `|N |`Format MIFARE Plus Tag as NFC Tag` |`hf mfp ndefread `|N |`Read and print NDEF records from card` +|`hf mfp ndefwrite `|N |`Write NDEF records to card` ### hf mfu @@ -557,6 +579,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf mfu help `|Y |`This help` +|`hf mfu list `|Y |`List MIFARE Ultralight / NTAG history` |`hf mfu keygen `|Y |`Generate 3DES MIFARE diversified keys` |`hf mfu pwdgen `|Y |`Generate pwd from known algos` |`hf mfu otptear `|N |`Tear-off test on OTP bits` @@ -568,7 +591,7 @@ Check column "offline" for their availability. |`hf mfu restore `|N |`Restore a dump onto a MFU MAGIC tag` |`hf mfu view `|Y |`Display content from tag dump file` |`hf mfu wrbl `|N |`Write block` -|`hf mfu tamper `|N |`Cofigure the tamper feature on an NTAG 213TT` +|`hf mfu tamper `|N |`Configure the tamper feature on an NTAG 213TT` |`hf mfu eload `|N |`Load Ultralight dump file into emulator memory` |`hf mfu esave `|N |`Save Ultralight dump file from emulator memory` |`hf mfu eview `|N |`View emulator memory` @@ -632,8 +655,13 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf ntag424 help `|Y |`This help` |`hf ntag424 info `|N |`Tag information` -|`hf ntag424 sdm `|N |`Prints NDEF records from card` |`hf ntag424 view `|Y |`Display content from tag dump file` +|`hf ntag424 auth `|N |`Test authentication with key` +|`hf ntag424 read `|N |`Read file` +|`hf ntag424 write `|N |`Write file` +|`hf ntag424 getfs `|N |`Get file settings` +|`hf ntag424 changefs `|N |`Change file settings` +|`hf ntag424 changekey `|N |`Change key` ### hf seos @@ -703,18 +731,39 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf topaz help `|Y |`This help` -|`hf topaz dump `|N |`Dump TOPAZ family tag to file` |`hf topaz list `|Y |`List Topaz history` +|`hf topaz dump `|N |`Dump TOPAZ family tag to file` |`hf topaz info `|N |`Tag information` +|`hf topaz raw `|N |`Send raw hex data to tag` +|`hf topaz rdbl `|N |`Read block` |`hf topaz reader `|N |`Act like a Topaz reader` |`hf topaz sim `|N |`Simulate Topaz tag` |`hf topaz sniff `|N |`Sniff Topaz reader-tag communication` -|`hf topaz raw `|N |`Send raw hex data to tag` -|`hf topaz rdbl `|N |`Read block` |`hf topaz view `|Y |`Display content from tag dump file` |`hf topaz wrbl `|N |`Write block` +### hf vas + + { Apple Value Added Service } + +|command |offline |description +|------- |------- |----------- +|`hf vas help `|Y |`This help` +|`hf vas reader `|N |`Read and decrypt VAS message` +|`hf vas decrypt `|Y |`Decrypt a previously captured VAS cryptogram` + + +### hf waveshare + + { Waveshare NFC ePaper... } + +|command |offline |description +|------- |------- |----------- +|`hf waveshare help `|Y |`This help` +|`hf waveshare loadbmp `|N |`Load BMP file to Waveshare NFC ePaper` + + ### hf xerox { Fuji/Xerox cartridge RFIDs... } @@ -727,16 +776,6 @@ Check column "offline" for their availability. |`hf xerox dump `|N |`Read all memory pages of an Fuji/Xerox tag, save to file` -### hf waveshare - - { Waveshare NFC ePaper... } - -|command |offline |description -|------- |------- |----------- -|`hf waveshare help `|Y |`This help` -|`hf waveshare loadbmp `|N |`Load BMP file to Waveshare NFC ePaper` - - ### hw { Hardware commands... } @@ -745,6 +784,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`hw help `|Y |`This help` |`hw break `|N |`Send break loop usb command` +|`hw bootloader `|N |`Reboot Proxmark3 into bootloader mode` |`hw connect `|Y |`Connect Proxmark3 to serial port` |`hw dbg `|N |`Set Proxmark3 debug level` |`hw detectreader `|N |`Detect external reader field` @@ -760,6 +800,7 @@ Check column "offline" for their availability. |`hw status `|N |`Show runtime status information about the connected Proxmark3` |`hw tearoff `|N |`Program a tearoff hook for the next command supporting tearoff` |`hw tia `|N |`Trigger a Timing Interval Acquisition to re-adjust the RealTimeCounter divider` +|`hw timeout `|Y |`Set the communication timeout on the client side` |`hw tune `|N |`Measure antenna tuning` |`hw version `|Y |`Show version information about the client and the connected Proxmark3, if any` @@ -857,14 +898,14 @@ Check column "offline" for their availability. |`lf em 4x05 help `|Y |`This help` |`lf em 4x05 brute `|N |`Bruteforce password` |`lf em 4x05 chk `|N |`Check passwords from dictionary` -|`lf em 4x05 demod `|Y |`demodulate a EM4x05/EM4x69 tag from the GraphBuffer` -|`lf em 4x05 dump `|N |`dump EM4x05/EM4x69 tag` -|`lf em 4x05 info `|N |`tag information EM4x05/EM4x69` -|`lf em 4x05 read `|N |`read word data from EM4x05/EM4x69` +|`lf em 4x05 demod `|Y |`Demodulate a EM4x05/EM4x69 tag from the GraphBuffer` +|`lf em 4x05 dump `|N |`Dump EM4x05/EM4x69 tag` +|`lf em 4x05 info `|N |`Tag information` +|`lf em 4x05 read `|N |`Read word data from EM4x05/EM4x69` |`lf em 4x05 sniff `|Y |`Attempt to recover em4x05 commands from sample buffer` -|`lf em 4x05 unlock `|N |`execute tear off against EM4x05/EM4x69` -|`lf em 4x05 wipe `|N |`wipe EM4x05/EM4x69 tag` -|`lf em 4x05 write `|N |`write word data to EM4x05/EM4x69` +|`lf em 4x05 unlock `|N |`Execute tear off against EM4x05/EM4x69` +|`lf em 4x05 wipe `|N |`Wipe EM4x05/EM4x69 tag` +|`lf em 4x05 write `|N |`Write word data to EM4x05/EM4x69` ### lf em 4x50 @@ -874,7 +915,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`lf em 4x50 help `|Y |`This help` -|`lf em 4x50 brute `|N |`Simple bruteforce attack to find password` +|`lf em 4x50 brute `|N |`Bruteforce attack to find password` |`lf em 4x50 chk `|N |`Check passwords from dictionary` |`lf em 4x50 dump `|N |`Dump EM4x50 tag` |`lf em 4x50 info `|N |`Tag information` @@ -968,15 +1009,16 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`lf hitag help `|Y |`This help` -|`lf hitag eload `|N |`Load Hitag dump file into emulator memory` |`lf hitag list `|Y |`List Hitag trace history` -|`lf hitag info `|N |`Hitag2 tag information` -|`lf hitag reader `|N |`Act like a Hitag reader` -|`lf hitag sim `|N |`Simulate Hitag transponder` +|`lf hitag info `|N |`Hitag 2 tag information` +|`lf hitag dump `|N |`Dump Hitag 2 tag` +|`lf hitag read `|N |`Read Hitag memory` +|`lf hitag wrbl `|N |`Write a block (page) in Hitag memory` |`lf hitag sniff `|N |`Eavesdrop Hitag communication` -|`lf hitag writer `|N |`Act like a Hitag writer` -|`lf hitag dump `|N |`Dump Hitag2 tag` -|`lf hitag cc `|N |`Test all challenges` +|`lf hitag cc `|N |`Hitag S: test all provided challenges` +|`lf hitag ta `|N |`Hitag 2: test all recorded authentications` +|`lf hitag eload `|N |`Load Hitag dump file into emulator memory` +|`lf hitag sim `|N |`Simulate Hitag transponder` ### lf idteck @@ -1049,7 +1091,7 @@ Check column "offline" for their availability. ### lf motorola - { Motorola RFIDs... } + { Motorola Flexpass RFIDs... } |command |offline |description |------- |------- |----------- @@ -1386,6 +1428,7 @@ Check column "offline" for their availability. |`smart help `|Y |`This help` |`smart list `|Y |`List ISO 7816 history` |`smart info `|N |`Tag information` +|`smart relay `|N |`Turn pm3 into pcsc reader and relay to host OS via vpcd` |`smart reader `|N |`Act like an IS07816 reader` |`smart raw `|N |`Send raw hex data to tag` |`smart upgrade `|Y |`Upgrade sim module firmware` diff --git a/doc/datasheets/Atmel SAM7S Series Datasheet AT91SAM 6175M–ATARM–26-Oct-12.pdf b/doc/datasheets/Atmel_SAM7S_Series_Datasheet_AT91SAM_6175M-ATARM-26-Oct-12.pdf similarity index 100% rename from doc/datasheets/Atmel SAM7S Series Datasheet AT91SAM 6175M–ATARM–26-Oct-12.pdf rename to doc/datasheets/Atmel_SAM7S_Series_Datasheet_AT91SAM_6175M-ATARM-26-Oct-12.pdf diff --git a/doc/datasheets/DS_N76E003_EN_Rev1.09.pdf b/doc/datasheets/DS_N76E003_EN_Rev1.09.pdf new file mode 100644 index 000000000..0fae083ac Binary files /dev/null and b/doc/datasheets/DS_N76E003_EN_Rev1.09.pdf differ diff --git a/doc/datasheets/W25X40CL_H_20220301.pdf b/doc/datasheets/W25X40CL_H_20220301.pdf new file mode 100644 index 000000000..c847a3b81 Binary files /dev/null and b/doc/datasheets/W25X40CL_H_20220301.pdf differ diff --git a/doc/datasheets/XILINX_Spartan-II_FPGA_Family.pdf b/doc/datasheets/XILINX_Spartan-II_FPGA_Family.pdf new file mode 100644 index 000000000..1b1eb4d23 Binary files /dev/null and b/doc/datasheets/XILINX_Spartan-II_FPGA_Family.pdf differ diff --git a/doc/emv_notes.md b/doc/emv_notes.md index 60f85ebb8..cd03e5d21 100644 --- a/doc/emv_notes.md +++ b/doc/emv_notes.md @@ -65,14 +65,14 @@ They can be implemented, but it needs to know issuer's card keys (usually 3DES) All this commands are parts of command `emv exec`. command `emv exec` executes EMV transaction. it have parameters: ``` - -j, -J, --jload Load transaction parameters from `emv/defparams.json` file. - -f, -F, --forceaid Force search AID. Search AID instead of execute PPSE. - By default: Transaction type - MSD - -v, -V, --qvsdc Transaction type - qVSDC or M/Chip. - -c, -C, --qvsdccda Transaction type - qVSDC or M/Chip plus CDA (SDAD generation). - -x, -X, --vsdc Transaction type - VSDC. - -g, -G, --acgpo VISA. generate AC from GPO. - -w, -W, --wired Send data via contact (iso7816) interface. Contactless interface set by default. + -j, --jload Load transaction parameters from `emv/defparams.json` file. + --forceaid Force search AID. Search AID instead of execute PPSE. + By default: Transaction type - MSD + -v, --qvsdc Transaction type - qVSDC or M/Chip. + -c, --qvsdccda Transaction type - qVSDC or M/Chip plus CDA (SDAD generation). + -x, --vsdc Transaction type - VSDC. + -g, --acgpo VISA. generate AC from GPO. + -w, --wired Send data via contact (iso7816) interface. Contactless interface set by default. ``` It works for VISA(r) and Mastercard(r) transactions. It may work with other EMV payment system's card (and it works in general cases that is described in EMV). @@ -96,19 +96,23 @@ MSD - compatibility mode. Now it work always. But it less secure and in near fut ^[Top](#top) ``` -exec Executes EMV contactless transaction. -pse Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory. -search Try to select all applets from applets list and print installed applets. -select Select applet. -gpo Execute GetProcessingOptions. -readrec Read files from card. -genac Generate ApplicationCryptogram. -challenge Generate challenge. -intauth Internal authentication. -scan Scan EMV card and save it contents to json file for emulator. -test Crypto logic test. +----------- ----------------------- general ----------------------- +help This help list List ISO7816 history +test Crypto logic test +----------- ---------------------- operations --------------------- +challenge Generate challenge +exec Executes EMV contactless transaction +genac Generate ApplicationCryptogram +gpo Execute GetProcessingOptions +intauth Internal authentication +pse Execute PPSE. It selects 2PAY.SYS.DDF01 or 1PAY.SYS.DDF01 directory +reader Act like an EMV reader +readrec Read files from card roca Extract public keys and run ROCA test +scan Scan EMV card and save it contents to json file for emulator +search Try to select all applets from applets list and print installed applets +select Select applet ``` All main commands are parts of EMV specification. Commands than not described there: diff --git a/doc/extensions_notes.md b/doc/extensions_notes.md index 063ce2b85..266a4df1e 100644 --- a/doc/extensions_notes.md +++ b/doc/extensions_notes.md @@ -6,7 +6,7 @@ The Proxmark3 client uses a wide range of files. Here is a brief recap to get yo |---|---| | .exe | windows executable | | .bin | binary file, can be firmware or memory dump of a tag or keys dump of a tag| -| .eml | text file, with memory dump of a tag | +| .eml | text file, with memory dump of a tag (deprecated) | | .mfd | binary file, MIFARE file dump, name comes from NFC-Tools. Usually created with Mifare Classic Tool app (MCT) or NFC-Tools, contains memory dump of tag. Very similar to .bin file | | .json | JSON file, usually settings file or it can also be a memory dump of a tag | | .dic | dictionary file. textual, with keys/passwords one line / key | diff --git a/doc/fpga_arm_notes.md b/doc/fpga_arm_notes.md index 4271f5790..4cc47b88a 100644 --- a/doc/fpga_arm_notes.md +++ b/doc/fpga_arm_notes.md @@ -2,7 +2,7 @@ # Table of Contents -- [Notes on ARM & FPGA communications](#notes-on-arm--fpga-communications) +- [Notes on ARM \& FPGA communications](#notes-on-arm--fpga-communications) - [Table of Contents](#table-of-contents) - [INTERFACE FROM THE ARM TO THE FPGA](#interface-from-the-arm-to-the-fpga) - [FPGA](#fpga) @@ -69,6 +69,15 @@ There is very rarely changes to the images so there is no need to setup a fpga t Since the FPGA is very old, the Xilinx WebPack ISE 10.1 is the last working tool chain. You can download this legacy development on Xilinx and register for a free product installation id. Or use mine `11LTAJ5ZJK3PXTUBMF0C0J6C4` The package to download is about 7Gb and linux based. Though I recently managed to install it on WSL for Windows 10. +There is a docker image with webpack installed which has been built which you can use to easily compile the images: + +``` +docker pull nhutton/prox-container:webp_image_complete +docker run -v /proxmark3:/tmp --rm -it nhutton/prox-container:webp_image_complete bash +$ cd /tmp/proxmark/fpga +$ make all +``` + In order to save space, these fpga images are LZ4 compressed and included in the fullimage.elf file when compiling the ARM SRC. `make armsrc` This means we save some precious space on the ARM but its a bit more complex when flashing to fpga since it has to decompress on the fly. @@ -185,7 +194,7 @@ communicating this to the FPGA. The microcontroller (ARM) implements the transport layer. First it decodes the samples received from the FPGA. These samples are stored in a Direct Memory Access (DMA) buffer. The samples are binary sequences that represent whether the signal was high or low. The software on the ARM tries to decode -these samples. When the Proxmark is in sniffing mode this is done for both the Manchester and Modified +these samples. When the Proxmark3 is in sniffing mode this is done for both the Manchester and Modified Miller at the same time. Whenever one of the decoding procedures returns a valid message, this message is stored in another buffer (BigBuf) and both decoding procedures are set to an un-synced state. The BigBuf is limited to the available memory on the ARM. The current firmware has 2 KB of memory diff --git a/doc/hid_downgrade.md b/doc/hid_downgrade.md new file mode 100644 index 000000000..d9bea939b --- /dev/null +++ b/doc/hid_downgrade.md @@ -0,0 +1,263 @@ +# Notes on downgrade attacks + + +Author [@kitsunehunter](https://gist.github.com/kitsunehunter) 2023 + +This is a reworked text. You find the [original text here](https://gist.github.com/kitsunehunter/c75294bdbd0533eca298d122c39fb1bd) + +The collective notes on iCLASS SR / iCLASS SE / SEOS downgrade attacks. + +This document targets both Proxmark3 and Flipper Zero devices. + +# Table of Contents +- [Notes on downgrade attacks](#notes-on-downgrade-attacks) +- [Table of Contents](#table-of-contents) +- [Terminology](#terminology) +- [Useful links](#useful-links) +- [Downgrade concept](#downgrade-concept) +- [Success rate](#success-rate) +- [Getting started](#getting-started) + - [Verfiy reader has iCLASS legacy enabled](#verfiy-reader-has-iclass-legacy-enabled) + - [Inspect reader with HID reader manager](#inspect-reader-with-hid-reader-manager) + - [Verify reader has ProxII enabled](#verify-reader-has-proxii-enabled) + - [Test files](#test-files) +- [Simulate a standard keyed iCLASS legacy credential](#simulate-a-standard-keyed-iclass-legacy-credential) +- [Write a downgraded iCLASS legacy credential](#write-a-downgraded-iclass-legacy-credential) + - [Using Omnikey Reader 5427CK Gen2 and Proxmark3](#using-omnikey-reader-5427ck-gen2-and-proxmark3) + - [Using Flipper Zero with NARD](#using-flipper-zero-with-nard) + - [Using Weaponized HID Reader](#using-weaponized-hid-reader) +- [Write ProxII credential to a T5577](#write-proxii-credential-to-a-t5577) + - [Using Proxmark3](#using-proxmark3) + - [Using Flipper Zero](#using-flipper-zero) + + +# Terminology +^[Top](#top) + +* Credential - an access token that acts as carrier of a SIO + +* SIO - Secure Identity Object + +* PACS - Physical Access Control System + +* PACS Payload - The binary encoded credential data. + +* Downgrade attack - Read the PACS payload off a SIO and encode it as a lesser secure legacy format + +* Omnikey - Official HID desktop reader to read PACS payload off iCLASS SE and SEOS cards + +* Weaponized reader - "DIY" omnikey reader to perform the same job as the omnikey using a actual HID reader you might find on a wall + +* NARD / SAM - SIM add-on for Flipper, used with HID SAM to read iCLASS SE and SEOS + +* SAM - HID Secure Access Module responsible for encoding and decoding PACS payload inside a SIO among others + +* T5577 - a low frequency multi purpose card. Used as clone card. + +# Useful links +^[Top](#top) + +[HID iCLASS Credentials tech primer](https://forum.dangerousthings.com/t/types-of-hid-iclass-cards/12243) + +[What does all data on my card mean?!](https://www.hidglobal.com/doclib/files/resource_files/an0109_a.2_credential_id_markings_application_note.pdf) + + +# Downgrade concept +^[Top](#top) + +There is not much you can do with just a card and a Proxmark3 or Flipper Zero. There is no card-only attack vectors. There are however reader/card vectors but that is outside the scope of this note. + +Your iCLASS SR/iCLASS SE/SEOS credential has a SIO (Secure Identity Object) that stores your access control information also known as the PACS payload. We will need to extract the SIO with one of the methods outlined below and write that data onto a Picopass or a T5577. + +In short: +We are downgrading from a secure credential to a lesser secure legacy format + +# Success rate +^[Top](#top) + +Unfortantely not all readers will have iCLASS legacy enabled and your **downgrade** will not work. The good thing is that **most** readers are left in their default configuration with iCLASS legacy enabled which allows us to easily take your secure credential and make a logical copy onto a less secure format. We can easily test if the reader is standard keyed and will accept a credential downgrade attack with the steps below. + +# Getting started +^[Top](#top) + +For the next steps, you will need a `Proxmark3` or `Flipper Zero` device. + +## Verfiy reader has iCLASS legacy enabled +^[Top](#top) + +Present a standard keyed iCLASS legacy credential at the reader and see if it beeps. +If the reader beeps, proceed to [Write a downgraded iCLASS legacy credential](#write-a-downgraded-iclass-legacy-credential) + +Instructions: +To check if your legacy credential is standard keyed. + +PM3 +`hf iclass dump --ki 0` if it dumps == standard key + +F0 +`Picopass app > Read card` check if key == standard + + +## Inspect reader with HID reader manager +^[Top](#top) + +Install [HID reader manager](https://play.google.com/store/apps/details?id=com.hidglobal.pacs.readermanager&hl=en&gl=US) and register before proceeding + +A Android phone with NFC is recommended for this next step as iPhone can only inspect readers that are bluetooth enabled natively or have a BLE backpack installed as a add-on. + +This method of inspection will not work if the reader has a MOB key or ELITE key. + +Reader inspection is only possible on official HID readers, not third party readers using HID credentials. + +Click use NFC and hold the phone to the reader and follow the prompts. Click on apply template. + +Reader Manager Home Screen + +Click on the plus button + +Templates + +Click on credentials + +creds + +Make sure the switch for iCLASS is switched on (blue) + +Screenshot 2023-11-14 221005 + +If you have successfully confirmed that iCLASS legacy is switched on then proceed to the next step + +## Verify reader has ProxII enabled +^[Top](#top) + +You can verify that the low frequency ProxII is enabled by using one of the following methods: + + * Hold a [RF field detector](https://sneaktechnology.com/product/rf-detector-by-proxgrind-2/) at the reader and see if the RED LED flashes + * Use the Flipper RFID detector app `apps > tools > RFID detector` and make sure RFID symbol is active + * Use [reader manager](#inspect-reader-with-hid-reader-manager) and inspect the reader and check if 125khz prox is enabled at the bottom of the credentials page + + +## Test files +^[Top](#top) + +Below are two dump files provided for easy testing. + +- PM3 - Download [hf-iclass-dump.json](../traces/iclass/hf-iclass-dump.json) +- F0 - Download [iclass-flipper.picopass](../traces/iclass/iclass-flipper.picopass) + + +How to restore the dump files on each device. + +PM3 +- run the follwing command to restore hf-iclass-dump.json to a picopass card + `hf iclass restore -f hf-iclass-dump.json --ki 0` + +F0 +- Drop the iclass-flipper.picopass file here and write to card on Flipper + `qflipper > SD card > apps data > picopass` + + +# Simulate a standard keyed iCLASS legacy credential +^[Top](#top) + +For [Test files](#test-files) if needed. + +Instructions: +Once you loaded the file and started the simulation. Hold the device to the reader. If it beeps, proceed to [Write a downgraded iCLASS legacy credential](#write-a-downgraded-iclass-legacy-credential) + +PM3 +``` +hf iclass eload -f hf-iclass-dump.json +hf iclass sim -t 3 +``` + +F0 +`qflipper > SD card > apps data > picopass` +drop iclass-flipper.picopass file here and simulate on Flipper + + +# Write a downgraded iCLASS legacy credential +^[Top](#top) + +## Using Omnikey Reader 5427CK Gen2 and Proxmark3 +^[Top](#top) + +1. Download latest version of Omnikey workbench [here](https://www3.hidglobal.com/drivers/14994) +2. Plug in Omnikey reader +3. Start Omnikey workbench +4. Switch reader mode to CCID mode +5. Go to reader upload tab +6. Use the "load file" function and load the `encoder.cfg` [config file](../traces/iclass/encoder.cfg) +7. Launch PM3 client, place iCLASS/Picopass card on HF antenna and read your original card on the Omnikey reader +8. Press enter + +## Using Flipper Zero with NARD +^[Top](#top) + +Prequisite, you must already have a [NARD add-on board](https://github.com/killergeek/nard) and a HID SAM + +If not, you can buy a [kit](https://www.redteamtools.com/nard-sam-expansion-board-for-flipper-zero-with-hid-seos-iclass-sam/) from RTA webshop. + +Follow these steps: + +1. Launch Seader application + + if `credential == iClass` use read picopass + + if `credential == SEOS` use read 14443A + +2. Place flipper on credential and read +3. Save as picopass +4. Go to picopass app and write your credential to a card + +## Using Weaponized HID Reader +^[Top](#top) + +OBS! +This method involves more technical steps, wiring, and is recommended for advanced users. If this is your first time with RFID technology and downgrade attacks, we suggest any of the two options above. + +Prequisite, you will need the following bill of materials (BOM): +* A standard keyed iCLASS SE reader +* A ESPKEY [Github project](https://github.com/rfidtool/ESP-RFID-Tool) +* Some 20-24 AWG wire or ethernet cable +* Your preferred power source (5-9v) + +The easiest way is to buy a [ESPKEY](https://www.aliexpress.com/item/32850151497.html) + +Follow these steps: + +1. Connect the `Data 0, Data 1, Ground, Power` to the respective terminals on the ESPKEY +2. Provide 5-9V power to the reader and ESPKEY at the same time using your preferred power source + +IT IS ABSOLUTELY NECESSARY THAT THE READER AND ESPKEY SHARE THE SAME GROUND EVEN IF YOU ARE POWERING ESPKEY AND READER SEPERATELY + +3. Connect to the wifi network the ESPKEY and navigate to `192.168.1.1` for the interface +4. Scan your credential on the reader +5. Open `log.txt` and copy the binary string WITHOUT the preamble +6. Use `hf iclass encode --bin --ki 0` to encode the PACS payload to a iCLASS legacy card + + +# Write ProxII credential to a T5577 +^[Top](#top) + +OBS! Downgrading to a T5577 will only work if reader has low frequency (125 kHz) / Prox II enabled. +A good indicator to look out for is the "multiCLASS" sticker on the reader. + +## Using Proxmark3 +^[Top](#top) + +1. Copy the raw PACS binary from your [Omnikey](#using-omnikey-reader-5427ck-gen2-and-proxmark3) output +2. PM3 ``wiegand decode --bin `` + +Below is example syntax, you will use your specific card information gathered in the previous step. + +3. `lf hid clone -w c1k48s --fc 69 --cn 69420` +4. `lf hid reader` to verify output + +## Using Flipper Zero +^[Top](#top) + +1. After reading your credential with [NARD / Seader](#using-flipper-zero-with-nard) +2. select the ``save RFID`` option +3. Use the 125kHz RFID app and write the data to a T5577 + diff --git a/doc/img/readermanager_1.png b/doc/img/readermanager_1.png new file mode 100644 index 000000000..e23ed99f1 Binary files /dev/null and b/doc/img/readermanager_1.png differ diff --git a/doc/img/readermanager_2.png b/doc/img/readermanager_2.png new file mode 100644 index 000000000..5c2db8e29 Binary files /dev/null and b/doc/img/readermanager_2.png differ diff --git a/doc/img/readermanager_3.png b/doc/img/readermanager_3.png new file mode 100644 index 000000000..37496f765 Binary files /dev/null and b/doc/img/readermanager_3.png differ diff --git a/doc/img/readermanager_4.png b/doc/img/readermanager_4.png new file mode 100644 index 000000000..15a616f21 Binary files /dev/null and b/doc/img/readermanager_4.png differ diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index c588d3b54..340581bd1 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -8,20 +8,35 @@ Useful docs: # Table of Contents - +- [Low frequency](#low-frequency) + * [T55xx](#t55xx) + * [EM4x05](#em4x05) + * [ID82xx series](#id82xx-series) + * [ID8265](#id8265) + * [ID-F8268](#id-f8268) + * [K8678](#k8678) + * [H series](#h-series) + * [H1](#h1) + * [H5.5 / H7](h55--h7) + * [i57 / i57v2](#i57--i57v2) - [ISO14443A](#iso14443a) * [Identifying broken ISO14443A magic](#identifying-broken-iso14443a-magic) - [MIFARE Classic](#mifare-classic) * [MIFARE Classic block0](#mifare-classic-block0) * [MIFARE Classic Gen1A aka UID](#mifare-classic-gen1a-aka-uid) * [MIFARE Classic Gen1B](#mifare-classic-gen1b) - * [MIFARE Classic Gen1A OTP/One Time Programming](#mifare-classic-gen1a-otpone-time-programming) + * [Mifare Classic Direct Write OTP](#mifare-classic-direct-write-otp) + * [MIFARE Classic OTP 2.0](#mifare-classic-otp-2.0) * [MIFARE Classic DirectWrite aka Gen2 aka CUID](#mifare-classic-directwrite-aka-gen2-aka-cuid) * [MIFARE Classic DirectWrite, FUID version aka 1-write](#mifare-classic-directwrite-fuid-version-aka-1-write) - * [MIFARE Classic DirectWrite, UFUID version](#mifare-classic-directwrite-ufuid-version) - * [MIFARE Classic, other versions](#mifare-classic-other-versions) * [MIFARE Classic Gen3 aka APDU](#mifare-classic-gen3-aka-apdu) - * [MIFARE Classic Gen4 aka GDM](#mifare-classic-gen4-aka-gdm) + * [MIFARE Classic USCUID](#mifare-classic-uscuid) + * [FUID](#fuid) + * [UFUID](#ufuid) + * [ZUID](#zuid) + * [GDM](#gdm) + * [GDCUID](#gdcuid) + * [MIFARE Classic, other versions](#mifare-classic-other-versions) * [MIFARE Classic Super](#mifare-classic-super) - [MIFARE Ultralight](#mifare-ultralight) * [MIFARE Ultralight blocks 0..2](#mifare-ultralight-blocks-02) @@ -30,6 +45,11 @@ Useful docs: * [MIFARE Ultralight EV1 DirectWrite](#mifare-ultralight-ev1-directwrite) * [MIFARE Ultralight C Gen1A](#mifare-ultralight-c-gen1a) * [MIFARE Ultralight C DirectWrite](#mifare-ultralight-c-directwrite) + * [UL series (RU)](#ul-series-ru) + * [UL-Y](#ul-y) + * [ULtra](#ultra) + * [UL-5](#ul-5) + * [UL, other chips](#ul-other-chips) - [NTAG](#ntag) * [NTAG213 DirectWrite](#ntag213-directwrite) * [NTAG21x](#ntag21x) @@ -37,12 +57,195 @@ Useful docs: * ["DESFire" APDU, 7b UID](#desfire-apdu-7b-uid) * ["DESFire" APDU, 4b UID](#desfire-apdu-4b-uid) - [ISO14443B](#iso14443b) - * [ISO14443B magic](#iso14443b-magic) + * [Tiananxin TCOS CPU card](#tiananxin-tcos-cpu-card) - [ISO15693](#iso15693) * [ISO15693 magic](#iso15693-magic) - [Multi](#multi) - * [Gen 4 GTU](#gen-4-gtu) + * [UMC](#umc) +- [Other](#other) + * [SID](#sid) + * [NSCK-II](#nsck-ii) +# Low frequency + +## T55xx +^[Top](#top) + +The temic T55xx/Atmel ATA5577 is the most commonly used chip for cloning LF RFIDs. + +A useful document can be found [here](https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/T5577_Guide.md). + +### Characteristics + +* 28/24 bytes of user memory (without/with password) +* Universal output settings (data rate, modulation, etc) +* Password protection (4 bytes), usually "19920427" +* Lock bits per page +* Analog frontend setup +* Other names: + * 5577 + * 5200 (CN) + - Cut down version of T55xx chip (no analog frontend setup, no test mode support). + * H2 (RU) + - Seems to be renamed 5200 chip. + * RW125T5 (RU) +* Old variant "T5555" is hard to come across + +### Detect + +``` +[usb] pm3 --> lf search +... +[+] Chipset detection: T55xx +``` + +This will **not** work if you have a downlink mode other than fixed bit length! + +### Commands + +*See ATMEL ATA5577C datasheet for sending commands to chip* + +* **Do not mix "password read" and "regular write" commands! You risk potentially writing incorrect data. +* When replying, the chip will use the modulation and data rate specified in block 0. + +## EM4x05 +^[Top](#top) + +The EM4305 and EM4205 (and 4469/4569) chips are the 2nd most common used chips for cloning LF RFIDs. +It is also used by HID Global (but with a custom chip) for HIDProx credentials. + +### Characteristics + +* 36 bytes of user memory +* Output settings are limited (ASK only, FSK added on HID variant) +* Password protection (4 bytes), usually "84AC15E2" +* Lock page used +* Other names: + * H3 (RU) + * RW125EM (RU) + +### Detect + +``` +[usb] pm3 --> lf search +... +[+] Chipset detection: EM4x05 / EM4x69 +``` + +### Commands + +*See EM microelectronic EM4305 datasheet for sending commands to chip* + +## ID82xx series +^[Top](#top) + +These are custom chinese chips designed to clone EM IDs only. Often times, these are redesigned clones of Hitag chips. + +### ID8265 +^[Top](#top) + +This is the cheapest and most common ID82xx chip available. It is usually sold as T55xx on AliExpress, with excuses to use cloners. + +#### Characteristics + +* Chip is likely a Hitag μ (micro) +* Password protection (4b), usually "1AC4999C" +* Currently unimplemented in proxmark3 client +* Other names: + * ID8210 (CN) + * H-125 (CN) + * H5 (RU) + - The sales of "H5" have been ceased because "the chip was leaked". + +#### Detect + +``` +[usb] pm3 --> lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W00011 -s 3000 +[usb] pm3 --> data plot +``` + +Check the green line of the plot. It must be a straight line at the end with no big waves. + +### ID-F8268 +^[Top](#top) + +This is an "improved" variant of ID82xx chips, bypassing some magic detection in China. + +#### Characteristics + +* Chip is likely a Hitag 1 +* Unsure whether password protection is used +* Currently unimplemeneted in proxmark3 client +* Other names: + - F8278 (CN) + - F8310 (CN) + +#### Detect + +``` +[usb] pm3 --> lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W00110 -s 3000 +[usb] pm3 --> data plot +``` + +Check the green line of the plot. It must be a straight line at the end with no big waves. + +### K8678 +^[Top](#top) + +This is an "even better" chip, manufactured by Hyctec. + +#### Characteristics + +* Chip is likely a Hitag S256 +* Plain mode used, no password protection +* Currently unimplemented in proxmark3 client +* Memory access is odd (chip doesnt reply to memory access commands for unknown reason) + +#### Detect + +``` +[usb] pm3 --> lf cmdread -d 50 -z 116 -o 166 -e W3000 -c W00110 -s 3000 +[usb] pm3 --> data plot +``` + +Check the green line of the plot. It must be a straight line at the end with no big waves. + +## H series +^[Top](#top) + +These are chips sold in Russia, manufactured by iKey LLC. Often times these are custom. + +### H1 +^[Top](#top) + +Simplest EM ID cloning chip available. Officially discontinued. + +#### Characteristics + +* Currently almost all structure is unknown +* No locking or password protection + * "OTP" chip is same chip, but with EM ID of zeroes. Locked after first write +* Other names: + * RW64bit + * RW125FL + + +### H5.5 / H7 +^[Top](#top) + +First "advanced" custom chip with H naming. + +#### Characteristics + +* Currently all structure is unknown +* No password protection +* Only supported by Russian "TMD"/"RFD" cloners +* H7 is advertised to work with "Stroymaster" access control +* Setting ID to "3F0096F87E" will make the chip show up like T55xx + +### i57 / i57v2 + +\[ Chip is discontinued, no info \] # ISO14443A @@ -109,7 +312,8 @@ UID 7b: ## MIFARE Classic Gen1A aka UID ^[Top](#top) -aka MF ZERO +* Other names: + - ZERO (RU) ### Identify ^[Top](#top) @@ -267,16 +471,50 @@ hf 14a info * Read: `40(7)`, `30xx` * Write: `40(7)`, `A0xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc -## MIFARE Classic Gen1A OTP/One Time Programming +## Mifare Classic Direct Write OTP ^[Top](#top) -aka MF OTP 2.0 +Chip manufactured by iKey LLC as a bypass for Gen1 filters. +Support Direct Write as CUID, but block0 can be written only once. + +The chip had an issue in the protocol implementation. + +The reader could interrupt radiofield for 2-3 microseconds (standard pause in the bit period of ISO14443-2). +After the response to first `26 (7)` command, but before the following `93 70` command. In that case original M1 card will stop the flow, but OTP will continue it. + +That issue led to the development of the filters against that card and discontinuation of the production. + +As a successor, [OTP 2.0](#mifare-classic-otp-2.0) was created. + +### Characteristics +^[Top](#top) + +* Initial UID is AA55C396 +* Android compatible + +### Identify +^[Top](#top) + +Only possible before personalization. + +``` +hf 14a info +... +[+] Magic capabilities : Write Once / FUID +``` + +## MIFARE Classic OTP 2.0 +^[Top](#top) Similar to Gen1A, but after first block 0 edit, tag no longer replies to 0x40 command. +Were manufactured by iKey LLC as a replacement for [OTP](#mifare-classic-direct-write-otp) -Initial UID is 00000000 +### Characteristics -All bytes are 00 from factory wherever possible. +* Initial UID is 00000000 +* BCC: unknown +* SAK/ATQA: fixed +* All bytes are 00 from factory wherever possible. ### Identify ^[Top](#top) @@ -287,6 +525,7 @@ Only possible before personalization. hf 14a info ... [+] Magic capabilities : Gen 1a +[+] Prng detection: hard ``` ### Magic commands @@ -299,6 +538,11 @@ hf 14a info (also referred as MCT compatible by some sellers) +* Other names: + * MF-8 (RU) + * MF3 (RU) + - What's so special about this chip in particular..? + ### Identify ^[Top](#top) @@ -439,48 +683,6 @@ hf 14a config --std hf 14a reader ``` -## MIFARE Classic DirectWrite, FUID version aka 1-write -^[Top](#top) - -aka MF OTP - -Same as MIFARE Classic DirectWrite, but block0 can be written only once. - -Initial UID is AA55C396 - -### Identify -^[Top](#top) - -Only possible before personalization. - -``` -hf 14a info -... -[+] Magic capabilities : Write Once / FUID -``` - -## MIFARE Classic DirectWrite, UFUID version -^[Top](#top) - -Same as MIFARE Classic DirectWrite, but block0 can be locked with special command. - -### Identify -^[Top](#top) - -**TODO** - -### Proxmark3 commands -^[Top](#top) - -To lock definitively block0: -``` -hf 14a raw -a -k -b 7 40 -hf 14a raw -k 43 -hf 14a raw -k -c e000 -hf 14a raw -k -c e100 -hf 14a raw -c 85000000000000000000000000000008 -``` - ## MIFARE Classic Gen3 aka APDU ^[Top](#top) @@ -554,85 +756,113 @@ hf 14a raw -s -c -t 2000 90F0CCCC10 041219c3219316984200e32000000000 hf 14a raw -s -c 90FD111100 ``` -## MIFARE Classic Gen4 aka GDM +## MIFARE Classic USCUID ^[Top](#top) -Tag has shadow mode enabled from start. -Meaning every write or changes to normal MFC memory is restored back to a copy from persistent memory after about 3 seconds -off rfid field. -Tag also seems to support Gen2 style, direct write, to block 0 to the normal MFC memory. +TLDR: These magic cards have a 16 byte long configuration page, which usually starts with 0x85. +All of the known tags using this, except for Ultralight tags, are listed here. -The persistent memory is also writable. For that tag uses its own backdoor commands. -for example to write, you must use a customer authentication byte, 0x80, to authenticate with an all zeros key, 0x0000000000. -Then send the data to be written. - -This tag has simular commands to the [UFUID](#mifare-classic-directwrite-ufuid-version) -This indicates that both tagtypes are developed by the same person. - -**OBS** - -When writing to persistent memory it is possible to write _bad_ ACL and perm-brick the tag. - -**OBS** - -It is possible to write a configuration that perma locks the tag, i.e. no more magic - -### Identify -^[Top](#top) - -``` -hf 14a info -... -[+] Magic capabilities : Gen 4 GDM -``` -### Magic commands -^[Top](#top) - -* Auth: `80xx`+crc -* Write: `A8xx`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc -* Read config: `E000`+crc -* Write config: `E100`+crc, `xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx`+crc +You cannot turn a Classic tag into an Ultralight and vice-versa! ### Characteristics ^[Top](#top) -* Have no knowledge in ATQA/SAK/BCC quirks or if there is a wipe, softbrick recover -* Its magic part seem to be three identified custom command. -* Auth command 0x80, with the key 0x0000000000, Write 0xA8 allows writing to persistent memory, Read 0xE0 which seems to return a configuration. This is unknown today what these bytes are. +* UID: 4/7 bytes +* ATQA: always read from block 0 +* SAK: read from backdoor or configuration +* BCC: read from memory, beware! +* ATS: no/unknown -Read config: -1. sending custom auth with all zeros key -2. send 0xE000, will return the configuration bytes. -`results: 850000000000000000005A5A00000008` +### Magic commands +^[Top](#top) +* Magic authentication: select, `8000+crc`, `[Crypto1 Auth: 000000000000]` + - Backdoor read: `38xx+crc` + - Backdoor write: `A8xx+crc`, `[16 bytes data]+crc` + - Read configuration: `E000+crc` + - Write configuration: `E100+crc`; `[16 bytes data]+crc` +* Magic wakeup (A: 00): `40(7)`, `43` +* Magic wakeup (B: 85): `20(7)`, `23` + - Backdoor read main block: `30xx+crc` + - Backdoor write main block: `A0xx+crc`, `[16 bytes data]+crc` + - Read hidden block: `38xx+crc` + - Write hidden block: `A8xx+crc`, `[16 bytes data]+crc` + - Read configuration: `E000+crc` + - Write configuration: `E100+crc` + + **DANGER** + - Set main memory and config to 00 `F000+crc` + - Set main memory and config to FF `F100+crc` + - Set main memory and config to 55 (no 0A response) `F600+crc` + - Set backdoor memory to 00 `F800+crc` + - Set backdoor memory to FF `F900+crc` + - Set backdoor memory to 55 (no 0A response) `FE00+crc` + +### USCUID configuration guide +^[Top](#top) -Mapping of configuration bytes so far: +1. Configuration ``` -850000000000000000005A5A00000008 - ^^ --> SAK +85000000000000000000000000000008 + ^^^^^^ ^^ ^^ >> ??? Mystery ??? +^^^^ >> Gen1a mode (works with bitflip) + ^^ >> Magic wakeup command (00 for 40-43; 85 for 20-23) + ^^ >> Block use of Key B if readable by ACL + ^^ >> CUID mode + ^^ >> MFC EV1 CL2 Perso config* + ^^ >> Shadow mode** + ^^ >> Magic Auth command + ^^ >> Static encrypted nonce mode + ^^ >> Signature sector + ^^ >> SAK*** + +To enable an option, set it to 5A. +* 5A - unfused F0. C3 - F0: CL2 UID; A5 - F1: CL2 UID with anticollision shortcut; 87 - F2: CL1 Random UID; 69 - F3: CL1 non-UID. Anything else is going to be ignored, and set as 4 bytes. +** Do not change the real ACL! Backdoor commands only acknowledge FF0780. To recover, disable this byte and issue regular write to sector trailer. +*** If perso byte is enabled, this SAK is ignored, and hidden SAK is used instead. +``` +* Gen1a mode: Allow using custom wakeup commands, like real gen1a chip, to run backdoor commands, as well as some extras. +* Magic wakeup command: Use different wakeup commands for entering Gen1a mode. A) 00 - 40(7), 43; B) 85 - 20(7), 23. +* Block use of Key B if readable by ACL: Per the MF1ICS50 datasheet, if Key B is readable by the ACL, using it shall give a Cmd Error 04. This option controls whether it happens or not. +* CUID mode: Allow direct write to block 0, instead of giving Cmd Error 04. +* MFC EV1 CL2 Perso config: When configured, the tag behaves like a real Mifare Classic EV1 7B UID tag, and reads UID from backdoor blocks. Otherwise, the tag acts like a 4 byte tag. +* Shadow mode: Writes to memory persisting in tag RAM. As soon as no power is left, the contents are restored to saved data. +* Magic Auth Command: Acknowledge command `8000` after selection, and call for Crypto1 auth with key `000000000000`. +* Static encrypted nonce mode: Use static encrypted nonces for authentication, making key recovery impossible. +* Signature sector: Acknowledge auth commands to sector 17, which is stored in backdoor sector 1. +* SAK: If perso byte is not set, after UID select, send this value. + + +2. Backdoor blocks ``` -Write config: -1. sending custom auth with all zeros key -2. send 0xE100 -3. send 16 bytes - -**Warning** - -Example of configuration to Perma lock tag: -`85000000000000000000000000000008` - - -It is unknown what kind of block 0 changes the tag supports -* UID: 4b -* ATQA/SAK: unknown -* BCC: unknown -* ATS: none +Sector 0 +88 04 BD E5 D4 04 6A BB 5B 80 0A 08 44 00 00 00 - Block 0: Perso F0, F1 data +^^ ^^ ^^ ^^ - UID0 + ^^ - BCC0 + ^^ - SAK0 (0x04 to call for CL2) + ^^ ^^ ^^ ^^ - UID1 + ^^ - BCC1 + ^^ - SAK1 + ^^ ^^ ^^ ^^ - Unused +04 BD E5 6A 36 08 00 00 00 00 00 00 00 00 00 00 - Block 1: Perso F3 data +^^ ^^ ^^ ^^ - UID0 + ^^ - BCC0 + ^^ - SAK0 + ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ ^^ - Unused +Block 2: unused +Block 3: ignored (custom keys, acl; broken acl ignored - anticollision will still work) +Sector 1 +[Signature sector (#17) - needs config byte 13 (from 0) enabled to allow auth] +Sectors 2-15 +[Unused] +``` ### Proxmark3 commands ^[Top](#top) ``` -# Write to persistent memory +Using magic auth: +# Write to persistent memory: hf mf gdmsetblk # Read configuration (0xE0): @@ -646,20 +876,278 @@ hf mf gdmsetcfg ^[Top](#top) No implemented commands today +### Variations +^[Top](#top) +| Factory configuration | Name | +| --- | --- | +| 850000000000000000005A5A00000008 | GDM | +| 850000000000005A00FF005A00000008 | GDCUID | +| 850000000000005A0000005A5A5A0008 | UCUID | +| 8500000000005A00005A005A005A0008 | "7 byte hard" | +| 7AFF850102015A00005A005A005A0008 | M1-7B | +| 7AFF85000000000000FF000000000008 | FUID | +| 7AFF000000000000BAFA358500000008 | PFUID | +| 7AFF000000000000BAFA000000000008 | UFUID | +| 7AFF0000000000000000000000000008 | ZUID | + +*Not all tags are the same!* UFUID, ZUID and PFUID* are not full implementations of Magic85 - they only acknowledge the first 8 (except wakeup command) and last config byte(s). + +*Read and write config commands are flipped + +Well-known variations are described below. + +## FUID +^[Top](#top) + +Known as "write only once", which is only partially true. + +Allows direct write to block 0 only when UID is default `AA55C396`. But always could be rewritten multiple times with backdoors commands. + +Backdoor commands are available even after the personalization and makes that tag detectable. + +That's a key difference from [OTP](#mifare-classic-direct-write-otp)/[OTP 2.0](#mifare-classic-otp-2.0) tags. + +### Characteristics +^[Top](#top) + +* Configuration block value: `7AFF85000000000000FF000000000008` +* Initial UID: `AA55C396` +* Allows direct write to the block 0 (before the personalisation), so is Android compatible +* Responds to magic wakeup `20(7)`, `23` commands + +### Identify +^[Top](#top) +``` +hf 14a info +... +[+] Magic capabilities : Write Once / FUID + +``` + +⚠️ **Current Proxmark3 identification is based on the initial UID. That could lead to the false positives. Also that doesn't allow to detect FUID after the personalization.** + +More correct detection should be based on a backdoor commands and configuration block value: + +``` +[usb] pm3 --> hf 14a raw -k -a -b 7 20 +[+] 0A +[usb] pm3 --> hf 14a raw -k -a 23 +[+] 0A +[usb] pm3 --> hf 14a raw -c -k -a E000 +[+] 7A FF 85 00 00 00 00 00 00 FF 00 00 00 00 00 08 [ 66 92 ] +``` +### Commands +^[Top](#top) + +* Commands described under the corresponding section of USCUID chip +* Example of changing block 0 after the personalization: +``` +[usb] pm3 --> hf 14a raw -k -a -b 7 20 +[+] 0A +[usb] pm3 --> hf 14a raw -k -a 23 +[+] 0A +[usb] pm3 --> hf 14a raw -c -k -a A000 +[+] 0A +[usb] pm3 --> hf 14a raw -c -k -a B502454EBC0804000168AA8947CE4D1D <- Writing 0 block with the backdoor command +[+] 0A +[usb] pm3 --> hf 14a raw -c -a 5000 +[usb] pm3 --> hf mf rdbl --blk 0 + +[=] # | sector 00 / 0x00 | ascii +[=] ----+-------------------------------------------------+----------------- +[=] 0 | B5 02 45 4E BC 08 04 00 01 68 AA 89 47 CE 4D 1D | ..EN.....h..G.M. +``` + +## UFUID +^[Top](#top) + +The tag is positioned as "sealable UID", so that means you could use the same commands, as you could use for UID chip in a default state. But after the sealing (changing the configuration) tag will not answer to the backdoor commands and will behave as a normal Mifare Classic tag. + +*But at the same time there is some unidentified behavior, which doesn't fully corresponds the protocol and original Mifare Classic tags. So the tag could be filtered out with a protocol-based filters (i.e. Iron Logic OTP2 filter).* + +### Characteristics +^[Top](#top) + +* Configuration block value: `7AFF000000000000BAFA000000000008` +* No direct write to block 0 +* Responds to magic wakeup `40(7)`, `43` commands before the sealing +* Acknowledge only the first (except wakeup command) and last config byte(s), so doesn't have the hidden block + +### Identify +^[Top](#top) + +``` +hf 14a info +... +[+] Magic capabilities : Gen 1a + +``` + +Currently Proxmark3 doesn't identify it as a separate tag. +Before the sealing could be detected from the config block value: + +``` +[usb] pm3 --> hf 14a raw -k -a -b 7 40 +[+] 0A +[usb] pm3 --> hf 14a raw -k -a 43 +[+] 0A +[usb] pm3 --> hf 14a raw -c -k -a E000 +[+] 7A FF 00 00 00 00 00 00 BA FA 00 00 00 00 00 08 [ F1 69 ] +``` + +### Commands +^[Top](#top) + +All commands are available before sealing. +* Proxmark3 magic Gen1 commands +* Read configuration: `E000+crc` +* Write configuration: `E100+crc` + +Example of the sealing, performed by Chinese copiers in raw commands: + +``` +hf 14a raw -a -k -b 7 40 +hf 14a raw -k 43 +hf 14a raw -k -c e100 +hf 14a raw -c 85000000000000000000000000000008 +``` + +## ZUID +^[Top](#top) + +That tag is a UID tag, built on USCUID chip. It doesn't sold separately, but could be found on marketplaces under the guise of a UID tag. + +### Characteristics +^[Top](#top) + +* Configuration block value: `7AFF0000000000000000000000000008` +* No direct write to block 0 +* Responds to magic wakeup `40(7)`, `43` commands +* Acknowledge only the first (except wakeup command) and last config byte(s), so doesn't have the hidden block + +### Identify +^[Top](#top) + +``` +hf 14a info +... +[+] Magic capabilities : Gen 1a + +``` + +Currently Proxmark3 doesn't identify it as a separate tag. +Could be detected from the config block value: + +``` +[usb] pm3 --> hf 14a raw -k -a -b 7 40 +[+] 0A +[usb] pm3 --> hf 14a raw -k -a 43 +[+] 0A +[usb] pm3 --> hf 14a raw -c -k -a E000 +[+] 7A FF 00 00 00 00 00 00 00 00 00 00 00 00 00 08 [ 4E 17 ] +``` + +### Commands +^[Top](#top) + +* Proxmark3 magic Gen1 commands +* Read configuration: `E000+crc` +* Write configuration: `E100+crc` + +## GDM +^[Top](#top) + +The tag has a shadow mode, which means that every change to normal MFC memory would be restored back from the persistent memory after being off RFID field. + +### Characteristics +^[Top](#top) + +* Configuration block value: `850000000000000000005A5A00000008` +* No direct write to block 0 +* Responds to magic authentication: select, `8000+crc`, `[Crypto1 Auth: 000000000000]` + +### Identify +^[Top](#top) + +``` +hf 14a info +... +[+] Magic capabilities : Gen 4 GDM + +``` + +Could be manually validated with the configuration block value: + +``` +[usb] pm3 --> hf mf gdmcfg +[+] config... 85 00 00 00 00 00 00 00 00 00 5A 5A 00 00 00 08 +``` + +### Commands +^[Top](#top) + +* Magic authentication: select, `8000+crc`, `[Crypto1 Auth: 000000000000]` + * Backdoor read: `38xx+crc` + * Backdoor write: `A8xx+crc`, `[16 bytes data]+crc` + * Read configuration: `E000+crc` + * Write configuration: `E100+crc`; `[16 bytes data]+crc` +* Proxmark3 commands (does auth and executes the corresponding command) + * Backdoor write: `gdmsetcfg` + * Read configuration: `gdmcfg` + * Write configuration: `gdmsetcfg` + +## GDCUID +^[Top](#top) + +That tag is a CUID tag, built on USCUID chip. It doesn't sold separately, but could be found on marketplaces under the guise of a CUID tag. + +### Characteristics +^[Top](#top) + +* Configuration block value: `850000000000005A00FF005A00000008` +* Allows direct write to the block 0, so is Android compatible +* Responds to magic authentication: select, `8000+crc`, `[Crypto1 Auth: 000000000000]` + +### Identify +^[Top](#top) + +``` +hf 14a info +... +[+] Magic capabilities : Gen 4 GDM + +``` +Currently Proxmark3 doesn't identify it as a separate tag. +Could be manually validated with the configuration block value: + +``` +[usb] pm3 --> hf mf gdmcfg +[+] config... 85 00 00 00 00 00 00 5A 00 FF 00 5A 00 00 00 08 +``` + +### Commands +^[Top](#top) + +* Magic authentication: select, `8000+crc`, `[Crypto1 Auth: 000000000000]` + * Read configuration: `E000+crc` + * Write configuration: `E100+crc`; `[16 bytes data]+crc` +* Proxmark3 commands (does auth and executes the corresponding command) + * Read configuration: `gdmcfg` + * Write configuration: `gdmsetcfg` + ## MIFARE Classic, other versions ^[Top](#top) **TODO** -* ZXUID, EUID, ICUID, KUID, HUID, RFUID ? -* Some cards exhibit a specific SAK=28 ?? +* ZXUID, EUID, ICUID, KUID? ## MIFARE Classic Super ^[Top](#top) It behaves like regular Mifare Classic but records reader auth attempts. -#### MIFARE Classic Super Gen1 +### MIFARE Classic Super Gen1 ^[Top](#top) Old type of cards, hard to obtain. They are DirectWrite, UID can be changed via 0 block or backdoor commands. @@ -684,19 +1172,19 @@ Backdoor commands provided over APDU. Format: 👉 You can't change UID with backdoor command if incorrect data is written to the 0 sector trailer! -#### MIFARE Classic Super Gen1B +### MIFARE Classic Super Gen1B DirectWrite card, ATS unknown. Probably same as Gen1, except backdoor commands. Implementation: https://github.com/netscylla/super-card/blob/master/libnfc-1.7.1/utils/nfc-super.c -#### MIFARE Classic Super Gen2 +### MIFARE Classic Super Gen2 ^[Top](#top) New generation of cards, based on limited Gen4 chip. Emulates Gen1 backdoor protocol, but can store up to 7 different traces. -Card always answer `ff ff ff ff` to auth, so writing/reading it via Mifare protocol is impossible. +Card always answers `ff ff ff ff` as `at`, so reading/writing it via Mifare protocol is impossible. -UID is changeable via Gen4 backdoor write to 0 block. +UID is changeable via UMC backdoor write to 0 block. * UID: 4b and 7b versions * ATQA/SAK: fixed @@ -707,12 +1195,53 @@ Gen4 commands available: ``` CF 34 <1b length><0-16b ATS> // Configure ATS -CF CC // Factory test, returns 00 00 00 02 AA +CF CC // Version information, returns 00 00 00 02 AA CF CD <1b block number><16b block data> // Backdoor write 16b block CF CE <1b block number> // Backdoor read 16b block CF FE <4b new_password> // Change password ``` +### MIFARE Classic Super Furui +^[Top](#top) + +#### Characteristics +^[Top](#top) + +* SAK/ATQA: play blindly the block0 bytes, beware! +* BCC: play blindly the block0 BCC bytes, beware! +* PRNG: hard + +**!!!WARNING!!!** This tag can die for no reason (no reply to WUPA/REQA). We don't know why this happens. + +#### Identify +^[Top](#top) + +``` +[usb] pm3 --> hf 14a raw -sct 250 AAA500000000000000000000000000000000 +[+] 90 00 +``` + +#### Magic commands +^[Top](#top) + +* Configure: `AAA5[16 byte config]`+crc +* Write block 0: `AAA4[4b UID][1b BCC][1b SAK][2b ATQA reversed]0000000000000000`+crc +* Recover trace: `AAA8[00/01][00-08]`+crc + +Caution: tag does not append CRC to magic responses! + +Please use config as 00 bytes. + +Parsing traces: +``` +44 33 22 11 03 61 08 68 7A C7 4B 62 43 A6 11 6F 64 F3 +^^ ^^ ^^ ^^ -- UID + ^^ ^^ -- auth command, reversed + ^^ ^^ ^^ ^^ -- Auth (nt) + ^^ ^^ ^^ ^^ -- Auth (nr) + ^^ ^^ ^^ ^^ -- Auth (ar) +``` + ### Identify ^[Top](#top) @@ -724,6 +1253,15 @@ hf 14a info [+] Magic capabilities : Super card (Gen ?) ``` +### Proxmark3 commands + +``` +[usb] pm3 --> hf mf supercard +... + +[usb] pm3 --> hf mf supercard --furui +... +``` # MIFARE Ultralight ^[Top](#top) @@ -746,6 +1284,7 @@ Int is internal, typically 0x48 Anticol shortcut (CL1/3000) is supported for UL, ULC, NTAG except NTAG I2C +Some cards have a password: `B6AA558D`. Usually "copykey" chips. ## MIFARE Ultralight Gen1A ^[Top](#top) @@ -874,8 +1413,6 @@ See `--uid` and `--full` ## MIFARE Ultralight EV1 DirectWrite ^[Top](#top) -aka UL2 - Similar to MFUL DirectWrite ### Identify @@ -957,11 +1494,6 @@ hf 14a info * ATS: 0A78008102DBA0C119402AB5 * Anticol shortcut (CL1/3000): fails -**TODO** - -* UL-X, UL-Y, UL-Z, ULtra, UL-5 ? - - # NTAG ^[Top](#top) @@ -1027,6 +1559,109 @@ Anticol shortcut (CL1/3000): fails script run hf_mfu_magicwrite -h ``` +## UL series (RU) +^[Top](#top) + +Custom chips, manufactured by iKey LLC for cloning Ultralight tags used in Visit intercoms. That leads to the non-standard for Ultralight chips tag version. + +### UL-Y +^[Top](#top) + +Ultralight magic, 16 pages. Recommended for Vizit RF3.1 with markings "3.1" or "4.1". +Behavior: allows writes to page 0-2. + +#### Identify +^[Top](#top) + +``` +hf mfu rdbl --force -b 16 +hf 14a raw -sct 250 60 +``` +If tag replies with +`Cmd Error: 00` +`00 00 00 00 00 00 00 00` +then it is UL-Y. + +### ULtra +^[Top](#top) + +Ultralight EV1 magic; 41 page. Recommended for Vizit RF3.1 with 41 page. +Behavior: allows writes to page 0-2. + +#### Identify +^[Top](#top) + +``` +hf mfu info +... +[=] TAG IC Signature: 0000000000000000000000000000000000000000000000000000000000000000 +[=] --- Tag Version +[=] Raw bytes: 00 34 21 01 01 00 0E 03 +[=] Vendor ID: 34, Mikron JSC Russia +[=] Product type: 21, unknown +``` + +#### ULtra flavour 1 +^[Top](#top) + +Could be identified by indirect evidence before writing + +* Initial UID: `34 D7 08 11 AD D7 D0` +* `hf mfu dump --ns` + ``` + [=] 3/0x03 | CF 39 A1 C8 | 1 | .9.. + [=] 4/0x04 | B6 69 26 0D | 1 | .i&. + [=] 5/0x05 | EC A1 73 C4 | 1 | ..s. + [=] 6/0x06 | 81 3D 29 B8 | 1 | .=). + [=] 16/0x10 | 6A F0 2D FF | 0 | j.-. + [=] 20/0x14 | 6A F0 2D FF | 0 | j.-. + [=] 24/0x18 | 6A F0 2D FF | 0 | j.-. + [=] 38/0x26 | 00 E2 00 00 | 0 | .... <- E2, Virtual Card Type Identifier is not default + + ``` + +#### ULtra flavour 2 +^[Top](#top) + +Could be identified by indirect evidence before writing + +* Initial UID: `04 15 4A 23 36 2F 81` +* Values in pages `3, 4, 5, 6, 16, 20, 24, 38` are default for that tag flavour + +### UL-5 +^[Top](#top) + +Ultralight EV1 magic; 41 page. Recommended for Vizit RF3.1 with 41 page. +Created as a response to filters that try to overwrite page 0 (as a detection for [ULtra](#mifare-ultra) tags). + +Behavior: similar to Ultra, but after editing page 0 become locked and tag becomes the original Mifare Ultralight EV1 (except the tag version, which remains specific). + +**WARNING!** When using UL-5 to clone, write UID pages in inverse (from 2 to 0) and do NOT make mistakes! This tag does not allow reversing one-way actions (OTP page, lock bits). + +#### Identify +^[Top](#top) + +``` +hf mfu info +[=] UID: AA 55 C3 A1 30 61 80 +TAG IC Signature: 0000000000000000000000000000000000000000000000000000000000000000 +[=] --- Tag Version +[=] Raw bytes: 00 34 21 01 01 00 0E 03 +[=] Vendor ID: 34, Mikron JSC Russia +``` + +After personalization it is not possible to identify UL-5. + +Usually chips have initial UIDs: + * `AA 55 C3 A4 30 61 80` + * `AA 55 C3 A4 30 61 80` + +### UL, other chips + +**TODO** + +UL-X, UL-Z - ? + # DESFire ^[Top](#top) @@ -1084,7 +1719,8 @@ Android compatible ### Characteristics ^[Top](#top) -* ATQA: 0008 ??? This is not DESFire, 0008/20 doesn't match anything +* ATQA: 0008 + * This is FM1208-9, NOT DESFire! * SAK: 20 * ATS: 0675338102005110 or 06757781028002F0 @@ -1129,12 +1765,37 @@ hf 14a info # ISO14443B ^[Top](#top) -## ISO14443B magic +## Tiananxin TCOS CPU card ^[Top](#top) -No such card is available. +This is a card sold on Taobao for testing readers. +ISO14443-4 compliant. -Some vendor allow to specify an ID (PUPI) when ordering a card. +### Identify + +``` +hf 14a apdu -s 90B2900000 // Get Card OS version +>>> 90 B2 90 00 00 +<<< 54 43 4F 53 20 56 31 2E 34 2E 30 90 00 | TCOS V1.4.0.. +``` + +### Magic commands + +All commands in APDU. + +``` +CL IN P1 P2 Lc Data +90 F4 CC CC 01 [..1 ] // Change protocol used (1: ISO14443 [AA - type A, BB - type B]) +90 F6 CC CC 01 [TA1 ] // Change TA1 value (transfer speed) +90 F8 CC CC 01 [..1 ] // Use random UID/PUPI value (1: FF: static, AB: random) +90 F8 DD DD 01 [..1 ] // Set UID length (1: bytes in UID (04, 07, 0A for 4, 7, 10 bytes accordingly)) +90 F8 EE EE 0B [... ] // Set UID/PUPI value (FF+enter UID value here). To clear, use Lc=01; data=00. +90 FA CC CC 01 [FSCI] // Set FSCI (1: value 0-8) +90 FC CC CC 01 [SFGI] // Set SFGI (DO NOT SET TOO HIGH!) (1: value 0-E) +90 FE CC CC 01 [FWI ] // Set FWI (DO NOT SET BELOW 4!!!) (value 0-E) +``` + +More commands to follow. Be careful with some. # ISO15693 ^[Top](#top) @@ -1164,7 +1825,7 @@ script run hf_15_magic -u E004013344556677 # Multi ^[Top](#top) -## Gen 4 GTU +## UMC ^[Top](#top) A.k.a ultimate magic card, most promenent feature is shadow mode (GTU) and optional password protected backdoor commands. @@ -1197,6 +1858,8 @@ Can emulate MIFARE Classic, Ultralight/NTAG families, 14b UID & App Data 👉 **TODO** If the password is not default, Tag doesn't get identified correctly by latest Proxmark3 client (it might get mislabeled as MFC Gen2/CUID, Gen3/APDU or NTAG21x Modifiable, depending on configured UID/ATQA/SAK/ATS) +👉 **TODO** Using C6 command can change config due to a bug in some cards. CC should be used instead. + ``` hf 14a info [+] Magic capabilities : Gen 4 GTU @@ -1217,7 +1880,7 @@ There are two ways to program this card. ***OR*** - 2. Use the hf_mf_ultimatecard.lua script commands designated but the `script run hf_mf_ulimatecard` examples. + 2. Use the hf_mf_ultimatecard.lua script commands designated but the `script run hf_mf_ultimatecard` examples. This script is nof fully compartible with new version UMC. script run hf_mf_ultimatecard.lua -h @@ -1281,7 +1944,7 @@ Example usage Special raw commands summary: ``` -CF 32 <00-03> // Configure GTU shadow mode +CF 32 <00-04> // Configure GTU shadow mode CF 34 <1b length><0-16b ATS> // Configure ATS CF 35 <2b ATQA><1b SAK> // Configure ATQA/SAK (swap ATQA bytes) CF 68 <00-02> // Configure UID length @@ -1289,7 +1952,7 @@ CF 69 <00-01> // (De)Activate Ultralight mode CF 6A <00-03> // Select Ultralight mode CF 6B <1b> // Set Ultralight and M1 maximum read/write sectors CF C6 // Dump configuration -CF CC // Factory test, returns 6666 +CF CC // Version info, returns `00 00 00 [03 A0 (old) / 06 A0 (new) ]` CF CD <1b block number><16b block data> // Backdoor write 16b block CF CE <1b block number> // Backdoor read 16b block CF CF <1b param> // (De)Activate direct write to block 0 @@ -1304,10 +1967,10 @@ Default ``: `00000000` * UID: 4b, 7b and 10b versions * ATQA/SAK: changeable -* BCC: auto +* BCC: computed * ATS: changeable, can be disabled -* Card Type: changeable -* Shadow mode: GTU +* Card Type: changeable +* Shadow mode: GTU * Backdoor password mode ### Proxmark3 commands @@ -1446,9 +2109,9 @@ Ultralight mode, 10b UID ### Set 14443B UID and ATQB ^[Top](#top) ^^[Gen4](#g4top) -UID and ATQB are configured according to block0 with a (14a) backdoor write. - -UID size is always 4 bytes. +* UID and ATQB are configured according to block0 with a (14a) backdoor write. +* UID size is always 4 bytes. +* 14B will show up only on new cards. (Need more tests on new card. Example not work) Example: ``` @@ -1529,11 +2192,21 @@ hf 14a raw -s -c -t 1000 CF000000006B3F ### Set shadow mode (GTU) ^[Top](#top) ^^[Gen4](#g4top) -This mode is divided into four states: off (pre-write), on (on restore), don’t care, and high-speed read and write. -If you use it, please enter the pre-write mode first. At this time, write the full card data. -After writing, set it to on. At this time, after writing the data, the first time you read the data just written, the next time you read It is the pre-written data. All modes support this operation. It should be noted that using any block to read and write in this mode may give wrong results. +This description of shadow modes wroted by seller at marketpalces: -Example: +>This mode is divided into four states: off (pre-write), on (on restore), don’t care, and high-speed read and write. If you use it, please enter the pre-write mode first. At this time, write the full card data. After writing, set it to on. At this time, after writing the data, the first time you read the data just written, the next time you read It is the pre-written data. All modes support this operation. It should be noted that using any block to read and write in this mode may give wrong results. + +And these conclusions were made after a number of tests with UMC (new version, configured as MFC for example): + +| Mode | Buffer | Standart command (rdbl, wrbl e.t.c) | Backdoor command (gsetblk, ggetblk, gload e.t.c.) | +|------|--------|-----------------------------------------|---------------------------------------------------| +| 2,3 | buf23 | read/write from/to buf23 | read/write from/to buf23 | +| 0 | buf0 | read from buf0, write to buf0 and buf23 | read/write from/to buf23 | +| 4 | - | read from buf0, write to buf23 | read/write from/to buf23 | + +Mode 1: For new card this mode looks like a bug. Reading/writing first two block use *buf23*. Reading other blocks use invalid region of memory and all returned data looks like pseudo-random. All acl looks like invalid. All data is readable by the keys and acl wich was written in *buf0*. Any writing operations in this mode use copy of *buf0* and only it. It`s not affected any other buffers. So if you change keys or/and acl you will must use new keys to read data. + +Example (not work with new UMC): `script run hf_mf_ultimatecard -w 1 -g 00 -t 18 -u 04112233445566 -s 112233445566778899001122334455667788990011223344556677 -p FFFFFFFF -a 8080 -o 11111111 -g 01` * -w 1 = wipe the card in Ultralight Mode * -g 00 = turn on pre-write mode @@ -1557,8 +2230,10 @@ hf 14a raw -s -c -t 1000 CF32<1b param> * `` * `00`: pre-write, shadow data can be written * `01`: restore mode + - WARNING: new UMC (06a0) cards return garbage data when using 01 * `02`: disabled * `03`: disabled, high speed R/W mode for Ultralight? + * `04`: split mode, work with new UMC. With old UMC is untested. ### Direct block read and write ^[Top](#top) ^^[Gen4](#g4top) @@ -1612,7 +2287,9 @@ hf 14a raw -s -c -t 1000 CF00000000CF01 ### Change backdoor password ^[Top](#top) ^^[Gen4](#g4top) -All backdoor operations are protected by a password. If password is forgotten, the card can't be recovered. Default password is `00000000`. +All backdoor operations are protected by a password. If password is forgotten, it can't be recovered. Default password is `00000000`. + +WARNING: new UMC (06A0) returns 6300 when issuing password change command. Please write the password using F0 and entering the full configuration, but with the new password. Change password: ``` @@ -1636,7 +2313,7 @@ hf 14a raw -s -c -t 1000 CFC6 Default configuration: ``` 00000000000002000978009102DABC191010111213141516040008006B024F6B - ^^^^ ?? + ^^^^ CRC, type unknown ^^ cf cmd cf: block0 direct write setting, factory value 0x02 ^^ cf cmd 6b: maximum read/write sectors, factory value 0x6b ^^ cf cmd 6a: UL mode @@ -1700,32 +2377,40 @@ hf 14a raw -s -c -t 1000 CF00000000F000010000000002000978009102DABC1910101112131 **Ultralight** ``` -hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000003 +hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000003FB ``` **Ultralight-C** ``` -hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000002 +hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000002FB ``` **Ultralight EV1** ``` -hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000000 +hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000000FB ``` **NTAG21x** ``` -hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000001 +hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000001FB ``` ### Version and Signature ^[Top](#top) ^^[Gen4](#g4top) +Don`t forget configure maximum read/write blocks. It`s can be adjusted directly in config (see *Dump configuration*) or by command 6B: + +``` +hf mf raw -s -c -t 1000 CF000000006BFB +``` + +Note: 0xFB = 251 + Ultralight EV1 and NTAG Version info and Signature are stored respectively in blocks 250-251 and 242-249. Example for an Ultralight EV1 128b with the signature sample from tools/recover_pk.py ``` -hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000000 +hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000000FB hf mfu wrbl -b 0 -d 04C12865 hf mfu wrbl -b 1 -d 5A373080 hf mfu wrbl -b 242 -d CEA2EB0B --force @@ -1743,7 +2428,7 @@ hf mfu info Example for an NTAG216 with the signature sample from tools/recover_pk.py ``` -hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000001 +hf 14a raw -s -c -t 1000 CF00000000F001010000000003000978009102DABC19101011121314151644000001FB hf mfu wrbl -b 0 -d 04E10C61 hf mfu wrbl -b 1 -d DA993C80 hf mfu wrbl -b 242 -d 8B76052E --force @@ -1758,3 +2443,72 @@ hf mfu wrbl -b 250 -d 00040402 --force hf mfu wrbl -b 251 -d 01001303 --force hf mfu info ``` + +# Other +^[Top](#top) + +These are chips to clone other ICs. Usually the originals are only sold in China. + +## SID +^[Top](#top) + +- Magic tag for Fudan FM1208-9 chips + +### Characteristics +^[Top](#top) +- ISO14443-A tag +- ATQA-SAK: `0008`-`20` +- ATS: `10 78 80 A0 02 00 9D 46 16 40 00 A3 [UID]` +- Compared to real FM1208 chip: + - CLA byte is ignored + - Command parsing is irregular (some replies are wrong) + +### Magic commands +^[Top](#top) + +**WARNING!!!** Risk of bricking tag - cause is unknown +- Below you can find a list of all INS bytes not present on real FM1208 chip, and what their output is when executed (P1, P2, Lc = 00) + - Results may vary between chips: +``` +INS | RES +0A | 44454641554C540000002018112840000000000000000000000000000000000000000000000000000000400000000000 +3B | 00000000001C0EF90000000000000000000000000000000000000000000000002000000000C09040009002840000000000000000000000000000000000006C0FC08700EB1A9F1BA01801010019000000000000000000000000000090000000000000094B066600000000007D000000000000000000000000000000003B000000107880A002009D46164000A3CA81E15000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +3C* | 0000 +3D | 6700 +7D | Tag does not reply (if 0