diff --git a/.github/ISSUE_TEMPLATE/checklist-for-release.md b/.github/ISSUE_TEMPLATE/checklist-for-release.md index 6fa2002f9..649281a4a 100644 --- a/.github/ISSUE_TEMPLATE/checklist-for-release.md +++ b/.github/ISSUE_TEMPLATE/checklist-for-release.md @@ -31,9 +31,13 @@ Run `tools/release_tests.sh` on: - [ ] Kali - [ ] Debian Stable - [ ] Debian Testing -- [ ] Ubuntu 22 +- [ ] Ubuntu 24.04 (LTS) +- [ ] Ubuntu 24.10 +- [ ] Ubuntu 25.04 - [ ] ParrotOS -- [ ] Fedora 37 +- [ ] Fedora 41 (till 2025-11-19) +- [ ] Fedora 42 (till 2026-05-13) +- [ ] Fedora 43 (till 2026-12-02) - [ ] OpenSuse Leap - [ ] OpenSuse Tumbleweed - [ ] OSX (MacPorts) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9ccf4b1a..73dcf3e25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,62 @@ 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... -## [Blue Ice][2025-03-25] +## [unreleased][unreleased] +- Changed `hf iclass wrbl` - replay behavior to use privilege escalation if the macs field is not passed empty(@antiklesys) +- Changed `hf iclass restore` - it now supports privilege escalation to restore card content using replay (@antiklesys) +- Fixed `hf 15 dump` - now reads sysinfo response correct (@iceman1001) +- Changed `make clean` - it now removes all __pycache__ folders (@iceman1001) +- Fixed `hf 15 readmulti` - fix block calculations (@iceman1001) +- Changed `mem load` - now handles UL-C and UL-AES dictionary files (@iceman1001) +- Changed `hf mfu sim` - now support UL-C simulation (@iceman1001) +- Added `!` - run system commands from inside the client. Potentially dangerous if running client as SUDO, SU, ROOT (@iceman1001) + +## [Daddy Iceman.4.20469][2025-06-16] +- Fixed edge case in fm11rf08s key recovery tools (@doegox) +- Removed `--par` from `lf em 4x70` commands. +- Changed `hf 14a info` - refactored code to be able to detect card technology across the client easier (@iceman1001) +- Changed `hf mf info` - now informs better if a different card technology is detected (@iceman1001) +- Changed `hf mf autopwn` - now exits if desfire is detected and limit attacks if mifare plus is detected (@iceman1001) +- Changed `hf mfp chk` - improved key handling and output (@iceman1001) +- Fix `hf mf dump` - added a check for keyfile to contain enough keys for card (@iceman1001) +- Fix `hf mf eview` - now viewing 2k, 4k cards doesn't get wrong background color (@iceman1001) +- Changed `hf mf info` - skip checking if it detects a MIFARE Ultralight family card (@iceman1001) +- Changed `hf mf rdsc` - it now addeds the used key to the output in the sector trailer (@iceman1001) +- Added the `PM3ULTIMATE` platform in the build / docs. *untested* (@iceman1001) +- Added fpga compilation for PM3ULTIMATE device (@n-hutton) +- Updated the ATR list (@iceman1001) +- Fixed fpga binary images to use fixed seed 2 (@n-hutton) +- Added `hf iclass sam --info` - option that returns sam specific details (@antiklesys) +- Changed `hf iclass sim -t 7` - implemented simulation that glitches key block responses (@antiklesys) +- Changed `hf iclass sim -t 6` - implemented simulation that glitches sio block (@antiklesys) +- Changed `hf iclass legbrute` - implemented multithreading support (@antiklesys) +- Changed `hf iclass legrec` - added a --sl option for further speed increase by tweaking the communication delays (@antiklesys) +- Changed `hf iclass legrec` - added a --fast option for further speed increase and automated AA2 block selection (@antiklesys) +- Changed `hf iclass legrec` - additional code optimizations gaining a ~147% speed increase (@antiklesys) +- Changed `hf iclass tear` - readability improvements for erase phase (@antiklesys) +- Changed `hf iclass legrec` - code optimizations gaining a ~8% speed increase (@antiklesys) +- Modified `hf iclass tear` - now has a device side implementation also. (@antiklesys) (@iceman1001) +- Changed `hf iclass info` - now uses CSN values based checks (@antiklesys) +- Changed `hf iclass dump` - now uses default AA1 key when called without a key or key index (@iceman1001) +- Renamed `hf iclass trbl` to `hf iclass tear` (@iceman1001) +- Changed `hw tearoff` - the device side message is now debug log controlled (@iceman1001) +- Changed `pm3.sh` - Serial ports enumeration on Proxspace3.xx / MINGW environments, now using powershell.exe since wmic is deprecated (@iceman1001) +- Fixed `hf iclass trbl` - to correctly use the credit key when passed and show partial tearoff results (@antiklesys) +- Fixed `hf iclass legbrute` was not correctly parsing the index value +- Fixed `hf mf ekeyprn` - failed to download emulator memory due to wrong size calculation (@iceman1001) +- Fixed `hf mf fchk --mem` to actually use flash dict (@doegox) +- Fixed `make install` on OSX thanks DaveItsLong (@doegox) +- Added new standalone mode `HF_ST25_TEAROFF` to store/restore ST25TB tags with tearoff for counters (@seclabz) +- Added `hf_mfu_ultra.lua` script enables restoring dump to ULTRA/UL-5 tags and clearing previously written ULTRA tags (@mak-42) +- Fixed `hf mfu sim` to make persistent the counter increases in the emulator memory (@sup3rgiu) +- Fixed `hf mf mad` to correctly display MAD version 2 card publisher sector (@BIOS9) +- Fixed `lf hitag dump` and related commands stability when tag is configured in public mode/TTF mode (@rfidgeek1337) + +## [Blue Ice.4.20142][2025-03-25] - Added `des_talk.py` script for easier MIFARE DESFire handling (@trigat) - Fixed `hf 14b info` - wrong endianess when looking for lock bits etc (@gentilkiwi) - Changed `hf mf autopwn` - tries to detect static encrypted nonces and also user cancel during chk keys (@iceman1001) -- Added option to `hf mf autopwn` to use SPI flash dictionary (@jmichelp) +- Changed `hf mf autopwn` - added option to use SPI flash dictionary (@jmichelp) - Changed `trace list -t seos` - now annotate ISO7816 (@iceman1001) - Updated aid and mad json files (@iceman1001) - Changed `hf 14a apdu` - now can be interrupted and dynamically adds time (@iceman1001) @@ -38,9 +89,9 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf mf cload` - now accepts MFC Ev1 sized dumps (@iceman1001) - Changed `hf mfu info` - now properly identify ULEv1 AES 50pF (@iceman1001) - Changed `hf mf info` - now differentiates between full USCUID and cut down ZUID chips (@nvx) -- Changed `lf hitag chk` - added key counter, client side abort and minor delay (@iceman1001) +- Changed `lf hitag chk` - added key counter, client side abort and minor delay (@iceman1001) - Added `hf seos sam` - Added support for HID SAM SEOS communications (@jkramarz) -- Changed (extended) area accessible by spiffs into last page of FLASH (@piotrva) +- Changed the extended area accessible by spiffs into last page of FLASH (@piotrva) - Changed flash-stored key dictionaries (Mifare, iClass, T55XX) and T55XX configurations to SPIFFS files (@piotrva) - Changed `lf em 410x sim` to use default gap value of 0 and extended help (@piotrva) - Changed `hf 14a info` - now identifies MIAFRE Duox (@iceman1001) @@ -54,13 +105,14 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed extended area for Mifare keys in SPI flash to hold 4095 keys (@piotrva) - Fixed DESFire D40 secure channel crypto (@nvx) - Fixed `hf mfp info` fix signature check on 4b UID cards (@doegox) -- Automatically set maximum read/write block when using predefined types in `hf_mf_ultimatecard` script (@piotrva) -- Changed SPI flash detection to calculate the size instead of table lookup, updated spi_flash_decode.py script with more ICs (@ANTodorov) +- Changed `hf_mf_ultimatecard` - it now automatically set maximum read/write block when using predefined types (@piotrva) +- Changed SPI flash detection to calculate the size instead of table lookup (@ANTodorov) +- Changed `spi_flash_decode.py` script with more ICs (@ANTodorov) - Fixed `hf/lf tune` segfault when called from script (@doegox) -- Added option to set and get maximum read/write block number using `hf_mf_ultimatecard` script (@piotrva) +- Changed `hf_mf_ultimatecard` - added option to set and get maximum read/write block number (@piotrva) - Added JEDEC information for SPI flash W25Q64JV (@ANTodorov) -- Added special iclass legacy config cards in `hf iclass configcard` (@antiklesys) -- Added simulation function to `hf iclass legrec` (@antiklesys) +- Changed `hf iclass configcard` - added special iclass legacy config cards (@antiklesys) +- Changed `hf iclass legrec` - added simulation function (@antiklesys) - Added keys from Momentum firmware projects. (@onovy) - Added Dutch Statistics Agency default key (@eagle00789) - Fixed Wiegand decode with hex input dropping the first bit (@emilyastranova) diff --git a/Makefile b/Makefile index ce4f7aa7f..c985246d5 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,9 @@ endif all clean install uninstall check: %: client/% bootrom/% armsrc/% recovery/% mfc_card_only/% mfc_card_reader/% mfd_aes_brute/% fpga_compress/% cryptorf/% # hitag2crack toolsuite is not yet integrated in "all", it must be called explicitly: "make hitag2crack" #all clean install uninstall check: %: hitag2crack/% +clean: %: hitag2crack/% + find . -type d -name __pycache__ -exec rm -rfv \{\} + + INSTALLTOOLS=mfc/pm3_eml2lower.sh mfc/pm3_eml2upper.sh mfc/pm3_mfdread.py mfc/pm3_mfd2eml.py mfc/pm3_eml2mfd.py pm3_amii_bin2eml.pl pm3_reblay-emulating.py pm3_reblay-reading.py INSTALLSIMFW=sim011.bin sim011.sha512.txt sim013.bin sim013.sha512.txt sim014.bin sim014.sha512.txt @@ -204,7 +207,7 @@ help: @echo "+ fpga_compress - Make tools/fpga_compress" @echo @echo "+ style - Apply some automated source code formatting rules" - @echo "+ commands - Regenerate commands documentation files and autocompletion data + @echo "+ commands - Regenerate commands documentation files and autocompletion data" @echo "+ check - Run offline tests. Set CHECKARGS to pass arguments to the test script" @echo "+ .../check - Run offline tests against specific target. See above." @echo "+ miscchecks - Detect various encoding issues in source code" @@ -264,8 +267,11 @@ ifeq ($(PLATFORM_CHANGED),true) $(Q)$(MAKE) --no-print-directory -C bootrom clean $(Q)$(MAKE) --no-print-directory -C armsrc clean $(Q)$(MAKE) --no-print-directory -C recovery clean - $(Q)$(MAKE) --no-print-directory -C client clean $(Q)$(MAKE) --no-print-directory -C tools/fpga_compress clean +# clean the client only if PLATFORM got changed from or to PM3ICOPYX +ifeq (PM3ICOPYX,$(filter PM3ICOPYX, $(PLATFORM) $(CACHED_PLATFORM))) + $(Q)$(MAKE) --no-print-directory -C client clean +endif $(Q)$(ECHO) CACHED_PLATFORM=$(PLATFORM) > .Makefile.options.cache $(Q)$(ECHO) CACHED_PLATFORM_EXTRAS=$(PLATFORM_EXTRAS) >> .Makefile.options.cache $(Q)$(ECHO) CACHED_PLATFORM_DEFS=$(PLATFORM_DEFS) >> .Makefile.options.cache @@ -366,10 +372,12 @@ release: @echo "# - Release Tag: $(VERSION)" @echo "# - Release Name: $(RELEASE_NAME)" # - Removing -Werror... - @find . \( -path "./Makefile.defs" -or -path "./client/Makefile" -or -path "./common_arm/Makefile.common" -or -path "./tools/hitag2crack/*/Makefile" \) -exec sed -i 's/ -Werror//' {} \; - @find . \( -path "./client/deps/*.cmake" -or -path "./client/CMakeLists.txt" \) -exec sed -i 's/ -Werror//' {} \; + @find . \( -path "./Makefile.defs" -or -path "./client/Makefile" -or -path "./common_arm/Makefile.common" -or -path "./tools/hitag2crack/*/Makefile" -or -path "./client/deps/*/Makefile" \) -exec sed -i 's/ -Werror//' {} \; + @find . \( -path "./client/deps/*.cmake" -or -path "./client/CMakeLists.txt" -or -path "./client/experimental_lib/CMakeLists.txt" \) -exec sed -i 's/ -Werror//' {} \; # - Changing banner... + @sed -i "s/^#define BANNERMSG2 .*/#define BANNERMSG2 \" -----------------------------------\"/" client/src/proxmark3.c @sed -i "s/^#define BANNERMSG3 .*/#define BANNERMSG3 \"Release $(VERSION) - $(RELEASE_NAME)\"/" client/src/proxmark3.c + @echo -n "# ";grep "^#define BANNERMSG2" client/src/proxmark3.c @echo -n "# ";grep "^#define BANNERMSG3" client/src/proxmark3.c # - Committing temporarily... @git commit -a -m "Release $(VERSION) - $(RELEASE_NAME)" diff --git a/Makefile.defs b/Makefile.defs index 0d6066489..2496057fa 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -112,8 +112,8 @@ ifeq ($(DEBUG),1) DEFCFLAGS = -g -O0 -fstrict-aliasing -pipe DEFLDFLAGS = else - DEFCXXFLAGS = -Wall -O3 -pipe - DEFCFLAGS = -Wall -O3 -fstrict-aliasing -pipe + DEFCXXFLAGS = -Wall -Werror -O3 -pipe + DEFCFLAGS = -Wall -Werror -O3 -fstrict-aliasing -pipe DEFLDFLAGS = endif diff --git a/Makefile.platform.sample b/Makefile.platform.sample index 2e1221cb7..fb5114f5f 100644 --- a/Makefile.platform.sample +++ b/Makefile.platform.sample @@ -14,6 +14,9 @@ PLATFORM=PM3RDV4 #PLATFORM=PM3ICOPYX #PLATFORM_EXTRAS=FLASH +# For PM3 Ultimate: +# uncomment the line below +#PLATFORM=PM3ULTIMATE # If you want more than one PLATFORM_EXTRAS option, separate them by spaces: #PLATFORM_EXTRAS=BTADDON @@ -27,6 +30,10 @@ PLATFORM=PM3RDV4 # Only available with PLATFORM=PM3GENERIC #LED_ORDER=PM3EASY +# Uncomment a line below to change default USART baud rate +# defaults to 115200 used by HC-05 in Blueshark +#USART_BAUD_RATE=19200 + # Uncomment the lines below in order to make a 256KB image # and comment out the lines above diff --git a/README.md b/README.md index 64ae86137..5efcfc5d7 100644 --- a/README.md +++ b/README.md @@ -96,12 +96,14 @@ We define generic Proxmark3 platforms as following devices. - **Note**: currently incompatible with iCopy-X GUI as Proxmark client commands using different syntax - **Note**: see also [icopyx-community repos](https://github.com/iCopy-X-Community/) for upstream sources, reversed hw etc. - **Note**: Uses DRM to lock down tags, ignores the open source licences. Use on your own risk. +- ⚠ Proxmark3 Ultimate + - **Note**: unknown device hw + - **Note**: FPGA images is building for it. Use on your own risk. **Unknown support status** - ⚠ VX - **Note**: unknown device hw -- ⚠ Proxmark3 Ultimate - - **Note**: unknown device hw + When it comes to these new unknown models we are depending on the community to report in if this repo works and what they did to make it work. @@ -180,10 +182,11 @@ We usually merge your contributions fast since we do like the idea of getting a The [public roadmap](https://github.com/RfidResearchGroup/proxmark3/wiki/Public-Roadmap) is an excellent start to read if you are interesting in contributing. -## Supported operative systems +## Supported operating systems This repo compiles nicely on - WSL1 on Windows 10 + - WSL2 on Windows 10/11 - Proxspace environment [release v3.xx](https://github.com/Gator96100/ProxSpace/releases) - Windows/MinGW environment - Ubuntu, ParrotOS, Gentoo, Pentoo, Kali, NetHunter, Arch Linux, Fedora, Debian, Raspbian diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 26af3af0d..b492b4205 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -121,7 +121,7 @@ void BigBuf_Clear_ext(bool verbose) { memset(BigBuf, 0, s_bigbuf_size); clear_trace(); if (verbose) { - Dbprintf("Buffer cleared (%i bytes)", s_bigbuf_size); + if (g_dbglevel >= DBG_ERROR) Dbprintf("Buffer cleared (%i bytes)", s_bigbuf_size); } } diff --git a/armsrc/BigBuf.h b/armsrc/BigBuf.h index 2e905a45c..366b7c802 100644 --- a/armsrc/BigBuf.h +++ b/armsrc/BigBuf.h @@ -23,8 +23,8 @@ #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 MAX_MIFARE_FRAME_SIZE 19 // biggest Mifare frame is UL AES answer to AUTH (1 + 16 Bytes) + 2 Bytes CRC +#define MAX_MIFARE_PARITY_SIZE 3 // need 19 parity bits for the 19 Byte above. 3 Bytes are enough to store these #define CARD_MEMORY_SIZE 4096 // For now we're storing FM11RF08S nonces in the upper 1k of CARD_MEMORY_SIZE // but we might have to allocate extra space if one day we've to support sth like a FM11RF32S diff --git a/armsrc/Makefile b/armsrc/Makefile index 0f83596be..9929b3ae3 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -186,7 +186,7 @@ showinfo: # version_pm3.c should be checked on every time fullimage.stage1.elf should be remade version_pm3.c: default_version_pm3.c $(OBJDIR)/fpga_version_info.o $(OBJDIR)/fpga_all.o $(THUMBOBJ) $(ARMOBJ) .FORCE $(info [-] CHECK $@) - $(Q)$(CP) $< $@ + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ fpga_version_info.c: $(FPGA_BITSTREAMS) $(FPGA_COMPRESSOR) $(info [-] GEN $@) diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index 91d87dd14..b521da878 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -119,6 +119,9 @@ define KNOWN_STANDALONE_DEFINITIONS | HF_REBLAY | 14A Relay over BT | | (RDV4 only) | - Salvador Mendoza | +----------------------------------------------------------+ +| HF_ST25_TEAROFF | Store/restore ST25TB tags with | +| | tear-off for counters - SecLabz | ++----------------------------------------------------------+ | HF_TCPRST | IKEA Rothult read/sim/dump/emul | | | - Nick Draffen | +----------------------------------------------------------+ @@ -139,11 +142,11 @@ endef STANDALONE_MODES := LF_SKELETON STANDALONE_MODES += LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_MULTIHID LF_NEDAP_SIM LF_NEXID LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE -STANDALONE_MODES += HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_15SIM HF_AVEFUL HF_BOG HF_CARDHOPPER HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_TCPRST HF_TMUDFORD HF_UNISNIFF HF_YOUNG +STANDALONE_MODES += HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_15SIM HF_AVEFUL HF_BOG HF_CARDHOPPER HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_ST25_TEAROFF HF_TCPRST HF_TMUDFORD HF_UNISNIFF HF_YOUNG STANDALONE_MODES += DANKARMULTI STANDALONE_MODES_REQ_BT := HF_CARDHOPPER HF_REBLAY STANDALONE_MODES_REQ_SMARTCARD := -STANDALONE_MODES_REQ_FLASH := LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_LEGICSIM HF_MFCSIM +STANDALONE_MODES_REQ_FLASH := LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_LEGICSIM HF_MFCSIM HF_ST25_TEAROFF ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE) ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),) diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index 5873c0aff..d4c858e8b 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -157,6 +157,10 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_YOUNG,$(APP_CFLAGS))) SRC_STANDALONE = hf_young.c endif +# WITH_STANDALONE_HF_ST25_TEAROFF +ifneq (,$(findstring WITH_STANDALONE_HF_ST25_TEAROFF,$(APP_CFLAGS))) + SRC_STANDALONE = hf_st25_tearoff.c +endif ifneq (,$(findstring WITH_STANDALONE_DANKARMULTI,$(APP_CFLAGS))) SRC_STANDALONE = dankarmulti.c diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c index 626aa9d17..d634f819c 100644 --- a/armsrc/Standalone/hf_aveful.c +++ b/armsrc/Standalone/hf_aveful.c @@ -157,7 +157,7 @@ void RunMod(void) { if (button_pressed != BUTTON_NO_CLICK || data_available()) break; else if (state == STATE_SEARCH) { - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, true) == 0) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelay(500); @@ -246,7 +246,7 @@ void RunMod(void) { FLAG_SET_UID_IN_DATA(flags, 7); Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); - SimulateIso14443aTag(7, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(7, flags, card.uid, 0, NULL, 0, false, false); // Go back to search state if user presses pm3-button state = STATE_SEARCH; diff --git a/armsrc/Standalone/hf_cardhopper.c b/armsrc/Standalone/hf_cardhopper.c index ed6e660bf..d199dcdab 100644 --- a/armsrc/Standalone/hf_cardhopper.c +++ b/armsrc/Standalone/hf_cardhopper.c @@ -59,7 +59,7 @@ static const uint8_t magicCARD[4] = "CARD"; static const uint8_t magicEND [4] = "\xff" "END"; static const uint8_t magicRSRT[7] = "RESTART"; static const uint8_t magicERR [4] = "\xff" "ERR"; -static uint8_t magicACK [1] = "\xfe"; // is constant, but must be passed to API that doesn't like that +static const uint8_t magicACK [1] = "\xfe"; // Forward declarations static void become_reader(void); @@ -72,7 +72,7 @@ static bool try_use_canned_response(const uint8_t *, int, tag_response_info_t *) static void reply_with_packet(packet_t *); static void read_packet(packet_t *); -static void write_packet(packet_t *); +static void write_packet(const packet_t *); static bool GetIso14443aCommandFromReaderInterruptible(uint8_t *, uint16_t, uint8_t *, int *); @@ -146,7 +146,7 @@ static void become_reader(void) { packet_t packet = { 0 }; packet_t *rx = &packet; packet_t *tx = &packet; - uint8_t toCard[256] = { 0 }; + uint8_t toCard[MAX_FRAME_SIZE] = { 0 }; uint8_t parity[MAX_PARITY_SIZE] = { 0 }; while (1) { @@ -178,11 +178,15 @@ static void become_reader(void) { AddCrc14A(toCard, rx->len); ReaderTransmit(toCard, rx->len + 2, NULL); - tx->len = ReaderReceive(tx->dat, sizeof(tx->dat), parity); - if (tx->len == 0) { + // read to toCard instead of tx->dat directly to allow the extra byte for the CRC + uint16_t fromCardLen = ReaderReceive(toCard, sizeof(toCard), parity); + if (fromCardLen <= 2) { tx->len = sizeof(magicERR); memcpy(tx->dat, magicERR, sizeof(magicERR)); - } else tx->len -= 2; // cut off the CRC + } else { + tx->len = fromCardLen - 2; // cut off the CRC + memcpy(tx->dat, toCard, tx->len); + } write_packet(tx); } @@ -229,14 +233,15 @@ static void become_card(void) { tag_response_info_t *canned; uint32_t cuid; - uint32_t counters[3] = { 0 }; - uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; uint8_t pages; - SimulateIso14443aInit(tagType, flags, data, NULL, 0, &canned, &cuid, counters, tearings, &pages); + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &canned, &cuid, &pages, NULL) == false) { + DbpString(_RED_("Error initializing the emulation process!")); + return; + } DbpString(_CYAN_("[@]") " Setup done - entering emulation loop"); int fromReaderLen; - uint8_t fromReaderDat[256] = { 0 }; + uint8_t fromReaderDat[MAX_FRAME_SIZE] = { 0 }; uint8_t parity[MAX_PARITY_SIZE] = { 0 }; packet_t packet = { 0 }; packet_t *tx = &packet; @@ -277,8 +282,14 @@ static void become_card(void) { memcpy(tx->dat, fromReaderDat, tx->len); write_packet(tx); + if (no_reply) { + // since the RATS reply has already been sent waiting here will can result in missing the next reader command + // if we do get a reply later on while waiting for the next reader message it will be safely ignored + continue; + } + read_packet(rx); - if (!no_reply && rx->len > 0) { + if (rx->len > 0) { reply_with_packet(rx); } } @@ -344,7 +355,13 @@ static void cook_ats(packet_t *ats, uint8_t fwi, uint8_t sfgi) { uint8_t orig_t0 = ats->dat[1]; // Update FSCI in T0 from the received ATS - t0 |= orig_t0 & 0x0F; + uint8_t fsci = orig_t0 & 0x0F; + if (fsci > 8) { + // our packet length maxes out at 255 bytes, an FSCI of 8 requires 256 bytes + // but since we drop the 2 byte CRC16 we're safe capping this at 8 + fsci = 8; + } + t0 |= fsci; uint8_t len = ats->len - 2; uint8_t *orig_ats_ptr = &ats->dat[2]; @@ -449,20 +466,12 @@ static bool try_use_canned_response(const uint8_t *dat, int len, tag_response_in } -static uint8_t g_responseBuffer [512 ] = { 0 }; -static uint8_t g_modulationBuffer[1024] = { 0 }; +static uint8_t g_responseBuffer [MAX_FRAME_SIZE] = { 0 }; static void reply_with_packet(packet_t *packet) { - tag_response_info_t response = { 0 }; - response.response = g_responseBuffer; - response.modulation = g_modulationBuffer; - - memcpy(response.response, packet->dat, packet->len); - AddCrc14A(response.response, packet->len); - response.response_n = packet->len + 2; - - prepare_tag_modulation(&response, sizeof(g_modulationBuffer)); - EmSendPrecompiledCmd(&response); + memcpy(g_responseBuffer, packet->dat, packet->len); + AddCrc14A(g_responseBuffer, packet->len); + EmSendCmd(g_responseBuffer, packet->len + 2); } @@ -496,19 +505,27 @@ static void read_packet(packet_t *packet) { // clear any remaining buffered data while (cardhopper_data_available()) { - cardhopper_read(packet->dat, 255); + cardhopper_read(packet->dat, sizeof(packet->dat)); } packet->len = 0; return; } } - cardhopper_write(magicACK, sizeof(magicACK)); + + if (packet->len > (MAX_FRAME_SIZE - 2)) { + // this will overrun MAX_FRAME_SIZE once we re-add the CRC + // in theory this should never happen but better to be defensive + packet->len = 0; + cardhopper_write(magicERR, sizeof(magicERR)); + } else { + cardhopper_write(magicACK, sizeof(magicACK)); + } } -static void write_packet(packet_t *packet) { - cardhopper_write((uint8_t *) packet, packet->len + 1); +static void write_packet(const packet_t *packet) { + cardhopper_write((const uint8_t *) packet, packet->len + 1); } diff --git a/armsrc/Standalone/hf_colin.c b/armsrc/Standalone/hf_colin.c index 423e093f6..0aa49c092 100644 --- a/armsrc/Standalone/hf_colin.c +++ b/armsrc/Standalone/hf_colin.c @@ -498,7 +498,7 @@ failtag: SpinOff(50); LED_A_ON(); - while (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + while (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { WDT_HIT(); if (BUTTON_HELD(10) == BUTTON_HOLD) { WDT_HIT(); @@ -785,7 +785,7 @@ static int e_MifareECardLoad(uint32_t numofsectors, uint8_t keytype) { bool isOK = true; - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { isOK = false; } @@ -844,8 +844,7 @@ static int cjat91_saMifareChkKeys(uint8_t blockNo, uint8_t keyType, bool clearTr for (uint8_t i = 0; i < keyCount; i++) { /* no need for anticollision. just verify tag is still here */ - // if (!iso14443a_fast_select_card(colin_cjuid, 0)) { - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { cjSetCursLeft(); DbprintfEx(FLAG_NEWLINE, "%sFATAL%s : E_MF_LOSTTAG", _XRED_, _XWHITE_); break; @@ -963,7 +962,7 @@ static int saMifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, const // get UID from chip if (workFlags & 0x01) { - if (!iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true)) { + if (iso14443a_select_card(colin_cjuid, &colin_p_card, &colin_cjcuid, true, 0, true) == 0) { DbprintfEx(FLAG_NEWLINE, "Can't select card"); break; }; diff --git a/armsrc/Standalone/hf_craftbyte.c b/armsrc/Standalone/hf_craftbyte.c index 6eb2ae2a2..736e89aca 100644 --- a/armsrc/Standalone/hf_craftbyte.c +++ b/armsrc/Standalone/hf_craftbyte.c @@ -89,22 +89,22 @@ void RunMod(void) { Dbprintf("Starting simulation, press " _GREEN_("pm3 button") " to stop and go back to search state."); if (card.sak == 0x08 && card.atqa[0] == 0x04 && card.atqa[1] == 0) { DbpString("Mifare Classic 1k"); - SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x08 && card.atqa[0] == 0x44 && card.atqa[1] == 0) { DbpString("Mifare Classic 4k "); - SimulateIso14443aTag(8, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(8, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x00 && card.atqa[0] == 0x44 && card.atqa[1] == 0) { DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(2, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x20 && card.atqa[0] == 0x04 && card.atqa[1] == 0x03) { DbpString("Mifare DESFire"); - SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0, false, false); } else if (card.sak == 0x20 && card.atqa[0] == 0x44 && card.atqa[1] == 0x03) { DbpString("Mifare DESFire Ev1/Plus/JCOP"); - SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(3, flags, card.uid, 0, NULL, 0, false, false); } else { Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0); + SimulateIso14443aTag(1, flags, card.uid, 0, NULL, 0, false, false); } // Go back to search state if user presses pm3-button diff --git a/armsrc/Standalone/hf_mattyrun.c b/armsrc/Standalone/hf_mattyrun.c index 615d79718..c68d12075 100644 --- a/armsrc/Standalone/hf_mattyrun.c +++ b/armsrc/Standalone/hf_mattyrun.c @@ -33,6 +33,7 @@ #include "mifaresim.h" // mifare1ksim #include "mifareutil.h" #include "proxmark3_arm.h" +#include "spiffs.h" #include "standalone.h" // standalone definitions #include "string.h" #include "ticks.h" @@ -534,7 +535,14 @@ void RunMod(void) { SpinErr(LED_D, 50, 8); partialEmulation = true; } else { - DbpString("[" _GREEN_("+") "] " _GREEN_("Emulator memory filled completely.")); +#ifdef WITH_FLASH + DbpString("[" _GREEN_("+") "] " _GREEN_("Emulator memory filled completely. Start storing card in spiff memory.")); + uint8_t *emCARD = BigBuf_get_EM_addr(); + char dumpFileName[30] = {0}; + sprintf(dumpFileName, DUMP_FILE, mattyrun_card.uid[0], mattyrun_card.uid[1], mattyrun_card.uid[2], mattyrun_card.uid[3]); + rdv40_spiffs_write(dumpFileName, emCARD, 1024, RDV40_SPIFFS_SAFETY_SAFE); + Dbprintf("[" _GREEN_("+") "] " _GREEN_("Stored card on %s"), dumpFileName); +#endif } state = STATE_EMULATE; diff --git a/armsrc/Standalone/hf_mattyrun.h b/armsrc/Standalone/hf_mattyrun.h index 605ea447b..892232e88 100644 --- a/armsrc/Standalone/hf_mattyrun.h +++ b/armsrc/Standalone/hf_mattyrun.h @@ -21,6 +21,9 @@ #include +// Filename to store the card info in spiff memory +#define DUMP_FILE "hf_mattyrun_dump_%02x%02x%02x%02x.bin" + // Set of standard keys to be used static uint64_t const MATTYRUN_MFC_DEFAULT_KEYS[] = { 0xFFFFFFFFFFFF, // Default key diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index 711e653a4..6eb5b46b2 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -379,7 +379,7 @@ void RunMod(void) { BigBuf_free_keep_EM(); // tag type: 11 = ISO/IEC 14443-4 - javacard (JCOP) - if (SimulateIso14443aInit(11, flags, data, NULL, 0, &responses, &cuid, NULL, NULL, NULL) == false) { + if (SimulateIso14443aInit(11, flags, data, NULL, 0, &responses, &cuid, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_RED_("Error initializing the emulation process!")); diff --git a/armsrc/Standalone/hf_reblay.c b/armsrc/Standalone/hf_reblay.c index 1b84eb3f7..30db64f41 100644 --- a/armsrc/Standalone/hf_reblay.c +++ b/armsrc/Standalone/hf_reblay.c @@ -268,7 +268,7 @@ void RunMod() { BigBuf_free_keep_EM(); // 4 = ISO/IEC 14443-4 - javacard (JCOP) - if (SimulateIso14443aInit(4, flags, data, NULL, 0, &responses, &cuid, NULL, NULL, NULL) == false) { + if (SimulateIso14443aInit(4, flags, data, NULL, 0, &responses, &cuid, NULL, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_RED_("Error initializing the emulation process!")); diff --git a/armsrc/Standalone/hf_st25_tearoff.c b/armsrc/Standalone/hf_st25_tearoff.c new file mode 100644 index 000000000..65fc2ba52 --- /dev/null +++ b/armsrc/Standalone/hf_st25_tearoff.c @@ -0,0 +1,1170 @@ +//----------------------------------------------------------------------------- +// Copyright (C) SecLabz, 2025 +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- +// Standalone mode for reading/storing and restoring ST25TB tags with tear-off for counters. +// Handles a collection of tags. Click swaps between Store and Restore modes. +// Requires WITH_FLASH enabled at compile time. +// Only tested on a Proxmark3 Easy with flash +// +// The initial mode is learning/storing with LED D. +// In this mode, the Proxmark3 is looking for an ST25TB tag, reads all its data, +// and stores the tag's contents to flash memory for later restoration. +// +// Clicking the button once will toggle to restore mode (LED C). +// In this mode, the Proxmark3 searches for an ST25TB tag and, if found, compares +// its UID with previously stored tags. If there's a match, it will restore the +// tag data from flash memory, including counter blocks using tear-off technique. +// +// The standalone supports a collection of up to 8 different ST25TB tags. +// +// Special handling is implemented for counter blocks 5 & 6. For these blocks, +// the tear-off technique is used to manipulate counters that normally can only +// be decremented, allowing restoration of previously stored counter values even +// if they're higher than the current value. +// +// Holding the button down for 1 second will exit the standalone mode. +// +// LEDs: +// LED D = Learn/Store mode (reading and storing tag data) +// LED C = Restore mode (writing stored data back to tags) +// LED A (blinking) = Operation successful +// LED B (blinking) = Operation failed +// +// Flash memory is required for this standalone mode to function properly. +// +//----------------------------------------------------------------------------- + + +//============================================================================= +// INCLUDES +//============================================================================= + +// System includes +#include // memcpy, memset + +// Proxmark3 includes +#include "standalone.h" +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "iso14443b.h" // ISO14443B operations +#include "util.h" +#include "spiffs.h" // Flash memory filesystem access +#include "dbprint.h" +#include "ticks.h" +#include "BigBuf.h" +#include "protocols.h" +#include "crc16.h" // compute_crc + +//============================================================================= +// FLASH MEMORY REQUIREMENT CHECK +//============================================================================= + +#ifndef WITH_FLASH +#error "This standalone mode requires WITH_FLASH to be defined. Please recompile with flash memory support." +#endif + +//============================================================================= +// CONSTANTS & DEFINITIONS +//============================================================================= + +// File and data structure constants +#define HF_ST25TB_MULTI_SR_FILE "hf_st25tb_tags.bin" // Store/Restore filename +#define ST25TB_BLOCK_COUNT 16 // ST25TB512 or similar with 16 blocks +#define ST25TB_BLOCK_SIZE 4 // 4 bytes per block +#define ST25TB_COUNTER_BLOCK_5 5 // Counter block indices +#define ST25TB_COUNTER_BLOCK_6 6 +#define ST25TB_DATA_SIZE (ST25TB_BLOCK_COUNT * ST25TB_BLOCK_SIZE) +#define MAX_SAVED_TAGS 8 // Allow storing up to 8 tags + +// Tear-off constants +#define TEAR_OFF_START_OFFSET_US 150 +#define TEAR_OFF_ADJUSTMENT_US 25 +#define PRE_READ_DELAY_US 0 +#define TEAR_OFF_WRITE_RETRY_COUNT 30 +#define TEAR_OFF_CONSOLIDATE_READ_COUNT 6 +#define TEAR_OFF_CONSOLIDATE_WAIT_READ_COUNT 2 +#define TEAR_OFF_CONSOLIDATE_WAIT_MS 2000 + +// Display/console colors +#define RESET "\033[0m" +#define BOLD "\033[01m" +#define RED "\033[31m" +#define BLUE "\033[34m" +#define GREEN "\033[32m" + +// Bit manipulation macros +#define IS_ONE_BIT(value, index) ((value) & ((uint32_t)1 << (index))) +#define IS_ZERO_BIT(value, index) (!IS_ONE_BIT(value, index)) + +#define RF_SWTICH_OFF() FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF) + +//============================================================================= +// TYPE DEFINITIONS +//============================================================================= + +// Operation modes +typedef enum { + MODE_LEARN = 0, // Store/learn tag data + MODE_RESTORE = 1 // Restore tag data +} standalone_mode_t; + +// Operation states +typedef enum { + STATE_BUSY = 0, // Actively processing + STATE_DONE = 1, // Operation completed successfully + STATE_ERROR = 2 // Operation failed +} standalone_state_t; + +// Structure to hold tag data in RAM +typedef struct { + uint64_t uid; + uint32_t blocks[ST25TB_BLOCK_COUNT]; + uint32_t otp; + bool data_valid; // Flag to indicate if this slot holds valid data +} st25tb_data_t; + +//============================================================================= +// GLOBAL VARIABLES +//============================================================================= + +// Tag collection and state tracking +static st25tb_data_t g_stored_tags[MAX_SAVED_TAGS]; +static uint8_t g_valid_tag_count = 0; // Number of valid entries +static standalone_mode_t g_current_mode = MODE_LEARN; // Current operation mode +static standalone_state_t current_state = STATE_BUSY; // Current operation state +static unsigned long g_prng_seed = 1; // Used for PRNG + +//============================================================================= +// FUNCTION DECLARATIONS +//============================================================================= + +// Core utility functions +static int dummy_rand(void); +uint64_t bytes_to_num_le(const uint8_t *src, size_t len); + +// UI/LED interaction functions +static void update_leds_mode(standalone_mode_t mode); +static void indicate_success(void); +static void indicate_failure(void); + +// Flash storage operations +static bool load_tags_from_flash(st25tb_data_t collection[MAX_SAVED_TAGS]); +static bool save_tags_to_flash(const st25tb_data_t collection[MAX_SAVED_TAGS]); +static int find_tag_by_uid(const uint64_t uid); +static int find_free_tag_slot(void); + +// ISO14443B communication functions +static void iso14443b_setup_light(void); + +// Tag read/write operations +static bool st25tb_tag_get_basic_info(iso14b_card_select_t *card_info); +static bool st25tb_tag_read(st25tb_data_t *tag_data_slot); +static bool st25tb_tag_restore(const st25tb_data_t *stored_data_slot); +static void st25tb_tag_print(st25tb_data_t *tag); + +// Tear-off operations +static int st25tb_cmd_write_block(uint8_t block_address, uint8_t *block); +static bool st25tb_write_block_with_retry(uint8_t block_address, uint32_t target_value); +static int st25tb_tear_off_read_block(uint8_t block_address, uint32_t *block_value); +static void st25tb_tear_off_write_block(uint8_t block_address, uint32_t data, uint16_t tearoff_delay_us); +static int8_t st25tb_tear_off_retry_write_verify(uint8_t block_address, uint32_t target_value, uint32_t max_try_count, int sleep_time_ms, uint32_t *read_back_value); +static int8_t st25tb_tear_off_is_consolidated(const uint8_t block_address, uint32_t value, int repeat_read, int sleep_time_ms, uint32_t *read_value); +static int8_t st25tb_tear_off_consolidate_block(const uint8_t block_address, uint32_t current_value, uint32_t target_value, uint32_t *read_back_value); +static uint32_t st25tb_tear_off_next_value(uint32_t current_value, bool randomness); +static void st25tb_tear_off_adjust_timing(int *tear_off_us, uint32_t tear_off_adjustment_us); +static void st25tb_tear_off_log(int tear_off_us, char *color, uint32_t value); +static int8_t st25tb_tear_off_write_counter(uint8_t block_address, uint32_t target_value, uint32_t tear_off_adjustment_us, uint32_t safety_value); + +// Main application functions +static void run_learn_function(void); +static void run_restore_function(void); +void ModInfo(void); +void RunMod(void); + +//============================================================================= +// CORE UTILITY FUNCTIONS +//============================================================================= + +/** + * @brief Simple PRNG implementation + * @return Random integer + */ +static int dummy_rand(void) { + g_prng_seed = g_prng_seed * 1103515245 + 12345; + return (unsigned int)(g_prng_seed / 65536) % 32768; +} + +/** + * @brief Convert bytes to number (little-endian) + * @param src Source byte array + * @param len Length of array + * @return Converted 64-bit value + */ +uint64_t bytes_to_num_le(const uint8_t *src, size_t len) { + uint64_t num = 0; + size_t i; + + if (len > sizeof(uint64_t)) { + len = sizeof(uint64_t); + } + + // Iterate from LSB to MSB + for (i = 0; i < len; ++i) { + num |= ((uint64_t)src[i] << (i * 8)); + } + + return num; +} + +//============================================================================= +// UI/LED INTERACTION FUNCTIONS +//============================================================================= + +/** + * @brief Update LEDs to indicate current mode and state + * @param mode Current operation mode + */ +static void update_leds_mode(standalone_mode_t mode) { + LEDsoff(); + if (mode == MODE_LEARN) { + LED_D_ON(); + } else { // MODE_RESTORE + LED_C_ON(); + } +} + +/** + * @brief Indicate successful operation with LED sequence + */ +static void indicate_success(void) { + // Blink Green LED (A) 3 times quickly for success + for (int i = 0; i < 3; ++i) { + LED_A_ON(); + SpinDelay(150); + LED_A_OFF(); + SpinDelay(150); + } +} + +/** + * @brief Indicate failed operation with LED sequence + */ +static void indicate_failure(void) { + // Blink Red LED (B) 3 times quickly for failure + for (int i = 0; i < 3; ++i) { + LED_B_ON(); + SpinDelay(150); + LED_B_OFF(); + SpinDelay(150); + } +} + +//============================================================================= +// FLASH STORAGE OPERATIONS +//============================================================================= + +/** + * @brief Load tag collection from flash + * @param collection Array to store loaded data + * @return true if successful, false otherwise + */ +static bool load_tags_from_flash(st25tb_data_t collection[MAX_SAVED_TAGS]) { + // Check if file exists + if (!exists_in_spiffs(HF_ST25TB_MULTI_SR_FILE)) { + return false; // File doesn't exist, nothing to load + } + + // Verify file size + uint32_t size = size_in_spiffs(HF_ST25TB_MULTI_SR_FILE); + if (size != sizeof(g_stored_tags)) { + Dbprintf(_RED_("Flash file size mismatch (expected %zu, got %u). Wiping old file."), + sizeof(g_stored_tags), size); + // Remove corrupted file + rdv40_spiffs_remove(HF_ST25TB_MULTI_SR_FILE, RDV40_SPIFFS_SAFETY_SAFE); + return false; + } + + // Read file contents + int res = rdv40_spiffs_read(HF_ST25TB_MULTI_SR_FILE, (uint8_t *)collection, + size, RDV40_SPIFFS_SAFETY_SAFE); + + if (res != SPIFFS_OK) { + Dbprintf(_RED_("Failed to read tag collection from flash (err %d)"), res); + // Mark all as invalid if read failed + for (int i = 0; i < MAX_SAVED_TAGS; i++) + collection[i].data_valid = false; + return false; + } + + return true; +} + +/** + * @brief Save tag collection to flash + * @param collection Array of tag data to save + * @return true if successful, false otherwise + */ +static bool save_tags_to_flash(const st25tb_data_t collection[MAX_SAVED_TAGS]) { + int res = rdv40_spiffs_write(HF_ST25TB_MULTI_SR_FILE, (uint8_t *)collection, + sizeof(g_stored_tags), RDV40_SPIFFS_SAFETY_SAFE); + return (res == SPIFFS_OK); +} + +/** + * @brief Find a tag in the collection by UID + * @param uid UID to search for + * @return Index of tag in collection, or -1 if not found + */ +static int find_tag_by_uid(const uint64_t uid) { + for (int i = 0; i < MAX_SAVED_TAGS; i++) { + if (g_stored_tags[i].data_valid && g_stored_tags[i].uid == uid) { + return i; + } + } + return -1; // Not found +} + +/** + * @brief Find next empty slot in the collection + * @return Index of empty slot, or -1 if collection is full + */ +static int find_free_tag_slot(void) { + for (int i = 0; i < MAX_SAVED_TAGS; i++) { + if (!g_stored_tags[i].data_valid) { + return i; + } + } + return -1; // Collection is full +} + +//============================================================================= +// ISO14443B COMMUNICATION FUNCTIONS +//============================================================================= + +/** + * @brief Stripped version of "iso14443b_setup" that avoids unnecessary LED + * operations and uses shorter delays + */ +static void iso14443b_setup_light(void) { + RF_SWTICH_OFF(); + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + + // Set up the synchronous serial port + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); + + // Signal field is on with the appropriate LED +#ifdef RDV4 + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD_RDV4); +#else + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SEND_SHALLOW_MOD); +#endif + + SpinDelayUs(250); + + // Start the timer + StartCountSspClk(); +} + +//============================================================================= +// TAG READ/WRITE OPERATIONS +//============================================================================= + +/** + * @brief Select a ST25TB tag and get basic info + * @param card_info Pointer to store card info + * @return true if successful, false otherwise + */ +static bool st25tb_tag_get_basic_info(iso14b_card_select_t *card_info) { + iso14443b_setup_light(); + int res = iso14443b_select_srx_card(card_info); + RF_SWTICH_OFF(); + return (res == PM3_SUCCESS); +} + +/** + * @brief Read all data from a ST25TB tag + * @param tag_data_slot Pointer to store tag data + * @return true if successful, false otherwise + */ +static bool st25tb_tag_read(st25tb_data_t *tag_data_slot) { + iso14443b_setup_light(); + iso14b_card_select_t card_info; + uint8_t block[ST25TB_BLOCK_SIZE]; + int res; + bool success = true; + + // Select card + res = iso14443b_select_srx_card(&card_info); + if (res != PM3_SUCCESS) { + RF_SWTICH_OFF(); + return false; + } + + Dbprintf("Found ST tag. Reading %d blocks...", ST25TB_BLOCK_COUNT); + tag_data_slot->uid = bytes_to_num_le(card_info.uid, sizeof(tag_data_slot->uid)); + + // Read all data blocks + for (uint8_t block_address = 0; block_address < ST25TB_BLOCK_COUNT; block_address++) { + WDT_HIT(); + res = read_14b_srx_block(block_address, block); + if (res != PM3_SUCCESS) { + Dbprintf(_RED_("Failed to read block %d"), block_address); + success = false; + break; + } + + // Store the read block data + tag_data_slot->blocks[block_address] = bytes_to_num_le(block, ST25TB_BLOCK_SIZE); + + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf("Read Block %02d: %08X", block_address, tag_data_slot->blocks[block_address]); + } + SpinDelay(5); // Small delay between block reads + } + + // Read OTP block + res = read_14b_srx_block(255, block); + if (res != PM3_SUCCESS) { + Dbprintf(_RED_("Failed to read otp block")); + success = false; + } else { + tag_data_slot->otp = bytes_to_num_le(block, ST25TB_BLOCK_SIZE); + } + + RF_SWTICH_OFF(); + + tag_data_slot->data_valid = success; + return success; +} + +/** + * @brief Restore data to a ST25TB tag + * @param stored_data_slot Pointer to stored tag data + * @return true if successful, false otherwise + */ +static bool st25tb_tag_restore(const st25tb_data_t *stored_data_slot) { + if (!stored_data_slot->data_valid) { + DbpString(_RED_("Restore error: Slot data is invalid.")); + return false; + } + + iso14443b_setup_light(); + iso14b_card_select_t card_info; + int res; + bool success = true; + + res = iso14443b_select_srx_card(&card_info); + if (res != PM3_SUCCESS) { + DbpString("Restore failed: No tag found or selection failed."); + RF_SWTICH_OFF(); + return false; + } + + uint64_t tag_uid = bytes_to_num_le(card_info.uid, sizeof(uint64_t)); + + // Verify UID match before restoring + if (tag_uid != stored_data_slot->uid) { + Dbprintf("Restore failed: UID mismatch (Tag: %llX, Slot: %llX)", tag_uid, stored_data_slot->uid); + RF_SWTICH_OFF(); + return false; + } + + Dbprintf("Found ST tag, UID: %llX. Starting restore...", tag_uid); + + // Process all blocks + for (uint8_t block_address = 0; block_address < ST25TB_BLOCK_COUNT; block_address++) { + WDT_HIT(); + uint32_t stored_value = stored_data_slot->blocks[block_address]; + + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf("Restoring Block %02d: %08X", block_address, stored_value); + } + + // Special handling for counter blocks 5 and 6 + if (block_address == ST25TB_COUNTER_BLOCK_5 || block_address == ST25TB_COUNTER_BLOCK_6) { + uint32_t current_value = 0; + + res = st25tb_tear_off_read_block(block_address, ¤t_value); + if (res != PM3_SUCCESS) { + Dbprintf(_RED_("Failed to read current counter value for block %d"), block_address); + success = false; + break; + } + + if (g_dbglevel >= DBG_DEBUG) { + Dbprintf("Counter Block %d: Stored=0x%08X, Current=0x%08X", + block_address, stored_value, current_value); + } + + // Only use tear-off logic if stored value is greater + if (stored_value > current_value) { + // The st25tb_tear_off_write_counter function handles the tear-off logic + if (st25tb_tear_off_write_counter(block_address, stored_value, TEAR_OFF_ADJUSTMENT_US, 0x1000) != 0) { + Dbprintf(_RED_("Tear-off write failed for counter block %d"), block_address); + success = false; + break; + } + Dbprintf("Used tear-off write for counter block %d", block_address); + } else if (stored_value < current_value) { + // Standard write for when stored value is less than current + if (!st25tb_write_block_with_retry(block_address, stored_value)) { + Dbprintf(_RED_("Failed to write block %d"), block_address); + success = false; + break; + } + } else { + Dbprintf("Counter block %d already has the target value (0x%08X). Skipping write.", + block_address, stored_value); + } + } else { + // Standard write for non-counter blocks + if (!st25tb_write_block_with_retry(block_address, stored_value)) { + Dbprintf(_RED_("Failed to write block %d with value 0x%08X"), block_address, stored_value); + success = false; + break; + } + } + SpinDelay(10); // Delay between writes + } + + RF_SWTICH_OFF(); + return success; +} + +/** + * @brief Print tag data in formatted table + * @param tag Pointer to tag data + */ +static void st25tb_tag_print(st25tb_data_t *tag) { + uint8_t i; + + Dbprintf("UID: %016llX", tag->uid); + + Dbprintf("+---------------+----------+--------------------+"); + Dbprintf("| BLOCK ADDRESS | VALUE | DESCRIPTION |"); + Dbprintf("+---------------+----------+--------------------+"); + + for (i = 0; i < 16; i++) { + if (i == 2) { + Dbprintf("| %03d | %08X | Lockable EEPROM |", i, tag->blocks[i]); + } else if (i == 5) { + Dbprintf("| %03d | %08X | Count down |", i, tag->blocks[i]); + } else if (i == 6) { + Dbprintf("| %03d | %08X | counter |", i, tag->blocks[i]); + } else if (i == 11) { + Dbprintf("| %03d | %08X | Lockable EEPROM |", i, tag->blocks[i]); + } else { + Dbprintf("| %03d | %08X | |", i, tag->blocks[i]); + } + if (i == 4 || i == 6 || i == 15) { + Dbprintf("+---------------+----------+--------------------+"); + } + } + + Dbprintf("| %03d | %08X | System OTP bits |", 255, tag->otp); + Dbprintf("+---------------+----------+--------------------+"); +} + +//============================================================================= +// TEAR-OFF OPERATIONS +//============================================================================= + +/** + * @brief Read a block + * @param block_address Block address to read + * @param block_value Pointer to store read value + * @return Result code (0 for success) + */ +static int st25tb_tear_off_read_block(uint8_t block_address, uint32_t *block_value) { + int res; + iso14b_card_select_t card; + iso14443b_setup_light(); + + res = iso14443b_select_srx_card(&card); + if (res != PM3_SUCCESS) { + goto out; + } + + uint8_t block[ST25TB_BLOCK_SIZE]; + res = read_14b_srx_block(block_address, block); + if (res == PM3_SUCCESS) { + *block_value = bytes_to_num_le(block, ST25TB_BLOCK_SIZE); + } + +out: + RF_SWTICH_OFF(); + return res; +} + +/** + * @brief Low-level block write function + * @param block_address Block number to write + * @param block Block data + * @return Result code (0 for success) + */ +static int st25tb_cmd_write_block(uint8_t block_address, uint8_t *block) { + uint8_t cmd[] = {ISO14443B_WRITE_BLK, block_address, block[0], block[1], block[2], block[3], 0x00, 0x00}; + AddCrc14B(cmd, 6); + + uint32_t start_time = 0; + uint32_t eof_time = 0; + CodeAndTransmit14443bAsReader(cmd, sizeof(cmd), &start_time, &eof_time, true); + + return PM3_SUCCESS; +} + +/** + * @brief Write a block with retry mechanism + * @param block_address Block number to write + * @param target_value Value to write + * @return true if successful, false otherwise + */ +static bool st25tb_write_block_with_retry(uint8_t block_address, uint32_t target_value) { + uint32_t read_back_value = 0; + int max_retries = 5; + + if (st25tb_tear_off_retry_write_verify(block_address, target_value, max_retries, 0, &read_back_value) != 0) { + return false; + } + + return (read_back_value == target_value); +} + +/** + * @brief Write a block with tear-off capability + * @param block_address Block number to write + * @param data Data to write + * @param tearoff_delay_us Tear-off delay in microseconds + */ +static void st25tb_tear_off_write_block(uint8_t block_address, uint32_t data, uint16_t tearoff_delay_us) { + iso14443b_setup_light(); + + uint8_t block[ST25TB_BLOCK_SIZE]; + block[0] = (data & 0xFF); + block[1] = (data >> 8) & 0xFF; + block[2] = (data >> 16) & 0xFF; + block[3] = (data >> 24) & 0xFF; + + iso14b_card_select_t card; + int res = iso14443b_select_srx_card(&card); + if (res != PM3_SUCCESS) { + goto out; + } + + res = st25tb_cmd_write_block(block_address, block); + + // Tear off the communication at precise timing + SpinDelayUsPrecision(tearoff_delay_us); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + +out: + RF_SWTICH_OFF(); +} + +/** + * @brief Write a block with retry and verification + * @param block_address Block address to write + * @param target_value Value to write + * @param max_try_count Maximum number of retries + * @param sleep_time_ms Sleep time between retries in milliseconds + * @param read_back_value Pointer to store read-back value + * @return 0 for success, -1 for failure + */ +static int8_t st25tb_tear_off_retry_write_verify(uint8_t block_address, uint32_t target_value, + uint32_t max_try_count, int sleep_time_ms, + uint32_t *read_back_value) { + int i = 0; + *read_back_value = ~target_value; // Initialize to ensure the loop runs at least once + + while (*read_back_value != target_value && i < max_try_count) { + st25tb_tear_off_write_block(block_address, target_value, 6000); // Long delay for reliability + if (sleep_time_ms > 0) SpinDelayUsPrecision(sleep_time_ms * 1000); + st25tb_tear_off_read_block(block_address, read_back_value); + if (sleep_time_ms > 0) SpinDelayUsPrecision(sleep_time_ms * 1000); + i++; + } + + return (*read_back_value == target_value) ? 0 : -1; +} + +/** + * @brief Check if a block's value is consolidated (stable) + * @param block_address Block address to check + * @param value Expected value + * @param repeat_read Number of reads to perform + * @param sleep_time_ms Sleep time between reads in milliseconds + * @param read_value Pointer to store read value + * @return 0 if consolidated, -1 otherwise + */ +static int8_t st25tb_tear_off_is_consolidated(const uint8_t block_address, uint32_t value, + int repeat_read, int sleep_time_ms, + uint32_t *read_value) { + int result; + for (int i = 0; i < repeat_read; i++) { + if (sleep_time_ms > 0) SpinDelayUsPrecision(sleep_time_ms * 1000); + result = st25tb_tear_off_read_block(block_address, read_value); + if (result != 0 || value != *read_value) { + return -1; // Read error or value changed + } + } + return 0; // Value remained stable +} + +/** + * @brief Consolidate a block to a stable state + * @param block_address Block address to consolidate + * @param current_value Current value + * @param target_value Target value + * @param read_back_value Pointer to store read-back value + * @return 0 for success, -1 for failure + */ +static int8_t st25tb_tear_off_consolidate_block(const uint8_t block_address, uint32_t current_value, + uint32_t target_value, uint32_t *read_back_value) { + int8_t result; + uint32_t consolidation_value; + + // Determine the value to write for consolidation based on target and current state + if (target_value <= 0xFFFFFFFD && current_value >= (target_value + 2)) { + consolidation_value = target_value + 2; + } else { + consolidation_value = current_value; + } + + // Try writing value - 1 + result = st25tb_tear_off_retry_write_verify(block_address, consolidation_value - 1, + TEAR_OFF_WRITE_RETRY_COUNT, 0, read_back_value); + if (result != 0) { + Dbprintf("Consolidation failed at step 1 (write 0x%08X)", consolidation_value - 1); + return -1; + } + + // If value is not FE or target is not FD, try writing value - 2 + if (*read_back_value != 0xFFFFFFFE || (*read_back_value == 0xFFFFFFFE && target_value == 0xFFFFFFFD)) { + result = st25tb_tear_off_retry_write_verify(block_address, consolidation_value - 2, + TEAR_OFF_WRITE_RETRY_COUNT, 0, read_back_value); + if (result != 0) { + Dbprintf("Consolidation failed at step 2 (write 0x%08X)", consolidation_value - 2); + return -1; + } + } + + // Final checks for stability of unstable high values (due to internal dual counters) + if (result == 0 && target_value > 0xFFFFFFFD && *read_back_value > 0xFFFFFFFD) { + result = st25tb_tear_off_is_consolidated(block_address, *read_back_value, + TEAR_OFF_CONSOLIDATE_READ_COUNT, 0, read_back_value); + if (result == 0) { + result = st25tb_tear_off_is_consolidated(block_address, *read_back_value, + TEAR_OFF_CONSOLIDATE_WAIT_READ_COUNT, + TEAR_OFF_CONSOLIDATE_WAIT_MS, read_back_value); + if (result != 0) { + Dbprintf("Consolidation failed stability check (long wait)"); + return -1; + } + } else { + Dbprintf("Consolidation failed stability check (short wait)"); + return -1; + } + } + + return 0; +} + +/** + * @brief Calculate next value for counter decrement + * @param current_value Current counter value + * @param randomness Whether to use randomization + * @return Next value to attempt + */ +static uint32_t st25tb_tear_off_next_value(uint32_t current_value, bool randomness) { + uint32_t value = 0; + int8_t index = 31; + + // Simple decrement for smaller values + if (current_value < 0x0000FFFF) { + return (current_value > 0) ? current_value - 1 : 0; + } + + // Loop through each bit starting from the most significant bit (MSB) + while (index >= 0) { + // Find the most significant '1' bit + if (value == 0 && IS_ONE_BIT(current_value, index)) { + // Create a mask with '1's up to this position + value = 0xFFFFFFFF >> (31 - index); + index--; // Move to the next bit + } + + // Once the first '1' is found, look for the first '0' after it + if (value != 0 && IS_ZERO_BIT(current_value, index)) { + index++; // Go back to the position of the '0' + // Clear the bit at this '0' position in our mask + value &= ~((uint32_t)1 << index); + + // Optional randomization: flip a random bit below the found '0' + if (randomness && value < 0xF0000000 && index > 1) { + value ^= ((uint32_t)1 << (dummy_rand() % index)); + } + return value; + } + + index--; + } + + return (current_value > 0) ? current_value - 1 : 0; +} + +/** + * @brief Adjust timing for tear-off operations + * @param tear_off_us Pointer to current tear-off timing + * @param tear_off_adjustment_us Adjustment amount + */ +static void st25tb_tear_off_adjust_timing(int *tear_off_us, uint32_t tear_off_adjustment_us) { + if (*tear_off_us > TEAR_OFF_START_OFFSET_US) { + *tear_off_us -= tear_off_adjustment_us; + } +} + +/** + * @brief Log tear-off operation details + * @param tear_off_us Current tear-off timing + * @param color Color code for output + * @param value Value being processed + */ +static void st25tb_tear_off_log(int tear_off_us, char *color, uint32_t value) { + char binaryRepresentation[33]; + for (int i = 31; i >= 0; i--) { + binaryRepresentation[31 - i] = IS_ONE_BIT(value, i) ? '1' : '0'; + } + binaryRepresentation[32] = '\0'; + Dbprintf("%s%08X%s : %s%s%s : %d us", color, value, RESET, color, binaryRepresentation, RESET, tear_off_us); +} + +/** + * @brief Main tear-off counter write function + * @param block_address Block address to write + * @param target_value Target value + * @param tear_off_adjustment_us Adjustment for tear-off timing + * @param safety_value Safety threshold to prevent going below + * @return 0 for success, non-zero for failure + */ +static int8_t st25tb_tear_off_write_counter(uint8_t block_address, uint32_t target_value, + uint32_t tear_off_adjustment_us, uint32_t safety_value) { + int result; + bool trigger = true; + + uint32_t read_value = 0; + uint32_t current_value = 0; + uint32_t last_consolidated_value = 0; + uint32_t tear_off_value = 0; + + int tear_off_us = TEAR_OFF_START_OFFSET_US; + if (tear_off_adjustment_us == 0) { + tear_off_adjustment_us = TEAR_OFF_ADJUSTMENT_US; + } + + // Initial read to get the current counter value + result = st25tb_tear_off_read_block(block_address, ¤t_value); + if (result != PM3_SUCCESS) { + Dbprintf("Initial read failed for block %d", block_address); + return -1; // Indicate failure + } + + // Calculate the first value to attempt writing via tear-off + tear_off_value = st25tb_tear_off_next_value(current_value, false); + + Dbprintf(" Target block: %d", block_address); + Dbprintf("Current value: 0x%08X", current_value); + Dbprintf(" Target value: 0x%08X", target_value); + Dbprintf(" Safety value: 0x%08X", safety_value); + Dbprintf("Adjustment us: %u", tear_off_adjustment_us); + + // Check if tear-off is even possible or needed + if (tear_off_value == 0 && current_value != 0) { + Dbprintf("Tear-off technique not possible from current value."); + return -1; + } + if (current_value == target_value) { + Dbprintf("Current value already matches target value."); + return 0; + } + + // Main tear-off loop + for (;;) { + // Safety check: ensure we don't go below the safety threshold + if (tear_off_value < safety_value) { + Dbprintf("Stopped. Safety threshold reached (next value 0x%08X < safety 0x%08X)", + tear_off_value, safety_value); + return -1; + } + + // Perform the tear-off write attempt + st25tb_tear_off_write_block(block_address, tear_off_value, tear_off_us); + + // Read back the value after the attempt + result = st25tb_tear_off_read_block(block_address, &read_value); + if (result != 0) { + continue; // Retry the loop if read fails (ex: tag is removed from the read for a short period) + } + + // Analyze the result and decide next action + if (read_value > current_value) { + // Partial write succeeded (successful tear-off) + if (read_value >= 0xFFFFFFFE || + (read_value - 2) > target_value || + read_value != last_consolidated_value || + ((read_value & 0xF0000000) > (current_value & 0xF0000000))) { // Major bit flip + + result = st25tb_tear_off_consolidate_block(block_address, read_value, + target_value, ¤t_value); + if (result == 0 && current_value == target_value) { + st25tb_tear_off_log(tear_off_us, GREEN, read_value); + Dbprintf("Target value 0x%08X reached successfully!", target_value); + return 0; + } + if (read_value != last_consolidated_value) { + st25tb_tear_off_adjust_timing(&tear_off_us, tear_off_adjustment_us); + } + last_consolidated_value = read_value; + tear_off_value = st25tb_tear_off_next_value(current_value, false); + trigger = true; + st25tb_tear_off_log(tear_off_us, GREEN, read_value); + } + } else if (read_value == tear_off_value) { + // Write succeeded completely (no tear-off effect) + if (trigger) { + tear_off_value = st25tb_tear_off_next_value(tear_off_value, true); + trigger = false; + } else { + tear_off_value = st25tb_tear_off_next_value(read_value, false); + trigger = true; + } + current_value = read_value; + st25tb_tear_off_adjust_timing(&tear_off_us, tear_off_adjustment_us); + st25tb_tear_off_log(tear_off_us, BLUE, read_value); + } else if (read_value < tear_off_value) { + // Partial write succeeded (successful tear-off) but lower value + tear_off_value = st25tb_tear_off_next_value(read_value, false); + st25tb_tear_off_adjust_timing(&tear_off_us, tear_off_adjustment_us); + current_value = read_value; + trigger = true; + st25tb_tear_off_log(tear_off_us, RED, read_value); + } + + // Increment tear-off timing for the next attempt + tear_off_us++; + + // Check for user interruption + WDT_HIT(); + if (BUTTON_PRESS()) { + DbpString("Tear-off stopped by user."); + return -1; + } + } + + return -1; +} + +//============================================================================= +// MAIN APPLICATION FUNCTIONS +//============================================================================= + +/** + * @brief Learn/store function implementation + */ +static void run_learn_function(void) { + st25tb_data_t temp_tag_data; // Temporary buffer to read into + memset(&temp_tag_data, 0, sizeof(temp_tag_data)); + + if (st25tb_tag_read(&temp_tag_data)) { + st25tb_tag_print(&temp_tag_data); + int slot_index = find_tag_by_uid(temp_tag_data.uid); + + if (slot_index != -1) { + Dbprintf("Tag with UID %llX already in Slot %d. Overwriting...", + temp_tag_data.uid, slot_index); + } else { + slot_index = find_free_tag_slot(); + if (slot_index == -1) { + DbpString("Collection full! Overwriting Slot 0."); + slot_index = 0; // Overwrite oldest/first slot if full + } else { + // Only increment if we are adding to a new slot, not overwriting + if (!g_stored_tags[slot_index].data_valid) { + g_valid_tag_count++; + } + } + } + + // Store tag data in collection + memcpy(&g_stored_tags[slot_index], &temp_tag_data, sizeof(st25tb_data_t)); + g_stored_tags[slot_index].data_valid = true; + Dbprintf("Stored tag in Slot %d. (UID: %llX)", slot_index, temp_tag_data.uid); + + // Save collection to flash + if (save_tags_to_flash(g_stored_tags)) { + DbpString("Collection saved to flash."); + } else { + DbpString(_RED_("Failed to save collection to flash!")); + } + + current_state = STATE_DONE; // Indicate success + } +} + +/** + * @brief Restore function implementation + */ +static void run_restore_function(void) { + iso14b_card_select_t current_tag_info; // To get UID of tag in field + + if (st25tb_tag_get_basic_info(¤t_tag_info)) { + // Tag found in field + uint64_t tag_uid = bytes_to_num_le(current_tag_info.uid, sizeof(uint64_t)); + int slot = find_tag_by_uid(tag_uid); + + if (slot != -1) { + Dbprintf("Found matching tag in Slot %d (UID: %llX). Restoring...", slot, tag_uid); + + current_state = STATE_BUSY; // Indicate busy during restore attempt + update_leds_mode(g_current_mode); + + bool success = st25tb_tag_restore(&g_stored_tags[slot]); + + if (success) { + DbpString(_GREEN_("Restore successful.")); + current_state = STATE_DONE; + } else { + DbpString(_RED_("Restore failed.")); + current_state = STATE_ERROR; + } + } else { + // Tag found but not in collection, remain busy to scan again + current_state = STATE_BUSY; + } + } else { + // No tag found, remain busy to scan again + current_state = STATE_BUSY; + } +} + +/** + * @brief Display module information + */ +void ModInfo(void) { + DbpString(" HF ST25TB Store/Restore"); + Dbprintf(" Data stored/restored from: %s", HF_ST25TB_MULTI_SR_FILE); + Dbprintf(" Supports up to %d tag slots.", MAX_SAVED_TAGS); +} + +/** + * @brief Main module function + */ +void RunMod(void) { + StandAloneMode(); + Dbprintf(_YELLOW_("HF ST25TB Store/Restore mode started")); + iso14443b_setup(); + LED_D_OFF(); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Use HF bitstream for ISO14443B + + // Initialize collection + for (int i = 0; i < MAX_SAVED_TAGS; i++) { + g_stored_tags[i].data_valid = false; + } + g_valid_tag_count = 0; + + // Mount filesystem and load previous tags if available + rdv40_spiffs_lazy_mount(); + if (load_tags_from_flash(g_stored_tags)) { + DbpString("Loaded previous tag collection from flash."); + // Count valid entries loaded + for (int i = 0; i < MAX_SAVED_TAGS; i++) { + if (g_stored_tags[i].data_valid) + g_valid_tag_count++; + } + g_current_mode = MODE_RESTORE; // Default to restore if data exists + } else { + DbpString("No previous tag data found in flash or error loading."); + g_current_mode = MODE_LEARN; // Default to store if no data + } + + bool mode_display_update = true; // Force initial display + current_state = STATE_BUSY; // Reset state at the beginning + + // Main application loop + for (;;) { + WDT_HIT(); + + // Exit condition: USB command received + if (data_available()) { + DbpString("USB data detected, exiting standalone mode."); + break; + } + + // --- Button Handling --- + int button_status = BUTTON_HELD(1000); // Check for 1 second hold + + if (button_status == BUTTON_HOLD) { + DbpString("Button held, exiting standalone mode."); + break; + } else if (button_status == BUTTON_SINGLE_CLICK) { + // Toggle between modes + g_current_mode = (g_current_mode == MODE_LEARN) ? MODE_RESTORE : MODE_LEARN; + current_state = STATE_BUSY; // Reset state when changing mode + mode_display_update = true; + SpinDelay(100); // Debounce/allow user to see mode change + } + + // --- Update Display (only if mode changed) --- + if (mode_display_update) { + if (g_current_mode == MODE_LEARN) { + Dbprintf("Mode: " _YELLOW_("Learn") ". (Cnt: %d/%d)", + g_valid_tag_count, MAX_SAVED_TAGS); + } else { + Dbprintf("Mode: " _BLUE_("Restore") ". (Cnt: %d/%d)", + g_valid_tag_count, MAX_SAVED_TAGS); + } + mode_display_update = false; + } + update_leds_mode(g_current_mode); + + // Process according to current state + if (current_state == STATE_BUSY) { + // Run appropriate function based on mode + if (g_current_mode == MODE_LEARN) { + run_learn_function(); + } else { // MODE_RESTORE + run_restore_function(); + } + } else if (current_state == STATE_DONE) { + indicate_success(); + } else { + indicate_failure(); + } + + // Loop delay + SpinDelay(100); + } + + // Clean up before exiting + LED_D_ON(); // Indicate potentially saving state on exit + rdv40_spiffs_lazy_unmount(); + LED_D_OFF(); + + switch_off(); // Turn off RF field + LEDsoff(); + DbpString("Exiting " _YELLOW_("HF ST25TB Store/Restore") " mode."); +} diff --git a/armsrc/Standalone/hf_tcprst.c b/armsrc/Standalone/hf_tcprst.c index e6f75bc75..d8ced29d4 100644 --- a/armsrc/Standalone/hf_tcprst.c +++ b/armsrc/Standalone/hf_tcprst.c @@ -118,8 +118,6 @@ void RunMod(void) { uint8_t tagType = 10; // 10 = ST25TA IKEA Rothult tag_response_info_t *responses; uint32_t cuid = 0; - uint32_t counters[3] = { 0x00, 0x00, 0x00 }; - uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; uint8_t pages = 0; // command buffers @@ -193,7 +191,7 @@ void RunMod(void) { memcpy(data, stuid, sizeof(stuid)); - if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, counters, tearings, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_YELLOW_("!!") "Error initializing the simulation process!"); @@ -371,7 +369,7 @@ void RunMod(void) { memcpy(data, stuid, sizeof(stuid)); - if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, counters, tearings, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, data, NULL, 0, &responses, &cuid, &pages, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); DbpString(_YELLOW_("!!") "Error initializing the simulation process!"); diff --git a/armsrc/Standalone/hf_young.c b/armsrc/Standalone/hf_young.c index 62d215dba..83ad1999a 100644 --- a/armsrc/Standalone/hf_young.c +++ b/armsrc/Standalone/hf_young.c @@ -96,7 +96,7 @@ void RunMod(void) { } } - if (!iso14443a_select_card(NULL, &card[selected], NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, &card[selected], NULL, true, 0, true) == 0) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); SpinDelay(500); @@ -253,25 +253,25 @@ void RunMod(void) { if (uids[selected].sak == 0x08 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0) { DbpString("Mifare Classic 1k"); - SimulateIso14443aTag(1, flags, data, 0, NULL, 0); + SimulateIso14443aTag(1, flags, data, 0, NULL, 0, false, false); } 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, 0, NULL, 0); + SimulateIso14443aTag(8, flags, data, 0, NULL, 0, false, false); } 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, 0, NULL, 0); + SimulateIso14443aTag(8, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x00 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0) { DbpString("Mifare Ultralight"); - SimulateIso14443aTag(2, flags, data, 0, NULL, 0); + SimulateIso14443aTag(2, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x04 && uids[selected].atqa[1] == 0x03) { DbpString("Mifare DESFire"); - SimulateIso14443aTag(3, flags, data, 0, NULL, 0); + SimulateIso14443aTag(3, flags, data, 0, NULL, 0, false, false); } else if (uids[selected].sak == 0x20 && uids[selected].atqa[0] == 0x44 && uids[selected].atqa[1] == 0x03) { DbpString("Mifare DESFire Ev1/Plus/JCOP"); - SimulateIso14443aTag(3, flags, data, 0, NULL, 0); + SimulateIso14443aTag(3, flags, data, 0, NULL, 0, false, false); } else { Dbprintf("Unrecognized tag type -- defaulting to Mifare Classic emulation"); - SimulateIso14443aTag(1, flags, data, 0, NULL, 0); + SimulateIso14443aTag(1, flags, data, 0, NULL, 0, false, false); } } else if (button_pressed == BUTTON_SINGLE_CLICK) { diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 49005a43d..db16394ab 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -99,12 +99,13 @@ int tearoff_hook(void) { if (g_tearoff_enabled) { if (g_tearoff_delay_us == 0) { Dbprintf(_RED_("No tear-off delay configured!")); + g_tearoff_enabled = false; return PM3_SUCCESS; // SUCCESS = the hook didn't do anything } SpinDelayUsPrecision(g_tearoff_delay_us); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); g_tearoff_enabled = false; - Dbprintf(_YELLOW_("Tear-off triggered!")); + if (g_dbglevel >= DBG_ERROR) Dbprintf(_YELLOW_("Tear-off triggered!")); return PM3_ETEAROFF; } else { return PM3_SUCCESS; // SUCCESS = the hook didn't do anything @@ -254,7 +255,7 @@ static uint32_t MeasureAntennaTuningLfData(void) { void print_stack_usage(void) { for (uint32_t *p = _stack_start; ; ++p) { if (*p != 0xdeadbeef) { - Dbprintf(" Max stack usage......... %d / %d bytes", (uint32_t)_stack_end - (uint32_t)p, (uint32_t)_stack_end - (uint32_t)_stack_start); + Dbprintf(" Max stack usage..... %d / %d bytes", (uint32_t)_stack_end - (uint32_t)p, (uint32_t)_stack_end - (uint32_t)_stack_start); break; } } @@ -365,7 +366,7 @@ static void print_debug_level(void) { sprintf(dbglvlstr, "extended"); break; } - Dbprintf(" Debug log level......... %d ( " _YELLOW_("%s")" )", g_dbglevel, dbglvlstr); + Dbprintf(" Debug log level..... %d ( " _YELLOW_("%s")" )", g_dbglevel, dbglvlstr); } // measure the Connection Speed by sending SpeedTestBufferSize bytes to client and measuring the elapsed time. @@ -421,11 +422,11 @@ static void SendStatus(uint32_t wait) { print_debug_level(); tosend_t *ts = get_tosend(); - Dbprintf(" ToSendMax............... %d", ts->max); - Dbprintf(" ToSend BUFFERSIZE....... %d", TOSEND_BUFFER_SIZE); + Dbprintf(" ToSendMax........... %d", ts->max); + Dbprintf(" ToSend BUFFERSIZE... %d", TOSEND_BUFFER_SIZE); while ((AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINRDY) == 0); // Wait for MAINF value to become available... uint16_t mainf = AT91C_BASE_PMC->PMC_MCFR & AT91C_CKGR_MAINF; // Get # main clocks within 16 slow clocks - Dbprintf(" Slow clock.............. %d Hz", (16 * MAINCK) / mainf); + Dbprintf(" Slow clock.......... %d Hz", (16 * MAINCK) / mainf); uint32_t delta_time = 0; uint32_t start_time = GetTickCount(); #define SLCK_CHECK_MS 50 @@ -449,10 +450,11 @@ static void SendStatus(uint32_t wait) { } else { num = 0; } + if (num > 0) { - Dbprintf(" Mifare.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, MF_KEYS_FILE); + Dbprintf(" Mifare... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MF_KEYS_FILE); } else { - Dbprintf(" Mifare.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, MF_KEYS_FILE); + Dbprintf(" Mifare... "_RED_("%u")" keys - "_RED_("%s"), num, MF_KEYS_FILE); } if (exists_in_spiffs(T55XX_KEYS_FILE)) { @@ -460,10 +462,11 @@ static void SendStatus(uint32_t wait) { } else { num = 0; } + if (num > 0) { - Dbprintf(" T55xx................... "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, T55XX_KEYS_FILE); + Dbprintf(" T55xx.... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, T55XX_KEYS_FILE); } else { - Dbprintf(" T55xx................... "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, T55XX_KEYS_FILE); + Dbprintf(" T55xx.... "_RED_("%u")" keys - "_RED_("%s"), num, T55XX_KEYS_FILE); } if (exists_in_spiffs(ICLASS_KEYS_FILE)) { @@ -471,11 +474,38 @@ static void SendStatus(uint32_t wait) { } else { num = 0; } + if (num > 0) { - Dbprintf(" iClass.................. "_YELLOW_("%u")" keys (spiffs: "_GREEN_("%s")")", num, ICLASS_KEYS_FILE); + Dbprintf(" iClass... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, ICLASS_KEYS_FILE); } else { - Dbprintf(" iClass.................. "_RED_("%u")" keys (spiffs: "_RED_("%s")")", num, ICLASS_KEYS_FILE); + Dbprintf(" iClass... "_RED_("%u")" keys - "_RED_("%s"), num, ICLASS_KEYS_FILE); } + + if (exists_in_spiffs(MFULC_KEYS_FILE)) { + num = size_in_spiffs(MFULC_KEYS_FILE) / MFULC_KEY_LENGTH; + } else { + num = 0; + } + + if (num > 0) { + Dbprintf(" UL-C..... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MFULC_KEYS_FILE); + } else { + Dbprintf(" UL-C..... "_RED_("%u")" keys - "_RED_("%s"), num, MFULC_KEYS_FILE); + } + + if (exists_in_spiffs(MFULAES_KEYS_FILE)) { + num = size_in_spiffs(MFULAES_KEYS_FILE) / MFULAES_KEY_LENGTH; + } else { + num = 0; + } + + if (num > 0) { + Dbprintf(" UL-AES... "_YELLOW_("%u")" keys - "_GREEN_("%s"), num, MFULAES_KEYS_FILE); + } else { + Dbprintf(" UL-AES... "_RED_("%u")" keys - "_RED_("%s"), num, MFULAES_KEYS_FILE); + } + + #endif DbpString(""); reply_ng(CMD_STATUS, PM3_SUCCESS, NULL, 0); @@ -1664,13 +1694,13 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_HF_ISO14443A_GET_CONFIG: { - hf14a_config *hf14aconfig = getHf14aConfig(); - reply_ng(CMD_HF_ISO14443A_GET_CONFIG, PM3_SUCCESS, (uint8_t *)hf14aconfig, sizeof(hf14a_config)); + hf14a_config_t *c = getHf14aConfig(); + reply_ng(CMD_HF_ISO14443A_GET_CONFIG, PM3_SUCCESS, (uint8_t *)c, sizeof(hf14a_config_t)); break; } case CMD_HF_ISO14443A_SET_CONFIG: { - hf14a_config c; - memcpy(&c, packet->data.asBytes, sizeof(hf14a_config)); + hf14a_config_t c; + memcpy(&c, packet->data.asBytes, sizeof(hf14a_config_t)); setHf14aConfig(&c); break; } @@ -1719,10 +1749,13 @@ static void PacketReceived(PacketCommandNG *packet) { uint8_t uid[10]; uint8_t exitAfter; uint8_t rats[20]; + bool ulc_p1; + bool ulc_p2; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; SimulateIso14443aTag(payload->tagtype, payload->flags, payload->uid, - payload->exitAfter, payload->rats, sizeof(payload->rats)); // ## Simulate iso14443a tag - pass tag type & UID + payload->exitAfter, payload->rats, sizeof(payload->rats), + payload->ulc_p1, payload->ulc_p2); // ## Simulate iso14443a tag - pass tag type & UID break; } case CMD_HF_ISO14443A_SIM_AID: { @@ -1806,7 +1839,7 @@ static void PacketReceived(PacketCommandNG *packet) { struct p { bool turn_off_field; uint8_t keyno; - uint8_t key[18]; + uint8_t key[16]; } PACKED; struct p *payload = (struct p *) packet->data.asBytes; MifareUL_AES_Auth(payload->turn_off_field, payload->keyno, payload->key); @@ -1969,7 +2002,7 @@ static void PacketReceived(PacketCommandNG *packet) { struct p *payload = (struct p *) packet->data.asBytes; // - size_t size = payload->blockno * payload->blockwidth; + size_t size = payload->blockcnt * payload->blockwidth; if (size > PM3_CMD_DATA_SIZE) { reply_ng(CMD_HF_MIFARE_EML_MEMGET, PM3_EMALLOC, NULL, 0); return; @@ -2226,6 +2259,10 @@ static void PacketReceived(PacketCommandNG *packet) { iclass_credit_epurse((iclass_credit_epurse_t *)packet->data.asBytes); break; } + case CMD_HF_ICLASS_TEARBL: { + iClass_TearBlock((iclass_tearblock_req_t *)packet->data.asBytes); + break; + } #endif #ifdef WITH_HFSNIFF @@ -2354,7 +2391,7 @@ static void PacketReceived(PacketCommandNG *packet) { uint16_t available; uint16_t pre_available = 0; - uint8_t *dest = BigBuf_malloc(USART_FIFOLEN); + uint8_t *dest = BigBuf_calloc(USART_FIFOLEN); uint32_t wait = payload->waittime; StartTicks(); @@ -2398,7 +2435,7 @@ static void PacketReceived(PacketCommandNG *packet) { uint16_t available; uint16_t pre_available = 0; - uint8_t *dest = BigBuf_malloc(USART_FIFOLEN); + uint8_t *dest = BigBuf_calloc(USART_FIFOLEN); uint32_t wait = payload->waittime; StartTicks(); @@ -2694,7 +2731,7 @@ static void PacketReceived(PacketCommandNG *packet) { uint32_t size = packet->oldarg[1]; - uint8_t *buff = BigBuf_malloc(size); + uint8_t *buff = BigBuf_calloc(size); if (buff == NULL) { if (g_dbglevel >= DBG_DEBUG) Dbprintf("Failed to allocate memory"); // Trigger a finish downloading signal with an PM3_EMALLOC @@ -2899,7 +2936,7 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_FLASHMEM_DOWNLOAD: { LED_B_ON(); - uint8_t *mem = BigBuf_malloc(PM3_CMD_DATA_SIZE); + uint8_t *mem = BigBuf_calloc(PM3_CMD_DATA_SIZE); uint32_t startidx = packet->oldarg[0]; uint32_t numofbytes = packet->oldarg[1]; // arg0 = startindex @@ -2931,7 +2968,7 @@ static void PacketReceived(PacketCommandNG *packet) { case CMD_FLASHMEM_INFO: { LED_B_ON(); - rdv40_validation_t *info = (rdv40_validation_t *)BigBuf_malloc(sizeof(rdv40_validation_t)); + rdv40_validation_t *info = (rdv40_validation_t *)BigBuf_calloc(sizeof(rdv40_validation_t)); bool isok = Flash_ReadData(FLASH_MEM_SIGNATURE_OFFSET_P(spi_flash_pages64k), info->signature, FLASH_MEM_SIGNATURE_LEN); diff --git a/armsrc/dbprint.c b/armsrc/dbprint.c index 42d96fc3e..687bdfb90 100644 --- a/armsrc/dbprint.c +++ b/armsrc/dbprint.c @@ -102,9 +102,7 @@ void Dbhexdump(int len, const uint8_t *d, bool bAsci) { } #endif } -void print_result(const char *name, const uint8_t *d, size_t - - n) { +void print_result(const char *name, const uint8_t *d, size_t n) { const uint8_t *p = d; uint16_t tmp = n & 0xFFF0; diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 095ae4240..8de00ccae 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -748,7 +748,7 @@ void em4x50_chk(const char *filename, bool ledcontrol) { uint16_t pwd_count = 0; uint32_t size = size_in_spiffs(filename); pwd_count = size / 4; - uint8_t *pwds = BigBuf_malloc(size); + uint8_t *pwds = BigBuf_calloc(size); rdv40_spiffs_read_as_filetype(filename, pwds, size, RDV40_SPIFFS_SAFETY_SAFE); diff --git a/armsrc/em4x70.c b/armsrc/em4x70.c index 4c2e01ded..ca56a8c2a 100644 --- a/armsrc/em4x70.c +++ b/armsrc/em4x70.c @@ -45,7 +45,7 @@ #define DPRINTF_EXTENDED(x) do { if ((FORCE_ENABLE_LOGGING) || (g_dbglevel >= DBG_EXTENDED)) { Dbprintf x ; } } while (0); #define DPRINTF_PROLIX(x) do { if ((FORCE_ENABLE_LOGGING) || (g_dbglevel > DBG_EXTENDED)) { Dbprintf x ; } } while (0); // EM4170 requires a parity bit on commands, other variants do not. -static bool g_command_parity = true; +static bool g_deprecated_command_parity = false; static em4x70_tag_t g_tag = { 0 }; @@ -905,8 +905,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_id(em4x70_command_bitstream_t bool result = true; memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); out_cmd_bitstream->command = EM4X70_COMMAND_ID; - //uint8_t cmd = with_command_parity ? 0x3u : 0x1u; - uint8_t cmd = 0x3u; + uint8_t cmd = 0x3u; // CMD + Parity bit == 0b001'1 result = result && add_nibble_to_bitstream(&out_cmd_bitstream->to_send, cmd, false); out_cmd_bitstream->to_receive.bitcount = 32; if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { @@ -920,8 +919,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_um1(em4x70_command_bitstream_ bool result = true; memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); out_cmd_bitstream->command = EM4X70_COMMAND_UM1; - //uint8_t cmd = with_command_parity ? 0x5u : 0x2u; - uint8_t cmd = 0x5u; + uint8_t cmd = 0x5u; // CMD + Parity bit == 0b010'1 result = result && add_nibble_to_bitstream(&out_cmd_bitstream->to_send, cmd, false); out_cmd_bitstream->to_receive.bitcount = 32; if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { @@ -935,8 +933,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_um2(em4x70_command_bitstream_ bool result = true; memset(out_cmd_bitstream, 0, sizeof(em4x70_command_bitstream_t)); out_cmd_bitstream->command = EM4X70_COMMAND_UM2; - //uint8_t cmd = with_command_parity ? 0xFu : 0x7u; - uint8_t cmd = 0xFu; + uint8_t cmd = 0xFu; // CMD + Parity bit == 0b111'1 result = result && add_nibble_to_bitstream(&out_cmd_bitstream->to_send, cmd, false); out_cmd_bitstream->to_receive.bitcount = 64; if (out_cmd_bitstream->to_send.bitcount != expected_bits_to_send) { @@ -954,8 +951,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_auth(em4x70_command_bitstream em4x70_bitstream_t *s = &out_cmd_bitstream->to_send; - // uint8_t cmd = with_command_parity ? 0x6u : 0x3u; - uint8_t cmd = 0x6u; // HACK - always sent with cmd parity + uint8_t cmd = 0x6u; // CMD + Parity bit == 0b011'0 result = result && add_nibble_to_bitstream(s, cmd, false); // Reader: [RM][0][Command][N55..N0][0000000][f(RN)27..f(RN)0] @@ -1004,8 +1000,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_pin(em4x70_command_bitstream_ out_cmd_bitstream->command = EM4X70_COMMAND_PIN; - //uint8_t cmd = with_command_parity ? 0x9u : 0x4u; - uint8_t cmd = 0x9u; // HACK - always sent with cmd parity, with extra zero bit in RM? + uint8_t cmd = 0x9u; // CMD + Parity bit == 0b100'1 result = result && add_nibble_to_bitstream(s, cmd, false); // Send tag's ID ... indexes 4 .. 35 @@ -1037,8 +1032,7 @@ static bool create_legacy_em4x70_bitstream_for_cmd_write(em4x70_command_bitstrea em4x70_bitstream_t *s = &out_cmd_bitstream->to_send; - //uint8_t cmd = with_command_parity ? 0xAu : 0x5u; - uint8_t cmd = 0xAu; // HACK - always sent with cmd parity, with extra zero bit in RM? + uint8_t cmd = 0xAu; // CMD + Parity bit == 0b101'0 result = result && add_nibble_to_bitstream(s, cmd, false); if ((address & 0x0Fu) != address) { @@ -1097,7 +1091,7 @@ static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *respon em4x70_command_bitstream_t auth_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->auth(&auth_cmd, g_command_parity, rnd, frnd); + generator->auth(&auth_cmd, g_deprecated_command_parity, rnd, frnd); bool result = send_bitstream_and_read(&auth_cmd); if (result) { @@ -1185,7 +1179,7 @@ static int bruteforce(const uint8_t address, const uint8_t *rnd, const uint8_t * static int send_pin(const uint32_t pin) { em4x70_command_bitstream_t send_pin_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->pin(&send_pin_cmd, g_command_parity, &g_tag.data[4], pin); + generator->pin(&send_pin_cmd, g_deprecated_command_parity, &g_tag.data[4], pin); bool result = send_bitstream_wait_ack_wait_read(&send_pin_cmd); return result ? PM3_SUCCESS : PM3_ESOFT; @@ -1196,7 +1190,7 @@ static int write(const uint16_t word, const uint8_t address) { em4x70_command_bitstream_t write_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->write(&write_cmd, g_command_parity, word, address); + generator->write(&write_cmd, g_deprecated_command_parity, word, address); bool result = send_bitstream_wait_ack_wait_ack(&write_cmd); if (!result) { @@ -1283,7 +1277,7 @@ static uint8_t encoded_bit_array_to_byte(const uint8_t *bits, int count_of_bits) static bool em4x70_read_id(void) { em4x70_command_bitstream_t read_id_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->id(&read_id_cmd, g_command_parity); + generator->id(&read_id_cmd, g_deprecated_command_parity); bool result = send_bitstream_and_read(&read_id_cmd); if (result) { @@ -1300,7 +1294,7 @@ static bool em4x70_read_id(void) { static bool em4x70_read_um1(void) { em4x70_command_bitstream_t read_um1_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->um1(&read_um1_cmd, g_command_parity); + generator->um1(&read_um1_cmd, g_deprecated_command_parity); bool result = send_bitstream_and_read(&read_um1_cmd); if (result) { @@ -1319,7 +1313,7 @@ static bool em4x70_read_um1(void) { static bool em4x70_read_um2(void) { em4x70_command_bitstream_t read_um2_cmd; const em4x70_command_generators_t *generator = &legacy_em4x70_command_generators; - generator->um2(&read_um2_cmd, g_command_parity); + generator->um2(&read_um2_cmd, g_deprecated_command_parity); bool result = send_bitstream_and_read(&read_um2_cmd); if (result) { @@ -1435,7 +1429,7 @@ void em4x70_info(const em4x70_data_t *etd, bool ledcontrol) { bool success_with_UM2 = false; // Support tags with and without command parity bits - g_command_parity = etd->parity; + g_deprecated_command_parity = false; init_tag(); em4x70_setup_read(); @@ -1463,10 +1457,10 @@ void em4x70_info(const em4x70_data_t *etd, bool ledcontrol) { void em4x70_write(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (g_command_parity) { + if (g_deprecated_command_parity) { DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 write` is non-functional and may corrupt data on the tag.")); // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); // return; @@ -1499,7 +1493,7 @@ void em4x70_unlock(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; init_tag(); em4x70_setup_read(); @@ -1534,10 +1528,10 @@ void em4x70_auth(const em4x70_data_t *etd, bool ledcontrol) { uint8_t response[3] = {0}; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (g_command_parity) { + if (g_deprecated_command_parity) { DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 auth` is non-functional.")); // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); // return; @@ -1562,10 +1556,10 @@ void em4x70_brute(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; uint8_t response[2] = {0}; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (g_command_parity) { + if (g_deprecated_command_parity) { DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 brute` is non-functional and may corrupt data on the tag.")); // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); // return; @@ -1590,10 +1584,10 @@ void em4x70_write_pin(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (g_command_parity) { + if (g_deprecated_command_parity) { DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 setpin` is non-functional and may corrupt data on the tag.")); // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); // return; @@ -1639,10 +1633,10 @@ void em4x70_write_key(const em4x70_data_t *etd, bool ledcontrol) { int status = PM3_ESOFT; - g_command_parity = etd->parity; + g_deprecated_command_parity = false; // Disable to prevent sending corrupted data to the tag. - if (g_command_parity) { + if (g_deprecated_command_parity) { DPRINTF_ALWAYS(("Use of `--par` option with `lf em 4x70 setkey` is non-functional and may corrupt data on the tag.")); // reply_ng(CMD_LF_EM4X70_WRITE, PM3_ENOTIMPL, NULL, 0); // return; diff --git a/armsrc/i2c.c b/armsrc/i2c.c index 501ce388e..b1af6e30a 100644 --- a/armsrc/i2c.c +++ b/armsrc/i2c.c @@ -857,7 +857,7 @@ void SmartCardRaw(const smart_card_raw_t *p) { LED_D_ON(); uint16_t len = 0; - uint8_t *resp = BigBuf_malloc(ISO7816_MAX_FRAME); + uint8_t *resp = BigBuf_calloc(ISO7816_MAX_FRAME); // check if alloacted... smartcard_command_t flags = p->flags; @@ -937,7 +937,7 @@ void SmartCardUpgrade(uint64_t arg0) { bool isOK = true; uint16_t length = arg0, pos = 0; const uint8_t *fwdata = BigBuf_get_addr(); - uint8_t *verfiydata = BigBuf_malloc(I2C_BLOCK_SIZE); + uint8_t *verfiydata = BigBuf_calloc(I2C_BLOCK_SIZE); while (length) { diff --git a/armsrc/i2c.h b/armsrc/i2c.h index 3b45c6d7d..b45832acd 100644 --- a/armsrc/i2c.h +++ b/armsrc/i2c.h @@ -36,7 +36,7 @@ // 8051 speaks with smart card. // 1000*50*3.07 = 153.5ms // 1 byte transfer == 1ms with max frame being 256 bytes -#define SIM_WAIT_DELAY 88000 // about 270ms delay // 109773 -- about 337.7ms delay +#define SIM_WAIT_DELAY 150000 // about 270ms delay // 109773 -- about 337.7ms delay void I2C_recovery(void); diff --git a/armsrc/i2c_direct.c b/armsrc/i2c_direct.c index 909c1ec30..49aaa4c2c 100644 --- a/armsrc/i2c_direct.c +++ b/armsrc/i2c_direct.c @@ -40,7 +40,7 @@ static void SmartCardDirectSend(uint8_t prepend, const smart_card_raw_t *p, uint LED_D_ON(); uint16_t len = 0; - uint8_t *resp = BigBuf_malloc(ISO7816_MAX_FRAME); + uint8_t *resp = BigBuf_calloc(ISO7816_MAX_FRAME); resp[0] = prepend; // check if alloacted... smartcard_command_t flags = p->flags; diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 6b79b7012..3837d6931 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -38,6 +38,7 @@ #include "iso15693.h" #include "iclass_cmd.h" // iclass_card_select_t struct #include "i2c.h" // i2c defines (SIM module access) +#include "printf.h" uint8_t get_pagemap(const picopass_hdr_t *hdr) { return (hdr->conf.fuses & (FUSE_CRYPT0 | FUSE_CRYPT1)) >> 3; @@ -195,7 +196,7 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ if (send_reply) reply_old(CMD_ACK, CMD_HF_ICLASS_SIMULATE, i, 0, mac_responses, i * EPURSE_MAC_SIZE); - } else if (sim_type == ICLASS_SIM_MODE_FULL) { + } else if (sim_type == ICLASS_SIM_MODE_FULL || sim_type == ICLASS_SIM_MODE_FULL_GLITCH || sim_type == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { //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 @@ -204,19 +205,13 @@ void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_ if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { do_iclass_simulation_nonsec(); } else { - do_iclass_simulation(ICLASS_SIM_MODE_FULL, NULL); + do_iclass_simulation(sim_type, NULL); } if (send_reply) { reply_mix(CMD_ACK, CMD_HF_ICLASS_SIMULATE, 0, 0, NULL, 0); } - } else if (sim_type == ICLASS_SIM_MODE_CONFIG_CARD) { - - // config card - do_iclass_simulation(ICLASS_SIM_MODE_FULL, NULL); - // swap bin - } else if (sim_type == ICLASS_SIM_MODE_READER_ATTACK_KEYROLL) { // This is the KEYROLL version of sim 2. @@ -333,7 +328,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { // AIA uint8_t aia_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { memcpy(conf_block, emulator + (8 * 1), 8); // blk 1 memcpy(card_challenge_data, emulator + (8 * 2), 8); // e-purse, blk 2 @@ -372,7 +367,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { cipher_state_KD[0] = opt_doTagMAC_1(card_challenge_data, diversified_kd); cipher_state_KC[0] = opt_doTagMAC_1(card_challenge_data, diversified_kc); - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { for (int i = 1; i < max_page; i++) { @@ -385,6 +380,8 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { } } + bool glitch_key_read = false; + // Anti-collision process: // Reader 0a // Tag 0f @@ -399,40 +396,40 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { int trace_data_size; // Respond SOF -- takes 1 bytes - uint8_t *resp_sof = BigBuf_malloc(1); + uint8_t resp_sof[2] = {0}; int resp_sof_len; // Anticollision CSN (rotated CSN) // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) - uint8_t *resp_anticoll = BigBuf_malloc(22); + uint8_t *resp_anticoll = BigBuf_calloc(22); int resp_anticoll_len; // CSN (block 0) // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) - uint8_t *resp_csn = BigBuf_malloc(22); + uint8_t *resp_csn = BigBuf_calloc(22); int resp_csn_len; // configuration (blk 1) PICOPASS 2ks - uint8_t *resp_conf = BigBuf_malloc(22); + uint8_t *resp_conf = BigBuf_calloc(22); int resp_conf_len; // e-Purse (blk 2) // 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit) - uint8_t *resp_cc = BigBuf_malloc(18); + uint8_t *resp_cc = BigBuf_calloc(18); int resp_cc_len; // Kd, Kc (blocks 3 and 4). Cannot be read. Always respond with 0xff bytes only - uint8_t *resp_ff = BigBuf_malloc(22); + uint8_t *resp_ff = BigBuf_calloc(22); int resp_ff_len; uint8_t ff_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; AddCrc(ff_data, 8); // Application Issuer Area (blk 5) - uint8_t *resp_aia = BigBuf_malloc(22); + uint8_t *resp_aia = BigBuf_calloc(22); int resp_aia_len; // receive command - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); // Prepare card messages tosend_t *ts = get_tosend(); @@ -474,11 +471,11 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { //This is used for responding to READ-block commands or other data which is dynamically generated //First the 'trace'-data, not encoded for FPGA - uint8_t *data_generic_trace = BigBuf_malloc(34); // 32 bytes data + 2byte CRC is max tag answer + uint8_t *data_generic_trace = BigBuf_calloc(34); // 32 bytes data + 2byte CRC is max tag answer //Then storage for the modulated data //Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes) - uint8_t *data_response = BigBuf_malloc((34 * 2) + 3); + uint8_t *data_response = BigBuf_calloc((34 * 2) + 3); enum { IDLE, ACTIVATED, SELECTED, HALTED } chip_state = IDLE; @@ -605,13 +602,27 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { goto send; } } // switch - } else if (simulationMode == ICLASS_SIM_MODE_FULL) { + } else if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { if (block == 3 || block == 4) { // Kd, Kc, always respond with 0xff bytes modulated_response = resp_ff; modulated_response_size = resp_ff_len; trace_data = ff_data; trace_data_size = sizeof(ff_data); } else { // use data from emulator memory + if (simulationMode == ICLASS_SIM_MODE_FULL_GLITCH) { + //Jam the read based on the last SIO block + if (memcmp(emulator + (current_page * page_size) + (5 * 8), ff_data, PICOPASS_BLOCK_SIZE) == 0) { //SR card + if (block == 16) { //SR cards use a standard legth SIO + goto send; + } + } else { //For SE cards we have to account for different SIO lengths depending if a standard or custom key is used + uint8_t *sio = emulator + (current_page * page_size) + (6 * 8); + if (block == (5 + ((sio[1] + 12) / 8))) { + goto send; + } + } + } + memcpy(data_generic_trace, emulator + (current_page * page_size) + (block * 8), 8); AddCrc(data_generic_trace, 8); trace_data = data_generic_trace; @@ -654,7 +665,12 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { goto send; } - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { + + if (glitch_key_read) { + goto send; + } + // NR, from reader, is in receivedCmd +1 opt_doTagMAC_2(*cipher_state, receivedCmd + 1, data_generic_trace, diversified_key); @@ -721,7 +737,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { chip_state = HALTED; goto send; - } else if (simulationMode == ICLASS_SIM_MODE_FULL && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 + } else if ((simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) && cmd == ICLASS_CMD_READ4 && len == 4) { // 0x06 if (chip_state != SELECTED) { goto send; @@ -762,20 +778,24 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { resp_cc_len = ts->max; cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { memcpy(emulator + (current_page * page_size) + (8 * 2), card_challenge_data, 8); } } else if (block == 3) { // update Kd for (int i = 0; i < 8; i++) { - if (personalization_mode) { + if (personalization_mode || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { diversified_kd[i] = receivedCmd[2 + i]; } else { diversified_kd[i] ^= receivedCmd[2 + i]; } } cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kd); - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { memcpy(emulator + (current_page * page_size) + (8 * 3), diversified_kd, 8); + if (simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { + glitch_key_read = true; + goto send; + } } } else if (block == 4) { // update Kc for (int i = 0; i < 8; i++) { @@ -786,14 +806,30 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { } } cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_kc); - if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { memcpy(emulator + (current_page * page_size) + (8 * 4), diversified_kc, 8); } - } else if (simulationMode == ICLASS_SIM_MODE_FULL) { + } else if (simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) { // update emulator memory memcpy(emulator + (current_page * page_size) + (8 * block), receivedCmd + 2, 8); } + if (simulationMode == ICLASS_SIM_MODE_FULL_GLITCH) { + //Jam the read based on the last SIO block + uint8_t *sr_or_sio = emulator + (current_page * page_size) + (6 * 8); + if (memcmp(emulator + (current_page * page_size) + (5 * 8), ff_data, PICOPASS_BLOCK_SIZE) == 0) { //SR card + if (block == 16) { //SR cards use a standard legth SIO + //update block 6 byte 1 from 03 to A3 + sr_or_sio[0] |= 0xA0; + goto send; + } + } else { //For SE cards we have to account for different SIO lengths depending if a standard or custom key is used + if (block == (5 + ((sr_or_sio[1] + 12) / 8))) { + goto send; + } + } + } + memcpy(data_generic_trace, receivedCmd + 2, 8); AddCrc(data_generic_trace, 8); trace_data = data_generic_trace; @@ -813,7 +849,7 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { goto send; } - if (simulationMode == ICLASS_SIM_MODE_FULL && max_page > 0) { + if ((simulationMode == ICLASS_SIM_MODE_FULL || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH || simulationMode == ICLASS_SIM_MODE_FULL_GLITCH_KEY) && max_page > 0) { // if on 2k, always ignore 3msb, & 0x1F) uint8_t page = receivedCmd[1] & 0x1F; @@ -942,29 +978,29 @@ int do_iclass_simulation_nonsec(void) { int trace_data_size = 0; // Respond SOF -- takes 1 bytes - uint8_t *resp_sof = BigBuf_malloc(2); + uint8_t resp_sof[2] = { 0 }; int resp_sof_len; // Anticollision CSN (rotated CSN) // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) - uint8_t *resp_anticoll = BigBuf_malloc(28); + uint8_t *resp_anticoll = BigBuf_calloc(28); int resp_anticoll_len; // CSN // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) - uint8_t *resp_csn = BigBuf_malloc(28); + uint8_t *resp_csn = BigBuf_calloc(28); int resp_csn_len; // configuration (blk 1) PICOPASS 2ks - uint8_t *resp_conf = BigBuf_malloc(28); + uint8_t *resp_conf = BigBuf_calloc(28); int resp_conf_len; // Application Issuer Area (blk 5) - uint8_t *resp_aia = BigBuf_malloc(28); + uint8_t *resp_aia = BigBuf_calloc(28); int resp_aia_len; // receive command - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); + uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); // Prepare card messages tosend_t *ts = get_tosend(); @@ -997,11 +1033,11 @@ int do_iclass_simulation_nonsec(void) { //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(32 + 2); // 32 bytes data + 2byte CRC is max tag answer + uint8_t *data_generic_trace = BigBuf_calloc(32 + 2); // 32 bytes data + 2byte CRC is max tag answer //Then storage for the modulated data //Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes) - uint8_t *data_response = BigBuf_malloc((32 + 2) * 2 + 2); + uint8_t *data_response = BigBuf_calloc((32 + 2) * 2 + 2); enum { IDLE, ACTIVATED, SELECTED, HALTED } chip_state = IDLE; @@ -1244,7 +1280,6 @@ static bool iclass_send_cmd_with_retries(uint8_t *cmd, size_t cmdsize, uint8_t * while (tries-- > 0) { iclass_send_as_reader(cmd, cmdsize, start_time, eof_time, shallow_mod); - if (resp == NULL) { return true; } @@ -1582,8 +1617,9 @@ bool iclass_read_block(uint16_t blockno, uint8_t *data, uint32_t *start_time, ui uint8_t c[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; AddCrc(c + 1, 1); bool isOK = iclass_send_cmd_with_retries(c, sizeof(c), resp, sizeof(resp), 10, 2, start_time, ICLASS_READER_TIMEOUT_OTHERS, eof_time, shallow_mod); - if (isOK) + if (isOK) { memcpy(data, resp, 8); + } return isOK; } @@ -1780,13 +1816,13 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, } } else if (blockno == 3 || blockno == 4) { // check response. Key updates always return 0xffffffffffffffff - uint8_t all_ff[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - if (memcmp(all_ff, resp, 8)) { + uint8_t all_ff[PICOPASS_BLOCK_SIZE] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + if (memcmp(all_ff, resp, PICOPASS_BLOCK_SIZE)) { return false; } } else { // check response. All other updates return unchanged data - if (memcmp(data, resp, 8)) { + if (memcmp(data, resp, PICOPASS_BLOCK_SIZE)) { return false; } } @@ -1794,8 +1830,66 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, return true; } +static bool iclass_writeblock_sp(uint8_t blockno, uint8_t *data, uint8_t *mac, bool shallow_mod, uint32_t *start_time, uint32_t *eof_time, bool short_delay) { + + // write command: cmd, 1 blockno, 8 data, 4 mac + uint8_t write[14] = { 0x80 | ICLASS_CMD_UPDATE, blockno }; + uint8_t write_len = 14; + memcpy(write + 2, data, 8); + memcpy(write + 10, mac, 4); + + uint8_t resp[10] = {0}; + bool isOK = false; + if (short_delay) { + isOK = iclass_send_cmd_with_retries(write, write_len, resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_UPDATE_FAST, eof_time, shallow_mod); + } else { + isOK = iclass_send_cmd_with_retries(write, write_len, resp, sizeof(resp), 10, 3, start_time, ICLASS_READER_TIMEOUT_UPDATE, eof_time, shallow_mod); + } + if (isOK == false) { + return false; + } + + // check response. All other updates return unchanged data + if (memcmp(data, resp, PICOPASS_BLOCK_SIZE)) { + return false; + } + + return true; +} + +uint8_t credit_key[8] = {0xFD, 0xCB, 0x5A, 0x52, 0xEA, 0x8F, 0x30, 0x90}; + +static bool do_privilege_escalation(uint8_t *read_check_cc, size_t cc_len, uint32_t *eof_time) { + + int priv_esc_tries = 5; + + while (priv_esc_tries--) { + + uint16_t resp_len = 0; + uint8_t resp[10] = {0}; + //The privilege escalation is done with a readcheck and not just a normal read! + uint32_t start_time = *eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + iclass_send_as_reader(read_check_cc, cc_len, &start_time, eof_time, false); + // expect a 8-byte response here + int res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, eof_time, false, true, &resp_len); + if (res == PM3_SUCCESS && resp_len == 8) { + return true; + } + } + + if (g_dbglevel == DBG_INFO) { + DbpString(""); + DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); + } + return false; +} + // turn off afterwards void iClass_WriteBlock(uint8_t *msg) { + bool priv_esc = false; + uint8_t read_check_cc[] = { 0x10 | ICLASS_CMD_READCHECK, 0x18 }; + uint8_t div_cc[8] = {0}; LED_A_ON(); @@ -1815,6 +1909,9 @@ void iClass_WriteBlock(uint8_t *msg) { goto out; } + iclass_calc_div_key(hdr.csn, credit_key, div_cc, false); + read_check_cc[1] = hdr.conf.app_limit + 1; //first block of AA2 + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; uint8_t mac[4] = {0}; @@ -1829,7 +1926,7 @@ void iClass_WriteBlock(uint8_t *msg) { } // new block data - memcpy(write + 2, payload->data, 8); + memcpy(write + 2, payload->data, PICOPASS_BLOCK_SIZE); uint8_t pagemap = get_pagemap(&hdr); if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { @@ -1841,20 +1938,27 @@ void iClass_WriteBlock(uint8_t *msg) { write_len -= 2; } else { - if (payload->req.use_replay) { + if (payload->req.use_replay && (memcmp(payload->mac, "\x00\x00\x00\x00", 4) != 0)) { memcpy(write + 10, payload->mac, sizeof(payload->mac)); } else { - // Secure tags uses MAC - uint8_t wb[9]; - wb[0] = payload->req.blockno; - memcpy(wb + 1, payload->data, 8); + // Secure tags uses MAC + uint8_t wb[9]; + wb[0] = payload->req.blockno; + memcpy(wb + 1, payload->data, PICOPASS_BLOCK_SIZE); - if (payload->req.use_credit_key) - doMAC_N(wb, sizeof(wb), hdr.key_c, mac); - else - doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + if (payload->req.use_credit_key){ + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + } else if (payload->req.use_replay) { + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (priv_esc == false) { + goto out; + } + doMAC_N(wb, sizeof(wb), div_cc, mac); + }else{ + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + } - memcpy(write + 10, mac, sizeof(mac)); + memcpy(write + 10, mac, sizeof(mac)); } } @@ -2074,7 +2178,425 @@ out: reply_ng(CMD_HF_ICLASS_CREDIT_EPURSE, PM3_SUCCESS, (uint8_t *)&res, sizeof(uint8_t)); } +static void iclass_cmp_print(uint8_t *b1, uint8_t *b2, const char *header1, const char *header2) { + + char line1[240] = {0}; + char line2[240] = {0}; + + strcat(line1, header1); + strcat(line2, header2); + + for (uint8_t i = 0; i < PICOPASS_BLOCK_SIZE; i++) { + + int l1 = strlen(line1); + int l2 = strlen(line2); + + uint8_t hi1 = NIBBLE_HIGH(b1[i]); + uint8_t low1 = NIBBLE_LOW(b1[i]); + + uint8_t hi2 = NIBBLE_HIGH(b2[i]); + uint8_t low2 = NIBBLE_LOW(b2[i]); + + if (hi1 != hi2) { + sprintf(line1 + l1, _RED_("%1X"), hi1); + sprintf(line2 + l2, _GREEN_("%1X"), hi2); + } else { + sprintf(line1 + l1, "%1X", hi1); + sprintf(line2 + l2, "%1X", hi2); + } + + l1 = strlen(line1); + l2 = strlen(line2); + + if (low1 != low2) { + sprintf(line1 + l1, _RED_("%1X"), low1); + sprintf(line2 + l2, _GREEN_("%1X"), low2); + } else { + sprintf(line1 + l1, "%1X", low1); + sprintf(line2 + l2, "%1X", low2); + } + } + DbpString(line1); + DbpString(line2); +} + +void iClass_TearBlock(iclass_tearblock_req_t *msg) { + + if (msg == NULL) { + reply_ng(CMD_HF_ICLASS_TEARBL, PM3_ESOFT, NULL, 0); + return; + } + + // local variable copies + int tear_start = msg->tear_start; + int tear_end = msg->tear_end; + int tear_inc = msg->increment; + int tear_loop = msg->tear_loop; + + int loop_count = 0; + + uint32_t start_time = 0; + uint32_t eof_time = 0; + + int isok = PM3_SUCCESS; + + uint8_t data[8] = {0}; + memcpy(data, msg->data, sizeof(data)); + + uint8_t mac[4] = {0}; + memcpy(mac, msg->mac, sizeof(mac)); + + picopass_hdr_t hdr = {0}; + iclass_auth_req_t req = { + .blockno = msg->req.blockno, + .do_auth = msg->req.do_auth, + .send_reply = msg->req.send_reply, + .shallow_mod = msg->req.shallow_mod, + .use_credit_key = msg->req.use_credit_key, + .use_elite = msg->req.use_elite, + .use_raw = msg->req.use_raw, + .use_replay = msg->req.use_replay + }; + memcpy(req.key, msg->req.key, PICOPASS_BLOCK_SIZE); + + LED_A_ON(); + Iso15693InitReader(); + + // save old debug log level + int oldbg = g_dbglevel; + + // no debug logging please + g_dbglevel = DBG_NONE; + + // select + bool res = select_iclass_tag(&hdr, req.use_credit_key, &eof_time, req.shallow_mod); + if (res == false) { + DbpString(_RED_("Failed to select iClass tag")); + isok = PM3_ECARDEXCHANGE; + goto out; + } + + // authenticate + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&req, &hdr, &start_time, &eof_time, mac); + if (res == false) { + DbpString(_RED_("Failed to authenticate with iClass tag")); + isok = PM3_ECARDEXCHANGE; + goto out; + } + + uint8_t data_read_orig[PICOPASS_BLOCK_SIZE] = {0}; + + // read block + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = iclass_read_block(req.blockno, data_read_orig, &start_time, &eof_time, req.shallow_mod); + if (res == false) { + Dbprintf("Failed to read block %u", req.blockno); + isok = PM3_ECARDEXCHANGE; + goto out; + } + + bool erase_phase = false; + bool read_ok = false; + + // static uint8_t empty[PICOPASS_BLOCK_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + static uint8_t zeros[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint8_t ff_data[PICOPASS_BLOCK_SIZE] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + uint8_t data_read[PICOPASS_BLOCK_SIZE] = {0}; + + // create READ command + uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, req.blockno, 0x00, 0x00}; + AddCrc(cmd_read + 1, 1); + + // create WRITE COMMAND and new block data + uint8_t cmd_write[14] = { 0x80 | ICLASS_CMD_UPDATE, req.blockno }; + uint8_t cmd_write_len = 14; + memcpy(cmd_write + 2, data, PICOPASS_BLOCK_SIZE); + + uint8_t pagemap = get_pagemap(&hdr); + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + // Unsecured tags uses CRC16, but don't include the UPDATE operation code + // byte0 = update op + // byte1 = block no + // byte2..9 = new block data + AddCrc(cmd_write + 1, 9); + cmd_write_len -= 2; + } else { + + if (req.use_replay) { + memcpy(cmd_write + 10, mac, sizeof(mac)); + } else { + // Secure tags uses MAC + uint8_t wb[9]; + wb[0] = req.blockno; + memcpy(wb + 1, data, PICOPASS_BLOCK_SIZE); + + if (req.use_credit_key) + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + else + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + + memcpy(cmd_write + 10, mac, sizeof(mac)); + } + } + + // Main loop + while ((tear_start <= tear_end) && (read_ok == false)) { + + if (BUTTON_PRESS() || data_available()) { + isok = PM3_EOPABORTED; + goto out; + } + + // set tear off trigger + g_tearoff_enabled = true; + g_tearoff_delay_us = (tear_start & 0xFFFF); + + if (tear_loop > 1) { + DbprintfEx(FLAG_INPLACE, "[" _BLUE_("#") "] Tear off delay " _YELLOW_("%u") " / " _YELLOW_("%u") " us - " _YELLOW_("%3u") " iter", tear_start, tear_end, loop_count + 1); + } else { + DbprintfEx(FLAG_INPLACE, "[" _BLUE_("#") "] Tear off delay " _YELLOW_("%u") " / " _YELLOW_("%u") " us", tear_start, tear_end); + } + + // write block + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(cmd_write, cmd_write_len, &start_time, &eof_time, req.shallow_mod); + + tearoff_hook(); + + switch_off(); + + // start reading block + + // reinit + Iso15693InitReader(); + + // select tag + res = select_iclass_tag(&hdr, req.use_credit_key, &eof_time, req.shallow_mod); + if (res == false) { + continue; + } + + // skip authentication for config and e-purse blocks (1,2) + if (req.blockno > 2) { + + // authenticate + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&req, &hdr, &start_time, &eof_time, NULL); + if (res == false) { + DbpString("Failed to authenticate after tear"); + continue; + } + } + + // read again and keep field on + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = iclass_read_block(req.blockno, data_read, &start_time, &eof_time, req.shallow_mod); + if (res == false) { + DbpString("Failed to read block after tear"); + continue; + } + + // + bool tear_success = true; + + if (memcmp(data_read, data, PICOPASS_BLOCK_SIZE) != 0) { + tear_success = false; + } + + if ((tear_success == false) && + (memcmp(data_read, zeros, PICOPASS_BLOCK_SIZE) != 0) && + (memcmp(data_read, data_read_orig, PICOPASS_BLOCK_SIZE) != 0)) { + + // tearoff succeeded (partially) + + if (memcmp(data_read, ff_data, PICOPASS_BLOCK_SIZE) == 0 && + memcmp(data_read_orig, ff_data, PICOPASS_BLOCK_SIZE) != 0) { + + if (erase_phase == false) { + DbpString(""); + DbpString(_CYAN_("Erase phase hit... ALL ONES")); + + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } + erase_phase = true; + + } else { + + if (erase_phase) { + DbpString(""); + DbpString(_MAGENTA_("Tearing! Write phase (post erase)")); + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } else { + DbpString(""); + DbpString(_CYAN_("Tearing! unknown phase")); + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } + } + + // shall we exit? well it depends on some things. + bool goto_out = false; + + if (req.blockno == 2) { + if (memcmp(data_read, ff_data, PICOPASS_BLOCK_SIZE) == 0 && memcmp(data_read_orig, ff_data, PICOPASS_BLOCK_SIZE) != 0) { + DbpString(""); + Dbprintf("E-purse has been teared ( %s )", _GREEN_("ok")); + isok = PM3_SUCCESS; + goto_out = true; + } + } + + if (req.blockno == 1) { + + // if more OTP bits set.. + if (data_read[1] > data_read_orig[1] || + data_read[2] > data_read_orig[2]) { + + + // step 4 if bits changed attempt to write the new bits to the tag + if (data_read[7] == 0xBC) { + data_read[7] = 0xAC; + } + + // prepare WRITE command + cmd_write_len = 14; + memcpy(cmd_write + 2, data_read, PICOPASS_BLOCK_SIZE); + + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + // Unsecured tags uses CRC16, but don't include the UPDATE operation code + // byte0 = update op + // byte1 = block no + // byte2..9 = new block data + AddCrc(cmd_write + 1, 9); + cmd_write_len -= 2; + } else { + + if (req.use_replay) { + memcpy(cmd_write + 10, mac, sizeof(mac)); + } else { + // Secure tags uses MAC + uint8_t wb[9]; + wb[0] = req.blockno; + memcpy(wb + 1, data_read, PICOPASS_BLOCK_SIZE); + + if (req.use_credit_key) + doMAC_N(wb, sizeof(wb), hdr.key_c, mac); + else + doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + + memcpy(cmd_write + 10, mac, sizeof(mac)); + } + } + + // write block + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(cmd_write, cmd_write_len, &start_time, &eof_time, req.shallow_mod); + + uint16_t resp_len = 0; + uint8_t resp[ICLASS_BUFFER_SIZE] = {0}; + res = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_UPDATE, &eof_time, false, true, &resp_len); + if (res == PM3_SUCCESS && resp_len == 10) { + Dbprintf("Wrote to block"); + } + + switch_off(); + + DbpString(""); + DbpString("More OTP bits got set!!!"); + + Iso15693InitReader(); + + // select tag, during which we read block1 + res = select_iclass_tag(&hdr, req.use_credit_key, &eof_time, req.shallow_mod); + if (res) { + + if (memcmp(&hdr.conf, cmd_write + 2, PICOPASS_BLOCK_SIZE) == 0) { + Dbprintf("Stabilize the bits ( "_GREEN_("ok") " )"); + } else { + Dbprintf("Stabilize the bits ( "_RED_("failed") " )"); + } + } + + isok = PM3_SUCCESS; + goto_out = true; + } + + if (data_read[0] != data_read_orig[0]) { + DbpString(""); + Dbprintf("Application limit changed, from "_YELLOW_("%u")" to "_YELLOW_("%u"), data_read_orig[0], data_read[0]); + isok = PM3_SUCCESS; + goto_out = true; + } + + if (data_read[7] != data_read_orig[7]) { + DbpString(""); + Dbprintf("Fuse changed, from "_YELLOW_("%02x")" to "_YELLOW_("%02x"), data_read_orig[7], data_read[7]); + + const char *flag_names[8] = { + "RA", + "Fprod0", + "Fprod1", + "Crypt0 (*1)", + "Crypt1 (*0)", + "Coding0", + "Coding1", + "Fpers (*1)" + }; + Dbprintf(_YELLOW_("%-10s %-10s %-10s"), "Fuse", "Original", "Changed"); + Dbprintf("---------------------------------------"); + for (int i = 7; i >= 0; --i) { + int bit1 = (data_read_orig[7] >> i) & 1; + int bit2 = (data_read[7] >> i) & 1; + Dbprintf("%-11s %-10d %-10d", flag_names[i], bit1, bit2); + } + + isok = PM3_SUCCESS; + goto_out = true; + } + } + + if (goto_out) { + goto out; + } + } + + // tearoff succeeded with expected values, which is unlikely + if (tear_success) { + read_ok = true; + tear_success = true; + DbpString(""); + DbpString("tear success (expected values)!"); + } + + loop_count++; + + // increase tear off delay + if (loop_count == tear_loop) { + tear_start += tear_inc; + loop_count = 0; + } + } + +out: + + switch_off(); + + // reset tear off trigger + g_tearoff_enabled = false; + + // restore debug message levels + g_dbglevel = oldbg; + + if (msg->req.send_reply) { + reply_ng(CMD_HF_ICLASS_TEARBL, isok, NULL, 0); + } +} + void iClass_Restore(iclass_restore_req_t *msg) { + bool priv_esc = false; + uint8_t read_check_cc[] = { 0x10 | ICLASS_CMD_READCHECK, 0x18 }; + uint8_t div_cc[8] = {0}; // sanitation if (msg == NULL) { @@ -2103,7 +2625,9 @@ void iClass_Restore(iclass_restore_req_t *msg) { if (res == false) { goto out; } + iclass_calc_div_key(hdr.csn, credit_key, div_cc, false); + read_check_cc[1] = hdr.conf.app_limit + 1; //first block of AA2 // authenticate uint8_t mac[4] = {0}; uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; @@ -2133,10 +2657,17 @@ void iClass_Restore(iclass_restore_req_t *msg) { wb[0] = item.blockno; memcpy(wb + 1, item.data, 8); - if (msg->req.use_credit_key) + if (msg->req.use_credit_key) { doMAC_N(wb, sizeof(wb), hdr.key_c, mac); - else + } else if (msg->req.use_replay) { + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (priv_esc == false) { + goto out; + } + doMAC_N(wb, sizeof(wb), div_cc, mac); + } else { doMAC_N(wb, sizeof(wb), hdr.key_d, mac); + } } // data + mac @@ -2160,7 +2691,7 @@ out: static void generate_single_key_block_inverted_opt(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { uint8_t bits_index = index / 16383; - uint8_t ending_bits[] = { //all possible 70 combinations of 4x0 and 4x1 as key ending bits + uint8_t ending_bits[] = { // all possible 70 combinations of 4x0 and 4x1 as key ending bits 0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D, 0x2E, 0x33, 0x35, 0x36, 0x39, 0x3A, 0x3C, 0x47, 0x4B, 0x4D, 0x4E, 0x53, 0x55, 0x56, 0x59, 0x5A, 0x5C, 0x63, 0x65, 0x66, 0x69, 0x6A, @@ -2188,7 +2719,7 @@ static void generate_single_key_block_inverted_opt(const uint8_t *startingKey, u // Start from the second byte, index 1 as we're never gonna touch the first byte for (int i = 1; i < PICOPASS_BLOCK_SIZE; i++) { - // Clear the last bit of the current byte (AND with 0xFE) + // Clear the last three bits of the current byte (AND with 0xF8) keyBlock[i] &= 0xF8; // Set the last bit to the corresponding value from binary_endings (OR with binary_endings[i]) keyBlock[i] |= ((binary_mids[i] & 0x03) << 1) | (binary_endings[i] & 0x01); @@ -2200,15 +2731,19 @@ void iClass_Recover(iclass_recover_req_t *msg) { bool shallow_mod = false; uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t genkeyblock[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t fast_restore_key[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t fast_previous_key[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t fast_current_key[PICOPASS_BLOCK_SIZE] = {0}; uint32_t index = msg->index; + bool short_delay = msg->short_delay; int bits_found = -1; bool recovered = false; bool completed = false; + bool interrupted = false; uint8_t div_key2[8] = {0}; uint32_t eof_time = 0; uint32_t start_time = 0; - uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x18 }; //block 24 - read_check_cc[0] = 0x10 | ICLASS_CMD_READCHECK; //use credit key + uint8_t read_check_cc[] = { 0x10 | ICLASS_CMD_READCHECK, 0x18 }; //block 24 with credit key uint8_t read_check_cc2[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; //block 2 -> to check Kd macs /* iclass_mac_table is a series of weak macs, those weak macs correspond to the different combinations of the last 3 bits of each key byte. */ @@ -2225,224 +2760,378 @@ void iClass_Recover(iclass_recover_req_t *msg) { }; LED_A_ON(); - DbpString(_RED_("Interrupting this process will render the card unusable!")); + DbpString(_RED_("Interrupting this process may render the card unusable!")); memcpy(div_key2, msg->nfa, 8); //START LOOP uint32_t loops = 1; + bool card_select = false; + bool card_auth = false; + bool priv_esc = false; + int status_message = 0; + int reinit_tentatives = 0; + bool res = false; + picopass_hdr_t hdr = {0}; + uint8_t original_mac[8] = {0}; + uint8_t mac1[4] = {0}; + + while ((card_select == false) || (card_auth == false)) { + + Iso15693InitReader(); //has to be at the top as it starts tracing + if (msg->debug == false) { + set_tracing(false); //disable tracing to prevent crashes - set to true for debugging + } else { + if (loops == 1) { + clear_trace(); //if we're debugging better to clear the trace but do it only on the first loop + } + } + //Step0 Card Select Routine + eof_time = 0; //reset eof time + res = select_iclass_tag(&hdr, false, &eof_time, shallow_mod); + if (res) { + status_message = 1; //card select successful + card_select = true; + } + + //Step 0A - The read_check_cc block has to be in AA2, set it by checking the card configuration + read_check_cc[1] = hdr.conf.app_limit + 1; //first block of AA2 + + //Step1 Authenticate with AA1 using trace + if (card_select) { + memcpy(original_mac, msg->req.key, 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (res) { + status_message = 2; //authentication with AA1 macs successful + card_auth = true; + } + } + + if ((card_select == false) || (card_auth == false)) { + reinit_tentatives++; + switch_off(); + } + + if (reinit_tentatives == 5) { + DbpString(""); + DbpString(_RED_("Unable to select or authenticate with card multiple times! Stopping.")); + goto out; + } + } while (bits_found == -1) { - bool card_select = false; - bool card_auth = false; - int reinit_tentatives = 0; - uint8_t original_mac[8] = {0}; - uint16_t resp_len = 0; - int res2; - uint8_t resp[10] = {0}; - uint8_t mac1[4] = {0}; - uint8_t mac2[4] = {0}; - picopass_hdr_t hdr = {0}; - bool res = false; - while (!card_select || !card_auth) { - Iso15693InitReader(); //has to be at the top as it starts tracing - if (!msg->debug) { - set_tracing(false); //disable tracing to prevent crashes - set to true for debugging + reinit_tentatives = 0; + uint8_t mac2[4] = {0}; + res = false; + + if (BUTTON_PRESS() || loops > msg->loop) { + if (loops > msg->loop) { + completed = true; } else { - if (loops == 1) { - clear_trace(); //if we're debugging better to clear the trace but do it only on the first loop - } + interrupted = true; } - if (msg->test) { - Dbprintf(_YELLOW_("*Cycled Reader*") " ----------------- TEST Index - Loops: "_YELLOW_("%3d / %3d") " --------------*", loops, msg->loop); - } else { - Dbprintf(_YELLOW_("*Cycled Reader*") " ----------------- Index: "_RED_("%3d")" Loops: "_YELLOW_("%3d / %3d") " --------------*", index, loops, msg->loop); + if (msg->fast) { + goto fast_restore; } - //Step0 Card Select Routine - eof_time = 0; //reset eof time + goto out; + } + + if (msg->test) { + Dbprintf(_YELLOW_("*Cycled Reader*") " TEST Index - Loops: "_YELLOW_("%3d / %3d") " *", loops, msg->loop); + } else if (msg->debug || ((card_select == false) && (card_auth == false))) { + Dbprintf(_YELLOW_("*Cycled Reader*") " Index: "_RED_("%3d")" Loops: "_YELLOW_("%3d / %3d") " *", index, loops, msg->loop); + } else { + DbprintfEx(FLAG_INPLACE, "[" _BLUE_("#") "] Index: "_CYAN_("%3d")" Loops: "_YELLOW_("%3d / %3d")" ", index, loops, msg->loop); + } + + while ((card_select == false) || (card_auth == false)) { + + Iso15693InitReader(); // has to be at the top as it starts tracing + set_tracing(false); // disable tracing to prevent crashes - set to true for debugging + // Step0 Card Select Routine + eof_time = 0; // reset eof time res = select_iclass_tag(&hdr, false, &eof_time, shallow_mod); - if (res == false) { - DbpString(_RED_("Unable to select card after reader cycle! Retrying...")); - } else { - DbpString(_GREEN_("Card selected successfully!")); + if (res) { + status_message = 1; // card select successful card_select = true; } - //Step1 Authenticate with AA1 using trace + // Step1 Authenticate with AA1 using trace if (card_select) { memcpy(original_mac, msg->req.key, 8); start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); - if (res == false) { - DbpString(_RED_("Unable to authenticate on AA1 using macs! Retrying...")); - } else { - DbpString(_GREEN_("AA1 authentication with macs successful!")); + if (res) { + status_message = 2; //authentication with AA1 macs successful card_auth = true; } } - if (!card_auth || !card_select) { + + if ((card_select == false) || (card_auth == false)) { reinit_tentatives++; switch_off(); } + if (reinit_tentatives == 5) { + DbpString(""); DbpString(_RED_("Unable to select or authenticate with card multiple times! Stopping.")); goto out; } } - //Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 - uint8_t blockno = 24; - uint8_t cmd_read[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockno, 0x00, 0x00}; - AddCrc(cmd_read + 1, 1); - int priv_esc_tries = 0; - bool priv_esc = false; - while (!priv_esc) { - //The privilege escalation is done with a readcheck and not just a normal read! - iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); - // expect a 8-byte response here - res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - if (res2 != PM3_SUCCESS || resp_len != 8) { - DbpString(_YELLOW_("Privilege Escalation -> ")_RED_("Read failed! Trying again...")); - priv_esc_tries++; + // Step2 Privilege Escalation: attempt to read AA2 with credentials for AA1 + if (priv_esc == false) { + priv_esc = do_privilege_escalation(read_check_cc, sizeof(read_check_cc), &eof_time); + if (priv_esc) { + status_message = 3; } else { - DbpString(_YELLOW_("Privilege Escalation -> ")_GREEN_("Response OK!")); - priv_esc = true; - } - if (priv_esc_tries == 5) { - DbpString(_RED_("Unable to complete privilege escalation! Stopping.")); goto out; } } - //Step3 Calculate New Key (Optimised Algo V2) + if (priv_esc && status_message != 3) { + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); + status_message = 3; + } + + // Step3 Calculate New Key (Optimised Algo V2) generate_single_key_block_inverted_opt(zero_key, index, genkeyblock); if (msg->test) { memcpy(genkeyblock, zero_key, PICOPASS_BLOCK_SIZE); } - //Step4 Calculate New Mac + if (msg->fast) { // if we're skipping restoring the original key to gain speed, xor the new index key with the previous index key and update the difference and track restore values differently + + if (index > 0 && loops > 1) { + generate_single_key_block_inverted_opt(zero_key, index - 1, fast_previous_key); + } else { + memcpy(fast_previous_key, zero_key, PICOPASS_BLOCK_SIZE); + } + + for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) { + fast_current_key[i] = genkeyblock[i] ^ fast_previous_key[i]; + fast_restore_key[i] = fast_restore_key[i] ^ fast_current_key[i]; + } + + memcpy(genkeyblock, fast_current_key, PICOPASS_BLOCK_SIZE); + } + + // Step4 Calculate New Mac uint8_t wb[9] = {0}; - blockno = 3; + uint8_t blockno = 3; wb[0] = blockno; memcpy(wb + 1, genkeyblock, 8); doMAC_N(wb, sizeof(wb), div_key2, mac2); - bool use_mac = true; bool written = false; bool write_error = false; + while (written == false && write_error == false) { - //Step5 Perform Write - if (iclass_writeblock_ext(blockno, genkeyblock, mac2, use_mac, shallow_mod)) { - DbpString("Wrote key: "); - Dbhexdump(8, genkeyblock, false); + // Step5 Perform Write + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + if (iclass_writeblock_sp(blockno, genkeyblock, mac2, shallow_mod, &start_time, &eof_time, short_delay)) { + status_message = 4; // wrote new key on the card - unverified } - //Reset cypher state - iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); - res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - //try to authenticate with the original mac to verify the write happened - memcpy(msg->req.key, original_mac, 8); - res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); - if (msg->test) { - if (res != true) { - DbpString(_RED_("*** CARD EPURSE IS SILENT! RISK OF BRICKING! DO NOT EXECUTE KEY UPDATES! SCAN IT ON READER FOR EPURSE UPDATE, COLLECT NEW TRACES AND TRY AGAIN! ***")); - goto out; + if (msg->fast == false) { // if we're going slow we check at every write that the write actually happened + // Reset cypher state + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + // try to authenticate with the original mac to verify the write happened + memcpy(msg->req.key, original_mac, 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (msg->test) { + if (res) { + DbpString(""); + DbpString(_GREEN_("*** CARD EPURSE IS LOUD! OK TO ATTEMPT KEY RETRIEVAL! RUN AGAIN WITH -notest ***")); + completed = true; + goto out; + } else { + DbpString(""); + DbpString(_RED_("*** CARD EPURSE IS SILENT! RISK OF BRICKING! DO NOT EXECUTE KEY UPDATES! SCAN IT ON READER FOR EPURSE UPDATE, COLLECT NEW TRACES AND TRY AGAIN! ***")); + goto out; + } } else { - DbpString(_GREEN_("*** CARD EPURSE IS LOUD! OK TO ATTEMPT KEY RETRIEVAL! RUN AGAIN WITH -notest ***")); - completed = true; - goto out; - } - } else { - if (res != true) { - DbpString("Write Operation : "_GREEN_("VERIFIED! Card Key Updated!")); - written = true; - } else { - DbpString("Write Operation : "_RED_("FAILED! Card Key is the Original. Retrying...")); - write_error = true; + if (res) { + write_error = true; // failed to update the key, the card's key is the original one + } else { + status_message = 5; // verified the card key was updated to the new one + written = true; + } } + } else { // if we're going fast we can skip the above checks as we're just xorring the key over and over + status_message = 5; + written = true; } } - if (!write_error) { - //Step6 Perform 8 authentication attempts + 1 to verify if we found the weak key + if (write_error == false) { + // Step6 Perform 8 authentication attempts + 1 to verify if we found the weak key for (int i = 0; i < 8 ; ++i) { + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); - res2 = GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - //need to craft the authentication payload accordingly + // need to craft the authentication payload accordingly memcpy(msg->req.key, iclass_mac_table[i], 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); //mac1 here shouldn't matter if (res == true) { bits_found = i; - DbpString(_RED_("--------------------------------------------------------")); - Dbprintf("Decimal Value of last 3 bits: " _GREEN_("[%3d]"), bits_found); - DbpString(_RED_("--------------------------------------------------------")); recovered = true; } } - //regardless of bits being found, restore the original key and verify it bool reverted = false; uint8_t revert_retries = 0; - while (!reverted) { - //Regain privilege escalation with a readcheck - iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); - // TODO: check result - GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - - DbpString(_YELLOW_("Attempting to restore the original key. ")); - if (iclass_writeblock_ext(blockno, genkeyblock, mac2, use_mac, shallow_mod)) { - DbpString("Restore of Original Key "_GREEN_("successful.")); - } else { - DbpString("Restore of Original Key " _RED_("failed.")); + if (msg->fast) { // if we're going fast only restore the original key at the end + if (recovered) { + goto fast_restore; } - DbpString(_YELLOW_("Verifying Key Restore...")); - //Do a readcheck first to reset the cypher state - iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); - // TODO: check result - GetIso15693AnswerFromTag(resp, sizeof(resp), ICLASS_READER_TIMEOUT_OTHERS, &eof_time, false, true, &resp_len); - - //need to craft the authentication payload accordingly - memcpy(msg->req.key, original_mac, 8); - res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); - if (res == true) { - DbpString("Restore of Original Key "_GREEN_("VERIFIED! Card is usable again.")); - reverted = true; - if (recovered) { - goto restore; + } else { + // if we're NOT going fast, regardless of bits being found, restore the original key and verify it + while (reverted == false) { + // Regain privilege escalation with a readcheck + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + if (iclass_writeblock_sp(blockno, genkeyblock, mac2, shallow_mod, &start_time, &eof_time, short_delay)) { + status_message = 6; // restore of original key successful but unverified } - } else { - DbpString("Restore of Original Key "_RED_("VERIFICATION FAILED! Trying again...")); - } + // Do a readcheck first to reset the cypher state + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); - revert_retries++; - if (revert_retries >= 7) { //must always be an odd number! - Dbprintf(_RED_("Attempted to restore original key for %3d times and failed. Stopping. Card is likely unusable."), revert_retries); - goto out; + // need to craft the authentication payload accordingly + memcpy(msg->req.key, original_mac, 8); + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (res == true) { + status_message = 7; // restore of original key verified - card usable again + reverted = true; + if (recovered) { + goto restore; + } + } + + revert_retries++; + if (revert_retries >= 7) { // must always be an odd number! + DbpString(""); + DbpString(_CYAN_("Last Written Key: ")); + Dbhexdump(8, genkeyblock, false); + Dbprintf(_RED_("Attempted to restore original key for %3d times and failed. Stopping. Card is likely unusable."), revert_retries); + goto out; + } } } - } - if (loops >= msg->loop) { - completed = true; - goto out; + if (msg->debug) { + if (status_message >= 1) { + DbpString(""); + DbpString("Card Select:............."_GREEN_("Ok!")); + } + if (status_message >= 2) { + DbpString("AA1 macs authentication:."_GREEN_("Ok!")); + } + if (status_message >= 3) { + DbpString("Privilege Escalation:...."_GREEN_("Ok!")); + } + if (status_message >= 4) { + DbpString("Wrote key: "); + Dbhexdump(8, genkeyblock, false); + } + if (status_message >= 5) { + DbpString("Key Update:.............."_GREEN_("Verified!")); + } + if (status_message >= 6) { + DbpString("Original Key Restore:...."_GREEN_("Ok!")); + } + if (status_message >= 7) { + DbpString("Original Key Restore:...."_GREEN_("Verified!")); + } } - if (!write_error) { //if there was a write error, re-run the loop for the same key index + + if (write_error && (msg->debug || msg->test)) { // if there was a write error, re-run the loop for the same key index + DbpString("Loop Error: "_RED_("Repeating Loop!")); + card_select = false; + card_auth = false; + priv_esc = false; + } else { loops++; index++; + status_message = 2; } - }//end while + }// end while +fast_restore: + ;// empty statement for compilation + uint8_t mac2[4] = {0}; + uint8_t wb[9] = {0}; + uint8_t blockno = 3; + wb[0] = blockno; + bool reverted = false; + uint8_t revert_retries = 0; + + while (reverted == false) { + // Regain privilege escalation with a readcheck + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc, sizeof(read_check_cc), &start_time, &eof_time, shallow_mod); + memcpy(wb + 1, fast_restore_key, 8); + doMAC_N(wb, sizeof(wb), div_key2, mac2); + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + if (iclass_writeblock_sp(blockno, fast_restore_key, mac2, shallow_mod, &start_time, &eof_time, short_delay)) { + status_message = 6; // restore of original key successful but unverified + } + + // Do a readcheck first to reset the cypher state + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + iclass_send_as_reader(read_check_cc2, sizeof(read_check_cc2), &start_time, &eof_time, shallow_mod); + + // need to craft the authentication payload accordingly + memcpy(msg->req.key, original_mac, 8); + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + res = authenticate_iclass_tag(&msg->req, &hdr, &start_time, &eof_time, mac1); + if (res == true) { + status_message = 7; // restore of original key verified - card usable again + reverted = true; + } + + revert_retries++; + if (revert_retries >= 7) { // must always be an odd number! + DbpString(""); + DbpString(_CYAN_("Last Written Key (fast): ")); + Dbhexdump(8, fast_restore_key, false); + Dbprintf(_RED_("Attempted to restore original key for %3d times and failed. Stopping. Card is likely unusable."), revert_retries); + } + if (recovered) { + goto restore; + } else { + goto out; + } + } restore: - ;//empty statement for compilation + ;// empty statement for compilation uint8_t partialkey[PICOPASS_BLOCK_SIZE] = {0}; for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) { - partialkey[i] = genkeyblock[i] ^ bits_found; + if (msg->fast) { + partialkey[i] = fast_restore_key[i] ^ bits_found; + } else { + partialkey[i] = genkeyblock[i] ^ bits_found; + } } - //Print the 24 bits found from k1 + // Print the bits decimal value + DbpString(""); + DbpString(_RED_("--------------------------------------------------------")); + Dbprintf("Decimal Value of last 3 bits: " _GREEN_("[%3d]"), bits_found); + // Print the 24 bits found from k1 DbpString(_RED_("--------------------------------------------------------")); DbpString(_RED_("SUCCESS! Raw Key Partial Bytes: ")); Dbhexdump(8, partialkey, false); @@ -2456,8 +3145,9 @@ out: switch_off(); if (completed) { reply_ng(CMD_HF_ICLASS_RECOVER, PM3_EINVARG, NULL, 0); + } else if (interrupted) { + reply_ng(CMD_HF_ICLASS_RECOVER, PM3_EOPABORTED, NULL, 0); } else { reply_ng(CMD_HF_ICLASS_RECOVER, PM3_ESOFT, NULL, 0); } - } diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 2185fd794..2d2bf8c42 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -34,6 +34,7 @@ // times in samples @ 212kHz when acting as reader #define ICLASS_READER_TIMEOUT_ACTALL 330 // 1558us, nominal 330us + 7slots*160us = 1450us #define ICLASS_READER_TIMEOUT_UPDATE 3390 // 16000us, nominal 4-15ms +#define ICLASS_READER_TIMEOUT_UPDATE_FAST 1500 // A copy of ICLASS_READER_TIMEOUT_UPDATE with reduced timeout values #define ICLASS_READER_TIMEOUT_OTHERS 80 // 380us, nominal 330us // The length of a received command will in most cases be no more than 18 bytes. @@ -72,4 +73,5 @@ uint8_t get_pagemap(const picopass_hdr_t *hdr); void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time, uint32_t *end_time, bool shallow_mod); void iClass_Recover(iclass_recover_req_t *msg); +void iClass_TearBlock(iclass_tearblock_req_t *msg); #endif diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 5165b5cad..c8fe0ce7f 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -36,9 +36,9 @@ #include "protocols.h" #include "generator.h" #include "desfire_crypto.h" // UL-C authentication helpers +#include "mifare.h" // for iso14a_polling_frame_t structure #define MAX_ISO14A_TIMEOUT 524288 - // this timeout is in MS static uint32_t iso14a_timeout; @@ -133,6 +133,34 @@ static uint32_t LastProxToAirDuration; #define SEC_Y 0x00 #define SEC_Z 0xc0 + +static const iso14a_polling_frame_t WUPA_CMD_FRAME = { + .frame = { ISO14443A_CMD_WUPA }, + .frame_length = 1, + .last_byte_bits = 7, + .extra_delay = 0 +}; + +static const iso14a_polling_frame_t MAGWUPA_CMD_FRAMES[] = { + {{ MAGSAFE_CMD_WUPA_1 }, 1, 7, 0}, + {{ MAGSAFE_CMD_WUPA_2 }, 1, 7, 0}, + {{ MAGSAFE_CMD_WUPA_3 }, 1, 7, 0}, + {{ MAGSAFE_CMD_WUPA_4 }, 1, 7, 0} +}; + +// Polling frames and configurations +iso14a_polling_parameters_t WUPA_POLLING_PARAMETERS = { + .frames = { {{ ISO14443A_CMD_WUPA }, 1, 7, 0 }}, + .frame_count = 1, + .extra_timeout = 0, +}; + +iso14a_polling_parameters_t REQA_POLLING_PARAMETERS = { + .frames = { {{ ISO14443A_CMD_REQA }, 1, 7, 0 }}, + .frame_count = 1, + .extra_timeout = 0, +}; + /* Default HF 14a config is set to: forceanticol = 0 (auto) @@ -140,21 +168,17 @@ Default HF 14a config is set to: forcecl2 = 0 (auto) forcecl3 = 0 (auto) forcerats = 0 (auto) + magsafe = 0 (disabled) + polling_loop_annotation = {{0}, 0, 0, 0} (disabled) */ -static hf14a_config hf14aconfig = { 0, 0, 0, 0, 0 } ; +static hf14a_config_t hf14aconfig = { 0, 0, 0, 0, 0, 0, {{0}, 0, 0, 0} }; +static iso14a_polling_parameters_t hf14a_polling_parameters = { + .frames = { {{ ISO14443A_CMD_WUPA }, 1, 7, 0 }}, + .frame_count = 1, + .extra_timeout = 0 +}; -// Polling frames and configurations -iso14a_polling_parameters_t WUPA_POLLING_PARAMETERS = { - .frames = { {{ ISO14443A_CMD_WUPA }, 1, 7, 0} }, - .frame_count = 1, - .extra_timeout = 0, -}; -iso14a_polling_parameters_t REQA_POLLING_PARAMETERS = { - .frames = { {{ ISO14443A_CMD_REQA }, 1, 7, 0} }, - .frame_count = 1, - .extra_timeout = 0, -}; // parity isn't used much static uint8_t parity_array[MAX_PARITY_SIZE] = {0}; @@ -166,31 +190,40 @@ struct Crypto1State crypto1_state = {0, 0}; void printHf14aConfig(void) { DbpString(_CYAN_("HF 14a config")); - Dbprintf(" [a] Anticol override.... %s%s%s", + Dbprintf(" [a] Anticol override........... %s%s%s", (hf14aconfig.forceanticol == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forceanticol == 1) ? _RED_("force") " ( always do anticol )" : "", (hf14aconfig.forceanticol == 2) ? _RED_("skip") " ( always skip anticol )" : "" ); - Dbprintf(" [b] BCC override........ %s%s%s", + Dbprintf(" [b] BCC override............... %s%s%s", (hf14aconfig.forcebcc == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcebcc == 1) ? _RED_("fix") " ( fix bad BCC )" : "", (hf14aconfig.forcebcc == 2) ? _RED_("ignore") " ( ignore bad BCC, always use card BCC )" : "" ); - Dbprintf(" [2] CL2 override........ %s%s%s", + Dbprintf(" [2] CL2 override............... %s%s%s", (hf14aconfig.forcecl2 == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcecl2 == 1) ? _RED_("force") " ( always do CL2 )" : "", (hf14aconfig.forcecl2 == 2) ? _RED_("skip") " ( always skip CL2 )" : "" ); - Dbprintf(" [3] CL3 override........ %s%s%s", + Dbprintf(" [3] CL3 override............... %s%s%s", (hf14aconfig.forcecl3 == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcecl3 == 1) ? _RED_("force") " ( always do CL3 )" : "", (hf14aconfig.forcecl3 == 2) ? _RED_("skip") " ( always skip CL3 )" : "" ); - Dbprintf(" [r] RATS override....... %s%s%s", + Dbprintf(" [r] RATS override.............. %s%s%s", (hf14aconfig.forcerats == 0) ? _GREEN_("std") " ( follow standard )" : "", (hf14aconfig.forcerats == 1) ? _RED_("force") " ( always do RATS )" : "", (hf14aconfig.forcerats == 2) ? _RED_("skip") " ( always skip RATS )" : "" ); + Dbprintf(" [m] Magsafe polling............ %s", + (hf14aconfig.magsafe == 1) ? _GREEN_("enabled") : _YELLOW_("disabled") + ); + Dbprintf(" [p] Polling loop annotation.... %s %*D", + (hf14aconfig.polling_loop_annotation.frame_length <= 0) ? _YELLOW_("disabled") : _GREEN_("enabled"), + hf14aconfig.polling_loop_annotation.frame_length, + hf14aconfig.polling_loop_annotation.frame, + "" + ); } /** @@ -201,21 +234,64 @@ void printHf14aConfig(void) { * @brief setSamplingConfig * @param sc */ -void setHf14aConfig(const hf14a_config *hc) { - - if ((hc->forceanticol >= 0) && (hc->forceanticol <= 2)) +void setHf14aConfig(const hf14a_config_t *hc) { + if ((hc->forceanticol >= 0) && (hc->forceanticol <= 2)) { hf14aconfig.forceanticol = hc->forceanticol; - if ((hc->forcebcc >= 0) && (hc->forcebcc <= 2)) + } + + if ((hc->forcebcc >= 0) && (hc->forcebcc <= 2)) { hf14aconfig.forcebcc = hc->forcebcc; - if ((hc->forcecl2 >= 0) && (hc->forcecl2 <= 2)) + } + + if ((hc->forcecl2 >= 0) && (hc->forcecl2 <= 2)) { hf14aconfig.forcecl2 = hc->forcecl2; - if ((hc->forcecl3 >= 0) && (hc->forcecl3 <= 2)) + } + + if ((hc->forcecl3 >= 0) && (hc->forcecl3 <= 2)) { hf14aconfig.forcecl3 = hc->forcecl3; - if ((hc->forcerats >= 0) && (hc->forcerats <= 2)) + } + + if ((hc->forcerats >= 0) && (hc->forcerats <= 2)) { hf14aconfig.forcerats = hc->forcerats; + } + + if ((hc->magsafe >= 0) && (hc->magsafe <= 1)) { + hf14aconfig.magsafe = hc->magsafe; + } + + if (hc->polling_loop_annotation.frame_length >= 0) { + memcpy(&hf14aconfig.polling_loop_annotation, &hc->polling_loop_annotation, sizeof(iso14a_polling_frame_t)); + } + + // iceman: Somehow I think we should memcpy WUPA_CMD and all other hf14a_polling_parameters.frames[xxx] assignments + // right now we are assigning... + + // Derive polling loop configuration based on 14a config + hf14a_polling_parameters.frames[0] = WUPA_CMD_FRAME; + hf14a_polling_parameters.frame_count = 1; + hf14a_polling_parameters.extra_timeout = 0; + + if (hf14aconfig.magsafe == 1) { + + for (int i = 0; i < ARRAYLEN(MAGWUPA_CMD_FRAMES); i++) { + if (hf14a_polling_parameters.frame_count < ARRAYLEN(hf14a_polling_parameters.frames) - 1) { + hf14a_polling_parameters.frames[hf14a_polling_parameters.frame_count] = MAGWUPA_CMD_FRAMES[i]; + hf14a_polling_parameters.frame_count++; + } + } + } + + if (hf14aconfig.polling_loop_annotation.frame_length > 0) { + + if (hf14a_polling_parameters.frame_count < ARRAYLEN(hf14a_polling_parameters.frames) - 1) { + hf14a_polling_parameters.frames[hf14a_polling_parameters.frame_count] = hf14aconfig.polling_loop_annotation; + hf14a_polling_parameters.frame_count++; + } + hf14a_polling_parameters.extra_timeout = 250; + } } -hf14a_config *getHf14aConfig(void) { +hf14a_config_t *getHf14aConfig(void) { return &hf14aconfig; } @@ -294,16 +370,16 @@ tUart14a *GetUart14a(void) { void Uart14aReset(void) { Uart.state = STATE_14A_UNSYNCD; + Uart.shiftReg = 0; // shiftreg to hold decoded data bits 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; // 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; + Uart.parityBits = 0; // holds 8 parity bits + Uart.parityLen = 0; // number of decoded parity bytes + Uart.fourBits = 0x00000000; // clear the buffer for 4 Bits + Uart.startTime = 0; + Uart.endTime = 0; } void Uart14aInit(uint8_t *d, uint16_t n, uint8_t *par) { @@ -520,6 +596,9 @@ void Demod14aInit(uint8_t *d, uint16_t n, uint8_t *par) { RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time) { if (Demod.len == Demod.output_len) { + // Flush last parity bits + Demod.parityBits <<= (8 - (Demod.len & 0x0007)); // left align remaining parity bits + Demod.parity[Demod.parityLen++] = Demod.parityBits; // and store them return true; } @@ -592,7 +671,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t 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.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 @@ -618,6 +697,9 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t static RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { if (Demod.len == Demod.output_len) { + // Flush last parity bits + Demod.parityBits <<= (8 - (Demod.len & 0x0007)); // left align remaining parity bits + Demod.parity[Demod.parityLen++] = Demod.parityBits; // and store them return true; } @@ -729,12 +811,12 @@ void RAMFUNC SniffIso14443a(uint8_t param) { set_tracing(true); // The command (reader -> tag) that we're receiving. - uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); - uint8_t *receivedCmdPar = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *receivedCmd = BigBuf_calloc(MAX_FRAME_SIZE); + uint8_t *receivedCmdPar = BigBuf_calloc(MAX_PARITY_SIZE); // The response (tag -> reader) that we're receiving. - uint8_t *receivedResp = BigBuf_malloc(MAX_FRAME_SIZE); - uint8_t *receivedRespPar = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *receivedResp = BigBuf_calloc(MAX_FRAME_SIZE); + uint8_t *receivedRespPar = BigBuf_calloc(MAX_PARITY_SIZE); uint8_t previous_data = 0; int maxDataLen = 0, dataLen; @@ -1106,9 +1188,24 @@ bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_ } } +static void Simulate_reread_ulc_key(uint8_t *ulc_key) { + // copy UL-C key from emulator memory + + mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); + + memcpy(ulc_key, mfu_header->data + (0x2D * 4), 4); + memcpy(ulc_key + 4, mfu_header->data + (0x2C * 4), 4); + memcpy(ulc_key + 8, mfu_header->data + (0x2F * 4), 4); + memcpy(ulc_key + 12, mfu_header->data + (0x2E * 4), 4); + + reverse_array(ulc_key, 4); + reverse_array(ulc_key + 4, 4); + reverse_array(ulc_key + 8, 4); + reverse_array(ulc_key + 12, 4); +} bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t *ats, size_t ats_len, tag_response_info_t **responses, - uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages) { + uint32_t *cuid, uint8_t *pages, uint8_t *ulc_key) { uint8_t sak = 0; // The first response contains the ATQA (note: bytes are transmitted in reverse order). static uint8_t rATQA[2] = { 0x00 }; @@ -1155,14 +1252,11 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); *pages = MAX(mfu_header->pages, 15); - // counters and tearing flags + // tearing flags // for old dumps with all zero headers, we need to set default values. for (uint8_t i = 0; i < 3; i++) { - - counters[i] = le24toh(mfu_header->counter_tearing[i]); - - if (mfu_header->counter_tearing[i][3] != 0x00) { - tearings[i] = mfu_header->counter_tearing[i][3]; + if (mfu_header->counter_tearing[i][3] == 0x00) { + mfu_header->counter_tearing[i][3] = 0xBD; } } @@ -1210,14 +1304,11 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); *pages = MAX(mfu_header->pages, 19); - // counters and tearing flags + // tearing flags // for old dumps with all zero headers, we need to set default values. for (uint8_t i = 0; i < 3; i++) { - - counters[i] = le24toh(mfu_header->counter_tearing[i]); - - if (mfu_header->counter_tearing[i][3] != 0x00) { - tearings[i] = mfu_header->counter_tearing[i][3]; + if (mfu_header->counter_tearing[i][3] == 0x00) { + mfu_header->counter_tearing[i][3] = 0xBD; } } @@ -1264,6 +1355,38 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, sak = 0x20; break; } + case 13: { // MIFARE Ultralight-C + + rATQA[0] = 0x44; + sak = 0x00; + + // some first pages of UL/NTAG dump is special data + mfu_dump_t *mfu_header = (mfu_dump_t *) BigBuf_get_EM_addr(); + *pages = MAX(mfu_header->pages, 47); + + // copy UL-C key from emulator memory + memcpy(ulc_key, mfu_header->data + (0x2D * 4), 4); + memcpy(ulc_key + 4, mfu_header->data + (0x2C * 4), 4); + memcpy(ulc_key + 8, mfu_header->data + (0x2F * 4), 4); + memcpy(ulc_key + 12, mfu_header->data + (0x2E * 4), 4); + + reverse_array(ulc_key, 4); + reverse_array(ulc_key + 4, 4); + reverse_array(ulc_key + 8, 4); + reverse_array(ulc_key + 12, 4); + + /* + Dbprintf("UL-C Pages....... %u ( 47 )", *pages); + DbpString("UL-C 3des key... "); + Dbhexdump(16, ulc_key, false); + */ + + if (IS_FLAG_UID_IN_DATA(flags, 7)) { + DbpString("UL-C UID........ "); + Dbhexdump(7, data, false); + } + break; + } default: { if (g_dbglevel >= DBG_ERROR) Dbprintf("Error: unknown tagtype (%d)", tagType); return false; @@ -1289,7 +1412,7 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, // if uid not supplied then get from emulator memory if ((memcmp(data, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 10) == 0) || IS_FLAG_UID_IN_EMUL(flags)) { - if (tagType == 2 || tagType == 7) { + if (tagType == 2 || tagType == 7 || tagType == 13) { uint16_t start = MFU_DUMP_PREFIX_LENGTH; uint8_t emdata[8]; emlGet(emdata, start, sizeof(emdata)); @@ -1456,15 +1579,18 @@ bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, // 'hf 14a sim' //----------------------------------------------------------------------------- void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads, - uint8_t *ats, size_t ats_len) { + uint8_t *ats, size_t ats_len, bool ulc_part1, bool ulc_part2) { #define ATTACK_KEY_COUNT 16 +#define ULC_TAG_NONCE "\x01\x02\x03\x04\x05\x06\x07\x08" tag_response_info_t *responses; uint32_t cuid = 0; uint32_t nonce = 0; - uint32_t counters[3] = { 0x00, 0x00, 0x00 }; - uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; + /// Ultralight-C 3des2k + uint8_t ulc_key[16] = { 0x00 }; + uint8_t ulc_iv[8] = { 0x00 }; + bool ulc_reread_key = false; uint8_t pages = 0; // Here, we collect CUID, block1, keytype1, NT1, NR1, AR1, CUID, block2, keytyp2, NT2, NR2, AR2 @@ -1508,12 +1634,24 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin .modulation_n = 0 }; - if (SimulateIso14443aInit(tagType, flags, useruid, ats, ats_len, &responses, &cuid, counters, tearings, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, useruid, ats, ats_len + , &responses, &cuid, &pages + , ulc_key) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; } + mfu_dump_t *mfu_em_dump = NULL; + if (tagType == 2 || tagType == 7) { + mfu_em_dump = (mfu_dump_t *)BigBuf_get_EM_addr(); + if (!mfu_em_dump) { + if (g_dbglevel >= DBG_ERROR) Dbprintf("[-] ERROR: Failed to get EM address for MFU/NTAG operations."); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EMALLOC, NULL, 0); + return; + } + } + // We need to listen to the high-frequency, peak-detected path. iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); @@ -1586,7 +1724,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin order = ORDER_NONE; // back to work state p_response = NULL; - } else if (order == ORDER_AUTH && len == 8) { + } else if (order == ORDER_AUTH && len == 8 && tagType != 2 && tagType != 7 && tagType != 13) { // 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); @@ -1676,21 +1814,21 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin } else if (receivedCmd[0] == ISO14443A_CMD_READBLOCK && len == 4) { // Received a (plain) READ uint8_t block = receivedCmd[1]; // if Ultralight or NTAG (4 byte blocks) - if (tagType == 7 || tagType == 2) { + if (tagType == 7 || tagType == 2 || tagType == 13) { if (block > pages) { // send NACK 0x0 == invalid argument EmSend4bit(CARD_NACK_IV); } else { // first blocks of emu are header uint16_t start = (block * 4) + MFU_DUMP_PREFIX_LENGTH; - uint8_t emdata[MAX_MIFARE_FRAME_SIZE] = {0}; + uint8_t emdata[MIFARE_BLOCK_SIZE + CRC16_SIZE] = {0}; emlGet(emdata, start, MIFARE_BLOCK_SIZE); AddCrc14A(emdata, MIFARE_BLOCK_SIZE); EmSendCmd(emdata, sizeof(emdata)); numReads++; // Increment number of times reader requested a block if (exitAfterNReads > 0 && numReads == exitAfterNReads) { - Dbprintf("[MFUEMUL_WORK] %d reads done, exiting", numReads); + Dbprintf("[MFUEMUL_WORK] " _YELLOW_("%u") " reads done, exiting", numReads); finished = true; } } @@ -1702,7 +1840,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin // block1 = 4byte UID. p_response = &responses[RESP_INDEX_UIDC1]; } else { // all other tags (16 byte block tags) - uint8_t emdata[MAX_MIFARE_FRAME_SIZE] = {0}; + uint8_t emdata[MIFARE_BLOCK_SIZE + CRC16_SIZE] = {0}; emlGet(emdata, block, MIFARE_BLOCK_SIZE); AddCrc14A(emdata, MIFARE_BLOCK_SIZE); EmSendCmd(emdata, sizeof(emdata)); @@ -1725,7 +1863,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin EmSendCmd(emdata, len + 2); } p_response = NULL; - } else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7)) { // Received a WRITE + } else if (receivedCmd[0] == MIFARE_ULC_WRITE && len == 8 && (tagType == 2 || tagType == 7 || tagType == 13)) { // Received a WRITE p_response = NULL; @@ -1763,12 +1901,15 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin // send ACK EmSend4bit(CARD_ACK); + if (tagType == 13 && block >= 0x2c && block <= 0x2F) { + ulc_reread_key = true; + } } else { // send NACK 0x1 == crc/parity error EmSend4bit(CARD_NACK_PA); } goto jump; - } else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7)) { + } else if (receivedCmd[0] == MIFARE_ULC_COMP_WRITE && len == 4 && (tagType == 2 || tagType == 7 || tagType == 13)) { // cmd + block + 2 bytes crc if (CheckCrc14A(receivedCmd, len)) { wrblock = receivedCmd[1]; @@ -1794,8 +1935,8 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin // send NACK 0x0 == invalid argument EmSend4bit(CARD_NACK_IV); } else { - uint8_t cmd[] = {0x00, 0x00, 0x00, 0x14, 0xa5}; - htole24(counters[index], cmd); + uint8_t cmd[] = {0, 0, 0, 0x14, 0xa5}; + memcpy(cmd, mfu_em_dump->counter_tearing[index], 3); AddCrc14A(cmd, sizeof(cmd) - 2); EmSendCmd(cmd, sizeof(cmd)); } @@ -1806,13 +1947,16 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin // send NACK 0x0 == invalid argument EmSend4bit(CARD_NACK_IV); } else { - uint32_t val = le24toh(receivedCmd + 2) + counters[index]; + uint32_t val = le24toh(mfu_em_dump->counter_tearing[index]); // get current counter value + val += le24toh(receivedCmd + 2); // increment in + // if new value + old value is bigger 24bits, fail if (val > 0xFFFFFF) { // send NACK 0x4 == counter overflow EmSend4bit(CARD_NACK_NA); } else { - counters[index] = val; + htole24(val, mfu_em_dump->counter_tearing[index]); + // send ACK EmSend4bit(CARD_ACK); } @@ -1826,7 +1970,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin EmSend4bit(CARD_NACK_IV); } else { uint8_t cmd[3] = {0, 0, 0}; - cmd[0] = tearings[index]; + cmd[0] = mfu_em_dump->counter_tearing[index][3]; AddCrc14A(cmd, sizeof(cmd) - 2); EmSendCmd(cmd, sizeof(cmd)); } @@ -1839,7 +1983,7 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin p_response = &responses[RESP_INDEX_VERSION]; } else if (receivedCmd[0] == MFDES_GET_VERSION && len == 4 && (tagType == 3)) { p_response = &responses[RESP_INDEX_VERSION]; - } else if ((receivedCmd[0] == MIFARE_AUTH_KEYA || receivedCmd[0] == MIFARE_AUTH_KEYB) && len == 4 && tagType != 2 && tagType != 7) { // Received an authentication request + } else if ((receivedCmd[0] == MIFARE_AUTH_KEYA || receivedCmd[0] == MIFARE_AUTH_KEYB) && len == 4 && tagType != 2 && tagType != 7 && tagType != 13) { // Received an authentication request cardAUTHKEY = receivedCmd[0] - 0x60; cardAUTHSC = receivedCmd[1] / 4; // received block num @@ -1858,9 +2002,85 @@ void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uin } else { p_response = &responses[RESP_INDEX_ATS]; } - } else if (receivedCmd[0] == MIFARE_ULC_AUTH_1) { // ULC authentication, or Desfire Authentication - LogTrace(receivedCmd, Uart.len, Uart.startTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.endTime * 16 - DELAY_AIR2ARM_AS_TAG, Uart.parity, true); - p_response = NULL; + } else if (receivedCmd[0] == MIFARE_ULC_AUTH_1 && len == 4 && tagType == 13) { // ULC authentication, or Desfire Authentication + + // reset IV to all zeros + memset(ulc_iv, 0x00, 8); + + if (ulc_reread_key) { + Simulate_reread_ulc_key(ulc_key); + ulc_reread_key = false; + } + + dynamic_response_info.response[0] = MIFARE_ULC_AUTH_2; + + // our very random TAG NONCE + memcpy(dynamic_response_info.response + 1, ULC_TAG_NONCE, 8); + + if (ulc_part1) { + memset(dynamic_response_info.response + 1, 0, 8); + } else { + // encrypt TAG NONCE + tdes_nxp_send(dynamic_response_info.response + 1, dynamic_response_info.response + 1, 8, ulc_key, ulc_iv, 2); + } + + // Add CRC + AddCrc14A(dynamic_response_info.response, 9); + + // prepare to send + dynamic_response_info.response_n = 1 + 8 + 2; + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + order = ORDER_AUTH; + + } else if (receivedCmd[0] == MIFARE_ULC_AUTH_2 && len == 19 && tagType == 13) { // ULC authentication, or Desfire Authentication + + uint8_t enc_rnd_ab[16] = { 0x00 }; + uint8_t rnd_ab[16] = { 0x00 }; + + // copy reader response + memcpy(enc_rnd_ab, receivedCmd + 1, 16); + + // decrypt + tdes_nxp_receive(enc_rnd_ab, rnd_ab, 16, ulc_key, ulc_iv, 2); + + ror(rnd_ab + 8, 8); + + if (memcmp(rnd_ab + 8, ULC_TAG_NONCE, 8) != 0) { + Dbprintf("failed authentication"); + } + + // OK response + dynamic_response_info.response[0] = 0x00; + + if (ulc_part2) { + // try empty auth but with correct CRC and 0x00 command + memset(dynamic_response_info.response + 1, 0, 8); + } else { + // rol RndA + rol(rnd_ab, 8); + + // encrypt RndA + tdes_nxp_send(rnd_ab, dynamic_response_info.response + 1, 8, ulc_key, ulc_iv, 2); + } + + // Add CRC + AddCrc14A(dynamic_response_info.response, 9); + + dynamic_response_info.response_n = 1 + 8 + 2; + + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + order = ORDER_NONE; + // Add CRC + AddCrc14A(dynamic_response_info.response, 17); + + dynamic_response_info.response_n = 1 + 16 + 2; + + prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE); + p_response = &dynamic_response_info; + order = ORDER_NONE; + } else if (receivedCmd[0] == MIFARE_ULEV1_AUTH && len == 7 && tagType == 7) { // NTAG / EV-1 uint8_t pwd[4] = {0, 0, 0, 0}; emlGet(pwd, (pages - 1) * 4 + MFU_DUMP_PREFIX_LENGTH, sizeof(pwd)); @@ -2041,13 +2261,16 @@ jump: // of bits specified in the delay parameter. static void PrepareDelayedTransfer(uint16_t delay) { delay &= 0x07; - if (!delay) return; + if (delay == 0) { + return; + } uint8_t bitmask = 0; uint8_t bits_shifted = 0; - for (uint16_t i = 0; i < delay; i++) + for (uint16_t i = 0; i < delay; i++) { bitmask |= (0x01 << i); + } tosend_t *ts = get_tosend(); @@ -2076,6 +2299,7 @@ static void TransmitFor14443a(const uint8_t *cmd, uint16_t len, uint32_t *timing Dbprintf("Warning: HF field is off"); return; } + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); if (timing) { @@ -2250,7 +2474,7 @@ int EmGetCmd(uint8_t *received, uint16_t received_max_len, uint16_t *len, uint8_ // button press, takes a bit time, might mess with simualtion if (checker-- == 0) { if (BUTTON_PRESS()) { - Dbprintf("----------- " _GREEN_("Breaking / User aborted") " ----------"); + Dbprintf("----------- " _GREEN_("Button pressed, user aborted") " ----------"); return false; } @@ -2360,7 +2584,7 @@ int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen) { int EmSend4bit(uint8_t resp) { Code4bitAnswerAsTag(resp); - tosend_t *ts = get_tosend(); + const tosend_t *ts = get_tosend(); int res = EmSendCmd14443aRaw(ts->buf, ts->max); // do the tracing for the previous reader request and this tag answer: uint8_t par[1] = {0x00}; @@ -2382,7 +2606,7 @@ int EmSendCmdPar(uint8_t *resp, uint16_t respLen, uint8_t *par) { } int EmSendCmdParEx(uint8_t *resp, uint16_t respLen, uint8_t *par, bool collision) { CodeIso14443aAsTagPar(resp, respLen, par, collision); - tosend_t *ts = get_tosend(); + const tosend_t *ts = get_tosend(); int res = EmSendCmd14443aRaw(ts->buf, ts->max); // do the tracing for the previous reader request and this tag answer: @@ -2428,9 +2652,9 @@ int EmSendPrecompiledCmd(tag_response_info_t *p_response) { return ret; } -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) { +bool EmLogTrace(const uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, + uint32_t reader_EndTime, const uint8_t *reader_Parity, const uint8_t *tag_data, + uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, const uint8_t *tag_Parity) { // 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. @@ -2549,28 +2773,27 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint16_t rec_max return false; } -void ReaderTransmitBitsPar(uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing) { - +void ReaderTransmitBitsPar(const uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing) { CodeIso14443aBitsAsReaderPar(frame, bits, par); // Send command to tag - tosend_t *ts = get_tosend(); + const tosend_t *ts = get_tosend(); TransmitFor14443a(ts->buf, ts->max, timing); if (g_trigger) LED_A_ON(); LogTrace(frame, nbytes(bits), (LastTimeProxToAirStart << 4) + DELAY_ARM2AIR_AS_READER, ((LastTimeProxToAirStart + LastProxToAirDuration) << 4) + DELAY_ARM2AIR_AS_READER, par, true); } -void ReaderTransmitPar(uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *timing) { +void ReaderTransmitPar(const uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *timing) { ReaderTransmitBitsPar(frame, len * 8, par, timing); } -static void ReaderTransmitBits(uint8_t *frame, uint16_t len, uint32_t *timing) { +static void ReaderTransmitBits(const uint8_t *frame, uint16_t len, uint32_t *timing) { // Generate parity and redirect GetParity(frame, len / 8, parity_array); ReaderTransmitBitsPar(frame, len, parity_array, timing); } -void ReaderTransmit(uint8_t *frame, uint16_t len, uint32_t *timing) { +void ReaderTransmit(const uint8_t *frame, uint16_t len, uint32_t *timing) { // Generate parity and redirect GetParity(frame, len, parity_array); ReaderTransmitBitsPar(frame, len * 8, parity_array, timing); @@ -2608,9 +2831,9 @@ void iso14443a_antifuzz(uint32_t flags) { int len = 0; // allocate buffers: - uint8_t *received = BigBuf_malloc(MAX_FRAME_SIZE); - uint8_t *receivedPar = BigBuf_malloc(MAX_PARITY_SIZE); - uint8_t *resp = BigBuf_malloc(20); + uint8_t *received = BigBuf_calloc(MAX_FRAME_SIZE); + uint8_t *receivedPar = BigBuf_calloc(MAX_PARITY_SIZE); + uint8_t *resp = BigBuf_calloc(20); memset(received, 0x00, MAX_FRAME_SIZE); memset(received, 0x00, MAX_PARITY_SIZE); @@ -2697,30 +2920,40 @@ static void iso14a_set_ATS_times(const uint8_t *ats) { } -static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, iso14a_polling_parameters_t *polling_parameters) { -#define WUPA_RETRY_TIMEOUT 10 +static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, const iso14a_polling_parameters_t *polling_parameters) { +#define RETRY_TIMEOUT 10 uint32_t save_iso14a_timeout = iso14a_get_timeout(); iso14a_set_timeout(1236 / 128 + 1); // response to WUPA is expected at exactly 1236/fc. No need to wait longer. + // refactored to use local pointer, now no modification of polling_parameters pointer is done + // I don't think the intention was to modify polling_parameters when sending in WUPA_POLLING_PARAMETERS etc. + // Modify polling_params, if null use default values. + iso14a_polling_parameters_t p; + memcpy(&p, (uint8_t *)polling_parameters, sizeof(iso14a_polling_parameters_t)); + + if (polling_parameters == NULL) { + memcpy(&p, (uint8_t *)&hf14a_polling_parameters, sizeof(iso14a_polling_parameters_t)); + } + bool first_try = true; - uint32_t retry_timeout = WUPA_RETRY_TIMEOUT * polling_parameters->frame_count + polling_parameters->extra_timeout; - uint32_t start_time = 0; int len; + uint32_t retry_timeout = ((RETRY_TIMEOUT * p.frame_count) + p.extra_timeout); + uint32_t start_time = 0; + uint8_t curr = 0; - uint8_t current_frame = 0; - + // Use the temporary polling parameters do { - iso14a_polling_frame_t *frame_parameters = &polling_parameters->frames[current_frame]; + const iso14a_polling_frame_t *frp = &p.frames[curr]; - if (frame_parameters->last_byte_bits == 8) { - ReaderTransmit(frame_parameters->frame, frame_parameters->frame_length, NULL); + if (frp->last_byte_bits == 8) { + ReaderTransmit(frp->frame, frp->frame_length, NULL); } else { - ReaderTransmitBitsPar(frame_parameters->frame, frame_parameters->last_byte_bits, NULL, NULL); + ReaderTransmitBitsPar(frp->frame, frp->last_byte_bits, NULL, NULL); } - if (frame_parameters->extra_delay) { - SpinDelay(frame_parameters->extra_delay); + if (frp->extra_delay) { + SpinDelay(frp->extra_delay); } // Receive the ATQA @@ -2729,12 +2962,12 @@ static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, iso14a_p // We set the start_time here otherwise in some cases we miss the window and only ever try once if (first_try) { start_time = GetTickCount(); + first_try = false; } - first_try = false; - // Go over frame configurations, loop back when we reach the end - current_frame = current_frame < (polling_parameters->frame_count - 1) ? current_frame + 1 : 0; + curr = (curr < (p.frame_count - 1)) ? curr + 1 : 0; + } while (len == 0 && GetTickCountDelta(start_time) <= retry_timeout); iso14a_set_timeout(save_iso14a_timeout); @@ -2743,9 +2976,13 @@ static int GetATQA(uint8_t *resp, uint16_t resp_len, uint8_t *resp_par, iso14a_p int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats) { - return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, no_rats, &WUPA_POLLING_PARAMETERS); + return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, no_rats, NULL, false); +} +int iso14443a_select_card_for_magic(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades) { + // Bug fix: When SAK is 0x00, `iso14443a_select_cardEx` would return too early at + // line "if (hf14aconfig.forcerats == 0)".`force_rats` is used to force RATS execution and ATS retrieval. + return iso14443a_select_cardEx(uid_ptr, p_card, cuid_ptr, anticollision, num_cascades, false, NULL, true); } - // performs iso14443a anticollision (optional) and card select procedure // fills the uid and cuid pointer unless NULL @@ -2755,7 +2992,7 @@ int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32 // requests ATS unless no_rats is true int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, - iso14a_polling_parameters_t *polling_parameters) { + const iso14a_polling_parameters_t *polling_parameters, bool force_rats) { uint8_t resp[MAX_FRAME_SIZE] = {0}; // theoretically. A usual RATS will be much smaller @@ -2810,8 +3047,9 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint if (anticollision) { // clear uid - if (uid_ptr) + if (uid_ptr) { memset(uid_ptr, 0, 10); + } } if (hf14aconfig.forceanticol == 0) { @@ -2972,18 +3210,18 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint p_card->sak = sak; } - if (hf14aconfig.forcerats == 0) { + if (hf14aconfig.forcerats == 0 && force_rats == false) { // PICC compliant with iso14443a-4 ---> (SAK & 0x20 != 0) if ((sak & 0x20) == 0) { return 2; } - } else if (hf14aconfig.forcerats == 2) { + } else if (hf14aconfig.forcerats == 2 && force_rats == false) { if ((sak & 0x20) != 0) Dbprintf("Skipping RATS according to hf 14a config"); return 2; } // else force RATS - if ((sak & 0x20) == 0) Dbprintf("Forcing RATS according to hf 14a config"); + if ((sak & 0x20) == 0 && force_rats == false) Dbprintf("Forcing RATS according to hf 14a config"); // RATS, Request for answer to select if (no_rats == false) { @@ -3009,14 +3247,14 @@ int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint return 1; } -int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) { +int iso14443a_fast_select_card(const uint8_t *uid_ptr, uint8_t num_cascades) { uint8_t resp[3] = { 0 }; // theoretically. max 1 Byte SAK, 2 Byte CRC, 3 bytes is enough uint8_t resp_par[1] = {0}; uint8_t sak = 0x04; // cascade uid int cascade_level = 1; - if (GetATQA(resp, sizeof(resp), resp_par, &WUPA_POLLING_PARAMETERS) == 0) { + if (GetATQA(resp, sizeof(resp), resp_par, NULL) == 0) { return 0; } @@ -3249,7 +3487,8 @@ void ReaderIso14443a(PacketCommandNG *c) { true, 0, ((param & ISO14A_NO_RATS) == ISO14A_NO_RATS), - ((param & ISO14A_USE_CUSTOM_POLLING) == ISO14A_USE_CUSTOM_POLLING) ? (iso14a_polling_parameters_t *)cmd : &WUPA_POLLING_PARAMETERS + ((param & ISO14A_USE_CUSTOM_POLLING) == ISO14A_USE_CUSTOM_POLLING) ? (iso14a_polling_parameters_t *)cmd : NULL, + false ); // TODO: Improve by adding a cmd parser pointer and moving it by struct length to allow combining data with polling params FpgaDisableTracing(); @@ -3427,17 +3666,23 @@ OUT: // Therefore try in alternating directions. static int32_t dist_nt(uint32_t nt1, uint32_t nt2) { - if (nt1 == nt2) return 0; + if (nt1 == nt2) { + return 0; + } 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; + 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 @@ -3445,8 +3690,8 @@ static int32_t dist_nt(uint32_t nt1, uint32_t nt2) { #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 +#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) @@ -3576,8 +3821,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time); // Receive the (4 Byte) "random" TAG nonce - if (ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) != 4) + if (ReaderReceive(receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar) != 4) { continue; + } previous_nt = nt; nt = bytes_to_num(receivedAnswer, 4); @@ -3600,9 +3846,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { // we didn't calibrate our clock yet, // iceman: has to be calibrated every time. - if (previous_nt && !nt_attacked) { + if (previous_nt && (nt_attacked == 0)) { - int nt_distance = dist_nt(previous_nt, nt); + int32_t nt_distance = dist_nt(previous_nt, nt); // if no distance between, then we are in sync. if (nt_distance == 0) { @@ -3628,7 +3874,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { sync_cycles = (sync_cycles - nt_distance) / elapsed_prng_sequences; // no negative sync_cycles, and too small sync_cycles will result in continuous misses - if (sync_cycles <= 10) sync_cycles += PRNG_SEQUENCE_LENGTH; + if (sync_cycles <= 10) { + sync_cycles += PRNG_SEQUENCE_LENGTH; + } // reset sync_cycles if (sync_cycles > PRNG_SEQUENCE_LENGTH * 2) { @@ -3636,8 +3884,14 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { sync_time = GetCountSspClk() & 0xfffffff8; } - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n", i, nt_distance, elapsed_prng_sequences, sync_cycles); + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n" + , i + , nt_distance + , elapsed_prng_sequences + , sync_cycles + ); + } continue; } @@ -3667,8 +3921,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { } else { sync_cycles += catch_up_cycles; - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d.\n", i, catch_up_cycles, sync_cycles); + } last_catch_up = 0; catch_up_cycles = 0; @@ -3681,8 +3936,9 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { 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) + if (nt_diff == 0) { 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 + } par_list[nt_diff] = reflect8(par[0]); ks_list[nt_diff] = receivedAnswer[0] ^ 0x05; // xor with NACK value to get keystream @@ -3701,12 +3957,15 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { } else { // 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; return_status = PM3_ESOFT; break; } + } else { // Why this? par[0] = ((par[0] & 0x1F) + 1) | par_low; @@ -3757,7 +4016,7 @@ void DetectNACKbug(void) { uint8_t uid[10] = { 0x00 }; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = { 0x00 }; uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = { 0x00 }; - uint8_t par[1] = {0x00 }; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough + uint8_t par[2] = {0x00 }; // maximum 8 Bytes to be sent here, 1 byte parity is therefore enough uint32_t nt = 0, previous_nt = 0, nt_attacked = 0, cuid = 0; int32_t catch_up_cycles = 0, last_catch_up = 0; @@ -3808,9 +4067,9 @@ void DetectNACKbug(void) { ++checkbtn_cnt; // this part is from Piwi's faster nonce collecting part in Hardnested. - if (!have_uid) { // need a full select cycle to get the uid first + if (have_uid == false) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; - if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (iso14443a_select_card(uid, &card_info, &cuid, true, 0, true) == 0) { if (g_dbglevel >= DBG_INFO) Dbprintf("Mifare: Can't select card (ALL)"); i = 0; continue; @@ -3832,7 +4091,7 @@ void DetectNACKbug(void) { } have_uid = true; } else { // no need for anticollision. We can directly select the card - if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (iso14443a_fast_select_card(uid, cascade_levels) == 0) { if (g_dbglevel >= DBG_INFO) Dbprintf("Mifare: Can't select card (UID)"); i = 0; have_uid = false; @@ -3985,9 +4244,7 @@ void DetectNACKbug(void) { // i = number of authentications sent. Not always 256, since we are trying to sync but close to it. FpgaDisableTracing(); - uint8_t *data = BigBuf_malloc(4); - data[0] = isOK; - data[1] = num_nacks; + uint8_t data[4] = {isOK, num_nacks, 0, 0}; num_to_bytes(i, 2, data + 2); reply_ng(CMD_HF_MIFARE_NACK_DETECT, status, data, 4); @@ -4008,8 +4265,6 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, uint8_t *getdata_response, size_t getdata_response_len) { tag_response_info_t *responses; uint32_t cuid = 0; - uint32_t counters[3] = { 0x00, 0x00, 0x00 }; - uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; uint8_t pages = 0; // command buffers @@ -4050,7 +4305,7 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, .modulation_n = 0 }; - if (SimulateIso14443aInit(tagType, flags, uid, ats, ats_len, &responses, &cuid, counters, tearings, &pages) == false) { + if (SimulateIso14443aInit(tagType, flags, uid, ats, ats_len, &responses, &cuid, &pages, NULL) == false) { BigBuf_free_keep_EM(); reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); return; @@ -4125,8 +4380,9 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, uint8_t offset = 0; switch (receivedCmd[0]) { case 0x0B: // IBlock with CID - case 0x0A: + case 0x0A: { offset = 1; + } case 0x02: // IBlock without CID case 0x03: { dynamic_response_info.response[0] = receivedCmd[0]; diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index d3463b138..5eb2e81ff 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -125,8 +125,8 @@ typedef enum { #endif void printHf14aConfig(void); -void setHf14aConfig(const hf14a_config *hc); -hf14a_config *getHf14aConfig(void); +void setHf14aConfig(const hf14a_config_t *hc); +hf14a_config_t *getHf14aConfig(void); void iso14a_set_timeout(uint32_t timeout); uint32_t iso14a_get_timeout(void); @@ -143,7 +143,7 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t void RAMFUNC SniffIso14443a(uint8_t param); void SimulateIso14443aTag(uint8_t tagType, uint16_t flags, uint8_t *useruid, uint8_t exitAfterNReads, - uint8_t *ats, size_t ats_len); + uint8_t *ats, size_t ats_len, bool ulc_part1, bool ulc_part2); void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, uint8_t *ats, size_t ats_len, uint8_t *aid, size_t aid_len, @@ -152,21 +152,25 @@ void SimulateIso14443aTagAID(uint8_t tagType, uint16_t flags, uint8_t *uid, bool SimulateIso14443aInit(uint8_t tagType, uint16_t flags, uint8_t *data, uint8_t *ats, size_t ats_len, tag_response_info_t **responses, - uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages); + uint32_t *cuid, uint8_t *pages, + uint8_t *ulc_key); bool GetIso14443aCommandFromReader(uint8_t *received, uint16_t received_maxlen, uint8_t *par, int *len); void iso14443a_antifuzz(uint32_t flags); void ReaderIso14443a(PacketCommandNG *c); -void ReaderTransmit(uint8_t *frame, uint16_t len, uint32_t *timing); -void ReaderTransmitBitsPar(uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing); -void ReaderTransmitPar(uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *timing); +void ReaderTransmit(const uint8_t *frame, uint16_t len, uint32_t *timing); +void ReaderTransmitBitsPar(const uint8_t *frame, uint16_t bits, uint8_t *par, uint32_t *timing); +void ReaderTransmitPar(const uint8_t *frame, uint16_t len, uint8_t *par, uint32_t *timing); uint16_t ReaderReceive(uint8_t *receivedAnswer, uint16_t answer_maxlen, uint8_t *par); void iso14443a_setup(uint8_t fpga_minor_mode); int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, uint16_t data_len, uint8_t *res); int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); -int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats, iso14a_polling_parameters_t *polling_parameters); -int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades); +int iso14443a_select_cardEx(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, + bool anticollision, uint8_t num_cascades, bool no_rats, + const iso14a_polling_parameters_t *polling_parameters, bool force_rats); +int iso14443a_select_card_for_magic(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades); +int iso14443a_fast_select_card(const uint8_t *uid_ptr, uint8_t num_cascades); void iso14a_set_trigger(bool enable); int EmSendCmd14443aRaw(const uint8_t *resp, uint16_t respLen); @@ -181,8 +185,9 @@ int EmSendPrecompiledCmd(tag_response_info_t *p_response); bool prepare_allocated_tag_modulation(tag_response_info_t *response_info, uint8_t **buffer, size_t *max_buffer_size); bool prepare_tag_modulation(tag_response_info_t *response_info, size_t max_buffer_size); -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); +bool EmLogTrace(const uint8_t *reader_data, uint16_t reader_len, uint32_t reader_StartTime, + uint32_t reader_EndTime, const uint8_t *reader_Parity, const uint8_t *tag_data, + uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, const uint8_t *tag_Parity); void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype); void DetectNACKbug(void); diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index 0ce6c055d..ed440c0c0 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1585,7 +1585,7 @@ static void CodeIso14443bAsReader(const uint8_t *cmd, int len, bool framing) { /* * Convenience function to encode, transmit and trace iso 14443b comms */ -static void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time, bool framing) { +void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time, bool framing) { const tosend_t *ts = get_tosend(); CodeIso14443bAsReader(cmd, len, framing); TransmitFor14443b_AsReader(start_time); @@ -1800,7 +1800,7 @@ static int iso14443b_select_cts_card(iso14b_cts_card_select_t *card) { /** * SRx Initialise. */ -static int iso14443b_select_srx_card(iso14b_card_select_t *card) { +int iso14443b_select_srx_card(iso14b_card_select_t *card) { // INITIATE command: wake up the tag using the INITIATE static const uint8_t init_srx[] = { ISO14443B_INITIATE, 0x00, 0x97, 0x5b }; uint8_t r_init[3] = { 0x00 }; @@ -2135,6 +2135,9 @@ static int iso14443b_select_picopass_card(picopass_hdr_t *hdr) { static uint8_t act_all[] = { ICLASS_CMD_ACTALL }; static uint8_t identify[] = { ICLASS_CMD_READ_OR_IDENTIFY }; static uint8_t read_conf[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x01, 0xfa, 0x22 }; + + // ICLASS_CMD_SELECT 0x81 tells ISO14443b/BPSK coding/106 kbits/s + // ICLASS_CMD_SELECT 0x41 tells ISO14443b/BPSK coding/423 kbits/s uint8_t select[] = { 0x80 | ICLASS_CMD_SELECT, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t read_aia[] = { ICLASS_CMD_READ_OR_IDENTIFY, 0x05, 0xde, 0x64}; uint8_t read_check_cc[] = { 0x80 | ICLASS_CMD_READCHECK, 0x02 }; @@ -2307,7 +2310,7 @@ void iso14443b_setup(void) { // // I tried to be systematic and check every answer of the tag, every CRC, etc... //----------------------------------------------------------------------------- -static int read_14b_srx_block(uint8_t blocknr, uint8_t *block) { +int read_14b_srx_block(uint8_t blocknr, uint8_t *block) { uint8_t cmd[] = {ISO14443B_READ_BLK, blocknr, 0x00, 0x00}; AddCrc14B(cmd, 2); diff --git a/armsrc/iso14443b.h b/armsrc/iso14443b.h index 70455ac15..7dadda7bd 100644 --- a/armsrc/iso14443b.h +++ b/armsrc/iso14443b.h @@ -45,8 +45,11 @@ int iso14443b_select_card(iso14b_card_select_t *card); void SimulateIso14443bTag(const uint8_t *pupi); void read_14b_st_block(uint8_t blocknr); +int read_14b_srx_block(uint8_t blocknr, uint8_t *block); +int iso14443b_select_srx_card(iso14b_card_select_t *card); void SniffIso14443b(void); void SendRawCommand14443B(iso14b_raw_cmd_t *p); +void CodeAndTransmit14443bAsReader(const uint8_t *cmd, int len, uint32_t *start_time, uint32_t *eof_time, bool framing); // States for 14B SIM command #define SIM_POWER_OFF 0 diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index dd3ffa865..a1ff3c721 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -985,10 +985,11 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo DecodeTagFSK_t dtfm = { 0 }; DecodeTagFSK_t *dtf = &dtfm; - if (fsk) + if (fsk) { DecodeTagFSKInit(dtf, response, max_len); - else + } else { DecodeTagInit(dt, response, max_len); + } // wait for last transfer to complete while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXEMPTY)); @@ -1014,8 +1015,9 @@ int GetIso15693AnswerFromTag(uint8_t *response, uint16_t max_len, uint16_t timeo for (;;) { volatile uint16_t behindBy = ((uint16_t *)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (DMA_BUFFER_SIZE - 1); - if (behindBy == 0) + if (behindBy == 0) { continue; + } samples++; if (samples == 1) { @@ -2657,7 +2659,7 @@ void BruteforceIso15693Afi(uint32_t flags) { Dbprintf("AFI = %i UID = %s", i, iso15693_sprintUID(NULL, recv + 2)); } - aborted = (BUTTON_PRESS() && data_available()); + aborted = (BUTTON_PRESS() || data_available()); if (aborted) { break; } diff --git a/armsrc/lfadc.c b/armsrc/lfadc.c index 38d6558d8..18712a7bb 100644 --- a/armsrc/lfadc.c +++ b/armsrc/lfadc.c @@ -236,8 +236,13 @@ void lf_init(bool reader, bool simulate, bool ledcontrol) { FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); // When in reader mode, give the field a bit of time to settle. - // 313T0 = 313 * 8us = 2504us = 2.5ms Hitag2 tags needs to be fully powered. - SpinDelay(10); + // Optimal timing window for LF ADC measurements to be performed: + // minimum: 313T0 = 313 * 8us = 2504us = 2.50ms - Hitag2 tag internal powerup time + // 280T0 = 280 * 8us = 2240us = 2.24ms - HitagS minimum time before the first command (powerup time) + // maximum: 545T0 = 545 * 8us = 4360us = 4.36ms - Hitag2 command waiting time before it starts transmitting in public mode (if configured so) + // 565T0 = 565 * 8us = 4520us = 4.52ms - HitagS waiting time before entering TTF mode (if configured so) + // Thus (2.50 ms + 4.36 ms) / 2 ~= 3 ms (rounded down to integer), should be a good timing for both tag models + SpinDelay(3); // Steal this pin from the SSP (SPI communication channel with fpga) and use it to control the modulation AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index ac93d149b..a9c0f68f1 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -83,14 +83,14 @@ static bool mifare_wakeup_auth(struct Crypto1State *pcs, MifareWakeupType wakeup break; } case MF_WAKE_WUPA: { - if (iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &WUPA_POLLING_PARAMETERS) == 0) { + if (iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &WUPA_POLLING_PARAMETERS, false) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); return false; }; break; } case MF_WAKE_REQA: { - if (iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &REQA_POLLING_PARAMETERS) == 0) { + if (iso14443a_select_cardEx(NULL, NULL, &cuid, true, 0, true, &REQA_POLLING_PARAMETERS, false) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Can't select card"); return false; }; @@ -274,7 +274,7 @@ void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes) { return; }; - if (!mifare_ultra_auth(keybytes)) { + if (mifare_ultra_auth(keybytes) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed"); OnError(1); return; @@ -304,7 +304,7 @@ void MifareUL_AES_Auth(bool turn_off_field, uint8_t keyno, uint8_t *keybytes) { return; }; - if (!mifare_ultra_aes_auth(keyno, keybytes)) { + if (mifare_ultra_aes_auth(keyno, keybytes) == 0) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Authentication failed"); OnErrorNG(CMD_HF_MIFAREULAES_AUTH, PM3_ESOFT); return; @@ -344,7 +344,7 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) { uint8_t key[16] = {0x00}; memcpy(key, datain, sizeof(key)); - if (!mifare_ultra_auth(key)) { + if (mifare_ultra_auth(key) == 0) { OnError(1); return; } @@ -1947,7 +1947,7 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da // Now append the SPI flash dictionnary if (SPIFFS_OK == rdv40_spiffs_read_as_filetype(MF_KEYS_FILE, dictkeys + (keyCount * MF_KEY_LENGTH), (key_mem_available - keyCount) * MF_KEY_LENGTH, RDV40_SPIFFS_SAFETY_SAFE)) { if (g_dbglevel >= DBG_ERROR) { - Dbprintf("loaded " _GREEN_("%u") " keys from spiffs file `" _YELLOW_("%s") "`", key_mem_available, MF_KEYS_FILE); + Dbprintf("loaded " _GREEN_("%u") " keys from spiffs file `" _YELLOW_("%s") "`", key_mem_available - keyCount, MF_KEYS_FILE); } } else { Dbprintf("Spiffs file `" _RED_("%s") "` cannot be read", MF_KEYS_FILE); @@ -1955,6 +1955,7 @@ void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *da } // Replace client provided keys datain = dictkeys; + keyCount = key_mem_available; } #endif @@ -2908,7 +2909,7 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain) { } // read block - if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL) != MAX_MIFARE_FRAME_SIZE)) { + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, sizeof(receivedAnswer), receivedAnswerPar, NULL) != MIFARE_BLOCK_SIZE + CRC16_SIZE)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("read block send command error"); errormsg = 0; break; @@ -3021,9 +3022,10 @@ void MifareCIdent(bool is_mfc, uint8_t keytype, uint8_t *key) { // reset card mf_reset_card(); - - res = iso14443a_select_card(uid, card, &cuid, true, 0, false); + // Use special magic detection function that always attempts RATS regardless of SAK + res = iso14443a_select_card_for_magic(uid, card, &cuid, true, 0); if (res) { + mf_reset_card(); if (cuid == 0xAA55C396) { flag |= MAGIC_FLAG_GEN_UNFUSED; } @@ -3219,7 +3221,7 @@ void MifareHasStaticNonce(void) { } if (counter) { - Dbprintf("Static nonce......... " _YELLOW_("%08x"), nt); + Dbprintf("Static nonce....... " _YELLOW_("%08x"), nt); data[0] = NONCE_STATIC; } else { data[0] = NONCE_NORMAL; @@ -3514,7 +3516,7 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { int retval = PM3_SUCCESS; uint8_t block_cmd[5] = { 0x90, 0xf0, 0xcc, 0xcc, 0x10 }; - uint8_t cmdlen = sizeof(block_cmd) + MAX_MIFARE_FRAME_SIZE; + uint8_t cmdlen = sizeof(block_cmd) + MIFARE_BLOCK_SIZE + CRC16_SIZE; uint8_t *cmd = BigBuf_calloc(cmdlen); iso14a_card_select_t *card_info = (iso14a_card_select_t *) BigBuf_calloc(sizeof(iso14a_card_select_t)); @@ -3531,7 +3533,7 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { bool doReselect = false; if (block_len < MIFARE_BLOCK_SIZE) { - if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, 0, &cmd[sizeof(block_cmd)], MAX_MIFARE_FRAME_SIZE, NULL, NULL) != MAX_MIFARE_FRAME_SIZE)) { + if ((mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_READBLOCK, 0, &cmd[sizeof(block_cmd)], MIFARE_BLOCK_SIZE + CRC16_SIZE, NULL, NULL) != MIFARE_BLOCK_SIZE + CRC16_SIZE)) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Read manufacturer block failed"); retval = PM3_ESOFT; goto OUT; @@ -3560,13 +3562,13 @@ void MifareGen3Blk(uint8_t block_len, uint8_t *block) { AddCrc14A(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE); if (doReselect) { - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (iso14443a_select_card(NULL, NULL, NULL, true, 0, true) == 0) { retval = PM3_ESOFT; goto OUT; } } - retval = DoGen3Cmd(cmd, sizeof(block_cmd) + MAX_MIFARE_FRAME_SIZE); + retval = DoGen3Cmd(cmd, sizeof(block_cmd) + MIFARE_BLOCK_SIZE + CRC16_SIZE); } OUT: diff --git a/armsrc/mifaredesfire.c b/armsrc/mifaredesfire.c index 448f475dd..cc2996b23 100644 --- a/armsrc/mifaredesfire.c +++ b/armsrc/mifaredesfire.c @@ -60,7 +60,7 @@ bool InitDesfireCard(void) { iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); set_tracing(true); - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, false) == 0) { if (g_dbglevel >= DBG_ERROR) DbpString("Can't select card"); OnError(1); return false; @@ -157,7 +157,7 @@ void MifareDesfireGetInformation(void) { pcb_blocknum = 0; // card select - information - if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) { + if (iso14443a_select_card(NULL, &card, NULL, true, 0, false) == 0) { if (g_dbglevel >= DBG_ERROR) { DbpString("Can't select card"); } diff --git a/armsrc/mifaresim.c b/armsrc/mifaresim.c index c58025473..21014be12 100644 --- a/armsrc/mifaresim.c +++ b/armsrc/mifaresim.c @@ -579,21 +579,6 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t counter++; } - /* - // find reader field - if (cardSTATE == MFEMUL_NOFIELD) { - - vHf = (MAX_ADC_HF_VOLTAGE * SumAdc(ADC_CHAN_HF, 32)) >> 15; - - if (vHf > MF_MINFIELDV) { - cardSTATE_TO_IDLE(); - LED_A_ON(); - } - button_pushed = BUTTON_PRESS(); - continue; - } - */ - FpgaEnableTracing(); //Now, get data int res = EmGetCmd(receivedCmd, sizeof(receivedCmd), &receivedCmd_len, receivedCmd_par); @@ -760,10 +745,6 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t // WORK case MFEMUL_WORK: { - if (g_dbglevel >= DBG_EXTENDED) { - // Dbprintf("[MFEMUL_WORK] Enter in case"); - } - if (receivedCmd_len == 0) { if (g_dbglevel >= DBG_EXTENDED) Dbprintf("[MFEMUL_WORK] NO CMD received"); break; @@ -1039,8 +1020,8 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t } } AddCrc14A(response, MIFARE_BLOCK_SIZE); - mf_crypto1_encrypt(pcs, response, MAX_MIFARE_FRAME_SIZE, response_par); - EmSendCmdPar(response, MAX_MIFARE_FRAME_SIZE, response_par); + mf_crypto1_encrypt(pcs, response, MIFARE_BLOCK_SIZE + CRC16_SIZE, response_par); + EmSendCmdPar(response, MIFARE_BLOCK_SIZE + CRC16_SIZE, response_par); FpgaDisableTracing(); if (g_dbglevel >= DBG_EXTENDED) { @@ -1052,7 +1033,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t numReads++; if (exitAfterNReads > 0 && numReads == exitAfterNReads) { - Dbprintf("[MFEMUL_WORK] %d reads done, exiting", numReads); + Dbprintf("[MFEMUL_WORK] " _YELLOW_("%u") " reads done, exiting", numReads); finished = true; } break; @@ -1309,7 +1290,7 @@ void Mifare1ksim(uint16_t flags, uint8_t exitAfterNReads, uint8_t *uid, uint16_t // WRITE BL2 case MFEMUL_WRITEBL2: { - if (receivedCmd_len == MAX_MIFARE_FRAME_SIZE) { + if (receivedCmd_len == MIFARE_BLOCK_SIZE + CRC16_SIZE) { mf_crypto1_decryptEx(pcs, receivedCmd, receivedCmd_len, receivedCmd_dec); diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 8247fae69..8c62a7a6a 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -100,7 +100,7 @@ uint16_t mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t uint16_t pos; 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 + uint8_t par[MAX_MIFARE_PARITY_SIZE] = {0x00}; // used for cmd and answer AddCrc14A(dcmd, 2); memcpy(ecmd, dcmd, sizeof(dcmd)); @@ -440,21 +440,17 @@ int mifare_ultra_aes_auth(uint8_t keyno, uint8_t *keybytes) { uint8_t key[16] = { 0 }; memcpy(key, keybytes, sizeof(key)); - uint16_t len = 0; - // 1 cmd + 16 bytes + 2 crc uint8_t resp[19] = {0x00}; uint8_t respPar[5] = {0}; - // setup AES mbedtls_aes_context actx; mbedtls_aes_init(&actx); - mbedtls_aes_init(&actx); mbedtls_aes_setkey_dec(&actx, key, 128); // Send REQUEST AUTHENTICATION / receive tag nonce - len = mifare_sendcmd_short(NULL, CRYPT_NONE, MIFARE_ULAES_AUTH_1, keyno, resp, sizeof(resp), respPar, NULL); + uint16_t len = mifare_sendcmd_short(NULL, CRYPT_NONE, MIFARE_ULAES_AUTH_1, keyno, resp, sizeof(resp), respPar, NULL); if (len != 19) { if (g_dbglevel >= DBG_ERROR) Dbprintf("Cmd Error: %02x - expected 19 got " _RED_("%u"), resp[0], len); return 0; diff --git a/armsrc/sam_common.c b/armsrc/sam_common.c index ed129134d..bfa959bba 100644 --- a/armsrc/sam_common.c +++ b/armsrc/sam_common.c @@ -218,17 +218,19 @@ out: * * @return Status code indicating success or failure of the operation. */ -int sam_get_version(void) { +int sam_get_version(bool info) { int res = PM3_SUCCESS; - if (g_dbglevel >= DBG_DEBUG) + if (g_dbglevel >= DBG_DEBUG) { DbpString("start sam_get_version"); + } - uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME); + uint8_t *response = BigBuf_calloc(ISO7816_MAX_FRAME); uint16_t response_len = ISO7816_MAX_FRAME; uint8_t payload[] = { - 0xa0, 0x02, // <- SAM command + 0xa0, // <- SAM command + 0x02, // <- Length 0x82, 0x00 // <- get version }; uint16_t payload_len = sizeof(payload); @@ -252,8 +254,9 @@ int sam_get_version(void) { // 82 01 // 01 // 90 00 - if (g_dbglevel >= DBG_DEBUG) + if (g_dbglevel >= DBG_DEBUG) { DbpString("end sam_get_version"); + } if (response[5] != 0xbd) { Dbprintf("Invalid SAM response"); @@ -266,18 +269,18 @@ int sam_get_version(void) { } uint8_t *sam_version_an = sam_find_asn1_node(sam_response_an, 0x80); if (sam_version_an == NULL) { - if (g_dbglevel >= DBG_ERROR) DbpString("SAM get version failed"); + if (g_dbglevel >= DBG_ERROR) DbpString(_RED_("SAM: get version failed")); goto error; } uint8_t *sam_build_an = sam_find_asn1_node(sam_response_an, 0x81); if (sam_build_an == NULL) { - if (g_dbglevel >= DBG_ERROR) DbpString("SAM get firmware ID failed"); + if (g_dbglevel >= DBG_ERROR) DbpString(_RED_("SAM: get firmware ID failed")); goto error; } - if (g_dbglevel >= DBG_INFO) { - DbpString("SAM get version successful"); - Dbprintf("Firmware version: %X.%X", sam_version_an[2], sam_version_an[3]); - Dbprintf("Firmware ID: "); + if (g_dbglevel >= DBG_INFO || info) { + DbpString(_BLUE_("-- SAM Information --")); + Dbprintf(_YELLOW_("Firmware version: ")"%d.%d", sam_version_an[2], sam_version_an[3]); + Dbprintf(_YELLOW_("Firmware ID: ")); Dbhexdump(sam_build_an[1], sam_build_an + 2, false); } goto out; @@ -289,8 +292,79 @@ error: out: BigBuf_free(); - if (g_dbglevel >= DBG_DEBUG) + if (g_dbglevel >= DBG_DEBUG) { DbpString("end sam_get_version"); + } + + return res; +} + +int sam_get_serial_number(void) { + int res = PM3_SUCCESS; + + if (g_dbglevel >= DBG_DEBUG) { + DbpString("start sam_get_serial_number"); + } + + uint8_t *response = BigBuf_calloc(ISO7816_MAX_FRAME); + uint16_t response_len = ISO7816_MAX_FRAME; + + uint8_t payload[] = { + 0xa0, // <- SAM command + 0x02, // <- Length + 0x96, 0x00 // <- get serial number + }; + uint16_t payload_len = sizeof(payload); + + sam_send_payload( + 0x44, 0x0a, 0x44, + payload, + &payload_len, + response, + &response_len + ); + + //resp: + //c1 64 00 00 00 + // bd 0e <- SAM response + // 8a 0c <- get serial number response + // 61 01 13 51 22 66 6e 15 3e 1b ff ff + //90 00 + + if (g_dbglevel >= DBG_DEBUG) { + DbpString("end sam_get_serial_number"); + } + + if (response[5] != 0xbd) { + Dbprintf("Invalid SAM response"); + goto error; + } else { + uint8_t *sam_response_an = sam_find_asn1_node(response + 5, 0x8a); + if (sam_response_an == NULL) { + if (g_dbglevel >= DBG_ERROR) DbpString(_RED_("SAM: get response failed")); + goto error; + } + uint8_t *sam_serial_an = sam_response_an + 2; + if (sam_serial_an == NULL) { + if (g_dbglevel >= DBG_ERROR) DbpString(_RED_("SAM get serial number failed")); + goto error; + } + + Dbprintf(_YELLOW_("Serial Number: ")); + Dbhexdump(sam_response_an[1], sam_serial_an, false); + + goto out; + } + +error: + res = PM3_ESOFT; + +out: + BigBuf_free(); + + if (g_dbglevel >= DBG_DEBUG) { + DbpString("end sam_get_serial_number"); + } return res; } @@ -350,12 +424,10 @@ void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type } void sam_send_ack(void) { - uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME); + uint8_t *response = BigBuf_calloc(ISO7816_MAX_FRAME); uint16_t response_len = ISO7816_MAX_FRAME; - uint8_t payload[] = { - 0xa0, 0 - }; + uint8_t payload[] = { 0xa0, 0 }; uint16_t payload_len = sizeof(payload); sam_send_payload( diff --git a/armsrc/sam_common.h b/armsrc/sam_common.h index 1c3a88e88..4cc6e364f 100644 --- a/armsrc/sam_common.h +++ b/armsrc/sam_common.h @@ -39,7 +39,8 @@ int sam_send_payload( uint16_t *response_len ); -int sam_get_version(void); +int sam_get_version(bool info); +int sam_get_serial_number(void); uint8_t *sam_find_asn1_node(const uint8_t *root, const uint8_t type); void sam_append_asn1_node(const uint8_t *root, const uint8_t *node, uint8_t type, const uint8_t *const data, uint8_t len); diff --git a/armsrc/sam_picopass.c b/armsrc/sam_picopass.c index 0bf2379d8..84ddf549b 100644 --- a/armsrc/sam_picopass.c +++ b/armsrc/sam_picopass.c @@ -46,11 +46,12 @@ */ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t request_len, uint8_t *response, uint8_t *response_len, const bool shallow_mod, const bool break_on_nr_mac, const bool prevent_epurse_update) { int res = PM3_SUCCESS; - if (g_dbglevel >= DBG_DEBUG) + if (g_dbglevel >= DBG_DEBUG) { DbpString("start sam_send_request_iso14a"); + } - uint8_t *buf1 = BigBuf_malloc(ISO7816_MAX_FRAME); - uint8_t *buf2 = BigBuf_malloc(ISO7816_MAX_FRAME); + uint8_t *buf1 = BigBuf_calloc(ISO7816_MAX_FRAME); + uint8_t *buf2 = BigBuf_calloc(ISO7816_MAX_FRAME); if (buf1 == NULL || buf2 == NULL) { res = PM3_EMALLOC; goto out; @@ -102,10 +103,13 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re nfc_tx_len = sam_copy_payload_sam2nfc(nfc_tx_buf, sam_rx_buf); - bool is_cmd_check = (nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_CHECK; + bool is_cmd_check = ((nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_CHECK); + if (is_cmd_check && break_on_nr_mac) { + memcpy(response, nfc_tx_buf, nfc_tx_len); *response_len = nfc_tx_len; + if (g_dbglevel >= DBG_INFO) { DbpString("NR-MAC: "); Dbhexdump((*response_len) - 1, response + 1, false); @@ -114,7 +118,8 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re goto out; } - bool is_cmd_update = (nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_UPDATE; + bool is_cmd_update = ((nfc_tx_buf[0] & 0x0F) == ICLASS_CMD_UPDATE); + if (is_cmd_update && prevent_epurse_update && nfc_tx_buf[0] == 0x87 && nfc_tx_buf[1] == 0x02) { // block update(2) command and fake the response to prevent update of epurse @@ -222,18 +227,27 @@ static int sam_send_request_iso15(const uint8_t *const request, const uint8_t re // 07 // 90 00 if (request_len == 0) { - if ( - !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03) - && - !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0) - ) { - if (g_dbglevel >= DBG_ERROR) + + if (!(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0x8a && sam_rx_buf[5 + 4] == 0x03) && + !(sam_rx_buf[5] == 0xbd && sam_rx_buf[5 + 2] == 0xb3 && sam_rx_buf[5 + 4] == 0xa0)) { + + if (g_dbglevel >= DBG_ERROR) { Dbprintf("No PACS data in SAM response"); + } res = PM3_ESOFT; } } - *response_len = sam_rx_buf[5 + 1] + 2; + if (sam_rx_buf[6] == 0x81 && sam_rx_buf[8] == 0x8a && sam_rx_buf[9] == 0x81) { //check if the response is an SNMP message + *response_len = sam_rx_buf[5 + 2] + 3; + } else { //if not, use the old logic + *response_len = sam_rx_buf[5 + 1] + 2; + } + + if (sam_rx_buf[5] == 0xBD && sam_rx_buf[4] != 0x00) { //secure channel flag is not 0x00 + Dbprintf(_YELLOW_("Secure channel flag set to: ")"%02x", sam_rx_buf[4]); + } + memcpy(response, sam_rx_buf + 5, *response_len); goto out; @@ -255,10 +269,10 @@ out: */ static int sam_set_card_detected_picopass(const picopass_hdr_t *card_select) { int res = PM3_SUCCESS; - if (g_dbglevel >= DBG_DEBUG) + if (g_dbglevel >= DBG_DEBUG) { DbpString("start sam_set_card_detected"); - - uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME); + } + uint8_t *response = BigBuf_calloc(ISO7816_MAX_FRAME); uint16_t response_len = ISO7816_MAX_FRAME; // a0 12 @@ -314,8 +328,9 @@ error: out: BigBuf_free(); - if (g_dbglevel >= DBG_DEBUG) + if (g_dbglevel >= DBG_DEBUG) { DbpString("end sam_set_card_detected"); + } return res; } @@ -336,11 +351,14 @@ int sam_picopass_get_pacs(PacketCommandNG *c) { const bool breakOnNrMac = !!(flags & BITMASK(2)); const bool preventEpurseUpdate = !!(flags & BITMASK(3)); const bool shallow_mod = !!(flags & BITMASK(4)); + const bool info = !!(flags & BITMASK(5)); uint8_t *cmd = c->data.asBytes + 1; uint16_t cmd_len = c->length - 1; int res = PM3_EFAILED; + uint8_t sam_response[ISO7816_MAX_FRAME] = { 0x00 }; + uint8_t sam_response_len = 0; clear_trace(); I2C_Reset_EnterMainProgram(); @@ -349,16 +367,21 @@ int sam_picopass_get_pacs(PacketCommandNG *c) { StartTicks(); // step 1: ping SAM - sam_get_version(); + sam_get_version(info); - if (!skipDetect) { + if (info) { + sam_get_serial_number(); + goto out; + } + + if (skipDetect == false) { // step 2: get card information picopass_hdr_t card_a_info; uint32_t eof_time = 0; // implicit StartSspClk() happens here Iso15693InitReader(); - if (!select_iclass_tag(&card_a_info, false, &eof_time, shallow_mod)) { + if (select_iclass_tag(&card_a_info, false, &eof_time, shallow_mod) == false) { goto err; } @@ -369,14 +392,14 @@ int sam_picopass_get_pacs(PacketCommandNG *c) { } // step 3: SamCommand RequestPACS, relay NFC communication - uint8_t sam_response[ISO7816_MAX_FRAME] = { 0x00 }; - uint8_t sam_response_len = 0; res = sam_send_request_iso15(cmd, cmd_len, sam_response, &sam_response_len, shallow_mod, breakOnNrMac, preventEpurseUpdate); if (res != PM3_SUCCESS) { goto err; } - if (g_dbglevel >= DBG_INFO) + + if (g_dbglevel >= DBG_INFO) { print_result("Response data", sam_response, sam_response_len); + } goto out; diff --git a/armsrc/sam_seos.c b/armsrc/sam_seos.c index 40846705a..3f8c806e9 100644 --- a/armsrc/sam_seos.c +++ b/armsrc/sam_seos.c @@ -51,13 +51,14 @@ */ static int sam_set_card_detected_seos(iso14a_card_select_t *card_select) { int res = PM3_SUCCESS; - if (g_dbglevel >= DBG_DEBUG) + if (g_dbglevel >= DBG_DEBUG) { DbpString("start sam_set_card_detected"); + } - uint8_t *request = BigBuf_malloc(ISO7816_MAX_FRAME); + uint8_t *request = BigBuf_calloc(ISO7816_MAX_FRAME); uint16_t request_len = ISO7816_MAX_FRAME; - uint8_t *response = BigBuf_malloc(ISO7816_MAX_FRAME); + uint8_t *response = BigBuf_calloc(ISO7816_MAX_FRAME); uint16_t response_len = ISO7816_MAX_FRAME; const uint8_t payload[] = { @@ -107,8 +108,9 @@ error: out: BigBuf_free(); - if (g_dbglevel >= DBG_DEBUG) + if (g_dbglevel >= DBG_DEBUG) { DbpString("end sam_set_card_detected"); + } return res; } @@ -280,7 +282,7 @@ int sam_seos_get_pacs(PacketCommandNG *c) { StartTicks(); // step 1: ping SAM - sam_get_version(); + sam_get_version(false); if (skipDetect == false) { // step 2: get card information diff --git a/armsrc/spiffs.c b/armsrc/spiffs.c index 71d0cbd12..0b2799c5a 100644 --- a/armsrc/spiffs.c +++ b/armsrc/spiffs.c @@ -312,7 +312,7 @@ static int is_valid_filename(const char *filename) { */ static void copy_in_spiffs(const char *src, const char *dst) { uint32_t size = size_in_spiffs(src); - uint8_t *mem = BigBuf_malloc(size); + uint8_t *mem = BigBuf_calloc(size); read_from_spiffs(src, (uint8_t *)mem, size); write_to_spiffs(dst, (uint8_t *)mem, size); } diff --git a/armsrc/usart.c b/armsrc/usart.c index 1dcaf7517..907f47239 100644 --- a/armsrc/usart.c +++ b/armsrc/usart.c @@ -218,7 +218,7 @@ uint32_t usart_read_ng(uint8_t *data, size_t len) { } // transfer from device to client -int usart_writebuffer_sync(uint8_t *data, size_t len) { +int usart_writebuffer_sync(const uint8_t *data, size_t len) { // Wait for current PDC bank to be free // (and check next bank too, in case there will be a usart_writebuffer_async) diff --git a/armsrc/usart.h b/armsrc/usart.h index e33745e00..e93f318f8 100644 --- a/armsrc/usart.h +++ b/armsrc/usart.h @@ -25,7 +25,7 @@ extern uint32_t g_usart_baudrate; extern uint8_t g_usart_parity; void usart_init(uint32_t baudrate, uint8_t parity); -int usart_writebuffer_sync(uint8_t *data, size_t len); +int usart_writebuffer_sync(const uint8_t *data, size_t len); uint32_t usart_read_ng(uint8_t *data, size_t len); uint16_t usart_rxdata_available(void); diff --git a/bootrom/Makefile b/bootrom/Makefile index 86c785cd1..b6825530d 100644 --- a/bootrom/Makefile +++ b/bootrom/Makefile @@ -56,7 +56,7 @@ OBJS = $(OBJDIR)/bootrom.s19 # version_pm3.c should be checked on every compilation version_pm3.c: default_version_pm3.c .FORCE $(info [=] CHECK $@) - $(Q)$(CP) $< $@ + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ all: showinfo $(OBJS) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 615c581cf..854828df9 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -434,7 +434,7 @@ set (TARGET_SOURCES add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/version_pm3.c - COMMAND ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c + COMMAND sh ${PM3_ROOT}/tools/mkversion.sh ${CMAKE_BINARY_DIR}/version_pm3.c || ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c DEPENDS ${PM3_ROOT}/common/default_version_pm3.c ) @@ -692,7 +692,7 @@ add_executable(proxmark3 ${ADDITIONAL_SRC} ) -target_compile_options(proxmark3 PUBLIC -Wall -O3) +target_compile_options(proxmark3 PUBLIC -Wall -Werror -O3) if (EMBED_READLINE) if (NOT SKIPREADLINE EQUAL 1) add_dependencies(proxmark3 ncurses readline) diff --git a/client/Makefile b/client/Makefile index f3c9f0b67..014211325 100644 --- a/client/Makefile +++ b/client/Makefile @@ -446,7 +446,7 @@ endif PM3CFLAGS += -DHAVE_SNPRINTF -CXXFLAGS ?= -Wall +CXXFLAGS ?= -Wall -Werror CXXFLAGS += $(MYDEFS) $(MYCXXFLAGS) $(MYINCLUDES) PM3CXXFLAGS = $(CXXFLAGS) @@ -889,11 +889,19 @@ endif ifneq (,$(INSTALLSHARE)) $(Q)$(INSTALLSUDO) $(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) # hack ahead: inject installation path into pm3_resources.py - $(Q)sed -i 's|^TOOLS_PATH \?= \?None|TOOLS_PATH="$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH)"|' pyscripts/pm3_resources.py - $(Q)sed -i 's|^DICTS_PATH \?= \?None|DICTS_PATH="$(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)/dictionaries"|' pyscripts/pm3_resources.py + ifeq ($(platform),Darwin) + $(Q)sed -E -i '' 's|^TOOLS_PATH \?= \?None|TOOLS_PATH="$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH)"|' pyscripts/pm3_resources.py + $(Q)sed -E -i '' 's|^DICTS_PATH \?= \?None|DICTS_PATH="$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)/dictionaries"|' pyscripts/pm3_resources.py + $(Q)$(INSTALLSUDO) $(CP) $(INSTALLSHARE) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) + $(Q)sed -E -i '' 's|^TOOLS_PATH \?=.*|TOOLS_PATH = None|' pyscripts/pm3_resources.py + $(Q)sed -E -i '' 's|^DICTS_PATH \?=.*|DICTS_PATH = None|' pyscripts/pm3_resources.py + else + $(Q)sed -i 's|^TOOLS_PATH \?= \?None|TOOLS_PATH="$(PREFIX)$(PATHSEP)$(INSTALLTOOLSRELPATH)"|' pyscripts/pm3_resources.py + $(Q)sed -i 's|^DICTS_PATH \?= \?None|DICTS_PATH="$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH)/dictionaries"|' pyscripts/pm3_resources.py $(Q)$(INSTALLSUDO) $(CP) $(INSTALLSHARE) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLSHARERELPATH) $(Q)sed -i 's|^TOOLS_PATH \?=.*|TOOLS_PATH = None|' pyscripts/pm3_resources.py $(Q)sed -i 's|^DICTS_PATH \?=.*|DICTS_PATH = None|' pyscripts/pm3_resources.py + endif endif @true @@ -987,7 +995,7 @@ src/pm3_pywrap.c: pm3.i # version_pm3.c should be checked on every compilation src/version_pm3.c: default_version_pm3.c .FORCE $(info [=] CHECK $@) - $(Q)$(CP) $< $@ + $(Q)$(SH) ../tools/mkversion.sh $@ || $(CP) $< $@ # easy printing of MAKE VARIABLES print-%: ; @echo $* = $($*) diff --git a/client/deps/amiibo.cmake b/client/deps/amiibo.cmake index 8c524c170..c946c0682 100644 --- a/client/deps/amiibo.cmake +++ b/client/deps/amiibo.cmake @@ -19,7 +19,7 @@ target_link_libraries(pm3rrg_rdv4_amiibo PRIVATE m pm3rrg_rdv4_mbedtls) -target_compile_options(pm3rrg_rdv4_amiibo PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_amiibo PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_amiibo PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pm3rrg_rdv4_amiibo PRIVATE amiitool diff --git a/client/deps/cliparser.cmake b/client/deps/cliparser.cmake index a85cc2374..fccae33b7 100644 --- a/client/deps/cliparser.cmake +++ b/client/deps/cliparser.cmake @@ -9,5 +9,5 @@ target_include_directories(pm3rrg_rdv4_cliparser PRIVATE ../../include ../src) target_include_directories(pm3rrg_rdv4_cliparser INTERFACE cliparser) -target_compile_options(pm3rrg_rdv4_cliparser PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_cliparser PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_cliparser PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/cliparser/argtable3.c b/client/deps/cliparser/argtable3.c index 0680e8dc9..080d0ad9a 100644 --- a/client/deps/cliparser/argtable3.c +++ b/client/deps/cliparser/argtable3.c @@ -3621,12 +3621,12 @@ TRex *trex_compile(const TRexChar *pattern, const TRexChar **error, int flags) { exp->_first = trex_newnode(exp, OP_EXPR); exp->_error = error; exp->_jmpbuf = malloc(sizeof(jmp_buf)); - + if (exp->_jmpbuf == NULL) { trex_free(exp); return NULL; } - + exp->_flags = flags; if (setjmp(*((jmp_buf *)exp->_jmpbuf)) == 0) { int res = trex_list(exp); diff --git a/client/deps/hardnested.cmake b/client/deps/hardnested.cmake index 468ee4ef2..ec545e2a8 100644 --- a/client/deps/hardnested.cmake +++ b/client/deps/hardnested.cmake @@ -2,7 +2,7 @@ add_library(pm3rrg_rdv4_hardnested_nosimd OBJECT hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) -target_compile_options(pm3rrg_rdv4_hardnested_nosimd PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_hardnested_nosimd PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_hardnested_nosimd PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pm3rrg_rdv4_hardnested_nosimd PRIVATE @@ -32,7 +32,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_mmx PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_mmx PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_mmx BEFORE PRIVATE -mmmx -mno-sse2 -mno-avx -mno-avx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_mmx PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -47,7 +47,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_sse2 PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_sse2 PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_sse2 BEFORE PRIVATE -mmmx -msse2 -mno-avx -mno-avx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_sse2 PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -62,7 +62,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_avx PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_avx PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_avx BEFORE PRIVATE -mmmx -msse2 -mavx -mno-avx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_avx PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -77,7 +77,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_avx2 PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_avx2 PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_avx2 BEFORE PRIVATE -mmmx -msse2 -mavx -mavx2 -mno-avx512f) set_property(TARGET pm3rrg_rdv4_hardnested_avx2 PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -92,7 +92,7 @@ if ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST X86_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_avx512 PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_avx512 PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_avx512 BEFORE PRIVATE -mmmx -msse2 -mavx -mavx2 -mavx512f) set_property(TARGET pm3rrg_rdv4_hardnested_avx512 PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -116,7 +116,7 @@ elseif ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST ARM64_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_hardnested_neon PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pm3rrg_rdv4_hardnested_neon PRIVATE @@ -134,7 +134,7 @@ elseif ("${CMAKE_SYSTEM_PROCESSOR}" IN_LIST ARM32_CPUS) hardnested/hardnested_bf_core.c hardnested/hardnested_bitarray_core.c) - target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -O3) + target_compile_options(pm3rrg_rdv4_hardnested_neon PRIVATE -Wall -Werror -O3) target_compile_options(pm3rrg_rdv4_hardnested_neon BEFORE PRIVATE -mfpu=neon) set_property(TARGET pm3rrg_rdv4_hardnested_neon PROPERTY POSITION_INDEPENDENT_CODE ON) @@ -155,7 +155,7 @@ add_library(pm3rrg_rdv4_hardnested STATIC hardnested/hardnested_bruteforce.c $ ${SIMD_TARGETS}) -target_compile_options(pm3rrg_rdv4_hardnested PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_hardnested PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_hardnested PROPERTY POSITION_INDEPENDENT_CODE ON) target_include_directories(pm3rrg_rdv4_hardnested PRIVATE ../../common diff --git a/client/deps/hardnested/hardnested_bruteforce.c b/client/deps/hardnested/hardnested_bruteforce.c index 655ef9dbb..c5d6aef43 100644 --- a/client/deps/hardnested/hardnested_bruteforce.c +++ b/client/deps/hardnested/hardnested_bruteforce.c @@ -177,14 +177,15 @@ crack_states_thread(void *x) { char progress_text[80]; char keystr[19]; - snprintf(keystr, sizeof(keystr), "%012" PRIX64 " ", key); + snprintf(keystr, sizeof(keystr), "%012" PRIX64, key); snprintf(progress_text, sizeof(progress_text), "Brute force phase completed. Key found: " _GREEN_("%s"), keystr); hardnested_print_progress(thread_arg->num_acquired_nonces, progress_text, 0.0, 0); + PrintAndLogEx(INFO, "---------+---------+---------------------------------------------------------+-----------------+-------"); break; } else if (keys_found) { break; } else { - if (!thread_arg->silent) { + if (thread_arg->silent == false) { char progress_text[80]; snprintf(progress_text, sizeof(progress_text), "Brute force phase: %6.02f%% ", 100.0 * (float)num_keys_tested / (float)(thread_arg->maximum_states)); float remaining_bruteforce = thread_arg->nonces[thread_arg->best_first_bytes[0]].expected_num_brute_force - (float)num_keys_tested / 2; @@ -337,7 +338,7 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint bucket_count = 0; for (statelist_t *p = candidates; p != NULL; p = p->next) { if (p->states[ODD_STATE] != NULL && p->states[EVEN_STATE] != NULL) { - if (!ensure_buckets_alloc(bucket_count + 1)) { + if (ensure_buckets_alloc(bucket_count + 1) == false) { PrintAndLogEx(ERR, "Can't allocate buckets, abort!"); return false; } @@ -375,6 +376,7 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint thread_args[i].best_first_bytes = best_first_bytes; pthread_create(&threads[i], NULL, crack_states_thread, (void *)&thread_args[i]); } + for (uint32_t i = 0; i < num_brute_force_threads; i++) { pthread_join(threads[i], 0); } @@ -385,11 +387,13 @@ bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint uint64_t elapsed_time = msclock() - start_time; - if (bf_rate != NULL) + if (bf_rate != NULL) { *bf_rate = (float)num_keys_tested / ((float)elapsed_time / 1000.0); + } - if (keys_found > 0) + if (keys_found > 0) { *found_key = found_bs_key; + } return (keys_found != 0); } diff --git a/client/deps/id48lib.cmake b/client/deps/id48lib.cmake index fa57d7855..47205d494 100644 --- a/client/deps/id48lib.cmake +++ b/client/deps/id48lib.cmake @@ -3,7 +3,7 @@ add_library(pm3rrg_rdv4_id48 STATIC id48/id48_generator.c id48/id48_recover.c ) -target_compile_options( pm3rrg_rdv4_id48 PRIVATE -Wpedantic -Wall -O3 -Wno-unknown-pragmas -Wno-inline -Wno-unused-function -DID48_NO_STDIO) +target_compile_options( pm3rrg_rdv4_id48 PRIVATE -Wpedantic -Wall -Werror -O3 -Wno-unknown-pragmas -Wno-inline -Wno-unused-function -DID48_NO_STDIO) target_include_directories(pm3rrg_rdv4_id48 PRIVATE id48) target_include_directories(pm3rrg_rdv4_id48 INTERFACE id48) set_property(TARGET pm3rrg_rdv4_id48 PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/jansson.cmake b/client/deps/jansson.cmake index 42c701d5e..c91a47047 100644 --- a/client/deps/jansson.cmake +++ b/client/deps/jansson.cmake @@ -14,5 +14,5 @@ add_library(pm3rrg_rdv4_jansson STATIC target_compile_definitions(pm3rrg_rdv4_jansson PRIVATE HAVE_STDINT_H) target_include_directories(pm3rrg_rdv4_jansson INTERFACE jansson) -target_compile_options(pm3rrg_rdv4_jansson PRIVATE -Wall -Wno-unused-function -O3) +target_compile_options(pm3rrg_rdv4_jansson PRIVATE -Wall -Werror -Wno-unused-function -O3) set_property(TARGET pm3rrg_rdv4_jansson PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/lua.cmake b/client/deps/lua.cmake index 3bf85e1ce..d89275be6 100644 --- a/client/deps/lua.cmake +++ b/client/deps/lua.cmake @@ -52,5 +52,5 @@ if (NOT MINGW) endif (NOT MINGW) target_include_directories(pm3rrg_rdv4_lua INTERFACE liblua) -target_compile_options(pm3rrg_rdv4_lua PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_lua PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_lua PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/mbedtls.cmake b/client/deps/mbedtls.cmake index 7c72925ab..49c141b68 100644 --- a/client/deps/mbedtls.cmake +++ b/client/deps/mbedtls.cmake @@ -49,5 +49,5 @@ add_library(pm3rrg_rdv4_mbedtls STATIC target_include_directories(pm3rrg_rdv4_mbedtls PRIVATE ../../common) target_include_directories(pm3rrg_rdv4_mbedtls INTERFACE ../../common/mbedtls) -target_compile_options(pm3rrg_rdv4_mbedtls PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_mbedtls PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_mbedtls PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/reveng.cmake b/client/deps/reveng.cmake index 1040730f1..d7e3cfd8a 100644 --- a/client/deps/reveng.cmake +++ b/client/deps/reveng.cmake @@ -13,5 +13,5 @@ target_include_directories(pm3rrg_rdv4_reveng PRIVATE ../src ../../include) target_include_directories(pm3rrg_rdv4_reveng INTERFACE reveng) -target_compile_options(pm3rrg_rdv4_reveng PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_reveng PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_reveng PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/tinycbor.cmake b/client/deps/tinycbor.cmake index c74618149..5a6abda25 100644 --- a/client/deps/tinycbor.cmake +++ b/client/deps/tinycbor.cmake @@ -11,5 +11,5 @@ add_library(pm3rrg_rdv4_tinycbor STATIC target_include_directories(pm3rrg_rdv4_tinycbor INTERFACE tinycbor) # Strange errors on Mingw when compiling with -O3 -target_compile_options(pm3rrg_rdv4_tinycbor PRIVATE -Wall -O2) +target_compile_options(pm3rrg_rdv4_tinycbor PRIVATE -Wall -Werror -O2) set_property(TARGET pm3rrg_rdv4_tinycbor PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/deps/tinycbor/compilersupport_p.h b/client/deps/tinycbor/compilersupport_p.h index 91d88229e..3223324d9 100644 --- a/client/deps/tinycbor/compilersupport_p.h +++ b/client/deps/tinycbor/compilersupport_p.h @@ -179,7 +179,9 @@ #ifndef unlikely # define unlikely(x) __builtin_expect(!!(x), 0) #endif +#ifndef unreachable # define unreachable() __builtin_unreachable() +#endif #elif defined(_MSC_VER) # define likely(x) (x) # define unlikely(x) (x) diff --git a/client/deps/whereami.cmake b/client/deps/whereami.cmake index 721873066..d2d6a5b2a 100644 --- a/client/deps/whereami.cmake +++ b/client/deps/whereami.cmake @@ -2,5 +2,5 @@ add_library(pm3rrg_rdv4_whereami STATIC whereami/whereami.c) target_compile_definitions(pm3rrg_rdv4_whereami PRIVATE WAI_PM3_TUNED) target_include_directories(pm3rrg_rdv4_whereami INTERFACE whereami) -target_compile_options(pm3rrg_rdv4_whereami PRIVATE -Wall -O3) +target_compile_options(pm3rrg_rdv4_whereami PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_whereami PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index e5af9c766..465431dd8 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -1905,15 +1905,14 @@ FF16014FEFC7 # Food GEM 6686FADE5566 # -# Samsung Data Systems (SDS) — Electronic Locks -# Gen 1 S10 KA/KB is FFFFFFFFFFFF, incompatible with Gen 2 locks -# -# SDS Gen 2 S10 KB +# Samsung Data Systems (SDS) +# 10-11 A/B (Gen 2) +9B7C25052FC3 C22E04247D9A +6C4F77534170 +704153564F6C # # Data from Discord, French pool -# SDS Gen 2 S10 KA -9B7C25052FC3 494446555455 # # Data from Discord, seems to be related to ASSA @@ -3108,3 +3107,80 @@ AB921CF0752C 265A5F32DE73 567D734C403C 2426217B3B3B +# +# Card keys from Andalusian public transport system (Consorcio de Transportes) +1848A8D1E4C5 +16EE1FE134E4 +5246B8F4ACFC +515A8209843C +0EF7636AA829 +E59D0F78C413 +5AF68604DD6B +B0BCB22DCBA3 +51B3EF60BF56 +99100225D83B +63C88F562B97 +B30B6A5AD434 +D33E4A4A0041 +9C0A4CC89D61 +5204D83D8CD3 +A662F9DC0D3D +# +# Card keys from EMT Malaga (Spain) bus system +41534E354936 +454D41343253 +4541444C4130 +46305234324E +505444505232 +5239425A3546 +454449434631 +414F4544384C +344E4F4E4937 +45444E413254 +3255534D3033 +4F554D523935 +3141544D3735 +494E47463539 +32414F4E3341 +41534C473637 +534E41395430 +41364C38364F +525241414D39 +41304532334F +4D4545494F35 +4E324C453045 +394143494E32 +5352554E3245 +324553553036 +444D414E3539 +324745413232 +4E4E41455236 +394C52493639 +4D4941413236 +414D504F3243 +434C414E3639 +# Key for Waferlock shadow programming card and shadow user card +333030313536 +# +# Poland Warsaw public transport card keys +2481118e5355 +b6f0fc87f57f +e4fdac292bed +5888180adbe6 +d572c9491137 +64ea317b7abd +a39a286285db +898989890823 +898989891789 +898989893089 +b6e56bad206a +8fe6fa230c69 +4d1095f1af34 +1ad2f99bb9e9 +891089898989 +896389898989 +890163898989 +898927638989 +898989063889 +898989428989 +898989048989 diff --git a/client/dictionaries/mfdes_default_keys.dic b/client/dictionaries/mfdes_default_keys.dic index 8611f2de9..3b4e1dc28 100644 --- a/client/dictionaries/mfdes_default_keys.dic +++ b/client/dictionaries/mfdes_default_keys.dic @@ -4,10 +4,12 @@ ffffffffffffffff 0011223344556677 1122334455667788 a0a1a2a3a4a5a6a7 +d3f7d3f7d3f7d3f7 00000000000000000000000000000000 #NXP Default 3DES/AES 000000000000000000000000000000000000000000000000 #NXP Default 3K3DES 00112233445566778899AABBCCDDEEFF0102030405060708 ffffffffffffffffffffffffffffffffffffffffffffffff +d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 425245414B4D454946594F5543414E21 # default UL-C key 00112233445566778899AABBCCDDEEFF #TI TRF7970A sloa213 79702553797025537970255379702553 #TI TRF7970A sloa213 diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index 6d4a9d9c8..7d0c952b1 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -434,7 +434,7 @@ set (TARGET_SOURCES add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/version_pm3.c - COMMAND ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c + COMMAND sh ${PM3_ROOT}/tools/mkversion.sh ${CMAKE_BINARY_DIR}/version_pm3.c || ${CMAKE_COMMAND} -E copy ${PM3_ROOT}/common/default_version_pm3.c ${CMAKE_BINARY_DIR}/version_pm3.c DEPENDS ${PM3_ROOT}/common/default_version_pm3.c ) diff --git a/client/luascripts/hf_mf_keycheck.lua b/client/luascripts/hf_mf_keycheck.lua index 29eb46072..53a003ebc 100644 --- a/client/luascripts/hf_mf_keycheck.lua +++ b/client/luascripts/hf_mf_keycheck.lua @@ -216,7 +216,7 @@ local function perform_check(uid, numsectors) for sector = 0, #keys do -- Check if user aborted if core.kbd_enter_pressed() then - print('Aborted by user') + print('Aborted via keyboard!') break end diff --git a/client/luascripts/hf_mfu_ultra.lua b/client/luascripts/hf_mfu_ultra.lua new file mode 100644 index 000000000..ed6a2b974 --- /dev/null +++ b/client/luascripts/hf_mfu_ultra.lua @@ -0,0 +1,357 @@ +local ansicolors = require('ansicolors') +local cmds = require('commands') +local getopt = require('getopt') +local lib14a = require('read14a') +local utils = require('utils') + +-- globals +copyright = '' +author = 'Dmitry Malenok' +version = 'v1.0.0' +desc = [[ +The script provides functionality for writing Mifare Ultralight Ultra/UL-5 tags. +]] +example = [[ + -- restpre (write) dump to tag + ]]..ansicolors.yellow..[[script run hf_mfu_ultra -f hf-mfu-3476FF1514D866-dump.bin -k ffffffff -r]]..ansicolors.reset..[[ + + -- wipe tag (]]..ansicolors.red..[[Do not use it with UL-5!]]..ansicolors.reset..[[) + ]]..ansicolors.yellow..[[script run hf_mfu_ultra -k 1d237f76 -w ]]..ansicolors.reset..[[ +]] +usage = [[ +script run hf_mfu_ultra -h -f -k -w -r +]] +arguments = [[ + -h this help + -f filename for the datadump to read (bin) + -k pwd to use with the restore and wipe operations + -r restore a binary dump to tag + -w wipe tag (]]..ansicolors.red..[[Do not use it with UL-5!]]..ansicolors.reset..[[) + +]] + + +local _password = nil +local _defaultPassword = 'FFFFFFFF' +local _dumpstart = 0x38*2 + 1 +--- + +--- Handles errors +local function error(err) + print(ansicolors.red.."ERROR:"..ansicolors.reset, err) + core.clearCommandBuffer() + return nil, err +end +--- + +-- sets the global password variable +local function setPassword(password) + if password == nil or #password == 0 then + _password = nil; + elseif #password ~= 8 then + return false, 'Password must be 4 hex bytes' + else + _password = password + end + return true, 'Sets' +end + + +--- Parses response data +local function parseResponse(rawResponse) + local resp = Command.parse(rawResponse) + local len = tonumber(resp.arg1) * 2 + return string.sub(tostring(resp.data), 0, len); +end +--- + +--- Sends raw data to PM3 and returns raw response if any +local function sendRaw(rawdata, options) + + local flags = lib14a.ISO14A_COMMAND.ISO14A_RAW + + if options.keep_signal then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + end + + if options.connect then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_CONNECT + end + + if options.no_select then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_SELECT + end + + if options.append_crc then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC + end + + local arg2 = #rawdata / 2 + if options.bits7 then + arg2 = arg2 | tonumber(bit32.lshift(7, 16)) + end + + local command = Command:newMIX{cmd = cmds.CMD_HF_ISO14443A_READER, + arg1 = flags, + arg2 = arg2, + data = rawdata} + return command:sendMIX(options.ignore_response) +end +--- + +--- Sends raw data to PM3 and returns parsed response +local function sendWithResponse(payload, options) + local opts; + if options then + opts = options + else + opts = {ignore_response = false, keep_signal = true, append_crc = true} + end + local rawResp, err = sendRaw(payload, opts) + if err then return err end + return parseResponse(rawResp) +end +--- + +-- Authenticates if password is provided +local function authenticate(password) + if password then + local resp, err = sendWithResponse('1B'..password) + if err then return err end + -- looking for 2 bytes (4 symbols) of PACK and 2 bytes (4 symbols) of CRC + if not resp or #resp ~=8 then return false, 'It seems that password is wrong' end + return true + end + return true +end +-- + +-- selects tag and authenticates if password is provided +local function connect() + core.clearCommandBuffer() + local info, err = lib14a.read(true, true) + if err then + lib14a.disconnect() + return false, err + end + core.clearCommandBuffer() + + return authenticate(_password) +end +-- + +-- reconnects and selects tag again +local function reconnect() + lib14a.disconnect() + utils.Sleep(1) + local info, err = connect() + if not info then return false, "Unable to select tag: "..err end + return true +end +-- + +-- checks tag version +local function checkTagVersion() + local resp, err = sendWithResponse('60'); + if err or resp == nil then return false, err end + if string.find(resp, '0034210101000E03') ~= 1 then return false, 'Wrong tag version: '..string.sub(resp,1,-5) end + return true +end +-- + +-- sends magic wakeup command +local function magicWakeup() + io.write('Sending magic wakeup command...') + local resp, err = sendRaw('5000', {ignore_response = false, append_crc = true}) + if err or resp == nil then return false, "Unable to send first magic wakeup command: "..err end + resp, err = sendRaw('40', {connect = true, no_select = true, ignore_response = false, keep_signal = true, append_crc = false, bits7 = true}) + if err or resp == nil then return false, "Unable to send first magic wakeup command: "..err end + resp, err = sendRaw('43', {ignore_response = false, keep_signal = true, append_crc = false}) + if err or resp == nil then return false, "Unable to send second magic wakeup command: "..err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + return true +end +-- + +-- Writes dump to tag +local function writeDump(filename) + print(string.rep('--',20)) + local info, err = connect() + if not info then return false, "Unable to select tag: "..err end + info, err = checkTagVersion() + if not info then return info, err end + + -- load dump from file + if not filename then return false, 'No dump filename provided' end + io.write('Loading dump from file '..filename..'...') + local dump + dump, err = utils.ReadDumpFile(filename) + if not dump then return false, err end + if #dump ~= _dumpstart - 1 + 0xa4*2 then return false, 'Invalid dump file' end + print(ansicolors.green..'done'..ansicolors.reset..'.') + + local resp + for i = 3, 0x23 do + local blockStart = i * 8 + _dumpstart + local block = string.sub(dump, blockStart, blockStart + 7) + local cblock = string.format('%02x',i) + io.write('Writing block 0x'..cblock..'...') + resp, err = sendWithResponse('A2'..cblock..block) + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + end + + -- set password + io.write('Setting password and pack ') + info, err = reconnect() + if not info then return false, err end + local passwordStart = 0x27*8 + _dumpstart + local password = string.sub(dump, passwordStart, passwordStart + 7) + local packBlock = string.sub(dump, passwordStart+8, passwordStart + 15) + io.write('(password: '..password..') (pack block: '..packBlock..')...') + resp, err = sendWithResponse('A227'..password) + if err ~= nil then return false, err end + resp, err = sendWithResponse('A228'..packBlock) + if err ~= nil then return false, err end + if not setPassword(password) then return false, 'Unable to set password' end + info, err = reconnect() + if not info then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + + -- set configs and locks + for i = 0x24, 0x26 do + local blockStart = i * 8 + _dumpstart + local block = string.sub(dump, blockStart, blockStart + 7) + local cblock = string.format('%02x',i) + io.write('Writing block 0x'..cblock..'...') + resp, err = sendWithResponse('A2'..cblock..block) + if err ~= nil then return false, err end + info, err = reconnect() + if not info then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + end + + info, err = magicWakeup() + if not info then return false, err end + -- set uid and locks + for i = 0x2, 0x0, -1 do + local blockStart = i * 8 + _dumpstart + local block = string.sub(dump, blockStart, blockStart + 7) + local cblock = string.format('%02x',i) + io.write('Writing block 0x'..cblock..'...') + resp, err = sendWithResponse('A2'..cblock..block, {connect = i == 0x2, ignore_response = false, keep_signal = i ~= 0, append_crc = true}) + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + end + + print(ansicolors.green..'The dump has been written to the tag.'..ansicolors.reset) + return true +end +-- + +-- Wipes tag +local function wipe() + print(string.rep('--',20)) + print('Wiping tag') + + local info, err = connect() + if not info then return false, "Unable to select tag: "..err end + info, err = checkTagVersion() + if not info then return info, err end + + + local resp + -- clear lock bytes on page 0x02 + resp, err = sendWithResponse('3000') + if err or resp == nil then return false, err end + local currentLowLockPage = string.sub(resp,17,24) + if(string.sub(currentLowLockPage,5,8) ~= '0000') then + info, err = magicWakeup() + if not info then return false, err end + local newLowLockPage = string.sub(currentLowLockPage,1,4)..'0000' + io.write('Clearing lock bytes on page 0x02...') + resp, err = sendWithResponse('A202'..newLowLockPage, {connect = true, ignore_response = false, keep_signal = true, append_crc = true}) + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + end + + -- clear lock bytes on page 0x24 + io.write('Clearing lock bytes on page 0x24...') + info, err = reconnect() + if not info then return false, err end + resp, err = sendWithResponse('A224000000BD') + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + + -- clear configs + io.write('Clearing cfg0 and cfg1...') + resp, err = sendWithResponse('A225000000FF') + if err ~= nil then return false, err end + resp, err = sendWithResponse('A22600050000') + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + + -- clear password + io.write('Reseting password (and pack) to default ('.._defaultPassword..') and 0000...') + info, err = reconnect() + if not info then return false, err end + resp, err = sendWithResponse('A227'.._defaultPassword) + if err ~= nil then return false, err end + resp, err = sendWithResponse('A22800000000') + if err ~= nil then return false, err end + if not setPassword(_defaultPassword) then return false, 'Unable to set password' end + info, err = reconnect() + if not info then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + + -- clear other blocks + for i = 3, 0x23 do + local cblock = string.format('%02x',i) + io.write('Clearing block 0x'..cblock..'...') + resp, err = sendWithResponse('A2'..cblock..'00000000') + if err ~= nil then return false, err end + print(ansicolors.green..'done'..ansicolors.reset..'.') + end + + print(ansicolors.green..'The tag has been wiped.'..ansicolors.reset) + + lib14a.disconnect() + return true +end +-- + +-- Prints help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +--- + +-- The main entry point +local function main(args) + if #args == 0 then return help() end + + local dumpFilename = nil + + for opt, value in getopt.getopt(args, 'hf:k:rw') do + local res, err + res = true + if opt == "h" then return help() end + if opt == "f" then dumpFilename = value end + if opt == 'k' then res, err = setPassword(value) end + if opt == 'r' then res, err = writeDump(dumpFilename) end + if opt == 'w' then res, err = wipe() end + if not res then return error(err) end + end + +end + +main(args) diff --git a/client/luascripts/kybercrystals.lua b/client/luascripts/kybercrystals.lua new file mode 100644 index 000000000..70306842b --- /dev/null +++ b/client/luascripts/kybercrystals.lua @@ -0,0 +1,747 @@ +--[[ + Simple script to program DIY kyber crystals + works on real kyber crystals and EM4305 2.12x12mm chips + simply run the program and select a profile via a number + + issues + if you are getting errors when trying to read or write a chip + run the cmd "lf tune" with no chip on the device, then move the chip over the coils till you see the lowest voltage. try different angles and in the center and or the edge of the antenna ring. + once you find the lowest voltage then try running the script again. + if thats still not working run "lf tune" again and put the chip in the best position like before + the total voltage may be too high to reduce it slowly lower tin foil over the antenna and watch the voltage. + the foil should be a bit bigger than the coil exact size does not matter. + + data pulled from here + https://docs.google.com/spreadsheets/d/13P_GE6tNYpGvoVUTEQvA3SQzMqpZ-SoiWaTNoJoTV9Q/edit?usp=sharing +--]] + +local cmds = require('commands') +local utils = require('utils') + + +function send_command(cmd) + core.console(cmd) + return "" +end + +function get_profile_data(profile_name) + local profiles = { + ["wipe chip"] = { + [0] = "00000000", + [1] = "00000000", + [2] = "00000000", + [3] = "00000000", + [4] = "00000000", + [5] = "00000000", + [6] = "00000000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Qui-Gon Jinn"] = { + [0] = "00040072", + [1] = "6147FBB3", + [2] = "00000000", + [3] = "000064FC", + [4] = "0001805F", + [5] = "000001FF", + [6] = "0C803000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Qui-Gon Jinn 2"] = { + [0] = "00040072", + [1] = "6148C1EF", + [2] = "00000000", + [3] = "000050C2", + [4] = "0001805F", + [5] = "000001FF", + [6] = "0C803000", + [7] = "00000000", + [8] = "10000040", + [9] = "00000000" + }, + ["Yoda"] = { + [0] = "00040072", + [1] = "660A50D6", + [2] = "00000000", + [3] = "0000379F", + [4] = "0001805F", + [5] = "000001FF", + [6] = "00C03000", + [7] = "00000000", + [8] = "00100040", + [9] = "00000000" + }, + ["Yoda 2"] = { + [0] = "00040072", + [1] = "667B2FEE", + [2] = "00000000", + [3] = "0000BDB6", + [4] = "0001805F", + [5] = "000001FF", + [6] = "00C03000", + [7] = "00000000", + [8] = "00100040", + [9] = "00000000" + }, + ["Yoda 8-Ball"] = { + [0] = "00040072", + [1] = "67AD7FC8", + [2] = "00000000", + [3] = "0000E0FE", + [4] = "0001805F", + [5] = "000001FF", + [6] = "5D183000", + [7] = "00000000", + [8] = "00000140", + [9] = "00000000" + }, + ["Old Obi-Wan"] = { + [0] = "00040072", + [1] = "6147BBB9", + [2] = "00000000", + [3] = "0000BE24", + [4] = "0001805F", + [5] = "000001FF", + [6] = "29803000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Old Luke"] = { + [0] = "00040072", + [1] = "614097AE", + [2] = "00000000", + [3] = "0000C134", + [4] = "0001805F", + [5] = "000001FF", + [6] = "25C03000", + [7] = "00000000", + [8] = "00100060", + [9] = "00000000" + }, + ["Old Obi-Wan 2"] = { + [0] = "00040072", + [1] = "614009A2", + [2] = "00000000", + [3] = "0000CF62", + [4] = "0001805F", + [5] = "000001FF", + [6] = "29803000", + [7] = "00000000", + [8] = "01000060", + [9] = "00000000" + }, + ["Old Luke 2"] = { + [0] = "00040072", + [1] = "75952DE5", + [2] = "00000000", + [3] = "00009988", + [4] = "0001805F", + [5] = "000001FF", + [6] = "25C03000", + [7] = "00000000", + [8] = "00010060", + [9] = "00000000" + }, + ["Old Obi-Wan 3"] = { + [0] = "00040072", + [1] = "65413B42", + [2] = "00000000", + [3] = "00001702", + [4] = "0001805F", + [5] = "000001FF", + [6] = "29803000", + [7] = "00000000", + [8] = "01000060", + [9] = "00000000" + }, + ["Mace Windu"] = { + [0] = "00040072", + [1] = "6147CCD4", + [2] = "00000000", + [3] = "0000A092", + [4] = "0001805F", + [5] = "000001FF", + [6] = "63C03000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Mace Windu 2"] = { + [0] = "00040072", + [1] = "6609B150", + [2] = "00000000", + [3] = "0000287E", + [4] = "0001805F", + [5] = "000001FF", + [6] = "63C03000", + [7] = "00000000", + [8] = "00010070", + [9] = "00000000" + }, + ["Mace Windu 3"] = { + [0] = "00040072", + [1] = "613F42AD", + [2] = "00000000", + [3] = "00002147", + [4] = "0001805F", + [5] = "000001FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "01000070", + [9] = "00000000" + }, + ["Mace Windu 4"] = { + [0] = "00040072", + [1] = "667B5B82", + [2] = "00000000", + [3] = "000050DF", + [4] = "0001805F", + [5] = "000001FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "10000070", + [9] = "00000000" + }, + ["Mace Windu 5"] = { + [0] = "00040072", + [1] = "614869C4", + [2] = "00000000", + [3] = "0000D691", + [4] = "0001805F", + [5] = "000001FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "01000070", + [9] = "00000000" + }, + ["Mace Windu 6"] = { + [0] = "00040072", + [1] = "759BEA43", + [2] = "00000000", + [3] = "00006CA0", + [4] = "0001805F", + [5] = "000001FF", + [6] = "63C03000", + [7] = "00000000", + [8] = "00100070", + [9] = "00000000" + }, + ["Mace Windu 7"] = { + [0] = "00040072", + [1] = "768E0D9D", + [2] = "00000000", + [3] = "0000668C", + [4] = "0001805F", + [5] = "000001FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "01000070", + [9] = "00000000" + }, + ["Temple Guard"] = { + [0] = "00040072", + [1] = "60954FDA", + [2] = "00000000", + [3] = "0000905A", + [4] = "0001805F", + [5] = "000001FF", + [6] = "7B003000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Maz Kanata"] = { + [0] = "00040072", + [1] = "6679DFF4", + [2] = "00000000", + [3] = "0000D691", + [4] = "0001805F", + [5] = "000001FF", + [6] = "77403000", + [7] = "00000000", + [8] = "00100030", + [9] = "00000000" + }, + ["Maz Kanata 2"] = { + [0] = "00040072", + [1] = "60953999", + [2] = "00000000", + [3] = "0000F521", + [4] = "0001805F", + [5] = "000001FF", + [6] = "77403000", + [7] = "00000000", + [8] = "00100030", + [9] = "00000000" + }, + ["Temple Guard 2"] = { + [0] = "00040072", + [1] = "667A67C5", + [2] = "00000000", + [3] = "00002B9C", + [4] = "0001805F", + [5] = "000001FF", + [6] = "7B003000", + [7] = "00000000", + [8] = "10000030", + [9] = "00000000" + }, + ["Maz Kanata 3"] = { + [0] = "00040072", + [1] = "7A213721", + [2] = "00000000", + [3] = "000083AB", + [4] = "0001805F", + [5] = "000001FF", + [6] = "77403000", + [7] = "00000000", + [8] = "00010030", + [9] = "00000000" + }, + ["Chirrut Îmwe"] = { + [0] = "00040072", + [1] = "6094F399", + [2] = "00000000", + [3] = "00009519", + [4] = "0001805F", + [5] = "000001FF", + [6] = "14403000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Chirrut Îmwe 2"] = { + [0] = "00040072", + [1] = "667A9AB7", + [2] = "00000000", + [3] = "00003BE4", + [4] = "0001805F", + [5] = "000001FF", + [6] = "14403000", + [7] = "00000000", + [8] = "00010000", + [9] = "00000000" + }, + ["Ahsoka Tano"] = { + [0] = "00040072", + [1] = "667B1626", + [2] = "00000000", + [3] = "00007907", + [4] = "0001805F", + [5] = "000001FF", + [6] = "18003000", + [7] = "00000000", + [8] = "10000000", + [9] = "00000000" + }, + ["Chirrut Îmwe 3"] = { + [0] = "00040072", + [1] = "667B7E07", + [2] = "00000000", + [3] = "00002960", + [4] = "0001805F", + [5] = "000001FF", + [6] = "14403000", + [7] = "00000000", + [8] = "00100000", + [9] = "00000000" + }, + ["Darth Vader"] = { + [0] = "00040072", + [1] = "6148C4F8", + [2] = "00000000", + [3] = "0000FDFF", + [4] = "0001805F", + [5] = "000001FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Darth Sidious"] = { + [0] = "00040072", + [1] = "613F8964", + [2] = "00000000", + [3] = "0000C0C1", + [4] = "0001805F", + [5] = "000001FF", + [6] = "52403000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Darth Maul"] = { + [0] = "00040072", + [1] = "613FD2A9", + [2] = "00000000", + [3] = "0000DAD2", + [4] = "0001805F", + [5] = "000001FF", + [6] = "46C03000", + [7] = "00000000", + [8] = "00000000", + [9] = "00000000" + }, + ["Count Dooku"] = { + [0] = "00040072", + [1] = "6140880C", + [2] = "00000000", + [3] = "0000952D", + [4] = "0001805F", + [5] = "000001FF", + [6] = "31403000", + [7] = "00000000", + [8] = "00010010", + [9] = "00000000" + }, + ["Darth Vader 2"] = { + [0] = "00040072", + [1] = "667B33DC", + [2] = "00000000", + [3] = "0000E804", + [4] = "0001805F", + [5] = "000001FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "01000010", + [9] = "00000000" + }, + ["Darth Maul 2"] = { + [0] = "00040072", + [1] = "667B26E9", + [2] = "00000000", + [3] = "00007689", + [4] = "0001805F", + [5] = "000001FF", + [6] = "46C03000", + [7] = "00000000", + [8] = "00100010", + [9] = "00000000" + }, + ["Vader 8-Ball"] = { + [0] = "00040072", + [1] = "6A92B478", + [2] = "00000000", + [3] = "00004CD1", + [4] = "0001805F", + [5] = "000001FF", + [6] = "3E183000", + [7] = "00000000", + [8] = "00000110", + [9] = "00000000" + }, + ["Darth Maul 3"] = { + [0] = "00040072", + [1] = "7597EF7E", + [2] = "00000000", + [3] = "00003BC0", + [4] = "0001805F", + [5] = "000001FF", + [6] = "46C03000", + [7] = "00000000", + [8] = "00100010", + [9] = "00000000" + }, + ["Darth Sidious 2"] = { + [0] = "00040072", + [1] = "768E4402", + [2] = "00000000", + [3] = "0000A0D2", + [4] = "0001805F", + [5] = "000001FF", + [6] = "52403000", + [7] = "00000000", + [8] = "10000010", + [9] = "00000000" + }, + ["Snoke"] = { + [0] = "00040072", + [1] = "6540BD8F", + [2] = "00000000", + [3] = "000064B9", + [4] = "0001805F", + [5] = "000001FF", + [6] = "1B183000", + [7] = "00000000", + [8] = "00001010", + [9] = "00000000" + }, + ["Luke Skywalker"] = { + [0] = "00040072", + [1] = "804B08F0", + [2] = "00000000", + [3] = "00006BF1", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "0C803000", + [7] = "00000000", + [8] = "00000000", + [9] = "050D0000" + }, + ["Luminara Unduli"] = { + [0] = "00040072", + [1] = "7B83C85A", + [2] = "00000000", + [3] = "000052CE", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "0C803000", + [7] = "00000000", + [8] = "00000000", + [9] = "180D0000" + }, + ["Plo Koon"] = { + [0] = "00040072", + [1] = "7B8998F3", + [2] = "00000000", + [3] = "00007703", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "29803000", + [7] = "00000000", + [8] = "00000000", + [9] = "040D0000" + }, + ["Plo Koon 2"] = { + [0] = "00040072", + [1] = "7B8413EA", + [2] = "00000000", + [3] = "0000D8F3", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "29803000", + [7] = "00000000", + [8] = "00000000", + [9] = "040D0000" + }, + ["Plo Koon 3"] = { + [0] = "00040072", + [1] = "7B84222B", + [2] = "00000000", + [3] = "000023E3", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "29803000", + [7] = "00000000", + [8] = "00000000", + [9] = "040D0000" + }, + ["Mace Windu S2"] = { + [0] = "00040072", + [1] = "7B8936EA", + [2] = "00000000", + [3] = "0000520D", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "00000000", + [9] = "070D0000" + }, + ["General Grievous"] = { + [0] = "00040072", + [1] = "7B896284", + [2] = "00000000", + [3] = "00008529", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "6F803000", + [7] = "00000000", + [8] = "00000000", + [9] = "060D0000" + }, + ["Rey Skywalker"] = { + [0] = "00040072", + [1] = "7B88F3F4", + [2] = "00000000", + [3] = "00001511", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "7B003000", + [7] = "00000000", + [8] = "00000000", + [9] = "170D0000" + }, + ["Rey Skywalker 2"] = { + [0] = "00040072", + [1] = "7B841039", + [2] = "00000000", + [3] = "0000EA22", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "7B003000", + [7] = "00000000", + [8] = "00000000", + [9] = "170D0000" + }, + ["Krin Dagbard"] = { + [0] = "00040072", + [1] = "7B894F46", + [2] = "00000000", + [3] = "00007BC2", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "18003000", + [7] = "00000000", + [8] = "00000000", + [9] = "140D0000" + }, + ["Krin Dagbard 2"] = { + [0] = "00040072", + [1] = "7B841797", + [2] = "00000000", + [3] = "00006A58", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "18003000", + [7] = "00000000", + [8] = "00000000", + [9] = "140D0000" + }, + ["Grand Inquisitor"] = { + [0] = "00040072", + [1] = "7B841185", + [2] = "00000000", + [3] = "00004656", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "130D0000" + }, + ["Maul"] = { + [0] = "00040072", + [1] = "7B895525", + [2] = "00000000", + [3] = "00003104", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "110D0000" + }, + ["Grand Inquisitor 2"] = { + [0] = "00040072", + [1] = "804B091A", + [2] = "00000000", + [3] = "00005909", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "130D0000" + }, + ["Asajj Ventress"] = { + [0] = "00040072", + [1] = "7A1C1F46", + [2] = "00000000", + [3] = "00008E4D", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "080D0000" + }, + ["Darth Sidious s2"] = { + [0] = "00040072", + [1] = "00000000", + [2] = "00000000", + [3] = "00000000", + [4] = "0001805F", + [5] = "18C631FF", + [6] = "5E003000", + [7] = "00000000", + [8] = "00000000", + [9] = "010D0000" + } + } + + -- When called without arguments, return the whole table + if not profile_name then + return profiles + end + + -- Otherwise return the specific profile or wipe chip + return profiles[profile_name] or profiles["wipe chip"] +end + +function get_profile_names() + -- Get the complete profiles table from get_profile_data + local all_profiles = get_profile_data() + + local names = {} + for name, _ in pairs(all_profiles) do + table.insert(names, name) + end + table.sort(names) + return names +end + +function select_profile() + local profile_names = get_profile_names() + + print("\nAvailable profiles:") + for i, name in ipairs(profile_names) do + print(string.format("%d. %s", i, name)) + end + + while true do + io.write("\nSelect profile (1-" .. #profile_names .. "): ") + local choice = tonumber(io.read()) + + if choice and choice >= 1 and choice <= #profile_names then + return profile_names[choice] + else + print("Invalid selection. Please try again.") + end + end +end + +function main() + print("\n[=== kyber crystal programmer ===]") + + -- Get profile from command line argument or prompt user + local profile_name = args and args[1] + if not profile_name then + --print("\nNo profile specified as argument.") + profile_name = select_profile() + end + + local data_to_write = get_profile_data(profile_name) + print("\n[+] Using profile: " .. profile_name) + + -- Display what will be written + print("\n[+] Data to be written:") + for addr, data in pairs(data_to_write) do + print(string.format("Address %d: %s", addr, data)) + end + + -- Step 1: Wipe the tag + print("\n[+] Wiping tag...") + send_command("lf em 4x05 wipe --4305") + + -- Step 2: Write data + print("\n[+] Writing data...") + for addr, data in pairs(data_to_write) do + send_command("lf em 4x05 write -a " .. addr .. " -d " .. data) + utils.Sleep(0.5) + end + + -- Step 3: Read back and display data for verification + print("\n[+] Verifying writes by reading back data...") + for addr, expected_data in pairs(data_to_write) do + local output = send_command("lf em 4x05 read -a " .. addr) + end + + print("\n[+] Read complete. Review output above.") +end + +main() diff --git a/client/luascripts/lf_awid_bulkclone.lua b/client/luascripts/lf_awid_bulkclone.lua index 0085a09c9..63394aa99 100644 --- a/client/luascripts/lf_awid_bulkclone.lua +++ b/client/luascripts/lf_awid_bulkclone.lua @@ -1,128 +1,128 @@ -local getopt = require('getopt') - - -copyright = '' -author = "TheChamp669" -version = 'v1.0.0' -desc = [[ -Perform bulk enrollment of 26 bit AWID style RFID Tags -For more info, check the comments in the code -]] -example = [[ - -- - script run lf_awid_bulkclone.lua -f 1 -b 1000 -]] -usage = [[ -script run lf_awid_bulkclone.lua -f facility -b base_id_num -]] -arguments = [[ - -h : this help - -f : facility id - -b : starting card id -]] -local DEBUG = true ---- --- A debug printout-function -local function dbg(args) - if not DEBUG then return end - if type(args) == 'table' then - local i = 1 - while args[i] do - dbg(args[i]) - i = i+1 - end - else - print('###', args) - end -end ---- --- This is only meant to be used when errors occur -local function oops(err) - print('ERROR:', err) - core.clearCommandBuffer() - return nil, errr -end ---- --- Usage help -local function help() - print(copyright) - print(author) - print(version) - print(desc) - print(ansicolors.cyan..'Usage'..ansicolors.reset) - print(usage) - print(ansicolors.cyan..'Arguments'..ansicolors.reset) - print(arguments) - print(ansicolors.cyan..'Example usage'..ansicolors.reset) - print(example) -end ---- --- Exit message -local function exitMsg(msg) - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print(msg) - print() -end - -local function showHelp() - print("Usage: script run [-h]") - print("Options:") - print("-h \t This help") -end - -local function main(args) - - - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print() - - if #args == 0 then return help() end - - for o, a in getopt.getopt(args, 'f:b:c:h') do - if o == 'h' then return help() end - if o == 'f' then - if isempty(a) then - print('You did not supply a facility code, using 255') - fc = 255 - else - fc = a - end - end - if o == 'b' then - if isempty(a) then - print('You did not supply a starting card number, using 59615') - cn = 59615 - else - cn = a - end - end - end - - -- Example starting values - local sessionStart = os.date("%Y_%m_%d_%H_%M_%S") -- Capture the session start time - - print("Session Start: " .. sessionStart) - print("Facility Code,Card Number") - - while true do - print(string.format("Preparing to Write: Facility Code %d, Card Number %d", fc, cn)) - - local command = string.format("lf awid clone --fmt 26 --fc %d --cn %d", fc, cn) - core.console(command) - - print(string.format("%d,%d", fc, cn)) - - print("Press Enter to continue with the next card number or type 'q' and press Enter to quit.") - local user_input = io.read() - - if user_input:lower() == 'q' then - break - else - cn = cn + 1 - end - end -end - +local getopt = require('getopt') + + +copyright = '' +author = "TheChamp669" +version = 'v1.0.0' +desc = [[ +Perform bulk enrollment of 26 bit AWID style RFID Tags +For more info, check the comments in the code +]] +example = [[ + -- + script run lf_awid_bulkclone.lua -f 1 -b 1000 +]] +usage = [[ +script run lf_awid_bulkclone.lua -f facility -b base_id_num +]] +arguments = [[ + -h : this help + -f : facility id + -b : starting card id +]] +local DEBUG = true +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, errr +end +--- +-- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +--- +-- Exit message +local function exitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end + +local function showHelp() + print("Usage: script run [-h]") + print("Options:") + print("-h \t This help") +end + +local function main(args) + + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + if #args == 0 then return help() end + + for o, a in getopt.getopt(args, 'f:b:c:h') do + if o == 'h' then return help() end + if o == 'f' then + if isempty(a) then + print('You did not supply a facility code, using 255') + fc = 255 + else + fc = a + end + end + if o == 'b' then + if isempty(a) then + print('You did not supply a starting card number, using 59615') + cn = 59615 + else + cn = a + end + end + end + + -- Example starting values + local sessionStart = os.date("%Y_%m_%d_%H_%M_%S") -- Capture the session start time + + print("Session Start: " .. sessionStart) + print("Facility Code,Card Number") + + while true do + print(string.format("Preparing to Write: Facility Code %d, Card Number %d", fc, cn)) + + local command = string.format("lf awid clone --fmt 26 --fc %d --cn %d", fc, cn) + core.console(command) + + print(string.format("%d,%d", fc, cn)) + + print("Press Enter to continue with the next card number or type 'q' and press Enter to quit.") + local user_input = io.read() + + if user_input:lower() == 'q' then + break + else + cn = cn + 1 + end + end +end + main(args) diff --git a/client/luascripts/lf_electra.lua b/client/luascripts/lf_electra.lua index 5b0886aa4..7502522e8 100644 --- a/client/luascripts/lf_electra.lua +++ b/client/luascripts/lf_electra.lua @@ -299,7 +299,7 @@ local function main(args) if answer == 'n' then core.console('clear') print( string.rep('--',39) ) - print(ac.red..' USER ABORTED'..ac.reset) + print(ac.red..' Aborted via keyboard!'..ac.reset) print( string.rep('--',39) ) break end diff --git a/client/luascripts/lf_em4100_bulk.lua b/client/luascripts/lf_em4100_bulk.lua index 87d8bc91b..8a2ff399e 100644 --- a/client/luascripts/lf_em4100_bulk.lua +++ b/client/luascripts/lf_em4100_bulk.lua @@ -198,7 +198,7 @@ local function main(args) core.console('lf em 410x reader') end else - print(ac.red..'User aborted'..ac.reset) + print(ac.red..'aborted via keyboard!'..ac.reset) low = i break end diff --git a/client/luascripts/lf_hid_bulkclone_v2.lua b/client/luascripts/lf_hid_bulkclone_v2.lua index 33d084dd8..31615d19d 100644 --- a/client/luascripts/lf_hid_bulkclone_v2.lua +++ b/client/luascripts/lf_hid_bulkclone_v2.lua @@ -1,131 +1,131 @@ -local getopt = require('getopt') -local ansicolors = require('ansicolors') -local cmds = require('commands') - -copyright = '' -author = "TheChamop669" -version = 'v1.0.1' -desc = [[ -Perform bulk enrollment of 26 bit H10301 style RFID Tags -For more info, check the comments in the code -]] -example = [[ - -- - script run lf_hid_bulkclone_v2.lua -f 1 -b 1000 -]] -usage = [[ -script run lf_hid_bulkclone_v2.lua -f facility -b base_id_num -]] -arguments = [[ - -h : this help - -f : facility id - -b : starting card id -]] -local DEBUG = true ---- --- A debug printout-function -local function dbg(args) - if not DEBUG then return end - if type(args) == 'table' then - local i = 1 - while args[i] do - dbg(args[i]) - i = i+1 - end - else - print('###', args) - end -end ---- --- This is only meant to be used when errors occur -local function oops(err) - print('ERROR:', err) - core.clearCommandBuffer() - return nil, errr -end ---- --- Usage help -local function help() - print(copyright) - print(author) - print(version) - print(desc) - print(ansicolors.cyan..'Usage'..ansicolors.reset) - print(usage) - print(ansicolors.cyan..'Arguments'..ansicolors.reset) - print(arguments) - print(ansicolors.cyan..'Example usage'..ansicolors.reset) - print(example) -end ---- --- Exit message -local function exitMsg(msg) - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print(msg) - print() -end - -local function main(args) - - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print() - - if #args == 0 then return help() end - - for o, a in getopt.getopt(args, 'f:b:h') do - if o == 'h' then return help() end - if o == 'f' then - if isempty(a) then - print('You did not supply a facility code, using 0') - fc = 10 - else - fc = a - end - end - if o == 'b' then - if isempty(a) then - print('You did not supply a starting card number, using 1000') - cn = 1000 - else - cn = a - end - end - end - - local successful_writes = {} - local timestamp = os.date('%Y-%m-%d %H:%M:%S', os.time()) - - while true do - print(string.format("Writing Facility Code: %d, Card Number: %d", fc, cn)) - - local command = string.format("lf hid clone -w H10301 --fc %d --cn %d", fc, cn) - core.console(command) - - table.insert(successful_writes, string.format("%d,%d", fc, cn)) - - print("Press Enter to write the next card, type 'r' and press Enter to retry, or type 'q' and press Enter to quit.") - local user_input = io.read() - - if user_input:lower() == 'q' then - print("Timestamp: ", timestamp) - print("Successful Writes:") - for _, v in ipairs(successful_writes) do print(v) end - break - elseif user_input:lower() ~= 'r' then - cn = cn + 1 - end - end -end - -main(args) - ---[[ -Notes: -1. The `lf hid clone` command is used to write HID formatted data to T5577 cards, using the H10301 format. -2. The script prompts the user for the initial facility code and card number at the start of the session. -3. Users can continue to write to the next card, retry the current write, or quit the session by responding to the prompts. -4. Upon quitting, the script prints all successful writes along with a timestamp. -5. Password-related features have been removed in this version of the script as they are not supported by the `lf hid clone` command. -]] +local getopt = require('getopt') +local ansicolors = require('ansicolors') +local cmds = require('commands') + +copyright = '' +author = "TheChamop669" +version = 'v1.0.1' +desc = [[ +Perform bulk enrollment of 26 bit H10301 style RFID Tags +For more info, check the comments in the code +]] +example = [[ + -- + script run lf_hid_bulkclone_v2.lua -f 1 -b 1000 +]] +usage = [[ +script run lf_hid_bulkclone_v2.lua -f facility -b base_id_num +]] +arguments = [[ + -h : this help + -f : facility id + -b : starting card id +]] +local DEBUG = true +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, errr +end +--- +-- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +--- +-- Exit message +local function exitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end + +local function main(args) + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + if #args == 0 then return help() end + + for o, a in getopt.getopt(args, 'f:b:h') do + if o == 'h' then return help() end + if o == 'f' then + if isempty(a) then + print('You did not supply a facility code, using 0') + fc = 10 + else + fc = a + end + end + if o == 'b' then + if isempty(a) then + print('You did not supply a starting card number, using 1000') + cn = 1000 + else + cn = a + end + end + end + + local successful_writes = {} + local timestamp = os.date('%Y-%m-%d %H:%M:%S', os.time()) + + while true do + print(string.format("Writing Facility Code: %d, Card Number: %d", fc, cn)) + + local command = string.format("lf hid clone -w H10301 --fc %d --cn %d", fc, cn) + core.console(command) + + table.insert(successful_writes, string.format("%d,%d", fc, cn)) + + print("Press Enter to write the next card, type 'r' and press Enter to retry, or type 'q' and press Enter to quit.") + local user_input = io.read() + + if user_input:lower() == 'q' then + print("Timestamp: ", timestamp) + print("Successful Writes:") + for _, v in ipairs(successful_writes) do print(v) end + break + elseif user_input:lower() ~= 'r' then + cn = cn + 1 + end + end +end + +main(args) + +--[[ +Notes: +1. The `lf hid clone` command is used to write HID formatted data to T5577 cards, using the H10301 format. +2. The script prompts the user for the initial facility code and card number at the start of the session. +3. Users can continue to write to the next card, retry the current write, or quit the session by responding to the prompts. +4. Upon quitting, the script prints all successful writes along with a timestamp. +5. Password-related features have been removed in this version of the script as they are not supported by the `lf hid clone` command. +]] diff --git a/client/luascripts/lf_ioprox_bulkclone.lua b/client/luascripts/lf_ioprox_bulkclone.lua index 46e33c63a..37e9c4ce9 100644 --- a/client/luascripts/lf_ioprox_bulkclone.lua +++ b/client/luascripts/lf_ioprox_bulkclone.lua @@ -1,125 +1,125 @@ -local getopt = require('getopt') -local cmds = require('commands') - - -copyright = '' -author = "TheChamp669" -version = 'v1.0.0' -desc = [[ -Perform bulk enrollment of 26 bit IO Prox / Kantech style RFID Tags -The vnc is set to 2. -For more info, check the comments in the code -]] -example = [[ - -- - script run lf_ioprox_bulkclone.lua -f 1 -b 1000 -]] -usage = [[ -script run lf_ioprox_bulkclone.lua -f facility -b base_id_num -]] -arguments = [[ - -h : this help - -f : facility id - -b : starting card id -]] -local DEBUG = true - ---- --- A debug printout-function -local function dbg(args) - if not DEBUG then return end - if type(args) == 'table' then - local i = 1 - while args[i] do - dbg(args[i]) - i = i+1 - end - else - print('###', args) - end -end ---- --- This is only meant to be used when errors occur -local function oops(err) - print('ERROR:', err) - core.clearCommandBuffer() - return nil, errr -end ---- --- Usage help -local function help() - print(copyright) - print(author) - print(version) - print(desc) - print(ansicolors.cyan..'Usage'..ansicolors.reset) - print(usage) - print(ansicolors.cyan..'Arguments'..ansicolors.reset) - print(arguments) - print(ansicolors.cyan..'Example usage'..ansicolors.reset) - print(example) -end ---- --- Exit message -local function exitMsg(msg) - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print(msg) - print() -end - -local function main(args) - - print( string.rep('--',20) ) - print( string.rep('--',20) ) - print() - - if #args == 0 then return help() end - - for o, a in getopt.getopt(args, 'f:b:h') do - if o == 'h' then return help() end - if o == 'f' then - if isempty(a) then - print('You did not supply a facility code, using 0') - fc = 0 - else - fc = a - end - end - if o == 'b' then - if isempty(a) then return oops('You must supply the flag -b (starting card number)') end - cn = a - end - end - - local successful_writes = {} - local timestamp = os.date('%Y-%m-%d %H:%M:%S', os.time()) - - while true do - print(string.format("Setting fob to Facility Code: %d, Card Number: %d", fc, cn)) - - -- Writing to block 0 with the specific data for ioProx card format - core.console("lf t55xx write -b 0 -d 00147040") - - -- Command to set facility code and card number on the fob - local command = string.format("lf io clone --vn 2 --fc %d --cn %d", fc, cn) - core.console(command) - - table.insert(successful_writes, string.format("%d,%d", fc, cn)) - print("Fob created successfully.") - - print("Press Enter to create the next fob, type 'r' and press Enter to retry, or type 'q' and press Enter to quit.") - local user_input = io.read() - - if user_input:lower() == 'q' then - print("Timestamp: ", timestamp) - print("Successful Writes:") - for _, v in ipairs(successful_writes) do print(v) end - break - elseif user_input:lower() ~= 'r' then - cn = cn + 1 - end - end -end - -main(args) +local getopt = require('getopt') +local cmds = require('commands') + + +copyright = '' +author = "TheChamp669" +version = 'v1.0.0' +desc = [[ +Perform bulk enrollment of 26 bit IO Prox / Kantech style RFID Tags +The vnc is set to 2. +For more info, check the comments in the code +]] +example = [[ + -- + script run lf_ioprox_bulkclone.lua -f 1 -b 1000 +]] +usage = [[ +script run lf_ioprox_bulkclone.lua -f facility -b base_id_num +]] +arguments = [[ + -h : this help + -f : facility id + -b : starting card id +]] +local DEBUG = true + +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, errr +end +--- +-- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end +--- +-- Exit message +local function exitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end + +local function main(args) + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + if #args == 0 then return help() end + + for o, a in getopt.getopt(args, 'f:b:h') do + if o == 'h' then return help() end + if o == 'f' then + if isempty(a) then + print('You did not supply a facility code, using 0') + fc = 0 + else + fc = a + end + end + if o == 'b' then + if isempty(a) then return oops('You must supply the flag -b (starting card number)') end + cn = a + end + end + + local successful_writes = {} + local timestamp = os.date('%Y-%m-%d %H:%M:%S', os.time()) + + while true do + print(string.format("Setting fob to Facility Code: %d, Card Number: %d", fc, cn)) + + -- Writing to block 0 with the specific data for ioProx card format + core.console("lf t55xx write -b 0 -d 00147040") + + -- Command to set facility code and card number on the fob + local command = string.format("lf io clone --vn 2 --fc %d --cn %d", fc, cn) + core.console(command) + + table.insert(successful_writes, string.format("%d,%d", fc, cn)) + print("Fob created successfully.") + + print("Press Enter to create the next fob, type 'r' and press Enter to retry, or type 'q' and press Enter to quit.") + local user_input = io.read() + + if user_input:lower() == 'q' then + print("Timestamp: ", timestamp) + print("Successful Writes:") + for _, v in ipairs(successful_writes) do print(v) end + break + elseif user_input:lower() ~= 'r' then + cn = cn + 1 + end + end +end + +main(args) diff --git a/client/luascripts/paxton_clone.lua b/client/luascripts/paxton_clone.lua index fd3beb280..b8aaf2a9d 100644 --- a/client/luascripts/paxton_clone.lua +++ b/client/luascripts/paxton_clone.lua @@ -1,488 +1,488 @@ -local getopt = require('getopt') -local utils = require('utils') -local ac = require('ansicolors') -local os = require('os') -local dash = string.rep('--', 32) -local dir = os.getenv('HOME') .. '/.proxmark3/logs/' -local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+")) -local log_file_path = dir .. "Paxton_log.txt" -local nam = "" -local pm3 = require('pm3') -p = pm3.pm3() -local command = core.console -command('clear') - -author = ' Author: jareckib - 30.01.2025' -tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102' -version = ' version v1.20' -desc = [[ - The script automates the copying of Paxton fobs read - write. - It also allows manual input of data for blocks 4-7. - The third option is reading data stored in the log file and create new fob. - Additionally, the script calculates the ID for downgrading Paxton to EM4102. - - ]] -usage = [[ - script run paxton_clone -]] -arguments = [[ - script run paxton_clone -h : this help -]] - -local debug = true - -local function dbg(args) - if not DEBUG then return end - if type(args) == 'table' then - local i = 1 - while args[i] do - dbg(args[i]) - i = i+1 - end - else - print('###', args) - end -end - -local function help() - print() - print(author) - print(tutorial) - print(version) - print(desc) - print(ac.cyan..' Usage'..ac.reset) - print(usage) - print(ac.cyan..' Arguments'..ac.reset) - print(arguments) -end - -local function reset_log_file() - local file = io.open(logfile, "w+") - file:write("") - file:close() -end - -local function read_log_file(logfile) - local file = io.open(logfile, "r") - if not file then - error(" Could not open the file") - end - local content = file:read("*all") - file:close() - return content -end - -local function parse_blocks(result) - local blocks = {} - for line in result:gmatch("[^\r\n]+") do - local block_num, block_data = line:match("%[%=%]%s+%d/0x0([4-7])%s+%|%s+([0-9A-F ]+)") - if block_num and block_data then - block_num = tonumber(block_num) - block_data = block_data:gsub("%s+", "") - blocks[block_num] = block_data - end - end - return blocks -end - -local function hex_to_bin(hex_string) - local bin_string = "" - local hex_to_bin_map = { - ['0'] = "0000", ['1'] = "0001", ['2'] = "0010", ['3'] = "0011", - ['4'] = "0100", ['5'] = "0101", ['6'] = "0110", ['7'] = "0111", - ['8'] = "1000", ['9'] = "1001", ['A'] = "1010", ['B'] = "1011", - ['C'] = "1100", ['D'] = "1101", ['E'] = "1110", ['F'] = "1111" - } - for i = 1, #hex_string do - bin_string = bin_string .. hex_to_bin_map[hex_string:sub(i, i)] - end - return bin_string -end - -local function remove_last_two_bits(binary_str) - return binary_str:sub(1, #binary_str - 2) -end - -local function split_into_5bit_chunks(binary_str) - local chunks = {} - for i = 1, #binary_str, 5 do - table.insert(chunks, binary_str:sub(i, i + 4)) - end - return chunks -end - -local function remove_parity_bit(chunks) - local no_parity_chunks = {} - for _, chunk in ipairs(chunks) do - if #chunk == 5 then - table.insert(no_parity_chunks, chunk:sub(2)) - end - end - return no_parity_chunks -end - -local function convert_to_hex(chunks) - local hex_values = {} - for _, chunk in ipairs(chunks) do - if #chunk > 0 then - table.insert(hex_values, string.format("%X", tonumber(chunk, 2))) - end - end - return hex_values -end - -local function convert_to_decimal(chunks) - local decimal_values = {} - for _, chunk in ipairs(chunks) do - table.insert(decimal_values, tonumber(chunk, 2)) - end - return decimal_values -end - -local function find_until_before_f(hex_values) - local result = {} - for _, value in ipairs(hex_values) do - if value == 'F' then - break - end - table.insert(result, value) - end - return result -end - -local function process_block(block) - local binary_str = hex_to_bin(block) - binary_str = remove_last_two_bits(binary_str) - local chunks = split_into_5bit_chunks(binary_str) - local no_parity_chunks = remove_parity_bit(chunks) - return no_parity_chunks -end - -local function calculate_id_net(blocks) - local all_hex_values = {} - for _, block in ipairs(blocks) do - local hex_values = convert_to_hex(process_block(block)) - for _, hex in ipairs(hex_values) do - table.insert(all_hex_values, hex) - end - end - local selected_hex_values = find_until_before_f(all_hex_values) - if #selected_hex_values == 0 then - error(ac.red..' Error: '..ac.reset..'No valid data found in blocks 4 and 5') - end - local combined_hex = table.concat(selected_hex_values) - if not combined_hex:match("^%x+$") then - error(ac.red..' Error: '..ac.reset..'Invalid data in blocks 4 and 5') - end - local decimal_id = tonumber(combined_hex) - local stripped_hex_id = string.format("%X", decimal_id) - local padded_hex_id = string.format("%010X", decimal_id) - return decimal_id, padded_hex_id -end - -local function calculate_id_switch(blocks) - local all_decimal_values = {} - for _, block in ipairs(blocks) do - local decimal_values = convert_to_decimal(process_block(block)) - for _, dec in ipairs(decimal_values) do - table.insert(all_decimal_values, dec) - end - end - if #all_decimal_values < 15 then - error(ac.red..' Error:'..ac.reset..' Not enough data after processing blocks 4, 5, 6, and 7') - end - local id_positions = {9, 11, 13, 15, 2, 4, 6, 8} - local id_numbers = {} - for _, pos in ipairs(id_positions) do - table.insert(id_numbers, all_decimal_values[pos]) - end - local decimal_id = tonumber(table.concat(id_numbers)) - local padded_hex_id = string.format("%010X", decimal_id) - return decimal_id, padded_hex_id -end - -local function name_exists_in_log(name) - local file = io.open(log_file_path, "r") - if not file then - return false - end - local pattern = "^Name:%s*" .. name .. "%s*$" - for line in file:lines() do - if line:match(pattern) then - file:close() - return true - end - end - file:close() - return false -end - -local function log_result(blocks, em410_id, name) - local log_file = io.open(log_file_path, "a") - if log_file then - log_file:write("Name: " .. name .. "\n") - log_file:write("Date: ", os.date("%Y-%m-%d %H:%M:%S"), "\n") - for i = 4, 7 do - log_file:write(string.format("Block %d: %s\n", i, blocks[i] or "nil")) - end - log_file:write(string.format('EM4102 ID: %s\n', em410_id or "nil")) - log_file:write('--------------------------\n') - log_file:close() - print(' Log saved as: pm3/.proxmark3/logs/' ..ac.yellow..' Paxton_log.txt'..ac.reset) - else - print(" Failed to open log file for writing.") - end -end - -local function verify_written_data(original_blocks) - p:console('lf hitag read --ht2 -k BDF5E846') - local result = read_log_file(logfile) - local verified_blocks = parse_blocks(result) - local success = true - for i = 4, 7 do - if original_blocks[i] ~= verified_blocks[i] then - print(' Verification failed.. Block '..ac.green.. i ..ac.reset.. ' inconsistent.') - success = false - end - end - - if success then - print(ac.green..' Verification successful. Data was written correctly.' .. ac.reset) - else - print(ac.yellow.. ' Adjust the position of the Paxton fob on the coil.' .. ac.reset) - end -end - -local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) - while true do - io.write(" Create Paxton choose " .. ac.cyan .. "1" .. ac.reset .. " or EM4102 choose " .. ac.cyan .. "2 " .. ac.reset) - local choice = io.read() - if choice == "1" then - io.write(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..") - io.read() - print(dash) - p:console("lf hitag wrbl --ht2 -p 4 -d " .. blocks[4] .. " -k BDF5E846") - p:console("lf hitag wrbl --ht2 -p 5 -d " .. blocks[5] .. " -k BDF5E846") - p:console("lf hitag wrbl --ht2 -p 6 -d " .. blocks[6] .. " -k BDF5E846") - p:console("lf hitag wrbl --ht2 -p 7 -d " .. blocks[7] .. " -k BDF5E846") - reset_log_file() - --timer(5) - verify_written_data(blocks) - elseif choice == "2" then - io.write(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..") - io.read() - p:console("lf em 410x clone --id " .. padded_hex_id) - print(' Cloned EM4102 to T5577 with ID ' ..ac.green.. padded_hex_id ..ac.reset) - else - print(ac.yellow .. " Invalid choice." .. ac.reset .. " Please enter " .. ac.cyan .. "1" .. ac.reset .. " or " .. ac.cyan .. "2" .. ac.reset) - goto ask_again - end - while true do - print(dash) - io.write(" Make next RFID Fob"..ac.cyan.." (y/n) "..ac.reset) - local another = io.read() - if another:lower() == "n" then - if was_option_3 then - print(" No writing to Paxton_log.txt - Name: " ..ac.green.. nam .. ac.reset.. " exist") - return - end - print() - print(ac.green .. " Saving Paxton_log file..." .. ac.reset) - while true do - io.write(" Enter a name for database (cannot be empty/duplicate): "..ac.yellow) - name = io.read() - io.write(ac.reset..'') - if name == nil or name:match("^%s*$") then - print(ac.red .. ' ERROR:'..ac.reset..' Name cannot be empty.') - else - if name_exists_in_log(name) then - print(ac.yellow .. ' Name exists!!! '..ac.reset.. 'Please choose a different name.') - else - break - end - end - end - log_result(blocks, padded_hex_id, name) - print(ac.green .. " Log saved successfully!" .. ac.reset) - reset_log_file() - return - elseif another:lower() == "y" then - goto ask_again - else - print(ac.yellow.." Invalid response."..ac.reset.." Please enter"..ac.cyan.." y"..ac.reset.." or"..ac.cyan.." n"..ac.reset) - end - end - ::ask_again:: - end -end - -local function is_valid_hex(input) - return #input == 8 and input:match("^[0-9A-Fa-f]+$") -end - -local function main(args) - while true do - for o, a in getopt.getopt(args, 'h') do - if o == 'h' then return help() end - end - command('clear') - print(dash) - print(ac.green .. ' Select option: ' .. ac.reset) - print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy') - print(ac.cyan .. ' 2' .. ac.reset .. ' - Manually input data for Paxton blocks 4-7') - print(ac.cyan .. " 3" .. ac.reset .. " - Search in Paxton_log by name and use the data") - print(dash) - while true do - io.write(' Your choice '..ac.cyan..'(1/2/3): ' .. ac.reset) - input_option = io.read() - if input_option == "1" or input_option == "2" or input_option == "3" then - break - else - print(ac.yellow .. ' Invalid choice.' .. ac.reset .. ' Please enter ' .. ac.cyan .. '1' .. ac.reset .. ' or ' .. ac.cyan .. '2' .. ac.reset..' or'..ac.cyan..' 3'..ac.reset) - end - end - local was_option_3 = false - if input_option == "1" then - local show_place_message = true - while true do - if show_place_message then - io.write(' Place the' .. ac.cyan .. ' Paxton' .. ac.reset .. ' Fob on the coil to read..' .. ac.green .. 'ENTER' .. ac.reset .. ' to continue..') - end - io.read() - print(dash) - p:console('lf hitag read --ht2 -k BDF5E846') - if not logfile then - error(" No files in this directory") - end - local result = read_log_file(logfile) - local blocks = parse_blocks(result) - local empty_block = false - for i = 4, 7 do - if not blocks[i] then - empty_block = true - break - end - end - if empty_block then - io.write(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..') - show_place_message = false - else - print(' Readed blocks:') - print() - for i = 4, 7 do - if blocks[i] then - print(string.format(" Block %d: %s%s%s", i, ac.yellow, blocks[i], ac.reset)) - end - end - local decimal_id, padded_hex_id - if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then - print(dash) - print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset) - decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]}) - else - print(dash) - print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset) - decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]}) - end - print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset)) - print(dash) - handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) - break - end - end - elseif input_option == "2" then - local blocks = {} - for i = 4, 7 do - while true do - io.write(ac.reset..' Enter data for block ' .. i .. ': ' .. ac.yellow) - local input = io.read() - input = input:upper() - if is_valid_hex(input) then - blocks[i] = input - break - else - print(ac.yellow .. ' Invalid input.' .. ac.reset .. ' Each block must be 4 bytes (8 hex characters).') - end - end - end - local decimal_id, padded_hex_id - if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then - print(ac.reset.. dash) - print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset) - decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]}) - else - print(ac.reset.. dash) - print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset) - decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]}) - end - print(dash) - print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset)) - print(dash) - if not padded_hex_id then - print(ac.red..' ERROR: '..ac.reset.. 'Invalid block data provided') - return - end - handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) - break - elseif input_option == "3" then - was_option_3 = true - local retries = 3 - while retries > 0 do - io.write(' Enter the name to search ('..retries..' attempts) : '..ac.yellow) - local user_input = io.read() - io.write(ac.reset..'') - if user_input == nil or user_input:match("^%s*$") then - print(ac.yellow..' Error: '..ac.reset.. 'Empty name !!!') - end - local name_clean = "^Name:%s*" .. user_input:gsub("%s", "%%s") .. "%s*$" - local file = io.open(log_file_path, "r") - if not file then - print(ac.red .. ' Error:'..ac.reset.. 'Could not open log file.') - return - end - local lines = {} - for line in file:lines() do - table.insert(lines, line) - end - file:close() - local found = false - for i = 1, #lines do - if lines[i]:match(name_clean) then - nam = user_input - local blocks = { - [4] = lines[i + 2]:match("Block 4: (.+)"), - [5] = lines[i + 3]:match("Block 5: (.+)"), - [6] = lines[i + 4]:match("Block 6: (.+)"), - [7] = lines[i + 5]:match("Block 7: (.+)") - } - local em4102_id = lines[i + 6]:match("EM4102 ID: (.+)") - print(dash) - print(' I found the data under the name: '..ac.yellow ..nam.. ac.reset) - for j = 4, 7 do - print(string.format(" Block %d: %s%s%s", j, ac.yellow, blocks[j] or "N/A", ac.reset)) - end - print(" EM4102 ID: " .. ac.green .. (em4102_id or "N/A") .. ac.reset) - print(dash) - local decimal_id, padded_hex_id = em4102_id, em4102_id - handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3, nam) - found = true - break - end - end - if not found then - retries = retries - 1 - else - break - end - end - if retries == 0 then - print(ac.yellow .. " Name not found after 3 attempts." .. ac.reset) - end - end - print(dash) - print(' Exiting script Lua...') - return - end -end - +local getopt = require('getopt') +local utils = require('utils') +local ac = require('ansicolors') +local os = require('os') +local dash = string.rep('--', 32) +local dir = os.getenv('HOME') .. '/.proxmark3/logs/' +local logfile = (io.popen('dir /a-d /o-d /tw /b/s "' .. dir .. '" 2>nul:'):read("*a"):match("%C+")) +local log_file_path = dir .. "Paxton_log.txt" +local nam = "" +local pm3 = require('pm3') +p = pm3.pm3() +local command = core.console +command('clear') + +author = ' Author: jareckib - 30.01.2025' +tutorial = ' Based on Equipter tutorial - Downgrade Paxton to EM4102' +version = ' version v1.20' +desc = [[ + The script automates the copying of Paxton fobs read - write. + It also allows manual input of data for blocks 4-7. + The third option is reading data stored in the log file and create new fob. + Additionally, the script calculates the ID for downgrading Paxton to EM4102. + + ]] +usage = [[ + script run paxton_clone +]] +arguments = [[ + script run paxton_clone -h : this help +]] + +local debug = true + +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end + +local function help() + print() + print(author) + print(tutorial) + print(version) + print(desc) + print(ac.cyan..' Usage'..ac.reset) + print(usage) + print(ac.cyan..' Arguments'..ac.reset) + print(arguments) +end + +local function reset_log_file() + local file = io.open(logfile, "w+") + file:write("") + file:close() +end + +local function read_log_file(logfile) + local file = io.open(logfile, "r") + if not file then + error(" Could not open the file") + end + local content = file:read("*all") + file:close() + return content +end + +local function parse_blocks(result) + local blocks = {} + for line in result:gmatch("[^\r\n]+") do + local block_num, block_data = line:match("%[%=%]%s+%d/0x0([4-7])%s+%|%s+([0-9A-F ]+)") + if block_num and block_data then + block_num = tonumber(block_num) + block_data = block_data:gsub("%s+", "") + blocks[block_num] = block_data + end + end + return blocks +end + +local function hex_to_bin(hex_string) + local bin_string = "" + local hex_to_bin_map = { + ['0'] = "0000", ['1'] = "0001", ['2'] = "0010", ['3'] = "0011", + ['4'] = "0100", ['5'] = "0101", ['6'] = "0110", ['7'] = "0111", + ['8'] = "1000", ['9'] = "1001", ['A'] = "1010", ['B'] = "1011", + ['C'] = "1100", ['D'] = "1101", ['E'] = "1110", ['F'] = "1111" + } + for i = 1, #hex_string do + bin_string = bin_string .. hex_to_bin_map[hex_string:sub(i, i)] + end + return bin_string +end + +local function remove_last_two_bits(binary_str) + return binary_str:sub(1, #binary_str - 2) +end + +local function split_into_5bit_chunks(binary_str) + local chunks = {} + for i = 1, #binary_str, 5 do + table.insert(chunks, binary_str:sub(i, i + 4)) + end + return chunks +end + +local function remove_parity_bit(chunks) + local no_parity_chunks = {} + for _, chunk in ipairs(chunks) do + if #chunk == 5 then + table.insert(no_parity_chunks, chunk:sub(2)) + end + end + return no_parity_chunks +end + +local function convert_to_hex(chunks) + local hex_values = {} + for _, chunk in ipairs(chunks) do + if #chunk > 0 then + table.insert(hex_values, string.format("%X", tonumber(chunk, 2))) + end + end + return hex_values +end + +local function convert_to_decimal(chunks) + local decimal_values = {} + for _, chunk in ipairs(chunks) do + table.insert(decimal_values, tonumber(chunk, 2)) + end + return decimal_values +end + +local function find_until_before_f(hex_values) + local result = {} + for _, value in ipairs(hex_values) do + if value == 'F' then + break + end + table.insert(result, value) + end + return result +end + +local function process_block(block) + local binary_str = hex_to_bin(block) + binary_str = remove_last_two_bits(binary_str) + local chunks = split_into_5bit_chunks(binary_str) + local no_parity_chunks = remove_parity_bit(chunks) + return no_parity_chunks +end + +local function calculate_id_net(blocks) + local all_hex_values = {} + for _, block in ipairs(blocks) do + local hex_values = convert_to_hex(process_block(block)) + for _, hex in ipairs(hex_values) do + table.insert(all_hex_values, hex) + end + end + local selected_hex_values = find_until_before_f(all_hex_values) + if #selected_hex_values == 0 then + error(ac.red..' Error: '..ac.reset..'No valid data found in blocks 4 and 5') + end + local combined_hex = table.concat(selected_hex_values) + if not combined_hex:match("^%x+$") then + error(ac.red..' Error: '..ac.reset..'Invalid data in blocks 4 and 5') + end + local decimal_id = tonumber(combined_hex) + local stripped_hex_id = string.format("%X", decimal_id) + local padded_hex_id = string.format("%010X", decimal_id) + return decimal_id, padded_hex_id +end + +local function calculate_id_switch(blocks) + local all_decimal_values = {} + for _, block in ipairs(blocks) do + local decimal_values = convert_to_decimal(process_block(block)) + for _, dec in ipairs(decimal_values) do + table.insert(all_decimal_values, dec) + end + end + if #all_decimal_values < 15 then + error(ac.red..' Error:'..ac.reset..' Not enough data after processing blocks 4, 5, 6, and 7') + end + local id_positions = {9, 11, 13, 15, 2, 4, 6, 8} + local id_numbers = {} + for _, pos in ipairs(id_positions) do + table.insert(id_numbers, all_decimal_values[pos]) + end + local decimal_id = tonumber(table.concat(id_numbers)) + local padded_hex_id = string.format("%010X", decimal_id) + return decimal_id, padded_hex_id +end + +local function name_exists_in_log(name) + local file = io.open(log_file_path, "r") + if not file then + return false + end + local pattern = "^Name:%s*" .. name .. "%s*$" + for line in file:lines() do + if line:match(pattern) then + file:close() + return true + end + end + file:close() + return false +end + +local function log_result(blocks, em410_id, name) + local log_file = io.open(log_file_path, "a") + if log_file then + log_file:write("Name: " .. name .. "\n") + log_file:write("Date: ", os.date("%Y-%m-%d %H:%M:%S"), "\n") + for i = 4, 7 do + log_file:write(string.format("Block %d: %s\n", i, blocks[i] or "nil")) + end + log_file:write(string.format('EM4102 ID: %s\n', em410_id or "nil")) + log_file:write('--------------------------\n') + log_file:close() + print(' Log saved as: pm3/.proxmark3/logs/' ..ac.yellow..' Paxton_log.txt'..ac.reset) + else + print(" Failed to open log file for writing.") + end +end + +local function verify_written_data(original_blocks) + p:console('lf hitag read --ht2 -k BDF5E846') + local result = read_log_file(logfile) + local verified_blocks = parse_blocks(result) + local success = true + for i = 4, 7 do + if original_blocks[i] ~= verified_blocks[i] then + print(' Verification failed.. Block '..ac.green.. i ..ac.reset.. ' inconsistent.') + success = false + end + end + + if success then + print(ac.green..' Verification successful. Data was written correctly.' .. ac.reset) + else + print(ac.yellow.. ' Adjust the position of the Paxton fob on the coil.' .. ac.reset) + end +end + +local function handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) + while true do + io.write(" Create Paxton choose " .. ac.cyan .. "1" .. ac.reset .. " or EM4102 choose " .. ac.cyan .. "2 " .. ac.reset) + local choice = io.read() + if choice == "1" then + io.write(" Place the" .. ac.cyan .. " Paxton " .. ac.reset .. "Fob on the coil to write.." .. ac.green .. " ENTER " .. ac.reset .. "to continue..") + io.read() + print(dash) + p:console("lf hitag wrbl --ht2 -p 4 -d " .. blocks[4] .. " -k BDF5E846") + p:console("lf hitag wrbl --ht2 -p 5 -d " .. blocks[5] .. " -k BDF5E846") + p:console("lf hitag wrbl --ht2 -p 6 -d " .. blocks[6] .. " -k BDF5E846") + p:console("lf hitag wrbl --ht2 -p 7 -d " .. blocks[7] .. " -k BDF5E846") + reset_log_file() + --timer(5) + verify_written_data(blocks) + elseif choice == "2" then + io.write(" Place the" .. ac.cyan .. " T5577 " .. ac.reset .. "tag on the coil and press" .. ac.green .. " ENTER " .. ac.reset .. "to continue..") + io.read() + p:console("lf em 410x clone --id " .. padded_hex_id) + print(' Cloned EM4102 to T5577 with ID ' ..ac.green.. padded_hex_id ..ac.reset) + else + print(ac.yellow .. " Invalid choice." .. ac.reset .. " Please enter " .. ac.cyan .. "1" .. ac.reset .. " or " .. ac.cyan .. "2" .. ac.reset) + goto ask_again + end + while true do + print(dash) + io.write(" Make next RFID Fob"..ac.cyan.." (y/n) "..ac.reset) + local another = io.read() + if another:lower() == "n" then + if was_option_3 then + print(" No writing to Paxton_log.txt - Name: " ..ac.green.. nam .. ac.reset.. " exist") + return + end + print() + print(ac.green .. " Saving Paxton_log file..." .. ac.reset) + while true do + io.write(" Enter a name for database (cannot be empty/duplicate): "..ac.yellow) + name = io.read() + io.write(ac.reset..'') + if name == nil or name:match("^%s*$") then + print(ac.red .. ' ERROR:'..ac.reset..' Name cannot be empty.') + else + if name_exists_in_log(name) then + print(ac.yellow .. ' Name exists!!! '..ac.reset.. 'Please choose a different name.') + else + break + end + end + end + log_result(blocks, padded_hex_id, name) + print(ac.green .. " Log saved successfully!" .. ac.reset) + reset_log_file() + return + elseif another:lower() == "y" then + goto ask_again + else + print(ac.yellow.." Invalid response."..ac.reset.." Please enter"..ac.cyan.." y"..ac.reset.." or"..ac.cyan.." n"..ac.reset) + end + end + ::ask_again:: + end +end + +local function is_valid_hex(input) + return #input == 8 and input:match("^[0-9A-Fa-f]+$") +end + +local function main(args) + while true do + for o, a in getopt.getopt(args, 'h') do + if o == 'h' then return help() end + end + command('clear') + print(dash) + print(ac.green .. ' Select option: ' .. ac.reset) + print(ac.cyan .. ' 1' .. ac.reset .. ' - Read Paxton blocks 4-7 to make a copy') + print(ac.cyan .. ' 2' .. ac.reset .. ' - Manually input data for Paxton blocks 4-7') + print(ac.cyan .. " 3" .. ac.reset .. " - Search in Paxton_log by name and use the data") + print(dash) + while true do + io.write(' Your choice '..ac.cyan..'(1/2/3): ' .. ac.reset) + input_option = io.read() + if input_option == "1" or input_option == "2" or input_option == "3" then + break + else + print(ac.yellow .. ' Invalid choice.' .. ac.reset .. ' Please enter ' .. ac.cyan .. '1' .. ac.reset .. ' or ' .. ac.cyan .. '2' .. ac.reset..' or'..ac.cyan..' 3'..ac.reset) + end + end + local was_option_3 = false + if input_option == "1" then + local show_place_message = true + while true do + if show_place_message then + io.write(' Place the' .. ac.cyan .. ' Paxton' .. ac.reset .. ' Fob on the coil to read..' .. ac.green .. 'ENTER' .. ac.reset .. ' to continue..') + end + io.read() + print(dash) + p:console('lf hitag read --ht2 -k BDF5E846') + if not logfile then + error(" No files in this directory") + end + local result = read_log_file(logfile) + local blocks = parse_blocks(result) + local empty_block = false + for i = 4, 7 do + if not blocks[i] then + empty_block = true + break + end + end + if empty_block then + io.write(ac.yellow .. ' Adjust the Fob position on the coil.' .. ac.reset .. ' Press' .. ac.green .. ' ENTER' .. ac.reset .. ' to continue..') + show_place_message = false + else + print(' Readed blocks:') + print() + for i = 4, 7 do + if blocks[i] then + print(string.format(" Block %d: %s%s%s", i, ac.yellow, blocks[i], ac.reset)) + end + end + local decimal_id, padded_hex_id + if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then + print(dash) + print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]}) + else + print(dash) + print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]}) + end + print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset)) + print(dash) + handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) + break + end + end + elseif input_option == "2" then + local blocks = {} + for i = 4, 7 do + while true do + io.write(ac.reset..' Enter data for block ' .. i .. ': ' .. ac.yellow) + local input = io.read() + input = input:upper() + if is_valid_hex(input) then + blocks[i] = input + break + else + print(ac.yellow .. ' Invalid input.' .. ac.reset .. ' Each block must be 4 bytes (8 hex characters).') + end + end + end + local decimal_id, padded_hex_id + if blocks[5] and (blocks[5]:sub(4, 4) == 'F' or blocks[5]:sub(4, 4) == 'f') then + print(ac.reset.. dash) + print(' Identified Paxton ' .. ac.cyan .. 'Net2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_net({blocks[4], blocks[5]}) + else + print(ac.reset.. dash) + print(' Identified Paxton ' .. ac.cyan .. 'Switch2' .. ac.reset) + decimal_id, padded_hex_id = calculate_id_switch({blocks[4], blocks[5], blocks[6], blocks[7]}) + end + print(dash) + print(string.format(" ID for EM4102 is: %s", ac.green .. padded_hex_id .. ac.reset)) + print(dash) + if not padded_hex_id then + print(ac.red..' ERROR: '..ac.reset.. 'Invalid block data provided') + return + end + handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3) + break + elseif input_option == "3" then + was_option_3 = true + local retries = 3 + while retries > 0 do + io.write(' Enter the name to search ('..retries..' attempts) : '..ac.yellow) + local user_input = io.read() + io.write(ac.reset..'') + if user_input == nil or user_input:match("^%s*$") then + print(ac.yellow..' Error: '..ac.reset.. 'Empty name !!!') + end + local name_clean = "^Name:%s*" .. user_input:gsub("%s", "%%s") .. "%s*$" + local file = io.open(log_file_path, "r") + if not file then + print(ac.red .. ' Error:'..ac.reset.. 'Could not open log file.') + return + end + local lines = {} + for line in file:lines() do + table.insert(lines, line) + end + file:close() + local found = false + for i = 1, #lines do + if lines[i]:match(name_clean) then + nam = user_input + local blocks = { + [4] = lines[i + 2]:match("Block 4: (.+)"), + [5] = lines[i + 3]:match("Block 5: (.+)"), + [6] = lines[i + 4]:match("Block 6: (.+)"), + [7] = lines[i + 5]:match("Block 7: (.+)") + } + local em4102_id = lines[i + 6]:match("EM4102 ID: (.+)") + print(dash) + print(' I found the data under the name: '..ac.yellow ..nam.. ac.reset) + for j = 4, 7 do + print(string.format(" Block %d: %s%s%s", j, ac.yellow, blocks[j] or "N/A", ac.reset)) + end + print(" EM4102 ID: " .. ac.green .. (em4102_id or "N/A") .. ac.reset) + print(dash) + local decimal_id, padded_hex_id = em4102_id, em4102_id + handle_cloning(decimal_id, padded_hex_id, blocks, was_option_3, nam) + found = true + break + end + end + if not found then + retries = retries - 1 + else + break + end + end + if retries == 0 then + print(ac.yellow .. " Name not found after 3 attempts." .. ac.reset) + end + end + print(dash) + print(' Exiting script Lua...') + return + end +end + main(args) diff --git a/client/pyscripts/des_talk.py b/client/pyscripts/des_talk.py index 5624f592d..97d7d5e72 100644 --- a/client/pyscripts/des_talk.py +++ b/client/pyscripts/des_talk.py @@ -28,6 +28,7 @@ Full license text: import subprocess import time +import sys import os import re @@ -100,24 +101,33 @@ def send_proxmark_command(command): def authenticate_and_menu(): + com_mode = input("Enter communication mode (PLAIN, MAC, ENCRYPT) (Default: PLAIN): ").strip() or "plain" key_type = input("Enter key type (DES, 2TDEA, 3TDEA, AES): ").strip() - key = input("Enter 8, 16, 24 or 32-byte hex key (no spaces): ").strip() + key = input("Enter 8, 16, or 24-byte hex key (no spaces): ").strip() # Authenticate - auth_command = f"hf mfdes auth -t {key_type} -k {key}" + auth_command = f"hf mfdes auth -t {key_type} -k {key} -m {com_mode}" auth_response = send_proxmark_command(auth_command) print(auth_response) + # print("DEBUG: Raw Proxmark response:\n", repr(auth_response)) # Check for Proxmark failure messages if "error" in auth_response.lower() or "must have" in auth_response.lower(): - print("❌ Authentication failed. Check your connection, key, and key type.") + print("❌ Authentication failed. Check your connection, mode, key type, and key.") return while True: - # Get AIDs - aids_command = f"hf mfdes getaids -n 0 -t {key_type} -k {key}" + aids_command = f"hf mfdes getaids -n 0 -t {key_type} -k {key} -m {com_mode}" aids_response = send_proxmark_command(aids_command) + + # Check for communication mode errors + com_mode_error_match = re.search(r"Wrong communication mode", aids_response) + crc_error_match = re.search(r"CRC32 error", aids_response) + if com_mode_error_match or crc_error_match: + print("❌ Incorrect communication mode.\n") + return + print(aids_response) # Regex to match valid 6-character hex AIDs @@ -144,7 +154,8 @@ def authenticate_and_menu(): print("3. Delete an AID") print("4. Format PICC") print("5. Show free memory") - print("6. Exit") + print("6. Change keys") + print("7. Exit") choice = input("Enter your choice: ").strip() @@ -157,32 +168,39 @@ def authenticate_and_menu(): selected_aid = aids[selected_index] print(f"\nSelecting AID: {selected_aid}") - select_command = f"hf mfdes selectapp --aid {selected_aid} -t {key_type} -k {key}" + select_command = f"hf mfdes selectapp --aid {selected_aid} -t {key_type} -k {key} -m {com_mode}" select_response = send_proxmark_command(select_command) print(select_response) + # Retrieve AID key 0 + aid_key_type = input(f"Enter AID encryption algorithm (DES, 2TDEA, 3TDEA, AES) (Default: {key_type.upper()}): ").strip() or key_type + aid_key = input(f"Enter AID key (Default: {key}): ").strip() or key + # Show file menu - aid_file_menu(selected_aid, key_type, key) + aid_file_menu(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) elif choice == "2": - create_aid(key_type, key) + create_aid(key_type, key, com_mode) elif choice == "3": - delete_aid(key_type, key) + delete_aid(key_type, key, com_mode) elif choice == "4": - format_picc(key_type, key) + format_picc(key_type, key, com_mode) elif choice == "5": - free_memory(key_type, key) + free_memory(key_type, key, com_mode) elif choice == "6": + change_key(key_type, key, com_mode) + + elif choice == "7": print("Exiting...") break else: print("Invalid choice, please try again.") -def aid_file_menu(selected_aid, key_type, key): +def aid_file_menu(selected_aid, key_type, key, com_mode, aid_key_type, aid_key): while True: print(f"\n[ AID {selected_aid} is open ]") @@ -191,50 +209,52 @@ def aid_file_menu(selected_aid, key_type, key): print("2. Read a File") print("3. Create a File") print("4. Write to a File") - print("5. Delete a File") - print("6. Exit") + print("5. Edit File Restrictions") + print("6. Delete a File") + print("7. Back") choice = input("Enter your choice: ").strip() if choice == "1": - list_files(selected_aid, key_type, key) + list_files(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) elif choice == "2": - read_file(selected_aid, key_type, key) + read_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) elif choice == "3": - create_file(selected_aid, key_type, key) + create_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) elif choice == "4": - write_to_file(selected_aid, key_type, key) + write_to_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) elif choice == "5": - delete_file(selected_aid, key_type, key) + edit_file_restriction(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) elif choice == "6": + delete_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key) + elif choice == "7": print("Returning to AID selection...") break else: print("Invalid choice, please try again.") -def create_aid(key_type, key): +def create_aid(key_type, key, com_mode): aid = input("Enter new AID (6 hex characters, e.g., 112233): ").strip() iso_fid = input("Enter ISO File ID (4 hex characters, e.g., 1234): ").strip() - dstalgo = input("Enter encryption algorithm (DES, 2TDEA, 3TDEA, AES): ").strip().upper() - - create_command = f"hf mfdes createapp -n 0 --aid {aid} --fid {iso_fid} --dstalgo {dstalgo} -t {key_type} -k {key} -a" + dstalgo = input(f"Enter encryption algorithm (DES, 2TDEA, 3TDEA, AES) (Default: {key_type.upper()}): ").strip() or key_type + create_command = f"hf mfdes createapp -n 0 --aid {aid} --fid {iso_fid} --dstalgo {dstalgo} -t {key_type} -k {key} -m {com_mode} -a" response = send_proxmark_command(create_command) print(response) -def delete_aid(key_type, key): +def delete_aid(key_type, key, com_mode): aid = input("Enter AID to delete (6 hex characters): ").strip() - delete_command = f"hf mfdes deleteapp --aid {aid} -n 0 -t {key_type} -k {key}" + delete_command = f"hf mfdes deleteapp --aid {aid} -n 0 -t {key_type} -k {key} -m {com_mode}" response = send_proxmark_command(delete_command) print(response) -def format_picc(key_type, key): +def format_picc(key_type, key, com_mode): confirm = input("Are you sure you want to format the PICC? This will erase all data. (y/n): ").strip().lower() if confirm == "y": - format_command = f"hf mfdes formatpicc -t {key_type} -k {key} -v" + format_command = f"hf mfdes formatpicc -t {key_type} -k {key} -m {com_mode} -v" response = send_proxmark_command(format_command) print(response) elif confirm == "n": @@ -242,9 +262,9 @@ def format_picc(key_type, key): else: print("Invalid input. Please enter 'y' or 'n'.") -def free_memory(key_type, key): +def free_memory(key_type, key, com_mode): - memory_command = f"hf mfdes freemem -t {key_type} -k {key}" + memory_command = f"hf mfdes freemem -t {key_type} -k {key} -m {com_mode}" response = send_proxmark_command(memory_command) for line in response.splitlines(): @@ -254,10 +274,54 @@ def free_memory(key_type, key): print("❌ Unable to retrieve free memory information.") -def list_files(aid, key_type, key): +def change_key(key_type, key, com_mode): + print("\nChange Key - Choose Target:") + print("1. PICC (Card Master Key)") + print("2. Application Key") + target = input("Change key for (1/2)? (Default: 1): ").strip() or "1" + aid = "" + + if target == "2": + aid = input("Enter 6-digit AID (e.g., 010203): ").strip() + + print("\n!! Verify and securely store the new key !!") + print("Key length guide:") + print(" DES : 8 bytes (16 hex chars)") + print(" 2TDEA : 16 bytes (32 hex chars)") + print(" 3TDEA : 24 bytes (48 hex chars)") + print(" AES : 16 bytes (32 hex chars)") + + newalgo = input(f"Enter new key encryption algorithm (DES, 2TDEA, 3TDEA, AES) " + f"(Default: {key_type.upper()}): ").strip() or key_type + newkey = input(f"Enter new 8, 16, or 24-byte hex key (no spaces) (Default: {key}): ").strip() or key + + confirm = input("Are you sure you want to change the key? (Key 0) (y or n): ").strip().lower() + + if confirm == "y": + changekey_command = f"hf mfdes changekey -n 0 -t {key_type} -k {key} -m {com_mode} " \ + f"--newalgo {newalgo} --newkey {newkey} --newver 00 -v" + if aid: + app_key_type = input(f"Enter original application encryption algorithm (DES, 2TDEA, 3TDEA, AES) " + f"(Default: DES): ").strip() or "DES" + app_key = input(f"Enter original application key " + f"(Default: 0000000000000000): ").strip() or "0000000000000000" + changekey_command = f"hf mfdes changekey -n 0 -t {app_key_type} -k {app_key} -m {com_mode} " \ + f"--newalgo {newalgo} --newkey {newkey} --newver 00 --aid {aid} -v" + + response = send_proxmark_command(changekey_command) + print(response) + print("\nReauthenticate with the master key.") + sys.exit() + + elif confirm == "n": + print("Cancelled.") + else: + print("Invalid input. Please enter 'y' or 'n'.") + +def list_files(aid, key_type, key, com_mode, aid_key_type, aid_key): print("\nFetching file list...") - command = f"hf mfdes getfileids --aid {aid} -t {key_type} -k {key}" + command = f"hf mfdes getfileids --aid {aid} -t {aid_key_type} -k {aid_key} -m {com_mode}" response = send_proxmark_command(command) # Extract file IDs by looking for "File ID:" regex @@ -276,7 +340,7 @@ def list_files(aid, key_type, key): print("No files found in this AID.") return [] -def read_file(aid, key_type, key): +def read_file(aid, key_type, key, com_mode, aid_key_type, aid_key): file_id = input("Enter file ID to read: ").strip() @@ -288,7 +352,8 @@ def read_file(aid, key_type, key): length_input = input("Enter length to read (e.g., 16 for 16 bytes, 64 for 64 bytes, default full read): ").strip() or "0" length_hex = format(int(length_input), '06X') # Convert to 3-byte hex - read_command = f"hf mfdes read --aid {aid} --fid {file_id} -t {key_type} -k {key} --offset {offset_hex} --length {length_hex}" + read_command = f"hf mfdes read --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} " \ + f"--offset {offset_hex} --length {length_hex} -m {com_mode}" response = send_proxmark_command(read_command) # Extract and display file content @@ -299,7 +364,7 @@ def read_file(aid, key_type, key): return response -def create_file(aid, key_type, key): +def create_file(aid, key_type, key, com_mode, aid_key_type, aid_key): # Prompt for file ID in hex format file_id = input("Enter file ID (2 hex characters, e.g., 01, 02): ").strip() @@ -332,16 +397,17 @@ def create_file(aid, key_type, key): print(f"Invalid file size: {e}") return - create_command = f"hf mfdes createfile --aid {aid} --fid {file_id} --isofid {iso_file_id} --size {file_size_hex} -t {key_type} -k {key}" + create_command = f"hf mfdes createfile --aid {aid} --fid {file_id} --isofid {iso_file_id} " \ + f"--size {file_size_hex} -t {aid_key_type} -k {aid_key} -m {com_mode}" response = send_proxmark_command(create_command) print(response) -def write_to_file(aid, key_type, key): +def write_to_file(aid, key_type, key, com_mode, aid_key_type, aid_key): file_id = input("Enter file ID to write to: ").strip() # Get file size - file_size_command = f"hf mfdes getfilesettings --aid {aid} --fid {file_id} -t {key_type} -k {key}" + file_size_command = f"hf mfdes getfilesettings --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m {com_mode}" response = send_proxmark_command(file_size_command) # Extract the file size from the response @@ -376,15 +442,49 @@ def write_to_file(aid, key_type, key): else: print("❌ Invalid choice. Please choose 1 for text or 2 for hex.") - write_command = f"hf mfdes write --aid {aid} --fid {file_id} -t {key_type} -k {key} -d {write_data_hex}" + write_command = f"hf mfdes write --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -d {write_data_hex} -m {com_mode}" response = send_proxmark_command(write_command) print(response) -def delete_file(aid, key_type, key): +def edit_file_restriction(aid, key_type, key, com_mode, aid_key_type, aid_key): + while True: + print("\nNOTE: This only works if you have changed the default keys.") + print("The Proxmark3 and other tools will automatically attempt to read files using DESFire default keys.") + print("\nWould you like to apply or remove a key from the file?") + print("1. Apply key 0 (Requires authentication for access)") + print("2. Remove key (Make file freely accessible)") + print("3. Back") + + choice = input("Enter your choice (1, 2, or 3): ").strip() + + if choice == "3": + print("Returning to the previous menu.") + break + + file_id = input("Enter file ID to update: ").strip() + + if choice == "1": + edit_file_command = f"hf mfdes chfilesettings --rawrights 0000 --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m {com_mode}" + print("Applying key 0 for read, write, and change access. This ensures authentication is required to access the file.") + + elif choice == "2": + # Must use encrypt communications mode to remove restrictions + edit_file_command = f"hf mfdes chfilesettings --rawrights EEEE --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m encrypt" + print("Removing key restrictions. File will be freely accessible.") + + else: + print("❌ Invalid choice. Please enter 1, 2, or 3.") + continue + + response = send_proxmark_command(edit_file_command) + print(response) + break + +def delete_file(aid, key_type, key, com_mode, aid_key_type, aid_key): file_id = input("Enter file ID to delete: ").strip() - delete_command = f"hf mfdes deletefile --aid {aid} --fid {file_id} -t {key_type} -k {key}" + delete_command = f"hf mfdes deletefile --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m {com_mode}" response = send_proxmark_command(delete_command) print(response) diff --git a/client/pyscripts/fm11rf08s_full.py b/client/pyscripts/fm11rf08s_full.py index 714d1acaf..8d1765b84 100644 --- a/client/pyscripts/fm11rf08s_full.py +++ b/client/pyscripts/fm11rf08s_full.py @@ -90,13 +90,14 @@ def lprint(s='', end='\n', flush=False, prompt="[" + color("=", fg="yellow") + - logfile (R) """ s = f"{prompt}" + f"\n{prompt}".join(s.split('\n')) - print(s, end=end, flush=flush) + safe_s = s.encode('utf-8', errors='ignore').decode('utf-8') + print(safe_s, end=end, flush=flush) if log is True: global logbuffer if logfile is not None: - with open(logfile, 'a') as f: - f.write(s + end) + with open(logfile, 'a', encoding='utf-8') as f: + f.write(safe_s + end) else: # buffering logbuffer += s + end diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index cbb7246d5..9c7b0797e 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -226,6 +226,7 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, dict_dnwd = None def_nt = ["" for _ in range(NUM_SECTORS)] if supply_chain: + default_nonces = '' try: default_nonces = f'{save_path}hf-mf-{uid:04X}-default_nonces.json' with open(default_nonces, 'r') as file: @@ -584,8 +585,6 @@ def recovery(init_check=False, final_check=False, keep=False, no_oob=False, if "Found keys have been dumped to" in line: keyfile = line[line.index("`"):].strip("`") else: - show() - show(color("found keys:", fg="green"), prompt=plus) show(prompt=plus) show("-----+-----+--------------+---+--------------+----", prompt=plus) show(" Sec | Blk | key A |res| key B |res", prompt=plus) diff --git a/client/pyscripts/intertic.py b/client/pyscripts/intertic.py index 2793aee43..8f2690dd7 100644 --- a/client/pyscripts/intertic.py +++ b/client/pyscripts/intertic.py @@ -211,6 +211,37 @@ def Describe_Usage_2_1(Usage, ContractMediumEndDate, Certificate): print(' left... :', Usage.nom_bits_left()) print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) +def Describe_Usage_2_2(Usage, ContractMediumEndDate, Certificate): + EventDateStamp = Usage.nom(10) + EventTimeStamp = Usage.nom(11) + unk0 = Usage.nom_bits(8) + EventCode_Nature = Usage.nom(5) + EventCode_Type = Usage.nom(5) + unk1 = Usage.nom_bits(11) + EventGeoRouteId = Usage.nom(14) + EventGeoRoute_Direction = Usage.nom(2) + EventGeoVehicleId = Usage.nom(16) + unk2 = Usage.nom_bits(4) + EventValidityTimeFirstStamp = Usage.nom(11) + unk3 = Usage.nom_bits(3) + EventCountPassengers_mb = Usage.nom(4) + + print(' DateStamp : {} ({})'.format(EventDateStamp, (datetime(1997, 1, 1) + timedelta(days = ContractMediumEndDate - EventDateStamp)).strftime('%Y-%m-%d'))) + print(' TimeStamp : {} ({:02d}:{:02d})'. format(EventTimeStamp, EventTimeStamp // 60, EventTimeStamp % 60)) + print(' unk0... :', unk0) + print(' Code/Nature : 0x{:x} ({})'.format(EventCode_Nature, TYPE_EventCode_Nature.get(EventCode_Nature, '?'))) + print(' Code/Type : 0x{:x} ({})'.format(EventCode_Type, TYPE_EventCode_Type.get(EventCode_Type, '?'))) + print(' unk1... :', unk1) + print(' GeoRouteId : {}'. format(EventGeoRouteId)) + print(' Direction : {} ({})'. format(EventGeoRoute_Direction, TYPE_EventGeoRoute_Direction.get(EventGeoRoute_Direction, '?'))) + print(' GeoVehicleId : {}'. format(EventGeoVehicleId)) + print(' unk2... :', unk2) + print(' ValidityTimeFirstStamp: {} ({:02d}:{:02d})'. format(EventValidityTimeFirstStamp, EventValidityTimeFirstStamp // 60, EventValidityTimeFirstStamp % 60)) + print(' unk3... :', unk3) + print(' Passengers(?) : {}'. format(EventCountPassengers_mb)) + print(' left... :', Usage.nom_bits_left()) + print(' [CER] Usage : {:04x}'.format(Certificate.nom(16))) + def Describe_Usage_3(Usage, ContractMediumEndDate, Certificate): EventDateStamp = Usage.nom(10) EventTimeStamp = Usage.nom(11) @@ -258,6 +289,7 @@ ISO_Countries = { FRA_OrganizationalAuthority_Contract_Provider = { 0x000: { + 1: InterticHelper('Valenciennes', 'Transvilles / Keolis', Describe_Usage_1_1), 5: InterticHelper('Lille', 'Ilévia / Keolis', Describe_Usage_1_1), 7: InterticHelper('Lens-Béthune', 'Tadao / Transdev', Describe_Usage_1_1), }, @@ -273,6 +305,9 @@ FRA_OrganizationalAuthority_Contract_Provider = { 0x021: { 1: InterticHelper('Bordeaux', 'TBM / Keolis', Describe_Usage_1_1), }, + 0x040: { + 28: InterticHelper('Colmar', 'Trace / Keolis', Describe_Usage_1_1), + }, 0x057: { 1: InterticHelper('Lyon', 'TCL / Keolis', Describe_Usage_1), # Strange usage ?, kept on generic 1 }, @@ -289,7 +324,7 @@ FRA_OrganizationalAuthority_Contract_Provider = { 0x502: { 83: InterticHelper('Annecy', 'Sibra', Describe_Usage_2), 84: InterticHelper('Bourg-en-Bresse', 'Rubis / Keolis'), - 10: InterticHelper('Clermont-Ferrand', 'T2C'), + 10: InterticHelper('Clermont-Ferrand', 'T2C', Describe_Usage_2_2), }, 0x907: { 1: InterticHelper('Dijon', 'Divia / Keolis'), @@ -315,6 +350,9 @@ FRA_OrganizationalAuthority_Contract_Provider = { 4: InterticHelper('Angers', 'Irigo / RATP', Describe_Usage_1_2), 7: InterticHelper('Saint-Nazaire', 'Stran'), }, + 0x920: { + 9: InterticHelper('Aix-en-Provence', 'Aixenbus / Keolis', Describe_Usage_2_1), + }, } MAR_OrganizationalAuthority_Contract_Provider = { diff --git a/client/pyscripts/pm3_resources.py b/client/pyscripts/pm3_resources.py index c50ede801..a31069dbd 100644 --- a/client/pyscripts/pm3_resources.py +++ b/client/pyscripts/pm3_resources.py @@ -36,7 +36,7 @@ DIR_PATH = os.path.dirname(os.path.abspath(__file__)) if TOOLS_PATH is None: if os.path.basename(os.path.dirname(DIR_PATH)) == 'client': # dev setup - DEV_TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "..", "tools", "mfc", "card_only")) + DEV_TOOLS_PATH = os.path.normpath(os.path.join(DIR_PATH, "..", "..", "tools")) if os.path.isdir(DEV_TOOLS_PATH): TOOLS_PATH = DEV_TOOLS_PATH @@ -62,11 +62,11 @@ def find_tool(tool_name): str: The full path to the tool if found, otherwise None. """ if TOOLS_PATH is not None: - tool = os.path.join(TOOLS_PATH, tool_name) - if os.path.isfile(tool): - return tool - elif os.path.isfile(tool + ".exe"): - return tool + ".exe" + for root, _, files in os.walk(TOOLS_PATH): + if tool_name in files: + return os.path.join(root, tool_name) + elif tool_name + ".exe" in files: + return os.path.join(root, tool_name + ".exe") # if not found, search in the user PATH for path in os.environ["PATH"].split(os.pathsep): env_tool = os.path.join(path, tool_name) diff --git a/client/resources/aid_desfire.json b/client/resources/aid_desfire.json index c88482f4a..cb10384f4 100644 --- a/client/resources/aid_desfire.json +++ b/client/resources/aid_desfire.json @@ -391,12 +391,20 @@ "Description": "Key as a Service // FID 01: Standard Data", "Type": "pacs" }, + { + "AID": "F51780", + "Vendor": "ASSA ABLOY", + "Country": "SE", + "Name": "SMARTair", + "Description": "SMARTair Credential", + "Type": "pacs" + }, { "AID": "F51BC0", "Vendor": "STid Group", "Country": "FR", "Name": "CCT Card / DTA Tag / PCG Fob", - "Description": "STid Easyline / Architect Access Credetials", + "Description": "STid Easyline / Architect Access Credentials", "Type": "pacs" }, { @@ -887,12 +895,20 @@ "Description": "car2go - Member Card // Multi Functional Badge / Private Application #1", "Type": "carsharing" }, + { + "AID": "000005", + "Vendor": "Transports Metropolitans de Barcelona (TMB)", + "Country": "ES", + "Name": "T-mobilitat (BCN)", + "Description": "BCN T-mobilitat", + "Type": "transport" + }, { "AID": "000001", "Vendor": "Invalid / Reserved", "Country": "", "Name": "Invalid / Reserved", - "Description": "Used by ATL Breeze, MAD Tarjeta Transporte Publico, and YVR Compass", + "Description": "Used by ATL Breeze, PHL FREEDOM, and YVR Compass", "Type": "transport" }, { @@ -915,7 +931,7 @@ "AID": "010000", "Vendor": "Consorcio Regional de Transportes Publicos Regulares de Madrid (CRTM)", "Country": "ES", - "Name": "Tarjeta Transporte Publico (MAD) (Alternative Endian)", + "Name": "Tarjeta Transporte Publico (MAD)", "Description": "MAD Public Transport Card", "Type": "transport" }, @@ -1183,6 +1199,22 @@ "Description": "DUB Leap Card // Transport for Ireland // FIDs: 01,1F: Backup Data; 02-0A: Standard Data", "Type": "transport" }, + { + "AID": "402301", + "Vendor": "Ministry of Transport, Communications and Works of the Republic of Cyprus", + "Country": "CY", + "Name": "motion BUS CARD (ECN)", + "Description": "ECN motion BUS CARD (App 1)", + "Type": "transport" + }, + { + "AID": "415431", + "Vendor": "Athens Urban Transport Organization (OASA)", + "Country": "GR", + "Name": "ATH.ENA CARD (ATH)", + "Description": "ATH ATH.ENA CARD", + "Type": "transport" + }, { "AID": "444D01", "Vendor": "Delhi Metro Rail Corporation Limited", @@ -1255,6 +1287,14 @@ "Description": "FIDs: 00-07: Standard Data", "Type": "transport" }, + { + "AID": "502301", + "Vendor": "Ministry of Transport, Communications and Works of the Republic of Cyprus", + "Country": "CY", + "Name": "motion BUS CARD (ECN)", + "Description": "ECN motion BUS CARD (App 2)", + "Type": "transport" + }, { "AID": "534531", "Vendor": "Transport for New South Wales (TfNSW)", @@ -1287,6 +1327,14 @@ "Description": "FIDs 01: Product Retailer; 02: Service Provider; 03: Special Event; 04: Stored Value; 05: General Event Log; 06: SV Reload Log; 0A: Environment; 0C: Card Holder", "Type": "transport" }, + { + "AID": "602301", + "Vendor": "Ministry of Transport, Communications and Works of the Republic of Cyprus", + "Country": "CY", + "Name": "motion BUS CARD (ECN)", + "Description": "ECN motion BUS CARD (App 3)", + "Type": "transport" + }, { "AID": "634000", "Vendor": "Doha Metro and Lusail Tram via Qatar Rail", @@ -1303,6 +1351,14 @@ "Description": "Umo Mobility Card", "Type": "transport" }, + { + "AID": "7A007A", + "Vendor": "Regional Transportation Commission of Southern Nevada (RTC) via Masabi Ltd", + "Country": "US", + "Name": "RTC TAP & GO (LAS)", + "Description": "LAS TAP & GO; Masabi Justride Tap and Ride DESFire Smartcard", + "Type": "transport" + }, { "AID": "784000", "Vendor": "Roads & Transport Authority (Government of Dubai)", @@ -1527,6 +1583,14 @@ "Description": "One Regional Card for All // FIDs 00: Standard Data; 01: Backup Data", "Type": "transport" }, + { + "AID": "F21400", + "Vendor": "Spokane Transit Authority (STA) via INIT", + "Country": "US", + "Name": "Connect Card (GEG)", + "Description": "GEG Connect Card", + "Type": "transport" + }, { "AID": "F40110", "Vendor": "ITSO Ltd", diff --git a/client/resources/iceman.txt b/client/resources/iceman.txt new file mode 100644 index 000000000..ad58ac814 --- /dev/null +++ b/client/resources/iceman.txt @@ -0,0 +1,8 @@ +$$$$$$\ $$$$$$\ $$$$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\  +\_$$ _|$$ __$$\ $$ _____|$$$\ $$$ |$$ __$$\ $$$\ $$ | + $$ | $$ / \__|$$ | $$$$\ $$$$ |$$ / $$ |$$$$\ $$ | + $$ | $$ | $$$$$\ $$\$$\$$ $$ |$$$$$$$$ |$$ $$\$$ | + $$ | $$ | $$ __| $$ \$$$ $$ |$$ __$$ |$$ \$$$$ | + $$ | $$ | $$\ $$ | $$ |\$ /$$ |$$ | $$ |$$ |\$$$ | +$$$$$$\ \$$$$$$ |$$$$$$$$\ $$ | \_/ $$ |$$ | $$ |$$ | \$$ | +\______| \______/ \________|\__| \__|\__| \__|\__| \__| \ No newline at end of file diff --git a/client/src/atrs.h b/client/src/atrs.h index 95a24e997..73ebb857d 100644 --- a/client/src/atrs.h +++ b/client/src/atrs.h @@ -44,6 +44,7 @@ const static atr_t AtrTable[] = { { "3B003B28003441454130323030", "Cryptoguard card used for pay tv Plustelka (DVB-T2 - Slovak) (Pay TV)\nhttps://www.plustelka.sk/" }, { "3B021050", "Visa (Bank)" }, { "3B02141C", "UAE (United Arab Emirates) (eID)" }, + { "3B021425", "Nk (Bank)" }, { "3B021435", "cartao cidadao (eID)" }, { "3B021450", "Schlumberger Multiflex 3k" }, { "3B02145011", "Maste visa card (Bank)" }, @@ -162,6 +163,7 @@ const static atr_t AtrTable[] = { { "3B1618D0000B010300", "Cellular SIM (Telecommunication)" }, { "3B169420020120010D", "Rogers SIM Card (phone / cable provider in Canada)" }, { "3B16942006010C0100", "Vodafone Romania (Telecommunication)" }, + { "3B1694588000010203", "Thales PayShield 9000 Test Local Master Key (hardware key) card (Other)" }, { "3B1694618000010603", "Thales PayShield9000 Local Master Key (hardware key) card (Other)" }, { "3B1694710101002700", "Cingular GSM SIM Card" }, { "3B1694710101010200", "Iridium SIM Card (Telecommunication)" }, @@ -181,6 +183,7 @@ const static atr_t AtrTable[] = { { "3B16959B0007011803", "Thai GSM UICC (Telecommunication)" }, { "3B1695D00045F70100", "Telefonica O2 Czech Republic, a.s. - O2 sim card - 173285 / SIM64ND.GO0\nhttp://www.o2.cz" }, { "3B1695D0016CFD0D00", "Virgin Mobile SIM card (SIM)" }, + { "3B1695D0016EF40C00", "SIM card (Telecommunication)" }, { "3B1695D0017B010E00", "Vivo Brasil - SIM Card (Telecommunication)" }, { "3B1695D0017BDA0D00", "Verizon GSM SIM (Telecommunication)" }, { "3B16962A000E010103", "RW SIM card for mobile forensics (Telecommunication)" }, @@ -217,6 +220,7 @@ const static atr_t AtrTable[] = { { "3B1894530D06772407FF02", "GSM SIM Tele2 Estonia, prepaid (Telecommunication)" }, { "3B1894532007AD0A05FF02", "GSM SIM Beeline Kazakhstan (Telecommunication)\nhttp://beeline.kz/" }, { "3B18962621550401030001", "OBILedit Forensic SIM Cloning card (Telecommunication)\nhttps://www.mobiledit.com/connection-kit" }, + { "3B18968921A00120240716", "Token2 T2F2-NFC-Card PIN+ Release3 (Other)\nhttps://www.token2.com/shop/product/t2f2-nfc-card-pin-release3" }, { "3B19145590010101000508B0", "Schlumberger Multiflex 8k" }, { "3B19145590010201000504B0", "Schlumberger Multiflex 4k" }, { "3B19145901010F01000508B0", "Schlumberger Multiflex 8k" }, @@ -373,6 +377,7 @@ const static atr_t AtrTable[] = { { "3B3B9600914110205709A000339000", "old vodafone romania sim (Telecommunication)\nhttps://vodafone.ro" }, { "3B3B9600924210105A02E000339000", "Roaming Test SIM (Telecommunication)" }, { "3B3B9600A74C90000090AC33339000", "Brazilian TIM GSM SIM" }, + { "3B3BD6970081B1FE451F078031C152", "US government PIV card eID\nOberthur ID One PIV eID\nNASA Personal Identity Verification (PIV) card (eID)\nIDEMIA Cosmo V8.0 with a PIV applet eID" }, { "3B3BF71800008031FE45736674652D", "Serasa Experian (PKI)\nhttps://serasa.certificadodigital.com.br/" }, { "3B3BFF1800FF8131FE55006B020903", "Smart card commercial chamber - Tuscan region - Aruba (eID)" }, { "3B3C110040AF13F31200068783809000", "SIM GSM Orange Fr" }, @@ -382,6 +387,7 @@ const static atr_t AtrTable[] = { { "3B3C9400423111A21202095183809000", "Omnitel IT 16K GSM SIM card" }, { "3B3C9400443111F000002CAE83839000", "Movistar Spain (Telecommunication)" }, { "3B3C94004B3125A21013144783839000", "GSM SFR" }, + { "3B3C94004B3125A7240C027E80809000", "D2 mannesmann mobilphone-card (Other)" }, { "3B3C94004C3125A7201B001583839000", "GSM-SIM (900MHz) card of the carrier vodafone for their cellular\nnetwork (phase 2+ with 3V)" }, { "3B3C9400633112F00000464083839000", "Old russian 'beeline' sim" }, { "3B3D9400010F0036000086601804000107", "Vodafone GSM / Turkey" }, @@ -432,6 +438,7 @@ const static atr_t AtrTable[] = { { "3B4F00536C653434322D34343DA2131091", "Debit card (Bank)" }, { "3B4F00536C65343433322D34323D30000000", "Blank card (Other)" }, { "3B4F00536C65343433322D34323DA2131091", "VISA (Bank)" }, + { "3B4F00536C65343433322D34323DFFFFFFFF", "LTE Testing (Telecommunication)" }, { "3B501100", "JAVA (JavaCard)" }, { "3B57180293020101019000", "Easyflex FastOS 2.0 / Schlumberger" }, { "3B5B96000031C064BAFC10000F9000", "SERGAS - Galician Healthcare Service (Spain) (HealthCare)\nhttps://www.sergas.gal" }, @@ -452,14 +459,19 @@ const static atr_t AtrTable[] = { { "3B5F9600805A3F0608201223C4427698829000", "Opus Card for public transportation in the Greater Montreal Area, and in Quebec City. It uses the Calypso Standard. (Transport)\nhttps://en.wikipedia.org/wiki/OPUS_card" }, { "3B5F9600805A3F0608201223C44CFB30829000", "OPUS Card, used for transit in Greater Montreal and Quebec City, Canada (Transport)\nhttps://www.carteopus.info/?language=en" }, { "3B5F9600805A3F0608201223C44E6F25829000", "OPUS Metro card from Quebec, Canada (Transport)\nhttps://en.wikipedia.org/wiki/Opus_card" }, + { "3B5F9600805A3F1330141001C568504E829000", "OURA transport French (Transport)" }, { "3B5F9600805A3F1330141001C5D3ADC1829000", "OURA card (Transport)" }, { "3B5F9600805A3F1330141001C5D7A7E9829000", "<< Oura >> transport card (Transport)\nhttps://www.oura.com/" }, { "3B5F9600805A3F1330141001C656B5AA829000", "Oura Auvergne Rhone Alpes (Transport)\nhttps://oura.com" }, { "3B5F9600805A3F1330141001C6619BEA829000", "Oura card for Auvergne-Rhone-Alpes region in France. Using for several public transport (Transport)\nhttps://www.oura.com/" }, + { "3B5F9600805A3F2BC4141001C52E6FD3829000", "Mobib basic (Brussels transit card) (contact interface) (Transport)\nhttps://www.stib-mivb.be/buy/all-your-journeys-with-mobib" }, { "3B5F9600805A3F2BC4141001C5826963829000", "MOBIB Personal Travel Card, Brussels | STIB-MIVB (Transport)\nhttps://www.stib-mivb.be/article.html?_guid=d02c7fb6-3e9c-3810-248e-eec4ee5ebc8c&l=en" }, { "3B5F9600805AFFFF00FFFF0178724041829000", "Oura Card SNCF Transport - France Auvergne Rhone-Alpes (Transport)\nhttps://www.oura.com/xbi/boutique/card" }, + { "3B5F9600805AFFFF00FFFF0178728A57829000", "France Rhone-Alpes Region 'Carte Oura' 2nd generation (Transport)\nhttps://www.oura.com/la-demarche-oura/" }, { "3B600000", "Meano (Bank)" }, + { "3B61000041", "SBERKARTA MOMENTUM (Bank)" }, { "3B61000080", "blank A40CR card (JavaCard)" }, + { "3B6200004744", "UnionPay Debit card issued by Bank of China (Contact) (Bank)\nhttps://www.boc.cn/" }, { "3B630000364180", "Schlumberger Payflex 4k User" }, { "3B64..FF8062..A2", "JCOP20" }, { "3B64000080620.51", "Setec SetCOS 5.1.0 EMV" }, @@ -469,6 +481,7 @@ const static atr_t AtrTable[] = { { "3B6500002063CB3040", "Credit Mutuel Debit card (Bank)" }, { "3B6500002063CB3220", "Societe Generale CB Visa Debit (Bank)" }, { "3B6500002063CB3240", "Fnac MasterCard (contact interface) (Bank)\nhttps://www.fnac.com/carte-fnac-mastercard" }, + { "3B6500002063CB32A1", "MONABANQ (Bank - France) (Bank)" }, { "3B6500002063CB32C1", "Credit Card Credit Mutuel (Bank)\nhttps://www.creditmutuel.fr/partage/fr/CC/telechargements/communiques-de-presse/CM/2021/2021-03-17_CP-Carte_PVC_recycle.pdf" }, { "3B6500002063CB4700", "Orga SmartyPlus DATA STORE issued by MORPHO CARDS PERU" }, { "3B6500002063CB6300", "Bank card from Societe Generale (Oberthur)" }, @@ -628,7 +641,7 @@ const static atr_t AtrTable[] = { { "3B6800000073C84010009000", "Icelandic Banking scheme Issued by the Ministry of Treasure in Iceland\nhttp://www.islandsrot.is/" }, { "3B6800000073C84011009000", "Woolworths Everyday Money prepaid Mastercard\nNordea Bank Norway Visa + national debet card [BankAxept]\nVISA Classic - Nordlandsbanken (Norway)\nCiti Double Cash MasterCard\nWescom Credit Union Visa Debit Card (Bank)\nhttps://www.wescom.org/CHECKING/CHECK-CARD.ASP" }, { "3B6800000073C84012009000", "Brazilian 'e-CPF' card" }, - { "3B6800000073C84013009000", "MASTERCARD issued by MLP (Marschollek, Lautenschlager and Partner)\nG&D 12696-GDM-10/11 DEBIT CARD issued by BANCO DE CREDITO DEL PERU\nVisa from Caisse populaire Desjardins (Canada) (Bank)\nhttps://www.desjardins.com/\nMasterCard issued by President's Choice Bank (Canada)\nhttp://pcfinancial.ca/mastercard\nMasterCard issued by CIBC (Canada)\nhttp://www.cibc.com/\nMasterCard issued by The Bank of Nova Scotia (Canada)\nhttp://www.scotiabank.com/\nMasterCard issued by JPMorgan Chase Bank, N.A.\nhttps://www.chase.com/\nCaixaBank Visa Electron (Bank)\nhttps://www.lacaixa.cat/\nAmerican Express Canada Credit Card (Bank)\nAlfa-bank Russia Visa" }, + { "3B6800000073C84013009000", "MASTERCARD issued by MLP (Marschollek, Lautenschlager and Partner)\nG&D 12696-GDM-10/11 DEBIT CARD issued by BANCO DE CREDITO DEL PERU\nVisa from Caisse populaire Desjardins (Canada) (Bank)\nhttps://www.desjardins.com/\nMasterCard issued by President's Choice Bank (Canada)\nhttp://pcfinancial.ca/mastercard\nMasterCard issued by CIBC (Canada)\nhttp://www.cibc.com/\nMasterCard issued by The Bank of Nova Scotia (Canada)\nhttp://www.scotiabank.com/\nMasterCard issued by JPMorgan Chase Bank, N.A.\nhttps://www.chase.com/\nCaixaBank Visa Electron (Bank)\nhttps://www.lacaixa.cat/\nAmerican Express Canada Credit Card (Bank)\nAlfa-bank Russia Visa\nPNC Bank Visa debit card (Bank)\nhttps://www.pnc.com/en/personal-banking/banking/debit-and-prepaid-cards/pnc-bank-visa-debit-card.html" }, { "3B6800000073C8401300907D", "NextCard - Mastercard Debit card - Intesa Sanpaolo Bank (Italy) (Bank)\nhttps://www.intesasanpaolo.com/it/persone-e-famiglie/prodotti/carte/carte-di-debito/next-card.html" }, { "3B68000000DE511001019000", "Itau Bank Mastercard Debit Card (Brazil) (Bank)\nhttps://www.itau.com.br" }, { "3B6800000101309600009000", "Edenred - French Restoration e-Ticket card (2013) (Other)\nhttps://www.edenred.fr/ticket-restaurant" }, @@ -640,6 +653,8 @@ const static atr_t AtrTable[] = { { "3B6800004D4343434C322E31", "Mastercard debit card from FIO bank, CZ (Bank)\nhttps://www.fio.cz/bankovni-sluzby/platebni-karty/mastercard-debit-contactless" }, { "3B68000053430660010F9000", "Credit Card Credicard (Bank)\nhttp://www.credicard.com.br" }, { "3B68000053430660010F9090", "Discount (Bank)" }, + { "3B68000053430662010F9000", "LINE Bank by Hana Bank Sally Debit Visa (Bank)" }, + { "3B68000053430663010F9000", "Maybank ID Debit Mastercard (Bank)\nBank BRI Blue Debit GPN (Bank)" }, { "3B68000053432D3031324A53", "Blue (Bank)" }, { "3B6800005448204E49442036", "Thai id card (Other)" }, { "3B680000565344434C433130", "VISA (Estonian), made by www.trueb.ch\nLatvian bank 'Latvijas Krajbanka' (VISA Electron)" }, @@ -671,6 +686,7 @@ const static atr_t AtrTable[] = { { "3B6900002494010000000001A9", "Kazakhstan Helios gas station debit card\nhttp://helios.kz/" }, { "3B6900002494010201000101A9", "Chipcard from SUN to be used in SunRay's\n370-4328-01 (31091)" }, { "3B6900002494010301000100A9", "Schlumberger MicroPayflex S card" }, + { "3B69000047445F44695F54504E", "ICBC (Bank)" }, { "3B6900004944353056312E....", "eID Card to user authenticate and save passwords in the Card. Product ID50 Password manager from IDENTOS GmbH (eID)\nhttps://identos.com/id50-password-manager/" }, { "3B6900004944363056312E....", "token appidkey ID60-USB (Other)\nhttps://identsmart.com/en/products/id60-datasafe/" }, { "3B6900004A434F503331563232", "Visa Europe Sample Card / Axalto" }, @@ -713,6 +729,7 @@ const static atr_t AtrTable[] = { { "3B6A00008066A1090201630E9000", "Danish Visa/Dankort\nUK MBNA MasterCard\nVisa Card - Worldcard - YapiKredi / Turkey\nVISA - Lloyds TSB DEBIT\nUK Halifax Visa Debit" }, { "3B6A00008066A20A01018B0E9000", "CAP-EMV demo card" }, { "3B6A0000813F017511010281010A", "Huada CIU9872B (Java Card 2.2.2) (JavaCard)" }, + { "3B6A00008141019810000000000A", "UnionPay Debit card issued by Bank of China (Contact) (Bank)\nhttps://www.boc.cn/" }, { "3B6A0000866500A758C046009000", "China Merchants Bank card (Bank)" }, { "3B6A0000866500A758C055009000", "Credit card (Bank)" }, { "3B6A00FF0031C173C84000009000", "Paypal EMV debit card (Bank)\nAmerican Express Canada Credit Card (Bank)\nhttps://www.americanexpress.com/ca/en/credit-cards/all-cards/?intlink=ca-en-hp-product1-cm-personalcards-03242021" }, @@ -784,12 +801,15 @@ const static atr_t AtrTable[] = { { "3B6B00000031C164086032200F9000", "Twisto (Bank)" }, { "3B6B00000031C16408603221079000", "DSK Bank Debit Mastercard (Bank)" }, { "3B6B00000031C164086032220F9000", "Universidade de Aveiro (ID Card) (eID)\nhttps://www.ua.pt/pt/sas/cartao" }, + { "3B6B00000031C16408603242079000", "CIMB BANK MALAYSIA (Bank)" }, { "3B6B00000031C164086032420F9000", "Westpac Handybank EFTPOS/ATM Card (Bank)" }, { "3B6B00000031C164087771300F9000", "Apple Card (from launch) (Bank)\nhttps://www.apple.com/apple-card/" }, { "3B6B00000031C16408777156079000", "American Express UK Euro ICC charge card (Bank)\nhttps://www.americanexpress.com/icc/cards/the-basic-international-currency-card.html" }, { "3B6B00000031C16408986200079000", "EquaBank Master Card (Bank)\nhttps://equabank.cz" }, { "3B6B00000031C164089862000F9000", "NovaKBM Visa Debit (Bank)" }, { "3B6B00000031C164089862010F9000", "Sodexo Czech Gastro/Multipass (Other)\nhttps://www.sodexo.cz/" }, + { "3B6B00000031C164089862020F9000", "Blu BCA Digital Debit Mastercard (Bank)" }, + { "3B6B00000031C16408986213079000", "Stravenka (Other)" }, { "3B6B00000031C1640924331E0F9000", "Cembra Money Bank - Certo! Mastercard credit card (Bank)\nhttps://certo-card.ch/certo/de/" }, { "3B6B00000031C164092962250F9000", "SANTANDER BASIC CASH CARD (Bank)\nhttps://www.santander.co.uk/assets/s3fs-public/2018-09/Basic%20Current%20Account%20KFD.pdf" }, { "3B6B00000031C16409644136079000", "HSBC UK Visa Debit Card (Bank)\nhttps://www.hsbc.co.uk/content/dam/hsbc/gb/pdf/help/hsbc-visa-debit-card-guide.pdf" }, @@ -814,6 +834,7 @@ const static atr_t AtrTable[] = { { "3B6B000081007226010010000F9000", "Debit card issued by Falabella Bank of Peru from IDEMIA (Bank)\nhttp://www.bancofalabella.pe" }, { "3B6B000081007226020040000F9000", "Yuna To Go prepaid MasterCard (PaySafeCard) (Bank)\nhttp://yunacard.com\nPaygoo Reload Mastercard" }, { "3B6B000081007226020041000F9000", "Wave Crest Holding (Bank)" }, + { "3B6B000081007230050070000F9000", "Postbank VISA card prepaid (Bank)\nhttps://www.postbank.de/privatkunden/services/konten-und-karten/visa-card-prepaid-aufladen.html" }, { "3B6B000081007843040241010F9000", "Max Mastercard (Bank)\nhttps://www.aumax.fr" }, { "3B6B0000FF86885A48544430322011", "CMB UnionPay Debit (Bank)" }, { "3B6B00FF33000009FA10008001FFFF", "Atmel 6464C PRO 64K" }, @@ -868,7 +889,10 @@ const static atr_t AtrTable[] = { { "3B6D00000080318065B0893501F183009000", "MasterCard (Bank)" }, { "3B6D000000814D22088660200811000001", "BANK OF HEBEI Debit Card (Bank)" }, { "3B6D000000814D22088660222228400001", "Unipay Credit Card Issued by CITIC (Bank)" }, + { "3B6D0000008500001086880000001DA844", "UnionPay Debit card issued by China Construction Bank (Bank)\nhttps://www.ccb.com/" }, + { "3B6D0000008C430101868821014015D856", "UnionPay Debit card issued by Industrial and Commercial Bank of China (Contact) (Bank)\nhttps://www.icbc.com.cn/" }, { "3B6D0000009008209000900000FFFFFFFF", "Student college card" }, + { "3B6D000004008688010001626A79686963", "UnionPay Debit card issued by China Minsheng Bank (Contact) (Bank)\nhttps://www.cmbc.com.cn/" }, { "3B6D00004946582D6A6333303167703232", "MasterCard Debit card of N26 bank (Bank)" }, { "3B6D00005744285746035062136568541F", "Card for decrypt encrypted tv channels made in china (Pay TV)" }, { "3B6D00005744296C808693D1271F13323D", "SCSTA (Transport)" }, @@ -900,6 +924,7 @@ const static atr_t AtrTable[] = { { "3B6D00FF80655343010D067394211B810[15]", "Giesecke & Devrient CardToken 350 (ICCD)" }, { "3B6D00FF8073002113574A544861314700", "ActiveKey SIM" }, { "3B6D00FF8073002113574A544861314800", "Spanish Medical College Card" }, + { "3B6E00000000814D22088660300020E00007", "Mastercard Debit card issued by Bank of China (Contact) (Bank)\nhttps://www.boc.cn/" }, { "3B6E00000031807186650164022232809000", "MasterCard Card - bonus plus (paypass) - Garanti Bank / Turkey" }, { "3B6E00000031807186650164022232839000", "MasterCard Card - bonus YKM - Garanti Bank / Turkey" }, { "3B6E0000003180718665016702A00A839000", "Australian ANZ First Visa Card from the ANZ\n(Australia and New Zealand) Bank" }, @@ -1016,7 +1041,7 @@ const static atr_t AtrTable[] = { { "3B6E000080318065B00301015E8300009000", "FirstUSA Visa" }, { "3B6E000080318065B00302015E8300009000", "Gemplus GemXpresso 211is" }, { "3B6E000080318066B0070300AC0183009000", "e-payment card with topup system, propreteary by local bank\nhttp://www.klikbca.com/individual/silver/product.html?s=69" }, - { "3B6E000080318066B0840C016E0183009000", "Optelio Cards (D72 R4 WR)\nNordea (a Skandinavian bank) eID card\nhttp://linux.fi/wiki/Nordea_eID\nNordea Mastercard card\nNordea Visa card\nRBC Royal Bank Client Card (bank in Canada)\nBanco Santander TUI/USC R7\nGemalto Optelio/Desineo D72 (JavaCard) with WG10 and Maestro (JavaCard) (Bank)\nCarte Ticket Restaurant with MasterCard\nCitigold VISA Debit for Citibank, Australia\nPlatinum VISA card for Citibank, Australia\nVISA Infinite issued by RBC Royal Bank (Canada)\nhttp://www.rbc.com/\nPostepay Evolution - Poste Italiane (mastercard)\n'la Caixa' (Spain) (VISA Electron) debit card (Bank)\nhttps://www.lacaixa.es/\nItalian Webank.it BPM Banca Popolare di Milano Bancomat & Maestro Card (Bank)\nSberbank of Russia MIR debit card (Bank)\nMasterCard bank card by OTP Bank (Hungary)" }, + { "3B6E000080318066B0840C016E0183009000", "Optelio Cards (D72 R4 WR)\nNordea (a Skandinavian bank) eID card\nhttp://linux.fi/wiki/Nordea_eID\nNordea Mastercard card\nNordea Visa card\nRBC Royal Bank Client Card (bank in Canada)\nBanco Santander TUI/USC R7\nGemalto Optelio/Desineo D72 (JavaCard) with WG10 and Maestro (JavaCard) (Bank)\nCarte Ticket Restaurant with MasterCard\nCitigold VISA Debit for Citibank, Australia\nPlatinum VISA card for Citibank, Australia\nVISA Infinite issued by RBC Royal Bank (Canada)\nhttp://www.rbc.com/\nPostepay Evolution - Poste Italiane (mastercard)\n'la Caixa' (Spain) (VISA Electron) debit card (Bank)\nhttps://www.lacaixa.es/\nItalian Webank.it BPM Banca Popolare di Milano Bancomat & Maestro Card (Bank)\nSberbank of Russia MIR debit card (Bank)\nMasterCard bank card by OTP Bank (Hungary)\nUniCredit Bank in Serbia" }, { "3B6E000080318066B08412016E0183009000", "Barclaycard Platinum VISA\nInteligo debit card\nVISA issued by ING (Poland)\nVISA Debit card for ING Direct, Australia\nVISA Gold issued by RBC Royal Bank (Canada)\nhttp://www.rbc.com/\nGas Natural Fenosa Visa (issued by CaixaBank) (Bank)\nhttp://www.clubfenosa.gasnaturalfenosa.es/ca/1285341160257/targeta+gas+natural+fenosa.html" }, { "3B6E000080318066B08416016E0183009000", "UK 'Barclaycard Gold VISA' with RFID" }, { "3B6E000080318066B0870C016E0183009000", "Banco Santander TUI/USC R7 - Gemalto Optelio/Desineo D72 (JavaCard)\nhttp://www.observatoriotui.com/home" }, @@ -1027,6 +1052,8 @@ const static atr_t AtrTable[] = { { "3B6E000080318066B1A50102321983009000", "Meeza BDC (Bank)" }, { "3B6E000080318066B1A60101011383009000", "Finnish Smartum benefit card (Other)\nhttps://www.smartum.fi/" }, { "3B6E00008066B1A30401110B83009000", "Bank of America (Bank)\nhttps://www.bankofamerica.com/" }, + { "3B6E00008066B1A30401110B830090000000", "Bank of America (Bank)\nhttps://www.bankofamerica.com/" }, + { "3B6E000086881102010100004748F1FF5550", "UnionPay Debit card issued by Industrial and Commercial Bank of China (Contact) (Bank)\nhttps://www.icbc.com.cn/" }, { "3B6E00FF00620000574156414E5410819000", "debit card (Visa Electron) issued by Nordea bank" }, { "3B6E00FF4573744549442076657220312E30", "Estonian Identity Card (EstEID v1.0 warm)" }, { "3B6E00FF47442D47502D333256342D444553", "Mastercard Ourocard Platinum from Banco do Brasil" }, @@ -1045,6 +1072,7 @@ const static atr_t AtrTable[] = { { "3B6F00000031C173C8211064474D3332009000", "td debit visa card (Bank)\nScotiabank ScotiaCard Interac/Visa debit card (Bank)\nhttps://www.scotiabank.com/ca/en/personal/ways-to-bank/debit-credit-prepaid-cards/debit-cards/scotiacards.html" }, { "3B6F00000031C173C8211064474D3338009000", "ING Direct Debit Card (Bank)" }, { "3B6F00000031C173C8211064474D3341009000", "Fineco debit card (Bancomat, Visa Debit) (Bank)\nhttps://finecobank.com/it/online/conto-e-carte/carte-e-bancomat/" }, + { "3B6F00000031C173C8211064474D3342009000", "Visa Debit card issued by Bank of China (Contact) (Bank)\nhttps://www.boc.cn/" }, { "3B6F00000031C173C8211064474D3435009000", "VISA card, issued by OP Financial Group, Finland (Bank)\nVISA card, issued by S-Pankki, Finland (Bank)" }, { "3B6F00000031C173C8211064474D3437009000", "Visa credit card, issued by Klarna (Bank) (Bank)\nhttps://www.klarna.com/se/kort/" }, { "3B6F00000031C173C8211064474D3533009000", "American Express Platinum Card Mexico (Bank)\nhttps://www.americanexpress.com/mx/tarjetas-de-credito/the-platinum-credit-card/" }, @@ -1071,6 +1099,7 @@ const static atr_t AtrTable[] = { { "3B6F00003101F1564011001900000000000000", "Postepay Evolution (Bank)\nhttps://postepay.poste.it/prodotti/postepay-evolution.html" }, { "3B6F000057694C4C5700000000000020190328", "meza classic card (Bank)" }, { "3B6F000057694C4C5700000000000020200429", "AMEX CHINA Debit card, Java Card version 2.2, Global Platform version 2.1.1, Visa card manager (Bank)" }, + { "3B6F000057694C4C5700000000000020200430", "Mastercard China Debit card issued by Postal Savings Bank of China (Contact) (Bank)\nhttps://www.psbc.com/" }, { "3B6F0000626C75636172642031364B422F7634", "eID Blutronics Blucard 16K\nhttp://blucard.blutronics.com" }, { "3B6F00008031C0520083640219083283839000", "Bancomer Mexican Bank" }, { "3B6F00008031C05205B5640200647183839000", "Read Card in USB, used for application in Java. (JavaCard)" }, @@ -1102,12 +1131,12 @@ const static atr_t AtrTable[] = { { "3B6F00008031E06B0421050261555555555555", "NORSK TIPPING NORWAY\nhttp://www.norsk-tipping.no/" }, { "3B6F00008031E06B042105026C555555555555", "Icelandic Electronic ID (eID)\nhttps://www.audkenni.is/rafraen-skilriki/einkaskilriki/" }, { "3B6F00008031E06B0421050272555555555555", "Card CAA Quebec (Mastercard) (Bank)\nhttps://www.caaquebec.com/en/your-privileges/caa-dollars/earning/caa-quebec-dollars-mastercardr-credit-card/" }, - { "3B6F00008031E06B04310502A6555555555555", "USAA EMV Mastercard Creditcard (Bank)\nhttps://www.usaa.com/inet/pages/bk_cc_chipcardLP_landing_mkt?adID=VURL_chipcard" }, + { "3B6F00008031E06B04310502A6555555555555", "USAA EMV Mastercard Creditcard (Bank)\nhttps://www.usaa.com/inet/pages/bk_cc_chipcardLP_landing_mkt?adID=VURL_chipcard\nTarget REDcard debit\nhttps://target.com/myREDcard" }, { "3B6F00008031E06B04310502AC555555555555", "Diners Club Credit card (British Airways, Switzerland) (Bank)\nhttps://dinersclub.ch/en/private-customers/all-credit-cards/british-airways-card/" }, { "3B6F00008031E06B04310502AF555555555555", "debit card (Bank)\nhttps://www.usaa.com/inet/wc/banking" }, { "3B6F00008031E06B04310502D1555555555555", "Pockit Pre-Paid Mastercard (Contact Chip). (Bank)\nhttps://revolut.com/" }, { "3B6F00008031E06B04310502D6555555555555", "Bank IN - Slovenian bank (Bank)\nhttps://www.bankain.si/BIN/vstopna.seam" }, - { "3B6F00008031E06B04520502BB555555555555", "AlfaBROU - Mastercard (prepaid card emitted by Banco Republica - Uruguay) (Bank)\nhttps://www.brou.com.uy/personas/tarjetas/prepaga-alfabrou" }, + { "3B6F00008031E06B04520502BB555555555555", "AlfaBROU - Mastercard (prepaid card emitted by Banco Republica - Uruguay) (Bank)\nhttps://www.brou.com.uy/personas/tarjetas/prepaga-alfabrou\nCapital One - Mastercard debit card (Bank)\nhttps://www.capitalone.com/bank/debit-card/" }, { "3B6F00008031E06B04520502FD555555555555", "C6 Bank Mastercard Global Account (Bank)\nhttps://www.c6bank.com.br/conta-global\nC6 Bank Mastercard Brazil Account (Bank)\nhttps://www.c6bank.com.br/nossos-produtos" }, { "3B6F00008031E06B04520502FE555555555555", "CITIBanamex 'Perfiles' debit card (Bank)\nhttps://www.banamex.com/es/personas/cuentas/cuenta-perfiles.html" }, { "3B6F00008031E06B04546B026D555555555555", "Mastercard Credit/Debit Card (Bank)" }, @@ -1201,6 +1230,7 @@ const static atr_t AtrTable[] = { { "3B6F0000805A0A070620042C0490EFCB829000", "Rav-Kav Israel (Transport)\nhttps://ravkavonline.co.il" }, { "3B6F0000805A0A070620042C04940F18829000", "Israeli anonymous rail road Rav Kav 2022 model (Transport)\nhttps://en.wikipedia.org/wiki/Rav-Kav" }, { "3B6F0000805A0A070620042C0494625E829000", "Israeli rail road rav card (Transport)\nhttps://en.wikipedia.org/wiki/Rav-Kav" }, + { "3B6F0000805A0A070620042D9363A22B829000", "Rav-Kav Transport\nhttps://ravkavonline.co.il/en/" }, { "3B6F0000805A0A070620042DC1660B73829000", "Rav-Kav: Israel's Travel Card (Transport)" }, { "3B6F0000805A0A070620042DC169517A829000", "Card for bus (Transport)" }, { "3B6F0000805A0A070620042DC2E8E270829000", "ravkav (Transport)" }, @@ -1247,6 +1277,7 @@ const static atr_t AtrTable[] = { { "3B6F0000805A28114210122B27AE59B9829000", "French 'Navigo' transport card (Transport)\nhttp://www.navigo.fr/" }, { "3B6F0000805A28114210122B4C09310D829000", "Pass Navigo personnalise (Paris) (Transport)\nhttp://www.navigo.fr/titres/le-forfait-navigo-mois-choix-carte-navigo-carte-decouverte/" }, { "3B6F0000805A28114210122B4C0EFAD0829000", "Ile-de-France Mobilites (Navigo Nominative) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo" }, + { "3B6F0000805A28130210122B034BA194829000", "Korrigo (Brittany transit card) (contact interface) (Transport)\nhttps://www.korrigo.bzh/" }, { "3B6F0000805A28130210122B034BEB8D829000", "Korigo card transport for star company (France) (Transport)\nhttp://www.star.fr/titres-et-tarifs/carte-korrigo/" }, { "3B6F0000805A28130210122B03CF898A829000", "Korrigo Card (Rennes Metropole bus network) (Transport)\nhttps://www.star.fr/" }, { "3B6F0000805A28130210122B03D99C6F829000", "KorriGo (Brittany) transport card (Transport)" }, @@ -1302,7 +1333,9 @@ const static atr_t AtrTable[] = { { "3B6F0000805A2D06081010027835EDCE829000", "Lisbon Transportation SmartCard (Transport)" }, { "3B6F0000805A2D06081010027848BBCC829000", "Lisbon Metro Monthly Student Pass (Transport)\nhttps://www.metrolisboa.pt/" }, { "3B6F0000805A2D0608101005935C42FB829000", "Comboios de Portugal Transit Card (Transport)\nhttps://www.cp.pt/passageiros/pt/consultar-horarios/precos/cartao-cp" }, + { "3B6F0000805A2D06081010059468C083829000", "Comboios de Portugal Transit Card (Transport)\nhttps://www.cp.pt/passageiros/pt/consultar-horarios/precos/cartao-cp" }, { "3B6F0000805A2E130200010104EF8342829000", "Oura Auvergne-Rhone-Alpes (Transport)\nhttps://www.oura.com" }, + { "3B6F0000805A2E1302000101055CA7E3829000", "OURA card, FRench isere transport (Transport)\nhttps://www.oura.com" }, { "3B6F0000805A3407061500017917A7E2829000", "Rav-Kav multi-line travel ticket used in the public transportation system in Israel (Transport)\nhttps://www.gov.il/en/departments/guides/multi_line_card" }, { "3B6F0000805A340706150001792A4B5C829000", "Rav Kav Transit Payment Card (Israel) (Transport)\nhttps://ravkavonline.co.il/" }, { "3B6F0000805A3B0102151201798DE8C9829000", "TAM Montpellier France (Transport)" }, @@ -1311,6 +1344,8 @@ const static atr_t AtrTable[] = { { "3B6F0000805A3B070615010279537211829000", "Israeli public transport card ('RavKav') (Transport)\nhttps://ravkavonline.co.il" }, { "3B6F0000805A3B07061501027956C5F4829000", "RAVKAV - Israel dual interface transport card (Calypso standard) (Transport)\nhttps://en.wikipedia.org/wiki/Rav-Kav" }, { "3B6F0000805A3B070615010279C3C331829000", "Rav-Kav Israel (Transport)\nhttps://ravkavonline.co.il" }, + { "3B6F0000805A3B070615010279ED6905829000", "rav-kav (Transport)" }, + { "3B6F0000805A3C0608140101C2F8B0EF829000", "Navegante Lisboa Viva (Transport)\nhttps://www.portalviva.pt/" }, { "3B6F0000805A3C0608140101C3805E38829000", "Lisboa VIVA - Lisbon public transport card (Transport)\nhttps://www.portalviva.pt/" }, { "3B6F0000805A3C0608140101C4D4FEC4829000", "Metropolitan Transports of Lisbon NAVEGANTE Card (Transport)\nhttps://www.navegante.pt/viajar/cartoes" }, { "3B6F0000805A3C0608140101C4D522FB829000", "Navegant Perdonal Card - Transportes Metropolitanos de Lisboa (Transport)\nhttps://www.navegante.pt/" }, @@ -1345,6 +1380,7 @@ const static atr_t AtrTable[] = { { "3B6F0000805A3D23C415010279376C64829000", "Brussel public transport NFC card (Transport)\nhttps://www.stib-mivb.be/mystib" }, { "3B6F0000805A3D23C41501027937D7AE829000", "MOBIB - Brussels (Transport)\nhttps://www.stib-mivb.be/article.html?_guid=d02c7fb6-3e9c-3810-248e-eec4ee5ebc8c&l=fr" }, { "3B6F0000805A3D23C41501027949789C829000", "MOBIB basic (Transport)\nhttp://www.stib-mivb.be/article.html?_guid=30af0085-2483-3410-5394-a71daf08acd1&l=en#contentBodyList1" }, + { "3B6F0000805A3D23C41501027949B7D5829000", "MOBIB basic card (Transport)\nhttps://www.stib-mivb.be/buy/all-your-journeys-with-mobib" }, { "3B6F0000805A3D23C4150102795A863C829000", "mobib transportation card (Transport)" }, { "3B6F0000805A3D23C415010279748A25829000", "Mobib (Brussels transport card) for the STIB-MIVB network (Transport)\nhttps://www.stib-mivb.be/article.html?_guid=d02c7fb6-3e9c-3810-248e-eec4ee5ebc8c&l=fr" }, { "3B6F0000805A3D23C415010279A9E567829000", "MoBIB card, a medium for the transport tickets of the four Belgian public transport operators (Transport)\nhttps://mobib.be/" }, @@ -1364,6 +1400,7 @@ const static atr_t AtrTable[] = { { "3B6F00008066B0070101770753023110829000", "University ID card (issued by Banco Santander Central Hispano)\n.\nUniversidad Nacional de Educacion a Distancia (UNED, Spain)\nhttp://www.uned.es/tarjeta\n.\nUniversitat Politecnica de Catalunya (UPC.edu)\nhttps://www.upc.edu/identitatdigital\n.\nUniversitat Ramon Llull (URL)\nhttp://www.url.edu/cont/url/carnet.php" }, { "3B6F00008066B0070101770753023124829000", "Santander 4B Maestro\nUniversity of Santiago de Compostela. Spain\nPolytechnical University of Madrid, Spain" }, { "3B6F00018031E06B0406050211555555555555", "American Express 'Entourage' credit card issued by CIBC\nhttp://www.cibc.com" }, + { "3B6F00FF0031C173C8211064414D3137079000", "Mastercard ING (Bank)" }, { "3B6F00FF0031C173C8211064414D3348079000", "BNP (Bank)" }, { "3B6F00FF0031C173C82110644930424E079000", "National Bank Card (Bank)" }, { "3B6F00FF00567275546F6B6E73302000009000", "Aktiv Rutoken S\nhttps://www.rutoken.ru/products/all/rutoken-s/" }, @@ -1381,6 +1418,7 @@ const static atr_t AtrTable[] = { { "3B751300004709EA9000", "Carte Vitale (HealthCare)" }, { "3B751300004809EA9000", "Carte Vitale (HealthCare)\nhttps://en.wikipedia.org/wiki/Carte_Vitale" }, { "3B751300004909EA9000", "Vitale Card French Healthcare (HealthCare)\nhttps://www.service-public.fr/particuliers/vosdroits/F265" }, + { "3B751300004B09EA9000", "Carte Vitale d'assurance maladie, France (HealthCare)\nhttps://www.service-public.fr/particuliers/vosdroits/F265" }, { "3B751300009C02020102", "Cyberflex Access 32k v2" }, { "3B759400006202020[1-3]01", "Schlumberger Cyberflex 32K e-gate\nGemalto TOP US (product code HWP115278A)" }, { "3B76110000009C11010202", "Schlumberger Cyberflex Access 32K" }, @@ -1423,6 +1461,7 @@ const static atr_t AtrTable[] = { { "3B789600005343066001079000", "Bank of America Travel Rewards Credit Card (Bank)\nhttps://www.bankofamerica.com/credit-cards/products/travel-rewards-credit-card/" }, { "3B789600005343066101079000", "Bank of America Cash Rewards World Mastercard (Bank)" }, { "3B789600005343066201079000", "ATM Card (Bank)" }, + { "3B789600005343066301079000", "Bank Saqu Debit GPN (Bank)" }, { "3B789600008100035001079000", "RMA BMCE BANK CARD (Bank)" }, { "3B791100008054434F4C44829000", "amazon.de / VISA / LBB Debit Card (Bank)" }, { "3B79130000806416030183829000", "Raiffeisen VPay Debit Card (Bank)\nhttp://raiffeisen.ch" }, @@ -1498,6 +1537,7 @@ const static atr_t AtrTable[] = { { "3B7D180002805759505349443033837F9000", "Sagem YpsID s2 (SafeSign)\nBrazilian 'e-CNPJ' card, issued by Certisign (Safesign)" }, { "3B7D180002805759505349443034837F9000", "Morpho e-CPF YpsID S2-11/11 (PKI)\nhttp://safeweb.com.br" }, { "3B7D9400005555530A7486930B247C4D5468", "SIM from sysmocom sysmoSIM-GR2" }, + { "3B7D94000057442101181207010000000000", "mir (Bank)" }, { "3B7D94000057442908308693070565182B56", "Canal Digitaal (Pay TV)\nhttp://webshop.canaldigitaal.nl/nl/smartcards-2" }, { "3B7D9400005744295A2186930332EF174607", "Chevrolet Club Card (Loyalty)\nhttps://my.chevrolet.com/login" }, { "3B7D940000574429615086930351462501DC", "Sodexo Meal Card (Other)\nhttps://www.sodexoavantaj.com" }, @@ -1505,6 +1545,7 @@ const static atr_t AtrTable[] = { { "3B7D94000057445324658693020581741057", "kyivstar (Telecommunication)" }, { "3B7D940000574453246586930210B7251297", "Kyivstar (Telecommunication)" }, { "3B7D94000057445363968693009DF710009D", "China Mobile SIM card" }, + { "3B7D94000057445364968693049DF710009D", "oiran-sim-card (Telecommunication)" }, { "3B7D94000057445372FD8693110106755B0F", "China Telecom UIM 64K" }, { "3B7D94000057445396FA869303B7BFBF5F63", "Airtel India SIM" }, { "3B7D94000057445399648693120300006346", "Dtac (Telecommunication)" }, @@ -1702,6 +1743,7 @@ const static atr_t AtrTable[] = { { "3B7F96000080318065B085040120F20002829000", "Health Insurance Institute of Slovenia - Professional Card Gen. 3 (HealthCare)" }, { "3B7F96000080318065B085050011120FFF829000", "LuxTrust card (Luxembourg qualified electronic signature / authentication system) (Other)\nhttps://www.luxtrust.com/en/professionals/smartcard" }, { "3B7F96000080318065B085050039120FFE829000", "SafeNet IDPrime 940C (eID)\nhttps://data-protection-updates.gemalto.com/2023/10/05/safenet-etoken-5110-cc-940c-release-announcement/" }, + { "3B7F96000080318065B085051024120FFF829000", "Swedish ID card from Skatteverket with an Electronic ID from AB Svenska Pass (eID)\nhttps://www.thalesgroup.com/sv/europe/sweden/digital-identity-services-sweden/svensk-elegitimation/skatteverkets-id" }, { "3B7F96000080318065B0855956FB120268829000", "qualified certificate (eID)\nhttps://www.elektronicznypodpis.pl/en/offer/qualified-certificates/" }, { "3B7F96000080318065B0855956FB1202C1829000", "Gemalto USB (eID)" }, { "3B7F96000080318065B0855956FB120FFE829000", "Thales (Gemalto) IDPrime 941 (PKI)\nhttps://cpl.thalesgroup.com/de/access-management/idprime-md-pki-smart-cards\nThales SafeNet IDPrime 940B (PKI)\nhttps://cpl.thalesgroup.com/resources/access-management/idprime-940-product-brief" }, @@ -1820,8 +1862,10 @@ const static atr_t AtrTable[] = { { "3B868001801434373000A0", "Malta eID Identity Card (eID)\nhttps://www.identitymalta.com/" }, { "3B8680018031C15211182C", "IDEMIA Cosmo V8.0 with a PIV applet (contactless) (PKI)" }, { "3B8680018031C152411A7E", "IDEMIA Cosmo V8.1 with a PIV applet (contactless) (PKI)" }, + { "3B868001804F03F001003A", "Second-generation Resident Identity Card of the People's Republic of China (eID)\nhttps://www.gov.cn/zhengce/2011-10/29/content_2602263.htm" }, { "3B86800180540410010FC9", "Nickel.eu prepaid account (Bank)\nhttps://nickel.eu" }, { "3B86800180540420010FF9", "advance medical expenses in France (consultation, medications, hospitalization, etc.). commercial name is 'Avance Sante'. (HealthCare)" }, + { "3B86800187870202000007", "Nano USIM card of 'LG U+' in South Korea. (model name U2720) (Telecommunication)" }, { "3B868001C1052F2F01BC7E", "Contactless interface to St. Petersburg unified card, Russia (Edinaia karta peterburzhtsa) (Other)\nhttps://ekp.spb.ru/" }, { "3B868001F04938DE0C3064", "blyt mtrw (Transport)\nhttps://ezpay.ir/" }, { "3B868131703445504120454B08", "Austrian Quick E-purse 'Einreichkarte' (transfer card)\nhttp://www.quick.at/" }, @@ -1829,6 +1873,7 @@ const static atr_t AtrTable[] = { { "3B8780014D525444312E3026", "Russian Foreign Passport (passport)" }, { "3B8780014D525444322E3025", "Biometric Passport of the Russian Federation (passport)" }, { "3B8780015A4350757273655E", "ZCPurse RFID card (ATR created by PCSC-Reader) (Transport)\nhttp://www.zeitcontrol.de/en/zcpurse" }, + { "3B87800169536869656C6450", "SwissBit iShield Key Security Key (FIDO2) (Other)\nhttps://www.swissbit.com/en/products/ishield-key/" }, { "3B87800173C840000090006D", "Amazon.de VISA Card (Bank)\nhttp://lbb.de/amazon\nCIBC Visa" }, { "3B8780017743324C0101004C", "CiPurse L profile with 304 bytes user memory (Other)\nhttps://www.infineon.com/cms/en/product/security-smart-card-solutions/cipurse-products/" }, { "3B878001774332530008005B", "CiPurse S profile (Other)\nhttps://www.infineon.com/cms/en/product/security-smart-card-solutions/cipurse-products/" }, @@ -1877,16 +1922,19 @@ const static atr_t AtrTable[] = { { "3B888001000000008081710079", "Apple Pay card - Usually EMV" }, { "3B88800100000000808175007D", "Singapore SimplyGo EZ-Link Card (Transport)\nhttps://simplygo.com.sg" }, { "3B888001000000009171710098", "Public transportation fare card (Morocco CAS: Urban bus) (Transport)" }, + { "3B888001000000009181710068", "PassPass card (Transport)\nhttps://www.passpass.fr/fr/e-boutique/achat-carte" }, { "3B888001000000009181C100D8", "Driving License card of Japan (NFC type-B) (eID)" }, { "3B88800100000000B37171.0.A", "Public transportation card in Riga, Latvia, called 'e-Talons'\nhttp://etalons.rigassatiksme.lv/en/payments/activating_the_e-ticket/\nBelgian MOBIB (transport)" }, + { "3B88800100000000B3717100BA", "Pastel (transport card in Occitania and Toulouse, France) (Transport)\nhttps://www.ter.sncf.com/occitanie/services-contacts/souscription-carte-pastel" }, { "3B88800100000000F781C100BE", "Italian Health Insurance card (eID)\nhttps://en.wikipedia.org/wiki/Italian_health_insurance_card" }, { "3B88800100000011778183006D", "Taglio PIVKey C980 - RFID I/F (PKI)" }, { "3B88800100000011F7818100EF", "MOBIB card (Transport)\nhttps://mobib.be/en.html\nPasse Navigo Easy (Transport Ile de France) (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-easy" }, { "3B8880010000010701729000EC", "Belgian passport (2008-2009)" }, - { "3B888001000005E0B381A1007F", "Japanese JPKI card (aka JINC card) (eID)\nhttps://github.com/jpki/myna" }, + { "3B888001000005E0B381A1007F", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nJapanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en" }, { "3B888001000014E0B38191005E", "'JUKICARD', the Basic Resident Registration Card in Japan (eID)" }, - { "3B888001000041E0B381A1003B", "ID card issued by Japan government (eID)\nhttps://www.kojinbango-card.go.jp/mynumber/index.html" }, + { "3B888001000041E0B381A1003B", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nJapanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en" }, { "3B8880010000C9047781730041", "D-TRUST Card 4.1, qualified signature card (eID)\nhttps://www.d-trust.net" }, + { "3B888001004B51FFB381D1000F", "Japanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en/" }, { "3B8880010073C8400000900062", "NXP JCOP 31 V2.2 36K - RFID I/F\nBarclaycard Visa Wave & Pay - RFID I/F\nCIBC Visa" }, { "3B8880010073C8401300900071", "Nokia 6131 NFC phone\nhttp://wiki.forum.nokia.com/index.php/Nokia_6131_NFC_-_FAQs\nGiesecke & Devrient's (G&D) Sm@rtCafe Expert 3.1\nAmex Bank of Canada American Express\nTD Canada Trust Visa\nTD Canada Trust Access Card (Visa Debit)" }, { "3B88800100883C1F77819500C1", "Polish Passport (passport)" }, @@ -1895,6 +1943,7 @@ const static atr_t AtrTable[] = { { "3B8880011000000000817000E8", "Tap&Go MasterCard Sim Card (Bank)\nhttps://www.tapngo.com.hk" }, { "3B88800111000011008184000C", "OCBC bank card (Bank)" }, { "3B888001110000113381A38098", "casatramway rechargeable card (Transport)\nhttps://www.casatramway.ma/" }, + { "3B88800111000011778181007E", "Transport card of Bordeaux, France (Transport)\nhttps://www.infotbm.com/fr" }, { "3B88800111000011778183007C", "Passport of citizen of Ukraine, Passport of citizen of Ukraine for traveling abroad (passport)" }, { "3B88800111223344808171003D", "FIME Card Emulator (Other)\nhttps://www.fime.com/products/terminal-integration/savvi.html" }, { "3B8880011BE1F35E11778100B9", "Uruguyan persona ID. Nowadays a eID. (eID)\nhttps://www.gub.uy/agencia-gobierno-electronico-sociedad-informacion-conocimiento/firma-digital/es-cedula-identidad-digital" }, @@ -1915,7 +1964,9 @@ const static atr_t AtrTable[] = { { "3B88800143433169AA200000DB", "PostFinance Switzerland (Bank)\nhttp://www.postfinance.ch" }, { "3B888001434C6169726520360F", "VISA credit card with NFC payment function (Bank)\nhttp://www.visa.ca/en/personal/visa-paywave/index.jsp" }, { "3B8880014431314352322E3070", "javacard.pro card (JavaCard)\nhttps://javacard.pro/" }, + { "3B8880014553434F5331303071", "Legic Prime and Legic Advant (Other)\nhttps://www.legic.com/de/products/smartcards/legic-smartcard-ics" }, { "3B88800146494445534D4F3167", "Fidesmo card (install or uninstall JavaCard applets or Mifare-based services on the field, using the Fidesmo Android App. (JavaCard))\nhttps://developer.fidesmo.com/" }, + { "3B8880014A434F50424D423260", "emoney (Bank)" }, { "3B8880014A434F50763234315E", "RFID - ISO 14443 Type A - NXP JCOP\nNXP J3A081 JavaCard (contactless interface)" }, { "3B88800150FFFF117783D50069", "Gematik TSYS eHBA G2.1 (HealthCare)" }, { "3B88800152744D430081C10061", "Rutoken 2151 smart card (eID)\nhttps://www.rutoken.ru/products/catalogue/id_50.html" }, @@ -1923,10 +1974,12 @@ const static atr_t AtrTable[] = { { "3B888001534B55500100000015", "Silesian public services card (Transport)\nhttp://www.kartaskup.pl" }, { "3B888001534C4A01305023100E", "bunq Credit MasterCard (contactless interface) (Bank)" }, { "3B888001536D61727441707011", "national Lithuania ID card (eID)\nhttps://www.dokumentai.lt/viewpage.php?page_id=77" }, + { "3B888001595A52434F53030004", "Waveshare 4.2inch Passive NFC-Powered e-Paper 400x300 Screen (Other)\nhttps://www.amazon.com/dp/B0CNR5KGZN?ref=ppx_yo2ov_dt_b_fed_asin_title&th=1" }, { "3B8880017661756C746974315F", "ID06 2.0 (eID)" }, { "3B8880017661756C746974325C", "Vaultit Identity Card (eID)" }, { "3B8880018066B007010107....", "Gemalto Santander Optelio TUI R7 with WG10 using Contactless interface" }, { "3B8880018073C8211052B81DF4", "Arculus AuthentiKey (via NFC) (Other)" }, + { "3B8880018921A0012024071615", "Token2 T2F2-NFC-Card PIN+ Release3 (via NFC) (Other)\nhttps://www.token2.com/shop/product/t2f2-nfc-card-pin-release3" }, { "3B888001990200D10304220167", "Debit card (Bank)" }, { "3B888001C91207520200811014", "electronic Tickes from the german Transport Association VGN (Verkehrsgemeinschaft Niederrhein)" }, { "3B888001D10386050080800058", "Resident Identity Card of People Republic of China (Second Generation with RF Feature) (eID)\nhttp://www.gov.cn/banshi/2005-08/02/content_19457.htm" }, @@ -1952,6 +2005,7 @@ const static atr_t AtrTable[] = { { "3B888001E1F35E11B381A500C3", "Australian Passport (passport)\nhttps://www.passports.gov.au/" }, { "3B888001E1F35E1377830000A2", "ePerso - German ID card (issued 2013)" }, { "3B888001E1F35E137783D50077", "ePerso - German ID card (issued 2011)" }, + { "3B888001FD01102020103000C5", "Changsha Xiaoxiang TU Transport Card issued by China Mobile (Transport)\nhttps://www.xxka.com/" }, { "3B88813120550057696E4361726429", "SmartCard for Windows 1.0" }, { "3B888EFE532A031E049280004132360111E4", "German C-Netz SIM card / TeleKarte for mobile phones or phone boxes - 1990s (Telecommunication)\nhttp://download.eversberg.eu/mobilfunk/C-Netz-Dokus/" }, { "3B890056434152445F4E5353", "Coolkey emulated card using virtual viewer with nssdb (eID)" }, @@ -1965,6 +2019,7 @@ const static atr_t AtrTable[] = { { "3B8980013233324353435333363E", "CSCS Smartcard (passport)\nhttp://getgosmart.io" }, { "3B89800141434F534A763130311A", "ACS ACOSJ (Combi) (JavaCard)\nhttp://www.acs.com.hk/en/products/405/acosj-java-card-combi/" }, { "3B89800141434F534A763230351D", "ACOSJ dual interface Java card 95K (JavaCard)" }, + { "3B89800143323330302D4B455930", "HID(r) Crescendo(r) Key (PKI)\nhttps://www.hidglobal.com/documents/hid-crescendo-key-datasheet" }, { "3B8980014341524441474146435E", "Student ID of University Duisburg-Essen (Other)\nhttps://www.uni-due.de/studierendensekretariat/studierendenausweis.shtml" }, { "3B898001434C616972653220363C", "Fidor Smartcard Mastercard/Maestro (Bank)\nhttps://www.fidor.eu" }, { "3B898001434C616972653320363D", "MasterCard with PayPass issued by Commerbank AG (Germany) (Bank)\nhttps://www.commerzbank.de/portal/de/privatkunden/produkte/bezahlen/kreditkarten/mastercard-classic/mastercardclassic.html" }, @@ -1994,6 +2049,8 @@ const static atr_t AtrTable[] = { { "3B8980018064160402828290006C", "Maestro card (from Mastercard) used by BCGE (switzerland) bank (Bank)" }, { "3B89800180642004018382900058", "Raiffeisen Debit Card / Master (Bank)" }, { "3B89800180670412B0030501024C", "Austrian Passport" }, + { "3B8980018684105430343131317B", "e-CNY hard wallet issued by Bank of China & Meituan (Bank)\nhttps://www.mpaypass.com.cn/news/202209/01101324.html" }, + { "3B89800186885550504849434B5A", "UnionPay Debit card issued by Industrial and Commercial Bank of China (Contactless) (Bank)\nhttps://www.icbc.com.cn/" }, { "3B8A0091010016000116010096", "GSM-SIM T-Mobil D1 (900MHz)" }, { "3B8A0091010016000120010096", "GSM-SIM T-D1 prepaid (Xtra)" }, { "3B8A0091010016000120020096", "GSM-SIM (900MHz) card of the carrier t-mobile for their cellular\nnetwork (phase 2+ with 3V)" }, @@ -2018,6 +2075,7 @@ const static atr_t AtrTable[] = { { "3B8A80014A434F503331563233327A", "Snapper New Zealand (JCOP)" }, { "3B8A80014A434F503431563232317F", "JCOP41 Cards (not supported, but recognized by Classic Client)\nNXP JCOP 41 v2.2.1 72k RFID I/F" }, { "3B8A80014D540005002086640001D4", "Beijing Municipal Administration & Communication Card (Transport)\nhttps://www.bmac.com.cn/" }, + { "3B8A80014D54000500308693695B00", "Beijing Municipal Administration & Communication Card (Transport)\nhttps://www.bmac.com.cn/" }, { "3B8A800150564A434F5033454D5676", "NXP JCOP3 J3H082 Java Card 3.0.4 Dual-Interface (JavaCard) (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop3-j3h082-java-card-3-0-4-j3h081-dual-interface/" }, { "3B8A800150564A434F503453494471", "J3R180 via ifdnfc (JavaCard)" }, { "3B8A80015345204445534669726557", "NXP-Mifare DESFire EV1 2k (used as a company ID card) (eID)" }, @@ -2052,6 +2110,7 @@ const static atr_t AtrTable[] = { { "3B8B80010031C1640840223000900054", "IDEMIA Cosmo v8.1-n (Other)" }, { "3B8B80010031C1640860320600900052", "Banco CTT (Portugal) contactless VISA Debit card (Bank) (Bank)\nhttps://www.bancoctt.pt/home/abrir-conta.html" }, { "3B8B80010031C1640860321200900046", "AMEX Silver Credit (Bank)" }, + { "3B8B80010031C1640860321A0090004E", "UnionPay Debit card issued by HSBC CN (Contactless) (Bank)\nhttps://www.hsbc.com.cn/debit-cards/" }, { "3B8B80010031C1640860321F0090004B", "Hanseatic Bank Visa Card (Bank)\nhttps://www.hanseaticbank.de/kreditkarte/genialcard" }, { "3B8B80010031C1640860322000900074", "IDEMIA (Other)" }, { "3B8B80010031C1640860324200900016", "Westpac Handybank EFTPOS/ATM Card - Contactless (Bank)" }, @@ -2062,14 +2121,18 @@ const static atr_t AtrTable[] = { { "3B8B80010031C1640924331E0090000E", "Cumulus Mastercard (Bank)\nhttps://www.migros.ch/cumulus/mastercard" }, { "3B8B80010031C164092914540090006E", "Italian State National Card (eID)\nhttps://www.inps.it/it/it/assistenza/cns---carta-nazionale-dei-servizi.html" }, { "3B8B80010031C1640937721300900051", "French ID Card 2021 (contactless interface) (eID)\nhttps://ants.gouv.fr/Les-titres/Carte-nationale-d-identite/La-puce-de-la-nouvelle-carte-nationale-d-identite" }, + { "3B8B80010031C1640946012A0090006A", "Zwipe Access badge with HID Seos applet (eID)\nhttps://www.zwipe.com/access" }, { "3B8B80010031C1640958213600900048", "American Express Corporate Card (Bank)\nhttps://www.americanexpress.com/es-es/negocios/corporativa/tarjetas/corporate-card/" }, { "3B8B80010031C1640964413600900014", "Monzo (Bank)\nhttps://monzo.com/" }, { "3B8B80010031C164097861310090002F", "BBVA credit card (Bank)\nhttps://www.bbva.es/personas/productos/tarjetas/credito.html" }, { "3B8B800100640411010131800090005A", "German Passport (issued Nov 2006)\nUnited Kingdom e-Passport\nLuxembourg passport (2007)" }, { "3B8B80010B7880820244492030324D1B", "Mastercard Debit issued by Raiffeisen bank in Czech Republic" }, + { "3B8B800120555F4A434152445F532052", "Universal JCard S (C-UJC080-PCG-101) (JavaCard)\nhttps://www.usmartcards.co.uk/universal-j-cards" }, + { "3B8B800120700100000000071C615170", "Residence Permit for Residents of Liaoning, China (eID)\nhttps://www.gov.cn/xinwen/2018-04/02/content_5279248.htm" }, { "3B8B80012085008B030FE09AA0E04052", "Shanghai Public Transportation Card (Transport)\nhttp://www.sptcc.com/" }, { "3B8B80012090000000000008EE277F04", "Hungarian eID (2024) (eID)\nhttps://eszemelyi.hu/en/" }, { "3B8B8001209000000000001614CDAADF", "JCOP 41 IBM card for Guizhou Normal University (JavaCard)" }, + { "3B8B800120900000000000995D583016", "Northeastern University (China) student ID card (Other)\nhttp://ecard.neu.edu.cn/" }, { "3B8B800120900000000000C3A4CC0918", "ID card (Other)" }, { "3B8B80012A26A7A10C804100000709C3", "T-Mobilitat (Transport)\nhttps://t-mobilitat.cat/" }, { "3B8B80012A26A7A10C804100019AB1E7", "T-Mobilitat (Transport)\nhttps://t-mobilitat.atm.cat/" }, @@ -2086,7 +2149,9 @@ const static atr_t AtrTable[] = { { "3B8B80018066475000B8007F8290002E", "Italian Card Identity (eID)\nhttps://www.cartaidentita.interno.gov.it/" }, { "3B8B80018066475000B80094829000C5", "Italian Electronic Contactless Identity Card v. 3.0 (CIE 3.0) (eID)\nhttps://www.cartaidentita.interno.gov.it/" }, { "3B8B800180F9A00000030800001000C8", "Probably the same as 'JCOP3 SecID P60 CS (JavaCard)' but mated with a contactless Identiv reader (JavaCard)" }, + { "3B8B8001868602563390314F8BB98F3E", "Macau Pass (Transport)\nhttps://www.macaupass.com/" }, { "3B8B80018688FF6F391E743C200800D3", "Chinese ICBC (bank)" }, + { "3B8B80018688FF8511BBBD2120080060", "UnionPay Debit card issued by China Minsheng Bank (Contactless) (Bank)\nhttps://www.cmbc.com.cn/" }, { "3B8B81314034534D41525453434F5045316D", "Zeeland kaart (Telecommunication)" }, { "3B8C014D79536D6172744C6F676F6EA5", "EIDVirtual (USB key emulated as a virtual smart card) (PKI)\nhttp://www.mysmartlogon.com/eidvirtual/" }, { "3B8C01805A4E6974726F6B657920337D", "'Nitrokey 3C NFC' USB authentication and security token (Other)\nhttps://shop.nitrokey.com/shop/product/nk3cn-nitrokey-3c-nfc-148" }, @@ -2152,6 +2217,7 @@ const static atr_t AtrTable[] = { { "3B8C800150710CF3C800000000B37171A8", "MOBIB CARD BELGIUM (Transport)" }, { "3B8C800150773B2DBD0000001100818594", "Texas Instruments Dynamic NFC Interface Transponder (RF430CL330H)" }, { "3B8C800150784B2CCB00000000B371713A", "... (Transport)\nhttps://www.portalviva.pt/" }, + { "3B8C800150787182D400000000118171E3", "BUS Synchro in Grand Chambery (Transport)\nhttps://synchro.grandchambery.fr" }, { "3B8C80015078F87217E1F35E117781A5B6", "US (passport)" }, { "3B8C8001507919600100DDA611F7718535", "Transport Traway Montpellier France (Transport)" }, { "3B8C8001507AA44007231801007781979F", "Indonesian Driver License" }, @@ -2187,7 +2253,9 @@ const static atr_t AtrTable[] = { { "3B8C800150C2B936860000000000717196", "Passe Navigo (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-decouverte" }, { "3B8C800150C326AF0C000000000071711B", "Navigo Mobility Paris transport's Card (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/liste?d=forfaits" }, { "3B8C800150C41466C30000000000717128", "Navigo easy (Transport)\nhttps://www.iledefrance-mobilites.fr/titres-et-tarifs/supports/passe-navigo-easy" }, + { "3B8C800150C464D71200000000118171D9", "Carte Opus (Montreal) (Transport)\nhttps://www.carteopus.info/" }, { "3B8C800150C51770F0000000008081717F", "SumUp Limited Apple Pay Virtual Card (Bank)\nhttps://sumup.co.uk" }, + { "3B8C800150C5FD630D00000000008171FB", "Navigo Easy (Transport)\nhttps://www.iledefrance-mobilites.fr/en/tickets-fares/media/navigo-easy-travel-card" }, { "3B8C800150C7251C5A00000011F781811F", "MOBIB Belgian public transport Card (Transport)\nhttps://mobib.be/fr.html" }, { "3B8C800150CA24513E00000011F781813A", "belgian mobib transportation card (Transport)" }, { "3B8C800150CBFB077E000000008081E1F4", "Samsung Digital Center in Seul Access ID card for guests (eID)" }, @@ -2195,6 +2263,7 @@ const static atr_t AtrTable[] = { { "3B8C800150D94E7D0000000000008180B6", "Chicago CTA Ventra Transit card (Transport)\nhttps://www.ventrachicago.com/" }, { "3B8C800150DF4C852C0000000000817791", "SNS Bank Netherlands (Bank)" }, { "3B8C800150E2F8282E0000000000818747", "ING Maestro Debit Card (Bank)\nhttps://www.ing.nl/particulier/index.html" }, + { "3B8C800150EA5A3F1930AAAA017781D7DB", "Indonesia's Official Citizenship/Identity Card (e-KTP) (eID)\nhttps://en.wikipedia.org/wiki/Indonesian_identity_card" }, { "3B8C800150EB5E6A120000000000817766", "Maestro card (Bank)" }, { "3B8C800150F2AD19D7000000000081773A", "ING Maestro Debit Card (Bank)\nhttps://www.ing.nl/particulier/index.html" }, { "3B8C800150F56FF82BE1F35E113381C73C", "ID of the Republic of Kazakhstan Ministry of Internal Affairs of the Republic of Kazakhstan (eID)" }, @@ -2230,6 +2299,7 @@ const static atr_t AtrTable[] = { { "3B8D80010073C80013645437443300900044", "master cRD (Bank)\nTD MasterCard\nTangerine MasterCard" }, { "3B8D80010073C80013645437473100900045", "Tangerine ATM/ABM Card" }, { "3B8D800100851410108501112002759000CF", "Sahl Payment Card (Other)\nhttps://www.english.sahlpay.app/" }, + { "3B8D8001008C430101868821014015D85636", "UnionPay Debit card issued by Industrial and Commercial Bank of China (Contactless) (Bank)\nhttps://www.icbc.com.cn/" }, { "3B8D80010D788084020073C840130090FFF8", "Nokia 6212 phone seen as NFC device" }, { "3B8D80010D78F7B1024A434F50763234316A", "Electronic Identity For Students at university (eID)" }, { "3B8D80014946582D6A63333031677032325A", "Degussa Bank Corporate MasterCard (Bank)\nhttps://firmenkarten.degussa-bank.de/" }, @@ -2245,6 +2315,7 @@ const static atr_t AtrTable[] = { { "3B8D80018091E165D0005B010273D44140B7", "Portuguese Passport (passport)\nhttp://www.pep.pt/PagesPT/Caracteristicas.aspx" }, { "3B8D80018091E165D0005B010373D44140B6", "French passport (2010-2011)" }, { "3B8E0100738020C006534C434F530590004F", "idex (JavaCard)" }, + { "3B8E80010000814D22088660300020E00007F8", "Mastercard Debit card issued by Bank of China (Contactless) (Bank)\nhttps://www.boc.cn/" }, { "3B8E8001005100631F6D01739F20C0C0900012", "Identity card (eID) Republic of Latvia (eID)\nhttps://www.pmlp.gov.lv/en/home/services/personal-certificates-%28eid%29/" }, { "3B8E8001005131631F5901739F20C0C0900017", "Identity card (eID) Republic of Latvia (eID)\nhttp://www.pmlp.gov.lv/en/home/services/personal-certificates-%28eid%29/\nFrench driving license (eID) (contactless)" }, { "3B8E80010E7833C4020064041501020090FF95", "Spanish Passport" }, @@ -2255,9 +2326,11 @@ const static atr_t AtrTable[] = { { "3B8E8001137880800246494F4D4B5F3030314E", "MasterCard/PayPass Card issued by Czech FIO Banka a.s. (contactless chip)\nnote the ASCII string 'FIOK_001N' embedded in ATR" }, { "3B8E8001410543000000000000000000900098", "ACOS5-EVO PKI Smart Card (Combi) (PKI)\nhttps://www.acs.com.hk/en/products/471/acos5-evo-pki-smart-card/" }, { "3B8E800152464944494F74204A434F5038305A", "RFIDIOt G&D SmartCafe 80K" }, + { "3B8E800153434520382E302D433256320D0A63", "ePassport (passport)\nhttps://www.veridos.com/en/" }, { "3B8E800153434520382E302D433356300D0A60", "Latvian passport (2024) (passport)" }, { "3B8E800153434536302D43443038312D6E464A", "SmartCafe Expert 6.0 80K Dual (JavaCard)\nhttp://www.smartcardfocus.com/shop/ilp/id~684/smartcafe-expert-6-0-80k-dual-/p/index.shtml" }, { "3B8E8001544A4F50323144303431563232311B", "China Construction Bank Lutong Card Type A (Credit) with ETC (Bank)\nhttp://ccb.com/sd/cn/fhgg/20200122_1579670876.html" }, + { "3B8E80015644562D4B41204944333720464D6E", "'VBB-fahrCard' time and season tickets on public transports in Berlin and Brandenburg - eTicket (Transport)\nhttps://www.vbb.de/tickets/tarifinformationen-services/vbb-fahrcard/" }, { "3B8E80015644562D4B412049443337204A341B", "eTicket (Transport)\nhttps://www.eticket-deutschland.de/" }, { "3B8E80018031806549544E5850120FFF8290F0", "Italian Electronic Contactless Identity Card v. 3.0 (CIE 3.0) - ICAO 9303 Compliant (eID)\nhttps://www.cartaidentita.interno.gov.it/caratteristiche-del-documento/" }, { "3B8E80018031806549544E5850120FFFFFFFE2", "Italian identity card, 2nd version (eID)\nhttps://www.cartaidentita.interno.gov.it/" }, @@ -2294,9 +2367,12 @@ const static atr_t AtrTable[] = { { "3B8F80010031C173C8211064414D333107900089", "NAB VISA Debit (Bank)\nhttps://www.nab.com.au/" }, { "3B8F80010031C173C8211064414D3341079000F9", "Ukrainian International resere bank (ex Sberbak RF) debit card (Bank)\nhttps://www.sbrf.com.ua/" }, { "3B8F80010031C173C8211064414D3348079000F0", "Consorsbank VISA debit card (Bank)" }, + { "3B8F80010031C173C8211064474D31360090008D", "Mastercard Debit card issued by China Construction Bank (Contactless) (Bank)\nhttps://www.ccb.com/" }, + { "3B8F80010031C173C8211064474D3342009000FB", "Visa Debit card issued by Bank of China (Contactless) (Bank)\nhttps://www.boc.cn/" }, { "3B8F80010031C173C8211064474D34350090008B", "S-Etukortti Visa (Bank)\nhttps://www.s-pankki.fi/fi/s-etukortti-visa" }, { "3B8F80010031C173C8211064474D343700900089", "SpareBank Visa Card, Norway (Bank)" }, { "3B8F80010031C173C82110644D30424E079000F6", "Debit Card (Deutsche Bank Card Plus) (Bank)\nhttps://www.deutsche-bank.de/pk/konto-und-karte/karten-im-ueberblick/deutsche-bank-card-plus.html" }, + { "3B8F80010031C173C82110645630434E079000EC", "Amazon VISA Card (Bank)" }, { "3B8F80010031C173C82110645631434E079000ED", "Tarjeta Despues BBVA Espana (contact interface) (Bank)\nhttps://www.bbva.es/personas/productos/tarjetas/tarjeta-despues.html" }, { "3B8F80010031C173C8211064574B31330090009E", "TIM Pay - HYPE (Mastercard debit card) (Bank)\nhttps://www.tim.it/fisso-e-mobile/mobile/servizi/tim-pay" }, { "3B8F80010031C173C8211064574B313400900099", "G&D Sm@rtCafe Card embedded in a wristband (Bank)\nhttps://www.gi-de.com/en/au/mobile-security/industries/financial-institutions/wearables/" }, @@ -2308,6 +2384,7 @@ const static atr_t AtrTable[] = { { "3B8F800101654E434F532D4D43412D434C30373C", "eNCOS + MCA, MchipAdvance 123 bundled with eNCOS (Bank)" }, { "3B8F80013101F1564011001900000000000000D1", "Revolut Mastercard (Bank)" }, { "3B8F80013101F1564011001900000000FFFFFF2E", "BVG Guthabenkarte (Prepaid Payment Card for Berlin/Brandenburg Public Transport) (Transport)\nhttps://www.bvg.de/de/service-und-kontakt/guthabenkarte" }, + { "3B8F80013101F1564011001D00000000FFFFFF2A", "n26 (Bank)" }, { "3B8F800141434F53204449616E6131204C63365B", "DKB-VISA-Card (Bank)\nhttps://produkte.dkb.de/?&page=girokonto#kreditkarte" }, { "3B8F800141434F53204449616E613120FFFFFFBD", "DKB Visa Credit Card (Bank)\nhttps://www.dkb.de" }, { "3B8F800141434F53204449616E6132204C633658", "comdirect Visa debit (Bank)\nhttps://www.comdirect.de" }, @@ -2318,10 +2395,14 @@ const static atr_t AtrTable[] = { { "3B8F800143553269AA20202020202020202020E9", "UBS Access Card (Mobile Online Banking, NFC, Switzerland)" }, { "3B8F800145504100000000........00........", "Austrian Quick E-purse contactless\nhttp://www.quick.at/" }, { "3B8F80014A434F50332041545320434841525326", "Samsung Galaxy Watch Active NFC (Other)" }, + { "3B8F80014D5400020100FFFF0114013422071100", "HK Octopus - China T-Union Card (Felica/ISO-14443A combo 14443A section) (Transport)\nhttps://www.octopus.com.hk/en/consumer/octopus-cards/products/cross-border/china-t-union.html" }, { "3B8F800152464944494F74204A434F5020333676", "RFIDIOt JCOP 36K Blank\nhttp://rfidiot.org" }, { "3B8F800152464944494F74204A434F5020373276", "RFIDIOt JCOP 72K Blank\nhttp://rfidiot.org" }, { "3B8F800152464944494F74204A434F5037327224", "RFIDIOt JCOP 72K RANDOM_UID Blank\nhttp://rfidiot.org" }, { "3B8F800156696E5061795379732050757273652F", "JCOP (Other)" }, + { "3B8F800157694C4C570000000000002019032875", "Mastercard Debit card issued by Industrial and Commercial Bank of China (Contactless) (Bank)\nhttps://www.icbc.com.cn/" }, + { "3B8F800157694C4C57000000000000202004294A", "Amex China bank card (Contactless) (Bank)\nhttps://www.americanexpress.com.cn/" }, + { "3B8F800157694C4C570000000000002020043053", "Mastercard China Debit card issued by Postal Savings Bank of China (Contactless) (Bank)\nhttps://www.psbc.com/" }, { "3B8F80018031806549544A3441120FFF82900088", "CIE - Carta di Identita Elettronica - Italian ID Card (eID)\nhttps://www.cartaidentita.interno.gov.it" }, { "3B8F80018031806549544A3442120FFF829000", "Italian Identity Card CIE (eID) (eID)\nhttps://www.cartaidentita.interno.gov.it/en/home/" }, { "3B8F80018031806549544A3442120FFF8290008B", "Identity Card (eID)" }, @@ -2468,6 +2549,7 @@ const static atr_t AtrTable[] = { { "3B8F80018091E1319865B0850300EF739441C08D", "Italian Passport (passport)\nhttps://www.passaportonline.poliziadistato.it/" }, { "3B8F80018091E1319865B0850300EF739441FFB2", "Italian Passport (passport)" }, { "3B8F80018091E131D865B28C01000773C441E05C", "Portuguese epassport (passport)" }, + { "3B8F80018091E131D865B28C01002673C441E07D", "ePassport chip of South Korea (passport)" }, { "3B8F800186384D574F4850CF1E4D2B1211291070", "Hangzhou Tong (Hangzhou Public Transport Card) (Transport)\nhttps://www.96225.com/smknet/service/show_allGet.action?chanageCrd=3" }, { "3B8F800186844354444E409F3EC40D682804007D", "Access card to the underground network of guangzhou (Transport)" }, { "3B8F8001FF43727970746E6F784649444F3230C2", "Fast Identification Online card (FIDO2) from Cryptnox manufacturer (Other)\nhttps://www.cryptnox.ch" }, @@ -2589,14 +2671,17 @@ const static atr_t AtrTable[] = { { "3B9C95801FC78031E073FE211B6457444946CF", "MTC (Moscow) phone SIM card (Telecommunication)\nhttps://moskva.mts.ru/personal" }, { "3B9C9580811F039067464A01005404F272FE00C0", "Feitian Technologies Java Card A22CR (JavaCard)\nhttps://www.javacardos.com/store/javacard-a22cr.php" }, { "3B9C9580811F039067464A01011706F2727E0000", "A40CR (JavaCard)" }, + { "3B9C958101505343502D53435356312E308E", "SSE Carte a puce Inc. : PIV - GIDS - FIDO2.1 - OPENPGP 2.1 Identity Services for Corporation (eID)\nhttps://www.smartcardsecurity.ca" }, { "3B9C958131FE9F9067464A010253050172FE00FB", "Feitian Biopass K27 (PKI)\nhttps://www.ftsafe.com/Products/FIDO/Bio" }, { "3B9C958131FE9F9067464A010400050172FE00AE", "ePass FIDO-NFC Plus (eID)\nhttps://www.ftsafe.com/products/FIDO/NFC" }, { "3B9C96005275746F6B656E4543507363", "Aktiv Rutoken ECP SC T0\nhttps://www.rutoken.ru/products/all/rutoken-ecp-sc/" }, { "3B9C960058442403020020010A009005", "Ticket Restaurant Card (Other)\nhttp://www.edenred.it/buoni-pasto-welfare-benefit/ticket-restaurant-card/" }, { "3B9C978011405275746F6B656E4543507363C0", "Aktiv Rutoken ECP 3.0 NFC (PKI)\nhttps://www.rutoken.ru/products/all/rutoken-ecp-nfc/" }, { "3B9D114023006810114D696F434F53009000", "MioCOS 1.0" }, - { "3B9D13813160378031C0694D54434F537302020440", "DPI Card Guatemala (eID)\nhttp://www.masktech.de/" }, + { "3B9D13813160378031C0694D54434F537302020440", "DPI Card Guatemala (eID)\nhttp://www.masktech.de/\nSerbian ID (eID)" }, { "3B9D13813160378031C0694D54434F537302020541", "MTCOS (eID)\nhttp://www.masktech.com/Products/MTCOS-Professional/11/en" }, + { "3B9D13813160378031C0694D54434F537302050447", "Serbian vehicle card (Other)" }, + { "3B9D188131FC358031C0694D54434F5373020502D4", "Serbian vehicle card (Other)" }, { "3B9D188131FC358031C0694D54434F5373020505D3", "Lithuanian e-ID Card (eID)\nhttps://www.nsc.vrm.lt/default_en.htm" }, { "3B9D188131FC358031C0694D54434F5373020604D1", "Lithuanian ID card (MaskTech, 2024) (eID)" }, { "3B9D944023006820014D696F434F53009000", "Miotec smartcard running Miocos 2.0 on an Atmel AT90SC646\nhttp://www.miotec.fi" }, @@ -2612,6 +2697,7 @@ const static atr_t AtrTable[] = { { "3B9D95801FC780731A211B63AF09A9830F9000F3", "Estonian GSM operator TELE2 (WPKI eID support)" }, { "3B9D95803FC7A08031A073BE21135105830590007C", "NTT docomo Xi(LTE) DN05(DNP) Pink SIM (Telecommunication)" }, { "3B9D960053492303030020000400F59000", "shell (Transport)" }, + { "3B9D9600534D4152545041524B20464B43", "T token (Russia) (Other)" }, { "3B9D96801FC78031E073FE2113654C0404020096", "China umion 5G SIM (Telecommunication)" }, { "3B9D96803FC7A08031E073FE211B65534130112546", "KT MVNO Baro USIM (Telecommunication)\nhttps://ktmyr.com/fe/service/svc/usimGuide.do?menuNo=F0604" }, { "3B9D96813160378031C0694D54434F5373020204C5", "Mozambique ID Card (eID)" }, @@ -2640,6 +2726,7 @@ const static atr_t AtrTable[] = { { "3B9E95801FC38031E073FE211B66D000490000004E", "UK O2 Unlimited Prepay GSM/UMTS USIM" }, { "3B9E95801FC3804B434F5320566572012E0190001C", "Vehicular License Veracruz, Mexico (Transport)" }, { "3B9E95801FC68031E073FE211B66D0019FBD100031", "H3G (Three UK) Prepaid USIM (Telecommunication)" }, + { "3B9E95801FC68031E073FE211B66D0025E73150038", "albert heijn prepaid SIM netherlands (Telecommunication)" }, { "3B9E95801FC78031E073FE211B66D00007001E001A", "H3G (Ireland, UK) UMTS USIM card" }, { "3B9E95801FC78031E073FE211B66D000261C010038", "GSM-SIM Telefonica Movistar, contract (Spain)\nhttp://www.movistar.es/" }, { "3B9E95801FC78031E073FE211B66D00028C40000EF", "H3G (Italy) UMTS USIM card" }, @@ -2686,6 +2773,8 @@ const static atr_t AtrTable[] = { { "3B9E96800141054300000000000000000090001E", "ACS CryptoMate EVO PKI token (PKI)\nhttps://www.acs.com.hk/en/products/494/cryptomate-evo-cryptographic-usb-tokens/" }, { "3B9E96801F478031A073BE211366868802101D1049", "Maroc Telecom USIM" }, { "3B9E96801F838031E073FE21126655574E41323391", "TDC mobile UICC (Telecommunication)" }, + { "3B9E96801F878031E073FE2119664A5543140110CB", "SIM card for 5G exploration (Telecommunication)" }, + { "3B9E96801F878031E073FE2119664A556021AA1670", "telkomsel (Telecommunication)" }, { "3B9E96801FC38031E073FE211B66D0016C040D0060", "toggle GSM SIM\nhttp://www.togglemobile.co.uk" }, { "3B9E96801FC38031E073FE211B66D0017B980D00EB", "Lycamobile Pay As You Go SIM" }, { "3B9E96801FC68031E073FE211B66D0019F134D00C1", "Telus Mobility Tri-SIM card (Telecommunication)\nhttps://www.telus.com/en/mobility/sim-cards" }, @@ -2728,6 +2817,7 @@ const static atr_t AtrTable[] = { { "3B9E96801FC78031E073FE211B66D00199C80F005F", "OneSimCard (Telecommunication)\nhttp://www.onesimcard.com/" }, { "3B9E96801FC78031E073FE211B66D00199FE0E0068", "Finnish Sonera SIM-card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A0041100B4", "Aldi Talk SIM card, Germany (Telecommunication)\nhttps://www.alditalk.de/talk" }, + { "3B9E96801FC78031E073FE211B66D001A00F1100BF", "Vodafone UK SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A01E1100AE", "Zevvle SIM Card (Telecommunication)\nhttps://zevvle.com" }, { "3B9E96801FC78031E073FE211B66D001A0741000C5", "USIM of Vodafone Germany (MCC 262, MNC 2) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A0DD12006E", "USIM (Telecommunication)" }, @@ -2735,27 +2825,32 @@ const static atr_t AtrTable[] = { { "3B9E96801FC78031E073FE211B66D001A1121000A2", "OpenAirInerface (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1181100A9", "WinEMP NRI License Card (Other)" }, { "3B9E96801FC78031E073FE211B66D001A122110093", "Lebara (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D001A1421000F2", "SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1581100E9", "Vodafone CZ: SIMPLUS V128 LTE (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1680F00C7", "Free Mobile SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1721000C2", "Vodafone (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D001A1731000C3", "Prepaid public telephone card from Lidl Connect, Germany (Telecommunication)\nhttps://www.lidl.de/de/lidl-connect/s7373597" }, { "3B9E96801FC78031E073FE211B66D001A1771000C7", "SIM Card Model X1 for Ting Mobile Carrier (Telecommunication)\nhttps://tingmobile.com/" }, { "3B9E96801FC78031E073FE211B66D001A1A70F0008", "Spanish Movistar Mobile phone SIM card (Telecommunication)\nhttp://www.movistar.es/" }, + { "3B9E96801FC78031E073FE211B66D001A1B8100008", "Bancorp (Bank)" }, { "3B9E96801FC78031E073FE211B66D001A1E60F0049", "SIM O2 CZ (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D002170912000E", "SIM/USIM (SPAIN) - ORANGE ESPAGNE VIRTUAL, S.A.U. (Orange / Jazztel / SIMYO) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0021759140058", "USIM card of of Ukrainian Telecommunications Operator Kyivstar, emitted after 2016 (Kyivstar GSM) (GSM/UMTS/LTE services) (Telecommunication) (Telecommunication)\nhttps://kyivstar.ua/uk/4g" }, { "3B9E96801FC78031E073FE211B66D002175B12005C", "Cellcom Israel USIM (micro FF) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0021760130066", "Vodafone spain barcelona (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0021762120065", "Free Mobile (Telecommunication)\nhttp://mobile.free.fr" }, + { "3B9E96801FC78031E073FE211B66D0021762130064", "SIM Card (possibly Gemalto/Thales) Telecommunication" }, { "3B9E96801FC78031E073FE211B66D0021795120092", "Twilio SIM (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D00217A01200A7", "USIM Card (Etisalat) (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D00217BF1100BB", "Telkom (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D00217C41100C0", "STC Telecommunication" }, { "3B9E96801FC78031E073FE211B66D00217C91100CD", "Vivo 4G (Other)" }, { "3B9E96801FC78031E073FE211B66D00217CD1100C9", "O2 4G SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D00217D91200DE", "telenor (swedish mobile provider) SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D00217F41200F3", "SIM card for Swedish operator Vimla! (Telecommunication)\nhttps://www.vimla.se" }, { "3B9E96801FC78031E073FE211B66D002194B120042", "halebop (swedish mobile provider) SIM card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022436140004", "SIM (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D00224B3140081", "Drei AT Prepaid SIM Card (Telecommunication)\nhttps://www.drei.at/de/shop/wertkarte/" }, { "3B9E96801FC78031E073FE211B66D00224DE1400EC", "Romanian Vodafone SIM Card (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022A3215000F", "Bell 5G/LTE multi SIM card, Canada (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022A6D130056", "USIM card (JavaCard)" }, @@ -2767,10 +2862,13 @@ const static atr_t AtrTable[] = { { "3B9E96801FC78031E073FE211B66D0022AB714008B", "AT&T Prepaid UICC (Telecommunication)" }, { "3B9E96801FC78031E073FE211B66D0022AD31300E8", "sim card from Mobilcom Debitel Telefonica (Telecommunication)\nhttps://md.de" }, { "3B9E96801FC78031E073FE211B66D0022AD91300E2", "sim (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D0022ADA1300E1", "Zevvle SIM Card (Telecommunication) Telecommunication\nhttps://zevvle.com" }, { "3B9E96801FC78031E073FE211B66D0022AE81300D3", "Twilio Super SIM card (Telecommunication)\nhttps://www.twilio.com/iot/super-sim-card" }, { "3B9E96801FC78031E073FE211B66D0022AF21400CE", "Tello (Telecommunication)\nhttps://tello.com/" }, { "3B9E96801FC78031E073FE211B66D00233AD140088", "4G-LTE (Telecommunication)" }, + { "3B9E96801FC78031E073FE211B66D002390D150023", "Orange Belgium Hello (Telecommunication)" }, { "3B9E968031FE4553434520382E302D433156300D0A6F", "Serbian Identity Card (eID) (eID)" }, + { "3B9E968031FE4553434520382E302D433256300D0A6C", "Serbian Identity Card (eID) (eID)" }, { "3B9E96803F47A08031E073EE211366868842185221EF", "SIM card (Telecommunication)" }, { "3B9E96803FC3A08031E073FE211B630801140F9000D3", "KT Olleh LTE Warp SA-L 1670 (Telecommunication)" }, { "3B9E97801FC68031E073FE211B66D0019F7A1200F6", "Spectrun USA Sim Card (Telecommunication)" }, @@ -2782,9 +2880,11 @@ const static atr_t AtrTable[] = { { "3B9E97801FC68031E073FE211B66D002402F150078", "Free Mobile (French carrier) SIM card (Telecommunication)\nhttps://mobile.free.fr/" }, { "3B9E97801FC68031E073FE211B66D0025E7315003A", "Twilio Super SIM (Telecommunication)\nhttps://www.twilio.com/iot/super-sim-card" }, { "3B9E97801FC68031E073FE211B66D0025E901500D9", "SMARTY (a subsidiary of Three UK) 4G SIM Card (Telecommunication)" }, + { "3B9E97801FC68031E073FE211B66D0025EAE1500E7", "kiwisim (Telecommunication)" }, { "3B9E97801FC68031E073FE211B66D0025EC0150089", "Orange France Postpaid SIM Card (Telecommunication)" }, { "3B9E97801FC78031E073FE211B66D0006B951100EE", "TracFone SIM Verizon Wireless LTE supported (Telecommunication)\nhttps://www.tracfone.com" }, { "3B9E97801FC78031E073FE211B66D0022AB3130089", "SIM T-MObile (Telecommunication)" }, + { "3B9E978031FE4553434520382E302D433156300D0A6E", "Serbian Health Care card (HealthCare)\nhttps://www.rfzo.rs/index.php/osiguranalica/ekartica" }, { "3B9F..801FC300681.4405014649534531C8..9000..", "Setec SetCOS 4.4.1" }, { "3B9F1110804154393853433033324354312E3535", "'ID Vault' brand device (Other)\nhttp://www.idvault.com/index.html" }, { "3B9F1110805661756C74494320343630312E3032", "Inside Secure VaultIC 460 Smart Object [ICCD 1.00] (Other)\nhttp://www.insidesecure.com/Products-Technologies/Secure-Solutions/VaultIC460" }, @@ -2862,6 +2962,7 @@ const static atr_t AtrTable[] = { { "3B9F94801FC78031E073FE211B573786609BA182109B", "U Mobile POWER Prepaid (Telecommunication)\nhttp://www.u.com.my/prepaid" }, { "3B9F94801FC78031E073FE211B573786609C8080802F", "Telenor (Telecommunication)\nhttps://www.telenor.com.pk/" }, { "3B9F94801FC78031E073FE211B573C8660AF030070E4", "MTS Russia (Telecommunication)\nhttps://www.mts.ru" }, + { "3B9F94801FC78031E073FE211B573C8660BEB2001024", "SIM card issued by Telenor Serbia (Telecommunication)" }, { "3B9F94801FC78031E073FE211B573C8660CDA1001246", "Beeline SIM card (RUS) (Telecommunication)\nhttps://beeline.ru" }, { "3B9F94801FC78031E073FE211B573F86604D03000075", "Prepaid SIM card MOCHE (Portugal) (Telecommunication)" }, { "3B9F94801FC78031E073FE211B573F866083020000BA", "GSM-SIM Beeline RU (Telecommunication)\nhttp://beeline.ru" }, @@ -2930,6 +3031,7 @@ const static atr_t AtrTable[] = { { "3B9F95801FC78031E073FE2113574A330E1A32330087", "Rohde and Schwarz CMW-Z04. Mini-UICC Test Card (Telecommunication)" }, { "3B9F95801FC78031E073FE2113574A330E1A32360082", "TELUS 3G SIM Card" }, { "3B9F95801FC78031E073FE2113574A330E1E32360086", "SIM Enreach (Telecommunication)" }, + { "3B9F95801FC78031E073FE21135786810686984000B4", "China Mobile LTE USIM Card (Telecommunication)" }, { "3B9F95801FC78031E073FE2113635510888307900006", "SHOW or UPlus USIM (Telecommunication)" }, { "3B9F95801FC78031E073FE2113635510AA8307900024", "2degrees NFC (Telecommunication)\nhttp://www.2degreesmobile.co.nz/home" }, { "3B9F95801FC78031E073FE2113672228004001000191", "Lycamobile Prepaid SIM-Card (Telecommunication)\nhttp://lycamobile.at" }, @@ -2969,9 +3071,11 @@ const static atr_t AtrTable[] = { { "3B9F958131FE9F006646530501001171DF0000........", "Feitian ePass2003 token" }, { "3B9F958131FE9F00664653051000FF71DF0000000000EC", "JavaCOS A22 dual interface Java card - 150K (JavaCard)\nhttp://www.smartcardfocus.us/shop/ilp/id~709/javacos-a22-dual-interface-java-card-150k/p/index.shtml" }, { "3B9F958131FE9F006646530510043171DF000000000026", "ePass2003 (PKI)" }, + { "3B9F958131FE9F006646530510043171DF0000036A82CD", "ePass2003 Auto (eID)" }, { "3B9F958131FE9F006646530510043171DF000006000020", "FEITIAN ePASS 2003 Auto (PKI)" }, { "3B9F958131FE9F006646530510043171DF00003900001F", "Feitian ePass2003 (PKI)" }, { "3B9F958131FE9F006646530510063171DF000000000024", "FT ePass2003Auto USB Token (PKI)" }, + { "3B9F958131FE9F006646530510113171DF000000000033", "Epass 2003 (PKI)" }, { "3B9F958131FE9F006646530510113171DF0000039000A0", "Feitian ePass2003 PKI Token (PKI)" }, { "3B9F958131FE9F006646530510113171DF0000860000B5", "HYPERSECU HYP2003 (PKI)\nhttps://www.hypersecu.com/hyperpki" }, { "3B9F958131FE9F006646530510323871DF00000600001F", "token (PKI)" }, @@ -2984,6 +3088,7 @@ const static atr_t AtrTable[] = { { "3B9F958131FE9F006646530523002571DF000003900096", "Feitian USB Cryptographic token (FIPS 140-2 Level 3) (PKI)\nhttp://www.ftsafe.com/product/epass/epass2003" }, { "3B9F958131FE9F006646530532022571DF000006000010", "ePass 3003 Auto (PKI)\nhttps://www.ftsafe.com/products/PKI/Standard/Specification" }, { "3B9F958131FE9F006646530534002571DF0000036A82F9", "Feitian ePass2003Auto (PKI)\nhttps://www.ftsafe.com/Products/PKI/Standard/Specification" }, + { "3B9F958131FE9F006646530540231871DF000000000078", "Feitian ePass2003 (PKI)\nhttps://www.ftsafe.com/store/product/epass2003-pki-token/" }, { "3B9F958131FE9F006646530551003371DF000000000061", "FT ePass2003Auto 00 00 (PKI)" }, { "3B9F958131FE9F006646530551003371DF0000036A828A", "Feitian epass2003 Auto (PKI)" }, { "3B9F958131FE9F006646530551003371DF0000039000F2", "HyperPKI USB Token (PKI)\nhttps://www.hypersecu.com/hyperpki" }, @@ -3010,6 +3115,8 @@ const static atr_t AtrTable[] = { { "3B9F96801F878031E073FE211B674A357530350265F8", "sysmoISIM-SJA5 (Telecommunication)\nhttps://sysmocom.de/products/sim/sysmoisim-sja5/index.html" }, { "3B9F96801F878031E073FE211B674A4C5275310451D5", "Test card provided with 4G/5G network from Amarisoft (Telecommunication)" }, { "3B9F96801F878031E073FE211B674A4C7530300248A9", "Cardcentrics (Telecommunication)" }, + { "3B9F96801F878031E073FE211B674A4C753030064BAE", "USIM (Telecommunication)" }, + { "3B9F96801F878031E073FE211B674A4C7530311359A8", "IR MCI simcard (Telecommunication)\nhttps://mci.ir/" }, { "3B9F96801F878031E073FE211B674A4C753034054BA9", "sysmoISIM-SJA2 (Telecommunication)\nhttps://osmocom.org/projects/cellular-infrastructure/wiki/SysmoISIM-SJA2" }, { "3B9F96801F878031E073FE211B674A4C75313305688C", "Somtel SIM (Telecommunication)" }, { "3B9F96801F878031E073FE211B674A55527531054BD7", "Softbank (C2) USIM card (Telecommunication)" }, @@ -3050,6 +3157,7 @@ const static atr_t AtrTable[] = { { "3B9F96801FC68031E073FE211B66D00221AB11180180", "Telekom Germany Triple SIM issued in 2018 (Telecommunication)" }, { "3B9F96801FC68031E073FE211B66D00221AB11180786", "telekom HU nano sim card (Telecommunication)\nhttps://www.telekom.hu" }, { "3B9F96801FC68031E073FE211B66D00224DB141005FC", "CZ KAKTUS SIM CARD (Telecommunication)" }, + { "3B9F96801FC68031E073FE211B66D0023BFE141001C2", "Freenet SIM Card (Deutsche Telekom, green LTE) (Telecommunication)\nhttps://www.freenet-mobilfunk.de/handytarife/green-lte-tarife/" }, { "3B9F96801FC68031E073FE211C6441193100829000AE", "SORACOM SIM (plan unknown) (Telecommunication)" }, { "3B9F96801FC78031A073BE21136742470111000001CC", "Bank of Hawai'i (Bank)\nhttps://www.boh.com/" }, { "3B9F96801FC78031A073BE2113674320071800000100", "sysmoUSIM-SJS1 (Telecommunication) (Telecommunication)\nhttp://www.sysmocom.de/products/sysmousim-sjs1-sim-usim" }, @@ -3166,20 +3274,27 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073FE211B6441447300829000B7", "Bouygues Telecom (French Mobile Provider SIM card) (Telecommunication)\nhttps://www.bouyguestelecom.fr" }, { "3B9F96801FC78031E073FE211B6441503100829000E1", "telcel sim card (Telecommunication)" }, { "3B9F96801FC78031E073FE211B644163940082900077", "EMnify SIMcard (Telecommunication)\nhttps://www.emnify.com/global-iot-sim" }, + { "3B9F96801FC78031E073FE211B6441703100829000C1", "Slovak Railways (Telecommunication)" }, { "3B9F96801FC78031E073FE211B6441725100829000A3", "Free Mobile (French wireless service provider) SIM card (mini-SIM with micro-SIM cutout) received in 2020-09 (Telecommunication)\nhttps://mobile.free.fr/" }, { "3B9F96801FC78031E073FE211B6441725200829000A0", "Woolworths Mobile Prepaid SIM (Telecommunication)\nhttps://mobile.woolworths.com.au/" }, { "3B9F96801FC78031E073FE211B6441763200829000C4", "T-Mobile SIM card issued April 2021 (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B644183710082900072", "SIM card (Telecommunication)" }, { "3B9F96801FC78031E073FE211B644183720082900071", "Ucom Armenia (Telecommunication)\nhttps://www.ucom.am" }, { "3B9F96801FC78031E073FE211B644187210082900026", "French SIM card from MVNO Phenix Partners, host operator Orange (Telecommunication)" }, { "3B9F96801FC78031E073FE211B644187950082900092", "Dish Wireless 5G (Telecommunication)\nhttps://help.boostmobile.com/docs/boost-mobile-network" }, + { "3B9F96801FC78031E073FE211B64418892008290009A", "VIVO pre SIM card (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B644189610082900068", "SIM card of a MEO subsidiary 'UZO' (Telecommunication)\nhttps://www.uzo.pt/uzo/" }, + { "3B9F96801FC78031E073FE211B6450016600829000F6", "SIM card (manufactured by IDEMIA) Telecommunication" }, { "3B9F96801FC78031E073FE211B6450040100826A82EC", "SIM (Telecommunication)" }, - { "3B9F96801FC78031E073FE211B6450055100829000C5", "Sim card Team Telecom Armenia (Telecommunication)\nhttps://www.telecomarmenia.am/hy/" }, + { "3B9F96801FC78031E073FE211B6450055100829000C5", "Sim card Team Telecom Armenia (Telecommunication)\nhttps://www.telecomarmenia.am/hy/\nSIM card issued by Serbian MTS (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B6450094300829000DB", "Czech Railways (Telecommunication)" }, { "3B9F96801FC78031E073FE211B65240109010081057B", "Lycamobile (UK) GSM SIM card" }, { "3B9F96801FC78031E073FE211B65260109000781057F", "EMT WPKI 2015 (ECC) subscription (Telecommunication)\nhttps://www.emt.ee/en/pakkumised/mobiil-id" }, { "3B9F96801FC78031E073FE211B65270109010381057B", "slarmy (Telecommunication)" }, { "3B9F96801FC78031E073FE211B652A01090101810574", "9mobile Nigeria (Telecommunication)\nhttp://www.9mobile.com.ng" }, { "3B9F96801FC78031E073FE211B652A010A0102810574", "Thailand AIS SIM Card (Telecommunication)" }, { "3B9F96801FC78031E073FE211B652A010E0201810570", "gsm (Telecommunication)" }, + { "3B9F96801FC78031E073FE211B652B010E010981057A", "Standard SIM card (Telecommunication)" }, { "3B9F96801FC78031E073FE211B652D010C0400810572", "Tri Indonesia Bima+ SIM Card (Telecommunication)\nhttps://beta.tri.co.id/3digiworld/Bimaplus" }, { "3B9F96801FC78031E073FE211B652F0109020A810579", "cellcom israel sim (Telecommunication)" }, { "3B9F96801FC78031E073FE211B652F01090602810575", "Indosat SIM Card (Telecommunication)" }, @@ -3196,20 +3311,26 @@ const static atr_t AtrTable[] = { { "3B9F96801FC78031E073FE211BB3E20394830F90006D", "GemXplore 3G v2.2" }, { "3B9F96801FC78031E073FE211BB3E204A5830F90005B", "Tre Italia Gemplus (Telecommunication)" }, { "3B9F96801FC78031E073FEA117574A33058C33390096", "1NCE SIM card (Telecommunication)\nhttps://1nce.com/en-eu/1nce-os/our-architecture" }, - { "3B9F96803F87828031E073FE211B67454D753034024B02", "Hologram Global G1 eUICC SIM(Telecommunication)\nhttps: //www.hologram.io/products/global-iot-sim-card/" }, + { "3B9F96801FC78031E073FEA11F6441805100829000D5", "Smartjac smaot500b234ff (Telecommunication)\nhttps://www.smartjac.biz" }, + { "3B9F96803F87828031E073FE211B67454D753034024B02", "Hologram Global G1 eUICC SIM (Telecommunication)\nhttps://www.hologram.io/products/global-iot-sim-card/" }, { "3B9F96803F87828031E073FE211F574543753130136502", "sysmoEUICC1-Cxx - eUCICC for econsumer eSIM RSP (Telecommunication)\nhttps://sysmocom.de/products/sim/sysmocom-euicc/index.html" }, - { "3B9F96803F87828031E073FE211F574543753130266F3D", "An eSIM physical card, you can write eSIM profiles into it and use it as a general SIM (Telecommunication)\nhttps://www.9esim.com/" }, + { "3B9F96803F87828031E073FE211F574543753130266F3D", "eSIM physical card, you can write eSIM profiles into it and use it as a general SIM (Telecommunication)\nhttps://www.9esim.com/" }, + { "3B9F96803F87828031E073FE211F574543753130276F3C", "An eSIM physical card, you can write eSIM profiles into it and use it as a general SIM Telecommunication\nhttps://www.9esim.com/" }, { "3B9F96803FC3A08031E073F62113574A4D0E1D31300071", "Telenor SIM card (Norway)" }, { "3B9F96803FC6A08031E073F62116574A4D020B34546369", "SIM card Wingo operator (Switzerland) (Telecommunication)" }, + { "3B9F96803FC7008031E073FE21136767A002D9000001E5", "Kcell (Telecommunication)" }, { "3B9F96803FC7008031E073FE211B6408050300829000EF", "Multipurpose UICC card for 2G, 3G, 4G/LTE, CDMA, ISIM & NFC (Telecommunication)\nhttp://www.smartjac.biz/index.php/component/eshop/telecom/test-uicc-sim-cards/2ff-mini-sim-cards/4g-open-multipurpose-uicc-card-3ff?Itemid=0" }, { "3B9F96803FC7008031E073FE211F6441262100829000A3", "Smartjac SMAOT100A234FF (Telecommunication)\nhttps://smartjac.com" }, { "3B9F96803FC7828031E073F62157574A330581053000CE", "COMPRION M2M eUICC (Telecommunication)" }, + { "3B9F96803FC7828031E073F62157574A4D020B3446007A", "cosmo one 9.1 (eID)" }, { "3B9F96803FC7828031E073F62157574A4D020B60010069", "eSIM GSMA Card (Telecommunication)\nhttps://www.gsma.com/newsroom/wp-content/uploads/SGP.22_v2.2.pdf" }, { "3B9F96803FC7828031E073F62157574A4D020B60610009", "ting (Telecommunication)" }, { "3B9F96803FC7828031E073FE211B57AA8660F0010004FB", "The eSIM.me Card (Telecommunication)\nhttps://esim.me/" }, { "3B9F96803FC7828031E073FE211B57AA8660F0010011EE", "eSIM.me pluggable eSIM (Telecommunication)\nhttps://esim.me/" }, { "3B9F96803FC7828031E073FE211B57AA8660F0010017E8", "eSim.me Orange Setup (Telecommunication)" }, { "3B9F96803FC7828031E073FE211B57AA8660F001001EE1", "5Ber (Telecommunication)\nhttps://esim.5ber.com" }, + { "3B9F96803FC7828031E073FE211B57AA8660F0010027D8", "5ber physical eUICC / eSIM (Standard Version) (Telecommunication)\nhttps://esim.5ber.com/order?language=en-US" }, + { "3B9F96803FC7828031E073FE211B57AA8660F3030002FC", "Xesim X2 (Telecommunication)\nhttps://xesim.cc/products/xesim?VariantsId=10004" }, { "3B9F96803FC7828031E073FE211B633A204E8300900031", "eSIM (Telecommunication)" }, { "3B9F96803FC7828031E075F62157200355020B60500018", "iPhone 11 SIM Slot eUICC chip. Identified by eSTK.me. (Telecommunication)" }, { "3B9F96803FC7828031E075F62157200355020C608000CF", "ST33J2M0STL9DZB0 (Telecommunication)\nhttps://www.st.com/en/secure-mcus/st33j2m0.html" }, @@ -3219,12 +3340,14 @@ const static atr_t AtrTable[] = { { "3B9F96803FC7828031E075F621573C0455020C61010054", "euicc from iphone14 (Telecommunication)" }, { "3B9F96803FC7A08031E073F62116574A4D020233456377", "ISIS-Ready T-Mobile Sim Card (Telecommunication)" }, { "3B9F96803FC7A08031E073F62156574A4D020B3444005B", "Norwegian telenor (Telecommunication)\nhttp://www.telenor.no" }, + { "3B9F96803FC7A08031E073F62157574A4D020B3444005A", "Verizon 4G SIM (Telecommunication)" }, { "3B9F96803FC7A08031E073F62157574A4D020B34546329", "Orange FR - opa (Telecommunication)" }, { "3B9F96803FC7A08031E073FE211B63F100E8830090005E", "UICC CARD (Telecommunication)" }, { "3B9F96803FC7A08031E073FE211B6407689A00829000B4", "Orange SIM Card (Telecommunication)" }, { "3B9F96803FC7A08031E073FE211B64080503008290004F", "NFC-enabled SIM card of MTS Russia. (Telecommunication)" }, { "3B9F96803FC7A08031E073FE211F6300690083819000AB", "GSM file system and SWP sample supplied with STMicro development kit (Other)" }, { "3B9F96803FC7A08031E073FE211F6441269100829000B3", "LTE Lab SIM Ver 1.3 (Telecommunication)" }, + { "3B9F968131FE454F52434C2D4A43332E324750322E3323", "Oracle JavaCard simulator (25.0) (Other)" }, { "3B9F968131FE458065544312210831C073F6218081059A", "Scientific and Technological Research Council of Turkey (test card) (eID)" }, { "3B9F968131FE45806755454B41451212318073B3A180EA", "AKiS v1.2 on nxp chip" }, { "3B9F968131FE45806755454B41451252318073B3A180AA", "AKiS v1.2.1 on infineon chip" }, @@ -3253,6 +3376,7 @@ const static atr_t AtrTable[] = { { "3B9F96C00A3FC6A08031E073FE211F65D001900F3B810FE6", "Verizon US USIM card (Telecommunication)" }, { "3B9F96C00A3FC6A08031E073FE211F65D00209107C810F24", "GSM SIM Vodafone NL postpaid NFC+ (Telecommunication)" }, { "3B9F96C00A3FC6A08031E073FE211F65D0021B12B7810FFF", "SIM Card Fastweb IT GSM mobile network (Telecommunication)" }, + { "3B9F96C00A3FC6A08031E073FE211F65D0023314E0810F86", "Verizon 5G sim card (Telecommunication)" }, { "3B9F96C00A3FC7828031E073FE211F65D00209146C810F13", "euicc (eID)" }, { "3B9F96C00A3FC7A08031E073FE211B65D001740E8D810FB0", "USIM" }, { "3B9F96C00A3FC7A08031E073FE211B65D001740EE3810FDE", "EE (UK) Mobile Phone SIM Card circa 2016 (Telecommunication)" }, @@ -3277,8 +3401,10 @@ const static atr_t AtrTable[] = { { "3B9F97C00A1FC78031E073FE211B65D0011009228100F2", "'ultra fast card, max speed supported for telecom'? (transport)" }, { "3B9F97C00A1FC78031E073FE211B65D001900F3B810F62", "Gemalto Multi-SIM consumer 4.2 (ST33I1M2) (Telecommunication)" }, { "3B9F97C00A3FC6828031E073FE211B65D0023314A5810FE4", "Thales eUICC French Ministry BAP v2 (Telecommunication)" }, + { "3B9F97C00A3FC6828031E073FE211F65D0023314A5810FE0", "Flexiroam SIM card (Telecommunication)\nhttps://www.flexiroam.com/" }, { "3B9F97C00A3FC6A08031E073FE211B65D001740EEB810FD6", "Verizon 4G LTE Micro SIM (Telecommunication)" }, { "3B9F97C00A3FC6A08031E073FE211F65D0021B13F6810FBE", "Verizon SIM Card (Telecommunication)" }, + { "3B9F97C00A3FC6A08031E073FE211F65D0023314E0810F87", "Verizon 5G SIM Card (Telecommunication)\nhttps://www.verizon.com/" }, { "3B9F97C00A3FC7A08031E073FE211F65D001900FEE810F33", "AT&T Mobility LLC MicroSIM Card (Telecommunication)\nhttps://www.att.com/wireless/" }, { "3B9F97C00AB1FE453FC6828031E073FE211B65D0023A14C9810F8B", "SIM (Telecommunication)" }, { "3B9F97C0FF1FC78031E073FE211B63F100AD830F90002A", "Gemalto Speed Enhancement 97 (Telecommunication)" }, @@ -3324,10 +3450,11 @@ const static atr_t AtrTable[] = { { "3BB794008131FE6553504B32339000D1", "Giesecke & Devrient Starcos 2.3\nDeutsche Bank WebSign (RSA-Card)\nG&D StarSign Token\nOsakidetza ONA (eID)\nhttp://www.osakidetza.euskadi.eus/r85-ckserv01/es/contenidos/nota_prensa/ruedasanidad35/es_rs/ruedasanidad35_c.html" }, { "3BB813008131205D0057696E4361726402", "SmartCard for Windows 1.1" }, { "3BB813008131FA524348544D4F494341A5", "citizen digital certificate (PKI)\nhttp://moica.nat.gov.tw/" }, + { "3BB89600C00831FE45FFFF1154305023006A", "Infineon SECORA ID S, SLJ52GDT120CS (JavaCard)" }, { "3BB897008131FE45FFFF148230502300F1", "UAE Emirates ID (eID)\nhttps://www.icp.gov.ae" }, { "3BB89700813FE45FFFF148230502300F", "UAE Emirates ID (eID)" }, { "3BB89700C00831FE45FFFF148230502300B8", "Infineon SECORA ID X (JavaCard)" }, - { "3BB918008131FE9E8073FF614083000000DF", "Serbian Identity Card\nThis is the new Serbian biometric identity card (every adult cityzen\nmust have). The chip contains owners picture, name, date and place\nof birth, current address, unique ID number and fingerprint." }, + { "3BB918008131FE9E8073FF614083000000DF", "Serbian Identity Card\n1st Serbian biometric identity card (every adult cityzen must have).\nThe chip contains owners picture, name, date and place of birth, current\naddress, unique ID number and fingerprint." }, { "3BB9940040144747334D4838353330", "T D1 GSM card (Telecommunication)" }, { "3BB9940040144747334E4838363430", "GSM-SIM card of the Austrian mobile phone provider One\nhttp://www.one.at\nProximus SIM - Belgium (SetCOS?)\no2 GSM-SIM card Germany 2003" }, { "3BBA11001000434C5F53414D00133800", "Planeta Informatica CL-SAM (Other)\nhttp://www.planeta.inf.br/" }, @@ -3421,6 +3548,7 @@ const static atr_t AtrTable[] = { { "3BBF1800C02031705253544152434F5320533231204390009C", "Giesecke & Devrient SPK 2.1 C" }, { "3BBF9300801FC68031E073FE2113576573746B2E6D65E3", "eSTK.me v1.2.5 or later (Telecommunication)\nhttps://eSTK.me" }, { "3BBF9300803FC6828031E073FE2113576573746B2E6D6541", "eSTK.me v1.2.4 (Telecommunication)\nhttps://eSTK.me" }, + { "3BBF9400801FC68031E073FE2113576573746B2E6D65E4", "eSTK.me SGP.22 consumer card T001V06 (3.4.3) (Telecommunication)" }, { "3BBF94008131FE65454C55204175737472696120312E3238", "A-Trust: trust-sign (Old Version, ca. 2002) for Digital Signature etc.\nA-Trust: a-sign-premium (ca. 2004) 'Burgerkarte' ('Citizen-Card')\nfor Identifikation, Digital Signature etc.\n('should be' Starcos 2.3)" }, { "3BBF9500801FC68031E073FE2113576573746B2E6D65E5", "eSTK.me v3.1.1 or later (Telecommunication)\nhttps://estk.me" }, { "3BBF9500803FC6828031E073FE2113576573746B2E6D6547", "eSTK.me v3.1.1 (Telecommunication)\nhttps://eSTK.me" }, @@ -3428,7 +3556,7 @@ const static atr_t AtrTable[] = { { "3BBF96008131FE5D00640411000031C073F701D0009000", "DATEV eG, Nuernberg, Bavaria, Germany (PKI)\nhttp://www.datev.de" }, { "3BBF96008131FE5D00640411030131C07301D000900000", "DATEV eG, Nuernberg, Bavaria, Germany (PKI)\nhttp://www.datev.de" }, { "3BBF96008131FE5D00640411030131C073F701D0009000", "DATEV eG, Nuernberg, Bavaria, Germany (PKI)\nhttp://www.datev.de" }, - { "3BC5FF8131FB458073C6010000", "Japanese Individual Number Card (eID)\nhttps://www.kojinbango-card.go.jp/en/kojinbango/index.html" }, + { "3BC5FF8131FB458073C6010000", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nJapanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en/" }, { "3BC800000073C84000000000", "verve (Bank)" }, { "3BCDFF8031FE450068D276000028040481009000CD", "Tachograph company test card (Transport)" }, { "3BD096FF81B1FE451F032E", "New european health insurance card of the German health insurance (G2) (HealthCare)\nhttps://de.wikipedia.org/wiki/Elektronische_Gesundheitskarte" }, @@ -3446,10 +3574,13 @@ const static atr_t AtrTable[] = { { "3BD218008131FE58CB0116", "D-Trust Card 5.1/5.4 (contact based)\nhttps://www.d-trust.net/de/support/signatur-und-siegelkarten" }, { "3BD21802C10A31FE58C80D51", "Siemens Card CardOS M4.4" }, { "3BD296FF81B1FE451F870102AB", "Electronic Vehicle Registration (eVR) from RDW.nl (The Netherlands), open sourced at [URL], demo (Windows / Linux Wine Mono) (Transport)\nhttps://github.com/eVRMTV/eVR" }, + { "3BD396FF81B1FE451F0380810529", "German Health Insurance Card 'Gesundheitskarte' (HealthCare)\nhttps://www.bahn-bkk.de/" }, { "3BD396FF81B1FE451F078081052D", "German public health insurance card (Elektronische Gesundheitskarte eGK), 2nd generation (G2) (HealthCare)" }, + { "3BD49500400A5348434C", "Rc book (Transport)" }, { "3BD5180081313A7D8073C8211030", "Aladdin eToken NG-Flash with 256MB of flash memory\nAladdin eToken PRO (72KB)\nhttp://www.aladdin.com/etoken/devices/default.aspx" }, { "3BD518008131FE7D8073C82110F4", "Bank of Lithuania Identification card\nGemalto SafeNet eToken Java Based Cards\nhttps://safenet.gemalto.com/multi-factor-authentication/authenticators/pki-usb-authentication/" }, { "3BD518FF8091FE1FC38073C8211308", "Athena IDProtect (JavaCard 2.2.2)\nhttp://www.athena-scs.com/product.asp?pid=32\nThales nShield Security World Card - Remote Administration Ready\nhttps://www.thalesesecurity.fr/products/hsm-management-and-monitoring/nshield-remote-administration" }, + { "3BD518FF8191FE1FC34A434F503422", "algerian driving licence (eID)" }, { "3BD518FF8191FE1FC38073C821100A", "ComSign digital signature card (eID)\nhttps://www.comsign.co.uk/" }, { "3BD518FF8191FE1FC38073C8211309", "Athena IDProtect Key (v2)\nhttp://www.athena-scs.com/product.asp?pid=33" }, { "3BD518FF81B1FE451FC38073C821106F", "DPI Card ID Guatemala Version 2024 (eID)\nhttps://www.renap.gob.gt/" }, @@ -3544,10 +3675,12 @@ const static atr_t AtrTable[] = { { "3BDB960080B1FE451F830031C164084022300F90000A", "Oberthur v7 - in a Gemalto (was Gemplus) GemPC Key SmartCard Reader (grey USB dongle) - bought at ChamberSign (PKI)" }, { "3BDB960080B1FE451F830031E85427E6040007900084", "Polish encard (eID)" }, { "3BDB960080B1FE451F830031E85427E604000F90008C", "Token card from iBRE CompanyNet (mbank) (Bank)" }, + { "3BDB960080B1FE451F830031E85427E6050007900085", "Polish qualified signature card by CenCert (Other)\nhttps://www.cencert.pl/produkt/podpis-elektroniczny-kwalifikowany-na-karcie-2024/" }, { "3BDB960080B1FE451F834553544F4E49412D65494455", "Estonian Identity Card (ID-One Cosmo v8.1) (eID)" }, { "3BDB960080B1FE451F870031C1640958223607900019", "Idemia Solvo Fly 40 (JavaCard)" }, { "3BDB960081B1FE451F0380F9A0000003080000100018", "Oberthur CS PIV End Point v1.08 FIPS201 Certified" }, { "3BDB960081B1FE451F0380F9A0000003480000000149", "Fly Clear card" }, + { "3BDB960081B1FE451F830031C064FC2910000190009B", "Sm@rtCafe Expert 7.0 (SCE7.0) SAM card (Transport)" }, { "3BDB960081B1FE451F8380F9A0000003080000100098", "Oberthur Cosmo v7 128K with PIV applet\nhttp://www.smartcardfocus.com/shop/ilp/id~410/p/index.shtml" }, { "3BDB96FF80B1FE451F870031C164093364490F9000BC", "cnie Carte Nationale d'Identite Electronique (eID)" }, { "3BDB96FF80B1FE451F870031C164093772130F9000F4", "French ID Card 2021 (eID)\nhttps://ants.gouv.fr/nos-missions/les-titres-produits-par-l-ants/les-documents-d-identite/la-puce-de-la-nouvelle-carte-nationale-didentite" }, @@ -3580,6 +3713,7 @@ const static atr_t AtrTable[] = { { "3BDD18008191FE1FC3006646530803003671DF00008097", "Feitian K9Plus - ePass FIDO-NFC with PIV (Other)\nhttps://ftsafe.com/products/FIDO/NFC" }, { "3BDD18FF8191FE1FC3006646530803003671DF00008068", "Feitian FIDO NFC Plus K9 Security Key (Other)\nhttps://www.ftsafe.com/products/FIDO/NFC" }, { "3BDD18FF8191FE1FC3FF4F70656E506879736963616CF6", "Open Physical PIV-Compatible NXP SECID P60 (eID)\nhttps://openphysical.org/" }, + { "3BDD18FF81B1FE451FC39E414B442D43657274696C6961BD", "Certilia Gen2 Card (eID)\nhttps://www.certilia.com/" }, { "3BDD18FFC080B1FE451FC30068D276000028040411009000C9", "Russian Federation driver card for the digital tachograph\nPolish driver card for digital tachograph" }, { "3BDD18FFC080B1FE451FC30068D276000028040971009000A4", "Worktime/driving style monitoring card (Transport)\nhttp://www.paetronics.fi/en/" }, { "3BDD96008010FE8031806301FFC073B3211B8105", "BIFIT iBank 2 USB Key (Bank)\nhttp://bifit.ua" }, @@ -3644,6 +3778,8 @@ const static atr_t AtrTable[] = { { "3BDF960080B1FE451F870031C16408923201738421E0059000C5", "Company Card for Transport companies (Transport)" }, { "3BDF96008131FE4541434F532D4944303032382E3031366F", "Sri Lankan driving license [ web: motortraffic.gov.lk ] (eID)\nhttp://www.motortraffic.gov.lk/web/index.php?option=com_content&view=article&id=83&Itemid=140&lang=en" }, { "3BDF96008131FE4580738421E0554978000080830F90000C", "Idemia Cosmo X (eID)\nhttps://cyber.gouv.fr/sites/default/files/2021/08/anssi-cible-cc-2021_36n.pdf" }, + { "3BDF96008131FE4580738421E05569780000808307900024", "Romanian Electronic Identity Card (eID)" }, + { "3BDF96008131FE4580738421E0556978540001830F9000F9", "National ID Card of Peru issued by RENIEC (eID)\nhttps://identidad.reniec.gob.pe/dni-electronico" }, { "3BDF96008131FE588031B05202056405A100AC73D622C020", "Austrian health insurance card 'e-card' (HealthCare)\nhttps://de.wikipedia.org/wiki/E-card_(Chipkarte)" }, { "3BDF960081B1FE451F838073CC91CBF9A0000003080000100079", "Test PIV Cards available for sale from NIST\nhttp://csrc.nist.gov/groups/SNS/piv/testcards.html" }, { "3BDF960090103F07008031E0677377696363000073FE210006", "swsim card (Telecommunication)" }, @@ -3654,6 +3790,9 @@ const static atr_t AtrTable[] = { { "3BDF96FF8131FE45805B44452E424E4F544B3130308105A0", "BeA - Certification Card for German Solicitors (Other)\nhttps://bea.bnotk.de/" }, { "3BDF96FF81B1FE451F870031B96409377213738401E000000000", "National Identity Card of Slovakia (eID) (eID)\nhttps://en.wikipedia.org/wiki/Slovak_identity_card" }, { "3BDF96FF910131FE4680319052410264050200AC73D622C017", "Acos-ID (AUSTRIACARD's Operating System) (Other)\nhttps://www.austriacard.com/digital-security/solutions/card-solutions/acos-id/" }, + { "3BDF97008131FE4541434F5300030001020001010001015D", "Academic ID (Other)\nhttps://submit-academicid.minedu.gov.gr/" }, + { "3BDF97008131FE4680319052410364050201AC73D622C0F8", "A-Trust ACOS-ID v3 (AustriaCard Operating System), used for Austrian RKSV fiscal signature cards (Other)\nhttps://github.com/A-Trust/RKSV" }, + { "3BDF97008131FE4680319052410464050401AC73D622C0F9", "A-Trust ACOS-ID v4.1 (AustriaCard Operating System), used for Austrian RKSV fiscal signature cards (Other)\nhttps://github.com/A-Trust/RKSV" }, { "3BDF97008131FE588031B05202056405A100AC73D622C021", "Austrian healthcare insurance identification card (HealthCare)\nhttps://www.chipkarte.at" }, { "3BDF970081B1FE451F838073CC91CBF9A0000003080000100078", "NASA PIV Card (Other)" }, { "3BE000008131104505", "Emoney indonesia (Bank)" }, @@ -3665,6 +3804,7 @@ const static atr_t AtrTable[] = { { "3BE2000040204907", "Schlumberger Cryptoflex Key Generation" }, { "3BE200FFC11031FE55C8029C", "Aladdin eToken PRO (USB token)\nSiemens CardOS M4.0" }, { "3BE300FF9181712644000113202D", "Metrebus Card\n(used in Rome to store personal information and Atac subscription.\nAtac is the public transport company of the city of Rome.)\nhttp://www.atac.roma.it/smart/smart.asp?A=2&S=22&PR=4&LNG=2" }, + { "3BE400008121459C1000800D", "JCP2040 (JavaCard)" }, { "3BE500008121459C100100800D", "BIN 470132 -- BANK OF AMERICA VISA DEBIT -- GEMALTO MGY 0 U1090788B - 12/14 F8 00 89 (Bank)" }, { "3BE500008131FE45D00037008089", "ATM card for Standard Chartered, Taiwan" }, { "3BE500FF8131FE458073C601082D", "MUFG (.jp) (Bank)" }, @@ -3695,6 +3835,7 @@ const static atr_t AtrTable[] = { { "3BE800008131FE450073C8400000900088", "VISA Card (Skandinaviska Enskilda Banken) with Swedish BankID\nVISA card (Chinatrust Bank (Taiwan), dual-interface card with a Taipei Metro e-purse function)" }, { "3BE800008131FE454A434F50763234....", "NXP JCOP v2.4.x (see hist bytes for more info)" }, { "3BE800008131FE454A434F5076323431B4", "VISA Debit card for NAB, Australia" }, + { "3BE800FF8131FE41534C4A01305023101F", "Cashapp Card - WIP arduino apdu interface (Bank)" }, { "3BE800FF8131FE43AA00000000000000B0", "Secure Signing Token (eID)" }, { "3BE800FF8131FE45434C6169726520361A", "DKB Visa card with PayWave" }, { "3BE90000812145454D565F41545220066C", "VISA card, issued by HVB Bank Czech Republic or austrian BankAustria\nhttp://www.hvb.cz" }, @@ -3744,6 +3885,7 @@ const static atr_t AtrTable[] = { { "3BED00008131FE450031C071C6644D3533560F900046", "Kostadin (Bank)" }, { "3BED00008131FE450031C071C6644D35354D0F9000", "ING Credit Card (Bank)\nhttps://www.ing.nl/particulier/betalen/creditcards/index.html" }, { "3BED00FF813120754D424320534D502056312E3130BD", "Used to Control a Laser Device" }, + { "3BEE00008131804180318066B0840C016E01830090008D", "CTBC Bank Co., Ltd. debit VISA card (Bank)\nhttps://www.ctbcbank.com/twrbo/zh_tw/cc_index/cc_product/cc_debitcard_index.html" }, { "3BEE00008131804280318066B0840C016E01830090008E", "MultiApp Cards (Easy 72K Type B and Combi 72K Type B)\nE.SUN Commercial bank debit master card (Bank)\nTaiwan EasyCard (Transport)\nhttps://www.easycard.com.tw/english/index.asp" }, { "3BEE00008131804380318066B1A1110100F683009000", "Optelio/Desineo Cards (D72 FXR1)" }, { "3BEE00008131804380318066B1A11101A0F683009000", "Optelio D72 FXR1 (MD) T=1" }, @@ -3836,6 +3978,7 @@ const static atr_t AtrTable[] = { { "3BEF00FF8131FE456563111562025000100A002EFC0720C6", "maestro BankCard (Bank)" }, { "3BEF00FF8131FE456563111562025000100A09AC030720B2", "Girocard Sparkasse Darmstadt (Bank)" }, { "3BEF00FF8131FE4565631901620280000F003500420620BB", "Credit card (Germany, Postbank AG): VISA" }, + { "3BEF00FF8131FE4565631D028401560024140B60E401016E", "EthikBank Giro Card (Bank)\nhttps://www.ethikbank.de/privatkunden/konten-karten/karten-zum-konto/girocard" }, { "3BEF00FF8131FE4565631D0284025000230509A0D9010182", "Debit card (Bank)" }, { "3BEF00FF8131FE4565631D028402500023180920E7010121", "Deutsche Kreditbank AG (DKB AG) bank card (Bank)\nhttps://www.dkb.de/info/tan-verfahren/chipTAN/" }, { "3BEF00FF8131FE458031C06B49424D204A65745A204D3239", "UBS Internet Card (IBM JetZ M2)" }, @@ -3858,11 +4001,14 @@ const static atr_t AtrTable[] = { { "3BF29800FFC11031FE55C80315", "Siemens CardOS M 4.01 (SLE66CX320P)" }, { "3BF29800FFC11031FE55C80412", "CardOS M4.01a (SLE66CX322P)" }, { "3BF39600FFC00A31FE4D8031E083", "MARX Cryptoken (supported by RaakSign)" }, + { "3BF41300008131FE45004E0000A2", "Japanese HPKI card (PKI)\nhttps://www.medis.or.jp/8_hpki/index.html" }, { "3BF41300008131FE4552465A4FED", "Serbian Health Care electronic card (HealthCare)\nhttp://www.rfzo.rs/index.php/osiguranalica/ekartica" }, { "3BF4180002C10A31FE5856346376C5", "Eutron CryptoIdentity (reader + card token)" }, { "3BF41800FF8131805500318000C7", "Identity card of Italian Republic" }, { "3BF49800FFC11031FE554D346376B4", "Eutron Digipass 860 (reader + card token)" }, + { "3BF513000010002063CB12C1", "CB Master Carte du Credit Mutuel (Bank)" }, { "3BF51300008131FE4573746431308F", "card for NF-e in Brazil (PKI)\nhttps://certificadodigital.imprensaoficial.com.br/certificados-digitais/e-cnpj/a3/e-cnpj-a3-cartao" }, + { "3BF51320060E49424D0101B3", "User Identity card for TeleGuide, a Swedish terminal from the 90's similar to the French minitel. (eID)" }, { "3BF51800008131FE454D794549449A", "Aventra ActiveSecurity MyEID\nhttp://www.aventra.fi/pdf/ActiveSecurity%20MyEID%20Tokens%20white%20paper%20(2p)%20EN.pdf" }, { "3BF518000210804F73454944", "Atmega 128 microcontroller based open source EID smartcard with RSA and ECC. (eID)\nhttps://oseid.sourceforge.io/" }, { "3BF57100FFFE2400011E0F3339320103", "Mydo IC Card from Japan, based on NTTDATA CARD (Loyalty)\nhttps://www.idemitsu.com/company/history/13.html" }, @@ -3872,9 +4018,9 @@ const static atr_t AtrTable[] = { { "3BF59100FF918171FE400041180000001D", "Contactless Mifare 4k" }, { "3BF59100FF918171FE400041880000008D", "Contactless Mifare 1k or 4k" }, { "3BF59100FF918171FE4000420001008186", "American Express Blue RFID" }, - { "3BF59100FF918171FE400042000100D1D6", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nMy Number Card (The Social Security and Tax Number System in JAPAN) (eID)\nhttps://www.cao.go.jp/bangouseido/" }, + { "3BF59100FF918171FE400042000100D1D6", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nJapanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en/" }, { "3BF59100FF918171FE400042000177D1A1", "German Passport (ePass) (issued May 2008)" }, - { "3BF59100FF918171FE4000420001B3A115", "Individual Number Card (eID)\nhttps://www.kojinbango-card.go.jp/" }, + { "3BF59100FF918171FE4000420001B3A115", "Japanese Public Key Infrastructure (PKI)\nhttps://www.jpki.go.jp/\nJapanese Individual Number Card (My Number Card) (eID)\nhttps://www.kojinbango-card.go.jp/en" }, { "3BF59600008.31FE454D794549441.", "MyEID card (Infineon chip) (PKI)\nhttps://services.aventra.fi/English/products_MyEID_E.php" }, { "3BF61300FF1080434849503232", "PostFinance debit (Bank)\nhttps://www.postfinance.ch" }, { "3BF61300FF910131FE4080640F7000009E", "JA Bank Cash Card (Bank)\nhttps://www.jabank.org/" }, @@ -3995,6 +4141,7 @@ const static atr_t AtrTable[] = { { "3BFA1300008131FE454A434F5034315632333197", "JCOP41 /72K (eID)" }, { "3BFA1300008131FE454A434F50763234........", "NXP JCOP v2.4.x (see hist bytes for more info)" }, { "3BFA1300008131FE54A434F503233191", "Jcop (JavaCard)" }, + { "3BFA1300008171FE4200434F4946433256312E31AF", "Castles Technology Partner Self Signing Card (Other)" }, { "3BFA1300FF813180450031C173C00100009000B1", "OpenPGP" }, { "3BFA1300FF918131FE478012392F31C073C7014907", "MITSUBISHI Standard-9M (PKI)\nhttps://www.mdis.co.jp/service/standard-9m/" }, { "3BFA1800008031FE45FE654944202F20504B4903", "Estonian Identity Card (EstEID v3.5 (10.2014) cold) (eID)\nhttp://id.ee/" }, @@ -4010,7 +4157,8 @@ const static atr_t AtrTable[] = { { "3BFA1800008131FE454D4F54494F4E0000900760", "SIM card (Telecommunication)" }, { "3BFA1800008131FE4550564A434F5033454D5694", "NXP JCOP3 J3H082 Java Card 3.0.4 Dual-Interface (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop3-j3h082-java-card-3-0-4-j3h081-dual-interface/" }, { "3BFA1800008131FE4550564A434F503453494493", "National Health Insurance (Taiwan) (HealthCare)" }, - { "3BFA180000910131FE454A33523138302D323535F5", "Cardlogix J3R180 NXP JCOP 4 Java Card 3.0.5 Classic Dual Interface (JavaCard) (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic/" }, + { "3BFA1800008131FE45534C4A35324778787979FC", "National Health Insurance of Taiwan (HealthCare)\nhttps://www.nhi.gov.tw/" }, + { "3BFA180000910131FE454A33523138302D323535F5", "Cardlogix J3R180 NXP JCOP 4 Java Card 3.0.5 Classic Dual Interface (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic/" }, { "3BFA180000910131FE454A33523331302D333535FF", "NXP JCOP 4 Java Card 3.0.5 Classic (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic/" }, { "3BFA180000910131FE4550564A434F503453494482", "Supposed P71 SecID purchased from a Chinese manufacturer (JavaCard)" }, { "3BFA180000910131FE456BD1936AC2F28547E164CC", "J3R180, NXP JCOP4 JC3.0.5 Classic, GP2.3, SECID (JavaCard)\nhttps://www.cardlogix.com/product/nxp-jcop-4-java-card-3-0-5-classic/" }, @@ -4033,12 +4181,18 @@ const static atr_t AtrTable[] = { { "3BFB1300008131FE454A434F50533.3.56323332..", "JCOP-Sxx/yy v2.3.2 (see hist bytes for more info)" }, { "3BFB1300008131FE456368617269736D6174657884", "Charismathics smart card JCOP and Qualified electronic signature CHJCOP-xxx (PKI)\nhttps://www.stampit.org/en/page/808" }, { "3BFB1300FF10000031C164099511380F9000", "Mastercard World Elite (CapitalOne Venture) (Bank)\nhttps://www.mastercard.us/en-us/personal/find-a-card/world-elite-mastercard-credit.html" }, + { "3BFB1300FF10800031C16408603206079000", "America First Credit Union Visa Credit Card (Bank)\nhttps://www.americafirst.com/" }, { "3BFB1300FF10800031C164086032060F9000", "Stripe Issuing Card (Bank)" }, { "3BFB1300FF10800031C164086032100F9000", "Varo (Bank)" }, + { "3BFB1300FF10800031C164087771300F9000", "Apple Card (Bank)" }, { "3BFB1300FF10800031C164089862210F9000", "Visa Debit (Bank)" }, { "3BFB1300FF10800031C164089862290F9000", "Bank Card (Bank)" }, { "3BFB1300FF10800031C1640924331E0F9000", "TransferWise Debit Card (Bank)\nhttps://wise.com/" }, + { "3BFB1300FF10800031C164092962250F9000", "Apple Credit Card (Bank)" }, + { "3BFB1300FF10800031C164095821360F9000", "American Express issued by American Express Europe S.A. (Germany branch) (Bank)\nhttps://www.americanexpress.com/de-de/" }, + { "3BFB1300FF10800031C164095822360F9000", "LINE Bank by Hana Bank BT21 Debit Visa (Bank)" }, { "3BFB1300FF10800031C164096441360F9000", "Truist Business Debit (Bank)" }, + { "3BFB1300FF10800031C164097861310F9000", "Deel Visa Business Debit Card (Bank)\nhttps://help.letsdeel.com/hc/en-gb/articles/4407737595409-What-is-the-Deel-Card" }, { "3BFB1300FF813180755A43352E3520524556204763", "ZeitControl BasicCard 5.5" }, { "3BFB1300FF813180755A43352E3620524556204D6A", "ZeitControl BasicCard ZC5.6 user-programmable smart card\nhttp://www.basiccard.com/index.html?overview.htm" }, { "3BFB1300FF813180755A43362E3520524556204364", "ZeitControl BasicCard 6.5, multiapplication with 30 kByte EEPROM" }, @@ -4059,6 +4213,7 @@ const static atr_t AtrTable[] = { { "3BFB9600008131FE450031E85427E60100079000BC", "Gemalto (PKI)" }, { "3BFB9600008131FE4556445349354001000400011F", "Vasco DIGIPASS KEY 200 usb token\nhttp://www.vasco.com/products/digipass/digipass_pki/digipass_pki_keys/digipass_key_200.aspx\nShould contain a 'Oberthur cosmo v 5.4 or V7.0D' smartcard" }, { "3BFB9800FFC11031FE550064052047033180009000F3", "Gemplus GemGate 32K\ndistributed by Postecert (www.postecert.it) to legally sign documents" }, + { "3BFC130000108081000300203002220100140C", "SberBank card (Bank)" }, { "3BFC1300008131FE15597562696B65794E454F7233E1", "YubiKey NEO (PKI)\nhttp://www.yubico.com/" }, { "3BFC1300008131FE45597562696B65794E454F7233B1", "Yubikey Neo\nhttp://www.yubico.com/products/yubikey-hardware/yubikey-neo/" }, { "3BFC180000813180459067464A00641606F2727E00E0", "PIVKey C910 PKI Smart Card (eID)\nhttp://pivkey.com/" }, @@ -4082,6 +4237,7 @@ const static atr_t AtrTable[] = { { "3BFD1300008131FE4580318153534431738421C0810730", "Personal Info Card (eID)" }, { "3BFD1300FF10000031C173C8400052A1C5009000", "IBKR Prepaid MasterCard, Issued by Peoples Trust Company (Bank)\nhttps://www.interactivebrokers.com/en/index.php?f=26451" }, { "3BFD1300FF10000031C173C8400052A1D5009000", "PayPal Business Debit Mastercard (Bank)\nhttps://www.paypal.com/merchantapps/appcenter/makepayments/bdmc" }, + { "3BFD1300FF10000031C173C8400052A37B009000", "Payoneer Card - USD Currency (Bank)\nhttps://payoneer.custhelp.com/app/answers/detail/a_id/18226/~/account-with-card---faq" }, { "3BFD1800008031FE45003180718E6452D904008190005B", "Oberthur Card Systems, authentIC" }, { "3BFD1800008031FE4553434536302D43443038312D46C4", "Panama Electronic Signature (JavaCard)" }, { "3BFD1800008031FE45736674652063643134342D6E66D8", "SmartCafe Expert 3.2 144K Dual is a contact and contactless technology Java card from G&D with 144K on-board EEPROM for application and data storage. Certified to FIPS 140-2 Level 3 and Common Criteria EAL 5+. Supports specifications ISO 14443A T=CL and ISO 7816 T=1/0. (PKI)\nhttp://www.smartcardfocus.us/shop/ilp/id~523/smartcafe-expert-3-2-144k-dual/p/index.shtml" }, @@ -4176,10 +4332,12 @@ const static atr_t AtrTable[] = { { "3BFF1300FF10000031C173C8211064414D3348079000", "BBVA blue VISA Debit Card (Bank)\nhttps://www.bbva.es/en/personas/productos/tarjetas/tarjeta-joven-ahora.html\nDesjardins Bonus Visa credit card (Bank)\nhttps://www.desjardins.com/ca/personal/loans-credit/credit-cards/bonus-visa/index.jsp" }, { "3BFF1300FF10000031C173C8211064414D3430079000", "PNC BUSINESS VISA DEBIT (Bank)\nhttps://www.pnc.com/en/small-business/payments-and-processing/payment-cards/pnc-bank-visa-business-debit-card.html" }, { "3BFF1300FF10000031C173C8211064414D3531079000", "Discover It Credit Card (Bank)" }, + { "3BFF1300FF10000031C173C82110644230434E079000", "Unicredit debit Card MasterCard (Italy) (Bank)" }, { "3BFF1300FF10000031C173C82110644930424E079000", "National Bank Debit Card with expiration date and cvv code (Bank)" }, { "3BFF1300FF10000031C173C82110644932424E079000", "Interact, Visa Debit Bank of Novia Scotia (Bank)\nhttps://www.scotiabank.com/global/en/credit-card-terms-and-conditions.html" }, { "3BFF1300FF10000031C173C82110644D30424E079000", "Debit payment card (Rabobank NL) (Bank)\nhttps://www.rabobank.nl/en/business/making-and-receiving-payments/payments/paying-with-your-bank-card" }, { "3BFF1300FF10000031C173C82110644D30434E079000", "Huntington (Bank)" }, + { "3BFF1300FF10000031C173C82110645530434E079000", "caixa bank debit card (Bank)" }, { "3BFF1300FF10000031C173C82110645630424E079000", "Rabobank Netherlands VISA debit (Bank)" }, { "3BFF1300FF10000031C173C82110645631424E079000", "Portuguese 'BancoCTT' Bank Card (Bank)\nhttps://www.bancoctt.pt/o-seu-dia-a-dia/cartao-de-credito-banco-ctt" }, { "3BFF1300FF10000031C173C82110645631434E079000", "Chase Freedom Unlimited Credit Card (Bank)" }, @@ -4194,9 +4352,12 @@ const static atr_t AtrTable[] = { { "3BFF1300FF8131FE45656311045002800008540004230502A5", "Maestrocard/Geldkarte (Stadtsparkasse Haltern, Germany)" }, { "3BFF1300FF8131FE5D8025A00000005657444B33323005003F", "Datakey DCOS model 320" }, { "3BFF1300FF910131FE210031C173C82110644D30434E07900094", "AirPlus MasterCard Commercial (Bank)\nhttps://www.airplus.com/us/en/products-solutions/products/corporate-cards/corporate-cards.html" }, + { "3BFF1300FF910131FE210031C173C82110645630434E0790008F", "Girocard for ING bank in germany (Bank)\nhttps://www.ing.de/girokonto/" }, + { "3BFF1300FF910131FE413101F1564012002200000000000000EA", "JCB T-CARD PLUS (eID)\nhttps://www.aplus.co.jp/creditcard/use/tcardplus_t/index.html" }, { "3BFF1300FF910131FE4141434F5320486F6C6C7931204C633665", "OEAMTC Visa Club Card (Bank)\nhttps://www.oeamtc.at/mitgliedschaft/leistungen/die-oeamtc-kreditkarte-31091443" }, + { "3BFF1300FF910131FE4145504100000001068135890000000063", "Austrian Bank Card for kids, teens and young adults Bank\nhttps://www.sparkasse.at/sgruppe/spark7" }, { "3BFF1300FF910131FE41455041000000010833995600000000AC", "Austrian Sparkasse ISIC debit card (Mastercard) (eID)\nhttps://isic.at/" }, - { "3BFF1300FF918131FE4141434F532046696F6E6131204C6336F4", "Deutsche Kreditbank Debit (Bank)" }, + { "3BFF1300FF918131FE4141434F532046696F6E6131204C6336F4", "Deutsche Kreditbank Debit (Bank)\nBanca Intesa Visa card (Bank)" }, { "3BFF1300FF918131FE4141434F53204769756C6961204C6336B5", "revolut debit visa (Bank)\nhttps://www.revolut.com/" }, { "3BFF1300FF918131FE4541434F53204449616E6132204C6336DF", "Alior Bank MasterCard debit (Bank)\nComdirect (Deutsch Bank) debit VISA (AUSTRIACARD 56015/001) (Bank)" }, { "3BFF1400FF8131FE458025A000000056575343363530010039", "SafeNet SC650 (PKI)\nhttp://www.safenet-inc.com/data-protection/authentication/smartcard-650/" }, @@ -4207,11 +4368,13 @@ const static atr_t AtrTable[] = { { "3BFF1800008131FE45006B0405010001210143494510318048", "hybrid card for various health services and regional services (access to various organizations and digital signatures)" }, { "3BFF1800008131FE45006B04050100012101434E5310318059", "CNS - Carta Nazionale dei Servizi (Italia)\nPA emittente: Regione Autonoma della Sardegna\nCarta del Servizio Sanitario Regionale - Emilia Romagna" }, { "3BFF1800008131FE45006B05051017012101434E531031805E", "Regional Card - Regione Liguria, Veneto - Italy (eID)\nTessera Sanitaria - Carta Regionale dei Servizi" }, + { "3BFF1800008131FE45006B050520000112024850431031804C", "CARTA SISS (HealthCare)" }, { "3BFF1800008131FE45006B05052000012101434E5310318079", "health card (HealthCare)\nhttps://tscns.regione.sardegna.it/" }, { "3BFF1800008131FE45006B0505200001F101434E53103180A9", "national health service card (HealthCare)\nhttps://ca.arubapec.it/downloads/MU_LINUX.zip" }, { "3BFF1800008131FE45006B0505912001F101434E5310318038", "Italian Health Card (TS) and Citizen's Card (CNS) based on IDEMIA ID-One CNS v2 on Cosmo 9.1 (HealthCare)" }, { "3BFF1800008131FE45006B11050700011101434E531131807B", "Italian National Fire Corps -special identification card (eID)" }, { "3BFF1800008131FE45006B11050700012101434E531031804A", "Oberthur ID-One Cosmo V7-n it's a java card 2.2.2\nIzenpe Certificado Ciudadano (eID)\nhttps://www.izenpe.eus/informacion/certificado-ciudadano/s15-content/es/" }, + { "3BFF1800008131FE45006B150C03020101014234441031800D", "bit4id (PKI)" }, { "3BFF1800008131FE45006B150C0302010101434E5310318061", "Bit4id Digital-DNA Key (eID)" }, { "3BFF1800008131FE4D8025A00000005657444B3430300600DD", "DataKey 400 (DK400)" }, { "3BFF1800008131FE55006B02090403010101434E5310318065", "Italian Chambers of Commerce CNS (PKI)\nhttp://www.card.infocamere.it/infocard/pub/" }, @@ -4239,13 +4402,19 @@ const static atr_t AtrTable[] = { { "3BFF1800FF8131FE4165630608710156000FB81026204712CD", "Fyrst Bank Card (Bank)\nhttps://fyrst.de" }, { "3BFF1800FF8131FE4165630608710156000FB85073204712D8", "Commerzbank maestro (Bank)\nhttps://www.commerzbank.de/konten-zahlungsverkehr/produkte/girokonten/kostenloses-girokonto/" }, { "3BFF1800FF8131FE4165630608710156000FB8602AA0471231", "Debit card (Germany): Postbank - GeldKarte (EUR), girocard, V-PAY (Bank)\nhttps://www.postbank.de/" }, + { "3BFF1800FF8131FE4165630608710156000FB8C040C047129B", "Maestro Bank card (aka. Giro card) of the Kreissparkasse (German bank) (Bank)" }, { "3BFF1800FF8131FE4165630608710156000FB8C0442147127E", "Commerzbank Classic Kreditkarte Mastercard (Bank)\nhttps://www.commerzbank.de/konten-zahlungsverkehr/produkte/kreditkarten/classic-kreditkarte/" }, { "3BFF1800FF8131FE4165630608710156000FB8D044A04712EF", "Debitcard (Bank)" }, + { "3BFF1800FF8131FE4165630608710156000FB8D09B604712F0", "Kreissparkasse Girocard (Germany) (Bank)" }, + { "3BFF1800FF8131FE4165630608710156000FB8F0BD204712B6", "Postbank Girocard Vpay Debit (Bank)\nhttps://postbank.de/privatkunden/services.html" }, { "3BFF1800FF8131FE41656306087102500023B80080C04712B2", "1822direct Bank Card (Bank)\nhttps://www.1822direkt.de" }, { "3BFF1800FF8131FE41656306087102500023B8907360471271", "Debit card (Germany): Deutsche Kreditbank (DKB), ec-cash, (Bank)\nhttps://www.dkb.de/privatkunden/karten/girocard" }, { "3BFF1800FF8131FE4165631116710156000F0308B09957115B", "Debit card Sparkasse (Germany) (Bank)" }, + { "3BFF1800FF8131FE4165631116710156000F030CD0435711E5", "Commerzbank AG (Bank)\nhttps://www.commerzbank.de/portal/de/privatkunden/produkte/bezahlen/kreditkarten" }, { "3BFF1800FF8131FE4165631116710156000F0902904E5711AC", "German Bank Card IDEMIA 9 Maestro/Girocard (Sparkasse S-Payment TPY 1974693D) (Bank)" }, { "3BFF1800FF8131FE4165631116710156000F0908309A5711D2", "Bank card from German Bank 'Sparkasse', issued by manufacturer 'S-Payment GmbH' (Bank)" }, + { "3BFF1800FF8131FE4165631116710156000F1606A09457115D", "GLS Bank MasterCard Debit (Bank)\nhttps://www.gls.de/konten-karten/karten/bankcard/" }, + { "3BFF1800FF8131FE4165631116710156000F1607F08D571115", "comdirect girocard (Bank)" }, { "3BFF1800FF8131FE4165631116710156000F16082024571163", "German Sparkasse with visa (Bank)\nhttps://www.sparkasse.de/lp/echtesmultitalent.html#alle-funktionen" }, { "3BFF1800FF8131FE450031C573C00180547615020105900074", "SIGILANCE NFC OpenPGP Smart Card (JavaCard)\nhttps://www.sigilance.com/" }, { "3BFF1800FF8131FE455448434331305445434F4744484E3224", "National Health Insurance Card, Taiwan" }, @@ -4285,6 +4454,7 @@ const static atr_t AtrTable[] = { { "3BFF1800FF8131FE4565631108660280001156000318062092", "Geldkarte [ec, Maestro] (Sparkasse Langen-Seligenstadt, Germany)" }, { "3BFF1800FF8131FE4565631901500280000F........0512..", "SAGEM ORGA GmbH\nROM Mask=SecV1.5.3\nInit-Table=SDR0O1G0.A_B (BES0), SWR0O1H0.A_5 (CS0)\nSignaturerstellungseinheit ZKA SECCOS Sig v1.5.3\nBSI.02076.TE.12.2006" }, { "3BFF1800FF8131FE4565631A01410250001052090567051021", "Maestro/Geldkarte (BBBank Karlsruhe, Germany)" }, + { "3BFF1800FF8131FE4D006B0404B85B01F101434E531031809D", "Italian CNS (HealthCare)" }, { "3BFF1800FF8131FE55006B02090200010101434E531031809F", "Carta Nazionale dei Servizi - InfoCamere" }, { "3BFF1800FF8131FE55006B0209020001010144534410318092", "Postcom S.P.A. (digital certificate)" }, { "3BFF1800FF8131FE55006B02090200011101434E531031808F", "Carta Regionale dei Servizi - Regione Lombardia" }, @@ -4299,7 +4469,7 @@ const static atr_t AtrTable[] = { { "3BFF1800FF8131FE55006B0209130301000150534510318094", "Italian Electronic ID Card (eID)\nhttp://www.interno.gov.it/mininterno/site/it/temi/servizi_demografici/scheda_006.html" }, { "3BFF1800FF8131FE55006B02091303010101434E531031808D", "Aruba Digital Signature (Other)\nhttps://www.pec.it/offerta-firma-digitale.aspx" }, { "3BFF1800FF8131FE55006B02091303011101434E531131809C", "Politecnico di Torino Student Card (eID)\nhttp://www.polito.it/" }, - { "3BFF1800FF8131FE55006B02091617011101434E531131808D", "Carta Regionale dei Servizi - Regione Autonoma Friuli Venezia Giulia (HealthCare)\nhttps://www.regione.fvg.it/rafvg/cms/RAFVG/GEN/carta-regionale-servizi/" }, + { "3BFF1800FF8131FE55006B02091617011101434E531131808D", "Tessera Sanitaria - Carta Nazionale dei Servizi (TS-CNS)\nItalian Health Insurance Card (healthcare)" }, { "3BFF1800FF8131FE55006B02091717011101434E531131808C", "european health insurance card and Regional (ItalY - Provincia Autonoma di Trento) Service Card (CPS) (eID)\nhttps://www.provincia.tn.it/Servizi/Attivare-la-Carta-Provinciale-dei-Servizi-CPS#cos_e" }, { "3BFF1800FF8131FE55006B42495434494420312E3000900091", "Touch&Sign 2048 (PKI)" }, { "3BFF1800FF8131FE55006B42495434494420322E3000900092", "Izenpe Green Card (Citizen Certificate) (eID)\nhttp://www.izenpe.com/s15-12020/en/contenidos/informacion/ciudadano/en_def/index.shtml" }, @@ -4366,15 +4536,22 @@ const static atr_t AtrTable[] = { { "3BFF9600008131FE4380318065B085040120120FFF829000D0", "Portuguese National Identity Card (eID) (eID)\nhttps://www.autenticacao.gov.pt/o-cartao-de-cidadao" }, { "3BFF9600008131FE4380318065B085050011120FFF829000E1", "Portuguese autentication card (eID)\nhttps://www.autenticacao.gov.pt/web/guest/cc-aplicacao" }, { "3BFF9600008131FE4380318065B08505003912017882900040", "Identicard for french advocates (eID)\nhttps://doc.ubuntu-fr.org/avocats_sur_ubuntu" }, + { "3BFF9600008131FE4380318065B085050039120FFE829000C8", "SafeNet IDPrime 3940C (PKI)\nhttps://cpl.thalesgroup.com/resources/access-management/safenet-idprime-940c-3940c-smart-cards-product-brief" }, { "3BFF9600008131FE4380318065B0855956FB12017882900088", "SafeNet 5110 token for eSignature (eID)\nhttps://www.certsign.ro/en/support/safenet-installing-the-device-on-windows/" }, { "3BFF9600008131FE4380318065B0855956FB120FFC82900002", "THALES SafeNet IDPrime 3940 Fido (PKI)\nhttps://cpl.thalesgroup.com/fr/resources/access-management/idprime-3940-product-brief" }, { "3BFF9600008131FE4380318065B0855956FB120FFE82900000", "SafeNet eToken 5110 SC (PKI)\nhttps://cpl.thalesgroup.com/access-management/authenticators/pki-usb-authentication/etoken-5110-usb-token" }, { "3BFF9600008131FE4580F9A0000003080000100053454E54AC", "cac (eID)" }, { "3BFF9600008131FE55006B02090403010101434E53103180EB", "Aruba PEC SpA digital signature card made by Incard (eID)\nhttps://www.pec.it/download-software-driver.aspx" }, { "3BFF960000C00A31FE4380318065B085040011120FFF829000AB", "French National Identity Card (eID) (eID)\nhttps://www.interieur.gouv.fr/actualites/actu-du-ministere/nouvelle-carte-nationale-didentite" }, + { "3BFF9600FF8131FE406563111562025000100A0169C9073026", "Kreissparkasse Mastercard (Germany) (Bank)" }, { "3BFF9600FF8131FE406563111562025000100A0190A90730BF", "girocard Sparkasse Ansbach, Germany BLZ 76550000 (Bank)" }, + { "3BFF9600FF8131FE406563111562025000100A01BF860730BF", "German Bank Verbundvolksbank-OWL Bank" }, { "3BFF9600FF8131FE406563111562025000100A0271500730A4", "Debitcard Sparkasse Duesseldorf (Bank) (Bank)\nhttps://www.sskduesseldorf.de/" }, { "3BFF9600FF8131FE406563111665025000100B22BBEB074080", "girocard contactless (Bank)" }, + { "3BFF9600FF8131FE406563111665025000100B238131074061", "Debit card (Germany): girocard, V-Pay (Bank)" }, + { "3BFF9600FF8131FE406563111665025000100B23B7B20740D4", "Hannoversche Volksbank girocard / Visa Debit Card (Bank)\nhttps://www.hannoversche-volksbank.de/privatkunden/girokonto-kreditkarten/girocards/hannover-girocard.html" }, + { "3BFF9600FF8131FE406563111665025000100B246D780740C3", "Mastercard Debit Card from the Kreissparkasse (German bank) (Bank)" }, + { "3BFF9600FF8131FE4065631D02840156001F1007C0BC020024", "sparda-bank (Bank)" }, { "3BFF9600FF8131FE4065631D02840156001F190850E10200EF", "Raiffeiesenbank Girocard Maestro (Bank)" }, { "3BFF9600FF8131FE4065631D02840156001F2108B0A902007F", "Debit Card Sparda-Bank Baden-Wurttemberg eG (Bank)" }, { "3BFF9600FF8131FE4065631D028401560024090A10CC0200AB", "Postbank Germany (Bank)\nhttps://www.postbank.de/privatkunden/services.html" }, @@ -4388,6 +4565,9 @@ const static atr_t AtrTable[] = { { "3BFF9600FF8131FE4065631D0284025000232106F0ED02004C", "DKB Girocard (Bank)" }, { "3BFF9600FF8131FE4065631D028402500023230900A80200F4", "Kreissparkasse girocard (Bank)" }, { "3BFF9600FF8131FE4065631D038601560002130B90FE011034", "EC card from Raiffeisenbank im Hochtaunus, Germany (Bank)" }, + { "3BFF9600FF8131FE4065631D03860156000220093138011062", "GLS Bank MasterCard (Bank)\nhttps://www.gls.de/konten-karten/karten/kreditkarte/" }, + { "3BFF9600FF8131FE4065631D038601560002210B612401102D", "Girocard (Bank)\nhttps://www.muenchner-bank.de" }, + { "3BFF9600FF8131FE4065631D038602500023020B20DD011092", "DKB Visa (Debit) (Bank)\nhttps://www.dkb.de/" }, { "3BFF9600FF8131FE4065631D0386025000230808914F0110B8", "Debit card (Germany): ec-cash, GeldKarte(EUR), Visa, Cirrus (Bank)" }, { "3BFF9600FF8131FE4065631D038602500023130981390110C4", "girocard contactless (Bank)" }, { "3BFF9600FF8131FE456563060752025000103025411A064082", "DKB (Deutsche Kreditbank) girocard (V-PAY, GeldKarte) (Bank)\nhttps://www.dkb.de/privatkunden/karten/girocard" }, @@ -4419,6 +4599,7 @@ const static atr_t AtrTable[] = { { "3F2F008069AF03070352000D0A0E833E9F16", "GSM-SIM Debitel D2 (900MHz)" }, { "3F2F008069AF0307035A00150A0E833E9F16", "Virgin Mobile SIM (Gemplus)" }, { "3F36110053495B015153", "Sodexo Pass Lunch Card. An employee benefits card to provide meal tickets to workers. (Other)\nhttps://www.sodexo-benefits.it/prodotto/aziende/pausa-pranzo-aziende/pass-lunch-card/#tabsoluzioni" }, + { "3F3B11004B494154325F5253079000", "singe-use Smart Card from Indesit/Whirlpool containing dishwasher EEPROM data (spare part no C00277010, containing the file 28655190905.eep) (Other)\nhttps://fixpart.co.uk/product/whirlpool-indesit-c00277010-programming-card" }, { "3F3BF81300008131FE454A434F5076", "District6 Group employee ID (eID)" }, { "3F3D1100806728500402200000838E9000", "GSM SIM card of the Austrian provider A1" }, { "3F3E110046524543434941524F5353419000", "Trenitalia (Italy) fidelity card 'CartaFreccia' (Smartcard)" }, @@ -4499,6 +4680,7 @@ const static atr_t AtrTable[] = { { "3F7F13250240B01269FF4A509054560000000000", "NDS Smartcard (Pay TV)" }, { "3F7F13250241B004FFFF4A508080000000475806", "NDS card DIRECTV (Other)" }, { "3F7F13250241B00EFFFF4A508080000000474C07", "SKY BRASIL (Pay TV)" }, + { "3F7F13250241B00EFFFF4A508080000000485507", "DirecTV Access Card (as of 2016) (Pay TV)" }, { "3F7F13250333B00669FF4A50D000005359000000", "Sky 2005/6 (DSS satellite TV card)" }, { "3F7F13250333B01169FF4A505000004956010000", "Indonesia Videoguard 2 card" }, { "3F7F13250333B01169FF4A505000005344010000", "STAR TV (Pay TV)" }, @@ -4554,6 +4736,7 @@ const static atr_t AtrTable[] = { { "3FFF142503108041B00769FF4A5070800058440100FF", "Provider Vivacom Bulgaria NDS (Pay TV)\nhttp://www.vivacom.bg/en/satellite-services" }, { "3FFF142503108041B00769FF4A507080005845010014", "Sat TV (Other)" }, { "3FFF142503108054B00169FF4A507000004B57010000", "PayTV Card Kabel BW (www.kabelbw.de), Encryption: NDS by Videoguard, Distribution Standard: DVB-C" }, + { "3FFF152503108041B00769FF4A507000005031010011", "V13 or V14 (NDS) (Pay TV)" }, { "3FFF152503108041B00769FF4A507000005031010015", "Sky (Germany) VideoGuard CAM card (www.sky.de) in Fast Mode (ins7e11=15) (Pay TV)" }, { "3FFF3F3F3F3F003F3FFF3F3F3F3F3FFF3FFF953FFF953FFF", "Premium joker card to see Spanish TDT premium (goltv)" }, { "3FFF9500FF918171..4700..4.4.....3.3.3.20..657.........", "Nagravision TV CAM card\nhttp://en.wikipedia.org/wiki/Nagravision" }, @@ -4589,6 +4772,7 @@ const static atr_t AtrTable[] = { { "3FFF9500FF918171FE5700444E4153503431302052657651323715", "New Digi Slovakia (Pay TV)\nhttps://www.lyngsat.com/packages/Digi.html" }, { "3FFF9500FF918171FE5700444E4153503431302052657651324260", "Nagravision Kudelski Generation 7 card Rom410 MerQ2B (Pay TV)" }, { "3FFF9500FF918171FE5700444E4153503431302052657651325371", "Slovak and Czech pay TV provider Slovak Telecom (Pay TV)\nhttp://www.flysat.com/novadigi-sk.php" }, + { "3FFF9500FF918171FE5700444E4153503432302052657653323615", "bank comercial do huambo (Bank)" }, { "3FFF9500FF918171FE5700444E4153503432302052657653363017", "HD+ HD04b Card (Pay TV)" }, { "3FFF9500FF918171FE5700444E4153503432302052657653363413", "claro card honduras central america 'NAGRA' (Pay TV)" }, { "3FFF9500FF918171FE5700444E4153503432302052657653364166", "NAGRA KUDELSKI (Pay TV)" }, diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index fa88549f7..607a36b31 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -192,21 +192,24 @@ static int CmdFlashMemLoad(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "mem load", "Loads binary file into flash memory on device\n" - "Warning: mem area to be written must have been wiped first\n" - "( dictionaries are serviced as files in spiffs so no wipe is needed )", - "mem load -f myfile -> upload file myfile values at default offset 0\n" - "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n" - "mem load -f mfc_default_keys -m -> upload MFC keys\n" - "mem load -f t55xx_default_pwds -t -> upload T55XX passwords\n" - "mem load -f iclass_default_keys -i -> upload iCLASS keys\n" + "Warning! - mem area to be written must have been wiped first\n\n" + "OBS! - dictionaries are serviced as files in spiffs so no wipe is needed", + "mem load -f myfile -> upload file myfile values at default offset 0\n" + "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024\n" + "mem load -f mfc_default_keys -m -> upload MIFARE Classic keys\n" + "mem load -f t55xx_default_pwds -t -> upload T55XX passwords\n" + "mem load -f iclass_default_keys -i -> upload iCLASS keys\n" + "mem load -f mfulc_default_keys --ulc -> upload MIFARE UL-C keys\n" ); void *argtable[] = { arg_param_begin, arg_int0("o", "offset", "", "offset in memory"), - arg_lit0("m", "mifare,mfc", "upload 6 bytes keys (mifare key dictionary)"), - arg_lit0("i", "iclass", "upload 8 bytes keys (iClass key dictionary)"), - arg_lit0("t", "t55xx", "upload 4 bytes keys (password dictionary)"), + arg_lit0("m", "mfc", "upload 6 bytes keys (MIFARE Classic dictionary)"), + arg_lit0("i", "iclass", "upload 8 bytes keys (iClass dictionary)"), + arg_lit0("t", "t55xx", "upload 4 bytes keys (T55xx dictionary)"), + arg_lit0(NULL, "ulc", "upload 16 bytes keys (MIFARE UL-C dictionary)"), + arg_lit0(NULL, "aes", "upload 16 bytes keys (MIFARE UL-AES dictionary)"), arg_str1("f", "file", "", "file name"), arg_param_end }; @@ -216,28 +219,35 @@ static int CmdFlashMemLoad(const char *Cmd) { bool is_mfc = arg_get_lit(ctx, 2); bool is_iclass = arg_get_lit(ctx, 3); bool is_t55xx = arg_get_lit(ctx, 4); + bool is_ulc = arg_get_lit(ctx, 5); + bool is_ulaes = arg_get_lit(ctx, 6); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - char spiffsDest[32] = {0}; - CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); Dictionary_t d = DICTIONARY_NONE; if (is_mfc) { d = DICTIONARY_MIFARE; - PrintAndLogEx(INFO, "treating file as MIFARE Classic keys"); + PrintAndLogEx(INFO, "Treating file as MIFARE Classic keys"); } else if (is_iclass) { d = DICTIONARY_ICLASS; - PrintAndLogEx(INFO, "treating file as iCLASS keys"); + PrintAndLogEx(INFO, "Treating file as iCLASS keys"); } else if (is_t55xx) { d = DICTIONARY_T55XX; - PrintAndLogEx(INFO, "treating file as T55xx passwords"); + PrintAndLogEx(INFO, "Treating file as T55xx passwords"); + } else if (is_ulc) { + d = DICTIONARY_MIFARE_ULC; + PrintAndLogEx(INFO, "Treating file as MIFARE Ultralight-C keys"); + } else if (is_ulaes) { + d = DICTIONARY_MIFARE_ULAES; + PrintAndLogEx(INFO, "Treating file as MIFARE Ultralight AES keys"); } uint8_t spi_flash_pages = 0; int res = rdv4_get_flash_pages64k(&spi_flash_pages); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "failed to get flash pages count (%x)", res); + PrintAndLogEx(ERR, "Failed to get flash pages count (%x)", res); return res; } @@ -246,6 +256,8 @@ static int CmdFlashMemLoad(const char *Cmd) { uint8_t keylen = 0; uint8_t *data = calloc(FLASH_MEM_MAX_SIZE_P(spi_flash_pages), sizeof(uint8_t)); + char spiffsDest[32] = {0}; + switch (d) { case DICTIONARY_MIFARE: { keylen = MF_KEY_LENGTH; @@ -292,6 +304,36 @@ static int CmdFlashMemLoad(const char *Cmd) { strcpy(spiffsDest, ICLASS_KEYS_FILE); break; } + case DICTIONARY_MIFARE_ULC: { + keylen = MFULC_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); + if (res || !keycount) { + free(data); + return PM3_EFILE; + } + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; + } + strcpy(spiffsDest, MFULC_KEYS_FILE); + break; + } + case DICTIONARY_MIFARE_ULAES: { + keylen = MFULAES_KEY_LENGTH; + res = loadFileDICTIONARY(filename, data, &datalen, keylen, &keycount); + if (res || !keycount) { + free(data); + return PM3_EFILE; + } + if (datalen > FLASH_MEM_MAX_SIZE_P(spi_flash_pages)) { + PrintAndLogEx(ERR, "error, filesize is larger than available memory"); + free(data); + return PM3_EOVFLOW; + } + strcpy(spiffsDest, MFULAES_KEYS_FILE); + break; + } case DICTIONARY_NONE: { res = loadFile_safe(filename, ".bin", (void **)&data, &datalen); if (res != PM3_SUCCESS) { @@ -330,7 +372,12 @@ static int CmdFlashMemLoad(const char *Cmd) { free(data); return res; } - PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest); + + if (d == DICTIONARY_T55XX) { + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" passwords to file "_GREEN_("%s"), keycount, spiffsDest); + } else { + PrintAndLogEx(SUCCESS, "Wrote "_GREEN_("%u")" keys to file "_GREEN_("%s"), keycount, spiffsDest); + } SendCommandNG(CMD_SPIFFS_UNMOUNT, NULL, 0); SendCommandNG(CMD_SPIFFS_MOUNT, NULL, 0); } else { @@ -729,6 +776,7 @@ static int CmdFlashMemInfo(const char *Cmd) { static command_t CommandTable[] = { {"spiffs", CmdFlashMemSpiFFS, IfPm3Flash, "{ SPI File system }"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, IfPm3Flash, "------------------- " _CYAN_("Operations") " -------------------"}, {"baudrate", CmdFlashmemSpiBaud, IfPm3Flash, "Set Flash memory Spi baudrate"}, {"dump", CmdFlashMemDump, IfPm3Flash, "Dump data from flash memory"}, {"info", CmdFlashMemInfo, IfPm3Flash, "Flash memory information"}, diff --git a/client/src/cmdflashmem.h b/client/src/cmdflashmem.h index ad8727204..f47547166 100644 --- a/client/src/cmdflashmem.h +++ b/client/src/cmdflashmem.h @@ -26,7 +26,9 @@ typedef enum { DICTIONARY_NONE = 0, DICTIONARY_MIFARE, DICTIONARY_T55XX, - DICTIONARY_ICLASS + DICTIONARY_ICLASS, + DICTIONARY_MIFARE_ULC, + DICTIONARY_MIFARE_ULAES, } Dictionary_t; int CmdFlashMem(const char *Cmd); diff --git a/client/src/cmdflashmemspiffs.c b/client/src/cmdflashmemspiffs.c index 8149236af..e2d5417ff 100644 --- a/client/src/cmdflashmemspiffs.c +++ b/client/src/cmdflashmemspiffs.c @@ -576,10 +576,11 @@ static int CmdFlashMemSpiFFSView(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, IfPm3Flash, "------------------- " _CYAN_("Operations") " -------------------"}, {"copy", CmdFlashMemSpiFFSCopy, IfPm3Flash, "Copy a file to another (destructively) in SPIFFS file system"}, {"check", CmdFlashMemSpiFFSCheck, IfPm3Flash, "Check/try to defrag faulty/fragmented file system"}, {"dump", CmdFlashMemSpiFFSDump, IfPm3Flash, "Dump a file from SPIFFS file system"}, - {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "Print file system info and usage statistics"}, + {"info", CmdFlashMemSpiFFSInfo, IfPm3Flash, "File system information and usage statistics"}, {"mount", CmdFlashMemSpiFFSMount, IfPm3Flash, "Mount the SPIFFS file system if not already mounted"}, {"remove", CmdFlashMemSpiFFSRemove, IfPm3Flash, "Remove a file from SPIFFS file system"}, {"rename", CmdFlashMemSpiFFSRename, IfPm3Flash, "Rename/move a file in SPIFFS file system"}, diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index ea0c47993..13c10d863 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -231,60 +231,60 @@ int CmdHFSearch(const char *Cmd) { } */ + DropField(); + PROMPT_CLEARLINE; if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, _RED_("No known/supported 13.56 MHz tags found")); - res = PM3_ESOFT; - } else { - - // no need to print 14A hints, since it will print itself - - if (success[THINFILM]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf thinfilm") "` commands\n"); - } - - if (success[LTO]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf lto") "` commands\n"); - } - - if (success[LEGIC]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf legic") "` commands\n"); - } - - if (success[TOPAZ]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf topaz") "` commands\n"); - } - - if (success[PROTO_TEXKOM]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf texkom") "` commands\n"); - } - - if (success[PROTO_XEROX]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf xerox") "` commands\n"); - } - - if (success[ISO_14443B]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 14b") "` commands\n"); - } - - if (success[ISO_15693]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 15") "` commands\n"); - } - - if (success[ICLASS]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass") "` commands\n"); - } - - if (success[FELICA]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf felica") "` commands\n"); - } - - if (success[PROTO_CRYPTORF]) { - PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf cryptorf") "` commands\n"); - } + return res; + } + + // no need to print 14A hints, since it will print itself + + if (success[THINFILM]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf thinfilm") "` commands\n"); + } + + if (success[LTO]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf lto") "` commands\n"); + } + + if (success[LEGIC]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf legic") "` commands\n"); + } + + if (success[TOPAZ]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf topaz") "` commands\n"); + } + + if (success[PROTO_TEXKOM]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf texkom") "` commands\n"); + } + + if (success[PROTO_XEROX]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf xerox") "` commands\n"); + } + + if (success[ISO_14443B]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 14b") "` commands\n"); + } + + if (success[ISO_15693]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf 15") "` commands\n"); + } + + if (success[ICLASS]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass") "` commands\n"); + } + + if (success[FELICA]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf felica") "` commands\n"); + } + + if (success[PROTO_CRYPTORF]) { + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf cryptorf") "` commands\n"); } - DropField(); return res; } @@ -477,7 +477,7 @@ int CmdHFSniff(const char *Cmd) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(INFO, "User aborted"); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); break; } @@ -600,7 +600,7 @@ static command_t CommandTable[] = { {"texkom", CmdHFTexkom, AlwaysAvailable, "{ Texkom RFIDs... }"}, {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, - {"vas", CmdHFVAS, AlwaysAvailable, "{ Apple Value Added Service }"}, + {"vas", CmdHFVAS, AlwaysAvailable, "{ Apple Value Added Service... }"}, #ifdef HAVE_GD {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, #endif diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 3174cc47e..42bee9388 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -43,6 +43,8 @@ #include "mifare/desfirecore.h" // desfire context #include "mifare/mifaredefault.h" #include "preferences.h" // get/set device debug level +#include "pm3_cmd.h" + static bool g_apdu_in_framing_enable = true; bool Get_apdu_in_framing(void) { @@ -56,34 +58,6 @@ static int CmdHelp(const char *Cmd); static int waitCmd(bool i_select, uint32_t timeout, bool verbose); -static const iso14a_polling_frame_t WUPA_FRAME = { - { 0x52 }, 1, 7, 0, -}; - -static const iso14a_polling_frame_t MAGWUPA1_FRAME = { - { 0x7A }, 1, 7, 0 -}; - -static const iso14a_polling_frame_t MAGWUPA2_FRAME = { - { 0x7B }, 1, 7, 0 -}; - -static const iso14a_polling_frame_t MAGWUPA3_FRAME = { - { 0x7C }, 1, 7, 0 -}; - -static const iso14a_polling_frame_t MAGWUPA4_FRAME = { - { 0x7D }, 1, 7, 0 -}; - -static const iso14a_polling_frame_t ECP_FRAME = { - .frame = { 0x6a, 0x02, 0xC8, 0x01, 0x00, 0x03, 0x00, 0x02, 0x79, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xD8}, - .frame_length = 15, - .last_byte_bits = 8, - .extra_delay = 0 -}; - - // based on ISO/IEC JTC1/SC17 STANDING DOCUMENT 5 (Updated 20 September 2024) Register of IC manufacturers static const manufactureName_t manufactureMapping[] = { // ID, "Vendor Country" @@ -272,11 +246,54 @@ static uint16_t gs_frame_len = 0; static uint8_t gs_frames_num = 0; static uint16_t atsFSC[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; +int hf14a_getversion_data(iso14a_card_select_t *card, uint64_t select_status, version_hw_t *hw) { + + // field on, card selected if select_status is 1 or 4, not selected if 2 + int res = PM3_EFAILED; + + // if 4b UID or NXP, try to get version + if ((card->uidlen == 4) || ((card->uidlen == 7) && (card->uid[0] == 0x04))) { + // GetVersion + if ((select_status == 1) || (select_status == 4)) { // L4 + + uint8_t response[PM3_CMD_DATA_SIZE] = {0}; + int resp_len = 0; + uint8_t getVersion[5] = {0x90, 0x60, 0x00, 0x00, 0x00}; + res = ExchangeAPDU14a(getVersion, sizeof(getVersion), false, false, response, sizeof(response), &resp_len); + DropField(); + + if (res == PM3_ETIMEOUT) { + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); + return PM3_ETIMEOUT; + } + + if (resp_len == 9) { + memcpy(hw, response, sizeof(version_hw_t)); + return PM3_SUCCESS; + } + return PM3_EFAILED; + + } + + // select_status = 2, L3 + uint8_t version[8] = {0}; + uint8_t uid[7] = {0}; + res = mfu_get_version_uid(version, uid); + DropField(); + if (res == PM3_SUCCESS) { + memcpy(hw, version + 1, sizeof(version_hw_t)); + } + } + + DropField(); + return res; +} + static int CmdHF14AList(const char *Cmd) { return CmdTraceListAlias(Cmd, "hf 14a", "14a -c"); } -int hf14a_getconfig(hf14a_config *config) { +int hf14a_getconfig(hf14a_config_t *config) { if (!g_session.pm3_present) return PM3_ENOTTY; if (config == NULL) { @@ -291,16 +308,16 @@ int hf14a_getconfig(hf14a_config *config) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } - memcpy(config, resp.data.asBytes, sizeof(hf14a_config)); + memcpy(config, resp.data.asBytes, sizeof(hf14a_config_t)); return PM3_SUCCESS; } -int hf14a_setconfig(hf14a_config *config, bool verbose) { +int hf14a_setconfig(hf14a_config_t *config, bool verbose) { if (!g_session.pm3_present) return PM3_ENOTTY; clearCommandBuffer(); if (config != NULL) { - SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)config, sizeof(hf14a_config)); + SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)config, sizeof(hf14a_config_t)); if (verbose) { SendCommandNG(CMD_HF_ISO14443A_PRINT_CONFIG, NULL, 0); } @@ -333,6 +350,15 @@ static int hf_14a_config_example(void) { PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip -rats skip")); PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid --uid 04112233445566")); PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + + PrintAndLogEx(NORMAL, "\nExamples of polling loop annotations used to enable anticollision on mobile targets:"); + PrintAndLogEx(NORMAL, _CYAN_(" ECP Express Transit EMV")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --pla 6a02c801000300027900000000")); + PrintAndLogEx(NORMAL, _CYAN_(" ECP VAS Only")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --pla 6a01000002")); + PrintAndLogEx(NORMAL, _CYAN_(" ECP Access Wildcard")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --pla 6a02c3020002ffff")); + return PM3_SUCCESS; } static int CmdHf14AConfig(const char *Cmd) { @@ -341,25 +367,28 @@ static int CmdHf14AConfig(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a config", "Configure 14a settings (use with caution)\n" - " `-v` also prints examples for reviving Gen2 cards", - "hf 14a config -> Print current configuration\n" - "hf 14a config --std -> Reset default configuration (follow standard)\n" - "hf 14a config --atqa std -> Follow standard\n" - "hf 14a config --atqa force -> Force execution of anticollision\n" - "hf 14a config --atqa skip -> Skip anticollision\n" - "hf 14a config --bcc std -> Follow standard\n" - "hf 14a config --bcc fix -> Fix bad BCC in anticollision\n" - "hf 14a config --bcc ignore -> Ignore bad BCC and use it as such\n" - "hf 14a config --cl2 std -> Follow standard\n" - "hf 14a config --cl2 force -> Execute CL2\n" - "hf 14a config --cl2 skip -> Skip CL2\n" - "hf 14a config --cl3 std -> Follow standard\n" - "hf 14a config --cl3 force -> Execute CL3\n" - "hf 14a config --cl3 skip -> Skip CL3\n" - "hf 14a config --rats std -> Follow standard\n" - "hf 14a config --rats force -> Execute RATS\n" - "hf 14a config --rats skip -> Skip RATS"); - + " `-v` also prints examples for reviving Gen2 cards & configuring polling loop annotations", + "hf 14a config -> Print current configuration\n" + "hf 14a config --std -> Reset default configuration (follow standard)\n" + "hf 14a config --atqa std -> Follow standard\n" + "hf 14a config --atqa force -> Force execution of anticollision\n" + "hf 14a config --atqa skip -> Skip anticollision\n" + "hf 14a config --bcc std -> Follow standard\n" + "hf 14a config --bcc fix -> Fix bad BCC in anticollision\n" + "hf 14a config --bcc ignore -> Ignore bad BCC and use it as such\n" + "hf 14a config --cl2 std -> Follow standard\n" + "hf 14a config --cl2 force -> Execute CL2\n" + "hf 14a config --cl2 skip -> Skip CL2\n" + "hf 14a config --cl3 std -> Follow standard\n" + "hf 14a config --cl3 force -> Execute CL3\n" + "hf 14a config --cl3 skip -> Skip CL3\n" + "hf 14a config --rats std -> Follow standard\n" + "hf 14a config --rats force -> Execute RATS\n" + "hf 14a config --rats skip -> Skip RATS\n" + "hf 14a config --mag on -> Enable Apple magsafe polling\n" + "hf 14a config --mag off -> Disable Apple magsafe polling\n" + "hf 14a config --pla -> Set polling loop annotation (max 22 bytes)\n" + "hf 14a config --pla off -> Disable polling loop annotation\n"); void *argtable[] = { arg_param_begin, arg_str0(NULL, "atqa", "", "Configure ATQA<>anticollision behavior"), @@ -367,14 +396,18 @@ static int CmdHf14AConfig(const char *Cmd) { arg_str0(NULL, "cl2", "", "Configure SAK<>CL2 behavior"), arg_str0(NULL, "cl3", "", "Configure SAK<>CL3 behavior"), arg_str0(NULL, "rats", "", "Configure RATS behavior"), + arg_str0(NULL, "mag", "", "Configure Apple MagSafe polling"), + arg_str0(NULL, "pla", "", "Configure polling loop annotation"), arg_lit0(NULL, "std", "Reset default configuration: follow all standard"), arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool defaults = arg_get_lit(ctx, 6); + bool defaults = arg_get_lit(ctx, 8); + bool verbose = arg_get_lit(ctx, 9); + int vlen = 0; - char value[10]; + char value[64]; int atqa = defaults ? 0 : -1; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)value, sizeof(value), &vlen); if (vlen > 0) { @@ -435,12 +468,62 @@ static int CmdHf14AConfig(const char *Cmd) { return PM3_EINVARG; } } + int magsafe = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) magsafe = 0; + else if (strcmp(value, "skip") == 0) magsafe = 0; + else if (strcmp(value, "disable") == 0) magsafe = 0; + else if (strcmp(value, "off") == 0) magsafe = 0; + else if (strcmp(value, "enable") == 0) magsafe = 1; + else if (strcmp(value, "on") == 0) magsafe = 1; + else { + PrintAndLogEx(ERR, "magsafe argument must be 'std', 'skip', 'off', 'disable', 'on' or 'enable'"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + // Handle polling loop annotation parameter + iso14a_polling_frame_t pla = { + // 0 signals that PLA has to be disabled, -1 signals that no change has to be made + .frame_length = defaults ? 0 : -1, + .last_byte_bits = 8, + .extra_delay = 5 + }; + CLIParamStrToBuf(arg_get_str(ctx, 7), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strncmp((char *)value, "std", 3) == 0) pla.frame_length = 0; + else if (strncmp((char *)value, "skip", 4) == 0) pla.frame_length = 0; + else if (strncmp((char *)value, "disable", 3) == 0) pla.frame_length = 0; + else if (strncmp((char *)value, "off", 3) == 0) pla.frame_length = 0; + else { + // Convert hex string to bytes + int length = 0; + if (param_gethex_to_eol((char *)value, 0, pla.frame, sizeof(pla.frame), &length) != 0) { + PrintAndLogEx(ERR, "Error parsing polling loop annotation bytes"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + pla.frame_length = length; - bool verbose = arg_get_lit(ctx, 7); + // Validate length before adding CRC + if (pla.frame_length < 1 || pla.frame_length > 22) { + PrintAndLogEx(ERR, "Polling loop annotation length invalid: min %d; max %d", 1, 22); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t first, second; + compute_crc(CRC_14443_A, pla.frame, pla.frame_length, &first, &second); + pla.frame[pla.frame_length++] = first; + pla.frame[pla.frame_length++] = second; + PrintAndLogEx(INFO, "Added CRC16A to polling loop annotation: %s", sprint_hex(pla.frame, pla.frame_length)); + } + } CLIParserFree(ctx); - // validations + // Handle empty command if (strlen(Cmd) == 0) { return hf14a_setconfig(NULL, verbose); } @@ -449,12 +532,15 @@ static int CmdHf14AConfig(const char *Cmd) { hf_14a_config_example(); } - hf14a_config config = { + // Initialize config with all parameters + hf14a_config_t config = { .forceanticol = atqa, .forcebcc = bcc, .forcecl2 = cl2, .forcecl3 = cl3, - .forcerats = rats + .forcerats = rats, + .magsafe = magsafe, + .polling_loop_annotation = pla }; return hf14a_setconfig(&config, verbose); @@ -546,48 +632,12 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) { return PM3_SUCCESS; } -iso14a_polling_parameters_t iso14a_get_polling_parameters(bool use_ecp, bool use_magsafe) { - // Extra 100ms give enough time for Apple (ECP) devices to proccess field info and make a decision - - if (use_ecp && use_magsafe) { - iso14a_polling_parameters_t full_polling_parameters = { - .frames = { WUPA_FRAME, ECP_FRAME, MAGWUPA1_FRAME, MAGWUPA2_FRAME, MAGWUPA3_FRAME, MAGWUPA4_FRAME }, - .frame_count = 6, - .extra_timeout = 100 - }; - return full_polling_parameters; - } else if (use_ecp) { - iso14a_polling_parameters_t ecp_polling_parameters = { - .frames = { WUPA_FRAME, ECP_FRAME }, - .frame_count = 2, - .extra_timeout = 100 - }; - return ecp_polling_parameters; - } else if (use_magsafe) { - iso14a_polling_parameters_t magsafe_polling_parameters = { - .frames = { WUPA_FRAME, MAGWUPA1_FRAME, MAGWUPA2_FRAME, MAGWUPA3_FRAME, MAGWUPA4_FRAME }, - .frame_count = 5, - .extra_timeout = 0 - }; - return magsafe_polling_parameters; - } - - iso14a_polling_parameters_t wupa_polling_parameters = { - .frames = { WUPA_FRAME }, - .frame_count = 1, - .extra_timeout = 0, - }; - return wupa_polling_parameters; -} - static int CmdHF14AReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14a reader", "Act as a ISO-14443a reader to identify tag. Look for ISO-14443a tags until Enter or the pm3 button is pressed", "hf 14a reader\n" "hf 14a reader -@ -> Continuous mode\n" - "hf 14a reader --ecp -> trigger apple enhanced contactless polling\n" - "hf 14a reader --mag -> trigger apple magsafe polling\n" ); void *argtable[] = { @@ -596,8 +646,6 @@ static int CmdHF14AReader(const char *Cmd) { arg_lit0("s", "silent", "silent (no messages)"), arg_lit0(NULL, "drop", "just drop the signal field"), arg_lit0(NULL, "skip", "ISO14443-3 select only (skip RATS)"), - arg_lit0(NULL, "ecp", "Use enhanced contactless polling"), - arg_lit0(NULL, "mag", "Use Apple magsafe polling"), arg_lit0("@", NULL, "continuous reader mode"), arg_lit0("w", "wait", "wait for card"), arg_param_end @@ -620,18 +668,8 @@ static int CmdHF14AReader(const char *Cmd) { cm |= ISO14A_NO_RATS; } - bool use_ecp = arg_get_lit(ctx, 5); - bool use_magsafe = arg_get_lit(ctx, 6); - - iso14a_polling_parameters_t *polling_parameters = NULL; - iso14a_polling_parameters_t parameters = iso14a_get_polling_parameters(use_ecp, use_magsafe); - if (use_ecp || use_magsafe) { - cm |= ISO14A_USE_CUSTOM_POLLING; - polling_parameters = ¶meters; - } - - bool continuous = arg_get_lit(ctx, 7); - bool wait = arg_get_lit(ctx, 8); + bool continuous = arg_get_lit(ctx, 5); + bool wait = arg_get_lit(ctx, 6); CLIParserFree(ctx); bool found = false; @@ -646,12 +684,7 @@ static int CmdHF14AReader(const char *Cmd) { int res = PM3_SUCCESS; do { clearCommandBuffer(); - - if ((cm & ISO14A_USE_CUSTOM_POLLING) == ISO14A_USE_CUSTOM_POLLING) { - SendCommandMIX(CMD_HF_ISO14443A_READER, cm, 0, 0, (uint8_t *)polling_parameters, sizeof(iso14a_polling_parameters_t)); - } else { - SendCommandMIX(CMD_HF_ISO14443A_READER, cm, 0, 0, NULL, 0); - } + SendCommandMIX(CMD_HF_ISO14443A_READER, cm, 0, 0, NULL, 0); if ((cm & ISO14A_CONNECT) == ISO14A_CONNECT) { PacketResponseNG resp; @@ -847,6 +880,7 @@ int CmdHF14ASim(const char *Cmd) { "hf 14a sim -t 10 -> ST25TA IKEA Rothult\n" "hf 14a sim -t 11 -> Javacard (JCOP)\n" "hf 14a sim -t 12 -> 4K Seos card\n" + "hf 14a sim -t 13 -> MIFARE Ultralight C" ); void *argtable[] = { @@ -857,6 +891,8 @@ int CmdHF14ASim(const char *Cmd) { arg_lit0("x", NULL, "Performs the 'reader attack', nr/ar attack against a reader"), arg_lit0(NULL, "sk", "Fill simulator keys from found keys"), arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "c1", "UL-C Auth - all zero handshake part 1"), + arg_lit0(NULL, "c2", "UL-C Auth - all zero handshake part 2"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -890,9 +926,12 @@ int CmdHF14ASim(const char *Cmd) { bool setEmulatorMem = arg_get_lit(ctx, 5); bool verbose = arg_get_lit(ctx, 6); + bool ulc_p1 = arg_get_lit(ctx, 7); + bool ulc_p2 = arg_get_lit(ctx, 8); + CLIParserFree(ctx); - if (tagtype > 12) { + if (tagtype > 13) { PrintAndLogEx(ERR, "Undefined tag %d", tagtype); return PM3_EINVARG; } @@ -906,11 +945,16 @@ int CmdHF14ASim(const char *Cmd) { uint16_t flags; uint8_t uid[10]; uint8_t exitAfter; + uint8_t rats[20]; + bool ulc_p1; + bool ulc_p2; } PACKED payload; payload.tagtype = tagtype; payload.flags = flags; payload.exitAfter = exitAfterNReads; + payload.ulc_p1 = ulc_p1; + payload.ulc_p2 = ulc_p2; memcpy(payload.uid, uid, uid_len); clearCommandBuffer(); @@ -924,14 +968,17 @@ int CmdHF14ASim(const char *Cmd) { bool keypress = kbd_enter_pressed(); while (keypress == false) { - if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false) { continue; + } - if (resp.status != PM3_SUCCESS) + if (resp.status != PM3_SUCCESS) { break; + } - if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) + if ((flags & FLAG_NR_AR_ATTACK) != FLAG_NR_AR_ATTACK) { break; + } const nonces_t *data = (nonces_t *)resp.data.asBytes; readerAttack(k_sector, k_sectors_cnt, data[0], setEmulatorMem, verbose); @@ -1283,7 +1330,7 @@ static int CmdExchangeAPDU(bool chainingin, const uint8_t *datain, int datainlen // Button pressed / user cancelled if (iLen == -3) { - PrintAndLogEx(DEBUG, "ERR: APDU: User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); return PM3_EAPDU_FAIL; } return PM3_SUCCESS; @@ -1413,6 +1460,7 @@ static int CmdHF14AAPDU(const char *Cmd) { CLIParserFree(ctx); return PM3_EINVARG; } + bool extendedAPDU = arg_get_lit(ctx, 6); int le = arg_get_int_def(ctx, 7, 0); @@ -1497,7 +1545,6 @@ static int CmdHF14ACmdRaw(const char *Cmd) { "Sends raw bytes over ISO14443a. With option to use TOPAZ 14a mode.", "hf 14a raw -sc 3000 -> select, crc, where 3000 == 'read block 00'\n" "hf 14a raw -ak -b 7 40 -> send 7 bit byte 0x40\n" - "hf 14a raw --ecp -s -> send ECP before select\n" "Crypto1 session example, with special auth shortcut 6xxx:\n" "hf 14a raw --crypto1 -skc 6000FFFFFFFFFFFF\n" "hf 14a raw --crypto1 -kc 3000\n" @@ -1516,8 +1563,6 @@ static int CmdHF14ACmdRaw(const char *Cmd) { arg_int0("t", "timeout", "", "Timeout in milliseconds"), arg_int0("b", NULL, "", "Number of bits to send. Useful for send partial byte"), arg_lit0("v", "verbose", "Verbose output"), - arg_lit0(NULL, "ecp", "Use enhanced contactless polling"), - arg_lit0(NULL, "mag", "Use Apple magsafe polling"), arg_lit0(NULL, "topaz", "Use Topaz protocol to send command"), arg_lit0(NULL, "crypto1", "Use crypto1 session"), arg_strx1(NULL, NULL, "", "Raw bytes to send"), @@ -1534,14 +1579,12 @@ static int CmdHF14ACmdRaw(const char *Cmd) { uint32_t timeout = (uint32_t)arg_get_int_def(ctx, 7, 0); uint16_t numbits = (uint16_t)arg_get_int_def(ctx, 8, 0); bool verbose = arg_get_lit(ctx, 9); - bool use_ecp = arg_get_lit(ctx, 10); - bool use_magsafe = arg_get_lit(ctx, 11); - bool topazmode = arg_get_lit(ctx, 12); - bool crypto1mode = arg_get_lit(ctx, 13); + bool topazmode = arg_get_lit(ctx, 10); + bool crypto1mode = arg_get_lit(ctx, 11); int datalen = 0; uint8_t data[PM3_CMD_DATA_SIZE_MIX] = {0}; - CLIGetHexWithReturn(ctx, 14, data, &datalen); + CLIGetHexWithReturn(ctx, 12, data, &datalen); CLIParserFree(ctx); bool bTimeout = (timeout) ? true : false; @@ -1597,7 +1640,7 @@ static int CmdHF14ACmdRaw(const char *Cmd) { if (crypto1mode) { flags |= ISO14A_CRYPTO1MODE; - if (numbits > 0 || topazmode || use_ecp || use_magsafe) { + if (numbits > 0 || topazmode) { PrintAndLogEx(FAILED, "crypto1 mode cannot be used with other modes or partial bytes"); return PM3_EINVARG; } @@ -1607,13 +1650,6 @@ static int CmdHF14ACmdRaw(const char *Cmd) { flags |= ISO14A_NO_RATS; } - // TODO: allow to use reader command with both data and polling configuration - if (use_ecp || use_magsafe) { - PrintAndLogEx(WARNING, "ECP and Magsafe not supported with this command at this moment. Instead use 'hf 14a reader -sk --ecp/--mag'"); - // flags |= ISO14A_USE_MAGSAFE; - // flags |= ISO14A_USE_ECP; - } - // Max buffer is PM3_CMD_DATA_SIZE_MIX datalen = (datalen > PM3_CMD_DATA_SIZE_MIX) ? PM3_CMD_DATA_SIZE_MIX : datalen; @@ -1756,259 +1792,678 @@ static void printTag(const char *tag) { PrintAndLogEx(SUCCESS, " " _YELLOW_("%s"), tag); } -int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status) { - +// Based on NXP AN10833 Rev 3.8 and NXP AN10834 Rev 4.2 +int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status, + uint8_t ats_hist_len, uint8_t *ats_hist, + bool version_hw_available, version_hw_t *version_hw) { int type = MTNONE; - if ((sak & 0x02) != 0x02) { - if ((sak & 0x19) == 0x19) { - type |= MTCLASSIC; - } else if ((sak & 0x40) == 0x40) { - type |= MTISO18092; - } else if ((sak & 0x38) == 0x38) { - type |= MTCLASSIC; - } else if ((sak & 0x18) == 0x18) { - if (select_status == 1) { - type |= MTPLUS; - } else { - type |= MTCLASSIC; - } - } else if ((sak & 0x09) == 0x09) { - type |= MTMINI; - } else if ((sak & 0x28) == 0x28) { - type |= MTCLASSIC; - } else if ((sak & 0x08) == 0x08) { - if (select_status == 1) { - type |= MTPLUS; - } else { - type |= MTCLASSIC; - } - } else if ((sak & 0x11) == 0x11) { - type |= MTPLUS; - } else if ((sak & 0x10) == 0x10) { - type |= MTPLUS; - } else if ((sak & 0x01) == 0x01) { - type |= MTCLASSIC; - } else if ((sak & 0x24) == 0x24) { - type |= MTDESFIRE; - } else if ((sak & 0x20) == 0x20) { - if (select_status == 1) { - if ((atqa & 0x0040) == 0x0040) { - if ((atqa & 0x0300) == 0x0300) { - type |= MTDESFIRE; - } else { - type |= MTPLUS; - } - } else { + if (version_hw_available) { - if ((atqa & 0x0001) == 0x0001) { - type |= HID_SEOS; - } else { - type |= MTPLUS; - } + switch (version_hw->product_type & 0x0F) { + case 0x1: { + type |= MTDESFIRE; - if ((atqa & 0x0004) == 0x0004) { + // special cases, override major_product_version_str when needed + switch (version_hw->major_product_version) { + case 0x42: type |= MTEMV; + break; + case 0xA0: + type |= MTDUOX; + break; + } + break; + } + case 0x2: { + type |= MTPLUS; + break; + } + case 0x3: { + type |= MTULTRALIGHT; + break; + } + case 0x4: { + type |= MTNTAG; + break; + } + case 0x7: { + type |= MTNTAG; + break; + } + case 0x8: { + type |= MTDESFIRE; + break; + } + case 0x9: { + break; + } + default: { + break; + } + } + + } + + if ((sak & 0x44) == 0x40) { + // ISO18092 Table 15: Target compliant with NFC transport protocol + type |= MTISO18092; + } + + if ((sak & 0x02) == 0x00) { // SAK b2=0 + + if ((sak & 0x08) == 0x08) { // SAK b2=0 b4=1 + + if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=1 b5=1 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=1 b1=1, SAK=0x19 + type |= MTCLASSIC; + + } else { // SAK b2=0 b4=1 b5=1 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=1 b1=0 b6=1, SAK=0x38 + type |= MTCLASSIC; + + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 + + if (select_status == 4) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS + + if (version_hw_available) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS GetVersion + type |= MTPLUS; + + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS No_GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + type |= MTPLUS; + } else { + type |= MTCLASSIC; + } + } + } + + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 no_ATS, SAK=0x18 + type |= MTCLASSIC; + } } } - type |= (MTDESFIRE | MT424); - } - } else if ((sak & 0x04) == 0x04) { - type |= MTDESFIRE; - } else { - type |= MTULTRALIGHT; - } - } else if ((sak & 0x0A) == 0x0A) { - if ((atqa & 0x0003) == 0x0003) { - type |= MTFUDAN; - } else if ((atqa & 0x0005) == 0x0005) { + } else { // SAK b2=0 b4=1 b5=0 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=0 b1=1, SAK=0x09 + type |= MTMINI; + + } else { // SAK b2=0 b4=1 b5=0 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=0 b1=0 b6=1, SAK=0x28 + type |= MTCLASSIC; + + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 + + if (select_status == 4) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS + + if (version_hw_available) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS GetVersion + type |= MTPLUS; + + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS No_GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + type |= MTPLUS; + + } else if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x21\x30", 4) == 0)) { + type |= MTPLUS; + + } else { + type |= MTCLASSIC; + } + } + } + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 no_ATS, SAK=0x08 + type |= MTCLASSIC; + } + } + } + } + + } else { // SAK b2=0 b4=0 + + if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=0 b5=1 + + // if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=1 b1=1, SAK=0x11 + // } else { // SAK b2=0 b4=0 b5=1 b1=0, SAK=0x10 + // } + type |= MTPLUS; + + } else { // SAK b2=0 b4=0 b5=0 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=0 b1=1 + type |= MTCLASSIC; + + } else { // SAK b2=0 b4=0 b5=0 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1, SAK=0x20 + + if (select_status == 1) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS + + if (version_hw_available) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS GetVersion + + if ((version_hw->product_type & 0x7F) == 0x02) { + type |= MTPLUS; + + } else if (((version_hw->product_type & 0x7F) == 0x01) || + (version_hw->product_type == 0x08) || + (version_hw->product_type == 0x91)) { + type |= MTDESFIRE; + + } else if (version_hw->product_type == 0x04) { + type |= (MTDESFIRE | MT424); + } + + } else { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS No GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + type |= MTPLUS; + + } else { + + if ((atqa == 0x0001) || (atqa == 0x0004)) { + type |= HID_SEOS; + } + + if (atqa == 0x0004) { + type |= MTEMV; + } + } + } + } + } + + } else { // SAK b2=0 b4=0 b5=0 b1=0 b6=0, SAK=0x00 + + if (version_hw_available == false) { + // SAK b2=0 b4=0 b5=0 b1=0 b6=0 No_GetVersion + int status = mfuc_test_authentication_support(); + if (status == PM3_SUCCESS) { + type |= MTULTRALIGHT_C; + } + } + type |= MTULTRALIGHT; + } + } + } + } + } else { // SAK b2=1 + + if (sak == 0x0A) { + + if (atqa == 0x0003) { + // Uses Shanghai algo + type |= MTFUDAN; + + } else if (atqa == 0x0005) { + type |= MTFUDAN; + } + + } else if (sak == 0x53) { type |= MTFUDAN; } - } else if ((sak & 0x53) == 0x53) { - type |= MTFUDAN; } return type; } - -// Based on NXP AN10833 Rev 3.6 and NXP AN10834 Rev 4.1 -static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_status) { +// Based on NXP AN10833 Rev 3.8 and NXP AN10834 Rev 4.2 +static int detect_nxp_card_print(uint8_t sak, uint16_t atqa, uint64_t select_status, + uint8_t ats_hist_len, uint8_t *ats_hist, + bool version_hw_available, version_hw_t *version_hw) { int type = MTNONE; + const char *product_type_str = ""; + const char *major_product_version_str = ""; + const char *storage_size_str = ""; - PrintAndLogEx(SUCCESS, "Possible types:"); + if (version_hw_available) { - if ((sak & 0x02) != 0x02) { - - if ((sak & 0x19) == 0x19) { - printTag("MIFARE Classic 2K"); - type |= MTCLASSIC; - - } else if ((sak & 0x40) == 0x40) { - - if ((atqa & 0x0110) == 0x0110) { - printTag("P2P Support / Proprietary"); - } else { - printTag("P2P Support / Android"); - } - type |= MTISO18092; - - } else if ((sak & 0x38) == 0x38) { - printTag("SmartMX with MIFARE Classic 4K"); - type |= MTCLASSIC; - - } else if ((sak & 0x18) == 0x18) { - - if (select_status == 1) { - - if ((atqa & 0x0040) == 0x0040) { - printTag("MIFARE Plus EV1 4K CL2 in SL1"); - printTag("MIFARE Plus S 4K CL2 in SL1"); - printTag("MIFARE Plus X 4K CL2 in SL1"); - } else { - printTag("MIFARE Plus EV1 4K in SL1"); - printTag("MIFARE Plus S 4K in SL1"); - printTag("MIFARE Plus X 4K in SL1"); + switch (version_hw->product_type & 0x0F) { + case 0x1: { + product_type_str = "MIFARE DESFire"; + // special cases, override product_type_str when needed + if (version_hw->product_type == 0x91) { + product_type_str = "Apple Wallet DESFire Applet"; } - type |= MTPLUS; - } else { - - if ((atqa & 0x0040) == 0x0040) { - printTag("MIFARE Classic 4K CL2"); - } else { - printTag("MIFARE Classic 4K"); + // general rule + switch (version_hw->major_product_version & 0x0F) { + case 0x01: + major_product_version_str = "EV1"; + break; + case 0x02: + major_product_version_str = "EV2"; + break; + case 0x03: + major_product_version_str = "EV3"; + break; } - type |= MTCLASSIC; - } - } else if ((sak & 0x09) == 0x09) { - - if ((atqa & 0x0040) == 0x0040) { - printTag("MIFARE Mini 0.3K CL2"); - } else { - printTag("MIFARE Mini 0.3K"); - } - type |= MTMINI; - - } else if ((sak & 0x28) == 0x28) { - printTag("SmartMX with MIFARE Classic 1K"); - printTag("FM1208-10 with MIFARE Classic 1K"); - printTag("FM1216-137 with MIFARE Classic 1K"); - type |= MTCLASSIC; - } else if ((sak & 0x08) == 0x08) { - - if (select_status == 1) { - - if ((atqa & 0x0040) == 0x0040) { - printTag("MIFARE Plus EV1 2K CL2 in SL1"); - printTag("MIFARE Plus S 2K CL2 in SL1"); - printTag("MIFARE Plus X 2K CL2 in SL1"); - printTag("MIFARE Plus SE 1K CL2"); - } else { - printTag("MIFARE Plus EV1 2K in SL1"); - printTag("MIFARE Plus S 2K in SL1"); - printTag("MIFARE Plus X 2K in SL1"); - printTag("MIFARE Plus SE 1K"); + // special cases, override major_product_version_str when needed + switch (version_hw->major_product_version) { + case 0x00: + major_product_version_str = "MF3ICD40"; + break; + case 0x42: + major_product_version_str = "EV2 + EMV"; + break; + case 0xA0: + product_type_str = "MIFARE DUOX"; + break; } - type |= MTPLUS; - - } else { - if ((atqa & 0x0040) == 0x0040) { - printTag("MIFARE Classic 1K CL2"); - } else { - printTag("MIFARE Classic 1K"); - } - type |= MTCLASSIC; + break; } + case 0x2: { + product_type_str = "MIFARE Plus"; + switch (version_hw->major_product_version) { + case 0x11: + major_product_version_str = "EV1"; + break; + case 0x22: + major_product_version_str = "EV2"; + break; + default: + major_product_version_str = "n/a"; + } + break; + } + case 0x3: { + product_type_str = "MIFARE Ultralight"; + switch (version_hw->major_product_version) { + case 0x01: { + major_product_version_str = "EV1"; - } else if ((sak & 0x11) == 0x11) { - printTag("MIFARE Plus 4K in SL2"); - type |= MTPLUS; - } else if ((sak & 0x10) == 0x10) { - printTag("MIFARE Plus 2K in SL2"); - type |= MTPLUS; - } else if ((sak & 0x01) == 0x01) { - printTag("TNP3xxx (TagNPlay, Activision Game Appliance)"); - type |= MTCLASSIC; - } else if ((sak & 0x24) == 0x24) { - printTag("MIFARE DESFire CL1"); - printTag("MIFARE DESFire EV1 CL1"); - type |= MTDESFIRE; - } else if ((sak & 0x20) == 0x20) { - - if (select_status == 1) { - - if ((atqa & 0x0040) == 0x0040) { - - if ((atqa & 0x0300) == 0x0300) { - printTag("MIFARE DESFire CL2"); - printTag("MIFARE DESFire EV1 256B/2K/4K/8K CL2"); - printTag("MIFARE DESFire EV2 2K/4K/8K/16K/32K"); - printTag("MIFARE DESFire EV3 2K/4K/8K"); - printTag("MIFARE DESFire Light 640B"); - printTag("MIFARE Duox"); - type |= MTDESFIRE; - } else { - printTag("MIFARE Plus EV1 2K/4K CL2 in SL3"); - printTag("MIFARE Plus S 2K/4K CL2 in SL3"); - printTag("MIFARE Plus X 2K/4K CL2 in SL3"); - printTag("MIFARE Plus SE 1K CL2"); - type |= MTPLUS; + if (version_hw->storage_size == 0x0B) { + storage_size_str = "48b"; + } else if (version_hw->storage_size == 0x0E) { + storage_size_str = "128b"; + } + break; } + case 0x02: + major_product_version_str = "Nano"; + break; + case 0x04: + major_product_version_str = "AES"; + break; + default: + major_product_version_str = "n/a"; + } + break; + } + case 0x4: { + product_type_str = "NTAG"; + switch (version_hw->major_product_version) { + case 0x01: + major_product_version_str = "2xx"; + break; + case 0x02: + major_product_version_str = "210µ"; + break; + case 0x03: + major_product_version_str = "213 TT"; + break; + case 0x10: + // Not sure about its product type = 4 + major_product_version_str = "413 DNA"; + break; + case 0x30: + major_product_version_str = "4xx"; + break; + default: + major_product_version_str = "n/a"; + } + break; + } + case 0x7: { + product_type_str = "NTAG I2C"; + break; + } + case 0x8: { + product_type_str = "MIFARE DESFire Light"; + break; + } + case 0x9: { + product_type_str = "MIFARE Hospitality"; + switch (version_hw->major_product_version) { + case 0x01: + major_product_version_str = "AES"; + break; + default: + major_product_version_str = "n/a"; + } + break; + } + default: { + product_type_str = "Unknown NXP tag"; + break; + } + } + + if (storage_size_str == NULL) { + static char size_str[16]; + uint16_t usize = 1 << ((version_hw->storage_size >> 1) + 1); + uint16_t lsize = 1 << (version_hw->storage_size >> 1); + + // is LSB set? + if ((version_hw->storage_size & 0x01) == 1) { + + // if set, its a range between upper size and lower size + + if (lsize < 1024) { + snprintf(size_str, sizeof(size_str), "%u - %uB", usize, lsize); } else { - - if ( - ((atqa & 0x0001) == 0x0001) || - ((atqa & 0x0004) == 0x0004) - ) { - printTag("HID SEOS (smartmx / javacard)"); - type |= HID_SEOS; - - } else { - printTag("MIFARE Plus EV1 2K/4K in SL3"); - printTag("MIFARE Plus S 2K/4K in SL3"); - printTag("MIFARE Plus X 2K/4K in SL3"); - printTag("MIFARE Plus SE 1K"); - type |= MTPLUS; - } - - if ((atqa & 0x0004) == 0x0004) { - printTag("EMV"); - type |= MTEMV; - } + snprintf(size_str, sizeof(size_str), "%d - %dK", (usize / 1024), (lsize / 1024)); } - printTag("NTAG 4xx"); - type |= (MTDESFIRE | MT424); + } else { + + // if not set, it's lower size + if (lsize < 1024) { + snprintf(size_str, sizeof(size_str), "%uB", lsize); + } else { + snprintf(size_str, sizeof(size_str), "%dK", (lsize / 1024)); + } } - } else if ((sak & 0x04) == 0x04) { - printTag("Any MIFARE CL1"); - type |= MTDESFIRE; + + storage_size_str = size_str; + + } + } + + char tag_info[128]; + + if ((sak & 0x44) == 0x40) { + // ISO18092 Table 15: Target compliant with NFC transport protocol + if ((atqa & 0x0110) == 0x0110) { + printTag("P2P Support / Proprietary"); } else { - printTag("MIFARE Ultralight"); - printTag("MIFARE Ultralight C"); - printTag("MIFARE Ultralight EV1"); - printTag("MIFARE Ultralight Nano"); - printTag("MIFARE Ultralight AES"); - printTag("MIFARE Hospitality"); - printTag("NTAG 2xx"); - type |= MTULTRALIGHT; + printTag("P2P Support / Android"); } - } else if ((sak & 0x0A) == 0x0A) { + type |= MTISO18092; + } - if ((atqa & 0x0003) == 0x0003) { - // Uses Shanghai algo - printTag("FM11RF005SH (FUDAN Shanghai Metro)"); - type |= MTFUDAN; - } else if ((atqa & 0x0005) == 0x0005) { - printTag("FM11RF005M (FUDAN ISO14443A w Crypto-1 algo)"); + if ((sak & 0x02) == 0x00) { // SAK b2=0 + + if ((sak & 0x08) == 0x08) { // SAK b2=0 b4=1 + + if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=1 b5=1 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=1 b1=1, SAK=0x19 + printTag("MIFARE Classic 2K"); + type |= MTCLASSIC; + } else { // SAK b2=0 b4=1 b5=1 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=1 b1=0 b6=1, SAK=0x38 + printTag("SmartMX with MIFARE Classic 4K"); + type |= MTCLASSIC; + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 + + if (select_status == 4) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS + + if (version_hw_available) { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS GetVersion + snprintf(tag_info, sizeof(tag_info), "%s %s %s in SL1", product_type_str, major_product_version_str, storage_size_str); + printTag(tag_info); + type |= MTPLUS; + + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 ATS No_GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + + if (memcmp(ats_hist + 4, "\x00\x35\xC7", 3) == 0) { + printTag("MIFARE Plus S 4K in SL1"); + } else if (memcmp(ats_hist + 4, "\x01\xBC\xD6", 3) == 0) { + printTag("MIFARE Plus X 4K in SL1"); + } else { + printTag("Unrecognized MIFARE Plus??"); + } + type |= MTPLUS; + } else { + if ((atqa & 0x0040) == 0x0040) { + printTag("MIFARE Classic 4K CL2 with ATS!"); + } else { + printTag("MIFARE Classic 4K with ATS!"); + } + type |= MTCLASSIC; + } + } + } + + } else { // SAK b2=0 b4=1 b5=1 b1=0 b6=0 no_ATS, SAK=0x18 + + if ((atqa & 0x0040) == 0x0040) { + printTag("MIFARE Classic 4K CL2"); + } else { + printTag("MIFARE Classic 4K"); + } + type |= MTCLASSIC; + } + } + } + + } else { // SAK b2=0 b4=1 b5=0 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=1 b5=0 b1=1, SAK=0x09 + + if ((atqa & 0x0040) == 0x0040) { + printTag("MIFARE Mini 0.3K CL2"); + } else { + printTag("MIFARE Mini 0.3K"); + } + type |= MTMINI; + + } else { // SAK b2=0 b4=1 b5=0 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=1 b5=0 b1=0 b6=1, SAK=0x28 + printTag("SmartMX with MIFARE Classic 1K"); + printTag("FM1208-10 with MIFARE Classic 1K"); + printTag("FM1216-137 with MIFARE Classic 1K"); + type |= MTCLASSIC; + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 + + if (select_status == 4) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS + + if (version_hw_available) { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS GetVersion + snprintf(tag_info, sizeof(tag_info), "%s %s %s in SL1", product_type_str, major_product_version_str, storage_size_str); + printTag(tag_info); + type |= MTPLUS; + + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 ATS No_GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + + if (memcmp(ats_hist + 4, "\x00\x35\xC7", 3) == 0) { + printTag("MIFARE Plus S 2K in SL1"); + } else if (memcmp(ats_hist + 4, "\x01\xBC\xD6", 3) == 0) { + printTag("MIFARE Plus X 2K in SL1"); + } else { + printTag("Unrecognized MIFARE Plus??"); + } + type |= MTPLUS; + + } else if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x21\x30", 4) == 0)) { + + if (memcmp(ats_hist + 4, "\x00\xF6\xD1", 3) == 0) { + printTag("MIFARE Plus SE 1K 17pF"); + } else if (memcmp(ats_hist + 4, "\x10\xF6\xD1", 3) == 0) { + printTag("MIFARE Plus SE 1K 70pF"); + } else { + printTag("Unrecognized MIFARE Plus SE??"); + } + type |= MTPLUS; + + } else { + + if ((atqa & 0x0040) == 0x0040) { + printTag("MIFARE Classic 1K CL2 with ATS!"); + } else { + printTag("MIFARE Classic 1K with ATS!"); + } + type |= MTCLASSIC; + } + } + } + } else { // SAK b2=0 b4=1 b5=0 b1=0 b6=0 no_ATS, SAK=0x08 + if ((atqa & 0x0040) == 0x0040) { + printTag("MIFARE Classic 1K CL2"); + } else { + printTag("MIFARE Classic 1K"); + } + type |= MTCLASSIC; + } + } + } + } + + } else { // SAK b2=0 b4=0 + + if ((sak & 0x10) == 0x10) { // SAK b2=0 b4=0 b5=1 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=1 b1=1, SAK=0x11 + printTag("MIFARE Plus 4K in SL2"); + } else { // SAK b2=0 b4=0 b5=1 b1=0, SAK=0x10 + printTag("MIFARE Plus 2K in SL2"); + } + type |= MTPLUS; + + } else { // SAK b2=0 b4=0 b5=0 + + if ((sak & 0x01) == 0x01) { // SAK b2=0 b4=0 b5=0 b1=1 + printTag("TNP3xxx (TagNPlay, Activision Game Appliance)"); + type |= MTCLASSIC; + + } else { // SAK b2=0 b4=0 b5=0 b1=0 + + if ((sak & 0x20) == 0x20) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1, SAK=0x20 + + if (select_status == 1) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS + + if (version_hw_available) { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS GetVersion + + if ((version_hw->product_type & 0x7F) == 0x02) { + snprintf(tag_info, sizeof(tag_info), "%s %s %s in SL0/SL3", product_type_str, major_product_version_str, storage_size_str); + type |= MTPLUS; + + } else if (((version_hw->product_type & 0x7F) == 0x01) || + (version_hw->product_type == 0x08) || + (version_hw->product_type == 0x91)) { + snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str); + type |= MTDESFIRE; + + } else if (version_hw->product_type == 0x04) { + snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str); + type |= (MTDESFIRE | MT424); + + } else { + snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str); + } + printTag(tag_info); + + } else { // SAK b2=0 b4=0 b5=0 b1=0 b6=1 ATS No GetVersion + + if (ats_hist_len > 0) { + + if ((ats_hist_len == 9) && (memcmp(ats_hist, "\xC1\x05\x2F\x2F", 4) == 0)) { + + if (memcmp(ats_hist + 4, "\x00\x35\xC7", 3) == 0) { + + if ((atqa & 0xFF0F) == 0x0004) { + printTag("MIFARE Plus S 2K in SL0/SL3"); + } else if ((atqa & 0xFF0F) == 0x0002) { + printTag("MIFARE Plus S 4K in SL0/SL3"); + } else { + printTag("Unrecognized MIFARE Plus??"); + } + + } else if (memcmp(ats_hist + 4, "\x01\xBC\xD6", 3) == 0) { + printTag("MIFARE Plus X 2K/4K in SL0/SL3"); + + } else if (memcmp(ats_hist + 4, "\x00\xF6\xD1", 3) == 0) { + printTag("MIFARE Plus SE 1K 17pF"); + + } else if (memcmp(ats_hist + 4, "\x10\xF6\xD1", 3) == 0) { + printTag("MIFARE Plus SE 1K 70pF"); + + } else { + printTag("Unknown MIFARE Plus"); + } + type |= MTPLUS; + + } else { + + if ((atqa == 0x0001) || (atqa == 0x0004)) { + printTag("HID SEOS (smartmx / javacard)"); + type |= HID_SEOS; + } + + if (atqa == 0x0004) { + printTag("EMV"); + type |= MTEMV; + } + } + } + } + } else { + printTag("Unknown tag claims to support RATS in SAK but does not..."); + } + + } else { // SAK b2=0 b4=0 b5=0 b1=0 b6=0, SAK=0x00 + + if (version_hw_available) { // SAK b2=0 b4=0 b5=0 b1=0 b6=0 GetVersion + snprintf(tag_info, sizeof(tag_info), "%s %s %s", product_type_str, major_product_version_str, storage_size_str); + printTag(tag_info); + + } else { // SAK b2=0 b4=0 b5=0 b1=0 b6=0 No_GetVersion + + int status = mfuc_test_authentication_support(); + if (status == PM3_SUCCESS) { + // TODO: read page 2/3, then ?? + printTag("MIFARE Ultralight C"); + printTag("MIFARE Hospitality"); + type |= MTULTRALIGHT_C; + + } else { + printTag("MIFARE Ultralight"); + } + + } + type |= MTULTRALIGHT; + } + } + } + } + } else { // SAK b2=1 + + if (sak == 0x0A) { + + if (atqa == 0x0003) { + // Uses Shanghai algo + printTag("FM11RF005SH (FUDAN Shanghai Metro)"); + type |= MTFUDAN; + + } else if (atqa == 0x0005) { + printTag("FM11RF005M (FUDAN ISO14443A w Crypto-1 algo)"); + type |= MTFUDAN; + } + + } else if (sak == 0x53) { + printTag("FM11RF08SH (FUDAN)"); type |= MTFUDAN; } - } else if ((sak & 0x53) == 0x53) { - printTag("FM11RF08SH (FUDAN)"); - type |= MTFUDAN; } if (type == MTNONE) { @@ -2131,7 +2586,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); DropField(); - return 0; + return PM3_ETIMEOUT; } iso14a_card_select_t card; @@ -2142,6 +2597,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { 1: OK, with ATS 2: OK, no ATS 3: proprietary Anticollision + 4: OK, SAK = no ATS but RATS possible (tested below) */ uint64_t select_status = resp.oldarg[0]; @@ -2169,10 +2625,43 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { return select_status; } + // 2: try to request ATS even if tag claims not to support it. If yes => 4 + if (select_status == 2) { + uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); + card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes + if (card.ats_len > 3) { + select_status = 4; + } + } + uint8_t ats_hist_pos = 0; + if ((card.ats_len > 3) && (card.ats[0] > 1)) { + ats_hist_pos = 2; + ats_hist_pos += (card.ats[1] & 0x10) == 0x10; + ats_hist_pos += (card.ats[1] & 0x20) == 0x20; + ats_hist_pos += (card.ats[1] & 0x40) == 0x40; + } + + + version_hw_t version_hw = {0}; + // if 4b UID or NXP, try to get version + int res = hf14a_getversion_data(&card, select_status, &version_hw); + bool version_hw_available = (res == PM3_SUCCESS); + PrintAndLogEx(INFO, "---------- " _CYAN_("ISO14443-A Information") " ----------"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s") " %s", sprint_hex(card.uid, card.uidlen), get_uid_type(&card)); PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, select_status); + if (version_hw_available) { + PrintAndLogEx(DEBUG, "GetV: " _GREEN_("%s"), sprint_hex((uint8_t *)&version_hw, sizeof(version_hw))); + } bool isMifareMini = false; bool isMifareClassic = true; @@ -2188,7 +2677,12 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { int nxptype = MTNONE; if (card.uidlen <= 4) { - nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), select_status); + + PrintAndLogEx(SUCCESS, "Possible types:"); + nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), + select_status, card.ats_len - ats_hist_pos, card.ats + ats_hist_pos, + version_hw_available, &version_hw + ); isMifareMini = ((nxptype & MTMINI) == MTMINI); isMifareClassic = ((nxptype & MTCLASSIC) == MTCLASSIC); @@ -2207,7 +2701,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } else { // Double & triple sized UID, can be mapped to a manufacturer. - PrintAndLogEx(SUCCESS, "MANUFACTURER: " _YELLOW_("%s"), getTagInfo(card.uid[0])); + PrintAndLogEx(SUCCESS, " " _YELLOW_("%s"), getTagInfo(card.uid[0])); + PrintAndLogEx(SUCCESS, "Possible types:"); switch (card.uid[0]) { case 0x02: { // ST @@ -2216,7 +2711,10 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { break; } case 0x04: { // NXP - nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), select_status); + nxptype = detect_nxp_card_print(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), + select_status, card.ats_len - ats_hist_pos, card.ats + ats_hist_pos, + version_hw_available, &version_hw + ); isMifareMini = ((nxptype & MTMINI) == MTMINI); isMifareClassic = ((nxptype & MTCLASSIC) == MTCLASSIC); @@ -2351,26 +2849,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } } - // try to request ATS even if tag claims not to support it - if (select_status == 2) { - uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 - clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); - if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply"); - return PM3_ETIMEOUT; - } - - memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); - card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes - if (card.ats_len > 3) { - select_status = 1; - } - } - if (card.ats_len >= 3) { // a valid ATS consists of at least the length byte (TL) and 2 CRC bytes - PrintAndLogEx(INFO, "-------------------------- " _CYAN_("ATS") " --------------------------"); + PrintAndLogEx(INFO, "-------------------------- " _CYAN_("ATS") " ----------------------------------"); bool ta1 = 0, tb1 = 0, tc1 = 0; if (select_status == 2) { @@ -2463,61 +2944,12 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { // ATS - Historial bytes and identify based on it if ((card.ats[0] > pos) && (card.ats_len >= card.ats[0] + 2)) { - char tip[60]; - tip[0] = '\0'; - if (card.ats[0] - pos >= 7) { - - snprintf(tip, sizeof(tip), " "); - - if ((card.sak & 0x70) == 0x40) { // and no GetVersion().. - - if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus X 2K/4K (SL3)"); - - } else if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { - - if ((card.atqa[0] & 0x02) == 0x02) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus S 2K (SL3)"); - } else if ((card.atqa[0] & 0x04) == 0x04) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus S 4K (SL3)"); - } - - } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x00\xF6\xD1", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus SE 1K (17pF)"); - - } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x10\xF6\xD1", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus SE 1K (70pF)"); - } - - } else { //SAK B4,5,6 - - if ((card.sak & 0x20) == 0x20) { // and no GetVersion().. - - if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus X 2K (SL1)"); - } else if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus S 2K (SL1)"); - } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x00\xF6\xD1", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus SE 1K (17pF)"); - } else if (memcmp(card.ats + pos, "\xC1\x05\x21\x30\x10\xF6\xD1", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus SE 1K (70pF)"); - } - } else { - if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x01\xBC\xD6", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus X 4K (SL1)"); - } else if (memcmp(card.ats + pos, "\xC1\x05\x2F\x2F\x00\x35\xC7", 7) == 0) { - snprintf(tip + strlen(tip), sizeof(tip) - strlen(tip), _GREEN_("%s"), "MIFARE Plus S 4K (SL1)"); - } - } - } - } - uint8_t calen = card.ats[0] - pos; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "-------------------- " _CYAN_("Historical bytes") " --------------------"); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("Historical bytes") " ----------------------------"); if (card.ats[pos] == 0xC1) { - PrintAndLogEx(INFO, " %s%s", sprint_hex(card.ats + pos, calen), tip); + PrintAndLogEx(INFO, " %s", sprint_hex(card.ats + pos, calen)); PrintAndLogEx(SUCCESS, " C1..................... Mifare or (multiple) virtual cards of various type"); PrintAndLogEx(SUCCESS, " %02X.................. length is " _YELLOW_("%d") " bytes", card.ats[pos + 1], card.ats[pos + 1]); switch (card.ats[pos + 2] & 0xf0) { @@ -2619,7 +3051,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { uint16_t sw = 0; uint8_t result[1024] = {0}; size_t resultlen = 0; - int res = Iso7816Select(CC_CONTACTLESS, ActivateField, true, vaid, vaidlen, result, sizeof(result), &resultlen, &sw); + res = Iso7816Select(CC_CONTACTLESS, ActivateField, true, vaid, vaidlen, result, sizeof(result), &resultlen, &sw); ActivateField = false; if (res) continue; @@ -2671,8 +3103,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } DropField(); - if (verbose == false && found) + if (verbose == false && found) { PrintAndLogEx(INFO, "----------------------------------------------------"); + } } } @@ -2682,7 +3115,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { PrintAndLogEx(INFO, "proprietary iso18092 card found"); } else { - PrintAndLogEx(INFO, "proprietary non iso14443-4 card found, RATS not supported"); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "Proprietary non iso14443-4 card found"); + PrintAndLogEx(INFO, "RATS not supported"); if ((card.sak & 0x20) == 0x20) { PrintAndLogEx(INFO, "--> SAK incorrectly claims that card supports RATS <--"); } @@ -2697,7 +3132,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { return PM3_EFAILED; } - PrintAndLogEx(INFO, ""); +// PrintAndLogEx(INFO, ""); uint16_t isMagic = 0; @@ -2710,20 +3145,20 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } if (isMifareClassic || isMifareMini) { - int res = detect_classic_static_nonce(); + res = detect_classic_static_nonce(); if (res == NONCE_STATIC) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); + PrintAndLogEx(SUCCESS, "Static nonce....... " _YELLOW_("yes")); } if (res == NONCE_NORMAL) { // not static res = detect_classic_prng(); if (res == 1) { - PrintAndLogEx(SUCCESS, "Prng detection....... " _GREEN_("weak")); + PrintAndLogEx(SUCCESS, "Prng detection..... " _GREEN_("weak")); } else if (res == 0) { - PrintAndLogEx(SUCCESS, "Prng detection....... " _YELLOW_("hard")); + PrintAndLogEx(SUCCESS, "Prng detection..... " _YELLOW_("hard")); } else { - PrintAndLogEx(FAILED, "Prng detection........ " _RED_("fail")); + PrintAndLogEx(FAILED, "Prng detection...... " _RED_("fail")); } if (do_nack_test) { @@ -3905,7 +4340,7 @@ int CmdHF14AAIDSim(const char *Cmd) { bool keypress = kbd_enter_pressed(); while (keypress == false) { - if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) { + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == false) { continue; } diff --git a/client/src/cmdhf14a.h b/client/src/cmdhf14a.h index 21b33fc13..2e438a2d6 100644 --- a/client/src/cmdhf14a.h +++ b/client/src/cmdhf14a.h @@ -20,7 +20,7 @@ #define CMDHF14A_H__ #include "common.h" -#include "pm3_cmd.h" //hf14a_config +#include "pm3_cmd.h" //hf14a_config_t #include "mifare.h" // structs // structure and database for uid -> tagtype lookups @@ -36,6 +36,16 @@ typedef struct { const char *hint; } hintAIDList_t; +typedef struct { + uint8_t vendor_id; + uint8_t product_type; + uint8_t product_subtype; + uint8_t major_product_version; + uint8_t minor_product_version; + uint8_t storage_size; + uint8_t protocol_type; +} version_hw_t; + typedef enum { MTNONE = 0, MTCLASSIC = 1, @@ -49,6 +59,9 @@ typedef enum { MTFUDAN = 256, MTISO18092 = 512, MT424 = 1024, + MTULTRALIGHT_C = 2048, + MTDUOX = 4096, + MTNTAG = 8192, } nxp_mifare_type_t; int CmdHF14A(const char *Cmd); @@ -59,10 +72,13 @@ int CmdHF14ANdefRead(const char *Cmd); // used by cmdnfc.c int CmdHF14ANdefFormat(const char *Cmd); // used by cmdnfc.c int CmdHF14ANdefWrite(const char *Cmd); // used by cmdnfc.c -int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status); -int hf14a_getconfig(hf14a_config *config); -int hf14a_setconfig(hf14a_config *config, bool verbose); +int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status, + uint8_t ats_hist_len, uint8_t *ats_hist, + bool version_hw_available, version_hw_t *version_hw); + +int hf14a_getconfig(hf14a_config_t *config); +int hf14a_setconfig(hf14a_config_t *config, bool verbose); int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search); int infoHF14A4Applications(bool verbose); const char *getTagInfo(uint8_t uid); @@ -70,10 +86,11 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card); int ExchangeAPDU14a(const uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode); -iso14a_polling_parameters_t iso14a_get_polling_parameters(bool use_ecp, bool use_magsafe); int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card); int SelectCard14443A_4_WithParameters(bool disconnect, bool verbose, iso14a_card_select_t *card, iso14a_polling_parameters_t *polling_parameters); bool Get_apdu_in_framing(void); void Set_apdu_in_framing(bool v); +int hf14a_getversion_data(iso14a_card_select_t *card, uint64_t select_status, version_hw_t *hw); + #endif diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index cb0482cc3..f61d7d1fb 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -1411,7 +1411,7 @@ static bool HF14B_ask_ct_reader(bool verbose) { return false; } -bool HF14B_picopass_reader(bool verbose, bool info) { +static bool HF14B_picopass_reader(bool verbose) { iso14b_raw_cmd_t packet = { .flags = (ISO14B_CONNECT | ISO14B_SELECT_PICOPASS | ISO14B_DISCONNECT), @@ -1437,10 +1437,8 @@ bool HF14B_picopass_reader(bool verbose, bool info) { return false; } memcpy(card, resp.data.asBytes, sizeof(picopass_hdr_t)); - if (info) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(card->csn, sizeof(card->csn))); - } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "iCLASS / Picopass CSN: " _GREEN_("%s"), sprint_hex(card->csn, sizeof(card->csn))); free(card); return true; } @@ -3029,14 +3027,15 @@ int infoHF14B(bool verbose, bool do_aid_search) { // 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"); + if (verbose) { + PrintAndLogEx(FAILED, "no 14443-B tag found"); + } return PM3_EOPABORTED; } // get and print general info about all known 14b chips int readHF14B(bool loop, bool verbose, bool read_plot) { bool found = false; - bool info = true; int res = PM3_SUCCESS; do { found = false; @@ -3052,7 +3051,7 @@ int readHF14B(bool loop, bool verbose, bool read_plot) { goto plot; // Picopass - found |= HF14B_picopass_reader(verbose, info); + found |= HF14B_picopass_reader(verbose); if (found) goto plot; diff --git a/client/src/cmdhf14b.h b/client/src/cmdhf14b.h index 067718507..065dbc29c 100644 --- a/client/src/cmdhf14b.h +++ b/client/src/cmdhf14b.h @@ -31,6 +31,5 @@ int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card); int infoHF14B(bool verbose, bool do_aid_search); int readHF14B(bool loop, bool verbose, bool read_plot); -bool HF14B_picopass_reader(bool verbose, bool info); #endif diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 25ee0a84f..d27c8d049 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -50,22 +50,6 @@ #define Logic1 Iso15693Logic1 #define FrameEOF Iso15693FrameEOF #define CARD_MEMORY_SIZE 4096 -#define HF15_UID_LENGTH 8 - -#ifndef Crc15 -# define Crc15(data, len) Crc16ex(CRC_15693, (data), (len)) -#endif -#ifndef CheckCrc15 -# define CheckCrc15(data, len) check_crc(CRC_15693, (data), (len)) -#endif -#ifndef AddCrc15 -#define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) -#endif - -#ifndef ISO15_RAW_LEN -#define ISO15_RAW_LEN(x) (sizeof(iso15_raw_cmd_t) + (x)) -#endif - #ifndef ISO15_ERROR_HANDLING_RESPONSE #define ISO15_ERROR_HANDLING_RESPONSE { \ @@ -98,6 +82,11 @@ } #endif +typedef struct { + uint8_t lock; + uint8_t block[8]; +} t15memory_t; + // structure and database for uid -> tagtype lookups typedef struct { uint64_t uid; @@ -279,22 +268,22 @@ static int CmdHF15Help(const char *Cmd); static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { int reason = 0; - int index = originality_check_verify(uid, 8, signature, 32, PK_MFC); + int index = originality_check_verify(uid, 8, signature, 32, PK_15); if (index >= 0) { reason = 1; } else { // try with sha256 - index = originality_check_verify_ex(uid, 8, signature, 32, PK_MFC, false, true); + index = originality_check_verify_ex(uid, 8, signature, 32, PK_15, false, true); if (index >= 0) { reason = 2; } else { // try with reversed uid / signature - index = originality_check_verify_ex(uid, 8, signature, 32, PK_MFC, true, false); + index = originality_check_verify_ex(uid, 8, signature, 32, PK_15, true, false); if (index >= 0) { reason = 3; } else { // try with sha256 and reversed uid / signature - index = originality_check_verify_ex(uid, 8, signature, 32, PK_MFC, true, true); + index = originality_check_verify_ex(uid, 8, signature, 32, PK_15, true, true); if (index >= 0) { reason = 3; } @@ -474,7 +463,7 @@ static int getUID(bool verbose, bool loop, uint8_t *buf) { // used with 'hf search' bool readHF15Uid(bool loop, bool verbose) { - uint8_t uid[HF15_UID_LENGTH] = {0}; + uint8_t uid[ISO15693_UID_LENGTH] = {0}; if (getUID(verbose, loop, uid) != PM3_SUCCESS) { return false; } @@ -665,7 +654,7 @@ static int NxpTestEAS(const uint8_t *uid) { return PM3_EINVARG; } - uint8_t approxlen = 3 + HF15_UID_LENGTH + 2; + uint8_t approxlen = 3 + ISO15693_UID_LENGTH + 2; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { PrintAndLogEx(WARNING, "Failed to allocate memory"); @@ -677,8 +666,8 @@ static int NxpTestEAS(const uint8_t *uid) { packet->raw[packet->rawlen++] = ISO15693_EAS_ALARM; packet->raw[packet->rawlen++] = 0x04; // IC manufacturer code - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); // add UID - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); // add UID + packet->rawlen += ISO15693_UID_LENGTH; AddCrc15(packet->raw, packet->rawlen); packet->rawlen += 2; @@ -720,7 +709,7 @@ static int NxpCheckSig(uint8_t *uid) { return PM3_EINVARG; } - uint8_t approxlen = 3 + HF15_UID_LENGTH + 2; + uint8_t approxlen = 3 + ISO15693_UID_LENGTH + 2; iso15_raw_cmd_t *packet = (iso15_raw_cmd_t *)calloc(1, sizeof(iso15_raw_cmd_t) + approxlen); if (packet == NULL) { PrintAndLogEx(WARNING, "Failed to allocate memory"); @@ -733,8 +722,8 @@ static int NxpCheckSig(uint8_t *uid) { packet->raw[packet->rawlen++] = ISO15693_READ_SIGNATURE; packet->raw[packet->rawlen++] = 0x04; // IC manufacturer code - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); // add UID - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); // add UID + packet->rawlen += ISO15693_UID_LENGTH; AddCrc15(packet->raw, packet->rawlen); packet->rawlen += 2; @@ -787,7 +776,7 @@ static int NxpSysInfo(uint8_t *uid) { packet->raw[packet->rawlen++] = 0x04; // IC manufacturer code memcpy(packet->raw + 3, uid, 8); // add UID - packet->rawlen += HF15_UID_LENGTH; + packet->rawlen += ISO15693_UID_LENGTH; AddCrc15(packet->raw, packet->rawlen); packet->rawlen += 2; @@ -900,11 +889,11 @@ static int StCheckSig(uint8_t *uid) { } // ISO15693 Protocol params - packet->raw[packet->rawlen++] = arg_get_raw_flag(HF15_UID_LENGTH, false, false, false); + packet->raw[packet->rawlen++] = arg_get_raw_flag(ISO15693_UID_LENGTH, false, false, false); packet->raw[packet->rawlen++] = ISO15693_READBLOCK; // add UID (scan, uid) - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; packet->flags = (ISO15_CONNECT | ISO15_READ_RESPONSE | ISO15_NO_DISCONNECT); uint16_t blkoff = packet->rawlen; char signature_hex[65] = {0}; @@ -943,9 +932,9 @@ static int StCheckSig(uint8_t *uid) { uint8_t signature[16]; size_t signature_len; hexstr_to_byte_array(signature_hex, signature, &signature_len); - uint8_t uid_swap[HF15_UID_LENGTH]; - reverse_array_copy(uid, HF15_UID_LENGTH, uid_swap); - int index = originality_check_verify_ex(uid_swap, HF15_UID_LENGTH, signature, signature_len, PK_ST25TV, false, true); + uint8_t uid_swap[ISO15693_UID_LENGTH]; + reverse_array_copy(uid, ISO15693_UID_LENGTH, uid_swap); + int index = originality_check_verify_ex(uid_swap, ISO15693_UID_LENGTH, signature, signature_len, PK_ST25TV, false, true); PrintAndLogEx(NORMAL, ""); return originality_check_print(signature, signature_len, index); } @@ -970,7 +959,7 @@ static int CmdHF15Info(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); bool unaddressed = arg_get_lit(ctx, 2); @@ -987,7 +976,7 @@ static int CmdHF15Info(const char *Cmd) { } // default fallback to scan for tag. - if (unaddressed == false && uidlen != HF15_UID_LENGTH) { + if (unaddressed == false && uidlen != ISO15693_UID_LENGTH) { scan = true; } @@ -1014,10 +1003,10 @@ static int CmdHF15Info(const char *Cmd) { free(packet); return PM3_EINVARG; } - uidlen = HF15_UID_LENGTH; + uidlen = ISO15693_UID_LENGTH; } - if (uidlen == HF15_UID_LENGTH) { + if (uidlen == ISO15693_UID_LENGTH) { // add UID (scan, uid) memcpy(packet->raw + packet->rawlen, uid, uidlen); packet->rawlen += uidlen; @@ -1251,8 +1240,11 @@ static int CmdHF15ELoad(const char *Cmd) { ((tag->pagesCount * tag->bytesPerPage) > ISO15693_TAG_MAX_SIZE) || (tag->pagesCount == 0) || (tag->bytesPerPage == 0)) { + PrintAndLogEx(FAILED, "Tag size error: pagesCount=%d, bytesPerPage=%d", - tag->pagesCount, tag->bytesPerPage); + tag->pagesCount, + tag->bytesPerPage + ); free(tag); return PM3_EINVARG; } @@ -1486,7 +1478,7 @@ static int CmdHF15Sim(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); struct { - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; uint8_t block_size; } PACKED payload; memset(&payload, 0, sizeof(payload)); @@ -1497,7 +1489,7 @@ static int CmdHF15Sim(const char *Cmd) { CLIParserFree(ctx); // sanity checks - if (uidlen != 0 && uidlen != HF15_UID_LENGTH) { + if (uidlen != 0 && uidlen != ISO15693_UID_LENGTH) { PrintAndLogEx(WARNING, "UID must include 8 hex bytes, got ( " _RED_("%i") " )", uidlen); return PM3_EINVARG; } @@ -1624,7 +1616,7 @@ static int CmdHF15WriteAfi(const char *Cmd) { struct { uint8_t pwd[4]; bool use_pwd; - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; bool use_uid; uint8_t afi; } PACKED payload; @@ -1645,7 +1637,7 @@ static int CmdHF15WriteAfi(const char *Cmd) { } payload.use_uid = false; - if (uidlen == HF15_UID_LENGTH) { + if (uidlen == ISO15693_UID_LENGTH) { payload.use_uid = true; } @@ -1703,7 +1695,7 @@ static int CmdHF15WriteDsfid(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH] = {0}; + uint8_t uid[ISO15693_UID_LENGTH] = {0}; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); @@ -1742,10 +1734,10 @@ static int CmdHF15WriteDsfid(const char *Cmd) { free(packet); return PM3_EINVARG; } - uidlen = HF15_UID_LENGTH; + uidlen = ISO15693_UID_LENGTH; } - if (uidlen == HF15_UID_LENGTH) { + if (uidlen == ISO15693_UID_LENGTH) { // add UID (scan, uid) memcpy(packet->raw + packet->rawlen, uid, uidlen); packet->rawlen += uidlen; @@ -1807,7 +1799,7 @@ static int CmdHF15Dump(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH] = {0}; + uint8_t uid[ISO15693_UID_LENGTH] = {0}; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); @@ -1838,7 +1830,7 @@ static int CmdHF15Dump(const char *Cmd) { } // default fallback to scan for tag. - if (uidlen != HF15_UID_LENGTH && !unaddressed) { + if (uidlen != ISO15693_UID_LENGTH && !unaddressed) { scan = true; } @@ -1874,11 +1866,11 @@ static int CmdHF15Dump(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } // add UID (scan, uid) - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; used_uid = true; } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -1915,35 +1907,49 @@ static int CmdHF15Dump(const char *Cmd) { uint8_t dCpt = 10; int res = iso15_error_handling_card_response(d, resp.length); - if (res != PM3_SUCCESS) { + if (res == PM3_ECRC) { free(tag); free(packet); return res; } - memcpy(tag->uid, &d[2], 8); + if (res == PM3_SUCCESS) { + memcpy(tag->uid, d + 2, 8); - if (d[1] & 0x01) { - tag->dsfid = d[dCpt++]; - } + if (d[1] & 0x01) { + tag->dsfid = d[dCpt]; + } + dCpt++; - if (d[1] & 0x02) { - tag->afi = d[dCpt++]; - } + if (d[1] & 0x02) { + tag->afi = d[dCpt]; + } + dCpt++; + + if (d[1] & 0x04) { + tag->pagesCount = d[dCpt] + 1; + tag->bytesPerPage = d[dCpt + 1] + 1; + } else { + // Set tag memory layout values (if can't be read in SYSINFO) + tag->bytesPerPage = blocksize; + tag->pagesCount = 128; + } + dCpt += 2; + + if (d[1] & 0x08) { + tag->ic = d[dCpt]; + } + dCpt++; - if (d[1] & 0x04) { - tag->pagesCount = d[dCpt++] + 1; - tag->bytesPerPage = d[dCpt++] + 1; } else { + tag->uid[0] = 0xE0; + tag->dsfid = 0; + tag->afi = 0; // Set tag memory layout values (if can't be read in SYSINFO) tag->bytesPerPage = blocksize; tag->pagesCount = 128; } - if (d[1] & 0x08) { - tag->ic = d[dCpt++]; - } - // add length for blockno (1) packet->rawlen++; packet->raw[0] |= ISO15_REQ_OPTION; // Add option to dump lock status @@ -2178,10 +2184,10 @@ static int CmdHF15Readmulti(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH] = {0x00}; + uint8_t uid[ISO15693_UID_LENGTH] = {0x00}; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; //Default fallback to scan for tag. Overriding unaddressed parameter. @@ -2239,11 +2245,11 @@ static int CmdHF15Readmulti(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } // add UID (scan, uid) - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2255,7 +2261,9 @@ static int CmdHF15Readmulti(const char *Cmd) { // 0 means 1 page, // 1 means 2 pages, ... - if (blockcnt > 0) blockcnt--; + if (blockcnt > 0) { + blockcnt--; + } packet->raw[packet->rawlen++] = blockno; packet->raw[packet->rawlen++] = blockcnt; @@ -2285,7 +2293,7 @@ static int CmdHF15Readmulti(const char *Cmd) { ISO15_ERROR_HANDLING_CARD_RESPONSE(d, resp.length) // 1 byte cmd, 1 lock byte, 4 / 8 bytes block size, 2 crc - if (resp.length > (1 + (blockcnt * (blocksize + 1)) + 2)) { + if (resp.length > (1 + ((blockcnt + 1) * (blocksize + 1)) + 2)) { PrintAndLogEx(WARNING, "got longer response. Check block size!"); } @@ -2336,10 +2344,10 @@ static int CmdHF15Readblock(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; // Default fallback to scan for tag. Overriding unaddressed parameter. @@ -2393,11 +2401,11 @@ static int CmdHF15Readblock(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } // add UID (scan, uid) - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2490,8 +2498,8 @@ static int hf_15_write_blk(const uint8_t *pm3flags, uint16_t flags, const uint8_ // add UID if (uid) { - memcpy(packet->raw + packet->rawlen, uid, HF15_UID_LENGTH); - packet->rawlen += HF15_UID_LENGTH; + memcpy(packet->raw + packet->rawlen, uid, ISO15693_UID_LENGTH); + packet->rawlen += ISO15693_UID_LENGTH; } packet->raw[packet->rawlen++] = blockno; @@ -2550,10 +2558,10 @@ static int CmdHF15Write(const char *Cmd) { argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; // Default fallback to scan for tag. Overriding unaddressed parameter. @@ -2589,7 +2597,7 @@ static int CmdHF15Write(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2624,7 +2632,7 @@ static int CmdHF15Restore(const char *Cmd) { "hf 15 restore -u E011223344556677 -f hf-15-my-dump.bin" ); - void *argtable[6 + 5] = {0}; + void *argtable[6 + 4] = {0}; uint8_t arglen = arg_add_default(argtable); argtable[arglen++] = arg_str0("f", "file", "", "Specify a filename for dump file"); argtable[arglen++] = arg_int0("r", "retry", "", "number of retries (def 3)"); @@ -2632,7 +2640,7 @@ static int CmdHF15Restore(const char *Cmd) { argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); @@ -2662,7 +2670,7 @@ static int CmdHF15Restore(const char *Cmd) { // default fallback to scan for tag. // overriding unaddress parameter :) - if (uidlen != HF15_UID_LENGTH) { + if (uidlen != ISO15693_UID_LENGTH) { scan = true; } @@ -2674,7 +2682,7 @@ static int CmdHF15Restore(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } } else { PrintAndLogEx(INFO, "Using unaddressed mode"); @@ -2713,8 +2721,11 @@ static int CmdHF15Restore(const char *Cmd) { ((tag->pagesCount * tag->bytesPerPage) > ISO15693_TAG_MAX_SIZE) || (tag->pagesCount == 0) || (tag->bytesPerPage == 0)) { + PrintAndLogEx(FAILED, "Tag size error: pagesCount=%d, bytesPerPage=%d", - tag->pagesCount, tag->bytesPerPage); + tag->pagesCount, + tag->bytesPerPage + ); free(tag); return PM3_EINVARG; } @@ -2745,8 +2756,8 @@ static int CmdHF15Restore(const char *Cmd) { for (tried = 0; tried < retries; tried++) { - retval = hf_15_write_blk(&pm3flags, flags, uid, fast - , i, data, tag->bytesPerPage); + retval = hf_15_write_blk(&pm3flags, flags, uid, fast, i, data, tag->bytesPerPage); + if (retval == PM3_SUCCESS) { PrintAndLogEx(INPLACE, "blk %3d", i); @@ -2809,7 +2820,7 @@ static int CmdHF15CSetUID(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, false); struct { - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; } PACKED payload; int uidlen = 0; @@ -2817,7 +2828,7 @@ static int CmdHF15CSetUID(const char *Cmd) { bool use_v2 = arg_get_lit(ctx, 2); CLIParserFree(ctx); - if (uidlen != HF15_UID_LENGTH) { + if (uidlen != ISO15693_UID_LENGTH) { PrintAndLogEx(WARNING, "UID must include 8 hex bytes, got " _RED_("%i"), uidlen); return PM3_EINVARG; } @@ -2831,7 +2842,7 @@ static int CmdHF15CSetUID(const char *Cmd) { PrintAndLogEx(INFO, "Get current tag"); - uint8_t carduid[HF15_UID_LENGTH] = {0x00}; + uint8_t carduid[ISO15693_UID_LENGTH] = {0x00}; if (getUID(true, false, carduid) != PM3_SUCCESS) { PrintAndLogEx(FAILED, "no tag found"); return PM3_ESOFT; @@ -2861,10 +2872,10 @@ static int CmdHF15CSetUID(const char *Cmd) { } // reverse cardUID to compare - uint8_t revuid[HF15_UID_LENGTH] = {0}; + uint8_t revuid[ISO15693_UID_LENGTH] = {0}; reverse_array_copy(carduid, sizeof(carduid), revuid); - if (memcmp(revuid, payload.uid, HF15_UID_LENGTH) == 0) { + if (memcmp(revuid, payload.uid, ISO15693_UID_LENGTH) == 0) { PrintAndLogEx(SUCCESS, "Setting new UID ( " _GREEN_("ok") " )"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS;; @@ -3478,8 +3489,11 @@ static int CmdHF15View(const char *Cmd) { ((tag->pagesCount * tag->bytesPerPage) > ISO15693_TAG_MAX_SIZE) || (tag->pagesCount == 0) || (tag->bytesPerPage == 0)) { + PrintAndLogEx(FAILED, "Tag size error: pagesCount=%d, bytesPerPage=%d", - tag->pagesCount, tag->bytesPerPage); + tag->pagesCount, + tag->bytesPerPage + ); free(tag); return PM3_EINVARG; } @@ -3498,15 +3512,15 @@ static int CmdHF15Wipe(const char *Cmd) { ); void *argtable[6 + 3] = {0}; uint8_t arglen = arg_add_default(argtable); - argtable[arglen++] = arg_int0(NULL, "bs", "", "block size (def 4)"), - argtable[arglen++] = arg_lit0("v", "verbose", "verbose output"); + argtable[arglen++] = arg_int0(NULL, "bs", "", "block size (def 4)"); + argtable[arglen++] = arg_lit0("v", "verbose", "verbose output"); argtable[arglen++] = arg_param_end; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[HF15_UID_LENGTH]; + uint8_t uid[ISO15693_UID_LENGTH]; int uidlen = 0; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - bool uid_set = (uidlen == HF15_UID_LENGTH) ? true : false; + bool uid_set = (uidlen == ISO15693_UID_LENGTH) ? true : false; bool unaddressed = arg_get_lit(ctx, 2); bool scan = (arg_get_lit(ctx, 3) || (!uid_set && !unaddressed)) ? true : false; // Default fallback to scan for tag. Overriding unaddressed parameter. @@ -3538,7 +3552,7 @@ static int CmdHF15Wipe(const char *Cmd) { return PM3_EINVARG; } } else { - reverse_array(uid, HF15_UID_LENGTH); + reverse_array(uid, ISO15693_UID_LENGTH); } } else { PrintAndLogEx(INFO, "Using unaddressed mode"); diff --git a/client/src/cmdhf15.h b/client/src/cmdhf15.h index 8803a58b4..b6f7d250b 100644 --- a/client/src/cmdhf15.h +++ b/client/src/cmdhf15.h @@ -20,9 +20,24 @@ #define CMDHF15_H__ #include "common.h" +#include "crc16.h" +#include "iso15.h" // typedef structs / enum + +#ifndef Crc15 +# define Crc15(data, len) Crc16ex(CRC_15693, (data), (len)) +#endif +#ifndef CheckCrc15 +# define CheckCrc15(data, len) check_crc(CRC_15693, (data), (len)) +#endif +#ifndef AddCrc15 +#define AddCrc15(data, len) compute_crc(CRC_15693, (data), (len), (data)+(len), (data)+(len)+1) +#endif + +#ifndef ISO15_RAW_LEN +#define ISO15_RAW_LEN(x) (sizeof(iso15_raw_cmd_t) + (x)) +#endif int CmdHF15(const char *Cmd); - bool readHF15Uid(bool loop, bool verbose); #endif diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c index 3f6e76ec2..9e0ad8976 100644 --- a/client/src/cmdhfcipurse.c +++ b/client/src/cmdhfcipurse.c @@ -1888,7 +1888,8 @@ static int CmdHFCipurseDefault(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help."}, - {"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Get info about CIPURSE tag"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "------------------- " _CYAN_("Operations") " -------------------"}, + {"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Tag information"}, {"select", CmdHFCipurseSelect, IfPm3Iso14443a, "Select CIPURSE application or file"}, {"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authenticate CIPURSE tag"}, {"read", CmdHFCipurseReadFile, IfPm3Iso14443a, "Read binary file"}, diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 896877a74..5d0182d56 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -2449,8 +2449,9 @@ static int CmdHFeMRTDList(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"-----------", CmdHelp, IfPm3Iso14443, "------------------- " _CYAN_("Operations") " -------------------"}, {"dump", CmdHFeMRTDDump, IfPm3Iso14443, "Dump eMRTD files to binary files"}, - {"info", CmdHFeMRTDInfo, AlwaysAvailable, "Display info about an eMRTD"}, + {"info", CmdHFeMRTDInfo, AlwaysAvailable, "Tag information"}, {"list", CmdHFeMRTDList, AlwaysAvailable, "List ISO 14443A/7816 history"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhffelica.c b/client/src/cmdhffelica.c index d52cb52da..278b301c4 100644 --- a/client/src/cmdhffelica.c +++ b/client/src/cmdhffelica.c @@ -1803,7 +1803,7 @@ static int CmdHFFelicaSniff(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); msleep(300); break; } @@ -1851,7 +1851,7 @@ static int CmdHFFelicaSimLite(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); msleep(300); break; } @@ -2054,7 +2054,7 @@ static int CmdHFFelicaDumpLite(const char *Cmd) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); return PM3_EOPABORTED; } diff --git a/client/src/cmdhffido.c b/client/src/cmdhffido.c index 2032dca95..54560354b 100644 --- a/client/src/cmdhffido.c +++ b/client/src/cmdhffido.c @@ -913,7 +913,8 @@ static int CmdHFFido2GetAssertion(const char *cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help."}, {"list", CmdHFFidoList, AlwaysAvailable, "List ISO 14443A history"}, - {"info", CmdHFFidoInfo, IfPm3Iso14443a, "Info about FIDO tag."}, + {"-----------", CmdHelp, IfPm3Iso14443a, "------------------- " _CYAN_("Operations") " -------------------"}, + {"info", CmdHFFidoInfo, IfPm3Iso14443a, "Tag information"}, {"reg", CmdHFFidoRegister, IfPm3Iso14443a, "FIDO U2F Registration Message."}, {"auth", CmdHFFidoAuthenticate, IfPm3Iso14443a, "FIDO U2F Authentication Message."}, {"make", CmdHFFido2MakeCredential, IfPm3Iso14443a, "FIDO2 MakeCredential command."}, diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index b21282ce2..f305c33e5 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -40,19 +40,21 @@ #include "crypto/asn1utils.h" // ASN1 decoder #include "preferences.h" #include "generator.h" -#include "cmdhf14b.h" #include "cmdhw.h" #include "hidsio.h" +#define ICLASS_DEBIT_KEYTYPE ( 0x88 ) +#define ICLASS_CREDIT_KEYTYPE ( 0x18 ) + #define NUM_CSNS 9 #define MAC_ITEM_SIZE 24 // csn(8) + epurse(8) + nr(4) + mac(4) = 24 bytes #define ICLASS_KEYS_MAX 8 #define ICLASS_AUTH_RETRY 10 #define ICLASS_CFG_BLK_SR_BIT 0xA0 // indicates SIO present when set in block6[0] (legacy tags) -#define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin" -#define ICLASS_DEFAULT_KEY_DIC "iclass_default_keys.dic" -#define ICLASS_DEFAULT_KEY_ELITE_DIC "iclass_elite_keys.dic" +#define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin" +#define ICLASS_DEFAULT_KEY_DIC "iclass_default_keys.dic" +#define ICLASS_DEFAULT_KEY_ELITE_DIC "iclass_elite_keys.dic" static void print_picopass_info(const picopass_hdr_t *hdr); void print_picopass_header(const picopass_hdr_t *hdr); @@ -325,6 +327,29 @@ static const iclass_config_card_item_t *get_config_card_item(int idx) { static void print_config_cards(void) { PrintAndLogEx(INFO, "---- " _CYAN_("Config cards options") " ------------"); for (int i = 0; i < ARRAYLEN(iclass_config_options) ; ++i) { + switch (i) { + case 0: + PrintAndLogEx(INFO, _YELLOW_("---- LED Operations ----")); + break; + case 16: + PrintAndLogEx(INFO, _YELLOW_("---- BEEP Operations ----")); + break; + case 18: + PrintAndLogEx(INFO, _YELLOW_("---- Mifare Operations ----")); + break; + case 22: + PrintAndLogEx(INFO, _YELLOW_("---- Keypad Operations ----")); + break; + case 25: + PrintAndLogEx(INFO, _YELLOW_("---- iClass Operations ----")); + break; + case 29: + PrintAndLogEx(INFO, _YELLOW_("---- Reset Operations ----")); + break; + case 31: + PrintAndLogEx(INFO, _YELLOW_("---- iClass Master Key Operations ----")); + break; + } PrintAndLogEx(INFO, "%2d, %s", i, iclass_config_options[i].desc); } PrintAndLogEx(NORMAL, ""); @@ -584,14 +609,14 @@ static void fuse_config(const picopass_hdr_t *hdr) { uint16_t otp = (hdr->conf.otp[1] << 8 | hdr->conf.otp[0]); - PrintAndLogEx(INFO, " Raw... " _YELLOW_("%s"), sprint_hex((uint8_t *)&hdr->conf, 8)); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") " ( %3u )............. app limit", hdr->conf.app_limit, hdr->conf.app_limit); - PrintAndLogEx(INFO, " " _YELLOW_("%04X") " ( %5u )...... OTP", otp, otp); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "............ block write lock", hdr->conf.block_writelock); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "......... chip", hdr->conf.chip_config); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "...... mem", hdr->conf.mem_config); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") "... EAS", hdr->conf.eas); - PrintAndLogEx(INFO, " " _YELLOW_("%02X") " fuses", hdr->conf.fuses); + PrintAndLogEx(INFO, " Raw: " _YELLOW_("%s"), sprint_hex((uint8_t *)&hdr->conf, 8)); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") " ( %3u )............. app limit", hdr->conf.app_limit, hdr->conf.app_limit); + PrintAndLogEx(INFO, " " _YELLOW_("%04X") " ( %5u )...... OTP", otp, otp); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "............ block write lock", hdr->conf.block_writelock); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "......... chip", hdr->conf.chip_config); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "...... mem", hdr->conf.mem_config); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") "... EAS", hdr->conf.eas); + PrintAndLogEx(INFO, " " _YELLOW_("%02X") " fuses", hdr->conf.fuses); uint8_t fuses = hdr->conf.fuses; @@ -702,7 +727,7 @@ static void mem_app_config(const picopass_hdr_t *hdr) { uint8_t app2_limit = card_app2_limit[type]; uint8_t pagemap = get_pagemap(hdr); - PrintAndLogEx(INFO, "-------------------------- " _CYAN_("Memory") " --------------------------"); + PrintAndLogEx(INFO, "------------------------ " _CYAN_("Memory") " -------------------------"); if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { PrintAndLogEx(INFO, " %u KBits ( " _YELLOW_("%u") " bytes )", kb, app2_limit * 8); @@ -731,7 +756,7 @@ static void mem_app_config(const picopass_hdr_t *hdr) { [=] AA2 blocks 5 { 0x100 - 0xFF (256 - 255) } */ - PrintAndLogEx(INFO, "------------------------- " _CYAN_("KeyAccess") " ------------------------"); + PrintAndLogEx(INFO, "----------------------- " _CYAN_("KeyAccess") " -----------------------"); PrintAndLogEx(INFO, " * Kd, Debit key, AA1 Kc, Credit key, AA2 *"); uint8_t keyAccess = isset(mem, 0x01); if (keyAccess) { @@ -752,13 +777,13 @@ static void mem_app_config(const picopass_hdr_t *hdr) { } void print_picopass_info(const picopass_hdr_t *hdr) { - PrintAndLogEx(INFO, "-------------------- " _CYAN_("Card configuration") " --------------------"); + PrintAndLogEx(INFO, "------------------- " _CYAN_("Card configuration") " ------------------"); fuse_config(hdr); mem_app_config(hdr); } void print_picopass_header(const picopass_hdr_t *hdr) { - PrintAndLogEx(INFO, "--------------------------- " _CYAN_("Card") " ---------------------------"); + PrintAndLogEx(INFO, "-------------------------- " _CYAN_("Card") " -------------------------"); PrintAndLogEx(SUCCESS, " CSN... " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); PrintAndLogEx(SUCCESS, " Config... %s card configuration", sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf))); PrintAndLogEx(SUCCESS, "E-purse... %s card challenge, CC", sprint_hex(hdr->epurse, sizeof(hdr->epurse))); @@ -766,13 +791,13 @@ void print_picopass_header(const picopass_hdr_t *hdr) { if (memcmp(hdr->key_d, zeros, sizeof(zeros)) && memcmp(hdr->key_d, empty, sizeof(empty))) { PrintAndLogEx(SUCCESS, " Kd... " _YELLOW_("%s") " debit key", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); } else { - PrintAndLogEx(SUCCESS, " Kd... %s debit key ( hidden )", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); + PrintAndLogEx(SUCCESS, " Kd... -- -- -- -- -- -- -- -- debit key ( hidden )"); } if (memcmp(hdr->key_c, zeros, sizeof(zeros)) && memcmp(hdr->key_c, empty, sizeof(empty))) { PrintAndLogEx(SUCCESS, " Kc... " _YELLOW_("%s") " credit key", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); } else { - PrintAndLogEx(SUCCESS, " Kc... %s credit key ( hidden )", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); + PrintAndLogEx(SUCCESS, " Kc... -- -- -- -- -- -- -- -- credit key ( hidden )"); } PrintAndLogEx(SUCCESS, " AIA... %s application issuer area", sprint_hex(hdr->app_issuer_area, sizeof(hdr->app_issuer_area))); @@ -842,7 +867,9 @@ static int CmdHFiClassSim(const char *Cmd) { "hf iclass sim -t 1 --> simulate with default CSN\n" "hf iclass sim -t 2 --> execute loclass attack online part\n" "hf iclass sim -t 3 --> simulate full iCLASS 2k tag\n" - "hf iclass sim -t 4 --> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key"); + "hf iclass sim -t 4 --> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key\n" + "hf iclass sim -t 6 --> simulate full iCLASS 2k tag that doesn't respond to r/w requests to the last SIO block\n" + "hf iclass sim -t 7 --> simulate full iCLASS 2k tag that doesn't XOR or respond to r/w requests on block 3"); void *argtable[] = { arg_param_begin, @@ -873,7 +900,7 @@ static int CmdHFiClassSim(const char *Cmd) { CLIParserFree(ctx); - if (sim_type > 4) { + if (sim_type > 4 && sim_type != 6 && sim_type != 7) { PrintAndLogEx(ERR, "Undefined simtype %d", sim_type); return PM3_EINVARG; } @@ -1026,6 +1053,8 @@ static int CmdHFiClassSim(const char *Cmd) { case ICLASS_SIM_MODE_CSN: case ICLASS_SIM_MODE_CSN_DEFAULT: case ICLASS_SIM_MODE_FULL: + case ICLASS_SIM_MODE_FULL_GLITCH: + case ICLASS_SIM_MODE_FULL_GLITCH_KEY: default: { PrintAndLogEx(INFO, "Starting iCLASS simulation"); PrintAndLogEx(INFO, "Press " _GREEN_("`pm3 button`") " to abort"); @@ -1033,7 +1062,7 @@ static int CmdHFiClassSim(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ICLASS_SIMULATE, sim_type, numberOfCSNs, 1, csn, 8); - if (sim_type == ICLASS_SIM_MODE_FULL) + if (sim_type == ICLASS_SIM_MODE_FULL || sim_type == ICLASS_SIM_MODE_FULL_GLITCH || sim_type == ICLASS_SIM_MODE_FULL_GLITCH_KEY) PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("hf iclass esave -h") "` to save the emulator memory to file"); break; } @@ -1467,8 +1496,9 @@ static void iclass_decode_credentials(uint8_t *data) { bool has_values = (memcmp(b7, empty, PICOPASS_BLOCK_SIZE) != 0) && (memcmp(b7, zeros, PICOPASS_BLOCK_SIZE) != 0); if (has_values && encryption == None) { - // todo: remove preamble/sentinel PrintAndLogEx(INFO, "------------------------ " _CYAN_("Block 7 decoder") " --------------------------"); + + // todo: remove preamble/sentinel if (has_new_pacs) { iclass_decode_credentials_new_pacs(b7); } else { @@ -1487,9 +1517,6 @@ static void iclass_decode_credentials(uint8_t *data) { PrintAndLogEx(NORMAL, ""); decode_wiegand(top, mid, bot, 0); } - - } else { - PrintAndLogEx(INFO, "No unencrypted legacy credential found"); } } @@ -1856,15 +1883,17 @@ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool verbose, bool shallow_ return false; } - if (CSN != NULL) - memcpy(CSN, hdr->csn, 8); + if (CSN != NULL) { + memcpy(CSN, hdr->csn, PICOPASS_BLOCK_SIZE); + } - if (CCNR != NULL) - memcpy(CCNR, hdr->epurse, 8); + if (CCNR != NULL) { + memcpy(CCNR, hdr->epurse, PICOPASS_BLOCK_SIZE); + } if (verbose) { - PrintAndLogEx(SUCCESS, "CSN %s", sprint_hex(CSN, 8)); - PrintAndLogEx(SUCCESS, "epurse %s", sprint_hex(CCNR, 8)); + PrintAndLogEx(SUCCESS, "CSN............ %s", sprint_hex_inrow(CSN, PICOPASS_BLOCK_SIZE)); + PrintAndLogEx(SUCCESS, "E-purse........ %s", sprint_hex_inrow(CCNR, PICOPASS_BLOCK_SIZE)); } return true; } @@ -1903,15 +1932,32 @@ static int CmdHFiClassDump(const char *Cmd) { int key_len = 0; uint8_t key[8] = {0}; - bool auth = false; - CLIGetHexWithReturn(ctx, 2, key, &key_len); int deb_key_nr = arg_get_int_def(ctx, 3, -1); + int credit_key_len = 0; + uint8_t credit_key[8] = {0}; + CLIGetHexWithReturn(ctx, 4, credit_key, &credit_key_len); + + int credit_key_nr = arg_get_int_def(ctx, 5, -1); + bool elite = arg_get_lit(ctx, 6); + bool rawkey = arg_get_lit(ctx, 7); + bool use_replay = arg_get_lit(ctx, 8); + bool dense_output = g_session.dense_output || arg_get_lit(ctx, 9); + bool force = arg_get_lit(ctx, 10); + bool shallow_mod = arg_get_lit(ctx, 11); + bool nosave = arg_get_lit(ctx, 12); + + CLIParserFree(ctx); + + bool auth = false; + bool have_credit_key = false; + + // Sanity checks + if (key_len > 0 && deb_key_nr >= 0) { PrintAndLogEx(ERR, "Please specify debit key or index, not both"); - CLIParserFree(ctx); return PM3_EINVARG; } @@ -1919,7 +1965,6 @@ static int CmdHFiClassDump(const char *Cmd) { auth = true; if (key_len != 8) { PrintAndLogEx(ERR, "Debit key is incorrect length"); - CLIParserFree(ctx); return PM3_EINVARG; } } @@ -1931,22 +1976,12 @@ static int CmdHFiClassDump(const char *Cmd) { PrintAndLogEx(SUCCESS, "Using AA1 (debit) key[%d] " _GREEN_("%s"), deb_key_nr, sprint_hex(iClass_Key_Table[deb_key_nr], 8)); } else { PrintAndLogEx(ERR, "Key number is invalid"); - CLIParserFree(ctx); return PM3_EINVARG; } } - int credit_key_len = 0; - uint8_t credit_key[8] = {0}; - bool have_credit_key = false; - - CLIGetHexWithReturn(ctx, 4, credit_key, &credit_key_len); - - int credit_key_nr = arg_get_int_def(ctx, 5, -1); - if (credit_key_len > 0 && credit_key_nr >= 0) { PrintAndLogEx(ERR, "Please specify credit key or index, not both"); - CLIParserFree(ctx); return PM3_EINVARG; } @@ -1955,7 +1990,6 @@ static int CmdHFiClassDump(const char *Cmd) { have_credit_key = true; if (credit_key_len != 8) { PrintAndLogEx(ERR, "Credit key is incorrect length"); - CLIParserFree(ctx); return PM3_EINVARG; } } @@ -1968,21 +2002,10 @@ static int CmdHFiClassDump(const char *Cmd) { PrintAndLogEx(SUCCESS, "Using AA2 (credit) key[%d] " _GREEN_("%s"), credit_key_nr, sprint_hex(iClass_Key_Table[credit_key_nr], 8)); } else { PrintAndLogEx(ERR, "Key number is invalid"); - CLIParserFree(ctx); return PM3_EINVARG; } } - bool elite = arg_get_lit(ctx, 6); - bool rawkey = arg_get_lit(ctx, 7); - bool use_replay = arg_get_lit(ctx, 8); - bool dense_output = g_session.dense_output || arg_get_lit(ctx, 9); - bool force = arg_get_lit(ctx, 10); - bool shallow_mod = arg_get_lit(ctx, 11); - bool nosave = arg_get_lit(ctx, 12); - - CLIParserFree(ctx); - if ((use_replay + rawkey + elite) > 1) { PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw', 'nr'"); return PM3_EINVARG; @@ -2005,7 +2028,6 @@ static int CmdHFiClassDump(const char *Cmd) { clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_HF_ICLASS_READER, (uint8_t *)&payload_rdr, sizeof(iclass_card_select_t)); - if (WaitForResponseTimeout(CMD_HF_ICLASS_READER, &resp, 2000) == false) { PrintAndLogEx(WARNING, "command execution time out"); DropField(); @@ -2062,9 +2084,11 @@ static int CmdHFiClassDump(const char *Cmd) { PrintAndLogEx(INFO, "No keys needed, ignoring user supplied key"); } } else { + if (auth == false) { - PrintAndLogEx(FAILED, "Run command with keys"); - return PM3_ESOFT; + auth = true; + memcpy(key, iClass_Key_Table[0], 8); + PrintAndLogEx(SUCCESS, "Default to AA1 (debit) " _GREEN_("%s"), sprint_hex(key, sizeof(key))); } if (app_limit2 != 0) { @@ -2106,8 +2130,9 @@ static int CmdHFiClassDump(const char *Cmd) { return PM3_EOPABORTED; } - if (WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) + if (WaitForResponseTimeout(CMD_HF_ICLASS_DUMP, &resp, 2000)) { break; + } } PrintAndLogEx(NORMAL, ""); @@ -2134,7 +2159,7 @@ static int CmdHFiClassDump(const char *Cmd) { uint8_t tempbuf[0x100 * 8]; // response ok - now get bigbuf content of the dump - if (!GetFromDevice(BIG_BUF, tempbuf, sizeof(tempbuf), startindex, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF, tempbuf, sizeof(tempbuf), startindex, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -2199,7 +2224,7 @@ static int CmdHFiClassDump(const char *Cmd) { } // get dumped data from bigbuf - if (!GetFromDevice(BIG_BUF, tempbuf, sizeof(tempbuf), startindex, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF, tempbuf, sizeof(tempbuf), startindex, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "command execution time out"); goto write_dump; } @@ -2249,7 +2274,8 @@ write_dump: return PM3_SUCCESS; } -static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *macdata, uint8_t *KEY, bool use_credit_key, bool elite, bool rawkey, bool replay, bool verbose, bool use_secure_pagemode, bool shallow_mod) { +static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *macdata, uint8_t *KEY, bool use_credit_key, + bool elite, bool rawkey, bool replay, bool verbose, bool use_secure_pagemode, bool shallow_mod) { iclass_writeblock_req_t payload = { .req.use_raw = rawkey, @@ -2272,7 +2298,7 @@ static int iclass_write_block(uint8_t blockno, uint8_t *bldata, uint8_t *macdata SendCommandNG(CMD_HF_ICLASS_WRITEBL, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_ICLASS_WRITEBL, &resp, 2000) == 0) { + if (WaitForResponseTimeout(CMD_HF_ICLASS_WRITEBL, &resp, 2000) == false) { if (verbose) PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -2303,7 +2329,7 @@ static int CmdHFiClass_WriteBlock(const char *Cmd) { arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"), - arg_lit0(NULL, "nr", "replay of NR/MAC"), + arg_lit0(NULL, "nr", "replay of NR/MAC block write or use privilege escalation if mac is empty"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), arg_param_end @@ -2406,14 +2432,13 @@ static int CmdHFiClassCreditEpurse(const char *Cmd) { "Credit the epurse on an iCLASS tag. The provided key must be the credit key.\n" "The first two bytes of the epurse are the debit value (big endian) and may be any value except FFFF.\n" "The remaining two bytes of the epurse are the credit value and must be smaller than the previous value.", - "hf iclass creditepurse -d FEFFFFFF -k 001122334455667B\n" - "hf iclass creditepurse -d FEFFFFFF --ki 0"); + "hf iclass creditepurse --ki 0 -d FEFFFEFF"); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "Credit key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), - arg_str1("d", "data", "", "data to write as 8 hex bytes"), + arg_str1("d", "data", "", "data to write as 4 hex bytes"), arg_lit0(NULL, "elite", "elite computations applied to key"), arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0("v", "verbose", "verbose output"), @@ -2498,7 +2523,7 @@ static int CmdHFiClassCreditEpurse(const char *Cmd) { PacketResponseNG resp; int isok; - if (WaitForResponseTimeout(CMD_HF_ICLASS_CREDIT_EPURSE, &resp, 2000) == 0) { + if (WaitForResponseTimeout(CMD_HF_ICLASS_CREDIT_EPURSE, &resp, 2000) == false) { if (verbose) PrintAndLogEx(WARNING, "command execution time out"); isok = PM3_ETIMEOUT; } else if (resp.status != PM3_SUCCESS) { @@ -2544,6 +2569,7 @@ static int CmdHFiClassRestore(const char *Cmd) { arg_lit0(NULL, "raw", "no computations applied to key"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), + arg_lit0(NULL, "nr", "replay of nr mac with privilege escalation"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -2594,6 +2620,7 @@ static int CmdHFiClassRestore(const char *Cmd) { bool rawkey = arg_get_lit(ctx, 8); bool verbose = arg_get_lit(ctx, 9); bool shallow_mod = arg_get_lit(ctx, 10); + bool use_replay = arg_get_lit(ctx, 11); CLIParserFree(ctx); @@ -2640,7 +2667,7 @@ static int CmdHFiClassRestore(const char *Cmd) { payload->req.use_raw = rawkey; payload->req.use_elite = elite; payload->req.use_credit_key = use_credit_key; - payload->req.use_replay = false; + payload->req.use_replay = use_replay; payload->req.blockno = startblock; payload->req.send_reply = true; payload->req.do_auth = true; @@ -2680,7 +2707,7 @@ static int CmdHFiClassRestore(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_HF_ICLASS_RESTORE, (uint8_t *)payload, payload_size); - if (WaitForResponseTimeout(CMD_HF_ICLASS_RESTORE, &resp, 2500) == 0) { + if (WaitForResponseTimeout(CMD_HF_ICLASS_RESTORE, &resp, 2500) == false) { PrintAndLogEx(WARNING, "command execution time out"); DropField(); free(payload); @@ -2812,10 +2839,10 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { int blockno = arg_get_int_def(ctx, 3, 0); - uint8_t keyType = 0x88; //debit key + uint8_t keyType = ICLASS_DEBIT_KEYTYPE; if (arg_get_lit(ctx, 4)) { PrintAndLogEx(SUCCESS, "Using " _YELLOW_("credit") " key"); - keyType = 0x18; //credit key + keyType = ICLASS_CREDIT_KEYTYPE; } bool elite = arg_get_lit(ctx, 5); @@ -2917,19 +2944,68 @@ static int CmdHFiClass_ReadBlock(const char *Cmd) { return PM3_SUCCESS; } + +static void iclass_cmp_print(uint8_t *b1, uint8_t *b2, const char *header1, const char *header2) { + + char line1[240] = {0}; + char line2[240] = {0}; + + strcat(line1, header1); + strcat(line2, header2); + + for (uint8_t i = 0; i < PICOPASS_BLOCK_SIZE; i++) { + + int l1 = strlen(line1); + int l2 = strlen(line2); + + uint8_t hi1 = NIBBLE_HIGH(b1[i]); + uint8_t low1 = NIBBLE_LOW(b1[i]); + + uint8_t hi2 = NIBBLE_HIGH(b2[i]); + uint8_t low2 = NIBBLE_LOW(b2[i]); + + if (hi1 != hi2) { + snprintf(line1 + l1, sizeof(line1) - l1, _RED_("%1X"), hi1); + snprintf(line2 + l2, sizeof(line2) - l2, _GREEN_("%1X"), hi2); + } else { + snprintf(line1 + l1, sizeof(line1) - l1, "%1X", hi1); + snprintf(line2 + l2, sizeof(line2) - l2, "%1X", hi2); + } + + l1 = strlen(line1); + l2 = strlen(line2); + + if (low1 != low2) { + snprintf(line1 + l1, sizeof(line1) - l1, _RED_("%1X"), low1); + snprintf(line2 + l2, sizeof(line2) - l2, _GREEN_("%1X"), low2); + } else { + snprintf(line1 + l1, sizeof(line1) - l1, "%1X", low1); + snprintf(line2 + l2, sizeof(line2) - l2, "%1X", low2); + } + } + + PrintAndLogEx(INFO, "%s", line1); + PrintAndLogEx(INFO, "%s", line2); +} + static int CmdHFiClass_TearBlock(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "hf iclass trbl", - "Tear off an iCLASS tag block", - "hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --tdb 100 --tde 150\n" - "hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA --ki 0 --tdb 100 --tde 150"); + CLIParserInit(&ctx, "hf iclass tear", + "Tear off an iCLASS tag block\n" + "e-purse usually 300-500us to trigger the erase phase\n" + "also seen 1800-2100us on some cards\n" + "Make sure you know the target card credit key. Typical `--ki 1` or `--ki 3`\n", + "hf iclass tear --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B -s 300 -e 600\n" + "hf iclass tear --blk 10 -d AAAAAAAAAAAAAAAA --ki 0 -s 300 -e 600\n" + "hf iclass tear --blk 2 -d fdffffffffffffff --ki 1 --credit -s 400 -e 500" + ); void *argtable[] = { arg_param_begin, arg_str0("k", "key", "", "Access key as 8 hex bytes"), arg_int0(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), arg_int1(NULL, "blk", "", "block number"), - arg_str1("d", "data", "", "data to write as 8 hex bytes"), + arg_str0("d", "data", "", "data to write as 8 hex bytes"), arg_str0("m", "mac", "", "replay mac data (4 hex bytes)"), arg_lit0(NULL, "credit", "key is assumed to be the credit key"), arg_lit0(NULL, "elite", "elite computations applied to key"), @@ -2937,83 +3013,31 @@ static int CmdHFiClass_TearBlock(const char *Cmd) { arg_lit0(NULL, "nr", "replay of NR/MAC"), arg_lit0("v", "verbose", "verbose output"), arg_lit0(NULL, "shallow", "use shallow (ASK) reader modulation instead of OOK"), - arg_int1(NULL, "tdb", "", "tearoff delay start in ms"), - arg_int1(NULL, "tde", "", "tearoff delay end in ms"), + arg_int1("s", NULL, "", "tearoff delay start (in us) must be between 1 and 43000 (43ms). Precision is about 1/3 us"), + arg_int0("i", NULL, "", "tearoff delay increment (in us) - default 10"), + arg_int0("e", NULL, "", "tearoff delay end (in us) must be a higher value than the start delay"), + arg_int0(NULL, "loop", "", "number of times to loop per tearoff time"), + arg_int0(NULL, "sleep", "", "Sleep between each tear"), + arg_lit0(NULL, "arm", "Runs the commands on device side and tries to stabilize tears"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); int key_len = 0; uint8_t key[8] = {0}; - CLIGetHexWithReturn(ctx, 1, key, &key_len); int key_nr = arg_get_int_def(ctx, 2, -1); - - if (key_len > 0 && key_nr >= 0) { - PrintAndLogEx(ERR, "Please specify key or index, not both"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - - bool auth = false; - - if (key_len > 0) { - auth = true; - if (key_len != 8) { - PrintAndLogEx(ERR, "Key is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } else if (key_nr >= 0) { - if (key_nr < ICLASS_KEYS_MAX) { - auth = true; - memcpy(key, iClass_Key_Table[key_nr], 8); - PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); - } else { - PrintAndLogEx(ERR, "Key number is invalid"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } - int blockno = arg_get_int_def(ctx, 3, 0); int data_len = 0; uint8_t data[8] = {0}; CLIGetHexWithReturn(ctx, 4, data, &data_len); - if (data_len != 8) { - PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 hex symbols)"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - int mac_len = 0; uint8_t mac[4] = {0}; CLIGetHexWithReturn(ctx, 5, mac, &mac_len); - if (mac_len) { - if (mac_len != 4) { - PrintAndLogEx(ERR, "MAC must be 4 hex bytes (8 hex symbols)"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } - - int tearoff_start = arg_get_int_def(ctx, 12, 100); - int tearoff_end = arg_get_int_def(ctx, 13, 200); - - if (tearoff_end <= tearoff_start) { - PrintAndLogEx(ERR, "Tearoff end delay must be bigger than the start delay."); - return PM3_EINVARG; - } - - if (tearoff_start < 0 || tearoff_end <= 0) { - PrintAndLogEx(ERR, "Tearoff start/end delays should be bigger than 0."); - return PM3_EINVARG; - } - bool use_credit_key = arg_get_lit(ctx, 6); bool elite = arg_get_lit(ctx, 7); bool rawkey = arg_get_lit(ctx, 8); @@ -3021,92 +3045,492 @@ static int CmdHFiClass_TearBlock(const char *Cmd) { bool verbose = arg_get_lit(ctx, 10); bool shallow_mod = arg_get_lit(ctx, 11); + int tearoff_start = arg_get_int_def(ctx, 12, 100); + int tearoff_original_start = tearoff_start; // save original start value for later use + int tearoff_increment = arg_get_int_def(ctx, 13, 10); + int tearoff_end = arg_get_int_def(ctx, 14, tearoff_start + tearoff_increment + 500); + int tearoff_loop = arg_get_int_def(ctx, 15, 1); + int tearoff_sleep = arg_get_int_def(ctx, 16, 0); + bool run_on_device = arg_get_lit(ctx, 17); + CLIParserFree(ctx); - if ((use_replay + rawkey + elite) > 1) { - PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw', 'nr'"); + // Sanity checks + if (key_len > 0 && key_nr >= 0) { + PrintAndLogEx(ERR, "Please specify key or index, not both"); return PM3_EINVARG; } - int isok = 0; - tearoff_params_t params; + + bool auth = false; + + if (key_len > 0) { + + auth = true; + if (key_len != 8) { + PrintAndLogEx(ERR, "Key is incorrect length"); + return PM3_EINVARG; + } + PrintAndLogEx(NORMAL, ""); + } + + if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + auth = true; + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex_inrow(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + return PM3_EINVARG; + } + } + + if (data_len && data_len != 8) { + PrintAndLogEx(ERR, "Data must be 8 hex bytes (16 hex symbols), got " _RED_("%u"), data_len); + return PM3_EINVARG; + } + + if (mac_len && mac_len != 4) { + PrintAndLogEx(ERR, "MAC must be 4 hex bytes (8 hex symbols)"); + return PM3_EINVARG; + } + + if (tearoff_end <= tearoff_start) { + PrintAndLogEx(ERR, "Tearoff end delay must be larger than the start delay"); + return PM3_EINVARG; + } + + if (tearoff_start <= 0) { + PrintAndLogEx(ERR, "Tearoff_start delays must be larger than 0"); + return PM3_EINVARG; + } + + if (tearoff_end <= 0) { + PrintAndLogEx(ERR, "Tearoff_end delays must be larger than 0"); + return PM3_EINVARG; + } + + if ((use_replay + rawkey + elite) > 1) { + PrintAndLogEx(ERR, "Can not use a combo of `--elite`, `--raw`, `--nr`"); + return PM3_EINVARG; + } + + int loop_count = 0; + int isok = PM3_SUCCESS; bool read_ok = false; - while (tearoff_start < tearoff_end && !read_ok) { - //perform read here, repeat if failed or 00s + uint8_t keyType = ICLASS_DEBIT_KEYTYPE; + if (use_credit_key) { + PrintAndLogEx(SUCCESS, "Using " _YELLOW_("credit") " key"); + keyType = ICLASS_CREDIT_KEYTYPE; + } - uint8_t data_read_orig[8] = {0}; - bool first_read = false; - bool reread = false; - while (!first_read) { - int res_orig = iclass_read_block_ex(key, blockno, 0x88, elite, rawkey, use_replay, verbose, auth, shallow_mod, data_read_orig, false); - if (res_orig == PM3_SUCCESS && !reread) { - if (memcmp(data_read_orig, zeros, 8) == 0) { - reread = true; - } else { - first_read = true; - reread = false; - } - } else if (res_orig == PM3_SUCCESS && reread) { - first_read = true; + if (data_len && auth == false) { + PrintAndLogEx(SUCCESS, "No key supplied. Trying no authentication read/writes"); + } + + if (tearoff_loop > 1) { + PrintAndLogEx(SUCCESS, _YELLOW_("%u") " attempts / tearoff", tearoff_loop); + } + + if (tearoff_sleep) { + PrintAndLogEx(SUCCESS, "Using " _YELLOW_("%u") " ms delay between attempts", tearoff_sleep); + } + + //check if the card is in secure mode or not + iclass_card_select_t payload_rdr = { + .flags = (FLAG_ICLASS_READER_INIT | FLAG_ICLASS_READER_CLEARTRACE) + }; + + if (shallow_mod) { + payload_rdr.flags |= FLAG_ICLASS_READER_SHALLOW_MOD; + } + clearCommandBuffer(); + PacketResponseNG resp; + SendCommandNG(CMD_HF_ICLASS_READER, (uint8_t *)&payload_rdr, sizeof(iclass_card_select_t)); + + if (WaitForResponseTimeout(CMD_HF_ICLASS_READER, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "command execution time out"); + DropField(); + return PM3_ESOFT; + } + DropField(); + + if (resp.status == PM3_ERFTRANS) { + PrintAndLogEx(FAILED, "no tag found"); + DropField(); + return PM3_ESOFT; + } + + iclass_card_select_resp_t *r = (iclass_card_select_resp_t *)resp.data.asBytes; + if (r->status == FLAG_ICLASS_NULL) { + PrintAndLogEx(FAILED, "failed to read block 0,1,2"); + return PM3_ESOFT; + } + + int fail_tolerance = 1; + if (memcmp(r->header.hdr.csn + 4, "\xFE\xFF\x12\xE0", 4) == 0) { + PrintAndLogEx(SUCCESS, "CSN................... %s ( new silicon )", sprint_hex_inrow(r->header.hdr.csn, PICOPASS_BLOCK_SIZE)); + } else { + PrintAndLogEx(SUCCESS, "CSN................... %s ( old silicon )", sprint_hex_inrow(r->header.hdr.csn, PICOPASS_BLOCK_SIZE)); + fail_tolerance = 5; + } + + picopass_hdr_t *hdr = &r->header.hdr; + uint8_t pagemap = get_pagemap(hdr); + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + PrintAndLogEx(INFO, "Card in non-secure page mode detected"); + auth = false; + } + + if (pagemap == 0x0) { + PrintAndLogEx(WARNING, _RED_("No auth possible. Read only if RA is enabled")); + goto out; + } + + + // perform initial read here, repeat if failed or 00s + bool read_auth = auth; + uint8_t data_read_orig[8] = {0}; + uint8_t ff_data[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + bool first_read = false; + bool reread = false; + bool erase_phase = false; + + if (blockno < 3) { + read_auth = false; + } + + int res_orig = iclass_read_block_ex(key, blockno, keyType, elite, rawkey, use_replay, verbose, read_auth, shallow_mod, data_read_orig, false); + while (reread) { + if (res_orig == PM3_SUCCESS && !reread) { + if (memcmp(data_read_orig, zeros, 8) == 0) { + reread = true; + } else { reread = false; } + } else if (res_orig == PM3_SUCCESS && reread) { + reread = false; + if (blockno == 2 && memcmp(data_read_orig, zeros, 8) == 0) { + reread = true; + } + } + } + + if (blockno == 2 && data_len == 0) { + int value_index = 0; //assuming FFFFFFFF is on the right + if (memcmp(data_read_orig + 4, "\xFF\xFF\xFF\xFF", 4) != 0) { //FFFFFFFF is on the left + value_index = 4; + } + memcpy(key, iClass_Key_Table[1], PICOPASS_BLOCK_SIZE); + use_credit_key = true; + auth = true; + memcpy(data, data_read_orig, PICOPASS_BLOCK_SIZE); + //decrease the debit epurse value by 1 + if (data_read_orig[value_index] != 0x00) { + data[value_index]--; + } else { + data[value_index + 2]--; + data[value_index] = 0xFF; + } + } + + PrintAndLogEx(SUCCESS, "Original block data... " _CYAN_("%s"), sprint_hex_inrow(data_read_orig, sizeof(data_read_orig))); + PrintAndLogEx(SUCCESS, "New data to write..... " _YELLOW_("%s"), sprint_hex_inrow(data, sizeof(data))); + PrintAndLogEx(SUCCESS, "Target block.......... " _YELLOW_("%u") " / " _YELLOW_("0x%02x"), blockno, blockno); + PrintAndLogEx(SUCCESS, "Using Key............. " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key)));; + // turn off Device side debug messages + uint8_t dbg_curr = DBG_NONE; + if (getDeviceDebugLevel(&dbg_curr) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + if (setDeviceDebugLevel(DBG_NONE, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + // clear trace log + SendCommandNG(CMD_BUFF_CLEAR, NULL, 0); + + if (run_on_device) { + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "---------------------------------------"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------\n"); + + iclass_tearblock_req_t payload = { + .req.use_raw = rawkey, + .req.use_elite = elite, + .req.use_credit_key = use_credit_key, + .req.use_replay = use_replay, + .req.blockno = blockno, + .req.send_reply = true, + .req.do_auth = auth, + .req.shallow_mod = shallow_mod, + .tear_start = tearoff_start, + .tear_end = tearoff_end, + .increment = tearoff_increment, + .tear_loop = tearoff_loop, + }; + memcpy(payload.req.key, key, PICOPASS_BLOCK_SIZE); + memcpy(payload.data, data, sizeof(payload.data)); + memcpy(payload.mac, mac, sizeof(payload.mac)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ICLASS_TEARBL, (uint8_t *)&payload, sizeof(payload)); + + if (WaitForResponseTimeout(CMD_HF_ICLASS_TEARBL, &resp, 1000)) { + if (resp.status == PM3_EOPABORTED) { + PrintAndLogEx(DEBUG, "Button pressed, user aborted"); + isok = resp.status; + } } - params.on = true; - params.delay_us = tearoff_start; - handle_tearoff(¶ms, false); - PrintAndLogEx(INFO, "Tear off delay: "_YELLOW_("%d")" ms", tearoff_start); - isok = iclass_write_block(blockno, data, mac, key, use_credit_key, elite, rawkey, use_replay, verbose, auth, shallow_mod); - switch (isok) { - case PM3_SUCCESS: - PrintAndLogEx(SUCCESS, "Wrote block " _YELLOW_("%d") " / " _YELLOW_("0x%02X") " ( " _GREEN_("ok") " )", blockno, blockno); - break; - case PM3_ETEAROFF: - break; - default: - PrintAndLogEx(FAILED, "Writing failed"); - break; - } - //read the data back - uint8_t data_read[8] = {0}; - first_read = false; - reread = false; - bool decrease = false; - while (!first_read) { - int res = iclass_read_block_ex(key, blockno, 0x88, elite, rawkey, use_replay, verbose, auth, shallow_mod, data_read, false); - if (res == PM3_SUCCESS && !reread) { - if (memcmp(data_read, zeros, 8) == 0) { - reread = true; - } else { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(NORMAL, ""); + clearCommandBuffer(); + return isok; + + } else { + + PrintAndLogEx(INFO, "---------------------------------------"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------\n"); + // Main loop + while ((tearoff_start <= tearoff_end) && (read_ok == false)) { + + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard."); + isok = PM3_EOPABORTED; + goto out; + } + + // set tear off trigger + clearCommandBuffer(); + tearoff_params_t params = { + .delay_us = (tearoff_start & 0xFFFF), + .on = true, + .off = false + }; + + int res = handle_tearoff(¶ms, verbose); + if (res != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Failed to configure tear off"); + isok = PM3_ESOFT; + goto out; + } + + if (tearoff_loop > 1) { + PrintAndLogEx(INPLACE, " Tear off delay "_YELLOW_("%u")" / "_YELLOW_("%d")" us - "_YELLOW_("%3u")" iter", params.delay_us, (tearoff_end & 0xFFFF), loop_count + 1); + } else { + PrintAndLogEx(INPLACE, " Tear off delay "_YELLOW_("%u")" / "_YELLOW_("%d")" us", params.delay_us, (tearoff_end & 0xFFFF)); + } + + // write block - don't check the return value. As a tear-off occurred, the write failed. + // when tear off is enabled, the return code will always be PM3_ETEAROFF + iclass_write_block(blockno, data, mac, key, use_credit_key, elite, rawkey, use_replay, false, auth, shallow_mod); + + // read the data back + uint8_t data_read[8] = {0}; + first_read = false; + reread = false; + bool decrease = false; + int readcount = 0; + while (first_read == false) { + + if (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard."); + isok = PM3_EOPABORTED; + goto out; + } + + // skip authentication for config and e-purse blocks (1,2) + if (blockno < 3) { + read_auth = false; + } + + res = iclass_read_block_ex(key, blockno, keyType, elite, rawkey, use_replay, verbose, read_auth, shallow_mod, data_read, false); + if (res == PM3_SUCCESS && !reread) { + if (memcmp(data_read, zeros, 8) == 0) { + reread = true; + } else { + first_read = true; + reread = false; + } + } else if (res == PM3_SUCCESS && reread) { first_read = true; reread = false; + } else if (res != PM3_SUCCESS) { + decrease = true; } - } else if (res == PM3_SUCCESS && reread) { - first_read = true; - reread = false; - } else if (res != PM3_SUCCESS) { - decrease = true; + + readcount++; } - } - if (decrease && tearoff_start > 0) { //if there was an error reading repeat the tearoff with the same delay - tearoff_start--; - } - bool tear_success = true; - for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) { - if (data[i] != data_read[i]) { + + if (readcount > fail_tolerance) { + PrintAndLogEx(WARNING, "\nRead block failed "_RED_("%d") " times", readcount); + } + + // if there was an error reading repeat the tearoff with the same delay + if (decrease && (tearoff_start > tearoff_increment) && (tearoff_start >= tearoff_original_start)) { + tearoff_start -= tearoff_increment; + if (verbose) { + PrintAndLogEx(INFO, " -> Read failed, retearing with "_CYAN_("%u")" us", tearoff_start); + } + } + + bool tear_success = true; + bool expected_values = true; + + if (memcmp(data_read, data, 8) != 0) { tear_success = false; } + + if ((tear_success == false) && + (memcmp(data_read, zeros, 8) != 0) && + (memcmp(data_read, data_read_orig, 8) != 0)) { + + // tearoff succeeded (partially) + + expected_values = false; + + if (memcmp(data_read, ff_data, 8) == 0 && + memcmp(data_read_orig, ff_data, 8) != 0) { + + if (erase_phase == false) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, _CYAN_("Erase phase hit... ALL ONES")); + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } + erase_phase = true; + } else { + + if (erase_phase) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, _MAGENTA_("Tearing! Write phase (post erase)")); + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } else { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, _CYAN_("Tearing! unknown phase")); + iclass_cmp_print(data_read_orig, data_read, "Original: ", "Read: "); + } + } + + bool goto_out = false; + if (blockno == 2) { + if (memcmp(data_read, ff_data, 8) == 0 && memcmp(data_read_orig, ff_data, 8) != 0) { + PrintAndLogEx(SUCCESS, "E-purse has been teared ( %s )", _GREEN_("ok")); + PrintAndLogEx(HINT, "Hint: try `hf iclass creditepurse -d FEFFFEFF --ki 1`"); + PrintAndLogEx(HINT, "Hint: try `hf iclass wrbl -d 'FFFFFFFF FFFF FEFF' --blk 2 --ki 1 --credit`"); + isok = PM3_SUCCESS; + goto_out = true; + } + } + + if (blockno == 1) { + if (data_read[0] != data_read_orig[0]) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Application limit changed, from "_YELLOW_("%u")" to "_YELLOW_("%u"), data_read_orig[0], data_read[0]); + isok = PM3_SUCCESS; + goto_out = true; + } + + if (data_read[7] != data_read_orig[7]) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "Fuse changed, from "_YELLOW_("%02x")" to "_YELLOW_("%02x"), data_read_orig[7], data_read[7]); + + const char *flag_names[8] = { + "RA", + "Fprod0", + "Fprod1", + "Crypt0 (*1)", + "Crypt1 (*0)", + "Coding0", + "Coding1", + "Fpers (*1)" + }; + PrintAndLogEx(INFO, _YELLOW_("%-10s %-10s %-10s"), "Fuse", "Original", "Changed"); + PrintAndLogEx(INFO, "---------------------------------------"); + for (int i = 7; i >= 0; --i) { + int bit1 = (data_read_orig[7] >> i) & 1; + int bit2 = (data_read[7] >> i) & 1; + PrintAndLogEx(INFO, "%-11s %-10d %-10d", flag_names[i], bit1, bit2); + } + + isok = PM3_SUCCESS; + goto_out = true; + } + + // if more OTP bits set.. + if (data_read[1] > data_read_orig[1] || + data_read[2] > data_read_orig[2]) { + PrintAndLogEx(SUCCESS, "More OTP bits got set!!!"); + + data_read[7] = 0xBC; + res = iclass_write_block(blockno, data_read, mac, key, use_credit_key, elite, rawkey, use_replay, verbose, auth, shallow_mod); + if (res != PM3_SUCCESS) { + PrintAndLogEx(INFO, "Stabilize the bits ( "_RED_("failed") " )"); + } + + isok = PM3_SUCCESS; + goto_out = true; + } + } + + if (goto_out) { + goto out; + } + } + + if (tear_success) { // tearoff succeeded with expected values + + read_ok = true; + tear_success = true; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Read: " _GREEN_("%s") " %s" + , sprint_hex_inrow(data_read, sizeof(data_read)), + (expected_values) ? _GREEN_(" -> Expected values!") : "" + ); + } + + loop_count++; + + if (loop_count == tearoff_loop) { + tearoff_start += tearoff_increment; + loop_count = 0; + } + + if (tearoff_sleep) { + msleep(tearoff_sleep); + } } - if (tear_success) { //tearoff succeeded - read_ok = true; - PrintAndLogEx(SUCCESS, _GREEN_("Tear-off Success!")); - PrintAndLogEx(INFO, "Read: %s", sprint_hex(data_read, sizeof(data_read))); - } else { //tearoff did not succeed - PrintAndLogEx(FAILED, _RED_("Tear-off Failed!")); - tearoff_start++; - } - PrintAndLogEx(INFO, "---------------"); } + +out: + + DropField(); + + if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + // disable tearoff in case of keyboard abort, or it'll trigger on next operation + clearCommandBuffer(); + tearoff_params_t params = { + .delay_us = tearoff_start, + .on = false, + .off = true + }; + handle_tearoff(¶ms, false); PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(NORMAL, ""); + clearCommandBuffer(); return isok; } @@ -3502,7 +3926,6 @@ static int CmdHFiClassView(const char *Cmd) { } if (verbose) { - PrintAndLogEx(INFO, "File: " _YELLOW_("%s"), filename); PrintAndLogEx(INFO, "File size %zu bytes, file blocks %d (0x%x)", bytes_read, (uint16_t)(bytes_read >> 3), (uint16_t)(bytes_read >> 3)); PrintAndLogEx(INFO, "Printing blocks from: " _YELLOW_("%02d") " to: " _YELLOW_("%02d"), (startblock == 0) ? 6 : startblock, endblock); } @@ -3521,13 +3944,14 @@ static int CmdHFiClassView(const char *Cmd) { void HFiClassCalcDivKey(uint8_t *CSN, uint8_t *KEY, uint8_t *div_key, bool elite) { if (elite) { uint8_t keytable[128] = {0}; - uint8_t key_index[8] = {0}; - uint8_t key_sel[8] = { 0 }; - uint8_t key_sel_p[8] = { 0 }; + uint8_t key_index[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t key_sel[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t key_sel_p[PICOPASS_BLOCK_SIZE] = {0}; hash2(KEY, keytable); hash1(CSN, key_index); - for (uint8_t i = 0; i < 8 ; i++) + for (uint8_t i = 0; i < 8 ; i++) { key_sel[i] = keytable[key_index[i]]; + } //Permute from iclass format to standard format permutekey_rev(key_sel, key_sel_p); @@ -3541,8 +3965,8 @@ void HFiClassCalcDivKey(uint8_t *CSN, uint8_t *KEY, uint8_t *div_key, bool elite //calculate and return xor_div_key (ready for a key write command) //print all div_keys if verbose static void HFiClassCalcNewKey(uint8_t *CSN, uint8_t *OLDKEY, uint8_t *NEWKEY, uint8_t *xor_div_key, bool elite, bool oldElite, bool verbose) { - uint8_t old_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t new_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t old_div_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t new_div_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //get old div key HFiClassCalcDivKey(CSN, OLDKEY, old_div_key, oldElite); //get new div key @@ -3552,9 +3976,9 @@ static void HFiClassCalcNewKey(uint8_t *CSN, uint8_t *OLDKEY, uint8_t *NEWKEY, u xor_div_key[i] = old_div_key[i] ^ new_div_key[i]; } if (verbose) { - PrintAndLogEx(SUCCESS, "Old div key......... %s", sprint_hex(old_div_key, 8)); - PrintAndLogEx(SUCCESS, "New div key......... %s", sprint_hex(new_div_key, 8)); - PrintAndLogEx(SUCCESS, "Xor div key......... " _YELLOW_("%s") "\n", sprint_hex(xor_div_key, 8)); + PrintAndLogEx(SUCCESS, "Old div key.... %s", sprint_hex_inrow(old_div_key, PICOPASS_BLOCK_SIZE)); + PrintAndLogEx(SUCCESS, "New div key.... " _MAGENTA_("%s"), sprint_hex_inrow(new_div_key, PICOPASS_BLOCK_SIZE)); + PrintAndLogEx(SUCCESS, "Xor div key.... " _YELLOW_("%s") "\n", sprint_hex_inrow(xor_div_key, PICOPASS_BLOCK_SIZE)); } } @@ -3677,6 +4101,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { uint8_t xor_div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + PrintAndLogEx(NORMAL, ""); if (givenCSN == false) { uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if (select_only(csn, CCNR, true, false) == false) { @@ -3686,7 +4111,8 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) { } HFiClassCalcNewKey(csn, old_key, new_key, xor_div_key, elite, old_elite, true); - + PrintAndLogEx(HINT, "Hint: Depending if card is in " _MAGENTA_("PERSONALIZATION") " or "_YELLOW_("APPLICATION") " mode"); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -3855,7 +4281,7 @@ static void add_key(uint8_t *key) { static int CmdHFiClassCheckKeys(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass chk", - "Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag", + "Checkkeys loads a dictionary text file with 8 byte hex keys to test authenticating against a iCLASS tag", "hf iclass chk -f iclass_default_keys.dic\n" "hf iclass chk -f iclass_elite_keys.dic --elite\n" "hf iclass chk --vb6kdf\n"); @@ -4131,10 +4557,10 @@ void picopass_elite_nextKey(uint8_t *key) { } prepared = true; } - memcpy(key, key_state, 8); + memcpy(key, key_state, PICOPASS_BLOCK_SIZE); } -static int iclass_recover(uint8_t key[8], uint32_t index_start, uint32_t loop, uint8_t no_first_auth[8], bool debug, bool test, bool allnight) { +static int iclass_recover(uint8_t key[8], uint32_t index_start, uint32_t loop, uint8_t no_first_auth[8], bool debug, bool test, bool fast, bool short_delay, bool allnight) { int runs = 1; int cycle = 1; @@ -4166,6 +4592,8 @@ static int iclass_recover(uint8_t key[8], uint32_t index_start, uint32_t loop, u payload->loop = loop; payload->debug = debug; payload->test = test; + payload->fast = fast; + payload->short_delay = short_delay; memcpy(payload->nfa, no_first_auth, PICOPASS_BLOCK_SIZE); memcpy(payload->req.key, key, PICOPASS_BLOCK_SIZE); memcpy(payload->req2.key, aa2_standard_key, PICOPASS_BLOCK_SIZE); @@ -4178,9 +4606,15 @@ static int iclass_recover(uint8_t key[8], uint32_t index_start, uint32_t loop, u WaitForResponse(CMD_HF_ICLASS_RECOVER, &resp); if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "iCLASS Key Bits Recovery: " _GREEN_("completed!")); repeat = false; + } else if (resp.status == PM3_EOPABORTED) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "iCLASS Key Bits Recovery: " _YELLOW_("aborted via keyboard!")); + repeat = false; } else if (resp.status == PM3_ESOFT) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(WARNING, "iCLASS Key Bits Recovery: " _RED_("failed/errors")); repeat = false; } else if (resp.status == PM3_EINVARG) { @@ -4219,14 +4653,131 @@ void generate_key_block_inverted(const uint8_t *startingKey, uint64_t index, uin } } -static int CmdHFiClassLegRecLookUp(const char *Cmd) { - //Standalone Command Start +// HF iClass legbrute - Thread argument structure +typedef struct { + uint8_t startingKey[8]; + uint64_t index_start; + uint8_t CCNR1[12]; + uint8_t MAC_TAG1[4]; + uint8_t CCNR2[12]; + uint8_t MAC_TAG2[4]; + int thread_id; + int thread_count; + volatile bool *found; + pthread_mutex_t *log_lock; +} thread_args_t; + +// HF iClass legbrute - Brute-force worker thread +static void *brute_thread(void *args_void) { + + thread_args_t *args = (thread_args_t *)args_void; + uint8_t div_key[8]; + uint8_t mac[4]; + uint8_t verification_mac[4]; + uint64_t index = args->index_start; + + while (!*(args->found)) { + + generate_key_block_inverted(args->startingKey, index, div_key); + doMAC(args->CCNR1, div_key, mac); + + if (memcmp(mac, args->MAC_TAG1, 4) == 0) { + doMAC(args->CCNR2, div_key, verification_mac); + if (memcmp(verification_mac, args->MAC_TAG2, 4) == 0) { + pthread_mutex_lock(args->log_lock); + if (!*(args->found)) { + *args->found = true; + PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(SUCCESS, "Found valid raw key " _GREEN_("%s"), sprint_hex_inrow(div_key, 8)); + PrintAndLogEx(HINT, "Hint: Run `"_YELLOW_("hf iclass unhash -k %s")"` to find the needed pre-images", sprint_hex_inrow(div_key, 8)); + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(NORMAL, ""); + } + pthread_mutex_unlock(args->log_lock); + break; + } + } + + if (index % 1000000 == 0 && !*(args->found)) { + + if (args->thread_id == 0) { + pthread_mutex_lock(args->log_lock); + PrintAndLogEx(INPLACE, "Tested "_YELLOW_("%" PRIu64)" million keys, curr index: "_YELLOW_("%" PRIu64)", Thread[0]: %s" + , ((index / 1000000) * args->thread_count) + , (index / 1000000) + , sprint_hex_inrow(div_key, 8) + ); + pthread_mutex_unlock(args->log_lock); + } + + } + index++; + } + return NULL; +} + +// HF iClass legbrute - Multithreaded brute-force function +static int CmdHFiClassLegBrute_MT(uint8_t epurse[8], uint8_t macs[8], uint8_t macs2[8], uint8_t startingKey[8], uint64_t index, int threads) { + + int thread_count = threads; + if (thread_count < 1) { + thread_count = 1; + } + if (thread_count > 16) { + thread_count = 16; + } + PrintAndLogEx(INFO, "Bruteforcing using " _YELLOW_("%u") " threads", thread_count); + PrintAndLogEx(NORMAL, ""); + + uint8_t CCNR[12], CCNR2[12], MAC_TAG[4], MAC_TAG2[4]; + + memcpy(CCNR, epurse, 8); + memcpy(CCNR2, epurse, 8); + memcpy(CCNR + 8, macs, 4); + memcpy(CCNR2 + 8, macs2, 4); + memcpy(MAC_TAG, macs + 4, 4); + memcpy(MAC_TAG2, macs2 + 4, 4); + + pthread_t tids[thread_count]; + thread_args_t args[thread_count]; + volatile bool found = false; + pthread_mutex_t log_lock; + pthread_mutex_init(&log_lock, NULL); + + int nibble_range = 16 / thread_count; + for (int i = 0; i < thread_count; i++) { + memcpy(args[i].startingKey, startingKey, 8); + args[i].startingKey[0] = (startingKey[0] & 0x0F) | ((i * nibble_range) << 4); + args[i].index_start = index; + memcpy(args[i].CCNR1, CCNR, 12); + memcpy(args[i].MAC_TAG1, MAC_TAG, 4); + memcpy(args[i].CCNR2, CCNR2, 12); + memcpy(args[i].MAC_TAG2, MAC_TAG2, 4); + args[i].thread_id = i; + args[i].thread_count = thread_count; + args[i].found = &found; + args[i].log_lock = &log_lock; + + pthread_create(&tids[i], NULL, brute_thread, &args[i]); + } + + for (int i = 0; i < thread_count; i++) { + pthread_join(tids[i], NULL); + } + pthread_mutex_destroy(&log_lock); + + return found ? PM3_SUCCESS : ERR; +} + +// CmdHFiClassLegBrute function with CLI and multithreading support +static int CmdHFiClassLegBrute(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass legbrute", - "This command take sniffed trace data and partial raw key and bruteforces the remaining 40 bits of the raw key.", - "hf iclass legbrute --epurse feffffffffffffff --macs1 1306cad9b6c24466 --macs2 f0bf905e35f97923 --pk B4F12AADC5301225" - ); + "This command takes sniffed trace data and a partial raw key and bruteforces the remaining 40 bits of the raw key.\n" + "Complete 40 bit keyspace is 1'099'511'627'776 and command is locked down to max 16 threads currently.\n" + "A possible worst case scenario on 16 threads estimates XXX days YYY hours MMM minutes.", + "hf iclass legbrute --epurse feffffffffffffff --macs1 1306cad9b6c24466 --macs2 f0bf905e35f97923 --pk B4F12AADC5301225"); void *argtable[] = { arg_param_begin, @@ -4235,6 +4786,7 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { arg_str1(NULL, "macs2", "", "MACs captured from the reader, different than the first set (with the same csn and epurse value)"), arg_str1(NULL, "pk", "", "Partial Key from legrec or starting key of keyblock from legbrute"), arg_int0(NULL, "index", "", "Where to start from to retrieve the key, default 0 - value in millions e.g. 1 is 1 million"), + arg_int0(NULL, "threads", "", "Number of threads to use, by default it uses the cpu's max threads (max 16)."), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -4255,9 +4807,9 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { uint8_t startingKey[PICOPASS_BLOCK_SIZE] = {0}; CLIGetHexWithReturn(ctx, 4, startingKey, &startingkey_len); - uint64_t index = arg_get_int_def(ctx, 6, 0); //has to be 64 as we're bruteforcing 40 bits - index = index * 1000000; - + uint64_t index = arg_get_int_def(ctx, 5, 0); + index *= 1000000; + int threads = arg_get_int_def(ctx, 6, num_CPUs()); CLIParserFree(ctx); if (epurse_len && epurse_len != PICOPASS_BLOCK_SIZE) { @@ -4279,81 +4831,8 @@ static int CmdHFiClassLegRecLookUp(const char *Cmd) { PrintAndLogEx(ERR, "Partial Key is incorrect length"); return PM3_EINVARG; } - //Standalone Command End - uint8_t CCNR[12]; - uint8_t MAC_TAG[4] = {0, 0, 0, 0}; - uint8_t CCNR2[12]; - uint8_t MAC_TAG2[4] = {0, 0, 0, 0}; - - // Copy CCNR and MAC_TAG - memcpy(CCNR, epurse, 8); - memcpy(CCNR2, epurse, 8); - memcpy(CCNR + 8, macs, 4); - memcpy(CCNR2 + 8, macs2, 4); - memcpy(MAC_TAG, macs + 4, 4); - memcpy(MAC_TAG2, macs2 + 4, 4); - - PrintAndLogEx(SUCCESS, " Epurse: %s", sprint_hex(epurse, 8)); - PrintAndLogEx(SUCCESS, " MACS1: %s", sprint_hex(macs, 8)); - PrintAndLogEx(SUCCESS, " MACS2: %s", sprint_hex(macs2, 8)); - PrintAndLogEx(SUCCESS, " CCNR1: " _GREEN_("%s"), sprint_hex(CCNR, sizeof(CCNR))); - PrintAndLogEx(SUCCESS, " CCNR2: " _GREEN_("%s"), sprint_hex(CCNR2, sizeof(CCNR2))); - PrintAndLogEx(SUCCESS, "TAG MAC1: %s", sprint_hex(MAC_TAG, sizeof(MAC_TAG))); - PrintAndLogEx(SUCCESS, "TAG MAC2: %s", sprint_hex(MAC_TAG2, sizeof(MAC_TAG2))); - PrintAndLogEx(SUCCESS, "Starting Key: %s", sprint_hex(startingKey, 8)); - - bool verified = false; - uint8_t div_key[PICOPASS_BLOCK_SIZE] = {0}; - uint8_t generated_mac[4] = {0, 0, 0, 0}; - - while (!verified) { - - //generate the key block - generate_key_block_inverted(startingKey, index, div_key); - - //generate the relevant macs - - doMAC(CCNR, div_key, generated_mac); - bool mac_match = true; - for (int i = 0; i < 4; i++) { - if (MAC_TAG[i] != generated_mac[i]) { - mac_match = false; - } - } - - if (mac_match) { - //verify this against macs2 - PrintAndLogEx(WARNING, _YELLOW_("Found potentially valid RAW key ") _GREEN_("%s")_YELLOW_(" verifying it..."), sprint_hex(div_key, 8)); - //generate the macs from the key and not the other way around, so we can quickly validate it - uint8_t verification_mac[4] = {0, 0, 0, 0}; - doMAC(CCNR2, div_key, verification_mac); - PrintAndLogEx(INFO, "Usr Provided Mac2: " _GREEN_("%s"), sprint_hex(MAC_TAG2, sizeof(MAC_TAG2))); - PrintAndLogEx(INFO, "Verification Mac: " _GREEN_("%s"), sprint_hex(verification_mac, sizeof(verification_mac))); - bool check_values = true; - for (int i = 0; i < 4; i++) { - if (MAC_TAG2[i] != verification_mac[i]) { - check_values = false; - } - } - if (check_values) { - PrintAndLogEx(SUCCESS, _GREEN_("CONFIRMED VALID RAW key ") _RED_("%s"), sprint_hex(div_key, 8)); - PrintAndLogEx(INFO, "You can now run -> "_YELLOW_("hf iclass unhash -k %s")" <-to find the pre-images.", sprint_hex(div_key, 8)); - verified = true; - } else { - PrintAndLogEx(INFO, _YELLOW_("Raw Key Invalid")); - } - - } - if (index % 1000000 == 0) { - PrintAndLogEx(INFO, "Tested: " _YELLOW_("%" PRIu64)" million keys", index / 1000000); - PrintAndLogEx(INFO, "Last Generated Key Value: " _YELLOW_("%s"), sprint_hex(div_key, 8)); - } - index++; - } - - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; + return CmdHFiClassLegBrute_MT(epurse, macs, macs2, startingKey, index, threads); } static void generate_single_key_block_inverted_opt(const uint8_t *startingKey, uint32_t index, uint8_t *keyBlock) { @@ -4398,59 +4877,68 @@ static void generate_single_key_block_inverted_opt(const uint8_t *startingKey, u static int CmdHFiClassLegacyRecSim(void) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, _YELLOW_("This simulation assumes the card is standard keyed.")); - - uint8_t key[PICOPASS_BLOCK_SIZE] = {0}; - uint8_t original_key[PICOPASS_BLOCK_SIZE]; + PrintAndLogEx(INFO, ""); uint8_t csn[8] = {0}; - uint8_t new_div_key[8] = {0}; uint8_t CCNR[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; if (select_only(csn, CCNR, true, false) == false) { DropField(); return PM3_ESOFT; } + + uint8_t new_div_key[8] = {0}; HFiClassCalcDivKey(csn, iClass_Key_Table[0], new_div_key, false); + + uint8_t key[PICOPASS_BLOCK_SIZE] = {0}; + uint8_t original_key[PICOPASS_BLOCK_SIZE] = {0}; + memcpy(key, new_div_key, PICOPASS_BLOCK_SIZE); memcpy(original_key, key, PICOPASS_BLOCK_SIZE); uint8_t zero_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t zero_key_two[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; int bits_found = -1; uint32_t index = 0; + #define MAX_UPDATES 16777216 + while (bits_found == -1 && index < MAX_UPDATES) { - uint8_t genkeyblock[PICOPASS_BLOCK_SIZE]; - uint8_t xorkeyblock[PICOPASS_BLOCK_SIZE] = {0}; + + uint8_t genkeyblock[PICOPASS_BLOCK_SIZE] = {0}; generate_single_key_block_inverted_opt(zero_key, index, genkeyblock); - memcpy(xorkeyblock, genkeyblock, PICOPASS_BLOCK_SIZE); for (int i = 0; i < 8 ; i++) { - key[i] = xorkeyblock[i] ^ original_key[i]; - memcpy(zero_key_two, xorkeyblock, PICOPASS_BLOCK_SIZE); + key[i] = genkeyblock[i] ^ original_key[i]; } // Extract the last 3 bits of the first byte uint8_t last_three_bits = key[0] & 0x07; // 0x07 is 00000111 in binary - bitmask + bool same_bits = true; // Check if the last 3 bits of all bytes are the same for (int i = 1; i < PICOPASS_BLOCK_SIZE; i++) { if ((key[i] & 0x07) != last_three_bits) { same_bits = false; + break; } } + if (same_bits) { - bits_found = index; - PrintAndLogEx(SUCCESS, "Original Key: " _GREEN_("%s"), sprint_hex(original_key, sizeof(original_key))); - PrintAndLogEx(SUCCESS, "Weak Key: " _GREEN_("%s"), sprint_hex(key, sizeof(key))); - PrintAndLogEx(SUCCESS, "Key Updates Required to Weak Key: " _GREEN_("%d"), index); - PrintAndLogEx(SUCCESS, "Estimated Time: ~" _GREEN_("%d")" hours", index / 6545); + PrintAndLogEx(SUCCESS, "Original key... " _GREEN_("%s"), sprint_hex_inrow(original_key, sizeof(original_key))); + PrintAndLogEx(SUCCESS, "Weak key....... " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(SUCCESS, "Key updates required to weak key..... " _GREEN_("%d"), index); + PrintAndLogEx(SUCCESS, "Estimated time ( default mode )...... " _GREEN_("~%d")" hours", index / 17800); + PrintAndLogEx(SUCCESS, "Estimated time ( default + --sl ).... " _GREEN_("~%d")" hours", index / 19450); + PrintAndLogEx(SUCCESS, "Estimated time ( --fast mode )....... " _GREEN_("~%d")" hours", index / 26860); + PrintAndLogEx(SUCCESS, "Estimated time ( --fast + --sl )..... " _GREEN_("~%d")" hours", index / 29750); + break; } index++; - }//end while + } // end while PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -4461,7 +4949,9 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass legrec", - "Attempts to recover the diversified key of a specific iClass card. This may take a long time. The Card must remain be on the PM3 antenna during the whole process! This process may brick the card!", + "Attempts to recover the diversified key of a specific iCLASS card. This may take several days.\n" + "The card must remain be on the PM3 antenna during the whole process.\n" + _RED_(" ! Warning ! ") _WHITE_(" This process may brick the card! ") _RED_(" ! Warning ! "), "hf iclass legrec --macs 0000000089cb984b\n" "hf iclass legrec --macs 0000000089cb984b --index 0 --loop 100 --notest" ); @@ -4469,12 +4959,14 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str1(NULL, "macs", "", "AA1 Authentication MACs"), - arg_int0(NULL, "index", "", "Where to start from to retrieve the key, default 0"), - arg_int0(NULL, "loop", "", "The number of key retrieval cycles to perform, max 10000, default 100"), - arg_lit0(NULL, "debug", "Re-enables tracing for debugging. Limits cycles to 1."), - arg_lit0(NULL, "notest", "Perform real writes on the card!"), - arg_lit0(NULL, "allnight", "Loops the loop for 10 times, recommended loop value of 5000."), - arg_lit0(NULL, "est", "Estimates the key updates based on the card's CSN assuming standard key."), + arg_int0(NULL, "index", "", "Where to start from to retrieve the key (def: 0)"), + arg_int0(NULL, "loop", "", "The number of key retrieval cycles to perform, max 10000 (def 100)"), + arg_lit0(NULL, "debug", "Re-enables tracing for debugging. Limits cycles to 1"), + arg_lit0(NULL, "notest", "Perform real writes on the card"), + arg_lit0(NULL, "allnight", "Loops the loop for 10 times, recommended loop value of 5000"), + arg_lit0(NULL, "fast", "Increases the speed (4.6->7.4 key updates/second), higher risk to brick the card"), + arg_lit0(NULL, "sl", "Lower card comms delay times, further speeds increases, may cause more errors"), + arg_lit0(NULL, "est", "Estimates the key updates based on the card's CSN assuming standard key"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -4489,7 +4981,9 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) { bool test = true; bool no_test = arg_get_lit(ctx, 5); bool allnight = arg_get_lit(ctx, 6); - bool sim = arg_get_lit(ctx, 7); + bool fast = arg_get_lit(ctx, 7); + bool short_delay = arg_get_lit(ctx, 8); + bool sim = arg_get_lit(ctx, 9); if (sim) { CmdHFiClassLegacyRecSim(); @@ -4506,6 +5000,7 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) { return PM3_EINVARG; } else if (debug || test) { loop = 1; + fast = false; } uint8_t csn[PICOPASS_BLOCK_SIZE] = {0}; @@ -4525,10 +5020,14 @@ static int CmdHFiClassLegacyRecover(const char *Cmd) { return PM3_EINVARG; } - iclass_recover(macs, index, loop, no_first_auth, debug, test, allnight); - - PrintAndLogEx(WARNING, _YELLOW_("If the process completed successfully, you can now run 'hf iclass legbrute' with the partial key found.")); - + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "---------------------------------------"); + PrintAndLogEx(INFO, "Press " _GREEN_("pm3 button") " to abort"); + PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------\n"); + iclass_recover(macs, index, loop, no_first_auth, debug, test, fast, short_delay, allnight); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, _YELLOW_("If the process completed successfully")); + PrintAndLogEx(HINT, "Hint: run `" _YELLOW_("hf iclass legbrute -h") "` with the partial key found"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -4561,13 +5060,32 @@ static int CmdHFiClassUnhash(const char *Cmd) { return PM3_EINVARG; } + //check if divkey respects hash0 rules (legacy format) or if it could be AES Based + + int count_lsb0 = 0; + int count_lsb1 = 0; + + for (int i = 0; i < PICOPASS_BLOCK_SIZE; i++) { + if ((div_key[i] & 0x01) == 0) { + count_lsb0++; + } else { + count_lsb1++; + } + } + + if (count_lsb0 != 4 || count_lsb1 != 4) { + PrintAndLogEx(INFO, _RED_("Incorrect LSB Distribution, unable to unhash - the key might be AES based.")); + return PM3_SUCCESS; + } + PrintAndLogEx(INFO, "Diversified key... %s", sprint_hex_inrow(div_key, sizeof(div_key))); - + PrintAndLogEx(INFO, "-----------------------------------"); invert_hash0(div_key); - - PrintAndLogEx(SUCCESS, "You can now retrieve the master key by cracking DES with hashcat!"); - PrintAndLogEx(SUCCESS, "hashcat.exe -a 3 -m 14000 preimage:csn -1 charsets/DES_full.hcchr --hex-charset ?1?1?1?1?1?1?1?1"); - + PrintAndLogEx(INFO, "-----------------------------------"); + PrintAndLogEx(INFO, "You can now retrieve the master key by cracking DES with hashcat."); + PrintAndLogEx(INFO, "Create a text file with : on each line and use it with hashcat."); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(HINT, "Hint: `" _YELLOW_("hashcat.exe -a 3 -m 14000 preimage:csn -1 charsets/DES_full.hcchr --hex-charset ?1?1?1?1?1?1?1?1") "`"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -4594,56 +5112,49 @@ static int CmdHFiClassLookUp(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - bool use_vb6kdf = arg_get_lit(ctx, 7); int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - bool use_elite = arg_get_lit(ctx, 5); - bool use_raw = arg_get_lit(ctx, 6); - if (use_vb6kdf) { - use_elite = true; - } else { - CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - } - int csn_len = 0; uint8_t csn[8] = {0}; CLIGetHexWithReturn(ctx, 2, csn, &csn_len); - if (csn_len > 0) { - if (csn_len != 8) { - PrintAndLogEx(ERR, "CSN is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } - int epurse_len = 0; uint8_t epurse[8] = {0}; CLIGetHexWithReturn(ctx, 3, epurse, &epurse_len); - if (epurse_len > 0) { - if (epurse_len != 8) { - PrintAndLogEx(ERR, "ePurse is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } - int macs_len = 0; uint8_t macs[8] = {0}; CLIGetHexWithReturn(ctx, 4, macs, &macs_len); - if (macs_len > 0) { - if (macs_len != 8) { - PrintAndLogEx(ERR, "MAC is incorrect length"); - CLIParserFree(ctx); - return PM3_EINVARG; - } + bool use_elite = arg_get_lit(ctx, 5); + bool use_raw = arg_get_lit(ctx, 6); + bool use_vb6kdf = arg_get_lit(ctx, 7); + + if (use_vb6kdf == false) { + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); } CLIParserFree(ctx); + // santity checks + + if (csn_len > 0 && csn_len != 8) { + PrintAndLogEx(ERR, "CSN is incorrect length"); + return PM3_EINVARG; + } + + if (epurse_len > 0 && epurse_len != 8) { + PrintAndLogEx(ERR, "ePurse is incorrect length"); + return PM3_EINVARG; + } + + if (macs_len > 0 && macs_len != 8) { + PrintAndLogEx(ERR, "MAC is incorrect length"); + return PM3_EINVARG; + } + + uint8_t CCNR[12]; uint8_t MAC_TAG[4] = { 0, 0, 0, 0 }; @@ -4664,7 +5175,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { uint8_t *keyBlock = NULL; uint32_t keycount = 0; - if (!use_vb6kdf) { + if (use_vb6kdf == false) { // Load keys int res = loadFileDICTIONARY_safe(filename, (void **)&keyBlock, 8, &keycount); if (res != PM3_SUCCESS || keycount == 0) { @@ -4688,7 +5199,8 @@ static int CmdHFiClassLookUp(const char *Cmd) { // Iclass_prekey_t iclass_prekey_t *prekey = calloc(keycount, sizeof(iclass_prekey_t)); - if (!prekey) { + if (prekey == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(keyBlock); return PM3_EMALLOC; } @@ -4709,7 +5221,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { // Sort mac list qsort(prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32); - PrintAndLogEx(SUCCESS, "Searching for " _YELLOW_("%s") " key...", "DEBIT"); + PrintAndLogEx(SUCCESS, "Searching for %s key...", _YELLOW_("DEBIT")); iclass_prekey_t *item; iclass_prekey_t lookup; memcpy(lookup.mac, MAC_TAG, 4); @@ -4718,7 +5230,7 @@ static int CmdHFiClassLookUp(const char *Cmd) { item = (iclass_prekey_t *) bsearch(&lookup, prekey, keycount, sizeof(iclass_prekey_t), cmp_uint32); if (item != NULL) { - PrintAndLogEx(SUCCESS, "Found valid key " _GREEN_("%s"), sprint_hex(item->key, 8)); + PrintAndLogEx(SUCCESS, "Found valid key " _GREEN_("%s"), sprint_hex_inrow(item->key, 8)); add_key(item->key); } @@ -4759,23 +5271,24 @@ static void *bf_generate_mac(void *thread_arg) { uint8_t *keys = targ->keys; iclass_premac_t *list = targ->list.premac; - uint8_t csn[8]; + uint8_t csn[PICOPASS_BLOCK_SIZE]; uint8_t cc_nr[12]; memcpy(csn, targ->csn, sizeof(csn)); memcpy(cc_nr, targ->cc_nr, sizeof(cc_nr)); - uint8_t key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t div_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; for (uint32_t i = idx; i < keycnt; i += iclass_tc) { - memcpy(key, keys + 8 * i, 8); + memcpy(key, keys + 8 * i, PICOPASS_BLOCK_SIZE); pthread_mutex_lock(&generator_mutex); - if (use_raw) - memcpy(div_key, key, 8); - else + if (use_raw) { + memcpy(div_key, key, PICOPASS_BLOCK_SIZE); + } else { HFiClassCalcDivKey(csn, key, div_key, use_elite); + } doMAC(cc_nr, div_key, list[i].mac); pthread_mutex_unlock(&generator_mutex); @@ -4829,22 +5342,23 @@ static void *bf_generate_mackey(void *thread_arg) { uint8_t *keys = targ->keys; iclass_prekey_t *list = targ->list.prekey; - uint8_t csn[8]; + uint8_t csn[PICOPASS_BLOCK_SIZE]; uint8_t cc_nr[12]; memcpy(csn, targ->csn, sizeof(csn)); memcpy(cc_nr, targ->cc_nr, sizeof(cc_nr)); - uint8_t div_key[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + uint8_t div_key[PICOPASS_BLOCK_SIZE] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; for (uint32_t i = idx; i < keycnt; i += iclass_tc) { - memcpy(list[i].key, keys + 8 * i, 8); + memcpy(list[i].key, keys + 8 * i, PICOPASS_BLOCK_SIZE); pthread_mutex_lock(&generator_mutex); - if (use_raw) - memcpy(div_key, list[i].key, 8); - else + if (use_raw) { + memcpy(div_key, list[i].key, PICOPASS_BLOCK_SIZE); + } else { HFiClassCalcDivKey(csn, list[i].key, div_key, use_elite); + } doMAC(cc_nr, div_key, list[i].mac); pthread_mutex_unlock(&generator_mutex); @@ -4880,17 +5394,19 @@ void GenerateMacKeyFrom(uint8_t *CSN, uint8_t *CCNR, bool use_raw, bool use_elit } } - for (int i = 0; i < iclass_tc; i++) + for (int i = 0; i < iclass_tc; i++) { pthread_join(threads[i], NULL); - + } } // print diversified keys void PrintPreCalcMac(uint8_t *keys, uint32_t keycnt, iclass_premac_t *pre_list) { iclass_prekey_t *b = calloc(keycnt, sizeof(iclass_prekey_t)); - if (!b) + if (b == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return; + } for (uint32_t i = 0; i < keycnt; i++) { memcpy(b[i].key, keys + 8 * i, 8); @@ -4963,31 +5479,27 @@ static void shave(uint8_t *data, uint8_t len) { } static void generate_rev(uint8_t *data, uint8_t len) { uint8_t *key = calloc(len, sizeof(uint8_t)); - PrintAndLogEx(SUCCESS, "input permuted key | %s \n", sprint_hex(data, len)); + PrintAndLogEx(SUCCESS, "permuted key..... %s", sprint_hex_inrow(data, len)); permute_rev(data, len, key); - PrintAndLogEx(SUCCESS, " unpermuted key | %s \n", sprint_hex(key, len)); + PrintAndLogEx(SUCCESS, "unpermuted key... %s", sprint_hex_inrow(key, len)); shave(key, len); - PrintAndLogEx(SUCCESS, " key | %s \n", sprint_hex(key, len)); + PrintAndLogEx(SUCCESS, "key.............. %s", sprint_hex_inrow(key, len)); free(key); } static void generate(uint8_t *data, uint8_t len) { uint8_t *key = calloc(len, sizeof(uint8_t)); uint8_t *pkey = calloc(len, sizeof(uint8_t)); - PrintAndLogEx(SUCCESS, " input key | %s \n", sprint_hex(data, len)); + PrintAndLogEx(SUCCESS, "input key...... %s", sprint_hex_inrow(data, len)); permute(data, len, pkey); - PrintAndLogEx(SUCCESS, "permuted key | %s \n", sprint_hex(pkey, len)); + PrintAndLogEx(SUCCESS, "permuted key... %s", sprint_hex_inrow(pkey, len)); simple_crc(pkey, len, key); - PrintAndLogEx(SUCCESS, " CRC'ed key | %s \n", sprint_hex(key, len)); + PrintAndLogEx(SUCCESS, "CRC'ed key..... %s", sprint_hex_inrow(key, len)); free(key); free(pkey); } static int CmdHFiClassPermuteKey(const char *Cmd) { - uint8_t key[8] = {0}; - uint8_t data[16] = {0}; - int len = 0; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass permutekey", "Permute function from 'heart of darkness' paper.", @@ -5001,22 +5513,29 @@ static int CmdHFiClassPermuteKey(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); + bool isReverse = arg_get_lit(ctx, 1); - CLIGetHexWithReturn(ctx, 2, data, &len); + + int dlen = 0; + uint8_t data[16] = {0}; + CLIGetHexWithReturn(ctx, 2, data, &dlen); CLIParserFree(ctx); - memcpy(key, data, 8); + uint8_t key[PICOPASS_BLOCK_SIZE] = {0}; + memcpy(key, data, PICOPASS_BLOCK_SIZE); if (isReverse) { - generate_rev(data, len); - uint8_t key_std_format[8] = {0}; + generate_rev(data, dlen); + uint8_t key_std_format[PICOPASS_BLOCK_SIZE] = {0}; permutekey_rev(key, key_std_format); - PrintAndLogEx(SUCCESS, "Standard NIST format key " _YELLOW_("%s") " \n", sprint_hex(key_std_format, 8)); + PrintAndLogEx(SUCCESS, "Standard NIST format key..... " _YELLOW_("%s"), sprint_hex_inrow(key_std_format, PICOPASS_BLOCK_SIZE)); + PrintAndLogEx(NORMAL, ""); } else { - generate(data, len); - uint8_t key_iclass_format[8] = {0}; + generate(data, dlen); + uint8_t key_iclass_format[PICOPASS_BLOCK_SIZE] = {0}; permutekey(key, key_iclass_format); - PrintAndLogEx(SUCCESS, "HID permuted iCLASS format: %s \n", sprint_hex(key_iclass_format, 8)); + PrintAndLogEx(SUCCESS, "HID permuted iCLASS format... " _YELLOW_("%s"), sprint_hex_inrow(key_iclass_format, PICOPASS_BLOCK_SIZE)); + PrintAndLogEx(NORMAL, ""); } return PM3_SUCCESS; } @@ -5026,12 +5545,12 @@ static int CmdHFiClassEncode(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass encode", "Encode binary wiegand to block 7,8,9\n" - "Use either --bin or --wiegand/--fc/--cn", + "Use either --bin or --wiegand/--fc/--cn\n" + "When using emulator you have to first load a credential into emulator memory", "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337 (H10301)\n" "hf iclass encode -w H10301 --fc 31 --cn 337 --ki 0 -> FC 31 CN 337 (H10301)\n" "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337 (H10301), writing w elite key\n" - "hf iclass encode -w H10301 --fc 31 --cn 337 --emu -> Writes the ecoded data to emulator memory\n" - "When using emulator you have to first load a credential into emulator memory" + "hf iclass encode -w H10301 --fc 31 --cn 337 --emu -> Writes the ecoded data to emulator memory" ); void *argtable[] = { @@ -5387,43 +5906,74 @@ static int CmdHFiClassConfigCard(const char *Cmd) { return PM3_SUCCESS; } +static bool match_with_wildcard(const uint8_t *data, const uint8_t *pattern, const bool *mask, size_t length) { + for (size_t i = 0; i < length; ++i) { + if (mask[i] && data[i] != pattern[i]) { + return false; + } + } + return true; +} + static int CmdHFiClassSAM(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf iclass sam", "Extract PACS via a HID SAM\n", "hf iclass sam\n" - "hf iclass sam -p -d a005a103800104 -> get PACS data, but ensure that epurse will stay unchanged\n" - "hf iclass sam --break-on-nr-mac -> get Nr-MAC for extracting encrypted SIO\n" + "hf iclass sam -p -d a005a103800104 -> get PACS data, prevent epurse update\n" + "hf iclass sam --break -> get Nr-MAC for extracting encrypted SIO\n" ); void *argtable[] = { arg_param_begin, - arg_lit0("v", "verbose", "verbose output"), - arg_lit0("k", "keep", "keep the field active after command executed"), - arg_lit0("n", "nodetect", "skip selecting the card and sending card details to SAM"), + arg_lit0("v", "verbose", "verbose output"), + arg_lit0("k", "keep", "keep the field active after command executed"), + arg_lit0("n", "nodetect", "skip selecting the card and sending card details to SAM"), arg_lit0("t", "tlv", "decode TLV"), - arg_lit0(NULL, "break-on-nr-mac", "stop tag interaction on nr-mac"), - arg_lit0("p", "prevent-epurse-update", "fake epurse update"), - arg_lit0(NULL, "shallow", "shallow mod"), - arg_strx0("d", "data", "", "DER encoded command to send to SAM"), + arg_lit0(NULL, "break", "stop tag interaction on nr-mac"), + arg_lit0("p", "prevent", "fake epurse update"), + arg_lit0(NULL, "shallow", "shallow mod"), + arg_strx0("d", "data", "", "DER encoded command to send to SAM"), + arg_lit0("s", "snmp", "data is in snmp format without headers"), + arg_lit0(NULL, "info", "get SAM infos (version, serial number)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool verbose = arg_get_lit(ctx, 1); - bool disconnectAfter = !arg_get_lit(ctx, 2); - bool skipDetect = arg_get_lit(ctx, 3); + bool disconnect_after = !arg_get_lit(ctx, 2); + bool skip_detect = arg_get_lit(ctx, 3); bool decodeTLV = arg_get_lit(ctx, 4); - bool breakOnNrMac = arg_get_lit(ctx, 5); - bool preventEpurseUpdate = arg_get_lit(ctx, 6); + bool break_nrmac = arg_get_lit(ctx, 5); + bool prevent = arg_get_lit(ctx, 6); bool shallow_mod = arg_get_lit(ctx, 7); + bool snmp_data = arg_get_lit(ctx, 9); + bool info = arg_get_lit(ctx, 10); uint8_t flags = 0; - if (disconnectAfter) flags |= BITMASK(0); - if (skipDetect) flags |= BITMASK(1); - if (breakOnNrMac) flags |= BITMASK(2); - if (preventEpurseUpdate) flags |= BITMASK(3); - if (shallow_mod) flags |= BITMASK(4); + if (disconnect_after) { + flags |= BITMASK(0); + } + + if (skip_detect) { + flags |= BITMASK(1); + } + + if (break_nrmac) { + flags |= BITMASK(2); + } + + if (prevent) { + flags |= BITMASK(3); + } + + if (shallow_mod) { + flags |= BITMASK(4); + } + + if (info) { + flags |= BITMASK(5); + } uint8_t data[PM3_CMD_DATA_SIZE] = {0}; data[0] = flags; @@ -5440,13 +5990,24 @@ static int CmdHFiClassSAM(const char *Cmd) { return PM3_ESOFT; } + if (snmp_data) { + uint8_t header[4] = {0xa0, cmdlen + 2, 0x94, cmdlen }; + memmove(data + 4, data, cmdlen + 1); + data[0] = flags; + memcpy(data + 1, header, 4); + cmdlen += 4; + } + clearCommandBuffer(); SendCommandNG(CMD_HF_SAM_PICOPASS, data, cmdlen + 1); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_SAM_PICOPASS, &resp, 4000) == false) { - PrintAndLogEx(WARNING, "SAM timeout"); - return PM3_ETIMEOUT; - } + WaitForResponse(CMD_HF_SAM_PICOPASS, &resp); + + bool is_snmp = false; + uint8_t snmp_pattern[] = {0xBD, 0x81, 0xFF, 0x8A, 0x81, 0xFF}; // SNMP Response header pattern, 0xFF is a wildcard value for message length + bool snmp_mask[] = {true, true, false, true, true, false}; // false means wildcard value in that position + uint8_t ok_pattern[] = {0xBD, 0xFF, 0x8A}; // Ok response header pattern, 0xFF is a wildcard value for message length + bool ok_mask[] = {true, false, true}; // false means wildcard value in that position switch (resp.status) { case PM3_SUCCESS: @@ -5494,22 +6055,36 @@ static int CmdHFiClassSAM(const char *Cmd) { const uint8_t *oid = pacs + 2 + pacs_length; const uint8_t oid_length = oid[1]; const uint8_t *oid_data = oid + 2; - PrintAndLogEx(SUCCESS, "SIO OID.......: " _GREEN_("%s"), sprint_hex_inrow(oid_data, oid_length)); + PrintAndLogEx(SUCCESS, "SIO OID.......... " _GREEN_("%s"), sprint_hex_inrow(oid_data, oid_length)); const uint8_t *mediaType = oid + 2 + oid_length; const uint8_t mediaType_data = mediaType[2]; - PrintAndLogEx(SUCCESS, "SIO Media Type: " _GREEN_("%s"), getSioMediaTypeInfo(mediaType_data)); - } else if (breakOnNrMac && d[0] == 0x05) { - PrintAndLogEx(SUCCESS, "Nr-MAC: " _GREEN_("%s"), sprint_hex_inrow(d + 1, 8)); + PrintAndLogEx(SUCCESS, "SIO Media Type... " _GREEN_("%s"), getSioMediaTypeInfo(mediaType_data)); + } else if (break_nrmac && d[0] == 0x05) { + PrintAndLogEx(SUCCESS, "Nr-MAC........... " _GREEN_("%s"), sprint_hex_inrow(d + 1, 8)); if (verbose) { PrintAndLogEx(INFO, "Replay Nr-MAC to dump SIO:"); - PrintAndLogEx(SUCCESS, " hf iclass dump -k \"%s\" --nr", sprint_hex_inrow(d + 1, 8)); + PrintAndLogEx(SUCCESS, " hf iclass dump --nr -k %s", sprint_hex_inrow(d + 1, 8)); } } else { - print_hex(d, resp.length); + //if it is an error decode it + if (memcmp(d, "\xBE\x07\x80\x01", 4) == 0) { //if it the string is 0xbe 0x07 0x80 0x01 the next byte will indicate the error code + PrintAndLogEx(ERR, _RED_("Sam Error Code: %02x"), d[4]); + print_hex(d, resp.length); + } else if (match_with_wildcard(d, snmp_pattern, snmp_mask, 6)) { + is_snmp = true; + PrintAndLogEx(SUCCESS, _YELLOW_("[samSNMPMessageResponse] ")"%s", sprint_hex(d + 6, resp.length - 6)); + } else if (match_with_wildcard(d, ok_pattern, ok_mask, 3)) { + PrintAndLogEx(SUCCESS, _YELLOW_("[samResponseAcknowledge] ")"%s", sprint_hex(d + 4, resp.length - 4)); + } else { + print_hex(d, resp.length); + } } - if (decodeTLV) { + + if (decodeTLV && is_snmp == false) { asn1_print(d, d[1] + 2, " "); + } else if (decodeTLV && is_snmp) { + asn1_print(d + 6, resp.length - 6, " "); } return PM3_SUCCESS; @@ -5530,14 +6105,14 @@ static command_t CommandTable[] = { {"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"}, {"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "Write Picopass / iCLASS block"}, {"creditepurse", CmdHFiClassCreditEpurse, IfPm3Iclass, "Credit epurse value"}, - {"trbl", CmdHFiClass_TearBlock, IfPm3Iclass, "Performs tearoff attack on iClass block"}, + {"tear", CmdHFiClass_TearBlock, IfPm3Iclass, "Performs tearoff attack on iCLASS block"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("Recovery") " --------------------"}, // {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "Automatic key recovery tool for iCLASS"}, {"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "Check keys"}, {"loclass", CmdHFiClass_loclass, AlwaysAvailable, "Use loclass to perform bruteforce reader attack"}, {"lookup", CmdHFiClassLookUp, AlwaysAvailable, "Uses authentication trace to check for key in dictionary file"}, {"legrec", CmdHFiClassLegacyRecover, IfPm3Iclass, "Recovers 24 bits of the diversified key of a legacy card provided a valid nr-mac combination"}, - {"legbrute", CmdHFiClassLegRecLookUp, AlwaysAvailable, "Bruteforces 40 bits of a partial diversified key, provided 24 bits of the key and two valid nr-macs"}, + {"legbrute", CmdHFiClassLegBrute, AlwaysAvailable, "Bruteforces 40 bits of a partial diversified key, provided 24 bits of the key and two valid nr-macs"}, {"unhash", CmdHFiClassUnhash, AlwaysAvailable, "Reverses a diversified key to retrieve hash0 pre-images after DES encryption"}, {"-----------", CmdHelp, IfPm3Iclass, "-------------------- " _CYAN_("Simulation") " -------------------"}, {"sim", CmdHFiClassSim, IfPm3Iclass, "Simulate iCLASS tag"}, @@ -5616,7 +6191,7 @@ int info_iclass(bool shallow_mod) { picopass_ns_hdr_t *ns_hdr = &r->header.ns_hdr; PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ----------------------------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " -------------------------------------"); if ((r->status & FLAG_ICLASS_CSN) == FLAG_ICLASS_CSN) { PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); @@ -5634,19 +6209,19 @@ int info_iclass(bool shallow_mod) { } else { if ((r->status & FLAG_ICLASS_CC) == FLAG_ICLASS_CC) { - PrintAndLogEx(SUCCESS, "E-purse: %s Card challenge, CC", sprint_hex(hdr->epurse, sizeof(hdr->epurse))); + PrintAndLogEx(SUCCESS, "E-purse: %s card challenge, CC", sprint_hex(hdr->epurse, sizeof(hdr->epurse))); } if (memcmp(hdr->key_d, zeros, sizeof(zeros))) { PrintAndLogEx(SUCCESS, " Kd: " _YELLOW_("%s") " debit key", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); } else { - PrintAndLogEx(SUCCESS, " Kd: %s debit key ( hidden )", sprint_hex(hdr->key_d, sizeof(hdr->key_d))); + PrintAndLogEx(SUCCESS, " Kd: -- -- -- -- -- -- -- -- debit key ( hidden )"); } if (memcmp(hdr->key_c, zeros, sizeof(zeros))) { PrintAndLogEx(SUCCESS, " Kc: " _YELLOW_("%s") " credit key", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); } else { - PrintAndLogEx(SUCCESS, " Kc: %s credit key ( hidden )", sprint_hex(hdr->key_c, sizeof(hdr->key_c))); + PrintAndLogEx(SUCCESS, " Kc: -- -- -- -- -- -- -- -- credit key ( hidden )"); } @@ -5659,7 +6234,7 @@ int info_iclass(bool shallow_mod) { print_picopass_info(hdr); } - PrintAndLogEx(INFO, "------------------------ " _CYAN_("Fingerprint") " -----------------------"); + PrintAndLogEx(INFO, "----------------------- " _CYAN_("Fingerprint") " ---------------------"); uint8_t aia[8]; if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { @@ -5691,33 +6266,46 @@ int info_iclass(bool shallow_mod) { uint8_t cardtype = get_mem_config(hdr); PrintAndLogEx(SUCCESS, " Card type.... " _GREEN_("%s"), card_types[cardtype]); - if (HF14B_picopass_reader(false, false)) { - PrintAndLogEx(SUCCESS, " Card chip.... "_YELLOW_("Old Silicon (14b support)")); - } else { + if (memcmp(hdr->csn + 4, "\xFE\xFF\x12\xE0", 4) == 0) { PrintAndLogEx(SUCCESS, " Card chip.... "_YELLOW_("NEW Silicon (No 14b support)")); + } else { + PrintAndLogEx(SUCCESS, " Card chip.... "_YELLOW_("Old Silicon (14b support)")); } if (legacy) { int res = PM3_ESOFT; - uint8_t key_type = 0x88; // debit key - uint8_t dump[PICOPASS_BLOCK_SIZE * 8] = {0}; // we take all raw bytes from response memcpy(dump, p_response, sizeof(picopass_hdr_t)); + bool found_aa1 = false; + bool found_aa2 = false; uint8_t key[8] = {0}; for (uint8_t i = 0; i < ARRAYLEN(iClass_Key_Table); i++) { memcpy(key, iClass_Key_Table[i], sizeof(key)); - res = iclass_read_block_ex(key, 6, key_type, false, false, false, false, true, false, dump + (PICOPASS_BLOCK_SIZE * 6), false); + + if (found_aa1 == false) { + res = iclass_read_block_ex(key, 6, ICLASS_DEBIT_KEYTYPE, false, false, false, false, true, false, dump + (PICOPASS_BLOCK_SIZE * 6), false); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, " AA1 Key...... " _GREEN_("%s"), sprint_hex_inrow(key, sizeof(key))); + found_aa1 = true; + } + } + + res = iclass_read_block_ex(key, 6, ICLASS_CREDIT_KEYTYPE, false, false, false, false, true, false, dump + (PICOPASS_BLOCK_SIZE * 7), false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, " AA1 Key...... " _GREEN_("%s"), sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(SUCCESS, " AA2 Key...... " _GREEN_("%s"), sprint_hex_inrow(key, sizeof(key))); + found_aa2 = true; + } + + if (found_aa1 && found_aa2) { break; } } - if (res == PM3_SUCCESS) { - res = iclass_read_block_ex(key, 7, key_type, false, false, false, false, true, false, dump + (PICOPASS_BLOCK_SIZE * 7), false); + if (found_aa1) { + res = iclass_read_block_ex(key, 7, ICLASS_DEBIT_KEYTYPE, false, false, false, false, true, false, dump + (PICOPASS_BLOCK_SIZE * 7), false); if (res == PM3_SUCCESS) { BLOCK79ENCRYPTION aa1_encryption = (dump[(6 * PICOPASS_BLOCK_SIZE) + 7] & 0x03); @@ -5733,5 +6321,6 @@ int info_iclass(bool shallow_mod) { } } + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } diff --git a/client/src/cmdhfjooki.c b/client/src/cmdhfjooki.c index 51f61e619..81e7b3546 100644 --- a/client/src/cmdhfjooki.c +++ b/client/src/cmdhfjooki.c @@ -584,7 +584,7 @@ static int CmdHF14AJookiSim(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); break; } diff --git a/client/src/cmdhfksx6924.c b/client/src/cmdhfksx6924.c index 7616ca766..b6a72fe2e 100644 --- a/client/src/cmdhfksx6924.c +++ b/client/src/cmdhfksx6924.c @@ -358,7 +358,7 @@ end: static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"select", CmdHFKSX6924Select, IfPm3Iso14443a, "Select application, and leave field up"}, - {"info", CmdHFKSX6924Info, IfPm3Iso14443a, "Get info about a KS X 6924 (T-Money, Snapper+) transit card"}, + {"info", CmdHFKSX6924Info, IfPm3Iso14443a, "Tag information"}, {"balance", CmdHFKSX6924Balance, IfPm3Iso14443a, "Get current purse balance"}, {"init", CmdHFKSX6924Initialize, IfPm3Iso14443a, "Perform transaction initialization with Mpda"}, {"prec", CmdHFKSX6924PRec, IfPm3Iso14443a, "Send proprietary get record command (CLA=90, INS=4C)"}, diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index 0ff062532..be84822bf 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -560,7 +560,7 @@ static int CmdLegicSim(const char *Cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "Aborted via keyboard!"); break; } diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index b56a0d34c..51ede17ca 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -131,7 +131,7 @@ static int lto_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response, uint16 SendCommandMIX(CMD_HF_ISO14443A_READER, arg0, arg1, 0, cmd, len); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { if (verbose) PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 74899cd38..29eefbf0e 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -48,8 +48,302 @@ #include "mifare/mifarehost.h" #include "crypto/originality.h" +// Defines for Saflok parsing +#define SAFLOK_YEAR_OFFSET 1980 +#define SAFLOK_BASIC_ACCESS_BYTE_NUM 17 + +typedef struct { + uint64_t a; + uint64_t b; +} MfClassicKeyPair; + +// Structure for Saflok key levels +typedef struct { + uint8_t level_num; + const char *level_name; +} SaflokKeyLevel; + static int CmdHelp(const char *Cmd); +// Static array for Saflok key levels +static const SaflokKeyLevel saflok_key_levels[] = { + {1, "Guest Key"}, + {2, "Connectors"}, + {3, "Suite"}, + {4, "Limited Use"}, + {5, "Failsafe"}, + {6, "Inhibit"}, + {7, "Pool/Meeting Master"}, + {8, "Housekeeping"}, + {9, "Floor Key"}, + {10, "Section Key"}, + {11, "Rooms Master"}, + {12, "Grand Master"}, + {13, "Emergency"}, + {14, "Electronic Lockout"}, + {15, "Secondary Programming Key (SPK)"}, + {16, "Primary Programming Key (PPK)"}, +}; + +// Lookup table for Saflok decryption +static const uint8_t saflok_c_aDecode[256] = { + 0xEA, 0x0D, 0xD9, 0x74, 0x4E, 0x28, 0xFD, 0xBA, 0x7B, 0x98, 0x87, 0x78, 0xDD, 0x8D, 0xB5, + 0x1A, 0x0E, 0x30, 0xF3, 0x2F, 0x6A, 0x3B, 0xAC, 0x09, 0xB9, 0x20, 0x6E, 0x5B, 0x2B, 0xB6, + 0x21, 0xAA, 0x17, 0x44, 0x5A, 0x54, 0x57, 0xBE, 0x0A, 0x52, 0x67, 0xC9, 0x50, 0x35, 0xF5, + 0x41, 0xA0, 0x94, 0x60, 0xFE, 0x24, 0xA2, 0x36, 0xEF, 0x1E, 0x6B, 0xF7, 0x9C, 0x69, 0xDA, + 0x9B, 0x6F, 0xAD, 0xD8, 0xFB, 0x97, 0x62, 0x5F, 0x1F, 0x38, 0xC2, 0xD7, 0x71, 0x31, 0xF0, + 0x13, 0xEE, 0x0F, 0xA3, 0xA7, 0x1C, 0xD5, 0x11, 0x4C, 0x45, 0x2C, 0x04, 0xDB, 0xA6, 0x2E, + 0xF8, 0x64, 0x9A, 0xB8, 0x53, 0x66, 0xDC, 0x7A, 0x5D, 0x03, 0x07, 0x80, 0x37, 0xFF, 0xFC, + 0x06, 0xBC, 0x26, 0xC0, 0x95, 0x4A, 0xF1, 0x51, 0x2D, 0x22, 0x18, 0x01, 0x79, 0x5E, 0x76, + 0x1D, 0x7F, 0x14, 0xE3, 0x9E, 0x8A, 0xBB, 0x34, 0xBF, 0xF4, 0xAB, 0x48, 0x63, 0x55, 0x3E, + 0x56, 0x8C, 0xD1, 0x12, 0xED, 0xC3, 0x49, 0x8E, 0x92, 0x9D, 0xCA, 0xB1, 0xE5, 0xCE, 0x4D, + 0x3F, 0xFA, 0x73, 0x05, 0xE0, 0x4B, 0x93, 0xB2, 0xCB, 0x08, 0xE1, 0x96, 0x19, 0x3D, 0x83, + 0x39, 0x75, 0xEC, 0xD6, 0x3C, 0xD0, 0x70, 0x81, 0x16, 0x29, 0x15, 0x6C, 0xC7, 0xE7, 0xE2, + 0xF6, 0xB7, 0xE8, 0x25, 0x6D, 0x3A, 0xE6, 0xC8, 0x99, 0x46, 0xB0, 0x85, 0x02, 0x61, 0x1B, + 0x8B, 0xB3, 0x9F, 0x0B, 0x2A, 0xA8, 0x77, 0x10, 0xC1, 0x88, 0xCC, 0xA4, 0xDE, 0x43, 0x58, + 0x23, 0xB4, 0xA1, 0xA5, 0x5C, 0xAE, 0xA9, 0x7E, 0x42, 0x40, 0x90, 0xD2, 0xE9, 0x84, 0xCF, + 0xE4, 0xEB, 0x47, 0x4F, 0x82, 0xD4, 0xC5, 0x8F, 0xCD, 0xD3, 0x86, 0x00, 0x59, 0xDF, 0xF2, + 0x0C, 0x7C, 0xC6, 0xBD, 0xF9, 0x7D, 0xC4, 0x91, 0x27, 0x89, 0x32, 0x72, 0x33, 0x65, 0x68, + 0xAF +}; + +// Function to decrypt Saflok card data +static void DecryptSaflokCardData( + const uint8_t strCard[SAFLOK_BASIC_ACCESS_BYTE_NUM], + uint8_t decryptedCard[SAFLOK_BASIC_ACCESS_BYTE_NUM] +) { + int i; + int num; + int num2; + int num3; + int num4; + int b = 0; + int b2 = 0; + + for (i = 0; i < SAFLOK_BASIC_ACCESS_BYTE_NUM; i++) { + num = saflok_c_aDecode[strCard[i]] - (i + 1); + if (num < 0) num += 256; + decryptedCard[i] = num; + } + + b = decryptedCard[10]; + b2 = b & 1; + + for (num2 = SAFLOK_BASIC_ACCESS_BYTE_NUM; num2 > 0; num2--) { + b = decryptedCard[num2 - 1]; + for (num3 = 8; num3 > 0; num3--) { + num4 = num2 + num3; + if (num4 > SAFLOK_BASIC_ACCESS_BYTE_NUM) num4 -= SAFLOK_BASIC_ACCESS_BYTE_NUM; + int b3 = decryptedCard[num4 - 1]; + int b4 = (b3 & 0x80) >> 7; + b3 = ((b3 << 1) & 0xFF) | b2; + b2 = (b & 0x80) >> 7; + b = ((b << 1) & 0xFF) | b4; + decryptedCard[num4 - 1] = b3; + } + decryptedCard[num2 - 1] = b; + } +} + +// Function to calculate Saflok checksum +static uint8_t CalculateCheckSum(uint8_t data[SAFLOK_BASIC_ACCESS_BYTE_NUM]) { + int sum = 0; + for (int i = 0; i < SAFLOK_BASIC_ACCESS_BYTE_NUM - 1; i++) { + sum += data[i]; + } + sum = 255 - (sum & 0xFF); + return sum & 0xFF; +} + +// Function to parse and print Saflok data +static void ParseAndPrintSaflokData(const sector_t *sector0_info, const sector_t *sector1_info) { + (void)sector1_info; // Not directly used for payload parsing currently + + if (sector0_info == NULL) { + PrintAndLogEx(WARNING, "Saflok: Sector 0 information not available for parsing"); + return; + } + + uint8_t key_bytes_for_s0[MIFARE_KEY_SIZE]; + uint8_t key_type_for_s0; // CORRECTED: Was MifareKeyType, now uint8_t + bool s0_key_found = false; + + // Prioritize Key A for Sector 0 if available + if (sector0_info->foundKey[MF_KEY_A]) { + num_to_bytes(sector0_info->Key[MF_KEY_A], MIFARE_KEY_SIZE, key_bytes_for_s0); + key_type_for_s0 = MF_KEY_A; // MF_KEY_A is typically #define'd as 0x60 + s0_key_found = true; + PrintAndLogEx(DEBUG, "Saflok: Using Sector 0 Key A for reading blocks"); + } else if (sector0_info->foundKey[MF_KEY_B]) { // Fallback to Key B for Sector 0 + num_to_bytes(sector0_info->Key[MF_KEY_B], MIFARE_KEY_SIZE, key_bytes_for_s0); + key_type_for_s0 = MF_KEY_B; // MF_KEY_B is typically #define'd as 0x61 + s0_key_found = true; + PrintAndLogEx(DEBUG, "Saflok: Using Sector 0 Key B for reading blocks"); + } + + if (s0_key_found == false) { + PrintAndLogEx(WARNING, "Saflok: No known keys for Sector 0. Cannot read blocks 1 & 2 for parsing"); + return; + } + + uint8_t block1_content[MFBLOCK_SIZE]; + uint8_t block2_content[MFBLOCK_SIZE]; + + // Read absolute block 1 (data block within sector 0) + if (mf_read_block(1, key_type_for_s0, key_bytes_for_s0, block1_content) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Saflok: Failed to read card Block 1 using Sector 0 %s key", (key_type_for_s0 == MF_KEY_A) ? "A" : "B"); + return; + } + PrintAndLogEx(DEBUG, "Saflok: Successfully read card Block 1"); + + // Read absolute block 2 (data block within sector 0) + if (mf_read_block(2, key_type_for_s0, key_bytes_for_s0, block2_content) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Saflok: Failed to read card Block 2 using Sector 0 %s key", (key_type_for_s0 == MF_KEY_A) ? "A" : "B"); + return; + } + PrintAndLogEx(DEBUG, "Saflok: Successfully read card Block 2"); + + uint8_t basicAccess[SAFLOK_BASIC_ACCESS_BYTE_NUM]; + uint8_t decodedBA[SAFLOK_BASIC_ACCESS_BYTE_NUM]; + + memcpy(basicAccess, block1_content, MFBLOCK_SIZE); // 16 bytes from Block 1 + memcpy(basicAccess + MFBLOCK_SIZE, block2_content, 1); // 1 byte from Block 2 + + DecryptSaflokCardData(basicAccess, decodedBA); + + + // Byte 0: Key level, LED warning bit, and subgroup functions + uint8_t key_level = (decodedBA[0] & 0xF0) >> 4; + uint8_t led_warning = (decodedBA[0] & 0x08) >> 3; + + // Byte 1: Key ID + uint8_t key_id = decodedBA[1]; + + // Byte 2 & 3: KeyRecord, including OpeningKey flag + uint8_t opening_key = (decodedBA[2] & 0x80) >> 7; + uint16_t key_record = ((decodedBA[2] & 0x3F) << 8) | decodedBA[3]; + + // Byte 5 & 6: EncryptSequence + Combination + uint16_t sequence_combination_number = ((decodedBA[5] & 0x0F) << 8) | decodedBA[6]; + + // Byte 7: OverrideDeadbolt and Days + uint8_t override_deadbolt = (decodedBA[7] & 0x80) >> 7; + uint8_t restricted_weekday = decodedBA[7] & 0x7F; + + // Weekday names array + static const char *weekdays[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; + + // Buffer to store the resulting string (sufficient size for all weekdays) + char restricted_weekday_string[128] = {0}; + int restricted_count = 0; + + // Check each bit from Monday to Sunday + for (int i = 0; i < 7; i++) { + if (restricted_weekday & (0b01000000 >> i)) { + // If the bit is set, append the corresponding weekday to the buffer + if (restricted_count > 0) { + strcat(restricted_weekday_string, ", "); + } + strcat(restricted_weekday_string, weekdays[i]); + restricted_count++; + } + } + + // Determine if all weekdays are restricted + if (restricted_weekday == 0b01111100) { + strcpy(restricted_weekday_string, "weekdays"); + } + // If there are specific restricted days + else if (restricted_weekday == 0b00000011) { + strcpy(restricted_weekday_string, "weekends"); + } + // If no weekdays are restricted + else if (restricted_weekday == 0) { + strcpy(restricted_weekday_string, "none"); + } + + // Bytes 14-15: Property number and part of creation year + uint8_t creation_year_high_bits = (decodedBA[14] & 0xF0); + uint16_t property_id = ((decodedBA[14] & 0x0F) << 8) | decodedBA[15]; + + // Bytes 11-13: Creation date since SAFLOK_YEAR_OFFSET Jan 1st + uint16_t creation_year = (((decodedBA[11] & 0xF0) >> 4) + SAFLOK_YEAR_OFFSET) | creation_year_high_bits; + uint8_t creation_month = decodedBA[11] & 0x0F; + uint8_t creation_day = (decodedBA[12] >> 3) & 0x1F; + uint8_t creation_hour = ((decodedBA[12] & 0x07) << 2) | ((decodedBA[13] & 0xC0) >> 6); + uint8_t creation_minute = decodedBA[13] & 0x3F; + + // Bytes 8-10: Expiry interval / absolute time components + uint8_t interval_year_val = (decodedBA[8] >> 4); + uint8_t interval_month_val = decodedBA[8] & 0x0F; + uint8_t interval_day_val = (decodedBA[9] >> 3) & 0x1F; + uint8_t expiry_hour = ((decodedBA[9] & 0x07) << 2) | ((decodedBA[10] & 0xC0) >> 6); + uint8_t expiry_minute = decodedBA[10] & 0x3F; + + uint16_t expire_year = creation_year + interval_year_val; + uint8_t expire_month = creation_month + interval_month_val; + uint8_t expire_day = creation_day + interval_day_val; + + // Handle month rollover for expiration + while (expire_month > 12) { + expire_month -= 12; + expire_year++; + } + + // Handle day rollover for expiration + static const uint8_t days_in_month_lookup[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // 1-indexed month + if (expire_month > 0 && expire_month <= 12) { + + while (true) { + + uint8_t max_days = days_in_month_lookup[expire_month]; + if (expire_month == 2 && + (expire_year % 4 == 0 && + (expire_year % 100 != 0 || expire_year % 400 == 0))) { + max_days = 29; // Leap year + } + + if (expire_day <= max_days) { + break; + } + + if (max_days == 0) { // Should not happen with valid month + PrintAndLogEx(WARNING, "Saflok: Invalid day/month for expiration rollover calculation"); + break; + } + + expire_day -= max_days; + expire_month++; + if (expire_month > 12) { + expire_month = 1; + expire_year++; + } + } + + } else if (expire_month != 0) { // Allow 0 if it signifies no expiration or error + PrintAndLogEx(WARNING, "Saflok: Invalid expiration month (%u) before day rollover", expire_month); + } + + uint8_t checksum = decodedBA[16]; + uint8_t checksum_calculated = CalculateCheckSum(decodedBA); + bool checksum_valid = (checksum_calculated == checksum); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("SAFLOK details")); + PrintAndLogEx(SUCCESS, "Key Level............. %u (%s)", saflok_key_levels[key_level].level_num, saflok_key_levels[key_level].level_name); + PrintAndLogEx(SUCCESS, "LED Warning........... %s", led_warning ? "Yes" : "No"); + PrintAndLogEx(SUCCESS, "Key ID................ %u (0x%02X)", key_id, key_id); + PrintAndLogEx(SUCCESS, "Key Record............ %u (0x%04X)", key_record, key_record); + PrintAndLogEx(SUCCESS, "Opening Key........... %s", opening_key ? "Yes" : "No"); + PrintAndLogEx(SUCCESS, "Sequence & Combination: %u (0x%02X)", sequence_combination_number, sequence_combination_number); + PrintAndLogEx(SUCCESS, "Override Deadbolt..... %s", override_deadbolt ? "Yes" : "No"); + PrintAndLogEx(SUCCESS, "Restricted Weekdays... %s", restricted_weekday_string); + PrintAndLogEx(SUCCESS, "Property ID........... %u (0x%04X)", property_id, property_id); + PrintAndLogEx(SUCCESS, "Creation Date......... %04u-%02u-%02u %02u:%02u", creation_year, creation_month, creation_day, creation_hour, creation_minute); + PrintAndLogEx(SUCCESS, "Expiration Date....... %04u-%02u-%02u %02u:%02u", expire_year, expire_month, expire_day, expiry_hour, expiry_minute); + PrintAndLogEx(SUCCESS, "Checksum Valid........ ( %s )", checksum_valid ? _GREEN_("ok") : _RED_("fail")); +} + /* static int usage_hf14_keybrute(void) { PrintAndLogEx(NORMAL, "J_Run's 2nd phase of multiple sector nested authentication key recovery"); @@ -75,7 +369,7 @@ int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, in static int mf_read_uid(uint8_t *uid, int *uidlen, int *nxptype) { clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select failed"); @@ -86,20 +380,63 @@ static int mf_read_uid(uint8_t *uid, int *uidlen, int *nxptype) { iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + uint64_t select_status = resp.oldarg[0]; + + // try to request ATS even if tag claims not to support it. If yes => 4 + if (select_status == 2) { + uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); + card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes + if (card.ats_len > 3) { + select_status = 4; + } + } + + uint8_t ats_hist_pos = 0; + if ((card.ats_len > 3) && (card.ats[0] > 1)) { + ats_hist_pos = 2; + ats_hist_pos += (card.ats[1] & 0x10) == 0x10; + ats_hist_pos += (card.ats[1] & 0x20) == 0x20; + ats_hist_pos += (card.ats[1] & 0x40) == 0x40; + } + + version_hw_t version_hw = {0}; + // if 4b UID or NXP, try to get version + int res = hf14a_getversion_data(&card, select_status, &version_hw); + DropField(); + + bool version_hw_available = (res == PM3_SUCCESS); + if (nxptype) { - uint64_t select_status = resp.oldarg[0]; - *nxptype = detect_nxp_card(card.sak, ((card.atqa[1] << 8) + card.atqa[0]), select_status); + + *nxptype = detect_nxp_card(card.sak + , ((card.atqa[1] << 8) + card.atqa[0]) + , select_status + , card.ats_len - ats_hist_pos + , card.ats + ats_hist_pos + , version_hw_available + , &version_hw + ); } memcpy(uid, card.uid, card.uidlen * sizeof(uint8_t)); *uidlen = card.uidlen; + return PM3_SUCCESS; } static char *GenerateFilename(const char *prefix, const char *suffix) { - if (! IfPm3Iso14443a()) { + if (IfPm3Iso14443a() == false) { return NULL; } + uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int uidlen = 0; char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(uid) * 2 + 1, sizeof(uint8_t)); @@ -139,20 +476,19 @@ static int initSectorTable(sector_t **src, size_t items) { static void decode_print_st(uint16_t blockno, uint8_t *data) { if (mfIsSectorTrailer(blockno)) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "-------------------------- " _CYAN_("Sector trailer decoder") " --------------------------"); - PrintAndLogEx(INFO, "key A........ " _GREEN_("%s"), sprint_hex_inrow(data, 6)); - PrintAndLogEx(INFO, "acr.......... " _GREEN_("%s"), sprint_hex_inrow(data + 6, 3)); - PrintAndLogEx(INFO, "user / gpb... " _GREEN_("%02x"), data[9]); - PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); + PrintAndLogEx(INFO, "------------------------ " _CYAN_("Sector trailer decoder") " ------------------------"); + PrintAndLogEx(INFO, " Key A........ " _BRIGHT_GREEN_("%s"), sprint_hex_inrow(data, 6)); + PrintAndLogEx(INFO, " ACR.......... " _MAGENTA_("%s"), sprint_hex_inrow(data + 6, 3)); + PrintAndLogEx(INFO, " User / gpb... %02x", data[9]); + PrintAndLogEx(INFO, " Key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, " # | access rights"); - PrintAndLogEx(INFO, "----+-----------------------------------------------------------------------"); + PrintAndLogEx(INFO, "----+-------------------------------------------------------------------"); if (mfValidateAccessConditions(&data[6]) == false) { - PrintAndLogEx(WARNING, _RED_("Invalid Access Conditions")); + PrintAndLogEx(WARNING, _RED_("Invalid access conditions")); } - int bln = mfFirstBlockOfSector(mfSectorNum(blockno)); int blinc = (mfNumBlocksPerSector(mfSectorNum(blockno)) > 4) ? 5 : 1; for (int i = 0; i < 4; i++) { @@ -163,13 +499,13 @@ static void decode_print_st(uint16_t blockno, uint8_t *data) { uint8_t cond = mf_get_accesscondition(i, &data[6]); if (cond == 0 || cond == 1 || cond == 2) { PrintAndLogEx(INFO, ""); - PrintAndLogEx(INFO, "OBS! Key B is readable, it SHALL NOT be able to authenticate on original MFC"); + PrintAndLogEx(INFO, "OBS!"); + PrintAndLogEx(INFO, "Key B is readable, it SHALL NOT be able to authenticate on original MFC"); } } } - - PrintAndLogEx(INFO, "----------------------------------------------------------------------------"); + PrintAndLogEx(INFO, "------------------------------------------------------------------------"); PrintAndLogEx(NORMAL, ""); } } @@ -246,7 +582,7 @@ void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose) { char ascii[24] = {0}; ascii_to_buffer((uint8_t *)ascii, d, MFBLOCK_SIZE, sizeof(ascii) - 1, 1); - PrintAndLogEx(INFO, "%3d | " _YELLOW_("%s") _MAGENTA_("%s") "%02X " _YELLOW_("%s") "| " _YELLOW_("%s"), + PrintAndLogEx(INFO, "%3d | " _BRIGHT_GREEN_("%s") _MAGENTA_("%s") "%02X " _GREEN_("%s") "| " _YELLOW_("%s"), blockno, keya, acl, @@ -265,7 +601,7 @@ void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose) { } } -static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) { +static void mf_print_block(uint16_t maxblocks, uint8_t blockno, uint8_t *d, bool verbose) { uint8_t sectorno = mfSectorNum(blockno); char secstr[6] = " "; @@ -297,7 +633,7 @@ static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) { char ascii[24] = {0}; ascii_to_buffer((uint8_t *)ascii, d, MFBLOCK_SIZE, sizeof(ascii) - 1, 1); - if (blockno >= MIFARE_1K_MAXBLOCK) { + if (maxblocks < 18 && blockno >= MIFARE_1K_MAXBLOCK) { PrintAndLogEx(INFO, _BACK_BLUE_("%s| %3d | " _YELLOW_("%s")) _BACK_BLUE_(_MAGENTA_("%s")) @@ -326,7 +662,7 @@ static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) { } } else { - if (blockno >= MIFARE_1K_MAXBLOCK) { + if (maxblocks < 18 && blockno >= MIFARE_1K_MAXBLOCK) { // MFC Ev1 signature blocks. PrintAndLogEx(INFO, _BACK_BLUE_("%s| %3d | %s"), secstr, blockno, sprint_hex_ascii(d, MFBLOCK_SIZE)); } else { @@ -340,15 +676,17 @@ static void mf_print_block(uint8_t blockno, uint8_t *d, bool verbose) { } } -static void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose) { +void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "-----+-----+-------------------------------------------------+-----------------"); PrintAndLogEx(INFO, " sec | blk | data | ascii"); PrintAndLogEx(INFO, "-----+-----+-------------------------------------------------+-----------------"); + for (uint16_t i = 0; i < n; i++) { - mf_print_block(i, d + (i * MFBLOCK_SIZE), verbose); + mf_print_block(n, i, d + (i * MFBLOCK_SIZE), verbose); } PrintAndLogEx(INFO, "-----+-----+-------------------------------------------------+-----------------"); + if (verbose) { PrintAndLogEx(HINT, _CYAN_("cyan") " = value block with decoded value"); PrintAndLogEx(HINT, _CYAN_("background blue") " = MFC Ev1 signature blocks"); @@ -362,7 +700,7 @@ static void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose) { } // assumes n is in number of blocks 0..255 -static int mf_print_keys(uint16_t n, uint8_t *d) { +int mf_print_keys(uint16_t n, uint8_t *d) { uint8_t sectors = 0; switch (n) { case MIFARE_MINI_MAXBLOCK: @@ -578,20 +916,25 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n char *fptr = NULL; if (keyfn == NULL || keyfn[0] == '\0') { fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) + if (fptr == NULL) { return PM3_ESOFT; + } keyfn = fptr ; } - PrintAndLogEx(INFO, "Using... %s", keyfn); - size_t alen = 0, blen = 0; - uint8_t *keyA, *keyB; - if (loadFileBinaryKey(keyfn, "", (void **)&keyA, (void **)&keyB, &alen, &blen) != PM3_SUCCESS) { + uint8_t *keyA = NULL, *keyB = NULL; + if (loadFileBinaryKey(keyfn, "", (void **)&keyA, (void **)&keyB, &alen, &blen, true) != PM3_SUCCESS) { free(fptr); return PM3_ESOFT; } + free(fptr); + + if ((alen < (numSectors * MIFARE_KEY_SIZE)) || (blen < (numSectors * MIFARE_KEY_SIZE))) { + PrintAndLogEx(WARNING, "Key file is too small for selected card type"); + return PM3_ELENGTH; + } PrintAndLogEx(INFO, "Reading sector access bits..."); PrintAndLogEx(INFO, "." NOLF); @@ -600,15 +943,17 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n mf_readblock_t payload; uint8_t current_key; + for (uint8_t sectorNo = 0; sectorNo < numSectors; sectorNo++) { + current_key = MF_KEY_A; + for (uint8_t tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) { PrintAndLogEx(NORMAL, "." NOLF); fflush(stdout); if (kbd_enter_pressed()) { PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - free(fptr); free(keyA); free(keyB); return PM3_EOPABORTED; @@ -653,7 +998,9 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n PrintAndLogEx(INFO, "Dumping all blocks from card..."); for (uint8_t sectorNo = 0; sectorNo < numSectors; sectorNo++) { + for (uint8_t blockNo = 0; blockNo < mfNumBlocksPerSector(sectorNo); blockNo++) { + bool received = false; current_key = MF_KEY_A; uint8_t data_area = (sectorNo < 32) ? blockNo : blockNo / 5; @@ -674,6 +1021,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); + } else { // data block. Check if it can be read with key A or key B if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) { @@ -685,6 +1033,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); received = WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500); + } else { // key A would work payload.blockno = mfFirstBlockOfSector(sectorNo) + blockNo; @@ -735,7 +1084,7 @@ static int mfc_read_tag(iso14a_card_select_t *card, uint8_t *carddata, uint8_t n } } - free(fptr); + free(keyA); free(keyB); @@ -897,13 +1246,15 @@ static int CmdHF14AMfDarkside(const char *Cmd) { uint64_t key = 0; uint64_t t1 = msclock(); - int ret = mf_dark_side(blockno, key_type, &key); + int res = mf_dark_side(blockno, key_type, &key); t1 = msclock() - t1; - if (ret != PM3_SUCCESS) return ret; + if (res != PM3_SUCCESS) { + return res; + } - PrintAndLogEx(SUCCESS, "found valid key: " _GREEN_("%012" PRIx64), key); - PrintAndLogEx(SUCCESS, "time in darkside " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(SUCCESS, "Found valid key [ "_GREEN_("%012" PRIX64) " ]", key); + PrintAndLogEx(SUCCESS, "Time in darkside " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); return PM3_SUCCESS; } @@ -1133,7 +1484,7 @@ static int CmdHF14AMfRdSc(const char *Cmd) { } int keylen = 0; - uint8_t key[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + uint8_t key[MIFARE_KEY_SIZE] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; CLIGetHexWithReturn(ctx, 4, key, &keylen); int s = arg_get_int_def(ctx, 5, 0); @@ -1165,6 +1516,13 @@ static int CmdHF14AMfRdSc(const char *Cmd) { uint8_t blocks = mfNumBlocksPerSector(sector); uint8_t start = mfFirstBlockOfSector(sector); + // since this was a successful read, add our known key to the output + if (keytype == MF_KEY_A) { + memcpy(data + ((blocks - 1) * MFBLOCK_SIZE), key, MIFARE_KEY_SIZE); + } else { + memcpy(data + ((blocks - 1) * MFBLOCK_SIZE) + 10, key, MIFARE_KEY_SIZE); + } + mf_print_sector_hdr(sector); for (int i = 0; i < blocks; i++) { mf_print_block_one(start + i, data + (i * MFBLOCK_SIZE), verbose); @@ -1208,7 +1566,7 @@ static int FastDumpWithEcFill(uint8_t numsectors) { } if (resp.status != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "fast dump reported back failure w KEY A, swapping to KEY B"); + PrintAndLogEx(FAILED, "fast dump reported back failure w KEY A. Swapping to KEY B"); // ecfill key B payload.keytype = MF_KEY_B; @@ -1466,12 +1824,10 @@ static int CmdHF14AMfRestore(const char *Cmd) { // size_t alen = 0, blen = 0; uint8_t *keyA, *keyB; - if (loadFileBinaryKey(keyfilename, "", (void **)&keyA, (void **)&keyB, &alen, &blen) != PM3_SUCCESS) { + if (loadFileBinaryKey(keyfilename, "", (void **)&keyA, (void **)&keyB, &alen, &blen, true) != PM3_SUCCESS) { return PM3_ESOFT; } - PrintAndLogEx(INFO, "Using key file `" _YELLOW_("%s") "`", keyfilename); - // try reading card uid and create filename if (datafnlen == 0) { char *fptr = GenerateFilename("hf-mf-", "-dump.bin"); @@ -1650,7 +2006,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't CLIExecWithReturn(ctx, Cmd, argtable, false); int keylen = 0; - uint8_t key[6] = {0}; + uint8_t key[MIFARE_KEY_SIZE] = {0}; CLIGetHexWithReturn(ctx, 1, key, &keylen); bool m0 = arg_get_lit(ctx, 2); @@ -1669,6 +2025,7 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else if (arg_get_lit(ctx, 8)) { keyType = MF_KEY_B; } + uint8_t prev_keytype = keyType; keyType = arg_get_int_def(ctx, 9, keyType); if ((arg_get_lit(ctx, 7) || arg_get_lit(ctx, 8)) && (keyType != prev_keytype)) { @@ -1688,13 +2045,16 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else if (arg_get_lit(ctx, 12)) { trgKeyType = MF_KEY_B; } + uint8_t prev_trgkeytype = trgKeyType; trgKeyType = arg_get_int_def(ctx, 13, trgKeyType); + if ((arg_get_lit(ctx, 11) || arg_get_lit(ctx, 12)) && (trgKeyType != prev_trgkeytype)) { CLIParserFree(ctx); PrintAndLogEx(WARNING, "Choose one single target key type"); return PM3_EINVARG; } + bool transferToEml = arg_get_lit(ctx, 14); bool createDumpFile = arg_get_lit(ctx, 15); bool singleSector = trgBlockNo > -1; @@ -1743,16 +2103,16 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't return PM3_EINVARG; } } + if (SectorsCnt == 1) { SectorsCnt = MIFARE_1K_MAXSECTOR; } - if (keylen != 6) { - PrintAndLogEx(WARNING, "Input key must include 12 HEX symbols"); + if (keylen != MIFARE_KEY_SIZE) { + PrintAndLogEx(WARNING, "Input key must include 6 HEX bytes, got %u", keylen); return PM3_EINVARG; } - sector_t *e_sector = NULL; uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6]; uint64_t key64 = 0; @@ -1766,15 +2126,17 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't // check if we can authenticate to sector if (mf_check_keys(blockNo, keyType, true, 1, key, &key64) != PM3_SUCCESS) { if (keyType < 2) { - PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); + PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block %3d key type %c", blockNo, keyType ? 'B' : 'A'); } else { - PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%02x", blockNo, MIFARE_AUTH_KEYA + keyType); + PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block %3d key type %02x", blockNo, MIFARE_AUTH_KEYA + keyType); } return PM3_EOPABORTED; } if (singleSector) { - int16_t isOK = mf_nested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, !ignore_static_encrypted); + + uint8_t foundkey[MIFARE_KEY_SIZE] = {0}; + int16_t isOK = mf_nested(blockNo, keyType, key, trgBlockNo, trgKeyType, foundkey, !ignore_static_encrypted); switch (isOK) { case PM3_ETIMEOUT: PrintAndLogEx(ERR, "command execution time out\n"); @@ -1789,10 +2151,10 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't PrintAndLogEx(FAILED, "No valid key found"); break; case PM3_ESTATIC_NONCE: - PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); break; - case PM3_SUCCESS: - key64 = bytes_to_num(keyBlock, 6); + case PM3_SUCCESS: { // transfer key to the emulator if (transferToEml) { @@ -1803,32 +2165,43 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } else { // 16 block sector sectortrailer = trgBlockNo | 0x0f; } - mf_eml_get_mem(keyBlock, sectortrailer, 1); - if (trgKeyType == MF_KEY_A) - num_to_bytes(key64, 6, keyBlock); - else - num_to_bytes(key64, 6, &keyBlock[10]); + uint8_t block[MFBLOCK_SIZE] = {0}; + mf_eml_get_mem(block, sectortrailer, 1); - mf_elm_set_mem(keyBlock, sectortrailer, 1); - PrintAndLogEx(SUCCESS, "Key transferred to emulator memory."); + if (trgKeyType == MF_KEY_A) { + memcpy(block, foundkey, MIFARE_KEY_SIZE); + } else { + memcpy(block + 10, foundkey, MIFARE_KEY_SIZE); + } + + mf_elm_set_mem(block, sectortrailer, 1); + PrintAndLogEx(SUCCESS, "Key transferred to emulator memory"); } return PM3_SUCCESS; - default : + } + default : { PrintAndLogEx(ERR, "Unknown error\n"); + } } return PM3_SUCCESS; } else { // ------------------------------------ multiple sectors working - uint64_t t1 = msclock(); - e_sector = calloc(SectorsCnt, sizeof(sector_t)); - if (e_sector == NULL) return PM3_EMALLOC; + uint64_t t2; + + sector_t *e_sector = NULL; + if (initSectorTable(&e_sector, SectorsCnt) != PM3_SUCCESS) { + return PM3_EMALLOC; + } // add our known key e_sector[mfSectorNum(blockNo)].foundKey[keyType] = 1; e_sector[mfSectorNum(blockNo)].Key[keyType] = key64; + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter dictionary recovery mode") " ---------------"); + PrintAndLogEx(SUCCESS, "Sector count "_YELLOW_("%d"), SectorsCnt); + //test current key and additional standard keys first // add parameter key memcpy(keyBlock + (ARRAYLEN(g_mifare_default_keys) * 6), key, 6); @@ -1837,6 +2210,8 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6)); } + uint64_t t1 = msclock(); + PrintAndLogEx(SUCCESS, "Testing known keys. Sector count "_YELLOW_("%d"), SectorsCnt); int res = mf_check_keys_fast(SectorsCnt, true, true, 1, ARRAYLEN(g_mifare_default_keys) + 1, keyBlock, e_sector, use_flashmemory, false); if (res == PM3_SUCCESS) { @@ -1844,9 +2219,9 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't goto jumptoend; } - uint64_t t2 = msclock() - t1; - PrintAndLogEx(SUCCESS, "Time to check " _YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0); - PrintAndLogEx(SUCCESS, "enter nested key recovery"); + t2 = msclock() - t1; + PrintAndLogEx(SUCCESS, "Time in check keys " _YELLOW_("%.0f") " seconds\n", (float)t2 / 1000.0); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter nested key recovery mode") " ---------------"); // nested sectors bool calibrate = !ignore_static_encrypted; @@ -1855,7 +2230,14 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) { for (int i = 0; i < MIFARE_SECTOR_RETRY; i++) { - if (e_sector[sectorNo].foundKey[trgKeyType]) continue; + while (kbd_enter_pressed()) { + PrintAndLogEx(WARNING, "\naborted via keyboard!"); + return PM3_EOPABORTED; + } + + if (e_sector[sectorNo].foundKey[trgKeyType]) { + continue; + } int16_t isOK = mf_nested(blockNo, keyType, key, mfFirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); switch (isOK) { @@ -1873,12 +2255,13 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't calibrate = false; continue; case PM3_ESTATIC_NONCE: - PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); break; case PM3_SUCCESS: calibrate = false; e_sector[sectorNo].foundKey[trgKeyType] = 1; - e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); + e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, MIFARE_KEY_SIZE); mf_check_keys_fast(SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false, false); continue; @@ -1892,37 +2275,41 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't } t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "time in nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(SUCCESS, "Time in nested " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? - PrintAndLogEx(INFO, "trying to read key B..."); + PrintAndLogEx(INFO, "Trying to read key B..."); for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { uint8_t sectrail = (mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1); - PrintAndLogEx(SUCCESS, "reading block %d", sectrail); + PrintAndLogEx(SUCCESS, "Reading block " _YELLOW_("%d"), sectrail); mf_readblock_t payload; payload.blockno = sectrail; payload.keytype = MF_KEY_A; - num_to_bytes(e_sector[i].Key[0], 6, payload.key); // KEY A + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, payload.key); // KEY A clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) continue; + if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) { + continue; + } - if (resp.status != PM3_SUCCESS) continue; + if (resp.status != PM3_SUCCESS) { + continue; + } uint8_t *data = resp.data.asBytes; - key64 = bytes_to_num(data + 10, 6); + key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); if (key64) { - PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, 6)); + PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data + 10, MIFARE_KEY_SIZE)); e_sector[i].foundKey[1] = true; e_sector[i].Key[1] = key64; } @@ -1931,10 +2318,6 @@ static int CmdHF14AMfNested(const char *Cmd) { //TODO: single mode broken? can't jumptoend: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - - //print them printKeyTable(SectorsCnt, e_sector); // transfer them to the emulator @@ -1945,10 +2328,10 @@ jumptoend: mf_eml_get_mem(keyBlock, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1); if (e_sector[i].foundKey[0]) - num_to_bytes(e_sector[i].Key[0], 6, keyBlock); + num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, keyBlock); if (e_sector[i].foundKey[1]) - num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]); + num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, &keyBlock[10]); if (i == SectorsCnt - 1) { // Disable fast mode on last packet @@ -1972,6 +2355,9 @@ jumptoend: } free(e_sector); } + + PrintAndLogEx(INFO, "Done!"); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -2106,8 +2492,8 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { } uint64_t t2 = msclock() - t1; - PrintAndLogEx(SUCCESS, "Time to check "_YELLOW_("%zu") " known keys: %.0f seconds\n", ARRAYLEN(g_mifare_default_keys), (float)t2 / 1000.0); - PrintAndLogEx(SUCCESS, "enter static nested key recovery"); + PrintAndLogEx(SUCCESS, "Time in check keys " _YELLOW_("%.0f") " seconds\n", (float)t2 / 1000.0); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("Enter static nested key recovery") " --------------"); // nested sectors for (trgKeyType = MF_KEY_A; trgKeyType <= MF_KEY_B; ++trgKeyType) { @@ -2147,7 +2533,7 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? - PrintAndLogEx(INFO, "trying to read key B..."); + PrintAndLogEx(INFO, "Trying to read key B..."); for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { @@ -2184,9 +2570,6 @@ static int CmdHF14AMfNestedStatic(const char *Cmd) { jumptoend: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - //print them printKeyTable(SectorsCnt, e_sector); @@ -2367,9 +2750,15 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { SetSIMDInstr(SIMD_NEON); #endif - if (in) + if (in) { SetSIMDInstr(SIMD_NONE); + } + // santiy checks + if ((g_session.pm3_present == false) && (tests == false)) { + PrintAndLogEx(INFO, "No device connected"); + return PM3_EFAILED; + } bool known_target_key = (trg_keylen); @@ -2425,13 +2814,13 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { } } - PrintAndLogEx(INFO, "Target block no " _YELLOW_("%3d") ", target key type: " _YELLOW_("%c") ", known target key: " _YELLOW_("%02x%02x%02x%02x%02x%02x%s"), + PrintAndLogEx(INFO, "Target block no " _YELLOW_("%3d") " target key type: " _YELLOW_("%c") " known target key: " _YELLOW_("%02x%02x%02x%02x%02x%02x%s"), trg_blockno, (trg_keytype == MF_KEY_B) ? 'B' : 'A', trg_key[0], trg_key[1], trg_key[2], trg_key[3], trg_key[4], trg_key[5], known_target_key ? "" : " (not set)" ); - PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") ", Slow: " _YELLOW_("%s") ", Tests: " _YELLOW_("%d"), + PrintAndLogEx(INFO, "File action: " _YELLOW_("%s") " Slow: " _YELLOW_("%s") " Tests: " _YELLOW_("%d"), nonce_file_write ? "write" : nonce_file_read ? "read" : "none", slow ? "Yes" : "No", tests); @@ -2446,7 +2835,8 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { PrintAndLogEx(WARNING, "Button pressed. Aborted\n"); break; case PM3_ESTATIC_NONCE: - PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); break; case PM3_EFAILED: { PrintAndLogEx(FAILED, "\nFailed to recover a key..."); @@ -2644,7 +3034,8 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Settings int prng_type = PM3_EUNDEF; int isOK = 0; - // ------------------------------ + + PrintAndLogEx(NORMAL, ""); uint64_t tagT = GetHF14AMfU_Type(); if (tagT != MFU_TT_UL_ERROR) { @@ -2655,10 +3046,11 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Select card to get UID/UIDLEN/ATQA/SAK information clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); + DropField(); return PM3_ETIMEOUT; } @@ -2674,6 +3066,64 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + // try to request ATS even if tag claims not to support it. If yes => 4 + if (select_status == 2) { + uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); + card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes + if (card.ats_len > 3) { + select_status = 4; + } + } + + uint8_t ats_hist_pos = 0; + if ((card.ats_len > 3) && (card.ats[0] > 1)) { + ats_hist_pos = 2; + ats_hist_pos += (card.ats[1] & 0x10) == 0x10; + ats_hist_pos += (card.ats[1] & 0x20) == 0x20; + ats_hist_pos += (card.ats[1] & 0x40) == 0x40; + } + + version_hw_t version_hw = {0}; + // if 4b UID or NXP, try to get version + int res = hf14a_getversion_data(&card, select_status, &version_hw); + DropField(); + + bool version_hw_available = (res == PM3_SUCCESS); + + int nxptype = detect_nxp_card(card.sak + , ((card.atqa[1] << 8) + card.atqa[0]) + , select_status + , card.ats_len - ats_hist_pos + , card.ats + ats_hist_pos + , version_hw_available + , &version_hw + ); + + if ((nxptype & MTDESFIRE) == MTDESFIRE) { + PrintAndLogEx(WARNING, "MIFARE DESFire card detected. Quitting..."); + return PM3_ESOFT; + } + + bool isMifareMini = ((nxptype & MTMINI) == MTMINI); + bool isMifarePlus = ((nxptype & MTPLUS) == MTPLUS); + + if (isMifarePlus) { + PrintAndLogEx(INFO, "MIFARE Plus card detected. Using limited set of attacks"); + } + + if (isMifareMini && sector_cnt != MIFARE_MINI_MAXSECTOR) { + PrintAndLogEx(WARNING, "MIFARE Mini S20 card detected. Changing sector count to %u", MIFARE_MINI_MAXSECTOR); + sector_cnt = MIFARE_MINI_MAXSECTOR; + } + bool known_key = (in_keys_len > 5); uint8_t key[MIFARE_KEY_SIZE] = {0}; if (known_key) { @@ -2713,6 +3163,19 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { block_cnt += 8; } + // check if we can authenticate to sector + uint8_t loopupblk = mfFirstBlockOfSector(sectorno); + if (mf_check_keys(loopupblk, keytype, true, (in_keys_len / MIFARE_KEY_SIZE), in_keys, &key64) != PM3_SUCCESS) { + if (keytype < 2) { + PrintAndLogEx(WARNING, "Known key failed. Can't authenticate to block %3d key type %c", loopupblk, keytype ? 'B' : 'A'); + } else { + PrintAndLogEx(WARNING, "Known key failed. Can't authenticate to block %3d key type %02x", loopupblk, MIFARE_AUTH_KEYA + keytype); + } + known_key = false; + } else { + num_to_bytes(key64, MIFARE_KEY_SIZE, key); + } + // create/initialize key storage structure sector_t *e_sector = NULL; size_t e_sector_cnt = (sector_cnt > sectorno) ? sector_cnt : sectorno + 1; @@ -2746,7 +3209,9 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // card prng type (weak=1 / hard=0 / select/card comm error = negative value) if (has_staticnonce == NONCE_NORMAL) { + prng_type = detect_classic_prng(); + if (prng_type < 0) { PrintAndLogEx(FAILED, "\nNo tag detected or other tag communication error (%i)", prng_type); free(e_sector); @@ -2754,47 +3219,50 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { return PM3_ESOFT; } - // - has_staticnonce = detect_classic_static_encrypted_nonce(0, MF_KEY_A, g_mifare_default_key); + if (known_key) { + has_staticnonce = detect_classic_static_encrypted_nonce(loopupblk, keytype, key); + } else { + has_staticnonce = detect_classic_static_encrypted_nonce(0, MF_KEY_A, g_mifare_default_key); + } } // print parameters if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("SETTINGS") " ======================="); - PrintAndLogEx(INFO, " card sectors .. " _YELLOW_("%d"), sector_cnt); - PrintAndLogEx(INFO, " key supplied .. " _YELLOW_("%s"), known_key ? "True" : "False"); - PrintAndLogEx(INFO, " known sector .. " _YELLOW_("%d"), sectorno); - PrintAndLogEx(INFO, " keytype ....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A'); - PrintAndLogEx(INFO, " known key ..... " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key))); + PrintAndLogEx(INFO, "---- " _CYAN_("Command settings") " ------------------------------------------"); + PrintAndLogEx(INFO, "Card sectors... " _YELLOW_("%d"), sector_cnt); + PrintAndLogEx(INFO, "Key supplied... " _YELLOW_("%s"), known_key ? "yes" : "no"); + PrintAndLogEx(INFO, "Known sector... " _YELLOW_("%d"), sectorno); + PrintAndLogEx(INFO, "Key type....... " _YELLOW_("%c"), (keytype == MF_KEY_B) ? 'B' : 'A'); + PrintAndLogEx(INFO, "Known key...... " _YELLOW_("%s"), sprint_hex_inrow(key, sizeof(key))); switch (has_staticnonce) { case NONCE_STATIC: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("STATIC")); + PrintAndLogEx(INFO, "Card PRNG ..... " _YELLOW_("STATIC")); break; } case NONCE_STATIC_ENC: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("STATIC ENCRYPTED")); + PrintAndLogEx(INFO, "Card PRNG ..... " _RED_("STATIC ENCRYPTED")); break; } case NONCE_NORMAL: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("%s"), prng_type ? "WEAK" : "HARD"); + PrintAndLogEx(INFO, "Card PRNG ..... %s", (prng_type) ? "weak" : "hard"); break; } default: { - PrintAndLogEx(INFO, " card PRNG ..... " _YELLOW_("Could not determine PRNG,") " " _RED_("read failed.")); + PrintAndLogEx(INFO, "Card PRNG ..... " _YELLOW_("Could not determine PRNG") " ( " _RED_("read failed") " ) %i", has_staticnonce); break; } } - PrintAndLogEx(INFO, " dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "NONE"); - PrintAndLogEx(INFO, " legacy mode ... " _YELLOW_("%s"), legacy_mfchk ? "True" : "False"); + PrintAndLogEx(INFO, "Dictionary .... " _YELLOW_("%s"), strlen(filename) ? filename : "n/a"); + PrintAndLogEx(INFO, "Legacy mode ... %s", (legacy_mfchk) ? _YELLOW_("yes") : "no"); - PrintAndLogEx(INFO, "========================================================================"); + PrintAndLogEx(INFO, "----------------------------------------------------------------"); } // check the user supplied key if (known_key == false) { - PrintAndLogEx(WARNING, "no known key was supplied, key recovery might fail"); + PrintAndLogEx(WARNING, "No known key was supplied, key recovery might fail"); } // Start the timer @@ -2805,17 +3273,18 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (use_flashmemory) { fnlen = 0; } + int ret = mf_load_keys(&keyBlock, &key_cnt, in_keys, in_keys_len, filename, fnlen, true); if (ret != PM3_SUCCESS) { free(e_sector); return ret; } - int32_t res = PM3_SUCCESS; + res = PM3_SUCCESS; // Use the dictionary to find sector keys on the card if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START DICTIONARY ATTACK") " ======================="); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter dictionary recovery mode") " -----------------------------"); } if (legacy_mfchk) { @@ -2888,6 +3357,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { uint8_t num_found_keys = 0; for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { + if (e_sector[i].foundKey[j] != 1) { continue; } @@ -2903,13 +3373,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { known_key = true; sectorno = i; keytype = j; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ] (used for nested / hardnested attack)", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -2928,7 +3398,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { // Check if the darkside attack can be used if (prng_type && has_staticnonce != NONCE_STATIC) { if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START DARKSIDE ATTACK") " ======================="); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter darkside key recovery mode") " ---------------------------------"); } PrintAndLogEx(NORMAL, ""); @@ -2939,13 +3409,13 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { goto noValidKeyFound; } - PrintAndLogEx(SUCCESS, "Found valid key [ " _GREEN_("%012" PRIx64) " ]\n", key64); + PrintAndLogEx(SUCCESS, "Found valid key [ " _GREEN_("%012" PRIX64) " ]\n", key64); // Store the keys num_to_bytes(key64, MIFARE_KEY_SIZE, key); e_sector[sectorno].Key[keytype] = key64; e_sector[sectorno].foundKey[keytype] = 'S'; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%012" PRIx64) " ] (used for nested / hardnested attack)", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type "_GREEN_("%c") " -- found valid key [ " _GREEN_("%012" PRIX64) " ] (used for nested / hardnested attack)", sectorno, (keytype == MF_KEY_B) ? 'B' : 'A', key64 @@ -2959,7 +3429,8 @@ noValidKeyFound: } if (has_staticnonce == NONCE_STATIC_ENC) { - PrintAndLogEx(HINT, "Hint: Static encrypted nonce detected, run `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); + PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); } DropField(); @@ -2983,8 +3454,9 @@ noValidKeyFound: // If the key is already known, just skip it if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { - if (has_staticnonce == NONCE_STATIC) + if (has_staticnonce == NONCE_STATIC) { goto tryStaticnested; + } // Try the found keys are reused if (bytes_to_num(tmp_key, MIFARE_KEY_SIZE) != 0) { @@ -2993,14 +3465,15 @@ noValidKeyFound: for (int i = 0; i < sector_cnt; i++) { for (int j = MF_KEY_A; j <= MF_KEY_B; j++) { // Check if the sector key is already broken - if (e_sector[i].foundKey[j]) + if (e_sector[i].foundKey[j]) { continue; + } // Check if the key works if (mf_check_keys(mfFirstBlockOfSector(i), j, true, 1, tmp_key, &key64) == PM3_SUCCESS) { e_sector[i].Key[j] = bytes_to_num(tmp_key, MIFARE_KEY_SIZE); e_sector[i].foundKey[j] = 'R'; - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", i, (j == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -3015,7 +3488,7 @@ noValidKeyFound: if (current_key_type_i == MF_KEY_B) { if (e_sector[current_sector_i].foundKey[0] && !e_sector[current_sector_i].foundKey[1]) { if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START READ B KEY ATTACK") " ======================="); + PrintAndLogEx(INFO, "--- " _CYAN_("Enter read B key recovery mode") " -----------------------"); PrintAndLogEx(INFO, "reading B key of sector %3d with key type %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); @@ -3031,24 +3504,29 @@ noValidKeyFound: clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); - if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) goto skipReadBKey; + if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) { + goto skipReadBKey; + } - if (resp.status != PM3_SUCCESS) goto skipReadBKey; + if (resp.status != PM3_SUCCESS) { + goto skipReadBKey; + } uint8_t *data = resp.data.asBytes; key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); + if (key64) { e_sector[current_sector_i].foundKey[current_key_type_i] = 'A'; e_sector[current_sector_i].Key[current_key_type_i] = key64; num_to_bytes(key64, MIFARE_KEY_SIZE, tmp_key); - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) ); } else { if (verbose) { - PrintAndLogEx(WARNING, "unknown B key: sector: %3d key type: %c", + PrintAndLogEx(WARNING, "Unknown B key: sector %3d key type %c", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A' ); @@ -3064,14 +3542,17 @@ noValidKeyFound: skipReadBKey: if (e_sector[current_sector_i].foundKey[current_key_type_i] == 0) { - if (has_staticnonce == NONCE_STATIC) + if (has_staticnonce == NONCE_STATIC) { goto tryStaticnested; + } if (prng_type && (nested_failed == false)) { uint8_t retries = 0; + + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START NESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter nested key recovery mode") " -----------------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") " key type " _YELLOW_("%c"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } @@ -3111,13 +3592,12 @@ tryNested: break; } case PM3_ESTATIC_NONCE: { - PrintAndLogEx(ERR, "Error: Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(ERR, "Static encrypted nonce detected. Aborted\n"); + PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); e_sector[current_sector_i].Key[current_key_type_i] = 0xffffffffffff; e_sector[current_sector_i].foundKey[current_key_type_i] = false; // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sector_cnt, e_sector); PrintAndLogEx(NORMAL, ""); free(e_sector); @@ -3131,7 +3611,7 @@ tryNested: break; } default: { - PrintAndLogEx(ERR, "unknown Error.\n"); + PrintAndLogEx(ERR, "Unknown error\n"); free(e_sector); free(fptr); return isOK; @@ -3140,9 +3620,22 @@ tryNested: } else { tryHardnested: // If the nested attack fails then we try the hardnested attack + + // skip this + if (isMifarePlus) { + + // Show the results to the user + printKeyTable(sector_cnt, e_sector); + PrintAndLogEx(NORMAL, ""); + free(e_sector); + free(fptr); + return PM3_ESOFT; + } + + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START HARDNESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c, Slow %s", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter hardnested key recovery mode") " -------------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") " key type " _YELLOW_("%c") ", slow " _YELLOW_("%s"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', slow ? "Yes" : "No"); @@ -3168,8 +3661,6 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack e_sector[current_sector_i].foundKey[current_key_type_i] = false; // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); printKeyTable(sector_cnt, e_sector); PrintAndLogEx(NORMAL, ""); break; @@ -3195,9 +3686,10 @@ tryHardnested: // If the nested attack fails then we try the hardnested attack if (has_staticnonce == NONCE_STATIC) { tryStaticnested: + PrintAndLogEx(NORMAL, ""); if (verbose) { - PrintAndLogEx(INFO, "======================= " _YELLOW_("START STATIC NESTED ATTACK") " ======================="); - PrintAndLogEx(INFO, "sector no %3d, target key type %c", + PrintAndLogEx(INFO, "--- " _CYAN_("Enter static nested key recovery mode") " -----------------------"); + PrintAndLogEx(INFO, "Sector " _YELLOW_("%3d") ", key type " _YELLOW_("%c"), current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A'); } @@ -3230,7 +3722,7 @@ tryStaticnested: // Check if the key was found if (e_sector[current_sector_i].foundKey[current_key_type_i]) { - PrintAndLogEx(SUCCESS, "target sector %3u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target sector " _GREEN_("%3u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", current_sector_i, (current_key_type_i == MF_KEY_B) ? 'B' : 'A', sprint_hex_inrow(tmp_key, sizeof(tmp_key)) @@ -3244,9 +3736,6 @@ tryStaticnested: all_found: // Show the results to the user - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sector_cnt, e_sector); if (no_save == false) { @@ -3262,21 +3751,26 @@ all_found: clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_EML_MEMCLR, NULL, 0); - PrintAndLogEx(INFO, "transferring keys to simulator memory " NOLF); + PrintAndLogEx(INFO, "Transferring keys to simulator memory " NOLF); bool transfer_status = true; for (current_sector_i = 0; current_sector_i < sector_cnt; current_sector_i++) { + mf_eml_get_mem(block, current_sector_i, 1); - if (e_sector[current_sector_i].foundKey[0]) + + if (e_sector[current_sector_i].foundKey[0]) { num_to_bytes(e_sector[current_sector_i].Key[0], MIFARE_KEY_SIZE, block); - if (e_sector[current_sector_i].foundKey[1]) + } + + if (e_sector[current_sector_i].foundKey[1]) { num_to_bytes(e_sector[current_sector_i].Key[1], MIFARE_KEY_SIZE, block + 10); + } transfer_status |= mf_elm_set_mem(block, mfFirstBlockOfSector(current_sector_i) + mfNumBlocksPerSector(current_sector_i) - 1, 1); } PrintAndLogEx(NORMAL, "( %s )", (transfer_status) ? _GREEN_("ok") : _RED_("fail")); - PrintAndLogEx(INFO, "dumping card content to emulator memory (Cmd Error: 04 can occur)"); + PrintAndLogEx(INFO, "Dumping card content to emulator memory (Cmd Error: 04 can occur)"); // use ecfill trick FastDumpWithEcFill(sector_cnt); @@ -3312,6 +3806,7 @@ all_found: } else { snprintf(suffix, sizeof(suffix), "-dump"); } + fptr = GenerateFilename("hf-mf-", suffix); if (fptr == NULL) { free(dump); @@ -3329,7 +3824,7 @@ all_found: out: // Generate and show statistics t1 = msclock() - t1; - PrintAndLogEx(INFO, "autopwn execution time: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); + PrintAndLogEx(INFO, "Autopwn execution time: " _YELLOW_("%.0f") " seconds", (float)t1 / 1000.0); DropField(); free(e_sector); @@ -3440,7 +3935,7 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { return PM3_EMALLOC; } - uint32_t chunksize = keycnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : keycnt; + uint32_t chunksize = (keycnt > (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE)) ? (PM3_CMD_DATA_SIZE / MIFARE_KEY_SIZE) : keycnt; bool firstChunk = true, lastChunk = false; int i = 0; @@ -3459,30 +3954,26 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { // strategies. 1= deep first on sector 0 AB, 2= width first on all sectors for (uint8_t strategy = 1; strategy < 3; strategy++) { - PrintAndLogEx(INFO, "Running strategy %u", strategy); + PrintAndLogEx(INFO, "Running strategy " _YELLOW_("%u"), strategy); // main keychunk loop for (i = 0; i < keycnt; i += chunksize) { if (kbd_enter_pressed()) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - // field is still ON if not on last chunk clearCommandBuffer(); - SendCommandNG(CMD_FPGA_MAJOR_MODE_OFF, NULL, 0); - // TODO: we're missing these cleanups on arm side, not sure if it's important... - // set_tracing(false); - // BigBuf_free(); - // BigBuf_Clear_ext(false); + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + SendCommandNG(CMD_FPGA_MAJOR_MODE_OFF, NULL, 0); // field is still ON if not on last chunk + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); goto out; } - PrintAndLogEx(INPLACE, "Testing %5i/%5i %02.1f%%", i, keycnt, (float)i * 100 / keycnt); + uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i; // last chunk? - if (size == keycnt - i) + if (size == keycnt - i) { lastChunk = true; - + } int res = mf_check_keys_fast_ex(sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * MIFARE_KEY_SIZE), e_sector, false, false, true, singleSectorParams); if (firstChunk) firstChunk = false; @@ -3492,12 +3983,19 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { PrintAndLogEx(NORMAL, ""); goto out; } + PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("%02.1f %%") " )", i, keycnt, (float)i * 100 / keycnt); } // end chunks of keys - PrintAndLogEx(INPLACE, "Testing %5i/%5i 100.00%%", keycnt, keycnt); + + PrintAndLogEx(INPLACE, "Testing %5i/%5i ( " _YELLOW_("100 %%") " ) ", keycnt, keycnt); PrintAndLogEx(NORMAL, ""); + + // reset chunks when swapping strategies firstChunk = true; lastChunk = false; - if (blockn != -1) break; + + if (blockn != -1) { + break; + } } // end strategy } out: @@ -3523,9 +4021,6 @@ out: PrintAndLogEx(WARNING, "No keys found"); } else { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sectorsCnt, e_sector); if (use_flashmemory && found_keys == (sectorsCnt << 1)) { @@ -3744,9 +4239,6 @@ out: PrintAndLogEx(WARNING, "No keys found"); } else { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - printKeyTable(sectorsCnt, e_sector); if (transferToEml) { @@ -3911,9 +4403,9 @@ static int CmdHF14AMfChk(const char *Cmd) { uint8_t *keyBlock = NULL; uint32_t keycnt = 0; - int ret = mf_load_keys(&keyBlock, &keycnt, key, keylen, filename, fnlen, load_default); - if (ret != PM3_SUCCESS) { - return ret; + int res = mf_load_keys(&keyBlock, &keycnt, key, keylen, filename, fnlen, load_default); + if (res != PM3_SUCCESS) { + return res; } uint64_t key64 = 0; @@ -3962,7 +4454,8 @@ static int CmdHF14AMfChk(const char *Cmd) { uint32_t size = keycnt - c > max_keys ? max_keys : keycnt - c; - if (mf_check_keys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64) == PM3_SUCCESS) { + res = mf_check_keys(b, trgKeyType, clearLog, size, &keyBlock[MIFARE_KEY_SIZE * c], &key64); + if (res == PM3_SUCCESS) { e_sector[i].Key[trgKeyType] = key64; e_sector[i].foundKey[trgKeyType] = true; clearLog = false; @@ -3970,18 +4463,21 @@ static int CmdHF14AMfChk(const char *Cmd) { } clearLog = false; } - if (singleSector) + + if (singleSector) { break; + } b < 127 ? (b += 4) : (b += 16); } } + t1 = msclock() - t1; - PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + PrintAndLogEx(INFO, "\nTime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? if (keyType != MF_KEY_B) { - PrintAndLogEx(INFO, "testing to read key B..."); + PrintAndLogEx(INFO, "Testing to read key B..."); // loop sectors but block is used as to keep track of from which blocks to test int b = blockNo; @@ -4005,9 +4501,13 @@ static int CmdHF14AMfChk(const char *Cmd) { SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) continue; + if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500) == false) { + continue; + } - if (resp.status != PM3_SUCCESS) continue; + if (resp.status != PM3_SUCCESS) { + continue; + } uint8_t *data = resp.data.asBytes; key64 = bytes_to_num(data + 10, MIFARE_KEY_SIZE); @@ -4024,9 +4524,6 @@ static int CmdHF14AMfChk(const char *Cmd) { } out: - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, _GREEN_("found keys:")); - //print keys // if (singleSector) // printKeyTableEx(1, e_sector, mfSectorNum(blockNo)); @@ -4037,15 +4534,18 @@ out: // fast push mode g_conn.block_after_ACK = true; uint8_t block[MFBLOCK_SIZE] = {0x00}; + for (int i = 0; i < sectors_cnt; ++i) { uint8_t blockno = mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1; mf_eml_get_mem(block, blockno, 1); - if (e_sector[i].foundKey[0]) + if (e_sector[i].foundKey[0]) { num_to_bytes(e_sector[i].Key[0], MIFARE_KEY_SIZE, block); + } - if (e_sector[i].foundKey[1]) + if (e_sector[i].foundKey[1]) { num_to_bytes(e_sector[i].Key[1], MIFARE_KEY_SIZE, block + 10); + } if (i == sectors_cnt - 1) { // Disable fast mode on last packet @@ -4070,7 +4570,7 @@ out: // Disable fast mode and send a dummy command to make it effective g_conn.block_after_ACK = false; SendCommandNG(CMD_PING, NULL, 0); - if (!WaitForResponseTimeout(CMD_PING, NULL, 1000)) { + if (WaitForResponseTimeout(CMD_PING, NULL, 1000) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -4151,7 +4651,7 @@ static int CmdHF14AMfSim(const char *Cmd) { "hf mf sim --1k -u 11223344556677 --> MIFARE Classic 1k with 7b UID\n" "hf mf sim --1k -u 11223344 -i -x --> Perform reader attack in interactive mode\n" "hf mf sim --2k --> MIFARE 2k\n" - "hf mf sim --4k --> MIFARE 4k" + "hf mf sim --4k --> MIFARE 4k\n" "hf mf sim --1k -x -e --> Keep simulation running and populate with found reader keys\n" ); @@ -4407,7 +4907,7 @@ static int CmdHF14AMfKeyBrute(const char *Cmd) { uint64_t t1 = msclock(); if (mfKeyBrute(blockNo, keytype, key, &foundkey)) - PrintAndLogEx(SUCCESS, "found valid key: %012" PRIx64 " \n", foundkey); + PrintAndLogEx(SUCCESS, "Found valid key [ %012" PRIX64 " ]\n", foundkey); else PrintAndLogEx(FAILED, "key not found"); @@ -4801,7 +5301,7 @@ int CmdHF14AMfELoad(const char *Cmd) { // update expected blocks to match converted data. block_cnt = bytes_read / MFU_BLOCK_SIZE; - PrintAndLogEx(INFO, "MIFARE Ultralight override, will use %d blocks ( %u bytes )", block_cnt, block_cnt * block_width); + PrintAndLogEx(INFO, "MIFARE Ultralight override, will use " _YELLOW_("%d") " blocks ( " _YELLOW_("%u") " bytes )", block_cnt, block_cnt * block_width); } PrintAndLogEx(INFO, "Uploading to emulator memory"); @@ -4996,7 +5496,7 @@ static int CmdHF14AMfEView(const char *Cmd) { } PrintAndLogEx(INFO, "downloading emulator memory"); - if (!GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false)) { + if (GetFromDevice(BIG_BUF_EML, dump, bytes, 0, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); free(dump); return PM3_ETIMEOUT; @@ -5186,7 +5686,7 @@ static int CmdHF14AMfEKeyPrn(const char *Cmd) { memcpy(uid, data, sizeof(uid)); // download keys from EMUL - for (int i = 0; i < sectors_cnt; i++) { + for (uint8_t i = 0; i < sectors_cnt; i++) { if (mf_eml_get_mem(data, mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1, 1) != PM3_SUCCESS) { PrintAndLogEx(WARNING, "error get block %d", mfFirstBlockOfSector(i) + mfNumBlocksPerSector(i) - 1); @@ -6098,8 +6598,9 @@ static int CmdHf14AMfNack(const char *Cmd) { bool verbose = arg_get_lit(ctx, 1); CLIParserFree(ctx); - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "Started testing card for NACK bug. Press Enter to abort"); + } detect_classic_nackbug(verbose); return PM3_SUCCESS; @@ -6176,7 +6677,9 @@ static int CmdHF14AMfice(const char *Cmd) { clearCommandBuffer(); SendCommandMIX(CMD_HF_MIFARE_ACQ_NONCES, blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags, NULL, 0); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) goto out; + if (WaitForResponseTimeout(CMD_ACK, &resp, 3000) == false) { + goto out; + } if (resp.oldarg[0]) goto out; uint32_t items = resp.oldarg[2]; @@ -6807,12 +7310,10 @@ int CmdHFMFNDEFFormat(const char *Cmd) { // size_t alen = 0, blen = 0; uint8_t *tmpA, *tmpB; - if (loadFileBinaryKey(keyFilename, "", (void **)&tmpA, (void **)&tmpB, &alen, &blen) != PM3_SUCCESS) { + if (loadFileBinaryKey(keyFilename, "", (void **)&tmpA, (void **)&tmpB, &alen, &blen, true) != PM3_SUCCESS) { goto skipfile; } - PrintAndLogEx(INFO, "Using `" _YELLOW_("%s") "`", keyFilename); - for (int i = 0; i < numSectors; i++) { memcpy(keyA[i], tmpA + (i * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); memcpy(keyB[i], tmpB + (i * MIFARE_KEY_SIZE), MIFARE_KEY_SIZE); @@ -9247,7 +9748,9 @@ static int CmdHF14AMfValue(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - uint8_t blockno = (uint8_t)arg_get_int_def(ctx, 13, 1); + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 1, key, &keylen); uint8_t keytype = MF_KEY_A; if (arg_get_lit(ctx, 2) && arg_get_lit(ctx, 3)) { @@ -9258,22 +9761,6 @@ static int CmdHF14AMfValue(const char *Cmd) { keytype = MF_KEY_B; } - uint8_t transferkeytype = MF_KEY_A; - if (arg_get_lit(ctx, 9) && arg_get_lit(ctx, 10)) { - CLIParserFree(ctx); - PrintAndLogEx(WARNING, "Choose one single transfer key type"); - return PM3_EINVARG; - } else if (arg_get_lit(ctx, 10)) { - transferkeytype = MF_KEY_B; - } - - int keylen = 0; - uint8_t key[6] = {0}; - CLIGetHexWithReturn(ctx, 1, key, &keylen); - - int transferkeylen = 0; - uint8_t transferkey[6] = {0}; - CLIGetHexWithReturn(ctx, 8, transferkey, &transferkeylen); /* Value /Value Value BLK /BLK BLK /BLK @@ -9288,11 +9775,29 @@ static int CmdHF14AMfValue(const char *Cmd) { int64_t decval = (int64_t)arg_get_u64_def(ctx, 5, -1); // Dec by -1 is invalid, so not set. int64_t setval = (int64_t)arg_get_u64_def(ctx, 6, 0x7FFFFFFFFFFFFFFF); // out of bounds (for int32) so not set int64_t trnval = (int64_t)arg_get_u64_def(ctx, 7, -1); // block to transfer to + + int transferkeylen = 0; + uint8_t transferkey[6] = {0}; + CLIGetHexWithReturn(ctx, 8, transferkey, &transferkeylen); + + uint8_t transferkeytype = MF_KEY_A; + if (arg_get_lit(ctx, 9) && arg_get_lit(ctx, 10)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Choose one single transfer key type"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 10)) { + transferkeytype = MF_KEY_B; + } + bool getval = arg_get_lit(ctx, 11); bool resval = arg_get_lit(ctx, 12); + + uint8_t blockno = (uint8_t)arg_get_int_def(ctx, 13, 1); + int dlen = 0; uint8_t data[16] = {0}; CLIGetHexWithReturn(ctx, 14, data, &dlen); + CLIParserFree(ctx); // sanity checks @@ -9676,15 +10181,16 @@ static int CmdHF14AMfInfo(const char *Cmd) { } if (keylen != 0 && keylen != MIFARE_KEY_SIZE) { - PrintAndLogEx(ERR, "Key length must be %u bytes", MIFARE_KEY_SIZE); + PrintAndLogEx(ERR, "Key length must be %u bytes, got %d", MIFARE_KEY_SIZE, keylen); return PM3_EINVARG; } clearCommandBuffer(); - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); PacketResponseNG resp; if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(DEBUG, "iso14443a card select timeout"); + DropField(); return PM3_ETIMEOUT; } @@ -9699,6 +10205,23 @@ static int CmdHF14AMfInfo(const char *Cmd) { */ uint64_t select_status = resp.oldarg[0]; + // try to request ATS even if tag claims not to support it. If yes => 4 + if (select_status == 2) { + uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, 2, 0, rats, sizeof(rats)); + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + memcpy(card.ats, resp.data.asBytes, resp.oldarg[0]); + card.ats_len = resp.oldarg[0]; // note: ats_len includes CRC Bytes + if (card.ats_len > 3) { + select_status = 4; + } + } + if (select_status == 0) { PrintAndLogEx(DEBUG, "iso14443a card select failed"); return select_status; @@ -9714,17 +10237,74 @@ static int CmdHF14AMfInfo(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " ---------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " -----------------------------"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]); + + uint8_t ats_hist_pos = 0; + if ((card.ats_len > 3) && (card.ats[0] > 1)) { + ats_hist_pos = 2; + ats_hist_pos += (card.ats[1] & 0x10) == 0x10; + ats_hist_pos += (card.ats[1] & 0x20) == 0x20; + ats_hist_pos += (card.ats[1] & 0x40) == 0x40; + } + + version_hw_t version_hw = {0}; + // if 4b UID or NXP, try to get version + int res = hf14a_getversion_data(&card, select_status, &version_hw); + DropField(); + bool version_hw_available = (res == PM3_SUCCESS); + + int card_type = detect_nxp_card(card.sak + , ((card.atqa[1] << 8) + card.atqa[0]) + , select_status + , card.ats_len - ats_hist_pos + , card.ats + ats_hist_pos + , version_hw_available + , &version_hw + ); + + if ((card_type & MTDESFIRE) == MTDESFIRE) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "MIFARE DESFire detected"); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfdes info") "`"); + goto out; + } + + if ((card_type & MTULTRALIGHT) == MTULTRALIGHT) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "MIFARE Ultralight / NTAG detected"); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfu info") "`"); + goto out; + } + + if ((card_type & MTPLUS) == MTPLUS) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "MIFARE Plus detected"); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf mfp info") "`"); + } + + if ((card_type & MTEMV) == MTEMV) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "EMV detected"); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("emv info") "`"); + } + + if ((card_type & MTFUDAN) == MTFUDAN) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "FUDAN FM11RF005 detected"); + PrintAndLogEx(HINT, "Hint: try `" _YELLOW_("hf fudan dump") "`"); + goto out; + } + if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) { return PM3_EFAILED; } uint8_t signature[32] = {0}; - int res = read_mfc_ev1_signature(signature); + res = read_mfc_ev1_signature(signature); if (res == PM3_SUCCESS) { mfc_ev1_print_signature(card.uid, card.uidlen, signature, sizeof(signature)); } @@ -9862,7 +10442,8 @@ static int CmdHF14AMfInfo(const char *Cmd) { && card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x45", 4) == 0) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5004"); } else if (fKeyType == MF_KEY_BD) { - PrintAndLogEx(SUCCESS, _RED_("Unknown card with backdoor, please report details!")); + PrintAndLogEx(SUCCESS, _RED_("Unknown card with backdoor")); + PrintAndLogEx(INFO, "Please report details!"); } else // other cards if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\x46", 4) == 0) { @@ -9876,11 +10457,12 @@ static int CmdHF14AMfInfo(const char *Cmd) { } else if (card.sak == 0x08 && memcmp(blockdata + 5, "\x88\x04\x00\xc0", 4) == 0) { PrintAndLogEx(SUCCESS, "NXP MF1ICS5035"); } else { - PrintAndLogEx(SUCCESS, "unknown"); + PrintAndLogEx(SUCCESS, "n/a"); } - if (e_sector[1].foundKey[MF_KEY_A] && (e_sector[1].Key[MF_KEY_A] == 0x2A2C13CC242A)) { + if (keycnt > 1 && e_sector != NULL && e_sector[1].foundKey[MF_KEY_A] && (e_sector[1].Key[MF_KEY_A] == 0x2A2C13CC242A)) { PrintAndLogEx(SUCCESS, "dormakaba Saflok detected"); + ParseAndPrintSaflokData(&e_sector[0], &e_sector[1]); } } else { @@ -9952,6 +10534,7 @@ static int CmdHF14AMfInfo(const char *Cmd) { PrintAndLogEx(HINT, "Hint: Try `" _YELLOW_("script run fm11rf08s_recovery.py") "`"); } +out: if (setDeviceDebugLevel(dbg_curr, false) != PM3_SUCCESS) { return PM3_EFAILED; } @@ -10188,7 +10771,7 @@ static int CmdHF14AMfISEN(const char *Cmd) { } PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " ---------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("ISO14443-a Information") " -----------------------------"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(SUCCESS, "ATQA: " _GREEN_("%02X %02X"), card.atqa[1], card.atqa[0]); PrintAndLogEx(SUCCESS, " SAK: " _GREEN_("%02X [%" PRIu64 "]"), card.sak, resp.oldarg[0]); @@ -10200,11 +10783,11 @@ static int CmdHF14AMfISEN(const char *Cmd) { int res = detect_classic_static_encrypted_nonce_ex(blockn, keytype, key, blockn_nested, keytype_nested, key_nested, nr_nested, reset, hardreset, addread, addauth, incblk2, corruptnrar, corruptnrarparity, true); if (res == NONCE_STATIC) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes")); + PrintAndLogEx(SUCCESS, "Static nonce....... " _YELLOW_("yes")); } else if (res == NONCE_SUPERSTATIC) { - PrintAndLogEx(SUCCESS, "Static nonce......... " _YELLOW_("yes, even when nested")); + PrintAndLogEx(SUCCESS, "Static nonce....... " _YELLOW_("yes, even when nested")); } else if (res == NONCE_STATIC_ENC) { - PrintAndLogEx(SUCCESS, "Static enc nonce..... " _RED_("yes")); + PrintAndLogEx(SUCCESS, "Static enc nonce... " _RED_("yes")); } if (res == NONCE_STATIC_ENC) { @@ -10219,12 +10802,68 @@ static int CmdHF14AMfISEN(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14AMfBambuKeys(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf bambukeys", + "Generate keys for a Bambu Lab filament tag", + "hf mf bambukeys -r\n" + "hf mf bambukeys -r -d\n" + "hf mf bambukeys -u 11223344\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("u", "uid", "", "UID (4 hex bytes)"), + arg_lit0("r", NULL, "Read UID from tag"), + arg_lit0("d", NULL, "Dump keys to file"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int u_len = 0; + uint8_t uid[7] = {0x00}; + CLIGetHexWithReturn(ctx, 1, uid, &u_len); + bool use_tag = arg_get_lit(ctx, 2); + bool dump_keys = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (use_tag) { + // read uid from tag + int res = mf_read_uid(uid, &u_len, NULL); + if (res != PM3_SUCCESS) { + return res; + } + } + + if (u_len != 4) { + PrintAndLogEx(WARNING, "Key must be 4 hex bytes"); + return PM3_EINVARG; + } + + PrintAndLogEx(INFO, "-----------------------------------"); + PrintAndLogEx(INFO, " UID 4b... " _YELLOW_("%s"), sprint_hex(uid, 4)); + PrintAndLogEx(INFO, "-----------------------------------"); + + uint8_t keys[32 * 6]; + mfc_algo_bambu_all(uid, (void *)keys); + + for (int block = 0; block < 32; block++) { + PrintAndLogEx(INFO, "%d: %012" PRIX64, block, bytes_to_num(keys + (block * 6), 6)); + } + + if (dump_keys) { + char fn[FILE_PATH_SIZE] = {0}; + snprintf(fn, sizeof(fn), "hf-mf-%s-key", sprint_hex_inrow(uid, 4)); + saveFileEx(fn, ".bin", keys, 32 * 6, spDump); + } + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("recovery") " -----------------------"}, - {"info", CmdHF14AMfInfo, IfPm3Iso14443a, "mfc card Info"}, - {"isen", CmdHF14AMfISEN, IfPm3Iso14443a, "mfc card Info Static Encrypted Nonces"}, {"darkside", CmdHF14AMfDarkside, IfPm3Iso14443a, "Darkside attack"}, {"nested", CmdHF14AMfNested, IfPm3Iso14443a, "Nested attack"}, {"hardnested", CmdHF14AMfNestedHard, AlwaysAvailable, "Nested attack for hardened MIFARE Classic cards"}, @@ -10237,10 +10876,13 @@ static command_t CommandTable[] = { {"fchk", CmdHF14AMfChk_fast, IfPm3Iso14443a, "Check keys fast, targets all keys on card"}, {"decrypt", CmdHf14AMfDecryptBytes, AlwaysAvailable, "Decrypt Crypto1 data from sniff or trace"}, {"supercard", CmdHf14AMfSuperCard, IfPm3Iso14443a, "Extract info from a `super card`"}, + {"bambukeys", CmdHF14AMfBambuKeys, AlwaysAvailable, "Generate key table for Bambu Lab filament tag"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("operations") " -----------------------"}, {"auth4", CmdHF14AMfAuth4, IfPm3Iso14443a, "ISO14443-4 AES authentication"}, {"acl", CmdHF14AMfAcl, AlwaysAvailable, "Decode and print MIFARE Classic access rights bytes"}, {"dump", CmdHF14AMfDump, IfPm3Iso14443a, "Dump MIFARE Classic tag to binary file"}, + {"info", CmdHF14AMfInfo, IfPm3Iso14443a, "Tag information"}, + {"isen", CmdHF14AMfISEN, IfPm3Iso14443a, "Information Static Encrypted Nonces"}, {"mad", CmdHF14AMfMAD, AlwaysAvailable, "Checks and prints MAD"}, {"personalize", CmdHFMFPersonalize, IfPm3Iso14443a, "Personalize UID (MIFARE Classic EV1 only)"}, {"rdbl", CmdHF14AMfRdBl, IfPm3Iso14443a, "Read MIFARE Classic block"}, diff --git a/client/src/cmdhfmf.h b/client/src/cmdhfmf.h index f39d63ff9..ff1695341 100644 --- a/client/src/cmdhfmf.h +++ b/client/src/cmdhfmf.h @@ -35,9 +35,13 @@ void printKeyTable(size_t sectorscnt, sector_t *e_sector); void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector); // void printKeyTableEx(size_t sectorscnt, sector_t *e_sector, uint8_t start_sector, bool singel_sector); + bool mfc_value(const uint8_t *d, int32_t *val); void mf_print_sector_hdr(uint8_t sector); void mf_print_block_one(uint8_t blockno, uint8_t *d, bool verbose); +int mf_print_keys(uint16_t n, uint8_t *d); +void mf_print_blocks(uint16_t n, uint8_t *d, bool verbose); + int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len); #endif diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index 4e4c19a57..341ced612 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -89,15 +89,6 @@ typedef struct mfdes_data { uint8_t *data; } PACKED mfdes_data_t; -typedef struct mfdes_info_res { - uint8_t isOK; - uint8_t uid[7]; - uint8_t uidlen; - uint8_t versionHW[7]; - uint8_t versionSW[7]; - uint8_t details[14]; -} PACKED mfdes_info_res_t; - typedef struct mfdes_value { uint8_t fileno; //01 uint8_t value[16]; @@ -136,21 +127,6 @@ typedef enum { MFDES_VALUE_FILE } MFDES_FILE_TYPE_T; -typedef enum { - DESFIRE_UNKNOWN = 0, - DESFIRE_MF3ICD40, - DESFIRE_EV1, - DESFIRE_EV2, - DESFIRE_EV2_XL, - DESFIRE_EV3, - DESFIRE_LIGHT, - PLUS_EV1, - PLUS_EV2, - NTAG413DNA, - NTAG424, - DUOX, -} nxp_cardtype_t; - typedef enum { DESFIRE_UNKNOWN_PROD = 0, DESFIRE_PHYSICAL, @@ -332,7 +308,7 @@ static const char *getAidCommentStr(uint32_t aid) { return ""; } -static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { +nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { // DESFire MF3ICD40 if (type == 0x01 && major == 0x00 && minor == 0x02) @@ -385,7 +361,7 @@ static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { if (type == 0x04 && major == 0x30 && minor == 0x00) return NTAG424; - return DESFIRE_UNKNOWN; + return NXP_UNKNOWN; } // ref: https://www.nxp.com/docs/en/application-note/AN12343.pdf p7 @@ -423,7 +399,7 @@ static const char *getProductTypeStr(const uint8_t *versionhw) { return "UNKNOWN PROD"; } -static int mfdes_get_info(mfdes_info_res_t *info) { +int mfdes_get_info(mfdes_info_res_t *info) { PacketResponseNG resp; SendCommandNG(CMD_HF_DESFIRE_INFO, NULL, 0); @@ -710,7 +686,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { return PM3_SUCCESS; } - if (cardtype == DESFIRE_UNKNOWN) { + if (cardtype == NXP_UNKNOWN) { PrintAndLogEx(INFO, "HW Version.. %s", sprint_hex_inrow(info.versionHW, sizeof(info.versionHW))); PrintAndLogEx(INFO, "SW Version.. %s", sprint_hex_inrow(info.versionSW, sizeof(info.versionSW))); PrintAndLogEx(INFO, "Version data identification failed. Report to Iceman!"); @@ -817,7 +793,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { if (aidbuflen > 2) { - uint8_t j = aidbuflen / 3; + uint8_t j = (aidbuflen / 3); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "--- " _CYAN_("AID list") " ( " _YELLOW_("%u") " found )", j); @@ -826,6 +802,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { uint32_t aid = DesfireAIDByteToUint(&aidbuf[i]); PrintAndLogEx(SUCCESS, _YELLOW_("%06X") ", %s", aid, getAidCommentStr(aid)); } + PrintAndLogEx(NORMAL, ""); } DesfireFillPICCInfo(&dctx, &PICCInfo, true); @@ -1082,7 +1059,7 @@ static int AuthCheckDesfire(DesfireContext_t *dctx, DesfireSetKeyNoClear(dctx, keyno, T_3DES, aeskeyList[curkey]); res = DesfireAuthenticate(dctx, secureChannel, false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 2TDEA Key %02u : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16)); + PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 2TDEA Key %02u... " _GREEN_("%s"), curaid, keyno, sprint_hex_inrow(aeskeyList[curkey], 16)); foundKeys[1][keyno][0] = 0x01; *result = true; memcpy(&foundKeys[1][keyno][1], aeskeyList[curkey], 16); @@ -1114,7 +1091,7 @@ static int AuthCheckDesfire(DesfireContext_t *dctx, DesfireSetKeyNoClear(dctx, keyno, T_AES, aeskeyList[curkey]); res = DesfireAuthenticate(dctx, secureChannel, false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "AID 0x%06X, Found AES Key %02u : " _GREEN_("%s"), curaid, keyno, sprint_hex(aeskeyList[curkey], 16)); + PrintAndLogEx(SUCCESS, "AID 0x%06X, Found AES Key %02u... " _GREEN_("%s"), curaid, keyno, sprint_hex_inrow(aeskeyList[curkey], 16)); foundKeys[2][keyno][0] = 0x01; *result = true; memcpy(&foundKeys[2][keyno][1], aeskeyList[curkey], 16); @@ -1146,7 +1123,7 @@ static int AuthCheckDesfire(DesfireContext_t *dctx, DesfireSetKeyNoClear(dctx, keyno, T_3K3DES, k3kkeyList[curkey]); res = DesfireAuthenticate(dctx, secureChannel, false); if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 3TDEA Key %02u : " _GREEN_("%s"), curaid, keyno, sprint_hex(k3kkeyList[curkey], 24)); + PrintAndLogEx(SUCCESS, "AID 0x%06X, Found 3TDEA Key %02u... " _GREEN_("%s"), curaid, keyno, sprint_hex_inrow(k3kkeyList[curkey], 24)); foundKeys[3][keyno][0] = 0x01; *result = true; memcpy(&foundKeys[3][keyno][1], k3kkeyList[curkey], 16); @@ -1186,8 +1163,8 @@ static int CmdHF14aDesChk(const char *Cmd) { CLIParserInit(&ctx, "hf mfdes chk", "Checks keys with MIFARE DESFire card.", "hf mfdes chk --aid 123456 -k 000102030405060708090a0b0c0d0e0f -> check key on aid 0x123456\n" - "hf mfdes chk -d mfdes_default_keys -> check keys against all existing aid on card\n" - "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys against aid 0x123456\n" + "hf mfdes chk -f mfdes_default_keys -> check keys against all existing aid on card\n" + "hf mfdes chk -f mfdes_default_keys --aid 123456 -> check keys against aid 0x123456\n" "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to `keys.json`\n" "hf mfdes chk --aid 123456 --pattern2b --startp2b FA00 -> check all 2-byte keys pattern on aid 0x123456. Start from key FA00FA00...FA00"); @@ -1195,7 +1172,7 @@ static int CmdHF14aDesChk(const char *Cmd) { arg_param_begin, arg_str0(NULL, "aid", "", "Use specific AID (3 hex bytes, big endian)"), arg_str0("k", "key", "", "Key for checking (HEX 16 bytes)"), - arg_str0("d", "dict", "", "Dictionary file with keys"), + arg_str0("f", "file", "", "Filename of dictionary"), arg_lit0(NULL, "pattern1b", "Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)"), arg_lit0(NULL, "pattern2b", "Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)"), arg_str0(NULL, "startp2b", "", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"), @@ -1208,12 +1185,12 @@ static int CmdHF14aDesChk(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, false); - bool APDULogging = arg_get_lit(ctx, 11); - int aidlength = 0; uint8_t aid[3] = {0}; CLIGetHexWithReturn(ctx, 1, aid, &aidlength); + swap24(aid); + uint8_t vkey[16] = {0}; int vkeylen = 0; CLIGetHexWithReturn(ctx, 2, vkey, &vkeylen); @@ -1292,17 +1269,26 @@ static int CmdHF14aDesChk(const char *Cmd) { int kdfInputLen = 0; CLIGetHexWithReturn(ctx, 10, kdfInput, &kdfInputLen); + bool APDULogging = arg_get_lit(ctx, 11); + CLIParserFree(ctx); SetAPDULogging(APDULogging); // 1-byte pattern search mode if (pattern1b) { - for (uint32_t i = 0; i < 0x100; i++) + + for (uint32_t i = 0; i < 0x100; i++) { memset(aeskeyList[i], i, 16); - for (uint32_t i = 0; i < 0x100; i++) + } + + for (uint32_t i = 0; i < 0x100; i++) { memset(deskeyList[i], i, 8); - for (uint32_t i = 0; i < 0x100; i++) + } + + for (uint32_t i = 0; i < 0x100; i++) { memset(k3kkeyList[i], i, 24); + } + aeskeyListLen = 0x100; deskeyListLen = 0x100; k3kkeyListLen = 0x100; @@ -1318,18 +1304,21 @@ static int CmdHF14aDesChk(const char *Cmd) { if (dict_filenamelen) { res = loadFileDICTIONARYEx((char *)dict_filename, deskeyList, sizeof(deskeyList), NULL, 8, &deskeyListLen, 0, &endFilePosition, true); - if (res == PM3_SUCCESS && endFilePosition) + if (res == PM3_SUCCESS && endFilePosition) { PrintAndLogEx(SUCCESS, "First part of des dictionary successfully loaded."); + } endFilePosition = 0; res = loadFileDICTIONARYEx((char *)dict_filename, aeskeyList, sizeof(aeskeyList), NULL, 16, &aeskeyListLen, 0, &endFilePosition, true); - if (res == PM3_SUCCESS && endFilePosition) + if (res == PM3_SUCCESS && endFilePosition) { PrintAndLogEx(SUCCESS, "First part of aes dictionary successfully loaded."); + } endFilePosition = 0; res = loadFileDICTIONARYEx((char *)dict_filename, k3kkeyList, sizeof(k3kkeyList), NULL, 24, &k3kkeyListLen, 0, &endFilePosition, true); - if (res == PM3_SUCCESS && endFilePosition) + if (res == PM3_SUCCESS && endFilePosition) { PrintAndLogEx(SUCCESS, "First part of k3kdes dictionary successfully loaded."); + } endFilePosition = 0; } @@ -1351,8 +1340,9 @@ static int CmdHF14aDesChk(const char *Cmd) { PrintAndLogEx(INFO, "Loaded " _YELLOW_("%"PRIu32) " k3kdes keys", k3kkeyListLen); } - if (verbose == false) + if (verbose == false) { PrintAndLogEx(INFO, "Search keys:"); + } bool result = false; uint8_t app_ids[78] = {0}; @@ -1507,7 +1497,7 @@ static int CmdHF14aDesDetect(const char *Cmd) { "Detect key type and tries to find one from the list.", "hf mfdes detect -> detect key 0 from PICC level\n" "hf mfdes detect --schann d40 -> detect key 0 from PICC level via secure channel D40\n" - "hf mfdes detect --dict mfdes_default_keys -> detect key 0 from PICC level with help of the standard dictionary\n" + "hf mfdes detect -f mfdes_default_keys -> detect key 0 from PICC level with help of the standard dictionary\n" "hf mfdes detect --aid 123456 -n 2 --save -> detect key 2 from app 123456 and if succeed - save params to defaults (`default` command)\n" "hf mfdes detect --isoid df01 --save -> detect key 0 and save to defaults with card in the LRP mode"); @@ -1525,7 +1515,7 @@ static int CmdHF14aDesDetect(const char *Cmd) { arg_str0(NULL, "schann", "", "Secure channel"), arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), arg_str0(NULL, "isoid", "", "Application ISO ID (ISO DF ID) (2 hex bytes, big endian)."), - arg_str0(NULL, "dict", "", "Dictionary file name with keys"), + arg_str0("f", "file", "", "Filename of dictionary"), arg_lit0(NULL, "save", "Save found key and parameters to defaults"), arg_param_end }; @@ -1570,28 +1560,38 @@ static int CmdHF14aDesDetect(const char *Cmd) { uint8_t data[250] = {0}; size_t datalen = 0; + res = DesfireGetKeySettings(&dctx, data, &datalen); if (res == PM3_SUCCESS && datalen >= 2) { + uint8_t num_keys = data[1]; + switch (num_keys >> 6) { - case 0: + case 0: { keytypes[T_DES] = true; keytypes[T_3DES] = true; break; - case 1: + } + case 1: { keytypes[T_3K3DES] = true; break; - case 2: + } + case 2: { keytypes[T_AES] = true; break; - default: + } + default: { break; + } } + } else { // if fail - check auth commands AuthCommandsChk_t authCmdCheck = {0}; DesfireCheckAuthCommands(selectway, id, NULL, 0, &authCmdCheck); + if (authCmdCheck.checked) { + if (authCmdCheck.auth) { keytypes[T_DES] = true; keytypes[T_3DES] = true; @@ -1600,14 +1600,17 @@ static int CmdHF14aDesDetect(const char *Cmd) { keytypes[T_3K3DES] = true; } } + if (authCmdCheck.authAES || authCmdCheck.authEV2) { keytypes[T_AES] = true; } + if (authCmdCheck.authLRP) { keytypes[T_AES] = true; uselrp = true; securechann = DACLRP; } + } else { // if nothing helps - we check DES only keytypes[T_DES] = true; @@ -1622,10 +1625,13 @@ static int CmdHF14aDesDetect(const char *Cmd) { } if (verbose) { - if (DesfireMFSelected(selectway, id)) + + if (DesfireMFSelected(selectway, id)) { PrintAndLogEx(INFO, "Check PICC key num: %d (0x%02x)", dctx.keyNum, dctx.keyNum); - else + } else { PrintAndLogEx(INFO, "Check: %s key num: %d (0x%02x)", DesfireWayIDStr(selectway, id), dctx.keyNum, dctx.keyNum); + } + PrintAndLogEx(INFO, "keys: DES: %s 2TDEA: %s 3TDEA: %s AES: %s LRP: %s", keytypes[T_DES] ? _GREEN_("YES") : _RED_("NO"), keytypes[T_3DES] ? _GREEN_("YES") : _RED_("NO"), @@ -1639,43 +1645,61 @@ static int CmdHF14aDesDetect(const char *Cmd) { bool found = false; size_t errcount = 0; for (uint8_t ktype = T_DES; ktype <= T_AES; ktype++) { - if (!keytypes[ktype]) + + if (keytypes[ktype] == false) { continue; + } + dctx.keyType = ktype; - if (verbose) + + if (verbose) { PrintAndLogEx(INFO, "Scan key type: %s", CLIGetOptionListStr(DesfireAlgoOpts, dctx.keyType)); + } if (dict_filenamelen == 0) { // keys from mifaredefault.h for (int i = 0; i < g_mifare_plus_default_keys_len; i++) { + uint8_t key[DESFIRE_MAX_KEY_SIZE] = {0}; - if (hex_to_bytes(g_mifare_plus_default_keys[i], key, 16) != 16) + if (hex_to_bytes(g_mifare_plus_default_keys[i], key, 16) != 16) { continue; - if (ktype == T_3K3DES) + } + + if (ktype == T_3K3DES) { memcpy(&key[16], key, 8); + } res = DesfireAuthCheck(&dctx, selectway, id, securechann, key); if (res == PM3_SUCCESS) { found = true; break; // all the params already in the dctx } + if (res == -10) { - if (verbose) + + if (verbose) { PrintAndLogEx(ERR, "Can't select AID. There is no connection with card."); + } found = false; break; // we can't select app after invalid 1st auth stages } + if (res == -11) { + if (errcount > 10) { - if (verbose) + if (verbose) { PrintAndLogEx(ERR, "Too much errors (%zu) from card", errcount); + } break; } errcount++; - } else + + } else { errcount = 0; + } } + } else { // keys from file uint8_t keyList[MAX_KEYS_LIST_LEN * MAX_KEY_LEN] = {0}; @@ -1683,49 +1707,65 @@ static int CmdHF14aDesDetect(const char *Cmd) { size_t keylen = desfire_get_key_length(dctx.keyType); size_t endFilePosition = 0; - while (!found) { + while (found == false) { + res = loadFileDICTIONARYEx((char *)dict_filename, keyList, sizeof(keyList), NULL, keylen, &keyListLen, endFilePosition, &endFilePosition, verbose); - if (res != 1 && res != PM3_SUCCESS) + if (res != 1 && res != PM3_SUCCESS) { break; + } for (int i = 0; i < keyListLen; i++) { + res = DesfireAuthCheck(&dctx, selectway, id, securechann, &keyList[i * keylen]); if (res == PM3_SUCCESS) { found = true; break; // all the params already in the dctx } + if (res == -10) { - if (verbose) + if (verbose) { PrintAndLogEx(ERR, "Can't select AID. There is no connection with card."); + } found = false; break; // we can't select app after invalid 1st auth stages } + if (res == -11) { + if (errcount > 10) { - if (verbose) + if (verbose) { PrintAndLogEx(ERR, "Too much errors (%zu) from card", errcount); + } break; } errcount++; - } else + + } else { errcount = 0; + } + } - if (endFilePosition == 0) + if (endFilePosition == 0) { break; + } } } - if (found) + + if (found) { break; + } } if (found) { - if (DesfireMFSelected(selectway, id)) + + if (DesfireMFSelected(selectway, id)) { PrintAndLogEx(INFO, _GREEN_("Found") " key num: %d (0x%02x)", dctx.keyNum, dctx.keyNum); - else + } else { PrintAndLogEx(INFO, "Found key for: %s key num: %d (0x%02x)", DesfireWayIDStr(selectway, id), dctx.keyNum, dctx.keyNum); + } PrintAndLogEx(INFO, "channel " _GREEN_("%s") " key " _GREEN_("%s") " [%d]: " _GREEN_("%s"), CLIGetOptionListStr(DesfireSecureChannelOpts, securechann), @@ -1740,6 +1780,7 @@ static int CmdHF14aDesDetect(const char *Cmd) { DropField(); if (found && save) { + defaultKeyNum = dctx.keyNum; defaultAlgoId = dctx.keyType; memcpy(defaultKey, dctx.key, DESFIRE_MAX_KEY_SIZE); @@ -1750,17 +1791,16 @@ static int CmdHF14aDesDetect(const char *Cmd) { defaultCommSet = dctx.cmdSet; PrintAndLogEx(INFO, "-----------" _CYAN_("Default parameters") "---------------------------------"); - - PrintAndLogEx(INFO, "Key Num : %d", defaultKeyNum); - PrintAndLogEx(INFO, "Algo : %s", CLIGetOptionListStr(DesfireAlgoOpts, defaultAlgoId)); - PrintAndLogEx(INFO, "Key : %s", sprint_hex(defaultKey, desfire_get_key_length(defaultAlgoId))); - PrintAndLogEx(INFO, "KDF algo : %s", CLIGetOptionListStr(DesfireKDFAlgoOpts, defaultKdfAlgo)); - PrintAndLogEx(INFO, "KDF input : [%d] %s", defaultKdfInputLen, sprint_hex(defaultKdfInput, defaultKdfInputLen)); - PrintAndLogEx(INFO, "Secure chan : %s", CLIGetOptionListStr(DesfireSecureChannelOpts, defaultSecureChannel)); - PrintAndLogEx(INFO, "Command set : %s", CLIGetOptionListStr(DesfireCommandSetOpts, defaultCommSet)); - PrintAndLogEx(INFO, _GREEN_("Saved")); + PrintAndLogEx(INFO, "Key Num....... %d", defaultKeyNum); + PrintAndLogEx(INFO, "Algo.......... %s", CLIGetOptionListStr(DesfireAlgoOpts, defaultAlgoId)); + PrintAndLogEx(INFO, "Key........... %s", sprint_hex(defaultKey, desfire_get_key_length(defaultAlgoId))); + PrintAndLogEx(INFO, "KDF algo...... %s", CLIGetOptionListStr(DesfireKDFAlgoOpts, defaultKdfAlgo)); + PrintAndLogEx(INFO, "KDF input..... [%d] %s", defaultKdfInputLen, sprint_hex(defaultKdfInput, defaultKdfInputLen)); + PrintAndLogEx(INFO, "Secure chan... %s", CLIGetOptionListStr(DesfireSecureChannelOpts, defaultSecureChannel)); + PrintAndLogEx(INFO, "Command set... %s", CLIGetOptionListStr(DesfireCommandSetOpts, defaultCommSet)); + PrintAndLogEx(INFO, "Parameters saved to in-memory ( %s )", _GREEN_("ok")); } - + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -2133,6 +2173,9 @@ static int CmdHF14ADesBruteApps(const char *Cmd) { startAid[1] = 0x00; startAid[2] = 0x0F; } + + reverse_array(startAid, 3); + reverse_array(endAid, 3); uint32_t idStart = DesfireAIDByteToUint(startAid); uint32_t idEnd = DesfireAIDByteToUint(endAid); if (idStart > idEnd) { @@ -3282,11 +3325,15 @@ static int CmdHF14ADesGetAIDs(const char *Cmd) { } if (buflen >= 3) { - PrintAndLogEx(INFO, "---- " _CYAN_("AID list") " ----"); + + uint8_t j = (buflen / 3); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("AID list") " ( " _YELLOW_("%u") " found )", j); for (int i = 0; i < buflen; i += 3) { uint32_t aid = DesfireAIDByteToUint(&buf[i]); PrintAndLogEx(SUCCESS, _YELLOW_("%06X") " %s", aid, getAidCommentStr(aid)); } + PrintAndLogEx(NORMAL, ""); } else { PrintAndLogEx(INFO, "There is no applications on the card"); } diff --git a/client/src/cmdhfmfdes.h b/client/src/cmdhfmfdes.h index 80b3e8c93..0eddcf6cf 100644 --- a/client/src/cmdhfmfdes.h +++ b/client/src/cmdhfmfdes.h @@ -20,6 +20,44 @@ #include "common.h" +// Ev1 card limits +#define MAX_NUM_KEYS 0x0F +#define MAX_APPLICATION_COUNT 28 +#define MAX_FILE_COUNT 32 +#define MAX_FRAME_SIZE 60 +#define FRAME_PAYLOAD_SIZE (MAX_FRAME_SIZE - 5) + +// Ev2 card limits +// Ev3 card limits +// Light card limits +// Light Ev1 card limits + +#define NOT_YET_AUTHENTICATED 0xFF + +typedef enum { + NXP_UNKNOWN = 0, + DESFIRE_MF3ICD40, + DESFIRE_EV1, + DESFIRE_EV2, + DESFIRE_EV2_XL, + DESFIRE_EV3, + DESFIRE_LIGHT, + PLUS_EV1, + PLUS_EV2, + NTAG413DNA, + NTAG424, + DUOX, +} nxp_cardtype_t; + +typedef struct { + uint8_t isOK; + uint8_t uid[7]; + uint8_t uidlen; + uint8_t versionHW[7]; + uint8_t versionSW[7]; + uint8_t details[14]; +} PACKED mfdes_info_res_t; + int CmdHFMFDes(const char *Cmd); /* @@ -29,24 +67,7 @@ int getKeySettings(uint8_t *aid); */ int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, size_t signature_len); - -// Ev1 card limits -#define MAX_NUM_KEYS 0x0F -#define MAX_APPLICATION_COUNT 28 -#define MAX_FILE_COUNT 32 -#define MAX_FRAME_SIZE 60 -#define FRAME_PAYLOAD_SIZE (MAX_FRAME_SIZE - 5) - -// Ev2 card limits - -// Ev3 card limits - -// Light card limits - -// Light Ev1 card limits - -#define NOT_YET_AUTHENTICATED 0xFF - - +nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor); +int mfdes_get_info(mfdes_info_res_t *info); #endif diff --git a/client/src/cmdhfmfhard.c b/client/src/cmdhfmfhard.c index 8381a4c8d..885162437 100644 --- a/client/src/cmdhfmfhard.c +++ b/client/src/cmdhfmfhard.c @@ -127,7 +127,6 @@ static void print_progress_header(void) { get_SIMD_instruction_set(instr_set); snprintf(progress_text, sizeof(progress_text), "Start using " _YELLOW_("%d") " threads and " _YELLOW_("%s") " SIMD core", num_CPUs(), instr_set); - PrintAndLogEx(INFO, "Hardnested attack starting..."); PrintAndLogEx(INFO, "---------+---------+---------------------------------------------------------+-----------------+-------"); PrintAndLogEx(INFO, " | | | Expected to brute force"); PrintAndLogEx(INFO, " Time | #nonces | Activity | #states | time "); @@ -136,12 +135,16 @@ static void print_progress_header(void) { } void hardnested_print_progress(uint32_t nonces, const char *activity, float brute_force, uint64_t min_diff_print_time) { + static uint64_t last_print_time = 0; + if (msclock() - last_print_time >= min_diff_print_time) { + last_print_time = msclock(); uint64_t total_time = msclock() - start_time; float brute_force_time = brute_force / brute_force_per_second; char brute_force_time_string[20]; + if (brute_force_time < 90) { snprintf(brute_force_time_string, sizeof(brute_force_time_string), "%2.0fs", brute_force_time); } else if (brute_force_time < 60 * 90) { @@ -151,7 +154,24 @@ void hardnested_print_progress(uint32_t nonces, const char *activity, float brut } else { snprintf(brute_force_time_string, sizeof(brute_force_time_string), "%2.0fd", brute_force_time / (60 * 60 * 24)); } - PrintAndLogEx(INFO, " %7.0f | %7u | %-55s | %15.0f | %5s", (float)total_time / 1000.0, nonces, activity, brute_force, brute_force_time_string); + + if (strlen(activity) > 67) { + PrintAndLogEx(INFO, " %7.0f | %7u | %-82s | %15.0f | %5s" + , (float)total_time / 1000.0 + , nonces + , activity + , brute_force + , brute_force_time_string + ); + } else { + PrintAndLogEx(INFO, " %7.0f | %7u | %-55s | %15.0f | %5s" + , (float)total_time / 1000.0 + , nonces + , activity + , brute_force + , brute_force_time_string + ); + } } } @@ -486,8 +506,14 @@ static void init_bitflip_bitarrays(void) { effective_bitflip[odd_even][num_effective_bitflips[odd_even]] = 0x400; // EndOfList marker } { - char progress_text[80]; - snprintf(progress_text, sizeof(progress_text), "Loaded %u RAW / %u LZ4 / %u BZ2 in %"PRIu64" ms", nraw, nlz4, nbz2, msclock() - init_bitflip_bitarrays_starttime); + char progress_text[100]; + memset(progress_text, 0, sizeof(progress_text)); + snprintf(progress_text, sizeof(progress_text), "Loaded " _YELLOW_("%u") " RAW / " _YELLOW_("%u") " LZ4 / " _YELLOW_("%u") " BZ2 in %4"PRIu64" ms" + , nraw + , nlz4 + , nbz2 + , msclock() - init_bitflip_bitarrays_starttime + ); hardnested_print_progress(0, progress_text, (float)(1LL << 47), 0); } uint16_t i = 0; @@ -2481,8 +2507,10 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_candidates_memory(candidates); candidates = NULL; } else { + pre_XOR_nonces(); prepare_bf_test_nonces(nonces, best_first_bytes[0]); + for (uint8_t j = 0; j < NUM_SUMS && !key_found; j++) { float expected_brute_force = nonces[best_first_bytes[0]].expected_num_brute_force; snprintf(progress_text, sizeof(progress_text), "(%d. guess: Sum(a8) = %" PRIu16 ")", j + 1, sums[nonces[best_first_bytes[0]].sum_a8_guess[j].sum_a8_idx]); @@ -2544,7 +2572,9 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc int res; if (nonce_file_read) { // use pre-acquired data from file nonces.bin + res = read_nonce_file(filename); + if (res != PM3_SUCCESS) { free_bitflip_bitarrays(); free_nonces_memory(); @@ -2554,12 +2584,16 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc free_part_sum_bitarrays(); return res; } + hardnested_stage = CHECK_1ST_BYTES | CHECK_2ND_BYTES; update_nonce_data(false); float brute_force_depth; shrink_key_space(&brute_force_depth); + } else { // acquire nonces. + res = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow, filename); + if (res != PM3_SUCCESS) { free_bitflip_bitarrays(); free_nonces_memory(); diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 8437d72da..6433bcf2e 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -17,6 +17,7 @@ //----------------------------------------------------------------------------- #include "cmdhfmfp.h" +#include "cmdhfmfdes.h" #include #include "cmdparser.h" // command_t #include "commonutil.h" // ARRAYLEN @@ -41,6 +42,7 @@ static const uint8_t mfp_default_key[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, static uint16_t mfp_card_adresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0x9006, 0x9007, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001}; #define MFP_KEY_FILE_SIZE 14 + (2 * 64 * (AES_KEY_LEN + 1)) +#define MFP_CHK_KEY_TRIES (2) static int CmdHelp(const char *Cmd); @@ -141,54 +143,6 @@ static char *getTypeStr(uint8_t type) { return buf; } -static nxp_cardtype_t getCardType(uint8_t type, uint8_t major, uint8_t minor) { - - // DESFire MF3ICD40 - if (type == 0x01 && major == 0x00 && minor == 0x02) - return DESFIRE_MF3ICD40; - - // DESFire EV1 - if (type == 0x01 && major == 0x01 && minor == 0x00) - return DESFIRE_EV1; - - // DESFire EV2 - if (type == 0x01 && major == 0x12 && minor == 0x00) - return DESFIRE_EV2; - - if (type == 0x01 && major == 0x22 && minor == 0x00) - return DESFIRE_EV2_XL; - - // DESFire EV3 - if (type == 0x01 && major == 0x33 && minor == 0x00) - return DESFIRE_EV3; - - // DESFire Light - if (type == 0x08 && major == 0x30 && minor == 0x00) - return DESFIRE_LIGHT; - - // combo card DESFire / EMV - if (type == 0x81 && major == 0x42 && minor == 0x00) - return DESFIRE_EV2; - - // Plus EV1 - if (type == 0x02 && major == 0x11 && minor == 0x00) - return PLUS_EV1; - - // Plus Ev2 - if (type == 0x02 && major == 0x22 && minor == 0x00) - return PLUS_EV2; - - // NTAG 413 DNA - if (type == 0x04 && major == 0x10 && minor == 0x00) - return NTAG413DNA; - - // NTAG 424 - if (type == 0x04 && major == 0x30 && minor == 0x00) - return NTAG424; - - return MFP_UNKNOWN; -} - // --- GET SIGNATURE static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) { int index = originality_check_verify(uid, uidlen, signature, signature_len, PK_MFP); @@ -259,6 +213,153 @@ static int get_plus_version(uint8_t *version, int *version_len) { return retval; } +static int mfp_read_card_id(iso14a_card_select_t *card, int *nxptype) { + + if (card == NULL) { + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(DEBUG, "iso14443a card select failed"); + DropField(); + return PM3_ERFTRANS; + } + + memcpy(card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + + if (nxptype) { + uint64_t select_status = resp.oldarg[0]; + + uint8_t ats_hist_pos = 0; + if ((card->ats_len > 3) && (card->ats[0] > 1)) { + ats_hist_pos = 2; + ats_hist_pos += (card->ats[1] & 0x10) == 0x10; + ats_hist_pos += (card->ats[1] & 0x20) == 0x20; + ats_hist_pos += (card->ats[1] & 0x40) == 0x40; + } + + version_hw_t version_hw = {0}; + // if 4b UID or NXP, try to get version + int res = hf14a_getversion_data(card, select_status, &version_hw); + DropField(); + + bool version_hw_available = (res == PM3_SUCCESS); + + *nxptype = detect_nxp_card(card->sak + , ((card->atqa[1] << 8) + card->atqa[0]) + , select_status + , card->ats_len - ats_hist_pos + , card->ats + ats_hist_pos + , version_hw_available + , &version_hw + ); + } + return PM3_SUCCESS; +} + +static int mfp_load_keygen_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *uid) { + + // Handle dymanica keys + + return PM3_SUCCESS; +} + +static int mfp_load_keys(uint8_t **pkeyBlock, uint32_t *pkeycnt, uint8_t *userkey, int userkeylen, const char *filename, int fnlen, uint8_t *uid, bool load_default) { + // Handle Keys + *pkeycnt = 0; + *pkeyBlock = NULL; + uint8_t *p; + + // Handle KDF uid based keys + if (uid) { + mfp_load_keygen_keys(pkeyBlock, pkeycnt, uid); + } + + // Handle user supplied key + // (it considers *pkeycnt and *pkeyBlock as possibly non-null so logic can be easily reordered) + if (userkeylen >= AES_KEY_LEN) { + int numKeys = userkeylen / AES_KEY_LEN; + p = realloc(*pkeyBlock, (*pkeycnt + numKeys) * AES_KEY_LEN); + if (p == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + + memcpy(*pkeyBlock, userkey, numKeys * AES_KEY_LEN); + + for (int i = 0; i < numKeys; i++) { + PrintAndLogEx(DEBUG, _YELLOW_("%2d") " - %s", i, sprint_hex_inrow(*pkeyBlock + i * AES_KEY_LEN, AES_KEY_LEN)); + } + *pkeycnt += numKeys; + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " user keys", numKeys); + } + + if (load_default) { + // Handle default keys + p = realloc(*pkeyBlock, (*pkeycnt + g_mifare_plus_default_keys_len) * AES_KEY_LEN); + if (p == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + + // Copy default keys to list + size_t cnt = 0; + for (cnt = 0; cnt < g_mifare_plus_default_keys_len; cnt++) { + + int len = hex_to_bytes(g_mifare_plus_default_keys[cnt], (uint8_t *)(*pkeyBlock + (*pkeycnt + cnt) * AES_KEY_LEN), AES_KEY_LEN); + + PrintAndLogEx(DEBUG, _YELLOW_("%2u") " - %s", *pkeycnt + cnt, sprint_hex_inrow(*pkeyBlock + (*pkeycnt + cnt) * AES_KEY_LEN, AES_KEY_LEN)); + if (len != AES_KEY_LEN) { + break; + } + } + *pkeycnt += cnt; + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%zu") " hardcoded keys", cnt); + } + + // Handle user supplied dictionary file + if (fnlen > 0) { + + uint32_t loaded_numKeys = 0; + uint8_t *dict_keys = NULL; + + int res = loadFileDICTIONARY_safe(filename, (void **) &dict_keys, AES_KEY_LEN, &loaded_numKeys); + + if (res != PM3_SUCCESS || loaded_numKeys == 0 || dict_keys == NULL) { + PrintAndLogEx(FAILED, "An error occurred while loading the dictionary!"); + free(dict_keys); + free(*pkeyBlock); + return PM3_EFILE; + + } else { + + p = realloc(*pkeyBlock, (*pkeycnt + loaded_numKeys) * AES_KEY_LEN); + if (p == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); + free(dict_keys); + free(*pkeyBlock); + return PM3_EMALLOC; + } + *pkeyBlock = p; + + memcpy(*pkeyBlock + *pkeycnt * AES_KEY_LEN, dict_keys, loaded_numKeys * AES_KEY_LEN); + + *pkeycnt += loaded_numKeys; + + free(dict_keys); + } + } + return PM3_SUCCESS; +} + + static int CmdHFMFPInfo(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp info", @@ -575,7 +676,8 @@ static int CmdHFMFPInitPerso(const char *Cmd) { CLIParserInit(&ctx, "hf mfp initp", "Executes Write Perso command for all card's keys. Can be used in SL0 mode only.", "hf mfp initp --key 000102030405060708090a0b0c0d0e0f -> fill all the keys with key (00..0f)\n" - "hf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange"); + "hf mfp initp -vv -> fill all the keys with default key(0xff..0xff) and show all the data exchange" + ); void *argtable[] = { arg_param_begin, @@ -700,13 +802,14 @@ static int CmdHFMFPAuth(const char *Cmd) { CLIParserInit(&ctx, "hf mfp auth", "Executes AES authentication command for MIFARE Plus card", "hf mfp auth --ki 4000 --key 000102030405060708090a0b0c0d0e0f -> executes authentication\n" - "hf mfp auth --ki 9003 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data"); + "hf mfp auth --ki 9003 --key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -v -> executes authentication and shows all the system data" + ); void *argtable[] = { arg_param_begin, arg_lit0("v", "verbose", "Verbose output"), arg_str1(NULL, "ki", "", "Key number, 2 hex bytes"), - arg_str1(NULL, "key", "", "Key, 16 hex bytes"), + arg_str1("k", "key", "", "Key, 16 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -728,35 +831,54 @@ static int CmdHFMFPAuth(const char *Cmd) { return MifareAuth4(NULL, keyn, key, true, false, true, verbose, false); } -static int data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev) { - uint8_t kenc[16]; - memcpy(kenc, mf4session->Kenc, 16); + +int mfp_data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev) { + uint8_t kenc[MFBLOCK_SIZE]; + memcpy(kenc, mf4session->Kenc, MFBLOCK_SIZE); + uint8_t ti[4]; memcpy(ti, mf4session->TI, 4); + uint8_t ctr[1]; - uint8_t IV[16] = {0, 0, 0x00, 0x00, 0x00, 0, 0x00, 0x00, 0x00, 0}; + uint8_t IV[MFBLOCK_SIZE] = { 0 }; + if (rev) { - ctr[0] = (uint8_t)(mf4session->R_Ctr & 0xff); - for (int i = 0; i < 9; i += 4) {memcpy(&IV[i], ctr, 1);} + + ctr[0] = (uint8_t)(mf4session->R_Ctr & 0xFF); + + for (int i = 0; i < 9; i += 4) { + memcpy(&IV[i], ctr, 1); + } + memcpy(&IV[12], ti, 4); // For reads TI is LS + } else { - ctr[0] = (uint8_t)(mf4session->W_Ctr & 0xff); - for (int i = 3; i < 16; i += 4) {memcpy(&IV[i], ctr, 1);} + + ctr[0] = (uint8_t)(mf4session->W_Ctr & 0xFF); + + for (int i = 3; i < MFBLOCK_SIZE; i += 4) { + memcpy(&IV[i], ctr, 1); + } + memcpy(&IV[0], ti, 4); // For writes TI is MS } + if (rev) { - aes_decode(IV, kenc, dati, dato, 16); + aes_decode(IV, kenc, dati, dato, MFBLOCK_SIZE); } else { - aes_encode(IV, kenc, dati, dato, 16); + aes_encode(IV, kenc, dati, dato, MFBLOCK_SIZE); } - return 0; + + return PM3_SUCCESS; } + static int CmdHFMFPRdbl(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfp rdbl", "Reads blocks from MIFARE Plus card", "hf mfp rdbl --blk 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read block 0 data\n" - "hf mfp rdbl --blk 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF"); + "hf mfp rdbl --blk 1 -v -> executes authentication and shows sector 1 data with default key 0xFF..0xFF" + ); void *argtable[] = { arg_param_begin, @@ -788,7 +910,7 @@ static int CmdHFMFPRdbl(const char *Cmd) { mfpSetVerboseMode(verbose); - if (!keylen) { + if (keylen == 0) { memmove(key, mfp_default_key, 16); keylen = 16; } @@ -817,8 +939,9 @@ static int CmdHFMFPRdbl(const char *Cmd) { uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); keyn[0] = uKeyNum >> 8; keyn[1] = uKeyNum & 0xff; - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "--block:%d sector[%u]:%02x key:%04x", blockn, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + } mf4Session_t mf4session; int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); @@ -846,7 +969,10 @@ static int CmdHFMFPRdbl(const char *Cmd) { return PM3_ESOFT; } - if (!plain) data_crypt(&mf4session, &data[1], &data[1], true); + if (plain == false) { + mfp_data_crypt(&mf4session, &data[1], &data[1], true); + } + uint8_t sector = mfSectorNum(blockn); mf_print_sector_hdr(sector); @@ -874,7 +1000,8 @@ static int CmdHFMFPRdsc(const char *Cmd) { CLIParserInit(&ctx, "hf mfp rdsc", "Reads one sector from MIFARE Plus card", "hf mfp rdsc -s 0 --key 000102030405060708090a0b0c0d0e0f -> executes authentication and read sector 0 data\n" - "hf mfp rdsc -s 1 -v -> executes authentication and shows sector 1 data with default key"); + "hf mfp rdsc -s 1 -v -> executes authentication and shows sector 1 data with default key" + ); void *argtable[] = { arg_param_begin, @@ -903,7 +1030,7 @@ static int CmdHFMFPRdsc(const char *Cmd) { mfpSetVerboseMode(verbose); - if (!keylen) { + if (keylen == 0) { memmove(key, mfp_default_key, 16); keylen = 16; } @@ -921,8 +1048,9 @@ static int CmdHFMFPRdsc(const char *Cmd) { uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); keyn[0] = uKeyNum >> 8; keyn[1] = uKeyNum & 0xff; - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "--sector[%u]:%02x key:%04x", mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + } mf4Session_t mf4session; int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); @@ -957,7 +1085,11 @@ static int CmdHFMFPRdsc(const char *Cmd) { DropField(); return PM3_ESOFT; } - if (!plain) data_crypt(&mf4session, &data[1], &data[1], true); + + if (plain == false) { + mfp_data_crypt(&mf4session, &data[1], &data[1], true); + } + mf_print_block_one(blockno, data + 1, verbose); if (memcmp(&data[1 + 16], mac, 8) && !nomacres) { @@ -1039,8 +1171,9 @@ static int CmdHFMFPWrbl(const char *Cmd) { uint16_t uKeyNum = 0x4000 + sectorNum * 2 + (keyB ? 1 : 0); keyn[0] = uKeyNum >> 8; keyn[1] = uKeyNum & 0xff; - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "--block:%d sector[%u]:%02x key:%04x", blockNum & 0xff, mfNumBlocksPerSector(sectorNum), sectorNum, uKeyNum); + } mf4Session_t mf4session; int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); @@ -1048,7 +1181,11 @@ static int CmdHFMFPWrbl(const char *Cmd) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; } - if (!plain) data_crypt(&mf4session, &datain[0], &datain[0], false); + + if (plain == false) { + mfp_data_crypt(&mf4session, &datain[0], &datain[0], false); + } + uint8_t data[250] = {0}; int datalen = 0; uint8_t mac[8] = {0}; @@ -1142,24 +1279,35 @@ static int CmdHFMFPChKey(const char *Cmd) { PrintAndLogEx(ERR, " must be 16 bytes. Got %d", datainlen); return PM3_EINVARG; } + mf4Session_t mf4session; + keyn[0] = ki[0]; + if (ki[0] == 0x40) { // Only if we are working with sector keys + if (usekeyb) { keyn[1] = (ki[1] % 2 == 0) ? ki[1] + 1 : ki[1]; // If we change using key B, check if KI is key A } else { keyn[1] = (ki[1] % 2 == 0) ? ki[1] : ki[1] - 1; // If we change using key A, check if KI is key A } - } else {keyn[1] = ki[1];} + + } else { + keyn[1] = ki[1]; + } + if (verbose) { PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2)); } + int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); if (res) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; } - data_crypt(&mf4session, &datain[0], &datain[0], false); + + mfp_data_crypt(&mf4session, &datain[0], &datain[0], false); + uint8_t data[250] = {0}; int datalen = 0; uint8_t mac[8] = {0}; @@ -1187,8 +1335,9 @@ static int CmdHFMFPChKey(const char *Cmd) { PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8)); PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); } else if (!nomacres) { - if (verbose) + if (verbose) { PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8)); + } } DropField(); @@ -1241,7 +1390,7 @@ static int CmdHFMFPChConf(const char *Cmd) { mfpSetVerboseMode(verbose); - if (!keylen) { + if (keylen == 0) { memmove(key, mfp_default_key, 16); keylen = 16; } @@ -1260,18 +1409,23 @@ static int CmdHFMFPChConf(const char *Cmd) { PrintAndLogEx(ERR, " must be in range [0..3]. Got %d", blockNum); return PM3_EINVARG; } + mf4Session_t mf4session; keyn[0] = 0x90; keyn[1] = usecck ? 0x01 : 0x00; + if (verbose) { PrintAndLogEx(INFO, "--key index:", sprint_hex(keyn, 2)); } + int res = MifareAuth4(&mf4session, keyn, key, true, true, true, verbose, false); if (res) { PrintAndLogEx(ERR, "Authentication error: %d", res); return res; } - data_crypt(&mf4session, &datain[0], &datain[0], false); + + mfp_data_crypt(&mf4session, &datain[0], &datain[0], false); + uint8_t data[250] = {0}; int datalen = 0; uint8_t mac[8] = {0}; @@ -1298,9 +1452,10 @@ static int CmdHFMFPChConf(const char *Cmd) { PrintAndLogEx(WARNING, "WARNING: mac not equal..."); PrintAndLogEx(WARNING, "MAC card: %s", sprint_hex(&data[1], 8)); PrintAndLogEx(WARNING, "MAC reader: %s", sprint_hex(mac, 8)); - } else if (!nomacres) { - if (verbose) + } else if (nomacres == false) { + if (verbose) { PrintAndLogEx(INFO, "MAC: %s", sprint_hex(&data[1], 8)); + } } DropField(); @@ -1308,19 +1463,33 @@ static int CmdHFMFPChConf(const char *Cmd) { return PM3_SUCCESS; } -static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startKeyAB, uint8_t endKeyAB, - uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], size_t keyListLen, uint8_t foundKeys[2][64][AES_KEY_LEN + 1], - bool verbose) { - int res; - bool selectCard = true; - uint8_t keyn[2] = {0}; +static int plus_key_check(uint8_t start_sector, uint8_t end_sector, uint8_t startKeyAB, uint8_t endKeyAB, + uint8_t *keys, size_t keycount, uint8_t foundKeys[2][64][AES_KEY_LEN + 1], + bool verbose, bool newline) { + + if (newline) { + PrintAndLogEx(INFO, "." NOLF); + } // sector number from 0 - for (uint8_t sector = startSector; sector <= endSector; sector++) { + for (uint8_t sector = start_sector; sector <= end_sector; sector++) { + // 0-keyA 1-keyB for (uint8_t keyAB = startKeyAB; keyAB <= endKeyAB; keyAB++) { + + // skip already found keys + if (foundKeys[keyAB][sector][0]) { + continue; + } + + int res; + bool selectCard = true; + + // reset current key pointer after each loop + uint8_t *currkey = keys; + // main cycle with key check - for (int i = 0; i < keyListLen; i++) { + for (int i = 0; i < keycount; i++) { // allow client abort every iteration if (kbd_enter_pressed()) { @@ -1336,18 +1505,21 @@ static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startK } uint16_t uKeyNum = 0x4000 + sector * 2 + keyAB; - keyn[0] = uKeyNum >> 8; - keyn[1] = uKeyNum & 0xff; + uint8_t keyn[2] = { uKeyNum >> 8, uKeyNum & 0xff}; - for (int retry = 0; retry < 4; retry++) { - res = MifareAuth4(NULL, keyn, keyList[i], selectCard, true, false, false, true); - if (res == PM3_SUCCESS || res == PM3_EWRONGANSWER) + // authentication loop with retries + for (int retry = 0; retry < MFP_CHK_KEY_TRIES; retry++) { + + res = MifareAuth4(NULL, keyn, currkey, selectCard, true, false, false, true); + if (res == PM3_SUCCESS || res == PM3_EWRONGANSWER) { break; + } - if (verbose) + if (verbose) { PrintAndLogEx(WARNING, "\nretried[%d]...", retry); - else + } else { PrintAndLogEx(NORMAL, "R" NOLF); + } DropField(); selectCard = true; @@ -1356,38 +1528,52 @@ static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startK // key for [sector,keyAB] found if (res == PM3_SUCCESS) { - if (verbose) - PrintAndLogEx(INFO, "\nFound key for sector %d key %s [%s]", sector, keyAB == 0 ? "A" : "B", sprint_hex_inrow(keyList[i], 16)); - else - PrintAndLogEx(NORMAL, "+" NOLF); + + if (verbose) { + PrintAndLogEx(INFO, "Found key for sector " _YELLOW_("%d") " key "_YELLOW_("%s") " [ " _GREEN_("%s") " ]", sector, (keyAB == 0) ? "A" : "B", sprint_hex_inrow(currkey, AES_KEY_LEN)); + } else { + PrintAndLogEx(NORMAL, _GREEN_("+") NOLF); + } foundKeys[keyAB][sector][0] = 0x01; - memcpy(&foundKeys[keyAB][sector][1], keyList[i], AES_KEY_LEN); + memcpy(&foundKeys[keyAB][sector][1], currkey, AES_KEY_LEN); + DropField(); selectCard = true; - msleep(50); +// msleep(50); + + // recursive test of a successful key + if (keycount > 1) { + plus_key_check(start_sector, end_sector, startKeyAB, endKeyAB, currkey, 1, foundKeys, verbose, false); + } // break out from keylist check loop, break; } - if (verbose) - PrintAndLogEx(WARNING, "\nsector %02d key %d [%s] res: %d", sector, keyAB, sprint_hex_inrow(keyList[i], 16), res); + if (verbose) { + PrintAndLogEx(WARNING, "\nsector %02d key %d [%s] res: %d", sector, keyAB, sprint_hex_inrow(currkey, AES_KEY_LEN), res); + } // RES can be: // PM3_ERFTRANS -7 // PM3_EWRONGANSWER -16 if (res == PM3_ERFTRANS) { - if (verbose) + + if (verbose) { PrintAndLogEx(ERR, "\nExchange error. Aborted."); - else + } else { PrintAndLogEx(NORMAL, "E" NOLF); + } DropField(); return PM3_ECARDEXCHANGE; } selectCard = false; + + // set pointer to next key + currkey += AES_KEY_LEN; } } } @@ -1396,18 +1582,31 @@ static int plus_key_check(uint8_t startSector, uint8_t endSector, uint8_t startK return PM3_SUCCESS; } -static void Fill2bPattern(uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], uint32_t *keyListLen, uint32_t *startPattern) { +static void Fill2bPattern(uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN], uint32_t *n, uint32_t *startPattern) { + + uint32_t cnt = 0; for (uint32_t pt = *startPattern; pt < 0x10000; pt++) { - keyList[*keyListLen][0] = (pt >> 8) & 0xff; - keyList[*keyListLen][1] = pt & 0xff; - memcpy(&keyList[*keyListLen][2], &keyList[*keyListLen][0], 2); - memcpy(&keyList[*keyListLen][4], &keyList[*keyListLen][0], 4); - memcpy(&keyList[*keyListLen][8], &keyList[*keyListLen][0], 8); - (*keyListLen)++; + + for (uint8_t i = 0; i < AES_KEY_LEN; i += 2) { + keyList[*n][i] = (pt >> 8) & 0xff; + keyList[*n][i + 1] = pt & 0xff; + } + + PrintAndLogEx(DEBUG, _YELLOW_("%4d") " - %s", *n, sprint_hex_inrow(keyList[*n], AES_KEY_LEN)); + + // increase number of keys + (*n)++; + cnt++; + *startPattern = pt; - if (*keyListLen == MAX_AES_KEYS_LIST_LEN) + + if (*n == MAX_AES_KEYS_LIST_LEN) { break; + } } + + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " pattern2 keys", cnt); + (*startPattern)++; } @@ -1418,9 +1617,10 @@ static int CmdHFMFPChk(const char *Cmd) { "Checks keys on MIFARE Plus card", "hf mfp chk -k 000102030405060708090a0b0c0d0e0f -> check key on sector 0 as key A and B\n" "hf mfp chk -s 2 -a -> check default key list on sector 2, only key A\n" - "hf mfp chk -d mfp_default_keys -s0 -e6 -> check keys from dictionary against sectors 0-6\n" + "hf mfp chk -f mfp_default_keys -s 0 -e 6 -> check keys from dictionary against sectors 0-6\n" "hf mfp chk --pattern1b --dump -> check all 1-byte keys pattern and save found keys to file\n" - "hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00"); + "hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00" + ); void *argtable[] = { arg_param_begin, @@ -1429,11 +1629,12 @@ static int CmdHFMFPChk(const char *Cmd) { arg_int0("s", "startsec", "<0..255>", "Start sector number"), arg_int0("e", "endsec", "<0..255>", "End sector number"), arg_str0("k", "key", "", "Key for checking (HEX 16 bytes)"), - arg_str0("d", "dict", "", "Dictionary file with keys"), + arg_str0("f", "file", "", "Dictionary file with default keys"), arg_lit0(NULL, "pattern1b", "Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)"), arg_lit0(NULL, "pattern2b", "Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)"), arg_str0(NULL, "startp2b", "", "Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)"), arg_lit0(NULL, "dump", "Dump found keys to JSON file"), + arg_lit0(NULL, "no-default", "Skip check default keys"), arg_lit0("v", "verbose", "Verbose output"), arg_param_end }; @@ -1441,88 +1642,117 @@ static int CmdHFMFPChk(const char *Cmd) { bool keyA = arg_get_lit(ctx, 1); bool keyB = arg_get_lit(ctx, 2); + uint8_t startSector = arg_get_int_def(ctx, 3, 0); uint8_t endSector = arg_get_int_def(ctx, 4, 0); - uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN] = {{0}}; uint32_t keyListLen = 0; + uint8_t keyList[MAX_AES_KEYS_LIST_LEN][AES_KEY_LEN] = {{0}}; uint8_t foundKeys[2][64][AES_KEY_LEN + 1] = {{{0}}}; - uint8_t vkey[16] = {0}; int vkeylen = 0; + uint8_t vkey[AES_KEY_LEN] = {0}; CLIGetHexWithReturn(ctx, 5, vkey, &vkeylen); - if (vkeylen > 0) { - if (vkeylen == 16) { - memcpy(&keyList[keyListLen], vkey, 16); - keyListLen++; - } else { - PrintAndLogEx(ERR, "Specified key must have 16 bytes. Got %d", vkeylen); - CLIParserFree(ctx); - return PM3_EINVARG; - } - } - uint8_t dict_filename[FILE_PATH_SIZE + 2] = {0}; - int dict_filenamelen = 0; - if (CLIParamStrToBuf(arg_get_str(ctx, 6), dict_filename, FILE_PATH_SIZE, &dict_filenamelen)) { - PrintAndLogEx(FAILED, "File name too long or invalid."); - CLIParserFree(ctx); - return PM3_EINVARG; - } + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 6), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); bool pattern1b = arg_get_lit(ctx, 7); bool pattern2b = arg_get_lit(ctx, 8); - if (pattern1b && pattern2b) { - PrintAndLogEx(ERR, "Pattern search mode must be 2-byte or 1-byte only."); - CLIParserFree(ctx); - return PM3_EINVARG; - } - - if (dict_filenamelen && (pattern1b || pattern2b)) { - PrintAndLogEx(ERR, "Pattern search mode and dictionary mode can't be used in one command."); - CLIParserFree(ctx); - return PM3_EINVARG; - } - - uint32_t startPattern = 0x0000; - uint8_t vpattern[2]; int vpatternlen = 0; + uint8_t vpattern[2]; CLIGetHexWithReturn(ctx, 9, vpattern, &vpatternlen); - if (vpatternlen > 0) { - if (vpatternlen <= 2) { - startPattern = (vpattern[0] << 8) + vpattern[1]; - } else { - PrintAndLogEx(ERR, "Pattern must be 2-bytes. Got %d", vpatternlen); - CLIParserFree(ctx); - return PM3_EINVARG; - } - if (!pattern2b) - PrintAndLogEx(WARNING, "Pattern entered, but search mode not is 2-byte search."); - } bool create_dumpfile = arg_get_lit(ctx, 10); - bool verbose = arg_get_lit(ctx, 11); + bool load_default = ! arg_get_lit(ctx, 11); + bool verbose = arg_get_lit(ctx, 12); + CLIParserFree(ctx); + // sanity checks + if (vkeylen && (vkeylen != AES_KEY_LEN)) { + PrintAndLogEx(ERR, "Specified key must have 16 bytes. Got %d", vkeylen); + return PM3_EINVARG; + } + + if (pattern1b && pattern2b) { + PrintAndLogEx(ERR, "Pattern search mode must be 2-byte or 1-byte only"); + return PM3_EINVARG; + } + + if (fnlen && (pattern1b || pattern2b)) { + PrintAndLogEx(ERR, "Pattern search mode and dictionary mode can't be used in one command"); + return PM3_EINVARG; + } + + if (vpatternlen && pattern2b == false) { + PrintAndLogEx(WARNING, "Pattern entered, but search mode not is 2-byte search"); + return PM3_EINVARG; + } + + if (vpatternlen > 2) { + PrintAndLogEx(ERR, "Pattern must be 2-bytes. Got %d", vpatternlen); + return PM3_EINVARG; + } + + uint32_t startPattern = (vpattern[0] << 8) + vpattern[1]; + + // read card UID + iso14a_card_select_t card; + int nxptype = MTNONE; + int res = mfp_read_card_id(&card, &nxptype); + if (res != PM3_SUCCESS) { + return res; + } + uint8_t startKeyAB = 0; uint8_t endKeyAB = 1; - if (keyA && (keyB == false)) + if (keyA && (keyB == false)) { endKeyAB = 0; + } - if ((keyA == false) && keyB) + if ((keyA == false) && keyB) { startKeyAB = 1; + } - if (endSector < startSector) + if (endSector < startSector) { endSector = startSector; + } + // Print generic information + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Check keys") " ----------"); + PrintAndLogEx(INFO, "Start sector... %u", startSector); + PrintAndLogEx(INFO, "End sector..... %u", endSector); + + char keytypestr[6] = {0}; + if (keyA == false && keyB == false) { + strcat(keytypestr, "AB"); + } + if (keyA) { + strcat(keytypestr, "A"); + } + if (keyB) { + strcat(keytypestr, "B"); + } + PrintAndLogEx(INFO, "Key type....... " _YELLOW_("%s"), keytypestr); + PrintAndLogEx(NORMAL, ""); + + // + // Key creation section + // // 1-byte pattern search mode if (pattern1b) { + for (int i = 0; i < 0x100; i++) { memset(keyList[i], i, 16); + PrintAndLogEx(DEBUG, _YELLOW_("%3d") " - %s", i, sprint_hex_inrow(keyList[i], AES_KEY_LEN)); } keyListLen = 0x100; + PrintAndLogEx(SUCCESS, "loaded " _GREEN_("%d") " pattern1b keys", 0x100); } // 2-byte pattern search mode @@ -1530,87 +1760,58 @@ static int CmdHFMFPChk(const char *Cmd) { Fill2bPattern(keyList, &keyListLen, &startPattern); } - int res = PM3_SUCCESS; - // dictionary mode - size_t endFilePosition = 0; - if (dict_filenamelen) { - uint32_t keycnt = 0; - res = loadFileDICTIONARYEx((char *)dict_filename, keyList, sizeof(keyList), NULL, 16, &keycnt, 0, &endFilePosition, true); + uint8_t *key_block = NULL; + uint32_t keycnt = 0; - if (res == PM3_SUCCESS && endFilePosition) { - keyListLen = keycnt; - PrintAndLogEx(SUCCESS, "First part of dictionary successfully loaded."); - } + int ret = mfp_load_keys(&key_block, &keycnt, vkey, vkeylen, filename, fnlen, card.uid, load_default); + if (ret != PM3_SUCCESS) { + return ret; } - if (keyListLen == 0) { - for (int i = 0; i < g_mifare_plus_default_keys_len; i++) { - if (hex_to_bytes(g_mifare_plus_default_keys[i], keyList[keyListLen], 16) != 16) { - break; - } + PrintAndLogEx(INFO, "Start check for keys..."); - keyListLen++; - } + // time + uint64_t t1 = msclock(); + + res = plus_key_check(startSector, endSector, startKeyAB, endKeyAB, key_block, keycnt, foundKeys, verbose, true); + if (res == PM3_EOPABORTED) { + t1 = msclock() - t1; + PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + return res; } - if (keyListLen == 0) { - PrintAndLogEx(ERR, "Key list is empty. Nothing to check."); - return PM3_EINVARG; - } else { - PrintAndLogEx(INFO, "Loaded " _YELLOW_("%"PRIu32) " keys", keyListLen); - } + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "Start check for pattern based keys..."); + while (keyListLen) { - if (verbose == false) { - PrintAndLogEx(INFO, "Search keys"); - } - - while (true) { - res = plus_key_check(startSector, endSector, startKeyAB, endKeyAB, keyList, keyListLen, foundKeys, verbose); + res = plus_key_check(startSector, endSector, startKeyAB, endKeyAB, (uint8_t *)keyList, keyListLen, foundKeys, verbose, true); if (res == PM3_EOPABORTED) { break; } if (pattern2b && startPattern < 0x10000) { - if (verbose == false) { - PrintAndLogEx(NORMAL, "p" NOLF); - } - keyListLen = 0; + PrintAndLogEx(NORMAL, ""); Fill2bPattern(keyList, &keyListLen, &startPattern); continue; } - - if (dict_filenamelen && endFilePosition) { - if (verbose == false) - PrintAndLogEx(NORMAL, "d" NOLF); - - uint32_t keycnt = 0; - res = loadFileDICTIONARYEx((char *)dict_filename, keyList, sizeof(keyList), NULL, 16, &keycnt, endFilePosition, &endFilePosition, false); - if (res == PM3_SUCCESS && endFilePosition) { - keyListLen = keycnt; - } - - continue; - } break; } - if (verbose == false) { - PrintAndLogEx(NORMAL, ""); - } + t1 = msclock() - t1; + PrintAndLogEx(INFO, "\ntime in checkkeys " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); // print result char strA[46 + 1] = {0}; char strB[46 + 1] = {0}; - uint8_t ndef_key[] = {0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7, 0xD3, 0xF7}; bool has_ndef_key = false; bool printedHeader = false; for (uint8_t s = startSector; s <= endSector; s++) { - if ((memcmp(&foundKeys[0][s][1], ndef_key, AES_KEY_LEN) == 0) || - (memcmp(&foundKeys[1][s][1], ndef_key, AES_KEY_LEN) == 0)) { + if ((memcmp(&foundKeys[0][s][1], g_mifarep_ndef_key, AES_KEY_LEN) == 0) || + (memcmp(&foundKeys[1][s][1], g_mifarep_ndef_key, AES_KEY_LEN) == 0)) { has_ndef_key = true; } @@ -1637,10 +1838,12 @@ static int CmdHFMFPChk(const char *Cmd) { PrintAndLogEx(INFO, " " _YELLOW_("%03d") " | %s | %s", s, strA, strB); } - if (printedHeader == false) - PrintAndLogEx(INFO, "No keys found("); - else - PrintAndLogEx(INFO, "-----+----------------------------------+----------------------------------\n"); + if (printedHeader == false) { + PrintAndLogEx(INFO, "No keys found"); + } else { + PrintAndLogEx(INFO, "-----+----------------------------------+----------------------------------"); + } + PrintAndLogEx(NORMAL, ""); // save keys to json if (create_dumpfile && printedHeader) { @@ -1650,29 +1853,12 @@ static int CmdHFMFPChk(const char *Cmd) { uint8_t data[10 + 1 + 2 + 1 + 256 + keys_len]; memset(data, 0, sizeof(data)); - // Mifare Plus info - SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); - - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply"); - return PM3_ETIMEOUT; - } - - iso14a_card_select_t card; - memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); - - uint64_t select_status = resp.oldarg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision - uint8_t atslen = 0; - if (select_status == 1 || select_status == 2) { - memcpy(data, card.uid, card.uidlen); - data[10] = card.sak; - data[11] = card.atqa[1]; - data[12] = card.atqa[0]; - atslen = card.ats_len; - data[13] = atslen; - memcpy(&data[14], card.ats, atslen); - } + memcpy(data, card.uid, card.uidlen); + data[10] = card.sak; + data[11] = card.atqa[1]; + data[12] = card.atqa[0]; + data[13] = card.ats_len; + memcpy(&data[14], card.ats, card.ats_len); char *fptr = calloc(sizeof(char) * (strlen("hf-mfp-") + strlen("-key")) + card.uidlen * 2 + 1, sizeof(uint8_t)); if (fptr == NULL) { @@ -1684,14 +1870,14 @@ static int CmdHFMFPChk(const char *Cmd) { FillFileNameByUID(fptr, card.uid, "-key", card.uidlen); // length: UID(10b)+SAK(1b)+ATQA(2b)+ATSlen(1b)+ATS(atslen)+foundKeys[2][64][AES_KEY_LEN + 1] - memcpy(&data[14 + atslen], foundKeys, keys_len); + memcpy(&data[14 + card.ats_len], foundKeys, keys_len); // 64 here is for how many "rows" there is in the data array. A bit confusing saveFileJSON(fptr, jsfMfPlusKeys, data, 64, NULL); free(fptr); } // MAD detection - if ((memcmp(&foundKeys[0][0][1], "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7", AES_KEY_LEN) == 0)) { + if ((memcmp(&foundKeys[0][0][1], g_mifarep_mad_key, AES_KEY_LEN) == 0)) { PrintAndLogEx(HINT, "Hint: MAD key detected. Try " _YELLOW_("`hf mfp mad`") " for more details"); } @@ -1784,7 +1970,8 @@ static int CmdHFMFPMAD(const char *Cmd) { CLIParserInit(&ctx, "hf mfp mad", "Checks and prints MIFARE Application Directory (MAD)", "hf mfp mad\n" - "hf mfp mad --aid e103 -k d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 -> read and print NDEF data from MAD aid"); + "hf mfp mad --aid e103 -k d3f7d3f7d3f7d3f7d3f7d3f7d3f7d3f7 -> read and print NDEF data from MAD aid" + ); void *argtable[] = { arg_param_begin, @@ -2139,7 +2326,7 @@ static command_t CommandTable[] = { {"auth", CmdHFMFPAuth, IfPm3Iso14443a, "Authentication"}, {"chk", CmdHFMFPChk, IfPm3Iso14443a, "Check keys"}, {"dump", CmdHFMFPDump, IfPm3Iso14443a, "Dump MIFARE Plus tag to binary file"}, - {"info", CmdHFMFPInfo, IfPm3Iso14443a, "Info about MIFARE Plus tag"}, + {"info", CmdHFMFPInfo, IfPm3Iso14443a, "Tag information"}, {"mad", CmdHFMFPMAD, IfPm3Iso14443a, "Check and print MAD"}, {"rdbl", CmdHFMFPRdbl, IfPm3Iso14443a, "Read blocks from card"}, {"rdsc", CmdHFMFPRdsc, IfPm3Iso14443a, "Read sectors from card"}, diff --git a/client/src/cmdhfmfp.h b/client/src/cmdhfmfp.h index f6189fadf..377525c03 100644 --- a/client/src/cmdhfmfp.h +++ b/client/src/cmdhfmfp.h @@ -19,20 +19,7 @@ #define CMDHFMFP_H__ #include "common.h" - -typedef enum { - MFP_UNKNOWN = 0, - DESFIRE_MF3ICD40, - DESFIRE_EV1, - DESFIRE_EV2, - DESFIRE_EV2_XL, - DESFIRE_EV3, - DESFIRE_LIGHT, - PLUS_EV1, - PLUS_EV2, - NTAG413DNA, - NTAG424 -} nxp_cardtype_t; +#include "mifare/mifare4.h" typedef struct mfp_key_item { uint8_t a[16]; @@ -46,5 +33,5 @@ typedef struct mfp_keys { int CmdHFMFP(const char *Cmd); int CmdHFMFPNDEFRead(const char *Cmd); - +int mfp_data_crypt(mf4Session_t *mf4session, uint8_t *dati, uint8_t *dato, bool rev); #endif diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index b98d6faa1..1815705de 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -59,11 +59,13 @@ static int CmdHelp(const char *Cmd); +static const char *key_type[] = { "DataProtKey", "UIDRetrKey", "OriginalityKey" }; + static uint8_t default_aes_keys[][16] = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // all zeroes { 0x42, 0x52, 0x45, 0x41, 0x4b, 0x4d, 0x45, 0x49, 0x46, 0x59, 0x4f, 0x55, 0x43, 0x41, 0x4e, 0x21 }, // 3des std key - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }, // NFC-key + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // all ones { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // all FF { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, // 11 22 33 @@ -74,8 +76,8 @@ static uint8_t default_aes_keys[][16] = { static uint8_t default_3des_keys[][16] = { { 0x42, 0x52, 0x45, 0x41, 0x4b, 0x4d, 0x45, 0x49, 0x46, 0x59, 0x4f, 0x55, 0x43, 0x41, 0x4e, 0x21 }, // 3des std key { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // all zeroes - { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46 }, // NFC-key + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }, // 0x00-0x0F { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // all ones { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, // all FF { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF }, // 11 22 33 @@ -299,7 +301,7 @@ static const char *getUlev1CardSizeStr(uint8_t fsize) { // is LSB set? if (fsize & 1) - snprintf(buf, sizeof(buf), "%02X, (%u <-> %u bytes)", fsize, usize, lsize); + snprintf(buf, sizeof(buf), "%02X, (%u - %u bytes)", fsize, usize, lsize); else snprintf(buf, sizeof(buf), "%02X, (%u bytes)", fsize, lsize); return buf; @@ -451,7 +453,7 @@ static int ul_comp_write(uint8_t page, const uint8_t *data, uint8_t datalen) { uint8_t response[1] = {0xFF}; ul_send_cmd_raw(cmd, 2 + datalen, response, sizeof(response)); // ACK - if (response[0] == 0x0a) { + if (response[0] == CARD_ACK) { return PM3_SUCCESS; } // NACK @@ -464,13 +466,31 @@ static int ulc_requestAuthentication(uint8_t *nonce, uint16_t nonceLength) { return ul_send_cmd_raw(cmd, sizeof(cmd), nonce, nonceLength); } +int mfuc_test_authentication_support(void) { + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(DEBUG, "iso14443a card select timeout"); + DropField(); + return PM3_ETIMEOUT; + } + uint8_t nonce1[11] = {0x00}; + int resplen = ulc_requestAuthentication(nonce1, sizeof(nonce1)); + DropField(); + if (resplen == 11) { // ULC nonce + return PM3_SUCCESS; + } + return PM3_ESOFT; +} + static int ulev1_requestAuthentication(const uint8_t *pwd, uint8_t *pack, uint16_t packLength) { uint8_t cmd[] = {MIFARE_ULEV1_AUTH, pwd[0], pwd[1], pwd[2], pwd[3]}; int len = ul_send_cmd_raw(cmd, sizeof(cmd), pack, packLength); // NACK tables different tags, but between 0-9 is a NEGATIVE response. // ACK == 0xA - if (len == 1 && pack[0] <= 0x09) { + // should only give you PACK (4 byytes) + if (len == 1) { return PM3_EWRONGANSWER; } return len; @@ -498,10 +518,7 @@ static int ulaes_requestAuthentication(const uint8_t *key, uint8_t keyno, bool s if (WaitForResponseTimeout(CMD_HF_MIFAREULAES_AUTH, &resp, 1500) == false) { return PM3_ETIMEOUT; } - if (resp.status != PM3_SUCCESS) { - return resp.status; - } - return PM3_SUCCESS; + return resp.status; } static int ulc_authentication(const uint8_t *key, bool switch_off_field) { @@ -1613,6 +1630,7 @@ static int mfu_dump_tag(uint16_t pages, void **pdata, uint16_t *len) { if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { PrintAndLogEx(WARNING, "command execution time out"); free(*pdata); + *pdata = NULL; res = PM3_ETIMEOUT; goto out; } @@ -1620,6 +1638,7 @@ static int mfu_dump_tag(uint16_t pages, void **pdata, uint16_t *len) { if (resp.oldarg[0] != 1) { PrintAndLogEx(WARNING, "Failed reading card"); free(*pdata); + *pdata = NULL; res = PM3_ESOFT; goto out; } @@ -1635,6 +1654,7 @@ static int mfu_dump_tag(uint16_t pages, void **pdata, uint16_t *len) { if (GetFromDevice(BIG_BUF, *pdata, buffer_size, startindex, NULL, 0, NULL, 2500, false) == false) { PrintAndLogEx(WARNING, "command execution time out"); free(*pdata); + *pdata = NULL; res = PM3_ETIMEOUT; goto out; } @@ -1666,7 +1686,7 @@ typedef struct { } mfu_otp_identify_t; static mfu_otp_identify_t mfu_otp_ident_table[] = { - { "SALTO Systems card", 12, 4, "534C544F", ul_c_otpgenA, NULL }, + { "SALTO Systems card", 12, 4, "534C544F", ul_c_otpgenA, "report to iceman!" }, { NULL, 0, 0, NULL, NULL, NULL } }; @@ -1832,7 +1852,7 @@ static uint8_t mfu_max_len(void) { return n; } -static int mfu_get_version_uid(uint8_t *version, uint8_t *uid) { +int mfu_get_version_uid(uint8_t *version, uint8_t *uid) { iso14a_card_select_t card; if (ul_select(&card) == false) { return PM3_ESOFT; @@ -1945,7 +1965,7 @@ static int mfu_fingerprint(uint64_t tagtype, bool hasAuthKey, const uint8_t *aut // OTP checks mfu_otp_identify_t *item = mfu_match_otp_fingerprint(uid, data); if (item) { - PrintAndLogEx(SUCCESS, _GREEN_("%s"), item->desc); + PrintAndLogEx(SUCCESS, _BACK_GREEN_(" %s "), item->desc); res = PM3_SUCCESS; if (item->hint) { @@ -2018,12 +2038,12 @@ uint64_t GetHF14AMfU_Type(void) { return MFU_TT_UL_ERROR; // Ultralight - ATQA / SAK - if (card.atqa[1] != 0x00 || card.atqa[0] != 0x44 || card.sak != 0x00) { + if (card.atqa[1] != 0x00 || card.sak != 0x00) { //PrintAndLogEx(NORMAL, "Tag is not Ultralight | NTAG | MY-D |ST25TN [ATQA: %02X %02X SAK: %02X]\n", card.atqa[1], card.atqa[0], card.sak); DropField(); return MFU_TT_UL_ERROR; } - if (card.uid[0] == 0x02) { + if ((card.uid[0] == 0x02) && (card.atqa[0] == 0x44)) { // ST25TN // read SYSBLOCK uint8_t data[4] = {0x00}; @@ -2045,7 +2065,7 @@ uint64_t GetHF14AMfU_Type(void) { } } - } else if (card.uid[0] == 0x05) { + } else if ((card.uid[0] == 0x05) && (card.atqa[0] == 0x44)) { // Infineon MY-D tests Exam high nibble DropField(); uint8_t nib = (card.uid[1] & 0xf0) >> 4; @@ -2066,7 +2086,7 @@ uint64_t GetHF14AMfU_Type(void) { } } else { - + // Note that SAK might be 0x44 but also e.g. 0x04 for cards in Random ID mode uint8_t version[10] = {0x00}; int len = ulev1_getVersion(version, sizeof(version)); DropField(); @@ -2317,8 +2337,8 @@ static int CmdHF14AMfUInfo(const char *Cmd) { uint8_t ulc_conf[16] = {0x00}; status = ul_read(0x28, ulc_conf, sizeof(ulc_conf)); if (status <= 0) { - PrintAndLogEx(ERR, "Error: tag didn't answer to READ UL-C"); - PrintAndLogEx(HINT, "Hint: Tag is most likely fully read protected"); + PrintAndLogEx(ERR, "Error: tag didn't answer to page 40 read command"); + PrintAndLogEx(HINT, "Hint: tag config may be set to read-protect those pages, try dumping"); DropField(); return PM3_ESOFT; } @@ -2707,8 +2727,9 @@ static int CmdHF14AMfUWrBl(const char *Cmd) { // starting with getting tagtype uint64_t tagtype = GetHF14AMfU_Type(); - if (tagtype == MFU_TT_UL_ERROR) + if (tagtype == MFU_TT_UL_ERROR) { return PM3_ESOFT; + } uint8_t maxblockno = 0; for (uint8_t idx = 1; idx < ARRAYLEN(UL_TYPES_ARRAY); idx++) { @@ -3817,18 +3838,21 @@ static int CmdHF14AMfUSim(const char *Cmd) { "ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n" "from emulator memory. See `hf mfu eload` first. \n" "The UID from emulator memory will be used if not specified.\n" - "See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", + "See `hf 14a sim -h` to see available types. You want 2, 7 or 13 usually.", "hf mfu sim -t 2 --uid 11223344556677 -> MIFARE Ultralight\n" "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> MFU EV1 / NTAG 215 Amiibo\n" - "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo" + "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo\n" + "hf mfu sim -t 13 -> MIFARE Ultralight-C\n" ); void *argtable[] = { arg_param_begin, - arg_int1("t", "type", "<1..12> ", "Simulation type to use"), + arg_int1("t", "type", "<1..13> ", "Simulation type to use"), arg_str0("u", "uid", "", "<4|7|10> hex bytes UID"), arg_int0("n", "num", "", "Exit simulation after blocks. 0 = infinite"), arg_lit0("v", "verbose", "Verbose output"), + arg_lit0(NULL, "c1", "UL-C Auth - all zero handshake part 1"), + arg_lit0(NULL, "c2", "UL-C Auth - all zero handshake part 2"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -3845,17 +3869,17 @@ static int CmdHF14AMfUSim(const char *Cmd) { static int CmdHF14AMfUCAuth(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfu cauth", - "Tests 3DES password on Mifare Ultralight-C tag.\n" - "If password is not specified, a set of known defaults will be tested.", + "Tests 3DES key on Mifare Ultralight-C tag.\n" + "If key is not specified, a set of known defaults will be tried.", "hf mfu cauth\n" "hf mfu cauth --key 000102030405060708090a0b0c0d0e0f" ); void *argtable[] = { arg_param_begin, - arg_str0(NULL, "key", "", "Authentication key (UL-C 16 hex bytes)"), + arg_str0(NULL, "key", "", "Authentication key (16 bytes in hex)"), arg_lit0("l", NULL, "Swap entered key's endianness"), - arg_lit0("k", NULL, "Keep field on (only if a password is provided)"), + arg_lit0("k", NULL, "Keep field on (only if a key is provided)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -3907,24 +3931,24 @@ static int CmdHF14AMfUAESAuth(const char *Cmd) { CLIParserInit(&ctx, "hf mfu aesauth", "Tests AES key on Mifare Ultralight AES tags.\n" "If no key is specified, null key will be tried.\n" - "Key index 0: DataProtKey (default)\n" - "Key index 1: UIDRetrKey\n" - "Key index 2: OriginalityKey\n", + " Key index 0... DataProtKey (default)\n" + " Key index 1... UIDRetrKey\n" + " Key index 2... OriginalityKey\n", "hf mfu aesauth\n" - "hf mfu aesauth --key <32 bytes> --index <0..2>" + "hf mfu aesauth --key <16 hex bytes> --idx <0..2>" ); void *argtable[] = { arg_param_begin, - arg_str0(NULL, "key", "", "AES key (32 hex bytes)"), - arg_int0("i", "index", "<0..2>", "Key index, default: 0"), + arg_str0(NULL, "key", "", "AES key (16 hex bytes)"), + arg_int0("i", "idx", "<0..2>", "Key index (def: 0)"), arg_lit0("k", NULL, "Keep field on (only if a key is provided)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); int ak_len = 0; - uint8_t authentication_key[32] = {0}; + uint8_t authentication_key[16] = {0}; uint8_t *authKeyPtr = authentication_key; CLIGetHexWithReturn(ctx, 1, authentication_key, &ak_len); int key_index = arg_get_int_def(ctx, 2, 0); @@ -3934,9 +3958,9 @@ static int CmdHF14AMfUAESAuth(const char *Cmd) { if (ak_len == 0) { // default to null key - ak_len = 32; + ak_len = 16; } - if (ak_len != 32) { + if (ak_len != 16) { PrintAndLogEx(WARNING, "Invalid key length"); return PM3_EINVARG; } @@ -3947,14 +3971,13 @@ static int CmdHF14AMfUAESAuth(const char *Cmd) { } int result = ulaes_requestAuthentication(authKeyPtr, key_index, !keep_field_on); - - const char *key_type[] = { "DataProtKey", "UIDRetrKey", "OriginalityKey" }; if (result == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Authentication with " _YELLOW_("%s") " " _GREEN_("%s") " ( " _GREEN_("ok")" )", - key_type[key_index], sprint_hex_inrow(authKeyPtr, ak_len)); + PrintAndLogEx(SUCCESS, "Authentication with " _YELLOW_("%s") " " _GREEN_("%s") " ( " _GREEN_("ok")" )" + , key_type[key_index] + , sprint_hex_inrow(authKeyPtr, ak_len) + ); } else { - PrintAndLogEx(WARNING, "Authentication with " _YELLOW_("%s") " ( " _RED_("fail") " )", - key_type[key_index]); + PrintAndLogEx(WARNING, "Authentication with " _YELLOW_("%s") " ( " _RED_("fail") " )", key_type[key_index]); } return result; } @@ -4147,17 +4170,17 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { // Enforce bad BCC handling temporarily as BCC will be wrong between // block 1 write and block2 write - hf14a_config config; + hf14a_config_t config; SendCommandNG(CMD_HF_ISO14443A_GET_CONFIG, NULL, 0); if (WaitForResponseTimeout(CMD_HF_ISO14443A_GET_CONFIG, &resp, 2000) == false) { PrintAndLogEx(WARNING, "command execute timeout"); return PM3_ETIMEOUT; } - memcpy(&config, resp.data.asBytes, sizeof(hf14a_config)); + memcpy(&config, resp.data.asBytes, sizeof(hf14a_config_t)); int8_t oldconfig_bcc = config.forcebcc; if (oldconfig_bcc != 2) { config.forcebcc = 2; - SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)&config, sizeof(hf14a_config)); + SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)&config, sizeof(hf14a_config_t)); } // block 0. @@ -4200,7 +4223,7 @@ static int CmdHF14AMfUCSetUid(const char *Cmd) { // restore BCC config if (oldconfig_bcc != 2) { config.forcebcc = oldconfig_bcc; - SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)&config, sizeof(hf14a_config)); + SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)&config, sizeof(hf14a_config_t)); } return PM3_SUCCESS; } @@ -4609,7 +4632,7 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { // we be getting ACK that we are silently ignoring here.. - if (!WaitForResponseTimeout(CMD_HF_MFU_OTP_TEAROFF, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_HF_MFU_OTP_TEAROFF, &resp, 2000) == false) { PrintAndLogEx(WARNING, "Failed"); return PM3_ESOFT; } @@ -4630,11 +4653,13 @@ static int CmdHF14AMfuOtpTearoff(const char *Cmd) { got_post = true; } } - if (! got_post) { + + if (!got_post) { PrintAndLogEx(FAILED, "Failed to read block BEFORE"); error_retries++; continue; // try again } + error_retries = 0; char prestr[20] = {0}; snprintf(prestr, sizeof(prestr), "%s", sprint_hex_inrow(pre, sizeof(pre))); @@ -4917,7 +4942,7 @@ static int CmdHF14AMfuEv1CounterTearoff(const char *Cmd) { clearCommandBuffer(); PacketResponseNG resp; SendCommandNG(CMD_HF_MFU_COUNTER_TEAROFF, (uint8_t*)&payload, sizeof(payload)); - if (!WaitForResponseTimeout(CMD_HF_MFU_COUNTER_TEAROFF, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_HF_MFU_COUNTER_TEAROFF, &resp, 2000) == false) { PrintAndLogEx(WARNING, "\ntear off command failed"); continue; } @@ -5898,12 +5923,6 @@ static int CmdHF14AMfUIncr(const char *Cmd) { increment_cmd[i + 2] = (value >> (8 * i)) & 0xff; } - iso14a_card_select_t card; - if (ul_select(&card) == false) { - PrintAndLogEx(FAILED, "failed to select card, exiting..."); - return PM3_ESOFT; - } - uint64_t tagtype = GetHF14AMfU_Type(); uint64_t tags_with_counter_ul = MFU_TT_UL_EV1_48 | MFU_TT_UL_EV1_128 | MFU_TT_UL_EV1; uint64_t tags_with_counter_ntag = MFU_TT_NTAG_213 | MFU_TT_NTAG_213_F | MFU_TT_NTAG_213_C | MFU_TT_NTAG_213_TT | MFU_TT_NTAG_215 | MFU_TT_NTAG_216; @@ -5929,6 +5948,12 @@ static int CmdHF14AMfUIncr(const char *Cmd) { } } + iso14a_card_select_t card; + if (ul_select(&card) == false) { + PrintAndLogEx(FAILED, "failed to select card, exiting..."); + return PM3_ESOFT; + } + uint8_t current_counter[3] = { 0, 0, 0 }; int len = ulev1_readCounter(counter, current_counter, sizeof(current_counter)); if (len != sizeof(current_counter)) { diff --git a/client/src/cmdhfmfu.h b/client/src/cmdhfmfu.h index a365e76d3..ea1c67340 100644 --- a/client/src/cmdhfmfu.h +++ b/client/src/cmdhfmfu.h @@ -53,6 +53,8 @@ int ul_print_type(uint64_t tagtype, uint8_t spaces); void mfu_print_dump(mfu_dump_t *card, uint16_t pages, uint8_t startpage, bool dense_output); int ul_read_uid(uint8_t *uid); int trace_mfuc_try_default_3des_keys(uint8_t **correct_key, int state, uint8_t (*authdata)[16]); +int mfu_get_version_uid(uint8_t *version, uint8_t *uid); +int mfuc_test_authentication_support(void); int CmdHFMFUltra(const char *Cmd); int CmdHF14MfuNDEFRead(const char *Cmd); diff --git a/client/src/cmdhfntag424.c b/client/src/cmdhfntag424.c index 81926f103..188b4eec5 100644 --- a/client/src/cmdhfntag424.c +++ b/client/src/cmdhfntag424.c @@ -34,8 +34,8 @@ #include "crc32.h" #include "cmdhfmfdes.h" -#define NTAG424_MAX_BYTES 412 - +#define NTAG424_MAX_BYTES 412 +#define NTAG424_RESPONSE_LENGTH 2 // NTAG424 commands currently implemented // icenam: should be able to use 14a / msdes to annotate NTAG424 communications @@ -542,19 +542,17 @@ static void ntag424_print_file_settings(uint8_t fileno, const ntag424_file_setti // NTAG424 only have one static application, so we select it here static int ntag424_select_application(void) { - const size_t RESPONSE_LENGTH = 2; uint8_t cmd[] = {0x00, 0xA4, 0x04, 0x0C, 0x07, 0xD2, 0x76, 0x00, 0x00, 0x85, 0x01, 0x01, 0x00 }; - uint8_t resp[RESPONSE_LENGTH]; + uint8_t resp[NTAG424_RESPONSE_LENGTH]; int outlen = 0; - int res; - res = ExchangeAPDU14a(cmd, sizeof(cmd), false, true, resp, RESPONSE_LENGTH, &outlen); + int res = ExchangeAPDU14a(cmd, sizeof(cmd), false, true, resp, NTAG424_RESPONSE_LENGTH, &outlen); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Failed to send apdu"); return res; } - if (outlen != RESPONSE_LENGTH || resp[RESPONSE_LENGTH - 2] != 0x90 || resp[RESPONSE_LENGTH - 1] != 0x00) { + if (outlen != NTAG424_RESPONSE_LENGTH || resp[NTAG424_RESPONSE_LENGTH - 2] != 0x90 || resp[NTAG424_RESPONSE_LENGTH - 1] != 0x00) { PrintAndLogEx(ERR, "Failed to select application"); return PM3_ESOFT; } @@ -762,7 +760,7 @@ static int ntag424_write_data(uint8_t fileno, uint32_t offset, uint32_t num_byte static int ntag424_read_data(uint8_t fileno, uint16_t offset, uint16_t num_bytes, uint8_t *out, ntag424_communication_mode_t comm_mode, ntag424_session_keys_t *session_keys) { uint8_t cmd_header[] = { fileno, - (uint8_t)offset, (uint8_t)(offset << 8), (uint8_t)(offset << 16), // offset + (uint8_t)offset, (uint8_t)(offset >> 8), (uint8_t)(offset >> 16), // offset (uint8_t)num_bytes, (uint8_t)(num_bytes >> 8), 0x00 }; diff --git a/client/src/cmdhfseos.c b/client/src/cmdhfseos.c index 6956f2f2c..69b789d42 100644 --- a/client/src/cmdhfseos.c +++ b/client/src/cmdhfseos.c @@ -562,7 +562,7 @@ static int select_DF_verify(uint8_t *response, uint8_t response_length, uint8_t } // ----------------- MAC Key Generation ----------------- - uint8_t cmac[8]; + uint8_t cmac[16]; uint8_t MAC_key[24] = {0x00}; memcpy(MAC_key, keys[key_index].privMacKey, 16); create_cmac(MAC_key, input, cmac, sizeof(input), encryption_algorithm); @@ -1351,7 +1351,7 @@ static int CmdHfSeosGDF(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); - int key_index = arg_get_int_def(ctx, 1, -1); + int key_index = arg_get_int_def(ctx, 1, 0); CLIParserFree(ctx); return seos_global_df(key_index); diff --git a/client/src/cmdhftesla.c b/client/src/cmdhftesla.c index e1687a170..bf42f84cb 100644 --- a/client/src/cmdhftesla.c +++ b/client/src/cmdhftesla.c @@ -24,6 +24,7 @@ #include "cmdtrace.h" #include "cliparser.h" #include "cmdhf14a.h" +#include "crypto/asn1utils.h" // ASN1 decode / print #include "protocols.h" // definitions of ISO14A/7816 protocol #include "iso7816/apduinfo.h" // GetAPDUCodeDescription #include "commonutil.h" // get_sw @@ -32,6 +33,7 @@ #include "cmdhf14a.h" // apdu chaining #define TIMEOUT 2000 +#define MAX_CERT_SIZE 768 static int CmdHelp(const char *Cmd); @@ -51,17 +53,22 @@ static int CmdHelp(const char *Cmd); */ // TESLA -static int info_hf_tesla(void) { +static int info_hf_tesla(bool parse_certs) { bool activate_field = true; bool keep_field_on = true; - uint8_t response[PM3_CMD_DATA_SIZE]; + uint8_t response[MAX_CERT_SIZE]; // Some cards have pretty large certificates int resplen = 0; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); + PrintAndLogEx(NORMAL, ""); + // --------------- Select TESLA application ---------------- uint8_t aSELECT_AID[80]; int aSELECT_AID_n = 0; - param_gethex_to_eol("00a404000a7465736c614c6f676963", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); + param_gethex_to_eol("00a404000a7465736c614c6f67696300", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { DropField(); @@ -73,7 +80,7 @@ static int info_hf_tesla(void) { if ((resplen < 2) || (sw != ISO7816_OK)) { - param_gethex_to_eol("00a404000af465736c614c6f676963", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); + param_gethex_to_eol("00a404000af465736c614c6f67696300", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { DropField(); @@ -92,9 +99,9 @@ static int info_hf_tesla(void) { // --------------- ECDH public key file reading ---------------- - uint8_t pk[3][65] = {{0}}; + uint8_t pk[4][65] = {{0}}; - for (uint8_t i = 0; i < 3; i++) { + for (uint8_t i = 0; i < 4; i++) { uint8_t aSELECT_PK[5] = {0x80, 0x04, i, 0x00, 0x00}; res = ExchangeAPDU14a(aSELECT_PK, sizeof(aSELECT_PK), activate_field, keep_field_on, response, sizeof(response), &resplen); @@ -110,7 +117,7 @@ static int info_hf_tesla(void) { uint8_t aREAD_FORM_FACTOR[30]; int aREAD_FORM_FACTOR_n = 0; - param_gethex_to_eol("80140000", 0, aREAD_FORM_FACTOR, sizeof(aREAD_FORM_FACTOR), &aREAD_FORM_FACTOR_n); + param_gethex_to_eol("8014000000", 0, aREAD_FORM_FACTOR, sizeof(aREAD_FORM_FACTOR), &aREAD_FORM_FACTOR_n); res = ExchangeAPDU14a(aREAD_FORM_FACTOR, aREAD_FORM_FACTOR_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { DropField(); @@ -149,23 +156,58 @@ static int info_hf_tesla(void) { Set_apdu_in_framing(true); for (uint8_t i = 0; i < 5; i++) { - uint8_t aSELECT_CERT[PM3_CMD_DATA_SIZE] = {0x80, 0x06, i, 0x00, 0x00, 0x00, 0xFF}; - int aSELECT_CERT_n = 7; + // First, read the certificate length + uint8_t aSELECT_CERT[PM3_CMD_DATA_SIZE] = {0x80, 0x06, i, 0x00, 0x04}; + int aSELECT_CERT_n = 5; - res = ExchangeAPDU14a(aSELECT_CERT, aSELECT_CERT_n, activate_field, keep_field_on, response, PM3_CMD_DATA_SIZE, &resplen); + res = ExchangeAPDU14a(aSELECT_CERT, aSELECT_CERT_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not read certificate %i length", i); continue; } sw = get_sw(response, resplen); + bool cert_len_present = false; - if (sw == ISO7816_OK) { - // save CERT for later - uint8_t cert[515] = {0}; - memcpy(cert, response, resplen - 2); - + if (sw == ISO7816_OK && resplen > 3) { + uint16_t cert_len = response[0] << 8 | response[1]; PrintAndLogEx(INFO, "CERT # %i", i); - PrintAndLogEx(INFO, "%s", sprint_hex_inrow(cert, resplen - 2)); + if (cert_len == 0x3082) { + cert_len = (response[2] << 8 | response[3]) + 4; + PrintAndLogEx(INFO, "Length (calculated from ASN.1): %i", cert_len); + } else { + PrintAndLogEx(INFO, "Length (included at start of cert slot): %i", cert_len); + cert_len_present = true; + } + cert_len += 2; // Add 2 bytes for the 9000 at the end + // Read the entire cert (extended length APDU) + aSELECT_CERT[4] = 0x00; + aSELECT_CERT[5] = (cert_len >> 8) & 0xff; + aSELECT_CERT[6] = cert_len & 0xff; + aSELECT_CERT_n = 7; + + res = ExchangeAPDU14a(aSELECT_CERT, aSELECT_CERT_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Could not read certificate %i (return code %i)", i, res); + continue; + } + + sw = get_sw(response, resplen); + if (sw == ISO7816_OK) { + // save CERT for later + uint8_t cert[MAX_CERT_SIZE] = {0}; + memcpy(cert, response, resplen - 2); + + PrintAndLogEx(INFO, "%s", sprint_hex_inrow(cert + (cert_len_present ? 2 : 0), resplen - 2)); + if (parse_certs) { + asn1_print(cert + (cert_len_present ? 2 : 0), cert_len - 2, " "); + } + } + } else if (sw == 0x6f17) { + PrintAndLogEx(INFO, "CERT # %i", i); + PrintAndLogEx(INFO, "No certificate in slot %i", i); + } else { + PrintAndLogEx(ERR, "Could not read certificate %i", i); } } Set_apdu_in_framing(false); @@ -175,30 +217,28 @@ static int info_hf_tesla(void) { // vehicle public key , 16 byte CHALLENGE // 00112233445566778899AABBCCDDEEFF // 0x51 = 81 dec -// param_gethex_to_eol("8011000051 046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C330 00112233445566778899AABBCCDDEEFF", 0, aAUTH, sizeof(aAUTH), &aAUTH_n); - param_gethex_to_eol("8011000051046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C33000112233445566778899AABBCCDDEEFF", 0, aAUTH, sizeof(aAUTH), &aAUTH_n); + // param_gethex_to_eol("8011000051 046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C330 00112233445566778899AABBCCDDEEFF", 0, aAUTH, sizeof(aAUTH), &aAUTH_n); + param_gethex_to_eol("8011000051046F08AE62526ABB5690643458152AC963CF5D7C113949F3C2453D1DDC6E4385B430523524045A22F5747BF236F1B5F60F0EA32DC2B8276D75ACDE9813EF77C33000112233445566778899AABBCCDDEEFF00", 0, aAUTH, sizeof(aAUTH), &aAUTH_n); res = ExchangeAPDU14a(aAUTH, aAUTH_n, activate_field, keep_field_on, response, sizeof(response), &resplen); if (res != PM3_SUCCESS) { - DropField(); - return res; - } + PrintAndLogEx(ERR, "Could not exchange authentication challenge"); + } else { - uint8_t auth[resplen - 2]; + uint8_t auth[resplen - 2]; - sw = get_sw(response, resplen); - if (sw == ISO7816_OK) { - // store CHALLENGE for later - memcpy(auth, response, sizeof(auth)); + sw = get_sw(response, resplen); + if (sw == ISO7816_OK) { + // store CHALLENGE for later + memcpy(auth, response, sizeof(auth)); + } + PrintAndLogEx(INFO, "CHALL......... %s", sprint_hex_inrow(auth, sizeof(auth))); } keep_field_on = false; - DropField(); + DropField(); // No further interaction with the card is needed - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); - PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "PUBLIC KEY"); - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 4; i++) { PrintAndLogEx(INFO, "%d - %s", i, sprint_hex_inrow(pk[i], 65)); } PrintAndLogEx(INFO, "Form factor... %s " NOLF, sprint_hex_inrow(form_factor, sizeof(form_factor))); @@ -207,16 +247,33 @@ static int info_hf_tesla(void) { switch (form_factor_value) { case 0x0001: - PrintAndLogEx(NORMAL, "( card )"); + PrintAndLogEx(NORMAL, "(NXP P60 card)"); + break; + case 0x0002: + PrintAndLogEx(NORMAL, "(NXP P71 card)"); + break; + case 0x0021: + PrintAndLogEx(NORMAL, "(Model 3 fob without passive entry)"); break; case 0x0022: - PrintAndLogEx(NORMAL, "( fob )"); + PrintAndLogEx(NORMAL, "(Model 3 fob with passive entry)"); + break; + case 0x0023: + case 0x0025: + case 0x0026: + PrintAndLogEx(NORMAL, "(Model S fob)"); + break; + case 0x0024: + PrintAndLogEx(NORMAL, "(Model X fob)"); break; case 0x0031: - PrintAndLogEx(NORMAL, "( phone app )"); + PrintAndLogEx(NORMAL, "(Android phone app with NFC)"); + break; + case 0x0032: + PrintAndLogEx(NORMAL, "(iOS phone app with NFC)"); break; default: - PrintAndLogEx(NORMAL, "( unknown )"); + PrintAndLogEx(NORMAL, "(Unknown)"); break; } @@ -224,8 +281,6 @@ static int info_hf_tesla(void) { PrintAndLogEx(INFO, "Version....... %s", sprint_hex_inrow(version, sizeof(version))); } - PrintAndLogEx(INFO, "CHALL......... %s", sprint_hex_inrow(auth, sizeof(auth))); - PrintAndLogEx(INFO, "Fingerprint"); if ((memcmp(pk[0], pk[1], 65) == 0)) { PrintAndLogEx(INFO, " GaussKey detected"); @@ -244,11 +299,14 @@ static int CmdHFTeslaInfo(const char *Cmd) { void *argtable[] = { arg_param_begin, + arg_lit0("p", "parse", "Parse the certificates as ASN.1"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool parse_certs = arg_get_lit(ctx, 1); CLIParserFree(ctx); - return info_hf_tesla(); + return info_hf_tesla(parse_certs); } static int CmdHFTeslaList(const char *Cmd) { diff --git a/client/src/cmdhftexkom.c b/client/src/cmdhftexkom.c index b033050ba..6993d70cf 100644 --- a/client/src/cmdhftexkom.c +++ b/client/src/cmdhftexkom.c @@ -664,7 +664,7 @@ static int CmdHFTexkomReader(const char *Cmd) { SendCommandNG(CMD_HF_ACQ_RAW_ADC, (uint8_t *)&samplesCount, sizeof(uint32_t)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_HF_ACQ_RAW_ADC, &resp, 2500)) { + if (WaitForResponseTimeout(CMD_HF_ACQ_RAW_ADC, &resp, 2500) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } diff --git a/client/src/cmdhfthinfilm.c b/client/src/cmdhfthinfilm.c index 4e3bafdb9..fa7921dc6 100644 --- a/client/src/cmdhfthinfilm.c +++ b/client/src/cmdhfthinfilm.c @@ -187,7 +187,7 @@ int CmdHfThinFilmSim(const char *Cmd) { int ret; while (!(ret = kbd_enter_pressed())) { - if (WaitForResponseTimeout(CMD_HF_THINFILM_SIMULATE, &resp, 500) == 0) { + if (WaitForResponseTimeout(CMD_HF_THINFILM_SIMULATE, &resp, 500) == false) { continue; } diff --git a/client/src/cmdhfxerox.c b/client/src/cmdhfxerox.c index ad13a0dca..0bec9a1d9 100644 --- a/client/src/cmdhfxerox.c +++ b/client/src/cmdhfxerox.c @@ -1013,8 +1013,8 @@ static int CmdHFXeroxRdBl(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHFXeroxList, AlwaysAvailable, "List ISO-14443B history"}, - {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("General") " -----------------------"}, - {"info", CmdHFXeroxInfo, IfPm3Iso14443b, "Short info on Fuji/Xerox tag"}, + {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("Operations") " -----------------------"}, + {"info", CmdHFXeroxInfo, IfPm3Iso14443b, "Tag information"}, {"dump", CmdHFXeroxDump, IfPm3Iso14443b, "Read all memory pages of an Fuji/Xerox tag, save to file"}, {"reader", CmdHFXeroxReader, IfPm3Iso14443b, "Act like a Fuji/Xerox reader"}, {"view", CmdHFXeroxView, AlwaysAvailable, "Display content from tag dump file"}, diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index 7dd2cee2f..047cf387f 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -928,15 +928,19 @@ static int CmdTune(const char *Cmd) { SendCommandNG(CMD_MEASURE_ANTENNA_TUNING, NULL, 0); PacketResponseNG resp; PrintAndLogEx(INPLACE, "% 3i", timeout_max - timeout); - while (!WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING, &resp, 500)) { + + while (WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING, &resp, 500) == false) { + fflush(stdout); if (timeout >= timeout_max) { PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); return PM3_ETIMEOUT; } + timeout++; PrintAndLogEx(INPLACE, "% 3i", timeout_max - timeout); } + PrintAndLogEx(NORMAL, ""); if (resp.status != PM3_SUCCESS) { @@ -1592,7 +1596,7 @@ void pm3_version_short(void) { PrintAndLogEx(NORMAL, " Client.... %s", temp); bool armsrc_mismatch = false; - char *ptr = strstr(payload->versionstr, " os: "); + char *ptr = strstr(payload->versionstr, "OS......... "); if (ptr != NULL) { ptr = strstr(ptr, "\n"); if ((ptr != NULL) && (strlen(g_version_information.armsrc) == 9)) { @@ -1607,7 +1611,7 @@ void pm3_version_short(void) { if (ptr != NULL) { char *ptr_end = strstr(ptr, "\n"); if (ptr_end != NULL) { - uint8_t len = ptr_end - 19 - ptr; + uint8_t len = ptr_end - 12 - ptr; PrintAndLogEx(NORMAL, " Bootrom... %.*s", len, ptr + 12); } } @@ -1617,7 +1621,7 @@ void pm3_version_short(void) { if (ptr != NULL) { char *ptr_end = strstr(ptr, "\n"); if (ptr_end != NULL) { - uint8_t len = ptr_end - 14 - ptr; + uint8_t len = ptr_end - 12 - ptr; PrintAndLogEx(NORMAL, " OS........ %.*s", len, ptr + 12); } } diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index 5a2278859..20940ae6d 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -83,7 +83,7 @@ int lfsim_wait_check(uint32_t cmd) { for (;;) { if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); break; } @@ -435,15 +435,17 @@ int CmdLFCommandRead(const char *Cmd) { i = 10; // 20sec wait loop - while (!WaitForResponseTimeout(CMD_LF_MOD_THEN_ACQ_RAW_ADC, &resp, 2000) && i != 0) { + while (WaitForResponseTimeout(CMD_LF_MOD_THEN_ACQ_RAW_ADC, &resp, 2000) == false && i != 0) { if (verbose) { PrintAndLogEx(NORMAL, "." NOLF); } i--; } + if (verbose) { PrintAndLogEx(NORMAL, ""); } + if (resp.status != PM3_SUCCESS) { PrintAndLogEx(WARNING, "command failed."); return PM3_ESOFT; @@ -595,7 +597,7 @@ int lf_getconfig(sample_config *config) { SendCommandNG(CMD_LF_SAMPLING_GET_CONFIG, NULL, 0); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_SAMPLING_GET_CONFIG, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_LF_SAMPLING_GET_CONFIG, &resp, 2000) == false) { PrintAndLogEx(WARNING, "command execution time out"); return PM3_ETIMEOUT; } @@ -797,10 +799,12 @@ static int lf_read_internal(bool realtime, bool verbose, uint64_t samples) { payload.samples = (samples > MAX_LF_SAMPLES) ? MAX_LF_SAMPLES : samples; SendCommandNG(CMD_LF_ACQ_RAW_ADC, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; + if (is_trigger_threshold_set) { WaitForResponse(CMD_LF_ACQ_RAW_ADC, &resp); } else { - if (!WaitForResponseTimeout(CMD_LF_ACQ_RAW_ADC, &resp, 2500)) { + + if (WaitForResponseTimeout(CMD_LF_ACQ_RAW_ADC, &resp, 2500) == false) { PrintAndLogEx(WARNING, "(lf_read) command execution time out"); return PM3_ETIMEOUT; } diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index 709322e3a..11589d881 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -2013,7 +2013,7 @@ int CmdEM4x05Unlock(const char *Cmd) { PrintAndLogEx(INFO, "----------------------------------------------------------------------------\n"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "Press " _GREEN_("'") " to exit"); + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--------------- " _CYAN_("start") " -----------------------\n"); diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 61cf2db22..a8b199700 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -639,10 +639,8 @@ int em4x50_read(em4x50_data_t *etd, em4x50_word_t *out) { return PM3_ESOFT; } - em4x50_read_data_response_t *o = (em4x50_read_data_response_t *)resp.data.asBytes; - em4x50_word_t words[EM4X50_NO_WORDS] = {0}; - em4x50_prepare_result((uint8_t *)o->words, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words); + em4x50_prepare_result(resp.data.asBytes, etd->addresses & 0xFF, (etd->addresses >> 8) & 0xFF, words); if (out != NULL) { memcpy(out, &words, sizeof(em4x50_word_t) * EM4X50_NO_WORDS); diff --git a/client/src/cmdlfem4x70.c b/client/src/cmdlfem4x70.c index 27672f9f4..8fd80e3d7 100644 --- a/client/src/cmdlfem4x70.c +++ b/client/src/cmdlfem4x70.c @@ -86,18 +86,12 @@ typedef struct _em4x70_tag_info_t { uint8_t Raw[32]; } em4x70_tag_info_t; -typedef struct _em4x70_cmd_input_info_t { - uint8_t use_parity; -} em4x70_cmd_input_info_t; - typedef struct _em4x70_cmd_input_writeblock_t { - uint8_t use_parity; uint8_t block; uint8_t value[2]; } em4x70_cmd_input_writeblock_t; typedef struct _em4x70_cmd_input_brute_t { - uint8_t use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; uint8_t block; @@ -121,12 +115,10 @@ typedef struct _em4x70_cmd_output_brute_t { } em4x70_cmd_output_brute_t; typedef struct _em4x70_cmd_input_unlock_t { - uint8_t use_parity; uint8_t pin[4]; } em4x70_cmd_input_unlock_t; typedef struct _em4x70_cmd_input_auth_t { - uint8_t use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; } em4x70_cmd_input_auth_t; @@ -136,12 +128,10 @@ typedef struct _em4x70_cmd_output_auth_t { } em4x70_cmd_output_auth_t; typedef struct _em4x70_cmd_input_setpin_t { - uint8_t use_parity; uint8_t pin[4]; } em4x70_cmd_input_setpin_t; typedef struct _em4x70_cmd_input_setkey_t { - uint8_t use_parity; ID48LIB_KEY key; } em4x70_cmd_input_setkey_t; @@ -151,7 +141,6 @@ typedef struct _em4x70_cmd_input_recover_t { ID48LIB_NONCE nonce; ID48LIB_FRN frn; ID48LIB_GRN grn; - bool parity; // if true, add parity bit to commands sent to tag bool verify; // if true, tag must be present } em4x70_cmd_input_recover_t; @@ -164,7 +153,6 @@ typedef struct _em4x70_cmd_output_recover_t { } em4x70_cmd_output_recover_t; typedef struct _em4x70_cmd_input_verify_auth_t { - uint8_t use_parity; ID48LIB_NONCE rn; ID48LIB_FRN frn; ID48LIB_GRN grn; @@ -228,12 +216,12 @@ static void em4x70_print_info_result(const em4x70_tag_info_t *data) { PrintAndLogEx(NORMAL, ""); } -static int get_em4x70_info(const em4x70_cmd_input_info_t *opts, em4x70_tag_info_t *data_out) { +static int get_em4x70_info(em4x70_tag_info_t *data_out) { memset(data_out, 0, sizeof(em4x70_tag_info_t)); // TODO: change firmware to use per-cmd structures - em4x70_data_t edata = { .parity = opts->use_parity }; + em4x70_data_t edata = {0}; clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X70_INFO, (uint8_t *)&edata, sizeof(em4x70_data_t)); PacketResponseNG resp; @@ -251,10 +239,10 @@ static int writeblock_em4x70(const em4x70_cmd_input_writeblock_t *opts, em4x70_t memset(data_out, 0, sizeof(em4x70_tag_info_t)); // TODO: change firmware to use per-cmd structures - em4x70_data_t etd = {0}; - etd.address = opts->block; - etd.word = BYTES2UINT16(opts->value); - etd.parity = opts->use_parity; + em4x70_data_t etd = { + .address = opts->block, + .word = BYTES2UINT16(opts->value), + }; clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X70_WRITE, (uint8_t *)&etd, sizeof(etd)); @@ -273,7 +261,6 @@ static int auth_em4x70(const em4x70_cmd_input_auth_t *opts, em4x70_cmd_output_au // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; memcpy(&etd.rnd[0], &opts->rn.rn[0], 7); memcpy(&etd.frnd[0], &opts->frn.frn[0], 4); @@ -298,7 +285,6 @@ static int setkey_em4x70(const em4x70_cmd_input_setkey_t *opts) { // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; memcpy(&etd.crypt_key[0], &opts->key.k[0], 12); clearCommandBuffer(); @@ -315,7 +301,6 @@ static int brute_em4x70(const em4x70_cmd_input_brute_t *opts, em4x70_cmd_output_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; etd.address = opts->block; memcpy(&etd.rnd[0], &opts->rn.rn[0], 7); memcpy(&etd.frnd[0], &opts->frn.frn[0], 4); @@ -366,7 +351,6 @@ static int unlock_em4x70(const em4x70_cmd_input_unlock_t *opts, em4x70_tag_info_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; etd.pin = BYTES2UINT32(opts->pin); clearCommandBuffer(); @@ -386,7 +370,6 @@ static int setpin_em4x70(const em4x70_cmd_input_setpin_t *opts, em4x70_tag_info_ // TODO: change firmware to use per-cmd structures em4x70_data_t etd = {0}; - etd.parity = opts->use_parity; etd.pin = BYTES2UINT32(opts->pin); clearCommandBuffer(); @@ -429,7 +412,6 @@ static int recover_em4x70(const em4x70_cmd_input_recover_t *opts, em4x70_cmd_out static int verify_auth_em4x70(const em4x70_cmd_input_verify_auth_t *opts) { em4x70_cmd_input_auth_t opts_auth = { - .use_parity = opts->use_parity, .rn = opts->rn, .frn = opts->frn, }; @@ -458,24 +440,19 @@ static int CmdEM4x70Info(const char *Cmd) { " ID48 does not use command parity (default).\n" " V4070 and EM4170 do require parity bit.", "lf em 4x70 info\n" - "lf em 4x70 info --par -> adds parity bit to command\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - em4x70_cmd_input_info_t opts = { - .use_parity = arg_get_lit(ctx, 0), - }; CLIParserFree(ctx); // Client command line parsing and validation complete ... now use the helper function em4x70_tag_info_t info; - int result = get_em4x70_info(&opts, &info); + int result = get_em4x70_info(&info); if (result == PM3_ETIMEOUT) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); @@ -494,12 +471,10 @@ static int CmdEM4x70Write(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x70 write", "Write EM4x70\n", "lf em 4x70 write -b 15 -d c0de -> write 'c0de' to block 15\n" - "lf em 4x70 write -b 15 -d c0de --par -> adds parity bit to commands\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_int1("b", "block", "", "block/word address, dec"), arg_str1("d", "data", "", "data, 2 bytes"), arg_param_end @@ -508,12 +483,12 @@ static int CmdEM4x70Write(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_writeblock_t opts = { - .use_parity = arg_get_lit(ctx, 1), - .block = arg_get_int_def(ctx, 2, 1), + .block = arg_get_int_def(ctx, 1, 1), .value = {0}, // hex value macro exits function, so cannot be initialized here }; + int value_len = 0; - CLIGetHexWithReturn(ctx, 3, opts.value, &value_len); + CLIGetHexWithReturn(ctx, 2, opts.value, &value_len); CLIParserFree(ctx); if (opts.block >= EM4X70_NUM_BLOCKS) { @@ -555,7 +530,6 @@ static int CmdEM4x70Brute(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_int1("b", "block", "", "block/word address, dec"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), @@ -565,8 +539,7 @@ static int CmdEM4x70Brute(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_brute_t opts = { - .use_parity = arg_get_lit(ctx, 1), - .block = arg_get_int_def(ctx, 2, 0), + .block = arg_get_int_def(ctx, 1, 0), .rn = {{0}}, // hex value macro exits function, so cannot be initialized here .frn = {{0}}, // hex value macro exits function, so cannot be initialized here .partial_key_start = {0}, // hex value macro exits function, so cannot be initialized here @@ -579,15 +552,15 @@ static int CmdEM4x70Brute(const char *Cmd) { } int rnd_len = 7; - CLIGetHexWithReturn(ctx, 3, opts.rn.rn, &rnd_len); + CLIGetHexWithReturn(ctx, 2, opts.rn.rn, &rnd_len); int frnd_len = 4; - CLIGetHexWithReturn(ctx, 4, opts.frn.frn, &frnd_len); + CLIGetHexWithReturn(ctx, 3, opts.frn.frn, &frnd_len); // would prefer to use above CLIGetHexWithReturn(), but it does not // appear to support optional arguments. uint32_t start_key = 0; - int res = arg_get_u32_hexstr_def_nlen(ctx, 5, 0, &start_key, 2, true); // this stores in NATIVE ENDIAN + int res = arg_get_u32_hexstr_def_nlen(ctx, 4, 0, &start_key, 2, true); // this stores in NATIVE ENDIAN if (res == 2) { PrintAndLogEx(WARNING, "start key parameter must be in range [0, FFFF]"); CLIParserFree(ctx); @@ -614,7 +587,7 @@ static int CmdEM4x70Brute(const char *Cmd) { em4x70_cmd_output_brute_t data; int result = brute_em4x70(&opts, &data); if (result == PM3_EOPABORTED) { - PrintAndLogEx(DEBUG, "User aborted"); + PrintAndLogEx(DEBUG, "\naborted via keyboard!"); } else if (result == PM3_ETIMEOUT) { PrintAndLogEx(WARNING, "\nNo response from Proxmark3. Aborting..."); } else if (result == PM3_SUCCESS) { @@ -635,11 +608,9 @@ static int CmdEM4x70Unlock(const char *Cmd) { " AAAAAAAA\n" " 00000000\n", "lf em 4x70 unlock -p 11223344 -> Unlock with PIN\n" - "lf em 4x70 unlock -p 11223344 --par -> Unlock with PIN using parity commands\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1("p", "pin", "", "pin, 4 bytes"), arg_param_end }; @@ -647,11 +618,10 @@ static int CmdEM4x70Unlock(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_unlock_t opts = { - .use_parity = arg_get_lit(ctx, 1), .pin = {0}, // hex value macro exits function, so cannot be initialized here }; int pin_len = 0; - CLIGetHexWithReturn(ctx, 2, opts.pin, &pin_len); + CLIGetHexWithReturn(ctx, 1, opts.pin, &pin_len); CLIParserFree(ctx); if (pin_len != 4) { @@ -691,7 +661,6 @@ static int CmdEM4x70Auth(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), arg_param_end @@ -700,15 +669,14 @@ static int CmdEM4x70Auth(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); em4x70_cmd_input_auth_t opts = { - .use_parity = arg_get_lit(ctx, 1), .rn = {{0}}, // hex value macro exits function, so cannot be initialized here .frn = {{0}}, // hex value macro exits function, so cannot be initialized here }; int rn_len = 7; - CLIGetHexWithReturn(ctx, 2, opts.rn.rn, &rn_len); + CLIGetHexWithReturn(ctx, 1, opts.rn.rn, &rn_len); int frn_len = 4; - CLIGetHexWithReturn(ctx, 3, opts.frn.frn, &frn_len); + CLIGetHexWithReturn(ctx, 2, opts.frn.frn, &frn_len); CLIParserFree(ctx); if (rn_len != 7) { PrintAndLogEx(FAILED, "Random number length must be 7 bytes, got %d", rn_len); @@ -738,23 +706,19 @@ static int CmdEM4x70SetPIN(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x70 setpin", "Write new PIN\n", "lf em 4x70 setpin -p 11223344 -> Write new PIN\n" - "lf em 4x70 setpin -p 11223344 --par -> Write new PIN using parity commands\n" ); void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1("p", "pin", "", "pin, 4 bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - em4x70_cmd_input_setpin_t opts = { - .use_parity = arg_get_lit(ctx, 1), .pin = {0}, // hex value macro exits function, so cannot be initialized here }; int pin_len = 0; - CLIGetHexWithReturn(ctx, 2, opts.pin, &pin_len); + CLIGetHexWithReturn(ctx, 1, opts.pin, &pin_len); CLIParserFree(ctx); if (pin_len != 4) { @@ -789,19 +753,16 @@ static int CmdEM4x70SetKey(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1("k", "key", "", "Key as 12 hex bytes"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - em4x70_cmd_input_setkey_t opts = { - .use_parity = arg_get_lit(ctx, 1), .key = {{0}}, // hex value macro exits function, so cannot be initialized here }; int key_len = 12; - CLIGetHexWithReturn(ctx, 2, opts.key.k, &key_len); + CLIGetHexWithReturn(ctx, 1, opts.key.k, &key_len); CLIParserFree(ctx); if (key_len != 12) { PrintAndLogEx(FAILED, "Key length must be 12 bytes, got %d", key_len); @@ -823,7 +784,6 @@ static int CmdEM4x70SetKey(const char *Cmd) { // Now verify authentication using the new key, to ensure it was correctly written em4x70_cmd_input_verify_auth_t opts_v = { - .use_parity = opts.use_parity, //.rn = opts_auth.rn, //.frn = opts_auth.frn, //.grn = {{0}}, @@ -909,7 +869,6 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_ void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1("k", "key", "", "Key as 6 hex bytes"), arg_str1(NULL, "rnd", "", "Random 56-bit"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes"), @@ -931,17 +890,16 @@ static int CmdEM4x70Recover_ParseArgs(const char *Cmd, em4x70_cmd_input_recover_ // if all OK so far, convert to internal data structure if (PM3_SUCCESS == result) { // magic number == index in argtable above. Fragile technique! - out_results->parity = arg_get_lit(ctx, 1); - if (CLIParamHexToBuf(arg_get_str(ctx, 2), &(out_results->key.k[0]), 12, &key_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 1), &(out_results->key.k[0]), 12, &key_len)) { result = PM3_ESOFT; } - if (CLIParamHexToBuf(arg_get_str(ctx, 3), &(out_results->nonce.rn[0]), 7, &rnd_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 2), &(out_results->nonce.rn[0]), 7, &rnd_len)) { result = PM3_ESOFT; } - if (CLIParamHexToBuf(arg_get_str(ctx, 4), &(out_results->frn.frn[0]), 4, &frn_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 3), &(out_results->frn.frn[0]), 4, &frn_len)) { result = PM3_ESOFT; } - if (CLIParamHexToBuf(arg_get_str(ctx, 5), &(out_results->grn.grn[0]), 3, &grn_len)) { + if (CLIParamHexToBuf(arg_get_str(ctx, 4), &(out_results->grn.grn[0]), 3, &grn_len)) { result = PM3_ESOFT; } //out_results->verify = arg_get_lit(ctx, 6); @@ -1112,7 +1070,6 @@ static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd, em4x70_cmd_input_reco void *argtable[] = { arg_param_begin, - arg_lit0(NULL, "par", "Add parity bit when sending commands"), arg_str1(NULL, "rnd", "", "Random 56-bit from known-good authentication"), arg_str1(NULL, "frn", "", "F(RN) 28-bit as 4 hex bytes from known-good authentication"), arg_str1(NULL, "grn", "", "G(RN) 20-bit as 3 hex bytes from known-good authentication"), @@ -1125,10 +1082,9 @@ static int CmdEM4x70AutoRecover_ParseArgs(const char *Cmd, em4x70_cmd_input_reco int rnd_len = 0; // must be 7 bytes hex data int frn_len = 0; // must be 4 bytes hex data int grn_len = 0; // must be 3 bytes hex data - out_results->parity = arg_get_lit(ctx, 1); - CLIGetHexWithReturn(ctx, 2, out_results->nonce.rn, &rnd_len); - CLIGetHexWithReturn(ctx, 3, out_results->frn.frn, &frn_len); - CLIGetHexWithReturn(ctx, 4, out_results->grn.grn, &grn_len); + CLIGetHexWithReturn(ctx, 1, out_results->nonce.rn, &rnd_len); + CLIGetHexWithReturn(ctx, 2, out_results->frn.frn, &frn_len); + CLIGetHexWithReturn(ctx, 3, out_results->grn.grn, &grn_len); CLIParserFree(ctx); if (rnd_len != 7) { @@ -1190,7 +1146,6 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 auth --rnd %s --frn %s"), rnd_string, frn_string); em4x70_cmd_input_auth_t opts_auth = { - .use_parity = opts.parity, .rn = opts.nonce, .frn = opts.frn, }; @@ -1234,7 +1189,6 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 write -b %d -d 0000"), block); em4x70_cmd_input_writeblock_t opt_write_zeros = { - .use_parity = opts.parity, .block = block, .value = {0x00, 0x00}, }; @@ -1255,7 +1209,6 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 brute -b %d --rnd %s --frn %s"), block, rnd_string, frn_string); em4x70_cmd_input_brute_t opts_brute = { - .use_parity = opts.parity, .block = block, .rn = opts.nonce, .frn = opts.frn, @@ -1294,7 +1247,6 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(HINT, "Hint: " _YELLOW_("lf em 4x70 write -b %d -d %02X%02X"), block, brute.partial_key[0], brute.partial_key[1]); em4x70_cmd_input_writeblock_t opt_write_zeros2 = { - .use_parity = opts.parity, .block = block, .value = {brute.partial_key[0], brute.partial_key[1]}, }; @@ -1354,7 +1306,6 @@ static int CmdEM4x70AutoRecover(const char *Cmd) { PrintAndLogEx(INFO, "Step 6. Verify which potential key is actually on the tag"); em4x70_cmd_input_verify_auth_t opts_v = { - .use_parity = opts.parity, //.rn = {{0}}, //.frn = {{0}}, //.grn = {{0}}, @@ -1521,7 +1472,7 @@ static int CmdHelp(const char *Cmd); static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"brute", CmdEM4x70Brute, IfPm3EM4x70, "Bruteforce EM4X70 to find partial key"}, - {"info", CmdEM4x70Info, IfPm3EM4x70, "Tag information EM4x70"}, + {"info", CmdEM4x70Info, IfPm3EM4x70, "Tag information"}, {"write", CmdEM4x70Write, IfPm3EM4x70, "Write EM4x70"}, {"unlock", CmdEM4x70Unlock, IfPm3EM4x70, "Unlock EM4x70 for writing"}, {"auth", CmdEM4x70Auth, IfPm3EM4x70, "Authenticate EM4x70"}, @@ -1554,9 +1505,8 @@ int CmdLFEM4X70(const char *Cmd) { // Use helper function `get_em4x70_info()` if wanting to limit / avoid output. bool detect_4x70_block(void) { em4x70_tag_info_t info; - em4x70_cmd_input_info_t opts = { 0 }; - int result = get_em4x70_info(&opts, &info); + int result = get_em4x70_info(&info); if (result == PM3_ETIMEOUT) { // consider removing this output? PrintAndLogEx(WARNING, "timeout while waiting for reply"); diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 8e3a0b808..63c25ff99 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -498,7 +498,7 @@ static int ht2_check_dictionary(uint32_t key_count, uint8_t *keys, uint8_t keyl if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(INFO, "User aborted"); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); break; } @@ -2468,7 +2468,7 @@ static command_t CommandTable[] = { {"hts", CmdLFHitagS, AlwaysAvailable, "{ Hitag S/8211 operations }"}, {"htu", CmdLFHitagU, AlwaysAvailable, "{ Hitag µ/8265 operations }"}, {"-----------", CmdHelp, IfPm3Hitag, "------------------------ " _CYAN_("General") " ------------------------"}, - {"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag 2 tag information"}, + {"info", CmdLFHitagInfo, IfPm3Hitag, "Tag information"}, {"reader", CmdLFHitagReader, IfPm3Hitag, "Act like a Hitag 2 reader"}, {"test", CmdLFHitag2Selftest, AlwaysAvailable, "Perform self tests"}, {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Operations") " -----------------------"}, diff --git a/client/src/cmdlfpac.c b/client/src/cmdlfpac.c index 757fd9051..8d9dbebf4 100644 --- a/client/src/cmdlfpac.c +++ b/client/src/cmdlfpac.c @@ -35,6 +35,9 @@ #include "cmdlfem4x05.h" // #include "cliparser.h" +// 8 bytes + null terminator +#define PAC_ID_LEN (8 + 1) + static int CmdHelp(const char *Cmd); // PAC_8byte format: preamble (8 mark/idle bits), ascii STX (02), ascii '2' (32), ascii '0' (30), ascii bytes 0..7 (cardid), then xor checksum of cardid bytes @@ -160,12 +163,13 @@ int demodPac(bool verbose) { uint32_t raw3 = bytebits_to_byte(g_DemodBuffer + 64, 32); uint32_t raw4 = bytebits_to_byte(g_DemodBuffer + 96, 32); - const size_t idLen = 9; // 8 bytes + null terminator - uint8_t cardid[idLen]; + // 8 bytes + null terminator + uint8_t cardid[PAC_ID_LEN]; int retval = pac_buf_to_cardid(g_DemodBuffer, g_DemodBufferLen, cardid, sizeof(cardid)); - if (retval == PM3_SUCCESS) + if (retval == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "PAC/Stanley - Card: " _GREEN_("%s") ", Raw: %08X%08X%08X%08X", cardid, raw1, raw2, raw3, raw4); + } return retval; } diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index 7da9967f2..96184c87f 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -466,7 +466,7 @@ int clone_t55xx_tag(uint32_t *blockdata, uint8_t numblocks) { ng.flags = 0; SendCommandNG(CMD_LF_T55XX_WRITEBL, (uint8_t *)&ng, sizeof(ng)); - if (!WaitForResponseTimeout(CMD_LF_T55XX_WRITEBL, &resp, T55XX_WRITE_TIMEOUT)) { + if (WaitForResponseTimeout(CMD_LF_T55XX_WRITEBL, &resp, T55XX_WRITE_TIMEOUT) == false) { PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); return PM3_ETIMEOUT; } @@ -664,7 +664,7 @@ int t55xxWrite(uint8_t block, bool page1, bool usepwd, bool testMode, uint32_t p PacketResponseNG resp; clearCommandBuffer(); SendCommandNG(CMD_LF_T55XX_WRITEBL, (uint8_t *)&ng, sizeof(ng)); - if (!WaitForResponseTimeout(CMD_LF_T55XX_WRITEBL, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_LF_T55XX_WRITEBL, &resp, 2000) == false) { PrintAndLogEx(ERR, "Error occurred, device did not ACK write operation."); return PM3_ETIMEOUT; } @@ -1992,7 +1992,7 @@ static int CmdT55xxDangerousRaw(const char *Cmd) { PacketResponseNG resp; clearCommandBuffer(); SendCommandNG(CMD_LF_T55XX_DANGERRAW, (uint8_t *)&ng, sizeof(ng)); - if (!WaitForResponseTimeout(CMD_LF_T55XX_DANGERRAW, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_LF_T55XX_DANGERRAW, &resp, 2000) == false) { PrintAndLogEx(ERR, "Error occurred, device did not ACK write operation."); return PM3_ETIMEOUT; } @@ -2840,7 +2840,7 @@ bool AcquireData(uint8_t page, uint8_t block, bool pwdmode, uint32_t password, u clearCommandBuffer(); SendCommandNG(CMD_LF_T55XX_READBL, (uint8_t *)&payload, sizeof(payload)); - if (!WaitForResponseTimeout(CMD_LF_T55XX_READBL, NULL, 2500)) { + if (WaitForResponseTimeout(CMD_LF_T55XX_READBL, NULL, 2500) == false) { PrintAndLogEx(WARNING, "command execution time out"); return false; } @@ -3435,7 +3435,7 @@ static int CmdT55xxChkPwds(const char *Cmd) { PacketResponseNG resp; uint8_t timeout = 0; - while (!WaitForResponseTimeout(CMD_LF_T55XX_CHK_PWDS, &resp, 2000)) { + while (WaitForResponseTimeout(CMD_LF_T55XX_CHK_PWDS, &resp, 2000) == false) { timeout++; PrintAndLogEx(NORMAL, "." NOLF); if (timeout > 180) { diff --git a/client/src/cmdlfviking.c b/client/src/cmdlfviking.c index 83f34862a..6807e828c 100644 --- a/client/src/cmdlfviking.c +++ b/client/src/cmdlfviking.c @@ -172,7 +172,7 @@ static int CmdVikingClone(const char *Cmd) { SendCommandNG(CMD_LF_VIKING_CLONE, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_VIKING_CLONE, &resp, T55XX_WRITE_TIMEOUT)) { + if (WaitForResponseTimeout(CMD_LF_VIKING_CLONE, &resp, T55XX_WRITE_TIMEOUT) == false) { PrintAndLogEx(ERR, "Error occurred, device did not respond during write operation."); return PM3_ETIMEOUT; } diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index 7421b3f58..fc628b947 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -20,11 +20,15 @@ #include #include - +#include // spinlock +#include // system #include "ui.h" #include "comms.h" #include "util_posix.h" // msleep +#if defined(__MACH__) && defined(__APPLE__) +# include "pthread_spin_lock_shim.h" // spinlock shim for OSX .. +#endif #define MAX_PM3_INPUT_ARGS_LENGTH 4096 @@ -219,6 +223,30 @@ void CmdsHelp(const command_t Commands[]) { PrintAndLogEx(NORMAL, ""); } +static int execute_system_command(const char *command) { + + pthread_spinlock_t sycmd_spinlock; + pthread_spin_init(&sycmd_spinlock, 0); + pthread_spin_lock(&sycmd_spinlock); + + int ret; + +#if defined(_WIN32) + char wrapped_command[255]; + strncat(wrapped_command, "cmd /C \"", 9); + strncat(wrapped_command, command, strlen(command)); + strncat(wrapped_command, "\"", 2); + + ret = system(wrapped_command); +#else + ret = system(command); +#endif + pthread_spin_unlock(&sycmd_spinlock); + pthread_spin_destroy(&sycmd_spinlock); + return ret; +} + + int CmdsParse(const command_t Commands[], const char *Cmd) { if (g_session.client_exe_delay != 0) { @@ -267,6 +295,9 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { return PM3_SUCCESS; } + if (Cmd[0] == '!') { + return execute_system_command(Cmd + 1); + } char cmd_name[128] = {0}; memset(cmd_name, 0, sizeof(cmd_name)); @@ -287,8 +318,9 @@ int CmdsParse(const command_t Commands[], const char *Cmd) { } // Comment - if (cmd_name[0] == '#') + if (cmd_name[0] == '#') { return PM3_SUCCESS; + } // find args, check for -h / --help int tmplen = len; diff --git a/client/src/cmdsmartcard.c b/client/src/cmdsmartcard.c index ed515a04a..46b467f94 100644 --- a/client/src/cmdsmartcard.c +++ b/client/src/cmdsmartcard.c @@ -663,7 +663,7 @@ static int CmdSmartUpgrade(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_SMART_UPLOAD, (uint8_t *)&upload, sizeof(upload)); - if (!WaitForResponseTimeout(CMD_SMART_UPLOAD, &resp, 2000)) { + if (WaitForResponseTimeout(CMD_SMART_UPLOAD, &resp, 2000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); free(firmware); return PM3_ETIMEOUT; @@ -695,7 +695,7 @@ static int CmdSmartUpgrade(const char *Cmd) { free(firmware); SendCommandNG(CMD_SMART_UPGRADE, (uint8_t *)&payload, sizeof(payload)); - if (!WaitForResponseTimeout(CMD_SMART_UPGRADE, &resp, 2500)) { + if (WaitForResponseTimeout(CMD_SMART_UPGRADE, &resp, 2500) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply"); return PM3_ETIMEOUT; } @@ -876,7 +876,7 @@ static int CmdSmartSetClock(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_SMART_SETCLOCK, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_SMART_SETCLOCK, &resp, 2500)) { + if (WaitForResponseTimeout(CMD_SMART_SETCLOCK, &resp, 2500) == false) { PrintAndLogEx(WARNING, "smart card select failed"); return PM3_ETIMEOUT; } diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 0e83bf128..0f3242345 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -181,7 +181,7 @@ static uint16_t extractChallenges(uint16_t tracepos, uint16_t traceLen, uint8_t } */ - // extract MFU-C KEY when written. + // extract UL-C KEY when written. switch (frame[0]) { case MIFARE_ULC_AUTH_1: { diff --git a/client/src/cmdusart.c b/client/src/cmdusart.c index 357d20c32..b3e2bdc5d 100644 --- a/client/src/cmdusart.c +++ b/client/src/cmdusart.c @@ -35,7 +35,7 @@ static int usart_tx(uint8_t *data, size_t len) { clearCommandBuffer(); SendCommandNG(CMD_USART_TX, data, len); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_USART_TX, &resp, 1000)) { + if (WaitForResponseTimeout(CMD_USART_TX, &resp, 1000) == false) { return PM3_ETIMEOUT; } return resp.status; @@ -49,7 +49,7 @@ static int usart_rx(uint8_t *data, size_t *len, uint32_t waittime) { payload.waittime = waittime; SendCommandNG(CMD_USART_RX, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_USART_RX, &resp, waittime + 500)) { + if (WaitForResponseTimeout(CMD_USART_RX, &resp, waittime + 500) == false) { return PM3_ETIMEOUT; } if (resp.status == PM3_SUCCESS) { @@ -99,7 +99,7 @@ static int set_usart_config(uint32_t baudrate, uint8_t parity) { payload.parity = parity; SendCommandNG(CMD_USART_CONFIG, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_USART_CONFIG, &resp, 1000)) { + if (WaitForResponseTimeout(CMD_USART_CONFIG, &resp, 1000) == false) { return PM3_ETIMEOUT; } return resp.status; diff --git a/client/src/cmdwiegand.c b/client/src/cmdwiegand.c index d29521d53..c2d6bb3b1 100644 --- a/client/src/cmdwiegand.c +++ b/client/src/cmdwiegand.c @@ -201,7 +201,7 @@ int CmdWiegandDecode(const char *Cmd) { PrintAndLogEx(ERR, "Binary string contains none <0|1> chars"); return PM3_EINVARG; } - PrintAndLogEx(INFO, "Input bin len... %d", blen); + PrintAndLogEx(INFO, "#bits... %d", blen); } else if (plen) { diff --git a/client/src/comms.c b/client/src/comms.c index e2684100f..2ded55ebc 100644 --- a/client/src/comms.c +++ b/client/src/comms.c @@ -871,7 +871,7 @@ int TestProxmark(pm3_device_t *dev) { #endif PacketResponseNG resp; - if (WaitForResponseTimeoutW(CMD_PING, &resp, timeout, false) == 0) { + if (WaitForResponseTimeoutW(CMD_PING, &resp, timeout, false) == false) { return PM3_ETIMEOUT; } @@ -881,7 +881,7 @@ int TestProxmark(pm3_device_t *dev) { } SendCommandNG(CMD_CAPABILITIES, NULL, 0); - if (WaitForResponseTimeoutW(CMD_CAPABILITIES, &resp, 1000, false) == 0) { + if (WaitForResponseTimeoutW(CMD_CAPABILITIES, &resp, 1000, false) == false) { return PM3_ETIMEOUT; } diff --git a/client/src/crypto/libpcrypto.c b/client/src/crypto/libpcrypto.c index 031005ce0..41f7574c5 100644 --- a/client/src/crypto/libpcrypto.c +++ b/client/src/crypto/libpcrypto.c @@ -55,13 +55,15 @@ void des_decrypt(void *out, const void *in, const void *key) { } void des_encrypt_ecb(void *out, const void *in, const int length, const void *key) { - for (int i = 0; i < length; i += 8) + for (int i = 0; i < length; i += 8) { des_encrypt((uint8_t *)out + i, (uint8_t *)in + i, key); + } } void des_decrypt_ecb(void *out, const void *in, const int length, const void *key) { - for (int i = 0; i < length; i += 8) + for (int i = 0; i < length; i += 8) { des_decrypt((uint8_t *)out + i, (uint8_t *)in + i, key); + } } void des_encrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv) { diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index b2aa524ac..b663af706 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -484,7 +484,7 @@ int EMVSearch(Iso7816CommandChannel channel, bool ActivateField, bool LeaveField for (int i = 0; i < ARRAYLEN(AIDlist); i ++) { if (kbd_enter_pressed()) { - PrintAndLogEx(INFO, "user aborted..."); + PrintAndLogEx(WARNING, "\naborted via keyboard!"); break; } diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 128826c10..f4c76f049 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -72,9 +72,7 @@ DumpFileType_t get_filetype(const char *filename) { size_t len = strlen(filename); if (len > 4) { // check if valid file extension and attempt to load data - char s[FILE_PATH_SIZE]; - memset(s, 0, sizeof(s)); - memcpy(s, filename, len); + char *s = str_dup(filename); str_lower(s); if (str_endswith(s, "bin")) { @@ -97,6 +95,8 @@ DumpFileType_t get_filetype(const char *filename) { // log is text // .pm3 is text values of signal data } + + free(s); } return o; } @@ -165,6 +165,7 @@ static char *filenamemcopy(const char *preferredName, const char *suffix) { char *fileName = (char *) calloc(strlen(preferredName) + strlen(suffix) + 1, sizeof(uint8_t)); if (fileName == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return NULL; } @@ -993,8 +994,8 @@ int loadFile_safeEx(const char *preferredName, const char *suffix, void **pdata, } *pdata = calloc(fsize, sizeof(uint8_t)); - if (!*pdata) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); + if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); fclose(f); return PM3_EMALLOC; } @@ -1044,8 +1045,8 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { } *pdata = calloc(fsize, sizeof(uint8_t)); - if (!*pdata) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); + if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); fclose(f); return PM3_EMALLOC; } @@ -1091,6 +1092,7 @@ int loadFileEML_safe(const char *preferredName, void **pdata, size_t *datalen) { uint8_t *newdump = realloc(*pdata, counter); if (newdump == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(*pdata); return PM3_EMALLOC; } else { @@ -1366,8 +1368,8 @@ int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { } *pdata = calloc(fsize, sizeof(uint8_t)); - if (!*pdata) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); + if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); fclose(f); return PM3_EMALLOC; } @@ -1414,6 +1416,7 @@ int loadFileMCT_safe(const char *preferredName, void **pdata, size_t *datalen) { uint8_t *newdump = realloc(*pdata, counter); if (newdump == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(*pdata); return PM3_EMALLOC; } else { @@ -2218,6 +2221,8 @@ int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, u return loadFileDICTIONARYEx(preferredName, data, 0, datalen, keylen, keycnt, 0, NULL, true); } +// this function handles exceptional large dictionaries, +// using start position and end position parameters. int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatalen, size_t *datalen, uint8_t keylen, uint32_t *keycnt, size_t startFilePosition, size_t *endFilePosition, bool verbose) { @@ -2243,17 +2248,17 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale int retval = PM3_SUCCESS; FILE *f = fopen(path, "r"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); - retval = PM3_EFILE; - goto out; + free(path); + return PM3_EFILE; } if (startFilePosition) { if (fseek(f, startFilePosition, SEEK_SET) < 0) { fclose(f); - retval = PM3_EFILE; - goto out; + free(path); + return PM3_EFILE; } } @@ -2261,6 +2266,7 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale // read file while (!feof(f)) { + long filepos = ftell(f); if (!fgets(line, sizeof(line), f)) { @@ -2318,7 +2324,7 @@ int loadFileDICTIONARYEx(const char *preferredName, void *data, size_t maxdatale if (keycnt) { *keycnt = vkeycnt; } -out: + free(path); return retval; } @@ -2343,12 +2349,11 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo // mf desfire == 3des3k 24 bytes // iclass == 8 bytes // default to 6 bytes. - if (keylen != 4 && keylen != 5 && keylen != 6 && keylen != 8 && keylen != 16 && keylen != 24) { + if (keylen != 4 && keylen != 5 && keylen != 6 && keylen != 8 && keylen != 12 && keylen != 16 && keylen != 24) { keylen = 6; } - size_t mem_size; - size_t block_size = 10 * keylen; + size_t block_size = 1000 * keylen; // double up since its chars keylen <<= 1; @@ -2358,13 +2363,14 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo // allocate some space for the dictionary *pdata = calloc(block_size, sizeof(uint8_t)); if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); free(path); return PM3_EFILE; } - mem_size = block_size; + size_t mem_size = block_size; FILE *f = fopen(path, "r"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); retval = PM3_EFILE; goto out; @@ -2377,9 +2383,10 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo if ((*keycnt * (keylen >> 1)) >= mem_size) { mem_size += block_size; - *pdata = realloc(*pdata, mem_size); + *pdata = realloc(*pdata, mem_size); if (*pdata == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); retval = PM3_EFILE; fclose(f); goto out; @@ -2395,6 +2402,7 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo // remove newline/linefeed str_cleanrn(line, strlen(line)); + str_trim(line); // smaller keys than expected is skipped if (strlen(line) < keylen) { @@ -2412,6 +2420,7 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo // larger keys than expected is skipped if (strlen(line) > keylen) { + PrintAndLogEx(INFO, "too long line (%zu) ... %s", strlen(line), line); continue; } @@ -2419,10 +2428,9 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo continue; } - if (hex_to_bytes( - line, - (uint8_t *)*pdata + (*keycnt * (keylen >> 1)), - keylen >> 1) != (keylen >> 1)) { + int ret = hex_to_bytes(line, (uint8_t *)*pdata + (*keycnt * (keylen >> 1)), keylen >> 1); + if (ret != (keylen >> 1)) { + PrintAndLogEx(INFO, "hex to bytes wrong %i", ret); continue; } @@ -2441,16 +2449,16 @@ out: return retval; } -int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen) { +int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen, bool verbose) { char *path; int res = searchFile(&path, RESOURCES_SUBDIR, preferredName, suffix, false); if (res != PM3_SUCCESS) { - return PM3_EFILE; + return PM3_ENOFILE; } FILE *f = fopen(path, "rb"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; @@ -2473,7 +2481,7 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya *keya = calloc(fsize, sizeof(uint8_t)); if (*keya == NULL) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); fclose(f); free(path); return PM3_EMALLOC; @@ -2483,7 +2491,7 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya *keyb = calloc(fsize, sizeof(uint8_t)); if (*keyb == NULL) { - PrintAndLogEx(FAILED, "error, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); fclose(f); free(*keya); free(path); @@ -2493,7 +2501,9 @@ int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya *blen = fread(*keyb, 1, fsize, f); fclose(f); - PrintAndLogEx(SUCCESS, "Loaded binary key file `" _YELLOW_("%s") "`", path); + if (verbose) { + PrintAndLogEx(SUCCESS, "Loaded binary key file `" _YELLOW_("%s") "`", path); + } free(path); return PM3_SUCCESS; } @@ -2568,7 +2578,7 @@ int detect_nfc_dump_format(const char *preferredName, nfc_df_e *dump_type, bool } FILE *f = fopen(path, "r"); - if (!f) { + if (f == NULL) { PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); free(path); return PM3_EFILE; @@ -2663,6 +2673,7 @@ static int convert_plain_mfu_dump(uint8_t **dump, size_t *dumplen, bool verbose) mfu_dump_t *mfu = (mfu_dump_t *) calloc(sizeof(mfu_dump_t), sizeof(uint8_t)); if (mfu == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -2700,6 +2711,7 @@ static int convert_old_mfu_dump(uint8_t **dump, size_t *dumplen, bool verbose) { mfu_dump_t *mfu_dump = (mfu_dump_t *) calloc(sizeof(mfu_dump_t), sizeof(uint8_t)); if (mfu_dump == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -2846,6 +2858,7 @@ static int searchFinalFile(char **foundpath, const char *pm3dir, const char *sea // explicit absolute (/) or relative path (./) => try only to match it directly char *filename = calloc(strlen(searchname) + 1, sizeof(char)); if (filename == NULL) { + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -3031,18 +3044,22 @@ out: int searchFile(char **foundpath, const char *pm3dir, const char *searchname, const char *suffix, bool silent) { - if (foundpath == NULL) + if (foundpath == NULL) { return PM3_EINVARG; + } - if (searchname == NULL || strlen(searchname) == 0) + if (searchname == NULL || strlen(searchname) == 0) { return PM3_EINVARG; + } - if (is_directory(searchname)) + if (is_directory(searchname)) { return PM3_EINVARG; + } char *filename = filenamemcopy(searchname, suffix); - if (filename == NULL) + if (filename == NULL) { return PM3_EMALLOC; + } if (strlen(filename) == 0) { free(filename); @@ -3059,6 +3076,82 @@ int searchFile(char **foundpath, const char *pm3dir, const char *searchname, con return res; } +/** + * Inserts a line into a text file only if it does not already exist. + * Returns PM3_SUCCES or, PM3_EFILE; + * + * @param filepath Path to the file. + * @param line Line to insert (should not contain a trailing newline). + */ +int insert_line_if_not_exists(const char *preferredName, const char *keystr) { + + char *path; + int res = searchFile(&path, DICTIONARIES_SUBDIR, preferredName, ".dic", false); + if (res != PM3_SUCCESS) { + return PM3_EFILE; + } + + FILE *f = fopen(path, "r"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + + // Maximum line length we assume (adjust as necessary for your use case) + char line[255]; + bool key_exists = false; + + char *keystrdup = str_dup(keystr); + str_upper(keystrdup); + + // First pass: check if the line exists + while (fgets(line, sizeof(line), f)) { + + // The line start with # is comment, skip + if (line[0] == '#') { + continue; + } + + // Remove trailing newline for comparison + line[strcspn(line, "\n")] = '\0'; + + // UPPER CASE + str_upper(line); + + key_exists = str_startswith(line, keystrdup); + if (key_exists) { + fclose(f); + free(path); + PrintAndLogEx(INFO, "already in there..."); + return PM3_SUCCESS; + } + } + + fclose(f); + + + // Reopen for appending if line doesn't exist + f = fopen(path, "a"); + if (f == NULL) { + PrintAndLogEx(WARNING, "file not found or locked `" _YELLOW_("%s") "`", path); + free(path); + return PM3_EFILE; + } + + free(path); + + // Append the line with a newline + if (fprintf(f, "%s\n", keystrdup) < 0) { + PrintAndLogEx(WARNING, "error writing to file"); + fclose(f); + return PM3_EFILE; + } + + fclose(f); + return PM3_SUCCESS; +} + int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumplen) { int res = PM3_SUCCESS; @@ -3081,7 +3174,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl case JSON: { *pdump = calloc(maxdumplen, sizeof(uint8_t)); if (*pdump == NULL) { - PrintAndLogEx(WARNING, "fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } @@ -3121,7 +3214,7 @@ int pm3_load_dump(const char *fn, void **pdump, size_t *dumplen, size_t maxdumpl *pdump = calloc(maxdumplen, sizeof(uint8_t)); if (*pdump == NULL) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + PrintAndLogEx(WARNING, "Failed to allocate memory"); return PM3_EMALLOC; } res = loadFileNFC_safe(fn, *pdump, maxdumplen, dumplen, dumptype); diff --git a/client/src/fileutils.h b/client/src/fileutils.h index 6fc450dd1..cc6d9ee14 100644 --- a/client/src/fileutils.h +++ b/client/src/fileutils.h @@ -307,7 +307,7 @@ int loadFileDICTIONARY_safe_ex(const char *preferredName, const char *suffix, vo */ int loadFileXML_safe(const char *preferredName, const char *suffix, void **pdata, size_t *datalen); -int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen); +int loadFileBinaryKey(const char *preferredName, const char *suffix, void **keya, void **keyb, size_t *alen, size_t *blen, bool verbose); /** * @brief Utility function to check and convert plain mfu dump format to new mfu binary format. @@ -385,4 +385,15 @@ int pm3_save_mf_dump(const char *fn, uint8_t *d, size_t n, JSONFileType jsft); * @return PM3_SUCCESS if OK */ int pm3_save_fm11rf08s_nonces(const char *fn, iso14a_fm11rf08s_nonces_with_data_t *d, bool with_data); + + +/** + * Inserts a line into a text file only if it does not already exist. + * Returns PM3_SUCCES or, PM3_EFILE; + * + * @param filepath Path to the file. + * @param line Line to insert (should not contain a trailing newline). + */ +int insert_line_if_not_exists(const char *preferredName, const char *line); + #endif // FILEUTILS_H diff --git a/client/src/flash.c b/client/src/flash.c index ff14309a0..1672a4497 100644 --- a/client/src/flash.c +++ b/client/src/flash.c @@ -30,6 +30,7 @@ #include "util_posix.h" #include "comms.h" #include "commonutil.h" +#include "fileutils.h" #define FLASH_START 0x100000 @@ -266,7 +267,7 @@ int flash_load(flash_file_t *ctx, bool force) { int res = PM3_EUNDEF; fd = fopen(ctx->filename, "rb"); - if (!fd) { + if (fd == NULL) { PrintAndLogEx(ERR, _RED_("Could not open file") " %s >>> ", ctx->filename); res = PM3_EFILE; goto fail; @@ -652,17 +653,41 @@ static const char ice[] = " !!: :!! !!: !!: !!: !!: !!! !!: !!!\n : :: :: : : :: ::: : : : : : :: : \n" _RED_(" . .. .. . . .. ... . . . . . .. . "); + +#define ICEMAN_LOGO_FN "iceman.txt" +#define ICEMAN_LOGO_SIZE (5000) // Write a file's segments to Flash int flash_write(flash_file_t *ctx) { - int len = 0; + + char ice2[ICEMAN_LOGO_SIZE] = {0}; + char ice3[ICEMAN_LOGO_SIZE] = {0}; + + bool is_loaded = false; + if (g_session.supports_colors) { + + uint8_t *iraw = NULL; + size_t irawlen = 0; + int res = loadFile_safeEx(ICEMAN_LOGO_FN, "", (void **)&iraw, &irawlen, false); + if (res == PM3_SUCCESS && irawlen > ICEMAN_LOGO_SIZE) { + irawlen = ICEMAN_LOGO_SIZE; + } + if (res == PM3_SUCCESS) { + memcpy(ice3, iraw, irawlen); + free(iraw); + is_loaded = true; + } + } + + if (is_loaded == false) { + memcpy_filter_ansi(ice2, ice, sizeof(ice), !g_session.supports_colors); + memcpy_filter_emoji(ice3, ice2, sizeof(ice2), g_session.emoji_mode); + } + + size_t ice3len = strlen(ice3); PrintAndLogEx(SUCCESS, "Writing segments for file: %s", ctx->filename); - char ice2[sizeof(ice)] = {0}; - char ice3[sizeof(ice)] = {0}; - memcpy_filter_ansi(ice2, ice, sizeof(ice), !g_session.supports_colors); - memcpy_filter_emoji(ice3, ice2, sizeof(ice2), g_session.emoji_mode); - size_t ice3len = strlen(ice3); + int len = 0; for (int i = 0; i < ctx->num_segs; i++) { flash_seg_t *seg = &ctx->segments[i]; @@ -672,6 +697,9 @@ int flash_write(flash_file_t *ctx) { uint32_t end = seg->start + length; PrintAndLogEx(SUCCESS, " 0x%08x..0x%08x [0x%x / %u blocks]", seg->start, end - 1, length, blocks); + if (is_loaded) { + fprintf(stdout, "\n\n"); + } fflush(stdout); int block = 0; uint8_t *data = seg->data; @@ -693,15 +721,36 @@ int flash_write(flash_file_t *ctx) { length -= block_size; block++; - if (len < ice3len) { - fprintf(stdout, "%c", ice3[len++]); - } else { + if (is_loaded) { + if (len < ice3len) { + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + fprintf(stdout, "%c", ice3[len++]); + } else { - if ((len - ice3len) % 67 == 0) { - fprintf(stdout, "\n"); + if ((len - ice3len - 1) % 61 == 0) { + fprintf(stdout, "\n"); + } + fprintf(stdout, "."); + len++; + } + + } else { + if (len < ice3len) { + fprintf(stdout, "%c", ice3[len++]); + } else { + + if ((len - ice3len) % 67 == 0) { + fprintf(stdout, "\n"); + } + fprintf(stdout, "."); + len++; } - fprintf(stdout, "."); - len++; } fflush(stdout); } diff --git a/client/src/loclass/cipherutils.c b/client/src/loclass/cipherutils.c index 83b23c698..2ff42274d 100644 --- a/client/src/loclass/cipherutils.c +++ b/client/src/loclass/cipherutils.c @@ -126,47 +126,67 @@ uint64_t x_bytes_to_num(uint8_t *src, size_t len) { } void printarr(const char *name, uint8_t *arr, int len) { - if (name == NULL || arr == NULL) return; + + if (name == NULL || arr == NULL) { + return; + } int cx, i; size_t outsize = 40 + strlen(name) + len * 5; + char *output = calloc(outsize, sizeof(char)); if (output == NULL) { PrintAndLogEx(WARNING, "Failed to allocate memory"); return; } + cx = snprintf(output, outsize, "uint8_t %s[] = {", name); for (i = 0; i < len; i++) { - if (cx < outsize) + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "0x%02x,", *(arr + i)); //5 bytes per byte + } } - if (cx < outsize) + + if (cx < outsize) { snprintf(output + cx, outsize - cx, "};"); + } + PrintAndLogEx(INFO, output); free(output); } void printarr_human_readable(const char *title, uint8_t *arr, int len) { - if (arr == NULL) return; + if (arr == NULL) { + return; + } int cx = 0, i; size_t outsize = 100 + strlen(title) + (len * 4); char *output = calloc(outsize, sizeof(char)); PrintAndLogEx(INFO, "%s", title); + for (i = 0; i < len; i++) { + if (i % 16 == 0) { if (i == 0) { - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "%02x| ", i); + } + } else { - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "\n%02x| ", i); + } } } - if (cx < outsize) + + if (cx < outsize) { cx += snprintf(output + cx, outsize - cx, "%02x ", *(arr + i)); + } } PrintAndLogEx(INFO, output); free(output); @@ -233,11 +253,14 @@ static int testReversedBitstream(void) { } int testCipherUtils(void) { - PrintAndLogEx(INFO, "Testing some internals..."); - int retval = testBitStream(); - if (retval == PM3_SUCCESS) - retval = testReversedBitstream(); - return retval; + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "---------------- " _CYAN_("Loclass selftests") " ----------------"); + + int res = testBitStream(); + if (res == PM3_SUCCESS) { + res = testReversedBitstream(); + } + return res; } #endif diff --git a/client/src/loclass/elite_crack.c b/client/src/loclass/elite_crack.c index 01ffe7b29..ad7beb249 100644 --- a/client/src/loclass/elite_crack.c +++ b/client/src/loclass/elite_crack.c @@ -248,10 +248,12 @@ void hash2(uint8_t *key64, uint8_t *outp_keytable) { } if (outp_keytable != NULL) { + for (uint8_t i = 0 ; i < 8 ; i++) { memcpy(outp_keytable + i * 16, y[i], 8); memcpy(outp_keytable + 8 + i * 16, z[i], 8); } + } else { printarr_human_readable("hash2", outp_keytable, 128); } @@ -329,7 +331,9 @@ static void *bf_thread(void *thread_arg) { int found = __atomic_load_n(&loclass_found, __ATOMIC_SEQ_CST); - if (found != 0xFF) return NULL; + if (found != 0xFF) { + return NULL; + } //Update the keytable with the brute-values for (uint8_t i = 0; i < numbytes_to_recover; i++) { @@ -383,15 +387,22 @@ static void *bf_thread(void *thread_arg) { #define _CLR_ "\x1b[0K" if (numbytes_to_recover == 3) { + if ((brute > 0) && ((brute & 0xFFFF) == 0)) { PrintAndLogEx(INPLACE, "[ %02x %02x %02x ] %8u / %u", bytes_to_recover[0], bytes_to_recover[1], bytes_to_recover[2], brute, 0xFFFFFF); } + } else if (numbytes_to_recover == 2) { - if ((brute > 0) && ((brute & 0x3F) == 0)) + + if ((brute > 0) && ((brute & 0x3F) == 0)) { PrintAndLogEx(INPLACE, "[ %02x %02x ] %5u / %u" _CLR_, bytes_to_recover[0], bytes_to_recover[1], brute, 0xFFFF); + } + } else { - if ((brute > 0) && ((brute & 0x1F) == 0)) + + if ((brute > 0) && ((brute & 0x1F) == 0)) { PrintAndLogEx(INPLACE, "[ %02x ] %3u / %u" _CLR_, bytes_to_recover[0], brute, 0xFF); + } } } pthread_exit(NULL); @@ -424,15 +435,19 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { uint8_t bytes_to_recover[3] = {0}; uint8_t numbytes_to_recover = 0; for (uint8_t i = 0; i < 8; i++) { - if (keytable[key_index[i]] & (LOCLASS_CRACKED | LOCLASS_BEING_CRACKED)) continue; + + if (keytable[key_index[i]] & (LOCLASS_CRACKED | LOCLASS_BEING_CRACKED)) { + continue; + } bytes_to_recover[numbytes_to_recover++] = key_index[i]; + keytable[key_index[i]] |= LOCLASS_BEING_CRACKED; if (numbytes_to_recover > 3) { PrintAndLogEx(FAILED, "The CSN requires > 3 byte bruteforce, not supported"); - PrintAndLogEx(INFO, "CSN %s", sprint_hex(item.csn, 8)); - PrintAndLogEx(INFO, "HASH1 %s", sprint_hex(key_index, 8)); + PrintAndLogEx(INFO, "CSN..... %s", sprint_hex_inrow(item.csn, 8)); + PrintAndLogEx(INFO, "HASH1... %s", sprint_hex_inrow(key_index, 8)); PrintAndLogEx(NORMAL, ""); //Before we exit, reset the 'BEING_CRACKED' to zero keytable[bytes_to_recover[0]] &= ~LOCLASS_BEING_CRACKED; @@ -443,6 +458,7 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { } if (numbytes_to_recover == 0) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "No bytes to recover, exiting"); return PM3_ESOFT; } @@ -472,8 +488,9 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { } // wait for threads to terminate: void *ptrs[loclass_tc]; - for (size_t i = 0; i < loclass_tc; i++) + for (size_t i = 0; i < loclass_tc; i++) { pthread_join(threads[i], &ptrs[i]); + } // was it a success? int res = PM3_SUCCESS; @@ -661,7 +678,6 @@ int bruteforceItem(loclass_dumpdata_t item, uint16_t keytable[]) { * @return 0 for ok, 1 for failz */ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { - mbedtls_des_context ctx_e; uint8_t z_0[8] = {0}; uint8_t y_0[8] = {0}; @@ -680,8 +696,9 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { permutekey_rev(z_0, z_0_rev); // ~K_cus = DESenc(z[0], y[0]) - mbedtls_des_setkey_enc(&ctx_e, z_0_rev); - mbedtls_des_crypt_ecb(&ctx_e, y_0, key64_negated); + mbedtls_des_context ctx; + mbedtls_des_setkey_enc(&ctx, z_0_rev); + mbedtls_des_crypt_ecb(&ctx, y_0, key64_negated); key64[0] = ~key64_negated[0]; key64[1] = ~key64_negated[1]; @@ -697,20 +714,24 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { uint8_t key64_stdformat[8] = {0}; permutekey_rev(key64, key64_stdformat); - mbedtls_des_setkey_enc(&ctx_e, key64_stdformat); - mbedtls_des_crypt_ecb(&ctx_e, key64_negated, result); + mbedtls_des_setkey_enc(&ctx, key64_stdformat); + mbedtls_des_crypt_ecb(&ctx, key64_negated, result); + mbedtls_des_free(&ctx); - if (kcus != NULL) + // copy key to out array + if (kcus != NULL) { memcpy(kcus, key64, 8); + } if (memcmp(z_0, result, 4) != 0) { - PrintAndLogEx(WARNING, _RED_("Failed to verify") " calculated master key (k_cus)! Something is wrong."); + PrintAndLogEx(WARNING, "Calculated master key, k_cus ( %s )", _RED_("fail")); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } - PrintAndLogEx(SUCCESS, "----- " _CYAN_("High security custom key (Kcus)") " -----"); - PrintAndLogEx(SUCCESS, "Standard format %s", sprint_hex(key64_stdformat, 8)); - PrintAndLogEx(SUCCESS, "iCLASS format " _GREEN_("%s"), sprint_hex(key64, 8)); + PrintAndLogEx(SUCCESS, "--- " _CYAN_("High security custom key (Kcus)") " ---"); + PrintAndLogEx(SUCCESS, "Standard format... %s", sprint_hex_inrow(key64_stdformat, sizeof(key64_stdformat))); + PrintAndLogEx(SUCCESS, "iCLASS format..... " _GREEN_("%s"), sprint_hex_inrow(key64, sizeof(key64))); PrintAndLogEx(SUCCESS, "Key verified ( " _GREEN_("ok") " )"); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; @@ -723,7 +744,7 @@ int calculateMasterKey(uint8_t first16bytes[], uint8_t kcus[]) { * @return */ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { - uint8_t i; + size_t itemsize = sizeof(loclass_dumpdata_t); loclass_dumpdata_t *attack = (loclass_dumpdata_t *) calloc(itemsize, sizeof(uint8_t)); if (attack == NULL) { @@ -737,19 +758,28 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { int res = 0; uint64_t t1 = msclock(); - for (i = 0 ; i * itemsize < dumpsize ; i++) { + for (uint16_t i = 0 ; i * itemsize < dumpsize ; i++) { + memcpy(attack, dump + i * itemsize, itemsize); + res = bruteforceItem(*attack, keytable); - if (res != PM3_SUCCESS) + if (res != PM3_SUCCESS) { break; + } } + free(attack); + t1 = msclock() - t1; - PrintAndLogEx(NORMAL, ""); + if (res == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, ""); + } PrintAndLogEx(SUCCESS, "time " _YELLOW_("%" PRIu64) " seconds", t1 / 1000); if (res != PM3_SUCCESS) { - PrintAndLogEx(ERR, "loclass exiting. Try run " _YELLOW_("`hf iclass sim -t 2`") " again and collect new data"); + PrintAndLogEx(ERR, "loclass key recovery( %s )", _RED_("fail")); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf iclass sim -t 2") "` again and collect new data"); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } @@ -758,11 +788,12 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) { // indicate crack-status. Those must be discarded for the // master key calculation uint8_t first16bytes[16] = {0}; - for (i = 0 ; i < 16 ; i++) { + for (uint8_t i = 0 ; i < 16 ; i++) { first16bytes[i] = keytable[i] & 0xFF; if ((keytable[i] & LOCLASS_CRACKED) != LOCLASS_CRACKED) { PrintAndLogEx(WARNING, "Warning: we are missing byte " _RED_("%d") " , custom key calculation will fail...", i); + PrintAndLogEx(NORMAL, ""); return PM3_ESOFT; } } @@ -873,7 +904,7 @@ static int _testHash1(void) { } int testElite(bool slowtests) { - PrintAndLogEx(INFO, "Testing iClass Elite functionality"); + PrintAndLogEx(INFO, "Testing iClass Elite functionality..."); PrintAndLogEx(INFO, "Testing hash2..."); uint8_t k_cus[8] = {0x5B, 0x7C, 0x62, 0xC4, 0x91, 0xC1, 0x1B, 0x39}; @@ -894,22 +925,23 @@ int testElite(bool slowtests) { */ uint8_t keytable[128] = {0}; hash2(k_cus, keytable); - printarr_human_readable("---------------------- Hash2 ----------------------", keytable, sizeof(keytable)); + printarr_human_readable("--------------------- " _CYAN_("Hash2") " -----------------------", keytable, sizeof(keytable)); if (keytable[3] == 0xA1 && keytable[0x30] == 0xA3 && keytable[0x6F] == 0x95) { - PrintAndLogEx(SUCCESS, " hash2 ( %s )", _GREEN_("ok")); + PrintAndLogEx(SUCCESS, " Hash2 ( %s )", _GREEN_("ok")); } int res = PM3_SUCCESS; PrintAndLogEx(INFO, "Testing hash1..."); res += _testHash1(); - PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " hash1 ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " Hash1 ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); PrintAndLogEx(INFO, "Testing key diversification..."); res += _test_iclass_key_permutation(); - PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " key diversification ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx((res == PM3_SUCCESS) ? SUCCESS : WARNING, " Key diversification ( %s )", (res == PM3_SUCCESS) ? _GREEN_("ok") : _RED_("fail")); - if (slowtests) + if (slowtests) { res += _testBruteforce(); + } return res; } diff --git a/client/src/loclass/ikeys.c b/client/src/loclass/ikeys.c index 9e9c4c5c4..b50699e9e 100644 --- a/client/src/loclass/ikeys.c +++ b/client/src/loclass/ikeys.c @@ -657,7 +657,7 @@ void invert_hash0(uint8_t k[8]) { x_num_to_bytes(original_z, sizeof(original_z), des_pre_image); if (image_match) { - PrintAndLogEx(INFO, "Pre-image......... " _YELLOW_("%s") " ( "_GREEN_("valid") " )", sprint_hex_inrow(des_pre_image, sizeof(des_pre_image))); + PrintAndLogEx(INFO, "Pre-image......... " _YELLOW_("%s") " ( "_GREEN_("ok") " )", sprint_hex_inrow(des_pre_image, sizeof(des_pre_image))); } else { if (g_debugMode > 0) { @@ -803,17 +803,17 @@ static bool des_getParityBitFromKey(uint8_t key) { } static void des_checkParity(uint8_t *key) { - int i; int fails = 0; - for (i = 0; i < 8; i++) { + for (uint8_t i = 0; i < 8; i++) { bool parity = des_getParityBitFromKey(key[i]); if (parity != (key[i] & 0x1)) { fails++; PrintAndLogEx(FAILED, "parity1 fail, byte %d [%02x] was %d, should be %d", i, key[i], (key[i] & 0x1), parity); } } + if (fails) { - PrintAndLogEx(FAILED, "parity fails: %d", fails); + PrintAndLogEx(FAILED, "parity fails... " _RED_("%d"), fails); } else { PrintAndLogEx(SUCCESS, " Key syntax is with parity bits inside each byte (%s)", _GREEN_("ok")); } @@ -894,15 +894,17 @@ static int testKeyDiversificationWithMasterkeyTestcases(uint8_t *key) { int i, error = 0; uint8_t empty[8] = {0}; - PrintAndLogEx(INFO, "Testing encryption/decryption"); + PrintAndLogEx(INFO, "Testing encryption/decryption..."); - for (i = 0; memcmp(testcases + i, empty, 8); i++) + for (i = 0; memcmp(testcases + i, empty, 8); i++) { error += testDES(key, testcases[i]); + } - if (error) - PrintAndLogEx(FAILED, "%d errors occurred (%d testcases)", error, i); - else - PrintAndLogEx(SUCCESS, "Hashing seems to work (%d testcases)", i); + if (error) { + PrintAndLogEx(FAILED, "%d errors occurred, %d testcases ( %s )", error, i, _RED_("fail")); + } else { + PrintAndLogEx(SUCCESS, " Hashing seems to work, " _YELLOW_("%d") " testcases ( %s )", i, _GREEN_("ok")); + } return error; } @@ -942,8 +944,9 @@ static int testDES2(uint8_t *key, uint64_t csn, uint64_t expected) { PrintAndLogEx(DEBUG, " {csn} %"PRIx64, crypt_csn); PrintAndLogEx(DEBUG, " expected %"PRIx64 " (%s)", expected, (expected == crypt_csn) ? _GREEN_("ok") : _RED_("fail")); - if (expected != crypt_csn) + if (expected != crypt_csn) { return PM3_ESOFT; + } return PM3_SUCCESS; } @@ -954,12 +957,12 @@ static int testDES2(uint8_t *key, uint64_t csn, uint64_t expected) { */ static int doTestsWithKnownInputs(void) { // KSel from http://www.proxmark.org/forum/viewtopic.php?pid=10977#p10977 - PrintAndLogEx(INFO, "Testing DES encryption"); + PrintAndLogEx(INFO, "Testing DES encryption... "); uint8_t key[8] = {0x6c, 0x8d, 0x44, 0xf9, 0x2a, 0x2d, 0x01, 0xbf}; testDES2(key, 0xbbbbaaaabbbbeeee, 0xd6ad3ca619659e6b); - PrintAndLogEx(INFO, "Testing hashing algorithm"); + PrintAndLogEx(INFO, "Testing hashing algorithm... "); int res = PM3_SUCCESS; res += testCryptedCSN(0x0102030405060708, 0x0bdd6512073c460a); @@ -973,57 +976,29 @@ static int doTestsWithKnownInputs(void) { res += testCryptedCSN(0x14e2adfc5bb7e134, 0x6ac90c6508bd9ea3); if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "%d res occurred (9 testcases)", res); + PrintAndLogEx(FAILED, "%d res occurred " _YELLOW_("9") " testcases ( %s )", res, _RED_("fail")); res = PM3_ESOFT; } else { - PrintAndLogEx(SUCCESS, "Hashing seems to work (9 testcases)"); + PrintAndLogEx(SUCCESS, " Hashing seems to work " _YELLOW_("9") " testcases ( %s )", _GREEN_("ok")); res = PM3_SUCCESS; } return res; } -static bool readKeyFile(uint8_t *key, size_t keylen) { - - bool retval = false; - size_t len = 0; - uint8_t *keyptr = NULL; - if (loadFile_safe("iclass_key.bin", "", (void **)&keyptr, &len) != PM3_SUCCESS) { - return retval; - } - if (keylen == len) { - memcpy(key, keyptr, keylen); - retval = true; - } - free(keyptr); - return retval; -} - int doKeyTests(void) { - PrintAndLogEx(INFO, "Checking if the master key is present (iclass_key.bin)..."); - uint8_t key[8] = {0}; - if (readKeyFile(key, sizeof(key)) == false) { - PrintAndLogEx(FAILED, "Master key not present, will not be able to do all testcases"); - } else { + uint8_t key[8] = { 0xAE, 0xA6, 0x84, 0xA6, 0xDA, 0xB2, 0x32, 0x78 }; + uint8_t parity[8] = {0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01}; - //Test if it's the right key... - uint8_t i; - uint8_t j = 0; - for (i = 0; i < ARRAYLEN(key); i++) - j += key[i]; - - if (j != 185) { - PrintAndLogEx(INFO, "A key was loaded, but it does not seem to be the correct one. Aborting these tests"); - } else { - PrintAndLogEx(SUCCESS, "Key present"); - PrintAndLogEx(SUCCESS, "Checking key parity..."); - des_checkParity(key); - - // Test hashing functions - PrintAndLogEx(SUCCESS, "The following tests require the correct 8-byte master key"); - testKeyDiversificationWithMasterkeyTestcases(key); - } + for (int i = 0; i < 8; i++) { + key[i] += parity[i]; } + + PrintAndLogEx(SUCCESS, "Checking key parity..."); + des_checkParity(key); + + // Test hashing functions + testKeyDiversificationWithMasterkeyTestcases(key); PrintAndLogEx(INFO, "Testing key diversification with non-sensitive keys..."); return doTestsWithKnownInputs(); } diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index 9adfcdefd..985aad1f4 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -2274,10 +2274,10 @@ int DesfireUpdateRecord(DesfireContext_t *dctx, uint8_t fnum, uint32_t recnum, u static void PrintKeySettingsPICC(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) { PrintAndLogEx(SUCCESS, "PICC level rights"); - PrintAndLogEx(SUCCESS, "[%c...] CMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)")); - PrintAndLogEx(SUCCESS, "[.%c..] CMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, "[..%c.] Directory list access with CMK : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, "[...%c] CMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : _RED_("NO (frozen)")); + PrintAndLogEx(SUCCESS, "[%c...] CMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)")); + PrintAndLogEx(SUCCESS, "[.%c..] CMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : _RED_("YES")); + PrintAndLogEx(SUCCESS, "[..%c.] CMK required for AID list / GetKeySettings : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : _RED_("YES")); + PrintAndLogEx(SUCCESS, "[...%c] CMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : _RED_("NO (frozen)")); PrintAndLogEx(SUCCESS, ""); if (print2ndbyte) { @@ -2314,10 +2314,10 @@ static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print } } - PrintAndLogEx(SUCCESS, "[%c...] AMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)")); - PrintAndLogEx(SUCCESS, "[.%c..] AMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, "[..%c.] Directory list access with AMK : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : "YES"); - PrintAndLogEx(SUCCESS, "[...%c] AMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : _RED_("NO (frozen)")); + PrintAndLogEx(SUCCESS, "[%c...] AMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : _RED_("NO (frozen)")); + PrintAndLogEx(SUCCESS, "[.%c..] AMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? _GREEN_("NO") : _RED_("YES")); + PrintAndLogEx(SUCCESS, "[..%c.] AMK required for FID list / GetKeySettings : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? _GREEN_("NO") : _RED_("YES")); + PrintAndLogEx(SUCCESS, "[...%c] AMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : _RED_("NO (frozen)")); PrintAndLogEx(SUCCESS, ""); if (print2ndbyte) { diff --git a/client/src/mifare/desfirecrypto.c b/client/src/mifare/desfirecrypto.c index bf61472e2..335a87f4d 100644 --- a/client/src/mifare/desfirecrypto.c +++ b/client/src/mifare/desfirecrypto.c @@ -106,6 +106,10 @@ void DesfireSetKdf(DesfireContext_t *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, ui } } +void DesfireSetSecureChannel(DesfireContext_t *ctx, DesfireSecureChannel schann) { + ctx->secureChannel = schann; +} + bool DesfireIsAuthenticated(DesfireContext_t *dctx) { return dctx->secureChannel != DACNone; } diff --git a/client/src/mifare/desfirecrypto.h b/client/src/mifare/desfirecrypto.h index 43f9cd386..464fb98ee 100644 --- a/client/src/mifare/desfirecrypto.h +++ b/client/src/mifare/desfirecrypto.h @@ -95,6 +95,7 @@ void DesfireSetKey(DesfireContext_t *ctx, uint8_t keyNum, DesfireCryptoAlgorithm void DesfireSetKeyNoClear(DesfireContext_t *ctx, uint8_t keyNum, DesfireCryptoAlgorithm keyType, uint8_t *key); void DesfireSetCommandSet(DesfireContext_t *ctx, DesfireCommandSet cmdSet); void DesfireSetCommMode(DesfireContext_t *ctx, DesfireCommunicationMode commMode); +void DesfireSetSecureChannel(DesfireContext_t *ctx, DesfireSecureChannel schann); void DesfireSetKdf(DesfireContext_t *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, uint8_t kdfInputLen); bool DesfireIsAuthenticated(DesfireContext_t *dctx); size_t DesfireGetMACLength(DesfireContext_t *ctx); diff --git a/client/src/mifare/mad.c b/client/src/mifare/mad.c index b5ab07897..eae149a4c 100644 --- a/client/src/mifare/mad.c +++ b/client/src/mifare/mad.c @@ -317,7 +317,6 @@ static int MADInfoByteDecode(const uint8_t *sector, bool swapmad, int mad_ver, b void MADPrintHeader(void) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("MIFARE App Directory Information") " ----------------"); - PrintAndLogEx(INFO, "-----------------------------------------------------"); } int MAD1DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose, bool *haveMAD2) { @@ -408,14 +407,14 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose) { uint16_t aid = madGetAID(sector, swapmad, 2, i); if (aid < 6) { PrintAndLogEx(INFO, - (ibs == i) ? _MAGENTA_(" %02d [%04X] %s") : " %02d [" _GREEN_("%04X") "] %s", + (ibs == i + 16) ? _MAGENTA_(" %02d [%04X] %s") : " %02d [" _GREEN_("%04X") "] %s", i + 16, aid, aid_admin[aid] ); } else if (prev_aid == aid) { PrintAndLogEx(INFO, - (ibs == i) ? _MAGENTA_(" %02d [%04X] continuation") : " %02d [" _YELLOW_("%04X") "] continuation", + (ibs == i + 16) ? _MAGENTA_(" %02d [%04X] continuation") : " %02d [" _YELLOW_("%04X") "] continuation", i + 16, aid ); @@ -423,7 +422,7 @@ int MAD2DecodeAndPrint(uint8_t *sector, bool swapmad, bool verbose) { char fmt[80]; snprintf(fmt , sizeof(fmt) - , (ibs == i) ? + , (ibs == i + 16) ? _MAGENTA_(" %02d [%04X] %s") : " %02d [" _GREEN_("%04X") "] %s" , i + 16 @@ -450,8 +449,9 @@ int MADDFDecodeAndPrint(uint32_t short_aid, bool verbose) { } bool HasMADKey(uint8_t *d) { - if (d == NULL) + if (d == NULL) { return false; + } return (memcmp(d + (3 * MFBLOCK_SIZE), g_mifare_mad_key, sizeof(g_mifare_mad_key)) == 0); } diff --git a/client/src/mifare/mifare4.c b/client/src/mifare/mifare4.c index 85af35820..ea7986441 100644 --- a/client/src/mifare/mifare4.c +++ b/client/src/mifare/mifare4.c @@ -210,15 +210,18 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo uint8_t RndA[17] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}; uint8_t RndB[17] = {0}; - if (silentMode) + if (silentMode) { verbose = false; + } - if (mf4session) + if (mf4session) { mf4session->Authenticated = false; + } uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00}; int res = ExchangeRAW14a(cmd1, sizeof(cmd1), activateField, true, data, sizeof(data), &datalen, silentMode); if (res != PM3_SUCCESS) { + if (silentMode == false) { PrintAndLogEx(ERR, "Exchange raw error: %d", res); } @@ -234,20 +237,35 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo } if (datalen < 1) { - if (!silentMode) PrintAndLogEx(ERR, "Card response wrong length: %d", datalen); - if (dropFieldIfError) DropField(); + if (silentMode == false) { + PrintAndLogEx(ERR, "Card response wrong length: %d", datalen); + } + + if (dropFieldIfError) { + DropField(); + } return PM3_EWRONGANSWER; } if (data[0] != 0x90) { - if (!silentMode) PrintAndLogEx(ERR, "Card response error: %02x %s", data[0], mfpGetErrorDescription(data[0])); - if (dropFieldIfError) DropField(); + if (silentMode == false) { + PrintAndLogEx(ERR, "Card response error: %02x %s", data[0], mfpGetErrorDescription(data[0])); + } + + if (dropFieldIfError) { + DropField(); + } return PM3_EWRONGANSWER; } if (datalen != 19) { // code 1b + 16b + crc 2b - if (!silentMode) PrintAndLogEx(ERR, "Card response must be 19 bytes long instead of: %d", datalen); - if (dropFieldIfError) DropField(); + if (silentMode == false) { + PrintAndLogEx(ERR, "Card response must be 19 bytes long instead of: %d", datalen); + } + + if (dropFieldIfError) { + DropField(); + } return PM3_EWRONGANSWER; } @@ -268,11 +286,13 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo if (verbose) { PrintAndLogEx(INFO, ">phase2: %s", sprint_hex(cmd2, 33)); } + res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen, silentMode); if (res != PM3_SUCCESS) { if (silentMode == false) { PrintAndLogEx(ERR, "Exchange raw error: %d", res); } + if (dropFieldIfError) { DropField(); } @@ -291,12 +311,18 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo } if (memcmp(&raw[4], &RndA[1], 16)) { - if (!silentMode) PrintAndLogEx(ERR, "\nAuthentication FAILED. rnd is not equal"); + if (silentMode == false) { + PrintAndLogEx(ERR, "\nAuthentication FAILED. rnd is not equal"); + } + if (verbose) { PrintAndLogEx(ERR, "RndA reader: %s", sprint_hex(&RndA[1], 16)); PrintAndLogEx(ERR, "RndA card: %s", sprint_hex(&raw[4], 16)); } - if (dropFieldIfError) DropField(); + + if (dropFieldIfError) { + DropField(); + } return PM3_EWRONGANSWER; } @@ -309,6 +335,7 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo uint8_t kenc[16] = {0}; memcpy(&kenc[0], &RndA[11], 5); memcpy(&kenc[5], &RndB[11], 5); + for (int i = 0; i < 5; i++) { kenc[10 + i] = RndA[4 + i] ^ RndB[4 + i]; } @@ -322,6 +349,7 @@ int MifareAuth4(mf4Session_t *mf4session, const uint8_t *keyn, uint8_t *key, boo uint8_t kmac[16] = {0}; memcpy(&kmac[0], &RndA[7], 5); memcpy(&kmac[5], &RndB[7], 5); + for (int i = 0; i < 5; i++) { kmac[10 + i] = RndA[0 + i] ^ RndB[0 + i]; } @@ -587,6 +615,8 @@ uint8_t mfFirstBlockOfSector(uint8_t sectorNo) { } } +// returns the sectortrailer block number in the range of all block no. +// ie: sector 1 has its sector trailer at block number 7 uint8_t mfSectorTrailerOfSector(uint8_t sectorNo) { if (sectorNo < 32) { return (sectorNo * 4) | 0x03; diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index 2a2aed593..d3e6a902b 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -43,6 +43,8 @@ #include "cmdhf14a.h" #include "gen4.h" #include "parity.h" +#include "pmflash.h" +#include "preferences.h" // setDeviceDebugLevel int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key) { uint32_t uid = 0; @@ -62,7 +64,7 @@ int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key) { //flush queue while (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(WARNING, "Aborted via keyboard"); + PrintAndLogEx(WARNING, "aborted via keyboard"); return PM3_EOPABORTED; } @@ -88,7 +90,7 @@ int mf_dark_side(uint8_t blockno, uint8_t key_type, uint64_t *key) { //TODO: Not really stopping the command in time. if (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(WARNING, "\nAborted via keyboard"); + PrintAndLogEx(WARNING, "\naborted via keyboard"); return PM3_EOPABORTED; } @@ -275,7 +277,7 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh while (kbd_enter_pressed()) { SendCommandNG(CMD_BREAK_LOOP, NULL, 0); - PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "aborted via keyboard!"); return PM3_EOPABORTED; } @@ -305,12 +307,11 @@ int mf_check_keys_fast_ex(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastCh uint8_t curr_keys = resp.oldarg[0]; if ((singleSectorParams >> 15) & 1) { - if (curr_keys) { - // uint64_t foo = bytes_to_num(resp.data.asBytes, 6); - PrintAndLogEx(NORMAL, ""); -// PrintAndLogEx(SUCCESS, "found Key %s for block %2i found: " _GREEN_("%012" PRIx64), (singleSectorParams >> 8) & 1 ? "B" : "A", singleSectorParams & 0xFF, foo); - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", + if (curr_keys) { + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, "\nTarget block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", singleSectorParams & 0xFF, ((singleSectorParams >> 8) & 1) ? 'B' : 'A', sprint_hex_inrow(resp.data.asBytes, MIFARE_KEY_SIZE) @@ -561,8 +562,9 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo struct p *package = (struct p *)resp.data.asBytes; // error during nested on device side - if (package->isOK != PM3_SUCCESS) + if (package->isOK != PM3_SUCCESS) { return package->isOK; + } memcpy(&uid, package->cuid, sizeof(package->cuid)); @@ -582,12 +584,14 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo pthread_t thread_id[2]; // create and run worker threads - for (uint8_t i = 0; i < 2; i++) + for (uint8_t i = 0; i < 2; i++) { pthread_create(thread_id + i, NULL, nested_worker_thread, &statelists[i]); + } // wait for threads to terminate: - for (uint8_t i = 0; i < 2; i++) + for (uint8_t i = 0; i < 2; i++) { pthread_join(thread_id[i], (void *)&statelists[i].head.slhead); + } // the first 16 Bits of the cryptostate already contain part of our key. // Create the intersection of the two lists based on these 16 Bits and @@ -596,9 +600,11 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo p2 = p4 = statelists[1].head.slhead; while (p1 <= statelists[0].tail.sltail && p2 <= statelists[1].tail.sltail) { + if (Compare16Bits(p1, p2) == 0) { struct Crypto1State savestate; + savestate = *p1; while (Compare16Bits(p1, &savestate) == 0 && p1 <= statelists[0].tail.sltail) { *p3 = *p1; @@ -606,6 +612,7 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo p3++; p1++; } + savestate = *p2; while (Compare16Bits(p2, &savestate) == 0 && p2 <= statelists[1].tail.sltail) { *p4 = *p2; @@ -613,6 +620,7 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo p4++; p2++; } + } else { while (Compare16Bits(p1, p2) == -1) p1++; while (Compare16Bits(p1, p2) == 1) p2++; @@ -635,13 +643,15 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo // Create the intersection statelists[0].len = intersection(statelists[0].head.keyhead, statelists[1].head.keyhead); + bool looped = false; + //statelists[0].tail.keytail = --p7; uint32_t keycnt = statelists[0].len; if (keycnt == 0) { goto out; } - PrintAndLogEx(SUCCESS, "Found " _YELLOW_("%u") " key candidates", keycnt); + PrintAndLogEx(SUCCESS, "Found " _YELLOW_("%u") " key candidate%c", keycnt, (keycnt > 1) ? 's' : ' '); memset(resultKey, 0, MIFARE_KEY_SIZE); uint64_t key64 = -1; @@ -659,44 +669,53 @@ int mf_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo register uint8_t j; for (j = 0; j < size; j++) { crypto1_get_lfsr(statelists[0].head.slhead + i, &key64); - num_to_bytes(key64, 6, keyBlock + j * MIFARE_KEY_SIZE); + num_to_bytes(key64, MIFARE_KEY_SIZE, keyBlock + j * MIFARE_KEY_SIZE); } if (mf_check_keys(statelists[0].blockNo, statelists[0].keyType, false, size, keyBlock, &key64) == PM3_SUCCESS) { + + if (looped) { + PrintAndLogEx(NORMAL, ""); + } + free(statelists[0].head.slhead); free(statelists[1].head.slhead); - num_to_bytes(key64, 6, resultKey); + num_to_bytes(key64, MIFARE_KEY_SIZE, resultKey); if (package->keytype < 2) { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", package->block, package->keytype ? 'B' : 'A', sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) ); } else { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %02x -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "Target block " _GREEN_("%4u") " key type " _GREEN_("%02x") " -- found valid key [ " _GREEN_("%s") " ]", package->block, MIFARE_AUTH_KEYA + package->keytype, sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) ); } - - return PM3_SUCCESS; } float bruteforce_per_second = (float)(i + max_keys) / ((msclock() - start_time) / 1000.0); PrintAndLogEx(INPLACE, "%6d/%u keys | %5.1f keys/sec | worst case %6.1f seconds remaining", i, keycnt, bruteforce_per_second, (keycnt - i) / bruteforce_per_second); + looped = true; } out: + + if (looped) { + PrintAndLogEx(NORMAL, ""); + } + if (package->keytype < 2) { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %c", + PrintAndLogEx(SUCCESS, "Target block " _YELLOW_("%4u") " key type " _YELLOW_("%c"), package->block, package->keytype ? 'B' : 'A' ); } else { - PrintAndLogEx(SUCCESS, "\nTarget block %4u key type %02x", + PrintAndLogEx(SUCCESS, "Target block " _YELLOW_("%4u") " key type " _YELLOW_("%02x"), package->block, MIFARE_AUTH_KEYA + package->keytype ); @@ -911,10 +930,11 @@ int mf_static_nested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trg num_to_bytes(key64, MIFARE_KEY_SIZE, resultKey); - if (IfPm3Flash() && keycnt > 70) + if (IfPm3Flash() && keycnt > 70) { PrintAndLogEx(NORMAL, ""); + } - PrintAndLogEx(SUCCESS, "target block %4u key type %c -- found valid key [ " _GREEN_("%s") " ]", + PrintAndLogEx(SUCCESS, "target block " _GREEN_("%4u") " key type " _GREEN_("%c") " -- found valid key [ " _GREEN_("%s") " ]", package->block, package->keytype ? 'B' : 'A', sprint_hex_inrow(resultKey, MIFARE_KEY_SIZE) @@ -991,7 +1011,7 @@ int mf_read_block(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t return PM3_SUCCESS; } -int mf_write_block(uint8_t blockno, uint8_t keyType, const uint8_t *key, uint8_t *block) { +int mf_write_block(uint8_t blockno, uint8_t keyType, const uint8_t *key, const uint8_t *block) { uint8_t data[26]; memcpy(data, key, MIFARE_KEY_SIZE); @@ -1308,6 +1328,28 @@ int mf_chinese_gen_3_freeze(void) { return resp.status; } +// GDM Gen4 write block +int mf_chinese_gen_4_set_block(uint8_t blockNo, uint8_t *block, uint8_t *key) { + struct p { + uint8_t blockno; + uint8_t key[6]; + uint8_t data[MFBLOCK_SIZE]; // data to be written + } PACKED payload; + + payload.blockno = blockNo; + memcpy(payload.key, key, sizeof(payload.key)); + memcpy(payload.data, block, sizeof(payload.data)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G4_GDM_WRBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G4_GDM_WRBL, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "command execution time out"); + return PM3_ETIMEOUT; + } + return resp.status; +} + void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len, bool isEncrypted) { if (len != 1) { for (int i = 0; i < len; i++) { @@ -1439,19 +1481,19 @@ int detect_classic_nackbug(bool verbose) { return PM3_SUCCESS; } case 2: { - PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("always leak NACK")); + PrintAndLogEx(SUCCESS, "NACK test... " _GREEN_("always leak NACK")); return PM3_SUCCESS; } case 1: { - PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("detected")); + PrintAndLogEx(SUCCESS, "NACK test... " _GREEN_("detected")); return PM3_SUCCESS; } case 0: { - PrintAndLogEx(SUCCESS, "NACK test: " _GREEN_("no bug")); + PrintAndLogEx(SUCCESS, "NACK test... " _GREEN_("no bug")); return PM3_SUCCESS; } default: { - PrintAndLogEx(ERR, "errorcode from device " _RED_("[%i]"), ok); + PrintAndLogEx(ERR, "errorcode from device (" _RED_("%u") " )", ok); return PM3_EUNDEF; } } @@ -1513,10 +1555,21 @@ int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, cdata[21] = corruptnrar; cdata[22] = corruptnrarparity; + uint8_t dbg_curr = DBG_NONE; + if (getDeviceDebugLevel(&dbg_curr) != PM3_SUCCESS) { + return PM3_EFAILED; + } + + if (setDeviceDebugLevel(verbose ? MAX(dbg_curr, DBG_INFO) : DBG_NONE, false) != PM3_SUCCESS) { + return PM3_EFAILED; + } + clearCommandBuffer(); SendCommandNG(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, cdata, sizeof(cdata)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1000)) { + if (WaitForResponseTimeout(CMD_HF_MIFARE_STATIC_ENCRYPTED_NONCE, &resp, 1500)) { + + setDeviceDebugLevel(dbg_curr, false); if (resp.status == PM3_ESOFT) { return NONCE_FAIL; @@ -1582,6 +1635,8 @@ int detect_classic_static_encrypted_nonce_ex(uint8_t block_no, uint8_t key_type, } return resp.data.asBytes[0]; } + + setDeviceDebugLevel(dbg_curr, false); return NONCE_FAIL; } diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index fd065bfbc..9e6ba67c3 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -26,7 +26,7 @@ #include "mifaredefault.h" // consts #include "protocol_vigik.h" -#define MIFARE_SECTOR_RETRY 10 +#define MIFARE_SECTOR_RETRY 6 // mifare tracer flags #define TRACE_IDLE 0x00 @@ -88,7 +88,7 @@ int mf_key_brute(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint64_t int mf_read_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *data); int mf_read_block(uint8_t blockNo, uint8_t keyType, const uint8_t *key, uint8_t *data); -int mf_write_block(uint8_t blockno, uint8_t keyType, const uint8_t *key, uint8_t *block); +int mf_write_block(uint8_t blockno, uint8_t keyType, const uint8_t *key, const uint8_t *block); int mf_write_sector(uint8_t sectorNo, uint8_t keyType, const uint8_t *key, uint8_t *sector); int mf_eml_get_mem(uint8_t *data, int blockNum, int blocksCount); @@ -105,6 +105,8 @@ int mf_chinese_gen_3_uid(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid); int mf_chinese_gen_3_block(uint8_t *block, int blockLen, uint8_t *newBlock); int mf_chinese_gen_3_freeze(void); +int mf_chinese_gen_4_set_block(uint8_t blockNo, uint8_t *block, uint8_t *key); + int try_decrypt_word(uint32_t nt, uint32_t ar_enc, uint32_t at_enc, uint8_t *data, int len); int detect_classic_prng(void); diff --git a/client/src/pm3line_vocabulary.h b/client/src/pm3line_vocabulary.h index bfe823459..a7cb8a8a2 100644 --- a/client/src/pm3line_vocabulary.h +++ b/client/src/pm3line_vocabulary.h @@ -281,7 +281,7 @@ const static vocabulary_t vocabulary[] = { { 1, "hf iclass view" }, { 0, "hf iclass wrbl" }, { 0, "hf iclass creditepurse" }, - { 0, "hf iclass trbl" }, + { 0, "hf iclass tear" }, { 0, "hf iclass chk" }, { 1, "hf iclass loclass" }, { 1, "hf iclass lookup" }, @@ -343,8 +343,6 @@ const static vocabulary_t vocabulary[] = { { 0, "hf lto wrbl" }, { 1, "hf mf help" }, { 1, "hf mf list" }, - { 0, "hf mf info" }, - { 0, "hf mf isen" }, { 0, "hf mf darkside" }, { 0, "hf mf nested" }, { 1, "hf mf hardnested" }, @@ -356,9 +354,12 @@ const static vocabulary_t vocabulary[] = { { 0, "hf mf fchk" }, { 1, "hf mf decrypt" }, { 0, "hf mf supercard" }, + { 1, "hf mf bambukeys" }, { 0, "hf mf auth4" }, { 1, "hf mf acl" }, { 0, "hf mf dump" }, + { 0, "hf mf info" }, + { 0, "hf mf isen" }, { 1, "hf mf mad" }, { 0, "hf mf personalize" }, { 0, "hf mf rdbl" }, diff --git a/client/src/proxmark3.c b/client/src/proxmark3.c index de668a0c0..2481803fa 100644 --- a/client/src/proxmark3.c +++ b/client/src/proxmark3.c @@ -49,7 +49,7 @@ static int mainret = PM3_SUCCESS; #ifndef LIBPM3 #define BANNERMSG1 "" #define BANNERMSG2 "" -#define BANNERMSG3 "Release v4.20142 - Blue Ice" +#define BANNERMSG3 "" typedef enum LogoMode { UTF8, ANSI, ASCII } LogoMode; @@ -75,8 +75,9 @@ static void showBanner_logo(LogoMode mode) { sq, sq, tl, hl, hl, hl, br, __, sq, sq, vl, bl, sq, sq, tl, br, sq, sq, vl, __, bl, hl, hl, sq, sq, tr); PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")" " BANNERMSG1, sq, sq, vl, __, __, __, __, __, sq, sq, vl, __, bl, hl, br, __, sq, sq, vl, sq, sq, sq, sq, sq, tl, br); - PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s")" " BANNERMSG2, + PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"), bl, hl, br, __, __, __, __, __, bl, hl, br, __, __, __, __, __, bl, hl, br, bl, hl, hl, hl, hl, br, __); + PrintAndLogEx(NORMAL, " " BANNERMSG2); break; } case ANSI: { @@ -87,7 +88,8 @@ static void showBanner_logo(LogoMode mode) { PrintAndLogEx(NORMAL, " " _CYAN_("8888888P\" 888 Y888P 888 \"Y8b. ")); PrintAndLogEx(NORMAL, " " _CYAN_("888 888 Y8P 888 888 888 ")); PrintAndLogEx(NORMAL, " " _CYAN_("888 888 \" 888 Y88b d88P") " " BANNERMSG1); - PrintAndLogEx(NORMAL, " " _CYAN_("888 888 888 \"Y8888P\"") " " BANNERMSG2); + PrintAndLogEx(NORMAL, " " _CYAN_("888 888 888 \"Y8888P\"")); + PrintAndLogEx(NORMAL, " " BANNERMSG2); break; } case ASCII: { @@ -98,11 +100,11 @@ static void showBanner_logo(LogoMode mode) { PrintAndLogEx(NORMAL, " 8888888P\" 888 Y888P 888 \"Y8b. "); PrintAndLogEx(NORMAL, " 888 888 Y8P 888 888 888 "); PrintAndLogEx(NORMAL, " 888 888 \" 888 Y88b d88P " BANNERMSG1); - PrintAndLogEx(NORMAL, " 888 888 888 \"Y8888P\" " BANNERMSG2); + PrintAndLogEx(NORMAL, " 888 888 888 \"Y8888P\""); + PrintAndLogEx(NORMAL, " " BANNERMSG2); break; } } - PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, BANNERMSG3); } @@ -125,17 +127,18 @@ static uint8_t detect_current_lang(void) { static const char *get_quote(void) { const char *quotes_en[] = { - "E Pluribus Unum", - "Carpe Diem", - "Ad astra per aspera", - "Fortes fortuna adiuvat", - "Non ducor, duco", - "Veni, vidi, vici", - "Audentes fortuna iuvat", - "Virtus in actione consistit", - "Dum spiro, spero", - "Non scholae, sed vitae discimus", - "Faber est suae quisque fortunae" + "too many secrets", + "It's not that simple", + "I have received a coded signal", + "I await your instructions", + "And so I watch, I wait", + "Listen to the Domain", + "ghost.713", + "Local node X.XX.713", + "Beggar after knowledge", + "343 Gulity Spark: offline", + "I serve the Builders!", + "This is rather distressing" }; const char *quotes_fr[] = { @@ -305,16 +308,18 @@ static bool DetectWindowsAnsiSupport(void) { #endif // disable colors if stdin or stdout are redirected - if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) + if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) { return false; + } HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); DWORD dwMode = 0; GetConsoleMode(hOut, &dwMode); //ENABLE_VIRTUAL_TERMINAL_PROCESSING is already set - if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) + if ((dwMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)) { return true; + } dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; @@ -334,11 +339,13 @@ int push_cmdscriptfile(char *path, bool stayafter) { } FILE *f = fopen(path, "r"); - if (f == NULL) + if (f == NULL) { return PM3_EFILE; + } - if (cmdscriptfile_idx == 0) + if (cmdscriptfile_idx == 0) { cmdscriptfile_stayafter = stayafter; + } cmdscriptfile[++cmdscriptfile_idx] = f; return PM3_SUCCESS; @@ -370,28 +377,32 @@ main_loop(const char *script_cmds_file, char *script_cmd, bool stayInCommandLoop bool execCommand = (script_cmd != NULL); bool fromInteractive = false; uint16_t script_cmd_len = 0; + if (execCommand) { script_cmd_len = strlen(script_cmd); str_creplace(script_cmd, script_cmd_len, ';', '\0'); } + bool stdinOnPipe = !isatty(STDIN_FILENO); char script_cmd_buf[256] = {0x00}; // iceman, needs lua script the same file_path_buffer as the rest // cache Version information now: - if (execCommand || script_cmds_file || stdinOnPipe) + if (execCommand || script_cmds_file || stdinOnPipe) { pm3_version(false, false); - else + } else { pm3_version_short(); + } if (script_cmds_file) { char *path; int res = searchFile(&path, CMD_SCRIPTS_SUBDIR, script_cmds_file, ".cmd", false); if (res == PM3_SUCCESS) { - if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS) + if (push_cmdscriptfile(path, stayInCommandLoop) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "executing commands from file: %s\n", path); - else + } else { PrintAndLogEx(ERR, "could not open " _YELLOW_("%s") "...", path); + } free(path); } } @@ -448,20 +459,23 @@ check_script: prompt_ctx = stdinOnPipe ? PROXPROMPT_CTX_STDIN : PROXPROMPT_CTX_SCRIPTCMD; cmd = str_dup(script_cmd); - if ((cmd != NULL) && (! fromInteractive)) + if ((cmd != NULL) && (! fromInteractive)) { printprompt = true; + } uint16_t len = strlen(script_cmd) + 1; script_cmd += len; - if (script_cmd_len == len - 1) + if (script_cmd_len == len - 1) { execCommand = false; + } script_cmd_len -= len; } else { // exit after exec command - if (script_cmd && !stayInCommandLoop) + if (script_cmd && !stayInCommandLoop) { break; + } // if there is a pipe from stdin if (stdinOnPipe) { @@ -551,22 +565,27 @@ check_script: mainret = CommandReceived(cmd); // exit or quit - if (mainret == PM3_EFATAL) + if (mainret == PM3_EFATAL) { break; + } + if (mainret == PM3_SQUIT) { // Normal quit, map to 0 mainret = PM3_SUCCESS; break; } } + free(cmd); cmd = NULL; + } else { PrintAndLogEx(NORMAL, "\n"); - if (script_cmds_file && stayInCommandLoop) + if (script_cmds_file && stayInCommandLoop) { stayInCommandLoop = false; - else + } else { break; + } } } // end while @@ -615,8 +634,9 @@ const char *get_my_executable_directory(void) { static void set_my_executable_path(void) { int path_length = wai_getExecutablePath(NULL, 0, NULL); - if (path_length == -1) + if (path_length == -1) { return; + } my_executable_path = (char *)calloc(path_length + 1, sizeof(uint8_t)); int dirname_length = 0; @@ -841,12 +861,13 @@ finish2: CloseProxmark(g_session.current_device); finish: - if (ret == PM3_SUCCESS) + if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, _CYAN_("All done")); - else if (ret == PM3_EOPABORTED) + } else if (ret == PM3_EOPABORTED) { PrintAndLogEx(FAILED, "Aborted by user"); - else + } else { PrintAndLogEx(ERR, "Aborted on error %u", ret); + } return ret; } @@ -905,8 +926,9 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file goto finish; } - if (num_files == 0) + if (num_files == 0) { goto finish; + } for (int i = 0 ; i < num_files; ++i) { ret = flash_prepare(&files[i], can_write_bl, max_allowed * ONE_KB); @@ -927,20 +949,26 @@ static int flash_pm3(char *serial_port_name, uint8_t num_files, const char *file } finish: - if (ret != PM3_SUCCESS) + if (ret != PM3_SUCCESS) { PrintAndLogEx(WARNING, "The flashing procedure failed, follow the suggested steps!"); + } + ret = flash_stop_flashing(); CloseProxmark(g_session.current_device); + finish2: for (int i = 0 ; i < num_files; ++i) { flash_free(&files[i]); } - if (ret == PM3_SUCCESS) + + if (ret == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, _CYAN_("All done")); - else if (ret == PM3_EOPABORTED) + } else if (ret == PM3_EOPABORTED) { PrintAndLogEx(FAILED, "Aborted by user"); - else + } else { PrintAndLogEx(ERR, "Aborted on error"); + } + PrintAndLogEx(INFO, "\nHave a nice day!"); return ret; } @@ -976,7 +1004,6 @@ void pm3_init(void) { // set global variables soon enough to get the log path set_my_executable_path(); set_my_user_directory(); - } #ifndef LIBPM3 @@ -1051,6 +1078,7 @@ int main(int argc, char *argv[]) { show_help(false, exec_name); return 1; } + if (port != NULL) { // We got already one PrintAndLogEx(ERR, _RED_("ERROR:") " cannot parse command line. We got " _YELLOW_("%s") " as port and now we got also: " _YELLOW_("%s") "\n", port, argv[i + 1]); @@ -1312,21 +1340,22 @@ int main(int argc, char *argv[]) { // This will allow the command line to override the settings.json values preferences_load(); // quick patch for debug level - if (! debug_mode_forced) { + if (debug_mode_forced == false) { g_debugMode = g_session.client_debug_level; } // settings_save (); // End Settings // even if prefs, we disable colors if stdin or stdout is not a TTY - if ((! g_session.stdinOnTTY) || (! g_session.stdoutOnTTY)) { + if ((g_session.stdinOnTTY == false) || (g_session.stdoutOnTTY == false)) { g_session.supports_colors = false; g_session.emoji_mode = EMO_ALTTEXT; } // Let's take a baudrate ok for real UART, USB-CDC & BT don't use that info anyway - if (speed == 0) + if (speed == 0) { speed = USART_BAUD_RATE; + } if (dumpmem_mode) { dumpmem_pm3(port, dumpmem_filename, dumpmem_addr, dumpmem_len, dumpmem_raw); @@ -1344,8 +1373,9 @@ int main(int argc, char *argv[]) { } if (script_cmd) { - while (script_cmd[strlen(script_cmd) - 1] == ' ') + while (script_cmd[strlen(script_cmd) - 1] == ' ') { script_cmd[strlen(script_cmd) - 1] = 0x00; + } if (strlen(script_cmd) == 0) { script_cmd = NULL; @@ -1378,23 +1408,23 @@ int main(int argc, char *argv[]) { CloseProxmark(g_session.current_device); } - if ((port != NULL) && (!g_session.pm3_present)) { + if ((port != NULL) && (g_session.pm3_present == false)) { exit(EXIT_FAILURE); } - if (!g_session.pm3_present) { + if (g_session.pm3_present == false) { PrintAndLogEx(INFO, _YELLOW_("OFFLINE") " mode. Check " _YELLOW_("\"%s -h\"") " if it's not what you want.\n", exec_name); } // ascii art only in interactive client - if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && !dumpmem_mode && !flash_mode && !reboot_bootloader_mode) { + if (!script_cmds_file && !script_cmd && g_session.stdinOnTTY && g_session.stdoutOnTTY && (dumpmem_mode == false) && (flash_mode == false) && (reboot_bootloader_mode == false)) { showBanner(); } // Save settings if not loaded from settings json file. // Doing this here will ensure other checks and updates are saved to over rule default // e.g. Linux color use check - if ((!g_session.preferences_loaded) && (!g_session.incognito)) { + if ((g_session.preferences_loaded == false) && (g_session.incognito == false)) { PrintAndLogEx(INFO, "Creating initial preferences file"); // json save reports file name, so just info msg here preferences_save(); // Save defaults g_session.preferences_loaded = true; @@ -1414,7 +1444,7 @@ int main(int argc, char *argv[]) { #ifdef HAVE_GUI -# if defined(_WIN32) +# if defined(_WIN32) || (defined(__MACH__) && defined(__APPLE__)) InitGraphics(argc, argv, script_cmds_file, script_cmd, stayInCommandLoop); MainGraphics(); # else diff --git a/client/src/pthread_spin_lock_shim.h b/client/src/pthread_spin_lock_shim.h new file mode 100644 index 000000000..243771a6c --- /dev/null +++ b/client/src/pthread_spin_lock_shim.h @@ -0,0 +1,55 @@ +/* + +Required imports: +#include + +*/ + +#ifndef PTHREAD_SPIN_LOCK_SHIM +#define PTHREAD_SPIN_LOCK_SHIM + +typedef int pthread_spinlock_t; + +#ifndef PTHREAD_PROCESS_SHARED +# define PTHREAD_PROCESS_SHARED 1 +#endif +#ifndef PTHREAD_PROCESS_PRIVATE +# define PTHREAD_PROCESS_PRIVATE 2 +#endif + +static inline int pthread_spin_init(pthread_spinlock_t *lock, int pshared) { + __asm__ __volatile__("" ::: "memory"); + *lock = 0; + return 0; +} + +static inline int pthread_spin_destroy(pthread_spinlock_t *lock) { + return 0; +} + +static inline int pthread_spin_lock(pthread_spinlock_t *lock) { + while (1) { + int i; + for (i = 0; i < 10000; i++) { + if (__sync_bool_compare_and_swap(lock, 0, 1)) { + return 0; + } + } + sched_yield(); + } +} + +static inline int pthread_spin_trylock(pthread_spinlock_t *lock) { + if (__sync_bool_compare_and_swap(lock, 0, 1)) { + return 0; + } + return 16; // EBUSY; +} + +static inline int pthread_spin_unlock(pthread_spinlock_t *lock) { + __asm__ __volatile__("" ::: "memory"); + *lock = 0; + return 0; +} + +#endif diff --git a/client/src/scripting.c b/client/src/scripting.c index 251a0d915..1cd1ed1a0 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -303,7 +303,7 @@ static int l_GetFromFlashMemSpiffs(lua_State *L) { // get size from spiffs itself ! SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)destfilename, 32); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000)) + if (WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000) == false) return returnToLuaWithError(L, "No response from the device"); len = resp.data.asDwords[0]; diff --git a/client/src/util.c b/client/src/util.c index 6a54387d2..ff71b74e1 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -1212,6 +1212,29 @@ void binstr_2_bytes(uint8_t *target, size_t *targetlen, const char *src) { } } +void binstr_2_u8(char *src, uint8_t n, uint8_t *dest) { + + uint8_t b = 0; + // Process binary string + for (uint8_t i = 0; i < n; ++i) { + b = (b << 1) | (src[i] == '1'); + } + if (dest) { + *dest = b; + } +} + +void binstr_2_u16(char *src, uint8_t n, uint16_t *dest) { + uint16_t b = 0; + // Process binary string + for (uint8_t i = 0; i < n; ++i) { + b = (b << 1) | (src[i] == '1'); + } + if (dest) { + *dest = b; + } +} + void hex_xor(uint8_t *d, const uint8_t *x, int n) { while (n--) { d[n] ^= x[n]; @@ -1409,6 +1432,20 @@ void str_inverse_bin(char *buf, size_t len) { } } +void str_trim(char *s) { + if (s == NULL) { + return; + } + + // handle empty string + if (!*s) { + return; + } + + char *ptr; + for (ptr = s + strlen(s) - 1; (ptr >= s) && isspace(*ptr); --ptr); + ptr[1] = '\0'; +} /** * Converts a hex string to component "hi2", "hi" and "lo" 32-bit integers @@ -1592,3 +1629,40 @@ uint8_t get_highest_frequency(const uint8_t *d, uint8_t n) { PrintAndLogEx(DEBUG, "highest occurance... %u xor byte... 0x%02X", highest, v); return v; } + +size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n) { + if (n == 0) { + return 0; + } + if (n == 1) { + return 1; + } + + int write_index = 0; + + for (int read_index = 0; read_index < n; ++read_index) { + uint8_t *current = d + read_index * item_n; + + bool is_duplicate = false; + + // Check against all previous unique elements + for (int i = 0; i < write_index; ++i) { + uint8_t *unique = d + i * item_n; + if (memcmp(current, unique, item_n) == 0) { + is_duplicate = 1; + break; + } + } + + // If not duplicate, move to the write_index position + if (is_duplicate == false) { + uint8_t *dest = d + write_index * item_n; + if (dest != current) { + memcpy(dest, current, item_n); + } + write_index++; + } + } + + return write_index; +} diff --git a/client/src/util.h b/client/src/util.h index d4c34eb46..186f2e2a7 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -141,6 +141,9 @@ int binstr_2_binarray(uint8_t *target, char *source, int length); void bytes_2_binstr(char *target, const uint8_t *source, size_t sourcelen); void binstr_2_bytes(uint8_t *target, size_t *targetlen, const char *src); +void binstr_2_u8(char *src, uint8_t n, uint8_t *dest); +void binstr_2_u16(char *src, uint8_t n, uint16_t *dest); + void hex_xor(uint8_t *d, const uint8_t *x, int n); void hex_xor_token(uint8_t *d, const uint8_t *x, int dn, int xn); @@ -168,10 +171,12 @@ void str_creplace(char *buf, size_t len, char from, char to); void str_reverse(char *buf, size_t len); void str_inverse_hex(char *buf, size_t len); void str_inverse_bin(char *buf, size_t len); +void str_trim(char *s); char *str_dup(const char *src); char *str_ndup(const char *src, size_t len); size_t str_nlen(const char *src, size_t maxlen); + int hexstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); int binstring_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const char *str); int binarray_to_u96(uint32_t *hi2, uint32_t *hi, uint32_t *lo, const uint8_t *arr, int arrlen); @@ -192,4 +197,7 @@ struct smartbuf { void sb_append_char(smartbuf *sb, unsigned char c); uint8_t get_highest_frequency(const uint8_t *d, uint8_t n); + +size_t unduplicate(uint8_t *d, size_t n, const uint8_t item_n); + #endif diff --git a/client/src/wiegand_formats.c b/client/src/wiegand_formats.c index 03b0f5501..fa1d68eb1 100644 --- a/client/src/wiegand_formats.c +++ b/client/src/wiegand_formats.c @@ -733,7 +733,7 @@ static bool Unpack_Sie36(wiegand_message_t *packed, wiegand_card_t *card) { card->CardNumber = get_linear_field(packed, 19, 16); card->ParityValid = (get_bit_by_position(packed, 0) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]) {1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30, 31, 33, 34}))) && - (get_bit_by_position(packed, 35) == oddparity32(get_nonlinear_field(packed, 23, (uint8_t[]) {1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34}))); + (get_bit_by_position(packed, 35) == evenparity32(get_nonlinear_field(packed, 23, (uint8_t[]) {1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20, 22, 23, 25, 26, 28, 29, 31, 32, 34}))); return true; } @@ -1477,7 +1477,7 @@ static const cardformat_t FormatTable[] = { {"BQT34", Pack_bqt34, Unpack_bqt34, "BQT 34-bit", 34, {1, 1, 0, 0, 1, 0xFF, 0xFFFFFF, 0, 0}}, // from cardinfo.barkweb.com.au {"C1k35s", Pack_C1k35s, Unpack_C1k35s, "HID Corporate 1000 35-bit std", 35, {1, 1, 0, 0, 1, 0xFFF, 0xFFFFF, 0, 0}}, // imported from old pack/unpack {"C15001", Pack_C15001, Unpack_C15001, "HID KeyScan 36-bit", 36, {1, 1, 0, 1, 1, 0xFF, 0xFFFF, 0, 0x3FF}}, // from Proxmark forums - {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", 36, {1, 1, 1, 0, 1, 0xFF, 0x3, 0xFFFFFF, 0}}, // from cardinfo.barkweb.com.au + {"S12906", Pack_S12906, Unpack_S12906, "HID Simplex 36-bit", 36, {1, 1, 1, 0, 1, 0xFF, 0xFFFFFF, 0x3, 0}}, // from cardinfo.barkweb.com.au {"Sie36", Pack_Sie36, Unpack_Sie36, "HID 36-bit Siemens", 36, {1, 1, 0, 0, 1, 0x3FFFF, 0xFFFF, 0, 0}}, // from cardinfo.barkweb.com.au {"H10320", Pack_H10320, Unpack_H10320, "HID H10320 37-bit BCD", 37, {1, 0, 0, 0, 1, 0, 99999999, 0, 0}}, // from Proxmark forums {"H10302", Pack_H10302, Unpack_H10302, "HID H10302 37-bit huge ID", 37, {1, 0, 0, 0, 1, 0, 0x7FFFFFFFF, 0, 0}}, // from Proxmark forums diff --git a/common/commonutil.c b/common/commonutil.c index 48ce64bfa..ff0782514 100644 --- a/common/commonutil.c +++ b/common/commonutil.c @@ -450,6 +450,43 @@ void lslx(uint8_t *d, size_t n, uint8_t shifts) { } } +// right shift an array of length one bit +void rsl(uint8_t *d, size_t n) { + + uint8_t carry = 0; + + for (size_t i = 0; i < n; i++) { + + // Save the LSB before shifting + uint8_t new_carry = d[i] & 0x1; + + // Shift current byte right and incorporate previous carry + d[i] = (d[i] >> 1) | (carry ? 0x80 : 0); + + // Update carry for next byte + carry = new_carry; + } +} + +void rslx(uint8_t *d, size_t n, uint8_t shifts) { + + uint8_t carry = 0; + for (uint8_t j = 0; j < shifts; j++) { + + for (size_t i = 0; i < n; i++) { + + // Save the LSB before shifting + uint8_t new_carry = d[i] & 0x1; + + // Shift current byte right and incorporate previous carry + d[i] = (d[i] >> 1) | (carry ? 0x80 : 0); + + // Update carry for next byte + carry = new_carry; + } + } +} + // BSWAP24 of array[3] uint32_t le24toh(const uint8_t data[3]) { diff --git a/common/commonutil.h b/common/commonutil.h index f963805bb..00949b851 100644 --- a/common/commonutil.h +++ b/common/commonutil.h @@ -135,6 +135,9 @@ void xor(uint8_t *dest, const uint8_t *src, size_t n); void lsl(uint8_t *d, size_t n); void lslx(uint8_t *d, size_t n, uint8_t shifts); +void rsl(uint8_t *d, size_t n); +void rslx(uint8_t *d, size_t n, uint8_t shifts); + uint32_t le24toh(const uint8_t data[3]); void htole24(uint32_t val, uint8_t data[3]); diff --git a/common/crapto1/crypto1.c b/common/crapto1/crypto1.c index 78d42cec4..8ffe04fdb 100644 --- a/common/crapto1/crypto1.c +++ b/common/crapto1/crypto1.c @@ -35,8 +35,9 @@ int filter(uint32_t const x) { (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) void crypto1_init(struct Crypto1State *state, uint64_t key) { - if (state == NULL) + if (state == NULL) { return; + } state->odd = 0; state->even = 0; for (int i = 47; i > 0; i -= 2) { @@ -53,7 +54,9 @@ void crypto1_deinit(struct Crypto1State *state) { #if !defined(__arm__) || defined(__linux__) || defined(_WIN32) || defined(__APPLE__) // bare metal ARM Proxmark lacks calloc()/free() struct Crypto1State *crypto1_create(uint64_t key) { struct Crypto1State *state = calloc(sizeof(*state), sizeof(uint8_t)); - if (!state) return NULL; + if (state == NULL) { + return NULL; + } crypto1_init(state, key); return state; } @@ -145,8 +148,8 @@ uint32_t crypto1_word(struct Crypto1State *s, uint32_t in, int is_encrypted) { */ uint32_t prng_successor(uint32_t x, uint32_t n) { SWAPENDIAN(x); - while (n--) + while (n--) { x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31; - + } return SWAPENDIAN(x); } diff --git a/common/crc16.h b/common/crc16.h index c8aecaf21..5e0686fca 100644 --- a/common/crc16.h +++ b/common/crc16.h @@ -20,6 +20,7 @@ #include "common.h" +#define CRC16_SIZE 2 #define CRC16_POLY_CCITT 0x1021 #define CRC16_POLY_KERMIT 0x8408 #define CRC16_POLY_LEGIC 0xc6c6 //0x6363 diff --git a/common/default_version_pm3.c b/common/default_version_pm3.c index 084eb2b63..d93a7ef15 100644 --- a/common/default_version_pm3.c +++ b/common/default_version_pm3.c @@ -1,5 +1,20 @@ +//----------------------------------------------------------------------------- +// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// See LICENSE.txt for the text of the license. +//----------------------------------------------------------------------------- #include "common.h" -/* Generated file, do not edit */ +/* This is the default version_pm3.c file that Makefile.common falls back to if sh is not available */ #ifndef ON_DEVICE #define SECTVERSINFO #else @@ -8,10 +23,10 @@ const struct version_information_t SECTVERSINFO g_version_information = { VERSION_INFORMATION_MAGIC, - 1, - 1, - 2, - "Iceman/master/v4.20142", - "2025-03-25 16:18:49", - "4c1a288da" + 1, /* version 1 */ + 0, /* version information not present */ + 2, /* cleanliness couldn't be determined */ + "Iceman/master/unknown", + "1970-01-01 00:00:00", + "no sha256" }; diff --git a/common_arm/Makefile.common b/common_arm/Makefile.common index a845963b2..e8e574112 100644 --- a/common_arm/Makefile.common +++ b/common_arm/Makefile.common @@ -49,7 +49,7 @@ VPATH = . ../common_arm ../common ../common/crapto1 ../common/mbedtls ../common/ INCLUDES = ../include/proxmark3_arm.h ../include/at91sam7s512.h ../include/config_gpio.h ../include/pm3_cmd.h ARMCFLAGS = -mthumb-interwork -fno-builtin -DEFCFLAGS = -Wall -Os -pedantic -fstrict-aliasing -pipe +DEFCFLAGS = -Wall -Werror -Os -pedantic -fstrict-aliasing -pipe # Some more warnings we want as errors: DEFCFLAGS += -Wbad-function-cast -Wchar-subscripts -Wundef -Wunused -Wuninitialized -Wpointer-arith -Wformat -Wformat-security -Winit-self -Wmissing-include-dirs -Wnested-externs -Wempty-body -Wignored-qualifiers -Wmissing-field-initializers -Wtype-limits diff --git a/common_arm/Makefile.hal b/common_arm/Makefile.hal index f9bb9065e..568c245ba 100644 --- a/common_arm/Makefile.hal +++ b/common_arm/Makefile.hal @@ -29,15 +29,17 @@ define KNOWN_PLATFORM_DEFINITIONS Known definitions: -+============================================+ ++==================================================+ | PLATFORM | DESCRIPTION | -+============================================+ ++==================================================+ | PM3RDV4 (def) | Proxmark3 RDV4 | -+--------------------------------------------+ ++--------------------------------------------------+ | PM3GENERIC | Proxmark3 generic target | -+--------------------------------------------+ ++--------------------------------------------------+ | PM3ICOPYX | iCopy-X with XC3S100E | -+--------------------------------------------+ ++--------------------------------------------------+ +| PM3ULTIMATE | Proxmark3 Ultimate with XC2S50 | ++--------------------------------------------------+ +============================================+ | PLATFORM_EXTRAS | DESCRIPTION | @@ -153,7 +155,21 @@ else ifeq ($(PLATFORM),PM3ICOPYX) PLATFORM_DEFS = -DWITH_FLASH -DICOPYX -DXC3 PLTNAME = iCopy-X with XC3S100E PLATFORM_FPGA = xc3s100e - +else ifeq ($(PLATFORM),PM3ULTIMATE) + # FPGA bitstream files, the order doesn't matter anymore - only hf has a bitstream + FPGA_BITSTREAMS = fpga_pm3_ult_hf.bit + ifneq ($(SKIP_LF),1) + FPGA_BITSTREAMS += fpga_pm3_ult_lf.bit + endif + ifneq ($(SKIP_FELICA),1) + FPGA_BITSTREAMS += fpga_pm3_ult_felica.bit + endif + ifneq ($(SKIP_ISO15693),1) + FPGA_BITSTREAMS += fpga_pm3_ult_hf_15.bit + endif + PLATFORM_DEFS = -DWITH_FLASH -DXC2S50 + PLTNAME = Proxmark3 Ultimate with XC2S50 + PLATFORM_FPGA = xc2s50 else $(error Invalid or empty PLATFORM: $(PLATFORM). $(KNOWN_DEFINITIONS)) endif @@ -252,6 +268,10 @@ endif # WITH_FPC_USART_* needs WITH_FPC_USART : ifneq (,$(findstring WITH_FPC_USART_,$(PLATFORM_DEFS))) PLATFORM_DEFS += -DWITH_FPC_USART + ifeq ($(USART_BAUD_RATE),) + USART_BAUD_RATE=115200 + endif + PLATFORM_DEFS += -DUSART_BAUD_RATE=$(USART_BAUD_RATE) endif PLATFORM_DEFS_INFO = $(strip $(filter-out STANDALONE%, $(subst -DWITH_,,$(PLATFORM_DEFS)))) diff --git a/common_arm/ticks.c b/common_arm/ticks.c index 4abda2689..73182a11c 100644 --- a/common_arm/ticks.c +++ b/common_arm/ticks.c @@ -116,8 +116,9 @@ uint32_t RAMFUNC GetTickCount(void) { uint32_t RAMFUNC GetTickCountDelta(uint32_t start_ticks) { uint32_t stop_ticks = AT91C_BASE_RTTC->RTTC_RTVR; - if (stop_ticks >= start_ticks) + if (stop_ticks >= start_ticks) { return stop_ticks - start_ticks; + } return (UINT32_MAX - start_ticks) + stop_ticks; } diff --git a/common_fpga/fpga.h b/common_fpga/fpga.h index c383d2d44..1b009cd62 100644 --- a/common_fpga/fpga.h +++ b/common_fpga/fpga.h @@ -25,6 +25,9 @@ #if defined XC3 #define FPGA_TYPE "3s100evq100" #define FPGA_CONFIG_SIZE 72864L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE +#elif defined XC2S50 +#define FPGA_TYPE "2s50vq144" +#define FPGA_CONFIG_SIZE 69984L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE #else #define FPGA_TYPE "2s30vq100" #define FPGA_CONFIG_SIZE 42336L // FPGA .bit file rounded up to next multiple of FPGA_INTERLEAVE_SIZE diff --git a/doc/commands.json b/doc/commands.json index 2012b339e..8bc8b21f2 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -1324,7 +1324,6 @@ "notes": [ "hf 14a raw -sc 3000 -> select, crc, where 3000 == 'read block 00'", "hf 14a raw -ak -b 7 40 -> send 7 bit byte 0x40", - "hf 14a raw --ecp -s -> send ECP before select", "Crypto1 session example, with special auth shortcut 6xxx:", "hf 14a raw --crypto1 -skc 6000FFFFFFFFFFFF", "hf 14a raw --crypto1 -kc 3000", @@ -1343,22 +1342,18 @@ "-t, --timeout Timeout in milliseconds", "-b Number of bits to send. Useful for send partial byte", "-v, --verbose Verbose output", - "--ecp Use enhanced contactless polling", - "--mag Use Apple magsafe polling", "--topaz Use Topaz protocol to send command", "--crypto1 Use crypto1 session", " Raw bytes to send" ], - "usage": "hf 14a raw [-hack3rsv] [-t ] [-b ] [--ecp] [--mag] [--topaz] [--crypto1] []..." + "usage": "hf 14a raw [-hack3rsv] [-t ] [-b ] [--topaz] [--crypto1] []..." }, "hf 14a reader": { "command": "hf 14a reader", "description": "Act as a ISO-14443a reader to identify tag. Look for ISO-14443a tags until Enter or the pm3 button is pressed", "notes": [ "hf 14a reader", - "hf 14a reader -@ -> Continuous mode", - "hf 14a reader --ecp -> trigger apple enhanced contactless polling", - "hf 14a reader --mag -> trigger apple magsafe polling" + "hf 14a reader -@ -> Continuous mode" ], "offline": false, "options": [ @@ -1367,12 +1362,10 @@ "-s, --silent silent (no messages)", "--drop just drop the signal field", "--skip ISO14443-3 select only (skip RATS)", - "--ecp Use enhanced contactless polling", - "--mag Use Apple magsafe polling", "-@ continuous reader mode", "-w, --wait wait for card" ], - "usage": "hf 14a reader [-hks@w] [--drop] [--skip] [--ecp] [--mag]" + "usage": "hf 14a reader [-hks@w] [--drop] [--skip]" }, "hf 14a sim": { "command": "hf 14a sim", @@ -1389,7 +1382,8 @@ "hf 14a sim -t 9 -> FM11RF005SH Shanghai Metro", "hf 14a sim -t 10 -> ST25TA IKEA Rothult", "hf 14a sim -t 11 -> Javacard (JCOP)", - "hf 14a sim -t 12 -> 4K Seos card" + "hf 14a sim -t 12 -> 4K Seos card", + "hf 14a sim -t 13 -> MIFARE Ultralight C" ], "offline": false, "options": [ @@ -1399,9 +1393,11 @@ "-n, --num Exit simulation after blocks have been read by reader. 0 = infinite", "-x Performs the 'reader attack', nr/ar attack against a reader", "--sk Fill simulator keys from found keys", - "-v, --verbose verbose output" + "-v, --verbose verbose output", + "--c1 UL-C Auth - all zero handshake part 1", + "--c2 UL-C Auth - all zero handshake part 2" ], - "usage": "hf 14a sim [-hxv] -t <1-12> [-u ] [-n ] [--sk]" + "usage": "hf 14a sim [-hxv] -t <1-12> [-u ] [-n ] [--sk] [--c1] [--c2]" }, "hf 14a simaid": { "command": "hf 14a simaid", @@ -2519,7 +2515,7 @@ }, "hf emrtd help": { "command": "hf emrtd help", - "description": "help This help info Display info about an eMRTD list List ISO 14443A/7816 history --------------------------------------------------------------------------------------- hf emrtd dump available offline: no Dump all files on an eMRTD", + "description": "help This help info Tag information list List ISO 14443A/7816 history --------------------------------------------------------------------------------------- hf emrtd dump available offline: no Dump all files on an eMRTD", "notes": [ "hf emrtd dump", "hf emrtd dump --dir ../dump", @@ -3208,7 +3204,7 @@ }, "hf help": { "command": "hf help", - "description": "-------- ----------------------- High Frequency ----------------------- 14a { ISO14443A RFIDs... } 14b { ISO14443B RFIDs... } 15 { ISO15693 RFIDs... } cipurse { Cipurse transport Cards... } epa { German Identification Card... } emrtd { Machine Readable Travel Document... } felica { ISO18092 / FeliCa RFIDs... } fido { FIDO and FIDO2 authenticators... } fudan { Fudan RFIDs... } gallagher { Gallagher DESFire RFIDs... } iclass { ICLASS RFIDs... } ict { ICT MFC/DESfire RFIDs... } jooki { Jooki RFIDs... } ksx6924 { KS X 6924 (T-Money, Snapper+) RFIDs } legic { LEGIC RFIDs... } lto { LTO Cartridge Memory RFIDs... } mf { MIFARE RFIDs... } mfp { MIFARE Plus RFIDs... } mfu { MIFARE Ultralight RFIDs... } mfdes { MIFARE Desfire RFIDs... } ntag424 { NXP NTAG 4242 DNA RFIDs... } seos { SEOS RFIDs... } st25ta { ST25TA RFIDs... } tesla { TESLA Cards... } texkom { Texkom RFIDs... } thinfilm { Thinfilm RFIDs... } topaz { TOPAZ (NFC Type 1) RFIDs... } vas { Apple Value Added Service } waveshare { Waveshare NFC ePaper... } xerox { Fuji/Xerox cartridge RFIDs... } ----------- --------------------- General --------------------- help This help list List protocol data in trace buffer search Search for known HF tags --------------------------------------------------------------------------------------- hf list available offline: yes Alias of `trace list -t raw` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "-------- ----------------------- High Frequency ----------------------- 14a { ISO14443A RFIDs... } 14b { ISO14443B RFIDs... } 15 { ISO15693 RFIDs... } cipurse { Cipurse transport Cards... } epa { German Identification Card... } emrtd { Machine Readable Travel Document... } felica { ISO18092 / FeliCa RFIDs... } fido { FIDO and FIDO2 authenticators... } fudan { Fudan RFIDs... } gallagher { Gallagher DESFire RFIDs... } iclass { ICLASS RFIDs... } ict { ICT MFC/DESfire RFIDs... } jooki { Jooki RFIDs... } ksx6924 { KS X 6924 (T-Money, Snapper+) RFIDs } legic { LEGIC RFIDs... } lto { LTO Cartridge Memory RFIDs... } mf { MIFARE RFIDs... } mfp { MIFARE Plus RFIDs... } mfu { MIFARE Ultralight RFIDs... } mfdes { MIFARE Desfire RFIDs... } ntag424 { NXP NTAG 4242 DNA RFIDs... } seos { SEOS RFIDs... } st25ta { ST25TA RFIDs... } tesla { TESLA Cards... } texkom { Texkom RFIDs... } thinfilm { Thinfilm RFIDs... } topaz { TOPAZ (NFC Type 1) RFIDs... } vas { Apple Value Added Service... } waveshare { Waveshare NFC ePaper... } xerox { Fuji/Xerox cartridge RFIDs... } ----------- --------------------- General --------------------- help This help list List protocol data in trace buffer search Search for known HF tags --------------------------------------------------------------------------------------- hf list available offline: yes Alias of `trace list -t raw` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf list --frame -> show frame delay times", "hf list -1 -> use trace buffer" @@ -3251,7 +3247,7 @@ }, "hf iclass chk": { "command": "hf iclass chk", - "description": "Checkkeys loads a dictionary text file with 8byte hex keys to test authenticating against a iClass tag", + "description": "Checkkeys loads a dictionary text file with 8 byte hex keys to test authenticating against a iCLASS tag", "notes": [ "hf iclass chk -f iclass_default_keys.dic", "hf iclass chk -f iclass_elite_keys.dic --elite", @@ -3292,15 +3288,14 @@ "command": "hf iclass creditepurse", "description": "Credit the epurse on an iCLASS tag. The provided key must be the credit key. The first two bytes of the epurse are the debit value (big endian) and may be any value except FFFF. The remaining two bytes of the epurse are the credit value and must be smaller than the previous value.", "notes": [ - "hf iclass creditepurse -d FEFFFFFF -k 001122334455667B", - "hf iclass creditepurse -d FEFFFFFF --ki 0" + "hf iclass creditepurse --ki 0 -d FEFFFEFF" ], "offline": false, "options": [ "-h, --help This help", "-k, --key Credit key as 8 hex bytes", "--ki Key index to select key from memory 'hf iclass managekeys'", - "-d, --data data to write as 8 hex bytes", + "-d, --data data to write as 4 hex bytes", "--elite elite computations applied to key", "--raw no computations applied to key", "-v, --verbose verbose output", @@ -3375,13 +3370,12 @@ }, "hf iclass encode": { "command": "hf iclass encode", - "description": "Encode binary wiegand to block 7,8,9 Use either --bin or --wiegand/--fc/--cn", + "description": "Encode binary wiegand to block 7,8,9 Use either --bin or --wiegand/--fc/--cn When using emulator you have to first load a credential into emulator memory", "notes": [ "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337 (H10301)", "hf iclass encode -w H10301 --fc 31 --cn 337 --ki 0 -> FC 31 CN 337 (H10301)", "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337 (H10301), writing w elite key", - "hf iclass encode -w H10301 --fc 31 --cn 337 --emu -> Writes the ecoded data to emulator memory", - "When using emulator you have to first load a credential into emulator memory" + "hf iclass encode -w H10301 --fc 31 --cn 337 --emu -> Writes the ecoded data to emulator memory" ], "offline": true, "options": [ @@ -3501,7 +3495,7 @@ }, "hf iclass legbrute": { "command": "hf iclass legbrute", - "description": "This command take sniffed trace data and partial raw key and bruteforces the remaining 40 bits of the raw key.", + "description": "This command takes sniffed trace data and a partial raw key and bruteforces the remaining 40 bits of the raw key. Complete 40 bit keyspace is 1'099'511'627'776 and command is locked down to max 16 threads currently. A possible worst case scenario on 16 threads estimates XXX days YYY hours MMM minutes.", "notes": [ "hf iclass legbrute --epurse feffffffffffffff --macs1 1306cad9b6c24466 --macs2 f0bf905e35f97923 --pk B4F12AADC5301225" ], @@ -3512,13 +3506,14 @@ "--macs1 MACs captured from the reader", "--macs2 MACs captured from the reader, different than the first set (with the same csn and epurse value)", "--pk Partial Key from legrec or starting key of keyblock from legbrute", - "--index Where to start from to retrieve the key, default 0 - value in millions e.g. 1 is 1 million" + "--index Where to start from to retrieve the key, default 0 - value in millions e.g. 1 is 1 million", + "--threads Number of threads to use, by default it uses the cpu's max threads (max 16)." ], - "usage": "hf iclass legbrute [-h] --epurse --macs1 --macs2 --pk [--index ]" + "usage": "hf iclass legbrute [-h] --epurse --macs1 --macs2 --pk [--index ] [--threads ]" }, "hf iclass legrec": { "command": "hf iclass legrec", - "description": "Attempts to recover the diversified key of a specific iClass card. This may take a long time. The Card must remain be on the PM3 antenna during the whole process! This process may brick the card!", + "description": "Attempts to recover the diversified key of a specific iCLASS card. This may take several days. The card must remain be on the PM3 antenna during the whole process. ! Warning ! This process may brick the card! ! Warning !", "notes": [ "hf iclass legrec --macs 0000000089cb984b", "hf iclass legrec --macs 0000000089cb984b --index 0 --loop 100 --notest" @@ -3527,14 +3522,16 @@ "options": [ "-h, --help This help", "--macs AA1 Authentication MACs", - "--index Where to start from to retrieve the key, default 0", - "--loop The number of key retrieval cycles to perform, max 10000, default 100", - "--debug Re-enables tracing for debugging. Limits cycles to 1.", - "--notest Perform real writes on the card!", - "--allnight Loops the loop for 10 times, recommended loop value of 5000.", - "--est Estimates the key updates based on the card's CSN assuming standard key." + "--index Where to start from to retrieve the key (def: 0)", + "--loop The number of key retrieval cycles to perform, max 10000 (def 100)", + "--debug Re-enables tracing for debugging. Limits cycles to 1", + "--notest Perform real writes on the card", + "--allnight Loops the loop for 10 times, recommended loop value of 5000", + "--fast Increases the speed (4.6->7.4 key updates/second), higher risk to brick the card", + "--sl Lower card comms delay times, further speeds increases, may cause more errors", + "--est Estimates the key updates based on the card's CSN assuming standard key" ], - "usage": "hf iclass legrec [-h] --macs [--index ] [--loop ] [--debug] [--notest] [--allnight] [--est]" + "usage": "hf iclass legrec [-h] --macs [--index ] [--loop ] [--debug] [--notest] [--allnight] [--fast] [--sl] [--est]" }, "hf iclass loclass": { "command": "hf iclass loclass", @@ -3666,17 +3663,18 @@ "--elite elite computations applied to key", "--raw no computations applied to key", "-v, --verbose verbose output", - "--shallow use shallow (ASK) reader modulation instead of OOK" + "--shallow use shallow (ASK) reader modulation instead of OOK", + "--nr replay of nr mac with privilege escalation" ], - "usage": "hf iclass restore [-hv] -f [-k ] [--ki ] --first --last [--credit] [--elite] [--raw] [--shallow]" + "usage": "hf iclass restore [-hv] -f [-k ] [--ki ] --first --last [--credit] [--elite] [--raw] [--shallow] [--nr]" }, "hf iclass sam": { "command": "hf iclass sam", "description": "Extract PACS via a HID SAM", "notes": [ "hf iclass sam", - "hf iclass sam -p -d a005a103800104 -> get PACS data, but ensure that epurse will stay unchanged", - "hf iclass sam --break-on-nr-mac -> get Nr-MAC for extracting encrypted SIO" + "hf iclass sam -p -d a005a103800104 -> get PACS data, prevent epurse update", + "hf iclass sam --break -> get Nr-MAC for extracting encrypted SIO" ], "offline": false, "options": [ @@ -3685,12 +3683,14 @@ "-k, --keep keep the field active after command executed", "-n, --nodetect skip selecting the card and sending card details to SAM", "-t, --tlv decode TLV", - "--break-on-nr-mac stop tag interaction on nr-mac", - "-p, --prevent-epurse-update fake epurse update", + "--break stop tag interaction on nr-mac", + "-p, --prevent fake epurse update", "--shallow shallow mod", - "-d, --data DER encoded command to send to SAM" + "-d, --data DER encoded command to send to SAM", + "-s, --snmp data is in snmp format without headers", + "--info get SAM infos (version, serial number)" ], - "usage": "hf iclass sam [-hvkntp] [--break-on-nr-mac] [--shallow] [-d ]..." + "usage": "hf iclass sam [-hvkntps] [--break] [--shallow] [-d ]... [--info]" }, "hf iclass sim": { "command": "hf iclass sim", @@ -3700,7 +3700,9 @@ "hf iclass sim -t 1 -> simulate with default CSN", "hf iclass sim -t 2 -> execute loclass attack online part", "hf iclass sim -t 3 -> simulate full iCLASS 2k tag", - "hf iclass sim -t 4 -> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key" + "hf iclass sim -t 4 -> Reader-attack, adapted for KeyRoll mode, gather reader responses to extract elite key", + "hf iclass sim -t 6 -> simulate full iCLASS 2k tag that doesn't respond to r/w requests to the last SIO block", + "hf iclass sim -t 7 -> simulate full iCLASS 2k tag that doesn't XOR or respond to r/w requests on block 3" ], "offline": false, "options": [ @@ -3724,12 +3726,13 @@ ], "usage": "hf iclass sniff [-hj]" }, - "hf iclass trbl": { - "command": "hf iclass trbl", - "description": "Tear off an iCLASS tag block", + "hf iclass tear": { + "command": "hf iclass tear", + "description": "Tear off an iCLASS tag block e-purse usually 300-500us to trigger the erase phase also seen 1800-2100us on some cards Make sure you know the target card credit key. Typical `--ki 1` or `--ki 3`", "notes": [ - "hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B --tdb 100 --tde 150", - "hf iclass trbl --blk 10 -d AAAAAAAAAAAAAAAA --ki 0 --tdb 100 --tde 150" + "hf iclass tear --blk 10 -d AAAAAAAAAAAAAAAA -k 001122334455667B -s 300 -e 600", + "hf iclass tear --blk 10 -d AAAAAAAAAAAAAAAA --ki 0 -s 300 -e 600", + "hf iclass tear --blk 2 -d fdffffffffffffff --ki 1 --credit -s 400 -e 500" ], "offline": false, "options": [ @@ -3745,10 +3748,14 @@ "--nr replay of NR/MAC", "-v, --verbose verbose output", "--shallow use shallow (ASK) reader modulation instead of OOK", - "--tdb tearoff delay start in ms", - "--tde tearoff delay end in ms" + "-s tearoff delay start (in us) must be between 1 and 43000 (43ms). Precision is about 1/3 us", + "-i tearoff delay increment (in us) - default 10", + "-e tearoff delay end (in us) must be a higher value than the start delay", + "--loop number of times to loop per tearoff time", + "--sleep Sleep between each tear", + "--arm Runs the commands on device side and tries to stabilize tears" ], - "usage": "hf iclass trbl [-hv] [-k ] [--ki ] --blk -d [-m ] [--credit] [--elite] [--raw] [--nr] [--shallow] --tdb --tde " + "usage": "hf iclass tear [-hv] [-k ] [--ki ] --blk [-d ] [-m ] [--credit] [--elite] [--raw] [--nr] [--shallow] -s [-i ] [-e ] [--loop ] [--sleep ] [--arm]" }, "hf iclass unhash": { "command": "hf iclass unhash", @@ -4415,6 +4422,23 @@ ], "usage": "hf mf autopwn [-hablv] [-k ]... [-s ] [-f ] [--suffix ] [--slow] [--mem] [--ns] [--mini] [--1k] [--2k] [--4k] [--in] [--im] [--is] [--ia] [--i2] [--i5]" }, + "hf mf bambukeys": { + "command": "hf mf bambukeys", + "description": "Generate keys for a Bambu Lab filament tag", + "notes": [ + "hf mf bambukeys -r", + "hf mf bambukeys -r -d", + "hf mf bambukeys -u 11223344" + ], + "offline": true, + "options": [ + "-h, --help This help", + "-u, --uid UID (4 hex bytes)", + "-r Read UID from tag", + "-d Dump keys to file" + ], + "usage": "hf mf bambukeys [-hrd] [-u ]" + }, "hf mf brute": { "command": "hf mf brute", "description": "This is a smart bruteforce, exploiting common patterns, bugs and bad designs in key generators.", @@ -5149,7 +5173,7 @@ }, "hf mf help": { "command": "hf mf help", - "description": "help This help list List MIFARE history hardnested Nested attack for hardened MIFARE Classic cards decrypt Decrypt Crypto1 data from sniff or trace acl Decode and print MIFARE Classic access rights bytes mad Checks and prints MAD value Value blocks view Display content from tag dump file ginfo Info about configuration of the card gdmparsecfg Parse config block to card --------------------------------------------------------------------------------------- hf mf list available offline: yes Alias of `trace list -t mf -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "help This help list List MIFARE history hardnested Nested attack for hardened MIFARE Classic cards decrypt Decrypt Crypto1 data from sniff or trace bambukeys Generate key table for Bambu Lab filament tag acl Decode and print MIFARE Classic access rights bytes mad Checks and prints MAD value Value blocks view Display content from tag dump file ginfo Info about configuration of the card gdmparsecfg Parse config block to card --------------------------------------------------------------------------------------- hf mf list available offline: yes Alias of `trace list -t mf -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf mf list --frame -> show frame delay times", "hf mf list -1 -> use trace buffer" @@ -5467,7 +5491,8 @@ "hf mf sim --1k -u 11223344556677 -> MIFARE Classic 1k with 7b UID", "hf mf sim --1k -u 11223344 -i -x -> Perform reader attack in interactive mode", "hf mf sim --2k -> MIFARE 2k", - "hf mf sim --4k -> MIFARE 4khf mf sim --1k -x -e --> Keep simulation running and populate with found reader keys" + "hf mf sim --4k -> MIFARE 4k", + "hf mf sim --1k -x -e -> Keep simulation running and populate with found reader keys" ], "offline": false, "options": [ @@ -5737,8 +5762,8 @@ "description": "Checks keys with MIFARE DESFire card.", "notes": [ "hf mfdes chk --aid 123456 -k 000102030405060708090a0b0c0d0e0f -> check key on aid 0x123456", - "hf mfdes chk -d mfdes_default_keys -> check keys against all existing aid on card", - "hf mfdes chk -d mfdes_default_keys --aid 123456 -> check keys against aid 0x123456", + "hf mfdes chk -f mfdes_default_keys -> check keys against all existing aid on card", + "hf mfdes chk -f mfdes_default_keys --aid 123456 -> check keys against aid 0x123456", "hf mfdes chk --aid 123456 --pattern1b -j keys -> check all 1-byte keys pattern on aid 0x123456 and save found keys to `keys.json`", "hf mfdes chk --aid 123456 --pattern2b --startp2b FA00 -> check all 2-byte keys pattern on aid 0x123456. Start from key FA00FA00...FA00" ], @@ -5747,7 +5772,7 @@ "-h, --help This help", "--aid Use specific AID (3 hex bytes, big endian)", "-k, --key Key for checking (HEX 16 bytes)", - "-d, --dict Dictionary file with keys", + "-f, --file Filename of dictionary", "--pattern1b Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)", "--pattern2b Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)", "--startp2b Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)", @@ -5757,7 +5782,7 @@ "-i, --kdfi KDF input (1-31 hex bytes)", "-a, --apdu Show APDU requests and responses" ], - "usage": "hf mfdes chk [-hva] [--aid ] [-k ] [-d ] [--pattern1b] [--pattern2b] [--startp2b ] [-j ] [--kdf <0|1|2>] [-i ]" + "usage": "hf mfdes chk [-hva] [--aid ] [-k ] [-f ] [--pattern1b] [--pattern2b] [--startp2b ] [-j ] [--kdf <0|1|2>] [-i ]" }, "hf mfdes chkeysettings": { "command": "hf mfdes chkeysettings", @@ -6112,7 +6137,7 @@ "notes": [ "hf mfdes detect -> detect key 0 from PICC level", "hf mfdes detect --schann d40 -> detect key 0 from PICC level via secure channel D40", - "hf mfdes detect --dict mfdes_default_keys -> detect key 0 from PICC level with help of the standard dictionary", + "hf mfdes detect -f mfdes_default_keys -> detect key 0 from PICC level with help of the standard dictionary", "hf mfdes detect --aid 123456 -n 2 --save -> detect key 2 from app 123456 and if succeed - save params to defaults (`default` command)", "hf mfdes detect --isoid df01 --save -> detect key 0 and save to defaults with card in the LRP mode" ], @@ -6131,10 +6156,10 @@ "--schann Secure channel", "--aid Application ID (3 hex bytes, big endian)", "--isoid Application ISO ID (ISO DF ID) (2 hex bytes, big endian).", - "--dict Dictionary file name with keys", + "-f, --file Filename of dictionary", "--save Save found key and parameters to defaults" ], - "usage": "hf mfdes detect [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [--dict ] [--save]" + "usage": "hf mfdes detect [-hav] [-n ] [-t ] [-k ] [--kdf ] [-i ] [-m ] [-c ] [--schann ] [--aid ] [--isoid ] [-f ] [--save]" }, "hf mfdes dump": { "command": "hf mfdes dump", @@ -6752,9 +6777,9 @@ "-h, --help This help", "-v, --verbose Verbose output", "--ki Key number, 2 hex bytes", - "--key Key, 16 hex bytes" + "-k, --key Key, 16 hex bytes" ], - "usage": "hf mfp auth [-hv] --ki --key " + "usage": "hf mfp auth [-hv] --ki -k " }, "hf mfp chconf": { "command": "hf mfp chconf", @@ -6788,7 +6813,7 @@ "notes": [ "hf mfp chk -k 000102030405060708090a0b0c0d0e0f -> check key on sector 0 as key A and B", "hf mfp chk -s 2 -a -> check default key list on sector 2, only key A", - "hf mfp chk -d mfp_default_keys -s0 -e6 -> check keys from dictionary against sectors 0-6", + "hf mfp chk -f mfp_default_keys -s 0 -e 6 -> check keys from dictionary against sectors 0-6", "hf mfp chk --pattern1b --dump -> check all 1-byte keys pattern and save found keys to file", "hf mfp chk --pattern2b --startp2b FA00 -> check all 2-byte keys pattern. Start from key FA00FA00...FA00" ], @@ -6800,14 +6825,15 @@ "-s, --startsec <0..255> Start sector number", "-e, --endsec <0..255> End sector number", "-k, --key Key for checking (HEX 16 bytes)", - "-d, --dict Dictionary file with keys", + "-f, --file Dictionary file with default keys", "--pattern1b Check all 1-byte combinations of key (0000...0000, 0101...0101, 0202...0202, ...)", "--pattern2b Check all 2-byte combinations of key (0000...0000, 0001...0001, 0002...0002, ...)", "--startp2b Start key (2-byte HEX) for 2-byte search (use with `--pattern2b`)", "--dump Dump found keys to JSON file", + "--no-default Skip check default keys", "-v, --verbose Verbose output" ], - "usage": "hf mfp chk [-habv] [-s <0..255>] [-e <0..255>] [-k ] [-d ] [--pattern1b] [--pattern2b] [--startp2b ] [--dump]" + "usage": "hf mfp chk [-habv] [-s <0..255>] [-e <0..255>] [-k ] [-f ] [--pattern1b] [--pattern2b] [--startp2b ] [--dump] [--no-default]" }, "hf mfp chkey": { "command": "hf mfp chkey", @@ -7065,16 +7091,16 @@ }, "hf mfu aesauth": { "command": "hf mfu aesauth", - "description": "Tests AES key on Mifare Ultralight AES tags. If no key is specified, null key will be tried. Key index 0: DataProtKey (default) Key index 1: UIDRetrKey Key index 2: OriginalityKey", + "description": "Tests AES key on Mifare Ultralight AES tags. If no key is specified, null key will be tried. Key index 0... DataProtKey (default) Key index 1... UIDRetrKey Key index 2... OriginalityKey", "notes": [ "hf mfu aesauth", - "hf mfu aesauth --key <32 bytes> --index <0..2>" + "hf mfu aesauth --key <16 hex bytes> --idx <0..2>" ], "offline": false, "options": [ "-h, --help This help", - "--key AES key (32 hex bytes)", - "-i, --index <0..2> Key index, default: 0", + "--key AES key (16 hex bytes)", + "-i, --idx <0..2> Key index (def: 0)", "-k Keep field on (only if a key is provided)" ], "usage": "hf mfu aesauth [-hk] [--key ] [-i <0..2>]" @@ -7099,7 +7125,7 @@ }, "hf mfu cauth": { "command": "hf mfu cauth", - "description": "Tests 3DES password on Mifare Ultralight-C tag. If password is not specified, a set of known defaults will be tested.", + "description": "Tests 3DES key on Mifare Ultralight-C tag. If key is not specified, a set of known defaults will be tried.", "notes": [ "hf mfu cauth", "hf mfu cauth --key 000102030405060708090a0b0c0d0e0f" @@ -7107,9 +7133,9 @@ "offline": false, "options": [ "-h, --help This help", - "--key Authentication key (UL-C 16 hex bytes)", + "--key Authentication key (16 bytes in hex)", "-l Swap entered key's endianness", - "-k Keep field on (only if a password is provided)" + "-k Keep field on (only if a key is provided)" ], "usage": "hf mfu cauth [-hlk] [--key ]" }, @@ -7378,21 +7404,24 @@ }, "hf mfu sim": { "command": "hf mfu sim", - "description": "Simulate MIFARE Ultralight family type based upon ISO/IEC 14443 type A tag with 4,7 or 10 byte UID from emulator memory. See `hf mfu eload` first. The UID from emulator memory will be used if not specified. See `hf 14a sim -h` to see available types. You want 2 or 7 usually.", + "description": "Simulate MIFARE Ultralight family type based upon ISO/IEC 14443 type A tag with 4,7 or 10 byte UID from emulator memory. See `hf mfu eload` first. The UID from emulator memory will be used if not specified. See `hf 14a sim -h` to see available types. You want 2, 7 or 13 usually.", "notes": [ "hf mfu sim -t 2 --uid 11223344556677 -> MIFARE Ultralight", "hf mfu sim -t 7 --uid 11223344556677 -n 5 -> MFU EV1 / NTAG 215 Amiibo", - "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo" + "hf mfu sim -t 7 -> MFU EV1 / NTAG 215 Amiibo", + "hf mfu sim -t 13 -> MIFARE Ultralight-C" ], "offline": false, "options": [ "-h, --help This help", - "-t, --type <1..12> Simulation type to use", + "-t, --type <1..13> Simulation type to use", "-u, --uid <4|7|10> hex bytes UID", "-n, --num Exit simulation after blocks. 0 = infinite", - "-v, --verbose Verbose output" + "-v, --verbose Verbose output", + "--c1 UL-C Auth - all zero handshake part 1", + "--c2 UL-C Auth - all zero handshake part 2" ], - "usage": "hf mfu sim [-hv] -t <1..12> [-u ] [-n ]" + "usage": "hf mfu sim [-hv] -t <1..13> [-u ] [-n ] [--c1] [--c2]" }, "hf mfu tamper": { "command": "hf mfu tamper", @@ -7873,9 +7902,10 @@ ], "offline": true, "options": [ - "-h, --help This help" + "-h, --help This help", + "-p, --parse Parse the certificates as ASN.1" ], - "usage": "hf telsa info [-h]" + "usage": "hf telsa info [-hp]" }, "hf tesla list": { "command": "hf tesla list", @@ -8215,7 +8245,7 @@ }, "hf xerox help": { "command": "hf xerox help", - "description": "help This help list List ISO-14443B history -------- ----------------------- General ----------------------- view Display content from tag dump file --------------------------------------------------------------------------------------- hf xerox list available offline: yes Alias of `trace list -t 14b -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", + "description": "help This help list List ISO-14443B history -------- ----------------------- Operations ----------------------- view Display content from tag dump file --------------------------------------------------------------------------------------- hf xerox list available offline: yes Alias of `trace list -t 14b -c` with selected protocol data to annotate trace buffer You can load a trace from file (see `trace load -h`) or it be downloaded from device by default It accepts all other arguments of `trace list`. Note that some might not be relevant for this specific protocol", "notes": [ "hf 14b list --frame -> show frame delay times", "hf 14b list -1 -> use trace buffer" @@ -9377,11 +9407,10 @@ "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "--rnd Random 56-bit", "--frn F(RN) 28-bit as 4 hex bytes" ], - "usage": "lf em 4x70 auth [-h] [--par] --rnd --frn " + "usage": "lf em 4x70 auth [-h] --rnd --frn " }, "lf em 4x70 autorecover": { "command": "lf em 4x70 autorecover", @@ -9394,12 +9423,11 @@ "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "--rnd Random 56-bit from known-good authentication", "--frn F(RN) 28-bit as 4 hex bytes from known-good authentication", "--grn G(RN) 20-bit as 3 hex bytes from known-good authentication" ], - "usage": "lf em 4x70 autorecover [-h] [--par] --rnd --frn --grn " + "usage": "lf em 4x70 autorecover [-h] --rnd --frn --grn " }, "lf em 4x70 calc": { "command": "lf em 4x70 calc", @@ -9428,27 +9456,24 @@ "offline": true, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-b, --block block/word address, dec", "--rnd Random 56-bit", "--frn F(RN) 28-bit as 4 hex bytes", "-s, --start Start bruteforce enumeration from this key value" ], - "usage": "lf em 4x70 brute [-h] [--par] -b --rnd --frn [-s ]" + "usage": "lf em 4x70 brute [-h] -b --rnd --frn [-s ]" }, "lf em 4x70 info": { "command": "lf em 4x70 info", "description": "Tag Information EM4x70 Tag variants include ID48 automotive transponder. ID48 does not use command parity (default). V4070 and EM4170 do require parity bit.", "notes": [ - "lf em 4x70 info", - "lf em 4x70 info --par -> adds parity bit to command" + "lf em 4x70 info" ], "offline": false, "options": [ - "-h, --help This help", - "--par Add parity bit when sending commands" + "-h, --help This help" ], - "usage": "lf em 4x70 info [-h] [--par]" + "usage": "lf em 4x70 info [-h]" }, "lf em 4x70 recover": { "command": "lf em 4x70 recover", @@ -9461,13 +9486,12 @@ "offline": true, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-k, --key Key as 6 hex bytes", "--rnd Random 56-bit", "--frn F(RN) 28-bit as 4 hex bytes", "--grn G(RN) 20-bit as 3 hex bytes" ], - "usage": "lf em 4x70 recover [-h] [--par] -k --rnd --frn --grn " + "usage": "lf em 4x70 recover [-h] -k --rnd --frn --grn " }, "lf em 4x70 setkey": { "command": "lf em 4x70 setkey", @@ -9480,56 +9504,49 @@ "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-k, --key Key as 12 hex bytes" ], - "usage": "lf em 4x70 setkey [-h] [--par] -k " + "usage": "lf em 4x70 setkey [-h] -k " }, "lf em 4x70 setpin": { "command": "lf em 4x70 setpin", "description": "Write new PIN", "notes": [ - "lf em 4x70 setpin -p 11223344 -> Write new PIN", - "lf em 4x70 setpin -p 11223344 --par -> Write new PIN using parity commands" + "lf em 4x70 setpin -p 11223344 -> Write new PIN" ], "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-p, --pin pin, 4 bytes" ], - "usage": "lf em 4x70 setpin [-h] [--par] -p " + "usage": "lf em 4x70 setpin [-h] -p " }, "lf em 4x70 unlock": { "command": "lf em 4x70 unlock", "description": "Unlock EM4x70 by sending PIN Default pin may be: AAAAAAAA 00000000", "notes": [ - "lf em 4x70 unlock -p 11223344 -> Unlock with PIN", - "lf em 4x70 unlock -p 11223344 --par -> Unlock with PIN using parity commands" + "lf em 4x70 unlock -p 11223344 -> Unlock with PIN" ], "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-p, --pin pin, 4 bytes" ], - "usage": "lf em 4x70 unlock [-h] [--par] -p " + "usage": "lf em 4x70 unlock [-h] -p " }, "lf em 4x70 write": { "command": "lf em 4x70 write", "description": "Write EM4x70", "notes": [ - "lf em 4x70 write -b 15 -d c0de -> write 'c0de' to block 15", - "lf em 4x70 write -b 15 -d c0de --par -> adds parity bit to commands" + "lf em 4x70 write -b 15 -d c0de -> write 'c0de' to block 15" ], "offline": false, "options": [ "-h, --help This help", - "--par Add parity bit when sending commands", "-b, --block block/word address, dec", "-d, --data data, 2 bytes" ], - "usage": "lf em 4x70 write [-h] [--par] -b -d " + "usage": "lf em 4x70 write [-h] -b -d " }, "lf em help": { "command": "lf em help", @@ -12109,24 +12126,27 @@ }, "mem load": { "command": "mem load", - "description": "Loads binary file into flash memory on device Warning: mem area to be written must have been wiped first ( dictionaries are serviced as files in spiffs so no wipe is needed )", + "description": "Loads binary file into flash memory on device Warning! - mem area to be written must have been wiped first OBS! - dictionaries are serviced as files in spiffs so no wipe is needed", "notes": [ "mem load -f myfile -> upload file myfile values at default offset 0", "mem load -f myfile -o 1024 -> upload file myfile values at offset 1024", - "mem load -f mfc_default_keys -m -> upload MFC keys", + "mem load -f mfc_default_keys -m -> upload MIFARE Classic keys", "mem load -f t55xx_default_pwds -t -> upload T55XX passwords", - "mem load -f iclass_default_keys -i -> upload iCLASS keys" + "mem load -f iclass_default_keys -i -> upload iCLASS keys", + "mem load -f mfulc_default_keys --ulc -> upload MIFARE UL-C keys" ], "offline": false, "options": [ "-h, --help This help", "-o, --offset offset in memory", - "-m, --mifare, --mfc upload 6 bytes keys (mifare key dictionary)", - "-i, --iclass upload 8 bytes keys (iClass key dictionary)", - "-t, --t55xx upload 4 bytes keys (password dictionary)", + "-m, --mfc upload 6 bytes keys (MIFARE Classic dictionary)", + "-i, --iclass upload 8 bytes keys (iClass dictionary)", + "-t, --t55xx upload 4 bytes keys (T55xx dictionary)", + "--ulc upload 16 bytes keys (MIFARE UL-C dictionary)", + "--aes upload 16 bytes keys (MIFARE UL-AES dictionary)", "-f, --file file name" ], - "usage": "mem load [-hmit] [-o ] -f " + "usage": "mem load [-hmit] [-o ] [--ulc] [--aes] -f " }, "mem spiffs check": { "command": "mem spiffs check", @@ -13355,8 +13375,8 @@ } }, "metadata": { - "commands_extracted": 767, + "commands_extracted": 768, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2025-03-24T22:47:29" + "extracted_on": "2025-07-04T10:19:21" } } diff --git a/doc/commands.md b/doc/commands.md index 12312b7fc..9cdcf7a47 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -277,7 +277,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf cipurse help `|Y |`This help.` -|`hf cipurse info `|N |`Get info about CIPURSE tag` +|`hf cipurse info `|N |`Tag information` |`hf cipurse select `|N |`Select CIPURSE application or file` |`hf cipurse auth `|N |`Authenticate CIPURSE tag` |`hf cipurse read `|N |`Read binary file` @@ -313,7 +313,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf emrtd help `|Y |`This help` |`hf emrtd dump `|N |`Dump eMRTD files to binary files` -|`hf emrtd info `|Y |`Display info about an eMRTD` +|`hf emrtd info `|Y |`Tag information` |`hf emrtd list `|Y |`List ISO 14443A/7816 history` @@ -351,7 +351,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf fido help `|Y |`This help.` |`hf fido list `|Y |`List ISO 14443A history` -|`hf fido info `|N |`Info about FIDO tag.` +|`hf fido info `|N |`Tag information` |`hf fido reg `|N |`FIDO U2F Registration Message.` |`hf fido auth `|N |`FIDO U2F Authentication Message.` |`hf fido make `|N |`FIDO2 MakeCredential command.` @@ -404,7 +404,7 @@ Check column "offline" for their availability. |`hf iclass view `|Y |`Display content from tag dump file` |`hf iclass wrbl `|N |`Write Picopass / iCLASS block` |`hf iclass creditepurse `|N |`Credit epurse value` -|`hf iclass trbl `|N |`Performs tearoff attack on iClass block` +|`hf iclass tear `|N |`Performs tearoff attack on iCLASS block` |`hf iclass chk `|N |`Check keys` |`hf iclass loclass `|Y |`Use loclass to perform bruteforce reader attack` |`hf iclass lookup `|Y |`Uses authentication trace to check for key in dictionary file` @@ -460,7 +460,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf ksx6924 help `|Y |`This help` |`hf ksx6924 select `|N |`Select application, and leave field up` -|`hf ksx6924 info `|N |`Get info about a KS X 6924 (T-Money, Snapper+) transit card` +|`hf ksx6924 info `|N |`Tag information` |`hf ksx6924 balance `|N |`Get current purse balance` |`hf ksx6924 init `|N |`Perform transaction initialization with Mpda` |`hf ksx6924 prec `|N |`Send proprietary get record command (CLA=90, INS=4C)` @@ -514,8 +514,6 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf mf help `|Y |`This help` |`hf mf list `|Y |`List MIFARE history` -|`hf mf info `|N |`mfc card Info` -|`hf mf isen `|N |`mfc card Info Static Encrypted Nonces` |`hf mf darkside `|N |`Darkside attack` |`hf mf nested `|N |`Nested attack` |`hf mf hardnested `|Y |`Nested attack for hardened MIFARE Classic cards` @@ -527,9 +525,12 @@ Check column "offline" for their availability. |`hf mf fchk `|N |`Check keys fast, targets all keys on card` |`hf mf decrypt `|Y |`Decrypt Crypto1 data from sniff or trace` |`hf mf supercard `|N |`Extract info from a `super card`` +|`hf mf bambukeys `|Y |`Generate key table for Bambu Lab filament tag` |`hf mf auth4 `|N |`ISO14443-4 AES authentication` |`hf mf acl `|Y |`Decode and print MIFARE Classic access rights bytes` |`hf mf dump `|N |`Dump MIFARE Classic tag to binary file` +|`hf mf info `|N |`Tag information` +|`hf mf isen `|N |`Information Static Encrypted Nonces` |`hf mf mad `|Y |`Checks and prints MAD` |`hf mf personalize `|N |`Personalize UID (MIFARE Classic EV1 only)` |`hf mf rdbl `|N |`Read MIFARE Classic block` @@ -589,7 +590,7 @@ Check column "offline" for their availability. |`hf mfp auth `|N |`Authentication` |`hf mfp chk `|N |`Check keys` |`hf mfp dump `|N |`Dump MIFARE Plus tag to binary file` -|`hf mfp info `|N |`Info about MIFARE Plus tag` +|`hf mfp info `|N |`Tag information` |`hf mfp mad `|N |`Check and print MAD` |`hf mfp rdbl `|N |`Read blocks from card` |`hf mfp rdsc `|N |`Read sectors from card` @@ -786,7 +787,7 @@ Check column "offline" for their availability. ### hf vas - { Apple Value Added Service } + { Apple Value Added Service... } |command |offline |description |------- |------- |----------- @@ -813,7 +814,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`hf xerox help `|Y |`This help` |`hf xerox list `|Y |`List ISO-14443B history` -|`hf xerox info `|N |`Short info on Fuji/Xerox tag` +|`hf xerox info `|N |`Tag information` |`hf xerox dump `|N |`Read all memory pages of an Fuji/Xerox tag, save to file` |`hf xerox reader `|N |`Act like a Fuji/Xerox reader` |`hf xerox view `|Y |`Display content from tag dump file` @@ -990,7 +991,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf em 4x70 help `|Y |`This help` |`lf em 4x70 brute `|N |`Bruteforce EM4X70 to find partial key` -|`lf em 4x70 info `|N |`Tag information EM4x70` +|`lf em 4x70 info `|N |`Tag information` |`lf em 4x70 write `|N |`Write EM4x70` |`lf em 4x70 unlock `|N |`Unlock EM4x70 for writing` |`lf em 4x70 auth `|N |`Authenticate EM4x70` @@ -1063,7 +1064,7 @@ Check column "offline" for their availability. |------- |------- |----------- |`lf hitag help `|Y |`This help` |`lf hitag list `|Y |`List Hitag trace history` -|`lf hitag info `|N |`Hitag 2 tag information` +|`lf hitag info `|N |`Tag information` |`lf hitag reader `|N |`Act like a Hitag 2 reader` |`lf hitag test `|Y |`Perform self tests` |`lf hitag dump `|N |`Dump Hitag 2 tag` @@ -1401,7 +1402,7 @@ Check column "offline" for their availability. |`mem spiffs copy `|N |`Copy a file to another (destructively) in SPIFFS file system` |`mem spiffs check `|N |`Check/try to defrag faulty/fragmented file system` |`mem spiffs dump `|N |`Dump a file from SPIFFS file system` -|`mem spiffs info `|N |`Print file system info and usage statistics` +|`mem spiffs info `|N |`File system information and usage statistics` |`mem spiffs mount `|N |`Mount the SPIFFS file system if not already mounted` |`mem spiffs remove `|N |`Remove a file from SPIFFS file system` |`mem spiffs rename `|N |`Rename/move a file in SPIFFS file system` diff --git a/doc/fpga_arm_notes.md b/doc/fpga_arm_notes.md index 4cc47b88a..21eba6818 100644 --- a/doc/fpga_arm_notes.md +++ b/doc/fpga_arm_notes.md @@ -73,9 +73,9 @@ There is a docker image with webpack installed which has been built which you ca ``` docker pull nhutton/prox-container:webp_image_complete -docker run -v /proxmark3:/tmp --rm -it nhutton/prox-container:webp_image_complete bash -$ cd /tmp/proxmark/fpga -$ make all +docker run -v /proxmark3:/tmp/proxmark3 --rm -it nhutton/prox-container:1.0 bash +$ cd /tmp/proxmark3/fpga +$ make all -j ``` In order to save space, these fpga images are LZ4 compressed and included in the fullimage.elf file when compiling the ARM SRC. `make armsrc` diff --git a/doc/hid_downgrade.md b/doc/hid_downgrade.md index c5a85f0ab..3cc23beea 100644 --- a/doc/hid_downgrade.md +++ b/doc/hid_downgrade.md @@ -24,6 +24,7 @@ This document targets both Proxmark3 and Flipper Zero devices. - [Simulate a standard keyed iCLASS legacy credential](#simulate-a-standard-keyed-iclass-legacy-credential) - [Write a downgraded iCLASS legacy credential](#write-a-downgraded-iclass-legacy-credential) - [Using Omnikey Reader 5427CK Gen2 and Proxmark3](#using-omnikey-reader-5427ck-gen2-and-proxmark3) + - [Using Elatec TWN4 or TWN4 mini (USB front reader)](#using-elatec-twn4-or-twn4-mini-usb-front-reader) - [Using Flipper Zero with NARD](#using-flipper-zero-with-nard) - [Using Weaponized HID Reader](#using-weaponized-hid-reader) - [Write ProxII credential to a T5577](#write-proxii-credential-to-a-t5577) @@ -82,7 +83,7 @@ Unfortantely not all readers will have iCLASS legacy enabled and your **downgrad For the next steps, you will need a `Proxmark3` or `Flipper Zero` device. -## Verfiy reader has iCLASS legacy enabled +## Verify reader has iCLASS legacy enabled ^[Top](#top) Present a standard keyed iCLASS legacy credential at the reader and see if it beeps. @@ -191,6 +192,20 @@ drop iclass-flipper.picopass file here and simulate on Flipper 7. Launch PM3 client, place iCLASS/Picopass card on HF antenna and read your original card on the Omnikey reader 8. Press enter +## Using Elatec TWN4 or TWN4 mini (USB front reader) +^[Top](#top) + +OBS! +The reader must have the `PI` designation on the label for it to have a embedded HID SAM. If you have a reader with a different configuration as per the label, an HID SAM will have to be installed in the SAM slot. + +1. Plug in Elatec reader +2. Launch [appblaster.exe](../tools/twn/AppBlaster.exe) +3. Click on "program firmware image" +4. Select [encoder.bix](../tools/twn/encoder.bix) as the reader firmware +5. Click program image +6. Launch PM3 client, place iCLASS/Picopass card on HF antenna and read your original card on the Elatec reader +8. Press enter + ## Using Flipper Zero with NARD ^[Top](#top) @@ -222,7 +237,7 @@ Prequisite, you will need the following bill of materials (BOM): * Some 20-24 AWG wire or ethernet cable * Your preferred power source (5-9v) -The easiest way is to buy a [ESPKEY](https://www.aliexpress.com/item/32850151497.html) +The easiest way is to buy a [ESPKEY](https://www.aliexpress.com/w/wholesale-esp-rfid-tool.html)) Follow these steps: diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index a1d4e7350..1b43873e5 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -27,7 +27,6 @@ Useful docs: * [MIFARE Classic block0](#mifare-classic-block0) * [MIFARE Classic Gen1A aka UID](#mifare-classic-gen1a-aka-uid) * [MIFARE Classic Gen1B](#mifare-classic-gen1b) - * [Mifare Classic Direct Write OTP](#mifare-classic-direct-write-otp) * [MIFARE Classic OTP 2.0](#mifare-classic-otp-20) * [MIFARE Classic MF4](#mifare-classic-mf4) * [MIFARE Classic DirectWrite aka Gen2 aka CUID](#mifare-classic-directwrite-aka-gen2-aka-cuid) @@ -47,12 +46,15 @@ Useful docs: * [MIFARE Ultralight EV1 DirectWrite](#mifare-ultralight-ev1-directwrite) * [MIFARE Ultralight C Gen1A](#mifare-ultralight-c-gen1a) * [MIFARE Ultralight C DirectWrite](#mifare-ultralight-c-directwrite) - * [UL series (RU)](#ul-series-ru) + * [MIFARE Ultralight USCUID-UL](#mifare-ultralight-uscuid-ul) + * [UL-2](#ul-2) + * [UL-2 (20 blocks)](#ul-2-20-blocks) + * [UL-2 (41 blocks)](#ul-2-41-blocks) + * [UL-2 (44 blocks)](#ul-2-44-blocks) * [UL-Y](#ul-y) - * [ULtra](#ultra) + * [Ultra](#ultra-ru) * [UL-5](#ul-5) * [UL, other chips](#ul-other-chips) - * [MIFARE Ultralight USCUID-UL](#mifare-ultralight-uscuid-ul) * [NTAG](#ntag) * [NTAG213 DirectWrite](#ntag213-directwrite) * [NTAG21x](#ntag21x) @@ -618,7 +620,7 @@ hf mf info ^[Top](#top) Similar to Gen1A, but after first block 0 edit, tag no longer replies to 0x40 command. -Were manufactured by iKey LLC as a replacement for [OTP](#mifare-classic-direct-write-otp) +Were manufactured by iKey LLC as a replacement for [OTP](#fuid) ### Characteristics @@ -743,9 +745,9 @@ Here is how the IC can be configured: * Other names: * MF-8 (RU) - * MF-3 (RU) - not susceptible to "field reset bug", a way to detect [OTP](#mifare-classic-direct-write-otp) chips. - * MF-3.2 (RU) - static nonce `01200145`, helps avoid magic detection. - + * MF-3 (RU) - not susceptible to "field reset bug", a way to detect [OTP](#fuid) chips. + * MF-3.2 (RU) - static nonce `01200145`, potentially fixed chip which can bypass Iron Logic's filters. +` ### Identify ^[Top](#top) @@ -1146,13 +1148,26 @@ Well-known variations are described below. ^[Top](#top) -Known as "write only once", which is only partially true. Please note that some newer FUIDs have had ton configration blocks locked down and are truly a write-once tag. +* Other names: + * OTP (RU) -Allows direct write to block 0 only when UID is default `AA55C396`. If your tag responds to a gen4 magic wakeup, the UID could always be rewritten multiple times with backdoors commands. +Known as "write only once", which is only partially true, because old revisions had backdoor commands enabled, so you could manipulate the tag, using them. +Newer FUIDs are based on new implementation of chip and have backdoor commands disabled by default. -Backdoor commands are available even after the personalization and makes that tag detectable. +Allows direct write to block 0 only when UID is default `AA55C396`. If your tag responds to a `20(7)`, `23` magic wakeup, the UID could always be rewritten multiple times with backdoors commands, but that makes that tag detecteable. -That's a key difference from [OTP](#mifare-classic-direct-write-otp)/[OTP 2.0](#mifare-classic-otp-20) tags. +### Market Usage + +In ex-USSR countries were widely used as a replacement for UID tags. Especially for protected Iron Logic readers.Later filter `OTP` was created in those readers. +The idea of the filter is that old version's chip had an issue in the protocol implementation. + +The reader could interrupt radiofield for 2-3 microseconds (standard pause in the bit period of ISO14443-2). +After the response to first `26 (7)` command, but before the following `93 70` command. In that case original M1 card will stop the flow, but OTP will continue it. + +That issue led to the development of the filters against that card and discontinuation of the production. +As a successor, [OTP 2.0](#mifare-classic-otp-20) was created for that market. + +Newer FUID tags (with backdoor command disabled) has protocol fixed and works fine on Iron Logic readers with firmware older than 7.28, but are filtered by latest filters on mentioned firmware. ### Characteristics @@ -1177,7 +1192,7 @@ hf mf info ``` -or locked down tag type: +Or locked down tag type: ``` hf mf info @@ -1343,16 +1358,22 @@ All commands are available before sealing. ^[Top](#top) -That tag is a UID tag, built on USCUID chip. It doesn't sold separately, but could be found on marketplaces under the guise of a UID tag. +That tag is a UID tag, built on USCUID chip. It is not sold separately, but could be found on marketplaces under the guise of a UID tag. ### Characteristics ^[Top](#top) -* Configuration block value: `7AFF0000000000000000000000000008` -* No direct write to block 0 +* Default configuration block value: `7AFF0000000000000000000000000008` * Responds to magic wakeup `40(7)`, `43` commands -* Acknowledge only the first (except wakeup command) and last config byte(s), so doesn't have the hidden block +* Does not have hidden blocks, and only acknowledges the following bytes marked with carats. As such, Gen 1a mode cannot be disabled. All acknowledged bytes follow the standard USCUID format. + +``` +7AFF0000000000000000000000000008 + ^^ >> Block use of Key B if readable by ACL + ^^ >> CUID mode + ^^ >> SAK*** +``` ### Identify @@ -1362,13 +1383,10 @@ That tag is a UID tag, built on USCUID chip. It doesn't sold separately, but cou hf mf info ... [+] Magic capabilities... Gen 1a -[+] Magic capabilities... Gen 4 GDM / USCUID ( Gen1 Magic Wakeup ) +[+] Magic capabilities... Gen 4 GDM / USCUID ( ZUID Gen1 Magic Wakeup ) ``` -Currently Proxmark3 doesn't identify it as a separate tag. -Could be detected from the config block value. - ### Parsed configuration ^[Top](#top) @@ -1398,8 +1416,6 @@ Could be detected from the config block value. * Magic wakeup: `40(7)`, `43` * Backdoor read main block: `30xx+crc` * Backdoor write main block: `A0xx+crc`, `[16 bytes data]+crc` - * Read hidden block: `38xx+crc` - * Write hidden block: `A8xx+crc`, `[16 bytes data]+crc` * Read configuration: `E000+crc` * Write configuration: `E100+crc` @@ -2020,121 +2036,6 @@ Anticol shortcut (CL1/3000): fails script run hf_mfu_magicwrite -h ``` -## UL series (RU) - -^[Top](#top) - -Custom chips, manufactured by iKey LLC for cloning Ultralight tags used in Visit intercoms. That leads to the non-standard for Ultralight chips tag version. - -### UL-Y - -^[Top](#top) - -Ultralight magic, 16 pages. Recommended for Vizit RF3.1 with markings "3.1" or "4.1". -Behavior: allows writes to page 0-2. - -#### Identify - -^[Top](#top) - -``` -hf mfu rdbl --force -b 16 -hf 14a raw -sct 250 60 -``` - -If tag replies with -`Cmd Error: 00` -`00 00 00 00 00 00 00 00` -then it is UL-Y. - -### ULtra - -^[Top](#top) - -Ultralight EV1 magic; 41 page. Recommended for Vizit RF3.1 with 41 page. -Behavior: allows writes to page 0-2. - -#### Identify - -^[Top](#top) - -``` -hf mfu info -... -[=] TAG IC Signature: 0000000000000000000000000000000000000000000000000000000000000000 -[=] --- Tag Version -[=] Raw bytes: 00 34 21 01 01 00 0E 03 -[=] Vendor ID: 34, Mikron JSC Russia -[=] Product type: 21, unknown -``` - -#### ULtra flavour 1 - -^[Top](#top) - -Could be identified by indirect evidence before writing - -* Initial UID: `34 D7 08 11 AD D7 D0` -* `hf mfu dump --ns` - - ``` - [=] 3/0x03 | CF 39 A1 C8 | 1 | .9.. - [=] 4/0x04 | B6 69 26 0D | 1 | .i&. - [=] 5/0x05 | EC A1 73 C4 | 1 | ..s. - [=] 6/0x06 | 81 3D 29 B8 | 1 | .=). - [=] 16/0x10 | 6A F0 2D FF | 0 | j.-. - [=] 20/0x14 | 6A F0 2D FF | 0 | j.-. - [=] 24/0x18 | 6A F0 2D FF | 0 | j.-. - [=] 38/0x26 | 00 E2 00 00 | 0 | .... <- E2, Virtual Card Type Identifier is not default - - ``` - -#### ULtra flavour 2 - -^[Top](#top) - -Could be identified by indirect evidence before writing - -* Initial UID: `04 15 4A 23 36 2F 81` -* Values in pages `3, 4, 5, 6, 16, 20, 24, 38` are default for that tag flavour - -### UL-5 - -^[Top](#top) - -Ultralight EV1 magic; 41 page. Recommended for Vizit RF3.1 with 41 page. -Created as a response to filters that try to overwrite page 0 (as a detection for [ULtra](#mifare-ultra) tags). - -Behavior: similar to Ultra, but after editing page 0 become locked and tag becomes the original Mifare Ultralight EV1 (except the tag version, which remains specific). - -**WARNING!** When using UL-5 to clone, write UID pages in inverse (from 2 to 0) and do NOT make mistakes! This tag does not allow reversing one-way actions (OTP page, lock bits). - -#### Identify - -^[Top](#top) - -``` -hf mfu info -... -TAG IC Signature: 0000000000000000000000000000000000000000000000000000000000000000 -[=] --- Tag Version -[=] Raw bytes: 00 34 21 01 01 00 0E 03 -[=] Vendor ID: 34, Mikron JSC Russia -``` - -After personalization it is not possible to identify UL-5. - -The manufacturer confirmed unpersonalized tags could be identified by first 3 bytes of UID: - -* `AA 55 39...` -* `AA 55 C3...` - -### UL, other chips - -**TODO** - -UL-X, UL-Z - ? - ## MIFARE Ultralight USCUID-UL ^[Top](#top) @@ -2305,7 +2206,9 @@ No implemented commands at time of writing No implemented commands at time of writing ### Variations + ^[Top](#top) + | Factory configuration | Name | | --- | --- | | 850000A0 00000AC3 00040301 01000B03 | UL-11 | @@ -2315,6 +2218,237 @@ No implemented commands at time of writing | 850000A0 00000A5A 00040402 01001103 | NTAG215 | | 850000A0 00000AAA 00040402 01001303 | NTAG216 | +Variations of USCUID-UL, that were distributed in ex-USSR countries are known as UL-family. +Different variarions were targeted for copying different original tags + for bypassing of different filters. + +## UL-2 + +^[Top](#top) + +Sold on Russian market in variations with 20, 41 and 44 blocks. +All variations support direct write to block 0-2. + +### UL-2 (20 blocks) + +#### Characteristics + +^[Top](#top) + +* Configuration block value: `850000A000000AC30034210101000B03`. +* EV1 Version: `0034210101000B03`. + +#### Identify + +^[Top](#top) + +``` +[usb] pm3 --> hf 14a info +... +[+] ATS: 85 00 00 A0 00 00 0A C3 00 34 21 01 01 00 0B 03 [ 84 00 ] +``` + +### UL-2 (41 blocks) + +Default configuration for USCUID-UL with 41 blocks. Can be found in China by names UL-21 or Ultra (targeting Russian market). + +In China exists in versions with opened and locked configuration. +Could be used for intercoms Grazhda (UA) and Vizit (RU) with non-Micron chips (original chips have EV1 Version `0004030101000E03`). + +* Other names: + * Ultra (China) + * UL-21 (China) + +#### Characteristics + +^[Top](#top) + +* Configuration block value: `850000A000000A3C0004030101000E03`. +* EV1 Version: `0004030101000E03`. + +#### Identify + +^[Top](#top) + +``` +[usb] pm3 --> hf 14a info +... +[+] ATS: 85 00 00 A0 00 00 0A 3C 00 04 03 01 01 00 0E 03 [ C8 1D ] +``` + +### UL-2 (44 blocks) + +#### Characteristics + +^[Top](#top) + +* Configuration block value: `850000A000000A5A0034210101000E03`. +* EV1 Version: `0034210101000E03`. + +#### Identify + +^[Top](#top) + +``` +[usb] pm3 --> hf 14a info +... +[+] ATS: 85 00 00 A0 00 00 0A 5A 00 34 21 01 01 00 0E 03 [ F1 F3 ] +``` + +## UL-Y + +^[Top](#top) + +Variation based on NTAG215 config. +Created for copying 16-blocks Vizit tags. +Now there are well-known 2 variations, which differs only with EV1 Version. +Newer has *Micron Russia* version. + +### Characteristics + +^[Top](#top) + +* Configuration block value: `850000A0AA000A5A0000000000000000` or `850000A0AA000A5A0034210100000000`. +* EV1 Version: `0000000000000000` or `0034210100000000`. +* Has 16 blocks readable. +* Allows write to pages 0-2. +* Has next NTAG215-related configuration: + +``` +[=] 130/0x82 | 00 00 00 BD | 0 | ...� +[=] 131/0x83 | 04 00 00 10 | 0 | .... +[=] 132/0x84 | C0 05 00 00 | 0 | �... +[=] 133/0x85 | FF FF FF FF | 0 | .... // Password, will not be readable in normal conditions +``` + +### Identify + +^[Top](#top) + +``` +[usb] pm3 --> hf 14a info +... +[+] ATS: 85 00 00 A0 AA 00 0A 5A 00 00 00 00 00 00 00 00 [ D5 F9 ] +``` + +## Ultra (RU) + +^[Top](#top) + +Modification of [UL-2 (41 blocks)](#ul-2-41-blocks) for Vizit (RU) intercoms. +Suitable for tags with EV1 Version `0034210101000E03`. + +After communication to iKey LLC (importer of those tags to Russian market), new revisions, imported to Russia have closed config. + +### Characteristics + +^[Top](#top) + +* Configuration block value: `850000A000000A3C0034210101000E03`. +* EV1 Version: `0034210101000E03`. + +### Identify + +^[Top](#top) + +``` +[usb] pm3 --> hf 14a info +... +[+] ATS: 85 00 00 A0 00 00 0A 3C 00 04 03 01 01 00 0E 03 [ C8 1D ] +``` + +### Magic commands + +^[Top](#top) + +Use the script `hf_mfu_ultra.lua` to restore (write) dump to tag or clear previously written tag. + +Usage: +1. Restore dump to tag: + ``` + script run hf_mfu_ultra -f -k -r + ``` +2. Wipe tag (use it to prepare tag for restoring another dump): + ``` + script run hf_mfu_ultra -k -w + ``` +3. Show help: + ``` + script run hf_mfu_ultra -h + ``` + +Examples: +1. Restore dump to tag: + ``` + script run hf_mfu_ultra -f hf-mfu-3476FF1514D866-dump.bin -k ffffffff -r + ``` +2. Wipe tag: + ``` + script run hf_mfu_ultra -k 1d237f76 -w + ``` + +## UL-5 + +^[Top](#top) + +Variation of [Ultra](#ultra-ru) tag, which allows to change UID only once. + +After editing page 0 become locked and tag becomes the original Mifare Ultralight EV1 (except the tag version, which remains specific). + +Created as a response to Vizit (RU) filters that try to overwrite page 0 (as a detection for Ultra (RU) tags). + +**WARNING!** When using UL-5 to clone, write UID pages in inverse (from 2 to 0) and do NOT make mistakes! This tag does not allow reversing one-way actions (OTP page, lock bits). + +It was confirmed from importers to Russian and Ukrainian market (independently) that UL-5 is a variation of USCUID-UL. But so far it's unknown how to achieve that behaviors, because by default UL-5 has it's config locked. + +### Identify + +^[Top](#top) + +``` +hf mfu info +... +TAG IC Signature: 0000000000000000000000000000000000000000000000000000000000000000 +[=] --- Tag Version +[=] Raw bytes: 00 34 21 01 01 00 0E 03 +[=] Vendor ID: 34, Mikron JSC Russia +``` + +After personalization it is not possible to identify UL-5. + +The manufacturer confirmed unpersonalized tags could be identified by first 2 bytes of UID: + +* `AA 55...` + +### Magic commands + +^[Top](#top) + +Use the script `hf_mfu_ultra.lua` to restore (write) dump to tag. + +Usage: +1. Restore dump to tag: + ``` + script run hf_mfu_ultra -f -k -r + ``` +3. Show help: + ``` + script run hf_mfu_ultra -h + ``` + +Examples: +1. Restore dump to tag: + ``` + script run hf_mfu_ultra -f hf-mfu-3476FF1514D866-dump.bin -k ffffffff -r + ``` + +## UL, other chips + +** TODO ** + +* UL +* UL-X +* UL-Z + # DESFire ^[Top](#top) diff --git a/doc/md/Development/Makefile-vs-CMake.md b/doc/md/Development/Makefile-vs-CMake.md index 2d1094970..b561ef2d2 100644 --- a/doc/md/Development/Makefile-vs-CMake.md +++ b/doc/md/Development/Makefile-vs-CMake.md @@ -112,7 +112,7 @@ At the moment both are maintained because they don't perfectly overlap yet. | Feature | Makefile | Remarks | |-----|---|---| -| Platform choice | `PLATFORM=` | values: `PM3RDV4`, `PM3GENERIC`, `PM3ICOPYX` | +| Platform choice | `PLATFORM=` | values: `PM3RDV4`, `PM3GENERIC`, `PM3ICOPYX`, `PM3ULTIMATE` | | Platform size | `PLATFORM_SIZE=` | values: `256`, `512` | | Platform extras | `PLATFORM_EXTRAS=` | values: `BTADDON`, `FPC_USART_DEV` | | Skip LF/HF techs in the firmware | `SKIP_`*`=1` | see `common_arm/Makefile.hal` for a list | diff --git a/doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md b/doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md index cfc6cd01b..ab25bbcde 100644 --- a/doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md @@ -467,7 +467,7 @@ You must have Windows Terminal installed to use this script. 3. Make sure your Proxmark3 is plugged in, and it is detected in the Device Manager as a COM port. 4. Run **pm3_quick_startup_wsl2.bat** and accept the UAC prompt. The script auto detects and asks for admin privileges, so you don't have to right-click and select Run As Administrator. 5. It will open up 2 windows. The first one is Command Prompt where initializing commands will run, and you need to keep this window open. The second one is Windows Terminal, where your pm3 client will run. - +6. On some systems, you will occasionally see this error popping up: `usbipd: error: WSL kernel is not USBIP capable`. Use command `service udev restart` to suppress that error. ```batch @echo off @@ -514,6 +514,9 @@ REM -- Replace the following hardware IDs with your actual Proxmark3 ID. You can usbipd bind --hardware-id 9ac4:4b8f usbipd attach --auto-attach --hardware-id 9ac4:4b8f --wsl +REM -- Activate below line by removing the "REM --" prefix if you encounter this error: "usbipd: error: WSL kernel is not USBIP capable" +REM -- wsl -u root "modprobe vhci_hcd" + wsl -u root "service udev restart" wsl -u root "udevadm trigger --action=change" diff --git a/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md b/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md index 9102de117..f44fa6851 100644 --- a/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/macOS-Homebrew-Installation-Instructions.md @@ -175,7 +175,7 @@ These instructions will show how to setup the environment on OSX to the point wh 2. Install dependencies: ``` -brew install readline qt5 gd pkgconfig coreutils +brew install readline qt5 gd pkgconfig coreutils openssl brew install RfidResearchGroup/proxmark3/arm-none-eabi-gcc ``` 3. (optional) Install makefile dependencies: diff --git a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md index e79863faa..55ca51687 100644 --- a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md +++ b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md @@ -61,11 +61,12 @@ For an up-to-date exhaustive list of options, you can run `make PLATFORM=`. Here are the supported values you can assign to `PLATFORM` in `Makefile.platform`: -| PLATFORM | DESCRIPTION | -|-----------------|--------------------------| -| PM3RDV4 (def) | Proxmark3 RDV4 | -| PM3GENERIC | Proxmark3 generic target | -| PM3ICOPYX | iCopy-X with XC3S100E | +| PLATFORM | DESCRIPTION | +|-----------------|-------------------------------| +| PM3RDV4 (def) | Proxmark3 RDV4 | +| PM3GENERIC | Proxmark3 generic target | +| PM3ICOPYX | iCopy-X with XC3S100E | +| PM3ULTIMATE | Proxmark3 Ultimate with XC2S50 | By default `PLATFORM=PM3RDV4`. @@ -140,6 +141,7 @@ Here are the supported values you can assign to `STANDALONE` in `Makefile.platfo | HF_MFCSIM | Simulate Mifare Classic 1k card storing in flashmem - Ray Lee | HF_MSDSAL | EMV Read and emulation - Salvador Mendoza | HF_REBLAY | 14A relay over BT - Salvador Mendoza +| HF_ST25_TEAROFF | Store/restore ST25TB tags with tear-off for counters - SecLabz | HF_TCPRST | IKEA Rothult ST25TA, Standalone Master Key Dump/Emulation - Nick Draffen | HF_TMUDFORD | Read and emulate ISO15693 card UID - Tim Mudford | HF_UNISNIFF | Combined 14a/14b/15 sniffer with runtime selection & extra save options diff --git a/docker/archlinux/docker_conf.inc b/docker/archlinux/docker_conf.inc new file mode 100644 index 000000000..0f069bf63 --- /dev/null +++ b/docker/archlinux/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-arch:1.0 diff --git a/docker/debian-11-bullseye/README.md b/docker/debian-11-bullseye/README.md deleted file mode 100644 index 3a7775b7c..000000000 --- a/docker/debian-11-bullseye/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Notes on run_tests.sh script -This script runs a bunch of different builds with make and cmake together -with the different combos of RDV4, GENERIC, BTADDON combos. - -If all tests OK, the script will finish with PASS. - - -# Notes to run tests -The script is to be run in proxmark root folder inside the docker env. - -``` -docker/debian-11-bullseye/run_tests.sh; -``` - -Or if you want to run single test, - -``` -sudo apt update -make clean; make -j -tools/pm3_tests.sh --long -``` diff --git a/docker/debian-11-bullseye/run_tests.sh b/docker/debian-11-bullseye/run_tests.sh deleted file mode 100755 index 9c7128942..000000000 --- a/docker/debian-11-bullseye/run_tests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# Iceman 2022 -# -# This script is to be run from proxmark root folder inside the docker env -# docker/debian-11-bullseye/run_tests.sh; - -sudo apt update && sudo apt upgrade -y -tools/release_tests.sh diff --git a/docker/debian-12-bookworm-arm64/docker_conf.inc b/docker/debian-12-bookworm-arm64/docker_conf.inc new file mode 100644 index 000000000..753d89ff3 --- /dev/null +++ b/docker/debian-12-bookworm-arm64/docker_conf.inc @@ -0,0 +1,2 @@ +DOCKER_IMAGE=pm3-debian-bookworm-arm64:1.0 +DOCKER_PLATFORM="--platform linux/arm64" diff --git a/docker/debian-12-bookworm-armhf/docker_conf.inc b/docker/debian-12-bookworm-armhf/docker_conf.inc new file mode 100644 index 000000000..3c9160afc --- /dev/null +++ b/docker/debian-12-bookworm-armhf/docker_conf.inc @@ -0,0 +1,2 @@ +DOCKER_IMAGE=pm3-debian-bookworm-armhf:1.0 +DOCKER_PLATFORM="--platform linux/arm/v7" diff --git a/docker/debian-12-bookworm/docker_conf.inc b/docker/debian-12-bookworm/docker_conf.inc new file mode 100644 index 000000000..6d979b6cc --- /dev/null +++ b/docker/debian-12-bookworm/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-debian-bookworm:1.0 diff --git a/docker/debian-13-trixie/docker_conf.inc b/docker/debian-13-trixie/docker_conf.inc new file mode 100644 index 000000000..481a94e9f --- /dev/null +++ b/docker/debian-13-trixie/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-debian-trixie:1.0 diff --git a/docker/fedora-36/README.md b/docker/fedora-36/README.md deleted file mode 100644 index 6648fb28d..000000000 --- a/docker/fedora-36/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Notes on run_tests.sh script -This script runs a bunch of different builds with make and cmake together -with the different combos of RDV4, GENERIC, BTADDON combos. - -If all tests OK, the script will finish with PASS. - - -# Notes to run tests -The script is to be run in proxmark root folder inside the docker env. - -``` -docker/fedora-36/run_tests.sh; -``` - -Or if you want to run single test, - -``` -sudo yum -y update -make clean; make -j -tools/pm3_tests.sh --long -``` diff --git a/docker/fedora-37/run_tests.sh b/docker/fedora-37/run_tests.sh deleted file mode 100755 index 05cdb7da8..000000000 --- a/docker/fedora-37/run_tests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# Iceman 2022 -# -# This script is to be run from proxmark root folder inside the docker env -# docker/fedora-36/run_tests.sh; - -sudo yum -y update -tools/release_tests.sh diff --git a/docker/fedora-37/Dockerfile b/docker/fedora-41/Dockerfile similarity index 98% rename from docker/fedora-37/Dockerfile rename to docker/fedora-41/Dockerfile index bb3118fd2..959922831 100644 --- a/docker/fedora-37/Dockerfile +++ b/docker/fedora-41/Dockerfile @@ -1,4 +1,4 @@ -FROM fedora:37 +FROM fedora:41 ENV LANG=C # qt5-qtbase-devel skipped diff --git a/docker/fedora-37/README.md b/docker/fedora-41/README.md similarity index 93% rename from docker/fedora-37/README.md rename to docker/fedora-41/README.md index 79c88e040..2c1d28040 100644 --- a/docker/fedora-37/README.md +++ b/docker/fedora-41/README.md @@ -8,7 +8,7 @@ If all tests OK, the script will finish with PASS. The script is to be run in proxmark root folder inside the docker env. ``` -docker/fedora-37/run_tests.sh; +docker/fedora-41/run_tests.sh; ``` Or if you want to run single test, diff --git a/docker/fedora-41/docker_conf.inc b/docker/fedora-41/docker_conf.inc new file mode 100644 index 000000000..eb8968f1c --- /dev/null +++ b/docker/fedora-41/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-fedora-41:1.0 diff --git a/docker/fedora-36/run_tests.sh b/docker/fedora-41/run_tests.sh similarity index 82% rename from docker/fedora-36/run_tests.sh rename to docker/fedora-41/run_tests.sh index 05cdb7da8..b97b86afe 100755 --- a/docker/fedora-36/run_tests.sh +++ b/docker/fedora-41/run_tests.sh @@ -2,7 +2,7 @@ # Iceman 2022 # # This script is to be run from proxmark root folder inside the docker env -# docker/fedora-36/run_tests.sh; +# docker/fedora-41/run_tests.sh; sudo yum -y update tools/release_tests.sh diff --git a/docker/fedora-36/Dockerfile b/docker/fedora-42/Dockerfile similarity index 98% rename from docker/fedora-36/Dockerfile rename to docker/fedora-42/Dockerfile index 1041b799a..181ac02a3 100644 --- a/docker/fedora-36/Dockerfile +++ b/docker/fedora-42/Dockerfile @@ -1,4 +1,4 @@ -FROM fedora:36 +FROM fedora:42 ENV LANG=C # qt5-qtbase-devel skipped diff --git a/docker/fedora-42/README.md b/docker/fedora-42/README.md new file mode 100644 index 000000000..25e0264e3 --- /dev/null +++ b/docker/fedora-42/README.md @@ -0,0 +1,20 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + +# Notes to run tests +The script is to be run in proxmark root folder inside the docker env. + +``` +docker/fedora-42/run_tests.sh; +``` + +Or if you want to run single test, + +``` +sudo yum -y update +make clean; make -j +tools/pm3_tests.sh --long +``` diff --git a/docker/fedora-42/docker_conf.inc b/docker/fedora-42/docker_conf.inc new file mode 100644 index 000000000..002e2ec26 --- /dev/null +++ b/docker/fedora-42/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-fedora-42:1.0 diff --git a/docker/fedora-42/run_tests.sh b/docker/fedora-42/run_tests.sh new file mode 100755 index 000000000..f94642645 --- /dev/null +++ b/docker/fedora-42/run_tests.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/fedora-42/run_tests.sh; + +sudo yum -y update +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +tools/release_tests.sh +deactivate diff --git a/docker/fedora-43/Dockerfile b/docker/fedora-43/Dockerfile new file mode 100644 index 000000000..1c8affc51 --- /dev/null +++ b/docker/fedora-43/Dockerfile @@ -0,0 +1,27 @@ +FROM fedora:rawhide + +ENV LANG=C +# qt5-qtbase-devel skipped +RUN dnf install -y passwd sudo git make gcc gcc-c++ arm-none-eabi-gcc-cs arm-none-eabi-newlib readline-devel bzip2-devel lz4-devel bluez-libs-devel python3-devel openssl-devel gd-devel libatomic findutils + +RUN yum -y update +RUN yum -y install cmake python-pip +RUN python3 -m pip install ansicolors sslcrypto + +RUN yum -y install mesa-libOpenCL ocl-icd-devel + +# Create rrg user +RUN useradd -ms /bin/bash rrg +RUN passwd -d rrg +ARG UART_GID +# dialout group may already exist on another numeric ID than on host +RUN if [ -n "${UART_GID}" ]; then \ + groupadd -g ${UART_GID} mydialout || true; \ + usermod -aG ${UART_GID} rrg; \ + fi +RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers + +USER rrg +WORKDIR "/home/rrg" + +CMD ["/bin/bash"] diff --git a/docker/fedora-43/README.md b/docker/fedora-43/README.md new file mode 100644 index 000000000..b88fb1457 --- /dev/null +++ b/docker/fedora-43/README.md @@ -0,0 +1,20 @@ +# Notes on run_tests.sh script +This script runs a bunch of different builds with make and cmake together +with the different combos of RDV4, GENERIC, BTADDON combos. + +If all tests OK, the script will finish with PASS. + +# Notes to run tests +The script is to be run in proxmark root folder inside the docker env. + +``` +docker/fedora-43/run_tests.sh; +``` + +Or if you want to run single test, + +``` +sudo yum -y update +make clean; make -j +tools/pm3_tests.sh --long +``` diff --git a/docker/fedora-43/docker_conf.inc b/docker/fedora-43/docker_conf.inc new file mode 100644 index 000000000..9f1c4c08c --- /dev/null +++ b/docker/fedora-43/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-fedora-43:1.0 diff --git a/docker/fedora-43/run_tests.sh b/docker/fedora-43/run_tests.sh new file mode 100755 index 000000000..f73e5fb62 --- /dev/null +++ b/docker/fedora-43/run_tests.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/fedora-43/run_tests.sh; + +sudo yum -y update +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +tools/release_tests.sh +deactivate diff --git a/docker/homebrew/docker_conf.inc b/docker/homebrew/docker_conf.inc new file mode 100644 index 000000000..b53f34184 --- /dev/null +++ b/docker/homebrew/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-brew:1.0 diff --git a/docker/kali/docker_conf.inc b/docker/kali/docker_conf.inc new file mode 100644 index 000000000..62f57876b --- /dev/null +++ b/docker/kali/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-kali:1.0 diff --git a/docker/opensuse-leap/docker_conf.inc b/docker/opensuse-leap/docker_conf.inc new file mode 100644 index 000000000..ac8d995f5 --- /dev/null +++ b/docker/opensuse-leap/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-suse-leap:1.0 diff --git a/docker/opensuse-tumbleweed/Dockerfile b/docker/opensuse-tumbleweed/Dockerfile index a0faf50c2..7bc086b29 100644 --- a/docker/opensuse-tumbleweed/Dockerfile +++ b/docker/opensuse-tumbleweed/Dockerfile @@ -2,7 +2,7 @@ FROM opensuse/tumbleweed ENV LANG=C # libqt5-qtbase-devel skipped -RUN zypper --non-interactive install --no-recommends shadow sudo git patterns-devel-base-devel_basis gcc-c++ readline-devel libbz2-devel liblz4-devel bluez-devel python3-devel libopenssl-devel cross-arm-none-gcc12 cross-arm-none-newlib-devel gd-devel +RUN zypper --non-interactive install --no-recommends shadow sudo git patterns-devel-base-devel_basis gcc-c++ readline-devel libbz2-devel liblz4-devel bluez-devel python3-devel libopenssl-devel cross-arm-none-gcc14 cross-arm-none-newlib-devel gd-devel #RUN zypper addrepo https://download.opensuse.org/repositories/home:wkazubski/openSUSE_Tumbleweed/home:wkazubski.repo && \ # zypper --gpg-auto-import-keys refresh && \ diff --git a/docker/opensuse-tumbleweed/docker_conf.inc b/docker/opensuse-tumbleweed/docker_conf.inc new file mode 100644 index 000000000..e6cc65b22 --- /dev/null +++ b/docker/opensuse-tumbleweed/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-suse-tumbleweed:1.0 diff --git a/docker/parrot-core-latest/docker_conf.inc b/docker/parrot-core-latest/docker_conf.inc new file mode 100644 index 000000000..6410849ad --- /dev/null +++ b/docker/parrot-core-latest/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-parrotsec-core-latest:1.0 diff --git a/docker/ubuntu-18.04/Dockerfile b/docker/ubuntu-18.04/Dockerfile deleted file mode 100644 index f287c344e..000000000 --- a/docker/ubuntu-18.04/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -FROM ubuntu:18.04 - -ENV LANG=C -ENV DEBIAN_FRONTEND=noninteractive -# qtbase5-dev skipped -# python3 skipped, not yet searchable with pkg-config python3 -RUN apt-get update && \ - apt-get upgrade -y && \ - apt-get dist-upgrade -y && \ - apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libssl-dev libgd-dev sudo && \ - apt-get clean - -RUN apt-get install -y python3-minimal && \ - apt-get install -y python3-pip && \ - apt-get clean && \ - python3 -m pip install ansicolors sslcrypto - -RUN apt-get install -y opencl-dev && \ - apt-get clean - -# Create rrg user -RUN useradd -ms /bin/bash rrg -RUN passwd -d rrg -ARG UART_GID -# dialout group may already exist on another numeric ID than on host -RUN if [ -n "${UART_GID}" ]; then \ - groupadd -g ${UART_GID} mydialout || true; \ - usermod -aG ${UART_GID} rrg; \ - fi -RUN printf 'rrg ALL=(ALL) ALL\n' | tee -a /etc/sudoers - -USER rrg -WORKDIR "/home/rrg" - -CMD ["/bin/bash"] diff --git a/docker/ubuntu-18.04/run_tests.sh b/docker/ubuntu-18.04/run_tests.sh deleted file mode 100755 index 1efdbc060..000000000 --- a/docker/ubuntu-18.04/run_tests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# Iceman 2022 -# -# This script is to be run from proxmark root folder inside the docker env -# docker/ubuntu-18.04/run_tests.sh; - -sudo apt update && sudo apt upgrade -y -tools/release_tests.sh diff --git a/docker/ubuntu-20.04/run_tests.sh b/docker/ubuntu-20.04/run_tests.sh deleted file mode 100755 index aa98bc327..000000000 --- a/docker/ubuntu-20.04/run_tests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# Iceman 2022 -# -# This script is to be run from proxmark root folder inside the docker env -# docker/ubuntu-20.04/run_tests.sh; - -sudo apt update && sudo apt upgrade -y -tools/release_tests.sh diff --git a/docker/ubuntu-22.04/run_tests.sh b/docker/ubuntu-22.04/run_tests.sh deleted file mode 100755 index 4f8ce55ea..000000000 --- a/docker/ubuntu-22.04/run_tests.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# Iceman 2022 -# -# This script is to be run from proxmark root folder inside the docker env -# docker/ubuntu-22.04/run_tests.sh; - -sudo apt update && sudo apt upgrade -y -tools/release_tests.sh diff --git a/docker/debian-11-bullseye/Dockerfile b/docker/ubuntu-24.04/Dockerfile similarity index 75% rename from docker/debian-11-bullseye/Dockerfile rename to docker/ubuntu-24.04/Dockerfile index c32710dda..0932f8e4f 100644 --- a/docker/debian-11-bullseye/Dockerfile +++ b/docker/ubuntu-24.04/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:bullseye-slim +FROM ubuntu:24.04 ENV LANG=C ENV DEBIAN_FRONTEND=noninteractive @@ -9,15 +9,15 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev libssl-dev libgd-dev sudo && \ apt-get clean -RUN apt-get install -y python3-minimal && \ - apt-get install -y python3-pip && \ - apt-get clean && \ - python3 -m pip install ansicolors sslcrypto +RUN apt-get install -y --no-install-recommends python3-minimal python3-pip python3-venv && \ + apt-get clean RUN apt-get install -y opencl-dev && \ apt-get clean -# Create rrg user +# Create rrg user => there is already a ubuntu user = 1000 that we need to move away +RUN usermod -u 999 ubuntu +RUN groupmod -g 999 ubuntu RUN useradd -ms /bin/bash rrg RUN passwd -d rrg ARG UART_GID diff --git a/docker/ubuntu-22.04/README.md b/docker/ubuntu-24.04/README.md similarity index 92% rename from docker/ubuntu-22.04/README.md rename to docker/ubuntu-24.04/README.md index 93d4462ad..24a2605a1 100644 --- a/docker/ubuntu-22.04/README.md +++ b/docker/ubuntu-24.04/README.md @@ -9,7 +9,7 @@ If all tests OK, the script will finish with PASS. The script is to be run in proxmark root folder inside the docker env. ``` -docker/ubuntu-22.04/run_tests.sh; +docker/ubuntu-24.04/run_tests.sh; ``` Or if you want to run single test, diff --git a/docker/ubuntu-24.04/docker_conf.inc b/docker/ubuntu-24.04/docker_conf.inc new file mode 100644 index 000000000..38c37aff7 --- /dev/null +++ b/docker/ubuntu-24.04/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-ubuntu-24.04:1.0 diff --git a/docker/ubuntu-24.04/run_tests.sh b/docker/ubuntu-24.04/run_tests.sh new file mode 100755 index 000000000..f77f54baf --- /dev/null +++ b/docker/ubuntu-24.04/run_tests.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/ubuntu-24.04/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +tools/release_tests.sh +deactivate diff --git a/docker/ubuntu-20.04/Dockerfile b/docker/ubuntu-24.10/Dockerfile similarity index 75% rename from docker/ubuntu-20.04/Dockerfile rename to docker/ubuntu-24.10/Dockerfile index c02544234..89abd2516 100644 --- a/docker/ubuntu-20.04/Dockerfile +++ b/docker/ubuntu-24.10/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:20.04 +FROM ubuntu:24.10 ENV LANG=C ENV DEBIAN_FRONTEND=noninteractive @@ -9,15 +9,15 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev libssl-dev libgd-dev sudo && \ apt-get clean -RUN apt-get install -y python3-minimal && \ - apt-get install -y python3-pip && \ - apt-get clean && \ - python3 -m pip install ansicolors sslcrypto +RUN apt-get install -y --no-install-recommends python3-minimal python3-pip python3-venv && \ + apt-get clean RUN apt-get install -y opencl-dev && \ apt-get clean -# Create rrg user +# Create rrg user => there is already a ubuntu user = 1000 that we need to move away +RUN usermod -u 999 ubuntu +RUN groupmod -g 999 ubuntu RUN useradd -ms /bin/bash rrg RUN passwd -d rrg ARG UART_GID diff --git a/docker/ubuntu-20.04/README.md b/docker/ubuntu-24.10/README.md similarity index 92% rename from docker/ubuntu-20.04/README.md rename to docker/ubuntu-24.10/README.md index 4c432eff1..53a3a2107 100644 --- a/docker/ubuntu-20.04/README.md +++ b/docker/ubuntu-24.10/README.md @@ -9,11 +9,10 @@ If all tests OK, the script will finish with PASS. The script is to be run in proxmark root folder inside the docker env. ``` -docker/ubuntu-20.04/run_tests.sh; +docker/ubuntu-24.10/run_tests.sh; ``` Or if you want to run single test, - ``` sudo apt update make clean; make -j diff --git a/docker/ubuntu-24.10/docker_conf.inc b/docker/ubuntu-24.10/docker_conf.inc new file mode 100644 index 000000000..a6c548152 --- /dev/null +++ b/docker/ubuntu-24.10/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-ubuntu-24.10:1.0 diff --git a/docker/ubuntu-24.10/run_tests.sh b/docker/ubuntu-24.10/run_tests.sh new file mode 100755 index 000000000..3dbfac71d --- /dev/null +++ b/docker/ubuntu-24.10/run_tests.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/ubuntu-24.10/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +tools/release_tests.sh +deactivate diff --git a/docker/ubuntu-22.04/Dockerfile b/docker/ubuntu-25.04/Dockerfile similarity index 75% rename from docker/ubuntu-22.04/Dockerfile rename to docker/ubuntu-25.04/Dockerfile index d182d893e..245d7343a 100644 --- a/docker/ubuntu-22.04/Dockerfile +++ b/docker/ubuntu-25.04/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.04 +FROM ubuntu:25.04 ENV LANG=C ENV DEBIAN_FRONTEND=noninteractive @@ -9,15 +9,15 @@ RUN apt-get update && \ apt-get install -y --no-install-recommends git ca-certificates build-essential cmake pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev liblz4-dev libbluetooth-dev libpython3-dev libssl-dev libgd-dev sudo && \ apt-get clean -RUN apt-get install -y python3-minimal && \ - apt-get install -y python3-pip && \ - apt-get clean && \ - python3 -m pip install ansicolors sslcrypto +RUN apt-get install -y --no-install-recommends python3-minimal python3-pip python3-venv && \ + apt-get clean RUN apt-get install -y opencl-dev && \ apt-get clean -# Create rrg user +# Create rrg user => there is already a ubuntu user = 1000 that we need to move away +RUN usermod -u 999 ubuntu +RUN groupmod -g 999 ubuntu RUN useradd -ms /bin/bash rrg RUN passwd -d rrg ARG UART_GID diff --git a/docker/ubuntu-18.04/README.md b/docker/ubuntu-25.04/README.md similarity index 92% rename from docker/ubuntu-18.04/README.md rename to docker/ubuntu-25.04/README.md index 6cc3b9ef2..ae44d231a 100644 --- a/docker/ubuntu-18.04/README.md +++ b/docker/ubuntu-25.04/README.md @@ -9,11 +9,10 @@ If all tests OK, the script will finish with PASS. The script is to be run in proxmark root folder inside the docker env. ``` -docker/ubuntu-18.04/run_tests.sh; +docker/ubuntu-25.04/run_tests.sh; ``` Or if you want to run single test, - ``` sudo apt update make clean; make -j diff --git a/docker/ubuntu-25.04/docker_conf.inc b/docker/ubuntu-25.04/docker_conf.inc new file mode 100644 index 000000000..3b0c2e0e3 --- /dev/null +++ b/docker/ubuntu-25.04/docker_conf.inc @@ -0,0 +1 @@ +DOCKER_IMAGE=pm3-ubuntu-25.04:1.0 diff --git a/docker/ubuntu-25.04/run_tests.sh b/docker/ubuntu-25.04/run_tests.sh new file mode 100755 index 000000000..d65d78809 --- /dev/null +++ b/docker/ubuntu-25.04/run_tests.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +# Iceman 2022 +# +# This script is to be run from proxmark root folder inside the docker env +# docker/ubuntu-25.04/run_tests.sh; + +sudo apt update && sudo apt upgrade -y +python3 -m venv /tmp/venv +source /tmp/venv/bin/activate +python3 -m pip install --use-pep517 pyaes +python3 -m pip install ansicolors sslcrypto +tools/release_tests.sh +deactivate diff --git a/fpga/Makefile b/fpga/Makefile index 769185260..2411ab7e8 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -45,7 +45,7 @@ XST_OPTS_AREA += -fsm_style bram XST_OPTS_AREA += -fsm_encoding compact # par specific option (set determistic seed) -PAR_OPTIONS = -t 1 +PAR_OPTIONS = -t 2 # Types of selective module compilation: # WITH_LF Enables selection of LF modules (and disables all HF) @@ -74,7 +74,11 @@ TARGET3_OPTIONS = -define \{WITH_HF0 WITH_HF1 WITH_HF3 WITH_HF5 WITH_HF_15 WITH_ # RDV40/Generic - Enable all HF modules except ISO14443 TARGET4_OPTIONS = -define \{WITH_HF0 WITH_HF1 WITH_HF3 WITH_HF4 WITH_HF5\} # ICOPYX -TARGET5_OPTIONS = -define {PM3ICOPYX} -rtlview Yes +TARGET5_OPTIONS = $(TARGET1_OPTIONS) +TARGET6_OPTIONS = $(TARGET1_OPTIONS) +TARGET7_OPTIONS = $(TARGET1_OPTIONS) +TARGET8_OPTIONS = $(TARGET1_OPTIONS) +TARGET9_OPTIONS = $(TARGET1_OPTIONS) # Here we list the target names TARGET1_NAME = fpga_pm3_lf @@ -82,6 +86,10 @@ TARGET2_NAME = fpga_pm3_hf TARGET3_NAME = fpga_pm3_hf_15 TARGET4_NAME = fpga_pm3_felica TARGET5_NAME = fpga_icopyx_hf +TARGET6_NAME = fpga_pm3_ult_lf +TARGET7_NAME = fpga_pm3_ult_hf +TARGET8_NAME = fpga_pm3_ult_hf_15 +TARGET9_NAME = fpga_pm3_ult_felica # Targets can be compiled for different FPGA flavours TARGET1_FPGA = xc2s30-5-vq100 @@ -89,6 +97,10 @@ TARGET2_FPGA = $(TARGET1_FPGA) TARGET3_FPGA = $(TARGET1_FPGA) TARGET4_FPGA = $(TARGET1_FPGA) TARGET5_FPGA = xc3s100e-4-vq100 +TARGET6_FPGA = xc2s50-5-tq144 +TARGET7_FPGA = $(TARGET6_FPGA) +TARGET8_FPGA = $(TARGET6_FPGA) +TARGET9_FPGA = $(TARGET6_FPGA) # Assemble the final XST options for each target TARGET1_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET1_FPGA) -ofn $(TARGET1_NAME) $(TARGET1_OPTIONS) @@ -96,6 +108,10 @@ TARGET2_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET2_FPGA) -ofn $( TARGET3_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET3_FPGA) -ofn $(TARGET3_NAME) $(TARGET3_OPTIONS) TARGET4_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_AREA) -p $(TARGET4_FPGA) -ofn $(TARGET4_NAME) $(TARGET4_OPTIONS) TARGET5_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET5_FPGA) -ofn $(TARGET5_NAME) $(TARGET5_OPTIONS) +TARGET6_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET6_FPGA) -ofn $(TARGET6_NAME) $(TARGET6_OPTIONS) +TARGET7_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET7_FPGA) -ofn $(TARGET7_NAME) $(TARGET7_OPTIONS) +TARGET8_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET8_FPGA) -ofn $(TARGET8_NAME) $(TARGET8_OPTIONS) +TARGET9_XST_OPTS = $(XST_OPTS_BASE) $(XST_OPTS_SPEED) -p $(TARGET9_FPGA) -ofn $(TARGET9_NAME) $(TARGET9_OPTIONS) # these files are common for all targets TARGET_COMMON_FILES = define.v @@ -121,14 +137,18 @@ TARGET2_FILES = $(TARGET1_FILES) TARGET3_FILES = $(TARGET1_FILES) TARGET4_FILES = $(TARGET1_FILES) TARGET5_FILES = $(TARGET_COMMON_FILES) mux2_onein.v mux2_oneout.v fpga_icopyx_hf.v fpga_icopyx_lf.v fpga_icopyx_top.v +TARGET6_FILES = $(TARGET1_FILES) +TARGET7_FILES = $(TARGET1_FILES) +TARGET8_FILES = $(TARGET1_FILES) +TARGET9_FILES = $(TARGET1_FILES) # List of all valid target FPGA images to build -TARGETS = $(TARGET1_NAME) $(TARGET2_NAME) $(TARGET3_NAME) $(TARGET4_NAME) $(TARGET5_NAME) +TARGETS = $(TARGET1_NAME) $(TARGET2_NAME) $(TARGET3_NAME) $(TARGET4_NAME) $(TARGET5_NAME) $(TARGET6_NAME) $(TARGET7_NAME) $(TARGET8_NAME) $(TARGET9_NAME) # Verbosity type for ISE tools ise|xflow|silent VERBOSITY = -intstyle silent # Echo (Q=) or not echo (Q=@) build commands to the terminal -Q=@ +Q= # Pass the custom variables to the lower make rules $(TARGET1_NAME).bit: TARGET_FPGA = $(TARGET1_FPGA) @@ -151,6 +171,22 @@ $(TARGET5_NAME).bit: TARGET_FPGA = $(TARGET5_FPGA) $(TARGET5_NAME).bit: TARGET_FILES = $(TARGET5_FILES) $(TARGET5_NAME).bit: TARGET_XST_OPTS = $(TARGET5_XST_OPTS) +$(TARGET6_NAME).bit: TARGET_FPGA = $(TARGET6_FPGA) +$(TARGET6_NAME).bit: TARGET_FILES = $(TARGET6_FILES) +$(TARGET6_NAME).bit: TARGET_XST_OPTS = $(TARGET6_XST_OPTS) + +$(TARGET7_NAME).bit: TARGET_FPGA = $(TARGET7_FPGA) +$(TARGET7_NAME).bit: TARGET_FILES = $(TARGET7_FILES) +$(TARGET7_NAME).bit: TARGET_XST_OPTS = $(TARGET7_XST_OPTS) + +$(TARGET8_NAME).bit: TARGET_FPGA = $(TARGET8_FPGA) +$(TARGET8_NAME).bit: TARGET_FILES = $(TARGET8_FILES) +$(TARGET8_NAME).bit: TARGET_XST_OPTS = $(TARGET8_XST_OPTS) + +$(TARGET9_NAME).bit: TARGET_FPGA = $(TARGET9_FPGA) +$(TARGET9_NAME).bit: TARGET_FILES = $(TARGET9_FILES) +$(TARGET9_NAME).bit: TARGET_XST_OPTS = $(TARGET9_XST_OPTS) + $(TARGETS): $(Q)$(MKDIR) $(PREFIX)build_$@ $(Q)$(MAKE) -C $(PREFIX)build_$@ -f ../Makefile $(notdir $@).bit @@ -188,7 +224,7 @@ work: $(Q)$(RM) $@ $*.drc $*.rbt $(info [=] BITGEN $@) $(Q)$(XILINX_TOOLS_PREFIX)bitgen $(VERBOSITY) -w $* $@ - python3 ../strip_date_time_from_binary.py $@ || true + $(Q)python3 ../strip_date_time_from_binary.py $@ || true $(Q)$(CP) $@ .. # Build all targets diff --git a/fpga/fpga_icopyx_hf.bit b/fpga/fpga_icopyx_hf.bit index a7824dd74..6198fa35f 100644 Binary files a/fpga/fpga_icopyx_hf.bit and b/fpga/fpga_icopyx_hf.bit differ diff --git a/fpga/fpga_pm3_felica.bit b/fpga/fpga_pm3_felica.bit index 386638478..cbc31b0e3 100644 Binary files a/fpga/fpga_pm3_felica.bit and b/fpga/fpga_pm3_felica.bit differ diff --git a/fpga/fpga_pm3_hf.bit b/fpga/fpga_pm3_hf.bit index 1c159a612..9713f7406 100644 Binary files a/fpga/fpga_pm3_hf.bit and b/fpga/fpga_pm3_hf.bit differ diff --git a/fpga/fpga_pm3_hf_15.bit b/fpga/fpga_pm3_hf_15.bit index 055615e71..b532b622a 100644 Binary files a/fpga/fpga_pm3_hf_15.bit and b/fpga/fpga_pm3_hf_15.bit differ diff --git a/fpga/fpga_pm3_lf.bit b/fpga/fpga_pm3_lf.bit index 79485cac1..50c8c6c1f 100644 Binary files a/fpga/fpga_pm3_lf.bit and b/fpga/fpga_pm3_lf.bit differ diff --git a/fpga/fpga_pm3_top.v b/fpga/fpga_pm3_top.v index a36cd3e6d..a183d34d2 100644 --- a/fpga/fpga_pm3_top.v +++ b/fpga/fpga_pm3_top.v @@ -28,6 +28,8 @@ //`define PM3GENERIC // iCopy-X with XC3S100E //`define PM3ICOPYX +// Proxmark3 Ultimate with XC2S50 +//`define PM3ULTIMATE // Pass desired defines to compiler to enable required modules // WITH_LF enables Low Frequency mode when defined else HF is enabled diff --git a/fpga/fpga_pm3_ult_felica.bit b/fpga/fpga_pm3_ult_felica.bit new file mode 100644 index 000000000..31bff7c05 Binary files /dev/null and b/fpga/fpga_pm3_ult_felica.bit differ diff --git a/fpga/fpga_pm3_ult_hf.bit b/fpga/fpga_pm3_ult_hf.bit new file mode 100644 index 000000000..4effbe83a Binary files /dev/null and b/fpga/fpga_pm3_ult_hf.bit differ diff --git a/fpga/fpga_pm3_ult_hf_15.bit b/fpga/fpga_pm3_ult_hf_15.bit new file mode 100644 index 000000000..deff00d73 Binary files /dev/null and b/fpga/fpga_pm3_ult_hf_15.bit differ diff --git a/fpga/fpga_pm3_ult_lf.bit b/fpga/fpga_pm3_ult_lf.bit new file mode 100644 index 000000000..74fbb661b Binary files /dev/null and b/fpga/fpga_pm3_ult_lf.bit differ diff --git a/include/ansi.h b/include/ansi.h index 20815bc03..b791741f5 100644 --- a/include/ansi.h +++ b/include/ansi.h @@ -21,6 +21,12 @@ #define AEND "\x1b[0m" +#define _CLEAR_ "\x1b[2J" +#define _CLEAR_SCROLLBACK_ "\x1b[3J" +#define _TOP_ "\x1b[1;1f" + +#define _CLR_ "\x1b[0K" + #define _BLACK_(s) "\x1b[30m" s AEND #define _RED_(s) "\x1b[31m" s AEND #define _GREEN_(s) "\x1b[32m" s AEND @@ -57,10 +63,6 @@ #define _BACK_BRIGHT_CYAN_(s) "\x1b[46;1m" s AEND #define _BACK_BRIGHT_WHITE_(s) "\x1b[47;1m" s AEND -#define _CLEAR_ "\x1b[2J" -#define _CLEAR_SCROLLBACK_ "\x1b[3J" -#define _TOP_ "\x1b[1;1f" - #if defined(HAVE_READLINE) // https://wiki.hackzine.org/development/misc/readline-color-prompt.html // Applications may indicate that the prompt contains diff --git a/include/em4x70.h b/include/em4x70.h index 597dbfe26..23cb2d8ef 100644 --- a/include/em4x70.h +++ b/include/em4x70.h @@ -38,7 +38,7 @@ /// The only requirement is that this structure remain /// smaller than the NG buffer size (256 bytes). typedef struct { - bool parity; + bool _ignored__was_use_parity; // BUGBUG: Ignored, but kept for structure size / offset compatibility // Used for writing address uint8_t address; diff --git a/include/iclass_cmd.h b/include/iclass_cmd.h index e4761584f..8210490a8 100644 --- a/include/iclass_cmd.h +++ b/include/iclass_cmd.h @@ -48,7 +48,8 @@ #define ICLASS_SIM_MODE_FULL 3 #define ICLASS_SIM_MODE_READER_ATTACK_KEYROLL 4 #define ICLASS_SIM_MODE_EXIT_AFTER_MAC 5 // note: device internal only -#define ICLASS_SIM_MODE_CONFIG_CARD 6 +#define ICLASS_SIM_MODE_FULL_GLITCH 6 +#define ICLASS_SIM_MODE_FULL_GLITCH_KEY 7 // iCLASS auth request data structure @@ -87,6 +88,17 @@ typedef struct { uint8_t mac[4]; } PACKED iclass_writeblock_req_t; +// iCLASS tearoff block request data structure +typedef struct { + iclass_auth_req_t req; + uint8_t data[8]; + uint8_t mac[4]; + int tear_start; + int tear_end; + int increment; + int tear_loop; +} PACKED iclass_tearblock_req_t; + // iCLASS write block request data structure typedef struct { iclass_auth_req_t req; @@ -113,6 +125,8 @@ typedef struct { uint8_t nfa[8]; bool debug; bool test; + bool fast; + bool short_delay; } PACKED iclass_recover_req_t; typedef struct iclass_premac { diff --git a/include/iso15.h b/include/iso15.h index 0d19c1756..9c39db3a3 100644 --- a/include/iso15.h +++ b/include/iso15.h @@ -20,10 +20,14 @@ #define _ISO15_H_ #include "common.h" + +#define ISO15693_UID_LENGTH 8 +#define ISO15693_ATQB_LENGTH 7 + typedef struct { - uint8_t uid[8]; + uint8_t uid[ISO15693_UID_LENGTH]; uint8_t uidlen; - uint8_t atqb[7]; + uint8_t atqb[ISO15693_ATQB_LENGTH]; uint8_t chipid; uint8_t cid; } PACKED iso15_card_select_t; diff --git a/include/mifare.h b/include/mifare.h index 69665cdc8..4754a6f20 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -108,23 +108,6 @@ typedef enum ISO14A_COMMAND { ISO14A_CRYPTO1MODE = (1 << 14) } iso14a_command_t; -// Defines a frame that will be used in a polling sequence -// ECP Frames are up to (7 + 16) bytes long, 24 bytes should cover future and other cases -typedef struct { - uint8_t frame[24]; - uint8_t frame_length; - uint8_t last_byte_bits; - uint16_t extra_delay; -} PACKED iso14a_polling_frame_t; - -// Defines polling sequence configuration -// 6 would be enough for 4 magsafe, 1 wupa, 1 ecp, -typedef struct { - iso14a_polling_frame_t frames[6]; - uint8_t frame_count; - uint16_t extra_timeout; -} PACKED iso14a_polling_parameters_t; - typedef struct { uint8_t *response; uint8_t *modulation; diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index 89600e8e4..cbce45a24 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -131,6 +131,27 @@ typedef struct { bool verbose; } PACKED sample_config; + +// Defines a frame that will be used in a polling sequence +// Polling loop annotations are up to 20 bytes long, 24 bytes should cover future and other cases +typedef struct { + uint8_t frame[24]; + // negative values can be used to carry special info + int8_t frame_length; + uint8_t last_byte_bits; + uint16_t extra_delay; +} PACKED iso14a_polling_frame_t; + + +// Defines polling sequence configuration +typedef struct { + // 6 would be enough for 4 magsafe, 1 wupa, 1 pla, + iso14a_polling_frame_t frames[6]; + int8_t frame_count; + uint16_t extra_timeout; +} PACKED iso14a_polling_parameters_t; + + // A struct used to send hf14a-configs over USB typedef struct { int8_t forceanticol; // 0:auto 1:force executing anticol 2:force skipping anticol @@ -138,7 +159,9 @@ typedef struct { int8_t forcecl2; // 0:auto 1:force executing CL2 2:force skipping CL2 int8_t forcecl3; // 0:auto 1:force executing CL3 2:force skipping CL3 int8_t forcerats; // 0:auto 1:force executing RATS 2:force skipping RATS -} PACKED hf14a_config; + int8_t magsafe; // 0:disabled 1:enabled + iso14a_polling_frame_t polling_loop_annotation; // Polling loop annotation +} PACKED hf14a_config_t; // Tracelog Header struct typedef struct { @@ -326,6 +349,12 @@ typedef struct { uint8_t key[6]; } PACKED mfc_eload_t; +typedef struct { + bool use_flashmem; + uint16_t keycount; + uint8_t keys[]; +} PACKED mfulc_keys_t; + typedef struct { uint8_t status; uint8_t CSN[8]; @@ -645,6 +674,7 @@ typedef struct { #define CMD_HF_ICLASS_RESTORE 0x039B #define CMD_HF_ICLASS_CREDIT_EPURSE 0x039C #define CMD_HF_ICLASS_RECOVER 0x039D +#define CMD_HF_ICLASS_TEARBL 0x039E // For ISO1092 / FeliCa @@ -934,6 +964,8 @@ typedef struct { // Cryptographic error client/pm3: cryptographic operation failed #define PM3_ECRYPTO -29 +// File error client: error related to file does not exist in search paths +#define PM3_ENOFILE -30 // No data client/pm3: no data available, no host frame available (not really an error) #define PM3_ENODATA -98 // Quit program client: reserved, order to quit the program diff --git a/include/pmflash.h b/include/pmflash.h index 7820ad4e2..7e601c2bc 100644 --- a/include/pmflash.h +++ b/include/pmflash.h @@ -79,6 +79,13 @@ #define MF_KEYS_FILE "dict_mf.bin" #define MF_KEY_LENGTH 6 +// MIFARE Ultralight-C keys stored in spiffs +#define MFULC_KEYS_FILE "dict_mfulc.bin" +#define MFULC_KEY_LENGTH (16) + +// MIFARE Ultralight-AES keys stored in spiffs +#define MFULAES_KEYS_FILE "dict_mfulaes.bin" +#define MFULAES_KEY_LENGTH (16) // RDV40, validation structure to help identifying that client/firmware is talking with RDV40 typedef struct { uint8_t magic[4]; diff --git a/include/usart_defs.h b/include/usart_defs.h index 501f66464..84d6ff17c 100644 --- a/include/usart_defs.h +++ b/include/usart_defs.h @@ -16,8 +16,9 @@ #ifndef __USART_DEFS_H #define __USART_DEFS_H -//#define USART_BAUD_RATE 9600 +#ifndef USART_BAUD_RATE #define USART_BAUD_RATE 115200 +#endif // BT HC-06 physical layer runs at 128kbps // so it's possible to gain a little bit by using 230400 // with some risk to overflow its internal buffers: diff --git a/pm3 b/pm3 index 80aad83d8..5122fe536 100755 --- a/pm3 +++ b/pm3 @@ -136,11 +136,15 @@ function get_pm3_list_Windows { PM3LIST=() # Normal SERIAL PORTS (COM) - for DEV in $(wmic /locale:ms_409 path Win32_SerialPort Where "PNPDeviceID LIKE '%VID_9AC4&PID_4B8F%' Or PNPDeviceID LIKE '%VID_2D2D&PID_504D%'" Get DeviceID 2>/dev/null | awk -b '/^COM/{print $1}'); do - DEV=${DEV/ */} + for DEV in $($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object {\$_.PNPDeviceID -like '*VID_9AC4&PID_4B8F*' -or \$_.PNPDeviceID -like '*VID_2D2D&PID_504D*'} | Select -expandproperty DeviceID" 2>/dev/null); do + + _comport=$DEV + #prevent soft bricking when using pm3-flash-all on an outdated bootloader if [ $(basename -- "$0") = "pm3-flash-all" ]; then - line=$(wmic /locale:ms_409 path Win32_SerialPort Where "DeviceID='$DEV'" Get PNPDeviceID 2>/dev/null | awk -b '/^USB/{print $1}'); + + line=$($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object {\$_.DeviceID -eq '$_comport'} | Select -expandproperty PNPDeviceID" 2>/dev/null); + if [[ ! $line =~ ^"USB\VID_9AC4&PID_4B8F\ICEMAN" ]]; then echo -e "\033[0;31m[!] Using pm3-flash-all on an oudated bootloader, use pm3-flash-bootrom first!" exit 1 @@ -154,8 +158,8 @@ function get_pm3_list_Windows { #BT direct SERIAL PORTS (COM) if $FINDBTRFCOMM; then - for DEV in $(wmic /locale:ms_409 path Win32_PnPEntity Where "Caption LIKE '%Bluetooth%(COM%'" Get Name 2> /dev/null | awk -b 'match($0,/(COM[0-9]+)/,m){print m[1]}'); do - DEV=${DEV/ */} + + for DEV in $($PSHEXE -command "Get-CimInstance -ClassName Win32_PnPEntity | Where-Object Caption -like 'Standard Serial over Bluetooth link (COM*' | Select Name" 2>/dev/null); do PM3LIST+=("$DEV") if [ ${#PM3LIST[*]} -ge "$N" ]; then return @@ -165,8 +169,8 @@ function get_pm3_list_Windows { #white BT dongle SERIAL PORTS (COM) if $FINDBTDONGLE; then - for DEV in $(wmic /locale:ms_409 path Win32_SerialPort Where "PNPDeviceID LIKE '%VID_10C4&PID_EA60%'" Get DeviceID 2>/dev/null | awk -b '/^COM/{print $1}'); do - DEV=${DEV/ */} + + for DEV in $($PSHEXE -command "Get-CimInstance -ClassName Win32_serialport | Where-Object PNPDeviceID -like '*VID_10C4&PID_EA60*' | Select -expandproperty DeviceID" 2>/dev/null); do PM3LIST+=("$DEV") if [ ${#PM3LIST[*]} -ge "$N" ]; then return @@ -497,6 +501,21 @@ if [ "$HOSTOS" = "LINUX" ]; then elif [ "$HOSTOS" = "DARWIN" ]; then GETPM3LIST=get_pm3_list_macOS elif [[ "$HOSTOS" =~ MINGW(32|64)_NT* ]]; then + + # First try finding it using the PATH environment variable + PSHEXE=$(command -v powershell.exe 2>/dev/null) + + # If it fails (such as if WSLENV is not set), try using the default installation path + if [ -z "$PSHEXE" ]; then + PSHEXE=/cygdrive/c/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe + fi + + # Finally test if PowerShell is working + if ! "$PSHEXE" exit >/dev/null 2>&1; then + echo >&2 "[!!] Cannot run powershell.exe in your MINGW environment" + exit 1 + fi + GETPM3LIST=get_pm3_list_Windows else echo >&2 "[!!] Host OS not recognized, abort: $HOSTOS" diff --git a/tools/build_all_firmwares.sh b/tools/build_all_firmwares.sh index 2740d7150..3023fe741 100755 --- a/tools/build_all_firmwares.sh +++ b/tools/build_all_firmwares.sh @@ -32,11 +32,11 @@ mv bootrom/obj/bootrom.elf "$DEST/PM3BOOTROM.elf" # cf armsrc/Standalone/Makefile.hal STANDALONE_MODES=(LF_SKELETON) STANDALONE_MODES+=(LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RSWW LF_EM4100RWC LF_HIDBRUTE LF_HIDFCBRUTE LF_ICEHID LF_MULTIHID LF_NEDAP_SIM LF_NEXID LF_PROXBRUTE LF_PROX2BRUTE LF_SAMYRUN LF_THAREXDE) -STANDALONE_MODES+=(HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_15SIM HF_AVEFUL HF_BOG HF_CARDHOPPER HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_TCPRST HF_TMUDFORD HF_UNISNIFF HF_YOUNG) +STANDALONE_MODES+=(HF_14ASNIFF HF_14BSNIFF HF_15SNIFF HF_15SIM HF_AVEFUL HF_BOG HF_CARDHOPPER HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_LEGICSIM HF_MATTYRUN HF_MFCSIM HF_MSDSAL HF_REBLAY HF_ST25_TEAROFF HF_TCPRST HF_TMUDFORD HF_UNISNIFF HF_YOUNG) STANDALONE_MODES+=(DANKARMULTI) STANDALONE_MODES_REQ_BT=(HF_CARDHOPPER HF_REBLAY) STANDALONE_MODES_REQ_SMARTCARD=() -STANDALONE_MODES_REQ_FLASH=(LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_LEGICSIM HF_MFCSIM) +STANDALONE_MODES_REQ_FLASH=(LF_HIDFCBRUTE LF_ICEHID LF_NEXID LF_THAREXDE HF_BOG HF_COLIN HF_ICECLASS HF_LEGICSIM HF_MFCSIM HF_ST25_TEAROFF) # PM3GENERIC 256kb, no flash, need to skip some parts to reduce size diff --git a/tools/cryptorf/sma.cpp b/tools/cryptorf/sma.cpp index 4b7b2d15a..9a019114e 100644 --- a/tools/cryptorf/sma.cpp +++ b/tools/cryptorf/sma.cpp @@ -706,6 +706,10 @@ void combine_valid_left_right_states(vector *plcstates, vector *prcs printf("but only " _GREEN_("%zu")" were valid!\n", pgc_candidates->size()); } +#if defined(__cplusplus) +} +#endif + int main(int argc, const char *argv[]) { size_t pos; crypto_state_t ostate; @@ -855,7 +859,3 @@ int main(int argc, const char *argv[]) { } return 0; } - -#if defined(__cplusplus) -} -#endif diff --git a/tools/cryptorf/sma_multi.cpp b/tools/cryptorf/sma_multi.cpp index 9bd95e1fa..10a15e7b1 100644 --- a/tools/cryptorf/sma_multi.cpp +++ b/tools/cryptorf/sma_multi.cpp @@ -802,6 +802,10 @@ static void ice_compare( return; } +#if defined(__cplusplus) +} +#endif + int main(int argc, const char *argv[]) { size_t pos; crypto_state_t ostate; @@ -969,7 +973,3 @@ int main(int argc, const char *argv[]) { } return 0; } - -#if defined(__cplusplus) -} -#endif diff --git a/tools/fpga_compress/Makefile b/tools/fpga_compress/Makefile index 324860995..92af2739e 100644 --- a/tools/fpga_compress/Makefile +++ b/tools/fpga_compress/Makefile @@ -11,6 +11,8 @@ MYINCLUDES = -I../../common_fpga MYCFLAGS = -std=c99 -D_ISOC99_SOURCE ifeq ($(PLATFORM),PM3ICOPYX) MYDEFS = -DXC3 +else ifeq ($(PLATFORM),PM3ULTIMATE) + MYDEFS = -DXC2S50 else MYDEFS = endif diff --git a/tools/mfc/card_only/staticnested_0nt.c b/tools/mfc/card_only/staticnested_0nt.c index 44bfaaf11..088b4aadc 100644 --- a/tools/mfc/card_only/staticnested_0nt.c +++ b/tools/mfc/card_only/staticnested_0nt.c @@ -272,7 +272,9 @@ static uint64_t **unpredictable_nested(NtpKs1List *pNKL, uint32_t keyCounts[]) { pthread_cond_wait(&status_cond, &status_mutex); activeThreads = 0; for (int i = 0; i < NUM_THREADS; i++) { - if (thread_status[i]) activeThreads++; + if (thread_status[i]) { + activeThreads++; + } } } diff --git a/tools/mfc/card_only/staticnested_2x1nt_rf08s.c b/tools/mfc/card_only/staticnested_2x1nt_rf08s.c index 10f31875d..0a83c10fd 100644 --- a/tools/mfc/card_only/staticnested_2x1nt_rf08s.c +++ b/tools/mfc/card_only/staticnested_2x1nt_rf08s.c @@ -31,11 +31,23 @@ static void init_lfsr16_table(void) { } // static uint16_t next_lfsr16(uint16_t nonce) { -// return s_lfsr16[(i_lfsr16[nonce]+1) % 65535]; +// uint16_t i = i_lfsr16[nonce]; +// if (i == 0xffff) { +// i = 1; +// } else { +// i++; +// } +// return s_lfsr16[i]; // } static uint16_t prev_lfsr16(uint16_t nonce) { - return s_lfsr16[(i_lfsr16[nonce] - 1) % 65535]; + uint16_t i = i_lfsr16[nonce]; + if (i == 1) { + i = 0xffff; + } else { + i--; + } + return s_lfsr16[i]; } static uint16_t compute_seednt16_nt32(uint32_t nt32, uint64_t key) { diff --git a/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c b/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c index 66f0cecab..f7b74ce0e 100644 --- a/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c +++ b/tools/mfc/card_only/staticnested_2x1nt_rf08s_1key.c @@ -33,11 +33,23 @@ static void init_lfsr16_table(void) { } // static uint16_t next_lfsr16(uint16_t nonce) { -// return s_lfsr16[(i_lfsr16[nonce]+1) % 65535]; +// uint16_t i = i_lfsr16[nonce]; +// if (i == 0xffff) { +// i = 1; +// } else { +// i++; +// } +// return s_lfsr16[i]; // } static uint16_t prev_lfsr16(uint16_t nonce) { - return s_lfsr16[(i_lfsr16[nonce] - 1) % 65535]; + uint16_t i = i_lfsr16[nonce]; + if (i == 1) { + i = 0xffff; + } else { + i--; + } + return s_lfsr16[i]; } static uint16_t compute_seednt16_nt32(uint32_t nt32, uint64_t key) { diff --git a/tools/mfd_aes_brute/Makefile b/tools/mfd_aes_brute/Makefile index 6f8fa44a9..07330e077 100644 --- a/tools/mfd_aes_brute/Makefile +++ b/tools/mfd_aes_brute/Makefile @@ -34,8 +34,8 @@ endif # OS X needs linking to openssl ifeq ($(USE_BREW),1) - MYCFLAGS += -I$(BREW_PREFIX)/opt/openssl@3/include -I$(BREW_PREFIX)/opt/openssl@1.1/include - MYLDFLAGS += -L$(BREW_PREFIX)/opt/openssl@3/lib -L$(BREW_PREFIX)/opt/openssl@1.1/lib + MYCFLAGS += -I$(BREW_PREFIX)/opt/openssl@3/include -I$(BREW_PREFIX)/opt/openssl@3.5/include + MYLDFLAGS += -L$(BREW_PREFIX)/opt/openssl@3/lib -L$(BREW_PREFIX)/opt/openssl@3.5/lib endif ifeq ($(USE_MACPORTS),1) diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 96a6376f7..f8e2dba2b 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash +# This is used to make sure that the language for the functions is english instead of the system default language. +LANG=C + PM3PATH="$(dirname "$0")/.." cd "$PM3PATH" || exit 1 @@ -570,8 +573,8 @@ while true; do if ! CheckExecute slow "hf iclass loclass long test" "$CLIENTBIN -c 'hf iclass loclass --long'" "verified \( ok \)"; then break; fi if ! CheckExecute slow "emv long test" "$CLIENTBIN -c 'emv test -l'" "Tests \( ok"; then break; fi if ! CheckExecute "hf iclass lookup test" "$CLIENTBIN -c 'hf iclass lookup --csn 9655a400f8ff12e0 --epurse f0ffffffffffffff --macs 0000000089cb984b -f $DICPATH/iclass_default_keys.dic'" \ - "valid key AE A6 84 A6 DA B2 32 78"; then break; fi - if ! CheckExecute "hf iclass loclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "key diversification \( ok \)"; then break; fi + "valid key AEA684A6DAB23278"; then break; fi + if ! CheckExecute "hf iclass loclass test" "$CLIENTBIN -c 'hf iclass loclass --test'" "Key diversification \( ok \)"; then break; fi if ! CheckExecute "emv test" "$CLIENTBIN -c 'emv test'" "Tests \( ok"; then break; fi if ! CheckExecute "hf cipurse test" "$CLIENTBIN -c 'hf cipurse test'" "Tests \( ok"; then break; fi if ! CheckExecute "hf mfdes test" "$CLIENTBIN -c 'hf mfdes test'" "Tests \( ok"; then break; fi diff --git a/tools/twn/AppBlaster.exe b/tools/twn/AppBlaster.exe new file mode 100644 index 000000000..6fdfae2fc Binary files /dev/null and b/tools/twn/AppBlaster.exe differ diff --git a/tools/twn/decoder.bix b/tools/twn/decoder.bix new file mode 100644 index 000000000..daf694630 Binary files /dev/null and b/tools/twn/decoder.bix differ diff --git a/tools/twn/encoder.bix b/tools/twn/encoder.bix new file mode 100644 index 000000000..1f472028d Binary files /dev/null and b/tools/twn/encoder.bix differ diff --git a/tools/twn/pcsc.bix b/tools/twn/pcsc.bix new file mode 100644 index 000000000..304500be1 Binary files /dev/null and b/tools/twn/pcsc.bix differ