diff --git a/armsrc/Makefile b/armsrc/Makefile index a71c46dfc..5475b57eb 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -27,7 +27,7 @@ APP_CFLAGS = $(PLATFORM_DEFS) \ SRC_LF = lfops.c lfsampling.c pcf7931.c lfdemod.c SRC_ISO15693 = iso15693.c iso15693tools.c -SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c +SRC_ISO14443a = iso14443a.c mifareutil.c mifarecmd.c epa.c mifaresim.c thinfilm.c SRC_ISO14443b = iso14443b.c SRC_FELICA = felica.c SRC_CRAPTO1 = crypto1.c des.c desfire_key.c desfire_crypto.c mifaredesfire.c aes.c platform_util.c diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 84a4748f0..4dcaac17a 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -26,6 +26,7 @@ #include "mifareutil.h" #include "mifaresim.h" #include "hitag.h" +#include "thinfilm.h" #define DEBUG 1 @@ -1278,6 +1279,10 @@ static void PacketReceived(PacketCommandNG *packet) { DetectNACKbug(); break; } + case CMD_THINFILM_READ: { + ReadThinFilm(); + break; + } #endif #ifdef WITH_ICLASS diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index a09978d7a..b6328ff19 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -463,6 +463,81 @@ RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_t return false; // not finished yet, need more data } + +// Thinfilm, Kovio mangels ISO14443A in the way that they don't use start bit nor parity bits. +RAMFUNC int ManchesterDecoding_Thinfilm(uint8_t bit) { + Demod.twoBits = (Demod.twoBits << 8) | bit; + + if (Demod.state == DEMOD_UNSYNCD) { + + if (Demod.highCnt < 2) { // wait for a stable unmodulated signal + if (Demod.twoBits == 0x0000) { + Demod.highCnt++; + } else { + Demod.highCnt = 0; + } + } else { + Demod.syncBit = 0xFFFF; // not set + if ((Demod.twoBits & 0x7700) == 0x7000) Demod.syncBit = 7; + else if ((Demod.twoBits & 0x3B80) == 0x3800) Demod.syncBit = 6; + else if ((Demod.twoBits & 0x1DC0) == 0x1C00) Demod.syncBit = 5; + else if ((Demod.twoBits & 0x0EE0) == 0x0E00) Demod.syncBit = 4; + else if ((Demod.twoBits & 0x0770) == 0x0700) Demod.syncBit = 3; + else if ((Demod.twoBits & 0x03B8) == 0x0380) Demod.syncBit = 2; + else if ((Demod.twoBits & 0x01DC) == 0x01C0) Demod.syncBit = 1; + else if ((Demod.twoBits & 0x00EE) == 0x00E0) Demod.syncBit = 0; + if (Demod.syncBit != 0xFFFF) { + Demod.startTime = (GetCountSspClk() & 0xfffffff8); + Demod.startTime -= Demod.syncBit; + Demod.bitCount = 0; // number of decoded data bits + Demod.state = DEMOD_MANCHESTER_DATA; + } + } + } else { + + if (IsManchesterModulationNibble1(Demod.twoBits >> Demod.syncBit)) { // modulation in first half + if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // ... and in second half = collision + if (!Demod.collisionPos) { + Demod.collisionPos = (Demod.len << 3) + Demod.bitCount; + } + } // modulation in first half only - Sequence D = 1 + Demod.bitCount++; + Demod.shiftReg = (Demod.shiftReg >> 1) | 0x100; // in both cases, add a 1 to the shiftreg + if (Demod.bitCount == 8) { // if we decoded a full byte + Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.bitCount = 0; + Demod.shiftReg = 0; + } + Demod.endTime = Demod.startTime + 8 * (8 * Demod.len + Demod.bitCount + 1) - 4; + } else { // no modulation in first half + if (IsManchesterModulationNibble2(Demod.twoBits >> Demod.syncBit)) { // and modulation in second half = Sequence E = 0 + Demod.bitCount++; + Demod.shiftReg = (Demod.shiftReg >> 1); // add a 0 to the shiftreg + if (Demod.bitCount >= 8) { // if we decoded a full byte + Demod.output[Demod.len++] = (Demod.shiftReg & 0xff); + Demod.bitCount = 0; + Demod.shiftReg = 0; + } + Demod.endTime = Demod.startTime + 8 * (8 * Demod.len + Demod.bitCount + 1); + } else { // no modulation in both halves - End of communication + if (Demod.bitCount > 0) { // there are some remaining data bits + Demod.shiftReg >>= (8 - Demod.bitCount); // right align the decoded bits + Demod.output[Demod.len++] = Demod.shiftReg & 0xff; // and add them to the output + return true; + } + if (Demod.len) { + return true; // we are finished with decoding the raw data sequence + } else { // nothing received. Start over + DemodReset(); + } + } + } + } + return false; // not finished yet, need more data +} + + + //============================================================================= // Finally, a `sniffer' for ISO 14443 Type A // Both sides of communication! @@ -579,7 +654,7 @@ void RAMFUNC SniffIso14443a(uint8_t param) { Uart.len, Uart.startTime * 16 - DELAY_READER_AIR2ARM_AS_SNIFFER, Uart.endTime * 16 - DELAY_READER_AIR2ARM_AS_SNIFFER, - Uart.parity, + Uart.parity, true)) break; } /* ready to receive another command. */ @@ -1930,6 +2005,55 @@ bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_Start } +//----------------------------------------------------------------------------- +// Kovio - Thinfilm barcode. TAG-TALK-FIRST - +// Wait a certain time for tag response +// If a response is captured return TRUE +// If it takes too long return FALSE +//----------------------------------------------------------------------------- +bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint8_t *received_len) { + + if (!iso14443a_active) + return false; + + // Set FPGA mode to "reader listen mode", no modulation (listen + // only, since we are receiving, not transmitting). + // Signal field is on with the appropriate LED + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_LISTEN); + + // Now get the answer from the card + DemodInit(receivedResponse, NULL); + + // clear RXRDY: + uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + (void)b; + + uint32_t receive_timer = GetTickCount(); + for (;;) { + WDT_HIT(); + + if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; + if (ManchesterDecoding_Thinfilm(b)) { + *received_len = Demod.len; + // log + LogTrace(receivedResponse, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, NULL, false); + return true; + } + } + + // timeout already in ms + 10ms guard time + if (GetTickCount() - receive_timer > 1160) + break; + } + *received_len = Demod.len; + // log + LogTrace(receivedResponse, Demod.len, Demod.startTime * 16 - DELAY_AIR2ARM_AS_READER, Demod.endTime * 16 - DELAY_AIR2ARM_AS_READER, NULL, false); + return false; +} + + //----------------------------------------------------------------------------- // Wait a certain time for tag response // If a response is captured return TRUE @@ -2018,6 +2142,7 @@ int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par) { return Demod.len; } + // This function misstreats the ISO 14443a anticollision procedure. // by fooling the reader there is a collision and forceing the reader to // increase the uid bytes. The might be an overflow, DoS will occure. @@ -2363,8 +2488,7 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { LED_D_OFF(); // Signal field is on with the appropriate LED - if (fpga_minor_mode == FPGA_HF_ISO14443A_READER_MOD || - fpga_minor_mode == FPGA_HF_ISO14443A_READER_LISTEN) + if (fpga_minor_mode == FPGA_HF_ISO14443A_READER_MOD || fpga_minor_mode == FPGA_HF_ISO14443A_READER_LISTEN) LED_D_ON(); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | fpga_minor_mode); @@ -2382,7 +2506,8 @@ void iso14443a_setup(uint8_t fpga_minor_mode) { iso14443a_active = true; } -void iso14443a_off() { + +void iso14443a_off(void) { FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); iso14443a_active = false; @@ -2893,7 +3018,7 @@ void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype) { * Mifare Classic NACK-bug detection * Thanks to @doegox for the feedback and new approaches. */ -void DetectNACKbug() { +void DetectNACKbug(void) { uint8_t mf_auth[] = {0x60, 0x00, 0xF5, 0x7B}; uint8_t mf_nr_ar[] = {0, 0, 0, 0, 0, 0, 0, 0}; uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index e5523c598..fab0dbdeb 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -132,6 +132,7 @@ int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, bool send_chaining, void *data, u int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *p_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats); int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades); void iso14a_set_trigger(bool enable); +void iso14443a_off(void); int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen); int EmSend4bit(uint8_t resp); @@ -148,7 +149,9 @@ bool EmLogTrace(uint8_t *reader_data, uint16_t reader_len, uint32_t reader_Start uint8_t *tag_data, uint16_t tag_len, uint32_t tag_StartTime, uint32_t tag_EndTime, uint8_t *tag_Parity); void ReaderMifare(bool first_try, uint8_t block, uint8_t keytype); -void DetectNACKbug(); +void DetectNACKbug(void); + +bool GetIso14443aAnswerFromTag_Thinfilm(uint8_t *receivedResponse, uint8_t *received_len); #ifdef __cplusplus } diff --git a/armsrc/thinfilm.c b/armsrc/thinfilm.c new file mode 100644 index 000000000..96cd062c6 --- /dev/null +++ b/armsrc/thinfilm.c @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2019 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Routines to support a mangeled ISO 14443 type A for Thinfilm tags by Kovio +//----------------------------------------------------------------------------- + +#include "thinfilm.h" + +/** + * ref + * https://www.thinfilmnfc.com/wp-content/uploads/2017/09/Thinfilm-Kovio-NFC-Barcode-Protocol-Tag-Functional-Specification-v3.4-2017-05-26.pdf + * https://developer.android.com/reference/android/nfc/tech/NfcBarcode + * + */ + +void ReadThinFilm(void) { + + clear_trace(); + + set_tracing(true); + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + uint8_t len = 0; + uint8_t buf[36] = {0x00}; + + // power on and listen for answer. + bool status = GetIso14443aAnswerFromTag_Thinfilm(buf, &len); + + // lsb -> msb + for (uint8_t i = 0; i < len; i++) { + uint8_t b = buf[i]; + buf[i] = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16; + } + // Startbit is always set and used in byte + buf[0] |= 0x80; + + reply_ng(CMD_THINFILM_READ, status ? PM3_SUCCESS : PM3_ENODATA, buf, len); + + iso14443a_off(); + set_tracing(false); +} + diff --git a/armsrc/thinfilm.h b/armsrc/thinfilm.h new file mode 100644 index 000000000..cfbfd03c1 --- /dev/null +++ b/armsrc/thinfilm.h @@ -0,0 +1,30 @@ +//----------------------------------------------------------------------------- +// Iceman - August 2019 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Routines to support a mangeled ISO 14443 type A for Thinfilm tags by Kovio +//----------------------------------------------------------------------------- + +#ifndef __THINFILM_H +#define __THINFILM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "pm3_cmd.h" +#include "cmd.h" +#include "apps.h" +//#include "util.h" +#include "iso14443a.h" + +void ReadThinFilm(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __ISO14443A_H */ diff --git a/client/Makefile b/client/Makefile index 440f2b888..b0544ced4 100644 --- a/client/Makefile +++ b/client/Makefile @@ -130,7 +130,7 @@ CORESRCS = uart_posix.c \ crc16.c \ comms.c -CMDSRCS = crapto1/crapto1.c \ +CMDSRCS = crapto1/crapto1.c \ crapto1/crypto1.c \ mifare/mfkey.c \ tea.c \ @@ -199,6 +199,7 @@ CMDSRCS = crapto1/crapto1.c \ cmdhftopaz.c \ cmdhffido.c \ cmdhffelica.c \ + cmdhfthinfilm.c \ cmdhw.c \ cmdlf.c \ cmdlfawid.c \ diff --git a/client/cmdhf.c b/client/cmdhf.c index 697aa9b62..2465641db 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -54,6 +54,11 @@ int CmdHFSearch(const char *Cmd) { PrintAndLogEx(INFO, "Checking for known tags...\n"); + if (infoThinFilm() == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("Thinfilm tag") " found\n"); + return 1; + } + if (infoHF14A(false, false) > 0) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("ISO14443-A tag") " found\n"); return 1; @@ -160,6 +165,7 @@ static command_t CommandTable[] = { {"mfdes", CmdHFMFDes, AlwaysAvailable, "{ MIFARE Desfire RFIDs... }"}, {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, + {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"}, {"tune", CmdHFTune, IfPm3Present, "Continuously measure HF antenna tuning"}, {"search", CmdHFSearch, AlwaysAvailable, "Search for known HF tags [preliminary]"}, diff --git a/client/cmdhf.h b/client/cmdhf.h index 4540caf3d..cd0ae5db5 100644 --- a/client/cmdhf.h +++ b/client/cmdhf.h @@ -31,6 +31,7 @@ #include "cmdhftopaz.h" // TOPAZ #include "cmdhffelica.h" // ISO18092 / FeliCa #include "cmdhffido.h" // FIDO authenticators +#include "cmdhfthinfilm.h" // Thinfilm #include "cmdtrace.h" // trace list int CmdHF(const char *Cmd); diff --git a/client/cmdhfthinfilm.c b/client/cmdhfthinfilm.c new file mode 100644 index 000000000..970ddfee5 --- /dev/null +++ b/client/cmdhfthinfilm.c @@ -0,0 +1,147 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2019 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Thinfilm commands +//----------------------------------------------------------------------------- +#include "cmdhfthinfilm.h" + +static int CmdHelp(const char *Cmd); + +static int usage_thinfilm_info(void) { + PrintAndLogEx(NORMAL, "Usage: hf thin info [h]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf thin info"); + return PM3_SUCCESS; +} + + +// Printing function based upon the code in libnfc +// ref +// https://github.com/nfc-tools/libnfc/blob/master/utils/nfc-barcode.c +static int print_barcode(uint8_t *barcode, const size_t barcode_len) { + + PrintAndLogEx(SUCCESS, " Manufacturer : "_YELLOW_("%s") "[0x%02X]", (barcode[0] == 0xB7) ? "Thinfilm" : "unknown", barcode[0] ); + PrintAndLogEx(SUCCESS, " Data format : "_YELLOW_("%02X"), barcode[1]); + PrintAndLogEx(SUCCESS, " Raw data : "_YELLOW_("%s"), sprint_hex(barcode, barcode_len) ); + + char s[45]; + memset(s, 0x00, sizeof(s)); + + switch (barcode[1]) { + case 0: + printf("Data Format Field: Reserved for allocation by tag manufacturer\n"); + return PM3_SUCCESS; + case 1: + snprintf(s, sizeof(s), "http://www." ); + break; + case 2: + snprintf(s, sizeof(s), "https://www."); + break; + case 3: + snprintf(s, sizeof(s), "http://"); + break; + case 4: + snprintf(s, sizeof(s), "https://"); + break; + case 5: + PrintAndLogEx(SUCCESS, "EPC: %s", sprint_hex(barcode + 2, 12) ); + return PM3_SUCCESS; + default: + PrintAndLogEx(SUCCESS, "Data Format Field: unknown (%02X)", barcode[1]); + PrintAndLogEx(SUCCESS, "Data:" _YELLOW_("%s"), sprint_hex(barcode + 2, barcode_len - 2) ); + return PM3_SUCCESS; + } + + snprintf(s + strlen(s), barcode_len - 1, (const char*)&barcode[2] , barcode_len - 2); + + for (uint8_t i = 0; i < strlen(s); i++) { + + // terminate string + if (s[i] == 0xFE) { + s[i] = 0; + break; + } + } + PrintAndLogEx(SUCCESS, " Decoded NFC URL : "_YELLOW_("%s"), s); + return PM3_SUCCESS; +} + + +static int CmdHfThinFilmInfo(const char *Cmd) { + + uint8_t cmdp = 0; + bool errors = false; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_thinfilm_info(); + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (errors) { + usage_thinfilm_info(); + return PM3_EINVARG; + } + + return infoThinFilm(); +} + +int infoThinFilm(void) { + + clearCommandBuffer(); + SendCommandNG(CMD_THINFILM_READ, NULL, 0); + + PacketResponseNG resp; + if (!WaitForResponseTimeout(CMD_THINFILM_READ, &resp, 1500)) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if ( resp.status == PM3_SUCCESS ) { + print_barcode( resp.data.asBytes, resp.length - 2); + } + + return resp.status; +} + +static int CmdHfThinFilmSim(const char *Cmd) { + PrintAndLogEx(INFO, "To be implemented"); + return PM3_ENOTIMPL; +} + +static int CmdHfThinFilmList(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdTraceList("14a"); + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"info", CmdHfThinFilmInfo, IfPm3Flash, "Tag information"}, + {"list", CmdHfThinFilmList, AlwaysAvailable, "List ISO 14443A / Thinfilm history - not correct"}, + {"sim", CmdHfThinFilmSim, IfPm3Flash, "Fake Thinfilm tag"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFThinfilm(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/cmdhfthinfilm.h b/client/cmdhfthinfilm.h new file mode 100644 index 000000000..f67084cbd --- /dev/null +++ b/client/cmdhfthinfilm.h @@ -0,0 +1,28 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2019 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Thinfilm commands +//----------------------------------------------------------------------------- + +#ifndef CMDHFTHINFILM_H__ +#define CMDHFTHINFILM_H__ + +#include +#include +#include +#include "common.h" +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "util.h" +#include "cmdhf.h" // list cmd + +int infoThinFilm(void); + +int CmdHFThinfilm(const char *Cmd); + +#endif diff --git a/include/pm3_cmd.h b/include/pm3_cmd.h index e8ff526d7..de4a622e4 100644 --- a/include/pm3_cmd.h +++ b/include/pm3_cmd.h @@ -494,6 +494,9 @@ typedef struct { #define CMD_EMV_LOAD_VALUE 0x0707 #define CMD_EMV_DUMP_CARD 0x0708 +// For ThinFilm Kovio +#define CMD_THINFILM_READ 0x0810 + #define CMD_UNKNOWN 0xFFFF //Mifare simulation flags