diff --git a/CHANGELOG.md b/CHANGELOG.md index b0795fc0e..1ab2736e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Changed `hf iclass info` - now checks for cards silicon version (@antiklesys) - Changed `hf iclass legrec` - updated script implementation to ensure functionality (@antiklesys) - Added recovered iclass custom key to dictionary (@antiklesys) +- Added support for all Hitag S response protocol mode (@douniwan5788) +- Fixed 'hf_young.c' - flags declaration was missing a semicolon (@jakkpotts) - Changed `hf mf sim` - add option to allow key b to be used even if readable (@doegox) - Changed `data num` - outputed binary strings are now properly zero padded (@iceman1001) - Changed `hf iclass info` - now tries default keys and decode if legacy (@iceman1001) diff --git a/armsrc/Standalone/hf_young.c b/armsrc/Standalone/hf_young.c index a887c2bb5..1356e2954 100644 --- a/armsrc/Standalone/hf_young.c +++ b/armsrc/Standalone/hf_young.c @@ -236,7 +236,7 @@ void RunMod(void) { int button_pressed = BUTTON_HELD(1000); if (button_pressed == BUTTON_NO_CLICK) { // No button action, proceed with sim - uint16_t flags = 0 + uint16_t flags = 0; FLAG_SET_UID_IN_DATA(flags, 4); uint8_t data[PM3_CMD_DATA_SIZE] = {0}; // in case there is a read command received we shouldn't break diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index edee23a4d..ac168c900 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -65,6 +65,7 @@ typedef enum modulation { MC8K } MOD; +static uint8_t protocol_mode = HITAGS_UID_REQ_ADV1; static MOD m = AC2K; // used modulation static uint32_t reader_selected_uid; static int rotate_uid = 0; @@ -383,18 +384,17 @@ static void hts_init_clock(void) { // Disable timer during configuration hts_stop_clock(); - // TC0: Capture mode, clock source = MCK/32 (TIMER_CLOCK3), no triggers + // TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - // TC1: Capture mode, clock source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger, - // external trigger falling edge, set RA on falling edge of TIOA. - AT91C_BASE_TC1->TC_CMR = - AT91C_TC_CLKS_TIMER_DIV3_CLOCK | // MCK/32 (TIMER_CLOCK3) - AT91C_TC_ETRGEDG_FALLING | // external trigger on falling edge - AT91C_TC_ABETRG | // TIOA is used as an external trigger - AT91C_TC_LDRA_FALLING; // load RA on on falling edge + // TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger, + AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK // use MCK/32 (TIMER_CLOCK3) + | AT91C_TC_ABETRG // TIOA is used as an external trigger + | AT91C_TC_ETRGEDG_FALLING // external trigger on falling edge + | AT91C_TC_LDRA_RISING // load RA on on rising edge of TIOA + | AT91C_TC_LDRB_FALLING; // load RB on on falling edge of TIOA - // TC2: Capture mode, clock source = MCK/32 (TIMER_CLOCK3), no triggers + // TC2: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; // Enable and reset counters @@ -429,24 +429,35 @@ static int check_select(const uint8_t *rx, uint32_t uid) { return 0; } -static void hts_set_frame_modulation(void) { - switch (tag.mode) { - case HT_STANDARD: { +static void hts_set_frame_modulation(uint8_t mode, bool ac_seq) { + switch (mode) { + case HITAGS_UID_REQ_STD: { sof_bits = 1; - m = MC4K; + if (ac_seq) + m = AC2K; + else + m = MC4K; break; } - case HT_ADVANCED: { - sof_bits = 6; - m = MC4K; + case HITAGS_UID_REQ_ADV1: + case HITAGS_UID_REQ_ADV2: { + if (ac_seq) { + sof_bits = 3; + m = AC2K; + } else { + sof_bits = 6; + m = MC4K; + } break; } - case HT_FAST_ADVANCED: { - sof_bits = 6; - m = MC8K; - break; - } - default: { + case HITAGS_UID_REQ_FADV: { + if (ac_seq) { + sof_bits = 3; + m = AC4K; + } else { + sof_bits = 6; + m = MC8K; + } break; } } @@ -463,7 +474,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, // Reset the transmission frame length *txlen = 0; // Reset the frame modulation - hts_set_frame_modulation(); + hts_set_frame_modulation(protocol_mode, false); // Try to find out which command was send by selecting on length (in bits) switch (rxlen) { @@ -475,24 +486,15 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, if (rx[0] == HITAGS_UID_REQ_STD) { DBG Dbprintf("HT_STANDARD"); - tag.mode = HT_STANDARD; - sof_bits = 1; - m = AC2K; - } - - if (rx[0] == HITAGS_UID_REQ_ADV1 || rx[0] == HITAGS_UID_REQ_ADV2) { + } else if (rx[0] == HITAGS_UID_REQ_ADV1 || rx[0] == HITAGS_UID_REQ_ADV2) { DBG Dbprintf("HT_ADVANCED"); - tag.mode = HT_ADVANCED; - sof_bits = 3; - m = AC2K; + } else if (rx[0] == HITAGS_UID_REQ_FADV) { + DBG Dbprintf("HT_FAST_ADVANCED"); } - if (rx[0] == HITAGS_UID_REQ_FADV) { - DBG Dbprintf("HT_FAST_ADVANCED"); - tag.mode = HT_FAST_ADVANCED; - sof_bits = 3; - m = AC4K; - } + protocol_mode = rx[0]; + hts_set_frame_modulation(protocol_mode, true); + //send uid as a response *txlen = 32; memcpy(tx, tag.data.pages[HITAGS_UID_PADR], HITAGS_PAGE_SIZE); @@ -515,7 +517,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, tx[3] = 0xff; - if (tag.mode != HT_STANDARD) { + if (protocol_mode != HITAGS_UID_REQ_STD) { //add crc8 *txlen += 8; crc = CRC_PRESET; @@ -549,7 +551,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, tx[2] = ht2_hitag2_byte(&state) ^ tag.data.s.pwdl0; tx[3] = ht2_hitag2_byte(&state) ^ tag.data.s.pwdl1; - if (tag.mode != HT_STANDARD) { + if (protocol_mode != HITAGS_UID_REQ_STD) { //add crc8 *txlen += 8; crc = CRC_PRESET; @@ -616,7 +618,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, tx[3] = 0xFF; } - if (tag.mode != HT_STANDARD) { + if (protocol_mode != HITAGS_UID_REQ_STD) { //add crc8 *txlen += 8; crc = CRC_PRESET; @@ -638,7 +640,7 @@ static void hts_handle_reader_command(uint8_t *rx, const size_t rxlen, //send page,...,page+3 data memcpy(tx, tag.data.pages[page], *txlen / 8); - if (tag.mode != HT_STANDARD) { + if (protocol_mode != HITAGS_UID_REQ_STD) { //add crc8 crc = CRC_PRESET; for (int i = 0; i < *txlen / 8; i++) { @@ -751,40 +753,7 @@ void hts_simulate(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol) { // Disable modulation at default, which means release resistance LOW(GPIO_SSC_DOUT); - // Enable Peripheral Clock for - // Timer Counter 0, used to measure exact timing before answering - // Timer Counter 1, used to capture edges of the tag frames - // Timer Counter 2, used to log trace time - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1) | (1 << AT91C_ID_TC2); - - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - - // Disable timer during configuration - hts_stop_clock(); - - // TC0: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - - // TC1: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), TIOA is external trigger, - // external trigger rising edge, load RA on rising edge of TIOA. - AT91C_BASE_TC1->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK - | AT91C_TC_ETRGEDG_RISING | AT91C_TC_ABETRG | AT91C_TC_LDRA_RISING; - // TC2: Capture mode, default timer source = MCK/32 (TIMER_CLOCK3), no triggers - AT91C_BASE_TC2->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK; - - // Enable and reset counter - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC2->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - // Assert a sync signal. This sets all timers to 0 on next active clock edge - AT91C_BASE_TCB->TCB_BCR = 1; - - // synchronized startup procedure - while (AT91C_BASE_TC0->TC_CV != 0); // wait until TC0 returned to zero - - // reset timestamp - timestamp_high = 0; + hts_init_clock(); if (ledcontrol) LED_D_ON(); @@ -890,21 +859,29 @@ static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint3 bool bSkip = true; uint32_t errorCount = 0; bool bStarted = false; + uint16_t next_edge_event = AT91C_TC_LDRBS; + int double_speed = (m == AC4K || m == MC8K) ? 2 : 1; - uint32_t ra_i = 0, h2 = 0, h3 = 0, h4 = 0; + uint32_t rb_i = 0, h2 = 0, h3 = 0, h4 = 0; uint8_t edges[160] = {0}; - // Dbprintf("TC0_CV:%i TC1_CV:%i TC1_RA:%i", AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV ,AT91C_BASE_TC1->TC_RA); + // Dbprintf("TC0_CV:%i TC1_CV:%i TC1_RB:%i TIMESTAMP:%u", AT91C_BASE_TC0->TC_CV, AT91C_BASE_TC1->TC_CV, + // AT91C_BASE_TC1->TC_RB, TIMESTAMP); // Receive tag frame, watch for at most T0*HITAG_T_PROG_MAX periods while (AT91C_BASE_TC0->TC_CV < (T0 * HITAG_T_PROG_MAX)) { - // Check if falling edge in tag modulation is detected - if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Check if edge in tag modulation is detected + if (AT91C_BASE_TC1->TC_SR & next_edge_event) { + + next_edge_event = next_edge_event ^ (AT91C_TC_LDRAS | AT91C_TC_LDRBS); + + // only use AT91C_TC_LDRBS falling edge for now + if (next_edge_event == AT91C_TC_LDRBS) continue; // Retrieve the new timing values - uint32_t ra = AT91C_BASE_TC1->TC_RA / T0; - edges[ra_i++] = ra; + uint32_t rb = AT91C_BASE_TC1->TC_RB / T0; + edges[rb_i++] = rb; // Reset timer every frame, we have to capture the last edge for timing AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; @@ -916,7 +893,7 @@ static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint3 // Capture tag frame (manchester decoding using only falling edges) if (bStarted == false) { - if (ra >= HITAG_T_WAIT_RESP) { + if (rb >= HITAG_T_WAIT_RESP) { bStarted = true; // We always receive a 'one' first, which has the falling edge after a half period |-_| @@ -926,39 +903,69 @@ static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint3 errorCount++; } - } else if (ra >= HITAG_T_TAG_CAPTURE_FOUR_HALF) { - - // Manchester coding example |-_|_-|-_| (101) - rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - - rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - h4++; - } else if (ra >= HITAG_T_TAG_CAPTURE_THREE_HALF) { - - // Manchester coding example |_-|...|_-|-_| (0...01) - rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - - // We have to skip this half period at start and add the 'one' the second time - if (bSkip == false) { - rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); - (*rxlen)++; - } - - lastbit = !lastbit; - bSkip = !bSkip; - h3++; - } else if (ra >= HITAG_T_TAG_CAPTURE_TWO_HALF) { - // Manchester coding example |_-|_-| (00) or |-_|-_| (11) - // bit is same as last bit - rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); - (*rxlen)++; - h2++; } else { - // Ignore weird value, is to small to mean anything - errorCount++; + // Anticollision Coding + if (m == AC2K || m == AC4K) { + if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) { + // Anticollision Coding example |--__|--__| (00) + lastbit = 0; + rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); + (*rxlen)++; + } else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) { + // Anticollision Coding example |-_-_|--__| (10) or |--__|-_-_| (01) + lastbit = !lastbit; + rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); + (*rxlen)++; + + bSkip = !!lastbit; + } else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) { + // Anticollision Coding example |-_-_| (1) + if (bSkip == false) { + lastbit = 1; + rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); + (*rxlen)++; + } + + bSkip = !bSkip; + } else { + // Ignore weird value, is to small to mean anything + errorCount++; + } + } else { + // Manchester coding + if (rb >= HITAG_T_TAG_CAPTURE_FOUR_HALF / double_speed) { + // Manchester coding example |-_|_-|-_| (101) + rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + + rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + h4++; + } else if (rb >= HITAG_T_TAG_CAPTURE_THREE_HALF / double_speed) { + // Manchester coding example |_-|...|_-|-_| (0...01) + rx[(*rxlen) / 8] |= 0 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + + // We have to skip this half period at start and add the 'one' the second time + if (bSkip == false) { + rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); + (*rxlen)++; + } + + lastbit = !lastbit; + bSkip = !bSkip; + h3++; + } else if (rb >= HITAG_T_TAG_CAPTURE_TWO_HALF / double_speed) { + // Manchester coding example |_-|_-| (00) or |-_|-_| (11) + // bit is same as last bit + rx[(*rxlen) / 8] |= lastbit << (7 - ((*rxlen) % 8)); + (*rxlen)++; + h2++; + } else { + // Ignore weird value, is to small to mean anything + errorCount++; + } + } } } @@ -979,10 +986,10 @@ static void hts_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, uint3 } DBG Dbprintf("RX0 %i:%02X.. err:%i resptime:%i h2:%i h3:%i h4:%i edges:", *rxlen, rx[0], errorCount, *resptime, h2, h3, h4); - DBG Dbhexdump(ra_i, edges, false); + DBG Dbhexdump(rb_i, edges, false); } -static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *prxbits, int t_wait, bool ledcontrol, bool ac_seq) { +static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t sizeofrx, size_t *rxlen, int t_wait, bool ledcontrol, bool ac_seq) { uint32_t start_time; // Send and store the reader command @@ -1010,73 +1017,41 @@ static int hts_send_receive(const uint8_t *tx, size_t txlen, uint8_t *rx, size_t // Enable and reset external trigger in timer for capturing future frames AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - size_t rxlen = 0; - hts_receive_frame(rx, sizeofrx, &rxlen, &start_time, ledcontrol); - int k = 0; + hts_set_frame_modulation(protocol_mode, ac_seq); + + hts_receive_frame(rx, sizeofrx, rxlen, &start_time, ledcontrol); // Check if frame was captured and store it - if (rxlen > 0) { + if (*rxlen > 0) { + DBG { + uint8_t response_bit[sizeofrx * 8]; - uint8_t response_bit[sizeofrx * 8]; - - for (size_t i = 0; i < rxlen; i++) { - response_bit[i] = (rx[i / 8] >> (7 - (i % 8))) & 1; - } - - DBG Dbprintf("htS: rxlen...... %zu", rxlen); - DBG Dbprintf("htS: sizeofrx... %zu", sizeofrx); - DBG DbpString("htS: response_bit:"); - DBG Dbhexdump(rxlen, response_bit, false); - - memset(rx, 0x00, sizeofrx); - - if (ac_seq) { - - // Tag Response is AC encoded - // We used UID Request Advanced, meaning AC SEQ SOF is 111. - for (int i = 7; i < rxlen; i += 2) { - - rx[k / 8] |= response_bit[i] << (7 - (k % 8)); - - k++; - - if (k > 8 * sizeofrx) { - break; - } + for (size_t i = 0; i < *rxlen; i++) { + response_bit[i] = (rx[i / 8] >> (7 - (i % 8))) & 1; } - // TODO: It's very confusing to reinterpreter the MC to AC; we should implement a more straightforward approach. - // add the lost bit zero, when AC64 last bit is zero - if (k % 8 == 7) { - k++; - } - - if (g_dbglevel >= DBG_EXTENDED) { - DbpString("htS: ac sequence compress"); - Dbhexdump(k / 8, rx, false); - } - - } else { - - if (g_dbglevel >= DBG_EXTENDED) { - DbpString("htS: skipping 6 bit header"); - } - - // ignore first 6 bits: SOF (actually 1 or 6 depending on response protocol) - // or rather a header. - for (size_t i = 6; i < rxlen; i++) { - - rx[k / 8] |= response_bit[i] << (7 - (k % 8)); - k++; - - if (k > 8 * sizeofrx) { - break; - } + Dbprintf("htS: rxlen...... %zu", *rxlen); + Dbprintf("htS: sizeofrx... %zu", sizeofrx); + DbpString("htS: response_bit:"); + Dbhexdump(*rxlen, response_bit, false); + Dbprintf("htS: skipping %d bit SOF", sof_bits); + if ((rx[0] >> (8 - sof_bits)) != ((1 << sof_bits) - 1)) { + DbpString("htS: Warning, not all bits of SOF are 1"); } } - LogTraceBits(rx, k, start_time, TIMESTAMP, false); + + // remove first sof_bits bits SOF + for (size_t i = 0; i < (*rxlen + 8) / 8; i++) { + rx[i] <<= sof_bits; + if (i + 1 < (*rxlen + 8) / 8) { + rx[i] |= (rx[i + 1] >> (8 - sof_bits)); + } + } + + *rxlen -= sof_bits; + + LogTraceBits(rx, *rxlen, start_time, TIMESTAMP, false); } - *prxbits = k; return PM3_SUCCESS; } @@ -1112,7 +1087,9 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz // UID request FAdvanced 11010 size_t txlen = 0; size_t rxlen = 0; - uint8_t cmd = HITAGS_UID_REQ_ADV1; + + protocol_mode = packet->mode; + uint8_t cmd = protocol_mode; txlen = concatbits(tx, txlen, &cmd, 0, 5); hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, true); @@ -1135,7 +1112,7 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); - if (rxlen != 40) { + if (rxlen != 32 + (protocol_mode == HITAGS_UID_REQ_STD ? 0 : 8)) { DBG Dbprintf("Select UID failed! %i", rxlen); return -3; } @@ -1234,7 +1211,7 @@ static int hts_select_tag(const lf_hitag_data_t *packet, uint8_t *tx, size_t siz hts_send_receive(tx, txlen, rx, sizeofrx, &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); - if (rxlen != 40) { + if (rxlen != 32 + (protocol_mode == HITAGS_UID_REQ_STD ? 0 : 8)) { DBG Dbprintf("Authenticate failed! " _RED_("%i"), rxlen); return -8; } @@ -1295,6 +1272,12 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) { while ((BUTTON_PRESS() == false) && (data_available() == false)) { + if (payload->page_count == 0) { + if (page_addr > tag.max_page) break; + } else if (page_addr > 255 || page_addr >= payload->page + payload->page_count) { + break; + } + WDT_HIT(); size_t rxlen = 0; @@ -1310,9 +1293,9 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) { hts_send_receive(tx, txlen, rx, ARRAYLEN(rx), &rxlen, HITAG_T_WAIT_SC, ledcontrol, false); - if (rxlen != 40) { + if (rxlen != 32 + (protocol_mode == HITAGS_UID_REQ_STD ? 0 : 8)) { DBG Dbprintf("Read page failed!"); - card.pages_reason[page_index] = -4; + card.pages_reason[page_index] = -11; // status = PM3_ERFTRANS; // goto read_end; page_addr++; @@ -1362,18 +1345,12 @@ void hts_read(const lf_hitag_data_t *payload, bool ledcontrol) { //if the authentication is done with a challenge the key and password are unknown DBG Dbprintf("Page[ 2]: __ __ __ __"); DBG Dbprintf("Page[ 3]: __ __ __ __"); - card.pages_reason[page_index++] = -4; - card.pages_reason[page_index++] = -4; + card.pages_reason[page_index++] = -11; + card.pages_reason[page_index++] = -11; } // since page 2+3 are not accessible when LKP == 1 and AUT == 1 fastforward to next readable page page_addr = 4; } - - if (payload->page_count == 0) { - if (page_addr > tag.max_page) break; - } else if (page_addr > 255 || page_addr >= payload->page + payload->page_count) { - break; - } } read_end: @@ -1505,7 +1482,8 @@ int hts_read_uid(uint32_t *uid, bool ledcontrol, bool send_answer) { // UID request standard 00110 // UID request Advanced 1100x // UID request FAdvanced 11010 - uint8_t cmd = HITAGS_UID_REQ_ADV1; + protocol_mode = HITAGS_UID_REQ_ADV1; + uint8_t cmd = protocol_mode; size_t rxlen = 0; uint8_t rx[HITAG_FRAME_LEN] = { 0x00 }; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 96f615c64..6be8f230b 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1534,7 +1534,7 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, continue; }; - if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, NULL, NULL)) { + if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt2, NULL)) { continue; }; @@ -1544,6 +1544,12 @@ void MifareStaticNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, }; nt3 = bytes_to_num(receivedAnswer, 4); + // fix for cards with distance 0 + if (nt1 == nt2) { + target_nt[0] = nt1; + target_nt[1] = nt1; + target_ks[0] = nt3 ^ target_nt[0]; + } target_ks[1] = nt3 ^ target_nt[1]; isOK = PM3_SUCCESS; diff --git a/client/luascripts/hf_mf_ultimatecard.lua b/client/luascripts/hf_mf_ultimatecard.lua index 26a5aaee2..09040eea5 100644 --- a/client/luascripts/hf_mf_ultimatecard.lua +++ b/client/luascripts/hf_mf_ultimatecard.lua @@ -191,7 +191,7 @@ local function read_config() if magicconfig == nil then lib14a.disconnect(); return nil, "can't read configuration, "..err_lock end if #magicconfig ~= 64 and #magicconfig ~= 68 then lib14a.disconnect(); return nil, "partial read of configuration, "..err_lock end if gtumode == '00' then gtustr = 'Pre-write/Shadow Mode' - elseif gtumode == '01' then gtustr = 'Restore Mode' + elseif gtumode == '01' or gtumode == '04' then gtustr = 'Restore Mode' elseif gtumode == '02' then gtustr = 'Disabled' elseif gtumode == '03' then gtustr = 'Disabled, high speed R/W mode for Ultralight' end @@ -553,7 +553,7 @@ local function write_gtu(gtu) if gtu == '00' then print('Enabling GTU Pre-Write') send('CF'.._key..'32'..gtu) - elseif gtu == '01' then + elseif gtu == '01' or gtu == '04' then print('Enabling GTU Restore Mode') send('CF'.._key..'32'..gtu) elseif gtu == '02' then diff --git a/client/pyscripts/fm11rf08s_recovery.py b/client/pyscripts/fm11rf08s_recovery.py index 3368749a1..71f785111 100755 --- a/client/pyscripts/fm11rf08s_recovery.py +++ b/client/pyscripts/fm11rf08s_recovery.py @@ -76,6 +76,10 @@ parser.add_argument('-x', '--init-check', action='store_true', help='Run an init parser.add_argument('-y', '--final-check', action='store_true', help='Run a final fchk with the found keys') parser.add_argument('-k', '--keep', action='store_true', help='Keep generated dictionaries after processing') parser.add_argument('-d', '--debug', action='store_true', help='Enable debug mode') +parser.add_argument('-s', '--supply-chain', action='store_true', help='Enable supply-chain mode. Look for hf-mf-XXXXXXXX-default_nonces.json') +# Such json can be produced from the json saved by +# "hf mf isen --collect_fm11rf08s --key A396EFA4E24F" on a wiped card, then processed with +# jq '{Created: .Created, FileType: "fm11rf08s_default_nonces", nt: .nt | del(.["32"]) | map_values(.a)}' args = parser.parse_args() start_time = time.time() @@ -191,6 +195,22 @@ if os.path.isfile(DICT_DEF_PATH): else: print(f"Warning, {DICT_DEF} not found.") +dict_dnwd = None +def_nt = ["" for _ in range(NUM_SECTORS)] +if args.supply_chain: + try: + default_nonces = f'{save_path}hf-mf-{uid:04X}-default_nonces.json' + with open(default_nonces, 'r') as file: + # Load and parse the JSON data + dict_dnwd = json.load(file) + for sec in range(NUM_SECTORS): + def_nt[sec] = dict_dnwd["nt"][f"{sec}"].lower() + print(f"Loaded default nonces from {default_nonces}.") + except FileNotFoundError: + pass + except json.decoder.JSONDecodeError: + print(f"Error parsing {default_nonces}, skipping.") + print("Running staticnested_1nt & 2x1nt when doable...") keys = [[set(), set()] for _ in range(NUM_SECTORS + NUM_EXTRA_SECTORS)] all_keys = set() @@ -225,9 +245,21 @@ for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): keys[sec][key_type] = keys_set.copy() duplicates.update(all_keys.intersection(keys_set)) all_keys.update(keys_set) - # Prioritize default keys - keys_def_set = DEFAULT_KEYS.intersection(keys_set) - keys_set.difference_update(DEFAULT_KEYS) + if dict_dnwd is not None and sec < NUM_SECTORS: + # Prioritize keys from supply-chain attack + cmd = [tools["staticnested_2x1nt1key"], def_nt[sec], "FFFFFFFFFFFF", f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}_filtered.dic"] + if args.debug: + print(' '.join(cmd)) + result = subprocess.run(cmd, capture_output=True, text=True).stdout + keys_def_set = set() + for line in result.split('\n'): + if "MATCH:" in line: + keys_def_set.add(line[12:]) + keys_set.difference_update(keys_def_set) + else: + # Prioritize default keys + keys_def_set = DEFAULT_KEYS.intersection(keys_set) + keys_set.difference_update(keys_def_set) # Prioritize sector 32 keyB starting with 0000 if real_sec == 32: keyb32cands = set(x for x in keys_set if x.startswith("0000")) @@ -257,9 +289,21 @@ for sec in range(NUM_SECTORS + NUM_EXTRA_SECTORS): keys[sec][key_type] = keys_set.copy() duplicates.update(all_keys.intersection(keys_set)) all_keys.update(keys_set) - # Prioritize default keys - keys_def_set = DEFAULT_KEYS.intersection(keys_set) - keys_set.difference_update(DEFAULT_KEYS) + if dict_dnwd is not None and sec < NUM_SECTORS: + # Prioritize keys from supply-chain attack + cmd = [tools["staticnested_2x1nt1key"], def_nt[sec], "FFFFFFFFFFFF", f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic"] + if args.debug: + print(' '.join(cmd)) + result = subprocess.run(cmd, capture_output=True, text=True).stdout + keys_def_set = set() + for line in result.split('\n'): + if "MATCH:" in line: + keys_def_set.add(line[12:]) + keys_set.difference_update(keys_def_set) + else: + # Prioritize default keys + keys_def_set = DEFAULT_KEYS.intersection(keys_set) + keys_set.difference_update(keys_def_set) if len(keys_def_set) > 0: found_default[sec][key_type] = True with (open(f"keys_{uid:08x}_{real_sec:02}_{nt[sec][key_type]}.dic", "w")) as f: @@ -484,6 +528,7 @@ if abort: print("Brute-forcing phase aborted via keyboard!") args.final_check = False +plus = "[" + color("+", fg="green") + "] " if args.final_check: print("Letting fchk do a final dump, just for confirmation and display...") keys_set = set([i for sl in found_keys for i in sl if i != ""]) @@ -495,7 +540,6 @@ if args.final_check: print(cmd) p.console(cmd, passthru=True) else: - plus = "[" + color("+", fg="green") + "] " print() print(plus + color("found keys:", fg="green")) print() diff --git a/client/src/cmdhfgallagher.c b/client/src/cmdhfgallagher.c index b83bfd455..4165856b0 100644 --- a/client/src/cmdhfgallagher.c +++ b/client/src/cmdhfgallagher.c @@ -1311,6 +1311,52 @@ static int CmdGallagherDecode(const char *cmd) { return PM3_SUCCESS; } +static int CmdGallagherEncode (const char *cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf gallagher encode", + "Encode a Gallagher credential block\n" + "Credential block can be specified with or without the bitwise inverse.", + "hf gallagher encode --rc 1 --fc 22153 --cn 1253518 --il 1" + ); + + void *argtable[] = { + arg_param_begin, + arg_u64_1("r", "rc", "", "Region code. 4 bits max"), + arg_u64_1("f", "fc", "", "Facility code. 2 bytes max"), + arg_u64_1("c", "cn", "", "Card number. 3 bytes max"), + arg_u64_1("i", "il", "", "Issue level. 4 bits max"), + arg_param_end + }; + CLIExecWithReturn(ctx, cmd, argtable, false); + + uint64_t region_code = arg_get_u64(ctx, 1); // uint4, input will be validated later + uint64_t facility_code = arg_get_u64(ctx, 2); // uint16 + uint64_t card_number = arg_get_u64(ctx, 3); // uint24 + uint64_t issue_level = arg_get_u64(ctx, 4); // uint4 + + CLIParserFree(ctx); + + GallagherCredentials_t creds = { + .region_code = region_code, + .facility_code = facility_code, + .card_number = card_number, + .issue_level = issue_level, + }; + + + uint8_t contents[16] = {0}; + + gallagher_encode_creds(contents, &creds); + for (int i = 0; i < 8; i++) { + contents[i + 8] = contents[i] ^ 0xFF; + } + + PrintAndLogEx(SUCCESS, "Raw: " _YELLOW_("%s"), sprint_hex_inrow(contents, ARRAYLEN(contents)/2)); + PrintAndLogEx(SUCCESS, "Bitwise: " _YELLOW_("%s"), sprint_hex_inrow(contents, ARRAYLEN(contents))); + + return PM3_SUCCESS; +} + static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, @@ -1319,6 +1365,7 @@ static command_t CommandTable[] = { {"delete", CmdGallagherDelete, IfPm3Iso14443, "Delete Gallagher credentials from a DESFire card"}, {"diversifykey", CmdGallagherDiversify, AlwaysAvailable, "Diversify Gallagher key"}, {"decode", CmdGallagherDecode, AlwaysAvailable, "Decode Gallagher credential block"}, + {"encode", CmdGallagherEncode, AlwaysAvailable, "Encode Gallagher credential block"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index d252cdac3..dc10cb8c9 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -4131,15 +4131,12 @@ static int CmdHF14AMfSim(const char *Cmd) { uint8_t uid[10] = {0}; CLIGetHexWithReturn(ctx, 1, uid, &uidlen); - char uidsize[9] = {0}; if (uidlen > 0) { FLAG_SET_UID_IN_DATA(flags, uidlen); if (IS_FLAG_UID_IN_EMUL(flags)) { PrintAndLogEx(WARNING, "Invalid parameter for UID"); CLIParserFree(ctx); return PM3_EINVARG; - } else { - snprintf(uidsize, sizeof(uidsize), "%i bytes", uidlen); } } @@ -4247,9 +4244,9 @@ static int CmdHF14AMfSim(const char *Cmd) { } } - PrintAndLogEx(INFO, _YELLOW_("MIFARE %s") " | %s UID " _YELLOW_("%s") "" + PrintAndLogEx(INFO, _YELLOW_("MIFARE %s") " | %i bytes UID " _YELLOW_("%s") "" , csize - , uidsize + , uidlen , (uidlen == 0) ? "n/a" : sprint_hex(uid, uidlen) ); diff --git a/client/src/cmdlfhitaghts.c b/client/src/cmdlfhitaghts.c index e72d7194f..3d0d0a1a7 100644 --- a/client/src/cmdlfhitaghts.c +++ b/client/src/cmdlfhitaghts.c @@ -147,6 +147,13 @@ static int process_hitags_common_args(CLIParserContext *ctx, lf_hitag_data_t *co return PM3_EINVARG; } + uint8_t mode = arg_get_int_def(ctx, 5, 3); + + if (mode > 3) { + PrintAndLogEx(WARNING, "Wrong response protocol mode, expected 0, 1, 2 or 3, got %d", mode); + return PM3_EINVARG; + } + // complete options switch (key_len) { case HITAG_PASSWORD_SIZE: @@ -194,6 +201,21 @@ static int process_hitags_common_args(CLIParserContext *ctx, lf_hitag_data_t *co PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag S") " in Crypto mode"); } + switch (mode) { + case 0: + packet->mode = HITAGS_UID_REQ_STD; + break; + case 1: + packet->mode = HITAGS_UID_REQ_ADV1; + break; + case 2: + packet->mode = HITAGS_UID_REQ_ADV2; + break; + default: + packet->mode = HITAGS_UID_REQ_FADV; + break; + } + return PM3_SUCCESS; } @@ -226,6 +248,9 @@ static void print_error(int8_t reason) { case -10: PrintAndLogEx(FAILED, "Write to page failed!"); break; + case -11: + PrintAndLogEx(FAILED, "Read page failed!"); + break; default: // PM3_REASON_UNKNOWN PrintAndLogEx(DEBUG, "DEBUG: Error - Hitag S failed"); @@ -254,8 +279,9 @@ static int CmdLFHitagSRead(const char *Cmd) { arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), arg_lit0(NULL, "crypto", "crypto mode"), arg_str0("k", "key", "", "pwd or key, 4 or 6 hex bytes"), + arg_int0("m", "mode", "", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"), arg_int0("p", "page", "", "page address to read from"), - arg_int0("c", "count", "", "how many pages to read. '0' reads all pages up to the end page (default: 1)"), + arg_int0("c", "count", "", "how many pages to read. '0' reads all pages up to the end page (def: 1)"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); @@ -264,14 +290,14 @@ static int CmdLFHitagSRead(const char *Cmd) { if (process_hitags_common_args(ctx, &packet) < 0) return PM3_EINVARG; - uint32_t page = arg_get_int_def(ctx, 5, 0); + uint32_t page = arg_get_int_def(ctx, 6, 0); if (page > 255) { PrintAndLogEx(WARNING, "Page address Invalid."); return PM3_EINVARG; } - uint32_t count = arg_get_int_def(ctx, 6, 1); + uint32_t count = arg_get_int_def(ctx, 7, 1); if (count > HITAGS_MAX_PAGES) { PrintAndLogEx(WARNING, "No more than 64 pages can be read at once."); @@ -404,8 +430,10 @@ static int CmdLFHitagSRead(const char *Cmd) { PrintAndLogEx(NORMAL, "Key"); } else PrintAndLogEx(NORMAL, "Data"); - } else - PrintAndLogEx(INFO, "%02u | -- -- -- -- | read failed reason: " _YELLOW_("%d"), page_addr, card->pages_reason[i]); + } else { + PrintAndLogEx(INFO, "% 3u | -- -- -- -- | .... | N/A | " NOLF, page_addr); + print_error(card->pages_reason[i]); + } } PrintAndLogEx(INFO, "----+-------------+-------+------+------"); @@ -438,6 +466,7 @@ static int CmdLFHitagSWrite(const char *Cmd) { arg_str0(NULL, "nrar", "", "nonce / answer writer, 8 hex bytes"), arg_lit0(NULL, "crypto", "crypto mode"), arg_str0("k", "key", "", "pwd or key, 4 or 6 hex bytes"), + arg_int0("m", "mode", "", "response protocol mode. 0 (Standard 00110), 1 (Advanced 11000), 2 (Advanced 11001), 3 (Fast Advanced 11010) (def: 3)"), arg_int1("p", "page", "", "page address to write to"), arg_str1("d", "data", "", "data, 4 hex bytes"), arg_param_end @@ -448,12 +477,12 @@ static int CmdLFHitagSWrite(const char *Cmd) { if (process_hitags_common_args(ctx, &packet) < 0) return PM3_EINVARG; - int page = arg_get_int_def(ctx, 5, 0); + int page = arg_get_int_def(ctx, 6, 0); uint8_t data[HITAGS_PAGE_SIZE]; int data_len = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, 6), data, HITAGS_PAGE_SIZE, &data_len); + int res = CLIParamHexToBuf(arg_get_str(ctx, 7), data, HITAGS_PAGE_SIZE, &data_len); if (res != 0) { CLIParserFree(ctx); return PM3_EINVARG; @@ -538,7 +567,7 @@ static int CmdLFHitagSSim(const char *Cmd) { CLIParserFree(ctx); clearCommandBuffer(); - SendCommandNG(CMD_LF_HITAGS_SIMULATE, NULL, 0); + SendCommandMIX(CMD_LF_HITAGS_SIMULATE, false, 0, 0, NULL, 0); return PM3_SUCCESS; } diff --git a/include/hitag.h b/include/hitag.h index 334ab773d..451952ce4 100644 --- a/include/hitag.h +++ b/include/hitag.h @@ -65,30 +65,6 @@ typedef enum { HT2_LAST_CMD = HT2F_UID_ONLY, } PACKED hitag_function; -typedef struct { - hitag_function cmd; - uint8_t page; - uint8_t page_count; - uint8_t data[HITAGS_PAGE_SIZE]; - uint8_t NrAr[HITAG_NRAR_SIZE]; - // unaligned access to key as uint64_t will abort. - // todo: Why does the compiler without -munaligned-access generate unaligned-access code in the first place? - uint8_t key[HITAG_CRYPTOKEY_SIZE] __attribute__((aligned(4))); - uint8_t pwd[HITAG_PASSWORD_SIZE]; - - // Hitag 1 section. - // will reuse pwd or key field. - uint8_t key_no; - uint8_t logdata_0[4]; - uint8_t logdata_1[4]; - uint8_t nonce[4]; -} PACKED lf_hitag_data_t; - -typedef struct { - int status; - uint8_t data[256]; -} PACKED lf_hitag_crack_response_t; - //--------------------------------------------------------- // Hitag S //--------------------------------------------------------- @@ -111,15 +87,6 @@ typedef enum TAG_STATE { HT_WRITING_BLOCK_DATA } TSATE; -//number of start-of-frame bits -typedef enum SOF_TYPE { - HT_STANDARD = 0, - HT_ADVANCED, - HT_FAST_ADVANCED, - HT_ONE, - HT_NO_BITS -} stype; - typedef struct { // con0 uint8_t MEMT : 2; @@ -156,7 +123,6 @@ struct hitagS_tag { TSATE tstate; // tag-state int max_page; - stype mode; union { uint8_t pages[64][4]; @@ -177,6 +143,33 @@ struct hitagS_tag { } PACKED; +typedef struct { + hitag_function cmd; + uint8_t page; + uint8_t page_count; + uint8_t data[HITAGS_PAGE_SIZE]; + uint8_t NrAr[HITAG_NRAR_SIZE]; + // unaligned access to key as uint64_t will abort. + // todo: Why does the compiler without -munaligned-access generate unaligned-access code in the first place? + uint8_t key[HITAG_CRYPTOKEY_SIZE] __attribute__((aligned(4))); + uint8_t pwd[HITAG_PASSWORD_SIZE]; + + // Hitag 1 section. + // will reuse pwd or key field. + uint8_t key_no; + uint8_t logdata_0[4]; + uint8_t logdata_1[4]; + uint8_t nonce[4]; + + //Hitag s section + uint8_t mode; +} PACKED lf_hitag_data_t; + +typedef struct { + int status; + uint8_t data[256]; +} PACKED lf_hitag_crack_response_t; + typedef struct { union { uint8_t asBytes[HITAGS_PAGE_SIZE];