// wait for a gap on the reader coil and return it's length // note this will return a partial gap if it's already started // return pulse length in ticks unsigned long get_reader_gap(unsigned int timeout_us) { // wait for start of gap GetTimer_us(RESET); while(READER_DATA) if(timeout_us) if (GetTimer_us(NO_RESET) > timeout_us) return 0; // measure gap length GetTimer_us(RESET); while(!READER_DATA) if(timeout_us) if (GetTimer_us(NO_RESET) > timeout_us) return 0; return GetTimer_ticks(NO_RESET); }
// h/w clock reader - initialise data pointers for ISR and start timers // timer2 creates clock output for external reader // timer4 reads data bit values // period_us == clock for reader // ticks == clock periods per bit // bits == number of bits to read // oneshot must be set if we are reading data in response to RWD, so no repeating stream BOOL read_ASK_HW_clock(unsigned int period, unsigned int ticks, BYTE *data, unsigned int bits, unsigned int timeout_us, BOOL oneshot) { unsigned long dwell, time; BYTE count; Manchester_Error= FALSE; // if we're manchester or bi-phase encoding, we want to clock twice as fast so we can read both halves of the bit if(RFIDlerConfig.Manchester || RFIDlerConfig.BiPhase) { ticks /= 2; HW_Bits= (unsigned long) bits * 2; } else HW_Bits= (unsigned long) bits; // point globals at data for ISR EMU_Data= data; memset(EMU_Data, '\0', bits); // stop USB interfering USBMaskInterrupts(); // start clock if not already running if(!mGetLED_Clock() == mLED_ON) { InitHWReaderClock(OC_TOGGLE_PULSE, period / 2L, period, RWD_STATE_ACTIVE); // give reader time to wake up and settle Delay_us((RFIDlerConfig.FrameClock * RFIDlerConfig.RWD_Wake_Period) / 100); } // align ourselves to reader's bit period by waiting until the end of a pulse GetTimer_us(RESET); count= READER_DATA; while(count == READER_DATA) if(GetTimer_us(NO_RESET) > timeout_us) return FALSE; // convert to ticks period= CONVERT_TO_TICKS(period); // biphase cannot auto-align when it detects a half-bit error, so we must align // on a full bit before we start if(!oneshot && RFIDlerConfig.BiPhase) { dwell= period * ticks * 2; count= 0; while((time= get_reader_gap(timeout_us))) { if(!time || count == 255) return; else if(approx(time, dwell, 10)) break; ++count; } } // wait for half the bit period so we sample mid-tick, not just as bit is toggling dwell= ((period * ticks) / 2L); GetTimer_ticks(RESET); //DEBUG_PIN_2= HIGH; while(GetTimer_ticks(NO_RESET) < dwell) ; // reset timer for external timeouts GetTimer_us(RESET); //DEBUG_PIN_2= LOW; // re-start reader ISR to read this bit if required InitHWReaderISR(period * ticks - 1L, TRUE); return TRUE; }
// note that PRN is not created with security in mind - just using a simple seed BOOL hitag2_crypto_auth(BYTE *response, BYTE *hexkey) { BYTE tmp[65], tmphex[9]; int seed; unsigned long long key; unsigned long uid; unsigned long initvec; // use default transport key if none specified if(strlen(hexkey) == 0) hexkey= HITAG2_KEY_DEFAULT; // get UID for initialisation if(!hitag2_get_uid(tmp)) return FALSE; // convert to numerics for crypto routines uid= hexreversetoulong(tmp); key= hexreversetoulonglong(hexkey); // generate 32 bit PRN for challenge seed= (int) GetTimer_ticks(NO_RESET); srand(seed); initvec= rand(); initvec <<= 16; initvec += rand(); // prepare to send IV in the clear to tag ulongtobinstring(tmp, initvec, 32); // convert IV to MSB for crypto routines binstringtohex(tmphex, tmp); initvec= hexreversetoulong(tmphex); // initialise crypto hitag2_init(&Hitag_Crypto_State, rev64(key), rev32(uid), rev32(initvec)); // send inverse of 1st 32bits of keystream to tag for authentication ulongtobinstring(tmp + 32, hitag2_crypt(0xFFFFFFFF, 32), 32); // restart the tag & auth process if(!hitag2_get_uid(TmpBuff)) return FALSE; // wait for RX->TX period, then send PRN+secret if(!rwd_send(tmp, strlen(tmp), NO_RESET, BLOCK, RWD_STATE_WAKING, RFIDlerConfig.FrameClock, 0, RFIDlerConfig.RWD_Wait_Switch_RX_TX, RFIDlerConfig.RWD_Zero_Period, RFIDlerConfig.RWD_One_Period, RFIDlerConfig.RWD_Gap_Period, RFIDlerConfig.RWD_Wait_Switch_TX_RX)) return FALSE; // get 37 bit response: sync + config byte + 24 bit TAG pwd if(read_ask_data(RFIDlerConfig.FrameClock, RFIDlerConfig.DataRate, tmp, 37, RFIDlerConfig.Sync, RFIDlerConfig.SyncBits, RFIDlerConfig.Timeout, ONESHOT_READ, BINARY) == 37) { // check sync bits if (memcmp(tmp, Hitag2Sync, 5) != 0) return FALSE; CryptoActive= TRUE; // decrypt binarraytohex(response, tmp + 5, 32); return hitag2_hex_crypt(response, response); } return FALSE; }
// Reader clock ISR // also process RWD commands while we toggle clock line void __ISR(_OUTPUT_COMPARE_5_VECTOR, ipl6auto) reader_clock_tick (void) { static unsigned int count= 0; static unsigned int bcount= 0; // Clear interrupt flag mOC5ClearIntFlag(); mLED_Clock_On(); // process RWD commands (if any) switch (RWD_State) { case RWD_STATE_INACTIVE: case RWD_STATE_ACTIVE: //DEBUG_PIN_4= !DEBUG_PIN_4; break; case RWD_STATE_GO_TO_SLEEP: //DEBUG_PIN_4= !DEBUG_PIN_4; //DEBUG_PIN_4= !DEBUG_PIN_4; // initial shutdown of coil to restart tag READER_CLOCK_ENABLE_OFF(); COIL_OUT_LOW(); // time small amounts with ticks, large with uS if(RWD_Sleep_Period > MAX_TIMER5_TICKS) Delay_us(CONVERT_TICKS_TO_US(RWD_Sleep_Period)); else { WriteTimer5(0); while(GetTimer_ticks(NO_RESET) < RWD_Sleep_Period) ; } count= 0; RWD_State= RWD_STATE_WAKING; // restart clock only if we have a wake period if(RWD_Wake_Period) READER_CLOCK_ENABLE_ON(); break; case RWD_STATE_WAKING: //DEBUG_PIN_4= !DEBUG_PIN_4; // leave coil running for wakeup period if(count == RWD_Wake_Period) { count= 0; bcount = 0; if(*RWD_Command_ThisBit != '*') RWD_State= RWD_STATE_START_SEND; else RWD_State= RWD_STATE_ACTIVE; } else count++; break; case RWD_STATE_START_SEND: //DEBUG_PIN_4= !DEBUG_PIN_4; // send initial gap // stop modulation of coil and wait READER_CLOCK_ENABLE_OFF(); COIL_OUT_LOW(); count= 0; if(RWD_Barrier) RWD_State= RWD_STATE_SENDING_BARRIER_LOW; else RWD_State= RWD_STATE_SENDING_BIT_LOW; //DEBUG_PIN_4= !DEBUG_PIN_4; // restart clock //READER_CLOCK_ENABLE_ON(); break; case RWD_STATE_SENDING_BIT_HIGH: //DEBUG_PIN_4= !DEBUG_PIN_4; // clock running for bit period, then wait for gap period if((*RWD_Command_ThisBit && count == RWD_One_Period) || (!*RWD_Command_ThisBit && count == RWD_Zero_Period)) { count= 0; if(*RWD_Command_ThisBit == '*') RWD_State= RWD_STATE_POST_WAIT; else if(RWD_Barrier && bcount == 7) RWD_State= RWD_STATE_SENDING_BARRIER_LOW; else RWD_State= RWD_STATE_SENDING_BIT_LOW; READER_CLOCK_ENABLE_OFF(); COIL_OUT_LOW(); bcount++; if(bcount == 8) bcount = 0; } else count++; break; case RWD_STATE_SENDING_BIT_LOW: if((*RWD_Command_ThisBit && count == RWD_One_Gap_Period) || (!*RWD_Command_ThisBit && count == RWD_Zero_Gap_Period)) { ++RWD_Command_ThisBit; count= 0; RWD_State= RWD_STATE_SENDING_BIT_HIGH; // restart clock READER_CLOCK_ENABLE_ON(); } else count++; break; case RWD_STATE_SENDING_BARRIER_HIGH: if(count == RWD_One_Barrier_Period){ count= 0; RWD_State= RWD_STATE_SENDING_BIT_LOW; READER_CLOCK_ENABLE_OFF(); COIL_OUT_LOW(); }else count++; break; case RWD_STATE_SENDING_BARRIER_LOW: if(count == RWD_Zero_Barrier_Period){ count= 0; RWD_State= RWD_STATE_SENDING_BARRIER_HIGH; READER_CLOCK_ENABLE_ON(); }else count++; break; case RWD_STATE_POST_WAIT: //DEBUG_PIN_4= !DEBUG_PIN_4; // coil running for forced post-command wait period if(count == RWD_Post_Wait) { count= 0; RWD_State= RWD_STATE_ACTIVE; } else count++; break; default: break; } }