void store_pwm(BYTE *data) { uint32_t count; BYTE *entry; BYTE datastr[20]; memcpy(&count, (void *)HITAG2_PWM_NVM_ADDRESS, 4); // UserMessage("current count: %d\r\n", count); if (count >= HITAG2_PWM_NVM_MAX) { return; } // convert data from bits to hexstring binstringtohex(datastr, data); // store data entry = (BYTE *)HITAG2_PWM_NVM_DATA + (20 * count); NVMProgram((void *) entry, (const void *) datastr, 20, (void*) TmpBuff); // update count count++; NVMProgram((void *) HITAG2_PWM_NVM_ADDRESS, (const void *) &count, sizeof(uint32_t), (void*) TmpBuff); // return; }
// decode externally sniffed PWM BOOL hitag2_decode_pwm(unsigned long pulses[], unsigned long gaps[], unsigned int count) { unsigned int i, j; BOOL decoded= FALSE, encrypted= FALSE, auth= FALSE; BYTE out[65], tmp[33], x; // max response from hitag2 is 64 bits j= 0; while(j < count) { i= generic_decode_pwm(out, &pulses[j], 10, 256, &gaps[j], 10, 80, count - j); if(i) { // there are only 4 message sizes, so decode accordingly switch(strlen(out)) { // start_auth case 5: auth= FALSE; if(memcmp(out, HITAG2_START_AUTH, 5) == 0) { UserMessage("\r\n%s, START_AUTH", out); auth= TRUE; } else UserMessage("\r\n%s, ?INVALID?", out); encrypted= FALSE; break; // read/write/halt case 10: auth= FALSE; if(encrypted) { UserMessage("\r\n%s, CMD_ENCRYPTED", out); break; } if(memcmp(out, HITAG2_HALT, 2) == 0) { UserMessage("\r\n%s, HALT", out); break; } if(memcmp(out, HITAG2_READ_PAGE, 2) == 0) UserMessage("\r\n%s, READ_PAGE:", out); if(memcmp(out, HITAG2_READ_PAGE_INVERTED, 2) == 0) UserMessage("\r\n%s, READ_PAGE_INVERTED:", out); if(memcmp(out, HITAG2_WRITE_PAGE, 2) == 0) UserMessage("\r\n%s, WRITE_PAGE:", out); binstringtobyte(&x, &out[2], 3); UserMessageNum("%d", x); break; // password or data case 32: if(auth) UserMessage("\r\n%s, PWD:", out); else UserMessage("\r\n%s, DATA:", out); binstringtohex(out, out); UserMessage("%s", out); auth= FALSE; break; // crypto handshake case 64: UserMessage("\r\n%s, PRN:", out); memcpy(tmp, out, 32); tmp[32]= '\0'; binstringtohex(out, tmp); UserMessage("%s: SECRET:", out); binstringtohex(out, out + 32); UserMessage("%s", out); encrypted= TRUE; auth= FALSE; break; default: UserMessage("\r\n%s,?INVALID?", out); } decoded= TRUE; j += i; } else break; } UserMessage("%s", "\r\n"); return decoded; }
// 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; }
// decode externally sniffed PWM BOOL hitag1_decode_pwm(unsigned long pulses[], unsigned long gaps[], unsigned int count) { unsigned int i, j; BOOL decoded= FALSE; BYTE out[46], tmp[9], x; // max command from hitag1 is 5 bit command plus 32 bits + 8 bit crc == 45 j= 0; while(j < count) { i= generic_decode_pwm(out, &pulses[j], 10, 256, &gaps[j], 10, 80, count - j); if(i) { // there are only 5 message sizes, so decode accordingly switch(strlen(out)) { // halt case 4: if(memcmp(out, HITAG1_HALT, 4) == 0) UserMessage("\r\n%s, HALT", out); else UserMessage("\r\n%s, ?INVALID?", out); break; // get UID case 5: if(memcmp(out, HITAG1_SET_CC, 5) == 0) UserMessage("\r\n%s, SET_CC", out); else UserMessage("\r\n%s, ?INVALID?", out); break; // read/write case 20: if(memcmp(out, HITAG1_RDPPAGE, 4) == 0) UserMessage("\r\n%s, READ_PLAINTEXT_PAGE:", out); else if(memcmp(out, HITAG1_WRPPAGE, 4) == 0) UserMessage("\r\n%s, WRITE_PLAINTEXT_PAGE:", out); else { UserMessage("\r\n%s, ?INVALID?", out); break; } out[20]= '\0'; binstringtohex(tmp, out + 4); // page UserMessage("%.2s:", tmp); // crc UserMessage("%s", tmp + 2); break; // data case 40: UserMessage("\r\n%s, DATA:", out); out[40]= '\0'; binstringtohex(tmp, out); // DATA UserMessage("%.8s:", tmp); // CRC UserMessage("%s", tmp + 8); break; // select case 45: if(memcmp(out, HITAG1_SELECT, 5) == 0) { UserMessage("\r\n%s, SELECT:", out); out[45]= '\0'; binstringtohex(tmp, out + 5); // UID UserMessage("%.8s:", tmp); // CRC UserMessage("%s", tmp + 8); } else UserMessage("\r\n%s, ?INVALID?", out); break; default: UserMessage("\r\n%s,?INVALID?", out); } decoded= TRUE; j += i; } else break; } UserMessage("%s", "\r\n"); return decoded; }
// decode externally sniffed PWM BOOL q5_decode_pwm(unsigned int pulses[], unsigned int gaps[], unsigned int count) { unsigned int i; BOOL decoded= FALSE, sequence; static BOOL pwd= FALSE; BYTE tmp[18], out[71], *p; // max response from q5 is 70 bits i= 0; while(i < count) { // we can't use generic_decode_pwm as Q5 commands may be all 0 or all 1 for(p= out, sequence= FALSE ; i < count ; ++i) if(gaps[i] >= 100 && gaps[i] <= 512) { // make sure invalid data doesn't overrun buffer if(p - out == sizeof(out) - 2) break; if(pulses[i] <= 512) { if(approx(pulses[i], 128, 20)) *(p++)= '0'; else *(p++)= '1'; sequence= TRUE; } else { ++i; break; } } *p= '\0'; if(sequence) { // there are only 7 message sizes, so decode accordingly switch(strlen(out)) { // GET Trace Data (UID)) or RESET case 2: if(memcmp(out, Q5_GET_TRACE_DATA, 2) == 0) UserMessage("\r\n%s, GET_TRACE_DATA", out); else if(memcmp(out, Q5_SOFT_RESET, 2) == 0) UserMessage("\r\n%s, RESET", out); else UserMessage("\r\n%s, ?INVALID?", out); break; // modulation defeat case 5: if(memcmp(out, Q5_MODULATION_DEFEAT, 5) == 0) UserMessage("\r\n%s, MODULATION_DEFEAT", out); else UserMessage("\r\n%s, ?INVALID?", out); break; // read case 6: if(memcmp(out, Q5_DIRECT_ACCESS, 2) == 0) UserMessage("\r\n%s, DIRECT_ACCESS:", out); else { UserMessage("\r\n%s, ?INVALID?", out); break; } // PWD mode (should be 0)) UserMessage("%0.1s:", out + 2); // ADDRESS binstringtohex(tmp, out + 3); UserMessage("%0.2s", tmp); pwd= FALSE; break; // login (AOR followed by PWD) case 34: if(memcmp(out, Q5_AOR, 2) == 0) UserMessage("\r\n%s, AOR:", out); else { UserMessage("\r\n%s, ?INVALID?", out); break; } // PWD binstringtohex(tmp, out + 2); UserMessage("%0.8s", tmp); pwd= TRUE; break; // PWD read/ STD write // note the only way to tell these apart is by knowing we are in PWD mode case 38: if(pwd && memcmp(out, Q5_DIRECT_ACCESS, 2) == 0) { UserMessage("\r\n%s, PWD_DIRECT_ACCESS:", out); // PWD binstringtohex(tmp, out + 2); UserMessage("%0.8s:", tmp); // Lock BIT UserMessage("%0.1s:", out + 34); // ADDRESS binstringtohex(tmp, out + 35); UserMessage("%0.1s", tmp); break; } if(!pwd && memcmp(out, Q5_WRITE_P0, 2) == 0) UserMessage("\r\n%s, WRITE_P0:", out); else { UserMessage("\r\n%s, ?INVALID?", out); break; } // Lock BIT UserMessage("%0.1s:", out + 2); // DATA binstringtohex(tmp, out + 3); UserMessage("%0.8s:", tmp); // ADDRESS binstringtohex(tmp, out + 35); UserMessage("%0.1s", tmp); break; // PWD write case 70: if(memcmp(out, Q5_WRITE_P0, 2) == 0) UserMessage("\r\n%s, PWD_WRITE_P0:", out); else { UserMessage("\r\n%s, ?INVALID?", out); break; } // PWD binstringtohex(tmp, out + 2); UserMessage("%0.8s:", tmp); // Lock BIT UserMessage("%0.1s:", out + 34); // DATA binstringtohex(tmp, out + 35); UserMessage("%0.8s:", tmp); // ADDRESS binstringtohex(tmp, out + 67); UserMessage("%0.1s", tmp); pwd= TRUE; break; default: UserMessage("\r\n%s, ?INVALID?", out); break; } decoded= TRUE; } } UserMessage("%s", "\r\n"); return decoded; }
// print a binstring value as hex void printbinstringashex(unsigned char *bin) { if(binstringtohex(TmpBits, bin)) UserMessage("%s\r\n", TmpBits); }