From 0ab9002f36f8297c640cf9920c06d106ccae2d70 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 28 Aug 2019 11:57:53 +0200 Subject: [PATCH 1/8] fix hf iclass sim * sim 2: add responses to read(1) (Config) and read(5) (AIA) * sim 2/3: don't restrict CC to 00 bytes only * sim 3: add responding to read block commands * sim 2/3: add responding to READ_CHECK_KC * fix sizes of pre-encoded tag answers * change default card challenge * remove commented code * use #defines instead of numerical constants for simulation modes * some reformatting --- armsrc/iclass.c | 320 ++++++++++++++++++----------------- client/cmdhficlass.c | 67 +++----- client/loclass/cipher.c | 1 + client/loclass/elite_crack.c | 49 +++--- include/usb_cmd.h | 14 +- 5 files changed, 227 insertions(+), 224 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 5d7375a7..23d407f7 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -391,8 +391,7 @@ static RAMFUNC int ManchesterDecoding(int v) { Demod.shiftReg = 0; Demod.samples = 0; if (Demod.posCount) { - //if (trigger) LED_A_OFF(); // Not useful in this case... - switch(Demod.syncBit) { + switch (Demod.syncBit) { case 0x08: Demod.samples = 3; break; case 0x04: Demod.samples = 2; break; case 0x02: Demod.samples = 1; break; @@ -414,12 +413,13 @@ static RAMFUNC int ManchesterDecoding(int v) { } } else { + // state is DEMOD is in SYNC from here on. modulation = bit & Demod.syncBit; modulation |= ((bit << 1) ^ ((Demod.buffer & 0x08) >> 3)) & Demod.syncBit; Demod.samples += 4; - if (Demod.posCount==0) { + if (Demod.posCount == 0) { Demod.posCount = 1; if (modulation) { Demod.sub = SUB_FIRST_HALF; @@ -428,14 +428,6 @@ static RAMFUNC int ManchesterDecoding(int v) { } } else { Demod.posCount = 0; - /*(modulation && (Demod.sub == SUB_FIRST_HALF)) { - if (Demod.state!=DEMOD_ERROR_WAIT) { - Demod.state = DEMOD_ERROR_WAIT; - Demod.output[Demod.len] = 0xaa; - error = 0x01; - } - }*/ - //else if (modulation) { if (modulation) { if (Demod.sub == SUB_FIRST_HALF) { Demod.sub = SUB_BOTH; @@ -447,23 +439,16 @@ static RAMFUNC int ManchesterDecoding(int v) { Demod.output[Demod.len] = 0x0f; Demod.len++; Demod.state = DEMOD_UNSYNCD; -// error = 0x0f; return true; } else { Demod.state = DEMOD_ERROR_WAIT; error = 0x33; } - /*if (Demod.state!=DEMOD_ERROR_WAIT) { - Demod.state = DEMOD_ERROR_WAIT; - Demod.output[Demod.len] = 0xaa; - error = 0x01; - }*/ } switch(Demod.state) { case DEMOD_START_OF_COMMUNICATION: if (Demod.sub == SUB_BOTH) { - //Demod.state = DEMOD_MANCHESTER_D; Demod.state = DEMOD_START_OF_COMMUNICATION2; Demod.posCount = 1; Demod.sub = SUB_NONE; @@ -484,10 +469,7 @@ static RAMFUNC int ManchesterDecoding(int v) { break; case DEMOD_START_OF_COMMUNICATION3: if (Demod.sub == SUB_SECOND_HALF) { -// Demod.state = DEMOD_MANCHESTER_D; Demod.state = DEMOD_SOF_COMPLETE; - //Demod.output[Demod.len] = Demod.syncBit & 0xFF; - //Demod.len++; } else { Demod.output[Demod.len] = 0xab; Demod.state = DEMOD_ERROR_WAIT; @@ -543,16 +525,6 @@ static RAMFUNC int ManchesterDecoding(int v) { break; } - /*if (Demod.bitCount>=9) { - Demod.output[Demod.len] = Demod.shiftReg & 0xff; - Demod.len++; - - Demod.parityBits <<= 1; - Demod.parityBits ^= ((Demod.shiftReg >> 8) & 0x01); - - Demod.bitCount = 0; - Demod.shiftReg = 0; - }*/ if (Demod.bitCount >= 8) { Demod.shiftReg >>= 1; Demod.output[Demod.len] = (Demod.shiftReg & 0xff); @@ -787,8 +759,7 @@ void rotateCSN(uint8_t* originalCSN, uint8_t* rotatedCSN) { // Stop when button is pressed // Or return true when command is captured //----------------------------------------------------------------------------- -static int GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) -{ +static int GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) { // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen // only, since we are receiving, not transmitting). // Signal field is off with the appropriate LED @@ -825,7 +796,7 @@ static uint8_t encode4Bits(const uint8_t b) { // The columns are // 1 - Bit value to send // 2 - Reversed (big-endian) - // 3 - Encoded + // 3 - Manchester Encoded // 4 - Hex values switch(c){ @@ -935,20 +906,19 @@ static int SendIClassAnswer(uint8_t *resp, int respLen, int delay) { AT91C_BASE_SSC->SSC_THR = 0x00; FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); - while (!BUTTON_PRESS()) { + while (true) { if ((AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)){ - b = AT91C_BASE_SSC->SSC_RHR; (void) b; + b = AT91C_BASE_SSC->SSC_RHR; + (void) b; } if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)){ b = 0x00; if (d < delay) { + // send 0x00 byte (causing a 2048/13,56MHz = 151us delay) d++; - } - else { + } else { if (i < respLen) { b = resp[i]; - //Hack - //b = 0xAC; } i++; } @@ -957,15 +927,13 @@ static int SendIClassAnswer(uint8_t *resp, int respLen, int delay) { // if (i > respLen +4) break; if (i > respLen + 1) break; + // send 2 more 0x00 bytes (causing a 302us delay) } return 0; } -#define MODE_SIM_CSN 0 -#define MODE_EXIT_AFTER_MAC 1 -#define MODE_FULLSIM 2 /** * @brief Does the actual simulation @@ -973,14 +941,16 @@ static int SendIClassAnswer(uint8_t *resp, int respLen, int delay) { * @param breakAfterMacReceived if true, returns after reader MAC has been received. */ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { + // free eventually allocated BigBuf memory BigBuf_free_keep_EM(); State cipher_state; -// State cipher_state_reserve; - uint8_t *csn = BigBuf_get_EM_addr(); - uint8_t *emulator = csn; - uint8_t sof_data[] = { 0x0F} ; + + uint8_t *emulator = BigBuf_get_EM_addr(); + uint8_t *csn = emulator; + uint8_t sof_data[] = { 0x0F } ; + // CSN followed by two CRC bytes uint8_t anticoll_data[10] = { 0 }; uint8_t csn_data[10] = { 0 }; @@ -991,21 +961,26 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { rotateCSN(csn_data, anticoll_data); // Compute CRC on both CSNs - ComputeCrc14443(CRC_ICLASS, anticoll_data, 8, &anticoll_data[8], &anticoll_data[9]); - ComputeCrc14443(CRC_ICLASS, csn_data, 8, &csn_data[8], &csn_data[9]); + AppendCrc(anticoll_data, 8); + AppendCrc(csn_data, 8); uint8_t diversified_key[8] = { 0 }; // e-Purse - uint8_t card_challenge_data[8] = { 0x00 }; - if (simulationMode == MODE_FULLSIM) { - //The diversified key should be stored on block 3 - //Get the diversified key from emulator memory - memcpy(diversified_key, emulator + (8*3), 8); - //Card challenge, a.k.a e-purse is on block 2 + uint8_t card_challenge_data[8] = { 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + //uint8_t card_challenge_data[8] = { 0 }; + if (simulationMode == ICLASS_SIM_MODE_FULL) { + // The diversified key should be stored on block 3 + // Get the diversified key from emulator memory + memcpy(diversified_key, emulator + (8 * 3), 8); + // Card challenge, a.k.a e-purse is on block 2 memcpy(card_challenge_data, emulator + (8 * 2), 8); - //Precalculate the cipher state, feeding it the CC + // Precalculate the cipher state, feeding it the CC cipher_state = opt_doTagMAC_1(card_challenge_data, diversified_key); } + // save card challenge for sim2,4 attack + if (reader_mac_buf != NULL) { + memcpy(reader_mac_buf, card_challenge_data, 8); + } int exitLoop = 0; // Reader 0a @@ -1026,19 +1001,31 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // Anticollision CSN (rotated CSN) // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) - uint8_t *resp_anticoll = BigBuf_malloc(28); + uint8_t *resp_anticoll = BigBuf_malloc(22); int resp_anticoll_len; - // CSN + // CSN (block 0) // 22: Takes 2 bytes for SOF/EOF and 10 * 2 = 20 bytes (2 bytes/byte) - uint8_t *resp_csn = BigBuf_malloc(30); + uint8_t *resp_csn = BigBuf_malloc(22); int resp_csn_len; - // e-Purse + // configuration (block 1) picopass 2ks + uint8_t *resp_conf = BigBuf_malloc(22); + int resp_conf_len; + uint8_t conf_data[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00}; + AppendCrc(conf_data, 8); + + // e-Purse (block 2) // 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit) - uint8_t *resp_cc = BigBuf_malloc(20); + uint8_t *resp_cc = BigBuf_malloc(18); int resp_cc_len; + // Application Issuer Area (block 5) + uint8_t *resp_aia = BigBuf_malloc(22); + int resp_aia_len; + uint8_t aia_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; + AppendCrc(aia_data, 8); + uint8_t *receivedCmd = BigBuf_malloc(MAX_FRAME_SIZE); int len; @@ -1055,25 +1042,30 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { memcpy(resp_anticoll, ToSend, ToSendMax); resp_anticoll_len = ToSendMax; - // CSN + // CSN (block 0) CodeIClassTagAnswer(csn_data, sizeof(csn_data)); memcpy(resp_csn, ToSend, ToSendMax); resp_csn_len = ToSendMax; - // e-Purse + // Configuration (block 1) + CodeIClassTagAnswer(conf_data, sizeof(conf_data)); + memcpy(resp_conf, ToSend, ToSendMax); + resp_conf_len = ToSendMax; + + // e-Purse (block 2) CodeIClassTagAnswer(card_challenge_data, sizeof(card_challenge_data)); - memcpy(resp_cc, ToSend, ToSendMax); resp_cc_len = ToSendMax; + memcpy(resp_cc, ToSend, ToSendMax); + resp_cc_len = ToSendMax; + + // Application Issuer Area (block 5) + CodeIClassTagAnswer(aia_data, sizeof(aia_data)); + memcpy(resp_aia, ToSend, ToSendMax); + resp_aia_len = ToSendMax; //This is used for responding to READ-block commands or other data which is dynamically generated - //First the 'trace'-data, not encoded for FPGA - uint8_t *data_generic_trace = BigBuf_malloc(8 + 2);//8 bytes data + 2byte CRC is max tag answer - //Then storage for the modulated data - //Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes) - uint8_t *data_response = BigBuf_malloc( (8+2) * 2 + 2); + uint8_t *data_generic_trace = BigBuf_malloc(8 + 2); // 8 bytes data + 2byte CRC is max tag answer + uint8_t *data_response = BigBuf_malloc( (8 + 2) * 2 + 2); - // Start from off (no field generated) - //FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - //SpinDelay(200); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); SpinDelay(100); StartCountSspClk(); @@ -1081,8 +1073,6 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); - // To control where we are in the protocol - int cmdsRecvd = 0; uint32_t time_0 = GetCountSspClk(); uint32_t t2r_time =0; uint32_t r2t_time =0; @@ -1091,6 +1081,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { bool buttonPressed = false; uint8_t response_delay = 1; while (!exitLoop) { + WDT_HIT(); response_delay = 1; LED_B_OFF(); //Signal tracer @@ -1105,41 +1096,95 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { //Signal tracer LED_C_ON(); - // Okay, look at the command now. + // Now look at the reader command and provide appropriate responses + // default is no response: + modulated_response = NULL; + modulated_response_size = 0; + trace_data = NULL; + trace_data_size = 0; if (receivedCmd[0] == ICLASS_CMD_ACTALL) { // Reader in anticollission phase modulated_response = resp_sof; - modulated_response_size = resp_sof_Len; //order = 1; + modulated_response_size = resp_sof_Len; trace_data = sof_data; trace_data_size = sizeof(sof_data); - } else if (receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY && len == 1) { + + } else if (receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY && len == 1) { // identify // Reader asks for anticollission CSN modulated_response = resp_anticoll; - modulated_response_size = resp_anticoll_len; //order = 2; + modulated_response_size = resp_anticoll_len; trace_data = anticoll_data; trace_data_size = sizeof(anticoll_data); //DbpString("Reader requests anticollission CSN:"); + + } else if (receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY && len == 4) { // read block + uint16_t blockNo = receivedCmd[1]; + if (simulationMode != ICLASS_SIM_MODE_FULL) { + // provide defaults for blocks 0, 1, 2, 5 + switch (blockNo) { + case 0: // csn (block 00) + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + break; + case 1: // configuration (block 01) + modulated_response = resp_conf; + modulated_response_size = resp_conf_len; + trace_data = conf_data; + trace_data_size = sizeof(conf_data); + break; + case 2: // e-purse (block 02) + modulated_response = resp_cc; + modulated_response_size = resp_cc_len; + trace_data = card_challenge_data; + trace_data_size = sizeof(card_challenge_data); + // set epurse of sim2,4 attack + if (reader_mac_buf != NULL) { + memcpy(reader_mac_buf, card_challenge_data, 8); + } + break; + case 5: // Application Issuer Area (block 05) + modulated_response = resp_aia; + modulated_response_size = resp_aia_len; + trace_data = aia_data; + trace_data_size = sizeof(aia_data); + break; + // default: don't respond + } + } else { // use data from emulator memory + memcpy(data_generic_trace, emulator + (receivedCmd[1] << 3), 8); + AppendCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIClassTagAnswer(trace_data, trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } + } else if (receivedCmd[0] == ICLASS_CMD_SELECT) { // Reader selects anticollission CSN. // Tag sends the corresponding real CSN modulated_response = resp_csn; - modulated_response_size = resp_csn_len; //order = 3; + modulated_response_size = resp_csn_len; trace_data = csn_data; trace_data_size = sizeof(csn_data); - //DbpString("Reader selects anticollission CSN:"); - } else if (receivedCmd[0] == ICLASS_CMD_READCHECK_KD) { - // Read e-purse (88 02) + + } else if (receivedCmd[0] == ICLASS_CMD_READCHECK_KD + || receivedCmd[0] == ICLASS_CMD_READCHECK_KC) { + // Read e-purse (88 02 || 18 02) modulated_response = resp_cc; - modulated_response_size = resp_cc_len; //order = 4; + modulated_response_size = resp_cc_len; trace_data = card_challenge_data; trace_data_size = sizeof(card_challenge_data); LED_B_ON(); + } else if (receivedCmd[0] == ICLASS_CMD_CHECK) { // Reader random and reader MAC!!! - if (simulationMode == MODE_FULLSIM) { + if (simulationMode == ICLASS_SIM_MODE_FULL) { //NR, from reader, is in receivedCmd +1 opt_doTagMAC_2(cipher_state, receivedCmd+1, data_generic_trace, diversified_key); - trace_data = data_generic_trace; trace_data_size = 4; CodeIClassTagAnswer(trace_data, trace_data_size); @@ -1148,13 +1193,9 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { modulated_response_size = ToSendMax; response_delay = 0; //We need to hurry here... (but maybe not too much... ??) //exitLoop = true; - } else { //Not fullsim, we don't respond + } else { // Not fullsim, we don't respond // We do not know what to answer, so lets keep quiet - modulated_response = resp_sof; - modulated_response_size = 0; - trace_data = NULL; - trace_data_size = 0; - if (simulationMode == MODE_EXIT_AFTER_MAC) { + if (simulationMode == ICLASS_SIM_MODE_EXIT_AFTER_MAC) { // dbprintf:ing ... Dbprintf("CSN: %02x %02x %02x %02x %02x %02x %02x %02x" ,csn[0],csn[1],csn[2],csn[3],csn[4],csn[5],csn[6],csn[7]); @@ -1163,7 +1204,8 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { receivedCmd[3], receivedCmd[4], receivedCmd[5], receivedCmd[6], receivedCmd[7], receivedCmd[8]); if (reader_mac_buf != NULL) { - memcpy(reader_mac_buf, receivedCmd+1, 8); + // save NR and MAC for sim 2,4 + memcpy(reader_mac_buf + 8, receivedCmd + 1, 8); } exitLoop = true; } @@ -1172,30 +1214,18 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (receivedCmd[0] == ICLASS_CMD_HALT && len == 1) { // Reader ends the session modulated_response = resp_sof; - modulated_response_size = 0; //order = 0; + modulated_response_size = 0; trace_data = NULL; trace_data_size = 0; - } else if (simulationMode == MODE_FULLSIM && receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY && len == 4) { - //Read block - uint16_t blk = receivedCmd[1]; - //Take the data... - memcpy(data_generic_trace, emulator + (blk << 3), 8); - //Add crc - AppendCrc(data_generic_trace, 8); - trace_data = data_generic_trace; - trace_data_size = 10; - CodeIClassTagAnswer(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); - modulated_response = data_response; - modulated_response_size = ToSendMax; - } else if (receivedCmd[0] == ICLASS_CMD_UPDATE && simulationMode == MODE_FULLSIM) { - //Probably the reader wants to update the nonce. Let's just ignore that for now. + + } else if (receivedCmd[0] == ICLASS_CMD_UPDATE && simulationMode == ICLASS_SIM_MODE_FULL) { + // Probably the reader wants to update the nonce. Let's just ignore that for now. // OBS! If this is implemented, don't forget to regenerate the cipher_state - //We're expected to respond with the data+crc, exactly what's already in the receivedcmd - //receivedcmd is now UPDATE 1b | ADDRESS 1b| DATA 8b| Signature 4b or CRC 2b| + // We're expected to respond with the data+crc, exactly what's already in the receivedCmd + // receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b //Take the data... - memcpy(data_generic_trace, receivedCmd+2, 8); + memcpy(data_generic_trace, receivedCmd + 2, 8); //Add crc AppendCrc(data_generic_trace, 8); trace_data = data_generic_trace; @@ -1204,35 +1234,23 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { memcpy(data_response, ToSend, ToSendMax); modulated_response = data_response; modulated_response_size = ToSendMax; + } else if (receivedCmd[0] == ICLASS_CMD_PAGESEL) { - //Pagesel - //Pagesel enables to select a page in the selected chip memory and return its configuration block - //Chips with a single page will not answer to this command + // Pagesel + // Pagesel enables to select a page in the selected chip memory and return its configuration block + // Chips with a single page will not answer to this command // It appears we're fine ignoring this. - //Otherwise, we should answer 8bytes (block) + 2bytes CRC + // Otherwise, we should answer 8bytes (block) + 2bytes CRC + } else { //#db# Unknown command received from reader (len=5): 26 1 0 f6 a 44 44 44 44 // Never seen this command before - Dbprintf("Unknown command received from reader (len=%d): %x %x %x %x %x %x %x %x %x", - len, - receivedCmd[0], receivedCmd[1], receivedCmd[2], - receivedCmd[3], receivedCmd[4], receivedCmd[5], - receivedCmd[6], receivedCmd[7], receivedCmd[8]); + print_result("Unhandled command received from reader ", receivedCmd, len); // Do not respond - modulated_response = resp_sof; - modulated_response_size = 0; //order = 0; - trace_data = NULL; - trace_data_size = 0; } - if (cmdsRecvd > 100) { - //DbpString("100 commands later..."); - //break; - } else { - cmdsRecvd++; - } /** - A legit tag has about 380us delay between reader EOT and tag SOF. + A legit tag has about 330us delay between reader EOT and tag SOF. **/ if (modulated_response_size > 0) { SendIClassAnswer(modulated_response, modulated_response_size, response_delay); @@ -1253,7 +1271,6 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } } - //Dbprintf("%x", cmdsRecvd); LED_A_OFF(); LED_B_OFF(); LED_C_OFF(); @@ -1280,6 +1297,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { uint32_t simType = arg0; uint32_t numberOfCSNS = arg1; + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); // Enable and clear the trace @@ -1288,35 +1306,35 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain //Use the emulator memory for SIM uint8_t *emulator = BigBuf_get_EM_addr(); - if (simType == 0) { + if (simType == ICLASS_SIM_MODE_CSN) { // Use the CSN from commandline memcpy(emulator, datain, 8); - doIClassSimulation(MODE_SIM_CSN,NULL); - } else if (simType == 1) { + doIClassSimulation(ICLASS_SIM_MODE_CSN, NULL); + } else if (simType == ICLASS_SIM_MODE_CSN_DEFAULT) { //Default CSN uint8_t csn_crc[] = { 0x03, 0x1f, 0xec, 0x8a, 0xf7, 0xff, 0x12, 0xe0, 0x00, 0x00 }; // Use the CSN from commandline memcpy(emulator, csn_crc, 8); - doIClassSimulation(MODE_SIM_CSN,NULL); - } else if (simType == 2) { + doIClassSimulation(ICLASS_SIM_MODE_CSN, NULL); + } else if (simType == ICLASS_SIM_MODE_READER_ATTACK) { uint8_t mac_responses[USB_CMD_DATA_SIZE] = { 0 }; Dbprintf("Going into attack mode, %d CSNS sent", numberOfCSNS); // In this mode, a number of csns are within datain. We'll simulate each one, one at a time - // in order to collect MAC's from the reader. This can later be used in an offlne-attack + // in order to collect MAC's from the reader. This can later be used in an offline-attack // in order to obtain the keys, as in the "dismantling iclass"-paper. - int i = 0; - for ( ; i < numberOfCSNS && i*8+8 < USB_CMD_DATA_SIZE; i++) { - // The usb data is 512 bytes, fitting 65 8-byte CSNs in there. + int i; + for (i = 0; i < numberOfCSNS && i*16+16 <= USB_CMD_DATA_SIZE; i++) { + // The usb data is 512 bytes, fitting 32 responses (8 byte CC + 4 Byte NR + 4 Byte MAC = 16 Byte response). memcpy(emulator, datain+(i*8), 8); - if (doIClassSimulation(MODE_EXIT_AFTER_MAC,mac_responses+i*8)) { - cmd_send(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i, 0, mac_responses, i*8); - return; // Button pressed + if (doIClassSimulation(ICLASS_SIM_MODE_EXIT_AFTER_MAC, mac_responses+i*16)) { + // Button pressed + break; } } - cmd_send(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i, 0, mac_responses, i*8); - } else if (simType == 3) { + cmd_send(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i, 0, mac_responses, i*16); + } else if (simType == ICLASS_SIM_MODE_FULL) { //This is 'full sim' mode, where we use the emulator storage for data. - doIClassSimulation(MODE_FULLSIM, NULL); + doIClassSimulation(ICLASS_SIM_MODE_FULL, NULL); } else { // We may want a mode here where we hardcode the csns to use (from proxclone). // That will speed things up a little, but not required just yet. @@ -1683,10 +1701,10 @@ void ReaderIClass(uint8_t arg0) { // 0 : CSN // 1 : Configuration // 2 : e-purse - // (3,4 write-only, kc and kd) - // 5 Application issuer area - // - //Then we can 'ship' back the 8 * 6 bytes of data, + // 3 : kd / debit / aa2 (write-only) + // 4 : kc / credit / aa1 (write-only) + // 5 : AIA, Application issuer area + //Then we can 'ship' back the 6 * 8 bytes of data, // with 0xFF:s in block 3 and 4. LED_B_ON(); diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index a2e31754..ee0dd16e 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -110,28 +110,20 @@ int CmdHFiClassSim(const char *Cmd) { } simType = param_get8ex(Cmd, 0, 0, 10); - if(simType == 0) - { + if (simType == ICLASS_SIM_MODE_CSN) { if (param_gethex(Cmd, 1, CSN, 16)) { PrintAndLog("A CSN should consist of 16 HEX symbols"); return usage_hf_iclass_sim(); } - PrintAndLog("--simtype:%02x csn:%s", simType, sprint_hex(CSN, 8)); } - if(simType > 3) - { - PrintAndLog("Undefined simptype %d", simType); - return usage_hf_iclass_sim(); - } - uint8_t numberOfCSNs=0; - if(simType == 2) - { - UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,NUM_CSNS}}; + uint8_t numberOfCSNs = 0; + if (simType == ICLASS_SIM_MODE_READER_ATTACK) { + UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType, NUM_CSNS}}; UsbCommand resp = {0}; - uint8_t csns[8*NUM_CSNS] = { + uint8_t csns[8 * NUM_CSNS] = { 0x00, 0x0B, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, 0x00, 0x04, 0x0E, 0x08, 0xF7, 0xFF, 0x12, 0xE0, 0x00, 0x09, 0x0D, 0x05, 0xF7, 0xFF, 0x12, 0xE0, @@ -148,7 +140,7 @@ int CmdHFiClassSim(const char *Cmd) { 0x00, 0x00, 0x02, 0x24, 0xF7, 0xFF, 0x12, 0xE0, 0x00, 0x05, 0x01, 0x21, 0xF7, 0xFF, 0x12, 0xE0 }; - memcpy(c.d.asBytes, csns, 8*NUM_CSNS); + memcpy(c.d.asBytes, csns, 8 * NUM_CSNS); SendCommand(&c); if (!WaitForResponseTimeout(CMD_ACK, &resp, -1)) { @@ -157,9 +149,9 @@ int CmdHFiClassSim(const char *Cmd) { } uint8_t num_mac_responses = resp.arg[1]; - PrintAndLog("Mac responses: %d MACs obtained (should be %d)", num_mac_responses,NUM_CSNS); + PrintAndLog("Mac responses: %d MACs obtained (should be %d)", num_mac_responses, NUM_CSNS); - size_t datalen = NUM_CSNS*24; + size_t datalen = NUM_CSNS * 24; /* * Now, time to dump to file. We'll use this format: * <8-byte CSN><8-byte CC><4 byte NR><4 byte MAC>.... @@ -167,28 +159,29 @@ int CmdHFiClassSim(const char *Cmd) { * 8 * 24 bytes. * * The returndata from the pm3 is on the following format - * <4 byte NR><4 byte MAC> - * CC are all zeroes, CSN is the same as was sent in + * <8 byte CC><4 byte NR><4 byte MAC> + * CSN is the same as was sent in **/ void* dump = malloc(datalen); - memset(dump,0,datalen);//<-- Need zeroes for the CC-field - uint8_t i = 0; - for(i = 0 ; i < NUM_CSNS ; i++) - { - memcpy(dump+i*24, csns+i*8,8); //CSN - //8 zero bytes here... + for(int i = 0; i < NUM_CSNS; i++) { + memcpy(dump + i*24, csns+i*8, 8); //CSN + //copy CC from response + memcpy(dump + i*24 + 8, resp.d.asBytes + i*16, 8); //Then comes NR_MAC (eight bytes from the response) - memcpy(dump+i*24+16,resp.d.asBytes+i*8,8); - + memcpy(dump + i*24 + 16, resp.d.asBytes + i*16 + 8, 8); } /** Now, save to dumpfile **/ saveFile("iclass_mac_attack", "bin", dump,datalen); free(dump); - }else - { - UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,numberOfCSNs}}; + + } else if (simType == ICLASS_SIM_MODE_CSN || simType == ICLASS_SIM_MODE_CSN_DEFAULT) { + UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType, numberOfCSNs}}; memcpy(c.d.asBytes, CSN, 8); SendCommand(&c); + + } else { + PrintAndLog("Undefined simtype %d", simType); + return usage_hf_iclass_sim(); } return 0; @@ -1265,24 +1258,18 @@ int CmdHFiClass_loclass(const char *Cmd) { return 0; } char fileName[255] = {0}; - if(opt == 'f') - { - if(param_getstr(Cmd, 1, fileName, sizeof(fileName)) > 0) - { + if(opt == 'f') { + if(param_getstr(Cmd, 1, fileName, sizeof(fileName)) > 0) { return bruteforceFileNoKeys(fileName); - }else - { + } else { PrintAndLog("You must specify a filename"); } - } - else if(opt == 't') - { + } else if(opt == 't') { int errors = testCipherUtils(); errors += testMAC(); errors += doKeyTests(0); errors += testElite(); - if(errors) - { + if(errors) { prnlog("OBS! There were errors!!!"); } return errors; diff --git a/client/loclass/cipher.c b/client/loclass/cipher.c index 3b146b10..03275a4f 100644 --- a/client/loclass/cipher.c +++ b/client/loclass/cipher.c @@ -240,6 +240,7 @@ void doMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) //free(cc_nr); return; } + void doMAC_N(uint8_t *address_data_p, uint8_t address_data_size, uint8_t *div_key_p, uint8_t mac[4]) { uint8_t *address_data; diff --git a/client/loclass/elite_crack.c b/client/loclass/elite_crack.c index 0cd00830..21a67f54 100644 --- a/client/loclass/elite_crack.c +++ b/client/loclass/elite_crack.c @@ -333,16 +333,14 @@ int bruteforceItem(dumpdata item, uint16_t keytable[]) * Only the lower eight bits correspond to the (hopefully cracked) key-value. **/ uint8_t bytes_to_recover[3] = {0}; - uint8_t numbytes_to_recover = 0 ; - int i; - for(i =0 ; i < 8 ; i++) - { - if(keytable[key_index[i]] & (CRACKED | BEING_CRACKED)) continue; + uint8_t numbytes_to_recover = 0; + + for(int i = 0; i < 8; i++) { + if (keytable[key_index[i]] & (CRACKED | BEING_CRACKED)) continue; bytes_to_recover[numbytes_to_recover++] = key_index[i]; keytable[key_index[i]] |= BEING_CRACKED; - if(numbytes_to_recover > 3) - { + if(numbytes_to_recover > 3) { prnlog("The CSN requires > 3 byte bruteforce, not supported"); printvar("CSN", item.csn,8); printvar("HASH1", key_index,8); @@ -370,15 +368,14 @@ int bruteforceItem(dumpdata item, uint16_t keytable[]) uint32_t endmask = 1 << 8*numbytes_to_recover; - for(i =0 ; i < numbytes_to_recover && numbytes_to_recover > 1; i++) + for (int i = 0; i < numbytes_to_recover && numbytes_to_recover > 1; i++) { prnlog("Bruteforcing byte %d", bytes_to_recover[i]); + } - while(!found && !(brute & endmask)) - { + while(!found && !(brute & endmask)) { //Update the keytable with the brute-values - for(i =0 ; i < numbytes_to_recover; i++) - { + for (int i = 0; i < numbytes_to_recover; i++) { keytable[bytes_to_recover[i]] &= 0xFF00; keytable[bytes_to_recover[i]] |= (brute >> (i*8) & 0xFF); } @@ -394,42 +391,34 @@ int bruteforceItem(dumpdata item, uint16_t keytable[]) //Diversify diversifyKey(item.csn, key_sel_p, div_key); //Calc mac - doMAC(item.cc_nr, div_key,calculated_MAC); + doMAC(item.cc_nr, div_key, calculated_MAC); - if(memcmp(calculated_MAC, item.mac, 4) == 0) - { - for(i =0 ; i < numbytes_to_recover; i++) - prnlog("=> %d: 0x%02x", bytes_to_recover[i],0xFF & keytable[bytes_to_recover[i]]); + if (memcmp(calculated_MAC, item.mac, 4) == 0) { + for (int i =0 ; i < numbytes_to_recover; i++) + prnlog("=> %d: 0x%02x", bytes_to_recover[i], 0xFF & keytable[bytes_to_recover[i]]); found = true; break; } brute++; - if((brute & 0xFFFF) == 0) - { + if ((brute & 0xFFFF) == 0) { printf("%d",(brute >> 16) & 0xFF); fflush(stdout); } } - if(! found) - { + if (! found) { prnlog("Failed to recover %d bytes using the following CSN",numbytes_to_recover); printvar("CSN",item.csn,8); errors++; //Before we exit, reset the 'BEING_CRACKED' to zero - for(i =0 ; i < numbytes_to_recover; i++) - { + for (int i = 0; i < numbytes_to_recover; i++) { keytable[bytes_to_recover[i]] &= 0xFF; keytable[bytes_to_recover[i]] |= CRACK_FAILED; } - - }else - { - for(i =0 ; i < numbytes_to_recover; i++) - { + } else { + for (int i = 0 ;i < numbytes_to_recover; i++) { keytable[bytes_to_recover[i]] &= 0xFF; keytable[bytes_to_recover[i]] |= CRACKED; } - } return errors; } @@ -516,7 +505,7 @@ int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) dumpdata* attack = (dumpdata* ) malloc(itemsize); - for(i = 0 ; i * itemsize < dumpsize ; i++ ) + for (i = 0 ; i * itemsize < dumpsize ; i++ ) { memcpy(attack,dump+i*itemsize, itemsize); errors += bruteforceItem(*attack, keytable); diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 1bc5e5ba..934b0924 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -228,7 +228,7 @@ typedef struct{ #define CMD_UNKNOWN 0xFFFF -//Mifare simulation flags +// Mifare simulation flags #define FLAG_INTERACTIVE (1<<0) #define FLAG_4B_UID_IN_DATA (1<<1) #define FLAG_7B_UID_IN_DATA (1<<2) @@ -236,7 +236,7 @@ typedef struct{ #define FLAG_RANDOM_NONCE (1<<5) -//Iclass reader flags +// iCLASS reader flags #define FLAG_ICLASS_READER_ONLY_ONCE 0x01 #define FLAG_ICLASS_READER_CC 0x02 #define FLAG_ICLASS_READER_CSN 0x04 @@ -245,8 +245,16 @@ typedef struct{ #define FLAG_ICLASS_READER_ONE_TRY 0x20 #define FLAG_ICLASS_READER_CEDITKEY 0x40 +// iCLASS simulation modes +#define ICLASS_SIM_MODE_CSN 0 +#define ICLASS_SIM_MODE_CSN_DEFAULT 1 +#define ICLASS_SIM_MODE_READER_ATTACK 2 +#define ICLASS_SIM_MODE_FULL 3 +#define ICLASS_SIM_MODE_READER_ATTACK_KEYROLL 4 +#define ICLASS_SIM_MODE_EXIT_AFTER_MAC 5 // note: device internal only -//hw tune args + +// hw tune args #define FLAG_TUNE_LF 1 #define FLAG_TUNE_HF 2 #define FLAG_TUNE_ALL 3 From 3d2c9c9b066c5a0b6a8dab467d86561223ddac10 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Mon, 2 Sep 2019 11:10:45 +0200 Subject: [PATCH 2/8] fix 'hf iclass sim' * fix debug print on unhandled commands * deduplicate: use sim functions from iso15693.c * fix times in tracelog and 'hf list iclass' (sim only) * don't check parity in 'hf list iclass' * fix timing in TransmitTo15693Reader() --- armsrc/iclass.c | 254 ++++++----------------------------------- armsrc/iso15693.c | 199 ++++++++++++++++++-------------- armsrc/iso15693.h | 20 +++- client/cmdhflist.c | 52 +++++---- common/iso15693tools.h | 6 +- 5 files changed, 204 insertions(+), 327 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 23d407f7..daa44256 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -42,9 +42,11 @@ #include "apps.h" #include "util.h" #include "string.h" +#include "printf.h" #include "common.h" #include "cmd.h" #include "iso14443a.h" +#include "iso15693.h" // Needed for CRC in emulation mode; // same construction as in ISO 14443; // different initial value (CRC_ICLASS) @@ -754,132 +756,7 @@ void rotateCSN(uint8_t* originalCSN, uint8_t* rotatedCSN) { } } -//----------------------------------------------------------------------------- -// Wait for commands from reader -// Stop when button is pressed -// Or return true when command is captured -//----------------------------------------------------------------------------- -static int GetIClassCommandFromReader(uint8_t *received, int *len, int maxLen) { - // Set FPGA mode to "simulated ISO 14443 tag", no modulation (listen - // only, since we are receiving, not transmitting). - // Signal field is off with the appropriate LED - LED_D_OFF(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); - - // Now run a `software UART' on the stream of incoming samples. - Uart.output = received; - Uart.byteCntMax = maxLen; - Uart.state = STATE_UNSYNCD; - - for (;;) { - WDT_HIT(); - - if (BUTTON_PRESS()) return false; - - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)) { - AT91C_BASE_SSC->SSC_THR = 0x00; - } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - - if (OutOfNDecoding(b & 0x0f)) { - *len = Uart.byteCnt; - return true; - } - } - } -} - -static uint8_t encode4Bits(const uint8_t b) { - uint8_t c = b & 0xF; - // OTA, the least significant bits first - // The columns are - // 1 - Bit value to send - // 2 - Reversed (big-endian) - // 3 - Manchester Encoded - // 4 - Hex values - - switch(c){ - // 1 2 3 4 - case 15: return 0x55; // 1111 -> 1111 -> 01010101 -> 0x55 - case 14: return 0x95; // 1110 -> 0111 -> 10010101 -> 0x95 - case 13: return 0x65; // 1101 -> 1011 -> 01100101 -> 0x65 - case 12: return 0xa5; // 1100 -> 0011 -> 10100101 -> 0xa5 - case 11: return 0x59; // 1011 -> 1101 -> 01011001 -> 0x59 - case 10: return 0x99; // 1010 -> 0101 -> 10011001 -> 0x99 - case 9: return 0x69; // 1001 -> 1001 -> 01101001 -> 0x69 - case 8: return 0xa9; // 1000 -> 0001 -> 10101001 -> 0xa9 - case 7: return 0x56; // 0111 -> 1110 -> 01010110 -> 0x56 - case 6: return 0x96; // 0110 -> 0110 -> 10010110 -> 0x96 - case 5: return 0x66; // 0101 -> 1010 -> 01100110 -> 0x66 - case 4: return 0xa6; // 0100 -> 0010 -> 10100110 -> 0xa6 - case 3: return 0x5a; // 0011 -> 1100 -> 01011010 -> 0x5a - case 2: return 0x9a; // 0010 -> 0100 -> 10011010 -> 0x9a - case 1: return 0x6a; // 0001 -> 1000 -> 01101010 -> 0x6a - default: return 0xaa; // 0000 -> 0000 -> 10101010 -> 0xaa - - } -} - -//----------------------------------------------------------------------------- -// Prepare tag messages -//----------------------------------------------------------------------------- -static void CodeIClassTagAnswer(const uint8_t *cmd, int len) { - - /* - * SOF comprises 3 parts; - * * An unmodulated time of 56.64 us - * * 24 pulses of 423.75 kHz (fc/32) - * * A logic 1, which starts with an unmodulated time of 18.88us - * followed by 8 pulses of 423.75kHz (fc/32) - * - * - * EOF comprises 3 parts: - * - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated - * time of 18.88us. - * - 24 pulses of fc/32 - * - An unmodulated time of 56.64 us - * - * - * A logic 0 starts with 8 pulses of fc/32 - * followed by an unmodulated time of 256/fc (~18,88us). - * - * A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by - * 8 pulses of fc/32 (also 18.88us) - * - * The mode FPGA_HF_SIMULATOR_MODULATE_424K_8BIT which we use to simulate tag, - * works like this. - * - A 1-bit input to the FPGA becomes 8 pulses on 423.5kHz (fc/32) (18.88us). - * - A 0-bit input to the FPGA becomes an unmodulated time of 18.88us - * - * In this mode the SOF can be written as 00011101 = 0x1D - * The EOF can be written as 10111000 = 0xb8 - * A logic 1 is 01 - * A logic 0 is 10 - * - * */ - - int i; - - ToSendReset(); - - // Send SOF - ToSend[++ToSendMax] = 0x1D; - - for (i = 0; i < len; i++) { - uint8_t b = cmd[i]; - ToSend[++ToSendMax] = encode4Bits(b & 0xF); // Least significant half - ToSend[++ToSendMax] = encode4Bits((b >>4) & 0xF); // Most significant half - } - - // Send EOF - ToSend[++ToSendMax] = 0xB8; - //lastProxToAirDuration = 8*ToSendMax - 3*8 - 3*8;//Not counting zeroes in the beginning or end - // Convert from last byte pos to length - ToSendMax++; -} - -// Only SOF +// Encode SOF only static void CodeIClassTagSOF() { //So far a dummy implementation, not used //int lastProxToAirDuration =0; @@ -897,43 +774,6 @@ static void AppendCrc(uint8_t *data, int len) { ComputeCrc14443(CRC_ICLASS, data, len, data+len, data+len+1); } -static int SendIClassAnswer(uint8_t *resp, int respLen, int delay) { - int i = 0, d = 0;//, u = 0, d = 0; - uint8_t b = 0; - - //FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR|FPGA_HF_SIMULATOR_MODULATE_424K); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K_8BIT); - - AT91C_BASE_SSC->SSC_THR = 0x00; - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); - while (true) { - if ((AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)){ - b = AT91C_BASE_SSC->SSC_RHR; - (void) b; - } - if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_TXRDY)){ - b = 0x00; - if (d < delay) { - // send 0x00 byte (causing a 2048/13,56MHz = 151us delay) - d++; - } else { - if (i < respLen) { - b = resp[i]; - } - i++; - } - AT91C_BASE_SSC->SSC_THR = b; - } - -// if (i > respLen +4) break; - if (i > respLen + 1) break; - // send 2 more 0x00 bytes (causing a 302us delay) - } - - return 0; -} - - /** * @brief Does the actual simulation @@ -1032,33 +872,33 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // Prepare card messages ToSendMax = 0; - // First card answer: SOF + // First card answer: SOF only CodeIClassTagSOF(); memcpy(resp_sof, ToSend, ToSendMax); resp_sof_Len = ToSendMax; // Anticollision CSN - CodeIClassTagAnswer(anticoll_data, sizeof(anticoll_data)); + CodeIso15693AsTag(anticoll_data, sizeof(anticoll_data)); memcpy(resp_anticoll, ToSend, ToSendMax); resp_anticoll_len = ToSendMax; // CSN (block 0) - CodeIClassTagAnswer(csn_data, sizeof(csn_data)); + CodeIso15693AsTag(csn_data, sizeof(csn_data)); memcpy(resp_csn, ToSend, ToSendMax); resp_csn_len = ToSendMax; // Configuration (block 1) - CodeIClassTagAnswer(conf_data, sizeof(conf_data)); + CodeIso15693AsTag(conf_data, sizeof(conf_data)); memcpy(resp_conf, ToSend, ToSendMax); resp_conf_len = ToSendMax; // e-Purse (block 2) - CodeIClassTagAnswer(card_challenge_data, sizeof(card_challenge_data)); + CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); memcpy(resp_cc, ToSend, ToSendMax); resp_cc_len = ToSendMax; // Application Issuer Area (block 5) - CodeIClassTagAnswer(aia_data, sizeof(aia_data)); + CodeIso15693AsTag(aia_data, sizeof(aia_data)); memcpy(resp_aia, ToSend, ToSendMax); resp_aia_len = ToSendMax; @@ -1066,33 +906,22 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t *data_generic_trace = BigBuf_malloc(8 + 2); // 8 bytes data + 2byte CRC is max tag answer uint8_t *data_response = BigBuf_malloc( (8 + 2) * 2 + 2); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_TAGSIM_LISTEN); - SpinDelay(100); - StartCountSspClk(); - // We need to listen to the high-frequency, peak-detected path. - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaSetupSsc(FPGA_MAJOR_MODE_HF_ISO14443A); - - uint32_t time_0 = GetCountSspClk(); - uint32_t t2r_time =0; - uint32_t r2t_time =0; - LED_A_ON(); bool buttonPressed = false; - uint8_t response_delay = 1; while (!exitLoop) { WDT_HIT(); - response_delay = 1; LED_B_OFF(); //Signal tracer // Can be used to get a trigger for an oscilloscope.. LED_C_OFF(); - if (!GetIClassCommandFromReader(receivedCmd, &len, 100)) { + uint32_t reader_eof_time = 0; + len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time); + if (len < 0) { buttonPressed = true; break; } - r2t_time = GetCountSspClk(); + //Signal tracer LED_C_ON(); @@ -1116,7 +945,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = anticoll_data; trace_data_size = sizeof(anticoll_data); //DbpString("Reader requests anticollission CSN:"); - + } else if (receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY && len == 4) { // read block uint16_t blockNo = receivedCmd[1]; if (simulationMode != ICLASS_SIM_MODE_FULL) { @@ -1157,7 +986,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { AppendCrc(data_generic_trace, 8); trace_data = data_generic_trace; trace_data_size = 10; - CodeIClassTagAnswer(trace_data, trace_data_size); + CodeIso15693AsTag(trace_data, trace_data_size); memcpy(data_response, ToSend, ToSendMax); modulated_response = data_response; modulated_response_size = ToSendMax; @@ -1183,26 +1012,18 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (receivedCmd[0] == ICLASS_CMD_CHECK) { // Reader random and reader MAC!!! if (simulationMode == ICLASS_SIM_MODE_FULL) { - //NR, from reader, is in receivedCmd +1 + //NR, from reader, is in receivedCmd+1 opt_doTagMAC_2(cipher_state, receivedCmd+1, data_generic_trace, diversified_key); trace_data = data_generic_trace; trace_data_size = 4; - CodeIClassTagAnswer(trace_data, trace_data_size); + CodeIso15693AsTag(trace_data, trace_data_size); memcpy(data_response, ToSend, ToSendMax); modulated_response = data_response; modulated_response_size = ToSendMax; - response_delay = 0; //We need to hurry here... (but maybe not too much... ??) //exitLoop = true; } else { // Not fullsim, we don't respond // We do not know what to answer, so lets keep quiet if (simulationMode == ICLASS_SIM_MODE_EXIT_AFTER_MAC) { - // dbprintf:ing ... - Dbprintf("CSN: %02x %02x %02x %02x %02x %02x %02x %02x" - ,csn[0],csn[1],csn[2],csn[3],csn[4],csn[5],csn[6],csn[7]); - Dbprintf("RDR: (len=%02d): %02x %02x %02x %02x %02x %02x %02x %02x %02x",len, - receivedCmd[0], receivedCmd[1], receivedCmd[2], - receivedCmd[3], receivedCmd[4], receivedCmd[5], - receivedCmd[6], receivedCmd[7], receivedCmd[8]); if (reader_mac_buf != NULL) { // save NR and MAC for sim 2,4 memcpy(reader_mac_buf + 8, receivedCmd + 1, 8); @@ -1223,14 +1044,11 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // OBS! If this is implemented, don't forget to regenerate the cipher_state // We're expected to respond with the data+crc, exactly what's already in the receivedCmd // receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b - - //Take the data... memcpy(data_generic_trace, receivedCmd + 2, 8); - //Add crc AppendCrc(data_generic_trace, 8); trace_data = data_generic_trace; trace_data_size = 10; - CodeIClassTagAnswer(trace_data, trace_data_size); + CodeIso15693AsTag(trace_data, trace_data_size); memcpy(data_response, ToSend, ToSendMax); modulated_response = data_response; modulated_response_size = ToSendMax; @@ -1243,9 +1061,13 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // Otherwise, we should answer 8bytes (block) + 2bytes CRC } else { - //#db# Unknown command received from reader (len=5): 26 1 0 f6 a 44 44 44 44 // Never seen this command before - print_result("Unhandled command received from reader ", receivedCmd, len); + char debug_message[250]; // should be enough + sprintf(debug_message, "Unhandled command (len = %d) received from reader:", len); + for (int i = 0; i < len && strlen(debug_message) < sizeof(debug_message) - 3 - 1; i++) { + sprintf(debug_message + strlen(debug_message), " %02x", receivedCmd[i]); + } + Dbprintf("%s", debug_message); // Do not respond } @@ -1253,22 +1075,11 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { A legit tag has about 330us delay between reader EOT and tag SOF. **/ if (modulated_response_size > 0) { - SendIClassAnswer(modulated_response, modulated_response_size, response_delay); - t2r_time = GetCountSspClk(); + uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM - DELAY_ARM_TO_READER_SIM; + TransmitTo15693Reader(modulated_response, modulated_response_size, response_time, false); + LogTrace(trace_data, trace_data_size, response_time + DELAY_ARM_TO_READER_SIM, response_time + (modulated_response_size << 6) + DELAY_ARM_TO_READER_SIM, NULL, false); } - uint8_t parity[MAX_PARITY_SIZE]; - GetParity(receivedCmd, len, parity); - LogTrace(receivedCmd, len, (r2t_time-time_0) << 4, (r2t_time-time_0) << 4, parity, true); - - if (trace_data != NULL) { - GetParity(trace_data, trace_data_size, parity); - LogTrace(trace_data, trace_data_size, (t2r_time-time_0) << 4, (t2r_time-time_0) << 4, parity, false); - } - if (!get_tracing()) { - DbpString("Trace full"); - //break; - } } LED_A_OFF(); @@ -1298,7 +1109,12 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain uint32_t simType = arg0; uint32_t numberOfCSNS = arg1; + // setup hardware for simulation: FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + SetAdcMuxFor(GPIO_MUXSEL_HIPKD); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); + StartCountSspClk(); // Enable and clear the trace set_tracing(true); @@ -1330,6 +1146,12 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain // Button pressed break; } + Dbprintf("CSN: %02x %02x %02x %02x %02x %02x %02x %02x", + datain[i*8+0], datain[i*8+1], datain[i*8+2], datain[i*8+3], + datain[i*8+4], datain[i*8+5], datain[i*8+6], datain[i*8+7]); + Dbprintf("NR,MAC: %02x %02x %02x %02x %02x %02x %02x %02x", + datain[i*8+ 8], datain[i*8+ 9], datain[i*8+10], datain[i*8+11], + datain[i*8+12], datain[i*8+13], datain[i*8+14], datain[i*8+15]); } cmd_send(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i, 0, mac_responses, i*16); } else if (simType == ICLASS_SIM_MODE_FULL) { diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index f4120512..7fdf2a35 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -68,27 +68,14 @@ static int DEBUG = 0; /////////////////////////////////////////////////////////////////////// // ISO 15693 Part 2 - Air Interface -// This section basicly contains transmission and receiving of bits +// This section basically contains transmission and receiving of bits /////////////////////////////////////////////////////////////////////// -#define Crc(data,datalen) Iso15693Crc(data,datalen) -#define AddCrc(data,datalen) Iso15693AddCrc(data,datalen) -#define sprintUID(target,uid) Iso15693sprintUID(target,uid) - // buffers -#define ISO15693_DMA_BUFFER_SIZE 2048 // must be a power of 2 +#define ISO15693_DMA_BUFFER_SIZE 2048 // must be a power of 2 #define ISO15693_MAX_RESPONSE_LENGTH 36 // allows read single block with the maximum block size of 256bits. Read multiple blocks not supported yet #define ISO15693_MAX_COMMAND_LENGTH 45 // allows write single block with the maximum block size of 256bits. Write multiple blocks not supported yet -// timing. Delays in SSP_CLK ticks. -// SSP_CLK runs at 13,56MHz / 32 = 423.75kHz when simulating a tag -#define DELAY_READER_TO_ARM_SIM 8 -#define DELAY_ARM_TO_READER_SIM 1 -#define DELAY_ISO15693_VCD_TO_VICC_SIM 132 // 132/423.75kHz = 311.5us from end of command EOF to start of tag response -//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when acting as reader -#define DELAY_ISO15693_VCD_TO_VICC_READER 1056 // 1056/3,39MHz = 311.5us from end of command EOF to start of tag response -#define DELAY_ISO15693_VICC_TO_VCD_READER 1017 // 1017/3.39MHz = 300us between end of tag response and next reader command - // --------------------------- // Signal Processing // --------------------------- @@ -228,22 +215,72 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) } -static void CodeIso15693AsTag(uint8_t *cmd, int n) -{ +// static uint8_t encode4Bits(const uint8_t b) { + // uint8_t c = b & 0xF; + // // OTA, the least significant bits first + // // The columns are + // // 1 - Bit value to send + // // 2 - Reversed (big-endian) + // // 3 - Manchester Encoded + // // 4 - Hex values + + // switch(c){ + // // 1 2 3 4 + // case 15: return 0x55; // 1111 -> 1111 -> 01010101 -> 0x55 + // case 14: return 0x95; // 1110 -> 0111 -> 10010101 -> 0x95 + // case 13: return 0x65; // 1101 -> 1011 -> 01100101 -> 0x65 + // case 12: return 0xa5; // 1100 -> 0011 -> 10100101 -> 0xa5 + // case 11: return 0x59; // 1011 -> 1101 -> 01011001 -> 0x59 + // case 10: return 0x99; // 1010 -> 0101 -> 10011001 -> 0x99 + // case 9: return 0x69; // 1001 -> 1001 -> 01101001 -> 0x69 + // case 8: return 0xa9; // 1000 -> 0001 -> 10101001 -> 0xa9 + // case 7: return 0x56; // 0111 -> 1110 -> 01010110 -> 0x56 + // case 6: return 0x96; // 0110 -> 0110 -> 10010110 -> 0x96 + // case 5: return 0x66; // 0101 -> 1010 -> 01100110 -> 0x66 + // case 4: return 0xa6; // 0100 -> 0010 -> 10100110 -> 0xa6 + // case 3: return 0x5a; // 0011 -> 1100 -> 01011010 -> 0x5a + // case 2: return 0x9a; // 0010 -> 0100 -> 10011010 -> 0x9a + // case 1: return 0x6a; // 0001 -> 1000 -> 01101010 -> 0x6a + // default: return 0xaa; // 0000 -> 0000 -> 10101010 -> 0xaa + + // } +// } + +void CodeIso15693AsTag(uint8_t *cmd, size_t len) { + /* + * SOF comprises 3 parts; + * * An unmodulated time of 56.64 us + * * 24 pulses of 423.75 kHz (fc/32) + * * A logic 1, which starts with an unmodulated time of 18.88us + * followed by 8 pulses of 423.75kHz (fc/32) + * + * EOF comprises 3 parts: + * - A logic 0 (which starts with 8 pulses of fc/32 followed by an unmodulated + * time of 18.88us. + * - 24 pulses of fc/32 + * - An unmodulated time of 56.64 us + * + * A logic 0 starts with 8 pulses of fc/32 + * followed by an unmodulated time of 256/fc (~18,88us). + * + * A logic 0 starts with unmodulated time of 256/fc (~18,88us) followed by + * 8 pulses of fc/32 (also 18.88us) + * + * A bit here becomes 8 pulses of fc/32. Therefore: + * The SOF can be written as 00011101 = 0x1D + * The EOF can be written as 10111000 = 0xb8 + * A logic 1 is 01 + * A logic 0 is 10 + * + * */ + ToSendReset(); // SOF - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); + ToSend[++ToSendMax] = 0x1D; // 00011101 // data - for(int i = 0; i < n; i++) { + for(int i = 0; i < len; i++) { for(int j = 0; j < 8; j++) { if ((cmd[i] >> j) & 0x01) { ToSendStuffBit(0); @@ -256,14 +293,7 @@ static void CodeIso15693AsTag(uint8_t *cmd, int n) } // EOF - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(1); - ToSendStuffBit(0); - ToSendStuffBit(0); - ToSendStuffBit(0); + ToSend[++ToSendMax] = 0xB8; // 10111000 ToSendMax++; } @@ -297,41 +327,41 @@ static void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t start_time) //----------------------------------------------------------------------------- // Transmit the tag response (to the reader) that was placed in cmd[]. //----------------------------------------------------------------------------- -static void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t start_time, bool slow) -{ +void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t start_time, bool slow) { // don't use the FPGA_HF_SIMULATOR_MODULATE_424K_8BIT minor mode. It would spoil GetCountSspClk() FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K); uint8_t shift_delay = start_time & 0x00000007; - uint8_t bitmask = 0x00; - for (int i = 0; i < shift_delay; i++) { - bitmask |= (0x01 << i); - } while (GetCountSspClk() < (start_time & 0xfffffff8)) ; - AT91C_BASE_SSC->SSC_THR = 0x00; // clear TXRDY - LED_C_ON(); uint8_t bits_to_shift = 0x00; - for(size_t c = 0; c <= len; c++) { - uint8_t bits_to_send = bits_to_shift << (8 - shift_delay) | (c==len?0x00:cmd[c]) >> shift_delay; - bits_to_shift = cmd[c] & bitmask; + uint8_t bits_to_send = 0x00; + for(size_t c = 0; c < len; c++) { for (int i = 7; i >= 0; i--) { + uint8_t cmd_bits = ((cmd[c] >> i) & 0x01) ? 0xff : 0x00; for (int j = 0; j < (slow?4:1); ) { + bits_to_send = bits_to_shift << (8 - shift_delay) | cmd_bits >> shift_delay; + bits_to_shift = cmd_bits; if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { - if (bits_to_send >> i & 0x01) { - AT91C_BASE_SSC->SSC_THR = 0xff; - } else { - AT91C_BASE_SSC->SSC_THR = 0x00; - } + AT91C_BASE_SSC->SSC_THR = bits_to_send; j++; } - WDT_HIT(); } } + WDT_HIT(); } + // send the remaining bits, padded with 0: + bits_to_send = bits_to_shift << (8 - shift_delay); + for ( ; ; ) { + if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + AT91C_BASE_SSC->SSC_THR = bits_to_send; + break; + } + } LED_C_OFF(); + } @@ -682,9 +712,9 @@ static void DecodeReaderReset(DecodeReader_t* DecodeReader) static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uint8_t bit, DecodeReader_t *restrict DecodeReader) { - switch(DecodeReader->state) { + switch (DecodeReader->state) { case STATE_READER_UNSYNCD: - if(!bit) { + if (!bit) { // we went low, so this could be the beginning of a SOF DecodeReader->posCount = 1; DecodeReader->state = STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF; @@ -693,14 +723,14 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin case STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF: DecodeReader->posCount++; - if(bit) { // detected rising edge - if(DecodeReader->posCount < 4) { // rising edge too early (nominally expected at 5) + if (bit) { // detected rising edge + if (DecodeReader->posCount < 4) { // rising edge too early (nominally expected at 5) DecodeReaderReset(DecodeReader); } else { // SOF DecodeReader->state = STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF; } } else { - if(DecodeReader->posCount > 5) { // stayed low for too long + if (DecodeReader->posCount > 5) { // stayed low for too long DecodeReaderReset(DecodeReader); } else { // do nothing, keep waiting @@ -710,7 +740,7 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin case STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF: DecodeReader->posCount++; - if(!bit) { // detected a falling edge + if (!bit) { // detected a falling edge if (DecodeReader->posCount < 20) { // falling edge too early (nominally expected at 21 earliest) DecodeReaderReset(DecodeReader); } else if (DecodeReader->posCount < 23) { // SOF for 1 out of 4 coding @@ -723,7 +753,7 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; } } else { - if(DecodeReader->posCount > 29) { // stayed high for too long + if (DecodeReader->posCount > 29) { // stayed high for too long DecodeReaderReset(DecodeReader); } else { // do nothing, keep waiting @@ -881,19 +911,18 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin // Receive a command (from the reader to us, where we are the simulated tag), // and store it in the given buffer, up to the given maximum length. Keeps // spinning, waiting for a well-framed command, until either we get one -// (returns true) or someone presses the pushbutton on the board (false). +// (returns len) or someone presses the pushbutton on the board (returns -1). // // Assume that we're called with the SSC (to the FPGA) and ADC path set // correctly. //----------------------------------------------------------------------------- -static int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time) -{ +int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time) { int samples = 0; bool gotFrame = false; uint8_t b; - uint8_t *dmaBuf = BigBuf_malloc(ISO15693_DMA_BUFFER_SIZE); + uint8_t dmaBuf[ISO15693_DMA_BUFFER_SIZE]; // the decoder data structure DecodeReader_t DecodeReader = {0}; @@ -910,21 +939,21 @@ static int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint3 (void) temp; while (!(AT91C_BASE_SSC->SSC_SR & AT91C_SSC_RXRDY)) ; - uint32_t bit_time = GetCountSspClk() & 0xfffffff8; + uint32_t dma_start_time = GetCountSspClk() & 0xfffffff8; // Setup and start DMA. FpgaSetupSscDma(dmaBuf, ISO15693_DMA_BUFFER_SIZE); uint8_t *upTo = dmaBuf; - for(;;) { + for (;;) { uint16_t behindBy = ((uint8_t*)AT91C_BASE_PDC_SSC->PDC_RPR - upTo) & (ISO15693_DMA_BUFFER_SIZE-1); if (behindBy == 0) continue; b = *upTo++; - if(upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. + if (upTo >= dmaBuf + ISO15693_DMA_BUFFER_SIZE) { // we have read all of the DMA buffer content. upTo = dmaBuf; // start reading the circular buffer from the beginning - if(behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { + if (behindBy > (9*ISO15693_DMA_BUFFER_SIZE/10)) { Dbprintf("About to blow circular buffer - aborted! behindBy=%d", behindBy); break; } @@ -936,7 +965,7 @@ static int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint3 for (int i = 7; i >= 0; i--) { if (Handle15693SampleFromReader((b >> i) & 0x01, &DecodeReader)) { - *eof_time = bit_time + samples - DELAY_READER_TO_ARM_SIM; // end of EOF + *eof_time = dma_start_time + samples - DELAY_READER_TO_ARM_SIM; // end of EOF gotFrame = true; break; } @@ -948,22 +977,24 @@ static int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint3 } if (BUTTON_PRESS()) { - DecodeReader.byteCount = 0; + DecodeReader.byteCount = -1; break; } WDT_HIT(); } - FpgaDisableSscDma(); - BigBuf_free_keep_EM(); if (DEBUG) Dbprintf("samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", samples, gotFrame, DecodeReader.state, DecodeReader.byteCount, DecodeReader.bitCount, DecodeReader.posCount); if (DecodeReader.byteCount > 0) { - LogTrace(DecodeReader.output, DecodeReader.byteCount, 0, *eof_time, NULL, true); + uint32_t sof_time = *eof_time + - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128:2048) // time for byte transfers + - 32 // time for SOF transfer + - 16; // time for EOF transfer + LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time, *eof_time, NULL, true); } return DecodeReader.byteCount; @@ -985,7 +1016,7 @@ static void BuildIdentifyRequest(void) // no mask cmd[2] = 0x00; //Now the CRC - crc = Crc(cmd, 3); + crc = Iso15693Crc(cmd, 3); cmd[3] = crc & 0xff; cmd[4] = crc >> 8; @@ -1219,7 +1250,7 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) // Block number to read cmd[10] = blockNumber; //Now the CRC - crc = Crc(cmd, 11); // the crc needs to be calculated over 11 bytes + crc = Iso15693Crc(cmd, 11); // the crc needs to be calculated over 11 bytes cmd[11] = crc & 0xff; cmd[12] = crc >> 8; @@ -1246,7 +1277,7 @@ static void BuildInventoryResponse(uint8_t *uid) cmd[8] = uid[1]; //0x05; cmd[9] = uid[0]; //0xe0; //Now the CRC - crc = Crc(cmd, 10); + crc = Iso15693Crc(cmd, 10); cmd[10] = crc & 0xff; cmd[11] = crc >> 8; @@ -1341,7 +1372,7 @@ void DbdecodeIso15693Answer(int len, uint8_t *d) { strncat(status,"NoErr ", DBD15STATLEN); } - crc=Crc(d,len-2); + crc=Iso15693Crc(d,len-2); if ( (( crc & 0xff ) == d[len-2]) && (( crc >> 8 ) == d[len-1]) ) strncat(status,"CrcOK",DBD15STATLEN); else @@ -1526,12 +1557,12 @@ void BruteforceIso15693Afi(uint32_t speed) data[0] = ISO15693_REQ_DATARATE_HIGH | ISO15693_REQ_INVENTORY | ISO15693_REQINV_SLOT1; data[1] = ISO15693_INVENTORY; data[2] = 0; // mask length - datalen = AddCrc(data,3); + datalen = Iso15693AddCrc(data,3); recvlen = SendDataTag(data, datalen, false, speed, recv, sizeof(recv), 0); uint32_t start_time = GetCountSspClk() + DELAY_ISO15693_VICC_TO_VCD_READER; WDT_HIT(); if (recvlen>=12) { - Dbprintf("NoAFI UID=%s", sprintUID(NULL, &recv[2])); + Dbprintf("NoAFI UID=%s", Iso15693sprintUID(NULL, &recv[2])); } // now with AFI @@ -1543,12 +1574,12 @@ void BruteforceIso15693Afi(uint32_t speed) for (int i = 0; i < 256; i++) { data[2] = i & 0xFF; - datalen = AddCrc(data,4); + datalen = Iso15693AddCrc(data,4); recvlen = SendDataTag(data, datalen, false, speed, recv, sizeof(recv), start_time); start_time = GetCountSspClk() + DELAY_ISO15693_VICC_TO_VCD_READER; WDT_HIT(); if (recvlen >= 12) { - Dbprintf("AFI=%i UID=%s", i, sprintUID(NULL, &recv[2])); + Dbprintf("AFI=%i UID=%s", i, Iso15693sprintUID(NULL, &recv[2])); } } Dbprintf("AFI Bruteforcing done."); @@ -1646,7 +1677,7 @@ void SetTag15693Uid(uint8_t *uid) for (int i=0; i<4; i++) { // Add the CRC - crc = Crc(cmd[i], 7); + crc = Iso15693Crc(cmd[i], 7); cmd[i][7] = crc & 0xff; cmd[i][8] = crc >> 8; @@ -1702,7 +1733,7 @@ static void __attribute__((unused)) BuildSysInfoRequest(uint8_t *uid) cmd[8] = 0x05; cmd[9]= 0xe0; // always e0 (not exactly unique) //Now the CRC - crc = Crc(cmd, 10); // the crc needs to be calculated over 2 bytes + crc = Iso15693Crc(cmd, 10); // the crc needs to be calculated over 2 bytes cmd[10] = crc & 0xff; cmd[11] = crc >> 8; @@ -1737,7 +1768,7 @@ static void __attribute__((unused)) BuildReadMultiBlockRequest(uint8_t *uid) // Number of Blocks to read cmd[11] = 0x2f; // read quite a few //Now the CRC - crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes + crc = Iso15693Crc(cmd, 12); // the crc needs to be calculated over 2 bytes cmd[12] = crc & 0xff; cmd[13] = crc >> 8; @@ -1772,7 +1803,7 @@ static void __attribute__((unused)) BuildArbitraryRequest(uint8_t *uid,uint8_t C // cmd[12] = 0x00; // cmd[13] = 0x00; //Now the CRC - crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes + crc = Iso15693Crc(cmd, 12); // the crc needs to be calculated over 2 bytes cmd[12] = crc & 0xff; cmd[13] = crc >> 8; @@ -1807,7 +1838,7 @@ static void __attribute__((unused)) BuildArbitraryCustomRequest(uint8_t uid[], u // cmd[12] = 0x00; // cmd[13] = 0x00; //Now the CRC - crc = Crc(cmd, 12); // the crc needs to be calculated over 2 bytes + crc = Iso15693Crc(cmd, 12); // the crc needs to be calculated over 2 bytes cmd[12] = crc & 0xff; cmd[13] = crc >> 8; diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index 68df2693..f6cf29f1 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -8,17 +8,31 @@ // Routines to support ISO 15693. //----------------------------------------------------------------------------- -#ifndef __ISO15693_H -#define __ISO15693_H +#ifndef ISO15693_H__ +#define ISO15693_H__ #include +#include +#include +// Delays in SSP_CLK ticks. +// SSP_CLK runs at 13,56MHz / 32 = 423.75kHz when simulating a tag +#define DELAY_READER_TO_ARM_SIM 8 +#define DELAY_ARM_TO_READER_SIM 1 +#define DELAY_ISO15693_VCD_TO_VICC_SIM 132 // 132/423.75kHz = 311.5us from end of command EOF to start of tag response +//SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when acting as reader +#define DELAY_ISO15693_VCD_TO_VICC_READER 1056 // 1056/3,39MHz = 311.5us from end of command EOF to start of tag response +#define DELAY_ISO15693_VICC_TO_VCD_READER 1017 // 1017/3.39MHz = 300us between end of tag response and next reader command + +void CodeIso15693AsTag(uint8_t *cmd, size_t len); +int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time); +void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t start_time, bool slow); void SnoopIso15693(void); void AcquireRawAdcSamplesIso15693(void); void ReaderIso15693(uint32_t parameter); void SimTagIso15693(uint32_t parameter, uint8_t *uid); void BruteforceIso15693Afi(uint32_t speed); -void DirectTag15693Command(uint32_t datalen,uint32_t speed, uint32_t recv, uint8_t data[]); +void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint8_t data[]); void SetTag15693Uid(uint8_t *uid); void SetDebugIso15693(uint32_t flag); diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 5384bfce..1b8e0955 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -213,30 +213,29 @@ uint8_t iclass_CRC_check(bool isResponse, uint8_t* data, uint8_t len) } -void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) -{ +void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { switch(cmd[0]) { - case ICLASS_CMD_ACTALL: snprintf(exp,size,"ACTALL"); break; - case ICLASS_CMD_READ_OR_IDENTIFY:{ - if(cmdsize > 1){ + case ICLASS_CMD_ACTALL: snprintf(exp, size, "ACTALL"); break; + case ICLASS_CMD_READ_OR_IDENTIFY: { + if (cmdsize > 1){ snprintf(exp,size,"READ(%d)",cmd[1]); - }else{ + } else { snprintf(exp,size,"IDENTIFY"); } break; } - case ICLASS_CMD_SELECT: snprintf(exp,size,"SELECT"); break; - case ICLASS_CMD_PAGESEL: snprintf(exp,size,"PAGESEL(%d)", cmd[1]); break; - case ICLASS_CMD_READCHECK_KC:snprintf(exp,size,"READCHECK[Kc](%d)", cmd[1]); break; - case ICLASS_CMD_READCHECK_KD:snprintf(exp,size,"READCHECK[Kd](%d)", cmd[1]); break; - case ICLASS_CMD_CHECK: snprintf(exp,size,"CHECK"); break; - case ICLASS_CMD_DETECT: snprintf(exp,size,"DETECT"); break; - case ICLASS_CMD_HALT: snprintf(exp,size,"HALT"); break; - case ICLASS_CMD_UPDATE: snprintf(exp,size,"UPDATE(%d)",cmd[1]); break; - case ICLASS_CMD_ACT: snprintf(exp,size,"ACT"); break; - case ICLASS_CMD_READ4: snprintf(exp,size,"READ4(%d)",cmd[1]); break; - default: snprintf(exp,size,"?"); break; + case ICLASS_CMD_SELECT: snprintf(exp,size, "SELECT"); break; + case ICLASS_CMD_PAGESEL: snprintf(exp,size, "PAGESEL(%d)", cmd[1]); break; + case ICLASS_CMD_READCHECK_KC:snprintf(exp,size, "READCHECK[Kc](%d)", cmd[1]); break; + case ICLASS_CMD_READCHECK_KD:snprintf(exp,size, "READCHECK[Kd](%d)", cmd[1]); break; + case ICLASS_CMD_CHECK: snprintf(exp,size, "CHECK"); break; + case ICLASS_CMD_DETECT: snprintf(exp,size, "DETECT"); break; + case ICLASS_CMD_HALT: snprintf(exp,size, "HALT"); break; + case ICLASS_CMD_UPDATE: snprintf(exp,size, "UPDATE(%d)",cmd[1]); break; + case ICLASS_CMD_ACT: snprintf(exp,size, "ACT"); break; + case ICLASS_CMD_READ4: snprintf(exp,size, "READ4(%d)",cmd[1]); break; + default: snprintf(exp,size, "?"); break; } return; } @@ -901,6 +900,13 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } } + // adjust for different time scales + if (protocol == ICLASS || protocol == ISO_15693) { + first_timestamp *= 32; + timestamp *= 32; + duration *= 32; + } + //Check the CRC status uint8_t crcStatus = 2; @@ -940,6 +946,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui uint8_t parityBits = parityBytes[j>>3]; if (protocol != ISO_14443B && protocol != ISO_15693 + && protocol != ICLASS && protocol != ISO_7816_4 && (isResponse || protocol == ISO_14443A) && (oddparity8(frame[j]) != ((parityBits >> (7-(j&0x0007))) & 0x01))) { @@ -950,8 +957,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } if (markCRCBytes) { - if(crcStatus == 0 || crcStatus == 1) - {//CRC-command + if (crcStatus == 0 || crcStatus == 1) { //CRC-command char *pos1 = line[(data_len-2)/16]+(((data_len-2) % 16) * 4); (*pos1) = '['; char *pos2 = line[(data_len)/16]+(((data_len) % 16) * 4); @@ -978,8 +984,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui if (protocol == PROTO_MIFARE) annotateMifare(explanation, sizeof(explanation), frame, data_len, parityBytes, parity_len, isResponse); - if(!isResponse) - { + if (!isResponse) { switch(protocol) { case ICLASS: annotateIclass(explanation,sizeof(explanation),frame,data_len); break; case ISO_14443A: annotateIso14443a(explanation,sizeof(explanation),frame,data_len); break; @@ -1027,6 +1032,11 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui if (showWaitCycles && !isResponse && next_record_is_response(tracepos, trace)) { uint32_t next_timestamp = *((uint32_t *)(trace + tracepos)); + // adjust for different time scales + if (protocol == ICLASS || protocol == ISO_15693) { + next_timestamp *= 32; + } + PrintAndLog(" %10d | %10d | %s | fdt (Frame Delay Time): %d", (EndOfTransmissionTimestamp - first_timestamp), (next_timestamp - first_timestamp), diff --git a/common/iso15693tools.h b/common/iso15693tools.h index d9f59cc6..ceb6393e 100644 --- a/common/iso15693tools.h +++ b/common/iso15693tools.h @@ -1,8 +1,8 @@ // ISO15693 commons // Adrian Dabrowski 2010 and others, GPLv2 -#ifndef ISO15693_H__ -#define ISO15693_H__ +#ifndef ISO15693TOOLS_H__ +#define ISO15693TOOLS_H__ // ISO15693 CRC #define ISO15693_CRC_PRESET (uint16_t)0xFFFF @@ -11,7 +11,7 @@ uint16_t Iso15693Crc(uint8_t *v, int n); int Iso15693AddCrc(uint8_t *req, int n); -char* Iso15693sprintUID(char *target,uint8_t *uid); +char* Iso15693sprintUID(char *target, uint8_t *uid); unsigned short iclass_crc16(char *data_p, unsigned short length); #endif From a66f26da182040ac798a7c629d255cb86803e9c2 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 10 Sep 2019 18:18:54 +0200 Subject: [PATCH 3/8] fix 'hf iclass sim': * add simulation of block 3 and 4 reads * add simulation of READ4 (4 blocks read) * fixing TransmitTo15693Reader() (again) * FPGA change (hi_simulate.v): avoid spp_clk phase changes * some whitespace fixes --- armsrc/iclass.c | 85 +++++++++++++----- armsrc/iso15693.c | 216 ++++++++++++++++++++++----------------------- armsrc/iso15693.h | 2 +- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_simulate.v | 54 ++++++------ 5 files changed, 198 insertions(+), 159 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index daa44256..66238a10 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -860,6 +860,12 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t *resp_cc = BigBuf_malloc(18); int resp_cc_len; + // Kd, Kc (blocks 3 and 4). Cannot be read. Always respond with 0xff bytes only + uint8_t *resp_ff = BigBuf_malloc(22); + int resp_ff_len; + uint8_t ff_data[10] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00}; + AppendCrc(ff_data, 8); + // Application Issuer Area (block 5) uint8_t *resp_aia = BigBuf_malloc(22); int resp_aia_len; @@ -897,14 +903,19 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { memcpy(resp_cc, ToSend, ToSendMax); resp_cc_len = ToSendMax; + // Kd, Kc (blocks 3 and 4) + CodeIso15693AsTag(ff_data, sizeof(ff_data)); + memcpy(resp_ff, ToSend, ToSendMax); + resp_ff_len = ToSendMax; + // Application Issuer Area (block 5) CodeIso15693AsTag(aia_data, sizeof(aia_data)); memcpy(resp_aia, ToSend, ToSendMax); resp_aia_len = ToSendMax; //This is used for responding to READ-block commands or other data which is dynamically generated - uint8_t *data_generic_trace = BigBuf_malloc(8 + 2); // 8 bytes data + 2byte CRC is max tag answer - uint8_t *data_response = BigBuf_malloc( (8 + 2) * 2 + 2); + uint8_t *data_generic_trace = BigBuf_malloc(32 + 2); // 32 bytes data + 2byte CRC is max tag answer + uint8_t *data_response = BigBuf_malloc( (32 + 2) * 2 + 2); LED_A_ON(); bool buttonPressed = false; @@ -921,7 +932,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { buttonPressed = true; break; } - + //Signal tracer LED_C_ON(); @@ -931,6 +942,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { modulated_response_size = 0; trace_data = NULL; trace_data_size = 0; + if (receivedCmd[0] == ICLASS_CMD_ACTALL) { // Reader in anticollission phase modulated_response = resp_sof; @@ -944,12 +956,11 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { modulated_response_size = resp_anticoll_len; trace_data = anticoll_data; trace_data_size = sizeof(anticoll_data); - //DbpString("Reader requests anticollission CSN:"); - + } else if (receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY && len == 4) { // read block uint16_t blockNo = receivedCmd[1]; - if (simulationMode != ICLASS_SIM_MODE_FULL) { - // provide defaults for blocks 0, 1, 2, 5 + if (simulationMode == ICLASS_SIM_MODE_EXIT_AFTER_MAC) { + // provide defaults for blocks 0 ... 5 switch (blockNo) { case 0: // csn (block 00) modulated_response = resp_csn; @@ -973,6 +984,13 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { memcpy(reader_mac_buf, card_challenge_data, 8); } break; + case 3: + case 4: // Kd, Kd, always respond with 0xff bytes + modulated_response = resp_ff; + modulated_response_size = resp_ff_len; + trace_data = ff_data; + trace_data_size = sizeof(ff_data); + break; case 5: // Application Issuer Area (block 05) modulated_response = resp_aia; modulated_response_size = resp_aia_len; @@ -981,15 +999,22 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { break; // default: don't respond } - } else { // use data from emulator memory - memcpy(data_generic_trace, emulator + (receivedCmd[1] << 3), 8); - AppendCrc(data_generic_trace, 8); - trace_data = data_generic_trace; - trace_data_size = 10; - CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); - modulated_response = data_response; - modulated_response_size = ToSendMax; + } else if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (blockNo == 3 || blockNo == 4) { // Kd, Kc, always respond with 0xff bytes + modulated_response = resp_ff; + modulated_response_size = resp_ff_len; + trace_data = ff_data; + trace_data_size = sizeof(ff_data); + } else { // use data from emulator memory + memcpy(data_generic_trace, emulator + (receivedCmd[1] << 3), 8); + AppendCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } } } else if (receivedCmd[0] == ICLASS_CMD_SELECT) { @@ -1039,6 +1064,18 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = NULL; trace_data_size = 0; + } else if (simulationMode == ICLASS_SIM_MODE_FULL && receivedCmd[0] == ICLASS_CMD_READ4 && len == 4) { // 0x06 + //Read block + //Take the data... + memcpy(data_generic_trace, emulator + (receivedCmd[1] << 3), 8 * 4); + AppendCrc(data_generic_trace, 8 * 4); + trace_data = data_generic_trace; + trace_data_size = 8 * 4 + 2; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } else if (receivedCmd[0] == ICLASS_CMD_UPDATE && simulationMode == ICLASS_SIM_MODE_FULL) { // Probably the reader wants to update the nonce. Let's just ignore that for now. // OBS! If this is implemented, don't forget to regenerate the cipher_state @@ -1072,7 +1109,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } /** - A legit tag has about 330us delay between reader EOT and tag SOF. + A legit tag has about 311,5us delay between reader EOT and tag SOF. **/ if (modulated_response_size > 0) { uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM - DELAY_ARM_TO_READER_SIM; @@ -1112,7 +1149,7 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain // setup hardware for simulation: FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); StartCountSspClk(); @@ -1150,8 +1187,8 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain datain[i*8+0], datain[i*8+1], datain[i*8+2], datain[i*8+3], datain[i*8+4], datain[i*8+5], datain[i*8+6], datain[i*8+7]); Dbprintf("NR,MAC: %02x %02x %02x %02x %02x %02x %02x %02x", - datain[i*8+ 8], datain[i*8+ 9], datain[i*8+10], datain[i*8+11], - datain[i*8+12], datain[i*8+13], datain[i*8+14], datain[i*8+15]); + datain[i*8+ 8], datain[i*8+ 9], datain[i*8+10], datain[i*8+11], + datain[i*8+12], datain[i*8+13], datain[i*8+14], datain[i*8+15]); } cmd_send(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i, 0, mac_responses, i*16); } else if (simType == ICLASS_SIM_MODE_FULL) { @@ -1319,10 +1356,10 @@ static int GetIClassAnswer(uint8_t *receivedResponse, int maxLen, int *samples, if (elapsed) (*elapsed)++; } if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { - if (c < timeout) { - c++; - } else { - return false; + if (c < timeout) { + c++; + } else { + return false; } b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; skip = !skip; diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 7fdf2a35..4b4577e7 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -2,7 +2,7 @@ // Jonathan Westhues, split Nov 2006 // Modified by Greg Jones, Jan 2009 // Modified by Adrian Dabrowski "atrox", Mar-Sept 2010,Oct 2011 -// Modified by piwi, Oct 2018 +// Modified by piwi, Oct 2018 // // This code is licensed to you under the terms of the GNU GPL, version 2 or, // at your option, any later version. See the LICENSE.txt file for the text of @@ -16,26 +16,26 @@ // transmission modes from tag to reader. As of Oct 2018 this code supports // both reader modes and the high speed variant with one subcarrier from card to reader. // As long as the card fully support ISO 15693 this is no problem, since the -// reader chooses both data rates, but some non-standard tags do not. +// reader chooses both data rates, but some non-standard tags do not. // For card simulation, the code supports both high and low speed modes with one subcarrier. // // VCD (reader) -> VICC (tag) // 1 out of 256: -// data rate: 1,66 kbit/s (fc/8192) -// used for long range +// data rate: 1,66 kbit/s (fc/8192) +// used for long range // 1 out of 4: -// data rate: 26,48 kbit/s (fc/512) -// used for short range, high speed +// data rate: 26,48 kbit/s (fc/512) +// used for short range, high speed // // VICC (tag) -> VCD (reader) // Modulation: -// ASK / one subcarrier (423,75 khz) -// FSK / two subcarriers (423,75 khz && 484,28 khz) +// ASK / one subcarrier (423,75 khz) +// FSK / two subcarriers (423,75 khz && 484,28 khz) // Data Rates / Modes: -// low ASK: 6,62 kbit/s -// low FSK: 6.67 kbit/s -// high ASK: 26,48 kbit/s -// high FSK: 26,69 kbit/s +// low ASK: 6,62 kbit/s +// low FSK: 6.67 kbit/s +// high ASK: 26,48 kbit/s +// high FSK: 26,69 kbit/s //----------------------------------------------------------------------------- @@ -161,7 +161,7 @@ static void CodeIso15693AsReader(uint8_t *cmd, int n) for(i = 0; i < 4; i++) { ToSendStuffBit(1); } - + ToSendMax++; } @@ -338,20 +338,20 @@ void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t start_time, LED_C_ON(); uint8_t bits_to_shift = 0x00; uint8_t bits_to_send = 0x00; - for(size_t c = 0; c < len; c++) { + for(size_t c = 0; c < len; c++) { for (int i = 7; i >= 0; i--) { uint8_t cmd_bits = ((cmd[c] >> i) & 0x01) ? 0xff : 0x00; for (int j = 0; j < (slow?4:1); ) { - bits_to_send = bits_to_shift << (8 - shift_delay) | cmd_bits >> shift_delay; - bits_to_shift = cmd_bits; if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { + bits_to_send = bits_to_shift << (8 - shift_delay) | cmd_bits >> shift_delay; AT91C_BASE_SSC->SSC_THR = bits_to_send; + bits_to_shift = cmd_bits; j++; } } - } + } WDT_HIT(); - } + } // send the remaining bits, padded with 0: bits_to_send = bits_to_shift << (8 - shift_delay); for ( ; ; ) { @@ -408,7 +408,7 @@ typedef struct DecodeTag { static int inline __attribute__((always_inline)) Handle15693SamplesFromTag(uint16_t amplitude, DecodeTag_t *DecodeTag) { switch(DecodeTag->state) { - case STATE_TAG_SOF_LOW: + case STATE_TAG_SOF_LOW: // waiting for 12 times low (11 times low is accepted as well) if (amplitude < NOISE_THRESHOLD) { DecodeTag->posCount++; @@ -422,7 +422,7 @@ static int inline __attribute__((always_inline)) Handle15693SamplesFromTag(uint1 } } break; - + case STATE_TAG_SOF_HIGH: // waiting for 10 times high. Take average over the last 8 if (amplitude > NOISE_THRESHOLD) { @@ -592,7 +592,7 @@ static int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, int tim bool gotFrame = false; uint16_t *dmaBuf = (uint16_t*)BigBuf_malloc(ISO15693_DMA_BUFFER_SIZE*sizeof(uint16_t)); - + // the Decoder data structure DecodeTag_t DecodeTag = { 0 }; DecodeTagInit(&DecodeTag, response, max_len); @@ -643,9 +643,9 @@ static int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, int tim FpgaDisableSscDma(); BigBuf_free(); - + if (DEBUG) Dbprintf("samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", - samples, gotFrame, DecodeTag.state, DecodeTag.len, DecodeTag.bitCount, DecodeTag.posCount); + samples, gotFrame, DecodeTag.state, DecodeTag.len, DecodeTag.bitCount, DecodeTag.posCount); if (DecodeTag.len > 0) { LogTrace(DecodeTag.output, DecodeTag.len, 0, 0, NULL, false); @@ -687,7 +687,7 @@ typedef struct DecodeReader { int byteCount; int byteCountMax; int posCount; - int sum1, sum2; + int sum1, sum2; uint8_t *output; } DecodeReader_t; @@ -985,12 +985,12 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo } FpgaDisableSscDma(); - + if (DEBUG) Dbprintf("samples = %d, gotFrame = %d, Decoder: state = %d, len = %d, bitCount = %d, posCount = %d", - samples, gotFrame, DecodeReader.state, DecodeReader.byteCount, DecodeReader.bitCount, DecodeReader.posCount); + samples, gotFrame, DecodeReader.state, DecodeReader.byteCount, DecodeReader.bitCount, DecodeReader.posCount); if (DecodeReader.byteCount > 0) { - uint32_t sof_time = *eof_time + uint32_t sof_time = *eof_time - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128:2048) // time for byte transfers - 32 // time for SOF transfer - 16; // time for EOF transfer @@ -1101,7 +1101,7 @@ void SnoopIso15693(void) Dbprintf(" DMA: %i bytes", ISO15693_DMA_BUFFER_SIZE * sizeof(uint16_t)); } Dbprintf("Snoop started. Press PM3 Button to stop."); - + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER | FPGA_HF_READER_MODE_SNOOP_AMPLITUDE); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); @@ -1139,7 +1139,7 @@ void SnoopIso15693(void) } } samples++; - + if (!TagIsActive) { // no need to try decoding reader data if the tag is sending if (Handle15693SampleFromReader(snoopdata & 0x02, &DecodeReader)) { FpgaDisableSscDma(); @@ -1168,7 +1168,7 @@ void SnoopIso15693(void) ReaderIsActive = (DecodeReader.state >= STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF); } - if (!ReaderIsActive && ExpectTagAnswer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet + if (!ReaderIsActive && ExpectTagAnswer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet if (Handle15693SamplesFromTag(snoopdata >> 2, &DecodeTag)) { FpgaDisableSscDma(); //Use samples as a time measurement @@ -1187,7 +1187,7 @@ void SnoopIso15693(void) FpgaDisableSscDma(); BigBuf_free(); - + LEDsoff(); DbpString("Snoop statistics:"); @@ -1234,7 +1234,7 @@ static void BuildReadBlockRequest(uint8_t *uid, uint8_t blockNumber ) uint16_t crc; // If we set the Option_Flag in this request, the VICC will respond with the security status of the block // followed by the block data - cmd[0] = ISO15693_REQ_OPTION | ISO15693_REQ_ADDRESS | ISO15693_REQ_DATARATE_HIGH; + cmd[0] = ISO15693_REQ_OPTION | ISO15693_REQ_ADDRESS | ISO15693_REQ_DATARATE_HIGH; // READ BLOCK command code cmd[1] = ISO15693_READBLOCK; // UID may be optionally specified here @@ -1285,10 +1285,10 @@ static void BuildInventoryResponse(uint8_t *uid) } // Universal Method for sending to and recv bytes from a tag -// init ... should we initialize the reader? -// speed ... 0 low speed, 1 hi speed -// *recv will contain the tag's answer -// return: lenght of received data +// init ... should we initialize the reader? +// speed ... 0 low speed, 1 hi speed +// *recv will contain the tag's answer +// return: lenght of received data int SendDataTag(uint8_t *send, int sendlen, bool init, int speed, uint8_t *recv, uint16_t max_recv_len, uint32_t start_time) { LED_A_ON(); @@ -1405,7 +1405,7 @@ void ReaderIso15693(uint32_t parameter) LED_A_ON(); set_tracing(true); - + int answerLen = 0; uint8_t TagUID[8] = {0x00}; @@ -1418,8 +1418,8 @@ void ReaderIso15693(uint32_t parameter) FpgaSetupSsc(FPGA_MAJOR_MODE_HF_READER); // Start from off (no field generated) - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(200); // Give the tags time to energize LED_D_ON(); @@ -1434,7 +1434,7 @@ void ReaderIso15693(uint32_t parameter) // Now send the IDENTIFY command BuildIdentifyRequest(); TransmitTo15693Tag(ToSend, ToSendMax, 0); - + // Now wait for a response answerLen = GetIso15693AnswerFromTag(answer, sizeof(answer), DELAY_ISO15693_VCD_TO_VICC_READER * 2) ; uint32_t start_time = GetCountSspClk() + DELAY_ISO15693_VICC_TO_VCD_READER; @@ -1489,7 +1489,7 @@ void ReaderIso15693(uint32_t parameter) // for the time being, switch field off to protect rdv4.0 // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); LED_A_OFF(); @@ -1506,7 +1506,7 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); StartCountSspClk(); @@ -1531,7 +1531,7 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) Dbhexdump(cmd_len, cmd, false); } - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); } @@ -1545,12 +1545,12 @@ void BruteforceIso15693Afi(uint32_t speed) uint8_t data[6]; uint8_t recv[ISO15693_MAX_RESPONSE_LENGTH]; - + int datalen=0, recvlen=0; Iso15693InitReader(); StartCountSspClk(); - + // first without AFI // Tags should respond without AFI and with AFI=0 even when AFI is active @@ -1584,7 +1584,7 @@ void BruteforceIso15693Afi(uint32_t speed) } Dbprintf("AFI Bruteforcing done."); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); } @@ -1616,7 +1616,7 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint // for the time being, switch field off to protect rdv4.0 // note: this prevents using hf 15 cmd with s option - which isn't implemented yet anyway - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LED_D_OFF(); LED_A_OFF(); @@ -1630,76 +1630,76 @@ void DirectTag15693Command(uint32_t datalen, uint32_t speed, uint32_t recv, uint // Set the UID to the tag (based on Iceman work). void SetTag15693Uid(uint8_t *uid) { - uint8_t cmd[4][9] = {0x00}; + uint8_t cmd[4][9] = {0x00}; - uint16_t crc; + uint16_t crc; - int recvlen = 0; - uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; + int recvlen = 0; + uint8_t recvbuf[ISO15693_MAX_RESPONSE_LENGTH]; - LED_A_ON(); + LED_A_ON(); - // Command 1 : 02213E00000000 - cmd[0][0] = 0x02; - cmd[0][1] = 0x21; - cmd[0][2] = 0x3e; - cmd[0][3] = 0x00; - cmd[0][4] = 0x00; - cmd[0][5] = 0x00; - cmd[0][6] = 0x00; + // Command 1 : 02213E00000000 + cmd[0][0] = 0x02; + cmd[0][1] = 0x21; + cmd[0][2] = 0x3e; + cmd[0][3] = 0x00; + cmd[0][4] = 0x00; + cmd[0][5] = 0x00; + cmd[0][6] = 0x00; - // Command 2 : 02213F69960000 - cmd[1][0] = 0x02; - cmd[1][1] = 0x21; - cmd[1][2] = 0x3f; - cmd[1][3] = 0x69; - cmd[1][4] = 0x96; - cmd[1][5] = 0x00; - cmd[1][6] = 0x00; + // Command 2 : 02213F69960000 + cmd[1][0] = 0x02; + cmd[1][1] = 0x21; + cmd[1][2] = 0x3f; + cmd[1][3] = 0x69; + cmd[1][4] = 0x96; + cmd[1][5] = 0x00; + cmd[1][6] = 0x00; - // Command 3 : 022138u8u7u6u5 (where uX = uid byte X) - cmd[2][0] = 0x02; - cmd[2][1] = 0x21; - cmd[2][2] = 0x38; - cmd[2][3] = uid[7]; - cmd[2][4] = uid[6]; - cmd[2][5] = uid[5]; - cmd[2][6] = uid[4]; + // Command 3 : 022138u8u7u6u5 (where uX = uid byte X) + cmd[2][0] = 0x02; + cmd[2][1] = 0x21; + cmd[2][2] = 0x38; + cmd[2][3] = uid[7]; + cmd[2][4] = uid[6]; + cmd[2][5] = uid[5]; + cmd[2][6] = uid[4]; - // Command 4 : 022139u4u3u2u1 (where uX = uid byte X) - cmd[3][0] = 0x02; - cmd[3][1] = 0x21; - cmd[3][2] = 0x39; - cmd[3][3] = uid[3]; - cmd[3][4] = uid[2]; - cmd[3][5] = uid[1]; - cmd[3][6] = uid[0]; + // Command 4 : 022139u4u3u2u1 (where uX = uid byte X) + cmd[3][0] = 0x02; + cmd[3][1] = 0x21; + cmd[3][2] = 0x39; + cmd[3][3] = uid[3]; + cmd[3][4] = uid[2]; + cmd[3][5] = uid[1]; + cmd[3][6] = uid[0]; - for (int i=0; i<4; i++) { - // Add the CRC - crc = Iso15693Crc(cmd[i], 7); - cmd[i][7] = crc & 0xff; - cmd[i][8] = crc >> 8; + for (int i=0; i<4; i++) { + // Add the CRC + crc = Iso15693Crc(cmd[i], 7); + cmd[i][7] = crc & 0xff; + cmd[i][8] = crc >> 8; - if (DEBUG) { - Dbprintf("SEND:"); - Dbhexdump(sizeof(cmd[i]), cmd[i], false); - } + if (DEBUG) { + Dbprintf("SEND:"); + Dbhexdump(sizeof(cmd[i]), cmd[i], false); + } - recvlen = SendDataTag(cmd[i], sizeof(cmd[i]), true, 1, recvbuf, sizeof(recvbuf), 0); + recvlen = SendDataTag(cmd[i], sizeof(cmd[i]), true, 1, recvbuf, sizeof(recvbuf), 0); - if (DEBUG) { - Dbprintf("RECV:"); - Dbhexdump(recvlen, recvbuf, false); - DbdecodeIso15693Answer(recvlen, recvbuf); - } + if (DEBUG) { + Dbprintf("RECV:"); + Dbhexdump(recvlen, recvbuf, false); + DbdecodeIso15693Answer(recvlen, recvbuf); + } - cmd_send(CMD_ACK, recvlen>ISO15693_MAX_RESPONSE_LENGTH?ISO15693_MAX_RESPONSE_LENGTH:recvlen, 0, 0, recvbuf, ISO15693_MAX_RESPONSE_LENGTH); - } - - LED_D_OFF(); + cmd_send(CMD_ACK, recvlen>ISO15693_MAX_RESPONSE_LENGTH?ISO15693_MAX_RESPONSE_LENGTH:recvlen, 0, 0, recvbuf, ISO15693_MAX_RESPONSE_LENGTH); + } - LED_A_OFF(); + LED_D_OFF(); + + LED_A_OFF(); } @@ -1801,8 +1801,8 @@ static void __attribute__((unused)) BuildArbitraryRequest(uint8_t *uid,uint8_t C cmd[10] = 0x00; cmd[11] = 0x0a; -// cmd[12] = 0x00; -// cmd[13] = 0x00; //Now the CRC +// cmd[12] = 0x00; +// cmd[13] = 0x00; //Now the CRC crc = Iso15693Crc(cmd, 12); // the crc needs to be calculated over 2 bytes cmd[12] = crc & 0xff; cmd[13] = crc >> 8; @@ -1836,8 +1836,8 @@ static void __attribute__((unused)) BuildArbitraryCustomRequest(uint8_t uid[], u cmd[10] = 0x05; // for custom codes this must be manufacturer code cmd[11] = 0x00; -// cmd[12] = 0x00; -// cmd[13] = 0x00; //Now the CRC +// cmd[12] = 0x00; +// cmd[13] = 0x00; //Now the CRC crc = Iso15693Crc(cmd, 12); // the crc needs to be calculated over 2 bytes cmd[12] = crc & 0xff; cmd[13] = crc >> 8; diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index f6cf29f1..7964d79e 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -18,7 +18,7 @@ // Delays in SSP_CLK ticks. // SSP_CLK runs at 13,56MHz / 32 = 423.75kHz when simulating a tag #define DELAY_READER_TO_ARM_SIM 8 -#define DELAY_ARM_TO_READER_SIM 1 +#define DELAY_ARM_TO_READER_SIM 0 #define DELAY_ISO15693_VCD_TO_VICC_SIM 132 // 132/423.75kHz = 311.5us from end of command EOF to start of tag response //SSP_CLK runs at 13.56MHz / 4 = 3,39MHz when acting as reader #define DELAY_ISO15693_VCD_TO_VICC_READER 1056 // 1056/3,39MHz = 311.5us from end of command EOF to start of tag response diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 665f7bcbe9df8553e37ddf040bf7406f7b59798e..70c7909aeb328809bb41a63c389b1db42501aceb 100644 GIT binary patch literal 42175 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 OIriLoM!!1$Z~qrf_Yi^r literal 42175 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 diff --git a/fpga/hi_simulate.v b/fpga/hi_simulate.v index de58a74e..92ebcb51 100644 --- a/fpga/hi_simulate.v +++ b/fpga/hi_simulate.v @@ -47,46 +47,48 @@ end // Divide 13.56 MHz to produce various frequencies for SSP_CLK // and modulation. -reg [7:0] ssp_clk_divider; +reg [8:0] ssp_clk_divider; -always @(posedge adc_clk) +always @(negedge adc_clk) ssp_clk_divider <= (ssp_clk_divider + 1); reg ssp_clk; always @(negedge adc_clk) begin - if(mod_type == `FPGA_HF_SIMULATOR_MODULATE_424K_8BIT) + if (mod_type == `FPGA_HF_SIMULATOR_MODULATE_424K_8BIT) // Get bit every at 53KHz (every 8th carrier bit of 424kHz) - ssp_clk <= ssp_clk_divider[7]; - else if(mod_type == `FPGA_HF_SIMULATOR_MODULATE_212K) + ssp_clk <= ~ssp_clk_divider[7]; + else if (mod_type == `FPGA_HF_SIMULATOR_MODULATE_212K) // Get next bit at 212kHz - ssp_clk <= ssp_clk_divider[5]; + ssp_clk <= ~ssp_clk_divider[5]; else // Get next bit at 424Khz - ssp_clk <= ssp_clk_divider[4]; + ssp_clk <= ~ssp_clk_divider[4]; end -// Divide SSP_CLK by 8 to produce the byte framing signal; the phase of -// this is arbitrary, because it's just a bitstream. -// One nasty issue, though: I can't make it work with both rx and tx at -// once. The phase wrt ssp_clk must be changed. TODO to find out why -// that is and make a better fix. -reg [2:0] ssp_frame_divider_to_arm; -always @(posedge ssp_clk) - ssp_frame_divider_to_arm <= (ssp_frame_divider_to_arm + 1); -reg [2:0] ssp_frame_divider_from_arm; -always @(negedge ssp_clk) - ssp_frame_divider_from_arm <= (ssp_frame_divider_from_arm + 1); - - +// Produce the byte framing signal; the phase of this signal +// is arbitrary, because it's just a bit stream in this module. reg ssp_frame; -always @(ssp_frame_divider_to_arm or ssp_frame_divider_from_arm or mod_type) - if(mod_type == `FPGA_HF_SIMULATOR_NO_MODULATION) // not modulating, so listening, to ARM - ssp_frame = (ssp_frame_divider_to_arm == 3'b000); - else - ssp_frame = (ssp_frame_divider_from_arm == 3'b000); +always @(negedge adc_clk) +begin + if (mod_type == `FPGA_HF_SIMULATOR_MODULATE_212K) + begin + if (ssp_clk_divider[8:5] == 4'd1) + ssp_frame <= 1'b1; + if (ssp_clk_divider[8:5] == 4'd5) + ssp_frame <= 1'b0; + end + else + begin + if (ssp_clk_divider[7:4] == 4'd1) + ssp_frame <= 1'b1; + if (ssp_clk_divider[7:4] == 4'd5) + ssp_frame <= 1'b0; + end +end + // Synchronize up the after-hysteresis signal, to produce DIN. reg ssp_din; @@ -120,6 +122,6 @@ assign pwr_lo = 1'b0; assign pwr_oe2 = 1'b0; -assign dbg = ssp_din; +assign dbg = ssp_frame; endmodule From 5b12974a7f01a94c40552402b66b01bf8ec0e214 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sat, 21 Sep 2019 11:58:51 +0200 Subject: [PATCH 4/8] fix 'hf iclass sim': * chg to reader command decoder in iso15693.c (require no modulation before SOF) * add 'has_been_low_for' logic to hi_simulate.v (same as in other FPGA modes, default to "no modulation") * add simulation of chip status (IDLE, ACTIVE, SELECTED, HALTED) * check ACSN on SELECT * add simulation of RESELECT * always check length of reader commands * fix printing of NR, MAC in sim 2 mode * fix response length to CHECK command --- armsrc/iclass.c | 290 ++++++++++++++++++++++++------------------- armsrc/iso15693.c | 25 ++-- client/cmdhficlass.c | 56 ++++++--- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_simulate.v | 24 +++- 5 files changed, 238 insertions(+), 157 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 66238a10..23701540 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -777,8 +777,6 @@ static void AppendCrc(uint8_t *data, int len) { /** * @brief Does the actual simulation - * @param csn - csn to use - * @param breakAfterMacReceived if true, returns after reader MAC has been received. */ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { @@ -919,6 +917,8 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { LED_A_ON(); bool buttonPressed = false; + enum { IDLE, ACTIVATED, SELECTED, HALTED } chip_state = IDLE; + while (!exitLoop) { WDT_HIT(); LED_B_OFF(); @@ -943,162 +943,193 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = NULL; trace_data_size = 0; - if (receivedCmd[0] == ICLASS_CMD_ACTALL) { - // Reader in anticollission phase - modulated_response = resp_sof; - modulated_response_size = resp_sof_Len; - trace_data = sof_data; - trace_data_size = sizeof(sof_data); + if (receivedCmd[0] == ICLASS_CMD_ACTALL && len == 1) { + // Reader in anticollision phase + if (chip_state != HALTED) { + modulated_response = resp_sof; + modulated_response_size = resp_sof_Len; + trace_data = sof_data; + trace_data_size = sizeof(sof_data); + chip_state = ACTIVATED; + } } else if (receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY && len == 1) { // identify - // Reader asks for anticollission CSN - modulated_response = resp_anticoll; - modulated_response_size = resp_anticoll_len; - trace_data = anticoll_data; - trace_data_size = sizeof(anticoll_data); + // Reader asks for anticollision CSN + if (chip_state == SELECTED || chip_state == ACTIVATED) { + modulated_response = resp_anticoll; + modulated_response_size = resp_anticoll_len; + trace_data = anticoll_data; + trace_data_size = sizeof(anticoll_data); + } + + } else if (receivedCmd[0] == ICLASS_CMD_SELECT && len == 9) { + // Reader selects anticollision CSN. + // Tag sends the corresponding real CSN + if (chip_state == ACTIVATED || chip_state == SELECTED) { + if (!memcmp(receivedCmd+1, anticoll_data, 8)) { + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + chip_state = SELECTED; + } else { + chip_state = IDLE; + } + } else if (chip_state == HALTED) { + // RESELECT with CSN + if (!memcmp(receivedCmd+1, csn_data, 8)) { + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + chip_state = SELECTED; + } + } } else if (receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY && len == 4) { // read block uint16_t blockNo = receivedCmd[1]; - if (simulationMode == ICLASS_SIM_MODE_EXIT_AFTER_MAC) { - // provide defaults for blocks 0 ... 5 - switch (blockNo) { - case 0: // csn (block 00) - modulated_response = resp_csn; - modulated_response_size = resp_csn_len; - trace_data = csn_data; - trace_data_size = sizeof(csn_data); - break; - case 1: // configuration (block 01) - modulated_response = resp_conf; - modulated_response_size = resp_conf_len; - trace_data = conf_data; - trace_data_size = sizeof(conf_data); - break; - case 2: // e-purse (block 02) - modulated_response = resp_cc; - modulated_response_size = resp_cc_len; - trace_data = card_challenge_data; - trace_data_size = sizeof(card_challenge_data); - // set epurse of sim2,4 attack - if (reader_mac_buf != NULL) { - memcpy(reader_mac_buf, card_challenge_data, 8); - } - break; - case 3: - case 4: // Kd, Kd, always respond with 0xff bytes + if (chip_state == SELECTED) { + if (simulationMode == ICLASS_SIM_MODE_EXIT_AFTER_MAC) { + // provide defaults for blocks 0 ... 5 + switch (blockNo) { + case 0: // csn (block 00) + modulated_response = resp_csn; + modulated_response_size = resp_csn_len; + trace_data = csn_data; + trace_data_size = sizeof(csn_data); + break; + case 1: // configuration (block 01) + modulated_response = resp_conf; + modulated_response_size = resp_conf_len; + trace_data = conf_data; + trace_data_size = sizeof(conf_data); + break; + case 2: // e-purse (block 02) + modulated_response = resp_cc; + modulated_response_size = resp_cc_len; + trace_data = card_challenge_data; + trace_data_size = sizeof(card_challenge_data); + // set epurse of sim2,4 attack + if (reader_mac_buf != NULL) { + memcpy(reader_mac_buf, card_challenge_data, 8); + } + break; + case 3: + case 4: // Kd, Kc, always respond with 0xff bytes + modulated_response = resp_ff; + modulated_response_size = resp_ff_len; + trace_data = ff_data; + trace_data_size = sizeof(ff_data); + break; + case 5: // Application Issuer Area (block 05) + modulated_response = resp_aia; + modulated_response_size = resp_aia_len; + trace_data = aia_data; + trace_data_size = sizeof(aia_data); + break; + // default: don't respond + } + } else if (simulationMode == ICLASS_SIM_MODE_FULL) { + if (blockNo == 3 || blockNo == 4) { // Kd, Kc, always respond with 0xff bytes modulated_response = resp_ff; modulated_response_size = resp_ff_len; trace_data = ff_data; trace_data_size = sizeof(ff_data); - break; - case 5: // Application Issuer Area (block 05) - modulated_response = resp_aia; - modulated_response_size = resp_aia_len; - trace_data = aia_data; - trace_data_size = sizeof(aia_data); - break; - // default: don't respond + } else { // use data from emulator memory + memcpy(data_generic_trace, emulator + 8*blockNo, 8); + AppendCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } } - } else if (simulationMode == ICLASS_SIM_MODE_FULL) { - if (blockNo == 3 || blockNo == 4) { // Kd, Kc, always respond with 0xff bytes - modulated_response = resp_ff; - modulated_response_size = resp_ff_len; - trace_data = ff_data; - trace_data_size = sizeof(ff_data); - } else { // use data from emulator memory - memcpy(data_generic_trace, emulator + (receivedCmd[1] << 3), 8); - AppendCrc(data_generic_trace, 8); + } + + } else if ((receivedCmd[0] == ICLASS_CMD_READCHECK_KD + || receivedCmd[0] == ICLASS_CMD_READCHECK_KC) && len == 2) { + // Read e-purse (88 02 || 18 02) + if (chip_state == SELECTED) { + modulated_response = resp_cc; + modulated_response_size = resp_cc_len; + trace_data = card_challenge_data; + trace_data_size = sizeof(card_challenge_data); + LED_B_ON(); + } + + } else if (receivedCmd[0] == ICLASS_CMD_CHECK && len == 9) { + // Reader random and reader MAC!!! + if (chip_state == SELECTED) { + if (simulationMode == ICLASS_SIM_MODE_FULL) { + //NR, from reader, is in receivedCmd+1 + opt_doTagMAC_2(cipher_state, receivedCmd+1, data_generic_trace, diversified_key); trace_data = data_generic_trace; - trace_data_size = 10; + trace_data_size = 4; CodeIso15693AsTag(trace_data, trace_data_size); memcpy(data_response, ToSend, ToSendMax); modulated_response = data_response; modulated_response_size = ToSendMax; - } - } - - } else if (receivedCmd[0] == ICLASS_CMD_SELECT) { - // Reader selects anticollission CSN. - // Tag sends the corresponding real CSN - modulated_response = resp_csn; - modulated_response_size = resp_csn_len; - trace_data = csn_data; - trace_data_size = sizeof(csn_data); - - } else if (receivedCmd[0] == ICLASS_CMD_READCHECK_KD - || receivedCmd[0] == ICLASS_CMD_READCHECK_KC) { - // Read e-purse (88 02 || 18 02) - modulated_response = resp_cc; - modulated_response_size = resp_cc_len; - trace_data = card_challenge_data; - trace_data_size = sizeof(card_challenge_data); - LED_B_ON(); - - } else if (receivedCmd[0] == ICLASS_CMD_CHECK) { - // Reader random and reader MAC!!! - if (simulationMode == ICLASS_SIM_MODE_FULL) { - //NR, from reader, is in receivedCmd+1 - opt_doTagMAC_2(cipher_state, receivedCmd+1, data_generic_trace, diversified_key); - trace_data = data_generic_trace; - trace_data_size = 4; - CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); - modulated_response = data_response; - modulated_response_size = ToSendMax; - //exitLoop = true; - } else { // Not fullsim, we don't respond - // We do not know what to answer, so lets keep quiet - if (simulationMode == ICLASS_SIM_MODE_EXIT_AFTER_MAC) { - if (reader_mac_buf != NULL) { - // save NR and MAC for sim 2,4 - memcpy(reader_mac_buf + 8, receivedCmd + 1, 8); + //exitLoop = true; + } else { // Not fullsim, we don't respond + // We do not know what to answer, so lets keep quiet + if (simulationMode == ICLASS_SIM_MODE_EXIT_AFTER_MAC) { + if (reader_mac_buf != NULL) { + // save NR and MAC for sim 2,4 + memcpy(reader_mac_buf + 8, receivedCmd + 1, 8); + } + exitLoop = true; } - exitLoop = true; } } } else if (receivedCmd[0] == ICLASS_CMD_HALT && len == 1) { - // Reader ends the session - modulated_response = resp_sof; - modulated_response_size = 0; - trace_data = NULL; - trace_data_size = 0; + if (chip_state == SELECTED) { + // Reader ends the session + chip_state = HALTED; + } } else if (simulationMode == ICLASS_SIM_MODE_FULL && receivedCmd[0] == ICLASS_CMD_READ4 && len == 4) { // 0x06 - //Read block - //Take the data... - memcpy(data_generic_trace, emulator + (receivedCmd[1] << 3), 8 * 4); - AppendCrc(data_generic_trace, 8 * 4); - trace_data = data_generic_trace; - trace_data_size = 8 * 4 + 2; - CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); - modulated_response = data_response; - modulated_response_size = ToSendMax; + //Read 4 blocks + if (chip_state == SELECTED) { + memcpy(data_generic_trace, emulator + (receivedCmd[1] << 3), 8 * 4); + AppendCrc(data_generic_trace, 8 * 4); + trace_data = data_generic_trace; + trace_data_size = 8 * 4 + 2; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } - } else if (receivedCmd[0] == ICLASS_CMD_UPDATE && simulationMode == ICLASS_SIM_MODE_FULL) { + } else if (receivedCmd[0] == ICLASS_CMD_UPDATE && (len == 12 || len == 14)) { // Probably the reader wants to update the nonce. Let's just ignore that for now. // OBS! If this is implemented, don't forget to regenerate the cipher_state // We're expected to respond with the data+crc, exactly what's already in the receivedCmd // receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b - memcpy(data_generic_trace, receivedCmd + 2, 8); - AppendCrc(data_generic_trace, 8); - trace_data = data_generic_trace; - trace_data_size = 10; - CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); - modulated_response = data_response; - modulated_response_size = ToSendMax; + if (chip_state == SELECTED) { + memcpy(data_generic_trace, receivedCmd + 2, 8); + AppendCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } - } else if (receivedCmd[0] == ICLASS_CMD_PAGESEL) { + } else if (receivedCmd[0] == ICLASS_CMD_PAGESEL && len == 4) { // Pagesel - // Pagesel enables to select a page in the selected chip memory and return its configuration block - // Chips with a single page will not answer to this command - // It appears we're fine ignoring this. - // Otherwise, we should answer 8bytes (block) + 2bytes CRC + if (chip_state == SELECTED) { + // Pagesel enables to select a page in the selected chip memory and return its configuration block + // Chips with a single page will not answer to this command + // It appears we're fine ignoring this. + // Otherwise, we should answer 8bytes (block) + 2bytes CRC + } } else { - // Never seen this command before + // don't know how to handle this command char debug_message[250]; // should be enough sprintf(debug_message, "Unhandled command (len = %d) received from reader:", len); for (int i = 0; i < len && strlen(debug_message) < sizeof(debug_message) - 3 - 1; i++) { @@ -1187,8 +1218,9 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain datain[i*8+0], datain[i*8+1], datain[i*8+2], datain[i*8+3], datain[i*8+4], datain[i*8+5], datain[i*8+6], datain[i*8+7]); Dbprintf("NR,MAC: %02x %02x %02x %02x %02x %02x %02x %02x", - datain[i*8+ 8], datain[i*8+ 9], datain[i*8+10], datain[i*8+11], - datain[i*8+12], datain[i*8+13], datain[i*8+14], datain[i*8+15]); + mac_responses[i*16+ 8], mac_responses[i*16+ 9], mac_responses[i*16+10], mac_responses[i*16+11], + mac_responses[i*16+12], mac_responses[i*16+13], mac_responses[i*16+14], mac_responses[i*16+15]); + SpinDelay(100); // give the reader some time to prepare for next CSN } cmd_send(CMD_ACK, CMD_SIMULATE_TAG_ICLASS, i, 0, mac_responses, i*16); } else if (simType == ICLASS_SIM_MODE_FULL) { diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index 4b4577e7..f33e0156 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -671,6 +671,7 @@ static int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, int tim typedef struct DecodeReader { enum { STATE_READER_UNSYNCD, + STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF, STATE_READER_AWAIT_1ST_RISING_EDGE_OF_SOF, STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF, STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF, @@ -714,6 +715,13 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin { switch (DecodeReader->state) { case STATE_READER_UNSYNCD: + // wait for unmodulated carrier + if (bit) { + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; + } + break; + + case STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF: if (!bit) { // we went low, so this could be the beginning of a SOF DecodeReader->posCount = 1; @@ -725,7 +733,7 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin DecodeReader->posCount++; if (bit) { // detected rising edge if (DecodeReader->posCount < 4) { // rising edge too early (nominally expected at 5) - DecodeReaderReset(DecodeReader); + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; } else { // SOF DecodeReader->state = STATE_READER_AWAIT_2ND_FALLING_EDGE_OF_SOF; } @@ -748,13 +756,13 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; } else if (DecodeReader->posCount < 28) { // falling edge too early (nominally expected at 29 latest) DecodeReaderReset(DecodeReader); - } else { // SOF for 1 out of 4 coding + } else { // SOF for 1 out of 256 coding DecodeReader->Coding = CODING_1_OUT_OF_256; DecodeReader->state = STATE_READER_AWAIT_2ND_RISING_EDGE_OF_SOF; } } else { if (DecodeReader->posCount > 29) { // stayed high for too long - DecodeReaderReset(DecodeReader); + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; } else { // do nothing, keep waiting } @@ -766,7 +774,7 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin if (bit) { // detected rising edge if (DecodeReader->Coding == CODING_1_OUT_OF_256) { if (DecodeReader->posCount < 32) { // rising edge too early (nominally expected at 33) - DecodeReaderReset(DecodeReader); + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; } else { DecodeReader->posCount = 1; DecodeReader->bitCount = 0; @@ -777,21 +785,22 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin } } else { // CODING_1_OUT_OF_4 if (DecodeReader->posCount < 24) { // rising edge too early (nominally expected at 25) - DecodeReaderReset(DecodeReader); + DecodeReader->state = STATE_READER_AWAIT_1ST_FALLING_EDGE_OF_SOF; } else { + DecodeReader->posCount = 1; DecodeReader->state = STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4; } } } else { if (DecodeReader->Coding == CODING_1_OUT_OF_256) { if (DecodeReader->posCount > 34) { // signal stayed low for too long - DecodeReaderReset(DecodeReader); + DecodeReaderReset(DecodeReader); } else { // do nothing, keep waiting } } else { // CODING_1_OUT_OF_4 if (DecodeReader->posCount > 26) { // signal stayed low for too long - DecodeReaderReset(DecodeReader); + DecodeReaderReset(DecodeReader); } else { // do nothing, keep waiting } @@ -802,7 +811,7 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin case STATE_READER_AWAIT_END_OF_SOF_1_OUT_OF_4: DecodeReader->posCount++; if (bit) { - if (DecodeReader->posCount == 33) { + if (DecodeReader->posCount == 9) { DecodeReader->posCount = 1; DecodeReader->bitCount = 0; DecodeReader->byteCount = 0; diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index ee0dd16e..48b62b17 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -100,7 +100,46 @@ int usage_hf_iclass_sim(void) { return 0; } +// the original malicious IDs from Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult, +// and Milosch Meriac. Dismantling iClass and iClass Elite. #define NUM_CSNS 15 +static uint8_t csns[8 * NUM_CSNS] = { + 0x00, 0x0B, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x04, 0x0E, 0x08, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x09, 0x0D, 0x05, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x0A, 0x0C, 0x06, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x0F, 0x0B, 0x03, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x08, 0x0A, 0x0C, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x0D, 0x09, 0x09, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x0E, 0x08, 0x0A, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x03, 0x07, 0x17, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x3C, 0x06, 0xE0, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x01, 0x05, 0x1D, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x02, 0x04, 0x1E, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x07, 0x03, 0x1B, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x00, 0x02, 0x24, 0xF7, 0xFF, 0x12, 0xE0, + 0x00, 0x05, 0x01, 0x21, 0xF7, 0xFF, 0x12, 0xE0 }; + + +// pre-defined 9 CSNs by iceman. +// only one csn depend on several others. +// six depends only on the first csn, (0,1, 0x45) + +// #define NUM_CSNS 9 +// static uint8_t csns[8 * NUM_CSNS] = { + // 0x01, 0x0A, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, + // 0x0C, 0x06, 0x0C, 0xFE, 0xF7, 0xFF, 0x12, 0xE0, + // 0x10, 0x97, 0x83, 0x7B, 0xF7, 0xFF, 0x12, 0xE0, + // 0x13, 0x97, 0x82, 0x7A, 0xF7, 0xFF, 0x12, 0xE0, + // 0x07, 0x0E, 0x0D, 0xF9, 0xF7, 0xFF, 0x12, 0xE0, + // 0x14, 0x96, 0x84, 0x76, 0xF7, 0xFF, 0x12, 0xE0, + // 0x17, 0x96, 0x85, 0x71, 0xF7, 0xFF, 0x12, 0xE0, + // 0xCE, 0xC5, 0x0F, 0x77, 0xF7, 0xFF, 0x12, 0xE0, + // 0xD2, 0x5A, 0x82, 0xF8, 0xF7, 0xFF, 0x12, 0xE0 + // //0x04, 0x08, 0x9F, 0x78, 0x6E, 0xFF, 0x12, 0xE0 +// }; + + int CmdHFiClassSim(const char *Cmd) { uint8_t simType = 0; uint8_t CSN[8] = {0, 0, 0, 0, 0, 0, 0, 0}; @@ -123,23 +162,6 @@ int CmdHFiClassSim(const char *Cmd) { UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType, NUM_CSNS}}; UsbCommand resp = {0}; - uint8_t csns[8 * NUM_CSNS] = { - 0x00, 0x0B, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x04, 0x0E, 0x08, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x09, 0x0D, 0x05, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x0A, 0x0C, 0x06, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x0F, 0x0B, 0x03, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x08, 0x0A, 0x0C, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x0D, 0x09, 0x09, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x0E, 0x08, 0x0A, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x03, 0x07, 0x17, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x3C, 0x06, 0xE0, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x01, 0x05, 0x1D, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x02, 0x04, 0x1E, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x07, 0x03, 0x1B, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x00, 0x02, 0x24, 0xF7, 0xFF, 0x12, 0xE0, - 0x00, 0x05, 0x01, 0x21, 0xF7, 0xFF, 0x12, 0xE0 }; - memcpy(c.d.asBytes, csns, 8 * NUM_CSNS); SendCommand(&c); diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 70c7909aeb328809bb41a63c389b1db42501aceb..1bd3c416fe728c26ee204ede9bc882c8f87486c8 100644 GIT binary patch literal 42175 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 OIriLoM!!1$Z~qrf_Yi^r diff --git a/fpga/hi_simulate.v b/fpga/hi_simulate.v index 92ebcb51..097b8a08 100644 --- a/fpga/hi_simulate.v +++ b/fpga/hi_simulate.v @@ -33,15 +33,33 @@ module hi_simulate( output dbg; input [2:0] mod_type; +assign adc_clk = ck_1356meg; // The comparator with hysteresis on the output from the peak detector. reg after_hysteresis; -assign adc_clk = ck_1356meg; +reg [11:0] has_been_low_for; always @(negedge adc_clk) begin - if(& adc_d[7:5]) after_hysteresis = 1'b1; // if (adc_d >= 224) - else if(~(| adc_d[7:5])) after_hysteresis = 1'b0; // if (adc_d <= 31) + if (& adc_d[7:5]) after_hysteresis <= 1'b1; // if (adc_d >= 224) + else if (~(| adc_d[7:5])) after_hysteresis <= 1'b0; // if (adc_d <= 31) + + if (adc_d >= 224) + begin + has_been_low_for <= 12'd0; + end + else + begin + if (has_been_low_for == 12'd4095) + begin + has_been_low_for <= 12'd0; + after_hysteresis <= 1'b1; + end + else + begin + has_been_low_for <= has_been_low_for + 1; + end + end end From e49d31c0e798d14d8b0607b953f06dceecb4a42e Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 25 Sep 2019 14:24:36 +0200 Subject: [PATCH 5/8] fix 'hf iclass sim': * ignore standard iso15693 INVENTORY commands silently * make iso15693 command decoder more strict (prevent decoding rubbish) * re-enable sim 3 --- armsrc/iclass.c | 3 +++ armsrc/iso15693.c | 16 ++++++---------- client/cmdhficlass.c | 7 +++---- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 23701540..2e3a4db8 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1128,6 +1128,9 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // Otherwise, we should answer 8bytes (block) + 2bytes CRC } + } else if (receivedCmd[0] == 0x26 && len == 5) { + // standard ISO15693 INVENTORY command. Ignore. + } else { // don't know how to handle this command char debug_message[250]; // should be enough diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index f33e0156..dbc1ca4c 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -827,6 +827,7 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin break; case STATE_READER_RECEIVE_DATA_1_OUT_OF_4: + bit = !!bit; DecodeReader->posCount++; if (DecodeReader->posCount == 1) { DecodeReader->sum1 = bit; @@ -839,17 +840,14 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin } if (DecodeReader->posCount == 8) { DecodeReader->posCount = 0; - int corr10 = DecodeReader->sum1 - DecodeReader->sum2; - int corr01 = DecodeReader->sum2 - DecodeReader->sum1; - int corr11 = (DecodeReader->sum1 + DecodeReader->sum2) / 2; - if (corr01 > corr11 && corr01 > corr10) { // EOF + if (DecodeReader->sum1 <= 1 && DecodeReader->sum2 >= 3) { // EOF LED_B_OFF(); // Finished receiving DecodeReaderReset(DecodeReader); if (DecodeReader->byteCount != 0) { return true; } } - if (corr10 > corr11) { // detected a 2bit position + if (DecodeReader->sum1 >= 3 && DecodeReader->sum2 <= 1) { // detected a 2bit position DecodeReader->shiftReg >>= 2; DecodeReader->shiftReg |= (DecodeReader->bitCount << 6); } @@ -869,6 +867,7 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin break; case STATE_READER_RECEIVE_DATA_1_OUT_OF_256: + bit = !!bit; DecodeReader->posCount++; if (DecodeReader->posCount == 1) { DecodeReader->sum1 = bit; @@ -881,17 +880,14 @@ static int inline __attribute__((always_inline)) Handle15693SampleFromReader(uin } if (DecodeReader->posCount == 8) { DecodeReader->posCount = 0; - int corr10 = DecodeReader->sum1 - DecodeReader->sum2; - int corr01 = DecodeReader->sum2 - DecodeReader->sum1; - int corr11 = (DecodeReader->sum1 + DecodeReader->sum2) / 2; - if (corr01 > corr11 && corr01 > corr10) { // EOF + if (DecodeReader->sum1 <= 1 && DecodeReader->sum2 >= 3) { // EOF LED_B_OFF(); // Finished receiving DecodeReaderReset(DecodeReader); if (DecodeReader->byteCount != 0) { return true; } } - if (corr10 > corr11) { // detected the bit position + if (DecodeReader->sum1 >= 3 && DecodeReader->sum2 <= 1) { // detected the bit position DecodeReader->shiftReg = DecodeReader->bitCount; } if (DecodeReader->bitCount == 255) { // we have a full byte diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 48b62b17..6f7cc4a4 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -144,7 +144,7 @@ int CmdHFiClassSim(const char *Cmd) { uint8_t simType = 0; uint8_t CSN[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - if (strlen(Cmd)<1) { + if (strlen(Cmd) < 1) { return usage_hf_iclass_sim(); } simType = param_get8ex(Cmd, 0, 0, 10); @@ -157,7 +157,6 @@ int CmdHFiClassSim(const char *Cmd) { PrintAndLog("--simtype:%02x csn:%s", simType, sprint_hex(CSN, 8)); } - uint8_t numberOfCSNs = 0; if (simType == ICLASS_SIM_MODE_READER_ATTACK) { UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType, NUM_CSNS}}; UsbCommand resp = {0}; @@ -196,8 +195,8 @@ int CmdHFiClassSim(const char *Cmd) { saveFile("iclass_mac_attack", "bin", dump,datalen); free(dump); - } else if (simType == ICLASS_SIM_MODE_CSN || simType == ICLASS_SIM_MODE_CSN_DEFAULT) { - UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType, numberOfCSNs}}; + } else if (simType == ICLASS_SIM_MODE_CSN || simType == ICLASS_SIM_MODE_CSN_DEFAULT || simType == ICLASS_SIM_MODE_FULL) { + UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType, 0}}; memcpy(c.d.asBytes, CSN, 8); SendCommand(&c); From 8ddb81a2175d69da00f1def3aec571d23e2563b9 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 25 Sep 2019 18:40:05 +0200 Subject: [PATCH 6/8] fix 'hf iclass sim': * implement CHECK[Kc] based on @sherhannn79 * implement UPDATE based on @sherhannn79 --- armsrc/iclass.c | 64 ++++++++++++++++++++++++++++++++++++---------- common/protocols.h | 3 ++- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 2e3a4db8..0b77a039 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -783,7 +783,8 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // free eventually allocated BigBuf memory BigBuf_free_keep_EM(); - State cipher_state; + State cipher_state_KC; + State cipher_state_KD; uint8_t *emulator = BigBuf_get_EM_addr(); uint8_t *csn = emulator; @@ -802,18 +803,20 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { AppendCrc(anticoll_data, 8); AppendCrc(csn_data, 8); - uint8_t diversified_key[8] = { 0 }; + uint8_t diversified_key_d[8] = { 0 }; + uint8_t diversified_key_c[8] = { 0 }; // e-Purse uint8_t card_challenge_data[8] = { 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; //uint8_t card_challenge_data[8] = { 0 }; if (simulationMode == ICLASS_SIM_MODE_FULL) { - // The diversified key should be stored on block 3 - // Get the diversified key from emulator memory - memcpy(diversified_key, emulator + (8 * 3), 8); + // Get the diversified keys from emulator memory + memcpy(diversified_key_d, emulator + (8 * 3), 8); + memcpy(diversified_key_c, emulator + (8 * 4), 8); // Card challenge, a.k.a e-purse is on block 2 memcpy(card_challenge_data, emulator + (8 * 2), 8); - // Precalculate the cipher state, feeding it the CC - cipher_state = opt_doTagMAC_1(card_challenge_data, diversified_key); + // Precalculate the cipher states, feeding it the CC + cipher_state_KD = opt_doTagMAC_1(card_challenge_data, diversified_key_d); + cipher_state_KC = opt_doTagMAC_1(card_challenge_data, diversified_key_c); } // save card challenge for sim2,4 attack if (reader_mac_buf != NULL) { @@ -1049,7 +1052,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } } else if ((receivedCmd[0] == ICLASS_CMD_READCHECK_KD - || receivedCmd[0] == ICLASS_CMD_READCHECK_KC) && len == 2) { + || receivedCmd[0] == ICLASS_CMD_READCHECK_KC) && receivedCmd[1] == 0x02 && len == 2) { // Read e-purse (88 02 || 18 02) if (chip_state == SELECTED) { modulated_response = resp_cc; @@ -1059,12 +1062,17 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { LED_B_ON(); } - } else if (receivedCmd[0] == ICLASS_CMD_CHECK && len == 9) { + } else if ((receivedCmd[0] == ICLASS_CMD_CHECK_KC + || receivedCmd[0] == ICLASS_CMD_CHECK_KD) && len == 9) { // Reader random and reader MAC!!! if (chip_state == SELECTED) { if (simulationMode == ICLASS_SIM_MODE_FULL) { //NR, from reader, is in receivedCmd+1 - opt_doTagMAC_2(cipher_state, receivedCmd+1, data_generic_trace, diversified_key); + if (receivedCmd[0] == ICLASS_CMD_CHECK_KC) { + opt_doTagMAC_2(cipher_state_KC, receivedCmd+1, data_generic_trace, diversified_key_c); + } else { + opt_doTagMAC_2(cipher_state_KD, receivedCmd+1, data_generic_trace, diversified_key_d); + } trace_data = data_generic_trace; trace_data_size = 4; CodeIso15693AsTag(trace_data, trace_data_size); @@ -1093,7 +1101,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (simulationMode == ICLASS_SIM_MODE_FULL && receivedCmd[0] == ICLASS_CMD_READ4 && len == 4) { // 0x06 //Read 4 blocks if (chip_state == SELECTED) { - memcpy(data_generic_trace, emulator + (receivedCmd[1] << 3), 8 * 4); + memcpy(data_generic_trace, emulator + receivedCmd[1]*8, 8 * 4); AppendCrc(data_generic_trace, 8 * 4); trace_data = data_generic_trace; trace_data_size = 8 * 4 + 2; @@ -1104,11 +1112,39 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } } else if (receivedCmd[0] == ICLASS_CMD_UPDATE && (len == 12 || len == 14)) { - // Probably the reader wants to update the nonce. Let's just ignore that for now. - // OBS! If this is implemented, don't forget to regenerate the cipher_state // We're expected to respond with the data+crc, exactly what's already in the receivedCmd // receivedCmd is now UPDATE 1b | ADDRESS 1b | DATA 8b | Signature 4b or CRC 2b if (chip_state == SELECTED) { + uint8_t blockNo = receivedCmd[1]; + if (blockNo == 2) { // update e-purse + memcpy(card_challenge_data, receivedCmd+2, 8); + CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); + memcpy(resp_cc, ToSend, ToSendMax); + resp_cc_len = ToSendMax; + cipher_state_KD = opt_doTagMAC_1(card_challenge_data, diversified_key_d); + cipher_state_KC = opt_doTagMAC_1(card_challenge_data, diversified_key_c); + if (simulationMode == ICLASS_SIM_MODE_FULL) { + memcpy(emulator + 8*2, card_challenge_data, 8); + } + } else if (blockNo == 3) { // update Kd + for (int i = 0; i < 8; i++){ + diversified_key_d[i] = diversified_key_d[i] ^ receivedCmd[2 + i]; + } + cipher_state_KD = opt_doTagMAC_1(card_challenge_data, diversified_key_d); + if (simulationMode == ICLASS_SIM_MODE_FULL) { + memcpy(emulator + 8*3, diversified_key_d, 8); + } + } else if (blockNo == 4) { // update Kc + for(int i = 0; i < 8; i++){ + diversified_key_c[i] = diversified_key_c[i] ^ receivedCmd[2 + i]; + } + cipher_state_KC = opt_doTagMAC_1(card_challenge_data, diversified_key_c); + if (simulationMode == ICLASS_SIM_MODE_FULL) { + memcpy(emulator + 8*4, diversified_key_c, 8); + } + } else if (simulationMode == ICLASS_SIM_MODE_FULL) { // update any other data block + memcpy(emulator + 8*blockNo, receivedCmd+2, 8); + } memcpy(data_generic_trace, receivedCmd + 2, 8); AppendCrc(data_generic_trace, 8); trace_data = data_generic_trace; @@ -1772,7 +1808,7 @@ void iClass_ReadCheck(uint8_t blockNo, uint8_t keyType) { } void iClass_Authentication(uint8_t *MAC) { - uint8_t check[] = { ICLASS_CMD_CHECK, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t check[] = { ICLASS_CMD_CHECK_KD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t resp[ICLASS_BUFFER_SIZE]; memcpy(check+5, MAC, 4); bool isOK; diff --git a/common/protocols.h b/common/protocols.h index 9de72661..703855f6 100644 --- a/common/protocols.h +++ b/common/protocols.h @@ -96,7 +96,8 @@ NXP/Philips CUSTOM COMMANDS #define ICLASS_CMD_PAGESEL 0x84 #define ICLASS_CMD_READCHECK_KD 0x88 #define ICLASS_CMD_READCHECK_KC 0x18 -#define ICLASS_CMD_CHECK 0x05 +#define ICLASS_CMD_CHECK_KC 0x95 +#define ICLASS_CMD_CHECK_KD 0x05 #define ICLASS_CMD_DETECT 0x0F #define ICLASS_CMD_HALT 0x00 #define ICLASS_CMD_UPDATE 0x87 From ae60ceca92b802fc980a883cb280d1c57b7932cf Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 1 Oct 2019 21:03:18 +0200 Subject: [PATCH 7/8] fix 'hf iclass sim' * add simulation of multiple pages (PAGESEL by @sherhannn9) * maintain cipher states per page * update cipher state after UPDATE commands (@sherhannn9) * add simulation of personalization mode * respond with SOF on HALT * display "" instead of "0f" in 'hf list iclass' * standard LED handling --- armsrc/iclass.c | 178 ++++++++++++++++++++++++++++----------------- client/cmdhflist.c | 39 +++++----- 2 files changed, 132 insertions(+), 85 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 0b77a039..d8f68c78 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -758,15 +758,8 @@ void rotateCSN(uint8_t* originalCSN, uint8_t* rotatedCSN) { // Encode SOF only static void CodeIClassTagSOF() { - //So far a dummy implementation, not used - //int lastProxToAirDuration =0; - ToSendReset(); - // Send SOF ToSend[++ToSendMax] = 0x1D; -// lastProxToAirDuration = 8*ToSendMax - 3*8;//Not counting zeroes in the beginning - - // Convert from last byte pos to length ToSendMax++; } @@ -783,16 +776,20 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // free eventually allocated BigBuf memory BigBuf_free_keep_EM(); - State cipher_state_KC; - State cipher_state_KD; + uint16_t page_size = 32 * 8; + uint8_t current_page = 0; + // maintain cipher states for both credit and debit key for each page + State cipher_state_KC[8]; + State cipher_state_KD[8]; + State *cipher_state = &cipher_state_KD[0]; + uint8_t *emulator = BigBuf_get_EM_addr(); uint8_t *csn = emulator; - uint8_t sof_data[] = { 0x0F } ; // CSN followed by two CRC bytes - uint8_t anticoll_data[10] = { 0 }; - uint8_t csn_data[10] = { 0 }; + uint8_t anticoll_data[10]; + uint8_t csn_data[10]; memcpy(csn_data, csn, sizeof(csn_data)); Dbprintf("Simulating CSN %02x%02x%02x%02x%02x%02x%02x%02x", csn[0], csn[1], csn[2], csn[3], csn[4], csn[5], csn[6], csn[7]); @@ -803,26 +800,56 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { AppendCrc(anticoll_data, 8); AppendCrc(csn_data, 8); - uint8_t diversified_key_d[8] = { 0 }; - uint8_t diversified_key_c[8] = { 0 }; + uint8_t diversified_key_d[8]; + uint8_t diversified_key_c[8]; + uint8_t *diversified_key = diversified_key_d; + + // configuration block + uint8_t conf_block[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00}; + AppendCrc(conf_block, 8); + // e-Purse uint8_t card_challenge_data[8] = { 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - //uint8_t card_challenge_data[8] = { 0 }; + if (simulationMode == ICLASS_SIM_MODE_FULL) { - // Get the diversified keys from emulator memory - memcpy(diversified_key_d, emulator + (8 * 3), 8); - memcpy(diversified_key_c, emulator + (8 * 4), 8); - // Card challenge, a.k.a e-purse is on block 2 - memcpy(card_challenge_data, emulator + (8 * 2), 8); - // Precalculate the cipher states, feeding it the CC - cipher_state_KD = opt_doTagMAC_1(card_challenge_data, diversified_key_d); - cipher_state_KC = opt_doTagMAC_1(card_challenge_data, diversified_key_c); + // initialize from page 0 + memcpy(conf_block, emulator + 8 * 1, 8); + memcpy(card_challenge_data, emulator + 8 * 2, 8); // e-purse + memcpy(diversified_key_d, emulator + 8 * 3, 8); // Kd + memcpy(diversified_key_c, emulator + 8 * 4, 8); // Kc } + // save card challenge for sim2,4 attack if (reader_mac_buf != NULL) { memcpy(reader_mac_buf, card_challenge_data, 8); } + if (conf_block[5] & 0x80) { + page_size = 256 * 8; + } + + // From PicoPass DS: + // When the page is in personalization mode this bit is equal to 1. + // Once the application issuer has personalized and coded its dedicated areas, this bit must be set to 0: + // the page is then "in application mode". + bool personalization_mode = conf_block[7] & 0x80; + + // chip memory may be divided in 8 pages + uint8_t max_page = conf_block[4] & 0x10 ? 0 : 7; + + // Precalculate the cipher states, feeding it the CC + cipher_state_KD[0] = opt_doTagMAC_1(card_challenge_data, diversified_key_d); + cipher_state_KC[0] = opt_doTagMAC_1(card_challenge_data, diversified_key_c); + if (simulationMode == ICLASS_SIM_MODE_FULL) { + for (int i = 1; i < max_page; i++) { + uint8_t *epurse = emulator + i*page_size + 8*2; + uint8_t *Kd = emulator + i*page_size + 8*3; + uint8_t *Kc = emulator + i*page_size + 8*4; + cipher_state_KD[i] = opt_doTagMAC_1(epurse, Kd); + cipher_state_KC[i] = opt_doTagMAC_1(epurse, Kc); + } + } + int exitLoop = 0; // Reader 0a // Tag 0f @@ -837,7 +864,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { int trace_data_size = 0; // Respond SOF -- takes 1 bytes - uint8_t *resp_sof = BigBuf_malloc(2); + uint8_t *resp_sof = BigBuf_malloc(1); int resp_sof_Len; // Anticollision CSN (rotated CSN) @@ -853,8 +880,6 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // configuration (block 1) picopass 2ks uint8_t *resp_conf = BigBuf_malloc(22); int resp_conf_len; - uint8_t conf_data[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00}; - AppendCrc(conf_data, 8); // e-Purse (block 2) // 18: Takes 2 bytes for SOF/EOF and 8 * 2 = 16 bytes (2 bytes/bit) @@ -877,7 +902,6 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { int len; // Prepare card messages - ToSendMax = 0; // First card answer: SOF only CodeIClassTagSOF(); @@ -895,7 +919,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { resp_csn_len = ToSendMax; // Configuration (block 1) - CodeIso15693AsTag(conf_data, sizeof(conf_data)); + CodeIso15693AsTag(conf_block, sizeof(conf_block)); memcpy(resp_conf, ToSend, ToSendMax); resp_conf_len = ToSendMax; @@ -918,16 +942,11 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { uint8_t *data_generic_trace = BigBuf_malloc(32 + 2); // 32 bytes data + 2byte CRC is max tag answer uint8_t *data_response = BigBuf_malloc( (32 + 2) * 2 + 2); - LED_A_ON(); bool buttonPressed = false; enum { IDLE, ACTIVATED, SELECTED, HALTED } chip_state = IDLE; while (!exitLoop) { WDT_HIT(); - LED_B_OFF(); - //Signal tracer - // Can be used to get a trigger for an oscilloscope.. - LED_C_OFF(); uint32_t reader_eof_time = 0; len = GetIso15693CommandFromReader(receivedCmd, MAX_FRAME_SIZE, &reader_eof_time); @@ -936,9 +955,6 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { break; } - //Signal tracer - LED_C_ON(); - // Now look at the reader command and provide appropriate responses // default is no response: modulated_response = NULL; @@ -951,8 +967,6 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { if (chip_state != HALTED) { modulated_response = resp_sof; modulated_response_size = resp_sof_Len; - trace_data = sof_data; - trace_data_size = sizeof(sof_data); chip_state = ACTIVATED; } @@ -1004,8 +1018,8 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { case 1: // configuration (block 01) modulated_response = resp_conf; modulated_response_size = resp_conf_len; - trace_data = conf_data; - trace_data_size = sizeof(conf_data); + trace_data = conf_block; + trace_data_size = sizeof(conf_block); break; case 2: // e-purse (block 02) modulated_response = resp_cc; @@ -1039,7 +1053,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data = ff_data; trace_data_size = sizeof(ff_data); } else { // use data from emulator memory - memcpy(data_generic_trace, emulator + 8*blockNo, 8); + memcpy(data_generic_trace, emulator + current_page*page_size + 8*blockNo, 8); AppendCrc(data_generic_trace, 8); trace_data = data_generic_trace; trace_data_size = 10; @@ -1055,11 +1069,17 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { || receivedCmd[0] == ICLASS_CMD_READCHECK_KC) && receivedCmd[1] == 0x02 && len == 2) { // Read e-purse (88 02 || 18 02) if (chip_state == SELECTED) { + if(receivedCmd[0] == ICLASS_CMD_READCHECK_KD){ + cipher_state = &cipher_state_KD[current_page]; + diversified_key = diversified_key_d; + } else { + cipher_state = &cipher_state_KC[current_page]; + diversified_key = diversified_key_c; + } modulated_response = resp_cc; modulated_response_size = resp_cc_len; trace_data = card_challenge_data; trace_data_size = sizeof(card_challenge_data); - LED_B_ON(); } } else if ((receivedCmd[0] == ICLASS_CMD_CHECK_KC @@ -1068,11 +1088,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { if (chip_state == SELECTED) { if (simulationMode == ICLASS_SIM_MODE_FULL) { //NR, from reader, is in receivedCmd+1 - if (receivedCmd[0] == ICLASS_CMD_CHECK_KC) { - opt_doTagMAC_2(cipher_state_KC, receivedCmd+1, data_generic_trace, diversified_key_c); - } else { - opt_doTagMAC_2(cipher_state_KD, receivedCmd+1, data_generic_trace, diversified_key_d); - } + opt_doTagMAC_2(*cipher_state, receivedCmd+1, data_generic_trace, diversified_key); trace_data = data_generic_trace; trace_data_size = 4; CodeIso15693AsTag(trace_data, trace_data_size); @@ -1095,13 +1111,16 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (receivedCmd[0] == ICLASS_CMD_HALT && len == 1) { if (chip_state == SELECTED) { // Reader ends the session + modulated_response = resp_sof; + modulated_response_size = resp_sof_Len; chip_state = HALTED; } } else if (simulationMode == ICLASS_SIM_MODE_FULL && receivedCmd[0] == ICLASS_CMD_READ4 && len == 4) { // 0x06 //Read 4 blocks if (chip_state == SELECTED) { - memcpy(data_generic_trace, emulator + receivedCmd[1]*8, 8 * 4); + uint8_t blockNo = receivedCmd[1]; + memcpy(data_generic_trace, emulator + current_page*page_size + blockNo*8, 8 * 4); AppendCrc(data_generic_trace, 8 * 4); trace_data = data_generic_trace; trace_data_size = 8 * 4 + 2; @@ -1121,29 +1140,37 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { CodeIso15693AsTag(card_challenge_data, sizeof(card_challenge_data)); memcpy(resp_cc, ToSend, ToSendMax); resp_cc_len = ToSendMax; - cipher_state_KD = opt_doTagMAC_1(card_challenge_data, diversified_key_d); - cipher_state_KC = opt_doTagMAC_1(card_challenge_data, diversified_key_c); + cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_key_d); + cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_key_c); if (simulationMode == ICLASS_SIM_MODE_FULL) { - memcpy(emulator + 8*2, card_challenge_data, 8); + memcpy(emulator + current_page*page_size + 8*2, card_challenge_data, 8); } } else if (blockNo == 3) { // update Kd - for (int i = 0; i < 8; i++){ - diversified_key_d[i] = diversified_key_d[i] ^ receivedCmd[2 + i]; + for (int i = 0; i < 8; i++) { + if (personalization_mode) { + diversified_key_d[i] = receivedCmd[2 + i]; + } else { + diversified_key_d[i] ^= receivedCmd[2 + i]; + } } - cipher_state_KD = opt_doTagMAC_1(card_challenge_data, diversified_key_d); + cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_key_d); if (simulationMode == ICLASS_SIM_MODE_FULL) { - memcpy(emulator + 8*3, diversified_key_d, 8); + memcpy(emulator + current_page*page_size + 8*3, diversified_key_d, 8); } } else if (blockNo == 4) { // update Kc - for(int i = 0; i < 8; i++){ - diversified_key_c[i] = diversified_key_c[i] ^ receivedCmd[2 + i]; + for (int i = 0; i < 8; i++) { + if (personalization_mode) { + diversified_key_c[i] = receivedCmd[2 + i]; + } else { + diversified_key_c[i] ^= receivedCmd[2 + i]; + } } - cipher_state_KC = opt_doTagMAC_1(card_challenge_data, diversified_key_c); + cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_key_c); if (simulationMode == ICLASS_SIM_MODE_FULL) { - memcpy(emulator + 8*4, diversified_key_c, 8); + memcpy(emulator + current_page*page_size + 8*4, diversified_key_c, 8); } } else if (simulationMode == ICLASS_SIM_MODE_FULL) { // update any other data block - memcpy(emulator + 8*blockNo, receivedCmd+2, 8); + memcpy(emulator + current_page*page_size + 8*blockNo, receivedCmd+2, 8); } memcpy(data_generic_trace, receivedCmd + 2, 8); AppendCrc(data_generic_trace, 8); @@ -1157,11 +1184,24 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (receivedCmd[0] == ICLASS_CMD_PAGESEL && len == 4) { // Pagesel + // Chips with a single page will not answer to this command + // Otherwise, we should answer 8bytes (block) + 2bytes CRC if (chip_state == SELECTED) { - // Pagesel enables to select a page in the selected chip memory and return its configuration block - // Chips with a single page will not answer to this command - // It appears we're fine ignoring this. - // Otherwise, we should answer 8bytes (block) + 2bytes CRC + if (simulationMode == ICLASS_SIM_MODE_FULL && max_page > 0) { + current_page = receivedCmd[1]; + memcpy(data_generic_trace, emulator + current_page*page_size + 8*1, 8); + memcpy(diversified_key_d, emulator + current_page*page_size + 8*3, 8); + memcpy(diversified_key_c, emulator + current_page*page_size + 8*4, 8); + cipher_state = &cipher_state_KD[current_page]; + personalization_mode = data_generic_trace[7] & 0x80; + AppendCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIso15693AsTag(trace_data, trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + } } } else if (receivedCmd[0] == 0x26 && len == 5) { @@ -1189,10 +1229,6 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } - LED_A_OFF(); - LED_B_OFF(); - LED_C_OFF(); - if (buttonPressed) { DbpString("Button pressed"); @@ -1213,6 +1249,9 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { * @param datain */ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { + + LED_A_ON(); + uint32_t simType = arg0; uint32_t numberOfCSNS = arg1; @@ -1220,6 +1259,7 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain FpgaDownloadAndGo(FPGA_BITSTREAM_HF); SetAdcMuxFor(GPIO_MUXSEL_HIPKD); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_NO_MODULATION); + LED_D_OFF(); FpgaSetupSsc(FPGA_MAJOR_MODE_HF_SIMULATOR); StartCountSspClk(); @@ -1270,8 +1310,10 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain // That will speed things up a little, but not required just yet. Dbprintf("The mode is not implemented, reserved for future use"); } + Dbprintf("Done..."); + LED_A_OFF(); } diff --git a/client/cmdhflist.c b/client/cmdhflist.c index 1b8e0955..07a286cc 100644 --- a/client/cmdhflist.c +++ b/client/cmdhflist.c @@ -225,17 +225,18 @@ void annotateIclass(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { } break; } - case ICLASS_CMD_SELECT: snprintf(exp,size, "SELECT"); break; - case ICLASS_CMD_PAGESEL: snprintf(exp,size, "PAGESEL(%d)", cmd[1]); break; - case ICLASS_CMD_READCHECK_KC:snprintf(exp,size, "READCHECK[Kc](%d)", cmd[1]); break; - case ICLASS_CMD_READCHECK_KD:snprintf(exp,size, "READCHECK[Kd](%d)", cmd[1]); break; - case ICLASS_CMD_CHECK: snprintf(exp,size, "CHECK"); break; - case ICLASS_CMD_DETECT: snprintf(exp,size, "DETECT"); break; - case ICLASS_CMD_HALT: snprintf(exp,size, "HALT"); break; - case ICLASS_CMD_UPDATE: snprintf(exp,size, "UPDATE(%d)",cmd[1]); break; - case ICLASS_CMD_ACT: snprintf(exp,size, "ACT"); break; - case ICLASS_CMD_READ4: snprintf(exp,size, "READ4(%d)",cmd[1]); break; - default: snprintf(exp,size, "?"); break; + case ICLASS_CMD_SELECT: snprintf(exp,size, "SELECT"); break; + case ICLASS_CMD_PAGESEL: snprintf(exp,size, "PAGESEL(%d)", cmd[1]); break; + case ICLASS_CMD_READCHECK_KC: snprintf(exp,size, "READCHECK[Kc](%d)", cmd[1]); break; + case ICLASS_CMD_READCHECK_KD: snprintf(exp,size, "READCHECK[Kd](%d)", cmd[1]); break; + case ICLASS_CMD_CHECK_KC: + case ICLASS_CMD_CHECK_KD: snprintf(exp,size, "CHECK"); break; + case ICLASS_CMD_DETECT: snprintf(exp,size, "DETECT"); break; + case ICLASS_CMD_HALT: snprintf(exp,size, "HALT"); break; + case ICLASS_CMD_UPDATE: snprintf(exp,size, "UPDATE(%d)",cmd[1]); break; + case ICLASS_CMD_ACT: snprintf(exp,size, "ACT"); break; + case ICLASS_CMD_READ4: snprintf(exp,size, "READ4(%d)",cmd[1]); break; + default: snprintf(exp,size, "?"); break; } return; } @@ -336,7 +337,7 @@ void annotateIso14443_4(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize){ else { int pos = 1; switch (cmd[0] & 0x0c) { - case 0x08: // CID following + case 0x08: // CID following case 0x04: // NAD following pos = 2; break; @@ -906,7 +907,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui timestamp *= 32; duration *= 32; } - + //Check the CRC status uint8_t crcStatus = 2; @@ -969,11 +970,15 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui if (protocol == ISO_14443A || protocol == PROTO_MIFARE) { if (duration < 128 * (9 * data_len)) { line[(data_len-1)/16][((data_len-1)%16) * 4 + 3] = '\''; - } + } } - + if (data_len == 0) { - sprintf(line[0]," "); + if (protocol == ICLASS && duration == 2048) { + sprintf(line[0], " "); + } else { + sprintf(line[0], " "); + } } //--- Draw the CRC column @@ -1014,7 +1019,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui (j == num_lines-1) ? explanation : ""); } } - + if (DecodeMifareData(frame, data_len, parityBytes, isResponse, mfData, &mfDataLen)) { memset(explanation, 0x00, sizeof(explanation)); if (!isResponse) { From 8efd0b80f2d11946b2fc0911cde939f9f93eb40f Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sat, 5 Oct 2019 17:57:16 +0200 Subject: [PATCH 8/8] fix 'hf iclass sim' * fix tag response timing. iClass differs from ISO15693 in this respect. * speedup CodeIso15693AsTag() * TransmitTo15693Tag(): don't send unmodulated start of SOF * reduce modulation depth in hi_simulate.v * calculate CRC for configuration block when simulating * Show real response time instead of planned response time in 'hf list iclass' --- armsrc/iclass.c | 57 ++++++++++++++++++++++++++------------------- armsrc/iso15693.c | 41 ++++++++++++++++++-------------- armsrc/iso15693.h | 2 +- fpga/fpga_hf.bit | Bin 42175 -> 42175 bytes fpga/hi_simulate.v | 2 +- 5 files changed, 59 insertions(+), 43 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index d8f68c78..0e42fb06 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -59,6 +59,13 @@ static int timeout = 4096; +// iCLASS has a slightly different timing compared to ISO15693. According to the picopass data sheet the tag response is expected 330us after +// the reader command. This is measured from end of reader EOF to first modulation of the tag's SOF which starts with a 56,64us unmodulated period. +// 330us = 140 ssp_clk cycles @ 423,75kHz when simulating. +// 56,64us = 24 ssp_clk_cycles +#define DELAY_ICLASS_VCD_TO_VICC_SIM 140 +#define TAG_SOF_UNMODULATED 24 + //----------------------------------------------------------------------------- // The software UART that receives commands from the reader, and its state // variables. @@ -783,7 +790,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { State cipher_state_KC[8]; State cipher_state_KD[8]; State *cipher_state = &cipher_state_KD[0]; - + uint8_t *emulator = BigBuf_get_EM_addr(); uint8_t *csn = emulator; @@ -800,13 +807,12 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { AppendCrc(anticoll_data, 8); AppendCrc(csn_data, 8); - uint8_t diversified_key_d[8]; - uint8_t diversified_key_c[8]; + uint8_t diversified_key_d[8] = { 0x00 }; + uint8_t diversified_key_c[8] = { 0x00 }; uint8_t *diversified_key = diversified_key_d; - + // configuration block uint8_t conf_block[10] = {0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C, 0x00, 0x00}; - AppendCrc(conf_block, 8); // e-Purse uint8_t card_challenge_data[8] = { 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; @@ -819,6 +825,8 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { memcpy(diversified_key_c, emulator + 8 * 4, 8); // Kc } + AppendCrc(conf_block, 8); + // save card challenge for sim2,4 attack if (reader_mac_buf != NULL) { memcpy(reader_mac_buf, card_challenge_data, 8); @@ -836,7 +844,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { // chip memory may be divided in 8 pages uint8_t max_page = conf_block[4] & 0x10 ? 0 : 7; - + // Precalculate the cipher states, feeding it the CC cipher_state_KD[0] = opt_doTagMAC_1(card_challenge_data, diversified_key_d); cipher_state_KC[0] = opt_doTagMAC_1(card_challenge_data, diversified_key_c); @@ -849,7 +857,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { cipher_state_KC[i] = opt_doTagMAC_1(epurse, Kc); } } - + int exitLoop = 0; // Reader 0a // Tag 0f @@ -1073,7 +1081,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { cipher_state = &cipher_state_KD[current_page]; diversified_key = diversified_key_d; } else { - cipher_state = &cipher_state_KC[current_page]; + cipher_state = &cipher_state_KC[current_page]; diversified_key = diversified_key_c; } modulated_response = resp_cc; @@ -1082,7 +1090,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { trace_data_size = sizeof(card_challenge_data); } - } else if ((receivedCmd[0] == ICLASS_CMD_CHECK_KC + } else if ((receivedCmd[0] == ICLASS_CMD_CHECK_KC || receivedCmd[0] == ICLASS_CMD_CHECK_KD) && len == 9) { // Reader random and reader MAC!!! if (chip_state == SELECTED) { @@ -1148,22 +1156,22 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (blockNo == 3) { // update Kd for (int i = 0; i < 8; i++) { if (personalization_mode) { - diversified_key_d[i] = receivedCmd[2 + i]; + diversified_key_d[i] = receivedCmd[2 + i]; } else { diversified_key_d[i] ^= receivedCmd[2 + i]; - } + } } - cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_key_d); + cipher_state_KD[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_key_d); if (simulationMode == ICLASS_SIM_MODE_FULL) { memcpy(emulator + current_page*page_size + 8*3, diversified_key_d, 8); } - } else if (blockNo == 4) { // update Kc + } else if (blockNo == 4) { // update Kc for (int i = 0; i < 8; i++) { if (personalization_mode) { - diversified_key_c[i] = receivedCmd[2 + i]; + diversified_key_c[i] = receivedCmd[2 + i]; } else { diversified_key_c[i] ^= receivedCmd[2 + i]; - } + } } cipher_state_KC[current_page] = opt_doTagMAC_1(card_challenge_data, diversified_key_c); if (simulationMode == ICLASS_SIM_MODE_FULL) { @@ -1171,7 +1179,7 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } } else if (simulationMode == ICLASS_SIM_MODE_FULL) { // update any other data block memcpy(emulator + current_page*page_size + 8*blockNo, receivedCmd+2, 8); - } + } memcpy(data_generic_trace, receivedCmd + 2, 8); AppendCrc(data_generic_trace, 8); trace_data = data_generic_trace; @@ -1185,20 +1193,20 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } else if (receivedCmd[0] == ICLASS_CMD_PAGESEL && len == 4) { // Pagesel // Chips with a single page will not answer to this command - // Otherwise, we should answer 8bytes (block) + 2bytes CRC + // Otherwise, we should answer 8bytes (conf block 1) + 2bytes CRC if (chip_state == SELECTED) { if (simulationMode == ICLASS_SIM_MODE_FULL && max_page > 0) { current_page = receivedCmd[1]; memcpy(data_generic_trace, emulator + current_page*page_size + 8*1, 8); memcpy(diversified_key_d, emulator + current_page*page_size + 8*3, 8); - memcpy(diversified_key_c, emulator + current_page*page_size + 8*4, 8); + memcpy(diversified_key_c, emulator + current_page*page_size + 8*4, 8); cipher_state = &cipher_state_KD[current_page]; personalization_mode = data_generic_trace[7] & 0x80; AppendCrc(data_generic_trace, 8); trace_data = data_generic_trace; trace_data_size = 10; CodeIso15693AsTag(trace_data, trace_data_size); - memcpy(data_response, ToSend, ToSendMax); + memcpy(data_response, ToSend, ToSendMax); modulated_response = data_response; modulated_response_size = ToSendMax; } @@ -1219,11 +1227,11 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { } /** - A legit tag has about 311,5us delay between reader EOT and tag SOF. + A legit tag has about 273,4us delay between reader EOT and tag SOF. **/ if (modulated_response_size > 0) { - uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM - DELAY_ARM_TO_READER_SIM; - TransmitTo15693Reader(modulated_response, modulated_response_size, response_time, false); + uint32_t response_time = reader_eof_time + DELAY_ICLASS_VCD_TO_VICC_SIM - TAG_SOF_UNMODULATED - DELAY_ARM_TO_READER_SIM; + TransmitTo15693Reader(modulated_response, modulated_response_size, &response_time, 0, false); LogTrace(trace_data, trace_data_size, response_time + DELAY_ARM_TO_READER_SIM, response_time + (modulated_response_size << 6) + DELAY_ARM_TO_READER_SIM, NULL, false); } @@ -1249,9 +1257,9 @@ int doIClassSimulation(int simulationMode, uint8_t *reader_mac_buf) { * @param datain */ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { - + LED_A_ON(); - + uint32_t simType = arg0; uint32_t numberOfCSNS = arg1; @@ -1559,6 +1567,7 @@ static uint8_t handshakeIclassTag_ext(uint8_t *card_data, bool use_credit_key) { ReaderTransmitIClass(act_all, 1); // Card present? if (!ReaderReceiveIClass(resp)) return read_status;//Fail + //Send Identify ReaderTransmitIClass(identify, 1); //We expect a 10-byte response here, 8 byte anticollision-CSN and 2 byte CRC diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index dbc1ca4c..85af0859 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -246,6 +246,8 @@ static void CodeIso15693AsReader256(uint8_t *cmd, int n) // } // } +static const uint8_t encode_4bits[16] = { 0xaa, 0x6a, 0x9a, 0x5a, 0xa6, 0x66, 0x96, 0x56, 0xa9, 0x69, 0x99, 0x59, 0xa5, 0x65, 0x95, 0x55 }; + void CodeIso15693AsTag(uint8_t *cmd, size_t len) { /* * SOF comprises 3 parts; @@ -280,16 +282,9 @@ void CodeIso15693AsTag(uint8_t *cmd, size_t len) { ToSend[++ToSendMax] = 0x1D; // 00011101 // data - for(int i = 0; i < len; i++) { - for(int j = 0; j < 8; j++) { - if ((cmd[i] >> j) & 0x01) { - ToSendStuffBit(0); - ToSendStuffBit(1); - } else { - ToSendStuffBit(1); - ToSendStuffBit(0); - } - } + for (int i = 0; i < len; i++) { + ToSend[++ToSendMax] = encode_4bits[cmd[i] & 0xF]; + ToSend[++ToSendMax] = encode_4bits[cmd[i] >> 4]; } // EOF @@ -327,19 +322,32 @@ static void TransmitTo15693Tag(const uint8_t *cmd, int len, uint32_t start_time) //----------------------------------------------------------------------------- // Transmit the tag response (to the reader) that was placed in cmd[]. //----------------------------------------------------------------------------- -void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t start_time, bool slow) { +void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow) { // don't use the FPGA_HF_SIMULATOR_MODULATE_424K_8BIT minor mode. It would spoil GetCountSspClk() FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_424K); - uint8_t shift_delay = start_time & 0x00000007; + uint32_t modulation_start_time = *start_time + 3 * 8; // no need to transfer the unmodulated start of SOF + + while (GetCountSspClk() > (modulation_start_time & 0xfffffff8) + 3) { // we will miss the intended time + if (slot_time) { + modulation_start_time += slot_time; // use next available slot + } else { + modulation_start_time = (modulation_start_time & 0xfffffff8) + 8; // next possible time + } + } - while (GetCountSspClk() < (start_time & 0xfffffff8)) ; + while (GetCountSspClk() < (modulation_start_time & 0xfffffff8)) + /* wait */ ; + + uint8_t shift_delay = modulation_start_time & 0x00000007; + + *start_time = modulation_start_time - 3 * 8; LED_C_ON(); uint8_t bits_to_shift = 0x00; uint8_t bits_to_send = 0x00; - for(size_t c = 0; c < len; c++) { - for (int i = 7; i >= 0; i--) { + for (size_t c = 0; c < len; c++) { + for (int i = (c==0?4:7); i >= 0; i--) { uint8_t cmd_bits = ((cmd[c] >> i) & 0x01) ? 0xff : 0x00; for (int j = 0; j < (slow?4:1); ) { if (AT91C_BASE_SSC->SSC_SR & AT91C_SSC_TXRDY) { @@ -361,7 +369,6 @@ void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t start_time, } } LED_C_OFF(); - } @@ -1529,7 +1536,7 @@ void SimTagIso15693(uint32_t parameter, uint8_t *uid) if ((cmd_len >= 5) && (cmd[0] & ISO15693_REQ_INVENTORY) && (cmd[1] == ISO15693_INVENTORY)) { // TODO: check more flags bool slow = !(cmd[0] & ISO15693_REQ_DATARATE_HIGH); start_time = eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM - DELAY_ARM_TO_READER_SIM; - TransmitTo15693Reader(ToSend, ToSendMax, start_time, slow); + TransmitTo15693Reader(ToSend, ToSendMax, &start_time, 0, slow); } Dbprintf("%d bytes read from reader:", cmd_len); diff --git a/armsrc/iso15693.h b/armsrc/iso15693.h index 7964d79e..7d2e7598 100644 --- a/armsrc/iso15693.h +++ b/armsrc/iso15693.h @@ -26,7 +26,7 @@ void CodeIso15693AsTag(uint8_t *cmd, size_t len); int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eof_time); -void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t start_time, bool slow); +void TransmitTo15693Reader(const uint8_t *cmd, size_t len, uint32_t *start_time, uint32_t slot_time, bool slow); void SnoopIso15693(void); void AcquireRawAdcSamplesIso15693(void); void ReaderIso15693(uint32_t parameter); diff --git a/fpga/fpga_hf.bit b/fpga/fpga_hf.bit index 1bd3c416fe728c26ee204ede9bc882c8f87486c8..44b2428040ab74e66878d78bf7cc7a79ee3c7266 100644 GIT binary patch literal 42175 zcma&Pe|%Kcoj?4Ub8q6!+)3t=1UD+`%_QJ}lQ2v$B8JH!r0!H{nYMnvFSgrfpV3k` z+qK=)^|cT6yHEFI5@1LojFh%%m-@PdXrra=5I}xN^@33c2r}|@U9_&f&`6`AjTUWy zAkX{EBs1}k@AKN(KlV7B+&Sl-^ZC3#@Av0(PN~E@p8G!{*L_s+K>Od{_kXuNP}lz8 zec$}nn)>g4a}C{3?$AFs1pnoGi-JM=Ch4KzqB|BY3N8#b(i$pR)VOrf9ZN$&`WDeZ z`&E3N{^|dCAV|VRMArmeG5Pn1 z`lurNFaM1`F7|(VM)v#MbN)Mh{u{sb-x_np|KE7j>_2{HP>ibRTDnG%Lu=`JDkhhp zHR8HhaaHrMEj!so<8*==sBAJY$MMBXm1g(|VBFWglUTxq3unP!nt(CG7g( z-67_qgz$Y^TM#$ZpzM<&mJHbrq_V}n|J8M^C@;Nm7ed@MDOWZ<2rG6!AOJTNZFXOA9Kcs8&4U>M2P03Y8;xNP*uQEdW~azh&+~(R883@ zUF4=XH}9(2&)yRUP2Xy*iv`4+!bh6LLd0bsr-)OBX}Yw|mSdE7e74_Y z`?1CFFqWJxtz~c19`O%mVl6A@k0q)dt&M59=R`DOwvp2O^OO-9H}(sUS|eKj zw<{B_~tXpIKNunJm7WyK2sc%eFlrtbd18}F;bA+Swf1OLb@zzlGJYY%Ru4<=(SRL$prRnc5_~ z7GthUVW@vc8{*X#8smm3e=AaXB-YUvIOpHbJbd?fz^MwxPl!&JDYQX6C%f)rvwzB7 zq2{FK+^~c`BcJW6Gu6Z$(HhL6hN4C}+%Z9@aJs#l|e8aV*&T_vc)HSq`d{J#1yM+$)nfNDmj&1Dl$%bhd z@;OySwdm8&{Ye%u52(H(RSW# zFg8m*hW3}RTyUMX%Zz>nCUdxtm zep4BxixlRinVNIO?eaNQMWe8i|GVg#62l=ewP}!$539%t=AI6rUccKZZcC# zz_c{c;;@9;FRBE4B{z4iXc#LP;5P)BDwi^*1=%odCqLJW;v+fxs+v8(d(3*S8?e)j zv+!EB`bODCa{=BDy_8!EtMV7(C=cK3?bB<`qdXK1Jccp9OG~5vJ~o!KFF$Q!m_^L{ zGi8*0L~|bS?pKEC&$O6(f2thI*_YqgVWiEF<`FAQV*;BgAQUH-rjUWg-8uX6t9TN> zwO@1C^|9v?HA$G&CAHh!A@EIiZY}&pUzRpFUa~murQMpD!MskgjyMT4x~J`HHf<1o zr$p}OXQgQTD0`LtC+!vfkJ*CUdnVcn8>}{T&L(NSQGFPFqHG^o z$&&UsT!tJ^ta5}pd9AINvBM;|C3KD1^g|l3N|rD!x9)?pchU<+=vAzRbF3p8vb_VH zZS*Yd8Y)?NCRK?KH? zL3Yw?5ZXAS)}S0uhx#CG6bq{E>1{blJx9!kw0?G+I>pmMOEHTZvX3ZgF}sCN(>vH1 zD$(XSnkfH}ddMf-=#yIujfizHzpYb?tpOBxAF;I3Nq%vCip8@qjm6+}Tn&*|RP1!Y zUDzv=D=a$BLZW_L5jp#^Zj_F&nY^|#P8VFcM%7=aS(J_|V>$Z*ehHw}vA`)M6(6x1 z%&yFia?e$<%<)`M5Hw`RY`ic>vugcWZ7;KO?>SMF=E7Bb z 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