// q5 needs special timings etc., so have a master send command routine to keep things simple // response length in bits BOOL q5_send_command(BYTE *response, BYTE *command, BYTE length, BOOL reset, BOOL sync, BYTE response_length) { // send command if(!rwd_send(command, length, reset, BLOCK, RWD_STATE_START_SEND, RFIDlerConfig.FrameClock, Q5_START_GAP - RFIDlerConfig.RWD_Gap_Period, 0, RFIDlerConfig.RWD_Zero_Period, RFIDlerConfig.RWD_One_Period, RFIDlerConfig.RWD_Gap_Period, RFIDlerConfig.RWD_Wait_Switch_TX_RX)) return FALSE; if(!response_length) return TRUE; // wait for Q5 damping to kick in: q5 may create a false pulse during RWD receipt, so wait for it to recognise // command and shut down before trying to read response. GetTimer_us(RESET); while(READER_DATA) if(GetTimer_us(NO_RESET) > RFIDlerConfig.Timeout) return FALSE; // read response as raw data with manchester auto-correct as first bit may be a '0' Manchester_Auto_Correct= TRUE; // DEBUG_PIN_4= !DEBUG_PIN_4; // DEBUG_PIN_4= !DEBUG_PIN_4; if(read_ask_data(RFIDlerConfig.FrameClock, RFIDlerConfig.DataRate, response, response_length, RFIDlerConfig.Sync, sync ? RFIDlerConfig.SyncBits : 0, RFIDlerConfig.Timeout, ONESHOT_READ, HEX) == response_length) { // delay for RX->TX time Delay_us((RFIDlerConfig.FrameClock * RFIDlerConfig.RWD_Wait_Switch_RX_TX) / 100); return 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; }
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; }
// calcuate CRC & send command - response in binarray BOOL hitag1_send_command(BYTE *response, BYTE *command, BOOL reset, BOOL sync, BYTE response_length) { BYTE crc= HITAG1_CRC_PRESET, tmp[HITAG1_MAX_COMMAND_LEN], length; // supplied command is 4 bits + 8 bit address. we add 8 bit CRC // calculate crc length= binstringtobinarray(tmp, command); hitag1_binarray_crc(&crc, tmp, length); // add 8 bit crc to command inttobinarray(tmp + length, (unsigned int) crc, 8); length += 8; // send command - first gap is GAP, not SLEEP if(!rwd_send(tmp, length, reset, BLOCK, RWD_STATE_START_SEND, RFIDlerConfig.FrameClock, RFIDlerConfig.RWD_Gap_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(!response_length) return TRUE; // read response as binary data //Manchester_Auto_Correct= TRUE; if(read_ask_data(RFIDlerConfig.FrameClock, RFIDlerConfig.DataRate, response, response_length, RFIDlerConfig.Sync, sync ? RFIDlerConfig.SyncBits : 0, RFIDlerConfig.Timeout, ONESHOT_READ, BINARY) == response_length) { // delay for RX->TX time Delay_us((RFIDlerConfig.FrameClock * RFIDlerConfig.RWD_Wait_Switch_RX_TX) / 100); return TRUE; } return FALSE; }
// response length in bits BOOL t55x7_send_command(BYTE *response, BYTE *command, BYTE length, BOOL reset, BOOL sync, BYTE response_length) { // send command if(!rwd_send(command, length, reset, BLOCK, RWD_STATE_START_SEND, RFIDlerConfig.FrameClock, T55X7_START_GAP - RFIDlerConfig.RWD_Gap_Period, 0, RFIDlerConfig.RWD_Zero_Period, RFIDlerConfig.RWD_One_Period, RFIDlerConfig.RWD_Gap_Period, RFIDlerConfig.RWD_Wait_Switch_TX_RX)) return FALSE; if(!response_length) return TRUE; // wait for T55x7 damping to kick in: t55x7 may create a false pulse during RWD receipt, so wait for it to recognise // command and shut down before trying to read response. GetTimer_us(RESET); while(READER_DATA) if(GetTimer_us(NO_RESET) > RFIDlerConfig.Timeout) return FALSE; // skip 4 bit Sequence Terminator pattern and leading '0' HW_Skip_Bits= 10; // 10 primitive 'bits' manchester encoded // DEBUG_PIN_4= !DEBUG_PIN_4; // DEBUG_PIN_4= !DEBUG_PIN_4; if(read_ask_data(RFIDlerConfig.FrameClock, RFIDlerConfig.DataRate, response, response_length, RFIDlerConfig.Sync, sync ? RFIDlerConfig.SyncBits : 0, RFIDlerConfig.Timeout, ONESHOT_READ, HEX) == response_length) { // delay for RX->TX time Delay_us((RFIDlerConfig.FrameClock * RFIDlerConfig.RWD_Wait_Switch_RX_TX) / 100); return TRUE; } return FALSE; }
// try to find values that correctly transmit all commands to q5 // to test this properly, q5 should have invalid data in it's data blocks // so that only a GET_TRACE_DATA command will return a true value BOOL q5_rwd_test(BYTE *pattern) { BYTE start_gap, gap, one, zero, i, tmp[Q5_BLOCKSIZE + 1]; BOOL found= FALSE, blank; // min/max from datasheets for(one= 48 ; one <= 63 ; ++one) for(zero= 16; zero <= 31 ; ++zero) for(gap= 10 ; gap <= 50 ; ++gap) for(start_gap= 11 ; start_gap <= 50 ; ++start_gap) { RFIDlerConfig.Manchester= TRUE; blank= TRUE; if(get_user_abort()) return found; RFIDlerConfig.RWD_Gap_Period= gap; RFIDlerConfig.RWD_One_Period= one; RFIDlerConfig.RWD_Zero_Period= zero; // reset tag get_tag_uid(tmp); // try to switch off modulation // send command with start gap: reset with sleep time set to start gap, and wake time set to 0 as we transmit the 1st bit immediately // note that we must also subtract standard gap period as it will be added to the front of the first bit by default. rwd_send(Q5_MODULATION_DEFEAT, strlen(Q5_MODULATION_DEFEAT), NO_RESET, BLOCK, RWD_STATE_START_SEND, RFIDlerConfig.FrameClock, start_gap - RFIDlerConfig.RWD_Gap_Period, 0, RFIDlerConfig.RWD_Zero_Period, RFIDlerConfig.RWD_One_Period, RFIDlerConfig.RWD_Gap_Period, RFIDlerConfig.RWD_Wait_Switch_TX_RX); // read a block with no sync & no manchester - will be all '0' if not modulating RFIDlerConfig.Manchester= FALSE; if(read_ask_data(RFIDlerConfig.FrameClock, RFIDlerConfig.DataRate, tmp, RFIDlerConfig.DataBits, RFIDlerConfig.Sync, 0, RFIDlerConfig.Timeout, NO_ONESHOT_READ, HEX) == RFIDlerConfig.DataBits) { for(i= 0 ; i < HEXDIGITS(RFIDlerConfig.DataBits) ; ++i) if(tmp[i] != '0') blank= FALSE; RFIDlerConfig.Manchester= TRUE; if(blank && get_tag_uid(tmp) && q5_read_block(tmp, 0)) { UserMessageNum("\r\nFound tag with start_gap %d", start_gap); UserMessageNum(" gap %d", gap); UserMessageNum(" one %d", one); UserMessageNum(" zero %d", zero); found= TRUE; } } } UserMessage("%s", "\r\n"); return found; }
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; }
// return raw HEX UID BOOL ask_raw_get_uid(BYTE *response) { unsigned int i; BOOL blank= TRUE; BYTE tmp[MAXUID + 1]; memset(tmp, '0', MAXUID); tmp[MAXUID]= '\0'; if (read_ask_data(RFIDlerConfig.FrameClock, RFIDlerConfig.DataRate, tmp, RFIDlerConfig.DataBits, RFIDlerConfig.Sync, RFIDlerConfig.SyncBits, RFIDlerConfig.Timeout, NO_ONESHOT_READ, HEX) != RFIDlerConfig.DataBits) return FALSE; for(i= 0 ; i < strlen(tmp) ; ++i) if(tmp[i] != '0') blank= FALSE; if(blank) return FALSE; strcpy(response, tmp); return TRUE; }
// try to find values that correctly transmit all commands to q5 // to test this properly, q5 should have invalid data in it's data blocks // so that only a GET_TRACE_DATA command will return a true value BOOL q5_rwd_test(BYTE *pattern) { BYTE gap, one, zero, tmp[Q5_BLOCKSIZE + 1]; BOOL found= FALSE, blank; // min/max from datasheets for(one= 48 ; one <= 63 ; ++one) for(zero= 16; zero <= 31 ; ++zero) for(gap= 10 ; gap <= 50 ; ++gap) { RFIDlerConfig.Manchester= TRUE; blank= TRUE; if(get_user_abort()) return found; RFIDlerConfig.RWD_Gap_Period= gap; RFIDlerConfig.RWD_One_Period= one; RFIDlerConfig.RWD_Zero_Period= zero; // reset tag get_tag_uid(tmp); // try to switch off modulation rwd_send(Q5_MODULATION_DEFEAT, strlen(Q5_MODULATION_DEFEAT), NO_RESET, BLOCK, RWD_STATE_START_SEND, RFIDlerConfig.FrameClock, RFIDlerConfig.RWD_Gap_Period, 0, RFIDlerConfig.RWD_Zero_Period, RFIDlerConfig.RWD_One_Period, RFIDlerConfig.RWD_Gap_Period, RFIDlerConfig.RWD_Wait_Switch_TX_RX); // read a block with no sync & no manchester - will be all '0' if not modulating RFIDlerConfig.Manchester= FALSE; if(read_ask_data(RFIDlerConfig.FrameClock, RFIDlerConfig.DataRate, tmp, RFIDlerConfig.DataBits, RFIDlerConfig.Sync, 0, RFIDlerConfig.Timeout, NO_ONESHOT_READ, HEX) == RFIDlerConfig.DataBits) { if(strcmp(tmp, "0000000000000000") != 0) blank= FALSE; RFIDlerConfig.Manchester= TRUE; if(blank && get_tag_uid(tmp) && q5_read_block(tmp, 0)) { UserMessageNum("\r\nFound tag with gap %d", gap); UserMessageNum(" one %d", one); UserMessageNum(" zero %d", zero); found= TRUE; } } } UserMessage("%s", "\r\n"); return found; }
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; }
BOOL hitag2_write_page(BYTE block, BYTE *data) { BYTE command[11], tmp[37], tmp1[37], tmp2[37], tmphex[9]; if(block > HITAG2_DATABLOCKS - 1) return FALSE; // create 10 bit command block: HITAG2_WRIE_PAGE + 3 bits address + invert(HITAG2_WRITE_PAGE + 3 bits address) memcpy(command, HITAG2_WRITE_PAGE, 2); inttobinstring(command + 2, (unsigned int) block, 3); invertbinstring(command + 5, command); command[10]= '\0'; // encrypt command if in crypto mode if(CryptoActive) hitag2_binstring_crypt(tmp, command); else memcpy(tmp, command, 11); // convert data to binstring for send if(hextobinstring(tmp1, data) != 32) return FALSE; // send command 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; // get ACK - 10 bytes + sync if(read_ask_data(RFIDlerConfig.FrameClock, RFIDlerConfig.DataRate, tmp2, 15, RFIDlerConfig.Sync, RFIDlerConfig.SyncBits, RFIDlerConfig.Timeout, ONESHOT_READ, BINARY) == 15) { // check sync bits if (memcmp(tmp2, Hitag2Sync, 5) != 0) return FALSE; // decrypt if(CryptoActive) hitag2_binarray_crypt(tmp2 + 5, tmp2 + 5, 10); // check ACK - should be our original command echo'd back binarraytobinstring(tmp2 + 5, tmp2 + 5, 10); if(memcmp(command, tmp2 + 5, 10) != 0) return FALSE; } // encrypt data if in crypto mode if(CryptoActive) hitag2_binstring_crypt(tmp1, tmp1); // send data with RX->TX delay and no reset if(!rwd_send(tmp1, strlen(tmp1), 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; // no ack, so read back and verify // delay for long enough to allow write plus RX->TX period Delay_us((HITAG2_WRITE_DELAY * RFIDlerConfig.FrameClock + RFIDlerConfig.RWD_Wait_Switch_RX_TX * RFIDlerConfig.FrameClock) / 100); if(!hitag2_read_page(tmp, block)) return FALSE; if(memcmp(tmp, data, 8) != 0) return FALSE; 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]; 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; }