Merge pull request #2461 from douniwan5788/hitags_timing

Fix Hitags timing
This commit is contained in:
Iceman 2024-08-20 18:55:37 +02:00 committed by GitHub
commit a7b5961688
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 102 additions and 72 deletions

View file

@ -33,9 +33,8 @@
// TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz
// EM4x50 units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier)
// T0 = TIMER_CLOCK1 / 125000 = 192
#ifndef T0
#define T0 192
#endif
// conversions (carrier frequency 125 kHz):
// 1 us = 1.5 ticks

View file

@ -79,21 +79,26 @@ static uint32_t rnd = 0x74124485; // random number
// TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz
// Hitag units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier)
// T0 = TIMER_CLOCK1 / 125000 = 192
#ifndef T0
#define T0 192
#endif
#define HITAG_FRAME_LEN 20
// TC0 and TC1 will overflow at 341 * T0, so avoid setting these timings above 341 when comparing without considering overflow,
// as they will never reach that value.
#define HITAG_T_STOP 36 /* T_EOF should be > 36 */
#define HITAG_T_LOW 8 /* T_LOW should be 4..10 */
#define HITAG_T_0_MIN 15 /* T[0] should be 18..22 */
#define HITAG_T_1_MIN 25 /* T[1] should be 26..30 */
//#define HITAG_T_EOF 40 /* T_EOF should be > 36 */
#define HITAG_T_0 20 /* T[0] should be 18..22 */
#define HITAG_T_1 28 /* T[1] should be 26..30 */
// #define HITAG_T_EOF 40 /* T_EOF should be > 36 */
#define HITAG_T_EOF 80 /* T_EOF should be > 36 */
#define HITAG_T_WAIT_1 200 /* T_wresp should be 199..206 */
#define HITAG_T_WAIT_2 90 /* T_wresp should be 199..206 */
#define HITAG_T_WAIT_MAX 300 /* bit more than HITAG_T_WAIT_1 + HITAG_T_WAIT_2 */
#define HITAG_T_PROG_MAX 750
#define HITAG_T_WAIT_RESP 200 /* T_wresp should be 204..212 */
#define HITAG_T_WAIT_SC 90 /* T_wsc should be 90..5000 */
#define HITAG_T_WAIT_FIRST 300 /* T_wfc should be 280..565 (T_ttf) */
#define HITAG_T_PROG_MAX 750 /* T_prog should be 716..726 */
#define HITAG_T_TAG_ONE_HALF_PERIOD 10
#define HITAG_T_TAG_TWO_HALF_PERIOD 25
@ -293,16 +298,16 @@ static void hitag_reader_send_bit(int bit, bool ledcontrol) {
}
#else
// Wait for 4-10 times the carrier period
while (AT91C_BASE_TC0->TC_CV < T0 * 6) {};
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
LOW(GPIO_SSC_DOUT);
if (bit == 0) {
// Zero bit: |_-|
while (AT91C_BASE_TC0->TC_CV < T0 * 22) {};
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_0) {};
} else {
// One bit: |_--|
while (AT91C_BASE_TC0->TC_CV < T0 * 28) {};
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_1) {};
}
#endif
@ -323,7 +328,7 @@ static void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool
HIGH(GPIO_SSC_DOUT);
// Wait for 4-10 times the carrier period
while (AT91C_BASE_TC0->TC_CV < T0 * 6) {};
while (AT91C_BASE_TC0->TC_CV < T0 * HITAG_T_LOW) {};
LOW(GPIO_SSC_DOUT);
}
@ -356,20 +361,24 @@ static void hitagS_init_clock(void) {
AT91C_BASE_TC0->TC_RC = 0; // set TIOA (carry bit) on overflow, return to zero
AT91C_BASE_TC0->TC_RA = 1; // clear carry bit on next clock cycle
AT91C_BASE_TC1->TC_RA = 1; // clear carry bit on next clock cycle
// Enable and reset counters
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// Typically 2 or 3, indicating that our execution is slow enough to wait for TC0 reset.
// If I am calculating correctly, theoretically, for AT91C_TC_CLKS_TIMER_DIV1_CLOCK, 2 instruction statements are sufficient?
// Dbprintf("TC_CV:%i", AT91C_BASE_TC0->TC_CV);
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
// uint16_t cv0 = AT91C_BASE_TC0->TC_CV;
// synchronized startup procedure
while (AT91C_BASE_TC0->TC_CV > 0); // wait until TC0 returned to zero
// while (AT91C_BASE_TC0->TC_CV < 2); // and has started (TC_CV > TC_RA, now TC1 is cleared)
// return to zero
AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG;
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
while (AT91C_BASE_TC0->TC_CV > 0);
// Waiting for TC0 to return to 0 takes a considerable amount of time (around 2730us),
// and we should be able to tolerate 1 * T0. Or is this even necessary?
while (AT91C_BASE_TC0->TC_CV > T0) {}; // wait until TC0 returned to zero
// while (AT91C_BASE_TC0->TC_CV < 2) {}; // and has started (TC_CV > TC_RA, now TC1 is cleared)
// Dbprintf("TC_CV0:%i TC_CV:%i", cv0, AT91C_BASE_TC0->TC_CV);
}
static void hitagS_stop_clock(void) {
@ -945,12 +954,12 @@ void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontr
// Process the incoming frame (rx) and prepare the outgoing frame (tx)
hitagS_handle_reader_command(rx, rxlen, tx, &txlen);
// Wait for HITAG_T_WAIT_1 carrier periods after the last reader bit,
// Wait for HITAG_T_WAIT_RESP carrier periods after the last reader bit,
// not that since the clock counts since the rising edge, but T_Wait1 is
// with respect to the falling edge, we need to wait actually (T_Wait1 - T_Low)
// periods. The gap time T_Low varies (4..10). All timer values are in
// terms of T0 units
while (AT91C_BASE_TC0->TC_CV < T0 * (HITAG_T_WAIT_1 - HITAG_T_LOW)) {};
while (AT91C_BASE_TC0->TC_CV < T0 * (HITAG_T_WAIT_RESP - HITAG_T_LOW)) {};
// Send and store the tag answer (if there is any)
if (txlen > 0) {
@ -1002,7 +1011,12 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui
uint32_t prevcv = 0;
bool bStarted = false;
// Receive frame, watch for at most T0*EOF periods
uint32_t ra_i=0, h2 = 0, h3 = 0, h4 = 0;
uint8_t edges[160]={0};
// Dbprintf("TC0_CV:%i TC1_CV:%i TC1_RA:%i", AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV ,AT91C_BASE_TC1->TC_RA);
// Receive frame, watch for at most T0*HITAG_T_PROG_MAX periods
while (AT91C_BASE_TC0->TC_CV + (overcount << 16) < (T0 * HITAG_T_PROG_MAX)) {
// detect and track counter overflows
@ -1016,8 +1030,8 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui
if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) {
// Retrieve the new timing values
uint32_t ra = (AT91C_BASE_TC1->TC_RA + (overcount << 16)) / T0;
uint32_t ra = AT91C_BASE_TC1->TC_RA / T0;
edges[ra_i++] = ra;
// Reset timer every frame, we have to capture the last edge for timing
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
@ -1030,11 +1044,14 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui
if (bStarted == false) {
if (ra >= HITAG_T_EOF) {
if (ra >= HITAG_T_WAIT_RESP) {
bStarted = true;
// Capture the T0 periods that have passed since last communication or field drop (reset)
// We always receive a 'one' first, which has the falling edge after a half period |-_|
*resptime = ra - HITAG_T_TAG_HALF_PERIOD;
rx[0] = 0x80;
(*rxlen)++;
} else {
errorCount++;
}
@ -1047,7 +1064,7 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui
rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8));
(*rxlen)++;
h4++;
} else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) {
// Manchester coding example |_-|...|_-|-_| (0...01)
@ -1062,13 +1079,13 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui
lastbit = !lastbit;
bSkip = !bSkip;
h3++;
} else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) {
// Manchester coding example |_-|_-| (00) or |-_|-_| (11)
// bit is same as last bit
rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8));
(*rxlen)++;
h2++;
} else {
// Ignore weird value, is to small to mean anything
errorCount++;
@ -1081,24 +1098,30 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui
}
// We can break this loop if we received the last bit from a frame
if (AT91C_BASE_TC1->TC_CV > (T0 * HITAG_T_EOF)) {
// max periods between 2 falling edge
// RTF AC64 |--__|--__| (00) 64 * T0
// RTF MC32 |_-|-_|_-| (010) 48 * T0
if (AT91C_BASE_TC1->TC_CV > (T0 * 80)) {
if ((*rxlen)) {
break;
}
}
}
// Dbprintf("RX0 %i:%02X.. err:%i resptime:%i", *rxlen, rx[0], errorCount, *resptime);
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("RX0 %i:%02X.. err:%i resptime:%i h2:%i h3:%i h4:%i edges", *rxlen, rx[0], errorCount, *resptime, h2, h3, h4);
Dbhexdump(ra_i, edges, false);
}
}
static void sendReceiveHitagS(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *prxbits, int t_wait, bool ledcontrol, bool ac_seq) {
LogTraceBits(tx, txlen, HITAG_T_WAIT_2, HITAG_T_WAIT_2, true);
LogTraceBits(tx, txlen, HITAG_T_WAIT_SC, HITAG_T_WAIT_SC, true);
// Send and store the reader command
// Disable timer 1 with external trigger to avoid triggers during our own modulation
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
// Wait for HITAG_T_WAIT_2 carrier periods after the last tag bit before transmitting,
// Wait for HITAG_T_WAIT_SC carrier periods after the last tag bit before transmitting,
// Since the clock counts since the last falling edge, a 'one' means that the
// falling edge occurred halfway the period. with respect to this falling edge,
// we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'.
@ -1125,19 +1148,20 @@ static void sendReceiveHitagS(const uint8_t *tx, size_t txlen, uint8_t *rx, size
response_bit[i] = (rx[i / 8] >> (7 - (i % 8))) & 1;
}
Dbprintf("htS: rxlen...... %zu", rxlen);
Dbprintf("htS: sizeofrx... %zu", sizeofrx);
if (g_dbglevel >= DBG_EXTENDED) {
Dbprintf("htS: rxlen...... %zu", rxlen);
Dbprintf("htS: sizeofrx... %zu", sizeofrx);
DbpString("htS: response_bit:");
Dbhexdump(rxlen, response_bit, false);
}
memset(rx, 0x00, sizeofrx);
if (ac_seq) {
DbpString("htS: AntiCollision Sequence ( ac seq )");
Dbhexdump(rxlen, response_bit, false);
// Tag Response is AC encoded
// We used UID Request Advanced, meaning AC SEQ header is 111.
for (int i = 6; i < rxlen; i += 2) {
for (int i = 7; i < rxlen; i += 2) {
rx[k / 8] |= response_bit[i] << (7 - (k % 8));
@ -1148,16 +1172,26 @@ static void sendReceiveHitagS(const uint8_t *tx, size_t txlen, uint8_t *rx, size
}
}
DbpString("htS: ac sequence compress");
Dbhexdump(k / 8, rx, false);
// TODO: It's very confusing to reinterpreter the MC to AC; we should implement a more straightforward approach.
// add the lost bit zero, when AC64 last bit is zero
if (k % 8 == 7) {
k++;
}
if (g_dbglevel >= DBG_EXTENDED) {
DbpString("htS: ac sequence compress");
Dbhexdump(k / 8, rx, false);
}
} else {
DbpString("htS: skipping 5 bit header");
if (g_dbglevel >= DBG_EXTENDED) {
DbpString("htS: skipping 6 bit header");
}
// ignore first 5 bits: SOF (actually 1 or 6 depending on response protocol)
// ignore first 6 bits: SOF (actually 1 or 6 depending on response protocol)
// or rather a header.
for (size_t i = 5; i < rxlen; i++) {
for (size_t i = 6; i < rxlen; i++) {
rx[k / 8] |= response_bit[i] << (7 - (k % 8));
k++;
@ -1166,8 +1200,6 @@ static void sendReceiveHitagS(const uint8_t *tx, size_t txlen, uint8_t *rx, size
break;
}
}
}
LogTraceBits(rx, k, resptime, resptime, false);
}
@ -1201,6 +1233,8 @@ static int selectHitagS(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeo
if (ledcontrol) LED_D_ON();
hitagS_init_clock();
// Set fpga in edge detect with reader field, we can modulate as reader now
FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD);
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); //125kHz
@ -1213,7 +1247,7 @@ static int selectHitagS(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeo
// Disable modulation at default, which means enable the field
LOW(GPIO_SSC_DOUT);
hitagS_init_clock();
// Dbprintf("TC0_CV:%i TC1_CV:%i TC1_RA:%i", AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV, AT91C_BASE_TC1->TC_RA);
// UID request standard 00110
// UID request Advanced 1100x
@ -1243,7 +1277,7 @@ static int selectHitagS(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeo
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
txlen = concatbits(tx, txlen, &crc, 0, 8);
sendReceiveHitagS(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, false);
sendReceiveHitagS(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
if (rxlen != 40) {
Dbprintf("Select UID failed! %i", rxlen);
@ -1348,7 +1382,7 @@ static int selectHitagS(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeo
return -1;
}
sendReceiveHitagS(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, false);
sendReceiveHitagS(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
if (rxlen != 40) {
Dbprintf("Authenticate failed! " _RED_("%i"), rxlen);
@ -1399,9 +1433,7 @@ void ReadHitagS(const lf_hitag_data_t *payload, bool ledcontrol) {
uint8_t tx[HITAG_FRAME_LEN];
int t_wait = HITAG_T_WAIT_MAX;
if (selectHitagS(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol) == -1) {
if (selectHitagS(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol) == -1) {
hitagS_stop_clock();
set_tracing(false);
@ -1425,7 +1457,7 @@ void ReadHitagS(const lf_hitag_data_t *payload, bool ledcontrol) {
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
txlen = concatbits(tx, txlen, &crc, 0, 8);
sendReceiveHitagS(tx, txlen, rx, ARRAYLEN(rx), &rxlen, t_wait, ledcontrol, false);
sendReceiveHitagS(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
if (rxlen == 0) {
Dbprintf("Read page failed!");
@ -1437,17 +1469,19 @@ void ReadHitagS(const lf_hitag_data_t *payload, bool ledcontrol) {
tag.pages[pageNum][i] = rx[i];
}
if (tag.auth && tag.LKP && pageNum == 1) {
Dbprintf("Page[%2d]: %02X %02X %02X %02X", pageNum, pwdh0,
(tag.pages[pageNum][2]) & 0xff,
(tag.pages[pageNum][1]) & 0xff,
tag.pages[pageNum][0] & 0xff);
} else {
Dbprintf("Page[%2d]: %02X %02X %02X %02X", pageNum,
(tag.pages[pageNum][3]) & 0xff,
(tag.pages[pageNum][2]) & 0xff,
(tag.pages[pageNum][1]) & 0xff,
tag.pages[pageNum][0] & 0xff);
if (g_dbglevel >= DBG_EXTENDED) {
if (tag.auth && tag.LKP && pageNum == 1) {
Dbprintf("Page[%2d]: %02X %02X %02X %02X", pageNum, pwdh0,
(tag.pages[pageNum][2]) & 0xff,
(tag.pages[pageNum][1]) & 0xff,
tag.pages[pageNum][0] & 0xff);
} else {
Dbprintf("Page[%2d]: %02X %02X %02X %02X", pageNum,
(tag.pages[pageNum][3]) & 0xff,
(tag.pages[pageNum][2]) & 0xff,
(tag.pages[pageNum][1]) & 0xff,
tag.pages[pageNum][0] & 0xff);
}
}
pageNum++;
@ -1504,11 +1538,9 @@ void WritePageHitagS(const lf_hitag_data_t *payload, bool ledcontrol) {
uint8_t tx[HITAG_FRAME_LEN];
size_t txlen = 0;
int t_wait = HITAG_T_WAIT_MAX;
int res = PM3_ESOFT;
if (selectHitagS(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol) == -1) {
if (selectHitagS(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol) == -1) {
res = PM3_ERFTRANS;
goto write_end;
}
@ -1532,7 +1564,7 @@ void WritePageHitagS(const lf_hitag_data_t *payload, bool ledcontrol) {
uint8_t crc = CRC8Hitag1Bits(tx, txlen);
txlen = concatbits(tx, txlen, &crc, 0, 8);
sendReceiveHitagS(tx, txlen, rx, ARRAYLEN(rx), &rxlen, t_wait, ledcontrol, false);
sendReceiveHitagS(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
if ((rxlen != 2) || (rx[0] >> (8 - 2) != 0x1)) {
Dbprintf("no write access on page " _YELLOW_("%d"), payload->page);
@ -1566,7 +1598,7 @@ void WritePageHitagS(const lf_hitag_data_t *payload, bool ledcontrol) {
crc = CRC8Hitag1Bits(tx, txlen);
txlen = concatbits(tx, txlen, &crc, 0, 8);
sendReceiveHitagS(tx, txlen, rx, ARRAYLEN(rx), &rxlen, t_wait, ledcontrol, false);
sendReceiveHitagS(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false);
if ((rxlen != 2) || (rx[0] >> (8 - 2) != 0x1)) {
res = PM3_ESOFT; // write failed
@ -1600,7 +1632,6 @@ void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontr
uint8_t rx[HITAG_FRAME_LEN];
uint8_t tx[HITAG_FRAME_LEN];
int t_wait = HITAG_T_WAIT_MAX;
while ((BUTTON_PRESS() == false) && (data_available() == false)) {
// Watchdog hit
@ -1612,7 +1643,7 @@ void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontr
memcpy(payload.NrAr, data + dataoffset, 8);
int res = selectHitagS(&payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol);
int res = selectHitagS(&payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol);
Dbprintf("Challenge %s: %02X %02X %02X %02X %02X %02X %02X %02X",
res == -1 ? "failed " : "success",
payload.NrAr[0], payload.NrAr[1],
@ -1625,7 +1656,7 @@ void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontr
// Need to do a dummy UID select that will fail
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
SpinDelay(2);
selectHitagS(&payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol);
selectHitagS(&payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), HITAG_T_WAIT_FIRST, ledcontrol);
}
dataoffset += 8;