From: grauerfuchs <42082416+grauerfuchs@users.noreply.github.com> Date: Wed, 22 Aug 2018 22:57:48 +0000 (-0400) Subject: Merge pull request #9 from Proxmark/master X-Git-Tag: v3.1.0~14^2~8 X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/commitdiff_plain/18a3bf6119630ad15968c252a4f69033ebd2f503?hp=1ee624fe6a48d83544df3ce2078e127390551e15 Merge pull request #9 from Proxmark/master Pulling in most recent changes in official code tree --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 13fc97fb..da6463e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,12 +17,14 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf 14a reader` to just reqest-anticilission-select sequence (Merlok) - Changed `hf 14a raw` - works with LED's and some exchange logic (Merlok) - Changed TLV parser messages to more convenient (Merlok) +- Rewritten Legic Prime reader (`hf legic reader`, `write` and `fill`) - it is using xcorrelation now (AntiCat) ### Fixed - Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok) - Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi) ### Added +- Added `sc` smartcard (contact card) commands - reader, info, raw, upgrade, setclock, list (hardware version RDV4.0 only) must turn option on in makefile options (Willok, Iceman, marshmellow) - Added a bitbang mode to `lf cmdread` if delay is 0 the cmd bits turn off and on the antenna with 0 and 1 respectively (marshmellow) - Added PAC/Stanley detection to lf search (marshmellow) - Added lf pac demod and lf pac read - extracts the raw blocks from a PAC/Stanley tag (marshmellow) diff --git a/armsrc/Makefile b/armsrc/Makefile index f0a0c0ff..d4b13c6b 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -15,18 +15,22 @@ APP_CFLAGS = -DON_DEVICE \ include ../common/Makefile_Enabled_Options.common -ifneq (,$(findstring LCD,$(APP_CFLAGS))) +ifneq (,$(findstring WITH_LCD,$(APP_CFLAGS))) SRC_LCD = fonts.c LCD.c else SRC_LCD = endif -#SRC_LCD = fonts.c LCD.c SRC_LF = lfops.c hitag2.c hitagS.c lfsampling.c pcf7931.c lfdemod.c protocols.c SRC_ISO15693 = iso15693.c iso15693tools.c SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c mifaresim.c SRC_ISO14443b = iso14443b.c SRC_CRAPTO1 = crypto1.c des.c SRC_CRC = iso14443crc.c crc.c crc16.c crc32.c parity.c +ifneq (,$(findstring WITH_SMARTCARD,$(APP_CFLAGS))) + SRC_SMARTCARD = i2c.c +else + SRC_SMARTCARD = +endif #the FPGA bitstream files. Note: order matters! FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit @@ -44,6 +48,7 @@ THUMBSRC = start.c \ $(SRC_ISO15693) \ $(SRC_LF) \ $(SRC_ZLIB) \ + $(SRC_SMARTCARD) \ appmain.c \ printf.c \ util.c \ diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 27f43b3f..4034788a 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -30,6 +30,10 @@ #ifdef WITH_LCD #include "LCD.h" #endif +#ifdef WITH_SMARTCARD + #include "i2c.h" +#endif + // Craig Young - 14a stand-alone code #ifdef WITH_ISO14443a @@ -357,12 +361,15 @@ void SendStatus(void) { BigBuf_print_status(); Fpga_print_status(); +#ifdef WITH_SMARTCARD + I2C_print_status(); +#endif printConfig(); //LF Sampling config printUSBSpeed(); Dbprintf("Various"); - Dbprintf(" MF_DBGLEVEL......%d", MF_DBGLEVEL); - Dbprintf(" ToSendMax........%d",ToSendMax); - Dbprintf(" ToSendBit........%d",ToSendBit); + Dbprintf(" MF_DBGLEVEL........%d", MF_DBGLEVEL); + Dbprintf(" ToSendMax..........%d", ToSendMax); + Dbprintf(" ToSendBit..........%d", ToSendBit); cmd_send(CMD_ACK,1,0,0,0,0); } @@ -1253,6 +1260,31 @@ void UsbPacketReceived(uint8_t *packet, int len) HfSnoop(c->arg[0], c->arg[1]); break; #endif +#ifdef WITH_SMARTCARD + case CMD_SMART_ATR: { + SmartCardAtr(); + break; + } + case CMD_SMART_SETCLOCK:{ + SmartCardSetClock(c->arg[0]); + break; + } + case CMD_SMART_RAW: { + SmartCardRaw(c->arg[0], c->arg[1], c->d.asBytes); + break; + } + case CMD_SMART_UPLOAD: { + // upload file from client + uint8_t *mem = BigBuf_get_addr(); + memcpy( mem + c->arg[0], c->d.asBytes, USB_CMD_DATA_SIZE); + cmd_send(CMD_ACK,1,0,0,0,0); + break; + } + case CMD_SMART_UPGRADE: { + SmartCardUpgrade(c->arg[0]); + break; + } +#endif case CMD_BUFF_CLEAR: BigBuf_Clear(); diff --git a/armsrc/i2c.c b/armsrc/i2c.c new file mode 100644 index 00000000..721b4b2e --- /dev/null +++ b/armsrc/i2c.c @@ -0,0 +1,720 @@ +//----------------------------------------------------------------------------- +// Willok, June 2018 +// Edits by Iceman, July 2018 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// The main i2c code, for communications with smart card module +//----------------------------------------------------------------------------- +#include "i2c.h" +#include "mifareutil.h" //for mf_dbglevel +#include "string.h" //for memset memcmp + +// ¶¨ÒåÁ¬½ÓÒý½Å +#define GPIO_RST AT91C_PIO_PA1 +#define GPIO_SCL AT91C_PIO_PA5 +#define GPIO_SDA AT91C_PIO_PA7 + +#define SCL_H HIGH(GPIO_SCL) +#define SCL_L LOW(GPIO_SCL) +#define SDA_H HIGH(GPIO_SDA) +#define SDA_L LOW(GPIO_SDA) + +#define SCL_read (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SCL) +#define SDA_read (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SDA) + +#define I2C_ERROR "I2C_WaitAck Error" + +volatile unsigned long c; + +// Ö±½ÓʹÓÃÑ­»·À´ÑÓʱ£¬Ò»¸öÑ­»· 6 ÌõÖ¸Á48M£¬ Delay=1 ´ó¸ÅΪ 200kbps +// timer. +// I2CSpinDelayClk(4) = 12.31us +// I2CSpinDelayClk(1) = 3.07us +void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) { + for (c = delay * 2; c; c--) {}; +} + +// ͨѶÑÓ³Ùº¯Êý communication delay function +#define I2C_DELAY_1CLK I2CSpinDelayClk(1) +#define I2C_DELAY_2CLK I2CSpinDelayClk(2) +#define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x)) + + +#define ISO7618_MAX_FRAME 255 + +void I2C_init(void) { + // ÅäÖø´Î»Òý½Å£¬¹Ø±ÕÉÏÀ­£¬ÍÆÍìÊä³ö£¬Ä¬ÈÏ¸ß + // Configure reset pin, close up pull up, push-pull output, default high + AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST; + AT91C_BASE_PIOA->PIO_MDDR = GPIO_RST; + + // ÅäÖà I2C Òý½Å£¬¿ªÆôÉÏÀ­£¬¿ªÂ©Êä³ö + // Configure I2C pin, open up, open leakage + AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_SCL | GPIO_SDA); // ´ò¿ªÉÏÀ­ Open up the pull up + AT91C_BASE_PIOA->PIO_MDER |= (GPIO_SCL | GPIO_SDA); + + // ĬÈÏÈý¸ùÏßÈ«²¿À­¸ß + // default three lines all pull up + AT91C_BASE_PIOA->PIO_SODR |= (GPIO_SCL | GPIO_SDA | GPIO_RST); + + // ÔÊÐíÊä³ö + // allow output + AT91C_BASE_PIOA->PIO_OER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); + AT91C_BASE_PIOA->PIO_PER |= (GPIO_SCL | GPIO_SDA | GPIO_RST); +} + + +// ÉèÖø´Î»×´Ì¬ +// set the reset state +void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) { + if (LineRST) + HIGH(GPIO_RST); + else + LOW(GPIO_RST); + + if (LineSCK) + HIGH(GPIO_SCL); + else + LOW(GPIO_SCL); + + if (LineSDA) + HIGH(GPIO_SDA); + else + LOW(GPIO_SDA); +} + +// ¸´Î»½øÈëÖ÷³ÌÐò +// Reset the SIM_Adapter, then enter the main program +// Note: the SIM_Adapter will not enter the main program after power up. Please run this function before use SIM_Adapter. +void I2C_Reset_EnterMainProgram(void) { + I2C_SetResetStatus(0, 0, 0); // À­µÍ¸´Î»Ïß + SpinDelay(30); + I2C_SetResetStatus(1, 0, 0); // ½â³ý¸´Î» + SpinDelay(30); + I2C_SetResetStatus(1, 1, 1); // À­¸ßÊý¾ÝÏß + SpinDelay(10); +} + +// ¸´Î»½øÈëÒýµ¼Ä£Ê½ +// Reset the SIM_Adapter, then enter the bootloader program +// Reserve£ºFor firmware update. +void I2C_Reset_EnterBootloader(void) { + I2C_SetResetStatus(0, 1, 1); // À­µÍ¸´Î»Ïß + SpinDelay(100); + I2C_SetResetStatus(1, 1, 1); // ½â³ý¸´Î» + SpinDelay(10); +} + +// µÈ´ýʱÖÓ±ä¸ß +// Wait for the clock to go High. +bool WaitSCL_H_delay(uint32_t delay) { + while (delay--) { + if (SCL_read) { + return true; + } + I2C_DELAY_1CLK; + } + return false; +} + +// 5000 * 3.07us = 15350us. 15.35ms +bool WaitSCL_H(void) { + return WaitSCL_H_delay(5000); +} + +// Wait max 300ms or until SCL goes LOW. +// Which ever comes first +bool WaitSCL_L_300ms(void) { + volatile uint16_t delay = 300; + while ( delay-- ) { + // exit on SCL LOW + if (!SCL_read) + return true; + + SpinDelay(1); + } + return (delay == 0); +} + +bool I2C_Start(void) { + + I2C_DELAY_XCLK(4); + SDA_H; I2C_DELAY_1CLK; + SCL_H; + if (!WaitSCL_H()) return false; + + I2C_DELAY_2CLK; + + if (!SCL_read) return false; + if (!SDA_read) return false; + + SDA_L; I2C_DELAY_2CLK; + return true; +} + +bool I2C_WaitForSim() { + // variable delay here. + if (!WaitSCL_L_300ms()) + return false; + + // 8051 speaks with smart card. + // 1000*50*3.07 = 153.5ms + // 1byte transfer == 1ms + if (!WaitSCL_H_delay(2000*50) ) + return false; + + return true; +} + +// send i2c STOP +void I2C_Stop(void) { + SCL_L; I2C_DELAY_2CLK; + SDA_L; I2C_DELAY_2CLK; + SCL_H; I2C_DELAY_2CLK; + if (!WaitSCL_H()) return; + SDA_H; + I2C_DELAY_XCLK(8); +} + +// Send i2c ACK +void I2C_Ack(void) { + SCL_L; I2C_DELAY_2CLK; + SDA_L; I2C_DELAY_2CLK; + SCL_H; I2C_DELAY_2CLK; + SCL_L; I2C_DELAY_2CLK; +} + +// Send i2c NACK +void I2C_NoAck(void) { + SCL_L; I2C_DELAY_2CLK; + SDA_H; I2C_DELAY_2CLK; + SCL_H; I2C_DELAY_2CLK; + SCL_L; I2C_DELAY_2CLK; +} + +bool I2C_WaitAck(void) { + SCL_L; I2C_DELAY_1CLK; + SDA_H; I2C_DELAY_1CLK; + SCL_H; + if (!WaitSCL_H()) + return false; + + I2C_DELAY_2CLK; + if (SDA_read) { + SCL_L; + return false; + } + SCL_L; + return true; +} + +void I2C_SendByte(uint8_t data) { + uint8_t i = 8; + + while (i--) { + SCL_L; I2C_DELAY_1CLK; + + if (data & 0x80) + SDA_H; + else + SDA_L; + + data <<= 1; + I2C_DELAY_1CLK; + + SCL_H; + if (!WaitSCL_H()) + return; + + I2C_DELAY_2CLK; + } + SCL_L; +} + +uint8_t I2C_ReadByte(void) { + uint8_t i = 8, b = 0; + + SDA_H; + while (i--) { + b <<= 1; + SCL_L; I2C_DELAY_2CLK; + SCL_H; + if (!WaitSCL_H()) + return 0; + + I2C_DELAY_2CLK; + if (SDA_read) + b |= 0x01; + } + SCL_L; + return b; +} + +// Sends one byte ( command to be written, SlaveDevice address) +bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) { + bool bBreak = true; + do { + if (!I2C_Start()) + return false; + //[C0] + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +// дÈë1×Ö½ÚÊý¾Ý £¨´ýдÈëÊý¾Ý£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£© +// Sends 1 byte data (Data to be written, command to be written , SlaveDevice address ). +bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) { + bool bBreak = true; + do { + if (!I2C_Start()) + return false; + + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(data); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +// дÈë1´®Êý¾Ý£¨´ýдÈëÊý×éµØÖ·£¬´ýдÈ볤¶È£¬´ýдÈëµØÖ·£¬Æ÷¼þÀàÐÍ£© +//Sends a string of data (Array, length, command to be written , SlaveDevice address ). +// len = uint8 (max buffer to write 256bytes) +bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { + bool bBreak = true; + do { + if (!I2C_Start()) + return false; + + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + while (len) { + + I2C_SendByte(*data); + if (!I2C_WaitAck()) + break; + + len--; + data++; + } + + if (len == 0) + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +// ¶Á³ö1´®Êý¾Ý£¨´æ·Å¶Á³öÊý¾Ý£¬´ý¶Á³ö³¤¶È£¬´ø¶Á³öµØÖ·£¬Æ÷¼þÀàÐÍ£© +// read 1 strings of data (Data array, Readout length, command to be written , SlaveDevice address ). +// len = uint8 (max buffer to read 256bytes) +uint8_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) { + + if ( !data || len == 0 ) + return 0; + + // extra wait 500us (514us measured) + // 200us (xx measured) + SpinDelayUs(200); + bool bBreak = true; + uint8_t readcount = 0; + + do { + if (!I2C_Start()) + return 0; + + // 0xB0 / 0xC0 == i2c write + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + I2C_SendByte(device_cmd); + if (!I2C_WaitAck()) + break; + + // 0xB1 / 0xC1 == i2c read + I2C_Start(); + I2C_SendByte(device_address | 1); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + if (bBreak) { + I2C_Stop(); + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return 0; + } + + // reading + while (len) { + + *data = I2C_ReadByte(); + + len--; + + // ¶ÁÈ¡µÄµÚÒ»¸ö×Ö½ÚΪºóÐø³¤¶È + // The first byte in response is the message length + if (!readcount && (len > *data)) { + len = *data; + } else { + data++; + } + readcount++; + + // acknowledgements. After last byte send NACK. + if (len == 0) + I2C_NoAck(); + else + I2C_Ack(); + } + + I2C_Stop(); + // return bytecount - first byte (which is length byte) + return (readcount) ? --readcount : 0; +} + +uint8_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { + //START, 0xB0, 0x00, 0x00, START, 0xB1, xx, yy, zz, ......, STOP + bool bBreak = true; + uint8_t readcount = 0; + + // sending + do { + if (!I2C_Start()) + return 0; + + // 0xB0 / 0xC0 i2c write + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + // msb + I2C_SendByte(msb); + if (!I2C_WaitAck()) + break; + + // lsb + I2C_SendByte(lsb); + if (!I2C_WaitAck()) + break; + + // 0xB1 / 0xC1 i2c read + I2C_Start(); + I2C_SendByte(device_address | 1); + if (!I2C_WaitAck()) + break; + + bBreak = false; + } while (false); + + if (bBreak) { + I2C_Stop(); + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return 0; + } + + // reading + while (len) { + *data = I2C_ReadByte(); + + data++; + readcount++; + len--; + + // acknowledgements. After last byte send NACK. + if (len == 0) + I2C_NoAck(); + else + I2C_Ack(); + } + + I2C_Stop(); + return readcount; +} + +bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) { + //START, 0xB0, 0x00, 0x00, xx, yy, zz, ......, STOP + bool bBreak = true; + + do { + if (!I2C_Start()) + return false; + + // 0xB0 == i2c write + I2C_SendByte(device_address & 0xFE); + if (!I2C_WaitAck()) + break; + + // msb + I2C_SendByte(msb); + if (!I2C_WaitAck()) + break; + + // lsb + I2C_SendByte(lsb); + if (!I2C_WaitAck()) + break; + + while (len) { + I2C_SendByte(*data); + if (!I2C_WaitAck()) + break; + + len--; + data++; + } + + if (len == 0) + bBreak = false; + } while (false); + + I2C_Stop(); + if (bBreak) { + if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR); + return false; + } + return true; +} + +void I2C_print_status(void) { + DbpString("Smart card module (ISO 7816)"); + uint8_t resp[] = {0,0,0,0}; + I2C_init(); + I2C_Reset_EnterMainProgram(); + uint8_t len = I2C_BufferRead(resp, sizeof(resp), I2C_DEVICE_CMD_GETVERSION, I2C_DEVICE_ADDRESS_MAIN); + if ( len > 0 ) + Dbprintf(" version.................v%x.%02x", resp[0], resp[1]); + else + DbpString(" version.................FAILED"); +} + +bool GetATR(smart_card_atr_t *card_ptr) { + + // clear + if ( card_ptr ) { + card_ptr->atr_len = 0; + memset(card_ptr->atr, 0, sizeof(card_ptr->atr)); + } + + // Send ATR + // start [C0 01] stop start C1 len aa bb cc stop] + I2C_WriteCmd(I2C_DEVICE_CMD_GENERATE_ATR, I2C_DEVICE_ADDRESS_MAIN); + uint8_t cmd[1] = {1}; + LogTrace(cmd, 1, 0, 0, NULL, true); + + //wait for sim card to answer. + if (!I2C_WaitForSim()) + return false; + + // read answer + uint8_t len = I2C_BufferRead(card_ptr->atr, sizeof(card_ptr->atr), I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); + + if ( len == 0 ) + return false; + + // for some reason we only get first byte of atr, if that is so, send dummy command to retrieve the rest of the atr + if (len == 1) { + + uint8_t data[1] = {0}; + I2C_BufferWrite(data, len, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN); + + if ( !I2C_WaitForSim() ) + return false; + + uint8_t len2 = I2C_BufferRead(card_ptr->atr + len, sizeof(card_ptr->atr) - len, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); + len = len + len2; + } + + if ( card_ptr ) { + card_ptr->atr_len = len; + LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false); + } + + return true; +} + +void SmartCardAtr(void) { + smart_card_atr_t card; + LED_D_ON(); + clear_trace(); + set_tracing(true); + I2C_init(); + I2C_Reset_EnterMainProgram(); + bool isOK = GetATR( &card ); + cmd_send(CMD_ACK, isOK, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); + set_tracing(false); + LEDsoff(); +} + +void SmartCardRaw( uint64_t arg0, uint64_t arg1, uint8_t *data ) { + + LED_D_ON(); + + uint8_t len = 0; + uint8_t *resp = BigBuf_malloc(ISO7618_MAX_FRAME); + smartcard_command_t flags = arg0; + + if ((flags & SC_CONNECT)) + clear_trace(); + + set_tracing(true); + + if ((flags & SC_CONNECT)) { + + I2C_init(); + I2C_Reset_EnterMainProgram(); + + if ( !(flags & SC_NO_SELECT) ) { + smart_card_atr_t card; + bool gotATR = GetATR( &card ); + //cmd_send(CMD_ACK, gotATR, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t)); + if ( !gotATR ) + goto OUT; + } + } + + if ((flags & SC_RAW)) { + + LogTrace(data, arg1, 0, 0, NULL, true); + + // Send raw bytes + // asBytes = A0 A4 00 00 02 + // arg1 = len 5 + I2C_BufferWrite(data, arg1, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN); + + if ( !I2C_WaitForSim() ) + goto OUT; + + len = I2C_BufferRead(resp, ISO7618_MAX_FRAME, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN); + LogTrace(resp, len, 0, 0, NULL, false); + } +OUT: + cmd_send(CMD_ACK, len, 0, 0, resp, len); + set_tracing(false); + LEDsoff(); +} + +void SmartCardUpgrade(uint64_t arg0) { + + LED_C_ON(); + + #define I2C_BLOCK_SIZE 128 + // write. Sector0, with 11,22,33,44 + // erase is 128bytes, and takes 50ms to execute + + I2C_init(); + I2C_Reset_EnterBootloader(); + + bool isOK = true; + uint8_t res = 0; + uint16_t length = arg0; + uint16_t pos = 0; + uint8_t *fwdata = BigBuf_get_addr(); + uint8_t *verfiydata = BigBuf_malloc(I2C_BLOCK_SIZE); + + while (length) { + + uint8_t msb = (pos >> 8) & 0xFF; + uint8_t lsb = pos & 0xFF; + + Dbprintf("FW %02X%02X", msb, lsb); + + size_t size = MIN(I2C_BLOCK_SIZE, length); + + // write + res = I2C_WriteFW(fwdata+pos, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT); + if ( !res ) { + DbpString("Writing failed"); + isOK = false; + break; + } + + // writing takes time. + SpinDelay(50); + + // read + res = I2C_ReadFW(verfiydata, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT); + if ( res == 0) { + DbpString("Reading back failed"); + isOK = false; + break; + } + + // cmp + if ( 0 != memcmp(fwdata+pos, verfiydata, size)) { + DbpString("not equal data"); + isOK = false; + break; + } + + length -= size; + pos += size; + } + cmd_send(CMD_ACK, isOK, pos, 0, 0, 0); + LED_C_OFF(); +} + +// unfinished (or not needed?) +//void SmartCardSetBaud(uint64_t arg0) { +//} + +void SmartCardSetClock(uint64_t arg0) { + LED_D_ON(); + set_tracing(true); + I2C_init(); + I2C_Reset_EnterMainProgram(); + + // Send SIM CLC + // start [C0 05 xx] stop + I2C_WriteByte(arg0, I2C_DEVICE_CMD_SIM_CLC, I2C_DEVICE_ADDRESS_MAIN); + + cmd_send(CMD_ACK, 1, 0, 0, 0, 0); + set_tracing(false); + LEDsoff(); +} diff --git a/armsrc/i2c.h b/armsrc/i2c.h new file mode 100644 index 00000000..4c5c5228 --- /dev/null +++ b/armsrc/i2c.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Willok, June 2018 +// Edits by Iceman, July 2018 +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// The main i2c code, for communications with smart card module +//----------------------------------------------------------------------------- +#ifndef __I2C_H +#define __I2C_H + +#include +#include "proxmark3.h" +#include "apps.h" +#include "util.h" +#include "BigBuf.h" +#include "smartcard.h" + +#define I2C_DEVICE_ADDRESS_BOOT 0xB0 +#define I2C_DEVICE_ADDRESS_MAIN 0xC0 + +#define I2C_DEVICE_CMD_GENERATE_ATR 0x01 +#define I2C_DEVICE_CMD_SEND 0x02 +#define I2C_DEVICE_CMD_READ 0x03 +#define I2C_DEVICE_CMD_SETBAUD 0x04 +#define I2C_DEVICE_CMD_SIM_CLC 0x05 +#define I2C_DEVICE_CMD_GETVERSION 0x06 + + +void I2C_init(void); +void I2C_Reset(void); +void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA); + +void I2C_Reset_EnterMainProgram(void); +void I2C_Reset_EnterBootloader(void); + +bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address); + +bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address); +bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address); +uint8_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address); + +// for firmware +uint8_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); +bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address); + +bool GetATR(smart_card_atr_t *card_ptr); + +// generic functions +void SmartCardAtr(void); +void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data); +void SmartCardUpgrade(uint64_t arg0); +//void SmartCardSetBaud(uint64_t arg0); +void SmartCardSetClock(uint64_t arg0); +void I2C_print_status(void); +#endif diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index 27dcc297..2a236b6f 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -1,5 +1,7 @@ //----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz +// 2016 Iceman +// 2018 AntiCat (rwd rewritten) // // 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 @@ -15,6 +17,7 @@ #include "legicrf.h" #include "legic_prng.h" +#include "legic.h" #include "crc.h" static struct legic_frame { @@ -40,6 +43,460 @@ static int legic_reqresp_drift; AT91PS_TC timer; AT91PS_TC prng_timer; +static legic_card_select_t card;/* metadata of currently selected card */ + +//----------------------------------------------------------------------------- +// Frame timing and pseudorandom number generator +// +// The Prng is forwarded every 100us (TAG_BIT_PERIOD), except when the reader is +// transmitting. In that case the prng has to be forwarded every bit transmitted: +// - 60us for a 0 (RWD_TIME_0) +// - 100us for a 1 (RWD_TIME_1) +// +// The data dependent timing makes writing comprehensible code significantly +// harder. The current aproach forwards the prng data based if there is data on +// air and time based, using GET_TICKS, during computational and wait periodes. +// +// To not have the necessity to calculate/guess exection time dependend timeouts +// tx_frame and rx_frame use a shared timestamp to coordinate tx and rx timeslots. +//----------------------------------------------------------------------------- + +static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ + +#define RWD_TIME_PAUSE 30 /* 20us */ +#define RWD_TIME_1 150 /* READER_TIME_PAUSE 20us off + 80us on = 100us */ +#define RWD_TIME_0 90 /* READER_TIME_PAUSE 20us off + 40us on = 60us */ +#define RWD_FRAME_WAIT 330 /* 220us from TAG frame end to READER frame start */ +#define TAG_FRAME_WAIT 495 /* 330us from READER frame end to TAG frame start */ +#define TAG_BIT_PERIOD 150 /* 100us */ +#define TAG_WRITE_TIMEOUT 60 /* 40 * 100us (write should take at most 3.6ms) */ + +#define SIM_DIVISOR 586 /* prng_time/DIV count prng needs to be forwared */ +#define SIM_SHIFT 900 /* prng_time+SHIFT shift of delayed start */ +#define RWD_TIME_FUZZ 20 /* rather generous 13us, since the peak detector + /+ hysteresis fuzz quite a bit */ + +#define LEGIC_READ 0x01 /* Read Command */ +#define LEGIC_WRITE 0x00 /* Write Command */ + +#define SESSION_IV 0x55 /* An arbitrary chose session IV, all shoud work */ +#define OFFSET_LOG 1024 /* The largest Legic Prime card is 1k */ +#define WRITE_LOWERLIMIT 4 /* UID and MCC are not writable */ + +#define INPUT_THRESHOLD 8 /* heuristically determined, lower values */ + /* lead to detecting false ack during write */ + +#define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz))) + +//----------------------------------------------------------------------------- +// I/O interface abstraction (FPGA -> ARM) +//----------------------------------------------------------------------------- + +static inline uint8_t rx_byte_from_fpga() { + for(;;) { + WDT_HIT(); + + // wait for byte be become available in rx holding register + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + return AT91C_BASE_SSC->SSC_RHR; + } + } +} + +//----------------------------------------------------------------------------- +// Demodulation (Reader) +//----------------------------------------------------------------------------- + +// Returns a demedulated bit +// +// The FPGA running xcorrelation samples the subcarrier at ~13.56 MHz. The mode +// was initialy designed to receive BSPK/2-PSK. Hance, it reports an I/Q pair +// every 4.7us (8 bits i and 8 bits q). +// +// The subcarrier amplitude can be calculated using Pythagoras sqrt(i^2 + q^2). +// To reduce CPU time the amplitude is approximated by using linear functions: +// am = MAX(ABS(i),ABS(q)) + 1/2*MIN(ABS(i),ABSq)) +// +// Note: The SSC receiver is never synchronized the calculation my be performed +// on a I/Q pair from two subsequent correlations, but does not matter. +// +// The bit time is 99.1us (21 I/Q pairs). The receiver skips the first 5 samples +// and averages the next (most stable) 8 samples. The final 8 samples are dropped +// also. +// +// The demedulated should be alligned to the bit periode by the caller. This is +// done in rx_bit_as_reader and rx_ack_as_reader. +static inline bool rx_bit_as_reader() { + int32_t cq = 0; + int32_t ci = 0; + + // skip first 5 I/Q pairs + for(size_t i = 0; i<5; ++i) { + (int8_t)rx_byte_from_fpga(); + (int8_t)rx_byte_from_fpga(); + } + + // sample next 8 I/Q pairs + for(size_t i = 0; i<8; ++i) { + cq += (int8_t)rx_byte_from_fpga(); + ci += (int8_t)rx_byte_from_fpga(); + } + + // calculate power + int32_t power = (MAX(ABS(ci), ABS(cq)) + (MIN(ABS(ci), ABS(cq)) >> 1)); + + // compare average (power / 8) to threshold + return ((power >> 3) > INPUT_THRESHOLD); +} + +//----------------------------------------------------------------------------- +// Modulation (Reader) +// +// I've tried to modulate the Legic specific pause-puls using ssc and the default +// ssc clock of 105.4 kHz (bit periode of 9.4us) - previous commit. However, +// the timing was not precise enough. By increasing the ssc clock this could +// be circumvented, but the adventage over bitbang would be little. +//----------------------------------------------------------------------------- + +static inline void tx_bit_as_reader(bool bit) { + // insert pause + LOW(GPIO_SSC_DOUT); + last_frame_end += RWD_TIME_PAUSE; + while(GET_TICKS < last_frame_end) { }; + HIGH(GPIO_SSC_DOUT); + + // return to high, wait for bit periode to end + last_frame_end += (bit ? RWD_TIME_1 : RWD_TIME_0) - RWD_TIME_PAUSE; + while(GET_TICKS < last_frame_end) { }; +} + +//----------------------------------------------------------------------------- +// Frame Handling (Reader) +// +// The LEGIC RF protocol from card to reader does not include explicit frame +// start/stop information or length information. The reader must know beforehand +// how many bits it wants to receive. +// Notably: a card sending a stream of 0-bits is indistinguishable from no card +// present. +//----------------------------------------------------------------------------- + +static void tx_frame_as_reader(uint32_t frame, uint8_t len) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + + // wait for next tx timeslot + last_frame_end += RWD_FRAME_WAIT; + while(GET_TICKS < last_frame_end) { }; + + // transmit frame, MSB first + for(uint8_t i = 0; i < len; ++i) { + bool bit = (frame >> i) & 0x01; + tx_bit_as_reader(bit ^ legic_prng_get_bit()); + legic_prng_forward(1); + }; + + // add pause to mark end of the frame + LOW(GPIO_SSC_DOUT); + last_frame_end += RWD_TIME_PAUSE; + while(GET_TICKS < last_frame_end) { }; + HIGH(GPIO_SSC_DOUT); +} + +static uint32_t rx_frame_as_reader(uint8_t len) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR + | FPGA_HF_READER_RX_XCORR_848_KHZ + | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + + // hold sampling until card is expected to respond + last_frame_end += TAG_FRAME_WAIT; + while(GET_TICKS < last_frame_end) { }; + + uint32_t frame = 0; + for(uint8_t i = 0; i < len; i++) { + frame |= (rx_bit_as_reader() ^ legic_prng_get_bit()) << i; + legic_prng_forward(1); + + // rx_bit_as_reader runs only 95us, resync to TAG_BIT_PERIOD + last_frame_end += TAG_BIT_PERIOD; + while(GET_TICKS < last_frame_end) { }; + } + + return frame; +} + +static bool rx_ack_as_reader() { + // change fpga into rx mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR + | FPGA_HF_READER_RX_XCORR_848_KHZ + | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + + // hold sampling until card is expected to respond + last_frame_end += TAG_FRAME_WAIT; + while(GET_TICKS < last_frame_end) { }; + + uint32_t ack = 0; + for(uint8_t i = 0; i < TAG_WRITE_TIMEOUT; ++i) { + // sample bit + ack = rx_bit_as_reader(); + legic_prng_forward(1); + + // rx_bit_as_reader runs only 95us, resync to TAG_BIT_PERIOD + last_frame_end += TAG_BIT_PERIOD; + while(GET_TICKS < last_frame_end) { }; + + // check if it was an ACK + if(ack) { + break; + } + } + + return ack; +} + +//----------------------------------------------------------------------------- +// Legic Reader +//----------------------------------------------------------------------------- + +int init_card(uint8_t cardtype, legic_card_select_t *p_card) { + p_card->tagtype = cardtype; + + switch(p_card->tagtype) { + case 0x0d: + p_card->cmdsize = 6; + p_card->addrsize = 5; + p_card->cardsize = 22; + break; + case 0x1d: + p_card->cmdsize = 9; + p_card->addrsize = 8; + p_card->cardsize = 256; + break; + case 0x3d: + p_card->cmdsize = 11; + p_card->addrsize = 10; + p_card->cardsize = 1024; + break; + default: + p_card->cmdsize = 0; + p_card->addrsize = 0; + p_card->cardsize = 0; + return 2; + } + return 0; +} + +static void init_reader(bool clear_mem) { + // configure FPGA + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR + | FPGA_HF_READER_RX_XCORR_848_KHZ + | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + LED_D_ON(); + + // configure SSC with defaults + FpgaSetupSsc(); + + // re-claim GPIO_SSC_DOUT as GPIO and enable output + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + HIGH(GPIO_SSC_DOUT); + + // init crc calculator + crc_init(&legic_crc, 4, 0x19 >> 1, 0x05, 0); + + // start us timer + StartTicks(); +} + +// Setup reader to card connection +// +// The setup consists of a three way handshake: +// - Transmit initialisation vector 7 bits +// - Receive card type 6 bits +// - Acknowledge frame 6 bits +static uint32_t setup_phase_reader(uint8_t iv) { + // init coordination timestamp + last_frame_end = GET_TICKS; + + // Switch on carrier and let the card charge for 5ms. + last_frame_end += 7500; + while(GET_TICKS < last_frame_end) { }; + + legic_prng_init(0); + tx_frame_as_reader(iv, 7); + + // configure iv + legic_prng_init(iv); + legic_prng_forward(2); + + // receive card type + int32_t card_type = rx_frame_as_reader(6); + legic_prng_forward(3); + + // send obsfuscated acknowledgment frame + switch (card_type) { + case 0x0D: + tx_frame_as_reader(0x19, 6); // MIM22 | READCMD = 0x18 | 0x01 + break; + case 0x1D: + case 0x3D: + tx_frame_as_reader(0x39, 6); // MIM256 | READCMD = 0x38 | 0x01 + break; + } + + return card_type; +} + +static uint8_t calc_crc4(uint16_t cmd, uint8_t cmd_sz, uint8_t value) { + crc_clear(&legic_crc); + crc_update(&legic_crc, (value << cmd_sz) | cmd, 8 + cmd_sz); + return crc_finish(&legic_crc); +} + +static int16_t read_byte(uint16_t index, uint8_t cmd_sz) { + uint16_t cmd = (index << 1) | LEGIC_READ; + + // read one byte + LED_B_ON(); + legic_prng_forward(2); + tx_frame_as_reader(cmd, cmd_sz); + legic_prng_forward(2); + uint32_t frame = rx_frame_as_reader(12); + LED_B_OFF(); + + // split frame into data and crc + uint8_t byte = BYTEx(frame, 0); + uint8_t crc = BYTEx(frame, 1); + + // check received against calculated crc + uint8_t calc_crc = calc_crc4(cmd, cmd_sz, byte); + if(calc_crc != crc) { + Dbprintf("!!! crc mismatch: %x != %x !!!", calc_crc, crc); + return -1; + } + + legic_prng_forward(1); + + return byte; +} + +// Transmit write command, wait until (3.6ms) the tag sends back an unencrypted +// ACK ('1' bit) and forward the prng time based. +bool write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz) { + uint32_t cmd = index << 1 | LEGIC_WRITE; // prepare command + uint8_t crc = calc_crc4(cmd, addr_sz + 1, byte); // calculate crc + cmd |= byte << (addr_sz + 1); // append value + cmd |= (crc & 0xF) << (addr_sz + 1 + 8); // and crc + + // send write command + LED_C_ON(); + legic_prng_forward(2); + tx_frame_as_reader(cmd, addr_sz + 1 + 8 + 4); // sz = addr_sz + cmd + data + crc + legic_prng_forward(3); + LED_C_OFF(); + + // wait for ack + return rx_ack_as_reader(); +} + +//----------------------------------------------------------------------------- +// Command Line Interface +// +// Only this functions are public / called from appmain.c +//----------------------------------------------------------------------------- +void LegicRfReader(int offset, int bytes) { + uint8_t *BigBuf = BigBuf_get_addr(); + memset(BigBuf, 0, 1024); + + // configure ARM and FPGA + init_reader(false); + + // establish shared secret and detect card type + DbpString("Reading card ..."); + uint8_t card_type = setup_phase_reader(SESSION_IV); + if(init_card(card_type, &card) != 0) { + Dbprintf("No or unknown card found, aborting"); + goto OUT; + } + + // if no argument is specified create full dump + if(bytes == -1) { + bytes = card.cardsize; + } + + // do not read beyond card memory + if(bytes + offset > card.cardsize) { + bytes = card.cardsize - offset; + } + + for(uint16_t i = 0; i < bytes; ++i) { + int16_t byte = read_byte(offset + i, card.cmdsize); + if(byte == -1) { + Dbprintf("operation failed @ 0x%03.3x", bytes); + goto OUT; + } + BigBuf[i] = byte; + } + + // OK + Dbprintf("Card (MIM %i) read, use 'hf legic decode' or", card.cardsize); + Dbprintf("'data hexsamples %d' to view results", (bytes+7) & ~7); + +OUT: + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + StopTicks(); +} + +void LegicRfWriter(int bytes, int offset) { + uint8_t *BigBuf = BigBuf_get_addr(); + + // configure ARM and FPGA + init_reader(false); + + // uid is not writeable + if(offset <= WRITE_LOWERLIMIT) { + goto OUT; + } + + // establish shared secret and detect card type + Dbprintf("Writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes); + uint8_t card_type = setup_phase_reader(SESSION_IV); + if(init_card(card_type, &card) != 0) { + Dbprintf("No or unknown card found, aborting"); + goto OUT; + } + + // do not write beyond card memory + if(bytes + offset > card.cardsize) { + bytes = card.cardsize - offset; + } + + // write in reverse order, only then is DCF (decremental field) writable + while(bytes-- > 0 && !BUTTON_PRESS()) { + if(!write_byte(bytes + offset, BigBuf[bytes + offset], card.addrsize)) { + Dbprintf("operation failed @ 0x%03.3x", bytes); + goto OUT; + } + } + + // OK + DbpString("Write successful"); + +OUT: + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + StopTicks(); +} + +//----------------------------------------------------------------------------- +// Legic Simulator +//----------------------------------------------------------------------------- + static void setup_timer(void) { /* Set up Timer 1 to use for measuring time between pulses. Since we're bit-banging @@ -62,22 +519,6 @@ static void setup_timer(void) prng_timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; } -/* At TIMER_CLOCK3 (MCK/32) */ -#define RWD_TIME_1 150 /* RWD_TIME_PAUSE off, 80us on = 100us */ -#define RWD_TIME_0 90 /* RWD_TIME_PAUSE off, 40us on = 60us */ -#define RWD_TIME_PAUSE 30 /* 20us */ -#define RWD_TIME_FUZZ 20 /* rather generous 13us, since the peak detector + hysteresis fuzz quite a bit */ -#define TAG_TIME_BIT 150 /* 100us for every bit */ -#define TAG_TIME_WAIT 490 /* time from RWD frame end to tag frame start, experimentally determined */ - -#define SIM_DIVISOR 586 /* prng_time/SIM_DIVISOR count prng needs to be forwared */ -#define SIM_SHIFT 900 /* prng_time+SIM_SHIFT shift of delayed start */ - -#define SESSION_IV 0x55 -#define OFFSET_LOG 1024 - -#define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz))) - /* Generate Keystream */ static uint32_t get_key_stream(int skip, int count) { @@ -138,11 +579,11 @@ static void frame_send_tag(uint16_t response, int bits, int crypt) } /* Wait for the frame start */ - while(timer->TC_CV < (TAG_TIME_WAIT - 30)) ; + while(timer->TC_CV < (TAG_FRAME_WAIT - 30)) ; int i; for(i=0; iTC_CV + TAG_TIME_BIT; + int nextbit = timer->TC_CV + TAG_BIT_PERIOD; int bit = response & 1; response = response >> 1; if(bit) { @@ -155,126 +596,6 @@ static void frame_send_tag(uint16_t response, int bits, int crypt) AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; } -/* Send a frame in reader mode, the FPGA must have been set up by - * LegicRfReader - */ -static void frame_send_rwd(uint32_t data, int bits) -{ - /* Start clock */ - timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ - - int i; - for(i=0; iTC_CV; - int pause_end = starttime + RWD_TIME_PAUSE, bit_end; - int bit = data & 1; - data = data >> 1; - - if(bit ^ legic_prng_get_bit()) { - bit_end = starttime + RWD_TIME_1; - } else { - bit_end = starttime + RWD_TIME_0; - } - - /* RWD_TIME_PAUSE time off, then some time on, so that the complete bit time is - * RWD_TIME_x, where x is the bit to be transmitted */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - while(timer->TC_CV < pause_end) ; - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - legic_prng_forward(1); /* bit duration is longest. use this time to forward the lfsr */ - - while(timer->TC_CV < bit_end) ; - } - - { - /* One final pause to mark the end of the frame */ - int pause_end = timer->TC_CV + RWD_TIME_PAUSE; - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - while(timer->TC_CV < pause_end) ; - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - } - - /* Reset the timer, to measure time until the start of the tag frame */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ -} - -/* Receive a frame from the card in reader emulation mode, the FPGA and - * timer must have been set up by LegicRfReader and frame_send_rwd. - * - * The LEGIC RF protocol from card to reader does not include explicit - * frame start/stop information or length information. The reader must - * know beforehand how many bits it wants to receive. (Notably: a card - * sending a stream of 0-bits is indistinguishable from no card present.) - * - * Receive methodology: There is a fancy correlator in hi_read_rx_xcorr, but - * I'm not smart enough to use it. Instead I have patched hi_read_tx to output - * the ADC signal with hysteresis on SSP_DIN. Bit-bang that signal and look - * for edges. Count the edges in each bit interval. If they are approximately - * 0 this was a 0-bit, if they are approximately equal to the number of edges - * expected for a 212kHz subcarrier, this was a 1-bit. For timing we use the - * timer that's still running from frame_send_rwd in order to get a synchronization - * with the frame that we just sent. - * - * FIXME: Because we're relying on the hysteresis to just do the right thing - * the range is severely reduced (and you'll probably also need a good antenna). - * So this should be fixed some time in the future for a proper receiver. - */ -static void frame_receive_rwd(struct legic_frame * const f, int bits, int crypt) -{ - uint32_t the_bit = 1; /* Use a bitmask to save on shifts */ - uint32_t data=0; - int i, old_level=0, edges=0; - int next_bit_at = TAG_TIME_WAIT; - - if(bits > 32) { - bits = 32; - } - - AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; - - /* we have some time now, precompute the cipher - * since we cannot compute it on the fly while reading */ - legic_prng_forward(2); - - if(crypt) - { - for(i=0; iTC_CV < next_bit_at) ; - - next_bit_at += TAG_TIME_BIT; - - for(i=0; iTC_CV < next_bit_at) { - int level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - if(level != old_level) - edges++; - old_level = level; - } - next_bit_at += TAG_TIME_BIT; - - if(edges > 20 && edges < 60) { /* expected are 42 edges */ - data ^= the_bit; - } - the_bit <<= 1; - } - - f->data = data; - f->bits = bits; - - /* Reset the timer, to synchronize the next frame */ - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ -} - static void frame_append_bit(struct legic_frame * const f, int bit) { if(f->bits >= 31) { @@ -290,250 +611,6 @@ static void frame_clean(struct legic_frame * const f) f->bits = 0; } -static uint32_t perform_setup_phase_rwd(int iv) -{ - - /* Switch on carrier and let the tag charge for 1ms */ - AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT; - SpinDelay(1); - - legic_prng_init(0); /* no keystream yet */ - frame_send_rwd(iv, 7); - legic_prng_init(iv); - - frame_clean(¤t_frame); - frame_receive_rwd(¤t_frame, 6, 1); - legic_prng_forward(1); /* we wait anyways */ - while(timer->TC_CV < 387) ; /* ~ 258us */ - frame_send_rwd(0x19, 6); - - return current_frame.data; -} - -static void LegicCommonInit(void) { - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); - - /* Bitbang the transmitter */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - - setup_timer(); - - crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); -} - -static void switch_off_tag_rwd(void) -{ - /* Switch off carrier, make sure tag is reset */ - AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT; - SpinDelay(10); - - WDT_HIT(); -} -/* calculate crc for a legic command */ -static int LegicCRC(int byte_index, int value, int cmd_sz) { - crc_clear(&legic_crc); - crc_update(&legic_crc, 1, 1); /* CMD_READ */ - crc_update(&legic_crc, byte_index, cmd_sz-1); - crc_update(&legic_crc, value, 8); - return crc_finish(&legic_crc); -} - -int legic_read_byte(int byte_index, int cmd_sz) { - int byte; - - legic_prng_forward(4); /* we wait anyways */ - while(timer->TC_CV < 387) ; /* ~ 258us + 100us*delay */ - - frame_send_rwd(1 | (byte_index << 1), cmd_sz); - frame_clean(¤t_frame); - - frame_receive_rwd(¤t_frame, 12, 1); - - byte = current_frame.data & 0xff; - if( LegicCRC(byte_index, byte, cmd_sz) != (current_frame.data >> 8) ) { - Dbprintf("!!! crc mismatch: expected %x but got %x !!!", - LegicCRC(byte_index, current_frame.data & 0xff, cmd_sz), current_frame.data >> 8); - return -1; - } - - return byte; -} - -/* legic_write_byte() is not included, however it's trivial to implement - * and here are some hints on what remains to be done: - * - * * assemble a write_cmd_frame with crc and send it - * * wait until the tag sends back an ACK ('1' bit unencrypted) - * * forward the prng based on the timing - */ -int legic_write_byte(int byte, int addr, int addr_sz) { - //do not write UID, CRC, DCF - if(addr <= 0x06) { - return 0; - } - - //== send write command ============================== - crc_clear(&legic_crc); - crc_update(&legic_crc, 0, 1); /* CMD_WRITE */ - crc_update(&legic_crc, addr, addr_sz); - crc_update(&legic_crc, byte, 8); - - uint32_t crc = crc_finish(&legic_crc); - uint32_t cmd = ((crc <<(addr_sz+1+8)) //CRC - |(byte <<(addr_sz+1)) //Data - |(addr <<1) //Address - |(0x00 <<0)); //CMD = W - uint32_t cmd_sz = addr_sz+1+8+4; //crc+data+cmd - - legic_prng_forward(2); /* we wait anyways */ - while(timer->TC_CV < 387) {}; /* ~ 258us */ - frame_send_rwd(cmd, cmd_sz); - - //== wait for ack ==================================== - int t, old_level=0, edges=0; - int next_bit_at =0; - while(timer->TC_CV < 387) ; /* ~ 258us */ - for(t=0; t<80; t++) { - edges = 0; - next_bit_at += TAG_TIME_BIT; - while(timer->TC_CV < next_bit_at) { - int level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); - if(level != old_level) { - edges++; - } - old_level = level; - } - if(edges > 20 && edges < 60) { /* expected are 42 edges */ - int t = timer->TC_CV; - int c = t/TAG_TIME_BIT; - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) ; /* Wait till the clock has reset */ - legic_prng_forward(c); - return 0; - } - } - timer->TC_CCR = AT91C_TC_SWTRG; - while(timer->TC_CV > 1) {}; /* Wait till the clock has reset */ - return -1; -} - -int LegicRfReader(int offset, int bytes) { - int byte_index=0, cmd_sz=0, card_sz=0; - - LegicCommonInit(); - - uint8_t *BigBuf = BigBuf_get_addr(); - memset(BigBuf, 0, 1024); - - DbpString("setting up legic card"); - uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV); - switch_off_tag_rwd(); //we lose to mutch time with dprintf - switch(tag_type) { - case 0x1d: - DbpString("MIM 256 card found, reading card ..."); - cmd_sz = 9; - card_sz = 256; - break; - case 0x3d: - DbpString("MIM 1024 card found, reading card ..."); - cmd_sz = 11; - card_sz = 1024; - break; - default: - Dbprintf("Unknown card format: %x",tag_type); - return -1; - } - if(bytes == -1) { - bytes = card_sz; - } - if(bytes+offset >= card_sz) { - bytes = card_sz-offset; - } - - perform_setup_phase_rwd(SESSION_IV); - - LED_B_ON(); - while(byte_index < bytes) { - int r = legic_read_byte(byte_index+offset, cmd_sz); - if(r == -1 ||BUTTON_PRESS()) { - DbpString("operation aborted"); - switch_off_tag_rwd(); - LED_B_OFF(); - LED_C_OFF(); - return -1; - } - BigBuf[byte_index] = r; - WDT_HIT(); - byte_index++; - if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); - } - LED_B_OFF(); - LED_C_OFF(); - switch_off_tag_rwd(); - Dbprintf("Card read, use 'hf legic decode' or"); - Dbprintf("'data hexsamples %d' to view results", (bytes+7) & ~7); - return 0; -} - -void LegicRfWriter(int bytes, int offset) { - int byte_index=0, addr_sz=0; - uint8_t *BigBuf = BigBuf_get_addr(); - - LegicCommonInit(); - - DbpString("setting up legic card"); - uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV); - switch_off_tag_rwd(); - switch(tag_type) { - case 0x1d: - if(offset+bytes > 0x100) { - Dbprintf("Error: can not write to 0x%03.3x on MIM 256", offset+bytes); - return; - } - addr_sz = 8; - Dbprintf("MIM 256 card found, writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes); - break; - case 0x3d: - if(offset+bytes > 0x400) { - Dbprintf("Error: can not write to 0x%03.3x on MIM 1024", offset+bytes); - return; - } - addr_sz = 10; - Dbprintf("MIM 1024 card found, writing 0x%03.3x - 0x%03.3x ...", offset, offset+bytes); - break; - default: - Dbprintf("No or unknown card found, aborting"); - return; - } - - LED_B_ON(); - perform_setup_phase_rwd(SESSION_IV); - legic_prng_forward(2); - while(byte_index < bytes) { - int r = legic_write_byte(BigBuf[byte_index+offset], byte_index+offset, addr_sz); - if((r != 0) || BUTTON_PRESS()) { - Dbprintf("operation aborted @ 0x%03.3x", byte_index); - switch_off_tag_rwd(); - LED_B_OFF(); - LED_C_OFF(); - return; - } - WDT_HIT(); - byte_index++; - if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF(); - } - LED_B_OFF(); - LED_C_OFF(); - DbpString("write successful"); -} - -int timestamp; - /* Handle (whether to respond) a frame in tag mode */ static void frame_handle_tag(struct legic_frame const * const f) { @@ -588,7 +665,7 @@ static void frame_handle_tag(struct legic_frame const * const f) int key = get_key_stream(-1, 11); //legic_phase_drift, 11); int addr = f->data ^ key; addr = addr >> 1; int data = BigBuf[addr]; - int hash = LegicCRC(addr, data, 11) << 8; + int hash = calc_crc4(addr, data, 11) << 8; BigBuf[OFFSET_LOG+legic_read_count] = (uint8_t)addr; legic_read_count++; diff --git a/armsrc/legicrf.h b/armsrc/legicrf.h index 57ab7e6d..46459856 100644 --- a/armsrc/legicrf.h +++ b/armsrc/legicrf.h @@ -12,7 +12,7 @@ #define __LEGICRF_H extern void LegicRfSimulate(int phase, int frame, int reqresp); -extern int LegicRfReader(int bytes, int offset); +extern void LegicRfReader(int bytes, int offset); extern void LegicRfWriter(int bytes, int offset); #endif /* __LEGICRF_H */ diff --git a/client/Makefile b/client/Makefile index 2d256b72..c6ca1cf1 100644 --- a/client/Makefile +++ b/client/Makefile @@ -26,6 +26,11 @@ CXXFLAGS = -I../include -Wall -O3 APP_CFLAGS = include ../common/Makefile_Enabled_Options.common CFLAGS += $(APP_CFLAGS) +ifneq (,$(findstring WITH_SMARTCARD,$(APP_CFLAGS))) + SRC_SMARTCARD = cmdsmartcard.c +else + SRC_SMARTCARD = +endif LUAPLATFORM = generic platform = $(shell uname) @@ -93,7 +98,8 @@ CORESRCS = uart_posix.c \ ui.c \ comms.c -CMDSRCS = crapto1/crapto1.c\ +CMDSRCS = $(SRC_SMARTCARD) \ + crapto1/crapto1.c\ crapto1/crypto1.c\ polarssl/des.c \ polarssl/aes.c\ @@ -310,9 +316,7 @@ DEPENDENCY_FILES = $(patsubst %.c, $(OBJDIR)/%.d, $(CORESRCS) $(CMDSRCS) $(ZLIBS $(patsubst %.cpp, $(OBJDIR)/%.d, $(QTGUISRCS)) \ $(OBJDIR)/proxmark3.d $(OBJDIR)/flash.d $(OBJDIR)/flasher.d $(OBJDIR)/fpga_compress.d - $(DEPENDENCY_FILES): ; .PRECIOUS: $(DEPENDENCY_FILES) -include $(DEPENDENCY_FILES) - diff --git a/client/cmdhf.c b/client/cmdhf.c index 93906a7d..b973354d 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -353,6 +353,12 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui int CmdHFList(const char *Cmd) { + #ifdef WITH_SMARTCARD + PrintAndLog("TEST_WITH_SMARTCARD"); + #endif + #ifdef WITH_TEST + PrintAndLog("TEST_WITH_TEST"); + #endif bool showWaitCycles = false; bool markCRCBytes = false; bool loadFromFile = false; diff --git a/client/cmdmain.c b/client/cmdmain.c index 01d4c9a7..f503021a 100644 --- a/client/cmdmain.c +++ b/client/cmdmain.c @@ -26,7 +26,9 @@ #include "util.h" #include "util_posix.h" #include "cmdscript.h" - +#ifdef WITH_SMARTCARD + #include "cmdsmartcard.h" +#endif static int CmdHelp(const char *Cmd); static int CmdQuit(const char *Cmd); @@ -39,6 +41,9 @@ static command_t CommandTable[] = {"hf", CmdHF, 1, "{ High Frequency commands... }"}, {"hw", CmdHW, 1, "{ Hardware commands... }"}, {"lf", CmdLF, 1, "{ Low Frequency commands... }"}, +#ifdef WITH_SMARTCARD + {"sc", CmdSmartcard,1,"{ Smartcard commands... }"}, +#endif {"script",CmdScript,1, "{ Scripting commands }"}, {"quit", CmdQuit, 1, "Exit program"}, {"exit", CmdQuit, 1, "Exit program"}, diff --git a/client/cmdsmartcard.c b/client/cmdsmartcard.c new file mode 100644 index 00000000..b2a5705d --- /dev/null +++ b/client/cmdsmartcard.c @@ -0,0 +1,707 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Proxmark3 RDV40 Smartcard module commands +//----------------------------------------------------------------------------- +#include "cmdsmartcard.h" +#include "smartcard.h" +#include "comms.h" +#include "protocols.h" + + +static int CmdHelp(const char *Cmd); + +int usage_sm_raw(void) { + PrintAndLog("Usage: sc raw [h|r|c] d <0A 0B 0C ... hex>"); + PrintAndLog(" h : this help"); + PrintAndLog(" r : do not read response"); + PrintAndLog(" a : active signal field ON without select"); + PrintAndLog(" s : active signal field ON with select"); + PrintAndLog(" t : executes TLV decoder if it is possible"); + PrintAndLog(" d : bytes to send"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc raw d 11223344"); + return 0; +} +int usage_sm_reader(void) { + PrintAndLog("Usage: sc reader [h|s]"); + PrintAndLog(" h : this help"); + PrintAndLog(" s : silent (no messages)"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc reader"); + return 0; +} +int usage_sm_info(void) { + PrintAndLog("Usage: sc info [h|s]"); + PrintAndLog(" h : this help"); + PrintAndLog(" s : silent (no messages)"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc info"); + return 0; +} +int usage_sm_upgrade(void) { + PrintAndLog("Upgrade firmware"); + PrintAndLog("Usage: sc upgrade f "); + PrintAndLog(" h : this help"); + PrintAndLog(" f : firmware file name"); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc upgrade f myfile"); + PrintAndLog(""); + PrintAndLog("WARNING - Dangerous command, do wrong and you will brick the smart card socket"); + return 0; +} +int usage_sm_setclock(void) { + PrintAndLog("Usage: sc setclock [h] c "); + PrintAndLog(" h : this help"); + PrintAndLog(" c <> : clockspeed (0 = 16mhz, 1=8mhz, 2=4mhz) "); + PrintAndLog(""); + PrintAndLog("Examples:"); + PrintAndLog(" sc setclock c 2"); + return 0; +} + +int CmdSmartRaw(const char *Cmd) { + + int hexlen = 0; + bool active = false; + bool active_select = false; + uint8_t cmdp = 0; + bool errors = false, reply = true, decodeTLV = false, breakloop = false; + uint8_t data[USB_CMD_DATA_SIZE] = {0x00}; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_raw(); + case 'r': + reply = false; + cmdp++; + break; + case 'a': + active = true; + cmdp++; + break; + case 's': + active_select = true; + cmdp++; + break; + case 't': + decodeTLV = true; + cmdp++; + break; + case 'd': { + switch (param_gethex_to_eol(Cmd, cmdp+1, data, sizeof(data), &hexlen)) { + case 1: + PrintAndLog("Invalid HEX value."); + return 1; + case 2: + PrintAndLog("Too many bytes. Max %d bytes", sizeof(data)); + return 1; + case 3: + PrintAndLog("Hex must have an even number of digits."); + return 1; + } + cmdp++; + breakloop = true; + break; + } + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + + if ( breakloop ) + break; + } + + //Validations + if (errors || cmdp == 0 ) return usage_sm_raw(); + + // arg0 = RFU flags + // arg1 = length + UsbCommand c = {CMD_SMART_RAW, {0, hexlen, 0}}; + + if (active || active_select) { + c.arg[0] |= SC_CONNECT; + if (active) + c.arg[0] |= SC_NO_SELECT; + } + + if (hexlen > 0) { + c.arg[0] |= SC_RAW; + } + + memcpy(c.d.asBytes, data, hexlen ); + clearCommandBuffer(); + SendCommand(&c); + + // reading response from smart card + if ( reply ) { + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + PrintAndLog("smart card response failed"); + return 1; + } + uint32_t datalen = resp.arg[0]; + + if ( !datalen ) { + PrintAndLog("smart card response failed"); + return 1; + } + + PrintAndLog("received %i bytes", datalen); + + if (!datalen) + return 1; + + uint8_t *data = resp.d.asBytes; + + // TLV decoder + if (decodeTLV ) { + + if (datalen >= 2) { + PrintAndLog("%02x %02x | %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1])); + } + if (datalen > 4) { + TLVPrintFromBuffer(data, datalen - 2); + } + } else { + PrintAndLog("%s", sprint_hex(data, datalen)); + } + } + return 0; +} + +int CmdSmartUpgrade(const char *Cmd) { + + PrintAndLog("WARNING - Smartcard socket firmware upgrade."); + PrintAndLog("Dangerous command, do wrong and you will brick the smart card socket"); + + FILE *f; + char filename[FILE_PATH_SIZE] = {0}; + uint8_t cmdp = 0; + bool errors = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'f': + //File handling and reading + if ( param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE ) { + PrintAndLog("Filename too long"); + errors = true; + break; + } + cmdp += 2; + break; + case 'h': + return usage_sm_upgrade(); + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (errors || cmdp == 0 ) return usage_sm_upgrade(); + + // load file + f = fopen(filename, "rb"); + if ( !f ) { + PrintAndLog("File: %s: not found or locked.", filename); + return 1; + } + + // get filesize in order to malloc memory + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + if (fsize < 0) { + PrintAndLog("error, when getting filesize"); + fclose(f); + return 1; + } + + uint8_t *dump = calloc(fsize, sizeof(uint8_t)); + if (!dump) { + PrintAndLog("error, cannot allocate memory "); + fclose(f); + return 1; + } + + size_t bytes_read = fread(dump, 1, fsize, f); + if (f) + fclose(f); + + PrintAndLog("Smartcard socket firmware uploading to PM3"); + //Send to device + uint32_t index = 0; + uint32_t bytes_sent = 0; + uint32_t bytes_remaining = bytes_read; + + while (bytes_remaining > 0){ + uint32_t bytes_in_packet = MIN(USB_CMD_DATA_SIZE, bytes_remaining); + UsbCommand c = {CMD_SMART_UPLOAD, {index + bytes_sent, bytes_in_packet, 0}}; + + // Fill usb bytes with 0xFF + memset(c.d.asBytes, 0xFF, USB_CMD_DATA_SIZE); + memcpy(c.d.asBytes, dump + bytes_sent, bytes_in_packet); + clearCommandBuffer(); + SendCommand(&c); + if ( !WaitForResponseTimeout(CMD_ACK, NULL, 2000) ) { + PrintAndLog("timeout while waiting for reply."); + free(dump); + return 1; + } + + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + printf("."); fflush(stdout); + } + free(dump); + printf("\n"); + PrintAndLog("Smartcard socket firmware updating, don\'t turn off your PM3!"); + + // trigger the firmware upgrade + UsbCommand c = {CMD_SMART_UPGRADE, {bytes_read, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + PrintAndLog("timeout while waiting for reply."); + return 1; + } + if ( (resp.arg[0] && 0xFF ) ) + PrintAndLog("Smartcard socket firmware upgraded successful"); + else + PrintAndLog("Smartcard socket firmware updating failed"); + return 0; +} + +int CmdSmartInfo(const char *Cmd){ + uint8_t cmdp = 0; + bool errors = false, silent = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_info(); + case 's': + silent = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + cmdp++; + } + + //Validations + if (errors ) return usage_sm_info(); + + UsbCommand c = {CMD_SMART_ATR, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + + smart_card_atr_t card; + memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t)); + + // print header + PrintAndLog("\n--- Smartcard Information ---------"); + PrintAndLog("-------------------------------------------------------------"); + PrintAndLog("ISO76183 ATR : %s", sprint_hex(card.atr, card.atr_len)); + PrintAndLog("look up ATR"); + PrintAndLog("http://smartcard-atr.appspot.com/parse?ATR=%s", sprint_hex_inrow(card.atr, card.atr_len) ); + return 0; +} + +int CmdSmartReader(const char *Cmd){ + uint8_t cmdp = 0; + bool errors = false, silent = false; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_reader(); + case 's': + silent = true; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + cmdp++; + } + + //Validations + if (errors ) return usage_sm_reader(); + + UsbCommand c = {CMD_SMART_ATR, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) { + if (!silent) PrintAndLog("smart card select failed"); + return 1; + } + smart_card_atr_t card; + memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t)); + PrintAndLog("ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len)); + return 0; +} + +int CmdSmartSetClock(const char *Cmd){ + uint8_t cmdp = 0; + bool errors = false; + uint8_t clock = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': return usage_sm_setclock(); + case 'c': + clock = param_get8ex(Cmd, cmdp+1, 2, 10); + if ( clock > 2) + errors = true; + + cmdp += 2; + break; + default: + PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + //Validations + if (errors || cmdp == 0) return usage_sm_setclock(); + + UsbCommand c = {CMD_SMART_SETCLOCK, {clock, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) { + PrintAndLog("smart card select failed"); + return 1; + } + + uint8_t isok = resp.arg[0] & 0xFF; + if (!isok) { + PrintAndLog("smart card set clock failed"); + return 1; + } + + switch (clock) { + case 0: + PrintAndLog("Clock changed to 16mhz giving 10800 baudrate"); + break; + case 1: + PrintAndLog("Clock changed to 8mhz giving 21600 baudrate"); + break; + case 2: + PrintAndLog("Clock changed to 4mhz giving 86400 baudrate"); + break; + default: + break; + } + return 0; +} + + +// iso 7816-3 +void annotateIso7816(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize){ + // S-block + if ( (cmd[0] & 0xC0) && (cmdsize == 3) ) { + switch ( (cmd[0] & 0x3f) ) { + case 0x00 : snprintf(exp, size, "S-block RESYNCH req"); break; + case 0x20 : snprintf(exp, size, "S-block RESYNCH resp"); break; + case 0x01 : snprintf(exp, size, "S-block IFS req"); break; + case 0x21 : snprintf(exp, size, "S-block IFS resp"); break; + case 0x02 : snprintf(exp, size, "S-block ABORT req"); break; + case 0x22 : snprintf(exp, size, "S-block ABORT resp"); break; + case 0x03 : snprintf(exp, size, "S-block WTX reqt"); break; + case 0x23 : snprintf(exp, size, "S-block WTX resp"); break; + default : snprintf(exp, size, "S-block"); break; + } + } + // R-block (ack) + else if ( ((cmd[0] & 0xD0) == 0x80) && ( cmdsize > 2) ) { + if ( (cmd[0] & 0x10) == 0 ) + snprintf(exp, size, "R-block ACK"); + else + snprintf(exp, size, "R-block NACK"); + } + // I-block + else { + + int pos = (cmd[0] == 2 || cmd[0] == 3) ? 2 : 3; + switch ( cmd[pos] ) { + case ISO7816_READ_BINARY :snprintf(exp, size, "READ BIN");break; + case ISO7816_WRITE_BINARY :snprintf(exp, size, "WRITE BIN");break; + case ISO7816_UPDATE_BINARY :snprintf(exp, size, "UPDATE BIN");break; + case ISO7816_ERASE_BINARY :snprintf(exp, size, "ERASE BIN");break; + case ISO7816_READ_RECORDS :snprintf(exp, size, "READ RECORDS");break; + case ISO7816_WRITE_RECORDS :snprintf(exp, size, "WRITE RECORDS");break; + case ISO7816_APPEND_RECORD :snprintf(exp, size, "APPEND RECORD");break; + case ISO7816_UPDATE_RECORD :snprintf(exp, size, "UPDATE RECORD");break; + case ISO7816_GET_DATA :snprintf(exp, size, "GET DATA");break; + case ISO7816_PUT_DATA :snprintf(exp, size, "PUT DATA");break; + case ISO7816_SELECT_FILE :snprintf(exp, size, "SELECT FILE");break; + case ISO7816_VERIFY :snprintf(exp, size, "VERIFY");break; + case ISO7816_INTERNAL_AUTHENTICATION :snprintf(exp, size, "INTERNAL AUTH");break; + case ISO7816_EXTERNAL_AUTHENTICATION :snprintf(exp, size, "EXTERNAL AUTH");break; + case ISO7816_GET_CHALLENGE :snprintf(exp, size, "GET CHALLENGE");break; + case ISO7816_MANAGE_CHANNEL :snprintf(exp, size, "MANAGE CHANNEL");break; + default :snprintf(exp, size, "?"); break; + } + } +} + + +uint16_t printScTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace) { + // sanity check + if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) > traceLen) return traceLen; + + bool isResponse; + uint16_t data_len, parity_len; + uint32_t duration, timestamp, first_timestamp, EndOfTransmissionTimestamp; + char explanation[30] = {0}; + + first_timestamp = *((uint32_t *)(trace)); + timestamp = *((uint32_t *)(trace + tracepos)); + tracepos += 4; + + duration = *((uint16_t *)(trace + tracepos)); + tracepos += 2; + + data_len = *((uint16_t *)(trace + tracepos)); + tracepos += 2; + + if (data_len & 0x8000) { + data_len &= 0x7fff; + isResponse = true; + } else { + isResponse = false; + } + + parity_len = (data_len-1)/8 + 1; + if (tracepos + data_len + parity_len > traceLen) { + return traceLen; + } + uint8_t *frame = trace + tracepos; + tracepos += data_len; + //uint8_t *parityBytes = trace + tracepos; + tracepos += parity_len; + + //--- Draw the data column + char line[18][110]; + + if (data_len == 0 ) { + sprintf(line[0],""); + return tracepos; + } + + for (int j = 0; j < data_len && j/18 < 18; j++) { + snprintf(line[j/18]+(( j % 18) * 4),110, "%02x ", frame[j]); + } + + EndOfTransmissionTimestamp = timestamp + duration; + + annotateIso7816(explanation,sizeof(explanation),frame,data_len); + + int num_lines = MIN((data_len - 1)/18 + 1, 18); + for (int j = 0; j < num_lines ; j++) { + if (j == 0) { + PrintAndLog(" %10u | %10u | %s |%-72s | %s| %s", + (timestamp - first_timestamp), + (EndOfTransmissionTimestamp - first_timestamp), + (isResponse ? "Tag" : "Rdr"), + line[j], + " ", + (j == num_lines-1) ? explanation : ""); + } else { + PrintAndLog(" | | |%-72s | %s| %s", + line[j], + " ", + (j == num_lines-1) ? explanation : ""); + } + } + + // if is last record + if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) >= traceLen) return traceLen; + + return tracepos; +} + +int ScTraceList(const char *Cmd) { + bool loadFromFile = false; + bool saveToFile = false; + char type[5] = {0}; + char filename[FILE_PATH_SIZE] = {0}; + + // parse command line + param_getstr(Cmd, 0, type, sizeof(type)); + param_getstr(Cmd, 1, filename, sizeof(filename)); + + bool errors = false; + if(type[0] == 'h') { + errors = true; + } + + if(!errors) { + if (strcmp(type, "s") == 0) { + saveToFile = true; + } else if (strcmp(type,"l") == 0) { + loadFromFile = true; + } + } + + if ((loadFromFile || saveToFile) && strlen(filename) == 0) { + errors = true; + } + + if (loadFromFile && saveToFile) { + errors = true; + } + + if (errors) { + PrintAndLog("List or save protocol data."); + PrintAndLog("Usage: sc list [l ]"); + PrintAndLog(" sc list [s ]"); + PrintAndLog(" l - load data from file instead of trace buffer"); + PrintAndLog(" s - save data to file"); + PrintAndLog(""); + PrintAndLog("example: sc list"); + PrintAndLog("example: sc list save myCardTrace.trc"); + PrintAndLog("example: sc list l myCardTrace.trc"); + return 0; + } + + uint8_t *trace; + uint32_t tracepos = 0; + uint32_t traceLen = 0; + + if (loadFromFile) { + #define TRACE_CHUNK_SIZE (1<<16) // 64K to start with. Will be enough for BigBuf and some room for future extensions + FILE *tracefile = NULL; + size_t bytes_read; + trace = malloc(TRACE_CHUNK_SIZE); + if (trace == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + return 2; + } + if ((tracefile = fopen(filename,"rb")) == NULL) { + PrintAndLog("Could not open file %s", filename); + free(trace); + return 0; + } + while (!feof(tracefile)) { + bytes_read = fread(trace+traceLen, 1, TRACE_CHUNK_SIZE, tracefile); + traceLen += bytes_read; + if (!feof(tracefile)) { + uint8_t *p = realloc(trace, traceLen + TRACE_CHUNK_SIZE); + if (p == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + free(trace); + fclose(tracefile); + return 2; + } + trace = p; + } + } + fclose(tracefile); + } else { + trace = malloc(USB_CMD_DATA_SIZE); + // Query for the size of the trace + UsbCommand response; + GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0, &response, -1, false); + traceLen = response.arg[2]; + if (traceLen > USB_CMD_DATA_SIZE) { + uint8_t *p = realloc(trace, traceLen); + if (p == NULL) { + PrintAndLog("Cannot allocate memory for trace"); + free(trace); + return 2; + } + trace = p; + GetFromBigBuf(trace, traceLen, 0, NULL, -1, false); + } + } + + if (saveToFile) { + FILE *tracefile = NULL; + if ((tracefile = fopen(filename,"wb")) == NULL) { + PrintAndLog("Could not create file %s", filename); + return 1; + } + fwrite(trace, 1, traceLen, tracefile); + PrintAndLog("Recorded Activity (TraceLen = %d bytes) written to file %s", traceLen, filename); + fclose(tracefile); + } else { + PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen); + PrintAndLog(""); + PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); + PrintAndLog(""); + PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |"); + PrintAndLog("------------|------------|-----|-------------------------------------------------------------------------|-----|--------------------|"); + + while(tracepos < traceLen) + { + tracepos = printScTraceLine(tracepos, traceLen, trace); + } + } + + free(trace); + return 0; +} + +int CmdSmartList(const char *Cmd) { + ScTraceList(Cmd); + return 0; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"list", CmdSmartList, 0, "List ISO 7816 history"}, + {"info", CmdSmartInfo, 1, "Tag information [rdv40]"}, + {"reader", CmdSmartReader, 1, "Act like an IS07816 reader [rdv40]"}, + {"raw", CmdSmartRaw, 1, "Send raw hex data to tag [rdv40]"}, + {"upgrade", CmdSmartUpgrade, 1, "Upgrade firmware [rdv40]"}, + {"setclock",CmdSmartSetClock, 1, "Set clock speed"}, + {NULL, NULL, 0, NULL} +}; + +int CmdSmartcard(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/cmdsmartcard.h b/client/cmdsmartcard.h new file mode 100644 index 00000000..caa06f4f --- /dev/null +++ b/client/cmdsmartcard.h @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2018 iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Proxmark3 RDV40 Smartcard module commands +//----------------------------------------------------------------------------- + +#ifndef CMDSMARTCARD_H__ +#define CMDSMARTCARD_H__ + +#include +#include +#include +#include +#include "proxmark3.h" +#include "ui.h" +#include "cmdparser.h" +#include "common.h" +#include "util.h" +#include "loclass/fileutils.h" // saveFile +#include "cmdmain.h" // getfromdevice +#include "emv/emvcore.h" // decodeTVL +#include "emv/apduinfo.h" // APDUcode description + +extern int CmdSmartcard(const char *Cmd); + +extern int CmdSmartRaw(const char* cmd); +extern int CmdSmartUpgrade(const char* cmd); +extern int CmdSmartInfo(const char* cmd); +extern int CmdSmartReader(const char *Cmd); + +extern int usage_sm_raw(void); +extern int usage_sm_reader(void); +extern int usage_sm_info(void); +extern int usage_sm_upgrade(void); +#endif diff --git a/common/lfdemod.c b/common/lfdemod.c index f470371a..76900047 100644 --- a/common/lfdemod.c +++ b/common/lfdemod.c @@ -10,8 +10,8 @@ // // NOTES: // LF Demod functions are placed here to allow the flexability to use client or -// device side. Most BUT NOT ALL of these functions are currenlty safe for -// device side use currently. (DetectST for example...) +// device side. Most BUT NOT ALL of these functions are currently safe for +// device side use. (DetectST for example...) // // There are likely many improvements to the code that could be made, please // make suggestions... diff --git a/common/protocols.h b/common/protocols.h index 57e6011f..9ba69d5c 100644 --- a/common/protocols.h +++ b/common/protocols.h @@ -200,11 +200,12 @@ NXP/Philips CUSTOM COMMANDS #define TOPAZ_WRITE_NE8 0x1B // Write-no-erase (eight bytes) -#define ISO_14443A 0 -#define ICLASS 1 -#define ISO_14443B 2 -#define TOPAZ 3 -#define PROTO_MIFARE 4 +#define ISO_14443A 0 +#define ICLASS 1 +#define ISO_14443B 2 +#define TOPAZ 3 +#define PROTO_MIFARE 4 +#define ISO_7816_4 5 //-- Picopass fuses #define FUSE_FPERS 0x80 @@ -216,6 +217,29 @@ NXP/Philips CUSTOM COMMANDS #define FUSE_FPROD0 0x02 #define FUSE_RA 0x01 +// ISO 7816-4 Basic interindustry commands. For command APDU's. +#define ISO7816_READ_BINARY 0xB0 +#define ISO7816_WRITE_BINARY 0xD0 +#define ISO7816_UPDATE_BINARY 0xD6 +#define ISO7816_ERASE_BINARY 0x0E +#define ISO7816_READ_RECORDS 0xB2 +#define ISO7816_WRITE_RECORDS 0xD2 +#define ISO7816_APPEND_RECORD 0xE2 +#define ISO7816_UPDATE_RECORD 0xDC +#define ISO7816_GET_DATA 0xCA +#define ISO7816_PUT_DATA 0xDA +#define ISO7816_SELECT_FILE 0xA4 +#define ISO7816_VERIFY 0x20 +#define ISO7816_INTERNAL_AUTHENTICATION 0x88 +#define ISO7816_EXTERNAL_AUTHENTICATION 0x82 +#define ISO7816_GET_CHALLENGE 0xB4 +#define ISO7816_MANAGE_CHANNEL 0x70 +// ISO7816-4 For response APDU's +#define ISO7816_OK 0x9000 +// 6x xx = ERROR + + + void printIclassDumpInfo(uint8_t* iclass_dump); void getMemConfig(uint8_t mem_cfg, uint8_t chip_cfg, uint8_t *max_blk, uint8_t *app_areas, uint8_t *kb); diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v index fc309cde..756683cd 100644 --- a/fpga/hi_read_tx.v +++ b/fpga/hi_read_tx.v @@ -71,21 +71,8 @@ always @(negedge ssp_clk) assign ssp_frame = (hi_byte_div == 3'b000); -// Implement a hysteresis to give out the received signal on -// ssp_din. Sample at fc. -assign adc_clk = ck_1356meg; +assign ssp_din = 1'b0; -// ADC data appears on the rising edge, so sample it on the falling edge -reg after_hysteresis; -always @(negedge adc_clk) -begin - if(& adc_d[7:0]) after_hysteresis <= 1'b1; - else if(~(| adc_d[7:0])) after_hysteresis <= 1'b0; -end - - -assign ssp_din = after_hysteresis; - -assign dbg = ssp_din; +assign dbg = ssp_frame; -endmodule +endmodule \ No newline at end of file diff --git a/include/legic.h b/include/legic.h new file mode 100644 index 00000000..246af0e8 --- /dev/null +++ b/include/legic.h @@ -0,0 +1,27 @@ +//----------------------------------------------------------------------------- +// (c) 2016 Iceman +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// LEGIC type prototyping +//----------------------------------------------------------------------------- + +#ifndef _LEGIC_H_ +#define _LEGIC_H_ + +#include "common.h" + +//----------------------------------------------------------------------------- +// LEGIC +//----------------------------------------------------------------------------- +typedef struct { + uint8_t uid[4]; + uint32_t tagtype; + uint8_t cmdsize; + uint8_t addrsize; + uint16_t cardsize; +} legic_card_select_t; + +#endif // _LEGIC_H_ diff --git a/include/smartcard.h b/include/smartcard.h new file mode 100644 index 00000000..9bed8c9d --- /dev/null +++ b/include/smartcard.h @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// (c) 2018 Iceman, adapted by Marshmellow +// +// 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. +//----------------------------------------------------------------------------- +// smart card type prototyping +//----------------------------------------------------------------------------- +#ifndef __SMARTCARD_H +#define __SMARTCARD_H + +//----------------------------------------------------------------------------- +// ISO 7618 Smart Card +//----------------------------------------------------------------------------- +typedef struct { + uint8_t atr_len; + uint8_t atr[30]; +} __attribute__((__packed__)) smart_card_atr_t; + +typedef enum SMARTCARD_COMMAND { + SC_CONNECT = (1 << 0), + SC_NO_DISCONNECT = (1 << 1), + SC_RAW = (1 << 2), + SC_NO_SELECT = (1 << 3) +} smartcard_command_t; + + +#endif diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 194a9d53..bdff7261 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -60,8 +60,17 @@ typedef struct{ #define CMD_BUFF_CLEAR 0x0105 #define CMD_READ_MEM 0x0106 #define CMD_VERSION 0x0107 -#define CMD_STATUS 0x0108 -#define CMD_PING 0x0109 +#define CMD_STATUS 0x0108 +#define CMD_PING 0x0109 + +// RDV40, Smart card operations +#define CMD_SMART_RAW 0x0140 +#define CMD_SMART_UPGRADE 0x0141 +#define CMD_SMART_UPLOAD 0x0142 +#define CMD_SMART_ATR 0x0143 +// CMD_SMART_SETBAUD is unused for now +#define CMD_SMART_SETBAUD 0x0144 +#define CMD_SMART_SETCLOCK 0x0145 // For low-frequency tags #define CMD_READ_TI_TYPE 0x0202 @@ -126,10 +135,10 @@ typedef struct{ #define CMD_READER_HITAG 0x0372 #define CMD_SIMULATE_HITAG_S 0x0368 -#define CMD_TEST_HITAGS_TRACES 0x0367 -#define CMD_READ_HITAG_S 0x0373 -#define CMD_WR_HITAG_S 0x0375 -#define CMD_EMU_HITAG_S 0x0376 +#define CMD_TEST_HITAGS_TRACES 0x0367 +#define CMD_READ_HITAG_S 0x0373 +#define CMD_WR_HITAG_S 0x0375 +#define CMD_EMU_HITAG_S 0x0376 #define CMD_SIMULATE_TAG_ISO_14443B 0x0381