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 e2d71324..715eba0a 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 d3544861..dac87677 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 6e98311a..f3f90237 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 c199b9a8..a719f5ed 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 15daa25e..75513232 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; -} - -int hitag2_init(void) -{ - memcpy(&tag, &resetdata, sizeof(tag)); - hitag2_reset(); - return 0; -} - -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; - } - } +//#define TRACE_LENGTH 3000 +//uint8_t *trace = (uint8_t *) BigBuf; +//int traceLen = 0; +//int rsamples = 0; - 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; - } +#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; - 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); +} + +int hitag2_reset(void) +{ + tag.state = TAG_STATE_RESET; + tag.crypto_active = 0; + return 0; +} -/* Cipher/tag glue code: */ +int hitag2_init(void) +{ + memcpy(&tag, &resetdata, sizeof(tag)); + hitag2_reset(); + return 0; +} -static void hitag2_cipher_reset(struct hitag2_tag *tag, const char *iv) +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 ced29424..c673bb54 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 530418e5..af7b3545 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 47364451..b73ee014 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 5ef01dcf..1b5f220e 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 daecf541..f18b75a0 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 3f30d97f..e1fc9b25 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 d598c8de..8759c22c 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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 Cu5t7L literal 42172 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 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 fd1a6a56..cfce1b9f 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 7606e2ce..3ed2b9fc 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 a209497f..a7552b3e 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 -- 2.39.2