// convert 96 bit AWID FSK data to 8 digit BCD UID BOOL awid26_hex_to_uid(unsigned char *response, unsigned char *awid26) { BYTE i, tmp[96], tmp1[7]; unsigned int site; unsigned int id; if(!hextobinarray(tmp, awid26)) return FALSE; // data is in blocks of 4 bits - every 4th bit is parity, except the first // block which is all zeros for(i= 0 ; i < 4 ; ++i) if(tmp[i] != 0x00) return FALSE; // discard 1st block memcpy(tmp, tmp + 4, 92); // check and strip parity on the rest for(i= 1 ; i < 23 ; ++i) if(tmp[(i * 4) - 1] != parity(tmp + (i - 1) * 4, ODD, 3)) return FALSE; else memcpy((tmp + (i - 1) * 3), tmp + (i - 1) * 4, 3); // discard the rest of the header - 1 more 3 bit block memcpy(tmp, tmp + 3, 66); // next 8 bits is data length - should be 26: 0x1A binarraytohex(tmp1, tmp, 8); if(strcmp(tmp1, "1A") != 0) return FALSE; memcpy(tmp, tmp +8, 58); // standard wiegand parity check - even for 1st 12 bits, odd for 2nd 12 if(tmp[0] != parity(tmp + 1, EVEN, 12)) return FALSE; if(tmp[25] != parity(tmp + 13, ODD, 12)) return FALSE; // convert to hex, ignoring parity bits if(!binarraytohex(tmp1, tmp + 1, 24)) return FALSE; // convert hex to site/id sscanf(tmp1,"%2X%4X", &site, &id); // final output 8 byte BCD sprintf(response,"%03d%05d", site, id); return TRUE; }
BOOL hitag1_get_uid(BYTE *response) { BYTE tmp[132]; // 33 bits x 4 rwd_send(HITAG1_SET_CC, strlen(HITAG1_SET_CC), RESET, BLOCK, RWD_STATE_START_SEND, RFIDlerConfig.FrameClock, RFIDlerConfig.RWD_Sleep_Period, RFIDlerConfig.RWD_Wake_Period, RFIDlerConfig.RWD_Zero_Period, RFIDlerConfig.RWD_One_Period, RFIDlerConfig.RWD_Gap_Period, RFIDlerConfig.RWD_Wait_Switch_TX_RX); // get tag id in anti-collision mode (proprietary data format, so switch off manchester and read at double the data rate, for 4 x the data bits) RFIDlerConfig.Manchester= FALSE; if(read_ask_data(RFIDlerConfig.FrameClock, RFIDlerConfig.DataRate / 2, tmp, RFIDlerConfig.DataBits * 4, RFIDlerConfig.Sync, RFIDlerConfig.SyncBits, RFIDlerConfig.Timeout, ONESHOT_READ, BINARY) == RFIDlerConfig.DataBits * 4) { if (hitag_ac_to_bin(tmp, tmp, 132)) { // check sync bit if(tmp[0] != 0x01) return FALSE; binarraytohex(response, tmp + 1, 32); RFIDlerConfig.Manchester= TRUE; // delay for RX->TX period Delay_us((RFIDlerConfig.FrameClock * RFIDlerConfig.RWD_Wait_Switch_RX_TX) / 100); return TRUE; } } RFIDlerConfig.Manchester= TRUE; return FALSE; }
// auth in password mode BOOL hitag2_pwd_auth(BYTE *response, BYTE *pwd) { BYTE tmp[37]; // restart the tag if(!hitag2_get_uid(tmp)) return FALSE; if(strlen(pwd) == 0) pwd= HITAG2_PWD_DEFAULT; // use default transport key if none specified if(!hextobinstring(tmp, pwd)) return FALSE; // wait for RX->TX period, then send 32 bit PWD 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; // skip 1/2 bit to synchronise manchester HW_Skip_Bits= 1; // 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; binarraytohex(response, tmp + 5, 32); return TRUE; } return FALSE; }
// HDX reads data in Half Duplex mode // first we energise the TAG for long enough that it charges up // then we use sniffer to read response // each bit is 16 ticks, a 1 is 124.2 KHz and a 0 is 134.2 KHz // activation frequency is 134.2 KHz (745 FCs)) BOOL hdx_get_uid(BYTE *response) { BYTE i; BYTE tmp[HDX_DATABITS]; // this is a work in progress, so until reading produces any data, return empty return FALSE; // energise tag & wait for wakeup period InitHWReaderClock(OC_TOGGLE_PULSE, 745 / 2L, 745, RWD_STATE_ACTIVE); Delay_us((RFIDlerConfig.FrameClock * RFIDlerConfig.RWD_Wake_Period) / 100); // switch off clock and switch to sniffer mode detect_init(); // read to temporary array for speed for(i= 0 ; i < HDX_DATABITS ; ++i) tmp[i]= read_external_clock_burst(16) / 1000L; // convert to binary based on freq +- 3 for(i= 0 ; i < HDX_DATABITS ; ++i) { if (tmp[i] >= 122 && tmp[i] <= 128) tmp[i]= 0x01; else if (tmp[i] >= 129 && tmp[i] <= 135) tmp[i]= 0x00; else return FALSE; } binarraytohex(response, tmp, HDX_DATABITS); return TRUE; }
// convert null-terminated BCD UID (8 digits) to hid26 encoded hex // UID is SSSIIIII where S is site-code and I is ID BOOL bcd_to_hid26_hex(unsigned char *hid26, unsigned char *bcd) { if(bcd_to_hid26_bin(hid26, bcd)) return binarraytohex(hid26, hid26, 96); else return FALSE; }
// convert null-terminated hex UID (10 hex digits) to 64 bit unique encoded hex BOOL hex_to_unique_hex(unsigned char *unique, unsigned char *hex) { if(hex_to_unique_bin(unique, hex)) return binarraytohex(unique, unique, 64); else return FALSE; }
// convert null-terminated hex UID (10 digits) to em4x02 encoded hex BOOL hex_to_em4x02_hex(unsigned char *em, unsigned char *hex) { if(hex_to_em4x02_bin(em, hex)) return binarraytohex(em, em, 64); else return FALSE; }
// convert null-terminated BCD UID (8 digits) to awid26 encoded hex // UID is SSSIIIII where S is site-code and I is ID BOOL bcd_to_awid26_hex(unsigned char *awid26, unsigned char *bcd) { if(bcd_to_awid26_bin(awid26, bcd)) return binarraytohex(awid26, awid26, 96); else return FALSE; }
// read page - 32 bits // pages 2 - 15 are probably not readable! BOOL hitag1_read_page(BYTE *response, BYTE block) { BYTE tmp[HITAG1_BLOCKSIZE + 6]; // 32 bits plus 6 sync bits if(block > HITAG1_DATABLOCKS - 1) return FALSE; // get tag's UID for select command if(!hitag1_get_uid(tmp)) return FALSE; // select for read/write hitag1_select(tmp, tmp); // create 12 bit command block: HITAG1_RDPPAGE + 8 bits address strcpy(tmp, HITAG1_RDPPAGE); inttobinstring(tmp + 4, (unsigned int) block, 8); tmp[12]= '\0'; // ??? docs say 6 sync bits! if(!hitag1_send_command(tmp, tmp, NO_RESET, NO_SYNC, HITAG1_BLOCKSIZE + 1, NO_ACK)) return FALSE; // check sync if(tmp[0] != 0x01) return FALSE; binarraytohex(response, tmp + 1, HITAG1_BLOCKSIZE); return TRUE; }
// convert 96 bit HID FSK data to 8 digit BCD UID BOOL hid26_hex_to_uid(unsigned char *response, unsigned char *hid26) { BYTE tmp[96], tmp1[7]; unsigned int site; unsigned int id; // todo parity checking etc. if(!hextobinarray(tmp, hid26)) return FALSE; // skip 44 bit header memcpy(tmp, tmp + 44, 52); // manchester decode if(manchester_decode(tmp, tmp, 52) != 26) return FALSE; // convert to hex, ignoring parity bits if(!binarraytohex(tmp1, tmp + 1, 24)) return FALSE; // convert hex to site/id sscanf(tmp1,"%2X%4X", &site, &id); // final output 8 byte BCD sprintf(response,"%03d%05d", site, id); return TRUE; }
BOOL em4x02_hex_to_uid(BYTE *response, BYTE *hex) { BYTE tmp[40]; if(em4x02_hex_to_bin(tmp, hex)) return binarraytohex(response, tmp, 40); return FALSE; }
// convert human readable UID to 128 bit fdx-b HEX string BOOL uid_to_hdx_hex(BYTE *hex, BYTE *uid) { BYTE tmp[128]; if(!uid_to_fdxb_bin(tmp, uid)) return FALSE; binarraytohex(hex, tmp, 128); return TRUE; }
// convert 64 bit unique encoded hex to 10 hex digit 40 bit UID // this is the same as em4x02 but with final output bits in a different order BOOL unique_hex_to_hex(unsigned char *hex, unsigned char *unique) { BYTE tmp[64]; if(em4x02_hex_to_bin(tmp, unique)) { string_reverse(tmp, 40); binarraytohex(hex, tmp, 40); string_byteswap(hex,10); string_reverse(hex, 10); return TRUE; } return FALSE; }
BOOL hitag2_get_uid(BYTE *response) { BYTE tmp[37]; // START_AUTH kills active crypto session CryptoActive= FALSE; if(!rwd_send(HITAG2_START_AUTH, strlen(HITAG2_START_AUTH), RESET, BLOCK, RWD_STATE_START_SEND, RFIDlerConfig.FrameClock, RFIDlerConfig.RWD_Sleep_Period, RFIDlerConfig.RWD_Wake_Period, RFIDlerConfig.RWD_Zero_Period, RFIDlerConfig.RWD_One_Period, RFIDlerConfig.RWD_Gap_Period, RFIDlerConfig.RWD_Wait_Switch_TX_RX)) return FALSE; if(read_ask_data(RFIDlerConfig.FrameClock, RFIDlerConfig.DataRate, tmp, RFIDlerConfig.DataBits, RFIDlerConfig.Sync, RFIDlerConfig.SyncBits, RFIDlerConfig.Timeout, ONESHOT_READ, BINARY) == RFIDlerConfig.DataBits) { // check sync bits if (memcmp(tmp, Hitag2Sync, 5) != 0) return FALSE; binarraytohex(response, tmp + 5, 32); return TRUE; } return FALSE; }
// select HITAG1 for read/write operations - return config page BOOL hitag1_select(BYTE *response, BYTE *uid) { BYTE command[38], tmp1[33]; // command: 5 bit command + 32 bit uid + NULL strcpy(command, HITAG1_SELECT); if(!hextobinstring(command + 5, uid)) return FALSE; // send command and get back 1 sync bit + 32 bit configpage if(!hitag1_send_command(tmp1, command, NO_RESET, NO_SYNC, 33, NO_ACK)) return FALSE; // check sync bit if(tmp1[0] == 0x01) { binarraytohex(response, tmp1 + 1, 32); return TRUE; } return FALSE; }
// copy raw UID to data blocks for re-transmission by target tag void tag_raw_uid_to_data(BYTE *data, BYTE *uid, BYTE host_tagtype) { BYTE tmp[MAXUID + 1]; unsigned int i; switch(host_tagtype) { // EM4X05 sends everything LSB, so reverse data case TAG_TYPE_EM4X05: hextobinarray(tmp, uid); for(i= 0 ; i < HEXTOBITS(strlen(uid)) ; i += EM4X05_BLOCKSIZE) string_reverse(tmp + i, EM4X05_BLOCKSIZE); binarraytohex(data, tmp, HEXTOBITS(strlen(uid))); break; // for most tags a straight copy will do default: strcpy(data, uid); break; } }
BOOL hitag2_read_page(BYTE *response, BYTE block) { BYTE tmp[37]; if(block > HITAG2_DATABLOCKS - 1) return FALSE; // create 10 bit command block: HITAG2_READ_PAGE + 3 bits address + invert(HITAG2_READ_PAGE + 3 bits address) memcpy(tmp, HITAG2_READ_PAGE, 2); inttobinstring(tmp + 2, (unsigned int) block, 3); invertbinstring(tmp + 5, tmp); tmp[10]= '\0'; // encrypt command if in crypto mode if(CryptoActive) hitag2_binstring_crypt(tmp, tmp); // send with RX->TX delay and no reset 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; // skip 1/2 bit to synchronise manchester HW_Skip_Bits= 1; 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; binarraytohex(response, tmp + 5, 32); // decrypt if(CryptoActive) return hitag2_hex_crypt(response, response); return TRUE; } return FALSE; }
// specify oneshot if data is not repeated (i.e. in response to a command rather than a cycling UID) unsigned int read_ask_data(unsigned int period_us, unsigned int ticks, BYTE *data, unsigned int bits, unsigned char *sync, unsigned char syncbits, unsigned int timeout_us, BOOL oneshot, BYTE format) { unsigned int i, j; BOOL found= FALSE; BYTE repeat= 0; // if we're not doing a 1-shot read, we can grab double the data to allow for sync search if(oneshot) repeat= 1; else repeat= 2; if(!read_ASK_HW_clock(period_us, ticks, TmpBits, bits * repeat, timeout_us, oneshot)) return 0; if(FakeRead) return 0; // reset timer if(timeout_us) GetTimer_us(RESET); // wait for semaphore to tell us read has finished while(mLED_Read == mLED_ON) if(timeout_us) if (GetTimer_us(NO_RESET) > timeout_us) return 0; if(Manchester_Error) return 0; // find start of data if(syncbits) { for(i= j= found= 0 ; i < bits * repeat ; ++i) { if(TmpBits[i] == getbit(sync[j / 8], 7 - (j % 8))) ++j; else { i -= j; j= 0; } if(j == syncbits) { found= TRUE; break; } } if(!found) return 0; } // point at start of sync if(syncbits) i -= syncbits - 1; else i= 0; // copy to output buffer switch(format) { case HEX: return binarraytohex(data, TmpBits + i, bits); case BINARY: default: memcpy(data, TmpBits + i, bits); return bits; } }
// 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]; 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 srand(Led_Count); 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; // skip 1/2 bit to synchronise manchester HW_Skip_Bits= 1; // 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; }
// read a PSK1 bitstream and find start of data // // parameters: // // period_us - clock period in uS // ticks - clock periods per bit // data - buffer for final data [no. of bits]) // bits - no. of bits to read (always specify in bits, not bytes) // leadin - no. of leading '0' bits // bytes - if set, return data as bytes, otherwise bits // timeout_us - timeout in uS, 0 for blocking // min_pulse_us - mininum pulse width from external reader for 'good' read. 0 for read anything. // // return: // // data will be filled with one byte per bit (value 0x00 or 0x01) // return will be 0 if leadin not found, or no. of BITS read // specify oneshot if data is not repeated (i.e. in response to a command rather than a cycling UID) unsigned int read_psk1_data(unsigned int period_us, unsigned int ticks, BYTE *data, unsigned int bits, unsigned char *sync, unsigned char syncbits, unsigned int timeout_us, unsigned int min_pulse_us, BOOL oneshot, BYTE format) { unsigned int i= 0, j; BYTE found= 0, inverted= 0, repeat; if(oneshot) repeat= 1; else repeat= 2; // if we're not doing a 1-shot read, we can grab double the data to allow for sync search read_PSK1_HW_clock(period_us, ticks, TmpBits, bits * repeat, timeout_us, min_pulse_us); if(FakeRead) return 0; // reset timer if(timeout_us) GetTimer_us(RESET); // wait for semaphore to tell us read has finished while(mLED_Read == mLED_ON) if(timeout_us) if (GetTimer_us(NO_RESET) > timeout_us) return 0; if(PSK_Read_Error) return 0; // find start of data while(syncbits) { for(i= j= found= 0 ; i < bits * repeat ; ++i) { if(TmpBits[i] == getbit(sync[j / 8], 7 - (j % 8))) ++j; else { i -= j; j= 0; } if(j == syncbits) { found= 1; break; } } // point at start of sync i -= syncbits - 1; if(found) break; if(inverted) return 0; // with PSK we have no way of knowing if 1st bit was a 0 or 1, so invert data and try again inverted= 1; for(i= 0 ; i < bits * repeat ; ++i) TmpBits[i] ^= 1; } // copy to output buffer switch(format) { case HEX: return binarraytohex(data, TmpBits + i, bits); case BINARY: default: memcpy(data, TmpBits + i, bits); data[bits]= '\0'; return bits; } }