From c339035ec5e5dbd7e363aeb1db964864e5feca33 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 6 Aug 2018 15:05:36 +0200 Subject: [PATCH] Revert "WIP: Clean Legic Reader" --- armsrc/fpgaloader.h | 1 - armsrc/legicrf.c | 1924 +++++++++++++++++++++++++++++++++++-------- armsrc/legicrf.h | 35 +- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_read_tx.v | 17 +- 5 files changed, 1608 insertions(+), 369 deletions(-) diff --git a/armsrc/fpgaloader.h b/armsrc/fpgaloader.h index 21731b571..66cca0510 100644 --- a/armsrc/fpgaloader.h +++ b/armsrc/fpgaloader.h @@ -74,7 +74,6 @@ extern void switch_off(void); // 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 (1<<2) // Options for the HF simulated tag, how to modulate #define FPGA_HF_SIMULATOR_NO_MODULATION (0<<0) // 0000 #define FPGA_HF_SIMULATOR_MODULATE_BPSK (1<<0) // 0001 diff --git a/armsrc/legicrf.c b/armsrc/legicrf.c index c04f742e0..dc4c8d4f6 100644 --- a/armsrc/legicrf.c +++ b/armsrc/legicrf.c @@ -1,7 +1,6 @@ //----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz // 2016 Iceman -// 2018 AntiCat (rwd rewritten) // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -11,410 +10,1613 @@ //----------------------------------------------------------------------------- #include "legicrf.h" -#include "ticks.h" /* timers */ -#include "crc.h" /* legic crc-4 */ -#include "legic_prng.h" /* legic PRNG impl */ -#include "legic.h" /* legic_card_select_t struct */ +static struct legic_frame { + uint8_t bits; + uint32_t data; +} current_frame; -static uint8_t* legic_mem; /* card memory, used for read, write and sim */ -static legic_card_select_t card;/* metadata of currently selected card */ -static crc_t legic_crc; -static int32_t input_threshold; /* values > threshold are 1 else 0 */ +static enum { + STATE_DISCON, + STATE_IV, + STATE_CON, +} legic_state; -//----------------------------------------------------------------------------- -// Frame timing and pseudorandom number generator -// -// The Prng is forwarded every 100us (TAG_BIT_PERIOD), except when the reader is -// transmitting. In that case the prng has to be forwarded every bit transmitted: -// - 60us for a 0 (RWD_TIME_0) -// - 100us for a 1 (RWD_TIME_1) -// -// The data dependent timing makes writing comprehensible code significantly -// harder. The current aproach forwards the prng data based if there is data on -// air and time based, using GetCountUS(), during computational and wait periodes. -// -// To not have the necessity to calculate/guess exection time dependend timeouts -// tx_frame and rx_frame use a shared timestamp to coordinate tx and rx timeslots. -//----------------------------------------------------------------------------- +static crc_t legic_crc; +static int legic_read_count; +static uint32_t legic_prng_bc; +static uint32_t legic_prng_iv; -static int32_t last_frame_end; /* ts of last bit of previews rx or tx frame */ +static int legic_phase_drift; +static int legic_frame_drift; +static int legic_reqresp_drift; -#define RWD_TIME_PAUSE 20 /* 20us */ -#define RWD_TIME_1 100 /* READER_TIME_PAUSE 20us off + 80us on = 100us */ -#define RWD_TIME_0 60 /* READER_TIME_PAUSE 20us off + 40us on = 60us */ -#define RWD_FRAME_WAIT 220 /* 220us from TAG frame end to READER frame start */ -#define TAG_FRAME_WAIT 330 /* 330us from READER frame end to TAG frame start */ -#define TAG_BIT_PERIOD 100 /* 100us */ +AT91PS_TC timer; +AT91PS_TC prng_timer; -#define LEGIC_CARD_MEMSIZE 1024 /* The largest Legic Prime card is 1k */ +/* +static void setup_timer(void) { + // Set up Timer 1 to use for measuring time between pulses. Since we're bit-banging + // this it won't be terribly accurate but should be good enough. + // + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); + timer = AT91C_BASE_TC1; + timer->TC_CCR = AT91C_TC_CLKDIS; + timer->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; + timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; -//----------------------------------------------------------------------------- -// I/O interface abstraction (FPGA -> ARM) -//----------------------------------------------------------------------------- - -static inline uint8_t rx_byte_from_fpga() { - for(;;) { - WDT_HIT(); - - // wait for byte be become available in rx holding register - if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - return AT91C_BASE_SSC->SSC_RHR; - } - } + // + // Set up Timer 2 to use for measuring time between frames in + // tag simulation mode. Runs 4x faster as Timer 1 + // + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC2); + prng_timer = AT91C_BASE_TC2; + prng_timer->TC_CCR = AT91C_TC_CLKDIS; + prng_timer->TC_CMR = AT91C_TC_CLKS_TIMER_DIV2_CLOCK; + prng_timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; } -//----------------------------------------------------------------------------- -// Demodulation -//----------------------------------------------------------------------------- + AT91C_BASE_PMC->PMC_PCER |= (0x1 << 12) | (0x1 << 13) | (0x1 << 14); + AT91C_BASE_TCB->TCB_BMR = AT91C_TCB_TC0XC0S_NONE | AT91C_TCB_TC1XC1S_TIOA0 | AT91C_TCB_TC2XC2S_NONE; -// Returns am aproximated power measurement -// -// The FPGA running on the xcorrelation kernel samples the subcarrier at ~3 MHz. -// The kernel was initialy designed to receive BSPK/2-PSK. Hance, it reports an -// I/Q pair every 18.9us (8 bits i and 8 bits q). -// -// The subcarrier amplitude can be calculated using Pythagoras sqrt(i^2 + q^2). -// To reduce CPU time the amplitude is approximated by using linear functions: -// am = MAX(ABS(i),ABS(q)) + 1/2*MIN(ABS(i),ABSq)) -// -// Note: The SSC receiver is never synchronized the calculation my be performed -// on a i/q pair from two subsequent correlations, but does not matter. -static inline int32_t sample_power() { - int32_t q = (int8_t)rx_byte_from_fpga(); q = ABS(q); - int32_t i = (int8_t)rx_byte_from_fpga(); i = ABS(i); + // fast clock + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; // timer disable + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK(48MHz)/32 -- tick=1.5mks + AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_ACPA_CLEAR | + AT91C_TC_ACPC_SET | AT91C_TC_ASWTRG_SET; + AT91C_BASE_TC0->TC_RA = 1; + AT91C_BASE_TC0->TC_RC = 0xBFFF + 1; // 0xC000 + +*/ - return MAX(i, q) + (MIN(i, q) >> 1); +// At TIMER_CLOCK3 (MCK/32) +// testing calculating in ticks. 1.5ticks = 1us +#define RWD_TIME_1 120 // READER_TIME_PAUSE 20us off, 80us on = 100us 80 * 1.5 == 120ticks +#define RWD_TIME_0 60 // READER_TIME_PAUSE 20us off, 40us on = 60us 40 * 1.5 == 60ticks +#define RWD_TIME_PAUSE 30 // 20us == 20 * 1.5 == 30ticks */ +#define TAG_BIT_PERIOD 142 // 100us == 100 * 1.5 == 150ticks +#define TAG_FRAME_WAIT 495 // 330us from READER frame end to TAG frame start. 330 * 1.5 == 495 + +#define RWD_TIME_FUZZ 20 // rather generous 13us, since the peak detector + hysteresis fuzz quite a bit + +#define SIM_DIVISOR 586 /* prng_time/SIM_DIVISOR count prng needs to be forwared */ +#define SIM_SHIFT 900 /* prng_time+SIM_SHIFT shift of delayed start */ + +#define OFFSET_LOG 1024 + +#define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz))) + +#ifndef SHORT_COIL +# define SHORT_COIL LOW(GPIO_SSC_DOUT); +#endif +#ifndef OPEN_COIL +# define OPEN_COIL HIGH(GPIO_SSC_DOUT); +#endif +#ifndef LINE_IN +# define LINE_IN AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; +#endif +// Pause pulse, off in 20us / 30ticks, +// ONE / ZERO bit pulse, +// one == 80us / 120ticks +// zero == 40us / 60ticks +#ifndef COIL_PULSE +# define COIL_PULSE(x) \ + do { \ + SHORT_COIL; \ + WaitTicks( (RWD_TIME_PAUSE) ); \ + OPEN_COIL; \ + WaitTicks((x)); \ + } while (0); +#endif + +// ToDo: define a meaningful maximum size for auth_table. The bigger this is, the lower will be the available memory for traces. +// Historically it used to be FREE_BUFFER_SIZE, which was 2744. +#define LEGIC_CARD_MEMSIZE 1024 +static uint8_t* cardmem; + +static void frame_append_bit(struct legic_frame * const f, uint8_t bit) { + // Overflow, won't happen + if (f->bits >= 31) return; + + f->data |= (bit << f->bits); + f->bits++; } -// Returns a demedulated bit -// -// An aproximated power measurement is available every 18.9us. The bit time -// is 100us. The code samples 5 times and uses samples 3 and 4. -// -// Note: The demodulator would be drifting (18.9us * 5 != 100us), rx_frame -// has a delay loop that aligns rx_bit calls to the TAG tx timeslots. -static inline bool rx_bit() { - static int32_t p[5]; - for(size_t i = 0; i<5; ++i) { - p[i] = sample_power(); - } - - if((p[2] > input_threshold) && (p[3] > input_threshold)) { - return true; - } - if((p[2] < input_threshold) && (p[3] < input_threshold)) { - return false; - } - - Dbprintf("rx_bit failed %i vs %i (threshold %i)", p[2], p[3], input_threshold); - return false; +static void frame_clean(struct legic_frame * const f) { + f->data = 0; + f->bits = 0; } -//----------------------------------------------------------------------------- -// Modulation -// -// I've tried to modulate the Legic specific pause-puls using ssc and the default -// ssc clock of 105.4 kHz (bit periode of 9.4us) - previous commit. However, -// the timing was not precise enough. By increasing the ssc clock this could -// be circumvented, but the adventage over bitbang would be little. -//----------------------------------------------------------------------------- +// Prng works when waiting in 99.1us cycles. +// and while sending/receiving in bit frames (100, 60) +/*static void CalibratePrng( uint32_t time){ + // Calculate Cycles based on timer 100us + uint32_t i = (time - sendFrameStop) / 100 ; -static inline void tx_bit(bool bit) { - uint32_t ts = GetCountUS(); + // substract cycles of finished frames + int k = i - legic_prng_count()+1; - // insert pause - LOW(GPIO_SSC_DOUT); - while(GetCountUS() < ts + RWD_TIME_PAUSE) { }; - HIGH(GPIO_SSC_DOUT); + // substract current frame length, rewind to beginning + if ( k > 0 ) + legic_prng_forward(k); +} +*/ - // return to high, wait for bit periode to end - if(bit) { - while(GetCountUS() < ts + RWD_TIME_1) { }; - } else { - while(GetCountUS() < ts + RWD_TIME_0) { }; - } +/* Generate Keystream */ +uint32_t get_key_stream(int skip, int count) { + + int i; + + // Use int to enlarge timer tc to 32bit + legic_prng_bc += prng_timer->TC_CV; + + // reset the prng timer. + + /* If skip == -1, forward prng time based */ + if(skip == -1) { + i = (legic_prng_bc + SIM_SHIFT)/SIM_DIVISOR; /* Calculate Cycles based on timer */ + i -= legic_prng_count(); /* substract cycles of finished frames */ + i -= count; /* substract current frame length, rewind to beginning */ + legic_prng_forward(i); + } else { + legic_prng_forward(skip); + } + + i = (count == 6) ? -1 : legic_read_count; + + /* Generate KeyStream */ + return legic_prng_get_bits(count); } -//----------------------------------------------------------------------------- -// Frame Handling -// -// The LEGIC RF protocol from card to reader does not include explicit frame -// start/stop information or length information. The reader must know beforehand -// how many bits it wants to receive. -// Notably: a card sending a stream of 0-bits is indistinguishable from no card -// present. -//----------------------------------------------------------------------------- +/* Send a frame in tag mode, the FPGA must have been set up by + * LegicRfSimulate + */ +void frame_send_tag(uint16_t response, uint8_t bits) { -static void tx_frame(uint32_t frame, uint8_t len) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + uint16_t mask = 1; + + /* Bitbang the response */ + SHORT_COIL; + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - // wait for next tx timeslot - last_frame_end += RWD_FRAME_WAIT; - while(GetCountUS() < last_frame_end) { }; + /* TAG_FRAME_WAIT -> shift by 2 */ + legic_prng_forward(3); + response ^= legic_prng_get_bits(bits); - // transmit frame, MSB first - for(uint8_t i = 0; i < len; ++i) { - bool bit = (frame >> i) & 0x01; - tx_bit(bit ^ legic_prng_get_bit()); - legic_prng_forward(1); - }; + /* Wait for the frame start */ + WaitTicks( TAG_FRAME_WAIT ); - // add pause to mark end of the frame - uint32_t ts = GetCountUS(); - LOW(GPIO_SSC_DOUT); - while(GetCountUS() < ts + RWD_TIME_PAUSE) { }; - HIGH(GPIO_SSC_DOUT); - - // update coordination timestamp - last_frame_end = GetCountUS(); + for (; mask < BITMASK(bits); mask <<= 1) { + if (response & mask) + OPEN_COIL + else + SHORT_COIL + WaitTicks(TAG_BIT_PERIOD); + } + SHORT_COIL; } -static uint32_t rx_frame(uint8_t len) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR - | FPGA_HF_READER_RX_XCORR_848_KHZ - | FPGA_HF_READER_RX_XCORR_QUARTER); +/* Send a frame in reader mode, the FPGA must have been set up by + * LegicRfReader + */ +void frame_sendAsReader(uint32_t data, uint8_t bits){ - // hold sampling until card is expect to respond - last_frame_end += TAG_FRAME_WAIT; - while(GetCountUS() < last_frame_end) { }; + uint32_t starttime = GET_TICKS, send = 0, mask = 1; + + // xor lsfr onto data. + send = data ^ legic_prng_get_bits(bits); + + for (; mask < BITMASK(bits); mask <<= 1) { + if (send & mask) + COIL_PULSE(RWD_TIME_1) + else + COIL_PULSE(RWD_TIME_0) + } - uint32_t frame = 0; - for(uint8_t i = 0; i < len; i++) { - frame |= (rx_bit() ^ legic_prng_get_bit()) << i; - legic_prng_forward(1); - - // rx_bit runs only 95us, resync to TAG_BIT_PERIOD - last_frame_end += TAG_BIT_PERIOD; - while(GetCountUS() < last_frame_end) { }; - } - - // update coordination timestamp - last_frame_end = GetCountUS(); - - return frame; + // Final pause to mark the end of the frame + COIL_PULSE(0); + + // log + uint8_t cmdbytes[] = {bits, BYTEx(data,0), BYTEx(data,1), BYTEx(data,2), BYTEx(send,0), BYTEx(send,1), BYTEx(send,2)}; + LogTrace(cmdbytes, sizeof(cmdbytes), starttime, GET_TICKS, NULL, true); } -//----------------------------------------------------------------------------- -// Legic Reader -//----------------------------------------------------------------------------- +/* Receive a frame from the card in reader emulation mode, the FPGA and + * timer must have been set up by LegicRfReader and frame_sendAsReader. + * + * The LEGIC RF protocol from card to reader does not include explicit + * frame start/stop information or length information. The reader must + * know beforehand how many bits it wants to receive. (Notably: a card + * sending a stream of 0-bits is indistinguishable from no card present.) + * + * Receive methodology: There is a fancy correlator in hi_read_rx_xcorr, but + * I'm not smart enough to use it. Instead I have patched hi_read_tx to output + * the ADC signal with hysteresis on SSP_DIN. Bit-bang that signal and look + * for edges. Count the edges in each bit interval. If they are approximately + * 0 this was a 0-bit, if they are approximately equal to the number of edges + * expected for a 212kHz subcarrier, this was a 1-bit. For timing we use the + * timer that's still running from frame_sendAsReader in order to get a synchronization + * with the frame that we just sent. + * + * FIXME: Because we're relying on the hysteresis to just do the right thing + * the range is severely reduced (and you'll probably also need a good antenna). + * So this should be fixed some time in the future for a proper receiver. + */ +static void frame_receiveAsReader(struct legic_frame * const f, uint8_t bits) { -int init_card(uint8_t cardtype, legic_card_select_t *p_card) { - p_card->tagtype = cardtype; + if ( bits > 32 ) return; + + uint8_t i = bits, edges = 0; + uint32_t the_bit = 1, next_bit_at = 0, data = 0; + uint32_t old_level = 0; + volatile uint32_t level = 0; + + frame_clean(f); + + // calibrate the prng. + legic_prng_forward(2); + data = legic_prng_get_bits(bits); + + //FIXED time between sending frame and now listening frame. 330us + uint32_t starttime = GET_TICKS; + // its about 9+9 ticks delay from end-send to here. + WaitTicks( 477 ); - switch(p_card->tagtype) { - case 0x0d: - p_card->cmdsize = 6; - p_card->addrsize = 5; - p_card->cardsize = 22; - break; - case 0x1d: - p_card->cmdsize = 9; - p_card->addrsize = 8; - p_card->cardsize = 256; - break; - case 0x3d: - p_card->cmdsize = 11; - p_card->addrsize = 10; - p_card->cardsize = 1024; - break; - default: - p_card->cmdsize = 0; - p_card->addrsize = 0; - p_card->cardsize = 0; - return 2; - } - return 0; + next_bit_at = GET_TICKS + TAG_BIT_PERIOD; + + while ( i-- ){ + edges = 0; + while ( GET_TICKS < next_bit_at) { + + level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); + + if (level != old_level) + ++edges; + + old_level = level; + } + + next_bit_at += TAG_BIT_PERIOD; + + // We expect 42 edges (ONE) + if ( edges > 20 ) + data ^= the_bit; + + the_bit <<= 1; + } + + // output + f->data = data; + f->bits = bits; + + // log + uint8_t cmdbytes[] = {bits, BYTEx(data, 0), BYTEx(data, 1)}; + LogTrace(cmdbytes, sizeof(cmdbytes), starttime, GET_TICKS, NULL, false); } -static void init_reader(bool clear_mem) { - // configure FPGA - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR - | FPGA_HF_READER_RX_XCORR_848_KHZ - | FPGA_HF_READER_RX_XCORR_QUARTER); - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - - // configure SSC with defaults - FpgaSetupSsc(); - - // re-claim GPIO_SSC_DOUT as GPIO and enable output - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; - HIGH(GPIO_SSC_DOUT); - - // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier. - legic_mem = BigBuf_get_EM_addr(); - if(legic_mem) { - memset(legic_mem, 0x00, LEGIC_CARD_MEMSIZE); - } - - // start trace - clear_trace(); - set_tracing(true); - - // init crc calculator - crc_init(&legic_crc, 4, 0x19 >> 1, 0x05, 0); - - // start us timer - StartCountUS(); -} - -// Setup reader to card connection -// -// The setup consists of a three way handshake: -// - Transmit initialisation vector 7 bits -// - Receive card type 6 bits -// - Acknowledge frame 6 bits +// Setup pm3 as a Legic Reader static uint32_t setup_phase_reader(uint8_t iv) { - // init coordination timestamp - last_frame_end = GetCountUS(); + + // Switch on carrier and let the tag charge for 5ms + HIGH(GPIO_SSC_DOUT); + WaitUS(5000); + + ResetTicks(); + + legic_prng_init(0); + + // send IV handshake + frame_sendAsReader(iv, 7); - // Switch on carrier and let the card charge for 5ms. - // Use the time to calibrate the treshhold. - input_threshold = 8; // heuristically determined - do { - int32_t sample = sample_power(); - if(sample > input_threshold) { - input_threshold = sample; + // tag and reader has same IV. + legic_prng_init(iv); + + frame_receiveAsReader(¤t_frame, 6); + + // 292us (438t) - fixed delay before sending ack. + // minus log and stuff 100tick? + WaitTicks(338); + legic_prng_forward(3); + + // Send obsfuscated acknowledgment frame. + // 0x19 = 0x18 MIM22, 0x01 LSB READCMD + // 0x39 = 0x38 MIM256, MIM1024 0x01 LSB READCMD + switch ( current_frame.data ) { + case 0x0D: frame_sendAsReader(0x19, 6); break; + case 0x1D: + case 0x3D: frame_sendAsReader(0x39, 6); break; + default: break; + } + + legic_prng_forward(2); + return current_frame.data; +} + +void LegicCommonInit(bool clear_mem) { + + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + /* Bitbang the transmitter */ + SHORT_COIL; + AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; + AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; + + // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier. + cardmem = BigBuf_get_EM_addr(); + if ( clear_mem ) + memset(cardmem, 0x00, LEGIC_CARD_MEMSIZE); + + clear_trace(); + set_tracing(true); + crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); + + StartTicks(); +} + +// Switch off carrier, make sure tag is reset +static void switch_off_tag_rwd(void) { + SHORT_COIL; + WaitUS(20); + WDT_HIT(); +} + +// calculate crc4 for a legic READ command +static uint32_t legic4Crc(uint8_t cmd, uint16_t byte_index, uint8_t value, uint8_t cmd_sz) { + crc_clear(&legic_crc); + uint32_t temp = (value << cmd_sz) | (byte_index << 1) | cmd; + crc_update(&legic_crc, temp, cmd_sz + 8 ); + return crc_finish(&legic_crc); +} + +int legic_read_byte( uint16_t index, uint8_t cmd_sz) { + + uint8_t byte, crc, calcCrc = 0; + uint32_t cmd = (index << 1) | LEGIC_READ; + + // 90ticks = 60us (should be 100us but crc calc takes time.) + //WaitTicks(330); // 330ticks prng(4) - works + WaitTicks(240); // 240ticks prng(3) - works + + frame_sendAsReader(cmd, cmd_sz); + frame_receiveAsReader(¤t_frame, 12); + + // CRC check. + byte = BYTEx(current_frame.data, 0); + crc = BYTEx(current_frame.data, 1); + calcCrc = legic4Crc(LEGIC_READ, index, byte, cmd_sz); + + if( calcCrc != crc ) { + Dbprintf("!!! crc mismatch: %x != %x !!!", calcCrc, crc); + return -1; + } + + legic_prng_forward(3); + return byte; +} + +/* + * - assemble a write_cmd_frame with crc and send it + * - wait until the tag sends back an ACK ('1' bit unencrypted) + * - forward the prng based on the timing + */ +bool legic_write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz) { + + bool isOK = false; + int8_t i = 40; + uint8_t edges = 0; + uint8_t cmd_sz = addr_sz+1+8+4; //crc+data+cmd; + uint32_t steps = 0, next_bit_at, start, crc, old_level = 0; + + crc = legic4Crc(LEGIC_WRITE, index, byte, addr_sz+1); + + // send write command + uint32_t cmd = LEGIC_WRITE; + cmd |= index << 1; // index + cmd |= byte << (addr_sz+1); // Data + cmd |= (crc & 0xF ) << (addr_sz+1+8); // CRC + + WaitTicks(240); + + frame_sendAsReader(cmd, cmd_sz); + + LINE_IN; + + start = GET_TICKS; + + // ACK, - one single "1" bit after 3.6ms + // 3.6ms = 3600us * 1.5 = 5400ticks. + WaitTicks(5400); + + next_bit_at = GET_TICKS + TAG_BIT_PERIOD; + + while ( i-- ) { + WDT_HIT(); + edges = 0; + while ( GET_TICKS < next_bit_at) { + + volatile uint32_t level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); + + if (level != old_level) + ++edges; + + old_level = level; + } + + next_bit_at += TAG_BIT_PERIOD; + + // We expect 42 edges (ONE) + if(edges > 20 ) { + steps = ( (GET_TICKS - start) / TAG_BIT_PERIOD); + legic_prng_forward(steps); + isOK = true; + goto OUT; + } } - } while(GetCountUS() < last_frame_end + 5000); - - // Set threshold to noise floor * 2 - input_threshold <<= 1; - - legic_prng_init(0); - tx_frame(iv, 7); - - // configure iv - legic_prng_init(iv); - legic_prng_forward(2); - - // receive card type - int32_t card_type = rx_frame(6); - legic_prng_forward(3); - - // send obsfuscated acknowledgment frame - switch (card_type) { - case 0x0D: - tx_frame(0x19, 6); // MIM22 | READCMD = 0x18 | 0x01 - break; - case 0x1D: - case 0x3D: - tx_frame(0x39, 6); // MIM256 | READCMD = 0x38 | 0x01 - break; - } - - return card_type; + +OUT: ; + legic_prng_forward(1); + + uint8_t cmdbytes[] = {1, isOK, BYTEx(steps, 0), BYTEx(steps, 1) }; + LogTrace(cmdbytes, sizeof(cmdbytes), start, GET_TICKS, NULL, false); + return isOK; } -static uint8_t calc_crc4(uint16_t cmd, uint8_t cmd_sz, uint8_t value) { - crc_clear(&legic_crc); - crc_update(&legic_crc, (value << cmd_sz) | cmd, 8 + cmd_sz); - return crc_finish(&legic_crc); -} +int LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv) { + + uint16_t i = 0; + uint8_t isOK = 1; + legic_card_select_t card; + + LegicCommonInit(true); + + if ( legic_select_card_iv(&card, iv) ) { + isOK = 0; + goto OUT; + } -static int16_t read_byte(uint16_t index, uint8_t cmd_sz) { - uint16_t cmd = (index << 1) | LEGIC_READ; + if (len + offset > card.cardsize) + len = card.cardsize - offset; - // read one byte - legic_prng_forward(2); - tx_frame(cmd, cmd_sz); - legic_prng_forward(2); - uint32_t frame = rx_frame(12); + LED_B_ON(); + while (i < len) { + int r = legic_read_byte(offset + i, card.cmdsize); + + if (r == -1 || BUTTON_PRESS()) { + if ( MF_DBGLEVEL >= 2) DbpString("operation aborted"); + isOK = 0; + goto OUT; + } + cardmem[i++] = r; + WDT_HIT(); + } - // split frame into data and crc - uint8_t byte = BYTEx(frame, 0); - uint8_t crc = BYTEx(frame, 1); - - // check received against calculated crc - uint8_t calc_crc = calc_crc4(cmd, cmd_sz, byte); - if(calc_crc != crc) { - Dbprintf("!!! crc mismatch: %x != %x !!!", calc_crc, crc); - return -1; - } - - legic_prng_forward(1); - - return byte; -} - -//----------------------------------------------------------------------------- -// Command Line Interface -// -// Only this functions are public / called from appmain.c -//----------------------------------------------------------------------------- -void LegicRfInfo(void) { - // configure ARM and FPGA - init_reader(false); - - // establish shared secret and detect card type - uint8_t card_type = setup_phase_reader(0x01); - if(init_card(card_type, &card) != 0) { - cmd_send(CMD_ACK, 0, 0, 0, 0, 0); - goto OUT; - } - - // read UID - for(uint8_t i = 0; i < sizeof(card.uid); ++i) { - int16_t byte = read_byte(i, card.cmdsize); - if(byte == -1) { - cmd_send(CMD_ACK, 0, 0, 0, 0, 0); - goto OUT; - } - card.uid[i] = byte & 0xFF; - } - - // read MCC and check against UID - int16_t mcc = read_byte(4, card.cmdsize); - int16_t calc_mcc = CRC8Legic(card.uid, 4);; - if(mcc != calc_mcc) { - cmd_send(CMD_ACK, 0, 0, 0, 0, 0); - goto OUT; - } - - // OK - cmd_send(CMD_ACK, 1, 0, 0, (uint8_t*)&card, sizeof(legic_card_select_t)); - -OUT: - switch_off(); -} - -void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv) { - // configure ARM and FPGA - init_reader(false); - - // establish shared secret and detect card type - uint8_t card_type = setup_phase_reader(iv); - if(init_card(card_type, &card) != 0) { - cmd_send(CMD_ACK, 0, 0, 0, 0, 0); - goto OUT; - } - - // do not read beyond card memory - if(len + offset > card.cardsize) { - len = card.cardsize - offset; - } - - for(uint16_t i = 0; i < len; ++i) { - int16_t byte = read_byte(offset + i, card.cmdsize); - if(byte == -1) { - cmd_send(CMD_ACK, 0, 0, 0, 0, 0); - goto OUT; - } - legic_mem[i] = byte; - } - - // OK - cmd_send(CMD_ACK, 1, len, 0, legic_mem, len); - -OUT: - switch_off(); +OUT: + WDT_HIT(); + switch_off_tag_rwd(); + LEDsoff(); + cmd_send(CMD_ACK, isOK, len, 0, cardmem, len); + return 0; } void LegicRfWriter(uint16_t offset, uint16_t len, uint8_t iv, uint8_t *data) { - cmd_send(CMD_ACK, 0, 0, 0, 0, 0); //TODO Implement + + #define LOWERLIMIT 4 + uint8_t isOK = 1, msg = 0; + legic_card_select_t card; + + // uid NOT is writeable. + if ( offset <= LOWERLIMIT ) { + isOK = 0; + goto OUT; + } + + LegicCommonInit(false); + + if ( legic_select_card_iv(&card, iv) ) { + isOK = 0; + msg = 1; + goto OUT; + } + + if ( len + offset > card.cardsize) + len = card.cardsize - offset; + + LED_B_ON(); + while( len > 0 ) { + --len; + if ( !legic_write_byte( len + offset, data[len], card.addrsize) ) { + Dbprintf("operation failed | %02X | %02X | %02X", len + offset, len, data[len] ); + isOK = 0; + goto OUT; + } + WDT_HIT(); + } +OUT: + cmd_send(CMD_ACK, isOK, msg,0,0,0); + switch_off_tag_rwd(); + LEDsoff(); } -void LegicRfSimulate(int phase, int frame, int reqresp) { - cmd_send(CMD_ACK, 0, 0, 0, 0, 0); //TODO Implement +int legic_select_card_iv(legic_card_select_t *p_card, uint8_t iv){ + + if ( p_card == NULL ) return 1; + + p_card->tagtype = setup_phase_reader(iv); + + switch(p_card->tagtype) { + case 0x0d: + p_card->cmdsize = 6; + p_card->addrsize = 5; + p_card->cardsize = 22; + break; + case 0x1d: + p_card->cmdsize = 9; + p_card->addrsize = 8; + p_card->cardsize = 256; + break; + case 0x3d: + p_card->cmdsize = 11; + p_card->addrsize = 10; + p_card->cardsize = 1024; + break; + default: + p_card->cmdsize = 0; + p_card->addrsize = 0; + p_card->cardsize = 0; + return 2; + } + return 0; } +int legic_select_card(legic_card_select_t *p_card){ + return legic_select_card_iv(p_card, 0x01); +} + +//----------------------------------------------------------------------------- +// Work with emulator memory +// +// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not +// involved in dealing with emulator memory. But if it is called later, it might +// destroy the Emulator Memory. +//----------------------------------------------------------------------------- +// arg0 = offset +// arg1 = num of bytes +void LegicEMemSet(uint32_t arg0, uint32_t arg1, uint8_t *data) { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + legic_emlset_mem(data, arg0, arg1); +} +// arg0 = offset +// arg1 = num of bytes +void LegicEMemGet(uint32_t arg0, uint32_t arg1) { + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + uint8_t buf[USB_CMD_DATA_SIZE] = {0x00}; + legic_emlget_mem(buf, arg0, arg1); + LED_B_ON(); + cmd_send(CMD_ACK, arg0, arg1, 0, buf, USB_CMD_DATA_SIZE); + LED_B_OFF(); +} +void legic_emlset_mem(uint8_t *data, int offset, int numofbytes) { + cardmem = BigBuf_get_EM_addr(); + memcpy(cardmem + offset, data, numofbytes); +} +void legic_emlget_mem(uint8_t *data, int offset, int numofbytes) { + cardmem = BigBuf_get_EM_addr(); + memcpy(data, cardmem + offset, numofbytes); +} + +void LegicRfInfo(void){ + + int r; + + uint8_t buf[sizeof(legic_card_select_t)] = {0x00}; + legic_card_select_t *card = (legic_card_select_t*) buf; + + LegicCommonInit(false); + + if ( legic_select_card(card) ) { + cmd_send(CMD_ACK,0,0,0,0,0); + goto OUT; + } + + // read UID bytes + for ( uint8_t i = 0; i < sizeof(card->uid); ++i) { + r = legic_read_byte(i, card->cmdsize); + if ( r == -1 ) { + cmd_send(CMD_ACK,0,0,0,0,0); + goto OUT; + } + card->uid[i] = r & 0xFF; + } + + // MCC byte. + r = legic_read_byte(4, card->cmdsize); + uint32_t calc_mcc = CRC8Legic(card->uid, 4);; + if ( r != calc_mcc) { + cmd_send(CMD_ACK,0,0,0,0,0); + goto OUT; + } + + // OK + cmd_send(CMD_ACK, 1, 0, 0, buf, sizeof(legic_card_select_t)); + +OUT: + switch_off_tag_rwd(); + LEDsoff(); +} + +/* Handle (whether to respond) a frame in tag mode + * Only called when simulating a tag. + */ +static void frame_handle_tag(struct legic_frame const * const f) +{ + // log + //uint8_t cmdbytes[] = {bits, BYTEx(data, 0), BYTEx(data, 1)}; + //LogTrace(cmdbytes, sizeof(cmdbytes), starttime, GET_TICKS, NULL, false); + //Dbprintf("ICE: enter frame_handle_tag: %02x ", f->bits); + + /* First Part of Handshake (IV) */ + if(f->bits == 7) { + + LED_C_ON(); + + // Reset prng timer + //ResetTimer(prng_timer); + ResetTicks(); + + // IV from reader. + legic_prng_init(f->data); + + Dbprintf("ICE: IV: %02x ", f->data); + + // We should have three tagtypes with three different answers. + legic_prng_forward(2); + //frame_send_tag(0x3d, 6); /* MIM1024 0x3d^0x26 = 0x1B */ + frame_send_tag(0x1d, 6); // MIM256 + + legic_state = STATE_IV; + legic_read_count = 0; + legic_prng_bc = 0; + legic_prng_iv = f->data; + + //ResetTimer(timer); + //WaitUS(280); + WaitTicks(388); + return; + } + + /* 0x19==??? */ + if(legic_state == STATE_IV) { + uint32_t local_key = get_key_stream(3, 6); + int xored = 0x39 ^ local_key; + if((f->bits == 6) && (f->data == xored)) { + legic_state = STATE_CON; + + ResetTimer(timer); + WaitTicks(300); + return; + + } else { + legic_state = STATE_DISCON; + LED_C_OFF(); + Dbprintf("iv: %02x frame: %02x key: %02x xored: %02x", legic_prng_iv, f->data, local_key, xored); + return; + } + } + + /* Read */ + if(f->bits == 11) { + if(legic_state == STATE_CON) { + uint32_t key = get_key_stream(2, 11); //legic_phase_drift, 11); + uint16_t addr = f->data ^ key; + addr >>= 1; + uint8_t data = cardmem[addr]; + + uint32_t crc = legic4Crc(LEGIC_READ, addr, data, 11) << 8; + + //legic_read_count++; + //legic_prng_forward(legic_reqresp_drift); + + frame_send_tag(crc | data, 12); + //ResetTimer(timer); + legic_prng_forward(2); + WaitTicks(330); + return; + } + } + + /* Write */ + if (f->bits == 23 || f->bits == 21 ) { + uint32_t key = get_key_stream(-1, 23); //legic_frame_drift, 23); + uint16_t addr = f->data ^ key; + addr >>= 1; + addr &= 0x3ff; + uint32_t data = f->data ^ key; + data >>= 11; + data &= 0xff; + + cardmem[addr] = data; + /* write command */ + legic_state = STATE_DISCON; + LED_C_OFF(); + Dbprintf("write - addr: %x, data: %x", addr, data); + // should send a ACK after 3.6ms + return; + } + + if(legic_state != STATE_DISCON) { + Dbprintf("Unexpected: sz:%u, Data:%03.3x, State:%u, Count:%u", f->bits, f->data, legic_state, legic_read_count); + Dbprintf("IV: %03.3x", legic_prng_iv); + } + + legic_state = STATE_DISCON; + legic_read_count = 0; + WaitMS(10); + LED_C_OFF(); + return; +} + +/* Read bit by bit untill full frame is received + * Call to process frame end answer + */ +static void emit(int bit) { + + switch (bit) { + case 1: + frame_append_bit(¤t_frame, 1); + break; + case 0: + frame_append_bit(¤t_frame, 0); + break; + default: + if(current_frame.bits <= 4) { + frame_clean(¤t_frame); + } else { + frame_handle_tag(¤t_frame); + frame_clean(¤t_frame); + } + WDT_HIT(); + break; + } +} + +void LegicRfSimulate(int phase, int frame, int reqresp) +{ + /* ADC path high-frequency peak detector, FPGA in high-frequency simulator mode, + * modulation mode set to 212kHz subcarrier. We are getting the incoming raw + * envelope waveform on DIN and should send our response on DOUT. + * + * The LEGIC RF protocol is pulse-pause-encoding from reader to card, so we'll + * measure the time between two rising edges on DIN, and no encoding on the + * subcarrier from card to reader, so we'll just shift out our verbatim data + * on DOUT, 1 bit is 100us. The time from reader to card frame is still unclear, + * seems to be 330us. + */ + + int old_level = 0, active = 0; + volatile int32_t level = 0; + + legic_state = STATE_DISCON; + legic_phase_drift = phase; + legic_frame_drift = frame; + legic_reqresp_drift = reqresp; + + + /* to get the stream of bits from FPGA in sim mode.*/ + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + // Set up the synchronous serial port + //FpgaSetupSsc(); + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K); + //FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + + #define LEGIC_DMA_BUFFER 256 + // The DMA buffer, used to stream samples from the FPGA + //uint8_t *dmaBuf = BigBuf_malloc(LEGIC_DMA_BUFFER); + //uint8_t *data = dmaBuf; + // Setup and start DMA. + // if ( !FpgaSetupSscDma((uint8_t*) dmaBuf, LEGIC_DMA_BUFFER) ){ + // if (MF_DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); + // return; + // } + + //StartCountSspClk(); + /* Bitbang the receiver */ + AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN; + AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN; + + // need a way to determine which tagtype we are simulating + + // hook up emulator memory + cardmem = BigBuf_get_EM_addr(); + + clear_trace(); + set_tracing(true); + + crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); + + StartTicks(); + + LED_B_ON(); + DbpString("Starting Legic emulator, press button to end"); + + /* + * The mode FPGA_HF_SIMULATOR_MODULATE_212K works like this. + * - A 1-bit input to the FPGA becomes 8 pulses on 212kHz (fc/64) (18.88us). + * - A 0-bit input to the FPGA becomes an unmodulated time of 18.88us + * + * In this mode the SOF can be written as 00011101 = 0x1D + * The EOF can be written as 10111000 = 0xb8 + * A logic 1 is 01 + * A logic 0 is 10 + volatile uint8_t b; + uint8_t i = 0; + while( !BUTTON_PRESS() ) { + WDT_HIT(); + + // not sending anything. + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0x00; + } + + // receive + if ( AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY ) { + b = (uint8_t) AT91C_BASE_SSC->SSC_RHR; + bd[i] = b; + ++i; + // if(OutOfNDecoding(b & 0x0f)) + // *len = Uart.byteCnt; + } + + } + */ + + while(!BUTTON_PRESS() && !usb_poll_validate_length()) { + + level = !!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN); + + uint32_t time = GET_TICKS; + + if (level != old_level) { + if (level == 1) { + + //Dbprintf("start, %u ", time); + StartTicks(); + // did we get a signal + if (FUZZ_EQUAL(time, RWD_TIME_1, RWD_TIME_FUZZ)) { + // 1 bit + emit(1); + active = 1; + LED_A_ON(); + } else if (FUZZ_EQUAL(time, RWD_TIME_0, RWD_TIME_FUZZ)) { + // 0 bit + emit(0); + active = 1; + LED_A_ON(); + } else if (active) { + // invalid + emit(-1); + active = 0; + LED_A_OFF(); + } + } + } + + + /* Frame end */ + if(time >= (RWD_TIME_1 + RWD_TIME_FUZZ) && active) { + emit(-1); + active = 0; + LED_A_OFF(); + } + + /* + * Disable the counter, Then wait for the clock to acknowledge the + * shutdown in its status register. Reading the SR has the + * side-effect of clearing any pending state in there. + */ + //if(time >= (20*RWD_TIME_1) && (timer->TC_SR & AT91C_TC_CLKSTA)) + if(time >= (20 * RWD_TIME_1) ) + StopTicks(); + + old_level = level; + WDT_HIT(); +} + + WDT_HIT(); + DbpString("LEGIC Prime emulator stopped"); + switch_off_tag_rwd(); + FpgaDisableSscDma(); + LEDsoff(); + cmd_send(CMD_ACK, 1, 0, 0, 0, 0); +} + + +//----------------------------------------------------------------------------- +// Code up a string of octets at layer 2 (including CRC, we don't generate +// that here) so that they can be transmitted to the reader. Doesn't transmit +// them yet, just leaves them ready to send in ToSend[]. +//----------------------------------------------------------------------------- +// static void CodeLegicAsTag(const uint8_t *cmd, int len) +// { + // int i; + + // ToSendReset(); + + // // Transmit a burst of ones, as the initial thing that lets the + // // reader get phase sync. This (TR1) must be > 80/fs, per spec, + // // but tag that I've tried (a Paypass) exceeds that by a fair bit, + // // so I will too. + // for(i = 0; i < 20; i++) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // // Send SOF. + // for(i = 0; i < 10; i++) { + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // } + // for(i = 0; i < 2; i++) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // for(i = 0; i < len; i++) { + // int j; + // uint8_t b = cmd[i]; + + // // Start bit + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + + // // Data bits + // for(j = 0; j < 8; j++) { + // if(b & 1) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } else { + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // } + // b >>= 1; + // } + + // // Stop bit + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // // Send EOF. + // for(i = 0; i < 10; i++) { + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // ToSendStuffBit(0); + // } + // for(i = 0; i < 2; i++) { + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // ToSendStuffBit(1); + // } + + // // Convert from last byte pos to length + // ToSendMax++; +// } + +//----------------------------------------------------------------------------- +// The software UART that receives commands from the reader, and its state +// variables. +//----------------------------------------------------------------------------- +/* +static struct { + enum { + STATE_UNSYNCD, + STATE_GOT_FALLING_EDGE_OF_SOF, + STATE_AWAITING_START_BIT, + STATE_RECEIVING_DATA + } state; + uint16_t shiftReg; + int bitCnt; + int byteCnt; + int byteCntMax; + int posCnt; + uint8_t *output; +} Uart; +*/ +/* Receive & handle a bit coming from the reader. + * + * This function is called 4 times per bit (every 2 subcarrier cycles). + * Subcarrier frequency fs is 212kHz, 1/fs = 4,72us, i.e. function is called every 9,44us + * + * LED handling: + * LED A -> ON once we have received the SOF and are expecting the rest. + * LED A -> OFF once we have received EOF or are in error state or unsynced + * + * Returns: true if we received a EOF + * false if we are still waiting for some more + */ +// static RAMFUNC int HandleLegicUartBit(uint8_t bit) +// { + // switch(Uart.state) { + // case STATE_UNSYNCD: + // if(!bit) { + // // we went low, so this could be the beginning of an SOF + // Uart.state = STATE_GOT_FALLING_EDGE_OF_SOF; + // Uart.posCnt = 0; + // Uart.bitCnt = 0; + // } + // break; + + // case STATE_GOT_FALLING_EDGE_OF_SOF: + // Uart.posCnt++; + // if(Uart.posCnt == 2) { // sample every 4 1/fs in the middle of a bit + // if(bit) { + // if(Uart.bitCnt > 9) { + // // we've seen enough consecutive + // // zeros that it's a valid SOF + // Uart.posCnt = 0; + // Uart.byteCnt = 0; + // Uart.state = STATE_AWAITING_START_BIT; + // LED_A_ON(); // Indicate we got a valid SOF + // } else { + // // didn't stay down long enough + // // before going high, error + // Uart.state = STATE_UNSYNCD; + // } + // } else { + // // do nothing, keep waiting + // } + // Uart.bitCnt++; + // } + // if(Uart.posCnt >= 4) Uart.posCnt = 0; + // if(Uart.bitCnt > 12) { + // // Give up if we see too many zeros without + // // a one, too. + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // } + // break; + + // case STATE_AWAITING_START_BIT: + // Uart.posCnt++; + // if(bit) { + // if(Uart.posCnt > 50/2) { // max 57us between characters = 49 1/fs, max 3 etus after low phase of SOF = 24 1/fs + // // stayed high for too long between + // // characters, error + // Uart.state = STATE_UNSYNCD; + // } + // } else { + // // falling edge, this starts the data byte + // Uart.posCnt = 0; + // Uart.bitCnt = 0; + // Uart.shiftReg = 0; + // Uart.state = STATE_RECEIVING_DATA; + // } + // break; + + // case STATE_RECEIVING_DATA: + // Uart.posCnt++; + // if(Uart.posCnt == 2) { + // // time to sample a bit + // Uart.shiftReg >>= 1; + // if(bit) { + // Uart.shiftReg |= 0x200; + // } + // Uart.bitCnt++; + // } + // if(Uart.posCnt >= 4) { + // Uart.posCnt = 0; + // } + // if(Uart.bitCnt == 10) { + // if((Uart.shiftReg & 0x200) && !(Uart.shiftReg & 0x001)) + // { + // // this is a data byte, with correct + // // start and stop bits + // Uart.output[Uart.byteCnt] = (Uart.shiftReg >> 1) & 0xff; + // Uart.byteCnt++; + + // if(Uart.byteCnt >= Uart.byteCntMax) { + // // Buffer overflowed, give up + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // } else { + // // so get the next byte now + // Uart.posCnt = 0; + // Uart.state = STATE_AWAITING_START_BIT; + // } + // } else if (Uart.shiftReg == 0x000) { + // // this is an EOF byte + // LED_A_OFF(); // Finished receiving + // Uart.state = STATE_UNSYNCD; + // if (Uart.byteCnt != 0) { + // return TRUE; + // } + // } else { + // // this is an error + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // } + // } + // break; + + // default: + // LED_A_OFF(); + // Uart.state = STATE_UNSYNCD; + // break; + // } + + // return false; +// } +/* + +static void UartReset() { + Uart.byteCntMax = 3; + Uart.state = STATE_UNSYNCD; + Uart.byteCnt = 0; + Uart.bitCnt = 0; + Uart.posCnt = 0; + memset(Uart.output, 0x00, 3); +} +*/ +// static void UartInit(uint8_t *data) { + // Uart.output = data; + // UartReset(); +// } + +//============================================================================= +// An LEGIC reader. We take layer two commands, code them +// appropriately, and then send them to the tag. We then listen for the +// tag's response, which we leave in the buffer to be demodulated on the +// PC side. +//============================================================================= +/* +static struct { + enum { + DEMOD_UNSYNCD, + DEMOD_PHASE_REF_TRAINING, + DEMOD_AWAITING_FALLING_EDGE_OF_SOF, + DEMOD_GOT_FALLING_EDGE_OF_SOF, + DEMOD_AWAITING_START_BIT, + DEMOD_RECEIVING_DATA + } state; + int bitCount; + int posCount; + int thisBit; + uint16_t shiftReg; + uint8_t *output; + int len; + int sumI; + int sumQ; +} Demod; +*/ +/* + * Handles reception of a bit from the tag + * + * This function is called 2 times per bit (every 4 subcarrier cycles). + * Subcarrier frequency fs is 212kHz, 1/fs = 4,72us, i.e. function is called every 9,44us + * + * LED handling: + * LED C -> ON once we have received the SOF and are expecting the rest. + * LED C -> OFF once we have received EOF or are unsynced + * + * Returns: true if we received a EOF + * false if we are still waiting for some more + * + */ + +/* +static RAMFUNC int HandleLegicSamplesDemod(int ci, int cq) +{ + int v = 0; + int ai = ABS(ci); + int aq = ABS(cq); + int halfci = (ai >> 1); + int halfcq = (aq >> 1); + + switch(Demod.state) { + case DEMOD_UNSYNCD: + + CHECK_FOR_SUBCARRIER() + + if(v > SUBCARRIER_DETECT_THRESHOLD) { // subcarrier detected + Demod.state = DEMOD_PHASE_REF_TRAINING; + Demod.sumI = ci; + Demod.sumQ = cq; + Demod.posCount = 1; + } + break; + + case DEMOD_PHASE_REF_TRAINING: + if(Demod.posCount < 8) { + + CHECK_FOR_SUBCARRIER() + + if (v > SUBCARRIER_DETECT_THRESHOLD) { + // set the reference phase (will code a logic '1') by averaging over 32 1/fs. + // note: synchronization time > 80 1/fs + Demod.sumI += ci; + Demod.sumQ += cq; + ++Demod.posCount; + } else { + // subcarrier lost + Demod.state = DEMOD_UNSYNCD; + } + } else { + Demod.state = DEMOD_AWAITING_FALLING_EDGE_OF_SOF; + } + break; + + case DEMOD_AWAITING_FALLING_EDGE_OF_SOF: + + MAKE_SOFT_DECISION() + + //Dbprintf("ICE: %d %d %d %d %d", v, Demod.sumI, Demod.sumQ, ci, cq ); + // logic '0' detected + if (v <= 0) { + + Demod.state = DEMOD_GOT_FALLING_EDGE_OF_SOF; + + // start of SOF sequence + Demod.posCount = 0; + } else { + // maximum length of TR1 = 200 1/fs + if(Demod.posCount > 25*2) Demod.state = DEMOD_UNSYNCD; + } + ++Demod.posCount; + break; + + case DEMOD_GOT_FALLING_EDGE_OF_SOF: + ++Demod.posCount; + + MAKE_SOFT_DECISION() + + if(v > 0) { + // low phase of SOF too short (< 9 etu). Note: spec is >= 10, but FPGA tends to "smear" edges + if(Demod.posCount < 10*2) { + Demod.state = DEMOD_UNSYNCD; + } else { + LED_C_ON(); // Got SOF + Demod.state = DEMOD_AWAITING_START_BIT; + Demod.posCount = 0; + Demod.len = 0; + } + } else { + // low phase of SOF too long (> 12 etu) + if(Demod.posCount > 13*2) { + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + } + break; + + case DEMOD_AWAITING_START_BIT: + ++Demod.posCount; + + MAKE_SOFT_DECISION() + + if(v > 0) { + // max 19us between characters = 16 1/fs, max 3 etu after low phase of SOF = 24 1/fs + if(Demod.posCount > 3*2) { + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + } + } else { + // start bit detected + Demod.bitCount = 0; + Demod.posCount = 1; // this was the first half + Demod.thisBit = v; + Demod.shiftReg = 0; + Demod.state = DEMOD_RECEIVING_DATA; + } + break; + + case DEMOD_RECEIVING_DATA: + + MAKE_SOFT_DECISION() + + if(Demod.posCount == 0) { + // first half of bit + Demod.thisBit = v; + Demod.posCount = 1; + } else { + // second half of bit + Demod.thisBit += v; + Demod.shiftReg >>= 1; + // logic '1' + if(Demod.thisBit > 0) + Demod.shiftReg |= 0x200; + + ++Demod.bitCount; + + if(Demod.bitCount == 10) { + + uint16_t s = Demod.shiftReg; + + if((s & 0x200) && !(s & 0x001)) { + // stop bit == '1', start bit == '0' + uint8_t b = (s >> 1); + Demod.output[Demod.len] = b; + ++Demod.len; + Demod.state = DEMOD_AWAITING_START_BIT; + } else { + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + + if(s == 0x000) { + // This is EOF (start, stop and all data bits == '0' + return true; + } + } + } + Demod.posCount = 0; + } + break; + + default: + Demod.state = DEMOD_UNSYNCD; + LED_C_OFF(); + break; + } + return false; +} +*/ +/* +// Clear out the state of the "UART" that receives from the tag. +static void DemodReset() { + Demod.len = 0; + Demod.state = DEMOD_UNSYNCD; + Demod.posCount = 0; + Demod.sumI = 0; + Demod.sumQ = 0; + Demod.bitCount = 0; + Demod.thisBit = 0; + Demod.shiftReg = 0; + memset(Demod.output, 0x00, 3); +} + +static void DemodInit(uint8_t *data) { + Demod.output = data; + DemodReset(); +} +*/ + +/* + * Demodulate the samples we received from the tag, also log to tracebuffer + * quiet: set to 'TRUE' to disable debug output + */ + + /* + #define LEGIC_DMA_BUFFER_SIZE 256 + + static void GetSamplesForLegicDemod(int n, bool quiet) +{ + int max = 0; + bool gotFrame = false; + int lastRxCounter = LEGIC_DMA_BUFFER_SIZE; + int ci, cq, samples = 0; + + BigBuf_free(); + + // And put the FPGA in the appropriate mode + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ); + + // The response (tag -> reader) that we're receiving. + // Set up the demodulator for tag -> reader responses. + DemodInit(BigBuf_malloc(MAX_FRAME_SIZE)); + + // The DMA buffer, used to stream samples from the FPGA + int8_t *dmaBuf = (int8_t*) BigBuf_malloc(LEGIC_DMA_BUFFER_SIZE); + int8_t *upTo = dmaBuf; + + // Setup and start DMA. + if ( !FpgaSetupSscDma((uint8_t*) dmaBuf, LEGIC_DMA_BUFFER_SIZE) ){ + if (MF_DBGLEVEL > 1) Dbprintf("FpgaSetupSscDma failed. Exiting"); + return; + } + + // Signal field is ON with the appropriate LED: + LED_D_ON(); + for(;;) { + int behindBy = lastRxCounter - AT91C_BASE_PDC_SSC->PDC_RCR; + if(behindBy > max) max = behindBy; + + while(((lastRxCounter-AT91C_BASE_PDC_SSC->PDC_RCR) & (LEGIC_DMA_BUFFER_SIZE-1)) > 2) { + ci = upTo[0]; + cq = upTo[1]; + upTo += 2; + if(upTo >= dmaBuf + LEGIC_DMA_BUFFER_SIZE) { + upTo = dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t) upTo; + AT91C_BASE_PDC_SSC->PDC_RNCR = LEGIC_DMA_BUFFER_SIZE; + } + lastRxCounter -= 2; + if(lastRxCounter <= 0) + lastRxCounter = LEGIC_DMA_BUFFER_SIZE; + + samples += 2; + + gotFrame = HandleLegicSamplesDemod(ci , cq ); + if ( gotFrame ) + break; + } + + if(samples > n || gotFrame) + break; + } + + FpgaDisableSscDma(); + + if (!quiet && Demod.len == 0) { + Dbprintf("max behindby = %d, samples = %d, gotFrame = %d, Demod.len = %d, Demod.sumI = %d, Demod.sumQ = %d", + max, + samples, + gotFrame, + Demod.len, + Demod.sumI, + Demod.sumQ + ); + } + + //Tracing + if (Demod.len > 0) { + uint8_t parity[MAX_PARITY_SIZE] = {0x00}; + LogTrace(Demod.output, Demod.len, 0, 0, parity, false); + } +} + +*/ + +//----------------------------------------------------------------------------- +// Transmit the command (to the tag) that was placed in ToSend[]. +//----------------------------------------------------------------------------- +/* +static void TransmitForLegic(void) +{ + int c; + + FpgaSetupSsc(); + + while(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) + AT91C_BASE_SSC->SSC_THR = 0xff; + + // Signal field is ON with the appropriate Red LED + LED_D_ON(); + + // Signal we are transmitting with the Green LED + LED_B_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + + for(c = 0; c < 10;) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = 0xff; + c++; + } + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; + (void)r; + } + WDT_HIT(); + } + + c = 0; + for(;;) { + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { + AT91C_BASE_SSC->SSC_THR = ToSend[c]; + legic_prng_forward(1); // forward the lfsr + c++; + if(c >= ToSendMax) { + break; + } + } + if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { + volatile uint32_t r = AT91C_BASE_SSC->SSC_RHR; + (void)r; + } + WDT_HIT(); + } + LED_B_OFF(); +} +*/ + +//----------------------------------------------------------------------------- +// Code a layer 2 command (string of octets, including CRC) into ToSend[], +// so that it is ready to transmit to the tag using TransmitForLegic(). +//----------------------------------------------------------------------------- +/* +static void CodeLegicBitsAsReader(const uint8_t *cmd, uint8_t cmdlen, int bits) +{ + int i, j; + uint8_t b; + + ToSendReset(); + + // Send SOF + for(i = 0; i < 7; i++) + ToSendStuffBit(1); + + + for(i = 0; i < cmdlen; i++) { + // Start bit + ToSendStuffBit(0); + + // Data bits + b = cmd[i]; + for(j = 0; j < bits; j++) { + if(b & 1) { + ToSendStuffBit(1); + } else { + ToSendStuffBit(0); + } + b >>= 1; + } + } + + // Convert from last character reference to length + ++ToSendMax; +} +*/ +/** + Convenience function to encode, transmit and trace Legic comms + **/ +/* + static void CodeAndTransmitLegicAsReader(const uint8_t *cmd, uint8_t cmdlen, int bits) +{ + CodeLegicBitsAsReader(cmd, cmdlen, bits); + TransmitForLegic(); + if (tracing) { + uint8_t parity[1] = {0x00}; + LogTrace(cmd, cmdlen, 0, 0, parity, true); + } +} + +*/ +// Set up LEGIC communication +/* +void ice_legic_setup() { + + // standard things. + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + DemodReset(); + UartReset(); + + // Set up the synchronous serial port + FpgaSetupSsc(); + + // connect Demodulated Signal to ADC: + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + + // Signal field is on with the appropriate LED + LED_D_ON(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX | FPGA_HF_READER_TX_SHALLOW_MOD); + SpinDelay(20); + // Start the timer + //StartCountSspClk(); + + // initalize CRC + crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0); + + // initalize prng + legic_prng_init(0); +} +*/ \ No newline at end of file diff --git a/armsrc/legicrf.h b/armsrc/legicrf.h index 4bcf04899..5c3bd81b9 100644 --- a/armsrc/legicrf.h +++ b/armsrc/legicrf.h @@ -11,11 +11,38 @@ #ifndef __LEGICRF_H #define __LEGICRF_H -#include "proxmark3.h" +#include "proxmark3.h" // +#include "apps.h" +#include "util.h" // +#include "string.h" +#include "legic_prng.h" // legic PRNG impl +#include "crc.h" // legic crc-4 +#include "ticks.h" // timers +#include "legic.h" // legic_card_select_t struct -extern void LegicRfInfo(void); -extern void LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv); -extern void LegicRfWriter(uint16_t offset, uint16_t byte, uint8_t iv, uint8_t *data); extern void LegicRfSimulate(int phase, int frame, int reqresp); +extern int LegicRfReader(uint16_t offset, uint16_t len, uint8_t iv); +extern void LegicRfWriter(uint16_t offset, uint16_t byte, uint8_t iv, uint8_t *data); +extern void LegicRfInfo(void); + +uint32_t get_key_stream(int skip, int count); +void frame_send_tag(uint16_t response, uint8_t bits); +void frame_sendAsReader(uint32_t data, uint8_t bits); + +int legic_read_byte( uint16_t index, uint8_t cmd_sz); +bool legic_write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz); + +int legic_select_card(legic_card_select_t *p_card); +int legic_select_card_iv(legic_card_select_t *p_card, uint8_t iv); + +void LegicCommonInit(bool clear_mem); + +// emulator mem +void LegicEMemSet(uint32_t arg0, uint32_t arg1, uint8_t *data); +void LegicEMemGet(uint32_t arg0, uint32_t arg1); +void legic_emlset_mem(uint8_t *data, int offset, int numofbytes); +void legic_emlget_mem(uint8_t *data, int offset, int numofbytes); + +void ice_legic_setup(); #endif /* __LEGICRF_H */ diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 549ae0297b0391dbccebc28e88fa7353bf0d7651..f86820525980ac5282b8c35eb476d489ad7d7c98 100644 GIT binary patch literal 42175 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%0D#K|9 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 diff --git a/fpga/hi_read_tx.v b/fpga/hi_read_tx.v index 756683cdd..bede40965 100644 --- a/fpga/hi_read_tx.v +++ b/fpga/hi_read_tx.v @@ -71,8 +71,19 @@ always @(negedge ssp_clk) assign ssp_frame = (hi_byte_div == 3'b000); -assign ssp_din = 1'b0; +// Implement a hysteresis to give out the received signal on +// ssp_din. Sample at fc. +assign adc_clk = ck_1356meg; -assign dbg = ssp_frame; +// ADC data appears on the rising edge, so sample it on the falling edge +reg after_hysteresis; +always @(negedge adc_clk) +begin + if(& adc_d[6:4]) after_hysteresis <= 1'b1; + else if(~(| adc_d[6:4])) after_hysteresis <= 1'b0; +end -endmodule \ No newline at end of file +assign ssp_din = after_hysteresis; +assign dbg = after_hysteresis; + +endmodule