#include "util.h"
#include "hitag2.h"
#include "string.h"
+#include "BigBuf.h"
static bool bQuiet;
-bool bCrypto;
-bool bAuthenticating;
-bool bPwd;
-bool bSuccessful;
+static bool bCrypto;
+static bool bAuthenticating;
+static bool bPwd;
+static bool bSuccessful;
+
+
struct hitag2_tag {
uint32_t uid;
},
};
-//#define TRACE_LENGTH 3000
-//uint8_t *trace = (uint8_t *) BigBuf;
-//int traceLen = 0;
-//int rsamples = 0;
-
-#define AUTH_TABLE_OFFSET FREE_BUFFER_OFFSET
-#define AUTH_TABLE_LENGTH FREE_BUFFER_SIZE
-byte_t* auth_table = (byte_t *)BigBuf+AUTH_TABLE_OFFSET;
-size_t auth_table_pos = 0;
-size_t auth_table_len = AUTH_TABLE_LENGTH;
+// ToDo: define a meaningful maximum size for auth_table. The bigger this is, the lower will be the available memory for traces.
+// Historically it used to be FREE_BUFFER_SIZE, which was 2744.
+#define AUTH_TABLE_LENGTH 2744
+static byte_t* auth_table;
+static size_t auth_table_pos = 0;
+static size_t auth_table_len = AUTH_TABLE_LENGTH;
-byte_t password[4];
-byte_t NrAr[8];
-byte_t key[8];
-uint64_t cipher_state;
+static byte_t password[4];
+static byte_t NrAr[8];
+static byte_t key[8];
+static uint64_t cipher_state;
/* Following is a modified version of cryptolib.com/ciphers/hitag2/ */
// Software optimized 48-bit Philips/NXP Mifare Hitag2 PCF7936/46/47/52 stream cipher algorithm by I.C. Wiener 2006-2007.
return c;
}
-size_t nbytes(size_t nbits) {
- return (nbits/8)+((nbits%8)>0);
-}
-
-int hitag2_reset(void)
+static int hitag2_reset(void)
{
tag.state = TAG_STATE_RESET;
tag.crypto_active = 0;
return 0;
}
-int hitag2_init(void)
+static int hitag2_init(void)
{
// memcpy(&tag, &resetdata, sizeof(tag));
hitag2_reset();
LOW(GPIO_SSC_DOUT);
}
-void hitag2_handle_reader_command(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen)
+
+static void hitag2_handle_reader_command(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen)
{
byte_t rx_air[HITAG_FRAME_LEN];
break;
}
-// LogTrace(rx,nbytes(rxlen),0,0,false);
-// LogTrace(tx,nbytes(*txlen),0,0,true);
+// LogTraceHitag(rx,rxlen,0,0,false);
+// LogTraceHitag(tx,*txlen,0,0,true);
if(tag.crypto_active) {
hitag2_cipher_transcrypt(&(tag.cs), tx, *txlen/8, *txlen%8);
// Binary puls length modulation (BPLM) is used to encode the data stream
// This means that a transmission of a one takes longer than that of a zero
- // Enable modulation, which means, drop the the field
+ // Enable modulation, which means, drop the field
HIGH(GPIO_SSC_DOUT);
// Wait for 4-10 times the carrier period
LED_A_OFF();
}
+
static void hitag_reader_send_frame(const byte_t* frame, size_t frame_len)
{
// Send the content of the frame
}
// Send EOF
AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG;
- // Enable modulation, which means, drop the the field
+ // Enable modulation, which means, drop the field
HIGH(GPIO_SSC_DOUT);
// Wait for 4-10 times the carrier period
while(AT91C_BASE_TC0->TC_CV < T0*6);
size_t blocknr;
-bool hitag2_password(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) {
+static bool hitag2_password(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) {
// Reset the transmission frame length
*txlen = 0;
return true;
}
-bool hitag2_crypto(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) {
+static bool hitag2_crypto(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) {
// Reset the transmission frame length
*txlen = 0;
case 0: {
// Stop if there is no answer while we are in crypto mode (after sending NrAr)
if (bCrypto) {
- DbpString("Authentication failed!");
- return false;
- }
- *txlen = 5;
- memcpy(tx,"\xc0",nbytes(*txlen));
+ // Failed during authentication
+ if (bAuthenticating) {
+ DbpString("Authentication failed!");
+ return false;
+ } else {
+ // Failed reading a block, could be (read/write) locked, skip block and re-authenticate
+ if (blocknr == 1) {
+ // Write the low part of the key in memory
+ memcpy(tag.sectors[1],key+2,4);
+ } else if (blocknr == 2) {
+ // Write the high part of the key in memory
+ tag.sectors[2][0] = 0x00;
+ tag.sectors[2][1] = 0x00;
+ tag.sectors[2][2] = key[0];
+ tag.sectors[2][3] = key[1];
+ } else {
+ // Just put zero's in the memory (of the unreadable block)
+ memset(tag.sectors[blocknr],0x00,4);
+ }
+ blocknr++;
+ bCrypto = false;
+ }
+ } else {
+ *txlen = 5;
+ memcpy(tx,"\xc0",nbytes(*txlen));
+ }
} break;
// Received UID, crypto tag answer
}
-bool hitag2_authenticate(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) {
+static bool hitag2_authenticate(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) {
// Reset the transmission frame length
*txlen = 0;
return true;
}
-bool hitag2_test_auth_attempts(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) {
+
+static bool hitag2_test_auth_attempts(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) {
+
// Reset the transmission frame length
*txlen = 0;
case 0: {
// Stop if there is no answer while we are in crypto mode (after sending NrAr)
if (bCrypto) {
- Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed!",NrAr[0],NrAr[1],NrAr[2],NrAr[3],NrAr[4],NrAr[5],NrAr[6],NrAr[7]);
+ Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x Failed, removed entry!",NrAr[0],NrAr[1],NrAr[2],NrAr[3],NrAr[4],NrAr[5],NrAr[6],NrAr[7]);
+
+ // Removing failed entry from authentiations table
+ memcpy(auth_table+auth_table_pos,auth_table+auth_table_pos+8,8);
+ auth_table_len -= 8;
+
+ // Return if we reached the end of the authentications table
bCrypto = false;
- if ((auth_table_pos+8) == auth_table_len) {
+ if (auth_table_pos == auth_table_len) {
return false;
}
- auth_table_pos += 8;
+
+ // Copy the next authentication attempt in row (at the same position, b/c we removed last failed entry)
memcpy(NrAr,auth_table+auth_table_pos,8);
}
*txlen = 5;
return true;
}
+static bool hitag2_read_uid(byte_t* rx, const size_t rxlen, byte_t* tx, size_t* txlen) {
+ // Reset the transmission frame length
+ *txlen = 0;
+
+ // Try to find out which command was send by selecting on length (in bits)
+ switch (rxlen) {
+ // No answer, try to resurrect
+ case 0: {
+ // Just starting or if there is no answer
+ *txlen = 5;
+ memcpy(tx,"\xc0",nbytes(*txlen));
+ } break;
+ // Received UID
+ case 32: {
+ // Check if we received answer tag (at)
+ if (bAuthenticating) {
+ bAuthenticating = false;
+ } else {
+ // Store the received block
+ memcpy(tag.sectors[blocknr],rx,4);
+ blocknr++;
+ }
+ if (blocknr > 0) {
+ //DbpString("Read successful!");
+ bSuccessful = true;
+ return false;
+ }
+ } break;
+ // Unexpected response
+ default: {
+ Dbprintf("Uknown frame length: %d",rxlen);
+ return false;
+ } break;
+ }
+ return true;
+}
+
void SnoopHitag(uint32_t type) {
int frame_count;
int response;
byte_t rx[HITAG_FRAME_LEN];
size_t rxlen=0;
- // Clean up trace and prepare it for storing frames
- iso14a_set_tracing(TRUE);
- iso14a_clear_trace();
+ FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
+ // Clean up trace and prepare it for storing frames
+ set_tracing(TRUE);
+ clear_trace();
+
auth_table_len = 0;
auth_table_pos = 0;
+
+ BigBuf_free();
+ auth_table = (byte_t *)BigBuf_malloc(AUTH_TABLE_LENGTH);
memset(auth_table, 0x00, AUTH_TABLE_LENGTH);
-
+
DbpString("Starting Hitag2 snoop");
LED_D_ON();
// Set up eavesdropping mode, frequency divisor which will drive the FPGA
// and analog mux selection.
- FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT);
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE);
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
RELAY_OFF();
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1);
AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME;
- // Disable timer during configuration
+ // Disable timer during configuration
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
// Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger,
// Check if frame was captured
if(rxlen > 0) {
frame_count++;
- if (!LogTrace(rx,nbytes(rxlen),response,0,reader_frame)) {
+ if (!LogTraceHitag(rx,rxlen,response,0,reader_frame)) {
DbpString("Trace full");
break;
}
bool bQuitTraceFull = false;
bQuiet = false;
+ FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
+
// Clean up trace and prepare it for storing frames
- iso14a_set_tracing(TRUE);
- iso14a_clear_trace();
+ set_tracing(TRUE);
+ clear_trace();
+
auth_table_len = 0;
auth_table_pos = 0;
+ byte_t* auth_table;
+ BigBuf_free();
+ auth_table = (byte_t *)BigBuf_malloc(AUTH_TABLE_LENGTH);
memset(auth_table, 0x00, AUTH_TABLE_LENGTH);
DbpString("Starting Hitag2 simulation");
// Set up simulator mode, frequency divisor which will drive the FPGA
// and analog mux selection.
- FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT);
+ FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_READER_FIELD);
FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); //125Khz
SetAdcMuxFor(GPIO_MUXSEL_LOPKD);
RELAY_OFF();
// Disable timer during configuration
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
- // Capture mode, defaul timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger,
+ // Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger,
// external trigger rising edge, load RA on rising edge of TIOA.
AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK | AT91C_TC_ETRGEDG_RISING | AT91C_TC_ABETRG | AT91C_TC_LDRA_RISING;
- // Enable and reset counter
- AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
-
// Reset the received frame, frame count and timing info
memset(rx,0x00,sizeof(rx));
frame_count = 0;
response = 0;
overflow = 0;
+
+ // Enable and reset counter
+ AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
while(!BUTTON_PRESS()) {
// Watchdog hit
if(rxlen > 4) {
frame_count++;
if (!bQuiet) {
- if (!LogTrace(rx,nbytes(rxlen),response,0,true)) {
+ if (!LogTraceHitag(rx,rxlen,response,0,true)) {
DbpString("Trace full");
if (bQuitTraceFull) {
break;
hitag_send_frame(tx,txlen);
// Store the frame in the trace
if (!bQuiet) {
- if (!LogTrace(tx,nbytes(txlen),0,0,false)) {
+ if (!LogTraceHitag(tx,txlen,0,0,false)) {
DbpString("Trace full");
if (bQuitTraceFull) {
break;
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
-// Dbprintf("frame received: %d",frame_count);
-// Dbprintf("Authentication Attempts: %d",(auth_table_len/8));
-// DbpString("All done");
+
+ DbpString("Sim Stopped");
+
}
void ReaderHitag(hitag_function htf, hitag_data* htd) {
bool bStop;
bool bQuitTraceFull = false;
- // Reset the return status
- bSuccessful = false;
+ FpgaDownloadAndGo(FPGA_BITSTREAM_LF);
+ // Reset the return status
+ bSuccessful = false;
// Clean up trace and prepare it for storing frames
- iso14a_set_tracing(TRUE);
- iso14a_clear_trace();
- DbpString("Starting Hitag reader family");
+ set_tracing(TRUE);
+ clear_trace();
+
+ //DbpString("Starting Hitag reader family");
// Check configuration
switch(htf) {
case RHT2F_PASSWORD: {
- Dbprintf("List identifier in password mode");
+ Dbprintf("List identifier in password mode");
memcpy(password,htd->pwd.password,4);
- blocknr = 0;
+ blocknr = 0;
bQuitTraceFull = false;
bQuiet = false;
bPwd = false;
} break;
-
case RHT2F_AUTHENTICATE: {
DbpString("Authenticating using nr,ar pair:");
memcpy(NrAr,htd->auth.NrAr,8);
Dbhexdump(8,NrAr,false);
bQuiet = false;
bCrypto = false;
- bAuthenticating = false;
+ bAuthenticating = false;
bQuitTraceFull = true;
} break;
-
case RHT2F_CRYPTO: {
DbpString("Authenticating using key:");
- memcpy(key,htd->crypto.key,6);
+ memcpy(key,htd->crypto.key,6); //HACK; 4 or 6?? I read both in the code.
Dbhexdump(6,key,false);
- blocknr = 0;
+ blocknr = 0;
bQuiet = false;
bCrypto = false;
- bAuthenticating = false;
+ bAuthenticating = false;
bQuitTraceFull = true;
} break;
-
case RHT2F_TEST_AUTH_ATTEMPTS: {
Dbprintf("Testing %d authentication attempts",(auth_table_len/8));
auth_table_pos = 0;
- memcpy(NrAr,auth_table,8);
+ memcpy(NrAr, auth_table, 8);
bQuitTraceFull = false;
bQuiet = false;
bCrypto = false;
} break;
-
+ case RHT2F_UID_ONLY: {
+ blocknr = 0;
+ bQuiet = false;
+ bCrypto = false;
+ bAuthenticating = false;
+ bQuitTraceFull = true;
+ } break;
default: {
Dbprintf("Error, unknown function: %d",htf);
return;
// hitagS settings
reset_sof = 1;
t_wait = 200;
- DbpString("Configured for hitagS reader");
+ //DbpString("Configured for hitagS reader");
} else if (htf < 20) {
// hitag1 settings
reset_sof = 1;
t_wait = 200;
- DbpString("Configured for hitag1 reader");
+ //DbpString("Configured for hitag1 reader");
} else if (htf < 30) {
// hitag2 settings
reset_sof = 4;
t_wait = HITAG_T_WAIT_2;
- DbpString("Configured for hitag2 reader");
+ //DbpString("Configured for hitag2 reader");
} else {
Dbprintf("Error, unknown hitag reader type: %d",htf);
return;
}
-
+ uint8_t attempt_count=0;
while(!bStop && !BUTTON_PRESS()) {
// Watchdog hit
WDT_HIT();
if(rxlen > 0) {
frame_count++;
if (!bQuiet) {
- if (!LogTrace(rx,nbytes(rxlen),response,0,false)) {
+ if (!LogTraceHitag(rx,rxlen,response,0,false)) {
DbpString("Trace full");
if (bQuitTraceFull) {
break;
case RHT2F_TEST_AUTH_ATTEMPTS: {
bStop = !hitag2_test_auth_attempts(rx,rxlen,tx,&txlen);
} break;
+ case RHT2F_UID_ONLY: {
+ bStop = !hitag2_read_uid(rx, rxlen, tx, &txlen);
+ attempt_count++; //attempt 3 times to get uid then quit
+ if (!bStop && attempt_count == 3) bStop = true;
+ } break;
default: {
Dbprintf("Error, unknown function: %d",htf);
return;
// we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'.
// All timer values are in terms of T0 units
while(AT91C_BASE_TC0->TC_CV < T0*(t_wait+(HITAG_T_TAG_HALF_PERIOD*lastbit)));
+
+ //Dbprintf("DEBUG: Sending reader frame");
// Transmit the reader frame
hitag_reader_send_frame(tx,txlen);
frame_count++;
if (!bQuiet) {
// Store the frame in the trace
- if (!LogTrace(tx,nbytes(txlen),HITAG_T_WAIT_2,0,true)) {
+ if (!LogTraceHitag(tx,txlen,HITAG_T_WAIT_2,0,true)) {
if (bQuitTraceFull) {
break;
} else {
bSkip = true;
tag_sof = reset_sof;
response = 0;
-
+ //Dbprintf("DEBUG: Waiting to receive frame");
+ uint32_t errorCount = 0;
+
// Receive frame, watch for at most T0*EOF periods
while (AT91C_BASE_TC1->TC_CV < T0*HITAG_T_WAIT_MAX) {
// Check if falling edge in tag modulation is detected
// Capture tag frame (manchester decoding using only falling edges)
if(ra >= HITAG_T_EOF) {
if (rxlen != 0) {
- //DbpString("wierd1?");
+ //Dbprintf("DEBUG: Wierd1");
}
// Capture the T0 periods that have passed since last communication or field drop (reset)
// We always recieve a 'one' first, which has the falling edge after a half period |-_|
response = ra-HITAG_T_TAG_HALF_PERIOD;
} else if(ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) {
// Manchester coding example |-_|_-|-_| (101)
+
+ //need to test to verify we don't exceed memory...
+ //if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) {
+ // break;
+ //}
rx[rxlen / 8] |= 0 << (7-(rxlen%8));
rxlen++;
rx[rxlen / 8] |= 1 << (7-(rxlen%8));
rxlen++;
} else if(ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) {
// Manchester coding example |_-|...|_-|-_| (0...01)
+
+ //need to test to verify we don't exceed memory...
+ //if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) {
+ // break;
+ //}
rx[rxlen / 8] |= 0 << (7-(rxlen%8));
rxlen++;
// We have to skip this half period at start and add the 'one' the second time
bSkip = !bSkip;
} else if(ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) {
// Manchester coding example |_-|_-| (00) or |-_|-_| (11)
+
+ //need to test to verify we don't exceed memory...
+ //if ( ((rxlen+2) / 8) > HITAG_FRAME_LEN) {
+ // break;
+ //}
if (tag_sof) {
// Ignore bits that are transmitted during SOF
tag_sof--;
rxlen++;
}
} else {
+ //Dbprintf("DEBUG: Wierd2");
+ errorCount++;
// Ignore wierd value, is to small to mean anything
}
}
-
+ //if we saw over 100 wierd values break it probably isn't hitag...
+ if (errorCount >100) break;
// We can break this loop if we received the last bit from a frame
if (AT91C_BASE_TC1->TC_CV > T0*HITAG_T_EOF) {
if (rxlen>0) break;
}
}
}
+ //Dbprintf("DEBUG: Done waiting for frame");
+
LED_B_OFF();
LED_D_OFF();
AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS;
AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS;
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
- Dbprintf("frame received: %d",frame_count);
- DbpString("All done");
+ //Dbprintf("frame received: %d",frame_count);
+ //DbpString("All done");
cmd_send(CMD_ACK,bSuccessful,0,0,(byte_t*)tag.sectors,48);
}