From d19929cbe8d681b60496ca6d9d9cbd806822e163 Mon Sep 17 00:00:00 2001 From: "roel@libnfc.org" Date: Tue, 18 Sep 2012 13:53:17 +0000 Subject: [PATCH] MAJOR update, added hitag2 reader, emulation and eavesdropping, lots of new code, including FPGA tweaks, part 2 --- armsrc/Makefile | 2 +- armsrc/appmain.c | 79 ++- armsrc/apps.h | 28 +- armsrc/fpgaloader.c | 14 +- armsrc/hitag2.c | 1267 +++++++++++++++++++++++++++++----- armsrc/iclass.c | 2 +- armsrc/iso14443a.c | 16 +- armsrc/iso15693.c | 12 +- armsrc/lfops.c | 210 +----- armsrc/mifarecmd.c | 16 +- common/usb.c | 25 +- fpga/Makefile | 2 +- fpga/fpga.bit | Bin 42172 -> 42172 bytes fpga/fpga.v | 11 +- fpga/lo_edge_detect.v | 90 +++ include/common.h | 1 + {armsrc => include}/hitag2.h | 28 +- include/usb_cmd.h | 6 + 18 files changed, 1355 insertions(+), 454 deletions(-) create mode 100644 fpga/lo_edge_detect.v rename {armsrc => include}/hitag2.h (50%) diff --git a/armsrc/Makefile b/armsrc/Makefile index e2d713247..715eba0a0 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -10,7 +10,7 @@ APP_INCLUDES = apps.h #remove one of the following defines and comment out the relevant line #in the next section to remove that particular feature from compilation -APP_CFLAGS = -O2 -DWITH_LF -DWITH_ISO15693 -DWITH_ISO14443a -DWITH_ISO14443b -DWITH_ICLASS -DWITH_LEGICRF +APP_CFLAGS = -O2 -DWITH_LF -DWITH_ISO15693 -DWITH_ISO14443a -DWITH_ISO14443b -DWITH_ICLASS -DWITH_LEGICRF -DWITH_HITAG #-DWITH_LCD #SRC_LCD = fonts.c LCD.c diff --git a/armsrc/appmain.c b/armsrc/appmain.c index d35448614..dac87677c 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -19,6 +19,7 @@ #include #include "legicrf.h" +#include #ifdef WITH_LCD # include "fonts.h" @@ -125,23 +126,27 @@ void Dbprintf(const char *fmt, ...) { } // prints HEX & ASCII -void Dbhexdump(int len, uint8_t *d) { +void Dbhexdump(int len, uint8_t *d, bool bAsci) { int l=0,i; char ascii[9]; - + while (len>0) { if (len>8) l=8; else l=len; memcpy(ascii,d,l); - ascii[l]=0; + ascii[l]=0; // filter safe ascii - for (i=0;i126) ascii[i]='.'; - - Dbprintf("%-8s %*D",ascii,l,d," "); - + + if (bAsci) { + Dbprintf("%-8s %*D",ascii,l,d," "); + } else { + Dbprintf("%*D",l,d," "); + } + len-=8; d+=8; } @@ -185,14 +190,15 @@ int AvgAdc(int ch) // was static - merlok void MeasureAntennaTuning(void) { - uint8_t *dest = (uint8_t *)BigBuf; + uint8_t *dest = (uint8_t *)BigBuf+FREE_BUFFER_OFFSET; int i, adcval = 0, peak = 0, peakv = 0, peakf = 0; //ptr = 0 int vLf125 = 0, vLf134 = 0, vHf = 0; // in mV UsbCommand c; - DbpString("Measuring antenna characteristics, please wait."); - memset(BigBuf,0,sizeof(BigBuf)); + LED_B_ON(); + DbpString("Measuring antenna characteristics, please wait..."); + memset(dest,0,sizeof(FREE_BUFFER_SIZE)); /* * Sweeps the useful LF range of the proxmark from @@ -202,8 +208,10 @@ void MeasureAntennaTuning(void) * the resonating frequency of your LF antenna * ( hopefully around 95 if it is tuned to 125kHz!) */ + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_READER); for (i=255; i>19; i--) { + WDT_HIT(); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, i); SpinDelay(20); // Vref = 3.3V, and a 10000:240 voltage divider on the input @@ -221,6 +229,7 @@ void MeasureAntennaTuning(void) } } + LED_A_ON(); // Let the FPGA drive the high-frequency antenna around 13.56 MHz. FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR); SpinDelay(20); @@ -232,7 +241,14 @@ void MeasureAntennaTuning(void) c.arg[0] = (vLf125 << 0) | (vLf134 << 16); c.arg[1] = vHf; c.arg[2] = peakf | (peakv << 16); + + DbpString("Measuring complete, sending report back to host"); + UsbSendPacket((uint8_t *)&c, sizeof(c)); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_A_OFF(); + LED_B_OFF(); + return; } void MeasureAntennaTuningHf(void) @@ -258,8 +274,7 @@ void MeasureAntennaTuningHf(void) void SimulateTagHfListen(void) { - uint8_t *dest = (uint8_t *)BigBuf; - int n = sizeof(BigBuf); + uint8_t *dest = (uint8_t *)BigBuf+FREE_BUFFER_OFFSET; uint8_t v = 0; int i; int p = 0; @@ -293,7 +308,7 @@ void SimulateTagHfListen(void) p = 0; i++; - if(i >= n) { + if(i >= FREE_BUFFER_SIZE) { break; } } @@ -644,6 +659,18 @@ void UsbPacketReceived(uint8_t *packet, int len) break; #endif +#ifdef WITH_HITAG + case CMD_SNOOP_HITAG: // Eavesdrop Hitag tag, args = type + SnoopHitag(c->arg[0]); + break; + case CMD_SIMULATE_HITAG: // Simulate Hitag tag, args = memory content + SimulateHitagTag((bool)c->arg[0],(byte_t*)c->d.asBytes); + break; + case CMD_READER_HITAG: // Reader for Hitag tags, args = type and function + ReaderHitag((hitag_function)c->arg[0],(hitag_data*)c->d.asBytes); + break; +#endif + #ifdef WITH_ISO15693 case CMD_ACQUIRE_RAW_ADC_SAMPLES_ISO_15693: AcquireRawAdcSamplesIso15693(); @@ -822,16 +849,14 @@ void UsbPacketReceived(uint8_t *packet, int len) LED_B_ON(); UsbSendPacket((uint8_t *)&n, sizeof(n)); LED_B_OFF(); - break; - } + } break; case CMD_DOWNLOADED_SIM_SAMPLES_125K: { uint8_t *b = (uint8_t *)BigBuf; memcpy(b+c->arg[0], c->d.asBytes, 48); //Dbprintf("copied 48 bytes to %i",b+c->arg[0]); UsbSendPacket((uint8_t*)&ack, sizeof(ack)); - break; - } + } break; case CMD_READ_MEM: ReadMem(c->arg[0]); @@ -854,10 +879,6 @@ void UsbPacketReceived(uint8_t *packet, int len) SendVersion(); break; -#ifdef WITH_LF - -#endif - #ifdef WITH_LCD case CMD_LCD_RESET: LCDReset(); @@ -868,7 +889,7 @@ void UsbPacketReceived(uint8_t *packet, int len) #endif case CMD_SETUP_WRITE: case CMD_FINISH_WRITE: - case CMD_HARDWARE_RESET: + case CMD_HARDWARE_RESET: { USB_D_PLUS_PULLUP_OFF(); SpinDelay(1000); SpinDelay(1000); @@ -876,16 +897,16 @@ void UsbPacketReceived(uint8_t *packet, int len) for(;;) { // We're going to reset, and the bootrom will take control. } - break; + } break; - case CMD_START_FLASH: + case CMD_START_FLASH: { if(common_area.flags.bootrom_present) { common_area.command = COMMON_AREA_COMMAND_ENTER_FLASH_MODE; } USB_D_PLUS_PULLUP_OFF(); AT91C_BASE_RSTC->RSTC_RCR = RST_CONTROL_KEY | AT91C_RSTC_PROCRST; for(;;); - break; + } break; case CMD_DEVICE_INFO: { UsbCommand c; @@ -893,11 +914,11 @@ void UsbPacketReceived(uint8_t *packet, int len) c.arg[0] = DEVICE_INFO_FLAG_OSIMAGE_PRESENT | DEVICE_INFO_FLAG_CURRENT_MODE_OS; if(common_area.flags.bootrom_present) c.arg[0] |= DEVICE_INFO_FLAG_BOOTROM_PRESENT; UsbSendPacket((uint8_t*)&c, sizeof(c)); - } - break; - default: + } break; + + default: { Dbprintf("%s: 0x%04x","unknown command:",c->cmd); - break; + } break; } } diff --git a/armsrc/apps.h b/armsrc/apps.h index 6e98311a1..f3f902378 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -14,7 +14,8 @@ #include #include -typedef unsigned char byte_t; +#include "common.h" +#include "hitag2.h" // The large multi-purpose buffer, typically used to hold A/D samples, // maybe processed in some way. @@ -49,7 +50,7 @@ void SamyRun(void); //void DbpIntegers(int a, int b, int c); void DbpString(char *str); void Dbprintf(const char *fmt, ...); -void Dbhexdump(int len, uint8_t *d); +void Dbhexdump(int len, uint8_t *d, bool bAsci); int AvgAdc(int ch); @@ -69,13 +70,9 @@ void FpgaDownloadAndGo(void); void FpgaGatherVersion(char *dst, int len); void FpgaSetupSsc(void); void SetupSpi(int mode); -void FpgaSetupSscDma(uint8_t *buf, int len); -void inline FpgaDisableSscDma(void){ - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; -} -void inline FpgaEnableSscDma(void){ - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; -} +bool FpgaSetupSscDma(uint8_t *buf, int len); +#define FpgaDisableSscDma(void) AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; +#define FpgaEnableSscDma(void) AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; void SetAdcMuxFor(uint32_t whichGpio); // Definitions for the FPGA commands. @@ -83,19 +80,21 @@ void SetAdcMuxFor(uint32_t whichGpio); #define FPGA_CMD_SET_DIVISOR (2<<12) // Definitions for the FPGA configuration word. #define FPGA_MAJOR_MODE_LF_READER (0<<5) -#define FPGA_MAJOR_MODE_LF_SIMULATOR (1<<5) +#define FPGA_MAJOR_MODE_LF_EDGE_DETECT (1<<5) #define FPGA_MAJOR_MODE_HF_READER_TX (2<<5) #define FPGA_MAJOR_MODE_HF_READER_RX_XCORR (3<<5) #define FPGA_MAJOR_MODE_HF_SIMULATOR (4<<5) #define FPGA_MAJOR_MODE_HF_ISO14443A (5<<5) #define FPGA_MAJOR_MODE_LF_PASSTHRU (6<<5) #define FPGA_MAJOR_MODE_OFF (7<<5) +// Options for LF_EDGE_DETECT +#define FPGA_LF_EDGE_DETECT_READER_FIELD (1<<0) // Options for the HF reader, tx to tag #define FPGA_HF_READER_TX_SHALLOW_MOD (1<<0) // Options for the HF reader, correlating against rx from tag #define FPGA_HF_READER_RX_XCORR_848_KHZ (1<<0) #define FPGA_HF_READER_RX_XCORR_SNOOP (1<<1) -#define FPGA_HF_READER_RX_XCORR_QUARTER_FREQ (1<<2) +#define FPGA_HF_READER_RX_XCORR_QUARTER_FREQ (1<<2) // Options for the HF simulated tag, how to modulate #define FPGA_HF_SIMULATOR_NO_MODULATION (0<<0) #define FPGA_HF_SIMULATOR_MODULATE_BPSK (1<<0) @@ -139,7 +138,7 @@ void ReaderIso14443a(UsbCommand * c, UsbCommand * ack); int RAMFUNC LogTrace(const uint8_t * btBytes, int iLen, int iSamples, uint32_t dwParity, int bReader); uint32_t GetParity(const uint8_t * pbtCmd, int iLen); void iso14a_set_trigger(int enable); -void iso14a_clear_tracelen(void); +void iso14a_clear_trace(void); void iso14a_set_tracing(int enable); void RAMFUNC SniffMifare(uint8_t param); @@ -176,6 +175,11 @@ void RAMFUNC SnoopIClass(void); void SimulateIClass(uint8_t arg0, uint8_t *datain); void ReaderIClass(uint8_t arg0); +// hitag2.h +void SnoopHitag(uint32_t type); +void SimulateHitagTag(bool tag_mem_supplied, byte_t* data); +void ReaderHitag(hitag_function htf, hitag_data* htd); + /// util.h #endif diff --git a/armsrc/fpgaloader.c b/armsrc/fpgaloader.c index c199b9a86..a719f5edf 100644 --- a/armsrc/fpgaloader.c +++ b/armsrc/fpgaloader.c @@ -136,18 +136,20 @@ void FpgaSetupSsc(void) // ourselves, not to another buffer). The stuff to manipulate those buffers // is in apps.h, because it should be inlined, for speed. //----------------------------------------------------------------------------- -void FpgaSetupSscDma(uint8_t *buf, int len) +bool FpgaSetupSscDma(uint8_t *buf, int len) { + if (buf == NULL) { + return false; + } + AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTDIS; - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t) buf; AT91C_BASE_PDC_SSC->PDC_RCR = len; AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) buf; AT91C_BASE_PDC_SSC->PDC_RNCR = len; - - if (buf != NULL) { - AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; - } + AT91C_BASE_PDC_SSC->PDC_PTCR = AT91C_PDC_RXTEN; + + return true; } static void DownloadFPGA_byte(unsigned char w) diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index 15daa25e5..755132323 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -1,14 +1,19 @@ //----------------------------------------------------------------------------- -// (c) 2009 Henryk Plötz -// // 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. //----------------------------------------------------------------------------- -// Hitag2 emulation +// Hitag2 emulation (preliminary test version) // -// Contains state and functions for an emulated Hitag2 tag. Offers an entry -// point to handle commands, needs a callback to send response. +// (c) 2009 Henryk Plötz +//----------------------------------------------------------------------------- +// Hitag2 complete rewrite of the code +// - Fixed modulation/encoding issues +// - Rewrote code for transponder emulation +// - Added snooping of transponder communication +// - Added reader functionality +// +// (c) 2012 Roel Verdult //----------------------------------------------------------------------------- #include "proxmark3.h" @@ -17,166 +22,57 @@ #include "hitag2.h" #include "string.h" -struct hitag2_cipher_state { - uint64_t state; -}; +static bool bQuiet; + +bool bCrypto; +bool bPwd; struct hitag2_tag { uint32_t uid; enum { - TAG_STATE_RESET, // Just powered up, awaiting GetSnr - TAG_STATE_ACTIVATING, // In activation phase (password mode), sent UID, awaiting reader password - TAG_STATE_AUTHENTICATING, // In activation phase (crypto mode), awaiting reader authentication - TAG_STATE_ACTIVATED, // Activation complete, awaiting read/write commands - TAG_STATE_WRITING, // In write command, awaiting sector contents to be written + TAG_STATE_RESET = 0x01, // Just powered up, awaiting GetSnr + TAG_STATE_ACTIVATING = 0x02 , // In activation phase (password mode), sent UID, awaiting reader password + TAG_STATE_ACTIVATED = 0x03, // Activation complete, awaiting read/write commands + TAG_STATE_WRITING = 0x04, // In write command, awaiting sector contents to be written } state; unsigned int active_sector; - char crypto_active; - struct hitag2_cipher_state cs; - char sectors[8][4]; + byte_t crypto_active; + uint64_t cs; + byte_t sectors[12][4]; }; -static void hitag2_cipher_reset(struct hitag2_tag *tag, const char *challenge); -static int hitag2_cipher_authenticate(struct hitag2_cipher_state *cs, const char *authenticator); -static int hitag2_cipher_transcrypt(struct hitag2_cipher_state *cs, char *data, unsigned int bytes, unsigned int bits); - static struct hitag2_tag tag; static const struct hitag2_tag resetdata = { - .state = TAG_STATE_RESET, - .sectors = { // Password mode: | Crypto mode: - [0] = { 0x35, 0x33, 0x70, 0x11}, // UID | UID - [1] = { 0x4d, 0x49, 0x4b, 0x52}, // Password RWD | 32 bit LSB key - [2] = { 0x20, 0xf0, 0x4f, 0x4e}, // Reserved | 16 bit MSB key, 16 bit reserved - [3] = { 0x0e, 0xaa, 'H', 'T'}, // Configuration, password TAG | Configuration, password TAG - }, + .state = TAG_STATE_RESET, + .sectors = { // Password mode: | Crypto mode: + [0] = { 0x02, 0x4e, 0x02, 0x20}, // UID | UID + [1] = { 0x4d, 0x49, 0x4b, 0x52}, // Password RWD | 32 bit LSB key + [2] = { 0x20, 0xf0, 0x4f, 0x4e}, // Reserved | 16 bit MSB key, 16 bit reserved + [3] = { 0x0e, 0xaa, 0x48, 0x54}, // Configuration, password TAG | Configuration, password TAG + [4] = { 0x46, 0x5f, 0x4f, 0x4b}, // Data: F_OK + [5] = { 0x55, 0x55, 0x55, 0x55}, // Data: UUUU + [6] = { 0xaa, 0xaa, 0xaa, 0xaa}, // Data: .... + [7] = { 0x55, 0x55, 0x55, 0x55}, // Data: UUUU + [8] = { 0x00, 0x00, 0x00, 0x00}, // RSK Low + [9] = { 0x00, 0x00, 0x00, 0x00}, // RSK High + [10] = { 0x00, 0x00, 0x00, 0x00}, // RCF + [11] = { 0x00, 0x00, 0x00, 0x00}, // SYNC + }, }; -int hitag2_reset(void) -{ - tag.state = TAG_STATE_RESET; - tag.crypto_active = 0; - return 0; -} +//#define TRACE_LENGTH 3000 +//uint8_t *trace = (uint8_t *) BigBuf; +//int traceLen = 0; +//int rsamples = 0; -int hitag2_init(void) -{ - memcpy(&tag, &resetdata, sizeof(tag)); - hitag2_reset(); - return 0; -} +#define AUTH_TABLE_OFFSET FREE_BUFFER_OFFSET +#define AUTH_TABLE_LENGTH FREE_BUFFER_SIZE +byte_t* auth_table = (byte_t *)BigBuf+AUTH_TABLE_OFFSET; +size_t auth_table_pos = 0; +size_t auth_table_len = AUTH_TABLE_LENGTH; -int hitag2_handle_command(const char* data, const int length, hitag2_response_callback_t cb, void *cb_cookie) -{ - (void)data; (void)length; (void)cb; (void)cb_cookie; - int retry = 0, done = 0, result=0; - char temp[10]; - - if(tag.crypto_active && length < sizeof(temp)*8) { - /* Decrypt command */ - memcpy(temp, data, (length+7)/8); - hitag2_cipher_transcrypt(&(tag.cs), temp, length/8, length%8); - data = temp; - } - - -handle_command_retry: - switch(tag.state) { - case TAG_STATE_RESET: - if(length == 5 && data[0] == 0xC0) { - /* Received 11000 from the reader, request for UID, send UID */ - result=cb(tag.sectors[0], sizeof(tag.sectors[0])*8, 208, cb_cookie); - done=1; - if(tag.sectors[3][0] & 0x08) { - tag.state=TAG_STATE_AUTHENTICATING; - } else { - tag.state=TAG_STATE_ACTIVATING; - } - } - break; - case TAG_STATE_ACTIVATING: - if(length == 0x20) { - /* Received RWD password, respond with configuration and our password */ - result=cb(tag.sectors[3], sizeof(tag.sectors[3])*8, 208, cb_cookie); - done=1; - tag.state=TAG_STATE_ACTIVATED; - } - break; - case TAG_STATE_AUTHENTICATING: - if(length == 0x40) { - /* Received initialisation vector || authentication token, fire up cipher, send our password */ - hitag2_cipher_reset(&tag, data); - if(hitag2_cipher_authenticate(&(tag.cs), data+4)) { - char response_enc[4]; - memcpy(response_enc, tag.sectors[3], 4); - hitag2_cipher_transcrypt(&(tag.cs), response_enc, 4, 0); - result=cb(response_enc, 4*8, 208, cb_cookie); - done=1; - tag.crypto_active = 1; - tag.state = TAG_STATE_ACTIVATED; - } else { - /* The reader failed to authenticate, do nothing */ - DbpString("Reader authentication failed"); - } - } - break; - case TAG_STATE_ACTIVATED: - if(length == 10) { - if( ((data[0] & 0xC0) == 0xC0) && ((data[0] & 0x06) == 0) ) { - /* Read command: 11xx x00y yy with yyy == ~xxx, xxx is sector number */ - unsigned int sector = (~( ((data[0]<<2)&0x04) | ((data[1]>>6)&0x03) ) & 0x07); - if(sector == ( (data[0]>>3)&0x07 ) ) { - memcpy(temp, tag.sectors[sector], 4); - if(tag.crypto_active) { - hitag2_cipher_transcrypt(&(tag.cs), temp, 4, 0); - } - /* Respond with contents of sector sector */ - result = cb(temp, 4*8, 208, cb_cookie); - done=1; - } else { - /* transmission error */ - DbpString("Transmission error (read) in activated state"); - } - } else if( ((data[0] & 0xC0) == 0x80) && ((data[0] & 0x06) == 2) ) { - /* Write command: 10xx x01y yy with yyy == ~xxx, xxx is sector number */ - unsigned int sector = (~( ((data[0]<<2)&0x04) | ((data[1]>>6)&0x03) ) & 0x07); - if(sector == ( (data[0]>>3)&0x07 ) ) { - /* Prepare write, acknowledge by repeating command */ - if(tag.crypto_active) { - hitag2_cipher_transcrypt(&(tag.cs), temp, length/8, length%8); - } - result = cb(data, length, 208, cb_cookie); - done=1; - tag.active_sector = sector; - tag.state=TAG_STATE_WRITING; - } else { - /* transmission error */ - DbpString("Transmission error (write) in activated state"); - } - } - - } - case TAG_STATE_WRITING: - if(length == 32) { - /* These are the sector contents to be written. We don't have to do anything else. */ - memcpy(tag.sectors[tag.active_sector], data, length/8); - tag.state=TAG_STATE_ACTIVATED; - done=1; - } - } - - if(!done && !retry) { - /* We didn't respond, maybe our state is faulty. Reset and try again. */ - retry=1; - if(tag.crypto_active) { - /* Restore undeciphered data */ - memcpy(temp, data, (length+7)/8); - } - hitag2_reset(); - goto handle_command_retry; - } - - return result; -} +byte_t password[4]; +byte_t NrAr[8]; /* Following is a modified version of cryptolib.com/ciphers/hitag2/ */ // Software optimized 48-bit Philips/NXP Mifare Hitag2 PCF7936/46/47/52 stream cipher algorithm by I.C. Wiener 2006-2007. @@ -254,10 +150,25 @@ static u32 _hitag2_byte (u64 * x) return c; } +size_t nbytes(size_t nbits) { + return (nbits/8)+((nbits%8)>0); +} -/* Cipher/tag glue code: */ +int hitag2_reset(void) +{ + tag.state = TAG_STATE_RESET; + tag.crypto_active = 0; + return 0; +} -static void hitag2_cipher_reset(struct hitag2_tag *tag, const char *iv) +int hitag2_init(void) +{ + memcpy(&tag, &resetdata, sizeof(tag)); + hitag2_reset(); + return 0; +} + +static void hitag2_cipher_reset(struct hitag2_tag *tag, const byte_t *iv) { uint64_t key = ((uint64_t)tag->sectors[2][2]) | ((uint64_t)tag->sectors[2][3] << 8) | @@ -273,23 +184,1051 @@ static void hitag2_cipher_reset(struct hitag2_tag *tag, const char *iv) (((uint32_t)(iv[1])) << 8) | (((uint32_t)(iv[2])) << 16) | (((uint32_t)(iv[3])) << 24); - tag->cs.state = _hitag2_init(rev64(key), rev32(uid), rev32(iv_)); + tag->cs = _hitag2_init(rev64(key), rev32(uid), rev32(iv_)); } -static int hitag2_cipher_authenticate(struct hitag2_cipher_state *cs, const char *authenticator_is) +static int hitag2_cipher_authenticate(uint64_t* cs, const byte_t *authenticator_is) { - char authenticator_should[4]; - authenticator_should[0] = ~_hitag2_byte(&(cs->state)); - authenticator_should[1] = ~_hitag2_byte(&(cs->state)); - authenticator_should[2] = ~_hitag2_byte(&(cs->state)); - authenticator_should[3] = ~_hitag2_byte(&(cs->state)); - return memcmp(authenticator_should, authenticator_is, 4) == 0; + byte_t authenticator_should[4]; + authenticator_should[0] = ~_hitag2_byte(cs); + authenticator_should[1] = ~_hitag2_byte(cs); + authenticator_should[2] = ~_hitag2_byte(cs); + authenticator_should[3] = ~_hitag2_byte(cs); + return (memcmp(authenticator_should, authenticator_is, 4) == 0); } -static int hitag2_cipher_transcrypt(struct hitag2_cipher_state *cs, char *data, unsigned int bytes, unsigned int bits) +static int hitag2_cipher_transcrypt(uint64_t* cs, byte_t *data, unsigned int bytes, unsigned int bits) { int i; - for(i=0; istate)); - for(i=0; istate)) << (7-i); + for(i=0; i 36 */ +#define HITAG_T_LOW 8 /* T_LOW should be 4..10 */ +#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */ +#define HITAG_T_1_MIN 25 /* T[1] should be 26..30 */ +//#define HITAG_T_EOF 40 /* T_EOF should be > 36 */ +#define HITAG_T_EOF 80 /* T_EOF should be > 36 */ +#define HITAG_T_WAIT_1 200 /* T_wresp should be 199..206 */ +#define HITAG_T_WAIT_2 90 /* T_wresp should be 199..206 */ +#define HITAG_T_WAIT_MAX 300 /* bit more than HITAG_T_WAIT_1 + HITAG_T_WAIT_2 */ + +#define HITAG_T_TAG_ONE_HALF_PERIOD 10 +#define HITAG_T_TAG_TWO_HALF_PERIOD 25 +#define HITAG_T_TAG_THREE_HALF_PERIOD 41 +#define HITAG_T_TAG_FOUR_HALF_PERIOD 57 + +#define HITAG_T_TAG_HALF_PERIOD 16 +#define HITAG_T_TAG_FULL_PERIOD 32 + +#define HITAG_T_TAG_CAPTURE_ONE_HALF 13 +#define HITAG_T_TAG_CAPTURE_TWO_HALF 25 +#define HITAG_T_TAG_CAPTURE_THREE_HALF 41 +#define HITAG_T_TAG_CAPTURE_FOUR_HALF 57 + + +static void hitag_send_bit(int bit) { + LED_A_ON(); + // Reset clock for the next bit + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + + // Fixed modulation, earlier proxmark version used inverted signal + if(bit == 0) { + // Manchester: Unloaded, then loaded |__--| + LOW(GPIO_SSC_DOUT); + while(AT91C_BASE_TC0->TC_CV < T0*HITAG_T_TAG_HALF_PERIOD); + HIGH(GPIO_SSC_DOUT); + while(AT91C_BASE_TC0->TC_CV < T0*HITAG_T_TAG_FULL_PERIOD); + } else { + // Manchester: Loaded, then unloaded |--__| + HIGH(GPIO_SSC_DOUT); + while(AT91C_BASE_TC0->TC_CV < T0*HITAG_T_TAG_HALF_PERIOD); + LOW(GPIO_SSC_DOUT); + while(AT91C_BASE_TC0->TC_CV < T0*HITAG_T_TAG_FULL_PERIOD); + } + LED_A_OFF(); +} + +static void hitag_send_frame(const byte_t* frame, size_t frame_len) +{ + // Send start of frame + for(size_t i=0; i<5; i++) { + hitag_send_bit(1); + } + + // Send the content of the frame + for(size_t i=0; i> (7-(i%8)))&1); + } + + // Drop the modulation + LOW(GPIO_SSC_DOUT); +} + +void hitag2_handle_reader_command(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) +{ + byte_t rx_air[HITAG_FRAME_LEN]; + + // Copy the (original) received frame how it is send over the air + memcpy(rx_air,rx,nbytes(rxlen)); + + if(tag.crypto_active) { + hitag2_cipher_transcrypt(&(tag.cs),rx,rxlen/8,rxlen%8); + } + + // Reset the transmission frame length + *txlen = 0; + + // Try to find out which command was send by selecting on length (in bits) + switch (rxlen) { + // Received 11000 from the reader, request for UID, send UID + case 05: { + // Always send over the air in the clear plaintext mode + if(rx_air[0] != 0xC0) { + // Unknown frame ? + return; + } + *txlen = 32; + memcpy(tx,tag.sectors[0],4); + tag.crypto_active = 0; + } + break; + + // Read/Write command: ..xx x..y yy with yyy == ~xxx, xxx is sector number + case 10: { + unsigned int sector = (~( ((rx[0]<<2)&0x04) | ((rx[1]>>6)&0x03) ) & 0x07); + // Verify complement of sector index + if(sector != ((rx[0]>>3)&0x07)) { + //DbpString("Transmission error (read/write)"); + return; + } + + switch (rx[0] & 0xC6) { + // Read command: 11xx x00y + case 0xC0: + memcpy(tx,tag.sectors[sector],4); + *txlen = 32; + break; + + // Inverted Read command: 01xx x10y + case 0x44: + for (size_t i=0; i<4; i++) { + tx[i] = tag.sectors[sector][i] ^ 0xff; + } + *txlen = 32; + break; + + // Write command: 10xx x01y + case 0x82: + // Prepare write, acknowledge by repeating command + memcpy(tx,rx,nbytes(rxlen)); + *txlen = rxlen; + tag.active_sector = sector; + tag.state=TAG_STATE_WRITING; + break; + + // Unknown command + default: + Dbprintf("Uknown command: %02x %02x",rx[0],rx[1]); + return; + break; + } + } + break; + + // Writing data or Reader password + case 32: { + if(tag.state == TAG_STATE_WRITING) { + // These are the sector contents to be written. We don't have to do anything else. + memcpy(tag.sectors[tag.active_sector],rx,nbytes(rxlen)); + tag.state=TAG_STATE_RESET; + return; + } else { + // Received RWD password, respond with configuration and our password + if(memcmp(rx,tag.sectors[1],4) != 0) { + DbpString("Reader password is wrong"); + return; + } + *txlen = 32; + memcpy(tx,tag.sectors[3],4); + } + } + break; + + // Received RWD authentication challenge and respnse + case 64: { + // Store the authentication attempt + if (auth_table_len < (AUTH_TABLE_LENGTH-8)) { + memcpy(auth_table+auth_table_len,rx,8); + auth_table_len += 8; + } + + // Reset the cipher state + hitag2_cipher_reset(&tag,rx); + // Check if the authentication was correct + if(!hitag2_cipher_authenticate(&(tag.cs),rx+4)) { + // The reader failed to authenticate, do nothing + Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed!",rx[0],rx[1],rx[2],rx[3],rx[4],rx[5],rx[6],rx[7]); + return; + } + // Succesful, but commented out reporting back to the Host, this may delay to much. + // Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x OK!",rx[0],rx[1],rx[2],rx[3],rx[4],rx[5],rx[6],rx[7]); + + // Activate encryption algorithm for all further communication + tag.crypto_active = 1; + + // Use the tag password as response + memcpy(tx,tag.sectors[3],4); + *txlen = 32; + } + break; + } + +// LogTrace(rx,nbytes(rxlen),0,0,false); +// LogTrace(tx,nbytes(*txlen),0,0,true); + + if(tag.crypto_active) { + hitag2_cipher_transcrypt(&(tag.cs), tx, *txlen/8, *txlen%8); + } +} + +static void hitag_reader_send_bit(int bit) { + LED_A_ON(); + // Reset clock for the next bit + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + + // Binary puls length modulation (BPLM) is used to encode the data stream + // This means that a transmission of a one takes longer than that of a zero + + // Enable modulation, which means, drop the the field + HIGH(GPIO_SSC_DOUT); + + // Wait for 4-10 times the carrier period + while(AT91C_BASE_TC0->TC_CV < T0*6); + // SpinDelayUs(8*8); + + // Disable modulation, just activates the field again + LOW(GPIO_SSC_DOUT); + + if(bit == 0) { + // Zero bit: |_-| + while(AT91C_BASE_TC0->TC_CV < T0*22); + // SpinDelayUs(16*8); + } else { + // One bit: |_--| + while(AT91C_BASE_TC0->TC_CV < T0*28); + // SpinDelayUs(22*8); + } + LED_A_OFF(); +} + +static void hitag_reader_send_frame(const byte_t* frame, size_t frame_len) +{ + // Send the content of the frame + for(size_t i=0; i> (7-(i%8)))&1); + } + // Send EOF + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + // Enable modulation, which means, drop the the field + HIGH(GPIO_SSC_DOUT); + // Wait for 4-10 times the carrier period + while(AT91C_BASE_TC0->TC_CV < T0*6); + // Disable modulation, just activates the field again + LOW(GPIO_SSC_DOUT); +} + +bool hitag2_password(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { + // Reset the transmission frame length + *txlen = 0; + + // Try to find out which command was send by selecting on length (in bits) + switch (rxlen) { + // No answer, try to resurrect + case 0: { + // Stop if there is no answer (after sending password) + if (bPwd) { + DbpString("Password failed!"); + return false; + } + *txlen = 5; + memcpy(tx,"\xc0",nbytes(*txlen)); + } break; + + // Received UID, tag password + case 32: { + if (!bPwd) { + *txlen = 32; + memcpy(tx,password,4); + bPwd = true; + } else { + DbpString("Password succesful!"); + // We are done... for now + return false; + } + } break; + + // Unexpected response + default: { + Dbprintf("Uknown frame length: %d",rxlen); + return false; + } break; + } + return true; +} + +bool hitag2_authenticate(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { + // Reset the transmission frame length + *txlen = 0; + + // Try to find out which command was send by selecting on length (in bits) + switch (rxlen) { + // No answer, try to resurrect + case 0: { + // Stop if there is no answer while we are in crypto mode (after sending NrAr) + if (bCrypto) { + DbpString("Authentication failed!"); + return false; + } + *txlen = 5; + memcpy(tx,"\xc0",nbytes(*txlen)); + } break; + + // Received UID, crypto tag answer + case 32: { + if (!bCrypto) { + *txlen = 64; + memcpy(tx,NrAr,8); + bCrypto = true; + } else { + DbpString("Authentication succesful!"); + // We are done... for now + return false; + } + } break; + + // Unexpected response + default: { + Dbprintf("Uknown frame length: %d",rxlen); + return false; + } break; + } + + return true; +} + +bool hitag2_test_auth_attempts(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) { + // Reset the transmission frame length + *txlen = 0; + + // Try to find out which command was send by selecting on length (in bits) + switch (rxlen) { + // No answer, try to resurrect + case 0: { + // Stop if there is no answer while we are in crypto mode (after sending NrAr) + if (bCrypto) { + Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed!",NrAr[0],NrAr[1],NrAr[2],NrAr[3],NrAr[4],NrAr[5],NrAr[6],NrAr[7]); + bCrypto = false; + if ((auth_table_pos+8) == auth_table_len) { + return false; + } + auth_table_pos += 8; + memcpy(NrAr,auth_table+auth_table_pos,8); + } + *txlen = 5; + memcpy(tx,"\xc0",nbytes(*txlen)); + } break; + + // Received UID, crypto tag answer, or read block response + case 32: { + if (!bCrypto) { + *txlen = 64; + memcpy(tx,NrAr,8); + bCrypto = true; + } else { + Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x OK",NrAr[0],NrAr[1],NrAr[2],NrAr[3],NrAr[4],NrAr[5],NrAr[6],NrAr[7]); + bCrypto = false; + if ((auth_table_pos+8) == auth_table_len) { + return false; + } + auth_table_pos += 8; + memcpy(NrAr,auth_table+auth_table_pos,8); + } + } break; + + default: { + Dbprintf("Uknown frame length: %d",rxlen); + return false; + } break; + } + + return true; +} + +void SnoopHitag(uint32_t type) { + int frame_count; + int response; + int overflow; + bool rising_edge; + bool reader_frame; + int lastbit; + bool bSkip; + int tag_sof; + byte_t rx[HITAG_FRAME_LEN]; + size_t rxlen=0; + + // Clean up trace and prepare it for storing frames + iso14a_set_tracing(TRUE); + iso14a_clear_trace(); + + auth_table_len = 0; + auth_table_pos = 0; + memset(auth_table, 0x00, AUTH_TABLE_LENGTH); + + DbpString("Starting Hitag2 snoop"); + LED_D_ON(); + + // Set up eavesdropping mode, frequency divisor which will drive the FPGA + // and analog mux selection. + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + RELAY_OFF(); + + // Configure output pin that is connected to the FPGA (for modulating) + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + + // Disable modulation, we are going to eavesdrop, not modulate ;) + LOW(GPIO_SSC_DOUT); + + // Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the reader frames + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + + // Disable timer during configuration + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, + // external trigger rising edge, load RA on rising edge of TIOA. + uint32_t t1_channel_mode = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | AT91C_TC_ETRGEDG_BOTH | AT91C_TC_ABETRG | AT91C_TC_LDRA_BOTH; + AT91C_BASE_TC1->TC_CMR = t1_channel_mode; + + // Enable and reset counter + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Reset the received frame, frame count and timing info + memset(rx,0x00,sizeof(rx)); + frame_count = 0; + response = 0; + overflow = 0; + reader_frame = false; + lastbit = 1; + bSkip = true; + tag_sof = 4; + + while(!BUTTON_PRESS()) { + // Watchdog hit + WDT_HIT(); + + // Receive frame, watch for at most T0*EOF periods + while (AT91C_BASE_TC1->TC_CV < T0*HITAG_T_EOF) { + // Check if rising edge in modulation is detected + if(AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Retrieve the new timing values + int ra = (AT91C_BASE_TC1->TC_RA/T0); + + // Find out if we are dealing with a rising or falling edge + rising_edge = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME) > 0; + + // Shorter periods will only happen with reader frames + if (!reader_frame && rising_edge && ra < HITAG_T_TAG_CAPTURE_ONE_HALF) { + // Switch from tag to reader capture + LED_C_OFF(); + reader_frame = true; + memset(rx,0x00,sizeof(rx)); + rxlen = 0; + } + + // Only handle if reader frame and rising edge, or tag frame and falling edge + if (reader_frame != rising_edge) { + overflow += ra; + continue; + } + + // Add the buffered timing values of earlier captured edges which were skipped + ra += overflow; + overflow = 0; + + if (reader_frame) { + LED_B_ON(); + // Capture reader frame + if(ra >= HITAG_T_STOP) { + if (rxlen != 0) { + //DbpString("wierd0?"); + } + // Capture the T0 periods that have passed since last communication or field drop (reset) + response = (ra - HITAG_T_LOW); + } else if(ra >= HITAG_T_1_MIN ) { + // '1' bit + rx[rxlen / 8] |= 1 << (7-(rxlen%8)); + rxlen++; + } else if(ra >= HITAG_T_0_MIN) { + // '0' bit + rx[rxlen / 8] |= 0 << (7-(rxlen%8)); + rxlen++; + } else { + // Ignore wierd value, is to small to mean anything + } + } else { + LED_C_ON(); + // Capture tag frame (manchester decoding using only falling edges) + if(ra >= HITAG_T_EOF) { + if (rxlen != 0) { + //DbpString("wierd1?"); + } + // Capture the T0 periods that have passed since last communication or field drop (reset) + // We always recieve a 'one' first, which has the falling edge after a half period |-_| + response = ra-HITAG_T_TAG_HALF_PERIOD; + } else if(ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { + // Manchester coding example |-_|_-|-_| (101) + rx[rxlen / 8] |= 0 << (7-(rxlen%8)); + rxlen++; + rx[rxlen / 8] |= 1 << (7-(rxlen%8)); + rxlen++; + } else if(ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { + // Manchester coding example |_-|...|_-|-_| (0...01) + rx[rxlen / 8] |= 0 << (7-(rxlen%8)); + rxlen++; + // We have to skip this half period at start and add the 'one' the second time + if (!bSkip) { + rx[rxlen / 8] |= 1 << (7-(rxlen%8)); + rxlen++; + } + lastbit = !lastbit; + bSkip = !bSkip; + } else if(ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { + // Manchester coding example |_-|_-| (00) or |-_|-_| (11) + if (tag_sof) { + // Ignore bits that are transmitted during SOF + tag_sof--; + } else { + // bit is same as last bit + rx[rxlen / 8] |= lastbit << (7-(rxlen%8)); + rxlen++; + } + } else { + // Ignore wierd value, is to small to mean anything + } + } + } + } + + // Check if frame was captured + if(rxlen > 0) { + frame_count++; + if (!LogTrace(rx,nbytes(rxlen),response,0,reader_frame)) { + DbpString("Trace full"); + break; + } + + // Check if we recognize a valid authentication attempt + if (nbytes(rxlen) == 8) { + // Store the authentication attempt + if (auth_table_len < (AUTH_TABLE_LENGTH-8)) { + memcpy(auth_table+auth_table_len,rx,8); + auth_table_len += 8; + } + } + + // Reset the received frame and response timing info + memset(rx,0x00,sizeof(rx)); + response = 0; + reader_frame = false; + lastbit = 1; + bSkip = true; + tag_sof = 4; + overflow = 0; + + LED_B_OFF(); + LED_C_OFF(); + } else { + // Save the timer overflow, will be 0 when frame was received + overflow += (AT91C_BASE_TC1->TC_CV/T0); + } + // Reset the frame length + rxlen = 0; + // Reset the timer to restart while-loop that receives frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + } + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + LED_D_OFF(); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LED_A_OFF(); + +// Dbprintf("frame received: %d",frame_count); +// Dbprintf("Authentication Attempts: %d",(auth_table_len/8)); +// DbpString("All done"); +} + +void SimulateHitagTag(bool tag_mem_supplied, byte_t* data) { + int frame_count; + int response; + int overflow; + byte_t rx[HITAG_FRAME_LEN]; + size_t rxlen=0; + byte_t tx[HITAG_FRAME_LEN]; + size_t txlen=0; + bool bQuitTraceFull = false; + bQuiet = false; + + // Clean up trace and prepare it for storing frames + iso14a_set_tracing(TRUE); + iso14a_clear_trace(); + auth_table_len = 0; + auth_table_pos = 0; + memset(auth_table, 0x00, AUTH_TABLE_LENGTH); + + DbpString("Starting Hitag2 simulation"); + LED_D_ON(); + hitag2_init(); + + if (tag_mem_supplied) { + DbpString("Loading hitag2 memory..."); + memcpy((byte_t*)tag.sectors,data,48); + } + + uint32_t block = 0; + for (size_t i=0; i<12; i++) { + for (size_t j=0; j<4; j++) { + block <<= 8; + block |= tag.sectors[i][j]; + } + Dbprintf("| %d | %08x |",i,block); + } + + // Set up simulator mode, frequency divisor which will drive the FPGA + // and analog mux selection. + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + RELAY_OFF(); + + // Configure output pin that is connected to the FPGA (for modulating) + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + + // Disable modulation at default, which means release resistance + LOW(GPIO_SSC_DOUT); + + // Enable Peripheral Clock for TIMER_CLOCK0, used to measure exact timing before answering + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); + + // Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the reader frames + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + + // Disable timer during configuration + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, + // external trigger rising edge, load RA on rising edge of TIOA. + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | AT91C_TC_ETRGEDG_RISING | AT91C_TC_ABETRG | AT91C_TC_LDRA_RISING; + + // Enable and reset counter + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Reset the received frame, frame count and timing info + memset(rx,0x00,sizeof(rx)); + frame_count = 0; + response = 0; + overflow = 0; + + while(!BUTTON_PRESS()) { + // Watchdog hit + WDT_HIT(); + + // Receive frame, watch for at most T0*EOF periods + while (AT91C_BASE_TC1->TC_CV < T0*HITAG_T_EOF) { + // Check if rising edge in modulation is detected + if(AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Retrieve the new timing values + int ra = (AT91C_BASE_TC1->TC_RA/T0) + overflow; + overflow = 0; + + // Reset timer every frame, we have to capture the last edge for timing + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + LED_B_ON(); + + // Capture reader frame + if(ra >= HITAG_T_STOP) { + if (rxlen != 0) { + //DbpString("wierd0?"); + } + // Capture the T0 periods that have passed since last communication or field drop (reset) + response = (ra - HITAG_T_LOW); + } else if(ra >= HITAG_T_1_MIN ) { + // '1' bit + rx[rxlen / 8] |= 1 << (7-(rxlen%8)); + rxlen++; + } else if(ra >= HITAG_T_0_MIN) { + // '0' bit + rx[rxlen / 8] |= 0 << (7-(rxlen%8)); + rxlen++; + } else { + // Ignore wierd value, is to small to mean anything + } + } + } + + // Check if frame was captured + if(rxlen > 4) { + frame_count++; + if (!bQuiet) { + if (!LogTrace(rx,nbytes(rxlen),response,0,true)) { + DbpString("Trace full"); + if (bQuitTraceFull) { + break; + } else { + bQuiet = true; + } + } + } + + // Disable timer 1 with external trigger to avoid triggers during our own modulation + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // Process the incoming frame (rx) and prepare the outgoing frame (tx) + hitag2_handle_reader_command(rx,rxlen,tx,&txlen); + + // Wait for HITAG_T_WAIT_1 carrier periods after the last reader bit, + // not that since the clock counts since the rising edge, but T_Wait1 is + // with respect to the falling edge, we need to wait actually (T_Wait1 - T_Low) + // periods. The gap time T_Low varies (4..10). All timer values are in + // terms of T0 units + while(AT91C_BASE_TC0->TC_CV < T0*(HITAG_T_WAIT_1-HITAG_T_LOW)); + + // Send and store the tag answer (if there is any) + if (txlen) { + // Transmit the tag frame + hitag_send_frame(tx,txlen); + // Store the frame in the trace + if (!bQuiet) { + if (!LogTrace(tx,nbytes(txlen),0,0,false)) { + DbpString("Trace full"); + if (bQuitTraceFull) { + break; + } else { + bQuiet = true; + } + } + } + } + + // Reset the received frame and response timing info + memset(rx,0x00,sizeof(rx)); + response = 0; + + // Enable and reset external trigger in timer for capturing future frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + LED_B_OFF(); + } + // Reset the frame length + rxlen = 0; + // Save the timer overflow, will be 0 when frame was received + overflow += (AT91C_BASE_TC1->TC_CV/T0); + // Reset the timer to restart while-loop that receives frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + } + LED_B_OFF(); + LED_D_OFF(); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); +// Dbprintf("frame received: %d",frame_count); +// Dbprintf("Authentication Attempts: %d",(auth_table_len/8)); +// DbpString("All done"); +} + +void ReaderHitag(hitag_function htf, hitag_data* htd) { + int frame_count; + int response; + byte_t rx[HITAG_FRAME_LEN]; + size_t rxlen=0; + byte_t txbuf[HITAG_FRAME_LEN]; + byte_t* tx = txbuf; + size_t txlen=0; + int lastbit; + bool bSkip; + int reset_sof; + int tag_sof; + int t_wait = HITAG_T_WAIT_MAX; + bool bStop; + bool bQuitTraceFull = false; + + // Clean up trace and prepare it for storing frames + iso14a_set_tracing(TRUE); + iso14a_clear_trace(); + DbpString("Starting Hitag reader family"); + + // Check configuration + switch(htf) { + case RHT2F_PASSWORD: { + Dbprintf("List identifier in password mode"); + memcpy(password,htd->pwd.password,4); + bQuitTraceFull = false; + bQuiet = false; + bPwd = false; + } break; + case RHT2F_AUTHENTICATE: { + DbpString("Authenticating in crypto mode"); + memcpy(NrAr,htd->auth.NrAr,8); + Dbprintf("Reader-challenge:"); + Dbhexdump(8,NrAr,false); + bQuiet = false; + bCrypto = false; + bQuitTraceFull = true; + } break; + + case RHT2F_TEST_AUTH_ATTEMPTS: { + Dbprintf("Testing %d authentication attempts",(auth_table_len/8)); + auth_table_pos = 0; + memcpy(NrAr,auth_table,8); + bQuitTraceFull = false; + bQuiet = false; + bCrypto = false; + } break; + + default: { + Dbprintf("Error, unknown function: %d",htf); + return; + } break; + } + + LED_D_ON(); + hitag2_init(); + + // Configure output and enable pin that is connected to the FPGA (for modulating) + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + + // Set fpga in edge detect with reader field, we can modulate as reader now + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD); + + // Set Frequency divisor which will drive the FPGA and analog mux selection + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + RELAY_OFF(); + + // Disable modulation at default, which means enable the field + LOW(GPIO_SSC_DOUT); + + // Give it a bit of time for the resonant antenna to settle. + SpinDelay(30); + + // Enable Peripheral Clock for TIMER_CLOCK0, used to measure exact timing before answering + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); + + // Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the tag frames + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + + // Disable timer during configuration + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, + // external trigger rising edge, load RA on falling edge of TIOA. + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | AT91C_TC_ETRGEDG_FALLING | AT91C_TC_ABETRG | AT91C_TC_LDRA_FALLING; + + // Enable and reset counters + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Reset the received frame, frame count and timing info + frame_count = 0; + response = 0; + lastbit = 1; + bStop = false; + + // Tag specific configuration settings (sof, timings, etc.) + if (htf < 10){ + // hitagS settings + reset_sof = 1; + t_wait = 200; + DbpString("Configured for hitagS reader"); + } else if (htf < 20) { + // hitag1 settings + reset_sof = 1; + t_wait = 200; + DbpString("Configured for hitag1 reader"); + } else if (htf < 30) { + // hitag2 settings + reset_sof = 4; + t_wait = HITAG_T_WAIT_2; + DbpString("Configured for hitag2 reader"); + } else { + Dbprintf("Error, unknown hitag reader type: %d",htf); + return; + } + + while(!bStop && !BUTTON_PRESS()) { + // Watchdog hit + WDT_HIT(); + + // Check if frame was captured and store it + if(rxlen > 0) { + frame_count++; + if (!bQuiet) { + if (!LogTrace(rx,nbytes(rxlen),response,0,false)) { + DbpString("Trace full"); + if (bQuitTraceFull) { + break; + } else { + bQuiet = true; + } + } + } + } + + // By default reset the transmission buffer + tx = txbuf; + switch(htf) { + case RHT2F_PASSWORD: { + bStop = !hitag2_password(rx,rxlen,tx,&txlen); + } break; + case RHT2F_AUTHENTICATE: { + bStop = !hitag2_authenticate(rx,rxlen,tx,&txlen); + } break; + case RHT2F_TEST_AUTH_ATTEMPTS: { + bStop = !hitag2_test_auth_attempts(rx,rxlen,tx,&txlen); + } break; + default: { + Dbprintf("Error, unknown function: %d",htf); + return; + } break; + } + + // Send and store the reader command + // Disable timer 1 with external trigger to avoid triggers during our own modulation + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // Wait for HITAG_T_WAIT_2 carrier periods after the last tag bit before transmitting, + // Since the clock counts since the last falling edge, a 'one' means that the + // falling edge occured halfway the period. with respect to this falling edge, + // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. + // All timer values are in terms of T0 units + while(AT91C_BASE_TC0->TC_CV < T0*(t_wait+(HITAG_T_TAG_HALF_PERIOD*lastbit))); + + // Transmit the reader frame + hitag_reader_send_frame(tx,txlen); + + // Enable and reset external trigger in timer for capturing future frames + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // Add transmitted frame to total count + if(txlen > 0) { + frame_count++; + if (!bQuiet) { + // Store the frame in the trace + if (!LogTrace(tx,nbytes(txlen),HITAG_T_WAIT_2,0,true)) { + if (bQuitTraceFull) { + break; + } else { + bQuiet = true; + } + } + } + } + + // Reset values for receiving frames + memset(rx,0x00,sizeof(rx)); + rxlen = 0; + lastbit = 1; + bSkip = true; + tag_sof = reset_sof; + response = 0; + + // Receive frame, watch for at most T0*EOF periods + while (AT91C_BASE_TC1->TC_CV < T0*HITAG_T_WAIT_MAX) { + // Check if falling edge in tag modulation is detected + if(AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Retrieve the new timing values + int ra = (AT91C_BASE_TC1->TC_RA/T0); + + // Reset timer every frame, we have to capture the last edge for timing + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + + LED_B_ON(); + + // Capture tag frame (manchester decoding using only falling edges) + if(ra >= HITAG_T_EOF) { + if (rxlen != 0) { + //DbpString("wierd1?"); + } + // Capture the T0 periods that have passed since last communication or field drop (reset) + // We always recieve a 'one' first, which has the falling edge after a half period |-_| + response = ra-HITAG_T_TAG_HALF_PERIOD; + } else if(ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { + // Manchester coding example |-_|_-|-_| (101) + rx[rxlen / 8] |= 0 << (7-(rxlen%8)); + rxlen++; + rx[rxlen / 8] |= 1 << (7-(rxlen%8)); + rxlen++; + } else if(ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { + // Manchester coding example |_-|...|_-|-_| (0...01) + rx[rxlen / 8] |= 0 << (7-(rxlen%8)); + rxlen++; + // We have to skip this half period at start and add the 'one' the second time + if (!bSkip) { + rx[rxlen / 8] |= 1 << (7-(rxlen%8)); + rxlen++; + } + lastbit = !lastbit; + bSkip = !bSkip; + } else if(ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { + // Manchester coding example |_-|_-| (00) or |-_|-_| (11) + if (tag_sof) { + // Ignore bits that are transmitted during SOF + tag_sof--; + } else { + // bit is same as last bit + rx[rxlen / 8] |= lastbit << (7-(rxlen%8)); + rxlen++; + } + } else { + // Ignore wierd value, is to small to mean anything + } + } + + // We can break this loop if we received the last bit from a frame + if (AT91C_BASE_TC1->TC_CV > T0*HITAG_T_EOF) { + if (rxlen>0) break; + } + } + } + LED_B_OFF(); + LED_D_OFF(); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + +// Dbprintf("frame received: %d",frame_count); +// DbpString("All done"); +} diff --git a/armsrc/iclass.c b/armsrc/iclass.c index ced29424f..c673bb54a 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -691,7 +691,7 @@ void RAMFUNC SnoopIClass(void) // reset traceLen to 0 iso14a_set_tracing(TRUE); - iso14a_clear_tracelen(); + iso14a_clear_trace(); iso14a_set_trigger(FALSE); // The DMA buffer, used to stream samples from the FPGA diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 530418e5e..af7b35457 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -21,7 +21,7 @@ #include "mifareutil.h" static uint32_t iso14a_timeout; -uint8_t *trace = (uint8_t *) BigBuf; +uint8_t *trace = (uint8_t *) BigBuf+TRACE_OFFSET; int traceLen = 0; int rsamples = 0; int tracing = TRUE; @@ -68,12 +68,15 @@ void iso14a_set_trigger(int enable) { trigger = enable; } -void iso14a_clear_tracelen(void) { +void iso14a_clear_trace(void) { + memset(trace, 0x44, TRACE_SIZE); traceLen = 0; } + void iso14a_set_tracing(int enable) { tracing = enable; } + void iso14a_set_timeout(uint32_t timeout) { iso14a_timeout = timeout; } @@ -580,8 +583,7 @@ void RAMFUNC SnoopIso14443a(uint8_t param) { LEDsoff(); // init trace buffer - traceLen = 0; - memset(trace, 0x44, TRACE_SIZE); + iso14a_clear_trace(); // We won't start recording the frames that we acquire until we trigger; // a good trigger condition to get started is probably when we see a @@ -905,8 +907,7 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd) { // Enable and clear the trace tracing = TRUE; - traceLen = 0; - memset(trace, 0x44, TRACE_SIZE); + iso14a_clear_trace(); // This function contains the tag emulation uint8_t sak; @@ -2379,8 +2380,7 @@ void RAMFUNC SniffMifare(uint8_t param) { // C(red) A(yellow) B(green) LEDsoff(); // init trace buffer - traceLen = 0; - memset(trace, 0x44, TRACE_SIZE); + iso14a_clear_trace(); // The command (reader -> tag) that we're receiving. // The length of a received command will in most cases be no more than 18 bytes. diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 47364451b..b73ee0147 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -1102,7 +1102,7 @@ void ReaderIso15693(uint32_t parameter) Dbprintf("%d octets read from IDENTIFY request:", answerLen1); DbdecodeIso15693Answer(answerLen1,answer1); - Dbhexdump(answerLen1,answer1); + Dbhexdump(answerLen1,answer1,true); // UID is reverse if (answerLen1>=12) @@ -1113,11 +1113,11 @@ void ReaderIso15693(uint32_t parameter) Dbprintf("%d octets read from SELECT request:", answerLen2); DbdecodeIso15693Answer(answerLen2,answer2); - Dbhexdump(answerLen2,answer2); + Dbhexdump(answerLen2,answer2,true); Dbprintf("%d octets read from XXX request:", answerLen3); DbdecodeIso15693Answer(answerLen3,answer3); - Dbhexdump(answerLen3,answer3); + Dbhexdump(answerLen3,answer3,true); // read all pages @@ -1130,7 +1130,7 @@ void ReaderIso15693(uint32_t parameter) if (answerLen2>0) { Dbprintf("READ SINGLE BLOCK %d returned %d octets:",i,answerLen2); DbdecodeIso15693Answer(answerLen2,answer2); - Dbhexdump(answerLen2,answer2); + Dbhexdump(answerLen2,answer2,true); if ( *((uint32_t*) answer2) == 0x07160101 ) break; // exit on NoPageErr } i++; @@ -1264,7 +1264,7 @@ void DirectTag15693Command(uint32_t datalen,uint32_t speed, uint32_t recv, uint8 if (DEBUG) { Dbprintf("SEND"); - Dbhexdump(datalen,data); + Dbhexdump(datalen,data,true); } recvlen=SendDataTag(data,datalen,1,speed,(recv?&recvbuf:NULL)); @@ -1280,7 +1280,7 @@ void DirectTag15693Command(uint32_t datalen,uint32_t speed, uint32_t recv, uint8 if (DEBUG) { Dbprintf("RECV"); DbdecodeIso15693Answer(recvlen,recvbuf); - Dbhexdump(recvlen,recvbuf); + Dbhexdump(recvlen,recvbuf,true); } } diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 5ef01dcf2..1b5f220e3 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -433,17 +433,17 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol) { int i; uint8_t *tab = (uint8_t *)BigBuf; - - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_SIMULATOR); - + + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK; - + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK; - + #define SHORT_COIL() LOW(GPIO_SSC_DOUT) #define OPEN_COIL() HIGH(GPIO_SSC_DOUT) - + i = 0; for(;;) { while(!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { @@ -453,18 +453,18 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol) } WDT_HIT(); } - + if (ledcontrol) LED_D_ON(); - + if(tab[i]) OPEN_COIL(); else SHORT_COIL(); - + if (ledcontrol) LED_D_OFF(); - + while(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) { if(BUTTON_PRESS()) { DbpString("Stopped"); @@ -472,7 +472,7 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol) } WDT_HIT(); } - + i++; if(i == period) { i = 0; @@ -484,197 +484,9 @@ void SimulateTagLowFrequency(int period, int gap, int ledcontrol) } } -/* Provides a framework for bidirectional LF tag communication - * Encoding is currently Hitag2, but the general idea can probably - * be transferred to other encodings. - * - * The new FPGA code will, for the LF simulator mode, give on SSC_FRAME - * (PA15) a thresholded version of the signal from the ADC. Setting the - * ADC path to the low frequency peak detection signal, will enable a - * somewhat reasonable receiver for modulation on the carrier signal - * that is generated by the reader. The signal is low when the reader - * field is switched off, and high when the reader field is active. Due - * to the way that the signal looks like, mostly only the rising edge is - * useful, your mileage may vary. - * - * Neat perk: PA15 can not only be used as a bit-banging GPIO, but is also - * TIOA1, which can be used as the capture input for timer 1. This should - * make it possible to measure the exact edge-to-edge time, without processor - * intervention. - * - * Arguments: divisor is the divisor to be sent to the FPGA (e.g. 95 for 125kHz) - * t0 is the carrier frequency cycle duration in terms of MCK (384 for 125kHz) - * - * The following defines are in carrier periods: - */ -#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */ -#define HITAG_T_1_MIN 24 /* T[1] should be 26..30 */ -#define HITAG_T_EOF 40 /* T_EOF should be > 36 */ -#define HITAG_T_WRESP 208 /* T_wresp should be 204..212 */ - -static void hitag_handle_frame(int t0, int frame_len, char *frame); -//#define DEBUG_RA_VALUES 1 #define DEBUG_FRAME_CONTENTS 1 void SimulateTagLowFrequencyBidir(int divisor, int t0) { -#if DEBUG_RA_VALUES || DEBUG_FRAME_CONTENTS - int i = 0; -#endif - char frame[10]; - int frame_pos=0; - - DbpString("Starting Hitag2 emulator, press button to end"); - hitag2_init(); - - /* Set up simulator mode, frequency divisor which will drive the FPGA - * and analog mux selection. - */ - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_SIMULATOR); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, divisor); - SetAdcMuxFor(GPIO_MUXSEL_LOPKD); - RELAY_OFF(); - - /* Set up Timer 1: - * Capture mode, timer source MCK/2 (TIMER_CLOCK1), TIOA is external trigger, - * external trigger rising edge, load RA on rising edge of TIOA, load RB on rising - * edge of TIOA. Assign PA15 to TIOA1 (peripheral B) - */ - - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | - AT91C_TC_ETRGEDG_RISING | - AT91C_TC_ABETRG | - AT91C_TC_LDRA_RISING | - AT91C_TC_LDRB_RISING; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | - AT91C_TC_SWTRG; - - /* calculate the new value for the carrier period in terms of TC1 values */ - t0 = t0/2; - - int overflow = 0; - while(!BUTTON_PRESS()) { - WDT_HIT(); - if(AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { - int ra = AT91C_BASE_TC1->TC_RA; - if((ra > t0*HITAG_T_EOF) | overflow) ra = t0*HITAG_T_EOF+1; -#if DEBUG_RA_VALUES - if(ra > 255 || overflow) ra = 255; - ((char*)BigBuf)[i] = ra; - i = (i+1) % 8000; -#endif - - if(overflow || (ra > t0*HITAG_T_EOF) || (ra < t0*HITAG_T_0_MIN)) { - /* Ignore */ - } else if(ra >= t0*HITAG_T_1_MIN ) { - /* '1' bit */ - if(frame_pos < 8*sizeof(frame)) { - frame[frame_pos / 8] |= 1<<( 7-(frame_pos%8) ); - frame_pos++; - } - } else if(ra >= t0*HITAG_T_0_MIN) { - /* '0' bit */ - if(frame_pos < 8*sizeof(frame)) { - frame[frame_pos / 8] |= 0<<( 7-(frame_pos%8) ); - frame_pos++; - } - } - - overflow = 0; - LED_D_ON(); - } else { - if(AT91C_BASE_TC1->TC_CV > t0*HITAG_T_EOF) { - /* Minor nuisance: In Capture mode, the timer can not be - * stopped by a Compare C. There's no way to stop the clock - * in software, so we'll just have to note the fact that an - * overflow happened and the next loaded timer value might - * have wrapped. Also, this marks the end of frame, and the - * still running counter can be used to determine the correct - * time for the start of the reply. - */ - overflow = 1; - - if(frame_pos > 0) { - /* Have a frame, do something with it */ -#if DEBUG_FRAME_CONTENTS - ((char*)BigBuf)[i++] = frame_pos; - memcpy( ((char*)BigBuf)+i, frame, 7); - i+=7; - i = i % sizeof(BigBuf); -#endif - hitag_handle_frame(t0, frame_pos, frame); - memset(frame, 0, sizeof(frame)); - } - frame_pos = 0; - - } - LED_D_OFF(); - } - } - DbpString("All done"); -} - -static void hitag_send_bit(int t0, int bit) { - if(bit == 1) { - /* Manchester: Loaded, then unloaded */ - LED_A_ON(); - SHORT_COIL(); - while(AT91C_BASE_TC1->TC_CV < t0*15); - OPEN_COIL(); - while(AT91C_BASE_TC1->TC_CV < t0*31); - LED_A_OFF(); - } else if(bit == 0) { - /* Manchester: Unloaded, then loaded */ - LED_B_ON(); - OPEN_COIL(); - while(AT91C_BASE_TC1->TC_CV < t0*15); - SHORT_COIL(); - while(AT91C_BASE_TC1->TC_CV < t0*31); - LED_B_OFF(); - } - AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; /* Reset clock for the next bit */ - -} -static void hitag_send_frame(int t0, int frame_len, const char const * frame, int fdt) -{ - OPEN_COIL(); - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - - /* Wait for HITAG_T_WRESP carrier periods after the last reader bit, - * not that since the clock counts since the rising edge, but T_wresp is - * with respect to the falling edge, we need to wait actually (T_wresp - T_g) - * periods. The gap time T_g varies (4..10). - */ - while(AT91C_BASE_TC1->TC_CV < t0*(fdt-8)); - - int saved_cmr = AT91C_BASE_TC1->TC_CMR; - AT91C_BASE_TC1->TC_CMR &= ~AT91C_TC_ETRGEDG; /* Disable external trigger for the clock */ - AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; /* Reset the clock and use it for response timing */ - - int i; - for(i=0; i<5; i++) - hitag_send_bit(t0, 1); /* Start of frame */ - - for(i=0; iTC_CMR = saved_cmr; -} - -/* Callback structure to cleanly separate tag emulation code from the radio layer. */ -static int hitag_cb(const char* response_data, const int response_length, const int fdt, void *cb_cookie) -{ - hitag_send_frame(*(int*)cb_cookie, response_length, response_data, fdt); - return 0; -} -/* Frame length in bits, frame contents in MSBit first format */ -static void hitag_handle_frame(int t0, int frame_len, char *frame) -{ - hitag2_handle_command(frame, frame_len, hitag_cb, &t0); } // compose fc/8 fc/10 waveform diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index daecf541e..f18b75a08 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -35,7 +35,7 @@ void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) pcs = &mpcs; // clear trace - iso14a_clear_tracelen(); + iso14a_clear_trace(); // iso14a_set_tracing(false); iso14443a_setup(); @@ -115,7 +115,7 @@ void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) pcs = &mpcs; // clear trace - iso14a_clear_tracelen(); + iso14a_clear_trace(); // iso14a_set_tracing(false); iso14443a_setup(); @@ -213,7 +213,7 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) pcs = &mpcs; // clear trace - iso14a_clear_tracelen(); + iso14a_clear_trace(); // iso14a_set_tracing(false); iso14443a_setup(); @@ -311,7 +311,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) for (i = 0; i < NES_MAX_INFO + 1; i++) nvectorcount[i] = 11; // 11 - empty block; // clear trace - iso14a_clear_tracelen(); + iso14a_clear_trace(); iso14a_set_tracing(false); iso14443a_setup(); @@ -539,7 +539,7 @@ void MifareChkKeys(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) MF_DBGLEVEL = MF_DBG_NONE; // clear trace - iso14a_clear_tracelen(); + iso14a_clear_trace(); iso14a_set_tracing(TRUE); iso14443a_setup(); @@ -641,7 +641,7 @@ void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai uint8_t uid[8]; // clear trace - iso14a_clear_tracelen(); + iso14a_clear_trace(); iso14a_set_tracing(false); iso14443a_setup(); @@ -759,7 +759,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai if (workFlags & 0x08) { // clear trace - iso14a_clear_tracelen(); + iso14a_clear_trace(); iso14a_set_tracing(TRUE); iso14443a_setup(); @@ -890,7 +890,7 @@ void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai if (workFlags & 0x08) { // clear trace - iso14a_clear_tracelen(); + iso14a_clear_trace(); iso14a_set_tracing(TRUE); iso14443a_setup(); diff --git a/common/usb.c b/common/usb.c index 3f30d97f6..e1fc9b25b 100644 --- a/common/usb.c +++ b/common/usb.c @@ -401,12 +401,15 @@ void UsbSendPacket(uint8_t *packet, int len) } AT91C_BASE_UDP->UDP_CSR[2] |= AT91C_UDP_TXPKTRDY; - while(!(AT91C_BASE_UDP->UDP_CSR[2] & AT91C_UDP_TXCOMP)) - ; + while(!(AT91C_BASE_UDP->UDP_CSR[2] & AT91C_UDP_TXCOMP)) { + WDT_HIT(); + } + AT91C_BASE_UDP->UDP_CSR[2] &= ~AT91C_UDP_TXCOMP; - while(AT91C_BASE_UDP->UDP_CSR[2] & AT91C_UDP_TXCOMP) - ; + while(AT91C_BASE_UDP->UDP_CSR[2] & AT91C_UDP_TXCOMP) { + WDT_HIT(); + } len -= thisTime; packet += thisTime; @@ -426,8 +429,9 @@ static void HandleRxdData(void) } AT91C_BASE_UDP->UDP_CSR[1] &= ~AT91C_UDP_RX_DATA_BK0; - while(AT91C_BASE_UDP->UDP_CSR[1] & AT91C_UDP_RX_DATA_BK0) - ; + while(AT91C_BASE_UDP->UDP_CSR[1] & AT91C_UDP_RX_DATA_BK0) { + WDT_HIT(); + } if(UsbSoFarCount >= 64) { UsbPacketReceived(UsbBuffer, UsbSoFarCount); @@ -444,14 +448,17 @@ static void HandleRxdData(void) } AT91C_BASE_UDP->UDP_CSR[1] &= ~AT91C_UDP_RX_DATA_BK1; - while(AT91C_BASE_UDP->UDP_CSR[1] & AT91C_UDP_RX_DATA_BK1) - ; - + while(AT91C_BASE_UDP->UDP_CSR[1] & AT91C_UDP_RX_DATA_BK1) { + WDT_HIT(); + } + if(UsbSoFarCount >= 64) { UsbPacketReceived(UsbBuffer, UsbSoFarCount); UsbSoFarCount = 0; } } + + WDT_HIT(); } void UsbStart(void) diff --git a/fpga/Makefile b/fpga/Makefile index d598c8ded..8759c22ca 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -6,7 +6,7 @@ clean: $(DELETE) fpga.map fpga.ngc fpga_ngdbuild.xrpt fpga.pcf fpga-placed_pad.csv fpga-placed.ptwx fpga.rbt xlnx_auto_0_xdb $(DELETE) fpga.bld fpga.mrp fpga.ngc_xst.xrpt fpga.ngm fpga-placed.ncd fpga-placed_pad.txt fpga-placed.unroutes fpga_summary.xml netlist.lst xst -fpga.ngc: fpga.v fpga.ucf xst.scr util.v lo_simulate.v lo_read.v lo_passthru.v hi_simulate.v hi_read_tx.v hi_read_rx_xcorr.v hi_iso14443a.v +fpga.ngc: fpga.v fpga.ucf xst.scr util.v lo_edge_detect.v lo_read.v lo_passthru.v hi_simulate.v hi_read_tx.v hi_read_rx_xcorr.v hi_iso14443a.v $(DELETE) fpga.ngc $(XILINX_TOOLS_PREFIX)xst -ifn xst.scr diff --git a/fpga/fpga.bit b/fpga/fpga.bit index 4e65514d68819bd63e5571f1c0fcb837cd8bb7b4..3ea1560ddb9e7ebc124ad9d5471441d3df5b09d5 100644 GIT binary patch literal 42172 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 literal 42172 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%0Afhp-gWu?AZOKM!szrsE6-3wR=wPeJk* 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 diff --git a/fpga/fpga.v b/fpga/fpga.v index 4002945ba..d2d84a32e 100644 --- a/fpga/fpga.v +++ b/fpga/fpga.v @@ -14,7 +14,7 @@ `include "lo_read.v" `include "lo_passthru.v" -`include "lo_simulate.v" +`include "lo_edge_detect.v" `include "hi_read_tx.v" `include "hi_read_rx_xcorr.v" `include "hi_simulate.v" @@ -111,6 +111,10 @@ assign hi_read_rx_xcorr_quarter = conf_word[2]; wire [2:0] hi_simulate_mod_type; assign hi_simulate_mod_type = conf_word[2:0]; +// For the high-frequency simulated tag: what kind of modulation to use. +wire lf_field; +assign lf_field = conf_word[0]; + //----------------------------------------------------------------------------- // And then we instantiate the modules corresponding to each of the FPGA's // major modes, and use muxes to connect the outputs of the active mode to @@ -136,13 +140,14 @@ lo_passthru lp( lp_dbg, divisor ); -lo_simulate ls( +lo_edge_detect ls( pck0, ck_1356meg, ck_1356megb, ls_pwr_lo, ls_pwr_hi, ls_pwr_oe1, ls_pwr_oe2, ls_pwr_oe3, ls_pwr_oe4, adc_d, ls_adc_clk, ls_ssp_frame, ls_ssp_din, ssp_dout, ls_ssp_clk, cross_hi, cross_lo, - ls_dbg, divisor + ls_dbg, divisor, + lf_field ); hi_read_tx ht( diff --git a/fpga/lo_edge_detect.v b/fpga/lo_edge_detect.v new file mode 100644 index 000000000..8458ee692 --- /dev/null +++ b/fpga/lo_edge_detect.v @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// The way that we connect things in low-frequency simulation mode. In this +// case just pass everything through to the ARM, which can bit-bang this +// (because it is so slow). +// +// Jonathan Westhues, April 2006 +//----------------------------------------------------------------------------- + +module lo_edge_detect( + pck0, ck_1356meg, ck_1356megb, + pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4, + adc_d, adc_clk, + ssp_frame, ssp_din, ssp_dout, ssp_clk, + cross_hi, cross_lo, + dbg, + divisor, + lf_field +); + input pck0, ck_1356meg, ck_1356megb; + output pwr_lo, pwr_hi, pwr_oe1, pwr_oe2, pwr_oe3, pwr_oe4; + input [7:0] adc_d; + output adc_clk; + input ssp_dout; + output ssp_frame, ssp_din, ssp_clk; + input cross_hi, cross_lo; + output dbg; + input [7:0] divisor; + input lf_field; + +// Divide the clock to be used for the ADC +reg [7:0] pck_divider; +reg clk_state; + +wire tag_modulation; +assign tag_modulation = ssp_dout & !lf_field; +wire reader_modulation; +assign reader_modulation = !ssp_dout & lf_field & clk_state; + +// No logic, straight through. +assign pwr_oe1 = 1'b0; // not used in LF mode +assign pwr_oe2 = tag_modulation; +assign pwr_oe3 = tag_modulation; +assign pwr_oe4 = tag_modulation; +assign ssp_clk = cross_lo; +assign pwr_lo = reader_modulation; +assign pwr_hi = 1'b0; +assign dbg = ssp_frame; + +always @(posedge pck0) +begin + if(pck_divider == divisor[7:0]) + begin + pck_divider <= 8'd0; + clk_state = !clk_state; + end + else + begin + pck_divider <= pck_divider + 1; + end +end + +assign adc_clk = ~clk_state; + +// Toggle the output with hysteresis +// Set to high if the ADC value is above 200 +// Set to low if the ADC value is below 64 +reg is_high; +reg is_low; +reg output_state; + +always @(posedge pck0) +begin + if((pck_divider == 8'd7) && !clk_state) begin + is_high = (adc_d >= 8'd190); + is_low = (adc_d <= 8'd70); + end +end + +always @(posedge is_high or posedge is_low) +begin + if(is_high) + output_state <= 1'd1; + else if(is_low) + output_state <= 1'd0; +end + +assign ssp_frame = output_state; + +endmodule + diff --git a/include/common.h b/include/common.h index fd1a6a56c..cfce1b9f0 100644 --- a/include/common.h +++ b/include/common.h @@ -13,6 +13,7 @@ #define __COMMON_H #include +typedef unsigned char byte_t; //----------------------------------------------------------------------------- // ISO 14443A diff --git a/armsrc/hitag2.h b/include/hitag2.h similarity index 50% rename from armsrc/hitag2.h rename to include/hitag2.h index 7606e2ceb..3ed2b9fcf 100644 --- a/armsrc/hitag2.h +++ b/include/hitag2.h @@ -1,19 +1,33 @@ //----------------------------------------------------------------------------- -// (c) 2009 Henryk Plötz +// (c) 2012 Roel Verdult // // 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. //----------------------------------------------------------------------------- -// Hitag2 emulation public interface +// Hitag2 type prototyping //----------------------------------------------------------------------------- -#ifndef __HITAG2_H -#define __HITAG2_H +#ifndef _HITAG2_H_ +#define _HITAG2_H_ -typedef int (*hitag2_response_callback_t)(const char* response_data, const int response_length, const int fdt, void *cb_cookie); +typedef enum { + RHT2F_PASSWORD = 21, + RHT2F_AUTHENTICATE = 22, + RHT2F_TEST_AUTH_ATTEMPTS = 25, +} hitag_function; -extern int hitag2_init(void); -extern int hitag2_handle_command(const char* data, const int length, hitag2_response_callback_t cb, void *cb_cookie); +typedef struct { + byte_t password[4]; +} PACKED rht2d_password; + +typedef struct { + byte_t NrAr[8]; +} PACKED rht2d_authenticate; + +typedef union { + rht2d_password pwd; + rht2d_authenticate auth; +} hitag_data; #endif diff --git a/include/usb_cmd.h b/include/usb_cmd.h index a209497fd..a7552b3ec 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -86,6 +86,12 @@ typedef struct { #define CMD_ISO_15693_COMMAND_DONE 0x0314 #define CMD_ISO_15693_FIND_AFI 0x0315 #define CMD_ISO_15693_DEBUG 0x0316 + +// For Hitag2 transponders +#define CMD_SNOOP_HITAG 0x0370 +#define CMD_SIMULATE_HITAG 0x0371 +#define CMD_READER_HITAG 0x0372 + #define CMD_SIMULATE_TAG_HF_LISTEN 0x0380 #define CMD_SIMULATE_TAG_ISO_14443 0x0381 #define CMD_SNOOP_ISO_14443 0x0382