diff --git a/.github/ISSUE_TEMPLATE/checklist-for-release.md b/.github/ISSUE_TEMPLATE/checklist-for-release.md index 27fde4573..eaf469cc7 100644 --- a/.github/ISSUE_TEMPLATE/checklist-for-release.md +++ b/.github/ISSUE_TEMPLATE/checklist-for-release.md @@ -36,7 +36,8 @@ sudo make install; pushd /tmp; proxmark3 -c 'data load -f lf_EM4x05.pm3;lf searc - [ ] RPI Zero - [ ] WSL -- [ ] PSv3.3 +- [ ] PSv3.9 +- [ ] Archlinux - [ ] Kali - [ ] Debian - [ ] Ubuntu20 @@ -46,3 +47,12 @@ sudo make install; pushd /tmp; proxmark3 -c 'data load -f lf_EM4x05.pm3;lf searc - [ ] OSX - [ ] Android - [ ] Termux + +# creating release +`make release RELEASE_NAME="ice awesome"` +last line of output, gives you next command to run +Sample: `git push && git push origin v4.15000` + + +Go to Github releases, create release based on the new created tag and publish +update homebrew repo, file `proxmark3.rb` with a SHA256 sum of the file `v4.15000.tar.gz` diff --git a/CHANGELOG.md b/CHANGELOG.md index 767c39521..5b961f15b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,11 +3,39 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] - - Added '-4041x' option to lf indala clone for the Indala 4041X format (@chunkystew) - - Fixed `hf fido` commands now works correctly (@merlokk) + - Added `-4041x` option to lf indala clone for the Indala 4041X format (@chunkystew) + - Added a new client preference, delay of execution, a delay in ms before a cmd is sent. Good for implants (@iceman1001) + - Fix `lf t55xx brute` - now correctly prints last key if it was correct (@scott4290) + - Added support python scripts (@salmg) + - Add new standalone mode `hf_reblay` - relay 14a over bt (@salmg) + - Added one new key from gun cloner found in wild (@scott4290) + - Fix `hf_msdsal` standalone in simulation flow (@salmg) + - Added a picture viewer in QT. To be used with `hf emrtd info` (@iceman1001) + - Fix - move des functions to libcrypto (@merlokk) + - Added `CLIGetOptionList` to cliparser that makes it easier to implement text options in the cli (@merlokk) + - Added experimental support for macOS users utilizing MacPorts instead of Homebrew (@linuxgemini) + - Added `pm3_online_check.py` - a script to verify and initialize a Proxmark3 RDV4 device (@iceman1001) + +## [midsummer.4.13441][2021-06-25] + - Fix `hf iclass` - a crash when AA1 limit was larger than AA2 (@pcunning) + - Added bruteforce function for the magic byte in `cmdlfnexwatch.c` and ability to clone with psk2 modulation (@Guilhem7, @MaximeBosca) + - Changed `hw setmux` - improve user feedback for special case (@iceman1001) + - Changed 'filename' - unified file name param across client (@iceman1001) + - Fix `lf em 4x05 brute/chk` - fixed input params crash (@iceman1001) + - Fix `lf hitag reader --23` - now accepts 6bytes key (@iceman1001) + - Fix coverity scans findigs (@merlokk, @iceman1001) + - Fix `hf iclass config` - now fallback to default config card configuration (@iceman1001) + - Changed `nfc parser` - now also identify xvcard types (@iceman1001) + - Added `hf mf gview` - view contents of a magic Gen3 GTU (@iceman1001) + - Added Standalone mode for nexwatch ID credentials (@Guilhem7, @MaximeBosca) + - Fix `lf em 4x50/4x70 *` reverted a missunderstanding in byte order macros (@iceman1001) + - Added more keys (@equipter) + - Changed `hf nfc ndefread` - ndef parser now handles more types (@iceman1001) + - Fix `hf desfire` changekey, GetUID, 3DES sesson key tweak. (@mwalker33) + - Fix `hf fido` commands now works correctly (@merlokk) - Moved / renamed `client/resource/fido2_defparams.json` -> `client/resource/hf_fido2_defparams.json` (@merlokk) - Added `hf cipurse` commands to work with cipurse transport cards (@merlokk) - - Added '--gap' option to lf em 410x sim for more control over sim data (@mwalker) + - Added `--gap` option to lf em 410x sim for more control over sim data (@mwalker) - Changed `hf fido` - refactored load/save json objects (@iceman1001) - Moved / renamed `fido2.json` -> `client/resource/fido2_defparams.json` (@iceman1001) - Added openocd shikra support based on @ninjastyle82 patch to deprecated iceman fork (@iceman1001) @@ -17,8 +45,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `data asn1` - a command to decode ASN1 byte arrays (@iceman1001) - Added `hf 15 disableprivacy` - from @g3gg0 fork *WIP* (@iceman1001) - Added `lf_ident_json.lua` - script to identify t55xx json dump files (@iceman1001) - - Fixed `hf iclass chk` - multithread concurrency issues solved (@iceman1001) - - Fixed hf_iceclass standalone - correct null terminator filename (@metalauricle) + - Fix `hf iclass chk` - multithread concurrency issues solved (@iceman1001) + - Fix hf_iceclass standalone - correct null terminator filename (@metalauricle) - Changed `trace list -t mfdes - added annotations for EV2, EV3 (@iceman1001)` - Changed `hf iclass lookup` - fixed swapped args (@iceman1001) - Changed `hf iclass decrypt` - added the possibility to decode the data as block6 if you have a cardhelper (@iceman1001) diff --git a/Makefile.defs b/Makefile.defs index 5a6993228..493c50ccd 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -59,7 +59,10 @@ else endif ifeq ($(USE_BREW),1) - BREW_PREFIX = $(shell brew --prefix) + BREW_PREFIX = $(shell brew --prefix 2>/dev/null) + ifeq ($(strip $(BREW_PREFIX)),) + MACPORTS_PREFIX ?= /opt/local + endif endif ifeq ($(DEBUG),1) diff --git a/README.md b/README.md index 4ef8f7de0..5e070c52a 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ ||[Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md)|[More cheat sheets](https://github.com/RfidResearchGroup/proxmark3/wiki/More-cheat-sheets)| ||**[Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md)**|[Complete client command set](/doc/commands.md)| ||**[JTAG](/doc/jtag_notes.md)**|[T5577 Introduction Guide](/doc/T5577_Guide.md)| +||**[MacPorts (Mac OS X, experimental)](/doc/md/Installation_Instructions/Mac-OS-X-MacPorts-Installation-Instructions.md)** | ## Notes / helpful documents @@ -59,12 +60,13 @@ We define generic Proxmark3 platforms as following devices. - **Note**: unknown pin assignments. - ⚠ Ryscorp Proxmark3 Pro - **Note**: device has different fpga and unknown pin assignments. + - ⚠ i-copy + - **Note**: reversed hw, experimental support maintained in [icopyx-community pm3 repo](https://github.com/iCopy-X-Community/icopyx-community-pm3) **Unknown support status** - ⚠ VX - **Note**: unknown device hw - - ⚠ i-copy - - **Note**: unknown device hw + **256kb flash memory size of generic Proxmark3 platforms** @@ -131,11 +133,11 @@ The [public roadmap](https://github.com/RfidResearchGroup/proxmark3/wiki/Public- ## Supported operative systems This repo compiles nicely on - WSL1 on Windows 10 - - Proxspace v3.9 [release v3.9](https://github.com/Gator96100/ProxSpace/releases) + - Proxspace enviroment [release v3.9](https://github.com/Gator96100/ProxSpace/releases) - Windows/MinGW environment - Ubuntu, ParrotOS, Gentoo, Pentoo, Kali, NetHunter, Arch Linux, Fedora, Debian, Raspbian - Android / Termux - - Mac OS X / Homebrew / Apple Silicon M1 + - Mac OS X / Homebrew (or MacPorts, experimental) / Apple Silicon M1 - Docker container - [ RRG / Iceman repo based ubuntu 18.04 container ](https://hub.docker.com/r/secopsconsult/proxmark3) - [ Iceman fork based container v1.7 ](https://hub.docker.com/r/iceman1001/proxmark3/) @@ -155,7 +157,7 @@ We don't maintain any precompiled binaries in this repo. There is community effo ## Official channels Where do you find the community? - [RFID Hacking community discord server](https://discord.gg/QfPvGFRQxH) - - [Proxmark3 IRC channel](http://webchat.freenode.net/?channels=#proxmark3) + - [Proxmark3 IRC channel](https://web.libera.chat/?channels=#proxmark3) - [Proxmark3 sub reddit](https://www.reddit.com/r/proxmark3/) - [Proxmark3 forum](http://www.proxmark.org/forum/index.php) diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index b413b1ac6..498300ac7 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -29,6 +29,9 @@ define KNOWN_STANDALONE_DEFINITIONS | LF_ICEHID | LF HID collector to flashmem | | (RDV4 only) | | +----------------------------------------------------------+ +| LF_NEXID | LF Nexwatch collector to flashmem | +| (RDV4 only) | | ++----------------------------------------------------------+ | LF_PROXBRUTE | HID ProxII bruteforce | | | - Brad Antoniewicz | +----------------------------------------------------------+ @@ -65,6 +68,9 @@ define KNOWN_STANDALONE_DEFINITIONS | HF_MSDSAL | Read and emulate MSD Visa cards | | (default) | - Salvador Mendoza | +----------------------------------------------------------+ +| HF_REBLAY | 14A Relay over BT | +| (RDV4 only) | - Salvador Mendoza | ++----------------------------------------------------------+ | HF_TCPRST | IKEA Rothult read/sim/dump/emul | | | - Nick Draffen | +----------------------------------------------------------+ @@ -76,10 +82,11 @@ define KNOWN_STANDALONE_DEFINITIONS +----------------------------------------------------------+ endef -STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN LF_THAREXDE -STANDALONE_MODES += HF_14ASNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_TCPRST HF_TMUDFORD HF_YOUNG +STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN LF_THAREXDE LF_NEXID +STANDALONE_MODES += HF_14ASNIFF HF_AVEFUL HF_BOG HF_COLIN HF_CRAFTBYTE HF_ICECLASS HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_TCPRST HF_TMUDFORD HF_YOUNG HF_REBLAY +STANDALONE_MODES_REQ_BT := HF_REBLAY STANDALONE_MODES_REQ_SMARTCARD := -STANDALONE_MODES_REQ_FLASH := LF_ICEHID LF_THAREXDE HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS +STANDALONE_MODES_REQ_FLASH := LF_ICEHID LF_NEXID LF_THAREXDE HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE) ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),) @@ -88,6 +95,9 @@ ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_FLASH)),) STANDALONE_REQ_DEFS += -DWITH_FLASH endif + ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_BT)),) + STANDALONE_REQ_DEFS += -DWITH_FPC_USART_HOST + endif else ifneq ($(STANDALONE),) $(error Invalid STANDALONE: $(STANDALONE). $(KNOWN_DEFINITIONS)) endif diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index c4590866f..510c56a48 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -49,6 +49,10 @@ endif ifneq (,$(findstring WITH_STANDALONE_LF_ICEHID,$(APP_CFLAGS))) SRC_STANDALONE = lf_icehid.c endif +# WITH_STANDALONE_LF_NEXID +ifneq (,$(findstring WITH_STANDALONE_LF_NEXID,$(APP_CFLAGS))) + SRC_STANDALONE = lf_nexid.c +endif # WITH_STANDALONE_LF_EM4100EMUL ifneq (,$(findstring WITH_STANDALONE_LF_EM4100EMUL,$(APP_CFLAGS))) SRC_STANDALONE = lf_em4100emul.c @@ -85,3 +89,7 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_TMUDFORD,$(APP_CFLAGS))) SRC_STANDALONE = hf_tmudford.c endif + # WITH_STANDALONE_HF_REBLAY +ifneq (,$(findstring WITH_STANDALONE_HF_REBLAY,$(APP_CFLAGS))) + SRC_STANDALONE = hf_reblay.c +endif \ No newline at end of file diff --git a/armsrc/Standalone/hf_msdsal.c b/armsrc/Standalone/hf_msdsal.c index b5585c188..2b836c8da 100644 --- a/armsrc/Standalone/hf_msdsal.c +++ b/armsrc/Standalone/hf_msdsal.c @@ -52,36 +52,40 @@ void ModInfo(void) { * technologies. Be brave enough to share your knowledge & inspire others. Salvador Mendoza. */ -static uint8_t ppdol [255] = {0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00}; // Default GET PROCESSING +// Default GET PROCESSING +static uint8_t ppdol [255] = {0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00}; + +// Generate GET PROCESSING +static uint8_t treatPDOL(uint8_t *apdu) { -static uint8_t treatPDOL(uint8_t *apdu) { //Generate GET PROCESSING uint8_t plen = 7; - //PDOL Format: 80 A8 00 00 + (PDOL Length+2) + 83 + PDOL Length + PDOL + 00 - for (uint8_t i = 1; i <= apdu[0]; i++) { //Magic stuff, the generation order is important - if (apdu[i] == 0x9F && apdu[i + 1] == 0x66) { //Terminal Transaction Qualifiers + + // PDOL Format: 80 A8 00 00 + (PDOL Length+2) + 83 + PDOL Length + PDOL + 00 + for (uint8_t i = 1; i <= apdu[0]; i++) { // Magic stuff, the generation order is important + if (apdu[i] == 0x9F && apdu[i + 1] == 0x66) { // Terminal Transaction Qualifiers ppdol[plen] = 0xF6; ppdol[plen + 1] = 0x20; ppdol[plen + 2] = 0xC0; ppdol[plen + 3] = 0x00; plen += 4; i += 2; - } else if (apdu[i] == 0x9F && apdu[i + 1] == 0x1A) { //Terminal Country Code + } else if (apdu[i] == 0x9F && apdu[i + 1] == 0x1A) { // Terminal Country Code ppdol[plen] = 0x9F; ppdol[plen + 1] = 0x1A; plen += 2; i += 2; - } else if (apdu[i] == 0x5F && apdu[i + 1] == 0x2A) { //Transaction Currency Code + } else if (apdu[i] == 0x5F && apdu[i + 1] == 0x2A) { // Transaction Currency Code ppdol[plen] = 0x5F; ppdol[plen + 1] = 0x2A; plen += 2; i += 2; - } else if (apdu[i] == 0x9A) { //Transaction Date + } else if (apdu[i] == 0x9A) { // Transaction Date ppdol[plen] = 0x9A; ppdol[plen + 1] = 0x9A; ppdol[plen + 2] = 0x9A; plen += 3; i += 1; - } else if (apdu[i] == 0x95) { //Terminal Verification Results + } else if (apdu[i] == 0x95) { // Terminal Verification Results ppdol[plen] = 0x95; ppdol[plen + 1] = 0x95; ppdol[plen + 2] = 0x95; @@ -89,18 +93,18 @@ static uint8_t treatPDOL(uint8_t *apdu) { //Generate GET PROCES ppdol[plen + 4] = 0x95; plen += 5; i += 1; - } else if (apdu[i] == 0x9C) { //Transaction Type + } else if (apdu[i] == 0x9C) { // Transaction Type ppdol[plen] = 0x9C; plen += 1; i += 1; - } else if (apdu[i] == 0x9F && apdu[i + 1] == 0x37) { //Unpredictable Number + } else if (apdu[i] == 0x9F && apdu[i + 1] == 0x37) { // Unpredictable Number ppdol[plen] = 0x9F; ppdol[plen + 1] = 0x37; ppdol[plen + 2] = 0x9F; ppdol[plen + 3] = 0x37; plen += 4; i += 2; - } else { //To the others, add "0" to complete the format depending on its range + } else { // To the others, add "0" to complete the format depending on its range uint8_t u = apdu[i + 2]; while (u > 0) { ppdol[plen] = 0; @@ -124,16 +128,25 @@ void RunMod(void) { //For reading process iso14a_card_select_t card_a_info; + uint8_t apdubuffer[MAX_FRAME_SIZE] = { 0x00 }; //Specific for Visa cards: select ppse, select Visa AID, GET PROCESSING, SFI - uint8_t ppse[20] = {0x00, 0xA4, 0x04, 0x00, 0x0e, 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0x00}; - uint8_t visa[13] = {0x00, 0xA4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x00}; + uint8_t ppse[20] = { + 0x00, 0xA4, 0x04, 0x00, 0x0e, 0x32, 0x50, 0x41, + 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, + 0x46, 0x30, 0x31, 0x00 + }; + uint8_t visa[13] = { + 0x00, 0xA4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, + 0x00, 0x03, 0x10, 0x10, 0x00 + }; + uint8_t processing [8] = {0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00}; uint8_t sfi[5] = {0x00, 0xb2, 0x01, 0x0c, 0x00}; uint8_t *apdus[4] = {ppse, visa, processing, sfi}; - uint8_t apdusLen [4] = { sizeof(ppse), sizeof(visa), sizeof(processing), sizeof(sfi)}; + uint8_t apduslen[4] = { sizeof(ppse), sizeof(visa), sizeof(processing), sizeof(sfi)}; uint8_t pdol[50], plen = 8; @@ -153,20 +166,15 @@ void RunMod(void) { char token[19] = {0x00}; bool chktoken = false; -//For emulation steps -#define ATQA 0 -#define UIDC1 1 -#define SAKC1 3 -#define RATS 5 -#define SIGNATURE 7 - -// Allocate 512 bytes for the dynamic modulation, created when the reader queries for it -// Such a response is less time critical, so we can prepare them on the fly + // Allocate 512 bytes for the dynamic modulation, created when the reader queries for it + // Such a response is less time critical, so we can prepare them on the fly #define DYNAMIC_RESPONSE_BUFFER_SIZE 64 #define DYNAMIC_MODULATION_BUFFER_SIZE 512 - uint8_t flags = FLAG_4B_UID_IN_DATA; //UID 4 bytes(could be 7 bytes if needed it) - uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; // in case there is a read command received we shouldn't break + // UID 4 bytes(could be 7 bytes if needed it) + uint8_t flags = FLAG_4B_UID_IN_DATA; + // in case there is a read command received we shouldn't break + uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; uint8_t visauid[7] = {0x01, 0x02, 0x03, 0x04}; memcpy(data, visauid, 4); @@ -203,7 +211,7 @@ void RunMod(void) { uint8_t state = STATE_READ; - //Checking if the user wants to go directly to emulation mode using a hardcoded track 2 + // Checking if the user wants to go directly to emulation mode using a hardcoded track 2 if (chktoken == true && token[0] != 0x00) { state = STATE_EMU; DbpString(_YELLOW_("[ ") "Initialized emulation mode" _YELLOW_(" ]")); @@ -222,11 +230,14 @@ void RunMod(void) { // Was our button held down or pressed? int button_pressed = BUTTON_HELD(1000); - if (button_pressed == BUTTON_HOLD) //Holding down the button + + if (button_pressed == BUTTON_HOLD) break; - else if (button_pressed == BUTTON_SINGLE_CLICK) { //Pressing one time change between reading & emulation + else if (button_pressed == BUTTON_SINGLE_CLICK) { + // pressing one time change between reading & emulation if (state == STATE_READ) { - if (chktoken == true && token[0] != 0x00) { //Only change to emulation if it saved a track 2 in memory + if (chktoken == true && token[0] != 0x00) { + // only change to emulation if it saved a track 2 in memory state = STATE_EMU; DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]")); } else @@ -254,28 +265,33 @@ void RunMod(void) { chktoken = false; LED_C_OFF(); LED_B_ON(); - uint8_t apdulen = iso14_apdu(apdus[i], (uint16_t) apdusLen[i], false, apdubuffer, NULL); + uint8_t apdulen = iso14_apdu(apdus[i], (uint16_t) apduslen[i], false, apdubuffer, NULL); if (apdulen > 0) { DbpString(_YELLOW_("[ ") "Proxmark command" _YELLOW_(" ]")); - Dbhexdump(apdusLen[i], apdus[i], false); + Dbhexdump(apduslen[i], apdus[i], false); DbpString(_GREEN_("[ ") "Card answer" _GREEN_(" ]")); Dbhexdump(apdulen - 2, apdubuffer, false); DbpString("----"); for (uint8_t u = 0; u < apdulen; u++) { if (i == 1) { - if (apdubuffer[u] == 0x9F && apdubuffer[u + 1] == 0x38) { //Check for PDOL + + // check for PDOL + if (apdubuffer[u] == 0x9F && apdubuffer[u + 1] == 0x38) { for (uint8_t e = 0; e <= apdubuffer[u + 2]; e++) pdol[e] = apdubuffer[u + e + 2]; - plen = treatPDOL(pdol); //Generate a challenge + // generate a challenge + plen = treatPDOL(pdol); apdus[2] = ppdol; - apdusLen[2] = plen; + apduslen[2] = plen; existpdol = true; } } else if (i == 3) { - if (apdubuffer[u] == 0x57 && apdubuffer[u + 1] == 0x13 && !chktoken) { //Find track 2 + + // find track 2 + if (apdubuffer[u] == 0x57 && apdubuffer[u + 1] == 0x13 && !chktoken) { chktoken = true; memcpy(&token, &apdubuffer[u + 2], 19); break; @@ -307,6 +323,7 @@ void RunMod(void) { } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); + } else if (state == STATE_EMU) { LED_A_OFF(); LED_C_ON(); @@ -328,15 +345,18 @@ void RunMod(void) { // We need to listen to the high-frequency, peak-detected path. iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); - int len = 0; // command length - int retval = PM3_SUCCESS; // to check emulation status + // command length + int len = 0; + // to check emulation status + int retval = PM3_SUCCESS; + bool odd_reply = true; clear_trace(); set_tracing(true); for (;;) { LED_B_OFF(); - // Clean receive command buffer + // clean receive command buffer if (!GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len)) { DbpString(_YELLOW_("!!") "Emulator stopped"); retval = PM3_EOPABORTED; @@ -348,50 +368,84 @@ void RunMod(void) { // dynamic_response_info will be in charge of responses dynamic_response_info.response_n = 0; - // Checking the commands order is important and elemental - if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { // Received a REQUEST - DbpString(_YELLOW_("+") "REQUEST Received"); - p_response = &responses[ATQA]; - } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { // Received a HALT + // received a REQUEST + if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { + odd_reply = !odd_reply; + if (odd_reply) { + p_response = &responses[RESP_INDEX_ATQA]; + } + + // received a HALT + } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { DbpString(_YELLOW_("+") "Received a HALT"); p_response = NULL; - } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { // Received a WAKEUP //Este!! + + // received a WAKEUP + } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { DbpString(_YELLOW_("+") "WAKEUP Received"); - p_response = &responses[ATQA]; prevCmd = 0; - } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { // Received request for UID (cascade 1) + p_response = &responses[RESP_INDEX_ATQA]; + + // received request for UID (cascade 1) + } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { DbpString(_YELLOW_("+") "Request for UID C1"); - p_response = &responses[UIDC1]; - } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1) + p_response = &responses[RESP_INDEX_UIDC1]; + + // received a SELECT (cascade 1) + } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { DbpString(_YELLOW_("+") "Request for SELECT S1"); - p_response = &responses[SAKC1]; - } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request + p_response = &responses[RESP_INDEX_SAKC1]; + + // received a RATS request + } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { DbpString(_YELLOW_("+") "Request for RATS"); - p_response = &responses[RATS]; + prevCmd = 0; + p_response = &responses[RESP_INDEX_RATS]; + } else { DbpString(_YELLOW_("[ ") "Card reader command" _YELLOW_(" ]")); Dbhexdump(len, receivedCmd, false); - if (receivedCmd[0] == 0x02 || receivedCmd[0] == 0x03) { //Emulate a Visa MSD(Magnetic stripe data) card + // emulate a Visa MSD(Magnetic stripe data) card + if (receivedCmd[0] == 0x02 || receivedCmd[0] == 0x03) { dynamic_response_info.response[0] = receivedCmd[0]; - //Depending on card reader commands, the Proxmark will answer to fool the reader - if (receivedCmd[2] == 0xA4 && receivedCmd[6] == 0x32 && prevCmd == 0) { //Respond with PPSE - uint8_t ppsea[39] = {0x6F, 0x23, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31, 0xA5, 0x11, 0xBF, 0x0C, 0x0E, 0x61, 0x0C, 0x4F, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x87, 0x01, 0x01, 0x90, 0x00}; + // depending on card reader commands, the Proxmark will answer to fool the reader + // respond with PPSE + if (receivedCmd[2] == 0xA4 && receivedCmd[6] == 0x32 && prevCmd == 0) { + uint8_t ppsea[39] = { + 0x6F, 0x23, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59, + 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, + 0x30, 0x31, 0xA5, 0x11, 0xBF, 0x0C, 0x0E, 0x61, + 0x0C, 0x4F, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, + 0x10, 0x10, 0x87, 0x01, 0x01, 0x90, 0x00 + }; memcpy(&dynamic_response_info.response[1], ppsea, sizeof(ppsea)); dynamic_response_info.response_n = sizeof(ppsea) + 1; prevCmd++; - } else if (receivedCmd[2] == 0xA4 && receivedCmd[10] == 0x03 && receivedCmd[11] == 0x10 && prevCmd == 1) { //Respond Visa AID - uint8_t visauid_long[34] = {0x6F, 0x1E, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xA5, 0x13, 0x50, 0x0B, 0x56, 0x49, 0x53, 0x41, 0x20, 0x43, 0x52, 0x45, 0x44, 0x49, 0x54, 0x9F, 0x38, 0x03, 0x9F, 0x66, 0x02, 0x90, 0x00}; + + // respond Visa AID + } else if (receivedCmd[2] == 0xA4 && receivedCmd[10] == 0x03 && receivedCmd[11] == 0x10 && prevCmd == 1) { + uint8_t visauid_long[34] = { + 0x6F, 0x1E, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00, + 0x03, 0x10, 0x10, 0xA5, 0x13, 0x50, 0x0B, 0x56, + 0x49, 0x53, 0x41, 0x20, 0x43, 0x52, 0x45, 0x44, + 0x49, 0x54, 0x9F, 0x38, 0x03, 0x9F, 0x66, 0x02, + 0x90, 0x00 + }; memcpy(&dynamic_response_info.response[1], visauid_long, sizeof(visauid_long)); dynamic_response_info.response_n = sizeof(visauid_long) + 1; prevCmd++; - } else if (receivedCmd[1] == 0x80 && receivedCmd[2] == 0xA8 && receivedCmd[6] == 0x83 && prevCmd == 2) { //GET PROCESSING + + // GET PROCESSING + } else if (receivedCmd[1] == 0x80 && receivedCmd[2] == 0xA8 && receivedCmd[6] == 0x83 && prevCmd == 2) { uint8_t processing_long[10] = {0x80, 0x06, 0x00, 0x80, 0x08, 0x01, 0x01, 0x00, 0x90, 0x00}; memcpy(&dynamic_response_info.response[1], processing_long, sizeof(processing_long)); dynamic_response_info.response_n = sizeof(processing_long) + 1; prevCmd++; - } else if (receivedCmd[1] == 0x00 && receivedCmd[2] == 0xB2 && prevCmd == 3) { //SFI + + // SFI + } else if (receivedCmd[1] == 0x00 && receivedCmd[2] == 0xB2 && prevCmd == 3) { uint8_t last[4] = {0x70, 0x15, 0x57, 0x13}; uint8_t statusapdu[2] = {0x90, 0x00}; uint8_t card[25]; @@ -401,6 +455,7 @@ void RunMod(void) { memcpy(&dynamic_response_info.response[1], card, sizeof(card)); dynamic_response_info.response_n = sizeof(card) + 1; prevCmd++; + } else { uint8_t finished[2] = {0x6f, 0x00}; memcpy(&dynamic_response_info.response[1], finished, sizeof(finished)); @@ -424,7 +479,7 @@ void RunMod(void) { Dbhexdump(dynamic_response_info.response_n, dynamic_response_info.response, false); DbpString("----"); - // Add CRC bytes, always used in ISO 14443A-4 compliant cards + // add CRC bytes, always used in ISO 14443A-4 compliant cards AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n); dynamic_response_info.response_n += 2; diff --git a/armsrc/Standalone/hf_reblay.c b/armsrc/Standalone/hf_reblay.c new file mode 100644 index 000000000..ac85c2d70 --- /dev/null +++ b/armsrc/Standalone/hf_reblay.c @@ -0,0 +1,415 @@ +//----------------------------------------------------------------------------- +// Salvador Mendoza (salmg.net) - January 01, 2021 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Code to relay 14a technology data aka reblay by Salvador Mendoza +//----------------------------------------------------------------------------- +#include "standalone.h" +#include "proxmark3_arm.h" +#include "appmain.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "ticks.h" +#include "string.h" +#include "BigBuf.h" +#include "iso14443a.h" +#include "protocols.h" +#include "cmd.h" + +#include "usart.h" // Bluetooth reading & writing + +void ModInfo(void) { + DbpString(" HF - Relaying ISO/14443A data over Bluetooth - (Salvador Mendoza)"); +} +/* This standalone implements two different modes: reading & emulating, to switch between them +* just press the button. +* +* Reading ISO-14443A technology is not limited to payment cards. This example +* was designed to open new possibilities relaying ISO-14443A data over Bluetooth. +* +* Instructions: +* +* I recommend setting up & run the other end before start sending or receving data in this Proxmark3 +* standalone. +* +* For the reading mode: +* - Set up and run the other end first, to where the Proxmark will send the data. +* - After the card is detected, Proxmark3 will send a package. The first byte will be the package +* length, then, the card data. Use the first length byte to read the whole package. +* - Proxmark3 will expect a raw APDU from the other end, then it will be sent to the card. +* - The answer of the card will be sent back to the connection, repeating the cycle. +* +* For the emulation mode: +* - Set up and run the other end first, from where the Proxmark will receive the data. +* - When the Proxmark3 detected the terminal, it will send the command to the connection. +* - The first byte will be the package length, then, the terminal command. Use the first +* length byte to read the whole package. +* - Proxmark3 will expect a raw APDU from the other end, then it will be sent to the terminal. +* - The command of the terminal will be sent back to the connection, repeating the cycle. +* +* Notes: +* - The emulation mode was tested in a real SumUp payment terminal. This does not mean +* that it will work in all the terminals around the world. +* - The emulation mode implements different techniques to try to keep the connection alive: +* WTX or ACK for NACK requests. Some of these requests could be denied depending on +* the reader configuration. +* +* +* Be brave enough to share your knowledge & inspire others. +*/ + +void RunMod() { + StandAloneMode(); + Dbprintf(_YELLOW_(">>") "Relaying ISO/14443A data over Bluetooth a.k.a. reblay Started<<"); + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + +// Allocate 512 bytes for the dynamic modulation, created when the reader queries for it +// Such a response is less time critical, so we can prepare them on the fly +#define DYNAMIC_RESPONSE_BUFFER_SIZE 512 +#define DYNAMIC_MODULATION_BUFFER_SIZE 1024 + + uint8_t flags = FLAG_4B_UID_IN_DATA; //UID 4 bytes(could be 7 bytes if needed it) + uint8_t data[PM3_CMD_DATA_SIZE] = {0x00}; // in case there is a read command received we shouldn't break + + uint8_t visauid[7] = {0x01, 0x02, 0x03, 0x04}; + memcpy(data, visauid, 4); + + // to initialize the emulation + uint8_t tagType = 4; // 4 = ISO/IEC 14443-4 - javacard (JCOP) + tag_response_info_t *responses; + + uint32_t cuid = 0; + uint32_t counters[3] = { 0x00, 0x00, 0x00 }; + uint8_t tearings[3] = { 0xbd, 0xbd, 0xbd }; + uint8_t pages = 0; + + + // For received Bluetooth package + uint8_t rpacket[MAX_FRAME_SIZE] = { 0x00 }; + uint16_t lenpacket = 0; + + // For answering the commands + uint8_t apdubuffer[MAX_FRAME_SIZE] = { 0x00 }; + uint8_t apdulen = 0; + + // Buffer for Bluetooth data + uint8_t buffert[MAX_FRAME_SIZE] = { 0x00 }; + uint8_t bufferlen = 0; + + // Reading card + iso14a_card_select_t card_a_info; + + // For init ping process + uint8_t sak = {0x0}; + uint8_t atqa[2] = { 0x00, 0x00 }; + uint8_t uidc[10] = { 0x00 }; + uint8_t uidlen = 0; + uint8_t ats[MAX_FRAME_SIZE] = { 0x00 }; + uint8_t atsl = 0; + + uint8_t rdata[14] = { 0x00 }; + + // Command buffers + uint8_t receivedCmd[MAX_FRAME_SIZE] = { 0x00 }; + uint8_t receivedCmdPar[MAX_PARITY_SIZE] = { 0x00 }; + + uint8_t dynamic_response_buffer[DYNAMIC_RESPONSE_BUFFER_SIZE] = {0}; + uint8_t dynamic_modulation_buffer[DYNAMIC_MODULATION_BUFFER_SIZE] = {0}; + + // Command response - handler + tag_response_info_t dynamic_response_info = { + .response = dynamic_response_buffer, + .response_n = 0, + .modulation = dynamic_modulation_buffer, + .modulation_n = 0 + }; + +#define STATE_READ 0 +#define STATE_EMU 1 + + uint8_t state = STATE_READ; + + if (state == STATE_READ) { + DbpString(_YELLOW_("[ ") "In reading mode" _YELLOW_(" ]")); + } else { + DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]")); + } + + for (;;) { + WDT_HIT(); + + // Exit from RunMod, send a usbcommand. + if (data_available()) break; + + // Button held down or pressed? + int button_pressed = BUTTON_HELD(1000); + + if (button_pressed == BUTTON_HOLD) // Holding down the button + break; + else if (button_pressed == BUTTON_SINGLE_CLICK) { // Pressing one time change between reading & emulation + if (state == STATE_READ) { + state = STATE_EMU; + DbpString(_YELLOW_("[ ") "In emulation mode" _YELLOW_(" ]")); + } else { + state = STATE_READ; + DbpString(_YELLOW_("[ ") "In reading mode" _YELLOW_(" ]")); + } + } + + SpinDelay(500); + + if (state == STATE_READ) { + LED_A_ON(); + clear_trace(); + set_tracing(true); + + iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); + if (iso14443a_select_card(NULL, &card_a_info, NULL, true, 0, false)) { + LED_B_ON(); + + // Get data to send a ping with UID + ATQA + SAK + sak = card_a_info.sak; + uidlen = card_a_info.uidlen; + atsl = card_a_info.ats_len; + + memcpy(uidc, card_a_info.uid, uidlen); + memcpy(atqa, card_a_info.atqa, 2); + memcpy(ats, card_a_info.ats, atsl); + + DbpString(_YELLOW_("[ ") "UID:" _YELLOW_(" ]")); + Dbhexdump(uidlen, uidc, false); + DbpString(_YELLOW_("[ ") "ATQA:" _YELLOW_(" ]")); + Dbhexdump(2, atqa, false); + Dbprintf(_YELLOW_("[ ") "SAK: %x "_YELLOW_(" ]"), sak); + DbpString(_YELLOW_("[ ") "ATS:" _YELLOW_(" ]")); + Dbhexdump(atsl, ats, false); + + memcpy(&rdata[1], uidc, uidlen); + memcpy(&rdata[uidlen + 1], atqa, 2); + memcpy(&rdata[uidlen + 3], &sak, 1); + + rdata[0] = uidlen + 3; + + // ping = UID + ATQA + SAK + DbpString(_YELLOW_("[ ") "Ping:" _YELLOW_(" ]")); + Dbhexdump(uidlen + 4, rdata, false); + + DbpString(_YELLOW_("[ ") "Sending ping" _YELLOW_(" ]")); + if (usart_writebuffer_sync(rdata, uidlen + 4) == PM3_SUCCESS) { + DbpString(_YELLOW_("[ ") "Sent!" _YELLOW_(" ]")); + + for (;;) { + if (usart_rxdata_available()) { + lenpacket = usart_read_ng(rpacket, sizeof(rpacket)); + + if (lenpacket > 1) { + DbpString(_YELLOW_("[ ") "Bluetooth data:" _YELLOW_(" ]")); + Dbhexdump(lenpacket, rpacket, false); + + apdulen = iso14_apdu(rpacket, (uint16_t) lenpacket, false, apdubuffer, NULL); + + DbpString(_YELLOW_("[ ") "Card response:" _YELLOW_(" ]")); + Dbhexdump(apdulen - 2, apdubuffer, false); + + bufferlen = apdulen - 2; + + memcpy(&buffert[0], &bufferlen, 1); + memcpy(&buffert[1], apdubuffer, bufferlen); + + DbpString(_YELLOW_("[ ") "Buffer:" _YELLOW_(" ]")); + Dbhexdump(bufferlen, buffert, false); + + usart_writebuffer_sync(buffert, bufferlen + 1); + + + } else if (lenpacket == 1) { + DbpString(_YELLOW_("[ ") "Done!" _YELLOW_(" ]")); + LED_C_ON(); + + for (uint8_t i = 0; i < 3; i++) + SpinDelay(1000); + + break; + } + } + LED_B_OFF(); + } + } else { + DbpString(_YELLOW_("[ ") "Cannot send it!" _YELLOW_(" ]")); + SpinDelay(1000); + } + } + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_D_OFF(); + } else if (state == STATE_EMU) { + LED_A_OFF(); + LED_C_ON(); + + // free eventually allocated BigBuf memory but keep Emulator Memory + BigBuf_free_keep_EM(); + + if (SimulateIso14443aInit(tagType, flags, data, &responses, &cuid, counters, tearings, &pages) == false) { + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, PM3_EINIT, NULL, 0); + DbpString(_YELLOW_("!!") "Error initializing the emulation process!"); + SpinDelay(500); + state = STATE_READ; + DbpString(_YELLOW_("[ ") "Initialized reading mode" _YELLOW_(" ]")); + break; + } + + // We need to listen to the high-frequency, peak-detected path. + iso14443a_setup(FPGA_HF_ISO14443A_TAGSIM_LISTEN); + + int len = 0; // Command length + int retval = PM3_SUCCESS; // Check emulation status + + uint8_t resp = 0; // Bluetooth response + lenpacket = 0; + + uint8_t prevcmd = 0x00; // Keep track of last terminal type command + + clear_trace(); + set_tracing(true); + + for (;;) { + LED_B_OFF(); + // Clean receive command buffer + if (!GetIso14443aCommandFromReader(receivedCmd, receivedCmdPar, &len)) { + DbpString(_YELLOW_("!!") "Emulator stopped"); + retval = PM3_EOPABORTED; + break; + } + tag_response_info_t *p_response = NULL; + LED_B_ON(); + + // dynamic_response_info will be in charge of responses + dynamic_response_info.response_n = 0; + + if (lenpacket == 0 && resp == 2) { // Check for Bluetooth packages + if (usart_rxdata_available()) { + lenpacket = usart_read_ng(rpacket, sizeof(rpacket)); + + if (lenpacket > 0) { + DbpString(_YELLOW_("[ ") "Received Bluetooth data" _YELLOW_(" ]")); + Dbhexdump(lenpacket, rpacket, false); + memcpy(&dynamic_response_info.response[1], rpacket, lenpacket); + dynamic_response_info.response[0] = prevcmd; + dynamic_response_info.response_n = lenpacket + 1; + resp = 1; + } + } + } + if (receivedCmd[0] == ISO14443A_CMD_REQA && len == 1) { // Received a REQUEST + DbpString(_YELLOW_("+") "REQUEST Received"); + p_response = &responses[RESP_INDEX_ATQA]; + } else if (receivedCmd[0] == ISO14443A_CMD_HALT && len == 4) { // Received a HALT + DbpString(_YELLOW_("+") "Received a HALT"); + p_response = NULL; + resp = 0; + } else if (receivedCmd[0] == ISO14443A_CMD_WUPA && len == 1) { // Received a WAKEUP + DbpString(_YELLOW_("+") "WAKEUP Received"); + p_response = &responses[RESP_INDEX_ATQA]; + resp = 0; + } else if (receivedCmd[1] == 0x20 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 2) { // Received request for UID (cascade 1) + DbpString(_YELLOW_("+") "Request for UID C1"); + p_response = &responses[RESP_INDEX_UIDC1]; + } else if (receivedCmd[1] == 0x70 && receivedCmd[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT && len == 9) { // Received a SELECT (cascade 1) + DbpString(_YELLOW_("+") "Request for SELECT S1"); + p_response = &responses[RESP_INDEX_SAKC1]; + } else if (receivedCmd[0] == ISO14443A_CMD_RATS && len == 4) { // Received a RATS request + DbpString(_YELLOW_("+") "Request for RATS"); + p_response = &responses[RESP_INDEX_RATS]; + resp = 1; + } else if (receivedCmd[0] == 0xf2 && len == 4) { // ACKed - Time extension + DbpString(_YELLOW_("!!") "Reader accepted time extension!"); + p_response = NULL; + } else if ((receivedCmd[0] == 0xb2 || receivedCmd[0] == 0xb3) && len == 3) { //NACK - Request more time WTX + DbpString(_YELLOW_("!!") "NACK - time extension request?"); + if (resp == 2 && lenpacket == 0) { + DbpString(_YELLOW_("!!") "Requesting more time - WTX"); + dynamic_response_info.response_n = 2; + dynamic_response_info.response[0] = 0xf2; + dynamic_response_info.response[1] = 0x0b; // Requesting the maximum amount of time + } else if (lenpacket == 0) { + DbpString(_YELLOW_("!!") "NACK - ACK - Resend last command!"); // To burn some time as well + dynamic_response_info.response[0] = 0xa3; + dynamic_response_info.response_n = 1; + } else { + DbpString(_YELLOW_("!!") "Avoiding request - Bluetooth data already in memory!!"); + } + } else { + DbpString(_GREEN_("[ ") "Card reader command" _GREEN_(" ]")); + Dbhexdump(len - 2, &receivedCmd[1], false); + + if ((receivedCmd[0] == 0x02 || receivedCmd[0] == 0x03) && len > 3) { // Process reader commands + + if (resp == 1) { + prevcmd = receivedCmd[0]; + bufferlen = len - 3; + memcpy(&buffert[0], &bufferlen, 1); + memcpy(&buffert[1], &receivedCmd[1], bufferlen); + resp = 2; + } + if (lenpacket > 0) { + DbpString(_YELLOW_("[ ") "Answering using Bluetooth data!" _YELLOW_(" ]")); + memcpy(&dynamic_response_info.response[1], rpacket, lenpacket); + dynamic_response_info.response[0] = receivedCmd[0]; + dynamic_response_info.response_n = lenpacket + 1; + lenpacket = 0; + resp = 1; + } else { + DbpString(_YELLOW_("[ ") "New command: sent it & waiting for Bluetooth response!" _YELLOW_(" ]")); + usart_writebuffer_sync(buffert, bufferlen + 1); + p_response = NULL; + } + + } else { + if (lenpacket == 0) { + DbpString(_YELLOW_("!!") "Received unknown command!"); + memcpy(dynamic_response_info.response, receivedCmd, len); + dynamic_response_info.response_n = len; + } else { + DbpString(_YELLOW_("!!") "Avoiding unknown command - Bluetooth data already in memory!!"); + } + } + } + if (dynamic_response_info.response_n > 0) { + DbpString(_GREEN_("[ ") "Proxmark3 answer" _GREEN_(" ]")); + Dbhexdump(dynamic_response_info.response_n, dynamic_response_info.response, false); + DbpString("----"); + if (lenpacket > 0) { + lenpacket = 0; + resp = 1; + } + // Add CRC bytes, always used in ISO 14443A-4 compliant cards + AddCrc14A(dynamic_response_info.response, dynamic_response_info.response_n); + dynamic_response_info.response_n += 2; + + if (prepare_tag_modulation(&dynamic_response_info, DYNAMIC_MODULATION_BUFFER_SIZE) == false) { + Dbprintf(_YELLOW_("[ ") "Buffer size: %d "_YELLOW_(" ]"), dynamic_response_info.response_n); + SpinDelay(500); + DbpString(_YELLOW_("!!") "Error preparing Proxmark to answer!"); + continue; + } + p_response = &dynamic_response_info; + } + + if (p_response != NULL) { + EmSendPrecompiledCmd(p_response); + } + } + switch_off(); + + set_tracing(false); + BigBuf_free_keep_EM(); + reply_ng(CMD_HF_MIFARE_SIMULATE, retval, NULL, 0); + } + } + DbpString(_YELLOW_("[=]") "exiting"); + LEDsoff(); +} diff --git a/armsrc/Standalone/lf_nexid.c b/armsrc/Standalone/lf_nexid.c new file mode 100644 index 000000000..819811223 --- /dev/null +++ b/armsrc/Standalone/lf_nexid.c @@ -0,0 +1,343 @@ +//----------------------------------------------------------------------------- +// BOSCA Maxime, RIOUX Guilhem 2021 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// main code for Nexwatch ID / Magic number collector. +//----------------------------------------------------------------------------- + +#include +#include "standalone.h" // standalone definitions +#include "proxmark3_arm.h" +#include "appmain.h" +#include "lfops.h" +#include "lfsampling.h" +#include "BigBuf.h" +#include "fpgaloader.h" +#include "util.h" +#include "dbprint.h" +#include "printf.h" +#include "spiffs.h" +#include "ticks.h" +#include "lfdemod.h" +#include "commonutil.h" + +/* + * `lf_nexid` sniffs after LF Nexwatch ID credentials, and stores them in internal + * flash. It requires RDV4 hardware (for flash and battery). + * + * On entering stand-alone mode, this module will start reading/record LF Nexwatch ID credentials. + * Every found / collected credential will be written/appended to the logfile in flash + * as a text string. + * + * LEDs: + * - LED A: reading / record + * - LED B: writing to flash + * - LED C: unmounting/sync'ing flash (normally < 100ms) + * + * To retrieve log file from flash: + * + * 1. mem spiffs dump -s lf_nexcollect.log -d lf_nexcollect.log + * Copies log file from flash to your client. + * + * 2. exit the Proxmark3 client + * + * 3. more lf_nexcollect.log + * + * This module emits debug strings during normal operation -- so try it out in + * the lab connected to PM3 client before taking it into the field. + * + * To delete the log file from flash: + * + * 1. mem spiffs remove -f lf_nexcollect.log + */ + +#define LF_NEXCOLLECT_LOGFILE "lf_nexcollect.log" +typedef enum { + SCRAMBLE, + DESCRAMBLE +} NexWatchScramble_t; + + +static void DownloadLogInstructions(void) { + Dbprintf(""); + Dbprintf("[=] To get the logfile from flash and display it:"); + Dbprintf("[=] " _YELLOW_("1.") " mem spiffs dump -s "LF_NEXCOLLECT_LOGFILE" -d "LF_NEXCOLLECT_LOGFILE); + Dbprintf("[=] " _YELLOW_("2.") " exit proxmark3 client"); + Dbprintf("[=] " _YELLOW_("3.") " cat "LF_NEXCOLLECT_LOGFILE); +} + +bool log_exists; + +// scramble parity (1234) -> (4231) +static uint8_t nexwatch_parity_swap(uint8_t parity) { + uint8_t a = (((parity >> 3) & 1)); + a |= (((parity >> 1) & 1) << 1); + a |= (((parity >> 2) & 1) << 2); + a |= ((parity & 1) << 3); + return a; +} +// parity check +// from 32b hex id, 4b mode, +static uint8_t nexwatch_parity(uint8_t hexid[5]) { + uint8_t p = 0; + for (uint8_t i = 0; i < 5; i++) { + p ^= NIBBLE_HIGH(hexid[i]); + p ^= NIBBLE_LOW(hexid[i]); + } + return nexwatch_parity_swap(p); +} + +/// NETWATCH checksum +/// @param magic = 0xBE Quadrakey, 0x88 Nexkey +/// @param id = descrambled id (printed card number) +/// @param parity = the parity based upon the scrambled raw id. +static uint8_t nexwatch_checksum(uint8_t magic, uint32_t id, uint8_t parity) { + uint8_t a = ((id >> 24) & 0xFF); + a -= ((id >> 16) & 0xFF); + a -= ((id >> 8) & 0xFF); + a -= (id & 0xFF); + a -= magic; + a -= (reflect8(parity) >> 4); + return reflect8(a); +} + +// Scrambled id ( 88 bit cardnumber format) +// ref:: http://www.proxmark.org/forum/viewtopic.php?pid=14662#p14662 +static int nexwatch_scamble(NexWatchScramble_t action, uint32_t *id, uint32_t *scambled) { + + // 255 = Not used/Unknown other values are the bit offset in the ID/FC values + uint8_t hex_2_id [] = { + 31, 27, 23, 19, 15, 11, 7, 3, + 30, 26, 22, 18, 14, 10, 6, 2, + 29, 25, 21, 17, 13, 9, 5, 1, + 28, 24, 20, 16, 12, 8, 4, 0 + }; + + switch (action) { + case DESCRAMBLE: { + *id = 0; + for (uint8_t idx = 0; idx < 32; idx++) { + + if (hex_2_id[idx] == 255) + continue; + + bool bit_state = (*scambled >> hex_2_id[idx]) & 1; + *id |= (bit_state << (31 - idx)); + } + break; + } + case SCRAMBLE: { + *scambled = 0; + for (uint8_t idx = 0; idx < 32; idx++) { + + if (hex_2_id[idx] == 255) + continue; + + bool bit_state = (*id >> idx) & 1; + *scambled |= (bit_state << (31 - hex_2_id[idx])); + } + break; + } + default: + break; + } + return PM3_SUCCESS; +} + + +static void append(uint8_t *entry, size_t entry_len) { + + LED_B_ON(); + if (log_exists == false) { + rdv40_spiffs_write(LF_NEXCOLLECT_LOGFILE, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE); + log_exists = true; + } else { + rdv40_spiffs_append(LF_NEXCOLLECT_LOGFILE, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE); + } + LED_B_OFF(); +} + + +static int detectNexWatch(uint8_t *dest, size_t *size, bool *invert) { + + uint8_t preamble[28] = {0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + // sanity check. + if (*size < 96) return -1; + + size_t startIdx = 0; + + if (!preambleSearch(dest, preamble, sizeof(preamble), size, &startIdx)) { + // if didn't find preamble try again inverting + uint8_t preamble_i[28] = {1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + if (!preambleSearch(dest, preamble_i, sizeof(preamble_i), size, &startIdx)) return -4; + *invert ^= 1; + } + // size tests? + return (int) startIdx; +} + +static uint32_t PSKDemod(uint8_t *dest, size_t *size, int *startIdx) { + //buffer for result + int clk = 0, invert = 0; + //checks if the signal is just noise + if (getSignalProperties()->isnoise) { + return PM3_ESOFT; + } + + //int pskRawDemod_ext(uint8_t *dest, size_t *size, int *clock, int *invert, int *startIdx) + int errCnt = pskRawDemod_ext(dest, size, &clk, &invert, startIdx); + if (errCnt > 100) { + BigBuf_free(); + return PM3_ESOFT; + } + return PM3_SUCCESS; +} + +static int demodNexWatch(void) { + uint8_t *dest = BigBuf_get_addr(); + size_t size = MIN(16385, BigBuf_max_traceLen()); + int startIdx = 0; + + if (PSKDemod(dest, &size, &startIdx) != PM3_SUCCESS) { + return PM3_ESOFT; + } + bool invert = false; + int idx = detectNexWatch(dest, &size, &invert); + if (idx < 0) { + return PM3_ESOFT; + } + + // skip the 4 first bits from the nexwatch preamble identification (we use 4 extra zeros..) + idx += 4; + + // size = size -idx; + dest = dest + idx; + Dbprintf("[+] Id: %d, Size: %d", idx, size); + //setClockGrid(g_DemodClock, g_DemodStartIdx + (idx * g_DemodClock)); + + if (invert) { + Dbprintf("Inverted the demodulated data"); + for (size_t i = 0; i < size; i++) + dest[i] ^= 1; + } + + //got a good demod + uint32_t raw1 = bytebits_to_byte(dest, 32); + uint32_t raw2 = bytebits_to_byte(dest + 32, 32); + uint32_t raw3 = bytebits_to_byte(dest + 32 + 32, 32); + + // get rawid + uint32_t rawid = 0; + for (uint8_t k = 0; k < 4; k++) { + for (uint8_t m = 0; m < 8; m++) { + rawid = (rawid << 1) | dest[m + k + (m * 4)]; + } + } + + // descrambled id + uint32_t cn = 0; + uint32_t scambled = bytebits_to_byte(dest + 8 + 32, 32); + nexwatch_scamble(DESCRAMBLE, &cn, &scambled); + + uint8_t mode = bytebits_to_byte(dest + 72, 4); + uint8_t chk = bytebits_to_byte(dest + 80, 8); + + // parity check + // from 32b hex id, 4b mode + uint8_t hex[5] = {0}; + for (uint8_t i = 0; i < 5; i++) { + hex[i] = bytebits_to_byte(dest + 8 + 32 + (i * 8), 8); + } + // mode is only 4 bits. + hex[4] &= 0xf0; + uint8_t calc_parity = nexwatch_parity(hex); + + uint8_t magic = 0; + // output + Dbprintf(" NexWatch raw id : " _YELLOW_("0x%08"PRIx32), rawid); + + uint8_t temp_checksum; + for (; magic < 255; magic++) { + temp_checksum = nexwatch_checksum(magic, cn, calc_parity); + if (temp_checksum == chk) { + Dbprintf(" Magic number : " _GREEN_("0x%X"), magic); + break; + } + } + + Dbprintf(" 88bit id : " _YELLOW_("%"PRIu32) " (" _YELLOW_("0x%08"PRIx32)")", cn, cn); + Dbprintf(" mode : %x", mode); + + Dbprintf(" Raw : " _YELLOW_("%08"PRIX32"%08"PRIX32"%08"PRIX32), raw1, raw2, raw3); + + uint8_t entry[81]; + memset(entry, 0, sizeof(entry)); + + sprintf((char *)entry, "Nexwatch ID: %"PRIu32", Magic bytes: 0x%X, Mode: %x\n", + cn, + magic, + mode); + + append(entry, strlen((char *)entry)); + Dbprintf("%s", entry); + + BigBuf_free(); + return PM3_SUCCESS; +} + +void ModInfo(void) { + DbpString(_YELLOW_(" Nexwatch credentials detection mode") " - a.k.a NexID (jrjgjk & Zolorah)"); +} + +void RunMod(void) { + + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); + LFSetupFPGAForADC(LF_DIVISOR_125, true); + BigBuf_Clear(); + + StandAloneMode(); + + Dbprintf(_YELLOW_("[=] Standalone mode nexid started")); + + rdv40_spiffs_lazy_mount(); + + log_exists = exists_in_spiffs(LF_NEXCOLLECT_LOGFILE); + + // the main loop for your standalone mode + for (;;) { + WDT_HIT(); + + // exit from IceHID, send a usbcommand. + if (data_available()) break; + + // Was our button held down or pressed? + int button_pressed = BUTTON_HELD(280); + if (button_pressed == BUTTON_HOLD) + break; + + LED_A_ON(); + + uint32_t res; + + + size_t size = MIN(16385, BigBuf_max_traceLen()); + DoAcquisition_config(false, size); + res = demodNexWatch(); + if (res == PM3_SUCCESS) { + LED_A_OFF(); + continue; + } + + } + + LED_C_ON(); + rdv40_spiffs_lazy_unmount(); + LED_C_OFF(); + + LEDsoff(); + DownloadLogInstructions(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); +} diff --git a/armsrc/appmain.c b/armsrc/appmain.c index a419617d1..b6781ac6d 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1114,15 +1114,9 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_LF_HITAG_ELOAD: { - /* - struct p { - uint16_t len; - uint8_t *data; - } PACKED; - struct p *payload = (struct p *) packet->data.asBytes; + lf_hitag_t *payload = (lf_hitag_t *) packet->data.asBytes; uint8_t *mem = BigBuf_get_EM_addr(); - memcpy((uint8_t *)mem.sectors, payload->data, payload->len); - */ + memcpy((uint8_t *)mem, payload->data, payload->len); break; } #endif @@ -1574,6 +1568,14 @@ static void PacketReceived(PacketCommandNG *packet) { MifareGen3Freez(); break; } + case CMD_HF_MIFARE_G3_RDBL: { + struct p { + uint8_t blockno; + } PACKED; + struct p *payload = (struct p *) packet->data.asBytes; + MifareG3ReadBlk(payload->blockno); + break; + } case CMD_HF_MIFARE_PERSONALIZE_UID: { struct p { uint8_t keytype; diff --git a/armsrc/desfire_crypto.c b/armsrc/desfire_crypto.c index 782466be2..c32d6e4df 100644 --- a/armsrc/desfire_crypto.c +++ b/armsrc/desfire_crypto.c @@ -103,8 +103,9 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, un uint8_t *tout = (uint8_t *) out; while (length > 0) { - for (i = 0; i < 8; i++) + for (i = 0; i < 8; i++) { tin[i] = (unsigned char)(tin[i] ^ iv[i]); + } mbedtls_des3_crypt_ecb(&ctx3, tin, tout); @@ -121,8 +122,9 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, un void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key) { uint8_t data[8]; memcpy(data, value, 8); - for (int n = 0; n < 8; n++) - data[n] &= 0xfe; + for (int n = 0; n < 8; n++) { + data[n] &= 0xFE; + } Desfire_des_key_new_with_version(data, key); } @@ -138,10 +140,12 @@ void Desfire_des_key_new_with_version(const uint8_t value[8], desfirekey_t key) void Desfire_3des_key_new(const uint8_t value[16], desfirekey_t key) { uint8_t data[16]; memcpy(data, value, 16); - for (int n = 0; n < 8; n++) - data[n] &= 0xfe; - for (int n = 8; n < 16; n++) + for (int n = 0; n < 8; n++) { + data[n] &= 0xFE; + } + for (int n = 8; n < 16; n++) { data[n] |= 0x01; + } Desfire_3des_key_new_with_version(data, key); } @@ -156,8 +160,9 @@ void Desfire_3des_key_new_with_version(const uint8_t value[16], desfirekey_t key void Desfire_3k3des_key_new(const uint8_t value[24], desfirekey_t key) { uint8_t data[24]; memcpy(data, value, 24); - for (int n = 0; n < 8; n++) - data[n] &= 0xfe; + for (int n = 0; n < 8; n++) { + data[n] &= 0xFE; + } Desfire_3k3des_key_new_with_version(data, key); } @@ -194,13 +199,13 @@ uint8_t Desfire_key_get_version(desfirekey_t key) { void Desfire_key_set_version(desfirekey_t key, uint8_t version) { for (int n = 0; n < 8; n++) { uint8_t version_bit = ((version & (1 << (7 - n))) >> (7 - n)); - key->data[n] &= 0xfe; + key->data[n] &= 0xFE; key->data[n] |= version_bit; if (key->type == T_DES) { key->data[n + 8] = key->data[n]; } else { // Write ~version to avoid turning a 3DES key into a DES key - key->data[n + 8] &= 0xfe; + key->data[n + 8] &= 0xFE; key->data[n + 8] |= ~version_bit; } } @@ -267,23 +272,32 @@ void cmac_generate_subkeys(desfirekey_t key) { // Used to compute CMAC on complete blocks memcpy(key->cmac_sk1, l, kbs); + txor = l[0] & 0x80; + lsl(key->cmac_sk1, kbs); - if (txor) + + if (txor) { key->cmac_sk1[kbs - 1] ^= R; + } // Used to compute CMAC on the last block if non-complete memcpy(key->cmac_sk2, key->cmac_sk1, kbs); + txor = key->cmac_sk1[0] & 0x80; + lsl(key->cmac_sk2, kbs); - if (txor) + + if (txor) { key->cmac_sk2[kbs - 1] ^= R; + } } void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac) { int kbs = key_block_size(key); - if (kbs == 0) + if (kbs == 0) { return; + } uint8_t *buffer = BigBuf_malloc(padded_data_length(len, kbs)); @@ -306,8 +320,10 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t le } size_t key_block_size(const desfirekey_t key) { - if (key == NULL) + if (key == NULL) { return 0; + } + size_t block_size = 8; switch (key->type) { case T_DES: @@ -830,10 +846,12 @@ void mifare_cypher_blocks_chained(desfiretag_t tag, desfirekey_t key, uint8_t *i size_t block_size; if (tag) { - if (!key) + if (key == NULL) { key = DESFIRE(tag)->session_key; - if (!ivect) + } + if (ivect == NULL) { ivect = DESFIRE(tag)->ivect; + } switch (DESFIRE(tag)->authentication_scheme) { case AS_LEGACY: diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 4ed10bd1e..d775f02cc 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -1313,7 +1313,7 @@ static bool em4x50_sim_read_word(uint32_t *word) { } } - *word = BYTES2UINT32(bytes); + *word = BYTES2UINT32_BE(bytes); // check parities if ((parities == parities_calculated) && (stop_bit == 0)) { diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 063ed6c07..8aa7178cb 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -1148,6 +1148,9 @@ void SniffHitag2(void) { // Enable and reset counter AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + // Assert a sync signal. This sets all timers to 0 on next active clock edge + AT91C_BASE_TCB->TCB_BCR = 1; + int frame_count = 0, response = 0, overflow = 0, lastbit = 1, tag_sof = 4; bool rising_edge = false, reader_frame = false, bSkip = true; uint8_t rx[HITAG_FRAME_LEN]; @@ -1293,11 +1296,15 @@ void SniffHitag2(void) { // Reset the timer to restart while-loop that receives frames AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + + // Assert a sync signal. This sets all timers to 0 on next active clock edge + AT91C_BASE_TCB->TCB_BCR = 1; } LEDsoff(); AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); set_tracing(false); diff --git a/armsrc/lfadc.c b/armsrc/lfadc.c index 6a351bfca..2bfe41529 100644 --- a/armsrc/lfadc.c +++ b/armsrc/lfadc.c @@ -246,6 +246,9 @@ void lf_init(bool reader, bool simulate) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + // Assert a sync signal. This sets all timers to 0 on next active clock edge + AT91C_BASE_TCB->TCB_BCR = 1; + // Prepare data trace uint32_t bufsize = 10000; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 4330f2c4f..2a8642f82 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -2594,6 +2594,42 @@ OUT: BigBuf_free(); } +void MifareG3ReadBlk(uint8_t blockno) { + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + int retval = PM3_SUCCESS; + uint8_t *par = BigBuf_malloc(MAX_PARITY_SIZE); + uint8_t *buf = BigBuf_malloc(PM3_CMD_DATA_SIZE); + uint8_t *uid = BigBuf_malloc(10); + if (iso14443a_select_card(uid, NULL, NULL, true, 0, true) == false) { + retval = PM3_ESOFT; + goto OUT; + } + + LED_B_ON(); + uint32_t save_iso14a_timeout = iso14a_get_timeout(); + iso14a_set_timeout(13560000 / 1000 / (8 * 16) * 1000); // 2 seconds timeout + + uint8_t cmd[] = { 0xCF, 0x00, 0x00, 0x00, 0x00, 0xCE, blockno, 0x00, 0x00}; + AddCrc14A(cmd, sizeof(cmd) - 2); + + ReaderTransmit(cmd, sizeof(cmd), NULL); + int res = ReaderReceive(buf, par); + if (res != 18) { + retval = PM3_ESOFT; + } + iso14a_set_timeout(save_iso14a_timeout); + LED_B_OFF(); + +OUT: + reply_ng(CMD_HF_MIFARE_G3_RDBL, retval, buf, 18); + // turns off + OnSuccessMagic(); + BigBuf_free(); +} + void MifareSetMod(uint8_t *datain) { uint8_t mod = datain[0]; diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index baf3d9e31..bacb8405e 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -49,6 +49,9 @@ void MifareGen3UID(uint8_t uidlen, uint8_t *uid); // Gen 3 magic card set UID wi void MifareGen3Blk(uint8_t block_len, uint8_t *block); // Gen 3 magic card overwrite manufacturer block void MifareGen3Freez(void); // Gen 3 magic card lock further UID changes +// MFC GEN3 GTU +void MifareG3ReadBlk(uint8_t blockno); + void MifareSetMod(uint8_t *datain); void MifarePersonalizeUID(uint8_t keyType, uint8_t perso_option, uint64_t key); diff --git a/armsrc/pcf7931.c b/armsrc/pcf7931.c index 6ad44d7c4..bb96ea97e 100644 --- a/armsrc/pcf7931.c +++ b/armsrc/pcf7931.c @@ -14,13 +14,17 @@ #define ALLOC 16 size_t DemodPCF7931(uint8_t **outBlocks) { + + // 2021 iceman, memor uint8_t bits[256] = {0x00}; uint8_t blocks[8][16]; + uint8_t *dest = BigBuf_get_addr(); int GraphTraceLen = BigBuf_max_traceLen(); - if (GraphTraceLen > 18000) + if (GraphTraceLen > 18000) { GraphTraceLen = 18000; + } int i = 2, j, lastval, bitidx, half_switch; int clock = 64; @@ -38,15 +42,17 @@ size_t DemodPCF7931(uint8_t **outBlocks) { /* Find first local max/min */ if (dest[1] > dest[0]) { while (i < GraphTraceLen) { - if (!(dest[i] > dest[i - 1]) && dest[i] > lmax) + if (!(dest[i] > dest[i - 1]) && dest[i] > lmax) { break; + } i++; } dir = 0; } else { while (i < GraphTraceLen) { - if (!(dest[i] < dest[i - 1]) && dest[i] < lmin) + if (!(dest[i] < dest[i - 1]) && dest[i] < lmin) { break; + } i++; } dir = 1; @@ -58,6 +64,7 @@ size_t DemodPCF7931(uint8_t **outBlocks) { block_done = 0; for (bitidx = 0; i < GraphTraceLen; i++) { + if ((dest[i - 1] > dest[i] && dir == 1 && dest[i] > lmax) || (dest[i - 1] < dest[i] && dir == 0 && dest[i] < lmin)) { lc = i - lastval; lastval = i; @@ -66,8 +73,8 @@ size_t DemodPCF7931(uint8_t **outBlocks) { // Tolerance is 1/8 of clock rate (arbitrary) if (ABS(lc - clock / 4) < tolerance) { // 16T0 - if ((i - pmc) == lc) { /* 16T0 was previous one */ - /* It's a PMC ! */ + if ((i - pmc) == lc) { // 16T0 was previous one + // It's a PMC i += (128 + 127 + 16 + 32 + 33 + 16) - 1; lastval = i; pmc = 0; @@ -77,8 +84,8 @@ size_t DemodPCF7931(uint8_t **outBlocks) { } } else if (ABS(lc - clock / 2) < tolerance) { // 32TO - if ((i - pmc) == lc) { /* 16T0 was previous one */ - /* It's a PMC ! */ + if ((i - pmc) == lc) { // 16T0 was previous one + // It's a PMC ! i += (128 + 127 + 16 + 32 + 33) - 1; lastval = i; pmc = 0; @@ -95,8 +102,9 @@ size_t DemodPCF7931(uint8_t **outBlocks) { // Error if (++warnings > 10) { - if (DBGLEVEL >= DBG_EXTENDED) - Dbprintf("Error: too many detection errors, aborting."); + if (DBGLEVEL >= DBG_EXTENDED) { + Dbprintf("Error: too many detection errors, aborting"); + } return 0; } @@ -122,13 +130,19 @@ size_t DemodPCF7931(uint8_t **outBlocks) { block_done = 0; half_switch = 0; } - if (i < GraphTraceLen) + + if (i < GraphTraceLen) { dir = (dest[i - 1] > dest[i]) ? 0 : 1; + } } - if (bitidx == 255) + + if (bitidx == 255) { bitidx = 0; - warnings = 0; - if (num_blocks == 4) break; + } + + if (num_blocks == 4) { + break; + } } memcpy(outBlocks, blocks, 16 * num_blocks); return num_blocks; @@ -138,10 +152,11 @@ bool IsBlock0PCF7931(uint8_t *block) { // assuming all RFU bits are set to 0 // if PAC is enabled password is set to 0 if (block[7] == 0x01) { - if (!memcmp(block, "\x00\x00\x00\x00\x00\x00\x00", 7) - && !memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) { + if (!memcmp(block, "\x00\x00\x00\x00\x00\x00\x00", 7) && + !memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) { return true; } + } else if (block[7] == 0x00) { if (!memcmp(block + 9, "\x00\x00\x00\x00\x00\x00\x00", 7)) { return true; @@ -211,8 +226,9 @@ void ReadPCF7931(void) { // exit if too many errors during reading if (tries > 50 && (2 * errors > tries)) { - if (DBGLEVEL >= DBG_INFO) + if (DBGLEVEL >= DBG_INFO) { Dbprintf("[!!] Error reading the tag, only partial content"); + } goto end; } @@ -461,27 +477,32 @@ void SendCmdPCF7931(uint32_t *tab) { //initialization of the timer AT91C_BASE_PMC->PMC_PCER |= (0x1 << AT91C_ID_TC0); AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; //clock at 48/32 MHz + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; // clock at 48/32 MHz AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN; + + // Assert a sync signal. This sets all timers to 0 on next active clock edge AT91C_BASE_TCB->TCB_BCR = 1; tempo = AT91C_BASE_TC0->TC_CV; for (u = 0; tab[u] != 0; u += 3) { // modulate antenna HIGH(GPIO_SSC_DOUT); - while (tempo != tab[u]) + while (tempo != tab[u]) { tempo = AT91C_BASE_TC0->TC_CV; + } // stop modulating antenna LOW(GPIO_SSC_DOUT); - while (tempo != tab[u + 1]) + while (tempo != tab[u + 1]) { tempo = AT91C_BASE_TC0->TC_CV; + } // modulate antenna HIGH(GPIO_SSC_DOUT); - while (tempo != tab[u + 2]) + while (tempo != tab[u + 2]) { tempo = AT91C_BASE_TC0->TC_CV; + } } LED_A_OFF(); diff --git a/armsrc/ticks.c b/armsrc/ticks.c index 9b563cc54..8f15afa12 100644 --- a/armsrc/ticks.c +++ b/armsrc/ticks.c @@ -125,6 +125,8 @@ void StartCountUS(void) { AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Assert a sync signal. This sets all timers to 0 on next active clock edge AT91C_BASE_TCB->TCB_BCR = 1; while (AT91C_BASE_TC1->TC_CV > 0); diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 5c826790d..21ebf9efd 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -76,7 +76,9 @@ endif (NOT SKIPPYTHON EQUAL 1) # If cross-compiled, we need to init source and build. if (CMAKE_TOOLCHAIN_FILE) - set(CFLAGS_EXTERNAL_LIB "CFLAGS=--target=${CMAKE_C_COMPILER_TARGET} -w") + if (ANDROID) + set(CFLAGS_EXTERNAL_LIB "CFLAGS=--target=${CMAKE_C_COMPILER_TARGET} -w") + endif (ANDROID) set(EMBED_READLINE ON) set(EMBED_BZIP2 ON) endif (CMAKE_TOOLCHAIN_FILE) @@ -213,6 +215,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/iso7816/iso7816core.c ${PM3_ROOT}/client/src/cipurse/cipursecrypto.c ${PM3_ROOT}/client/src/cipurse/cipursecore.c + ${PM3_ROOT}/client/src/cipurse/cipursetest.c ${PM3_ROOT}/client/src/loclass/cipher.c ${PM3_ROOT}/client/src/loclass/cipherutils.c ${PM3_ROOT}/client/src/loclass/elite_crack.c @@ -225,9 +228,14 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/mifare/mifarehost.c ${PM3_ROOT}/client/src/nfc/ndef.c ${PM3_ROOT}/client/src/mifare/desfire_crypto.c + ${PM3_ROOT}/client/src/mifare/desfirecrypto.c + ${PM3_ROOT}/client/src/mifare/desfiresecurechan.c + ${PM3_ROOT}/client/src/mifare/desfirecore.c + ${PM3_ROOT}/client/src/mifare/desfiretest.c ${PM3_ROOT}/client/src/uart/uart_posix.c ${PM3_ROOT}/client/src/uart/uart_win32.c ${PM3_ROOT}/client/src/ui/overlays.ui + ${PM3_ROOT}/client/src/ui/image.ui ${PM3_ROOT}/client/src/aiddesfire.c ${PM3_ROOT}/client/src/aidsearch.c ${PM3_ROOT}/client/src/cmdanalyse.c diff --git a/client/Makefile b/client/Makefile index c0f87e0a7..51f7cb2df 100644 --- a/client/Makefile +++ b/client/Makefile @@ -14,10 +14,18 @@ vpath %.dic dictionaries OBJDIR = obj ifeq ($(USE_BREW),1) - INCLUDES += -I$(BREW_PREFIX)/include - LDLIBS += -L$(BREW_PREFIX)/lib - PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt/lib/pkgconfig - PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt5/lib/pkgconfig + ifdef MACPORTS_PREFIX + INCLUDES += -I$(MACPORTS_PREFIX)/include + LDLIBS += -L$(MACPORTS_PREFIX)/lib + PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(MACPORTS_PREFIX)/lib/pkgconfig + PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(MACPORTS_PREFIX)/libexec/qt/lib/pkgconfig + PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(MACPORTS_PREFIX)/libexec/qt5/lib/pkgconfig + else + INCLUDES += -I$(BREW_PREFIX)/include + LDLIBS += -L$(BREW_PREFIX)/lib + PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt/lib/pkgconfig + PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt5/lib/pkgconfig + endif endif ################### @@ -117,8 +125,13 @@ INCLUDES += $(HARDNESTEDLIBINC) ## Lua ifneq ($(SKIPLUASYSTEM),1) - LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua5.2 2>/dev/null) - LUALDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs lua5.2 2>/dev/null) + ifdef MACPORTS_PREFIX + LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua-5.2 2>/dev/null) + LUALDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs lua-5.2 2>/dev/null) + else + LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua5.2 2>/dev/null) + LUALDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs lua5.2 2>/dev/null) + endif ifneq ($(LUALDLIBS),) LUALIB = LUALIBLD = $(LUALDLIBS) @@ -279,8 +292,12 @@ CXXINCLUDES += $(QTINCLUDES) ## Readline ifneq ($(SKIPREADLINE),1) ifeq ($(USE_BREW),1) - LDLIBS += -L$(BREW_PREFIX)/opt/readline/lib - INCLUDES += -I$(BREW_PREFIX)/opt/readline/include + ifdef MACPORTS_PREFIX + INCLUDES += -I$(MACPORTS_PREFIX)/include/readline + else + LDLIBS += -L$(BREW_PREFIX)/opt/readline/lib + INCLUDES += -I$(BREW_PREFIX)/opt/readline/include + endif endif LDLIBS += -lreadline READLINE_FOUND = 1 @@ -559,6 +576,7 @@ SRCS = aiddesfire.c \ fido/fidocore.c \ cipurse/cipursecore.c \ cipurse/cipursecrypto.c \ + cipurse/cipursetest.c \ fileutils.c \ flash.c \ generator.c \ @@ -571,6 +589,10 @@ SRCS = aiddesfire.c \ loclass/elite_crack.c \ loclass/ikeys.c \ mifare/desfire_crypto.c \ + mifare/desfirecrypto.c \ + mifare/desfirecore.c \ + mifare/desfiresecurechan.c \ + mifare/desfiretest.c \ mifare/mad.c \ mifare/mfkey.c \ mifare/mifare4.c \ @@ -639,9 +661,9 @@ OBJS += $(OBJCSRCS:%.m=$(OBJDIR)/%.o) BINS = proxmark3 -CLEAN = $(BINS) src/version_pm3.c src/*.moc.cpp src/ui/ui_overlays.h lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lua +CLEAN = $(BINS) src/version_pm3.c src/*.moc.cpp src/ui/ui_overlays.h src/ui/ui_image.h lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lua # transition: cleaning also old path stuff -CLEAN += flasher *.moc.cpp ui/ui_overlays.h +CLEAN += flasher *.moc.cpp ui/ui_overlays.h ui/ui_image.h ########### # targets # @@ -657,7 +679,7 @@ proxmark3: $(OBJS) $(STATICLIBS) lualibs/pm3_cmd.lua lualibs/mfc_default_keys.lu $(info [=] LD $@) $(Q)$(LD) $(PM3LDFLAGS) $(OBJS) $(STATICLIBS) $(LDLIBS) -o $@ -src/proxgui.cpp: src/ui/ui_overlays.h +src/proxgui.cpp: src/ui/ui_overlays.h src/ui/ui_image.h src/proxguiqt.moc.cpp: src/proxguiqt.h $(info [-] MOC $@) @@ -667,6 +689,10 @@ src/ui/ui_overlays.h: src/ui/overlays.ui $(info [-] UIC $@) $(Q)$(UIC) $^ > $@ +src/ui/ui_image.h: src/ui/image.ui + $(info [-] UIC $@) + $(Q)$(UIC) $^ > $@ + lualibs/pm3_cmd.lua: ../include/pm3_cmd.h $(info [=] GEN $@) $(Q)awk -f pm3_cmd_h2lua.awk $^ > $@ diff --git a/client/android/pm3_main.c b/client/android/pm3_main.c index 74019b0cd..1fe2f7d32 100644 --- a/client/android/pm3_main.c +++ b/client/android/pm3_main.c @@ -60,6 +60,12 @@ void HideGraphWindow(void) {} void RepaintGraphWindow(void) {} +void ShowPictureWindow(char *fn) {} + +void HidePictureWindow(void) {} + +void RepaintPictureWindow(void) {} + int push_cmdscriptfile(char *path, bool stayafter) { return PM3_SUCCESS; } static bool OpenPm3(void) { diff --git a/client/deps/cliparser/cliparser.c b/client/deps/cliparser/cliparser.c index ec9f50e20..2a5804550 100644 --- a/client/deps/cliparser/cliparser.c +++ b/client/deps/cliparser/cliparser.c @@ -298,6 +298,59 @@ int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int return 0; } +int CLIGetOptionList(struct arg_str *argstr, const CLIParserOption *option_array, int *value) { + char data[200] = {0}; + int datalen = 0; + int res = CLIParamStrToBuf(argstr, (uint8_t *)data, sizeof(data), &datalen); + if (res) + return res; + + // no data to check - we do not touch *value, just return + if (datalen == 0) + return 0; + + str_lower(data); + + int val = -1; + int cntr = 0; + for (int i = 0; i < CLI_MAX_OPTLIST_LEN && option_array[i].text != NULL; i++) { + // exact match + if (strcmp(option_array[i].text, data) == 0) { + *value = option_array[i].code; + return 0; + } + // partial match + if (strncmp(option_array[i].text, data, datalen) == 0) { + val = option_array[i].code; + cntr++; + } + } + + // check partial match + if (cntr == 0) { + PrintAndLogEx(ERR, "Parameter error: No similar option to `%s`. Valid options: %s\n", argstr->sval[0], argstr->hdr.datatype); + return 20; + } + if (cntr > 1) { + PrintAndLogEx(ERR, "Parameter error: Several options fit to `%s`. Valid options: %s\n", argstr->sval[0], argstr->hdr.datatype); + return 21; + } + + *value = val; + return 0; +} + +const char *CLIGetOptionListStr(const CLIParserOption *option_array, int value) { + static const char *errmsg = "n/a"; + + for (int i = 0; i < CLI_MAX_OPTLIST_LEN && option_array[i].text != NULL; i++) { + if (option_array[i].code == value) + return option_array[i].text; + } + return errmsg; +} + + // hexstr -> u64, w optional len input and default value fallback. // 0 = failed // 1 = OK diff --git a/client/deps/cliparser/cliparser.h b/client/deps/cliparser/cliparser.h index 39a5792f8..f50fec2ee 100644 --- a/client/deps/cliparser/cliparser.h +++ b/client/deps/cliparser/cliparser.h @@ -51,6 +51,8 @@ #define CLIGetStrWithReturn(ctx, paramnum, data, datalen) if (CLIParamStrToBuf(arg_get_str((ctx), (paramnum)), (data), (*datalen), (datalen))) {CLIParserFree((ctx)); return PM3_ESOFT;} +#define CLIGetOptionListWithReturn(ctx, paramnum, option_array, option_array_len, value) if (CLIGetOptionList(arg_get_str((ctx), (paramnum)), (option_array), (option_array_len), (value))) {CLIParserFree((ctx)); return PM3_ESOFT;} + typedef struct { void **argtable; size_t argtableLen; @@ -59,6 +61,14 @@ typedef struct { const char *programHelp; char buf[1024 + 60]; } CLIParserContext; + +#define CLI_MAX_OPTLIST_LEN 50 +// option list needs to have NULL at the last record int the field `text` +typedef struct { + int code; + const char *text; +} CLIParserOption; + int CLIParserInit(CLIParserContext **ctx, const char *vprogramName, const char *vprogramHint, const char *vprogramHelp); void CLIParserPrintHelp(CLIParserContext *ctx); int CLIParserParseString(CLIParserContext *ctx, const char *str, void *vargtable[], size_t vargtableLen, bool allowEmptyExec); @@ -69,6 +79,10 @@ int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); int CLIParamBinToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); +// names in the CLIParserOption array must be in the lowercase format +int CLIGetOptionList(struct arg_str *argstr, const CLIParserOption *option_array, int *value); +const char *CLIGetOptionListStr(const CLIParserOption *option_array, int value); + uint64_t arg_get_u64_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint64_t def); int arg_get_u64_hexstr_def_nlen(CLIParserContext *ctx, uint8_t paramnum, uint64_t def, uint64_t *out, uint8_t nlen, bool optional); int arg_get_u32_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out); diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index d22d2e7c2..688165fbe 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -6,7 +6,8 @@ ffffffffffff # Defaultkey(firstkeyusedbyprogramifnouserdefinedkey) 000000000000 # Blankkey a0a1a2a3a4a5 # NFC Forum MADkey -A5A4A3A2A1A0 # MAD access key (reversed) +A5A4A3A2A1A0 # MAD access key A (reversed) +89ECA97F8C2A # MAD access key B b0b1b2b3b4b5 c0c1c2c3c4c5 d0d1d2d3d4d5 @@ -20,6 +21,9 @@ d3f7d3f7d3f7 # key A Wien a0478cc39091 533cb6c723f6 8fd0a4f256e9 +e00000000000 # iCopy-X +e7d6064c5860 +b27ccab30dbd # d2ece8b9395e # lib / Nat Bieb # NSCP default key @@ -289,6 +293,49 @@ c2b7ec7d4eb1 ac70ca327a04 eb0a8ff88ade # +# Transport system Metromoney +2803bcb0c7e1 +9c616585e26d +4fa9eb49f75e +2dade48942c5 +a160fcd5ec4c +112233445566 +361a62f35bc9 +# +# Transport system Spain +83f3cb98c258 +070d486bc555 +a9b018868cc1 +9dcdb136110c +749934cc8ed3 +506db955f161 +f088a85e71d7 +72b458d60363 +70c714869dc7 +b32464412ee3 +f253c30568c4 +1c68315674ac +cfe63749080a +c1e6f8afc9ec +dd0de3ba08a6 +3d923eb73534 +ff94f86b09a6 +d61707ffdfb1 +8223205047b6 +9951a273dee7 +c9449301af93 +66695a45c9fa +89aa9d743812 +c41514defc07 +c52876869800 +5353b3aecb53 +2e4169a5c79d +4bb747e48c2a +6285a1c8eb5c +5145c34dba19 +25352912cd8d +81b20c274c3f +# # Data from https://github.com/RadioWar/NFCGUI 44dd5a385aaf 21a600056cb0 @@ -1369,6 +1416,17 @@ F678905568C3 D1417E431949 4BF6DE347FB6 # +# 3a471b2192bf a297ceb7d34b ae76242931f1 +# +# +124578ABFEDC +ABFEDC124578 +4578ABFEDC12 +# +# Data from +# premier inn hotel chain +5e594208ef02 +af9e38d36582 diff --git a/client/dictionaries/mfdes_default_keys.dic b/client/dictionaries/mfdes_default_keys.dic index a26d13d32..9e0a50fd0 100644 --- a/client/dictionaries/mfdes_default_keys.dic +++ b/client/dictionaries/mfdes_default_keys.dic @@ -9,6 +9,7 @@ 6AC292FAA1315B4D858AB3A3D7D5933A 404142434445464748494a4b4c4d4e4f 3112B738D8862CCD34302EB299AAB456 # Gallagher AES (https://pastebin.com/GkbGLz8r) +47454D5850524553534F53414D504C45 # Gemalto 00112233445566778899aabbccddeeff 2b7e151628aed2a6abf7158809cf4f3c fbeed618357133667c85e08f7236a8de diff --git a/client/dictionaries/mfp_default_keys.dic b/client/dictionaries/mfp_default_keys.dic index a063e34ad..f8fcc6972 100644 --- a/client/dictionaries/mfp_default_keys.dic +++ b/client/dictionaries/mfp_default_keys.dic @@ -1,3 +1,4 @@ +47454D5850524553534F53414D504C45 # Gemalto ffffffffffffffffffffffffffffffff 00000000000000000000000000000000 a0a1a2a3a4a5a6a7a0a1a2a3a4a5a6a7 diff --git a/client/dictionaries/t55xx_default_pwds.dic b/client/dictionaries/t55xx_default_pwds.dic index ce916dfe8..65e0ebbc2 100644 --- a/client/dictionaries/t55xx_default_pwds.dic +++ b/client/dictionaries/t55xx_default_pwds.dic @@ -38,6 +38,10 @@ E9920427 50520901 # iCopy-X 20206666 +# ID/HID CARD COPER SK-663 +65857569 +# password found on discord +5469616E # Default pwd, simple: 00000000 11111111 diff --git a/client/experimental_lib/CMakeLists.txt b/client/experimental_lib/CMakeLists.txt index 084259d34..dcf17a367 100644 --- a/client/experimental_lib/CMakeLists.txt +++ b/client/experimental_lib/CMakeLists.txt @@ -76,7 +76,9 @@ endif (NOT SKIPPYTHON EQUAL 1) # If cross-compiled, we need to init source and build. if (CMAKE_TOOLCHAIN_FILE) - set(CFLAGS_EXTERNAL_LIB "CFLAGS=--target=${CMAKE_C_COMPILER_TARGET} -w") + if (ANDROID) + set(CFLAGS_EXTERNAL_LIB "CFLAGS=--target=${CMAKE_C_COMPILER_TARGET} -w") + endif (ANDROID) set(EMBED_READLINE ON) set(EMBED_BZIP2 ON) endif (CMAKE_TOOLCHAIN_FILE) diff --git a/client/luascripts/hf_14a_protectimus_nfc.lua b/client/luascripts/hf_14a_protectimus_nfc.lua index 77b595cd5..2ff3fec06 100644 --- a/client/luascripts/hf_14a_protectimus_nfc.lua +++ b/client/luascripts/hf_14a_protectimus_nfc.lua @@ -81,7 +81,7 @@ function getUnixTime(datetime) local datetime_pattern = "(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)+(%d+):(%d+)" local new_year, new_month, new_day, new_hour, new_minute, new_seconds, new_hour_offset, new_minute_offset = datetime:match(datetime_pattern) - if new_year == nil or new_month == nil or new_day == nil or + if new_year == nil or new_month == nil or new_day == nil or new_hour == nil or new_minute == nil or new_seconds == nil or new_hour_offset == nil or new_minute_offset == nil then @@ -111,7 +111,7 @@ function sendRaw(rawdata, options) -- arg1 is the defined flags for sending "raw" ISO 14443A package arg1 = flags, - + -- arg2 contains the length, which is half the length of the ASCII -- string data arg2 = string.len(rawdata) / 2, @@ -137,7 +137,7 @@ function readOTP(show_output) lib14a.disconnect() return oops(err) end - + -- parse the response local cmd_response = Command.parse(res) local len = tonumber(cmd_response.arg1) * 2 @@ -160,7 +160,7 @@ function readOTP(show_output) if show_output then print("[" .. ansicolors.green .. "+" .. ansicolors.reset .. "] OTP: " .. ansicolors.green .. otp_value .. ansicolors.reset) end - else + else print("[" .. ansicolors.red .. "-" .. ansicolors.reset .."] Error: Could not read the OTP") otp_value = nil end @@ -183,7 +183,7 @@ function readInfo(show_output) lib14a.disconnect() return oops(err) end - + -- parse the response local cmd_response = Command.parse(res) local len = tonumber(cmd_response.arg1) * 2 @@ -233,7 +233,7 @@ function readInfo(show_output) end return otp_interval - else + else print("[" .. ansicolors.red .. "-" .. ansicolors.reset .."] Error: Could not read the token info") otp_value = nil end @@ -261,7 +261,7 @@ function bruteforceCommands() lib14a.disconnect() return oops(err) end - + -- parse the response local cmd_response = Command.parse(res) local len = tonumber(cmd_response.arg1) * 2 @@ -287,7 +287,7 @@ function setTime(time, otp_interval) -- build the raw command data local data = "120000" ..string.format("%02x", otp_interval) .. string.format("%08x", time_var1) .. string.format("%02x", time_var2) - + -- calculate XOR checksum on data local checksum = 0 for i = 1, #data, 2 do @@ -306,7 +306,7 @@ function setTime(time, otp_interval) lib14a.disconnect() return oops(err) end - + -- parse the response local cmd_response = Command.parse(res) local len = tonumber(cmd_response.arg1) * 2 @@ -343,8 +343,8 @@ function timeTravelAttack(datetime_string, otp_interval) -- read the OTP local otp = readOTP(false) - print(string.format("[" .. ansicolors.green .. "+" .. ansicolors.reset .. "] The future OTP on " .. - ansicolors.yellow .. "%s (%d) " .. ansicolors.reset .. "is " .. + print(string.format("[" .. ansicolors.green .. "+" .. ansicolors.reset .. "] The future OTP on " .. + ansicolors.yellow .. "%s (%d) " .. ansicolors.reset .. "is " .. ansicolors.green .. "%s" .. ansicolors.reset, datetime_string, future_time, otp)) -- reset the current time @@ -372,7 +372,7 @@ function main(args) if o == 'h' then return help() end if o == 'i' then operation = READ_INFO end if o == 'r' then operation = READ_OTP end - if o == 't' then + if o == 't' then operation = TIME_TRAVELER_ATTACK target_time = a end diff --git a/client/luascripts/hf_15_magic.lua b/client/luascripts/hf_15_magic.lua index 173cf1ee7..e7c6bf1ce 100644 --- a/client/luascripts/hf_15_magic.lua +++ b/client/luascripts/hf_15_magic.lua @@ -68,10 +68,10 @@ end --- Set UID on magic command enabled on a ICEMAN based REPO local function magicUID_iceman(b0, b1) print('Using backdoor Magic tag function') - core.console('hf 15 raw -2 -c 02213E00000000') - core.console('hf 15 raw -2 -c 02213F69960000') - core.console('hf 15 raw -2 -c 022138'..b1) - core.console('hf 15 raw -2 -c 022139'..b0) + core.console('hf 15 raw -2 -c -d 02213E00000000') + core.console('hf 15 raw -2 -c -d 02213F69960000') + core.console('hf 15 raw -2 -c -d 022138'..b1) + core.console('hf 15 raw -2 -c -d 022139'..b0) end -- --- Set UID on magic command enabled, OFFICAL REPO diff --git a/client/luascripts/hf_mfu_magicwrite.lua b/client/luascripts/hf_mfu_magicwrite.lua index c01f043fe..2919562db 100644 --- a/client/luascripts/hf_mfu_magicwrite.lua +++ b/client/luascripts/hf_mfu_magicwrite.lua @@ -12,29 +12,30 @@ local err_lock = 'use -k or change cfg0 block' copyright = 'Copyright (c) 2017 IceSQL AB. All rights reserved.' author = 'Christian Herrmann' -version = 'v1.1.3' +version = 'v1.1.4' desc = 'This script enables easy programming of a MAGIC NTAG 21* card' example = [[ - -- wipe tag - script run hf_mfu_magicwrite -w - - -- wipe a locked down tag by giving the password - script run hf_mfu_magicwrite -k ffffffff -w - - --read magic tag configuration - script run hf_mfu_magicwrite -c + -- read magic tag configuration + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -c ]]..ansicolors.reset..[[ -- set uid - script run hf_mfu_magicwrite -u 04112233445566 + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -u 04112233445566 ]]..ansicolors.reset..[[ -- set pwd / pack - script run hf_mfu_magicwrite -p 11223344 -a 8080 + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -p 11223344 -a 8080 ]]..ansicolors.reset..[[ -- set version to NTAG213 - script run hf_mfu_magicwrite -v 0004040201000f03 + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -v 0004040201000f03 ]]..ansicolors.reset..[[ -- set signature - script run hf_mfu_magicwrite -s 1122334455667788990011223344556677889900112233445566778899001122 + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -s 1122334455667788990011223344556677889900112233445566778899001122 ]]..ansicolors.reset..[[ + + -- wipe tag + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -w ]]..ansicolors.reset..[[ + + -- wipe a locked down tag by giving the password + ]]..ansicolors.yellow..[[script run hf_mfu_magicwrite -k ffffffff -w ]]..ansicolors.reset..[[ + ]] usage = [[ script run hf_mfu_easywrite -h -k -c -w -u -t -p -a -s -o -v @@ -190,11 +191,27 @@ local function read_config() elseif cardtype == '02' then typestr = 'NTAG 216' end + local versionstr = 'unknown' + if version == '0004030101000B03' then versionstr = 'UL EV1 48b' + elseif version == '0004030101000E03' then versionstr = 'UL EV1 128b' + elseif version == '0004040101000B03' then versionstr = 'NTAG 210' + elseif version == '0004040101000E03' then versionstr = 'NTAG 212' + elseif version == '0004040201000F03' then versionstr = 'NTAG 213' + elseif version == '0004040201001103' then versionstr = 'NTAG 215' + elseif version == '0004040201001303' then versionstr = 'NTAG 216' + elseif version == '0004040502011303' then versionstr = 'NTAG I2C 1K' + elseif version == '0004040502011503' then versionstr = 'NTAG I2C 2K' + elseif version == '0004040502021303' then versionstr = 'NTAG I2C 1K PLUS' + elseif version == '0004040502021503' then versionstr = 'NTAG I2C 2K PLUS' + elseif version == '0004040401000F03' then versionstr = 'NTAG 213F' + elseif version == '0004040401001303' then versionstr = 'NTAG 216F' + end + print('Magic NTAG 21* Configuration') print(' - Type ', typestr, '(genuine cardtype)') print(' - Password', pwd) print(' - Pack ', pack) - print(' - Version ', version) + print(' - Version ', version, '(' .. versionstr .. ')') print(' - Signature', signature1..signature2) lib14a.disconnect() diff --git a/client/resources/mad.json b/client/resources/mad.json index aae377af8..8b1c4193c 100644 --- a/client/resources/mad.json +++ b/client/resources/mad.json @@ -5995,6 +5995,7 @@ "application": "(access control and security) VIGIK", "company": "CDV International", "mad": "0x4905", + "service_provider": "CDVI", "system_integrator": "CDVI" }, @@ -11836,6 +11837,13 @@ "service_provider": "HUG-Witschi AG", "system_integrator": "HUG-Witschi AG" }, + { + "application": "Access control - Hotel lodging system", + "company": "DORMA/KABA", + "mad": "0x9051", + "service_provider": "DORMA/KABA", + "system_integrator": "DORMA/KABA" + }, { "application": "FIDELIO CRUISE ship property management system, - materials management system, - meal counting system", "company": "FIDELIO CRUISE SOFTWARE GMBH", @@ -13558,4 +13566,4 @@ "service_provider": "Tech ID", "system_integrator": "Tech ID" } -] \ No newline at end of file +] diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c index 70d2b51d3..a72011717 100644 --- a/client/src/cipurse/cipursecore.c +++ b/client/src/cipurse/cipursecore.c @@ -18,23 +18,30 @@ #include "cmdhf14a.h" #include "emv/emvcore.h" #include "emv/emvjson.h" +#include "iso7816/apduinfo.h" #include "ui.h" #include "util.h" // context for secure channel CipurseContext cipurseContext; -static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - uint8_t data[APDU_RES_LEN] = {0}; - uint8_t securedata[APDU_RES_LEN] = {0}; - sAPDU secapdu; +static int CIPURSEExchangeEx(bool activate_field, bool leave_field_on, sAPDU apdu, bool include_le, + uint16_t le, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { - *ResultLen = 0; - if (sw) *sw = 0; + if (result_len == NULL) { + PrintAndLogEx(FAILED, "CIPURSEExchangeEx, result_len is NULL"); + return PM3_EINVARG; + } + + *result_len = 0; + + if (sw) { + *sw = 0; + } uint16_t isw = 0; int res = 0; - if (ActivateField) { + if (activate_field) { DropField(); msleep(50); } @@ -45,48 +52,58 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, // COMPUTE APDU int datalen = 0; - uint16_t xle = IncludeLe ? 0x100 : 0x00; - if (xle == 0x100 && Le != 0) - xle = Le; + uint16_t xle = include_le ? 0x100 : 0x00; + if (xle == 0x100 && le != 0) { + xle = le; + } - CipurseCAPDUReqEncode(&cipurseContext, &apdu, &secapdu, securedata, IncludeLe, Le); + sAPDU secapdu; + uint8_t securedata[APDU_RES_LEN] = {0}; + CipurseCAPDUReqEncode(&cipurseContext, &apdu, &secapdu, securedata, include_le, le); + uint8_t data[APDU_RES_LEN] = {0}; if (APDUEncodeS(&secapdu, false, xle, data, &datalen)) { PrintAndLogEx(ERR, "APDU encoding error."); return 201; } - if (GetAPDULogging()) + if (GetAPDULogging()) { PrintAndLogEx(SUCCESS, ">>>> %s", sprint_hex(data, datalen)); + } - res = ExchangeAPDU14a(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + res = ExchangeAPDU14a(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len); if (res) { return res; } - if (GetAPDULogging()) - PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen)); + if (GetAPDULogging()) { + PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(result, *result_len)); + } - if (*ResultLen < 2) { + if (*result_len < 2) { return 200; } size_t rlen = 0; - if (*ResultLen == 2) { - if (cipurseContext.RequestSecurity == CPSMACed || cipurseContext.RequestSecurity == CPSEncrypted) + if (*result_len == 2) { + if (cipurseContext.RequestSecurity == CPSMACed || cipurseContext.RequestSecurity == CPSEncrypted) { CipurseCClearContext(&cipurseContext); + } + + isw = result[0] * 0x0100 + result[1]; - isw = Result[0] * 0x0100 + Result[1]; } else { - CipurseCAPDURespDecode(&cipurseContext, Result, *ResultLen, securedata, &rlen, &isw); - memcpy(Result, securedata, rlen); + CipurseCAPDURespDecode(&cipurseContext, result, *result_len, securedata, &rlen, &isw); + memcpy(result, securedata, rlen); } - if (ResultLen != NULL) - *ResultLen = rlen; + if (result_len != NULL) { + *result_len = rlen; + } - if (sw != NULL) + if (sw != NULL) { *sw = isw; + } if (isw != 0x9000) { if (GetAPDULogging()) { @@ -102,69 +119,69 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, return PM3_SUCCESS; } -static int CIPURSEExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - return CIPURSEExchangeEx(false, true, apdu, true, 0, Result, MaxResultLen, ResultLen, sw); +static int CIPURSEExchange(sAPDU apdu, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchangeEx(false, true, apdu, true, 0, result, max_result_len, result_len, sw); } -int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { +int CIPURSESelect(bool activate_field, bool leave_field_on, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { uint8_t data[] = {0x41, 0x44, 0x20, 0x46, 0x31}; CipurseCClearContext(&cipurseContext); - return EMVSelect(CC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL); + return EMVSelect(CC_CONTACTLESS, activate_field, leave_field_on, data, sizeof(data), result, max_result_len, result_len, sw, NULL); } -int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x84, 0x00, 0x00, 0x00, NULL}, true, 0x16, Result, MaxResultLen, ResultLen, sw); +int CIPURSEChallenge(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x84, 0x00, 0x00, 0x00, NULL}, true, 0x16, result, max_result_len, result_len, sw); } -int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw); +int CIPURSEMutalAuthenticate(uint8_t keyindex, uint8_t *params, uint8_t paramslen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyindex, paramslen, params}, true, 0x10, result, max_result_len, result_len, sw); } -int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, attrlen, attr}, false, 0, Result, MaxResultLen, ResultLen, sw); +int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, attrlen, attr}, false, 0, result, max_result_len, result_len, sw); } -int CIPURSEDeleteFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff}; - return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, 02, fileIdBin}, false, 0, Result, MaxResultLen, ResultLen, sw); +int CIPURSEDeleteFile(uint16_t fileid, uint8_t *Result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + uint8_t fileIdBin[] = {fileid >> 8, fileid & 0xff}; + return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, 02, fileIdBin}, false, 0, Result, max_result_len, result_len, sw); } -int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff}; - return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, Result, MaxResultLen, ResultLen, sw); +int CIPURSESelectFile(uint16_t fileid, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + uint8_t fileIdBin[] = {fileid >> 8, fileid & 0xff}; + return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, result, max_result_len, result_len, sw); } -int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 0, NULL}, Result, MaxResultLen, ResultLen, sw); +int CIPURSESelectMFFile(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 0, NULL}, result, max_result_len, result_len, sw); } -int CIPURSEReadFileAttributes(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - return CIPURSEExchange((sAPDU) {0x80, 0xce, 0x00, 0x00, 0, NULL}, Result, MaxResultLen, ResultLen, sw); +int CIPURSEReadFileAttributes(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchange((sAPDU) {0x80, 0xce, 0x00, 0x00, 0, NULL}, result, max_result_len, result_len, sw); } -int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - return CIPURSEExchange((sAPDU) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, Result, MaxResultLen, ResultLen, sw); +int CIPURSEReadBinary(uint16_t offset, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchange((sAPDU) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, result, max_result_len, result_len, sw); } -int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - return CIPURSEExchange((sAPDU) {0x00, 0xd6, (offset >> 8) & 0x7f, offset & 0xff, datalen, data}, Result, MaxResultLen, ResultLen, sw); +int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchange((sAPDU) {0x00, 0xd6, (offset >> 8) & 0x7f, offset & 0xff, datalen, data}, result, max_result_len, result_len, sw); } -bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) { +bool CIPURSEChannelAuthenticate(uint8_t keyindex, uint8_t *key, bool verbose) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; uint16_t sw = 0; CipurseContext cpc = {0}; - CipurseCSetKey(&cpc, keyIndex, key); + CipurseCSetKey(&cpc, keyindex, key); // get RP, rP int res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw); if (res != 0 || len != 0x16) { - if (verbose) - PrintAndLogEx(ERR, "Cipurse get challenge " _RED_("error") ". Card returns 0x%04x.", sw); - + if (verbose) { + PrintAndLogEx(ERR, "Cipurse get challenge ( " _RED_("fail") " ). Card returns 0x%04x", sw); + } return false; } CipurseCSetRandomFromPICC(&cpc, buf); @@ -174,17 +191,20 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) { CipurseCAuthenticateHost(&cpc, authparams); // authenticate - res = CIPURSEMutalAuthenticate(keyIndex, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw); + res = CIPURSEMutalAuthenticate(keyindex, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000 || len != 16) { if (sw == 0x6988) { - if (verbose) - PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key."); + if (verbose) { + PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ). Wrong key"); + } } else if (sw == 0x6A88) { - if (verbose) - PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key number."); + if (verbose) { + PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ). Wrong key number"); + } } else { - if (verbose) - PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Card returns 0x%04x.", sw); + if (verbose) { + PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ). Card returns 0x%04x", sw); + } } CipurseCClearContext(&cipurseContext); @@ -192,15 +212,17 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) { } if (CipurseCCheckCT(&cpc, buf)) { - if (verbose) - PrintAndLogEx(INFO, "Authentication " _GREEN_("OK")); + if (verbose) { + PrintAndLogEx(SUCCESS, "Authentication ( " _GREEN_("ok") " )"); + } CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed); memcpy(&cipurseContext, &cpc, sizeof(CipurseContext)); return true; } else { - if (verbose) - PrintAndLogEx(ERR, "Authentication " _RED_("ERROR") " card returned wrong CT"); + if (verbose) { + PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ) card returned wrong CT"); + } CipurseCClearContext(&cipurseContext); return false; @@ -212,46 +234,47 @@ void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, Cipurs } static void CIPURSEPrintPersoMode(uint8_t data) { - if (data & 0x01) - PrintAndLogEx(INFO, "Perso: filesystem"); - if (data & 0x02) - PrintAndLogEx(INFO, "Perso: EMV"); - if (data & 0x04) - PrintAndLogEx(INFO, "Perso: transaction supported"); - + if ((data & 0x01) == 0x01) + PrintAndLogEx(INFO, "Perso... " _YELLOW_("filesystem")); + if ((data & 0x02) == 0x02) + PrintAndLogEx(INFO, "Perso... " _YELLOW_("EMV")); + if ((data & 0x04) == 0x04) + PrintAndLogEx(INFO, "Perso... " _YELLOW_("transaction supported")); } - + +// 2021 iceman: what is the description text of profile L,S,T ? static void CIPURSEPrintProfileInfo(uint8_t data) { - if (data & 0x01) - PrintAndLogEx(INFO, "Profile: L"); - if (data & 0x02) - PrintAndLogEx(INFO, "Profile: S"); - if (data & 0x04) - PrintAndLogEx(INFO, "Profile: T"); + if ((data & 0x01) == 0x01) + PrintAndLogEx(INFO, "Profile... L"); + if ((data & 0x02) == 0x02) + PrintAndLogEx(INFO, "Profile... S"); + if ((data & 0x04) == 0x04) + PrintAndLogEx(INFO, "Profile... T"); } static void CIPURSEPrintManufacturerInfo(uint8_t data) { - if (data == 0) - PrintAndLogEx(INFO, "Manufacturer: n/a"); - else - PrintAndLogEx(INFO, "Manufacturer: %s", getTagInfo(data)); // getTagInfo from cmfhf14a.h + if (data == 0) + PrintAndLogEx(INFO, "Manufacturer... n/a"); + else + PrintAndLogEx(INFO, "Manufacturer... %s", getTagInfo(data)); // getTagInfo from cmfhf14a.h } void CIPURSEPrintInfoFile(uint8_t *data, size_t len) { if (len < 2) { - PrintAndLogEx(ERR, "Info file length " _RED_("ERROR")); + PrintAndLogEx(FAILED, "Info file length too short"); return; } - PrintAndLogEx(INFO, "------------ INFO ------------"); - PrintAndLogEx(INFO, "CIPURSE version %d revision %d", data[0], data[1]); - + PrintAndLogEx(INFO, "--- " _CYAN_("CIPURSE Information") "---------------------"); + PrintAndLogEx(INFO, "version.... " _YELLOW_("%d"), data[0]); + PrintAndLogEx(INFO, "revision... " _YELLOW_("%d"), data[1]); + if (len >= 3) CIPURSEPrintPersoMode(data[2]); - + if (len >= 4) CIPURSEPrintProfileInfo(data[3]); - + if (len >= 9) CIPURSEPrintManufacturerInfo(data[8]); } @@ -278,101 +301,111 @@ static void CIPURSEPrintFileDescriptor(uint8_t desc) { } static void CIPURSEPrintKeyAttrib(uint8_t *attr) { - PrintAndLogEx(INFO, "-------- KEY ATTRIBUTES --------"); - PrintAndLogEx(INFO, "Additional info: 0x%02x", attr[0]); - PrintAndLogEx(INFO, "Key length: %d", attr[1]); - PrintAndLogEx(INFO, "Algorithm ID: 0x%02x", attr[2]); - PrintAndLogEx(INFO, "Security attr: 0x%02x", attr[3]); - PrintAndLogEx(INFO, "KVV: 0x%02x%02x%02x", attr[4], attr[5], attr[6]); - PrintAndLogEx(INFO, "-------------------------------"); + PrintAndLogEx(INFO, "--- " _CYAN_("Key Attributes") "---------------------"); + PrintAndLogEx(INFO, "Additional info... 0x%02x", attr[0]); + PrintAndLogEx(INFO, "Key length........ %d", attr[1]); + PrintAndLogEx(INFO, "Algorithm ID...... 0x%02x", attr[2]); + PrintAndLogEx(INFO, "Security attr..... 0x%02x", attr[3]); + PrintAndLogEx(INFO, "KVV............... 0x%02x%02x%02x", attr[4], attr[5], attr[6]); + PrintAndLogEx(NORMAL, ""); } -void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len) { +void CIPURSEPrintFileAttr(uint8_t *attr, size_t len) { if (len < 7) { - PrintAndLogEx(ERR, "Attributes length " _RED_("ERROR")); + PrintAndLogEx(FAILED, "Attributes length too short"); return; } - PrintAndLogEx(INFO, "--------- FILE ATTRIBUTES ---------"); - if (fileAttr[0] == 0x38) { - PrintAndLogEx(INFO, "Type: MF, ADF"); - if (fileAttr[1] == 0x00) { - PrintAndLogEx(INFO, "Type: MF"); + PrintAndLogEx(INFO, "--- " _CYAN_("File Attributes") "---------------------"); + if (attr[0] == 0x38) { + PrintAndLogEx(INFO, "Type... MF, ADF"); + + if (attr[1] == 0x00) { + PrintAndLogEx(INFO, "Type... MF"); } else { - if ((fileAttr[1] & 0xe0) == 0x00) - PrintAndLogEx(INFO, "Type: Unknown"); - if ((fileAttr[1] & 0xe0) == 0x20) - PrintAndLogEx(INFO, "Type: CIPURSE L"); - if ((fileAttr[1] & 0xe0) == 0x40) - PrintAndLogEx(INFO, "Type: CIPURSE S"); - if ((fileAttr[1] & 0xe0) == 0x60) - PrintAndLogEx(INFO, "Type: CIPURSE T"); - if ((fileAttr[1] & 0x02) == 0x00) + if ((attr[1] & 0xe0) == 0x00) + PrintAndLogEx(INFO, "Type... Unknown"); + + if ((attr[1] & 0xe0) == 0x20) + PrintAndLogEx(INFO, "Type... CIPURSE L"); + + if ((attr[1] & 0xe0) == 0x40) + PrintAndLogEx(INFO, "Type... CIPURSE S"); + + if ((attr[1] & 0xe0) == 0x60) + PrintAndLogEx(INFO, "Type... CIPURSE T"); + + if ((attr[1] & 0x02) == 0x00) PrintAndLogEx(INFO, "Autoselect on PxSE select OFF"); else PrintAndLogEx(INFO, "Autoselect on PxSE select ON"); - if ((fileAttr[1] & 0x01) == 0x00) + + if ((attr[1] & 0x01) == 0x00) PrintAndLogEx(INFO, "PxSE select returns FCPTemplate OFF"); else PrintAndLogEx(INFO, "PxSE select returns FCPTemplate ON"); } - PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]); + PrintAndLogEx(INFO, "File ID................... 0x%02x%02x", attr[2], attr[3]); + PrintAndLogEx(INFO, "Maximum # custom EFs...... %d", attr[4]); + PrintAndLogEx(INFO, "Maximum # EFs with SFID... %d", attr[5]); - PrintAndLogEx(INFO, "Maximum number of custom EFs: %d", fileAttr[4]); - PrintAndLogEx(INFO, "Maximum number of EFs with SFID: %d", fileAttr[5]); - uint8_t keyNum = fileAttr[6]; - PrintAndLogEx(INFO, "Keys assigned: %d", keyNum); + uint8_t keynum = attr[6]; + PrintAndLogEx(INFO, "Keys assigned... %d", keynum); if (len >= 9) { - PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]); + PrintAndLogEx(INFO, "SMR entries... %02x%02x", attr[7], attr[8]); } - if (len >= 10 + keyNum + 1) { - PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], keyNum + 1)); + if (len >= 10 + keynum + 1) { + PrintAndLogEx(INFO, "ART... %s", sprint_hex(&attr[9], keynum + 1)); } - if (len >= 11 + keyNum + 1 + keyNum * 7) { - for (int i = 0; i < keyNum; i++) { - PrintAndLogEx(INFO, "Key %d Attributes: %s", i, sprint_hex(&fileAttr[11 + keyNum + 1 + i * 7], 7)); - CIPURSEPrintKeyAttrib(&fileAttr[11 + keyNum + 1 + i * 7]); + if (len >= 11 + keynum + 1 + keynum * 7) { + for (int i = 0; i < keynum; i++) { + PrintAndLogEx(INFO, "Key %d Attributes... %s", i, sprint_hex(&attr[11 + keynum + 1 + i * 7], 7)); + CIPURSEPrintKeyAttrib(&attr[11 + keynum + 1 + i * 7]); } } // MF - if (fileAttr[1] == 0x00) { - PrintAndLogEx(INFO, "Total memory size: %d", (fileAttr[len - 6] << 16) + (fileAttr[len - 1] << 5) + fileAttr[len - 4]); - PrintAndLogEx(INFO, "Free memory size: %d", (fileAttr[len - 3] << 16) + (fileAttr[len - 2] << 8) + fileAttr[len - 1]); + if (attr[1] == 0x00) { + PrintAndLogEx(INFO, "Total memory size... %d", (attr[len - 6] << 16) + (attr[len - 1] << 5) + attr[len - 4]); + PrintAndLogEx(INFO, "Free memory size.... %d", (attr[len - 3] << 16) + (attr[len - 2] << 8) + attr[len - 1]); } else { - int ptr = 11 + keyNum + 1 + keyNum * 7; - if (len > ptr) - PrintAndLogEx(INFO, "TLV file control: %s", sprint_hex(&fileAttr[ptr], len - ptr)); + int ptr = 11 + keynum + 1 + keynum * 7; + if (len > ptr) { + PrintAndLogEx(INFO, "TLV file control... %s", sprint_hex(&attr[ptr], len - ptr)); + } } } else { - PrintAndLogEx(INFO, "Type: EF"); - CIPURSEPrintFileDescriptor(fileAttr[0]); - if (fileAttr[1] == 0) - PrintAndLogEx(INFO, "SFI: not assigned"); + PrintAndLogEx(INFO, "Type... EF"); + CIPURSEPrintFileDescriptor(attr[0]); + + if (attr[1] == 0) + PrintAndLogEx(INFO, "SFI.... not assigned"); else - PrintAndLogEx(INFO, "SFI: 0x%02x", fileAttr[1]); + PrintAndLogEx(INFO, "SFI.... 0x%02x", attr[1]); - PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]); + PrintAndLogEx(INFO, "File ID... 0x%02x%02x", attr[2], attr[3]); - if (fileAttr[0] == 0x01 || fileAttr[0] == 0x11) - PrintAndLogEx(INFO, "File size: %d", (fileAttr[4] << 8) + fileAttr[5]); + if (attr[0] == 0x01 || attr[0] == 0x11) + PrintAndLogEx(INFO, "File size... %d", (attr[4] << 8) + attr[5]); else - PrintAndLogEx(INFO, "Record num: %d record size: %d", fileAttr[4], fileAttr[5]); + PrintAndLogEx(INFO, "Record num " _YELLOW_("%d") " record size " _YELLOW_("%d"), attr[4], attr[5]); - PrintAndLogEx(INFO, "Keys assigned: %d", fileAttr[6]); + PrintAndLogEx(INFO, "Keys assigned... %d", attr[6]); if (len >= 9) { - PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]); + PrintAndLogEx(INFO, "SMR entries... %02x%02x", attr[7], attr[8]); } if (len >= 10) { - PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], len - 9)); - if (fileAttr[6] + 1 != len - 9) + PrintAndLogEx(INFO, "ART... %s", sprint_hex(&attr[9], len - 9)); + + if (attr[6] + 1 != len - 9) { PrintAndLogEx(WARNING, "ART length is wrong"); + } } } diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h index 7ace29ea5..e16a931c2 100644 --- a/client/src/cipurse/cipursecore.h +++ b/client/src/cipurse/cipursecore.h @@ -11,10 +11,10 @@ #ifndef __CIPURSECORE_H__ #define __CIPURSECORE_H__ -#include "common.h" - #include -#include "iso7816/apduinfo.h" // sAPDU +#include +#include "common.h" +#include "iso7816/apduinfo.h" // sAPDU #include "cipurse/cipursecrypto.h" @@ -22,23 +22,23 @@ void CIPURSEPrintInfoFile(uint8_t *data, size_t len); -int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); +int CIPURSESelect(bool activate_field, bool leave_field_on, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); -int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); -int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); +int CIPURSEChallenge(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); +int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); -int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); -int CIPURSEDeleteFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); +int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); +int CIPURSEDeleteFile(uint16_t fileid, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); -int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) ; -int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); -int CIPURSEReadFileAttributes(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); -int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); -int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); +int CIPURSESelectMFFile(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) ; +int CIPURSESelectFile(uint16_t fileid, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); +int CIPURSEReadFileAttributes(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); +int CIPURSEReadBinary(uint16_t offset, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); +int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); -bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose); +bool CIPURSEChannelAuthenticate(uint8_t keyindex, uint8_t *key, bool verbose); void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp); -void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len); +void CIPURSEPrintFileAttr(uint8_t *attr, size_t len); #endif /* __CIPURSECORE_H__ */ diff --git a/client/src/cipurse/cipursecrypto.c b/client/src/cipurse/cipursecrypto.c index e68adc14f..cbf084ef4 100644 --- a/client/src/cipurse/cipursecrypto.c +++ b/client/src/cipurse/cipursecrypto.c @@ -40,11 +40,6 @@ uint8_t CipurseCSecurityLevelEnc(CipurseChannelSecurityLevel lvl) { } } -static void bin_xor(uint8_t *d1, uint8_t *d2, size_t len) { - for (size_t i = 0; i < len; i++) - d1[i] = d1[i] ^ d2[i]; -} - static void bin_ext(uint8_t *dst, size_t dstlen, uint8_t *src, size_t srclen) { if (srclen > dstlen) memcpy(dst, &src[srclen - dstlen], dstlen); @@ -218,24 +213,6 @@ bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT) { return (memcmp(CT, ctx->CT, CIPURSE_AES_KEY_LENGTH) == 0); } -void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen) { - *ddatalen = sdatalen + 1; - *ddatalen += blocklen - *ddatalen % blocklen; - memset(ddata, 0, *ddatalen); - memcpy(ddata, sdata, sdatalen); - ddata[sdatalen] = ISO9797_M2_PAD_BYTE; -} - -size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen) { - for (int i = datalen; i > 0; i--) { - if (data[i - 1] == 0x80) - return i - 1; - if (data[i - 1] != 0x00) - return 0; - } - return 0; -} - static uint16_t CipurseCComputeMICCRC(uint8_t *data, size_t len) { uint16_t initCRC = 0x6363; for (size_t i = 0; i < len; i++) { @@ -506,6 +483,11 @@ void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdat CipurseCChannelDecrypt(ctx, srcdata, srcdatalen, buf, &buflen); //PrintAndLogEx(INFO, "data plain[%d]: %s", buflen, sprint_hex(buf, buflen)); + if (buflen == 0) { + PrintAndLogEx(ERR, "APDU can't decode crypto stream"); + break; + } + micdatalen = buflen - 2 - CIPURSE_MIC_LENGTH; memcpy(micdata, buf, buflen); memcpy(&micdata[micdatalen], &buf[buflen - 2], 2); diff --git a/client/src/cipurse/cipursecrypto.h b/client/src/cipurse/cipursecrypto.h index 7790eb600..aba244830 100644 --- a/client/src/cipurse/cipursecrypto.h +++ b/client/src/cipurse/cipursecrypto.h @@ -21,7 +21,6 @@ #define CIPURSE_MAC_LENGTH 8 #define CIPURSE_MIC_LENGTH 4 #define CIPURSE_POLY 0x35b088cce172UL -#define ISO9797_M2_PAD_BYTE 0x80 #define member_size(type, member) sizeof(((type *)0)->member) @@ -66,9 +65,6 @@ bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT); void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp); bool isCipurseCChannelSecuritySet(CipurseContext *ctx); -void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen); -size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen); - void CipurseCGenerateMAC(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac); void CipurseCCalcMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac); bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *mac); diff --git a/client/src/cipurse/cipursetest.c b/client/src/cipurse/cipursetest.c new file mode 100644 index 000000000..5de6be161 --- /dev/null +++ b/client/src/cipurse/cipursetest.c @@ -0,0 +1,376 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2021 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// tests for crypto +//----------------------------------------------------------------------------- + +#include "cipursetest.h" + +#include +#include // memcpy memset +#include "fileutils.h" + +#include "crypto/libpcrypto.h" +#include "cipurse/cipursecrypto.h" +#include "cipurse/cipursecore.h" + +uint8_t Key[] = CIPURSE_DEFAULT_KEY; +uint8_t KeyKvv[CIPURSE_KVV_LENGTH] = {0x5f, 0xd6, 0x7b, 0xcb}; + +uint8_t TestRandom[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22}; + +uint8_t TestData[16] = {0x11, 0x22, 0x33, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +uint8_t TestDataPadded[16] = {0x11, 0x22, 0x33, 0x44, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +static bool TestKVV(void) { + uint8_t kvv[CIPURSE_KVV_LENGTH] = {0}; + CipurseCGetKVV(Key, kvv); + + bool res = memcmp(KeyKvv, kvv, CIPURSE_KVV_LENGTH) == 0; + + if (res) + PrintAndLogEx(INFO, "kvv.............. " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "kvv.............. " _RED_("fail")); + + return res; +} + +static bool TestISO9797M2(void) { + uint8_t data[32] = {0}; + + size_t ddatalen = 0; + AddISO9797M2Padding(data, &ddatalen, TestData, 4, 16); + bool res = (ddatalen == 16); + res = res && (memcmp(data, TestDataPadded, ddatalen) == 0); + + res = res && (FindISO9797M2PaddingDataLen(data, ddatalen) == 4); + + if (res) + PrintAndLogEx(INFO, "ISO9797M2........ " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "ISO9797M2........ " _RED_("fail")); + + return res; +} + +static bool TestSMI(void) { + CipurseContext ctx = {0}; + CipurseCClearContext(&ctx); + + bool res = (isCipurseCChannelSecuritySet(&ctx) == false); + + CipurseCChannelSetSecurityLevels(&ctx, CPSPlain, CPSPlain); + res = res && (CipurseCGetSMI(&ctx, false) == 0x00); + res = res && (CipurseCGetSMI(&ctx, true) == 0x01); + + CipurseCChannelSetSecurityLevels(&ctx, CPSPlain, CPSMACed); + res = res && (CipurseCGetSMI(&ctx, false) == 0x04); + res = res && (CipurseCGetSMI(&ctx, true) == 0x05); + + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed); + res = res && (CipurseCGetSMI(&ctx, false) == 0x44); + res = res && (CipurseCGetSMI(&ctx, true) == 0x45); + + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSEncrypted); + res = res && (CipurseCGetSMI(&ctx, false) == 0x48); + res = res && (CipurseCGetSMI(&ctx, true) == 0x49); + + CipurseCChannelSetSecurityLevels(&ctx, CPSEncrypted, CPSEncrypted); + res = res && (CipurseCGetSMI(&ctx, false) == 0x88); + res = res && (CipurseCGetSMI(&ctx, true) == 0x89); + + if (res) + PrintAndLogEx(INFO, "SMI.............. " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "SMI.............. " _RED_("fail")); + + return res; +} + +static bool TestMIC(void) { + uint8_t mic[4] = {0}; + + CipurseCGenerateMIC(TestData, 4, mic); + uint8_t valid_mic4[4] = {0xD4, 0x71, 0xA7, 0x73}; + bool res = (memcmp(mic, valid_mic4, 4) == 0); + + res = res && (CipurseCCheckMIC(TestData, 4, mic)); + + CipurseCGenerateMIC(TestData, 6, mic); + uint8_t valid_mic6[4] = {0xAA, 0x90, 0xFC, 0x5A}; + res = res && (memcmp(mic, valid_mic6, 4) == 0); + + res = res && (CipurseCCheckMIC(TestData, 6, mic)); + + if (res) + PrintAndLogEx(INFO, "MIC.............. " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "MIC.............. " _RED_("fail")); + + return res; +} + + +static bool TestAuth(void) { + CipurseContext ctx = {0}; + CipurseCClearContext(&ctx); + + bool res = (isCipurseCChannelSecuritySet(&ctx) == false); + + CipurseCSetKey(&ctx, 1, Key); + res = res && (memcmp(ctx.key, Key, 16) == 0); + res = res && (ctx.keyId == 1); + + CipurseCSetRandomFromPICC(&ctx, TestRandom); + res = res && (memcmp(ctx.RP, TestRandom, 16) == 0); + res = res && (memcmp(ctx.rP, &TestRandom[16], 6) == 0); + + uint8_t hrandom[] = {0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}; + CipurseCSetRandomHost(&ctx); + res = res && (memcmp(ctx.RT, hrandom, 16) == 0); + res = res && (memcmp(ctx.rT, &hrandom[16], 6) == 0); + + uint8_t authparams[16 + 16 + 6] = {0}; + CipurseCAuthenticateHost(&ctx, authparams); + uint8_t aparamstest[] = {0x12, 0xAA, 0x79, 0xA9, 0x03, 0xC5, 0xB4, 0x6A, 0x27, 0x1B, 0x13, 0xAE, 0x02, 0x50, 0x1C, 0x99, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + res = res && (memcmp(authparams, aparamstest, sizeof(authparams)) == 0); + + uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41}; + res = res && CipurseCCheckCT(&ctx, ct); + + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed); + res = res && (isCipurseCChannelSecuritySet(&ctx) == true); + + uint8_t framekey[] = {0xCF, 0x6F, 0x3A, 0x47, 0xFC, 0xAC, 0x8D, 0x38, 0x25, 0x75, 0x8B, 0xFC, 0x8B, 0x61, 0x68, 0xF3}; + res = res && (memcmp(ctx.frameKey, framekey, sizeof(framekey)) == 0); + + if (res) + PrintAndLogEx(INFO, "Auth............. " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "Auth............. " _RED_("fail")); + + return res; +} + +static bool TestMAC(void) { + CipurseContext ctx = {0}; + + // authentication + CipurseCClearContext(&ctx); + CipurseCSetKey(&ctx, 1, Key); + CipurseCSetRandomFromPICC(&ctx, TestRandom); + uint8_t authparams[16 + 16 + 6] = {0}; + CipurseCAuthenticateHost(&ctx, authparams); + uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41}; + bool res = CipurseCCheckCT(&ctx, ct); + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed); + res = res && (isCipurseCChannelSecuritySet(&ctx) == true); + + // check MAC + uint8_t mac[8] = {0}; + + CipurseCGenerateMAC(&ctx, TestData, 4, mac); + uint8_t testmac1[8] = {0xAB, 0x5C, 0x86, 0x18, 0x7F, 0x73, 0xEC, 0x4E}; + res = res && (memcmp(mac, testmac1, 8) == 0); + + uint8_t framekey1[] = {0x7D, 0x6F, 0x31, 0x40, 0xC8, 0x47, 0xED, 0x3F, 0x0A, 0x21, 0xE6, 0xFB, 0xC7, 0xDB, 0x27, 0xB0}; + res = res && (memcmp(ctx.frameKey, framekey1, sizeof(framekey1)) == 0); + + CipurseCCalcMACPadded(&ctx, TestData, 4, mac); + uint8_t testmac2[8] = {0x9F, 0xE9, 0x54, 0xBF, 0xFC, 0xA0, 0x7D, 0x75}; + res = res && (memcmp(mac, testmac2, 8) == 0); + + uint8_t framekey2[] = {0x1E, 0xD4, 0xB6, 0x87, 0x85, 0x93, 0x5B, 0xAF, 0xA9, 0xF2, 0xF0, 0x8F, 0xA9, 0xF0, 0xA5, 0xFB}; + res = res && (memcmp(ctx.frameKey, framekey2, sizeof(framekey2)) == 0); + + CipurseCCalcMACPadded(&ctx, TestData, 4, mac); + uint8_t testmac3[8] = {0x15, 0x6F, 0x08, 0x5C, 0x0F, 0x80, 0xE7, 0x07}; + res = res && (memcmp(mac, testmac3, 8) == 0); + + uint8_t framekey3[] = {0x0C, 0x42, 0x93, 0x73, 0x88, 0x8F, 0x63, 0xB3, 0x10, 0x8E, 0xDF, 0xDB, 0xC1, 0x20, 0x63, 0x4C}; + res = res && (memcmp(ctx.frameKey, framekey3, sizeof(framekey3)) == 0); + + uint8_t testmac4[8] = {0x0E, 0xF0, 0x70, 0xA6, 0xA1, 0x15, 0x9A, 0xB6}; + res = res && CipurseCCheckMACPadded(&ctx, TestData, 4, testmac4); + + uint8_t framekey4[] = {0xA0, 0x65, 0x1A, 0x62, 0x56, 0x5D, 0xD7, 0xC9, 0x32, 0xAE, 0x1D, 0xE0, 0xCF, 0x8D, 0xC1, 0xB9}; + res = res && (memcmp(ctx.frameKey, framekey4, sizeof(framekey4)) == 0); + + if (res) + PrintAndLogEx(INFO, "channel MAC...... " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "channel MAC...... " _RED_("fail")); + + return res; +} + +static bool TestEncDec(void) { + CipurseContext ctx = {0}; + + // authentication + CipurseCClearContext(&ctx); + CipurseCSetKey(&ctx, 1, Key); + CipurseCSetRandomFromPICC(&ctx, TestRandom); + uint8_t authparams[16 + 16 + 6] = {0}; + CipurseCAuthenticateHost(&ctx, authparams); + uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41}; + bool res = CipurseCCheckCT(&ctx, ct); + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed); + res = res && (isCipurseCChannelSecuritySet(&ctx) == true); + + // check Encode-Decode + uint8_t dstdata[32] = {0}; + size_t dstdatalen = 0; + + CipurseCEncryptDecrypt(&ctx, TestData, 16, dstdata, true); + uint8_t tested1[16] = {0x5F, 0x01, 0x18, 0x79, 0xE0, 0x57, 0xA7, 0xE5, 0x34, 0x39, 0x6E, 0x32, 0x62, 0xF2, 0x71, 0x27}; + res = res && (memcmp(dstdata, tested1, 16) == 0); + + uint8_t tested2[16] = {0xA6, 0x22, 0xB5, 0xCF, 0xE8, 0x6E, 0x67, 0xF4, 0xAA, 0x88, 0xB1, 0x19, 0x87, 0xCF, 0xC9, 0xD2}; + CipurseCEncryptDecrypt(&ctx, tested2, 16, dstdata, false); + res = res && (memcmp(dstdata, TestData, 16) == 0); + + CipurseCChannelEncrypt(&ctx, TestData, 16, dstdata, &dstdatalen); + uint8_t tested3[32] = {0x1E, 0x0C, 0xD1, 0xF5, 0x8E, 0x0B, 0xAE, 0xF0, 0x06, 0xC6, 0xED, 0x73, 0x3F, 0x8A, 0x87, 0xCF, + 0x36, 0xCC, 0xF2, 0xF4, 0x7D, 0x33, 0x50, 0xF1, 0x8E, 0xFF, 0xD1, 0x7D, 0x42, 0x88, 0xD5, 0xEE + }; + res = res && (dstdatalen == 32); + res = res && (memcmp(dstdata, tested3, 32) == 0); + + uint8_t tested4[32] = {0xC0, 0x42, 0xDB, 0xD9, 0x53, 0xFF, 0x01, 0xE5, 0xCC, 0x49, 0x8C, 0x9C, 0xDA, 0x60, 0x73, 0xA7, + 0xE1, 0xEB, 0x14, 0x69, 0xF6, 0x39, 0xF3, 0xE1, 0x07, 0x03, 0x32, 0xF4, 0x27, 0xF9, 0x48, 0x3D + }; + CipurseCChannelDecrypt(&ctx, tested4, 32, dstdata, &dstdatalen); + res = res && (dstdatalen == 16); + res = res && (memcmp(dstdata, TestData, 16) == 0); + + if (res) + PrintAndLogEx(INFO, "channel EncDec... " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "channel EncDec... " _RED_("fail")); + + return res; +} + +//void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu, uint8_t *dstdatabuf, bool includeLe, uint8_t Le); +//void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw); +static bool TestAPDU(void) { + CipurseContext ctx = {0}; + + // authentication + CipurseCClearContext(&ctx); + CipurseCSetKey(&ctx, 1, Key); + CipurseCSetRandomFromPICC(&ctx, TestRandom); + uint8_t authparams[16 + 16 + 6] = {0}; + CipurseCAuthenticateHost(&ctx, authparams); + uint8_t ct[] = {0xBE, 0x10, 0x6B, 0xB9, 0xAD, 0x84, 0xBC, 0xE1, 0x9F, 0xAE, 0x0C, 0x62, 0xCC, 0xC7, 0x0D, 0x41}; + bool res = CipurseCCheckCT(&ctx, ct); + CipurseCChannelSetSecurityLevels(&ctx, CPSMACed, CPSMACed); + res = res && (isCipurseCChannelSecuritySet(&ctx) == true); + + // check APDU formatting + sAPDU srcAPDU = {0}; + sAPDU dstAPDU = {0}; + uint8_t dstdata[256] = {0}; + size_t dstdatalen = 0; + + // MACED APDU + srcAPDU.CLA = 0x00; + srcAPDU.INS = 0x55; + srcAPDU.P1 = 0x11; + srcAPDU.P2 = 0x22; + srcAPDU.data = TestData; + srcAPDU.Lc = 5; + + CipurseCAPDUReqEncode(&ctx, &srcAPDU, &dstAPDU, dstdata, true, 0x88); + uint8_t test1[] = {0x45, 0x11, 0x22, 0x33, 0x44, 0x00, 0x88, 0x79, 0x2B, 0xB7, 0xDD, 0xD1, 0x69, 0xA6, 0x66}; + res = res && ((srcAPDU.CLA | 0x04) == dstAPDU.CLA); + res = res && (srcAPDU.INS == dstAPDU.INS); + res = res && (srcAPDU.P1 == dstAPDU.P1); + res = res && (srcAPDU.P2 == dstAPDU.P2); + res = res && (dstAPDU.Lc == sizeof(test1)); + res = res && (memcmp(dstdata, test1, sizeof(test1)) == 0); + + uint16_t sw = 0; + uint8_t test2[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x9D, 0x80, 0xE7, 0xE3, 0x34, 0xE9, 0x97, 0x82, 0xdd, 0xee}; + CipurseCAPDURespDecode(&ctx, test2, sizeof(test2), dstdata, &dstdatalen, &sw); + res = res && (dstdatalen == 6); + res = res && (memcmp(test2, dstdata, dstdatalen) == 0); + res = res && (sw == 0xddee); + + // Plain APDU + CipurseCChannelSetSecurityLevels(&ctx, CPSPlain, CPSPlain); + CipurseCAPDUReqEncode(&ctx, &srcAPDU, &dstAPDU, dstdata, true, 0x55); + uint8_t test3[] = {0x01, 0x11, 0x22, 0x33, 0x44, 0x00, 0x55}; + res = res && ((srcAPDU.CLA | 0x04) == dstAPDU.CLA); + res = res && (srcAPDU.INS == dstAPDU.INS); + res = res && (srcAPDU.P1 == dstAPDU.P1); + res = res && (srcAPDU.P2 == dstAPDU.P2); + res = res && (dstAPDU.Lc == sizeof(test3)); + res = res && (memcmp(dstdata, test3, sizeof(test3)) == 0); + + uint8_t test4[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0xcc, 0xdd}; + CipurseCAPDURespDecode(&ctx, test4, sizeof(test4), dstdata, &dstdatalen, &sw); + res = res && (dstdatalen == 6); + res = res && (memcmp(test4, dstdata, dstdatalen) == 0); + res = res && (sw == 0xccdd); + + // Encrypted APDU + CipurseCChannelSetSecurityLevels(&ctx, CPSEncrypted, CPSEncrypted); + CipurseCAPDUReqEncode(&ctx, &srcAPDU, &dstAPDU, dstdata, true, 0x55); + uint8_t test5[] = {0x89, 0x7D, 0xED, 0x0D, 0x04, 0x8E, 0xE1, 0x99, 0x08, 0x70, 0x56, 0x7C, 0xEE, 0x67, 0xB3, 0x33, 0x6F, 0x00}; + res = res && ((srcAPDU.CLA | 0x04) == dstAPDU.CLA); + res = res && (srcAPDU.INS == dstAPDU.INS); + res = res && (srcAPDU.P1 == dstAPDU.P1); + res = res && (srcAPDU.P2 == dstAPDU.P2); + res = res && (dstAPDU.Lc == sizeof(test5)); + res = res && (memcmp(dstdata, test5, sizeof(test5)) == 0); + + uint8_t test6[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x7E, 0x4B, 0xA0, 0xB7, 0xcc, 0xdd}; + //CipurseCChannelEncrypt(&ctx, test6, sizeof(test6), dstdata, &dstdatalen); + //PrintAndLogEx(INFO, "dstdata[%d]: %s", dstdatalen, sprint_hex(dstdata, dstdatalen)); + + uint8_t test7[] = {0x07, 0xEF, 0x16, 0x91, 0xE7, 0x0F, 0xB5, 0x10, 0x63, 0xCE, 0x66, 0xDB, 0x3B, 0xC6, 0xD4, 0xE0, 0x90, 0x00}; + CipurseCAPDURespDecode(&ctx, test7, sizeof(test7), dstdata, &dstdatalen, &sw); + res = res && (dstdatalen == 8); + res = res && (memcmp(test6, dstdata, dstdatalen) == 0); + res = res && (sw == 0xccdd); + + if (res) + PrintAndLogEx(INFO, "apdu............. " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "apdu............. " _RED_("fail")); + + return res; +} + +bool CIPURSETest(bool verbose) { + bool res = true; + + PrintAndLogEx(INFO, "------ " _CYAN_("CIPURSE Tests") " ------"); + + res = res && TestKVV(); + res = res && TestISO9797M2(); + res = res && TestSMI(); + res = res && TestMIC(); + res = res && TestAuth(); + res = res && TestMAC(); + res = res && TestEncDec(); + res = res && TestAPDU(); + + PrintAndLogEx(INFO, "---------------------------"); + if (res) + PrintAndLogEx(SUCCESS, " Tests [ %s ]", _GREEN_("ok")); + else + PrintAndLogEx(FAILED, " Tests [ %s ]", _RED_("fail")); + + PrintAndLogEx(NORMAL, ""); + return res; +} diff --git a/client/src/cipurse/cipursetest.h b/client/src/cipurse/cipursetest.h new file mode 100644 index 000000000..1f69a3abc --- /dev/null +++ b/client/src/cipurse/cipursetest.h @@ -0,0 +1,20 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2021 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// tests for crypto +//----------------------------------------------------------------------------- + +#ifndef __CIPURSETEST_H__ +#define __CIPURSETEST_H__ + +#include +#include "common.h" +#include "cipurse/cipursecrypto.h" + +bool CIPURSETest(bool verbose); + +#endif /* __CIPURSETEST_H__ */ diff --git a/client/src/cmddata.c b/client/src/cmddata.c index 2a715325c..4371a0187 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -2702,7 +2702,7 @@ typedef struct { static int print_modulation(lf_modulation_t b) { PrintAndLogEx(INFO, " Modulation........ " _GREEN_("%s"), GetSelectedModulationStr(b.modulation)); PrintAndLogEx(INFO, " Bit clock......... " _GREEN_("RF/%d"), b.bitrate); - PrintAndLogEx(INFO, " Approx baudrate... " _GREEN_("%.f") "bauds", (125000 / (float)b.bitrate)); + PrintAndLogEx(INFO, " Approx baudrate... " _GREEN_("%.f") " baud", (125000 / (float)b.bitrate)); switch (b.modulation) { case DEMOD_PSK1: case DEMOD_PSK2: diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index 6fd9faaac..83b551e33 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -59,9 +59,13 @@ int CmdHFSearch(const char *Cmd) { ); void *argtable[] = { arg_param_begin, + arg_lit0("v", "verbose", "verbose output"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool verbose = arg_get_lit(ctx, 1); + CLIParserFree(ctx); int res = PM3_ESOFT; @@ -87,9 +91,13 @@ int CmdHFSearch(const char *Cmd) { PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for ISO14443-A tag..."); if (IfPm3Iso14443a()) { - if (infoHF14A(false, false, false) > 0) { + int sel_state = infoHF14A(false, false, false); + if (sel_state > 0) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO 14443-A tag") " found\n"); res = PM3_SUCCESS; + + if (sel_state == 1) + infoHF14A4Applications(verbose); } } @@ -129,15 +137,6 @@ int CmdHFSearch(const char *Cmd) { } } - PROMPT_CLEARLINE; - PrintAndLogEx(INPLACE, " Searching for Cipurse tag..."); - if (IfPm3Iso14443a()) { - if (CheckCardCipurse()) { - PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Cipurse tag") " found\n"); - res = PM3_SUCCESS; - } - } - // 14b is the longest test PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for ISO14443-B tag..."); diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 6396323ca..c19941ed6 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -169,8 +169,19 @@ const char *getTagInfo(uint8_t uid) { return manufactureMapping[ARRAYLEN(manufactureMapping) - 1].desc; } +static const hintAIDListT hintAIDList[] = { + // AID, AID len, name, hint - how to use + { "\xA0\x00\x00\x06\x47\x2F\x00\x01", 8, "FIDO", "hf fido" }, + { "\xA0\x00\x00\x03\x08\x00\x00\x10\x00\x01\x00", 11, "PIV", "" }, + { "\xD2\x76\x00\x01\x24\x01", 8, "OpenPGP", "" }, + { "\x31\x50\x41\x59\x2E\x53\x59\x53\x2E\x44\x44\x46\x30\x31", 14, "EMV (pse)", "emv" }, + { "\x32\x50\x41\x59\x2E\x53\x59\x53\x2E\x44\x44\x46\x30\x31", 14, "EMV (ppse)", "emv" }, + { "\x41\x44\x20\x46\x31", 5, "CIPURSE", "hf cipurse" }, + { "\xd2\x76\x00\x00\x85\x01\x00", 7, "desfire", "hf mfdes" }, +}; + // iso14a apdu input frame length -static uint16_t frameLength = 0; +static uint16_t g_frame_len = 0; uint16_t atsFSC[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; static int CmdHF14AList(const char *Cmd) { @@ -846,31 +857,35 @@ int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leav return 0; } -int SelectCard14443A_4(bool disconnect, iso14a_card_select_t *card) { - PacketResponseNG resp; +int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card) { - frameLength = 0; + // global vars should be prefixed with g_ + g_frame_len = 0; - if (card) + if (card) { memset(card, 0, sizeof(iso14a_card_select_t)); + } DropField(); // Anticollision + SELECT card + PacketResponseNG resp; SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(ERR, "Proxmark3 connection timeout"); + PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } // check result if (resp.oldarg[0] == 0) { - PrintAndLogEx(ERR, "No card in field"); + if (verbose) { + PrintAndLogEx(FAILED, "No card in field"); + } return PM3_ECARDEXCHANGE; } if (resp.oldarg[0] != 1 && resp.oldarg[0] != 2) { - PrintAndLogEx(ERR, "Card not in iso14443-4, res=%" PRId64 ".", resp.oldarg[0]); + PrintAndLogEx(WARNING, "Card not in iso14443-4, res=%" PRId64 ".", resp.oldarg[0]); return PM3_ECARDEXCHANGE; } @@ -879,36 +894,44 @@ int SelectCard14443A_4(bool disconnect, iso14a_card_select_t *card) { uint8_t rats[] = { 0xE0, 0x80 }; // FSDI=8 (FSD=256), CID=0 SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_RAW | ISO14A_APPEND_CRC | ISO14A_NO_DISCONNECT, sizeof(rats), 0, rats, sizeof(rats)); if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { - PrintAndLogEx(ERR, "Proxmark3 connection timeout"); + PrintAndLogEx(WARNING, "Command execute timeout"); return PM3_ETIMEOUT; } if (resp.oldarg[0] == 0) { // ats_len - PrintAndLogEx(ERR, "Can't get ATS"); + if (verbose) { + PrintAndLogEx(FAILED, "Can't get ATS"); + } return PM3_ECARDEXCHANGE; } // get frame length from ATS in data field if (resp.oldarg[0] > 1) { uint8_t fsci = resp.data.asBytes[1] & 0x0f; - if (fsci < ARRAYLEN(atsFSC)) - frameLength = atsFSC[fsci]; + if (fsci < ARRAYLEN(atsFSC)) { + g_frame_len = atsFSC[fsci]; + } } } else { // get frame length from ATS in card data structure iso14a_card_select_t *vcard = (iso14a_card_select_t *) resp.data.asBytes; if (vcard->ats_len > 1) { uint8_t fsci = vcard->ats[1] & 0x0f; - if (fsci < ARRAYLEN(atsFSC)) - frameLength = atsFSC[fsci]; + if (fsci < ARRAYLEN(atsFSC)) { + g_frame_len = atsFSC[fsci]; + } } - if (card) + if (card) { memcpy(card, vcard, sizeof(iso14a_card_select_t)); + } } + SetISODEPState(ISODEP_NFCA); - if (disconnect) + + if (disconnect) { DropField(); + } return PM3_SUCCESS; } @@ -917,8 +940,8 @@ static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool *chainingout = false; if (activateField) { - // select with no disconnect and set frameLength - int selres = SelectCard14443A_4(false, NULL); + // select with no disconnect and set g_frame_len + int selres = SelectCard14443A_4(false, true, NULL); if (selres != PM3_SUCCESS) return selres; } @@ -954,7 +977,7 @@ static int CmdExchangeAPDU(bool chainingin, uint8_t *datain, int datainlen, bool } // I-block ACK - if ((res & 0xf2) == 0xa2) { + if ((res & 0xF2) == 0xA2) { *dataoutlen = 0; *chainingout = true; return PM3_SUCCESS; @@ -1004,13 +1027,14 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea // 3 byte here - 1b framing header, 2b crc16 if (APDUInFramingEnable && - ((frameLength && (datainlen > frameLength - 3)) || (datainlen > PM3_CMD_DATA_SIZE - 3))) { + ((g_frame_len && (datainlen > g_frame_len - 3)) || (datainlen > PM3_CMD_DATA_SIZE - 3))) { + int clen = 0; bool vActivateField = activateField; do { - int vlen = MIN(frameLength - 3, datainlen - clen); + int vlen = MIN(g_frame_len - 3, datainlen - clen); bool chainBlockNotLast = ((clen + vlen) < datainlen); *dataoutlen = 0; @@ -1034,17 +1058,19 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea clen += vlen; vActivateField = false; if (*dataoutlen) { - if (clen != datainlen) + if (clen != datainlen) { PrintAndLogEx(ERR, "APDU: I-block/R-block sequence error. Data len=%d, Sent=%d, Last packet len=%d", datainlen, clen, *dataoutlen); + } break; } } while (clen < datainlen); + } else { res = CmdExchangeAPDU(false, datain, datainlen, activateField, dataout, maxdataoutlen, dataoutlen, &chaining); if (res != PM3_SUCCESS) { - if (leaveSignalON == false) + if (leaveSignalON == false) { DropField(); - + } return res; } } @@ -1053,15 +1079,16 @@ int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool lea // I-block with chaining res = CmdExchangeAPDU(false, NULL, 0, false, &dataout[*dataoutlen], maxdataoutlen, dataoutlen, &chaining); if (res != PM3_SUCCESS) { - if (leaveSignalON == false) + if (leaveSignalON == false) { DropField(); - + } return 100; } } - if (!leaveSignalON) + if (leaveSignalON == false) { DropField(); + } return PM3_SUCCESS; } @@ -1780,6 +1807,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { 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 @@ -2070,6 +2099,8 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if ((card.sak & 0x20) == 0x20) { PrintAndLogEx(INFO, "--> SAK incorrectly claims that card supports RATS <--"); } + if (select_status == 1) + select_status = 2; } int isMagic = 0; @@ -2131,6 +2162,57 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { return select_status; } +int infoHF14A4Applications(bool verbose) { + bool cardFound[ARRAYLEN(hintAIDList)] = {0}; + bool ActivateField = true; + int found = 0; + for (int i = 0; i < ARRAYLEN(hintAIDList); i++) { + uint16_t sw = 0; + uint8_t result[1024] = {0}; + size_t resultlen = 0; + int res = Iso7816Select(CC_CONTACTLESS, ActivateField, true, (uint8_t *)hintAIDList[i].aid, hintAIDList[i].aid_length, result, sizeof(result), &resultlen, &sw); + ActivateField = false; + if (res) + break; + + if (sw == 0x9000 || sw == 0x6283 || sw == 0x6285) { + if (!found) { + if (verbose) + PrintAndLogEx(INFO, "----------------- " _CYAN_("Short AID search") " -----------------"); + } + found++; + + if (sw == 0x9000) { + if (verbose) + PrintAndLogEx(SUCCESS, "Application " _CYAN_("%s") " ( " _GREEN_("ok") " )", hintAIDList[i].desc); + cardFound[i] = true; + } else { + if (verbose) + PrintAndLogEx(WARNING, "Application " _CYAN_("%s") " ( " _RED_("blocked") " )", hintAIDList[i].desc); + } + } + } + + if (found) { + if (verbose) + PrintAndLogEx(INFO, "---------------------------------------------------"); + else + PrintAndLogEx(INFO, "Short AID search:"); + + if (found >= ARRAYLEN(hintAIDList) - 1) { + PrintAndLogEx(HINT, "Hint: card answers to all AID. It maybe the latest revision of plus/desfire/ultralight card."); + } else { + for (int i = 0; i < ARRAYLEN(hintAIDList); i++) { + if (cardFound[i] && strlen(hintAIDList[i].hint)) + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("%s") " commands", hintAIDList[i].hint); + } + } + } + + DropField(); + return found; +} + static uint16_t get_sw(uint8_t *d, uint8_t n) { if (n < 2) { return 0; diff --git a/client/src/cmdhf14a.h b/client/src/cmdhf14a.h index 874a073c5..18e9fffd7 100644 --- a/client/src/cmdhf14a.h +++ b/client/src/cmdhf14a.h @@ -22,6 +22,13 @@ typedef struct { const char *desc; } manufactureName; +typedef struct { + const char *aid; + const uint8_t aid_length; + const char *desc; + const char *hint; +} hintAIDListT; + int CmdHF14A(const char *Cmd); int CmdHF14ASniff(const char *Cmd); // used by hf topaz sniff int CmdHF14ASim(const char *Cmd); // used by hf mfu sim @@ -30,10 +37,11 @@ int CmdHF14ANdefRead(const char *Cmd); int hf14a_getconfig(hf14a_config *config); int hf14a_setconfig(hf14a_config *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); int Hf14443_4aGetCardData(iso14a_card_select_t *card); int ExchangeAPDU14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen); int ExchangeRAW14a(uint8_t *datain, int datainlen, bool activateField, bool leaveSignalON, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool silentMode); -int SelectCard14443A_4(bool disconnect, iso14a_card_select_t *card); +int SelectCard14443A_4(bool disconnect, bool verbose, iso14a_card_select_t *card); #endif diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c index 6ce7c8895..02010e454 100644 --- a/client/src/cmdhfcipurse.c +++ b/client/src/cmdhfcipurse.c @@ -25,12 +25,17 @@ #include "cmdhfcipurse.h" #include "cipurse/cipursecore.h" #include "cipurse/cipursecrypto.h" +#include "cipurse/cipursetest.h" #include "ui.h" #include "cmdhf14a.h" #include "cmdtrace.h" #include "util.h" #include "fileutils.h" // laodFileJSONroot +static uint8_t defaultKeyId = 1; +static uint8_t defaultKey[CIPURSE_AES_KEY_LENGTH] = CIPURSE_DEFAULT_KEY; +static uint16_t defaultFileId = 0x2ff7; + static int CmdHelp(const char *Cmd); static int CmdHFCipurseInfo(const char *Cmd) { @@ -65,15 +70,15 @@ static int CmdHFCipurseInfo(const char *Cmd) { if (sw != 0x9000) { if (sw) - PrintAndLogEx(INFO, "Not a CIPURSE card! APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + PrintAndLogEx(INFO, "Not a CIPURSE card. APDU response: %04x - %s", sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); else - PrintAndLogEx(ERR, "APDU exchange error. Card returns 0x0000."); + PrintAndLogEx(ERR, "APDU exchange error. Card returns 0x0000"); DropField(); return PM3_SUCCESS; } - PrintAndLogEx(INFO, "Cipurse card: " _GREEN_("OK")); + PrintAndLogEx(INFO, "Cipurse card ( " _GREEN_("ok") " )"); res = CIPURSESelectFile(0x2ff7, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { @@ -88,8 +93,8 @@ static int CmdHFCipurseInfo(const char *Cmd) { } if (len > 0) { - PrintAndLogEx(INFO, "Info file: " _GREEN_("OK")); - PrintAndLogEx(INFO, "[%d]: %s", len, sprint_hex(buf, len)); + PrintAndLogEx(INFO, "Info file ( " _GREEN_("ok") " )"); + PrintAndLogEx(INFO, "[%zu]: %s", len, sprint_hex(buf, len)); CIPURSEPrintInfoFile(buf, len); } @@ -98,83 +103,93 @@ static int CmdHFCipurseInfo(const char *Cmd) { } static int CmdHFCipurseAuth(const char *Cmd) { - uint8_t buf[APDU_RES_LEN] = {0}; - size_t len = 0; - uint16_t sw = 0; - uint8_t keyId = 1; - uint8_t key[] = {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73}; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf cipurse auth", - "Authenticate with key ID and key", - "hf cipurse auth -> Authenticate with keyID=1 and key = 7373...7373\n" - "hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> Authenticate with key\n"); + "Authenticate with key ID and key. If no key is supplied, default key of 737373...7373 will be used", + "hf cipurse auth -> Authenticate with keyID 1, default key\n" + "hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> Authenticate keyID 2 with key\n"); void *argtable[] = { arg_param_begin, arg_lit0("a", "apdu", "show APDU requests and responses"), arg_lit0("v", "verbose", "show technical data"), - arg_int0("n", "keyid", "", "key id"), - arg_str0("k", "key", "", "key for authenticate"), + arg_int0("n", NULL, "", "key ID"), + arg_str0("k", "key", "", "Auth key"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - keyId = arg_get_int_def(ctx, 3, 1); + uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId); uint8_t hdata[250] = {0}; int hdatalen = sizeof(hdata); CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen); if (hdatalen && hdatalen != 16) { - PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only."); + PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only"); CLIParserFree(ctx); return PM3_EINVARG; } + + uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0}; if (hdatalen) memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH); + else + memcpy(key, defaultKey, sizeof(defaultKey)); SetAPDULogging(APDULogging); CLIParserFree(ctx); + size_t len = 0; + uint16_t sw = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { - PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } uint8_t kvv[CIPURSE_KVV_LENGTH] = {0}; CipurseCGetKVV(key, kvv); - if (verbose) - PrintAndLogEx(INFO, "Key id: %d key: %s KVV: %s", keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH), sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH)); + if (verbose) { + PrintAndLogEx(INFO, "Key id " _YELLOW_("%d") " key " _YELLOW_("%s") " KVV " _YELLOW_("%s") + , keyId + , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) + , sprint_hex_inrow(kvv, CIPURSE_KVV_LENGTH) + ); + } bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose); if (verbose == false) { if (bres) - PrintAndLogEx(INFO, "Authentication " _GREEN_("OK")); + PrintAndLogEx(INFO, "Authentication ( " _GREEN_("ok") " )"); else - PrintAndLogEx(ERR, "Authentication " _RED_("ERROR")); + PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )"); } DropField(); - return bres ? PM3_SUCCESS : PM3_ESOFT; + return (bres) ? PM3_SUCCESS : PM3_ESOFT; } static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, size_t sreqid, size_t srespid, uint8_t *key, CipurseChannelSecurityLevel *sreq, CipurseChannelSecurityLevel *sresp) { uint8_t hdata[250] = {0}; int hdatalen = sizeof(hdata); - CLIGetHexWithReturn(ctx, keyid, hdata, &hdatalen); + if (CLIParamHexToBuf(arg_get_str(ctx, keyid), hdata, hdatalen, &hdatalen)) + return PM3_ESOFT; + if (hdatalen && hdatalen != 16) { - PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only."); - CLIParserFree(ctx); + PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only"); return PM3_EINVARG; } if (hdatalen) memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH); + else + memcpy(key, defaultKey, sizeof(defaultKey)); *sreq = CPSMACed; *sresp = CPSMACed; @@ -182,7 +197,9 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz char cdata[250] = {0}; int cdatalen = sizeof(cdata); cdatalen--; // for trailer 0x00 - CLIGetStrWithReturn(ctx, sreqid, (uint8_t *)cdata, &cdatalen); + if (CLIParamStrToBuf(arg_get_str(ctx, sreqid), (uint8_t *)cdata, cdatalen, &cdatalen)) + return PM3_ESOFT; + if (cdatalen) { str_lower(cdata); if (strcmp(cdata, "plain") == 0) @@ -192,7 +209,7 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz else if (strcmp(cdata, "enc") == 0 || strcmp(cdata, "encode") == 0 || strcmp(cdata, "encrypted") == 0) *sreq = CPSEncrypted; else { - PrintAndLogEx(ERR, _RED_("ERROR:") " security level can be only: plain|mac|encode."); + PrintAndLogEx(ERR, _RED_("ERROR:") " security level can be only: plain | mac | encode"); return PM3_EINVARG; } } @@ -200,7 +217,9 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz cdatalen = sizeof(cdata); memset(cdata, 0, cdatalen); cdatalen--; // for trailer 0x00 - CLIGetStrWithReturn(ctx, srespid, (uint8_t *)cdata, &cdatalen); + if (CLIParamStrToBuf(arg_get_str(ctx, srespid), (uint8_t *)cdata, cdatalen, &cdatalen)) + return PM3_ESOFT; + if (cdatalen) { str_lower(cdata); if (strcmp(cdata, "plain") == 0) @@ -210,7 +229,7 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz else if (strcmp(cdata, "enc") == 0 || strcmp(cdata, "encode") == 0 || strcmp(cdata, "encrypted") == 0) *sresp = CPSEncrypted; else { - PrintAndLogEx(ERR, _RED_("ERROR:") " security level can be only: plain|mac|encode."); + PrintAndLogEx(ERR, _RED_("ERROR:") " security level can be only: plain | mac | encode"); return PM3_EINVARG; } } @@ -219,24 +238,19 @@ static int CLIParseKeyAndSecurityLevels(CLIParserContext *ctx, size_t keyid, siz } static int CmdHFCipurseReadFile(const char *Cmd) { - uint8_t buf[APDU_RES_LEN] = {0}; - size_t len = 0; - uint16_t sw = 0; - uint8_t key[] = CIPURSE_DEFAULT_KEY; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf cipurse read", - "Read file by file ID with key ID and key", - "hf cipurse read -f 2ff7 -> Authenticate with keyID=1 and key = 7373...7373 and read file with id 2ff7\n" - "hf cipurse read -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file\n"); + "Read file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used", + "hf cipurse read --fid 2ff7 -> Authenticate with keyID 1, read file with id 2ff7\n" + "hf cipurse read -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and read file\n"); void *argtable[] = { arg_param_begin, arg_lit0("a", "apdu", "show APDU requests and responses"), arg_lit0("v", "verbose", "show technical data"), - arg_int0("n", "keyid", "", "key id"), - arg_str0("k", "key", "", "key for authenticate"), - arg_str0("f", "file", "", "file ID"), + arg_int0("n", NULL, "", "key ID"), + arg_str0("k", "key", "", "Auth key"), + arg_str0(NULL, "fid", "", "file ID"), arg_int0("o", "offset", "", "offset for reading data from file"), arg_lit0(NULL, "noauth", "read file without authentication"), arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), @@ -245,28 +259,30 @@ static int CmdHFCipurseReadFile(const char *Cmd) { }; CLIExecWithReturn(ctx, Cmd, argtable, true); + bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - uint8_t keyId = arg_get_int_def(ctx, 3, 1); + uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId); CipurseChannelSecurityLevel sreq = CPSMACed; CipurseChannelSecurityLevel sresp = CPSMACed; + uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0}; int res = CLIParseKeyAndSecurityLevels(ctx, 4, 8, 9, key, &sreq, &sresp); if (res) { CLIParserFree(ctx); return PM3_EINVARG; } - uint16_t fileId = 0x2ff7; - uint8_t hdata[250] = {0}; int hdatalen = sizeof(hdata); CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen); if (hdatalen && hdatalen != 2) { - PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only."); + PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only"); CLIParserFree(ctx); return PM3_EINVARG; } + + uint16_t fileId = defaultFileId; if (hdatalen) fileId = (hdata[0] << 8) + hdata[1]; @@ -278,21 +294,25 @@ static int CmdHFCipurseReadFile(const char *Cmd) { CLIParserFree(ctx); + size_t len = 0; + uint16_t sw = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { - PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } if (verbose) - PrintAndLogEx(INFO, "File id: %x offset %d key id: %d key: %s", fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)); + PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " offset " _YELLOW_("%zu") " key id " _YELLOW_("%d") " key " _YELLOW_("%s"), fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)); if (noAuth == false) { bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose); if (bres == false) { if (verbose == false) - PrintAndLogEx(ERR, "Authentication " _RED_("ERROR")); + PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )"); DropField(); return PM3_ESOFT; } @@ -304,78 +324,75 @@ static int CmdHFCipurseReadFile(const char *Cmd) { res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { if (verbose == false) - PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } if (verbose) - PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId); + PrintAndLogEx(INFO, "Select file 0x%x ( " _GREEN_("ok") " )", fileId); res = CIPURSEReadBinary(offset, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { if (verbose == false) - PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } if (len == 0) - PrintAndLogEx(INFO, "File id: %x is empty", fileId); + PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " is empty", fileId); else - PrintAndLogEx(INFO, "File id: %x data[%d]: %s", fileId, len, sprint_hex(buf, len)); + PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " data[%zu]: %s", fileId, len, sprint_hex(buf, len)); DropField(); return PM3_SUCCESS; } static int CmdHFCipurseWriteFile(const char *Cmd) { - uint8_t buf[APDU_RES_LEN] = {0}; - size_t len = 0; - uint16_t sw = 0; - uint8_t key[] = CIPURSE_DEFAULT_KEY; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf cipurse write", - "Write file by file ID with key ID and key", - "hf cipurse write -f 2ff7 -> Authenticate with keyID=1 and key = 7373...7373 and write file with id 2ff7\n" - "hf cipurse write -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and write file\n"); + "Write file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used", + "hf cipurse write --fid 2ff7 -> Authenticate with keyID 1, write file with id 2ff7\n" + "hf cipurse write -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and write file\n"); void *argtable[] = { arg_param_begin, arg_lit0("a", "apdu", "show APDU requests and responses"), arg_lit0("v", "verbose", "show technical data"), - arg_int0("n", "keyid", "", "key id"), - arg_str0("k", "key", "", "key for authenticate"), - arg_str0("f", "file", "", "file ID"), + arg_int0("n", NULL, "", "key ID"), + arg_str0("k", "key", "", "Auth key"), + arg_str0(NULL, "fid", "", "file ID"), arg_int0("o", "offset", "", "offset for reading data from file"), arg_lit0(NULL, "noauth", "read file without authentication"), arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), arg_str0(NULL, "sresp", "", "communication PICC-reader security level"), - arg_str0("c", "content", "", "new file content"), + arg_str0("d", "data", "", "hex data to write to new file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - uint8_t keyId = arg_get_int_def(ctx, 3, 1); + uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId); CipurseChannelSecurityLevel sreq = CPSMACed; CipurseChannelSecurityLevel sresp = CPSMACed; + + uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0}; int res = CLIParseKeyAndSecurityLevels(ctx, 4, 8, 9, key, &sreq, &sresp); if (res) { CLIParserFree(ctx); return PM3_EINVARG; } - uint16_t fileId = 0x2ff7; + uint16_t fileId = defaultFileId; uint8_t hdata[250] = {0}; int hdatalen = sizeof(hdata); CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen); if (hdatalen && hdatalen != 2) { - PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only."); + PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only"); CLIParserFree(ctx); return PM3_EINVARG; } @@ -389,7 +406,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { hdatalen = sizeof(hdata); CLIGetHexWithReturn(ctx, 10, hdata, &hdatalen); if (hdatalen == 0) { - PrintAndLogEx(ERR, _RED_("ERROR:") " file content length must be more 0."); + PrintAndLogEx(ERR, _RED_("ERROR:") " file content length must be more 0"); CLIParserFree(ctx); return PM3_EINVARG; } @@ -398,15 +415,24 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { CLIParserFree(ctx); + size_t len = 0; + uint16_t sw = 0; + uint8_t buf[APDU_RES_LEN] = {0}; + res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { - PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } if (verbose) { - PrintAndLogEx(INFO, "File id: %x offset %d key id: %d key: %s", fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)); + PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " offset " _YELLOW_("%zu") " key id " _YELLOW_("%d") " key " _YELLOW_("%s") + , fileId + , offset + , keyId + , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) + ); PrintAndLogEx(INFO, "data[%d]: %s", hdatalen, sprint_hex(hdata, hdatalen)); } @@ -414,7 +440,7 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose); if (bres == false) { if (verbose == false) - PrintAndLogEx(ERR, "Authentication " _RED_("ERROR")); + PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )"); DropField(); return PM3_ESOFT; } @@ -426,83 +452,78 @@ static int CmdHFCipurseWriteFile(const char *Cmd) { res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { if (verbose == false) - PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } if (verbose) - PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId); + PrintAndLogEx(INFO, "Select file 0x%x ( " _GREEN_("ok") " )", fileId); res = CIPURSEUpdateBinary(offset, hdata, hdatalen, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { if (verbose == false) - PrintAndLogEx(ERR, "File write " _RED_("ERROR") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "File write " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } - PrintAndLogEx(INFO, "File id: %x successfully written.", fileId); + PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " successfully written", fileId); DropField(); return PM3_SUCCESS; } static int CmdHFCipurseReadFileAttr(const char *Cmd) { - uint8_t buf[APDU_RES_LEN] = {0}; - size_t len = 0; - uint16_t sw = 0; - uint8_t key[] = CIPURSE_DEFAULT_KEY; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf cipurse aread", - "Read file attributes by file ID with key ID and key", - "hf cipurse aread -f 2ff7 -> Authenticate with keyID=1 and key = 7373...7373 and read file attributes with id 2ff7\n" - "hf cipurse aread -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file attributes\n"); + "Read file attributes by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used", + "hf cipurse aread --fid 2ff7 -> Authenticate with keyID 1, read file attributes with id 2ff7\n" + "hf cipurse aread -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2, read file attributes\n"); void *argtable[] = { arg_param_begin, arg_lit0("a", "apdu", "show APDU requests and responses"), arg_lit0("v", "verbose", "show technical data"), - arg_int0("n", "keyid", "", "key id"), - arg_str0("k", "key", "", "key for authenticate"), - arg_str0("f", "file", "", "file ID"), + arg_int0("n", NULL, "", "key ID"), + arg_str0("k", "key", "", "Auth key"), + arg_str0(NULL, "fid", "", "file ID"), arg_lit0(NULL, "noauth", "read file attributes without authentication"), arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), arg_str0(NULL, "sresp", "", "communication PICC-reader security level"), - arg_lit0(NULL, "sel-adf", "show info about ADF itself"), - arg_lit0(NULL, "sel-mf", "show info about master file"), + arg_lit0(NULL, "sel-adf", "show info about ADF itself"), + arg_lit0(NULL, "sel-mf", "show info about master file"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - uint8_t keyId = arg_get_int_def(ctx, 3, 1); + uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId); CipurseChannelSecurityLevel sreq = CPSMACed; CipurseChannelSecurityLevel sresp = CPSMACed; + uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0}; int res = CLIParseKeyAndSecurityLevels(ctx, 4, 7, 8, key, &sreq, &sresp); if (res) { CLIParserFree(ctx); return PM3_EINVARG; } - uint16_t fileId = 0x2ff7; - uint8_t hdata[250] = {0}; int hdatalen = sizeof(hdata); CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen); if (hdatalen && hdatalen != 2) { - PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only."); + PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only"); CLIParserFree(ctx); return PM3_EINVARG; } + + uint16_t fileId = defaultFileId; if (hdatalen) fileId = (hdata[0] << 8) + hdata[1]; bool noAuth = arg_get_lit(ctx, 6); - bool seladf = arg_get_lit(ctx, 9); bool selmf = arg_get_lit(ctx, 10); @@ -510,21 +531,30 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) { CLIParserFree(ctx); + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { - PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } - if (verbose) - PrintAndLogEx(INFO, "File id: %x key id: %d key: %s", fileId, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)); + if (verbose) { + PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " key id " _YELLOW_("%d") " key " _YELLOW_("%s") + , fileId + , keyId + , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) + ); + } if (noAuth == false) { bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose); if (bres == false) { if (verbose == false) - PrintAndLogEx(ERR, "Authentication " _RED_("ERROR")); + PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )"); DropField(); return PM3_ESOFT; } @@ -541,31 +571,31 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) { if (res != 0 || sw != 0x9000) { if (verbose == false) - PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } } if (verbose) - PrintAndLogEx(INFO, "Select file 0x%x " _GREEN_("OK"), fileId); + PrintAndLogEx(INFO, "Select file 0x%x ( " _GREEN_("ok") " )", fileId); res = CIPURSEReadFileAttributes(buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { if (verbose == false) - PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } if (len == 0) { - PrintAndLogEx(WARNING, "File id: %x attributes is empty", fileId); + PrintAndLogEx(WARNING, "File id " _YELLOW_("%x") " attributes is empty", fileId); DropField(); return PM3_SUCCESS; } if (verbose) - PrintAndLogEx(INFO, "File id: %x attributes[%d]: %s", fileId, len, sprint_hex(buf, len)); + PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " attributes[%zu]: %s", fileId, len, sprint_hex(buf, len)); CIPURSEPrintFileAttr(buf, len); @@ -574,24 +604,19 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) { } static int CmdHFCipurseDeleteFile(const char *Cmd) { - uint8_t buf[APDU_RES_LEN] = {0}; - size_t len = 0; - uint16_t sw = 0; - uint8_t key[] = CIPURSE_DEFAULT_KEY; - CLIParserContext *ctx; CLIParserInit(&ctx, "hf cipurse delete", - "Read file by file ID with key ID and key", - "hf cipurse delete -f 2ff7 -> Authenticate with keyID=1 and key = 7373...7373 and delete file with id 2ff7\n" - "hf cipurse delete -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and delete file\n"); + "Read file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used", + "hf cipurse delete --fid 2ff7 -> Authenticate with keyID 1, delete file with id 2ff7\n" + "hf cipurse delete -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and delete file\n"); void *argtable[] = { arg_param_begin, arg_lit0("a", "apdu", "show APDU requests and responses"), arg_lit0("v", "verbose", "show technical data"), - arg_int0("n", "keyid", "", "key id"), - arg_str0("k", "key", "", "key for authenticate"), - arg_str0("f", "file", "", "file ID"), + arg_int0("n", NULL, "", "key ID"), + arg_str0("k", "key", "", "Auth key"), + arg_str0(NULL, "fid", "", "file ID"), arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), arg_str0(NULL, "sresp", "", "communication PICC-reader security level"), arg_param_end @@ -600,26 +625,27 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { bool APDULogging = arg_get_lit(ctx, 1); bool verbose = arg_get_lit(ctx, 2); - uint8_t keyId = arg_get_int_def(ctx, 3, 1); + uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId); CipurseChannelSecurityLevel sreq = CPSMACed; CipurseChannelSecurityLevel sresp = CPSMACed; + uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0}; int res = CLIParseKeyAndSecurityLevels(ctx, 4, 6, 7, key, &sreq, &sresp); if (res) { CLIParserFree(ctx); return PM3_EINVARG; } - uint16_t fileId = 0x2ff7; - uint8_t hdata[250] = {0}; int hdatalen = sizeof(hdata); CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen); if (hdatalen && hdatalen != 2) { - PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only."); + PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only"); CLIParserFree(ctx); return PM3_EINVARG; } + + uint16_t fileId = defaultFileId; if (hdatalen) fileId = (hdata[0] << 8) + hdata[1]; @@ -627,20 +653,29 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { CLIParserFree(ctx); + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { - PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } - if (verbose) - PrintAndLogEx(INFO, "File id: %x key id: %d key: %s", fileId, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)); + if (verbose) { + PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " key id " _YELLOW_("%d") " key " _YELLOW_("%s") + , fileId + , keyId + , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) + ); + } bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose); if (bres == false) { if (verbose == false) - PrintAndLogEx(ERR, "Authentication " _RED_("ERROR")); + PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )"); DropField(); return PM3_ESOFT; } @@ -651,28 +686,17 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { res = CIPURSEDeleteFile(fileId, buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { if (verbose == false) - PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x.", sw); + PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw); DropField(); return PM3_ESOFT; } - PrintAndLogEx(INFO, "File id: 04x deleted " _GREEN_("succesfully"), fileId); + PrintAndLogEx(INFO, "File id " _YELLOW_("%04x") " deleted " _GREEN_("succesfully"), fileId); DropField(); return PM3_SUCCESS; } - - - - - - - - - - - bool CheckCardCipurse(void) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; @@ -682,14 +706,83 @@ bool CheckCardCipurse(void) { return (res == 0 && sw == 0x9000); } +static int CmdHFCipurseTest(const char *Cmd) { + CIPURSETest(true); + return PM3_SUCCESS; +} + +static int CmdHFCipurseDefault(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf cipurse default", + "Set default parameters for access to cipurse card", + "hf cipurse default -n 1 -k 65656565656565656565656565656565 --fid 2ff7 -> Set key, key id and file id\n"); + + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "clear", "resets to defaults"), + arg_int0("n", NULL, "", "Key ID"), + arg_str0("k", "key", "", "Authentication key"), + arg_str0(NULL, "fid", "", "File ID"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool clearing = arg_get_lit(ctx, 1); + if (clearing) { + defaultKeyId = 1; + defaultFileId = 0x2ff7; + uint8_t ckey[CIPURSE_AES_KEY_LENGTH] = CIPURSE_DEFAULT_KEY; + memcpy(defaultKey, ckey, CIPURSE_AES_KEY_LENGTH); + } + + defaultKeyId = arg_get_int_def(ctx, 2, defaultKeyId); + + uint8_t hdata[250] = {0}; + int hdatalen = sizeof(hdata); + CLIGetHexWithReturn(ctx, 3, hdata, &hdatalen); + if (hdatalen && hdatalen != 16) { + PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (hdatalen) + memcpy(defaultKey, hdata, CIPURSE_AES_KEY_LENGTH); + + memset(hdata, 0, sizeof(hdata)); + hdatalen = sizeof(hdata); + CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen); + if (hdatalen && hdatalen != 2) { + PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (hdatalen) + defaultFileId = (hdata[0] << 8) + hdata[1]; + + CLIParserFree(ctx); + + + PrintAndLogEx(INFO, "-----------" _CYAN_("Default parameters") "---------------------------------"); + + PrintAndLogEx(INFO, "Key ID : %d", defaultKeyId); + PrintAndLogEx(INFO, "Key : %s", sprint_hex(defaultKey, sizeof(defaultKey))); + PrintAndLogEx(INFO, "File ID: 0x%04x", defaultFileId); + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help."}, - {"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Info about Cipurse tag."}, - {"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authentication."}, - {"read", CmdHFCipurseReadFile, IfPm3Iso14443a, "Read binary file."}, - {"write", CmdHFCipurseWriteFile, IfPm3Iso14443a, "Write binary file."}, - {"aread", CmdHFCipurseReadFileAttr, IfPm3Iso14443a, "Read file attributes."}, - {"delete", CmdHFCipurseDeleteFile, IfPm3Iso14443a, "Delete file."}, + {"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Get info about CIPURSE tag"}, + {"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authenticate CIPURSE tag"}, + {"read", CmdHFCipurseReadFile, IfPm3Iso14443a, "Read binary file"}, + {"write", CmdHFCipurseWriteFile, IfPm3Iso14443a, "Write binary file"}, + {"aread", CmdHFCipurseReadFileAttr, IfPm3Iso14443a, "Read file attributes"}, + {"delete", CmdHFCipurseDeleteFile, IfPm3Iso14443a, "Delete file"}, + {"default", CmdHFCipurseDefault, IfPm3Iso14443a, "Set default key and file id for all the other commands"}, + {"test", CmdHFCipurseTest, AlwaysAvailable, "Tests"}, {NULL, NULL, 0, NULL} }; diff --git a/client/src/cmdhfcryptorf.c b/client/src/cmdhfcryptorf.c index bbfb00c0e..412fc4421 100644 --- a/client/src/cmdhfcryptorf.c +++ b/client/src/cmdhfcryptorf.c @@ -260,7 +260,7 @@ static int CmdHFCryptoRFDump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename to save dump to"), + arg_str0("f", "file", "", "filename to save dump to"), arg_lit0(NULL, "64", "64byte / 512bit memory"), arg_lit0(NULL, "512", "512byte / 4096bit memory"), arg_param_end @@ -486,7 +486,7 @@ static int CmdHFCryptoRFESave(const char *Cmd) { ); void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of dumpfile"), + arg_str0("f", "file", "", "filename of dumpfile"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c index 6f635fdb0..d9f487ec8 100644 --- a/client/src/cmdhfemrtd.c +++ b/client/src/cmdhfemrtd.c @@ -19,12 +19,13 @@ #include "protocols.h" // definitions of ISO14A/7816 protocol #include "iso7816/apduinfo.h" // GetAPDUCodeDescription #include "iso7816/iso7816core.h" // Iso7816ExchangeEx etc -#include "crypto/libpcrypto.h" // Hash calculation (sha1, sha256, sha512) -#include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt +#include "crypto/libpcrypto.h" // Hash calculation (sha1, sha256, sha512), des_encrypt/des_decrypt #include "des.h" // mbedtls_des_key_set_parity #include "crapto1/crapto1.h" // prng_successor #include "commonutil.h" // num_to_bytes #include "util_posix.h" // msclock +#include "ui.h" // searchhomedirectory +#include "proxgui.h" // Picture Window // Max file size in bytes. Used in several places. // Average EF_DG2 seems to be around 20-25kB or so, but ICAO doesn't set an upper limit @@ -54,6 +55,7 @@ static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const c static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path); static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen); +static int emrtd_print_ef_dg2_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen); static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen); @@ -85,7 +87,7 @@ static emrtd_dg_t dg_table[] = { // tag dg# fileid filename desc pace eac req fast parser dumper {0x60, 0, 0x011E, "EF_COM", "Header and Data Group Presence Information", false, false, true, true, emrtd_print_ef_com_info, NULL}, {0x61, 1, 0x0101, "EF_DG1", "Details recorded in MRZ", false, false, true, true, emrtd_print_ef_dg1_info, NULL}, - {0x75, 2, 0x0102, "EF_DG2", "Encoded Face", false, false, true, false, NULL, emrtd_dump_ef_dg2}, + {0x75, 2, 0x0102, "EF_DG2", "Encoded Face", false, false, true, false, emrtd_print_ef_dg2_info, emrtd_dump_ef_dg2}, {0x63, 3, 0x0103, "EF_DG3", "Encoded Finger(s)", false, true, false, false, NULL, NULL}, {0x76, 4, 0x0104, "EF_DG4", "Encoded Eye(s)", false, true, false, false, NULL, NULL}, {0x65, 5, 0x0105, "EF_DG5", "Displayed Portrait", false, false, false, false, NULL, emrtd_dump_ef_dg5}, @@ -263,20 +265,6 @@ static int emrtd_get_asn1_field_length(uint8_t *datain, int datainlen, int offse return 0; } -static void des_encrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) { - mbedtls_des_context ctx_enc; - mbedtls_des_setkey_enc(&ctx_enc, key); - mbedtls_des_crypt_ecb(&ctx_enc, input, output); - mbedtls_des_free(&ctx_enc); -} - -static void des_decrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) { - mbedtls_des_context ctx_dec; - mbedtls_des_setkey_dec(&ctx_dec, key); - mbedtls_des_crypt_ecb(&ctx_dec, input, output); - mbedtls_des_free(&ctx_dec); -} - static void des3_encrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) { mbedtls_des3_context ctx; mbedtls_des3_set2key_enc(&ctx, key); @@ -345,15 +333,15 @@ static void retail_mac(uint8_t *key, uint8_t *input, int inputlen, uint8_t *outp intermediate[x] = intermediate[x] ^ block[x]; } - des_encrypt_ecb(k0, intermediate, intermediate_des); + des_encrypt(intermediate_des, intermediate, k0); memcpy(intermediate, intermediate_des, 8); } - des_decrypt_ecb(k1, intermediate, intermediate_des); + des_decrypt(intermediate_des, intermediate, k1); memcpy(intermediate, intermediate_des, 8); - des_encrypt_ecb(k0, intermediate, intermediate_des); + des_encrypt(intermediate_des, intermediate, k0); memcpy(output, intermediate_des, 8); } @@ -698,18 +686,18 @@ static bool emrtd_lds_get_data_by_tag(uint8_t *datain, size_t datainlen, uint8_t static bool emrtd_select_and_read(uint8_t *dataout, size_t *dataoutlen, uint16_t file, uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, bool use_secure) { if (use_secure) { if (emrtd_secure_select_file_by_ef(ks_enc, ks_mac, ssc, file) == false) { - PrintAndLogEx(ERR, "Failed to secure select %s.", file); + PrintAndLogEx(ERR, "Failed to secure select %04X", file); return false; } } else { if (emrtd_select_file_by_ef(file) == false) { - PrintAndLogEx(ERR, "Failed to select %04X.", file); + PrintAndLogEx(ERR, "Failed to select %04X", file); return false; } } if (emrtd_read_file(dataout, dataoutlen, ks_enc, ks_mac, ssc, use_secure) == false) { - PrintAndLogEx(ERR, "Failed to read %04X.", file); + PrintAndLogEx(ERR, "Failed to read %04X", file); return false; } return true; @@ -843,9 +831,13 @@ static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, uint strncat(filepath, PATHSEP, 2); strcat(filepath, name); - PrintAndLogEx(INFO, "Read %s, len: %i.", name, resplen); - PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + PrintAndLogEx(INFO, "Read " _YELLOW_("%s") " , len %zu", name, resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars)"); + PrintAndLogEx(DEBUG, "------------------------------------------"); + PrintAndLogEx(DEBUG, "%s", sprint_hex_inrow(response, resplen)); + PrintAndLogEx(DEBUG, "------------------------------------------"); saveFile(filepath, ".BIN", response, resplen); + emrtd_dg_t *dg = emrtd_fileid_to_dg(file); if ((dg != NULL) && (dg->dumper != NULL)) { dg->dumper(response, resplen, path); @@ -1030,8 +1022,8 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab // Dump EF_CardAccess (if available) if (!emrtd_dump_file(ks_enc, ks_mac, ssc, dg_table[EF_CardAccess].fileid, dg_table[EF_CardAccess].filename, BAC, path)) { - PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE."); - PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about."); + PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE"); + PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about"); } // Authenticate with the eMRTD @@ -1042,7 +1034,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab // Select EF_COM if (!emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC)) { - PrintAndLogEx(ERR, "Failed to read EF_COM."); + PrintAndLogEx(ERR, "Failed to read EF_COM"); DropField(); return PM3_ESOFT; } @@ -1051,11 +1043,12 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab char *filepath = calloc(strlen(path) + 100, sizeof(char)); if (filepath == NULL) return PM3_EMALLOC; + strcpy(filepath, path); strncat(filepath, PATHSEP, 2); strcat(filepath, dg_table[EF_COM].filename); - PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen); + PrintAndLogEx(INFO, "Read EF_COM, len: %zu", resplen); PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); saveFile(filepath, ".BIN", response, resplen); @@ -1065,7 +1058,7 @@ int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_availab size_t filelistlen = 0; if (emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0) == false) { - PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); + PrintAndLogEx(ERR, "Failed to read file list from EF_COM"); DropField(); return PM3_ESOFT; } @@ -1408,6 +1401,65 @@ static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) { return PM3_SUCCESS; } +static int emrtd_print_ef_dg2_info(uint8_t *data, size_t datalen) { + + int offset = 0; + + // This is a hacky impl that just looks for the image header. I'll improve it eventually. + // based on mrpkey.py + // Note: Doing datalen - 6 to account for the longest data we're checking. + // Checks first byte before the rest to reduce overhead + for (offset = 0; offset < datalen - 6; offset++) { + if ((data[offset] == 0xFF && memcmp(jpeg_header, data + offset, 4) == 0) || + (data[offset] == 0x00 && memcmp(jpeg2k_header, data + offset, 6) == 0)) { + datalen = datalen - offset; + break; + } + } + + // If we didn't get any data, return false. + if (datalen == 0) { + return PM3_ESOFT; + } + + bool is_jpg = (data[offset] == 0xFF); + + char *fn = calloc(strlen(dg_table[EF_DG2].filename) + 4 + 1, sizeof(uint8_t)); + if (fn == NULL) + return PM3_EMALLOC; + + sprintf(fn, "%s.%s", dg_table[EF_DG2].filename, (is_jpg) ? "jpg" : "jp2"); + + PrintAndLogEx(DEBUG, "image filename `" _YELLOW_("%s") "`", fn); + + char *path; + if (searchHomeFilePath(&path, NULL, fn, false) != PM3_SUCCESS) { + free(fn); + return PM3_EFILE; + } + free(fn); + + // remove old file + if (fileExists(path)) { + PrintAndLogEx(DEBUG, "Delete old temp file `" _YELLOW_("%s") "`", path); + remove(path); + } + + // temp file. + PrintAndLogEx(DEBUG, "Save temp file `" _YELLOW_("%s") "`", path); + saveFile(path, "", data + offset, datalen); + + PrintAndLogEx(DEBUG, "view temp file `" _YELLOW_("%s") "`", path); + ShowPictureWindow(path); + msleep(500); + + // delete temp file + PrintAndLogEx(DEBUG, "Deleting temp file `" _YELLOW_("%s") "`", path); + remove(path); + //free(path); + return PM3_SUCCESS; +} + static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) { uint8_t taglist[100] = { 0x00 }; size_t taglistlen = 0; @@ -1940,6 +1992,7 @@ int infoHF_EMRTD_offline(const char *path) { if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) { emrtd_print_ef_cardaccess_info(data, datalen); + free(data); } else { PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE."); } @@ -2194,12 +2247,12 @@ static int CmdHFeMRTDInfo(const char *Cmd) { } } uint8_t path[FILENAME_MAX] = { 0x00 }; - bool offline = CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) == 0 && slen > 0; + bool is_offline = CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) == 0 && slen > 0; CLIParserFree(ctx); if (error) { return PM3_ESOFT; } - if (offline) { + if (is_offline) { return infoHF_EMRTD_offline((const char *)path); } else { bool restore_apdu_logging = GetAPDULogging(); diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h index 81f59b88c..45d0d28df 100644 --- a/client/src/cmdhfemrtd.h +++ b/client/src/cmdhfemrtd.h @@ -13,6 +13,10 @@ #include "common.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct emrtd_dg_s { uint8_t tag; uint8_t dgnum; @@ -53,4 +57,8 @@ int CmdHFeMRTD(const char *Cmd); int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path); int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available); int infoHF_EMRTD_offline(const char *path); + +#ifdef __cplusplus +} +#endif #endif diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 08c4e9360..0facfa41f 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -37,6 +37,9 @@ #define ICLASS_AUTH_RETRY 10 #define ICLASS_DECRYPTION_BIN "iclass_decryptionkey.bin" +static void print_picopass_info(const picopass_hdr_t *hdr); +void print_picopass_header(const picopass_hdr_t *hdr); + static picopass_hdr_t iclass_last_known_card; static void iclass_set_last_known_card(picopass_hdr_t *card) { memcpy(&iclass_last_known_card, card, sizeof(picopass_hdr_t)); @@ -129,7 +132,7 @@ static void iclass_upload_emul(uint8_t *d, uint16_t n, uint16_t *bytes_sent) { uint16_t bytes_remaining = n; while (bytes_remaining > 0) { - uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE, bytes_remaining); + uint32_t bytes_in_packet = MIN(PM3_CMD_DATA_SIZE - 4, bytes_remaining); if (bytes_in_packet == bytes_remaining) { // Disable fast mode on last packet conn.block_after_ACK = false; @@ -244,8 +247,8 @@ static void print_config_cards(void) { static void print_config_card(const iclass_config_card_item_t *o) { if (check_config_card(o)) { - PrintAndLogEx(INFO, "description... %s", o->desc); - PrintAndLogEx(INFO, "data....... " _YELLOW_("%s"), sprint_hex_inrow(o->data, sizeof(o->data))); + PrintAndLogEx(INFO, "description... " _YELLOW_("%s"), o->desc); + PrintAndLogEx(INFO, "data.......... " _YELLOW_("%s"), sprint_hex_inrow(o->data, sizeof(o->data))); } } @@ -253,20 +256,37 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke if (check_config_card(o) == false) { return PM3_EINVARG; } + + // generated config card header + picopass_hdr_t configcard; + memset(&configcard, 0xFF, sizeof(picopass_hdr_t)); + memcpy(configcard.csn, "\x41\x87\x66\x00\xFB\xFF\x12\xE0", 8); + memcpy(&configcard.conf, "\xFF\xFF\xFF\xFF\xF9\xFF\xFF\xBC", 8); + memcpy(&configcard.epurse, "\xFE\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8); + // defaulting to known AA1 key + HFiClassCalcDivKey(configcard.csn, iClass_Key_Table[0], configcard.key_d, false); + + // reference + picopass_hdr_t *cc = &configcard; + // get header from card - //bool have = memcmp(iclass_last_known_card.csn, "\x00\x00\x00\x00\x00\x00\x00\x00", 8); PrintAndLogEx(INFO, "trying to read a card.."); int res = read_iclass_csn(false, false); - if (res != PM3_SUCCESS) { - PrintAndLogEx(FAILED, "Put a card on antenna and try again..."); - return res; + if (res == PM3_SUCCESS) { + cc = &iclass_last_known_card; + // calc diversified key for selected card + HFiClassCalcDivKey(cc->csn, iClass_Key_Table[0], cc->key_d, false); + } else { + PrintAndLogEx(INFO, "failed to read a card, will use default config card data"); } // generate dump file - uint8_t app1_limit = iclass_last_known_card.conf.app_limit; + uint8_t app1_limit = cc->conf.app_limit; uint8_t old_limit = app1_limit; - uint8_t tot_bytes = (app1_limit + 1) * 8; + uint16_t tot_bytes = (app1_limit + 1) * 8; + PrintAndLogEx(INFO, " APP1 limit: %u", app1_limit); + PrintAndLogEx(INFO, "total bytes: %u", tot_bytes); // normal size uint8_t *data = calloc(1, tot_bytes); if (data == NULL) { @@ -274,12 +294,9 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke return PM3_EMALLOC; } + memcpy(data, cc, sizeof(picopass_hdr_t)); - // calc diversified key for selected card - HFiClassCalcDivKey(iclass_last_known_card.csn, iClass_Key_Table[0], iclass_last_known_card.key_d, false); - - memset(data, 0x00, tot_bytes); - memcpy(data, (uint8_t *)&iclass_last_known_card, sizeof(picopass_hdr_t)); + print_picopass_header(cc); // Keyrolling configuration cards are special. if (strstr(o->desc, "Keyroll") != NULL) { @@ -295,7 +312,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke PrintAndLogEx(WARNING, "Adapting applimit1 for KEY rolling.."); app1_limit = 0x16; - iclass_last_known_card.conf.app_limit = 0x16; + cc->conf.app_limit = 0x16; tot_bytes = (app1_limit + 1) * 8; uint8_t *p = realloc(data, tot_bytes); @@ -308,25 +325,22 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke memset(data, 0xFF, tot_bytes); } - // need to encrypt - PrintAndLogEx(INFO, "Detecting cardhelper..."); - if (IsCardHelperPresent(false) == false) { - PrintAndLogEx(FAILED, "failed to detect cardhelper"); - free(data); - return PM3_ENODATA; - } - + // KEYROLL need to encrypt uint8_t ffs[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; if (Encrypt(ffs, ffs) == false) { PrintAndLogEx(WARNING, "failed to encrypt FF"); } + // local key copy + uint8_t lkey[8]; + memcpy(lkey, key, sizeof(lkey)); + uint8_t enckey1[8]; - if (Encrypt(key, enckey1) == false) { + if (Encrypt(lkey, enckey1) == false) { PrintAndLogEx(WARNING, "failed to encrypt key1"); } - memcpy(data, &iclass_last_known_card, sizeof(picopass_hdr_t)); + memcpy(data, cc, sizeof(picopass_hdr_t)); memcpy(data + (6 * 8), o->data, sizeof(o->data)); // encrypted keyroll key 0D @@ -338,7 +352,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke // encrypted partial keyroll key 14 uint8_t foo[8] = {0x15}; - memcpy(foo + 1, key, 7); + memcpy(foo + 1, lkey, 7); uint8_t enckey2[8]; if (Encrypt(foo, enckey2) == false) { PrintAndLogEx(WARNING, "failed to encrypt partial 1"); @@ -347,7 +361,7 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke // encrypted partial keyroll key 15 memset(foo, 0xFF, sizeof(foo)); - foo[0] = key[7]; + foo[0] = lkey[7]; if (Encrypt(foo, enckey2) == false) { PrintAndLogEx(WARNING, "failed to encrypt partial 2"); } @@ -358,12 +372,11 @@ static int generate_config_card(const iclass_config_card_item_t *o, uint8_t *ke memcpy(data + (i * 8), ffs, sizeof(ffs)); } - // revert potential modified app1_limit - iclass_last_known_card.conf.app_limit = old_limit; + cc->conf.app_limit = old_limit; } else { - memcpy(data, &iclass_last_known_card, sizeof(picopass_hdr_t)); + memcpy(data, cc, sizeof(picopass_hdr_t)); memcpy(data + (6 * 8), o->data, sizeof(o->data)); } @@ -532,13 +545,13 @@ static void mem_app_config(const picopass_hdr_t *hdr) { } } -static void print_picopass_info(const picopass_hdr_t *hdr) { +void print_picopass_info(const picopass_hdr_t *hdr) { PrintAndLogEx(INFO, "-------------------- " _CYAN_("card configuration") " --------------------"); fuse_config(hdr); mem_app_config(hdr); } -static void print_picopass_header(const picopass_hdr_t *hdr) { +void print_picopass_header(const picopass_hdr_t *hdr) { PrintAndLogEx(INFO, "--------------------------- " _CYAN_("card") " ---------------------------"); PrintAndLogEx(SUCCESS, " CSN: " _GREEN_("%s") " uid", sprint_hex(hdr->csn, sizeof(hdr->csn))); PrintAndLogEx(SUCCESS, " Config: %s Card configuration", sprint_hex((uint8_t *)&hdr->conf, sizeof(hdr->conf))); @@ -1640,6 +1653,9 @@ static int CmdHFiClassDump(const char *Cmd) { app_limit1 = card_app2_limit[type]; app_limit2 = 0; + } else if (hdr->conf.app_limit >= hdr->conf.mem_config) { + PrintAndLogEx(WARNING, "AA1 config is >= card size, using card size as AA1 limit"); + app_limit1 = card_app2_limit[type]; } else { app_limit1 = hdr->conf.app_limit; app_limit2 = card_app2_limit[type]; @@ -1661,7 +1677,12 @@ static int CmdHFiClassDump(const char *Cmd) { PrintAndLogEx(FAILED, "Run command with keys"); return PM3_ESOFT; } - PrintAndLogEx(INFO, "Card has atleast 2 application areas. AA1 limit %u (0x%02X) AA2 limit %u (0x%02X)", app_limit1, app_limit1, app_limit2, app_limit2); + + if (app_limit2 != 0) { + PrintAndLogEx(INFO, "Card has at least 2 application areas. AA1 limit %u (0x%02X) AA2 limit %u (0x%02X)", app_limit1, app_limit1, app_limit2, app_limit2); + } else { + PrintAndLogEx(INFO, "Card has 1 application area. AA1 limit %u (0x%02X)", app_limit1, app_limit1); + } } iclass_dump_req_t payload = { @@ -1737,7 +1758,7 @@ static int CmdHFiClassDump(const char *Cmd) { // AIA data memcpy(tag_data + (8 * 5), tempbuf + (8 * 5), 8); // AA1 data - memcpy(tag_data + (8 * 6), tempbuf + (8 * 6), (blocks_read * 8)); + memcpy(tag_data + (8 * 6), tempbuf + (8 * 6), ((blocks_read - 6) * 8)); } uint16_t bytes_got = (app_limit1 + 1) * 8; @@ -1745,7 +1766,7 @@ static int CmdHFiClassDump(const char *Cmd) { // try AA2 Kc, Credit bool aa2_success = false; - if (have_credit_key && pagemap != 0x01) { + if (have_credit_key && pagemap != PICOPASS_NON_SECURE_PAGEMODE && app_limit2 != 0) { // AA2 authenticate credit key memcpy(payload.req.key, credit_key, 8); @@ -3859,6 +3880,8 @@ int CmdHFiClass(const char *Cmd) { // SR | 6,7,8,9, | AA1, Access control payload | 2 // | 10,11,12,13,14,15,16 | AA1, Secure identity object (SIO) | // SEOS | | | +// MFC SIO| | | +// DESFIRE| | | //} int info_iclass(void) { diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index c1ada36a3..3e079cbb4 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -762,7 +762,7 @@ static int CmdLegicDump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "specify a filename for dump file"), + arg_str0("f", "file", "", "specify a filename for dump file"), arg_lit0(NULL, "de", "deobfuscate dump data (xor with MCC)"), arg_param_end }; @@ -1032,7 +1032,7 @@ static int CmdLegicESave(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "Specify a filename to save"), + arg_str0("f", "file", "", "Specify a filename to save"), arg_int0("t", "type", "", "Tag type"), arg_lit0(NULL, "deobfuscate", "De-obfuscate dump data (xor with MCC)"), arg_param_end diff --git a/client/src/cmdhflto.c b/client/src/cmdhflto.c index 2ea818b0b..727da2548 100644 --- a/client/src/cmdhflto.c +++ b/client/src/cmdhflto.c @@ -650,7 +650,7 @@ static int CmdHfLTODump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "specify a filename for dumpfile"), + arg_str0("f", "file", "", "specify a filename for dumpfile"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 6731b6f79..985d156db 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -24,7 +24,6 @@ #include "protocols.h" #include "util_posix.h" // msclock #include "cmdhfmfhard.h" -#include "des.h" // des ecb #include "crapto1/crapto1.h" // prng_successor #include "cmdhf14a.h" // exchange APDU #include "crypto/libpcrypto.h" @@ -161,7 +160,7 @@ static void decode_print_st(uint16_t blockno, uint8_t *data) { 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 / gdb... " _GREEN_("%02x"), data[9]); + PrintAndLogEx(INFO, "user / gpb... " _GREEN_("%02x"), data[9]); PrintAndLogEx(INFO, "key B........ " _GREEN_("%s"), sprint_hex_inrow(data + 10, 6)); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, " # | Access rights"); @@ -2577,9 +2576,9 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { "hf mf fchk --2k -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n" "hf mf fchk --4k -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k\n" "hf mf fchk --1k -f mfc_default_keys.dic --> Target 1K using default dictionary file\n" - "hf mf fchk --1k --emu --> Target 1K, write to emulator memory\n" - "hf mf fchk --1k --dump --> Target 1K, write to file\n" - "hf mf fchk --1k --mem --> Target 1K, use dictionary from flashmemory"); + "hf mf fchk --1k --emu --> Target 1K, write keys to emulator memory\n" + "hf mf fchk --1k --dump --> Target 1K, write keys to file\n" + "hf mf fchk --1k --mem --> Target 1K, use dictionary from flash memory"); void *argtable[] = { arg_param_begin, @@ -2885,7 +2884,7 @@ static int CmdHF14AMfChk(const char *Cmd) { arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), arg_lit0(NULL, "dump", "Dump found keys to binary file"), - arg_str0("f", "file", "", "filename of dictionary"), + arg_str0("f", "file", "", "filename of dictionary"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -5031,7 +5030,7 @@ static int CmdHF14AMfice(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "file", "", "filename of nonce dump"), + arg_str0("f", "file", "", "filename of nonce dump"), arg_u64_0(NULL, "limit", "", "nonces to be collected"), arg_param_end }; @@ -5645,12 +5644,6 @@ static int CmdHf14AGen3Freeze(const char *Cmd) { return res; } -static void des_decrypt(void *out, const void *in, const void *key) { - mbedtls_des_context ctx; - mbedtls_des_setkey_dec(&ctx, key); - mbedtls_des_crypt_ecb(&ctx, in, out); -} - static int CmdHf14AMfSuperCard(const char *Cmd) { CLIParserContext *ctx; @@ -6007,6 +6000,110 @@ static int CmdHF14AMfView(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14AGen3View(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf gview", + "View `magic gen3 gtu` card memory", + "hf mf gview\n" + "hf mf gview --4k" + ); + void *argtable[] = { + arg_param_begin, + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (def)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool m0 = arg_get_lit(ctx, 1); + bool m1 = arg_get_lit(ctx, 2); + bool m2 = arg_get_lit(ctx, 3); + bool m4 = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + // validations + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4) == 0) { + m1 = true; + } + + char s[6]; + memset(s, 0, sizeof(s)); + uint16_t block_cnt = MIFARE_1K_MAXBLOCK; + if (m0) { + block_cnt = MIFARE_MINI_MAXBLOCK; + strncpy(s, "Mini", 5); + } else if (m1) { + block_cnt = MIFARE_1K_MAXBLOCK; + strncpy(s, "1K", 3); + } else if (m2) { + block_cnt = MIFARE_2K_MAXBLOCK; + strncpy(s, "2K", 3); + } else if (m4) { + block_cnt = MIFARE_4K_MAXBLOCK; + strncpy(s, "4K", 3); + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + PrintAndLogEx(SUCCESS, "View magic gen3 GTU MIFARE Classic " _GREEN_("%s"), s); + PrintAndLogEx(INFO, "." NOLF); + + // Select card to get UID/UIDLEN information + clearCommandBuffer(); + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500) == false) { + PrintAndLogEx(WARNING, "iso14443a card select timeout"); + return PM3_ETIMEOUT; + } + + /* + 0: couldn't read + 1: OK, with ATS + 2: OK, no ATS + 3: proprietary Anticollision + */ + uint64_t select_status = resp.oldarg[0]; + + if (select_status == 0) { + PrintAndLogEx(WARNING, "iso14443a card select failed"); + return select_status; + } + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.data.asBytes, sizeof(iso14a_card_select_t)); + + // reserve memory + uint16_t bytes = block_cnt * MFBLOCK_SIZE; + uint8_t *dump = calloc(bytes, sizeof(uint8_t)); + if (dump == NULL) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return PM3_EMALLOC; + } + + for (uint16_t i = 0; i < block_cnt; i++) { + + if (mfG3GetBlock(i, dump + (i * MFBLOCK_SIZE)) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "Can't get magic card block: %u", i); + PrintAndLogEx(HINT, "Verify your card size, and try again or try another tag position"); + free(dump); + return PM3_ESOFT; + } + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + } + + PrintAndLogEx(NORMAL, ""); + mf_print_blocks(block_cnt, dump); + free(dump); + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdHF14AMfList, AlwaysAvailable, "List MIFARE history"}, @@ -6059,7 +6156,8 @@ static command_t CommandTable[] = { {"gen3uid", CmdHf14AGen3UID, IfPm3Iso14443a, "Set UID without changing manufacturer block"}, {"gen3blk", CmdHf14AGen3Block, IfPm3Iso14443a, "Overwrite manufacturer block"}, {"gen3freeze", CmdHf14AGen3Freeze, IfPm3Iso14443a, "Perma lock UID changes. irreversible"}, - + {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("magic gen3 GTU") " -----------------------"}, + {"gview", CmdHF14AGen3View, IfPm3Iso14443a, "View card"}, // {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("i") " -----------------------"}, // {"ice", CmdHF14AMfice, IfPm3Iso14443a, "collect MIFARE Classic nonces to file"}, {NULL, NULL, NULL, NULL} diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index ae0d7f2c4..3c4cce49a 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- // Copyright (C) 2014 Iceman +// Copyright (C) 2021 Merlok // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -28,9 +29,12 @@ #include "iso7816/iso7816core.h" // APDU logging #include "util_posix.h" // msleep #include "mifare/desfire_crypto.h" +#include "mifare/desfirecore.h" +#include "mifare/desfiretest.h" +#include "mifare/desfiresecurechan.h" +#include "mifare/mifaredefault.h" // default keys #include "crapto1/crapto1.h" #include "fileutils.h" -#include "mifare/mifaredefault.h" // default keys #include "nfc/ndef.h" // NDEF #include "mifare/mad.h" #include "generator.h" @@ -508,125 +512,6 @@ static int DESFIRESendApdu(bool activate_field, bool leavefield_on, sAPDU apdu, return PM3_SUCCESS; } -static const char *getstatus(uint16_t *sw) { - if (sw == NULL) return "--> sw argument error. This should never happen !"; - if (((*sw >> 8) & 0xFF) == 0x91) { - switch (*sw & 0xFF) { - case MFDES_E_OUT_OF_EEPROM: - return "Out of Eeprom, insufficient NV-Memory to complete command"; - case MFDES_E_ILLEGAL_COMMAND_CODE: - return "Command code not supported"; - - case MFDES_E_INTEGRITY_ERROR: - return "CRC or MAC does not match data / Padding bytes invalid"; - - case MFDES_E_NO_SUCH_KEY: - return "Invalid key number specified"; - - case MFDES_E_LENGTH: - return "Length of command string invalid"; - - case MFDES_E_PERMISSION_DENIED: - return "Current configuration/status does not allow the requested command"; - - case MFDES_E_PARAMETER_ERROR: - return "Value of the parameter(s) invalid"; - - case MFDES_E_APPLICATION_NOT_FOUND: - return "Requested AID not present on PICC"; - - case MFDES_E_APPL_INTEGRITY: - return "Application integrity error, application will be disabled"; - - case MFDES_E_AUTHENTIFICATION_ERROR: - return "Current authentication status does not allow the requested command"; - - case MFDES_E_BOUNDARY: - return "Attempted to read/write data from/to beyond the file's/record's limit"; - - case MFDES_E_PICC_INTEGRITY: - return "PICC integrity error, PICC will be disabled"; - - case MFDES_E_COMMAND_ABORTED: - return "Previous command was not fully completed / Not all Frames were requested or provided by the PCD"; - - case MFDES_E_PICC_DISABLED: - return "PICC was disabled by an unrecoverable error"; - - case MFDES_E_COUNT: - return "Application count is limited to 28, not addition CreateApplication possible"; - - case MFDES_E_DUPLICATE: - return "Duplicate entry: File/Application/ISO Text does already exist"; - - case MFDES_E_EEPROM: - return "Eeprom error due to loss of power, internal backup/rollback mechanism activated"; - - case MFDES_E_FILE_NOT_FOUND: - return "Specified file number does not exist"; - - case MFDES_E_FILE_INTEGRITY: - return "File integrity error, file will be disabled"; - - default: - return "Unknown error"; - } - } - return "Unknown error"; -} - -static const char *GetErrorString(int res, uint16_t *sw) { - switch (res) { - case PM3_EAPDU_FAIL: - return getstatus(sw); - case PM3_EUNDEF: - return "Undefined error"; - case PM3_EINVARG: - return "Invalid argument(s)"; - case PM3_EDEVNOTSUPP: - return "Operation not supported by device"; - case PM3_ETIMEOUT: - return "Operation timed out"; - case PM3_EOPABORTED: - return "Operation aborted (by user)"; - case PM3_ENOTIMPL: - return "Not (yet) implemented"; - case PM3_ERFTRANS: - return "Error while RF transmission"; - case PM3_EIO: - return "Input / output error"; - case PM3_EOVFLOW: - return "Buffer overflow"; - case PM3_ESOFT: - return "Software error"; - case PM3_EFLASH: - return "Flash error"; - case PM3_EMALLOC: - return "Memory allocation error"; - case PM3_EFILE: - return "File error"; - case PM3_ENOTTY: - return "Generic TTY error"; - case PM3_EINIT: - return "Initialization error"; - case PM3_EWRONGANSWER: - return "Expected a different answer error"; - case PM3_EOUTOFBOUND: - return "Memory out-of-bounds error"; - case PM3_ECARDEXCHANGE: - return "Exchange with card error"; - case PM3_EAPDU_ENCODEFAIL: - return "Failed to create APDU"; - case PM3_ENODATA: - return "No data"; - case PM3_EFATAL: - return "Fatal error"; - default: - break; - } - return ""; -} - static int send_desfire_cmd(sAPDU *apdu, bool select, uint8_t *dest, uint32_t *recv_len, uint16_t *sw, uint32_t splitbysize, bool readalldata) { if (apdu == NULL) { PrintAndLogEx(DEBUG, "APDU=NULL"); @@ -648,7 +533,7 @@ static int send_desfire_cmd(sAPDU *apdu, bool select, uint8_t *dest, uint32_t *r uint32_t i = 1; int res = DESFIRESendApdu(select, true, *apdu, data, sizeof(data), &resplen, sw); if (res != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "%s", GetErrorString(res, sw)); + PrintAndLogEx(DEBUG, "%s", DesfireGetErrorString(res, sw)); DropFieldDesfire(); return res; } @@ -673,7 +558,7 @@ static int send_desfire_cmd(sAPDU *apdu, bool select, uint8_t *dest, uint32_t *r res = DESFIRESendApdu(false, true, *apdu, data, sizeof(data), &resplen, sw); if (res != PM3_SUCCESS) { - PrintAndLogEx(DEBUG, "%s", GetErrorString(res, sw)); + PrintAndLogEx(DEBUG, "%s", DesfireGetErrorString(res, sw)); DropFieldDesfire(); return res; } @@ -983,7 +868,17 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp } // Part 4 - tag->session_key = &default_key; + // tag->session_key = &default_key; + struct desfire_key *p = realloc(tag->session_key, sizeof(struct desfire_key)); + if (!p) { + PrintAndLogEx(FAILED, "Cannot allocate memory for session keys"); + free(tag->session_key); + return PM3_EMALLOC; + } + tag->session_key = p; + + memset(tag->session_key, 0x00, sizeof(struct desfire_key)); + Desfire_session_key_new(RndA, RndB, key, tag->session_key); if (payload->mode != MFDES_AUTH_PICC) { @@ -1017,10 +912,18 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp } } + // If the 3Des key first 8 bytes = 2nd 8 Bytes then we are really using Singe Des + // As such we need to set the session key such that the 2nd 8 bytes = 1st 8 Bytes + if (payload->algo == MFDES_ALGO_3DES) { + if (memcmp(key->data, &key->data[8], 8) == 0) + memcpy(&tag->session_key->data[8], tag->session_key->data, 8); + } + rpayload->sessionkeylen = payload->keylen; memcpy(rpayload->sessionkey, tag->session_key->data, rpayload->sessionkeylen); memset(tag->ivect, 0, MAX_CRYPTO_BLOCK_SIZE); tag->authenticated_key_no = payload->keyno; + if (tag->authentication_scheme == AS_NEW) { cmac_generate_subkeys(tag->session_key, MCD_RECEIVE); } @@ -1182,7 +1085,7 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n // PICC master key, keyalgo specific 2bit MSB switch (new_algo) { - case MFDES_ALGO_DES: + // case MFDES_ALGO_DES: // not needed as we patched des to 3des above. (coverty deadcode) case MFDES_ALGO_3DES: break; // 00xx xxx case MFDES_ALGO_3K3DES: @@ -1206,7 +1109,7 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n sAPDU apdu = {0x90, MFDES_CHANGE_KEY, 0x00, 0x00, 0x01, data}; // 0xC4 size_t cmdcnt = 0; - uint8_t csPkt[30] = {0x00}; // temp storage for AES first CheckSum + uint8_t csPkt[100] = {0x00}; // temp storage for AES/3K3Des packet to calculate checksum (size ????) uint8_t new_key_length = 16; switch (new_algo) { @@ -1264,6 +1167,11 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n memcpy(&csPkt[1], data, 18); desfire_crc32(csPkt, 19, data + 1 + cmdcnt); + } else if (new_algo == MFDES_ALGO_3K3DES) { + // 3K3Des checksum must cover : C4 + csPkt[0] = MFDES_CHANGE_KEY; + memcpy(&csPkt[1], data, 25); + desfire_crc32(csPkt, 26, data + 1 + cmdcnt); } else { desfire_crc32_append(data + 1, cmdcnt); } @@ -1281,11 +1189,16 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n break; case AS_NEW: if (new_algo == MFDES_ALGO_AES) { - // AES Checksum must cover : C4 + // AES Checksum must cover : C4 // C4 01 A0B08090E0F0C0D02030001060704050 03 - csPkt[0] = 0xC4; + csPkt[0] = MFDES_CHANGE_KEY; memcpy(&csPkt[1], data, 18); desfire_crc32(csPkt, 19, data + 1 + cmdcnt); + } else if (new_algo == MFDES_ALGO_3K3DES) { + // 3K3Des checksum must cover : C4 + csPkt[0] = MFDES_CHANGE_KEY; + memcpy(&csPkt[1], data, 25); + desfire_crc32(csPkt, 26, data + 1 + cmdcnt); } else { desfire_crc32_append(data + 1, cmdcnt); } @@ -1298,8 +1211,8 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n uint8_t *p = mifare_cryto_preprocess_data(tag, data + 1, (size_t *)&cmdcnt, 0, MDCM_ENCIPHERED | ENC_COMMAND | NO_CRC); apdu.Lc = (uint8_t)cmdcnt + 1; -// apdu.data = p; -// the above data pointed to from p did not have the key no. at the start, so copy preprocessed data after the key no. + // apdu.data = p; + // the above data pointed to from p did not have the key no. at the start, so copy preprocessed data after the key no. memcpy(&data[1], p, cmdcnt); apdu.data = data; @@ -1312,18 +1225,23 @@ static int mifare_desfire_change_key(uint8_t key_no, uint8_t *new_key, uint8_t n int res = send_desfire_cmd(&apdu, false, NULL, &recv_len, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_("can't change key -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_("can't change key -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } size_t sn = recv_len; - if (new_algo == MFDES_ALGO_AES) { + + if ((new_algo == MFDES_ALGO_AES) || (new_algo == MFDES_ALGO_3K3DES)) { // AES expects us to Calculate CMAC for status byte : OK 0x00 (0x91 00) // As such if we get this far without an error, we should be good // Since we are dropping the field, we dont need to maintain the CMAC etc. // Setting sn = 1 will allow the post process to just exit (as status only) + + // Simular 3K3Des has some work to validate, but as long as the reply code was 00 + // e.g. 02 fe ec 77 ca 13 e0 c2 06 [91 00 (OK)] 69 67 + sn = 1; } @@ -1600,25 +1518,6 @@ static int handler_desfire_getkeysettings(uint8_t *key_settings, uint8_t *num_ke return res; } -static int handler_desfire_getuid(uint8_t *uid) { - if (uid == NULL) { - PrintAndLogEx(DEBUG, "UID=NULL"); - return PM3_EINVARG; - } - sAPDU apdu = {0x90, MFDES_GET_UID, 0x00, 0x00, 0x00, NULL}; //0x51 - uint32_t recv_len = 0; - uint16_t sw = 0; - int res = send_desfire_cmd(&apdu, false, uid, &recv_len, &sw, 0, true); - - if (res != PM3_SUCCESS) - return res; - - if (sw != status(MFDES_S_OPERATION_OK)) - return PM3_ESOFT; - - return res; -} - static int handler_desfire_commit_transaction(void) { sAPDU apdu = {0x90, MFDES_COMMIT_TRANSACTION, 0x00, 0x00, 0x00, NULL}; //0xC7 uint32_t recv_len = 0; @@ -1721,7 +1620,7 @@ static int handler_desfire_select_application(uint8_t *aid) { PrintAndLogEx(WARNING, _RED_(" Can't select AID 0x%X -> %s"), (aid[2] << 16) + (aid[1] << 8) + aid[0], - GetErrorString(res, &sw) + DesfireGetErrorString(res, &sw) ); DropFieldDesfire(); return res; @@ -1764,7 +1663,7 @@ static int handler_desfire_fileids(uint8_t *dest, uint32_t *file_ids_len) { *file_ids_len = 0; int res = send_desfire_cmd(&apdu, false, dest, &recv_len, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't get file ids -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't get file ids -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1783,14 +1682,14 @@ static int handler_desfire_filesettings(uint8_t file_id, uint8_t *dest, uint32_t uint16_t sw = 0; int res = send_desfire_cmd(&apdu, false, dest, destlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't get file settings -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't get file settings -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } return res; } -static int handler_desfire_createapp(aidhdr_t *aidhdr, bool usename, bool usefid) { +/*static int handler_desfire_createapp(aidhdr_t *aidhdr, bool usename, bool usefid) { if (aidhdr == NULL) return PM3_EINVARG; sAPDU apdu = {0x90, MFDES_CREATE_APPLICATION, 0x00, 0x00, sizeof(aidhdr_t), (uint8_t *)aidhdr}; // 0xCA @@ -1823,26 +1722,11 @@ static int handler_desfire_createapp(aidhdr_t *aidhdr, bool usename, bool usefid free(data); } if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create aid -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create aid -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); } return res; -} - -static int handler_desfire_deleteapp(const uint8_t *aid) { - if (aid == NULL) { - return PM3_EINVARG; - } - sAPDU apdu = {0x90, MFDES_DELETE_APPLICATION, 0x00, 0x00, 3, (uint8_t *)aid}; // 0xDA - uint16_t sw = 0; - uint32_t recvlen = 0; - int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); - if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't delete aid -> %s"), GetErrorString(res, &sw)); - DropFieldDesfire(); - } - return res; -} +}*/ static int handler_desfire_credit(mfdes_value_t *value, uint8_t cs) { sAPDU apdu = {0x90, MFDES_CREDIT, 0x00, 0x00, 1 + 4, (uint8_t *)value}; // 0x0C @@ -1856,7 +1740,7 @@ static int handler_desfire_credit(mfdes_value_t *value, uint8_t cs) { int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't credit value -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't credit value -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1875,7 +1759,7 @@ static int handler_desfire_limitedcredit(mfdes_value_t *value, uint8_t cs) { int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't credit limited value -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't credit limited value -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1894,7 +1778,7 @@ static int handler_desfire_debit(mfdes_value_t *value, uint8_t cs) { int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't debit value -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't debit value -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1912,7 +1796,7 @@ static int handler_desfire_readdata(mfdes_data_t *data, MFDES_FILE_TYPE_T type, } // we need the CMD 0xBD to calc the CMAC - uint8_t tmp_data[8]; // Since the APDU is hardcoded to 7 bytes of payload 7+1 = 8 is enough. + uint8_t tmp_data[15]; // Since the APDU is hardcoded to 7 bytes of payload 7+1 = 8 + 4 bytes for CRC/CMAC should be enough. tmp_data[0] = apdu.INS; memcpy(&tmp_data[1], data, 7); @@ -1932,7 +1816,7 @@ static int handler_desfire_readdata(mfdes_data_t *data, MFDES_FILE_TYPE_T type, uint32_t resplen = 0; int res = send_desfire_cmd(&apdu, false, data->data, &resplen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't read data -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't read data -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1964,7 +1848,7 @@ static int handler_desfire_getvalue(mfdes_value_t *value, uint32_t *resplen, uin int res = send_desfire_cmd(&apdu, false, value->value, resplen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't read data -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't read data -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -1983,18 +1867,24 @@ static int handler_desfire_writedata(mfdes_data_t *data, MFDES_FILE_TYPE_T type, if (data->fileno > 0x1F) { return PM3_EINVARG; } + uint32_t datatowrite = le24toh(data->length); uint32_t offset = le24toh(data->offset); uint32_t datasize, recvlen = 0; int res = PM3_SUCCESS; uint16_t sw = 0; - uint8_t tmp[60] = {0}; + mfdes_data_t sdata; sAPDU apdu = {0x90, MFDES_WRITE_DATA, 0x00, 0x00, 0, (uint8_t *) &sdata}; // 0x3D + + uint8_t tmp[61] = {0}; tmp[0] = MFDES_WRITE_DATA; tmp[1] = data->fileno; apdu.data = &tmp[1]; // tmp[0] is holding the OPCODE for macd calc, so we dont want it in the apdu - if (type == MFDES_RECORD_FILE) apdu.INS = MFDES_WRITE_RECORD; + + if (type == MFDES_RECORD_FILE) { + apdu.INS = MFDES_WRITE_RECORD; + } while (datatowrite) { @@ -2010,7 +1900,8 @@ static int handler_desfire_writedata(mfdes_data_t *data, MFDES_FILE_TYPE_T type, tmp[5] = datasize & 0xFF; tmp[6] = (datasize >> 8) & 0xFF; tmp[7] = (datasize >> 16) & 0xFF; - memcpy(&tmp[8], (uint8_t *)&data->data[offset], datasize); + + memcpy(&tmp[8], (uint8_t *)data->data, datasize); size_t plen = datasize + 8; uint8_t *p = mifare_cryto_preprocess_data(tag, tmp, &plen, 8, cs | MAC_COMMAND | CMAC_COMMAND | ENC_COMMAND); @@ -2033,7 +1924,7 @@ static int handler_desfire_writedata(mfdes_data_t *data, MFDES_FILE_TYPE_T type, res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't write data -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't write data -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2042,7 +1933,7 @@ static int handler_desfire_writedata(mfdes_data_t *data, MFDES_FILE_TYPE_T type, } if (type == MFDES_RECORD_FILE) { if (handler_desfire_commit_transaction() != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't commit transaction -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't commit transaction -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2059,7 +1950,7 @@ static int handler_desfire_deletefile(uint8_t file_no) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't delete file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't delete file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2075,13 +1966,13 @@ static int handler_desfire_clear_record_file(uint8_t file_no) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't clear record file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't clear record file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } else { res = handler_desfire_commit_transaction(); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't commit transaction -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't commit transaction -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2098,7 +1989,7 @@ static int handler_desfire_create_value_file(mfdes_value_file_t *value) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create value -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create value -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2115,7 +2006,7 @@ static int handler_desfire_create_std_file(mfdes_file_t *file) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2133,7 +2024,7 @@ static int handler_desfire_create_linearrecordfile(mfdes_linear_t *file) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create linear record file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create linear record file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2153,7 +2044,7 @@ static int handler_desfire_create_cyclicrecordfile(mfdes_linear_t *file) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create cyclic record file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create cyclic record file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2169,7 +2060,7 @@ static int handler_desfire_create_backup_file(mfdes_file_t *file) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't create backup file -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't create backup file -> %s"), DesfireGetErrorString(res, &sw)); DropFieldDesfire(); return res; } @@ -2369,30 +2260,6 @@ static int desfire_authenticate(int cmdAuthMode, int cmdAuthAlgo, uint8_t *aid, return error; } -static int CmdHF14ADesGetUID(const char *Cmd) { - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf mfdes getuid", - "Get UID from a MIFARE DESfire tag", - "hf mfdes getuid"); - - void *argtable[] = { - arg_param_begin, - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, true); - CLIParserFree(ctx); - - uint8_t uid[16] = {0}; - int res = handler_desfire_getuid(uid); - if (res != PM3_SUCCESS) { - DropFieldDesfire(); - PrintAndLogEx(ERR, "Error on getting uid."); - return res; - } - PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s"), sprint_hex(uid, sizeof(uid))); - return res; -} - static int CmdHF14ADesSelectApp(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfdes selectaid", @@ -2428,190 +2295,6 @@ static int CmdHF14ADesSelectApp(const char *Cmd) { return res; } -static int CmdHF14ADesCreateApp(const char *Cmd) { - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf mfdes createaid", - "Create Application ID", - "hf mfdes createaid -a 123456 -f 1111 -k 0E -l 2E --name Test" - ); - - void *argtable[] = { - arg_param_begin, - arg_strx0("a", "aid", "", "App ID to create as hex bytes (3 hex bytes)"), - arg_strx0("f", "fid", "", "File ID to create"), - arg_strx0("k", "ks1", "", "Key Setting 1 (Application Master Key Settings)"), - arg_strx0("l", "ks2", "", "Key Setting 2"), - arg_str0(NULL, "name", "", "App ISO-4 Name"), - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, false); - /* KeySetting 1 (AMK Setting): - 0: Allow change master key - 1: Free Directory list access without master key - 0: AMK auth needed for GetFileSettings and GetKeySettings - 1: No AMK auth needed for GetFileIDs, GetISOFileIDs, GetFileSettings, GetKeySettings - 2: Free create/delete without master key - 0: CreateFile/DeleteFile only with AMK auth - 1: CreateFile/DeleteFile always - 3: Configuration changable - 0: Configuration frozen - 1: Configuration changable if authenticated with AMK (default) - 4-7: ChangeKey Access Rights - 0: Application master key needed (default) - 0x1..0xD: Auth with specific key needed to change any key - 0xE: Auth with the key to be changed (same KeyNo) is necessary to change a key - 0xF: All Keys within this application are frozen - - */ - /* KeySetting 2: - 0..3: Number of keys stored within the application (max. 14 keys) - 4: RFU - 5: Use of 2 byte ISO FID, 0: No, 1: Yes - 6..7: Crypto Method 00: DES/3DES, 01: 3K3DES, 10: AES - Example: - 2E = FID, DES, 14 keys - 6E = FID, 3K3DES, 14 keys - AE = FID, AES, 14 keys - */ - int aidlength = 3; - uint8_t aid[3] = {0}; - CLIGetHexWithReturn(ctx, 1, aid, &aidlength); - - int fidlength = 2; - uint8_t fid[2] = {0}; - CLIGetHexWithReturn(ctx, 2, fid, &fidlength); - - int keylen1 = 1; - uint8_t keysetting1[1] = {0}; - CLIGetHexWithReturn(ctx, 3, keysetting1, &keylen1); - - int keylen2 = 1; - uint8_t keysetting2[1] = {0}; - CLIGetHexWithReturn(ctx, 4, keysetting2, &keylen2); - - int namelen = 16; - uint8_t name[16] = {0}; - CLIGetStrWithReturn(ctx, 5, name, &namelen); - CLIParserFree(ctx); - - swap24(aid); - swap16(fid); - - if (aidlength != 3) { - PrintAndLogEx(ERR, "AID must have 3 bytes length"); - return PM3_EINVARG; - } - - if (fidlength != 2 && fidlength != 0) { - PrintAndLogEx(ERR, "FID must have 2 bytes length"); - return PM3_EINVARG; - } - - bool usefid = (fidlength != 0); - - if (keylen1 != 1) { - PrintAndLogEx(ERR, "Keysetting1 must have 1 byte length"); - return PM3_EINVARG; - } - - if (keylen2 != 1) { - PrintAndLogEx(ERR, "Keysetting2 must have 1 byte length"); - return PM3_EINVARG; - } - - if (namelen > 16) { - PrintAndLogEx(ERR, "Name has a max. of 16 bytes length"); - return PM3_EINVARG; - } - bool usename = true; - if (namelen == 0) usename = false; - - //90 ca 00 00 0e 3cb849 09 22 10e1 d27600 00850101 00 - /*char name[]="Test"; - uint8_t aid[]={0x12,0x34,0x56}; - uint8_t fid[]={0x11,0x22}; - uint8_t keysetting1=0xEE; - uint8_t keysetting2=0xEE;*/ - - if (memcmp(aid, "\x00\x00\x00", 3) == 0) { - PrintAndLogEx(WARNING, _RED_(" Creating root aid 000000 is forbidden")); - return PM3_ESOFT; - } - - aidhdr_t aidhdr; - memcpy(aidhdr.aid, aid, sizeof(aid)); - aidhdr.keysetting1 = keysetting1[0]; - aidhdr.keysetting2 = keysetting2[0]; - - if (usefid) - memcpy(aidhdr.fid, fid, sizeof(aidhdr.fid)); - - if (usename) - memcpy(aidhdr.name, name, sizeof(aidhdr.name)); - - PrintAndLogEx(INFO, "Creating AID using:"); - PrintAndLogEx(INFO, "AID %s", sprint_hex_inrow(aidhdr.aid, sizeof(aidhdr.aid))); - PrintAndLogEx(INFO, "Key set1 0x%02X", aidhdr.keysetting1); - PrintAndLogEx(INFO, "Key Set2 0x%02X", aidhdr.keysetting2); - if (usefid) - PrintAndLogEx(INFO, "FID %s", sprint_hex_inrow(aidhdr.fid, sizeof(aidhdr.fid))); - if (usename) - PrintAndLogEx(INFO, "DF Name %s", aidhdr.name); - - /* - uint8_t rootaid[3] = {0x00, 0x00, 0x00}; - int res = handler_desfire_select_application(rootaid); - if (res != PM3_SUCCESS) { - DropFieldDesfire(); - return res; - } - */ - - int res = handler_desfire_createapp(&aidhdr, usename, usefid); - DropFieldDesfire(); - if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Successfully created aid."); - } - return res; -} - -static int CmdHF14ADesDeleteApp(const char *Cmd) { - CLIParserContext *ctx; - CLIParserInit(&ctx, "hf mfdes deleteaid", - "Delete Application ID", - "hf mfdes deleteaid -a 123456" - ); - - void *argtable[] = { - arg_param_begin, - arg_strx0("a", "aid", "", "App ID to delete (3 hex bytes, big endian)"), - arg_param_end - }; - CLIExecWithReturn(ctx, Cmd, argtable, false); - int aidlength = 3; - uint8_t aid[3] = {0}; - CLIGetHexWithReturn(ctx, 1, aid, &aidlength); - CLIParserFree(ctx); - swap24(aid); - if (aidlength != 3) { - PrintAndLogEx(ERR, "AID must have 3 bytes length."); - return PM3_EINVARG; - } - - if (memcmp(aid, "\x00\x00\x00", 3) == 0) { - PrintAndLogEx(WARNING, _RED_(" Deleting root aid 000000 is forbidden.")); - return PM3_ESOFT; - } - - int res = handler_desfire_deleteapp(aid); - DropFieldDesfire(); - - if (res == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Successfully deleted aid."); - } - return res; -} - static int selectfile(uint8_t *aid, uint8_t fileno, uint8_t *cs) { if (handler_desfire_select_application(aid) != PM3_SUCCESS) { PrintAndLogEx(ERR, _RED_(" Couldn't select aid.")); @@ -2839,6 +2522,9 @@ static int CmdHF14ADesCreateFile(const char *Cmd) { memcpy(aid, (uint8_t *)&tag->selected_application, 3); } + // int res; + // a select here seems to invalidate the current authentication with AMK and create file fails if not open access. + // This will be managed when we track Authenticated or Note, so a place holder comment as a reminder. int res = handler_desfire_select_application(aid); if (res != PM3_SUCCESS) { PrintAndLogEx(ERR, "Couldn't select aid. Error %d", res); @@ -2927,11 +2613,11 @@ static int CmdHF14ADesGetValueData(const char *Cmd) { static int CmdHF14ADesReadData(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "hf mfdes readdata", + CLIParserInit(&ctx, "hf mfdes read", "Read data from File\n" "Make sure to select aid or authenticate aid before running this command.", - "hf mfdes readdata -n 01 -t 0 -o 000000 -l 000000 -a 123456\n" - "hf mfdes readdata -n 01 -t 0 --> Read all data from standard file, fileno 01" + "hf mfdes read -n 1 -t 0 -o 000000 -l 000000 -a 123456\n" + "hf mfdes read -n 1 -t 0 --> Read all data from standard file, fileno 1" ); void *argtable[] = { @@ -3021,13 +2707,13 @@ static int CmdHF14ADesReadData(const char *Cmd) { if (res == PM3_SUCCESS) { uint32_t len = le24toh(ft.length); - PrintAndLogEx(SUCCESS, "Read %u bytes from file %d:", ft.fileno, len); + PrintAndLogEx(SUCCESS, "Read %u bytes from file %d", ft.fileno, len); PrintAndLogEx(INFO, "Offset | Data | Ascii"); PrintAndLogEx(INFO, "----------------------------------------------------------------------------"); for (uint32_t i = 0; i < len; i += 16) { uint32_t l = len - i; - PrintAndLogEx(INFO, "%02d/0x%02X | %s| %s", i, i, sprint_hex(&ft.data[i], l > 16 ? 16 : l), sprint_ascii(&ft.data[i], l > 16 ? 16 : l)); + PrintAndLogEx(INFO, "%3d/0x%02X | %s| %s", i, i, sprint_hex(&ft.data[i], l > 16 ? 16 : l), sprint_ascii(&ft.data[i], l > 16 ? 16 : l)); } } else { PrintAndLogEx(ERR, "Couldn't read data. Error %d", res); @@ -3131,10 +2817,10 @@ static int CmdHF14ADesChangeValue(const char *Cmd) { static int CmdHF14ADesWriteData(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "hf mfdes writedata", - "Write data to File\n" + CLIParserInit(&ctx, "hf mfdes write", + "Write data to file\n" "Make sure to select aid or authenticate aid before running this command.", - "hf mfdes writedata -n 01 -t 0 -o 000000 -d 3132333435363738" + "hf mfdes write -n 01 -t 0 -o 000000 -d 3132333435363738" ); void *argtable[] = { @@ -3475,7 +3161,7 @@ static int CmdHF14ADesCreateValueFile(const char *Cmd) { DropFieldDesfire(); return res; } - +/* static int CmdHF14ADesFormatPICC(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mfdes formatpicc", @@ -3494,13 +3180,13 @@ static int CmdHF14ADesFormatPICC(const char *Cmd) { uint32_t recvlen = 0; int res = send_desfire_cmd(&apdu, false, NULL, &recvlen, &sw, 0, true); if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_(" Can't format picc -> %s"), GetErrorString(res, &sw)); + PrintAndLogEx(WARNING, _RED_(" Can't format picc -> %s"), DesfireGetErrorString(res, &sw)); } else { PrintAndLogEx(INFO, "Card successfully reset"); } DropFieldDesfire(); return res; -} +}*/ static int CmdHF14ADesInfo(const char *Cmd) { CLIParserContext *ctx; @@ -3613,7 +3299,7 @@ static int CmdHF14ADesInfo(const char *Cmd) { iso14a_card_select_t card; - res = SelectCard14443A_4(true, &card); + res = SelectCard14443A_4(true, false, &card); if (res == PM3_SUCCESS) { static const char STANDALONE_DESFIRE[] = { 0x75, 0x77, 0x81, 0x02}; static const char JCOP_DESFIRE[] = { 0x75, 0xf7, 0xb1, 0x02 }; @@ -5037,25 +4723,1061 @@ static int CmdHF14aDesMAD(const char *Cmd) { return PM3_SUCCESS; } */ +static uint8_t defaultKeyNum = 0; +static enum DESFIRE_CRYPTOALGO defaultAlgoId = T_DES; +static uint8_t defaultKey[DESFIRE_MAX_KEY_SIZE] = {0}; +static int defaultKdfAlgo = MFDES_KDF_ALGO_NONE; +static int defaultKdfInputLen = 0; +static uint8_t defaultKdfInput[50] = {0}; +static DesfireSecureChannel defaultSecureChannel = DACEV1; +static DesfireCommandSet defaultCommSet = DCCNativeISO; +static DesfireCommunicationMode defaultCommMode = DCMPlain; + +static int CmdDesGetSessionParameters(CLIParserContext *ctx, DesfireContext *dctx, + uint8_t keynoid, uint8_t algoid, uint8_t keyid, + uint8_t kdfid, uint8_t kdfiid, + uint8_t cmodeid, uint8_t ccsetid, uint8_t schannid, + uint8_t appid, + int *securechannel, + DesfireCommunicationMode defcommmode, + uint32_t *aid) { + + uint8_t keynum = defaultKeyNum; + int algores = defaultAlgoId; + uint8_t key[DESFIRE_MAX_KEY_SIZE] = {0}; + memcpy(key, defaultKey, DESFIRE_MAX_KEY_SIZE); + int kdfAlgo = defaultKdfAlgo; + int kdfInputLen = defaultKdfInputLen; + uint8_t kdfInput[50] = {0}; + memcpy(kdfInput, defaultKdfInput, defaultKdfInputLen); + int commmode = defaultCommMode; + if (defcommmode != DCMNone) + commmode = defcommmode; + int commset = defaultCommSet; + int secchann = defaultSecureChannel; + + if (keynoid) { + keynum = arg_get_int_def(ctx, keynoid, keynum); + } + + if (algoid) { + if (CLIGetOptionList(arg_get_str(ctx, algoid), DesfireAlgoOpts, &algores)) + return PM3_ESOFT; + } + + if (keyid) { + int keylen = 0; + uint8_t keydata[200] = {0}; + if (CLIParamHexToBuf(arg_get_str(ctx, keyid), keydata, sizeof(keydata), &keylen)) + return PM3_ESOFT; + if (keylen && keylen != desfire_get_key_length(algores)) { + PrintAndLogEx(ERR, "%s key must have %d bytes length instead of %d.", CLIGetOptionListStr(DesfireAlgoOpts, algores), desfire_get_key_length(algores), keylen); + return PM3_EINVARG; + } + if (keylen) + memcpy(key, keydata, keylen); + } + + if (kdfid) { + if (CLIGetOptionList(arg_get_str(ctx, kdfid), DesfireKDFAlgoOpts, &kdfAlgo)) + return PM3_ESOFT; + } + + if (kdfiid) { + int datalen = kdfInputLen; + uint8_t data[200] = {0}; + if (CLIParamHexToBuf(arg_get_str(ctx, kdfiid), data, sizeof(data), &datalen)) + return PM3_ESOFT; + if (datalen) { + kdfInputLen = datalen; + memcpy(kdfInput, data, datalen); + } + } + + if (cmodeid) { + if (CLIGetOptionList(arg_get_str(ctx, cmodeid), DesfireCommunicationModeOpts, &commmode)) + return PM3_ESOFT; + } + + if (ccsetid) { + if (CLIGetOptionList(arg_get_str(ctx, ccsetid), DesfireCommandSetOpts, &commset)) + return PM3_ESOFT; + } + + if (schannid) { + + if (CLIGetOptionList(arg_get_str(ctx, schannid), DesfireSecureChannelOpts, &secchann)) + return PM3_ESOFT; + } + + if (appid && aid) { + *aid = 0x000000; + int res = arg_get_u32_hexstr_def_nlen(ctx, appid, 0x000000, aid, 3, true); + if (res == 0) + return PM3_ESOFT; + if (res == 2) { + PrintAndLogEx(ERR, "AID length must have 3 bytes length"); + return PM3_EINVARG; + } + } + + DesfireSetKey(dctx, keynum, algores, key); + DesfireSetKdf(dctx, kdfAlgo, kdfInput, kdfInputLen); + DesfireSetCommandSet(dctx, commset); + DesfireSetCommMode(dctx, commmode); + if (securechannel) + *securechannel = secchann; + + return PM3_SUCCESS; +} + +static int CmdHF14ADesDefault(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes default", + "Set default parameters for access to desfire card.", + "hf mfdes default -n 0 -t des -k 0000000000000000 -f none -> save to the default parameters"); + + void *argtable[] = { + arg_param_begin, + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + int res = CmdDesGetSessionParameters(ctx, &dctx, 1, 2, 3, 4, 5, 6, 7, 8, 0, &securechann, DCMNone, NULL); + if (res) { + CLIParserFree(ctx); + return res; + } + + CLIParserFree(ctx); + + defaultKeyNum = dctx.keyNum; + defaultAlgoId = dctx.keyType; + memcpy(defaultKey, dctx.key, DESFIRE_MAX_KEY_SIZE); + defaultKdfAlgo = dctx.kdfAlgo; + defaultKdfInputLen = dctx.kdfInputLen; + memcpy(defaultKdfInput, dctx.kdfInput, sizeof(dctx.kdfInput)); + defaultSecureChannel = securechann; + defaultCommSet = dctx.cmdSet; + defaultCommMode = dctx.commMode; + + 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, "Comm mode : %s", CLIGetOptionListStr(DesfireCommunicationModeOpts, defaultCommMode)); + + return PM3_SUCCESS; +} + +static int CmdHF14ADesCreateApp(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes createapp", + "Create application. Master key needs to be provided.", + "option rawdata have priority over the rest settings, and options ks1 and ks2 have priority over corresponded key settings\n" + "\n"\ + "KeySetting 1 (AMK Setting, ks1):\n"\ + " 0: Allow change master key. 1 - allow, 0 - frozen\n"\ + " 1: Free Directory list access without master key\n"\ + " 0: AMK auth needed for GetFileSettings and GetKeySettings\n"\ + " 1: No AMK auth needed for GetFileIDs, GetISOFileIDs, GetFileSettings, GetKeySettings\n"\ + " 2: Free create/delete without master key\n"\ + " 0: CreateFile/DeleteFile only with AMK auth\n"\ + " 1: CreateFile/DeleteFile always\n"\ + " 3: Configuration changable\n"\ + " 0: Configuration frozen\n"\ + " 1: Configuration changable if authenticated with AMK (default)\n"\ + " 4-7: ChangeKey Access Rights\n"\ + " 0: Application master key needed (default)\n"\ + " 0x1..0xD: Auth with specific key needed to change any key\n"\ + " 0xE: Auth with the key to be changed (same KeyNo) is necessary to change a key\n"\ + " 0xF: All Keys within this application are frozen\n"\ + "\n"\ + "KeySetting 2 (ks2):\n"\ + " 0..3: Number of keys stored within the application (max. 14 keys)\n"\ + " 4: ks3 is present\n"\ + " 5: Use of 2 byte ISO FID, 0: No, 1: Yes\n"\ + " 6..7: Crypto Method 00: DES/2TDEA, 01: 3TDEA, 10: AES, 11: RFU\n"\ + " Example:\n"\ + " 2E = with FID, DES/2TDEA, 14 keys\n"\ + " 6E = with FID, 3TDEA, 14 keys\n"\ + " AE = with FID, AES, 14 keys\n"\ + "\n"\ + "hf mfdes createapp --rawdata 5634122F2E4523616964313233343536 -> execute create by rawdata\n"\ + "hf mfdes createapp --aid 123456 --fid 2345 --dfname aid123456 -> app aid, iso file id, and iso df name is specified\n" + "hf mfdes createapp --aid 123456 --fid 2345 --dfname aid123456 --dstalgo aes -> with algorithm for key AES"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_str0(NULL, "rawdata", "", "Rawdata that sends to command"), + arg_str0(NULL, "aid", "", "Application ID for create. Mandatory. (3 hex bytes, big endian)"), + arg_str0(NULL, "fid", "", "ISO file ID. Forbidden values: 0000 3F00, 3FFF, FFFF. (2 hex bytes, big endian). If specified - enable iso file id over all the files in the app."), + arg_str0(NULL, "dfname", "", "ISO DF Name 1..16 chars string"), + arg_str0(NULL, "ks1", "", "Key settings 1 (HEX 1 byte). Application Master Key Settings. default 0x2f"), + arg_str0(NULL, "ks2", "", "Key settings 2 (HEX 1 byte). default 0x0e"), + arg_str0(NULL, "dstalgo", "", "Application key crypt algo: DES, 2TDEA, 3TDEA, AES. default DES"), + arg_int0(NULL, "numkeys", "", "Keys count. 0x00..0x0e. default 0x0e"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + uint32_t appid = 0x000000; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 12, &securechann, DCMPlain, &appid); + if (res) { + CLIParserFree(ctx); + return res; + } + + uint8_t rawdata[250] = {0}; + int rawdatalen = sizeof(rawdata); + CLIGetHexWithReturn(ctx, 11, rawdata, &rawdatalen); + + uint32_t fileid = 0x0000; + res = arg_get_u32_hexstr_def_nlen(ctx, 13, 0x0000, &fileid, 2, true); + bool fileidpresent = (res == 1); + if (res == 2) { + PrintAndLogEx(ERR, "ISO file ID must have 2 bytes length"); + return PM3_EINVARG; + } + + uint8_t dfname[250] = {0}; + int dfnamelen = 16; + CLIGetStrWithReturn(ctx, 14, dfname, &dfnamelen); + + uint32_t ks1 = 0x2f; + res = arg_get_u32_hexstr_def_nlen(ctx, 15, 0x2f, &ks1, 1, true); + if (res == 2) { + PrintAndLogEx(ERR, "Key settings 1 must have 1 byte length"); + return PM3_EINVARG; + } + + uint32_t ks2 = 0x0e; + res = arg_get_u32_hexstr_def_nlen(ctx, 16, 0x0e, &ks2, 1, true); + bool ks2present = (res == 1); + if (res == 2) { + PrintAndLogEx(ERR, "Key settings 2 must have 1 byte length"); + return PM3_EINVARG; + } + + int dstalgo = T_DES; + if (CLIGetOptionList(arg_get_str(ctx, 17), DesfireAlgoOpts, &dstalgo)) + return PM3_ESOFT; + + int keycount = arg_get_int_def(ctx, 18, 0x0e); + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + if (rawdatalen == 0 && appid == 0x000000) { + PrintAndLogEx(ERR, "Creating the root aid (0x000000) is " _RED_("forbidden")); + return PM3_ESOFT; + } + + if (rawdatalen == 0 && (fileidpresent || (ks2 & 0x20) != 0) && fileid == 0x0000) { + PrintAndLogEx(ERR, "Creating the application with ISO file ID 0x0000 is " _RED_("forbidden")); + return PM3_ESOFT; + } + + if (keycount > 0x0e || keycount < 1) { + PrintAndLogEx(ERR, "Key count must be in the range 1..14"); + return PM3_ESOFT; + } + + if (dfnamelen > 16) { + PrintAndLogEx(ERR, "DF name must be a maximum of 16 bytes in length"); + return PM3_EINVARG; + } + + res = DesfireSelectAndAuthenticate(&dctx, securechann, 0x000000, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint8_t data[250] = {0}; + size_t datalen = 0; + if (rawdatalen > 0) { + memcpy(data, rawdata, rawdatalen); + datalen = rawdatalen; + } else { + DesfireAIDUintToByte(appid, &data[0]); + data[3] = ks1 & 0xff; + data[4] = ks2 & 0xff; + + if (!ks2present) { + if (keycount > 0) { + data[4] &= 0xf0; + data[4] |= keycount & 0x0f; + } + uint8_t kt = DesfireKeyAlgoToType(dstalgo); + data[4] &= 0x3f; + data[4] |= (kt & 0x03) << 6; + } + + datalen = 5; + if (fileidpresent || (data[4] & 0x20) != 0) { + data[5] = fileid & 0xff; + data[6] = (fileid >> 8) & 0xff; + data[4] |= 0x20; // set bit FileID in the ks2 + memcpy(&data[7], dfname, dfnamelen); + datalen = 7 + dfnamelen; + } + } + + if (verbose) { + PrintAndLogEx(INFO, "---------------------------"); + PrintAndLogEx(INFO, _CYAN_("Creating Application using:")); + PrintAndLogEx(INFO, "AID 0x%02X%02X%02X", data[2], data[1], data[0]); + PrintAndLogEx(INFO, "Key Set 1 0x%02X", data[3]); + PrintAndLogEx(INFO, "Key Set 2 0x%02X", data[4]); + PrintAndLogEx(INFO, "ISO file ID %s", (data[4] & 0x20) ? "enabled" : "disabled"); + if ((data[4] & 0x20)) { + PrintAndLogEx(INFO, "FID 0x%02x%02x", data[6], data[5]); + PrintAndLogEx(INFO, "DF Name[%02d] %s\n", strnlen((char *)&data[7], 16), (char *)&data[7]); + } + PrintKeySettings(data[3], data[4], true, true); + PrintAndLogEx(INFO, "---------------------------"); + } + + res = DesfireCreateApplication(&dctx, data, datalen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire CreateApplication command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "Desfire application %06x successfully " _GREEN_("created"), appid); + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesDeleteApp(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes deleteapp", + "Delete application by its 3-byte AID. Master key needs to be provided. ", + "hf mfdes deleteapp --aid 123456 -> execute with default factory setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_str0(NULL, "aid", "", "Application ID of delegated application (3 hex bytes, big endian)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + uint32_t appid = 0x000000; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); + if (res) { + CLIParserFree(ctx); + return res; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + if (appid == 0x000000) { + PrintAndLogEx(WARNING, "Deleting the root aid (0x000000) is " _RED_("forbidden")); + return PM3_ESOFT; + } + + res = DesfireSelectAndAuthenticate(&dctx, securechann, appid, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + res = DesfireDeleteApplication(&dctx, appid); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire DeleteApplication command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "Desfire application %06x " _GREEN_("deleted"), appid); + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesGetUID(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getuid", + "Get UID from card. Get the real UID if the random UID bit is on and get the same UID as in anticollision if not. Master key needs to be provided. ", + "hf mfdes getuid -> execute with default factory setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, &securechann, DCMEncrypted, NULL); + if (res) { + CLIParserFree(ctx); + return res; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + res = DesfireSelectAndAuthenticate(&dctx, securechann, 0x000000, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint8_t buf[APDU_RES_LEN] = {0}; + size_t buflen = 0; + + res = DesfireGetUID(&dctx, buf, &buflen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire GetUID command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "Desfire UID[%zu]: %s", buflen, sprint_hex(buf, buflen)); + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesFormatPICC(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes formatpicc", + "Format card. Can be done only if enabled in the configuration. Master key needs to be provided. ", + "hf mfdes formatpicc -> execute with default factory setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_str0(NULL, "aid", "", "Application ID of delegated application (3 hex bytes, big endian)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + uint32_t appid = 0x000000; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); + if (res) { + CLIParserFree(ctx); + return res; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + res = DesfireSelectAndAuthenticate(&dctx, securechann, appid, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + res = DesfireFormatPICC(&dctx); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire FormatPICC command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "Desfire format: " _GREEN_("done")); + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesGetFreeMem(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getfreemem", + "Get card's free memory. Can be done with ot without authentication. Master key may be provided.", + "hf mfdes getfreemem -> execute with default factory setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_lit0(NULL, "no-auth", "execute without authentication"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + + bool noauth = arg_get_lit(ctx, 11); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, &securechann, (noauth) ? DCMPlain : DCMMACed, NULL); + if (res) { + CLIParserFree(ctx); + return res; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + if (noauth) { + res = DesfireSelectAIDHex(&dctx, 0x000000, false, 0); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + DropField(); + return res; + } + } else { + res = DesfireSelectAndAuthenticate(&dctx, securechann, 0x000000, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + } + + uint32_t freemem = 0; + + res = DesfireGetFreeMem(&dctx, &freemem); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire GetFreeMem command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "Free memory [0x%06x] %d bytes", freemem, freemem); + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesChKeySettings(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes chkeysettings", + "Change key settings for card level or application level. WARNING: card level changes may block the card!", + "hf mfdes chkeysettings -d 0f -> set picc key settings with default key/channel setup\n"\ + "hf mfdes chkeysettings --aid 123456 -d 0f -> set app 123456 key settings with default key/channel setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), + arg_str0("d", "data", "", "Key settings (HEX 1 byte)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + uint32_t appid = 0x000000; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMEncrypted, &appid); + if (res) { + CLIParserFree(ctx); + return res; + } + + uint32_t ksett32 = 0; + res = arg_get_u32_hexstr_def_nlen(ctx, 12, 0x0f, &ksett32, 1, false); + if (res == 0) { + CLIParserFree(ctx); + return PM3_ESOFT; + } + if (res == 2) { + PrintAndLogEx(ERR, "Key settings must have 1 byte length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + if (verbose) { + PrintAndLogEx(SUCCESS, "\nNew key settings:"); + PrintKeySettings(ksett32, 0, (appid != 0x000000), false); + } + + res = DesfireSelectAndAuthenticate(&dctx, securechann, appid, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint8_t keysett = ksett32 & 0xff; + res = DesfireChangeKeySettings(&dctx, &keysett, 1); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire ChangeKeySettings command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "Key settings " _GREEN_("changed")); + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesGetKeyVersions(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getkeyversions", + "Get key versions for card level or application level.", + "--keynum parameter: App level: key number. PICC level: 00..0d - keys count, 21..23 vc keys, default 0x00.\n"\ + "hf mfdes getkeyversions --keynum 00 -> get picc master key version with default key/channel setup\n"\ + "hf mfdes getkeyversions --aid 123456 --keynum 0d -> get app 123456 all key versions with default key/channel setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number for authentication"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), + arg_str0(NULL, "keynum", "", "Key number/count (HEX 1 byte). Default 0x00."), + arg_str0(NULL, "keyset", "", "Keyset number (HEX 1 byte)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + uint32_t appid = 0x000000; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMPlain, &appid); // DCMMACed + if (res) { + CLIParserFree(ctx); + return res; + } + + uint32_t keynum32 = 0x00; + res = arg_get_u32_hexstr_def_nlen(ctx, 12, 0x00, &keynum32, 1, false); + if (res == 0) { + keynum32 = 0x00; + } + if (res == 2) { + PrintAndLogEx(ERR, "Key number must have 1 byte length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint32_t keysetnum32 = 0x00; + bool keysetpresent = true; + res = arg_get_u32_hexstr_def_nlen(ctx, 13, 0x00, &keysetnum32, 1, false); + if (res == 0) { + keysetpresent = false; + } + if (res == 2) { + PrintAndLogEx(ERR, "Keyset number must have 1 byte length"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + if (keysetpresent && appid == 0x000000) { + PrintAndLogEx(WARNING, "Keyset only at Application level"); + keysetpresent = false; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + res = DesfireSelectAndAuthenticate(&dctx, securechann, appid, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint8_t buf[APDU_RES_LEN] = {0}; + size_t buflen = 0; + + uint8_t data[2] = {0}; + data[0] = keynum32 & 0xff; + if (keysetpresent) { + data[0] |= 0x40; + data[1] = keysetnum32 & 0xff; + } + + res = DesfireGetKeyVersion(&dctx, data, (keysetpresent) ? 2 : 1, buf, &buflen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire GetKeyVersion command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (verbose) + PrintAndLogEx(INFO, "GetKeyVersion[%zu]: %s", buflen, sprint_hex(buf, buflen)); + + if (buflen > 0) { + PrintAndLogEx(INFO, "----------------------- " _CYAN_("Key Versions") " -----------------------"); + for (int i = 0; i < buflen; i++) + PrintAndLogEx(INFO, "Key 0x%02x version 0x%02x", i, buf[i]); + } else { + PrintAndLogEx(INFO, "No key versions returned."); + } + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesGetKeySettings(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getkeysettings", + "Get key settings for card level or application level.", + "hf mfdes getkeysettings -> get picc key settings with default key/channel setup\n"\ + "hf mfdes getkeysettings --aid 123456 -> get app 123456 key settings with default key/channel setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_str0(NULL, "aid", "", "Application ID (3 hex bytes, big endian)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + uint32_t appid = 0x000000; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 11, &securechann, DCMMACed, &appid); + if (res) { + CLIParserFree(ctx); + return res; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + res = DesfireSelectAndAuthenticate(&dctx, securechann, appid, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint8_t buf[APDU_RES_LEN] = {0}; + size_t buflen = 0; + + res = DesfireGetKeySettings(&dctx, buf, &buflen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire GetKeySettings command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (verbose) + PrintAndLogEx(INFO, "GetKeySettings[%zu]: %s", buflen, sprint_hex(buf, buflen)); + + if (buflen < 2) { + PrintAndLogEx(ERR, "Command GetKeySettings returned wrong length: %zu", buflen); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, "----------------------- " _CYAN_("Key settings") " -----------------------"); + PrintKeySettings(buf[0], buf[1], (appid != 0x000000), true); + if (buflen > 2) + PrintAndLogEx(INFO, "ak ver: %d", buf[2]); + if (buflen > 3) + PrintAndLogEx(INFO, "num keysets: %d", buf[3]); + if (buflen > 4) + PrintAndLogEx(INFO, "max keysize: %d", buf[4]); + if (buflen > 5) + PrintAndLogEx(INFO, "app key settings: 0x%02x", buf[5]); + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesGetAIDs(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getaids", + "Get Application IDs list from card. Master key needs to be provided or flag --no-auth set.", + "hf mfdes getaids -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_lit0(NULL, "no-auth", "execute without authentication"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + bool noauth = arg_get_lit(ctx, 11); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, &securechann, DCMMACed, NULL); + if (res) { + CLIParserFree(ctx); + return res; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + if (noauth) { + res = DesfireSelectAIDHex(&dctx, 0x000000, false, 0); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + DropField(); + return res; + } + } else { + res = DesfireSelectAndAuthenticate(&dctx, securechann, 0x000000, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + } + + uint8_t buf[APDU_RES_LEN] = {0}; + size_t buflen = 0; + + res = DesfireGetAIDList(&dctx, buf, &buflen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire GetAIDList command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (buflen >= 3) { + PrintAndLogEx(INFO, "---- " _CYAN_("AID list") " ----"); + for (int i = 0; i < buflen; i += 3) + PrintAndLogEx(INFO, "AID: %06x", DesfireAIDByteToUint(&buf[i])); + } else { + PrintAndLogEx(INFO, "There is no applications on the card"); + } + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesGetAppNames(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getappnames", + "Get Application IDs, ISO IDs and DF names from card. Master key needs to be provided or flag --no-auth set.", + "hf mfdes getappnames -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", "keyno", "", "Key number"), + arg_str0("t", "algo", "", "Crypt algo: DES, 2TDEA, 3TDEA, AES"), + arg_str0("k", "key", "", "Key for authenticate (HEX 8(DES), 16(2TDEA or AES) or 24(3TDEA) bytes)"), + arg_str0("f", "kdf", "", "Key Derivation Function (KDF): None, AN10922, Gallagher"), + arg_str0("i", "kdfi", "", "KDF input (HEX 1-31 bytes)"), + arg_str0("m", "cmode", "", "Communicaton mode: plain/mac/encrypt"), + arg_str0("c", "ccset", "", "Communicaton command set: native/niso/iso"), + arg_str0("s", "schann", "", "Secure channel: d40/ev1/ev2"), + arg_lit0(NULL, "no-auth", "execute without authentication"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + bool noauth = arg_get_lit(ctx, 11); + + DesfireContext dctx; + int securechann = defaultSecureChannel; + int res = CmdDesGetSessionParameters(ctx, &dctx, 3, 4, 5, 6, 7, 8, 9, 10, 0, &securechann, DCMMACed, NULL); + if (res) { + CLIParserFree(ctx); + return res; + } + + SetAPDULogging(APDULogging); + CLIParserFree(ctx); + + if (noauth) { + res = DesfireSelectAIDHex(&dctx, 0x000000, false, 0); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + DropField(); + return res; + } + } else { + res = DesfireSelectAndAuthenticate(&dctx, securechann, 0x000000, verbose); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + } + + uint8_t buf[APDU_RES_LEN] = {0}; + size_t buflen = 0; + + // result bytes: 3, 2, 1-16. total record size = 24 + res = DesfireGetDFList(&dctx, buf, &buflen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire GetDFList command " _RED_("error") ". Result: %d", res); + DropField(); + return PM3_ESOFT; + } + + if (buflen > 0) { + PrintAndLogEx(INFO, "----------------------- " _CYAN_("File list") " -----------------------"); + for (int i = 0; i < buflen; i++) + PrintAndLogEx(INFO, "AID: %06x ISO file id: %02x%02x ISO DF name[%zu]: %s", + DesfireAIDByteToUint(&buf[i * 24 + 1]), + buf[i * 24 + 1 + 3], buf[i * 24 + 1 + 4], + strlen((char *)&buf[i * 24 + 1 + 5]), + &buf[i * 24 + 1 + 5]); + } else { + PrintAndLogEx(INFO, "There is no applications on the card"); + } + + DropField(); + return PM3_SUCCESS; +} + +static int CmdHF14ADesTest(const char *Cmd) { + DesfireTest(true); + return PM3_SUCCESS; +} static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, {"-----------", CmdHelp, IfPm3Iso14443a, "---------------------- " _CYAN_("general") " ----------------------"}, + {"default", CmdHF14ADesDefault, IfPm3Iso14443a, "[new]Set defaults for all the commands"}, {"auth", CmdHF14ADesAuth, IfPm3Iso14443a, "Tries a MIFARE DesFire Authentication"}, - {"changekey", CmdHF14ADesChangeKey, IfPm3Iso14443a, "Change Key"}, {"chk", CmdHF14aDesChk, IfPm3Iso14443a, "Check keys"}, {"enum", CmdHF14ADesEnumApplications, IfPm3Iso14443a, "Tries enumerate all applications"}, - {"formatpicc", CmdHF14ADesFormatPICC, IfPm3Iso14443a, "Format PICC"}, - {"getuid", CmdHF14ADesGetUID, IfPm3Iso14443a, "Get random uid"}, + {"formatpicc", CmdHF14ADesFormatPICC, IfPm3Iso14443a, "[new]Format PICC"}, + {"freemem", CmdHF14ADesGetFreeMem, IfPm3Iso14443a, "[new]Get free memory size"}, + {"getuid", CmdHF14ADesGetUID, IfPm3Iso14443a, "[new]Get uid from card"}, {"info", CmdHF14ADesInfo, IfPm3Iso14443a, "Tag information"}, {"list", CmdHF14ADesList, AlwaysAvailable, "List DESFire (ISO 14443A) history"}, // {"ndefread", CmdHF14aDesNDEFRead, IfPm3Iso14443a, "Prints NDEF records from card"}, // {"mad", CmdHF14aDesMAD, IfPm3Iso14443a, "Prints MAD records from card"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "------------------------ " _CYAN_("Keys") " -----------------------"}, + {"changekey", CmdHF14ADesChangeKey, IfPm3Iso14443a, "Change Key"}, + {"chkeysettings", CmdHF14ADesChKeySettings, IfPm3Iso14443a, "[new]Change Key Settings"}, + {"getkeysettings", CmdHF14ADesGetKeySettings, IfPm3Iso14443a, "[new]Get Key Settings"}, + {"getkeyversions", CmdHF14ADesGetKeyVersions, IfPm3Iso14443a, "[new]Get Key Versions"}, {"-----------", CmdHelp, IfPm3Iso14443a, "-------------------- " _CYAN_("Applications") " -------------------"}, {"bruteaid", CmdHF14ADesBruteApps, IfPm3Iso14443a, "Recover AIDs by bruteforce"}, - {"createaid", CmdHF14ADesCreateApp, IfPm3Iso14443a, "Create Application ID"}, - {"deleteaid", CmdHF14ADesDeleteApp, IfPm3Iso14443a, "Delete Application ID"}, + {"createapp", CmdHF14ADesCreateApp, IfPm3Iso14443a, "[new]Create Application"}, + {"deleteapp", CmdHF14ADesDeleteApp, IfPm3Iso14443a, "[new]Delete Application"}, {"selectaid", CmdHF14ADesSelectApp, IfPm3Iso14443a, "Select Application ID"}, + {"getaids", CmdHF14ADesGetAIDs, IfPm3Iso14443a, "[new]Get Application IDs list"}, + {"getappnames", CmdHF14ADesGetAppNames, IfPm3Iso14443a, "[new]Get Applications list"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("Files") " -----------------------"}, {"changevalue", CmdHF14ADesChangeValue, IfPm3Iso14443a, "Write value of a value file (credit/debit/clear)"}, {"clearfile", CmdHF14ADesClearRecordFile, IfPm3Iso14443a, "Clear record File"}, @@ -5065,8 +5787,10 @@ static command_t CommandTable[] = { {"deletefile", CmdHF14ADesDeleteFile, IfPm3Iso14443a, "Create Delete File"}, {"dump", CmdHF14ADesDump, IfPm3Iso14443a, "Dump all files"}, {"getvalue", CmdHF14ADesGetValueData, IfPm3Iso14443a, "Get value of file"}, - {"readdata", CmdHF14ADesReadData, IfPm3Iso14443a, "Read data from standard/backup/record file"}, - {"writedata", CmdHF14ADesWriteData, IfPm3Iso14443a, "Write data to standard/backup/record file"}, + {"read", CmdHF14ADesReadData, IfPm3Iso14443a, "Read data from standard/backup/record file"}, + {"write", CmdHF14ADesWriteData, IfPm3Iso14443a, "Write data to standard/backup/record file"}, + {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("System") " -----------------------"}, + {"test", CmdHF14ADesTest, AlwaysAvailable, "Test crypto"}, {NULL, NULL, NULL, NULL} }; @@ -5086,10 +5810,7 @@ int CmdHFMFDes(const char *Cmd) { Native Cmds ----------- - ChangeKeySettings 0x5F SetConfiguration - GetISOFileIDs - GetCardUID ChangeFileSettings ISO/IEC 7816 Cmds diff --git a/client/src/cmdhfmfdes.h b/client/src/cmdhfmfdes.h index eb4dd77e0..60ef9c35f 100644 --- a/client/src/cmdhfmfdes.h +++ b/client/src/cmdhfmfdes.h @@ -1,5 +1,6 @@ //----------------------------------------------------------------------------- -// Iceman, 2014 +// Copyright (C) Iceman, 2014 +// Copyright (C) 2021 Merlok // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of diff --git a/client/src/cmdhftopaz.c b/client/src/cmdhftopaz.c index 45724ac5d..9dc7d2257 100644 --- a/client/src/cmdhftopaz.c +++ b/client/src/cmdhftopaz.c @@ -133,7 +133,7 @@ static int topaz_rall(uint8_t *uid, uint8_t *response) { static int topaz_read_block(uint8_t *uid, uint8_t blockno, uint8_t *block_data) { uint16_t resp_len = 0; uint8_t read8_cmd[] = {TOPAZ_READ8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t read8_response[11]; + uint8_t read8_response[11] = {0}; read8_cmd[1] = blockno; memcpy(&read8_cmd[10], uid, 4); @@ -571,10 +571,10 @@ int CmdHFTopaz(const char *Cmd) { int readTopazUid(bool verbose) { - uint8_t atqa[2]; - uint8_t rid_response[8]; + uint8_t atqa[2] = {0}; + uint8_t rid_response[8] = {0}; uint8_t *uid_echo = &rid_response[2]; - uint8_t rall_response[124]; + uint8_t rall_response[124] = {0}; int status = topaz_select(atqa, sizeof(atqa), rid_response, sizeof(rid_response), verbose); if (status == PM3_ESOFT) { diff --git a/client/src/cmdhfwaveshare.c b/client/src/cmdhfwaveshare.c index d2a0e7b6e..b4c418329 100644 --- a/client/src/cmdhfwaveshare.c +++ b/client/src/cmdhfwaveshare.c @@ -92,8 +92,10 @@ static model_t models[] = { static int CmdHelp(const char *Cmd); static int picture_bit_depth(const uint8_t *bmp, const size_t bmpsize, const uint8_t model_nr) { - if (bmpsize < sizeof(BMP_HEADER)) + if (bmpsize < sizeof(BMP_HEADER)) { return PM3_ESOFT; + } + BMP_HEADER *pbmpheader = (BMP_HEADER *)bmp; PrintAndLogEx(DEBUG, "colorsused = %d", pbmpheader->colorsused); PrintAndLogEx(DEBUG, "pbmpheader->bpp = %d", pbmpheader->bpp); @@ -403,14 +405,14 @@ static int read_bmp_rgb(uint8_t *bmp, const size_t bmpsize, uint8_t model_nr, ui if ((model_nr == M1in54B) || (model_nr == M2in13B)) { // for BW+Red screens: - uint8_t *mapBlack = calloc(width * height, sizeof(uint8_t)); + uint8_t *mapBlack = calloc((uint32_t)(width * height), sizeof(uint8_t)); if (mapBlack == NULL) { free(chanR); free(chanG); free(chanB); return PM3_EMALLOC; } - uint8_t *mapRed = calloc(width * height, sizeof(uint8_t)); + uint8_t *mapRed = calloc((uint32_t)(width * height), sizeof(uint8_t)); if (mapRed == NULL) { free(chanR); free(chanG); diff --git a/client/src/cmdhw.c b/client/src/cmdhw.c index eed42dff5..d475ba76e 100644 --- a/client/src/cmdhw.c +++ b/client/src/cmdhw.c @@ -539,7 +539,7 @@ static int CmdSetMux(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hw setmux", "Set the ADC mux to a specific value", - "hw setmux --hiraw -> set HIGH RAW" + "hw setmux --hipkd -> set HIGH PEAK\n" ); void *argtable[] = { @@ -562,6 +562,13 @@ static int CmdSetMux(const char *Cmd) { return PM3_EINVARG; } +#ifdef WITH_FPC_USART + if (loraw || hiraw) { + PrintAndLogEx(INFO, "this ADC mux option is unavailable on RDV4 compiled with FPC USART"); + return PM3_EINVARG; + } +#endif + uint8_t arg = 0; if (lopkd) arg = 0; diff --git a/client/src/cmdlf.c b/client/src/cmdlf.c index ef7e916b7..25b304c9e 100644 --- a/client/src/cmdlf.c +++ b/client/src/cmdlf.c @@ -686,9 +686,12 @@ int lf_sniff(bool verbose, uint32_t samples) { int CmdLFSniff(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf sniff", - "Sniff low frequency signal.\n" + "Sniff low frequency signal. You need to configure the LF part on the Proxmark3 device manually.\n" + "Usually a trigger and skip samples is a good thing to set before doing a low frequency sniff.\n" + "\n" " - use " _YELLOW_("`lf config`") _CYAN_(" to set parameters.\n") - _CYAN_(" - use ") _YELLOW_("`data plot`") _CYAN_(" to look at it"), + _CYAN_(" - use ") _YELLOW_("`data plot`") _CYAN_(" to look at sniff signal.\n") + _CYAN_(" - use ") _YELLOW_("`lf search -1`") _CYAN_(" to see if signal can be automatic decoded\n"), "lf sniff -v\n" "lf sniff -s 3000 -@ --> oscilloscope style \n" ); diff --git a/client/src/cmdlfem4x05.c b/client/src/cmdlfem4x05.c index 1a2fc8255..c9a25ac2d 100644 --- a/client/src/cmdlfem4x05.c +++ b/client/src/cmdlfem4x05.c @@ -1319,13 +1319,13 @@ int CmdEM4x05Chk(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x05 chk", "This command uses a dictionary attack against EM4205/4305/4469/4569", "lf em 4x05 chk\n" - "lf em 4x05 chk -e 000022B8 -> remember to use 0x for hex\n" + "lf em 4x05 chk -e 000022B8 -> check password 000022B8\n" "lf em 4x05 chk -f t55xx_default_pwds -> use T55xx default dictionary" ); void *argtable[] = { arg_param_begin, - arg_strx0("f", "file", "<*.dic>", "loads a default keys dictionary file <*.dic>"), + arg_str0("f", "file", "", "loads a default keys dictionary file <*.dic>"), arg_str0("e", "em", "", "try the calculated password from some cloners based on EM4100 ID"), arg_param_end }; @@ -1334,7 +1334,18 @@ int CmdEM4x05Chk(const char *Cmd) { char filename[FILE_PATH_SIZE] = {0}; CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - uint64_t card_id = arg_get_u64_hexstr_def(ctx, 2, 0); + uint64_t card_id = 0; + int res = arg_get_u64_hexstr_def_nlen(ctx, 2, 0, &card_id, 5, true); + if (res == 2) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "EM4100 ID must be 5 hex bytes"); + return PM3_EINVARG; + } + if (res == 0) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + CLIParserFree(ctx); if (strlen(filename) == 0) { @@ -1366,7 +1377,7 @@ int CmdEM4x05Chk(const char *Cmd) { uint32_t keycount = 0; - int res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 4, &keycount); + res = loadFileDICTIONARY_safe(filename, (void **) &keyBlock, 4, &keycount); if (res != PM3_SUCCESS || keycount == 0 || keyBlock == NULL) { PrintAndLogEx(WARNING, "no keys found in file"); if (keyBlock != NULL) @@ -1418,22 +1429,30 @@ int CmdEM4x05Chk(const char *Cmd) { int CmdEM4x05Brute(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x05 brute", - "This command tries to bruteforce the password of a EM4205/4305/4469/4569\n", + "This command tries to bruteforce the password of a EM4205/4305/4469/4569\n" + "The loop is running on device side, press Proxmark3 button to abort\n", "Note: if you get many false positives, change position on the antenna" "lf em 4x05 brute\n" - "lf em 4x05 brute -n 1 -> stop after first candidate found\n" - "lf em 4x05 brute -s 000022B8 -> remember to use 0x for hex" + "lf em 4x05 brute -n 1 -> stop after first candidate found\n" + "lf em 4x05 brute -s 000022AA -> start at 000022AA" ); void *argtable[] = { arg_param_begin, - arg_u64_0("s", "start", "", "Start bruteforce enumeration from this password value"), - arg_int0("n", NULL, "", "Stop after having found n candidates. Default: 0 => infinite"), + arg_str0("s", "start", "", "Start bruteforce enumeration from this password value"), + arg_u64_0("n", NULL, "", "Stop after having found n candidates. Default: 0 (infinite)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - uint32_t start_pwd = arg_get_u64_hexstr_def(ctx, 1, 0); - uint32_t n = arg_get_int_def(ctx, 2, 0); + uint32_t start_pwd = 0; + int res = arg_get_u32_hexstr_def(ctx, 1, 0, &start_pwd); + if (res != 1) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "check `start_pwd` parameter"); + return PM3_EINVARG; + } + + uint32_t n = arg_get_u32_def(ctx, 2, 0); CLIParserFree(ctx); PrintAndLogEx(NORMAL, ""); @@ -1449,7 +1468,7 @@ int CmdEM4x05Brute(const char *Cmd) { clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X_BF, (uint8_t *)&payload, sizeof(payload)); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_LF_EM4X_BF, &resp, 1000)) { + if (WaitForResponseTimeout(CMD_LF_EM4X_BF, &resp, 1000) == false) { PrintAndLogEx(WARNING, "(EM4x05 Bruteforce) timeout while waiting for reply."); return PM3_ETIMEOUT; } @@ -1857,7 +1876,7 @@ int CmdEM4x05Unlock(const char *Cmd) { // compute number of bits flipped PrintAndLogEx(INFO, "Bitflips: %2u events => %s", bitcount32(bitflips), bitstring); PrintAndLogEx(INFO, "New protection word => " _CYAN_("%08X") "\n", word14b); - PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05_dump`")); + PrintAndLogEx(INFO, "Try " _YELLOW_("`lf em 4x05 dump`")); } if (verbose) { diff --git a/client/src/cmdlfem4x50.c b/client/src/cmdlfem4x50.c index 149ba0b42..33c447da0 100644 --- a/client/src/cmdlfem4x50.c +++ b/client/src/cmdlfem4x50.c @@ -14,6 +14,7 @@ #include "cmdlfem4x50.h" #include #include "cmdparser.h" // command_t +#include "util_posix.h" // msclock #include "fileutils.h" #include "commonutil.h" #include "pmflash.h" @@ -100,8 +101,6 @@ static void print_info_result(uint8_t *data, bool verbose) { PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); // data section - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, _YELLOW_("EM4x50 data:")); if (verbose) { print_result(words, 0, EM4X50_NO_WORDS - 1); } else { @@ -112,16 +111,16 @@ static void print_info_result(uint8_t *data, bool verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "---- " _CYAN_("Configuration") " ----"); - PrintAndLogEx(INFO, "first word read %3i", fwr); - PrintAndLogEx(INFO, "last word read %3i", lwr); - PrintAndLogEx(INFO, "password check %3s", (bpwc) ? _RED_("on") : _GREEN_("off")); - PrintAndLogEx(INFO, "read after write %3s", (braw) ? "on" : "off"); + PrintAndLogEx(INFO, "first word read.... " _YELLOW_("%i"), fwr); + PrintAndLogEx(INFO, "last word read..... " _YELLOW_("%i"), lwr); + PrintAndLogEx(INFO, "password check..... %s", (bpwc) ? _RED_("on") : _GREEN_("off")); + PrintAndLogEx(INFO, "read after write... %s", (braw) ? "on" : "off"); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(INFO, "--------- " _CYAN_("Protection") " ---------"); - PrintAndLogEx(INFO, "first word read protected %3i", fwrp); - PrintAndLogEx(INFO, "last word read protected %3i", lwrp); - PrintAndLogEx(INFO, "first word write inhibited %3i", fwwi); - PrintAndLogEx(INFO, "last word write inhibited %3i", lwwi); + PrintAndLogEx(INFO, "--------- " _CYAN_("Protection") " ------------"); + PrintAndLogEx(INFO, "first word read protected.... %i", fwrp); + PrintAndLogEx(INFO, "last word read protected..... %i", lwrp); + PrintAndLogEx(INFO, "first word write inhibited... %i", fwwi); + PrintAndLogEx(INFO, "last word write inhibited.... %i", lwwi); PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "zero values may indicate read protection"); PrintAndLogEx(NORMAL, ""); @@ -238,7 +237,7 @@ int CmdEM4x50ESave(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "data filename"), + arg_str0("f", "file", "", "save filename"), arg_param_end }; @@ -344,7 +343,7 @@ int CmdEM4x50Login(const char *Cmd) { return PM3_EINVARG; } - uint32_t password = BYTES2UINT32(pwd); + uint32_t password = BYTES2UINT32_BE(pwd); // start clearCommandBuffer(); @@ -364,9 +363,9 @@ int CmdEM4x50Login(const char *Cmd) { int CmdEM4x50Brute(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 brute", - "Tries to bruteforce the password of a EM4x50.\n" + "Tries to bruteforce the password of a EM4x50 card.\n" "Function can be stopped by pressing pm3 button.", - "lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000\n" + "lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000\n" ); void *argtable[] = { @@ -395,8 +394,8 @@ int CmdEM4x50Brute(const char *Cmd) { } em4x50_data_t etd; - etd.password1 = BYTES2UINT32(first); - etd.password2 = BYTES2UINT32(last); + etd.password1 = BYTES2UINT32_BE(first); + etd.password2 = BYTES2UINT32_BE(last); // 27 passwords/second (empirical value) const int speed = 27; @@ -408,12 +407,12 @@ int CmdEM4x50Brute(const char *Cmd) { int dur_m = (dur_s - dur_h * 3600) / 60; dur_s -= dur_h * 3600 + dur_m * 60; - PrintAndLogEx(INFO, "Trying %i passwords in range [0x%08x, 0x%08x]" + PrintAndLogEx(INFO, "Trying " _YELLOW_("%i") " passwords in range [0x%08x, 0x%08x]" , no_iter , etd.password1 , etd.password2 ); - PrintAndLogEx(INFO, "Estimated duration: %ih%im%is", dur_h, dur_m, dur_s); + PrintAndLogEx(INFO, "Estimated duration: %ih %im %is", dur_h, dur_m, dur_s); // start clearCommandBuffer(); @@ -423,9 +422,9 @@ int CmdEM4x50Brute(const char *Cmd) { // print response if (resp.status == PM3_SUCCESS) - PrintAndLogEx(SUCCESS, "Password " _GREEN_("found") ": 0x%08x", resp.data.asDwords[0]); + PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", resp.data.asDwords[0]); else - PrintAndLogEx(FAILED, "Password: " _RED_("not found")); + PrintAndLogEx(WARNING, "brute pwd failed"); return PM3_SUCCESS; } @@ -435,14 +434,14 @@ int CmdEM4x50Brute(const char *Cmd) { int CmdEM4x50Chk(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf em 4x50 chk", - "Dictionary attack against EM4x50.", + "Run dictionary key recovery against EM4x50 card.", "lf em 4x50 chk -> uses T55xx default dictionary\n" "lf em 4x50 chk -f my.dic" ); void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "dictionary filename"), + arg_str0("f", "file", "", "dictionary filename"), arg_param_end }; @@ -463,30 +462,34 @@ int CmdEM4x50Chk(const char *Cmd) { PrintAndLogEx(INFO, "treating file as T55xx keys"); } - size_t datalen = 0; - uint8_t data[100000] = {0x0}; - uint8_t *keys = data; + // load keys + uint8_t *keys = NULL; uint32_t key_count = 0; + int res = loadFileDICTIONARY_safe(filename, (void **)&keys, 4, &key_count); + if (res != PM3_SUCCESS || key_count == 0) { + free(keys); + return res; + } - int res = loadFileDICTIONARY(filename, data, &datalen, 4, &key_count); - if ((res != PM3_SUCCESS) || (key_count == 0)) - return PM3_EFILE; + uint8_t *pkeys = keys; + + uint64_t t1 = msclock(); PrintAndLogEx(INFO, "You can cancel this operation by pressing the pm3 button"); - int status = PM3_EFAILED; - int keyblock = 2000; // block with 2000 bytes -> 500 keys + // block with 2000 bytes -> 500 keys uint8_t destfn[32] = "em4x50_chk.bin"; - PacketResponseNG resp; - int bytes_remaining = datalen; + int bytes_remaining = key_count * 4; + int status = PM3_EFAILED; + while (bytes_remaining > 0) { PrintAndLogEx(INPLACE, "Remaining keys: %i ", bytes_remaining / 4); // upload to flash. - datalen = MIN(bytes_remaining, keyblock); - res = flashmem_spiffs_load((char *)destfn, keys, datalen); + size_t n = MIN(bytes_remaining, 2000); + res = flashmem_spiffs_load((char *)destfn, keys, n); if (res != PM3_SUCCESS) { PrintAndLogEx(WARNING, "SPIFFS upload failed"); return res; @@ -500,25 +503,22 @@ int CmdEM4x50Chk(const char *Cmd) { if ((status == PM3_SUCCESS) || (status == PM3_EOPABORTED)) break; - bytes_remaining -= keyblock; - keys += keyblock; + bytes_remaining -= n; + keys += n; } + free(pkeys); PrintAndLogEx(NORMAL, ""); - // print response if (status == PM3_SUCCESS) { - PrintAndLogEx(SUCCESS, "Key " _GREEN_("found: %02x %02x %02x %02x"), - resp.data.asBytes[3], - resp.data.asBytes[2], - resp.data.asBytes[1], - resp.data.asBytes[0] - ); + uint32_t pwd = BYTES2UINT32(resp.data.asBytes); + PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%08"PRIX32) " ]", pwd); } else { PrintAndLogEx(FAILED, "No key found"); } - PrintAndLogEx(INFO, "Done"); + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\ntime in check pwd " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); return PM3_SUCCESS; } @@ -622,7 +622,7 @@ int CmdEM4x50Read(const char *Cmd) { PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len); return PM3_EINVARG; } else { - etd.password1 = BYTES2UINT32(pwd); + etd.password1 = BYTES2UINT32_BE(pwd); etd.pwd_given = true; } } @@ -637,8 +637,8 @@ int CmdEM4x50Info(const char *Cmd) { CLIParserInit(&ctx, "lf em 4x50 info", "Tag information EM4x50.", "lf em 4x50 info\n" - "lf em 4x50 info -v -> show data section\n" - "lf em 4x50 info -p 12345678 -> uses pwd 0x12345678\n" + "lf em 4x50 info -v -> show data section\n" + "lf em 4x50 info -p 12345678 -> uses pwd 0x12345678\n" ); void *argtable[] = { @@ -661,7 +661,7 @@ int CmdEM4x50Info(const char *Cmd) { PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len); return PM3_EINVARG; } else { - etd.password1 = BYTES2UINT32(pwd); + etd.password1 = BYTES2UINT32_BE(pwd); etd.pwd_given = true; } } @@ -750,7 +750,7 @@ int CmdEM4x50Dump(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), + arg_str0("f", "file", "", "dump filename (bin/eml/json)"), arg_str0("p", "pwd", "", "password, 4 hex bytes, lsb"), arg_param_end }; @@ -773,7 +773,7 @@ int CmdEM4x50Dump(const char *Cmd) { CLIParserFree(ctx); return PM3_EINVARG; } else { - etd.password1 = BYTES2UINT32(pwd); + etd.password1 = BYTES2UINT32_BE(pwd); etd.pwd_given = true; } } @@ -865,14 +865,14 @@ int CmdEM4x50Write(const char *Cmd) { PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len); return PM3_EINVARG; } else { - etd.password1 = BYTES2UINT32(pwd); + etd.password1 = BYTES2UINT32_BE(pwd); etd.pwd_given = true; } } etd.addresses = (addr << 8) | addr; etd.addr_given = true; - etd.word = BYTES2UINT32(word); + etd.word = BYTES2UINT32_BE(word); clearCommandBuffer(); SendCommandNG(CMD_LF_EM4X50_WRITE, (uint8_t *)&etd, sizeof(etd)); @@ -906,9 +906,9 @@ int CmdEM4x50Write(const char *Cmd) { // envokes changing the password of EM4x50 tag int CmdEM4x50WritePwd(const char *Cmd) { CLIParserContext *ctx; - CLIParserInit(&ctx, "lf em 4x50 writepwd", + CLIParserInit(&ctx, "lf em 4x50 wrpwd", "Writes EM4x50 password.", - "lf em 4x50 writepwd -p 4f22e7ff -n 12345678" + "lf em 4x50 wrpwd -p 4f22e7ff -n 12345678" ); void *argtable[] = { @@ -934,14 +934,14 @@ int CmdEM4x50WritePwd(const char *Cmd) { PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len); return PM3_EINVARG; } else { - etd.password1 = BYTES2UINT32(pwd); + etd.password1 = BYTES2UINT32_BE(pwd); } if (npwd_len != 4) { PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", npwd_len); return PM3_EINVARG; } else { - etd.password2 = BYTES2UINT32(npwd); + etd.password2 = BYTES2UINT32_BE(npwd); } PacketResponseNG resp; @@ -997,7 +997,7 @@ int CmdEM4x50Wipe(const char *Cmd) { em4x50_data_t etd = {.pwd_given = false, .word = 0x0, .password2 = 0x0}; - etd.password1 = BYTES2UINT32(pwd); + etd.password1 = BYTES2UINT32_BE(pwd); etd.pwd_given = true; // clear password @@ -1061,7 +1061,7 @@ int CmdEM4x50Restore(const char *Cmd) { void *argtable[] = { arg_param_begin, arg_str0("u", "uid", "", "uid, 4 hex bytes, msb"), - arg_str0("f", "filename", "", "dump filename (bin/eml/json)"), + arg_str0("f", "file", "", "dump filename (bin/eml/json)"), arg_str0("p", "pwd", "", "password, 4 hex bytes, lsb"), arg_param_end }; @@ -1093,7 +1093,7 @@ int CmdEM4x50Restore(const char *Cmd) { PrintAndLogEx(FAILED, "password length must be 4 bytes instead of %d", pwd_len); return PM3_EINVARG; } else { - etd.password1 = BYTES2UINT32(pwd); + etd.password1 = BYTES2UINT32_BE(pwd); etd.pwd_given = true; // if password is available protection and control word can be restored startblock = EM4X50_PROTECTION; @@ -1120,7 +1120,7 @@ int CmdEM4x50Restore(const char *Cmd) { PrintAndLogEx(INPLACE, "Restoring block %i", i); etd.addresses = i << 8 | i; - etd.word = reflect32(BYTES2UINT32((data + 4 * i))); + etd.word = reflect32(BYTES2UINT32_BE((data + 4 * i))); PacketResponseNG resp; clearCommandBuffer(); @@ -1170,7 +1170,7 @@ int CmdEM4x50Sim(const char *Cmd) { PrintAndLogEx(FAILED, "password length must be 4 bytes, got %d", pwd_len); return PM3_EINVARG; } else { - password = BYTES2UINT32(pwd); + password = BYTES2UINT32_BE(pwd); } } @@ -1208,21 +1208,23 @@ int CmdEM4x50Sim(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"brute", CmdEM4x50Brute, IfPm3EM4x50, "guess password of EM4x50"}, - {"chk", CmdEM4x50Chk, IfPm3EM4x50, "check passwords from dictionary"}, - {"dump", CmdEM4x50Dump, IfPm3EM4x50, "dump EM4x50 tag"}, - {"info", CmdEM4x50Info, IfPm3EM4x50, "tag information EM4x50"}, - {"login", CmdEM4x50Login, IfPm3EM4x50, "login into EM4x50"}, - {"rdbl", CmdEM4x50Read, IfPm3EM4x50, "read word data from EM4x50"}, - {"wrbl", CmdEM4x50Write, IfPm3EM4x50, "write word data to EM4x50"}, - {"writepwd", CmdEM4x50WritePwd, IfPm3EM4x50, "change password of EM4x50"}, - {"wipe", CmdEM4x50Wipe, IfPm3EM4x50, "wipe EM4x50 tag"}, - {"reader", CmdEM4x50Reader, IfPm3EM4x50, "show standard read mode data of EM4x50"}, - {"restore", CmdEM4x50Restore, IfPm3EM4x50, "restore EM4x50 dump to tag"}, - {"sim", CmdEM4x50Sim, IfPm3EM4x50, "simulate EM4x50 tag"}, - {"eload", CmdEM4x50ELoad, IfPm3EM4x50, "upload dump of EM4x50 to emulator memory"}, - {"esave", CmdEM4x50ESave, IfPm3EM4x50, "save emulator memory to file"}, - {"eview", CmdEM4x50EView, IfPm3EM4x50, "view EM4x50 content in emulator memory"}, + {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"}, + {"brute", CmdEM4x50Brute, IfPm3EM4x50, "Simple bruteforce attack to find password"}, + {"chk", CmdEM4x50Chk, IfPm3EM4x50, "Check passwords from dictionary"}, + {"dump", CmdEM4x50Dump, IfPm3EM4x50, "Dump EM4x50 tag"}, + {"info", CmdEM4x50Info, IfPm3EM4x50, "Tag information"}, + {"login", CmdEM4x50Login, IfPm3EM4x50, "Login into EM4x50 tag"}, + {"rdbl", CmdEM4x50Read, IfPm3EM4x50, "Read EM4x50 word data"}, + {"reader", CmdEM4x50Reader, IfPm3EM4x50, "Show standard read mode data"}, + {"restore", CmdEM4x50Restore, IfPm3EM4x50, "Restore EM4x50 dump to tag"}, + {"wrbl", CmdEM4x50Write, IfPm3EM4x50, "Write EM4x50 word data"}, + {"wrpwd", CmdEM4x50WritePwd, IfPm3EM4x50, "Change EM4x50 password"}, + {"wipe", CmdEM4x50Wipe, IfPm3EM4x50, "Wipe EM4x50 tag"}, + {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("simulation") " ---------------------"}, + {"eload", CmdEM4x50ELoad, IfPm3EM4x50, "Upload EM4x50 dump to emulator memory"}, + {"esave", CmdEM4x50ESave, IfPm3EM4x50, "Save emulator memory to file"}, + {"eview", CmdEM4x50EView, IfPm3EM4x50, "View EM4x50 content in emulator memory"}, + {"sim", CmdEM4x50Sim, IfPm3EM4x50, "Simulate EM4x50 tag"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 232acf068..c90637b6c 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -210,14 +210,15 @@ static int CmdLFHitagEload(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag eload", "Loads hitag tag dump into emulator memory on device", - "lf hitag eload -f lf-hitag-11223344-dump.bin\n"); + "lf hitag eload -2 -f lf-hitag-11223344-dump.bin\n"); void *argtable[] = { arg_param_begin, arg_str1("f", "file", "", "filename of dump"), - arg_lit0("1", NULL, "simulate Hitag1"), - arg_lit0("2", NULL, "simulate Hitag2"), - arg_lit0("s", NULL, "simulate HitagS"), + arg_lit0("1", NULL, "Card type Hitag1"), + arg_lit0("2", NULL, "Card type Hitag2"), + arg_lit0("s", NULL, "Card type HitagS"), + arg_lit0("m", NULL, "Card type HitagM"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -229,9 +230,10 @@ static int CmdLFHitagEload(const char *Cmd) { bool use_ht1 = arg_get_lit(ctx, 2); bool use_ht2 = arg_get_lit(ctx, 3); bool use_hts = arg_get_lit(ctx, 4); + bool use_htm = arg_get_lit(ctx, 5); CLIParserFree(ctx); - uint8_t n = (use_ht1 + use_ht2 + use_hts); + uint8_t n = (use_ht1 + use_ht2 + use_hts + use_htm); if (n != 1) { PrintAndLogEx(ERR, "error, only specify one Hitag type"); return PM3_EINVARG; @@ -274,15 +276,24 @@ static int CmdLFHitagEload(const char *Cmd) { // check dump len.. if (dumplen == 48 || dumplen == 4 * 64) { - struct { - uint16_t len; - uint8_t *data; - } PACKED payload; - payload.len = dumplen; - memcpy(payload.data, dump, dumplen); + + lf_hitag_t *payload = calloc(1, sizeof(lf_hitag_t) + dumplen); + + if (use_ht1) + payload->type = 1; + if (use_ht2) + payload->type = 2; + if (use_hts) + payload->type = 3; + if (use_htm) + payload->type = 4; + + payload->len = dumplen; + memcpy(payload->data, dump, dumplen); clearCommandBuffer(); - SendCommandNG(CMD_LF_HITAG_ELOAD, (uint8_t *)&payload, 2 + dumplen); + SendCommandNG(CMD_LF_HITAG_ELOAD, (uint8_t *)payload, 3 + dumplen); + free(payload); } else { PrintAndLogEx(ERR, "error, wrong dump file size. got %zu", dumplen); } @@ -559,8 +570,8 @@ static int CmdLFHitagReader(const char *Cmd) { } // sanity checks - if (keylen != 0 && keylen != 4) { - PrintAndLogEx(WARNING, "Wrong KEY len expected 0 or 4, got %d", keylen); + if (keylen != 0 && keylen != 4 && keylen != 6) { + PrintAndLogEx(WARNING, "Wrong KEY len expected 0, 4 or 6, got %d", keylen); return PM3_EINVARG; } @@ -646,7 +657,7 @@ static int CmdLFHitagCheckChallenges(const char *Cmd) { void *argtable[] = { arg_param_begin, - arg_str0("f", "filename", "", "filename to load from"), + arg_str0("f", "file", "", "filename to load ( w/o ext )"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); diff --git a/client/src/cmdlfkeri.c b/client/src/cmdlfkeri.c index 8bd471049..f5df3b392 100644 --- a/client/src/cmdlfkeri.c +++ b/client/src/cmdlfkeri.c @@ -154,7 +154,7 @@ int demodKeri(bool verbose) { raw1 = bytebits_to_byte(DemodBuffer, 32); raw2 = bytebits_to_byte(DemodBuffer + 32, 32); - CmdPrintDemodBuff("x"); + CmdPrintDemodBuff("-x"); } //get internal id diff --git a/client/src/cmdlfnexwatch.c b/client/src/cmdlfnexwatch.c index d46c40046..0a85ada67 100644 --- a/client/src/cmdlfnexwatch.c +++ b/client/src/cmdlfnexwatch.c @@ -24,6 +24,8 @@ #include "cmdlft55xx.h" // clone.. #include "cmdlfem4x05.h" // #include "cliparser.h" +#include "util.h" + typedef enum { SCRAMBLE, @@ -52,7 +54,7 @@ static uint8_t nexwatch_parity(uint8_t hexid[5]) { } /// NETWATCH checksum -/// @param magic = 0xBE Quadrakey, 0x88 Nexkey +/// @param magic = 0xBE Quadrakey, 0x88 Nexkey, 0x86 Honeywell /// @param id = descrambled id (printed card number) /// @param parity = the parity based upon the scrambled raw id. static uint8_t nexwatch_checksum(uint8_t magic, uint32_t id, uint8_t parity) { @@ -108,6 +110,20 @@ static int nexwatch_scamble(NexWatchScramble_t action, uint32_t *id, uint32_t *s return PM3_SUCCESS; } +static int nexwatch_magic_bruteforce(uint32_t cn, uint8_t calc_parity, uint8_t chk) { + for (uint8_t magic = 0; magic < 255; magic++) { + uint8_t temp_checksum; + temp_checksum = nexwatch_checksum(magic, cn, calc_parity); + if (temp_checksum == chk) { + PrintAndLogEx(SUCCESS, " Magic number : " _GREEN_("0x%X"), magic); + return PM3_SUCCESS; + } + } + PrintAndLogEx(DEBUG, "DEBUG: Error - Magic number not found"); + return PM3_ESOFT; +} + + int demodNexWatch(bool verbose) { (void) verbose; // unused so far if (PSKDemod(0, 0, 100, false) != PM3_SUCCESS) { @@ -204,10 +220,13 @@ int demodNexWatch(bool verbose) { if (m_idx < ARRAYLEN(items)) { PrintAndLogEx(SUCCESS, " fingerprint : " _GREEN_("%s"), items[m_idx].desc); + } else { + nexwatch_magic_bruteforce(cn, calc_parity, chk); } PrintAndLogEx(SUCCESS, " 88bit id : " _YELLOW_("%"PRIu32) " (" _YELLOW_("0x%08"PRIx32)")", cn, cn); PrintAndLogEx(SUCCESS, " mode : %x", mode); + if (parity == calc_parity) { PrintAndLogEx(DEBUG, " parity : %s (0x%X)", _GREEN_("ok"), parity); } else { @@ -285,6 +304,8 @@ static int CmdNexWatchClone(const char *Cmd) { arg_lit0(NULL, "hc", "Honeywell credential"), arg_lit0(NULL, "q5", "optional - specify writing to Q5/T5555 tag"), arg_lit0(NULL, "em", "optional - specify writing to EM4305/4469 tag"), + arg_str0(NULL, "magic", "", "optional - magic hex data. 1 byte"), + arg_lit0(NULL, "psk2", "optional - specify writing a tag in psk2 modulation"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -298,9 +319,16 @@ static int CmdNexWatchClone(const char *Cmd) { uint32_t mode = arg_get_u32_def(ctx, 3, -1); bool use_nexkey = arg_get_lit(ctx, 4); bool use_quadrakey = arg_get_lit(ctx, 5); - bool use_unk = arg_get_lit(ctx, 6); + bool use_honeywell = arg_get_lit(ctx, 6); bool q5 = arg_get_lit(ctx, 7); bool em = arg_get_lit(ctx, 8); + + uint8_t magic_arg[2]; + int mlen = 0; + CLIGetHexWithReturn(ctx, 9, magic_arg, &mlen); + + bool use_psk2 = arg_get_lit(ctx, 10); + CLIParserFree(ctx); if (use_nexkey && use_quadrakey) { @@ -316,6 +344,13 @@ static int CmdNexWatchClone(const char *Cmd) { // 56000000 00213C9F 8F150C00 bool use_raw = (raw_len != 0); + bool use_custom_magic = (mlen != 0); + + if (mlen > 1) { + PrintAndLogEx(FAILED, "Can't specify a magic number bigger than one byte"); + return PM3_EINVARG; + } + if (use_raw && cn != -1) { PrintAndLogEx(FAILED, "Can't specify both Raw and Card id at the same time"); return PM3_EINVARG; @@ -336,20 +371,27 @@ static int CmdNexWatchClone(const char *Cmd) { } uint8_t magic = 0xBE; - if (use_nexkey) - magic = 0x88; + if (use_custom_magic) { + magic = magic_arg[0]; + } else { + if (use_nexkey) + magic = 0x88; - if (use_quadrakey) - magic = 0xBE; + if (use_quadrakey) + magic = 0xBE; - if (use_unk) - magic = 0x86; + if (use_honeywell) + magic = 0x86; + + } + PrintAndLogEx(INFO, "Magic byte selected... " _YELLOW_("0x%X"), magic); uint32_t blocks[4]; //Nexwatch - compat mode, PSK, data rate 40, 3 data blocks blocks[0] = T55x7_MODULATION_PSK1 | T55x7_BITRATE_RF_32 | 3 << T55x7_MAXBLOCK_SHIFT; char cardtype[16] = {"T55x7"}; + // Q5 if (q5) { blocks[0] = T5555_FIXED | T5555_MODULATION_MANCHESTER | T5555_SET_BITRATE(64) | T5555_ST_TERMINATOR | 3 << T5555_MAXBLOCK_SHIFT; @@ -368,6 +410,21 @@ static int CmdNexWatchClone(const char *Cmd) { raw[10] |= nexwatch_checksum(magic, cn, parity); } + if (use_psk2) { + blocks[0] = 0x00042080; + + uint8_t *res_shifted = calloc(96, sizeof(uint8_t)); + uint8_t *res = calloc(96, sizeof(uint8_t)); + + bytes_to_bytebits(raw, 12, res); + psk1TOpsk2(res, 96); + memcpy(res_shifted, &res[1], 95 * sizeof(uint8_t)); + free(res); + bits_to_array(res_shifted, 96, raw); + free(res_shifted); + } + + for (uint8_t i = 1; i < ARRAYLEN(blocks); i++) { blocks[i] = bytes_to_num(raw + ((i - 1) * 4), sizeof(uint32_t)); } @@ -408,6 +465,8 @@ static int CmdNexWatchSim(const char *Cmd) { arg_lit0(NULL, "nc", "Nexkey credential"), arg_lit0(NULL, "qc", "Quadrakey credential"), arg_lit0(NULL, "hc", "Honeywell credential"), + arg_str0(NULL, "magic", "", "optional - magic hex data. 1 byte"), + arg_lit0(NULL, "psk2", "optional - specify writing a tag in psk2 modulation"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); diff --git a/client/src/cmdlft55xx.c b/client/src/cmdlft55xx.c index e7ae82ed3..765f966e4 100644 --- a/client/src/cmdlft55xx.c +++ b/client/src/cmdlft55xx.c @@ -2210,7 +2210,7 @@ static int CmdT55xxDump(const char *Cmd) { // 1 (help) + 3 (two user specified params) + (5 T55XX_DLMODE_SINGLE) void *argtable[4 + 5] = { arg_param_begin, - arg_str0("f", "filename", "", "filename (default is generated on blk 0)"), + arg_str0("f", "file", "", "filename (default is generated on blk 0)"), arg_lit0("o", "override", "override, force pwd read despite danger to card"), arg_str0("p", "pwd", "", "password (4 hex bytes)"), }; @@ -3301,7 +3301,7 @@ static int CmdT55xxBruteForce(const char *Cmd) { uint32_t curr = 0; uint8_t found = 0; // > 0 if found xx1 xx downlink needed, 1 found - if (start_password >= end_password) { + if (start_password > end_password) { PrintAndLogEx(FAILED, "Error, start larger then end password"); return PM3_EINVARG; } @@ -3331,7 +3331,10 @@ static int CmdT55xxBruteForce(const char *Cmd) { PrintAndLogEx(NORMAL, ""); if (found) { - PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr - 1); + if (curr != end_password) { + PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr - 1); + } else + PrintAndLogEx(SUCCESS, "Found valid password: [ " _GREEN_("%08X") " ]", curr); T55xx_Print_DownlinkMode((found >> 1) & 3); } else PrintAndLogEx(WARNING, "Bruteforce failed, last tried: [ " _YELLOW_("%08X") " ]", curr); diff --git a/client/src/cmdmain.c b/client/src/cmdmain.c index f291b6dfb..8b7c6b29f 100644 --- a/client/src/cmdmain.c +++ b/client/src/cmdmain.c @@ -17,7 +17,7 @@ #include #include -#include // MingW +#include // MingW #include // calloc #include "comms.h" diff --git a/client/src/cmdparser.c b/client/src/cmdparser.c index f67e18608..0d79ebc36 100644 --- a/client/src/cmdparser.c +++ b/client/src/cmdparser.c @@ -15,6 +15,7 @@ #include "ui.h" #include "comms.h" +#include "util_posix.h" // msleep bool AlwaysAvailable(void) { return true; @@ -193,6 +194,11 @@ void CmdsHelp(const command_t Commands[]) { } int CmdsParse(const command_t Commands[], const char *Cmd) { + + if (session.client_exe_delay != 0) { + msleep(session.client_exe_delay); + } + // Help dump children if (strcmp(Cmd, "XX_internal_command_dump_XX") == 0) { dumpCommandsRecursive(Commands, 0, false); diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 977a7483b..f9f34ce20 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -498,7 +498,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr static int download_trace(void) { if (IfPm3Present() == false) { - PrintAndLogEx(FAILED, "You requested a trace upload in offline mode, consider using parameter '1' for working from Tracebuffer"); + PrintAndLogEx(FAILED, "You requested a trace upload in offline mode, consider using parameter '-1' for working from Tracebuffer"); return PM3_EINVARG; } @@ -753,7 +753,7 @@ int CmdTraceList(const char *Cmd) { if (use_buffer == false) { download_trace(); } else if (g_traceLen == 0) { - PrintAndLogEx(FAILED, "You requested a trace list in offline mode but there is no trace, consider using 'trace load' or removing parameter '1'"); + PrintAndLogEx(FAILED, "You requested a trace list in offline mode but there is no trace, consider using 'trace load' or removing parameter '-1'"); return PM3_EINVARG; } diff --git a/client/src/crypto/asn1dump.c b/client/src/crypto/asn1dump.c index 84ff1d4a2..f09c4284f 100644 --- a/client/src/crypto/asn1dump.c +++ b/client/src/crypto/asn1dump.c @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +// //----------------------------------------------------------------------------- // Copyright (C) 2018 Merlok // // This code is licensed to you under the terms of the GNU GPL, version 2 or, @@ -197,14 +197,14 @@ static unsigned long asn1_value_integer(const struct tlv *tlv, unsigned start, u i = start; for (; i < end - 1; i += 2) { - ret *= 10; + ret = ret << 4; // was: ret*=10 ret += tlv->value[i / 2] >> 4; - ret *= 10; + ret = ret << 4; // was: ret*=10 ret += tlv->value[i / 2] & 0xf; } if (end & 1) { - ret *= 10; + ret = ret << 4; // was: ret*=10 ret += tlv->value[end / 2] >> 4; } @@ -227,10 +227,11 @@ static void asn1_tag_dump_integer(const struct tlv *tlv, const struct asn1_tag * for (size_t i = 0; i < tlv->len; i++) { val = (val << 8) + tlv->value[i]; } - PrintAndLogEx(NORMAL, " value4b: %d", val); + PrintAndLogEx(NORMAL, " value: %d (0x%08X)", val, val); return; } - PrintAndLogEx(NORMAL, " value: %lu", asn1_value_integer(tlv, 0, tlv->len * 2)); + uint32_t val = asn1_value_integer(tlv, 0, tlv->len * 2); + PrintAndLogEx(NORMAL, " value: %" PRIu32 " (0x%X)", val, val); } static char *asn1_oid_description(const char *oid, bool with_group_desc) { @@ -326,7 +327,7 @@ bool asn1_tag_dump(const struct tlv *tlv, int level, bool *candump) { */ PrintAndLogEx(INFO, - "%*s-- %2x [%02zx] '"_YELLOW_("%s") "'" NOLF + "%*s-- %02X [%02ZX] '"_YELLOW_("%s") "'" NOLF , (level * 4) , " " , tlv->tag diff --git a/client/src/crypto/libpcrypto.c b/client/src/crypto/libpcrypto.c index 723753d5f..32686b199 100644 --- a/client/src/crypto/libpcrypto.c +++ b/client/src/crypto/libpcrypto.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,43 @@ #include #include "util.h" #include "ui.h" + +void des_encrypt(void *out, const void *in, const void *key) { + mbedtls_des_context ctx; + mbedtls_des_setkey_enc(&ctx, key); + mbedtls_des_crypt_ecb(&ctx, in, out); + mbedtls_des_free(&ctx); +} + +void des_decrypt(void *out, const void *in, const void *key) { + mbedtls_des_context ctx; + mbedtls_des_setkey_dec(&ctx, key); + mbedtls_des_crypt_ecb(&ctx, in, out); + mbedtls_des_free(&ctx); +} + +void des_encrypt_ecb(void *out, const void *in, const int length, const void *key) { + 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) + 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) { + mbedtls_des_context ctx; + mbedtls_des_setkey_enc(&ctx, key); + mbedtls_des_crypt_cbc(&ctx, MBEDTLS_DES_ENCRYPT, length, iv, in, out); +} + +void des_decrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv) { + mbedtls_des_context ctx; + mbedtls_des_setkey_dec(&ctx, key); + mbedtls_des_crypt_cbc(&ctx, MBEDTLS_DES_DECRYPT, length, iv, in, out); +} + // NIST Special Publication 800-38A — Recommendation for block cipher modes of operation: methods and techniques, 2001. int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length) { uint8_t iiv[16] = {0}; @@ -498,3 +536,26 @@ exit: PrintAndLogEx(NORMAL, _RED_("failed\n")); return res; } + +void bin_xor(uint8_t *d1, uint8_t *d2, size_t len) { + for (size_t i = 0; i < len; i++) + d1[i] = d1[i] ^ d2[i]; +} + +void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen) { + *ddatalen = sdatalen + 1; + *ddatalen += blocklen - *ddatalen % blocklen; + memset(ddata, 0, *ddatalen); + memcpy(ddata, sdata, sdatalen); + ddata[sdatalen] = ISO9797_M2_PAD_BYTE; +} + +size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen) { + for (int i = datalen; i > 0; i--) { + if (data[i - 1] == 0x80) + return i - 1; + if (data[i - 1] != 0x00) + return 0; + } + return 0; +} diff --git a/client/src/crypto/libpcrypto.h b/client/src/crypto/libpcrypto.h index 099fd4423..3f3bdb45b 100644 --- a/client/src/crypto/libpcrypto.h +++ b/client/src/crypto/libpcrypto.h @@ -16,6 +16,13 @@ #include #include +void des_encrypt(void *out, const void *in, const void *key); +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); +void des_decrypt_ecb(void *out, const void *in, const int length, const void *key); +void des_encrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv); +void des_decrypt_cbc(void *out, const void *in, const int length, const void *key, uint8_t *iv); + int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length); int aes_cmac(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int length); @@ -35,4 +42,11 @@ char *ecdsa_get_error(int ret); int ecdsa_nist_test(bool verbose); +void bin_xor(uint8_t *d1, uint8_t *d2, size_t len); + +#define ISO9797_M2_PAD_BYTE 0x80 +void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen); +size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen); + + #endif /* libpcrypto.h */ diff --git a/client/src/emv/emvcore.c b/client/src/emv/emvcore.c index 7d0ab8ce4..2522aa8b5 100644 --- a/client/src/emv/emvcore.c +++ b/client/src/emv/emvcore.c @@ -758,6 +758,7 @@ int trDDA(Iso7816CommandChannel channel, bool decodeTLV, struct tlvdb *tlv) { tlvdb_free(atc_db); return 9; } + tlvdb_free(atc_db); } else { struct tlvdb *dac_db = emv_pki_recover_dac(issuer_pk, tlv, sda_tlv); diff --git a/client/src/fileutils.c b/client/src/fileutils.c index 415a0bb3c..2f18e0845 100644 --- a/client/src/fileutils.c +++ b/client/src/fileutils.c @@ -1292,12 +1292,13 @@ out: } int loadFileDICTIONARY(const char *preferredName, void *data, size_t *datalen, uint8_t keylen, uint32_t *keycnt) { - // t5577 == 4bytes + // t5577 == 4 bytes // mifare == 6 bytes // mf plus == 16 bytes + // mf desfire == 3des3k 24 bytes // iclass == 8 bytes // default to 6 bytes. - if (keylen != 4 && keylen != 6 && keylen != 8 && keylen != 16) { + if (keylen != 4 && keylen != 6 && keylen != 8 && keylen != 16 && keylen != 24) { keylen = 6; } @@ -1404,9 +1405,10 @@ int loadFileDICTIONARY_safe(const char *preferredName, void **pdata, uint8_t key // t5577 == 4bytes // mifare == 6 bytes // mf plus == 16 bytes + // mf desfire == 3des3k 24 bytes // iclass == 8 bytes // default to 6 bytes. - if (keylen != 4 && keylen != 6 && keylen != 8 && keylen != 16) { + if (keylen != 4 && keylen != 6 && keylen != 8 && keylen != 16 && keylen != 24) { keylen = 6; } diff --git a/client/src/flash.c b/client/src/flash.c index 2f5f037c3..6bca04d43 100644 --- a/client/src/flash.c +++ b/client/src/flash.c @@ -123,7 +123,8 @@ static int build_segs_from_phdrs(flash_file_t *ctx, FILE *fd, Elf32_Phdr *phdrs, if (paddr < FLASH_START || (paddr + filesz) > flash_end) { PrintAndLogEx(ERR, "Error: PHDR is not contained in Flash"); if ((paddr + filesz) > flash_end) { - PrintAndLogEx(ERR, "Firmware probably too big for your device"); + PrintAndLogEx(ERR, "Firmware is probably too big for your device"); + PrintAndLogEx(ERR, "See README.md for information on compiling for platforms with 256KB of flash memory"); } return PM3_EFILE; } diff --git a/client/src/guidummy.cpp b/client/src/guidummy.cpp index 8557b0015..886bc1516 100644 --- a/client/src/guidummy.cpp +++ b/client/src/guidummy.cpp @@ -21,6 +21,18 @@ extern "C" void ShowGraphWindow(void) { extern "C" void HideGraphWindow(void) {} extern "C" void RepaintGraphWindow(void) {} + +extern "C" void ShowPictureWindow(char *fn) { + static int warned = 0; + + if (!warned) { + printf("No GUI in this build!\n"); + warned = 1; + } +} +extern "C" void HidePictureWindow(void) {} +extern "C" void RepaintPictureWindow(void) {} + extern "C" void MainGraphics() {} extern "C" void InitGraphics(int argc, char **argv) {} extern "C" void ExitGraphics(void) {} diff --git a/client/src/iso7816/iso7816core.c b/client/src/iso7816/iso7816core.c index fae43b9dd..4168036a4 100644 --- a/client/src/iso7816/iso7816core.c +++ b/client/src/iso7816/iso7816core.c @@ -37,7 +37,11 @@ static isodep_state_t isodep_state = ISODEP_INACTIVE; void SetISODEPState(isodep_state_t state) { isodep_state = state; if (APDULogging) { - PrintAndLogEx(SUCCESS, ">>>> ISODEP -> %s%s%s", isodep_state == ISODEP_INACTIVE ? "inactive" : "", isodep_state == ISODEP_NFCA ? "NFC-A" : "", isodep_state == ISODEP_NFCB ? "NFC-B" : ""); + PrintAndLogEx(SUCCESS, "Setting ISODEP -> %s%s%s" + , isodep_state == ISODEP_INACTIVE ? "inactive" : "" + , isodep_state == ISODEP_NFCA ? _GREEN_("NFC-A") : "" + , isodep_state == ISODEP_NFCB ? _GREEN_("NFC-B") : "" + ); } } @@ -51,46 +55,50 @@ int Iso7816Connect(Iso7816CommandChannel channel) { } // Try to 14a // select with no disconnect and set frameLength - int selres = SelectCard14443A_4(false, NULL); - if (selres == PM3_SUCCESS) { + int res = SelectCard14443A_4(false, false, NULL); + if (res == PM3_SUCCESS) { SetISODEPState(ISODEP_NFCA); return PM3_SUCCESS; } PrintAndLogEx(DEBUG, "No 14a tag spotted, trying 14b"); // If not 14a, try to 14b - selres = select_card_14443b_4(false, NULL); - if (selres == PM3_SUCCESS) { + res = select_card_14443b_4(false, NULL); + if (res == PM3_SUCCESS) { SetISODEPState(ISODEP_NFCB); return PM3_SUCCESS; } + PrintAndLogEx(DEBUG, "No 14b tag spotted, failed to find any tag."); - return selres; + return res; } -int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool includeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - uint8_t data[APDU_RES_LEN] = {0}; +int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool leave_field_on, + sAPDU apdu, bool include_le, uint16_t le, uint8_t *result, + size_t max_result_len, size_t *result_len, uint16_t *sw) { - *ResultLen = 0; - if (sw) *sw = 0; - uint16_t isw = 0; - int res = 0; + *result_len = 0; + if (sw) { + *sw = 0; + } - if (ActivateField) { + if (activate_field) { DropFieldEx(channel); msleep(50); } // COMPUTE APDU int datalen = 0; - if (includeLe) { - if (Le == 0) { - Le = 0x100; + if (include_le) { + if (le == 0) { + le = 0x100; } } else { - Le = 0; + le = 0; } - if (APDUEncodeS(&apdu, false, Le, data, &datalen)) { + + uint8_t data[APDU_RES_LEN] = {0}; + if (APDUEncodeS(&apdu, false, le, data, &datalen)) { PrintAndLogEx(ERR, "APDU encoding error."); return 201; } @@ -98,59 +106,68 @@ int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool ActivateField, bool Le if (APDULogging) PrintAndLogEx(SUCCESS, ">>>> %s", sprint_hex(data, datalen)); + int res = 0; + switch (channel) { - case CC_CONTACTLESS: + case CC_CONTACTLESS: { + switch (GetISODEPState()) { case ISODEP_NFCA: - res = ExchangeAPDU14a(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + res = ExchangeAPDU14a(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len); break; case ISODEP_NFCB: - res = exchange_14b_apdu(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen, 4000); + res = exchange_14b_apdu(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len, 4000); break; case ISODEP_INACTIVE: - if (! ActivateField) { + if (activate_field == false) { PrintAndLogEx(FAILED, "Field currently inactive, cannot send an APDU"); return PM3_EIO; } - res = ExchangeAPDU14a(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + res = ExchangeAPDU14a(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len); if (res != PM3_SUCCESS) { - res = exchange_14b_apdu(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen, 4000); + res = exchange_14b_apdu(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len, 4000); } break; } + if (res != PM3_SUCCESS) { return res; } break; - case CC_CONTACT: + } + case CC_CONTACT: { res = 1; - if (IfPm3Smartcard()) - res = ExchangeAPDUSC(false, data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen); + if (IfPm3Smartcard()) { + res = ExchangeAPDUSC(false, data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len); + } if (res) { return res; } break; + } } if (APDULogging) - PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen)); + PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(result, *result_len)); - if (*ResultLen < 2) { + if (*result_len < 2) { return 200; } - *ResultLen -= 2; - isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; - if (sw) + *result_len -= 2; + uint16_t isw = (result[*result_len] * 0x0100) + result[*result_len + 1]; + + if (sw) { *sw = isw; + } if (isw != 0x9000) { if (APDULogging) { if (*sw >> 8 == 0x61) { - PrintAndLogEx(ERR, "APDU chaining len:%02x -->", *sw & 0xff); + PrintAndLogEx(ERR, "APDU chaining len %02x", *sw & 0xFF); } else { - PrintAndLogEx(ERR, "APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff)); + PrintAndLogEx(ERR, "APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xFF)); return 5; } } @@ -158,10 +175,32 @@ int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool ActivateField, bool Le return PM3_SUCCESS; } -int Iso7816Exchange(Iso7816CommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - return Iso7816ExchangeEx(channel, false, LeaveFieldON, apdu, false, 0, Result, MaxResultLen, ResultLen, sw); +int Iso7816Exchange(Iso7816CommandChannel channel, bool leave_field_on, sAPDU apdu, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return Iso7816ExchangeEx(channel + , false + , leave_field_on + , apdu + , false + , 0 + , result + , max_result_len + , result_len + , sw + ); } -int Iso7816Select(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { - return Iso7816ExchangeEx(channel, ActivateField, LeaveFieldON, (sAPDU) {0x00, 0xa4, 0x04, 0x00, AIDLen, AID}, (channel == CC_CONTACTLESS), 0, Result, MaxResultLen, ResultLen, sw); +int Iso7816Select(Iso7816CommandChannel channel, bool activate_field, bool leave_field_on, uint8_t *aid, size_t aid_len, + uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + + return Iso7816ExchangeEx(channel + , activate_field + , leave_field_on + , (sAPDU) {0x00, 0xa4, 0x04, 0x00, aid_len, aid} + , (channel == CC_CONTACTLESS) + , 0 + , result + , max_result_len + , result_len + , sw + ); } diff --git a/client/src/iso7816/iso7816core.h b/client/src/iso7816/iso7816core.h index 8c0b44629..003db1256 100644 --- a/client/src/iso7816/iso7816core.h +++ b/client/src/iso7816/iso7816core.h @@ -12,9 +12,7 @@ #define ISO7816CORE_H__ #include "common.h" - #include - #include "apduinfo.h" #define APDU_RES_LEN 260 @@ -41,9 +39,14 @@ isodep_state_t GetISODEPState(void); int Iso7816Connect(Iso7816CommandChannel channel); // exchange -int Iso7816Exchange(Iso7816CommandChannel channel, bool LeaveFieldON, sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); -int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); +int Iso7816Exchange(Iso7816CommandChannel channel, bool leave_field_on, sAPDU apdu, uint8_t *result, size_t max_result_len, + size_t *result_len, uint16_t *sw); + +int Iso7816ExchangeEx(Iso7816CommandChannel channel, bool activate_field, bool leave_field_on, sAPDU apdu, bool include_le, + uint16_t le, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); // search application -int Iso7816Select(Iso7816CommandChannel channel, bool ActivateField, bool LeaveFieldON, uint8_t *AID, size_t AIDLen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); +int Iso7816Select(Iso7816CommandChannel channel, bool activate_field, bool leave_field_on, uint8_t *aid, size_t aid_len, + uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); + #endif diff --git a/client/src/mifare/desfire_crypto.c b/client/src/mifare/desfire_crypto.c index 1cf043a7b..fd3cb427d 100644 --- a/client/src/mifare/desfire_crypto.c +++ b/client/src/mifare/desfire_crypto.c @@ -1,5 +1,6 @@ /*- * Copyright (C) 2010, Romain Tartiere. + * Copyright (C) 2021 Merlok * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the @@ -30,6 +31,7 @@ #include #include #include "commonutil.h" +#include "crypto/libpcrypto.h" #include "aes.h" #include "des.h" #include "ui.h" @@ -51,25 +53,31 @@ static inline void update_key_schedules(desfirekey_t key) { // } } +int desfire_get_key_length(enum DESFIRE_CRYPTOALGO key_type) { + switch (key_type) { + case T_DES: + return 8; + case T_3DES: + return 16; + case T_3K3DES: + return 24; + case T_AES: + return 16; + } + return 0; +} + /******************************************************************************/ -void des_encrypt(void *out, const void *in, const void *key) { - mbedtls_des_context ctx; - mbedtls_des_setkey_enc(&ctx, key); - mbedtls_des_crypt_ecb(&ctx, in, out); -} - -void des_decrypt(void *out, const void *in, const void *key) { - mbedtls_des_context ctx; - mbedtls_des_setkey_dec(&ctx, key); - mbedtls_des_crypt_ecb(&ctx, in, out); -} - void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode) { - if (length % 8) return; + if (length % 8) + return; + mbedtls_des3_context ctx3; - if (keymode == 2) mbedtls_des3_set2key_dec(&ctx3, key); - else mbedtls_des3_set3key_dec(&ctx3, key); + if (keymode == 2) + mbedtls_des3_set2key_dec(&ctx3, key); + else + mbedtls_des3_set3key_dec(&ctx3, key); uint8_t i; unsigned char temp[8]; @@ -81,8 +89,9 @@ void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, mbedtls_des3_crypt_ecb(&ctx3, tin, tout); - for (i = 0; i < 8; i++) + for (i = 0; i < 8; i++) { tout[i] = (unsigned char)(tout[i] ^ iv[i]); + } memcpy(iv, temp, 8); @@ -93,18 +102,24 @@ void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, } void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode) { - if (length % 8) return; + if (length % 8) + return; + mbedtls_des3_context ctx3; - if (keymode == 2) mbedtls_des3_set2key_enc(&ctx3, key); - else mbedtls_des3_set3key_enc(&ctx3, key); + + if (keymode == 2) + mbedtls_des3_set2key_enc(&ctx3, key); + else + mbedtls_des3_set3key_enc(&ctx3, key); uint8_t i; uint8_t *tin = (uint8_t *) in; uint8_t *tout = (uint8_t *) out; while (length > 0) { - for (i = 0; i < 8; i++) + for (i = 0; i < 8; i++) { tin[i] = (unsigned char)(tin[i] ^ iv[i]); + } mbedtls_des3_crypt_ecb(&ctx3, tin, tout); @@ -120,8 +135,9 @@ void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, un void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key) { uint8_t data[8]; memcpy(data, value, 8); - for (int n = 0; n < 8; n++) - data[n] &= 0xfe; + for (int n = 0; n < 8; n++) { + data[n] &= 0xFE; + } Desfire_des_key_new_with_version(data, key); } @@ -137,10 +153,12 @@ void Desfire_des_key_new_with_version(const uint8_t value[8], desfirekey_t key) void Desfire_3des_key_new(const uint8_t value[16], desfirekey_t key) { uint8_t data[16]; memcpy(data, value, 16); - for (int n = 0; n < 8; n++) - data[n] &= 0xfe; - for (int n = 8; n < 16; n++) + for (int n = 0; n < 8; n++) { + data[n] &= 0xFE; + } + for (int n = 8; n < 16; n++) { data[n] |= 0x01; + } Desfire_3des_key_new_with_version(data, key); } @@ -155,8 +173,9 @@ void Desfire_3des_key_new_with_version(const uint8_t value[16], desfirekey_t key void Desfire_3k3des_key_new(const uint8_t value[24], desfirekey_t key) { uint8_t data[24]; memcpy(data, value, 24); - for (int n = 0; n < 8; n++) - data[n] &= 0xfe; + for (int n = 0; n < 8; n++) { + data[n] &= 0xFE; + } Desfire_3k3des_key_new_with_version(data, key); } @@ -173,7 +192,6 @@ void Desfire_aes_key_new(const uint8_t value[16], desfirekey_t key) { } void Desfire_aes_key_new_with_version(const uint8_t value[16], uint8_t version, desfirekey_t key) { - if (key != NULL) { memcpy(key->data, value, 16); key->type = T_AES; @@ -193,13 +211,15 @@ uint8_t Desfire_key_get_version(desfirekey_t key) { void Desfire_key_set_version(desfirekey_t key, uint8_t version) { for (int n = 0; n < 8; n++) { uint8_t version_bit = ((version & (1 << (7 - n))) >> (7 - n)); - key->data[n] &= 0xfe; + + key->data[n] &= 0xFE; key->data[n] |= version_bit; + if (key->type == T_DES) { key->data[n + 8] = key->data[n]; } else { // Write ~version to avoid turning a 3DES key into a DES key - key->data[n + 8] &= 0xfe; + key->data[n + 8] &= 0xFE; key->data[n + 8] |= ~version_bit; } } @@ -268,15 +288,17 @@ void cmac_generate_subkeys(desfirekey_t key, MifareCryptoDirection direction) { memcpy(key->cmac_sk1, l, kbs); txor = l[0] & 0x80; lsl(key->cmac_sk1, kbs); - if (txor) + if (txor) { key->cmac_sk1[kbs - 1] ^= R; + } // Used to compute CMAC on the last block if non-complete memcpy(key->cmac_sk2, key->cmac_sk1, kbs); txor = key->cmac_sk1[0] & 0x80; lsl(key->cmac_sk2, kbs); - if (txor) + if (txor) { key->cmac_sk2[kbs - 1] ^= R; + } } void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t len, uint8_t *cmac) { @@ -286,6 +308,10 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t le } uint8_t *buffer = calloc(padded_data_length(len, kbs), sizeof(uint8_t)); + if (buffer == NULL) { + PrintAndLogEx(WARNING, "failed to allocate memory"); + return; + } memcpy(buffer, data, len); @@ -313,12 +339,24 @@ void mifare_kdf_an10922(const desfirekey_t key, const uint8_t *data, size_t len) return; } + // AES uses 16 byte IV + if (kbs < 16) + kbs = 16; + cmac_generate_subkeys(key, MCD_SEND); - uint8_t *buffer = calloc(kbs2, sizeof(uint8_t)); + // reserv atleast 32bytes. + uint8_t *buffer = calloc(len, sizeof(uint8_t)); + if (buffer == NULL) { + PrintAndLogEx(WARNING, "failed to allocate memory"); + return; + } uint8_t *ivect = calloc(kbs, sizeof(uint8_t)); - - memset(ivect, 0, kbs); + if (ivect == NULL) { + PrintAndLogEx(WARNING, "failed to allocate memory"); + free(buffer); + return; + } buffer[0] = 0x01; memcpy(&buffer[1], data, len++); @@ -344,11 +382,9 @@ void mifare_kdf_an10922(const desfirekey_t key, const uint8_t *data, size_t len) free(buffer); } -size_t key_block_size(const desfirekey_t key) { - if (key == NULL) - return 0; +size_t desfire_get_key_block_length(enum DESFIRE_CRYPTOALGO key_type) { size_t block_size = 8; - switch (key->type) { + switch (key_type) { case T_DES: case T_3DES: case T_3K3DES: @@ -361,6 +397,13 @@ size_t key_block_size(const desfirekey_t key) { return block_size; } +size_t key_block_size(const desfirekey_t key) { + if (key == NULL) { + return 0; + } + return desfire_get_key_block_length(key->type); +} + /* * Size of MACing produced with the key. */ @@ -924,19 +967,3 @@ void mifare_cypher_blocks_chained(desfiretag_t tag, desfirekey_t key, uint8_t *i offset += block_size; } } - -void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc) { - crc32_ex(data, len, crc); -} - -void desfire_crc32_append(uint8_t *data, const size_t len) { - crc32_ex(data, len, data + len); -} - -void iso14443a_crc_append(uint8_t *data, size_t len) { - return compute_crc(CRC_14443_A, data, len, data + len, data + len + 1); -} - -void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc) { - return compute_crc(CRC_14443_A, data, len, pbtCrc, pbtCrc + 1); -} diff --git a/client/src/mifare/desfire_crypto.h b/client/src/mifare/desfire_crypto.h index ecc6fc4a5..c4c879ef3 100644 --- a/client/src/mifare/desfire_crypto.h +++ b/client/src/mifare/desfire_crypto.h @@ -1,12 +1,33 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * Copyright (C) 2021 Merlok + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + * $Id$ + */ + #ifndef __DESFIRE_CRYPTO_H #define __DESFIRE_CRYPTO_H #include "common.h" #include "mifare.h" // structs #include "crc32.h" +#include "crypto/libpcrypto.h" +#include "mifare/desfirecrypto.h" -#define MAX_CRYPTO_BLOCK_SIZE 16 /* Mifare DESFire EV1 Application crypto operations */ #define APPLICATION_CRYPTO_DES 0x00 #define APPLICATION_CRYPTO_3K3DES 0x40 @@ -53,12 +74,8 @@ typedef enum { /* Error code managed by the library */ #define CRYPTO_ERROR 0x01 -enum DESFIRE_CRYPTOALGO { - T_DES = 0x00, - T_3DES = 0x01, //aka 2K3DES - T_3K3DES = 0x02, - T_AES = 0x03 -}; +int desfire_get_key_length(enum DESFIRE_CRYPTOALGO key_type); +size_t desfire_get_key_block_length(enum DESFIRE_CRYPTOALGO key_type); enum DESFIRE_AUTH_SCHEME { AS_LEGACY, @@ -102,8 +119,6 @@ typedef unsigned long DES3_KS[48][2]; /* Triple-DES key schedule */ extern int Asmversion; /* 1 if we're linked with an asm version, 0 if C */ -void des_encrypt(void *out, const void *in, const void *key); -void des_decrypt(void *out, const void *in, const void *key); void tdes_nxp_receive(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode); void tdes_nxp_send(const void *in, void *out, size_t length, const void *key, unsigned char iv[8], int keymode); void Desfire_des_key_new(const uint8_t value[8], desfirekey_t key); @@ -132,8 +147,4 @@ void cmac(const desfirekey_t key, uint8_t *ivect, const uint8_t *data, size_t l void mifare_kdf_an10922(const desfirekey_t key, const uint8_t *data, size_t len); -void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc); -void desfire_crc32_append(uint8_t *data, const size_t len); -void iso14443a_crc_append(uint8_t *data, size_t len); -void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc); #endif diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c new file mode 100644 index 000000000..b0c4d9b8d --- /dev/null +++ b/client/src/mifare/desfirecore.c @@ -0,0 +1,1076 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 Romain Tartiere. +// Copyright (C) 2014 Iceman +// Copyright (C) 2021 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency Desfire core functions +//----------------------------------------------------------------------------- +// Info from here and many other sources from the public internet sites +// https://github.com/revk/DESFireAES +// https://github.com/step21/desfire_rfid +// https://github.com/patsys/desfire-python/blob/master/Desfire/DESFire.py +//----------------------------------------------------------------------------- + +#include "desfirecore.h" +#include +#include +#include +#include "aes.h" +#include "ui.h" +#include "crc.h" +#include "crc16.h" // crc16 ccitt +#include "crc32.h" +#include "protocols.h" +#include "commonutil.h" +#include "cmdhf14a.h" +#include "iso7816/apduinfo.h" // APDU manipulation / errorcodes +#include "iso7816/iso7816core.h" // APDU logging +#include "util_posix.h" // msleep +#include "mifare/desfire_crypto.h" +#include "desfiresecurechan.h" + +const CLIParserOption DesfireAlgoOpts[] = { + {T_DES, "des"}, + {T_3DES, "2tdea"}, + {T_3K3DES, "3tdea"}, + {T_AES, "aes"}, + {0, NULL}, +}; +const size_t DesfireAlgoOptsLen = ARRAY_LENGTH(DesfireAlgoOpts); + +const CLIParserOption DesfireKDFAlgoOpts[] = { + {MFDES_KDF_ALGO_NONE, "none"}, + {MFDES_KDF_ALGO_AN10922, "an10922"}, + {MFDES_KDF_ALGO_GALLAGHER, "gallagher"}, + {0, NULL}, +}; +const size_t DesfireKDFAlgoOptsLen = ARRAY_LENGTH(DesfireKDFAlgoOpts); + +const CLIParserOption DesfireCommunicationModeOpts[] = { + {DCMPlain, "plain"}, + {DCMMACed, "mac"}, + {DCMEncrypted, "encrypt"}, + {0, NULL}, +}; +const size_t DesfireCommunicationModeOptsLen = ARRAY_LENGTH(DesfireCommunicationModeOpts); + +const CLIParserOption DesfireCommandSetOpts[] = { + {DCCNative, "native"}, + {DCCNativeISO, "niso"}, + {DCCISO, "iso"}, + {0, NULL}, +}; +const size_t DesfireCommandSetOptsLen = ARRAY_LENGTH(DesfireCommandSetOpts); + +const CLIParserOption DesfireSecureChannelOpts[] = { + {DACd40, "d40"}, + {DACEV1, "ev1"}, + {DACEV2, "ev2"}, + {0, NULL}, +}; +const size_t DesfireSecureChannelOptsLen = ARRAY_LENGTH(DesfireSecureChannelOpts); + +static const char *getstatus(uint16_t *sw) { + if (sw == NULL) return "--> sw argument error. This should never happen !"; + if (((*sw >> 8) & 0xFF) == 0x91) { + switch (*sw & 0xFF) { + case MFDES_E_OUT_OF_EEPROM: + return "Out of Eeprom, insufficient NV-Memory to complete command"; + case MFDES_E_ILLEGAL_COMMAND_CODE: + return "Command code not supported"; + + case MFDES_E_INTEGRITY_ERROR: + return "CRC or MAC does not match data / Padding bytes invalid"; + + case MFDES_E_NO_SUCH_KEY: + return "Invalid key number specified"; + + case MFDES_E_LENGTH: + return "Length of command string invalid"; + + case MFDES_E_PERMISSION_DENIED: + return "Current configuration/status does not allow the requested command"; + + case MFDES_E_PARAMETER_ERROR: + return "Value of the parameter(s) invalid"; + + case MFDES_E_APPLICATION_NOT_FOUND: + return "Requested AID not present on PICC"; + + case MFDES_E_APPL_INTEGRITY: + return "Application integrity error, application will be disabled"; + + case MFDES_E_AUTHENTIFICATION_ERROR: + return "Current authentication status does not allow the requested command"; + + case MFDES_E_BOUNDARY: + return "Attempted to read/write data from/to beyond the file's/record's limit"; + + case MFDES_E_PICC_INTEGRITY: + return "PICC integrity error, PICC will be disabled"; + + case MFDES_E_COMMAND_ABORTED: + return "Previous command was not fully completed / Not all Frames were requested or provided by the PCD"; + + case MFDES_E_PICC_DISABLED: + return "PICC was disabled by an unrecoverable error"; + + case MFDES_E_COUNT: + return "Application count is limited to 28, not addition CreateApplication possible"; + + case MFDES_E_DUPLICATE: + return "Duplicate entry: File/Application/ISO Text does already exist"; + + case MFDES_E_EEPROM: + return "Eeprom error due to loss of power, internal backup/rollback mechanism activated"; + + case MFDES_E_FILE_NOT_FOUND: + return "Specified file number does not exist"; + + case MFDES_E_FILE_INTEGRITY: + return "File integrity error, file will be disabled"; + + default: + return "Unknown error"; + } + } + return "Unknown error"; +} + +const char *DesfireGetErrorString(int res, uint16_t *sw) { + switch (res) { + case PM3_EAPDU_FAIL: + return getstatus(sw); + case PM3_EUNDEF: + return "Undefined error"; + case PM3_EINVARG: + return "Invalid argument(s)"; + case PM3_EDEVNOTSUPP: + return "Operation not supported by device"; + case PM3_ETIMEOUT: + return "Operation timed out"; + case PM3_EOPABORTED: + return "Operation aborted (by user)"; + case PM3_ENOTIMPL: + return "Not (yet) implemented"; + case PM3_ERFTRANS: + return "Error while RF transmission"; + case PM3_EIO: + return "Input / output error"; + case PM3_EOVFLOW: + return "Buffer overflow"; + case PM3_ESOFT: + return "Software error"; + case PM3_EFLASH: + return "Flash error"; + case PM3_EMALLOC: + return "Memory allocation error"; + case PM3_EFILE: + return "File error"; + case PM3_ENOTTY: + return "Generic TTY error"; + case PM3_EINIT: + return "Initialization error"; + case PM3_EWRONGANSWER: + return "Expected a different answer error"; + case PM3_EOUTOFBOUND: + return "Memory out-of-bounds error"; + case PM3_ECARDEXCHANGE: + return "Exchange with card error"; + case PM3_EAPDU_ENCODEFAIL: + return "Failed to create APDU"; + case PM3_ENODATA: + return "No data"; + case PM3_EFATAL: + return "Fatal error"; + default: + break; + } + return ""; +} + +uint32_t DesfireAIDByteToUint(uint8_t *data) { + return data[0] + (data[1] << 8) + (data[2] << 16); +} + +void DesfireAIDUintToByte(uint32_t aid, uint8_t *data) { + data[0] = aid & 0xff; + data[1] = (aid >> 8) & 0xff; + data[2] = (aid >> 16) & 0xff; +} + +void DesfirePrintContext(DesfireContext *ctx) { + PrintAndLogEx(INFO, "Key num: %d Key algo: %s Key[%d]: %s", + ctx->keyNum, + CLIGetOptionListStr(DesfireAlgoOpts, ctx->keyType), + desfire_get_key_length(ctx->keyType), + sprint_hex(ctx->key, + desfire_get_key_length(ctx->keyType))); + + if (ctx->kdfAlgo != MFDES_KDF_ALGO_NONE) + PrintAndLogEx(INFO, "KDF algo: %s KDF input[%d]: %s", CLIGetOptionListStr(DesfireKDFAlgoOpts, ctx->kdfAlgo), ctx->kdfInputLen, sprint_hex(ctx->kdfInput, ctx->kdfInputLen)); + + PrintAndLogEx(INFO, "Secure channel: %s Command set: %s Communication mode: %s", + CLIGetOptionListStr(DesfireSecureChannelOpts, ctx->secureChannel), + CLIGetOptionListStr(DesfireCommandSetOpts, ctx->cmdSet), + CLIGetOptionListStr(DesfireCommunicationModeOpts, ctx->commMode)); + + if (DesfireIsAuthenticated(ctx)) { + PrintAndLogEx(INFO, "Session key MAC [%d]: %s ", + desfire_get_key_length(ctx->keyType), + sprint_hex(ctx->sessionKeyMAC, desfire_get_key_length(ctx->keyType))); + PrintAndLogEx(INFO, " ENC: %s", + sprint_hex(ctx->sessionKeyEnc, desfire_get_key_length(ctx->keyType))); + PrintAndLogEx(INFO, " IV [%zu]: %s", + desfire_get_key_block_length(ctx->keyType), + sprint_hex(ctx->IV, desfire_get_key_block_length(ctx->keyType))); + + } +} + +static int DESFIRESendApdu(bool activate_field, sAPDU apdu, uint8_t *result, uint32_t max_result_len, uint32_t *result_len, uint16_t *sw) { + if (result_len) *result_len = 0; + if (sw) *sw = 0; + + uint16_t isw = 0; + int res = 0; + + if (activate_field) { + DropField(); + msleep(50); + } + + uint8_t data[APDU_RES_LEN] = {0}; + + // COMPUTE APDU + int datalen = 0; + if (APDUEncodeS(&apdu, false, 0x100, data, &datalen)) { // 100 == with Le + PrintAndLogEx(ERR, "APDU encoding error."); + return PM3_EAPDU_ENCODEFAIL; + } + + if (GetAPDULogging()) + PrintAndLogEx(SUCCESS, ">>>> %s", sprint_hex(data, datalen)); + + res = ExchangeAPDU14a(data, datalen, activate_field, true, result, max_result_len, (int *)result_len); + if (res != PM3_SUCCESS) { + return res; + } + + if (GetAPDULogging()) + PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(result, *result_len)); + + if (*result_len < 2) { + return PM3_SUCCESS; + } + + *result_len -= 2; + isw = (result[*result_len] << 8) + result[*result_len + 1]; + if (sw) + *sw = isw; + + if (isw != 0x9000 && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_OPERATION_OK) && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_SIGNATURE) && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_ADDITIONAL_FRAME) && + isw != DESFIRE_GET_ISO_STATUS(MFDES_S_NO_CHANGES)) { + if (GetAPDULogging()) { + if (isw >> 8 == 0x61) { + PrintAndLogEx(ERR, "APDU chaining len: 0x%02x -->", isw & 0xff); + } else { + PrintAndLogEx(ERR, "APDU(%02x%02x) ERROR: [0x%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(isw >> 8, isw & 0xff)); + return PM3_EAPDU_FAIL; + } + } + return PM3_EAPDU_FAIL; + } + return PM3_SUCCESS; +} + +static int DESFIRESendRaw(bool activate_field, uint8_t *data, size_t datalen, uint8_t *result, uint32_t max_result_len, uint32_t *result_len, uint8_t *respcode) { + *result_len = 0; + if (respcode) *respcode = 0xff; + + if (activate_field) { + DropField(); + msleep(50); + } + + if (GetAPDULogging()) + PrintAndLogEx(SUCCESS, "raw>> %s", sprint_hex(data, datalen)); + + int res = ExchangeRAW14a(data, datalen, activate_field, true, result, max_result_len, (int *)result_len, true); + if (res != PM3_SUCCESS) { + return res; + } + + if (GetAPDULogging()) + PrintAndLogEx(SUCCESS, "raw<< %s", sprint_hex(result, *result_len)); + + if (*result_len < 1) { + return PM3_SUCCESS; + } + + *result_len -= 1 + 2; + uint8_t rcode = result[0]; + if (respcode) *respcode = rcode; + memmove(&result[0], &result[1], *result_len); + + if (rcode != MFDES_S_OPERATION_OK && + rcode != MFDES_S_SIGNATURE && + rcode != MFDES_S_ADDITIONAL_FRAME && + rcode != MFDES_S_NO_CHANGES) { + if (GetAPDULogging()) + PrintAndLogEx(ERR, "Command (%02x) ERROR: 0x%02x", data[0], rcode); + return PM3_EAPDU_FAIL; + } + return PM3_SUCCESS; +} + +static int DesfireExchangeNative(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize) { + if (resplen) + *resplen = 0; + if (respcode) + *respcode = 0xff; + + uint8_t buf[255 * 5] = {0x00}; + uint32_t buflen = 0; + uint32_t pos = 0; + uint32_t i = 1; + + uint8_t rcode = 0xff; + uint8_t cdata[255] = {0}; + uint32_t cdatalen = 0; + cdata[0] = cmd; + memcpy(&cdata[1], data, datalen); + cdatalen = datalen + 1; + + int res = DESFIRESendRaw(activate_field, cdata, cdatalen, buf, sizeof(buf), &buflen, &rcode); + if (res != PM3_SUCCESS) { + uint16_t ssw = DESFIRE_GET_ISO_STATUS(rcode); + PrintAndLogEx(DEBUG, "error DESFIRESendRaw %s", DesfireGetErrorString(res, &ssw)); + return res; + } + + if (resp) { + if (splitbysize) { + resp[0] = buflen; + memcpy(&resp[1], buf, buflen); + } else { + memcpy(resp, buf, buflen); + } + } + if (respcode != NULL) + *respcode = rcode; + + pos += buflen; + if (!enable_chaining) { + if (rcode == MFDES_S_OPERATION_OK || + rcode == MFDES_ADDITIONAL_FRAME) { + if (resplen) + *resplen = pos; + } + return PM3_SUCCESS; + } + + while (rcode == MFDES_ADDITIONAL_FRAME) { + cdata[0] = MFDES_ADDITIONAL_FRAME; //0xAF + + res = DESFIRESendRaw(false, cdata, 1, buf, sizeof(buf), &buflen, &rcode); + if (res != PM3_SUCCESS) { + uint16_t ssw = DESFIRE_GET_ISO_STATUS(rcode); + PrintAndLogEx(DEBUG, "error DESFIRESendRaw %s", DesfireGetErrorString(res, &ssw)); + return res; + } + + if (respcode != NULL) + *respcode = rcode; + + if (resp != NULL) { + if (splitbysize) { + resp[i * splitbysize] = buflen; + memcpy(&resp[i * splitbysize + 1], buf, buflen); + i += 1; + } else { + memcpy(&resp[pos], buf, buflen); + } + } + pos += buflen; + + if (rcode != MFDES_ADDITIONAL_FRAME) break; + } + + if (resplen) + *resplen = (splitbysize) ? i : pos; + + return PM3_SUCCESS; +} + +static int DesfireExchangeISO(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize) { + if (resplen) + *resplen = 0; + if (respcode) + *respcode = 0xff; + + uint16_t sw = 0; + uint8_t buf[255 * 5] = {0x00}; + uint32_t buflen = 0; + uint32_t pos = 0; + uint32_t i = 1; + + sAPDU apdu = {0}; + apdu.CLA = MFDES_NATIVE_ISO7816_WRAP_CLA; //0x90 + apdu.INS = cmd; + apdu.Lc = datalen; + apdu.P1 = 0; + apdu.P2 = 0; + apdu.data = data; + + int res = DESFIRESendApdu(activate_field, apdu, buf, sizeof(buf), &buflen, &sw); + if (res != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "error DESFIRESendApdu %s", DesfireGetErrorString(res, &sw)); + return res; + } + + if (respcode != NULL && ((sw & 0xff00) == 0x9100)) + *respcode = sw & 0xff; + + if (resp) { + if (splitbysize) { + resp[0] = buflen; + memcpy(&resp[1], buf, buflen); + } else { + memcpy(resp, buf, buflen); + } + } + + pos += buflen; + if (!enable_chaining) { + if (sw == DESFIRE_GET_ISO_STATUS(MFDES_S_OPERATION_OK) || + sw == DESFIRE_GET_ISO_STATUS(MFDES_ADDITIONAL_FRAME)) { + if (resplen) + *resplen = pos; + } + return PM3_SUCCESS; + } + + while (sw == DESFIRE_GET_ISO_STATUS(MFDES_ADDITIONAL_FRAME)) { + apdu.CLA = MFDES_NATIVE_ISO7816_WRAP_CLA; //0x90 + apdu.INS = MFDES_ADDITIONAL_FRAME; //0xAF + apdu.Lc = 0; + apdu.P1 = 0; + apdu.P2 = 0; + apdu.data = NULL; + + res = DESFIRESendApdu(false, apdu, buf, sizeof(buf), &buflen, &sw); + if (res != PM3_SUCCESS) { + PrintAndLogEx(DEBUG, "error DESFIRESendApdu %s", DesfireGetErrorString(res, &sw)); + return res; + } + + if (respcode != NULL && ((sw & 0xff00) == 0x9100)) + *respcode = sw & 0xff; + + if (resp != NULL) { + if (splitbysize) { + resp[i * splitbysize] = buflen; + memcpy(&resp[i * splitbysize + 1], buf, buflen); + i += 1; + } else { + memcpy(&resp[pos], buf, buflen); + } + } + pos += buflen; + + if (sw != DESFIRE_GET_ISO_STATUS(MFDES_ADDITIONAL_FRAME)) break; + } + + if (resplen) + *resplen = (splitbysize) ? i : pos; + + return PM3_SUCCESS; +} + +// move data from blockdata [format: ...] to single data block +static void DesfireJoinBlockToBytes(uint8_t *blockdata, size_t blockdatacount, size_t blockdatasize, uint8_t *dstdata, size_t *dstdatalen) { + *dstdatalen = 0; + for (int i = 0; i < blockdatacount; i++) { + memcpy(&dstdata[*dstdatalen], &blockdata[i * blockdatasize + 1], blockdata[i * blockdatasize]); + *dstdatalen += blockdata[i * blockdatasize]; + } +} + +// move data from single data block to blockdata [format: ...] +// lengths in the blockdata is not changed. result - in the blockdata +static void DesfireSplitBytesToBlock(uint8_t *blockdata, size_t *blockdatacount, size_t blockdatasize, uint8_t *dstdata, size_t dstdatalen) { + size_t len = 0; + for (int i = 0; i < *blockdatacount; i++) { + size_t tlen = len + blockdata[i * blockdatasize]; + if (tlen > dstdatalen) + tlen = dstdatalen; + if (len == tlen) { + *blockdatacount = i; + break; + } + memcpy(&blockdata[i * blockdatasize + 1], &dstdata[len], tlen - len); + len = tlen; + } +} + +int DesfireExchangeEx(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize) { + int res = PM3_SUCCESS; + + if (!PrintChannelModeWarning(cmd, ctx->secureChannel, ctx->cmdSet, ctx->commMode)) + DesfirePrintContext(ctx); + + uint8_t databuf[250 * 5] = {0}; + size_t databuflen = 0; + + switch (ctx->cmdSet) { + case DCCNative: + case DCCNativeISO: + DesfireSecureChannelEncode(ctx, cmd, data, datalen, databuf, &databuflen); + + if (ctx->cmdSet == DCCNative) + res = DesfireExchangeNative(activate_field, ctx, cmd, databuf, databuflen, respcode, databuf, &databuflen, enable_chaining, splitbysize); + else + res = DesfireExchangeISO(activate_field, ctx, cmd, databuf, databuflen, respcode, databuf, &databuflen, enable_chaining, splitbysize); + + if (splitbysize) { + uint8_t sdata[250 * 5] = {0}; + size_t sdatalen = 0; + DesfireJoinBlockToBytes(databuf, databuflen, splitbysize, sdata, &sdatalen); + + //PrintAndLogEx(INFO, "block : %s", sprint_hex(sdata, sdatalen)); + DesfireSecureChannelDecode(ctx, sdata, sdatalen, *respcode, resp, resplen); + + DesfireSplitBytesToBlock(databuf, &databuflen, splitbysize, resp, *resplen); + memcpy(resp, databuf, databuflen * splitbysize); + *resplen = databuflen; + } else { + DesfireSecureChannelDecode(ctx, databuf, databuflen, *respcode, resp, resplen); + } + break; + case DCCISO: + return PM3_EAPDU_FAIL; + break; + } + + return res; +} + +int DesfireExchange(DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen) { + return DesfireExchangeEx(false, ctx, cmd, data, datalen, respcode, resp, resplen, true, 0); +} + +int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2) { + if (aid1 == NULL) + return PM3_EINVARG; + + uint8_t data[6] = {0}; + memcpy(data, aid1, 3); + if (aid2 != NULL) + memcpy(&data[3], aid2, 3); + uint8_t resp[257] = {0}; + size_t resplen = 0; + uint8_t respcode = 0; + + int res = DesfireExchangeEx(true, ctx, MFDES_SELECT_APPLICATION, data, (aid2 == NULL) ? 3 : 6, &respcode, resp, &resplen, true, 0); + if (res == PM3_SUCCESS) { + if (resplen != 0) + return PM3_ECARDEXCHANGE; + + // select operation fail + if (respcode != MFDES_S_OPERATION_OK) + return PM3_EAPDU_FAIL; + + return PM3_SUCCESS; + } + return res; +} + +int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2) { + uint8_t data[6] = {0}; + + DesfireAIDUintToByte(aid1, data); + DesfireAIDUintToByte(aid2, &data[3]); + + return DesfireSelectAID(ctx, data, (select_two) ? &data[3] : NULL); +} + +int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool verbose) { + if (verbose) + DesfirePrintContext(dctx); + + int res = DesfireSelectAIDHex(dctx, aid, false, 0); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire select " _RED_("error") "."); + return PM3_ESOFT; + } + + res = DesfireAuthenticate(dctx, secureChannel); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: %d", res); + return PM3_ESOFT; + } + + if (DesfireIsAuthenticated(dctx)) { + if (verbose) + PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated")); + } else { + return PM3_ESOFT; + } + + return PM3_SUCCESS; +} + +int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel) { + // 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32) + // 4 different crypto arg1 DES, 3DES, 3K3DES, AES + // 3 different communication modes, PLAIN,MAC,CRYPTO + + DesfireClearSession(dctx); + + if (secureChannel == DACNone) + return PM3_SUCCESS; + + mbedtls_aes_context ctx; + + uint8_t keybytes[24] = {0}; + // Crypt constants + uint8_t IV[16] = {0}; + uint8_t RndA[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16}; + uint8_t RndB[16] = {0}; + uint8_t encRndB[16] = {0}; + uint8_t rotRndB[16] = {0}; //RndB' + uint8_t both[32 + 1] = {0}; // ek/dk_keyNo(RndA+RndB') + + // Part 1 + memcpy(keybytes, dctx->key, desfire_get_key_length(dctx->keyType)); + + struct desfire_key dkey = {0}; + desfirekey_t key = &dkey; + + if (dctx->keyType == T_AES) { + mbedtls_aes_init(&ctx); + Desfire_aes_key_new(keybytes, key); + } else if (dctx->keyType == T_3DES) { + Desfire_3des_key_new_with_version(keybytes, key); + } else if (dctx->keyType == T_DES) { + Desfire_des_key_new(keybytes, key); + } else if (dctx->keyType == T_3K3DES) { + Desfire_3k3des_key_new_with_version(keybytes, key); + } + + if (dctx->kdfAlgo == MFDES_KDF_ALGO_AN10922) { + mifare_kdf_an10922(key, dctx->kdfInput, dctx->kdfInputLen); + PrintAndLogEx(DEBUG, " Derrived key: " _GREEN_("%s"), sprint_hex(key->data, key_block_size(key))); + } else if (dctx->kdfAlgo == MFDES_KDF_ALGO_GALLAGHER) { + // We will overrite any provided KDF input since a gallagher specific KDF was requested. + dctx->kdfInputLen = 11; + + /*if (mfdes_kdf_input_gallagher(tag->info.uid, tag->info.uidlen, dctx->keyNum, tag->selected_application, dctx->kdfInput, &dctx->kdfInputLen) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Could not generate Gallagher KDF input"); + }*/ + + mifare_kdf_an10922(key, dctx->kdfInput, dctx->kdfInputLen); + PrintAndLogEx(DEBUG, " KDF Input: " _YELLOW_("%s"), sprint_hex(dctx->kdfInput, dctx->kdfInputLen)); + PrintAndLogEx(DEBUG, " Derrived key: " _GREEN_("%s"), sprint_hex(key->data, key_block_size(key))); + + } + + uint8_t subcommand = MFDES_AUTHENTICATE; + if (secureChannel == DACEV1) { + if (dctx->keyType == T_AES) + subcommand = MFDES_AUTHENTICATE_AES; + else + subcommand = MFDES_AUTHENTICATE_ISO; + } + + size_t recv_len = 0; + uint8_t respcode = 0; + uint8_t recv_data[256] = {0}; + + // Let's send our auth command + int res = DesfireExchangeEx(false, dctx, subcommand, &dctx->keyNum, 1, &respcode, recv_data, &recv_len, false, 0); + if (res != PM3_SUCCESS) { + return 1; + } + + if (!recv_len) { + return 2; + } + + if (respcode != MFDES_ADDITIONAL_FRAME) { + return 3; + } + + uint32_t expectedlen = 8; + if (dctx->keyType == T_AES || dctx->keyType == T_3K3DES) { + expectedlen = 16; + } + + if (recv_len != expectedlen) { + return 4; + } + + // Part 2 + uint32_t rndlen = recv_len; + memcpy(encRndB, recv_data, rndlen); + + + // Part 3 + if (dctx->keyType == T_AES) { + if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) { + return 5; + } + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, rndlen, IV, encRndB, RndB); + } else if (dctx->keyType == T_DES) { + if (secureChannel == DACd40) + des_decrypt(RndB, encRndB, key->data); + if (secureChannel == DACEV1) + des_decrypt_cbc(RndB, encRndB, rndlen, key->data, IV); + } else if (dctx->keyType == T_3DES) + tdes_nxp_receive(encRndB, RndB, rndlen, key->data, IV, 2); + else if (dctx->keyType == T_3K3DES) { + tdes_nxp_receive(encRndB, RndB, rndlen, key->data, IV, 3); + } + + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "encRndB: %s", sprint_hex(encRndB, 8)); + PrintAndLogEx(DEBUG, "RndB: %s", sprint_hex(RndB, 8)); + } + + // - Rotate RndB by 8 bits + memcpy(rotRndB, RndB, rndlen); + rol(rotRndB, rndlen); + + uint8_t encRndA[16] = {0x00}; + + // - Encrypt our response + if (secureChannel == DACd40) { + if (dctx->keyType == T_DES) { + des_decrypt(encRndA, RndA, key->data); + memcpy(both, encRndA, rndlen); + + for (uint32_t x = 0; x < rndlen; x++) { + rotRndB[x] = rotRndB[x] ^ encRndA[x]; + } + + des_decrypt(encRndB, rotRndB, key->data); + memcpy(both + rndlen, encRndB, rndlen); + } else if (dctx->keyType == T_3DES) { + //TODO + } + } else if (secureChannel == DACEV1 && dctx->keyType != T_AES) { + if (dctx->keyType == T_DES) { + uint8_t tmp[16] = {0x00}; + memcpy(tmp, RndA, rndlen); + memcpy(tmp + rndlen, rotRndB, rndlen); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "rotRndB: %s", sprint_hex(rotRndB, rndlen)); + PrintAndLogEx(DEBUG, "Both: %s", sprint_hex(tmp, 16)); + } + des_encrypt_cbc(both, tmp, 16, key->data, IV); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "EncBoth: %s", sprint_hex(both, 16)); + } + } else if (dctx->keyType == T_3DES) { + uint8_t tmp[16] = {0x00}; + memcpy(tmp, RndA, rndlen); + memcpy(tmp + rndlen, rotRndB, rndlen); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "rotRndB: %s", sprint_hex(rotRndB, rndlen)); + PrintAndLogEx(DEBUG, "Both: %s", sprint_hex(tmp, 16)); + } + tdes_nxp_send(tmp, both, 16, key->data, IV, 2); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "EncBoth: %s", sprint_hex(both, 16)); + } + } else if (dctx->keyType == T_3K3DES) { + uint8_t tmp[32] = {0x00}; + memcpy(tmp, RndA, rndlen); + memcpy(tmp + rndlen, rotRndB, rndlen); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "rotRndB: %s", sprint_hex(rotRndB, rndlen)); + PrintAndLogEx(DEBUG, "Both3k3: %s", sprint_hex(tmp, 32)); + } + tdes_nxp_send(tmp, both, 32, key->data, IV, 3); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "EncBoth: %s", sprint_hex(both, 32)); + } + } + } else if (secureChannel == DACEV1 && dctx->keyType == T_AES) { + uint8_t tmp[32] = {0x00}; + memcpy(tmp, RndA, rndlen); + memcpy(tmp + rndlen, rotRndB, rndlen); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "rotRndB: %s", sprint_hex(rotRndB, rndlen)); + PrintAndLogEx(DEBUG, "Both3k3: %s", sprint_hex(tmp, 32)); + } + if (dctx->keyType == T_AES) { + if (mbedtls_aes_setkey_enc(&ctx, key->data, 128) != 0) { + return 6; + } + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, 32, IV, tmp, both); + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "EncBoth: %s", sprint_hex(both, 32)); + } + } + } + + uint32_t bothlen = 16; + if (dctx->keyType == T_AES || dctx->keyType == T_3K3DES) { + bothlen = 32; + } + + res = DesfireExchangeEx(false, dctx, MFDES_ADDITIONAL_FRAME, both, bothlen, &respcode, recv_data, &recv_len, false, 0); + if (res != PM3_SUCCESS) { + return 7; + } + + if (!recv_len) { + return 8; + } + + if (respcode != MFDES_S_OPERATION_OK) { + return 9; + } + + // Part 4 + memcpy(encRndA, recv_data, rndlen); + + struct desfire_key sesskey = {0}; + + Desfire_session_key_new(RndA, RndB, key, &sesskey); + memcpy(dctx->sessionKeyEnc, sesskey.data, desfire_get_key_length(dctx->keyType)); + + //PrintAndLogEx(INFO, "encRndA : %s", sprint_hex(encRndA, rndlen)); + //PrintAndLogEx(INFO, "IV : %s", sprint_hex(IV, rndlen)); + if (dctx->keyType == T_DES) { + if (secureChannel == DACd40) + des_decrypt(encRndA, encRndA, key->data); + if (secureChannel == DACEV1) + des_decrypt_cbc(encRndA, encRndA, rndlen, key->data, IV); + } else if (dctx->keyType == T_3DES) + tdes_nxp_receive(encRndA, encRndA, rndlen, key->data, IV, 2); + else if (dctx->keyType == T_3K3DES) + tdes_nxp_receive(encRndA, encRndA, rndlen, key->data, IV, 3); + else if (dctx->keyType == T_AES) { + if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) { + return 10; + } + mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, rndlen, IV, encRndA, encRndA); + } + + rol(RndA, rndlen); + //PrintAndLogEx(INFO, "Expected_RndA : %s", sprint_hex(RndA, rndlen)); + //PrintAndLogEx(INFO, "Generated_RndA : %s", sprint_hex(encRndA, rndlen)); + for (uint32_t x = 0; x < rndlen; x++) { + if (RndA[x] != encRndA[x]) { + if (g_debugMode > 1) { + PrintAndLogEx(DEBUG, "Expected_RndA : %s", sprint_hex(RndA, rndlen)); + PrintAndLogEx(DEBUG, "Generated_RndA : %s", sprint_hex(encRndA, rndlen)); + } + return 11; + } + } + + // If the 3Des key first 8 bytes = 2nd 8 Bytes then we are really using Singe Des + // As such we need to set the session key such that the 2nd 8 bytes = 1st 8 Bytes + if (dctx->keyType == T_3DES) { + if (memcmp(key->data, &key->data[8], 8) == 0) + memcpy(&dctx->sessionKeyEnc[8], dctx->sessionKeyEnc, 8); + } + + if (secureChannel == DACEV1) { + cmac_generate_subkeys(&sesskey, MCD_RECEIVE); + //key->cmac_sk1 and key->cmac_sk2 + //memcpy(dctx->sessionKeyEnc, sesskey.data, desfire_get_key_length(dctx->keyType)); + } + + memset(dctx->IV, 0, DESFIRE_MAX_KEY_SIZE); + dctx->secureChannel = secureChannel; + memcpy(dctx->sessionKeyMAC, dctx->sessionKeyEnc, desfire_get_key_length(dctx->keyType)); + PrintAndLogEx(INFO, "Session key : %s", sprint_hex(dctx->sessionKeyEnc, desfire_get_key_length(dctx->keyType))); + + return PM3_SUCCESS; +} + +static int DesfireCommandEx(DesfireContext *dctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen, int checklength, size_t splitbysize) { + if (resplen) + *resplen = 0; + + uint8_t respcode = 0xff; + uint8_t xresp[257] = {0}; + size_t xresplen = 0; + int res = DesfireExchangeEx(false, dctx, cmd, data, datalen, &respcode, xresp, &xresplen, true, splitbysize); + if (res != PM3_SUCCESS) + return res; + if (respcode != MFDES_S_OPERATION_OK) + return PM3_EAPDU_FAIL; + if (checklength >= 0 && xresplen != checklength) + return PM3_EAPDU_FAIL; + + if (resplen) + *resplen = xresplen; + if (resp) + memcpy(resp, xresp, (splitbysize == 0) ? xresplen : xresplen * splitbysize); + return PM3_SUCCESS; +} + +static int DesfireCommand(DesfireContext *dctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen, int checklength) { + return DesfireCommandEx(dctx, cmd, data, datalen, resp, resplen, checklength, 0); +} + +static int DesfireCommandNoData(DesfireContext *dctx, uint8_t cmd) { + return DesfireCommand(dctx, cmd, NULL, 0, NULL, NULL, 0); +} + +static int DesfireCommandTxData(DesfireContext *dctx, uint8_t cmd, uint8_t *data, size_t datalen) { + return DesfireCommand(dctx, cmd, data, datalen, NULL, NULL, 0); +} + +static int DesfireCommandRxData(DesfireContext *dctx, uint8_t cmd, uint8_t *resp, size_t *resplen, int checklength) { + return DesfireCommand(dctx, cmd, NULL, 0, resp, resplen, checklength); +} + +int DesfireFormatPICC(DesfireContext *dctx) { + return DesfireCommandNoData(dctx, MFDES_FORMAT_PICC); +} + +int DesfireGetFreeMem(DesfireContext *dctx, uint32_t *freemem) { + *freemem = 0; + + uint8_t resp[257] = {0}; + size_t resplen = 0; + int res = DesfireCommandRxData(dctx, MFDES_GET_FREE_MEMORY, resp, &resplen, 3); + if (res == PM3_SUCCESS) + *freemem = DesfireAIDByteToUint(resp); + return res; +} + +int DesfireGetUID(DesfireContext *dctx, uint8_t *resp, size_t *resplen) { + return DesfireCommandRxData(dctx, MFDES_GET_UID, resp, resplen, -1); +} + +int DesfireGetAIDList(DesfireContext *dctx, uint8_t *resp, size_t *resplen) { + return DesfireCommandRxData(dctx, MFDES_GET_APPLICATION_IDS, resp, resplen, -1); +} + +int DesfireGetDFList(DesfireContext *dctx, uint8_t *resp, size_t *resplen) { + return DesfireCommandEx(dctx, MFDES_GET_DF_NAMES, NULL, 0, resp, resplen, -1, 24); +} + +int DesfireCreateApplication(DesfireContext *dctx, uint8_t *appdata, size_t appdatalen) { + return DesfireCommandTxData(dctx, MFDES_CREATE_APPLICATION, appdata, appdatalen); +} + +int DesfireDeleteApplication(DesfireContext *dctx, uint32_t aid) { + uint8_t data[3] = {0}; + DesfireAIDUintToByte(aid, data); + return DesfireCommandTxData(dctx, MFDES_DELETE_APPLICATION, data, sizeof(data)); +} + +int DesfireGetKeySettings(DesfireContext *dctx, uint8_t *resp, size_t *resplen) { + return DesfireCommandRxData(dctx, MFDES_GET_KEY_SETTINGS, resp, resplen, -1); +} + +int DesfireGetKeyVersion(DesfireContext *dctx, uint8_t *data, size_t len, uint8_t *resp, size_t *resplen) { + return DesfireCommand(dctx, MFDES_GET_KEY_VERSION, data, len, resp, resplen, -1); +} + +int DesfireChangeKeySettings(DesfireContext *dctx, uint8_t *data, size_t len) { + return DesfireCommandTxData(dctx, MFDES_CHANGE_KEY_SETTINGS, data, len); +} + +uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorythm keyType) { + switch (keyType) { + case T_DES: + return 0x00; + case T_3DES: + return 0x00; + case T_3K3DES: + return 0x01; + case T_AES: + return 0x02; + } + return 0; +} +static void PrintKeyType(uint8_t keytype) { + switch (keytype) { + case 00: + PrintAndLogEx(SUCCESS, "Key: 2TDEA"); + break; + case 01: + PrintAndLogEx(SUCCESS, "Key: 3TDEA"); + break; + case 02: + PrintAndLogEx(SUCCESS, "Key: AES"); + break; + default: + PrintAndLogEx(SUCCESS, "Key: unknown: 0x%02x", keytype); + break; + } +} + +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") : "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") : "NO (frozen)"); + PrintAndLogEx(SUCCESS, ""); + + if (print2ndbyte) + PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f); +} + +static void PrintKeySettingsApp(uint8_t keysettings, uint8_t numkeys, bool print2ndbyte) { + // Access rights. + PrintAndLogEx(SUCCESS, "Application level rights:"); + uint8_t rights = ((keysettings >> 4) & 0x0F); + switch (rights) { + case 0x0: + PrintAndLogEx(SUCCESS, "-- AMK authentication is necessary to change any key (default)"); + break; + case 0xE: + PrintAndLogEx(SUCCESS, "-- Authentication with the key to be changed (same KeyNo) is necessary to change a key"); + break; + case 0xF: + PrintAndLogEx(SUCCESS, "-- All keys (except AMK,see Bit0) within this application are frozen"); + break; + default: + PrintAndLogEx(SUCCESS, + "-- Authentication with the specified key is necessary to change any key.\n" + "A change key and a PICC master key (CMK) can only be changed after authentication with the master key.\n" + "For keys other then the master or change key, an authentication with the same key is needed." + ); + break; + } + + PrintAndLogEx(SUCCESS, "[%c...] AMK Configuration changeable : %s", (keysettings & (1 << 3)) ? '1' : '0', (keysettings & (1 << 3)) ? _GREEN_("YES") : "NO (frozen)"); + PrintAndLogEx(SUCCESS, "[.%c..] AMK required for create/delete : %s", (keysettings & (1 << 2)) ? '1' : '0', (keysettings & (1 << 2)) ? "NO" : "YES"); + PrintAndLogEx(SUCCESS, "[..%c.] Directory list access with AMK : %s", (keysettings & (1 << 1)) ? '1' : '0', (keysettings & (1 << 1)) ? "NO" : "YES"); + PrintAndLogEx(SUCCESS, "[...%c] AMK is changeable : %s", (keysettings & (1 << 0)) ? '1' : '0', (keysettings & (1 << 0)) ? _GREEN_("YES") : "NO (frozen)"); + PrintAndLogEx(SUCCESS, ""); + + if (print2ndbyte) { + PrintKeyType(numkeys >> 6); + PrintAndLogEx(SUCCESS, "key count: %d", numkeys & 0x0f); + if (numkeys & 0x20) + PrintAndLogEx(SUCCESS, "iso file id: enabled"); + PrintAndLogEx(SUCCESS, ""); + } +} + +void PrintKeySettings(uint8_t keysettings, uint8_t numkeys, bool applevel, bool print2ndbyte) { + if (applevel) + PrintKeySettingsApp(keysettings, numkeys, print2ndbyte); + else + PrintKeySettingsPICC(keysettings, numkeys, print2ndbyte); +} diff --git a/client/src/mifare/desfirecore.h b/client/src/mifare/desfirecore.h new file mode 100644 index 000000000..c6b092b59 --- /dev/null +++ b/client/src/mifare/desfirecore.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 Romain Tartiere. +// Copyright (C) 2014 Iceman +// Copyright (C) 2021 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency Desfire core functions +//----------------------------------------------------------------------------- + +#ifndef __DESFIRECORE_H +#define __DESFIRECORE_H + +#include "common.h" +#include "cliparser.h" +#include "mifare/desfirecrypto.h" +#include "mifare/desfire_crypto.h" +#include "mifare/mifare4.h" + +extern const CLIParserOption DesfireAlgoOpts[]; +extern const CLIParserOption DesfireKDFAlgoOpts[]; +extern const CLIParserOption DesfireCommunicationModeOpts[]; +extern const CLIParserOption DesfireCommandSetOpts[]; +extern const CLIParserOption DesfireSecureChannelOpts[]; + +const char *DesfireGetErrorString(int res, uint16_t *sw); +uint32_t DesfireAIDByteToUint(uint8_t *data); +void DesfireAIDUintToByte(uint32_t aid, uint8_t *data); + +void DesfirePrintContext(DesfireContext *ctx); + +int DesfireExchange(DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen); +int DesfireExchangeEx(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize); + +int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2); +int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2); + +int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool verbose); +int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel); + +int DesfireFormatPICC(DesfireContext *dctx); +int DesfireGetFreeMem(DesfireContext *dctx, uint32_t *freemem); +int DesfireGetUID(DesfireContext *dctx, uint8_t *resp, size_t *resplen); +int DesfireGetAIDList(DesfireContext *dctx, uint8_t *resp, size_t *resplen); +int DesfireGetDFList(DesfireContext *dctx, uint8_t *resp, size_t *resplen); + +int DesfireCreateApplication(DesfireContext *dctx, uint8_t *appdata, size_t appdatalen); +int DesfireDeleteApplication(DesfireContext *dctx, uint32_t aid); + +int DesfireGetKeyVersion(DesfireContext *dctx, uint8_t *data, size_t len, uint8_t *resp, size_t *resplen); +int DesfireGetKeySettings(DesfireContext *dctx, uint8_t *resp, size_t *resplen); +int DesfireChangeKeySettings(DesfireContext *dctx, uint8_t *data, size_t len); +void PrintKeySettings(uint8_t keysettings, uint8_t numkeys, bool applevel, bool print2ndbyte); +uint8_t DesfireKeyAlgoToType(DesfireCryptoAlgorythm keyType); + +#endif // __DESFIRECORE_H diff --git a/client/src/mifare/desfirecrypto.c b/client/src/mifare/desfirecrypto.c new file mode 100644 index 000000000..11b29b9db --- /dev/null +++ b/client/src/mifare/desfirecrypto.c @@ -0,0 +1,341 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * Copyright (C) 2021 Merlok + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + * $Id$ + */ + +#include "desfirecrypto.h" + +#include +#include +#include +#include "ui.h" +#include "aes.h" +#include "des.h" +#include +#include "crc.h" +#include "crc16.h" // crc16 ccitt +#include "crc32.h" +#include "commonutil.h" +#include "mifare/desfire_crypto.h" + +void DesfireClearContext(DesfireContext *ctx) { + ctx->keyNum = 0; + ctx->keyType = T_DES; + memset(ctx->key, 0, sizeof(ctx->key)); + + ctx->secureChannel = DACNone; + ctx->cmdSet = DCCNative; + ctx->commMode = DCMNone; + + ctx->kdfAlgo = 0; + ctx->kdfInputLen = 0; + memset(ctx->kdfInput, 0, sizeof(ctx->kdfInput)); + + DesfireClearSession(ctx); +} + +void DesfireClearSession(DesfireContext *ctx) { + ctx->secureChannel = DACNone; // here none - not authenticared + + memset(ctx->IV, 0, sizeof(ctx->IV)); + memset(ctx->sessionKeyMAC, 0, sizeof(ctx->sessionKeyMAC)); + memset(ctx->sessionKeyEnc, 0, sizeof(ctx->sessionKeyEnc)); + memset(ctx->lastIV, 0, sizeof(ctx->lastIV)); + ctx->lastCommand = 0; + ctx->lastRequestZeroLen = false; + ctx->cntrTx = 0; + ctx->cntrRx = 0; + memset(ctx->TI, 0, sizeof(ctx->TI)); +} + +void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key) { + DesfireClearContext(ctx); + + ctx->keyNum = keyNum; + ctx->keyType = keyType; + memcpy(ctx->key, key, desfire_get_key_length(keyType)); +} + +void DesfireSetCommandSet(DesfireContext *ctx, DesfireCommandSet cmdSet) { + ctx->cmdSet = cmdSet; +} + +void DesfireSetCommMode(DesfireContext *ctx, DesfireCommunicationMode commMode) { + ctx->commMode = commMode; +} + +void DesfireSetKdf(DesfireContext *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, uint8_t kdfInputLen) { + ctx->kdfAlgo = kdfAlgo; + ctx->kdfInputLen = kdfInputLen; + if (kdfInputLen) + memcpy(ctx->kdfInput, kdfInput, kdfInputLen); +} + +bool DesfireIsAuthenticated(DesfireContext *dctx) { + return dctx->secureChannel != DACNone; +} + +size_t DesfireGetMACLength(DesfireContext *ctx) { + size_t mac_length = MAC_LENGTH; + switch (ctx->secureChannel) { + case DACNone: + mac_length = 0; + break; + case DACd40: + mac_length = 4; + break; + case DACEV1: + mac_length = 8; + break; + case DACEV2: + mac_length = 8; + break; + } + return mac_length; +} + +size_t DesfireSearchCRCPos(uint8_t *data, size_t datalen, uint8_t respcode, uint8_t crclen) { + size_t crcpos = datalen - 1; + while (crcpos > 0) + if (data[crcpos] == 0) + crcpos--; + else + break; + crcpos++; // crc may be 0x00000000 or 0x0000 + if (crcpos < crclen) { + PrintAndLogEx(WARNING, "No space for crc. pos: %zu", crcpos); + return 0; + } + + uint8_t crcdata[1024] = {0}; + size_t crcposfound = 0; + for (int i = 0; i < crclen + 1; i++) { + if (crcpos - i == 0) + break; + if (crcpos - i + crclen > datalen) + continue; + + memcpy(crcdata, data, crcpos - i); + crcdata[crcpos - i] = respcode; + bool res; + if (crclen == 4) + res = desfire_crc32_check(crcdata, crcpos - i + 1, &data[crcpos - i]); + else + res = iso14443a_crc_check(data, crcpos - i, &data[crcpos - i]); + if (res) { + crcposfound = crcpos - i; + } + } + + return crcposfound; +} + +static void DesfireCryptoEncDecSingleBlock(uint8_t *key, DesfireCryptoAlgorythm keyType, uint8_t *data, uint8_t *dstdata, uint8_t *ivect, bool dir_to_send, bool encode) { + size_t block_size = desfire_get_key_block_length(keyType); + uint8_t sdata[MAX_CRYPTO_BLOCK_SIZE] = {0}; + memcpy(sdata, data, block_size); + if (dir_to_send) { + bin_xor(sdata, ivect, block_size); + } + + uint8_t edata[MAX_CRYPTO_BLOCK_SIZE] = {0}; + + switch (keyType) { + case T_DES: + if (encode) + des_encrypt(edata, sdata, key); + else + des_decrypt(edata, sdata, key); + break; + case T_3DES: + if (encode) { + mbedtls_des3_context ctx3; + mbedtls_des3_set2key_enc(&ctx3, key); + mbedtls_des3_crypt_ecb(&ctx3, sdata, edata); + } else { + mbedtls_des3_context ctx3; + mbedtls_des3_set2key_dec(&ctx3, key); + mbedtls_des3_crypt_ecb(&ctx3, sdata, edata); + } + break; + case T_3K3DES: + if (encode) { + mbedtls_des3_context ctx3; + mbedtls_des3_set3key_enc(&ctx3, key); + mbedtls_des3_crypt_ecb(&ctx3, sdata, edata); + } else { + mbedtls_des3_context ctx3; + mbedtls_des3_set3key_dec(&ctx3, key); + mbedtls_des3_crypt_ecb(&ctx3, sdata, edata); + } + break; + case T_AES: + if (encode) { + mbedtls_aes_context actx; + mbedtls_aes_init(&actx); + mbedtls_aes_setkey_enc(&actx, key, 128); + mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_ENCRYPT, sdata, edata); + mbedtls_aes_free(&actx); + } else { + mbedtls_aes_context actx; + mbedtls_aes_init(&actx); + mbedtls_aes_setkey_dec(&actx, key, 128); + mbedtls_aes_crypt_ecb(&actx, MBEDTLS_AES_DECRYPT, sdata, edata); + mbedtls_aes_free(&actx); + } + break; + } + + if (dir_to_send) { + memcpy(ivect, edata, block_size); + } else { + bin_xor(edata, ivect, block_size); + memcpy(ivect, data, block_size); + } + + memcpy(dstdata, edata, block_size); +} + +void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode, uint8_t *iv) { + uint8_t data[1024] = {0}; + uint8_t xiv[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; + + bool xencode = encode; + if (ctx->secureChannel == DACd40) { + memset(ctx->IV, 0, DESFIRE_MAX_CRYPTO_BLOCK_SIZE); + xencode = false; + } + + size_t block_size = desfire_get_key_block_length(ctx->keyType); + + if (iv == NULL) + memcpy(xiv, ctx->IV, block_size); + else + memcpy(xiv, iv, block_size); + + size_t offset = 0; + while (offset < srcdatalen) { + if (use_session_key) + DesfireCryptoEncDecSingleBlock(ctx->sessionKeyMAC, ctx->keyType, srcdata + offset, data + offset, xiv, encode, xencode); + else + DesfireCryptoEncDecSingleBlock(ctx->key, ctx->keyType, srcdata + offset, data + offset, xiv, encode, xencode); + offset += block_size; + } + + if (iv == NULL) + memcpy(ctx->IV, xiv, block_size); + else + memcpy(iv, xiv, block_size); + + if (dstdata) + memcpy(dstdata, data, srcdatalen); +} + +void DesfireCryptoEncDec(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode) { + DesfireCryptoEncDecEx(ctx, use_session_key, srcdata, srcdatalen, dstdata, encode, NULL); +} + +static void DesfireCMACGenerateSubkeys(DesfireContext *ctx, uint8_t *sk1, uint8_t *sk2) { + int kbs = desfire_get_key_block_length(ctx->keyType); + const uint8_t R = (kbs == 8) ? 0x1B : 0x87; + + uint8_t l[kbs]; + memset(l, 0, kbs); + + uint8_t ivect[kbs]; + memset(ivect, 0, kbs); + + DesfireCryptoEncDecEx(ctx, true, l, kbs, l, true, ivect); + + bool txor = false; + + // Used to compute CMAC on complete blocks + memcpy(sk1, l, kbs); + txor = l[0] & 0x80; + lsl(sk1, kbs); + if (txor) { + sk1[kbs - 1] ^= R; + } + + // Used to compute CMAC on the last block if non-complete + memcpy(sk2, sk1, kbs); + txor = sk1[0] & 0x80; + lsl(sk2, kbs); + if (txor) { + sk2[kbs - 1] ^= R; + } +} + +void DesfireCryptoCMAC(DesfireContext *ctx, uint8_t *data, size_t len, uint8_t *cmac) { + int kbs = desfire_get_key_block_length(ctx->keyType); + if (kbs == 0) + return; + + uint8_t buffer[padded_data_length(len, kbs)]; + memset(buffer, 0, sizeof(buffer)); + + uint8_t sk1[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; + uint8_t sk2[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; + DesfireCMACGenerateSubkeys(ctx, sk1, sk2); + + memcpy(buffer, data, len); + + if ((!len) || (len % kbs)) { + buffer[len++] = 0x80; + while (len % kbs) { + buffer[len++] = 0x00; + } + bin_xor(buffer + len - kbs, sk2, kbs); + } else { + bin_xor(buffer + len - kbs, sk1, kbs); + } + + DesfireCryptoEncDec(ctx, true, buffer, len, NULL, true); + + if (cmac != NULL) + memcpy(cmac, ctx->IV, kbs); +} + + +void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc) { + crc32_ex(data, len, crc); +} + +void desfire_crc32_append(uint8_t *data, const size_t len) { + crc32_ex(data, len, data + len); +} + +bool desfire_crc32_check(uint8_t *data, const size_t len, uint8_t *crc) { + uint8_t ccrc[4] = {0}; + desfire_crc32(data, len, ccrc); + return (memcmp(ccrc, crc, 4) == 0); +} + +void iso14443a_crc_append(uint8_t *data, size_t len) { + return compute_crc(CRC_14443_A, data, len, data + len, data + len + 1); +} + +void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc) { + return compute_crc(CRC_14443_A, data, len, pbtCrc, pbtCrc + 1); +} + +bool iso14443a_crc_check(uint8_t *data, const size_t len, uint8_t *crc) { + uint8_t ccrc[2] = {0}; + iso14443a_crc(data, len, ccrc); + return (memcmp(ccrc, crc, 2) == 0); +} diff --git a/client/src/mifare/desfirecrypto.h b/client/src/mifare/desfirecrypto.h new file mode 100644 index 000000000..6bffa376e --- /dev/null +++ b/client/src/mifare/desfirecrypto.h @@ -0,0 +1,111 @@ +/*- + * Copyright (C) 2010, Romain Tartiere. + * Copyright (C) 2021 Merlok + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + * $Id$ + */ + +#ifndef __DESFIRECRYPTO_H +#define __DESFIRECRYPTO_H + +#include "common.h" +#include "mifare/mifare4.h" + +#define MAX_CRYPTO_BLOCK_SIZE 16 +#define DESFIRE_MAX_CRYPTO_BLOCK_SIZE 16 +#define DESFIRE_MAX_KEY_SIZE 24 + +#define DESFIRE_GET_ISO_STATUS(x) ( ((uint16_t)(0x91<<8)) + (uint16_t)x ) + +enum DESFIRE_CRYPTOALGO { + T_DES = 0x00, + T_3DES = 0x01, //aka 2K3DES + T_3K3DES = 0x02, + T_AES = 0x03 +}; + +typedef enum DESFIRE_CRYPTOALGO DesfireCryptoAlgorythm; + +typedef enum { + DACNone, + DACd40, + DACEV1, + DACEV2 +} DesfireSecureChannel; + +typedef enum { + DCCNative, + DCCNativeISO, + DCCISO +} DesfireCommandSet; + +typedef enum { + DCMNone, + DCMPlain, + DCMMACed, + DCMEncrypted +} DesfireCommunicationMode; + + +typedef struct DesfireContextS { + uint8_t keyNum; + DesfireCryptoAlgorythm keyType; // des/2tdea/3tdea/aes + uint8_t key[DESFIRE_MAX_KEY_SIZE]; + + // KDF finction + uint8_t kdfAlgo; + uint8_t kdfInputLen; + uint8_t kdfInput[31]; + + DesfireSecureChannel secureChannel; // none/d40/ev1/ev2 + DesfireCommandSet cmdSet; // native/nativeiso/iso + DesfireCommunicationMode commMode; // plain/mac/enc + + uint8_t IV[DESFIRE_MAX_KEY_SIZE]; + uint8_t sessionKeyMAC[DESFIRE_MAX_KEY_SIZE]; + uint8_t sessionKeyEnc[DESFIRE_MAX_KEY_SIZE]; // look at mifare4.h - mf4Session_t + uint8_t lastIV[DESFIRE_MAX_KEY_SIZE]; + uint8_t lastCommand; + bool lastRequestZeroLen; + //mf4Session_t AESSession; + uint16_t cntrTx; // for AES + uint16_t cntrRx; // for AES + uint8_t TI[4]; // for AES +} DesfireContext; + +void DesfireClearContext(DesfireContext *ctx); +void DesfireClearSession(DesfireContext *ctx); +void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key); +void DesfireSetCommandSet(DesfireContext *ctx, DesfireCommandSet cmdSet); +void DesfireSetCommMode(DesfireContext *ctx, DesfireCommunicationMode commMode); +void DesfireSetKdf(DesfireContext *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, uint8_t kdfInputLen); +bool DesfireIsAuthenticated(DesfireContext *dctx); +size_t DesfireGetMACLength(DesfireContext *ctx); + +size_t DesfireSearchCRCPos(uint8_t *data, size_t datalen, uint8_t respcode, uint8_t crclen); + +void DesfireCryptoEncDec(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode); +void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode, uint8_t *iv); +void DesfireCryptoCMAC(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *cmac); + +void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc); +void desfire_crc32_append(uint8_t *data, const size_t len); +bool desfire_crc32_check(uint8_t *data, const size_t len, uint8_t *crc); +void iso14443a_crc_append(uint8_t *data, size_t len); +void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc); +bool iso14443a_crc_check(uint8_t *data, const size_t len, uint8_t *crc); + +#endif // __DESFIRECRYPTO_H diff --git a/client/src/mifare/desfiresecurechan.c b/client/src/mifare/desfiresecurechan.c new file mode 100644 index 000000000..2e49ce5ac --- /dev/null +++ b/client/src/mifare/desfiresecurechan.c @@ -0,0 +1,298 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 Romain Tartiere. +// Copyright (C) 2014 Iceman +// Copyright (C) 2021 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency Desfire secure channel functions +//----------------------------------------------------------------------------- + +#include "desfiresecurechan.h" + +#include +#include +#include +#include "ui.h" +#include "crc.h" +#include "crc16.h" // crc16 ccitt +#include "crc32.h" +#include "commonutil.h" +#include "protocols.h" +#include "mifare/desfire_crypto.h" + +AllowedChannelModesS AllowedChannelModes[] = { + {MFDES_CREATE_APPLICATION, DACd40, DCCNative, DCMPlain}, + {MFDES_DELETE_APPLICATION, DACd40, DCCNative, DCMPlain}, + {MFDES_GET_APPLICATION_IDS, DACd40, DCCNative, DCMPlain}, + {MFDES_GET_DF_NAMES, DACd40, DCCNative, DCMPlain}, + {MFDES_GET_KEY_SETTINGS, DACd40, DCCNative, DCMPlain}, + {MFDES_GET_KEY_VERSION, DACd40, DCCNative, DCMPlain}, + {MFDES_GET_FREE_MEMORY, DACd40, DCCNative, DCMPlain}, + + {MFDES_READ_DATA, DACd40, DCCNative, DCMMACed}, + {MFDES_WRITE_DATA, DACd40, DCCNative, DCMMACed}, + {MFDES_GET_VALUE, DACd40, DCCNative, DCMMACed}, + {MFDES_CREDIT, DACd40, DCCNative, DCMMACed}, + {MFDES_DEBIT, DACd40, DCCNative, DCMMACed}, + {MFDES_LIMITED_CREDIT, DACd40, DCCNative, DCMMACed}, + {MFDES_READ_RECORDS, DACd40, DCCNative, DCMMACed}, + {MFDES_WRITE_RECORD, DACd40, DCCNative, DCMMACed}, + {MFDES_UPDATE_RECORD1, DACd40, DCCNative, DCMMACed}, + {MFDES_UPDATE_RECORD2, DACd40, DCCNativeISO, DCMMACed}, + {MFDES_INIT_KEY_SETTINGS, DACd40, DCCNative, DCMMACed}, + {MFDES_FINALIZE_KEY_SETTINGS, DACd40, DCCNative, DCMMACed}, + {MFDES_ROLL_KEY_SETTINGS, DACd40, DCCNative, DCMMACed}, + {MFDES_COMMIT_READER_ID, DACd40, DCCNative, DCMMACed}, + + {MFDES_GET_UID, DACd40, DCCNative, DCMEncrypted}, + {MFDES_CHANGE_KEY_SETTINGS, DACd40, DCCNative, DCMEncrypted}, + {MFDES_READ_DATA, DACd40, DCCNative, DCMEncrypted}, + {MFDES_WRITE_DATA, DACd40, DCCNative, DCMEncrypted}, + + {MFDES_GET_KEY_VERSION, DACEV1, DCCNative, DCMPlain}, + {MFDES_GET_FREE_MEMORY, DACEV1, DCCNative, DCMPlain}, + + {MFDES_CREATE_APPLICATION, DACEV1, DCCNative, DCMMACed}, + {MFDES_DELETE_APPLICATION, DACEV1, DCCNative, DCMMACed}, + {MFDES_GET_APPLICATION_IDS, DACEV1, DCCNative, DCMMACed}, + {MFDES_GET_DF_NAMES, DACEV1, DCCNative, DCMMACed}, + {MFDES_GET_KEY_SETTINGS, DACEV1, DCCNative, DCMMACed}, + + {MFDES_GET_UID, DACEV1, DCCNative, DCMEncrypted}, + {MFDES_CHANGE_KEY_SETTINGS, DACEV1, DCCNative, DCMEncrypted}, +}; + +static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + + uint8_t data[1024] = {0}; + size_t rlen = 0; + + switch (ctx->commMode) { + case DCMPlain: + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + break; + case DCMMACed: + if (srcdatalen == 0) + break; + + rlen = srcdatalen + DesfireGetMACLength(ctx); + memcpy(data, srcdata, srcdatalen); + DesfireCryptoEncDec(ctx, true, data, srcdatalen, NULL, true); + memcpy(dstdata, srcdata, srcdatalen); + memcpy(&dstdata[srcdatalen], ctx->IV, 4); + *dstdatalen = rlen; + break; + case DCMEncrypted: + if (srcdatalen == 0) + break; + + rlen = padded_data_length(srcdatalen + 2, desfire_get_key_block_length(ctx->keyType)); // 2 - crc16 + memcpy(data, srcdata, srcdatalen); + compute_crc(CRC_14443_A, data, srcdatalen, &data[srcdatalen], &data[srcdatalen + 1]); + DesfireCryptoEncDec(ctx, true, data, rlen, dstdata, true); + *dstdatalen = rlen; + break; + case DCMNone: + ; + } +} + +static void DesfireSecureChannelEncodeEV1(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { + uint8_t data[1024] = {0}; + size_t rlen = 0; + + // we calc MAC anyway + // if encypted channel and no data - we only calc MAC + if (ctx->commMode == DCMPlain || ctx->commMode == DCMMACed || (ctx->commMode == DCMEncrypted && srcdatalen == 0)) { + data[0] = cmd; + memcpy(&data[1], srcdata, srcdatalen); + uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; + DesfireCryptoCMAC(ctx, data, srcdatalen + 1, cmac); + + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + if (srcdatalen != 0 && ctx->commMode == DCMMACed) { + memcpy(&dstdata[srcdatalen], cmac, DesfireGetMACLength(ctx)); + *dstdatalen = srcdatalen + DesfireGetMACLength(ctx); + } + } else if (ctx->commMode == DCMEncrypted) { + rlen = padded_data_length(srcdatalen + 4, desfire_get_key_block_length(ctx->keyType)); + data[0] = cmd; + memcpy(&data[1], srcdata, srcdatalen); + desfire_crc32_append(data, srcdatalen + 1); + + DesfireCryptoEncDec(ctx, true, &data[1], rlen, dstdata, true); + + *dstdatalen = rlen; + } else { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + } +} + +void DesfireSecureChannelEncode(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) { + ctx->lastCommand = cmd; + ctx->lastRequestZeroLen = (srcdatalen == 0); + + switch (ctx->secureChannel) { + case DACd40: + DesfireSecureChannelEncodeD40(ctx, cmd, srcdata, srcdatalen, dstdata, dstdatalen); + break; + case DACEV1: + DesfireSecureChannelEncodeEV1(ctx, cmd, srcdata, srcdatalen, dstdata, dstdatalen); + break; + case DACEV2: + break; + case DACNone: + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + break; + } +} + +static void DesfireSecureChannelDecodeD40(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + + switch (ctx->commMode) { + case DCMMACed: + + break; + case DCMEncrypted: + if (srcdatalen < desfire_get_key_block_length(ctx->keyType)) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + return; + } + + DesfireCryptoEncDec(ctx, true, srcdata, srcdatalen, dstdata, false); + //PrintAndLogEx(INFO, "decoded[%d]: %s", srcdatalen, sprint_hex(dstdata, srcdatalen)); + + size_t puredatalen = DesfireSearchCRCPos(dstdata, srcdatalen, respcode, 2); + if (puredatalen != 0) { + *dstdatalen = puredatalen; + } else { + PrintAndLogEx(WARNING, "CRC16 error."); + *dstdatalen = srcdatalen; + } + break; + case DCMPlain: + case DACNone: + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + break; + } +} + +static void DesfireSecureChannelDecodeEV1(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) { + uint8_t data[1024] = {0}; + + // if comm mode = plain --> response with MAC + // if request is not zero length --> response MAC + if (ctx->commMode == DCMPlain || ctx->commMode == DCMMACed || (ctx->commMode == DCMEncrypted && !ctx->lastRequestZeroLen)) { + if (srcdatalen < DesfireGetMACLength(ctx)) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + return; + } + + memcpy(dstdata, srcdata, srcdatalen - DesfireGetMACLength(ctx)); + *dstdatalen = srcdatalen - DesfireGetMACLength(ctx); + + memcpy(data, srcdata, *dstdatalen); + data[*dstdatalen] = respcode; + + uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0}; + DesfireCryptoCMAC(ctx, data, *dstdatalen + 1, cmac); + if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) { + PrintAndLogEx(WARNING, "Received MAC is not match with calculated"); + PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], desfire_get_key_block_length(ctx->keyType))); + PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType))); + } + } else if (ctx->commMode == DCMEncrypted) { + if (srcdatalen < desfire_get_key_block_length(ctx->keyType)) { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + return; + } + + DesfireCryptoEncDec(ctx, true, srcdata, srcdatalen, dstdata, false); + //PrintAndLogEx(INFO, "decoded[%d]: %s", srcdatalen, sprint_hex(dstdata, srcdatalen)); + + size_t puredatalen = DesfireSearchCRCPos(dstdata, srcdatalen, respcode, 4); + if (puredatalen != 0) { + *dstdatalen = puredatalen; + } else { + PrintAndLogEx(WARNING, "CRC32 error."); + *dstdatalen = srcdatalen; + } + + } else { + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + } +} + +void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) { + switch (ctx->secureChannel) { + case DACd40: + DesfireSecureChannelDecodeD40(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen); + break; + case DACEV1: + DesfireSecureChannelDecodeEV1(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen); + break; + case DACEV2: + break; + case DACNone: + memcpy(dstdata, srcdata, srcdatalen); + *dstdatalen = srcdatalen; + break; + } +} + +bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, DesfireCommandSet cmdSet, DesfireCommunicationMode commMode) { + if (commMode == DCMNone) { + PrintAndLogEx(WARNING, "Communication mode can't be NONE. command: %02x", cmd); + return false; + } + + // no security set + if (secureChannel == DACNone) + return true; + + bool found = false; + for (int i = 0; i < ARRAY_LENGTH(AllowedChannelModes); i++) + if (AllowedChannelModes[i].cmd == cmd) { + // full compare + if (AllowedChannelModes[i].secureChannel == secureChannel && + (AllowedChannelModes[i].cmdSet == cmdSet || (AllowedChannelModes[i].cmdSet == DCCNative && cmdSet == DCCNativeISO)) && + AllowedChannelModes[i].commMode == commMode) { + + found = true; + break; + } + + // ev1 plain and mac are the same + if (AllowedChannelModes[i].secureChannel == secureChannel && + AllowedChannelModes[i].secureChannel == DACEV1 && + (AllowedChannelModes[i].cmdSet == cmdSet || (AllowedChannelModes[i].cmdSet == DCCNative && cmdSet == DCCNativeISO)) && + (commMode == DCMPlain || commMode == DCMMACed)) { + + found = true; + break; + } + + } + + if (!found) + PrintAndLogEx(WARNING, "Wrong communication mode. Check settings. command: %02x", cmd); + + return found; +} + diff --git a/client/src/mifare/desfiresecurechan.h b/client/src/mifare/desfiresecurechan.h new file mode 100644 index 000000000..aa056323a --- /dev/null +++ b/client/src/mifare/desfiresecurechan.h @@ -0,0 +1,34 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2010 Romain Tartiere. +// Copyright (C) 2014 Iceman +// Copyright (C) 2021 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency Desfire secure channel functions +//----------------------------------------------------------------------------- + +#ifndef __DESFIRESECURECHAN_H +#define __DESFIRESECURECHAN_H + +#include "common.h" +#include "mifare/desfirecore.h" +#include "mifare/desfirecrypto.h" +#include "mifare/desfire_crypto.h" +#include "mifare/mifare4.h" + +typedef struct { + uint8_t cmd; + DesfireSecureChannel secureChannel; + DesfireCommandSet cmdSet; + DesfireCommunicationMode commMode; +} AllowedChannelModesS; + +void DesfireSecureChannelEncode(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen); +void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen); + +bool PrintChannelModeWarning(uint8_t cmd, DesfireSecureChannel secureChannel, DesfireCommandSet cmdSet, DesfireCommunicationMode commMode); + +#endif // __DESFIRESECURECHAN_H diff --git a/client/src/mifare/desfiretest.c b/client/src/mifare/desfiretest.c new file mode 100644 index 000000000..b89963352 --- /dev/null +++ b/client/src/mifare/desfiretest.c @@ -0,0 +1,228 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2021 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// tests for desfire +//----------------------------------------------------------------------------- + +#include "desfiretest.h" + +#include +#include // memcpy memset +#include "fileutils.h" + +#include "crypto/libpcrypto.h" +#include "mifare/desfirecrypto.h" + +static uint8_t CMACData[] = {0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, + 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, + 0xAE, 0x2D, 0x8A, 0x57, 0x1E, 0x03, 0xAC, 0x9C, + 0x9E, 0xB7, 0x6F, 0xAC, 0x45, 0xAF, 0x8E, 0x51 + }; + + +static bool TestCRC16(void) { + uint8_t data[] = {0x04, 0x44, 0x0F, 0x32, 0x76, 0x31, 0x80, 0x27, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + bool res = true; + size_t len = DesfireSearchCRCPos(data, 16, 0x00, 2); + res = res && (len == 7); + + len = DesfireSearchCRCPos(data, 7 + 2, 0x00, 2); + res = res && (len == 7); + + len = DesfireSearchCRCPos(data, 7, 0x00, 2); + res = res && (len == 0); + + len = DesfireSearchCRCPos(data, 3, 0x00, 2); + res = res && (len == 0); + + len = DesfireSearchCRCPos(data, 1, 0x00, 2); + res = res && (len == 0); + + if (res) + PrintAndLogEx(INFO, "crc16............ " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "crc16............ " _RED_("fail")); + + return res; +} + +static bool TestCRC32(void) { + uint8_t data[] = {0x04, 0x44, 0x0F, 0x32, 0x76, 0x31, 0x80, 0x99, 0xCE, 0x1A, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00}; + + bool res = true; + size_t len = DesfireSearchCRCPos(data, 16, 0x00, 4); + res = res && (len == 7); + + len = DesfireSearchCRCPos(data, 7 + 4, 0x00, 4); + res = res && (len == 7); + + len = DesfireSearchCRCPos(data, 5, 0x00, 4); + res = res && (len == 0); + + len = DesfireSearchCRCPos(data, 4, 0x00, 4); + res = res && (len == 0); + + len = DesfireSearchCRCPos(data, 2, 0x00, 4); + res = res && (len == 0); + + if (res) + PrintAndLogEx(INFO, "crc32............ " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "crc32............ " _RED_("fail")); + + return res; +} + +// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/TDES_CMAC.pdf +static bool TestCMAC3TDEA(void) { + bool res = true; + + uint8_t key[DESFIRE_MAX_KEY_SIZE] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, + 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 + }; + DesfireContext dctx; + DesfireSetKey(&dctx, 0, T_3K3DES, key); + memcpy(dctx.sessionKeyMAC, key, DESFIRE_MAX_KEY_SIZE); + uint8_t cmac[DESFIRE_MAX_KEY_SIZE] = {0}; + + uint8_t cmac1[] = {0x7D, 0xB0, 0xD3, 0x7D, 0xF9, 0x36, 0xC5, 0x50}; + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 0, cmac); + res = res && (memcmp(cmac, cmac1, sizeof(cmac1)) == 0); + + uint8_t cmac2[] = {0x30, 0x23, 0x9C, 0xF1, 0xF5, 0x2E, 0x66, 0x09}; + memset(cmac, 0, sizeof(cmac)); + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 16, cmac); + res = res && (memcmp(cmac, cmac2, sizeof(cmac1)) == 0); + + uint8_t cmac3[] = {0x6C, 0x9F, 0x3E, 0xE4, 0x92, 0x3F, 0x6B, 0xE2}; + memset(cmac, 0, sizeof(cmac)); + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 20, cmac); + res = res && (memcmp(cmac, cmac3, sizeof(cmac1)) == 0); + + uint8_t cmac4[] = {0x99, 0x42, 0x9B, 0xD0, 0xBF, 0x79, 0x04, 0xE5}; + memset(cmac, 0, sizeof(cmac)); + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 32, cmac); + res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0); + + if (res) + PrintAndLogEx(INFO, "CMAC 3TDEA....... " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "CMAC 3TDEA....... " _RED_("fail")); + + return res; +} + +// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/TDES_CMAC.pdf +static bool TestCMAC2TDEA(void) { + bool res = true; + + uint8_t key[DESFIRE_MAX_KEY_SIZE] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, + 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01 + }; + DesfireContext dctx; + DesfireSetKey(&dctx, 0, T_3DES, key); + memcpy(dctx.sessionKeyMAC, key, DESFIRE_MAX_KEY_SIZE); + uint8_t cmac[DESFIRE_MAX_KEY_SIZE] = {0}; + + uint8_t cmac1[] = {0x79, 0xCE, 0x52, 0xA7, 0xF7, 0x86, 0xA9, 0x60}; + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 0, cmac); +// PrintAndLogEx(INFO, "cmac: %s", sprint_hex(cmac, 16)); + res = res && (memcmp(cmac, cmac1, sizeof(cmac1)) == 0); + + uint8_t cmac2[] = {0xCC, 0x18, 0xA0, 0xB7, 0x9A, 0xF2, 0x41, 0x3B}; + memset(cmac, 0, sizeof(cmac)); + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 16, cmac); + res = res && (memcmp(cmac, cmac2, sizeof(cmac1)) == 0); + + uint8_t cmac3[] = {0xC0, 0x6D, 0x37, 0x7E, 0xCD, 0x10, 0x19, 0x69}; + memset(cmac, 0, sizeof(cmac)); + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 20, cmac); + res = res && (memcmp(cmac, cmac3, sizeof(cmac1)) == 0); + + uint8_t cmac4[] = {0x9C, 0xD3, 0x35, 0x80, 0xF9, 0xB6, 0x4D, 0xFB}; + memset(cmac, 0, sizeof(cmac)); + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 32, cmac); + res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0); + + if (res) + PrintAndLogEx(INFO, "CMAC 2TDEA....... " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "CMAC 2TDEA....... " _RED_("fail")); + + return res; +} + +static bool TestCMACDES(void) { + bool res = true; + + uint8_t key[DESFIRE_MAX_KEY_SIZE] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; + DesfireContext dctx; + DesfireSetKey(&dctx, 0, T_DES, key); + memcpy(dctx.sessionKeyMAC, key, DESFIRE_MAX_KEY_SIZE); + uint8_t cmac[DESFIRE_MAX_KEY_SIZE] = {0}; + + uint8_t cmac1[] = {0x86, 0xF7, 0x9C, 0x13, 0xFD, 0x30, 0x6E, 0x67}; + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 0, cmac); + res = res && (memcmp(cmac, cmac1, sizeof(cmac1)) == 0); + + uint8_t cmac2[] = {0xBE, 0xA4, 0x21, 0x22, 0x92, 0x46, 0x2A, 0x85}; + memset(cmac, 0, sizeof(cmac)); + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 16, cmac); + res = res && (memcmp(cmac, cmac2, sizeof(cmac1)) == 0); + + uint8_t cmac3[] = {0x3E, 0x2F, 0x83, 0x10, 0xC5, 0x69, 0x27, 0x5E}; + memset(cmac, 0, sizeof(cmac)); + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 20, cmac); + res = res && (memcmp(cmac, cmac3, sizeof(cmac1)) == 0); + + uint8_t cmac4[] = {0x9D, 0x1F, 0xC4, 0xD4, 0xC0, 0x25, 0x91, 0x32}; + memset(cmac, 0, sizeof(cmac)); + memset(dctx.IV, 0, DESFIRE_MAX_KEY_SIZE); + DesfireCryptoCMAC(&dctx, CMACData, 32, cmac); + res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0); + + if (res) + PrintAndLogEx(INFO, "CMAC DES......... " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "CMAC DES......... " _RED_("fail")); + + return res; +} + +bool DesfireTest(bool verbose) { + bool res = true; + + PrintAndLogEx(INFO, "------ " _CYAN_("Desfire Tests") " ------"); + + res = res && TestCRC16(); + res = res && TestCRC32(); + res = res && TestCMAC3TDEA(); + res = res && TestCMAC2TDEA(); + res = res && TestCMACDES(); + + PrintAndLogEx(INFO, "---------------------------"); + if (res) + PrintAndLogEx(SUCCESS, " Tests [ %s ]", _GREEN_("ok")); + else + PrintAndLogEx(FAILED, " Tests [ %s ]", _RED_("fail")); + + PrintAndLogEx(NORMAL, ""); + return res; +} diff --git a/client/src/mifare/desfiretest.h b/client/src/mifare/desfiretest.h new file mode 100644 index 000000000..e89330899 --- /dev/null +++ b/client/src/mifare/desfiretest.h @@ -0,0 +1,19 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2021 Merlok +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// tests for desfire +//----------------------------------------------------------------------------- + +#ifndef __CIPURSETEST_H__ +#define __CIPURSETEST_H__ + +#include +#include "common.h" + +bool DesfireTest(bool verbose); + +#endif /* __CIPURSETEST_H__ */ diff --git a/client/src/mifare/mifarehost.c b/client/src/mifare/mifarehost.c index d068aaac0..cba4d5ac7 100644 --- a/client/src/mifare/mifarehost.c +++ b/client/src/mifare/mifarehost.c @@ -56,6 +56,8 @@ int mfDarkside(uint8_t blockno, uint8_t key_type, uint64_t *key) { return PM3_EOPABORTED; } + PrintAndLogEx(INFO, "." NOLF); + // wait cycle while (true) { PrintAndLogEx(NORMAL, "." NOLF); @@ -197,16 +199,20 @@ int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, uint32_t size, uint8_t *keyBlock, sector_t *e_sector, bool use_flashmemory) { uint64_t t2 = msclock(); - uint32_t timeout = 0; // send keychunk clearCommandBuffer(); SendCommandOLD(CMD_HF_MIFARE_CHKKEYS_FAST, (sectorsCnt | (firstChunk << 8) | (lastChunk << 12)), ((use_flashmemory << 8) | strategy), size, keyBlock, 6 * size); PacketResponseNG resp; + uint32_t timeout = 0; while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + + PrintAndLogEx((timeout == 0) ? INFO : NORMAL, "." NOLF); + fflush(stdout); + timeout++; - PrintAndLogEx(NORMAL, "." NOLF); + // max timeout for one chunk of 85keys, 60*3sec = 180seconds // s70 with 40*2 keys to check, 80*85 = 6800 auth. // takes about 97s, still some margin before abort @@ -217,6 +223,10 @@ int mfCheckKeys_fast(uint8_t sectorsCnt, uint8_t firstChunk, uint8_t lastChunk, } t2 = msclock() - t2; + if (timeout) { + PrintAndLogEx(NORMAL, ""); + } + // time to convert the returned data. uint8_t curr_keys = resp.oldarg[0]; @@ -538,7 +548,7 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, PrintAndLogEx(SUCCESS, "\ntarget block:%3u key type: %c -- found valid key [ " _GREEN_("%s") "]", package->block, package->keytype ? 'B' : 'A', - sprint_hex(resultKey, 6) + sprint_hex_inrow(resultKey, 6) ); return PM3_SUCCESS; } @@ -717,7 +727,7 @@ int mfStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBl PrintAndLogEx(SUCCESS, "target block:%3u key type: %c -- found valid key [ " _GREEN_("%s") "]", package->block, package->keytype ? 'B' : 'A', - sprint_hex(resultKey, 6) + sprint_hex_inrow(resultKey, 6) ); return PM3_SUCCESS; } else if (res == PM3_ETIMEOUT || res == PM3_EOPABORTED) { @@ -1042,6 +1052,27 @@ int mfGen3Freeze(void) { } } +int mfG3GetBlock(uint8_t blockno, uint8_t *data) { + struct p { + uint8_t blockno; + } PACKED payload; + payload.blockno = blockno; + + clearCommandBuffer(); + SendCommandNG(CMD_HF_MIFARE_G3_RDBL, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_HF_MIFARE_G3_RDBL, &resp, 1500)) { + if (resp.status != PM3_SUCCESS) + return PM3_EUNDEF; + memcpy(data, resp.data.asBytes, 16); + } else { + PrintAndLogEx(WARNING, "command execute timeout"); + return PM3_ETIMEOUT; + } + return PM3_SUCCESS; +} + + // variables uint32_t cuid = 0; // uid part used for crypto1. diff --git a/client/src/mifare/mifarehost.h b/client/src/mifare/mifarehost.h index 49adc8cbe..8f5f215fd 100644 --- a/client/src/mifare/mifarehost.h +++ b/client/src/mifare/mifarehost.h @@ -85,6 +85,8 @@ int mfGen3UID(uint8_t *uid, uint8_t uidlen, uint8_t *oldUid); int mfGen3Block(uint8_t *block, int blockLen, uint8_t *newBlock); int mfGen3Freeze(void); +int mfG3GetBlock(uint8_t blockno, uint8_t *data); + int tryDecryptWord(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/nfc/ndef.c b/client/src/nfc/ndef.c index bd1109e80..ac131b221 100644 --- a/client/src/nfc/ndef.c +++ b/client/src/nfc/ndef.c @@ -15,9 +15,17 @@ #include "ui.h" #include "util.h" // sprint_hex... #include "crypto/asn1utils.h" +#include "crypto/libpcrypto.h" +#include "ecp.h" +#include "commonutil.h" // ARRAYLEN #include "pm3_cmd.h" -#define STRBOOL(p) ((p) ? "+" : "-") +#define STRBOOL(p) ((p) ? "1" : "0") + +#define NDEF_WIFIAPPL "application/vnd.wfa" +#define NDEF_BLUEAPPL "application/vnd.bluetooth" +#define NDEF_VCARDTEXT "text/vcard" +#define NDEF_XVCARDTEXT "text/x-vcard" static const char *TypeNameFormat_s[] = { "Empty Record", @@ -91,6 +99,9 @@ static const char *URI_s[] = { "urn:nfc:" // 0x23 }; +static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen); +static int ndefDecodePayload(NDEFHeader_t *ndef); + static uint16_t ndefTLVGetLength(uint8_t *data, size_t *indx) { uint16_t len = 0; if (data[0] == 0xff) { @@ -142,20 +153,122 @@ static int ndefDecodeHeader(uint8_t *data, size_t datalen, NDEFHeader_t *header) } static int ndefPrintHeader(NDEFHeader_t *header) { - PrintAndLogEx(INFO, "Header:"); + PrintAndLogEx(INFO, _CYAN_("Header info")); - PrintAndLogEx(SUCCESS, "\tMessage Begin: %s", STRBOOL(header->MessageBegin)); - PrintAndLogEx(SUCCESS, "\tMessage End: %s", STRBOOL(header->MessageEnd)); - PrintAndLogEx(SUCCESS, "\tChunk Flag: %s", STRBOOL(header->ChunkFlag)); - PrintAndLogEx(SUCCESS, "\tShort Record Bit: %s", STRBOOL(header->ShortRecordBit)); - PrintAndLogEx(SUCCESS, "\tID Len Present: %s", STRBOOL(header->IDLenPresent)); - PrintAndLogEx(SUCCESS, "\tType Name Format: [0x%02x] %s", header->TypeNameFormat, TypeNameFormat_s[header->TypeNameFormat]); + PrintAndLogEx(SUCCESS, " %s ....... Message begin", STRBOOL(header->MessageBegin)); + PrintAndLogEx(SUCCESS, " %s ...... Message end", STRBOOL(header->MessageEnd)); + PrintAndLogEx(SUCCESS, " %s ..... Chunk flag", STRBOOL(header->ChunkFlag)); + PrintAndLogEx(SUCCESS, " %s .... Short record bit", STRBOOL(header->ShortRecordBit)); + PrintAndLogEx(SUCCESS, " %s ... ID Len present", STRBOOL(header->IDLenPresent)); + PrintAndLogEx(SUCCESS, ""); - PrintAndLogEx(SUCCESS, "\tHeader length : %zu", header->len); - PrintAndLogEx(SUCCESS, "\tType length : %zu", header->TypeLen); - PrintAndLogEx(SUCCESS, "\tPayload length : %zu", header->PayloadLen); - PrintAndLogEx(SUCCESS, "\tID length : %zu", header->IDLen); - PrintAndLogEx(SUCCESS, "\tRecord length : %zu", header->RecLen); + PrintAndLogEx(SUCCESS, " Header length...... %zu", header->len); + PrintAndLogEx(SUCCESS, " Type length........ %zu", header->TypeLen); + PrintAndLogEx(SUCCESS, " Payload length..... %zu", header->PayloadLen); + PrintAndLogEx(SUCCESS, " ID length.......... %zu", header->IDLen); + PrintAndLogEx(SUCCESS, " Record length...... %zu", header->RecLen); + + PrintAndLogEx(SUCCESS, " Type name format... [ 0x%02x ] " _YELLOW_("%s"), header->TypeNameFormat, TypeNameFormat_s[header->TypeNameFormat]); + return PM3_SUCCESS; +} + +static const char *get_curve_name(mbedtls_ecp_group_id grp_id) { + switch (grp_id) { + case MBEDTLS_ECP_DP_NONE: + return ""; + case MBEDTLS_ECP_DP_SECP192R1: + return "SECP192R1"; // Domain parameters for the 192-bit curve defined by FIPS 186-4 and SEC1 + case MBEDTLS_ECP_DP_SECP224R1: + return "SECP224R1"; // Domain parameters for the 224-bit curve defined by FIPS 186-4 and SEC1 + case MBEDTLS_ECP_DP_SECP256R1: + return "SECP256R1"; // Domain parameters for the 256-bit curve defined by FIPS 186-4 and SEC1 + case MBEDTLS_ECP_DP_SECP384R1: + return "SECP384R1"; // Domain parameters for the 384-bit curve defined by FIPS 186-4 and SEC1 + case MBEDTLS_ECP_DP_SECP521R1: + return "SECP521R1"; // Domain parameters for the 521-bit curve defined by FIPS 186-4 and SEC1 + case MBEDTLS_ECP_DP_BP256R1: + return "BP256R1"; // Domain parameters for 256-bit Brainpool curve + case MBEDTLS_ECP_DP_BP384R1: + return "BP384R1"; // Domain parameters for 384-bit Brainpool curve + case MBEDTLS_ECP_DP_BP512R1: + return "BP512R1"; // Domain parameters for 512-bit Brainpool curve + case MBEDTLS_ECP_DP_CURVE25519: + return "CURVE25519"; // Domain parameters for Curve25519 + case MBEDTLS_ECP_DP_SECP192K1: + return "SECP192K1"; // Domain parameters for 192-bit "Koblitz" curve + case MBEDTLS_ECP_DP_SECP224K1: + return "SECP224K1"; // Domain parameters for 224-bit "Koblitz" curve + case MBEDTLS_ECP_DP_SECP256K1: + return "SECP256K1"; // Domain parameters for 256-bit "Koblitz" curve + case MBEDTLS_ECP_DP_CURVE448: + return "CURVE448"; // Domain parameters for Curve448 + case MBEDTLS_ECP_DP_SECP128R1: + return "SECP128R1"; // Domain parameters for the 128-bit curve used for NXP originality check + default : + return ""; + } + return ""; +} + +typedef struct { + mbedtls_ecp_group_id grp_id; + uint8_t keylen; + const char *desc; + const char *value; +} PACKED ndef_publickey_t; + +static int ndef_print_signature(uint8_t *data, uint8_t data_len, uint8_t *signature, uint8_t sign_len) { + + const ndef_publickey_t ndef_public_keys[] = { + { MBEDTLS_ECP_DP_SECP256R1, 65, "Minecraft Earth", "04760200b60315f31ff7951d0892b87930c34967dfbf57763afc775fc56a22b601f7b8fd9e47519524505322435b07d0782463f39400a39a9dbc06bab2225c082a"}, + }; + + uint8_t i; + int reason = 0; + bool is_valid = false; + for (i = 0; i < ARRAYLEN(ndef_public_keys); i++) { + + int dl = 0; + uint8_t key[ndef_public_keys[i].keylen]; + param_gethex_to_eol(ndef_public_keys[i].value, 0, key, ndef_public_keys[i].keylen, &dl); + + int res = ecdsa_signature_r_s_verify(ndef_public_keys[i].grp_id, key, data, data_len, signature, sign_len, false); + is_valid = (res == 0); + if (is_valid) { + reason = 1; + break; + } + + // try with sha256 + res = ecdsa_signature_r_s_verify(ndef_public_keys[i].grp_id, key, data, data_len, signature, sign_len, true); + is_valid = (res == 0); + if (is_valid) { + reason = 2; + break; + } + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("NDEF Signature")); + if (is_valid == false || i == ARRAYLEN(ndef_public_keys)) { + PrintAndLogEx(INFO, " NDEF Signature: %s", sprint_hex_inrow(signature, 32)); + PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed")); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, " IC signature public key name: %s", ndef_public_keys[i].desc); + PrintAndLogEx(INFO, "IC signature public key value: %s", ndef_public_keys[i].value); + PrintAndLogEx(INFO, " Elliptic curve parameters: %s", get_curve_name(ndef_public_keys[i].grp_id)); + PrintAndLogEx(INFO, " NDEF Signature: %s", sprint_hex_inrow(signature, 32)); + PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful")); + switch (reason) { + case 1: + PrintAndLogEx(INFO, " Params used: signature, plain"); + break; + case 2: + PrintAndLogEx(INFO, " Params used: signature, SHA256"); + break; + } return PM3_SUCCESS; } @@ -251,6 +364,8 @@ static int ndefDecodeSig2(uint8_t *sig, size_t siglen) { PrintAndLogEx(SUCCESS, "\tsignature : " _GREEN_("ECDSA-%d"), slen * 8); PrintAndLogEx(SUCCESS, "\t\tr: %s", sprint_hex(&sig[indx], slen)); PrintAndLogEx(SUCCESS, "\t\ts: %s", sprint_hex(&sig[indx + slen], slen)); + + ndef_print_signature(NULL, 0, NULL, 0); } } else { PrintAndLogEx(INFO, "\tsignature: unknown type"); @@ -301,18 +416,169 @@ static int ndefDecodeSig(uint8_t *sig, size_t siglen) { return PM3_ESOFT; } +static int ndefDecodePayloadDeviceInfo(uint8_t *payload, size_t len) { + if (payload == NULL) + return PM3_EINVARG; + if (len < 1) + return PM3_EINVARG; + + PrintAndLogEx(INFO, _CYAN_("Device information")); + uint8_t *p = payload; + p++; + uint8_t n = *(p++); + PrintAndLogEx(INFO, "Vendor........ " _YELLOW_("%.*s"), n, p); + p += n + 1; + n = *(p++); + PrintAndLogEx(INFO, "Model......... " _YELLOW_("%.*s"), n, p); + p += n + 1; + n = *(p++); + PrintAndLogEx(INFO, "Unique name... " _YELLOW_("%.*s"), n, p); + p += n + 1; + n = *(p++); + //uuid string + // record.uuid_string = '123e4567-e89b-12d3-a456-426655440000' + // 8-4-4-4-12 + char uuid[37] = {0}; + sprintf(uuid, "%s-", sprint_hex_inrow(p, 4)); + p += 4; + sprintf(uuid + strlen(uuid), "%s-", sprint_hex_inrow(p, 2)); + p += 2; + sprintf(uuid + strlen(uuid), "%s-", sprint_hex_inrow(p, 2)); + p += 2; + sprintf(uuid + strlen(uuid), "%s-", sprint_hex_inrow(p, 2)); + p += 2; + sprintf(uuid + strlen(uuid), "%s", sprint_hex_inrow(p, 6)); + p += 6; + PrintAndLogEx(INFO, "UUID.......... " _YELLOW_("%s"), uuid); + p++; + n = *(p++); + PrintAndLogEx(INFO, "Version....... " _YELLOW_("%.*s"), n, p); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + +static int ndefDecodePayloadSmartPoster(uint8_t *ndef, size_t ndeflen, bool print, bool verbose) { + if (print) { + PrintAndLogEx(INFO, _YELLOW_("Well Known Record - Smartposter {")); + } + + NDEFHeader_t NDEFHeader = {0}; + int res = ndefDecodeHeader(ndef, ndeflen, &NDEFHeader); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "decode header failed.."); + return res; + } + + if (verbose) { + ndefPrintHeader(&NDEFHeader); + } + + if (NDEFHeader.TypeLen && NDEFHeader.PayloadLen) { + ndefDecodePayload(&NDEFHeader); + } + + if (NDEFHeader.TypeLen) { + PrintAndLogEx(INFO, "Type data"); + print_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, 1); + } + if (NDEFHeader.IDLen) { + PrintAndLogEx(INFO, "ID data"); + print_buffer(NDEFHeader.ID, NDEFHeader.IDLen, 1); + } + if (NDEFHeader.PayloadLen) { + PrintAndLogEx(INFO, "Payload data"); + print_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, 1); + } + // recursive + if (NDEFHeader.MessageEnd == false) { + ndefDecodePayloadSmartPoster(ndef + NDEFHeader.RecLen, ndeflen - NDEFHeader.RecLen, false, false); + } + + if (print) { + PrintAndLogEx(INFO, _YELLOW_("}")); + } + return PM3_SUCCESS; +} + +static int ndefDecodeMime_wifi(NDEFHeader_t *ndef) { + PrintAndLogEx(INFO, _CYAN_("WiFi details")); + if (ndef->PayloadLen > 1) { + PrintAndLogEx(INFO, ">>> decorder, to be implemented <<<"); + } + return PM3_SUCCESS; +} + +static int ndefDecodeMime_vcard(NDEFHeader_t *ndef) { + PrintAndLogEx(INFO, _CYAN_("VCARD details")); + if (ndef->PayloadLen > 1) { + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "%.*s", (int)ndef->PayloadLen, ndef->Payload); + } + return PM3_SUCCESS; +} + +static int ndefDecodeMime_bt(NDEFHeader_t *ndef) { + PrintAndLogEx(INFO, "Type............ " _YELLOW_("%.*s"), (int)ndef->TypeLen, ndef->Type); + if (ndef->PayloadLen > 1) { + uint16_t ooblen = (ndef->Payload[1] << 8 | ndef->Payload[0]); + PrintAndLogEx(INFO, "OOB data len.... %u", ooblen); + PrintAndLogEx(INFO, "BT MAC.......... " _YELLOW_("%s"), sprint_hex(ndef->Payload + 2, 6)); + // Let's check payload[8]. Tells us a bit about the UUID's. If 0x07 then it tells us a service UUID is 128bit + switch (ndef->Payload[8]) { + case 0x02: + PrintAndLogEx(INFO, "Optional Data... incomplete list 16-bit UUID's"); + break; + case 0x03: + PrintAndLogEx(INFO, "Optional Data... complete list 16-bit UUID's"); + break; + case 0x04: + PrintAndLogEx(INFO, "Optional Data... incomplete list 32-bit UUID's"); + break; + case 0x05: + PrintAndLogEx(INFO, "Optional Data... complete list 32-bit UUID's"); + break; + case 0x06: + PrintAndLogEx(INFO, "Optional Data... incomplete list 128-bit UUID's"); + break; + case 0x07: + PrintAndLogEx(INFO, "Optional Data... complete list 128-bit UUID's"); + break; + default: + PrintAndLogEx(INFO, "Optional Data... [ %02x ]", ndef->Payload[8]); + break; + } + // Let's check payload[9]. If 0x08 then SHORT_NAME or if 0x09 then COMPLETE_NAME + if (ndef->Payload[9] == 0x08) { + PrintAndLogEx(INFO, "Short name...... " _YELLOW_("%.*s"), (int)(ndef->PayloadLen - 10), ndef->Payload + 10); + } else if (ndef->Payload[9] == 0x09) { + PrintAndLogEx(INFO, "Complete name... " _YELLOW_("%.*s"), (int)(ndef->PayloadLen - 10), ndef->Payload + 10); + } else { + PrintAndLogEx(INFO, "[ %02x ]", ndef->Payload[9]); + } + PrintAndLogEx(NORMAL, ""); + } + return PM3_SUCCESS; +} + static int ndefDecodePayload(NDEFHeader_t *ndef) { + PrintAndLogEx(INFO, ""); switch (ndef->TypeNameFormat) { + case tnfEmptyRecord: + PrintAndLogEx(INFO, "Empty Record"); + if (ndef->TypeLen != 0 || ndef->IDLen != 0 || ndef->PayloadLen != 0) { + PrintAndLogEx(FAILED, "unexpected data in TNF_EMPTY record"); + break; + } + break; case tnfWellKnownRecord: - PrintAndLogEx(INFO, "Well Known Record"); - PrintAndLogEx(INFO, "\ttype\t: %.*s", (int)ndef->TypeLen, ndef->Type); if (!strncmp((char *)ndef->Type, "T", ndef->TypeLen)) { + PrintAndLogEx(INFO, _CYAN_("Text")); uint8_t utf8 = (ndef->Payload[0] >> 7); uint8_t lc_len = ndef->Payload[0] & 0x3F; PrintAndLogEx(INFO, - "\tUTF %d\t: " _GREEN_("%.*s") ", " _GREEN_("%.*s"), + " UTF %d... " _GREEN_("%.*s") ", " _GREEN_("%.*s"), (utf8 == 0) ? 8 : 16, lc_len, ndef->Payload + 1, @@ -322,8 +588,9 @@ static int ndefDecodePayload(NDEFHeader_t *ndef) { } if (!strncmp((char *)ndef->Type, "U", ndef->TypeLen)) { + PrintAndLogEx(INFO, _CYAN_("URL")); PrintAndLogEx(INFO - , "\turi\t: " _GREEN_("%s%.*s") + , " uri... " _GREEN_("%s%.*s") , (ndef->Payload[0] <= 0x23 ? URI_s[ndef->Payload[0]] : "[err]") , (int)(ndef->PayloadLen - 1) , &ndef->Payload[1] @@ -331,34 +598,76 @@ static int ndefDecodePayload(NDEFHeader_t *ndef) { } if (!strncmp((char *)ndef->Type, "Sig", ndef->TypeLen)) { + PrintAndLogEx(INFO, _CYAN_("Signature")); ndefDecodeSig(ndef->Payload, ndef->PayloadLen); } + if (!strncmp((char *)ndef->Type, "Sp", ndef->TypeLen)) { + ndefDecodePayloadSmartPoster(ndef->Payload, ndef->PayloadLen, true, false); + } + + if (!strncmp((char *)ndef->Type, "Di", ndef->TypeLen)) { + ndefDecodePayloadDeviceInfo(ndef->Payload, ndef->PayloadLen); + } + + if (!strncmp((char *)ndef->Type, "Hc", ndef->TypeLen)) { + PrintAndLogEx(INFO, _CYAN_("Handover carrier")); + PrintAndLogEx(INFO, "- decoder to be impl -"); + } + + if (!strncmp((char *)ndef->Type, "Hr", ndef->TypeLen)) { + PrintAndLogEx(INFO, _CYAN_("Handover request")); + PrintAndLogEx(INFO, "- decoder to be impl -"); + } + + if (!strncmp((char *)ndef->Type, "Hs", ndef->TypeLen)) { + PrintAndLogEx(INFO, _CYAN_("Handover select")); + PrintAndLogEx(INFO, "- decoder to be impl -"); + } + + if (!strncmp((char *)ndef->Type, "ac", ndef->TypeLen)) { + PrintAndLogEx(INFO, _CYAN_("Alternative carrier")); + PrintAndLogEx(INFO, "- decoder to be impl -"); + } break; + case tnfMIMEMediaRecord: { + PrintAndLogEx(INFO, "MIME Media Record"); + if (ndef->TypeLen == 0) { + PrintAndLogEx(INFO, "type length is zero"); + break; + } + + char begin[ndef->TypeLen]; + memcpy(begin, ndef->Type, ndef->TypeLen); + str_lower(begin); + + if (str_startswith(begin, NDEF_WIFIAPPL)) { + ndefDecodeMime_wifi(ndef); + } + if (str_startswith(begin, NDEF_VCARDTEXT) || str_startswith(begin, NDEF_XVCARDTEXT)) { + ndefDecodeMime_vcard(ndef); + } + if (str_startswith(begin, NDEF_BLUEAPPL)) { + ndefDecodeMime_bt(ndef); + } + + break; + } case tnfAbsoluteURIRecord: PrintAndLogEx(INFO, "Absolute URI Record"); - PrintAndLogEx(INFO, "\ttype : %.*s", (int)ndef->TypeLen, ndef->Type); - PrintAndLogEx(INFO, "\tpayload : %.*s", (int)ndef->PayloadLen, ndef->Payload); - break; - case tnfEmptyRecord: - PrintAndLogEx(INFO, "Empty Record"); - PrintAndLogEx(INFO, "\t -to be impl-"); - break; - case tnfMIMEMediaRecord: - PrintAndLogEx(INFO, "MIME Media Record"); - PrintAndLogEx(INFO, "\t -to be impl-"); + PrintAndLogEx(INFO, " payload : %.*s", (int)ndef->PayloadLen, ndef->Payload); break; case tnfExternalRecord: PrintAndLogEx(INFO, "External Record"); - PrintAndLogEx(INFO, "\t -to be impl-"); - break; - case tnfUnchangedRecord: - PrintAndLogEx(INFO, "Unchanged Record"); - PrintAndLogEx(INFO, "\t -to be impl-"); + PrintAndLogEx(INFO, "- decoder to be impl -"); break; case tnfUnknownRecord: PrintAndLogEx(INFO, "Unknown Record"); - PrintAndLogEx(INFO, "\t -to be impl-"); + PrintAndLogEx(INFO, "- decoder to be impl -"); + break; + case tnfUnchangedRecord: + PrintAndLogEx(INFO, "Unchanged Record"); + PrintAndLogEx(INFO, "- decoder to be impl -"); break; } return PM3_SUCCESS; @@ -371,20 +680,23 @@ static int ndefRecordDecodeAndPrint(uint8_t *ndefRecord, size_t ndefRecordLen) { return res; ndefPrintHeader(&NDEFHeader); + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, _CYAN_("Payload info")); if (NDEFHeader.TypeLen) { - PrintAndLogEx(INFO, "Type data:"); + PrintAndLogEx(INFO, "Type data"); print_buffer(NDEFHeader.Type, NDEFHeader.TypeLen, 1); } if (NDEFHeader.IDLen) { - PrintAndLogEx(INFO, "ID data:"); + PrintAndLogEx(INFO, "ID data"); print_buffer(NDEFHeader.ID, NDEFHeader.IDLen, 1); } if (NDEFHeader.PayloadLen) { - PrintAndLogEx(INFO, "Payload data:"); + PrintAndLogEx(INFO, "Payload data"); print_buffer(NDEFHeader.Payload, NDEFHeader.PayloadLen, 1); - if (NDEFHeader.TypeLen) - ndefDecodePayload(&NDEFHeader); + } + if (NDEFHeader.TypeLen && NDEFHeader.PayloadLen) { + ndefDecodePayload(&NDEFHeader); } return PM3_SUCCESS; @@ -438,8 +750,6 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("NDEF parsing") " ----------------"); while (indx < ndefLen) { - - PrintAndLogEx(INFO, "-----------------------------------------------------"); switch (ndef[indx]) { case 0x00: { indx++; @@ -462,11 +772,11 @@ int NDEFDecodeAndPrint(uint8_t *ndef, size_t ndefLen, bool verbose) { uint8_t Size = ndef[indx + 1]; uint8_t BytesLockedPerLockBit = (ndef[indx + 2] >> 4) & 0x0f; uint8_t bytes_per_page = ndef[indx + 2] & 0x0f; - PrintAndLogEx(SUCCESS, " Pages addr (number of pages) : %d", pages_addr); - PrintAndLogEx(SUCCESS, "Byte offset (number of bytes) : %d", byte_offset); - PrintAndLogEx(SUCCESS, "Size in bits of the lock area : %d. bytes approx: %d", Size, Size / 8); - PrintAndLogEx(SUCCESS, " Number of bytes / page : %d", bytes_per_page); - PrintAndLogEx(SUCCESS, "Bytes Locked Per LockBit."); + PrintAndLogEx(SUCCESS, " Pages addr (number of pages)... %d", pages_addr); + PrintAndLogEx(SUCCESS, "Byte offset (number of bytes)... %d", byte_offset); + PrintAndLogEx(SUCCESS, "Size in bits of the lock area %d. bytes approx %d", Size, Size / 8); + PrintAndLogEx(SUCCESS, " Number of bytes / page... %d", bytes_per_page); + PrintAndLogEx(SUCCESS, "Bytes Locked Per LockBit"); PrintAndLogEx(SUCCESS, " number of bytes that each dynamic lock bit is able to lock: %d", BytesLockedPerLockBit); } indx += len; diff --git a/client/src/preferences.c b/client/src/preferences.c index 3228498f4..f34d535e3 100644 --- a/client/src/preferences.c +++ b/client/src/preferences.c @@ -243,6 +243,8 @@ void preferences_save_callback(json_t *root) { JsonSaveStr(root, "logging.level", "NORMAL"); } */ + JsonSaveInt(root, "client.exe.delay", session.client_exe_delay); + } void preferences_load_callback(json_t *root) { json_error_t up_error = {0}; @@ -331,6 +333,9 @@ void preferences_load_callback(json_t *root) { if (strncmp(tempStr, "extended", 8) == 0) session.device_debug_level = ddbEXTENDED; } */ + // client command execution delay + if (json_unpack_ex(root, &up_error, 0, "{s:i}", "client.exe.delay", &i1) == 0) + session.client_exe_delay = i1; } // Help Functions @@ -502,6 +507,11 @@ static void showBarModeState(prefShowOpt_t opt) { } } +static void showClientExeDelayState(void) { + PrintAndLogEx(INFO, " Cmd execution delay.... "_GREEN_("%u"), session.client_exe_delay); +} + + static int setCmdEmoji(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs set emoji ", @@ -718,6 +728,35 @@ static int setCmdDeviceDebug (const char *Cmd) } */ + +static int setCmdExeDelay(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs set clientdelay", + "Set presistent preference of delay before executing a command in the client", + "prefs set clientdelay --ms 0 --> unsets any delay\n" + "prefs set clientdelay --ms 1000 --> sets 1000ms delay" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0(NULL, "ms", "", "delay in micro seconds"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + uint16_t new_value = (uint16_t)arg_get_int_def(ctx, 1, 0); + CLIParserFree(ctx); + + if (session.client_exe_delay != new_value) { + showClientExeDelayState(); + session.client_exe_delay = new_value; + showClientExeDelayState(); + preferences_save(); + } else { + showClientExeDelayState(); + } + return PM3_SUCCESS; +} + static int setCmdHint(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "prefs set hints ", @@ -1045,9 +1084,26 @@ static int getCmdSavePaths(const char *Cmd) { return PM3_SUCCESS; } +static int getCmdExeDelay(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "prefs get clientdelay", + "Get preference of delay time before execution of a command in the client", + "prefs get clientdelay" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + showClientExeDelayState(); + return PM3_SUCCESS; +} + static command_t CommandTableGet[] = { {"barmode", getCmdBarMode, AlwaysAvailable, "Get bar mode preference"}, {"clientdebug", getCmdDebug, AlwaysAvailable, "Get client debug level preference"}, + {"clientdelay", getCmdExeDelay, AlwaysAvailable, "Get client execution delay preference"}, {"color", getCmdColor, AlwaysAvailable, "Get color support preference"}, {"savepaths", getCmdSavePaths, AlwaysAvailable, "Get file folder "}, // {"devicedebug", getCmdDeviceDebug, AlwaysAvailable, "Get device debug level"}, @@ -1061,6 +1117,7 @@ static command_t CommandTableSet[] = { {"help", setCmdHelp, AlwaysAvailable, "This help"}, {"barmode", setCmdBarMode, AlwaysAvailable, "Set bar mode"}, {"clientdebug", setCmdDebug, AlwaysAvailable, "Set client debug level"}, + {"clientdelay", setCmdExeDelay, AlwaysAvailable, "Set client execution delay"}, {"color", setCmdColor, AlwaysAvailable, "Set color support"}, {"emoji", setCmdEmoji, AlwaysAvailable, "Set emoji display"}, {"hints", setCmdHint, AlwaysAvailable, "Set hint display"}, @@ -1120,8 +1177,9 @@ static int CmdPrefShow(const char *Cmd) { showClientDebugState(prefShowNone); showPlotSliderState(prefShowNone); // showDeviceDebugState(prefShowNone); - showBarModeState(prefShowNone); + showClientExeDelayState(); + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } diff --git a/client/src/proxgui.cpp b/client/src/proxgui.cpp index f8dd10fa4..b8d19b10a 100644 --- a/client/src/proxgui.cpp +++ b/client/src/proxgui.cpp @@ -40,6 +40,7 @@ extern "C" void ShowGraphWindow(void) { } gui->ShowGraphWindow(); + } extern "C" void HideGraphWindow(void) { @@ -56,6 +57,36 @@ extern "C" void RepaintGraphWindow(void) { gui->RepaintGraphWindow(); } + +// hook up picture viewer +extern "C" void ShowPictureWindow(char *fn) { + if (!gui) { + // Show a notice if X11/XQuartz isn't available +#if defined(__MACH__) && defined(__APPLE__) + PrintAndLogEx(WARNING, "You appear to be on a MacOS device without XQuartz.\nYou may need to install XQuartz (https://www.xquartz.org/) to make the plot work."); +#else + PrintAndLogEx(WARNING, "You appear to be on an environment without an X11 server or without DISPLAY environment variable set.\nPlot may not work until you resolve these issues."); +#endif + return; + } + + gui->ShowPictureWindow(fn); +} + +extern "C" void HidePictureWindow(void) { + if (!gui) + return; + + gui->HidePictureWindow(); +} + +extern "C" void RepaintPictureWindow(void) { + if (!gui) + return; + + gui->RepaintPictureWindow(); +} + extern "C" void MainGraphics(void) { if (!gui) return; diff --git a/client/src/proxgui.h b/client/src/proxgui.h index 887a0e36d..f5942e926 100644 --- a/client/src/proxgui.h +++ b/client/src/proxgui.h @@ -22,6 +22,12 @@ extern "C" { void ShowGraphWindow(void); void HideGraphWindow(void); void RepaintGraphWindow(void); + +// hook up picture viewer +void ShowPictureWindow(char *fn); +void HidePictureWindow(void); +void RepaintPictureWindow(void); + void MainGraphics(void); void InitGraphics(int argc, char **argv, char *script_cmds_file, char *script_cmd, bool stayInCommandLoop); void ExitGraphics(void); diff --git a/client/src/proxguiqt.cpp b/client/src/proxguiqt.cpp index 6ba6b7ab7..54e431a3f 100644 --- a/client/src/proxguiqt.cpp +++ b/client/src/proxguiqt.cpp @@ -12,6 +12,7 @@ #include #include #include +//#include #include #include #include @@ -32,6 +33,7 @@ #include "graph.h" #include "cmddata.h" #include "util_darwin.h" +//#include "fileutils.h" extern "C" int preferences_save(void); @@ -54,6 +56,19 @@ void ProxGuiQT::HideGraphWindow(void) { emit HideGraphWindowSignal(); } +// emit picture viewer signals +void ProxGuiQT::ShowPictureWindow(char *fn) { + emit ShowPictureWindowSignal(fn); +} + +void ProxGuiQT::RepaintPictureWindow(void) { + emit RepaintPictureWindowSignal(); +} + +void ProxGuiQT::HidePictureWindow(void) { + emit HidePictureWindowSignal(); +} + void ProxGuiQT::Exit(void) { emit ExitSignal(); } @@ -71,6 +86,7 @@ void ProxGuiQT::_ShowGraphWindow(void) { plotwidget = new ProxWidget(); } plotwidget->show(); + } void ProxGuiQT::_RepaintGraphWindow(void) { @@ -87,6 +103,70 @@ void ProxGuiQT::_HideGraphWindow(void) { plotwidget->hide(); } +// picture viewer +void ProxGuiQT::_ShowPictureWindow(char *fn) { + + if (!plotapp) + return; + + if (fn == NULL) + return; + + size_t slen = strlen(fn); + if (slen == 0) + return; + + char *myfn = (char *)calloc(slen + 1, sizeof(uint8_t)); + if (myfn == NULL) + return; + + memcpy(myfn, fn, slen); + + if (!pictureWidget) { + +#if defined(__MACH__) && defined(__APPLE__) + makeFocusable(); +#endif + + pictureWidget = new PictureWidget(); + } + + QPixmap pm; + if (pm.load(myfn) == false) { + qWarning("Failed to load %s", myfn); + } + free(myfn); + free(fn); + + //QPixmap newPixmap = pm.scaled(QSize(50,50), Qt::KeepAspectRatio); + //pm = pm.scaled(pictureController->lbl_pm->size(), Qt::KeepAspectRatio); + + pictureController->lbl_pm->setPixmap(pm); + pictureController->lbl_pm->setScaledContents(false); + pictureController->lbl_pm->setAlignment(Qt::AlignCenter); + + QString s = QString("w: %1 h: %2") + .arg(pm.size().width()) + .arg(pm.size().height() + ); + pictureController->lbl_sz->setText(s); + pictureWidget->show(); +} + +void ProxGuiQT::_RepaintPictureWindow(void) { + if (!plotapp || !pictureWidget) + return; + + pictureWidget->update(); +} + +void ProxGuiQT::_HidePictureWindow(void) { + if (!plotapp || !pictureWidget) + return; + + pictureWidget->hide(); +} + void ProxGuiQT::_Exit(void) { delete this; } @@ -105,11 +185,30 @@ void ProxGuiQT::_StartProxmarkThread(void) { void ProxGuiQT::MainLoop() { plotapp = new QApplication(argc, argv); + // Setup the picture widget + pictureWidget = new PictureWidget(); + pictureController = new Ui::PictureForm(); + pictureController->setupUi(pictureWidget); +// pictureWidget->setAttribute(Qt::WA_DeleteOnClose,true); + + // Set picture widget position if no settings. + if (session.preferences_loaded == false) { + // Move controller widget below plot + //pictureController->move(x(), y() + frameSize().height()); + //pictureController->resize(size().width(), 200); + } + connect(this, SIGNAL(ShowGraphWindowSignal()), this, SLOT(_ShowGraphWindow())); connect(this, SIGNAL(RepaintGraphWindowSignal()), this, SLOT(_RepaintGraphWindow())); connect(this, SIGNAL(HideGraphWindowSignal()), this, SLOT(_HideGraphWindow())); + connect(this, SIGNAL(ExitSignal()), this, SLOT(_Exit())); + // hook up picture viewer signals + connect(this, SIGNAL(ShowPictureWindowSignal(char *)), this, SLOT(_ShowPictureWindow(char *))); + connect(this, SIGNAL(RepaintPictureWindowSignal()), this, SLOT(_RepaintPictureWindow())); + connect(this, SIGNAL(HidePictureWindowSignal()), this, SLOT(_HidePictureWindow())); + //start proxmark thread after starting event loop QTimer::singleShot(200, this, SLOT(_StartProxmarkThread())); @@ -118,21 +217,51 @@ void ProxGuiQT::MainLoop() { makeUnfocusable(); #endif - plotapp->exec(); } -ProxGuiQT::ProxGuiQT(int argc, char **argv, WorkerThread *wthread) : plotapp(NULL), plotwidget(NULL), - argc(argc), argv(argv), proxmarkThread(wthread) { +ProxGuiQT::ProxGuiQT(int argc, char **argv, WorkerThread *wthread) : + plotapp(NULL), plotwidget(NULL), pictureController(NULL), pictureWidget(NULL), argc(argc), argv(argv), proxmarkThread(wthread) { + } ProxGuiQT::~ProxGuiQT(void) { + + if (pictureController) { + delete pictureController; + pictureController = NULL; + } + + if (pictureWidget) { + pictureWidget->close(); + delete pictureWidget; + pictureWidget = NULL; + } + if (plotapp) { plotapp->quit(); plotapp = NULL; } } +// ------------------------------------------------- +// Slider Widget form based on a class to enable +// Event override functions +// ------------------------------------------------- +PictureWidget::PictureWidget() { + // Set the initail postion and size from settings +// if (session.preferences_loaded) +// setGeometry(session.pw.x, session.pw.y, session.pw.w, session.pw.h); +// else + resize(400, 400); +} + +void PictureWidget::closeEvent(QCloseEvent *event) { + this->hide(); + event->ignore(); +} + + // ------------------------------------------------- // Slider Widget form based on a class to enable // Event override functions @@ -197,6 +326,8 @@ void ProxWidget::vchange_dthr_down(int v) { g_useOverlays = true; RepaintGraphWindow(); } + + ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) { this->master = master; // Set the initail postion and size from settings @@ -206,7 +337,7 @@ ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) { resize(800, 400); // Setup the controller widget - controlWidget = new SliderWidget(); //new QWidget(); + controlWidget = new SliderWidget(); opsController = new Ui::Form(); opsController->setupUi(controlWidget); //Due to quirks in QT Designer, we need to fiddle a bit @@ -242,7 +373,7 @@ ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) { show(); // Set Slider/Overlay position if no settings. - if (!session.preferences_loaded) { + if (session.preferences_loaded == false) { // Move controller widget below plot controlWidget->move(x(), y() + frameSize().height()); controlWidget->resize(size().width(), 200); @@ -295,6 +426,7 @@ void ProxWidget::showEvent(QShowEvent *event) { controlWidget->show(); else controlWidget->hide(); + plot->show(); } void ProxWidget::moveEvent(QMoveEvent *event) { diff --git a/client/src/proxguiqt.h b/client/src/proxguiqt.h index 168b1b9eb..326274ae8 100644 --- a/client/src/proxguiqt.h +++ b/client/src/proxguiqt.h @@ -22,6 +22,7 @@ #include #include "ui/ui_overlays.h" +#include "ui/ui_image.h" class ProxWidget; @@ -70,6 +71,14 @@ class SliderWidget : public QWidget { SliderWidget(); }; +// Added class for SliderWidget to allow move/resize event override +class PictureWidget : public QWidget { + protected: + void closeEvent(QCloseEvent *event); + public: + PictureWidget(); +}; + /** * The window with plot and controls */ @@ -81,8 +90,8 @@ class ProxWidget : public QWidget { ProxGuiQT *master; Plot *plot; Ui::Form *opsController; -// QWidget *controlWidget; SliderWidget *controlWidget; + public: ProxWidget(QWidget *parent = 0, ProxGuiQT *master = NULL); ~ProxWidget(void); @@ -125,6 +134,9 @@ class ProxGuiQT : public QObject { private: QApplication *plotapp; ProxWidget *plotwidget; + Ui::PictureForm *pictureController; + PictureWidget *pictureWidget; + int argc; char **argv; //void (*main_func)(void); @@ -136,6 +148,12 @@ class ProxGuiQT : public QObject { void ShowGraphWindow(void); void RepaintGraphWindow(void); void HideGraphWindow(void); + + // hook up picture viewer + void ShowPictureWindow(char *fn); + void HidePictureWindow(void); + void RepaintPictureWindow(void); + void MainLoop(void); void Exit(void); @@ -143,6 +161,12 @@ class ProxGuiQT : public QObject { void _ShowGraphWindow(void); void _RepaintGraphWindow(void); void _HideGraphWindow(void); + + // hook up picture viewer + void _ShowPictureWindow(char *fn); + void _HidePictureWindow(void); + void _RepaintPictureWindow(void); + void _Exit(void); void _StartProxmarkThread(void); @@ -151,6 +175,11 @@ class ProxGuiQT : public QObject { void RepaintGraphWindowSignal(void); void HideGraphWindowSignal(void); void ExitSignal(void); + + // hook up picture viewer signals + void ShowPictureWindowSignal(char *fn); + void HidePictureWindowSignal(void); + void RepaintPictureWindowSignal(void); }; #endif // PROXGUI_QT diff --git a/client/src/scripting.c b/client/src/scripting.c index 37769bffe..0184c78a5 100644 --- a/client/src/scripting.c +++ b/client/src/scripting.c @@ -342,12 +342,12 @@ static int l_GetFromFlashMemSpiffs(lua_State *L) { return returnToLuaWithError(L, "Filename missing or invalid"); // get size from spiffs itself ! - SendCommandMIX(CMD_SPIFFS_STAT, 0, 0, 0, (uint8_t *)destfilename, 32); + SendCommandNG(CMD_SPIFFS_STAT, (uint8_t *)destfilename, 32); PacketResponseNG resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) + if (!WaitForResponseTimeout(CMD_SPIFFS_STAT, &resp, 2000)) return returnToLuaWithError(L, "No response from the device"); - len = resp.oldarg[0]; + len = resp.data.asDwords[0]; if (len == 0) return returnToLuaWithError(L, "Filename invalid or empty"); diff --git a/client/src/ui.h b/client/src/ui.h index 632a818d3..21ad74be7 100644 --- a/client/src/ui.h +++ b/client/src/ui.h @@ -48,6 +48,7 @@ typedef struct { clientdebugLevel_t client_debug_level; barMode_t bar_mode; // uint8_t device_debug_level; + uint16_t client_exe_delay; char *history_path; pm3_device *current_device; } session_arg_t; diff --git a/client/src/ui/image.ui b/client/src/ui/image.ui new file mode 100644 index 000000000..cb28d7700 --- /dev/null +++ b/client/src/ui/image.ui @@ -0,0 +1,33 @@ + + + PictureForm + + + + 0 + 0 + 400 + 400 + + + + Picture Viewer + + + + + + + + + + Image size + + + + + + + + + diff --git a/client/src/util.c b/client/src/util.c index c8cc26752..ed6ca6e7c 100644 --- a/client/src/util.c +++ b/client/src/util.c @@ -25,7 +25,7 @@ #include "ui.h" // PrintAndLog -#define UTIL_BUFFER_SIZE_SPRINT 4097 +#define UTIL_BUFFER_SIZE_SPRINT 8193 // global client debug variable uint8_t g_debugMode = 0; // global client disable logging variable @@ -450,7 +450,7 @@ void num_to_bytebits(uint64_t n, size_t len, uint8_t *dest) { } } -//least significant bit first +// least significant bit (lsb) first void num_to_bytebitsLSBF(uint64_t n, size_t len, uint8_t *dest) { for (size_t i = 0 ; i < len ; ++i) { dest[i] = n & 1; diff --git a/client/src/util.h b/client/src/util.h index 2730ed44f..9357eb1d0 100644 --- a/client/src/util.h +++ b/client/src/util.h @@ -16,6 +16,8 @@ #include #endif +#define ARRAY_LENGTH( array ) ( sizeof( array ) / sizeof( *( array ) ) ) + // used for save/load files #ifndef FILE_PATH_SIZE # define FILE_PATH_SIZE 1000 diff --git a/doc/bt_manual_v10.md b/doc/bt_manual_v10.md index bdc371250..bf9fc0fdb 100644 --- a/doc/bt_manual_v10.md +++ b/doc/bt_manual_v10.md @@ -190,10 +190,11 @@ The first time, your OS will ask you for pairing. The default PIN is 1234. If PIN is not typed in quickly, the client might timeout. Simply restart it again after pairing. -If your OS doesn't prompt you for pairing, you can do it in command line, e.g. (again, replace with your addon MAC address): +If your OS doesn't prompt you for pairing or if the device connects and immediately disconnects, you can pair it in command line, e.g. (again, replace with your addon MAC address): ```sh bluetoothctl +[bluetooth]# remove aa:bb:cc:dd:ee:ff [bluetooth]# pairable on [bluetooth]# scan on Discovery started diff --git a/doc/commands.json b/doc/commands.json index cc359e3c9..b050766e5 100644 --- a/doc/commands.json +++ b/doc/commands.json @@ -973,7 +973,7 @@ }, "help": { "command": "help", - "description": "help use ` help` for details of a command prefs { edit client/device preferences... } -------- ----------------------- technology ----------------------- analyse { analyse utils... } data { plot window / data buffer manipulation... } emv { emv iso-14443 / iso-7816... } hf { high frequency commands... } hw { hardware commands... } lf { low frequency commands... } nfc { nfc commands... } reveng { crc calculations from reveng software... } smart { smart card iso-7816 commands... } script { scripting commands... } trace { trace manipulation... } wiegand { wiegand format manipulation... } -------- ----------------------- general ----------------------- clear clear screen hints turn hints on / off msleep add a pause in milliseconds rem add a text line in log file quit exit exit program [=] session log e:\\proxspace\\pm3/.proxmark3/logs/log_20210615.txt --------------------------------------------------------------------------------------- auto available offline: no run lf search / hf search / data plot / data save", + "description": "help use ` help` for details of a command prefs { edit client/device preferences... } -------- ----------------------- technology ----------------------- analyse { analyse utils... } data { plot window / data buffer manipulation... } emv { emv iso-14443 / iso-7816... } hf { high frequency commands... } hw { hardware commands... } lf { low frequency commands... } nfc { nfc commands... } reveng { crc calculations from reveng software... } smart { smart card iso-7816 commands... } script { scripting commands... } trace { trace manipulation... } wiegand { wiegand format manipulation... } -------- ----------------------- general ----------------------- clear clear screen hints turn hints on / off msleep add a pause in milliseconds rem add a text line in log file quit exit exit program [=] session log e:\\proxspace\\pm3/.proxmark3/logs/log_20210709.txt --------------------------------------------------------------------------------------- auto available offline: no run lf search / hf search / data plot / data save", "notes": [ "auto" ], @@ -1684,67 +1684,83 @@ }, "hf cipurse aread": { "command": "hf cipurse aread", - "description": "read file attributes by file id with key id and key", + "description": "read file attributes by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used", "notes": [ - "hf cipurse aread -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and read file attributes with id 2ff7", - "hf cipurse aread -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and read file attributes" + "hf cipurse aread --fid 2ff7 -> authenticate with keyid 1, read file attributes with id 2ff7", + "hf cipurse aread -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2, read file attributes" ], "offline": false, "options": [ "-h, --help this help", "-a, --apdu show apdu requests and responses", "-v, --verbose show technical data", - "-n, --keyid key id", - "-k, --key key for authenticate", - "-f, --file file id", + "-n key id", + "-k, --key auth key", + "--fid file id", "--noauth read file attributes without authentication", "--sreq communication reader-picc security level", "--sresp communication picc-reader security level", "--sel-adf show info about adf itself", "--sel-mf show info about master file" ], - "usage": "hf cipurse aread [-hav] [-n ] [-k ] [-f ] [--noauth] [--sreq ] [--sresp ] [--sel-adf] [--sel-mf]" + "usage": "hf cipurse aread [-hav] [-n ] [-k ] [--fid ] [--noauth] [--sreq ] [--sresp ] [--sel-adf] [--sel-mf]" }, "hf cipurse auth": { "command": "hf cipurse auth", - "description": "authenticate with key id and key", + "description": "authenticate with key id and key. if no key is supplied, default key of 737373...7373 will be used", "notes": [ - "hf cipurse auth -> authenticate with keyid=1 and key = 7373...7373", - "hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> authenticate with key" + "hf cipurse auth -> authenticate with keyid 1, default key", + "hf cipurse auth -n 2 -k 65656565656565656565656565656565 -> authenticate keyid 2 with key" ], "offline": false, "options": [ "-h, --help this help", "-a, --apdu show apdu requests and responses", "-v, --verbose show technical data", - "-n, --keyid key id", - "-k, --key key for authenticate" + "-n key id", + "-k, --key auth key" ], "usage": "hf cipurse auth [-hav] [-n ] [-k ]" }, + "hf cipurse default": { + "command": "hf cipurse default", + "description": "set default parameters for access to cipurse card", + "notes": [ + "hf cipurse default -n 1 -k 65656565656565656565656565656565 --fid 2ff7 -> set key, key id and file id" + ], + "offline": false, + "options": [ + "-h, --help this help", + "--clear resets to defaults", + "-n key id", + "-k, --key authentication key", + "--fid file id" + ], + "usage": "hf cipurse default [-h] [--clear] [-n ] [-k ] [--fid ]" + }, "hf cipurse delete": { "command": "hf cipurse delete", - "description": "read file by file id with key id and key", + "description": "read file by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used", "notes": [ - "hf cipurse delete -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and delete file with id 2ff7", - "hf cipurse delete -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and delete file" + "hf cipurse delete --fid 2ff7 -> authenticate with keyid 1, delete file with id 2ff7", + "hf cipurse delete -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2 and delete file" ], "offline": false, "options": [ "-h, --help this help", "-a, --apdu show apdu requests and responses", "-v, --verbose show technical data", - "-n, --keyid key id", - "-k, --key key for authenticate", - "-f, --file file id", + "-n key id", + "-k, --key auth key", + "--fid file id", "--sreq communication reader-picc security level", "--sresp communication picc-reader security level" ], - "usage": "hf cipurse delete [-hav] [-n ] [-k ] [-f ] [--sreq ] [--sresp ]" + "usage": "hf cipurse delete [-hav] [-n ] [-k ] [--fid ] [--sreq ] [--sresp ]" }, "hf cipurse help": { "command": "hf cipurse help", - "description": "help this help. --------------------------------------------------------------------------------------- hf cipurse info available offline: no get info from cipurse tags", + "description": "help this help. test tests --------------------------------------------------------------------------------------- hf cipurse info available offline: no get info from cipurse tags", "notes": [ "hf cipurse info" ], @@ -1756,48 +1772,63 @@ }, "hf cipurse read": { "command": "hf cipurse read", - "description": "read file by file id with key id and key", + "description": "read file by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used", "notes": [ - "hf cipurse read -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and read file with id 2ff7", - "hf cipurse read -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and read file" + "hf cipurse read --fid 2ff7 -> authenticate with keyid 1, read file with id 2ff7", + "hf cipurse read -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2 and read file" ], "offline": false, "options": [ "-h, --help this help", "-a, --apdu show apdu requests and responses", "-v, --verbose show technical data", - "-n, --keyid key id", - "-k, --key key for authenticate", - "-f, --file file id", + "-n key id", + "-k, --key auth key", + "--fid file id", "-o, --offset offset for reading data from file", "--noauth read file without authentication", "--sreq communication reader-picc security level", "--sresp communication picc-reader security level" ], - "usage": "hf cipurse read [-hav] [-n ] [-k ] [-f ] [-o ] [--noauth] [--sreq ] [--sresp ]" + "usage": "hf cipurse read [-hav] [-n ] [-k ] [--fid ] [-o ] [--noauth] [--sreq ] [--sresp ]" + }, + "hf cipurse test": { + "command": "hf cipurse test", + "description": "[=] ------ cipurse tests ------ [=] kvv.............. passed [=] iso9797m2........ passed [=] smi.............. passed [=] mic.............. passed [=] auth............. passed [=] channel mac...... passed [=] channel encdec... passed [=] apdu............. passed [=] --------------------------- [+] tests [ ok ] ======================================================================================= hf epa { german identification card... } --------------------------------------------------------------------------------------- hf epa help available offline: yes help this help --------------------------------------------------------------------------------------- hf epa cnonces available offline: no tries to collect nonces when doing part of pace protocol.", + "notes": [ + "hf epa cnonces --size 4 --num 4 --delay 1" + ], + "offline": true, + "options": [ + "-h, --help this help", + "--size nonce size", + "--num number of nonces to collect", + "-d, --delay delay between attempts" + ], + "usage": "hf epa cnonces [-h] --size --num -d " }, "hf cipurse write": { "command": "hf cipurse write", - "description": "write file by file id with key id and key", + "description": "write file by file id with key id and key. if no key is supplied, default key of 737373...7373 will be used", "notes": [ - "hf cipurse write -f 2ff7 -> authenticate with keyid=1 and key = 7373...7373 and write file with id 2ff7", - "hf cipurse write -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> authenticate with specified key and write file" + "hf cipurse write --fid 2ff7 -> authenticate with keyid 1, write file with id 2ff7", + "hf cipurse write -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> authenticate keyid 2 and write file" ], "offline": false, "options": [ "-h, --help this help", "-a, --apdu show apdu requests and responses", "-v, --verbose show technical data", - "-n, --keyid key id", - "-k, --key key for authenticate", - "-f, --file file id", + "-n key id", + "-k, --key auth key", + "--fid file id", "-o, --offset offset for reading data from file", "--noauth read file without authentication", "--sreq communication reader-picc security level", "--sresp communication picc-reader security level", - "-c, --content new file content" + "-d, --data hex data to write to new file" ], - "usage": "hf cipurse write [-hav] [-n ] [-k ] [-f ] [-o ] [--noauth] [--sreq ] [--sresp ] [-c ]" + "usage": "hf cipurse write [-hav] [-n ] [-k ] [--fid ] [-o ] [--noauth] [--sreq ] [--sresp ] [-d ]" }, "hf emrtd help": { "command": "hf emrtd help", @@ -1854,21 +1885,6 @@ ], "usage": "hf emrtd list [-h1fcrux] [--dict ]..." }, - "hf epa help": { - "command": "hf epa help", - "description": "help this help --------------------------------------------------------------------------------------- hf epa cnonces available offline: no tries to collect nonces when doing part of pace protocol.", - "notes": [ - "hf epa cnonces --size 4 --num 4 --delay 1" - ], - "offline": true, - "options": [ - "-h, --help this help", - "--size nonce size", - "--num number of nonces to collect", - "-d, --delay delay between attempts" - ], - "usage": "hf epa cnonces [-h] --size --num -d " - }, "hf epa preplay": { "command": "hf epa preplay", "description": "perform pace protocol by replaying given apdus", @@ -2826,10 +2842,10 @@ "offline": false, "options": [ "-h, --help this help", - "-f, --file specify a filename for dump file", + "-f, --file specify a filename for dump file", "--de deobfuscate dump data (xor with mcc)" ], - "usage": "hf legic dump [-h] [-f ] [--de]" + "usage": "hf legic dump [-h] [-f ] [--de]" }, "hf legic eload": { "command": "hf legic eload", @@ -2860,11 +2876,11 @@ "offline": true, "options": [ "-h, --help this help", - "-f, --file specify a filename to save", + "-f, --file specify a filename to save", "-t, --type tag type", "--deobfuscate de-obfuscate dump data (xor with mcc)" ], - "usage": "hf legic esave [-h] [-f ] [-t ] [--deobfuscate]" + "usage": "hf legic esave [-h] [-f ] [-t ] [--deobfuscate]" }, "hf legic help": { "command": "hf legic help", @@ -2996,9 +3012,9 @@ "offline": true, "options": [ "-h, --help this help", - "-f, --file specify a filename for dumpfile" + "-f, --file specify a filename for dumpfile" ], - "usage": "hf lto dump [-h] [-f ]" + "usage": "hf lto dump [-h] [-f ]" }, "hf lto info": { "command": "hf lto info", @@ -3175,9 +3191,9 @@ "--4k mifare classic 4k / s70", "--emu fill simulator keys from found keys", "--dump dump found keys to binary file", - "-f, --file filename of dictionary" + "-f, --file filename of dictionary" ], - "usage": "hf mf chk [-hab*] [-k ]... [--blk ] [--mini] [--1k] [--2k] [--4k] [--emu] [--dump] [-f ]" + "usage": "hf mf chk [-hab*] [-k ]... [--blk ] [--mini] [--1k] [--2k] [--4k] [--emu] [--dump] [-f ]" }, "hf mf cload": { "command": "hf mf cload", @@ -3491,9 +3507,9 @@ "hf mf fchk --2k -k ffffffffffff -> key recovery against mifare 2k", "hf mf fchk --4k -k ffffffffffff -> key recovery against mifare 4k", "hf mf fchk --1k -f mfc_default_keys.dic -> target 1k using default dictionary file", - "hf mf fchk --1k --emu -> target 1k, write to emulator memory", - "hf mf fchk --1k --dump -> target 1k, write to file", - "hf mf fchk --1k --mem -> target 1k, use dictionary from flashmemory" + "hf mf fchk --1k --emu -> target 1k, write keys to emulator memory", + "hf mf fchk --1k --dump -> target 1k, write keys to file", + "hf mf fchk --1k --mem -> target 1k, use dictionary from flash memory" ], "offline": false, "options": [ @@ -3553,6 +3569,23 @@ ], "usage": "hf mf gen3uid [-h] [-u ]" }, + "hf mf gview": { + "command": "hf mf gview", + "description": "view `magic gen3 gtu` card memory", + "notes": [ + "hf mf gview", + "hf mf gview --4k" + ], + "offline": false, + "options": [ + "-h, --help this help", + "--mini mifare classic mini / s20", + "--1k mifare classic 1k / s50 (def)", + "--2k mifare classic/plus 2k", + "--4k mifare classic 4k / s70" + ], + "usage": "hf mf gview [-h] [--mini] [--1k] [--2k] [--4k]" + }, "hf mf hardnested": { "command": "hf mf hardnested", "description": "nested attack for hardened mifare classic cards. `--i` set type of simd instructions. without this flag programs autodetect it. or hf mf hardnested -r --tk [known target key] add the known target key to check if it is present in the remaining key space hf mf hardnested --blk 0 -a -k a0a1a2a3a4a5 --tblk 4 --ta --tk ffffffffffff", @@ -3905,6 +3938,31 @@ ], "usage": "hf mf wrbl [-hab] --blk [-k ] [-d ]" }, + "hf mfdes auth": { + "command": "hf mfdes auth", + "description": "authenticates mifare desfire using key", + "notes": [ + "hf mfdes auth -m 3 -t 4 -a 808301 -n 0 -k 00000000000000000000000000000000 -> aes,keynumber 0, aid 0x803201", + "hf mfdes auth -m 2 -t 2 -a 000000 -n 1 -k 00000000000000000000000000000000 -> 3des,keynumber 1, aid 0x000000", + "hf mfdes auth -m 1 -t 1 -a 000000 -n 2 -k 0000000000000000 -> des,keynumber 2, aid 0x000000", + "hf mfdes auth -m 1 -t 1 -a 000000 -n 0 -> des, defaultkey, aid 0x000000", + "hf mfdes auth -m 2 -t 2 -a 000000 -n 0 -> 3des, defaultkey, aid 0x000000", + "hf mfdes auth -m 3 -t 4 -a 000000 -n 0 -> 3k3des, defaultkey, aid 0x000000", + "hf mfdes auth -m 3 -t 4 -a 000000 -n 0 -> aes, defaultkey, aid 0x000000" + ], + "offline": false, + "options": [ + "-h, --help this help", + "-m, --type auth type (1=normal, 2=iso, 3=aes)", + "-t, --algo crypt algo (1=des, 2=3des(2k2des), 3=3k3des, 4=aes)", + "-a, --aid aid used for authentification (hex 3 bytes)", + "-n, --keyno key number used for authentification", + "-k, --key key for checking (hex 8-24 bytes)", + "-d, --kdf key derivation function (kdf) (0=none, 1=an10922, 2=gallagher)", + "-i, --kdfi kdf input (hex 1-31 bytes)" + ], + "usage": "hf mfdes auth [-h] [-m ] [-t ] [-a ]... [-n ] [-k ] [-d ] [-i ]" + }, "hf mfdes bruteaid": { "command": "hf mfdes bruteaid", "description": "recover aids by bruteforce. warning: this command takes a long time", @@ -4135,6 +4193,50 @@ ], "usage": "hf mfdes formatpicc [-h]" }, + "hf mfdes getaids": { + "command": "hf mfdes getaids", + "description": "get application ids list from card. master key needs to be provided.", + "notes": [ + "hf mfdes getaids -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup" + ], + "offline": false, + "options": [ + "-h, --help this help", + "-a, --apdu show apdu requests and responses", + "-v, --verbose show technical data", + "-n, --keyno key number", + "-t, --algo crypt algo: des, 2tdea, 3tdea, aes", + "-k, --key key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)", + "-f, --kdf key derivation function (kdf): none, an10922, gallagher", + "-i, --kdfi kdf input (hex 1-31 bytes)", + "-m, --cmode communicaton mode: plain/mac/encrypt", + "-c, --ccset communicaton command set: native/niso/iso", + "-s, --schann secure channel: d40/ev1/ev2" + ], + "usage": "hf mfdes getaids [-hav] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ]" + }, + "hf mfdes getappnames": { + "command": "hf mfdes getappnames", + "description": "get application ids, iso ids and df names from card. master key needs to be provided.", + "notes": [ + "hf mfdes getappnames -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup" + ], + "offline": false, + "options": [ + "-h, --help this help", + "-a, --apdu show apdu requests and responses", + "-v, --verbose show technical data", + "-n, --keyno key number", + "-t, --algo crypt algo: des, 2tdea, 3tdea, aes", + "-k, --key key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)", + "-f, --kdf key derivation function (kdf): none, an10922, gallagher", + "-i, --kdfi kdf input (hex 1-31 bytes)", + "-m, --cmode communicaton mode: plain/mac/encrypt", + "-c, --ccset communicaton command set: native/niso/iso", + "-s, --schann secure channel: d40/ev1/ev2" + ], + "usage": "hf mfdes getappnames [-hav] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ]" + }, "hf mfdes getuid": { "command": "hf mfdes getuid", "description": "get uid from a mifare desfire tag", @@ -4163,28 +4265,23 @@ }, "hf mfdes help": { "command": "hf mfdes help", - "description": "help this help list list desfire (iso 14443a) history --------------------------------------------------------------------------------------- hf mfdes auth available offline: no authenticates mifare desfire using key", + "description": "help this help list list desfire (iso 14443a) history --------------------------------------------------------------------------------------- hf mfdes default available offline: no get application ids, iso ids and df names from card. master key needs to be provided.", "notes": [ - "hf mfdes auth -m 3 -t 4 -a 808301 -n 0 -k 00000000000000000000000000000000 -> aes,keynumber 0, aid 0x803201", - "hf mfdes auth -m 2 -t 2 -a 000000 -n 1 -k 00000000000000000000000000000000 -> 3des,keynumber 1, aid 0x000000", - "hf mfdes auth -m 1 -t 1 -a 000000 -n 2 -k 0000000000000000 -> des,keynumber 2, aid 0x000000", - "hf mfdes auth -m 1 -t 1 -a 000000 -n 0 -> des, defaultkey, aid 0x000000", - "hf mfdes auth -m 2 -t 2 -a 000000 -n 0 -> 3des, defaultkey, aid 0x000000", - "hf mfdes auth -m 3 -t 4 -a 000000 -n 0 -> 3k3des, defaultkey, aid 0x000000", - "hf mfdes auth -m 3 -t 4 -a 000000 -n 0 -> aes, defaultkey, aid 0x000000" + "hf mfdes getappnames -n 0 -t des -k 0000000000000000 -f none -> execute with default factory setup" ], "offline": true, "options": [ "-h, --help this help", - "-m, --type auth type (1=normal, 2=iso, 3=aes)", - "-t, --algo crypt algo (1=des, 2=3des(2k2des), 3=3k3des, 4=aes)", - "-a, --aid aid used for authentification (hex 3 bytes)", - "-n, --keyno key number used for authentification", - "-k, --key key for checking (hex 8-24 bytes)", - "-d, --kdf key derivation function (kdf) (0=none, 1=an10922, 2=gallagher)", - "-i, --kdfi kdf input (hex 1-31 bytes)" + "-n, --keyno key number", + "-t, --algo crypt algo: des, 2tdea, 3tdea, aes", + "-k, --key key for authenticate (hex 8(des), 16(2tdea or aes) or 24(3tdea) bytes)", + "-f, --kdf key derivation function (kdf): none, an10922, gallagher", + "-i, --kdfi kdf input (hex 1-31 bytes)", + "-m, --cmode communicaton mode: plain/mac/encrypt", + "-c, --ccset communicaton command set: native/niso/iso", + "-s, --schann secure channel: d40/ev1/ev2" ], - "usage": "hf mfdes auth [-h] [-m ] [-t ] [-a ]... [-n ] [-k ] [-d ] [-i ]" + "usage": "hf mfdes default [-h] [-n ] [-t ] [-k ] [-f ] [-i ] [-m ] [-c ] [-s ]" }, "hf mfdes info": { "command": "hf mfdes info", @@ -4219,12 +4316,12 @@ ], "usage": "hf mfdes list [-h1fcrux] [--dict ]..." }, - "hf mfdes readdata": { - "command": "hf mfdes readdata", + "hf mfdes read": { + "command": "hf mfdes read", "description": "read data from file make sure to select aid or authenticate aid before running this command.", "notes": [ - "hf mfdes readdata -n 01 -t 0 -o 000000 -l 000000 -a 123456", - "hf mfdes readdata -n 01 -t 0 -> read all data from standard file, fileno 01" + "hf mfdes read -n 1 -t 0 -o 000000 -l 000000 -a 123456", + "hf mfdes read -n 1 -t 0 -> read all data from standard file, fileno 1" ], "offline": false, "options": [ @@ -4235,7 +4332,7 @@ "-t, --type file type (0 = standard / backup, 1 = record)", "-a, --aid app id to select (3 hex bytes, big endian)" ], - "usage": "hf mfdes readdata [-h] [-n ] [-o ]... [-l ]... [-t ] [-a ]..." + "usage": "hf mfdes read [-h] [-n ] [-o ]... [-l ]... [-t ] [-a ]..." }, "hf mfdes selectaid": { "command": "hf mfdes selectaid", @@ -4250,11 +4347,23 @@ ], "usage": "hf mfdes selectaid [-h] [-a ]..." }, - "hf mfdes writedata": { - "command": "hf mfdes writedata", + "hf mfdes test": { + "command": "hf mfdes test", + "description": "[=] key num: 0 key algo: des key[8]: 00 00 00 00 00 00 00 00 [=] secure channel: n/a command set: niso communication mode: encrypt [+] setting isodep -> inactive [=] sending bytes to proxmark failed - offline [+] >>>> 90 5a 00 00 03 00 00 00 00 [+] setting isodep -> inactive [=] sending bytes to proxmark failed - offline [=] sending bytes to proxmark failed - offline [!] command execute timeout [+] setting isodep -> inactive [=] sending bytes to proxmark failed - offline ======================================================================================= hf seos { seos rfids... } --------------------------------------------------------------------------------------- hf seos help available offline: yes help this help list list seos history --------------------------------------------------------------------------------------- hf seos info available offline: no get info from seos tags", + "notes": [ + "hf seos info" + ], + "offline": false, + "options": [ + "-h, --help this help" + ], + "usage": "hf seos info [-h]" + }, + "hf mfdes write": { + "command": "hf mfdes write", "description": "write data to file make sure to select aid or authenticate aid before running this command.", "notes": [ - "hf mfdes writedata -n 01 -t 0 -o 000000 -d 3132333435363738" + "hf mfdes write -n 01 -t 0 -o 000000 -d 3132333435363738" ], "offline": false, "options": [ @@ -4265,7 +4374,7 @@ "-t, --type file type (0 = standard / backup, 1 = record)", "-a, --aid app id to select as hex bytes (3 bytes, big endian)" ], - "usage": "hf mfdes writedata [-h] [-n ] [-o ]... [-d ]... [-t ] [-a ]..." + "usage": "hf mfdes write [-h] [-n ] [-o ]... [-d ]... [-t ] [-a ]..." }, "hf mfp auth": { "command": "hf mfp auth", @@ -4726,21 +4835,10 @@ ], "offline": true, "options": [ - "-h, --help this help" + "-h, --help this help", + "-v, --verbose verbose output" ], - "usage": "hf search [-h]" - }, - "hf seos help": { - "command": "hf seos help", - "description": "help this help list list seos history --------------------------------------------------------------------------------------- hf seos info available offline: no get info from seos tags", - "notes": [ - "hf seos info" - ], - "offline": true, - "options": [ - "-h, --help this help" - ], - "usage": "hf seos info [-h]" + "usage": "hf search [-hv]" }, "hf seos list": { "command": "hf seos list", @@ -5208,7 +5306,7 @@ "command": "hw setmux", "description": "set the adc mux to a specific value", "notes": [ - "hw setmux --hiraw -> set high raw" + "hw setmux --hipkd -> set high peak" ], "offline": false, "options": [ @@ -5602,16 +5700,16 @@ "description": "this command uses a dictionary attack against em4205/4305/4469/4569", "notes": [ "lf em 4x05 chk", - "lf em 4x05 chk -e 000022b8 -> remember to use 0x for hex", + "lf em 4x05 chk -e 000022b8 -> check password 000022b8", "lf em 4x05 chk -f t55xx_default_pwds -> use t55xx default dictionary" ], "offline": false, "options": [ "-h, --help this help", - "-f, --file <*.dic> loads a default keys dictionary file <*.dic>", + "-f, --file loads a default keys dictionary file <*.dic>", "-e, --em try the calculated password from some cloners based on em4100 id" ], - "usage": "lf em 4x05 chk [-h] [-f <*.dic>]... [-e ]" + "usage": "lf em 4x05 chk [-h] [-f ] [-e ]" }, "lf em 4x05 demod": { "command": "lf em 4x05 demod", @@ -5643,19 +5741,19 @@ }, "lf em 4x05 help": { "command": "lf em 4x05 help", - "description": "help this help demod demodulate a em4x05/em4x69 tag from the graphbuffer sniff attempt to recover em4x05 commands from sample buffer --------------------------------------------------------------------------------------- lf em 4x05 brute available offline: no this command tries to bruteforce the password of a em4205/4305/4469/4569", + "description": "help this help demod demodulate a em4x05/em4x69 tag from the graphbuffer sniff attempt to recover em4x05 commands from sample buffer --------------------------------------------------------------------------------------- lf em 4x05 brute available offline: no this command tries to bruteforce the password of a em4205/4305/4469/4569 the loop is running on device side, press proxmark3 button to abort", "notes": [ "note: if you get many false positives, change position on the antennalf em 4x05 brute", "lf em 4x05 brute -n 1 -> stop after first candidate found", - "lf em 4x05 brute -s 000022b8 -> remember to use 0x for hex" + "lf em 4x05 brute -s 000022aa -> start at 000022aa" ], "offline": true, "options": [ "-h, --help this help", - "-s, --start start bruteforce enumeration from this password value", - "-n stop after having found n candidates. default: 0 => infinite" + "-s, --start start bruteforce enumeration from this password value", + "-n stop after having found n candidates. default: 0 (infinite)" ], - "usage": "lf em 4x05 brute [-h] [-s ] [-n ]" + "usage": "lf em 4x05 brute [-h] [-s ] [-n ]" }, "lf em 4x05 info": { "command": "lf em 4x05 info", @@ -5760,7 +5858,7 @@ }, "lf em 4x50 chk": { "command": "lf em 4x50 chk", - "description": "dictionary attack against em4x50.", + "description": "run dictionary key recovery against em4x50 card.", "notes": [ "lf em 4x50 chk -> uses t55xx default dictionary", "lf em 4x50 chk -f my.dic" @@ -5768,7 +5866,7 @@ "offline": false, "options": [ "-h, --help this help", - "-f, --filename dictionary filename" + "-f, --file dictionary filename" ], "usage": "lf em 4x50 chk [-h] [-f ]" }, @@ -5784,7 +5882,7 @@ "offline": false, "options": [ "-h, --help this help", - "-f, --filename dump filename (bin/eml/json)", + "-f, --file dump filename (bin/eml/json)", "-p, --pwd password, 4 hex bytes, lsb" ], "usage": "lf em 4x50 dump [-h] [-f ] [-p ]" @@ -5812,7 +5910,7 @@ "offline": false, "options": [ "-h, --help this help", - "-f, --filename data filename" + "-f, --file save filename" ], "usage": "lf em 4x50 esave [-h] [-f ]" }, @@ -5830,7 +5928,7 @@ }, "lf em 4x50 help": { "command": "lf em 4x50 help", - "description": "help this help --------------------------------------------------------------------------------------- lf em 4x50 brute available offline: no tries to bruteforce the password of a em4x50. function can be stopped by pressing pm3 button.", + "description": "help this help ----------- --------------------- operations --------------------- ----------- --------------------- simulation --------------------- --------------------------------------------------------------------------------------- lf em 4x50 brute available offline: no tries to bruteforce the password of a em4x50 card. function can be stopped by pressing pm3 button.", "notes": [ "lf em 4x50 brute --first 12330000 --last 12340000 -> tries pwds from 0x12330000 to 0x1234000000" ], @@ -5913,7 +6011,7 @@ "options": [ "-h, --help this help", "-u, --uid uid, 4 hex bytes, msb", - "-f, --filename dump filename (bin/eml/json)", + "-f, --file dump filename (bin/eml/json)", "-p, --pwd password, 4 hex bytes, lsb" ], "usage": "lf em 4x50 restore [-h] [-u ] [-f ] [-p ]" @@ -5961,11 +6059,11 @@ ], "usage": "lf em 4x50 wrbl [-h] -b -d [-p ]" }, - "lf em 4x50 writepwd": { - "command": "lf em 4x50 writepwd", + "lf em 4x50 wrpwd": { + "command": "lf em 4x50 wrpwd", "description": "writes em4x50 password.", "notes": [ - "lf em 4x50 writepwd -p 4f22e7ff -n 12345678" + "lf em 4x50 wrpwd -p 4f22e7ff -n 12345678" ], "offline": false, "options": [ @@ -5973,7 +6071,7 @@ "-p, --pwd password, 4 hex bytes, lsb", "-n, --new new password, 4 hex bytes, lsb" ], - "usage": "lf em 4x50 writepwd [-h] -p -n " + "usage": "lf em 4x50 wrpwd [-h] -p -n " }, "lf em 4x70 auth": { "command": "lf em 4x70 auth", @@ -6416,9 +6514,9 @@ "offline": false, "options": [ "-h, --help this help", - "-f, --filename filename to load from" + "-f, --file filename to load ( w/o ext )" ], - "usage": "lf hitag cc [-h] [-f ]" + "usage": "lf hitag cc [-h] [-f ]" }, "lf hitag dump": { "command": "lf hitag dump", @@ -6440,17 +6538,18 @@ "command": "lf hitag help", "description": "help this help list list hitag trace history --------------------------------------------------------------------------------------- lf hitag eload available offline: no loads hitag tag dump into emulator memory on device", "notes": [ - "lf hitag eload -f lf-hitag-11223344-dump.bin" + "lf hitag eload -2 -f lf-hitag-11223344-dump.bin" ], "offline": true, "options": [ "-h, --help this help", "-f, --file filename of dump", - "-1 simulate hitag1", - "-2 simulate hitag2", - "-s simulate hitags" + "-1 card type hitag1", + "-2 card type hitag2", + "-s card type hitags", + "-m card type hitagm" ], - "usage": "lf hitag eload [-h12s] -f " + "usage": "lf hitag eload [-h12sm] -f " }, "lf hitag info": { "command": "lf hitag info", @@ -7015,9 +7114,11 @@ "--qc quadrakey credential", "--hc honeywell credential", "--q5 optional - specify writing to q5/t5555 tag", - "--em optional - specify writing to em4305/4469 tag" + "--em optional - specify writing to em4305/4469 tag", + "--magic optional - magic hex data. 1 byte", + "--psk2 optional - specify writing a tag in psk2 modulation" ], - "usage": "lf nexwatch clone [-h] [-r ] [--cn ] [-m ] [--nc] [--qc] [--hc] [--q5] [--em]" + "usage": "lf nexwatch clone [-h] [-r ] [--cn ] [-m ] [--nc] [--qc] [--hc] [--q5] [--em] [--magic ] [--psk2]" }, "lf nexwatch help": { "command": "lf nexwatch help", @@ -7061,9 +7162,11 @@ "-m, --mode mode (decimal) (0-15, defaults to 1)", "--nc nexkey credential", "--qc quadrakey credential", - "--hc honeywell credential" + "--hc honeywell credential", + "--magic optional - magic hex data. 1 byte", + "--psk2 optional - specify writing a tag in psk2 modulation" ], - "usage": "lf nexwatch sim [-h] [-r ] [--cn ] [-m ] [--nc] [--qc] [--hc]" + "usage": "lf nexwatch sim [-h] [-r ] [--cn ] [-m ] [--nc] [--qc] [--hc] [--magic ] [--psk2]" }, "lf noralsy clone": { "command": "lf noralsy clone", @@ -7585,7 +7688,7 @@ }, "lf sniff": { "command": "lf sniff", - "description": "sniff low frequency signal. - use `lf config` to set parameters. - use `data plot` to look at it", + "description": "sniff low frequency signal. you need to configure the lf part on the proxmark3 device manually. usually a trigger and skip samples is a good thing to set before doing a low frequency sniff. - use `lf config` to set parameters. - use `data plot` to look at sniff signal. - use `lf search -1` to see if signal can be automatic decoded", "notes": [ "lf sniff -v", "lf sniff -s 3000 -@ -> oscilloscope style" @@ -7748,7 +7851,7 @@ "offline": false, "options": [ "-h, --help this help", - "-f, --filename filename (default is generated on blk 0)", + "-f, --file filename (default is generated on blk 0)", "-o, --override override, force pwd read despite danger to card", "-p, --pwd password (4 hex bytes)", "--r0 downlink - fixed bit length", @@ -9178,8 +9281,8 @@ } }, "metadata": { - "commands_extracted": 570, + "commands_extracted": 575, "extracted_by": "PM3Help2JSON v1.00", - "extracted_on": "2021-06-15T12:04:40" + "extracted_on": "2021-07-09T11:42:14" } } \ No newline at end of file diff --git a/doc/commands.md b/doc/commands.md index 32f98bdef..2bd429d15 100644 --- a/doc/commands.md +++ b/doc/commands.md @@ -241,12 +241,14 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf cipurse help `|Y |`This help.` -|`hf cipurse info `|N |`Info about Cipurse tag.` -|`hf cipurse auth `|N |`Authentication.` -|`hf cipurse read `|N |`Read binary file.` -|`hf cipurse write `|N |`Write binary file.` -|`hf cipurse aread `|N |`Read file attributes.` -|`hf cipurse delete `|N |`Delete file.` +|`hf cipurse info `|N |`Get info about CIPURSE tag` +|`hf cipurse auth `|N |`Authenticate CIPURSE tag` +|`hf cipurse read `|N |`Read binary file` +|`hf cipurse write `|N |`Write binary file` +|`hf cipurse aread `|N |`Read file attributes` +|`hf cipurse delete `|N |`Delete file` +|`hf cipurse default `|N |`Set default key and file id for all the other commands` +|`hf cipurse test `|Y |`Tests` ### hf epa @@ -445,6 +447,7 @@ Check column "offline" for their availability. |`hf mf gen3uid `|N |`Set UID without changing manufacturer block` |`hf mf gen3blk `|N |`Overwrite manufacturer block` |`hf mf gen3freeze `|N |`Perma lock UID changes. irreversible` +|`hf mf gview `|N |`View card` ### hf mfp @@ -498,6 +501,7 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`hf mfdes help `|Y |`This help` +|`hf mfdes default `|N |`[new]Set defaults for all the commands` |`hf mfdes auth `|N |`Tries a MIFARE DesFire Authentication` |`hf mfdes changekey `|N |`Change Key` |`hf mfdes chk `|N |`Check keys` @@ -510,6 +514,8 @@ Check column "offline" for their availability. |`hf mfdes createaid `|N |`Create Application ID` |`hf mfdes deleteaid `|N |`Delete Application ID` |`hf mfdes selectaid `|N |`Select Application ID` +|`hf mfdes getaids `|N |`[new]Get Application IDs list` +|`hf mfdes getappnames `|N |`[new]Get Applications list` |`hf mfdes changevalue `|N |`Write value of a value file (credit/debit/clear)` |`hf mfdes clearfile `|N |`Clear record File` |`hf mfdes createfile `|N |`Create Standard/Backup File` @@ -518,8 +524,9 @@ Check column "offline" for their availability. |`hf mfdes deletefile `|N |`Create Delete File` |`hf mfdes dump `|N |`Dump all files` |`hf mfdes getvalue `|N |`Get value of file` -|`hf mfdes readdata `|N |`Read data from standard/backup/record file` -|`hf mfdes writedata `|N |`Write data to standard/backup/record file` +|`hf mfdes read `|N |`Read data from standard/backup/record file` +|`hf mfdes write `|N |`Write data to standard/backup/record file` +|`hf mfdes test `|N |`Test crypto` ### hf seos @@ -722,21 +729,21 @@ Check column "offline" for their availability. |command |offline |description |------- |------- |----------- |`lf em 4x50 help `|Y |`This help` -|`lf em 4x50 brute `|N |`guess password of EM4x50` -|`lf em 4x50 chk `|N |`check passwords from dictionary` -|`lf em 4x50 dump `|N |`dump EM4x50 tag` -|`lf em 4x50 info `|N |`tag information EM4x50` -|`lf em 4x50 login `|N |`login into EM4x50` -|`lf em 4x50 rdbl `|N |`read word data from EM4x50` -|`lf em 4x50 wrbl `|N |`write word data to EM4x50` -|`lf em 4x50 writepwd `|N |`change password of EM4x50` -|`lf em 4x50 wipe `|N |`wipe EM4x50 tag` -|`lf em 4x50 reader `|N |`show standard read mode data of EM4x50` -|`lf em 4x50 restore `|N |`restore EM4x50 dump to tag` -|`lf em 4x50 sim `|N |`simulate EM4x50 tag` -|`lf em 4x50 eload `|N |`upload dump of EM4x50 to emulator memory` -|`lf em 4x50 esave `|N |`save emulator memory to file` -|`lf em 4x50 eview `|N |`view EM4x50 content in emulator memory` +|`lf em 4x50 brute `|N |`Simple bruteforce attack to find password` +|`lf em 4x50 chk `|N |`Check passwords from dictionary` +|`lf em 4x50 dump `|N |`Dump EM4x50 tag` +|`lf em 4x50 info `|N |`Tag information` +|`lf em 4x50 login `|N |`Login into EM4x50 tag` +|`lf em 4x50 rdbl `|N |`Read EM4x50 word data` +|`lf em 4x50 reader `|N |`Show standard read mode data` +|`lf em 4x50 restore `|N |`Restore EM4x50 dump to tag` +|`lf em 4x50 wrbl `|N |`Write EM4x50 word data` +|`lf em 4x50 wrpwd `|N |`Change EM4x50 password` +|`lf em 4x50 wipe `|N |`Wipe EM4x50 tag` +|`lf em 4x50 eload `|N |`Upload EM4x50 dump to emulator memory` +|`lf em 4x50 esave `|N |`Save emulator memory to file` +|`lf em 4x50 eview `|N |`View EM4x50 content in emulator memory` +|`lf em 4x50 sim `|N |`Simulate EM4x50 tag` ### lf em 4x70 diff --git a/doc/magic_cards_notes.md b/doc/magic_cards_notes.md index a5056d6fa..0fbb3b027 100644 --- a/doc/magic_cards_notes.md +++ b/doc/magic_cards_notes.md @@ -14,7 +14,8 @@ Useful docs: * [MIFARE Classic DirectWrite, FUID version aka 1-write](#mifare-classic-directwrite-fuid-version-aka-1-write) * [MIFARE Classic DirectWrite, UFUID version](#mifare-classic-directwrite-ufuid-version) * [MIFARE Classic, other versions](#mifare-classic-other-versions) - * [MIFARE Classic APDU aka Gen3](#mifare-classic-apdu-aka-gen3) + * [MIFARE Classic Gen3 aka APDU](#mifare-classic-gen3-aka-apdu) + * [MIFARE Classic Gen3 aka GTU](#mifare-classic-gen3-aka-gtu) * [MIFARE Classic Super](#mifare-classic-super) - [MIFARE Ultralight](#mifare-ultralight) * [MIFARE Ultralight blocks 0..2](#mifare-ultralight-blocks-02) @@ -400,7 +401,7 @@ hf 14a raw -c 85000000000000000000000000000008 * ZXUID, EUID, ICUID ? * Some cards exhibit a specific SAK=28 ?? -## MIFARE Classic APDU aka Gen3 +## MIFARE Classic Gen3 aka APDU ### Identify @@ -467,6 +468,154 @@ hf 14a raw -s -c -t 2000 90F0CCCC10 041219c3219316984200e32000000000 # lock (uid/block0?) forever: hf 14a raw -s -c 90FD111100 ``` +--------- +## MIFARE Classic Gen3 aka GTU + +### Identify + +Tag doesn't get identified by latest Proxmark3 client correct but instead mislabled as Gen2/CUID + +``` +hf 14a info +... +[+] Magic capabilities : Gen 2 / CUID +``` + + +### Magic commands + +Android compatible - unknown status + +* issue special APDUs + +``` +cla ins p1 p2 len + CF 00 00 00 00 32 + CF 00 00 00 00 35 + CF 00 00 00 00 34 + CF 00 00 00 00 68 + CF 00 00 00 00 69 <00> + CF 00 00 00 00 CE // Backdoor read card + CF 00 00 00 00 CD // Backdoor write card + CF 00 00 00 00 FE aabbccdd // set password to AABBCCDD + CF 00 00 00 00 F1 // fuse is set ??? + CF 00 00 00 00 F0 // fuse ??? +``` +Note: It doesn't seem to follow a APDU structure per default, + + +### Characteristics + +* UID: 4b, 7b and 10b versions +* ATQA/SAK: changeable +* BCC: +* ATS: changable +* Card Type: changeable +* Shadow mode: GTU +* Backdoor password mode: + +#### Possible card types +Known Preset Change Available: +* MIFARE Mini +* MIFARE 1k S50 4 byte UID +* MIFARE 1k S50 7 byte UID +* MIFARE 1k S50 10 byte UID +* MIFARE 4k S70 4 byte UID +* MIFARE 4k S70 7 byte UID +* MIFARE 4k S70 10 byte UID +* Ultralight +* Ultralight-C +* Ultralight Ev1 +* NTAG + + + +### Proxmark3 commands + +``` +# view contents of tag memory: +hf mf gview +``` + +Equivalent: + + +change ATQA / SAK +================= +``` +hf 14a raw -s -c -t 1000 cf0000000035 +hf 14a raw -s -c -t 1000 cf0000000035440028 // ATQA 00 44 SAK 28 +``` + +change ATS +========== +It should be noted that when SAK=20,28, ATS must be turned on, otherwise the card will not be recognized! + +``` +hf 14a raw -s -c -t 1000 cf0000000034 +hf 14a raw -s -c -t 1000 cf000000003406067577810280 // ATS to 0606757781028002F0 +``` + * length 0, ATS is not sent + * when SAK is 20 or 28, ATS must be set, otherwise the card cannot be read + * the last two digits of ATS are CRC and cannot be counted as length + +set UID length (4, 7, 10) +========================= +``` +hf 14a raw -s -c -t 1000 cf0000000068 +hf 14a raw -s -c -t 1000 cf000000006801 // set UID length to 7 bytes +``` + * param=00: 4 bytes 01: 7 bytes 02: 10 bytes + +Set 14443A/B-UID +================ +--> missing instructions. + + + +Set Ultralight mode +=================== +``` +hf 14a raw -s -c -t 1000 cf0000000069<00> +``` + * 01: UL agreement opened + * 00: UL agreement closed + * in this mode, if SAK=00 ATQA=0044 (Pm3 sequence), it can become an Ultralight card + + +set shadow mode (GTU) +===================== + +This mode is divided into four states: off (pre-write), on (on restore), don’t care, and high-speed read and write. +If you use it, please enter the pre-write mode first. At this time, write the full card data. +After writing, set it to on. At this time, after writing the data, the first time you read the data just written, the next time you read It is the pre-written data. All modes support this operation. It should be noted that using any block to read and write in this mode may give wrong results. + +``` +hf 14a raw -s -c -t 1000 cf0000000032 +``` +Param | Description + 00 | Closed, shadow data can be written at this time + 01 | Open, start restore + 02 | Turn it off completely, as a normal card + 03 | High-speed read and write mode + +Any block read and write +======================== +Using the backdoor command can read and write any area without password, similar to UID card, it should be noted that this command must be used to modify UID. + +``` +hf 14a raw -s -c -t 1000 cf00000000CE // Backdoor read card +hf 14a raw -s -c -t 1000 cf00000000CD // Backdoor write card +``` + +set backdoor password +===================== +All backdoor operations are protected by passwords. If password is forgotten, the card will be scrapped +default password is 00000000 +``` +hf 14a raw -s -c -t 1000 cf00000000feaabbccdd // set password to AABBCCDD +``` + ## MIFARE Classic Super diff --git a/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md b/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md index fc3b51c47..bdd13df49 100644 --- a/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/Mac-OS-X-Homebrew-Installation-Instructions.md @@ -30,11 +30,12 @@ For further questions about Mac & Homebrew, contact [\@Chrisfu on Twitter](https 2. Tap this repo: `brew tap RfidResearchGroup/proxmark3` 3. Install Proxmark3: - - (Optional) `export HOMEBREW_PROXMARK3_PLATFORM=xxxxxx` to specify [platform](https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md#platform), default value is `PM3RDV4` if none - `brew install proxmark3` for stable release - `brew install --HEAD proxmark3` for latest non-stable from GitHub (use this if previous command fails) - `brew install --with-blueshark proxmark3` for blueshark support, stable release - `brew install --HEAD --with-blueshark proxmark3` for blueshark support, latest non-stable from GitHub (use this if previous command fails) + - `brew install --with-generic proxmark3`: for generic (non-RDV4) devices ([platform](https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md#platform)), stable release + - `brew install --HEAD --with-generic proxmark3`: for generic (non-RDV4) devices ([platform](https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md#platform)), latest non-stable from GitHub (use this if previous command fails) For more info, go to https://github.com/RfidResearchGroup/homebrew-proxmark3 diff --git a/doc/md/Installation_Instructions/Mac-OS-X-MacPorts-Installation-Instructions.md b/doc/md/Installation_Instructions/Mac-OS-X-MacPorts-Installation-Instructions.md new file mode 100644 index 000000000..e402db158 --- /dev/null +++ b/doc/md/Installation_Instructions/Mac-OS-X-MacPorts-Installation-Instructions.md @@ -0,0 +1,72 @@ +# MacPorts (Mac OS X), developer installation, ***experimental*** + +

These insturctions are not tested on Apple Silicon!

+ +## Prerequisites + +These instructions will show how to setup the environment on OSX to the point where you'll be able to clone and compile the repo by yourself, as on Linux, Windows, etc. + +1. Have MacPorts installed. Visit https://www.macports.org/ for more information. + + * MacPorts may require a bit more setup. You first need to set up your PATH variable: + + ```bash + export "/opt/local/bin:/opt/local/sbin:/usr/local/bin:/usr/local/sbin:$PATH" + ``` + + Although it is optional for proxmark3 repository, you can also set include variables: + + ```bash + export C_INCLUDE_PATH="/opt/local/include" + export CPLUS_INCLUDE_PATH="/opt/local/include" + export LIBRARY_PATH="/opt/local/lib" + export LDFLAGS="-L/opt/local/lib" + export CFLAGS="-I/opt/local/include" + export CPPFLAGS="-isystem/opt/local/include -I/opt/local/include" + ``` + +2. Install dependencies: + + ``` + sudo port install readline qt5 qt5-qtbase pkgconfig arm-none-eabi-gcc arm-none-eabi-binutils lua52 + ``` + +3. Clamp Python version for pkg-config + + MacPorts doesn't handle Python version defaults when it comes to pkg-config. So even if you have done: + + ``` + sudo port install python39 cython39 + + sudo port select --set python python39 # this also makes calls to "python" operate on python3.9 + sudo port select --set python3 python39 + sudo port select --set cython cython39 + ``` + + This won't set a default python3.pc (and python3-embed.pc) under the MacPorts pkgconfig includes folder. + + To fix that, follow these steps: + + ``` + cd /opt/local/lib/pkgconfig + sudo ln -svf python3.pc python-3.9.pc + sudo ln -svf python3-embed.pc python-3.9-embed.pc + ``` + +4. (optional) Install makefile dependencies: + + ``` + sudo port install recode + sudo port install astyle + ``` + + +## Compile and use the project + +To use the compiled client, you can use `pm3` script, it is a wrapper of the proxmark3 client that handles automatic detection of your proxmark. + +Now you're ready to follow the [compilation instructions](/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md). + +To flash on OS X, better to enter the bootloader mode manually, else you may experience errors. +With your Proxmark3 unplugged from your machine, press and hold the button on your Proxmark3 as you plug it into a USB port. You can release the button, two of the four LEDs should stay on. You're in bootloader mode, ready for the next step. In case the two LEDs don't stay on when you're releasing the button, you've an old bootloader, start over and keep the button pressed during the whole flashing procedure. +From there, you can follow the original compilation instructions. \ No newline at end of file diff --git a/doc/md/Installation_Instructions/Windows-Installation-Instructions.md b/doc/md/Installation_Instructions/Windows-Installation-Instructions.md index 25d9bebc3..123d415d5 100644 --- a/doc/md/Installation_Instructions/Windows-Installation-Instructions.md +++ b/doc/md/Installation_Instructions/Windows-Installation-Instructions.md @@ -20,7 +20,7 @@ We have listed three ways to use these two setups (dev environment vs pre-compi ^[Top](#top) ## Video Installation guide -_note: this video is a bit out-of-date but still informative_ +_note: this video is out-of-date but still informative_ [![Windows Installation tutorial](https://raw.githubusercontent.com/Chrissy-Morgan/Proxmark3-RDV4-ParrotOS/master/screenshot-www.youtube.com-2019.03.17-20-44-33.png)](https://youtu.be/zzF0NCMJnYU "Windows Installation Tutorial") ## Driver Installation ( Windows 7 ) @@ -83,7 +83,10 @@ Now you're ready to follow the [compilation instructions](/doc/md/Use_of_Proxmar # Installing pre-compiled binaries with ProxSpace ^[Top](#top) -to be done (tcprst) +There are a community effort by @gator96100 to make up-to-date precompiled version of the offical repository and this repository. +[www.proxmarkbuilds.org](https://www.proxmarkbuilds.org/) + +It has excellent instructions to follow. @@ -104,7 +107,9 @@ Make sure your WSL can launch Windows processes to get the `pm3` scripts working ## X Server Installation -If you want to run the graphical components of the Proxmark3 client, you need to install a X Server such as [VcXsrv](https://sourceforge.net/projects/vcxsrv/) or [Xming](https://sourceforge.net/projects/xming/) and launch it, e.g. by executing XLaunch. +If you want to run the graphical components of the Proxmark3 client, you need to install a X Server such as in the list below, and launch it, e.g. by executing XLaunch. + * [VcXsrv](https://sourceforge.net/projects/vcxsrv/) + * [Xming](https://sourceforge.net/projects/xming/) ## Windows Terminal Installation @@ -114,7 +119,7 @@ It is also open sourced (see [github.com/microsoft/terminal](https://github.com/ ## Dependencies -Enter WSL prompt (`wsl` or Start Windows Terminal) and from there, follow the [Linux Installation Instructions](/doc/md/Installation_Instructions/Linux-Installation-Instructions.md) for Ubuntu, summarized here below: +Enter WSL prompt (`wsl` or Start Windows Terminal with `wt`) and from there, follow the [Linux Installation Instructions](/doc/md/Installation_Instructions/Linux-Installation-Instructions.md) for Ubuntu, summarized here below: Make sure your WSL guest OS is up-to-date first: diff --git a/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md b/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md index c40d18458..9ac9472ea 100644 --- a/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md +++ b/doc/md/Use_of_Proxmark/0_Compilation-Instructions.md @@ -7,9 +7,14 @@ Nevertheless, the firmware can be tuned depending on the Proxmark3 platform and Indeed, the RRG/Iceman fork can be used on other Proxmark3 hardware platforms as well. -Via some definitions, you can adjust the firmware for a given platform, but also to add features like the support of the Blue Shark add-on or to select which standalone mode to embed. +Via some definitions, you can adjust the firmware for a given platform, but also to add features like the support of the Blue Shark add-on or to select which standalone mode to embed. To learn how to adjust the firmware, please read [Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md). + +### Compile for Proxmark3 RDV4 +The repo defaults for compiling a firmware and client suitable for Proxmark3 RDV4. + +### Compile for generic Proxmark3 platforms +In order to build this repo for generic Proxmark3 platforms we urge you to read [Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md) -To learn how to adjust the firmware, please read [Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md). ## Get the latest commits @@ -21,13 +26,13 @@ git pull ## Clean and compile everything ```sh -make clean && make all +make clean && make -j ``` ### if you got an error Read the [troubleshooting guide](/doc/md/Installation_Instructions/Troubleshooting.md), -For instance, on WSl-1 you usually get the _libQt5Core.so.5 not found_ message +For instance, on WSl-1 you usually get the `libQt5Core.so.5 not found` message [solution](/doc/md/Installation_Instructions/Troubleshooting.md#libQt5Coreso5-not-found) @@ -74,6 +79,9 @@ proxmark3 /dev/ttyACM0 --flash --unlock-bootloader --image /tmp/my-bootrom.elf - ## Run the client In most cases, you can run the script `pm3` which try to auto-detect the port to use, on several OS. +```sh +./pm3 +``` For the other cases, specify the port by yourself. For example, for a Proxmark3 connected via USB under Linux: diff --git a/doc/md/Use_of_Proxmark/1_Validation.md b/doc/md/Use_of_Proxmark/1_Validation.md index 6f644d206..7e75a12c3 100644 --- a/doc/md/Use_of_Proxmark/1_Validation.md +++ b/doc/md/Use_of_Proxmark/1_Validation.md @@ -3,9 +3,9 @@ If all went well you should get some information about the firmware and memory usage as well as the prompt, something like this. ``` -[=] Session log /home/iceman/.proxmark3/log_20200521.txt +[=] Session log /home/iceman/.proxmark3/log_20210708.txt [+] loaded from JSON file /home/iceman/.proxmark3/preferences.json -[=] Using UART port /dev/ttyS7 +[=] Using UART port /dev/ttyS3 [=] Communicating with PM3 over USB-CDC @@ -21,8 +21,8 @@ If all went well you should get some information about the firmware and memory u [ Proxmark3 RFID instrument ] [ CLIENT ] - client: RRG/Iceman/master/v4.9237-2-g2cb19874 2020-05-21 22:00:00 - compiled with GCC 9.3.0 OS:Linux ARCH:x86_64 + client: RRG/Iceman/master/v4.13441-129-g60d132fcc 2021-07-08 22:00:00 + compiled with GCC 10.3.0 OS:Linux ARCH:x86_64 [ PROXMARK RDV4 ] device.................... RDV4 @@ -32,8 +32,8 @@ If all went well you should get some information about the firmware and memory u FPC USART for BT add-on... absent [ ARM ] - bootrom: RRG/Iceman/master/v4.9237-2-g2cb19874 2020-05-21 22:00:10 - os: RRG/Iceman/master/v4.9237-2-g2cb19874 2019-05-21 22:00:26 + bootrom: RRG/Iceman/master/v4.13441 2020-05-21 22:00:10 + os: RRG/Iceman/master/v4.13441 2019-05-21 22:00:26 compiled with GCC 9.2.1 20191025 (release) [ARM/arm-9-branch revision 277599] [ FPGA ] diff --git a/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md b/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md index 55e05af92..8ed9adf2b 100644 --- a/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md +++ b/doc/md/Use_of_Proxmark/2_Configuration-and-Verification.md @@ -1,6 +1,6 @@ -### First things on your RDV40 +### First things on your Proxmark3 RDV4 -You will need to run these commands to make sure your rdv4 is prepared +You will need to run these commands to make sure your RDV4 is prepared ``` [usb] pm3 --> script run init_rdv4 ``` diff --git a/doc/md/Use_of_Proxmark/3_Commands-and-Features.md b/doc/md/Use_of_Proxmark/3_Commands-and-Features.md index d2786956a..59be59127 100644 --- a/doc/md/Use_of_Proxmark/3_Commands-and-Features.md +++ b/doc/md/Use_of_Proxmark/3_Commands-and-Features.md @@ -9,7 +9,7 @@ Please make sure you've gone through the following pages firstly: ## To get interactive help -As seen before, for basic help type `help`. Or for help on a set of sub commands type the command followed by `help`. For example `hf mf help`. Many commands uses the `h` / `-h` parameter to show a help text. +As seen before, for basic help type `help`. Or for help on a set of sub commands type the command followed by `help`. For example `hf mf help`. Many commands uses the `-h` / `--help` parameter to show a help text. ## New Features in RDV4 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 4ed8a22a4..b28fcf64e 100644 --- a/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md +++ b/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md @@ -71,22 +71,23 @@ If you have installed a Blue Shark add-on on your RDV4, define `PLATFORM_EXTRAS= ## STANDALONE -The RRG/Iceman fork gives you to easily choose which standalone mode to embed in the firmware. +The RRG/Iceman repository gives you to easily choose which standalone mode to embed in the firmware. Here are the supported values you can assign to `STANDALONE` in `Makefile.platform`: | STANDALONE | DESCRIPTION | |-----------------|----------------------------------------| | | No standalone mode -| LF_SKELETON | standalone mode skeleton - Iceman1001 -| LF_THAREXDE | LF EM4x50 simulator/read standalone mode - tharexde | LF_EM4100EMUL | LF EM4100 simulator standalone mode - temskiy | LF_EM4100RSWB | LF EM4100 read/write/clone/brute mode - Monster1024 | LF_EM4100RWC | LF EM4100 read/write/clone mode - temskiy | LF_HIDBRUTE | HID corporate 1000 bruteforce - Federico dotta & Maurizio Agazzini | LF_ICEHID | LF HID collector to flashmem - Iceman1001 +| LF_NEXID | Nexwatch credentials detection mode - jrjgjk & Zolorah | LF_PROXBRUTE | HID ProxII bruteforce - Brad Antoniewicz | LF_SAMYRUN | HID26 read/clone/sim - Samy Kamkar +| LF_SKELETON | standalone mode skeleton - Iceman1001 +| LF_THAREXDE | LF EM4x50 simulator/read standalone mode - tharexde | HF_14ASNIFF | 14a sniff storing to flashmem - Micolous | HF_AVEFUL | MIFARE Ultralight read/simulation - Ave Ozkal | HF_BOG | 14a sniff with ULC/ULEV1/NTAG auth storing in flashmem - Bogito @@ -96,6 +97,7 @@ Here are the supported values you can assign to `STANDALONE` in `Makefile.platfo | HF_LEGIC | HF Legic Prime standalone - uhei | HF_MATTYRUN | Mifare sniff/clone - Matías A. Ré Medina | HF_MSDSAL (def)| EMV Read and emulation - Salvador Mendoza +| HF_REBLAY | 14A relay over BT - Salvador Mendoza | HF_TCPRST | IKEA Rothult ST25TA, Standalone Master Key Dump/Emulation - Nick Draffen | HF_TMUDFORD | Read and emulate ISO15693 card UID - Tim Mudford | HF_YOUNG | Mifare sniff/simulation - Craig Young @@ -134,6 +136,7 @@ a series of `SKIP_*` allow to skip some of the functionalities and to get a smal |SKIP_HFSNIFF=1 | 0.5kb |SKIP_HFPLOT=1 | 0.3kb + So for example, at the time of writing, this is a valid `Makefile.platform` compiling an image for 256k: ``` PLATFORM=PM3GENERIC diff --git a/doc/termux_notes.md b/doc/termux_notes.md index da737b608..ae7207ddf 100644 --- a/doc/termux_notes.md +++ b/doc/termux_notes.md @@ -99,7 +99,7 @@ pkg install proxmark3-git ``` ### Optional: Building Proxmark3 client from source ``` -pkg install make clang clang++ readline libc++ git +pkg install make clang readline libc++ git git clone https://github.com/RfidResearchGroup/proxmark3.git cd proxmark make clean && make client diff --git a/docker/archlinux/README.md b/docker/archlinux/README.md index 67bf501e5..f1bb08523 100644 --- a/docker/archlinux/README.md +++ b/docker/archlinux/README.md @@ -26,3 +26,14 @@ sudo pacman -S staging/gcc sudo pacman -S testing/gcc sudo pacman -S gcc ``` + +# Notes to run tests + +Add first the mirrors, see above + +``` +sudo pacman -S python +sudo pacman -S python-pip +python3 -m pip install ansicolors sslcrypto +tools/pm3_tests.sh --long +``` diff --git a/docker/debian-buster/README.md b/docker/debian-buster/README.md new file mode 100644 index 000000000..c6fce658c --- /dev/null +++ b/docker/debian-buster/README.md @@ -0,0 +1,9 @@ +# Notes to run tests + +``` +sudo apt update +sudo apt install python3-minimal +sudo apt install python3-pip +python3 -m pip install ansicolors sslcrypto +tools/pm3_tests.sh --long +``` diff --git a/docker/fedora-34/README.md b/docker/fedora-34/README.md new file mode 100644 index 000000000..31e409af7 --- /dev/null +++ b/docker/fedora-34/README.md @@ -0,0 +1,26 @@ +# Notes to run tests + +``` +sudo yum -y update +sudo yum -y install python-pip +python3 -m pip install ansicolors sslcrypto +tools/pm3_tests.sh --long +``` + +Warning, no idea how to manage to run `recover_pk` on Fedora... +Tried the followings: +``` +python3 -m pip install pyopenssl +sudo yum -y install openssl-devel libffi-devel +``` +Error is: +``` + File "/home/rrg/.local/lib/python3.9/site-packages/sslcrypto/_ecc.py", line 202, in get_curve + return EllipticCurve(self._backend, params, self._aes, nid) + File "/home/rrg/.local/lib/python3.9/site-packages/sslcrypto/_ecc.py", line 211, in __init__ + self._backend = backend_factory(**params) + File "/home/rrg/.local/lib/python3.9/site-packages/sslcrypto/openssl/ecc.py", line 221, in __init__ + raise ValueError("Could not create group object") +``` + +So just comment the "recover_pk test" for now, until someone figures out how to solve the issue. diff --git a/docker/opensuse-leap/Dockerfile b/docker/opensuse-leap/Dockerfile index cf860aeff..2fa63c4ad 100644 --- a/docker/opensuse-leap/Dockerfile +++ b/docker/opensuse-leap/Dockerfile @@ -2,7 +2,7 @@ FROM opensuse/leap 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 cross-arm-none-gcc9 cross-arm-none-newlib-devel +RUN zypper --non-interactive install --no-recommends shadow sudo git patterns-devel-base-devel_basis gcc-c++ readline-devel libbz2-devel # Create rrg user RUN useradd -ms /bin/bash rrg diff --git a/docker/opensuse-tumbleweed/README.md b/docker/opensuse-tumbleweed/README.md new file mode 100644 index 000000000..091f2144b --- /dev/null +++ b/docker/opensuse-tumbleweed/README.md @@ -0,0 +1,8 @@ +# Notes to run tests + +``` +sudo zypper --non-interactive install python3 +sudo zypper --non-interactive install python3-pip +python3 -m pip install ansicolors sslcrypto +tools/pm3_tests.sh --long +``` diff --git a/docker/ubuntu-18.04/README.md b/docker/ubuntu-18.04/README.md new file mode 100644 index 000000000..c6fce658c --- /dev/null +++ b/docker/ubuntu-18.04/README.md @@ -0,0 +1,9 @@ +# Notes to run tests + +``` +sudo apt update +sudo apt install python3-minimal +sudo apt install python3-pip +python3 -m pip install ansicolors sslcrypto +tools/pm3_tests.sh --long +``` diff --git a/include/common.h b/include/common.h index 50d0aed69..95784acee 100644 --- a/include/common.h +++ b/include/common.h @@ -131,15 +131,21 @@ extern bool tearoff_enabled; #endif #endif -// convert 2 bytes to U16 +// convert 2 bytes to U16 in little endian #ifndef BYTES2UINT16 # define BYTES2UINT16(x) ((x[1] << 8) | (x[0])) #endif -// convert 4 bytes to U32 +// convert 4 bytes to U32 in little endian #ifndef BYTES2UINT32 # define BYTES2UINT32(x) ((x[3] << 24) | (x[2] << 16) | (x[1] << 8) | (x[0])) #endif +// convert 4 bytes to U32 in big endian +#ifndef BYTES2UINT32_BE +# define BYTES2UINT32_BE(x) ((x[0] << 24) | (x[1] << 16) | (x[2] << 8) | (x[3])) +#endif + + #define EVEN 0 #define ODD 1 diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index ed96c8467..53343c5e1 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -259,6 +259,12 @@ typedef struct { uint8_t data[]; } PACKED lf_nrzsim_t; +typedef struct { + uint8_t type; + uint16_t len; + uint8_t *data; +} PACKED lf_hitag_t; + typedef struct { uint8_t blockno; uint8_t keytype; @@ -734,6 +740,9 @@ typedef struct { #define CMD_HF_MIFARE_GEN3BLK 0x0851 #define CMD_HF_MIFARE_GEN3FREEZ 0x0852 +// Gen 3 GTU magic cards +#define CMD_HF_MIFARE_G3_RDBL 0x0860 + #define CMD_UNKNOWN 0xFFFF //Mifare simulation flags diff --git a/include/protocols.h b/include/protocols.h index 4b3cf763f..58bd7e90f 100644 --- a/include/protocols.h +++ b/include/protocols.h @@ -436,6 +436,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MFDES_GET_FILE_IDS 0x6F #define MFDES_ABORT_TRANSACTION 0xA7 #define MFDES_ADDITIONAL_FRAME 0xAF +#define MFDES_UPDATE_RECORD1 0xBA #define MFDES_READ_RECORDS 0xBB #define MFDES_READ_DATA 0xBD #define MFDES_CREATE_CYCLIC_RECORD_FILE 0xC0 @@ -451,6 +452,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MFDES_CREATE_STD_DATA_FILE 0xCD #define MFDES_CREATE_TRANS_MAC_FILE 0xCE #define MFDES_DELETE_APPLICATION 0xDA +#define MFDES_UPDATE_RECORD2 0xDB #define MFDES_DEBIT 0xDC #define MFDES_DELETE_FILE 0xDF #define MFDES_CLEAR_RECORD_FILE 0xEB @@ -459,6 +461,7 @@ ISO 7816-4 Basic interindustry commands. For command APDU's. #define MFDES_GET_FILE_SETTINGS 0xF5 #define MFDES_FORMAT_PICC 0xFC #define MFDES_VERIFY_PC 0xFD +#define MFDES_NATIVE_ISO7816_WRAP_CLA 0x90 // MIFARE DESFire status & error codes: #define MFDES_S_OPERATION_OK 0x00 diff --git a/tools/fpga_compress/fpga_compress.c b/tools/fpga_compress/fpga_compress.c index 003236994..1726117fd 100644 --- a/tools/fpga_compress/fpga_compress.c +++ b/tools/fpga_compress/fpga_compress.c @@ -179,6 +179,9 @@ static int zlib_decompress(FILE *infile, FILE *outfiles[], uint8_t num_outfiles, fclose(outfiles[j]); } } + if (outbufall) { + free(outbufall); + } free(inbuf); return (EXIT_FAILURE); } @@ -227,8 +230,8 @@ static int zlib_decompress(FILE *infile, FILE *outfiles[], uint8_t num_outfiles, total_size = 0; // FPGA bit file ends with 16 zeroes for (uint16_t j = 0; j < num_outfiles; j++) { - outfilesizes[j] += 16; - total_size += outfilesizes[j]; + outfilesizes[j] += 16; + total_size += outfilesizes[j]; } offset = 0; for (uint16_t k = 0; k < *outsize / (FPGA_INTERLEAVE_SIZE * num_outfiles); k++) { @@ -247,6 +250,9 @@ static int zlib_decompress(FILE *infile, FILE *outfiles[], uint8_t num_outfiles, fclose(outfiles[j]); } } + if (outbufall) { + free(outbufall); + } return (EXIT_SUCCESS); } diff --git a/tools/mf_nonce_brute/mf_nonce_brute.c b/tools/mf_nonce_brute/mf_nonce_brute.c index b064f3683..973989eda 100644 --- a/tools/mf_nonce_brute/mf_nonce_brute.c +++ b/tools/mf_nonce_brute/mf_nonce_brute.c @@ -417,6 +417,7 @@ static void *brute_thread(void *arguments) { if (isOK == false) { printf(_RED_("<-- not a valid cmd\n")); pthread_mutex_unlock(&print_lock); + free(revstate); continue; } @@ -425,6 +426,7 @@ static void *brute_thread(void *arguments) { if (isOK == false) { printf(_RED_("<-- not a valid crc\n")); pthread_mutex_unlock(&print_lock); + free(revstate); continue; } else { printf("<-- valid cmd\n"); diff --git a/tools/pm3_online_check.py b/tools/pm3_online_check.py new file mode 100755 index 000000000..ac7ef152b --- /dev/null +++ b/tools/pm3_online_check.py @@ -0,0 +1,449 @@ +#!/usr/bin/env python3 + +''' + +# pm3_online_check.py +# Christian Herrmann, Iceman, 2020 +# version = 'v1.0.5' +# +# This code is copyright (c) Christian Herrmann, 2020, All rights reserved. +# For non-commercial use only, the following terms apply - for all other +# uses, please contact the author: +# +# This code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This code 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. +# +# +# Dependecies: +# +# pip3 install pexpect ansicolors +# +''' +import pexpect +from colors import color +import requests +import string +import re +import time +import argparse + +def pm3_flashbootrom(): + flbootrom = pexpect.spawnu('./pm3-flash-bootrom') + flbootrom.expect(pexpect.EOF) + msg = escape_ansi(str(flbootrom.before)) + if 'Have a nice day!'.lower() in msg: + print("Flashing bootrom ", color('[OK]', fg='green')) + else: + print("Flashing bootrom ", color('[FAIL]', fg='red')) + + time.sleep(20) + +def pm3_flashfullimage(): + flimage = pexpect.spawnu('./pm3-flash-fullimage') + flimage.expect(pexpect.EOF) + msg = escape_ansi(str(flimage.before)) + if 'Have a nice day!'.lower() in msg: + print("Flashing fullimage ", color('[OK]', fg='green')) + else: + print("Flashing fullimage ", color('[FAIL]', fg='red')) + + time.sleep(20) + +def escape_ansi(line): + ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]') + return ansi_escape.sub('', str(line)).lower() + +def pm3_initrdv4(child): + child.sendline('script run init_rdv4') + i = child.expect('pm3 --> ') + + msg = escape_ansi(str(child.before)) + if 'finished init_rdv4'.lower() in msg: + print("Init RDV4 ", color('[OK]', fg='green')) + else: + print("Init RDV4 ", color('[FAIL]', fg='red')) + +# LF T55x7 wipe/clone/read/wipe test +def pm3_lf_t55xx(child): + + try: + print("[=] starting lf t55xx tests...") + + # wipe t55xx + child.sendline('lf t55xx wipe') + i = child.expect('pm3 --> ') + + msg = escape_ansi(str(child.before)) + if 'Writing page 0 block: 07 data: 0x00000000'.lower() in msg: + print("[+] LF T55XX WIPE ", color('[OK]', fg='green')) + else: + print("[-] LF T55XX WIPE ", color('[FAIL]', fg='red')) + + # clone HID + child.sendline('lf hid clone -r 2006ec0c86') + i = child.expect('pm3 --> ') + + msg = escape_ansi(str(child.before)) + if 'Done'.lower() in msg: + print("[+] LF HID CLONE ", color('[OK]', fg='green')) + else: + print("[-] LF HID CLONE ", color('[FAIL]', fg='red')) + + # read HID + child.sendline('lf hid read') + i = child.expect('pm3 --> ') + + msg = escape_ansi(str(child.before)) + if "HID H10301 26-bit; FC: 118 CN: 1603 parity: valid".lower() in msg: + print("[+] LF HID READ ", color('[OK]', fg='green')) + else: + print("[-] LF HID READ ", color('[FAIL]', fg='red')) + + # wipe t55xx + child.sendline('lf t55xx wipe') + i = child.expect('pm3 --> ') + return True + + except: + print(color("[!] exception for LF T55XX", fg='red')) + msg = escape_ansi(str(child.before)) + print(msg) + child.sendline('quit') + child.expect(pexpect.EOF) + return False + +def pm3_flash_sm(child): + try: + print("[+] Updating smart card fw") + child.sendline('smart upgrade -f sim011.bin') + i = child.expect('pm3 --> ') + msg = escape_ansi(str(child.before)) + print("================") + print(" smart card upgrade") + print("==== msg ========") + print(msg) + if "successful" in msg: + print("[+] Smart card firmware upgrade ", color('[OK]', fg='green')) + return True + else: + print("[-] Smart card firmware upgrade ", color('[FAIL]', fg='red')) + return False + except: + print(color("[!] exception for SMART UPGRADE", fg='red')) + msg = escape_ansi(str(child.before)) + print(msg) + child.sendline('quit') + child.expect(pexpect.EOF) + return False + +def main(): + + parser = argparse.ArgumentParser() + parser.add_argument("--flash", help="flash bootrom & fullimage", action="store_true") + parser.add_argument("--init", help="run init rdv4 script", action="store_true") + parser.add_argument("-y", help="automatic yes to prompts", action="store_true") + args = parser.parse_args() + + print("-----------", color('Proxmark3 online test script v1.0.3', fg='cyan'), "------------") + print("This script will run some series of test against a connected Proxmark3 device") + print("Steps:"); + print(" 1. flash bootrom, fullimage"); + print(" 2. init_rdv4 / flash smartcard"); + print(" 3. check device mismatch message"); + print(" 4. check smart card fw, flash memory"); + print(" if needed, flash flash smartcard reader firmware"); + print(" 5. check antenna tuning"); + print(" 6. check LF T55x7 functionality"); + print(" 7. check HF search"); + print(" 8. check SPIFFS"); + print(" 9. check HF iCLASS functionality"); + print("\n"); + + # result + res = 0 + total_tests = 12 + must_update_fw = 0 + msg = '' + + if args.flash: + print("-----------------------", color('Flashing phase', fg='cyan'), "---------------------") + print("flashing bootrom - don't touch the device or cables") + pm3_flashbootrom() + + print("flashing fullimage - don't touch the device or cables") + pm3_flashfullimage() + print("\n") + + # start pm3 + child = pexpect.spawnu('./pm3') + i = child.expect('pm3 --> ') + print("[+] Proxmark3 client open") + + if args.init: + print("------------------------", color('Init phase', fg='cyan'), "------------------------") + print("Running init rdv4 script - don't touch the device or cables") + pm3_initrdv4(child) + print("flashing smartcard - don't touch the device or cables") + pm3_flash_sm(child) + print("\n") + + print("------------------------", color('Test phase', fg='cyan'), "------------------------") + + + # check device mismatch + signature_msg = "device.................... RDV4".lower() + + # check flashmemory + flash_mem = "baudrate................24 mhz".lower() + + # check smartcard fw version + sm_version = "version.................v3.11".lower() + + # check LF + lf_search = "valid hid prox id found!".lower() + + # check HF + hf_search = "Valid iCLASS tag / PicoPass tag found".lower() + + # mem spiffs info + mem_spiffs = "max path length............32 chars".lower() + + # lf antenna tuning + lf_tune = "LF antenna is OK".lower() + + # hf antenna tuning + hf_tune = "HF antenna is OK".lower() + + try: + # HW VERSION checks + child.sendline('hw version') + i = child.expect('pm3 --> ') + msg = escape_ansi(str(child.before)) + + if signature_msg in msg: + print("[+] RDV4 signature ", color('[OK]', fg='green')) + res += 1 + else: + print("[-] RDV4 signature ", color('[FAIL]', fg='red')) + + + # HW STATUS checks + child.sendline('hw status') + i = child.expect('pm3 --> ') + msg = escape_ansi(str(child.before)) + + if sm_version in msg: + print("[+] Smart card firmware version ", color('[OK]', fg='green')) + res += 1 + else: + print("[-] Smart card firmware version ", color('[FAIL]', fg='red'), " will upgrade fw in the next step") + must_update_fw = 1 + + if flash_mem in msg: + print("[+] Flash memory accessable ", color('[OK]', fg='green')) + res += 1 + else: + print("[-] Flash memory accessable ", color('[FAIL]', fg='red')) + + # extract slow clock and verify its OK... + # slow clock check: + # Slow clock..............30057 Hz + for line in msg.splitlines(): + match_slow = line.find('slow clock..............') + + if match_slow > -1: + match = re.search(r'\d+', line) + if match: + clock = int(match[0]) + if clock < 29000: + print("[-] Warning, Slow clock too slow (%d Hz)" % (clock), color('[FAIL]', fg='red')) + elif clock > 33000: + print("[-] Warning, Slow clock too fast (%d Hz)" % (clock), color('[FAIL]', fg='red')) + else: + print("[+] Slow clock within acceptable range (%d Hz)" % (clock), color('[OK]', fg='green')) + res += 1 + except: + print(color("[!] exception for HW STATUS", fg='red')) + msg = escape_ansi(str(child.before)) + print(msg) + child.sendline('quit') + child.expect(pexpect.EOF) + return + + if must_update_fw == 1: + if pm3_flash_sm(child): + res += 1 + + try: + print("[=] starting antenna tune tests, this takes some time and plot window will flash up...") + # HW TUNE checks + child.sendline('hw tune') + i = child.expect('pm3 --> ') + + msg = escape_ansi(str(child.before)) + if lf_tune in msg: + print("[+] LF antenna tuning ", color('[OK]', fg='green')) + res += 1 + else: + print("[-] LF antenna tuning ", color('[FAIL]', fg='red')) + + if hf_tune in msg: + print("[+] HF antenna tuning ", color('[OK]', fg='green')) + res += 1 + else: + print("[-] HF antenna tuning ", color('[FAIL]', fg='red')) + + except: + print(color("[!] exception for hw tune", fg='red')) + msg = escape_ansi(str(child.before)) + print(msg) + child.sendline('quit') + child.expect(pexpect.EOF) + return + + # hide plot window again + child.sendline('data hide') + i = child.expect('pm3 --> ') + + ans = '' + + while ans != 'y' and args.y == False: + + ans = (input(color('>>> Put LF card and HF card on Proxmark3 antenna', fg='yellow') + ' [Y/n/q] ') or "y") + + if ans == 'q': + child.sendline('quit') + child.expect(pexpect.EOF) + print('[!] Aborted all tests ', color('[USER ABORTED]', fg='red')) + return + + # LF T55X7 WIPE/CLONE/READ TESTS + if pm3_lf_t55xx(child): + res += 1 + + # HF SEARCH TESTS + try: + print("[=] starting HF SEARCH tests...") + + # HF SEARCH Test + child.sendline('hf search') + i = child.expect('pm3 --> ') + + msg = escape_ansi(str(child.before)) + if hf_search in msg: + print("[+] HF SEARCH ", color('[OK]', fg='green')) + res += 1 + else: + print("[-] HF SEARCH ", color('[FAIL]', fg='red')) + + except: + print(color("[!] exception for HF SEARCH", fg='red')) + msg = escape_ansi(str(child.before)) + print(msg) + child.sendline('quit') + child.expect(pexpect.EOF) + return + + # MEM Tree test + child.sendline('mem spiffs info') + i = child.expect('/', timeout=10) + + msg = escape_ansi(str(child.before)) + if mem_spiffs in msg: + print("[+] MEM SPIFFS INFO ", color('[OK]', fg='green')) + res += 1 + else: + print("[-] MEM SPIFFS INFO ", color('[FAIL]', fg='red')) + + + ans = '' + while ans != 'y' and args.y == False: + + ans = (input(color('>>> Put iCLASS legacy card on Proxmark3 antenna', fg='yellow') + ' [Y/n/q] ') or "y") + + if ans == 'q': + child.sendline('quit') + child.expect(pexpect.EOF) + print('[!] Aborted all tests ', color('[USER ABORTED]', fg='red')) + return + + # iCLASS read/write test + try: + print("[=] starting iCLASS info/read/write tests...") + child.sendline('hf iclass info') + i = child.expect('pm3 --> ') + + # iclass info / read / write checks + iclass_info = 'Credential... iCLASS legacy'.lower() + + iclass_ok = False + msg = escape_ansi(str(child.before)) + if iclass_info in msg: + print("[+] HF ICLASS INFO ", color('[OK]', fg='green')) + res += 1 + iclass_ok = True + else: + print("[-] HF ICLASS INFO ", color('[FAIL]', fg='red')) + + if iclass_ok: + + child.sendline('hf iclass rdbl -b 10 --ki 0') + i = child.expect('pm3 --> ') + msg = escape_ansi(str(child.before)) + for line in msg.splitlines(): + iclass_read = 'block 10'.lower() + if iclass_read in line: + res += 1 + print("[+] HF ICLASS RDBL ", color('[OK]', fg='green')) + old_b10 = line[16:].replace(" ","") + + child.sendline('hf iclass wrbl -b 10 --ki 0 -d 0102030405060708') + i = child.expect('pm3 --> ') + msg = escape_ansi(str(child.before)) + iclass_write = 'wrote block 10 successful'.lower() + if iclass_write in msg: + res += 1 + print("[+] HF ICLASS WRBL ", color('[OK]', fg='green')) + child.sendline('hf iclass wrbl -b 10 --ki 0 -d %s' % (old_b10)) + i = child.expect('pm3 --> ') + else: + print("[-] HF ICLASS WRBL ", color('[FAIL]', fg='red')) + + break; + + else: + print("[-] skipping iclass read/write") + + except: + print(color("[!] exception iCLASS read/write", fg='red')) + msg = escape_ansi(str(child.before)) + print(msg) + child.sendline('quit') + child.expect(pexpect.EOF) + return + + + # exit Proxmark3 client + child.sendline('quit') + i = child.expect(pexpect.EOF) + + print("[+] PM3 client closed\n") + + # validate test results + + print("-------------------------", color('Results', fg='cyan'), "-------------------------") + if res == total_tests: + print('[+] Passed ', color('[OK]', fg='green')) + else: + print('[-] failed test ', color('[FAIL]', fg='red'), '(%d / %d tests)' % (res, total_tests)) + print("") + +if __name__ == "__main__": + main() diff --git a/tools/pm3_reblay-emulating.py b/tools/pm3_reblay-emulating.py new file mode 100644 index 000000000..27b88f06b --- /dev/null +++ b/tools/pm3_reblay-emulating.py @@ -0,0 +1,55 @@ +""" +//----------------------------------------------------------------------------- +// Salvador Mendoza (salmg.net), 2021 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Code to test Proxmark3 Standalone mode aka reblay by Salvador Mendoza +//----------------------------------------------------------------------------- +""" + +import serial +from smartcard.util import toHexString, toBytes +from smartcard.CardType import AnyCardType +from smartcard.CardRequest import CardRequest + +ser = serial.Serial('/dev/rfcomm0') # open Proxmark3 Bluetooth port + +def pd(data): + rapdu = map(ord, data) + return rapdu + +apdu = [ + [0x6F, 0x23, 0x84, 0x0E, 0x32, 0x50, 0x41, 0x59, 0x2E, 0x53, 0x59, 0x53, 0x2E, 0x44, 0x44, 0x46, 0x30, 0x31, 0xA5, 0x11, 0xBF, 0x0C, 0x0E, 0x61, 0x0C, 0x4F, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x87, 0x01, 0x01, 0x90, 0x00], + [0x6F, 0x1E, 0x84, 0x07, 0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0xA5, 0x13, 0x50, 0x0B, 0x56, 0x49, 0x53, 0x41, 0x20, 0x43, 0x52, 0x45, 0x44, 0x49, 0x54, 0x9F, 0x38, 0x03, 0x9F, 0x66, 0x02, 0x90, 0x00], + [0x80, 0x06, 0x00, 0x80, 0x08, 0x01, 0x01, 0x00, 0x90, 0x00], + [0x70,0x15,0x57,0x13,0x46,0x50,0x98,0x29,0x81,0x62,0x29,0x58,0xd2,0x40,0x32,0x01,0x14,0x69,0x00,0x00,0x13,0x83,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x8f,0x90,0x00], + [0x6f, 0x00], + [0x6f, 0x00] +] + +print('Testing code: bluetooth has to be connected with the right rfcomm port!') +print('Waiting for data...') +initd = ser.read(1) + +bufferlen = pd(initd)[0] +rping = ser.read(bufferlen) +ping = pd(rping) + +print('Terminal command:'), +print(toHexString(ping)) + +for x in apdu: + print('Sending cmd: '), + ser.write(x) + print(toHexString(x)) + print('--') + + lenpk = ser.read(1) #first byte is the buffer length + bufferlen = pd(lenpk)[0] + + buffer = pd(ser.read(bufferlen)) + print('Terminal command:'), + print(toHexString(buffer)) diff --git a/tools/pm3_reblay-reading.py b/tools/pm3_reblay-reading.py new file mode 100644 index 000000000..26fa96844 --- /dev/null +++ b/tools/pm3_reblay-reading.py @@ -0,0 +1,69 @@ +""" +//----------------------------------------------------------------------------- +// Salvador Mendoza (salmg.net), 2021 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Code to test Proxmark3 Standalone mode aka reblay by Salvador Mendoza +//----------------------------------------------------------------------------- +""" + +import serial +from smartcard.util import toHexString + +ser = serial.Serial('/dev/rfcomm0') # open Proxmark3 Bluetooth port + +def pd(data): + rapdu = map(ord, data) + return rapdu + +apdu = [ + [0x00, 0xA4, 0x04, 0x00, 0x0e, 0x32, 0x50, 0x41, 0x59, 0x2e, 0x53, 0x59, 0x53, 0x2e, 0x44, 0x44, 0x46, 0x30, 0x31, 0x00], # PPSE + [0x00, 0xA4, 0x04, 0x00, 0x07, 0xa0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10, 0x00], # Visa card + [0x80, 0xA8, 0x00, 0x00, 0x02, 0x83, 0x00, 0x00], # GET PROCESSING + [0x00, 0xb2, 0x01, 0x0c, 0x00] # SFI +] + +print('Testing code: bluetooth has to be connected with the right rfcomm port!') +print('Waiting for data...') +initd = ser.read(1) + +bufferlen = pd(initd)[0] +rping = ser.read(bufferlen) + +ping = pd(rping) +if (len(ping) == 7): + print('UID:'), + print(toHexString(ping[:4])) + print('ATQA:'), + print(toHexString(ping[4:-1])) + print('SAK:'), + print(toHexString(ping[-1:])) +elif (len(ping) == 10): + print('UID:'), + print(toHexString(ping[:7])) + print('ATQA:'), + print(toHexString(ping[7:-1])) + print('SAK:'), + print(toHexString(ping[-1:])) +else: + print('got ping, no sure what it means: '), + print(ping) + +for x in apdu: + print('Sending cmd: '), + ser.write(x) + print(toHexString(x)) + + lenpk = ser.read(1) #first byte is the buffer length + bufferlen = pd(lenpk)[0] + + buffer = pd(ser.read(bufferlen)) + print('Card Response:'), + print(toHexString(buffer)) + print('--') + +ser.write(b'1') #tell Proxmark3 that we finish the communication +ser.close() diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index b812bdd03..499673c80 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -367,6 +367,8 @@ while true; do if ! CheckExecute "jooki encode test" "$CLIENTBIN -c 'hf jooki encode -t'" "04 28 F4 DA F0 4A 81 ( ok )"; then break; fi if ! CheckExecute "trace load/list 14a" "$CLIENTBIN -c 'trace load -f traces/hf_14a_mfu.trace; trace list -1 -t 14a;'" "READBLOCK(8)"; then break; fi if ! CheckExecute "trace load/list x" "$CLIENTBIN -c 'trace load -f traces/hf_14a_mfu.trace; trace list -x1 -t 14a;'" "0.0101840425"; then break; fi + if ! CheckExecute "nfc decode test - oob" "$CLIENTBIN -c 'nfc decode -d DA2010016170706C69636174696F6E2F766E642E626C7565746F6F74682E65702E6F6F62301000649201B96DFB0709466C65782032'" "Flex 2"; then break; fi + if ! CheckExecute "nfc decode test - device info" "$CLIENTBIN -c 'nfc decode -d d1025744690004536f6e79010752432d533338300220426c61636b204e46432052656164657220636f6e6e656374656420746f2050430310123e4567e89b12d3a45642665544000004124e464320506f72742d3130302076312e3032'" "NFC Port-100 v1.02"; then break; fi echo -e "\n${C_BLUE}Testing LF:${C_NC}" if ! CheckExecute "lf AWID test" "$CLIENTBIN -c 'data load -f traces/lf_AWID-15-259.pm3;lf search -1'" "AWID ID found"; then break; fi @@ -485,11 +487,10 @@ while true; do if ! CheckExecute slow "emv long test" "$CLIENTBIN -c 'emv test -l'" "Test(s) \[ 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 ! $SLOWTESTS; then - 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'" "Test(s) \[ ok"; then break; fi - 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'" "Test(s) \[ 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 fi echo -e "\n------------------------------------------------------------" echo -e "Tests [ ${C_GREEN}OK${C_NC} ] ${C_OK}\n"