diff --git a/.github/ISSUE_TEMPLATE/checklist-for-release.md b/.github/ISSUE_TEMPLATE/checklist-for-release.md new file mode 100644 index 000000000..aae8ab053 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/checklist-for-release.md @@ -0,0 +1,47 @@ +--- +name: Checklist for release +about: A template when making a release (usage reserved to repo maintainers) +title: "[RELEASE 4.x] Checklist" +labels: Release +assignees: doegox, iceman1001 + +--- + +# Checklist + +- [ ] CHANGELOG.md +- [ ] `make style` +- [ ] `make clean; make client CC=clang CXX=clang++ LD=clang++` on recent Debian or Ubuntu +- [ ] `mymanualchecks.sh` +- [ ] `mycppcheck.sh` no alarming warning? +- [ ] `mymakeclang.sh` no alarming error/warning ? +- [ ] `mystandalone_makes.sh` compile all standalone modes (linux only) +- [ ] [Travis](https://travis-ci.org/github/RfidResearchGroup/proxmark3/builds) green (linux noqt / osx+qt ; with makefile (w/wo bt) / with cmake) +- [ ] [Appveyor](https://ci.appveyor.com/project/RfidResearchGroup/proxmark3/history) green (PS) + +# OS compilation and tests + +```bash +make clean && make -j PLATFORM=PM3OTHER && tools/pm3test.sh +make clean && make -j PLATFORM=PM3RDV4 && tools/pm3test.sh +make clean && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON && tools/pm3test.sh +make install; pushd /tmp; proxmark3 -c 'data load em4x05.pm3;lf search 1'; popd; make uninstall + +( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3OTHER && PM3BIN=./proxmark3 ../../tools/pm3test.sh client ) +( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3RDV4 && PM3BIN=./proxmark3 ../../tools/pm3test.sh client ) +( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON && PM3BIN=./proxmark3 ../../tools/pm3test.sh client ) +``` + +- [ ] RPI Zero +- [ ] WSL +- [ ] PSv3.3 +- [ ] Kali +- [ ] Debian +- [ ] Ubuntu20 +- [ ] ParrotOS +- [ ] Fedora +- [ ] OpenSuse +- [ ] OSX +- [ ] Android +- [ ] Termux + diff --git a/.gitignore b/.gitignore index 4ee047c9e..081c0f8e8 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ version.c *.swp *.json.bak *.pyc +*.bmp # new build file for add-ons. Makefile.platform @@ -41,6 +42,7 @@ Makefile.platform # cmake client/build/ client/android/build/ +client/deps/bzip2/ # Coverity cov-int/ @@ -65,6 +67,10 @@ tools/fpga_compress/fpga_compress tools/mfkey/mfkey32 tools/mfkey/mfkey64 tools/nonce2key/nonce2key +tools/cryptorf/cm +tools/cryptorf/sm +tools/cryptorf/sma +tools/cryptorf/sma_multi fpga/* !fpga/tests @@ -77,17 +83,16 @@ fpga/* !fpga/xst_hf.scr !fpga/go.bat !fpga/sim.tcl + # offcial dumps folder dumps/* +traces/* #client/* -# my own traces folder client/traces/* -# my own dumps folder client/dumps/* *.ice *.new -armsrc/TEMP EMV/* tools/mf_nonce_brute/mf_nonce_brute tools/andrew/* tools/jtag_openocd/openocd_configuration diff --git a/.lsan_suppressions b/.lsan_suppressions new file mode 100644 index 000000000..6ac2d14a1 --- /dev/null +++ b/.lsan_suppressions @@ -0,0 +1 @@ +leak:libfontconfig.so diff --git a/CHANGELOG.md b/CHANGELOG.md index 416f063b9..7e5f00a78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,27 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] - - Added lf em function: 4x50_sread (@tharexde) - - Added lf em functions: 4x50_info, 4x50_write, 4x50_write_password (@tharexde) + - Add low level support for 14b' aka Innovatron (@doegox) + - Add doc/cliparser.md (@mwalker33) + - Add `hf 14b apdu` - send APDU over ISO14443B (@iceman1001) + - Add `lf t55xx chk e option` - Checks calculated password based on the EM4100 id from some white cloners forumla by paleopterix (@mwalker33) + - Add `lf t55xx sniff` to allow extracting commands and passwords used be cloners. (@mwalker33) + - Add options to `lf read`, `lf cmdread`, `lf sniff` for repeated acquisitions (@doegox) + - Change options of `lf read` to match `lf cmdread`, this affects historical `d` and `s` options (@doegox) + - Add `hf waveshare` to upload picture to Waveshare NFC-Powered e-Paper (@doegox) + - Add `hf 14a config` to deal with badly configured cards: invalid ATQA/BCC/SAK (@doegox) + - Mikron JSC Russia Ultralight EV1 41 pages tag type support (@McEloff) + - Add test for Ultralight gen2 magic 'hf search' (@McEloff) + - Add test for Ultralight EV1 gen2 magic 'hf search' (@McEloff) + - Added `hf mf gen3*`magic gen 3 card operations (@McEloff) + - Readded verichip command which seems missing (@iceman1001) + - Fix missing t55x7 config block detection (@iceman1001) + - Fix missing define on proxspace (@mwalker33) + - Added `lf em 4x50_dump` (@iceman1001) + - Added `lf em 4x50_read` (@tharexde) + - Added `lf em 4x50_info` (@tharexde) + - Added `4x50_write` (@tharexde) + - Added `4x50_write_password` (@tharexde) - Fix em4x50 demodulation error (@tharexde) - Fix `hf mfdes` authentification issues, DES working (@bkerler) - Add Android cross-compilation to client cmake (@dxl, @doegox) @@ -90,7 +109,7 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Change appveyor verbose (@doegox) - Change `lf nexwatch demod` - now detects type, and show parity /chksum (@iceman1001) - Change `lfsampling` - interruptible only when logging not yet triggered (@doegox) - - Change `lf keri demod - more leanient when it comes to bits (@iceman1001) + - Change `lf keri demod` - more leanient when it comes to bits (@iceman1001) - fix, proper filtering of RL markers (@doegox) - Change, clean deps [compiler trials] (@doegox) - Change, remove c99 restrictions [compiler trials] (@doegox) diff --git a/Makefile b/Makefile index f7a434d3b..89e5b4c98 100644 --- a/Makefile +++ b/Makefile @@ -173,6 +173,9 @@ help: @echo "+ .../check - Run offline tests against specific target. See above." @echo "+ miscchecks - Detect various encoding issues in source code" @echo + @echo "+ udev - Sets udev rules on *nix" + @echo "+ accessrights - Ensure user belongs to correct group on *nix" + @echo @echo "Possible platforms: try \"make PLATFORM=\" for more info, default is PM3RDV4" @echo "To activate verbose mode, use make V=1" @@ -248,8 +251,8 @@ print-%: ; @echo $* = $($*) style: # Make sure astyle is installed @which astyle >/dev/null || ( echo "Please install 'astyle' package first" ; exit 1 ) - # Remove spaces & tabs at EOL, add LF at EOF if needed on *.c, *.h, *.cpp. *.lua, *.py, *.pl, Makefile - find . \( -not -path "./cov-int/*" -and \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "Makefile" -or -name "*.v" \) \) \ + # Remove spaces & tabs at EOL, add LF at EOF if needed on *.c, *.h, *.cpp. *.lua, *.py, *.pl, Makefile, *.v + find . \( -not -path "./cov-int/*" -and -not -path "./fpga/xst/*" -and \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "Makefile" -or -name "*.v" \) \) \ -exec perl -pi -e 's/[ \t]+$$//' {} \; \ -exec sh -c "tail -c1 {} | xxd -p | tail -1 | grep -q -v 0a$$" \; \ -exec sh -c "echo >> {}" \; @@ -259,23 +262,32 @@ style: --keep-one-line-blocks --max-instatement-indent=60 \ --style=google --pad-oper --unpad-paren --pad-header \ --align-pointer=name {} \; + # Update commands.md + [ -x client/proxmark3 ] && client/proxmark3 -m > doc/commands.md # Detecting weird codepages and tabs. +ifeq ($(platform),Darwin) +miscchecks: TABSCMD=egrep -l '\t' {} +else +miscchecks: TABSCMD=grep -lP '\t' {} +endif +ifneq (,$(EDIT)) +miscchecks: TABSCMD+= && vi {} -c ':set tabstop=4' -c ':set et|retab' -c ':wq' +endif miscchecks: - # Make sure recode is installed +# Make sure recode is installed @which recode >/dev/null || ( echo "Please install 'recode' package first" ; exit 1 ) @echo "Files with suspicious chars:" - @find . \( -name "*.[ch]" -or -name "*.cpp" -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "Makefile" -or -name "*.v" \) \ + @find . \( -not -path "./cov-int/*" -and -not -path "./client/deps/*" -and \( -name "*.[ch]" -or -name "*.cpp" -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "Makefile" -or -name "*.v" \) \) \ -exec sh -c "cat {} |recode utf8.. >/dev/null || echo {}" \; - @echo "Files with tabs:" -# to remove tabs within lines, one can try with: vi $file -c ':set tabstop=4' -c ':set et|retab' -c ':wq' -ifeq ($(platform),Darwin) - @find . \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "*.md" -or -name "*.txt" -or -name "*.awk" -or -name "*.v" \) \ - -exec egrep -l '\t' {} \; +ifneq (,$(EDIT)) + @echo "Files with tabs: (EDIT enabled, files will be rewritten!)" else - @find . \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "*.md" -or -name "*.txt" -or -name "*.awk" -or -name "*.v" \) \ - -exec grep -lP '\t' {} \; + @echo "Files with tabs: (rerun with EDIT=1 if you want to convert them with vim)" endif +# to remove tabs within lines, one can try with: vi $file -c ':set tabstop=4' -c ':set et|retab' -c ':wq' + @find . \( -not -path "./cov-int/*" -and -not -path "./client/deps/*" -and \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "*.md" -or -name "*.txt" -or -name "*.awk" -or -name "*.v" \) \) \ + -exec sh -c "$(TABSCMD)" \; # @echo "Files with printf \\\\t:" # @find . \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "*.md" -or -name "*.txt" -or -name "*.awk" -or -name "*.v" \) \ # -exec grep -lP '\\t' {} \; diff --git a/Makefile.defs b/Makefile.defs index fb8a7a45b..06eb2e36f 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -48,7 +48,15 @@ else RANLIB= ranlib endif +DEFCXXFLAGS = -Wall -Werror -O3 -pipe DEFCFLAGS = -Wall -Werror -O3 -fstrict-aliasing -pipe +DEFLDFLAGS = +# Next ones are activated only if SANITIZE=1 +ifeq ($(SANITIZE),1) + DEFCFLAGS += -g -fsanitize=address -fno-omit-frame-pointer + DEFCXXFLAGS += -g -fsanitize=address -fno-omit-frame-pointer + DEFLDFLAGS += -g -fsanitize=address +endif # Some more warnings we want as errors: DEFCFLAGS += -Wbad-function-cast -Wredundant-decls -Wmissing-prototypes -Wchar-subscripts -Wshadow -Wundef -Wwrite-strings -Wunused -Wuninitialized -Wpointer-arith -Winline -Wformat -Wformat-security -Winit-self -Wmissing-include-dirs -Wnested-externs -Wmissing-declarations -Wempty-body -Wignored-qualifiers -Wmissing-field-initializers -Wtype-limits -Wold-style-definition # Some more warnings we need first to eliminate, so temporarely tolerated: diff --git a/Makefile.host b/Makefile.host index 07b8c2199..694ab5a1c 100644 --- a/Makefile.host +++ b/Makefile.host @@ -17,6 +17,9 @@ endif CFLAGS ?= $(DEFCFLAGS) CFLAGS += $(MYDEFS) $(MYCFLAGS) $(MYINCLUDES) +CXXFLAGS ?= $(DEFCXXFLAGS) +CXXFLAGS += $(MYDEFS) $(MYCXXFLAGS) $(MYINCLUDES) +LDFLAGS ?= $(DEFLDFLAGS) LDFLAGS += $(MYLDFLAGS) LDLIBS += $(MYLDLIBS) @@ -31,6 +34,7 @@ BINDIR := . OBJDIR := obj MYOBJS ?= $(MYSRCS:%.c=$(OBJDIR)/%.o) +MYCXXOBJS ?= $(MYCXXSRCS:%.cpp=$(OBJDIR)/%.o) CLEAN = $(foreach bin,$(MYLIBS) $(BINS) $(LIB_A),$(BINDIR)/$(bin)) all: $(foreach bin,$(MYLIBS) $(BINS) $(LIB_A),$(BINDIR)/$(bin)) @@ -61,24 +65,31 @@ endif .PHONY: all clean install uninstall -$(BINDIR)/$(LIB_A): $(MYOBJS) +$(BINDIR)/$(LIB_A): $(MYOBJS) $(MYCXXOBJS) $(info [=] AR $(notdir $@)) - $(Q)$(AR) $@ $(MYOBJS) + $(Q)$(AR) $@ $(MYOBJS) $(MYCXXOBJS) $(Q)$(RANLIB) $@ -$(BINDIR)/% : $(OBJDIR)/%.o $(MYOBJS) $(MYLIBS) +$(BINDIR)/% : $(OBJDIR)/%.o $(MYOBJS) $(MYCXXOBJS) $(MYLIBS) $(info [=] LD $(notdir $@)) - $(Q)$(LD) $(LDFLAGS) $(MYOBJS) $< -o $@ $(MYLIBS) $(MYLDLIBS) + $(Q)$(LD) $(LDFLAGS) $(MYOBJS) $(MYCXXOBJS) $< -o $@ $(MYLIBS) $(MYLDLIBS) -$(OBJDIR)/%.o : %.c | $(OBJDIR) +%.o: %.c +$(OBJDIR)/%.o : %.c $(OBJDIR)/%.d | $(OBJDIR) $(info [-] CC $<) $(Q)$(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $< $(Q)$(POSTCOMPILE) +%.o: %.cpp +$(OBJDIR)/%.o : %.cpp $(OBJDIR)/%.d | $(OBJDIR) + $(info [-] CXX $<) + $(Q)$(CXX) $(DEPFLAGS) $(CXXFLAGS) -c -o $@ $< + $(Q)$(POSTCOMPILE) + $(OBJDIR): $(Q)$(MKDIR) $(OBJDIR) -DEPENDENCY_FILES = $(MYOBJS:%.o=%.d) $(BINS:%=$(OBJDIR)/%.d) +DEPENDENCY_FILES = $(MYOBJS:%.o=%.d) $(MYCXXOBJS:%.o=%.d) $(BINS:%=$(OBJDIR)/%.d) $(DEPENDENCY_FILES): ; .PRECIOUS: $(DEPENDENCY_FILES) diff --git a/README.md b/README.md index e77062fff..330faaef6 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ -| Releases | Linux & OSX CI | Windows CI | Coverity | -| ------------------- |:-------------------:| -------------------:| -------------------:| -| [![Latest release](https://img.shields.io/github/v/release/rfidresearchgroup/proxmark3)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Build status](https://api.travis-ci.org/RfidResearchGroup/proxmark3.svg?branch=master)](https://travis-ci.org/RfidResearchGroup/proxmark3) | [![Build status](https://ci.appveyor.com/api/projects/status/b4gwrhq3nc876cuu/branch/master?svg=true)](https://ci.appveyor.com/project/RfidResearchGroup/proxmark3/branch/master) | [![Coverity Status](https://scan.coverity.com/projects/19334/badge.svg)](https://scan.coverity.com/projects/proxmark3-rrg-iceman-repo)| +| Releases | Linux & OSX CI | Windows CI | Coverity | Contributors | +| ------------------- |:-------------------:| -------------------:| -------------------:| -------------------:| +| [![Latest release](https://img.shields.io/github/v/release/rfidresearchgroup/proxmark3)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Build status](https://api.travis-ci.com/RfidResearchGroup/proxmark3.svg?branch=master)](https://travis-ci.com/RfidResearchGroup/proxmark3) | [![Build status](https://ci.appveyor.com/api/projects/status/b4gwrhq3nc876cuu/branch/master?svg=true)](https://ci.appveyor.com/project/RfidResearchGroup/proxmark3/branch/master) | [![Coverity Status](https://scan.coverity.com/projects/19334/badge.svg)](https://scan.coverity.com/projects/proxmark3-rrg-iceman-repo)| ![GitHub contributors](https://img.shields.io/github/contributors/rfidresearchgroup/proxmark3) | @@ -30,11 +30,11 @@ | ------------------- |:-------------------:| -------------------:| |[Notes on UART](/doc/uart_notes.md)|[Notes on Termux / Android](/doc/termux_notes.md)|[Notes on paths](/doc/path_notes.md)| |[Notes on frame format](/doc/new_frame_format.md)|[Notes on tracelog / wireshark](/doc/trace_notes.md)|[Notes on EMV](/doc/emv_notes.md)| -|[Notes on external flash](/doc/ext_flash_notes.md)|[Notes on loclass](/doc/loclass_notes.md)|[Notes on Coverity Scan Config & Run](/doc/md/Development/Coverity-Scan-Config-%26-Run.md)| +|[Notes on external flash](/doc/ext_flash_notes.md)|[Notes on loclass](/doc/loclass_notes.md)|[Notes on Coverity Scan Config & Run](/doc/md/Development/Coverity-Scan-Config-and-Run.md)| |[Notes on file formats used with Proxmark3](/doc/extensions_notes.md)|[Notes on MFU binary format](/doc/mfu_binary_format_notes.md)|[Notes on FPGA & ARM](/doc/fpga_arm_notes.md)| -|[Developing standalone mode](/armsrc/Standalone/readme.md)|[Wiki about standalone mode](https://github.com/RfidResearchGroup/proxmark3/wiki/Standalone-mode)|| -|[Notes on Color usage](/doc/colors_notes.md)|[Makefile vs CMake](/doc/md/Development/Makefile-vs-CMake.md)| - +|[Developing standalone mode](/armsrc/Standalone/readme.md)|[Wiki about standalone mode](https://github.com/RfidResearchGroup/proxmark3/wiki/Standalone-mode)|[Notes on Magic 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)||| ## Build for non-RDV4 Proxmark3 platforms @@ -57,14 +57,14 @@ On the software side: quite a lot, see the [Changelog file](CHANGELOG.md). This repo compiles nicely on - Proxspace v3.x - - [latest release v3.4](https://github.com/Gator96100/ProxSpace/releases) + - [latest release v3.7](https://github.com/Gator96100/ProxSpace/releases) - Windows/mingw environment with Qt5.6.1 & GCC 4.9 - - Ubuntu 1604 -> 2004 + - Ubuntu 16.04 -> 20.04 - ParrotOS, Gentoo, Pentoo, Kali, Nethunter, Archlinux, Fedora, Debian - Rasbian - Android / Termux - Mac OS X / Homebrew - - WSL, WSL2 (Windows subsystem linux) on Windows 10 + - WSL1 (Windows subsystem linux) on Windows 10 - Docker container - [ RRG / 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/) @@ -73,7 +73,7 @@ Hardware to run client on - PC - Android - Raspberry Pi & Raspberry Pi Zero - - Jetson Nano + - Nvidia Jetson Nano ## Precompiled binaries We don't maintain any precompiled binaries in this repo. There is community effort over at the Proxmark3 forum where @gator96100 has set up a google drive with many mingw binaries which is up-to-date. We link to these files here as to make it easier for users. @@ -81,14 +81,15 @@ If you are having troubles with these files, contact the package maintainer @gat Ref: - +For Proxmark3 RDV4 - [Precompiled builds for RDV40 dedicated x86](https://drive.google.com/open?id=13zUs-aiQkYaSl5KWrBtuW5IWCoHJPsue) - [Precompiled builds for RDV40 dedicated x64](https://drive.google.com/open?id=1SyPB8t5Vo8O0Lh7PjNm3Kv-mO4BNbxjX) +For Proxmark3 RDV4 with blueshark addon - [Precompiled builds for RDV40 dedicated with Bluetooth addon x86](https://drive.google.com/open?id=1TqWYctkRvkLshQ1ZRBHPLDzYHR-asuMO) - [Precompiled builds for RDV40 dedicated with Bluetooth addon x64](https://drive.google.com/open?id=17ful7u2QyYmMQzQzc5fAf8nJvyoDJfSL) -Generice Proxmark3 devices (non RDV4) +Generice Proxmark3 devices (non RDV4), for Proxmark3 Easy, RDV1, RDV2, RDV3, etc etc - [Precompiled builds for RRG / Iceman repository x86](https://drive.google.com/open?id=1PI3Xr1mussPBPnYGu4ZjWzGPARK4N7JR) - [Precompiled builds for RRG / Iceman repository x64](https://drive.google.com/open?id=1uX9RtYGinuFrpHybu4xq_BE3HrobI20e) @@ -101,16 +102,18 @@ We usually merge your contributions fast since we do like the idea of getting a ## Issues & Troubleshooting -Please search the [issues](https://github.com/rfidresearchgroup/proxmark3/issues) page here and see if your issue is listed in the first instance. Next place to visit is the [Proxmark Forum](http://www.proxmark.org/forum/index.php). Learn to search it well and finally Google / duckduckgo is your friend :) You will find many blogposts, youtube videos, tweets, reddit - +Please search the [issues](https://github.com/rfidresearchgroup/proxmark3/issues) page here and see if your issue is listed in the first instance. Read the [Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md) guide to weed out most known problems. -Offical channels +Next place to visit is the [Proxmark Forum](http://www.proxmark.org/forum/index.php). Learn to search it well and finally Google / duckduckgo is your friend :) You will find many blogposts, youtube videos, tweets, reddit + +### Offical channels - [Proxmark3 IRC channel](http://webchat.freenode.net/?channels=#proxmark3) - [Proxmark3 sub reddit](https://www.reddit.com/r/proxmark3/) - [Twitter](https://twitter.com/proxmark3/) - - _no discord or slack channel_ + - [Proxmark3 community discord server](https://discord.gg/zjxc8ZB) + + _no slack channel_ Iceman has quite a few videos on his [youtube channel](https://www.youtube.com/c/ChrisHerrmann1001) @@ -130,7 +133,7 @@ The separation from official Proxmark3 repo gives us a lot of freedom to create ## Proxmark3 GUI The official PM3-GUI from Gaucho will not work. -The new universal GUI will work. [Proxmark3 Universal GUI](https://github.com/burma69/PM3UniversalGUI) Almost, change needed in order to show helptext when client isn't connected to a device. +The new [Proxmark3 Universal GUI](https://github.com/burma69/PM3UniversalGUI) will work more or less. Change is needed in order to show helptext when client isn't connected to a device. We don't know how active the maintainers. ## The end diff --git a/appveyor.yml b/appveyor.yml index 240724d80..44e6b6040 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,18 @@ version: 3.0.1.{build} image: Visual Studio 2019 -clone_folder: C:\ProxSpace\pm3 +clone_folder: C:\ProxSpace\pm3\proxmark +cache: + - C:\ps-cache -> appveyor.yml +environment: + proxspace_url: https://github.com/Gator96100/ProxSpace/archive/master.zip + proxspace_zip_file: \proxspace.zip + proxspace_zip_folder_name: ProxSpace-* + proxspace_path: C:\ProxSpace + proxspace_home_path: \ProxSpace\pm3 + proxspace_cache_path: C:\ps-cache + wsl_git_path: C:\proxmark + APPVEYOR_SAVE_CACHE_ON_ERROR: true + init: - ps: >- $psversiontable @@ -17,7 +29,6 @@ init: $releasename+=$env:APPVEYOR_BUILD_VERSION + " [" + $env:APPVEYOR_REPO_COMMIT_SHORT + "]" - Write-Host "repository: $env:appveyor_repo_name branch:$env:APPVEYOR_REPO_BRANCH release: $releasename" -ForegroundColor Yellow Add-AppveyorMessage -Message "[$env:APPVEYOR_REPO_COMMIT_SHORT]$env:appveyor_repo_name($env:APPVEYOR_REPO_BRANCH)" -Category Information -Details "repository: $env:appveyor_repo_name branch: $env:APPVEYOR_REPO_BRANCH release: $releasename" @@ -25,240 +36,301 @@ init: # iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) clone_script: - ps: >- - Write-Host "Removing ProxSpace..." -NoNewLine - $CloneTime=[System.Environment]::TickCount - + Function GitClone($Text, $Folder) { + Write-Host "$Text" -NoNewLine + if(-not $env:appveyor_pull_request_number) { + git clone -q --branch=$env:appveyor_repo_branch https://github.com/$env:appveyor_repo_name.git $Folder + cd $Folder + git checkout -qf $env:appveyor_repo_commit + } else { + git clone -q https://github.com/$env:appveyor_repo_name.git $Folder + cd $Folder + git fetch -q origin +refs/pull/$env:appveyor_pull_request_number/merge: + git checkout -qf FETCH_HEAD + } + Write-Host "[ OK ]" -ForegroundColor Green + } + + $WSLjob = Start-Job -Name WSLInstall -ScriptBlock { + Function WSLExec($Text, $Cmd) { + Write-Host "$Text" + wsl -- bash -c $Cmd + Write-Host "$Text" -NoNewLine + Write-Host "[ OK ]" -ForegroundColor Green + } + + $WSLInstallTime=[System.Environment]::TickCount + WSLExec "WSL update..." "sudo apt-get update 1>/dev/null" + WSLExec "WSL upgrade..." "sudo apt-get upgrade -y 1>/dev/null" + WSLExec "WSL cleanup..." "sudo apt-get auto-remove -y 1>/dev/null" + WSLExec "WSL install..." "sudo apt-get -y install --reinstall --no-install-recommends git ca-certificates build-essential pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev qtbase5-dev cmake 1>/dev/null" + WSLExec "WSL QT fix..." "sudo strip --remove-section=.note.ABI-tag /usr/lib/x86_64-linux-gnu/libQt5Core.so.5" + Add-AppveyorMessage -Message "WSL setup took $(([System.Environment]::TickCount-$WSLInstallTime) / 1000) sec" -Category Information + New-Item -ItemType "file" -Path "C:\WSL-Finished.txt" -Force | Out-Null + } + + $env:PSInstallTime=[System.Environment]::TickCount + + Write-Host "ProxSpace: Removing folder..." -NoNewLine + cd \ - - Remove-Item -Recurse -Force -Path c:\ProxSpace\* - - Write-Host "[ OK ]" -ForegroundColor Green - - - Write-Host "Git clone ProxSpace..." -NoNewLine - - git clone -q https://github.com/Gator96100/ProxSpace c:\ProxSpace - - Write-Host "[ OK ]" -ForegroundColor Green - - - if(!(Test-Path -Path C:\ProxSpace\pm3)){ - - New-Item -ItemType Directory -Force -Path C:\ProxSpace\pm3 - - } - Write-Host "Removing pm3 dir..." -NoNewLine - - Remove-Item -Recurse -Force -Path c:\ProxSpace\pm3\* - - Write-Host "[ OK ]" -ForegroundColor Green - - - Write-Host "Cloning repository <$env:appveyor_repo_name> to $env:appveyor_build_folder ..." -NoNewLine - - if(-not $env:appveyor_pull_request_number) { - git clone -q --branch=$env:appveyor_repo_branch https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder - cd $env:appveyor_build_folder - git checkout -qf $env:appveyor_repo_commit - } else { - git clone -q https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder - cd $env:appveyor_build_folder - git fetch -q origin +refs/pull/$env:appveyor_pull_request_number/merge: - git checkout -qf FETCH_HEAD - } - - Write-Host "[ OK ]" -ForegroundColor Green - - - Write-Host "Fill msys2\etc\fstab file..." -NoNewLine - - New-Item c:\ProxSpace\msys2\etc\fstab -type file -force -value "# For a description of the file format, see the Users Guide`n# http://cygwin.com/cygwin-ug-net/using.html#mount-table`nnone / cygdrive binary,posix=0,noacl,user 0 0 `nC:\ProxSpace\pm3 /pm3 ntfs noacl 0 0 `nC:\ProxSpace\gcc-arm-none-eabi /gcc-arm-none-eabi ntfs noacl 0 0 `n" - - Write-Host "[ OK ]" -ForegroundColor Green - - - Write-Host "Update msys2 packages..." - - $env:Path = "C:\ProxSpace\msys2\usr\bin;C:\ProxSpace\msys2\mingw32\bin;C:\ProxSpace\gcc-arm-none-eabi\bin;$env:Path" - - Function ExecUpdate($Name, $Cmd, $ErrorLine) { - - Write-Host "Exec [$Name]... " -NoNewLine - #--- begin Job - - $Job = Start-Job -Name "$Name" -ScriptBlock { - $env:Path = "C:\ProxSpace\msys\bin;$env:Path" - Set-Location $using:PWD - - $sb=[scriptblock]::Create("$using:Cmd") - #execute scriptblock - $Cond=&$sb - - return $Cond - } - - #--- end Job - - $JobTime=[System.Environment]::TickCount - while($true) { - Try { - $Res = Receive-Job -Job $Job -Keep 2>&1 6>&1 - } - Catch { - $Res = "" - Write-host "error in Receive-Job" - } - - if ($Res -is "String" -and $Res -like "*$ErrorLine*"){ - Write-host "Exit by stop phrase" -ForegroundColor Green - break - } - - if ($Res -is [Object]){ - [bool]$needexit = $false - ForEach($line in $Res){ - if ($line -like "*$ErrorLine*"){ - Write-host "Exit by stop phrase [obj]" -ForegroundColor Green - $needexit = $true - break - } - } - if ($needexit) { - break - } - } - - if(Wait-Job $Job -Timeout 5){ - Write-host "Exit by end job" -ForegroundColor Green - break - } - - if ([System.Environment]::TickCount-$JobTime -gt 1000000) { - Write-host "Exit by timeout" -ForegroundColor Yellow - break - } - } - - Remove-Job -Force $Job - } - - cd C:\ProxSpace\ - - C:\ProxSpace\msys2\ps\setup.cmd - - ExecUpdate "update1" "C:\ProxSpace\msys2\msys2_shell.cmd -mingw32 -defterm -no-start /dev/null" "terminate?MSYS2" - - ExecUpdate "update2" "C:\ProxSpace\msys2\msys2_shell.cmd -mingw32 -defterm -no-start /dev/null" "terminate?MSYS2" - - Add-AppveyorMessage -Message "ProxSpace download and update took $(([System.Environment]::TickCount-$CloneTime) / 1000) sec" -Category Information - - Write-Host "Update " -NoNewLine + Remove-Item -Recurse -Force -Path $env:proxspace_path -ErrorAction SilentlyContinue Write-Host "[ OK ]" -ForegroundColor Green + + Write-Host "ProxSpace: downloading..." -NoNewLine + + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + + Invoke-WebRequest "$env:proxspace_url" -outfile "$env:proxspace_zip_file" + + Write-Host "[ OK ]" -ForegroundColor Green + + Write-Host "ProxSpace: extracting..." -NoNewLine + + Expand-Archive -LiteralPath "$env:proxspace_zip_file" -DestinationPath "\" + + Remove-Item "$env:proxspace_zip_file" + + Write-Host "[ OK ]" -ForegroundColor Green + + Write-Host "ProxSpace: renaming folder..." -NoNewLine + + Get-ChildItem -Path "\$env:proxspace_zip_folder_name" | Rename-Item -NewName (Split-Path $env:proxspace_path -Leaf) + + Write-Host "[ OK ]" -ForegroundColor Gree + + $psversion = (Select-String -Pattern 'PSVERSION=' -SimpleMatch -Path "$env:proxspace_path\msys2\ps\09-proxspace_setup.post").Line.Split("""")[1] + + Write-Host "ProxSpace version: $psversion" -ForegroundColor Yellow + + GitClone "ProxSpace: Cloning repository <$env:appveyor_repo_name> to $env:appveyor_build_folder ..." $env:appveyor_build_folder + + GitClone "WSL: Cloning repository <$env:appveyor_repo_name> to $env:wsl_git_path ..." $env:wsl_git_path + + install: +- ps: >- + + Function ExecUpdate($Text, $firstStart) { + Write-Host "$Text" + + $PSjob = Start-Job -Name PSInstall -ScriptBlock { + cd $env:proxspace_path + ./runme64.bat -c "exit" + } + + $StartTime=[System.Environment]::TickCount + Start-Sleep -s 10 + while($true) { + if ($PSjob.State -eq 'Completed') { + Write-Host "$Text" -NoNewLine + Write-Host "[ OK ]" -ForegroundColor Green + break + } + + if ($PSjob.State -eq 'Failed') { + Write-Host "$Text" -NoNewLine + Write-Host "[ Failed ]" -ForegroundColor Red + break + } + + if ($firstStart -And (Test-Path "$env:proxspace_path\msys2\etc\pacman.conf.pacnew")) { + Start-Sleep -s 5 + Stop-Job -Job $PSjob + Start-Sleep -s 5 + Write-Host "$Text" -NoNewLine + Write-Host "Exit by pacman.conf" -ForegroundColor Green + break + } + + if ([System.Environment]::TickCount-$StartTime -gt 1000000) { + Stop-Job -Job $PSjob + Write-Host "$Text" -NoNewLine + Write-host "Exit by timeout" -ForegroundColor Yellow + break + } + + Start-Sleep -s 5 + Receive-Job -Name WSLInstall -ErrorAction SilentlyContinue + } + #Receive-Job -Wait -Name PSInstall + } + + Function GitClone($Text, $Folder) { + Write-Host "$Text" -NoNewLine + if(-not $env:appveyor_pull_request_number) { + git clone -q --branch=$env:appveyor_repo_branch https://github.com/$env:appveyor_repo_name.git $Folder + cd $Folder + git checkout -qf $env:appveyor_repo_commit + } else { + git clone -q https://github.com/$env:appveyor_repo_name.git $Folder + cd $Folder + git fetch -q origin +refs/pull/$env:appveyor_pull_request_number/merge: + git checkout -qf FETCH_HEAD + } + Write-Host "[ OK ]" -ForegroundColor Green + } + + Write-Host "ProxSpace: move cache..." -NoNewLine + + New-Item -ItemType Directory -Force -Path "$env:proxspace_path\msys2\var\cache\" | Out-Null + + Copy-Item -Path "$env:proxspace_cache_path\*" -Destination "$env:proxspace_path\msys2\var\cache\" -Force -Recurse -ErrorAction SilentlyContinue + + Write-Host "[ OK ]" -ForegroundColor Gree + + ExecUpdate "ProxSpace: initial msys2 startup..." $true + + ExecUpdate "ProxSpace: installing required packages..." $false + + Add-AppveyorMessage -Message "ProxSpace download and update took $(([System.Environment]::TickCount-$env:PSInstallTime) / 1000) sec" -Category Information + build_script: - ps: >- - $env:Path="C:\ProxSpace\msys2\usr\bin;C:\ProxSpace\msys2\mingw32\bin;C:\ProxSpace\gcc-arm-none-eabi\bin;c:\Python38;c:\Python38\Scripts;$env:Path" - $env:MINGW_HOME="C:\ProxSpace\msys2\mingw32" + $pmfolder = Split-Path $env:appveyor_build_folder -Leaf + + Function ExecMinGWCmd($Cmd) { + cd $env:proxspace_path + ./runme64.bat -c "cd $pmfolder && $Cmd" + } + + Function ExecCheck($Name) { + $testspass = ($LASTEXITCODE -eq 0) + + $global:TestsPassed=$testspass - $env:MSYS_HOME="C:\ProxSpace\msys2" + if ($testspass) { + Add-AppveyorTest -Name $Name -Framework NUnit -Filename $Name -Outcome Passed -Duration "$([System.Environment]::TickCount-$TestTime)" + Write-Host "$Name [ OK ]" -ForegroundColor Green + } else { + Add-AppveyorTest -Name $Name -Framework NUnit -Filename $Name -Outcome Failed -Duration "$([System.Environment]::TickCount-$TestTime)" + Write-Host "$Name [ ERROR ]" -ForegroundColor Red + throw "Tests error." + } + } + + $WSLjob = Start-Job -Name WSLCompile -ScriptBlock { + Function ExecWSLCmd($Cmd) { + cd $env:wsl_git_path + wsl -- bash -c $Cmd + } + + Function ExecCheck($Name) { + $testspass = ($LASTEXITCODE -eq 0) + + $global:TestsPassed=$testspass + + if ($testspass) { + Add-AppveyorTest -Name $Name -Framework NUnit -Filename $Name -Outcome Passed -Duration "$([System.Environment]::TickCount-$TestTime)" + Write-Host "$Name [ OK ]" -ForegroundColor Green + } else { + Add-AppveyorTest -Name $Name -Framework NUnit -Filename $Name -Outcome Failed -Duration "$([System.Environment]::TickCount-$TestTime)" + Write-Host "$Name [ ERROR ]" -ForegroundColor Red + throw "Tests error." + } + } + + #WSL: wait for installation to finish + if(!(Test-Path "C:\WSL-Finished.txt")){ + Write-Host "Waiting for WSL installation to finish..." -NoNewLine + while(!(Test-Path "C:\WSL-Finished.txt")) { + Start-Sleep -s 5 + } + Remove-Item -Force "C:\WSL-Finished.txt" -ErrorAction SilentlyContinue + Write-Host "$Name [ OK ]" -ForegroundColor Green + } + + #Windows Subsystem for Linux (WSL) + Write-Host "---------- WSL make ----------" -ForegroundColor Yellow + $TestTime=[System.Environment]::TickCount + ExecWSLCmd "make clean;make V=1" + #some checks + if(!(Test-Path "$env:wsl_git_path\client\proxmark3")){ + throw "Main file proxmark3 not exists." + } + + ExecWSLCmd "make check" + ExecCheck "WSL make Tests" + Start-Sleep -s 2 + Write-Host "---------- WSL btaddon ----------" -ForegroundColor Yellow + $TestTime=[System.Environment]::TickCount + ExecWSLCmd "make clean;make V=1 PLATFORM_EXTRAS=BTADDON" + ExecWSLCmd "make check" + ExecCheck "WSL BTaddon Tests" + Start-Sleep -s 2 + Write-Host "---------- WSL make clean ----------" -ForegroundColor Yellow + ExecWSLCmd 'make clean' + Write-Host "---------- WSL cmake ----------" -ForegroundColor Yellow + $TestTime=[System.Environment]::TickCount + ExecWSLCmd 'mkdir -p client/build; cd client/build; cmake ..; make VERBOSE=1;' + Write-Host "---------- WSL cmake tests ----------" -ForegroundColor Yellow + ExecWSLCmd './tools/pm3_tests.sh --clientbin client/build/proxmark3 client' + ExecCheck "WSL cmake Tests" + } + + #ProxSpace + + Write-Host "ProxSpace: create new cache..." -NoNewLine + + cd $env:proxspace_path + + ./runme64.bat -c "yes | pacman -Sc > /dev/null 2>&1" + + Remove-Item -Recurse -Force -Path "$env:proxspace_cache_path" -ErrorAction SilentlyContinue + + Move-Item -Path "$env:proxspace_path\msys2\var\cache" -Destination "$env:proxspace_cache_path" -Force + + Write-Host "[ OK ]" -ForegroundColor Gree + + Write-Host "---------- PS make ----------" -ForegroundColor Yellow + + $TestTime=[System.Environment]::TickCount + + ExecMinGWCmd "make clean;make V=1" - $env:MSYSTEM="MINGW32" + if(!(Test-Path "$env:proxspace_home_path\$pmfolder\client\proxmark3.exe")){ - $env:MINGW_PREFIX="/mingw32" + throw "Main file proxmark3.exe not exists." - $env:SHELL="/bin/bash" + } - $env:MSYSTEM_CHOST="i686-w64-mingw32" + ExecMinGWCmd 'make check' - cd C:\ProxSpace\pm3 - - Write-Host "---------- make ----------" -ForegroundColor Yellow + ExecCheck "PS make Tests" + + Write-Host "---------- PS btaddon ----------" -ForegroundColor Yellow $TestTime=[System.Environment]::TickCount - #make + ExecMinGWCmd 'make clean;make V=1 PLATFORM_EXTRAS=BTADDON' - bash -c -i 'echo $PATH;pwd;make clean;make V=1' - - - #some checks - - if(!(Test-Path C:\ProxSpace\pm3\client\proxmark3.exe)){ - - throw "Main file proxmark3.exe not exists." - - } - - cd c:\ProxSpace\pm3 - - bash -c -i 'make check' - - $testspass = ($LASTEXITCODE -eq 0) + ExecMinGWCmd 'make check' - $global:TestsPassed=$testspass - - if ($testspass) { - Add-AppveyorTest -Name "make Tests" -Framework NUnit -Filename "make check" -Outcome Passed -Duration "$([System.Environment]::TickCount-$TestTime)" - Write-Host "make Tests [ OK ]" -ForegroundColor Green - } else { - Add-AppveyorTest -Name "make Tests" -Framework NUnit -Filename "make check" -Outcome Failed -Duration "$([System.Environment]::TickCount-$TestTime)" - Write-Host "make Tests [ ERROR ]" -ForegroundColor Red - throw "Tests error." - } + ExecCheck "PS BTaddon Tests" - Write-Host "---------- btaddon ----------" -ForegroundColor Yellow + Write-Host "---------- PS make clean ----------" -ForegroundColor Yellow + + ExecMinGWCmd 'make clean' + + Write-Host "---------- PS cmake ----------" -ForegroundColor Yellow $TestTime=[System.Environment]::TickCount - - bash -c -i 'pwd;make clean;make PLATFORM_EXTRAS=BTADDON' - - cd c:\ProxSpace\pm3 - - bash -c -i 'make check' - - $testspass = ($LASTEXITCODE -eq 0) - - $global:TestsPassed=(($global:TestsPassed) -and ($testspass)) - if ($testspass) { - Add-AppveyorTest -Name "BTaddon Tests" -Framework NUnit -Filename "make check" -Outcome Passed -Duration "$([System.Environment]::TickCount-$TestTime)" - Write-Host "BTaddon Tests [ OK ]" -ForegroundColor Green - } else { - Add-AppveyorTest -Name "BTaddon Tests" -Framework NUnit -Filename "make check" -Outcome Failed -Duration "$([System.Environment]::TickCount-$TestTime)" - Write-Host "BTaddon Tests [ ERROR ]" -ForegroundColor Red - } + ExecMinGWCmd 'mkdir -p client/build; cd client/build; cmake -G""MSYS Makefiles"" ..; make VERBOSE=1;' - Write-Host "---------- make clean ----------" -ForegroundColor Yellow - - bash -c -i 'make clean' - - Write-Host "---------- cmake ----------" -ForegroundColor Yellow - - $TestTime=[System.Environment]::TickCount + Write-Host "---------- PS cmake tests ----------" -ForegroundColor Yellow - cmd.exe /c 'C:\ProxSpace\msys2\msys2_shell.cmd -mingw32 -defterm -no-start -c "mkdir -p client/build; cd client/build; cmake -G""MSYS Makefiles"" ..; make VERBOSE=1;"' + ExecMinGWCmd './tools/pm3_tests.sh --clientbin client/build/proxmark3.exe client' - Write-Host "---------- cmake tests ----------" -ForegroundColor Yellow - - cd c:\ProxSpace\pm3 - - bash -c -i './tools/pm3_tests.sh --clientbin client/build/proxmark3.exe client' + ExecCheck "PS cmake Tests" - $testspass = ($LASTEXITCODE -eq 0) - - $global:TestsPassed=(($global:TestsPassed) -and ($testspass)) + Receive-Job -Wait -Name WSLInstall -ErrorAction SilentlyContinue - if ($testspass) { - Add-AppveyorTest -Name "cmake Tests" -Framework NUnit -Filename "make client/check" -Outcome Passed -Duration "$([System.Environment]::TickCount-$TestTime)" - Write-Host "cmake Tests [ OK ]" -ForegroundColor Green - } else { - Add-AppveyorTest -Name "cmake Tests" -Framework NUnit -Filename "make client/check" -Outcome Failed -Duration "$([System.Environment]::TickCount-$TestTime)" - Write-Host "cmake Tests [ ERROR ]" -ForegroundColor Red - } + Receive-Job -Wait -Job $WSLjob test_script: - ps: >- @@ -273,4 +345,4 @@ on_success: on_failure: - ps: Write-Host "Build error." -ForegroundColor Red on_finish: -- ps: # $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) +- ps: # $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) \ No newline at end of file diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index f41801a26..c7c370047 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -22,29 +22,56 @@ extern uint8_t _stack_start, __bss_end__; static uint8_t *BigBuf = &__bss_end__; /* BigBuf memory layout: -Pointer to highest available memory: BigBuf_hi - high BigBuf_size - reserved = BigBuf_malloc() subtracts amount from BigBuf_hi, +Pointer to highest available memory: s_bigbuf_hi + high s_bigbuf_size + reserved = BigBuf_malloc() subtracts amount from s_bigbuf_hi, low 0x00 */ -static uint32_t BigBuf_size = 0; +static uint32_t s_bigbuf_size = 0; // High memory mark -static uint32_t BigBuf_hi = 0; +static uint32_t s_bigbuf_hi = 0; // pointer to the emulator memory. static uint8_t *emulator_memory = NULL; +//============================================================================= +// The ToSend buffer. +// A buffer where we can queue things up to be sent through the FPGA, for +// any purpose (fake tag, as reader, whatever). We go MSB first, since that +// is the order in which they go out on the wire. +//============================================================================= +static tosend_t toSend = { + .max = -1, + .bit = 8, + .buf = NULL +}; +//============================================================================= +// The dmaBuf 16bit buffer. +// A buffer where we recive IQ samples sent from the FPGA, for demodulating +//============================================================================= +static dmabuf16_t dma_16 = { + .size = DMA_BUFFER_SIZE, + .buf = NULL +}; +// dmaBuf 8bit buffer +static dmabuf8_t dma_8 = { + .size = DMA_BUFFER_SIZE, + .buf = NULL +}; + + + // trace related variables -static uint32_t traceLen = 0; +static uint32_t trace_len = 0; static bool tracing = true; // compute the available size for BigBuf void BigBuf_initialize(void) { - BigBuf_size = (uint32_t)&_stack_start - (uint32_t)&__bss_end__; - BigBuf_hi = BigBuf_size; - traceLen = 0; + s_bigbuf_size = (uint32_t)&_stack_start - (uint32_t)&__bss_end__; + s_bigbuf_hi = s_bigbuf_size; + trace_len = 0; } // get the address of BigBuf @@ -53,7 +80,7 @@ uint8_t *BigBuf_get_addr(void) { } uint32_t BigBuf_get_size(void) { - return BigBuf_size; + return s_bigbuf_size; } // get the address of the emulator memory. Allocate part of Bigbuf for it, if not yet done @@ -64,6 +91,11 @@ uint8_t *BigBuf_get_EM_addr(void) { return emulator_memory; } +/* +uint32_t BigBuf_get_EM_size(void) { + return CARD_MEMORY_SIZE; +} +*/ // clear ALL of BigBuf void BigBuf_Clear(void) { @@ -72,9 +104,10 @@ void BigBuf_Clear(void) { // clear ALL of BigBuf void BigBuf_Clear_ext(bool verbose) { - memset(BigBuf, 0, BigBuf_size); + memset(BigBuf, 0, s_bigbuf_size); + clear_trace(); if (verbose) - Dbprintf("Buffer cleared (%i bytes)", BigBuf_size); + Dbprintf("Buffer cleared (%i bytes)", s_bigbuf_size); } void BigBuf_Clear_EM(void) { @@ -82,57 +115,66 @@ void BigBuf_Clear_EM(void) { } void BigBuf_Clear_keep_EM(void) { - memset(BigBuf, 0, BigBuf_hi); + memset(BigBuf, 0, s_bigbuf_hi); } // 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 (BigBuf_hi < chunksize) + if (s_bigbuf_hi < chunksize) return NULL; // no memory left chunksize = (chunksize + 3) & 0xfffc; // round to next multiple of 4 - BigBuf_hi -= chunksize; // aligned to 4 Byte boundary - return (uint8_t *)BigBuf + BigBuf_hi; + s_bigbuf_hi -= chunksize; // aligned to 4 Byte boundary + return (uint8_t *)BigBuf + s_bigbuf_hi; } // free ALL allocated chunks. The whole BigBuf is available for traces or samples again. void BigBuf_free(void) { - BigBuf_hi = BigBuf_size; + s_bigbuf_hi = s_bigbuf_size; emulator_memory = NULL; // shouldn't this empty BigBuf also? + toSend.buf = NULL; + dma_16.buf = NULL; + dma_8.buf = NULL; } // free allocated chunks EXCEPT the emulator memory void BigBuf_free_keep_EM(void) { if (emulator_memory != NULL) - BigBuf_hi = emulator_memory - (uint8_t *)BigBuf; + s_bigbuf_hi = emulator_memory - (uint8_t *)BigBuf; else - BigBuf_hi = BigBuf_size; + s_bigbuf_hi = s_bigbuf_size; - // shouldn't this empty BigBuf also? + toSend.buf = NULL; + dma_16.buf = NULL; + dma_8.buf = NULL; } void BigBuf_print_status(void) { DbpString(_CYAN_("Memory")); - Dbprintf(" BigBuf_size.............%d", BigBuf_size); - Dbprintf(" Available memory........%d", BigBuf_hi); + Dbprintf(" BigBuf_size.............%d", s_bigbuf_size); + Dbprintf(" Available memory........%d", s_bigbuf_hi); DbpString(_CYAN_("Tracing")); Dbprintf(" tracing ................%d", tracing); - Dbprintf(" traceLen ...............%d", traceLen); + Dbprintf(" traceLen ...............%d", trace_len); + + Dbprintf(" dma8 memory.............%d", dma_8.buf - BigBuf_get_addr()); + Dbprintf(" dma16 memory............%d", (uint8_t *)dma_16.buf - BigBuf_get_addr()); + Dbprintf(" toSend memory...........%d", toSend.buf - BigBuf_get_addr()); } // return the maximum trace length (i.e. the unallocated size of BigBuf) uint16_t BigBuf_max_traceLen(void) { - return BigBuf_hi; + return s_bigbuf_hi; } void clear_trace(void) { - traceLen = 0; + trace_len = 0; } void set_tracelen(uint32_t value) { - traceLen = value; + trace_len = value; } void set_tracing(bool enable) { @@ -148,7 +190,7 @@ bool get_tracing(void) { * @return */ uint32_t BigBuf_get_traceLen(void) { - return traceLen; + return trace_len; } /** @@ -158,16 +200,18 @@ uint32_t BigBuf_get_traceLen(void) { 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 readerToTag) { - if (!tracing) return false; + if (tracing == false) { + return false; + } uint8_t *trace = BigBuf_get_addr(); - tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + traceLen); + tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + trace_len); uint32_t num_paritybytes = (iLen - 1) / 8 + 1; // number of valid paritybytes in *parity // Return when trace is full - if (TRACELOG_HDR_LEN + iLen + num_paritybytes >= BigBuf_max_traceLen() - traceLen) { - tracing = false; // don't trace any more + if (TRACELOG_HDR_LEN + iLen + num_paritybytes >= BigBuf_max_traceLen() - trace_len) { + tracing = false; return false; } @@ -178,39 +222,48 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ duration = (UINT32_MAX - timestamp_start) + timestamp_end; } - if (duration > 0x7FFF) { + if (duration > 0xFFFF) { + /* if (DBGLEVEL >= DBG_DEBUG) { - Dbprintf("Error in LogTrace: duration too long for 15 bits encoding: 0x%08x start:0x%08x end:0x%08x", duration, timestamp_start, timestamp_end); - Dbprintf("Forcing duration = 0"); + Dbprintf("Error in LogTrace: duration too long for 16 bits encoding: 0x%08x start: 0x%08x end: 0x%08x", duration, timestamp_start, timestamp_end); } + */ duration = 0; } hdr->timestamp = timestamp_start; - hdr->duration = duration; + hdr->duration = duration & 0xFFFF; hdr->data_len = iLen; hdr->isResponse = !readerToTag; - traceLen += TRACELOG_HDR_LEN; + trace_len += TRACELOG_HDR_LEN; // data bytes if (btBytes != NULL && iLen != 0) { - memcpy(trace + traceLen, btBytes, iLen); + memcpy(hdr->frame, btBytes, iLen); + trace_len += iLen; } - traceLen += iLen; // parity bytes if (num_paritybytes != 0) { if (parity != NULL) { - memcpy(trace + traceLen, parity, num_paritybytes); + memcpy(trace + trace_len, parity, num_paritybytes); } else { - memset(trace + traceLen, 0x00, num_paritybytes); + memset(trace + trace_len, 0x00, num_paritybytes); } + trace_len += num_paritybytes; } - traceLen += num_paritybytes; - return true; } +// 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) { + uint32_t duration = ts_end - ts_start; + duration /= 32; + ts_end = ts_start + duration; + return LogTrace(bytes, len, ts_start, ts_end, parity, reader2tag); +} + + // Emulator memory uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length) { uint8_t *mem = BigBuf_get_EM_addr(); @@ -221,3 +274,57 @@ uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length) { Dbprintf("Error, trying to set memory outside of bounds! %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) + toSend.buf = BigBuf_malloc(TOSEND_BUFFER_SIZE); + + return &toSend; +} + +void tosend_reset(void) { + toSend.max = -1; + toSend.bit = 8; +} + +void tosend_stuffbit(int b) { + + if (toSend.max >= TOSEND_BUFFER_SIZE - 1) { + Dbprintf(_RED_("toSend overflow")); + return; + } + + if (toSend.bit >= 8) { + toSend.max++; + toSend.buf[toSend.max] = 0; + toSend.bit = 0; + } + + if (b) + toSend.buf[toSend.max] |= (1 << (7 - toSend.bit)); + + toSend.bit++; + + if (toSend.max >= TOSEND_BUFFER_SIZE) { + toSend.bit = 0; + } +} + +dmabuf16_t *get_dma16(void) { + if (dma_16.buf == NULL) + dma_16.buf = (uint16_t *)BigBuf_malloc(DMA_BUFFER_SIZE * sizeof(uint16_t)); + + return &dma_16; +} + +dmabuf8_t *get_dma8(void) { + if (dma_8.buf == NULL) + dma_8.buf = BigBuf_malloc(DMA_BUFFER_SIZE); + + return &dma_8; +} + diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index 60857e82a..bf6600e1e 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -19,7 +19,10 @@ #define MAX_MIFARE_FRAME_SIZE 18 // biggest Mifare frame is answer to a read (one block = 16 Bytes) + 2 Bytes CRC #define MAX_MIFARE_PARITY_SIZE 3 // need 18 parity bits for the 18 Byte above. 3 Bytes are enough to store these #define CARD_MEMORY_SIZE 4096 -#define DMA_BUFFER_SIZE 256 //128 (how big is the dma?!? +#define DMA_BUFFER_SIZE 256 + +// 8 data bits and 1 parity bit per payload byte, 1 correction bit, 1 SOC bit, 2 EOC bits +#define TOSEND_BUFFER_SIZE (9 * MAX_FRAME_SIZE + 1 + 1 + 2) uint8_t *BigBuf_get_addr(void); uint32_t BigBuf_get_size(void); @@ -39,7 +42,34 @@ void clear_trace(void); 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 readerToTag); +bool LogTrace_ISO15693(const uint8_t *bytes, uint16_t len, uint32_t ts_start, uint32_t ts_end, uint8_t *parity, bool reader2tag); + + uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length); + +typedef struct { + int max; + int bit; + uint8_t *buf; +} tosend_t; + +tosend_t *get_tosend(void); +void tosend_reset(void); +void tosend_stuffbit(int b); + +typedef struct { + uint16_t size; + uint8_t *buf; +} dmabuf8_t; + +typedef struct { + uint16_t size; + uint16_t *buf; +} dmabuf16_t; + +dmabuf8_t *get_dma8(void); +dmabuf16_t *get_dma16(void); #endif /* __BIGBUF_H */ diff --git a/armsrc/Makefile b/armsrc/Makefile index 1bbbf2ba7..f3caf6e22 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -30,7 +30,7 @@ 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_cipher.c +SRC_ICLASS = iclass.c optimized_cipherutils.c optimized_ikeys.c optimized_elite.c optimized_cipher.c SRC_LEGIC = legicrf.c legicrfsim.c legic_prng.c SRC_NFCBARCODE = thinfilm.c @@ -79,7 +79,7 @@ endif include Standalone/Makefile.inc #the FPGA bitstream files. Note: order matters! -FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit +FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit fpga_felica.bit #the lz4 source files required for decompressing the fpga config at run time SRC_LZ4 = lz4.c diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index c6dc9952e..eef4ecf6f 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -38,12 +38,18 @@ define KNOWN_STANDALONE_DEFINITIONS | HF_14ASNIFF | 14a sniff to flashmem | | (RDV4 only) | | +----------------------------------------------------------+ +| HF_AVEFUL | Mifare ultralight read/simulation | +| | - Ave Ozkal | ++----------------------------------------------------------+ | HF_BOG | 14a sniff with ULC/ULEV1/NTAG auth | | (RDV4 only) | storing in flashmem - Bogito | +----------------------------------------------------------+ | HF_COLIN | Mifare ultra fast sniff/sim/clone | | (RDV4 only) | - Colin Brigato | +----------------------------------------------------------+ +| HF_ICECLASS | Simulate HID iCLASS legacy ags | +| (RDV4 only) | storing in flashmem | ++----------------------------------------------------------+ | HF_LEGIC | Read/simulate Legic Prime tags | | | storing in flashmem | +----------------------------------------------------------+ @@ -59,9 +65,9 @@ define KNOWN_STANDALONE_DEFINITIONS endef STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN -STANDALONE_MODES += HF_14ASNIFF HF_BOG HF_COLIN HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_YOUNG +STANDALONE_MODES += HF_14ASNIFF HF_AVEFUL HF_BOG HF_COLIN HF_ICECLASS HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_YOUNG STANDALONE_MODES_REQ_SMARTCARD := -STANDALONE_MODES_REQ_FLASH := LF_ICEHID HF_14ASNIFF HF_BOG HF_COLIN +STANDALONE_MODES_REQ_FLASH := LF_ICEHID HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE) ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),) diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index a06cfe75e..c5730965b 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -37,6 +37,10 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_14ASNIFF,$(APP_CFLAGS))) SRC_STANDALONE = hf_14asniff.c endif +# WITH_STANDALONE_HF_AVEFUL +ifneq (,$(findstring WITH_STANDALONE_HF_AVEFUL,$(APP_CFLAGS))) + SRC_STANDALONE = hf_aveful.c +endif # WITH_STANDALONE_LF_ICEHID ifneq (,$(findstring WITH_STANDALONE_LF_ICEHID,$(APP_CFLAGS))) SRC_STANDALONE = lf_icehid.c @@ -57,7 +61,11 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_LEGIC,$(APP_CFLAGS))) SRC_STANDALONE = hf_legic.c endif -# WITH_STANDALONE_LF_MSDSAL +# WITH_STANDALONE_HF_MSDSAL ifneq (,$(findstring WITH_STANDALONE_HF_MSDSAL,$(APP_CFLAGS))) SRC_STANDALONE = hf_msdsal.c endif +# WITH_STANDALONE_HF_ICECLASS +ifneq (,$(findstring WITH_STANDALONE_HF_ICECLASS,$(APP_CFLAGS))) + SRC_STANDALONE = hf_iceclass.c +endif diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c new file mode 100644 index 000000000..97ef78745 --- /dev/null +++ b/armsrc/Standalone/hf_aveful.c @@ -0,0 +1,258 @@ +//----------------------------------------------------------------------------- +// A. Ozkal, 2020 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// main code for HF Mifare Ultralight read/simulation by Ave Ozkal +//----------------------------------------------------------------------------- + +// Several parts of this code is based on code by Craig Young from HF_YOUNG + +// This code does not: +// - Account for cards with non-default keys on authentication (MFU EV1 etc) + +// This code is designed to work with: +// - MIFARE Ultralight +// - MIFARE Ultralight EV1 (default keys) +// - MIFARE Ultralight Nano (untested, but should work) +// - Infineon My-d Move (without password set) +// - Infineon My-d Move Lean +// - Any other Ultralight clones that have no auth and MAX_DEFAULT_BLOCKS (16) blocks + +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" + +#include "ticks.h" // SpinDelay +#include "protocols.h" // MIFARE_ULEV1_VERSION, MIFARE_ULEV1_READSIG, MIFARE_ULEV1_READ_CNT, MIFARE_ULEV1_CHECKTEAR +#include // memcmp +#include "mifareutil.h" +#include "iso14443a.h" + +#define SAK 0x00 +#define ATQA0 0x44 +#define ATQA1 0x00 + +#define STATE_SEARCH 0 +#define STATE_READ 1 +#define STATE_EMUL 2 + +// Taken from cmdhfmfu.c, increased by 01h to be 1 indexed +#define MAX_UL_BLOCKS 0x10 +#define MAX_UL_NANO_40 0x0B +#define MAX_ULEV1a_BLOCKS 0x14 +#define MAX_ULEV1b_BLOCKS 0x29 +#define MAX_MY_D_MOVE 0x26 +#define MAX_MY_D_MOVE_LEAN 0x10 +#define MAX_DEFAULT_BLOCKS 0x10 + +typedef struct { + uint8_t uid[10]; + uint8_t uidlen; + uint8_t atqa[2]; + uint8_t sak; +} PACKED card_clone_t; + +int get_block_count(iso14a_card_select_t card, uint8_t version[], uint16_t version_len); +uint16_t get_ev1_version(iso14a_card_select_t card, uint8_t *version); +uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *signature); +uint16_t get_ev1_counter(iso14a_card_select_t card, uint8_t counter, uint8_t *response); +uint16_t get_ev1_tearing(iso14a_card_select_t card, uint8_t counter, uint8_t *response); + +uint16_t get_ev1_version(iso14a_card_select_t card, uint8_t *version) { + return mifare_sendcmd(MIFARE_ULEV1_VERSION, NULL, 0, version, NULL, NULL); +} + +uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *response) { + uint8_t cmd[4] = {MIFARE_ULEV1_READSIG, 0x00, 0x00, 0x00}; + AddCrc14A(cmd, 2); + ReaderTransmit(cmd, sizeof(cmd), NULL); + return ReaderReceive(response, NULL); +} + +uint16_t get_ev1_counter(iso14a_card_select_t card, uint8_t counter, uint8_t *response) { + uint8_t cmd[4] = {MIFARE_ULEV1_READ_CNT, counter, 0x00, 0x00}; + AddCrc14A(cmd, 2); + ReaderTransmit(cmd, sizeof(cmd), NULL); + return ReaderReceive(response, NULL); +} + +uint16_t get_ev1_tearing(iso14a_card_select_t card, uint8_t counter, uint8_t *response) { + uint8_t cmd[4] = {MIFARE_ULEV1_CHECKTEAR, counter, 0x00, 0x00}; + AddCrc14A(cmd, 2); + ReaderTransmit(cmd, sizeof(cmd), NULL); + return ReaderReceive(response, NULL); +} + +int get_block_count(iso14a_card_select_t card, uint8_t version[], uint16_t version_len) { + // Default to MAX_DEFAULT_BLOCKS blocks + int block_count = MAX_DEFAULT_BLOCKS; + // Most of this code is from cmdhfmfu.c + // Infineon manufacturer ID + if (card.uid[0] == 0x05) { + // Infinition MY-D tests Exam high nibble + uint8_t nib = (card.uid[1] & 0xf0) >> 4; + switch (nib) { + case 3: + block_count = MAX_MY_D_MOVE; + break; // or SLE 66R01P // 38 pages of 4 bytes + case 7: + block_count = MAX_MY_D_MOVE_LEAN; + break; // or SLE 66R01L // 16 pages of 4 bytes + } + } else { + // Moved this from case to if as I only care about non-ultralight ev0. + if (version_len == 0x0A) { + if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0B", 7) == 0) { block_count = MAX_ULEV1a_BLOCKS; } + else if (memcmp(version, "\x00\x04\x03\x01\x02\x00\x0B", 7) == 0) { block_count = MAX_UL_NANO_40; } + else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0B", 7) == 0) { block_count = MAX_ULEV1a_BLOCKS; } + else if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0E", 7) == 0) { block_count = MAX_ULEV1b_BLOCKS; } + else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0E", 7) == 0) { block_count = MAX_ULEV1b_BLOCKS; } + else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { block_count = MAX_ULEV1b_BLOCKS; } // Mikron JSC Russia EV1 41 pages tag + else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { block_count = MAX_UL_BLOCKS; } + else if (version[2] == 0x03) { block_count = MAX_ULEV1a_BLOCKS; } + } + } + + return block_count; +} + +void ModInfo(void) { + DbpString(" HF Mifare Ultralight read/simulation by Ave Ozkal"); +} + +void RunMod(void) { + StandAloneMode(); + Dbprintf("AveFUL (MF Ultralight read/emul) started"); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // the main loop for your standalone mode + for (;;) { + WDT_HIT(); + + // exit from RunMod, send a usbcommand. + if (data_available()) break; + + iso14a_card_select_t card; + + SpinDelay(500); + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); + + // 0 = search, 1 = read, 2 = emul + int state = STATE_SEARCH; + + DbpString("Scanning..."); + int button_pressed = BUTTON_NO_CLICK; + for (;;) { + // Was our button held down or pressed? + button_pressed = BUTTON_HELD(1000); + + if (button_pressed != BUTTON_NO_CLICK || data_available()) + break; + else if (state == STATE_SEARCH) { + if (!iso14443a_select_card(NULL, &card, NULL, true, 0, true)) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + SpinDelay(500); + continue; + } else { + if (card.sak == SAK && card.atqa[0] == ATQA0 && card.atqa[1] == ATQA1 && card.uidlen == 7) { + DbpString("Found ultralight with UID: "); + Dbhexdump(card.uidlen, card.uid, 0); + state = STATE_READ; + } else { + DbpString("Found non-ultralight card, ignoring."); + } + } + } else if (state == STATE_READ) { + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + iso14443a_select_card(NULL, NULL, NULL, true, 0, true); + bool read_successful = true; + + // Get version and re-select card as UL EV0s like to shut off after a 0x60 + uint8_t version[10] = {0x00}; + uint16_t version_len = 0; + version_len = get_ev1_version(card, version); + iso14443a_select_card(NULL, NULL, NULL, true, 0, true); + + int block_count = get_block_count(card, version, version_len); + Dbprintf("Card was determined as having %d blocks.", block_count); + Dbprintf("Contents:"); + + for (int i = 0; i < block_count; i++) { + uint8_t dataout[16] = {0x00}; + if (mifare_ultra_readblock(i, dataout)) { + // If there's an error reading, go back to search state + read_successful = false; + break; + } + // We're skipping 14 blocks (56 bytes) here, as that "[...] has version/signature/counter data here" according to comments on hf_mfu_dumptoemulator + // When converting a bin, it's almost all 0 other than one 0x0F byte, and functionality seems to be unaffected if that byte is set to 0x00. + emlSetMem_xt(dataout, 14 + i, 1, 4); + Dbhexdump(4, dataout, 0); + } + + // It's not the best way to determine this, + // but with what I'm trying to support It Should Be Okay + bool is_ev1 = (version_len != 0) && (block_count != 16); + + if (read_successful) { + uint8_t signature[34] = {0x00}; + if (is_ev1) { + get_ev1_signature(card, signature); + } + Dbprintf("Preparing emulator memory with:"); + // Fill first 14 blocks with 0x00 (see comment above) + for (int i = 0; i < 14; i++) { + uint8_t dataout[4] = {0x00, 0x00, 0x00, 0x00}; + + if (is_ev1 && (i == 0 || i == 1)) { + // On block 0 and 1, set version on EV1 + memcpy(dataout, version + (i * 4), 4); + } else if (i == 2) { + // On block 2, set last byte to the card's block count + dataout[3] = block_count; + } else if (is_ev1 && ((i > 2 && i < 11))) { + // On 3-10 add signature on EV1 + memcpy(dataout, signature + ((i - 3) * 4), 4); + } else if (is_ev1 && (i > 10)) { + // On 11-14 read and set counter and tearing on EV1 + uint8_t counter[5]; + uint8_t tearing[3]; + get_ev1_counter(card, i - 11, counter); + get_ev1_tearing(card, i - 11, tearing); + memcpy(dataout, counter, 3); + memcpy(dataout + 3, tearing, 1); + } + + Dbhexdump(4, dataout, 0); + emlSetMem_xt(dataout, i, 1, 4); + } + Dbprintf("Successfully loaded into emulator memory..."); + state = STATE_EMUL; + } else { + Dbprintf("Read failure, going back to search state."); + state = STATE_SEARCH; + } + } else if (state == STATE_EMUL) { + uint8_t flags = FLAG_7B_UID_IN_DATA; + + Dbprintf("Starting simulation, press pm3-button to stop and go back to search state."); + SimulateIso14443aTag(7, flags, card.uid); + + // Go back to search state if user presses pm3-button + state = STATE_SEARCH; + } + } + if (button_pressed == BUTTON_HOLD) //Holding down the button + break; + } + + DbpString("exiting"); + LEDsoff(); +} diff --git a/armsrc/Standalone/hf_bog.c b/armsrc/Standalone/hf_bog.c index 50e1d824f..1d766aa52 100644 --- a/armsrc/Standalone/hf_bog.c +++ b/armsrc/Standalone/hf_bog.c @@ -15,7 +15,7 @@ The retrieved sniffing session can be acquired by connecting the device to a client that supports the reconnect capability and issue 'hf 14a list'. In order to view the grabbed authentication attempts in the flash mem, -you can simply run 'script run read_pwd_mem' or just 'mem dump p l 256' +you can simply run 'script run mem_readpwd' or just 'mem dump p l 256' from the client to view the stored quadlets. */ @@ -249,5 +249,5 @@ void RunMod(void) { LEDsoff(); SpinDelay(300); Dbprintf("- [ End ] -> You can take shell back ..."); - Dbprintf("- [ ! ] -> use 'script run read_pwd_mem_spiffs' to print passwords"); + Dbprintf("- [ ! ] -> use 'script run data_read_pwd_mem_spiffs' to print passwords"); } diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index 8e20ae414..e3115ee7a 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -484,22 +484,18 @@ failtag: iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); SpinOff(50); LED_A_ON(); - uint8_t ticker = 0; while (!iso14443a_select_card(cjuid, &p_card, &cjcuid, true, 0, true)) { WDT_HIT(); - - ticker++; - if (ticker % 64 == 0) { - LED_A_INV(); - } - if (BUTTON_HELD(10) == BUTTON_HOLD) { WDT_HIT(); DbprintfEx(FLAG_NEWLINE, "\t\t\t[ READING FLASH ]"); ReadLastTagFromFlash(); goto readysim; } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(500); + LED_A_INV(); } SpinOff(50); diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c new file mode 100644 index 000000000..00de115e6 --- /dev/null +++ b/armsrc/Standalone/hf_iceclass.c @@ -0,0 +1,521 @@ +//----------------------------------------------------------------------------- +// Christian Herrmann, 2020 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// main code for hf_iceclass by Iceman +//----------------------------------------------------------------------------- +// +// Created for the live streamed talk 'DEFCON 28 Wireless Village-Omikron and Iceman - Ghosting the PACS-man: New Tools and Techniques' +// https://www.youtube.com/watch?v=ghiHXK4GEzE +// +// I created a youtube video demostrating the HF_ICECLASS standalone mode +// https://youtu.be/w_1GnAscNIU +// +// + +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "appmain.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "util.h" +#include "ticks.h" +#include "dbprint.h" +#include "spiffs.h" +#include "iclass.h" +#include "iso15693.h" +#include "optimized_cipher.h" +#include "pm3_cmd.h" +#include "protocols.h" + + +#define ICE_STATE_NONE 0 +#define ICE_STATE_FULLSIM 1 +#define ICE_STATE_ATTACK 2 +#define ICE_STATE_READER 3 +#define ICE_STATE_CONFIGCARD 4 + +// ==================================================== +// Select which standalone function to be active. +// 4 possiblities. Uncomment the one you wanna use. + +#define ICE_USE ICE_STATE_FULLSIM +//#define ICE_USE ICE_STATE_ATTACK +//#define ICE_USE ICE_STATE_READER +//#define ICE_USE ICE_STATE_CONFIGCARD + +// ==================================================== + + +#define NUM_CSNS 9 +#define MAC_RESPONSES_SIZE (16 * NUM_CSNS) +#define HF_ICLASS_FULLSIM_ORIG_BIN "iceclass-orig.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" +#define HF_ICLASS_CC_B "iceclass_cc_b.bin" +char *cc_files[] = { HF_ICLASS_CC_A, HF_ICLASS_CC_B }; + + + +// times in ssp_clk_cycles @ 3,3625MHz when acting as reader +#ifndef DELAY_ICLASS_VICC_TO_VCD_READER +#define DELAY_ICLASS_VICC_TO_VCD_READER DELAY_ISO15693_VICC_TO_VCD_READER +#endif + +#ifndef ICLASS_16KS_SIZE +#define ICLASS_16KS_SIZE 0x100 * 8 +#endif + +// iclass card descriptors +char *card_types[] = { + "PicoPass 16K / 16", // 000 + "PicoPass 32K with current book 16K / 16", // 001 + "Unknown Card Type!", // 010 + "Unknown Card Type!", // 011 + "PicoPass 2K", // 100 + "Unknown Card Type!", // 101 + "PicoPass 16K / 2", // 110 + "PicoPass 32K with current book 16K / 2", // 111 +}; + +uint8_t card_app2_limit[] = { + 0xff, + 0xff, + 0xff, + 0xff, + 0x1f, + 0xff, + 0xff, + 0xff, +}; + +static uint8_t aa2_key[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +static uint8_t legacy_aa1_key[] = {0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78}; + +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 *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, + 0x10, 0x97, 0x83, 0x7B, 0xF7, 0xFF, 0x12, 0xE0, + 0x13, 0x97, 0x82, 0x7A, 0xF7, 0xFF, 0x12, 0xE0, + 0x07, 0x0E, 0x0D, 0xF9, 0xF7, 0xFF, 0x12, 0xE0, + 0x14, 0x96, 0x84, 0x76, 0xF7, 0xFF, 0x12, 0xE0, + 0x17, 0x96, 0x85, 0x71, 0xF7, 0xFF, 0x12, 0xE0, + 0xCE, 0xC5, 0x0F, 0x77, 0xF7, 0xFF, 0x12, 0xE0, + 0xD2, 0x5A, 0x82, 0xF8, 0xF7, 0xFF, 0x12, 0xE0 +}; + +static void download_instructions(uint8_t t) { + DbpString(""); + switch (t) { + case ICE_STATE_FULLSIM: { + DbpString("The emulator memory was saved to SPIFFS"); + DbpString("1. " _YELLOW_("mem spiffs dump o " HF_ICLASS_FULLSIM_MOD_BIN " f " HF_ICLASS_FULLSIM_MOD" e")); + DbpString("2. " _YELLOW_("hf iclass view -f " HF_ICLASS_FULLSIM_MOD_BIN)); + break; + } + case ICE_STATE_ATTACK: { + DbpString("The collected data was saved to SPIFFS. The file names below may differ"); + DbpString("1. " _YELLOW_("mem spiffs tree")); + DbpString("2. " _YELLOW_("mem spiffs dump o " HF_ICLASS_ATTACK_BIN " f " HF_ICLASS_ATTACK_BIN)); + DbpString("3. " _YELLOW_("hf iclass loclass f " HF_ICLASS_ATTACK_BIN)); + break; + } + case ICE_STATE_READER: { + DbpString("The found tags was saved to SPIFFS"); + DbpString("1. " _YELLOW_("mem spiffs tree")); + DbpString("2. " _YELLOW_("mem spiffs dump h")); + break; + } + } +} + +// Save to flash if file doesn't exist. +// Write over file if size of flash file is less than new datalen +static void save_to_flash(uint8_t *data, uint16_t datalen) { + + rdv40_spiffs_lazy_mount(); + + char fn[SPIFFS_OBJ_NAME_LEN]; + sprintf(fn, "iclass-%02X%02X%02X%02X%02X%02X%02X%02X.bin", + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7] + ); + + int res; + if (exists_in_spiffs(fn) == false) { + res = rdv40_spiffs_write(fn, data, datalen, RDV40_SPIFFS_SAFETY_SAFE); + if (res == SPIFFS_OK) { + Dbprintf("saved to " _GREEN_("%s"), fn); + } + } else { + + // if already exist, see if saved file is smaller.. + uint32_t fsize = 0; + res = rdv40_spiffs_stat(fn, &fsize, RDV40_SPIFFS_SAFETY_SAFE); + if (res == SPIFFS_OK) { + + if (fsize < datalen) { + res = rdv40_spiffs_write(fn, data, datalen, RDV40_SPIFFS_SAFETY_SAFE); + if (res == SPIFFS_OK) { + Dbprintf("wrote over " _GREEN_("%s"), fn); + } + } + } + } + + rdv40_spiffs_lazy_unmount(); +} + +static int fullsim_mode(void) { + + rdv40_spiffs_lazy_mount(); + + SpinOff(0); + uint8_t *emul = BigBuf_get_EM_addr(); + uint32_t fsize = size_in_spiffs(HF_ICLASS_FULLSIM_ORIG_BIN); + int res = rdv40_spiffs_read_as_filetype(HF_ICLASS_FULLSIM_ORIG_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + if (res == SPIFFS_OK) { + Dbprintf("loaded " _GREEN_(HF_ICLASS_FULLSIM_ORIG_BIN) " (%u bytes)", fsize); + } + + iclass_simulate(ICLASS_SIM_MODE_FULL, 0, false, NULL, NULL, NULL); + + LED_B_ON(); + rdv40_spiffs_lazy_mount(); + res = rdv40_spiffs_write(HF_ICLASS_FULLSIM_MOD_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + LED_B_OFF(); + if (res == SPIFFS_OK) { + Dbprintf("wrote emulator memory to " _GREEN_(HF_ICLASS_FULLSIM_MOD_BIN)); + } else { + Dbprintf(_RED_("error") " writing "HF_ICLASS_FULLSIM_MOD_BIN" to flash ( %d )", res); + } + + DbpString("-=[ exiting " _CYAN_("`full simulation`") " mode ]=-"); + return PM3_SUCCESS; +} + +static int reader_attack_mode(void) { + + BigBuf_free(); + uint16_t mac_response_len = 0; + uint8_t *mac_responses = BigBuf_malloc(MAC_RESPONSES_SIZE); + + iclass_simulate(ICLASS_SIM_MODE_READER_ATTACK, NUM_CSNS, false, csns, mac_responses, &mac_response_len); + + if (mac_response_len > 0) { + + bool success = (mac_response_len == MAC_RESPONSES_SIZE); + uint8_t num_mac = (mac_response_len >> 4); + Dbprintf("%u out of %d MAC obtained [%s]", num_mac, NUM_CSNS, (success) ? _GREEN_("ok") : _RED_("fail")); + + size_t dumplen = NUM_CSNS * 24; + + uint8_t *dump = BigBuf_malloc(dumplen); + if (dump == false) { + Dbprintf("failed to allocate memory"); + return PM3_EMALLOC; + } + + // need zeroes for the EPURSE + memset(dump, 0, dumplen); + + for (uint8_t i = 0 ; i < NUM_CSNS ; i++) { + //copy CSN + memcpy(dump + (i * 24), csns + (i * 8), 8); + //copy epurse + memcpy(dump + (i * 24) + 8, mac_responses + (i * 16), 8); + // NR_MAC (eight bytes from the response) ( 8b csn + 8b epurse == 16) + memcpy(dump + (i * 24) + 16, mac_responses + (i * 16) + 8, 8); + } + + LED_B_ON(); + rdv40_spiffs_lazy_mount(); + + char fn[32]; + uint16_t p_namelen = strlen(HF_ICLASS_ATTACK_BIN); + uint16_t num = 1; + sprintf(fn, "%.*s%s", p_namelen, HF_ICLASS_ATTACK_BIN, ".bin"); + + while (exists_in_spiffs(fn)) { + sprintf(fn, "%.*s-%u%s", p_namelen, HF_ICLASS_ATTACK_BIN, num, ".bin"); + num++; + } + int res = rdv40_spiffs_write(fn, dump, dumplen, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + LED_B_OFF(); + if (res == SPIFFS_OK) { + Dbprintf("saved to " _GREEN_("%s"), fn); + } else { + Dbprintf(_RED_("error") " writing %s to flash ( %d )", fn, res); + } + } + BigBuf_free(); + DbpString("-=[ exiting " _CYAN_("`reader attack`") " mode ]=-"); + return PM3_SUCCESS; +} + +static int reader_dump_mode(void) { + + DbpString("this mode has no tracelog"); + if (have_aa2()) + DbpString("dumping of " _YELLOW_("AA2 enabled")); + + for (;;) { + + BigBuf_free(); + + uint8_t *card_data = BigBuf_malloc(ICLASS_16KS_SIZE); + memset(card_data, 0xFF, ICLASS_16KS_SIZE); + + if (BUTTON_PRESS()) { + DbpString("button pressed"); + break; + } + + // setup authenticate AA1 + iclass_auth_req_t auth = { + .use_raw = false, + .use_elite = false, + .use_credit_key = false, + .do_auth = true, + .send_reply = false, + }; + memcpy(auth.key, legacy_aa1_key, sizeof(auth.key)); + + Iso15693InitReader(); + set_tracing(false); + + // select tag. + uint32_t eof_time = 0; + bool res = select_iclass_tag(card_data, auth.use_credit_key, &eof_time); + if (res == false) { + switch_off(); + continue; + } + + picopass_hdr *hdr = (picopass_hdr *)card_data; + // sanity check of CSN. + if (hdr->csn[7] != 0xE0 && hdr->csn[6] != 0x12) { + switch_off(); + continue; + } + + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + // get 3 config bits + uint8_t type = (hdr->conf.chip_config & 0x10) >> 2; + type |= (hdr->conf.mem_config & 0x80) >> 6; + type |= (hdr->conf.mem_config & 0x20) >> 5; + + Dbprintf(_GREEN_("%s") ", dumping...", card_types[type]); + + uint8_t pagemap = get_pagemap(hdr); + uint8_t app1_limit, app2_limit, start_block; + + // tags configured for NON SECURE PAGE, acts different + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + app1_limit = card_app2_limit[type]; + app2_limit = 0; + start_block = 3; + } else { + + app1_limit = hdr->conf.app_limit; + app2_limit = card_app2_limit[type]; + start_block = 5; + + res = authenticate_iclass_tag(&auth, hdr, &start_time, &eof_time, NULL); + if (res == false) { + switch_off(); + Dbprintf(_RED_("failed AA1 auth") ", skipping "); + continue; + } + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + } + + uint16_t dumped = 0; + + // main read loop + for (uint16_t i = start_block; i <= app1_limit; i++) { + if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time)) { + dumped++; + } + } + + if (pagemap != PICOPASS_NON_SECURE_PAGEMODE && have_aa2()) { + + // authenticate AA2 + auth.use_raw = false; + auth.use_credit_key = true; + memcpy(auth.key, aa2_key, sizeof(auth.key)); + + res = select_iclass_tag(card_data, auth.use_credit_key, &eof_time); + if (res) { + + // sanity check of CSN. + if (hdr->csn[7] != 0xE0 && hdr->csn[6] != 0x12) { + switch_off(); + continue; + } + + res = authenticate_iclass_tag(&auth, hdr, &start_time, &eof_time, NULL); + if (res) { + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + for (uint16_t i = app1_limit + 1; i <= app2_limit; i++) { + if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time)) { + dumped++; + } + } + } else { + DbpString(_RED_("failed AA2 auth")); + } + } else { + DbpString(_RED_("failed selecting AA2")); + + // sanity check of CSN. + if (hdr->csn[7] != 0xE0 && hdr->csn[6] != 0x12) { + switch_off(); + continue; + } + } + } + switch_off(); + save_to_flash(card_data, (start_block + dumped) * 8); + Dbprintf("%u bytes saved", (start_block + dumped) * 8); + } + DbpString("-=[ exiting " _CYAN_("`read & dump`") " mode ]=-"); + return PM3_SUCCESS; +} + +static int config_sim_mode(void) { + + uint8_t *emul = BigBuf_get_EM_addr(); + + for (uint8_t i = 0; i < 2; i++) { + SpinOff(0); + rdv40_spiffs_lazy_mount(); + uint32_t fsize = size_in_spiffs(cc_files[i]); + int res = rdv40_spiffs_read_as_filetype(cc_files[i], emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + + if (res == SPIFFS_OK) { + Dbprintf("loaded " _GREEN_("%s") " (%u bytes) to emulator memory", cc_files[i], fsize); + iclass_simulate(ICLASS_SIM_MODE_FULL, 0, false, NULL, NULL, NULL); + } + } + + DbpString("-=[ exiting " _CYAN_("`glitch & config`") " mode ]=-"); + return PM3_SUCCESS; +} + +void ModInfo(void) { + DbpString(" HF iCLASS mode - aka iceCLASS (iceman)"); +} + +void RunMod(void) { + + uint8_t mode = ICE_USE; + uint8_t *bb = BigBuf_get_EM_addr(); + if (bb[0] > 0 && bb[0] < 5) { + mode = bb[0]; + } + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + BigBuf_Clear_ext(false); + + StandAloneMode(); + Dbprintf(_YELLOW_("HF iCLASS mode a.k.a iceCLASS started")); + + + for (;;) { + + WDT_HIT(); + + if (mode == ICE_STATE_NONE) break; + if (data_available()) break; + + int res; + switch (mode) { + + case ICE_STATE_FULLSIM: { + DbpString("-=[ enter " _CYAN_("`full simulation`") " mode ]=-"); + + // Look for iCLASS dump file + rdv40_spiffs_lazy_mount(); + if (exists_in_spiffs(HF_ICLASS_FULLSIM_ORIG_BIN) == false) { + Dbprintf(_RED_("error") " " _YELLOW_(HF_ICLASS_FULLSIM_ORIG_BIN) " file missing"); + mode = ICE_STATE_NONE; + } + rdv40_spiffs_lazy_unmount(); + + if (mode == ICE_STATE_FULLSIM) { + res = fullsim_mode(); + if (res == PM3_SUCCESS) { + download_instructions(mode); + } + } + // the button press to exit sim, is captured in main loop here + mode = ICE_STATE_NONE; + break; + } + case ICE_STATE_ATTACK: { + DbpString("-=[ enter " _CYAN_("`reader attack`") " mode ]=-"); + res = reader_attack_mode(); + if (res == PM3_SUCCESS) + download_instructions(mode); + + mode = ICE_STATE_NONE; + break; + } + case ICE_STATE_READER: { + DbpString("-=[ enter " _CYAN_("`read & dump`") " mode, continuous scanning ]=-"); + res = reader_dump_mode(); + if (res == PM3_SUCCESS) + download_instructions(mode); + + mode = ICE_STATE_NONE; + break; + } + case ICE_STATE_CONFIGCARD: { + DbpString("-=[ enter " _CYAN_("`glitch & config`") " mode ]=-"); + + // Look for config cards + rdv40_spiffs_lazy_mount(); + for (uint8_t i = 0; i < 2; i++) { + if (exists_in_spiffs(cc_files[i]) == false) { + Dbprintf(_RED_("error") ", " _YELLOW_("%s") " file missing", cc_files[i]); + mode = ICE_STATE_NONE; + } + } + rdv40_spiffs_lazy_unmount(); + + if (mode == ICE_STATE_CONFIGCARD) + config_sim_mode(); + + mode = ICE_STATE_NONE; + break; + } + } + } + + switch_off(); + Dbprintf("-=[ exit ]=-"); +} diff --git a/armsrc/Standalone/hf_legic.c b/armsrc/Standalone/hf_legic.c index 73590268e..6ebe6eff6 100644 --- a/armsrc/Standalone/hf_legic.c +++ b/armsrc/Standalone/hf_legic.c @@ -22,7 +22,6 @@ #include "legic.h" // legic_card_select_t struct #include "spiffs.h" // flashmem - /* * To list all dump files from flash: * @@ -106,6 +105,7 @@ void RunMod(void) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); Dbprintf("[=] >> HF Legic Prime Read/Simulate Started <<"); DbpString("[=] press and HOLD button to exit standalone mode"); + for (;;) { WDT_HIT(); @@ -163,7 +163,7 @@ void RunMod(void) { } // The read data is migrated to a MIM1024 card - LegicRfSimulate(ct); + LegicRfSimulate(ct, false); } } diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c index c0d603fbd..e14e89789 100644 --- a/armsrc/Standalone/hf_mattyrun.c +++ b/armsrc/Standalone/hf_mattyrun.c @@ -575,4 +575,5 @@ void RunMod(void) { } } } + LEDsoff(); } diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index f97dc0d7c..b5585c188 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -305,6 +305,8 @@ void RunMod(void) { DbpString("\n"_YELLOW_("!!") "Waiting for a card reader..."); } } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); } else if (state == STATE_EMU) { LED_A_OFF(); LED_C_ON(); diff --git a/armsrc/Standalone/hf_young.c b/armsrc/Standalone/hf_young.c index a1b32dfab..1673b8975 100644 --- a/armsrc/Standalone/hf_young.c +++ b/armsrc/Standalone/hf_young.c @@ -54,7 +54,7 @@ void RunMod(void) { for (;;) { WDT_HIT(); // exit from Standalone Mode, send a usbcommand. - if (data_available()) return; + if (data_available()) break; SpinDelay(300); @@ -72,7 +72,7 @@ void RunMod(void) { for (;;) { // exit from Standalone Mode, send a usbcommand. - if (data_available()) return; + if (data_available()) break; if (BUTTON_PRESS()) { if (cardRead[selected]) { @@ -89,6 +89,9 @@ void RunMod(void) { } if (!iso14443a_select_card(NULL, &card[selected], NULL, true, 0, true)) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + SpinDelay(500); continue; } else { Dbprintf("Read UID:"); @@ -220,7 +223,7 @@ void RunMod(void) { DbpString("Playing"); for (; ;) { // exit from Standalone Mode, send a usbcommand. - if (data_available()) return; + if (data_available()) break; int button_pressed = BUTTON_HELD(1000); if (button_pressed == BUTTON_NO_CLICK) { // No button action, proceed with sim @@ -277,4 +280,6 @@ void RunMod(void) { LED(selected + 1, 0); } } + DbpString(_YELLOW_("[=]") "exiting"); + LEDsoff(); } diff --git a/armsrc/Standalone/lf_em4100rwc.c b/armsrc/Standalone/lf_em4100rwc.c index c54e54fa2..d095b944b 100644 --- a/armsrc/Standalone/lf_em4100rwc.c +++ b/armsrc/Standalone/lf_em4100rwc.c @@ -34,7 +34,7 @@ #endif #define MAX_IND 16 // 4 LEDs - 2^4 combinations -#define CLOCK 64 //for 125kHz +#define LF_CLOCK 64 // for 125kHz // low & high - array for storage IDs. Its length must be equal. // Predefined IDs must be stored in low[]. @@ -57,34 +57,35 @@ static uint64_t rev_quads(uint64_t bits) { } static void fillbuff(uint8_t bit) { - memset(bba + buflen, bit, CLOCK / 2); - buflen += (CLOCK / 2); - memset(bba + buflen, bit ^ 1, CLOCK / 2); - buflen += (CLOCK / 2); + memset(bba + buflen, bit, LF_CLOCK / 2); + buflen += (LF_CLOCK / 2); + memset(bba + buflen, bit ^ 1, LF_CLOCK / 2); + buflen += (LF_CLOCK / 2); } static void construct_EM410x_emul(uint64_t id) { + int i, j; int binary[4] = {0}; int parity[4] = {0}; buflen = 0; - - for (uint8_t i = 0; i < 9; i++) + + for (i = 0; i < 9; i++) fillbuff(1); - for (uint8_t i = 0; i < 10; i++) { - for (uint8_t j = 3; j > 0; j--, id /= 2) + for (i = 0; i < 10; i++) { + for (j = 3; j >= 0; j--, id /= 2) binary[j] = id % 2; - for (uint8_t j = 0; j < 4; j++) + for (j = 0; j < 4; j++) fillbuff(binary[j]); fillbuff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); - for (uint8_t j = 0; j < 4; j++) + for (j = 0; j < 4; j++) parity[j] ^= binary[j]; } - for (uint8_t j = 0; j < 4; j++) + for (j = 0; j < 4; j++) fillbuff(parity[j]); fillbuff(0); @@ -207,7 +208,7 @@ void RunMod(void) { state = 0; } else if (button_pressed == BUTTON_SINGLE_CLICK) { // Click - write ID to tag - copy_em410x_to_t55xx(0, CLOCK, (uint32_t)(low[selected] >> 32), (uint32_t)(low[selected] & 0xffffffff)); + copy_em410x_to_t55xx(0, LF_CLOCK, (uint32_t)(low[selected] >> 32), (uint32_t)(low[selected] & 0xffffffff)); led_slot(selected); state = 0; // Switch to select mode } diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 95b42982a..9aa65de03 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -20,6 +20,7 @@ #include "fpga.h" #include "fpgaloader.h" #include "string.h" +#include "printf.h" #include "legicrf.h" #include "BigBuf.h" #include "iso14443a.h" @@ -32,6 +33,7 @@ #include "em4x50.h" #include "iclass.h" #include "legicrfsim.h" +//#include "cryptorfsim.h" #include "epa.h" #include "hfsnoop.h" #include "lfops.h" @@ -62,53 +64,35 @@ #include "spiffs.h" #endif -//============================================================================= -// A buffer where we can queue things up to be sent through the FPGA, for -// any purpose (fake tag, as reader, whatever). We go MSB first, since that -// is the order in which they go out on the wire. -//============================================================================= - -#define TOSEND_BUFFER_SIZE (9*MAX_FRAME_SIZE + 1 + 1 + 2) // 8 data bits and 1 parity bit per payload byte, 1 correction bit, 1 SOC bit, 2 EOC bits -uint8_t ToSend[TOSEND_BUFFER_SIZE]; -int ToSendMax = -1; - extern uint32_t _stack_start, _stack_end; - - -static int ToSendBit; struct common_area common_area __attribute__((section(".commonarea"))); static int button_status = BUTTON_NO_CLICK; static bool allow_send_wtx = false; +static uint16_t tearoff_delay_us = 0; +static bool tearoff_enabled = false; -inline void send_wtx(uint16_t wtx) { +int tearoff_hook(void) { + if (tearoff_enabled) { + if (tearoff_delay_us == 0) { + Dbprintf(_RED_("No tear-off delay configured!")); + return PM3_SUCCESS; // SUCCESS = the hook didn't do anything + } + SpinDelayUsPrecision(tearoff_delay_us); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + tearoff_enabled = false; + Dbprintf(_YELLOW_("Tear-off triggered!")); + return PM3_ETEAROFF; + } else { + return PM3_SUCCESS; // SUCCESS = the hook didn't do anything + } +} + +void send_wtx(uint16_t wtx) { if (allow_send_wtx) { reply_ng(CMD_WTX, PM3_SUCCESS, (uint8_t *)&wtx, sizeof(wtx)); } } -void ToSendReset(void) { - ToSendMax = -1; - ToSendBit = 8; -} - -void ToSendStuffBit(int b) { - if (ToSendBit >= 8) { - ToSendMax++; - ToSend[ToSendMax] = 0; - ToSendBit = 0; - } - - if (b) - ToSend[ToSendMax] |= (1 << (7 - ToSendBit)); - - ToSendBit++; - - if (ToSendMax >= sizeof(ToSend)) { - ToSendBit = 0; - DbpString("ToSendStuffBit overflowed!"); - } -} - //----------------------------------------------------------------------------- // Read an ADC channel and block till it completes, then return the result // in ADC units (0 to 1023). Also a routine to sum up a number of samples and @@ -213,7 +197,7 @@ static void MeasureAntennaTuning(void) { LED_A_ON(); // Let the FPGA drive the high-frequency antenna around 13.56 MHz. FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); SpinDelay(50); #if defined RDV4 @@ -272,7 +256,7 @@ static void SendVersion(void) { */ char *bootrom_version = *(char **)&_bootphase1_version_pointer; - strncat(VersionString, " "_YELLOW_("[ ARM ]")"\n", sizeof(VersionString) - strlen(VersionString) - 1); + strncat(VersionString, " [ "_YELLOW_("ARM")" ]\n", sizeof(VersionString) - strlen(VersionString) - 1); if (bootrom_version < &_flash_start || bootrom_version >= &_flash_end) { strcat(VersionString, "bootrom version information appears invalid\n"); @@ -292,7 +276,7 @@ static void SendVersion(void) { strncat(VersionString, " compiled with GCC "__VERSION__"\n", sizeof(VersionString) - strlen(VersionString) - 1); #endif - strncat(VersionString, "\n "_YELLOW_("[ FPGA ]")"\n ", sizeof(VersionString) - strlen(VersionString) - 1); + strncat(VersionString, "\n [ "_YELLOW_("FPGA")" ] \n ", sizeof(VersionString) - strlen(VersionString) - 1); for (int i = 0; i < g_fpga_bitstream_num; i++) { strncat(VersionString, g_fpga_version_information[i], sizeof(VersionString) - strlen(VersionString) - 1); @@ -328,6 +312,28 @@ static void TimingIntervalAcquisition(void) { StartTickCount(); } +static void print_debug_level(void) { + char dbglvlstr[20] = {0}; + switch (DBGLEVEL) { + case DBG_NONE: + sprintf(dbglvlstr, "NONE"); + break; + case DBG_ERROR: + sprintf(dbglvlstr, "ERROR"); + break; + case DBG_INFO: + sprintf(dbglvlstr, "INFO"); + break; + case DBG_DEBUG: + sprintf(dbglvlstr, "DEBUG"); + break; + case DBG_EXTENDED: + sprintf(dbglvlstr, "EXTENDED"); + break; + } + Dbprintf(" DBGLEVEL................%d ( " _YELLOW_("%s")" )", DBGLEVEL, dbglvlstr); +} + // 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) { @@ -367,17 +373,20 @@ static void SendStatus(void) { I2C_print_status(); #endif #ifdef WITH_LF - printConfig(); // LF Sampling config + printLFConfig(); // LF Sampling config printT55xxConfig(); // LF T55XX Config +#endif +#ifdef WITH_ISO14443a + printHf14aConfig(); // HF 14a config #endif printConnSpeed(); DbpString(_CYAN_("Various")); print_stack_usage(); + print_debug_level(); - Dbprintf(" DBGLEVEL................%d", DBGLEVEL); - Dbprintf(" ToSendMax...............%d", ToSendMax); - Dbprintf(" ToSendBit...............%d", ToSendBit); + tosend_t *ts = get_tosend(); + Dbprintf(" ToSendMax...............%d", ts->max); Dbprintf(" ToSend BUFFERSIZE.......%d", TOSEND_BUFFER_SIZE); while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available... uint16_t mainf = AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINF; // Get # main clocks within 16 slow clocks @@ -515,6 +524,7 @@ static void SendCapabilities(void) { // Show some leds in a pattern to identify StandAlone mod is running void StandAloneMode(void) { + DbpString(""); DbpString("Stand-alone mode, no computer necessary"); SpinDown(50); SpinDelay(50); @@ -735,10 +745,28 @@ static void PacketReceived(PacketCommandNG *packet) { // emulator case CMD_SET_DBGMODE: { DBGLEVEL = packet->data.asBytes[0]; - Dbprintf("Debug level: %d", DBGLEVEL); + print_debug_level(); reply_ng(CMD_SET_DBGMODE, PM3_SUCCESS, NULL, 0); break; } + case CMD_SET_TEAROFF: { + struct p { + uint16_t delay_us; + bool on; + bool off; + } PACKED; + struct p *payload = (struct p *)packet->data.asBytes; + if (payload->on && payload->off) + reply_ng(CMD_SET_TEAROFF, PM3_EINVARG, NULL, 0); + if (payload->on) + tearoff_enabled = true; + if (payload->off) + tearoff_enabled = false; + if (payload->delay_us > 0) + tearoff_delay_us = payload->delay_us; + reply_ng(CMD_SET_TEAROFF, PM3_SUCCESS, NULL, 0); + break; + } // always available case CMD_HF_DROPFIELD: { hf_field_off(); @@ -750,7 +778,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_LF_SAMPLING_PRINT_CONFIG: { - printConfig(); + printLFConfig(); break; } case CMD_LF_SAMPLING_GET_CONFIG: { @@ -767,8 +795,8 @@ static void PacketReceived(PacketCommandNG *packet) { } case CMD_LF_ACQ_RAW_ADC: { struct p { - uint8_t verbose; - uint32_t samples; + uint32_t samples : 31; + bool verbose : 1; } PACKED; struct p *payload = (struct p *)packet->data.asBytes; uint32_t bits = SampleLF(payload->verbose, payload->samples); @@ -778,22 +806,35 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_LF_MOD_THEN_ACQ_RAW_ADC: { struct p { uint32_t delay; - uint16_t ones; - uint16_t zeros; + uint16_t period_0; + uint16_t period_1; + uint8_t symbol_extra[LF_CMDREAD_MAX_EXTRA_SYMBOLS]; + uint16_t period_extra[LF_CMDREAD_MAX_EXTRA_SYMBOLS]; + uint32_t samples : 31; + bool verbose : 1; } PACKED; struct p *payload = (struct p *)packet->data.asBytes; - ModThenAcquireRawAdcSamples125k(payload->delay, payload->zeros, payload->ones, packet->data.asBytes + 8); + uint8_t symbol_extra[LF_CMDREAD_MAX_EXTRA_SYMBOLS]; + uint16_t period_extra[LF_CMDREAD_MAX_EXTRA_SYMBOLS]; + memcpy(symbol_extra, payload->symbol_extra, sizeof(symbol_extra)); + memcpy(period_extra, payload->period_extra, sizeof(period_extra)); + ModThenAcquireRawAdcSamples125k(payload->delay, payload->period_0, payload->period_1, symbol_extra, period_extra, packet->data.asBytes + sizeof(struct p), payload->verbose, payload->samples); break; } case CMD_LF_SNIFF_RAW_ADC: { - uint32_t bits = SniffLF(); - reply_mix(CMD_ACK, bits, 0, 0, 0, 0); + 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); + reply_ng(CMD_LF_SNIFF_RAW_ADC, PM3_SUCCESS, (uint8_t *)&bits, sizeof(bits)); break; } case CMD_LF_HID_WATCH: { uint32_t high, low; int res = lf_hid_watch(0, &high, &low); - reply_ng(CMD_LF_HID_WATCH, res, NULL, 0); + reply_ng(CMD_LF_HID_WATCH, res, NULL, 0); break; } case CMD_LF_HID_SIMULATE: { @@ -822,7 +863,8 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_LF_HID_CLONE: { - CopyHIDtoT55x7(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes[0]); + lf_hidsim_t *payload = (lf_hidsim_t *)packet->data.asBytes; + CopyHIDtoT55x7(payload->hi2, payload->hi, payload->lo, payload->longFMT); break; } case CMD_LF_IO_WATCH: { @@ -949,6 +991,16 @@ static void PacketReceived(PacketCommandNG *packet) { EM4xWriteWord(payload->address, payload->data, payload->password, payload->usepwd); break; } + case CMD_LF_EM4X_PROTECTWORD: { + struct p { + uint32_t password; + uint32_t data; + uint8_t usepwd; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + EM4xProtectWord(payload->data, payload->password, payload->usepwd); + break; + } case CMD_LF_AWID_WATCH: { uint32_t high, low; int res = lf_awid_watch(0, &high, &low); @@ -965,7 +1017,11 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_LF_COTAG_READ: { - Cotag(packet->oldarg[0]); + struct p { + uint8_t mode; + } PACKED; + struct p *payload = (struct p *)packet->data.asBytes; + Cotag(payload->mode); break; } #endif @@ -974,6 +1030,7 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_LF_HITAG_SNIFF: { // Eavesdrop Hitag tag, args = type SniffHitag2(); // SniffHitag2(packet->oldarg[0]); + reply_ng(CMD_LF_HITAG_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_LF_HITAG_SIMULATE: { // Simulate Hitag tag, args = memory content @@ -1023,7 +1080,10 @@ static void PacketReceived(PacketCommandNG *packet) { em4x50_read((em4x50_data_t *)packet->data.asBytes); break; } - + case CMD_LF_EM4X50_WIPE: { + em4x50_wipe((em4x50_data_t *)packet->data.asBytes); + break; + } #endif #ifdef WITH_ISO15693 @@ -1031,8 +1091,9 @@ static void PacketReceived(PacketCommandNG *packet) { AcquireRawAdcSamplesIso15693(); break; } - case CMD_HF_ISO15693_RAWADC: { - RecordRawAdcSamplesIso15693(); + case CMD_HF_ISO15693_SNIFF: { + SniffIso15693(0, NULL); + reply_ng(CMD_HF_ISO15693_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_ISO15693_COMMAND: { @@ -1048,14 +1109,31 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ISO15693_SIMULATE: { - SimTagIso15693(packet->oldarg[0], packet->data.asBytes); + struct p { + uint8_t uid[8]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + SimTagIso15693(payload->uid); + break; + } + case CMD_HF_ISO15693_CSETUID: { + struct p { + uint8_t uid[8]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + SetTag15693Uid(payload->uid); break; } #endif #ifdef WITH_LEGICRF case CMD_HF_LEGIC_SIMULATE: { - LegicRfSimulate(packet->oldarg[0]); + struct p { + uint8_t tagtype; + bool send_reply; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + LegicRfSimulate(payload->tagtype, payload->send_reply); break; } case CMD_HF_LEGIC_WRITER: { @@ -1091,17 +1169,21 @@ static void PacketReceived(PacketCommandNG *packet) { } case CMD_HF_ISO14443B_SNIFF: { SniffIso14443b(); + reply_ng(CMD_HF_ISO14443B_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_ISO14443B_SIMULATE: { - SimulateIso14443bTag(packet->oldarg[0]); + SimulateIso14443bTag(packet->data.asBytes); break; } case CMD_HF_ISO14443B_COMMAND: { - //SendRawCommand14443B(packet->oldarg[0],packet->oldarg[1],packet->oldarg[2],packet->data.asBytes); SendRawCommand14443B_Ex(packet); break; } + case CMD_HF_CRYPTORF_SIM : { +// simulate_crf_tag(); + break; + } #endif #ifdef WITH_FELICA @@ -1115,6 +1197,7 @@ static void PacketReceived(PacketCommandNG *packet) { } case CMD_HF_FELICA_SNIFF: { felica_sniff(packet->oldarg[0], packet->oldarg[1]); + reply_ng(CMD_HF_FELICA_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_FELICALITE_DUMP: { @@ -1124,8 +1207,24 @@ static void PacketReceived(PacketCommandNG *packet) { #endif #ifdef WITH_ISO14443a + case CMD_HF_ISO14443A_PRINT_CONFIG: { + printHf14aConfig(); + break; + } + case CMD_HF_ISO14443A_GET_CONFIG: { + hf14a_config *hf14aconfig = getHf14aConfig(); + reply_ng(CMD_HF_ISO14443A_GET_CONFIG, PM3_SUCCESS, (uint8_t *)hf14aconfig, sizeof(hf14a_config)); + break; + } + case CMD_HF_ISO14443A_SET_CONFIG: { + hf14a_config c; + memcpy(&c, packet->data.asBytes, sizeof(hf14a_config)); + setHf14aConfig(&c); + break; + } case CMD_HF_ISO14443A_SNIFF: { SniffIso14443a(packet->data.asBytes[0]); + reply_ng(CMD_HF_ISO14443A_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_ISO14443A_READER: { @@ -1197,6 +1296,10 @@ static void PacketReceived(PacketCommandNG *packet) { MifareUWriteBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } + case CMD_HF_MIFAREU_WRITEBL_COMPAT: { + MifareUWriteBlockCompat(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); + break; + } case CMD_HF_MIFARE_ACQ_ENCRYPTED_NONCES: { MifareAcquireEncryptedNonces(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); break; @@ -1231,7 +1334,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFARE_CHKKEYS: { - MifareChkKeys(packet->data.asBytes); + MifareChkKeys(packet->data.asBytes, false); break; } case CMD_HF_MIFARE_CHKKEYS_FAST: { @@ -1298,7 +1401,21 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_MIFARE_CIDENT: { - MifareCIdent(); + bool is_mfc = packet->data.asBytes[0]; + MifareCIdent(is_mfc); + break; + } + // Gen 3 magic cards + case CMD_HF_MIFARE_GEN3UID: { + MifareGen3UID(packet->oldarg[0], packet->data.asBytes); + break; + } + case CMD_HF_MIFARE_GEN3BLK: { + MifareGen3Blk(packet->oldarg[0], packet->data.asBytes); + break; + } + case CMD_HF_MIFARE_GEN3FREEZ: { + MifareGen3Freez(); break; } // mifare sniffer @@ -1356,6 +1473,15 @@ static void PacketReceived(PacketCommandNG *packet) { MifareU_Otp_Tearoff(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } + case CMD_HF_MFU_COUNTER_TEAROFF: { + struct p { + uint8_t counter; + uint32_t tearoff_time; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareU_Counter_Tearoff(payload->counter, payload->tearoff_time); + break; + } case CMD_HF_MIFARE_STATIC_NONCE: { MifareHasStaticNonce(); break; @@ -1376,7 +1502,13 @@ static void PacketReceived(PacketCommandNG *packet) { #ifdef WITH_ICLASS // Makes use of ISO14443a FPGA Firmware case CMD_HF_ICLASS_SNIFF: { - SniffIClass(); + struct p { + uint8_t jam_search_len; + uint8_t jam_search_string[]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + SniffIClass(payload->jam_search_len, payload->jam_search_string); + reply_ng(CMD_HF_ICLASS_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_HF_ICLASS_SIMULATE: { @@ -1388,7 +1520,12 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ICLASS_REPLAY: { - ReaderIClass_Replay(packet->oldarg[0], packet->data.asBytes); + struct p { + uint8_t reader[4]; + uint8_t mac[4]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + ReaderIClass_Replay(payload->reader, payload->mac); break; } case CMD_HF_ICLASS_EML_MEMSET: { @@ -1398,36 +1535,14 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ICLASS_WRITEBL: { - struct p { - uint8_t blockno; - uint8_t data[12]; - } PACKED; - struct p *payload = (struct p *)packet->data.asBytes; - iClass_WriteBlock(payload->blockno, payload->data); - break; - } - // iceman2019, unused? - case CMD_HF_ICLASS_READCHECK: { // auth step 1 - iClass_ReadCheck(packet->oldarg[0], packet->oldarg[1]); + iClass_WriteBlock(packet->data.asBytes); break; } case CMD_HF_ICLASS_READBL: { - /* - struct p { - uint8_t blockno; - } PACKED; - struct p *payload = (struct p *)packet->data.asBytes; - */ - iClass_ReadBlk(packet->data.asBytes[0]); + iClass_ReadBlock(packet->data.asBytes); break; } case CMD_HF_ICLASS_AUTH: { //check - /* - struct p { - uint8_t mac[4]; - } PACKED; - struct p *payload = (struct p *)packet->data.asBytes; - */ iClass_Authentication(packet->data.asBytes); break; } @@ -1436,7 +1551,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ICLASS_DUMP: { - iClass_Dump(packet->oldarg[0], packet->oldarg[1]); + iClass_Dump(packet->data.asBytes); break; } case CMD_HF_ICLASS_CLONE: { @@ -1449,6 +1564,10 @@ static void PacketReceived(PacketCommandNG *packet) { iClass_Clone(payload->startblock, payload->endblock, payload->data); break; } + case CMD_HF_ICLASS_RESTORE: { + iClass_Restore(packet->data.asBytes); + break; + } #endif #ifdef WITH_HFSNIFF @@ -1616,7 +1735,7 @@ static void PacketReceived(PacketCommandNG *packet) { case 1: // MEASURE_ANTENNA_TUNING_HF_START // Let the FPGA drive the high-frequency antenna around 13.56 MHz. FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_SUCCESS, NULL, 0); break; case 2: @@ -1796,12 +1915,9 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t filename[32]; uint8_t *pfilename = packet->data.asBytes; memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); - if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs dump : %s", filename); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Filename received for spiffs dump : %s", filename); - //uint32_t size = 0; - //rdv40_spiffs_stat((char *)filename, (uint32_t *)size,RDV40_SPIFFS_SAFETY_SAFE); uint32_t size = packet->oldarg[1]; - //uint8_t buff[size]; uint8_t *buff = BigBuf_malloc(size); rdv40_spiffs_read_as_filetype((char *)filename, (uint8_t *)buff, size, RDV40_SPIFFS_SAFETY_SAFE); @@ -1826,7 +1942,7 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t filename[32]; uint8_t *pfilename = packet->data.asBytes; memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); - if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs STAT : %s", filename); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Filename received for spiffs STAT : %s", filename); int changed = rdv40_spiffs_lazy_mount(); uint32_t size = size_in_spiffs((char *)filename); if (changed) rdv40_spiffs_lazy_unmount(); @@ -1839,7 +1955,7 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t filename[32]; uint8_t *pfilename = packet->data.asBytes; memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); - if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs REMOVE : %s", filename); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Filename received for spiffs REMOVE : %s", filename); rdv40_spiffs_remove((char *) filename, RDV40_SPIFFS_SAFETY_SAFE); LED_B_OFF(); break; @@ -1854,9 +1970,9 @@ static void PacketReceived(PacketCommandNG *packet) { strncpy((char *)src, token, sizeof(src) - 1); token = strtok(NULL, ","); strncpy((char *)dest, token, sizeof(dest) - 1); - if (DBGLEVEL > 1) { - Dbprintf("> Filename received as source for spiffs RENAME : %s", src); - Dbprintf("> Filename received as destination for spiffs RENAME : %s", dest); + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Filename received as source for spiffs RENAME : %s", src); + Dbprintf("Filename received as destination for spiffs RENAME : %s", dest); } rdv40_spiffs_rename((char *) src, (char *)dest, RDV40_SPIFFS_SAFETY_SAFE); LED_B_OFF(); @@ -1872,9 +1988,9 @@ static void PacketReceived(PacketCommandNG *packet) { strncpy((char *)src, token, sizeof(src) - 1); token = strtok(NULL, ","); strncpy((char *)dest, token, sizeof(dest) - 1); - if (DBGLEVEL > 1) { - Dbprintf("> Filename received as source for spiffs COPY : %s", src); - Dbprintf("> Filename received as destination for spiffs COPY : %s", dest); + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Filename received as source for spiffs COPY : %s", src); + Dbprintf("Filename received as destination for spiffs COPY : %s", dest); } rdv40_spiffs_copy((char *) src, (char *)dest, RDV40_SPIFFS_SAFETY_SAFE); LED_B_OFF(); @@ -1886,14 +2002,12 @@ static void PacketReceived(PacketCommandNG *packet) { uint32_t append = packet->oldarg[0]; uint32_t size = packet->oldarg[1]; uint8_t *data = packet->data.asBytes; - - //rdv40_spiffs_lazy_mount(); - uint8_t *pfilename = packet->data.asBytes; memcpy(filename, pfilename, SPIFFS_OBJ_NAME_LEN); data += SPIFFS_OBJ_NAME_LEN; - if (DBGLEVEL > 1) Dbprintf("> Filename received for spiffs WRITE : %s with APPEND SET TO : %d", filename, append); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("> Filename received for spiffs WRITE : %s with APPEND SET TO : %d", filename, append); + if (!append) { rdv40_spiffs_write((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); } else { @@ -1903,6 +2017,13 @@ static void PacketReceived(PacketCommandNG *packet) { LED_B_OFF(); break; } + case CMD_SPIFFS_WIPE: { + LED_B_ON(); + rdv40_spiffs_safe_wipe(); + reply_ng(CMD_SPIFFS_WIPE, PM3_SUCCESS, NULL, 0); + LED_B_OFF(); + break; + } case CMD_FLASHMEM_SET_SPIBAUDRATE: { if (packet->length != sizeof(uint32_t)) break; @@ -2060,6 +2181,8 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_STANDALONE: { + uint8_t *bb = BigBuf_get_EM_addr(); + bb[0] = packet->data.asBytes[0]; RunMod(); break; } diff --git a/armsrc/appmain.h b/armsrc/appmain.h index 594723983..092e88d0e 100644 --- a/armsrc/appmain.h +++ b/armsrc/appmain.h @@ -16,6 +16,8 @@ extern int g_rsamples; // = 0; extern uint8_t g_trigger; +int tearoff_hook(void); + // ADC Vref = 3300mV, and an (10M+1M):1M voltage divider on the HF input can measure voltages up to 36300 mV #define MAX_ADC_HF_VOLTAGE 36300 // ADC Vref = 3300mV, (240k-10M):240k voltage divider, 140800 mV diff --git a/armsrc/desfire_crypto.h b/armsrc/desfire_crypto.h index f8a99a991..bad89294b 100644 --- a/armsrc/desfire_crypto.h +++ b/armsrc/desfire_crypto.h @@ -69,8 +69,6 @@ enum DESFIRE_AUTH_SCHEME { AS_NEW }; - - #define DESFIRE_KEY(key) ((struct desfire_key *) key) struct desfire_key { enum DESFIRE_CRYPTOALGO type; diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index ddc297591..3f0ad9b6b 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -90,6 +90,7 @@ static em4x50_tag_t tag = { #define EM4X50_COMMAND_WRITE_PASSWORD 0x11 #define EM4X50_COMMAND_SELECTIVE_READ 0x0A +#define EM4X50_COMMAND_TIMEOUT 5000 #define FPGA_TIMER_0 0 int gHigh = 0; @@ -99,8 +100,9 @@ int gLow = 0; static void init_tag(void) { + // iceman: memset(tag.sectors, 0x00, sizeof)); + // initialize global tag structure - for (int i = 0; i < 34; i++) for (int j = 0; j < 7; j++) tag.sectors[i][j] = 0x00; @@ -109,14 +111,12 @@ static void init_tag(void) { static uint8_t bits2byte(uint8_t *bits, int length) { // converts separate bits into a single "byte" - uint8_t byte = 0; - for (int i = 0; i < length; i++) { byte |= bits[i]; - if (i != length-1) + if (i != length - 1) byte <<= 1; } @@ -124,11 +124,10 @@ static uint8_t bits2byte(uint8_t *bits, int length) { } static void msb2lsb_word(uint8_t *word) { - + // reorders given according to EM4x50 datasheet (msb -> lsb) - + uint8_t buff[4]; - buff[0] = reflect8(word[3]); buff[1] = reflect8(word[2]); buff[2] = reflect8(word[1]); @@ -141,27 +140,26 @@ static void msb2lsb_word(uint8_t *word) { } static void save_word(int pos, uint8_t bits[EM4X50_TAG_WORD]) { - + // split "raw" word into data, row and column parity bits and stop bit and // save them in global tag structure - uint8_t row_parity[4]; uint8_t col_parity[8]; - + // data and row parities for (int i = 0; i < 4; i++) { - tag.sectors[pos][i] = bits2byte(&bits[9*i],8); - row_parity[i] = bits[9*i+8]; + tag.sectors[pos][i] = bits2byte(&bits[9 * i], 8); + row_parity[i] = bits[9 * i + 8]; } - tag.sectors[pos][4] = bits2byte(row_parity,4); + tag.sectors[pos][4] = bits2byte(row_parity, 4); // column parities for (int i = 0; i < 8; i++) - col_parity[i] = bits[36+i]; + col_parity[i] = bits[36 + i]; + + tag.sectors[pos][5] = bits2byte(col_parity, 8); - tag.sectors[pos][5] = bits2byte(col_parity,8); - // stop bit tag.sectors[pos][6] = bits[44]; } @@ -169,7 +167,7 @@ static void save_word(int pos, uint8_t bits[EM4X50_TAG_WORD]) { static void wait_timer(int timer, uint32_t period) { // do nothing for using timer - + if (timer == FPGA_TIMER_0) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; @@ -184,23 +182,21 @@ static void wait_timer(int timer, uint32_t period) { } static void em4x50_setup_read(void) { - + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); // 50ms for the resonant antenna to settle. SpinDelay(50); + // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); - // start a 1.5ticks is 1us - StartTicks(); - + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); // Connect the A/D to the peak-detected low-frequency path. SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - + // Steal this pin from the SSP (SPI communication channel with fpga) and // use it to control the modulation AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; @@ -208,7 +204,7 @@ static void em4x50_setup_read(void) { // Disable modulation at default, which means enable the field LOW(GPIO_SSC_DOUT); - + // Enable Peripheral Clock for // TIMER_CLOCK0, used to measure exact timing before answering // TIMER_CLOCK1, used to capture edges of the tag frames @@ -221,17 +217,17 @@ static void em4x50_setup_read(void) { // TC0: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; - + // TC1: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; - + // Enable and reset counters AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - + // synchronized startup procedure while (AT91C_BASE_TC0->TC_CV > 0) {}; // wait until TC1 returned to zero - + // Watchdog hit WDT_HIT(); } @@ -239,21 +235,21 @@ static void em4x50_setup_read(void) { // functions for "reader" use case static bool get_signalproperties(void) { - + // calculate signal properties (mean amplitudes) from measured data: // 32 amplitudes (maximum values) -> mean amplitude value -> gHigh -> gLow - + bool signal_found = false; int no_periods = 32, pct = 75, noise = 140; - uint8_t sample = 0, sample_ref = 127; + uint8_t sample_ref = 127; uint8_t sample_max_mean = 0; uint8_t sample_max[no_periods]; uint32_t sample_max_sum = 0; + memcpy(sample_max, 0x00, sizeof(sample_max)); - // wait until signal/noise > 1 (max. 32 periods) for (int i = 0; i < T0 * no_periods; i++) { - + // about 2 samples per bit period wait_timer(0, T0 * EM4X50_T_TAG_HALF_PERIOD); @@ -261,10 +257,9 @@ static bool get_signalproperties(void) { signal_found = true; break; } - } - - if (!signal_found) + + if (signal_found == false) return false; // calculate mean maximum value of 32 periods, each period has a length of @@ -273,39 +268,36 @@ static bool get_signalproperties(void) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; while (AT91C_BASE_TC0->TC_CV < T0 * 3 * EM4X50_T_TAG_FULL_PERIOD) { - - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - + + volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (sample > sample_max[i]) sample_max[i] = sample; - + } - + sample_max_sum += sample_max[i]; } - + sample_max_mean = sample_max_sum / no_periods; - + // set global envelope variables gHigh = sample_ref + pct * (sample_max_mean - sample_ref) / 100; gLow = sample_ref - pct * (sample_max_mean - sample_ref) / 100; - return true; } static int get_next_bit(void) { - + // returns bit value (or EM4X50_BIT_OTHER -> no bit pattern) by evaluating // a single sample within a bit period (given there is no LIW, ACK or NAK) // This function is not used for decoding, it is only used for identifying // a listen window (return value = EM4X50_BIT_OTHER) in functions // "find_double_listen_window" and "check_ack" - - uint8_t sample; // get sample at 3/4 of bit period wait_timer(0, T0 * EM4X50_T_TAG_THREE_QUARTER_PERIOD); - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; // wait until end of bit period wait_timer(0, T0 * EM4X50_T_TAG_QUARTER_PERIOD); @@ -315,47 +307,58 @@ static int get_next_bit(void) { return EM4X50_BIT_0; else if (sample < gLow) return EM4X50_BIT_1; - + return EM4X50_BIT_OTHER; } static uint32_t get_pulse_length(void) { - - // iterates pulse length (low -> high -> low) - - uint8_t sample = 0; - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - - while (sample > gLow) +// Dbprintf( _CYAN_("4x50 get_pulse_length A") ); + + int32_t timeout = (T0 * 3 * EM4X50_T_TAG_FULL_PERIOD); + + // iterates pulse length (low -> high -> low) + + volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + + while (sample > gLow && (timeout--)) { sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + } + + if (timeout == 0) + return 0; AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + timeout = (T0 * 3 * EM4X50_T_TAG_FULL_PERIOD); - while (sample < gHigh) + while (sample < gHigh && (timeout--)) { sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + } - while (sample > gLow) + if (timeout == 0) + return 0; + + timeout = (T0 * 3 * EM4X50_T_TAG_FULL_PERIOD); + while (sample > gLow && (timeout--)) { sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + } + + if (timeout == 0) + return 0; return (uint32_t)AT91C_BASE_TC1->TC_CV; + } static bool check_pulse_length(uint32_t pl, int length) { - // check if pulse length corresponds to given length - - if ((pl >= T0 * (length - EM4X50_TAG_TOLERANCE)) & - (pl <= T0 * (length + EM4X50_TAG_TOLERANCE))) - return true; - else - return false; + return ((pl >= T0 * (length - EM4X50_TAG_TOLERANCE)) & (pl <= T0 * (length + EM4X50_TAG_TOLERANCE))); } static void em4x50_send_bit(int bit) { - + // send single bit according to EM4x50 application note and datasheet - + // reset clock for the next bit AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; @@ -370,13 +373,13 @@ static void em4x50_send_bit(int bit) { // half of bit period HIGH(GPIO_SSC_DOUT); while (AT91C_BASE_TC0->TC_CV < T0 * EM4X50_T_TAG_HALF_PERIOD); - + // disable modulation for second half of bit period LOW(GPIO_SSC_DOUT); while (AT91C_BASE_TC0->TC_CV < T0 * EM4X50_T_TAG_FULL_PERIOD); } else { - + // bit = "1" means disable modulation for full bit period LOW(GPIO_SSC_DOUT); while (AT91C_BASE_TC0->TC_CV < T0 * EM4X50_T_TAG_FULL_PERIOD); @@ -386,20 +389,20 @@ static void em4x50_send_bit(int bit) { static void em4x50_send_byte(uint8_t byte) { // send byte (without parity) - + for (int i = 0; i < 8; i++) - em4x50_send_bit((byte >> (7-i)) & 1); + em4x50_send_bit((byte >> (7 - i)) & 1); } static void em4x50_send_byte_with_parity(uint8_t byte) { // send byte followed by its (equal) parity bit - + int parity = 0, bit = 0; - + for (int i = 0; i < 8; i++) { - bit = (byte >> (7-i)) & 1; + bit = (byte >> (7 - i)) & 1; em4x50_send_bit(bit); parity ^= bit; } @@ -408,12 +411,12 @@ static void em4x50_send_byte_with_parity(uint8_t byte) { } static void em4x50_send_word(const uint8_t bytes[4]) { - + // send 32 bit word with parity bits according to EM4x50 datasheet - + for (int i = 0; i < 4; i++) em4x50_send_byte_with_parity(bytes[i]); - + // send column parities em4x50_send_byte(bytes[0] ^ bytes[1] ^ bytes[2] ^ bytes[3]); @@ -422,70 +425,66 @@ static void em4x50_send_word(const uint8_t bytes[4]) { } static bool find_single_listen_window(void) { - + // find single listen window - + int cnt_pulses = 0; - + while (cnt_pulses < EM4X50_T_WAITING_FOR_SNGLLIW) { - + // identification of listen window is done via evaluation of // pulse lengths if (check_pulse_length(get_pulse_length(), 3 * EM4X50_T_TAG_FULL_PERIOD)) { - + if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) { - + // listen window found return true; - } - } else { - - cnt_pulses++; - } + cnt_pulses++; } - + return false; } static bool find_double_listen_window(bool bcommand) { - + // find two successive listen windows that indicate the beginning of // data transmission // double listen window to be detected within 1600 pulses -> worst case // reason: first detectable double listen window after 34 words // -> 34 words + 34 single listen windows -> about 1600 pulses - + int cnt_pulses = 0; - + while (cnt_pulses < EM4X50_T_WAITING_FOR_DBLLIW) { - + // identification of listen window is done via evaluation of // pulse lengths if (check_pulse_length(get_pulse_length(), 3 * EM4X50_T_TAG_FULL_PERIOD)) { - + if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) { - + // first listen window found - + if (bcommand) { // data transmission from card has to be stopped, because // a commamd shall be issued - + // unfortunately the posititon in listen window (where // command request has to be sent) has gone, so if a // second window follows - sync on this to issue a command - + // skip the next bit... wait_timer(FPGA_TIMER_0, T0 * EM4X50_T_TAG_FULL_PERIOD); - + // ...and check if the following bit does make sense // (if not it is the correct position within the second // listen window) if (get_next_bit() == EM4X50_BIT_OTHER) { - + // send RM for request mode em4x50_send_bit(0); em4x50_send_bit(0); @@ -503,51 +502,46 @@ static bool find_double_listen_window(bool bcommand) { return true; } } - } else { cnt_pulses++; } } - + return false; } static bool find_em4x50_tag(void) { - + // function is used to check wether a tag on the proxmark is an // EM4x50 tag or not -> speed up "lf search" process - - return (find_single_listen_window()); - + return find_single_listen_window(); } static bool request_receive_mode(void) { - + // To issue a command we have to find a listen window first. // Because identification and sychronization at the same time is not // possible when using pulse lengths a double listen window is used. - bool bcommand = true; - return find_double_listen_window(bcommand); } static bool check_ack(bool bliw) { - + // returns true if signal structue corresponds to ACK, anything else is // counted as NAK (-> false) // Only relevant for pasword writing function: // If is true then within the single listen window right after the // ack signal a RM request has to be sent. - + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; while (AT91C_BASE_TC0->TC_CV < T0 * 4 * EM4X50_T_TAG_FULL_PERIOD) { - + if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) { - + // The received signal is either ACK or NAK. - + if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) { - + // Now the signal must be ACK. if (!bliw) { @@ -555,17 +549,17 @@ static bool check_ack(bool bliw) { return true; } else { - + // send RM request after ack signal // wait for 2 bits (remaining "bit" of ACK signal + first // "bit" of listen window) wait_timer(FPGA_TIMER_0, T0 * 2 * EM4X50_T_TAG_FULL_PERIOD); - + // check for listen window (if first bit cannot be inerpreted // as a valid bit it must belong to a listen window) if (get_next_bit() == EM4X50_BIT_OTHER) { - + // send RM for request mode em4x50_send_bit(0); em4x50_send_bit(0); @@ -574,7 +568,7 @@ static bool check_ack(bool bliw) { } } } else { - + // It's NAK -> stop searching break; } @@ -589,7 +583,7 @@ static int get_word_from_bitstream(uint8_t bits[EM4X50_TAG_WORD]) { // decodes one word by evaluating pulse lengths and previous bit; // word must have 45 bits in total: // 32 data bits + 4 row parity bits + 8 column parity bits + 1 stop bit - + bool bbitchange = false; int i = 0; uint32_t pl = 0; @@ -608,7 +602,7 @@ static int get_word_from_bitstream(uint8_t bits[EM4X50_TAG_WORD]) { bbitchange = true; } else { - + // pulse length = 2.5 bits[0] = 0; bits[1] = 1; @@ -618,35 +612,35 @@ static int get_word_from_bitstream(uint8_t bits[EM4X50_TAG_WORD]) { // identify remaining bits based on pulse lengths // between two listen windows only pulse lengths of 1, 1.5 and 2 are possible while (true) { - + i++; pl = get_pulse_length(); - + if (check_pulse_length(pl, EM4X50_T_TAG_FULL_PERIOD)) { // pulse length = 1 -> keep former bit value - bits[i] = bits[i-1]; + bits[i] = bits[i - 1]; } else if (check_pulse_length(pl, 3 * EM4X50_T_TAG_HALF_PERIOD)) { // pulse length = 1.5 -> decision on bit change - + if (bbitchange) { // if number of pulse lengths with 1.5 periods is even -> add bit - bits[i] = (bits[i-1] == 1) ? 1 : 0; + bits[i] = (bits[i - 1] == 1) ? 1 : 0; // pulse length of 1.5 changes bit value - bits[i+1] = (bits[i] == 1) ? 0 : 1; + bits[i + 1] = (bits[i] == 1) ? 0 : 1; i++; - + // next time add only one bit bbitchange = false; - + } else { - bits[i] = (bits[i-1] == 1) ? 0 : 1; - + bits[i] = (bits[i - 1] == 1) ? 0 : 1; + // next time two bits have to be added bbitchange = true; } @@ -655,7 +649,7 @@ static int get_word_from_bitstream(uint8_t bits[EM4X50_TAG_WORD]) { // pulse length of 2 means: adding 2 bits "01" bits[i] = 0; - bits[i+1] = 1; + bits[i + 1] = 1; i++; } else if (check_pulse_length(pl, 3 * EM4X50_T_TAG_FULL_PERIOD)) { @@ -663,7 +657,7 @@ static int get_word_from_bitstream(uint8_t bits[EM4X50_TAG_WORD]) { // pulse length of 3 indicates listen window -> clear last // bit (= 0) and return return --i; - + } } } @@ -676,8 +670,8 @@ static bool login(uint8_t password[4]) { // simple login to EM4x50, // used in operations that require authentication - - if (request_receive_mode ()) { + + if (request_receive_mode()) { // send login command em4x50_send_byte_with_parity(EM4X50_COMMAND_LOGIN); @@ -688,12 +682,12 @@ static bool login(uint8_t password[4]) { // check if ACK is returned if (check_ack(false)) return true; - + } else { - if (DBGLEVEL >= DBG_DEBUG) - Dbprintf("error in command request"); + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("error in command request"); } - + return false; } @@ -704,18 +698,18 @@ static bool login(uint8_t password[4]) { static bool reset(void) { // resets EM4x50 tag (used by write function) - - if (request_receive_mode ()) { + + if (request_receive_mode()) { // send login command em4x50_send_byte_with_parity(EM4X50_COMMAND_RESET); - + if (check_ack(false)) return true; } else { - if (DBGLEVEL >= DBG_DEBUG) - Dbprintf("error in command request"); + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("error in command request"); } return false; @@ -726,10 +720,10 @@ static bool reset(void) { //============================================================================== static bool standard_read(int *now) { - + // reads data that tag transmits when exposed to reader field // (standard read mode); number of read words is saved in - + int fwr = *now; uint8_t bits[EM4X50_TAG_WORD] = {0}; @@ -742,23 +736,23 @@ static bool standard_read(int *now) { // number of detected words *now -= fwr; - + return true; } else { - if (DBGLEVEL >= DBG_DEBUG) - Dbprintf("didn't find a listen window"); + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("didn't find a listen window"); } return false; } static bool selective_read(uint8_t addresses[4]) { - + // reads from "first word read" (fwr = addresses[3]) to "last word read" // (lwr = addresses[2]) // result is verified by "standard read mode" - + int fwr = addresses[3]; // first word read int lwr = addresses[2]; // last word read int now = fwr; // number of words @@ -773,26 +767,26 @@ static bool selective_read(uint8_t addresses[4]) { // look for ACK sequence if (check_ack(false)) - + // save and verify via standard read mode (compare number of words) if (standard_read(&now)) if (now == (lwr - fwr + 1)) return true; - + } else { - if (DBGLEVEL >= DBG_DEBUG) - Dbprintf("error in command request"); + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("error in command request"); } return false; } void em4x50_info(em4x50_data_t *etd) { - + // collects as much information as possible via selective read mode // if no password is given -> try with standard password "0x00000000" // otherwise continue without login - + bool bsuccess = false, blogin = false; uint8_t status = 0; uint8_t addresses[] = {0x00, 0x00, 0x21, 0x00}; // fwr = 0, lwr = 33 @@ -810,28 +804,28 @@ void em4x50_info(em4x50_data_t *etd) { blogin = login(etd->password); } else { - + // if no password is given, try to login with "0x00000000" blogin = login(password); } - + bsuccess = selective_read(addresses); } status = (bsuccess << 1) + blogin; - + lf_finalize(); reply_ng(CMD_ACK, status, (uint8_t *)tag.sectors, 238); } void em4x50_read(em4x50_data_t *etd) { - + // reads in two different ways: // - using "selective read mode" -> bidirectional communication // - using "standard read mode" -> unidirectional communication (read // data that tag transmits "voluntarily") - + bool bsuccess = false, blogin = false; int now = 0; uint8_t status = 0; @@ -839,32 +833,32 @@ void em4x50_read(em4x50_data_t *etd) { init_tag(); em4x50_setup_read(); - + // set gHigh and gLow if (get_signalproperties() && find_em4x50_tag()) { - + if (etd->addr_given) { // selective read mode - + // try to login with given password if (etd->pwd_given) blogin = login(etd->password); - + // only one word has to be read -> first word read = last word read addresses[2] = addresses[3] = etd->address; bsuccess = selective_read(addresses); - + } else { - + // standard read mode bsuccess = standard_read(&now); - + } } - + status = (now << 2) + (bsuccess << 1) + blogin; - + lf_finalize(); reply_ng(CMD_ACK, status, (uint8_t *)tag.sectors, 238); } @@ -876,7 +870,7 @@ void em4x50_read(em4x50_data_t *etd) { static bool write(uint8_t word[4], uint8_t address) { // writes to specified
- + if (request_receive_mode()) { // send write command @@ -902,8 +896,8 @@ static bool write(uint8_t word[4], uint8_t address) { } } else { - if (DBGLEVEL >= DBG_DEBUG) - Dbprintf("error in command request"); + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("error in command request"); } return false; @@ -912,7 +906,7 @@ static bool write(uint8_t word[4], uint8_t address) { static bool write_password(uint8_t password[4], uint8_t new_password[4]) { // changes password from to - + if (request_receive_mode()) { // send write password command @@ -941,36 +935,36 @@ static bool write_password(uint8_t password[4], uint8_t new_password[4]) { } } else { - if (DBGLEVEL >= DBG_DEBUG) - Dbprintf("error in command request"); + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("error in command request"); } return false; } void em4x50_write(em4x50_data_t *etd) { - + // write operation process for EM4x50 tag, // single word is written to given address, verified by selective read operation - + bool bsuccess = false, blogin = false; uint8_t status = 0; uint8_t word[4] = {0x00, 0x00, 0x00, 0x00}; uint8_t addresses[4] = {0x00, 0x00, 0x00, 0x00}; - + init_tag(); em4x50_setup_read(); - + // set gHigh and gLow if (get_signalproperties() && find_em4x50_tag()) { - + // reorder word according to datasheet msb2lsb_word(etd->word); - + // if password is given try to login first if (etd->pwd_given) blogin = login(etd->password); - + // write word to given address if (write(etd->word, etd->address)) { @@ -991,7 +985,7 @@ void em4x50_write(em4x50_data_t *etd) { word[2] = tag.sectors[etd->address][2]; word[3] = tag.sectors[etd->address][3]; msb2lsb_word(word); - + bsuccess = true; for (int i = 0; i < 4; i++) bsuccess &= (word[i] == etd->word[i]) ? true : false; @@ -1002,20 +996,20 @@ void em4x50_write(em4x50_data_t *etd) { } status = (bsuccess << 1) + blogin; - + lf_finalize(); reply_ng(CMD_ACK, status, (uint8_t *)tag.sectors, 238); } void em4x50_write_password(em4x50_data_t *etd) { - + // sinmple change of password - + bool bsuccess = false; - + init_tag(); em4x50_setup_read(); - + // set gHigh and gLow if (get_signalproperties() && find_em4x50_tag()) { @@ -1028,3 +1022,61 @@ void em4x50_write_password(em4x50_data_t *etd) { lf_finalize(); reply_ng(CMD_ACK, bsuccess, 0, 0); } + +void em4x50_wipe(em4x50_data_t *etd) { + + // set all data of EM4x50 tag to 0x0 including password + + bool bsuccess = false; + uint8_t zero[4] = {0, 0, 0, 0}; + uint8_t addresses[4] = {0, 0, EM4X50_NO_WORDS - 3, 1}; + + init_tag(); + em4x50_setup_read(); + + // set gHigh and gLow + if (get_signalproperties() && find_em4x50_tag()) { + + // login first + if (login(etd->password)) { + + // write 0x0 to each address but ignore addresses + // 0 -> password, 32 -> serial, 33 -> uid + // writing 34 words takes about 3.6 seconds -> high timeout needed + for (int i = 1; i <= EM4X50_NO_WORDS - 3; i++) + write(zero, i); + + // to verify result reset EM4x50 + if (reset()) { + + // login not necessary because protectd word has been set to 0 + // -> no read protected words + // -> selective read can be called immediately + if (selective_read(addresses)) { + + // check if everything is zero + bsuccess = true; + for (int i = 1; i <= EM4X50_NO_WORDS - 3; i++) + for (int j = 0; j < 4; j++) + bsuccess &= (tag.sectors[i][j] == 0) ? true : false; + + } + + if (bsuccess) { + + // so far everything is fine + // last task: reset password + if (login(etd->password)) + bsuccess = write_password(etd->password, zero); + + // verify by login with new password + if (bsuccess) + bsuccess = login(zero); + } + } + } + } + + lf_finalize(); + reply_ng(CMD_ACK, bsuccess, (uint8_t *)tag.sectors, 238); +} diff --git a/armsrc/em4x50.h b/armsrc/em4x50.h index d786e61c0..f9f1375f2 100644 --- a/armsrc/em4x50.h +++ b/armsrc/em4x50.h @@ -21,5 +21,6 @@ void em4x50_info(em4x50_data_t *etd); void em4x50_write(em4x50_data_t *etd); void em4x50_write_password(em4x50_data_t *etd); void em4x50_read(em4x50_data_t *etd); +void em4x50_wipe(em4x50_data_t *etd); #endif /* EM4X50_H */ diff --git a/armsrc/epa.c b/armsrc/epa.c index cdc83fe5d..2a0025610 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -113,17 +113,14 @@ static char iso_type = 0; //----------------------------------------------------------------------------- // Wrapper for sending APDUs to type A and B cards //----------------------------------------------------------------------------- -static int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response) { +static int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response, uint16_t respmaxlen) { switch (iso_type) { case 'a': return iso14_apdu(apdu, (uint16_t) length, false, response, NULL); - break; case 'b': - return iso14443b_apdu(apdu, length, response); - break; + return iso14443b_apdu(apdu, length, false, response, respmaxlen, NULL); default: return 0; - break; } } @@ -149,9 +146,8 @@ 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) { +size_t EPA_Parse_CardAccess(uint8_t *data, size_t length, pace_version_info_t *pace_info) { + size_t index = 0; while (index <= length - 2) { @@ -165,19 +161,22 @@ size_t EPA_Parse_CardAccess(uint8_t *data, index += (data[index - 1] & 0x7F); } } + // OID else if (data[index] == 0x06) { // is this a PACE OID? if (data[index + 1] == 0x0A // length matches - && memcmp(data + index + 2, - oid_pace_start, - sizeof(oid_pace_start)) == 0 // content matches + && memcmp(data + index + 2, oid_pace_start, sizeof(oid_pace_start)) == 0 // content matches && pace_info != NULL) { + // first, clear the pace_info struct memset(pace_info, 0, sizeof(pace_version_info_t)); + memcpy(pace_info->oid, data + index + 2, sizeof(pace_info->oid)); + // a PACE OID is followed by the version index += data[index + 1] + 2; + if (data[index] == 02 && data[index + 1] == 01) { pace_info->version = data[index + 2]; index += 3; @@ -189,6 +188,7 @@ size_t EPA_Parse_CardAccess(uint8_t *data, pace_info->parameter_id = data[index + 2]; index += 3; } + } else { // skip this OID index += 2 + data[index + 1]; @@ -222,12 +222,14 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) { // 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) uint8_t response_apdu[262]; - int rapdu_length = 0; // select the file EF.CardAccess - rapdu_length = EPA_APDU((uint8_t *)apdu_select_binary_cardaccess, - sizeof(apdu_select_binary_cardaccess), - response_apdu); + int rapdu_length = EPA_APDU((uint8_t *)apdu_select_binary_cardaccess, + sizeof(apdu_select_binary_cardaccess), + response_apdu, + sizeof(response_apdu) + ); + if (rapdu_length < 6 || response_apdu[rapdu_length - 4] != 0x90 || response_apdu[rapdu_length - 3] != 0x00) { @@ -238,7 +240,10 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) { // read the file rapdu_length = EPA_APDU((uint8_t *)apdu_read_binary, sizeof(apdu_read_binary), - response_apdu); + response_apdu, + sizeof(response_apdu) + ); + if (rapdu_length <= 6 || response_apdu[rapdu_length - 4] != 0x90 || response_apdu[rapdu_length - 3] != 0x00) { @@ -248,22 +253,22 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) { // copy the content into the buffer // length of data available: apdu_length - 4 (ISO frame) - 2 (SW) - size_t to_copy = rapdu_length - 6; - to_copy = to_copy < max_length ? to_copy : max_length; - memcpy(buffer, response_apdu + 2, to_copy); - return to_copy; + size_t len = rapdu_length - 6; + len = len < max_length ? len : max_length; + memcpy(buffer, response_apdu + 2, len); + return len; } //----------------------------------------------------------------------------- // Abort helper function for EPA_PACE_Collect_Nonce // sets relevant data in ack, sends the response //----------------------------------------------------------------------------- -static void EPA_PACE_Collect_Nonce_Abort(uint8_t step, int func_return) { +static void EPA_PACE_Collect_Nonce_Abort(uint32_t cmd, uint8_t step, int func_return) { // power down the field EPA_Finish(); // send the USB packet - reply_mix(CMD_ACK, step, func_return, 0, 0, 0); + reply_mix(cmd, step, func_return, 0, 0, 0); } //----------------------------------------------------------------------------- @@ -283,28 +288,28 @@ void EPA_PACE_Collect_Nonce(PacketCommandNG *c) { // set up communication int func_return = EPA_Setup(); if (func_return != 0) { - EPA_PACE_Collect_Nonce_Abort(1, func_return); + EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_COLLECT_NONCE, 1, func_return); return; } // read the CardAccess file // this array will hold the CardAccess file uint8_t card_access[256] = {0}; - int card_access_length = EPA_Read_CardAccess(card_access, 256); + int cardlen = EPA_Read_CardAccess(card_access, 256); // the response has to be at least this big to hold the OID - if (card_access_length < 18) { - EPA_PACE_Collect_Nonce_Abort(2, card_access_length); + if (cardlen < 18) { + EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_COLLECT_NONCE, 2, cardlen); return; } // this will hold the PACE info of the card pace_version_info_t pace_version_info; + // search for the PACE OID - func_return = EPA_Parse_CardAccess(card_access, - card_access_length, - &pace_version_info); + func_return = EPA_Parse_CardAccess(card_access, cardlen, &pace_version_info); + if (func_return != 0 || pace_version_info.version == 0) { - EPA_PACE_Collect_Nonce_Abort(3, func_return); + EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_COLLECT_NONCE, 3, func_return); return; } @@ -313,25 +318,29 @@ void EPA_PACE_Collect_Nonce(PacketCommandNG *c) { func_return = EPA_PACE_MSE_Set_AT(pace_version_info, 2); // check if the command succeeded if (func_return != 0) { - EPA_PACE_Collect_Nonce_Abort(4, func_return); + EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_COLLECT_NONCE, 4, func_return); return; } // now get the nonce uint8_t nonce[256] = {0}; - uint8_t requested_size = (uint8_t)c->oldarg[0]; - func_return = EPA_PACE_Get_Nonce(requested_size, nonce); + + struct p { + uint32_t m; + } PACKED; + struct p *packet = (struct p *)c->data.asBytes; + + func_return = EPA_PACE_Get_Nonce(packet->m, nonce); // check if the command succeeded if (func_return < 0) { - EPA_PACE_Collect_Nonce_Abort(5, func_return); + EPA_PACE_Collect_Nonce_Abort(CMD_HF_EPA_COLLECT_NONCE, 5, func_return); return; } - // all done, return EPA_Finish(); // save received information - reply_mix(CMD_ACK, 0, func_return, 0, nonce, func_return); + reply_mix(CMD_HF_EPA_COLLECT_NONCE, 0, func_return, 0, nonce, func_return); } //----------------------------------------------------------------------------- @@ -343,21 +352,18 @@ void EPA_PACE_Collect_Nonce(PacketCommandNG *c) { // code on failure. //----------------------------------------------------------------------------- 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]; + // copy the constant part - memcpy(apdu, - apdu_general_authenticate_pace_get_nonce, - sizeof(apdu_general_authenticate_pace_get_nonce)); + memcpy(apdu, apdu_general_authenticate_pace_get_nonce, sizeof(apdu_general_authenticate_pace_get_nonce)); + // append Le (requested length + 2 due to tag/length taking 2 bytes) in RAPDU apdu[sizeof(apdu_general_authenticate_pace_get_nonce)] = requested_length + 4; - // send it uint8_t response_apdu[262]; - int send_return = EPA_APDU(apdu, - sizeof(apdu), - response_apdu); - // check if the command succeeded + int send_return = EPA_APDU(apdu, sizeof(apdu), response_apdu, sizeof(response_apdu)); if (send_return < 6 || response_apdu[send_return - 4] != 0x90 || response_apdu[send_return - 3] != 0x00) { @@ -387,26 +393,31 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) { int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password) { // create the MSE: Set AT APDU uint8_t apdu[23]; + // the minimum length (will be increased as more data is added) size_t apdu_length = 20; + // copy the constant part - memcpy(apdu, - apdu_mse_set_at_start, - sizeof(apdu_mse_set_at_start)); + memcpy(apdu, apdu_mse_set_at_start, sizeof(apdu_mse_set_at_start)); + // type: OID apdu[5] = 0x80; + // length of the OID apdu[6] = sizeof(pace_version_info.oid); + // copy the OID - memcpy(apdu + 7, - pace_version_info.oid, - sizeof(pace_version_info.oid)); + memcpy(apdu + 7, pace_version_info.oid, sizeof(pace_version_info.oid)); + // type: password apdu[17] = 0x83; + // length: 1 apdu[18] = 1; + // password apdu[19] = password; + // if standardized domain parameters are used, copy the ID if (pace_version_info.parameter_id != 0) { apdu_length += 3; @@ -417,17 +428,23 @@ int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password) // copy the parameter ID apdu[22] = pace_version_info.parameter_id; } + // now set Lc to the actual length apdu[4] = apdu_length - 5; + // send it uint8_t response_apdu[6]; - int send_return = EPA_APDU(apdu, - apdu_length, - response_apdu); + int send_return = EPA_APDU(apdu, apdu_length, response_apdu, sizeof(response_apdu)); + + Dbprintf("send ret %d bytes", send_return); + +// Dbhexdump(send_return, response_apdu, false); + // check if the command succeeded - if (send_return != 6 - || response_apdu[send_return - 4] != 0x90 - || response_apdu[send_return - 3] != 0x00) { + if (send_return != 6) +// && response_apdu[send_return - 4] != 0x90 +// || response_apdu[send_return - 3] != 0x00) + { return 1; } return 0; @@ -480,7 +497,9 @@ void EPA_PACE_Replay(PacketCommandNG *c) { StartCountUS(); func_return = EPA_APDU(apdus_replay[i].data, apdu_lengths_replay[i], - response_apdu); + response_apdu, + sizeof(response_apdu) + ); timings[i] = GetCountUS(); // every step but the last one should succeed if (i < ARRAYLEN(apdu_lengths_replay) - 1 @@ -536,6 +555,6 @@ int EPA_Setup(void) { iso_type = 'b'; return 0; } - Dbprintf("No card found."); + Dbprintf("No card found"); return 1; } diff --git a/armsrc/felica.c b/armsrc/felica.c index f8375f0cc..aafdae5d8 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -9,7 +9,7 @@ #include "commonutil.h" #include "dbprint.h" #include "ticks.h" -#include "mifare.h" +#include "iso18.h" // FeliCa timings // minimum time between the start bits of consecutive transfers from reader to tag: 6800 carrier (13.56MHz) cycles @@ -336,7 +336,7 @@ static void BuildFliteRdblk(uint8_t *idm, int blocknum, uint16_t *blocks) { } static void TransmitFor18092_AsReader(uint8_t *frame, int len, uint32_t *timing, uint8_t power, uint8_t highspeed) { - uint8_t flags = FPGA_MAJOR_MODE_HF_ISO18092; + uint16_t flags = FPGA_MAJOR_MODE_HF_ISO18092; if (power) flags |= FPGA_HF_ISO18092_FLAG_READER; if (highspeed) @@ -445,7 +445,7 @@ static void iso18092_setup(uint8_t fpga_minor_mode) { if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Start iso18092_setup"); LEDsoff(); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF_FELICA); // allocate command receive buffer BigBuf_free(); @@ -464,7 +464,7 @@ static void iso18092_setup(uint8_t fpga_minor_mode) { SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO18092); // LSB transfer. Remember to set it back to MSB with AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index f1f157eec..2df952214 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -134,7 +134,7 @@ void SetupSpi(int mode) { // Set up the synchronous serial port with the set of options that fits // the FPGA mode. Both RX and TX are always enabled. //----------------------------------------------------------------------------- -void FpgaSetupSsc(void) { +void FpgaSetupSsc(uint16_t fpga_mode) { // First configure the GPIOs, and get ourselves a clock. AT91C_BASE_PIOA->PIO_ASR = GPIO_SSC_FRAME | @@ -152,12 +152,16 @@ void FpgaSetupSsc(void) { // data and frame signal is sampled on falling edge of RK AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(1) | SSC_CLOCK_MODE_START(1); - // 8 bits per transfer, no loopback, MSB first, 1 transfer per sync + // 8, 16 or 32 bits per transfer, no loopback, MSB first, 1 transfer per sync // pulse, no output sync - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + if ((fpga_mode & FPGA_MAJOR_MODE_MASK) == FPGA_MAJOR_MODE_HF_READER && FpgaGetCurrent() == FPGA_BITSTREAM_HF) { + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + } else { + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + } - // TX clock comes from TK pin, no clock output, outputs change on falling - // edge of TK, frame sync is sampled on rising edge of TK, start TX on rising edge of TF + // TX clock comes from TK pin, no clock output, outputs change on rising edge of TK, + // TF (frame sync) is sampled on falling edge of TK, start TX on rising edge of TF AT91C_BASE_SSC->SSC_TCMR = SSC_CLOCK_MODE_SELECT(2) | SSC_CLOCK_MODE_START(5); // tx framing is the same as the rx framing @@ -171,7 +175,7 @@ void FpgaSetupSsc(void) { // a single buffer as a circular buffer (so that we just chain back to // ourselves, not to another buffer). //----------------------------------------------------------------------------- -bool FpgaSetupSscDma(uint8_t *buf, int len) { +bool FpgaSetupSscDma(uint8_t *buf, uint16_t len) { if (buf == NULL) return false; FpgaDisableSscDma(); @@ -184,8 +188,7 @@ bool FpgaSetupSscDma(uint8_t *buf, int len) { } //---------------------------------------------------------------------------- -// Uncompress (inflate) the FPGA data. Returns one decompressed byte with -// each call. +// Uncompress (inflate) the FPGA data. Returns one decompressed byte with each call. //---------------------------------------------------------------------------- static int get_from_fpga_combined_stream(lz4_streamp compressed_fpga_stream, uint8_t *output_buffer) { if (fpga_image_ptr == output_buffer + FPGA_RING_BUFFER_BYTES) { // need more data @@ -347,8 +350,10 @@ static void DownloadFPGA(int bitstream_version, int FpgaImageLen, lz4_streamp co * length. */ static int bitparse_find_section(int bitstream_version, char section_name, uint32_t *section_length, lz4_streamp compressed_fpga_stream, uint8_t *output_buffer) { - int result = 0; + #define MAX_FPGA_BIT_STREAM_HEADER_SEARCH 100 // maximum number of bytes to search for the requested section + + int result = 0; uint16_t numbytes = 0; while (numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH) { char current_name = get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); @@ -439,7 +444,7 @@ void FpgaDownloadAndGo(int bitstream_version) { // The bit format is: C3 C2 C1 C0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 // where C is the 4 bit command and D is the 12 bit data // -// @params cmd and v gets or over eachother. Take careful note of overlapping bits. +// @params cmd and v gets OR:ED over each other. Take careful note of overlapping bits. //----------------------------------------------------------------------------- void FpgaSendCommand(uint16_t cmd, uint16_t v) { SetupSpi(SPI_FPGA_MODE); @@ -520,10 +525,13 @@ int FpgaGetCurrent(void) { // if HF, Disable SSC DMA // turn off trace and leds off. void switch_off(void) { - if (DBGLEVEL > 3) Dbprintf("switch_off"); + if (DBGLEVEL > 3) { + Dbprintf("switch_off"); + } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - if (downloaded_bitstream == FPGA_BITSTREAM_HF) + if (downloaded_bitstream == FPGA_BITSTREAM_HF) { FpgaDisableSscDma(); + } set_tracing(false); LEDsoff(); } diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 636c5b7b7..530dc21b4 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -20,6 +20,7 @@ // definitions for multiple FPGA config files support #define FPGA_BITSTREAM_LF 1 #define FPGA_BITSTREAM_HF 2 +#define FPGA_BITSTREAM_HF_FELICA 3 /* Communication between ARM / FPGA is done inside armsrc/fpgaloader.c (function FpgaSendCommand) @@ -52,23 +53,26 @@ thres| x x x x x x x x #define FPGA_CMD_TRACE_ENABLE (2<<12) // C // Definitions for the FPGA configuration word. +#define FPGA_MAJOR_MODE_MASK 0x01C0 +#define FPGA_MINOR_MODE_MASK 0x003F + // LF -#define FPGA_MAJOR_MODE_LF_READER (0<<5) -#define FPGA_MAJOR_MODE_LF_EDGE_DETECT (1<<5) -#define FPGA_MAJOR_MODE_LF_PASSTHRU (2<<5) -#define FPGA_MAJOR_MODE_LF_ADC (3<<5) +#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_TX (0<<5) // D -#define FPGA_MAJOR_MODE_HF_READER_RX_XCORR (1<<5) // D -#define FPGA_MAJOR_MODE_HF_SIMULATOR (2<<5) // D -#define FPGA_MAJOR_MODE_HF_ISO14443A (3<<5) // D -#define FPGA_MAJOR_MODE_HF_SNOOP (4<<5) // D -#define FPGA_MAJOR_MODE_HF_ISO18092 (5<<5) // D -#define FPGA_MAJOR_MODE_HF_GET_TRACE (6<<5) // D +#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 // BOTH HF / LF -#define FPGA_MAJOR_MODE_OFF (7<<5) // D +#define FPGA_MAJOR_MODE_OFF (7<<6) // D + // Options for LF_READER #define FPGA_LF_ADC_READER_FIELD 0x1 @@ -78,13 +82,20 @@ thres| x x x x x x x x #define FPGA_LF_EDGE_DETECT_READER_FIELD 0x1 #define FPGA_LF_EDGE_DETECT_TOGGLE_MODE 0x2 -// Options for the HF reader, tx to tag -#define FPGA_HF_READER_TX_SHALLOW_MOD 0x1 +// 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 HF reader, correlating against rx from tag -#define FPGA_HF_READER_RX_XCORR_848_KHZ 0x1 -#define FPGA_HF_READER_RX_XCORR_SNOOP 0x2 -#define FPGA_HF_READER_RX_XCORR_QUARTER 0x4 +#define FPGA_HF_READER_SUBCARRIER_848_KHZ (0<<4) +#define FPGA_HF_READER_SUBCARRIER_424_KHZ (1<<4) +#define FPGA_HF_READER_SUBCARRIER_212_KHZ (2<<4) // Options for the HF simulated tag, how to modulate #define FPGA_HF_SIMULATOR_NO_MODULATION 0x0 // 0000 @@ -112,9 +123,9 @@ void FpgaEnableTracing(void); void FpgaDisableTracing(void); void FpgaDownloadAndGo(int bitstream_version); // void FpgaGatherVersion(int bitstream_version, char *dst, int len); -void FpgaSetupSsc(void); +void FpgaSetupSsc(uint16_t fpga_mode); void SetupSpi(int mode); -bool FpgaSetupSscDma(uint8_t *buf, int len); +bool FpgaSetupSscDma(uint8_t *buf, uint16_t len); void Fpga_print_status(void); int FpgaGetCurrent(void); void SetAdcMuxFor(uint32_t whichGpio); diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index 177a1f99e..4e4539f3e 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -41,18 +41,18 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len) { SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SNIFF); // Setting Frame Mode For better performance on high speed data transfer. AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SNOOP); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SNIFF); SpinDelay(100); *len = (BigBuf_max_traceLen() & 0xFFFE); uint8_t *mem = BigBuf_malloc(*len); - uint32_t trigger_cnt = 0; + uint32_t trigger_cnt = 0; uint16_t r = 0, interval = 0; bool pressed = false; @@ -77,7 +77,7 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len) { // 180 (0xB4) arbitary value to see if a strong RF field is near. if (r > 180) { - + if (++trigger_cnt > triggersToSkip) { break; } @@ -97,9 +97,9 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len) { } } - optimizedSniff((uint16_t*)mem, *len); + optimizedSniff((uint16_t *)mem, *len); - if (DBGLEVEL >= DBG_INFO) { + if (DBGLEVEL >= DBG_INFO) { Dbprintf("Trigger kicked in (%d >= 180)", r); Dbprintf("Collected %u samples", *len); } @@ -114,17 +114,18 @@ int HfSniff(uint32_t samplesToSkip, uint32_t triggersToSkip, uint16_t *len) { } void HfPlotDownload(void) { - uint8_t *buf = ToSend; - uint8_t *this_buf = buf; + + tosend_t *ts = get_tosend(); + uint8_t *this_buf = ts->buf; FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_GET_TRACE); AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; // Disable DMA Transfer AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) this_buf; // start transfer to this memory address AT91C_BASE_PDC_SSC->PDC_RCR = PM3_CMD_DATA_SIZE; // transfer this many samples - buf[0] = (uint8_t)AT91C_BASE_SSC->SSC_RHR; // clear receive register + ts->buf[0] = (uint8_t)AT91C_BASE_SSC->SSC_RHR; // clear receive register AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // Start DMA transfer FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_GET_TRACE); // let FPGA transfer its internal Block-RAM @@ -132,7 +133,7 @@ void HfPlotDownload(void) { LED_B_ON(); for (size_t i = 0; i < FPGA_TRACE_SIZE; i += PM3_CMD_DATA_SIZE) { // prepare next DMA transfer: - uint8_t *next_buf = buf + ((i + PM3_CMD_DATA_SIZE) % (2 * PM3_CMD_DATA_SIZE)); + uint8_t *next_buf = ts->buf + ((i + PM3_CMD_DATA_SIZE) % (2 * PM3_CMD_DATA_SIZE)); AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)next_buf; AT91C_BASE_PDC_SSC->PDC_RNCR = PM3_CMD_DATA_SIZE; diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 79b09395f..66b3f4573 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -130,6 +130,8 @@ static int hitag2_init(void) { #define HITAG_T_WAIT_2_MIN 90 /* T_wait2 should be at least 90 */ #define HITAG_T_WAIT_MAX 300 /* bit more than HITAG_T_WAIT_1 + HITAG_T_WAIT_2 */ #define HITAG_T_PROG 614 +#define HITAG_T_WAIT_POWERUP 313 /* transponder internal powerup time is 312.5 */ +#define HITAG_T_WAIT_START_AUTH_MAX 232 /* transponder waiting time to receive the START_AUTH command is 232.5, then it enters public mode */ #define HITAG_T_TAG_ONE_HALF_PERIOD 10 #define HITAG_T_TAG_TWO_HALF_PERIOD 25 @@ -1002,7 +1004,7 @@ void SniffHitag2(void) { size_t periods = 0; uint8_t periods_bytes[4]; - // int16_t checked = 0; +// int16_t checked = 0; /*bool waiting_for_first_edge = true;*/ LED_C_ON(); @@ -1015,18 +1017,18 @@ void SniffHitag2(void) { WDT_HIT(); -/* - // only every 1000th times, in order to save time when collecting samples. - if (checked == 1000) { - if (data_available()) { - checked = -1; - break; - } else { - checked = 0; - } - } - ++checked; - */ + /* + // only every 1000th times, in order to save time when collecting samples. + if (checked == 1000) { + if (data_available()) { + checked = -1; + break; + } else { + checked = 0; + } + } + ++checked; + */ // Receive frame, watch for at most T0*EOF periods @@ -1431,13 +1433,14 @@ void ReaderHitag(hitag_function htf, hitag_data *htd) { // init as reader lf_init(true, false); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); uint8_t tag_modulation; size_t max_nrzs = (8 * HITAG_FRAME_LEN + 5) * 2; // up to 2 nrzs per bit uint8_t nrz_samples[max_nrzs]; + bool turn_on = true; size_t nrzs = 0; int16_t checked = 0; - uint32_t signal_size = 10000; while (bStop == false && BUTTON_PRESS() == false) { @@ -1487,6 +1490,7 @@ void ReaderHitag(hitag_function htf, hitag_data *htd) { } case RHT2F_UID_ONLY: { bStop = !hitag2_read_uid(rx, rxlen, tx, &txlen); + if (bSuccessful) bStop = true; attempt_count++; //attempt 3 times to get uid then quit if (!bStop && attempt_count == 3) bStop = true; @@ -1498,11 +1502,20 @@ void ReaderHitag(hitag_function htf, hitag_data *htd) { goto out; } } - - // Wait for t_wait_2 carrier periods after the last tag bit before transmitting, - lf_wait_periods(t_wait_2); - command_start += t_wait_2; - + if (bStop) break; + if (turn_on) { + // Wait 50ms with field off to be sure the transponder gets reset + SpinDelay(50); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + turn_on = false; + // Wait with field on to be in "Wait for START_AUTH" timeframe + lf_wait_periods(HITAG_T_WAIT_POWERUP + HITAG_T_WAIT_START_AUTH_MAX / 4); + command_start += HITAG_T_WAIT_POWERUP + HITAG_T_WAIT_START_AUTH_MAX / 4; + } else { + // Wait for t_wait_2 carrier periods after the last tag bit before transmitting, + lf_wait_periods(t_wait_2); + command_start += t_wait_2; + } // Transmit the reader frame command_duration = hitag_reader_send_frame(tx, txlen); response_start = command_start + command_duration; @@ -1734,6 +1747,7 @@ void WriterHitag(hitag_function htf, hitag_data *htd, int page) { // init as reader lf_init(true, false); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // Tag specific configuration settings (sof, timings, etc.) // TODO HTS @@ -1767,9 +1781,10 @@ void WriterHitag(hitag_function htf, hitag_data *htd, int page) { size_t max_nrzs = (8 * HITAG_FRAME_LEN + 5) * 2; // up to 2 nrzs per bit uint8_t nrz_samples[max_nrzs]; size_t nrzs = 0; - int16_t checked = 0; uint32_t signal_size = 10000; + bool turn_on = true; + while (bStop == false && BUTTON_PRESS() == false) { // use malloc @@ -1805,9 +1820,20 @@ void WriterHitag(hitag_function htf, hitag_data *htd, int page) { } } - // Wait for t_wait_2 carrier periods after the last tag bit before transmitting, - lf_wait_periods(t_wait_2); - command_start += t_wait_2; + if (bStop) break; + if (turn_on) { + // Wait 50ms with field off to be sure the transponder gets reset + SpinDelay(50); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + turn_on = false; + // Wait with field on to be in "Wait for START_AUTH" timeframe + lf_wait_periods(HITAG_T_WAIT_POWERUP + HITAG_T_WAIT_START_AUTH_MAX / 4); + command_start += HITAG_T_WAIT_POWERUP + HITAG_T_WAIT_START_AUTH_MAX / 4; + } else { + // Wait for t_wait_2 carrier periods after the last tag bit before transmitting, + lf_wait_periods(t_wait_2); + command_start += t_wait_2; + } // Transmit the reader frame command_duration = hitag_reader_send_frame(tx, txlen); diff --git a/armsrc/i2c.c b/armsrc/i2c.c index 23ae07f10..1ca742893 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -225,7 +225,7 @@ static bool I2C_WaitForSim(void) { // 8051 speaks with smart card. // 1000*50*3.07 = 153.5ms // 1byte transfer == 1ms with max frame being 256bytes - if (!WaitSCL_H_delay(20 * 1000 * 50)) + if (!WaitSCL_H_delay(30 * 1000 * 50)) return false; return true; @@ -631,7 +631,7 @@ int I2C_get_version(uint8_t *maj, uint8_t *min) { } // Will read response from smart card module, retries 3 times to get the data. -static bool sc_rx_bytes(uint8_t *dest, uint8_t *destlen) { +bool sc_rx_bytes(uint8_t *dest, uint8_t *destlen) { uint8_t i = 3; int16_t len = 0; @@ -658,7 +658,7 @@ static bool sc_rx_bytes(uint8_t *dest, uint8_t *destlen) { return true; } -bool GetATR(smart_card_atr_t *card_ptr) { +bool GetATR(smart_card_atr_t *card_ptr, bool verbose) { if (!card_ptr) return false; @@ -666,19 +666,18 @@ bool GetATR(smart_card_atr_t *card_ptr) { card_ptr->atr_len = 0; memset(card_ptr->atr, 0, sizeof(card_ptr->atr)); - // Send ATR // 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. - // 1byte = 1ms , max frame 256bytes. SHould wait 256ms atleast just in case. - if (!I2C_WaitForSim()) + // 1byte = 1ms , max frame 256bytes. Should wait 256ms atleast just in case. + if (I2C_WaitForSim() == false) return false; // read bytes from module uint8_t len = sizeof(card_ptr->atr); - if (!sc_rx_bytes(card_ptr->atr, &len)) + if (sc_rx_bytes(card_ptr->atr, &len) == false) return false; uint8_t pos_td = 1; @@ -706,17 +705,19 @@ bool GetATR(smart_card_atr_t *card_ptr) { } card_ptr->atr_len = len; - LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false); + if (verbose) { + LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false); + } + return true; } void SmartCardAtr(void) { smart_card_atr_t card; LED_D_ON(); - clear_trace(); set_tracing(true); I2C_Reset_EnterMainProgram(); - bool isOK = GetATR(&card); + bool isOK = GetATR(&card, true); reply_mix(CMD_ACK, isOK, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); set_tracing(false); LEDsoff(); @@ -730,10 +731,13 @@ void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data) { uint8_t *resp = BigBuf_malloc(ISO7618_MAX_FRAME); smartcard_command_t flags = arg0; - if ((flags & SC_CONNECT)) + if ((flags & SC_CLEARLOG) == SC_CLEARLOG) clear_trace(); - set_tracing(true); + if ((flags & SC_LOG) == SC_LOG) + set_tracing(true); + else + set_tracing(false); if ((flags & SC_CONNECT)) { @@ -741,7 +745,7 @@ void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data) { if ((flags & SC_SELECT)) { smart_card_atr_t card; - bool gotATR = GetATR(&card); + bool gotATR = GetATR(&card, true); //reply_old(CMD_ACK, gotATR, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); if (!gotATR) goto OUT; diff --git a/armsrc/i2c.h b/armsrc/i2c.h index 7b1707502..5f0f6dd18 100644 --- a/armsrc/i2c.h +++ b/armsrc/i2c.h @@ -33,8 +33,9 @@ int16_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t d 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 sc_rx_bytes(uint8_t *dest, uint8_t *destlen); // -bool GetATR(smart_card_atr_t *card_ptr); +bool GetATR(smart_card_atr_t *card_ptr, bool verbose); // generice functions void SmartCardAtr(void); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 1e9bb671f..7f22b59cc 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -3,6 +3,7 @@ // Hagen Fritsch - June 2010 // Gerhard de Koning Gans - May 2011 // Gerhard de Koning Gans - June 2012 - Added iClass card and reader emulation +// piwi - 2019 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -10,32 +11,10 @@ //----------------------------------------------------------------------------- // Routines to support iClass. //----------------------------------------------------------------------------- -// Based on ISO14443a implementation. Still in experimental phase. // Contribution made during a security research at Radboud University Nijmegen // // Please feel free to contribute and extend iClass support!! //----------------------------------------------------------------------------- -// -// FIX: -// ==== -// We still have sometimes a demodulation error when sniffing iClass communication. -// The resulting trace of a read-block-03 command may look something like this: -// -// + 22279: : 0c 03 e8 01 -// -// ...with an incorrect answer... -// -// + 85: 0: TAG ff! ff! ff! ff! ff! ff! ff! ff! bb 33 bb 00 01! 0e! 04! bb !crc -// -// We still left the error signalling bytes in the traces like 0xbb -// -// A correct trace should look like this: -// -// + 21112: : 0c 03 e8 01 -// + 85: 0: TAG ff ff ff ff ff ff ff ff ea f5 -// -//----------------------------------------------------------------------------- - #include "iclass.h" #include "proxmark3_arm.h" @@ -54,531 +33,38 @@ #include "dbprint.h" #include "protocols.h" #include "ticks.h" +#include "iso15693.h" -static int g_wait = 290; -static int timeout = 5000; -static uint32_t time_rdr = 0; -static uint32_t time_response = 0; - -static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay); -int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf); - -#define MODE_SIM_CSN 0 -#define MODE_EXIT_AFTER_MAC 1 -#define MODE_FULLSIM 2 - -#ifndef ICLASS_DMA_BUFFER_SIZE -# define ICLASS_DMA_BUFFER_SIZE 256 -#endif +static uint8_t get_pagemap(const picopass_hdr *hdr) { + return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; +} // The length of a received command will in most cases be no more than 18 bytes. -// 32 should be enough! +// we expect max 34 (32+2) bytes as tag answer (response to READ4) #ifndef ICLASS_BUFFER_SIZE -#define ICLASS_BUFFER_SIZE 32 +#define ICLASS_BUFFER_SIZE 34 + 2 #endif +#ifndef ICLASS_16KS_SIZE +#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) -//----------------------------------------------------------------------------- -// The software UART that receives commands from the reader, and its state -// variables. -//----------------------------------------------------------------------------- -/* -typedef struct { - enum { - STATE_UNSYNCD, - STATE_START_OF_COMMUNICATION, - STATE_RECEIVING - } state; - uint16_t shiftReg; - int bitCnt; - int byteCnt; -// int byteCntMax; - int posCnt; - int nOutOfCnt; - int OutOfCnt; - int syncBit; - int samples; - int highCnt; - int swapper; - int counter; - int bitBuffer; - int dropPosition; - uint8_t *output; -} tUartIc; -*/ -typedef struct { - enum { - DEMOD_IC_UNSYNCD, - DEMOD_IC_START_OF_COMMUNICATION, - DEMOD_IC_START_OF_COMMUNICATION2, - DEMOD_IC_START_OF_COMMUNICATION3, - DEMOD_IC_SOF_COMPLETE, - DEMOD_IC_MANCHESTER_D, - DEMOD_IC_MANCHESTER_E, - DEMOD_IC_END_OF_COMMUNICATION, - DEMOD_IC_END_OF_COMMUNICATION2, - DEMOD_IC_MANCHESTER_F, - DEMOD_IC_ERROR_WAIT - } state; - int bitCount; - int posCount; - int syncBit; - uint16_t shiftReg; - uint32_t buffer; - uint32_t buffer2; - uint32_t buffer3; - int buff; - int samples; - int len; - enum { - SUB_NONE, - SUB_FIRST_HALF, - SUB_SECOND_HALF, - SUB_BOTH - } sub; - uint8_t *output; -} tDemodIc; - -/* -* Abrasive's uart implementation -* https://github.com/abrasive/proxmark3/commit/2b8bff7daea8ae1193bf7ee29b1fa46e95218902 -*/ -// Static vars for UART -typedef struct { - bool synced; - bool frame; - bool frame_done; - uint8_t *buf; - int len; -} tUartIc; -static tUartIc Uart; - -static void OnError(uint8_t reason) { - reply_mix(CMD_ACK, 0, reason, 0, 0, 0); - switch_off(); -} - -static void uart_reset(void) { - Uart.frame_done = false; - Uart.synced = false; - Uart.frame = false; -} - -static void uart_init(uint8_t *data) { - Uart.buf = data; - uart_reset(); -} - -static void uart_bit(uint8_t bit) { - static uint8_t buf = 0xff; - static uint8_t n_buf; - static int nmsg_byte; - buf <<= 1; - buf |= bit ? 1 : 0; - - if (!Uart.frame) { - if (buf == 0x7b) { // 0b0111 1011 - Uart.frame = true; - n_buf = 0; - Uart.len = 0; - nmsg_byte = 0; - } - } else { - static uint8_t msg_byte; - n_buf++; - if (n_buf == 8) { - msg_byte >>= 2; - switch (buf) { - case 0xbf: // 0 - 1011 1111 - break; - case 0xef: // 1 - 1110 1111 - msg_byte |= (1 << 6); - break; - case 0xfb: // 2 - 1111 1011 - msg_byte |= (2 << 6); - break; - case 0xfe: // 3 - 1111 1110 - msg_byte |= (3 << 6); - break; - case 0xdf: // eof - 1101 1111 - Uart.frame = false; - Uart.synced = false; - Uart.frame_done = true; - break; - default: - Uart.frame = false; - Uart.synced = false; - Dbprintf("[-] bad %02X at %d:%d", buf, Uart.len, nmsg_byte); - } - - if (Uart.frame) { // data bits - nmsg_byte += 2; - if (nmsg_byte >= 8) { - Uart.buf[Uart.len++] = msg_byte; - nmsg_byte = 0; - } - } - n_buf = 0; - buf = 0xff; - } - } -} - -static void uart_samples(uint8_t byte) { - static uint32_t buf; - static int window; - static int drop_next = 0; - - uint32_t falling; - int lz; - - if (!Uart.synced) { - if (byte == 0xFF) - return; - buf = 0xFFFFFFFF; - window = 0; - drop_next = 0; - Uart.synced = true; - } - - buf <<= 8; - buf |= byte; - - if (drop_next) { - drop_next = 0; - return; - } - -again: - falling = ~buf & ((buf >> 1) ^ buf) & (0xFF << window); - - uart_bit(!falling); - - if (!falling) - return; - - lz = __builtin_clz(falling) - 24 + window; - - // aim to get falling edge on fourth-leftmost bit of window - window += 3 - lz; - - if (window < 0) { - window += 8; - drop_next = 1; - } else if (window >= 8) { - window -= 8; - goto again; - } -} - - -/* -static void UartReset(){ - Uart.state = STATE_UNSYNCD; - Uart.shiftReg = 0; - Uart.bitCnt = 0; - Uart.byteCnt = 0; - Uart.posCnt = 0; - Uart.nOutOfCnt = 0; - Uart.OutOfCnt = 0; - Uart.syncBit = 0; - Uart.samples = 0; - Uart.highCnt = 0; - Uart.swapper = 0; - Uart.counter = 0; - Uart.bitBuffer = 0; - Uart.dropPosition = 0; -} -*/ - -/* -* READER TO CARD -* 1 out of 4 Decoding -* 1 out of 256 Decoding -*/ -/* -static RAMFUNC int OutOfNDecoding(int bit) { - //int error = 0; - int bitright; - - if (!Uart.bitBuffer) { - Uart.bitBuffer = bit ^ 0xFF0; - return false; - } else { - Uart.bitBuffer <<= 4; - Uart.bitBuffer ^= bit; - } - - // if (Uart.swapper) { - // Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; - // Uart.byteCnt++; - // Uart.swapper = 0; - // if (Uart.byteCnt > 15) return true; - // } - // else { - // Uart.swapper = 1; - // } - - if (Uart.state != STATE_UNSYNCD) { - Uart.posCnt++; - - if ((Uart.bitBuffer & Uart.syncBit) ^ Uart.syncBit) - bit = 0; - else - bit = 1; - - if (((Uart.bitBuffer << 1) & Uart.syncBit) ^ Uart.syncBit) - bitright = 0; - else - bitright = 1; - - if(bit != bitright) - bit = bitright; - - - // So, now we only have to deal with *bit*, lets see... - if (Uart.posCnt == 1) { - // measurement first half bitperiod - if (!bit) { - // Drop in first half means that we are either seeing - // an SOF or an EOF. - - if (Uart.nOutOfCnt == 1) { - // End of Communication - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - if (Uart.byteCnt == 0) { - // Its not straightforward to show single EOFs - // So just leave it and do not return TRUE - Uart.output[0] = 0xf0; - Uart.byteCnt++; - } else { - return true; - } - } else if (Uart.state != STATE_START_OF_COMMUNICATION) { - // When not part of SOF or EOF, it is an error - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - //error = 4; - } - } - } else { - // measurement second half bitperiod - // Count the bitslot we are in... (ISO 15693) - Uart.nOutOfCnt++; - - if (!bit) { - if (Uart.dropPosition) { - if (Uart.state == STATE_START_OF_COMMUNICATION) { - //error = 1; - } else { - //error = 7; - } - // It is an error if we already have seen a drop in current frame - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - } else { - Uart.dropPosition = Uart.nOutOfCnt; - } - } - Uart.posCnt = 0; - - if (Uart.nOutOfCnt == Uart.OutOfCnt && Uart.OutOfCnt == 4) { - Uart.nOutOfCnt = 0; - - if (Uart.state == STATE_START_OF_COMMUNICATION) { - if (Uart.dropPosition == 4) { - Uart.state = STATE_RECEIVING; - Uart.OutOfCnt = 256; - } else if (Uart.dropPosition == 3) { - Uart.state = STATE_RECEIVING; - Uart.OutOfCnt = 4; - //Uart.output[Uart.byteCnt] = 0xdd; - //Uart.byteCnt++; - } else { - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - } - Uart.dropPosition = 0; - } else { - // RECEIVING DATA - // 1 out of 4 - if (!Uart.dropPosition) { - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - //error = 9; - } else { - Uart.shiftReg >>= 2; - - // Swap bit order - Uart.dropPosition--; - //if(Uart.dropPosition == 1) { Uart.dropPosition = 2; } - //else if(Uart.dropPosition == 2) { Uart.dropPosition = 1; } - - Uart.shiftReg ^= ((Uart.dropPosition & 0x03) << 6); - Uart.bitCnt += 2; - Uart.dropPosition = 0; - - if (Uart.bitCnt == 8) { - Uart.output[Uart.byteCnt] = (Uart.shiftReg & 0xff); - Uart.byteCnt++; - Uart.bitCnt = 0; - Uart.shiftReg = 0; - } - } - } - } else if (Uart.nOutOfCnt == Uart.OutOfCnt) { - // RECEIVING DATA - // 1 out of 256 - if (!Uart.dropPosition) { - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - //error = 3; - } else { - Uart.dropPosition--; - Uart.output[Uart.byteCnt] = (Uart.dropPosition & 0xff); - Uart.byteCnt++; - Uart.bitCnt = 0; - Uart.shiftReg = 0; - Uart.nOutOfCnt = 0; - Uart.dropPosition = 0; - } - } -*/ -/*if (error) { - Uart.output[Uart.byteCnt] = 0xAA; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = error & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = 0xAA; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = (Uart.bitBuffer >> 8) & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = (Uart.syncBit >> 3) & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = 0xAA; - Uart.byteCnt++; - return true; -}*/ -/* - } - } else { - bit = Uart.bitBuffer & 0xf0; - bit >>= 4; - bit ^= 0x0F; // drops become 1s ;-) - if (bit) { - // should have been high or at least (4 * 128) / fc - // according to ISO this should be at least (9 * 128 + 20) / fc - if (Uart.highCnt == 8) { - // we went low, so this could be start of communication - // it turns out to be safer to choose a less significant - // syncbit... so we check whether the neighbour also represents the drop - Uart.posCnt = 1; // apparently we are busy with our first half bit period - Uart.syncBit = bit & 8; - Uart.samples = 3; - - if (!Uart.syncBit) { Uart.syncBit = bit & 4; Uart.samples = 2; } - else if (bit & 4) { Uart.syncBit = bit & 4; Uart.samples = 2; bit <<= 2; } - - if (!Uart.syncBit) { Uart.syncBit = bit & 2; Uart.samples = 1; } - else if (bit & 2) { Uart.syncBit = bit & 2; Uart.samples = 1; bit <<= 1; } - - if (!Uart.syncBit) { Uart.syncBit = bit & 1; Uart.samples = 0; - if (Uart.syncBit && (Uart.bitBuffer & 8)) { - Uart.syncBit = 8; - - // the first half bit period is expected in next sample - Uart.posCnt = 0; - Uart.samples = 3; - } - } else if (bit & 1) { Uart.syncBit = bit & 1; Uart.samples = 0; } - - Uart.syncBit <<= 4; - Uart.state = STATE_START_OF_COMMUNICATION; - Uart.bitCnt = 0; - Uart.byteCnt = 0; - Uart.nOutOfCnt = 0; - Uart.OutOfCnt = 4; // Start at 1/4, could switch to 1/256 - Uart.dropPosition = 0; - Uart.shiftReg = 0; - //error = 0; - } else { - Uart.highCnt = 0; - } - } else { - if (Uart.highCnt < 8) - Uart.highCnt++; - } - } - return false; -} -*/ -//============================================================================= -// Manchester -//============================================================================= -static tDemodIc Demod; -static void DemodIcReset(void) { - Demod.bitCount = 0; - Demod.posCount = 0; - Demod.syncBit = 0; - Demod.shiftReg = 0; - Demod.buffer = 0; - Demod.buffer2 = 0; - Demod.buffer3 = 0; - Demod.buff = 0; - Demod.samples = 0; - Demod.len = 0; - Demod.sub = SUB_NONE; - Demod.state = DEMOD_IC_UNSYNCD; -} -static void DemodIcInit(uint8_t *data) { - Demod.output = data; - DemodIcReset(); -} - -// UART debug -// it adds the debug values which will be put in the tracelog, -// visible on client when running 'hf list iclass' -/* -pm3 --> hf li iclass -Recorded Activity (TraceLen = 162 bytes) - Start | End | Src | Data (! denotes parity error) | CRC | Annotation | -------------|------------|-----|-----------------------------------------------------------------|-----|--------------------| - 0 | 0 | Rdr |0a | | ACTALL - 1280 | 1280 | Tag |bb! 33! bb! 01 02 04 08 bb! | ok | - 1280 | 1280 | Rdr |0c | | IDENTIFY - 1616 | 1616 | Tag |bb! 33! bb! 00! 02 00! 02 bb! | ok | - 1616 | 1616 | Rdr |0a | | ACTALL - 2336 | 2336 | Tag |bb! d4! bb! 02 08 00! 08 bb! | ok | - 2336 | 2336 | Rdr |0c | | IDENTIFY - 2448 | 2448 | Tag |bb! 33! bb! 00! 00! 00! 02 bb! | ok | - 2448 | 2448 | Rdr |0a | | ACTALL - 2720 | 2720 | Tag |bb! d4! bb! 08 0b 01 04 bb! | ok | - 2720 | 2720 | Rdr |0c | | IDENTIFY - 3232 | 3232 | Tag |bb! d4! bb! 02 02 08 04 bb! | ok | -*/ -static void uart_debug(int error, int bit) { - Demod.output[Demod.len] = 0xBB; - Demod.len++; - Demod.output[Demod.len] = error & 0xFF; - Demod.len++; - Demod.output[Demod.len] = 0xBB; - Demod.len++; - Demod.output[Demod.len] = bit & 0xFF; - Demod.len++; - Demod.output[Demod.len] = Demod.buffer & 0xFF; - Demod.len++; - // Look harder ;-) - Demod.output[Demod.len] = Demod.buffer2 & 0xFF; - Demod.len++; - Demod.output[Demod.len] = Demod.syncBit & 0xFF; - Demod.len++; - Demod.output[Demod.len] = 0xBB; - Demod.len++; -} /* * CARD TO READER @@ -596,568 +82,63 @@ static void uart_debug(int error, int bit) { * * So for current implementation in ISO15693, its 330 µs from end of reader, to start of card. */ -static RAMFUNC int ManchesterDecoding_iclass(uint32_t v) { - int bit; - int modulation; - int error = 0; - - bit = Demod.buffer; - Demod.buffer = Demod.buffer2; - Demod.buffer2 = Demod.buffer3; - Demod.buffer3 = v; - - // too few bits? - if (Demod.buff < 3) { - Demod.buff++; - return false; - } - - if (Demod.state == DEMOD_IC_UNSYNCD) { - Demod.output[Demod.len] = 0xfa; - Demod.syncBit = 0; - //Demod.samples = 0; - Demod.posCount = 1; // This is the first half bit period, so after syncing handle the second part - - if (bit & 0x08) - Demod.syncBit = 0x08; - - if (bit & 0x04) { - if (Demod.syncBit) - bit <<= 4; - - Demod.syncBit = 0x04; - } - - if (bit & 0x02) { - if (Demod.syncBit) - bit <<= 2; - - Demod.syncBit = 0x02; - } - - if (bit & 0x01 && Demod.syncBit) - Demod.syncBit = 0x01; - - if (Demod.syncBit) { - Demod.len = 0; - Demod.state = DEMOD_IC_START_OF_COMMUNICATION; - Demod.sub = SUB_FIRST_HALF; - Demod.bitCount = 0; - Demod.shiftReg = 0; - Demod.samples = 0; - - if (Demod.posCount) { - - switch (Demod.syncBit) { - case 0x08: - Demod.samples = 3; - break; - case 0x04: - Demod.samples = 2; - break; - case 0x02: - Demod.samples = 1; - break; - case 0x01: - Demod.samples = 0; - break; - } - // SOF must be long burst... otherwise stay unsynced!!! - if (!(Demod.buffer & Demod.syncBit) || !(Demod.buffer2 & Demod.syncBit)) - Demod.state = DEMOD_IC_UNSYNCD; - - } else { - // SOF must be long burst... otherwise stay unsynced!!! - if (!(Demod.buffer2 & Demod.syncBit) || !(Demod.buffer3 & Demod.syncBit)) { - Demod.state = DEMOD_IC_UNSYNCD; - error = 0x88; - uart_debug(error, bit); - return false; - } - } - } - return false; - } - - // state is DEMOD is in SYNC from here on. - - modulation = bit & Demod.syncBit; - modulation |= ((bit << 1) ^ ((Demod.buffer & 0x08) >> 3)) & Demod.syncBit; - Demod.samples += 4; - - if (Demod.posCount == 0) { - Demod.posCount = 1; - Demod.sub = (modulation) ? SUB_FIRST_HALF : SUB_NONE; - return false; - } - - Demod.posCount = 0; - - if (modulation) { - - if (Demod.sub == SUB_FIRST_HALF) - Demod.sub = SUB_BOTH; - else - Demod.sub = SUB_SECOND_HALF; - } - - if (Demod.sub == SUB_NONE) { - if (Demod.state == DEMOD_IC_SOF_COMPLETE) { - Demod.output[Demod.len] = 0x0f; - Demod.len++; - Demod.state = DEMOD_IC_UNSYNCD; - return true; - } else { - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0x33; - } - } - - switch (Demod.state) { - - case DEMOD_IC_START_OF_COMMUNICATION: - if (Demod.sub == SUB_BOTH) { - - Demod.state = DEMOD_IC_START_OF_COMMUNICATION2; - Demod.posCount = 1; - Demod.sub = SUB_NONE; - } else { - Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0xd2; - } - break; - - case DEMOD_IC_START_OF_COMMUNICATION2: - if (Demod.sub == SUB_SECOND_HALF) { - Demod.state = DEMOD_IC_START_OF_COMMUNICATION3; - } else { - Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0xd3; - } - break; - - case DEMOD_IC_START_OF_COMMUNICATION3: - if (Demod.sub == SUB_SECOND_HALF) { - Demod.state = DEMOD_IC_SOF_COMPLETE; - } else { - Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0xd4; - } - break; - - case DEMOD_IC_SOF_COMPLETE: - case DEMOD_IC_MANCHESTER_D: - case DEMOD_IC_MANCHESTER_E: - // OPPOSITE FROM ISO14443 - 11110000 = 0 (1 in 14443) - // 00001111 = 1 (0 in 14443) - if (Demod.sub == SUB_SECOND_HALF) { // SUB_FIRST_HALF - Demod.bitCount++; - Demod.shiftReg = (Demod.shiftReg >> 1) ^ 0x100; - Demod.state = DEMOD_IC_MANCHESTER_D; - } else if (Demod.sub == SUB_FIRST_HALF) { // SUB_SECOND_HALF - Demod.bitCount++; - Demod.shiftReg >>= 1; - Demod.state = DEMOD_IC_MANCHESTER_E; - } else if (Demod.sub == SUB_BOTH) { - Demod.state = DEMOD_IC_MANCHESTER_F; - } else { - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0x55; - } - break; - - case DEMOD_IC_MANCHESTER_F: - // Tag response does not need to be a complete byte! - if (Demod.len > 0 || Demod.bitCount > 0) { - if (Demod.bitCount > 1) { // was > 0, do not interpret last closing bit, is part of EOF - Demod.shiftReg >>= (9 - Demod.bitCount); // right align data - Demod.output[Demod.len] = Demod.shiftReg & 0xff; - Demod.len++; - } - - Demod.state = DEMOD_IC_UNSYNCD; - return true; - } else { - Demod.output[Demod.len] = 0xad; - Demod.state = DEMOD_IC_ERROR_WAIT; - error = 0x03; - } - break; - - case DEMOD_IC_ERROR_WAIT: - Demod.state = DEMOD_IC_UNSYNCD; - break; - - default: - Demod.output[Demod.len] = 0xdd; - Demod.state = DEMOD_IC_UNSYNCD; - break; - } - - if (Demod.bitCount >= 8) { - Demod.shiftReg >>= 1; - Demod.output[Demod.len] = (Demod.shiftReg & 0xff); - Demod.len++; - Demod.bitCount = 0; - Demod.shiftReg = 0; - } - - if (error) { - uart_debug(error, bit); - return true; - } - - return false; -} //============================================================================= -// Finally, a `sniffer' for iClass communication +// a `sniffer' for iClass communication // Both sides of communication! //============================================================================= -static void iclass_setup_sniff(void) { - if (DBGLEVEL > 3) Dbprintf("iclass_setup_sniff Enter"); - - LEDsoff(); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - - // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - // Set up the synchronous serial port - FpgaSetupSsc(); - - BigBuf_free(); - BigBuf_Clear_ext(false); - clear_trace(); - set_tracing(true); - - // Initialize Demod and Uart structs - DemodIcInit(BigBuf_malloc(ICLASS_BUFFER_SIZE)); - - uart_init(BigBuf_malloc(ICLASS_BUFFER_SIZE)); - //UartIcInit(BigBuf_malloc(ICLASS_BUFFER_SIZE)); - - if (DBGLEVEL > 1) { - // Print debug information about the buffer sizes - Dbprintf("[+] Sniffing buffers initialized:"); - Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); - Dbprintf(" Reader -> tag: %i bytes", ICLASS_BUFFER_SIZE); - Dbprintf(" tag -> Reader: %i bytes", ICLASS_BUFFER_SIZE); - Dbprintf(" DMA: %i bytes", ICLASS_DMA_BUFFER_SIZE); - } - - // Set FPGA in the appropriate mode - // put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER); - SpinDelay(200); - - // Start the SSP timer - StartCountSspClk(); - - LED_A_ON(); - if (DBGLEVEL > 3) Dbprintf("[+] iclass_setup_sniff Exit"); +void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string) { + SniffIso15693(jam_search_len, jam_search_string); } -//----------------------------------------------------------------------------- -// Record the sequence of commands sent by the reader to the tag, with -// triggering so that we start recording at the point that the tag is moved -// near the reader. -//----------------------------------------------------------------------------- -// turn off afterwards -void RAMFUNC SniffIClass(void) { - - //int datalen = 0; - uint32_t previous_data = 0; - uint32_t time_0 = 0, time_start = 0, time_stop; - uint32_t sniffCounter = 0; - bool TagIsActive = false; - bool ReaderIsActive = false; - - iclass_setup_sniff(); - - // The DMA buffer, used to stream samples from the FPGA - // *dmaBuf is the start reference. - uint8_t *dmaBuf = BigBuf_malloc(ICLASS_DMA_BUFFER_SIZE); - // pointer to samples from fpga - uint8_t *data = dmaBuf; - - // Setup and start DMA. - if (!FpgaSetupSscDma(dmaBuf, ICLASS_DMA_BUFFER_SIZE)) { - if (DBGLEVEL > 1) DbpString("[-] FpgaSetupSscDma failed. Exiting"); - return; +static void rotateCSN(uint8_t *original_csn, uint8_t *rotated_csn) { + for (uint8_t i = 0; i < 8; i++) { + rotated_csn[i] = (original_csn[i] >> 3) | (original_csn[(i + 1) % 8] << 5); } - - // time ZERO, the point from which it all is calculated. - time_0 = GetCountSspClk(); - - // loop and listen - // every sample (1byte in data), - // contains HIGH nibble = reader data - // contains LOW nibble = tag data - // so two bytes are needed in order to get 1byte of either reader or tag data. (ie 2 sample bytes) - // since reader data is manchester encoded, we need 2bytes of data in order to get one demoded byte. (ie: 4 sample bytes) - uint16_t checked = 0; - for (;;) { - WDT_HIT(); - - if (checked == 1000) { - if (BUTTON_PRESS() || data_available()) break; - checked = 0; - } - ++checked; - - previous_data <<= 8; - previous_data |= *data; - - sniffCounter++; - data++; - - if (data == dmaBuf + ICLASS_DMA_BUFFER_SIZE) { - data = dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = ICLASS_DMA_BUFFER_SIZE; - } - - // every odd sample - if (sniffCounter & 0x01) { - // no need to try decoding reader data if the tag is sending - // READER TO CARD - if (!TagIsActive) { - LED_C_INV(); - // HIGH nibble is always reader data. - uint8_t reader_byte = (previous_data & 0xF0) | (*data >> 4); - uart_samples(reader_byte); - if (Uart.frame_done) { - time_stop = GetCountSspClk() - time_0; - LogTrace(Uart.buf, Uart.len, time_start, time_stop, NULL, true); - DemodIcReset(); - uart_reset(); - } else { - time_start = GetCountSspClk() - time_0; - } - ReaderIsActive = Uart.frame_done; - } - } - // every four sample - if ((sniffCounter % 4) == 0) { - // need two samples to feed Manchester - // no need to try decoding tag data if the reader is sending - and we cannot afford the time - // CARD TO READER - if (!ReaderIsActive) { - LED_C_INV(); - // LOW nibble is always tag data. - /* - uint32_t tag_byte = - ((previous_data & 0x0F000000) >> 8 ) | - ((previous_data & 0x000F0000) >> 4 ) | - ((previous_data & 0x00000F00) ) | - ((previous_data & 0x0000000F) << 4 ) | - (*data & 0xF); - */ - - - uint8_t tag_byte = ((previous_data & 0xF) << 4) | (*data & 0xF); - if (ManchesterDecoding_iclass(tag_byte)) { - time_stop = GetCountSspClk() - time_0; - LogTrace(Demod.output, Demod.len, time_start, time_stop, NULL, false); - DemodIcReset(); - uart_reset(); - } else { - time_start = GetCountSspClk() - time_0; - } - TagIsActive = (Demod.state != DEMOD_IC_UNSYNCD); - } - } - } // end main loop - - /* - if (DBGLEVEL >= 1) { - DbpString("[+] Sniff statistics:"); - Dbhexdump(ICLASS_DMA_BUFFER_SIZE, data, false); - } - */ - switch_off(); } -static void rotateCSN(uint8_t *originalCSN, uint8_t *rotatedCSN) { - int i; - for (i = 0; i < 8; i++) - rotatedCSN[i] = (originalCSN[i] >> 3) | (originalCSN[(i + 1) % 8] << 5); -} - -//----------------------------------------------------------------------------- -// SIMULATION -// Wait for commands from reader -// Stop when button is pressed -// Or return TRUE when command is captured -//----------------------------------------------------------------------------- -static bool GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) { - // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen - // only, since we are receiving, not transmitting). - // Signal field is off with the appropriate LED - LED_D_OFF(); - uart_init(received); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); - // clear RXRDY: - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - (void)b; - - uint16_t checked = 0; - for (;;) { - - WDT_HIT(); - - if (checked == 1000) { - if (BUTTON_PRESS() || data_available()) return false; - checked = 0; - } - ++checked; - - // keep tx buffer in a defined state anyway. - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) - AT91C_BASE_SSC->SSC_THR = 0x00; - - // wait for byte to become available in rx holding register - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - - uart_samples(b); - if (Uart.frame_done) { - *len = Uart.len; - return true; - } - } - } - return false; +// Encode SOF only +static void CodeIClassTagSOF(void) { + tosend_reset(); + tosend_t *ts = get_tosend(); + ts->buf[++ts->max] = 0x1D; + ts->max++; } /* -static uint8_t encode4Bits(const uint8_t b) { - // OTA, the least significant bits first - // Manchester encoding added - // The columns are - // 1 - Bit value to send - // 2 - Reversed (big-endian) - // 3 - Machester Encoded - // 4 - Hex values - - uint8_t c = b & 0xF; - switch (c) { - // 1 2 3 4 - case 15: - return 0x55; // 1111 -> 1111 -> 01010101 -> 0x55 - case 14: - return 0x95; // 1110 -> 0111 -> 10010101 -> 0x95 - case 13: - return 0x65; // 1101 -> 1011 -> 01100101 -> 0x65 - case 12: - return 0xa5; // 1100 -> 0011 -> 10100101 -> 0xa5 - case 11: - return 0x59; // 1011 -> 1101 -> 01011001 -> 0x59 - case 10: - return 0x99; // 1010 -> 0101 -> 10011001 -> 0x99 - case 9: - return 0x69; // 1001 -> 1001 -> 01101001 -> 0x69 - case 8: - return 0xa9; // 1000 -> 0001 -> 10101001 -> 0xa9 - case 7: - return 0x56; // 0111 -> 1110 -> 01010110 -> 0x56 - case 6: - return 0x96; // 0110 -> 0110 -> 10010110 -> 0x96 - case 5: - return 0x66; // 0101 -> 1010 -> 01100110 -> 0x66 - case 4: - return 0xa6; // 0100 -> 0010 -> 10100110 -> 0xa6 - case 3: - return 0x5a; // 0011 -> 1100 -> 01011010 -> 0x5a - case 2: - return 0x9a; // 0010 -> 0100 -> 10011010 -> 0x9a - case 1: - return 0x6a; // 0001 -> 1000 -> 01101010 -> 0x6a - default: - return 0xaa; // 0000 -> 0000 -> 10101010 -> 0xaa - } -} -*/ - -static uint8_t lut_enc[] = { 0xAA, 0x6A, 0x9A, 0x5A, 0xA6, 0x66, 0x96, 0x56, 0xA9, 0x69, 0x99, 0x59, 0xA5, 0x65, 0x95, 0x55 }; - -//----------------------------------------------------------------------------- -// Prepare tag messages -//----------------------------------------------------------------------------- -static void CodeIClassTagAnswer(const uint8_t *cmd, int len) { - /* - * SOF comprises 3 parts; - * * An unmodulated time of 56.64 us - * * 24 pulses of 423.75 kHz (fc/32) - * * A logic 1, which starts with an unmodulated time of 18.88us - * followed by 8 pulses of 423.75kHz (fc/32) - * - * - * EOF comprises 3 parts: - * - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated - * time of 18.88us. - * - 24 pulses of fc/32 - * - An unmodulated time of 56.64 us - * - * - * A logic 0 starts with 8 pulses of fc/32 - * followed by an unmodulated time of 256/fc (~18,88us). - * - * A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by - * 8 pulses of fc/32 (also 18.88us) - * - * The mode FPGA_HF_SIMULATOR_MODULATE_424K_8BIT which we use to simulate tag, - * works like this. - * - A 1-bit input to the FPGA becomes 8 pulses on 423.5kHz (fc/32) (18.88us). - * - A 0-bit input to the FPGA becomes an unmodulated time of 18.88us - * - * In this mode - * SOF can be written as 00011101 = 0x1D - * EOF can be written as 10111000 = 0xb8 - * logic 1 be written as 01 = 0x1 - * logic 0 be written as 10 = 0x2 - * - * */ - ToSendReset(); - - // Send SOF - ToSend[++ToSendMax] = 0x1D; - - int i; - for (i = 0; i < len; i++) { - uint8_t b = cmd[i]; - ToSend[++ToSendMax] = lut_enc[b & 0xF]; // least significant half - ToSend[++ToSendMax] = lut_enc[(b >> 4) & 0xF]; // most significant half - } - - // Send EOF - ToSend[++ToSendMax] = 0xB8; - //lastProxToAirDuration = 8*ToSendMax - 3*8 - 3*8;//Not counting zeroes in the beginning or end - // Convert from last byte pos to length - ToSendMax++; -} - -// Only SOF -static void CodeIClassTagSOF(void) { - //So far a dummy implementation, not used - //int lastProxToAirDuration =0; - - ToSendReset(); - // Send SOF - ToSend[++ToSendMax] = 0x1D; - // lastProxToAirDuration = 8*ToSendMax - 3*8;//Not counting zeroes in the beginning - - // Convert from last byte pos to length - ToSendMax++; -} + * SOF comprises 3 parts; + * * An unmodulated time of 56.64 us + * * 24 pulses of 423.75 kHz (fc/32) + * * A logic 1, which starts with an unmodulated time of 18.88us + * followed by 8 pulses of 423.75kHz (fc/32) + * + * + * EOF comprises 3 parts: + * - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated + * time of 18.88us. + * - 24 pulses of fc/32 + * - An unmodulated time of 56.64 us + * + * + * A logic 0 starts with 8 pulses of fc/32 + * followed by an unmodulated time of 256/fc (~18,88us). + * + * A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by + * 8 pulses of fc/32 (also 18.88us) + * + * The mode FPGA_HF_SIMULATOR_MODULATE_424K_8BIT which we use to simulate tag, + * works like this. + * - A 1-bit input to the FPGA becomes 8 pulses on 423.5kHz (fc/32) (18.88us). + * - A 0-bit input to the FPGA becomes an unmodulated time of 18.88us + * + * In this mode + * SOF can be written as 00011101 = 0x1D + * EOF can be written as 10111000 = 0xb8 + * logic 1 be written as 01 = 0x1 + * logic 0 be written as 10 = 0x2 + * + * + */ /** * @brief SimulateIClass simulates an iClass card. @@ -1173,70 +154,90 @@ static void CodeIClassTagSOF(void) { */ // turn off afterwards void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { + iclass_simulate(arg0, arg1, arg2, datain, NULL, NULL); +} - if (DBGLEVEL > 3) Dbprintf("[+] iClass_simulate Enter"); +void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_t *datain, uint8_t *dataout, uint16_t *dataoutlen) { LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - // this will clear out bigbuf memory, the eload command must select this before! - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaSetupSsc(); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + Iso15693InitTag(); - // Enable and clear the trace clear_trace(); - set_tracing(true); - uint32_t simType = arg0; - uint32_t numberOfCSNS = arg1; + // only logg if we are called from the client. + set_tracing(send_reply); //Use the emulator memory for SIM uint8_t *emulator = BigBuf_get_EM_addr(); uint8_t mac_responses[PM3_CMD_DATA_SIZE] = { 0 }; - if (simType == 0) { + if (sim_type == ICLASS_SIM_MODE_CSN) { // Use the CSN from commandline memcpy(emulator, datain, 8); - doIClassSimulation(MODE_SIM_CSN, NULL); - } else if (simType == 1) { - //Default CSN - uint8_t csn_crc[] = { 0x03, 0x1f, 0xec, 0x8a, 0xf7, 0xff, 0x12, 0xe0, 0x00, 0x00 }; - // Use the CSN from commandline - memcpy(emulator, csn_crc, 8); - doIClassSimulation(MODE_SIM_CSN, NULL); - } else if (simType == 2) { + do_iclass_simulation(ICLASS_SIM_MODE_CSN, NULL); - Dbprintf("[+] going into attack mode, %d CSNS sent", numberOfCSNS); + } else if (sim_type == ICLASS_SIM_MODE_CSN_DEFAULT) { + //Default CSN + uint8_t csn[] = { 0x03, 0x1f, 0xec, 0x8a, 0xf7, 0xff, 0x12, 0xe0 }; + // Use the CSN from commandline + memcpy(emulator, csn, 8); + do_iclass_simulation(ICLASS_SIM_MODE_CSN, NULL); + + } else if (sim_type == ICLASS_SIM_MODE_READER_ATTACK) { + + Dbprintf("going into attack mode, %d CSNS sent", num_csns); // In this mode, a number of csns are within datain. We'll simulate each one, one at a time // in order to collect MAC's from the reader. This can later be used in an offlne-attack // in order to obtain the keys, as in the "dismantling iclass"-paper. #define EPURSE_MAC_SIZE 16 int i = 0; - for (; i < numberOfCSNS && i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE; i++) { - // The usb data is 512 bytes, fitting 65 8-byte CSNs in there. + for (; i < num_csns && i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE; i++) { memcpy(emulator, datain + (i * 8), 8); - if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { + if (do_iclass_simulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { + + if (dataoutlen) + *dataoutlen = i * EPURSE_MAC_SIZE; + // Button pressed - reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); + if (send_reply) + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); goto out; } } - reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); + if (dataoutlen) + *dataoutlen = i * EPURSE_MAC_SIZE; + + if (send_reply) + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); + + } else if (sim_type == ICLASS_SIM_MODE_FULL) { - } else if (simType == 3) { //This is 'full sim' mode, where we use the emulator storage for data. //ie: BigBuf_get_EM_addr should be previously filled with data from the "eload" command - doIClassSimulation(MODE_FULLSIM, NULL); - } else if (simType == 4) { + picopass_hdr *hdr = (picopass_hdr *)BigBuf_get_EM_addr(); + uint8_t pagemap = get_pagemap(hdr); + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + do_iclass_simulation_nonsec(); + } else { + do_iclass_simulation(ICLASS_SIM_MODE_FULL, NULL); + } + + } else if (sim_type == ICLASS_SIM_MODE_CONFIG_CARD) { + + // config card + do_iclass_simulation(ICLASS_SIM_MODE_FULL, NULL); + // swap bin + + } else if (sim_type == ICLASS_SIM_MODE_READER_ATTACK_KEYROLL) { // This is the KEYROLL version of sim 2. // the collected data (mac_response) is doubled out since we are trying to collect both keys in the keyroll process. // Keyroll iceman 9 csns * 8 * 2 = 144 // keyroll CARL55 15csns * 8 * 2 = 15 * 8 * 2 = 240 - Dbprintf("[+] going into attack keyroll mode, %d CSNS sent", numberOfCSNS); + Dbprintf("going into attack keyroll mode, %d CSNS sent", num_csns); // In this mode, a number of csns are within datain. We'll simulate each one, one at a time // in order to collect MAC's from the reader. This can later be used in an offlne-attack // in order to obtain the keys, as in the "dismantling iclass"-paper. @@ -1245,53 +246,81 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain // attack below is same as SIM 2, but we run the CSN twice to collected the mac for both keys. int i = 0; // The usb data is 512 bytes, fitting 65 8-byte CSNs in there. iceman fork uses 9 CSNS - for (; i < numberOfCSNS && i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE; i++) { + for (; i < num_csns && i * EPURSE_MAC_SIZE + 8 < PM3_CMD_DATA_SIZE; i++) { memcpy(emulator, datain + (i * 8), 8); // keyroll 1 - if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { - reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + if (do_iclass_simulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { + + if (dataoutlen) + *dataoutlen = i * EPURSE_MAC_SIZE * 2; + + if (send_reply) + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + // Button pressed goto out; } // keyroll 2 - if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + (i + numberOfCSNS) * EPURSE_MAC_SIZE)) { - reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + if (do_iclass_simulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses + (i + num_csns) * EPURSE_MAC_SIZE)) { + + if (dataoutlen) + *dataoutlen = i * EPURSE_MAC_SIZE * 2; + + if (send_reply) + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + // Button pressed goto out; } } + + if (dataoutlen) + *dataoutlen = i * EPURSE_MAC_SIZE * 2; + // double the amount of collected data. - reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + if (send_reply) + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); } else { // We may want a mode here where we hardcode the csns to use (from proxclone). // That will speed things up a little, but not required just yet. - DbpString("[-] the mode is not implemented, reserved for future use"); + DbpString("the mode is not implemented, reserved for future use"); } out: + if (dataout && dataoutlen) + memcpy(dataout, mac_responses, *dataoutlen); + switch_off(); BigBuf_free_keep_EM(); } /** + * Simulation assumes a SECURE PAGE simulation with authentication and application areas. + * + * * @brief Does the actual simulation * @param csn - csn to use * @param breakAfterMacReceived if true, returns after reader MAC has been received. */ -int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { +int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { // free eventually allocated BigBuf memory BigBuf_free_keep_EM(); - State cipher_state; + uint16_t page_size = 32 * 8; + uint8_t current_page = 0; - uint8_t *csn = BigBuf_get_EM_addr(); - uint8_t *emulator = csn; - uint8_t sof_data[] = { 0x0F} ; + // maintain cipher states for both credit and debit key for each page + State cipher_state_KD[8]; + State cipher_state_KC[8]; + State *cipher_state = &cipher_state_KD[0]; + + uint8_t *emulator = BigBuf_get_EM_addr(); + uint8_t *csn = emulator; // CSN followed by two CRC bytes uint8_t anticoll_data[10] = { 0 }; @@ -1305,28 +334,616 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { AddCrc(anticoll_data, 8); AddCrc(csn_data, 8); - uint8_t diversified_key[8] = { 0 }; + uint8_t diversified_kd[8] = { 0 }; + uint8_t diversified_kc[8] = { 0 }; + uint8_t *diversified_key = diversified_kd; + + // configuration block + uint8_t conf_block[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00}; // e-Purse uint8_t card_challenge_data[8] = { 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - //uint8_t card_challenge_data[8] = { 0 }; - if (simulationMode == MODE_FULLSIM) { - //The diversified key should be stored on block 3 - //Get the diversified key from emulator memory - memcpy(diversified_key, emulator + (8 * 3), 8); - //Card challenge, a.k.a e-purse is on block 2 - memcpy(card_challenge_data, emulator + (8 * 2), 8); + // AIA + uint8_t aia_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; - //Precalculate the cipher state, feeding it the CC - cipher_state = opt_doTagMAC_1(card_challenge_data, diversified_key); + if (simulationMode == ICLASS_SIM_MODE_FULL) { + + memcpy(conf_block, emulator + (8 * 1), 8); // blk 1 + memcpy(card_challenge_data, emulator + (8 * 2), 8); // e-purse, blk 2 + memcpy(diversified_kd, emulator + (8 * 3), 8); // Kd, blk 3 + memcpy(diversified_kc, emulator + (8 * 4), 8); // Kc, blk 4 + + // (iceman) this only works for 2KS / 16KS tags. + // Use application data from block 5 + memcpy(aia_data, emulator + (8 * 5), 8); } + + AddCrc(conf_block, 8); + AddCrc(aia_data, 8); + // set epurse of sim2,4 attack if (reader_mac_buf != NULL) { memcpy(reader_mac_buf, card_challenge_data, 8); } - int exitLoop = 0; + if ((conf_block[5] & 0x80) == 0x80) { + page_size = 256 * 8; + } + + // From PicoPass DS: + // When the page is in personalization mode this bit is equal to 1. + // Once the application issuer has personalized and coded its dedicated areas, this bit must be set to 0: + // the page is then "in application mode". + bool personalization_mode = conf_block[7] & 0x80; + + uint8_t block_wr_lock = conf_block[3]; + + // chip memory may be divided in 8 pages + uint8_t max_page = ((conf_block[4] & 0x10) == 0x10) ? 0 : 7; + + // pre-calculate the cipher states, feeding it the CC + cipher_state_KD[0] = opt_doTagMAC_1(card_challenge_data, diversified_kd); + cipher_state_KC[0] = opt_doTagMAC_1(card_challenge_data, diversified_kc); + + if (simulationMode == ICLASS_SIM_MODE_FULL) { + + for (int i = 1; i < max_page; i++) { + + uint8_t *epurse = emulator + (i * page_size) + (8 * 2); + uint8_t *kd = emulator + (i * page_size) + (8 * 3); + uint8_t *kc = emulator + (i * page_size) + (8 * 4); + + cipher_state_KD[i] = opt_doTagMAC_1(epurse, kd); + cipher_state_KC[i] = opt_doTagMAC_1(epurse, kc); + } + } + + // Anti-collision process: + // Reader 0a + // Tag 0f + // Reader 0c + // Tag anticoll. CSN + // Reader 81 anticoll. CSN + // Tag CSN + + uint8_t *modulated_response = NULL; + int modulated_response_size = 0; + uint8_t *trace_data = NULL; + int trace_data_size = 0; + + // Respond SOF -- takes 1 bytes + uint8_t *resp_sof = BigBuf_malloc(1); + int resp_sof_len; + + // Anticollision CSN (rotated CSN) + // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) + uint8_t *resp_anticoll = BigBuf_malloc(22); + int resp_anticoll_len; + + // CSN (block 0) + // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) + uint8_t *resp_csn = BigBuf_malloc(22); + int resp_csn_len; + + // configuration (blk 1) PICOPASS 2ks + uint8_t *resp_conf = BigBuf_malloc(22); + int resp_conf_len; + + // e-Purse (blk 2) + // 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit) + uint8_t *resp_cc = BigBuf_malloc(18); + int resp_cc_len; + + // Kd, Kc (blocks 3 and 4). Cannot be read. Always respond with 0xff bytes only + uint8_t *resp_ff = BigBuf_malloc(22); + int resp_ff_len; + uint8_t ff_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; + AddCrc(ff_data, 8); + + // Application Issuer Area (blk 5) + uint8_t *resp_aia = BigBuf_malloc(22); + int resp_aia_len; + + // receive command + uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + + // Prepare card messages + tosend_t *ts = get_tosend(); + + // First card answer: SOF + CodeIClassTagSOF(); + memcpy(resp_sof, ts->buf, ts->max); + resp_sof_len = ts->max; + + // Anticollision CSN + CodeIso15693AsTag(anticoll_data, sizeof(anticoll_data)); + memcpy(resp_anticoll, ts->buf, ts->max); + resp_anticoll_len = ts->max; + + // CSN (block 0) + CodeIso15693AsTag(csn_data, sizeof(csn_data)); + memcpy(resp_csn, ts->buf, ts->max); + resp_csn_len = ts->max; + + // Configuration (block 1) + CodeIso15693AsTag(conf_block, sizeof(conf_block)); + memcpy(resp_conf, ts->buf, ts->max); + resp_conf_len = ts->max; + + // e-Purse (block 2) + CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); + memcpy(resp_cc, ts->buf, ts->max); + resp_cc_len = ts->max; + + // Kd, Kc (blocks 3 and 4) + CodeIso15693AsTag(ff_data, sizeof(ff_data)); + memcpy(resp_ff, ts->buf, ts->max); + resp_ff_len = ts->max; + + // Application Issuer Area (block 5) + CodeIso15693AsTag(aia_data, sizeof(aia_data)); + memcpy(resp_aia, ts->buf, ts->max); + resp_aia_len = ts->max; + + //This is used for responding to READ-block commands or other data which is dynamically generated + //First the 'trace'-data, not encoded for FPGA + uint8_t *data_generic_trace = BigBuf_malloc(34); // 32 bytes data + 2byte CRC is max tag answer + + //Then storage for the modulated data + //Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes) + uint8_t *data_response = BigBuf_malloc((34 * 2) + 3); + + enum { IDLE, ACTIVATED, SELECTED, HALTED } chip_state = IDLE; + + bool button_pressed = false; + uint8_t cmd, options, block; + int len = 0; + bool exit_loop = false; + bool using_kc = false; + int kc_attempt = 0; + + while (exit_loop == false) { + WDT_HIT(); + + // Now look at the reader command and provide appropriate responses + // default is no response: + modulated_response = NULL; + modulated_response_size = 0; + trace_data = NULL; + trace_data_size = 0; + + uint32_t reader_eof_time = 0; + len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time); + if (len < 0) { + button_pressed = true; + exit_loop = true; + continue; + } + + // extra response data + cmd = receivedCmd[0] & 0xF; + options = (receivedCmd[0] >> 4) & 0xFF; + block = receivedCmd[1]; + + if (cmd == ICLASS_CMD_ACTALL && len == 1) { // 0x0A + // Reader in anti collision phase + modulated_response = resp_sof; + modulated_response_size = resp_sof_len; + chip_state = ACTIVATED; + goto send; + + } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY && len == 1) { // 0x0C + // Reader asks for anti collision CSN + if (chip_state == SELECTED || chip_state == ACTIVATED) { + modulated_response = resp_anticoll; + modulated_response_size = resp_anticoll_len; + trace_data = anticoll_data; + trace_data_size = sizeof(anticoll_data); + } + goto send; + + } else if (cmd == ICLASS_CMD_SELECT && len == 9) { + // Reader selects anticollision CSN. + // Tag sends the corresponding real CSN + if (chip_state == ACTIVATED || chip_state == SELECTED) { + if (!memcmp(receivedCmd + 1, anticoll_data, 8)) { + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + chip_state = SELECTED; + } else { + chip_state = IDLE; + } + } else if (chip_state == HALTED || chip_state == IDLE) { + // RESELECT with CSN + if (!memcmp(receivedCmd + 1, csn_data, 8)) { + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + chip_state = SELECTED; + } + } + goto send; + + + } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY && len == 4) { // 0x0C + + if (chip_state != SELECTED) { + goto send; + } + if (simulationMode == ICLASS_SIM_MODE_EXIT_AFTER_MAC) { + // provide defaults for blocks 0 ... 5 + + // block0,1,2,5 is always readable. + switch (block) { + case 0: { // csn (0c 00) + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + goto send; + } + case 1: { // configuration (0c 01) + modulated_response = resp_conf; + modulated_response_size = resp_conf_len; + trace_data = conf_block; + trace_data_size = sizeof(conf_block); + goto send; + } + case 2: {// e-purse (0c 02) + modulated_response = resp_cc; + modulated_response_size = resp_cc_len; + trace_data = card_challenge_data; + trace_data_size = sizeof(card_challenge_data); + // set epurse of sim2,4 attack + if (reader_mac_buf != NULL) { + memcpy(reader_mac_buf, card_challenge_data, 8); + } + goto send; + } + case 3: + case 4: { // Kd, Kc, always respond with 0xff bytes + modulated_response = resp_ff; + modulated_response_size = resp_ff_len; + trace_data = ff_data; + trace_data_size = sizeof(ff_data); + goto send; + } + case 5: { // Application Issuer Area (0c 05) + modulated_response = resp_aia; + modulated_response_size = resp_aia_len; + trace_data = aia_data; + trace_data_size = sizeof(aia_data); + goto send; + } + } // switch + } else if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (block == 3 || block == 4) { // Kd, Kc, always respond with 0xff bytes + modulated_response = resp_ff; + modulated_response_size = resp_ff_len; + trace_data = ff_data; + trace_data_size = sizeof(ff_data); + } else { // use data from emulator memory + memcpy(data_generic_trace, emulator + (current_page * page_size) + (block * 8), 8); + AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ts->buf, ts->max); + modulated_response = data_response; + modulated_response_size = ts->max; + } + goto send; + } + + } else if (cmd == ICLASS_CMD_READCHECK && block == 0x02 && len == 2) { // 0x88 + // Read e-purse KD (88 02) KC (18 02) + if (chip_state != SELECTED) { + goto send; + } + + // debit key + if (receivedCmd[0] == 0x88) { + cipher_state = &cipher_state_KD[current_page]; + diversified_key = diversified_kd; + using_kc = false; + } else { + cipher_state = &cipher_state_KC[current_page]; + diversified_key = diversified_kc; + using_kc = true; + } + + modulated_response = resp_cc; + modulated_response_size = resp_cc_len; + trace_data = card_challenge_data; + trace_data_size = sizeof(card_challenge_data); + goto send; + + } else if (cmd == ICLASS_CMD_CHECK && len == 9) { // 0x05 + + // Reader random and reader MAC!!! + if (chip_state != SELECTED) { + goto send; + } + + if (simulationMode == ICLASS_SIM_MODE_FULL) { + // NR, from reader, is in receivedCmd +1 + opt_doTagMAC_2(*cipher_state, receivedCmd + 1, data_generic_trace, diversified_key); + + /* + uint8_t _mac[4] = {0}; + opt_doReaderMAC_2(*cipher_state, receivedCmd + 1, _mac, diversified_key); + + if (_mac[0] != receivedCmd[5] || _mac[1] != receivedCmd[6] || _mac[2] != receivedCmd[7] || _mac[3] != receivedCmd[8]) { + Dbprintf("reader auth " _RED_("failed")); + Dbprintf("hf iclass lookup u %02x%02x%02x%02x%02x%02x%02x%02x p %02x%02x%02x%02x%02x%02x%02x%02x m %02x%02x%02x%02x%02x%02x%02x%02x f iclass_default_keys.dic", + csn_data[0], csn_data[1], csn_data[2], csn_data[3], csn_data[4], csn_data[5], csn_data[6], csn_data[7], + card_challenge_data[0], card_challenge_data[1], card_challenge_data[2], card_challenge_data[3], + card_challenge_data[4], card_challenge_data[5], card_challenge_data[6], card_challenge_data[7], + receivedCmd[1], receivedCmd[2], receivedCmd[3], receivedCmd[4], + receivedCmd[5], receivedCmd[6], receivedCmd[7], receivedCmd[8] + ); + + goto send; + } + */ + + trace_data = data_generic_trace; + trace_data_size = 4; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ts->buf, ts->max); + modulated_response = data_response; + modulated_response_size = ts->max; + + if (using_kc) + kc_attempt++; + + } else { + // Not fullsim, we don't respond + chip_state = HALTED; + + if (simulationMode == ICLASS_SIM_MODE_EXIT_AFTER_MAC) { + + if (DBGLEVEL == DBG_EXTENDED) { + Dbprintf("CSN: %02x %02x %02x %02x %02x %02x %02x %02x", csn[0], csn[1], csn[2], csn[3], csn[4], csn[5], csn[6], csn[7]); + Dbprintf("RDR: (len=%02d): %02x %02x %02x %02x %02x %02x %02x %02x %02x", len, + receivedCmd[0], receivedCmd[1], receivedCmd[2], + receivedCmd[3], receivedCmd[4], receivedCmd[5], + receivedCmd[6], receivedCmd[7], receivedCmd[8]); + } else { + Dbprintf("CSN: %02x .... %02x OK", csn[0], csn[7]); + } + if (reader_mac_buf != NULL) { + // save NR and MAC for sim 2,4 + memcpy(reader_mac_buf + 8, receivedCmd + 1, 8); + } + exit_loop = true; + } + } + goto send; + + } else if (cmd == ICLASS_CMD_HALT && options == 0 && len == 1) { + + if (chip_state != SELECTED) { + goto send; + } + // Reader ends the session + modulated_response = resp_sof; + modulated_response_size = resp_sof_len; + chip_state = HALTED; + goto send; + + } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 + + if (chip_state != SELECTED) { + goto send; + } + //Read block + memcpy(data_generic_trace, emulator + (current_page * page_size) + (block * 8), 32); + AddCrc(data_generic_trace, 32); + trace_data = data_generic_trace; + trace_data_size = 34; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ts->buf, ts->max); + modulated_response = data_response; + modulated_response_size = ts->max; + goto send; + + } else if (cmd == ICLASS_CMD_UPDATE && (len == 12 || len == 14)) { + + // We're expected to respond with the data+crc, exactly what's already in the receivedCmd + // receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b + if (chip_state != SELECTED) { + goto send; + } + // is chip in ReadOnly (RO) + if ((block_wr_lock & 0x80) == 0) goto send; + + if (block == 12 && (block_wr_lock & 0x40) == 0) goto send; + if (block == 11 && (block_wr_lock & 0x20) == 0) goto send; + if (block == 10 && (block_wr_lock & 0x10) == 0) goto send; + if (block == 9 && (block_wr_lock & 0x08) == 0) goto send; + if (block == 8 && (block_wr_lock & 0x04) == 0) goto send; + if (block == 7 && (block_wr_lock & 0x02) == 0) goto send; + if (block == 6 && (block_wr_lock & 0x01) == 0) goto send; + + if (block == 2) { // update e-purse + memcpy(card_challenge_data, receivedCmd + 2, 8); + CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); + memcpy(resp_cc, ts->buf, ts->max); + resp_cc_len = ts->max; + cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); + cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); + if (simulationMode == ICLASS_SIM_MODE_FULL) { + memcpy(emulator + (current_page * page_size) + (8 * 2), card_challenge_data, 8); + } + } else if (block == 3) { // update Kd + for (int i = 0; i < 8; i++) { + if (personalization_mode) { + diversified_kd[i] = receivedCmd[2 + i]; + } else { + diversified_kd[i] ^= receivedCmd[2 + i]; + } + } + cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); + if (simulationMode == ICLASS_SIM_MODE_FULL) { + memcpy(emulator + (current_page * page_size) + (8 * 3), diversified_kd, 8); + } + } else if (block == 4) { // update Kc + for (int i = 0; i < 8; i++) { + if (personalization_mode) { + diversified_kc[i] = receivedCmd[2 + i]; + } else { + diversified_kc[i] ^= receivedCmd[2 + i]; + } + } + cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); + if (simulationMode == ICLASS_SIM_MODE_FULL) { + memcpy(emulator + (current_page * page_size) + (8 * 4), diversified_kc, 8); + } + } else if (simulationMode == ICLASS_SIM_MODE_FULL) { + // update emulator memory + memcpy(emulator + (current_page * page_size) + (8 * block), receivedCmd + 2, 8); + } + + memcpy(data_generic_trace, receivedCmd + 2, 8); + AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ts->buf, ts->max); + modulated_response = data_response; + modulated_response_size = ts->max; + goto send; + + } else if (cmd == ICLASS_CMD_PAGESEL && len == 4) { // 0x84 + // Pagesel, + // - enables to select a page in the selected chip memory and return its configuration block + // Chips with a single page will not answer to this command + // Otherwise, we should answer 8bytes (conf block 1) + 2bytes CRC + if (chip_state != SELECTED) { + goto send; + } + + if (simulationMode == ICLASS_SIM_MODE_FULL && max_page > 0) { + + // if on 2k, always ignore 3msb, & 0x1F) + uint8_t page = receivedCmd[1] & 0x1F; + if (page > max_page) { + goto send; + } + + current_page = page; + + memcpy(data_generic_trace, emulator + (current_page * page_size) + (8 * 1), 8); + memcpy(diversified_kd, emulator + (current_page * page_size) + (8 * 3), 8); + memcpy(diversified_kc, emulator + (current_page * page_size) + (8 * 4), 8); + + cipher_state = &cipher_state_KD[current_page]; + + personalization_mode = data_generic_trace[7] & 0x80; + block_wr_lock = data_generic_trace[3]; + + AddCrc(data_generic_trace, 8); + + trace_data = data_generic_trace; + trace_data_size = 10; + + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ts->buf, ts->max); + modulated_response = data_response; + modulated_response_size = ts->max; + } + goto send; + + } else if (cmd == ICLASS_CMD_DETECT) { // 0x0F + // not supported yet, ignore + } else if (cmd == 0x26 && len == 5) { + // standard ISO15693 INVENTORY command. Ignore. + } else { + // Never seen this command before + if (DBGLEVEL >= DBG_EXTENDED) + print_result("Unhandled command received ", receivedCmd, len); + } + +send: + /** + A legit tag has about 330us delay between reader EOT and tag SOF. + **/ + if (modulated_response_size > 0) { + uint32_t response_time = reader_eof_time + DELAY_ICLASS_VCD_TO_VICC_SIM; + TransmitTo15693Reader(modulated_response, modulated_response_size, &response_time, 0, false); + LogTrace_ISO15693(trace_data, trace_data_size, response_time * 32, (response_time * 32) + (modulated_response_size * 32 * 64), NULL, false); + } + + if (chip_state == HALTED) { + uint32_t wait_time = GetCountSspClk() + ICLASS_READER_TIMEOUT_ACTALL; + while (GetCountSspClk() < wait_time) {}; + } + + // CC attack + // wait to trigger the reader bug, then wait 1000ms + if (kc_attempt > 3) { + uint32_t wait_time = GetCountSspClk() + (16000 * 100); + while (GetCountSspClk() < wait_time) {}; + kc_attempt = 0; + exit_loop = true; + } + } + + LEDsoff(); + + if (button_pressed) + DbpString("button pressed"); + + return button_pressed; +} + +int do_iclass_simulation_nonsec(void) { + // free eventually allocated BigBuf memory + BigBuf_free_keep_EM(); + + uint16_t page_size = 32 * 8; + uint8_t current_page = 0; + + uint8_t *emulator = BigBuf_get_EM_addr(); + uint8_t *csn = emulator; + + // CSN followed by two CRC bytes + uint8_t anticoll_data[10] = { 0 }; + uint8_t csn_data[10] = { 0 }; + memcpy(csn_data, csn, sizeof(csn_data)); + + // Construct anticollision-CSN + rotateCSN(csn_data, anticoll_data); + + // Compute CRC on both CSNs + AddCrc(anticoll_data, 8); + AddCrc(csn_data, 8); + + // configuration block + uint8_t conf_block[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00}; + + // AIA + uint8_t aia_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; + + memcpy(conf_block, emulator + (8 * 1), 8); + memcpy(aia_data, emulator + (8 * 2), 8); + + AddCrc(conf_block, 8); + AddCrc(aia_data, 8); + + if ((conf_block[5] & 0x80) == 0x80) { + page_size = 256 * 8; + } + + // chip memory may be divided in 8 pages + uint8_t max_page = ((conf_block[4] & 0x10) == 0x10) ? 0 : 7; + + // Anti-collision process: // Reader 0a // Tag 0f // Reader 0c @@ -1341,7 +958,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // Respond SOF -- takes 1 bytes uint8_t *resp_sof = BigBuf_malloc(2); - int resp_sof_Len; + int resp_sof_len; // Anticollision CSN (rotated CSN) // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) @@ -1353,286 +970,256 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t *resp_csn = BigBuf_malloc(28); int resp_csn_len; - // configuration Picopass 2ks + // configuration (blk 1) PICOPASS 2ks uint8_t *resp_conf = BigBuf_malloc(28); int resp_conf_len; - uint8_t conf_data[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00}; - AddCrc(conf_data, 8); - // e-Purse - // 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit) - uint8_t *resp_cc = BigBuf_malloc(28); - int resp_cc_len; - - // Application Issuer Area + // Application Issuer Area (blk 5) uint8_t *resp_aia = BigBuf_malloc(28); int resp_aia_len; - uint8_t aia_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; - if (simulationMode == MODE_FULLSIM) { - - // (iceman) this only works for 2KS / 16KS tags. - // Use application data from block 5 - memcpy(aia_data, emulator + (8 * 5), 8); - - // older 2K / 16K tags has its application issuer data on block 2 - } - AddCrc(aia_data, 8); // receive command uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); - int len = 0; // Prepare card messages - ToSendMax = 0; + tosend_t *ts = get_tosend(); + ts->max = 0; // First card answer: SOF CodeIClassTagSOF(); - memcpy(resp_sof, ToSend, ToSendMax); - resp_sof_Len = ToSendMax; + memcpy(resp_sof, ts->buf, ts->max); + resp_sof_len = ts->max; // Anticollision CSN - CodeIClassTagAnswer(anticoll_data, sizeof(anticoll_data)); - memcpy(resp_anticoll, ToSend, ToSendMax); - resp_anticoll_len = ToSendMax; + CodeIso15693AsTag(anticoll_data, sizeof(anticoll_data)); + memcpy(resp_anticoll, ts->buf, ts->max); + resp_anticoll_len = ts->max; - // CSN - CodeIClassTagAnswer(csn_data, sizeof(csn_data)); - memcpy(resp_csn, ToSend, ToSendMax); - resp_csn_len = ToSendMax; + // CSN (block 0) + CodeIso15693AsTag(csn_data, sizeof(csn_data)); + memcpy(resp_csn, ts->buf, ts->max); + resp_csn_len = ts->max; - // Configuration - CodeIClassTagAnswer(conf_data, sizeof(conf_data)); - memcpy(resp_conf, ToSend, ToSendMax); - resp_conf_len = ToSendMax; + // Configuration (block 1) + CodeIso15693AsTag(conf_block, sizeof(conf_block)); + memcpy(resp_conf, ts->buf, ts->max); + resp_conf_len = ts->max; - // e-Purse - CodeIClassTagAnswer(card_challenge_data, sizeof(card_challenge_data)); - memcpy(resp_cc, ToSend, ToSendMax); - resp_cc_len = ToSendMax; - - // Application Issuer Area - CodeIClassTagAnswer(aia_data, sizeof(aia_data)); - memcpy(resp_aia, ToSend, ToSendMax); - resp_aia_len = ToSendMax; + // Application Issuer Area (block 2) + CodeIso15693AsTag(aia_data, sizeof(aia_data)); + memcpy(resp_aia, ts->buf, ts->max); + resp_aia_len = ts->max; //This is used for responding to READ-block commands or other data which is dynamically generated //First the 'trace'-data, not encoded for FPGA - uint8_t *data_generic_trace = BigBuf_malloc((8 * 4) + 2);//8 bytes data + 2byte CRC is max tag answer + uint8_t *data_generic_trace = BigBuf_malloc(32 + 2); // 32 bytes data + 2byte CRC is max tag answer //Then storage for the modulated data //Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes) - uint8_t *data_response = BigBuf_malloc(((8 * 4) + 2) * 2 + 2); + uint8_t *data_response = BigBuf_malloc((32 + 2) * 2 + 2); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); - SpinDelay(100); - StartCountSspClk(); + enum { IDLE, ACTIVATED, SELECTED, HALTED } chip_state = IDLE; - // To control where we are in the protocol - uint32_t time_0 = GetCountSspClk(); - uint32_t t2r_stime = 0, t2r_etime = 0; - uint32_t r2t_stime, r2t_etime = 0; - LED_A_ON(); - bool buttonPressed = false; + bool button_pressed = false; uint8_t cmd, options, block; + int len = 0; - while (!exitLoop) { + bool exit_loop = false; + while (exit_loop == false) { WDT_HIT(); - //Signal tracer, can be used to get a trigger for an oscilloscope.. - LED_B_OFF(); - LED_C_OFF(); - - r2t_stime = (GetCountSspClk() - time_0) << 4; - if (!GetIClassCommandFromReader(receivedCmd, &len, 0)) { - buttonPressed = true; - exitLoop = true; + uint32_t reader_eof_time = 0; + len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time); + if (len < 0) { + button_pressed = true; + exit_loop = true; continue; } - r2t_etime = ((GetCountSspClk() - time_0) << 4) - r2t_stime; - // 330us normal wait, adjusted for our execution - - LED_C_ON(); //Signal tracer + // Now look at the reader command and provide appropriate responses + // default is no response: + modulated_response = NULL; + modulated_response_size = 0; + trace_data = NULL; + trace_data_size = 0; + // extra response data cmd = receivedCmd[0] & 0xF; options = (receivedCmd[0] >> 4) & 0xFF; block = receivedCmd[1]; - if (cmd == ICLASS_CMD_ACTALL) { // 0x0A - // Reader in anticollission phase - modulated_response = resp_sof; - modulated_response_size = resp_sof_Len; //order = 1; - trace_data = sof_data; - trace_data_size = sizeof(sof_data); - // adjusted for 330 + (160*num of slot) + if (cmd == ICLASS_CMD_ACTALL && len == 1) { // 0x0A + // Reader in anti collision phase + if (chip_state != HALTED) { + modulated_response = resp_sof; + modulated_response_size = resp_sof_len; + chip_state = ACTIVATED; + } goto send; - } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY) { // 0x0C - if (len == 1) { - // Reader asks for anticollission CSN + + } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY && len == 1) { // 0x0C + // Reader asks for anti collision CSN + if (chip_state == SELECTED || chip_state == ACTIVATED) { modulated_response = resp_anticoll; - modulated_response_size = resp_anticoll_len; //order = 2; + modulated_response_size = resp_anticoll_len; trace_data = anticoll_data; trace_data_size = sizeof(anticoll_data); - goto send; } + goto send; - if (len == 4) { - // block0,1,2,5 is always readable. - switch (block) { - case 0: // csn (0c 00) - modulated_response = resp_csn; - modulated_response_size = resp_csn_len; - trace_data = csn_data; - trace_data_size = sizeof(csn_data); - goto send; - case 1: // configuration (0c 01) - modulated_response = resp_conf; - modulated_response_size = resp_conf_len; - trace_data = conf_data; - trace_data_size = sizeof(conf_data); - goto send; - case 2: // e-purse (0c 02) - modulated_response = resp_cc; - modulated_response_size = resp_cc_len; - trace_data = card_challenge_data; - trace_data_size = sizeof(card_challenge_data); - // set epurse of sim2,4 attack - if (reader_mac_buf != NULL) { - memcpy(reader_mac_buf, card_challenge_data, 8); - } - goto send; - case 5:// Application Issuer Area (0c 05) - modulated_response = resp_aia; - modulated_response_size = resp_aia_len; - trace_data = aia_data; - trace_data_size = sizeof(aia_data); - goto send; - default : { - if (simulationMode == MODE_FULLSIM) { // 0x0C - //Read block - //Take the data... - memcpy(data_generic_trace, emulator + (block << 3), 8); - AddCrc(data_generic_trace, 8); - trace_data = data_generic_trace; - trace_data_size = 10; - CodeIClassTagAnswer(trace_data, trace_data_size); - memcpy(modulated_response, ToSend, ToSendMax); - modulated_response_size = ToSendMax; - goto send; - } - break; - } - }//swith - }// if 4 - } else if (cmd == ICLASS_CMD_SELECT) { // 0x81 - // Reader selects anticollission CSN. + } else if (cmd == ICLASS_CMD_SELECT && len == 9) { + // Reader selects anticollision CSN. // Tag sends the corresponding real CSN - modulated_response = resp_csn; - modulated_response_size = resp_csn_len; //order = 3; - trace_data = csn_data; - trace_data_size = sizeof(csn_data); - goto send; - } else if (cmd == ICLASS_CMD_READCHECK) { // 0x88 - // Read e-purse KD (88 02) KC (18 02) - modulated_response = resp_cc; - modulated_response_size = resp_cc_len; //order = 4; - trace_data = card_challenge_data; - trace_data_size = sizeof(card_challenge_data); - LED_B_ON(); - goto send; - } else if (cmd == ICLASS_CMD_CHECK) { // 0x05 - // Reader random and reader MAC!!! - if (simulationMode == MODE_FULLSIM) { - // NR, from reader, is in receivedCmd +1 - opt_doTagMAC_2(cipher_state, receivedCmd + 1, data_generic_trace, diversified_key); - - trace_data = data_generic_trace; - trace_data_size = 4; - CodeIClassTagAnswer(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); - modulated_response = data_response; - modulated_response_size = ToSendMax; - } else { - // Not fullsim, we don't respond - // We do not know what to answer, so lets keep quiet - modulated_response = resp_sof; - modulated_response_size = 0; - trace_data = NULL; - trace_data_size = 0; - - if (simulationMode == MODE_EXIT_AFTER_MAC) { - - if (DBGLEVEL == DBG_EXTENDED) { - Dbprintf("[+] CSN: %02x %02x %02x %02x %02x %02x %02x %02x", csn[0], csn[1], csn[2], csn[3], csn[4], csn[5], csn[6], csn[7]); - Dbprintf("[+] RDR: (len=%02d): %02x %02x %02x %02x %02x %02x %02x %02x %02x", len, - receivedCmd[0], receivedCmd[1], receivedCmd[2], - receivedCmd[3], receivedCmd[4], receivedCmd[5], - receivedCmd[6], receivedCmd[7], receivedCmd[8]); - } else { - Dbprintf("[+] CSN: %02x .... %02x OK", csn[0], csn[7]); - } - if (reader_mac_buf != NULL) { - memcpy(reader_mac_buf + 8, receivedCmd + 1, 8); - } - exitLoop = true; + if (chip_state == ACTIVATED || chip_state == SELECTED) { + if (!memcmp(receivedCmd + 1, anticoll_data, 8)) { + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + chip_state = SELECTED; + } else { + chip_state = IDLE; + } + } else if (chip_state == HALTED) { + // RESELECT with CSN + if (!memcmp(receivedCmd + 1, csn_data, 8)) { + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + chip_state = SELECTED; } } goto send; + + + } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY && len == 4) { // 0x0C + + if (chip_state != SELECTED) { + goto send; + } + + switch (block) { + case 0: { // csn (0c 00) + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + goto send; + } + case 1: { // configuration (0c 01) + modulated_response = resp_conf; + modulated_response_size = resp_conf_len; + trace_data = conf_block; + trace_data_size = sizeof(conf_block); + goto send; + } + case 2: { // Application Issuer Area (0c 02) + modulated_response = resp_aia; + modulated_response_size = resp_aia_len; + trace_data = aia_data; + trace_data_size = sizeof(aia_data); + goto send; + } + default : { + memcpy(data_generic_trace, emulator + (block << 3), 8); + AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ts->buf, ts->max); + modulated_response = data_response; + modulated_response_size = ts->max; + goto send; + } + } // swith + + } else if (cmd == ICLASS_CMD_READCHECK) { // 0x88 + goto send; + + } else if (cmd == ICLASS_CMD_CHECK && len == 9) { // 0x05 + goto send; + } else if (cmd == ICLASS_CMD_HALT && options == 0 && len == 1) { + + if (chip_state != SELECTED) { + goto send; + } // Reader ends the session modulated_response = resp_sof; - modulated_response_size = 0; //order = 0; - trace_data = NULL; - trace_data_size = 0; + modulated_response_size = resp_sof_len; + chip_state = HALTED; goto send; - } else if (simulationMode == MODE_FULLSIM && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 + + } else if (cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 + + if (chip_state != SELECTED) { + goto send; + } //Read block - //Take the data... - memcpy(data_generic_trace, emulator + (block << 3), 8 * 4); + memcpy(data_generic_trace, emulator + (current_page * page_size) + (block * 8), 8 * 4); AddCrc(data_generic_trace, 8 * 4); trace_data = data_generic_trace; trace_data_size = 34; - CodeIClassTagAnswer(trace_data, trace_data_size); - memcpy(modulated_response, ToSend, ToSendMax); - modulated_response_size = ToSendMax; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ts->buf, ts->max); + modulated_response = data_response; + modulated_response_size = ts->max; goto send; - } else if (simulationMode == MODE_FULLSIM && cmd == ICLASS_CMD_UPDATE) { - //Probably the reader wants to update the nonce. Let's just ignore that for now. - // OBS! If this is implemented, don't forget to regenerate the cipher_state - //We're expected to respond with the data+crc, exactly what's already in the receivedcmd - //receivedcmd is now UPDATE 1b | ADDRESS 1b| DATA 8b| Signature 4b or CRC 2b| + } else if (cmd == ICLASS_CMD_UPDATE && (len == 12 || len == 14)) { + + // We're expected to respond with the data+crc, exactly what's already in the receivedCmd + // receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b + if (chip_state != SELECTED) { + goto send; + } + + // update emulator memory + memcpy(emulator + (current_page * page_size) + (8 * block), receivedCmd + 2, 8); - //Take the data... memcpy(data_generic_trace, receivedCmd + 2, 8); AddCrc(data_generic_trace, 8); trace_data = data_generic_trace; trace_data_size = 10; - CodeIClassTagAnswer(trace_data, trace_data_size); - - memcpy(data_response, ToSend, ToSendMax); + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ts->buf, ts->max); modulated_response = data_response; - modulated_response_size = ToSendMax; -// response_delay = 4600 * 1.5; // tPROG 4-15ms + modulated_response_size = ts->max; goto send; -// } else if(receivedCmd[0] == ICLASS_CMD_PAGESEL) { // 0x84 - //Pagesel - //Pagesel enables to select a page in the selected chip memory and return its configuration block - //Chips with a single page will not answer to this command - // It appears we're fine ignoring this. - //Otherwise, we should answer 8bytes (block) + 2bytes CRC -// } else if(receivedCmd[0] == ICLASS_CMD_DETECT) { // 0x0F - } else { - //#db# Unknown command received from reader (len=5): 26 1 0 f6 a 44 44 44 44 - // Never seen this command before - if (DBGLEVEL == DBG_EXTENDED) - print_result("[-] Unhandled command received ", receivedCmd, len); - // Do not respond - modulated_response = resp_sof; - modulated_response_size = 0; //order = 0; - trace_data = NULL; - trace_data_size = 0; + } else if (cmd == ICLASS_CMD_PAGESEL && len == 4) { // 0x84 + // Pagesel, + // - enables to select a page in the selected chip memory and return its configuration block + // Chips with a single page will not answer to this command + // Otherwise, we should answer 8bytes (conf block 1) + 2bytes CRC + if (chip_state != SELECTED) { + goto send; + } + + if (max_page > 0) { + + current_page = receivedCmd[1]; + + memcpy(data_generic_trace, emulator + (current_page * page_size) + (8 * 1), 8); + AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ts->buf, ts->max); + modulated_response = data_response; + modulated_response_size = ts->max; + } + goto send; + +// } else if(cmd == ICLASS_CMD_DETECT) { // 0x0F + } else if (cmd == 0x26 && len == 5) { + // standard ISO15693 INVENTORY command. Ignore. + } else { + // Never seen this command before + if (DBGLEVEL >= DBG_EXTENDED) + print_result("Unhandled command received ", receivedCmd, len); } send: @@ -1640,660 +1227,383 @@ send: A legit tag has about 330us delay between reader EOT and tag SOF. **/ if (modulated_response_size > 0) { - t2r_stime = GetCountSspClkDelta(time_0) << 4; - SendIClassAnswer(modulated_response, modulated_response_size, 0); - t2r_etime = ((GetCountSspClk() - time_0) << 4) - t2r_stime; + uint32_t response_time = reader_eof_time + DELAY_ICLASS_VCD_TO_VICC_SIM; + TransmitTo15693Reader(modulated_response, modulated_response_size, &response_time, 0, false); + LogTrace_ISO15693(trace_data, trace_data_size, response_time * 32, (response_time * 32) + (modulated_response_size * 32 * 64), NULL, false); } - - LogTrace(receivedCmd, len, r2t_stime, r2t_etime, NULL, true); - - if (trace_data != NULL) - LogTrace(trace_data, trace_data_size, t2r_stime, t2r_etime, NULL, false); } LEDsoff(); - if (buttonPressed) - DbpString("[+] button pressed"); + if (button_pressed) + DbpString("button pressed"); + + return button_pressed; - return buttonPressed; } -/** - * @brief sends our simulated tag answer - * @param resp - * @param respLen - * @param delay - */ -static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay) { - int i = 0; - volatile uint8_t b; - - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K_8BIT); - - AT91C_BASE_SSC->SSC_THR = 0x00; - - uint16_t checked = 0; - for (;;) { - - if (checked == 1000) { - if (BUTTON_PRESS() || data_available()) return 0; - checked = 0; - } - ++checked; - - // Prevent rx holding register from overflowing - if ((AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) { - b = AT91C_BASE_SSC->SSC_RHR; - (void) b; - } - - // Put byte into tx holding register as soon as it is ready - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - b = 0x00; - if (i < respLen) { - b = resp[i]; - //Hack - //b = 0xAC; - } - i++; - AT91C_BASE_SSC->SSC_THR = b; - } -// if (i > respLen + 4) break; - if (i > respLen + 1) break; - } - return 0; +// THE READER CODE +static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time) { + CodeIso15693AsReader(frame, len); + tosend_t *ts = get_tosend(); + TransmitTo15693Tag(ts->buf, ts->max, start_time); + *end_time = *start_time + (32 * ((8 * ts->max) - 4)); // substract the 4 padding bits after EOF + LogTrace_ISO15693(frame, len, (*start_time * 4), (*end_time * 4), NULL, true); } -/// THE READER CODE +static bool iclass_send_cmd_with_retries(uint8_t *cmd, size_t cmdsize, uint8_t *resp, size_t max_resp_size, + uint8_t expected_size, uint8_t tries, uint32_t *start_time, + uint16_t timeout, uint32_t *eof_time) { + while (tries-- > 0) { -//----------------------------------------------------------------------------- -// Transmit the command (to the tag) that was placed in ToSend[]. -//----------------------------------------------------------------------------- -static void TransmitIClassCommand(const uint8_t *cmd, int len, int *wait) { + iclass_send_as_reader(cmd, cmdsize, start_time, eof_time); - int c = 0; - bool firstpart = true; - uint8_t sendbyte; - - time_rdr = 0; - - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); - - AT91C_BASE_SSC->SSC_THR = 0x00; - - // make sure we timeout previous comms. - if (*wait) - SpinDelayUs(*wait); - - for (;;) { - - WDT_HIT(); - - // Put byte into tx holding register as soon as it is ready - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - - // DOUBLE THE SAMPLES! - if (firstpart) { - sendbyte = (cmd[c] & 0xf0) | (cmd[c] >> 4); - } else { - sendbyte = (cmd[c] & 0x0f) | (cmd[c] << 4); - c++; - } - - if (sendbyte == 0xff) - sendbyte = 0xfe; - - AT91C_BASE_SSC->SSC_THR = sendbyte; - firstpart = !firstpart; - - if (c >= len) break; - } - } - - time_rdr = GetCountSspClk(); -} - -//----------------------------------------------------------------------------- -// Prepare iClass reader command to send to FPGA -//----------------------------------------------------------------------------- -static void CodeIClassCommand(const uint8_t *cmd, int len) { - int i, j, k; - - ToSendReset(); - - // (SOC) Start of Communication: 1 out of 4 - ToSend[++ToSendMax] = 0xf0; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x0f; - ToSend[++ToSendMax] = 0x00; - - // Modulate the bytes - for (i = 0; i < len; i++) { - uint8_t b = cmd[i]; - for (j = 0; j < 4; j++) { - for (k = 0; k < 4; k++) { - - if (k == (b & 3)) - ToSend[++ToSendMax] = 0x0f; - else - ToSend[++ToSendMax] = 0x00; - } - b >>= 2; - } - } - - // (EOC) End of Communication - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0xf0; - ToSend[++ToSendMax] = 0x00; - - // Convert from last character reference to length - ToSendMax++; -} - -static void ReaderTransmitIClass_ext(uint8_t *frame, int len, int wait) { - - // This is tied to other size changes - CodeIClassCommand(frame, len); - - // Select the card - TransmitIClassCommand(ToSend, ToSendMax, &wait); - LED_A_ON(); - - LogTrace(frame, len, g_rsamples, g_rsamples, NULL, true); -} -static void ReaderTransmitIClass(uint8_t *frame, int len) { - ReaderTransmitIClass_ext(frame, len, 330); -} - -//----------------------------------------------------------------------------- -// Wait a certain time for tag response -// If a response is captured return TRUE -// If it takes too long return FALSE -//----------------------------------------------------------------------------- -static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *wait) { - // buffer needs to be 512 bytes - // maxLen is not used... - bool skip = false; - - LED_D_ON(); - // Set FPGA mode to "reader listen mode", no modulation (listen - // only, since we are receiving, not transmitting). - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN); - - // Setup UART/DEMOD to receive - DemodIcInit(receivedResponse); - - SpinDelayUs(g_wait); //310 Tout= 330us (iso15603-2) (330/21.3) take consideration for clock increments. - - // clear RXRDY: - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - (void)b; - - uint16_t checked = 0; - - uint32_t card_start = GetCountSspClk(); - for (;;) { - WDT_HIT(); - - if (checked == 1000) { - if (BUTTON_PRESS() || data_available()) return false; - checked = 0; - } - ++checked; - - // Wait for byte be become available in rx holding register - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - - b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - skip = !skip; - if (skip) continue; - - if (ManchesterDecoding_iclass(b & 0x0f)) { - time_response = GetCountSspClk() - card_start; - return true; - } else if (GetCountSspClkDelta(card_start) > timeout && Demod.state == DEMOD_IC_UNSYNCD) { - return false; - } - } - } - return false; -} - -static int ReaderReceiveIClass(uint8_t *receivedAnswer) { - - if (GetIClassAnswer(receivedAnswer, 0, NULL) == false) - return 0; - - LogTrace(receivedAnswer, Demod.len, g_rsamples, g_rsamples, NULL, false); - return Demod.len; -} - -static void setupIclassReader(void) { - - LEDsoff(); - - // Start from off (no field generated) - // Signal field is off with the appropriate LED - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - - FpgaSetupSsc(); - - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - clear_trace(); - set_tracing(true); - - // Now give it time to spin up. - // Signal field is on with the appropriate LED - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); - SpinDelay(500); - - StartCountSspClk(); - - LED_A_ON(); -} - -static bool sendCmdGetResponseWithRetries(uint8_t *command, size_t cmdsize, uint8_t *resp, uint8_t expected_size, int8_t retries) { - while (retries-- > 0) { - - ReaderTransmitIClass(command, cmdsize); - - //iceman - if received size is bigger than expected, we smash the stack here - // since its called with fixed sized arrays - - // update/write command takes 4ms to 15ms before responding - int old_wait = g_wait; - if ((command[0] & 0xF) == ICLASS_CMD_UPDATE) - g_wait = 3900; - - uint8_t got_n = ReaderReceiveIClass(resp); - - g_wait = old_wait; - - // 0xBB is the internal debug separator byte.. - if (expected_size != got_n || (resp[0] == 0xBB || resp[7] == 0xBB || resp[2] == 0xBB)) { - //try again -// SpinDelayUs(360); - continue; - } - - if (got_n == expected_size) + if (resp == NULL) { return true; + } + + if (expected_size == GetIso15693AnswerFromTag(resp, max_resp_size, timeout, eof_time)) { + return true; + } } return false; } /** * @brief Talks to an iclass tag, sends the commands to get CSN and CC. - * @param card_data where the CSN and CC are stored for return - * @return 0 = fail - * 1 = Got CSN - * 2 = Got CSN and CC + * @param card_data where the CSN, CONFIG, CC are stored for return + * 8 bytes csn + 8 bytes config + 8 bytes CC + * @return false = fail + * true = Got all. */ -static uint8_t handshakeIclassTag_ext(uint8_t *card_data, bool use_credit_key) { - - // act_all... - static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; - static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x00, 0x73, 0x33 }; - static uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t readcheck_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; - - // Bit 4: K.If this bit equals to one, the READCHECK will use the Credit Key (Kc); if equals to zero, Debit Key (Kd) willbe used - // bit 7: parity. - - if (use_credit_key) - readcheck_cc[0] = 0x10 | ICLASS_CMD_READCHECK; +static bool select_iclass_tag_ex(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time, uint8_t *status) { + 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 }; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; - // Send act_all ( 330 timeout + 160 timeslot); - ReaderTransmitIClass_ext(act_all, 1, 330 + 180); + picopass_hdr *hdr = (picopass_hdr *)card_data; - // Card present? - if (ReaderReceiveIClass(resp) == 0) - return 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; - //Send Identify - ReaderTransmitIClass(identify, 1); + // wakeup + uint32_t start_time = GetCountSspClk(); + iclass_send_as_reader(act_all, 1, &start_time, eof_time); + int len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_ACTALL, eof_time); + if (len < 0) + return false; - //We expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC - if (ReaderReceiveIClass(resp) != 10) - return 0; + // send Identify + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(identify, 1, &start_time, eof_time); - //Copy the Anti-collision CSN to our select-packet + // expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) + return false; + + // copy the Anti-collision CSN to our select-packet memcpy(&select[1], resp, 8); - //Select the card - ReaderTransmitIClass(select, sizeof(select)); + // select the card + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(select, sizeof(select), &start_time, eof_time); - //We expect a 10-byte response here, 8 byte CSN and 2 byte CRC - if (ReaderReceiveIClass(resp) != 10) - return 0; + // expect a 10-byte response here, 8 byte CSN and 2 byte CRC + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) + return false; - // Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) - // ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc)); - // if (ReaderReceiveIClass(resp) == 8) { - // //Save CC (e-purse) in response data - // memcpy(card_data+8, resp, 8); - // read_status++; - // } + // save CSN + memcpy(hdr->csn, resp, sizeof(hdr->csn)); - //Success - level 1, we got CSN - //Save CSN in response data - memcpy(card_data, resp, 8); + // 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); - bool isBlk_2 = sendCmdGetResponseWithRetries(readcheck_cc, sizeof(readcheck_cc), resp, 8, 3); + // expect a 8-byte response here + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) + return false; - //Flag that we got to at least stage 1, read CSN - if (isBlk_2 == false) { - return 1; + // save CONF + memcpy((uint8_t *)&hdr->conf, resp, sizeof(hdr->conf)); + + if (status) + *status |= (FLAG_ICLASS_CSN | FLAG_ICLASS_CONF); + + uint8_t pagemap = get_pagemap(hdr); + if (pagemap != PICOPASS_NON_SECURE_PAGEMODE) { + + // 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); + + // expect a 10-byte response here + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) + return false; + + if (status) { + *status |= FLAG_ICLASS_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); + + // expect a 8-byte response here + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 8) + return false; + + memcpy(hdr->epurse, resp, sizeof(hdr->epurse)); + *status |= FLAG_ICLASS_CC; + + } else { + + // read App Issuer Area block 2 + read_aia[1] = 0x02; + read_aia[2] = 0x61; + read_aia[3] = 0x10; + + start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_aia, sizeof(read_aia), &start_time, eof_time); + + // expect a 10-byte response here + len = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (len != 10) + return false; + + if (status) { + *status |= FLAG_ICLASS_AIA; + memcpy(card_data + (8 * 2), resp, 8); + } } - //Save CC (e-purse) in response data - memcpy(card_data + 8, resp, 8); - - // we got all data; - return 2; + return true; } -static uint8_t handshakeIclassTag(uint8_t *card_data) { - return handshakeIclassTag_ext(card_data, false); + +bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time) { + uint8_t result = 0; + return select_iclass_tag_ex(card_data, use_credit_key, eof_time, &result); } // Reader iClass Anticollission // turn off afterwards -void ReaderIClass(uint8_t arg0) { +void ReaderIClass(uint8_t flags) { - uint8_t card_data[6 * 8] = {0}; - uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t resp[ICLASS_BUFFER_SIZE]; - - memset(card_data, 0xFF, sizeof(card_data)); + uint8_t card_data[6 * 8] = {0xFF}; +// uint8_t last_csn[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; memset(resp, 0xFF, sizeof(resp)); - //Read conf block CRC(0x01) => 0xfa 0x22 - uint8_t readConf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22}; +// bool flag_readonce = flags & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully + bool use_credit_key = flags & FLAG_ICLASS_READER_CREDITKEY; // flag to use credit key - //Read App Issuer Area block CRC(0x05) => 0xde 0x64 - uint8_t readAA[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; - - uint16_t tryCnt = 0; - - bool abort_after_read = arg0 & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully - bool try_once = arg0 & FLAG_ICLASS_READER_ONE_TRY; // flag to not to loop continuously, looking for tag - bool use_credit_key = arg0 & FLAG_ICLASS_READER_CEDITKEY; // flag to use credit key - bool flagReadConfig = arg0 & FLAG_ICLASS_READER_CONF; // flag to read block1, configuration - bool flagReadCC = arg0 & FLAG_ICLASS_READER_CC; // flag to read block2, e-purse - bool flagReadAIA = arg0 & FLAG_ICLASS_READER_AIA; // flag to read block5, application issuer area - - setupIclassReader(); - - uint16_t checked = 0; - bool userCancelled = BUTTON_PRESS() || data_available(); - while (!userCancelled) { - - WDT_HIT(); - - // if only looking for one card try 2 times if we missed it the first time - if (try_once && tryCnt > 10) { - if (DBGLEVEL > 1) DbpString("Failed to find a tag"); - break; - } - - tryCnt++; - uint8_t result_status = 0; - - int read_status = handshakeIclassTag_ext(card_data, use_credit_key); - - if (read_status == 0) continue; - if (read_status == 1) result_status = FLAG_ICLASS_READER_CSN; - if (read_status == 2) result_status = FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CC; - - // handshakeIclass returns CSN|CC, but the actual block - // layout is CSN|CONFIG|CC, so here we reorder the data, - // moving CC forward 8 bytes - memcpy(card_data + 16, card_data + 8, 8); - - //Read block 1, config - if (flagReadConfig) { - if (sendCmdGetResponseWithRetries(readConf, sizeof(readConf), resp, 10, 5)) { - result_status |= FLAG_ICLASS_READER_CONF; - memcpy(card_data + 8, resp, 8); - } else { - if (DBGLEVEL > 1) DbpString("Failed to dump config block"); - } - } - - //Read block 5, AIA - if (flagReadAIA) { - if (sendCmdGetResponseWithRetries(readAA, sizeof(readAA), resp, 10, 5)) { - result_status |= FLAG_ICLASS_READER_AIA; - memcpy(card_data + (8 * 5), resp, 8); - } else { - if (DBGLEVEL > 1) DbpString("Failed to dump AA block"); - } - } - - // 0 : CSN - // 1 : Configuration - // 2 : e-purse - // 3 : kd / debit / aa2 (write-only) - // 4 : kc / credit / aa1 (write-only) - // 5 : AIA, Application issuer area - // - //Then we can 'ship' back the 6 * 8 bytes of data, - // with 0xFF:s in block 3 and 4. - - LED_B_ON(); - //Send back to client, but don't bother if we already sent this - - // only useful if looping in arm (not try_once && not abort_after_read) - if (memcmp(last_csn, card_data, 8) != 0) { - // If caller requires that we get Conf, CC, AA, continue until we got it - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("STATUS %02X | CSN %c | CONF %c | CC %c | AIA %c | ONCE %c | 1TRY %c", - result_status, - (result_status & FLAG_ICLASS_READER_CSN) ? 'Y' : 'N', - (result_status & FLAG_ICLASS_READER_CONF) ? 'Y' : 'N', - (result_status & FLAG_ICLASS_READER_CC) ? 'Y' : 'N', - (result_status & FLAG_ICLASS_READER_AIA) ? 'Y' : 'N' - ); - Dbprintf(" aar %c | to %c, | uc %c | frc %c | fra %c | cc %c", - abort_after_read ? 'Y' : 'N', - try_once ? 'Y' : 'N', - use_credit_key ? 'Y' : 'N', - flagReadConfig ? 'Y' : 'N', - flagReadAIA ? 'Y' : 'N', - flagReadCC ? 'Y' : 'N' - ); - } - - bool send = (result_status & FLAG_ICLASS_READER_CSN); - if (flagReadCC) - send |= (result_status & FLAG_ICLASS_READER_CC); - if (flagReadAIA) - send |= (result_status & FLAG_ICLASS_READER_AIA); - if (flagReadConfig) - send |= (result_status & FLAG_ICLASS_READER_CONF); - - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("SEND %c", send ? 'y' : 'n'); - - if (send) { - reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); - if (abort_after_read) { - LED_B_OFF(); - return; - } - //Save that we already sent this.... - memcpy(last_csn, card_data, 8); - } - } - LED_B_OFF(); - - if (checked == 1000) { - userCancelled = BUTTON_PRESS() || data_available(); - checked = 0; - } - ++checked; + if ((flags & FLAG_ICLASS_READER_INIT) == FLAG_ICLASS_READER_INIT) { + Iso15693InitReader(); } - if (userCancelled) { + if ((flags & FLAG_ICLASS_READER_CLEARTRACE) == FLAG_ICLASS_READER_CLEARTRACE) { + clear_trace(); + } + + uint8_t result_status = 0; + uint32_t eof_time = 0; + bool status = select_iclass_tag_ex(card_data, use_credit_key, &eof_time, &result_status); + if (status == false) { reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); switch_off(); - } else { - reply_mix(CMD_ACK, 0, 0, 0, card_data, 0); + return; } -} -// turn off afterwards -void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { - uint8_t cardsize = 0; - uint8_t mem = 0; - uint8_t check[] = { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t read[] = { 0x0c, 0x00, 0x00, 0x00 }; - uint8_t card_data[PM3_CMD_DATA_SIZE] = {0}; - uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; + // Page mapping for secure mode + // 0 : CSN + // 1 : Configuration + // 2 : e-purse + // 3 : kd / debit / aa2 (write-only) + // 4 : kc / credit / aa1 (write-only) + // 5 : AIA, Application issuer area + // + // Page mapping for non secure mode + // 0 : CSN + // 1 : Configuration + // 2 : AIA, Application issuer area - static struct memory_t { - int k16; - int book; - int k2; - int lockauth; - int keyaccess; - } memory; + // Return to client, e 6 * 8 bytes of data. + // with 0xFF:s in block 3 and 4. - setupIclassReader(); + LED_B_ON(); + reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); - while (!BUTTON_PRESS()) { + //Send back to client, but don't bother if we already sent this - + // only useful if looping in arm (not try_once && not abort_after_read) + /* + if (memcmp(last_csn, card_data, 8) != 0) { - WDT_HIT(); - - uint8_t read_status = handshakeIclassTag(card_data); - if (read_status < 2) continue; - - //for now replay captured auth (as cc not updated) - memcpy(check + 5, mac, 4); - - if (!sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 5)) { - DbpString("Error: Authentication Fail!"); - continue; + reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); + if (flag_readonce) { + LED_B_OFF(); + return; } - - //first get configuration block (block 1) - read[1] = 1; - AddCrc(read + 1, 1); - - if (!sendCmdGetResponseWithRetries(read, sizeof(read), resp, 10, 5)) { - DbpString("Dump config (block 1) failed"); - continue; - } - - mem = resp[5]; - memory.k16 = (mem & 0x80); - memory.book = (mem & 0x20); - memory.k2 = (mem & 0x8); - memory.lockauth = (mem & 0x2); - memory.keyaccess = (mem & 0x1); - - cardsize = memory.k16 ? 255 : 32; - - WDT_HIT(); - //Set card_data to all zeroes, we'll fill it with data - memset(card_data, 0x0, PM3_CMD_DATA_SIZE); - uint8_t failedRead = 0; - uint32_t stored_data_length = 0; - - //then loop around remaining blocks - for (uint16_t block = 0; block < cardsize; block++) { - - read[1] = block; - AddCrc(read + 1, 1); - - if (sendCmdGetResponseWithRetries(read, sizeof(read), resp, 10, 5)) { - Dbprintf(" %02x: %02x %02x %02x %02x %02x %02x %02x %02x", - block, resp[0], resp[1], resp[2], - resp[3], resp[4], resp[5], - resp[6], resp[7] - ); - - //Fill up the buffer - memcpy(card_data + stored_data_length, resp, 8); - stored_data_length += 8; - if (stored_data_length + 8 > PM3_CMD_DATA_SIZE) { - //Time to send this off and start afresh - reply_old(CMD_ACK, - stored_data_length,//data length - failedRead,//Failed blocks? - 0,//Not used ATM - card_data, - stored_data_length - ); - //reset - stored_data_length = 0; - failedRead = 0; - } - } else { - failedRead = 1; - stored_data_length += 8;//Otherwise, data becomes misaligned - Dbprintf("Failed to dump block %d", block); - } - } - - //Send off any remaining data - if (stored_data_length > 0) { - reply_old(CMD_ACK, - stored_data_length,//data length - failedRead,//Failed blocks? - 0,//Not used ATM - card_data, - stored_data_length - ); - } - //If we got here, let's break - break; + LED_B_OFF(); } - //Signal end of transmission - reply_old(CMD_ACK, - 0,//data length - 0,//Failed blocks? - 0,//Not used ATM - card_data, - 0 - ); + */ + +// if (userCancelled) { +// reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); +// switch_off(); +// } else { +// reply_mix(CMD_ACK, result_status, 0, 0, card_data, 0); +// } + switch_off(); } -// not used. ?!? ( CMD_HF_ICLASS_READCHECK) // turn off afterwards -void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { - uint8_t readcheck[] = { keytype, blockno }; - uint8_t resp[] = {0, 0, 0, 0, 0, 0, 0, 0}; - size_t isOK = 0; - isOK = sendCmdGetResponseWithRetries(readcheck, sizeof(readcheck), resp, sizeof(resp), 6); - reply_mix(CMD_ACK, isOK, 0, 0, 0, 0); +void ReaderIClass_Replay(uint8_t *rnr, uint8_t *mac) { + + BigBuf_free(); + + uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + memcpy(check + 1, rnr, 4); + memcpy(check + 5, mac, 4); + + uint8_t *card_data = BigBuf_malloc(ICLASS_16KS_SIZE); + if (card_data == NULL) { + DbpString("fail to allocate memory"); + reply_ng(CMD_HF_ICLASS_REPLAY, PM3_EMALLOC, NULL, 0); + return; + } + memset(card_data, 0xFF, ICLASS_16KS_SIZE); + + uint32_t start_time = 0; + uint32_t eof_time = 0; + + Iso15693InitReader(); + + picopass_hdr hdr = {0}; + bool res = select_iclass_tag((uint8_t *)&hdr, false, &eof_time); + if (res == false) { + reply_ng(CMD_HF_ICLASS_REPLAY, PM3_ETIMEOUT, NULL, 0); + switch_off(); + return; + } + + uint8_t resp[10] = {0}; + + //for now replay captured auth (as cc not updated) + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + if (res == false) { + reply_ng(CMD_HF_ICLASS_REPLAY, PM3_ETIMEOUT, NULL, 0); + switch_off(); + return; + } + + uint8_t mem = hdr.conf.mem_config; + uint8_t cardsize = ((mem & 0x80) == 0x80) ? 255 : 32; + + /* + static struct memory_t { + int k16; + int book; + int k2; + int lockauth; + int keyaccess; + } memory; + + // memory.k16 = ((mem & 0x80) == 0x80); + // memory.book = ((mem & 0x20) == 0x20); + // memory.k2 = ((mem & 0x08) == 0x08); + // memory.lockauth = ((mem & 0x02) == 0x02); + // memory.keyaccess = ((mem & 0x01) == 0x01); + // uint8_t cardsize = memory.k16 ? 255 : 32; + */ + + bool dumpsuccess = true; + + // main read loop + uint16_t i; + for (i = 0; i <= cardsize; i++) { + + uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, i, 0x00, 0x00}; + AddCrc(c + 1, 1); + + res = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + if (res) { + memcpy(card_data + (8 * i), resp, 8); + } else { + Dbprintf("failed to read block %u ( 0x%02x)", i, i); + dumpsuccess = false; + } + } + + struct p { + bool isOK; + uint16_t block_cnt; + uint32_t bb_offset; + } PACKED response; + + response.isOK = dumpsuccess; + response.block_cnt = i; + response.bb_offset = card_data - BigBuf_get_addr(); + reply_ng(CMD_HF_ICLASS_REPLAY, PM3_SUCCESS, (uint8_t *)&response, sizeof(response)); + + BigBuf_free(); switch_off(); } // used with function select_and_auth (cmdhficlass.c) // which needs to authenticate before doing more things like read/write -void iClass_Authentication(uint8_t *mac) { - uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t resp[ICLASS_BUFFER_SIZE]; +// selects and authenticate to a card, sends back div_key and mac to client. +void iClass_Authentication(uint8_t *msg) { +} + +bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out) { + + uint8_t cmd_check[9] = { ICLASS_CMD_CHECK }; + uint8_t div_key[8] = {0}; + uint8_t mac[4] = {0}; + uint8_t resp_auth[4] = {0}; + uint8_t ccnr[12] = {0}; + + uint8_t *pmac = mac; + if (mac_out) + pmac = mac_out; + + memcpy(ccnr, hdr->epurse, sizeof(hdr->epurse)); + + if (payload->use_raw) + memcpy(div_key, payload->key, 8); + else + iclass_calc_div_key(hdr->csn, payload->key, div_key, payload->use_elite); + + if (payload->use_credit_key) + memcpy(hdr->key_c, div_key, sizeof(hdr->key_c)); + else + memcpy(hdr->key_d, div_key, sizeof(hdr->key_d)); + + opt_doReaderMAC(ccnr, div_key, pmac); // copy MAC to check command (readersignature) - check[5] = mac[0]; - check[6] = mac[1]; - check[7] = mac[2]; - check[8] = mac[3]; - //memcpy(check+5, mac, 4); + cmd_check[5] = pmac[0]; + cmd_check[6] = pmac[1]; + cmd_check[7] = pmac[2]; + cmd_check[8] = pmac[3]; - // 6 retries - uint8_t isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 6); - reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); + return iclass_send_cmd_with_retries(cmd_check, sizeof(cmd_check), resp_auth, sizeof(resp_auth), 4, 2, start_time, ICLASS_READER_TIMEOUT_OTHERS, eof_time); } typedef struct iclass_premac { @@ -2308,12 +1618,14 @@ typedef struct iclass_premac { * to cover debit and credit key. (AA1/AA2) */ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { + uint8_t i = 0, isOK = 0; - uint8_t lastChunk = ((arg0 >> 8) & 0xFF); +// uint8_t lastChunk = ((arg0 >> 8) & 0xFF); bool use_credit_key = ((arg0 >> 16) & 0xFF); uint8_t keyCount = arg1 & 0xFF; - uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t resp[ICLASS_BUFFER_SIZE]; + + uint8_t check[9] = { ICLASS_CMD_CHECK }; + uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; uint8_t readcheck_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; if (use_credit_key) @@ -2326,39 +1638,28 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { LED_A_ON(); + // fresh start switch_off(); SpinDelay(20); - setupIclassReader(); + Iso15693InitReader(); + uint32_t start_time = 0, eof_time = 0; + + if (select_iclass_tag(card_data, use_credit_key, &eof_time) == false) + goto out; + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + // since select_iclass_tag call sends s readcheck, we start with sending first response. uint16_t checked = 0; - int read_status = 0; - uint8_t startup_limit = 10; - while (read_status != 2) { - - if (checked == 1000) { - if (BUTTON_PRESS() || !data_available()) goto out; - checked = 0; - } - ++checked; - - read_status = handshakeIclassTag_ext(card_data, use_credit_key); - if (startup_limit-- == 0) { - Dbprintf("[-] Handshake status | %d (fail 10)", read_status); - isOK = 99; - goto out; - } - }; - // since handshakeIclassTag_ext call sends s readcheck, we start with sending first response. - - checked = 0; // Keychunk loop for (i = 0; i < keyCount; i++) { // Allow button press / usb cmd to interrupt device if (checked == 1000) { - if (BUTTON_PRESS() || !data_available()) goto out; + if (BUTTON_PRESS() || data_available()) goto out; checked = 0; } ++checked; @@ -2373,125 +1674,338 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { check[8] = keys[i].mac[3]; // expect 4bytes, 3 retries times.. - isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 3); + isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 2, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); if (isOK) goto out; + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; // Auth Sequence MUST begin with reading e-purse. (block2) // Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) - ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc)); + iclass_send_as_reader(readcheck_cc, sizeof(readcheck_cc), &start_time, &eof_time); LED_B_OFF(); } out: // send keyindex. - reply_mix(CMD_ACK, isOK, i, 0, 0, 0); - - if (isOK >= 1 || lastChunk) { - switch_off(); - LED_A_OFF(); - } - - LED_B_OFF(); - LED_C_OFF(); + reply_mix(CMD_HF_ICLASS_CHKKEYS, isOK, i, 0, 0, 0); + switch_off(); } // Tries to read block. -// retries 10times. -bool iClass_ReadBlock(uint8_t blockno, uint8_t *data, uint8_t len) { +// retries 3times. +// reply 8 bytes block +bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, uint32_t *eof_time) { uint8_t resp[10]; - uint8_t cmd[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; - AddCrc(cmd + 1, 1); - // expect size 10, retry 5times - bool isOK = sendCmdGetResponseWithRetries(cmd, sizeof(cmd), resp, 10, 5); - memcpy(data, resp, len); + uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; + AddCrc(c + 1, 1); + bool isOK = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 2, start_time, ICLASS_READER_TIMEOUT_OTHERS, eof_time); + if (isOK) + memcpy(data, resp, 8); return isOK; } // turn off afterwards -// readblock 8 + 2. only want 8. -void iClass_ReadBlk(uint8_t blockno) { - struct p { - bool isOK; - uint8_t blockdata[8]; - } PACKED result; +// send in authentication needed data, if to use auth. +// reply 8 bytes block if send_reply (for client) +void iClass_ReadBlock(uint8_t *msg) { - result.isOK = iClass_ReadBlock(blockno, result.blockdata, sizeof(result.blockdata)); + iclass_auth_req_t *payload = (iclass_auth_req_t *)msg; + + iclass_readblock_resp_t response = { .isOK = true }; + memset(response.data, 0, sizeof(response.data)); + + uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, payload->blockno, 0x00, 0x00}; + AddCrc(cmd_read + 1, 1); + + Iso15693InitReader(); + + // select tag. + uint32_t eof_time = 0; + picopass_hdr hdr = {0}; + bool res = select_iclass_tag((uint8_t *)&hdr, payload->use_credit_key, &eof_time); + if (res == false) { + if (payload->send_reply) { + response.isOK = res; + reply_ng(CMD_HF_ICLASS_READBL, PM3_ETIMEOUT, (uint8_t *)&response, sizeof(response)); + } + goto out; + } + + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + // authenticate + if (payload->do_auth) { + + res = authenticate_iclass_tag(payload, &hdr, &start_time, &eof_time, NULL); + if (res == false) { + if (payload->send_reply) { + response.isOK = res; + reply_ng(CMD_HF_ICLASS_READBL, PM3_ETIMEOUT, (uint8_t *)&response, sizeof(response)); + } + goto out; + } + } + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + // read data + uint8_t resp[10]; + res = iclass_send_cmd_with_retries(cmd_read, sizeof(cmd_read), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + if (res) { + memcpy(response.data, resp, sizeof(response.data)); + if (payload->send_reply) { + reply_ng(CMD_HF_ICLASS_READBL, PM3_SUCCESS, (uint8_t *)&response, sizeof(response)); + } + } else { + if (payload->send_reply) { + response.isOK = res; + reply_ng(CMD_HF_ICLASS_READBL, PM3_ETIMEOUT, (uint8_t *)&response, sizeof(response)); + } + } + +out: switch_off(); - reply_ng(CMD_HF_ICLASS_READBL, PM3_SUCCESS, (uint8_t *)&result, sizeof(result)); } +// Dump command seems to dump a block related portion of card memory. +// I suppose it will need to do an authentatication to AA1, read its blocks by calling this. +// then authenticate AA2, and read those blocks by calling this. +// By the looks at it only 2K cards is supported, or first page dumps on larger cards. // turn off afterwards -void iClass_Dump(uint8_t blockno, uint8_t numblks) { - uint8_t blockdata[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - bool isOK = false; - uint8_t blkCnt = 0; +void iClass_Dump(uint8_t *msg) { BigBuf_free(); - uint8_t *dataout = BigBuf_malloc(255 * 8); + + iclass_dump_req_t *cmd = (iclass_dump_req_t *)msg; + iclass_auth_req_t *req = &cmd->req; + + uint8_t *dataout = BigBuf_malloc(ICLASS_16KS_SIZE); if (dataout == NULL) { - DbpString("[!] fail to allocate memory"); - OnError(1); + DbpString("fail to allocate memory"); + if (req->send_reply) { + reply_ng(CMD_HF_ICLASS_DUMP, PM3_EMALLOC, NULL, 0); + } + switch_off(); return; } - // fill mem with 0xFF - memset(dataout, 0xFF, 255 * 8); + memset(dataout, 0xFF, ICLASS_16KS_SIZE); - for (; blkCnt < numblks; blkCnt++) { - isOK = iClass_ReadBlock(blockno + blkCnt, blockdata, sizeof(blockdata)); + Iso15693InitReader(); - // 0xBB is the internal debug separator byte.. - if (!isOK || (blockdata[0] == 0xBB || blockdata[7] == 0xBB || blockdata[2] == 0xBB)) { //try again - isOK = iClass_ReadBlock(blockno + blkCnt, blockdata, sizeof(blockdata)); - if (!isOK) { - Dbprintf("[!] block %02X failed to read", blkCnt + blockno); - break; - } + // select tag. + uint32_t eof_time = 0; + picopass_hdr hdr = {0}; + bool res = select_iclass_tag((uint8_t *)&hdr, req->use_credit_key, &eof_time); + if (res == false) { + if (req->send_reply) { + reply_ng(CMD_HF_ICLASS_DUMP, PM3_ETIMEOUT, NULL, 0); + } + switch_off(); + return; + } + + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + // authenticate + if (req->do_auth) { + res = authenticate_iclass_tag(req, &hdr, &start_time, &eof_time, NULL); + if (res == false) { + if (req->send_reply) { + reply_ng(CMD_HF_ICLASS_DUMP, PM3_ETIMEOUT, NULL, 0); + } + switch_off(); + return; + } + } + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + bool dumpsuccess = true; + + // main read loop + uint16_t i; + for (i = cmd->start_block; i <= cmd->end_block; i++) { + + uint8_t resp[10]; + uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, i, 0x00, 0x00}; + AddCrc(c + 1, 1); + + res = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + if (res) { + memcpy(dataout + (8 * i), resp, 8); + } else { + Dbprintf("failed to read block %u ( 0x%02x)", i, i); + dumpsuccess = false; } - memcpy(dataout + (blkCnt * 8), blockdata, 8); } switch_off(); - //return pointer to dump memory in arg3 - reply_mix(CMD_ACK, isOK, blkCnt, BigBuf_max_traceLen(), 0, 0); + + // copy diversified key back. + if (req->do_auth) { + if (req->use_credit_key) + memcpy(dataout + (8 * 4), hdr.key_c, 8); + else + memcpy(dataout + (8 * 3), hdr.key_d, 8); + } + + if (req->send_reply) { + struct p { + bool isOK; + uint16_t block_cnt; + uint32_t bb_offset; + } PACKED response; + + response.isOK = dumpsuccess; + response.block_cnt = i; + response.bb_offset = dataout - BigBuf_get_addr(); + reply_ng(CMD_HF_ICLASS_DUMP, PM3_SUCCESS, (uint8_t *)&response, sizeof(response)); + } + BigBuf_free(); } -static bool iClass_WriteBlock_ext(uint8_t blockno, uint8_t *data) { - uint8_t resp[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t write[] = { 0x80 | ICLASS_CMD_UPDATE, blockno, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data) { + + uint8_t write[16] = { 0x80 | ICLASS_CMD_UPDATE, blockno }; memcpy(write + 2, data, 12); // data + mac AddCrc(write + 1, 13); - return sendCmdGetResponseWithRetries(write, sizeof(write), resp, sizeof(resp), 5); + + uint8_t resp[10] = {0}; + uint32_t eof_time = 0, start_time = 0; + bool isOK = iclass_send_cmd_with_retries(write, sizeof(write), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); + if (isOK == false) { + 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)) { + return false; + } + } else if (blockno == 3 || blockno == 4) { + // check response. Key updates always return 0xffffffffffffffff + if (memcmp(all_ff, resp, 8)) { + return false; + } + } else { + // check response. All other updates return unchanged data + if (memcmp(data, resp, 8)) { + return false; + } + } + + return true; } // turn off afterwards -void iClass_WriteBlock(uint8_t blockno, uint8_t *data) { - uint8_t isOK = iClass_WriteBlock_ext(blockno, data); +void iClass_WriteBlock(uint8_t *msg) { + + LED_A_ON(); + + iclass_writeblock_req_t *payload = (iclass_writeblock_req_t *)msg; + + uint8_t write[16] = { 0x80 | ICLASS_CMD_UPDATE, payload->req.blockno }; + + Iso15693InitReader(); + + // select tag. + uint32_t eof_time = 0; + picopass_hdr hdr = {0}; + bool res = select_iclass_tag((uint8_t *)&hdr, payload->req.use_credit_key, &eof_time); + 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; + } + } + + // calc new mac for write + uint8_t wb[9]; + wb[0] = payload->req.blockno; + memcpy(wb + 1, payload->data, 8); + + if (payload->req.use_credit_key) + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + else + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + + memcpy(write + 2, payload->data, 8); // data + memcpy(write + 10, mac, sizeof(mac)); // mac + AddCrc(write + 1, 13); + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + uint8_t resp[10] = {0}; + res = iclass_send_cmd_with_retries(write, sizeof(write), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); + if (res == false) { + goto out; + } + + // verify write + uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + if (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) { + // check response. Key updates always return 0xffffffffffffffff + if (memcmp(all_ff, resp, 8)) { + res = false; + goto out; + } + } else { + // check response. All other updates return unchanged data + if (memcmp(payload->data, resp, 8)) { + res = false; + goto out; + } + } + +out: switch_off(); - reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); + + if (payload->req.send_reply) + reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&res, sizeof(uint8_t)); } // turn off afterwards void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data) { - int i, written = 0; - int total_block = (endblock - startblock) + 1; - for (i = 0; i < total_block; i++) { - // block number - if (iClass_WriteBlock_ext(startblock + i, data + (i * 12))) { - Dbprintf("Write block [%02x] successful", startblock + i); +} + +void iClass_Restore(uint8_t *msg) { + + iclass_restore_req_t *cmd = (iclass_restore_req_t *)msg; +// iclass_auth_req_t *req = &cmd->req; + + LED_A_ON(); + uint16_t written = 0; + uint16_t total_blocks = (cmd->end_block - cmd->start_block) + 1; + for (uint8_t b = cmd->start_block; b < total_blocks; b++) { + + if (iclass_writeblock_ext(b, cmd->data + ((b - cmd->start_block) * 12))) { + Dbprintf("Write block [%02x] successful", b); written++; } else { - Dbprintf("Write block [%02x] failed", startblock + i); + Dbprintf("Write block [%02x] failed", b); } } switch_off(); - - uint8_t isOK = 0; - if (written == total_block) - isOK = 1; - + uint8_t isOK = (written == total_blocks) ? 1 : 0; reply_ng(CMD_HF_ICLASS_CLONE, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); } diff --git a/armsrc/iclass.h b/armsrc/iclass.h index a9edbdfed..3776aa385 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -1,6 +1,7 @@ //----------------------------------------------------------------------------- // Jonathan Westhues, Aug 2005 // Gerhard de Koning Gans, April 2008, May 2011 +// Iceman, August 2020 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -12,18 +13,30 @@ #define __ICLASS_H #include "common.h" +#include "pm3_cmd.h" -void RAMFUNC SniffIClass(void); -void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); +void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string); void ReaderIClass(uint8_t arg0); -void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac); -void iClass_Authentication(uint8_t *mac); -void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain); -void iClass_WriteBlock(uint8_t blockno, uint8_t *data); -void iClass_ReadBlk(uint8_t blockno); -bool iClass_ReadBlock(uint8_t blockno, uint8_t *data, uint8_t len); -void iClass_Dump(uint8_t blockno, uint8_t numblks); -void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data); -void iClass_ReadCheck(uint8_t blockno, uint8_t keytype); +void ReaderIClass_Replay(uint8_t *rnr, uint8_t *mac); +void iClass_WriteBlock(uint8_t *msg); +void iClass_Dump(uint8_t *msg); + +void iClass_Restore(uint8_t *msg); +void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data); + +int do_iclass_simulation_nonsec(void); +int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf); +void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); +void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_t *datain, uint8_t *dataout, uint16_t *dataoutlen); + +void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain); +void iClass_Authentication(uint8_t *bytes); +bool iclass_auth(iclass_auth_req_t *payload, uint8_t *out); + +void iClass_ReadBlock(uint8_t *msg); +bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, uint32_t *eof_time); + +bool select_iclass_tag(uint8_t *card_data, bool use_credit_key, uint32_t *eof_time); +bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out); #endif diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 3b7b096a4..19c16f656 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -122,6 +122,76 @@ static uint32_t LastProxToAirDuration; #define SEC_Y 0x00 #define SEC_Z 0xc0 +/* +Default HF 14a config is set to: + forceanticol = 0 (auto) + forcebcc = 0 (expect valid BCC) + forcecl2 = 0 (auto) + forcecl3 = 0 (auto) + forcerats = 0 (auto) +*/ +static hf14a_config hf14aconfig = { 0, 0, 0, 0, 0 } ; + +void printHf14aConfig(void) { + DbpString(_CYAN_("HF 14a config")); + Dbprintf(" [a] Anticol override....%i %s%s%s", + hf14aconfig.forceanticol, + (hf14aconfig.forceanticol == 0) ? "( " _GREEN_("No") " ) follow standard " : "", + (hf14aconfig.forceanticol == 1) ? "( " _RED_("Yes") " ) always do anticol" : "", + (hf14aconfig.forceanticol == 2) ? "( " _RED_("Yes") " ) always skip anticol" : "" + ); + Dbprintf(" [b] BCC override........%i %s%s%s", + hf14aconfig.forcebcc, + (hf14aconfig.forcebcc == 0) ? "( " _GREEN_("No") " ) follow standard" : "", + (hf14aconfig.forcebcc == 1) ? "( " _RED_("Yes") " ) always do CL2" : "", + (hf14aconfig.forcebcc == 2) ? "( " _RED_("Yes") " ) always use card BCC" : "" + ); + Dbprintf(" [2] CL2 override........%i %s%s%s", + hf14aconfig.forcecl2, + (hf14aconfig.forcecl2 == 0) ? "( " _GREEN_("No") " ) follow standard" : "", + (hf14aconfig.forcecl2 == 1) ? "( " _RED_("Yes") " ) always do CL2" : "", + (hf14aconfig.forcecl2 == 2) ? "( " _RED_("Yes") " ) always skip CL2" : "" + ); + Dbprintf(" [3] CL3 override........%i %s%s%s", + hf14aconfig.forcecl3, + (hf14aconfig.forcecl3 == 0) ? "( " _GREEN_("No") " ) follow standard" : "", + (hf14aconfig.forcecl3 == 1) ? "( " _RED_("Yes") " ) always do CL3" : "", + (hf14aconfig.forcecl3 == 2) ? "( " _RED_("Yes") " ) always skip CL3" : "" + ); + Dbprintf(" [r] RATS override.......%i %s%s%s", + hf14aconfig.forcerats, + (hf14aconfig.forcerats == 0) ? "( " _GREEN_("No") " q follow standard " : "", + (hf14aconfig.forcerats == 1) ? "( " _RED_("Yes") " ) always do RATS" : "", + (hf14aconfig.forcerats == 2) ? "( " _RED_("Yes") " ) always skip RATS" : "" + ); +} + +/** + * Called from the USB-handler to set the 14a configuration + * The 14a config is used for card selection sequence. + * + * Values set to '-1' implies no change + * @brief setSamplingConfig + * @param sc + */ +void setHf14aConfig(hf14a_config *hc) { + + if ((hc->forceanticol >= 0) && (hc->forceanticol <= 2)) + hf14aconfig.forceanticol = hc->forceanticol; + if ((hc->forcebcc >= 0) && (hc->forcebcc <= 2)) + hf14aconfig.forcebcc = hc->forcebcc; + if ((hc->forcecl2 >= 0) && (hc->forcecl2 <= 2)) + hf14aconfig.forcecl2 = hc->forcecl2; + if ((hc->forcecl3 >= 0) && (hc->forcecl3 <= 2)) + hf14aconfig.forcecl3 = hc->forcecl3; + if ((hc->forcerats >= 0) && (hc->forcerats <= 2)) + hf14aconfig.forcerats = hc->forcerats; +} + +hf14a_config *getHf14aConfig(void) { + return &hf14aconfig; +} + void iso14a_set_trigger(bool enable) { g_trigger = enable; } @@ -585,10 +655,6 @@ void RAMFUNC SniffIso14443a(uint8_t param) { uint8_t *receivedResp = BigBuf_malloc(MAX_FRAME_SIZE); uint8_t *receivedRespPar = BigBuf_malloc(MAX_PARITY_SIZE); - // The DMA buffer, used to stream samples from the FPGA - uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); - uint8_t *data = dmaBuf; - uint8_t previous_data = 0; int maxDataLen = 0, dataLen; bool TagIsActive = false; @@ -600,10 +666,14 @@ void RAMFUNC SniffIso14443a(uint8_t param) { // Set up the demodulator for the reader -> tag commands Uart14aInit(receivedCmd, receivedCmdPar); - DbpString("Starting to sniff"); + Dbprintf("Starting to sniff. Press PM3 Button to stop."); + + // The DMA buffer, used to stream samples from the FPGA + dmabuf8_t *dma = get_dma8(); + uint8_t *data = dma->buf; // Setup and start DMA. - if (!FpgaSetupSscDma((uint8_t *) dmaBuf, DMA_BUFFER_SIZE)) { + if (!FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE)) { if (DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); return; } @@ -621,7 +691,7 @@ void RAMFUNC SniffIso14443a(uint8_t param) { WDT_HIT(); LED_A_ON(); - int register readBufDataP = data - dmaBuf; + int register readBufDataP = data - dma->buf; int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; if (readBufDataP <= dmaBufDataP) dataLen = dmaBufDataP - readBufDataP; @@ -640,13 +710,13 @@ void RAMFUNC SniffIso14443a(uint8_t param) { // primary buffer was stopped( <-- we lost data! if (!AT91C_BASE_PDC_SSC->PDC_RCR) { - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary } // secondary buffer sets as primary, secondary buffer was stopped if (!AT91C_BASE_PDC_SSC->PDC_RNCR) { - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } @@ -710,16 +780,15 @@ void RAMFUNC SniffIso14443a(uint8_t param) { previous_data = *data; rx_samples++; data++; - if (data == dmaBuf + DMA_BUFFER_SIZE) { - data = dmaBuf; + if (data == dma->buf + DMA_BUFFER_SIZE) { + data = dma->buf; } } // end main loop FpgaDisableTracing(); if (DBGLEVEL >= DBG_ERROR) { - Dbprintf("maxDataLen=%d, Uart.state=%x, Uart.len=%d", maxDataLen, Uart.state, Uart.len); - Dbprintf("traceLen=" _YELLOW_("%d")", Uart.output[0]="_YELLOW_("%08x"), BigBuf_get_traceLen(), (uint32_t)Uart.output[0]); + Dbprintf("trace len = " _YELLOW_("%d"), BigBuf_get_traceLen()); } switch_off(); } @@ -729,62 +798,61 @@ void RAMFUNC SniffIso14443a(uint8_t param) { //----------------------------------------------------------------------------- static void CodeIso14443aAsTagPar(const uint8_t *cmd, uint16_t len, uint8_t *par, bool collision) { - //uint8_t localCol = 0; - ToSendReset(); + tosend_reset(); + + tosend_t *ts = get_tosend(); // Correction bit, might be removed when not needed - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(1); // <----- - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(1); // <----- + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); // Send startbit - ToSend[++ToSendMax] = SEC_D; - LastProxToAirDuration = 8 * ToSendMax - 4; + ts->buf[++ts->max] = SEC_D; + LastProxToAirDuration = 8 * ts->max - 4; for (uint16_t i = 0; i < len; i++) { uint8_t b = cmd[i]; // Data bits for (uint16_t j = 0; j < 8; j++) { - //if (collision && (localCol >= colpos)){ if (collision) { - ToSend[++ToSendMax] = SEC_COLL; - //localCol++; + ts->buf[++ts->max] = SEC_COLL; } else { if (b & 1) { - ToSend[++ToSendMax] = SEC_D; + ts->buf[++ts->max] = SEC_D; } else { - ToSend[++ToSendMax] = SEC_E; + ts->buf[++ts->max] = SEC_E; } b >>= 1; } } if (collision) { - ToSend[++ToSendMax] = SEC_COLL; - LastProxToAirDuration = 8 * ToSendMax; + ts->buf[++ts->max] = SEC_COLL; + LastProxToAirDuration = 8 * ts->max; } else { // Get the parity bit if (par[i >> 3] & (0x80 >> (i & 0x0007))) { - ToSend[++ToSendMax] = SEC_D; - LastProxToAirDuration = 8 * ToSendMax - 4; + ts->buf[++ts->max] = SEC_D; + LastProxToAirDuration = 8 * ts->max - 4; } else { - ToSend[++ToSendMax] = SEC_E; - LastProxToAirDuration = 8 * ToSendMax; + ts->buf[++ts->max] = SEC_E; + LastProxToAirDuration = 8 * ts->max; } } } // Send stopbit - ToSend[++ToSendMax] = SEC_F; + ts->buf[++ts->max] = SEC_F; // Convert from last byte pos to length - ToSendMax++; + ts->max++; } static void CodeIso14443aAsTagEx(const uint8_t *cmd, uint16_t len, bool collision) { @@ -799,37 +867,39 @@ static void CodeIso14443aAsTag(const uint8_t *cmd, uint16_t len) { static void Code4bitAnswerAsTag(uint8_t cmd) { uint8_t b = cmd; - ToSendReset(); + tosend_reset(); + + tosend_t *ts = get_tosend(); // Correction bit, might be removed when not needed - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(1); // 1 - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(1); // 1 + tosend_stuffbit(0); + tosend_stuffbit(0); + tosend_stuffbit(0); // Send startbit - ToSend[++ToSendMax] = SEC_D; + ts->buf[++ts->max] = SEC_D; for (uint8_t i = 0; i < 4; i++) { if (b & 1) { - ToSend[++ToSendMax] = SEC_D; - LastProxToAirDuration = 8 * ToSendMax - 4; + ts->buf[++ts->max] = SEC_D; + LastProxToAirDuration = 8 * ts->max - 4; } else { - ToSend[++ToSendMax] = SEC_E; - LastProxToAirDuration = 8 * ToSendMax; + ts->buf[++ts->max] = SEC_E; + LastProxToAirDuration = 8 * ts->max; } b >>= 1; } // Send stopbit - ToSend[++ToSendMax] = SEC_F; + ts->buf[++ts->max] = SEC_F; // Convert from last byte pos to length - ToSendMax++; + ts->max++; } //----------------------------------------------------------------------------- @@ -854,13 +924,15 @@ bool GetIso14443aCommandFromReader(uint8_t *received, uint8_t *par, int *len) { uint16_t check = 0; for (;;) { - if (check == 1000) { - if (BUTTON_PRESS() || data_available()) + if (check == 4000) { +// if (BUTTON_PRESS() || data_available()) + if (BUTTON_PRESS()) return false; + check = 0; + WDT_HIT(); } ++check; - WDT_HIT(); if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; @@ -887,32 +959,36 @@ bool prepare_tag_modulation(tag_response_info_t *response_info, size_t max_buffe // Prepare the tag modulation bits from the message CodeIso14443aAsTag(response_info->response, response_info->response_n); + tosend_t *ts = get_tosend(); + // Make sure we do not exceed the free buffer space - if (ToSendMax > max_buffer_size) { + if (ts->max > max_buffer_size) { Dbprintf("ToSend buffer, Out-of-bound, when modulating bits for tag answer:"); Dbhexdump(response_info->response_n, response_info->response, false); return false; } // Copy the byte array, used for this modulation to the buffer position - memcpy(response_info->modulation, ToSend, ToSendMax); + memcpy(response_info->modulation, ts->buf, ts->max); // Store the number of bytes that were used for encoding/modulation and the time needed to transfer them - response_info->modulation_n = ToSendMax; + response_info->modulation_n = ts->max; response_info->ProxToAirDuration = LastProxToAirDuration; return true; } bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *max_buffer_size) { + tosend_t *ts = get_tosend(); + // Retrieve and store the current buffer index response_info->modulation = *buffer; // Forward the prepare tag modulation function to the inner function if (prepare_tag_modulation(response_info, *max_buffer_size)) { // Update the free buffer offset and the remaining buffer size - *buffer += ToSendMax; - *max_buffer_size -= ToSendMax; + *buffer += ts->max; + *max_buffer_size -= ts->max; return true; } else { return false; @@ -932,11 +1008,15 @@ bool SimulateIso14443aInit(int tagType, int flags, uint8_t *data, tag_response_i // Prepare the optional second SAK (for 7 byte UID), drop the cascade bit static uint8_t rSAKc2[3] = { 0x00 }; // dummy ATS (pseudo-ATR), answer to RATS - static uint8_t rRATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 }; +// static uint8_t rRATS[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 }; + static uint8_t rRATS[] = { 0x05, 0x75, 0x80, 0x60, 0x02, 0x00, 0x00 }; + // GET_VERSION response for EV1/NTAG static uint8_t rVERSION[10] = { 0x00 }; // READ_SIG response for EV1/NTAG static uint8_t rSIGN[34] = { 0x00 }; + // PPS respoonse + static uint8_t rPPS[3] = { 0xD0 }; switch (tagType) { case 1: { // MIFARE Classic 1k @@ -1010,12 +1090,19 @@ bool SimulateIso14443aInit(int tagType, int flags, uint8_t *data, tag_response_i sak = 0x18; } break; - case 9 : { // FM11RF005SH (Shanghai Metro) + case 9: { // FM11RF005SH (Shanghai Metro) rATQA[0] = 0x03; rATQA[1] = 0x00; sak = 0x0A; } break; + case 10: { // JCOP31/41 Rothult + rATQA[0] = 0x42; + rATQA[1] = 0x00; + sak = 0x00; + } + break; + default: { if (DBGLEVEL >= DBG_ERROR) Dbprintf("Error: unknown tagtype (%d)", tagType); return false; @@ -1066,13 +1153,20 @@ bool SimulateIso14443aInit(int tagType, int flags, uint8_t *data, tag_response_i return false; } - // Calculate the BitCountCheck (BCC) for the first 4 bytes of the UID. + // Calculate BCC for the first 4 bytes of the UID. rUIDc1[4] = rUIDc1[0] ^ rUIDc1[1] ^ rUIDc1[2] ^ rUIDc1[3]; - rSAKc1[0] = sak; - AddCrc14A(rSAKc1, sizeof(rSAKc1) - 2); - rSAKc2[0] = sak & 0xFB; + if (tagType == 10) { + rSAKc1[0] = 0x04; + rSAKc2[0] = 0x20; + } else { + rSAKc1[0] = sak; + rSAKc2[0] = sak & 0xFB; + } + + // crc + AddCrc14A(rSAKc1, sizeof(rSAKc1) - 2); AddCrc14A(rSAKc2, sizeof(rSAKc2) - 2); // Format byte = 0x58: FSCI=0x08 (FSC=256), TA(1) and TC(1) present, @@ -1081,22 +1175,26 @@ bool SimulateIso14443aInit(int tagType, int flags, uint8_t *data, tag_response_i // TC(1) = 0x02: CID supported, NAD not supported AddCrc14A(rRATS, sizeof(rRATS) - 2); -#define TAG_RESPONSE_COUNT 8 + AddCrc14A(rPPS, sizeof(rPPS) - 2); + +#define TAG_RESPONSE_COUNT 9 static tag_response_info_t responses_init[TAG_RESPONSE_COUNT] = { - { .response = rATQA, .response_n = sizeof(rATQA) }, // Answer to request - respond with card type - { .response = rUIDc1, .response_n = sizeof(rUIDc1) }, // Anticollision cascade1 - respond with uid - { .response = rUIDc2, .response_n = sizeof(rUIDc2) }, // Anticollision cascade2 - respond with 2nd half of uid if asked - { .response = rSAKc1, .response_n = sizeof(rSAKc1) }, // Acknowledge select - cascade 1 - { .response = rSAKc2, .response_n = sizeof(rSAKc2) }, // Acknowledge select - cascade 2 - { .response = rRATS, .response_n = sizeof(rRATS) }, // dummy ATS (pseudo-ATR), answer to RATS - { .response = rVERSION, .response_n = sizeof(rVERSION) }, // EV1/NTAG GET_VERSION response - { .response = rSIGN, .response_n = sizeof(rSIGN) } // EV1/NTAG READ_SIG response + { .response = rATQA, .response_n = sizeof(rATQA) }, // Answer to request - respond with card type + { .response = rUIDc1, .response_n = sizeof(rUIDc1) }, // Anticollision cascade1 - respond with uid + { .response = rUIDc2, .response_n = sizeof(rUIDc2) }, // Anticollision cascade2 - respond with 2nd half of uid if asked + { .response = rSAKc1, .response_n = sizeof(rSAKc1) }, // Acknowledge select - cascade 1 + { .response = rSAKc2, .response_n = sizeof(rSAKc2) }, // Acknowledge select - cascade 2 + { .response = rRATS, .response_n = sizeof(rRATS) }, // dummy ATS (pseudo-ATR), answer to RATS + { .response = rVERSION, .response_n = sizeof(rVERSION) }, // EV1/NTAG GET_VERSION response + { .response = rSIGN, .response_n = sizeof(rSIGN) }, // EV1/NTAG READ_SIG response + { .response = rPPS, .response_n = sizeof(rPPS) } // PPS response }; - // "precompile" responses. There are 8 predefined responses with a total of 68 bytes data to transmit. + // "precompile" responses. There are 9 predefined responses with a total of 72 bytes data to transmit. // Coded responses need one byte per bit to transfer (data, parity, start, stop, correction) - // 68 * 8 data bits, 68 * 1 parity bits, 8 start bits, 8 stop bits, 8 correction bits -- 636 bytes buffer -#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 636 + // 72 * 8 data bits, 72 * 1 parity bits, 9 start bits, 9 stop bits, 9 correction bits -- 677 bytes buffer +#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 675 +// 576 + 72 + 9 + 9 + 9 == 675 uint8_t *free_buffer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); // modulation buffer pointer and current buffer free space size @@ -1124,7 +1222,7 @@ bool SimulateIso14443aInit(int tagType, int flags, uint8_t *data, tag_response_i #define RATS 5 #define VERSION 6 #define SIGNATURE 7 - +#define PPS 8 return true; } @@ -1183,6 +1281,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { // We need to listen to the high-frequency, peak-detected path. iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); + iso14a_set_timeout(201400); // 106 * 19ms default + int len = 0; // To control where we are in the protocol @@ -1197,8 +1297,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { #define ORDER_SELECT_CL2 30 #define ORDER_EV1_COMP_WRITE 40 #define ORDER_RATS 70 - uint8_t order = ORDER_NONE; + uint8_t order = ORDER_NONE; int retval = PM3_SUCCESS; // Just to allow some checks @@ -1209,27 +1309,26 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { // compatible write block number uint8_t wrblock = 0; + bool odd_reply = true; + clear_trace(); set_tracing(true); LED_A_ON(); + + // main loop for (;;) { WDT_HIT(); + tag_response_info_t *p_response = NULL; + // Clean receive command buffer - if (!GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len)) { - Dbprintf("Emulator stopped. Trace length: %d ", BigBuf_get_traceLen()); + if (GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len) == false) { + Dbprintf("Emulator stopped. Trace length: %d ", BigBuf_get_traceLen()); retval = PM3_EOPABORTED; break; } - tag_response_info_t *p_response = NULL; - // Okay, look at the command now. - int lastorder = order; - - // // we need to check "ordered" states before, because received data may be same to any command - is wrong!!! - // - if (order == ORDER_EV1_COMP_WRITE && len == 18) { // MIFARE_ULC_COMP_WRITE part 2 // 16 bytes data + 2 bytes crc, only least significant 4 bytes are written @@ -1312,28 +1411,22 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { order = ORDER_NONE; // back to work state p_response = NULL; - // - // now check commands in received buffer - // - - } else if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { // Received a REQUEST - p_response = &responses[ATQA]; - order = ORDER_REQA; + } else if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { // Received a REQUEST, but in HALTED, skip + odd_reply = !odd_reply; + if (odd_reply) + p_response = &responses[ATQA]; } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { // Received a WAKEUP p_response = &responses[ATQA]; - order = ORDER_WUPA; } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { // Received request for UID (cascade 1) p_response = &responses[UIDC1]; - order = ORDER_SELECT_ALL_CL1; } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 2) { // Received request for UID (cascade 2) p_response = &responses[UIDC2]; - order = ORDER_SELECT_ALL_CL2; } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1) p_response = &responses[SAKC1]; - order = ORDER_SELECT_CL1; } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && len == 9) { // Received a SELECT (cascade 2) p_response = &responses[SAKC2]; - order = ORDER_SELECT_CL2; + } else if (receivedCmd[0] == ISO14443A_CMD_PPS) { + p_response = &responses[PPS]; } else if (receivedCmd[0] == ISO14443A_CMD_READBLOCK && len == 4) { // Received a (plain) READ uint8_t block = receivedCmd[1]; // if Ultralight or NTAG (4 byte blocks) @@ -1361,8 +1454,6 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { emlGetMemBt(emdata, block, 16); AddCrc14A(emdata, 16); EmSendCmd(emdata, sizeof(emdata)); - // EmSendCmd(data+(4*receivedCmd[1]),16); - // Dbprintf("Read request from reader: %x %x",receivedCmd[0],receivedCmd[1]); // We already responded, do not send anything with the EmSendCmd14443aRaw() that is called below p_response = NULL; } @@ -1384,8 +1475,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { p_response = NULL; } else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7)) { // Received a WRITE // cmd + block + 4 bytes data + 2 bytes crc - bool isCrcCorrect = CheckCrc14A(receivedCmd, len); - if (isCrcCorrect) { + if (CheckCrc14A(receivedCmd, len)) { uint8_t block = receivedCmd[1]; if (block > pages) { // send NACK 0x0 == invalid argument @@ -1403,8 +1493,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { p_response = NULL; } else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7)) { // cmd + block + 2 bytes crc - bool isCrcCorrect = CheckCrc14A(receivedCmd, len); - if (isCrcCorrect) { + if (CheckCrc14A(receivedCmd, len)) { wrblock = receivedCmd[1]; if (wrblock > pages) { // send NACK 0x0 == invalid argument @@ -1489,7 +1578,6 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { p_response = NULL; } else { p_response = &responses[RATS]; - order = ORDER_RATS; } } 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); @@ -1499,10 +1587,13 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { uint8_t pwd[4]; emlGetMemBt(pwd, (pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(pwd)); if (memcmp(receivedCmd + 1, pwd, 4) == 0) { - uint8_t cmd[4]; - emlGetMemBt(cmd, pages * 4 + MFU_DUMP_PREFIX_LENGTH, 2); - AddCrc14A(cmd, sizeof(cmd) - 2); - EmSendCmd(cmd, sizeof(cmd)); + uint8_t pack[4]; + emlGetMemBt(pack, pages * 4 + MFU_DUMP_PREFIX_LENGTH, 2); + if (memcmp(pack, "\x00\x00\x00\x00", 4) == 0) { + memcpy(pack, "\x80\x80\x00\x00", 4); + } + AddCrc14A(pack, sizeof(pack) - 2); + EmSendCmd(pack, sizeof(pack)); } else { EmSend4bit(CARD_NACK_NA); if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Auth attempt: %08x", bytes_to_num(receivedCmd + 1, 4)); @@ -1514,73 +1605,103 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { AddCrc14A(cmd, sizeof(cmd) - 2); EmSendCmd(cmd, sizeof(cmd)); p_response = NULL; + } else { - // Check for ISO 14443A-4 compliant commands, look at left nibble - switch (receivedCmd[0]) { - case 0x02: - case 0x03: { // IBlock (command no CID) + + // clear old dynamic responses + dynamic_response_info.response_n = 0; + dynamic_response_info.modulation_n = 0; + + // ST25TA512B IKEA Rothult + if (tagType == 10) { + // we replay 90 00 for all commands but the read bin and we deny the verify cmd. + + if (memcmp("\x02\xa2\xb0\x00\x00\x1d\x51\x69", receivedCmd, 8) == 0) { + dynamic_response_info.response[0] = receivedCmd[0]; + memcpy(dynamic_response_info.response + 1, "\x00\x1b\xd1\x01\x17\x54\x02\x7a\x68\xa2\x34\xcb\xd0\xe2\x03\xc7\x3e\x62\x0b\xe8\xc6\x3c\x85\x2c\xc5\x31\x31\x31\x32\x90\x00", 31); + dynamic_response_info.response_n = 32; + } else if (memcmp("\x02\x00\x20\x00\x01\x00\x6e\xa9", receivedCmd, 8) == 0) { + dynamic_response_info.response[0] = receivedCmd[0]; + dynamic_response_info.response[1] = 0x63; + dynamic_response_info.response[2] = 0x00; + dynamic_response_info.response_n = 3; + } else { dynamic_response_info.response[0] = receivedCmd[0]; dynamic_response_info.response[1] = 0x90; dynamic_response_info.response[2] = 0x00; dynamic_response_info.response_n = 3; } - break; - case 0x0B: - case 0x0A: { // IBlock (command CID) - dynamic_response_info.response[0] = receivedCmd[0]; - dynamic_response_info.response[1] = 0x00; - dynamic_response_info.response[2] = 0x90; - dynamic_response_info.response[3] = 0x00; - dynamic_response_info.response_n = 4; - } - break; + } else { - case 0x1A: - case 0x1B: { // Chaining command - dynamic_response_info.response[0] = 0xaa | ((receivedCmd[0]) & 1); - dynamic_response_info.response_n = 2; - } - break; - - case 0xAA: - case 0xBB: { - dynamic_response_info.response[0] = receivedCmd[0] ^ 0x11; - dynamic_response_info.response_n = 2; - } - break; - - case 0xBA: { // ping / pong - dynamic_response_info.response[0] = 0xAB; - dynamic_response_info.response[1] = 0x00; - dynamic_response_info.response_n = 2; - } - break; - - case 0xCA: - case 0xC2: { // Readers sends deselect command - dynamic_response_info.response[0] = 0xCA; - dynamic_response_info.response[1] = 0x00; - dynamic_response_info.response_n = 2; - } - break; - - default: { - // Never seen this command before - LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - if (DBGLEVEL >= DBG_DEBUG) { - Dbprintf("Received unknown command (len=%d):", len); - Dbhexdump(len, receivedCmd, false); + // Check for ISO 14443A-4 compliant commands, look at left nibble + switch (receivedCmd[0]) { + case 0x02: + case 0x03: { // IBlock (command no CID) + dynamic_response_info.response[0] = receivedCmd[0]; + dynamic_response_info.response[1] = 0x90; + dynamic_response_info.response[2] = 0x00; + dynamic_response_info.response_n = 3; } - // Do not respond - dynamic_response_info.response_n = 0; - order = ORDER_NONE; // back to work state - } - break; - } + break; + case 0x0B: + case 0x0A: { // IBlock (command CID) + dynamic_response_info.response[0] = receivedCmd[0]; + dynamic_response_info.response[1] = 0x00; + dynamic_response_info.response[2] = 0x90; + dynamic_response_info.response[3] = 0x00; + dynamic_response_info.response_n = 4; + } + break; + case 0x1A: + case 0x1B: { // Chaining command + dynamic_response_info.response[0] = 0xaa | ((receivedCmd[0]) & 1); + dynamic_response_info.response_n = 2; + } + break; + + case 0xAA: + case 0xBB: { + dynamic_response_info.response[0] = receivedCmd[0] ^ 0x11; + dynamic_response_info.response_n = 2; + } + break; + + case 0xBA: { // ping / pong + dynamic_response_info.response[0] = 0xAB; + dynamic_response_info.response[1] = 0x00; + dynamic_response_info.response_n = 2; + } + break; + + case 0xCA: + case 0xC2: { // Readers sends deselect command + dynamic_response_info.response[0] = 0xCA; + dynamic_response_info.response[1] = 0x00; + dynamic_response_info.response_n = 2; + } + break; + + default: { + // Never seen this command before + LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Received unknown command (len=%d):", len); + Dbhexdump(len, receivedCmd, false); + } + // Do not respond + dynamic_response_info.response_n = 0; + order = ORDER_NONE; // back to work state + } + break; + } + + } if (dynamic_response_info.response_n > 0) { + // Copy the CID from the reader query - dynamic_response_info.response[1] = receivedCmd[1]; + if (tagType != 10) + dynamic_response_info.response[1] = receivedCmd[1]; // Add CRC bytes, always used in ISO 14443A-4 compliant cards AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n); @@ -1596,16 +1717,15 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { } // Count number of wakeups received after a halt - if (order == ORDER_WUPA && lastorder == ORDER_HALTED) { happened++; } +// if (order == ORDER_WUPA && lastorder == ORDER_HALTED) { happened++; } // Count number of other messages after a halt - if (order != ORDER_WUPA && lastorder == ORDER_HALTED) { happened2++; } +// if (order != ORDER_WUPA && lastorder == ORDER_HALTED) { happened2++; } cmdsRecvd++; - if (p_response != NULL) { - EmSendPrecompiledCmd(p_response); - } + // Send response + EmSendPrecompiledCmd(p_response); } switch_off(); @@ -1635,12 +1755,14 @@ static void PrepareDelayedTransfer(uint16_t delay) { for (uint16_t i = 0; i < delay; i++) bitmask |= (0x01 << i); - ToSend[ToSendMax++] = 0x00; + tosend_t *ts = get_tosend(); - for (uint16_t i = 0; i < ToSendMax; i++) { - uint8_t bits_to_shift = ToSend[i] & bitmask; - ToSend[i] = ToSend[i] >> delay; - ToSend[i] = ToSend[i] | (bits_shifted << (8 - delay)); + ts->buf[ts->max++] = 0x00; + + for (uint16_t i = 0; i < ts->max; i++) { + uint8_t bits_to_shift = ts->buf[i] & bitmask; + ts->buf[i] = ts->buf[i] >> delay; + ts->buf[i] = ts->buf[i] | (bits_shifted << (8 - delay)); bits_shifted = bits_to_shift; } } @@ -1698,11 +1820,12 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing static void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, const uint8_t *par) { int last = 0; - ToSendReset(); + tosend_reset(); + tosend_t *ts = get_tosend(); // Start of Communication (Seq. Z) - ToSend[++ToSendMax] = SEC_Z; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 6; + ts->buf[++ts->max] = SEC_Z; + LastProxToAirDuration = 8 * (ts->max + 1) - 6; size_t bytecount = nbytes(bits); // Generate send structure for the data bits @@ -1714,17 +1837,17 @@ static void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, cons for (j = 0; j < bitsleft; j++) { if (b & 1) { // Sequence X - ToSend[++ToSendMax] = SEC_X; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 2; + ts->buf[++ts->max] = SEC_X; + LastProxToAirDuration = 8 * (ts->max + 1) - 2; last = 1; } else { if (last == 0) { // Sequence Z - ToSend[++ToSendMax] = SEC_Z; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 6; + ts->buf[++ts->max] = SEC_Z; + LastProxToAirDuration = 8 * (ts->max + 1) - 6; } else { // Sequence Y - ToSend[++ToSendMax] = SEC_Y; + ts->buf[++ts->max] = SEC_Y; last = 0; } } @@ -1736,17 +1859,17 @@ static void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, cons // Get the parity bit if (par[i >> 3] & (0x80 >> (i & 0x0007))) { // Sequence X - ToSend[++ToSendMax] = SEC_X; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 2; + ts->buf[++ts->max] = SEC_X; + LastProxToAirDuration = 8 * (ts->max + 1) - 2; last = 1; } else { if (last == 0) { // Sequence Z - ToSend[++ToSendMax] = SEC_Z; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 6; + ts->buf[++ts->max] = SEC_Z; + LastProxToAirDuration = 8 * (ts->max + 1) - 6; } else { // Sequence Y - ToSend[++ToSendMax] = SEC_Y; + ts->buf[++ts->max] = SEC_Y; last = 0; } } @@ -1756,16 +1879,16 @@ static void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, cons // End of Communication: Logic 0 followed by Sequence Y if (last == 0) { // Sequence Z - ToSend[++ToSendMax] = SEC_Z; - LastProxToAirDuration = 8 * (ToSendMax + 1) - 6; + ts->buf[++ts->max] = SEC_Z; + LastProxToAirDuration = 8 * (ts->max + 1) - 6; } else { // Sequence Y - ToSend[++ToSendMax] = SEC_Y; + ts->buf[++ts->max] = SEC_Y; } - ToSend[++ToSendMax] = SEC_Y; + ts->buf[++ts->max] = SEC_Y; // Convert to length of command: - ToSendMax++; + ts->max++; } //----------------------------------------------------------------------------- @@ -1822,8 +1945,8 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *par) { for (;;) { WDT_HIT(); - if (check == 1000) { - if (BUTTON_PRESS() || data_available()) + if (check == 2000) { + if (BUTTON_PRESS()) return 1; check = 0; } @@ -1903,7 +2026,7 @@ int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen) { volatile uint8_t b; uint16_t i = 0; uint32_t ThisTransferTime; - bool correctionNeeded; + bool correction_needed; // Modulate Manchester FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_MOD); @@ -1911,21 +2034,23 @@ int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen) { // Include correction bit if necessary if (Uart.bitCount == 7) { // Short tags (7 bits) don't have parity, determine the correct value from MSB - correctionNeeded = Uart.output[0] & 0x40; + correction_needed = Uart.output[0] & 0x40; } else { // The parity bits are left-aligned - correctionNeeded = Uart.parity[(Uart.len - 1) / 8] & (0x80 >> ((Uart.len - 1) & 7)); + correction_needed = Uart.parity[(Uart.len - 1) / 8] & (0x80 >> ((Uart.len - 1) & 7)); } // 1236, so correction bit needed - i = (correctionNeeded) ? 0 : 1; + i = (correction_needed) ? 0 : 1; // clear receiving shift register and holding register while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); b = AT91C_BASE_SSC->SSC_RHR; (void) b; - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); - b = AT91C_BASE_SSC->SSC_RHR; - (void) b; + /* + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY)); + b = AT91C_BASE_SSC->SSC_THR; + (void) b; + */ // wait for the FPGA to signal fdt_indicator == 1 (the FPGA is ready to queue new data in its delay line) for (uint8_t j = 0; j < 5; j++) { // allow timeout - better late than never @@ -1945,29 +2070,31 @@ int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen) { FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR; } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); - (void)b; - } - if (BUTTON_PRESS()) break; + /* + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + b = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); + (void)b; + } + */ } // Ensure that the FPGA Delay Queue is empty before we switch to TAGSIM_LISTEN again: uint8_t fpga_queued_bits = FpgaSendQueueDelay >> 3; - for (i = 0; i <= fpga_queued_bits / 8 + 1;) { + for (i = 0; i <= (fpga_queued_bits >> 3) + 1;) { if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = SEC_F; FpgaSendQueueDelay = (uint8_t)AT91C_BASE_SSC->SSC_RHR; i++; } } - LastTimeProxToAirStart = ThisTransferTime + (correctionNeeded ? 8 : 0); + LastTimeProxToAirStart = ThisTransferTime + (correction_needed ? 8 : 0); return 0; } int EmSend4bit(uint8_t resp) { Code4bitAnswerAsTag(resp); - int res = EmSendCmd14443aRaw(ToSend, ToSendMax); + tosend_t *ts = get_tosend(); + int res = EmSendCmd14443aRaw(ts->buf, ts->max); // do the tracing for the previous reader request and this tag answer: uint8_t par[1] = {0x00}; GetParity(&resp, 1, par); @@ -1988,7 +2115,9 @@ int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par) { } int EmSendCmdParEx(uint8_t *resp, uint16_t respLen, uint8_t *par, bool collision) { CodeIso14443aAsTagPar(resp, respLen, par, collision); - int res = EmSendCmd14443aRaw(ToSend, ToSendMax); + tosend_t *ts = get_tosend(); + int res = EmSendCmd14443aRaw(ts->buf, ts->max); + // do the tracing for the previous reader request and this tag answer: EmLogTrace(Uart.output, Uart.len, @@ -2012,6 +2141,7 @@ int EmSendCmdEx(uint8_t *resp, uint16_t respLen, bool 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}; @@ -2149,7 +2279,8 @@ void ReaderTransmitBitsPar(uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t CodeIso14443aBitsAsReaderPar(frame, bits, par); // Send command to tag - TransmitFor14443a(ToSend, ToSendMax, timing); + tosend_t *ts = get_tosend(); + TransmitFor14443a(ts->buf, ts->max, timing); if (g_trigger) LED_A_ON(); LogTrace(frame, nbytes(bits), (LastTimeProxToAirStart << 4) + DELAY_ARM2AIR_AS_READER, ((LastTimeProxToAirStart + LastProxToAirDuration) << 4) + DELAY_ARM2AIR_AS_READER, par, true); @@ -2322,7 +2453,8 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 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 = 0x04; // cascade uid + uint8_t sak; // cascade uid + bool do_cascade = 1; int cascade_level = 0; if (p_card) { @@ -2346,26 +2478,32 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 memset(uid_ptr, 0, 10); } - // check for proprietary anticollision: - if ((resp[0] & 0x1F) == 0) return 3; + if (hf14aconfig.forceanticol == 0) { + // check for proprietary anticollision: + if ((resp[0] & 0x1F) == 0) return 3; + } else if (hf14aconfig.forceanticol == 2) { + return 3; // force skipping anticol + } // else force executing // OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in // 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++) { + for (; do_cascade; cascade_level++) { // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) 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}; - uint8_t uid_resp[4] = {0}; + uint8_t uid_resp[5] = {0}; // UID + original BCC sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2; if (anticollision) { // SELECT_ALL ReaderTransmit(sel_all, sizeof(sel_all), NULL); - if (!ReaderReceive(resp, resp_par)) return 0; - + if (!ReaderReceive(resp, resp_par)) { + Dbprintf("Card didn't answer to CL%i select all", cascade_level + 1); + return 0; + } if (Demod.collisionPos) { // we had a collision and need to construct the UID bit by bit - memset(uid_resp, 0, 4); + memset(uid_resp, 0, 5); uint16_t uid_resp_bits = 0; uint16_t collision_answer_offset = 0; // anti-collision-loop: @@ -2377,7 +2515,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 } 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 anticollosion command: + // construct anticollision command: sel_uid[1] = ((2 + uid_resp_bits / 8) << 4) | (uid_resp_bits & 0x07); // length of data in bytes and bits for (uint16_t i = 0; i <= uid_resp_bits / 8; i++) { sel_uid[2 + i] = uid_resp[i]; @@ -2393,7 +2531,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 } } else { // no collision, use the response to SELECT_ALL as current uid - memcpy(uid_resp, resp, 4); + memcpy(uid_resp, resp, 5); // UID + original BCC } } else { @@ -2412,18 +2550,51 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 // Construct SELECT UID command sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC) - memcpy(sel_uid + 2, uid_resp, 4); // the UID received during anticollision, or the provided UID - sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC + + 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]); + } + } else { + memcpy(sel_uid + 2, uid_resp, 4); // the provided UID + sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC + } + AddCrc14A(sel_uid, 7); // calculate and add CRC ReaderTransmit(sel_uid, sizeof(sel_uid), NULL); // Receive the SAK - if (!ReaderReceive(resp, resp_par)) return 0; - + if (!ReaderReceive(resp, resp_par)) { + Dbprintf("Card didn't answer to select"); + return 0; + } sak = resp[0]; // Test if more parts of the uid are coming - if ((sak & 0x04) /* && uid_resp[0] == 0x88 */) { + 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) { + do_cascade = true; + } // else 0==auto + } + if (do_cascade) { // Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of: // http://www.nxp.com/documents/application_note/AN10927.pdf uid_resp[0] = uid_resp[1]; @@ -2445,8 +2616,12 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 p_card->sak = sak; } - // PICC compilant with iso14443a-4 ---> (SAK & 0x20 != 0) - if ((sak & 0x20) == 0) return 2; + if (hf14aconfig.forcerats == 0) { + // PICC compliant with iso14443a-4 ---> (SAK & 0x20 != 0) + if ((sak & 0x20) == 0) return 2; + } else if (hf14aconfig.forcerats == 2) { + return 2; + } // else force RATS // RATS, Request for answer to select if (!no_rats) { @@ -2527,7 +2702,7 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -2537,7 +2712,7 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | fpga_minor_mode); - SpinDelay(100); + SpinDelay(50); // Start the timer StartCountSspClk(); @@ -2551,7 +2726,6 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { hf_field_active = true; } - void hf_field_off(void) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); @@ -2755,10 +2929,15 @@ void ReaderIso14443a(PacketCommandNG *c) { ReaderTransmit(cmd, len, NULL); // 8 bits, odd parity } } - arg0 = ReaderReceive(buf, par); - FpgaDisableTracing(); - reply_old(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + FpgaDisableTracing(); + reply_old(CMD_ACK, 0, 0, 0, NULL, 0); + } else { + arg0 = ReaderReceive(buf, par); + FpgaDisableTracing(); + reply_old(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); + } } if ((param & ISO14A_REQUEST_TRIGGER)) diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 94b1d5a2d..d1df5534c 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -96,6 +96,9 @@ typedef struct { # define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len)) #endif +void printHf14aConfig(void); +void setHf14aConfig(hf14a_config *hc); +hf14a_config *getHf14aConfig(void); void iso14a_set_timeout(uint32_t timeout); uint32_t iso14a_get_timeout(void); diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 4999c4d85..976e13148 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Jonathan Westhues, split Nov 2006 +// piwi 2018 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -24,20 +25,42 @@ #include "dbprint.h" #include "ticks.h" -#ifndef FWT_TIMEOUT_14B + +// Delays in SSP_CLK ticks. +// SSP_CLK runs at 13,56MHz / 32 = 423.75kHz when simulating a tag +#define DELAY_READER_TO_ARM 8 +#define DELAY_ARM_TO_READER 0 + +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when acting as reader. All values should be multiples of 16 +#define DELAY_ARM_TO_TAG 16 +#define DELAY_TAG_TO_ARM 32 + +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when sniffing. All values should be multiples of 16 +#define DELAY_TAG_TO_ARM_SNIFF 32 +#define DELAY_READER_TO_ARM_SNIFF 32 + // defaults to 2000ms -# define FWT_TIMEOUT_14B 35312 -#endif -#ifndef ISO14443B_DMA_BUFFER_SIZE -# define ISO14443B_DMA_BUFFER_SIZE 512 //changed this from 256 +#ifndef FWT_TIMEOUT_14B +# define FWT_TIMEOUT_14B 35312 #endif + +// 1 tick == 1/13.56 mhz +// 1 us = 1.5 tick + +// 330/848kHz = 1558us / 4 == 400us, +#define ISO14443B_READER_TIMEOUT 10000 //330 + +// 1024/3.39MHz = 302.1us between end of tag response and next reader cmd +#define DELAY_ISO14443B_VICC_TO_VCD_READER (28*9) // 1024 ( counting from start of PICC EOF 14 ETU's) +#define DELAY_ISO14443B_VCD_TO_VICC_READER (28*9) // 1056 + #ifndef RECEIVE_MASK -# define RECEIVE_MASK (ISO14443B_DMA_BUFFER_SIZE-1) +# define RECEIVE_MASK (DMA_BUFFER_SIZE - 1) #endif // Guard Time (per 14443-2) #ifndef TR0 -# define TR0 32 //this value equals 8 ETU = 32 ssp clk (w/ 424 khz) +# define TR0 32 // TR0 max is 151/fs = 151/(848kHz) = 302us or 64 samples from FPGA #endif // Synchronization time (per 14443-2) @@ -50,31 +73,166 @@ #endif // 4sample -#define SEND4STUFFBIT(x) ToSendStuffBit(x);ToSendStuffBit(x);ToSendStuffBit(x);ToSendStuffBit(x); -//#define SEND4STUFFBIT(x) ToSendStuffBit(x); -// iceman, this threshold value, what makes 8 a good amplitude for this IQ values? -#ifndef SUBCARRIER_DETECT_THRESHOLD -# define SUBCARRIER_DETECT_THRESHOLD 8 -#endif +#define SEND4STUFFBIT(x) tosend_stuffbit(x);tosend_stuffbit(x);tosend_stuffbit(x);tosend_stuffbit(x); static void iso14b_set_timeout(uint32_t timeout); static void iso14b_set_maxframesize(uint16_t size); // the block number for the ISO14443-4 PCB (used with APDUs) -static uint8_t pcb_blocknum = 0; +static uint8_t iso14b_pcb_blocknum = 0; static uint32_t iso14b_timeout = FWT_TIMEOUT_14B; +/* ISO 14443 B +* +* Reader to card | ASK - Amplitude Shift Keying Modulation (PCD to PICC for Type B) (NRZ-L encodig) +* Card to reader | BPSK - Binary Phase Shift Keying Modulation, (PICC to PCD for Type B) +* +* fc - carrier frequency 13.56 MHz +* TR0 - Guard Time per 14443-2 +* TR1 - Synchronization Time per 14443-2 +* TR2 - PICC to PCD Frame Delay Time (per 14443-3 Amendment 1) +* +* Elementary Time Unit (ETU) is +* - 128 Carrier Cycles (9.4395 µS) = 8 Subcarrier Units +* - 1 ETU = 1 bit +* - 10 ETU = 1 startbit, 8 databits, 1 stopbit (10bits length) +* - startbit is a 0 +* - stopbit is a 1 +* +* Start of frame (SOF) is +* - [10-11] ETU of ZEROS, unmodulated time +* - [2-3] ETU of ONES, +* +* End of frame (EOF) is +* - [10-11] ETU of ZEROS, unmodulated time +* +* -TO VERIFY THIS BELOW- +* The mode FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK which we use to simulate tag +* works like this: +* - A 1-bit input to the FPGA becomes 8 pulses at 847.5kHz (1.18µS / pulse) == 9.44us +* - A 0-bit input to the FPGA becomes an unmodulated time of 1.18µS or does it become 8 nonpulses for 9.44us +* +* FPGA doesn't seem to work with ETU. It seems to work with pulse / duration instead. +* +* Card sends data ub 847.e kHz subcarrier +* subcar |duration| FC division +* -------+--------+------------ +* 106kHz | 9.44µS | FC/128 +* 212kHz | 4.72µS | FC/64 +* 424kHz | 2.36µS | FC/32 +* 848kHz | 1.18µS | FC/16 +* -------+--------+------------ +* +* Reader data transmission: +* - no modulation ONES +* - SOF +* - Command, data and CRC_B +* - EOF +* - no modulation ONES +* +* Card data transmission +* - TR1 +* - SOF +* - data (each bytes is: 1startbit, 8bits, 1stopbit) +* - CRC_B +* - EOF +* +* FPGA implementation : +* At this point only Type A is implemented. This means that we are using a +* bit rate of 106 kbit/s, or fc/128. Oversample by 4, which ought to make +* things practical for the ARM (fc/32, 423.8 kbits/s, ~50 kbytes/s) +* +* Let us report a correlation every 64 samples. I.e. +* one Q/I pair after 4 subcarrier cycles for the 848kHz subcarrier, +* one Q/I pair after 2 subcarrier cycles for the 424kHz subcarrier, +* one Q/I pair for each subcarrier cyle for the 212kHz subcarrier. +*/ + + + //============================================================================= // An ISO 14443 Type B tag. We listen for commands from the reader, using -// a kind of thing that's implemented in software. When we get a +// a UART kind of thing that's implemented in software. When we get a // frame (i.e., a group of bytes between SOF and EOF), we check the CRC. // If it's good, then we can do something appropriate with it, and send // a response. //============================================================================= +//----------------------------------------------------------------------------- +// Code up a string of octets at layer 2 (including CRC, we don't generate +// that here) so that they can be transmitted to the reader. Doesn't transmit +// them yet, just leaves them ready to send in ToSend[]. +//----------------------------------------------------------------------------- +static void CodeIso14443bAsTag(const uint8_t *cmd, int len) { + int i; + + tosend_reset(); + + // Transmit a burst of ones, as the initial thing that lets the + // reader get phase sync. + // This loop is TR1, per specification + // TR1 minimum must be > 80/fs + // TR1 maximum 200/fs + // 80/fs < TR1 < 200/fs + // 10 ETU < TR1 < 24 ETU + + // Send TR1. + // 10-11 ETU * 4times samples ONES + for (i = 0; i < 20; i++) { + SEND4STUFFBIT(1); + } + + // Send SOF. + // 10-11 ETU * 4times samples ZEROS + for (i = 0; i < 10; i++) { + SEND4STUFFBIT(0); + } + + // 2-3 ETU * 4times samples ONES + for (i = 0; i < 2; i++) { + SEND4STUFFBIT(1); + } + + // data + for (i = 0; i < len; i++) { + + // Start bit + SEND4STUFFBIT(0); + + // Data bits + uint8_t b = cmd[i]; + for (int j = 0; j < 8; j++) { + SEND4STUFFBIT(b & 1); + b >>= 1; + } + + // Stop bit + SEND4STUFFBIT(1); + + // Extra Guard bit + // For PICC it ranges 0-18us (1etu = 9us) + //SEND4STUFFBIT(1); + } + + // Send EOF. + // 10-11 ETU * 4 sample rate = ZEROS + for (i = 0; i < 10; i++) { + SEND4STUFFBIT(0); + } + + // why this? + for (i = 0; i < 2; i++) { + SEND4STUFFBIT(1); + } + + tosend_t *ts = get_tosend(); + // Convert from last byte pos to length + ts->max++; +} //----------------------------------------------------------------------------- -// The software that receives commands from the reader, and its state variables. +// The software UART that receives commands from the reader, and its state +// variables. //----------------------------------------------------------------------------- static struct { enum { @@ -103,34 +261,32 @@ static void Uart14bReset(void) { static void Uart14bInit(uint8_t *data) { Uart.output = data; Uart14bReset(); -// memset(Uart.output, 0x00, MAX_FRAME_SIZE); } //----------------------------------------------------------------------------- // The software Demod that receives commands from the tag, and its state variables. //----------------------------------------------------------------------------- + +#define NOISE_THRESHOLD 80 // don't try to correlate noise +#define MAX_PREVIOUS_AMPLITUDE (-1 - NOISE_THRESHOLD) + static struct { enum { DEMOD_UNSYNCD, DEMOD_PHASE_REF_TRAINING, - DEMOD_AWAITING_FALLING_EDGE_OF_SOF, - DEMOD_GOT_FALLING_EDGE_OF_SOF, + WAIT_FOR_RISING_EDGE_OF_SOF, DEMOD_AWAITING_START_BIT, DEMOD_RECEIVING_DATA } state; uint16_t bitCount; int posCount; int thisBit; - /* this had been used to add RSSI (Received Signal Strength Indication) to traces. Currently not implemented. - int metric; - int metricN; - */ uint16_t shiftReg; + uint16_t max_len; uint8_t *output; uint16_t len; int sumI; int sumQ; - uint32_t startTime, endTime; } Demod; // Clear out the state of the "UART" that receives from the tag. @@ -143,17 +299,14 @@ static void Demod14bReset(void) { Demod.len = 0; Demod.sumI = 0; Demod.sumQ = 0; - Demod.startTime = 0; - Demod.endTime = 0; } -static void Demod14bInit(uint8_t *data) { +static void Demod14bInit(uint8_t *data, uint16_t max_len) { Demod.output = data; + Demod.max_len = max_len; Demod14bReset(); - // memset(Demod.output, 0x00, MAX_FRAME_SIZE); } - /* * 9.4395 us = 1 ETU and clock is about 1.5 us * 13560000Hz @@ -173,152 +326,17 @@ static void iso14b_set_timeout(uint32_t timeout) { timeout = MAX_TIMEOUT; iso14b_timeout = timeout; - if (DBGLEVEL >= 3) Dbprintf("ISO14443B Timeout set to %ld fwt", iso14b_timeout); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("ISO14443B Timeout set to %ld fwt", iso14b_timeout); } + static void iso14b_set_maxframesize(uint16_t size) { if (size > 256) size = MAX_FRAME_SIZE; Uart.byteCntMax = size; - if (DBGLEVEL >= 3) Dbprintf("ISO14443B Max frame size set to %d bytes", Uart.byteCntMax); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("ISO14443B Max frame size set to %d bytes", Uart.byteCntMax); } -//----------------------------------------------------------------------------- -// Code up a string of octets at layer 2 (including CRC, we don't generate -// that here) so that they can be transmitted to the reader. Doesn't transmit -// them yet, just leaves them ready to send in ToSend[]. -//----------------------------------------------------------------------------- -static void CodeIso14443bAsTag(const uint8_t *cmd, int len) { - /* ISO 14443 B - * - * Reader to card | ASK - Amplitude Shift Keying Modulation (PCD to PICC for Type B) (NRZ-L encodig) - * Card to reader | BPSK - Binary Phase Shift Keying Modulation, (PICC to PCD for Type B) - * - * fc - carrier frequency 13.56 MHz - * TR0 - Guard Time per 14443-2 - * TR1 - Synchronization Time per 14443-2 - * TR2 - PICC to PCD Frame Delay Time (per 14443-3 Amendment 1) - * - * Elementary Time Unit (ETU) is - * - 128 Carrier Cycles (9.4395 µS) = 8 Subcarrier Units - * - 1 ETU = 1 bit - * - 10 ETU = 1 startbit, 8 databits, 1 stopbit (10bits length) - * - startbit is a 0 - * - stopbit is a 1 - * - * Start of frame (SOF) is - * - [10-11] ETU of ZEROS, unmodulated time - * - [2-3] ETU of ONES, - * - * End of frame (EOF) is - * - [10-11] ETU of ZEROS, unmodulated time - * - * -TO VERIFY THIS BELOW- - * The mode FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK which we use to simulate tag - * works like this: - * - A 1-bit input to the FPGA becomes 8 pulses at 847.5kHz (1.18µS / pulse) == 9.44us - * - A 0-bit input to the FPGA becomes an unmodulated time of 1.18µS or does it become 8 nonpulses for 9.44us - * - * FPGA doesn't seem to work with ETU. It seems to work with pulse / duration instead. - * - * Card sends data ub 847.e kHz subcarrier - * subcar |duration| FC division - * -------+--------+------------ - * 106kHz | 9.44µS | FC/128 - * 212kHz | 4.72µS | FC/64 - * 424kHz | 2.36µS | FC/32 - * 848kHz | 1.18µS | FC/16 - * -------+--------+------------ - * - * Reader data transmission: - * - no modulation ONES - * - SOF - * - Command, data and CRC_B - * - EOF - * - no modulation ONES - * - * Card data transmission - * - TR1 - * - SOF - * - data (each bytes is: 1startbit, 8bits, 1stopbit) - * - CRC_B - * - EOF - * - * FPGA implementation : - * At this point only Type A is implemented. This means that we are using a - * bit rate of 106 kbit/s, or fc/128. Oversample by 4, which ought to make - * things practical for the ARM (fc/32, 423.8 kbits/s, ~50 kbytes/s) - * - */ - - ToSendReset(); - - // Transmit a burst of ones, as the initial thing that lets the - // reader get phase sync. - // This loop is TR1, per specification - // TR1 minimum must be > 80/fs - // TR1 maximum 200/fs - // 80/fs < TR1 < 200/fs - // 10 ETU < TR1 < 24 ETU - - // Send TR1. - // 10-11 ETU * 4times samples ONES - for (int i = 0; i < 10; i++) { SEND4STUFFBIT(1); } - - // Send SOF. - // 10-11 ETU * 4times samples ZEROS - for (int i = 0; i < 10; i++) { SEND4STUFFBIT(0); } - //for(i = 0; i < 10; i++) { ToSendStuffBit(0); } - - // 2-3 ETU * 4times samples ONES - for (int i = 0; i < 3; i++) { SEND4STUFFBIT(1); } - //for(i = 0; i < 3; i++) { ToSendStuffBit(1); } - - // data - for (int i = 0; i < len; ++i) { - - // Start bit - SEND4STUFFBIT(0); - //ToSendStuffBit(0); - - // Data bits - uint8_t b = cmd[i]; - for (int j = 0; j < 8; ++j) { - // if(b & 1) { - // SEND4STUFFBIT(1); - // //ToSendStuffBit(1); - // } else { - // SEND4STUFFBIT(0); - // //ToSendStuffBit(0); - // } - SEND4STUFFBIT(b & 1); - b >>= 1; - } - - // Stop bit - SEND4STUFFBIT(1); - //ToSendStuffBit(1); - - // Extra Guard bit - // For PICC it ranges 0-18us (1etu = 9us) - SEND4STUFFBIT(1); - //ToSendStuffBit(1); - } - - // Send EOF. - // 10-11 ETU * 4 sample rate = ZEROS - for (int i = 0; i < 10; i++) { SEND4STUFFBIT(0); } - //for(i = 0; i < 10; i++) { ToSendStuffBit(0); } - - // why this? - for (int i = 0; i < 2; i++) { SEND4STUFFBIT(1); } - //for(i = 0; i < 40; i++) { ToSendStuffBit(1); } - - // Convert from last byte pos to length - ++ToSendMax; -} - - /* Receive & handle a bit coming from the reader. * * This function is called 4 times per bit (every 2 subcarrier cycles). @@ -331,10 +349,10 @@ static void CodeIso14443bAsTag(const uint8_t *cmd, int len) { * Returns: true if we received a EOF * false if we are still waiting for some more */ -static RAMFUNC int Handle14443bReaderUartBit(uint8_t bit) { +static RAMFUNC int Handle14443bSampleFromReader(uint8_t bit) { switch (Uart.state) { case STATE_14B_UNSYNCD: - if (!bit) { + if (bit == false) { // we went low, so this could be the beginning of an SOF Uart.state = STATE_14B_GOT_FALLING_EDGE_OF_SOF; Uart.posCnt = 0; @@ -344,7 +362,9 @@ static RAMFUNC int Handle14443bReaderUartBit(uint8_t bit) { case STATE_14B_GOT_FALLING_EDGE_OF_SOF: Uart.posCnt++; + if (Uart.posCnt == 2) { // sample every 4 1/fs in the middle of a bit + if (bit) { if (Uart.bitCnt > 9) { // we've seen enough consecutive @@ -362,7 +382,11 @@ static RAMFUNC int Handle14443bReaderUartBit(uint8_t bit) { } Uart.bitCnt++; } - if (Uart.posCnt >= 4) Uart.posCnt = 0; + + if (Uart.posCnt >= 4) { + Uart.posCnt = 0; + } + if (Uart.bitCnt > 12) { // Give up if we see too many zeros without a one, too. LED_A_OFF(); @@ -372,11 +396,16 @@ static RAMFUNC int Handle14443bReaderUartBit(uint8_t bit) { case STATE_14B_AWAITING_START_BIT: Uart.posCnt++; + if (bit) { - if (Uart.posCnt > 50 / 2) { // max 57us between characters = 49 1/fs, max 3 etus after low phase of SOF = 24 1/fs + + // max 57us between characters = 49 1/fs, + // max 3 etus after low phase of SOF = 24 1/fs + if (Uart.posCnt > 50 / 2) { // stayed high for too long between characters, error Uart.state = STATE_14B_UNSYNCD; } + } else { // falling edge, this starts the data byte Uart.posCnt = 0; @@ -387,7 +416,9 @@ static RAMFUNC int Handle14443bReaderUartBit(uint8_t bit) { break; case STATE_14B_RECEIVING_DATA: + Uart.posCnt++; + if (Uart.posCnt == 2) { // time to sample a bit Uart.shiftReg >>= 1; @@ -396,14 +427,16 @@ static RAMFUNC int Handle14443bReaderUartBit(uint8_t bit) { } Uart.bitCnt++; } + if (Uart.posCnt >= 4) { Uart.posCnt = 0; } + if (Uart.bitCnt == 10) { if ((Uart.shiftReg & 0x200) && !(Uart.shiftReg & 0x001)) { // this is a data byte, with correct // start and stop bits - Uart.output[Uart.byteCnt] = (Uart.shiftReg >> 1) & 0xff; + Uart.output[Uart.byteCnt] = (Uart.shiftReg >> 1) & 0xFF; Uart.byteCnt++; if (Uart.byteCnt >= Uart.byteCntMax) { @@ -454,42 +487,16 @@ static int GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) { LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); - StartCountSspClk(); - - volatile uint8_t b; - - // clear receiving shift register and holding register - // What does this loop do? Is it TR1? - // loop is a wait/delay ? - /* - for(uint8_t c = 0; c < 10;) { - - // keep tx buffer in a defined state anyway. - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xFF; - ++c; - } - } - */ // Now run a `software UART' on the stream of incoming samples. Uart14bInit(received); - uint8_t mask; - while (!BUTTON_PRESS()) { + while (BUTTON_PRESS() == false) { WDT_HIT(); - // keep tx buffer in a defined state anyway. - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; - } - - // Wait for byte be become available in rx holding register - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - - b = (uint8_t) AT91C_BASE_SSC->SSC_RHR; - - for (mask = 0x80; mask != 0; mask >>= 1) { - if (Handle14443bReaderUartBit(b & mask)) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + for (uint8_t mask = 0x80; mask != 0x00; mask >>= 1) { + if (Handle14443bSampleFromReader(b & mask)) { *len = Uart.byteCnt; return true; } @@ -499,144 +506,107 @@ static int GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) { return false; } -void ClearFpgaShiftingRegisters(void) { - - volatile uint8_t b; - - // clear receiving shift register and holding register - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) {}; - - b = AT91C_BASE_SSC->SSC_RHR; - (void) b; - - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) {}; - - b = AT91C_BASE_SSC->SSC_RHR; - (void) b; - - // wait for the FPGA to signal fdt_indicator == 1 (the FPGA is ready to queue new data in its delay line) - for (uint8_t j = 0; j < 5; j++) { // allow timeout - better late than never - while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) {}; - if (AT91C_BASE_SSC->SSC_RHR) break; - } - - // Clear TXRDY: - //AT91C_BASE_SSC->SSC_THR = 0xFF; -} - -void WaitForFpgaDelayQueueIsEmpty(uint16_t delay) { - // Ensure that the FPGA Delay Queue is empty before we switch to TAGSIM_LISTEN again: - uint8_t fpga_queued_bits = delay >> 3; // twich /8 ?? >>3, - for (uint8_t i = 0; i <= fpga_queued_bits / 8 + 1;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xFF; - i++; - } - } -} static void TransmitFor14443b_AsTag(uint8_t *response, uint16_t len) { - volatile uint32_t b; - // Signal field is off with the appropriate LED LED_D_OFF(); - //uint16_t fpgasendQueueDelay = 0; // Modulate BPSK FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK); - SpinDelay(40); - - ClearFpgaShiftingRegisters(); - - FpgaSetupSsc(); + AT91C_BASE_SSC->SSC_THR = 0xFF; + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // Transmit the response. for (uint16_t i = 0; i < len;) { // Put byte into tx holding register as soon as it is ready if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - AT91C_BASE_SSC->SSC_THR = response[++i]; - } - - // Prevent rx holding register from overflowing - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = AT91C_BASE_SSC->SSC_RHR; - (void)b; + AT91C_BASE_SSC->SSC_THR = response[i++]; } } - - //WaitForFpgaDelayQueueIsEmpty(fpgasendQueueDelay); - AT91C_BASE_SSC->SSC_THR = 0xFF; } //----------------------------------------------------------------------------- // Main loop of simulated tag: receive commands from reader, decide what // response to send, and send it. //----------------------------------------------------------------------------- -void SimulateIso14443bTag(uint32_t pupi) { +void SimulateIso14443bTag(uint8_t *pupi) { + + LED_A_ON(); + // the only commands we understand is WUPB, AFI=0, Select All, N=1: +// static const uint8_t cmdWUPB[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 }; // WUPB + // ... and REQB, AFI=0, Normal Request, N=1: +// static const uint8_t cmdREQB[] = { ISO14443B_REQB, 0x00, 0x00, 0x71, 0xFF }; // REQB + // ... and HLTB +// static const uint8_t cmdHLTB[] = { 0x50, 0xff, 0xff, 0xff, 0xff }; // HLTB + // ... and ATTRIB +// static const uint8_t cmdATTRIB[] = { ISO14443B_ATTRIB, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; // ATTRIB + + // ... if not PUPI/UID is supplied we always respond with ATQB, PUPI = 820de174, Application Data = 0x20381922, + // supports only 106kBit/s in both directions, max frame size = 32Bytes, + // supports ISO14443-4, FWI=8 (77ms), NAD supported, CID not supported: + uint8_t respATQB[] = { + 0x50, + 0x82, 0x0d, 0xe1, 0x74, + 0x20, 0x38, 0x19, + 0x22, 0x00, 0x21, 0x85, + 0x5e, 0xd7 + }; + + // ...PUPI/UID supplied from user. Adjust ATQB response accordingly + if (memcmp("\x00\x00\x00\x00", pupi, 4) != 0) { + memcpy(respATQB + 1, pupi, 4); + AddCrc14B(respATQB, 12); + } + + // response to HLTB and ATTRIB + static const uint8_t respOK[] = {0x00, 0x78, 0xF0}; // setup device. FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // allocate command receive buffer BigBuf_free(); BigBuf_Clear_ext(false); - - clear_trace(); //sim + clear_trace(); set_tracing(true); uint16_t len, cmdsReceived = 0; int cardSTATE = SIM_NOFIELD; int vHf = 0; // in mV - // uint32_t time_0 = 0; - // uint32_t t2r_time = 0; - // uint32_t r2t_time = 0; + + tosend_t *ts = get_tosend(); + uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); - // the only commands we understand is WUPB, AFI=0, Select All, N=1: -// static const uint8_t cmdWUPB[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 }; // WUPB - // ... and REQB, AFI=0, Normal Request, N=1: -// static const uint8_t cmdREQB[] = { ISO14443B_REQB, 0x00, 0x00, 0x71, 0xFF }; // REQB - // ... and ATTRIB -// static const uint8_t cmdATTRIB[] = { ISO14443B_ATTRIB, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; // ATTRIB - - // ... if not PUPI/UID is supplied we always respond with ATQB, PUPI = 820de174, Application Data = 0x20381922, - // supports only 106kBit/s in both directions, max frame size = 32Bytes, - // supports ISO14443-4, FWI=8 (77ms), NAD supported, CID not supported: - uint8_t respATQB[] = { 0x50, 0x82, 0x0d, 0xe1, 0x74, 0x20, 0x38, 0x19, - 0x22, 0x00, 0x21, 0x85, 0x5e, 0xd7 - }; - - // response to HLTB and ATTRIB - static const uint8_t respOK[] = {0x00, 0x78, 0xF0}; - - // ...PUPI/UID supplied from user. Adjust ATQB response accordingly - if (pupi > 0) { - num_to_bytes(pupi, 4, respATQB + 1); - AddCrc14B(respATQB, 12); - } - // prepare "ATQB" tag answer (encoded): CodeIso14443bAsTag(respATQB, sizeof(respATQB)); - uint8_t *encodedATQB = BigBuf_malloc(ToSendMax); - uint16_t encodedATQBLen = ToSendMax; - memcpy(encodedATQB, ToSend, ToSendMax); + uint8_t *encodedATQB = BigBuf_malloc(ts->max); + uint16_t encodedATQBLen = ts->max; + memcpy(encodedATQB, ts->buf, ts->max); // prepare "OK" tag answer (encoded): CodeIso14443bAsTag(respOK, sizeof(respOK)); - uint8_t *encodedOK = BigBuf_malloc(ToSendMax); - uint16_t encodedOKLen = ToSendMax; - memcpy(encodedOK, ToSend, ToSendMax); + uint8_t *encodedOK = BigBuf_malloc(ts->max); + uint16_t encodedOKLen = ts->max; + memcpy(encodedOK, ts->buf, ts->max); // Simulation loop - while (!BUTTON_PRESS() && !data_available()) { + while (BUTTON_PRESS() == false) { WDT_HIT(); + //iceman: limit with 2000 times.. + if (data_available()) { + break; + } + // find reader field if (cardSTATE == SIM_NOFIELD) { @@ -714,15 +684,21 @@ void SimulateIso14443bTag(uint32_t pupi) { // - SLOT MARKER // - ISO7816 // - emulate with a memory dump - Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsReceived); + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsReceived); // CRC Check if (len >= 3) { // if crc exists - if (!check_crc(CRC_14443_B, receivedCmd, len)) - DbpString("+++CRC fail"); - else - DbpString("CRC passes"); + if (!check_crc(CRC_14443_B, receivedCmd, len)) { + if (DBGLEVEL >= DBG_DEBUG) { + DbpString("CRC fail"); + } + } + } else { + if (DBGLEVEL >= DBG_DEBUG) { + DbpString("CRC passed"); + } } cardSTATE = SIM_IDLE; } @@ -734,8 +710,10 @@ void SimulateIso14443bTag(uint32_t pupi) { ++cmdsReceived; } - if (DBGLEVEL >= 2) + + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Emulator stopped. Trace length: %d ", BigBuf_get_traceLen()); + switch_off(); //simulate } @@ -745,6 +723,13 @@ void SimulateIso14443bTag(uint32_t pupi) { // tag's response, which we leave in the buffer to be demodulated on the // PC side. //============================================================================= +// We support both 14b framing and 14b' framing. +// 14b framing looks like: +// xxxxxxxx1111111111111111-000000000011-0........1-0........1-0........1-1-0........1-0........1-1000000000011xxxxxx +// TR1 SOF 10*0+2*1 start-stop ^^^^^^^^byte ^ occasional stuff bit EOF 10*0+N*1 +// 14b' framing looks like: +// xxxxxxxxxxxxxxxx111111111111111111111-0........1-0........1-0........1-1-0........1-0........1-000000000000xxxxxxx +// SOF? start-stop ^^^^^^^^byte ^ occasional stuff bit EOF /* * Handles reception of a bit from the tag @@ -760,303 +745,332 @@ void SimulateIso14443bTag(uint32_t pupi) { * false if we are still waiting for some more * */ -static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { - int v = 0, myI = ABS(ci), myQ = ABS(cq); +static RAMFUNC int Handle14443bSamplesFromTag(int ci, int cq) { + + int v = 0; // The soft decision on the bit uses an estimate of just the // quadrant of the reference angle, not the exact angle. -#define MAKE_SOFT_DECISION(void) { \ - if (Demod.sumI > 0) { \ +#define MAKE_SOFT_DECISION() { \ + if(Demod.sumI > 0) { \ v = ci; \ } else { \ v = -ci; \ } \ - if (Demod.sumQ > 0) { \ + if(Demod.sumQ > 0) { \ v += cq; \ } else { \ v -= cq; \ } \ } -// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by abs(ci) + abs(cq) +#define SUBCARRIER_DETECT_THRESHOLD 8 // Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by max(abs(ci),abs(cq)) + 1/2*min(abs(ci),abs(cq))) -#define CHECK_FOR_SUBCARRIER_old(void) { \ - if (ci < 0) { \ - if (cq < 0) { /* ci < 0, cq < 0 */ \ - if (cq < ci) { \ - v = -cq - (ci >> 1); \ - } else { \ - v = -ci - (cq >> 1); \ - } \ - } else { /* ci < 0, cq >= 0 */ \ - if (cq < -ci) { \ - v = -ci + (cq >> 1); \ - } else { \ - v = cq - (ci >> 1); \ - } \ - } \ - } else { \ - if (cq < 0) { /* ci >= 0, cq < 0 */ \ - if (-cq < ci) { \ - v = ci - (cq >> 1); \ - } else { \ - v = -cq + (ci >> 1); \ - } \ - } else { /* ci >= 0, cq >= 0 */ \ - if (cq < ci) { \ - v = ci + (cq >> 1); \ - } else { \ - v = cq + (ci >> 1); \ - } \ - } \ - } \ - } - -//note: couldn't we just use MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2) from common.h - marshmellow -#define CHECK_FOR_SUBCARRIER(void) { v = MAX(myI, myQ) + (MIN(myI, myQ) >> 1); } +#define AMPLITUDE(ci,cq) (MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2)) switch (Demod.state) { - case DEMOD_UNSYNCD: - CHECK_FOR_SUBCARRIER(); - - // subcarrier detected - - if (v > SUBCARRIER_DETECT_THRESHOLD) { + case DEMOD_UNSYNCD: { + if (AMPLITUDE(ci, cq) > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected Demod.state = DEMOD_PHASE_REF_TRAINING; Demod.sumI = ci; Demod.sumQ = cq; Demod.posCount = 1; } break; + } + case DEMOD_PHASE_REF_TRAINING: { + // While we get a constant signal + if (AMPLITUDE(ci, cq) > SUBCARRIER_DETECT_THRESHOLD) { + if (((ABS(Demod.sumI) > ABS(Demod.sumQ)) && (((ci > 0) && (Demod.sumI > 0)) || ((ci < 0) && (Demod.sumI < 0)))) || // signal closer to horizontal, polarity check based on on I + ((ABS(Demod.sumI) <= ABS(Demod.sumQ)) && (((cq > 0) && (Demod.sumQ > 0)) || ((cq < 0) && (Demod.sumQ < 0))))) { // signal closer to vertical, polarity check based on on Q - case DEMOD_PHASE_REF_TRAINING: - if (Demod.posCount < 8) { - - CHECK_FOR_SUBCARRIER(); - - if (v > SUBCARRIER_DETECT_THRESHOLD) { - // set the reference phase (will code a logic '1') by averaging over 32 1/fs. - // note: synchronization time > 80 1/fs - Demod.sumI += ci; - Demod.sumQ += cq; - Demod.posCount++; + if (Demod.posCount < 10) { // refine signal approximation during first 10 samples + Demod.sumI += ci; + Demod.sumQ += cq; + } + Demod.posCount += 1; } else { - // subcarrier lost - Demod.state = DEMOD_UNSYNCD; + // transition + if (Demod.posCount < 10) { + // subcarrier lost + Demod.state = DEMOD_UNSYNCD; + break; + } else { + // at this point it can be start of 14b' data or start of 14b SOF + MAKE_SOFT_DECISION(); + Demod.posCount = 1; // this was the first half + Demod.thisBit = v; + Demod.shiftReg = 0; + Demod.state = DEMOD_RECEIVING_DATA; + } } } else { - Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; + // subcarrier lost + Demod.state = DEMOD_UNSYNCD; } break; - - case DEMOD_AWAITING_FALLING_EDGE_OF_SOF: - - MAKE_SOFT_DECISION(); - - if (v < 0) { // logic '0' detected - Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF; - Demod.posCount = 0; // start of SOF sequence - } else { - // maximum length of TR1 = 200 1/fs - if (Demod.posCount > 200 / 4) Demod.state = DEMOD_UNSYNCD; - } + } + case DEMOD_AWAITING_START_BIT: { Demod.posCount++; - break; - - case DEMOD_GOT_FALLING_EDGE_OF_SOF: - Demod.posCount++; - MAKE_SOFT_DECISION(); - if (v > 0) { - // low phase of SOF too short (< 9 etu). Note: spec is >= 10, but FPGA tends to "smear" edges - if (Demod.posCount < 9 * 2) { - Demod.state = DEMOD_UNSYNCD; - } else { - LED_C_ON(); // Got SOF - Demod.state = DEMOD_AWAITING_START_BIT; - Demod.posCount = 0; - Demod.len = 0; - } - } else { - // low phase of SOF too long (> 12 etu) - if (Demod.posCount > 14 * 2) { - Demod.state = DEMOD_UNSYNCD; - LED_C_OFF(); - } - } - break; - - case DEMOD_AWAITING_START_BIT: - Demod.posCount++; - - MAKE_SOFT_DECISION(); - - if (v > 0) { - if (Demod.posCount > 6 * 2) { // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs - Demod.state = DEMOD_UNSYNCD; + if (Demod.posCount > 3 * 2) { // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs LED_C_OFF(); + if (Demod.bitCount == 0 && Demod.len == 0) { // received SOF only, this is valid for iClass/Picopass + return true; + } else { + Demod.state = DEMOD_UNSYNCD; + } } } else { // start bit detected - Demod.bitCount = 0; Demod.posCount = 1; // this was the first half Demod.thisBit = v; Demod.shiftReg = 0; Demod.state = DEMOD_RECEIVING_DATA; } break; + } + case WAIT_FOR_RISING_EDGE_OF_SOF: { - case DEMOD_RECEIVING_DATA: + Demod.posCount++; + MAKE_SOFT_DECISION(); + if (v > 0) { + if (Demod.posCount < 9 * 2) { // low phase of SOF too short (< 9 etu). Note: spec is >= 10, but FPGA tends to "smear" edges + Demod.state = DEMOD_UNSYNCD; + } else { + LED_C_ON(); // Got SOF + Demod.posCount = 0; + Demod.bitCount = 0; + Demod.len = 0; + Demod.state = DEMOD_AWAITING_START_BIT; + } + } else { + if (Demod.posCount > 12 * 2) { // low phase of SOF too long (> 12 etu) + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + } + break; + } + case DEMOD_RECEIVING_DATA: { MAKE_SOFT_DECISION(); - if (Demod.posCount == 0) { - // first half of bit + if (Demod.posCount == 0) { // first half of bit Demod.thisBit = v; Demod.posCount = 1; - } else { - // second half of bit + } else { // second half of bit Demod.thisBit += v; - Demod.shiftReg >>= 1; - // OR in a logic '1' - if (Demod.thisBit > 0) + Demod.shiftReg >>= 1; + if (Demod.thisBit > 0) { // logic '1' Demod.shiftReg |= 0x200; + } Demod.bitCount++; - - // 1 start 8 data 1 stop = 10 if (Demod.bitCount == 10) { uint16_t s = Demod.shiftReg; - // stop bit == '1', start bit == '0' - if ((s & 0x200) && (s & 0x001) == 0) { - // left shift to drop the startbit - uint8_t b = (s >> 1); - Demod.output[Demod.len] = b; - ++Demod.len; + if ((s & 0x200) && !(s & 0x001)) { // stop bit == '1', start bit == '0' + Demod.output[Demod.len] = (s >> 1); + Demod.len++; + Demod.bitCount = 0; Demod.state = DEMOD_AWAITING_START_BIT; } else { - // this one is a bit hard, either its a correc byte or its unsynced. - Demod.state = DEMOD_UNSYNCD; - LED_C_OFF(); - - // This is EOF (start, stop and all data bits == '0' - if (s == 0) return true; + if (s == 0x000) { + if (Demod.len > 0) { + LED_C_OFF(); + // This is EOF (start, stop and all data bits == '0' + return true; + } else { + // Zeroes but no data acquired yet? + // => Still in SOF of 14b, wait for raising edge + Demod.posCount = 10 * 2; + Demod.bitCount = 0; + Demod.len = 0; + Demod.state = WAIT_FOR_RISING_EDGE_OF_SOF; + break; + } + } + if (AMPLITUDE(ci, cq) < SUBCARRIER_DETECT_THRESHOLD) { + LED_C_OFF(); + // subcarrier lost + Demod.state = DEMOD_UNSYNCD; + if (Demod.len > 0) { // no EOF but no signal anymore and we got data, e.g. ASK CTx + return true; + } + } + // we have still signal but no proper byte or EOF? this shouldn't happen + //Demod.posCount = 10 * 2; + Demod.bitCount = 0; + Demod.len = 0; + Demod.state = WAIT_FOR_RISING_EDGE_OF_SOF; + break; } } Demod.posCount = 0; } break; - - default: + } + default: { Demod.state = DEMOD_UNSYNCD; LED_C_OFF(); break; + } } return false; } - /* * Demodulate the samples we received from the tag, also log to tracebuffer - * quiet: set to 'TRUE' to disable debug output */ -static void GetTagSamplesFor14443bDemod(void) { - bool finished = false; -// int lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; - uint32_t time_0 = 0, time_stop = 0; +static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, int timeout, uint32_t *eof_time) { - BigBuf_free(); + int samples = 0, ret = 0; // Set up the demodulator for tag -> reader responses. - Demod14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); - - // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t *) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); - int8_t *upTo = dmaBuf; + Demod14bInit(response, max_len); // Setup and start DMA. - if (!FpgaSetupSscDma((uint8_t *) dmaBuf, ISO14443B_DMA_BUFFER_SIZE)) { - if (DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); - return; + //FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + + // 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 (DBGLEVEL > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + return -1; } - // And put the FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); + uint32_t dma_start_time = 0; + uint16_t *upTo = dma->buf; - // get current clock - time_0 = GetCountSspClk(); + // Put FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); - // rx counter - dma counter? (how much?) & (mod) mask > 2. (since 2bytes at the time is read) - while (!finished) { + for (;;) { - LED_A_INV(); - WDT_HIT(); + volatile uint16_t behindBy = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + if (behindBy == 0) + continue; - // LSB is a fpga signal bit. - int ci = upTo[0]; - int cq = upTo[1]; - upTo += 2; -// lastRxCounter -= 2; + samples++; - // restart DMA buffer to receive again. - if (upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { - upTo = dmaBuf; -// lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + if (samples == 1) { + // DMA has transferred the very first data + dma_start_time = GetCountSspClk() & 0xfffffff0; } - // https://github.com/Proxmark/proxmark3/issues/103 - bool gotFrame = Handle14443bTagSamplesDemod(ci, cq); - time_stop = GetCountSspClk() - time_0; + volatile int8_t ci = *upTo >> 8; + volatile int8_t cq = *upTo; + upTo++; - finished = (time_stop > iso14b_timeout || gotFrame); + // we have read all of the DMA buffer content. + if (upTo >= dma->buf + DMA_BUFFER_SIZE) { + + // start reading the circular buffer from the beginning again + upTo = dma->buf; + + // DMA Counter Register had reached 0, already rotated. + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { + + // primary buffer was stopped + if (AT91C_BASE_PDC_SSC->PDC_RCR == false) { + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; + AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; + } + // secondary buffer sets as primary, secondary buffer was stopped + if (AT91C_BASE_PDC_SSC->PDC_RNCR == false) { + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + } + + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString("stopped"); + break; + } + } + } + + if (Handle14443bSamplesFromTag(ci, cq)) { + + *eof_time = dma_start_time + (samples) - DELAY_TAG_TO_ARM; // end of EOF + + if (Demod.len > Demod.max_len) { + ret = -2; // overflow + } + break; + } + + if (samples > timeout && Demod.state < DEMOD_PHASE_REF_TRAINING) { + ret = -1; + break; + } } FpgaDisableSscDma(); + if (ret < 0) { + return ret; + } - if (upTo) - upTo = NULL; - - if (Demod.len > 0) - LogTrace(Demod.output, Demod.len, time_0, time_stop, NULL, false); + if (Demod.len > 0) { + uint32_t sof_time = *eof_time + - (Demod.len * (8 + 2)) // time for byte transfers + - (12) // time for SOF transfer + - (12); // time for EOF transfer + LogTrace(Demod.output, Demod.len, (sof_time * 4), (*eof_time * 4), NULL, false); + } + return Demod.len; } //----------------------------------------------------------------------------- // Transmit the command (to the tag) that was placed in ToSend[]. //----------------------------------------------------------------------------- -static void TransmitFor14443b_AsReader(void) { - int c; +static void TransmitFor14443b_AsReader(uint32_t *start_time) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - SpinDelay(60); + tosend_t *ts = get_tosend(); - // What does this loop do? Is it TR1? - // 0xFF = 8 bits of 1. 1 bit == 1Etu,.. - // loop 10 * 8 = 80 ETU of delay, with a non modulated signal. why? - // 80*9 = 720us. + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); - for (c = 0; c < 50;) { - - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xFF; - c++; - } + // TR2 minimum 14 ETUs + if (*start_time < DELAY_ARM_TO_TAG) { + *start_time = DELAY_ARM_TO_TAG; } - // Send frame loop - for (c = 0; c < ToSendMax;) { + *start_time = (*start_time - DELAY_ARM_TO_TAG) & 0xfffffff0; - // Put byte into tx holding register as soon as it is ready - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c++]; - } + if (GetCountSspClk() > *start_time) { // we may miss the intended time + *start_time = (GetCountSspClk() + 32) & 0xfffffff0; // next possible time } - WDT_HIT(); + + // wait + while (GetCountSspClk() < *start_time); + + LED_B_ON(); + for (int c = 0; c < ts->max; c++) { + volatile uint8_t data = ts->buf[c]; + + for (uint8_t i = 0; i < 8; i++) { + volatile uint16_t send_word = (data & 0x80) ? 0x0000 : 0xFFFF; + + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + + data <<= 1; + } + WDT_HIT(); + } + LED_B_OFF(); + + *start_time += DELAY_ARM_TO_TAG; + + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) {}; } //----------------------------------------------------------------------------- @@ -1073,181 +1087,322 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { * - no modulation ONES * * 1 ETU == 1 BIT! - * TR0 - 8 ETUS minimum. + * TR0 - 8 ETU's minimum. + * TR0 - 32 ETU's maximum for ATQB only + * TR0 - FWT for all other commands * * QUESTION: how long is a 1 or 0 in pulses in the xcorr_848 mode? * 1 "stuffbit" = 1ETU (9us) + * + * TR2 - After the PICC response, the PCD is required to wait the Frame Delay Time (TR2) + before transmission of the next command. The minimum frame delay time required for + all commands is 14 ETUs + * */ - - ToSendReset(); + int i; + tosend_reset(); // Send SOF // 10-11 ETUs of ZERO - for (int i = 0; i < 10; ++i) ToSendStuffBit(0); - + for (i = 0; i < 11; i++) { + tosend_stuffbit(0); + } // 2-3 ETUs of ONE - ToSendStuffBit(1); - ToSendStuffBit(1); -// ToSendStuffBit(1); + tosend_stuffbit(1); + tosend_stuffbit(1); // Sending cmd, LSB // from here we add BITS - for (int i = 0; i < len; ++i) { + for (i = 0; i < len; i++) { // Start bit - ToSendStuffBit(0); - // Data bits - uint8_t b = cmd[i]; - // if ( b & 1 ) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>1) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>2) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>3) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>4) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>5) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>6) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); - // if ( (b>>7) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); + tosend_stuffbit(0); - ToSendStuffBit(b & 1); - ToSendStuffBit((b >> 1) & 1); - ToSendStuffBit((b >> 2) & 1); - ToSendStuffBit((b >> 3) & 1); - ToSendStuffBit((b >> 4) & 1); - ToSendStuffBit((b >> 5) & 1); - ToSendStuffBit((b >> 6) & 1); - ToSendStuffBit((b >> 7) & 1); + // Data bits + volatile uint8_t b = cmd[i]; + tosend_stuffbit(b & 1); + tosend_stuffbit((b >> 1) & 1); + tosend_stuffbit((b >> 2) & 1); + tosend_stuffbit((b >> 3) & 1); + tosend_stuffbit((b >> 4) & 1); + tosend_stuffbit((b >> 5) & 1); + tosend_stuffbit((b >> 6) & 1); + tosend_stuffbit((b >> 7) & 1); // Stop bit - ToSendStuffBit(1); - // EGT extra guard time - // For PCD it ranges 0-57us (1etu = 9us) - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); + tosend_stuffbit(1); + // EGT extra guard time 1 ETU = 9us + // For PCD it ranges 0-57us === 0 - 6 ETU + // FOR PICC it ranges 0-19us == 0 - 2 ETU } // Send EOF // 10-11 ETUs of ZERO - for (int i = 0; i < 10; ++i) ToSendStuffBit(0); + for (i = 0; i < 11; i++) { + tosend_stuffbit(0); + } - // Transition time. TR0 - guard time - // 8ETUS minum? - // Per specification, Subcarrier must be stopped no later than 2 ETUs after EOF. - // I'm guessing this is for the FPGA to be able to send all bits before we switch to listening mode - for (int i = 0; i < 24 ; ++i) ToSendStuffBit(1); + /* Transition time. TR0 - guard time + * TR0 - 8 ETU's minimum. + * TR0 - 32 ETU's maximum for ATQB only + * TR0 - FWT for all other commands + * 32,64,128,256,512, ... , 262144, 524288 ETU + */ + int pad = (11 + 2 + (len * 10) + 11) & 0x7; - // TR1 - Synchronization time - // Convert from last character reference to length - ToSendMax++; + for (i = 0; i < 16 - pad; ++i) + tosend_stuffbit(1); } /* * Convenience function to encode, transmit and trace iso 14443b comms */ -static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) { - - uint32_t time_start = GetCountSspClk(); - +static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time) { + tosend_t *ts = get_tosend(); CodeIso14443bAsReader(cmd, len); - - TransmitFor14443b_AsReader(); - - if (g_trigger) LED_A_ON(); - - LogTrace(cmd, len, time_start, GetCountSspClk(), NULL, true); + TransmitFor14443b_AsReader(start_time); + *eof_time = *start_time + (10 * ts->max) + 10 + 2 + 10; + LogTrace(cmd, len, *start_time, *eof_time, NULL, true); } /* Sends an APDU to the tag * TODO: check CRC and preamble */ -uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response) { +int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void *rxdata, uint16_t rxmaxlen, uint8_t *res) { + + 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); + } else { + // R-block. ACK + real_cmd[0] = 0xA2; // r-block + ACK + real_cmd[0] |= iso14b_pcb_blocknum; + } + + AddCrc14B(real_cmd, msg_len + 1); - uint8_t message_frame[message_length + 4]; - // PCB - message_frame[0] = 0x0A | pcb_blocknum; - pcb_blocknum ^= 1; - // CID - message_frame[1] = 0; - // INF - memcpy(message_frame + 2, message, message_length); - // EDC (CRC) - AddCrc14B(message_frame, message_length + 2); // send - CodeAndTransmit14443bAsReader(message_frame, message_length + 4); //no - // get response - GetTagSamplesFor14443bDemod(); //no + uint32_t start_time = 0; + uint32_t eof_time = 0; + CodeAndTransmit14443bAsReader(real_cmd, msg_len + 3, &start_time, &eof_time); + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + int len = Get14443bAnswerFromTag(rxdata, rxmaxlen, ISO14443B_READER_TIMEOUT, &eof_time); FpgaDisableTracing(); - if (Demod.len < 3) - return 0; + uint8_t *data_bytes = (uint8_t *) rxdata; - // VALIDATE CRC - if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) { - if (DBGLEVEL > 3) Dbprintf("crc fail ICE"); - return 0; + if (len <= 0) { + return 0; //DATA LINK ERROR + } else { + // S-Block WTX + while (len && ((data_bytes[0] & 0xF2) == 0xF2)) { + uint32_t save_iso14b_timeout = iso14b_timeout; + // temporarily increase timeout + iso14b_set_timeout(MAX((data_bytes[1] & 0x3f) * save_iso14b_timeout, ISO14443B_READER_TIMEOUT)); + // Transmit WTX back + // byte1 - WTXM [1..59]. command FWT = FWT * WTXM + data_bytes[1] = data_bytes[1] & 0x3f; // 2 high bits mandatory set to 0b + // now need to fix CRC. + AddCrc14B(data_bytes, len - 2); + + // transmit S-Block + CodeAndTransmit14443bAsReader(data_bytes, len, &start_time, &eof_time); + + // retrieve the result again (with increased timeout) + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + len = Get14443bAnswerFromTag(rxdata, rxmaxlen, ISO14443B_READER_TIMEOUT, &eof_time); + FpgaDisableTracing(); + + data_bytes = rxdata; + // restore timeout + iso14b_set_timeout(save_iso14b_timeout); + } + + // 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 + iso14b_pcb_blocknum ^= 1; + } + + // if we received I-block with chaining we need to send ACK and receive another block of data + if (res) + *res = data_bytes[0]; + + // crc check + if (len >= 3 && !check_crc(CRC_14443_B, data_bytes, len)) { + return -1; + } } - // copy response contents - if (response != NULL) - memcpy(response, Demod.output, Demod.len); - return Demod.len; + 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]; + } + + return len; } /** -* SRx Initialise. +* ASK CTS initialise. */ -static uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { +static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { // INITIATE command: wake up the tag using the INITIATE - static const uint8_t init_srx[] = { ISO14443B_INITIATE, 0x00, 0x97, 0x5b }; - // SELECT command (with space for CRC) - uint8_t select_srx[] = { ISO14443B_SELECT, 0x00, 0x00, 0x00}; + uint8_t cmdINIT[] = {ASK_REQT, 0xF9, 0xE0}; + uint8_t cmdMSBUID[] = {ASK_SELECT, 0xFF, 0xFF, 0x00, 0x00}; + uint8_t cmdLSBUID[] = {0xC4, 0x00, 0x00}; - CodeAndTransmit14443bAsReader(init_srx, sizeof(init_srx)); - GetTagSamplesFor14443bDemod(); //no + AddCrc14B(cmdMSBUID, 3); + AddCrc14B(cmdLSBUID, 1); + + uint8_t r[8]; + + uint32_t start_time = 0; + uint32_t eof_time = 0; + CodeAndTransmit14443bAsReader(cmdINIT, sizeof(cmdINIT), &start_time, &eof_time); + + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + int retlen = Get14443bAnswerFromTag(r, sizeof(r), ISO14443B_READER_TIMEOUT, &eof_time); FpgaDisableTracing(); - if (Demod.len == 0) - return 2; + if (retlen != 4) { + return -1; + } + if (check_crc(CRC_14443_B, r, retlen) == false) { + return -2; + } + + if (card) { + // pc. fc Product code, Facility code + card->pc = r[0]; + card->fc = r[1]; + } + + start_time = eof_time + DELAY_ISO14443B_VICC_TO_VCD_READER; + CodeAndTransmit14443bAsReader(cmdMSBUID, sizeof(cmdMSBUID), &start_time, &eof_time); + + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + retlen = Get14443bAnswerFromTag(r, sizeof(r), ISO14443B_READER_TIMEOUT, &eof_time); + FpgaDisableTracing(); + + if (retlen != 4) { + return -1; + } + if (check_crc(CRC_14443_B, r, retlen) == false) { + return -2; + } + + if (card) { + memcpy(card->uid, r, 2); + } + + start_time = eof_time + DELAY_ISO14443B_VICC_TO_VCD_READER; + CodeAndTransmit14443bAsReader(cmdLSBUID, sizeof(cmdLSBUID), &start_time, &eof_time); + + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + retlen = Get14443bAnswerFromTag(r, sizeof(r), ISO14443B_READER_TIMEOUT, &eof_time); + FpgaDisableTracing(); + + if (retlen != 4) { + return -1; + } + if (check_crc(CRC_14443_B, r, retlen) == false) { + return -2; + } + + if (card) { + memcpy(card->uid + 2, r, 2); + } + + return 0; +} +/** +* SRx Initialise. +*/ +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}; + + uint32_t start_time = 0; + uint32_t eof_time = 0; + CodeAndTransmit14443bAsReader(init_srx, sizeof(init_srx), &start_time, &eof_time); + + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + int retlen = Get14443bAnswerFromTag(r_init, sizeof(r_init), ISO14443B_READER_TIMEOUT, &eof_time); + FpgaDisableTracing(); + + if (retlen <= 0) { + return -1; + } // Randomly generated Chip ID - if (card) card->chipid = Demod.output[0]; + if (card) { + card->chipid = Demod.output[0]; + } - select_srx[1] = Demod.output[0]; + // SELECT command (with space for CRC) + uint8_t select_srx[] = { ISO14443B_SELECT, 0x00, 0x00, 0x00}; + select_srx[1] = r_init[0]; AddCrc14B(select_srx, 2); - CodeAndTransmit14443bAsReader(select_srx, sizeof(select_srx)); - GetTagSamplesFor14443bDemod(); //no + start_time = eof_time + DELAY_ISO14443B_VICC_TO_VCD_READER; + CodeAndTransmit14443bAsReader(select_srx, sizeof(select_srx), &start_time, &eof_time); + + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + retlen = Get14443bAnswerFromTag(r_select, sizeof(r_select), ISO14443B_READER_TIMEOUT, &eof_time); FpgaDisableTracing(); - if (Demod.len != 3) - return 2; - - // Check the CRC of the answer: - if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) - return 3; + if (retlen != 3) { + return -1; + } + if (!check_crc(CRC_14443_B, r_select, retlen)) { + return -2; + } // Check response from the tag: should be the same UID as the command we just sent: - if (select_srx[1] != Demod.output[0]) - return 1; + if (select_srx[1] != r_select[0]) { + return -3; + } // First get the tag's UID: select_srx[0] = ISO14443B_GET_UID; AddCrc14B(select_srx, 1); - CodeAndTransmit14443bAsReader(select_srx, 3); // Only first three bytes for this one - GetTagSamplesFor14443bDemod(); //no + + start_time = eof_time + DELAY_ISO14443B_VICC_TO_VCD_READER; + CodeAndTransmit14443bAsReader(select_srx, 3, &start_time, &eof_time); // Only first three bytes for this one + + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + retlen = Get14443bAnswerFromTag(r_papid, sizeof(r_papid), ISO14443B_READER_TIMEOUT, &eof_time); FpgaDisableTracing(); - if (Demod.len != 10) - return 2; - - // The check the CRC of the answer - if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) - return 3; + if (retlen != 10) { + return -1; + } + if (!check_crc(CRC_14443_B, r_papid, retlen)) { + return -2; + } if (card) { card->uidlen = 8; - memcpy(card->uid, Demod.output, 8); + memcpy(card->uid, r_papid, 8); } return 0; @@ -1258,55 +1413,71 @@ static uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { * TODO: Support multiple cards (perform anticollision) * TODO: Verify CRC checksums */ -uint8_t iso14443b_select_card(iso14b_card_select_t *card) { +int iso14443b_select_card(iso14b_card_select_t *card) { // WUPB command (including CRC) // Note: WUPB wakes up all tags, REQB doesn't wake up tags in HALT state - static const uint8_t wupb[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 }; + // WUTB or REQB is denoted in the third byte, lower nibble. 0 vs 8 + //static const uint8_t wupb[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 }; + 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}; + // first, wake up the tag - CodeAndTransmit14443bAsReader(wupb, sizeof(wupb)); - GetTagSamplesFor14443bDemod(); //select_card + uint32_t start_time = 0; + uint32_t eof_time = 0; + CodeAndTransmit14443bAsReader(wupb, sizeof(wupb), &start_time, &eof_time); + + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER;; + int retlen = Get14443bAnswerFromTag(r_pupid, sizeof(r_pupid), ISO14443B_READER_TIMEOUT, &eof_time); FpgaDisableTracing(); // ATQB too short? - if (Demod.len < 14) - return 2; + if (retlen < 14) { + return -1; + } // VALIDATE CRC - if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) - return 3; + if (!check_crc(CRC_14443_B, r_pupid, retlen)) { + return -2; + } if (card) { card->uidlen = 4; - memcpy(card->uid, Demod.output + 1, 4); - memcpy(card->atqb, Demod.output + 5, 7); + memcpy(card->uid, r_pupid + 1, 4); + memcpy(card->atqb, r_pupid + 5, 7); } // copy the PUPI to ATTRIB ( PUPI == UID ) - memcpy(attrib + 1, Demod.output + 1, 4); + memcpy(attrib + 1, r_pupid + 1, 4); // copy the protocol info from ATQB (Protocol Info -> Protocol_Type) into ATTRIB (Param 3) - attrib[7] = Demod.output[10] & 0x0F; + attrib[7] = r_pupid[10] & 0x0F; AddCrc14B(attrib, 9); + start_time = eof_time + DELAY_ISO14443B_VICC_TO_VCD_READER; + CodeAndTransmit14443bAsReader(attrib, sizeof(attrib), &start_time, &eof_time); - CodeAndTransmit14443bAsReader(attrib, sizeof(attrib)); - GetTagSamplesFor14443bDemod();//select_card + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + retlen = Get14443bAnswerFromTag(r_attrib, sizeof(r_attrib), ISO14443B_READER_TIMEOUT, &eof_time); FpgaDisableTracing(); // Answer to ATTRIB too short? - if (Demod.len < 3) - return 2; + if (retlen < 3) { + return -1; + } // VALIDATE CRC - if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) - return 3; + if (!check_crc(CRC_14443_B, r_attrib, retlen)) { + return -2; + } if (card) { // CID - card->cid = Demod.output[0]; + card->cid = r_attrib[0]; // MAX FRAME uint16_t maxFrame = card->atqb[5] >> 4; @@ -1326,7 +1497,7 @@ uint8_t iso14443b_select_card(iso14b_card_select_t *card) { } } // reset PCB block number - pcb_blocknum = 0; + iso14b_pcb_blocknum = 0; return 0; } @@ -1336,19 +1507,23 @@ void iso14443b_setup(void) { LEDsoff(); 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)); + Demod14bInit(BigBuf_malloc(MAX_FRAME_SIZE), MAX_FRAME_SIZE); Uart14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // Signal field is on with the appropriate LED - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - SpinDelay(100); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); + SpinDelay(50); // Start the timer StartCountSspClk(); @@ -1365,108 +1540,79 @@ void iso14443b_setup(void) { // // I tried to be systematic and check every answer of the tag, every CRC, etc... //----------------------------------------------------------------------------- -static bool ReadSTBlock(uint8_t block) { - uint8_t cmd[] = {ISO14443B_READ_BLK, block, 0x00, 0x00}; +static bool ReadSTBlock(uint8_t blocknr, uint8_t *block) { + uint8_t cmd[] = {ISO14443B_READ_BLK, blocknr, 0x00, 0x00}; AddCrc14B(cmd, 2); - CodeAndTransmit14443bAsReader(cmd, sizeof(cmd)); - GetTagSamplesFor14443bDemod(); + + uint8_t r_block[6] = {0}; + + uint32_t start_time = 0; + uint32_t eof_time = 0; + CodeAndTransmit14443bAsReader(cmd, sizeof(cmd), &start_time, &eof_time); + + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + int retlen = Get14443bAnswerFromTag(r_block, sizeof(r_block), ISO14443B_READER_TIMEOUT, &eof_time); FpgaDisableTracing(); // Check if we got an answer from the tag - if (Demod.len != 6) { + if (retlen != 6) { DbpString("[!] expected 6 bytes from tag, got less..."); return false; } // The check the CRC of the answer - if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) { - DbpString("[!] CRC Error block!"); + if (!check_crc(CRC_14443_B, r_block, retlen)) { + DbpString("CRC fail"); return false; } + + if (block) { + memcpy(block, r_block, 4); + } + + Dbprintf("Address=%02x, Contents=%08x, CRC=%04x", + blocknr, + (r_block[3] << 24) + (r_block[2] << 16) + (r_block[1] << 8) + r_block[0], + (r_block[4] << 8) + r_block[5]); + return true; } -void ReadSTMemoryIso14443b(uint8_t numofblocks) { - // Make sure that we start from off, since the tags are stateful; - // confusing things will happen if we don't reset them between reads. - //switch_off(); - uint8_t i = 0x00; - uint8_t *buf = BigBuf_malloc(sizeof(iso14b_card_select_t)); +void ReadSTMemoryIso14443b(uint16_t numofblocks) { iso14443b_setup(); - iso14b_card_select_t *card = (iso14b_card_select_t *)buf; - uint8_t res = iso14443b_select_srx_card(card); + uint8_t *mem = BigBuf_malloc((numofblocks + 1) * 4); + + iso14b_card_select_t card; + int res = iso14443b_select_srx_card(&card); + int isOK = PM3_SUCCESS; // 0: OK 2: attrib fail, 3:crc fail, - if (res > 0) goto out; - - Dbprintf("[+] Tag memory dump, block 0 to %d", numofblocks); + if (res < 1) { + isOK = PM3_ETIMEOUT; + goto out; + } ++numofblocks; - for (;;) { - if (i == numofblocks) { - DbpString("System area block (0xFF):"); - i = 0xff; + for (uint8_t i = 0; i < numofblocks; i++) { + + if (ReadSTBlock(i, mem + (i * 4)) == false) { + isOK = PM3_ETIMEOUT; + break; } - - uint8_t retries = 3; - do { - res = ReadSTBlock(i); - } while (!res && --retries); - - if (!res && !retries) { - goto out; - } - - // Now print out the memory location: - Dbprintf("Address=%02x, Contents=%08x, CRC=%04x", i, - (Demod.output[3] << 24) + (Demod.output[2] << 16) + (Demod.output[1] << 8) + Demod.output[0], - (Demod.output[4] << 8) + Demod.output[5]); - - if (i == 0xff) break; - ++i; } + // System area block (0xFF) + if (ReadSTBlock(0xFF, mem + (numofblocks * 4)) == false) + isOK = PM3_ETIMEOUT; + out: - switch_off(); // disconnect raw - SpinDelay(20); -} -static void iso1444b_setup_sniff(void) { + reply_ng(CMD_HF_SRI_READ, isOK, mem, numofblocks * 4); - LEDsoff(); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); BigBuf_free(); - BigBuf_Clear_ext(false); - clear_trace(); - set_tracing(true); - - // Initialize Demod and Uart structs - Demod14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); - Uart14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); - - if (DBGLEVEL > 1) { - // Print debug information about the buffer sizes - Dbprintf("[+] Sniff buffers initialized:"); - Dbprintf("[+] trace: %i bytes", BigBuf_max_traceLen()); - Dbprintf("[+] reader -> tag: %i bytes", MAX_FRAME_SIZE); - Dbprintf("[+] tag -> reader: %i bytes", MAX_FRAME_SIZE); - Dbprintf("[+] DMA: %i bytes", ISO14443B_DMA_BUFFER_SIZE); - } - - // connect Demodulated Signal to ADC: - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - // Setup for the DMA. - FpgaSetupSsc(); - - // Set FPGA in the appropriate mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); - SpinDelay(20); - - // Start the SSP timer - StartCountSspClk(); + switch_off(); } //============================================================================= @@ -1486,95 +1632,181 @@ static void iso1444b_setup_sniff(void) { * DMA Buffer - ISO14443B_DMA_BUFFER_SIZE * Demodulated samples received - all the rest */ -void RAMFUNC SniffIso14443b(void) { +void SniffIso14443b(void) { - uint32_t time_0 = 0, time_start = 0, time_stop; + LEDsoff(); + LED_A_ON(); + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + DbpString("Starting to sniff. Press PM3 Button to stop."); + + BigBuf_free(); + clear_trace(); + set_tracing(true); + + // Initialize Demod and Uart structs + uint8_t dm_buf[MAX_FRAME_SIZE] = {0}; + Demod14bInit(dm_buf, sizeof(dm_buf)); + + uint8_t ua_buf[MAX_FRAME_SIZE] = {0}; + Uart14bInit(ua_buf); + + //Demod14bInit(BigBuf_malloc(MAX_FRAME_SIZE), MAX_FRAME_SIZE); + //Uart14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); + + // Set FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_SNIFF_IQ); +// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_848_KHZ | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); + + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + + StartCountSspClk(); + + // The DMA buffer, used to stream samples from the FPGA + dmabuf16_t *dma = get_dma16(); + + // Setup and start DMA. + if (!FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE)) { + if (DBGLEVEL > DBG_ERROR) DbpString("FpgaSetupSscDma failed. Exiting"); + switch_off(); + return; + } // We won't start recording the frames that we acquire until we trigger; // a good trigger condition to get started is probably when we see a // response from the tag. - bool TagIsActive = false; - bool ReaderIsActive = false; + bool tag_is_active = false; + bool reader_is_active = false; + bool expect_tag_answer = false; + int dma_start_time = 0; - iso1444b_setup_sniff(); + // Count of samples received so far, so that we can include timing + int samples = 0; - // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = (int8_t *) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); - int8_t *data = dmaBuf; + uint16_t *upTo = dma->buf; - // Setup and start DMA. - if (!FpgaSetupSscDma((uint8_t *) dmaBuf, ISO14443B_DMA_BUFFER_SIZE)) { - if (DBGLEVEL > 1) Dbprintf("[!] FpgaSetupSscDma failed. Exiting"); - BigBuf_free(); - return; - } + for (;;) { - // time ZERO, the point from which it all is calculated. - time_0 = GetCountSspClk(); + volatile int behind_by = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + if (behind_by < 1) continue; - // loop and listen - while (!BUTTON_PRESS()) { - WDT_HIT(); + samples++; + if (samples == 1) { + // DMA has transferred the very first data + dma_start_time = GetCountSspClk() & 0xfffffff0; + } - int ci = data[0]; - int cq = data[1]; - data += 2; + volatile int8_t ci = *upTo >> 8; + volatile int8_t cq = *upTo; + upTo++; - if (data >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { - data = dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + // we have read all of the DMA buffer content. + if (upTo >= dma->buf + DMA_BUFFER_SIZE) { + + // start reading the circular buffer from the beginning again + upTo = dma->buf; + + // DMA Counter Register had reached 0, already rotated. + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { + + // primary buffer was stopped + if (AT91C_BASE_PDC_SSC->PDC_RCR == false) { + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; + AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; + } + // secondary buffer sets as primary, secondary buffer was stopped + if (AT91C_BASE_PDC_SSC->PDC_RNCR == false) { + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + } + + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString("Sniff stopped"); + break; + } + } } // no need to try decoding reader data if the tag is sending - if (!TagIsActive) { + if (tag_is_active == false) { - LED_A_INV(); - - if (Handle14443bReaderUartBit(ci & 0x01)) { - time_stop = GetCountSspClk() - time_0; - LogTrace(Uart.output, Uart.byteCnt, time_start, time_stop, NULL, true); + if (Handle14443bSampleFromReader(ci & 0x01)) { + uint32_t eof_time = dma_start_time + (samples * 16) + 8; // - DELAY_READER_TO_ARM_SNIFF; // end of EOF + if (Uart.byteCnt > 0) { + uint32_t sof_time = eof_time + - Uart.byteCnt * 1 // time for byte transfers + - 32 * 16 // time for SOF transfer + - 16 * 16; // time for EOF transfer + LogTrace(Uart.output, Uart.byteCnt, (sof_time * 4), (eof_time * 4), NULL, true); + } + // And ready to receive another command. Uart14bReset(); Demod14bReset(); - } else { - time_start = GetCountSspClk() - time_0; + reader_is_active = false; + expect_tag_answer = true; } - if (Handle14443bReaderUartBit(cq & 0x01)) { - time_stop = GetCountSspClk() - time_0; - LogTrace(Uart.output, Uart.byteCnt, time_start, time_stop, NULL, true); + if (Handle14443bSampleFromReader(cq & 0x01)) { + + uint32_t eof_time = dma_start_time + (samples * 16) + 16; // - DELAY_READER_TO_ARM_SNIFF; // end of EOF + if (Uart.byteCnt > 0) { + uint32_t sof_time = eof_time + - Uart.byteCnt * 1 // time for byte transfers + - 32 * 16 // time for SOF transfer + - 16 * 16; // time for EOF transfer + LogTrace(Uart.output, Uart.byteCnt, (sof_time * 4), (eof_time * 4), NULL, true); + } + // And ready to receive another command Uart14bReset(); Demod14bReset(); - } else { - time_start = GetCountSspClk() - time_0; + reader_is_active = false; + expect_tag_answer = true; } - ReaderIsActive = (Uart.state > STATE_14B_GOT_FALLING_EDGE_OF_SOF); + + reader_is_active = (Uart.state > STATE_14B_GOT_FALLING_EDGE_OF_SOF); } // no need to try decoding tag data if the reader is sending - and we cannot afford the time - if (!ReaderIsActive) { + if (reader_is_active == false && expect_tag_answer) { - // is this | 0x01 the error? & 0xfe in https://github.com/Proxmark/proxmark3/issues/103 - // LSB is a fpga signal bit. - if (Handle14443bTagSamplesDemod(ci, cq)) { - time_stop = GetCountSspClk() - time_0; - LogTrace(Demod.output, Demod.len, time_start, time_stop, NULL, false); + if (Handle14443bSamplesFromTag((ci >> 1), (cq >> 1))) { + + uint32_t eof_time = dma_start_time + (samples * 16); // - DELAY_TAG_TO_ARM_SNIFF; // end of EOF + uint32_t sof_time = eof_time + - Demod.len * 8 * 8 * 16 // time for byte transfers + - (32 * 16) // time for SOF transfer + - 0; // time for EOF transfer + + LogTrace(Demod.output, Demod.len, (sof_time * 4), (eof_time * 4), NULL, false); + // And ready to receive another response. Uart14bReset(); Demod14bReset(); + expect_tag_answer = false; + tag_is_active = false; } else { - time_start = GetCountSspClk() - time_0; + tag_is_active = (Demod.state > WAIT_FOR_RISING_EDGE_OF_SOF); } - TagIsActive = (Demod.state > DEMOD_GOT_FALLING_EDGE_OF_SOF); } } - if (DBGLEVEL >= 2) { - DbpString("[+] Sniff statistics:"); - Dbprintf("[+] uart State: %x ByteCount: %i ByteCountMax: %i", Uart.state, Uart.byteCnt, Uart.byteCntMax); - Dbprintf("[+] trace length: %i", BigBuf_get_traceLen()); - } - + FpgaDisableTracing(); switch_off(); + + DbpString(""); + DbpString(_CYAN_("Sniff statistics")); + DbpString("================================="); + Dbprintf(" DecodeTag State........%d", Demod.state); + Dbprintf(" DecodeTag byteCnt......%d", Demod.len); + Dbprintf(" DecodeTag posCount.....%d", Demod.posCount); + Dbprintf(" DecodeReader State.....%d", Uart.state); + Dbprintf(" DecodeReader byteCnt...%d", Uart.byteCnt); + Dbprintf(" DecodeReader posCount..%d", Uart.posCnt); + Dbprintf(" Trace length..........." _YELLOW_("%d"), BigBuf_get_traceLen()); + DbpString(""); } static void iso14b_set_trigger(bool enable) { @@ -1593,15 +1825,14 @@ static void iso14b_set_trigger(bool enable) { * */ void SendRawCommand14443B_Ex(PacketCommandNG *c) { + iso14b_command_t param = c->oldarg[0]; size_t len = c->oldarg[1] & 0xffff; uint32_t timeout = c->oldarg[2]; uint8_t *cmd = c->data.asBytes; - uint8_t status; - uint32_t sendlen = sizeof(iso14b_card_select_t); uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00}; - if (DBGLEVEL > 3) Dbprintf("14b raw: param, %04x", param); + if (DBGLEVEL > DBG_DEBUG) Dbprintf("14b raw: param, %04x", param); // turn on trigger (LED_A) if ((param & ISO14B_REQUEST_TRIGGER) == ISO14B_REQUEST_TRIGGER) @@ -1609,33 +1840,50 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) { if ((param & ISO14B_CONNECT) == ISO14B_CONNECT) { iso14443b_setup(); - clear_trace(); } - if ((param & ISO14B_SET_TIMEOUT)) + if ((param & ISO14B_SET_TIMEOUT) == ISO14B_SET_TIMEOUT) { iso14b_set_timeout(timeout); + } + if ((param & ISO14B_CLEARTRACE) == ISO14B_CLEARTRACE) { + clear_trace(); + } set_tracing(true); + int status; + uint32_t sendlen = sizeof(iso14b_card_select_t); + iso14b_card_select_t card; + memset((void *)&card, 0x00, sizeof(card)); + if ((param & ISO14B_SELECT_STD) == ISO14B_SELECT_STD) { - iso14b_card_select_t *card = (iso14b_card_select_t *)buf; - status = iso14443b_select_card(card); - reply_mix(CMD_ACK, status, sendlen, 0, buf, sendlen); - // 0: OK 2: attrib fail, 3:crc fail, - if (status > 0) goto out; + 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; } if ((param & ISO14B_SELECT_SR) == ISO14B_SELECT_SR) { - iso14b_card_select_t *card = (iso14b_card_select_t *)buf; - status = iso14443b_select_srx_card(card); - reply_mix(CMD_ACK, status, sendlen, 0, buf, sendlen); + 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 ((param & 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; } if ((param & ISO14B_APDU) == ISO14B_APDU) { - status = iso14443b_apdu(cmd, len, buf); - reply_mix(CMD_ACK, status, status, 0, buf, status); + uint8_t res; + status = iso14443b_apdu(cmd, len, (param & 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); } if ((param & ISO14B_RAW) == ISO14B_RAW) { @@ -1643,14 +1891,16 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) { AddCrc14B(cmd, len); len += 2; } + uint32_t start_time = 0; + uint32_t eof_time = 0; + CodeAndTransmit14443bAsReader(cmd, len, &start_time, &eof_time); - CodeAndTransmit14443bAsReader(cmd, len); // raw - GetTagSamplesFor14443bDemod(); // raw + eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; + status = Get14443bAnswerFromTag(buf, sizeof(buf), 5 * ISO14443B_READER_TIMEOUT, &eof_time); // raw FpgaDisableTracing(); sendlen = MIN(Demod.len, PM3_CMD_DATA_SIZE); - status = (Demod.len > 0) ? 0 : 1; - reply_old(CMD_ACK, status, sendlen, 0, Demod.output, sendlen); + reply_mix(CMD_HF_ISO14443B_COMMAND, status, sendlen, 0, Demod.output, sendlen); } out: diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index 62dbd6590..f770bbf8c 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -15,7 +15,7 @@ #include "common.h" -#include "mifare.h" +#include "iso14b.h" #include "pm3_cmd.h" #ifndef AddCrc14A @@ -27,21 +27,18 @@ #endif void iso14443b_setup(void); -uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response); -uint8_t iso14443b_select_card(iso14b_card_select_t *card); -uint8_t iso14443b_select_card_srx(iso14b_card_select_t *card); +int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void *rxdata, uint16_t rxmaxlen, uint8_t *res); -void SimulateIso14443bTag(uint32_t pupi); +int iso14443b_select_card(iso14b_card_select_t *card); +int iso14443b_select_card_srx(iso14b_card_select_t *card); + +void SimulateIso14443bTag(uint8_t *pupi); void AcquireRawAdcSamplesIso14443b(uint32_t parameter); -void ReadSTMemoryIso14443b(uint8_t numofblocks); -void RAMFUNC SniffIso14443b(void); +void ReadSTMemoryIso14443b(uint16_t numofblocks); +void SniffIso14443b(void); void SendRawCommand14443B(uint32_t, uint32_t, uint8_t, uint8_t[]); void SendRawCommand14443B_Ex(PacketCommandNG *c); -// testfunctions -void WaitForFpgaDelayQueueIsEmpty(uint16_t delay); -void ClearFpgaShiftingRegisters(void); - // States for 14B SIM command #define SIM_NOFIELD 0 #define SIM_IDLE 1 diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index d88bae1f0..e84edd8a0 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -2,25 +2,23 @@ // Jonathan Westhues, split Nov 2006 // Modified by Greg Jones, Jan 2009 // Modified by Adrian Dabrowski "atrox", Mar-Sept 2010,Oct 2011 -// Modified by Christian Herrmann "iceman", 2017 +// Modified by Christian Herrmann "iceman", 2017, 2020 +// Modified by piwi, Oct 2018 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- // Routines to support ISO 15693. This includes both the reader software and -// the `fake tag' modes, but at the moment I've implemented only the reader -// stuff, and that barely. -// Modified to perform modulation onboard in arm rather than on PC -// Also added additional reader commands (SELECT, READ etc.) +// the `fake tag' modes. //----------------------------------------------------------------------------- -// The ISO 15693 describes two transmission modes from reader to tag, and 4 -// transmission modes from tag to reader. As of Mar 2010 this code only -// supports one of each: "1of4" mode from reader to tag, and the highspeed -// variant with one subcarrier from card to reader. -// As long, as the card fully support ISO 15693 this is no problem, since the -// reader chooses both data rates, but some non-standard tags do not. Further for -// the simulation to work, we will need to support all data rates. + +// The ISO 15693 describes two transmission modes from reader to tag, and four +// transmission modes from tag to reader. As of Oct 2018 this code supports +// both reader modes and the high speed variant with one subcarrier from card to reader. +// As long as the card fully support ISO 15693 this is no problem, since the +// reader chooses both data rates, but some non-standard tags do not. +// For card simulation, the code supports both high and low speed modes with one subcarrier. // // VCD (reader) -> VICC (tag) // 1 out of 256: @@ -47,15 +45,11 @@ // *) UID is always used "transmission order" (LSB), which is reverse to display order // TODO / BUGS / ISSUES: -// *) writing to tags takes longer: we miss the answer from the tag in most cases -// -> tweak the read-timeout times -// *) signal decoding from the card is still a bit shaky. -// *) signal decoding is unable to detect collissions. -// *) add anti-collission support for inventory-commands +// *) signal decoding is unable to detect collisions. +// *) add anti-collision support for inventory-commands // *) read security status of a block -// *) sniffing and simulation do only support one transmission mode. need to support -// all 8 transmission combinations -// *) remove or refactor code under "depricated" +// *) sniffing and simulation do not support two subcarrier modes. +// *) remove or refactor code under "deprecated" // *) document all the functions #include "iso15693.h" @@ -73,417 +67,1121 @@ #include "BigBuf.h" #include "crc16.h" +// Delays in SSP_CLK ticks. +// SSP_CLK runs at 13,56MHz / 32 = 423.75kHz when simulating a tag +#define DELAY_READER_TO_ARM 8 +#define DELAY_ARM_TO_READER 0 + +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when acting as reader. All values should be multiples of 16 +#define DELAY_ARM_TO_TAG 16 +#define DELAY_TAG_TO_ARM 32 + +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when sniffing. All values should be multiples of 16 +#define DELAY_TAG_TO_ARM_SNIFF 32 +#define DELAY_READER_TO_ARM_SNIFF 32 + +// times in samples @ 212kHz when acting as reader +#define ISO15693_READER_TIMEOUT 330 // 330/212kHz = 1558us +#define ISO15693_READER_TIMEOUT_WRITE 4700 // 4700/212kHz = 22ms, nominal 20ms + +// iceman: This defines below exists in the header file, just here for my easy reading +// Delays in SSP_CLK ticks. +// SSP_CLK runs at 13,56MHz / 32 = 423.75kHz when simulating a tag +//#define DELAY_ISO15693_VCD_TO_VICC_SIM 132 // 132/423.75kHz = 311.5us from end of command EOF to start of tag response + +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when acting as reader. All values should be multiples of 16 +//#define DELAY_ISO15693_VCD_TO_VICC_READER 1056 // 1056/3,39MHz = 311.5us from end of command EOF to start of tag response +//#define DELAY_ISO15693_VICC_TO_VCD_READER 1024 // 1024/3.39MHz = 302.1us between end of tag response and next reader command + + /////////////////////////////////////////////////////////////////////// // ISO 15693 Part 2 - Air Interface -// This section basicly contains transmission and receiving of bits +// This section basically contains transmission and receiving of bits /////////////////////////////////////////////////////////////////////// +// buffers +#define ISO15693_MAX_RESPONSE_LENGTH 36 // allows read single block with the maximum block size of 256bits. Read multiple blocks not supported yet +#define ISO15693_MAX_COMMAND_LENGTH 45 // allows write single block with the maximum block size of 256bits. Write multiple blocks not supported yet + // 32 + 2 crc + 1 #define ISO15_MAX_FRAME 35 #define CMD_ID_RESP 5 #define CMD_READ_RESP 13 #define CMD_INV_RESP 12 -#define FrameSOF Iso15693FrameSOF -#define Logic0 Iso15693Logic0 -#define Logic1 Iso15693Logic1 -#define FrameEOF Iso15693FrameEOF - //#define Crc(data, len) Crc(CRC_15693, (data), (len)) #define CheckCrc15(data, len) check_crc(CRC_15693, (data), (len)) #define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) -static void BuildIdentifyRequest(uint8_t *cmdout); -//static void BuildReadBlockRequest(uint8_t *cmdout, uint8_t *uid, uint8_t blockNumber ); -static void BuildInventoryResponse(uint8_t *cmdout, uint8_t *uid); +static void BuildIdentifyRequest(uint8_t *cmd); // --------------------------- + // Signal Processing // --------------------------- // prepare data using "1 out of 4" code for later transmission -// resulting data rate is 26,48 kbit/s (fc/512) +// resulting data rate is 26.48 kbit/s (fc/512) // cmd ... data // n ... length of data -static void CodeIso15693AsReader(uint8_t *cmd, int n) { - int i, j; +static uint8_t encode15_lut[] = { + 0x40, // 01000000 + 0x10, // 00010000 + 0x04, // 00000100 + 0x01 // 00000001 +}; - ToSendReset(); +void CodeIso15693AsReader(uint8_t *cmd, int n) { - // Give it a bit of slack at the beginning - for (i = 0; i < 24; i++) - ToSendStuffBit(1); + tosend_reset(); + tosend_t *ts = get_tosend(); // SOF for 1of4 - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - for (i = 0; i < n; i++) { - for (j = 0; j < 8; j += 2) { - int these = (cmd[i] >> j) & 3; - switch (these) { - case 0: - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - break; - case 1: - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - break; - case 2: - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - break; - case 3: - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - break; - } - } - } - // EOF - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); + ts->buf[++ts->max] = 0x84; //10000100 - // And slack at the end, too. - for (i = 0; i < 24; i++) - ToSendStuffBit(1); + // data + for (int i = 0; i < n; i++) { + + volatile uint8_t b = (cmd[i] >> 0) & 0x03; + ts->buf[++ts->max] = encode15_lut[b]; + + b = (cmd[i] >> 2) & 0x03; + ts->buf[++ts->max] = encode15_lut[b]; + + b = (cmd[i] >> 4) & 0x03; + ts->buf[++ts->max] = encode15_lut[b]; + + b = (cmd[i] >> 6) & 0x03; + ts->buf[++ts->max] = encode15_lut[b]; + } + + // EOF + ts->buf[++ts->max] = 0x20; //0010 + 0000 padding + ts->max++; } -// encode data using "1 out of 256" sheme +// Encode EOF only +static void CodeIso15693AsReaderEOF(void) { + tosend_reset(); + tosend_t *ts = get_tosend(); + ts->buf[++ts->max] = 0x20; + ts->max++; +} + + +// encode data using "1 out of 256" scheme // data rate is 1,66 kbit/s (fc/8192) // is designed for more robust communication over longer distances static void CodeIso15693AsReader256(uint8_t *cmd, int n) { - int i, j; - ToSendReset(); - - // Give it a bit of slack at the beginning - for (i = 0; i < 24; i++) - ToSendStuffBit(1); + tosend_reset(); + tosend_t *ts = get_tosend(); // SOF for 1of256 - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); + ts->buf[++ts->max] = 0x81; //10000001 - for (i = 0; i < n; i++) { - for (j = 0; j <= 255; j++) { + // data + for (int i = 0; i < n; i++) { + for (int j = 0; j <= 255; j++) { if (cmd[i] == j) { - ToSendStuffBit(1); - ToSendStuffBit(0); + tosend_stuffbit(0); + tosend_stuffbit(1); } else { - ToSendStuffBit(1); - ToSendStuffBit(1); + tosend_stuffbit(0); + tosend_stuffbit(0); } } } - // EOF - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); - // And slack at the end, too. - for (i = 0; i < 24; i++) - ToSendStuffBit(1); + // EOF + ts->buf[++ts->max] = 0x20; //0010 + 0000 padding + ts->max++; } -// Transmit the command (to the tag) that was placed in ToSend[]. -static void TransmitTo15693Tag(const uint8_t *cmd, int len, int *samples, int *wait) { +static const uint8_t encode_4bits[16] = { +// 0 1 2 3 + 0xaa, 0x6a, 0x9a, 0x5a, +// 4 5 6 7 + 0xa6, 0x66, 0x96, 0x56, +// 8 9 A B + 0xa9, 0x69, 0x99, 0x59, +// C D E F + 0xa5, 0x65, 0x95, 0x55 +}; - int c; - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); +void CodeIso15693AsTag(uint8_t *cmd, size_t len) { + /* + * SOF comprises 3 parts; + * * An unmodulated time of 56.64 us + * * 24 pulses of 423.75 kHz (fc/32) + * * A logic 1, which starts with an unmodulated time of 18.88us + * followed by 8 pulses of 423.75kHz (fc/32) + * + * EOF comprises 3 parts: + * - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated + * time of 18.88us. + * - 24 pulses of fc/32 + * - An unmodulated time of 56.64 us + * + * A logic 0 starts with 8 pulses of fc/32 + * followed by an unmodulated time of 256/fc (~18,88us). + * + * A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by + * 8 pulses of fc/32 (also 18.88us) + * + * A bit here becomes 8 pulses of fc/32. Therefore: + * The SOF can be written as 00011101 = 0x1D + * The EOF can be written as 10111000 = 0xb8 + * A logic 1 is 01 + * A logic 0 is 10 + * + * */ + tosend_reset(); + tosend_t *ts = get_tosend(); - if (wait) { - for (c = 0; c < *wait;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! - ++c; - } - WDT_HIT(); - } + // SOF + ts->buf[++ts->max] = 0x1D; // 00011101 + + // data + for (int i = 0; i < len; i += 2) { + ts->buf[++ts->max] = encode_4bits[cmd[i] & 0xF]; + ts->buf[++ts->max] = encode_4bits[cmd[i] >> 4]; + ts->buf[++ts->max] = encode_4bits[cmd[i + 1] & 0xF]; + ts->buf[++ts->max] = encode_4bits[cmd[i + 1] >> 4]; } - c = 0; - for (;;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = cmd[c]; - if (++c >= len) break; + // EOF + ts->buf[++ts->max] = 0xB8; // 10111000 + ts->max++; +} + +// Transmit the command (to the tag) that was placed in cmd[]. +void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time) { + + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_FULL_MOD); + + if (*start_time < DELAY_ARM_TO_TAG) { + *start_time = DELAY_ARM_TO_TAG; + } + + *start_time = (*start_time - DELAY_ARM_TO_TAG) & 0xfffffff0; + + if (GetCountSspClk() > *start_time) { // we may miss the intended time + *start_time = (GetCountSspClk() + 16) & 0xfffffff0; // next possible time + } + + // wait + while (GetCountSspClk() < *start_time) ; + + LED_B_ON(); + for (int c = 0; c < len; c++) { + volatile uint8_t data = cmd[c]; + + for (uint8_t i = 0; i < 8; i++) { + uint16_t send_word = (data & 0x80) ? 0xffff : 0x0000; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) ; + AT91C_BASE_SSC->SSC_THR = send_word; + data <<= 1; } WDT_HIT(); } + LED_B_OFF(); - if (samples) { - if (wait) - *samples = (c + *wait) << 3; - else - *samples = c << 3; - } + *start_time = *start_time + DELAY_ARM_TO_TAG; } //----------------------------------------------------------------------------- -// Transmit the command (to the reader) that was placed in ToSend[]. +// Transmit the tag response (to the reader) that was placed in cmd[]. //----------------------------------------------------------------------------- -static void TransmitTo15693Reader(const uint8_t *cmd, int len, int *samples, int *wait) { - int c = 0; +void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow) { + + // don't use the FPGA_HF_SIMULATOR_MODULATE_424K_8BIT minor mode. It would spoil GetCountSspClk() FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K); - if (wait) { - for (c = 0; c < *wait;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! - ++c; - } - WDT_HIT(); + uint32_t modulation_start_time = *start_time - DELAY_ARM_TO_READER + 3 * 8; // no need to transfer the unmodulated start of SOF + + while (GetCountSspClk() > (modulation_start_time & 0xfffffff8) + 3) { // we will miss the intended time + if (slot_time) { + modulation_start_time += slot_time; // use next available slot + } else { + modulation_start_time = (modulation_start_time & 0xfffffff8) + 8; // next possible time } } - c = 0; - for (;;) { - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = cmd[c]; - if (++c >= len) break; + // wait + while (GetCountSspClk() < (modulation_start_time & 0xfffffff8)) ; + + uint8_t shift_delay = modulation_start_time & 0x00000007; + + *start_time = modulation_start_time + DELAY_ARM_TO_READER - 3 * 8; + + LED_C_ON(); + uint8_t bits_to_shift = 0x00; + uint8_t bits_to_send = 0x00; + + for (size_t c = 0; c < len; c++) { + for (int i = (c == 0 ? 4 : 7); i >= 0; i--) { + + uint8_t cmd_bits = ((cmd[c] >> i) & 0x01) ? 0xff : 0x00; + + for (int j = 0; j < (slow ? 4 : 1);) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + bits_to_send = bits_to_shift << (8 - shift_delay) | cmd_bits >> shift_delay; + AT91C_BASE_SSC->SSC_THR = bits_to_send; + bits_to_shift = cmd_bits; + j++; + } + } } WDT_HIT(); } - if (samples) { - if (wait) - *samples = (c + *wait) << 3; - else - *samples = c << 3; + + // send the remaining bits, padded with 0: + bits_to_send = bits_to_shift << (8 - shift_delay); + if (bits_to_send) { + for (; ;) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + AT91C_BASE_SSC->SSC_THR = bits_to_send; + break; + } + } } + LED_C_OFF(); } +//============================================================================= +// An ISO 15693 decoder for tag responses (one subcarrier only). +// Uses cross correlation to identify each bit and EOF. +// This function is called 8 times per bit (every 2 subcarrier cycles). +// Subcarrier frequency fs is 424kHz, 1/fs = 2,36us, +// i.e. function is called every 4,72us +// LED handling: +// LED C -> ON once we have received the SOF and are expecting the rest. +// LED C -> OFF once we have received EOF or are unsynced +// +// Returns: true if we received a EOF +// false if we are still waiting for some more +//============================================================================= + +#define NOISE_THRESHOLD 80 // don't try to correlate noise +#define MAX_PREVIOUS_AMPLITUDE (-1 - NOISE_THRESHOLD) + +typedef struct { + enum { + STATE_TAG_SOF_LOW, + STATE_TAG_SOF_RISING_EDGE, + STATE_TAG_SOF_HIGH, + STATE_TAG_SOF_HIGH_END, + STATE_TAG_RECEIVING_DATA, + STATE_TAG_EOF, + STATE_TAG_EOF_TAIL + } state; + int bitCount; + int posCount; + enum { + LOGIC0, + LOGIC1, + SOF_PART1, + SOF_PART2 + } lastBit; + uint16_t shiftReg; + uint16_t max_len; + uint8_t *output; + int len; + int sum1; + int sum2; + int threshold_sof; + int threshold_half; + uint16_t previous_amplitude; +} DecodeTag_t; + //----------------------------------------------------------------------------- // DEMODULATE tag answer //----------------------------------------------------------------------------- -static int DemodAnswer(uint8_t *received, uint8_t *dest, uint16_t samplecount) { +static RAMFUNC int Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *tag) { - int i, j; - int max = 0, maxPos = 0, skip = 4; - int k = 0; // this will be our return value + switch (tag->state) { - // First, correlate for SOF - for (i = 0; i < samplecount; i++) { - int corr = 0; - for (j = 0; j < ARRAYLEN(FrameSOF); j += skip) { - corr += FrameSOF[j] * dest[i + (j / skip)]; - } - if (corr > max) { - max = corr; - maxPos = i; - } - } - // DbpString("SOF at %d, correlation %d", maxPos,max/(ARRAYLEN(FrameSOF)/skip)); - - // greg - If correlation is less than 1 then there's little point in continuing - if ((max / (ARRAYLEN(FrameSOF) / skip)) < 1) - return k; - - i = maxPos + ARRAYLEN(FrameSOF) / skip; - - uint8_t outBuf[ISO15_MAX_FRAME]; - memset(outBuf, 0, sizeof(outBuf)); - uint8_t mask = 0x01; - for (;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; - for (j = 0; j < ARRAYLEN(Logic0); j += skip) { - corr0 += Logic0[j] * dest[i + (j / skip)]; - } - for (j = 0; j < ARRAYLEN(Logic1); j += skip) { - corr1 += Logic1[j] * dest[i + (j / skip)]; - } - for (j = 0; j < ARRAYLEN(FrameEOF); j += skip) { - corrEOF += FrameEOF[j] * dest[i + (j / skip)]; - } - // Even things out by the length of the target waveform. - corr0 *= 4; - corr1 *= 4; - // if (DBGLEVEL >= DBG_EXTENDED) - // Dbprintf("Corr1 %d, Corr0 %d, CorrEOF %d", corr1, corr0, corrEOF); - - if (corrEOF > corr1 && corrEOF > corr0) + case STATE_TAG_SOF_LOW: { + // waiting for a rising edge + if (amplitude > NOISE_THRESHOLD + tag->previous_amplitude) { + if (tag->posCount > 10) { + tag->threshold_sof = amplitude - tag->previous_amplitude; // to be divided by 2 + tag->threshold_half = 0; + tag->state = STATE_TAG_SOF_RISING_EDGE; + } else { + tag->posCount = 0; + } + } else { + tag->posCount++; + tag->previous_amplitude = amplitude; + } break; - - if (corr1 > corr0) { - i += ARRAYLEN(Logic1) / skip; - outBuf[k] |= mask; - } else { - i += ARRAYLEN(Logic0) / skip; } - mask <<= 1; - - if (mask == 0) { - k++; - mask = 0x01; + case STATE_TAG_SOF_RISING_EDGE: { + if (amplitude > tag->threshold_sof + tag->previous_amplitude) { // edge still rising + if (amplitude > tag->threshold_sof + tag->threshold_sof) { // steeper edge, take this as time reference + tag->posCount = 1; + } else { + tag->posCount = 2; + } + tag->threshold_sof = (amplitude - tag->previous_amplitude) / 2; + } else { + tag->posCount = 2; + tag->threshold_sof = tag->threshold_sof / 2; + } + tag->state = STATE_TAG_SOF_HIGH; + break; } - if ((i + (int)ARRAYLEN(FrameEOF)) >= samplecount - 1) { - //Dbprintf("[!] ran off end! %d | %d",( i + (int)ARRAYLEN(FrameEOF)), samplecount-1); + case STATE_TAG_SOF_HIGH: { + // waiting for 10 times high. Take average over the last 8 + if (amplitude > tag->threshold_sof) { + tag->posCount++; + if (tag->posCount > 2) { + tag->threshold_half += amplitude; // keep track of average high value + } + if (tag->posCount == 10) { + tag->threshold_half >>= 2; // (4 times 1/2 average) + tag->state = STATE_TAG_SOF_HIGH_END; + } + } else { // high phase was too short + tag->posCount = 1; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; + } + break; + } + + case STATE_TAG_SOF_HIGH_END: { + // check for falling edge + if (tag->posCount == 13 && amplitude < tag->threshold_sof) { + tag->lastBit = SOF_PART1; // detected 1st part of SOF (12 samples low and 12 samples high) + tag->shiftReg = 0; + tag->bitCount = 0; + tag->len = 0; + tag->sum1 = amplitude; + tag->sum2 = 0; + tag->posCount = 2; + tag->state = STATE_TAG_RECEIVING_DATA; + LED_C_ON(); + } else { + tag->posCount++; + if (tag->posCount > 13) { // high phase too long + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + break; + } + + case STATE_TAG_RECEIVING_DATA: { + if (tag->posCount == 1) { + tag->sum1 = 0; + tag->sum2 = 0; + } + + if (tag->posCount <= 4) { + tag->sum1 += amplitude; + } else { + tag->sum2 += amplitude; + } + + if (tag->posCount == 8) { + if (tag->sum1 > tag->threshold_half && tag->sum2 > tag->threshold_half) { // modulation in both halves + if (tag->lastBit == LOGIC0) { // this was already part of EOF + tag->state = STATE_TAG_EOF; + } else { + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } else if (tag->sum1 < tag->threshold_half && tag->sum2 > tag->threshold_half) { // modulation in second half + // logic 1 + if (tag->lastBit == SOF_PART1) { // still part of SOF + tag->lastBit = SOF_PART2; // SOF completed + } else { + tag->lastBit = LOGIC1; + tag->shiftReg >>= 1; + tag->shiftReg |= 0x80; + tag->bitCount++; + if (tag->bitCount == 8) { + tag->output[tag->len] = tag->shiftReg; + tag->len++; + + if (tag->len > tag->max_len) { + // buffer overflow, give up + LED_C_OFF(); + return true; + } + tag->bitCount = 0; + tag->shiftReg = 0; + } + } + } else if (tag->sum1 > tag->threshold_half && tag->sum2 < tag->threshold_half) { // modulation in first half + // logic 0 + if (tag->lastBit == SOF_PART1) { // incomplete SOF + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } else { + tag->lastBit = LOGIC0; + tag->shiftReg >>= 1; + tag->bitCount++; + + if (tag->bitCount == 8) { + tag->output[tag->len] = tag->shiftReg; + tag->len++; + + if (tag->len > tag->max_len) { + // buffer overflow, give up + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + tag->bitCount = 0; + tag->shiftReg = 0; + } + } + } else { // no modulation + if (tag->lastBit == SOF_PART2) { // only SOF (this is OK for iClass) + LED_C_OFF(); + return true; + } else { + tag->posCount = 0; + tag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + tag->posCount = 0; + } + tag->posCount++; + break; + } + + case STATE_TAG_EOF: { + if (tag->posCount == 1) { + tag->sum1 = 0; + tag->sum2 = 0; + } + + if (tag->posCount <= 4) { + tag->sum1 += amplitude; + } else { + tag->sum2 += amplitude; + } + + if (tag->posCount == 8) { + if (tag->sum1 > tag->threshold_half && tag->sum2 < tag->threshold_half) { // modulation in first half + tag->posCount = 0; + tag->state = STATE_TAG_EOF_TAIL; + } else { + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + tag->posCount++; + break; + } + + case STATE_TAG_EOF_TAIL: { + if (tag->posCount == 1) { + tag->sum1 = 0; + tag->sum2 = 0; + } + + if (tag->posCount <= 4) { + tag->sum1 += amplitude; + } else { + tag->sum2 += amplitude; + } + + if (tag->posCount == 8) { + if (tag->sum1 < tag->threshold_half && tag->sum2 < tag->threshold_half) { // no modulation in both halves + LED_C_OFF(); + return true; + } else { + tag->posCount = 0; + tag->previous_amplitude = amplitude; + tag->state = STATE_TAG_SOF_LOW; + LED_C_OFF(); + } + } + tag->posCount++; break; } } - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("ice: demod bytes %u", k); - - if (mask != 0x01) { // this happens, when we miss the EOF - - // TODO: for some reason this happens quite often - if (DBGLEVEL >= DBG_ERROR && k != 0) Dbprintf("[!] error, uneven octet! (extra bits!) mask %02x", mask); - //if (mask < 0x08) k--; // discard the last uneven octet; - // 0x08 is an assumption - but works quite often - } - - for (i = 0; i < k; i++) - received[i] = outBuf[i]; - - // return the number of bytes demodulated - return k; + return false; } -// Read from Tag -// Parameters: -// received -// samples -// elapsed -// returns: -// number of decoded bytes -// logging enabled -#define SIGNAL_BUFF_SIZE 20000 +static void DecodeTagReset(DecodeTag_t *tag) { + tag->posCount = 0; + tag->state = STATE_TAG_SOF_LOW; + tag->previous_amplitude = MAX_PREVIOUS_AMPLITUDE; +} -static int GetIso15693AnswerFromTag(uint8_t *received, int *elapsed) { +static void DecodeTagInit(DecodeTag_t *tag, uint8_t *data, uint16_t max_len) { + tag->output = data; + tag->max_len = max_len; + DecodeTagReset(tag); +} - // get current clock - uint32_t time_0 = GetCountSspClk(); - uint32_t time_stop = 0; - bool getNext = false; - int counter = 0, ci, cq = 0; - uint8_t *buf = BigBuf_malloc(SIGNAL_BUFF_SIZE); +/* + * Receive and decode the tag response, also log to tracebuffer + */ +int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time) { - if (elapsed) *elapsed = 0; + int samples = 0, ret = 0; - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + // the Decoder data structure + DecodeTag_t dtm = { 0 }; + DecodeTag_t *dt = &dtm; + DecodeTagInit(dt, response, max_len); + + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + + // And put the FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); + + // Setup and start DMA. + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + + // The DMA buffer, used to stream samples from the FPGA + dmabuf16_t *dma = get_dma16(); + + // Setup and start DMA. + if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { + if (DBGLEVEL > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + return -4; + } + + uint32_t dma_start_time = 0; + uint16_t *upTo = dma->buf; for (;;) { - WDT_HIT(); - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; //0x43; - // To make use of exact timing of next command from reader!! - if (elapsed)(*elapsed)++; + volatile uint16_t behindBy = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + if (behindBy == 0) + continue; + + samples++; + if (samples == 1) { + // DMA has transferred the very first data + dma_start_time = GetCountSspClk() & 0xfffffff0; } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; - ci = ABS(ci); + volatile uint16_t tagdata = *upTo++; - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - // iceman 2016, amplitude sqrt(abs(i) + abs(q)) - if (getNext) { + if (upTo >= dma->buf + DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dma->buf; // start reading the circular buffer from the beginning - buf[counter++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1)); + // DMA Counter Register had reached 0, already rotated. + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { - if (counter >= SIGNAL_BUFF_SIZE) + // primary buffer was stopped + if (AT91C_BASE_PDC_SSC->PDC_RCR == false) { + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; + AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; + } + // secondary buffer sets as primary, secondary buffer was stopped + if (AT91C_BASE_PDC_SSC->PDC_RNCR == false) { + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + } + + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString("stopped"); break; - } else { - cq = ci; + } } - getNext = !getNext; } + + if (Handle15693SamplesFromTag(tagdata, dt)) { + + *eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM; // end of EOF + + if (dt->lastBit == SOF_PART2) { + *eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) + } + if (dt->len > dt->max_len) { + ret = -2; // buffer overflow + Dbprintf("overflow (%d > %d", dt->len, dt->max_len); + } + break; + } + + // timeout + if (samples > timeout && dt->state < STATE_TAG_RECEIVING_DATA) { + ret = -3; + break; + } + } - time_stop = GetCountSspClk(); - int len = DemodAnswer(received, buf, counter); - LogTrace(received, len, time_0 << 4, time_stop << 4, NULL, false); - BigBuf_free(); - return len; + + FpgaDisableSscDma(); + + uint32_t sof_time = *eof_time + - (dt->len * 8 * 8 * 16) // time for byte transfers + - (32 * 16) // time for SOF transfer + - (dt->lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer + + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("samples = %d, ret = %d, Decoder: state = %d, lastBit = %d, len = %d, bitCount = %d, posCount = %d, maxlen = %u", + samples, + ret, + dt->state, + dt->lastBit, + dt->len, + dt->bitCount, + dt->posCount, + dt->max_len + ); + Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); + } + + if (ret < 0) { + return ret; + } + + LogTrace_ISO15693(dt->output, dt->len, (sof_time * 4), (*eof_time * 4), NULL, false); + return dt->len; } -// Now the GetISO15693 message from sniffing command -// logging enable, -static int GetIso15693AnswerFromSniff(uint8_t *received, int *samples, int *elapsed) { +//============================================================================= +// An ISO15693 decoder for reader commands. +// +// This function is called 4 times per bit (every 2 subcarrier cycles). +// Subcarrier frequency fs is 848kHz, 1/fs = 1,18us, i.e. function is called every 2,36us +// LED handling: +// LED B -> ON once we have received the SOF and are expecting the rest. +// LED B -> OFF once we have received EOF or are in error state or unsynced +// +// Returns: true if we received a EOF +// false if we are still waiting for some more +//============================================================================= - bool getNext = false; - int counter = 0, ci, cq = 0; - uint32_t time_0 = 0, time_stop = 0; - uint8_t *buf = BigBuf_malloc(SIGNAL_BUFF_SIZE); +typedef struct { + enum { + STATE_READER_UNSYNCD, + STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF, + STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF, + STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF, + STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF, + STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4, + STATE_READER_RECEIVE_DATA_1_OUT_OF_4, + STATE_READER_RECEIVE_DATA_1_OUT_OF_256, + STATE_READER_RECEIVE_JAMMING + } state; + enum { + CODING_1_OUT_OF_4, + CODING_1_OUT_OF_256 + } Coding; + uint8_t shiftReg; + uint8_t bitCount; + int byteCount; + int byteCountMax; + int posCount; + int sum1, sum2; + uint8_t *output; + uint8_t jam_search_len; + uint8_t *jam_search_string; +} DecodeReader_t; - // get current clock - time_0 = GetCountSspClk(); +static void DecodeReaderInit(DecodeReader_t *reader, uint8_t *data, uint16_t max_len, uint8_t jam_search_len, uint8_t *jam_search_string) { + reader->output = data; + reader->byteCountMax = max_len; + reader->state = STATE_READER_UNSYNCD; + reader->byteCount = 0; + reader->bitCount = 0; + reader->posCount = 1; + reader->shiftReg = 0; + reader->jam_search_len = jam_search_len; + reader->jam_search_string = jam_search_string; +} - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); +static void DecodeReaderReset(DecodeReader_t *reader) { + reader->state = STATE_READER_UNSYNCD; +} - for (;;) { - WDT_HIT(); - - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - - ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; - ci = ABS(ci); - - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if (getNext) { - - buf[counter++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1)); - - if (counter >= 20000) - break; - } else { - cq = ci; +//static inline __attribute__((always_inline)) +static int RAMFUNC Handle15693SampleFromReader(bool bit, DecodeReader_t *reader) { + switch (reader->state) { + case STATE_READER_UNSYNCD: + // wait for unmodulated carrier + if (bit) { + reader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; } - getNext = !getNext; - } + break; + + case STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF: + if (!bit) { + // we went low, so this could be the beginning of a SOF + reader->posCount = 1; + reader->state = STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF; + } + break; + + case STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF: + reader->posCount++; + if (bit) { // detected rising edge + if (reader->posCount < 4) { // rising edge too early (nominally expected at 5) + reader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { // SOF + reader->state = STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF; + } + } else { + if (reader->posCount > 5) { // stayed low for too long + DecodeReaderReset(reader); + } else { + // do nothing, keep waiting + } + } + break; + + case STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF: + + reader->posCount++; + + if (bit == false) { // detected a falling edge + + if (reader->posCount < 20) { // falling edge too early (nominally expected at 21 earliest) + DecodeReaderReset(reader); + } else if (reader->posCount < 23) { // SOF for 1 out of 4 coding + reader->Coding = CODING_1_OUT_OF_4; + reader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + } else if (reader->posCount < 28) { // falling edge too early (nominally expected at 29 latest) + DecodeReaderReset(reader); + } else { // SOF for 1 out of 256 coding + reader->Coding = CODING_1_OUT_OF_256; + reader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; + } + + } else { + if (reader->posCount > 29) { // stayed high for too long + reader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { + // do nothing, keep waiting + } + } + break; + + case STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF: + + reader->posCount++; + + if (bit) { // detected rising edge + if (reader->Coding == CODING_1_OUT_OF_256) { + if (reader->posCount < 32) { // rising edge too early (nominally expected at 33) + reader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { + reader->posCount = 1; + reader->bitCount = 0; + reader->byteCount = 0; + reader->sum1 = 1; + reader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; + LED_B_ON(); + } + } else { // CODING_1_OUT_OF_4 + if (reader->posCount < 24) { // rising edge too early (nominally expected at 25) + reader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } else { + reader->posCount = 1; + reader->state = STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4; + } + } + } else { + if (reader->Coding == CODING_1_OUT_OF_256) { + if (reader->posCount > 34) { // signal stayed low for too long + DecodeReaderReset(reader); + } else { + // do nothing, keep waiting + } + } else { // CODING_1_OUT_OF_4 + if (reader->posCount > 26) { // signal stayed low for too long + DecodeReaderReset(reader); + } else { + // do nothing, keep waiting + } + } + } + break; + + case STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4: + + reader->posCount++; + + if (bit) { + if (reader->posCount == 9) { + reader->posCount = 1; + reader->bitCount = 0; + reader->byteCount = 0; + reader->sum1 = 1; + reader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; + LED_B_ON(); + } else { + // do nothing, keep waiting + } + } else { // unexpected falling edge + DecodeReaderReset(reader); + } + break; + + case STATE_READER_RECEIVE_DATA_1_OUT_OF_4: + + reader->posCount++; + + if (reader->posCount == 1) { + + reader->sum1 = bit ? 1 : 0; + + } else if (reader->posCount <= 4) { + + if (bit) + reader->sum1++; + + } else if (reader->posCount == 5) { + + reader->sum2 = bit ? 1 : 0; + + } else { + if (bit) + reader->sum2++; + } + + if (reader->posCount == 8) { + reader->posCount = 0; + if (reader->sum1 <= 1 && reader->sum2 >= 3) { // EOF + LED_B_OFF(); // Finished receiving + DecodeReaderReset(reader); + if (reader->byteCount != 0) { + return true; + } + + } else if (reader->sum1 >= 3 && reader->sum2 <= 1) { // detected a 2bit position + reader->shiftReg >>= 2; + reader->shiftReg |= (reader->bitCount << 6); + } + + if (reader->bitCount == 15) { // we have a full byte + + reader->output[reader->byteCount++] = reader->shiftReg; + if (reader->byteCount > reader->byteCountMax) { + // buffer overflow, give up + LED_B_OFF(); + DecodeReaderReset(reader); + } + + reader->bitCount = 0; + reader->shiftReg = 0; + if (reader->byteCount == reader->jam_search_len) { + if (!memcmp(reader->output, reader->jam_search_string, reader->jam_search_len)) { + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_JAM); + reader->state = STATE_READER_RECEIVE_JAMMING; + } + } + + } else { + reader->bitCount++; + } + } + break; + + case STATE_READER_RECEIVE_DATA_1_OUT_OF_256: + + reader->posCount++; + + if (reader->posCount == 1) { + reader->sum1 = bit ? 1 : 0; + } else if (reader->posCount <= 4) { + if (bit) reader->sum1++; + } else if (reader->posCount == 5) { + reader->sum2 = bit ? 1 : 0; + } else if (bit) { + reader->sum2++; + } + + if (reader->posCount == 8) { + reader->posCount = 0; + if (reader->sum1 <= 1 && reader->sum2 >= 3) { // EOF + LED_B_OFF(); // Finished receiving + DecodeReaderReset(reader); + if (reader->byteCount != 0) { + return true; + } + + } else if (reader->sum1 >= 3 && reader->sum2 <= 1) { // detected the bit position + reader->shiftReg = reader->bitCount; + } + + if (reader->bitCount == 255) { // we have a full byte + reader->output[reader->byteCount++] = reader->shiftReg; + if (reader->byteCount > reader->byteCountMax) { + // buffer overflow, give up + LED_B_OFF(); + DecodeReaderReset(reader); + } + + if (reader->byteCount == reader->jam_search_len) { + if (!memcmp(reader->output, reader->jam_search_string, reader->jam_search_len)) { + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_JAM); + reader->state = STATE_READER_RECEIVE_JAMMING; + } + } + } + reader->bitCount++; + } + break; + + case STATE_READER_RECEIVE_JAMMING: + + reader->posCount++; + + if (reader->Coding == CODING_1_OUT_OF_4) { + if (reader->posCount == 7 * 16) { // 7 bits jammed + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming + // FpgaDisableTracing(); + LED_D_OFF(); + } else if (reader->posCount == 8 * 16) { + reader->posCount = 0; + reader->output[reader->byteCount++] = 0x00; + reader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_4; + } + } else { + if (reader->posCount == 7 * 256) { // 7 bits jammend + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); // stop jamming + LED_D_OFF(); + } else if (reader->posCount == 8 * 256) { + reader->posCount = 0; + reader->output[reader->byteCount++] = 0x00; + reader->state = STATE_READER_RECEIVE_DATA_1_OUT_OF_256; + } + } + break; + + default: + LED_B_OFF(); + DecodeReaderReset(reader); + break; } - time_stop = GetCountSspClk(); - int k = DemodAnswer(received, buf, counter); - LogTrace(received, k, time_0 << 4, time_stop << 4, NULL, false); - BigBuf_free(); - return k; + return false; +} + +//----------------------------------------------------------------------------- +// Receive a command (from the reader to us, where we are the simulated tag), +// and store it in the given buffer, up to the given maximum length. Keeps +// spinning, waiting for a well-framed command, until either we get one +// (returns len) or someone presses the pushbutton on the board (returns -1). +// +// Assume that we're called with the SSC (to the FPGA) and ADC path set +// correctly. +//----------------------------------------------------------------------------- + +int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time) { + int samples = 0; + bool gotFrame = false; + + // the decoder data structure + DecodeReader_t *dr = (DecodeReader_t *)BigBuf_malloc(sizeof(DecodeReader_t)); + DecodeReaderInit(dr, received, max_len, 0, NULL); + + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); + + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + + // clear receive register and wait for next transfer + uint32_t temp = AT91C_BASE_SSC->SSC_RHR; + (void) temp; + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) ; + + // Setup and start DMA. + dmabuf8_t *dma = get_dma8(); + if (FpgaSetupSscDma(dma->buf, DMA_BUFFER_SIZE) == false) { + if (DBGLEVEL > DBG_ERROR) Dbprintf("FpgaSetupSscDma failed. Exiting"); + return -4; + } + uint8_t *upTo = dma->buf; + + uint32_t dma_start_time = GetCountSspClk() & 0xfffffff8; + + for (;;) { + volatile uint16_t behindBy = ((uint8_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + if (behindBy == 0) continue; + + if (samples == 0) { + // DMA has transferred the very first data + dma_start_time = GetCountSspClk() & 0xfffffff0; + } + + volatile uint8_t b = *upTo++; + if (upTo >= dma->buf + DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + upTo = dma->buf; // start reading the circular buffer from the beginning + if (behindBy > (9 * DMA_BUFFER_SIZE / 10)) { + Dbprintf("About to blow circular buffer - aborted! behindBy %d", behindBy); + break; + } + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { // DMA Counter Register had reached 0, already rotated. + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; // refresh the DMA Next Buffer and + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; // DMA Next Counter registers + } + + for (int i = 7; i >= 0; i--) { + if (Handle15693SampleFromReader((b >> i) & 0x01, dr)) { + *eof_time = dma_start_time + samples - DELAY_READER_TO_ARM; // end of EOF + gotFrame = true; + break; + } + samples++; + } + + if (gotFrame) { + break; + } + + if (BUTTON_PRESS()) { + dr->byteCount = -1; + break; + } + + WDT_HIT(); + } + + FpgaDisableSscDma(); + + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", + samples, gotFrame, dr->state, dr->byteCount, + dr->bitCount, dr->posCount); + } + + if (dr->byteCount >= 0) { + uint32_t sof_time = *eof_time + - dr->byteCount * (dr->Coding == CODING_1_OUT_OF_4 ? 128 : 2048) // time for byte transfers + - 32 // time for SOF transfer + - 16; // time for EOF transfer + LogTrace_ISO15693(dr->output, dr->byteCount, (sof_time * 32), (*eof_time * 32), NULL, true); + } + + return dr->byteCount; } //----------------------------------------------------------------------------- @@ -492,133 +1190,241 @@ static int GetIso15693AnswerFromSniff(uint8_t *received, int *samples, int *elap // so that it can be downloaded to a PC and processed there. //----------------------------------------------------------------------------- void AcquireRawAdcSamplesIso15693(void) { - int c = 0, getNext = false; - int ci, cq = 0; + LED_A_ON(); + uint8_t *dest = BigBuf_malloc(4000); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); + LED_D_ON(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + + uint8_t cmd[5]; + BuildIdentifyRequest(cmd); + CodeIso15693AsReader(cmd, sizeof(cmd)); + + // Give the tags time to energize + SpinDelay(100); // Now send the command - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - SpinDelay(200); + tosend_t *ts = get_tosend(); - uint8_t *buf = BigBuf_get_addr(); + uint32_t start_time = 0; + TransmitTo15693Tag(ts->buf, ts->max, &start_time); - uint32_t time_start = GetCountSspClk(); - uint8_t cmd[CMD_ID_RESP] = {0}; - BuildIdentifyRequest(cmd); + // wait for last transfer to complete + while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)) ; - // sending command - c = 0; - for (;;) { - WDT_HIT(); - - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c]; - c++; - if (c == ToSendMax + 3) { - break; - } - } - } - - LogTrace(cmd, CMD_ID_RESP, time_start << 4, GetCountSspClk() << 4, NULL, true); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - - c = 0; - for (;;) { - WDT_HIT(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_424_KHZ | FPGA_HF_READER_MODE_RECEIVE_AMPLITUDE); + for (int c = 0; c < 4000;) { if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - - ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; - ci = ABS(ci); - - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - // iceman 2016, amplitude sqrt(abs(i) + abs(q)) - if (getNext) { - - buf[c++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1)); - - if (c >= 7000) break; - - } else { - cq = ci; - } - getNext = !getNext; - } - } -} - -// switch_off, initreader, no logging -void RecordRawAdcSamplesIso15693(void) { - - int c = 0, getNext = false; - int ci, cq = 0; - - Iso15693InitReader(); - - uint8_t *buf = BigBuf_get_addr(); - - for (;;) { - WDT_HIT(); - - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - - ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; - ci = ABS(ci); - // The samples are correlations against I and Q versions of the - // tone that the tag AM-modulates, so every other sample is I, - // every other is Q. We just want power, so abs(I) + abs(Q) is - // close to what we want. - if (getNext) { - - buf[c++] = (uint8_t)(MAX(ci, cq) + (MIN(ci, cq) >> 1)); - - if (c >= 7000) - break; - } else { - cq = ci; - } - - getNext = !getNext; + uint16_t r = AT91C_BASE_SSC->SSC_RHR; + dest[c++] = r >> 5; } } - Dbprintf("done"); - switch_off(); -} - -// Initialize the proxmark as iso15k reader -// (this might produces glitches that confuse some tags -void Iso15693InitReader(void) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); +} + +void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { + + LEDsoff(); + LED_A_ON(); + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + DbpString("Starting to sniff. Press PM3 Button to stop."); + + BigBuf_free(); clear_trace(); set_tracing(true); + DecodeTag_t dtag = {0}; + uint8_t response[ISO15693_MAX_RESPONSE_LENGTH] = {0}; + DecodeTagInit(&dtag, response, sizeof(response)); + + DecodeReader_t dreader = {0}; + uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH] = {0}; + DecodeReaderInit(&dreader, cmd, sizeof(cmd), jam_search_len, jam_search_string); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNIFF_AMPLITUDE); + LED_D_OFF(); + + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + + StartCountSspClk(); + + // The DMA buffer, used to stream samples from the FPGA + dmabuf16_t *dma = get_dma16(); + + // Setup and start DMA. + if (FpgaSetupSscDma((uint8_t *) dma->buf, DMA_BUFFER_SIZE) == false) { + if (DBGLEVEL > DBG_ERROR) DbpString("FpgaSetupSscDma failed. Exiting"); + switch_off(); + return; + } + + bool tag_is_active = false; + bool reader_is_active = false; + bool expect_tag_answer = false; + int dma_start_time = 0; + + // Count of samples received so far, so that we can include timing + int samples = 0; + + uint16_t *upTo = dma->buf; + + for (;;) { + + volatile int behind_by = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); + if (behind_by < 1) continue; + + samples++; + if (samples == 1) { + // DMA has transferred the very first data + dma_start_time = GetCountSspClk() & 0xfffffff0; + } + + volatile uint16_t sniffdata = *upTo++; + + // we have read all of the DMA buffer content + if (upTo >= dma->buf + DMA_BUFFER_SIZE) { + + // start reading the circular buffer from the beginning + upTo = dma->buf; + + // DMA Counter Register had reached 0, already rotated. + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX)) { + + // primary buffer was stopped + if (AT91C_BASE_PDC_SSC->PDC_RCR == false) { + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) dma->buf; + AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; + } + // secondary buffer sets as primary, secondary buffer was stopped + if (AT91C_BASE_PDC_SSC->PDC_RNCR == false) { + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dma->buf; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + } + + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString("Sniff stopped"); + break; + } + } + } + + // no need to try decoding reader data if the tag is sending + if (tag_is_active == false) { + + if (Handle15693SampleFromReader((sniffdata & 0x02) >> 1, &dreader)) { + + uint32_t eof_time = dma_start_time + (samples * 16) + 8 - 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); + } + // And ready to receive another command. + DecodeReaderReset(&dreader); + DecodeTagReset(&dtag); + 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); + } + // And ready to receive another command + DecodeReaderReset(&dreader); + DecodeTagReset(&dtag); + 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 == false && expect_tag_answer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet + + if (Handle15693SamplesFromTag(sniffdata >> 2, &dtag)) { + + 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 + + LogTrace_ISO15693(dtag.output, dtag.len, (sof_time * 4), (eof_time * 4), NULL, false); + // And ready to receive another response. + DecodeTagReset(&dtag); + DecodeReaderReset(&dreader); + expect_tag_answer = false; + tag_is_active = false; + } else { + tag_is_active = (dtag.state >= STATE_TAG_RECEIVING_DATA); + } + } + + } + + FpgaDisableTracing(); + 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(" 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(""); + +} + +// Initialize Proxmark3 as ISO15693 reader +void Iso15693InitReader(void) { + + LEDsoff(); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(10); + // switch field on + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER); + LED_D_ON(); + + // initialize SSC and select proper AD input + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); + set_tracing(true); - // Give the tags time to energize - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - SpinDelay(200); + // give tags some time to energize + SpinDelay(250); - // Start the timer StartCountSspClk(); - - LED_A_ON(); } /////////////////////////////////////////////////////////////////////// @@ -626,75 +1432,19 @@ void Iso15693InitReader(void) { // This section basicly contains transmission and receiving of bits /////////////////////////////////////////////////////////////////////// -// Encode (into the ToSend buffers) an identify request, which is the first +// Encode an identify request, which is the first // thing that you must send to a tag to get a response. // It expects "cmdout" to be at least CMD_ID_RESP large -static void BuildIdentifyRequest(uint8_t *cmdout) { - uint8_t cmd[CMD_ID_RESP] = {0, ISO15_CMD_INVENTORY, 0, 0, 0}; +// When READER: +static void BuildIdentifyRequest(uint8_t *cmd) { // flags cmd[0] = ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; + // inventory command code + cmd[1] = ISO15_CMD_INVENTORY; // no mask cmd[2] = 0x00; // CRC AddCrc15(cmd, 3); - // coding as high speed (1 out of 4) - CodeIso15693AsReader(cmd, CMD_ID_RESP); - memcpy(cmdout, cmd, CMD_ID_RESP); -} - -// uid is in transmission order (which is reverse of display order) -/* -static void BuildReadBlockRequest(uint8_t **out, uint8_t *uid, uint8_t blockNumber ) { - uint8_t cmd[CMD_READ_RESP] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; - // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block - // followed by teh block data - // one sub-carrier, inventory, 1 slot, fast rate - cmd[0] = (1 << 6)| (1 << 5) | (1 << 1); // no SELECT bit, ADDR bit, OPTION bit - // READ BLOCK command code - cmd[1] = 0x20; - // UID may be optionally specified here - // 64-bit UID - cmd[2] = uid[0]; - cmd[3] = uid[1]; - cmd[4] = uid[2]; - cmd[5] = uid[3]; - cmd[6] = uid[4]; - cmd[7] = uid[5]; - cmd[8] = uid[6]; - cmd[9] = uid[7]; // 0xe0; // always e0 (not exactly unique) - // Block number to read - cmd[10] = blockNumber;//0x00; - // CRC - AddCrc15(cmd, 11); - CodeIso15693AsReader(cmd, CMD_READ_RESP); - memcpy(out, cmd, CMD_ID_RESP); -} -*/ - -// Now the VICC>VCD responses when we are simulating a tag -// It expects "out" to be at least CMD_INV_RESP large -static void BuildInventoryResponse(uint8_t *cmdout, uint8_t *uid) { - - uint8_t cmd[CMD_INV_RESP] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - - // one sub-carrier, inventory, 1 slot, fast rate - // AFI is at bit 5 (1<<4) when doing an INVENTORY - //(1 << 2) | (1 << 5) | (1 << 1); - cmd[0] = 0; // - cmd[1] = 0; // DSFID (data storage format identifier). 0x00 = not supported - // 64-bit UID - cmd[2] = uid[7]; //0x32; - cmd[3] = uid[6]; //0x4b; - cmd[4] = uid[5]; //0x03; - cmd[5] = uid[4]; //0x01; - cmd[6] = uid[3]; //0x00; - cmd[7] = uid[2]; //0x10; - cmd[8] = uid[1]; //0x05; - cmd[9] = uid[0]; //0xe0; - // CRC - AddCrc15(cmd, 10); - CodeIso15693AsReader(cmd, CMD_INV_RESP); - memcpy(cmdout, cmd, CMD_INV_RESP); } // Universal Method for sending to and recv bytes from a tag @@ -704,36 +1454,49 @@ static void BuildInventoryResponse(uint8_t *cmdout, uint8_t *uid) { // If you do not need the answer use NULL for *recv[] // return: length of received data // logging enabled -static int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t *outdata) { +int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *recv, + uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time) { - int t_samples = 0, wait = 0, elapsed = 0, answer_len = 0; - - LEDsoff(); - - if (init) Iso15693InitReader(); - - LED_A_ON(); - - if (!speed) - CodeIso15693AsReader256(send, sendlen); // low speed (1 out of 256) - else - CodeIso15693AsReader(send, sendlen); // high speed (1 out of 4) - - LED_A_INV(); - - uint32_t time_start = GetCountSspClk(); - - TransmitTo15693Tag(ToSend, ToSendMax, &t_samples, &wait); - LogTrace(send, sendlen, time_start << 4, GetCountSspClk() << 4, NULL, true); - - // Now wait for a response - if (outdata != NULL) { - LED_B_INV(); - answer_len = GetIso15693AnswerFromTag(outdata, &elapsed); + if (init) { + Iso15693InitReader(); + start_time = GetCountSspClk(); } - LEDsoff(); - return answer_len; + if (speed_fast) { + // high speed (1 out of 4) + CodeIso15693AsReader(send, sendlen); + } else { + // low speed (1 out of 256) + CodeIso15693AsReader256(send, sendlen); + } + + tosend_t *ts = get_tosend(); + TransmitTo15693Tag(ts->buf, ts->max, &start_time); + *eof_time = start_time + 32 * ((8 * ts->max) - 4); // substract the 4 padding bits after EOF + LogTrace_ISO15693(send, sendlen, (start_time * 4), (*eof_time * 4), NULL, true); + + int res = 0; + if (recv != NULL) { + res = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); + } + FpgaDisableTracing(); + return res; +} + +int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time) { + + CodeIso15693AsReaderEOF(); + tosend_t *ts = get_tosend(); + TransmitTo15693Tag(ts->buf, ts->max, &start_time); + uint32_t end_time = start_time + 32 * (8 * ts->max - 4); // substract the 4 padding bits after EOF + LogTrace_ISO15693(NULL, 0, (start_time * 4), (end_time * 4), NULL, true); + + int res = 0; + if (recv != NULL) { + res = GetIso15693AnswerFromTag(recv, max_recv_len, timeout, eof_time); + } + FpgaDisableTracing(); + return res; } // -------------------------------------------------------------------- @@ -745,10 +1508,13 @@ static int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t static void DbdecodeIso15693Answer(int len, uint8_t *d) { if (len > 3) { + char status[DBD15STATLEN + 1] = {0}; - if (d[0] & (1 << 3)) + + if (d[0] & ISO15_RES_EXT) strncat(status, "ProtExt ", DBD15STATLEN - strlen(status)); - if (d[0] & 1) { + + if (d[0] & ISO15_RES_ERROR) { // error strncat(status, "Error ", DBD15STATLEN - strlen(status)); switch (d[1]) { @@ -788,9 +1554,9 @@ static void DbdecodeIso15693Answer(int len, uint8_t *d) { } if (CheckCrc15(d, len)) - strncat(status, "[+] crc OK", DBD15STATLEN - strlen(status)); + strncat(status, "[+] crc (" _GREEN_("OK") ")", DBD15STATLEN - strlen(status)); else - strncat(status, "[!] crc fail", DBD15STATLEN - strlen(status)); + strncat(status, "[!] crc (" _RED_("fail") ")", DBD15STATLEN - strlen(status)); if (DBGLEVEL >= DBG_ERROR) Dbprintf("%s", status); } @@ -807,41 +1573,35 @@ static void DbdecodeIso15693Answer(int len, uint8_t *d) { // ok // parameter is unused !?! void ReaderIso15693(uint32_t parameter) { - int answerLen1 = 0; - int tsamples = 0, wait = 0, elapsed = 0; - // set up device/fpga - Iso15693InitReader(); - uint8_t *answer1 = BigBuf_malloc(50); - uint8_t *answer2 = BigBuf_malloc(50); + LED_A_ON(); + set_tracing(true); - // Blank arrays - memset(answer1, 0x00, 50); - memset(answer2, 0x00, 50); + uint8_t *answer = BigBuf_malloc(ISO15693_MAX_RESPONSE_LENGTH); + memset(answer, 0x00, ISO15693_MAX_RESPONSE_LENGTH); - // Now send the IDENTIFY command // FIRST WE RUN AN INVENTORY TO GET THE TAG UID // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME - uint32_t time_start = GetCountSspClk(); - uint8_t cmd[CMD_ID_RESP] = {0}; - BuildIdentifyRequest(cmd); - TransmitTo15693Tag(ToSend, ToSendMax, &tsamples, &wait); - LogTrace(cmd, CMD_ID_RESP, time_start << 4, GetCountSspClk() << 4, NULL, true); - // Now wait for a response - answerLen1 = GetIso15693AnswerFromTag(answer1, &elapsed) ; + // Send the IDENTIFY command + uint8_t cmd[5] = {0}; + BuildIdentifyRequest(cmd); + uint32_t start_time = 0; + uint32_t eof_time; + int answerLen = SendDataTag(cmd, sizeof(cmd), true, true, answer, ISO15693_MAX_RESPONSE_LENGTH, start_time, ISO15693_READER_TIMEOUT, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; // we should do a better check than this - if (answerLen1 >= 12) { + if (answerLen >= 12) { uint8_t uid[8]; - uid[0] = answer1[9]; // always E0 - uid[1] = answer1[8]; // IC Manufacturer code - uid[2] = answer1[7]; - uid[3] = answer1[6]; - uid[4] = answer1[5]; - uid[5] = answer1[4]; - uid[6] = answer1[3]; - uid[7] = answer1[2]; + uid[0] = answer[9]; // always E0 + uid[1] = answer[8]; // IC Manufacturer code + uid[2] = answer[7]; + uid[3] = answer[6]; + uid[4] = answer[5]; + uid[5] = answer[4]; + uid[6] = answer[3]; + uid[7] = answer[2]; if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf("[+] UID = %02X%02X%02X%02X%02X%02X%02X%02X", @@ -854,82 +1614,139 @@ void ReaderIso15693(uint32_t parameter) { // arg1 = len of response (12 bytes) // arg2 = rtf // asbytes = uid. - reply_old(CMD_ACK, 1, sizeof(uid), 0, uid, sizeof(uid)); + reply_mix(CMD_ACK, 1, sizeof(uid), 0, uid, sizeof(uid)); } if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("[+] %d octets read from IDENTIFY request:", answerLen1); - DbdecodeIso15693Answer(answerLen1, answer1); - Dbhexdump(answerLen1, answer1, true); + Dbprintf("[+] %d octets read from IDENTIFY request:", answerLen); + DbdecodeIso15693Answer(answerLen, answer); + Dbhexdump(answerLen, answer, true); } switch_off(); + BigBuf_free(); +} + +// When SIM: initialize the Proxmark3 as ISO15693 tag +void Iso15693InitTag(void) { + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // Start from off (no field generated) + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + SpinDelay(10); + + // switch simulation FPGA + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + + // initialize SSC and select proper AD input + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + clear_trace(); + set_tracing(true); + + StartCountSspClk(); } // Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands // all demodulation performed in arm rather than host. - greg -void SimTagIso15693(uint32_t parameter, uint8_t *uid) { +void SimTagIso15693(uint8_t *uid) { - LEDsoff(); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - // Start from off (no field generated) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); + // free eventually allocated BigBuf memory + BigBuf_free_keep_EM(); + + Iso15693InitTag(); LED_A_ON(); - uint32_t time_start; - int samples = 0, tsamples = 0; - int wait = 0, elapsed = 0; - Dbprintf("ISO-15963 Simulating uid: %02X%02X%02X%02X%02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7]); - uint8_t buf[ISO15_MAX_FRAME]; - memset(buf, 0x00, sizeof(buf)); - LED_C_ON(); - // Build a suitable reponse to the reader INVENTORY cocmmand - // not so obsvious, but in the call to BuildInventoryResponse, the command is copied to the global ToSend buffer used below. - uint8_t cmd[CMD_INV_RESP] = {0}; - BuildInventoryResponse(cmd, uid); + // Build INVENTORY command + uint8_t resp_inv[CMD_INV_RESP] = {0}; - while (!BUTTON_PRESS() && !data_available()) { + resp_inv[0] = 0; // No error, no protocol format extension + resp_inv[1] = 0; // DSFID (data storage format identifier). 0x00 = not supported + + // 64-bit UID + resp_inv[2] = uid[7]; + resp_inv[3] = uid[6]; + resp_inv[4] = uid[5]; + resp_inv[5] = uid[4]; + resp_inv[6] = uid[3]; + resp_inv[7] = uid[2]; + resp_inv[8] = uid[1]; + resp_inv[9] = uid[0]; + + // CRC + AddCrc15(resp_inv, 10); + CodeIso15693AsTag(resp_inv, CMD_INV_RESP); + + tosend_t *ts = get_tosend(); + + enum { NO_FIELD, IDLE, ACTIVATED, SELECTED, HALTED } chip_state = NO_FIELD; + + bool button_pressed = false; + int vHf = 0; // in mV + + bool exit_loop = false; + while (exit_loop == false) { WDT_HIT(); - // Listen to reader - int ans = GetIso15693AnswerFromSniff(buf, &samples, &elapsed) ; + // find reader field + if (chip_state == NO_FIELD) { - // we should do a better check than this - if (ans >= 1) { - - time_start = GetCountSspClk(); - TransmitTo15693Reader(ToSend, ToSendMax, &tsamples, &wait); - LogTrace(cmd, CMD_INV_RESP, time_start << 4, GetCountSspClk() << 4, NULL, true); - - if (DBGLEVEL >= DBG_EXTENDED) { - Dbprintf("[+] %d octets read from reader command: %x %x %x %x %x %x %x %x", ans, - buf[0], buf[1], buf[2], buf[3], - buf[4], buf[5], buf[6], buf[7] - ); +#if defined RDV4 + vHf = (MAX_ADC_HF_VOLTAGE_RDV40 * SumAdc(ADC_CHAN_HF_RDV40, 32)) >> 15; +#else + vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; +#endif + if (vHf > MF_MINFIELDV) { + chip_state = IDLE; + LED_A_ON(); + } else { + continue; } } + + // Listen to reader + uint8_t cmd[ISO15693_MAX_COMMAND_LENGTH]; + uint32_t reader_eof_time = 0; + int cmd_len = GetIso15693CommandFromReader(cmd, sizeof(cmd), &reader_eof_time); + if (cmd_len < 0) { + button_pressed = true; + exit_loop = true; + break; + } + + // TODO: check more flags + if ((cmd_len >= 5) && (cmd[0] & ISO15_REQ_INVENTORY) && (cmd[1] == ISO15_CMD_INVENTORY)) { + bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); + uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; + TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow); + LogTrace_ISO15693(resp_inv, CMD_INV_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); + + chip_state = SELECTED; + } } + switch_off(); + + if (button_pressed) + DbpString("button pressed"); + + reply_ng(CMD_HF_ISO15693_SIMULATE, PM3_SUCCESS, NULL, 0); } // Since there is no standardized way of reading the AFI out of a tag, we will brute force it // (some manufactures offer a way to read the AFI, though) void BruteforceIso15693Afi(uint32_t speed) { - uint8_t data[7] = {0, 0, 0, 0, 0, 0, 0}; - uint8_t buf[ISO15_MAX_FRAME]; - memset(buf, 0x00, sizeof(buf)); - int datalen = 0, recvlen = 0; - bool aborted = false; - + uint8_t data[7] = {0}; + uint8_t recv[ISO15693_MAX_RESPONSE_LENGTH]; Iso15693InitReader(); // first without AFI @@ -939,31 +1756,40 @@ void BruteforceIso15693Afi(uint32_t speed) { data[1] = ISO15_CMD_INVENTORY; data[2] = 0; // AFI AddCrc15(data, 3); - datalen = 5; - recvlen = SendDataTag(data, datalen, false, speed, buf); + + int datalen = 5; + uint32_t eof_time = 0; + uint32_t start_time = GetCountSspClk(); + int recvlen = SendDataTag(data, datalen, true, speed, recv, sizeof(recv), 0, ISO15693_READER_TIMEOUT, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; WDT_HIT(); if (recvlen >= 12) { - Dbprintf("NoAFI UID = %s", iso15693_sprintUID(NULL, buf + 2)); + Dbprintf("NoAFI UID = %s", iso15693_sprintUID(NULL, recv + 2)); } // now with AFI data[0] |= ISO15_REQINV_AFI; - //data[1] = ISO15_CMD_INVENTORY; data[2] = 0; // AFI data[3] = 0; // mask length // 4 + 2crc datalen = 6; + bool aborted = false; for (uint16_t i = 0; i < 256; i++) { + data[2] = i & 0xFF; AddCrc15(data, 4); - recvlen = SendDataTag(data, datalen, false, speed, buf); + + recvlen = SendDataTag(data, datalen, false, speed, recv, sizeof(recv), start_time, ISO15693_READER_TIMEOUT, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + WDT_HIT(); + if (recvlen >= 12) { - Dbprintf("AFI = %i UID = %s", i, iso15693_sprintUID(NULL, buf + 2)); + Dbprintf("AFI = %i UID = %s", i, iso15693_sprintUID(NULL, recv + 2)); } aborted = BUTTON_PRESS(); @@ -985,34 +1811,230 @@ void BruteforceIso15693Afi(uint32_t speed) { } // Allows to directly send commands to the tag via the client -// Has to increase dialog between device and client. +// OBS: doesn't turn off rf field afterwards. void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t *data) { - bool init = true; - int buflen = 0; - uint8_t buf[ISO15_MAX_FRAME]; - memset(buf, 0x00, sizeof(buf)); + LED_A_ON(); + + int recvlen = 0; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint32_t eof_time = 0; + uint16_t timeout; + bool request_answer = false; + + switch (data[1]) { + case ISO15_CMD_WRITE: + case ISO15_CMD_LOCK: + case ISO15_CMD_WRITEMULTI: + case ISO15_CMD_WRITEAFI: + case ISO15_CMD_LOCKAFI: + case ISO15_CMD_WRITEDSFID: + case ISO15_CMD_LOCKDSFID: + timeout = ISO15693_READER_TIMEOUT_WRITE; + request_answer = data[0] & ISO15_REQ_OPTION; + break; + default: + timeout = ISO15693_READER_TIMEOUT; + } if (DBGLEVEL >= DBG_EXTENDED) { - DbpString("[+] SEND"); - Dbhexdump(datalen, data, true); + Dbprintf("SEND:"); + Dbhexdump(datalen, data, false); } - buflen = SendDataTag(data, datalen, init, speed, (recv ? buf : NULL)); + uint32_t start_time = 0; + recvlen = SendDataTag(data, datalen, true, speed, (recv ? recvbuf : NULL), sizeof(recvbuf), start_time, timeout, &eof_time); + + // send a single EOF to get the tag response + if (request_answer) { + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + recvlen = SendDataTagEOF((recv ? recvbuf : NULL), sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT, &eof_time); + } + + // for the time being, switch field off to protect rdv4.0 + // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); if (recv) { - buflen = (buflen > ISO15_MAX_FRAME) ? ISO15_MAX_FRAME : buflen; - LED_B_ON(); - reply_old(CMD_ACK, buflen, 0, 0, buf, buflen); - LED_B_OFF(); + if (recvlen > ISO15693_MAX_RESPONSE_LENGTH) { + recvlen = ISO15693_MAX_RESPONSE_LENGTH; + } + reply_mix(CMD_ACK, recvlen, 0, 0, recvbuf, recvlen); if (DBGLEVEL >= DBG_EXTENDED) { - DbpString("[+] RECV"); - DbdecodeIso15693Answer(buflen, buf); - Dbhexdump(buflen, buf, true); + + Dbprintf("RECV:"); + if (recvlen > 0) { + Dbhexdump(recvlen, recvbuf, false); + DbdecodeIso15693Answer(recvlen, recvbuf); + } } } else { - reply_old(CMD_ACK, 1, 0, 0, 0, 0); + reply_mix(CMD_ACK, 1, 0, 0, 0, 0); } } + +/* +SLIx functions from official master forks. + +void LockPassSlixIso15693(uint32_t pass_id, uint32_t password) { + + LED_A_ON(); + + uint8_t cmd_inventory[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_SLOT1, 0x01, 0x00, 0x00, 0x00 }; + uint8_t cmd_get_rnd[] = {ISO15693_REQ_DATARATE_HIGH, 0xB2, 0x04, 0x00, 0x00 }; + uint8_t cmd_set_pass[] = {ISO15693_REQ_DATARATE_HIGH, 0xB3, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + //uint8_t cmd_write_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, 0xB4, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t cmd_lock_pass[] = {ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_ADDRESS, 0xB5, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00 }; + uint16_t crc; + int recvlen = 0; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + uint32_t start_time = 0; + bool done = false; + + // setup 'get random number' command + crc = Iso15693Crc(cmd_get_rnd, 3); + cmd_get_rnd[3] = crc & 0xff; + cmd_get_rnd[4] = crc >> 8; + + Dbprintf("LockPass: Press button lock password, long-press to terminate."); + + while (!done) { + + LED_D_ON(); + switch(BUTTON_HELD(1000)) { + case BUTTON_SINGLE_CLICK: + Dbprintf("LockPass: Reset 'DONE'-LED (A)"); + LED_A_OFF(); + LED_B_OFF(); + LED_C_OFF(); + break; + case BUTTON_HOLD: + Dbprintf("LockPass: Terminating"); + done = true; + break; + default: + SpinDelay(50); + continue; + } + + if (done) [ + break; + } + + recvlen = SendDataTag(cmd_get_rnd, sizeof(cmd_get_rnd), true, true, recvbuf, sizeof(recvbuf), start_time); + if (recvlen != 5) { + LED_C_ON(); + } else { + Dbprintf("LockPass: Received random 0x%02X%02X (%d)", recvbuf[1], recvbuf[2], recvlen); + + // setup 'set password' command + cmd_set_pass[4] = ((password>>0) &0xFF) ^ recvbuf[1]; + cmd_set_pass[5] = ((password>>8) &0xFF) ^ recvbuf[2]; + cmd_set_pass[6] = ((password>>16) &0xFF) ^ recvbuf[1]; + cmd_set_pass[7] = ((password>>24) &0xFF) ^ recvbuf[2]; + + crc = Iso15693Crc(cmd_set_pass, 8); + cmd_set_pass[8] = crc & 0xff; + cmd_set_pass[9] = crc >> 8; + + Dbprintf("LockPass: Sending old password to end privacy mode", cmd_set_pass[4], cmd_set_pass[5], cmd_set_pass[6], cmd_set_pass[7]); + recvlen = SendDataTag(cmd_set_pass, sizeof(cmd_set_pass), false, true, recvbuf, sizeof(recvbuf), start_time); + if (recvlen != 3) { + Dbprintf("LockPass: Failed to set password (%d)", recvlen); + LED_B_ON(); + } else { + crc = Iso15693Crc(cmd_inventory, 3); + cmd_inventory[3] = crc & 0xff; + cmd_inventory[4] = crc >> 8; + + Dbprintf("LockPass: Searching for tag..."); + recvlen = SendDataTag(cmd_inventory, sizeof(cmd_inventory), false, true, recvbuf, sizeof(recvbuf), start_time); + if (recvlen != 12) { + Dbprintf("LockPass: Failed to read inventory (%d)", recvlen); + LED_B_ON(); + LED_C_ON(); + } else { + + Dbprintf("LockPass: Answer from %02X%02X%02X%02X%02X%02X%02X%02X", recvbuf[9], recvbuf[8], recvbuf[7], recvbuf[6], recvbuf[5], recvbuf[4], recvbuf[3], recvbuf[2]); + + memcpy(&cmd_lock_pass[3], &recvbuf[2], 8); + + cmd_lock_pass[8+3] = pass_id; + + crc = Iso15693Crc(cmd_lock_pass, 8+4); + cmd_lock_pass[8+4] = crc & 0xff; + cmd_lock_pass[8+5] = crc >> 8; + + Dbprintf("LockPass: locking to password 0x%02X%02X%02X%02X for ID %02X", cmd_set_pass[4], cmd_set_pass[5], cmd_set_pass[6], cmd_set_pass[7], pass_id); + + recvlen = SendDataTag(cmd_lock_pass, sizeof(cmd_lock_pass), false, true, recvbuf, sizeof(recvbuf), start_time); + if (recvlen != 3) { + Dbprintf("LockPass: Failed to lock password (%d)", recvlen); + } else { + Dbprintf("LockPass: Successful (%d)", recvlen); + } + LED_A_ON(); + } + } } + } + + Dbprintf("LockPass: Finishing"); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + cmd_send(CMD_ACK, recvlen, 0, 0, recvbuf, recvlen); + LED_A_OFF(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); +} +*/ + +//----------------------------------------------------------------------------- +// Work with "magic Chinese" card. +// +//----------------------------------------------------------------------------- + +// Set the UID on Magic ISO15693 tag (based on Iceman's LUA-script). +void SetTag15693Uid(uint8_t *uid) { + + LED_A_ON(); + + uint8_t cmd[4][9] = { + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x3e, 0x00, 0x00, 0x00, 0x00}, + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x3f, 0x69, 0x96, 0x00, 0x00}, + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x38}, + {ISO15_REQ_DATARATE_HIGH, ISO15_CMD_WRITE, 0x39} + }; + + // Command 3 : 02 21 38 u8u7u6u5 (where uX = uid byte X) + cmd[2][3] = uid[7]; + cmd[2][4] = uid[6]; + cmd[2][5] = uid[5]; + cmd[2][6] = uid[4]; + + // Command 4 : 02 21 39 u4u3u2u1 (where uX = uid byte X) + cmd[3][3] = uid[3]; + cmd[3][4] = uid[2]; + cmd[3][5] = uid[1]; + cmd[3][6] = uid[0]; + + AddCrc15(cmd[0], 7); + AddCrc15(cmd[1], 7); + AddCrc15(cmd[2], 7); + AddCrc15(cmd[3], 7); + + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + + uint32_t start_time = 0; + uint32_t eof_time = 0; + for (int i = 0; i < 4; i++) { + SendDataTag(cmd[i], sizeof(cmd[i]), i == 0 ? true : false, true, recvbuf, sizeof(recvbuf), start_time, ISO15693_READER_TIMEOUT_WRITE, &eof_time); + start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + } + + reply_ng(CMD_HF_ISO15693_CSETUID, PM3_SUCCESS, NULL, 0); + switch_off(); +} diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index 2233ba9fd..168cee42c 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -12,15 +12,41 @@ #define __ISO15693_H #include "common.h" - #include "pm3_cmd.h" // struct -void RecordRawAdcSamplesIso15693(void); + +// Delays in SSP_CLK ticks. +// SSP_CLK runs at 13,56MHz / 32 = 423.75kHz when simulating a tag +#define DELAY_ISO15693_VCD_TO_VICC_SIM 132 // 132/423.75kHz = 311.5us from end of command EOF to start of tag response + +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when acting as reader. All values should be multiples of 16 +#define DELAY_ISO15693_VCD_TO_VICC_READER 1056 // 1056/3,39MHz = 311.5us from end of command EOF to start of tag response +#define DELAY_ISO15693_VICC_TO_VCD_READER 1024 // 1024/3.39MHz = 302.1us between end of tag response and next reader command + +void Iso15693InitReader(void); +void Iso15693InitTag(void); +void CodeIso15693AsReader(uint8_t *cmd, int n); +void CodeIso15693AsTag(uint8_t *cmd, size_t len); + +void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow); +int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time); +void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t *start_time); +int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeout, uint32_t *eof_time); + +//void RecordRawAdcSamplesIso15693(void); void AcquireRawAdcSamplesIso15693(void); void ReaderIso15693(uint32_t parameter); // Simulate an ISO15693 reader - greg -void SimTagIso15693(uint32_t parameter, uint8_t *uid); // simulate an ISO15693 tag - greg +void SimTagIso15693(uint8_t *uid); // simulate an ISO15693 tag - greg void BruteforceIso15693Afi(uint32_t speed); // find an AFI of a tag - atrox void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t *data); // send arbitrary commands from CLI - atrox void Iso15693InitReader(void); +void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string); + +int SendDataTag(uint8_t *send, int sendlen, bool init, bool speed_fast, uint8_t *recv, + uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time); + +int SendDataTagEOF(uint8_t *recv, uint16_t max_recv_len, uint32_t start_time, uint16_t timeout, uint32_t *eof_time); + +void SetTag15693Uid(uint8_t *uid); #endif diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 21b0c0a42..92961fd4a 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -64,11 +64,11 @@ static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ //----------------------------------------------------------------------------- // I/O interface abstraction (FPGA -> ARM) //----------------------------------------------------------------------------- -static uint8_t rx_byte_from_fpga(void) { +static uint16_t rx_frame_from_fpga(void) { for (;;) { WDT_HIT(); - // wait for byte be become available in rx holding register + // wait for frame be become available in rx holding register if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { return AT91C_BASE_SSC->SSC_RHR; } @@ -76,48 +76,53 @@ static uint8_t rx_byte_from_fpga(void) { } //----------------------------------------------------------------------------- -// Demodulation +// Demodulation (Reader) //----------------------------------------------------------------------------- -// Returns am aproximated power measurement +// Returns a demedulated bit // -// The FPGA running on the xcorrelation kernel samples the subcarrier at ~3 MHz. -// The kernel was initialy designed to receive BSPK/2-PSK. Hance, it reports an -// I/Q pair every 18.9us (8 bits i and 8 bits q). +// The FPGA running xcorrelation samples the subcarrier at ~13.56 MHz. The mode +// was initialy designed to receive BSPK/2-PSK. Hance, it reports an I/Q pair +// every 4.7us (8 bits i and 8 bits q). // // The subcarrier amplitude can be calculated using Pythagoras sqrt(i^2 + q^2). // To reduce CPU time the amplitude is approximated by using linear functions: // am = MAX(ABS(i),ABS(q)) + 1/2*MIN(ABS(i),ABSq)) // -// Note: The SSC receiver is never synchronized the calculation may be performed -// on a i/q pair from two subsequent correlations, but does not matter. -// Note: inlining this function would fail with -Os -static int32_t sample_power(void) { - int32_t q = (int8_t)rx_byte_from_fpga(); - q = ABS(q); - int32_t i = (int8_t)rx_byte_from_fpga(); - i = ABS(i); - - return MAX(i, q) + (MIN(i, q) >> 1); -} - -// Returns a demedulated bit +// The bit time is 99.1us (21 I/Q pairs). The receiver skips the first 5 samples +// and averages the next (most stable) 8 samples. The final 8 samples are dropped +// also. // -// An aproximated power measurement is available every 18.9us. The bit time -// is 100us. The code samples 5 times and uses the last (most stable) sample. +// The demodulated should be alligned to the bit period by the caller. This is +// done in rx_bit and rx_ack. // // Note: The demodulator would be drifting (18.9us * 5 != 100us), rx_frame // has a delay loop that aligns rx_bit calls to the TAG tx timeslots. - +// // Note: inlining this function would fail with -Os static bool rx_bit(void) { - int32_t power; + int32_t sum_cq = 0; + int32_t sum_ci = 0; + // skip first 5 I/Q pairs for (size_t i = 0; i < 5; ++i) { - power = sample_power(); + (void)rx_frame_from_fpga(); } - return (power > INPUT_THRESHOLD); + // sample next 8 I/Q pairs + for (uint8_t i = 0; i < 8; ++i) { + uint16_t iq = rx_frame_from_fpga(); + int8_t ci = (int8_t)(iq >> 8); + int8_t cq = (int8_t)(iq & 0xff); + sum_ci += ci; + sum_cq += cq; + } + + // calculate power + int32_t power = (MAX(ABS(sum_ci), ABS(sum_cq)) + (MIN(ABS(sum_ci), ABS(sum_cq)) >> 1)); + + // compare average (power / 8) to threshold + return ((power >> 3) > INPUT_THRESHOLD); } //----------------------------------------------------------------------------- @@ -131,18 +136,18 @@ static bool rx_bit(void) { static void tx_bit(bool bit) { // insert pause - LOW(GPIO_SSC_DOUT); + HIGH(GPIO_SSC_DOUT); last_frame_end += RWD_TIME_PAUSE; while (GET_TICKS < last_frame_end) { }; - HIGH(GPIO_SSC_DOUT); - // return to high, wait for bit periode to end + // return to carrier on, wait for bit periode to end + LOW(GPIO_SSC_DOUT); last_frame_end += (bit ? RWD_TIME_1 : RWD_TIME_0) - RWD_TIME_PAUSE; while (GET_TICKS < last_frame_end) { }; } //----------------------------------------------------------------------------- -// Frame Handling +// Frame Handling (Reader) // // The LEGIC RF protocol from card to reader does not include explicit frame // start/stop information or length information. The reader must know beforehand @@ -152,7 +157,7 @@ static void tx_bit(bool bit) { //----------------------------------------------------------------------------- static void tx_frame(uint32_t frame, uint8_t len) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_FULL_MOD); // wait for next tx timeslot last_frame_end += RWD_FRAME_WAIT; @@ -169,10 +174,10 @@ static void tx_frame(uint32_t frame, uint8_t len) { }; // add pause to mark end of the frame - LOW(GPIO_SSC_DOUT); + HIGH(GPIO_SSC_DOUT); last_frame_end += RWD_TIME_PAUSE; while (GET_TICKS < last_frame_end) { }; - HIGH(GPIO_SSC_DOUT); + LOW(GPIO_SSC_DOUT); // log uint8_t cmdbytes[] = {len, BYTEx(frame, 0), BYTEx(frame, 1), BYTEx(frame, 2)}; @@ -180,9 +185,7 @@ static void tx_frame(uint32_t frame, uint8_t len) { } static uint32_t rx_frame(uint8_t len) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR - | FPGA_HF_READER_RX_XCORR_848_KHZ - | FPGA_HF_READER_RX_XCORR_QUARTER); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_212_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); // hold sampling until card is expected to respond last_frame_end += TAG_FRAME_WAIT; @@ -210,9 +213,7 @@ static uint32_t rx_frame(uint8_t len) { static bool rx_ack(void) { // change fpga into rx mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR - | FPGA_HF_READER_RX_XCORR_848_KHZ - | FPGA_HF_READER_RX_XCORR_QUARTER); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_212_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); // hold sampling until card is expected to respond last_frame_end += TAG_FRAME_WAIT; @@ -271,27 +272,25 @@ static int init_card(uint8_t cardtype, legic_card_select_t *p_card) { p_card->cmdsize = 0; p_card->addrsize = 0; p_card->cardsize = 0; - return 2; + return PM3_ESOFT; } - return 0; + return PM3_SUCCESS; } -static void init_reader(bool clear_mem) { +static void init_reader(void) { // configure FPGA FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR - | FPGA_HF_READER_RX_XCORR_848_KHZ - | FPGA_HF_READER_RX_XCORR_QUARTER); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_SUBCARRIER_212_KHZ | FPGA_HF_READER_MODE_RECEIVE_IQ); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); LED_A_ON(); // configure SSC with defaults - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // re-claim GPIO_SSC_DOUT as GPIO and enable output AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - HIGH(GPIO_SSC_DOUT); + LOW(GPIO_SSC_DOUT); // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier. legic_mem = BigBuf_get_EM_addr(); @@ -412,11 +411,11 @@ legic_card_select_t *getLegicCardInfo(void) { void LegicRfInfo(void) { // configure ARM and FPGA - init_reader(false); + init_reader(); // establish shared secret and detect card type uint8_t card_type = setup_phase(0x01); - if (init_card(card_type, &card) != 0) { + if (init_card(card_type, &card) != PM3_SUCCESS) { reply_mix(CMD_ACK, 0, 0, 0, 0, 0); goto OUT; } @@ -452,11 +451,11 @@ int LegicRfReaderEx(uint16_t offset, uint16_t len, uint8_t iv) { int res = PM3_SUCCESS; // configure ARM and FPGA - init_reader(false); + init_reader(); // establish shared secret and detect card type uint8_t card_type = setup_phase(iv); - if (init_card(card_type, &card) != 0) { + if (init_card(card_type, &card) != PM3_SUCCESS) { res = PM3_ESOFT; goto OUT; } @@ -487,11 +486,11 @@ OUT: void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv) { // configure ARM and FPGA - init_reader(false); + init_reader(); // establish shared secret and detect card type uint8_t card_type = setup_phase(iv); - if (init_card(card_type, &card) != 0) { + if (init_card(card_type, &card) != PM3_SUCCESS) { reply_mix(CMD_ACK, 0, 0, 0, 0, 0); goto OUT; } @@ -524,7 +523,7 @@ OUT: void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, uint8_t *data) { // configure ARM and FPGA - init_reader(false); + init_reader(); // uid is not writeable if (offset <= WRITE_LOWERLIMIT) { @@ -534,7 +533,7 @@ void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, uint8_t *data) { // establish shared secret and detect card type uint8_t card_type = setup_phase(iv); - if (init_card(card_type, &card) != 0) { + if (init_card(card_type, &card) != PM3_SUCCESS) { reply_mix(CMD_ACK, 0, 0, 0, 0, 0); goto OUT; } @@ -545,8 +544,8 @@ void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, uint8_t *data) { } // write in reverse order, only then is DCF (decremental field) writable - while (len-- > 0 && !BUTTON_PRESS()) { - if (!write_byte(len + offset, data[len], card.addrsize)) { + while (len-- > 0 && BUTTON_PRESS() == false) { + if (write_byte(len + offset, data[len], card.addrsize) == false) { Dbprintf("operation failed | %02X | %02X | %02X", len + offset, len, data[len]); reply_mix(CMD_ACK, 0, 0, 0, 0, 0); goto OUT; diff --git a/armsrc/legicrfsim.c b/armsrc/legicrfsim.c index 35eefc56c..00b40efb2 100644 --- a/armsrc/legicrfsim.c +++ b/armsrc/legicrfsim.c @@ -15,7 +15,7 @@ #include "crc.h" /* legic crc-4 */ #include "legic_prng.h" /* legic PRNG impl */ #include "legic.h" /* legic_card_select_t struct */ - +#include "cmd.h" #include "proxmark3_arm.h" #include "BigBuf.h" #include "fpgaloader.h" @@ -53,7 +53,7 @@ static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ #define RWD_TIME_PAUSE 4 /* 18.9us */ #define RWD_TIME_1 21 /* RWD_TIME_PAUSE 18.9us off + 80.2us on = 99.1us */ #define RWD_TIME_0 13 /* RWD_TIME_PAUSE 18.9us off + 42.4us on = 61.3us */ -#define RWD_CMD_TIMEOUT 120 /* 120 * 99.1us (arbitrary value) */ +#define RWD_CMD_TIMEOUT 400 /* 120 * 99.1us (arbitrary value) */ #define RWD_MIN_FRAME_LEN 6 /* Shortest frame is 6 bits */ #define RWD_MAX_FRAME_LEN 23 /* Longest frame is 23 bits */ @@ -68,6 +68,7 @@ static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ // Note: inlining this function would fail with -Os static bool wait_for(bool value, const uint32_t timeout) { while ((bool)(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN) != value) { + WDT_HIT(); if (GetCountSspClk() > timeout) { return false; } @@ -215,7 +216,7 @@ static int32_t rx_frame(uint8_t *len) { last_frame_end -= 2; // wait for first pause (start of frame) - for (uint8_t i = 0; true; ++i) { + for (uint16_t i = 0; true; ++i) { // increment prng every TAG_BIT_PERIOD last_frame_end += TAG_BIT_PERIOD; legic_prng_forward(1); @@ -295,20 +296,19 @@ static int32_t init_card(uint8_t cardtype, legic_card_select_t *p_card) { p_card->cmdsize = 0; p_card->addrsize = 0; p_card->cardsize = 0; - return 2; + return PM3_ESOFT; } - return 0; + return PM3_SUCCESS; } static void init_tag(void) { // configure FPGA FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR - | FPGA_HF_SIMULATOR_MODULATE_212K); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); // configure SSC with defaults - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); // first pull output to low to prevent glitches then re-claim GPIO_SSC_DOUT LOW(GPIO_SSC_DOUT); @@ -456,23 +456,37 @@ static int32_t connected_phase(legic_card_select_t *p_card) { // Only this function is public / called from appmain.c //----------------------------------------------------------------------------- -void LegicRfSimulate(uint8_t cardtype) { +void LegicRfSimulate(uint8_t tagtype, bool send_reply) { // configure ARM and FPGA init_tag(); + int res = PM3_SUCCESS; // verify command line input - if (init_card(cardtype, &card) != 0) { - DbpString("[!] Unknown tagtype."); + if (init_card(tagtype, &card) != PM3_SUCCESS) { + DbpString("Unknown tagtype to simulate"); + res = PM3_ESOFT; goto OUT; } + uint16_t counter = 0; LED_A_ON(); - DbpString("[=] Starting Legic emulator, press " _YELLOW_("button") " to end"); - while (!BUTTON_PRESS() && !data_available()) { + + Dbprintf("Legic Prime, simulating uid: %02X%02X%02X%02X", legic_mem[0], legic_mem[1], legic_mem[2], legic_mem[3]); + + while (BUTTON_PRESS() == false) { WDT_HIT(); + if (counter >= 2000) { + if (data_available()) { + res = PM3_EOPABORTED; + break; + } + counter = 0; + } + counter++; + // wait for carrier, restart after timeout - if (!wait_for(RWD_PULSE, GetCountSspClk() + TAG_BIT_PERIOD)) { + if (wait_for(RWD_PULSE, GetCountSspClk() + TAG_BIT_PERIOD) == false) { continue; } @@ -482,13 +496,25 @@ void LegicRfSimulate(uint8_t cardtype) { } // conection is established, process commands until one fails - while (!connected_phase(&card)) { + while (connected_phase(&card) == false) { WDT_HIT(); } } OUT: - DbpString("[=] Sim stopped"); + + if (DBGLEVEL >= DBG_ERROR) { + Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", get_tracing(), BigBuf_get_traceLen()); + } + + if (res == PM3_EOPABORTED) + DbpString("aborted by user"); + switch_off(); StopTicks(); + + if (send_reply) + reply_ng(CMD_HF_LEGIC_SIMULATE, res, NULL, 0); + + BigBuf_free_keep_EM(); } diff --git a/armsrc/legicrfsim.h b/armsrc/legicrfsim.h index f7be94165..0b0bdbd9c 100644 --- a/armsrc/legicrfsim.h +++ b/armsrc/legicrfsim.h @@ -1,7 +1,8 @@ //----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz // 2018 AntiCat -// +// 2019 Piwi +// 2020 Iceman // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. @@ -14,6 +15,6 @@ #include "common.h" -void LegicRfSimulate(uint8_t cardtype); +void LegicRfSimulate(uint8_t tagtype, bool send_reply); #endif /* __LEGICRFSIM_H */ diff --git a/armsrc/lfadc.c b/armsrc/lfadc.c index 8c453bdc9..1fb1bccde 100644 --- a/armsrc/lfadc.c +++ b/armsrc/lfadc.c @@ -148,7 +148,7 @@ bool lf_get_reader_modulation(void) { } void lf_wait_periods(size_t periods) { - // wait detect gap + // wait detect gap lf_count_edge_periods_ex(periods, true, false); } @@ -181,7 +181,7 @@ void lf_init(bool reader, bool simulate) { SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); // When in reader mode, give the field a bit of time to settle. // 313T0 = 313 * 8us = 2504us = 2.5ms Hitag2 tags needs to be fully powered. @@ -238,40 +238,40 @@ void lf_finalize(void) { } size_t lf_detect_field_drop(size_t max) { -/* - size_t periods = 0; -// int16_t checked = 0; + /* + size_t periods = 0; + // int16_t checked = 0; - while (BUTTON_PRESS() == false) { + while (BUTTON_PRESS() == false) { - // // only every 1000th times, in order to save time when collecting samples. - // if (checked == 4000) { - // if (data_available()) { - // checked = -1; - // break; - // } else { - // checked = 0; + // // only every 1000th times, in order to save time when collecting samples. + // if (checked == 4000) { + // if (data_available()) { + // checked = -1; + // break; + // } else { + // checked = 0; + // } // } - // } - // ++checked; + // ++checked; - WDT_HIT(); + WDT_HIT(); - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - periods++; - volatile uint8_t adc_val = AT91C_BASE_SSC->SSC_RHR; + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + periods++; + volatile uint8_t adc_val = AT91C_BASE_SSC->SSC_RHR; - if (g_logging) logSampleSimple(adc_val); + if (g_logging) logSampleSimple(adc_val); - if (adc_val == 0) { - rising_edge = false; - return periods; + if (adc_val == 0) { + rising_edge = false; + return periods; + } + + if (periods == max) return 0; } - - if (periods == max) return 0; } - } -*/ + */ return 0; } diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 7e907da09..03e322681 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -27,6 +27,7 @@ #include "protocols.h" #include "pmflash.h" #include "flashmem.h" // persistence on flash +#include "appmain.h" // print stack /* Notes about EM4xxx timings. @@ -382,13 +383,15 @@ void loadT55xxConfig(void) { * @param period_1 * @param command (in binary char array) */ -void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint32_t period_1, uint8_t *command) { +void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint16_t period_0, uint16_t period_1, uint8_t *symbol_extra, uint16_t *period_extra, uint8_t *command, bool verbose, uint32_t samples) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); // use lf config settings sample_config *sc = getSamplingConfig(); + LFSetupFPGAForADC(sc->divisor, true); + // this causes the field to turn on for uncontrolled amount of time, so we'll turn it off // Make sure the tag is reset FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -401,15 +404,14 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint // clear read buffer BigBuf_Clear_keep_EM(); - LFSetupFPGAForADC(sc->divisor, true); - - // little more time for the tag to fully power up - WaitMS(20); - // if delay_off = 0 then just bitbang 1 = antenna on 0 = off for respective periods. bool bitbang = (delay_off == 0); // now modulate the reader field + + // Some tags need to be interrogated very soon after activation else they enter their emulation mode + // Therefore it's up to the caller to add an initial symbol of adequate duration, except for bitbang mode. if (bitbang) { + TurnReadLFOn(20000); // HACK it appears the loop and if statements take up about 7us so adjust waits accordingly... uint8_t hack_cnt = 7; if (period_0 < hack_cnt || period_1 < hack_cnt) { @@ -458,11 +460,19 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint } else { // old mode of cmd read using delay as off period while (*command != '\0' && *command != ' ') { LED_D_ON(); - if (*(command++) == '0') + if (*command == '0') { TurnReadLFOn(period_0); - else + } else if (*command == '1') { TurnReadLFOn(period_1); - + } else { + for (uint8_t i = 0; i < LF_CMDREAD_MAX_EXTRA_SYMBOLS; i++) { + if (*command == symbol_extra[i]) { + TurnReadLFOn(period_extra[i]); + break; + } + } + } + command++; LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); WaitUS(delay_off); @@ -474,7 +484,7 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD); // now do the read - DoAcquisition_config(true, 0); + DoAcquisition_config(verbose, samples); // Turn off antenna FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -678,7 +688,7 @@ void AcquireTiType(void) { AT91C_BASE_SSC->SSC_TCMR = 0; // Transmit Frame Mode Register AT91C_BASE_SSC->SSC_TFMR = 0; - // iceman, FpgaSetupSsc() ?? the code above? can it be replaced? + // iceman, FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER) ?? the code above? can it be replaced? LED_D_ON(); // modulate antenna @@ -721,7 +731,7 @@ void AcquireTiType(void) { } // reset SSC - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); } // arguments: 64bit data split into 32bit idhi:idlo and optional 16bit crc @@ -1245,7 +1255,7 @@ int lf_hid_watch(int findone, uint32_t *high, uint32_t *low) { while (BUTTON_PRESS() == false) { WDT_HIT(); - + // cancel w usb command. if (interval == 4000) { if (data_available()) { @@ -1262,7 +1272,7 @@ int lf_hid_watch(int findone, uint32_t *high, uint32_t *low) { // FSK demodulator // 50 * 128 * 2 - big enough to catch 2 sequences of largest format size = MIN(12800, BigBuf_max_traceLen()); - + int idx = HIDdemodFSK(dest, &size, &hi2, &hi, &lo, &dummyIdx); if (idx < 0) continue; @@ -1354,7 +1364,7 @@ int lf_awid_watch(int findone, uint32_t *high, uint32_t *low) { while (BUTTON_PRESS() == false) { WDT_HIT(); - + // cancel w usb command. if (interval == 4000) { if (data_available()) { @@ -1535,7 +1545,7 @@ int lf_io_watch(int findone, uint32_t *high, uint32_t *low) { while (BUTTON_PRESS() == false) { WDT_HIT(); - + // cancel w usb command. if (interval == 4000) { if (data_available()) { @@ -2010,13 +2020,12 @@ void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, flags |= (downlink_mode & 3) << 3; if (brute_mem) flags |= 0x0100; -// T55xxReadBlockExt (flags,block,pwd); + size_t samples = 12000; - // bool brute_mem = (flags & 0x0100) >> 8; LED_A_ON(); - if (brute_mem) samples = 1024; + if (brute_mem) samples = 2048; //-- Set Read Flag to ensure SendCMD does not add "data" to the packet //-- flags |= 0x40; @@ -2041,47 +2050,62 @@ void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, // Acquisition // Now do the acquisition - DoPartialAcquisition(0, false, samples, 0); + DoPartialAcquisition(0, false, samples, 1000); // Turn the field off - if (!brute_mem) { + if (brute_mem == false) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); reply_ng(CMD_LF_T55XX_READBL, PM3_SUCCESS, NULL, 0); LED_A_OFF(); } - } + void T55xx_ChkPwds(uint8_t flags) { - DbpString("[+] T55XX Check pwds using flashmemory starting"); +#define CHK_SAMPLES_SIGNAL 2048 + +#ifdef WITH_FLASH + DbpString(_CYAN_("T55XX Check pwds using flashmemory starting")); +#else + DbpString(_CYAN_("T55XX Check pwds starting")); +#endif // First get baseline and setup LF mode. - // tends to mess up BigBuf uint8_t *buf = BigBuf_get_addr(); - uint8_t ret = 0; uint8_t downlink_mode = (flags >> 3) & 0x03; - uint32_t b1, baseline = 0; + uint64_t b1, baseline_faulty = 0; - // collect baseline for failed attempt + DbpString("Determine baseline..."); + + // collect baseline for failed attempt ( should give me block1 ) uint8_t x = 32; while (x--) { b1 = 0; - T55xxReadBlock(0, 0, true, 1, 0, downlink_mode); - for (uint16_t j = 0; j < 1024; ++j) - b1 += buf[j]; - + T55xxReadBlock(0, 0, true, 0, 0, downlink_mode); + for (uint16_t j = 0; j < CHK_SAMPLES_SIGNAL; ++j) { + b1 += (buf[j] * buf[j]); + } b1 *= b1; b1 >>= 8; - baseline += b1; + baseline_faulty += b1; } + baseline_faulty >>= 5; - baseline >>= 5; - Dbprintf("[=] Baseline determined [%u]", baseline); + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("Baseline " _YELLOW_("%llu"), baseline_faulty); uint8_t *pwds = BigBuf_get_EM_addr(); uint16_t pwd_count = 0; - uint32_t candidate = 0; + + struct p { + bool found; + uint32_t candidate; + } PACKED payload; + + payload.found = false; + payload.candidate = 0; + #ifdef WITH_FLASH BigBuf_Clear_EM(); @@ -2107,48 +2131,51 @@ void T55xx_ChkPwds(uint8_t flags) { if (isok != pwd_size_available) goto OUT; - Dbprintf("[=] Password dictionary count %d ", pwd_count); + Dbprintf("Password dictionary count " _YELLOW_("%d"), pwd_count); + #endif - uint32_t pwd = 0, curr = 0, prev = 0; - for (uint16_t i = 0; i < pwd_count; ++i) { + uint64_t curr = 0, prev = 0; + int32_t idx = -1; - if (BUTTON_PRESS() && !data_available()) { - goto OUT; - } + for (uint32_t i = 0; i < pwd_count; i++) { - pwd = bytes_to_num(pwds + i * 4, 4); + uint32_t pwd = bytes_to_num(pwds + (i * 4), 4); T55xxReadBlock(0, true, true, 0, pwd, downlink_mode); - // calc mean of BigBuf 1024 samples. - uint32_t sum = 0; - for (uint16_t j = 0; j < 1024; ++j) { - sum += buf[j]; + uint64_t sum = 0; + for (uint16_t j = 0; j < CHK_SAMPLES_SIGNAL; ++j) { + sum += (buf[j] * buf[j]); } - sum *= sum; sum >>= 8; - int32_t tmp = (sum - baseline); - curr = ABS(tmp); + int64_t tmp_dist = (baseline_faulty - sum); + curr = ABS(tmp_dist); - Dbprintf("[=] Pwd %08X | ABS %u", pwd, curr); + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("%08x has distance " _YELLOW_("%llu"), pwd, curr); if (curr > prev) { - Dbprintf("[=] --> ABS %u Candidate %08X <--", curr, pwd); - candidate = pwd; + idx = i; prev = curr; } } - if (candidate) - ret = 1; + if (idx != -1) { + payload.found = true; + payload.candidate = bytes_to_num(pwds + (idx * 4), 4); + } +#ifdef WITH_FLASH OUT: +#endif + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - reply_mix(CMD_ACK, ret, candidate, 0, 0, 0); LEDsoff(); + reply_ng(CMD_LF_T55XX_CHK_PWDS, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); + BigBuf_free(); } void T55xxWakeUp(uint32_t pwd, uint8_t flags) { @@ -2342,6 +2369,7 @@ int copy_em410x_to_t55xx(uint8_t card, uint8_t clock, uint32_t id_hi, uint32_t i #define FWD_CMD_LOGIN 0xC #define FWD_CMD_WRITE 0xA #define FWD_CMD_READ 0x9 +#define FWD_CMD_PROTECT 0x3 #define FWD_CMD_DISABLE 0x5 static uint8_t forwardLink_data[64]; //array of forwarded bits @@ -2474,36 +2502,37 @@ static void SendForward(uint8_t fwd_bit_count) { } static void EM4xLogin(uint32_t pwd) { - uint8_t len; forward_ptr = forwardLink_data; - len = Prepare_Cmd(FWD_CMD_LOGIN); + uint8_t len = Prepare_Cmd(FWD_CMD_LOGIN); len += Prepare_Data(pwd & 0xFFFF, pwd >> 16); SendForward(len); //WaitUS(20); // no wait for login command. // should receive - // 0000 1010 ok. + // 0000 1010 ok // 0000 0001 fail } void EM4xReadWord(uint8_t addr, uint32_t pwd, uint8_t usepwd) { - LED_A_ON(); - uint8_t len; + StartTicks(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + WaitMS(20); - //clear buffer now so it does not interfere with timing later + LED_A_ON(); + + // clear buffer now so it does not interfere with timing later BigBuf_Clear_ext(false); - StartTicks(); /* should we read answer from Logincommand? * * should receive - * 0000 1010 ok. + * 0000 1010 ok * 0000 0001 fail **/ if (usepwd) EM4xLogin(pwd); forward_ptr = forwardLink_data; - len = Prepare_Cmd(FWD_CMD_READ); + uint8_t len = Prepare_Cmd(FWD_CMD_READ); len += Prepare_Addr(addr); SendForward(len); @@ -2512,19 +2541,23 @@ void EM4xReadWord(uint8_t addr, uint32_t pwd, uint8_t usepwd) { DoPartialAcquisition(20, false, 6000, 1000); + StopTicks(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); reply_ng(CMD_LF_EM4X_READWORD, PM3_SUCCESS, NULL, 0); - LED_A_OFF(); + LEDsoff(); } void EM4xWriteWord(uint8_t addr, uint32_t data, uint32_t pwd, uint8_t usepwd) { - LED_A_ON(); - uint8_t len; - - //clear buffer now so it does not interfere with timing later - BigBuf_Clear_ext(false); StartTicks(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + WaitMS(50); + + LED_A_ON(); + + // clear buffer now so it does not interfere with timing later + BigBuf_Clear_ext(false); + /* should we read answer from Logincommand? * * should receive @@ -2534,20 +2567,68 @@ void EM4xWriteWord(uint8_t addr, uint32_t data, uint32_t pwd, uint8_t usepwd) { if (usepwd) EM4xLogin(pwd); forward_ptr = forwardLink_data; - len = Prepare_Cmd(FWD_CMD_WRITE); + uint8_t len = Prepare_Cmd(FWD_CMD_WRITE); len += Prepare_Addr(addr); len += Prepare_Data(data & 0xFFFF, data >> 16); SendForward(len); - //Wait 20ms for write to complete? - WaitMS(7); + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + StopTicks(); + reply_ng(CMD_LF_EM4X_WRITEWORD, PM3_ETEAROFF, NULL, 0); + } else { + // Wait 20ms for write to complete? + // No, when write is denied, err preamble comes much sooner + //WaitUS(10820); // tPC+tWEE - DoPartialAcquisition(20, false, 6000, 1000); + DoPartialAcquisition(0, false, 6000, 1000); + StopTicks(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + reply_ng(CMD_LF_EM4X_WRITEWORD, PM3_SUCCESS, NULL, 0); + } + LEDsoff(); +} + +void EM4xProtectWord(uint32_t data, uint32_t pwd, uint8_t usepwd) { + + StartTicks(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - reply_ng(CMD_LF_EM4X_WRITEWORD, PM3_SUCCESS, NULL, 0); - LED_A_OFF(); + WaitMS(50); + + LED_A_ON(); + + // clear buffer now so it does not interfere with timing later + BigBuf_Clear_ext(false); + + /* should we read answer from Logincommand? + * + * should receive + * 0000 1010 ok. + * 0000 0001 fail + **/ + if (usepwd) EM4xLogin(pwd); + + forward_ptr = forwardLink_data; + uint8_t len = Prepare_Cmd(FWD_CMD_PROTECT); + len += Prepare_Data(data & 0xFFFF, data >> 16); + + SendForward(len); + + if (tearoff_hook() == PM3_ETEAROFF) { // tearoff occured + StopTicks(); + reply_ng(CMD_LF_EM4X_PROTECTWORD, PM3_ETEAROFF, NULL, 0); + } else { + // Wait 20ms for write to complete? + // No, when write is denied, err preamble comes much sooner + //WaitUS(13640); // tPC+tPR + + DoPartialAcquisition(0, false, 6000, 1000); + StopTicks(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + reply_ng(CMD_LF_EM4X_PROTECTWORD, PM3_SUCCESS, NULL, 0); + } + LEDsoff(); } /* @@ -2594,32 +2675,53 @@ void Cotag(uint32_t arg0) { LED_A_ON(); - LFSetupFPGAForADC(LF_FREQ2DIV(132), true); + LFSetupFPGAForADC(LF_FREQ2DIV(132), true); //132 //clear buffer now so it does not interfere with timing later + BigBuf_free(); BigBuf_Clear_ext(false); - //send COTAG start pulse - ON(740) OFF(2035) - ON(3330) OFF(2035) - ON(740) OFF(2035) - ON(1000) + // send COTAG start pulse + // http://www.proxmark.org/forum/viewtopic.php?id=4455 + /* + ON(740) OFF(2035) + ON(3330) OFF(2035) + ON(740) OFF(2035) + ON(2000) + */ + ON(800) OFF(2200) + ON(3600) OFF(2200) + ON(800) OFF(2200) + ON(2000) // ON(3400) + + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_FREQ2DIV(66)); // 66kHz switch (rawsignal) { - case 0: - doCotagAcquisition(40000); + case 0: { + doCotagAcquisition(); + reply_ng(CMD_LF_COTAG_READ, PM3_SUCCESS, NULL, 0); break; - case 1: - doCotagAcquisitionManchester(); + } + case 1: { + uint8_t *dest = BigBuf_malloc(COTAG_BITS); + uint16_t bits = doCotagAcquisitionManchester(dest, COTAG_BITS); + reply_ng(CMD_LF_COTAG_READ, PM3_SUCCESS, dest, bits); break; - case 2: + } + case 2: { DoAcquisition_config(false, 0); + reply_ng(CMD_LF_COTAG_READ, PM3_SUCCESS, NULL, 0); break; + } + default: { + reply_ng(CMD_LF_COTAG_READ, PM3_SUCCESS, NULL, 0); + break; + } } + // Turn the field off - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off - reply_ng(CMD_LF_COTAG_READ, PM3_SUCCESS, NULL, 0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); } diff --git a/armsrc/lfops.h b/armsrc/lfops.h index 2d76bef48..1c191d65d 100644 --- a/armsrc/lfops.h +++ b/armsrc/lfops.h @@ -15,8 +15,7 @@ #include "pm3_cmd.h" // struct -void AcquireRawAdcSamples125k(int divisor); -void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint32_t period_1, uint8_t *command); +void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint16_t period_0, uint16_t period_1, uint8_t *symbol_extra, uint16_t *period_extra, uint8_t *command, bool verbose, uint32_t samples); void ReadTItag(void); void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc); @@ -59,6 +58,7 @@ void TurnReadLFOn(uint32_t delay); void EM4xReadWord(uint8_t addr, uint32_t pwd, uint8_t usepwd); void EM4xWriteWord(uint8_t addr, uint32_t data, uint32_t pwd, uint8_t usepwd); +void EM4xProtectWord(uint32_t data, uint32_t pwd, uint8_t usepwd); void Cotag(uint32_t arg0); void setT55xxConfig(uint8_t arg0, t55xx_configurations_t *c); diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index f77084b3d..d31342ac0 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -36,7 +36,7 @@ static BitstreamOut data = {0, 0, 0}; // internal struct to keep track of samples gathered static sampling_t samples = {0, 0, 0, 0}; -void printConfig(void) { +void printLFConfig(void) { uint32_t d = config.divisor; DbpString(_CYAN_("LF Sampling config")); Dbprintf(" [q] divisor.............%d ( "_GREEN_("%d.%02d kHz")" )", d, 12000 / (d + 1), ((1200000 + (d + 1) / 2) / (d + 1)) - ((12000 / (d + 1)) * 100)); @@ -51,11 +51,11 @@ void printConfig(void) { } void printSamples(void) { - DbpString(_CYAN_("LF Sampling memory")); - Dbprintf(" decimation counter.....%d ", samples.dec_counter); - Dbprintf(" sum.....%u ", samples.sum); - Dbprintf(" counter.....%u ", samples.counter); - Dbprintf(" total saved.....%u ", samples.total_saved); + DbpString(_CYAN_("LF Sampling memory usage")); +// Dbprintf(" decimation counter...%d", samples.dec_counter); +// Dbprintf(" sum..................%u", samples.sum); + Dbprintf(" counter.............." _YELLOW_("%u"), samples.counter); + Dbprintf(" total saved.........." _YELLOW_("%u"), samples.total_saved); print_stack_usage(); } @@ -97,7 +97,7 @@ void setSamplingConfig(sample_config *sc) { config.samples_to_skip = sc->samples_to_skip; if (sc->verbose) - printConfig(); + printLFConfig(); } sample_config *getSamplingConfig(void) { @@ -127,7 +127,7 @@ void initSampleBufferEx(uint32_t *sample_size, bool use_malloc) { Dbprintf("initSampleBufferEx, param NULL"); return; } - BigBuf_free(); + BigBuf_free_keep_EM(); // We can't erase the buffer now, it would drastically delay the acquisition if (use_malloc) { @@ -171,7 +171,7 @@ void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool // keep track of total gather samples regardless how many was discarded. if (samples.counter-- == 0) return; - if (bits_per_sample == 0) bits_per_sample = 1; + if (bits_per_sample == 0) bits_per_sample = 1; if (bits_per_sample > 8) bits_per_sample = 8; if (decimation == 0) decimation = 1; @@ -236,15 +236,18 @@ void LFSetupFPGAForADC(int divisor, bool reader_field) { // Connect the A/D to the peak-detected low-frequency path. SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - // 50ms for the resonant antenna to settle. - if (reader_field) - SpinDelay(50); - // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); // start a 1.5ticks is 1us StartTicks(); + + // 50ms for the resonant antenna to settle. + if (reader_field) { + WaitMS(50); + } else { + WaitMS(1); + } } /** @@ -268,18 +271,18 @@ uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, in initSampleBuffer(&sample_size); if (DBGLEVEL >= DBG_DEBUG) { - Dbprintf("lf sampling - after init"); printSamples(); } + bool trigger_hit = false; uint32_t cancel_counter = 0; int16_t checked = 0; while (BUTTON_PRESS() == false) { - // only every 1000th times, in order to save time when collecting samples. + // only every 4000th times, in order to save time when collecting samples. // interruptible only when logging not yet triggered - if ((checked == 4000) && (trigger_threshold > 0)) { + if ((checked >= 4000) && trigger_hit == false) { if (data_available()) { checked = -1; break; @@ -298,20 +301,22 @@ 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; - // Testpoint 8 (TP8) can be used to trigger oscilliscope + // 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_threshold > 0) && (sample < (trigger_threshold + 128)) && (sample > (128 - trigger_threshold))) { - if (cancel_after > 0) { - cancel_counter++; - if (cancel_after == cancel_counter) - break; + if (trigger_hit == false) { + if ((trigger_threshold > 0) && (sample < (trigger_threshold + 128)) && (sample > (128 - trigger_threshold))) { + if (cancel_after > 0) { + cancel_counter++; + if (cancel_after == cancel_counter) + break; + } + continue; } - continue; } - trigger_threshold = 0; + trigger_hit = true; if (samples_to_skip > 0) { samples_to_skip--; @@ -323,19 +328,20 @@ uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, in if (samples.total_saved >= sample_size) break; } } - - if (checked == -1 && verbose) { - Dbprintf("lf sampling aborted"); - } if (verbose) { + if (checked == -1) { + Dbprintf("lf sampling aborted"); + } else if ((cancel_counter == cancel_after) && (cancel_after > 0)) { + Dbprintf("lf sampling cancelled after %u", cancel_counter); + } + Dbprintf("Done, saved " _YELLOW_("%d")" out of " _YELLOW_("%d")" seen samples at " _YELLOW_("%d")" bits/sample", samples.total_saved, samples.counter, bits_per_sample); } // Ensure that DC offset removal and noise check is performed for any device-side processing removeSignalOffset(data.buffer, samples.total_saved); computeSignalProperties(data.buffer, samples.total_saved); - return data.numbits; } /** @@ -356,20 +362,28 @@ uint32_t DoAcquisition_config(bool verbose, uint32_t sample_size) { , config.trigger_threshold , verbose , sample_size - , 0 + , 0 // cancel_after , config.samples_to_skip); } uint32_t DoPartialAcquisition(int trigger_threshold, bool verbose, uint32_t sample_size, uint32_t cancel_after) { - return DoAcquisition(1, 8, 0, trigger_threshold, verbose, sample_size, cancel_after, 0); + return DoAcquisition(config.decimation + , config.bits_per_sample + , config.averaging + , trigger_threshold + , verbose + , sample_size + , cancel_after + , 0); // samples to skip } static uint32_t ReadLF(bool reader_field, bool verbose, uint32_t sample_size) { if (verbose) - printConfig(); + printLFConfig(); LFSetupFPGAForADC(config.divisor, reader_field); uint32_t ret = DoAcquisition_config(verbose, sample_size); + StopTicks(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return ret; } @@ -386,9 +400,9 @@ uint32_t SampleLF(bool verbose, uint32_t sample_size) { * Initializes the FPGA for sniffer-mode (field off), and acquires the samples. * @return number of bits sampled **/ -uint32_t SniffLF(void) { +uint32_t SniffLF(bool verbose, uint32_t sample_size) { BigBuf_Clear_ext(false); - return ReadLF(false, true, 0); + return ReadLF(false, verbose, sample_size); } /** @@ -414,14 +428,14 @@ void doT55x7Acquisition(size_t sample_size) { bool lowFound = false; uint16_t checker = 0; - + if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("doT55x7Acquisition - after init"); print_stack_usage(); } while (skipCnt < 1000 && (i < bufsize)) { - + if (BUTTON_PRESS()) break; @@ -479,139 +493,102 @@ void doT55x7Acquisition(size_t sample_size) { **/ #define COTAG_T1 384 -#define COTAG_T2 (COTAG_T1>>1) -#define COTAG_ONE_THRESHOLD 128+10 -#define COTAG_ZERO_THRESHOLD 128-10 +#define COTAG_T2 (COTAG_T1 >> 1) +#define COTAG_ONE_THRESHOLD 127+5 +#define COTAG_ZERO_THRESHOLD 127-5 #ifndef COTAG_BITS #define COTAG_BITS 264 #endif -void doCotagAcquisition(size_t sample_size) { +void doCotagAcquisition(void) { - uint8_t *dest = BigBuf_get_addr(); - uint16_t bufsize = MIN(sample_size, BigBuf_max_traceLen()); + uint16_t bufsize = BigBuf_max_traceLen(); + uint8_t *dest = BigBuf_malloc(bufsize); dest[0] = 0; - uint8_t firsthigh = 0, firstlow = 0; - uint16_t i = 0, noise_counter = 0, checker = 0; - - if (DBGLEVEL >= DBG_DEBUG) { - Dbprintf("doCotagAcquisition - after init"); - print_stack_usage(); - } - while ((i < bufsize) && (noise_counter < (COTAG_T1 << 1))) { - + bool firsthigh = false, firstlow = false; + uint16_t i = 0, noise_counter = 0; + + while ((i < bufsize - 1) && (noise_counter < COTAG_T1 << 1)) { + if (BUTTON_PRESS()) break; - - if (checker == 4000) { - if (data_available()) - break; - else - checker = 0; - } else { - ++checker; - } 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; // find first peak - if (!firsthigh) { + if (firsthigh == false) { if (sample < COTAG_ONE_THRESHOLD) { noise_counter++; continue; } + noise_counter = 0; - firsthigh = 1; + firsthigh = true; } - if (!firstlow) { + + if (firstlow == false) { if (sample > COTAG_ZERO_THRESHOLD) { noise_counter++; continue; } + noise_counter = 0; - firstlow = 1; + firstlow = true; } ++i; - - if (sample > COTAG_ONE_THRESHOLD) + if (sample > COTAG_ONE_THRESHOLD) { dest[i] = 255; - else if (sample < COTAG_ZERO_THRESHOLD) + } else if (sample < COTAG_ZERO_THRESHOLD) { dest[i] = 0; - else + } else { dest[i] = dest[i - 1]; + } } } // Ensure that DC offset removal and noise check is performed for any device-side processing - removeSignalOffset(dest, bufsize); - computeSignalProperties(dest, bufsize); + removeSignalOffset(dest, i); + computeSignalProperties(dest, i); } -uint32_t doCotagAcquisitionManchester(void) { +uint16_t doCotagAcquisitionManchester(uint8_t *dest, uint16_t destlen) { - uint8_t *dest = BigBuf_get_addr(); - uint16_t bufsize = MIN(COTAG_BITS, BigBuf_max_traceLen()); + if (dest == NULL) + return 0; dest[0] = 0; - uint8_t firsthigh = 0, firstlow = 0; + + bool firsthigh = false, firstlow = false; uint8_t curr = 0, prev = 0; - uint16_t sample_counter = 0, period = 0; - uint16_t noise_counter = 0, checker = 0; - - if (DBGLEVEL >= DBG_DEBUG) { - Dbprintf("doCotagAcquisitionManchester - after init"); - print_stack_usage(); - } - - while ((sample_counter < bufsize) && (noise_counter < (COTAG_T1 << 1))) { - - if (BUTTON_PRESS()) - break; - - if (checker == 4000) { - if ( data_available()) - break; - else - checker = 0; - } else { - ++checker; - } + uint16_t i = 0; + uint16_t period = 0; + + while ((i < destlen) && BUTTON_PRESS() == false) { 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; // find first peak - if (!firsthigh) { + if (firsthigh == false) { if (sample < COTAG_ONE_THRESHOLD) { - noise_counter++; continue; } - noise_counter = 0; - firsthigh = 1; + firsthigh = true; } - if (!firstlow) { + if (firstlow == false) { if (sample > COTAG_ZERO_THRESHOLD) { - noise_counter++; continue; } - noise_counter = 0; - firstlow = 1; + firstlow = true; } // set sample 255, 0, or previous @@ -631,10 +608,11 @@ uint32_t doCotagAcquisitionManchester(void) { continue; } - dest[sample_counter] = curr; - ++sample_counter; + dest[i] = curr; + ++i; period = COTAG_T1; } } - return sample_counter; + + return i; } diff --git a/armsrc/lfsampling.h b/armsrc/lfsampling.h index 9b8c4c6b1..d10f8279e 100644 --- a/armsrc/lfsampling.h +++ b/armsrc/lfsampling.h @@ -21,8 +21,8 @@ typedef struct { * acquisition of Cotag LF signal. Similar to other LF, since the Cotag has such long datarate RF/384 * and is Manchester?, we directly gather the manchester data into bigbuff **/ -void doCotagAcquisition(size_t sample_size); -uint32_t doCotagAcquisitionManchester(void); +void doCotagAcquisition(void); +uint16_t doCotagAcquisitionManchester(uint8_t *dest, uint16_t destlen); /** * acquisition of T55x7 LF signal. Similar to other LF, but adjusted with @marshmellows thresholds @@ -40,7 +40,7 @@ uint32_t SampleLF(bool verbose, uint32_t sample_size); * Initializes the FPGA for sniff-mode (field off), and acquires the samples. * @return number of bits sampled **/ -uint32_t SniffLF(void); +uint32_t SniffLF(bool verbose, uint32_t sample_size); uint32_t DoAcquisition(uint8_t decimation, uint8_t bits_per_sample, bool avg, int16_t trigger_threshold, bool verbose, uint32_t sample_size, uint32_t cancel_after, int32_t samples_to_skip); @@ -99,7 +99,7 @@ void setSamplingConfig(sample_config *sc); sample_config *getSamplingConfig(void); -void printConfig(void); +void printLFConfig(void); void printSamples(void); #endif // __LFSAMPLING_H diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 9e19cbc53..f442166f4 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -34,6 +34,7 @@ #include "ticks.h" #include "usb_cdc.h" // usb_poll_validate_length #include "spiffs.h" // spiffs +#include "appmain.h" // print_stack_usage #ifndef HARDNESTED_AUTHENTICATION_TIMEOUT # define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) @@ -369,7 +370,7 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) countblocks *= 4; - reply_mix(CMD_ACK, 1, countblocks, BigBuf_max_traceLen(), 0, 0); + reply_mix(CMD_ACK, 1, countblocks, dataout - BigBuf_get_addr(), 0, 0); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); BigBuf_free(); @@ -443,47 +444,6 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { set_tracing(false); } -/* // Command not needed but left for future testing -void MifareUWriteBlockCompat(uint8_t arg0, uint8_t *datain) -{ - uint8_t blockNo = arg0; - uint8_t blockdata[16] = {0x00}; - - memcpy(blockdata, datain, 16); - - uint8_t uid[10] = {0x00}; - - LED_A_ON(); LED_B_OFF(); LED_C_OFF(); - - clear_trace(); - set_tracing(true); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - if(!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); - OnError(0); - return; - }; - - if(mifare_ultra_writeblock_compat(blockNo, blockdata)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error"); - OnError(0); - return; }; - - if(mifare_ultra_halt()) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error"); - OnError(0); - return; - }; - - if (DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); - - reply_mix(CMD_ACK,1,0,0,0,0); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); -} -*/ - // Arg0 : Block to write to. // Arg1 : 0 = use no authentication. // 1 = use 0x1A authentication. @@ -553,6 +513,75 @@ void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { set_tracing(false); } +// Arg0 : Block to write to. +// Arg1 : 0 = use no authentication. +// 1 = use 0x1A authentication. +// 2 = use 0x1B authentication. +// datain : 16 first bytes is data to be written. +// : 4/16 next bytes is authentication key. +void MifareUWriteBlockCompat(uint8_t arg0, uint8_t arg1, uint8_t *datain) { + uint8_t blockNo = arg0; + bool useKey = (arg1 == 1); //UL_C + bool usePwd = (arg1 == 2); //UL_EV1/NTAG + uint8_t blockdata[16] = {0x00}; + + memcpy(blockdata, datain, 16); + + LEDsoff(); + LED_A_ON(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); + OnError(0); + return; + }; + + // UL-C authentication + if (useKey) { + uint8_t key[16] = {0x00}; + memcpy(key, datain + 16, sizeof(key)); + + if (!mifare_ultra_auth(key)) { + OnError(1); + return; + } + } + + // UL-EV1 / NTAG authentication + if (usePwd) { + uint8_t pwd[4] = {0x00}; + memcpy(pwd, datain + 16, 4); + uint8_t pack[4] = {0, 0, 0, 0}; + if (!mifare_ul_ev1_auth(pwd, pack)) { + OnError(1); + return; + } + } + + if (mifare_ultra_writeblock_compat(blockNo, blockdata)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error"); + OnError(0); + return; + }; + + if (mifare_ultra_halt()) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error"); + OnError(0); + return; + }; + + if (DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); + + reply_mix(CMD_ACK, 1, 0, 0, 0, 0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); +} + void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { uint8_t pwd[16] = {0x00}; @@ -905,7 +934,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 set_tracing(true); // statistics on nonce distance - int16_t isOK = 0; + int16_t isOK = PM3_SUCCESS; #define NESTED_MAX_TRIES 12 if (calibrate) { // calibrate: for first call only. Otherwise reuse previous calibration LED_B_ON(); @@ -921,7 +950,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 // Test if the action was cancelled if (BUTTON_PRESS() || data_available()) { - isOK = -2; + isOK = PM3_EOPABORTED; break; } @@ -976,7 +1005,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 } else { unsuccessful_tries++; if (unsuccessful_tries > NESTED_MAX_TRIES) { // card isn't vulnerable to nested attack (random numbers are not predictable) - isOK = -3; + isOK = PM3_EFAILED; } } } @@ -1002,7 +1031,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 // Test if the action was cancelled if (BUTTON_PRESS() || data_available()) { - isOK = -2; + isOK = PM3_EOPABORTED; break; } @@ -1091,12 +1120,7 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 memcpy(payload.nt_b, &target_nt[1], 4); memcpy(payload.ks_b, &target_ks[1], 4); - LED_B_ON(); reply_ng(CMD_HF_MIFARE_NESTED, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); - LED_B_OFF(); - - if (DBGLEVEL >= 3) DbpString("NESTED FINISHED"); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); set_tracing(false); @@ -1108,13 +1132,10 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint64_t ui64Key = 0; ui64Key = bytes_to_num(key, 6); - - // variables uint16_t len; - uint8_t uid[10] = {0x00}; uint32_t cuid = 0, nt1, nt2; - uint32_t target_nt = {0x00}, target_ks = {0x00}; + uint32_t target_nt = 0, target_ks = 0; uint8_t par[1] = {0x00}; uint8_t receivedAnswer[10] = {0x00}; @@ -1132,7 +1153,6 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, set_tracing(true); int16_t isOK = 0; - LED_C_ON(); for (uint8_t retry = 0; retry < 3 && (isOK == 0); retry++) { @@ -1152,7 +1172,7 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, continue; }; - // First authenticatoin. Normal auth. + // First authentication. Normal auth. if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) { if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth1 error"); retry--; @@ -1167,9 +1187,8 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, }; nt2 = bytes_to_num(receivedAnswer, 4); - uint32_t nt_tmp = prng_successor(nt1, 160); - target_ks = nt2 ^ nt_tmp; - target_nt = nt_tmp; + target_nt = prng_successor(nt1, 160); + target_ks = nt2 ^ target_nt; isOK = 1; if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Testing nt1=%08x nt2enc=%08x nt2par=%02x ks=%08x", nt1, nt2, par[0], target_ks); @@ -1644,7 +1663,7 @@ OUT: bar |= ((uint16_t)(found[m] & 1) << j++); } - uint8_t *tmp = BigBuf_malloc(480 + 10); + uint8_t *tmp = BigBuf_malloc(480 + 10); memcpy(tmp, k_sector, sectorcnt * sizeof(sector_t)); num_to_bytes(foo, 8, tmp + 480); tmp[488] = bar & 0xFF; @@ -1689,7 +1708,7 @@ OUT: DBGLEVEL = oldbg; } -void MifareChkKeys(uint8_t *datain) { +void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -1715,7 +1734,12 @@ void MifareChkKeys(uint8_t *datain) { bool clearTrace = datain[2]; uint16_t key_count = (datain[3] << 8) | datain[4]; - uint16_t key_mem_available = MIN((PM3_CMD_DATA_SIZE - 5), key_count * 6); + uint16_t key_mem_available; + if (reserved_mem) + key_mem_available = key_count * 6; + else + key_mem_available = MIN((PM3_CMD_DATA_SIZE - 5), key_count * 6); + key_count = key_mem_available / 6; datain += 5; @@ -1793,6 +1817,8 @@ void MifareChkKeys(uint8_t *datain) { void MifareChkKeys_file(uint8_t *fn) { #ifdef WITH_FLASH + BigBuf_free(); + SpinOff(0); int changed = rdv40_spiffs_lazy_mount(); @@ -1807,7 +1833,7 @@ void MifareChkKeys_file(uint8_t *fn) { SpinOff(0); - MifareChkKeys(mem); + MifareChkKeys(mem, true); BigBuf_free(); #endif @@ -1952,29 +1978,35 @@ int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { uint64_t ui64Key = emlGetKey(sectorNo, keytype); if (sectorNo == 0) { if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keytype, ui64Key, AUTH_FIRST)) { - retval = PM3_ESOFT; + retval = PM3_EPARTIAL; if (DBGLEVEL > DBG_ERROR) Dbprintf("Sector[%2d]. Auth error", sectorNo); - goto out; + continue; } } else { if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keytype, ui64Key, AUTH_NESTED)) { - retval = PM3_ESOFT; + retval = PM3_EPARTIAL; if (DBGLEVEL > DBG_ERROR) Dbprintf("Sector[%2d]. Auth nested error", sectorNo); - goto out; + continue; } } for (uint8_t blockNo = 0; blockNo < NumBlocksPerSector(sectorNo); blockNo++) { if (mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf)) { - retval = PM3_ESOFT; + retval = PM3_EPARTIAL; + if (DBGLEVEL > DBG_ERROR) Dbprintf("Error reading sector %2d block %2d", sectorNo, blockNo); - break; + continue; } + + 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); + memcpy(dataoutbuf2 + 6, dataoutbuf + 6, 4); emlSetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1); } } @@ -2059,12 +2091,19 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { break; } + uint32_t old_timeout = iso14a_get_timeout(); + + // 2000 ms timeout + // 13560000 / 1000 / (8 * 16) * timeout + iso14a_set_timeout(21190); + ReaderTransmit(wipeC, sizeof(wipeC), NULL); if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { if (DBGLEVEL >= DBG_ERROR) Dbprintf("wipeC error"); errormsg = MAGIC_WIPE; break; } + iso14a_set_timeout(old_timeout); mifare_classic_halt_ex(NULL); } @@ -2197,12 +2236,14 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { OnSuccessMagic(); } -void MifareCIdent(void) { +void MifareCIdent(bool is_mfc) { // 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 *par = BigBuf_malloc(MAX_PARITY_SIZE); uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); uint8_t *uid = BigBuf_malloc(10); @@ -2237,13 +2278,79 @@ void MifareCIdent(void) { ReaderTransmit(rats, sizeof(rats), NULL); res = ReaderReceive(buf, par); + // test for some MFC gen2 if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10\xF0\x05", 11) == 0) { + + // super card ident + uint8_t super[] = {0x0A, 0x00, 0x00, 0xA6, 0xB0, 0x00, 0x10, 0x14, 0x1D}; + ReaderTransmit(super, sizeof(super), NULL); + res = ReaderReceive(buf, par); + if (res == 22) { + isGen = MAGIC_SUPER; + goto OUT; + } + 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; } + // 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; + } + + if (! is_mfc) { + // magic ntag test + 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(rdblf0, sizeof(rdblf0), NULL); + res = ReaderReceive(buf, par); + if (res == 18) { + isGen = MAGIC_NTAG21X; + } + } + } + if (is_mfc) { + // magic MFC Gen3 test + 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; + } + } + } }; OUT: @@ -2258,29 +2365,29 @@ OUT: void MifareHasStaticNonce(void) { // variables - int retval = PM3_SUCCESS, len; - - uint32_t nt = 0 ; - uint8_t rec[1] = {0x00}; - uint8_t recpar[1] = {0x00}; + int retval = PM3_SUCCESS; + uint32_t nt = 0; uint8_t *uid = BigBuf_malloc(10); - uint8_t data[1] = {0x00}; - + uint8_t data[1] = { NONCE_FAIL }; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; - iso14a_card_select_t card_info; iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - for (int i = 0; i < 3; i++) { + 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)) { retval = PM3_ESOFT; goto OUT; } + uint8_t rec[1] = {0x00}; + uint8_t recpar[1] = {0x00}; // Transmit MIFARE_CLASSIC_AUTH 0x60, block 0 - len = mifare_sendcmd_short(pcs, false, MIFARE_AUTH_KEYA, 0, rec, recpar, NULL); + int len = mifare_sendcmd_short(pcs, false, MIFARE_AUTH_KEYA, 0, rec, recpar, NULL); if (len != 4) { retval = PM3_ESOFT; goto OUT; @@ -2288,14 +2395,24 @@ void MifareHasStaticNonce(void) { // Save the tag nonce (nt) if (nt == bytes_to_num(rec, 4)) { - data[0]++; + counter++; } nt = bytes_to_num(rec, 4); + // some cards with static nonce need to be reset before next query + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); CHK_TIMEOUT(); } + if (counter) { + Dbprintf("%u static nonce %08x", data[0], nt); + data[0] = NONCE_STATIC; + } else { + data[0] = NONCE_NORMAL; + } + OUT: reply_ng(CMD_HF_MIFARE_STATIC_NONCE, retval, data, sizeof(data)); // turns off @@ -2315,6 +2432,154 @@ void OnErrorMagic(uint8_t reason) { OnSuccessMagic(); } +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); + + LED_B_ON(); + uint32_t save_iso14a_timeout = iso14a_get_timeout(); + iso14a_set_timeout(13560000 / 1000 / (8 * 16) * 2000); // 2 seconds timeout + + ReaderTransmit(cmd, cmd_len, NULL); + int res = ReaderReceive(buf, par); + if (res == 4 && memcmp(buf, "\x90\x00\xfd\x07", 4) == 0) { + // timeout for card memory reset + SpinDelay(1000); + } else { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card operation not completed"); + retval = PM3_ESOFT; + } + 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)); + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + if (!iso14443a_select_card(old_uid, card_info, NULL, true, 0, true)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected"); + retval = PM3_ESOFT; + goto OUT; + } + if (card_info->uidlen != uidlen) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Wrong UID length"); + retval = PM3_ESOFT; + goto OUT; + } + + memcpy(cmd, uid_cmd, sizeof(uid_cmd)); + memcpy(&cmd[sizeof(uid_cmd)], uid, uidlen); + AddCrc14A(cmd, sizeof(uid_cmd) + uidlen); + + retval = DoGen3Cmd(cmd, sizeof(uid_cmd) + uidlen + 2); + +OUT: + reply_ng(CMD_HF_MIFARE_GEN3UID, retval, old_uid, uidlen); + // turns off + OnSuccessMagic(); + BigBuf_free(); +} + +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)); + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + if (!iso14443a_select_card(uid, card_info, NULL, true, 0, true)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected"); + retval = PM3_ESOFT; + goto OUT; + } + + bool doReselect = false; + if (block_len < MIFARE_BLOCK_SIZE) { + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, 0, &cmd[sizeof(block_cmd)], NULL, NULL) != MAX_MIFARE_FRAME_SIZE)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Read manufacturer block failed"); + retval = PM3_ESOFT; + goto OUT; + } + doReselect = true; + } + + if (block_len > 0) { + memcpy(cmd, block_cmd, sizeof(block_cmd)); + memcpy(&cmd[sizeof(block_cmd)], block, block_len); + int ofs = sizeof(block_cmd); + if (card_info->uidlen == 4) { + cmd[ofs + 4] = cmd[ofs + 0] ^ cmd[ofs + 1] ^ cmd[ofs + 2] ^ cmd[ofs + 3]; + ofs += 5; + } else if (card_info->uidlen == 7) { + ofs += 7; + } else { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Wrong Card UID length"); + retval = PM3_ESOFT; + goto OUT; + } + cmd[ofs++] = card_info->sak; + cmd[ofs++] = card_info->atqa[0]; + cmd[ofs++] = card_info->atqa[1]; + AddCrc14A(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE); + + if (doReselect) { + if (!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected"); + retval = PM3_ESOFT; + goto OUT; + } + } + + retval = DoGen3Cmd(cmd, sizeof(block_cmd) + MAX_MIFARE_FRAME_SIZE); + } + +OUT: + reply_ng(CMD_HF_MIFARE_GEN3BLK, retval, &cmd[sizeof(block_cmd)], MIFARE_BLOCK_SIZE); + // turns off + OnSuccessMagic(); + BigBuf_free(); +} + +void MifareGen3Freez(void) { + int retval = PM3_SUCCESS; + uint8_t freeze_cmd[7] = { 0x90, 0xfd, 0x11, 0x11, 0x00, 0xe7, 0x91 }; + uint8_t *uid = BigBuf_malloc(10); + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + if (!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Card not selected"); + retval = PM3_ESOFT; + goto OUT; + } + + retval = DoGen3Cmd(freeze_cmd, sizeof(freeze_cmd)); + +OUT: + reply_ng(CMD_HF_MIFARE_GEN3FREEZ, retval, NULL, 0); + // turns off + OnSuccessMagic(); + BigBuf_free(); +} + void MifareSetMod(uint8_t *datain) { uint8_t mod = datain[0]; @@ -2430,32 +2695,30 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain) { // // Tear-off attack against MFU. // - Moebius et al -void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain) { +void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t tearoff_time, uint8_t *datain) { uint8_t blockNo = arg0; - uint32_t tearOffTime = arg1; uint8_t data_fullwrite[4] = {0x00}; uint8_t data_testwrite[4] = {0x00}; memcpy(data_fullwrite, datain, 4); memcpy(data_testwrite, datain + 4, 4); -// optional authentication before? - if (DBGLEVEL >= DBG_ERROR) DbpString("Preparing OTP tear-off"); + if (DBGLEVEL >= DBG_DEBUG) DbpString("Preparing OTP tear-off"); + + if (tearoff_time > 43000) + tearoff_time = 43000; + + MifareUWriteBlock(blockNo, 0, data_fullwrite); LEDsoff(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); - StartTicks(); - // write cmd to send, include CRC // 1b write, 1b block, 4b data, 2 crc uint8_t cmd[] = {MIFARE_ULC_WRITE, blockNo, data_testwrite[0], data_testwrite[1], data_testwrite[2], data_testwrite[3], 0, 0}; - - MifareUWriteBlock(blockNo, 0, data_fullwrite); - AddCrc14A(cmd, sizeof(cmd) - 2); - if (DBGLEVEL >= DBG_ERROR) DbpString("Transmitting"); + // anticollision / select card if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); @@ -2467,11 +2730,51 @@ void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain) { // Wait before cutting power. aka tear-off LED_D_ON(); - WaitUS(tearOffTime); + + SpinDelayUsPrecision(tearoff_time); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf(_YELLOW_("OTP tear-off triggered!")); switch_off(); reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_SUCCESS, NULL, 0); - StopTicks(); - - if (DBGLEVEL >= DBG_ERROR) DbpString("Done"); +} + +// +// Tear-off attack against MFU counter +void MifareU_Counter_Tearoff(uint8_t counter, uint32_t tearoff_time) { + + if (tearoff_time > 43000) + tearoff_time = 43000; + + LEDsoff(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + // Send MFU counter increase cmd + uint8_t cmd[] = { + MIFARE_ULEV1_INCR_CNT, + counter, + 0, // lsb + 0, + 0, // msb + 0, // rfu + 0, + 0, + }; + AddCrc14A(cmd, sizeof(cmd) - 2); + + // anticollision / select card + if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); + OnError(1); + return; + }; + + // send + ReaderTransmit(cmd, sizeof(cmd), NULL); + LED_D_ON(); + SpinDelayUsPrecision(tearoff_time); + switch_off(); + + reply_ng(CMD_HF_MFU_COUNTER_TEAROFF, PM3_SUCCESS, NULL, 0); } diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 2d9a4799a..f83f119e2 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -20,7 +20,7 @@ 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 MifareUWriteBlockCompat(uint8_t arg0,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); @@ -29,7 +29,7 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain); void MifareAcquireNonces(uint32_t arg0, uint32_t flags); -void MifareChkKeys(uint8_t *datain); +void MifareChkKeys(uint8_t *datain, uint8_t reserved_mem); void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareChkKeys_file(uint8_t *fn); @@ -41,9 +41,14 @@ int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype); 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(void); // is "magic chinese" card? +void MifareCIdent(bool is_mfc); // is "magic chinese" card? void MifareHasStaticNonce(void); // Has the tag a static nonce? +int DoGen3Cmd(uint8_t *cmd, uint8_t cmd_len); +void MifareGen3UID(uint8_t uidlen, uint8_t *uid); // Gen 3 magic card set UID without manufacturer block +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 + void MifareSetMod(uint8_t *datain); void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key); @@ -59,5 +64,5 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain); // Tear-off test for MFU void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain); - +void MifareU_Counter_Tearoff(uint8_t counter, uint32_t tearoff_time); #endif diff --git a/armsrc/mifaredesfire.c b/armsrc/mifaredesfire.c index d578d026b..b6ca949c6 100644 --- a/armsrc/mifaredesfire.c +++ b/armsrc/mifaredesfire.c @@ -118,6 +118,7 @@ void MifareDesfireGetInformation(void) { struct p { uint8_t isOK; uint8_t uid[7]; + uint8_t uidlen; uint8_t versionHW[7]; uint8_t versionSW[7]; uint8_t details[14]; @@ -148,15 +149,9 @@ void MifareDesfireGetInformation(void) { return; } - if (card.uidlen != 7) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Wrong UID size. Expected 7byte got %d", card.uidlen); - payload.isOK = 2; // 2 == WRONG UID - reply_ng(CMD_HF_DESFIRE_INFO, PM3_ESOFT, (uint8_t *)&payload, sizeof(payload)); - switch_off(); - return; - } // add uid. - memcpy(payload.uid, card.uid, sizeof(payload.uid)); + memcpy(payload.uid, card.uid, card.uidlen); + payload.uidlen = card.uidlen; LED_A_ON(); uint8_t cmd[] = {0x90, MFDES_GET_VERSION, 0x00, 0x00, 0x00}; diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 60f9a7810..4eaf711b3 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -85,7 +85,6 @@ static bool IsTrailerAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t act } } - static bool IsDataAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { uint8_t sector_trailer[16]; @@ -243,7 +242,7 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ rSAK[0] = block0[7]; memcpy(rATQA, &block0[8], sizeof(rATQA)); } else { - Dbprintf("[-] ERROR: Invalid dump. UID/SAK/ATQA not found"); + Dbprintf("ERROR: " _RED_("Invalid dump. UID/SAK/ATQA not found")); return false; } } @@ -342,7 +341,7 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_ // Correct uid size bits in ATQA rATQA[0] = (rATQA[0] & 0x3f) | 0x80; // triple size uid } else { - Dbprintf("[-] ERROR: UID size not defined"); + Dbprintf("ERROR: " _RED_("UID size not defined")); return false; } if (flags & FLAG_FORCED_ATQA) { @@ -529,12 +528,22 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint1 LED_D_ON(); ResetSspClk(); + int counter = 0; bool finished = false; bool button_pushed = BUTTON_PRESS(); + while (!button_pushed && !finished) { - while (!button_pushed && !finished && !data_available()) { WDT_HIT(); + if (counter == 2000) { + if (data_available()) { + break; + } + counter = 0; + } else { + counter++; + } + // find reader field if (cardSTATE == MFEMUL_NOFIELD) { diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 71c57a963..5d8989ef8 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -203,14 +203,14 @@ int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockN iso14a_set_timeout(save_timeout); if (!len) { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Authentication failed. Card timeout."); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Authentication failed. Card timeout"); return 2; } ntpp = prng_successor(nt, 32) ^ crypto1_word(pcs, 0, 0); if (ntpp != bytes_to_num(receivedAnswer, 4)) { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Authentication failed. Error card response."); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Authentication failed. Error card response"); return 3; } return 0; @@ -225,18 +225,18 @@ int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blo len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); if (len == 1) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Cmd Error %02x", receivedAnswer[0]); return 1; } if (len != 18) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Cmd Error: wrong response len: %x (expected 18)", len); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("wrong response len %d (expected 18)", len); return 2; } memcpy(bt, receivedAnswer + 16, 2); AddCrc14A(receivedAnswer, 16); if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { - if (DBGLEVEL >= DBG_INFO) Dbprintf("Cmd CRC response error."); + if (DBGLEVEL >= DBG_INFO) Dbprintf("CRC response error"); return 3; } @@ -446,37 +446,37 @@ int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t bl return 0; } -/* // command not needed, but left for future testing int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) { - uint16_t len; - uint8_t par[3] = {0}; // enough for 18 parity bits - uint8_t d_block[18] = {0x00}; - uint8_t receivedAnswer[MAX_FRAME_SIZE]; - uint8_t receivedAnswerPar[MAX_PARITY_SIZE]; + // variables + uint16_t len = 0; + + uint8_t d_block[18]; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); - if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK + if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK if (DBGLEVEL >= DBG_ERROR) - Dbprintf("Cmd Addr Error: %02x", receivedAnswer[0]); + Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0], len); return 1; } memcpy(d_block, blockData, 16); AddCrc14A(d_block, 16); - ReaderTransmitPar(d_block, sizeof(d_block), par, NULL); + ReaderTransmit(d_block, sizeof(d_block), NULL); + // Receive the response len = ReaderReceive(receivedAnswer, receivedAnswerPar); - if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK + if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK if (DBGLEVEL >= DBG_ERROR) - Dbprintf("Cmd Data Error: %02x %d", receivedAnswer[0],len); + Dbprintf("Cmd Send Data Error: %02x %d", receivedAnswer[0], len); return 2; } return 0; } -*/ int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData) { uint16_t len = 0; @@ -613,7 +613,7 @@ void emlClearMem(void) { memset(emCARD, 0, CARD_MEMORY_SIZE); // fill sectors trailer data - for (uint16_t b = 3; b <= MIFARE_4K_MAXBLOCK; ((b <= MIFARE_2K_MAXBLOCK) ? (b += 4) : (b += 16))) + for (uint16_t b = 3; b < MIFARE_4K_MAXBLOCK; ((b < MIFARE_2K_MAXBLOCK - 4) ? (b += 4) : (b += 16))) emlSetMem((uint8_t *)trailer, b, 1); // uid diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index b1d6396d5..d444dc81f 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -73,7 +73,7 @@ int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t bl int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack); int mifare_ultra_auth(uint8_t *keybytes); int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData); -//int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData); +int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData); int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData); int mifare_ultra_halt(void); diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c index 0f762a8d6..98c2034e2 100644 --- a/armsrc/optimized_cipher.c +++ b/armsrc/optimized_cipher.c @@ -77,7 +77,15 @@ -- piwi 2019 **/ +/** + add the possibility to do iCLASS on device only + -- iceman 2020 +**/ + #include "optimized_cipher.h" +#include "optimized_elite.h" +#include "optimized_ikeys.h" +#include "optimized_cipherutils.h" static const uint8_t opt_select_LUT[256] = { 00, 03, 02, 01, 02, 03, 00, 01, 04, 07, 07, 04, 06, 07, 05, 04, @@ -241,11 +249,34 @@ static void opt_MAC(uint8_t *k, uint8_t *input, uint8_t *out) { opt_output(k, &_init, out); } +static void opt_MAC_N(uint8_t *k, uint8_t *input, uint8_t in_size, uint8_t *out) { + State _init = { + ((k[0] ^ 0x4c) + 0xEC) & 0xFF,// l + ((k[0] ^ 0x4c) + 0x21) & 0xFF,// r + 0x4c, // b + 0xE012 // t + }; + + opt_suc(k, &_init, input, in_size, false); + opt_output(k, &_init, out); +} + void opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) { uint8_t dest [] = {0, 0, 0, 0, 0, 0, 0, 0}; opt_MAC(div_key_p, cc_nr_p, dest); memcpy(mac, dest, 4); - return; +} + +void opt_doReaderMAC_2(State _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p) { + opt_suc(div_key_p, &_init, nr, 4, false); + opt_output(div_key_p, &_init, mac); +} + + +void doMAC_N(uint8_t *in_p, uint8_t in_size, uint8_t *div_key_p, uint8_t mac[4]) { + uint8_t dest [] = {0, 0, 0, 0, 0, 0, 0, 0}; + opt_MAC_N(div_key_p, in_p, in_size, dest); + memcpy(mac, dest, 4); } void opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]) { @@ -257,7 +288,6 @@ void opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]) { }; opt_suc(div_key_p, &_init, cc_p, 12, true); opt_output(div_key_p, &_init, mac); - return; } /** @@ -291,5 +321,24 @@ State opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p) { void opt_doTagMAC_2(State _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p) { opt_suc(div_key_p, &_init, nr, 4, true); opt_output(div_key_p, &_init, mac); - return; +} + + +void iclass_calc_div_key(uint8_t *csn, uint8_t *key, uint8_t *div_key, bool elite) { + if (elite) { + uint8_t keytable[128] = {0}; + uint8_t key_index[8] = {0}; + uint8_t key_sel[8] = { 0 }; + uint8_t key_sel_p[8] = { 0 }; + hash2(key, keytable); + hash1(csn, key_index); + for (uint8_t i = 0; i < 8 ; i++) + key_sel[i] = keytable[key_index[i]]; + + //Permute from iclass format to standard format + permutekey_rev(key_sel, key_sel_p); + diversifyKey(csn, key_sel_p, div_key); + } else { + diversifyKey(csn, key, div_key); + } } diff --git a/armsrc/optimized_cipher.h b/armsrc/optimized_cipher.h index c6df25ab8..587f6b7a6 100644 --- a/armsrc/optimized_cipher.h +++ b/armsrc/optimized_cipher.h @@ -21,6 +21,9 @@ typedef struct { /** The reader MAC is MAC(key, CC * NR ) **/ void opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]); + +void opt_doReaderMAC_2(State _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p); + /** * The tag MAC is MAC(key, CC * NR * 32x0)) */ @@ -46,4 +49,6 @@ State opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p); */ void opt_doTagMAC_2(State _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p); +void doMAC_N(uint8_t *in_p, uint8_t in_size, uint8_t *div_key_p, uint8_t mac[4]); +void iclass_calc_div_key(uint8_t *csn, uint8_t *key, uint8_t *div_key, bool elite); #endif // OPTIMIZED_CIPHER_H diff --git a/armsrc/optimized_cipherutils.c b/armsrc/optimized_cipherutils.c new file mode 100644 index 000000000..c51f83f9b --- /dev/null +++ b/armsrc/optimized_cipherutils.c @@ -0,0 +1,140 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ +#include "optimized_cipherutils.h" +#include + +/** + * + * @brief Return and remove the first bit (x0) in the stream : + * @param stream + * @return + */ +bool headBit(BitstreamIn *stream) { + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = (stream->position++) & 7; // mask out 00000111 + return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; +} +/** + * @brief Return and remove the last bit (xn) in the stream: + * @param stream + * @return + */ +bool tailBit(BitstreamIn *stream) { + int bitpos = stream->numbits - 1 - (stream->position++); + + int bytepos = bitpos >> 3; + bitpos &= 7; + return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1; +} +/** + * @brief Pushes bit onto the stream + * @param stream + * @param bit + */ +void pushBit(BitstreamOut *stream, bool bit) { + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = stream->position & 7; + *(stream->buffer + bytepos) |= (bit) << (7 - bitpos); + stream->position++; + stream->numbits++; +} + +/** + * @brief Pushes the lower six bits onto the stream + * as b0 b1 b2 b3 b4 b5 b6 + * @param stream + * @param bits + */ +void push6bits(BitstreamOut *stream, uint8_t bits) { + pushBit(stream, bits & 0x20); + pushBit(stream, bits & 0x10); + pushBit(stream, bits & 0x08); + pushBit(stream, bits & 0x04); + pushBit(stream, bits & 0x02); + pushBit(stream, bits & 0x01); +} + +/** + * @brief bitsLeft + * @param stream + * @return number of bits left in stream + */ +int bitsLeft(BitstreamIn *stream) { + return stream->numbits - stream->position; +} +/** + * @brief numBits + * @param stream + * @return Number of bits stored in stream + */ +void x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest) { + while (len--) { + dest[len] = (uint8_t) n; + n >>= 8; + } +} + +uint64_t x_bytes_to_num(uint8_t *src, size_t len) { + uint64_t num = 0; + while (len--) { + num = (num << 8) | (*src); + src++; + } + return num; +} + +uint8_t reversebytes(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; +} + +void reverse_arraybytes(uint8_t *arr, size_t len) { + uint8_t i; + for (i = 0; i < len ; i++) { + arr[i] = reversebytes(arr[i]); + } +} + +void reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len) { + uint8_t i; + for (i = 0; i < len ; i++) { + dest[i] = reversebytes(arr[i]); + } +} + diff --git a/armsrc/optimized_cipherutils.h b/armsrc/optimized_cipherutils.h new file mode 100644 index 000000000..63ba8b8aa --- /dev/null +++ b/armsrc/optimized_cipherutils.h @@ -0,0 +1,66 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ +#ifndef CIPHERUTILS_H +#define CIPHERUTILS_H +#include +#include +#include + +typedef struct { + uint8_t *buffer; + uint8_t numbits; + uint8_t position; +} BitstreamIn; + +typedef struct { + uint8_t *buffer; + uint8_t numbits; + uint8_t position; +} BitstreamOut; + +bool headBit(BitstreamIn *stream); +bool tailBit(BitstreamIn *stream); +void pushBit(BitstreamOut *stream, bool bit); +int bitsLeft(BitstreamIn *stream); + +void push6bits(BitstreamOut *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); +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/optimized_elite.c b/armsrc/optimized_elite.c new file mode 100644 index 000000000..c84dd58be --- /dev/null +++ b/armsrc/optimized_elite.c @@ -0,0 +1,238 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + * + ****************************************************************************/ +#include "optimized_elite.h" + +#include +#include +#include +#include "mbedtls/des.h" +#include "optimized_ikeys.h" + +/** + * @brief Permutes a key from standard NIST format to Iclass specific format + * from http://www.proxmark.org/forum/viewtopic.php?pid=11220#p11220 + * + * If you permute [6c 8d 44 f9 2a 2d 01 bf] you get [8a 0d b9 88 bb a7 90 ea] as shown below. + * + * 1 0 1 1 1 1 1 1 bf + * 0 0 0 0 0 0 0 1 01 + * 0 0 1 0 1 1 0 1 2d + * 0 0 1 0 1 0 1 0 2a + * 1 1 1 1 1 0 0 1 f9 + * 0 1 0 0 0 1 0 0 44 + * 1 0 0 0 1 1 0 1 8d + * 0 1 1 0 1 1 0 0 6c + * + * 8 0 b 8 b a 9 e + * a d 9 8 b 7 0 a + * + * @param key + * @param dest + */ +void permutekey(uint8_t key[8], uint8_t dest[8]) { + int i; + for (i = 0 ; i < 8 ; i++) { + dest[i] = (((key[7] & (0x80 >> i)) >> (7 - i)) << 7) | + (((key[6] & (0x80 >> i)) >> (7 - i)) << 6) | + (((key[5] & (0x80 >> i)) >> (7 - i)) << 5) | + (((key[4] & (0x80 >> i)) >> (7 - i)) << 4) | + (((key[3] & (0x80 >> i)) >> (7 - i)) << 3) | + (((key[2] & (0x80 >> i)) >> (7 - i)) << 2) | + (((key[1] & (0x80 >> i)) >> (7 - i)) << 1) | + (((key[0] & (0x80 >> i)) >> (7 - i)) << 0); + } +} +/** + * Permutes a key from iclass specific format to NIST format + * @brief permutekey_rev + * @param key + * @param dest + */ +void permutekey_rev(uint8_t key[8], uint8_t dest[8]) { + int i; + for (i = 0 ; i < 8 ; i++) { + dest[7 - i] = (((key[0] & (0x80 >> i)) >> (7 - i)) << 7) | + (((key[1] & (0x80 >> i)) >> (7 - i)) << 6) | + (((key[2] & (0x80 >> i)) >> (7 - i)) << 5) | + (((key[3] & (0x80 >> i)) >> (7 - i)) << 4) | + (((key[4] & (0x80 >> i)) >> (7 - i)) << 3) | + (((key[5] & (0x80 >> i)) >> (7 - i)) << 2) | + (((key[6] & (0x80 >> i)) >> (7 - i)) << 1) | + (((key[7] & (0x80 >> i)) >> (7 - i)) << 0); + } +} + +/** + * Helper function for hash1 + * @brief rr + * @param val + * @return + */ +static uint8_t rr(uint8_t val) { + return val >> 1 | ((val & 1) << 7); +} + +/** + * Helper function for hash1 + * @brief rl + * @param val + * @return + */ +static uint8_t rl(uint8_t val) { + return val << 1 | ((val & 0x80) >> 7); +} + +/** + * Helper function for hash1 + * @brief swap + * @param val + * @return + */ +static uint8_t swap(uint8_t val) { + return ((val >> 4) & 0xFF) | ((val & 0xFF) << 4); +} + +/** + * Hash1 takes CSN as input, and determines what bytes in the keytable will be used + * when constructing the K_sel. + * @param csn the CSN used + * @param k output + */ +void hash1(uint8_t csn[], uint8_t k[]) { + k[0] = csn[0] ^ csn[1] ^ csn[2] ^ csn[3] ^ csn[4] ^ csn[5] ^ csn[6] ^ csn[7]; + k[1] = csn[0] + csn[1] + csn[2] + csn[3] + csn[4] + csn[5] + csn[6] + csn[7]; + k[2] = rr(swap(csn[2] + k[1])); + k[3] = rl(swap(csn[3] + k[0])); + k[4] = ~rr(csn[4] + k[2]) + 1; + k[5] = ~rl(csn[5] + k[3]) + 1; + k[6] = rr(csn[6] + (k[4] ^ 0x3c)); + k[7] = rl(csn[7] + (k[5] ^ 0xc3)); + + k[7] &= 0x7F; + k[6] &= 0x7F; + k[5] &= 0x7F; + k[4] &= 0x7F; + k[3] &= 0x7F; + k[2] &= 0x7F; + k[1] &= 0x7F; + k[0] &= 0x7F; +} +/** +Definition 14. Define the rotate key function rk : (F 82 ) 8 × N → (F 82 ) 8 as +rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] +rk(x [0] . . . x [7] , n + 1) = rk(rl(x [0] ) . . . rl(x [7] ), n) +**/ +static void rk(uint8_t *key, uint8_t n, uint8_t *outp_key) { + memcpy(outp_key, key, 8); + uint8_t j; + while (n-- > 0) { + for (j = 0; j < 8 ; j++) + outp_key[j] = rl(outp_key[j]); + } + return; +} + +static mbedtls_des_context ctx_enc; +static mbedtls_des_context ctx_dec; + +static void desdecrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) { + uint8_t key_std_format[8] = {0}; + permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_dec(&ctx_dec, key_std_format); + mbedtls_des_crypt_ecb(&ctx_dec, input, output); +} + +static void desencrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) { + uint8_t key_std_format[8] = {0}; + permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_enc(&ctx_enc, key_std_format); + mbedtls_des_crypt_ecb(&ctx_enc, input, output); +} + +/** + * @brief Insert uint8_t[8] custom master key to calculate hash2 and return key_select. + * @param key unpermuted custom key + * @param hash1 hash1 + * @param key_sel output key_sel=h[hash1[i]] + */ +void hash2(uint8_t *key64, uint8_t *outp_keytable) { + /** + *Expected: + * High Security Key Table + + 00 F1 35 59 A1 0D 5A 26 7F 18 60 0B 96 8A C0 25 C1 + 10 BF A1 3B B0 FF 85 28 75 F2 1F C6 8F 0E 74 8F 21 + 20 14 7A 55 16 C8 A9 7D B3 13 0C 5D C9 31 8D A9 B2 + 30 A3 56 83 0F 55 7E DE 45 71 21 D2 6D C1 57 1C 9C + 40 78 2F 64 51 42 7B 64 30 FA 26 51 76 D3 E0 FB B6 + 50 31 9F BF 2F 7E 4F 94 B4 BD 4F 75 91 E3 1B EB 42 + 60 3F 88 6F B8 6C 2C 93 0D 69 2C D5 20 3C C1 61 95 + 70 43 08 A0 2F FE B3 26 D7 98 0B 34 7B 47 70 A0 AB + + **** The 64-bit HS Custom Key Value = 5B7C62C491C11B39 ******/ + uint8_t key64_negated[8] = {0}; + uint8_t z[8][8] = {{0}, {0}}; + uint8_t temp_output[8] = {0}; + + //calculate complement of key + int i; + for (i = 0; i < 8; i++) + key64_negated[i] = ~key64[i]; + + // Once again, key is on iclass-format + desencrypt_iclass(key64, key64_negated, z[0]); + + uint8_t y[8][8] = {{0}, {0}}; + + // y[0]=DES_dec(z[0],~key) + // Once again, key is on iclass-format + desdecrypt_iclass(z[0], key64_negated, y[0]); + + for (i = 1; i < 8; i++) { + rk(key64, i, temp_output); + desdecrypt_iclass(temp_output, z[i - 1], z[i]); + desencrypt_iclass(temp_output, y[i - 1], y[i]); + } + + if (outp_keytable != NULL) { + for (i = 0 ; i < 8 ; i++) { + memcpy(outp_keytable + i * 16, y[i], 8); + memcpy(outp_keytable + 8 + i * 16, z[i], 8); + } + } +} diff --git a/armsrc/optimized_elite.h b/armsrc/optimized_elite.h new file mode 100644 index 000000000..281ecf0bb --- /dev/null +++ b/armsrc/optimized_elite.h @@ -0,0 +1,62 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ + + +#ifndef ELITE_CRACK_H +#define ELITE_CRACK_H + +#include +#include + +void permutekey(uint8_t key[8], uint8_t dest[8]); +/** + * Permutes a key from iclass specific format to NIST format + * @brief permutekey_rev + * @param key + * @param dest + */ +void permutekey_rev(uint8_t key[8], uint8_t dest[8]); +/** + * Hash1 takes CSN as input, and determines what bytes in the keytable will be used + * when constructing the K_sel. + * @param csn the CSN used + * @param k output + */ +void hash1(uint8_t *csn, uint8_t *k); +void hash2(uint8_t *key64, uint8_t *outp_keytable); + +#endif diff --git a/armsrc/optimized_ikeys.c b/armsrc/optimized_ikeys.c new file mode 100644 index 000000000..eeb00e562 --- /dev/null +++ b/armsrc/optimized_ikeys.c @@ -0,0 +1,324 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ + +/** +From "Dismantling iclass": + This section describes in detail the built-in key diversification algorithm of iClass. + Besides the obvious purpose of deriving a card key from a master key, this + algorithm intends to circumvent weaknesses in the cipher by preventing the + usage of certain ‘weak’ keys. In order to compute a diversified key, the iClass + reader first encrypts the card identity id with the master key K, using single + DES. The resulting ciphertext is then input to a function called hash0 which + outputs the diversified key k. + + k = hash0(DES enc (id, K)) + + Here the DES encryption of id with master key K outputs a cryptogram c + of 64 bits. These 64 bits are divided as c = x, y, z [0] , . . . , z [7] ∈ F 82 × F 82 × (F 62 ) 8 + which is used as input to the hash0 function. This function introduces some + obfuscation by performing a number of permutations, complement and modulo + operations, see Figure 2.5. Besides that, it checks for and removes patterns like + similar key bytes, which could produce a strong bias in the cipher. Finally, the + output of hash0 is the diversified card key k = k [0] , . . . , k [7] ∈ (F 82 ) 8 . + +**/ +#include "optimized_ikeys.h" + +#include +#include +#include +#include "mbedtls/des.h" +#include "optimized_cipherutils.h" + +uint8_t pi[35] = { + 0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, + 0x2E, 0x33, 0x35, 0x39, 0x36, 0x3A, 0x3C, 0x47, + 0x4B, 0x4D, 0x4E, 0x53, 0x55, 0x56, 0x59, 0x5A, + 0x5C, 0x63, 0x65, 0x66, 0x69, 0x6A, 0x6C, 0x71, + 0x72, 0x74, 0x78 +}; + +static mbedtls_des_context ctx_enc; + +/** + * @brief The key diversification algorithm uses 6-bit bytes. + * This implementation uses 64 bit uint to pack seven of them into one + * variable. When they are there, they are placed as follows: + * XXXX XXXX N0 .... N7, occupying the last 48 bits. + * + * This function picks out one from such a collection + * @param all + * @param n bitnumber + * @return + */ +static uint8_t getSixBitByte(uint64_t c, int n) { + return (c >> (42 - 6 * n)) & 0x3F; +} + +/** + * @brief Puts back a six-bit 'byte' into a uint64_t. + * @param c buffer + * @param z the value to place there + * @param n bitnumber. + */ +static void pushbackSixBitByte(uint64_t *c, uint8_t z, int n) { + //0x XXXX YYYY ZZZZ ZZZZ ZZZZ + // ^z0 ^z7 + //z0: 1111 1100 0000 0000 + + uint64_t masked = z & 0x3F; + uint64_t eraser = 0x3F; + masked <<= 42 - 6 * n; + eraser <<= 42 - 6 * n; + + //masked <<= 6*n; + //eraser <<= 6*n; + + eraser = ~eraser; + (*c) &= eraser; + (*c) |= masked; + +} +/** + * @brief Swaps the z-values. + * If the input value has format XYZ0Z1...Z7, the output will have the format + * XYZ7Z6...Z0 instead + * @param c + * @return + */ +static uint64_t swapZvalues(uint64_t c) { + uint64_t newz = 0; + pushbackSixBitByte(&newz, getSixBitByte(c, 0), 7); + pushbackSixBitByte(&newz, getSixBitByte(c, 1), 6); + pushbackSixBitByte(&newz, getSixBitByte(c, 2), 5); + pushbackSixBitByte(&newz, getSixBitByte(c, 3), 4); + pushbackSixBitByte(&newz, getSixBitByte(c, 4), 3); + pushbackSixBitByte(&newz, getSixBitByte(c, 5), 2); + pushbackSixBitByte(&newz, getSixBitByte(c, 6), 1); + pushbackSixBitByte(&newz, getSixBitByte(c, 7), 0); + newz |= (c & 0xFFFF000000000000); + return newz; +} + +/** +* @return 4 six-bit bytes chunked into a uint64_t,as 00..00a0a1a2a3 +*/ +static uint64_t ck(int i, int j, uint64_t z) { + if (i == 1 && j == -1) { + // ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + return z; + } else if (j == -1) { + // ck(i, −1, z [0] . . . z [3] ) = ck(i − 1, i − 2, z [0] . . . z [3] ) + return ck(i - 1, i - 2, z); + } + + if (getSixBitByte(z, i) == getSixBitByte(z, j)) { + //ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ) + uint64_t newz = 0; + int c; + for (c = 0; c < 4; c++) { + uint8_t val = getSixBitByte(z, c); + if (c == i) + pushbackSixBitByte(&newz, j, c); + else + pushbackSixBitByte(&newz, val, c); + } + return ck(i, j - 1, newz); + } else { + return ck(i, j - 1, z); + } +} +/** + + Definition 8. + Let the function check : (F 62 ) 8 → (F 62 ) 8 be defined as + check(z [0] . . . z [7] ) = ck(3, 2, z [0] . . . z [3] ) · ck(3, 2, z [4] . . . z [7] ) + + where ck : N × N × (F 62 ) 4 → (F 62 ) 4 is defined as + + ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + ck(i, −1, z [0] . . . z [3] ) = ck(i − 1, i − 2, z [0] . . . z [3] ) + ck(i, j, z [0] . . . z [3] ) = + ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ), if z [i] = z [j] ; + ck(i, j − 1, z [0] . . . z [3] ), otherwise + + otherwise. +**/ + +static uint64_t check(uint64_t z) { + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + + // ck(3, 2, z [0] . . . z [3] ) + uint64_t ck1 = ck(3, 2, z); + + // ck(3, 2, z [4] . . . z [7] ) + uint64_t ck2 = ck(3, 2, z << 24); + + //The ck function will place the values + // in the middle of z. + ck1 &= 0x00000000FFFFFF000000; + ck2 &= 0x00000000FFFFFF000000; + + return ck1 | ck2 >> 24; +} + +static void permute(BitstreamIn *p_in, uint64_t z, int l, int r, BitstreamOut *out) { + if (bitsLeft(p_in) == 0) + return; + + bool pn = tailBit(p_in); + if (pn) { // pn = 1 + uint8_t zl = getSixBitByte(z, l); + + push6bits(out, zl + 1); + permute(p_in, z, l + 1, r, out); + } else { // otherwise + uint8_t zr = getSixBitByte(z, r); + + push6bits(out, zr); + permute(p_in, z, l, r + 1, out); + } +} + +/** + * @brief + *Definition 11. Let the function hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where + * z'[i] = (z[i] mod (63-i)) + i i = 0...3 + * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 + * ẑ = check(z'); + * @param c + * @param k this is where the diversified key is put (should be 8 bytes) + * @return + */ +void hash0(uint64_t c, uint8_t k[8]) { + c = swapZvalues(c); + + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + // x = 8 bits + // y = 8 bits + // z0-z7 6 bits each : 48 bits + uint8_t x = (c & 0xFF00000000000000) >> 56; + uint8_t y = (c & 0x00FF000000000000) >> 48; + uint64_t zP = 0; + + for (int n = 0; n < 4 ; n++) { + uint8_t zn = getSixBitByte(c, n); + uint8_t zn4 = getSixBitByte(c, n + 4); + uint8_t _zn = (zn % (63 - n)) + n; + uint8_t _zn4 = (zn4 % (64 - n)) + n; + pushbackSixBitByte(&zP, _zn, n); + pushbackSixBitByte(&zP, _zn4, n + 4); + } + + uint64_t zCaret = check(zP); + uint8_t p = pi[x % 35]; + + if (x & 1) //Check if x7 is 1 + p = ~p; + + BitstreamIn p_in = { &p, 8, 0 }; + uint8_t outbuffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; + BitstreamOut out = {outbuffer, 0, 0}; + permute(&p_in, zCaret, 0, 4, &out); //returns 48 bits? or 6 8-bytes + + //Out is now a buffer containing six-bit bytes, should be 48 bits + // if all went well + //Shift z-values down onto the lower segment + + uint64_t zTilde = x_bytes_to_num(outbuffer, sizeof(outbuffer)); + + zTilde >>= 16; + + for (int i = 0; i < 8; i++) { + // the key on index i is first a bit from y + // then six bits from z, + // then a bit from p + + // Init with zeroes + k[i] = 0; + // First, place yi leftmost in k + //k[i] |= (y << i) & 0x80 ; + + // First, place y(7-i) leftmost in k + k[i] |= (y << (7 - i)) & 0x80 ; + + uint8_t zTilde_i = getSixBitByte(zTilde, i); + // zTildeI is now on the form 00XXXXXX + // with one leftshift, it'll be + // 0XXXXXX0 + // So after leftshift, we can OR it into k + // However, when doing complement, we need to + // again MASK 0XXXXXX0 (0x7E) + zTilde_i <<= 1; + + //Finally, add bit from p or p-mod + //Shift bit i into rightmost location (mask only after complement) + uint8_t p_i = p >> i & 0x1; + + if (k[i]) { // yi = 1 + k[i] |= ~zTilde_i & 0x7E; + k[i] |= p_i & 1; + k[i] += 1; + + } else { // otherwise + k[i] |= zTilde_i & 0x7E; + k[i] |= (~p_i) & 1; + } + } +} +/** + * @brief Performs Elite-class key diversification + * @param csn + * @param key + * @param div_key + */ +void diversifyKey(uint8_t *csn, uint8_t *key, uint8_t *div_key) { + // Prepare the DES key + mbedtls_des_setkey_enc(&ctx_enc, key); + + uint8_t crypted_csn[8] = {0}; + + // Calculate DES(CSN, KEY) + mbedtls_des_crypt_ecb(&ctx_enc, csn, crypted_csn); + + //Calculate HASH0(DES)) + uint64_t c_csn = x_bytes_to_num(crypted_csn, sizeof(crypted_csn)); + + hash0(c_csn, div_key); +} + diff --git a/armsrc/optimized_ikeys.h b/armsrc/optimized_ikeys.h new file mode 100644 index 000000000..91fa406ad --- /dev/null +++ b/armsrc/optimized_ikeys.h @@ -0,0 +1,69 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file 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. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ + +#ifndef IKEYS_H +#define IKEYS_H + +#include + +/** + * @brief + *Definition 11. Let the function hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where + * z'[i] = (z[i] mod (63-i)) + i i = 0...3 + * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 + * ẑ = check(z'); + * @param c + * @param k this is where the diversified key is put (should be 8 bytes) + * @return + */ +void hash0(uint64_t c, uint8_t k[8]); +/** + * @brief Performs Elite-class key diversification + * @param csn + * @param key + * @param div_key + */ + +void diversifyKey(uint8_t csn[8], uint8_t key[8], uint8_t div_key[8]); +/** + * @brief Permutes a key from standard NIST format to Iclass specific format + * @param key + * @param dest + */ + +#endif // IKEYS_H diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index 025146b55..a1b3e8e39 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -67,30 +67,33 @@ static s32_t rdv40_spiffs_llwrite(u32_t addr, u32_t size, u8_t *src) { } static s32_t rdv40_spiffs_llerase(u32_t addr, u32_t size) { - - uint8_t erased = 0; if (!FlashInit()) { return 130; } - if (DBGLEVEL > 2) Dbprintf("LLERASEDBG : Orig addr : %d\n", addr); + + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("LLERASEDBG : Orig addr : %d\n", addr); + uint8_t block, sector = 0; block = addr / RDV40_LLERASE_BLOCKSIZE; if (block) { addr = addr - (block * RDV40_LLERASE_BLOCKSIZE); } - if (DBGLEVEL > 2) Dbprintf("LLERASEDBG : Result addr : %d\n", addr); + + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("LLERASEDBG : Result addr : %d\n", addr); + sector = addr / SPIFFS_CFG_LOG_BLOCK_SZ; Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); - if (DBGLEVEL > 2) Dbprintf("LLERASEDBG : block : %d, sector : %d \n", block, sector); - erased = Flash_Erase4k(block, sector); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("LLERASEDBG : block : %d, sector : %d \n", block, sector); + + erased = Flash_Erase4k(block, sector); Flash_CheckBusy(BUSY_TIMEOUT); FlashStop(); - return SPIFFS_OK == erased ; + return (SPIFFS_OK == erased); } //////////////////////////////////////////////////////////////////////////////// @@ -252,7 +255,7 @@ static RDV40SpiFFSFileType filetype_in_spiffs(const char *filename) { filetype = RDV40_SPIFFS_FILETYPE_SYMLINK; } } - if (DBGLEVEL > 1) { + if (DBGLEVEL >= DBG_DEBUG) { switch (filetype) { case RDV40_SPIFFS_FILETYPE_REAL: Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_REAL"); @@ -472,14 +475,21 @@ int rdv40_spiffs_is_symlink(const char *s) { // 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) { - RDV40_SPIFFS_SAFE_FUNCTION( // - char linkdest[SPIFFS_OBJ_NAME_LEN]; // - char linkfilename[SPIFFS_OBJ_NAME_LEN]; // + + RDV40_SPIFFS_SAFE_FUNCTION( + char linkdest[SPIFFS_OBJ_NAME_LEN]; + char linkfilename[SPIFFS_OBJ_NAME_LEN]; sprintf(linkfilename, "%s.lnk", filename); - if (DBGLEVEL > 1) Dbprintf("Linkk real filename is destination is : %s", linkfilename); + + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("Linkk real filename is : " _YELLOW_("%s"), linkfilename); + read_from_spiffs((char *)linkfilename, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); - if (DBGLEVEL > 1) Dbprintf("Symlink destination is : %s", linkdest); - read_from_spiffs((char *)linkdest, (uint8_t *)dst, size); // + + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("Symlink destination is : " _YELLOW_("%s"), linkdest); + + read_from_spiffs((char *)linkdest, (uint8_t *)dst, size); ) } @@ -496,10 +506,10 @@ int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RD // 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) { - RDV40_SPIFFS_SAFE_FUNCTION( // - char linkfilename[SPIFFS_OBJ_NAME_LEN]; // + 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((char *)linkfilename, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); ) } @@ -510,8 +520,8 @@ int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyL // preexistance, 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) { - RDV40_SPIFFS_SAFE_FUNCTION( // - RDV40SpiFFSFileType filetype = filetype_in_spiffs((char *)filename); // + 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); @@ -523,7 +533,7 @@ int rdv40_spiffs_read_as_filetype(char *filename, uint8_t *dst, uint32_t size, R case RDV40_SPIFFS_FILETYPE_UNKNOWN: default: ; - } // + } ) } @@ -552,7 +562,7 @@ void rdv40_spiffs_safe_print_fsinfo(void) { Dbprintf(" Max Path Length............" _YELLOW_("%d")" chars", fsinfo.maxPathLength); DbpString(""); Dbprintf(" filesystem size used available use% mounted"); - Dbprintf(" spiffs %6d B %6d B %6d B"_YELLOW_("%2d%")" /" + Dbprintf(" spiffs %6d B %6d B %6d B " _YELLOW_("%2d%")" /" , fsinfo.totalBytes , fsinfo.usedBytes , fsinfo.freeBytes @@ -601,6 +611,37 @@ void rdv40_spiffs_safe_print_tree(uint8_t banner) { rdv40_spiffs_lazy_mount_rollback(changed); } +void rdv40_spiffs_safe_wipe(void) { + + int changed = rdv40_spiffs_lazy_mount(); + + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + SPIFFS_opendir(&fs, "/", &d); + + while ((pe = SPIFFS_readdir(&d, pe))) { + + if (rdv40_spiffs_is_symlink((const char *)pe->name)) { + + char linkdest[SPIFFS_OBJ_NAME_LEN]; + read_from_spiffs((char *)pe->name, (uint8_t *)linkdest, SPIFFS_OBJ_NAME_LEN); + + remove_from_spiffs(linkdest); + Dbprintf(".lnk removed %s", pe->name); + + remove_from_spiffs((char *)pe->name); + Dbprintf("removed %s", linkdest); + + } else { + remove_from_spiffs((char *)pe->name); + Dbprintf("removed %s", pe->name); + } + } + + SPIFFS_closedir(&d); + rdv40_spiffs_lazy_mount_rollback(changed); +} // Selftest function void test_spiffs(void) { diff --git a/armsrc/spiffs.h b/armsrc/spiffs.h index 5a9e58a94..8754cb432 100644 --- a/armsrc/spiffs.h +++ b/armsrc/spiffs.h @@ -58,6 +58,8 @@ int rdv40_spiffs_stat(char *filename, uint32_t *buf, RDV40SpiFFSSafetyLevel leve uint32_t size_in_spiffs(const char *filename); int exists_in_spiffs(const char *filename); +void rdv40_spiffs_safe_wipe(void); + #define SPIFFS_OK 0 #define SPIFFS_ERR_NOT_MOUNTED -10000 #define SPIFFS_ERR_FULL -10001 diff --git a/armsrc/spiffs_config.h b/armsrc/spiffs_config.h index 0cf3b96f5..85997903b 100644 --- a/armsrc/spiffs_config.h +++ b/armsrc/spiffs_config.h @@ -58,8 +58,6 @@ typedef uint8_t u8_t; #define SPIFFS_API_DBGF(str) SPIFFS_API_DBG(str,NULL) #endif - - // Defines spiffs debug print formatters // some general signed number #ifndef _SPIPRIi diff --git a/armsrc/thinfilm.c b/armsrc/thinfilm.c index a77a70744..dc99bf4c7 100644 --- a/armsrc/thinfilm.c +++ b/armsrc/thinfilm.c @@ -30,7 +30,6 @@ void ReadThinFilm(void) { clear_trace(); - set_tracing(true); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -60,15 +59,19 @@ static uint16_t ReadReaderField(void) { } static void CodeThinfilmAsTag(const uint8_t *cmd, uint16_t len) { - ToSendReset(); + + tosend_reset(); + + tosend_t *ts = get_tosend(); + for (uint16_t i = 0; i < len; i++) { uint8_t b = cmd[i]; for (uint8_t j = 0; j < 8; j++) { - ToSend[++ToSendMax] = (b & 0x80) ? SEC_D : SEC_E; + ts->buf[++ts->max] = (b & 0x80) ? SEC_D : SEC_E; b <<= 1; } } - ToSendMax++; + ts->max++; } static int EmSendCmdThinfilmRaw(uint8_t *resp, uint16_t respLen) { @@ -122,7 +125,7 @@ void SimulateThinFilm(uint8_t *data, size_t len) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port - FpgaSetupSsc(); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -132,6 +135,8 @@ void SimulateThinFilm(uint8_t *data, size_t len) { uint16_t hf_baseline = ReadReaderField(); + tosend_t *ts = get_tosend(); + // Start the timer StartCountSspClk(); @@ -147,7 +152,8 @@ void SimulateThinFilm(uint8_t *data, size_t len) { if (hf_av < hf_baseline) hf_baseline = hf_av; if (hf_av > hf_baseline + 10) { - EmSendCmdThinfilmRaw(ToSend, ToSendMax); + + EmSendCmdThinfilmRaw(ts->buf, ts->max); if (!reader_detected) { LED_B_ON(); //Dbprintf("Reader detected, start beaming data"); diff --git a/armsrc/ticks.c b/armsrc/ticks.c index 0ce55e417..9b563cc54 100644 --- a/armsrc/ticks.c +++ b/armsrc/ticks.c @@ -13,6 +13,31 @@ #include "proxmark3_arm.h" #include "dbprint.h" + +// timer counts in 666ns increments (32/48MHz), rounding applies +// WARNING: timer can't measure more than 43ms (666ns * 0xFFFF) +void SpinDelayUsPrecision(int us) { + int ticks = ((MCK / 1000000) * us + 16) >> 5; + + // Borrow a PWM unit for my real-time clock + AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); + + // 48 MHz / 32 gives 1.5 Mhz + AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(5); // Channel Mode Register + AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; // Channel Duty Cycle Register + AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xFFFF; // Channel Period Register + + uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + + for (;;) { + uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + if (now == (uint16_t)(start + ticks)) + return; + + WDT_HIT(); + } +} + // timer counts in 21.3us increments (1024/48MHz), rounding applies // WARNING: timer can't measure more than 1.39s (21.3us * 0xffff) void SpinDelayUs(int us) { @@ -117,47 +142,61 @@ uint32_t RAMFUNC GetCountUS(void) { void StartCountSspClk(void) { AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2); // Enable Clock to all timers AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_TIOA1 // XC0 Clock = TIOA1 - | AT91C_TCB_TC1XC1S_NONE // XC1 Clock = none - | AT91C_TCB_TC2XC2S_TIOA0; // XC2 Clock = TIOA0 + | AT91C_TCB_TC1XC1S_NONE // XC1 Clock = none + | AT91C_TCB_TC2XC2S_TIOA0; // XC2 Clock = TIOA0 // configure TC1 to create a short pulse on TIOA1 when a rising edge on TIOB1 (= ssp_clk from FPGA) occurs: AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // disable TC1 AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK // TC1 Clock = MCK(48MHz)/2 = 24MHz - | AT91C_TC_CPCSTOP // Stop clock on RC compare - | AT91C_TC_EEVTEDG_RISING // Trigger on rising edge of Event - | AT91C_TC_EEVT_TIOB // Event-Source: TIOB1 (= ssp_clk from FPGA = 13,56MHz/16) - | AT91C_TC_ENETRG // Enable external trigger event - | AT91C_TC_WAVESEL_UP // Upmode without automatic trigger on RC compare - | AT91C_TC_WAVE // Waveform Mode - | AT91C_TC_AEEVT_SET // Set TIOA1 on external event - | AT91C_TC_ACPC_CLEAR; // Clear TIOA1 on RC Compare - AT91C_BASE_TC1->TC_RC = 0x04; // RC Compare value = 0x04 + | AT91C_TC_CPCSTOP // Stop clock on RC compare + | AT91C_TC_EEVTEDG_RISING // Trigger on rising edge of Event + | AT91C_TC_EEVT_TIOB // Event-Source: TIOB1 (= ssp_clk from FPGA = 13,56MHz/16) + | AT91C_TC_ENETRG // Enable external trigger event + | AT91C_TC_WAVESEL_UP // Upmode without automatic trigger on RC compare + | AT91C_TC_WAVE // Waveform Mode + | AT91C_TC_AEEVT_SET // Set TIOA1 on external event + | AT91C_TC_ACPC_CLEAR; // Clear TIOA1 on RC Compare + AT91C_BASE_TC1->TC_RC = 0x01; // RC Compare value = 0x01, pulse width to TC0 // use TC0 to count TIOA1 pulses AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // disable TC0 AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_XC0 // TC0 clock = XC0 clock = TIOA1 - | AT91C_TC_WAVE // Waveform Mode - | AT91C_TC_WAVESEL_UP // just count - | AT91C_TC_ACPA_CLEAR // Clear TIOA0 on RA Compare - | AT91C_TC_ACPC_SET; // Set TIOA0 on RC Compare + | AT91C_TC_WAVE // Waveform Mode + | AT91C_TC_WAVESEL_UP // just count + | AT91C_TC_ACPA_CLEAR // Clear TIOA0 on RA Compare + | AT91C_TC_ACPC_SET; // Set TIOA0 on RC Compare AT91C_BASE_TC0->TC_RA = 1; // RA Compare value = 1; pulse width to TC2 AT91C_BASE_TC0->TC_RC = 0; // RC Compare value = 0; increment TC2 on overflow // use TC2 to count TIOA0 pulses (giving us a 32bit counter (TC0/TC2) clocked by ssp_clk) AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; // disable TC2 AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_XC2 // TC2 clock = XC2 clock = TIOA0 - | AT91C_TC_WAVE // Waveform Mode - | AT91C_TC_WAVESEL_UP; // just count + | AT91C_TC_WAVE // Waveform Mode + | AT91C_TC_WAVESEL_UP; // just count AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC0 AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC1 AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC2 + // // synchronize the counter with the ssp_frame signal. - // Note: FPGA must be in any iso14443 mode, otherwise the frame signal would not be present - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) + // Note: FPGA must be in a FPGA mode with SSC transfer, otherwise SSC_FRAME and SSC_CLK signals would not be present + // while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high + while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) + while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 1st ssp_clk after start of frame + while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; + while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 2nd ssp_clk after start of frame + if ((AT91C_BASE_SSC->SSC_RFMR & SSC_FRAME_MODE_BITS_IN_WORD(32)) == SSC_FRAME_MODE_BITS_IN_WORD(16)) { // 16bit frame + while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; + while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 3rd ssp_clk after start of frame + while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; + while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 4th ssp_clk after start of frame + while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; + while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 5th ssp_clk after start of frame + while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK); // wait for ssp_clk to go low; + while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high; 6th ssp_clk after start of frame + } // note: up to now two ssp_clk rising edges have passed since the rising edge of ssp_frame // it is now safe to assert a sync signal. This sets all timers to 0 on next active clock edge diff --git a/armsrc/ticks.h b/armsrc/ticks.h index 9d8d178d5..d26b05bbc 100644 --- a/armsrc/ticks.h +++ b/armsrc/ticks.h @@ -20,6 +20,7 @@ void SpinDelay(int ms); void SpinDelayUs(int us); +void SpinDelayUsPrecision(int us); // precision 0.6us , running for 43ms before void StartTickCount(void); uint32_t RAMFUNC GetTickCount(void); diff --git a/armsrc/usart.c b/armsrc/usart.c index 203572f90..b3ed42978 100644 --- a/armsrc/usart.c +++ b/armsrc/usart.c @@ -51,25 +51,32 @@ static size_t us_rxfifo_high = 0; static void usart_fill_rxfifo(void) { uint16_t rxfifo_free ; if (pUS1->US_RNCR == 0) { // One buffer got filled, backup buffer being used + if (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; + uint16_t available = USART_BUFFLEN - usart_cur_inbuf_off; + if (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)) us_rxfifo_high = 0; } + // Give next buffer pUS1->US_RNPR = (uint32_t)usart_cur_inbuf; pUS1->US_RNCR = USART_BUFFLEN; + // Swap current buff if (usart_cur_inbuf == us_inbuf1) usart_cur_inbuf = us_inbuf2; else usart_cur_inbuf = us_inbuf1; + usart_cur_inbuf_off = 0; } else { // Take only what we have room for @@ -83,12 +90,16 @@ static void usart_fill_rxfifo(void) { return; } } + 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; else rxfifo_free = sizeof(us_rxfifo) - us_rxfifo_high + us_rxfifo_low; + 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++) { @@ -150,7 +161,7 @@ uint32_t usart_read_ng(uint8_t *data, size_t len) { } // transfer from device to client -inline int usart_writebuffer_sync(uint8_t *data, size_t len) { +int usart_writebuffer_sync(uint8_t *data, size_t len) { // Wait for current PDC bank to be free // (and check next bank too, in case there will be a usart_writebuffer_async) diff --git a/armsrc/usart.h b/armsrc/usart.h index 5a7dea38f..abe495692 100644 --- a/armsrc/usart.h +++ b/armsrc/usart.h @@ -4,9 +4,6 @@ #include "common.h" #include "usart_defs.h" -#define USART_BUFFLEN 512 -#define USART_FIFOLEN (2*USART_BUFFLEN) - // Higher baudrates are pointless, only increasing overflow risk extern uint32_t g_usart_baudrate; diff --git a/armsrc/util.h b/armsrc/util.h index c6523a813..1d8586c43 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -63,7 +63,7 @@ #endif #ifndef REV64 -#define REV64(x) (REV32(x) + (REV32((x) >> 32) << 32)) +#define REV64(x) (REV32(x) + ((uint64_t)(REV32((x) >> 32) << 32))) #endif #ifndef BIT32 diff --git a/bootrom/Makefile b/bootrom/Makefile index 75a4e5ced..9a0359d2f 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -25,8 +25,8 @@ VERSIONSRC = version.c # stdint.h provided locally until GCC 4.5 becomes C99 compliant APP_CFLAGS = -I. -ffunction-sections -fdata-sections -# stack-protect , no-pie reduces size on Gentoo Hardened 8.2 gcc -APP_CFLAGS += -fno-stack-protector -fno-pie +# stack-protect , no-pie reduces size on Gentoo Hardened 8.2 gcc, no-common makes sure uninitalized vars doesn't end up in COMMON area +APP_CFLAGS += -fno-stack-protector -fno-pie -fno-common # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 565bfba14..e7262ba33 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -238,19 +238,23 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdhfmfhard.c ${PM3_ROOT}/client/src/cmdhfmfp.c ${PM3_ROOT}/client/src/cmdhfmfu.c + ${PM3_ROOT}/client/src/cmdhfst.c ${PM3_ROOT}/client/src/cmdhfthinfilm.c ${PM3_ROOT}/client/src/cmdhftopaz.c + ${PM3_ROOT}/client/src/cmdhfwaveshare.c ${PM3_ROOT}/client/src/cmdhw.c ${PM3_ROOT}/client/src/cmdlf.c ${PM3_ROOT}/client/src/cmdlfawid.c ${PM3_ROOT}/client/src/cmdlfcotag.c + ${PM3_ROOT}/client/src/cmdlfdestron.c ${PM3_ROOT}/client/src/cmdlfem4x.c ${PM3_ROOT}/client/src/cmdlfem4x50.c - ${PM3_ROOT}/client/src/cmdlffdx.c + ${PM3_ROOT}/client/src/cmdlffdxb.c ${PM3_ROOT}/client/src/cmdlfgallagher.c ${PM3_ROOT}/client/src/cmdlfguard.c ${PM3_ROOT}/client/src/cmdlfhid.c ${PM3_ROOT}/client/src/cmdlfhitag.c + ${PM3_ROOT}/client/src/cmdlfidteck.c ${PM3_ROOT}/client/src/cmdlfindala.c ${PM3_ROOT}/client/src/cmdlfio.c ${PM3_ROOT}/client/src/cmdlfjablotron.c @@ -267,7 +271,6 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdlfsecurakey.c ${PM3_ROOT}/client/src/cmdlft55xx.c ${PM3_ROOT}/client/src/cmdlfti.c - ${PM3_ROOT}/client/src/cmdlfverichip.c ${PM3_ROOT}/client/src/cmdlfviking.c ${PM3_ROOT}/client/src/cmdlfvisa2000.c ${PM3_ROOT}/client/src/cmdmain.c @@ -311,7 +314,7 @@ set(X86_CPUS x86 x86_64 i686) message(STATUS "CMAKE_SYSTEM_PROCESSOR := ${CMAKE_SYSTEM_PROCESSOR}") if (APPLE) - message("Apple device detected.") + 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") endif (APPLE) @@ -377,79 +380,79 @@ if (WHEREAMI_FOUND) set(ADDITIONAL_LNK ${WHEREAMI_LIBRARIES} ${ADDITIONAL_LNK}) endif (WHEREAMI_FOUND) -message("===================================================================") +message(STATUS "===================================================================") if (SKIPQT EQUAL 1) - message("GUI support: skipped") + message(STATUS "GUI support: skipped") else (SKIPQT EQUAL 1) if (Qt5_FOUND) - message("GUI support: QT5 found, enabled") + message(STATUS "GUI support: QT5 found, enabled") else (Qt5_FOUND) - message("GUI support: QT5 not found, disabled") + message(STATUS "GUI support: QT5 not found, disabled") endif (Qt5_FOUND) endif (SKIPQT EQUAL 1) if (SKIPBT EQUAL 1) - message("native BT support: skipped") + message(STATUS "native BT support: skipped") else (SKIPBT EQUAL 1) if (BLUEZ_FOUND) - message("native BT support: Bluez found, enabled") + message(STATUS "native BT support: Bluez found, enabled") else (BLUEZ_FOUND) - message("native BT support: Bluez not found, disabled") + message(STATUS "native BT support: Bluez not found, disabled") endif (BLUEZ_FOUND) endif(SKIPBT EQUAL 1) if (EMBED_BZIP2) - message("Bzip2 library: embedded") + message(STATUS "Bzip2 library: embedded") else (EMBED_BZIP2) - message("Bzip2 library: system library found") + message(STATUS "Bzip2 library: system library found") endif (EMBED_BZIP2) if (SKIPJANSSONSYSTEM EQUAL 1) - message("Jansson library: local library forced") + message(STATUS "Jansson library: local library forced") else (SKIPJANSSONSYSTEM EQUAL 1) if (JANSSON_FOUND) - message("Jansson library: system library found") + message(STATUS "Jansson library: system library found") else (JANSSON_FOUND) - message("Jansson library: system library not found, using local library") + message(STATUS "Jansson library: system library not found, using local library") endif (JANSSON_FOUND) endif (SKIPJANSSONSYSTEM EQUAL 1) if (SKIPPYTHON EQUAL 1) - message("Python3 library: skipped") + message(STATUS "Python3 library: skipped") else (SKIPPYTHON EQUAL 1) if (PYTHON3EMBED_FOUND) - message("Python3 library: Python3 embed found, enabled") + message(STATUS "Python3 library: Python3 embed found, enabled") elseif (PYTHON_FOUND) - message("Python3 library: Python3 found, enabled") + message(STATUS "Python3 library: Python3 found, enabled") else (PYTHON3EMBED_FOUND) - message("Python3 library: Python3 not found, disabled") + message(STATUS "Python3 library: Python3 not found, disabled") endif (PYTHON3EMBED_FOUND) endif(SKIPPYTHON EQUAL 1) if (SKIPREADLINE EQUAL 1) - message("Readline library: skipped") + message(STATUS "Readline library: skipped") else (SKIPREADLINE EQUAL 1) if (READLINE_FOUND) if (EMBED_READLINE) - message("Readline library: embedded") + message(STATUS "Readline library: embedded") else (EMBED_READLINE) - message("Readline library: system library found") + message(STATUS "Readline library: system library found") endif (EMBED_READLINE) else (READLINE_FOUND) - message("Readline library: Readline not found, disabled") + message(STATUS "Readline library: Readline not found, disabled") endif (READLINE_FOUND) endif(SKIPREADLINE EQUAL 1) if (SKIPWHEREAMISYSTEM EQUAL 1) - message("Whereami library: local library forced") + message(STATUS "Whereami library: local library forced") else (SKIPWHEREAMISYSTEM EQUAL 1) if (WHEREAMI_FOUND) - message("Whereami library: system library found") + message(STATUS "Whereami library: system library found") else (WHEREAMI_FOUND) - message("Whereami library: system library not found, using local library") + message(STATUS "Whereami library: system library not found, using local library") endif (WHEREAMI_FOUND) endif (SKIPWHEREAMISYSTEM EQUAL 1) -message("===================================================================") +message(STATUS "===================================================================") add_executable(proxmark3 ${PM3_ROOT}/client/src/proxmark3.c diff --git a/client/Makefile b/client/Makefile index a4e8b193d..27702a9d6 100644 --- a/client/Makefile +++ b/client/Makefile @@ -309,6 +309,7 @@ ifeq ($(QT_FOUND),1) endif endif +LDFLAGS ?= $(DEFLDFLAGS) PM3LDFLAGS = $(LDFLAGS) ifeq ($(platform),Darwin) PM3LDFLAGS += -framework Foundation -framework AppKit @@ -326,9 +327,9 @@ ifeq ($(SKIPQT),1) else ifeq ($(QT_FOUND),1) ifeq ($(QT5_FOUND),1) - $(info GUI support: QT5 found, enabled) + $(info GUI support: QT5 found, enabled ($(shell QT_SELECT=5 qmake -v 2>/dev/null|grep -o 'Qt version.*'))) else - $(info GUI support: QT4 found, enabled) + $(info GUI support: QT4 found, enabled ($(shell QT_SELECT=4 qmake -v 2>/dev/null|grep -o 'Qt version.*'))) endif else $(info GUI support: QT not found, disabled) @@ -432,19 +433,23 @@ SRCS = aidsearch.c \ cmdhfmfhard.c \ cmdhfmfu.c \ cmdhfmfp.c \ + cmdhfst.c \ cmdhfthinfilm.c \ cmdhftopaz.c \ + cmdhfwaveshare.c \ cmdhw.c \ cmdlf.c \ cmdlfawid.c \ cmdlfcotag.c \ + cmdlfdestron.c \ cmdlfem4x.c \ cmdlfem4x50.c \ - cmdlffdx.c \ + cmdlffdxb.c \ cmdlfguard.c \ cmdlfgallagher.c \ cmdlfhid.c \ cmdlfhitag.c \ + cmdlfidteck.c \ cmdlfindala.c \ cmdlfio.c \ cmdlfjablotron.c \ diff --git a/client/android/CMakeLists.txt b/client/android/CMakeLists.txt index cc827d143..b3f432c6c 100644 --- a/client/android/CMakeLists.txt +++ b/client/android/CMakeLists.txt @@ -117,19 +117,23 @@ add_library(pm3rrg_rdv4 SHARED ${PM3_ROOT}/client/src/cmdhfmfhard.c ${PM3_ROOT}/client/src/cmdhfmfp.c ${PM3_ROOT}/client/src/cmdhfmfu.c + ${PM3_ROOT}/client/src/cmdhfst.c ${PM3_ROOT}/client/src/cmdhfthinfilm.c ${PM3_ROOT}/client/src/cmdhftopaz.c + ${PM3_ROOT}/client/src/cmdhfwaveshare.c ${PM3_ROOT}/client/src/cmdhw.c ${PM3_ROOT}/client/src/cmdlf.c ${PM3_ROOT}/client/src/cmdlfawid.c ${PM3_ROOT}/client/src/cmdlfcotag.c + ${PM3_ROOT}/client/src/cmdlfdestron.c ${PM3_ROOT}/client/src/cmdlfem4x.c ${PM3_ROOT}/client/src/cmdlfem4x50.c - ${PM3_ROOT}/client/src/cmdlffdx.c + ${PM3_ROOT}/client/src/cmdlffdxb.c ${PM3_ROOT}/client/src/cmdlfgallagher.c ${PM3_ROOT}/client/src/cmdlfguard.c ${PM3_ROOT}/client/src/cmdlfhid.c ${PM3_ROOT}/client/src/cmdlfhitag.c + ${PM3_ROOT}/client/src/cmdlfidteck.c ${PM3_ROOT}/client/src/cmdlfindala.c ${PM3_ROOT}/client/src/cmdlfio.c ${PM3_ROOT}/client/src/cmdlfjablotron.c @@ -207,4 +211,4 @@ target_link_libraries(pm3rrg_rdv4 pm3rrg_rdv4_reveng pm3rrg_rdv4_whereami android - log) \ No newline at end of file + log) diff --git a/client/android/pm3_main.c b/client/android/pm3_main.c index fc4e0b2dc..0da684e01 100644 --- a/client/android/pm3_main.c +++ b/client/android/pm3_main.c @@ -86,7 +86,7 @@ jint Console(JNIEnv *env, jobject instance, jstring cmd_) { PrintAndLogEx(NORMAL, ""); - char *cmd = (char *) ((*env)->GetStringUTFChars(env, cmd_, 0)); + char *cmd = (char *)((*env)->GetStringUTFChars(env, cmd_, 0)); int ret = CommandReceived(cmd); if (ret == 99) { // exit / quit @@ -102,7 +102,7 @@ jint Console(JNIEnv *env, jobject instance, jstring cmd_) { * Is client running! * */ jboolean IsClientRunning(JNIEnv *env, jobject instance) { - return (jboolean) ((jboolean) conn.run); + return (jboolean)((jboolean) conn.run); } /* @@ -114,7 +114,7 @@ jboolean TestPm3(JNIEnv *env, jobject instance) { return false; } bool ret = (TestProxmark() == PM3_SUCCESS); - return (jboolean) (ret); + return (jboolean)(ret); } /* @@ -141,18 +141,18 @@ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { } jclass clz_test = (*jniEnv)->FindClass(jniEnv, "cn/rrg/devices/Proxmark3RRGRdv4"); JNINativeMethod methods[] = { - {"startExecute", "(Ljava/lang/String;)I", (void *) Console}, - {"stopExecute", "()V", (void *) ClosePm3}, - {"isExecuting", "()Z", (void *) IsClientRunning} + {"startExecute", "(Ljava/lang/String;)I", (void *) Console}, + {"stopExecute", "()V", (void *) ClosePm3}, + {"isExecuting", "()Z", (void *) IsClientRunning} }; JNINativeMethod methods1[] = { - {"testPm3", "()Z", (void *) TestPm3}, - {"closePm3", "()V", ClosePm3} + {"testPm3", "()Z", (void *) TestPm3}, + {"closePm3", "()V", ClosePm3} }; if ((*jniEnv)->RegisterNatives(jniEnv, clazz, methods, sizeof(methods) / sizeof(methods[0])) != - JNI_OK) { + JNI_OK) { return -1; } @@ -164,4 +164,4 @@ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { (*jniEnv)->DeleteLocalRef(jniEnv, clazz); (*jniEnv)->DeleteLocalRef(jniEnv, clz_test); return JNI_VERSION_1_4; -} \ No newline at end of file +} diff --git a/client/cmdscripts/rdv4_init_extflash.cmd b/client/cmdscripts/rdv4_init_extflash.cmd index 2cae05294..6fe6de3ba 100755 --- a/client/cmdscripts/rdv4_init_extflash.cmd +++ b/client/cmdscripts/rdv4_init_extflash.cmd @@ -1,6 +1,6 @@ #!/usr/bin/env -S pm3 -s -mem load f mfc_default_keys m -mem load f t55xx_default_pwds t -mem load f iclass_default_keys i +mem load -f mfc_default_keys --mfc +mem load -f t55xx_default_pwds --t55xx +mem load -f iclass_default_keys --iclass lf t55xx deviceconfig z p diff --git a/client/cmdscripts/test_psk_clone.cmd b/client/cmdscripts/test_psk_clone.cmd new file mode 100644 index 000000000..784fecd1e --- /dev/null +++ b/client/cmdscripts/test_psk_clone.cmd @@ -0,0 +1,28 @@ +clear + +rem Test of Motorola clone & read +lf t55xx wipe +lf motorola clone a0000000a0002021 +lf motorola read +lf search + +rem Test of Nexwatch clone & read +lf t55xx wipe +lf nexwatch clone c 1337 m 1 n +lf nexwatch read +lf search + +rem Test of keri clone & read +lf t55xx wipe +lf keri clone --id 1337 +lf keri read +lf search + +rem Test of Indala clone & read +lf t55xx wipe +lf indala clone --fc 7 --cn 1337 +lf indala read +lf search + +rem done, just wiping your tag. +lf t55xx wipe diff --git a/client/deps/cliparser/argtable3.c b/client/deps/cliparser/argtable3.c index f0b6a4d37..2fe32b082 100644 --- a/client/deps/cliparser/argtable3.c +++ b/client/deps/cliparser/argtable3.c @@ -1927,10 +1927,8 @@ struct arg_file *arg_filen( #include #include #include - #include "argtable3.h" - static void arg_int_resetfn(struct arg_int *parent) { ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); parent->count = 0; @@ -2226,6 +2224,263 @@ struct arg_int *arg_intn( ARG_TRACE(("arg_intn() returns %p\n", result)); return result; } + + +// uint64_t support +#include +#include +#include +static uint64_t strtollu0X(const char *str, + const char * *endptr, + char X, + int base) { + uint64_t val; /* stores result */ + int s = 1; /* sign is +1 or -1 */ + const char *ptr = str; /* ptr to current position in str */ + + /* skip leading whitespace */ + while (ISSPACE(*ptr)) + ptr++; + // printf("1) %s\n",ptr); + + /* scan optional sign character */ + switch (*ptr) { + case '+': + ptr++; + s = 1; + break; + case '-': + ptr++; + s = -1; + break; + default: + s = 1; + break; + } + // printf("2) %s\n",ptr); + + /* '0X' prefix */ + if ((*ptr++) != '0') { + /* printf("failed to detect '0'\n"); */ + *endptr = str; + return 0; + } + // printf("3) %s\n",ptr); + if (toupper(*ptr++) != toupper(X)) { + /* printf("failed to detect '%c'\n",X); */ + *endptr = str; + return 0; + } + // printf("4) %s\n",ptr); + + /* attempt conversion on remainder of string using strtol() */ + val = strtoull(ptr, (char * *)endptr, base); + + if (*endptr == ptr) { + /* conversion failed */ + *endptr = str; + return 0; + } + + /* success */ + return s * val; +} + + +static void arg_u64_resetfn(struct arg_u64 *parent) { + ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); + parent->count = 0; +} + +static int arg_u64_scanfn(struct arg_u64 *parent, const char *argval) { + int errorcode = 0; + if (parent->count == parent->hdr.maxcount) { + /* maximum number of arguments exceeded */ + errorcode = EMAXCOUNT; + } else if (!argval) { + /* a valid argument with no argument value was given. */ + /* This happens when an optional argument value was invoked. */ + /* leave parent arguiment value unaltered but still count the argument. */ + parent->count++; + } else { + uint64_t val; + const char *end; + + /* attempt to extract hex integer (eg: +0x123) from argval into val conversion */ + val = strtollu0X(argval, &end, 'X', 16); + if (end == argval) { + /* hex failed, attempt octal conversion (eg +0o123) */ + val = strtollu0X(argval, &end, 'O', 8); + if (end == argval) { + /* octal failed, attempt binary conversion (eg +0B101) */ + val = strtollu0X(argval, &end, 'B', 2); + if (end == argval) { + /* binary failed, attempt decimal conversion with no prefix (eg 1234) */ + val = strtoull(argval, (char * *)&end, 10); + if (end == argval) { + /* all supported number formats failed */ + return EBADINT; + } + } + } + } + + /* Safety check for integer overflow. WARNING: this check */ + /* achieves nothing on machines where size(int)==size(long). */ + if (val > ULLONG_MAX) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; +#else + errorcode = EOVERFLOW; +#endif + + /* Detect any suffixes (KB,MB,GB) and multiply argument value appropriately. */ + /* We need to be mindful of integer overflows when using such big numbers. */ + if (detectsuffix(end, "KB")) { /* kilobytes */ + if (val > (ULLONG_MAX / 1024)) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */ +#else + errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ +#endif + else + val *= 1024; /* 1KB = 1024 */ + } else if (detectsuffix(end, "MB")) { /* megabytes */ + if (val > (ULLONG_MAX / 1048576)) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */ +#else + errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ +#endif + else + val *= 1048576; /* 1MB = 1024*1024 */ + } else if (detectsuffix(end, "GB")) { /* gigabytes */ + if (val > (ULLONG_MAX / 1073741824)) +#ifdef __STDC_WANT_SECURE_LIB__ + errorcode = EOVERFLOW_; /* Overflow would occur if we proceed */ +#else + errorcode = EOVERFLOW; /* Overflow would occur if we proceed */ +#endif + else + val *= 1073741824; /* 1GB = 1024*1024*1024 */ + } else if (!detectsuffix(end, "")) + errorcode = EBADINT; /* invalid suffix detected */ + + /* if success then store result in parent->uval[] array */ + if (errorcode == 0) + parent->uval[parent->count++] = val; + } + + /* printf("%s:scanfn(%p,%p) returns %d\n",__FILE__,parent,argval,errorcode); */ + return errorcode; +} + +static int arg_u64_checkfn(struct arg_u64 *parent) { + int errorcode = (parent->count < parent->hdr.mincount) ? EMINCOUNT : 0; + /*printf("%s:checkfn(%p) returns %d\n",__FILE__,parent,errorcode);*/ + return errorcode; +} + +static void arg_u64_errorfn( + struct arg_u64 *parent, + FILE *fp, + int errorcode, + const char *argval, + const char *progname) { + const char *shortopts = parent->hdr.shortopts; + const char *longopts = parent->hdr.longopts; + const char *datatype = parent->hdr.datatype; + + /* make argval NULL safe */ + argval = argval ? argval : ""; + + fprintf(fp, "%s: ", progname); + switch (errorcode) { + case EMINCOUNT: + fputs("missing option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + + case EMAXCOUNT: + fputs("excess option ", fp); + arg_print_option(fp, shortopts, longopts, argval, "\n"); + break; + + case EBADINT: + fprintf(fp, "invalid argument \"%s\" to option ", argval); + arg_print_option(fp, shortopts, longopts, datatype, "\n"); + break; + +#ifdef __STDC_WANT_SECURE_LIB__ + case EOVERFLOW_: +#else + case EOVERFLOW: +#endif + fputs("integer overflow at option ", fp); + arg_print_option(fp, shortopts, longopts, datatype, " "); + fprintf(fp, "(%s is too large)\n", argval); + break; + } +} + +struct arg_u64 *arg_u64_0( + const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary) { + return arg_u64_n(shortopts, longopts, datatype, 0, 1, glossary); +} + +struct arg_u64 *arg_u64_1( + const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary) { + return arg_u64_n(shortopts, longopts, datatype, 1, 1, glossary); +} + +struct arg_u64 *arg_u64_n( + const char *shortopts, + const char *longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary) { + size_t nbytes; + struct arg_u64 *result; + + /* foolproof things by ensuring maxcount is not less than mincount */ + maxcount = (maxcount < mincount) ? mincount : maxcount; + + nbytes = sizeof(struct arg_u64) /* storage for struct arg_u64 */ + + maxcount * sizeof(uint64_t); /* storage for uval[maxcount] array */ + + result = (struct arg_u64 *)malloc(nbytes); + if (result) { + /* init the arg_hdr struct */ + result->hdr.flag = ARG_HASVALUE; + result->hdr.shortopts = shortopts; + result->hdr.longopts = longopts; + result->hdr.datatype = datatype ? datatype : ""; + result->hdr.glossary = glossary; + result->hdr.mincount = mincount; + result->hdr.maxcount = maxcount; + result->hdr.parent = result; + result->hdr.resetfn = (arg_resetfn *)arg_u64_resetfn; + result->hdr.scanfn = (arg_scanfn *)arg_u64_scanfn; + result->hdr.checkfn = (arg_checkfn *)arg_u64_checkfn; + result->hdr.errorfn = (arg_errorfn *)arg_u64_errorfn; + + /* store the uval[maxcount] array immediately after the arg_int struct */ + result->uval = (uint64_t *)(result + 1); + result->count = 0; + } + + ARG_TRACE(("arg_u64_n() returns %p\n", result)); + return result; +} + + /******************************************************************************* * This file is part of the argtable3 library. * @@ -4020,7 +4275,10 @@ static void arg_parse_untagged(int argc, /* register an error for each unused argv[] entry */ while (optind < argc) { /*printf("arg_parse_untagged(): argv[%d]=\"%s\" not consumed\n",optind,argv[optind]);*/ - arg_register_error(endtable, endtable, ARG_ENOMATCH, argv[optind++]); + if (argv[optind][0] != '\x00') { + arg_register_error(endtable, endtable, ARG_ENOMATCH, argv[optind]); + } + optind++; } return; @@ -4195,7 +4453,8 @@ static void arg_cat_option(char *dest, #endif if (datatype) { - arg_cat(&dest, "=", &ndest); +// arg_cat(&dest, "=", &ndest); + arg_cat(&dest, " ", &ndest); if (optvalue) { arg_cat(&dest, "[", &ndest); arg_cat(&dest, datatype, &ndest); @@ -4270,8 +4529,13 @@ static void arg_cat_optionv(char *dest, } if (datatype) { + /* if (longopts) + arg_cat(&dest, "=", &ndest); + else if (shortopts) + arg_cat(&dest, " ", &ndest); + */ if (longopts) - arg_cat(&dest, "=", &ndest); + arg_cat(&dest, " ", &ndest); else if (shortopts) arg_cat(&dest, " ", &ndest); diff --git a/client/deps/cliparser/argtable3.h b/client/deps/cliparser/argtable3.h index d8e3c53cc..55564aa2f 100644 --- a/client/deps/cliparser/argtable3.h +++ b/client/deps/cliparser/argtable3.h @@ -35,6 +35,7 @@ #include /* FILE */ #include /* struct tm */ +#include #ifdef __cplusplus extern "C" { @@ -110,6 +111,12 @@ struct arg_int { int *ival; /* Array of parsed argument values */ }; +struct arg_u64 { + struct arg_hdr hdr; /* The mandatory argtable header struct */ + int count; /* Number of matching command line args */ + uint64_t *uval; /* Array of parsed argument values */ +}; + struct arg_dbl { struct arg_hdr hdr; /* The mandatory argtable header struct */ int count; /* Number of matching command line args */ @@ -176,32 +183,29 @@ struct arg_lit *arg_litn(const char *shortopts, int maxcount, const char *glossary); -struct arg_key *arg_key0(const char *keyword, - int flags, - const char *glossary); -struct arg_key *arg_key1(const char *keyword, - int flags, - const char *glossary); -struct arg_key *arg_keyn(const char *keyword, - int flags, - int mincount, - int maxcount, - const char *glossary); +struct arg_key *arg_key0(const char *keyword, int flags, const char *glossary); +struct arg_key *arg_key1(const char *keyword, int flags, const char *glossary); +struct arg_key *arg_keyn(const char *keyword, int flags, int mincount, int maxcount, const char *glossary); + +struct arg_int *arg_int0(const char *shortopts, const char *longopts, const char *datatype, const char *glossary); +struct arg_int *arg_int1(const char *shortopts, const char *longopts, const char *datatype, const char *glossary); +struct arg_int *arg_intn(const char *shortopts, const char *longopts, const char *datatype, int mincount, int maxcount, const char *glossary); + +struct arg_u64 *arg_u64_0(const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary); +struct arg_u64 *arg_u64_1(const char *shortopts, + const char *longopts, + const char *datatype, + const char *glossary); +struct arg_u64 *arg_u64_n(const char *shortopts, + const char *longopts, + const char *datatype, + int mincount, + int maxcount, + const char *glossary); -struct arg_int *arg_int0(const char *shortopts, - const char *longopts, - const char *datatype, - const char *glossary); -struct arg_int *arg_int1(const char *shortopts, - const char *longopts, - const char *datatype, - const char *glossary); -struct arg_int *arg_intn(const char *shortopts, - const char *longopts, - const char *datatype, - int mincount, - int maxcount, - const char *glossary); struct arg_dbl *arg_dbl0(const char *shortopts, const char *longopts, diff --git a/client/deps/cliparser/cliparser.c b/client/deps/cliparser/cliparser.c index 3eaec2b55..cc82fc334 100644 --- a/client/deps/cliparser/cliparser.c +++ b/client/deps/cliparser/cliparser.c @@ -11,10 +11,25 @@ #include "cliparser.h" #include #include +#include // Get color constants +#include // get PrintAndLogEx + #ifndef ARRAYLEN # define ARRAYLEN(x) (sizeof(x)/sizeof((x)[0])) #endif +// Custom Colors +// To default the color return s +#define _SectionTagColor_(s) _GREEN_(s) +#define _ExampleColor_(s) _YELLOW_(s) +#define _CommandColor_(s) _RED_(s) +#define _DescriptionColor_(s) _CYAN_(s) +#define _ArgColor_(s) s +#define _ArgHelpColor_(s) s +// End Custom Colors +// Option width set to 30 to allow option descriptions to align. approx line 74 +// Example width set to 50 to allow help descriptions to align. approx line 93 + int CLIParserInit(CLIParserContext **ctx, const char *vprogramName, const char *vprogramHint, const char *vprogramHelp) { *ctx = malloc(sizeof(CLIParserContext)); if (!*ctx) { @@ -40,7 +55,7 @@ int CLIParserParseArg(CLIParserContext *ctx, int argc, char **argv, void *vargta /* verify the argtable[] entries were allocated sucessfully */ if (arg_nullcheck(ctx->argtable) != 0) { /* NULL entries were detected, some allocations must have failed */ - printf("ERROR: Insufficient memory\n"); + PrintAndLogEx(ERR, "ERROR: Insufficient memory\n"); fflush(stdout); return 2; } @@ -49,14 +64,50 @@ int CLIParserParseArg(CLIParserContext *ctx, int argc, char **argv, void *vargta /* special case: '--help' takes precedence over error reporting */ if ((argc < 2 && !allowEmptyExec) || ((struct arg_lit *)(ctx->argtable)[0])->count > 0) { // help must be the first record - printf("Usage: %s", ctx->programName); - arg_print_syntaxv(stdout, ctx->argtable, "\n"); if (ctx->programHint) - printf("%s\n\n", ctx->programHint); - arg_print_glossary(stdout, ctx->argtable, " %-20s %s\n"); - printf("\n"); - if (ctx->programHelp) - printf("%s \n", ctx->programHelp); + PrintAndLogEx(NORMAL, "\n"_DescriptionColor_("%s"), ctx->programHint); + + PrintAndLogEx(NORMAL, "\n"_SectionTagColor_("usage:")); + PrintAndLogEx(NORMAL, " "_CommandColor_("%s")NOLF, ctx->programName); + arg_print_syntax(stdout, ctx->argtable, "\n\n"); + + PrintAndLogEx(NORMAL, _SectionTagColor_("options:")); + + arg_print_glossary(stdout, ctx->argtable, " "_ArgColor_("%-30s")" "_ArgHelpColor_("%s")"\n"); + + PrintAndLogEx(NORMAL, ""); + if (ctx->programHelp) { + PrintAndLogEx(NORMAL, _SectionTagColor_("examples/notes:")); + char *buf = NULL; + int idx = 0; + buf = realloc(buf, strlen(ctx->programHelp) + 1); // more then enough as we are splitting + + char *p2; // pointer to split example from comment. + int egWidth = 30; + for (int i = 0; i <= strlen(ctx->programHelp); i++) { // <= so to get string terminator. + buf[idx++] = ctx->programHelp[i]; + if ((ctx->programHelp[i] == '\n') || (ctx->programHelp[i] == 0x00)) { + buf[idx - 1] = 0x00; + p2 = strstr(buf, "->"); // See if the example has a comment. + if (p2 != NULL) { + *(p2 - 1) = 0x00; + + if (strlen(buf) > 28) + egWidth = strlen(buf) + 5; + else + egWidth = 30; + + PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s")" %s", egWidth, buf, p2); + } else { + PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s"), egWidth, buf); + } + idx = 0; + } + } + + PrintAndLogEx(NORMAL, ""); + free(buf); + } fflush(stdout); return 1; @@ -66,7 +117,7 @@ int CLIParserParseArg(CLIParserContext *ctx, int argc, char **argv, void *vargta if (nerrors > 0) { /* Display the error details contained in the arg_end struct.*/ arg_print_errors(stdout, ((struct arg_end *)(ctx->argtable)[vargtableLen - 1]), ctx->programName); - printf("Try '%s --help' for more information.\n", ctx->programName); + PrintAndLogEx(WARNING, "Try '%s --help' for more information.\n", ctx->programName); fflush(stdout); return 3; } @@ -74,6 +125,7 @@ int CLIParserParseArg(CLIParserContext *ctx, int argc, char **argv, void *vargta return 0; } + enum ParserState { PS_FIRST, PS_ARGUMENT, @@ -152,7 +204,7 @@ int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen = 0; int ibuf = 0; - uint8_t tmp_buf[256] = {0}; + uint8_t tmp_buf[512] = {0}; int res = CLIParamStrToBuf(argstr, tmp_buf, maxdatalen * 2, &ibuf); // *2 because here HEX if (res) { printf("Parameter error: buffer overflow.\n"); @@ -186,7 +238,7 @@ int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int if (!argstr->count) return 0; - uint8_t tmp_buf[256] = {0}; + uint8_t tmp_buf[512] = {0}; int ibuf = 0; for (int i = 0; i < argstr->count; i++) { @@ -199,12 +251,13 @@ int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int if (!ibuf) return 0; - if (ibuf > maxdatalen) { + if (ibuf + 1 > maxdatalen) { + printf("Parameter error: string too long, expect max %i chars\n", maxdatalen - 1); fflush(stdout); return 2; } - memcpy(data, tmp_buf, ibuf); + memcpy(data, tmp_buf, ibuf + 1); *datalen = ibuf; return 0; } diff --git a/client/deps/cliparser/cliparser.h b/client/deps/cliparser/cliparser.h index 86ed21977..68beca038 100644 --- a/client/deps/cliparser/cliparser.h +++ b/client/deps/cliparser/cliparser.h @@ -14,25 +14,42 @@ #include #include "util.h" -#define arg_param_begin arg_lit0("hH", "help", "This help") +#define arg_param_begin arg_lit0("h", "help", "This help") #define arg_param_end arg_end(20) -#define arg_getsize(a) (sizeof(a) / sizeof(a[0])) -#define arg_get_lit(ctx, n) (((struct arg_lit*)((ctx)->argtable)[n])->count) -#define arg_get_int_count(ctx, n)(((struct arg_int*)((ctx)->argtable)[n])->count) -#define arg_get_int(ctx, n) (((struct arg_int*)((ctx)->argtable)[n])->ival[0]) -#define arg_get_int_def(ctx, n, def)(arg_get_int_count((ctx), n) ? (arg_get_int((ctx), n)) : (def)) -#define arg_get_str(ctx, n) ((struct arg_str*)((ctx)->argtable)[n]) -#define arg_get_str_len(ctx, n) (strlen(((struct arg_str*)((ctx)->argtable)[n])->sval[0])) +#define arg_getsize(a) (sizeof(a) / sizeof(a[0])) +#define arg_get_lit(ctx, n) (((struct arg_lit*)((ctx)->argtable)[(n)])->count) + +#define arg_get_int_count(ctx, n) (((struct arg_int*)((ctx)->argtable)[(n)])->count) +#define arg_get_int(ctx, n) (((struct arg_int*)((ctx)->argtable)[(n)])->ival[0]) +#define arg_get_int_def(ctx, n, def)(arg_get_int_count((ctx), (n)) ? (arg_get_int((ctx), (n))) : (def)) + +#define arg_get_dbl_count(ctx, n) (((struct arg_dbl*)((ctx)->argtable)[(n)])->count) +#define arg_get_dbl(ctx, n) (((struct arg_dbl*)((ctx)->argtable)[(n)])->dval[0]) +#define arg_get_dbl_def(ctx, n, def)(arg_get_dbl_count((ctx), (n)) ? (arg_get_dbl((ctx), (n))) : (def)) + +#define arg_get_u32(ctx, n) (uint32_t)(((struct arg_u64*)((ctx)->argtable)[(n)])->uval[0]) +#define arg_get_u32_def(ctx, n, def) (arg_get_u64_count((ctx), (n)) ? (arg_get_u32((ctx), (n))) : (uint32_t)(def)) + +#define arg_get_u64_count(ctx, n) (((struct arg_u64*)((ctx)->argtable)[(n)])->count) +#define arg_get_u64(ctx, n) (((struct arg_u64*)((ctx)->argtable)[(n)])->uval[0]) +#define arg_get_u64_def(ctx, n, def) (arg_get_u64_count((ctx), (n)) ? (arg_get_u64((ctx), (n))) : (uint64_t)(def)) + +#define arg_get_str(ctx, n) ((struct arg_str*)((ctx)->argtable)[(n)]) +#define arg_get_str_len(ctx, n) (strlen(((struct arg_str*)((ctx)->argtable)[(n)])->sval[0])) #define arg_strx1(shortopts, longopts, datatype, glossary) (arg_strn((shortopts), (longopts), (datatype), 1, 250, (glossary))) #define arg_strx0(shortopts, longopts, datatype, glossary) (arg_strn((shortopts), (longopts), (datatype), 0, 250, (glossary))) -#define CLIParserFree(ctx) if ((ctx)) {arg_freetable(ctx->argtable, ctx->argtableLen); free((ctx)); (ctx)=NULL;} -#define CLIExecWithReturn(ctx, cmd, atbl, ifempty) if (CLIParserParseString(ctx, cmd, atbl, arg_getsize(atbl), ifempty)) {CLIParserFree((ctx)); return PM3_ESOFT;} -#define CLIGetHexBLessWithReturn(ctx, paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str(ctx, paramnum), data, sizeof(data) - (delta), datalen)) {CLIParserFree((ctx)); return PM3_ESOFT;} -#define CLIGetHexWithReturn(ctx, paramnum, data, datalen) if (CLIParamHexToBuf(arg_get_str(ctx, paramnum), data, sizeof(data), datalen)) {CLIParserFree((ctx)); return PM3_ESOFT;} -#define CLIGetStrWithReturn(ctx, paramnum, data, datalen) if (CLIParamStrToBuf(arg_get_str(ctx, paramnum), data, sizeof(data), datalen)) {CLIParserFree((ctx)); return PM3_ESOFT;} +#define CLIParserFree(ctx) if ((ctx)) {arg_freetable((ctx)->argtable, (ctx)->argtableLen); free((ctx)); (ctx)=NULL;} + +#define CLIExecWithReturn(ctx, cmd, atbl, ifempty) if (CLIParserParseString((ctx), (cmd), (atbl), arg_getsize((atbl)), (ifempty))) {CLIParserFree((ctx)); return PM3_ESOFT;} + +#define CLIGetHexBLessWithReturn(ctx, paramnum, data, datalen, delta) if (CLIParamHexToBuf(arg_get_str((ctx), (paramnum)), (data), sizeof((data)) - (delta), (datalen))) {CLIParserFree((ctx)); return PM3_ESOFT;} + +#define CLIGetHexWithReturn(ctx, paramnum, data, datalen) if (CLIParamHexToBuf(arg_get_str((ctx), (paramnum)), (data), sizeof((data)), (datalen))) {CLIParserFree((ctx)); return PM3_ESOFT;} + +#define CLIGetStrWithReturn(ctx, paramnum, data, datalen) if (CLIParamStrToBuf(arg_get_str((ctx), (paramnum)), (data), sizeof((data)), (datalen))) {CLIParserFree((ctx)); return PM3_ESOFT;} typedef struct { void **argtable; diff --git a/client/deps/hardnested.cmake b/client/deps/hardnested.cmake index 6ec4f0297..f9248b82d 100644 --- a/client/deps/hardnested.cmake +++ b/client/deps/hardnested.cmake @@ -7,7 +7,8 @@ set_property(TARGET pm3rrg_rdv4_hardnested_nosimd PROPERTY POSITION_INDEPENDENT_ target_include_directories(pm3rrg_rdv4_hardnested_nosimd PRIVATE ../../common - ../../include) + ../../include + ../src) ## CPU-specific code ## These are mostly for x86-based architectures, which is not useful for many Android devices. @@ -34,7 +35,8 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) target_include_directories(pm3rrg_rdv4_hardnested_mmx PRIVATE ../../common - ../../include) + ../../include + ../src) ## x86 / SSE2 add_library(pm3rrg_rdv4_hardnested_sse2 OBJECT @@ -48,7 +50,8 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) target_include_directories(pm3rrg_rdv4_hardnested_sse2 PRIVATE ../../common - ../../include) + ../../include + ../src) ## x86 / AVX add_library(pm3rrg_rdv4_hardnested_avx OBJECT @@ -62,7 +65,8 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) target_include_directories(pm3rrg_rdv4_hardnested_avx PRIVATE ../../common - ../../include) + ../../include + ../src) ## x86 / AVX2 add_library(pm3rrg_rdv4_hardnested_avx2 OBJECT @@ -76,7 +80,8 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) target_include_directories(pm3rrg_rdv4_hardnested_avx2 PRIVATE ../../common - ../../include) + ../../include + ../src) ## x86 / AVX512 add_library(pm3rrg_rdv4_hardnested_avx512 OBJECT @@ -90,7 +95,8 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) target_include_directories(pm3rrg_rdv4_hardnested_avx512 PRIVATE ../../common - ../../include) + ../../include + ../src) set(SIMD_TARGETS $ diff --git a/client/deps/hardnested/hardnested_bf_core.c b/client/deps/hardnested/hardnested_bf_core.c index 5f4fcf991..719578331 100644 --- a/client/deps/hardnested/hardnested_bf_core.c +++ b/client/deps/hardnested/hardnested_bf_core.c @@ -59,7 +59,7 @@ THE SOFTWARE. #include #include "crapto1/crapto1.h" #include "parity.h" -//#include "util.h" +#include "ui.h" // PrintAndLogEx //#include "common.h" // bitslice type @@ -238,18 +238,18 @@ uint64_t CRACK_STATES_BITSLICED(uint32_t cuid, uint8_t *best_first_bytes, statel // bitslice all the even states bitslice_t **restrict bitsliced_even_states = (bitslice_t **)malloc(((p->len[EVEN_STATE] - 1) / MAX_BITSLICES + 1) * sizeof(bitslice_t *)); if (bitsliced_even_states == NULL) { - printf("Out of memory error in brute_force. Aborting..."); + PrintAndLogEx(WARNING, "Out of memory error in brute_force. Aborting..."); exit(4); } bitslice_value_t *restrict bitsliced_even_feedback = malloc_bitslice(((p->len[EVEN_STATE] - 1) / MAX_BITSLICES + 1) * sizeof(bitslice_value_t)); if (bitsliced_even_feedback == NULL) { - printf("Out of memory error in brute_force. Aborting..."); + PrintAndLogEx(WARNING, "Out of memory error in brute_force. Aborting..."); exit(4); } for (uint32_t *restrict p_even = p->states[EVEN_STATE]; p_even < p_even_end; p_even += MAX_BITSLICES) { bitslice_t *restrict lstate_p = malloc_bitslice(STATE_SIZE / 2 * sizeof(bitslice_t)); if (lstate_p == NULL) { - printf("Out of memory error in brute_force. Aborting... \n"); + PrintAndLogEx(WARNING, "Out of memory error in brute_force. Aborting... \n"); exit(4); } memset(lstate_p, 0x00, STATE_SIZE / 2 * sizeof(bitslice_t)); // zero even bits @@ -265,8 +265,8 @@ uint64_t CRACK_STATES_BITSLICED(uint32_t cuid, uint8_t *best_first_bytes, statel #ifdef DEBUG_KEY_ELIMINATION if (known_target_key != -1 && e == test_state[EVEN_STATE]) { bucket_contains_test_key[bitsliced_blocks] = true; - // printf("bucket %d contains test key even state\n", bitsliced_blocks); - // printf("in slice %d\n", slice_idx); + // PrintAndLogEx(INFO, "bucket %d contains test key even state", bitsliced_blocks); + // PrintAndLogEx(INFO, "in slice %d", slice_idx); } #endif for (uint32_t bit_idx = 0; bit_idx < STATE_SIZE / 2; bit_idx++, e >>= 1) { @@ -336,8 +336,8 @@ uint64_t CRACK_STATES_BITSLICED(uint32_t cuid, uint8_t *best_first_bytes, statel #ifdef DEBUG_KEY_ELIMINATION // if (known_target_key != -1 && bucket_contains_test_key[block_idx] && *p_odd == test_state[ODD_STATE]) { - // printf("Now testing known target key.\n"); - // printf("block_idx = %d/%d\n", block_idx, bitsliced_blocks); + // PrintAndLogEx(INFO, "Now testing known target key."); + // PrintAndLogEx(INFO, "block_idx = %d/%d", block_idx, bitsliced_blocks); // } #endif // add the even state bits @@ -444,8 +444,8 @@ uint64_t CRACK_STATES_BITSLICED(uint32_t cuid, uint8_t *best_first_bytes, statel #endif #ifdef DEBUG_KEY_ELIMINATION if (known_target_key != -1 && bucket_contains_test_key[block_idx] && *p_odd == test_state[ODD_STATE]) { - printf("Known target key eliminated in brute_force.\n"); - printf("block_idx = %d/%d, nonce = %d/%d\n", block_idx, bitsliced_blocks, tests, nonces_to_bruteforce); + PrintAndLogEx(INFO, "Known target key eliminated in brute_force."); + PrintAndLogEx(INFO, "block_idx = %d/%d, nonce = %d/%d", block_idx, bitsliced_blocks, tests, nonces_to_bruteforce); } #endif goto stop_tests; @@ -495,15 +495,15 @@ uint64_t CRACK_STATES_BITSLICED(uint32_t cuid, uint8_t *best_first_bytes, statel } #ifdef DEBUG_KEY_ELIMINATION if (known_target_key != -1 && *p_even_test == test_state[EVEN_STATE] && *p_odd == test_state[ODD_STATE]) { - printf("Known target key eliminated in brute_force verification.\n"); - printf("block_idx = %d/%d\n", block_idx, bitsliced_blocks); + PrintAndLogEx(INFO, "Known target key eliminated in brute_force verification."); + PrintAndLogEx(INFO, "block_idx = %d/%d", block_idx, bitsliced_blocks); } #endif } #ifdef DEBUG_KEY_ELIMINATION if (known_target_key != -1 && *p_even_test == test_state[EVEN_STATE] && *p_odd == test_state[ODD_STATE]) { - printf("Known target key eliminated in brute_force (results_bit == 0).\n"); - printf("block_idx = %d/%d\n", block_idx, bitsliced_blocks); + PrintAndLogEx(INFO, "Known target key eliminated in brute_force (results_bit == 0)."); + PrintAndLogEx(INFO, "block_idx = %d/%d", block_idx, bitsliced_blocks); } #endif results64 >>= 1; @@ -533,7 +533,7 @@ out: #if defined (DEBUG_BRUTE_FORCE) for (uint32_t i = 0; i < MAX_ELIMINATION_STEP; i++) { - printf("Eliminated after %2u test_bytes: %5.2f%%\n", i + 1, (float)keys_eliminated[i] / bucket_states_tested * 100); + PrintAndLogEx(INFO, "Eliminated after %2u test_bytes: %5.2f%%", i + 1, (float)keys_eliminated[i] / bucket_states_tested * 100); } #endif return key; diff --git a/client/deps/hardnested/hardnested_bruteforce.c b/client/deps/hardnested/hardnested_bruteforce.c index 82f8c6c04..0c768f400 100644 --- a/client/deps/hardnested/hardnested_bruteforce.c +++ b/client/deps/hardnested/hardnested_bruteforce.c @@ -174,7 +174,7 @@ crack_states_thread(void *x) { char progress_text[80]; char keystr[19]; sprintf(keystr, "%012" PRIx64 " ", key); - sprintf(progress_text, "Brute force phase completed. Key found: " _YELLOW_("%s"), keystr); + sprintf(progress_text, "Brute force phase completed. Key found: " _YELLOW_("%s"), keystr); hardnested_print_progress(thread_arg->num_acquired_nonces, progress_text, 0.0, 0); break; } else if (keys_found) { diff --git a/client/deps/jansson/pack_unpack.c b/client/deps/jansson/pack_unpack.c index 279499ebd..fbe0d44ef 100644 --- a/client/deps/jansson/pack_unpack.c +++ b/client/deps/jansson/pack_unpack.c @@ -740,8 +740,12 @@ static int unpack(scanner_t *s, json_t *root, va_list *ap) { case 'b': if (root && !json_is_boolean(root)) { - set_error(s, "", json_error_wrong_type, "Expected true or false, got %s", - type_name(root)); + set_error(s, + "", + json_error_wrong_type, + "Expected true or false, got %s", + type_name(root) + ); return -1; } diff --git a/client/deps/reveng/cli.c b/client/deps/reveng/cli.c index 88e8d3722..5d5d91a18 100644 --- a/client/deps/reveng/cli.c +++ b/client/deps/reveng/cli.c @@ -167,8 +167,9 @@ int reveng_main(int argc, char *argv[]) { pkchop(&model.spoly); width = plen(model.spoly); rflags |= R_HAVEP; - if (c == 'P') + if (c == 'P') { prcp(&model.spoly); + } mnovel(&model); break; case 'l': /* l little-endian input and output */ @@ -332,6 +333,7 @@ ipqx: mbynum(&model, --args); ufound(&model); } while (args); + mfree(&model); break; case 'd': /* d dump CRC model */ /* maybe we don't want to do this: @@ -387,6 +389,10 @@ ipqx: apolys = calloc(args * sizeof(poly_t), sizeof(char)); if (!apolys) { uerror("cannot allocate memory for argument list"); + pfree(&model.spoly); + pfree(&model.init); + pfree(&model.xorout); + mfree(&model); return 0; } @@ -421,16 +427,20 @@ ipqx: continue; if (rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout)) continue; + apoly = pclone(pset.xorout); - if (pset.flags & P_REFOUT) + if (pset.flags & P_REFOUT) { prev(&apoly); + } + for (qptr = apolys; qptr < pptr; ++qptr) { crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0); if (ptst(crc)) { pfree(&crc); break; - } else + } else { pfree(&crc); + } } pfree(&apoly); if (qptr == pptr) { @@ -444,14 +454,19 @@ ipqx: /* toggle refIn/refOut and reflect arguments */ if (~rflags & R_HAVERI) { model.flags ^= P_REFIN | P_REFOUT; - for (qptr = apolys; qptr < pptr; ++qptr) + for (qptr = apolys; qptr < pptr; ++qptr) { prevch(qptr, ibperhx); + } } } while (~rflags & R_HAVERI && ++pass < 2); } + if (uflags & C_RESULT) { - for (qptr = apolys; qptr < pptr; ++qptr) + for (qptr = apolys; qptr < pptr; ++qptr) { pfree(qptr); + } + free(apolys); + mfree(&model); return 1; //exit(EXIT_SUCCESS); } @@ -467,8 +482,10 @@ ipqx: pass = 0; do { mptr = candmods = reveng(&model, qpoly, rflags, args, apolys); - if (mptr && plen(mptr->spoly)) + if (mptr && plen(mptr->spoly)) { uflags |= C_RESULT; + } + while (mptr && plen(mptr->spoly)) { /* results were printed by the callback * string = mtostr(mptr); @@ -478,26 +495,31 @@ ipqx: mfree(mptr++); } free(candmods); + if (~rflags & R_HAVERI) { model.flags ^= P_REFIN | P_REFOUT; - for (qptr = apolys; qptr < pptr; ++qptr) + for (qptr = apolys; qptr < pptr; ++qptr) { prevch(qptr, ibperhx); + } } } while (~rflags & R_HAVERI && ++pass < 2); - for (qptr = apolys; qptr < pptr; ++qptr) + + for (qptr = apolys; qptr < pptr; ++qptr) { pfree(qptr); + } + free(apolys); + if (~uflags & C_RESULT) uerror("no models found"); + break; default: /* no mode specified */ fprintf(stderr, "%s: no mode switch specified. Use %s -h for help.\n", myname, myname); return 0; - //exit(EXIT_FAILURE); } return 1; - //exit(EXIT_SUCCESS); } void diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index cc68b0d88..58c2c40bc 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -57,6 +57,9 @@ f1d83f964314 # RKF RejskortDanmark KeyB # 4b0b20107ccb # TNP3xxx # +# Access control system +605F5E5D5C5B +# # more Keys from mfc_default_keys.lua 000000000001 000000000002 @@ -1115,3 +1118,88 @@ fe04ecfe5577 C1E51C63B8F5 # RATB key 1DB710648A65 18F34C92A56E # E-GO card key +# +# Library Card MFP - SL1 +4a832584637d +ca679d6291b0 +30d9690fc5bc +5296c26109d4 +e77952748484 +91c2376005a1 +30b7680b2bc9 +e2a9e88bfe16 +43b04995d234 +aade86b1f9c1 +5ea088c824c9 +c67beb41ffbf +5ea088c824c9 +c67beb41ffbf +b84d52971107 +52b0d3f6116e +# +# Data from https://pastebin.com/cLSQQ9xN +ca3a24669d45 +4087c6a75a96 +160a91d29a9c +b7bf0c13066e +403f09848b87 +d73438698eea +5f31f6fcd3a0 +a0974382c4c5 +a82045a10949 +# +# Data from https://pastebin.com/2iV8h93h +# +# funnivarium +# forum ankara +# +2602FFFFFFFF +# +# macera adasi +# ankara kentpark +# INACTIVE +# +0A4600FF00FF +DFF293979FA7 +4D6F62692E45 +4118D7EF0902 +# +# petrol ofisi +# positive card +# ode-gec +# +0406080A0C0E +# +# konya elkart +# +988ACDECDFB0 +120D00FFFFFF +# +# bowlingo +# serdivan avym +# +4AE23A562A80 +# +# kart54 +# +2AFFD6F88B97 +A9F3F289B70C +DB6819558A25 +6130DFA578A0 +B16B2E573235 +42EF7BF572AB +274E6101FC5E +# +# crazy park +# kizilay avm +# +00DD300F4F10 +# +# kartsistem B +# +FEE2A3FBC5B6 +# +# toru ent +# taurus avm +# +005078565703 diff --git a/client/dictionaries/mfdes_default_keys.dic b/client/dictionaries/mfdes_default_keys.dic index dac73fa04..4c863c3f5 100644 --- a/client/dictionaries/mfdes_default_keys.dic +++ b/client/dictionaries/mfdes_default_keys.dic @@ -42,3 +42,5 @@ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 100f0e0d0c0b0a090807060504030201 404142434445464748494a4b4c4d4e4f 303132333435363738393a3b3c3d3e3f +9CABF398358405AE2F0E2B3D31C99A8A # Default key +605F5E5D5C5B5A59605F5E5D5C5B5A59 # access control \ No newline at end of file diff --git a/client/dictionaries/mfp_default_keys.dic b/client/dictionaries/mfp_default_keys.dic index e492e2686..a063e34ad 100644 --- a/client/dictionaries/mfp_default_keys.dic +++ b/client/dictionaries/mfp_default_keys.dic @@ -27,4 +27,4 @@ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 100f0e0d0c0b0a090807060504030201 404142434445464748494a4b4c4d4e4f 303132333435363738393a3b3c3d3e3f - +605F5E5D5C5B5A59605F5E5D5C5B5A59 diff --git a/client/gen_pm3mfsim_script.sh b/client/gen_pm3mfsim_script.sh index 73c7a4dd4..c3726d69c 100644 --- a/client/gen_pm3mfsim_script.sh +++ b/client/gen_pm3mfsim_script.sh @@ -11,7 +11,7 @@ show_usage() { echo echo "Usage:" - echo "${0##/} input_eml_without_extension output.pm3scr" + echo "${0##/} input_eml_without_extension output.cmd" exit } @@ -32,4 +32,4 @@ rm $2 echo "hf mf eclr" >> $2 echo "hf mf eload" $1 >> $2 echo "hf mf ekeyprn" >> $2 -echo "hf mf sim" `cat $1.eml | (read -n 8 uid; echo $uid)` >> $2 \ No newline at end of file +echo "hf mf sim u" `cat $1.eml | (read -n 8 uid; echo $uid)` >> $2 \ No newline at end of file diff --git a/client/lualibs/commands.lua b/client/lualibs/commands.lua index 11227b8ac..dbbc3b131 100644 --- a/client/lualibs/commands.lua +++ b/client/lualibs/commands.lua @@ -93,8 +93,10 @@ Command = { o.data = data return o end, - parse = function (packet) - local count, cmd, arg1, arg2, arg3, data = bin.unpack('LLLLH511', packet) + parse = function(packet) + local count, cmd, arg1, arg2, arg3, data = bin.unpack('LLLL', packet) + local length = #packet - count + 1 + count, data = bin.unpack('H'..length, packet, count) return Command:new{cmd = cmd, arg1 = arg1, arg2 = arg2, arg3 = arg3, data = data} end } @@ -121,26 +123,28 @@ end -- @param command - the usb packet to send -- @param ignoreresponse - if set to true, we don't read the device answer packet -- which is usually recipe for fail. If not sent, the host will wait 2s for a --- response of type CMD_ACK +-- response of type CMD_ACK or like NG use the CMD as ack.. -- @return packet,nil if successful -- nil, errormessage if unsuccessful -function Command:sendMIX( ignore_response, timeout ) +function Command:sendMIX( ignore_response, timeout, use_cmd_ack) + if timeout == nil then timeout = TIMEOUT end local data = self.data local cmd = self.cmd local arg1, arg2, arg3 = self.arg1, self.arg2, self.arg3 local err, msg = core.SendCommandMIX(cmd, arg1, arg2, arg3, data) if err == nil then return err, msg end - if ignore_response then return true, nil end - if timeout == nil then timeout = TIMEOUT end + local ack = _commands.CMD_ACK + if use_cmd_ack then + ack = cmd + end - local response, msg = core.WaitForResponseTimeout(_commands.CMD_ACK, timeout) + local response, msg = core.WaitForResponseTimeout(ack, timeout) if response == nil then return nil, 'Error, waiting for response timed out :: '..msg end - -- lets digest data = nil cmd = nil @@ -157,15 +161,13 @@ function Command:sendMIX( ignore_response, timeout ) return packed, nil; end function Command:sendNG( ignore_response, timeout ) + if timeout == nil then timeout = TIMEOUT end local data = self.data local cmd = self.cmd local err, msg = core.SendCommandNG(cmd, data) if err == nil then return nil, msg end if ignore_response then return true, nil end - - if timeout == nil then timeout = TIMEOUT end - local response, msg = core.WaitForResponseTimeout(cmd, timeout) if response == nil then return nil, 'Error, waiting for response timed out :: '..msg diff --git a/client/lualibs/read14b.lua b/client/lualibs/read14b.lua index b1688552f..7547bd866 100644 --- a/client/lualibs/read14b.lua +++ b/client/lualibs/read14b.lua @@ -15,17 +15,18 @@ local cmds = require('commands') local utils = require('utils') -- Shouldn't take longer than 2.5 seconds -local TIMEOUT = 2500 +local TIMEOUT = 2000 local ISO14B_COMMAND = { - ISO14B_CONNECT = 1, - ISO14B_DISCONNECT = 2, - ISO14B_APDU = 4, - ISO14B_RAW = 8, + ISO14B_CONNECT = 0x1, + ISO14B_DISCONNECT = 0x2, + ISO14B_APDU = 0x4, + ISO14B_RAW = 0x8, ISO14B_REQUEST_TRIGGER = 0x10, ISO14B_APPEND_CRC = 0x20, ISO14B_SELECT_STD = 0x40, ISO14B_SELECT_SR = 0x80, + ISO14B_SET_TIMEOUT = 0x100, } local function parse14443b(data) @@ -74,9 +75,11 @@ local function read14443b(disconnect) arg1 = flags } - local result, err = command:sendMIX() + info = nil + + local result, err = command:sendMIX(false, TIMEOUT, true) if result then - local count,cmd,arg0,arg1,arg2 = bin.unpack('LLLL',result) + local count,cmd,arg0,arg1,arg2 = bin.unpack('LLLL', result) if arg0 == 0 then data = string.sub(result, count) info, err = parse14443b(data) @@ -88,12 +91,10 @@ local function read14443b(disconnect) end if err then - print(err) return nil, err end - return info + return info, nil end - --- -- Waits for a mifare card to be placed within the vicinity of the reader. -- @return if successful: an table containing card info @@ -102,12 +103,11 @@ local function waitFor14443b() print('Waiting for card... press Enter to quit') while not core.kbd_enter_pressed() do res, err = read14443b(false) - if res then return res end + if res then return res, err end -- err means that there was no response from card end return nil, 'Aborted by user' end - --- -- turns on the HF field. local function connect14443b() diff --git a/client/luascripts/e.lua b/client/luascripts/data_hex_crc.lua similarity index 94% rename from client/luascripts/e.lua rename to client/luascripts/data_hex_crc.lua index 43e0ab407..a970eaf93 100644 --- a/client/luascripts/e.lua +++ b/client/luascripts/data_hex_crc.lua @@ -9,11 +9,11 @@ desc = [[ This script calculates many checksums (CRC) over the provided hex input. ]] example = [[ - script run e -b 010203040506070809 - script run e -b 010203040506070809 -w 16 + script run data_hex_crc -b 010203040506070809 + script run data_hex_crc -b 010203040506070809 -w 16 ]] usage = [[ -script run e [-b ] +script run data_hex_crc [-b ] ]] arguments = [[ -b data in hex diff --git a/client/luascripts/mifare_access.lua b/client/luascripts/data_mf_accessdecode.lua similarity index 97% rename from client/luascripts/mifare_access.lua rename to client/luascripts/data_mf_accessdecode.lua index 4c416e8d6..551080b5d 100644 --- a/client/luascripts/mifare_access.lua +++ b/client/luascripts/data_mf_accessdecode.lua @@ -8,10 +8,10 @@ desc = [[ This script tries to decode Mifare Classic Access bytes ]] example = [[ - 1. script run mifare_access -a 7F0F0869 + 1. script run data_mf_accessdecode -a 7F0F0869 ]] usage = [[ -script run mifare_access [-h] [-a ] +script run data_mf_accessdecode [-h] [-a ] ]] arguments = [[ -h : this help diff --git a/client/luascripts/dumptoemul.lua b/client/luascripts/data_mf_bin2eml.lua similarity index 97% rename from client/luascripts/dumptoemul.lua rename to client/luascripts/data_mf_bin2eml.lua index 88121f86d..ef48ead16 100644 --- a/client/luascripts/dumptoemul.lua +++ b/client/luascripts/data_mf_bin2eml.lua @@ -12,10 +12,10 @@ This script takes a dumpfile from 'hf mf dump' and converts it to a format that by the emulator ]] example = [[ - script run dumptoemul -i dumpdata-foobar.bin + script run data_mf_bin2eml -i dumpdata-foobar.bin ]] usage = [[ -script run dumptoemul [-i ] [-o ] +script run data_mf_bin2eml [-i ] [-o ] ]] arguments = [[ -h This help diff --git a/client/luascripts/htmldump.lua b/client/luascripts/data_mf_bin2html.lua similarity index 95% rename from client/luascripts/htmldump.lua rename to client/luascripts/data_mf_bin2html.lua index 2853d343b..c33b4782b 100644 --- a/client/luascripts/htmldump.lua +++ b/client/luascripts/data_mf_bin2html.lua @@ -13,10 +13,10 @@ This script takes a dumpfile and produces a html based dump, which is a bit more easily analyzed. ]] example = [[ - script run htmldump -o mifarecard_foo.html + script run data_mf_bin2html -o mifarecard_foo.html ]] usage = [[ -script run htmldump [-i ] [-o ] +script run data_mf_bin2html [-i ] [-o ] ]] arguments = [[ -h This help diff --git a/client/luascripts/emul2dump.lua b/client/luascripts/data_mf_eml2bin.lua similarity index 91% rename from client/luascripts/emul2dump.lua rename to client/luascripts/data_mf_eml2bin.lua index e0286e625..d31b58418 100644 --- a/client/luascripts/emul2dump.lua +++ b/client/luascripts/data_mf_eml2bin.lua @@ -10,12 +10,12 @@ desc =[[ This script takes an dumpfile in EML (ASCII) format and converts it to the PM3 dumpbin file to be used with `hf mf restore` ]] example =[[ - 1. script run emul2dump - 2. script run emul2dump -i myfile.eml - 3. script run emul2dump -i myfile.eml -o myfile.bin + 1. script run data_mf_eml2bin + 2. script run data_mf_eml2bin -i myfile.eml + 3. script run data_mf_eml2bin -i myfile.eml -o myfile.bin ]] usage = [[ -script run emul2dump [-i ] [-o ] +script run data_mf_eml2bin [-i ] [-o ] ]] arguments = [[ -h This help diff --git a/client/luascripts/emul2html.lua b/client/luascripts/data_mf_eml2html.lua similarity index 95% rename from client/luascripts/emul2html.lua rename to client/luascripts/data_mf_eml2html.lua index 533637669..9b2edbe87 100644 --- a/client/luascripts/emul2html.lua +++ b/client/luascripts/data_mf_eml2html.lua @@ -13,10 +13,10 @@ This script takes a dumpfile on EML (ASCII) format and produces a html based dum bit more easily analyzed. ]] example = [[ - script run emul2html -o dumpdata.eml + script run data_mf_eml2html -o dumpdata.eml ]] usage = [[ -script run htmldump [-i ] [-o ] +script run data_mf_eml2html [-i ] [-o ] ]] arguments = [[ -h This help diff --git a/client/luascripts/cmdline.lua b/client/luascripts/examples/example_cmdline.lua similarity index 100% rename from client/luascripts/cmdline.lua rename to client/luascripts/examples/example_cmdline.lua diff --git a/client/luascripts/parameters.lua b/client/luascripts/examples/example_parameters.lua similarity index 88% rename from client/luascripts/parameters.lua rename to client/luascripts/examples/example_parameters.lua index dce215eb7..0925f7d2c 100644 --- a/client/luascripts/parameters.lua +++ b/client/luascripts/examples/example_parameters.lua @@ -4,7 +4,7 @@ local getopt = require('getopt') local ansicolors = require('ansicolors') copyright = '' -usage = 'script run parameters.lua -a 1 -blala -c -de' +usage = 'script run example_parameters.lua -a 1 -blala -c -de' author = 'Martin Holst Swende' version = 'v1.0.2' desc = [[ @@ -12,10 +12,10 @@ This is an example script to demonstrate handle parameters in scripts. For more info, check the comments in the code ]] example = [[ - 1. script run parameters -a mytestparam_input -c + 1. script run example_parameters -a mytestparam_input -c ]] usage = [[ -script run parameters [-h] [-a ] [-b ] [-c] [-d] [-e] +script run example_parameters [-h] [-a ] [-b ] [-c] [-d] [-e] ]] arguments = [[ -h This help @@ -52,7 +52,7 @@ local function main(args) 5 parameters; two with values and three flags. The following should be valid: - script run parameters.lua -a 1 -blala -c -de + script run example_parameters.lua -a 1 -blala -c -de Notice two things: 1. 'blala' works just like 'b lala', both set 'b' to 'lala' diff --git a/client/luascripts/ufodump.lua b/client/luascripts/hf_14a_aztek.lua similarity index 98% rename from client/luascripts/ufodump.lua rename to client/luascripts/hf_14a_aztek.lua index d9a8faa68..c63a9f85c 100644 --- a/client/luascripts/ufodump.lua +++ b/client/luascripts/hf_14a_aztek.lua @@ -14,13 +14,13 @@ xor: the first three block (0,1,2) is not XORED. The rest seems to be xored. ]] example = [[ -- default - script run ufodump + script run hf_14a_aztek -- stop at block 10 - script run ufodump -b 10 + script run hf_14a_aztek -b 10 ]] usage = [[ -script run ufudump [-h] [-b] +script run hf_14a_aztek [-h] [-b] ]] arguments = [[ -h This help diff --git a/client/luascripts/14araw.lua b/client/luascripts/hf_14a_raw.lua similarity index 93% rename from client/luascripts/14araw.lua rename to client/luascripts/hf_14a_raw.lua index e6a180bfb..1d087a1c4 100644 --- a/client/luascripts/14araw.lua +++ b/client/luascripts/hf_14a_raw.lua @@ -7,29 +7,29 @@ copyright = '' author = "Martin Holst Swende" version = 'v1.0.2' desc = [[ -This is a script to allow raw 1444a commands to be sent and received. +This is a script to allow raw 14443a commands to be sent and received. ]] example = [[ # 1. Connect and don't disconnect - script run 14araw -p + script run hf_14a_raw -k # 2. Send mf auth, read response (nonce) - script run 14araw -o -x 6000F57b -p + script run hf_14a_raw -o -x 6000F57b -k # 3. disconnect - script run 14araw -o + script run hf_14a_raw -o # All three steps in one go: - script run 14araw -x 6000F57b + script run hf_14a_raw -x 6000F57b ]] usage = [[ -script run 14araw -x 6000F57b +script run hf_14a_raw -x 6000F57b ]] arguments = [[ - -o do not connect - use this only if you previously used -p to stay connected + -o do not connect - use this only if you previously used -k to stay connected -r do not read response -c calculate and append CRC - -p stay connected - dont inactivate the field + -k stay connected - dont inactivate the field -x Data to send (NO SPACES!) -d Debug flag -t Topaz mode @@ -187,8 +187,8 @@ function selftest() DEBUG = true dbg('Performing test') main() - main('-p') - main(' -o -x 6000F57b -p') + main('-k') + main(' -o -x 6000F57b -k') main('-o') main('-x 6000F57b') dbg('Tests done') diff --git a/client/luascripts/lto_dump.lua b/client/luascripts/hf_14a_read-ltocm.lua similarity index 93% rename from client/luascripts/lto_dump.lua rename to client/luascripts/hf_14a_read-ltocm.lua index d2dd87762..362868177 100644 --- a/client/luascripts/lto_dump.lua +++ b/client/luascripts/hf_14a_read-ltocm.lua @@ -13,13 +13,13 @@ It starts from block 0 and ends at default block 254. ]] example = [[ -- default - script run lto_dump + script run hf_14a_read-ltocm -- stop at block 10 - script run lto_dump -e 10 + script run hf_14a_read-ltocm -e 10 ]] usage = [[ -script run lto_dump -h -s -e +script run hf_14a_read-ltocm -h -s -e ]] arguments = [[ h this helptext @@ -135,13 +135,13 @@ function main(args) end -- Original loop --- core.console("hf 14a raw -a -p -b 7 45") --- local cmd_select = string.format("hf 14a raw -c -p 9370%s", serial_number) +-- core.console("hf 14a raw -a -k -b 7 45") +-- local cmd_select = string.format("hf 14a raw -c -k 9370%s", serial_number) -- core.console(cmd_select) -- for i = 0, 254 do --- local cmd_rd_blk = string.format("hf 14a raw -c -p 30 %02x", i) +-- local cmd_rd_blk = string.format("hf 14a raw -c -k 30 %02x", i) -- core.console(cmd_rd_blk) --- core.console("hf 14a raw -p 80") +-- core.console("hf 14a raw -k 80") -- end -- core.console("hf 14a raw -r") diff --git a/client/luascripts/calypso.lua b/client/luascripts/hf_14b_calypso.lua similarity index 65% rename from client/luascripts/calypso.lua rename to client/luascripts/hf_14b_calypso.lua index de72f41c6..72ac0f102 100644 --- a/client/luascripts/calypso.lua +++ b/client/luascripts/hf_14b_calypso.lua @@ -7,16 +7,16 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Iceman' -version = 'v1.0.2' +version = 'v1.0.4' desc = [[ This is a script to communicate with a CALYSPO / 14443b tag using the '14b raw' commands ]] example = [[ - script run calypso -b 11223344 + script run hf_14b_calypso -b 11223344 ]] usage = [[ -script run calypso -h -b +script run hf_14b_calypso -h -b ]] arguments = [[ h this helptext @@ -31,11 +31,12 @@ device-side. local function calypso_parse(result) local r = Command.parse(result) - local len = r.arg2 * 2 - r.data = string.sub(r.data, 0, len); - print('GOT:', r.data) - if r.arg1 == 0 then - return r, nil + if r.arg1 >= 0 then + local len = r.arg1 * 2 + if len > 0 then + r.data = string.sub(r.data, 0, len); + return r, nil + end end return nil,nil end @@ -112,8 +113,9 @@ end local function calypso_send_cmd_raw(data, ignoreresponse ) local command, flags, result, err - flags = lib14b.ISO14B_COMMAND.ISO14B_RAW + - lib14b.ISO14B_COMMAND.ISO14B_APPEND_CRC + flags = lib14b.ISO14B_COMMAND.ISO14B_APDU +-- flags = lib14b.ISO14B_COMMAND.ISO14B_RAW + +-- lib14b.ISO14B_COMMAND.ISO14B_APPEND_CRC data = data or "00" @@ -123,12 +125,19 @@ local function calypso_send_cmd_raw(data, ignoreresponse ) arg2 = #data/2, -- LEN of data, half the length of the ASCII-string hex string data = data} -- data bytes (commands etc) - result, err = command:sendMIX(ignoreresponse) + local use_cmd_ack = true + result, err = command:sendMIX(ignoreresponse, 2000, use_cmd_ack) if result then - local r = calypso_parse(result) - return r, nil + local count,cmd,arg0,arg1,arg2 = bin.unpack('LLLL', result) + if arg0 >= 0 then + return calypso_parse(result) + else + err = 'card response failed' + end + else + err = 'No response from card' end - return respone, err + return result, err end --- -- calypso_card_num : Reads card number from ATR and @@ -136,25 +145,25 @@ end local function calypso_card_num(card) if not card then return end local card_num = tonumber( card.uid:sub(1,8),16 ) - print('Card UID', card.uid) - print('Card Number', card_num) + print('') + print('Card UID ' ..ansicolors.green..card.uid:format('%x')..ansicolors.reset) + print('Card Number ' ..ansicolors.green..string.format('%u', card_num)..ansicolors.reset) + print('-----------------------') end --- -- analyse CALYPSO apdu status bytes. local function calypso_apdu_status(apdu) -- last two is CRC -- next two is APDU status bytes. - local status = false local mess = 'FAIL' local sw = apdu:sub( #apdu-7, #apdu-4) desc, err = iso7816.tostring(sw) - print ('SW', sw, desc, err ) - - status = ( sw == '9000' ) - - return status + --print ('SW', sw, desc, err ) + local status = ( sw == '9000' ) + return status, desc, err end +local CLA = '94' local _calypso_cmds = { -- Break down of command bytes: @@ -177,27 +186,25 @@ local _calypso_cmds = { -- Electronic Purse file -- Electronic Transaction log file - - --['01.Select ICC file'] = '0294 a4 00 0002 3f00', - ['01.Select ICC file'] = '0294 a4 080004 3f00 0002', - ['02.ICC'] = '0394 b2 01 041d', - ['03.Select EnvHol file'] = '0294 a4 080004 2000 2001', - ['04.EnvHol1'] = '0394 b2 01 041d', - ['05.Select EvLog file'] = '0294 a4 080004 2000 2010', - ['06.EvLog1'] = '0394 b2 01 041d', - ['07.EvLog2'] = '0294 b2 02 041d', - ['08.EvLog3'] = '0394 b2 03 041d', - ['09.Select ConList file']= '0294 a4 080004 2000 2050', - ['10.ConList'] = '0394 b2 01 041d', - ['11.Select Contra file'] = '0294 a4 080004 2000 2020', - ['12.Contra1'] = '0394 b2 01 041d', - ['13.Contra2'] = '0294 b2 02 041d', - ['14.Contra3'] = '0394 b2 03 041d', - ['15.Contra4'] = '0294 b2 04 041d', - ['16.Select Counter file']= '0394 a4 080004 2000 2069', - ['17.Counter'] = '0294 b2 01 041d', - ['18.Select SpecEv file'] = '0394 a4 080004 2000 2040', - ['19.SpecEv1'] = '0294 b2 01 041d', + ['01.Select ICC file'] = CLA..'a4 080004 3f00 0002', + ['02.ICC'] = CLA..'b2 01 041d', + ['03.Select EnvHol file'] = CLA..'a4 080004 2000 2001', + ['04.EnvHol1'] = CLA..'b2 01 041d', + ['05.Select EvLog file'] = CLA..'a4 080004 2000 2010', + ['06.EvLog1'] = CLA..'b2 01 041d', + ['07.EvLog2'] = CLA..'b2 02 041d', + ['08.EvLog3'] = CLA..'b2 03 041d', + ['09.Select ConList file']= CLA..'a4 080004 2000 2050', + ['10.ConList'] = CLA..'b2 01 041d', + ['11.Select Contra file'] = CLA..'a4 080004 2000 2020', + ['12.Contra1'] = CLA..'b2 01 041d', + ['13.Contra2'] = CLA..'b2 02 041d', + ['14.Contra3'] = CLA..'b2 03 041d', + ['15.Contra4'] = CLA..'b2 04 041d', + ['16.Select Counter file']= CLA..'a4 080004 2000 2069', + ['17.Counter'] = CLA..'b2 01 041d', + ['18.Select SpecEv file'] = CLA..'a4 080004 2000 2040', + ['19.SpecEv1'] = CLA..'b2 01 041d', } --- @@ -215,7 +222,7 @@ function main(args) if o == 'b' then bytes = a end end - lib14b.connect() +-- lib14b.connect() -- Select 14b tag. card, err = lib14b.waitFor14443b() @@ -241,14 +248,23 @@ function main(args) --for i = 1,10 do --result, err = calypso_send_cmd_raw('0294a40800043f000002',false) --select ICC file for i, apdu in spairs(_calypso_cmds) do - print('>>', i ) + print('>> '..ansicolors.yellow..i..ansicolors.reset) apdu = apdu:gsub('%s+', '') result, err = calypso_send_cmd_raw(apdu , false) - if result then - calypso_apdu_status(result.data) - print('<<', result.data ) + if err then + print('<< '..err) else - print('<< no answer') + if result then + local status, desc, err = calypso_apdu_status(result.data) + local d = result.data:sub(3, (#result.data - 8)) + if status then + print('<< '..d..' ('..ansicolors.green..'ok'..ansicolors.reset..')') + else + print('<< '..d..' '..ansicolors.red..err..ansicolors.reset ) + end + else + print('<< no answer') + end end end lib14b.disconnect() diff --git a/client/luascripts/hf_14b_mobib.lua b/client/luascripts/hf_14b_mobib.lua new file mode 100644 index 000000000..01dd0f3a0 --- /dev/null +++ b/client/luascripts/hf_14b_mobib.lua @@ -0,0 +1,277 @@ +local cmds = require('commands') +local getopt = require('getopt') +local lib14b = require('read14b') +local utils = require('utils') +local iso7816 = require('7816_error') +local ansicolors = require('ansicolors') + +copyright = '' +author = 'Iceman' +version = 'v1.0.0' +desc = [[ +This is a script to communicate with a MOBIB tag using the '14b raw' commands +]] +example = [[ + script run hf_14b_mobib + script run hf_14b_mobib -b 11223344 + +]] +usage = [[ +script run hf_14b_mobib -h -b +]] +arguments = [[ + h this helptext + b raw bytes to send +]] + +--[[ +This script communicates with /armsrc/iso14443b.c, +Check there for details about data format and how commands are interpreted on the +device-side. +]] + +local function calypso_parse(result) + local r = Command.parse(result) + if r.arg1 >= 0 then + local len = r.arg1 * 2 + if len > 0 then + r.data = string.sub(r.data, 0, len); + return r, nil + end + end + return nil,nil +end +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR: ', err) + lib14b.disconnect() + return nil, err +end +--- +-- 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 +-- +-- helper function, give current count of items in lua-table. +local function tablelen(T) + local count = 0 + for _ in pairs(T) do count = count + 1 end + return count +end +--- +-- helper function, gives a sorted table from table t, +-- order can be a seperate sorting-order function. +local function spairs(t, order) + -- collect the keys + local keys = {} + for k in pairs(t) do keys[#keys+1] = k end + + -- if order function given, sort by it by passing the table and keys a, b, + -- otherwise just sort the keys + if order then + table.sort(keys, function(a,b) return order(t, a, b) end) + else + table.sort(keys) + end + + -- return the iterator function + local i = 0 + return function() + i = i + 1 + if keys[i] then + return keys[i], t[keys[i]] + end + end +end +--- +-- Sends a usbpackage , "hf 14b raw" +-- if it reads the response, it converts it to a lua object "Command" first and the Data is cut to correct length. +local function calypso_send_cmd_raw(data, ignoreresponse ) + + local command, flags, result, err + flags = lib14b.ISO14B_COMMAND.ISO14B_APDU + + data = data or "00" + + command = Command:newMIX{ + cmd = cmds.CMD_HF_ISO14443B_COMMAND, + arg1 = flags, + arg2 = #data/2, -- LEN of data, half the length of the ASCII-string hex string + data = data} -- data bytes (commands etc) + + local use_cmd_ack = true + result, err = command:sendMIX(ignoreresponse, 2000, use_cmd_ack) + if result then + local count,cmd,arg0,arg1,arg2 = bin.unpack('LLLL', result) + if arg0 >= 0 then + return calypso_parse(result) + else + err = 'card response failed' + end + else + err = 'No response from card' + end + return result, err +end +--- +-- calypso_card_num : Reads card number from ATR and +-- 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 ) + print('') + print('Card UID ' ..ansicolors.green..card.uid:format('%x')..ansicolors.reset) + print('Card Number ' ..ansicolors.green..string.format('%u', card_num)..ansicolors.reset) + print('-----------------------') +end +--- +-- analyse CALYPSO apdu status bytes. +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) + desc, err = iso7816.tostring(sw) + --print ('SW', sw, desc, err ) + local status = ( sw == '9000' ) + return status, desc, err +end + +local CLA = '00' +local _calypso_cmds = { + ['01.SELECT AID 1TIC.ICA'] = CLA..'a4 0400 08 315449432e494341', + ['02.Select ICC file a'] = CLA..'a4 0000 02 3f00', + ['03.Select ICC file b'] = CLA..'a4 0000 02 0002', + ['04.ICC'] = CLA..'b2 0104 1d', + ['05.Select Holder file'] = CLA..'a4 0000 02 3f1c', + ['06.Holder1'] = CLA..'b2 0104 1d', + ['07.Holder2'] = CLA..'b2 0204 1d', + ['08.Select EnvHol file a'] = CLA..'a4 0000 00', + ['09.Select EnvHol file b'] = CLA..'a4 0000 02 2000', + ['10.Select EnvHol file c'] = CLA..'a4 0000 02 2001', + ['11.EnvHol1'] = CLA..'b2 0104 1d', + ['11.EnvHol2'] = CLA..'b2 0204 1d', + ['12.Select EvLog file'] = CLA..'a4 0000 02 2010', + ['13.EvLog1'] = CLA..'b2 0104 1d', + ['14.EvLog2'] = CLA..'b2 0204 1d', + ['15.EvLog3'] = CLA..'b2 0304 1d', + ['16.Select ConList file'] = CLA..'a4 0000 02 2050', + ['17.ConList'] = CLA..'b2 0104 1d', + ['18.Select Contra file'] = CLA..'a4 0000 02 2020', + ['19.Contra1'] = CLA..'b2 0104 1d', + ['20.Contra2'] = CLA..'b2 0204 1d', + ['21.Contra3'] = CLA..'b2 0304 1d', + ['22.Contra4'] = CLA..'b2 0404 1d', + ['23.Contra5'] = CLA..'b2 0504 1d', + ['24.Contra6'] = CLA..'b2 0604 1d', + ['25.Contra7'] = CLA..'b2 0704 1d', + ['26.Contra8'] = CLA..'b2 0804 1d', + ['27.Contra9'] = CLA..'b2 0904 1d', + ['28.ContraA'] = CLA..'b2 0a04 1d', + ['29.ContraB'] = CLA..'b2 0b04 1d', + ['30.ContraC'] = CLA..'b2 0c04 1d', + ['31.Select Counter file'] = CLA..'a4 0000 02 2069', + ['32.Counter'] = CLA..'b2 0104 1d', + ['33.Select LoadLog file a'] = CLA..'a4 0000 00', + ['34.Select LoadLog file b'] = CLA..'a4 0000 02 1000', + ['35.Select LoadLog file c'] = CLA..'a4 0000 02 1014', + ['36.LoadLog'] = CLA..'b2 0104 1d', + ['37.Select Purcha file'] = CLA..'a4 0000 02 1015', + ['38.Purcha1'] = CLA..'b2 0104 1d', + ['39.Purcha2'] = CLA..'b2 0204 1d', + ['40.Purcha3'] = CLA..'b2 0304 1d', + ['41.Select SpecEv file a'] = CLA..'a4 0000 00', + ['42.Select SpecEv file b'] = CLA..'a4 0000 02 2000', + ['43.Select SpecEv file c'] = CLA..'a4 0000 02 2040', + ['44.SpecEv1'] = CLA..'b2 0104 1d', + ['45.SpecEv2'] = CLA..'b2 0204 1d', + ['46.SpecEv3'] = CLA..'b2 0304 1d', + ['47.SpecEv4'] = CLA..'b2 0404 1d', +} + +--- +-- The main entry point +function main(args) + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + local data, apdu, flags, uid, cid, result, err, card + -- Read the parameters + for o, a in getopt.getopt(args, 'h') do + if o == 'h' then return help() end + if o == 'b' then bytes = a end + end + +-- lib14b.connect() + + -- Select 14b tag. + card, err = lib14b.waitFor14443b() + if not card then return oops(err) end + + calypso_card_num(card) + cid = card.cid + + for i, apdu in spairs(_calypso_cmds) do + print('>> '..ansicolors.yellow..i..ansicolors.reset) + apdu = apdu:gsub('%s+', '') + result, err = calypso_send_cmd_raw(apdu , false) + if err then + print('<< '..err) + else + if result then + local status, desc, err = calypso_apdu_status(result.data) + local d = result.data:sub(3, (#result.data - 8)) + if status then + print('<< '..d..' ('..ansicolors.green..'ok'..ansicolors.reset..')') + else + print('<< '..d..' '..ansicolors.red..err..ansicolors.reset ) + end + else + print('<< no answer') + end + end + end + lib14b.disconnect() +end +--- +-- a simple selftest function, tries to convert +function selftest() + DEBUG = true + dbg('Performing test') + dbg('Tests done') +end +-- Flip the switch here to perform a sanity check. +-- It read a nonce in two different ways, as specified in the usage-section +if '--test'==args then + selftest() +else + -- Call the main + main(args) +end diff --git a/client/luascripts/iso15_magic.lua b/client/luascripts/hf_15_magic.lua similarity index 95% rename from client/luascripts/iso15_magic.lua rename to client/luascripts/hf_15_magic.lua index 94f1d8f97..173cf1ee7 100644 --- a/client/luascripts/iso15_magic.lua +++ b/client/luascripts/hf_15_magic.lua @@ -15,12 +15,12 @@ example = [[ -- ISO15693 slix magic tag - script run iso15_magic -u E004013344556677 + script run hf_15_magic -u E004013344556677 - script run iso15_magic -u E004013344556677 -a + script run hf_15_magic -u E004013344556677 -a ]] usage = [[ -script run iso15_magic -h -u +script run hf_15_magic -h -u ]] arguments = [[ -h : this help diff --git a/client/luascripts/legic.lua b/client/luascripts/hf_legic.lua similarity index 99% rename from client/luascripts/legic.lua rename to client/luascripts/hf_legic.lua index 393cc9e52..1715bf400 100644 --- a/client/luascripts/legic.lua +++ b/client/luascripts/hf_legic.lua @@ -96,7 +96,7 @@ 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 legic" +example = "script run hf_legic" author = "Mosci, uhei" version = "1.0.4" diff --git a/client/luascripts/legic_buffer2card.lua b/client/luascripts/hf_legic_buffer2card.lua similarity index 95% rename from client/luascripts/legic_buffer2card.lua rename to client/luascripts/hf_legic_buffer2card.lua index d0a018d83..eac8f1ed2 100644 --- a/client/luascripts/legic_buffer2card.lua +++ b/client/luascripts/hf_legic_buffer2card.lua @@ -11,10 +11,10 @@ desc = This is a script which writes value 0x01 to bytes from position 0x07 until 0xFF on a Legic Prime Tag (MIM256 or MIM1024) -- (created with 'hf legic save my_dump.hex') -- ]] example = [[ - script run legic_buffer2card + script run hf_legic_buffer2card ]] usage = [[ -script run legic_buffer2card -h +script run hf_legic_buffer2card -h ]] arguments = [[ -h - Help text diff --git a/client/luascripts/legic_clone.lua b/client/luascripts/hf_legic_clone.lua similarity index 98% rename from client/luascripts/legic_clone.lua rename to client/luascripts/hf_legic_clone.lua index 0f03157e5..28a95bca7 100644 --- a/client/luascripts/legic_clone.lua +++ b/client/luascripts/hf_legic_clone.lua @@ -18,7 +18,7 @@ local ansicolors = require('ansicolors') simplest usage: Dump a legic tag with 'hf legic dump' place your 'empty' tag on the reader and run - 'script run legic_clone -i orig.bin -w' + 'script run hf_legic_clone -i orig.bin -w' you will see some output like: @@ -95,11 +95,11 @@ This is a script which creates a clone-dump of a dump from a LEGIC Prime Tag (MI Create a dump by running `hf legic dump`. ]] example = [[ - script run legic_clone -i my_dump.bin -o my_clone.bin -c f8 - script run legic_clone -i my_dump.bin -d -s + script run hf_legic_clone -i my_dump.bin -o my_clone.bin -c f8 + script run hf_legic_clone -i my_dump.bin -d -s ]] usage = [[ -script run legic_clone [-h] [-i ] [-o ] [-c ] [-d] [-s] [-w] +script run hf_legic_clone [-h] [-i ] [-o ] [-c ] [-d] [-s] [-w] ]] arguments = [[ required : diff --git a/client/luascripts/mifare_autopwn.lua b/client/luascripts/hf_mf_autopwn.lua similarity index 97% rename from client/luascripts/mifare_autopwn.lua rename to client/luascripts/hf_mf_autopwn.lua index 6ff40a70d..f7ea2a832 100644 --- a/client/luascripts/mifare_autopwn.lua +++ b/client/luascripts/hf_mf_autopwn.lua @@ -13,10 +13,10 @@ This is a script which automates cracking and dumping mifare classic cards. It s place by the device. ]] example = [[ - 1. script run mifare_autopwn + 1. script run hf_mf_autopwn ]] usage = [[ -script run mifare_autopwn [-h] [-d] [-k ] +script run hf_mf_autopwn [-h] [-d] [-k ] ]] arguments = [[ -h this help @@ -128,10 +128,10 @@ local function dump_tag(uid, numsectors) -- Save the global args, those are *our* arguments local myargs = args - -- Set the arguments for htmldump script + -- Set the arguments for data_mf_bin2html script args =('-i %s.bin -o %s.html'):format(dumpfile, dumpfile) -- call it - require('htmldump') + require('data_mf_bin2html') -- Set back args. Not that it's used, just for the karma... args = myargs diff --git a/client/luascripts/hf_mf_dump-luxeo.lua b/client/luascripts/hf_mf_dump-luxeo.lua new file mode 100644 index 000000000..70f1b42e0 --- /dev/null +++ b/client/luascripts/hf_mf_dump-luxeo.lua @@ -0,0 +1,300 @@ +--- +-- This Lua script is designed to run with Iceman/RRG Proxmark3 fork +-- Just copy hf_mf_dump-luxeo.lua to client/luascripts/ +-- and run "script run hf_mf_dump-luxeo" + +-- requirements +local cmds = require('commands') +local getopt = require('getopt') +local utils = require('utils') +local lib14a = require('read14a') +local ansicolors = require('ansicolors') + +copyright = '' +author = '0xdrrb' +version = 'v0.1.1' +desc = [[ +This is a script that tries to dump and decrypt the data of a specific type of Mifare laundromat token. OBS! Tag must be on the antenna. +]] +example = [[ + script run hf_mf_dump-luxeo +]] +usage = [[ +script run hf_mf_dump-luxeo +]] +arguments = [[ + -h This help +]] +local PM3_SUCCESS = 0 + +-- Some shortcuts +local band = bit32.band +local bor = bit32.bor +local bnot = bit32.bnot +local bxor = bit32.bxor +local lsh = bit32.lshift +local rsh = bit32.rshift + +local acgreen = ansicolors.bright..ansicolors.green +local accyan = ansicolors.bright..ansicolors.cyan +local acred = ansicolors.red +local acyellow = ansicolors.bright..ansicolors.yellow +local acblue = ansicolors.bright..ansicolors.blue +local acmagenta = ansicolors.bright..ansicolors.magenta +local acoff = ansicolors.reset + + +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR: ', err) + core.clearCommandBuffer() + return nil, err +end +--- +-- 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 +--- +-- +local function setdevicedebug( status ) + local c = 'hw dbg ' + if status then + c = c..'1' + else + c = c..'0' + end + core.console(c) +end + +local function xteaCrypt(num_rounds, v, key) + local v0 = v[0] + local v1 = v[1] + local delta = 0x9E3779B9 + local sum = 0 + + for i = 0, num_rounds-1 do + -- v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); + v0 = band(bxor(bxor(lsh(v1,4), rsh(v1,5)) + v1, sum + key[band(sum,3)]) + v0, 0xFFFFFFFF) + sum = band(sum + delta, 0xFFFFFFFF) + -- v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]); + v1 = band(bxor(bxor(lsh(v0,4), rsh(v0,5)) + v0, sum + key[band(rsh(sum,11),3)]) + v1, 0xFFFFFFFF) + end + v[0] = v0 + v[1] = v1 +end + +local function xteaDecrypt(num_rounds, v, key) + local v0 = v[0] + local v1 = v[1] + local delta = 0x9E3779B9 + local sum = band(delta * num_rounds, 0xFFFFFFFF) + + for i = 0, num_rounds-1 do + -- v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]); + v1 = band(v1 - bxor(bxor(lsh(v0,4), rsh(v0,5)) + v0, sum + key[band(rsh(sum,11),3)]), 0xFFFFFFFF) + sum = band(sum - delta, 0xFFFFFFFF) + -- v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); + v0 = band(v0 - bxor(bxor(lsh(v1,4), rsh(v1,5)) + v1, sum + key[band(sum,3)]), 0xFFFFFFFF) + end + v[0] = v0 + v[1] = v1 +end + +local function createxteakey(mfuid) + local xteakey = {} + local buid = {} + local tmpkey = {} + local uid = {} + + -- Warning ! "it is customary in Lua to START ARRAYS WITH ONE" + buid = utils.ConvertHexToBytes(mfuid) + uid[0] = bor(buid[1], lsh(buid[2], 8)) + uid[1] = bor(buid[3], lsh(buid[4], 8)) + + tmpkey[0] = 0x198B + tmpkey[1] = uid[0] + tmpkey[2] = 0x46D8 + tmpkey[3] = uid[1] + tmpkey[4] = 0x5310 + tmpkey[5] = bxor(uid[0], 0xA312) + tmpkey[6] = 0xFFCB + tmpkey[7] = bxor(uid[1], 0x55AA) + + xteakey[0] = bor(lsh(tmpkey[1], 16), tmpkey[0]) + xteakey[1] = bor(lsh(tmpkey[3], 16), tmpkey[2]) + xteakey[2] = bor(lsh(tmpkey[5], 16), tmpkey[4]) + xteakey[3] = bor(lsh(tmpkey[7], 16), tmpkey[6]) + + return xteakey +end + +local function getblockdata(response) + if not response then + return nil, 'No response from device' + end + if response.Status == PM3_SUCCESS then + return response.Data + else + return nil, "Couldn't read block.. ["..response.Status.."]" + end +end + +local function readblock(blockno, key) + -- Read block N + local keytype = '01' -- key B + local data = ('%02x%s%s'):format(blockno, keytype, key) + local c = Command:newNG{cmd = cmds.CMD_HF_MIFARE_READBL, data = data} + local b, err = getblockdata(c:sendNG(false)) + if not b then return oops(err) end + return b +end + +local function readtag(mfkey,xteakey) + local tagdata = {} + local cleardata = {} + local v = {} + local vv = {} + + -- Read 4 sectors and build table + for sect = 8, 11 do + for blockn = sect * 4, (sect * 4) + 2 do + local blockdata = readblock(blockn, mfkey) + if not blockdata then return oops('[!] failed reading block') end + table.insert(tagdata, blockdata) + end + end + + -- Decrypt data and build clear table + for key,value in ipairs(tagdata) do + local clearblockdata + v[0] = utils.SwapEndianness(value:sub(1, 8), 32) + v[1] = utils.SwapEndianness(value:sub(9, 16), 32) + xteaDecrypt(16, v, xteakey) + vv[0] = utils.SwapEndianness(value:sub(17, 24), 32) + vv[1] = utils.SwapEndianness(value:sub(25, 32), 32) + xteaDecrypt(16, vv, xteakey) + clearblockdata=string.format("%08X%08X%08X%08X", + utils.SwapEndianness(string.format("%08X", v[0]), 32), + utils.SwapEndianness(string.format("%08X", v[1]), 32), + utils.SwapEndianness(string.format("%08X", vv[0]), 32), + utils.SwapEndianness(string.format("%08X", vv[1]), 32)) + table.insert(cleardata, clearblockdata) + end + + return tagdata,cleardata + +end + + +local function main(args) + + -- Arguments for the script + for o, a in getopt.getopt(args, 'h') do + if o == 'h' then return help() end + end + + local xteakey = {} + -- local v = {} + local edata = {} + local cdata = {} + + -- Turn off Debug + setdevicedebug(false) + + -- GET TAG UID + tag, err = lib14a.read(false, true) + if err then + lib14a.disconnect() + return oops(err) + end + core.clearCommandBuffer() + + -- simple tag check + if 0x08 ~= tag.sak then + if 0x0400 ~= tag.atqa then + return oops(('[fail] found tag %s :: looking for Mifare S50 1k'):format(tag.name)) + end + end + + xteakey = createxteakey(tag.uid) + 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) + + if edata == nil or cdata == nil then + print("ERROR Reading tag!") + return nil + end + + print("Ciphered data:") + for key,value in ipairs(edata) do + print(value) + if key % 3 == 0 then print("") end + end + + -- compute CRC for each segment + crcH = utils.SwapEndianness(core.reveng_runmodel("CRC-16/ARC", cdata[1]..cdata[2]..cdata[3]:sub(1,28), false, '0'),16) + crcA = utils.SwapEndianness(core.reveng_runmodel("CRC-16/ARC", cdata[4]..cdata[5]..cdata[6]..cdata[7]:sub(1,28), false, '0'),16) + crcB = utils.SwapEndianness(core.reveng_runmodel("CRC-16/ARC", cdata[8]..cdata[9]..cdata[10]..cdata[11]:sub(1,28), false, '0'),16) + + print("\nHeader:") + for key,value in ipairs(cdata) do + if key == 3 then + print(value:sub(1,28)..acmagenta..value:sub(29,32)..acoff) + if utils.SwapEndianness(value:sub(29,32),16) == crcH then strcrc = " OK" else strcrc = acred.." CRCERROR !!" end + print(acmagenta.."CRC16/ARC = "..string.format("0x%04X", crcH)..strcrc..acoff) + print("\nDataA:") + elseif key == 4 then + print(acgreen..value:sub(1,4)..acoff..value:sub(5,16)..accyan..value:sub(17,24)..acoff..value:sub(25,26)..accyan..value:sub(27,28)..acoff..value:sub(29,32)) + versionA = utils.SwapEndianness(value:sub(1,4),16) + dateA = string.format("%d/%02d/%02d %02d:%02d", tonumber(value:sub(17,18),10)+2000, tonumber(value:sub(19,20),10), + tonumber(string.format("%02X", band(tonumber(value:sub(21,22),16),0x3f)),10), + tonumber(value:sub(23,24),10), tonumber(value:sub(27,28),10)) + elseif key == 8 then + print(acgreen..value:sub(1,4)..acoff..value:sub(5,16)..accyan..value:sub(17,24)..acoff..value:sub(25,26)..accyan..value:sub(27,28)..acoff..value:sub(29,32)) + versionB = utils.SwapEndianness(value:sub(1,4),16) + dateB = string.format("%d/%02d/%02d %02d:%02d", tonumber(value:sub(17,18),10)+2000, tonumber(value:sub(19,20),10), + tonumber(string.format("%02X", band(tonumber(value:sub(21,22),16),0x3f)),10), + tonumber(value:sub(23,24),10), tonumber(value:sub(27,28),10)) + elseif key == 5 then + print(acyellow..value:sub(1,4)..acoff..value:sub(5,32)) + creditA = utils.SwapEndianness(value:sub(1,4),16)/100 + elseif key == 9 then + print(acyellow..value:sub(1,4)..acoff..value:sub(5,32)) + creditB = utils.SwapEndianness(value:sub(1,4),16)/100 + elseif key == 7 then + print(value:sub(1,28)..acmagenta..value:sub(29,32)..acoff) + print(acgreen.."Version "..string.format("0x%04X", versionA)..acoff) + print(acyellow.."Credit : "..creditA..acoff) + if utils.SwapEndianness(value:sub(29,32),16) == crcA then strcrc = " OK" else strcrc = acred.." CRCERROR !!" end + print(acmagenta.."CRC16/ARC = "..string.format("0x%04X", crcA)..strcrc..acoff) + print(accyan.."Date: "..dateA..acoff) + print("\nDataB:") + elseif key == 11 then + print(value:sub(1,28)..acmagenta..value:sub(29,32)..acoff) + print(acgreen.."Version "..string.format("0x%04X", versionB)..acoff) + print(acyellow.."Credit : "..creditB..acoff) + if utils.SwapEndianness(value:sub(29,32),16) == crcB then strcrc = " OK" else strcrc = acred.." CRCERROR !!" end + print(acmagenta.."CRC16/ARC = "..string.format("0x%04X", crcB)..strcrc..acoff) + print(accyan.."Date: "..dateB..acoff) + print("\nFooter:") + else + print(value) + end + end + + return +end + +main(args) diff --git a/client/luascripts/formatMifare.lua b/client/luascripts/hf_mf_format.lua similarity index 97% rename from client/luascripts/formatMifare.lua rename to client/luascripts/hf_mf_format.lua index 30ab20535..d949c25f2 100644 --- a/client/luascripts/formatMifare.lua +++ b/client/luascripts/hf_mf_format.lua @@ -20,16 +20,16 @@ The script will skip the manufactoring block 0. ]] example = [[ -- generate commands - 1. script run formatMifare + 1. script run hf_mf_format -- generate command, replacing key with new key. - 2. script run formatMifare -k aabbccddeeff -n 112233445566 -a FF0780 + 2. script run hf_mf_format -k aabbccddeeff -n 112233445566 -a FF0780 -- generate commands and execute them against card. - 3. script run formatMifare -x + 3. script run hf_mf_format -x ]] usage = [[ -script run formatMifare -k -n -a -x +script run hf_mf_format -k -n -a -x ]] arguments = [[ -h - this help diff --git a/client/luascripts/hf_mf_gen3_writer.lua b/client/luascripts/hf_mf_gen3_writer.lua new file mode 100644 index 000000000..1257f3413 --- /dev/null +++ b/client/luascripts/hf_mf_gen3_writer.lua @@ -0,0 +1,430 @@ +local utils = require('utils') +local getopt = require('getopt') +local cmds = require('commands') +local read14a = require('read14a') +-- +--- +------------------------------- +-- Notes +------------------------------- +--- +-- +--[[ +---Suggestions of improvement: +--- Add support another types of dumps: BIN, JSON +--- Maybe it will be not only as `hf_mf_gen3_writer`, like a universal dump manager. +--- Hide system messages when you writing a dumps, replace it to some of like [#####----------] 40% + +-- iceman notes: +-- doesn't take consideration filepaths for dump files. +-- doesn't allow A keys for authenticating when writing +-- doesn't verify that card is magic gen3. +-- doesn't take several versions of same dump ( -1, -2, -3 ) styles. +--]] +-- +--- +------------------------------- +-- Script hat +------------------------------- +--- +-- +copyright = 'RRG Team' +author = 'Winds' +version = 'v1.0.0' +desc = [[ + This script gives you an easy way to write your *.eml dumps into normal MIFARE Classic and Magic Gen3 cards. + + Works with both 4 and 7 bytes NXP MIFARE Classic 1K cards. + The script also has the possibility to change UID and permanent lock uid on magic Gen3 cards. + + It supports the following functionality. + + 1. Write it to the same of current card UID. + 2. Write it to magic Gen3 card. + 3. Change uid to match dump on magic Gen3 card. + 4. Permanent lock UID on magic Gen3 card. + 5. Erase all data at the card and set the FF FF FF FF FF FF keys, and Access Conditions to 78778800. + + Script works in a wizard styled way. + + Author Youtube channel: https://yev.ooo/ + + Many Thanks, + Best Regards +]] +example = [[ + 1. script run mfc_gen3_writer +]] +usage = [[ + Give script to know if you uses an Windows OS + Select your *.eml dump from list to write to the card. + Follow the wizard. +]] +-- +--- +------------------------------- +-- Global variables +------------------------------- +--- +-- +local DEBUG = false -- the debug flag +local dumpEML -- Find all *.EML files +local files = {} -- Array for eml files +local b_keys = {} -- Array for B keys +local eml = {} -- Array for data in block 32 +local num_dumps = 0 -- num of found eml dump files +local tab = string.rep('-', 64) +local empty = string.rep('0', 32) -- Writing blocks +local default_key = 'FFFFFFFFFFFF' -- Writing blocks +local default_key_type = '01' --KeyA: 00, KeyB: 01 +local default_key_blk = 'FFFFFFFFFFFF7C378800FFFFFFFFFFFF' -- Writing blocks +local piswords_uid_lock = 'hf 14a raw -s -c -t 2000 90fd111100' +local piswords_uid_change = 'hf 14a raw -s -c -t 2000 90f0cccc10' +local cmd_wrbl_a = 'hf mf wrbl %d A %s %s' -- Writing blocks by A key +local cmd_wrbl_b = 'hf mf wrbl %d B %s %s' -- Writing blocks by B key +-- +--- +------------------------------- +-- A debug printout-function +------------------------------- +--- +-- +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end +-- +--- +------------------------------- +-- This is only meant to be used when errors occur +------------------------------- +--- +-- +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, err +end +-- +--- +------------------------------- +-- Usage help +------------------------------- +--- +-- +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print('Example usage') + print(example) + print(usage) +end +-- +--- +------------------------------- +-- GetUID +------------------------------- +--- +-- +local function GetUID() + return read14a.read(true, true).uid +end +-- +local function dropfield() + read14a.disconnect() + core.clearCommandBuffer() +end +-- +--- +------------------------------- +-- Wait for tag (MFC) +------------------------------- +--- +-- +local function wait() + read14a.waitFor14443a() +end +-- +--- +------------------------------- +-- Return key code 00/01 to string +------------------------------- +--- +-- +local function KeyAB() + if default_key_type == '00' then + return 'KeyA' + else + return 'KeyB' + end +end +-- +--- +------------------------------- +-- Check response from Proxmark +------------------------------- +--- +-- +local function getblockdata(response) + if response.Status == 0 then + return true + else + return false + end +end +-- +--- +------------------------------- +-- Check 0xFFFFFFFFFFFF key for tag (MFC) +------------------------------- +--- +-- +local function checkkey() + local status = 0 + for i = 1, #eml do + cmd = Command:newNG{cmd = cmds.CMD_HF_MIFARE_READBL, data = ('%02x%02x%s'):format((i-1), default_key_type, default_key)} + if (getblockdata(cmd:sendNG(false)) == true) then + status = status + 1 + print(('%s %02s %s %s %s'):format(' ', (i-1), KeyAB(), default_key, 'OK')) + else + break + end + end + if status == #eml then + return true + end +end +-- +--- +------------------------------- +-- Check user input A or B for blank tag (MFC) +------------------------------- +--- +-- +local function check_user_key(user_key_type) + if user_key_type == 'A' then + return cmd_wrbl_a + elseif user_key_type == 'B' then + return cmd_wrbl_b + end +end +-- +--- +------------------------------- +-- Main function +------------------------------- +--- +-- +local function main(args) + -- + --- + ------------------------------- + -- Arguments for script + ------------------------------- + --- + -- + for o, a in getopt.getopt(args, 'hd') do + if o == 'h' then return help() end + if o == 'd' then DEBUG = true end + end + -- + wait() + print(tab) + -- + --- + ------------------------------- + -- Detect 7/4 byte card + ------------------------------- + --- + -- + if (utils.confirm(' Are you use a Windwos OS ?') == true) then + dumpEML = 'find "." "*dump.eml"' + if string.len(GetUID()) == 14 then + eml_file_uid_start = 18 + eml_file_uid_end = 31 + eml_file_lengt = 40 + else + eml_file_uid_start = 18 + eml_file_uid_end = 25 + eml_file_lengt = 34 + end + else + dumpEML = "find '.' -iname '*dump.eml' -type f" + if string.len(GetUID()) == 14 then + eml_file_uid_start = 9 + eml_file_uid_end = 22 + eml_file_lengt = 31 + else + eml_file_uid_start = 9 + eml_file_uid_end = 16 + eml_file_lengt = 25 + end + end + print(tab) + dropfield() + -- + --- + ------------------------------- + -- List all EML files in /client + ------------------------------- + --- + -- + local p = assert(io.popen(dumpEML)) + for _ in p:lines() do + -- The length of eml file + if string.len(_) == eml_file_lengt then + num_dumps = num_dumps + 1 + -- cut UID from eml file + files[num_dumps] = string.sub(_, eml_file_uid_start, eml_file_uid_end) -- cut numeretic UID + print(' '..num_dumps..' | '..files[num_dumps]) + end + end + -- + p.close() + -- + if num_dumps == 0 then return oops("Didn't find any dump files") end + -- + print(tab) + print(' Your card has UID '..GetUID()) + print('') + print(' Select which dump to write (1 until '..num_dumps..')') + print(tab) + io.write(' --> ') + -- + local uid_no = tonumber(io.read()) + print(tab) + print(' You have been selected card dump No ' .. uid_no .. ', with UID: ' .. files[uid_no] .. '. Your card UID: ' .. GetUID()) + -- + -- + --- + ------------------------------- + -- Load eml file + ------------------------------- + --- + -- + local dumpfile = assert(io.open('hf-mf-' .. files[uid_no] .. '-dump.eml', 'r')) + for _ in dumpfile:lines() do table.insert(eml, _); end + dumpfile.close() + -- + --- + ------------------------------- + -- Extract B key from EML file + ------------------------------- + --- + -- + local b = 0 + for i = 1, #eml do + if (i % 4 == 0) then + repeat + b = b + 1 + -- Cut key from block + b_keys[b] = string.sub(eml[i], (#eml[i] - 11), #eml[i]) + until b % 4 == 0 + end + end + print(tab) + dbg(b_keys) + dbg(eml) + -- + --- + ------------------------------- + -- Change UID on certain version of magic Gen3 card. + ------------------------------- + --- + -- + if (utils.confirm(' Change UID ?') == true) then + wait() + core.console(piswords_uid_change .. tostring(eml[1])) + print(tab) + print(' The new card UID : ' .. GetUID()) + end + print(tab) + -- + --- + ------------------------------- + -- Lock UID + ------------------------------- + --- + -- + if (utils.confirm(' Permanent lock UID ? (card can never change uid again) ') == true) then + wait() + core.console(piswords_uid_lock) + end + -- + print(tab) + print(' Going to check the all ' .. KeyAB() .. ' by ' .. default_key) + print(tab) + -- + if checkkey() == true then + print(tab) + if (utils.confirm(' Card is Empty. Write selected dump to card ?') == true) then + for i = 1, #eml do + core.console(string.format(cmd_wrbl_b, (i-1), default_key, eml[i])) + end + end + else + print(tab) + if (utils.confirm(' It this is a new blank card ? Do you wishing to change Access Conditions to using B key ' .. default_key .. ' as main ?') == true) then + print(tab) + print(' With one key type we will use, A or B ?') + print(tab) + io.write(' --> ') + local user_key_type = tostring(io.read()) + print(tab) + print(' Enter 12 HEX chars of the key for access to card. By default ' .. default_key .. '.') + print(tab) + io.write(' --> ') + local user_key_input = tostring(io.read()) + wait() + for i = 1, #eml do + if (i % 4 == 0) then + core.console(string.format(check_user_key(user_key_type), (i-1), user_key_input, default_key_blk)) + else + core.console(string.format(check_user_key(user_key_type), (i-1), user_key_input, empty)) + end + end + else + print(tab) + if (utils.confirm(' Write selected dump to card ?') == true) then + print(tab) + wait() + for i = 1, #eml do + core.console(string.format(cmd_wrbl_b, (i-1), b_keys[i], eml[i])) + end + else + print(tab) + if (utils.confirm(' Delete ALL data and write all keys to 0x' .. default_key .. ' ?') == true) then + wait() + for i = 1, #eml do + if (i % 4 == 0) then + core.console(string.format(cmd_wrbl_b, (i-1), b_keys[i], default_key_blk)) + else + core.console(string.format(cmd_wrbl_b, (i-1), b_keys[i], empty)) + end + end + end + end + end + end + dropfield() + print(tab) + print('You are welcome') +end +-- +--- +------------------------------- +-- Start Main function +------------------------------- +--- +-- +main(args) diff --git a/client/luascripts/mfckeys.lua b/client/luascripts/hf_mf_keycheck.lua similarity index 97% rename from client/luascripts/mfckeys.lua rename to client/luascripts/hf_mf_keycheck.lua index bf0cccb45..59680cbe4 100644 --- a/client/luascripts/mfckeys.lua +++ b/client/luascripts/hf_mf_keycheck.lua @@ -22,10 +22,10 @@ desc = ("This script implements Mifare check keys.\ It utilises a large list of default keys (currently %d keys).\ If you want to add more, just put them inside /lualibs/mfc_default_keys.lua\n"):format(#keylist) example = [[ - 1. script run mfckeys + 1. script run hf_mf_keycheck ]] usage = [[ -script run mfckeys [-p] +script run hf_mf_keycheck [-p] ]] arguments = [[ -h : this help @@ -60,7 +60,7 @@ end -- waits for answer from pm3 device local function checkCommand(response) if not response then - print("Timeout while waiting for response. Increase TIMEOUT in mfckeys.lua to wait longer") + print("Timeout while waiting for response. Increase TIMEOUT in hf_mf_keycheck.lua to wait longer") return nil, "Timeout while waiting for device to respond" end @@ -237,7 +237,7 @@ local function perform_check(uid, numsectors) local end_time = os.time() print('') - print('[+] mfckeys - Checkkey execution time: '..os.difftime(end_time, start_time)..' sec') + print('[+] hf_mf_keycheck - Checkkey execution time: '..os.difftime(end_time, start_time)..' sec') core.fast_push_mode(false) diff --git a/client/luascripts/remagic.lua b/client/luascripts/hf_mf_magicrevive.lua similarity index 82% rename from client/luascripts/remagic.lua rename to client/luascripts/hf_mf_magicrevive.lua index 51aa0be40..9354b0f96 100644 --- a/client/luascripts/remagic.lua +++ b/client/luascripts/hf_mf_magicrevive.lua @@ -11,15 +11,15 @@ or mifare Ultralight magic card which answers to chinese backdoor commands ]] example = [[ -- target a Ultralight based card - 1. script run remagic -u + 1. script run hf_mf_magicrevive -u ]] usage = [[ -script run remagic [-h] [-u] +script run hf_mf_magicrevive [-h] [-u] ]] arguments = [[ -h this help - -u remagic a Ultralight tag w 7 bytes UID. + -u try to revive a bricked magic Ultralight tag w 7 bytes UID. ]] --- -- A debug printout-function @@ -59,24 +59,24 @@ end local function cmdUltralight() return { - [0] = 'hf 14a raw -p -a -b 7 40', - [1] = 'hf 14a raw -p -a 43', + [0] = 'hf 14a raw -k -a -b 7 40', + [1] = 'hf 14a raw -k -a 43', [2] = 'hf 14a raw -c -a A2005380712A', - [3] = 'hf 14a raw -p -a -b 7 40', - [4] = 'hf 14a raw -p -a 43', + [3] = 'hf 14a raw -k -a -b 7 40', + [4] = 'hf 14a raw -k -a 43', [5] = 'hf 14a raw -c -a A2010200D980', - [6] = 'hf 14a raw -p -a -b 7 40', - [7] = 'hf 14a raw -p -a 43', + [6] = 'hf 14a raw -k -a -b 7 40', + [7] = 'hf 14a raw -k -a 43', [8] = 'hf 14a raw -c -a A2025B480000', [9] = 'hf 14a raw -c -a 5000', } end local function cmdClassic() return { - [0] = 'hf 14a raw -p -a -b 7 40', - [1] = 'hf 14a raw -p -a 43', - [2] = 'hf 14a raw -c -p -a A000', - [3] = 'hf 14a raw -c -p -a 01020304049802000000000000001001', + [0] = 'hf 14a raw -k -a -b 7 40', + [1] = 'hf 14a raw -k -a 43', + [2] = 'hf 14a raw -c -k -a A000', + [3] = 'hf 14a raw -c -k -a 01020304049802000000000000001001', [4] = 'hf 14a raw -c -a 5000', } end diff --git a/client/luascripts/didump.lua b/client/luascripts/hf_mf_mini_dumpdecrypt.lua similarity index 98% rename from client/luascripts/didump.lua rename to client/luascripts/hf_mf_mini_dumpdecrypt.lua index 39207151e..ec6006ed2 100644 --- a/client/luascripts/didump.lua +++ b/client/luascripts/hf_mf_mini_dumpdecrypt.lua @@ -16,19 +16,19 @@ This is a script to dump and decrypt the data of a specific type of Mifare Mini The dump is decrypted. If a raw dump is wanted, use the -r parameter ]] example = [[ - script run didump + script run hf_mf_mini_dumpdecrypt -- selftest - script run didump -t + script run hf_mf_mini_dumpdecrypt -t -- Generate raw dump, into json. - script run didump -r + script run hf_mf_mini_dumpdecrypt -r -- load file - script run didump -i dumpdata.json + script run hf_mf_mini_dumpdecrypt -i dumpdata.json ]] usage = [[ -script run didump -h -t -r -d -e -v -i dumpdata.json +script run hf_mf_mini_dumpdecrypt -h -t -r -d -e -v -i dumpdata.json ]] arguments = [[ h this helptext diff --git a/client/luascripts/tnp3clone.lua b/client/luascripts/hf_mf_tnp3_clone.lua similarity index 94% rename from client/luascripts/tnp3clone.lua rename to client/luascripts/hf_mf_tnp3_clone.lua index 056e7021b..490c49242 100644 --- a/client/luascripts/tnp3clone.lua +++ b/client/luascripts/hf_mf_tnp3_clone.lua @@ -15,17 +15,17 @@ copyright = '' author = "Iceman" version = 'v1.0.2' desc = [[ -This script will try making a barebone clone of a tnp3 tag on to a magic generation1 card. +This script will try to make a barebones clone of a tnp3 tag on to a magic generation1 card. ]] example = [[ - script run tnp3clone - script run tnp3clone -h - script run tnp3clone -l - script run tnp3clone -t aa00 -s 0030 + script run hf_mf_tnp3_clone + script run hf_mf_tnp3_clone -h + script run hf_mf_tnp3_clone -l + script run hf_mf_tnp3_clone -t aa00 -s 0030 ]] usage = [[ -script run tnp3clone [-h] [-t ] [-s ] +script run hf_mf_tnp3_clone [-h] [-t ] [-s ] ]] arguments = [[ -h : this help diff --git a/client/luascripts/tnp3dump.lua b/client/luascripts/hf_mf_tnp3_dump.lua similarity index 94% rename from client/luascripts/tnp3dump.lua rename to client/luascripts/hf_mf_tnp3_dump.lua index 0dea0a01a..ee59046eb 100644 --- a/client/luascripts/tnp3dump.lua +++ b/client/luascripts/hf_mf_tnp3_dump.lua @@ -16,18 +16,18 @@ This script will try to dump the contents of a Mifare TNP3xxx card. It will need a valid KeyA in order to find the other keys and decode the card. ]] example = [[ - script run tnp3dump - script run tnp3dump -n - script run tnp3dump -p - script run tnp3dump -k aabbccddeeff - script run tnp3dump -k aabbccddeeff -n - script run tnp3dump -o myfile - script run tnp3dump -n -o myfile - script run tnp3dump -p -o myfile - script run tnp3dump -k aabbccddeeff -n -o myfile + script run hf_mf_tnp3_dump + script run hf_mf_tnp3_dump -n + script run hf_mf_tnp3_dump -p + script run hf_mf_tnp3_dump -k aabbccddeeff + script run hf_mf_tnp3_dump -k aabbccddeeff -n + script run hf_mf_tnp3_dump -o myfile + script run hf_mf_tnp3_dump -n -o myfile + script run hf_mf_tnp3_dump -p -o myfile + script run hf_mf_tnp3_dump -k aabbccddeeff -n -o myfile ]] usage = [[ -script run tnp3dump [-h] [-k ] [-n] [-p] [-o ] +script run hf_mf_tnp3_dump [-h] [-k ] [-n] [-p] [-o ] ]] arguments = [[ -h : this help diff --git a/client/luascripts/tnp3sim.lua b/client/luascripts/hf_mf_tnp3_sim.lua similarity index 98% rename from client/luascripts/tnp3sim.lua rename to client/luascripts/hf_mf_tnp3_sim.lua index 069bbfe08..dded9327a 100644 --- a/client/luascripts/tnp3sim.lua +++ b/client/luascripts/hf_mf_tnp3_sim.lua @@ -13,17 +13,17 @@ author = 'Iceman' version = 'v1.0.2' desc = [[ This script will try to load a binary datadump of a Mifare TNP3xxx card. -It vill try to validate all checksums and view some information stored in the dump +It will try to validate all checksums and view some information stored in the dump For an experimental mode, it tries to manipulate some data. At last it sends all data to the PM3 device memory where it can be used in the command "hf mf sim" ]] example = [[ - 1. script run tnp3sim - 2. script run tnp3sim -m - 3. script run tnp3sim -m -i myfile + 1. script run hf_mf_tnp3_sim + 2. script run hf_mf_tnp3_sim -m + 3. script run hf_mf_tnp3_sim -m -i myfile ]] usage = [[ -script run tnp3sim [-h] [-m] [-i ] +script run hf_mf_tnp3_sim [-h] [-m] [-i ] ]] arguments = [[ -h : this help diff --git a/client/luascripts/hf_bruteforce.lua b/client/luascripts/hf_mf_uidbruteforce.lua similarity index 87% rename from client/luascripts/hf_bruteforce.lua rename to client/luascripts/hf_mf_uidbruteforce.lua index 1d3b00777..fc85b63bb 100644 --- a/client/luascripts/hf_bruteforce.lua +++ b/client/luascripts/hf_mf_uidbruteforce.lua @@ -1,5 +1,5 @@ --- Run me like this (connected via USB): ./pm3 -l hf_bruteforce.lua --- Run me like this (connected via Blueshark addon): ./client/proxmark3 /dev/rfcomm0 -l ./hf_bruteforce.lua +-- Run me like this (connected via USB): ./pm3 -l hf_mf_uidbruteforce.lua +-- Run me like this (connected via Blueshark addon): ./client/proxmark3 /dev/rfcomm0 -l ./hf_mf_uidbruteforce.lua local getopt = require('getopt') local ansicolors = require('ansicolors') @@ -13,14 +13,14 @@ This script bruteforces 4 or 7 byte UID Mifare classic card numbers. example =[[ Bruteforce a 4 byte UID Mifare classic card number, starting at 11223344, ending at 11223346. - script run hf_bruteforce -s 0x11223344 -e 0x11223346 -t 1000 -x mfc + script run hf_mf_uidbruteforce -s 0x11223344 -e 0x11223346 -t 1000 -x mfc Bruteforce a 7 byte UID Mifare Ultralight card number, starting at 11223344556677, ending at 11223344556679. - script run hf_bruteforce -s 0x11223344556677 -e 0x11223344556679 -t 1000 -x mfu + script run hf_mf_uidbruteforce -s 0x11223344556677 -e 0x11223344556679 -t 1000 -x mfu ]] usage = [[ -script run hf_bruteforce [-s ] [-e ] [-t ] [-x ] +script run hf_mf_uidbruteforce [-s ] [-e ] [-t ] [-x ] ]] arguments = [[ -h this help @@ -114,7 +114,7 @@ local function main(args) local c = string.format( command, n ) print('Running: "'..c..'"') core.console(c) - core.console('msleep '..timeout); + core.console('msleep '..timeout); core.console('hw ping') end diff --git a/client/luascripts/calc_mizip.lua b/client/luascripts/hf_mf_uidkeycalc-mizip.lua similarity index 97% rename from client/luascripts/calc_mizip.lua rename to client/luascripts/hf_mf_uidkeycalc-mizip.lua index 79fbae75f..902aeeda8 100644 --- a/client/luascripts/calc_mizip.lua +++ b/client/luascripts/hf_mf_uidkeycalc-mizip.lua @@ -13,13 +13,13 @@ Algo not found by me. ]] example = [[ -- if called without, it reads tag uid - script run calc_mizip + script run hf_mf_uidkeycalc-mizip -- - script run calc_mizip -u 11223344 + script run hf_mf_uidkeycalc-mizip -u 11223344 ]] usage = [[ -script run calc_mizip -h -u +script run hf_mf_uidkeycalc-mizip -h -u ]] arguments = [[ -h : this help diff --git a/client/luascripts/calc_di.lua b/client/luascripts/hf_mf_uidkeycalc.lua similarity index 97% rename from client/luascripts/calc_di.lua rename to client/luascripts/hf_mf_uidkeycalc.lua index 737d993af..88330ae85 100644 --- a/client/luascripts/calc_di.lua +++ b/client/luascripts/hf_mf_uidkeycalc.lua @@ -13,13 +13,13 @@ Algo not found by me. ]] example = [[ -- if called without, it reads tag uid - script run calc_di + script run hf_mf_uidkeycalc -- - script run calc_di -u 11223344556677 + script run hf_mf_uidkeycalc -u 11223344556677 ]] usage = [[ -script run calc_di -h -u +script run hf_mf_uidkeycalc -h -u ]] arguments = [[ -h : this help diff --git a/client/luascripts/mifareplus.lua b/client/luascripts/hf_mfp_raw.lua similarity index 99% rename from client/luascripts/mifareplus.lua rename to client/luascripts/hf_mfp_raw.lua index a30f24f8b..e28c274a8 100644 --- a/client/luascripts/mifareplus.lua +++ b/client/luascripts/hf_mfp_raw.lua @@ -15,10 +15,10 @@ Please read the NXP manual before running this script to prevent making irrevers Small changes can be to made this script to communicate with the Mifare Plus S, X, or SE. ]] example = [[ - 1. script run mifareplus + 1. script run hf_mfp_raw ]] usage = [[ -script run mifareplus [-h] +script run hf_mfp_raw [-h] ]] arguments = [[ -h : this help diff --git a/client/luascripts/dumptoemul-mfu.lua b/client/luascripts/hf_mfu_dumptoemulator.lua similarity index 97% rename from client/luascripts/dumptoemul-mfu.lua rename to client/luascripts/hf_mfu_dumptoemulator.lua index 0bd11e297..47f52e135 100644 --- a/client/luascripts/dumptoemul-mfu.lua +++ b/client/luascripts/hf_mfu_dumptoemulator.lua @@ -12,10 +12,10 @@ This script takes a dumpfile from 'hf mfu dump' and converts it to a format that by the emulator ]] example = [[ - script run dumptoemul-mfu -i dumpdata-foobar.bin + script run hf_mfu_dumptoemulator -i dumpdata-foobar.bin ]] usage = [[ -script run dumptoemul-mfu [-i ] [-o ] +script run hf_mfu_dumptoemulator [-i ] [-o ] ]] arguments = [[ -h This help diff --git a/client/luascripts/hf_mfu_magicwrite.lua b/client/luascripts/hf_mfu_magicwrite.lua new file mode 100644 index 000000000..2961bd54c --- /dev/null +++ b/client/luascripts/hf_mfu_magicwrite.lua @@ -0,0 +1,659 @@ +local cmds = require('commands') +local getopt = require('getopt') +local lib14a = require('read14a') +local utils = require('utils') +local ansicolors = require('ansicolors') + +-- global +local DEBUG = false -- the debug flag +local bxor = bit32.bxor +local _password = nil +local err_lock = 'use -k or change cfg0 block' + +copyright = 'Copyright (c) 2017 IceSQL AB. All rights reserved.' +author = 'Christian Herrmann' +version = 'v1.1.3' +desc = 'This script enables easy programming of a MAGIC NTAG 21* card' +example = [[ + -- wipe tag + script run hf_mfu_magicwrite -w + + -- wipe a locked down tag by giving the password + script run hf_mfu_magicwrite -k ffffffff -w + + --read magic tag configuration + script run hf_mfu_magicwrite -c + + -- set uid + script run hf_mfu_magicwrite -u 04112233445566 + + -- set pwd / pack + script run hf_mfu_magicwrite -p 11223344 -a 8080 + + -- set version to NTAG213 + script run hf_mfu_magicwrite -v 0004040201000f03 + + -- set signature + script run hf_mfu_magicwrite -s 1122334455667788990011223344556677889900112233445566778899001122 +]] +usage = [[ +script run hf_mfu_easywrite -h -k -c -w -u -t -p -a -s -o -v +]] +arguments = [[ + -h this help + -c read magic configuration + -u UID (14 hexsymbols), set UID on tag + -t tag type to impersonate + 1 = UL_EV1 48k + 2 = UL_EV1 128k + 3 = NTAG 210 + 4 = NTAG 212 + 5 = NTAG 213 (true) + 6 = NTAG 215 (true) + 7 = NTAG 216 (true) + 8 = NTAG I2C 1K + 9 = NTAG I2C 2K + 10 = NTAG I2C 1K PLUS + 11 = NTAG I2C 2K PLUS + 12 = NTAG 213F (true) + 13 = NTAG 216F (true) + -p password (8 hexsymbols), set password on tag. + -a pack ( 4 hexsymbols), set pack on tag. + -s signature data (64 hexsymbols), set signature data on tag. + -o OTP data (8 hexsymbols), set one-time-pad data on tag. + -v version data (16 hexsymbols), set version data on tag. + -w wipe tag. You can specify password if the tag has been locked down. Fills tag with zeros and put default values for NTAG213 (like -t 5) + -k pwd to use with the wipe option +]] +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while result[i] do + dbg(result[i]) + i = i+1 + end + else + print('###', args) + end +end +-- This is only meant to be used when errors occur +local function oops(err) + print("ERROR: ",err) + core.clearCommandBuffer() + return nil, err +end +--- +-- 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 +--- +-- set the global password variable +local function set_password(pwd) + if pwd == nil then _password = nil; return true, 'Ok' end + if #pwd ~= 8 then return nil, 'password wrong length. Must be 4 hex bytes' end + if #pwd == 0 then _password = nil end + _password = pwd + return true, 'Ok' +end +--- Picks out and displays the data read from a tag +-- Specifically, takes a usb packet, converts to a Command +-- (as in commands.lua), takes the data-array and +-- reads the number of bytes specified in arg1 (arg0 in c-struct) +-- @param usbpacket the data received from the device +local function getResponseData(usbpacket) + local resp = Command.parse(usbpacket) + local len = tonumber(resp.arg1) * 2 + return string.sub(tostring(resp.data), 0, len); +end +--- +-- +local function sendRaw(rawdata, options) + + local flags = lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + + lib14a.ISO14A_COMMAND.ISO14A_RAW + + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC + + local c = Command:newMIX{cmd = cmds.CMD_HF_ISO14443A_READER, + arg1 = flags, + -- arg2 contains the length, which is half the length of the ASCII-string rawdata + arg2 = string.len(rawdata)/2, + data = rawdata} + + return c:sendMIX(options.ignore_response) +end +--- +-- +local function send(payload) + local usb, err = sendRaw(payload,{ignore_response = false}) + if err then return oops(err) end + return getResponseData(usb) +end +--- +-- select tag and if password is set, authenticate +local function connect() + core.clearCommandBuffer() + + -- First of all, connect + info, err = lib14a.read(true, true) + if err then + lib14a.disconnect() + return oops(err) + end + core.clearCommandBuffer() + + --authenticate if needed using global variable + if _password then + send('1B'.._password) + end + return true +end +-- +-- Read magic configuration +local function read_config() + local info = connect() + if not info then return false, "Can't select card" end + + -- read PWD + local pwd = send("30F0"):sub(1,8) + + -- 04 response indicates that blocks has been locked down. + if pwd == '04' then lib14a.disconnect(); return nil, "can't read configuration, "..err_lock end + + -- read PACK + local pack = send("30F1"):sub(1,4) + + -- read SIGNATURE + local signature1 = send('30F2'):sub(1,32) + local signature2 = send('30F6'):sub(1,32) + + -- read VERSION + local version = send('30FA'):sub(1,16) + -- read config + local cardtype = send('30FC'):sub(1,2) + + local typestr = '' + if cardtype == '00' then typestr = 'NTAG 213' + elseif cardtype == '01' then typestr = 'NTAG 215' + elseif cardtype == '02' then typestr = 'NTAG 216' + end + + print('Magic NTAG 21* Configuration') + print(' - Type ', typestr, '(genuine cardtype)') + print(' - Password', pwd) + print(' - Pack ', pack) + print(' - Version ', version) + print(' - Signature', signature1..signature2) + + lib14a.disconnect() + return true, 'Ok' +end +--- +-- Write SIGNATURE data +local function write_signature(data) + + -- uid string checks + if data == nil then return nil, 'empty data string' end + if #data == 0 then return nil, 'empty data string' end + if #data ~= 64 then return nil, 'data wrong length. Should be 32 hex bytes' end + + local info = connect() + if not info then return false, "Can't select card" end + + print('Writing new signature') + + local b,c + local cmd = 'A2F%d%s' + local j = 2 + for i = 1, #data, 8 do + b = data:sub(i,i+7) + c = cmd:format(j,b) + local resp = send(c) + if resp == '04' then lib14a.disconnect(); return nil, 'Failed to write signature' end + j = j + 1 + end + lib14a.disconnect() + return true, 'Ok' +end +--- +-- Write PWD +local function write_pwd(pwd) + -- PWD string checks + if pwd == nil then return nil, 'empty PWD string' end + if #pwd == 0 then return nil, 'empty PWD string' end + if #pwd ~= 8 then return nil, 'PWD wrong length. Should be 4 hex bytes' end + + local info = connect() + if not info then return false, "Can't select card" end + + print('Writing new PWD ', pwd) + + local resp = send('A2F0'..pwd) + lib14a.disconnect() + if resp == '04' then + return nil, 'Failed to write password' + else + return true, 'Ok' + end +end +--- +-- Write PACK +local function write_pack(pack) + -- PACK string checks + if pack == nil then return nil, 'empty PACK string' end + if #pack == 0 then return nil, 'empty PACK string' end + if #pack ~= 4 then return nil, 'PACK wrong length. Should be 4 hex bytes' end + + local info = connect() + if not info then return false, "Can't select card" end + + print('Writing new PACK', pack) + + local resp = send('A2F1'..pack..'0000') + lib14a.disconnect() + if resp == '04' then + return nil, 'Failed to write pack' + else + return true, 'Ok' + end +end +-- +-- Write OTP block +local function write_otp(block3) + + -- OTP string checks + if block3 == nil then return nil, 'empty OTP string' end + if #block3 == 0 then return nil, 'empty OTP string' end + if #block3 ~= 8 then return nil, 'OTP wrong length. Should be 4 hex bytes' end + + local info = connect() + if not info then return false, "Can't select card" end + + print('Writing new OTP ', block3) + + local resp = send('A203'..block3) + lib14a.disconnect() + if resp == '04' then + return nil, 'Failed to write OTP' + else + return true, 'Ok' + end +end +-- +-- Writes a UID with bcc1, bcc2. Needs a magic tag. +local function write_uid(uid) + -- uid string checks + if uid == nil then return nil, 'empty uid string' end + if #uid == 0 then return nil, 'empty uid string' end + if #uid ~= 14 then return nil, 'uid wrong length. Should be 7 hex bytes' end + + local info = connect() + if not info then return false, "Can't select card" end + + print('Writing new UID ', uid) + + local uidbytes = utils.ConvertHexToBytes(uid) + local bcc1 = bxor(bxor(bxor(uidbytes[1], uidbytes[2]), uidbytes[3]), 0x88) + local bcc2 = bxor(bxor(bxor(uidbytes[4], uidbytes[5]), uidbytes[6]), uidbytes[7]) + local block0 = string.format('%02X%02X%02X%02X', uidbytes[1], uidbytes[2], uidbytes[3], bcc1) + local block1 = string.format('%02X%02X%02X%02X', uidbytes[4], uidbytes[5], uidbytes[6], uidbytes[7]) + local block2 = string.format('%02X%02X%02X%02X', bcc2, 0x48, 0x00, 0x00) + local resp + + resp = send('A200'..block0) + resp = send('A201'..block1) + resp = send('A202'..block2) + lib14a.disconnect() + + if resp == '04' then + return nil, 'Failed to write new uid' + else + return true, 'Ok' + end +end +--- +-- Write VERSION data, +-- make sure you have correct version data +local function write_version(data) + -- version string checks + if data == nil then return nil, 'empty version string' end + if #data == 0 then return nil, 'empty version string' end + if #data ~= 16 then return nil, 'version wrong length. Should be 8 hex bytes' end + + local info = connect() + if not info then return false, "Can't select card" end + + print('Writing new version', data) + + local b1 = data:sub(1,8) + local b2 = data:sub(9,16) + local resp + resp = send('A2FA'..b1) + resp = send('A2FB'..b2) + lib14a.disconnect() + if resp == '04' then + return nil, 'Failed to write version' + else + return true, 'Ok' + end +end +--- +-- writen TYPE which card is based on. +-- 00 = 213, 01 = 215, 02 = 216 +local function write_type(data) + -- type string checks + if data == nil then return nil, 'empty type string' end + if #data == 0 then return nil, 'empty type string' end + if #data ~= 2 then return nil, 'type wrong length. Should be 1 hex byte' end + + local info = connect() + if not info then return false, "Can't select card" end + print('Writing new type', data) + + local resp = send('A2FC'..data..'000000') + lib14a.disconnect() + if resp == '04' then + return nil, 'Failed to write type' + else + return true, 'Ok' + end +end +--- +-- Set tag type. Predefinde version data together with magic type set. +-- Since cmd always gives 10 bytes len (data+crc) we can impersonate the following types +-- we only truely be three types NTAG 213,215 and 216 +local function set_type(tagtype) + + -- tagtype checks + if type(tagtype) == 'string' then tagtype = tonumber(tagtype, 10) end + if tagtype == nil then return nil, 'empty tagtype' end + + if tagtype == 1 then + print('Setting: UL-EV1 48') + 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 + write_type('00') -- based on NTAG213.. + + -- Setting UL-Ev1 default config bl 16,17 + connect() + send('a210000000FF') + send('a21100050000') + + elseif tagtype == 2 then + print('Setting: UL-EV1 128') + write_otp('00000000') -- Setting OTP to default 00 00 00 00 + write_version('0004030101000e03') -- UL-EV1 (128) 00 04 03 01 01 00 0e 03 + write_type('01') + + -- Setting UL-Ev1 default config bl 37,38 + connect() + send('a225000000FF') + send('a22600050000') + elseif tagtype == 3 then + print('Setting: NTAG 210') + write_version('0004040101000b03') -- NTAG210 00 04 04 01 01 00 0b 03 + write_type('00') + + -- Setting NTAG210 default CC block456 + connect() + send('a203e1100600') + send('a2040300fe00') + send('a20500000000') + -- Setting cfg1/cfg2 + send('a210000000FF') + send('a21100050000') + elseif tagtype == 4 then + print('Setting: NTAG 212') + write_version('0004040101000E03') -- NTAG212 00 04 04 01 01 00 0E 03 + write_type('00') + + -- Setting NTAG212 default CC block456 + connect() + send('a203e1101000') + send('a2040103900a') + send('a205340300fe') + -- Setting cfg1/cfg2 + send('a225000000FF') + send('a22600050000') + elseif tagtype == 5 then + print('Setting: NTAG 213') + write_version('0004040201000F03') -- NTAG213 00 04 04 02 01 00 0f 03 + write_type('00') + + -- Setting NTAG213 default CC block456 + connect() + send('a203e1101200') + send('a2040103a00c') + send('a205340300fe') + -- setting cfg1/cfg2 + send('a229000000ff') + send('a22a00050000') + elseif tagtype == 6 then + print('Setting: NTAG 215') + write_version('0004040201001103') -- NTAG215 00 04 04 02 01 00 11 03 + write_type('01') + + -- Setting NTAG215 default CC block456 + connect() + send('a203e1103e00') + send('a2040300fe00') + send('a20500000000') + -- setting cfg1/cfg2 + send('a283000000ff') + send('a28400050000') + elseif tagtype == 7 then + print('Setting: NTAG 216') + write_version('0004040201001303') -- NTAG216 00 04 04 02 01 00 13 03 + write_type('02') + + -- Setting NTAG216 default CC block456 + connect() + send('a203e1106d00') + send('a2040300fe00') + send('a20500000000') + -- setting cfg1/cfg2 + send('a2e3000000ff') + send('a2e400050000') + elseif tagtype == 8 then + print('Setting: NTAG I2C 1K') + write_version('0004040502011303') -- NTAG_I2C_1K 00 04 04 05 02 01 13 03 + write_type('02') + + -- Setting NTAG I2C 1K default CC block456 + connect() + send('a203e1106D00') + send('a2040300fe00') + send('a20500000000') + elseif tagtype == 9 then + print('Setting: NTAG I2C 2K') + write_version('0004040502011503') -- NTAG_I2C_2K 00 04 04 05 02 01 15 03 + write_type('02') + + -- Setting NTAG I2C 2K default CC block456 + connect() + send('a203e110EA00') + send('a2040300fe00') + send('a20500000000') + elseif tagtype == 10 then + print('Setting: NTAG I2C plus 1K') + write_version('0004040502021303') -- NTAG_I2C_1K 00 04 04 05 02 02 13 03 + write_type('02') + + -- Setting NTAG I2C 1K default CC block456 + connect() + send('a203e1106D00') + send('a2040300fe00') + send('a20500000000') + elseif tagtype == 11 then + print('Setting: NTAG I2C plus 2K') + write_version('0004040502021503') -- NTAG_I2C_2K 00 04 04 05 02 02 15 03 + write_type('02') + + -- Setting NTAG I2C 2K default CC block456 + connect() + send('a203e1106D00') + send('a2040300fe00') + send('a20500000000') + elseif tagtype == 12 then + print('Setting: NTAG 213F') + write_version('0004040401000F03') -- NTAG213F 00 04 04 04 01 00 0f 03 + write_type('00') + + -- Setting NTAG213 default CC block456 + connect() + send('a203e1101200') + send('a2040103a00c') + send('a205340300fe') + -- setting cfg1/cfg2 + send('a229000000ff') + send('a22a00050000') + elseif tagtype == 13 then + print('Setting: NTAG 216F') + write_version('0004040401001303') -- NTAG216F 00 04 04 04 01 00 13 03 + write_type('02') + + -- Setting NTAG216 default CC block456 + connect() + send('a203e1106d00') + send('a2040300fe00') + send('a20500000000') + -- setting cfg1/cfg2 + send('a2e3000000ff') + send('a2e400050000') + end + + lib14a.disconnect() + if resp == '04' then + return nil, 'Failed to set type' + else + return true, 'Ok' + end +end +--- +-- wipe tag +local function wipe() + + local info = connect() + if not info then return false, "Can't select card" end + + local err, msg, resp + local cmd_empty = 'A2%02X00000000' + local cmd_cfg1 = 'A2%02X000000FF' + local cmd_cfg2 = 'A2%02X00050000' + + print('Wiping tag') + + for b = 3, 0xFB do + --configuration block 0 + if b == 0x29 or b == 0x83 or b == 0xe3 then + local cmd = (cmd_cfg1):format(b) + resp = send(cmd) + --configuration block 1 + elseif b == 0x2a or b == 0x84 or b == 0xe4 then + local cmd = (cmd_cfg2):format(b) + resp = send(cmd) + else + resp = send(cmd_empty:format(b)) + end + if resp == '04' or #resp == 0 then + io.write('\nwrote block '..b, ' failed\n') + err = true + else + io.write('.') + end + io.flush() + end + io.write('\r\n') + + lib14a.disconnect() + + if err then return nil, "Tag locked down, "..err_lock end + + print('setting default values...') + + set_password(nil) + + -- set NTAG213 default values + err, msg = set_type(5) + if err == nil then return err, msg end + + --set UID + err, msg = write_uid('04112233445566') + if err == nil then return err, msg end + + --set pwd + err, msg = write_pwd('FFFFFFFF') + if err == nil then return err, msg end + + --set pack + err, msg = write_pack('0000') + if err == nil then return err, msg end + + return true, 'Ok' +end +--- +-- The main entry point +function main(args) + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + local err, msg + + if #args == 0 then return help() end + + -- Read the parameters + for o, a in getopt.getopt(args, 'hck:u:t:p:a:s:o:v:w') do + + -- help + if o == "h" then return help() end + + --key + if o == 'k' then err, msg = set_password(a) end + + -- configuration + if o == "c" then err, msg = read_config() end + + --wipe tag + if o == "w" then err, msg = wipe() end + + -- write uid + if o == "u" then err, msg = write_uid(a) end + + -- write type/version + if o == "t" then err, msg = set_type(a) end + + -- write pwd + if o == "p" then err, msg = write_pwd(a) end + + -- write pack + if o == "a" then err, msg = write_pack(a) end + + -- write signature + if o == "s" then err, msg = write_signature(a) end + + -- write otp + if o == "o" then err, msg = write_otp(a) end + + -- write version + if o == "v" then err, msg = write_version(a) end + + if err == nil then return oops(msg) end + end + +end + +main(args) diff --git a/client/luascripts/ul_uid.lua b/client/luascripts/hf_mfu_setuid.lua similarity index 84% rename from client/luascripts/ul_uid.lua rename to client/luascripts/hf_mfu_setuid.lua index ece368ce5..86bef09c3 100644 --- a/client/luascripts/ul_uid.lua +++ b/client/luascripts/hf_mfu_setuid.lua @@ -12,13 +12,13 @@ This script tries to set UID on a mifare Ultralight magic card which either ]] example = [[ -- backdoor magic tag - script run ul_uid -u 11223344556677 + script run hf_mfu_setuid -u 11223344556677 -- brickable magic tag - script run ul_uid -b -u 11223344556677 + script run hf_mfu_setuid -b -u 11223344556677 ]] usage = [[ -script run ul_uid [-h] [-b] [-u ] +script run hf_mfu_setuid [-h] [-b] [-u ] ]] arguments = [[ -h : this help @@ -70,18 +70,18 @@ function magicUID(b0, b1, b2) print('Using backdoor Magic tag function') -- write block 0 - core.console('hf 14a raw -p -a -b 7 40') - core.console('hf 14a raw -p -a 43') + core.console('hf 14a raw -k -a -b 7 40') + core.console('hf 14a raw -k -a 43') core.console('hf 14a raw -c -a A200'..b0) -- write block 1 - core.console('hf 14a raw -p -a -b 7 40') - core.console('hf 14a raw -p -a 43') + core.console('hf 14a raw -k -a -b 7 40') + core.console('hf 14a raw -k -a 43') core.console('hf 14a raw -c -a A201'..b1) -- write block 2 - core.console('hf 14a raw -p -a -b 7 40') - core.console('hf 14a raw -p -a 43') + core.console('hf 14a raw -k -a -b 7 40') + core.console('hf 14a raw -k -a 43') core.console('hf 14a raw -c -a A202'..b2) end -- @@ -90,16 +90,16 @@ function brickableUID(b0, b1, b2) print('Using BRICKABLE Magic tag function') - core.console('hf 14a raw -p -s -3') + core.console('hf 14a raw -k -s -3') -- write block 0 - core.console('hf 14a raw -p -c A200'..b0) + core.console('hf 14a raw -k -c A200'..b0) -- write block 1 - core.console('hf 14a raw -p -c A201'..b1) + core.console('hf 14a raw -k -c A201'..b1) -- write block 2 - core.console('hf 14a raw -p -c A202'..b2) + core.console('hf 14a raw -k -c A202'..b2) end --- -- The main entry point diff --git a/client/luascripts/calc_ev1_it.lua b/client/luascripts/hf_mfu_uidkeycalc-italy.lua similarity index 96% rename from client/luascripts/calc_ev1_it.lua rename to client/luascripts/hf_mfu_uidkeycalc-italy.lua index 2e333d147..4777a186d 100644 --- a/client/luascripts/calc_ev1_it.lua +++ b/client/luascripts/hf_mfu_uidkeycalc-italy.lua @@ -13,13 +13,13 @@ Algo not found by me. ]] example =[[ -- if called without, it reads tag uid - script run calc_ev1_it + script run hf_mfu_uidkeycalc-italy -- - script run calc_ev1_it -u 11223344556677 + script run hf_mfu_uidkeycalc-italy -u 11223344556677 ]] usage = [[ -script run calc_ev1_it -h -u " +script run hf_mfu_uidkeycalc-italy -h -u " ]] arguments = [[ -h : this help diff --git a/client/luascripts/ndef_dump.lua b/client/luascripts/hf_ndef_dump.lua similarity index 99% rename from client/luascripts/ndef_dump.lua rename to client/luascripts/hf_ndef_dump.lua index 672beda1c..b2c282cf8 100644 --- a/client/luascripts/ndef_dump.lua +++ b/client/luascripts/hf_ndef_dump.lua @@ -21,10 +21,10 @@ Therefore, the .eml is better to use file when saving dumps.) ]] example = [[ - 1. script run ndef_dump + 1. script run hf_ndef_dump ]] usage = [[ -script run ndef_dump [-h] [-d] [-v] +script run hf_ndef_dump [-h] [-d] [-v] ]] arguments = [[ -h this help diff --git a/client/luascripts/ntag_3d.lua b/client/luascripts/hf_ntag-3d.lua similarity index 95% rename from client/luascripts/ntag_3d.lua rename to client/luascripts/hf_ntag-3d.lua index d986923ba..e46705bd1 100644 --- a/client/luascripts/ntag_3d.lua +++ b/client/luascripts/hf_ntag-3d.lua @@ -13,16 +13,16 @@ Thanks to @jack for his invaluable input on some of the configuration. ]] example =[[ -- This will generate GOLD, PLA, TH, EU, 200m, tagbased uid. - script run ntag_3d -c 46 -m 50 -p 5448 -s 4555 -l 200 + script run hf_ntag-3d -c 46 -m 50 -p 5448 -s 4555 -l 200 -- This will generate GOLD, PLA, TH, EU, 200m, userbased uid. - script run ntag_3d -u 11223344556677 -c 46 -m 50 -p 5448 -s 4555 -l 200 + script run hf_ntag-3d -u 11223344556677 -c 46 -m 50 -p 5448 -s 4555 -l 200 -- This will generate GOLD, PLA, TH, EU, 200m, userbased uid. and configure a MAGIC NTAG. - script run ntag_3d -u 11223344556677 -c 46 -m 50 -p 5448 -s 4555 -l 200 -1 + script run hf_ntag-3d -u 11223344556677 -c 46 -m 50 -p 5448 -s 4555 -l 200 -1 ]] usage = [[ -script run ntag_3d [-h] [-t] [-u ] [-c ] [-m ] [-p ] [-s ] [-l ] +script run hf_ntag-3d [-h] [-t] [-u ] [-c ] [-m ] [-p ] [-s ] [-l ] ]] arguments = [[ -h : this help @@ -221,13 +221,13 @@ local function configure_magic_ntag(uid) local pwd, pack = core.keygen_algo_d(uid) - -- Set the arguments for mfu_magic script v1.0.8 + -- Set the arguments for hf_mfu_magicwrite script v1.0.8 -- -t 12 == configure NTAG213F -- -u == set UID -- -p == set pwd -- -a == set pack args =('-t 12 -u %s -p %08X -a %04X'):format(uid, pwd, pack) - require('mfu_magic') + require('hf_mfu_magicwrite') -- Set back args. Not that it's used, just for the karma... args = myargs diff --git a/client/luascripts/init_rdv4.lua b/client/luascripts/init_rdv4.lua index bc18dc0a9..a724ed189 100644 --- a/client/luascripts/init_rdv4.lua +++ b/client/luascripts/init_rdv4.lua @@ -74,9 +74,9 @@ function main(args) -- Upload dictionaries print('Uploading dictionaries to RDV4 flashmemory') print(dash) - core.console('mem load f mfc_default_keys m') - core.console('mem load f t55xx_default_pwds t') - core.console('mem load f iclass_default_keys i') + core.console('mem load -f mfc_default_keys --mfc') + core.console('mem load -f t55xx_default_pwds --t55xx') + core.console('mem load -f iclass_default_keys --iclass') print(dash) -- T55x7 Device configuration diff --git a/client/luascripts/lf_em_tearoff.lua b/client/luascripts/lf_em_tearoff.lua new file mode 100644 index 000000000..2aa0dd5ee --- /dev/null +++ b/client/luascripts/lf_em_tearoff.lua @@ -0,0 +1,176 @@ +local getopt = require('getopt') +local ansicolors = require('ansicolors') + +copyright = 'Iceman' +author = 'Iceman' +version = 'v0.9.9' +desc = [[ +This is scripts loops though a tear attack and reads expected value. +]] +example = [[ + 1. script run tearoff -n 2 -s 200 -e 400 -a 5 +]] +usage = [[ +script run tearoff [-h] [-n ] [-a ] [-p ] [-s ] [-e ] [-r ] [-w ] +]] +arguments = [[ + -h This help + -n steps in milliseconds for each tearoff + -a address to target on card + -p (optional) use a password + -s initial start delay + -e end delay, must be larger than start delay + -r 4 hex bytes value to be read + -w 4 hex bytes value to be written + end +]] + +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, err +end +--- +-- 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 + +local function main(args) + + --[[ + Basically do the following, + + 1. hw tear + 2. lf em 4x05_write + 3. lf em 4x05_read + + The first two commands doesn't need a feedback from the system, so going with core.console commands. + Since the read needs demodulation of signal I opted to add that function from cmdlfem4x.c to the core lua scripting + core.em4x05_read(addr, password) + + --]] + local n, addr, password, sd, ed, wr_value, rd_value + + for o, a in getopt.getopt(args, 'he:s:a:p:n:r:w:') do + if o == 'h' then return help() end + if o == 'n' then n = a end + if o == 'a' then addr = a end + if o == 'p' then password = a end + if o == 'e' then ed = tonumber(a) end + if o == 's' then sd = tonumber(a) end + if o == 'w' then wr_value = a end + if o == 'r' then rd_value = a end + end + + rd_value = rd_value or 'FFFFFFFF' + wr_value = wr_value or 'FFFFFFFF' + addr = addr or 5 + password = password or '' + n = n or 2 + sd = sd or 2000 + ed = ed or 2100 + + if #password ~= 8 then + password = '' + end + + if #wr_value ~= 8 then + wr_value = 'FFFFFFFF' + end + + if #rd_value ~= 8 then + rd_value = 'FFFFFFFF' + end + + if sd > ed then + return oops('start delay can\'t be larger than end delay', sd, ed) + end + + print('Starting EM4x05 tear off') + print('target addr', addr) + if password then + print('target pwd', password) + end + print('target stepping', n) + print('target delay', sd ,ed) + print('read value', rd_value) + print('write value', wr_value) + + local res_tear = 0 + local res_nowrite = 0 + + local set_tearoff_delay = 'hw tearoff --delay %d' + local enable_tearoff = 'hw tearoff --on' + + local wr_template = 'lf em 4x05_write %s %s %s' + + -- init addr to value + core.console(wr_template:format(addr, wr_value, password)) + + if sd == ed then + ed = n + n = 0 + end + + for step = sd, ed, n do + + io.flush() + if core.kbd_enter_pressed() then + print("aborted by user") + break + end + + core.clearCommandBuffer() + + -- reset addr to known value, if not locked into. + if n ~= 0 then + c = wr_template:format(addr, wr_value, password) + core.console(c) + end + + local c = set_tearoff_delay:format(step) + core.console(c); + core.console(enable_tearoff) + + c = wr_template:format(addr, wr_value, password) + core.console(c) + + local word, err = core.em4x05_read(addr, password) + if err then + return oops(err) + end + + local wordstr = ('%08X'):format(word) + + if wordstr ~= wr_value then + if wordstr ~= rd_value then + print((ansicolors.red..'TEAR OFF occurred:'..ansicolors.reset..' %08X'):format(word)) + res_tear = res_tear + 1 + else + print((ansicolors.cyan..'TEAR OFF occurred:'..ansicolors.reset..' %08X'):format(word)) + res_nowrite = res_nowrite + 1 + end + else + print((ansicolors.green..'Good write occurred:'..ansicolors.reset..' %08X'):format(word)) + end + end +end + +--[[ +In the future, we may implement so that scripts are invoked directly +into a 'main' function, instead of being executed blindly. For future +compatibility, I have done so, but I invoke my main from here. +--]] +main(args) diff --git a/client/luascripts/lf_em_tearoff_protect.lua b/client/luascripts/lf_em_tearoff_protect.lua new file mode 100644 index 000000000..49ee99cec --- /dev/null +++ b/client/luascripts/lf_em_tearoff_protect.lua @@ -0,0 +1,326 @@ +local getopt = require('getopt') +local ansicolors = require('ansicolors') + +copyright = 'Iceman' +author = [[ +'Author Iceman + CoAuthor Doegox +]] +version = 'v1.0.1' +desc = [[ +This is scripts loops though a tear attack and reads expected value. +]] +example = [[ + Full automatic, with password: + script run lf_em_tearoff_protect -p 50524F58 + + Manual fix increment over specified range: + script run lf_em_tearoff_protect -n 2 -s 200 -e 400 + + Trying repeatedly for a fixed timing, forever or till success: + script run lf_em_tearoff_protect -s 400 -e 400 + + Tips: + Use a low Q antenna + Move card somehow away from the antenna to a position where it still works +]] +usage = [[ +script run lf_em_tearoff_protect [-h] [-n ] [-p ] [-s ] [-e ] +]] +arguments = [[ + -h This help + -n steps in milliseconds for each tear-off + -p (optional) use a password + -s initial start delay + -e end delay, must be larger or equal to start delay + end +]] + +local set_tearoff_delay = 'hw tearoff -s --on --delay %d' +local wr_template = 'lf em 4x05_write %s %s %s' + +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, err +end +--- +-- 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 + +local function exit_msg() + print('') + print('================= '..ansicolors.green..'verify with'..ansicolors.reset..' =================') + print(' lf em 4x05_dump') + print('===============================================') + return nil +end + +local function reset(wr_value, password) + print('[=] '..ansicolors.red..'resetting the active lock block'..ansicolors.reset) + core.console(wr_template:format(99, wr_value, password)) +end + +local function main(args) + + --[[ + Basically it does the following, + + 1. hw tear + 2. lf em 4x05_write + 3. lf em 4x05_read + + The first two commands dont need a feedback from the system, so going with core.console commands. + Since the read needs demodulation of signal I opted to add that function from cmdlfem4x.c to the core lua scripting + core.em4x05_read(addr, password) + + --]] + local n, password, sd, ed + + for o, a in getopt.getopt(args, 'he:s:p:n:') do + if o == 'h' then return help() end + if o == 'n' then n = tonumber(a) end + if o == 'p' then password = a end + if o == 'e' then ed = tonumber(a) end + if o == 's' then sd = tonumber(a) end + end + + password = password or '' + if #password ~= 8 then + password = '' + end + + local word14, err14 = core.em4x05_read(14, password) + if err14 then + return oops(err14) + end + local word15, err15 = core.em4x05_read(15, password) + if err15 then + return oops(err15) + end + local bit15 = bit.band(0x00008000, word15) + if bit15 == 0x00008000 then + rd_value = ('%08X'):format(word15) + reset(wr_value, password) + else + rd_value = ('%08X'):format(word14) + end + if rd_value == '00008000' then + print('Tag already fully unlocked, nothing to do') + return nil + end + local wr_value = '00000000' + local auto = false + if n == nil then + auto = true + sd = sd or 2000 + ed = ed or 6000 + n = (ed - sd) / 2 + else + if sd == nil or ed == nil then + return oops('start and stop delays need to be defined') + end + if sd > ed then + return oops('start delay can\'t be larger than end delay', sd, ed) + end + end + + print('==========================================') + print('Starting EM4x05 tear-off : target PROTECT') + + if password ~= '' then + print('target pwd', password) + end + if auto then + print('automatic mode', 'enabled') + end + print('target stepping', n) + print('target delay', sd ,ed) + print('read value', rd_value) + print('write value', wr_value) + print('==========================================') + + local res_tear = 0 + local res_nowrite = 0 + + -- fix at one specific delay + if sd == ed then + n = 0 + end + + local tries = 0 + local soon = 0 + local late = 0 + while sd <= ed do + + if auto and n < 1 then -- n is a float + print('[!] Reached n < 1 => '..ansicolors.yellow..'disabling automatic mode'..ansicolors.reset) + ed = sd + auto = false + n = 0 + end + if not auto then + sd = sd + n + end + if (tries >= 5) and (n == 0) and (soon ~= late) then + if soon > late then + print(('[!] Tried %d times, soon:%i late:%i => '):format(tries, soon, late)..ansicolors.yellow..'adjusting delay by +1 us'..ansicolors.reset) + sd = sd + 1 + ed = ed + 1 + else + print(('[!] Tried %d times, soon:%i late:%i => '):format(tries, soon, late)..ansicolors.yellow..'adjusting delay by -1 us'..ansicolors.reset) + sd = sd - 1 + ed = ed - 1 + end + tries = 0 + soon = 0 + late = 0 + end + + io.flush() + if core.kbd_enter_pressed() then + print("aborted by user") + break + end + + core.clearCommandBuffer() + + local c = set_tearoff_delay:format(sd) + core.console(c); + + c = wr_template:format(99, wr_value, password) + core.console(c) + + word14, err14 = core.em4x05_read(14, password) + if err14 then + return oops(err14) + end + + local wordstr14 = ('%08X'):format(word14) + + word15, err15 = core.em4x05_read(15, password) + if err15 then + return oops(err15) + end + + local wordstr15 = ('%08X'):format(word15) + + print(('[=] ref:'..rd_value..' 14:%08X 15:%08X '):format(word14, word15)) + + + if wordstr14 == rd_value and wordstr15 == '00000000' then + print('[=] Status: Nothing happened => '..ansicolors.green..'tearing too soon'..ansicolors.reset) + if auto then + sd = sd + n + n = n / 2 + print(('[+] Adjusting params: n=%i sd=%i ed=%i'):format(n, sd, ed)) + else + soon = soon + 1 + end + else + if wordstr15 == rd_value then + if wordstr14 == '00000000' then + print('[=] Status: Protect succeeded => '..ansicolors.green..'tearing too late'..ansicolors.reset) + else + if wordstr14 == rd_value then + print('[=] Status: 15 ok, 14 not yet erased => '..ansicolors.green..'tearing too late'..ansicolors.reset) + else + print('[=] Status: 15 ok, 14 partially erased => '..ansicolors.green..'tearing too late'..ansicolors.reset) + end + end + reset(wr_value, password) + -- it could still happen that a bitflip got committed, let's check... + local word14b, err14b = core.em4x05_read(14, password) + if err14b then + return oops(err14b) + end + local wordstr14b = ('%08X'):format(word14b) + if (wordstr14b == '00000000') then + reset(wr_value, password) + end + if (wordstr14b ~= rd_value) then + local word15b, err15b = core.em4x05_read(15, password) + if err15b then + return oops(err15b) + end + print(('[=] Status: new definitive value! => '..ansicolors.red..'SUCCESS: '..ansicolors.reset..'14: '..ansicolors.cyan..'%08X'..ansicolors.reset..' 15: %08X'):format(word14b, word15b)) + return exit_msg() + end + if auto then + ed = sd + sd = sd - n + n = n / 2 + print(('[+] Adjusting params: n=%i sd=%i ed=%i'):format(n, sd, ed)) + else + late = late + 1 + end + else + bit15 = bit.band(0x00008000, word15) + if bit15 == 0x00008000 then + print(('[=] Status: 15 bitflipped and active => '..ansicolors.red..'SUCCESS?: '..ansicolors.reset..'14: %08X 15: '..ansicolors.cyan..'%08X'..ansicolors.reset):format(word14, word15)) + print('[+] Committing results...') + reset(wr_value, password) + local word14b, err14b = core.em4x05_read(14, password) + if err14b then + return oops(err14b) + end + local wordstr14b = ('%08X'):format(word14b) + local word15b, err15b = core.em4x05_read(15, password) + if err15b then + return oops(err15b) + end + local wordstr15b = ('%08X'):format(word15b) + print(('[=] ref:'..rd_value..' 14:%08X 15:%08X '):format(word14b, word15b)) + + bit15 = bit.band(0x00008000, word14b) + if bit15 == 0x00008000 then + if (wordstr14b == wordstr15) then + print(('[=] Status: confirmed => '..ansicolors.red..'SUCCESS: '..ansicolors.reset..'14: '..ansicolors.cyan..'%08X'..ansicolors.reset..' 15: %08X'):format(word14b, word15b)) + return exit_msg() + end + if (wordstr14b ~= rd_value) then + print(('[=] Status: new definitive value! => '..ansicolors.red..'SUCCESS: '..ansicolors.reset..'14: '..ansicolors.cyan..'%08X'..ansicolors.reset..' 15: %08X'):format(word14b, word15b)) + return exit_msg() + end + print(('[=] Status: failed to commit bitflip => '..ansicolors.red..'FAIL: '..ansicolors.reset..'14: %08X 15: %08X'):format(word14b, word15b)) + else + print(('[=] Status: failed to commit => '..ansicolors.red..'FAIL: '..ansicolors.reset..'14: %08X 15: %08X'):format(word14b, word15b)) + end + else + print(('[=] Status: 15 bitflipped but inactive => '..ansicolors.yellow..'PROMISING: '..ansicolors.reset..'14: %08X 15: '..ansicolors.cyan..'%08X'..ansicolors.reset):format(word14, word15)) + end + if auto then + n = 0 + ed = sd + else + tries = 0 + soon = 0 + late = 0 + end + end + end + if not auto then + tries = tries + 1 + end + end +end + +--[[ +In the future, we may implement so that scripts are invoked directly +into a 'main' function, instead of being executed blindly. For future +compatibility, I have done so, but I invoke my main from here. +--]] +main(args) diff --git a/client/luascripts/lf_bulk.lua b/client/luascripts/lf_hid_bulkclone.lua similarity index 95% rename from client/luascripts/lf_bulk.lua rename to client/luascripts/lf_hid_bulkclone.lua index 709c992a4..0e74f82b5 100644 --- a/client/luascripts/lf_bulk.lua +++ b/client/luascripts/lf_hid_bulkclone.lua @@ -1,5 +1,5 @@ -- --- lf_bulk.lua - A tool to clone a large number of tags at once. +-- lf_hid_bulkclone.lua - A tool to clone a large number of tags at once. -- Updated 2017-04-18 -- Updated 2018-02-20 iceman local getopt = require('getopt') @@ -14,10 +14,10 @@ For more info, check the comments in the code ]] example = [[ -- - script run lf_bulk.lua -f 1 -b 1000 -c 10 + script run lf_hid_bulkclone.lua -f 1 -b 1000 -c 10 ]] usage = [[ -script run lf_bulk.lua -f facility -b base_id_num -c count +script run lf_hid_bulkclone.lua -f facility -b base_id_num -c count ]] arguments = [[ -h : this help @@ -192,7 +192,7 @@ local function main(args) print('Press enter to program card '..cardnum..':'..facility..' (hex: '..card..')') --This would be better with 'press Enter', but we'll take what we can get. io.read() - core.console( ('lf hid clone %s'):format(card) ) + core.console( ('lf hid clone -r %s'):format(card) ) end end diff --git a/client/luascripts/luxeodump.lua b/client/luascripts/luxeodump.lua deleted file mode 100644 index 3efc8e7c0..000000000 --- a/client/luascripts/luxeodump.lua +++ /dev/null @@ -1,300 +0,0 @@ ---- --- This Lua script is designed to run with Iceman/RRG Proxmark3 fork --- Just copy luxeodump.lua to client/luascripts/ --- and run "script run luxeodump" - --- requirements -local cmds = require('commands') -local getopt = require('getopt') -local utils = require('utils') -local lib14a = require('read14a') -local ansicolors = require('ansicolors') - -copyright = '' -author = '0xdrrb' -version = 'v0.1.1' -desc = [[ -This is a script that tries to dump and decrypt the data of a specific type of Mifare laundromat token. OBS! Tag must be on the antenna. -]] -example = [[ - script run luxeodump -]] -usage = [[ -script run luxeodump -]] -arguments = [[ - -h This help -]] -local PM3_SUCCESS = 0 - --- Some shortcuts -local band = bit32.band -local bor = bit32.bor -local bnot = bit32.bnot -local bxor = bit32.bxor -local lsh = bit32.lshift -local rsh = bit32.rshift - -local acgreen = ansicolors.bright..ansicolors.green -local accyan = ansicolors.bright..ansicolors.cyan -local acred = ansicolors.red -local acyellow = ansicolors.bright..ansicolors.yellow -local acblue = ansicolors.bright..ansicolors.blue -local acmagenta = ansicolors.bright..ansicolors.magenta -local acoff = ansicolors.reset - - --- This is only meant to be used when errors occur -local function oops(err) - print('ERROR: ', err) - core.clearCommandBuffer() - return nil, err -end ---- --- 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 ---- --- -local function setdevicedebug( status ) - local c = 'hw dbg ' - if status then - c = c..'1' - else - c = c..'0' - end - core.console(c) -end - -local function xteaCrypt(num_rounds, v, key) - local v0 = v[0] - local v1 = v[1] - local delta = 0x9E3779B9 - local sum = 0 - - for i = 0, num_rounds-1 do - -- v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); - v0 = band(bxor(bxor(lsh(v1,4), rsh(v1,5)) + v1, sum + key[band(sum,3)]) + v0, 0xFFFFFFFF) - sum = band(sum + delta, 0xFFFFFFFF) - -- v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]); - v1 = band(bxor(bxor(lsh(v0,4), rsh(v0,5)) + v0, sum + key[band(rsh(sum,11),3)]) + v1, 0xFFFFFFFF) - end - v[0] = v0 - v[1] = v1 -end - -local function xteaDecrypt(num_rounds, v, key) - local v0 = v[0] - local v1 = v[1] - local delta = 0x9E3779B9 - local sum = band(delta * num_rounds, 0xFFFFFFFF) - - for i = 0, num_rounds-1 do - -- v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum>>11) & 3]); - v1 = band(v1 - bxor(bxor(lsh(v0,4), rsh(v0,5)) + v0, sum + key[band(rsh(sum,11),3)]), 0xFFFFFFFF) - sum = band(sum - delta, 0xFFFFFFFF) - -- v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); - v0 = band(v0 - bxor(bxor(lsh(v1,4), rsh(v1,5)) + v1, sum + key[band(sum,3)]), 0xFFFFFFFF) - end - v[0] = v0 - v[1] = v1 -end - -local function createxteakey(mfuid) - local xteakey = {} - local buid = {} - local tmpkey = {} - local uid = {} - - -- Warning ! "it is customary in Lua to START ARRAYS WITH ONE" - buid = utils.ConvertHexToBytes(mfuid) - uid[0] = bor(buid[1], lsh(buid[2], 8)) - uid[1] = bor(buid[3], lsh(buid[4], 8)) - - tmpkey[0] = 0x198B - tmpkey[1] = uid[0] - tmpkey[2] = 0x46D8 - tmpkey[3] = uid[1] - tmpkey[4] = 0x5310 - tmpkey[5] = bxor(uid[0], 0xA312) - tmpkey[6] = 0xFFCB - tmpkey[7] = bxor(uid[1], 0x55AA) - - xteakey[0] = bor(lsh(tmpkey[1], 16), tmpkey[0]) - xteakey[1] = bor(lsh(tmpkey[3], 16), tmpkey[2]) - xteakey[2] = bor(lsh(tmpkey[5], 16), tmpkey[4]) - xteakey[3] = bor(lsh(tmpkey[7], 16), tmpkey[6]) - - return xteakey -end - -local function getblockdata(response) - if not response then - return nil, 'No response from device' - end - if response.Status == PM3_SUCCESS then - return response.Data - else - return nil, "Couldn't read block.. ["..response.Status.."]" - end -end - -local function readblock(blockno, key) - -- Read block N - local keytype = '01' -- key B - local data = ('%02x%s%s'):format(blockno, keytype, key) - local c = Command:newNG{cmd = cmds.CMD_HF_MIFARE_READBL, data = data} - local b, err = getblockdata(c:sendNG(false)) - if not b then return oops(err) end - return b -end - -local function readtag(mfkey,xteakey) - local tagdata = {} - local cleardata = {} - local v = {} - local vv = {} - - -- Read 4 sectors and build table - for sect = 8, 11 do - for blockn = sect * 4, (sect * 4) + 2 do - local blockdata = readblock(blockn, mfkey) - if not blockdata then return oops('[!] failed reading block') end - table.insert(tagdata, blockdata) - end - end - - -- Decrypt data and build clear table - for key,value in ipairs(tagdata) do - local clearblockdata - v[0] = utils.SwapEndianness(value:sub(1, 8), 32) - v[1] = utils.SwapEndianness(value:sub(9, 16), 32) - xteaDecrypt(16, v, xteakey) - vv[0] = utils.SwapEndianness(value:sub(17, 24), 32) - vv[1] = utils.SwapEndianness(value:sub(25, 32), 32) - xteaDecrypt(16, vv, xteakey) - clearblockdata=string.format("%08X%08X%08X%08X", - utils.SwapEndianness(string.format("%08X", v[0]), 32), - utils.SwapEndianness(string.format("%08X", v[1]), 32), - utils.SwapEndianness(string.format("%08X", vv[0]), 32), - utils.SwapEndianness(string.format("%08X", vv[1]), 32)) - table.insert(cleardata, clearblockdata) - end - - return tagdata,cleardata - -end - - -local function main(args) - - -- Arguments for the script - for o, a in getopt.getopt(args, 'h') do - if o == 'h' then return help() end - end - - local xteakey = {} - -- local v = {} - local edata = {} - local cdata = {} - - -- Turn off Debug - setdevicedebug(false) - - -- GET TAG UID - tag, err = lib14a.read(false, true) - if err then - lib14a.disconnect() - return oops(err) - end - core.clearCommandBuffer() - - -- simple tag check - if 0x08 ~= tag.sak then - if 0x0400 ~= tag.atqa then - return oops(('[fail] found tag %s :: looking for Mifare S50 1k'):format(tag.name)) - end - end - - xteakey = createxteakey(tag.uid) - 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) - - if edata == nil or cdata == nil then - print("ERROR Reading tag!") - return nil - end - - print("Ciphered data:") - for key,value in ipairs(edata) do - print(value) - if key % 3 == 0 then print("") end - end - - -- compute CRC for each segment - crcH = utils.SwapEndianness(core.reveng_runmodel("CRC-16/ARC", cdata[1]..cdata[2]..cdata[3]:sub(1,28), false, '0'),16) - crcA = utils.SwapEndianness(core.reveng_runmodel("CRC-16/ARC", cdata[4]..cdata[5]..cdata[6]..cdata[7]:sub(1,28), false, '0'),16) - crcB = utils.SwapEndianness(core.reveng_runmodel("CRC-16/ARC", cdata[8]..cdata[9]..cdata[10]..cdata[11]:sub(1,28), false, '0'),16) - - print("\nHeader:") - for key,value in ipairs(cdata) do - if key == 3 then - print(value:sub(1,28)..acmagenta..value:sub(29,32)..acoff) - if utils.SwapEndianness(value:sub(29,32),16) == crcH then strcrc = " OK" else strcrc = acred.." CRCERROR !!" end - print(acmagenta.."CRC16/ARC = "..string.format("0x%04X", crcH)..strcrc..acoff) - print("\nDataA:") - elseif key == 4 then - print(acgreen..value:sub(1,4)..acoff..value:sub(5,16)..accyan..value:sub(17,24)..acoff..value:sub(25,26)..accyan..value:sub(27,28)..acoff..value:sub(29,32)) - versionA = utils.SwapEndianness(value:sub(1,4),16) - dateA = string.format("%d/%02d/%02d %02d:%02d", tonumber(value:sub(17,18),10)+2000, tonumber(value:sub(19,20),10), - tonumber(string.format("%02X", band(tonumber(value:sub(21,22),16),0x3f)),10), - tonumber(value:sub(23,24),10), tonumber(value:sub(27,28),10)) - elseif key == 8 then - print(acgreen..value:sub(1,4)..acoff..value:sub(5,16)..accyan..value:sub(17,24)..acoff..value:sub(25,26)..accyan..value:sub(27,28)..acoff..value:sub(29,32)) - versionB = utils.SwapEndianness(value:sub(1,4),16) - dateB = string.format("%d/%02d/%02d %02d:%02d", tonumber(value:sub(17,18),10)+2000, tonumber(value:sub(19,20),10), - tonumber(string.format("%02X", band(tonumber(value:sub(21,22),16),0x3f)),10), - tonumber(value:sub(23,24),10), tonumber(value:sub(27,28),10)) - elseif key == 5 then - print(acyellow..value:sub(1,4)..acoff..value:sub(5,32)) - creditA = utils.SwapEndianness(value:sub(1,4),16)/100 - elseif key == 9 then - print(acyellow..value:sub(1,4)..acoff..value:sub(5,32)) - creditB = utils.SwapEndianness(value:sub(1,4),16)/100 - elseif key == 7 then - print(value:sub(1,28)..acmagenta..value:sub(29,32)..acoff) - print(acgreen.."Version "..string.format("0x%04X", versionA)..acoff) - print(acyellow.."Credit : "..creditA..acoff) - if utils.SwapEndianness(value:sub(29,32),16) == crcA then strcrc = " OK" else strcrc = acred.." CRCERROR !!" end - print(acmagenta.."CRC16/ARC = "..string.format("0x%04X", crcA)..strcrc..acoff) - print(accyan.."Date: "..dateA..acoff) - print("\nDataB:") - elseif key == 11 then - print(value:sub(1,28)..acmagenta..value:sub(29,32)..acoff) - print(acgreen.."Version "..string.format("0x%04X", versionB)..acoff) - print(acyellow.."Credit : "..creditB..acoff) - if utils.SwapEndianness(value:sub(29,32),16) == crcB then strcrc = " OK" else strcrc = acred.." CRCERROR !!" end - print(acmagenta.."CRC16/ARC = "..string.format("0x%04X", crcB)..strcrc..acoff) - print(accyan.."Date: "..dateB..acoff) - print("\nFooter:") - else - print(value) - end - end - - return -end - -main(args) diff --git a/client/luascripts/read_pwd_mem.lua b/client/luascripts/mem_readpwd.lua similarity index 91% rename from client/luascripts/read_pwd_mem.lua rename to client/luascripts/mem_readpwd.lua index 0c786f6f9..82343b943 100644 --- a/client/luascripts/read_pwd_mem.lua +++ b/client/luascripts/mem_readpwd.lua @@ -9,31 +9,31 @@ desc = [[ This script will read the flash memory of RDV4 and print the stored passwords/keys. It was meant to be used as a help tool after using the BogRun standalone mode before SPIFFS. -You should now use read_pwd_mem_spiffs instead after the updated BogRun standalone mode. +You should now use data_read_pwd_mem_spiffs instead after the updated BogRun standalone mode. (Iceman) script adapted to read and print keys in the default dictionary flashmemory sections. ]] example = [[ -- This will scan the first 256 bytes of flash memory for stored passwords - script run read_pwd_mem + script run mem_readpwd -- This will scan 256 bytes of flash memory at offset 64 for stored passwords - script run read_pwd_mem -o 64 + script run mem_readpwd -o 64 -- This will scan 32 bytes of flash memory at offset 64 for stored passwords - script run read_pwd_mem -o 64 -l 32 + script run mem_readpwd -o 64 -l 32 -- This will print the stored Mifare dictionary keys - script run read_pwd_mem -m + script run mem_readpwd -m -- This will print the stored t55xx dictionary passwords - script run read_pwd_mem -t + script run mem_readpwd -t -- This will print the stored iClass dictionary keys - script run read_pwd_mem -i + script run mem_readpwd -i ]] usage = [[ - script run read_pwd_mem [-h] [-o ] [-l ] [-k ] [-m] [-t] [-i] + script run mem_readpwd [-h] [-o ] [-l ] [-k ] [-m] [-t] [-i] ]] arguments = [[ -h : this help diff --git a/client/luascripts/read_pwd_mem_spiffs.lua b/client/luascripts/mem_spiffs_readpwd.lua similarity index 93% rename from client/luascripts/read_pwd_mem_spiffs.lua rename to client/luascripts/mem_spiffs_readpwd.lua index ae2f0ba99..b4995e406 100644 --- a/client/luascripts/read_pwd_mem_spiffs.lua +++ b/client/luascripts/mem_spiffs_readpwd.lua @@ -11,16 +11,16 @@ It was meant to be used as a help tool after using the BogRun standalone mode. ]] example = [[ -- This will read the hf_bog.log file in SPIFFS and print the stored passwords - script run read_pwd_mem_spiffs + script run mem_spiffs_readpwd -- This will read the other.log file in SPIFFS and print the stored passwords - script run read_pwd_mem_spiffs -f other.log + script run mem_spiffs_readpwd -f other.log -- This will delete the hf_bog.log file from SPIFFS - script run read_pwd_mem_spiffs -r + script run mem_spiffs_readpwd -r ]] usage = [[ - script run read_pwd_mem_spiffs [-h] [-f ] [-r] + script run mem_spiffs_readpwd [-h] [-f ] [-r] ]] arguments = [[ -h : this help diff --git a/client/luascripts/mfu_magic.lua b/client/luascripts/mfu_magic.lua deleted file mode 100644 index d921a9fb2..000000000 --- a/client/luascripts/mfu_magic.lua +++ /dev/null @@ -1,659 +0,0 @@ -local cmds = require('commands') -local getopt = require('getopt') -local lib14a = require('read14a') -local utils = require('utils') -local ansicolors = require('ansicolors') - --- global -local DEBUG = false -- the debug flag -local bxor = bit32.bxor -local _password = nil -local err_lock = 'use -k or change cfg0 block' - -copyright = 'Copyright (c) 2017 IceSQL AB. All rights reserved.' -author = 'Christian Herrmann' -version = 'v1.1.3' -desc = 'This script enables easy programming of a MAGIC NTAG 21* card' -example = [[ - -- wipe tag - script run mfu_magic -w - - -- wipe a locked down tag by giving the password - script run mfu_magic -k ffffffff -w - - --read magic tag configuration - script run mfu_magic -c - - -- set uid - script run mfu_magic -u 04112233445566 - - -- set pwd / pack - script run mfu_magic -p 11223344 -a 8080 - - -- set version to NTAG213 - script run mfu_magic -v 0004040201000f03 - - -- set signature - script run mfu_magic -s 1122334455667788990011223344556677889900112233445566778899001122 -]] -usage = [[ -script run mfu_magic -h -k -c -w -u -t -p -a -s -o -v -]] -arguments = [[ - -h this help - -c read magic configuration - -u UID (14 hexsymbols), set UID on tag - -t tag type to impersonate - 1 = UL_EV1 48k - 2 = UL_EV1 128k - 3 = NTAG 210 - 4 = NTAG 212 - 5 = NTAG 213 (true) - 6 = NTAG 215 (true) - 7 = NTAG 216 (true) - 8 = NTAG I2C 1K - 9 = NTAG I2C 2K - 10 = NTAG I2C 1K PLUS - 11 = NTAG I2C 2K PLUS - 12 = NTAG 213F (true) - 13 = NTAG 216F (true) - -p password (8 hexsymbols), set password on tag. - -a pack ( 4 hexsymbols), set pack on tag. - -s signature data (64 hexsymbols), set signature data on tag. - -o OTP data (8 hexsymbols), set one-time-pad data on tag. - -v version data (16 hexsymbols), set version data on tag. - -w wipe tag. You can specify password if the tag has been locked down. Fills tag with zeros and put default values for NTAG213 (like -t 5) - -k pwd to use with the wipe option -]] ---- --- A debug printout-function -local function dbg(args) - if not DEBUG then return end - if type(args) == 'table' then - local i = 1 - while result[i] do - dbg(result[i]) - i = i+1 - end - else - print('###', args) - end -end --- This is only meant to be used when errors occur -local function oops(err) - print("ERROR: ",err) - core.clearCommandBuffer() - return nil, err -end ---- --- 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 ---- --- set the global password variable -local function set_password(pwd) - if pwd == nil then _password = nil; return true, 'Ok' end - if #pwd ~= 8 then return nil, 'password wrong length. Must be 4 hex bytes' end - if #pwd == 0 then _password = nil end - _password = pwd - return true, 'Ok' -end ---- Picks out and displays the data read from a tag --- Specifically, takes a usb packet, converts to a Command --- (as in commands.lua), takes the data-array and --- reads the number of bytes specified in arg1 (arg0 in c-struct) --- @param usbpacket the data received from the device -local function getResponseData(usbpacket) - local resp = Command.parse(usbpacket) - local len = tonumber(resp.arg1) * 2 - return string.sub(tostring(resp.data), 0, len); -end ---- --- -local function sendRaw(rawdata, options) - - local flags = lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT - + lib14a.ISO14A_COMMAND.ISO14A_RAW - + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC - - local c = Command:newMIX{cmd = cmds.CMD_HF_ISO14443A_READER, - arg1 = flags, - -- arg2 contains the length, which is half the length of the ASCII-string rawdata - arg2 = string.len(rawdata)/2, - data = rawdata} - - return c:sendMIX(options.ignore_response) -end ---- --- -local function send(payload) - local usb, err = sendRaw(payload,{ignore_response = false}) - if err then return oops(err) end - return getResponseData(usb) -end ---- --- select tag and if password is set, authenticate -local function connect() - core.clearCommandBuffer() - - -- First of all, connect - info, err = lib14a.read(true, true) - if err then - lib14a.disconnect() - return oops(err) - end - core.clearCommandBuffer() - - --authenticate if needed using global variable - if _password then - send('1B'.._password) - end - return true -end --- --- Read magic configuration -local function read_config() - local info = connect() - if not info then return false, "Can't select card" end - - -- read PWD - local pwd = send("30F0"):sub(1,8) - - -- 04 response indicates that blocks has been locked down. - if pwd == '04' then lib14a.disconnect(); return nil, "can't read configuration, "..err_lock end - - -- read PACK - local pack = send("30F1"):sub(1,4) - - -- read SIGNATURE - local signature1 = send('30F2'):sub(1,32) - local signature2 = send('30F6'):sub(1,32) - - -- read VERSION - local version = send('30FA'):sub(1,16) - -- read config - local cardtype = send('30FC'):sub(1,2) - - local typestr = '' - if cardtype == '00' then typestr = 'NTAG 213' - elseif cardtype == '01' then typestr = 'NTAG 215' - elseif cardtype == '02' then typestr = 'NTAG 216' - end - - print('Magic NTAG 21* Configuration') - print(' - Type ', typestr, '(geniune cardtype)') - print(' - Password', pwd) - print(' - Pack ', pack) - print(' - Version ', version) - print(' - Signature', signature1..signature2) - - lib14a.disconnect() - return true, 'Ok' -end ---- --- Write SIGNATURE data -local function write_signature(data) - - -- uid string checks - if data == nil then return nil, 'empty data string' end - if #data == 0 then return nil, 'empty data string' end - if #data ~= 64 then return nil, 'data wrong length. Should be 32 hex bytes' end - - local info = connect() - if not info then return false, "Can't select card" end - - print('Writing new signature') - - local b,c - local cmd = 'A2F%d%s' - local j = 2 - for i = 1, #data, 8 do - b = data:sub(i,i+7) - c = cmd:format(j,b) - local resp = send(c) - if resp == '04' then lib14a.disconnect(); return nil, 'Failed to write signature' end - j = j + 1 - end - lib14a.disconnect() - return true, 'Ok' -end ---- --- Write PWD -local function write_pwd(pwd) - -- PWD string checks - if pwd == nil then return nil, 'empty PWD string' end - if #pwd == 0 then return nil, 'empty PWD string' end - if #pwd ~= 8 then return nil, 'PWD wrong length. Should be 4 hex bytes' end - - local info = connect() - if not info then return false, "Can't select card" end - - print('Writing new PWD ', pwd) - - local resp = send('A2F0'..pwd) - lib14a.disconnect() - if resp == '04' then - return nil, 'Failed to write password' - else - return true, 'Ok' - end -end ---- --- Write PACK -local function write_pack(pack) - -- PACK string checks - if pack == nil then return nil, 'empty PACK string' end - if #pack == 0 then return nil, 'empty PACK string' end - if #pack ~= 4 then return nil, 'PACK wrong length. Should be 4 hex bytes' end - - local info = connect() - if not info then return false, "Can't select card" end - - print('Writing new PACK', pack) - - local resp = send('A2F1'..pack..'0000') - lib14a.disconnect() - if resp == '04' then - return nil, 'Failed to write pack' - else - return true, 'Ok' - end -end --- --- Write OTP block -local function write_otp(block3) - - -- OTP string checks - if block3 == nil then return nil, 'empty OTP string' end - if #block3 == 0 then return nil, 'empty OTP string' end - if #block3 ~= 8 then return nil, 'OTP wrong length. Should be 4 hex bytes' end - - local info = connect() - if not info then return false, "Can't select card" end - - print('Writing new OTP ', block3) - - local resp = send('A203'..block3) - lib14a.disconnect() - if resp == '04' then - return nil, 'Failed to write OTP' - else - return true, 'Ok' - end -end --- --- Writes a UID with bcc1, bcc2. Needs a magic tag. -local function write_uid(uid) - -- uid string checks - if uid == nil then return nil, 'empty uid string' end - if #uid == 0 then return nil, 'empty uid string' end - if #uid ~= 14 then return nil, 'uid wrong length. Should be 7 hex bytes' end - - local info = connect() - if not info then return false, "Can't select card" end - - print('Writing new UID ', uid) - - local uidbytes = utils.ConvertHexToBytes(uid) - local bcc1 = bxor(bxor(bxor(uidbytes[1], uidbytes[2]), uidbytes[3]), 0x88) - local bcc2 = bxor(bxor(bxor(uidbytes[4], uidbytes[5]), uidbytes[6]), uidbytes[7]) - local block0 = string.format('%02X%02X%02X%02X', uidbytes[1], uidbytes[2], uidbytes[3], bcc1) - local block1 = string.format('%02X%02X%02X%02X', uidbytes[4], uidbytes[5], uidbytes[6], uidbytes[7]) - local block2 = string.format('%02X%02X%02X%02X', bcc2, 0x48, 0x00, 0x00) - local resp - - resp = send('A200'..block0) - resp = send('A201'..block1) - resp = send('A202'..block2) - lib14a.disconnect() - - if resp == '04' then - return nil, 'Failed to write new uid' - else - return true, 'Ok' - end -end ---- --- Write VERSION data, --- make sure you have correct version data -local function write_version(data) - -- version string checks - if data == nil then return nil, 'empty version string' end - if #data == 0 then return nil, 'empty version string' end - if #data ~= 16 then return nil, 'version wrong length. Should be 8 hex bytes' end - - local info = connect() - if not info then return false, "Can't select card" end - - print('Writing new version', data) - - local b1 = data:sub(1,8) - local b2 = data:sub(9,16) - local resp - resp = send('A2FA'..b1) - resp = send('A2FB'..b2) - lib14a.disconnect() - if resp == '04' then - return nil, 'Failed to write version' - else - return true, 'Ok' - end -end ---- --- writen TYPE which card is based on. --- 00 = 213, 01 = 215, 02 = 216 -local function write_type(data) - -- type string checks - if data == nil then return nil, 'empty type string' end - if #data == 0 then return nil, 'empty type string' end - if #data ~= 2 then return nil, 'type wrong length. Should be 1 hex byte' end - - local info = connect() - if not info then return false, "Can't select card" end - print('Writing new type', data) - - local resp = send('A2FC'..data..'000000') - lib14a.disconnect() - if resp == '04' then - return nil, 'Failed to write type' - else - return true, 'Ok' - end -end ---- --- Set tag type. Predefinde version data together with magic type set. --- Since cmd always gives 10 bytes len (data+crc) we can impersonate the following types --- we only truely be three types NTAG 213,215 and 216 -local function set_type(tagtype) - - -- tagtype checks - if type(tagtype) == 'string' then tagtype = tonumber(tagtype, 10) end - if tagtype == nil then return nil, 'empty tagtype' end - - if tagtype == 1 then - print('Setting: UL-EV1 48') - 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 - write_type('00') -- based on NTAG213.. - - -- Setting UL-Ev1 default config bl 16,17 - connect() - send('a210000000FF') - send('a21100050000') - - elseif tagtype == 2 then - print('Setting: UL-EV1 128') - write_otp('00000000') -- Setting OTP to default 00 00 00 00 - write_version('0004030101000e03') -- UL-EV1 (128) 00 04 03 01 01 00 0e 03 - write_type('01') - - -- Setting UL-Ev1 default config bl 37,38 - connect() - send('a225000000FF') - send('a22600050000') - elseif tagtype == 3 then - print('Setting: NTAG 210') - write_version('0004040101000b03') -- NTAG210 00 04 04 01 01 00 0b 03 - write_type('00') - - -- Setting NTAG210 default CC block456 - connect() - send('a203e1100600') - send('a2040300fe00') - send('a20500000000') - -- Setting cfg1/cfg2 - send('a210000000FF') - send('a21100050000') - elseif tagtype == 4 then - print('Setting: NTAG 212') - write_version('0004040101000E03') -- NTAG212 00 04 04 01 01 00 0E 03 - write_type('00') - - -- Setting NTAG212 default CC block456 - connect() - send('a203e1101000') - send('a2040103900a') - send('a205340300fe') - -- Setting cfg1/cfg2 - send('a225000000FF') - send('a22600050000') - elseif tagtype == 5 then - print('Setting: NTAG 213') - write_version('0004040201000F03') -- NTAG213 00 04 04 02 01 00 0f 03 - write_type('00') - - -- Setting NTAG213 default CC block456 - connect() - send('a203e1101200') - send('a2040103a00c') - send('a205340300fe') - -- setting cfg1/cfg2 - send('a229000000ff') - send('a22a00050000') - elseif tagtype == 6 then - print('Setting: NTAG 215') - write_version('0004040201001103') -- NTAG215 00 04 04 02 01 00 11 03 - write_type('01') - - -- Setting NTAG215 default CC block456 - connect() - send('a203e1103e00') - send('a2040300fe00') - send('a20500000000') - -- setting cfg1/cfg2 - send('a283000000ff') - send('a28400050000') - elseif tagtype == 7 then - print('Setting: NTAG 216') - write_version('0004040201001303') -- NTAG216 00 04 04 02 01 00 13 03 - write_type('02') - - -- Setting NTAG216 default CC block456 - connect() - send('a203e1106d00') - send('a2040300fe00') - send('a20500000000') - -- setting cfg1/cfg2 - send('a2e3000000ff') - send('a2e400050000') - elseif tagtype == 8 then - print('Setting: NTAG I2C 1K') - write_version('0004040502011303') -- NTAG_I2C_1K 00 04 04 05 02 01 13 03 - write_type('02') - - -- Setting NTAG I2C 1K default CC block456 - connect() - send('a203e1106D00') - send('a2040300fe00') - send('a20500000000') - elseif tagtype == 9 then - print('Setting: NTAG I2C 2K') - write_version('0004040502011503') -- NTAG_I2C_2K 00 04 04 05 02 01 15 03 - write_type('02') - - -- Setting NTAG I2C 2K default CC block456 - connect() - send('a203e110EA00') - send('a2040300fe00') - send('a20500000000') - elseif tagtype == 10 then - print('Setting: NTAG I2C plus 1K') - write_version('0004040502021303') -- NTAG_I2C_1K 00 04 04 05 02 02 13 03 - write_type('02') - - -- Setting NTAG I2C 1K default CC block456 - connect() - send('a203e1106D00') - send('a2040300fe00') - send('a20500000000') - elseif tagtype == 11 then - print('Setting: NTAG I2C plus 2K') - write_version('0004040502021503') -- NTAG_I2C_2K 00 04 04 05 02 02 15 03 - write_type('02') - - -- Setting NTAG I2C 2K default CC block456 - connect() - send('a203e1106D00') - send('a2040300fe00') - send('a20500000000') - elseif tagtype == 12 then - print('Setting: NTAG 213F') - write_version('0004040401000F03') -- NTAG213F 00 04 04 04 01 00 0f 03 - write_type('00') - - -- Setting NTAG213 default CC block456 - connect() - send('a203e1101200') - send('a2040103a00c') - send('a205340300fe') - -- setting cfg1/cfg2 - send('a229000000ff') - send('a22a00050000') - elseif tagtype == 13 then - print('Setting: NTAG 216F') - write_version('0004040401001303') -- NTAG216F 00 04 04 04 01 00 13 03 - write_type('02') - - -- Setting NTAG216 default CC block456 - connect() - send('a203e1106d00') - send('a2040300fe00') - send('a20500000000') - -- setting cfg1/cfg2 - send('a2e3000000ff') - send('a2e400050000') - end - - lib14a.disconnect() - if resp == '04' then - return nil, 'Failed to set type' - else - return true, 'Ok' - end -end ---- --- wipe tag -local function wipe() - - local info = connect() - if not info then return false, "Can't select card" end - - local err, msg, resp - local cmd_empty = 'A2%02X00000000' - local cmd_cfg1 = 'A2%02X000000FF' - local cmd_cfg2 = 'A2%02X00050000' - - print('Wiping tag') - - for b = 3, 0xFB do - --configuration block 0 - if b == 0x29 or b == 0x83 or b == 0xe3 then - local cmd = (cmd_cfg1):format(b) - resp = send(cmd) - --configuration block 1 - elseif b == 0x2a or b == 0x84 or b == 0xe4 then - local cmd = (cmd_cfg2):format(b) - resp = send(cmd) - else - resp = send(cmd_empty:format(b)) - end - if resp == '04' or #resp == 0 then - io.write('\nwrote block '..b, ' failed\n') - err = true - else - io.write('.') - end - io.flush() - end - io.write('\r\n') - - lib14a.disconnect() - - if err then return nil, "Tag locked down, "..err_lock end - - print('setting default values...') - - set_password(nil) - - -- set NTAG213 default values - err, msg = set_type(5) - if err == nil then return err, msg end - - --set UID - err, msg = write_uid('04112233445566') - if err == nil then return err, msg end - - --set pwd - err, msg = write_pwd('FFFFFFFF') - if err == nil then return err, msg end - - --set pack - err, msg = write_pack('0000') - if err == nil then return err, msg end - - return true, 'Ok' -end ---- --- The main entry point -function main(args) - - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print() - - local err, msg - - if #args == 0 then return help() end - - -- Read the parameters - for o, a in getopt.getopt(args, 'hck:u:t:p:a:s:o:v:w') do - - -- help - if o == "h" then return help() end - - --key - if o == 'k' then err, msg = set_password(a) end - - -- configuration - if o == "c" then err, msg = read_config() end - - --wipe tag - if o == "w" then err, msg = wipe() end - - -- write uid - if o == "u" then err, msg = write_uid(a) end - - -- write type/version - if o == "t" then err, msg = set_type(a) end - - -- write pwd - if o == "p" then err, msg = write_pwd(a) end - - -- write pack - if o == "a" then err, msg = write_pack(a) end - - -- write signature - if o == "s" then err, msg = write_signature(a) end - - -- write otp - if o == "o" then err, msg = write_otp(a) end - - -- write version - if o == "v" then err, msg = write_version(a) end - - if err == nil then return oops(msg) end - end - -end - -main(args) diff --git a/client/luascripts/brutesim.lua b/client/luascripts/multi_bruteforce.lua similarity index 94% rename from client/luascripts/brutesim.lua rename to client/luascripts/multi_bruteforce.lua index 4d8ba7d57..e9b320058 100644 --- a/client/luascripts/brutesim.lua +++ b/client/luascripts/multi_bruteforce.lua @@ -18,7 +18,7 @@ desc = [[ \ / \ / `---' `---' -*SUPPORTED TAGS: pyramid, awid, fdx, jablotron, noralsy, presco, visa2000, 14a, hid +*SUPPORTED TAGS: pyramid, awid, fdxb, jablotron, noralsy, presco, visa2000, 14a, hid This script uses the Proxmark3 implementations of simulation to bruteforce given ranges of id. It uses both LF and HF simulations. @@ -26,23 +26,23 @@ It uses both LF and HF simulations. -- Author note -- I wrote this as i was doing a PACS audit. This is far from complete, but is easily expandable. -- The idea was based on proxbrute, but i needed more options, and support for different readers. - -- I dont know LUA, so I used Brian Redbeards lf_bulk_program.lua script as a starting point, sorry if its kludgy. + -- I dont know LUA, so I used Brian Redbeards lf_hid_bulkclone.lua script as a starting point, sorry if its kludgy. ]] example = [[ -- (the above example would bruteforce pyramid tags, starting at 10:1000, ending at 10:991, and waiting 1 second between each card) - script run brutesim -r pyramid -f 10 -b 1000 -c 10 -t 1 -d down + script run multi_bruteforce -r pyramid -f 10 -b 1000 -c 10 -t 1 -d down ]] usage = [[ -script run brutesim -r rfid_tag -f facility_code -b base_card_number -c count -t timeout -d direction +script run multi_bruteforce -r rfid_tag -f facility_code -b base_card_number -c count -t timeout -d direction ]] arguments = [[ -h this help -r *see below RFID Tag: the RFID tag to emulate pyramid awid - fdx + fdxb jablotron noralsy presco @@ -110,7 +110,7 @@ local function isempty(s) return s == nil or s == '' end --- The code below was blatantly stolen from Brian Redbeard's lf_bulk_program.lua script +-- The code below was blatantly stolen from Brian Redbeard's lf_hid_bulkclone.lua script local function toBits(num, bits) bits = bits or math.max(1, select(2, math.frexp(num))) local t = {} @@ -180,8 +180,8 @@ local function main(args) consolecommand = 'lf awid sim' rfidtagname = 'AWID' facilityrequired = 1 - elseif rfidtag == 'fdx' then -- I'm not sure why you would need to bruteforce this ¯\_(ツ)_/¯ - consolecommand = 'lf fdx sim' + elseif rfidtag == 'fdxb' then -- I'm not sure why you would need to bruteforce this ¯\_(ツ)_/¯ + consolecommand = 'lf fdxb sim' rfidtagname = 'FDX-B' facilityrequired = 1 elseif rfidtag == 'jablotron' then @@ -214,7 +214,7 @@ local function main(args) end facilityrequired = 0 -- Disable the FC required check, as we used it for type instead of FC elseif rfidtag == 'hid' then - consolecommand = 'lf hid sim' + consolecommand = 'lf hid sim -r' rfidtagname = 'HID' facilityrequired = 1 else -- Display error and exit out if bad RFID tag was supplied diff --git a/client/luascripts/tracetest.lua b/client/luascripts/tests/data_tracetest.lua similarity index 96% rename from client/luascripts/tracetest.lua rename to client/luascripts/tests/data_tracetest.lua index b7e931b99..1e75b707b 100644 --- a/client/luascripts/tracetest.lua +++ b/client/luascripts/tests/data_tracetest.lua @@ -18,10 +18,10 @@ The following tracefiles will be loaded: modulation*.pm3 ]] example = [[ - 1. script run tracetest + 1. script run data_tracetest ]] usage = [[ -script run tracetest [-h] +script run data_tracetest [-h] ]] arguments = [[ -h : this help @@ -77,7 +77,7 @@ local function main(args) print( string.rep('--',20) ) print( string.rep('--',20) ) - local cmdDataLoad = 'data load %s'; + local cmdDataLoad = 'data load -f %s'; local cwd = core.cwd(); local tracesEM = "find '"..cwd.."/traces/ ' -iname 'em*.pm3' -type f" diff --git a/client/luascripts/hf_read.lua b/client/luascripts/tests/hf_read.lua similarity index 100% rename from client/luascripts/hf_read.lua rename to client/luascripts/tests/hf_read.lua diff --git a/client/luascripts/test_t55x7_ask.lua b/client/luascripts/tests/lf_t55xx_defaultask.lua similarity index 96% rename from client/luascripts/test_t55x7_ask.lua rename to client/luascripts/tests/lf_t55xx_defaultask.lua index adfaf1eeb..efb6ce8fe 100644 --- a/client/luascripts/test_t55x7_ask.lua +++ b/client/luascripts/tests/lf_t55xx_defaultask.lua @@ -36,13 +36,13 @@ Loop: -xx 1C xxxx = RF/128 -testsuit for the ASK/MANCHESTER demod +testsuite for the ASK/MANCHESTER demod ]] example =[[ - 1. script run test_t55x7_ask + 1. script run lf_t55xx_defaultask ]] usage = [[ -script run test_t55x7_ask [-h] +script run lf_t55xx_defaultask [-h] ]] arguments = [[ -h : this help diff --git a/client/luascripts/test_t55x7_bi.lua b/client/luascripts/tests/lf_t55xx_defaultbi.lua similarity index 97% rename from client/luascripts/test_t55x7_bi.lua rename to client/luascripts/tests/lf_t55xx_defaultbi.lua index 4b897b367..495faef02 100644 --- a/client/luascripts/test_t55x7_bi.lua +++ b/client/luascripts/tests/lf_t55xx_defaultbi.lua @@ -33,10 +33,10 @@ Loop: testsuit for the BIPHASE demod ]] example = [[ - 1. script run test_t55x7_bi + 1. script run lf_t55xx_defaultbi ]] usage = [[ -script run test_t55x7_bi [-h] +script run lf_t55xx_defaultbi [-h] ]] arguments = [[ -h : this help diff --git a/client/luascripts/test_t55x7_fsk.lua b/client/luascripts/tests/lf_t55xx_defaultfsk.lua similarity index 97% rename from client/luascripts/test_t55x7_fsk.lua rename to client/luascripts/tests/lf_t55xx_defaultfsk.lua index 7a8d724a0..2a5c272c4 100644 --- a/client/luascripts/test_t55x7_fsk.lua +++ b/client/luascripts/tests/lf_t55xx_defaultfsk.lua @@ -35,10 +35,10 @@ Loop: testsuit for the ASK/MANCHESTER demod ]] example = [[ - 1. script run test_t55x7_fsk + 1. script run lf_t55xx_defaultfsk ]] usage = [[ -script run test_t55x7_fsk [-h] +script run lf_t55xx_defaultfsk [-h] ]] arguments = [[ -h : this help diff --git a/client/luascripts/test_t55x7_psk.lua b/client/luascripts/tests/lf_t55xx_defaultpsk.lua similarity index 98% rename from client/luascripts/test_t55x7_psk.lua rename to client/luascripts/tests/lf_t55xx_defaultpsk.lua index 361b231c4..8b4f0447a 100644 --- a/client/luascripts/test_t55x7_psk.lua +++ b/client/luascripts/tests/lf_t55xx_defaultpsk.lua @@ -30,10 +30,10 @@ Loop OUTER: In all 12 individual test for the PSK demod ]] example = [[ - 1. script run test_t55x7_psk + 1. script run lf_t55xx_defaultpsk ]] usage = [[ -script run test_t55x7_psk [-h] +script run lf_t55xx_defaultpsk [-h] ]] arguments = [[ -h : this help diff --git a/client/luascripts/test_t55x7.lua b/client/luascripts/tests/lf_t55xx_writetest.lua similarity index 71% rename from client/luascripts/test_t55x7.lua rename to client/luascripts/tests/lf_t55xx_writetest.lua index 4be0ed4b6..cfe46d565 100644 --- a/client/luascripts/test_t55x7.lua +++ b/client/luascripts/tests/lf_t55xx_writetest.lua @@ -9,7 +9,7 @@ local floor = math.floor copyright = '' author = "Iceman" -version = 'v1.0.3' +version = 'v1.0.4' desc =[[ This script will program a T55x7 TAG with a configuration and four blocks of data. It will then try to detect and read back those block data and compare if read data matches the expected data. @@ -30,12 +30,12 @@ testsuit for T55XX commands demodulation ]] example = [[ - 1. script run test_t55x7 - 2. script run test_t55x7 -t FSK2A - 3. script run test_t55x7 -t PSK1 + 1. script run lf_t55xx_writetest + 2. script run lf_t55xx_writetest -t FSK2A + 3. script run lf_t55xx_writetest -t PSK1 ]] usage = [[ -script run test_t55x7 [-h] [-t +script run lf_t55xx_writetest [-h] [-t ]] arguments = [[ -h this help @@ -106,36 +106,93 @@ local function GetConfigs( modulation ) local t = {} t['PSK1'] = { - [1] = '00001040', - [2] = '00041040', - [3] = '00081040', - [4] = '000c1040', - [5] = '00101040', - [6] = '00141040', - [7] = '00181040', - [8] = '001c1040', + -- Rf2 + [1] = '00001040', -- 8 + [2] = '00041040', -- 16 + [3] = '00081040', -- 32 + [4] = '000c1040', -- 40 + [5] = '00101040', -- 50 + [6] = '00141040', -- 64 + [7] = '00181040', -- 100 + [8] = '001c1040', -- 128 + -- Rf4 + [9] = '00001440', -- 8 + [10] = '00041440', -- 16 + [11] = '00081440', -- 32 + [12] = '000c1440', -- 40 +-- [] = '00101440', -- 50 50/4 == 12.5 invalid + [13] = '00141440', -- 64 + [14] = '00181440', -- 100 + [15] = '001c1440', -- 128 + -- Rf8 + [16] = '00001840', -- 8 + [17] = '00041840', -- 16 + [18] = '00081840', -- 32 + [19] = '000c1840', -- 40 +-- [] = '00101840', -- 50 50/8 = 6.25 invalid + [20] = '00141840', -- 64 +-- [] = '00181840', -- 100 100/8 == 12.5 invalid + [21] = '001c1840', -- 128 } t['PSK2'] = { - [1] = '00002040', - [2] = '00042040', - [3] = '00082040', - [4] = '000c2040', - [5] = '00102040', - [6] = '00142040', - [7] = '00182040', - [8] = '001c2040', + -- Rf2 + [1] = '00002040', -- 8 + [2] = '00042040', -- 16 + [3] = '00082040', -- 32 + [4] = '000c2040', -- 40 + [5] = '00102040', -- 50 + [6] = '00142040', -- 64 + [7] = '00182040', -- 100 + [8] = '001c2040', -- 128 + -- Rf4 + [9] = '00002440', -- 8 + [10] = '00042440', -- 16 + [11] = '00082440', -- 32 + [12] = '000c2440', -- 40 +-- [] = '00102440', -- 50 50/4 == 12.5 invalid + [13] = '00142440', -- 64 + [14] = '00182440', -- 100 + [15] = '001c2440', -- 128 + -- Rf8 + [16] = '00002840', -- 8 + [17] = '00042840', -- 16 + [18] = '00082840', -- 32 + [19] = '000c2840', -- 40 +-- [] = '00102840', -- 50 50/8 == 6.25 invalid + [20] = '00142840', -- 64 +-- [] = '00182840', -- 100 100/8 == 12.5 invalid + [21] = '001c2840', -- 128 } t['PSK3'] = { - [1] = '00003040', - [2] = '00043040', - [3] = '00083040', - [4] = '000c3040', - [5] = '00103040', - [6] = '00143040', - [7] = '00183040', - [8] = '001c3040', + -- Rf2 + [1] = '00003040', -- 8 + [2] = '00043040', -- 16 + [3] = '00083040', -- 32 + [4] = '000c3040', -- 40 + [5] = '00103040', -- 50 + [6] = '00143040', -- 64 + [7] = '00183040', -- 100 + [8] = '001c3040', -- 128 + -- Rf4 + [9] = '00003440', -- 8 + [10] = '00043440', -- 16 + [11] = '00083440', -- 32 + [12] = '000c3440', -- 40 +-- [] = '00103440', -- 50 50/4 == 12.5 invalid + [13] = '00143440', -- 64 + [14] = '00183440', -- 100 + [15] = '001c3440', -- 128 + -- Rf2 + [16] = '00003840', -- 8 + [17] = '00043840', -- 16 + [18] = '00083840', -- 32 + [19] = '000c3840', -- 40 +-- [] = '00103840', -- 50 50/8 == 6.25 invalid + [20] = '00143840', -- 64 +-- [] = '00183840', -- 100 100/8 == 12.5 invalid + [21] = '001c3840', -- 128 } t['FSK1'] = { diff --git a/client/src/cmdanalyse.c b/client/src/cmdanalyse.c index c4fe2d795..ecf8e50de 100644 --- a/client/src/cmdanalyse.c +++ b/client/src/cmdanalyse.c @@ -12,7 +12,7 @@ #include // size_t #include #include // tolower -#include // printf +//#include // printf #include "commonutil.h" // reflect... #include "comms.h" // clearCommandBuffer #include "cmdparser.h" // command_t @@ -914,16 +914,33 @@ static int CmdAnalyseDemodBuffer(const char *Cmd) { if (c == '0') DemodBuffer[i] = 0; - printf("%c", c); + PrintAndLogEx(NORMAL, "%c" NOLF, c); } - printf("\n"); + PrintAndLogEx(NORMAL, ""); DemodBufferLen = len; free(data); return PM3_SUCCESS; } +static int CmdAnalyseFreq(const char *Cmd) { + +// char cmdp = tolower(param_getchar(Cmd, 0)); +// if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_freq(); + + const double c = 299792458; + double len_125 = c / 125000; + double len_134 = c / 134000; + double len_1356 = c / 13560000; + + PrintAndLogEx(INFO, "Wavelengths"); + PrintAndLogEx(INFO, " 125 kHz has %f meters", len_125); + PrintAndLogEx(INFO, " 134 kHz has %f meters", len_134); + PrintAndLogEx(INFO, " 13.56 mHz has %f meters", len_1356); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"lcr", CmdAnalyseLCR, AlwaysAvailable, "Generate final byte for XOR LRC"}, @@ -935,6 +952,7 @@ static command_t CommandTable[] = { {"a", CmdAnalyseA, AlwaysAvailable, "num bits test"}, {"nuid", CmdAnalyseNuid, AlwaysAvailable, "create NUID from 7byte UID"}, {"demodbuff", CmdAnalyseDemodBuffer, AlwaysAvailable, "Load binary string to demodbuffer"}, + {"freq", CmdAnalyseFreq, AlwaysAvailable, "Calc wave lengths"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdcrc.c b/client/src/cmdcrc.c index 8830f5530..4f241dcee 100644 --- a/client/src/cmdcrc.c +++ b/client/src/cmdcrc.c @@ -25,6 +25,7 @@ #include "reveng.h" #include "ui.h" #include "util.h" +#include "pm3_cmd.h" #define MAX_ARGS 20 @@ -253,7 +254,6 @@ int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *res int ibperhx = 8, obperhx = 8; // int rflags = 0; // search flags - int c; poly_t apoly, crc; char *string; @@ -265,7 +265,7 @@ int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *res SETBMP(); //set model - c = mbynam(&model, inModel); + int c = mbynam(&model, inModel); if (!c) { PrintAndLogEx(ERR, "error: preset model '%s' not found. Use reveng -D to list presets. [%d]", inModel, c); return 0; @@ -405,7 +405,7 @@ static int CmdrevengSearch(const char *Cmd) { #define NMODELS 106 - char inHexStr[100] = {0x00}; + char inHexStr[256] = {0x00}; int dataLen = param_getstr(Cmd, 0, inHexStr, sizeof(inHexStr)); if (dataLen < 4) return 0; @@ -418,7 +418,12 @@ static int CmdrevengSearch(const char *Cmd) { char revResult[30]; int ans = GetModels(Models, &count, width); bool found = false; - if (!ans) return 0; + if (!ans) { + for (int i = 0; i < count; i++) { + free(Models[i]); + } + return 0; + } // try each model and get result for (int i = 0; i < count; i++) { @@ -429,8 +434,10 @@ static int CmdrevengSearch(const char *Cmd) { // round up to # of characters in this model's crc uint8_t crcChars = ((width[i] + 7) / 8) * 2; // can't test a model that has more crc digits than our data - if (crcChars >= dataLen) + if (crcChars >= dataLen) { + free(Models[i]); continue; + } PrintAndLogEx(DEBUG , "DEBUG: dataLen %d, crcChars %u, width[i] %u" @@ -439,8 +446,10 @@ static int CmdrevengSearch(const char *Cmd) { , width[i] ); - if (crcChars == 0) + if (crcChars == 0) { + free(Models[i]); continue; + } memset(result, 0, 30); char *inCRC = calloc(crcChars + 1, sizeof(char)); @@ -492,26 +501,29 @@ static int CmdrevengSearch(const char *Cmd) { free(Models[i]); } - if (!found) PrintAndLogEx(FAILED, "\nno matches found\n"); - return 1; + if (found == false) + PrintAndLogEx(FAILED, "\nno matches found\n"); + + return PM3_SUCCESS; } int CmdCrc(const char *Cmd) { - char name[] = {"reveng "}; - char Cmd2[100 + 7]; - memcpy(Cmd2, name, 7); - memcpy(Cmd2 + 7, Cmd, 100); + char c[100 + 7]; + snprintf(c, sizeof(c), "reveng "); + snprintf(c + strlen(c), sizeof(c) - strlen(c), Cmd, strlen(Cmd)); + char *argv[MAX_ARGS]; - int argc = split(Cmd2, argv); + int argc = split(c, argv); if (argc == 3 && memcmp(argv[1], "-g", 2) == 0) { CmdrevengSearch(argv[2]); } else { reveng_main(argc, argv); } + for (int i = 0; i < argc; ++i) { free(argv[i]); } - return 0; + return PM3_SUCCESS; } diff --git a/client/src/cmddata.c b/client/src/cmddata.c index 3765c2ded..4f5c55363 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -27,45 +27,17 @@ #include "fileutils.h" // searchFile #include "mifare/ndef.h" #include "cliparser.h" +#include "cmdlft55xx.h" // print... uint8_t DemodBuffer[MAX_DEMOD_BUF_LEN]; size_t DemodBufferLen = 0; -size_t g_DemodStartIdx = 0; +int32_t g_DemodStartIdx = 0; int g_DemodClock = 0; static int CmdHelp(const char *Cmd); -static int usage_data_save(void) { - PrintAndLogEx(NORMAL, "Save trace from graph window , i.e. the GraphBuffer"); - PrintAndLogEx(NORMAL, "This is a text file with number -127 to 127. With the option `w` you can save it as wave file"); - PrintAndLogEx(NORMAL, "Filename should be without file extension"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: data save [h] [w] [f ]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " w save as wave format (.wav)"); - PrintAndLogEx(NORMAL, " f save file name"); - PrintAndLogEx(NORMAL, "Samples:"); - PrintAndLogEx(NORMAL, " data save f mytrace - save graphbuffer to file"); - PrintAndLogEx(NORMAL, " data save f mytrace w - save graphbuffer to wave file"); - return PM3_SUCCESS; -} -static int usage_data_scale(void) { - PrintAndLogEx(NORMAL, "Set cursor display scale."); - PrintAndLogEx(NORMAL, "Setting the scale makes the differential `dt` reading between the yellow and purple markers meaningful. "); - PrintAndLogEx(NORMAL, "once the scale is set, the differential reading between brackets is the time duration in seconds."); - PrintAndLogEx(NORMAL, "For example, if acquiring in 125kHz, use scale 125."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: data scale [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " sets scale of carrier frequency expressed in kHz"); - PrintAndLogEx(NORMAL, "Samples:"); - PrintAndLogEx(NORMAL, " data scale 125 - if sampled in 125kHz"); - return PM3_SUCCESS; -} static int usage_data_printdemodbuf(void) { - PrintAndLogEx(NORMAL, "Usage: data printdemodbuffer x o l "); + PrintAndLogEx(NORMAL, "Usage: data print x o l "); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h this help"); PrintAndLogEx(NORMAL, " i invert Demodbuffer before printing"); @@ -250,15 +222,6 @@ static int usage_data_autocorr(void) { PrintAndLogEx(NORMAL, " g save back to GraphBuffer (overwrite)"); return PM3_SUCCESS; } -static int usage_data_undecimate(void) { - PrintAndLogEx(NORMAL, "Usage: data undec [factor]"); - PrintAndLogEx(NORMAL, "This function performs un-decimation, by repeating each sample N times"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " factor The number of times to repeat each sample.[default:2]"); - PrintAndLogEx(NORMAL, "Example: 'data undec 3'"); - return PM3_SUCCESS; -} static int usage_data_detectclock(void) { PrintAndLogEx(NORMAL, "Usage: data detectclock [modulation] "); PrintAndLogEx(NORMAL, " [modulation as char], specify the modulation type you want to detect the clock of"); @@ -283,7 +246,7 @@ static int usage_data_bin2hex(void) { } static int usage_data_buffclear(void) { PrintAndLogEx(NORMAL, "This function clears the bigbuff on deviceside"); - PrintAndLogEx(NORMAL, "Usage: data buffclear [h]"); + PrintAndLogEx(NORMAL, "Usage: data clear [h]"); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h This help"); return PM3_SUCCESS; @@ -557,27 +520,12 @@ static int CmdConvertBitStream(const char *Cmd) { //verbose will print results and demoding messages //emSearch will auto search for EM410x format in bitstream //askType switches decode: ask/raw = 0, ask/manchester = 1 -int ASKDemod_ext(const char *Cmd, bool verbose, bool emSearch, uint8_t askType, bool *stCheck) { - int invert = 0; - int clk = 0; - int maxErr = 100; - size_t maxLen = 0; +int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxLen, bool amplify, bool verbose, bool emSearch, uint8_t askType, bool *stCheck) { + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) clk %i invert %i maxErr %i maxLen %zu amplify %i verbose %i emSearch %i askType %i ", clk, invert, maxErr, maxLen, amplify, verbose, emSearch, askType); uint8_t askamp = 0; - char amp = tolower(param_getchar(Cmd, 0)); - - sscanf(Cmd, "%i %i %i %zu %c", &clk, &invert, &maxErr, &maxLen, &); if (!maxLen) maxLen = pm3_capabilities.bigbuf_size; - if (invert != 0 && invert != 1) { - PrintAndLogEx(WARNING, "Invalid argument: %s", Cmd); - return PM3_EINVARG; - } - - if (clk == 1) { - invert = 1; - clk = 0; - } uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); if (bits == NULL) { return PM3_EMALLOC; @@ -597,7 +545,7 @@ int ASKDemod_ext(const char *Cmd, bool verbose, bool emSearch, uint8_t askType, int foundclk = 0; //amplify signal before ST check - if (amp == 'a') { + if (amplify) { askAmp(bits, BitLen); } @@ -658,9 +606,9 @@ int ASKDemod_ext(const char *Cmd, bool verbose, bool emSearch, uint8_t askType, free(bits); return PM3_SUCCESS; } -int ASKDemod(const char *Cmd, bool verbose, bool emSearch, uint8_t askType) { +int ASKDemod(int clk, int invert, int maxErr, size_t maxLen, bool amplify, bool verbose, bool emSearch, uint8_t askType) { bool st = false; - return ASKDemod_ext(Cmd, verbose, emSearch, askType, &st); + return ASKDemod_ext(clk, invert, maxErr, maxLen, amplify, verbose, emSearch, askType, &st); } //by marshmellow @@ -668,21 +616,47 @@ int ASKDemod(const char *Cmd, bool verbose, bool emSearch, uint8_t askType) { //attempts to demodulate ask while decoding manchester //prints binary found and saves in graphbuffer for further commands static int Cmdaskmandemod(const char *Cmd) { + + size_t slen = strlen(Cmd); + char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 45 || cmdp == 'h') return usage_data_rawdemod_am(); + if (slen > 45 || cmdp == 'h') return usage_data_rawdemod_am(); - bool st = true; - if (Cmd[0] == 's') - return ASKDemod_ext(Cmd++, true, true, 1, &st); - else if (Cmd[1] == 's') - return ASKDemod_ext(Cmd += 2, true, true, 1, &st); + bool st = false, amplify = false; + int clk = 0, invert = 0, maxErr = 100; + size_t maxLen = 0; - return ASKDemod(Cmd, true, true, 1); + if (slen) { + + if (Cmd[0] == 's') { + st = true; + Cmd++; + } else if (slen > 1 && Cmd[1] == 's') { + st = true; + Cmd += 2; + } + + char amp = tolower(param_getchar(Cmd, 0)); + sscanf(Cmd, "%i %i %i %zu %c", &clk, &invert, &maxErr, &maxLen, &); + + amplify = (amp == 'a'); + } + + if (clk == 1) { + invert = 1; + clk = 0; + } + + if (invert != 0 && invert != 1) { + PrintAndLogEx(WARNING, "Invalid value for invert: %i", invert); + return PM3_EINVARG; + } + return ASKDemod_ext(clk, invert, maxErr, maxLen, amplify, true, true, 1, &st); } //by marshmellow //manchester decode -//stricktly take 10 and 01 and convert to 0 and 1 +//strictly take 10 and 01 and convert to 0 and 1 static int Cmdmandecoderaw(const char *Cmd) { size_t size = 0; int high = 0, low = 0; @@ -785,10 +759,8 @@ static int CmdBiphaseDecodeRaw(const char *Cmd) { //by marshmellow // - ASK Demod then Biphase decode GraphBuffer samples -int ASKbiphaseDemod(const char *Cmd, bool verbose) { +int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) { //ask raw demod GraphBuffer first - int offset = 0, clk = 0, invert = 0, maxErr = 50; - sscanf(Cmd, "%i %i %i %i", &offset, &clk, &invert, &maxErr); uint8_t BitStream[MAX_DEMOD_BUF_LEN]; size_t size = getFromGraphBuf(BitStream); @@ -828,16 +800,33 @@ int ASKbiphaseDemod(const char *Cmd, bool verbose) { static int Cmdaskbiphdemod(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (strlen(Cmd) > 25 || cmdp == 'h') return usage_data_rawdemod_ab(); - - return ASKbiphaseDemod(Cmd, true); + int offset = 0, clk = 0, invert = 0, maxErr = 50; + sscanf(Cmd, "%i %i %i %i", &offset, &clk, &invert, &maxErr); + return ASKbiphaseDemod(offset, clk, invert, maxErr, true); } //by marshmellow - see ASKDemod static int Cmdaskrawdemod(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (strlen(Cmd) > 25 || cmdp == 'h') return usage_data_rawdemod_ar(); - - return ASKDemod(Cmd, true, false, 0); + bool st = false; + int clk = 0; + int invert = 0; + int maxErr = 100; + size_t maxLen = 0; + bool amplify = false; + char amp = tolower(param_getchar(Cmd, 0)); + sscanf(Cmd, "%i %i %i %zu %c", &clk, &invert, &maxErr, &maxLen, &); + amplify = amp == 'a'; + if (clk == 1) { + invert = 1; + clk = 0; + } + if (invert != 0 && invert != 1) { + PrintAndLogEx(WARNING, "Invalid value for invert: %i", invert); + return PM3_EINVARG; + } + return ASKDemod_ext(clk, invert, maxErr, maxLen, amplify, true, false, 0, &st); } int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveGrph, bool verbose) { @@ -996,12 +985,29 @@ static int CmdBuffClear(const char *Cmd) { return PM3_SUCCESS; } -static int CmdDec(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - for (size_t i = 0; i < (GraphTraceLen / 2); ++i) - GraphBuffer[i] = GraphBuffer[i * 2]; - GraphTraceLen /= 2; - PrintAndLogEx(NORMAL, "decimated by 2"); +static int CmdDecimate(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "data decimate", + "Performs decimation, by reducing samples N times in the grapbuf. Good for PSK\n", + "data decimate\n" + "data decimate 4" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0(NULL, NULL, "", "factor to reduce sample set (default 2)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int n = arg_get_int_def(ctx, 1, 2); + CLIParserFree(ctx); + + for (size_t i = 0; i < (GraphTraceLen / n); ++i) + GraphBuffer[i] = GraphBuffer[i * n]; + + GraphTraceLen /= n; + PrintAndLogEx(SUCCESS, "decimated by " _GREEN_("%u"), n); RepaintGraphWindow(); return PM3_SUCCESS; } @@ -1012,19 +1018,34 @@ static int CmdDec(const char *Cmd) { * @param Cmd * @return */ -static int CmdUndec(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_data_undecimate(); +static int CmdUndecimate(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "data undecimate", + "Performs un-decimation, by repeating each sample N times in the graphbuf", + "data undecimate\n" + "data undecimate 4\n" + ); - uint8_t factor = param_get8ex(Cmd, 0, 2, 10); + void *argtable[] = { + arg_param_begin, + arg_int0(NULL, NULL, "", "factor to repeat each sample (default 2)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int factor = arg_get_int_def(ctx, 1, 2); + CLIParserFree(ctx); //We have memory, don't we? int swap[MAX_GRAPH_TRACE_LEN] = {0}; uint32_t g_index = 0, s_index = 0; while (g_index < GraphTraceLen && s_index + factor < MAX_GRAPH_TRACE_LEN) { int count = 0; - for (count = 0; count < factor && s_index + count < MAX_GRAPH_TRACE_LEN; count++) - swap[s_index + count] = GraphBuffer[g_index]; + for (count = 0; count < factor && s_index + count < MAX_GRAPH_TRACE_LEN; count++) { + swap[s_index + count] = ( + (double)(factor - count) / (factor - 1)) * GraphBuffer[g_index] + + ((double)count / factor) * GraphBuffer[g_index + 1] + ; + } s_index += count; g_index++; } @@ -1140,24 +1161,8 @@ static char *GetFSKType(uint8_t fchigh, uint8_t fclow, uint8_t invert) { //fsk raw demod and print binary //takes 4 arguments - Clock, invert, fchigh, fclow //defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a)) -int FSKrawDemod(const char *Cmd, bool verbose) { +int FSKrawDemod(uint8_t rfLen, uint8_t invert, uint8_t fchigh, uint8_t fclow, bool verbose) { //raw fsk demod no manchester decoding no start bit finding just get binary from wave - uint8_t rfLen, invert, fchigh, fclow; - - //set defaults - //set options from parameters entered with the command - rfLen = param_get8(Cmd, 0); - invert = param_get8(Cmd, 1); - fchigh = param_get8(Cmd, 2); - fclow = param_get8(Cmd, 3); - - if (strlen(Cmd) > 0 && strlen(Cmd) <= 2) { - if (rfLen == 1) { - invert = 1; //if invert option only is used - rfLen = 0; - } - } - if (getSignalProperties()->isnoise) return PM3_ESOFT; @@ -1218,26 +1223,27 @@ out: static int CmdFSKrawdemod(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (strlen(Cmd) > 20 || cmdp == 'h') return usage_data_rawdemod_fs(); + uint8_t rfLen, invert, fchigh, fclow; - return FSKrawDemod(Cmd, true); + //set defaults + //set options from parameters entered with the command + rfLen = param_get8(Cmd, 0); + invert = param_get8(Cmd, 1); + fchigh = param_get8(Cmd, 2); + fclow = param_get8(Cmd, 3); + + if (strlen(Cmd) > 0 && strlen(Cmd) <= 2) { + if (rfLen == 1) { + invert = 1; //if invert option only is used + rfLen = 0; + } + } + return FSKrawDemod(rfLen, invert, fchigh, fclow, true); } //by marshmellow //attempt to psk1 demod graph buffer -int PSKDemod(const char *Cmd, bool verbose) { - int invert = 0, clk = 0, maxErr = 100; - - sscanf(Cmd, "%i %i %i", &clk, &invert, &maxErr); - - if (clk == 1) { - invert = 1; - clk = 0; - } - if (invert != 0 && invert != 1) { - if (g_debugMode || verbose) PrintAndLogEx(WARNING, "Invalid argument: %s", Cmd); - return PM3_EINVARG; - } - +int PSKDemod(int clk, int invert, int maxErr, bool verbose) { if (getSignalProperties()->isnoise) return PM3_ESOFT; @@ -1276,91 +1282,13 @@ int PSKDemod(const char *Cmd, bool verbose) { return PM3_SUCCESS; } -int demodIdteck(void) { - - if (PSKDemod("", false) != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck PSKDemod failed"); - return PM3_ESOFT; - } - size_t size = DemodBufferLen; - - //get binary from PSK1 wave - int idx = detectIdteck(DemodBuffer, &size); - if (idx < 0) { - - if (idx == -1) - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: not enough samples"); - else if (idx == -2) - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: just noise"); - else if (idx == -3) - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: preamble not found"); - else if (idx == -4) - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: size not correct: %zu", size); - else - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: idx: %d", idx); - - // if didn't find preamble try again inverting - if (PSKDemod("1", false) != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck PSKDemod failed"); - return PM3_ESOFT; - } - idx = detectIdteck(DemodBuffer, &size); - if (idx < 0) { - - if (idx == -1) - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: not enough samples"); - else if (idx == -2) - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: just noise"); - else if (idx == -3) - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: preamble not found"); - else if (idx == -4) - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: size not correct: %zu", size); - else - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: idx: %d", idx); - - return PM3_ESOFT; - } - } - setDemodBuff(DemodBuffer, 64, idx); - - //got a good demod - uint32_t id = 0; - uint32_t raw1 = bytebits_to_byte(DemodBuffer, 32); - uint32_t raw2 = bytebits_to_byte(DemodBuffer + 32, 32); - - //parity check (TBD) - //checksum check (TBD) - - //output - PrintAndLogEx(SUCCESS, "IDTECK Tag Found: Card ID %u , Raw: %08X%08X", id, raw1, raw2); - return PM3_SUCCESS; -} - -/* -static int CmdIdteckDemod(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - return demodIdteck(); -} -*/ - // by marshmellow // takes 3 arguments - clock, invert, maxErr as integers // attempts to demodulate nrz only // prints binary found and saves in demodbuffer for further commands -int NRZrawDemod(const char *Cmd, bool verbose) { +int NRZrawDemod(int clk, int invert, int maxErr, bool verbose) { int errCnt = 0, clkStartIdx = 0; - int invert = 0, clk = 0, maxErr = 100; - sscanf(Cmd, "%i %i %i", &clk, &invert, &maxErr); - if (clk == 1) { - invert = 1; - clk = 0; - } - - if (invert != 0 && invert != 1) { - PrintAndLogEx(WARNING, "(NRZrawDemod) Invalid argument: %s", Cmd); - return PM3_EINVARG; - } if (getSignalProperties()->isnoise) return PM3_ESOFT; @@ -1409,8 +1337,18 @@ int NRZrawDemod(const char *Cmd, bool verbose) { static int CmdNRZrawDemod(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (strlen(Cmd) > 16 || cmdp == 'h') return usage_data_rawdemod_nr(); + int invert = 0, clk = 0, maxErr = 100; + sscanf(Cmd, "%i %i %i", &clk, &invert, &maxErr); + if (clk == 1) { + invert = 1; + clk = 0; + } - return NRZrawDemod(Cmd, true); + if (invert != 0 && invert != 1) { + PrintAndLogEx(WARNING, "(NRZrawDemod) Invalid argument: %s", Cmd); + return PM3_EINVARG; + } + return NRZrawDemod(clk, invert, maxErr, true); } // by marshmellow @@ -1420,8 +1358,17 @@ static int CmdNRZrawDemod(const char *Cmd) { int CmdPSK1rawDemod(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (strlen(Cmd) > 16 || cmdp == 'h') return usage_data_rawdemod_p1(); - - int ans = PSKDemod(Cmd, true); + int clk = 0, invert = 0, maxErr = 100; + sscanf(Cmd, "%i %i %i", &clk, &invert, &maxErr); + if (clk == 1) { + invert = 1; + clk = 0; + } + if (invert != 0 && invert != 1) { + PrintAndLogEx(WARNING, "Invalid value for invert: %i", invert); + return PM3_EINVARG; + } + int ans = PSKDemod(clk, invert, maxErr, true); //output if (ans != PM3_SUCCESS) { if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans); @@ -1438,8 +1385,17 @@ int CmdPSK1rawDemod(const char *Cmd) { static int CmdPSK2rawDemod(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (strlen(Cmd) > 16 || cmdp == 'h') return usage_data_rawdemod_p2(); - - int ans = PSKDemod(Cmd, true); + int clk = 0, invert = 0, maxErr = 100; + sscanf(Cmd, "%i %i %i", &clk, &invert, &maxErr); + if (clk == 1) { + invert = 1; + clk = 0; + } + if (invert != 0 && invert != 1) { + PrintAndLogEx(WARNING, "Invalid value for invert: %i", invert); + return PM3_EINVARG; + } + int ans = PSKDemod(clk, invert, maxErr, true); if (ans != PM3_SUCCESS) { if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans); return PM3_ESOFT; @@ -1500,7 +1456,7 @@ void setClockGrid(uint32_t clk, int offset) { } int CmdGrid(const char *Cmd) { - sscanf(Cmd, "%i %i", &PlotGridX, &PlotGridY); + sscanf(Cmd, "%lf %lf", &PlotGridX, &PlotGridY); PlotGridXdefault = PlotGridX; PlotGridYdefault = PlotGridY; RepaintGraphWindow(); @@ -1738,6 +1694,48 @@ int CmdTuneSamples(const char *Cmd) { 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)); + const double vdd_rdv4 = 9000; + const double vdd_other = 5400; // Empirical measures in mV + double vdd = IfPm3Rdv4Fw() ? vdd_rdv4 : vdd_other; + if (package->peak_v > NON_VOLTAGE && package->peak_f > 0) { + + // Q measure with Q=f/delta_f + double v_3db_scaled = (double)(package->peak_v * 0.707) / 512; // /512 == >>9 + uint32_t s2 = 0, s4 = 0; + for (int i = 1; i < 256; i++) { + if ((s2 == 0) && (package->results[i] > v_3db_scaled)) { + s2 = i; + } + if ((s2 != 0) && (package->results[i] < v_3db_scaled)) { + s4 = i; + break; + } + } + double lfq1 = 0; + if (s4 != 0) { // we got all our points of interest + double a = package->results[s2 - 1]; + double b = package->results[s2]; + double f1 = LF_DIV2FREQ(s2 - 1 + (v_3db_scaled - a) / (b - a)); + double c = package->results[s4 - 1]; + 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); + } + + // 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); + // cross-check results + if (lfq1 > 3) { + double approx_vdd = (double)package->peak_v * 3.14 / 2 / lfq1; + if ((approx_vdd > (vdd_rdv4 + vdd_other) / 2) && (! IfPm3Rdv4Fw())) + PrintAndLogEx(WARNING, "Contradicting measures seem to indicate you're running a " _YELLOW_("PM3_OTHER firmware on a RDV4") ", please check your setup"); + if ((approx_vdd < (vdd_rdv4 + vdd_other) / 2) && (IfPm3Rdv4Fw())) + PrintAndLogEx(WARNING, "Contradicting measures seem to indicate you're running a " _YELLOW_("PM3_RDV4 firmware on a non-RDV4") ", please check your setup"); + } + } + char judgement[20]; memset(judgement, 0, sizeof(judgement)); // LF evaluation @@ -1757,6 +1755,11 @@ int CmdTuneSamples(const char *Cmd) { memset(judgement, 0, sizeof(judgement)); + 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); + } if (package->v_hf < HF_UNUSABLE_V) sprintf(judgement, _RED_("UNUSABLE")); else if (package->v_hf < HF_MARGINAL_V) @@ -1765,6 +1768,7 @@ int CmdTuneSamples(const char *Cmd) { sprintf(judgement, _GREEN_("OK")); PrintAndLogEx((package->v_hf < HF_UNUSABLE_V) ? WARNING : SUCCESS, "HF antenna is %s", judgement); + PrintAndLogEx(NORMAL, "\n(*) Q factor must be measured without tag on the antenna"); // graph LF measurements // even here, these values has 3% error. @@ -1775,9 +1779,11 @@ int CmdTuneSamples(const char *Cmd) { } if (test1 > 0) { - PrintAndLogEx(SUCCESS, "\nDisplaying LF tuning graph. Divisor %d is %.2f kHz, %d is %.2f kHz.\n\n", + 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)); GraphTraceLen = 256; + CursorCPos = LF_DIVISOR_125; + CursorDPos = LF_DIVISOR_134; ShowGraphWindow(); RepaintGraphWindow(); } else { @@ -1789,16 +1795,26 @@ int CmdTuneSamples(const char *Cmd) { } static int CmdLoad(const char *Cmd) { - char filename[FILE_PATH_SIZE] = {0x00}; - int len = 0; - len = strlen(Cmd); - if (len == 0) return PM3_EFILE; + CLIParserContext *ctx; + CLIParserInit(&ctx, "data load", + "This command loads the contents of a pm3 file into graph window\n", + "data load -f myfilename" + ); - if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; - memcpy(filename, Cmd, len); + void *argtable[] = { + arg_param_begin, + arg_strx0("f", "file", "", "file to load"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - char *path; + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); + + char *path = NULL; if (searchFile(&path, TRACES_SUBDIR, filename, ".pm3", true) != PM3_SUCCESS) { if (searchFile(&path, TRACES_SUBDIR, filename, "", false) != PM3_SUCCESS) { return PM3_EFILE; @@ -1822,7 +1838,6 @@ static int CmdLoad(const char *Cmd) { if (GraphTraceLen >= MAX_GRAPH_TRACE_LEN) break; } - fclose(f); PrintAndLogEx(SUCCESS, "loaded " _YELLOW_("%zu") " samples", GraphTraceLen); @@ -1852,6 +1867,7 @@ int CmdLtrim(const char *Cmd) { GraphBuffer[i - ds] = GraphBuffer[i]; GraphTraceLen -= ds; + g_DemodStartIdx -= ds; RepaintGraphWindow(); return PM3_SUCCESS; } @@ -1874,7 +1890,8 @@ static int CmdMtrim(const char *Cmd) { uint32_t start = 0, stop = 0; sscanf(Cmd, "%u %u", &start, &stop); - if (start > GraphTraceLen || stop > GraphTraceLen || start > stop) return PM3_ESOFT; + if (start > GraphTraceLen || stop > GraphTraceLen || start >= stop) + return PM3_ESOFT; // leave start position sample start++; @@ -1920,39 +1937,31 @@ int CmdPlot(const char *Cmd) { int CmdSave(const char *Cmd) { - int len = 0; - char filename[FILE_PATH_SIZE] = {0x00}; - uint8_t cmdp = 0; - bool errors = false, as_wave = false, has_name = false; + CLIParserContext *ctx; + CLIParserInit(&ctx, "data save", + "Save trace from graph window , i.e. the GraphBuffer\n" + "This is a text file with number -127 to 127. With the option `w` you can save it as wave file\n" + "Filename should be without file extension", + "data save -f myfilename -> save graph buffer to file\n" + "data save --wave -f myfilename -> save graph buffer to wave file" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - char ctmp = tolower(param_getchar(Cmd, cmdp)); - switch (ctmp) { - case 'h': - return usage_data_save(); - case 'f': - len = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - if (len < 1) { - errors = true; - break; - } - has_name = true; - cmdp += 2; - break; - case 'w': - as_wave = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } + void *argtable[] = { + arg_param_begin, + arg_lit0("w", "wave", "save as wave format (.wav)"), + arg_str1("f", "file", "", "save file name"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - if (!has_name) errors = true; + bool as_wave = arg_get_lit(ctx, 1); - if (errors || cmdp == 0) return usage_data_save(); + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + // CLIGetStrWithReturn(ctx, 2, (uint8_t *)filename, &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + CLIParserFree(ctx); if (as_wave) return saveFileWAVE(filename, GraphBuffer, GraphTraceLen); @@ -1960,15 +1969,33 @@ int CmdSave(const char *Cmd) { return saveFilePM3(filename, GraphBuffer, GraphTraceLen); } -static int CmdScale(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_data_scale(); +static int CmdTimeScale(const char *Cmd) { - CursorScaleFactor = atoi(Cmd); - if (CursorScaleFactor == 0) { - PrintAndLogEx(FAILED, "bad, can't have zero scale"); + CLIParserContext *ctx; + CLIParserInit(&ctx, "data timescale", + "Set cursor display timescale.\n" + "Setting the timescale makes the differential `dt` reading between the yellow and purple markers meaningful.\n" + "once the timescale is set, the differential reading between brackets can become a time duration.", + "data timescale --sr 125 -u ms -> for LF sampled at 125 kHz. Reading will be in milliseconds\n" + "data timescale --sr 1.695 -u us -> for HF sampled at 16 * fc/128. Reading will be in microseconds\n" + "data timescale --sr 16 -u ETU -> for HF with 16 samples per ETU (fc/128). Reading will be in ETUs" + ); + void *argtable[] = { + arg_param_begin, + arg_dbl1(NULL, "sr", "", "sets timescale factor according to sampling rate"), + arg_str0("u", "unit", "", "time unit to display (max 10 chars)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + CursorScaleFactor = arg_get_dbl_def(ctx, 1, 1); + if (CursorScaleFactor <= 0) { + PrintAndLogEx(FAILED, "bad, can't have negative or zero timescale factor"); CursorScaleFactor = 1; } + int len = 0; + CursorScaleFactorUnit[0] = '\x00'; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)CursorScaleFactorUnit, sizeof(CursorScaleFactorUnit), &len); + CLIParserFree(ctx); RepaintGraphWindow(); return PM3_SUCCESS; } @@ -2104,11 +2131,10 @@ static int Cmdhex2bin(const char *Cmd) { else continue; - //Uses printf instead of PrintAndLog since the latter adds linebreaks to each printout for (int i = 0 ; i < 4 ; ++i) - printf("%d", (x >> (3 - i)) & 1); + PrintAndLogEx(NORMAL, "%d" NOLF, (x >> (3 - i)) & 1); } - PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -2314,15 +2340,14 @@ static int CmdDataNDEF(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "data ndef", "Decode and print NFC Data Exchange Format (NDEF)", - "Samples:\n" - _YELLOW_("\tdata ndef -d 9101085402656e48656c6c6f5101085402656e576f726c64\n") - _YELLOW_("\tdata ndef -d 0103d020240203e02c040300fe\n") - ); + "data ndef -d 9101085402656e48656c6c6f5101085402656e576f726c64\n" + "data ndef -d 0103d020240203e02c040300fe\n" + ); void *argtable[] = { arg_param_begin, - arg_strx0("dD", "data", "", "NDEF data to decode"), - arg_lit0("vV", "verbose", "verbose mode"), + arg_strx0("d", "data", "", "NDEF data to decode"), + arg_lit0("v", "verbose", "verbose mode"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2344,45 +2369,206 @@ static int CmdDataNDEF(const char *Cmd) { return res; } +typedef struct { + t55xx_modulation modulation; + int bitrate; + int carrier; + uint8_t fc1; + uint8_t fc2; +} lf_modulation_t; + +static int print_modulation(lf_modulation_t b) { + PrintAndLogEx(INFO, " Modulation.... " _GREEN_("%s"), GetSelectedModulationStr(b.modulation)); + PrintAndLogEx(INFO, " Bit clock..... " _GREEN_("RF/%d"), b.bitrate); + switch (b.modulation) { + case DEMOD_PSK1: + case DEMOD_PSK2: + case DEMOD_PSK3: + PrintAndLogEx(SUCCESS, " Carrier rate.. %d", b.carrier); + break; + case DEMOD_FSK: + case DEMOD_FSK1: + case DEMOD_FSK1a: + case DEMOD_FSK2: + case DEMOD_FSK2a: + PrintAndLogEx(SUCCESS, " Field Clocks.. FC/%u, FC/%u", b.fc1, b.fc2); + break; + case DEMOD_NRZ: + case DEMOD_ASK: + case DEMOD_BI: + case DEMOD_BIa: + default: + break; + } + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + +static int try_detect_modulation(void) { + + lf_modulation_t tests[6]; + int clk = 0, firstClockEdge = 0; + uint8_t hits = 0, ans = 0; + uint8_t fc1 = 0, fc2 = 0; + bool st = false; + + 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)) { + tests[hits].modulation = DEMOD_FSK; + if (fc1 == 8 && fc2 == 5) { + tests[hits].modulation = DEMOD_FSK1a; + } else if (fc1 == 10 && fc2 == 8) { + tests[hits].modulation = DEMOD_FSK2; + } + + tests[hits].bitrate = clk; + tests[hits].fc1 = fc1; + tests[hits].fc2 = fc2; + ++hits; + } + + } else { + clk = GetAskClock("", false); + if (clk > 0) { + // 0 = auto clock + // 0 = no invert + // 1 = maxError 1 + // 0 = max len + // false = no amplify + // false = no verbose + // false = no emSearch + // 1 = Ask/Man + // st = true + if ((ASKDemod_ext(0, 0, 1, 0, false, false, false, 1, &st) == PM3_SUCCESS)) { + tests[hits].modulation = DEMOD_ASK; + tests[hits].bitrate = clk; + ++hits; + } + // "0 0 1 " == clock auto, invert true, maxError 1. + // false = no verbose + // false = no emSearch + // 1 = Ask/Man + // st = true + + // ASK / biphase + if ((ASKbiphaseDemod(0, 0, 0, 2, false) == PM3_SUCCESS)) { + tests[hits].modulation = DEMOD_BI; + tests[hits].bitrate = clk; + ++hits; + } + // ASK / Diphase + if ((ASKbiphaseDemod(0, 0, 1, 2, false) == PM3_SUCCESS)) { + tests[hits].modulation = DEMOD_BIa; + tests[hits].bitrate = clk; + ++hits; + } + } + clk = GetNrzClock("", false); + if ((NRZrawDemod(0, 0, 1, false) == PM3_SUCCESS)) { + tests[hits].modulation = DEMOD_NRZ; + tests[hits].bitrate = clk; + ++hits; + } + + clk = GetPskClock("", false); + if (clk > 0) { + // allow undo + save_restoreGB(GRAPH_SAVE); + // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) + CmdLtrim("160"); + if ((PSKDemod(0, 0, 6, false) == PM3_SUCCESS)) { + tests[hits].modulation = DEMOD_PSK1; + tests[hits].bitrate = clk; + ++hits; + + // get psk carrier + tests[hits].carrier = GetPskCarrier(false); + } + //undo trim samples + save_restoreGB(GRAPH_RESTORE); + } + } + + if (hits) { + PrintAndLogEx(SUCCESS, "Found [%d] possible matches for modulation.", hits); + for (int i = 0; i < hits; ++i) { + PrintAndLogEx(INFO, "--[%d]---------------", i + 1); + print_modulation(tests[i]); + } + return PM3_SUCCESS; + } else { + PrintAndLogEx(INFO, "Signal doesn't match"); + return PM3_ESOFT; + } +} + +static int CmdDataModulationSearch(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "data modulation", + "search LF signal after clock and modulation\n", + "data modulation" + ); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + return try_detect_modulation(); +} + static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"askedgedetect", CmdAskEdgeDetect, AlwaysAvailable, "[threshold] Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave (use 20-45, def:25)"}, - {"autocorr", CmdAutoCorr, AlwaysAvailable, "[window length] [g] -- Autocorrelation over window - g to save back to GraphBuffer (overwrite)"}, - {"biphaserawdecode", CmdBiphaseDecodeRaw, AlwaysAvailable, "[offset] [invert<0|1>] [maxErr] -- Biphase decode bin stream in DemodBuffer (offset = 0|1 bits to shift the decode start)"}, - {"bin2hex", Cmdbin2hex, AlwaysAvailable, " -- Converts binary to hexadecimal"}, - {"bitsamples", CmdBitsamples, IfPm3Present, "Get raw samples as bitstring"}, - {"buffclear", CmdBuffClear, AlwaysAvailable, "Clears bigbuff on deviceside and graph window"}, - {"convertbitstream", CmdConvertBitStream, AlwaysAvailable, "Convert GraphBuffer's 0/1 values to 127 / -127"}, - {"dec", CmdDec, AlwaysAvailable, "Decimate samples"}, - {"detectclock", CmdDetectClockRate, AlwaysAvailable, "[] Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer"}, - {"fsktonrz", CmdFSKToNRZ, AlwaysAvailable, "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)"}, - {"getbitstream", CmdGetBitStream, AlwaysAvailable, "Convert GraphBuffer's >=1 values to 1 and <1 to 0"}, - {"grid", CmdGrid, AlwaysAvailable, " -- overlay grid on graph window, use zero value to turn off either"}, - {"hexsamples", CmdHexsamples, IfPm3Present, " [] -- Dump big buffer as hex bytes"}, - {"hex2bin", Cmdhex2bin, AlwaysAvailable, " -- Converts hexadecimal to binary"}, - {"hide", CmdHide, AlwaysAvailable, "Hide graph window"}, - {"hpf", CmdHpf, AlwaysAvailable, "Remove DC offset from trace"}, - {"load", CmdLoad, AlwaysAvailable, " -- Load trace (to graph window"}, - {"ltrim", CmdLtrim, AlwaysAvailable, " -- Trim samples from left of trace"}, - {"rtrim", CmdRtrim, AlwaysAvailable, " -- Trim samples from right of trace"}, - {"mtrim", CmdMtrim, AlwaysAvailable, " -- Trim out samples from the specified start to the specified stop"}, - {"manrawdecode", Cmdmandecoderaw, AlwaysAvailable, "[invert] [maxErr] -- Manchester decode binary stream in DemodBuffer"}, - {"norm", CmdNorm, AlwaysAvailable, "Normalize max/min to +/-128"}, - {"plot", CmdPlot, AlwaysAvailable, "Show graph window (hit 'h' in window for keystroke help)"}, - {"printdemodbuffer", CmdPrintDemodBuff, AlwaysAvailable, "[x] [o] [l] -- print the data in the DemodBuffer - 'x' for hex output"}, - {"rawdemod", CmdRawDemod, AlwaysAvailable, "[modulation] ... -see help (h option) -- Demodulate the data in the GraphBuffer and output binary"}, - {"samples", CmdSamples, IfPm3Present, "[512 - 40000] -- Get raw samples for graph window (GraphBuffer)"}, - {"save", CmdSave, AlwaysAvailable, "Save trace (from graph window)"}, - {"setgraphmarkers", CmdSetGraphMarkers, AlwaysAvailable, "[orange_marker] [blue_marker] (in graph window)"}, - {"scale", CmdScale, AlwaysAvailable, " -- Set cursor display scale in carrier frequency expressed in kHz"}, - {"setdebugmode", CmdSetDebugMode, AlwaysAvailable, "<0|1|2> -- Set Debugging Level on client side"}, - {"shiftgraphzero", CmdGraphShiftZero, AlwaysAvailable, " -- Shift 0 for Graphed wave + or - shift value"}, - {"dirthreshold", CmdDirectionalThreshold, AlwaysAvailable, " -- Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev."}, - {"tune", CmdTuneSamples, IfPm3Present, "Get hw tune samples for graph window"}, - {"undec", CmdUndec, AlwaysAvailable, "Un-decimate samples by 2"}, - {"zerocrossings", CmdZerocrossings, AlwaysAvailable, "Count time between zero-crossings"}, - {"iir", CmdDataIIR, AlwaysAvailable, "apply IIR buttersworth filter on plotdata"}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + + {"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("Modulation") "-------------------------"}, + {"biphaserawdecode", CmdBiphaseDecodeRaw, AlwaysAvailable, "Biphase decode bin stream in DemodBuffer"}, + {"detectclock", CmdDetectClockRate, AlwaysAvailable, "Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer"}, + {"fsktonrz", CmdFSKToNRZ, AlwaysAvailable, "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)"}, + {"manrawdecode", Cmdmandecoderaw, AlwaysAvailable, "Manchester decode binary stream in DemodBuffer"}, + {"modulation", CmdDataModulationSearch, AlwaysAvailable, "Identify LF signal for clock and modulation"}, + {"rawdemod", CmdRawDemod, AlwaysAvailable, "Demodulate the data in the GraphBuffer and output binary"}, + + {"-----------", CmdHelp, AlwaysAvailable, "------------------------- " _CYAN_("Graph") "-------------------------"}, + {"askedgedetect", CmdAskEdgeDetect, AlwaysAvailable, "[threshold] Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave (use 20-45, def:25)"}, + {"autocorr", CmdAutoCorr, AlwaysAvailable, "Autocorrelation over window"}, + {"dirthreshold", CmdDirectionalThreshold, AlwaysAvailable, " -- Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev."}, + {"decimate", CmdDecimate, AlwaysAvailable, "Decimate samples"}, + {"undecimate", CmdUndecimate, AlwaysAvailable, "Un-decimate samples"}, + {"hide", CmdHide, AlwaysAvailable, "Hide graph window"}, + {"hpf", CmdHpf, AlwaysAvailable, "Remove DC offset from trace"}, + {"iir", CmdDataIIR, AlwaysAvailable, "apply IIR buttersworth filter on plotdata"}, + {"grid", CmdGrid, AlwaysAvailable, " -- overlay grid on graph window, use zero value to turn off either"}, + {"ltrim", CmdLtrim, AlwaysAvailable, " -- Trim samples from left of trace"}, + {"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 (hit 'h' in window for keystroke help)"}, + {"rtrim", CmdRtrim, AlwaysAvailable, " -- Trim samples from right of trace"}, + {"setgraphmarkers", CmdSetGraphMarkers, AlwaysAvailable, "[orange_marker] [blue_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\n"}, + {"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") "-------------------------"}, + {"bin2hex", Cmdbin2hex, AlwaysAvailable, "Converts binary to hexadecimal"}, + {"bitsamples", CmdBitsamples, IfPm3Present, "Get raw samples as bitstring"}, + {"clear", CmdBuffClear, AlwaysAvailable, "Clears bigbuf on deviceside and graph window"}, + {"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"}, {"ndef", CmdDataNDEF, AlwaysAvailable, "Decode NDEF records"}, + {"print", CmdPrintDemodBuff, AlwaysAvailable, "print the data in the DemodBuffer"}, + {"samples", CmdSamples, IfPm3Present, "[512 - 40000] -- Get raw samples for graph window (GraphBuffer)"}, + {"save", CmdSave, AlwaysAvailable, "Save signal trace data (from graph window)"}, + {"setdebugmode", CmdSetDebugMode, AlwaysAvailable, "<0|1|2> -- Set Debugging Level on client side"}, + {"tune", CmdTuneSamples, IfPm3Present, "Measure tuning of device antenna. Results shown in graph window"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmddata.h b/client/src/cmddata.h index acda950a5..785e88b49 100644 --- a/client/src/cmddata.h +++ b/client/src/cmddata.h @@ -59,12 +59,13 @@ int CmdNorm(const char *Cmd); int CmdPlot(const char *Cmd); // used by cmd lf cotag int CmdSave(const char *Cmd); // used by cmd auto int CmdTuneSamples(const char *Cmd); // used by cmd lf hw -int ASKbiphaseDemod(const char *Cmd, bool verbose); // used by cmd lf em4x, lf fdx, lf guard, lf jablotron, lf nedap, lf t55xx -int ASKDemod(const char *Cmd, bool verbose, bool emSearch, uint8_t askType); // used by cmd lf em4x, lf t55xx, lf viking -int ASKDemod_ext(const char *Cmd, bool verbose, bool emSearch, uint8_t askType, bool *stCheck); // used by cmd lf, lf em4x, lf noralsy, le presco, lf securekey, lf t55xx, lf visa2k -int FSKrawDemod(const char *Cmd, bool verbose); // used by cmd lf, lf em4x, lf t55xx -int PSKDemod(const char *Cmd, bool verbose); // used by cmd lf em4x, lf indala, lf keri, lf nexwatch, lf t55xx -int NRZrawDemod(const char *Cmd, bool verbose); // used by cmd lf pac, lf t55xx + +int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose); // used by cmd lf em4x, lf fdxb, lf guard, lf jablotron, lf nedap, lf t55xx +int ASKDemod(int clk, int invert, int maxErr, size_t maxLen, bool amplify, bool verbose, bool emSearch, uint8_t askType); // used by cmd lf em4x, lf t55xx, lf viking +int ASKDemod_ext(int clk, int invert, int maxErr, size_t maxLen, bool amplify, bool verbose, bool emSearch, uint8_t askType, bool *stCheck); // used by cmd lf, lf em4x, lf noralsy, le presco, lf securekey, lf t55xx, lf visa2k +int FSKrawDemod(uint8_t rfLen, uint8_t invert, uint8_t fchigh, uint8_t fclow, bool verbose); // used by cmd lf, lf em4x, lf t55xx +int PSKDemod(int clk, int invert, int maxErr, bool verbose); // used by cmd lf em4x, lf indala, lf keri, lf nexwatch, lf t55xx +int NRZrawDemod(int clk, int invert, int maxErr, bool verbose); // used by cmd lf pac, lf t55xx void printDemodBuff(void); @@ -79,14 +80,13 @@ int getSamplesEx(uint32_t start, uint32_t end, 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 AskEdgeDetect(const int *in, int *out, int len, int threshold); -int demodIdteck(void); #define MAX_DEMOD_BUF_LEN (1024*128) extern uint8_t DemodBuffer[MAX_DEMOD_BUF_LEN]; extern size_t DemodBufferLen; extern int g_DemodClock; -extern size_t g_DemodStartIdx; +extern int32_t g_DemodStartIdx; #ifdef __cplusplus } diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index e90e6025c..7f663c45b 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -8,16 +8,13 @@ // Proxmark3 RDV40 Flash memory commands //----------------------------------------------------------------------------- #include "cmdflashmem.h" - #include - -#include "cmdparser.h" // command_t - +#include "cmdparser.h" // command_t +#include "cliparser.h" #include "pmflash.h" -#include "fileutils.h" //saveFile -#include "comms.h" //getfromdevice +#include "fileutils.h" // saveFile +#include "comms.h" // getfromdevice #include "cmdflashmemspiffs.h" // spiffs commands - #include "rsa.h" #include "sha1.h" @@ -29,90 +26,34 @@ static int CmdHelp(const char *Cmd); -static int usage_flashmem_spibaud(void) { - PrintAndLogEx(NORMAL, "Usage: mem spibaud [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " SPI baudrate in MHz [24|48]"); - PrintAndLogEx(NORMAL, " "); - PrintAndLogEx(NORMAL, " If >= 24MHz, FASTREADS instead of READS instruction will be used."); - PrintAndLogEx(NORMAL, " Reading Flash ID will virtually always fail under 48MHz setting"); - PrintAndLogEx(NORMAL, " Unless you know what you are doing, please stay at 24MHz"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " mem spibaud 48"); - return PM3_SUCCESS; -} - -static int usage_flashmem_load(void) { - PrintAndLogEx(NORMAL, "Loads binary file into flash memory on device"); - PrintAndLogEx(NORMAL, "Usage: mem load [o ] f [m|t|i]"); - PrintAndLogEx(NORMAL, "Warning: mem area to be written must have been wiped first"); - PrintAndLogEx(NORMAL, "(this is already taken care when loading dictionaries)"); - PrintAndLogEx(NORMAL, " o : offset in memory"); - PrintAndLogEx(NORMAL, " f : file name"); - PrintAndLogEx(NORMAL, " m : upload 6 bytes keys (mifare key dictionary)"); - PrintAndLogEx(NORMAL, " i : upload 8 bytes keys (iClass key dictionary)"); - PrintAndLogEx(NORMAL, " t : upload 4 bytes keys (pwd dictionary)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " mem load f myfile"); // upload file myfile at default offset 0 - PrintAndLogEx(NORMAL, " mem load f myfile o 1024"); // upload file myfile at offset 1024 - PrintAndLogEx(NORMAL, " mem load f mfc_default_keys m"); - PrintAndLogEx(NORMAL, " mem load f t55xx_default_pwds t"); - PrintAndLogEx(NORMAL, " mem load f iclass_default_keys i"); - return PM3_SUCCESS; -} -static int usage_flashmem_dump(void) { - PrintAndLogEx(NORMAL, "Dumps flash memory on device into a file or in console"); - PrintAndLogEx(NORMAL, " Usage: mem dump [o ] [l ] [f ] [p]"); - PrintAndLogEx(NORMAL, " o : offset in memory"); - PrintAndLogEx(NORMAL, " l : length"); - PrintAndLogEx(NORMAL, " f : file name"); - PrintAndLogEx(NORMAL, " p : print dump in console"); - PrintAndLogEx(NORMAL, " You must specify at lease option f or option p, both if you wish"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " mem dump f myfile"); // download whole flashmem to file myfile - PrintAndLogEx(NORMAL, " mem dump p o 262015 l 128"); // display 128 bytes from offset 262015 (RSA sig) - PrintAndLogEx(NORMAL, " mem dump p f myfile o 241664 l 58"); // download and display 58 bytes from offset 241664 to file myfile - return PM3_SUCCESS; -} -static int usage_flashmem_wipe(void) { - - PrintAndLogEx(WARNING, "[OBS] use with caution."); - PrintAndLogEx(NORMAL, "Wipe flash memory on device, which fills memory with 0xFF\n"); - - PrintAndLogEx(NORMAL, " Usage: mem wipe p "); - PrintAndLogEx(NORMAL, " p : 0,1,2 page memory"); -// PrintAndLogEx(NORMAL, " i : inital total wipe"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " mem wipe p 0"); // wipes first page. - return PM3_SUCCESS; -} -static int usage_flashmem_info(void) { - PrintAndLogEx(NORMAL, "Collect signature and verify it from flash memory\n"); - PrintAndLogEx(NORMAL, " Usage: mem info"); -// PrintAndLogEx(NORMAL, " s : create a signature"); -// PrintAndLogEx(NORMAL, " w : write signature to flash memory"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " mem info"); -// PrintAndLogEx(NORMAL, " mem info s"); - return PM3_SUCCESS; -} - static int CmdFlashmemSpiBaudrate(const char *Cmd) { - char ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || ctmp == 'h') { - return usage_flashmem_spibaud(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem baudrate", + "Set the baudrate for the SPI flash memory communications.\n" + "Reading Flash ID will virtually always fail under 48MHz setting.\n" + "Unless you know what you are doing, please stay at 24MHz.\n" + "If >= 24MHz, FASTREADS instead of READS instruction will be used.", + "mem baudrate --mhz 48" + ); + + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "mhz", "<24|48>", "SPI baudrate in MHz"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int br = arg_get_int_def(ctx, 1, -1); + CLIParserFree(ctx); + + if (br == -1) { + PrintAndLogEx(ERR, "failed to get baudrate"); + return PM3_EINVARG; } - uint32_t baudrate = param_get32ex(Cmd, 0, 0, 10); - baudrate = baudrate * 1000000; + uint32_t baudrate = br * 1000000; if (baudrate != FLASH_BAUD && baudrate != FLASH_MINBAUD) { - usage_flashmem_spibaud(); + PrintAndLogEx(ERR, "wrong baudrate. Only 24 or 48 is allowed"); return PM3_EINVARG; } SendCommandNG(CMD_FLASHMEM_SET_SPIBAUDRATE, (uint8_t *)&baudrate, sizeof(uint32_t)); @@ -121,52 +62,50 @@ static int CmdFlashmemSpiBaudrate(const char *Cmd) { static int CmdFlashMemLoad(const char *Cmd) { - uint32_t start_index = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem load", + "Loads binary file into flash memory on device\n" + "Warning: mem area to be written must have been wiped first\n" + "( this is already taken care when loading dictionaries )", + "mem load -f myfile -> upload file myfile values at default offset 0\n" + "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n" + "mem load -f mfc_default_keys -m -> upload MFC keys\n" + "mem load -f t55xx_default_pwds -t -> upload T55XX passwords\n" + "mem load -f iclass_default_keys -i -> upload iCLASS keys\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("o", "offset", "", "offset in memory"), + arg_lit0("m", "mifare,mfc", "upload 6 bytes keys (mifare key dictionary)"), + arg_lit0("i", "iclass", "upload 8 bytes keys (iClass key dictionary)"), + arg_lit0("t", "t55xx", "upload 4 bytes keys (password dictionary)"), + arg_strx0("f", "file", "", "file name"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int offset = arg_get_int_def(ctx, 1, 0); + bool is_mfc = arg_get_lit(ctx, 2); + bool is_iclass = arg_get_lit(ctx, 3); + bool is_t55xx = arg_get_lit(ctx, 4); + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - bool errors = false; - uint8_t cmdp = 0; + CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); + Dictionary_t d = DICTIONARY_NONE; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_flashmem_load(); - case 'f': - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - errors = true; - break; - } - cmdp += 2; - break; - case 'o': - start_index = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 'm': - d = DICTIONARY_MIFARE; - cmdp++; - break; - case 't': - d = DICTIONARY_T55XX; - cmdp++; - break; - case 'i': - d = DICTIONARY_ICLASS; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + if (is_mfc) { + d = DICTIONARY_MIFARE; + PrintAndLogEx(INFO, "treating file as MIFARE Classic keys"); + } else if (is_iclass) { + d = DICTIONARY_ICLASS; + PrintAndLogEx(INFO, "treating file as iCLASS keys"); + } else if (is_t55xx) { + d = DICTIONARY_T55XX; + PrintAndLogEx(INFO, "treating file as T55xx passwords"); } - //Validations - if (errors || cmdp == 0) { - usage_flashmem_load(); - return PM3_EINVARG; - } size_t datalen = 0; uint32_t keycount = 0; int res = 0; @@ -174,7 +113,7 @@ static int CmdFlashMemLoad(const char *Cmd) { switch (d) { case DICTIONARY_MIFARE: - start_index = DEFAULT_MF_KEYS_OFFSET; + offset = DEFAULT_MF_KEYS_OFFSET; res = loadFileDICTIONARY(filename, data + 2, &datalen, 6, &keycount); if (res || !keycount) { free(data); @@ -189,7 +128,7 @@ static int CmdFlashMemLoad(const char *Cmd) { datalen += 2; break; case DICTIONARY_T55XX: - start_index = DEFAULT_T55XX_KEYS_OFFSET; + offset = DEFAULT_T55XX_KEYS_OFFSET; res = loadFileDICTIONARY(filename, data + 2, &datalen, 4, &keycount); if (res || !keycount) { free(data); @@ -204,7 +143,7 @@ static int CmdFlashMemLoad(const char *Cmd) { datalen += 2; break; case DICTIONARY_ICLASS: - start_index = DEFAULT_ICLASS_KEYS_OFFSET; + offset = DEFAULT_ICLASS_KEYS_OFFSET; res = loadFileDICTIONARY(filename, data + 2, &datalen, 8, &keycount); if (res || !keycount) { free(data); @@ -253,13 +192,13 @@ static int CmdFlashMemLoad(const char *Cmd) { clearCommandBuffer(); - SendCommandOLD(CMD_FLASHMEM_WRITE, start_index + bytes_sent, bytes_in_packet, 0, data + bytes_sent, bytes_in_packet); + SendCommandOLD(CMD_FLASHMEM_WRITE, offset + bytes_sent, bytes_in_packet, 0, data + bytes_sent, bytes_in_packet); bytes_remaining -= bytes_in_packet; bytes_sent += bytes_in_packet; PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); conn.block_after_ACK = false; free(data); @@ -276,54 +215,37 @@ static int CmdFlashMemLoad(const char *Cmd) { conn.block_after_ACK = false; free(data); - PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, start_index); + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")" bytes to offset "_GREEN_("%u"), datalen, offset); return PM3_SUCCESS; } + static int CmdFlashMemDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem dump", + "Dumps flash memory on device into a file or view in console", + "mem dump -f myfile -> download all flashmem to file\n" + "mem dump --view -o 262015 --len 128 -> display 128 bytes from offset 262015 (RSA sig)\n" + "mem dump --view -f myfile -o 241664 --len 58 -> display 58 bytes from offset 241664 and save to file" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("o", "offset", "", "offset in memory"), + arg_int0("l", "len", "", "length"), + arg_lit0("v", "view", "view dump"), + arg_strx0("f", "file", "", "file name"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int offset = arg_get_int_def(ctx, 1, 0); + int len = arg_get_int_def(ctx, 2, FLASH_MEM_MAX_SIZE); + bool view = arg_get_lit(ctx, 3); + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - uint8_t cmdp = 0; - bool errors = false; - bool print = false; - uint32_t start_index = 0, len = FLASH_MEM_MAX_SIZE; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_flashmem_dump(); - case 'l': - len = param_get32ex(Cmd, cmdp + 1, FLASH_MEM_MAX_SIZE, 10); - cmdp += 2; - break; - case 'o': - start_index = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 'p': - print = true; - cmdp += 1; - break; - case 'f': - //File handling - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - errors = true; - break; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - - //Validations - if (errors || cmdp == 0) { - usage_flashmem_dump(); - return PM3_EINVARG; - } + CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); uint8_t *dump = calloc(len, sizeof(uint8_t)); if (!dump) { @@ -331,14 +253,15 @@ static int CmdFlashMemDump(const char *Cmd) { return PM3_EMALLOC; } - PrintAndLogEx(INFO, "downloading "_YELLOW_("%u")" bytes from flashmem", len); - if (!GetFromDevice(FLASH_MEM, dump, len, start_index, NULL, 0, NULL, -1, true)) { - PrintAndLogEx(FAILED, "ERROR; downloading from flashmemory"); + PrintAndLogEx(INFO, "downloading "_YELLOW_("%u")" bytes from flash memory", len); + if (!GetFromDevice(FLASH_MEM, dump, len, offset, NULL, 0, NULL, -1, true)) { + PrintAndLogEx(FAILED, "ERROR; downloading from flash memory"); free(dump); return PM3_EFLASH; } - if (print) { + if (view) { + PrintAndLogEx(INFO, "---- " _CYAN_("data") " ---------------"); print_hex_break(dump, len, 32); } @@ -350,39 +273,32 @@ static int CmdFlashMemDump(const char *Cmd) { free(dump); return PM3_SUCCESS; } + static int CmdFlashMemWipe(const char *Cmd) { - uint8_t cmdp = 0; - bool errors = false; - bool initalwipe = false; - uint8_t page = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_flashmem_wipe(); - case 'p': - page = param_get8ex(Cmd, cmdp + 1, 0, 10); - if (page > 2) { - PrintAndLogEx(WARNING, "page must be 0, 1 or 2"); - errors = true; - break; - } - cmdp += 2; - break; - case 'i': - initalwipe = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem wipe", + "Wipe flash memory on device, which fills it with 0xFF\n" + _WHITE_("[ ") _RED_("!!! OBS") " ] use with caution", + "mem wipe -p 0 -> wipes first page" +// "mem wipe -i -> inital total wipe" + ); - //Validations - if (errors || cmdp == 0) { - usage_flashmem_wipe(); + void *argtable[] = { + arg_param_begin, + arg_int0("p", NULL, "", "0,1,2 page memory"), +// arg_lit0("i", NULL, "inital total wipe"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool initalwipe = false; + int page = arg_get_int_def(ctx, 1, -1); +// initalwipe = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (page < 0 || page > 2) { + PrintAndLogEx(WARNING, "page must be 0, 1 or 2"); return PM3_EINVARG; } @@ -393,59 +309,51 @@ static int CmdFlashMemWipe(const char *Cmd) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } + + const char *msg = "Flash WIPE "; uint8_t isok = resp.oldarg[0] & 0xFF; if (isok) - PrintAndLogEx(SUCCESS, "Flash WIPE ok"); + PrintAndLogEx(SUCCESS, "%s ( " _GREEN_("ok")" )", msg); else { - PrintAndLogEx(FAILED, "Flash WIPE failed"); + PrintAndLogEx(FAILED, "%s ( " _RED_("failed") " )", msg); return PM3_EFLASH; } return PM3_SUCCESS; } + static int CmdFlashMemInfo(const char *Cmd) { - uint8_t sha_hash[20] = {0}; - mbedtls_rsa_context rsa; + CLIParserContext *ctx; + CLIParserInit(&ctx, "mem info", + "Collect signature and verify it from flash memory", + "mem info" +// "mem info -s" + ); - uint8_t cmdp = 0; - bool errors = false, shall_write = false, shall_sign = false; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_flashmem_info(); - case 's': { - shall_sign = true; - cmdp++; - break; - } - case 'w': - shall_write = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } + void *argtable[] = { + arg_param_begin, +// arg_lit0("s", NULL, "create a signature"), +// arg_lit0("w", NULL, "write signature to flash memory"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - //Validations - if (errors) { - usage_flashmem_info(); - return PM3_EINVARG; - } + bool shall_sign = false, shall_write = false; +// shall_sign = arg_get_lit(ctx, 1); +// shall_write = arg_get_lit(ctx, 2); + CLIParserFree(ctx); clearCommandBuffer(); SendCommandNG(CMD_FLASHMEM_INFO, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } uint8_t isok = resp.oldarg[0] & 0xFF; - if (!isok) { + if (isok == false) { PrintAndLogEx(FAILED, "failed"); return PM3_EFLASH; } @@ -455,15 +363,20 @@ static int CmdFlashMemInfo(const char *Cmd) { memcpy(&mem, (rdv40_validation_t *)resp.data.asBytes, sizeof(rdv40_validation_t)); // Flash ID hash (sha1) + uint8_t sha_hash[20] = {0}; mbedtls_sha1(mem.flashid, sizeof(mem.flashid), sha_hash); // print header - PrintAndLogEx(INFO, "\n--- Flash memory Information ---------"); - PrintAndLogEx(INFO, "-------------------------------------------------------------"); - PrintAndLogEx(INFO, "ID | %s", sprint_hex(mem.flashid, sizeof(mem.flashid))); - PrintAndLogEx(INFO, "SHA1 | %s", sprint_hex(sha_hash, sizeof(sha_hash))); - PrintAndLogEx(INFO, "RSA SIGNATURE |"); - print_hex_break(mem.signature, sizeof(mem.signature), 32); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information") " ---------"); +// PrintAndLogEx(INFO, "-----------------------------------------------------------------"); + PrintAndLogEx(INFO, "ID................... %s", sprint_hex_inrow(mem.flashid, sizeof(mem.flashid))); + PrintAndLogEx(INFO, "SHA1................. %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash))); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA signature") " ---------------"); + for (int i = 0; i < (sizeof(mem.signature) / 32); i++) { + PrintAndLogEx(INFO, " %s", sprint_hex_inrow(mem.signature + (i * 32), 32)); + } //------------------------------------------------------------------------------- // RRG Public RSA Key @@ -473,7 +386,10 @@ static int CmdFlashMemInfo(const char *Cmd) { #define RSA_E "010001" // public key modulus N -#define RSA_N "E28D809BF323171D11D1ACA4C32A5B7E0A8974FD171E75AD120D60E9B76968FF4B0A6364AE50583F9555B8EE1A725F279E949246DF0EFCE4C02B9F3ACDCC623F9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5" +#define RSA_N "E28D809BF323171D11D1ACA4C32A5B7E0A8974FD171E75AD120D60E9B76968FF" \ + "4B0A6364AE50583F9555B8EE1A725F279E949246DF0EFCE4C02B9F3ACDCC623F" \ + "9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \ + "DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5" //------------------------------------------------------------------------------- // Example RSA-1024 keypair, for test purposes (from common/polarssl/rsa.c) @@ -516,9 +432,9 @@ static int CmdFlashMemInfo(const char *Cmd) { "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ "A74206CEC169D74BF5A8C50D6F48EA08" - #define KEY_LEN 128 + mbedtls_rsa_context rsa; mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0); rsa.len = KEY_LEN; @@ -532,13 +448,31 @@ static int CmdFlashMemInfo(const char *Cmd) { mbedtls_mpi_read_string(&rsa.DQ, 16, RSA_DQ); mbedtls_mpi_read_string(&rsa.QP, 16, RSA_QP); - PrintAndLogEx(INFO, "KEY length | %d", KEY_LEN); - bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0 || mbedtls_rsa_check_privkey(&rsa) == 0); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA Public key") " --------------"); + + char str_exp[10]; + char str_pk[261]; + size_t exlen = 0, pklen = 0; + mbedtls_mpi_write_string(&rsa.E, 16, str_exp, sizeof(str_exp), &exlen); + mbedtls_mpi_write_string(&rsa.N, 16, str_pk, sizeof(str_pk), &pklen); + + PrintAndLogEx(INFO, "Len.................. %"PRIu64, rsa.len); + PrintAndLogEx(INFO, "Exponent............. %s", str_exp); + PrintAndLogEx(INFO, "Public key modulus N"); + PrintAndLogEx(INFO, " %.64s", str_pk); + PrintAndLogEx(INFO, " %.64s", str_pk + 64); + PrintAndLogEx(INFO, " %.64s", str_pk + 128); + PrintAndLogEx(INFO, " %.64s", str_pk + 192); + + PrintAndLogEx(NORMAL, ""); + const char *msgkey = "RSA key validation... "; if (is_keyok) - PrintAndLogEx(SUCCESS, "RSA key validation ok"); + PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgkey); else - PrintAndLogEx(FAILED, "RSA key validation failed"); + PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgkey); // uint8_t from_device[KEY_LEN]; @@ -554,10 +488,11 @@ static int CmdFlashMemInfo(const char *Cmd) { if (shall_sign) { int is_signed = mbedtls_rsa_pkcs1_sign(&rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA1, 20, sha_hash, sign); + const char *msgsign = "RSA signing.......... "; if (is_signed == 0) - PrintAndLogEx(SUCCESS, "RSA Signing ok"); + PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgsign); else - PrintAndLogEx(FAILED, "RSA Signing failed"); + PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgsign); if (shall_write) { // save to mem @@ -574,29 +509,33 @@ static int CmdFlashMemInfo(const char *Cmd) { } } - PrintAndLogEx(INFO, "Signed | "); - print_hex_break(sign, sizeof(sign), 32); + PrintAndLogEx(INFO, "Signed"); + for (int i = 0; i < (sizeof(sign) / 32); i++) { + PrintAndLogEx(INFO, " %s", sprint_hex_inrow(sign + (i * 32), 32)); + } } // Verify (public key) int is_verified = mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device); + const char *msgverify = "RSA verification..... "; if (is_verified == 0) - PrintAndLogEx(SUCCESS, "RSA Verification ok"); + PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgverify); else - PrintAndLogEx(FAILED, "RSA Verification failed"); + PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgverify); + PrintAndLogEx(NORMAL, ""); mbedtls_rsa_free(&rsa); return PM3_SUCCESS; } static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "High level SPI FileSystem Flash manipulation [rdv40]"}, - {"spibaud", CmdFlashmemSpiBaudrate, IfPm3Flash, "Set Flash memory Spi baudrate [rdv40]"}, - {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information [rdv40]"}, - {"load", CmdFlashMemLoad, IfPm3Flash, "Load data into flash memory [rdv40]"}, - {"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory [rdv40]"}, - {"wipe", CmdFlashMemWipe, IfPm3Flash, "Wipe data from flash memory [rdv40]"}, + {"baudrate", CmdFlashmemSpiBaudrate, IfPm3Flash, "Set Flash memory Spi baudrate"}, + {"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "High level SPI FileSystem Flash manipulation"}, + {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"}, + {"load", CmdFlashMemLoad, IfPm3Flash, "Load data into flash memory"}, + {"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"}, + {"wipe", CmdFlashMemWipe, IfPm3Flash, "Wipe data from flash memory"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdflashmemspiffs.c b/client/src/cmdflashmemspiffs.c index b994ea56c..11c4db2d7 100644 --- a/client/src/cmdflashmemspiffs.c +++ b/client/src/cmdflashmemspiffs.c @@ -19,53 +19,69 @@ static int CmdHelp(const char *Cmd); static int usage_flashmemspiffs_remove(void) { - PrintAndLogEx(NORMAL, "Remove a file from spiffs filesystem"); - PrintAndLogEx(NORMAL, " Usage: mem spiffs remove "); + PrintAndLogEx(NORMAL, "Remove a file from spiffs filesystem\n"); + PrintAndLogEx(NORMAL, "Usage: mem spiffs remove "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs remove lasttag.bin")); return PM3_SUCCESS; } static int usage_flashmemspiffs_rename(void) { - PrintAndLogEx(NORMAL, "Rename/move a file in spiffs filesystem"); - PrintAndLogEx(NORMAL, " Usage: mem spiffs rename "); + PrintAndLogEx(NORMAL, "Rename/move a file in spiffs filesystem\n"); + PrintAndLogEx(NORMAL, "Usage: mem spiffs rename "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs rename lasttag.bin oldtag.bin")); return PM3_SUCCESS; } static int usage_flashmemspiffs_copy(void) { - PrintAndLogEx(NORMAL, "Copy a file to another (destructively) in spiffs filesystem"); - PrintAndLogEx(NORMAL, " Usage: mem spiffs copy "); + PrintAndLogEx(NORMAL, "Copy a file to another (destructively) in spiffs filesystem\n"); + PrintAndLogEx(NORMAL, "Usage: mem spiffs copy "); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs copy lasttag.bin lasttag_cpy.bin")); return PM3_SUCCESS; } static int usage_flashmemspiffs_dump(void) { PrintAndLogEx(NORMAL, "Dumps flash memory on device into a file or in console"); - PrintAndLogEx(NORMAL, "Size is handled by first sending a STAT command against file existence"); - PrintAndLogEx(NORMAL, " Usage: mem spiffs dump o [f [e]] [p]"); - PrintAndLogEx(NORMAL, " o : filename in SPIFFS"); - PrintAndLogEx(NORMAL, " f : file name to save to"); - PrintAndLogEx(NORMAL, " p : print dump in console"); - PrintAndLogEx(NORMAL, " e : also save in EML format (good for tags save and dictonnary files)"); + PrintAndLogEx(NORMAL, "Size is handled by first sending a STAT command against file existence\n"); + PrintAndLogEx(NORMAL, "Usage: mem spiffs dump o [f [e]] [p]"); + PrintAndLogEx(NORMAL, " o - filename in SPIFFS"); + PrintAndLogEx(NORMAL, " f - file name to save to "); + PrintAndLogEx(NORMAL, " p - print dump in console"); + PrintAndLogEx(NORMAL, " e - also save in EML format (good for tags save and dictonnary files)"); PrintAndLogEx(NORMAL, " You must specify at lease option f or option p, both if you wish"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " mem spiffs dump o hf_colin/lasttag f lasttag e"); - PrintAndLogEx(NORMAL, " mem spiffs dump o hf_colin/lasttag p"); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs dump o lasttag.bin f lasttag e")); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs dump o lasttag.bin p")); return PM3_SUCCESS; } static int usage_flashmemspiffs_load(void) { PrintAndLogEx(NORMAL, "Uploads binary-wise file into device filesystem"); - PrintAndLogEx(NORMAL, "Usage: mem spiffs load o f "); PrintAndLogEx(NORMAL, "Warning: mem area to be written must have been wiped first"); - PrintAndLogEx(NORMAL, "(this is already taken care when loading dictionaries)"); - PrintAndLogEx(NORMAL, " o : destination filename"); - PrintAndLogEx(NORMAL, " f : local filename"); + PrintAndLogEx(NORMAL, "(this is already taken care when loading dictionaries)\n"); + PrintAndLogEx(NORMAL, "Usage: mem spiffs load o f "); + PrintAndLogEx(NORMAL, " o - destination filename"); + PrintAndLogEx(NORMAL, " f - local filename"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " mem spiffs load f myfile o myapp.conf"); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs load f myfile o myapp.conf")); return PM3_SUCCESS; } - +static int usage_flashmemspiffs_wipe(void) { + PrintAndLogEx(NORMAL, "wipes all files on the device filesystem " _RED_("* Warning *")); + PrintAndLogEx(NORMAL, "Usage: mem spiffs wipe [h]"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" mem spiffs wipe")); + return PM3_SUCCESS; +} static int CmdFlashMemSpiFFSMount(const char *Cmd) { (void)Cmd; // Cmd is not used so far clearCommandBuffer(); @@ -277,12 +293,12 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { } if ((filename[0] == '\0') && (!print)) { - PrintAndLogEx(FAILED, "No print asked and Local dump Filename missing or invalid"); + PrintAndLogEx(FAILED, "No print asked and local dump filename missing or invalid"); errors = true; } if (destfilename[0] == '\0') { - PrintAndLogEx(FAILED, "SPIFFS Filename missing or invalid"); + PrintAndLogEx(FAILED, "SPIFFS filename missing or invalid"); errors = true; } @@ -320,9 +336,16 @@ static int CmdFlashMemSpiFFSDump(const char *Cmd) { } if (filename[0] != '\0') { - saveFile(filename, "", dump, len); + saveFile(filename, ".bin", dump, len); if (eml) { - saveFileEML(filename, dump, len, 16); + uint8_t eml_len = 16; + + if (strstr(filename, "class") != NULL) + eml_len = 8; + else if (strstr(filename, "mfu") != NULL) + eml_len = 4; + + saveFileEML(filename, dump, len, eml_len); } } @@ -397,6 +420,23 @@ out: return ret_val; } +static int CmdFlashMemSpiFFSWipe(const char *Cmd) { + + char ctmp = tolower(param_getchar(Cmd, 0)); + if (ctmp == 'h') { + return usage_flashmemspiffs_wipe(); + } + + PrintAndLogEx(INFO, "Wiping all files from SPIFFS FileSystem"); + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_WIPE, NULL, 0); + WaitForResponse(CMD_SPIFFS_WIPE, &resp); + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(HINT, "Try use '" _YELLOW_("mem spiffs tree") "' to verify."); + return PM3_SUCCESS; +} + static int CmdFlashMemSpiFFSLoad(const char *Cmd) { char filename[FILE_PATH_SIZE] = {0}; @@ -457,20 +497,18 @@ static int CmdFlashMemSpiFFSLoad(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - { - "copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, - "Copy a file to another (destructively) in SPIFFS FileSystem in FlashMEM (spiffs)" - }, - {"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented Filesystem"}, - {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print filesystem info and usage statistics (spiffs)"}, - {"load", CmdFlashMemSpiFFSLoad, IfPm3Flash, "Upload file into SPIFFS Filesystem (spiffs)"}, - {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS Filesystem if not already mounted (spiffs)"}, - {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS FileSystem in FlashMEM (spiffs)"}, - {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations (require wiping pages 0 and 1)"}, - {"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash Memory FileSystem Tree (spiffs)"}, + {"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS FileSystem in FlashMEM (spiffs)"}, + {"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented Filesystem"}, + {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, + {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print filesystem info and usage statistics (spiffs)"}, + {"load", CmdFlashMemSpiFFSLoad, IfPm3Flash, "Upload file into SPIFFS Filesystem (spiffs)"}, + {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS Filesystem if not already mounted (spiffs)"}, + {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS FileSystem in FlashMEM (spiffs)"}, + {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS FileSystem in FlashMEM (spiffs)"}, + {"test", CmdFlashMemSpiFFSTest, IfPm3Flash, "Test SPIFFS Operations (require wiping pages 0 and 1)"}, + {"tree", CmdFlashMemSpiFFSTree, IfPm3Flash, "Print the Flash Memory FileSystem Tree (spiffs)"}, {"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS Filesystem if not already mounted (spiffs)"}, + {"wipe", CmdFlashMemSpiFFSWipe, IfPm3Flash, "Wipe all files from SPIFFS FileSystem." _RED_("* dangerous *") }, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 4fc138a98..dcd85c5d5 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -35,6 +35,8 @@ #include "cmdhfthinfilm.h" // Thinfilm #include "cmdhflto.h" // LTO-CM #include "cmdhfcryptorf.h" // CryptoRF +#include "cmdhfst.h" // ST rothult +#include "cmdhfwaveshare.h" // Waveshare #include "cmdtrace.h" // trace list #include "ui.h" #include "proxgui.h" @@ -84,8 +86,6 @@ static int usage_hf_tune(void) { return PM3_SUCCESS; } -#define PROMPT_CLEARLINE PrintAndLogEx(INPLACE, " ") - int CmdHFSearch(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); @@ -123,12 +123,21 @@ int CmdHFSearch(const char *Cmd) { PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for ISO15693 tag..."); if (IfPm3Iso15693()) { - if (readHF15Uid(false)) { + if (readHF15Uid(false, false)) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO15693 tag") " found\n"); res = PM3_SUCCESS; } } + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, " Searching for iCLASS / PicoPass tag..."); + if (IfPm3Iclass()) { + if (read_iclass_csn(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()) { @@ -147,16 +156,27 @@ int CmdHFSearch(const char *Cmd) { } } + // 14b is the longest test (put last) PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for FeliCa tag..."); - if (IfPm3Felica()) { - if (readFelicaUid(false) == PM3_SUCCESS) { - PrintAndLogEx(NORMAL, "\nValid " _GREEN_("ISO18092 / FeliCa tag") " found\n"); + PrintAndLogEx(INPLACE, " Searching for ISO14443-B tag..."); + if (IfPm3Iso14443b()) { + if (readHF14B(false) == 1) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-B tag") " found\n"); res = PM3_SUCCESS; } } + + /* + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, " Searching for FeliCa tag..."); + if (IfPm3Felica()) { + if (readFelicaUid(false) == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, "\nValid " _GREEN_("ISO18092 / FeliCa tag") " found\n"); + res = PM3_SUCCESS; + } + } + */ /* - // 14b and iclass is the longest test (put last) PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for CryptoRF tag..."); if (IfPm3Iso14443b()) { @@ -167,32 +187,13 @@ int CmdHFSearch(const char *Cmd) { } */ - // 14b and iclass is the longest test (put last) - PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for ISO14443-B tag..."); - if (IfPm3Iso14443b()) { - if (readHF14B(false) == 1) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-B tag") " found\n"); - res = PM3_SUCCESS; - } - } - - PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for iClass / PicoPass tag..."); - if (IfPm3Iclass()) { - if (readIclass(false, false) == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("iClass tag / PicoPass tag") " found\n"); - res = PM3_SUCCESS; - } - } - PROMPT_CLEARLINE; if (res != PM3_SUCCESS) { - PrintAndLogEx(INPLACE, _RED_("No known/supported 13.56 MHz tags found")); + PrintAndLogEx(WARNING, _RED_("No known/supported 13.56 MHz tags found")); res = PM3_ESOFT; } - printf("\n"); + return res; } @@ -285,16 +286,16 @@ int CmdHFSniff(const char *Cmd) { uint16_t len; } PACKED; struct r *retval = (struct r *)resp.data.asBytes; - + PrintAndLogEx(INFO, "HF sniff (%u samples)", retval->len); PrintAndLogEx(HINT, "Use `" _YELLOW_("data hpf") "` to remove offset"); PrintAndLogEx(HINT, "Use `" _YELLOW_("data plot") "` to view"); PrintAndLogEx(HINT, "Use `" _YELLOW_("data save") "` to save"); - // download bigbuf_malloc:d. + // download bigbuf_malloc:d. // it reserve memory from the higher end. - // At the moment, sniff takes all free memory in bigbuff. If this changes, + // At the moment, sniff takes all free memory in bigbuff. If this changes, // we can't start from beginning idx 0 but from that hi-to-start-of-allocated. uint32_t start = pm3_capabilities.bigbuf_size - retval->len; int res = getSamplesEx(start, start, false); @@ -364,8 +365,10 @@ static command_t CommandTable[] = { {"mfp", CmdHFMFP, AlwaysAvailable, "{ MIFARE Plus RFIDs... }"}, {"mfu", CmdHFMFUltra, AlwaysAvailable, "{ MIFARE Ultralight RFIDs... }"}, {"mfdes", CmdHFMFDes, AlwaysAvailable, "{ MIFARE Desfire RFIDs... }"}, + {"st", CmdHF_ST, AlwaysAvailable, "{ ST Rothult RFIDs... }"}, {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, + {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"}, {"plot", CmdHFPlot, IfPm3Hfplot, "Plot signal"}, {"tune", CmdHFTune, IfPm3Present, "Continuously measure HF antenna tuning"}, diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 6ce4d077e..2b65be89c 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -31,7 +31,7 @@ bool APDUInFramingEnable = true; static int CmdHelp(const char *Cmd); -static int waitCmd(uint8_t iSelect); +static int waitCmd(uint8_t iSelect, uint32_t timeout); static const manufactureName manufactureMapping[] = { // ID, "Vendor Country" @@ -168,6 +168,45 @@ const char *getTagInfo(uint8_t uid) { static uint16_t frameLength = 0; uint16_t atsFSC[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; +static int usage_hf_14a_config(void) { + PrintAndLogEx(NORMAL, "Usage: hf 14a config [a 0|1|2] [b 0|1|2] [2 0|1|2] [3 0|1|2]"); + PrintAndLogEx(NORMAL, "\nOptions:"); + PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " a 0|1|2 ATQA<>anticollision: 0=follow standard 1=execute anticol 2=skip anticol"); + PrintAndLogEx(NORMAL, " b 0|1|2 BCC: 0=follow standard 1=use fixed BCC 2=use card BCC"); + PrintAndLogEx(NORMAL, " 2 0|1|2 SAK<>CL2: 0=follow standard 1=execute CL2 2=skip CL2"); + PrintAndLogEx(NORMAL, " 3 0|1|2 SAK<>CL3: 0=follow standard 1=execute CL3 2=skip CL3"); + PrintAndLogEx(NORMAL, " r 0|1|2 SAK<>ATS: 0=follow standard 1=execute RATS 2=skip RATS"); + PrintAndLogEx(NORMAL, "\nExamples:"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config ")" Print current configuration"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 ")" Force execution of anticollision"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 ")" Restore ATQA interpretation"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config b 1 ")" Force fix of bad BCC in anticollision"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config b 0 ")" Restore BCC check"); + PrintAndLogEx(NORMAL, "\nExamples to revive Gen2/DirectWrite magic cards failing at anticollision:"); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 4b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 2 r 2")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344440804006263646566676869")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 r 0")); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 4b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 2 r 2")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344441802006263646566676869")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 r 0")); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 7b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 1 3 2 r 2")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566084400626364656667")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 3 0 r 0")); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 7b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 1 3 2 r 2")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566184200626364656667")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 3 0 r 0")); + PrintAndLogEx(NORMAL, _CYAN_(" MFUL ")"/" _CYAN_(" MFUL EV1 ")"/" _CYAN_(" MFULC")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 1 3 2 r 2")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid 04112233445566")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 3 0 r 0")); + return PM3_SUCCESS; +} + static int usage_hf_14a_sim(void) { // PrintAndLogEx(NORMAL, "\n Emulating ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n"); PrintAndLogEx(NORMAL, "\n Emulating ISO/IEC 14443 type A tag with 4,7 byte UID\n"); @@ -183,6 +222,7 @@ static int usage_hf_14a_sim(void) { PrintAndLogEx(NORMAL, " 7 = AMIIBO (NTAG 215), pack 0x8080"); PrintAndLogEx(NORMAL, " 8 = MIFARE Classic 4k"); PrintAndLogEx(NORMAL, " 9 = FM11RF005SH Shanghai Metro"); + PrintAndLogEx(NORMAL, " 10 = JCOP 31/41 Rothult"); // PrintAndLogEx(NORMAL, " u : 4, 7 or 10 byte UID"); PrintAndLogEx(NORMAL, " u : 4, 7 byte UID"); PrintAndLogEx(NORMAL, " x : (Optional) Performs the 'reader attack', nr/ar attack against a reader"); @@ -196,8 +236,8 @@ static int usage_hf_14a_sim(void) { return PM3_SUCCESS; } static int usage_hf_14a_sniff(void) { - PrintAndLogEx(NORMAL, "It get data from the field and saves it into command buffer."); - PrintAndLogEx(NORMAL, "Buffer accessible from command 'hf list 14a'"); + PrintAndLogEx(NORMAL, "Collect data from the field and save into command buffer."); + PrintAndLogEx(NORMAL, "Buffer accessible from command 'hf 14a list'"); PrintAndLogEx(NORMAL, "Usage: hf 14a sniff [c][r]"); PrintAndLogEx(NORMAL, "c - triggered by first data from card"); PrintAndLogEx(NORMAL, "r - triggered by first 7-bit request from reader (REQ,WUP,...)"); @@ -206,11 +246,11 @@ static int usage_hf_14a_sniff(void) { return PM3_SUCCESS; } static int usage_hf_14a_raw(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14a raw [-h] [-r] [-c] [-p] [-a] [-T] [-t] [-b] <0A 0B 0C ... hex>"); + PrintAndLogEx(NORMAL, "Usage: hf 14a raw [-h] [-r] [-c] [-k] [-a] [-T] [-t] [-b] <0A 0B 0C ... hex>"); PrintAndLogEx(NORMAL, " -h this help"); PrintAndLogEx(NORMAL, " -r do not read response"); PrintAndLogEx(NORMAL, " -c calculate and append CRC"); - PrintAndLogEx(NORMAL, " -p leave the signal field ON after receive"); + PrintAndLogEx(NORMAL, " -k keep signal field ON after receive"); PrintAndLogEx(NORMAL, " -a active signal field ON without select"); PrintAndLogEx(NORMAL, " -s active signal field ON with select"); PrintAndLogEx(NORMAL, " -b number of bits to send. Useful for send partial byte"); @@ -229,9 +269,169 @@ static int usage_hf_14a_reader(void) { } static int CmdHF14AList(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - CmdTraceList("14a"); - return 0; + char args[128] = {0}; + if (strlen(Cmd) == 0) { + snprintf(args, sizeof(args), "-t 14a"); + } else { + strncpy(args, Cmd, sizeof(args) - 1); + } + return CmdTraceList(args); +} + +int hf14a_getconfig(hf14a_config *config) { + if (!session.pm3_present) return PM3_ENOTTY; + + if (config == NULL) + return PM3_EINVARG; + + clearCommandBuffer(); + + SendCommandNG(CMD_HF_ISO14443A_GET_CONFIG, NULL, 0); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_HF_ISO14443A_GET_CONFIG, &resp, 2000)) { + PrintAndLogEx(WARNING, "command execution time out"); + return PM3_ETIMEOUT; + } + memcpy(config, resp.data.asBytes, sizeof(hf14a_config)); + return PM3_SUCCESS; +} + +int hf14a_setconfig(hf14a_config *config) { + if (!session.pm3_present) return PM3_ENOTTY; + + clearCommandBuffer(); + if (config != NULL) + SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)config, sizeof(hf14a_config)); + else + SendCommandNG(CMD_HF_ISO14443A_PRINT_CONFIG, NULL, 0); + + return PM3_SUCCESS; +} + +static int CmdHf14AConfig(const char *Cmd) { + + if (!session.pm3_present) return PM3_ENOTTY; + + // if called with no params, just print the device config + if (strlen(Cmd) == 0) { + return hf14a_setconfig(NULL); + } + + hf14a_config config = { + .forceanticol = -1, + .forcebcc = -1, + .forcecl2 = -1, + .forcecl3 = -1, + .forcerats = -1 + }; + + bool errors = false; + uint8_t cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (param_getchar(Cmd, cmdp)) { + case 'h': + return usage_hf_14a_config(); + case 'a': + switch (param_getchar(Cmd, cmdp + 1)) { + case '0': + config.forceanticol = 0; + break; + case '1': + config.forceanticol = 1; + break; + case '2': + config.forceanticol = 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); + errors = 1; + break; + } + cmdp += 2; + break; + case 'b': + switch (param_getchar(Cmd, cmdp + 1)) { + case '0': + config.forcebcc = 0; + break; + case '1': + config.forcebcc = 1; + break; + case '2': + config.forcebcc = 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); + errors = 1; + break; + } + cmdp += 2; + break; + case '2': + switch (param_getchar(Cmd, cmdp + 1)) { + case '0': + config.forcecl2 = 0; + break; + case '1': + config.forcecl2 = 1; + break; + case '2': + config.forcecl2 = 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); + errors = 1; + break; + } + cmdp += 2; + break; + case '3': + switch (param_getchar(Cmd, cmdp + 1)) { + case '0': + config.forcecl3 = 0; + break; + case '1': + config.forcecl3 = 1; + break; + case '2': + config.forcecl3 = 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); + errors = 1; + break; + } + cmdp += 2; + break; + case 'r': + switch (param_getchar(Cmd, cmdp + 1)) { + case '0': + config.forcerats = 0; + break; + case '1': + config.forcerats = 1; + break; + case '2': + config.forcerats = 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); + errors = 1; + break; + } + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = 1; + break; + } + } + + // validations + if (errors) return usage_hf_14a_config(); + + return hf14a_setconfig(&config); } int Hf14443_4aGetCardData(iso14a_card_select_t *card) { @@ -367,13 +567,13 @@ static int CmdHF14AInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a info", "This command makes more extensive tests against a ISO14443a tag in order to collect information", - "Sample:\n\thf 14a info -nsv - shows full information about the card\n"); + "hf 14a info -nsv -> shows full information about the card\n"); void *argtable[] = { arg_param_begin, - arg_lit0("vV", "verbose", "adds some information to results"), - arg_lit0("nN", "nacktest", "test for nack bug"), - arg_lit0("sS", "aidsearch", "checks if AIDs from aidlist.json is present on the card and prints information about found AIDs"), + arg_lit0("v", "verbose", "adds some information to results"), + arg_lit0("n", "nacktest", "test for nack bug"), + arg_lit0("s", "aidsearch", "checks if AIDs from aidlist.json is present on the card and prints information about found AIDs"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -884,20 +1084,20 @@ static int CmdHF14AAPDU(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a apdu", "Sends an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol (T=CL). works with all apdu types from ISO 7816-4:2013", - "Sample:\n\thf 14a apdu -st 00A404000E325041592E5359532E444446303100\n" - "\thf 14a apdu -sd 00A404000E325041592E5359532E444446303100 - decode apdu\n" - "\thf 14a apdu -sm 00A40400 325041592E5359532E4444463031 -l 256 - encode standard apdu\n" - "\thf 14a apdu -sm 00A40400 325041592E5359532E4444463031 -el 65536 - encode extended apdu\n"); + "hf 14a apdu -st 00A404000E325041592E5359532E444446303100\n" + "hf 14a apdu -sd 00A404000E325041592E5359532E444446303100 -> decode apdu\n" + "hf 14a apdu -sm 00A40400 325041592E5359532E4444463031 -l 256 -> encode standard apdu\n" + "hf 14a apdu -sm 00A40400 325041592E5359532E4444463031 -el 65536 -> encode extended apdu\n"); void *argtable[] = { arg_param_begin, - arg_lit0("sS", "select", "activate field and select card"), - arg_lit0("kK", "keep", "leave the signal field ON after receive response"), - arg_lit0("tT", "tlv", "executes TLV decoder if it possible"), - arg_lit0("dD", "decapdu", "decode apdu request if it possible"), - arg_str0("mM", "make", "", "make apdu with head from this field and data from data field. Must be 4 bytes length: "), - arg_lit0("eE", "extended", "make extended length apdu if `m` parameter included"), - arg_int0("lL", "le", "", "Le apdu parameter if `m` parameter included"), + arg_lit0("s", "select", "activate field and select card"), + arg_lit0("k", "keep", "leave the signal field ON after receive response"), + arg_lit0("t", "tlv", "executes TLV decoder if it possible"), + arg_lit0("d", "decapdu", "decode apdu request if it possible"), + arg_str0("m", "make", "", "make apdu with head from this field and data from data field. Must be 4 bytes length: "), + arg_lit0("e", "extended", "make extended length apdu if `m` parameter included"), + arg_int0("l", "le", "", "Le apdu parameter if `m` parameter included"), arg_strx1(NULL, NULL, "", "data if `m` parameter included"), arg_param_end }; @@ -990,7 +1190,7 @@ static int CmdHF14AAPDU(const char *Cmd) { static int CmdHF14ACmdRaw(const char *Cmd) { bool reply = 1; bool crc = false; - bool power = false; + bool keep_field_on = false; bool active = false; bool active_select = false; bool no_rats = false; @@ -1022,8 +1222,8 @@ static int CmdHF14ACmdRaw(const char *Cmd) { case 'c': crc = true; break; - case 'p': - power = true; + case 'k': + keep_field_on = true; break; case 'a': active = true; @@ -1071,14 +1271,14 @@ static int CmdHF14ACmdRaw(const char *Cmd) { *buf = 0; if (++datalen >= sizeof(data)) { if (crc) - PrintAndLogEx(NORMAL, "Buffer is full, we can't add CRC to your data"); + PrintAndLogEx(FAILED, "Buffer is full, we can't add CRC to your data"); break; } } continue; } - PrintAndLogEx(NORMAL, "Invalid char on input"); - return 0; + PrintAndLogEx(FAILED, "Invalid char on input"); + return PM3_ESOFT; } if (crc && datalen > 0 && datalen < sizeof(data) - 2) { @@ -1105,12 +1305,12 @@ static int CmdHF14ACmdRaw(const char *Cmd) { flags |= ISO14A_SET_TIMEOUT; if (timeout > MAX_TIMEOUT) { timeout = MAX_TIMEOUT; - PrintAndLogEx(NORMAL, "Set timeout to 40542 seconds (11.26 hours). The max we can wait for response"); + PrintAndLogEx(INFO, "Set timeout to 40542 seconds (11.26 hours). The max we can wait for response"); } argtimeout = 13560000 / 1000 / (8 * 16) * timeout; // timeout in ETUs (time to transfer 1 bit, approx. 9.4 us) } - if (power) { + if (keep_field_on) { flags |= ISO14A_NO_DISCONNECT; } @@ -1134,33 +1334,47 @@ static int CmdHF14ACmdRaw(const char *Cmd) { if (reply) { int res = 0; if (active_select) - res = waitCmd(1); + res = waitCmd(1, timeout); if (!res && datalen > 0) - waitCmd(0); + waitCmd(0, timeout); } return 0; } -static int waitCmd(uint8_t iSelect) { +static int waitCmd(uint8_t iSelect, uint32_t timeout) { PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, timeout + 1500)) { uint16_t len = (resp.oldarg[0] & 0xFFFF); if (iSelect) { len = (resp.oldarg[1] & 0xFFFF); if (len) { - PrintAndLogEx(NORMAL, "Card selected. UID[%i]:", len); + PrintAndLogEx(SUCCESS, "Card selected. UID[%u]:", len); } else { PrintAndLogEx(WARNING, "Can't select card."); } } else { - PrintAndLogEx(NORMAL, "received %i bytes", len); + PrintAndLogEx(SUCCESS, "received " _YELLOW_("%u") " bytes", len); } if (!len) return 1; - PrintAndLogEx(NORMAL, "%s", sprint_hex(resp.data.asBytes, len)); + uint8_t *data = resp.data.asBytes; + + if (iSelect == 0 && len >= 3) { + bool crc = check_crc(CRC_14443_A, data, len); + + PrintAndLogEx(SUCCESS, "%s[%02X %02X] %s", + sprint_hex(data, len - 2), + data[len - 2], + data[len - 1], + (crc) ? _GREEN_("ok") : _RED_("fail") + ); + } else { + PrintAndLogEx(SUCCESS, "%s", sprint_hex(data, len)); + } + } else { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return 3; @@ -1173,8 +1387,7 @@ static int CmdHF14AAntiFuzz(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a antifuzz", "Tries to fuzz the ISO14443a anticollision phase", - "Usage:\n" - "\thf 14a antifuzz -4\n"); + "hf 14a antifuzz -4\n"); void *argtable[] = { arg_param_begin, @@ -1202,9 +1415,8 @@ static int CmdHF14AChaining(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a chaining", "Enable/Disable ISO14443a input chaining. Maximum input length goes from ATS.", - "Usage:\n" - "\thf 14a chaining disable -> disable chaining\n" - "\thf 14a chaining -> show chaining enable/disable state\n"); + "hf 14a chaining disable -> disable chaining\n" + "hf 14a chaining -> show chaining enable/disable state\n"); void *argtable[] = { arg_param_begin, @@ -1229,37 +1441,10 @@ static int CmdHF14AChaining(const char *Cmd) { return 0; } -static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdHF14AList, AlwaysAvailable, "List ISO 14443-a history"}, - {"info", CmdHF14AInfo, IfPm3Iso14443a, "Tag information"}, - {"reader", CmdHF14AReader, IfPm3Iso14443a, "Act like an ISO14443-a reader"}, - {"cuids", CmdHF14ACUIDs, IfPm3Iso14443a, " Collect n>0 ISO14443-a UIDs in one go"}, - {"sim", CmdHF14ASim, IfPm3Iso14443a, " -- Simulate ISO 14443-a tag"}, - {"sniff", CmdHF14ASniff, IfPm3Iso14443a, "sniff ISO 14443-a traffic"}, - {"apdu", CmdHF14AAPDU, IfPm3Iso14443a, "Send ISO 14443-4 APDU to tag"}, - {"chaining", CmdHF14AChaining, IfPm3Iso14443a, "Control ISO 14443-4 input chaining"}, - {"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, - {"antifuzz", CmdHF14AAntiFuzz, IfPm3Iso14443a, "Fuzzing the anticollision phase. Warning! Readers may react strange"}, - {NULL, NULL, NULL, NULL} -}; - -static int CmdHelp(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - CmdsHelp(CommandTable); - return PM3_SUCCESS; -} - -int CmdHF14A(const char *Cmd) { - clearCommandBuffer(); - return CmdsParse(CommandTable, Cmd); -} - static void printTag(const char *tag) { PrintAndLogEx(SUCCESS, " " _YELLOW_("%s"), tag); } - typedef enum { MTNONE = 0, MTCLASSIC = 1, @@ -1441,6 +1626,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { bool isMifareDESFire = false; bool isMifarePlus = false; bool isMifareUltralight = false; + bool isST = false; int nxptype = MTNONE; if (card.uidlen <= 4) { @@ -1460,6 +1646,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { PrintAndLogEx(SUCCESS, "MANUFACTURER: " _YELLOW_("%s"), getTagInfo(card.uid[0])); switch (card.uid[0]) { + case 0x02: // ST + isST = true; + break; case 0x04: // NXP nxptype = detect_nxp_card(card.sak, ((card.atqa[1] << 8) + card.atqa[0])); @@ -1482,12 +1671,22 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } else if ((card.uid[1] & 0xF0) == 0x70) { printTag("my-d(tm) move lean SLE 66R01L"); } + isMifareUltralight = true; + isMifareClassic = false; if (card.sak == 0x88) { printTag("Infineon MIFARE CLASSIC 1K"); + isMifareUltralight = false; + isMifareClassic = true; } getTagLabel(card.uid[0], card.uid[1]); break; + case 0x57: // Qualcomm + if (memcmp(card.uid, "WSDZ10m", 7) == 0) { + isMifareClassic = false; + printTag("Waveshare NFC-Powered e-Paper"); + } + break; default: getTagLabel(card.uid[0], card.uid[1]); switch (card.sak) { @@ -1773,14 +1972,30 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } } else { PrintAndLogEx(INFO, "proprietary non iso14443-4 card found, RATS not supported"); + if ((card.sak & 0x20) == 0x20) { + PrintAndLogEx(INFO, "SAK incorrectly claims that card supports RATS"); + } } int isMagic = 0; - if (isMifareClassic || isMifareUltralight) { - isMagic = detect_classic_magic(); + if (isMifareClassic) { + isMagic = detect_mf_magic(true); + } + if (isMifareUltralight) { + isMagic = detect_mf_magic(false); + } + if (isMifareClassic) { + int res = detect_classic_static_nonce(); + if (res == NONCE_STATIC) + PrintAndLogEx(SUCCESS, "Static nonce: " _YELLOW_("yes")); - if (isMifareClassic) { - int res = detect_classic_prng(); + 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 detection: " _GREEN_("weak")); else if (res == 0) @@ -1790,13 +2005,6 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (do_nack_test) detect_classic_nackbug(false); - - res = detect_classic_static_nonce(); - if (res == 1) - PrintAndLogEx(SUCCESS, "Static nonce: " _YELLOW_("yes")); - if (res == 2 && verbose) - PrintAndLogEx(SUCCESS, "Static nonce: " _RED_("fail")); - } } @@ -1809,6 +2017,36 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (isMifareDESFire && isMagic == 0) PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfdes info`")); + if (isST) + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf st info`")); + DropField(); return select_status; } + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdHF14AList, AlwaysAvailable, "List ISO 14443-a history"}, + {"info", CmdHF14AInfo, IfPm3Iso14443a, "Tag information"}, + {"reader", CmdHF14AReader, IfPm3Iso14443a, "Act like an ISO14443-a reader"}, + {"cuids", CmdHF14ACUIDs, IfPm3Iso14443a, " Collect n>0 ISO14443-a UIDs in one go"}, + {"sim", CmdHF14ASim, IfPm3Iso14443a, " -- Simulate ISO 14443-a tag"}, + {"sniff", CmdHF14ASniff, IfPm3Iso14443a, "sniff ISO 14443-a traffic"}, + {"apdu", CmdHF14AAPDU, IfPm3Iso14443a, "Send ISO 14443-4 APDU to tag"}, + {"chaining", CmdHF14AChaining, IfPm3Iso14443a, "Control ISO 14443-4 input chaining"}, + {"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, + {"antifuzz", CmdHF14AAntiFuzz, IfPm3Iso14443a, "Fuzzing the anticollision phase. Warning! Readers may react strange"}, + {"config", CmdHf14AConfig, IfPm3Iso14443a, "Configure 14a settings (use with caution)"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHF14A(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdhf14a.h b/client/src/cmdhf14a.h index 83fb2c69d..925bf8bef 100644 --- a/client/src/cmdhf14a.h +++ b/client/src/cmdhf14a.h @@ -13,7 +13,7 @@ #define CMDHF14A_H__ #include "common.h" - +#include "pm3_cmd.h" //hf14a_config #include "mifare.h" // structs // structure and database for uid -> tagtype lookups @@ -26,6 +26,8 @@ int CmdHF14A(const char *Cmd); int CmdHF14ASniff(const char *Cmd); // used by hf topaz sniff int CmdHF14ASim(const char *Cmd); // used by hf mfu sim +int hf14a_getconfig(hf14a_config *config); +int hf14a_setconfig(hf14a_config *config); int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search); const char *getTagInfo(uint8_t uid); int Hf14443_4aGetCardData(iso14a_card_select_t *card); diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index bfb19323b..9056b26a9 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh -// Modified 2018 iceman +// Modified 2018, 2020 iceman // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -10,273 +10,253 @@ //----------------------------------------------------------------------------- #include "cmdhf14b.h" - #include +#include "iso14b.h" #include "fileutils.h" - -#include "cmdparser.h" // command_t -#include "comms.h" // clearCommandBuffer +#include "cmdparser.h" // command_t +#include "commonutil.h" // ARRAYLEN +#include "comms.h" // clearCommandBuffer +#include "emv/emvcore.h" // TLVPrintFromBuffer #include "cmdtrace.h" - +#include "cliparser.h" #include "crc16.h" #include "cmdhf14a.h" -#include "protocols.h" // definitions of ISO14B protocol +#include "protocols.h" // definitions of ISO14B/7816 protocol +#include "emv/apduinfo.h" // GetAPDUCodeDescription +#include "mifare/ndef.h" // NDEFRecordsDecodeAndPrint #define TIMEOUT 2000 + +// iso14b apdu input frame length +static uint16_t apdu_frame_length = 0; +uint16_t ats_fsc[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; +bool apdu_in_framing_enable = true; + static int CmdHelp(const char *Cmd); -static int usage_hf_14b_info(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14b info [h] [s]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " s silently"); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " hf 14b info"); - return 0; -} -static int usage_hf_14b_reader(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14b reader [h] [s]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " s silently"); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " hf 14b reader"); - return 0; -} -static int usage_hf_14b_raw(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14b raw [-h] [-r] [-c] [-p] [-s / -ss] [-t] <0A 0B 0C ... hex>"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " -h this help"); - PrintAndLogEx(NORMAL, " -r do not read response"); - PrintAndLogEx(NORMAL, " -c calculate and append CRC"); - PrintAndLogEx(NORMAL, " -p leave the field on after receive"); - PrintAndLogEx(NORMAL, " -s active signal field ON with select"); - PrintAndLogEx(NORMAL, " -ss active signal field ON with select for SRx ST Microelectronics tags"); - PrintAndLogEx(NORMAL, " -t timeout in ms"); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " hf 14b raw -s -c -p 0200a40400"); - return 0; -} -static int usage_hf_14b_sniff(void) { - PrintAndLogEx(NORMAL, "It get data from the field and saves it into command buffer."); - PrintAndLogEx(NORMAL, "Buffer accessible from command 'hf list 14b'"); - PrintAndLogEx(NORMAL, "Usage: hf 14b sniff [h]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " hf 14b sniff"); - return 0; -} -static int usage_hf_14b_sim(void) { - PrintAndLogEx(NORMAL, "Emulating ISO/IEC 14443 type B tag with 4 UID / PUPI"); - PrintAndLogEx(NORMAL, "Usage: hf 14b sim [h] u "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " u 4byte UID/PUPI"); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " hf 14b sim"); - PrintAndLogEx(NORMAL, " hf 14b sim u 11223344"); - return 0; -} -static int usage_hf_14b_read_srx(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14b sriread [h] <1|2>"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " <1|2> 1 = SRIX4K , 2 = SRI512"); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " hf 14b sriread 1"); - PrintAndLogEx(NORMAL, " hf 14b sriread 2"); - return 0; -} static int usage_hf_14b_write_srx(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14b [h] sriwrite <1|2> "); + PrintAndLogEx(NORMAL, "Usage: hf 14b [h] sriwrite <1|2> "); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h this help"); PrintAndLogEx(NORMAL, " <1|2> 1 = SRIX4K , 2 = SRI512"); - PrintAndLogEx(NORMAL, " BLOCK number depends on tag, special block == FF"); + PrintAndLogEx(NORMAL, " (hex) block number depends on tag, special block == FF"); PrintAndLogEx(NORMAL, " hex bytes of data to be written"); PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " hf 14b sriwrite 1 7F 11223344"); - PrintAndLogEx(NORMAL, " hf 14b sriwrite 1 FF 11223344"); - PrintAndLogEx(NORMAL, " hf 14b sriwrite 2 15 11223344"); - PrintAndLogEx(NORMAL, " hf 14b sriwrite 2 FF 11223344"); - return 0; + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 1 7F 11223344")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 1 FF 11223344")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 2 15 11223344")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 2 FF 11223344")); + return PM3_SUCCESS; } -static int usage_hf_14b_dump(void) { - PrintAndLogEx(NORMAL, "This command dumps the contents of a ISO-14443-B tag and save it to file\n" - "\n" - "Usage: hf 14b dump [h] [card memory] \n" - "Options:\n" - "\th this help\n" - "\t[card memory] 1 = SRIX4K (default), 2 = SRI512" - "\tf filename, if no UID will be used as filename\n" - "\n" - "Example:\n" - "\thf 14b dump f\n" - "\thf 14b dump 2 f mydump"); - return 0; -} - -/* -static void switch_on_field_14b(void) { - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT, 0, 0, NULL, 0); -} -*/ static int switch_off_field_14b(void) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_DISCONNECT, 0, 0, NULL, 0); - return 0; + return PM3_SUCCESS; } -static bool waitCmd14b(bool verbose) { +static uint16_t get_sw(uint8_t *d, uint8_t n) { + if (n < 2) + return 0; + + n -= 2; + return d[n] * 0x0100 + d[n + 1]; +} + +static bool wait_cmd_14b(bool verbose, bool is_select) { PacketResponseNG resp; - - if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { - - if ((resp.oldarg[0] & 0xFF) > 0) return false; + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { uint16_t len = (resp.oldarg[1] & 0xFFFF); + uint8_t *data = resp.data.asBytes; - uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; - memcpy(data, resp.data.asBytes, len); + // 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 { + return false; + } + } + + // handle raw bytes responses if (verbose) { + if (len >= 3) { bool crc = check_crc(CRC_14443_B, data, len); - PrintAndLogEx(SUCCESS, "[LEN %u] %s[%02X %02X] %s", - len, + PrintAndLogEx(SUCCESS, "received " _YELLOW_("%u") " bytes", len); + PrintAndLogEx(SUCCESS, "%s[%02X %02X] %s", sprint_hex(data, len - 2), data[len - 2], data[len - 1], - (crc) ? _GREEN_("OK") : _RED_("FAIL") + (crc) ? _GREEN_("ok") : _RED_("fail") ); + } else if (len == 0) { + if (verbose) + PrintAndLogEx(INFO, "no response from tag"); } else { - PrintAndLogEx(SUCCESS, "[LEN %u] %s", len, sprint_hex(data, len)); + PrintAndLogEx(SUCCESS, "%s", sprint_hex(data, len)); } } return true; } else { - PrintAndLogEx(WARNING, "command execution timeout"); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); return false; } } static int CmdHF14BList(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - CmdTraceList("14b"); - return 0; + char args[128] = {0}; + if (strlen(Cmd) == 0) { + snprintf(args, sizeof(args), "-t 14b"); + } else { + strncpy(args, Cmd, sizeof(args) - 1); + } + return CmdTraceList(args); } static int CmdHF14BSim(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_14b_sim(); - uint32_t pupi = 0; - if (cmdp == 'u') { - pupi = param_get32ex(Cmd, 1, 0, 16); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b sim", + "Simulate a ISO/IEC 14443 type B tag with 4 byte UID / PUPI", + "hf 14b sim\n" + "hf 14b sim -u 11AA33BB" + ); + + void *argtable[] = { + arg_param_begin, + arg_strx0("u", "uid", "hex", "4byte UID/PUPI"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t pupi[4]; + int n = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), pupi, sizeof(pupi), &n); + CLIParserFree(ctx); + + if (res) { + PrintAndLogEx(FAILED, "failed to read pupi"); + return PM3_EINVARG; } - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_SIMULATE, pupi, 0, 0, NULL, 0); + SendCommandNG(CMD_HF_ISO14443B_SIMULATE, pupi, sizeof(pupi)); return PM3_SUCCESS; } static int CmdHF14BSniff(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_14b_sniff(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b sniff", + "Sniff the communication reader and tag", + "hf 14b sniff" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + PacketResponseNG resp; clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443B_SNIFF, NULL, 0); + + WaitForResponse(CMD_HF_ISO14443B_SNIFF, &resp); + + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 14b list") "` to view captured tracelog"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save h") "` to save tracelog for later analysing"); return PM3_SUCCESS; } static int CmdHF14BCmdRaw(const char *Cmd) { - bool reply = true, power = false, select = false, hasTimeout = false; - char buf[5] = ""; - int i = 0; - uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; - uint16_t datalen = 0; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b raw", + "Sends raw bytes to card ", + "hf 14b raw -cks --data 0200a40400 -> standard select\n" + "hf 14b raw -ck --sr --data 0200a40400 -> SRx select\n" + "hf 14b raw -ck --cts --data 0200a40400 -> C-ticket select\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_lit0("k", "keep", "leave the signal field ON after receive response"), + arg_lit0("s", "std", "activate field, use ISO14B select"), + arg_lit0(NULL, "sr", "activate field, use SRx ST select"), + arg_lit0(NULL, "cts", "activate field, use ASK C-ticket select"), + arg_lit0("c", "crc", "calculate and append CRC"), + arg_lit0("r", "noresponse", "do not read response from card"), + arg_int0("t", "timeout", "", "timeout in ms"), + arg_lit0("v", "verbose", "verbose"), + arg_strx0("d", "data", "", "data, bytes to send"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool keep_field_on = arg_get_lit(ctx, 1); + bool select_std = arg_get_lit(ctx, 2); + bool select_sr = arg_get_lit(ctx, 3); + bool select_cts = arg_get_lit(ctx, 4); + bool add_crc = arg_get_lit(ctx, 5); + bool read_reply = !arg_get_lit(ctx, 6); + int user_timeout = arg_get_int_def(ctx, 7, -1); + bool verbose = arg_get_lit(ctx, 8); + uint32_t flags = ISO14B_CONNECT; - uint32_t temp = 0, user_timeout = 0, time_wait = 0; - - if (strlen(Cmd) < 3) return usage_hf_14b_raw(); - - // strip - while (*Cmd == ' ' || *Cmd == '\t') ++Cmd; - - while (Cmd[i] != '\0') { - if (Cmd[i] == ' ' || Cmd[i] == '\t') { ++i; continue; } - if (Cmd[i] == '-') { - switch (tolower(Cmd[i + 1])) { - case 'h': - return usage_hf_14b_raw(); - case 'r': - reply = false; - break; - case 'c': - flags |= ISO14B_APPEND_CRC; - break; - case 'p': - power = true; - break; - case 's': - select = true; - if (tolower(Cmd[i + 2]) == 's') { - flags |= ISO14B_SELECT_SR; - ++i; - } else { - flags |= ISO14B_SELECT_STD; - } - break; - case 't': - hasTimeout = true; - sscanf(Cmd + i + 2, "%d", &user_timeout); - i += 3; - while (Cmd[i] != ' ' && Cmd[i] != '\0') { i++; } - i -= 2; - break; - default: - return usage_hf_14b_raw(); - } - i += 2; - continue; - } - if ((Cmd[i] >= '0' && Cmd[i] <= '9') || - (Cmd[i] >= 'a' && Cmd[i] <= 'f') || - (Cmd[i] >= 'A' && Cmd[i] <= 'F')) { - buf[strlen(buf) + 1] = 0; - buf[strlen(buf)] = Cmd[i]; - i++; - - if (strlen(buf) >= 2) { - sscanf(buf, "%x", &temp); - data[datalen++] = (uint8_t)(temp & 0xff); - *buf = 0; - memset(buf, 0x00, sizeof(buf)); - } - continue; - } - PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, i)); - return PM3_EINVARG; + if (add_crc) { + flags |= ISO14B_APPEND_CRC; } - if (hasTimeout) { + 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"); + } + + uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; + int datalen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, 9), data, sizeof(data), &datalen); + if (res && verbose) { + PrintAndLogEx(INFO, "called with no raw bytes"); + } + CLIParserFree(ctx); + + uint32_t time_wait = 0; + if (user_timeout > 0) { #define MAX_14B_TIMEOUT 40542464 // = (2^32-1) * (8*16) / 13560000Hz * 1000ms/s flags |= ISO14B_SET_TIMEOUT; if (user_timeout > MAX_14B_TIMEOUT) { user_timeout = MAX_14B_TIMEOUT; - PrintAndLogEx(INFO, "Set timeout to 40542 seconds (11.26 hours). The max we can wait for response"); + PrintAndLogEx(INFO, "set timeout to 40542 seconds (11.26 hours). The max we can wait for response"); } time_wait = 13560000 / 1000 / (8 * 16) * user_timeout; // timeout in ETUs (time to transfer 1 bit, approx. 9.4 us) + if (verbose) + PrintAndLogEx(INFO, "using timeout %u", user_timeout); } - if (power == 0) + if (keep_field_on == 0) flags |= ISO14B_DISCONNECT; if (datalen > 0) @@ -286,63 +266,73 @@ static int CmdHF14BCmdRaw(const char *Cmd) { datalen = (datalen > PM3_CMD_DATA_SIZE) ? PM3_CMD_DATA_SIZE : datalen; clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO14443B_COMMAND, flags, datalen, time_wait, data, datalen); - - if (!reply) return PM3_SUCCESS; + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, datalen, time_wait, data, datalen); + if (read_reply == false) { + clearCommandBuffer(); + return PM3_SUCCESS; + } bool success = true; - // get back iso14b_card_select_t, don't print it. - if (select) - success = waitCmd14b(false); + + // Select, device will send back iso14b_card_select_t, don't print it. + if (select_std) { + success = wait_cmd_14b(verbose, true); + if (verbose && success) + PrintAndLogEx(SUCCESS, "Got response for standard select"); + } + + if (select_sr) { + success = wait_cmd_14b(verbose, true); + if (verbose && success) + PrintAndLogEx(SUCCESS, "Got response for ST/SRx select"); + } + + if (select_cts) { + success = wait_cmd_14b(verbose, true); + if (verbose && success) + PrintAndLogEx(SUCCESS, "Got response for ASK/C-ticket select"); + } // get back response from the raw bytes you sent. - if (success && datalen > 0) waitCmd14b(true); + if (success && datalen > 0) { + wait_cmd_14b(true, false); + } return PM3_SUCCESS; } static bool get_14b_UID(iso14b_card_select_t *card) { - if (!card) + if (card == NULL) return false; - int8_t retry = 3; + int status = 0; + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { - // test for 14b SR - while (retry--) { - - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); - if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { - - uint8_t status = resp.oldarg[0]; - if (status == 0) { - memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - return true; - } + status = resp.oldarg[0]; + if (status == 0) { + memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + return true; } - } // retry + } // test 14b standard - retry = 3; - while (retry--) { + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); - if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { - - uint8_t status = resp.oldarg[0]; - if (status == 0) { - memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - return true; - } + status = resp.oldarg[0]; + if (status == 0) { + memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); + return true; } - } // retry - - if (retry <= 0) - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + } + PrintAndLogEx(WARNING, "timeout while waiting for reply."); return false; } @@ -352,19 +342,19 @@ static bool get_14b_UID(iso14b_card_select_t *card) { // 4 = bit rate capacity // 5 = max frame size / -4 info // 6 = FWI / Coding options -static void print_atqb_resp(uint8_t *data, uint8_t cid) { - //PrintAndLogEx(NORMAL, " UID: %s", sprint_hex(data+1,4)); - PrintAndLogEx(NORMAL, " App Data: %s", sprint_hex(data, 4)); - PrintAndLogEx(NORMAL, " Protocol: %s", sprint_hex(data + 4, 3)); +static int print_atqb_resp(uint8_t *data, uint8_t cid) { + //PrintAndLogEx(SUCCESS, " UID: %s", sprint_hex(data+1,4)); + PrintAndLogEx(SUCCESS, " App Data: %s", sprint_hex(data, 4)); + PrintAndLogEx(SUCCESS, " Protocol: %s", sprint_hex(data + 4, 3)); uint8_t BitRate = data[4]; - if (!BitRate) PrintAndLogEx(NORMAL, " Bit Rate: 106 kbit/s only PICC <-> PCD"); - if (BitRate & 0x10) PrintAndLogEx(NORMAL, " Bit Rate: 212 kbit/s PICC -> PCD supported"); - if (BitRate & 0x20) PrintAndLogEx(NORMAL, " Bit Rate: 424 kbit/s PICC -> PCD supported"); - if (BitRate & 0x40) PrintAndLogEx(NORMAL, " Bit Rate: 847 kbit/s PICC -> PCD supported"); - if (BitRate & 0x01) PrintAndLogEx(NORMAL, " Bit Rate: 212 kbit/s PICC <- PCD supported"); - if (BitRate & 0x02) PrintAndLogEx(NORMAL, " Bit Rate: 424 kbit/s PICC <- PCD supported"); - if (BitRate & 0x04) PrintAndLogEx(NORMAL, " Bit Rate: 847 kbit/s PICC <- PCD supported"); - if (BitRate & 0x80) PrintAndLogEx(NORMAL, " Same bit rate <-> required"); + if (!BitRate) PrintAndLogEx(SUCCESS, " Bit Rate: 106 kbit/s only PICC <-> PCD"); + if (BitRate & 0x10) PrintAndLogEx(SUCCESS, " Bit Rate: 212 kbit/s PICC -> PCD supported"); + if (BitRate & 0x20) PrintAndLogEx(SUCCESS, " Bit Rate: 424 kbit/s PICC -> PCD supported"); + if (BitRate & 0x40) PrintAndLogEx(SUCCESS, " Bit Rate: 847 kbit/s PICC -> PCD supported"); + if (BitRate & 0x01) PrintAndLogEx(SUCCESS, " Bit Rate: 212 kbit/s PICC <- PCD supported"); + if (BitRate & 0x02) PrintAndLogEx(SUCCESS, " Bit Rate: 424 kbit/s PICC <- PCD supported"); + if (BitRate & 0x04) PrintAndLogEx(SUCCESS, " Bit Rate: 847 kbit/s PICC <- PCD supported"); + if (BitRate & 0x80) PrintAndLogEx(SUCCESS, " Same bit rate <-> required"); uint16_t maxFrame = data[5] >> 4; if (maxFrame < 5) maxFrame = 8 * maxFrame + 16; @@ -374,31 +364,31 @@ static void print_atqb_resp(uint8_t *data, uint8_t cid) { else if (maxFrame == 8) maxFrame = 256; else maxFrame = 257; - PrintAndLogEx(NORMAL, "Max Frame Size: %u%s bytes", maxFrame, (maxFrame == 257) ? "+ RFU" : ""); + PrintAndLogEx(SUCCESS, "Max Frame Size: %u%s bytes", maxFrame, (maxFrame == 257) ? "+ RFU" : ""); uint8_t protocolT = data[5] & 0xF; - PrintAndLogEx(NORMAL, " Protocol Type: Protocol is %scompliant with ISO/IEC 14443-4", (protocolT) ? "" : "not "); + PrintAndLogEx(SUCCESS, " Protocol Type: Protocol is %scompliant with ISO/IEC 14443-4", (protocolT) ? "" : "not "); uint8_t fwt = data[6] >> 4; if (fwt < 16) { uint32_t etus = (32 << fwt); uint32_t fwt_time = (302 << fwt); - PrintAndLogEx(NORMAL, "Frame Wait Integer: %u - %u ETUs | %u us", fwt, etus, fwt_time); + PrintAndLogEx(SUCCESS, "Frame Wait Integer: %u - %u ETUs | %u us", fwt, etus, fwt_time); } else { - PrintAndLogEx(NORMAL, "Frame Wait Integer: %u - RFU", fwt); + PrintAndLogEx(SUCCESS, "Frame Wait Integer: %u - RFU", fwt); } - PrintAndLogEx(NORMAL, " App Data Code: Application is %s", (data[6] & 4) ? "Standard" : "Proprietary"); - PrintAndLogEx(NORMAL, " Frame Options: NAD is %ssupported", (data[6] & 2) ? "" : "not "); - PrintAndLogEx(NORMAL, " Frame Options: CID is %ssupported", (data[6] & 1) ? "" : "not "); - PrintAndLogEx(NORMAL, "Tag :"); - PrintAndLogEx(NORMAL, " Max Buf Length: %u (MBLI) %s", cid >> 4, (cid & 0xF0) ? "" : "chained frames not supported"); - PrintAndLogEx(NORMAL, " CID : %u", cid & 0x0f); - return; + PrintAndLogEx(SUCCESS, " App Data Code: Application is %s", (data[6] & 4) ? "Standard" : "Proprietary"); + PrintAndLogEx(SUCCESS, " Frame Options: NAD is %ssupported", (data[6] & 2) ? "" : "not "); + PrintAndLogEx(SUCCESS, " Frame Options: CID is %ssupported", (data[6] & 1) ? "" : "not "); + PrintAndLogEx(SUCCESS, "Tag :"); + PrintAndLogEx(SUCCESS, " Max Buf Length: %u (MBLI) %s", cid >> 4, (cid & 0xF0) ? "" : "chained frames not supported"); + PrintAndLogEx(SUCCESS, " CID : %u", cid & 0x0f); + return PM3_SUCCESS; } // get SRx chip model (from UID) // from ST Microelectronics -static char *get_ST_Chip_Model(uint8_t data) { +static char *get_st_chip_model(uint8_t data) { static char model[20]; char *retStr = model; memset(model, 0, sizeof(model)); @@ -432,66 +422,221 @@ static char *get_ST_Chip_Model(uint8_t data) { return retStr; } -// REMAKE: -/* -static int print_ST_Lock_info(uint8_t model) { +static char *get_st_lock_info(uint8_t model, uint8_t *lockbytes, uint8_t blk) { - PrintAndLogEx(NORMAL, "Chip Write Protection Bits:"); - // now interpret the data - switch (model){ - case 0x0: //fall through (SRIX4K special) - case 0x3: //fall through (SRIx4K) - case 0x7: // (SRI4K) - //only need data[3] - blk1 = 9; - PrintAndLogEx(NORMAL, " raw: %s", sprint_bin(data+3, 1)); - PrintAndLogEx(NORMAL, " 07/08:%slocked", (data[3] & 1) ? " not " : " " ); - for (uint8_t i = 1; i<8; i++){ - PrintAndLogEx(NORMAL, " %02u:%slocked", blk1, (data[3] & (1 << i)) ? " not " : " " ); - blk1++; + static char str[16]; + char *s = str; + sprintf(s, " "); + + if (blk > 15) { + return s; } - break; - case 0x4: //fall through (SRIX512) - case 0x6: //fall through (SRI512) - case 0xC: // (SRT512) - //need data[2] and data[3] - blk1 = 0; - PrintAndLogEx(NORMAL, " raw: %s", sprint_bin(data+2, 2)); - for (uint8_t b=2; b<4; b++){ - for (uint8_t i=0; i<8; i++){ - PrintAndLogEx(NORMAL, " %02u:%slocked", blk1, (data[b] & (1 << i)) ? " not " : " " ); - blk1++; + + uint8_t mask = 0; + switch (model) { + case 0x0: // SRIX4K special + case 0x3: // SRIx4K + case 0x7: { // SRI4K + //only need data[3] + switch (blk) { + case 7: + case 8: + mask = 0x01; + break; + case 9: + mask = 0x02; + break; + case 10: + mask = 0x04; + break; + case 11: + mask = 0x08; + break; + case 12: + mask = 0x10; + break; + case 13: + mask = 0x20; + break; + case 14: + mask = 0x40; + break; + case 15: + mask = 0x80; + break; + default: + return s; + } + if ((lockbytes[1] & mask) == 0) { + sprintf(s, _RED_("1")); + } + return s; + } + case 0x4: // SRIX512 + case 0x6: // SRI512 + case 0xC: { // SRT512 + //need data[2] and data[3] + uint8_t b = 1; + switch (blk) { + case 0: + mask = 0x01; + break; + case 1: + mask = 0x02; + break; + case 2: + mask = 0x04; + break; + case 3: + mask = 0x08; + break; + case 4: + mask = 0x10; + break; + case 5: + mask = 0x20; + break; + case 6: + mask = 0x40; + break; + case 7: + mask = 0x80; + break; + case 8: + mask = 0x01; + b = 0; + break; + case 9: + mask = 0x02; + b = 0; + break; + case 10: + mask = 0x04; + b = 0; + break; + case 11: + mask = 0x08; + b = 0; + break; + case 12: + mask = 0x10; + b = 0; + break; + case 13: + mask = 0x20; + b = 0; + break; + case 14: + mask = 0x40; + b = 0; + break; + case 15: + mask = 0x80; + b = 0; + break; + } + if ((lockbytes[b] & mask) == 0) { + sprintf(s, _RED_("1")); + } + return s; + } + case 0x2: { // SR176 + //need data[2] + switch (blk) { + case 0: + case 1: + mask = 0x1; + break; + case 2: + case 3: + mask = 0x2; + break; + case 4: + case 5: + mask = 0x4; + break; + case 6: + case 7: + mask = 0x8; + break; + case 8: + case 9: + mask = 0x10; + break; + case 10: + case 11: + mask = 0x20; + break; + case 12: + case 13: + mask = 0x40; + break; + case 14: + case 15: + mask = 0x80; + break; + } + // iceman: this is opposite! need sample to test with. + if ((lockbytes[0] & mask)) { + sprintf(s, _RED_("1")); + } + return s; + } + default: + break; } - } - break; - case 0x2: // (SR176) - //need data[2] - blk1 = 0; - PrintAndLogEx(NORMAL, " raw: %s", sprint_bin(data+2, 1)); - for (uint8_t i = 0; i<8; i++){ - PrintAndLogEx(NORMAL, " %02u/%02u:%slocked", blk1, blk1+1, (data[2] & (1 << i)) ? " " : " not " ); - blk1+=2; - } - break; - default: - return rawClose(); - } - return 1; + return s; } -*/ + +static uint8_t get_st_chipid(uint8_t *uid) { + return uid[5] >> 2; +} + +static uint8_t get_st_cardsize(uint8_t *uid) { + uint8_t chipid = get_st_chipid(uid); + switch (chipid) { + case 0x0: + case 0x3: + case 0x7: + return 1; + case 0x4: + case 0x6: + case 0xC: + return 2; + default: + return 0; + } + 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 - PrintAndLogEx(NORMAL, " UID: %s", sprint_hex(SwapEndian64(data, 8, 8), len)); - PrintAndLogEx(NORMAL, " MFG: %02X, %s", data[6], getTagInfo(data[6])); - PrintAndLogEx(NORMAL, "Chip: %02X, %s", data[5] >> 2, get_ST_Chip_Model(data[5] >> 2)); + uint8_t mfgid = data[6]; + uint8_t chipid = get_st_chipid(data); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(SwapEndian64(data, 8, 8), len)); + PrintAndLogEx(SUCCESS, " MFG: %02X, " _YELLOW_("%s"), mfgid, getTagInfo(mfgid)); + PrintAndLogEx(SUCCESS, "Chip: %02X, " _YELLOW_("%s"), chipid, get_st_chip_model(chipid)); } -//05 00 00 = find one tag in field -//1d xx xx xx xx 00 08 01 00 = attrib xx=UID (resp 10 [f9 e0]) -//a3 = ? (resp 03 [e2 c2]) -//02 = ? (resp 02 [6a d3]) -// 022b (resp 02 67 00 [29 5b]) +// print UID info from ASK CT chips +static void print_ct_general_info(void *vcard) { + iso14b_cts_card_select_t card; + memcpy(&card, (iso14b_cts_card_select_t *)vcard, sizeof(iso14b_cts_card_select_t)); + + uint32_t uid32 = (card.uid[0] | card.uid[1] << 8 | card.uid[2] << 16 | card.uid[3] << 24); + PrintAndLogEx(SUCCESS, "ASK C-Ticket"); + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s") " ( " _YELLOW_("%010u") " )", sprint_hex(card.uid, sizeof(card.uid)), uid32); + PrintAndLogEx(SUCCESS, " Product Code: %02X", card.pc); + PrintAndLogEx(SUCCESS, " Facility Code: %02X", card.fc); + PrintAndLogEx(NORMAL, ""); + +} + +// iceman, calypso? +// 05 00 00 = find one tag in field +// 1d xx xx xx xx 00 08 01 00 = attrib xx=UID (resp 10 [f9 e0]) // 0200a40400 (resp 02 67 00 [29 5b]) // 0200a4040c07a0000002480300 (resp 02 67 00 [29 5b]) // 0200a4040c07a0000002480200 (resp 02 67 00 [29 5b]) @@ -500,45 +645,41 @@ static void print_st_general_info(uint8_t *data, uint8_t len) { // 0200a404000cd2760001354b414e4d30310000 (resp 02 6a 82 [4b 4c]) // 0200a404000ca000000063504b43532d313500 (resp 02 6a 82 [4b 4c]) // 0200a4040010a000000018300301000000000000000000 (resp 02 6a 82 [4b 4c]) -//03 = ? (resp 03 [e3 c2]) -//c2 = ? (resp c2 [66 15]) -//b2 = ? (resp a3 [e9 67]) -//a2 = ? (resp 02 [6a d3]) // 14b get and print Full Info (as much as we know) static bool HF14B_Std_Info(bool verbose) { - bool isSuccess = false; + bool is_success = false; // 14b get and print UID only (general info) clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { - if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply"); switch_off_field_14b(); - return false; + return is_success; } 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]; + int status = resp.oldarg[0]; switch (status) { case 0: PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, " UID : " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid); print_atqb_resp(card.atqb, card.cid); - isSuccess = true; + is_success = true; break; - case 2: + case -1: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); break; - case 3: + case -2: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); break; default: @@ -546,203 +687,273 @@ static bool HF14B_Std_Info(bool verbose) { break; } - return isSuccess; + return is_success; } // SRx get and print full info (needs more info...) static bool HF14B_ST_Info(bool verbose) { - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { - if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply"); 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]; - if (status > 0) + int status = resp.oldarg[0]; + if (status < 0) return false; print_st_general_info(card.uid, card.uidlen); - - //add locking bit information here. uint8_t data[16] = {0x00}; - // uint8_t datalen = 2; - // uint8_t resplen; - // uint8_t blk1; - // data[0] = 0x08; - - // - // if (model == 0x2) { //SR176 has special command: - // data[1] = 0xf; - // resplen = 4; - // } else { - // data[1] = 0xff; - // resplen = 6; - // } - - // //std read cmd - // if (HF14BCmdRaw(true, true, data, &datalen, false)==0) - // return rawClose(); - - // if (datalen != resplen || !crc) return rawClose(); - //print_ST_Lock_info(data[5]>>2); return true; } // menu command to get and print all info known about any known 14b tag static int CmdHF14Binfo(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_14b_info(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b info", + "Tag information for ISO/IEC 14443 type B based tags", + "hf 14b info\n" + ); - bool verbose = !(cmdp == 's'); + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + CLIParserFree(ctx); return infoHF14B(verbose); } -static bool HF14B_ST_Reader(bool verbose) { +static bool HF14B_st_reader(bool verbose) { - bool isSuccess = false; + bool is_success = false; // SRx get and print general info about SRx chip from UID clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { - if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); - return false; + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); + + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return is_success; } 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]; + int status = resp.oldarg[0]; switch (status) { case 0: print_st_general_info(card.uid, card.uidlen); - isSuccess = true; + is_success = true; break; - case 1: - if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 random chip id fail"); + case -1: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ST ATTRIB fail"); break; - case 2: - if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); + case -2: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ST CRC fail"); break; - case 3: - if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); + case -3: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ST random chip id fail"); break; default: - if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b card select SRx failed"); + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b ST card select SRx failed"); break; } - return isSuccess; + return is_success; } -static bool HF14B_Std_Reader(bool verbose) { +static bool HF14B_std_reader(bool verbose) { - bool isSuccess = false; + bool is_success = false; // 14b get and print UID only (general info) clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); - if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { - if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { + if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply"); 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]; + int status = resp.oldarg[0]; switch (status) { - case 0: + case 0: { + 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, " UID : " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid); print_atqb_resp(card.atqb, card.cid); - isSuccess = true; + is_success = true; break; - case 2: + } + case -1: { if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); break; - case 3: + } + case -2: { if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); break; - default: + } + default: { if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b card select failed"); break; + } } - return isSuccess; + return is_success; +} + +static bool HF14B_ask_ct_reader(bool verbose) { + + bool is_success = false; + + // 14b get and print UID only (general info) + clearCommandBuffer(); + PacketResponseNG resp; + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_CTS | ISO14B_DISCONNECT, 0, 0, NULL, 0); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { + if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return false; + } + + int status = resp.oldarg[0]; + + switch (status) { + case 0: { + print_ct_general_info(resp.data.asBytes); + is_success = true; + break; + } + case -1: { + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CTS wrong length"); + break; + } + case -2: { + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CTS CRC fail"); + break; + } + default: { + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b CTS card select failed"); + break; + } + } + return is_success; } // test for other 14b type tags (mimic another reader - don't have tags to identify) -static bool HF14B_Other_Reader(void) { +static bool HF14B_other_reader(bool verbose) { - // uint8_t data[] = {0x00, 0x0b, 0x3f, 0x80}; - // uint8_t datalen = 4; + uint8_t data[] = {0x00, 0x0b, 0x3f, 0x80}; + uint8_t datalen = 4; - // // 14b get and print UID only (general info) - // uint32_t flags = ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_RAW | ISO14B_APPEND_CRC; + // 14b get and print UID only (general info) + uint32_t flags = ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_RAW | ISO14B_APPEND_CRC; - // clearCommandBuffer(); - // SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, datalen, 0, data, datalen); - // PacketResponseNG resp; - // WaitForResponse(CMD_ACK,&resp); + clearCommandBuffer(); + PacketResponseNG resp; + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, datalen, 0, data, datalen); - // if (datalen > 2 ) { - // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); - // PrintAndLogEx(NORMAL, "unknown tag type answered to a 0x000b3f80 command ans:"); - // //PrintAndLogEx(NORMAL, "%s", sprint_hex(data, datalen)); - // rawclose(); - // return true; - // } + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply"); + switch_off_field_14b(); + return false; + } + int status = resp.oldarg[0]; + PrintAndLogEx(DEBUG, "status %d", status); - // data[0] = ISO14443B_AUTHENTICATE; - // clearCommandBuffer(); - // SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); - // PacketResponseNG resp; - // WaitForResponse(CMD_ACK, &resp); + if (status == 0) { + PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); + PrintAndLogEx(SUCCESS, "unknown tag type answered to a 0x000b3f80 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 0x000b3f80 command ans:"); + PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp.data.asBytes, status)); + switch_off_field_14b(); + return true; + } - // if (datalen > 0) { - // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); - // PrintAndLogEx(NORMAL, "Unknown tag type answered to a 0x0A command ans:"); - // // PrintAndLogEx(NORMAL, "%s", sprint_hex(data, datalen)); - // rawClose(); - // return true; - // } + data[0] = ISO14443B_AUTHENTICATE; + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply"); + switch_off_field_14b(); + return false; + } + status = resp.oldarg[0]; + PrintAndLogEx(DEBUG, "status %d", status); - // data[0] = ISO14443B_RESET; - // clearCommandBuffer(); - // SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); - // PacketResponseNG resp; - // WaitForResponse(CMD_ACK, &resp); + if (status == 0) { + PrintAndLogEx(SUCCESS, "\n14443-3b tag found:"); + PrintAndLogEx(SUCCESS, "Unknown tag type answered to a 0x0A 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 0x0A command ans:"); + PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp.data.asBytes, status)); + switch_off_field_14b(); + return true; + } - // if (datalen > 0) { - // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); - // PrintAndLogEx(NORMAL, "Unknown tag type answered to a 0x0C command ans:"); - // PrintAndLogEx(NORMAL, "%s", sprint_hex(data, datalen)); - // rawClose(); - // return true; - // } + data[0] = ISO14443B_RESET; + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply"); + switch_off_field_14b(); + return false; + } + status = resp.oldarg[0]; + PrintAndLogEx(DEBUG, "status %d", status); - // rawClose(); + if (status == 0) { + 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)); + switch_off_field_14b(); + return true; + } + + switch_off_field_14b(); return false; } // menu command to get and print general info about all known 14b chips static int CmdHF14BReader(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_14b_reader(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b reader", + "Act as a 14443B reader to identify a tag", + "hf 14b reader\n" + ); - bool verbose = !(cmdp == 's'); + void *argtable[] = { + arg_param_begin, + arg_lit0("v", "verbose", "verbose"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool verbose = arg_get_lit(ctx, 1); + CLIParserFree(ctx); return readHF14B(verbose); } @@ -751,24 +962,52 @@ static int CmdHF14BReader(const char *Cmd) { * this command just dumps the contents of the memory/ */ static int CmdHF14BReadSri(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || cmdp == 'h') return usage_hf_14b_read_srx(); - uint8_t tagtype = param_get8(Cmd, 0); - uint8_t blocks = (tagtype == 1) ? 0x7F : 0x0F; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b sriread", + "Read contents of a SRI512 | SRIX4K tag", + "hf 14b sriread\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + iso14b_card_select_t card; + if (get_14b_UID(&card) == false) { + PrintAndLogEx(WARNING, "no tag found"); + return PM3_SUCCESS; + } + + if (card.uidlen != 8) { + PrintAndLogEx(FAILED, "current dump command only work with SRI4K / SRI512 tags"); + return PM3_SUCCESS; + } + + // detect cardsize + // 1 = 4096 + // 2 = 512 + uint8_t cardtype = get_st_cardsize(card.uid); + uint8_t blocks = (cardtype == 1) ? 0x7F : 0x0F; clearCommandBuffer(); SendCommandMIX(CMD_HF_SRI_READ, blocks, 0, 0, NULL, 0); - return 0; + + // iceman: should download read data and print in client. + return PM3_SUCCESS; } + // New command to write a SRI512/SRIX4K tag. static int CmdHF14BWriteSri(const char *Cmd) { /* * For SRIX4K blocks 00 - 7F - * hf 14b raw -c -p 09 $srix4kwblock $srix4kwdata + * hf 14b raw -c -k 09 $srix4kwblock $srix4kwdata * * For SR512 blocks 00 - 0F - * hf 14b raw -c -p 09 $sr512wblock $sr512wdata + * hf 14b raw -c -k 09 $sr512wblock $sr512wdata * * Special block FF = otp_lock_reg block. * Data len 4 bytes- @@ -823,44 +1062,51 @@ static int CmdHF14BWriteSri(const char *Cmd) { ); } - sprintf(str, "-ss -c %02x %02x %02x %02x %02x %02x", ISO14443B_WRITE_BLK, blockno, data[0], data[1], data[2], data[3]); + sprintf(str, "--ss -c %02x %02x %02x %02x %02x %02x", ISO14443B_WRITE_BLK, blockno, data[0], data[1], data[2], data[3]); return CmdHF14BCmdRaw(str); } // need to write to file static int CmdHF14BDump(const char *Cmd) { - uint8_t fileNameLen = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b dump", + "This command dumps the contents of a ISO-14443-B tag and save it to file\n" + "Tries to autodetect cardtype, memory size defaults to SRI4K", + "hf 14b dump\n" + "hf 14b dump -f myfilename\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_strx0("f", "file", "", "(optional) filename, if no UID will be used as filename"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; char *fptr = filename; - bool errors = false; - uint8_t cmdp = 0, cardtype = 1; - uint16_t cardsize = 0; - uint8_t blocks = 0; - iso14b_card_select_t card; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParserFree(ctx); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_14b_dump(); - case 'f': - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - cmdp += 2; - break; - default: - if (cmdp == 0) { - cardtype = param_get8ex(Cmd, cmdp, 1, 10); - cmdp++; - } else { - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } + iso14b_card_select_t card; + if (get_14b_UID(&card) == false) { + PrintAndLogEx(WARNING, "no tag found"); + return PM3_SUCCESS; } - //Validations - if (errors) return usage_hf_14b_dump(); + if (card.uidlen != 8) { + PrintAndLogEx(FAILED, "current dump command only work with SRI4K / SRI512 tags"); + return PM3_SUCCESS; + } + + // detect cardsize + // 1 = 4096 + // 2 = 512 + uint8_t cardtype = get_st_cardsize(card.uid); + uint8_t blocks = 0; + uint16_t cardsize = 0; switch (cardtype) { case 2: @@ -874,70 +1120,70 @@ static int CmdHF14BDump(const char *Cmd) { break; } - if (!get_14b_UID(&card)) { - PrintAndLogEx(WARNING, "No tag found."); - return PM3_SUCCESS; + if (fnlen < 1) { + PrintAndLogEx(INFO, "using UID as filename"); + fptr += sprintf(fptr, "hf-14b-"); + FillFileNameByUID(fptr, SwapEndian64(card.uid, card.uidlen, 8), "-dump", card.uidlen); } - if (fileNameLen < 1) { - PrintAndLogEx(INFO, "Using UID as filename"); - fptr += sprintf(fptr, "hf-14b-"); - FillFileNameByUID(fptr, card.uid, "-dump", card.uidlen); - } + uint8_t chipid = get_st_chipid(card.uid); + PrintAndLogEx(SUCCESS, "found a " _GREEN_("%s") " tag", get_st_chip_model(chipid)); // detect blocksize from card :) - PrintAndLogEx(NORMAL, "Reading memory from tag UID %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(INFO, "reading tag memory from UID " _GREEN_("%s"), sprint_hex_inrow(SwapEndian64(card.uid, card.uidlen, 8), card.uidlen)); uint8_t data[cardsize]; memset(data, 0, sizeof(data)); - - int blocknum = 0; uint8_t *recv = NULL; + int status = 0; PacketResponseNG resp; clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR, 0, 0, NULL, 0); //select - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - if (resp.oldarg[0]) { - PrintAndLogEx(INFO, "failed to select %" PRId64 " | %" PRId64, resp.oldarg[0], resp.oldarg[1]); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { + status = resp.oldarg[0]; + if (status < 0) { + PrintAndLogEx(FAILED, "failed to select arg0[%" PRId64 "] arg1 [%" PRId64 "]", resp.oldarg[0], resp.oldarg[1]); goto out; } } - uint8_t req[2] = {ISO14443B_READ_BLK}; + PrintAndLogEx(INFO, "." NOLF); + uint8_t req[2] = {ISO14443B_READ_BLK}; + int blocknum = 0; for (int retry = 0; retry < 5; retry++) { req[1] = blocknum; clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APPEND_CRC | ISO14B_RAW, 2, 0, req, sizeof(req)); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APPEND_CRC | ISO14B_RAW, 2, 0, req, sizeof(req)); - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2000)) { - uint8_t status = resp.oldarg[0] & 0xFF; - if (status > 0) { + status = resp.oldarg[0]; + if (status < 0) { + PrintAndLogEx(FAILED, "retrying one more time"); continue; } uint16_t len = (resp.oldarg[1] & 0xFFFF); recv = resp.data.asBytes; - if (!check_crc(CRC_14443_B, recv, len)) { + if (check_crc(CRC_14443_B, recv, len) == false) { PrintAndLogEx(FAILED, "crc fail, retrying one more time"); continue; } memcpy(data + (blocknum * 4), resp.data.asBytes, 4); + // last read. if (blocknum == 0xFF) { - //last read. break; } - retry = 0; blocknum++; if (blocknum > blocks) { @@ -945,36 +1191,51 @@ static int CmdHF14BDump(const char *Cmd) { blocknum = 0xFF; } - printf("."); + PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); } } + PrintAndLogEx(NORMAL, ""); if (blocknum != 0xFF) { - PrintAndLogEx(NORMAL, "\n Dump failed"); + PrintAndLogEx(FAILED, "dump failed"); goto out; } - PrintAndLogEx(NORMAL, "\n"); - PrintAndLogEx(NORMAL, "block# | data | ascii"); - PrintAndLogEx(NORMAL, "---------+--------------+----------"); + PrintAndLogEx(DEBUG, "systemblock : %s", sprint_hex(data + (blocknum * 4), 4)); + PrintAndLogEx(DEBUG, " otp lock : %02x %02x", data[(blocknum * 4)], data[(blocknum * 4) + 1]); + + + PrintAndLogEx(INFO, " block# | data |lck| ascii"); + PrintAndLogEx(INFO, "---------+--------------+---+----------"); for (int i = 0; i <= blocks; i++) { - PrintAndLogEx(NORMAL, - "%3d/0x%02X | %s | %s", + PrintAndLogEx(INFO, + "%3d/0x%02X | %s | %s | %s", i, i, sprint_hex(data + (i * 4), 4), + get_st_lock_info(chipid, data + (blocknum * 4), i), sprint_ascii(data + (i * 4), 4) ); } - PrintAndLogEx(NORMAL, "\n"); - + PrintAndLogEx(INFO, + "%3d/0x%02X | %s | %s | %s", + 0xFF, + 0xFF, + sprint_hex(data + (0xFF * 4), 4), + get_st_lock_info(chipid, data + (blocknum * 4), 0xFF), + sprint_ascii(data + (0xFF * 4), 4) + ); + PrintAndLogEx(INFO, "---------+--------------+---+----------"); + PrintAndLogEx(NORMAL, ""); + // save to file size_t datalen = (blocks + 1) * 4; saveFileEML(filename, data, datalen, 4); saveFile(filename, ".bin", data, datalen); + // JSON? out: return switch_off_field_14b(); } @@ -1085,18 +1346,448 @@ static int srix4kValid(const char *Cmd) { return 0; } */ + +static int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card) { + + PacketResponseNG resp; + if (card) + memset(card, 0, sizeof(iso14b_card_select_t)); + + switch_off_field_14b(); + + // Anticollision + SELECT STD card + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD, 0, 0, NULL, 0); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { + PrintAndLogEx(INFO, "Trying 14B Select SRx"); + + // Anticollision + SELECT SR card + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR, 0, 0, NULL, 0); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { + PrintAndLogEx(INFO, "Trying 14B Select CTS"); + + // Anticollision + SELECT ASK C-Ticket card + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_CTS, 0, 0, NULL, 0); + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT) == false) { + PrintAndLogEx(ERR, "connection timeout"); + switch_off_field_14b(); + return PM3_ESOFT; + } + } + } + + // check result + int status = resp.oldarg[0]; + if (status < 0) { + PrintAndLogEx(ERR, "No card in field."); + switch_off_field_14b(); + return PM3_ESOFT; + } + + apdu_frame_length = 0; + // get frame length from ATS in card data structure + iso14b_card_select_t *vcard = (iso14b_card_select_t *) resp.data.asBytes; +// uint8_t fsci = vcard->atqb[1] & 0x0f; +// if (fsci < ARRAYLEN(ats_fsc)) { +// apdu_frame_length = ats_fsc[fsci]; +// } + + if (card) { + memcpy(card, vcard, sizeof(iso14b_card_select_t)); + } + + if (disconnect) { + switch_off_field_14b(); + } + return PM3_SUCCESS; +} + +static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, bool activateField, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool *chainingout) { + *chainingout = false; + + if (activateField) { + // select with no disconnect and set frameLength + int selres = select_card_14443b_4(false, NULL); + if (selres != PM3_SUCCESS) + return selres; + } + + uint16_t flags = 0; + + if (chainingin) + flags = ISO14B_SEND_CHAINING; + + // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes + // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size + // here length PM3_CMD_DATA_SIZE=512 + if (datain) + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APDU | flags, (datainlen & 0xFFFF), 0, datain, datainlen & 0xFFFF); + else + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APDU | flags, 0, 0, NULL, 0); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + uint8_t *recv = resp.data.asBytes; + int rlen = resp.oldarg[0]; + uint8_t res = resp.oldarg[1]; + + int dlen = rlen - 2; + if (dlen < 0) { + dlen = 0; + } + + *dataoutlen += dlen; + + if (maxdataoutlen && *dataoutlen > maxdataoutlen) { + PrintAndLogEx(ERR, "APDU: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); + return PM3_ESOFT; + } + + // I-block ACK + if ((res & 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); + return PM3_ESOFT; + } + + memcpy(dataout, recv, dlen); + + // chaining + if ((res & 0x10) != 0) { + *chainingout = true; + } + + } else { + PrintAndLogEx(ERR, "APDU: Reply timeout."); + return PM3_ETIMEOUT; + } + + return PM3_SUCCESS; +} + +static int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + *dataoutlen = 0; + bool chaining = false; + int res; + + // 3 byte here - 1b framing header, 2b crc16 + if (apdu_in_framing_enable && + ((apdu_frame_length && (datainlen > apdu_frame_length - 3)) || (datainlen > PM3_CMD_DATA_SIZE - 3))) { + + int clen = 0; + bool v_activate_field = activate_field; + + do { + int vlen = MIN(apdu_frame_length - 3, datainlen - clen); + bool chainBlockNotLast = ((clen + vlen) < datainlen); + + *dataoutlen = 0; + res = handle_14b_apdu(chainBlockNotLast, &datain[clen], vlen, v_activate_field, dataout, maxdataoutlen, dataoutlen, &chaining); + if (res) { + if (leave_signal_on == false) + switch_off_field_14b(); + + return 200; + } + + // TODO check this one... + // check R-block ACK + // *dataoutlen!=0. 'A && (!A || B)' is equivalent to 'A && B' + if ((*dataoutlen == 0) && (*dataoutlen != 0 || chaining != chainBlockNotLast)) { + if (leave_signal_on == false) { + switch_off_field_14b(); + } + return 201; + } + + clen += vlen; + v_activate_field = false; + if (*dataoutlen) { + 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); + + } else { + + res = handle_14b_apdu(false, datain, datainlen, activate_field, dataout, maxdataoutlen, dataoutlen, &chaining); + if (res != PM3_SUCCESS) { + if (leave_signal_on == false) { + switch_off_field_14b(); + } + return res; + } + } + + while (chaining) { + // I-block with chaining + res = handle_14b_apdu(false, NULL, 0, false, &dataout[*dataoutlen], maxdataoutlen, dataoutlen, &chaining); + if (res != PM3_SUCCESS) { + if (leave_signal_on == false) { + switch_off_field_14b(); + } + return 100; + } + } + + if (leave_signal_on == false) { + switch_off_field_14b(); + } + + return PM3_SUCCESS; +} + +// ISO14443-4. 7. Half-duplex block transmission protocol +static int CmdHF14BAPDU(const char *Cmd) { + uint8_t data[PM3_CMD_DATA_SIZE]; + int datalen = 0; + uint8_t header[PM3_CMD_DATA_SIZE]; + int headerlen = 0; + bool activate_field = false; + bool leave_signal_on = false; + bool decode_TLV = false; + bool decode_APDU = false; + bool make_APDU = false; + bool extended_APDU = false; + int le = 0; + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b apdu", + "Sends an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol (T=CL). works with all apdu types from ISO 7816-4:2013", + "hf 14b apdu -s --hex 94a40800043f000002\n" + "hf 14b apdu -sd --hex 00A404000E325041592E5359532E444446303100 -> decode apdu\n" + "hf 14b apdu -sm 00A40400 -l 256 --hex 325041592E5359532E4444463031 -> encode standard apdu\n" + "hf 14b apdu -sm 00A40400 -el 65536 --hex 325041592E5359532E4444463031 -> encode extended apdu\n"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("s", "select", "activate field and select card"), + arg_lit0("k", "keep", "leave the signal field ON after receive response"), + arg_lit0("t", "tlv", "executes TLV decoder if it possible"), + arg_lit0("d", "decode", "decode apdu request if it possible"), + arg_str0("m", "make", "", "make apdu with head from this field and data from data field. Must be 4 bytes length: "), + arg_lit0("e", "extended", "make extended length apdu if `m` parameter included"), + arg_int0("l", "le", "", "Le apdu parameter if `m` parameter included"), + arg_strx1(NULL, "hex", "", " if `m` parameter included"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + activate_field = arg_get_lit(ctx, 1); + leave_signal_on = arg_get_lit(ctx, 2); + decode_TLV = arg_get_lit(ctx, 3); + decode_APDU = arg_get_lit(ctx, 4); + + CLIGetHexWithReturn(ctx, 5, header, &headerlen); + make_APDU = headerlen > 0; + if (make_APDU && headerlen != 4) { + PrintAndLogEx(ERR, "header length must be 4 bytes instead of %d", headerlen); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + extended_APDU = arg_get_lit(ctx, 6); + le = arg_get_int_def(ctx, 7, 0); + + if (make_APDU) { + uint8_t apdudata[PM3_CMD_DATA_SIZE] = {0}; + int apdudatalen = 0; + + CLIGetHexBLessWithReturn(ctx, 8, apdudata, &apdudatalen, 1 + 2); + + APDUStruct apdu; + apdu.cla = header[0]; + apdu.ins = header[1]; + apdu.p1 = header[2]; + apdu.p2 = header[3]; + + apdu.lc = apdudatalen; + apdu.data = apdudata; + + apdu.extended_apdu = extended_APDU; + apdu.le = le; + + if (APDUEncode(&apdu, data, &datalen)) { + PrintAndLogEx(ERR, "can't make apdu with provided parameters."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + } else { + if (extended_APDU) { + PrintAndLogEx(ERR, "make mode not set but here `e` option."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + if (le > 0) { + PrintAndLogEx(ERR, "make mode not set but here `l` option."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + // len = data + PCB(1b) + CRC(2b) + CLIGetHexBLessWithReturn(ctx, 8, data, &datalen, 1 + 2); + } + CLIParserFree(ctx); + + PrintAndLogEx(NORMAL, ">>>>[%s%s%s] %s", + activate_field ? "sel" : "", + leave_signal_on ? " keep" : "", + decode_TLV ? " TLV" : "", + sprint_hex(data, datalen) + ); + + if (decode_APDU) { + APDUStruct apdu; + if (APDUDecode(data, datalen, &apdu) == 0) + APDUPrint(apdu); + else + PrintAndLogEx(WARNING, "can't decode APDU."); + } + + int res = exchange_14b_apdu(data, datalen, activate_field, leave_signal_on, data, PM3_CMD_DATA_SIZE, &datalen); + if (res != PM3_SUCCESS) { + return res; + } + + PrintAndLogEx(NORMAL, "<<<< %s", sprint_hex(data, datalen)); + PrintAndLogEx(SUCCESS, "APDU response: " _YELLOW_("%02x %02x") " - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); + + // TLV decoder + if (decode_TLV && datalen > 4) { + TLVPrintFromBuffer(data, datalen - 2); + } + + return PM3_SUCCESS; +} + +static int CmdHF14BNdef(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b ndef", + "Print NFC Data Exchange Format (NDEF)", + "hf 14b ndef" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + bool activate_field = true; + bool keep_field_on = true; + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + // --------------- Select NDEF Tag application ---------------- + uint8_t aSELECT_AID[80]; + int aSELECT_AID_n = 0; + param_gethex_to_eol("00a4040007d276000085010100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); + int res = exchange_14b_apdu(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res) { + goto out; + } + + if (resplen < 2) { + res = PM3_ESOFT; + goto out; + } + + uint16_t sw = get_sw(response, resplen); + if (sw != 0x9000) { + PrintAndLogEx(ERR, "Selecting NDEF aid failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + res = PM3_ESOFT; + goto out; + } + + activate_field = false; + keep_field_on = true; + // --------------- Send CC select ---------------- + // --------------- Read binary ---------------- + + // --------------- NDEF file reading ---------------- + uint8_t aSELECT_FILE_NDEF[30]; + int aSELECT_FILE_NDEF_n = 0; + param_gethex_to_eol("00a4000c020001", 0, aSELECT_FILE_NDEF, sizeof(aSELECT_FILE_NDEF), &aSELECT_FILE_NDEF_n); + res = exchange_14b_apdu(aSELECT_FILE_NDEF, aSELECT_FILE_NDEF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res) + goto out; + + sw = get_sw(response, resplen); + if (sw != 0x9000) { + PrintAndLogEx(ERR, "Selecting NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + res = PM3_ESOFT; + goto out; + } + + // --------------- Read binary ---------------- + uint8_t aREAD_NDEF[30]; + int aREAD_NDEF_n = 0; + param_gethex_to_eol("00b0000002", 0, aREAD_NDEF, sizeof(aREAD_NDEF), &aREAD_NDEF_n); + res = exchange_14b_apdu(aREAD_NDEF, aREAD_NDEF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res) { + goto out; + } + + sw = get_sw(response, resplen); + if (sw != 0x9000) { + PrintAndLogEx(ERR, "reading NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + res = PM3_ESOFT; + goto out; + } + // take offset from response + uint8_t offset = response[1]; + + // --------------- Read binary w offset ---------------- + keep_field_on = false; + aREAD_NDEF_n = 0; + param_gethex_to_eol("00b00002", 0, aREAD_NDEF, sizeof(aREAD_NDEF), &aREAD_NDEF_n); + aREAD_NDEF[4] = offset; + res = exchange_14b_apdu(aREAD_NDEF, aREAD_NDEF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res) { + goto out; + } + + sw = get_sw(response, resplen); + if (sw != 0x9000) { + PrintAndLogEx(ERR, "reading NDEF file failed (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + res = PM3_ESOFT; + goto out; + } + + res = NDEFRecordsDecodeAndPrint(response + 2, resplen - 4); + +out: + switch_off_field_14b(); + return res; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"apdu", CmdHF14BAPDU, IfPm3Iso14443b, "Send ISO 14443-4 APDU to tag"}, {"dump", CmdHF14BDump, IfPm3Iso14443b, "Read all memory pages of an ISO14443-B tag, save to file"}, {"info", CmdHF14Binfo, IfPm3Iso14443b, "Tag information"}, - {"list", CmdHF14BList, AlwaysAvailable, "List ISO 14443B history"}, + {"list", CmdHF14BList, AlwaysAvailable, "List ISO 14443B history"}, + {"ndef", CmdHF14BNdef, IfPm3Iso14443b, "Read NDEF file on tag"}, {"raw", CmdHF14BCmdRaw, IfPm3Iso14443b, "Send raw hex data to tag"}, {"reader", CmdHF14BReader, IfPm3Iso14443b, "Act as a 14443B reader to identify a tag"}, {"sim", CmdHF14BSim, IfPm3Iso14443b, "Fake ISO 14443B tag"}, {"sniff", CmdHF14BSniff, IfPm3Iso14443b, "Eavesdrop ISO 14443B"}, {"sriread", CmdHF14BReadSri, IfPm3Iso14443b, "Read contents of a SRI512 | SRIX4K tag"}, {"sriwrite", CmdHF14BWriteSri, IfPm3Iso14443b, "Write data to a SRI512 | SRIX4K tag"}, - //{"valid", srix4kValid, AlwaysAvailable, "srix4k checksum test"}, +// {"valid", srix4kValid, AlwaysAvailable, "srix4k checksum test"}, {NULL, NULL, NULL, NULL} }; @@ -1115,10 +1806,12 @@ int CmdHF14B(const char *Cmd) { int infoHF14B(bool verbose) { // try std 14b (atqb) - if (HF14B_Std_Info(verbose)) return 1; + if (HF14B_Std_Info(verbose)) + return 1; // try ST 14b - if (HF14B_ST_Info(verbose)) return 1; + if (HF14B_ST_Info(verbose)) + return 1; // try unknown 14b read commands (to be identified later) // could be read of calypso, CEPAS, moneo, or pico pass. @@ -1130,14 +1823,21 @@ int infoHF14B(bool verbose) { int readHF14B(bool verbose) { // try std 14b (atqb) - if (HF14B_Std_Reader(verbose)) return 1; + if (HF14B_std_reader(verbose)) + return 1; // try ST Microelectronics 14b - if (HF14B_ST_Reader(verbose)) return 1; + if (HF14B_st_reader(verbose)) + return 1; + + // try ASK CT 14b + if (HF14B_ask_ct_reader(verbose)) + return 1; // try unknown 14b read commands (to be identified later) // could be read of calypso, CEPAS, moneo, or pico pass. - if (HF14B_Other_Reader()) return 1; + if (HF14B_other_reader(verbose)) + return 1; if (verbose) PrintAndLogEx(FAILED, "no 14443-B tag found"); return 0; diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 2b8812d33..238aad435 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -25,7 +25,7 @@ #include "cmdhf15.h" #include #include "cmdparser.h" // command_t -#include "commonutil.h" // ARRAYLEN +#include "commonutil.h" // ARRAYLEN #include "comms.h" // clearCommandBuffer #include "cmdtrace.h" #include "iso15693tools.h" @@ -60,10 +60,9 @@ typedef struct { uint64_t uid; int mask; // how many MSB bits used const char *desc; -} productName; +} productName_t; - -const productName uidmapping[] = { +const productName_t uidmapping[] = { // UID, #significant Bits, "Vendor(+Product)" { 0xE001000000000000LL, 16, "Motorola UK" }, @@ -210,6 +209,167 @@ const productName uidmapping[] = { static int CmdHF15Help(const char *Cmd); +static int usage_15_demod(void) { + PrintAndLogEx(NORMAL, "Tries to demodulate / decode ISO15693, from downloaded samples.\n" + "Gather samples with 'hf 15 read' / 'hf 15 record'"); + return PM3_SUCCESS; +} +static int usage_15_samples(void) { + PrintAndLogEx(NORMAL, "Acquire samples as Reader (enables carrier, send inquiry\n" + "and download it to graphbuffer. Try 'hf 15 demod' to try to demodulate/decode signal"); + return PM3_SUCCESS; +} +static int usage_15_info(void) { + PrintAndLogEx(NORMAL, "Uses the optional command 'get_systeminfo' 0x2B to try and extract information\n" + "command may fail, depending on tag.\n" + "defaults to '1 out of 4' mode\n" + "\n" + "Usage: hf 15 info [options] \n" + "Options:\n" + "\t-2 use slower '1 out of 256' mode\n" + "\tuid (either): \n" + "\t <8B hex> full UID eg E011223344556677\n" + "\t u unaddressed mode\n" + "\t * scan for tag\n" + "Examples:\n" + _YELLOW_("\thf 15 info u")); + return PM3_SUCCESS; +} +static int usage_15_record(void) { + PrintAndLogEx(NORMAL, "Record activity without enabling carrier"); + return PM3_SUCCESS; +} +static int usage_15_reader(void) { + PrintAndLogEx(NORMAL, "This command identifies a ISO 15693 tag\n" + "\n" + "Usage: hf 15 reader [h]\n" + "Options:\n" + "\th this help\n" + "\t1 read once\n" + "\n" + "Example:\n" + _YELLOW_("\thf 15 reader\n") + _YELLOW_("\thf 15 reader 1\n")); + return PM3_SUCCESS; +} +static int usage_15_sim(void) { + PrintAndLogEx(NORMAL, "Usage: hf 15 sim \n" + "\n" + "Example:\n" + _YELLOW_("\thf 15 sim E016240000000000")); + return PM3_SUCCESS; +} +static int usage_15_findafi(void) { + PrintAndLogEx(NORMAL, "This command attempts to brute force AFI of an ISO15693 tag\n" + "\n" + "Usage: hf 15 findafi"); + return PM3_SUCCESS; +} +static int usage_15_writeafi(void) { + PrintAndLogEx(NORMAL, "Usage: hf 15 writeafi \n" + "\tuid (either): \n" + "\t <8B hex> full UID eg E011223344556677\n" + "\t u unaddressed mode\n" + "\t * scan for tag\n" + "\t AFI number 0-255"); + return PM3_SUCCESS; +} +static int usage_15_writedsfid(void) { + PrintAndLogEx(NORMAL, "Usage: hf 15 writedsfid \n" + "\tuid (either): \n" + "\t <8B hex> full UID eg E011223344556677\n" + "\t u unaddressed mode\n" + "\t * scan for tag\n" + "\t DSFID number 0-255"); + return PM3_SUCCESS; +} +static int usage_15_dump(void) { + PrintAndLogEx(NORMAL, "This command dumps the contents of a ISO-15693 tag and save it to file\n" + "\n" + "Usage: hf 15 dump [h] \n" + "Options:\n" + "\th this help\n" + "\tf filename, if no UID will be used as filename\n" + "\n" + "Example:\n" + _YELLOW_("\thf 15 dump f\n") + _YELLOW_("\thf 15 dump f mydump")); + return PM3_SUCCESS; +} +static int usage_15_restore(void) { + const char *options[][2] = { + {"h", "this help"}, + {"-2", "use slower '1 out of 256' mode"}, + {"-o", "set OPTION Flag (needed for TI)"}, + {"a", "use addressed mode"}, + {"r ", "numbers of retries on error, default is 3"}, + {"f ", "load "}, + {"b ", "block size, default is 4"} + }; + PrintAndLogEx(NORMAL, "Usage: hf 15 restore [-2] [-o] [h] [r ] [u ] [f ] [b ]"); + PrintAndLogOptions(options, 7, 3); + return PM3_SUCCESS; +} +static int usage_15_raw(void) { + const char *options[][2] = { + {"-r", "do not read response" }, + {"-2", "use slower '1 out of 256' mode" }, + {"-c", "calculate and append CRC" }, + {"-k", "keep signal field ON after receive" }, + {"", "Tip: turn on debugging for verbose output"}, + }; + PrintAndLogEx(NORMAL, "Usage: hf 15 raw [-r] [-2] [-k] [-c] <0A 0B 0C ... hex>\n"); + PrintAndLogOptions(options, 4, 3); + return PM3_SUCCESS; +} +static int usage_15_read(void) { + PrintAndLogEx(NORMAL, "Usage: hf 15 read [options] \n" + "Options:\n" + "\t-2 use slower '1 out of 256' mode\n" + "\tuid (either): \n" + "\t <8B hex> full UID eg E011223344556677\n" + "\t u unaddressed mode\n" + "\t * scan for tag\n" + "\t page number 0-255"); + return PM3_SUCCESS; +} +static int usage_15_write(void) { + PrintAndLogEx(NORMAL, "Usage: hf 15 write [options] \n" + "Options:\n" + "\t-2 use slower '1 out of 256' mode\n" + "\t-o set OPTION Flag (needed for TI)\n" + "\tuid (either): \n" + "\t <8B hex> full UID eg E011223344556677\n" + "\t u unaddressed mode\n" + "\t * scan for tag\n" + "\t page number 0-255\n" + "\t data to be written eg AA BB CC DD"); + return PM3_SUCCESS; +} +static int usage_15_readmulti(void) { + PrintAndLogEx(NORMAL, "Usage: hf 15 readmulti [options] \n" + "Options:\n" + "\t-2 use slower '1 out of 256' mode\n" + "\tuid (either): \n" + "\t <8B hex> full UID eg E011223344556677\n" + "\t u unaddressed mode\n" + "\t * scan for tag\n" + "\t 0-255, page number to start\n" + "\t 1-6, number of pages"); + return PM3_SUCCESS; +} +static int usage_15_csetuid(void) { + PrintAndLogEx(NORMAL, "Set UID for magic Chinese card (only works with such cards)\n" + "\n" + "Usage: hf 15 csetuid \n" + "Options:\n" + "\tuid : <8B hex> full UID eg E011223344556677\n" + "\n" + "Example:\n" + _YELLOW_("\thf 15 csetuid E011223344556677")); + return PM3_SUCCESS; +} + static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { #define PUBLIC_ECDA_KEYLEN 33 @@ -220,7 +380,8 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, {"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"}, - {"MICRON Public key", "04f971eda742a4a80d32dcf6a814a707cc3dc396d35902f72929fdcd698b3468f2"}, + {"MIKRON Public key", "04f971eda742a4a80d32dcf6a814a707cc3dc396d35902f72929fdcd698b3468f2"}, + {"VivoKey Spark1 Public key", "04d64bb732c0d214e7ec580736acf847284b502c25c0f7f2fa86aace1dada4387a"}, }; /* uint8_t nxp_15693_public_keys[][PUBLIC_ECDA_KEYLEN] = { @@ -261,8 +422,17 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { } }; */ - uint8_t i; + uint8_t revuid[8]; + for (i = 0; i < sizeof(revuid); i++) { + revuid[i] = uid[7 - i]; + } + uint8_t revsign[32]; + for (i = 0; i < sizeof(revsign); i++) { + revsign[i] = signature[31 - i]; + } + + int reason = 0; bool is_valid = false; for (i = 0; i < ARRAYLEN(nxp_15693_public_keys); i++) { @@ -272,61 +442,68 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 8, signature, 32, false); is_valid = (res == 0); - if (is_valid) + if (is_valid) { + reason = 1; break; + } + + // try with sha256 + res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 8, signature, 32, true); + is_valid = (res == 0); + if (is_valid) { + reason = 2; + break; + } + + // try with reversed uid / signature + res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, revuid, sizeof(revuid), revsign, sizeof(revsign), false); + is_valid = (res == 0); + if (is_valid) { + reason = 3; + break; + } + + + // try with sha256 + res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, revuid, sizeof(revuid), revsign, sizeof(revsign), true); + is_valid = (res == 0); + if (is_valid) { + reason = 4; + break; + } } PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); if (is_valid == false || i == ARRAYLEN(nxp_15693_public_keys)) { - PrintAndLogEx(SUCCESS, "Signature verification " _RED_("failed")); + PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); + PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); + PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed")); return PM3_ESOFT; } - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); PrintAndLogEx(INFO, " IC signature public key name: %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(signature, 32)); - PrintAndLogEx(SUCCESS, " Signature verified: " _GREEN_("successful")); + PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); + PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful")); + switch (reason) { + case 1: + PrintAndLogEx(INFO, " Params used: UID and signature, plain"); + break; + case 2: + PrintAndLogEx(INFO, " Params used: UID and signature, SHA256"); + break; + case 3: + PrintAndLogEx(INFO, " Params used: reversed UID and signature, plain"); + break; + case 4: + PrintAndLogEx(INFO, " Params used: reversed UID and signature, SHA256"); + break; + } return PM3_SUCCESS; } -// fast method to just read the UID of a tag (collision detection not supported) -// *buf should be large enough to fit the 64bit uid -// returns 1 if succeeded -static bool getUID(uint8_t *buf) { - - PacketResponseNG resp; - uint8_t data[5]; - data[0] = ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; - data[1] = ISO15_CMD_INVENTORY; - data[2] = 0; // mask length - - AddCrc15(data, 3); - - uint8_t retry; - - // don't give up the at the first try - for (retry = 0; retry < 3; retry++) { - - clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, sizeof(data), 1, 1, data, sizeof(data)); - - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - - uint8_t resplen = resp.oldarg[0]; - if (resplen >= 12 && CheckCrc15(resp.data.asBytes, 12)) { - memcpy(buf, resp.data.asBytes + 2, 8); - DropField(); - return true; - } - } - } // retry - - DropField(); - return false; -} - // get a product description based on the UID // uid[8] tag uid // returns description of the best match @@ -379,163 +556,62 @@ static const char *TagErrorStr(uint8_t error) { } } -static int usage_15_demod(void) { - PrintAndLogEx(NORMAL, "Tries to demodulate / decode ISO15693, from downloaded samples.\n" - "Gather samples with 'hf 15 read' / 'hf 15 record'"); - return PM3_SUCCESS; +// fast method to just read the UID of a tag (collision detection not supported) +// *buf should be large enough to fit the 64bit uid +// returns 1 if succeeded +static bool getUID(bool loop, uint8_t *buf) { + + uint8_t data[5]; + data[0] = ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; + data[1] = ISO15_CMD_INVENTORY; + data[2] = 0; // mask length + + AddCrc15(data, 3); + + // params + uint8_t fast = 1; + uint8_t reply = 1; + + while (kbd_enter_pressed() == false) { + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, sizeof(data), fast, reply, data, sizeof(data)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + + int resplen = resp.oldarg[0]; + if (resplen >= 12 && CheckCrc15(resp.data.asBytes, 12)) { + + if (buf) + memcpy(buf, resp.data.asBytes + 2, 8); + + DropField(); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), iso15693_sprintUID(NULL, buf)); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("%s"), getTagInfo_15(buf)); + + if (loop == false) { + return true; + } + } + } + if (loop == false) { + break; + } + } + + DropField(); + return false; } -static int usage_15_samples(void) { - PrintAndLogEx(NORMAL, "Acquire samples as Reader (enables carrier, send inquiry\n" - "and download it to graphbuffer. Try 'hf 15 demod' to try to demodulate/decode signal"); - return PM3_SUCCESS; -} -static int usage_15_info(void) { - PrintAndLogEx(NORMAL, "Uses the optional command 'get_systeminfo' 0x2B to try and extract information\n" - "command may fail, depending on tag.\n" - "defaults to '1 out of 4' mode\n" - "\n" - "Usage: hf 15 info [options] \n" - "Options:\n" - "\t-2 use slower '1 out of 256' mode\n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "Examples:\n" - "\thf 15 info u"); - return PM3_SUCCESS; -} -static int usage_15_record(void) { - PrintAndLogEx(NORMAL, "Record activity without enabling carrier"); - return PM3_SUCCESS; -} -static int usage_15_reader(void) { - PrintAndLogEx(NORMAL, "This command identifies a ISO 15693 tag\n" - "\n" - "Usage: hf 15 reader [h]\n" - "Options:\n" - "\th this help\n" - "\n" - "Example:\n" - "\thf 15 reader"); - return PM3_SUCCESS; -} -static int usage_15_sim(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 sim \n" - "\n" - "Example:\n" - "\thf 15 sim E016240000000000"); - return PM3_SUCCESS; -} -static int usage_15_findafi(void) { - PrintAndLogEx(NORMAL, "This command attempts to brute force AFI of an ISO15693 tag\n" - "\n" - "Usage: hf 15 findafi"); - return PM3_SUCCESS; -} -static int usage_15_writeafi(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 writeafi \n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "\tafi#: AFI number 0-255"); - return PM3_SUCCESS; -} -static int usage_15_writedsfid(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 writedsfid \n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "\tdsfid#: DSFID number 0-255"); - return PM3_SUCCESS; -} -static int usage_15_dump(void) { - PrintAndLogEx(NORMAL, "This command dumps the contents of a ISO-15693 tag and save it to file\n" - "\n" - "Usage: hf 15 dump [h] \n" - "Options:\n" - "\th this help\n" - "\tf filename, if no UID will be used as filename\n" - "\n" - "Example:\n" - "\thf 15 dump f\n" - "\thf 15 dump f mydump"); - return PM3_SUCCESS; -} -static int usage_15_restore(void) { - const char *options[][2] = { - {"h", "this help"}, - {"-2", "use slower '1 out of 256' mode"}, - {"-o", "set OPTION Flag (needed for TI)"}, - {"a", "use addressed mode"}, - {"r ", "numbers of retries on error, default is 3"}, - {"f ", "load "}, - {"b ", "block size, default is 4"} - }; - PrintAndLogEx(NORMAL, "Usage: hf 15 restore [-2] [-o] [h] [r ] [u ] [f ] [b ]"); - PrintAndLogOptions(options, 7, 3); - return PM3_SUCCESS; -} -static int usage_15_raw(void) { - const char *options[][2] = { - {"-r", "do not read response" }, - {"-2", "use slower '1 out of 256' mode" }, - {"-c", "calculate and append CRC" }, - {"-p", "leave the signal field ON" }, - {"", "Tip: turn on debugging for verbose output"}, - }; - PrintAndLogEx(NORMAL, "Usage: hf 15 raw [-r] [-2] [-c] <0A 0B 0C ... hex>\n"); - PrintAndLogOptions(options, 4, 3); - return PM3_SUCCESS; -} -static int usage_15_read(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 read [options] \n" - "Options:\n" - "\t-2 use slower '1 out of 256' mode\n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "\tpage#: page number 0-255"); - return PM3_SUCCESS; -} -static int usage_15_write(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 write [options] \n" - "Options:\n" - "\t-2 use slower '1 out of 256' mode\n" - "\t-o set OPTION Flag (needed for TI)\n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "\tpage#: page number 0-255\n" - "\thexdata: data to be written eg AA BB CC DD"); - return PM3_SUCCESS; -} -static int usage_15_readmulti(void) { - PrintAndLogEx(NORMAL, "Usage: hf 15 readmulti [options] \n" - "Options:\n" - "\t-2 use slower '1 out of 256' mode\n" - "\tuid (either): \n" - "\t <8B hex> full UID eg E011223344556677\n" - "\t u unaddressed mode\n" - "\t * scan for tag\n" - "\tstart#: page number to start 0-255\n" - "\tcount#: number of pages"); - return PM3_SUCCESS; -} -static int usage_15_csetuid(void) { - PrintAndLogEx(NORMAL, "Set UID for magic Chinese card (only works with such cards)\n" - "\n" - "Usage: hf 15 csetuid \n" - "Options:\n" - "\tuid : <8B hex> full UID eg E011223344556677\n" - "\n" - "Example:\n" - "\thf 15 csetuid E011223344556677"); - return PM3_SUCCESS; + +// used with 'hf search' +bool readHF15Uid(bool loop, bool verbose) { + uint8_t uid[8] = {0}; + if (getUID(loop, uid) == false) { + if (verbose) PrintAndLogEx(WARNING, "No tag found"); + return false; + } + return true; } /** @@ -583,7 +659,7 @@ static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t req[tmpreqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; req[tmpreqlen++] = iso15cmd; - if (!getUID(uid)) { + if (getUID(false, uid) == false) { PrintAndLogEx(WARNING, "No tag found"); return false; } @@ -594,7 +670,6 @@ static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t default: req[tmpreqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; req[tmpreqlen++] = iso15cmd; - // parse UID for (int i = 0; i < 8 && (*cmd)[i * 2] && (*cmd)[i * 2 + 1]; i++) { sscanf((char[]) {(*cmd)[i * 2], (*cmd)[i * 2 + 1], 0}, "%X", &temp); @@ -710,17 +785,85 @@ static int CmdHF15Samples(const char *Cmd) { // Get NXP system information from SLIX2 tag/VICC static int NxpSysInfo(uint8_t *uid) { - PacketResponseNG resp; - uint8_t *recv; - uint8_t req[PM3_CMD_DATA_SIZE] = {0}; - uint16_t reqlen; - uint32_t status; - uint8_t arg1 = 1; - if (uid != NULL) { + if (uid == NULL) { + return PM3_EINVARG; + } + + uint8_t req[PM3_CMD_DATA_SIZE] = {0}; + uint8_t fast = 1; + uint8_t reply = 1; + uint16_t reqlen = 0; + + req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; + req[reqlen++] = ISO15_CMD_GETNXPSYSTEMINFO; + req[reqlen++] = 0x04; // IC manufacturer code + memcpy(req + 3, uid, 8); // add UID + reqlen += 8; + + AddCrc15(req, reqlen); + reqlen += 2; + + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "iso15693 timeout"); + DropField(); + return PM3_ETIMEOUT; + } + + DropField(); + + int status = resp.oldarg[0]; + if (status < 2) { + PrintAndLogEx(WARNING, "iso15693 card doesn't answer to NXP systeminfo command"); + return PM3_EWRONGANSWER; + } + + uint8_t *recv = resp.data.asBytes; + + if ((recv[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { + PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + return PM3_EWRONGANSWER; + } + + bool support_signature = (recv[5] & 0x01); + bool support_easmode = (recv[4] & 0x03); + + 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] & 0x08) ? "" : " not")); + PrintAndLogEx(INFO, " * Page H write%s password protected", ((recv[2] & 0x20) ? "" : " 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, " 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"); + PrintAndLogEx(INFO, " * EAS password protection%s supported", ((recv[4] & 0x04) ? "" : " not")); + PrintAndLogEx(INFO, " * AFI password protection%s supported", ((recv[4] & 0x10) ? "" : " not")); + PrintAndLogEx(INFO, " * Extended mode%s supported by INVENTORY READ command", ((recv[4] & 0x20) ? "" : " not")); + PrintAndLogEx(INFO, " * EAS selection%s supported by extended mode in INVENTORY READ command", ((recv[4] & 0x40) ? "" : " not")); + PrintAndLogEx(INFO, " * READ SIGNATURE command%s supported", support_signature ? "" : " not"); + PrintAndLogEx(INFO, " * Password protection for READ SIGNATURE command%s supported", ((recv[5] & 0x02) ? "" : " not")); + PrintAndLogEx(INFO, " * STAY QUIET PERSISTENT command%s supported", ((recv[5] & 0x03) ? "" : " not")); + PrintAndLogEx(INFO, " * ENABLE PRIVACY command%s supported", ((recv[5] & 0x10) ? "" : " not")); + PrintAndLogEx(INFO, " * DESTROY command%s supported", ((recv[5] & 0x20) ? "" : " not")); + PrintAndLogEx(INFO, " * Additional 32 bits feature flags are%s transmitted", ((recv[5] & 0x80) ? "" : " not")); + + if (support_easmode) { reqlen = 0; req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - req[reqlen++] = ISO15_CMD_GETNXPSYSTEMINFO; + req[reqlen++] = ISO15_CMD_EASALARM; req[reqlen++] = 0x04; // IC manufacturer code memcpy(req + 3, uid, 8); // add UID reqlen += 8; @@ -728,13 +871,45 @@ static int NxpSysInfo(uint8_t *uid) { AddCrc15(req, reqlen); reqlen += 2; - //PrintAndLogEx(NORMAL, "cmd %s", sprint_hex(req, reqlen) ); - clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(WARNING, "iso15693 card select failed"); + PrintAndLogEx(WARNING, "iso15693 timeout"); + } else { + PrintAndLogEx(NORMAL, ""); + + status = resp.oldarg[0]; + if (status < 2) { + PrintAndLogEx(INFO, " EAS (Electronic Article Surveillance) is not active"); + } else { + recv = resp.data.asBytes; + + if (!(recv[0] & ISO15_RES_ERROR)) { + PrintAndLogEx(INFO, " EAS (Electronic Article Surveillance) is active."); + PrintAndLogEx(INFO, " EAS sequence: %s", sprint_hex(recv + 1, 32)); + } + } + } + } + + if (support_signature) { + // Check if we can also read the signature + reqlen = 0; + req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; + req[reqlen++] = ISO15_CMD_READSIGNATURE; + req[reqlen++] = 0x04; // IC manufacturer code + memcpy(req + 3, uid, 8); // add UID + reqlen += 8; + + AddCrc15(req, reqlen); + reqlen += 2; + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); + + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "iso15693 timeout"); DropField(); return PM3_ETIMEOUT; } @@ -742,134 +917,22 @@ static int NxpSysInfo(uint8_t *uid) { DropField(); status = resp.oldarg[0]; - if (status < 2) { - PrintAndLogEx(WARNING, "iso15693 card doesn't answer to NXP systeminfo command"); + PrintAndLogEx(WARNING, "iso15693 card doesn't answer to READ SIGNATURE command"); return PM3_EWRONGANSWER; } recv = resp.data.asBytes; - if (recv[0] & ISO15_RES_ERROR) { + if ((recv[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); return PM3_EWRONGANSWER; } - bool support_signature = (recv[5] & 0x01); - bool support_easmode = (recv[4] & 0x03); + uint8_t signature[32] = {0x00}; + memcpy(signature, recv + 1, 32); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " NXP SYSINFO : %s", sprint_hex(recv, 8)); - PrintAndLogEx(NORMAL, " Password protection configuration:"); - PrintAndLogEx(NORMAL, " * Page L read%s password protected", ((recv[2] & 0x01) ? "" : " not")); - PrintAndLogEx(NORMAL, " * Page L write%s password protected", ((recv[2] & 0x02) ? "" : " not")); - PrintAndLogEx(NORMAL, " * Page H read%s password protected", ((recv[2] & 0x08) ? "" : " not")); - PrintAndLogEx(NORMAL, " * Page H write%s password protected", ((recv[2] & 0x20) ? "" : " not")); - - PrintAndLogEx(NORMAL, " Lock bits:"); - PrintAndLogEx(NORMAL, " * AFI%s locked", ((recv[3] & 0x01) ? "" : " not")); // AFI lock bit - PrintAndLogEx(NORMAL, " * EAS%s locked", ((recv[3] & 0x02) ? "" : " not")); // EAS lock bit - PrintAndLogEx(NORMAL, " * DSFID%s locked", ((recv[3] & 0x03) ? "" : " not")); // DSFID lock bit - PrintAndLogEx(NORMAL, " * Password protection configuration%s locked", ((recv[3] & 0x04) ? "" : " not")); // Password protection pointer address and access conditions lock bit - - PrintAndLogEx(NORMAL, " Features:"); - PrintAndLogEx(NORMAL, " * User memory password protection%s supported", ((recv[4] & 0x01) ? "" : " not")); - PrintAndLogEx(NORMAL, " * Counter feature%s supported", ((recv[4] & 0x02) ? "" : " not")); - PrintAndLogEx(NORMAL, " * EAS ID%s supported by EAS ALARM command", support_easmode ? "" : " not"); - - PrintAndLogEx(NORMAL, " * EAS password protection%s supported", ((recv[4] & 0x04) ? "" : " not")); - PrintAndLogEx(NORMAL, " * AFI password protection%s supported", ((recv[4] & 0x10) ? "" : " not")); - PrintAndLogEx(NORMAL, " * Extended mode%s supported by INVENTORY READ command", ((recv[4] & 0x20) ? "" : " not")); - PrintAndLogEx(NORMAL, " * EAS selection%s supported by extended mode in INVENTORY READ command", ((recv[4] & 0x40) ? "" : " not")); - PrintAndLogEx(NORMAL, " * READ SIGNATURE command%s supported", support_signature ? "" : " not"); - - PrintAndLogEx(NORMAL, " * Password protection for READ SIGNATURE command%s supported", ((recv[5] & 0x02) ? "" : " not")); - PrintAndLogEx(NORMAL, " * STAY QUIET PERSISTENT command%s supported", ((recv[5] & 0x03) ? "" : " not")); - PrintAndLogEx(NORMAL, " * ENABLE PRIVACY command%s supported", ((recv[5] & 0x10) ? "" : " not")); - PrintAndLogEx(NORMAL, " * DESTROY command%s supported", ((recv[5] & 0x20) ? "" : " not")); - PrintAndLogEx(NORMAL, " * Additional 32 bits feature flags are%s transmitted", ((recv[5] & 0x80) ? "" : " not")); - - if (support_easmode) { - reqlen = 0; - req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - req[reqlen++] = ISO15_CMD_EASALARM; - req[reqlen++] = 0x04; // IC manufacturer code - memcpy(req + 3, uid, 8); // add UID - reqlen += 8; - - AddCrc15(req, reqlen); - reqlen += 2; - - //PrintAndLogEx(NORMAL, "cmd %s", sprint_hex(req, reqlen) ); - - clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); - - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(WARNING, "iso15693 card select failed"); - } else { - status = resp.oldarg[0]; - - PrintAndLogEx(NORMAL, ""); - - if (status < 2) { - PrintAndLogEx(INFO, " EAS (Electronic Article Surveillance) is not active"); - } else { - recv = resp.data.asBytes; - - if (!(recv[0] & ISO15_RES_ERROR)) { - PrintAndLogEx(INFO, " EAS (Electronic Article Surveillance) is active."); - PrintAndLogEx(INFO, " EAS sequence: %s", sprint_hex(recv + 1, 32)); - } - } - } - } - - if (support_signature) { - // Check if we can also read the signature - reqlen = 0; - req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - req[reqlen++] = ISO15_CMD_READSIGNATURE; - req[reqlen++] = 0x04; // IC manufacturer code - memcpy(req + 3, uid, 8); // add UID - reqlen += 8; - - AddCrc15(req, reqlen); - reqlen += 2; - - //PrintAndLogEx(NORMAL, "cmd %s", sprint_hex(req, reqlen) ); - - clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); - - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(WARNING, "iso15693 card select failed"); - DropField(); - return PM3_ETIMEOUT; - } - - DropField(); - - status = resp.oldarg[0]; - - if (status < 2) { - PrintAndLogEx(WARNING, "iso15693 card doesn't answer to READ SIGNATURE command"); - return PM3_EWRONGANSWER; - } - - recv = resp.data.asBytes; - - if (recv[0] & ISO15_RES_ERROR) { - PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); - return PM3_EWRONGANSWER; - } - - uint8_t signature[32] = {0x00}; - memcpy(signature, recv + 1, 32); - - nxp_15693_print_signature(uid, signature); - - } + nxp_15693_print_signature(uid, signature); } return PM3_SUCCESS; @@ -885,85 +948,84 @@ static int CmdHF15Info(const char *Cmd) { if (strlen(Cmd) < 1 || cmdp == 'h') return usage_15_info(); PacketResponseNG resp; - uint8_t *recv; uint8_t req[PM3_CMD_DATA_SIZE] = {0}; uint16_t reqlen; - uint8_t arg1 = 1; - char cmdbuf[100] = {0}; - char *cmd = cmdbuf; + uint8_t fast = 1; + uint8_t reply = 1; uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + char cmdbuf[100] = {0}; + char *cmd = cmdbuf; strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); - if (!prepareHF15Cmd(&cmd, &reqlen, &arg1, req, ISO15_CMD_SYSINFO)) + if (prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_SYSINFO) == false) return PM3_SUCCESS; AddCrc15(req, reqlen); reqlen += 2; clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(WARNING, "iso15693 card select failed"); + PrintAndLogEx(WARNING, "iso15693 timeout"); DropField(); return PM3_ETIMEOUT; } DropField(); - uint32_t status = resp.oldarg[0]; - + int status = resp.oldarg[0]; if (status < 2) { - PrintAndLogEx(WARNING, "iso15693 card doesn't answer to systeminfo command"); + PrintAndLogEx(WARNING, "iso15693 card doesn't answer to systeminfo command (%d)", status); return PM3_EWRONGANSWER; } - recv = resp.data.asBytes; + uint8_t *data = resp.data.asBytes; - if (recv[0] & ISO15_RES_ERROR) { - PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + if ((data[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { + PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", data[0], TagErrorStr(data[0])); return PM3_EWRONGANSWER; } - memcpy(uid, recv + 2, sizeof(uid)); + memcpy(uid, data + 2, sizeof(uid)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); PrintAndLogEx(INFO, "-------------------------------------------------------------"); - PrintAndLogEx(SUCCESS, " TYPE: " _YELLOW_("%s"), getTagInfo_15(recv + 2)); + PrintAndLogEx(SUCCESS, " TYPE: " _YELLOW_("%s"), getTagInfo_15(data + 2)); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), iso15693_sprintUID(NULL, uid)); - PrintAndLogEx(SUCCESS, " SYSINFO: %s", sprint_hex(recv, status - 2)); + PrintAndLogEx(SUCCESS, " SYSINFO: %s", sprint_hex(data, status - 2)); // DSFID - if (recv[1] & 0x01) - PrintAndLogEx(SUCCESS, " - DSFID supported [0x%02X]", recv[10]); + if (data[1] & 0x01) + PrintAndLogEx(SUCCESS, " - DSFID supported [0x%02X]", data[10]); else PrintAndLogEx(SUCCESS, " - DSFID not supported"); // AFI - if (recv[1] & 0x02) - PrintAndLogEx(SUCCESS, " - AFI supported [0x%02X]", recv[11]); + if (data[1] & 0x02) + PrintAndLogEx(SUCCESS, " - AFI supported [0x%02X]", data[11]); else PrintAndLogEx(SUCCESS, " - AFI not supported"); // IC reference - if (recv[1] & 0x08) - PrintAndLogEx(SUCCESS, " - IC reference supported [0x%02X]", recv[14]); + if (data[1] & 0x08) + PrintAndLogEx(SUCCESS, " - IC reference supported [0x%02X]", data[14]); else PrintAndLogEx(SUCCESS, " - IC reference not supported"); // memory - if (recv[1] & 0x04) { + if (data[1] & 0x04) { PrintAndLogEx(SUCCESS, " - Tag provides info on memory layout (vendor dependent)"); - uint8_t blocks = recv[12] + 1; - uint8_t size = (recv[13] & 0x1F); + uint8_t blocks = data[12] + 1; + uint8_t size = (data[13] & 0x1F); PrintAndLogEx(SUCCESS, " %u (or %u) bytes/blocks x %u blocks", size + 1, size, blocks); } else { PrintAndLogEx(SUCCESS, " - Tag does not provide information on memory layout"); } // Check if SLIX2 and attempt to get NXP System Information - PrintAndLogEx(DEBUG, "4 & 08 :: %02x 7 == 1 :: %u 8 == 4 :: %u", recv[4], recv[7], recv[8]); - if (recv[8] == 0x04 && recv[7] == 0x01 && recv[4] & 0x80) { + PrintAndLogEx(DEBUG, "4 & 08 :: %02x 7 == 1 :: %u 8 == 4 :: %u", data[4], data[7], data[8]); + if (data[8] == 0x04 && data[7] == 0x01 && data[4] & 0x80) { return NxpSysInfo(uid); } @@ -973,20 +1035,27 @@ static int CmdHF15Info(const char *Cmd) { // Record Activity without enabling carrier //helptext -static int CmdHF15Record(const char *Cmd) { +static int CmdHF15Sniff(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (cmdp == 'h') return usage_15_record(); + PacketResponseNG resp; clearCommandBuffer(); - SendCommandNG(CMD_HF_ISO15693_RAWADC, NULL, 0); + SendCommandNG(CMD_HF_ISO15693_SNIFF, NULL, 0); + + WaitForResponse(CMD_HF_ISO15693_SNIFF, &resp); + + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 15 list") "` to view captured tracelog"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("trace save h") "` to save tracelog for later analysing"); return PM3_SUCCESS; } static int CmdHF15Reader(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (cmdp == 'h') return usage_15_reader(); + bool loop_read = (cmdp == '1') ? false : true; - readHF15Uid(true); + readHF15Uid(loop_read, true); return PM3_SUCCESS; } @@ -996,16 +1065,21 @@ static int CmdHF15Sim(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (strlen(Cmd) < 1 || cmdp == 'h') return usage_15_sim(); - uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - if (param_gethex(Cmd, 0, uid, 16)) { + struct { + uint8_t uid[8]; + } PACKED payload; + + if (param_gethex(Cmd, 0, payload.uid, 16)) { PrintAndLogEx(WARNING, "UID must include 16 HEX symbols"); return PM3_EINVARG; } - PrintAndLogEx(SUCCESS, "Starting simulating UID " _YELLOW_("%s"), iso15693_sprintUID(NULL, uid)); + PrintAndLogEx(SUCCESS, "Starting simulating UID " _YELLOW_("%s"), iso15693_sprintUID(NULL, payload.uid)); + PacketResponseNG resp; clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_SIMULATE, 0, 0, 0, uid, 8); + SendCommandNG(CMD_HF_ISO15693_SIMULATE, (uint8_t *)&payload, sizeof(payload)); + WaitForResponse(CMD_HF_ISO15693_SIMULATE, &resp); return PM3_SUCCESS; } @@ -1042,11 +1116,8 @@ static int CmdHF15FindAfi(const char *Cmd) { // Writes the AFI (Application Family Identifier) of a card static int CmdHF15WriteAfi(const char *Cmd) { - char cmdp = param_getchar(Cmd, 0); - if (strlen(Cmd) < 3 || cmdp == 'h' || cmdp == 'H') return usage_15_writeafi(); - - PacketResponseNG resp; - uint8_t *recv; + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_writeafi(); // arg: len, speed, recv? // arg0 (datalen, cmd len? .arg0 == crc?) @@ -1054,58 +1125,54 @@ static int CmdHF15WriteAfi(const char *Cmd) { // arg2 (recv == 1 == expect a response) uint8_t req[PM3_CMD_DATA_SIZE] = {0}; uint16_t reqlen = 0; - uint8_t arg1 = 1; - int afinum; + uint8_t fast = 1; + uint8_t reply = 1; + char cmdbuf[100] = {0}; char *cmd = cmdbuf; strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); - if (!prepareHF15Cmd(&cmd, &reqlen, &arg1, req, ISO15_CMD_WRITEAFI)) + if (!prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_WRITEAFI)) return PM3_SUCCESS; req[0] |= ISO15_REQ_OPTION; // Since we are writing - afinum = strtol(cmd, NULL, 0); + int afinum = strtol(cmd, NULL, 0); req[reqlen++] = (uint8_t)afinum; AddCrc15(req, reqlen); reqlen += 2; - // PrintAndLogEx(NORMAL, "cmd %s", sprint_hex(req, reqlen) ); - + PacketResponseNG resp; clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(ERR, "iso15693 card select failed"); + PrintAndLogEx(ERR, "iso15693 timeout"); DropField(); return PM3_ETIMEOUT; } DropField(); - recv = resp.data.asBytes; + uint8_t *data = resp.data.asBytes; - if (recv[0] & ISO15_RES_ERROR) { - PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + if ((data[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { + PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", data[0], TagErrorStr(data[0])); return PM3_EWRONGANSWER; } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "Wrote AFI 0x%02X", afinum); - return PM3_SUCCESS; } // Writes the DSFID (Data Storage Format Identifier) of a card static int CmdHF15WriteDsfid(const char *Cmd) { - char cmdp = param_getchar(Cmd, 0); - if (strlen(Cmd) < 3 || cmdp == 'h' || cmdp == 'H') return usage_15_writedsfid(); - - PacketResponseNG resp; - uint8_t *recv; + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_writedsfid(); // arg: len, speed, recv? // arg0 (datalen, cmd len? .arg0 == crc?) @@ -1113,13 +1180,15 @@ static int CmdHF15WriteDsfid(const char *Cmd) { // arg2 (recv == 1 == expect a response) uint8_t req[PM3_CMD_DATA_SIZE] = {0}; uint16_t reqlen = 0; - uint8_t arg1 = 1; + uint8_t fast = 1; + uint8_t reply = 1; int dsfidnum; + char cmdbuf[100] = {0}; char *cmd = cmdbuf; strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); - if (!prepareHF15Cmd(&cmd, &reqlen, &arg1, req, ISO15_CMD_WRITEDSFID)) + if (prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_WRITEDSFID) == false) return PM3_SUCCESS; req[0] |= ISO15_REQ_OPTION; // Since we are writing @@ -1132,28 +1201,27 @@ static int CmdHF15WriteDsfid(const char *Cmd) { reqlen += 2; // PrintAndLogEx(NORMAL, "cmd %s", sprint_hex(req, reqlen) ); - + PacketResponseNG resp; clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(ERR, "iso15693 card select failed"); + PrintAndLogEx(ERR, "iso15693 timeout"); DropField(); return PM3_ETIMEOUT; } DropField(); - recv = resp.data.asBytes; + uint8_t *data = resp.data.asBytes; - if (recv[0] & ISO15_RES_ERROR) { - PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + if ((data[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { + PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", data[0], TagErrorStr(data[0])); return PM3_EWRONGANSWER; } PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "Wrote DSFID 0x%02X", dsfidnum); - return PM3_SUCCESS; } @@ -1186,15 +1254,13 @@ static int CmdHF15Dump(const char *Cmd) { //Validations if (errors) return usage_15_dump(); - if (!getUID(uid)) { + if (getUID(false, uid) == false) { PrintAndLogEx(WARNING, "No tag found."); return PM3_ESOFT; } if (fileNameLen < 1) { - PrintAndLogEx(INFO, "Using UID as filename"); - fptr += sprintf(fptr, "hf-15-"); FillFileNameByUID(fptr, uid, "-dump", sizeof(uid)); } @@ -1225,24 +1291,24 @@ static int CmdHF15Dump(const char *Cmd) { AddCrc15(req, 11); clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, sizeof(req), 1, 1, req, sizeof(req)); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, sizeof(req), 1, 1, req, sizeof(req)); if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - uint8_t len = resp.oldarg[0]; + int len = resp.oldarg[0]; if (len < 2) { - PrintAndLogEx(FAILED, "iso15693 card select failed"); + PrintAndLogEx(FAILED, "iso15693 command failed"); continue; } recv = resp.data.asBytes; - if (!CheckCrc15(recv, len)) { - PrintAndLogEx(FAILED, "crc fail"); + if (CheckCrc15(recv, len) == false) { + PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); continue; } - if (recv[0] & ISO15_RES_ERROR) { + if ((recv[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { PrintAndLogEx(FAILED, "Tag returned Error %i: %s", recv[1], TagErrorStr(recv[1])); break; } @@ -1254,8 +1320,7 @@ static int CmdHF15Dump(const char *Cmd) { retry = 0; blocknum++; - printf("."); - fflush(stdout); + PrintAndLogEx(NORMAL, "." NOLF); } } @@ -1277,28 +1342,22 @@ static int CmdHF15Dump(const char *Cmd) { } static int CmdHF15List(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - CmdTraceList("15"); - return PM3_SUCCESS; + char args[128] = {0}; + if (strlen(Cmd) == 0) { + snprintf(args, sizeof(args), "-t 15"); + } else { + strncpy(args, Cmd, sizeof(args) - 1); + } + return CmdTraceList(args); } -/* -// Record Activity without enabling carrier -static int CmdHF15Sniff(const char *Cmd) { - clearCommandBuffer(); - SendCommandNG(CMD_HF_ISO15693_SNIFF, NULL, 0); - return PM3_SUCCESS; -} -*/ - static int CmdHF15Raw(const char *Cmd) { - char cmdp = param_getchar(Cmd, 0); - if (strlen(Cmd) < 3 || cmdp == 'h' || cmdp == 'H') return usage_15_raw(); + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_raw(); - PacketResponseNG resp; int reply = 1, fast = 1, i = 0; - bool crc = false, leaveSignalON = false; + bool crc = false, keep_field_on = false; char buf[5] = ""; uint8_t data[100]; uint32_t datalen = 0, temp; @@ -1309,21 +1368,18 @@ static int CmdHF15Raw(const char *Cmd) { while (Cmd[i] != '\0') { if (Cmd[i] == ' ' || Cmd[i] == '\t') { i++; continue; } if (Cmd[i] == '-') { - switch (Cmd[i + 1]) { + switch (tolower(Cmd[i + 1])) { case 'r': - case 'R': reply = 0; break; case '2': fast = 0; break; case 'c': - case 'C': crc = true; break; - case 'p': - case 'P': - leaveSignalON = true; + case 'k': + keep_field_on = true; break; default: PrintAndLogEx(WARNING, "Invalid option"); @@ -1356,20 +1412,25 @@ static int CmdHF15Raw(const char *Cmd) { datalen += 2; } + PacketResponseNG resp; clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, datalen, fast, reply, data, datalen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, datalen, fast, reply, data, datalen); if (reply) { if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - uint8_t len = resp.oldarg[0]; - PrintAndLogEx(INFO, "received %i octets", len); - PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp.data.asBytes, len)); + int len = resp.oldarg[0]; + if (len < 2) { + PrintAndLogEx(WARNING, "command failed"); + } else { + PrintAndLogEx(SUCCESS, "received %i octets", len); + PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp.data.asBytes, len)); + } } else { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply"); } } - if (!leaveSignalON) + if (keep_field_on == false) DropField(); return PM3_SUCCESS; @@ -1381,30 +1442,32 @@ static int CmdHF15Raw(const char *Cmd) { */ static int CmdHF15Readmulti(const char *Cmd) { - char cmdp = param_getchar(Cmd, 0); - if (strlen(Cmd) < 3 || cmdp == 'h' || cmdp == 'H') return usage_15_readmulti(); + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_readmulti(); - PacketResponseNG resp; - uint8_t *recv; uint8_t req[PM3_CMD_DATA_SIZE] = {0}; uint16_t reqlen = 0; - uint8_t arg1 = 1; - uint8_t pagenum, pagecount; + uint8_t fast = 1; + uint8_t reply = 1; + char cmdbuf[100] = {0}; char *cmd = cmdbuf; strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); - if (!prepareHF15Cmd(&cmd, &reqlen, &arg1, req, ISO15_CMD_READMULTI)) + if (!prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_READMULTI)) return PM3_SUCCESS; // add OPTION flag, in order to get lock-info req[0] |= ISO15_REQ_OPTION; // decimal - pagenum = param_get8ex(cmd, 0, 0, 10); - pagecount = param_get8ex(cmd, 1, 0, 10); + uint8_t pagenum = param_get8ex(cmd, 0, 0, 10); + uint8_t pagecount = param_get8ex(cmd, 1, 0, 10); - // PrintAndLogEx(NORMAL, "ice %d %d\n", pagenum, pagecount); + if (pagecount > 6) { + PrintAndLogEx(WARNING, "Page count must be 6 or less (%d)", pagecount); + return PM3_EINVARG; + } // 0 means 1 page, // 1 means 2 pages, ... @@ -1415,32 +1478,33 @@ static int CmdHF15Readmulti(const char *Cmd) { AddCrc15(req, reqlen); reqlen += 2; + PacketResponseNG resp; clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(FAILED, "iso15693 card select failed"); + PrintAndLogEx(FAILED, "iso15693 card timeout"); DropField(); return PM3_ETIMEOUT; } DropField(); - uint32_t status = resp.oldarg[0]; + int status = resp.oldarg[0]; if (status < 2) { - PrintAndLogEx(FAILED, "iso15693 card select failed"); + PrintAndLogEx(FAILED, "iso15693 card readmulti failed"); return PM3_EWRONGANSWER; } - recv = resp.data.asBytes; + uint8_t *data = resp.data.asBytes; - if (!CheckCrc15(recv, status)) { - PrintAndLogEx(FAILED, "CRC failed"); + if (CheckCrc15(data, status) == false) { + PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); return PM3_ESOFT; } - if (recv[0] & ISO15_RES_ERROR) { - PrintAndLogEx(FAILED, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + if ((data[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { + PrintAndLogEx(FAILED, "iso15693 card returned error %i: %s", data[0], TagErrorStr(data[0])); return PM3_EWRONGANSWER; } @@ -1448,12 +1512,14 @@ static int CmdHF15Readmulti(const char *Cmd) { int start = 1; int stop = (pagecount + 1) * 5; int currblock = pagenum; - // print response + + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "block# | data |lck| ascii"); PrintAndLogEx(NORMAL, "---------+--------------+---+----------"); + for (int i = start; i < stop; i += 5) { - PrintAndLogEx(NORMAL, "%3d/0x%02X | %s | %d | %s", currblock, currblock, sprint_hex(recv + i + 1, 4), recv[i], sprint_ascii(recv + i + 1, 4)); + PrintAndLogEx(NORMAL, "%3d/0x%02X | %s | %d | %s", currblock, currblock, sprint_hex(data + i + 1, 4), data[i], sprint_ascii(data + i + 1, 4)); currblock++; } @@ -1466,11 +1532,8 @@ static int CmdHF15Readmulti(const char *Cmd) { */ static int CmdHF15Read(const char *Cmd) { - char cmdp = param_getchar(Cmd, 0); - if (strlen(Cmd) < 3 || cmdp == 'h' || cmdp == 'H') return usage_15_read(); - - PacketResponseNG resp; - uint8_t *recv; + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_read(); // arg: len, speed, recv? // arg0 (datalen, cmd len? .arg0 == crc?) @@ -1478,13 +1541,14 @@ static int CmdHF15Read(const char *Cmd) { // arg2 (recv == 1 == expect a response) uint8_t req[PM3_CMD_DATA_SIZE] = {0}; uint16_t reqlen = 0; - uint8_t arg1 = 1; + uint8_t fast = 1; + uint8_t reply = 1; int blocknum; char cmdbuf[100] = {0}; char *cmd = cmdbuf; strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); - if (!prepareHF15Cmd(&cmd, &reqlen, &arg1, req, ISO15_CMD_READ)) + if (prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_READ) == false) return PM3_SUCCESS; // add OPTION flag, in order to get lock-info @@ -1497,32 +1561,33 @@ static int CmdHF15Read(const char *Cmd) { AddCrc15(req, reqlen); reqlen += 2; + PacketResponseNG resp; clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(ERR, "iso15693 card select failed"); + PrintAndLogEx(ERR, "iso15693 timeout"); DropField(); return PM3_ETIMEOUT; } DropField(); - uint32_t status = resp.oldarg[0]; + int status = resp.oldarg[0]; if (status < 2) { - PrintAndLogEx(ERR, "iso15693 card select failed"); + PrintAndLogEx(ERR, "iso15693 command failed"); return PM3_EWRONGANSWER; } - recv = resp.data.asBytes; + uint8_t *data = resp.data.asBytes; - if (!CheckCrc15(recv, status)) { - PrintAndLogEx(ERR, "CRC failed"); + if (CheckCrc15(data, status) == false) { + PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); return PM3_ESOFT; } - if (recv[0] & ISO15_RES_ERROR) { - PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + if ((data[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { + PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", data[0], TagErrorStr(data[0])); return PM3_EWRONGANSWER; } @@ -1530,7 +1595,7 @@ static int CmdHF15Read(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "block #%3d |lck| ascii", blocknum); PrintAndLogEx(NORMAL, "------------+---+------"); - PrintAndLogEx(NORMAL, "%s| %d | %s", sprint_hex(recv + 2, status - 4), recv[1], sprint_ascii(recv + 2, status - 4)); + PrintAndLogEx(NORMAL, "%s| %d | %s", sprint_hex(data + 2, status - 4), data[1], sprint_ascii(data + 2, status - 4)); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -1541,22 +1606,22 @@ static int CmdHF15Read(const char *Cmd) { */ static int CmdHF15Write(const char *Cmd) { - char cmdp = param_getchar(Cmd, 0); - if (strlen(Cmd) < 3 || cmdp == 'h' || cmdp == 'H') return usage_15_write(); + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 3 || cmdp == 'h') return usage_15_write(); - PacketResponseNG resp; - uint8_t *recv; uint8_t req[PM3_CMD_DATA_SIZE] = {0}; uint16_t reqlen = 0; - uint8_t arg1 = 1; + uint8_t fast = 1; + uint8_t reply = 1; int pagenum, temp; + char cmdbuf[100] = {0}; char *cmd = cmdbuf; char *cmd2; strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); - if (!prepareHF15Cmd(&cmd, &reqlen, &arg1, req, ISO15_CMD_WRITE)) + if (prepareHF15Cmd(&cmd, &reqlen, &fast, req, ISO15_CMD_WRITE) == false) return PM3_SUCCESS; // *cmd -> page num ; *cmd2 -> data @@ -1583,8 +1648,9 @@ static int CmdHF15Write(const char *Cmd) { PrintAndLogEx(INFO, "iso15693 writing to page %02d (0x%02X) | data ", pagenum, pagenum); + PacketResponseNG resp; clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); + SendCommandMIX(CMD_HF_ISO15693_COMMAND, reqlen, fast, reply, req, reqlen); if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { PrintAndLogEx(FAILED, "iso15693 card timeout, data may be written anyway"); @@ -1594,25 +1660,25 @@ static int CmdHF15Write(const char *Cmd) { DropField(); - uint32_t status = resp.oldarg[0]; + int status = resp.oldarg[0]; if (status < 2) { - PrintAndLogEx(FAILED, "iso15693 card select failed"); + PrintAndLogEx(FAILED, "iso15693 command failed"); return PM3_EWRONGANSWER; } - recv = resp.data.asBytes; + uint8_t *data = resp.data.asBytes; - if (!CheckCrc15(recv, status)) { - PrintAndLogEx(FAILED, "CRC failed"); + if (CheckCrc15(data, status) == false) { + PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); return PM3_ESOFT; } - if (recv[0] & ISO15_RES_ERROR) { - PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + if ((data[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { + PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", data[0], TagErrorStr(data[0])); return PM3_EWRONGANSWER; } - PrintAndLogEx(NORMAL, "OK"); + PrintAndLogEx(SUCCESS, "Write " _GREEN_("OK")); return PM3_SUCCESS; } @@ -1635,7 +1701,7 @@ static int CmdHF15Restore(const char *Cmd) { sprintf(newPrefix, " %s", param); break; default: - PrintAndLogEx(WARNING, "11 Unknown parameter " _YELLOW_("'%s'"), param); + PrintAndLogEx(WARNING, "11 unknown parameter " _YELLOW_("'%s'"), param); return usage_15_restore(); } break; @@ -1658,39 +1724,39 @@ static int CmdHF15Restore(const char *Cmd) { case 'h': return usage_15_restore(); default: - PrintAndLogEx(WARNING, "Unknown parameter " _YELLOW_("'%c'"), param_getchar(Cmd, cmdp)); + PrintAndLogEx(WARNING, "unknown parameter " _YELLOW_("'%c'"), param_getchar(Cmd, cmdp)); return usage_15_restore(); } cmdp++; } - PrintAndLogEx(INFO, "Blocksize: %zu", blocksize); + PrintAndLogEx(INFO, "blocksize: %zu", blocksize); if (!strlen(filename)) { - PrintAndLogEx(WARNING, "Please provide a filename"); + PrintAndLogEx(WARNING, "please provide a filename"); return usage_15_restore(); } uint8_t uid[8] = {0x00}; - if (!getUID(uid)) { - PrintAndLogEx(WARNING, "No tag found"); + if (getUID(false, uid) == false) { + PrintAndLogEx(WARNING, "no tag found"); return PM3_ESOFT; } size_t datalen = 0; uint8_t *data = NULL; if (loadFile_safe(filename, ".bin", (void **)&data, &datalen) != PM3_SUCCESS) { - PrintAndLogEx(WARNING, "Could not find file " _YELLOW_("%s"), filename); + PrintAndLogEx(WARNING, "could not find file " _YELLOW_("%s"), filename); return PM3_EFILE; } if ((datalen % blocksize) != 0) { - PrintAndLogEx(WARNING, "Datalen %zu isn't dividable with blocksize %zu", datalen, blocksize); + PrintAndLogEx(WARNING, "datalen %zu isn't dividable with blocksize %zu", datalen, blocksize); free(data); return PM3_ESOFT; } - PrintAndLogEx(INFO, "Restoring data blocks."); + PrintAndLogEx(INFO, "restoring data blocks"); int retval = PM3_SUCCESS; size_t bytes = 0; @@ -1714,14 +1780,15 @@ static int CmdHF15Restore(const char *Cmd) { PrintAndLogEx(DEBUG, "hf 15 write %s", tmpCmd); for (tried = 0; tried < retries; tried++) { - if (!(retval = CmdHF15Write(tmpCmd))) { + retval = CmdHF15Write(tmpCmd); + if (retval == false) { break; } } if (tried >= retries) { free(data); - PrintAndLogEx(FAILED, "Restore failed. Too many retries."); + PrintAndLogEx(FAILED, "restore failed. Too many retries."); return retval; } bytes += blocksize; @@ -1729,7 +1796,7 @@ static int CmdHF15Restore(const char *Cmd) { } free(data); PrintAndLogEx(INFO, "done"); - PrintAndLogEx(HINT, "Try reading your card to verify with " _YELLOW_("`hf 15 dump`")); + PrintAndLogEx(HINT, "try `" _YELLOW_("hf 15 dump") "` to read your card to verify"); return PM3_SUCCESS; } @@ -1738,98 +1805,64 @@ static int CmdHF15Restore(const char *Cmd) { * Set UID for magic Chinese card */ static int CmdHF15CSetUID(const char *Cmd) { - uint8_t uid[8] = {0x00}; - uint8_t oldUid[8], newUid[8] = {0x00}; - PacketResponseNG resp; - int reply = 1, fast = 0; - uint8_t data[4][9] = {{0x00}}; char cmdp = tolower(param_getchar(Cmd, 0)); if (strlen(Cmd) < 1 || cmdp == 'h') return usage_15_csetuid(); - if (param_gethex(Cmd, 0, uid, 16)) { + + struct { + uint8_t uid[8]; + } PACKED payload; + + if (param_gethex(Cmd, 0, payload.uid, 16)) { PrintAndLogEx(WARNING, "UID must include 16 HEX symbols"); return PM3_EINVARG; } - if (uid[0] != 0xe0) { + if (payload.uid[0] != 0xE0) { PrintAndLogEx(WARNING, "UID must begin with the byte " _YELLOW_("E0")); return PM3_EINVARG; } - PrintAndLogEx(SUCCESS, "Input new UID | " _YELLOW_("%s"), iso15693_sprintUID(NULL, uid)); + PrintAndLogEx(SUCCESS, "reverse input UID " _YELLOW_("%s"), iso15693_sprintUID(NULL, payload.uid)); - if (!getUID(oldUid)) { - PrintAndLogEx(FAILED, "Can't get old/current UID."); + PrintAndLogEx(INFO, "getting current card details..."); + uint8_t carduid[8] = {0x00}; + if (getUID(false, carduid) == false) { + PrintAndLogEx(FAILED, "can't read card UID"); return PM3_ESOFT; } - PrintAndLogEx(INFO, "Using backdoor magic tag function"); + PrintAndLogEx(INFO, "updating tag uid..."); - // Command 1 : 02213E00000000 - data[0][0] = 0x02; - data[0][1] = 0x21; - data[0][2] = 0x3e; - data[0][3] = 0x00; - data[0][4] = 0x00; - data[0][5] = 0x00; - data[0][6] = 0x00; + PacketResponseNG resp; + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO15693_CSETUID, (uint8_t *)&payload, sizeof(payload)); - // Command 2 : 02213F69960000 - data[1][0] = 0x02; - data[1][1] = 0x21; - data[1][2] = 0x3f; - data[1][3] = 0x69; - data[1][4] = 0x96; - data[1][5] = 0x00; - data[1][6] = 0x00; - - // Command 3 : 022138u8u7u6u5 (where uX = uid byte X) - data[2][0] = 0x02; - data[2][1] = 0x21; - data[2][2] = 0x38; - data[2][3] = uid[7]; - data[2][4] = uid[6]; - data[2][5] = uid[5]; - data[2][6] = uid[4]; - - // Command 4 : 022139u4u3u2u1 (where uX = uid byte X) - data[3][0] = 0x02; - data[3][1] = 0x21; - data[3][2] = 0x39; - data[3][3] = uid[3]; - data[3][4] = uid[2]; - data[3][5] = uid[1]; - data[3][6] = uid[0]; - - for (int i = 0; i < 4; i++) { - AddCrc15(data[i], 7); - - clearCommandBuffer(); - SendCommandOLD(CMD_HF_ISO15693_COMMAND, sizeof(data[i]), fast, reply, data[i], sizeof(data[i])); - - if (reply) { - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - uint8_t len = resp.oldarg[0]; - PrintAndLogEx(INFO, "received %i octets", len); - PrintAndLogEx(INFO, "%s", sprint_hex(resp.data.asBytes, len)); - } else { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); - } - } + if (WaitForResponseTimeout(CMD_HF_ISO15693_CSETUID, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); } - if (!getUID(newUid)) { - PrintAndLogEx(FAILED, "Can't get new UID."); + PrintAndLogEx(INFO, "getting updated card details..."); + + if (getUID(false, carduid) == false) { + PrintAndLogEx(FAILED, "can't read card UID"); return PM3_ESOFT; } - if (memcmp(newUid, uid, 8) != 0) { - PrintAndLogEx(FAILED, "Setting UID on tag failed."); + // reverse cardUID to compare + uint8_t revuid[8] = {0}; + uint8_t i = 0; + while (i < sizeof(revuid)) { + revuid[i] = carduid[7 - i]; + i++; + } + + if (memcmp(revuid, payload.uid, 8) != 0) { + PrintAndLogEx(FAILED, "setting new UID (" _RED_("failed") ")"); return PM3_ESOFT; } else { - PrintAndLogEx(SUCCESS, "Old: %s", iso15693_sprintUID(NULL, oldUid)); - PrintAndLogEx(SUCCESS, "New: " _GREEN_("%s"), iso15693_sprintUID(NULL, newUid)); + PrintAndLogEx(SUCCESS, "setting new UID (" _GREEN_("ok") ")"); return PM3_SUCCESS; } } @@ -1841,9 +1874,8 @@ static command_t CommandTable[] = { {"demod", CmdHF15Demod, AlwaysAvailable, "Demodulate ISO15693 from tag"}, {"dump", CmdHF15Dump, IfPm3Iso15693, "Read all memory pages of an ISO15693 tag, save to file"}, {"info", CmdHF15Info, IfPm3Iso15693, "Tag information"}, -// {"sniff", CmdHF15Sniff, IfPm3Iso15693, "Sniff ISO15693 traffic"}, + {"sniff", CmdHF15Sniff, IfPm3Iso15693, "Sniff ISO15693 traffic"}, {"raw", CmdHF15Raw, IfPm3Iso15693, "Send raw hex data to tag"}, - {"record", CmdHF15Record, IfPm3Iso15693, "Record Samples (ISO15693)"}, {"read", CmdHF15Read, IfPm3Iso15693, "Read a block"}, {"reader", CmdHF15Reader, IfPm3Iso15693, "Act like an ISO15693 reader"}, {"readmulti", CmdHF15Readmulti, IfPm3Iso15693, "Reads multiple Blocks"}, @@ -1870,16 +1902,3 @@ int CmdHF15(const char *Cmd) { clearCommandBuffer(); return CmdsParse(CommandTable, Cmd); } - -// used with 'hf search' -bool readHF15Uid(bool verbose) { - uint8_t uid[8] = {0}; - if (!getUID(uid)) { - if (verbose) PrintAndLogEx(WARNING, "No tag found."); - return false; - } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), iso15693_sprintUID(NULL, uid)); - PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("%s"), getTagInfo_15(uid)); - return true; -} diff --git a/client/src/cmdhf15.h b/client/src/cmdhf15.h index 81530a58c..42e5e12c0 100644 --- a/client/src/cmdhf15.h +++ b/client/src/cmdhf15.h @@ -15,6 +15,6 @@ int CmdHF15(const char *Cmd); -bool readHF15Uid(bool verbose); +bool readHF15Uid(bool loop, bool verbose); #endif diff --git a/client/src/cmdhfcryptorf.c b/client/src/cmdhfcryptorf.c index 7f727d4bf..bf2df530c 100644 --- a/client/src/cmdhfcryptorf.c +++ b/client/src/cmdhfcryptorf.c @@ -19,6 +19,7 @@ #include "crc16.h" #include "cmdhf14a.h" #include "protocols.h" // definitions of ISO14B protocol +#include "iso14b.h" #define TIMEOUT 2000 static int CmdHelp(const char *Cmd); @@ -58,11 +59,10 @@ static int usage_hf_cryptorf_sniff(void) { return PM3_SUCCESS; } static int usage_hf_cryptorf_sim(void) { - PrintAndLogEx(NORMAL, "Emulating CryptoRF tag with 4 UID / PUPI\n" - "Usage: hf cryptorf sim [h] [u ]\n" + PrintAndLogEx(NORMAL, "Emulating CryptoRF tag with emulator memory\n" + "Usage: hf cryptorf sim [h] \n" "Options:\n" " h this help\n" - " u 4byte UID/PUPI\n" "\n" "Example:\n" _YELLOW_(" hf cryptorf sim") @@ -115,22 +115,21 @@ static int switch_off_field_cryptorf(void) { } static int CmdHFCryptoRFList(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - CmdTraceList("14b"); - return PM3_SUCCESS; + char args[128] = {0}; + if (strlen(Cmd) == 0) { + snprintf(args, sizeof(args), "-t cryptorf"); + } else { + strncpy(args, Cmd, sizeof(args) - 1); + } + return CmdTraceList(args); } static int CmdHFCryptoRFSim(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (cmdp == 'h') return usage_hf_cryptorf_sim(); - uint32_t pupi = 0; - if (cmdp == 'u') { - pupi = param_get32ex(Cmd, 1, 0, 16); - } - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_SIMULATE, pupi, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_CRYPTORF_SIM, 0, 0, 0, NULL, 0); return PM3_SUCCESS; } @@ -152,23 +151,7 @@ static bool get_14b_UID(iso14b_card_select_t *card) { int8_t retry = 3; PacketResponseNG resp; - // test for 14b SR - while (retry--) { - - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); - if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { - - uint8_t status = resp.oldarg[0]; - if (status == 0) { - memcpy(card, (iso14b_card_select_t *)resp.data.asBytes, sizeof(iso14b_card_select_t)); - return true; - } - } - } // retry - - // test 14b standard - retry = 3; + // test while (retry--) { clearCommandBuffer(); diff --git a/client/src/cmdhfepa.c b/client/src/cmdhfepa.c index b7c49bd83..83f347604 100644 --- a/client/src/cmdhfepa.c +++ b/client/src/cmdhfepa.c @@ -14,6 +14,7 @@ #include #include #include +#include // tolower #include "cmdparser.h" // command_t #include "commonutil.h" // ARRAYLEN @@ -23,8 +24,27 @@ static int CmdHelp(const char *Cmd); +static int usage_epa_collect(void) { + PrintAndLogEx(NORMAL, "Tries to collect nonces when doing part of PACE protocol.\n" + "\n" + "Usage: hf epa cnonces \n" + "Options:\n" + "\t nonce size\n" + "\t number of nonces to collect\n" + "\t delay between\n" + "\n" + "Example:\n" + _YELLOW_("\thf epa cnonces 4 4 1") + ); + return PM3_SUCCESS; +} + // Perform (part of) the PACE protocol static int CmdHFEPACollectPACENonces(const char *Cmd) { + + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 'h') return usage_epa_collect(); + // requested nonce size uint32_t m = 0; // requested number of Nonces @@ -40,13 +60,20 @@ static int CmdHFEPACollectPACENonces(const char *Cmd) { PrintAndLogEx(SUCCESS, "Collecting %u %u byte nonces", n, m); PrintAndLogEx(SUCCESS, "Start: %" PRIu64, msclock() / 1000); - // repeat n times + + struct p { + uint32_t m; + } PACKED payload; + payload.m = m; + for (uint32_t i = 0; i < n; i++) { // execute PACE - clearCommandBuffer(); - SendCommandMIX(CMD_HF_EPA_COLLECT_NONCE, (int)m, 0, 0, NULL, 0); + PacketResponseNG resp; - WaitForResponse(CMD_ACK, &resp); + clearCommandBuffer(); + SendCommandNG(CMD_HF_EPA_COLLECT_NONCE, (uint8_t *)&payload, sizeof(payload)); + + WaitForResponse(CMD_HF_EPA_COLLECT_NONCE, &resp); // check if command failed if (resp.oldarg[0] != 0) { @@ -65,6 +92,7 @@ static int CmdHFEPACollectPACENonces(const char *Cmd) { sleep(d); } } + PrintAndLogEx(SUCCESS, "End: %" PRIu64, msclock() / 1000); return PM3_SUCCESS; } @@ -155,6 +183,7 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_HF_EPA_REPLAY, 0, 0, 0, NULL, 0); WaitForResponse(CMD_ACK, &resp); + if (resp.oldarg[0] != 0) { PrintAndLogEx(SUCCESS, "\nPACE replay failed in step %u!", (uint32_t)resp.oldarg[0]); PrintAndLogEx(SUCCESS, "Measured times:"); diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index f62a9a8ff..083e69b21 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -21,7 +21,7 @@ #include "crc16.h" #include "util.h" #include "ui.h" -#include "mifare.h" // felica_card_select_t struct +#include "iso18.h" // felica_card_select_t struct #include "des.h" #define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len)) @@ -84,11 +84,11 @@ static int usage_hf_felica_dumplite(void) { } static int usage_hf_felica_raw(void) { - PrintAndLogEx(NORMAL, "Usage: hf felica raw [-h] [-r] [-c] [-p] [-a] <0A 0B 0C ... hex>"); + PrintAndLogEx(NORMAL, "Usage: hf felica raw [-h] [-r] [-c] [-k] [-a] <0A 0B 0C ... hex>"); PrintAndLogEx(NORMAL, " -h this help"); PrintAndLogEx(NORMAL, " -r do not read response"); PrintAndLogEx(NORMAL, " -c calculate and append CRC"); - PrintAndLogEx(NORMAL, " -p leave the signal field ON after receive"); + PrintAndLogEx(NORMAL, " -k keep signal field ON after receive"); PrintAndLogEx(NORMAL, " -a active signal field ON without select"); PrintAndLogEx(NORMAL, " -s active signal field ON with select"); return PM3_SUCCESS; @@ -407,9 +407,13 @@ static bool add_last_IDm(uint8_t position, uint8_t *data) { } static int CmdHFFelicaList(const char *Cmd) { - (void)Cmd; - CmdTraceList("felica"); - return PM3_SUCCESS; + char args[128] = {0}; + if (strlen(Cmd) == 0) { + snprintf(args, sizeof(args), "-t felica"); + } else { + strncpy(args, Cmd, sizeof(args) - 1); + } + return CmdTraceList(args); } static int CmdHFFelicaReader(const char *Cmd) { @@ -1630,27 +1634,30 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { uint8_t timeout = 0; while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { timeout++; - printf("."); + PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); if (kbd_enter_pressed()) { - PrintAndLogEx(WARNING, "aborted via keyboard!\n"); + PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); DropField(); return PM3_EOPABORTED; } - if (timeout > 100) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); + if (timeout > 10) { + PrintAndLogEx(WARNING, "\ntimeout while waiting for reply."); DropField(); return PM3_ETIMEOUT; } } + + PrintAndLogEx(NORMAL, ""); + if (resp.oldarg[0] == 0) { - PrintAndLogEx(WARNING, "\nButton pressed. Aborted."); + PrintAndLogEx(WARNING, "Button pressed, aborted"); return PM3_EOPABORTED; } uint32_t tracelen = resp.oldarg[1]; if (tracelen == 0) { - PrintAndLogEx(WARNING, "\nNo trace data! Maybe not a FeliCa Lite card?"); + PrintAndLogEx(WARNING, "No trace data! Maybe not a FeliCa Lite card?"); return PM3_ESOFT; } @@ -1684,7 +1691,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { static int CmdHFFelicaCmdRaw(const char *Cmd) { bool reply = 1; bool crc = false; - bool power = false; + bool keep_field_on = false; bool active = false; bool active_select = false; uint16_t numbits = 0; @@ -1711,8 +1718,8 @@ static int CmdHFFelicaCmdRaw(const char *Cmd) { case 'c': crc = true; break; - case 'p': - power = true; + case 'k': + keep_field_on = true; break; case 'a': active = true; @@ -1768,7 +1775,7 @@ static int CmdHFFelicaCmdRaw(const char *Cmd) { flags |= FELICA_NO_SELECT; } - if (power) { + if (keep_field_on) { flags |= FELICA_NO_DISCONNECT; } diff --git a/client/src/cmdhffelica.h b/client/src/cmdhffelica.h index 8d8156006..e7be7ede1 100644 --- a/client/src/cmdhffelica.h +++ b/client/src/cmdhffelica.h @@ -12,7 +12,7 @@ #define CMDHFFELICA_H__ #include "common.h" -#include "mifare.h" +#include "iso18.h" int CmdHFFelica(const char *Cmd); int readFelicaUid(bool verbose); diff --git a/client/src/cmdhffido.c b/client/src/cmdhffido.c index 50c1495c8..009a14e9f 100644 --- a/client/src/cmdhffido.c +++ b/client/src/cmdhffido.c @@ -36,10 +36,21 @@ #include "emv/dump.h" #include "ui.h" #include "cmdhf14a.h" +#include "cmdtrace.h" static int CmdHelp(const char *Cmd); -static int CmdHFFidoInfo(const char *cmd) { +static int cmd_hf_fido_list(const char *Cmd) { + char args[128] = {0}; + if (strlen(Cmd) == 0) { + snprintf(args, sizeof(args), "-t 14a"); + } else { + strncpy(args, Cmd, sizeof(args) - 1); + } + return CmdTraceList(args); +} + +static int cmd_hf_fido_info(const char *cmd) { if (cmd && strlen(cmd) > 0) PrintAndLogEx(WARNING, "WARNING: command doesn't have any parameters.\n"); @@ -150,7 +161,7 @@ static json_t *OpenJson(CLIParserContext *ctx, int paramnum, char *fname, void * return root; } -static int CmdHFFidoRegister(const char *cmd) { +static int cmd_hf_fido_register(const char *cmd) { uint8_t data[64] = {0}; int chlen = 0; uint8_t cdata[250] = {0}; @@ -160,17 +171,17 @@ static int CmdHFFidoRegister(const char *cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf fido reg", "Initiate a U2F token registration. Needs two 32-byte hash numbers. \nchallenge parameter (32b) and application parameter (32b).", - "Usage:\n\thf fido reg -> execute command with 2 parameters, filled 0x00\n" - "\thf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters" - "\thf fido reg -p s0 s1 -> execute command with plain parameters"); + "hf fido reg -> execute command with 2 parameters, filled 0x00\n" + "hf fido reg 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters\n" + "hf fido reg -p s0 s1 -> execute command with plain parameters"); void *argtable[] = { arg_param_begin, - arg_lit0("aA", "apdu", "show APDU requests and responses"), - arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"), - arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"), - arg_lit0("tT", "tlv", "Show DER certificate contents in TLV representation"), - arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."), + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"), + arg_lit0("p", "plain", "send plain ASCII to challenge and application parameters instead of HEX"), + arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"), + arg_str0("j", "json", "fido.json", "JSON input / output file name for parameters."), arg_str0(NULL, NULL, "", NULL), arg_str0(NULL, NULL, "", NULL), arg_param_end @@ -386,7 +397,7 @@ static int CmdHFFidoRegister(const char *cmd) { return PM3_SUCCESS; } -static int CmdHFFidoAuthenticate(const char *cmd) { +static int cmd_hf_fido_authenticate(const char *cmd) { uint8_t data[512] = {0}; uint8_t hdata[250] = {0}; bool public_key_loaded = false; @@ -397,20 +408,20 @@ static int CmdHFFidoAuthenticate(const char *cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf fido auth", "Initiate a U2F token authentication. Needs key handle and two 32-byte hash numbers. \nkey handle(var 0..255), challenge parameter (32b) and application parameter (32b).", - "Usage:\n\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n" - "\thf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f " + "hf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with 2 parameters, filled 0x00 and key handle\n" + "hf fido auth 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f " "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"); void *argtable[] = { arg_param_begin, - arg_lit0("aA", "apdu", "show APDU reqests and responses"), - arg_lit0("vV", "verbose", "show technical data"), - arg_lit0("pP", "plain", "send plain ASCII to challenge and application parameters instead of HEX"), + arg_lit0("a", "apdu", "show APDU reqests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_lit0("p", "plain", "send plain ASCII to challenge and application parameters instead of HEX"), arg_rem("default mode:", "dont-enforce-user-presence-and-sign"), - arg_lit0("uU", "user", "mode: enforce-user-presence-and-sign"), - arg_lit0("cC", "check", "mode: check-only"), - arg_str0("jJ", "json", "fido.json", "JSON input / output file name for parameters."), - arg_str0("kK", "key", "public key to verify signature", NULL), + arg_lit0("u", "user", "mode: enforce-user-presence-and-sign"), + arg_lit0("c", "check", "mode: check-only"), + arg_str0("j", "json", "fido.json", "JSON input / output file name for parameters."), + arg_str0("k", "key", "public key to verify signature", NULL), arg_str0(NULL, NULL, "", NULL), arg_str0(NULL, NULL, "", NULL), arg_str0(NULL, NULL, "", NULL), @@ -652,22 +663,22 @@ static int GetExistsFileNameJson(const char *prefixDir, const char *reqestedFile return PM3_SUCCESS; } -static int CmdHFFido2MakeCredential(const char *cmd) { +static int cmd_hf_fido_2make_credential(const char *cmd) { json_error_t error; char fname[FILE_PATH_SIZE] = {0}; CLIParserContext *ctx; CLIParserInit(&ctx, "hf fido make", - "Execute a FIDO2 Make Credential command. Needs json file with parameters. Sample file " _YELLOW_("`fido2.json`") " in `resources/`.", - "Usage:\n\thf fido make -> execute command with default parameters file `fido2.json`\n" - "\thf fido make test.json -> execute command with parameters file `text.json`"); + "Execute a FIDO2 Make Credential command. Needs json file with parameters. Sample file `fido2.json` in `resources/`.", + "hf fido make -> execute command with default parameters file `fido2.json`\n" + "hf fido make test.json -> execute command with parameters file `text.json`"); void *argtable[] = { arg_param_begin, - arg_lit0("aA", "apdu", "show APDU reqests and responses"), - arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"), - arg_lit0("tT", "tlv", "Show DER certificate contents in TLV representation"), - arg_lit0("cC", "cbor", "show CBOR decoded data"), + arg_lit0("a", "apdu", "show APDU reqests and responses"), + arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"), + arg_lit0("t", "tlv", "Show DER certificate contents in TLV representation"), + arg_lit0("c", "cbor", "show CBOR decoded data"), arg_str0(NULL, NULL, "", "JSON input / output file name for parameters. Default `fido2.json`"), arg_param_end }; @@ -777,22 +788,22 @@ static int CmdHFFido2MakeCredential(const char *cmd) { return PM3_SUCCESS; } -static int CmdHFFido2GetAssertion(const char *cmd) { +static int cmd_hf_fido_2get_assertion(const char *cmd) { json_error_t error; char fname[FILE_PATH_SIZE] = {0}; CLIParserContext *ctx; CLIParserInit(&ctx, "hf fido assert", - "Execute a FIDO2 Get Assertion command. Needs json file with parameters. Sample file " _YELLOW_("`fido2.json`") " in `resources/`.", - "Usage:\n\thf fido assert -> execute command with default parameters file `fido2.json`\n" - "\thf fido assert test.json -l -> execute command with parameters file `text.json` and add to request CredentialId"); + "Execute a FIDO2 Get Assertion command. Needs json file with parameters. Sample file `fido2.json` in `resources/`.", + "hf fido assert -> execute command with default parameters file `fido2.json`\n" + "hf fido assert test.json -l -> execute command with parameters file `text.json` and add to request CredentialId"); void *argtable[] = { arg_param_begin, - arg_lit0("aA", "apdu", "show APDU reqests and responses"), - arg_litn("vV", "verbose", 0, 2, "show technical data. vv - show full certificates data"), - arg_lit0("cC", "cbor", "show CBOR decoded data"), - arg_lit0("lL", "list", "add CredentialId from json to allowList. Needs if `rk` option is `false` (authenticator doesn't store credential to its memory)"), + arg_lit0("a", "apdu", "show APDU reqests and responses"), + arg_litn("v", "verbose", 0, 2, "show technical data. vv - show full certificates data"), + arg_lit0("c", "cbor", "show CBOR decoded data"), + arg_lit0("l", "list", "add CredentialId from json to allowList. Needs if `rk` option is `false` (authenticator doesn't store credential to its memory)"), arg_str0(NULL, NULL, "", "JSON input / output file name for parameters. Default `fido2.json`"), arg_param_end }; @@ -903,13 +914,14 @@ static int CmdHFFido2GetAssertion(const char *cmd) { } static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help."}, - {"info", CmdHFFidoInfo, IfPm3Iso14443a, "Info about FIDO tag."}, - {"reg", CmdHFFidoRegister, IfPm3Iso14443a, "FIDO U2F Registration Message."}, - {"auth", CmdHFFidoAuthenticate, IfPm3Iso14443a, "FIDO U2F Authentication Message."}, - {"make", CmdHFFido2MakeCredential, IfPm3Iso14443a, "FIDO2 MakeCredential command."}, - {"assert", CmdHFFido2GetAssertion, IfPm3Iso14443a, "FIDO2 GetAssertion command."}, - {NULL, NULL, 0, NULL} + {"help", CmdHelp, AlwaysAvailable, "This help."}, + {"info", cmd_hf_fido_list, IfPm3Iso14443a, "List ISO 14443A history"}, + {"info", cmd_hf_fido_info, IfPm3Iso14443a, "Info about FIDO tag."}, + {"reg", cmd_hf_fido_register, IfPm3Iso14443a, "FIDO U2F Registration Message."}, + {"auth", cmd_hf_fido_authenticate, IfPm3Iso14443a, "FIDO U2F Authentication Message."}, + {"make", cmd_hf_fido_2make_credential, IfPm3Iso14443a, "FIDO2 MakeCredential command."}, + {"assert", cmd_hf_fido_2get_assertion, IfPm3Iso14443a, "FIDO2 GetAssertion command."}, + {NULL, NULL, 0, NULL} }; int CmdHFFido(const char *Cmd) { diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 501190170..28b710fe8 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -2,6 +2,7 @@ // Copyright (C) 2010 iZsh , Hagen Fritsch // Copyright (C) 2011 Gerhard de Koning Gans // Copyright (C) 2014 Midnitesnake & Andy Davies & Martin Holst Swende +// Copyright (C) 2019 piwi // Copyright (C) 2020 Iceman // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -13,9 +14,11 @@ #include "cmdhficlass.h" #include +#include "cliparser.h" #include "cmdparser.h" // command_t #include "commonutil.h" // ARRAYLEN #include "cmdtrace.h" +#include "cliparser.h" #include "util_posix.h" #include "comms.h" #include "des.h" @@ -33,6 +36,7 @@ #define ICLASS_KEYS_MAX 8 #define ICLASS_AUTH_RETRY 10 #define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin" +static uint8_t empty[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; static int CmdHelp(const char *Cmd); @@ -48,8 +52,8 @@ static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = { }; static int usage_hf_iclass_sim(void) { - PrintAndLogEx(NORMAL, "Simulate a iclass legacy/standard tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf iclass sim [h]