From c8849af5e0817a1ef1447d2ab6fcc2c46eceabe5 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Mon, 22 Apr 2024 16:20:24 +0200 Subject: [PATCH] This is the major changes made to the HITAG2 commands. Its heavly based on RFIDLers implementation and its been converted to work with Proxmark3. Special thanks to @kevsecurity for his amazing implementations of the Gone in 360 Seconds paper by Roel, Flavio & Balasch. Thanks to @adamlaurie for his RFIDler project. It wouldnt been doable without it. --- CHANGELOG.md | 4 + armsrc/Makefile | 2 +- armsrc/appmain.c | 44 +- armsrc/hitag2.c | 845 ++++++++++++----- armsrc/hitag2.h | 10 +- armsrc/hitagS.c | 462 ++++++---- armsrc/hitagS.h | 5 +- client/CMakeLists.txt | 1 + client/dictionaries/ht2_default.dic | 22 +- client/src/cmdlfhitag.c | 1278 ++++++++++++++++++++++---- client/src/cmdlfhitag.h | 7 +- client/src/cmdtrace.c | 115 ++- client/src/crypto/libpcrypto.c | 88 +- common/hitag2/hitag2_crypto.c | 383 +++++++- common/hitag2/hitag2_crypto.h | 32 +- common/parity.h | 12 +- include/common.h | 11 + include/hitag.h | 33 +- tools/hitag2crack/hitag2_gen_nRaR.py | 26 +- tools/pm3_tests.sh | 1 + 20 files changed, 2688 insertions(+), 693 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 563c6e44bf..4cfb878e03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] +- Changed `lf hitag dump --nrar` - now supports attack 1 from "gone in 360 seconds" paper. Thanks @kevsecurity! (@iceman1001) +- Added `lf hitag selftest` - converted from RFIDLers selftest (@iceman1001) +- Added `lf hitag chk` - dictionary attack against card (@iceman1001) +- Added `lf hitag lookup` - verify collected challenges aginst dictionary (@iceman1001) - Updated windows workflow to use latest setup-wsl script (@iceman1001) - Added a micro second clock in the client (@iceman1001) - Fix `hf mfdes read` - buffer overflow when reading large files (@iceman1001) diff --git a/armsrc/Makefile b/armsrc/Makefile index b42061267f..2f27534ef5 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -71,7 +71,7 @@ else endif ifneq (,$(findstring WITH_HITAG,$(APP_CFLAGS))) - SRC_HITAG = hitag2_crypto.c hitag2.c hitagS.c + SRC_HITAG = hitag2_crypto.c hitag2.c hitagS.c hitag2_crack.c APP_CFLAGS += -I../common/hitag2 else SRC_HITAG = diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 22bdc0ca15..c7f16eadf3 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -40,6 +40,7 @@ #include "thinfilm.h" #include "felica.h" #include "hitag2.h" +#include "hitag2_crack.h" #include "hitagS.h" #include "em4x50.h" #include "em4x70.h" @@ -1131,16 +1132,32 @@ static void PacketReceived(PacketCommandNG *packet) { #ifdef WITH_HITAG case CMD_LF_HITAG_SNIFF: { // Eavesdrop Hitag tag, args = type SniffHitag2(true); -// SniffHitag2(packet->oldarg[0]); + //hitag_sniff(); reply_ng(CMD_LF_HITAG_SNIFF, PM3_SUCCESS, NULL, 0); break; } case CMD_LF_HITAG_SIMULATE: { // Simulate Hitag tag, args = memory content SimulateHitag2(true); + break; + } + case CMD_LF_HITAG2_CRACK: { + lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes; + ht2_crack(payload->NrAr); break; } case CMD_LF_HITAG_READER: { // Reader for Hitag tags, args = type and function - ReaderHitag((hitag_function)packet->oldarg[0], (hitag_data *)packet->data.asBytes, true); + lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes; + + switch (payload->cmd) { + case RHT2F_UID_ONLY: { + ht2_read_uid(NULL, true, true, false); + break; + } + default: { + ReaderHitag(payload, true); + break; + } + } break; } case CMD_LF_HITAGS_SIMULATE: { // Simulate Hitag s tag, args = memory content @@ -1148,25 +1165,28 @@ static void PacketReceived(PacketCommandNG *packet) { break; } case CMD_LF_HITAGS_TEST_TRACES: { // Tests every challenge within the given file - Hitag_check_challenges(packet->data.asBytes, packet->oldarg[0], true); + Hitag_check_challenges(packet->data.asBytes, packet->length, true); break; } - case CMD_LF_HITAGS_READ: { //Reader for only Hitag S tags, args = key or challenge - ReadHitagS((hitag_function)packet->oldarg[0], (hitag_data *)packet->data.asBytes, true); + case CMD_LF_HITAGS_READ: { // Reader for only Hitag S tags, args = key or challenge + lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes; + ReadHitagS(payload, true); break; } - case CMD_LF_HITAGS_WRITE: { //writer for Hitag tags args=data to write,page and key or challenge - if ((hitag_function)packet->oldarg[0] < 10) { - WritePageHitagS((hitag_function)packet->oldarg[0], (hitag_data *)packet->data.asBytes, packet->oldarg[2], true); - } else { - WriterHitag((hitag_function)packet->oldarg[0], (hitag_data *)packet->data.asBytes, packet->oldarg[2], true); - } + case CMD_LF_HITAGS_WRITE: { + lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes; + WritePageHitagS(payload, true); + break; + } + case CMD_LF_HITAG2_WRITE: { + lf_hitag_data_t *payload = (lf_hitag_data_t *) packet->data.asBytes; + WriterHitag(payload, true); break; } case CMD_LF_HITAG_ELOAD: { lf_hitag_t *payload = (lf_hitag_t *) packet->data.asBytes; uint8_t *mem = BigBuf_get_EM_addr(); - memcpy((uint8_t *)mem, payload->data, payload->len); + memcpy(mem, payload->data, payload->len); break; } #endif diff --git a/armsrc/hitag2.c b/armsrc/hitag2.c index ff345afb7a..2fc452c52e 100644 --- a/armsrc/hitag2.c +++ b/armsrc/hitag2.c @@ -146,6 +146,8 @@ static void hitag2_init(void) { #define HITAG_T_TAG_CAPTURE_THREE_HALF 41 #define HITAG_T_TAG_CAPTURE_FOUR_HALF 57 +#define HT2_MAX_NRSZ ((8 * HITAG_FRAME_LEN + 5) * 2) + /* // sim static void hitag_send_bit(int bit, bool ledcontrol) { @@ -319,9 +321,9 @@ static void hitag2_handle_reader_command(uint8_t *rx, const size_t rxlen, uint8_ // reader/writer // returns how long it took -static uint32_t hitag_reader_send_bit(int bit, bool ledcontrol) { +static uint32_t hitag_reader_send_bit(int bit) { uint32_t wait = 0; - if (ledcontrol) LED_A_ON(); + // Binary pulse length modulation (BPLM) is used to encode the data stream // This means that a transmission of a one takes longer than that of a zero @@ -345,17 +347,43 @@ static uint32_t hitag_reader_send_bit(int bit, bool ledcontrol) { wait += HITAG_T_1 - HITAG_T_LOW; } - if (ledcontrol) LED_A_OFF(); return wait; } // reader / writer commands -static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol) { +// frame_len is in number of bits? +static uint32_t hitag_reader_send_frame(const uint8_t *frame, size_t frame_len) { uint32_t wait = 0; // Send the content of the frame for (size_t i = 0; i < frame_len; i++) { - wait += hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1, ledcontrol); + wait += hitag_reader_send_bit((frame[i / 8] >> (7 - (i % 8))) & 1); + } + + // Enable modulation, which means, drop the field + lf_modulation(true); + + // Wait for 4-10 times the carrier period + lf_wait_periods(HITAG_T_LOW); + wait += HITAG_T_LOW; + + // Disable modulation, just activates the field again + lf_modulation(false); + + // t_stop, high field for stop condition (> 36) + lf_wait_periods(HITAG_T_STOP); + wait += HITAG_T_STOP; + return wait; +} + +// reader / writer commands +// frame_len is in number of bits? +static uint32_t hitag_reader_send_framebits(const uint8_t *frame, size_t frame_len) { + + uint32_t wait = 0; + // Send the content of the frame + for (size_t i = 0; i < frame_len; i++) { + wait += hitag_reader_send_bit(frame[i]); } // Enable modulation, which means, drop the field @@ -739,7 +767,7 @@ static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t * ht2_hitag2_cipher_transcrypt(&cipher_state, rx, rxlen / 8, rxlen % 8); } - if (bCrypto && !bAuthenticating && write) { + if (bCrypto && (bAuthenticating == false) && write) { if (hitag2_write_page(rx, rxlen, tx, txlen) == false) { return false; } @@ -808,22 +836,26 @@ static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t * bCrypto = true; bAuthenticating = true; } else { + // stage 2, got config byte+password TAG, discard as will read later if (bAuthenticating) { + bAuthenticating = false; + if (write) { if (hitag2_write_page(rx, rxlen, tx, txlen) == false) { return false; } break; } - } - // stage 2+, got data block - else { + + } else { // stage 2+, got data block + // Store the received block memcpy(tag.sectors[blocknr], rx, 4); blocknr++; } + if (blocknr > 7) { DBG DbpString("Read successful!"); bSuccessful = true; @@ -834,67 +866,99 @@ static bool hitag2_crypto(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t * tx[1] = ((blocknr ^ 7) << 6); } } + break; } - break; - - // Unexpected response default: { DBG Dbprintf("Unknown frame length: " _RED_("%d"), rxlen); return false; } - break; } } - if (bCrypto) { - // We have to return now to avoid double encryption - if (bAuthenticating == false) { - ht2_hitag2_cipher_transcrypt(&cipher_state, tx, *txlen / 8, *txlen % 8); - } + // try to avoid double encryption calls + if (bCrypto && bAuthenticating == false) { + ht2_hitag2_cipher_transcrypt(&cipher_state, tx, *txlen / 8, *txlen % 8); } return true; } -static bool hitag2_authenticate(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) { +static bool hitag2_authenticate(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen, bool write) { // Reset the transmission frame length *txlen = 0; // Try to find out which command was send by selecting on length (in bits) switch (rxlen) { - // No answer, try to resurrect case 0: { + // No answer, try to resurrect // Stop if there is no answer while we are in crypto mode (after sending NrAr) if (bCrypto) { - DBG DbpString("Authentication failed!"); + DBG DbpString("No answer after sending NrAr!"); return false; + } else { + + // Failed during authentication + if (bAuthenticating) { + DBG DbpString("Authentication - failed!"); + return false; + } + + DBG DbpString("Authenticating - send 0xC0"); + *txlen = 5; + memcpy(tx, "\xC0", nbytes(*txlen)); } - *txlen = 5; - memcpy(tx, "\xC0", nbytes(*txlen)); + break; } - break; - - // Received UID, crypto tag answer case 32: { - if (!bCrypto) { + // Received UID or crypto tag answer + if (bCrypto == false) { *txlen = 64; - memcpy(tx, NrAr, 8); + memcpy(tx, NrAr, sizeof(NrAr)); bCrypto = true; + bAuthenticating = true; + DBG DbpString("Authenticating sending NrAr"); } else { DBG DbpString("Authentication successful!"); - return true; + + // stage 2, got config byte+password TAG, discard as will read later + if (bAuthenticating) { + + bAuthenticating = false; + + if (write) { + if (hitag2_write_page(rx, rxlen, tx, txlen) == false) { + return false; + } + break; + } + + } else { // stage 2+, got data block + + // Store the received block + memcpy(tag.sectors[blocknr], rx, 4); + blocknr++; + } + + if (blocknr > 7) { + DBG DbpString("Read successful!"); + bSuccessful = true; + return false; + } else { + + DBG Dbprintf("Sending read block %u", blocknr); + + *txlen = 10; + tx[0] = 0xc0 | (blocknr << 3) | ((blocknr ^ 7) >> 2); + tx[1] = ((blocknr ^ 7) << 6); + } } + break; } - break; - - // Unexpected response default: { - DBG Dbprintf("Unknown frame length: %d", rxlen); + DBG Dbprintf("Unknown frame length: " _RED_("%d"), rxlen); return false; } - break; } - return true; } @@ -936,7 +1000,7 @@ static bool hitag2_test_auth_attempts(uint8_t *rx, const size_t rxlen, uint8_t * memcpy(tx, NrAr, 8); bCrypto = true; } else { - Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x OK", NrAr[0], NrAr[1], NrAr[2], NrAr[3], NrAr[4], NrAr[5], NrAr[6], NrAr[7]); + Dbprintf("auth: %02x%02x%02x%02x%02x%02x%02x%02x ( " _GREEN_("ok") " )", NrAr[0], NrAr[1], NrAr[2], NrAr[3], NrAr[4], NrAr[5], NrAr[6], NrAr[7]); bCrypto = false; if ((auth_table_pos + 8) == auth_table_len) { return false; @@ -948,7 +1012,7 @@ static bool hitag2_test_auth_attempts(uint8_t *rx, const size_t rxlen, uint8_t * break; default: { - Dbprintf("Unknown frame length: %d", rxlen); + Dbprintf("Unknown frame length: " _RED_("%d"), rxlen); return false; } break; @@ -957,53 +1021,25 @@ static bool hitag2_test_auth_attempts(uint8_t *rx, const size_t rxlen, uint8_t * return true; } -static bool hitag2_read_uid(uint8_t *rx, const size_t rxlen, uint8_t *tx, size_t *txlen) { - // Reset the transmission frame length - *txlen = 0; +// Hitag2 Sniffing +void hitag_sniff(void) { - // Try to find out which command was send by selecting on length (in bits) - switch (rxlen) { - // No answer, try to resurrect - case 0: { - // Just starting or if there is no answer - *txlen = 5; - memcpy(tx, "\xC0", nbytes(*txlen)); - } - break; - // Received UID - case 32: { - // Check if we received answer tag (at) - if (bAuthenticating) { - bAuthenticating = false; - } else { - // Store the received block - memcpy(tag.sectors[blocknr], rx, 4); - blocknr++; + FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - DBG Dbhexdump(4, rx, false); - } - if (blocknr > 0) { - DBG DbpString("Read successful!"); - bSuccessful = true; - return true; - } - } - break; - // Unexpected response - default: { - DBG Dbprintf("Unknown frame length: " _RED_("%d"), rxlen); - return false; - } - break; - } - return true; -} + BigBuf_free(); + BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + + // Set up eavesdropping mode, frequency divisor which will drive the FPGA + // and analog mux selection. + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT | FPGA_LF_EDGE_DETECT_TOGGLE_MODE); + FpgaSendCommand(FPGA_CMD_SET_DIVISOR, 95); // 125Khz + SetAdcMuxFor(GPIO_MUXSEL_LOPKD); + RELAY_OFF(); -void EloadHitag(const uint8_t *data, uint16_t len) { - memcpy(tag.sectors, data, sizeof(tag.sectors)); } -// Hitag2 Sniffing // T0 18-22 fc (total time ZERO) // T1 26-32 fc (total time ONE) @@ -1024,8 +1060,7 @@ void SniffHitag2(bool ledcontrol) { lf_init(false, false, ledcontrol); // no logging of the raw signal - // g_logging = lf_get_reader_modulation(); - g_logging = false; + g_logging = true; uint32_t total_count = 0; uint8_t rx[HITAG_FRAME_BIT_COUNT * 2]; @@ -1050,9 +1085,12 @@ void SniffHitag2(bool ledcontrol) { // Detected two sequential equal bits and a modulation switch // NRZ modulation: (11 => --|) or (11 __|) rx[rxlen++] = mod_state; + if (rxlen < sizeof(rx)) { rx[rxlen++] = mod_state; + } // toggle tag modulation state mod_state ^= 1; + } else if (periods > 0 && periods < 24) { // Detected one bit and a modulation switch // NRZ modulation: (1 => -|) or (0 _|) @@ -1060,49 +1098,55 @@ void SniffHitag2(bool ledcontrol) { mod_state ^= 1; } else { mod_state ^= 1; + // The function lf_count_edge_periods() returns > 64 periods, this is not a valid number periods + Dbprintf("Detected unexpected period count... " _YELLOW_("%zu"), periods); break; } + } - if (rxlen == 0) + if (rxlen < 10) { continue; + } // tag sends 11111 + uid, - bool got_tag = ((memcmp(rx, "\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00", 10) == 0)); + bool got_tag = (memcmp(rx, "\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00", 10) == 0); + + Dbprintf("periods... %zu rxlen... %u", periods, rxlen); + Dbhexdump(rxlen, rx, false); if (got_tag) { - // mqnchester decode + bool bad_man = false; uint16_t bitnum = 0; + // mqnchester decode for (uint16_t i = 0; i < rxlen; i += 2) { + if (rx[i] == 1 && (rx[i + 1] == 0)) { rx[bitnum++] = 0; } else if ((rx[i] == 0) && rx[i + 1] == 1) { rx[bitnum++] = 1; } else { bad_man = true; + break; } } - + // Dbprintf(_YELLOW_("TAG") " rxlen... %u bitnum... %u", rxlen, bitnum); if (bad_man) { - DBG DbpString("bad manchester"); - continue; + Dbprintf("bad manchester ( bitnum %u )", bitnum); + continue;; } if (bitnum < 5) { - DBG DbpString("too few bits"); + DbpString("too few bits"); continue; } - // skip header 11111 - uint16_t i = 0; - if (got_tag) { - i = 5; - } - - // Pack the response into a byte array + // Pack the response into a byte array, + // and skip header 11111 (start at idx 5) rxlen = 0; - for (; i < bitnum; i++) { + + for (uint16_t i = 5; i < bitnum; i++) { uint8_t b = rx[i]; rx[rxlen >> 3] |= b << (7 - (rxlen % 8)); rxlen++; @@ -1114,27 +1158,41 @@ void SniffHitag2(bool ledcontrol) { } // nothing to log - if (rxlen == 0) + if (rxlen == 0) { + if (ledcontrol) LED_A_INV(); continue; + } - LogTraceBits(rx, rxlen, 0, 0, false); + LogTraceBits(rx, rxlen, 0, periods, false); total_count += nbytes(rxlen); + } else { - // decode reader comms - LogTrace(rx, rxlen, 0, 0, NULL, true); - total_count += rxlen; - // Pack the response into a byte array - // LogTraceBits(rx, rdr, 0, 0, true); - // total_count += nbytes(rdr); + // nothing to log + if (rxlen < 3) { + if (ledcontrol) LED_A_INV(); + continue; + } + + uint16_t n = 0; + for (uint16_t i = 0; i < rxlen; i++) { + uint8_t b = rx[i]; + rx[n >> 3] |= b << (7 - (n % 8)); + n++; + } + + // decode reader comms + LogTraceBits(rx, n, 0, periods, true); + total_count += nbytes(n); } if (ledcontrol) LED_A_INV(); } lf_finalize(ledcontrol); - Dbprintf("Collected %u bytes", total_count); - + switch_off(); + BigBuf_free(); + } */ // Set up eavesdropping mode, frequency divisor which will drive the FPGA @@ -1145,11 +1203,11 @@ void SniffHitag2(bool ledcontrol) { RELAY_OFF(); // Configure output pin that is connected to the FPGA (for modulating) - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; +// AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; +// AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT; // Disable modulation, we are going to eavesdrop, not modulate ;) - LOW(GPIO_SSC_DOUT); +// LOW(GPIO_SSC_DOUT); // Enable Peripheral Clock for TIMER_CLOCK1, used to capture edges of the reader frames AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); @@ -1168,31 +1226,35 @@ void SniffHitag2(bool ledcontrol) { // Assert a sync signal. This sets all timers to 0 on next active clock edge AT91C_BASE_TCB->TCB_BCR = 1; - int frame_count = 0, response = 0, overflow = 0, lastbit = 1, tag_sof = 4; + int frame_count = 0, response = 0, lastbit = 1, tag_sof = 4; + int overflow = 0; bool rising_edge, reader_frame = false, bSkip = true; - uint8_t rx[HITAG_FRAME_LEN]; + +// bool exit_due_to_overflow; + // HACK -- add one byte to avoid rewriting manchester decoder for edge case + uint8_t rx[HITAG_FRAME_LEN + 1]; size_t rxlen = 0; auth_table_len = 0; auth_table_pos = 0; - // Reset the received frame, frame count and timing info - memset(rx, 0x00, sizeof(rx)); - - auth_table = (uint8_t *)BigBuf_malloc(AUTH_TABLE_LENGTH); - memset(auth_table, 0x00, AUTH_TABLE_LENGTH); + auth_table = (uint8_t *)BigBuf_calloc(AUTH_TABLE_LENGTH); while (BUTTON_PRESS() == false) { WDT_HIT(); - memset(rx, 0x00, sizeof(rx)); + +// bool exit_due_to_overflow = false; // Receive frame, watch for at most T0 * EOF periods while (AT91C_BASE_TC1->TC_CV < (HITAG_T0 * HITAG_T_EOF)) { + // Check if rising edge in modulation is detected if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Retrieve the new timing values - int ra = (AT91C_BASE_TC1->TC_RA / HITAG_T0); + int ra = (AT91C_BASE_TC1->TC_RA / HITAG_T0) + overflow; + overflow = 0; // Find out if we are dealing with a rising or falling edge rising_edge = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_FRAME) > 0; @@ -1206,7 +1268,7 @@ void SniffHitag2(bool ledcontrol) { } // Only handle if reader frame and rising edge, or tag frame and falling edge - if (reader_frame == rising_edge) { + if (reader_frame != rising_edge) { overflow += ra; continue; } @@ -1216,6 +1278,7 @@ void SniffHitag2(bool ledcontrol) { overflow = 0; if (reader_frame) { + if (ledcontrol) LED_B_ON(); // Capture reader frame if (ra >= HITAG_T_STOP) { @@ -1224,6 +1287,7 @@ void SniffHitag2(bool ledcontrol) { // } // Capture the T0 periods that have passed since last communication or field drop (reset) response = (ra - HITAG_T_LOW); + if (rxlen != 0) { Dbprintf("ra - HITAG_T_LOW... %i", response); } } else if (ra >= HITAG_T_1_MIN) { // '1' bit @@ -1279,15 +1343,16 @@ void SniffHitag2(bool ledcontrol) { } } } - } + } // end while // Check if frame was captured if (rxlen) { + frame_count++; LogTraceBits(rx, rxlen, response, 0, reader_frame); // Check if we recognize a valid authentication attempt - if (nbytes(rxlen) == 8) { + if (rxlen == 64) { // Store the authentication attempt if (auth_table_len < (AUTH_TABLE_LENGTH - 8)) { memcpy(auth_table + auth_table_len, rx, 8); @@ -1295,6 +1360,11 @@ void SniffHitag2(bool ledcontrol) { } } + if (ledcontrol) { + LED_B_OFF(); + LED_C_OFF(); + } + response = 0; reader_frame = false; lastbit = 1; @@ -1317,13 +1387,13 @@ void SniffHitag2(bool ledcontrol) { // Reset the timer to restart while-loop that receives frames AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; // Assert a sync signal. This sets all timers to 0 on next active clock edge AT91C_BASE_TCB->TCB_BCR = 1; } if (ledcontrol) LEDsoff(); + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; @@ -1334,6 +1404,7 @@ void SniffHitag2(bool ledcontrol) { BigBuf_free(); } + // Hitag2 simulation void SimulateHitag2(bool ledcontrol) { @@ -1468,7 +1539,7 @@ void SimulateHitag2(bool ledcontrol) { } else { reader_modulation ^= 1; // The function lf_count_edge_periods() returns > 64 periods, this is not a valid number periods - Dbprintf("Detected unexpected period count: %d", periods); + Dbprintf("Detected unexpected period count: %zu", periods); break; } } @@ -1488,6 +1559,10 @@ void SimulateHitag2(bool ledcontrol) { // The last modulation change of a zero is not detected, but we should take // the half period in account, otherwise the demodulator will fail. if ((nrzs % 2) != 0) { + if (nrzs >= max_nrzs) { + Dbprintf("max_nrzs (%d) is odd? Must be even!", max_nrzs); // should be a static assert above + continue; + } nrz_samples[nrzs++] = reader_modulation; } @@ -1558,7 +1633,7 @@ void SimulateHitag2(bool ledcontrol) { // reply_ng(CMD_LF_HITAG_SIMULATE, (checked == -1) ? PM3_EOPABORTED : PM3_SUCCESS, (uint8_t *)tag.sectors, tag_size); } -void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { +void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol) { uint32_t command_start = 0, command_duration = 0; uint32_t response_start = 0, response_duration = 0; @@ -1587,7 +1662,7 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { clear_trace(); // Check configuration - switch (htf) { + switch (payload->cmd) { case RHT1F_PLAIN: { DBG Dbprintf("Read public blocks in plain mode"); // this part will be unreadable @@ -1597,15 +1672,19 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { } case RHT1F_AUTHENTICATE: { DBG Dbprintf("Read all blocks in authed mode"); - memcpy(nonce, htd->ht1auth.nonce, 4); - memcpy(key, htd->ht1auth.key, 4); - memcpy(logdata_0, htd->ht1auth.logdata_0, 4); - memcpy(logdata_1, htd->ht1auth.logdata_1, 4); + + memcpy(nonce, payload->nonce, 4); + memcpy(key, payload->key, 4); + memcpy(logdata_0, payload->logdata_0, 4); + memcpy(logdata_1, payload->logdata_1, 4); + // TEST memset(nonce, 0x0, 4); memset(logdata_1, 0x00, 4); + byte_value = 0; - key_no = htd->ht1auth.key_no; + key_no = payload->key_no; + DBG Dbprintf("Authenticating using key #%u :", key_no); DBG Dbhexdump(4, key, false); DBG DbpString("Nonce:"); @@ -1619,31 +1698,34 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { } case RHT2F_PASSWORD: { DBG Dbprintf("List identifier in password mode"); - if (memcmp(htd->pwd.password, "\x00\x00\x00\x00", 4) == 0) + if (memcmp(payload->pwd, "\x00\x00\x00\x00", 4) == 0) { memcpy(password, tag.sectors[1], sizeof(password)); - else - memcpy(password, htd->pwd.password, sizeof(password)); - + } else { + memcpy(password, payload->pwd, sizeof(password)); + } blocknr = 0; bPwd = false; bAuthenticating = false; break; } case RHT2F_AUTHENTICATE: { - DBG DbpString("Authenticating using nr,ar pair:"); - memcpy(NrAr, htd->auth.NrAr, 8); + DBG DbpString("Authenticating using NrAr pair:"); + memcpy(NrAr, payload->NrAr, 8); DBG Dbhexdump(8, NrAr, false); + // We can't read block 0, 1, 2.. + blocknr = 3; bCrypto = false; + bPwd = false; bAuthenticating = false; break; } case RHT2F_CRYPTO: { DBG DbpString("Authenticating using key:"); - memcpy(key, htd->crypto.key, 6); //HACK; 4 or 6?? I read both in the code. + memcpy(key, payload->key, 6); //HACK; 4 or 6?? I read both in the code. DBG Dbhexdump(6, key, false); DBG DbpString("Nonce:"); DBG Dbhexdump(4, nonce, false); - memcpy(nonce, htd->crypto.data, 4); + memcpy(nonce, payload->data, 4); blocknr = 0; bCrypto = false; bAuthenticating = false; @@ -1656,15 +1738,10 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { bCrypto = false; break; } - case RHT2F_UID_ONLY: { - blocknr = 0; - bCrypto = false; - bAuthenticating = false; - break; - } default: { - DBG Dbprintf("Error, unknown function: %d", htf); + DBG Dbprintf("Error, unknown function: " _RED_("%d"), payload->cmd); set_tracing(false); + reply_ng(CMD_LF_HITAG_READER, PM3_ESOFT, NULL, 0); return; } } @@ -1674,11 +1751,9 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { // hitag2 state machine? hitag2_init(); - uint8_t attempt_count = 0; - // Tag specific configuration settings (sof, timings, etc.) // TODO HTS - /* if (htf <= HTS_LAST_CMD) { + /* if (payload->cmd <= HTS_LAST_CMD) { // hitagS settings t_wait_1 = 204; t_wait_2 = 128; @@ -1686,14 +1761,14 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { tag_size = 8; DBG DbpString("Configured for " _YELLOW_("HitagS") " reader"); } else */ - if (htf <= HT1_LAST_CMD) { + if (payload->cmd <= HT1_LAST_CMD) { // hitag1 settings t_wait_1 = 204; t_wait_2 = 128; tag_size = 256; flipped_bit = 0; - DBG DbpString("Configured for hitag1 reader"); - } else if (htf <= HT2_LAST_CMD) { + DBG DbpString("Configured for " _YELLOW_("Hitag 1") " reader"); + } else if (payload->cmd <= HT2_LAST_CMD) { // hitag2 settings t_wait_1 = HITAG_T_WAIT_1_MIN; t_wait_2 = HITAG_T_WAIT_2_MIN; @@ -1733,7 +1808,7 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { // By default reset the transmission buffer tx = txbuf; - switch (htf) { + switch (payload->cmd) { case RHT1F_PLAIN: { bStop = !hitag_plain(rx, rxlen, tx, &txlen, false); break; @@ -1747,7 +1822,7 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { break; } case RHT2F_AUTHENTICATE: { - bStop = !hitag2_authenticate(rx, rxlen, tx, &txlen); + bStop = !hitag2_authenticate(rx, rxlen, tx, &txlen, false); break; } case RHT2F_CRYPTO: { @@ -1758,20 +1833,12 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { bStop = !hitag2_test_auth_attempts(rx, rxlen, tx, &txlen); break; } - case RHT2F_UID_ONLY: { - bStop = !hitag2_read_uid(rx, rxlen, tx, &txlen); - - attempt_count++; //attempt 3 times to get uid then quit - if ((bStop == false) && (attempt_count == 3)) { - bStop = true; - } - break; - } default: { - DBG Dbprintf("Error, unknown function: %d", htf); + DBG Dbprintf("Error, unknown function: " _RED_("%d"), payload->cmd); goto out; } } + if (bStop) { break; } @@ -1791,7 +1858,7 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { } // Transmit the reader frame - command_duration = hitag_reader_send_frame(tx, txlen, ledcontrol); + command_duration = hitag_reader_send_frame(tx, txlen); response_start = command_start + command_duration; // Let the antenna and ADC values settle @@ -1855,7 +1922,9 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { // Detected two sequential equal bits and a modulation switch // NRZ modulation: (11 => --|) or (11 __|) nrz_samples[nrzs++] = tag_modulation; - nrz_samples[nrzs++] = tag_modulation; + if (nrzs < max_nrzs) { + nrz_samples[nrzs++] = tag_modulation; + } response_duration += periods; // Invert tag modulation state tag_modulation ^= 1; @@ -1894,6 +1963,11 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { // The last modulation change of a zero is not detected, but we should take // the half period in account, otherwise the demodulator will fail. if ((nrzs % 2) != 0) { + + if (nrzs >= max_nrzs) { + DBG Dbprintf("max_nrzs ( " _YELLOW_("%zu") " ) is odd? Must be even!", max_nrzs); + continue; + } nrz_samples[nrzs++] = tag_modulation; } @@ -1955,19 +2029,19 @@ void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol) { // release allocated memory from BigBuff. BigBuf_free(); - // + if (checked == -1) { - // user interupted - reply_mix(CMD_ACK, false, 0, 0, 0, 0); + reply_ng(CMD_LF_HITAG_READER, PM3_ESOFT, NULL, 0); } - if (bSuccessful) - reply_mix(CMD_ACK, bSuccessful, 0, 0, (uint8_t *)tag.sectors, tag_size); - else - reply_mix(CMD_ACK, bSuccessful, 0, 0, 0, 0); + reply_ng(CMD_LF_HITAG_READER + , (bSuccessful) ? PM3_SUCCESS : PM3_EFAILED + , (uint8_t *)tag.sectors + , tag_size + ); } -void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledcontrol) { +void WriterHitag(const lf_hitag_data_t *payload, bool ledcontrol) { uint32_t command_start = 0; uint32_t command_duration = 0; @@ -1991,6 +2065,8 @@ void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledco // Raw demodulation/decoding by sampling edge periods size_t periods = 0; + // iceman: Hitag2 is filled with static global vars. + // these following are globals status indicator :-| // Reset the return status bSuccessful = false; @@ -2001,33 +2077,35 @@ void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledco set_tracing(true); clear_trace(); - // Check configuration - switch (htf) { + switch (payload->cmd) { case WHT2F_CRYPTO: { DbpString("Authenticating using key:"); - memcpy(key, htd->crypto.key, 6); //HACK; 4 or 6?? I read both in the code. - memcpy(writedata, htd->crypto.data, 4); + memcpy(key, payload->key, 6); //HACK; 4 or 6?? I read both in the code. + memcpy(writedata, payload->data, 4); Dbhexdump(6, key, false); - blocknr = page; + blocknr = payload->page; bCrypto = false; bAuthenticating = false; - writestate = WRITE_STATE_START; } break; case WHT2F_PASSWORD: { - DbpString("Authenticating using password:"); - memcpy(password, htd->pwd.password, 4); - memcpy(writedata, htd->crypto.data, 4); - Dbhexdump(4, password, false); - blocknr = page; + DBG DbpString("Authenticating using password:"); + if (memcmp(payload->pwd, "\x00\x00\x00\x00", 4) == 0) { + memcpy(password, tag.sectors[1], sizeof(password)); + } else { + memcpy(password, payload->pwd, sizeof(password)); + } + memcpy(writedata, payload->data, 4); + DBG Dbhexdump(4, password, false); + blocknr = payload->page; bPwd = false; bAuthenticating = false; - writestate = WRITE_STATE_START; } break; default: { - Dbprintf("Error, unknown function: %d", htf); + Dbprintf("Error, unknown function: " _RED_("%d"), payload->cmd); + reply_ng(CMD_LF_HITAG2_WRITE, PM3_ESOFT, NULL, 0); return; } break; @@ -2043,31 +2121,30 @@ void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledco // Tag specific configuration settings (sof, timings, etc.) // TODO HTS - /* if (htf <= HTS_LAST_CMD) { + /* if (payload->cmd <= HTS_LAST_CMD) { // hitagS settings t_wait_1 = 204; t_wait_2 = 128; //tag_size = 256; flipped_bit = 0; tag_size = 8; - DbpString("Configured for hitagS writer"); - } else */ -// TODO HT1 - /* if (htf <= HT1_LAST_CMD) { - // hitag1 settings - t_wait_1 = 204; - t_wait_2 = 128; - tag_size = 256; - flipped_bit = 0; - DbpString("Configured for hitag1 writer"); - } else */ -// if (htf <= HT2_LAST_CMD) { - // hitag2 settings - t_wait_1 = HITAG_T_WAIT_1_MIN; - t_wait_2 = HITAG_T_WAIT_2_MIN; - tag_size = 48; - DbpString("Configured for hitag2 writer"); -// } + DBG DbpString("Configured for " _YELLOW_("HitagS") " writer"); + } else + */ + if (payload->cmd <= HT1_LAST_CMD) { + // hitag1 settings + t_wait_1 = 204; + t_wait_2 = 128; + tag_size = 256; + flipped_bit = 0; + DBG DbpString("Configured for " _YELLOW_("Hitag 1") " writer"); + } else if (payload->cmd <= HT2_LAST_CMD) { + // hitag2 settings + t_wait_1 = HITAG_T_WAIT_1_MIN; + t_wait_2 = HITAG_T_WAIT_2_MIN; + tag_size = 48; + DBG DbpString("Configured for " _YELLOW_("Hitag 2") " writer"); + } uint8_t tag_modulation; size_t max_nrzs = (8 * HITAG_FRAME_LEN + 5) * 2; // up to 2 nrzs per bit @@ -2097,7 +2174,8 @@ void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledco // By default reset the transmission buffer tx = txbuf; - switch (htf) { + + switch (payload->cmd) { case WHT2F_CRYPTO: { bStop = !hitag2_crypto(rx, rxlen, tx, &txlen, true); break; @@ -2107,7 +2185,6 @@ void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledco break; } default: { - Dbprintf("Error, unknown function: %d", htf); goto out; } } @@ -2131,7 +2208,16 @@ void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledco } // Transmit the reader frame - command_duration = hitag_reader_send_frame(tx, txlen, ledcontrol); + command_duration = hitag_reader_send_frame(tx, txlen); + + // global write state variable used + // tearoff occurred + if ((writestate == WRITE_STATE_PROG) && (tearoff_hook() == PM3_ETEAROFF)) { + reply_ng(CMD_LF_HITAG2_WRITE, PM3_ETEAROFF, NULL, 0); + lf_finalize(ledcontrol); + BigBuf_free(); + return; + } response_start = command_start + command_duration; @@ -2195,7 +2281,9 @@ void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledco // Detected two sequential equal bits and a modulation switch // NRZ modulation: (11 => --|) or (11 __|) nrz_samples[nrzs++] = tag_modulation; - nrz_samples[nrzs++] = tag_modulation; + if (nrzs < max_nrzs) { + nrz_samples[nrzs++] = tag_modulation; + } response_duration += periods; // Invert tag modulation state tag_modulation ^= 1; @@ -2236,7 +2324,13 @@ void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledco // The last modulation change of a zero is not detected, but we should take // the half period in account, otherwise the demodulator will fail. if ((nrzs % 2) != 0) { - nrz_samples[nrzs++] = tag_modulation; + + if (nrzs >= max_nrzs) { + Dbprintf("max_nrzs ( " _YELLOW_("%zu") " ) is odd? Must be even!", max_nrzs); + continue; + } else { + nrz_samples[nrzs++] = tag_modulation; + } } if (ledcontrol) LED_B_ON(); @@ -2299,8 +2393,335 @@ void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledco BigBuf_free(); if (checked == -1) { - reply_mix(CMD_ACK, false, 0, 0, 0, 0); + reply_ng(CMD_LF_HITAG2_WRITE, PM3_ESOFT, NULL, 0); + } + + reply_ng(CMD_LF_HITAG2_WRITE + , (bSuccessful) ? PM3_SUCCESS : PM3_EFAILED + , (uint8_t *)tag.sectors + , tag_size + ); +} + + +static void ht2_send(bool turn_on, uint32_t *cmd_start + , uint32_t *cmd_duration, uint32_t *resp_start + , uint8_t *tx, size_t txlen, bool send_bits) { + + // Tag specific configuration settings (sof, timings, etc.) HITAG2 Settings +#define T_WAIT_1_GUARD 8 + + if (turn_on) { + // Wait 50ms with field off to be sure the transponder gets reset + SpinDelay(50); + FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + + // Wait with field on to be in "Wait for START_AUTH" timeframe + lf_wait_periods(HITAG_T_WAIT_POWERUP + HITAG_T_WAIT_START_AUTH_MAX / 4); + *cmd_start += HITAG_T_WAIT_POWERUP + HITAG_T_WAIT_START_AUTH_MAX / 4; + } else { - reply_mix(CMD_ACK, bSuccessful, 0, 0, (uint8_t *)tag.sectors, tag_size); + // Wait for t_wait_2 carrier periods after the last tag bit before transmitting, + lf_wait_periods(HITAG_T_WAIT_2_MIN + HITAG_T_WAIT_2_MIN); + *cmd_start += (HITAG_T_WAIT_2_MIN + HITAG_T_WAIT_2_MIN); + } + + // Transmit the reader frame + if (send_bits) { + *cmd_duration = hitag_reader_send_framebits(tx, txlen); + } else { + *cmd_duration = hitag_reader_send_frame(tx, txlen); + } + + *resp_start = (*cmd_start + *cmd_duration); + + *resp_start += (HITAG_T_WAIT_1_MIN - T_WAIT_1_GUARD); + // Let the antenna and ADC values settle + // And find the position where edge sampling should start + lf_wait_periods(HITAG_T_WAIT_1_MIN - T_WAIT_1_GUARD); +} + +static bool ht2_receive(uint32_t *resp_start, uint32_t *resp_duration, uint8_t *nrz_samples, size_t *samples) { + + // Keep administration of the first edge detection + bool waiting_for_first_edge = true; + + // Did we detected any modulaiton at all + bool detected_tag_modulation = false; + + // Reset the number of NRZ samples and use edge detection to detect them + size_t nrzs = 0; + + // Use the current modulation state as starting point + uint8_t tag_modulation = lf_get_tag_modulation(); + + // Raw demodulation/decoding by sampling edge periods + + while (nrzs < HT2_MAX_NRSZ) { + + // Get the timing of the next edge in number of wave periods + size_t periods = lf_count_edge_periods(128); + + // Are we dealing with the first incoming edge + if (waiting_for_first_edge) { + + // Just break out of loop after an initial time-out (tag is probably not available) + if (periods == 0) { + break; + } + + if (tag_modulation == 0) { + // hitag replies always start with 11111 == 1010101010, if we see 0 + // it means we missed the first period, e.g. if the signal never crossed 0 since reader signal + // so let's add it: + nrz_samples[nrzs++] = tag_modulation ^ 1; + // Register the number of periods that have passed + // we missed the begin of response but we know it happened one period of 16 earlier + resp_start += (periods - 16); + resp_duration = resp_start; + + } else { + // Register the number of periods that have passed + resp_start += periods; + resp_duration = resp_start; + } + + // Indicate that we have dealt with the first edge + waiting_for_first_edge = false; + // The first edge is always a single NRZ bit, force periods on 16 + periods = 16; + // We have received more than 0 periods, so we have detected a tag response + detected_tag_modulation = true; + + } else { + // The function lf_count_edge_periods() returns 0 when a time-out occurs + if (periods == 0) { + break; + } + } + // Evaluate the number of periods before the next edge + if (periods > 24 && periods <= 64) { + // Detected two sequential equal bits and a modulation switch + // NRZ modulation: (11 => --|) or (11 __|) + nrz_samples[nrzs++] = tag_modulation; + + if (nrzs < HT2_MAX_NRSZ) { + nrz_samples[nrzs++] = tag_modulation; + } + + resp_duration += periods; + // Invert tag modulation state + tag_modulation ^= 1; + + } else if (periods > 0 && periods <= 24) { + // Detected one bit and a modulation switch + // NRZ modulation: (1 => -|) or (0 _|) + nrz_samples[nrzs++] = tag_modulation; + + resp_duration += periods; + + tag_modulation ^= 1; + + } else { + // The function lf_count_edge_periods() returns > 64 periods, this is not a valid number periods + break; + } + } + + // Make sure we always have an even number of samples. This fixes the problem + // of ending the manchester decoding with a zero. See the example below where + // the '|' character is end of modulation + // One at the end: ..._-|_____... + // Zero at the end: ...-_|_____... + // The last modulation change of a zero is not detected, but we should take + // the half period in account, otherwise the demodulator will fail. + if ((nrzs % 2) != 0) { + + if (nrzs >= HT2_MAX_NRSZ) { + return false; + } + + nrz_samples[nrzs++] = tag_modulation; + } + + *samples = nrzs; + + return detected_tag_modulation; +} + +bool ht2_packbits(uint8_t *nrz_samples, size_t nrzs, uint8_t *rx, size_t *rxlen) { + // Verify if the header consists of five consecutive ones + if (nrzs < 5) { + return false; + } + + // detect hitag2 header + if (memcmp(nrz_samples, "\x01\x01\x01\x01\x01", 5)) { + return false; + } + + // Pack the response into a byte array + for (size_t i = 5; i < nrzs && *rxlen < (HITAG_FRAME_LEN << 3); i++) { + + uint8_t bit = nrz_samples[i]; + + // When Manchester detects impossible symbol it writes "7" + if (bit > 1) { + break; + } + + rx[*rxlen >> 3] |= bit << (7 - (*rxlen % 8)); + *rxlen = *rxlen + 1; + } + + // skip spurious bit + if (*rxlen % 8 == 1) { + *rxlen = *rxlen - 1; + } + return true; +} + +int ht2_read_uid(uint8_t *uid, bool ledcontrol, bool send_answer, bool keep_field_up) { + + // Clean up trace and prepare it for storing frames + set_tracing(true); + + // keep field up indicates there are more traffic to be done. + if (keep_field_up == false) { + clear_trace(); + } + + // hitag2 state machine? + hitag2_init(); + + // init as reader + lf_init(true, false, true); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + uint8_t rx[HITAG_FRAME_LEN] = {0}; + size_t rxlen = 0; // In number of bits + + uint8_t nrz_samples[HT2_MAX_NRSZ]; + + uint8_t attempt_count = 3; + + int res = PM3_EFAILED; + bool turn_on = true; + + while (attempt_count && BUTTON_PRESS() == false) { + + attempt_count--; + + WDT_HIT(); + + uint32_t command_start = 0, command_duration = 0; + uint32_t response_start = 0, response_duration = 0; + + // start AUTH command + size_t txlen = 5; + uint8_t tx[1] = {0xC0}; + + // Transmit as reader + ht2_send(turn_on, &command_start, &command_duration, &response_start, tx, txlen, false); + + turn_on = false; + + // Reset the number of NRZ samples and use edge detection to detect them + size_t nrzs = 0; + + // receive raw samples + if (ht2_receive(&response_start, &response_duration, nrz_samples, &nrzs) == false) { + continue;; + } + + // Store the transmit frame ( TX ), we do this now at this point, to avoid delay in processing + // and to be able to overwrite the first samples with the trace (since they currently + // still use the same memory space) + LogTraceBits(tx, txlen, command_start, command_start + command_duration, true); + + // decode raw samples from Manchester Encoded to bits + manrawdecode(nrz_samples, &nrzs, true, 0); + + // pack bits to bytes + if (ht2_packbits(nrz_samples, nrzs, rx, &rxlen) == false) { + continue; + } + + // log Receive data + LogTraceBits(rx, rxlen, response_start, response_start + response_duration, false); + + if (rxlen != 32) { + continue; + } + + // Store received UID + memcpy(tag.sectors[0], rx, 4); + if (uid) { + memcpy(uid, rx, 4); + } + res = PM3_SUCCESS; + break; + } + + if (keep_field_up == false) { + lf_finalize(false); + BigBuf_free_keep_EM(); + } + + if (send_answer) { + reply_ng(CMD_LF_HITAG_READER, res, (uint8_t *)tag.sectors, 4); + } + + return res; +} + +// This function assumes you have called hitag2_read_uid before to turn on the field :) +// tx = expects bin arrays 0,1 i +// txlen = number of bits to send +// rx = return bin arrys +// rxlen = number of bits returned +int ht2_tx_rx(uint8_t *tx, size_t txlen, uint8_t *rx, size_t *rxlen, bool ledcontrol, bool keep_field_up) { + + int res = PM3_EFAILED; + size_t nrzs = 0; + uint8_t samples[HT2_MAX_NRSZ]; + + // waith between sending commands + lf_wait_periods(HITAG_T_WAIT_2_MIN); + + WDT_HIT(); + + uint32_t command_start = 0, command_duration = 0; + uint32_t response_start = 0, response_duration = 0; + + // Transmit as reader + ht2_send(false, &command_start, &command_duration, &response_start, tx, txlen, true); + + // receive raw samples + if (ht2_receive(&response_start, &response_duration, samples, &nrzs) == false) { + goto out; + } + + // decode raw samples from Manchester Encoded to bits + if ( manrawdecode(samples, &nrzs, true, 0) ) { + goto out; + } + + // pack bits to bytes + if (ht2_packbits(samples, nrzs, rx, rxlen) == false) { + goto out; + } + + // log Receive data + LogTraceBits(rx, *rxlen, response_start, response_start + response_duration, false); + + res = PM3_SUCCESS; + +out: + if (keep_field_up == false) { + lf_finalize(false); + BigBuf_free_keep_EM(); } + return res; } diff --git a/armsrc/hitag2.h b/armsrc/hitag2.h index 90aa8132b4..f2f7fff06a 100644 --- a/armsrc/hitag2.h +++ b/armsrc/hitag2.h @@ -23,8 +23,12 @@ #include "hitag.h" void SniffHitag2(bool ledcontrol); +void hitag_sniff(void); void SimulateHitag2(bool ledcontrol); -void ReaderHitag(hitag_function htf, const hitag_data *htd, bool ledcontrol); -void WriterHitag(hitag_function htf, const hitag_data *htd, int page, bool ledcontrol); -void EloadHitag(const uint8_t *data, uint16_t len); +void ReaderHitag(const lf_hitag_data_t *payload, bool ledcontrol); +void WriterHitag(const lf_hitag_data_t *payload, bool ledcontrol); + +bool ht2_packbits(uint8_t *nrz_samples, size_t nrzs, uint8_t *rx, size_t *rxlen); +int ht2_read_uid(uint8_t *uid, bool ledcontrol, bool send_answer, bool keep_field_up); +int ht2_tx_rx(uint8_t *tx, size_t txlen, uint8_t *rx, size_t *rxlen, bool ledcontrol, bool keep_field_up); #endif diff --git a/armsrc/hitagS.c b/armsrc/hitagS.c index 0c24daeaca..16ba8ef114 100644 --- a/armsrc/hitagS.c +++ b/armsrc/hitagS.c @@ -52,7 +52,7 @@ static uint32_t temp_uid; static int temp2 = 0; static int sof_bits; // number of start-of-frame bits static uint8_t pwdh0, pwdl0, pwdl1; // password bytes -static uint32_t rnd = 0x74124485; // randomnumber +static uint32_t rnd = 0x74124485; // random number //#define SENDBIT_TEST /* array index 3 2 1 0 // bytes in sim.bin file are 0 1 2 3 @@ -125,12 +125,13 @@ static void calc_crc(unsigned char *crc, unsigned char data, unsigned char Bitco } static void hitag_send_bit(int bit, bool ledcontrol) { + if (ledcontrol) LED_A_ON(); // Reset clock for the next bit AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; switch (m) { - case AC2K: + case AC2K: { if (bit == 0) { // AC Coding --__ HIGH(GPIO_SSC_DOUT); @@ -156,7 +157,8 @@ static void hitag_send_bit(int bit, bool ledcontrol) { } if (ledcontrol) LED_A_OFF(); break; - case AC4K: + } + case AC4K: { if (bit == 0) { // AC Coding --__ HIGH(GPIO_SSC_DOUT); @@ -181,7 +183,8 @@ static void hitag_send_bit(int bit, bool ledcontrol) { } if (ledcontrol) LED_A_OFF(); break; - case MC4K: + } + case MC4K: { if (bit == 0) { // Manchester: Unloaded, then loaded |__--| LOW(GPIO_SSC_DOUT); @@ -201,7 +204,8 @@ static void hitag_send_bit(int bit, bool ledcontrol) { } if (ledcontrol) LED_A_OFF(); break; - case MC8K: + } + case MC8K: { if (bit == 0) { // Manchester: Unloaded, then loaded |__--| LOW(GPIO_SSC_DOUT); @@ -221,26 +225,33 @@ static void hitag_send_bit(int bit, bool ledcontrol) { } if (ledcontrol) LED_A_OFF(); break; - default: + } + default: { break; + } } } static void hitag_send_frame(const uint8_t *frame, size_t frame_len, bool ledcontrol) { - if (g_dbglevel >= DBG_EXTENDED) + + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("hitag_send_frame: (%i) %02X %02X %02X %02X", frame_len, frame[0], frame[1], frame[2], frame[3]); + } + // The beginning of the frame is hidden in some high level; pause until our bits will have an effect AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; HIGH(GPIO_SSC_DOUT); switch (m) { case AC4K: - case MC8K: + case MC8K: { while (AT91C_BASE_TC0->TC_CV < T0 * 40) {}; //FADV break; + } case AC2K: - case MC4K: + case MC4K: { while (AT91C_BASE_TC0->TC_CV < T0 * 20) {}; //STD + ADV break; + } } // SOF - send start of frame @@ -317,43 +328,101 @@ static void hitag_reader_send_frame(const uint8_t *frame, size_t frame_len, bool LOW(GPIO_SSC_DOUT); } +static void hitagS_init_clock(void) { + + // Enable Peripheral Clock for + // TIMER_CLOCK0, used to measure exact timing before answering + // TIMER_CLOCK1, used to capture edges of the tag frames + AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); + + AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; + + // Disable timer during configuration + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; + + // TC0: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers + AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; + + // TC1: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, + // external trigger rising edge, load RA on falling edge of TIOA. + AT91C_BASE_TC1->TC_CMR = + AT91C_TC_CLKS_TIMER_DIV1_CLOCK | + AT91C_TC_ETRGEDG_FALLING | + AT91C_TC_ABETRG | + AT91C_TC_LDRA_FALLING | + AT91C_TC_ACPA_CLEAR | // RA comperator clears TIOA (carry bit) + AT91C_TC_ASWTRG_SET; // SWTriger sets TIOA (carry bit) + + AT91C_BASE_TC0->TC_RC = 0; // set TIOA (carry bit) on overflow, return to zero + AT91C_BASE_TC0->TC_RA = 1; // clear carry bit on next clock cycle + + // Enable and reset counters + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; + + // synchronized startup procedure + while (AT91C_BASE_TC0->TC_CV > 0); // wait until TC0 returned to zero +// while (AT91C_BASE_TC0->TC_CV < 2); // and has started (TC_CV > TC_RA, now TC1 is cleared) + + // return to zero + AT91C_BASE_TC1->TC_CCR = AT91C_TC_SWTRG; + AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + while (AT91C_BASE_TC0->TC_CV > 0); + +} + +static void hitagS_stop_clock(void) { + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; +} + /* * to check if the right uid was selected */ static int check_select(const uint8_t *rx, uint32_t uid) { + unsigned char resp[48]; uint32_t ans = 0x0; - for (int i = 0; i < 48; i++) + + for (int i = 0; i < 48; i++) { resp[i] = (rx[i / 8] >> (7 - (i % 8))) & 0x1; + } - for (int i = 0; i < 32; i++) + for (int i = 0; i < 32; i++) { ans += resp[5 + i] << (31 - i); + } // global var? temp_uid = ans; - if (ans == tag.uid) + if (ans == tag.uid) { return 1; + } return 0; } static void hitagS_set_frame_modulation(void) { switch (tag.mode) { - case HT_STANDARD: + case HT_STANDARD: { sof_bits = 1; m = MC4K; break; - case HT_ADVANCED: + } + case HT_ADVANCED: { sof_bits = 6; m = MC4K; break; - case HT_FAST_ADVANCED: + } + case HT_FAST_ADVANCED: { sof_bits = 6; m = MC8K; break; - default: + } + default: { break; + } } } @@ -411,16 +480,18 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, for (int i = 0; i < 4; i++) { tx[i] = (tag.uid >> (24 - (i * 8))) & 0xFF; } + break; } - break; case 45: { //select command from reader received - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { DbpString("SELECT"); + } if (check_select(rx, tag.uid) == 1) { - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { DbpString("SELECT match"); + } //if the right tag was selected *txlen = 32; @@ -434,8 +505,10 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, tx[3] = 0xff; if (tag.mode != HT_STANDARD) { + *txlen = 40; crc = CRC_PRESET; + for (int i = 0; i < 4; i++) { calc_crc(&crc, tx[i], 8); } @@ -443,8 +516,8 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, tx[4] = crc; } } + break; } - break; case 64: { //challenge message received Dbprintf("Challenge for UID: %X", temp_uid); @@ -499,9 +572,9 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, tag.pages[0][3] = 0x88; } */ + break; } - break; - case 40: + case 40: { if (g_dbglevel >= DBG_EXTENDED) Dbprintf("WRITE"); //data received to be written @@ -535,6 +608,7 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, } } break; + } case 20: { //write page, write block, read page or read block command received if ((rx[0] & 0xf0) == 0xc0) { //read page @@ -567,9 +641,12 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, sof_bits = 0; *txlen = 0; } + } else if ((rx[0] & 0xf0) == 0xd0) { //read block + uint8_t page = ((rx[0] & 0x0f) * 16) + ((rx[1] & 0xf0) / 16); *txlen = 32 * 4; + //send page,...,page+3 data for (int i = 0; i < 4; i++) { tx[0 + i * 4] = tag.pages[page + 0 + i * 4][0]; @@ -594,8 +671,11 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, sof_bits = 0; *txlen = 0; } + } else if ((rx[0] & 0xf0) == 0x80) { //write page + uint8_t page = ((rx[0] & 0x0f) * 16) + ((rx[1] & 0xf0) / 16); + if ((tag.LCON && page == 1) || (tag.LKP && (page == 2 || page == 3))) { //deny @@ -609,8 +689,10 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, } } else if ((rx[0] & 0xf0) == 0x90) { //write block + uint8_t page = ((rx[0] & 0x0f) * 6) + ((rx[1] & 0xf0) / 16); hitagS_set_frame_modulation(); + if (page % 4 != 0 || page == 0) { //deny *txlen = 0; @@ -623,12 +705,14 @@ static void hitagS_handle_reader_command(uint8_t *rx, const size_t rxlen, tag.tstate = HT_WRITING_BLOCK_DATA; } } + break; } - break; - default: - if (g_dbglevel >= DBG_EXTENDED) + default: { + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("unknown rxlen: (%i) %02X %02X %02X %02X ...", rxlen, rx[0], rx[1], rx[2], rx[3]); + } break; + } } } @@ -639,7 +723,6 @@ void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontr StopTicks(); -// int frame_count = 0; int response = 0, overflow = 0; uint8_t rx[HITAG_FRAME_LEN]; size_t rxlen = 0; @@ -666,6 +749,7 @@ void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontr // read tag data into memory if (tag_mem_supplied) { + for (int i = 0; i < 16; i++) { for (int j = 0; j < 4; j++) { tag.pages[i][j] = 0x0; @@ -700,7 +784,8 @@ void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontr tag.max_page = 0; } - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { + for (int i = 0; i < tag.max_page; i++) { Dbprintf("Page[%2d]: %02X %02X %02X %02X", i, (tag.pages[i][3]) & 0xFF, @@ -709,6 +794,8 @@ void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontr tag.pages[i][0] & 0xFF ); } + } + //con1 tag.auth = 0; if ((tag.pages[1][1] & 0x80) == 0x80) { @@ -814,8 +901,10 @@ void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontr // Receive frame, watch for at most T0*EOF periods while (AT91C_BASE_TC1->TC_CV < T0 * HITAG_T_EOF) { + // Check if rising edge in modulation is detected if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Retrieve the new timing values int ra = (AT91C_BASE_TC1->TC_RA / T0) + overflow; overflow = 0; @@ -901,6 +990,7 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui // Reset values for receiving frames memset(rx, 0x00, sizeofrx); *rxlen = 0; + int lastbit = 1; bool bSkip = true; *resptime = 0; @@ -914,8 +1004,8 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui // Receive frame, watch for at most T0*EOF periods while (AT91C_BASE_TC0->TC_CV + (overcount << 16) < (T0 * HITAG_T_PROG_MAX)) { - // detect and track counter overflows + // detect and track counter overflows uint32_t tmpcv = AT91C_BASE_TC0->TC_CV; if (tmpcv < prevcv) { overcount++; @@ -924,10 +1014,13 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui // Check if falling edge in tag modulation is detected if (AT91C_BASE_TC1->TC_SR & AT91C_TC_LDRAS) { + // Retrieve the new timing values uint32_t ra = (AT91C_BASE_TC1->TC_RA + (overcount << 16)) / T0; + // Reset timer every frame, we have to capture the last edge for timing AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + prevcv = 0; overcount = 0; @@ -935,7 +1028,8 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui // Capture tag frame (manchester decoding using only falling edges) - if (!bStarted) { + if (bStarted == false) { + if (ra >= HITAG_T_EOF) { bStarted = true; // Capture the T0 periods that have passed since last communication or field drop (reset) @@ -944,28 +1038,37 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui } else { 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)++; + } 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) { + if (bSkip == false) { rx[(*rxlen) / 8] |= 1 << (7 - ((*rxlen) % 8)); (*rxlen)++; } + lastbit = !lastbit; bSkip = !bSkip; + } 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)++; + } else { // Ignore weird value, is to small to mean anything errorCount++; @@ -973,11 +1076,13 @@ static void hitagS_receive_frame(uint8_t *rx, size_t sizeofrx, size_t *rxlen, ui } // if we saw over 100 weird values break it probably isn't hitag... - if (errorCount > 100) break; + if (errorCount > 100) { + break; + } // We can break this loop if we received the last bit from a frame - if (AT91C_BASE_TC1->TC_CV > T0 * HITAG_T_EOF) { - if ((*rxlen) > 0) { + if (AT91C_BASE_TC1->TC_CV > (T0 * HITAG_T_EOF)) { + if ((*rxlen)) { break; } } @@ -998,7 +1103,6 @@ static void sendReceiveHitagS(uint8_t *tx, size_t txlen, uint8_t *rx, size_t siz // falling edge occurred halfway the period. with respect to this falling edge, // we need to wait (T_Wait2 + half_tag_period) when the last was a 'one'. // All timer values are in terms of T0 units - while (AT91C_BASE_TC0->TC_CV < T0 * t_wait) {}; // Transmit the reader frame @@ -1011,48 +1115,82 @@ static void sendReceiveHitagS(uint8_t *tx, size_t txlen, uint8_t *rx, size_t siz size_t rxlen = 0; hitagS_receive_frame(rx, sizeofrx, &rxlen, &resptime, ledcontrol); int k = 0; + // Check if frame was captured and store it if (rxlen > 0) { + uint8_t response_bit[sizeofrx * 8]; - for (int i = 0; i < rxlen; i++) { + + for (size_t i = 0; i < rxlen; i++) { response_bit[i] = (rx[i / 8] >> (7 - (i % 8))) & 1; } + + Dbprintf("htS: rxlen...... %zu", rxlen); + Dbprintf("htS: sizeofrx... %zu", sizeofrx); + memset(rx, 0x00, sizeofrx); + if (ac_seq) { + + DbpString("htS: AntiCollision Sequence ( ac seq )"); + Dbhexdump(rxlen, response_bit, false); + // Tag Response is AC encoded + // We used UID Request Advanced, meaning AC SEQ header is 111. for (int i = 6; i < rxlen; i += 2) { + rx[k / 8] |= response_bit[i] << (7 - (k % 8)); + k++; - if (k >= 8 * sizeofrx) + + if (k > 8 * sizeofrx) { break; + } } + + DbpString("htS: ac sequence compress"); + Dbhexdump(k / 8, rx, false); + } else { - for (int i = 5; i < rxlen; i++) { // ignore first 5 bits: SOF (actually 1 or 6 depending on response protocol) + + DbpString("htS: skipping 5 bit header"); + + // ignore first 5 bits: SOF (actually 1 or 6 depending on response protocol) + // or rather a header. + for (size_t i = 5; i < rxlen; i++) { + rx[k / 8] |= response_bit[i] << (7 - (k % 8)); k++; - if (k >= 8 * sizeofrx) + + if (k > 8 * sizeofrx) { break; + } } + + } LogTraceBits(rx, k, resptime, resptime, false); } *prxbits = k; } -static size_t concatbits(uint8_t *dstbuf, size_t dstbufskip, const uint8_t *srcbuf, size_t srcbufstart, size_t srcbuflen) { +static size_t concatbits(uint8_t *dst, size_t dstskip, const uint8_t *src, size_t srcstart, size_t srclen) { // erase dstbuf bits that will be overriden - dstbuf[dstbufskip / 8] &= 0xFF - ((1 << (7 - (dstbufskip % 8) + 1)) - 1); - for (size_t i = (dstbufskip / 8) + 1; i <= (dstbufskip + srcbuflen) / 8; i++) { - dstbuf[i] = 0; + dst[dstskip / 8] &= 0xFF - ((1 << (7 - (dstskip % 8) + 1)) - 1); + for (size_t i = (dstskip / 8) + 1; i <= (dstskip + srclen) / 8; i++) { + dst[i] = 0; } - for (size_t i = 0; i < srcbuflen; i++) { + + for (size_t i = 0; i < srclen; i++) { // equiv of dstbufbits[dstbufskip + i] = srcbufbits[srcbufstart + i] - dstbuf[(dstbufskip + i) / 8] |= ((srcbuf[(srcbufstart + i) / 8] >> (7 - ((srcbufstart + i) % 8))) & 1) << (7 - ((dstbufskip + i) % 8)); + dst[(dstskip + i) / 8] |= ((src[(srcstart + i) / 8] >> (7 - ((srcstart + i) % 8))) & 1) << (7 - ((dstskip + i) % 8)); } - return dstbufskip + srcbuflen; + + return dstskip + srclen; } -static int selectHitagS(hitag_function htf, const hitag_data *htd, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx, int t_wait, bool ledcontrol) { +static int selectHitagS(const lf_hitag_data_t *packet, uint8_t *tx, size_t sizeoftx, uint8_t *rx, size_t sizeofrx, int t_wait, bool ledcontrol) { + StopTicks(); FpgaDownloadAndGo(FPGA_BITSTREAM_LF); @@ -1075,50 +1213,28 @@ static int selectHitagS(hitag_function htf, const hitag_data *htd, uint8_t *tx, // Disable modulation at default, which means enable the field LOW(GPIO_SSC_DOUT); - // Enable Peripheral Clock for - // TIMER_CLOCK0, used to measure exact timing before answering - // TIMER_CLOCK1, used to capture edges of the tag frames - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0) | (1 << AT91C_ID_TC1); - - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - - // Disable timer during configuration - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKDIS; - - // TC0: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; - - // TC1: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), TIOA is external trigger, - // external trigger rising edge, load RA on falling edge of TIOA. - AT91C_BASE_TC1->TC_CMR = - AT91C_TC_CLKS_TIMER_DIV1_CLOCK | - AT91C_TC_ETRGEDG_FALLING | - AT91C_TC_ABETRG | - AT91C_TC_LDRA_FALLING; - - // Enable and reset counters - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - // synchronized startup procedure - while (AT91C_BASE_TC0->TC_CV > 0); // wait until TC0 returned to zero + hitagS_init_clock(); - //start authentication + // UID request standard 00110 + // UID request Advanced 1100x + // UID request FAdvanced 11010 size_t txlen = 0; size_t rxlen = 0; - uint8_t cmd = 0x18; + uint8_t cmd = 0x18; // 11000 UID Request Advanced txlen = concatbits(tx, txlen, &cmd, 8 - 5, 5); sendReceiveHitagS(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, true); if (rxlen != 32) { - Dbprintf("UID Request failed!"); + DbpString("UID Request failed!"); return -1; } tag.uid = (rx[3] << 24 | rx[2] << 16 | rx[1] << 8 | rx[0]); - if (g_dbglevel >= DBG_EXTENDED) + + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("UID: %02X %02X %02X %02X", rx[0], rx[1], rx[2], rx[3]); + } + //select uid txlen = 0; cmd = 0x00; @@ -1165,25 +1281,29 @@ static int selectHitagS(hitag_function htf, const hitag_data *htd, uint8_t *tx, tag.LCK1 = (conf_pages[2] >> 1) & 0x1; tag.LCK0 = (conf_pages[2] >> 0) & 0x1; - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("conf0: %02X conf1: %02X conf2: %02X", conf_pages[0], conf_pages[1], conf_pages[2]); + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("conf 0: %02X conf 1: %02X conf 2: %02X", conf_pages[0], conf_pages[1], conf_pages[2]); + } if (tag.auth == 1) { uint64_t key = 0; //if the tag is in authentication mode try the key or challenge - if (htf == RHTSF_KEY || htf == WHTSF_KEY) { + if (packet->cmd == RHTSF_KEY || packet->cmd == WHTSF_KEY) { + if (g_dbglevel >= DBG_EXTENDED) { DbpString("Authenticating using key:"); - Dbhexdump(6, htd->crypto.key, false); + Dbhexdump(6, packet->key, false); } - key = ((uint64_t)htd->crypto.key[0]) << 0 | - ((uint64_t)htd->crypto.key[1]) << 8 | - ((uint64_t)htd->crypto.key[2]) << 16 | - ((uint64_t)htd->crypto.key[3]) << 24 | - ((uint64_t)htd->crypto.key[4]) << 32 | - ((uint64_t)htd->crypto.key[5]) << 40 + key = ((uint64_t)packet->key[0]) << 0 | + ((uint64_t)packet->key[1]) << 8 | + ((uint64_t)packet->key[2]) << 16 | + ((uint64_t)packet->key[3]) << 24 | + ((uint64_t)packet->key[4]) << 32 | + ((uint64_t)packet->key[5]) << 40 ; + uint64_t state = ht2_hitag2_init(REV64(key), REV32(tag.uid), REV32(rnd)); + uint8_t auth_ks[4]; for (int i = 0; i < 4; i++) { auth_ks[i] = ht2_hitag2_byte(&state) ^ 0xff; @@ -1194,37 +1314,44 @@ static int selectHitagS(hitag_function htf, const hitag_data *htd, uint8_t *tx, txlen = concatbits(tx, txlen, revrnd, 0, 32); txlen = concatbits(tx, txlen, auth_ks, 0, 32); - if (g_dbglevel >= DBG_EXTENDED) - Dbprintf("%02X %02X %02X %02X %02X %02X %02X %02X", tx[0], - tx[1], tx[2], tx[3], tx[4], tx[5], tx[6], tx[7]); + if (g_dbglevel >= DBG_EXTENDED) { + Dbprintf("%02X %02X %02X %02X %02X %02X %02X %02X" + , tx[0], tx[1], tx[2], tx[3] + , tx[4], tx[5], tx[6], tx[7] + ); + } + + } else if (packet->cmd == RHTSF_CHALLENGE || packet->cmd == WHTSF_CHALLENGE) { - } else if (htf == RHTSF_CHALLENGE || htf == WHTSF_CHALLENGE) { if (g_dbglevel >= DBG_EXTENDED) { DbpString("Authenticating using nr,ar pair:"); - Dbhexdump(8, htd->auth.NrAr, false); + Dbhexdump(8, packet->NrAr, false); } + uint64_t NrAr = 0; - NrAr = ((uint64_t)htd->auth.NrAr[7]) << 0 | - ((uint64_t)htd->auth.NrAr[6]) << 8 | - ((uint64_t)htd->auth.NrAr[5]) << 16 | - ((uint64_t)htd->auth.NrAr[4]) << 24 | - ((uint64_t)htd->auth.NrAr[3]) << 32 | - ((uint64_t)htd->auth.NrAr[2]) << 40 | - ((uint64_t)htd->auth.NrAr[1]) << 48 | - ((uint64_t)htd->auth.NrAr[0]) << 56; + NrAr = ((uint64_t)packet->NrAr[7]) << 0 | + ((uint64_t)packet->NrAr[6]) << 8 | + ((uint64_t)packet->NrAr[5]) << 16 | + ((uint64_t)packet->NrAr[4]) << 24 | + ((uint64_t)packet->NrAr[3]) << 32 | + ((uint64_t)packet->NrAr[2]) << 40 | + ((uint64_t)packet->NrAr[1]) << 48 | + ((uint64_t)packet->NrAr[0]) << 56; + txlen = 64; for (int i = 0; i < 8; i++) { tx[i] = ((NrAr >> (56 - (i * 8))) & 0xFF); } + } else { - Dbprintf("Error , unknown function: %d", htf); + Dbprintf("Error , unknown function: " _RED_("%d"), packet->cmd); return -1; } sendReceiveHitagS(tx, txlen, rx, sizeofrx, &rxlen, t_wait, ledcontrol, false); if (rxlen != 40) { - Dbprintf("Authenticate failed! %i", rxlen); + Dbprintf("Authenticate failed! " _RED_("%i"), rxlen); return -1; } @@ -1238,19 +1365,21 @@ static int selectHitagS(hitag_function htf, const hitag_data *htd, uint8_t *tx, pwdh0 = 0; pwdl0 = 0; pwdl1 = 0; - if (htf == RHTSF_KEY || htf == WHTSF_KEY) { + if (packet->cmd == RHTSF_KEY || packet->cmd == WHTSF_KEY) { + uint64_t state = ht2_hitag2_init(REV64(key), REV32(tag.uid), REV32(rnd)); for (int i = 0; i < 4; i++) { ht2_hitag2_byte(&state); } + uint8_t con2 = rx[0] ^ ht2_hitag2_byte(&state); pwdh0 = rx[1] ^ ht2_hitag2_byte(&state); pwdl0 = rx[2] ^ ht2_hitag2_byte(&state); pwdl1 = rx[3] ^ ht2_hitag2_byte(&state); - if (g_dbglevel >= DBG_EXTENDED) + if (g_dbglevel >= DBG_EXTENDED) { Dbprintf("con2 %02X pwdh0 %02X pwdl0 %02X pwdl1 %02X", con2, pwdh0, pwdl0, pwdl1); - + } //Dbprintf("%X %02X", rnd, ((rx[4] & 0x0f) * 16) + ((rx[5] & 0xf0) / 16)); //rnd += 1; } @@ -1263,21 +1392,26 @@ static int selectHitagS(hitag_function htf, const hitag_data *htd, uint8_t *tx, * If the key was given the password will be decrypted. * Reads every page of a hitag S transpoder. */ -void ReadHitagS(hitag_function htf, const hitag_data *htd, bool ledcontrol) { +void ReadHitagS(const lf_hitag_data_t *payload, bool ledcontrol) { uint8_t rx[HITAG_FRAME_LEN]; size_t rxlen = 0; + uint8_t tx[HITAG_FRAME_LEN]; + int t_wait = HITAG_T_WAIT_MAX; + if (selectHitagS(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol) == -1) { - if (selectHitagS(htf, htd, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol) == -1) { + hitagS_stop_clock(); set_tracing(false); lf_finalize(ledcontrol); - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_LF_HITAGS_READ, PM3_ERFTRANS, NULL, 0); return; } + int pageNum = 0; + while ((BUTTON_PRESS() == false) && (data_available() == false)) { WDT_HIT(); @@ -1319,18 +1453,18 @@ void ReadHitagS(hitag_function htf, const hitag_data *htd, bool ledcontrol) { pageNum++; //display key and password if possible if (pageNum == 2 && tag.auth == 1 && tag.LKP) { - if (htf == RHTSF_KEY) { + if (payload->cmd == RHTSF_KEY) { Dbprintf("Page[ 2]: %02X %02X %02X %02X", - htd->crypto.key[1], - htd->crypto.key[0], + payload->key[1], + payload->key[0], pwdl1, pwdl0 ); Dbprintf("Page[ 3]: %02X %02X %02X %02X", - htd->crypto.key[5], - htd->crypto.key[4], - htd->crypto.key[3], - htd->crypto.key[2] + payload->key[5], + payload->key[4], + payload->key[3], + payload->key[2] ); } else { //if the authentication is done with a challenge the key and password are unknown @@ -1344,78 +1478,89 @@ void ReadHitagS(hitag_function htf, const hitag_data *htd, bool ledcontrol) { break; } } - set_tracing(false); + hitagS_stop_clock(); + set_tracing(false); lf_finalize(ledcontrol); - - // TODO reply_mix(CMD_ACK, 1, 0, 0, 0, 0); and send dump as well, to be decoded in the client - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + reply_ng(CMD_LF_HITAGS_READ, PM3_SUCCESS, (uint8_t *)tag.pages, sizeof(tag.pages)); } /* * Authenticates to the Tag with the given Key or Challenge. * Writes the given 32Bit data into page_ */ -void WritePageHitagS(hitag_function htf, const hitag_data *htd, int page, bool ledcontrol) { +void WritePageHitagS(const lf_hitag_data_t *payload, bool ledcontrol) { - bool bSuccessful = false; //check for valid input - if (page == 0) { + if (payload->page == 0) { Dbprintf("Error, invalid page"); - reply_mix(CMD_ACK, bSuccessful, 0, 0, 0, 0); + reply_ng(CMD_LF_HITAGS_WRITE, PM3_EINVARG, NULL, 0); return; } uint8_t rx[HITAG_FRAME_LEN]; size_t rxlen = 0; + uint8_t tx[HITAG_FRAME_LEN]; size_t txlen = 0; + int t_wait = HITAG_T_WAIT_MAX; - if (selectHitagS(htf, htd, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol) == -1) { + int res = PM3_ESOFT; + + if (selectHitagS(payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol) == -1) { + res = PM3_ERFTRANS; goto write_end; } //check if the given page exists - if (page > tag.max_page) { - Dbprintf("page number too big for this tag"); + if (payload->page > tag.max_page) { + Dbprintf("Error, page number too large"); + res = PM3_EINVARG; goto write_end; } //send write page request txlen = 0; + uint8_t cmd = 0x08; txlen = concatbits(tx, txlen, &cmd, 8 - 4, 4); - uint8_t addr = page; + + uint8_t addr = payload->page; txlen = concatbits(tx, txlen, &addr, 0, 8); + uint8_t crc = CRC8Hitag1Bits(tx, txlen); txlen = concatbits(tx, txlen, &crc, 0, 8); sendReceiveHitagS(tx, txlen, rx, ARRAYLEN(rx), &rxlen, t_wait, ledcontrol, false); if ((rxlen != 2) || (rx[0] >> (8 - 2) != 0x1)) { - Dbprintf("no write access on page %d", page); + Dbprintf("no write access on page " _YELLOW_("%d"), payload->page); + res = PM3_ESOFT; goto write_end; } //ACK received to write the page. send data uint8_t data[4] = {0, 0, 0, 0}; - switch (htf) { + switch (payload->cmd) { case WHTSF_CHALLENGE: - data[0] = htd->auth.data[3]; - data[1] = htd->auth.data[2]; - data[2] = htd->auth.data[1]; - data[3] = htd->auth.data[0]; + data[0] = payload->data[3]; + data[1] = payload->data[2]; + data[2] = payload->data[1]; + data[3] = payload->data[0]; break; case WHTSF_KEY: - data[0] = htd->crypto.data[3]; - data[1] = htd->crypto.data[2]; - data[2] = htd->crypto.data[1]; - data[3] = htd->crypto.data[0]; + data[0] = payload->data[3]; + data[1] = payload->data[2]; + data[2] = payload->data[1]; + data[3] = payload->data[0]; break; - default: + default: { + res = PM3_EINVARG; return; + } } + txlen = 0; txlen = concatbits(tx, txlen, data, 0, 32); crc = CRC8Hitag1Bits(tx, txlen); @@ -1424,16 +1569,16 @@ void WritePageHitagS(hitag_function htf, const hitag_data *htd, int page, bool l sendReceiveHitagS(tx, txlen, rx, ARRAYLEN(rx), &rxlen, t_wait, ledcontrol, false); if ((rxlen != 2) || (rx[0] >> (8 - 2) != 0x1)) { - Dbprintf("write on page %d failed", page); + res = PM3_ESOFT; // write failed } else { - Dbprintf("write on page %d successful", page); - bSuccessful = true; + res = PM3_SUCCESS; } write_end: + hitagS_stop_clock(); set_tracing(false); lf_finalize(ledcontrol); - reply_mix(CMD_ACK, bSuccessful, 0, 0, 0, 0); + reply_ng(CMD_LF_HITAGS_WRITE, res, NULL, 0); } /* @@ -1444,10 +1589,11 @@ void WritePageHitagS(hitag_function htf, const hitag_data *htd, int page, bool l * detects these challenges. */ void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol) { + //check for valid input if (datalen < 8) { - Dbprintf("Error, need chals"); - reply_mix(CMD_ACK, 0, 0, 0, 0, 0); + Dbprintf("Error, missing challenges"); + reply_ng(CMD_LF_HITAGS_TEST_TRACES, PM3_EINVARG, NULL, 0); return; } uint32_t dataoffset = 0; @@ -1460,38 +1606,42 @@ void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontr // Watchdog hit WDT_HIT(); - hitag_data htd; - memset(&htd, 0, sizeof(htd)); + lf_hitag_data_t payload; + memset(&payload, 0, sizeof(payload)); + payload.cmd = RHTSF_CHALLENGE; - memcpy(htd.auth.NrAr, data + dataoffset, 8); + memcpy(payload.NrAr, data + dataoffset, 8); - int res = selectHitagS(RHTSF_CHALLENGE, &htd, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol); + int res = selectHitagS(&payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol); Dbprintf("Challenge %s: %02X %02X %02X %02X %02X %02X %02X %02X", res == -1 ? "failed " : "success", - htd.auth.NrAr[0], htd.auth.NrAr[1], - htd.auth.NrAr[2], htd.auth.NrAr[3], - htd.auth.NrAr[4], htd.auth.NrAr[5], - htd.auth.NrAr[6], htd.auth.NrAr[7] + payload.NrAr[0], payload.NrAr[1], + payload.NrAr[2], payload.NrAr[3], + payload.NrAr[4], payload.NrAr[5], + payload.NrAr[6], payload.NrAr[7] ); if (res == -1) { // Need to do a dummy UID select that will fail FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); SpinDelay(2); - selectHitagS(RHTSF_CHALLENGE, &htd, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol); + selectHitagS(&payload, tx, ARRAYLEN(tx), rx, ARRAYLEN(rx), t_wait, ledcontrol); } dataoffset += 8; - if (dataoffset >= datalen - 8) + if (dataoffset >= datalen - 8) { break; + } // reset field FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + // min t_reset = 2ms SpinDelay(2); } + hitagS_stop_clock(); set_tracing(false); lf_finalize(ledcontrol); - reply_mix(CMD_ACK, 1, 0, 0, 0, 0); + reply_ng(CMD_ACK, PM3_SUCCESS, NULL, 0); return; } diff --git a/armsrc/hitagS.h b/armsrc/hitagS.h index cbb87722f4..6278b1b188 100644 --- a/armsrc/hitagS.h +++ b/armsrc/hitagS.h @@ -22,11 +22,10 @@ #define _HITAGS_H_ #include "common.h" - #include "hitag.h" void SimulateHitagSTag(bool tag_mem_supplied, const uint8_t *data, bool ledcontrol); -void ReadHitagS(hitag_function htf, const hitag_data *htd, bool ledcontrol); -void WritePageHitagS(hitag_function htf, const hitag_data *htd, int page, bool ledcontrol); +void ReadHitagS(const lf_hitag_data_t *payload, bool ledcontrol); +void WritePageHitagS(const lf_hitag_data_t *payload, bool ledcontrol); void Hitag_check_challenges(const uint8_t *data, uint32_t datalen, bool ledcontrol); #endif diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 65784c0083..4169b8b577 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -263,6 +263,7 @@ set (TARGET_SOURCES ${PM3_ROOT}/common/cardhelper.c ${PM3_ROOT}/common/generator.c ${PM3_ROOT}/common/bruteforce.c + ${PM3_ROOT}/common/hitag2/hitag2_crypto.c ${PM3_ROOT}/client/src/crypto/asn1dump.c ${PM3_ROOT}/client/src/crypto/asn1utils.c ${PM3_ROOT}/client/src/crypto/libpcrypto.c diff --git a/client/dictionaries/ht2_default.dic b/client/dictionaries/ht2_default.dic index f503fa5438..43120be3b5 100644 --- a/client/dictionaries/ht2_default.dic +++ b/client/dictionaries/ht2_default.dic @@ -1,13 +1,25 @@ # +# Mifare Default Keys +# -- Iceman version -- +# -- contribute to this list, sharing is caring -- +# +# Lets see how long it takes before other project takes this file +# and claim they created it. +# # factory HT2 pwd 4D494B52 +# factory HT2 crypto key +4F4E4D494B52 # -# GE HT2 reader +# Gone in 360 seconds +FFFF814632FF +# +# Paxton HT2 +BDF5E846 # +# +# GE HT2 reader # TSPL 5453504C 05040202 -25293C2F -# -# Paxton HT2 -BDF5E846 +25293C2F \ No newline at end of file diff --git a/client/src/cmdlfhitag.c b/client/src/cmdlfhitag.c index 4edc507246..1faae6830b 100644 --- a/client/src/cmdlfhitag.c +++ b/client/src/cmdlfhitag.c @@ -30,6 +30,8 @@ #include "lfdemod.h" #include "cmddata.h" // setDemodBuff #include "pm3_cmd.h" // return codes +#include "hitag2/hitag2_crypto.h" +#include "util_posix.h" // msclock static int CmdHelp(const char *Cmd); @@ -205,33 +207,66 @@ static int CmdLFHitagList(const char *Cmd) { static void print_hitag2_paxton(const uint8_t *data) { - uint64_t bytes = 0; + // if the pwd isn't.. + if (memcmp(data + 4, "\xBD\xF5\xE8\x46", 4)) { + return; + } + uint64_t num = 0; uint64_t paxton_id = 0; uint16_t skip = 48; - uint16_t digit = 0; uint64_t mask = 0xF80000000000; - for (int i = 16; i < 22; i++) { - bytes = (bytes * 0x100) + data[i]; - } + uint64_t bytes = bytes_to_num(data + 16, 6); for (int j = 0; j < 8; j++) { + num = bytes & mask; skip -= 5; - mask = mask >> 5; - digit = (num >> skip & 15); + mask >>= 5; + + uint8_t digit = (num >> skip & 0xF); paxton_id = (paxton_id * 10) + digit; if (j == 5) { skip -= 2; - mask = mask >> 2; + mask >>= 2; + } + } + + /* + const uint8_t isocard = 0x06; + const uint8_t fob = 0x03; + const uint8_t iso_magstripe = 0x02; + */ + +// [=] 4/0x04 | 39 04 21 1C | 9.!. | RW | User +// [=] 5/0x05 | AC 3F 00 06 | .?.. | RW | User + + char formfactor[16]; + switch (data[23]) { + case 0x06: { + strcat(formfactor, "isocard"); + break; + } + case 0x03: { + strcat(formfactor, "fob"); + break; + } + case 0x02: { + strcat(formfactor, "iso magstripe"); + break; + } + default: { + snprintf(formfactor, sizeof(formfactor), "unk: %02x", data[23]); + break; } } PrintAndLogEx(INFO, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Possible de-scramble patterns") " -------------"); - PrintAndLogEx(SUCCESS, "Paxton id... %" PRIu64 " | 0x%" PRIx64, paxton_id, paxton_id); + PrintAndLogEx(SUCCESS, "Paxton id... %" PRIu64 " | 0x%" PRIx64 " ( %s )", paxton_id, paxton_id, formfactor); + PrintAndLogEx(INFO, ""); } static void print_hitag2_configuration(uint32_t uid, uint8_t config) { @@ -283,6 +318,7 @@ static void print_hitag2_configuration(uint32_t uid, uint8_t config) { } else { PrintAndLogEx(SUCCESS, " %s", sprint_breakdown_bin(C_NONE, bs, 8, 7, 1, "Manchester")); } + PrintAndLogEx(NORMAL, ""); } const char *annotation[] = { @@ -381,15 +417,207 @@ static struct { STATE_HALT, STATE_START_AUTH, STATE_AUTH, + STATE_START_ENCRYPTED, STATE_ENCRYPTED, } state; + uint32_t uid; + uint64_t cipher_state; + uint8_t plainlen; + uint8_t plain[30]; + bool found_key; + uint64_t key; } _ht2state; void annotateHitag2_init(void) { _ht2state.state = STATE_HALT; + _ht2state.uid = 0; + _ht2state.cipher_state = 0; + _ht2state.plainlen = 0; + memset(_ht2state.plain, 0, sizeof(_ht2state.plain)); +} + +static void rev_msb_array(uint8_t *d, uint8_t n) { + for (uint8_t i = 0 ; i < n ; i++) { + d[i] = reflect8(d[i]); + } +} + +// param nrar must be 8 bytes +static bool ht2_check_cryptokeys(const uint64_t *keys, const uint32_t keycount, const uint8_t *nrar) { + + if (keys == NULL || keycount == 0 || nrar == NULL) { + return false; + } + + uint32_t iv = REV32((nrar[3] << 24) + (nrar[2] << 16) + (nrar[1] << 8) + nrar[0]); + uint32_t ar = (nrar[4] << 24) + (nrar[5] << 16) + (nrar[6] << 8) + nrar[7]; + + bool found = false; + for (uint32_t i = 0; i < keycount; i++) { + + uint64_t key = keys[i]; + key = BSWAP_48(key); + key = REV64(key); + + hitag_state_t hs2; + ht2_hitag2_init_ex(&hs2, key, _ht2state.uid, iv); + + uint32_t tbits = ht2_hitag2_nstep(&hs2, 32); + if ((ar ^ tbits) == 0xFFFFFFFF) { + _ht2state.found_key = true; + _ht2state.key = key; + found = true; + break; + } + } + return found; +} + +static int ht2_check_dictionary(uint32_t key_count, uint8_t *keys, uint8_t keylen, uint32_t *found_idx) { + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + uint8_t *pkeys = keys; + + while (key_count--) { + + if (keylen == 4) { + packet.cmd = RHT2F_PASSWORD; + memcpy(packet.pwd, pkeys, keylen); + } else { + packet.cmd = RHT2F_CRYPTO; + memcpy(packet.key, pkeys, keylen); + } + + pkeys += keylen; + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 2000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + return PM3_ETIMEOUT; + } + + if (resp.status != PM3_SUCCESS) { + *found_idx = *found_idx + 1; + continue; + } + return PM3_SUCCESS; + } + return PM3_ESOFT; +} + + +bool hitag2_get_plain(uint8_t *plain, uint8_t *plen) { + if (_ht2state.state == STATE_ENCRYPTED || _ht2state.state == STATE_START_ENCRYPTED) { + if (_ht2state.found_key) { + *plen = _ht2state.plainlen; + memcpy(plain, _ht2state.plain, _ht2state.plainlen); + return true; + } + } + return false; +} + +static uint8_t hitag2_get_page(const char *bs) { + if ((memcmp(bs + 2, "000", 3) == 0) && (memcmp(bs + 2 + 3 + 2, "111", 3) == 0)) { + return 0; + } + if ((memcmp(bs + 2, "001", 3) == 0) && (memcmp(bs + 2 + 3 + 2, "110", 3) == 0)) { + return 1; + } + if ((memcmp(bs + 2, "010", 3) == 0) && (memcmp(bs + 2 + 3 + 2, "101", 3) == 0)) { + return 2; + } + if ((memcmp(bs + 2, "011", 3) == 0) && (memcmp(bs + 2 + 3 + 2, "100", 3) == 0)) { + return 3; + } + if ((memcmp(bs + 2, "100", 3) == 0) && (memcmp(bs + 2 + 3 + 2, "011", 3) == 0)) { + return 4; + } + if ((memcmp(bs + 2, "101", 3) == 0) && (memcmp(bs + 2 + 3 + 2, "010", 3) == 0)) { + return 5; + } + if ((memcmp(bs + 2, "110", 3) == 0) && (memcmp(bs + 2 + 3 + 2, "001", 3) == 0)) { + return 6; + } + if ((memcmp(bs + 2, "111", 3) == 0) && (memcmp(bs + 2 + 3 + 2, "000", 3) == 0)) { + return 7; + } + return 255; +} + +void hitag2_annotate_plain(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, uint8_t bits) { + + if (cmdsize == 0) { + return; + } + + char *binstr = (char *)calloc((cmdsize * 8) + 1, sizeof(uint8_t)); + if (binstr == NULL) { + return; + } + + bytes_2_binstr(binstr, cmd, cmdsize); + + size_t bn = strlen(binstr); + if (bits) { + if (cmdsize == 1) { + bn = bits; + } else if (cmdsize > 1) { + bn = ((cmdsize - 1) * 8) + bits; + } + } + + switch (bn) { + case 5: { + snprintf(exp, size, " "); + break; + } + case 10: { + if (memcmp(binstr, HITAG2_HALT, 2) == 0) { + snprintf(exp, size, " "); + break; + } + + uint8_t page = hitag2_get_page(binstr); + + if (memcmp(binstr, HITAG2_READ_PAGE, 2) == 0) { + snprintf(exp, size, "READ PAGE (" _MAGENTA_("%u") ")", page); + break; + } + + if (memcmp(binstr, HITAG2_READ_PAGE_INVERTED, 2) == 0) { + snprintf(exp, size, "READ PAGE INV (" _MAGENTA_("%u") ")", page); + break; + } + + if (memcmp(binstr, HITAG2_WRITE_PAGE, 2) == 0) { + snprintf(exp, size, "WRITE PAGE (" _MAGENTA_("%u") ")", page); + break; + } + break; + } + case 32: { // password or data + snprintf(exp, size, " "); + break; + } + case 64: { // crypto handshake + snprintf(exp, size, " "); + break; + } + default: { + snprintf(exp, size, " "); + break; + } + } + free(binstr); } -void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, uint8_t bits, bool is_response) { +void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, uint8_t bits, bool is_response, const uint64_t *keys, uint32_t keycount, bool isdecrypted) { if (cmdsize == 0) { return; @@ -411,10 +639,21 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, } } + memcpy(_ht2state.plain, cmd, cmdsize); + _ht2state.plainlen = cmdsize; + + if (_ht2state.state == STATE_ENCRYPTED || _ht2state.state == STATE_START_ENCRYPTED) { + + if (_ht2state.found_key && isdecrypted == false) { + ht2_hitag2_cipher_transcrypt(&_ht2state.cipher_state, _ht2state.plain, bn / 8, bn % 8); + } + } + // 11000 AUTH only one with 5 bits. cmdsize 1 switch (bn) { case 5: { - _ht2state.state = STATE_HALT; + annotateHitag2_init(); + if (memcmp(binstr, HITAG2_START_AUTH, 5) == 0) { snprintf(exp, size, "START AUTH"); _ht2state.state = STATE_START_AUTH; @@ -425,7 +664,7 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, } case 10: { - if (_ht2state.state == STATE_ENCRYPTED) { + if (isdecrypted == false && _ht2state.state == STATE_ENCRYPTED) { snprintf(exp, size, "ENC CMD"); break; } @@ -435,18 +674,21 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, _ht2state.state = STATE_HALT; break; } + + uint8_t page = hitag2_get_page(binstr); + if (memcmp(binstr, HITAG2_READ_PAGE, 2) == 0) { - snprintf(exp, size, "READ_PAGE (%u)", 0); + snprintf(exp, size, "READ PAGE (" _MAGENTA_("%u") ")", page); break; } if (memcmp(binstr, HITAG2_READ_PAGE_INVERTED, 2) == 0) { - snprintf(exp, size, "READ_PAGE_INVERTED (%u)", 0); + snprintf(exp, size, "READ PAGE INV (" _MAGENTA_("%u") ")", page); break; } if (memcmp(binstr, HITAG2_WRITE_PAGE, 2) == 0) { - snprintf(exp, size, "WRITE_PAGE ()"); + snprintf(exp, size, "WRITE PAGE (" _MAGENTA_("%u") ")", page); break; } break; @@ -456,6 +698,10 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, if (_ht2state.state == STATE_START_AUTH) { if (is_response) { snprintf(exp, size, "UID"); + uint8_t uid[4]; + memcpy(uid, cmd, 4); + rev_msb_array(uid, 4); + _ht2state.uid = MemLeToUint4byte(uid); } else { snprintf(exp, size, "PWD: " _GREEN_("0x%02X%02X%02X%02X"), cmd[0], cmd[1], cmd[2], cmd[3]); _ht2state.state = STATE_AUTH; @@ -465,19 +711,51 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, if (_ht2state.state == STATE_AUTH) { snprintf(exp, size, "DATA"); - } else { - snprintf(exp, size, "?"); + break; + } + + if (_ht2state.state == STATE_START_ENCRYPTED) { + snprintf(exp, size, "At"); + _ht2state.state = STATE_ENCRYPTED; + break; + } + + if (isdecrypted == false && _ht2state.state == STATE_ENCRYPTED) { + snprintf(exp, size, "ENC DATA"); } break; } case 64: { // crypto handshake - snprintf(exp, size, "AUTH: Nr Ar"); - _ht2state.state = STATE_ENCRYPTED; + + if (_ht2state.state == STATE_START_AUTH) { + _ht2state.state = STATE_START_ENCRYPTED; + + // need to be called with filename... + if (ht2_check_cryptokeys(keys, keycount, cmd)) { + + _ht2state.cipher_state = ht2_hitag2_init( + _ht2state.key, + _ht2state.uid, + REV32((cmd[3] << 24) + (cmd[2] << 16) + (cmd[1] << 8) + cmd[0]) + ); + ht2_hitag2_cipher_transcrypt(&_ht2state.cipher_state, _ht2state.plain + 4, 4, 0); + + uint64_t key = REV64(_ht2state.key); + key = BSWAP_48(key); + snprintf(exp, size, "Nr Ar " _WHITE_("( ") _GREEN_("%012" PRIx64) " )", key); + + } else { + snprintf(exp, size, "AUTH: Nr Ar"); + } + } else { + snprintf(exp, size, "AUTH: Nr Ar"); + } break; } default: { snprintf(exp, size, "?"); + _ht2state.state = STATE_HALT; break; } } @@ -488,18 +766,22 @@ void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response) { } + static bool getHitag2Uid(uint32_t *uid) { - hitag_data htd; - memset(&htd, 0, sizeof(htd)); + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + packet.cmd = RHT2F_UID_ONLY; + clearCommandBuffer(); - SendCommandMIX(CMD_LF_HITAG_READER, RHT2F_UID_ONLY, 0, 0, &htd, sizeof(htd)); + SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *) &packet, sizeof(packet)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 2500) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return false; } - if (resp.oldarg[0] == false) { + if (resp.status != PM3_SUCCESS) { PrintAndLogEx(DEBUG, "DEBUG: Error - failed getting UID"); return false; } @@ -530,14 +812,16 @@ static int CmdLFHitagInfo(const char *Cmd) { return PM3_ESOFT; } // how to determine Hitag types? + // need auth / pwd to get it. + // we could try the default key/pwd and print if successful // read block3, get configuration byte. // common configurations. - print_hitag2_configuration(uid, 0x06); - // print_hitag2_configuration( uid, 0x0E ); - // print_hitag2_configuration( uid, 0x02 ); - // print_hitag2_configuration( uid, 0x00 ); - // print_hitag2_configuration( uid, 0x04 ); + // print_hitag2_configuration(uid, 0x06); // pwd mode enabled / AM + // print_hitag2_configuration(uid, 0x0E); // crypto mode enabled / AM + // print_hitag2_configuration(uid, 0x02); + // print_hitag2_configuration(uid, 0x00); + // print_hitag2_configuration(uid, 0x04); return PM3_SUCCESS; } @@ -545,27 +829,22 @@ static int CmdLFHitagReader(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag read", - "Read Hitag memory\n" - "Crypto mode key format: ISK high + ISK low", - "Hitag S, plain mode\n" - " lf hitag read --hts\n" - "Hitag S, challenge mode\n" - " lf hitag read --hts --nrar 0102030411223344\n" - "Hitag S, crypto mode => use default key 4F4E4D494B52 (ONMIKR)\n" - " lf hitag read --hts --crypto\n" - "Hitag S, long key = crypto mode\n" - " lf hitag read --hts -k 4F4E4D494B52\n\n" - - "Hitag 2, password mode => use default key 4D494B52 (MIKR)\n" - " lf hitag read --ht2 --pwd\n" - "Hitag 2, providing a short key = password mode\n" - " lf hitag read --ht2 -k 4D494B52\n" - "Hitag 2, challenge mode\n" - " lf hitag read --ht2 --nrar 0102030411223344\n" - "Hitag 2, crypto mode => use default key 4F4E4D494B52 (ONMIKR)\n" - " lf hitag read --ht2 --crypto\n" - "Hitag 2, providing a long key = crypto mode\n" - " lf hitag read --ht2 -k 4F4E4D494B52\n" + "Read Hitag memory. It support HitagS and Hitag 2\n\n" + " Password mode:\n" + " - default key 4D494B52 (MIKR)\n\n" + " Crypto mode: \n" + " - key format ISK high + ISK low\n" + " - default key 4F4E4D494B52 (ONMIKR)\n" + , + " lf hitag read --hts -> HitagS, plain mode\n" + " lf hitag read --hts --nrar 0102030411223344 -> HitagS, challenge mode\n" + " lf hitag read --hts --crypto -> HitagS, crypto mode, def key\n" + " lf hitag read --hts -k 4F4E4D494B52 -> HitagS, crypto mode\n\n" + " lf hitag read --ht2 --pwd -> Hitag 2, pwd mode, def key\n" + " lf hitag read --ht2 -k 4D494B52 -> Hitag 2, pwd mode\n" + " lf hitag read --ht2 --nrar 0102030411223344 -> Hitag 2, challenge mode\n" + " lf hitag read --ht2 --crypto -> Hitag 2, crypto mode, def key\n" + " lf hitag read --ht2 -k 4F4E4D494B52 -> Hitag 2, crypto mode\n" ); void *argtable[] = { @@ -670,48 +949,61 @@ static int CmdLFHitagReader(const char *Cmd) { return PM3_EINVARG; } - hitag_function htf; - hitag_data htd; - memset(&htd, 0, sizeof(htd)); - uint16_t cmd; - if (use_hts && use_nrar) { - cmd = CMD_LF_HITAGS_READ; - htf = RHTSF_CHALLENGE; - memcpy(htd.auth.NrAr, nrar, sizeof(htd.auth.NrAr)); + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + + int pm3cmd; + if (use_hts) { + // plain mode? + pm3cmd = CMD_LF_HITAGS_READ; + } else if (use_hts && use_nrar) { + pm3cmd = CMD_LF_HITAGS_READ; + packet.cmd = RHTSF_CHALLENGE; + memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); + } else if (use_hts && use_crypto) { - cmd = CMD_LF_HITAGS_READ; - htf = RHTSF_KEY; - memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); + pm3cmd = CMD_LF_HITAGS_READ; + packet.cmd = RHTSF_KEY; + memcpy(packet.key, key, sizeof(packet.key)); + } else if (use_ht2 && use_pwd) { - cmd = CMD_LF_HITAG_READER; - htf = RHT2F_PASSWORD; - memcpy(htd.pwd.password, key, sizeof(htd.pwd.password)); + pm3cmd = CMD_LF_HITAG_READER; + packet.cmd = RHT2F_PASSWORD; + memcpy(packet.pwd, key, sizeof(packet.pwd)); + } else if (use_ht2 && use_nrar) { - cmd = CMD_LF_HITAG_READER; - htf = RHT2F_AUTHENTICATE; - memcpy(htd.auth.NrAr, nrar, sizeof(htd.auth.NrAr)); + pm3cmd = CMD_LF_HITAG_READER; + packet.cmd = RHT2F_AUTHENTICATE; + memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); } else if (use_ht2 && use_crypto) { - htf = RHT2F_CRYPTO; - cmd = CMD_LF_HITAG_READER; - memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); + + pm3cmd = CMD_LF_HITAG_READER; + packet.cmd = RHT2F_CRYPTO; + memcpy(packet.key, key, sizeof(packet.key)); } else { PrintAndLogEx(WARNING, "Sorry, not yet implemented"); return PM3_ENOTIMPL; } clearCommandBuffer(); - SendCommandMIX(cmd, htf, 0, 0, &htd, sizeof(htd)); + SendCommandNG(pm3cmd, (uint8_t *)&packet, sizeof(packet)); + PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + if (WaitForResponseTimeout(pm3cmd, &resp, 2000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); SendCommandNG(CMD_BREAK_LOOP, NULL, 0); return PM3_ETIMEOUT; } - if (resp.oldarg[0] == false) { + + if (resp.status != PM3_SUCCESS) { PrintAndLogEx(DEBUG, "DEBUG: Error - hitag failed"); return PM3_ESOFT; } + if (use_nrar) { + return PM3_SUCCESS; + } + uint8_t *data = resp.data.asBytes; uint32_t uid = bytes_to_num(data, HITAG_UID_SIZE); print_hitag2_configuration(uid, data[HITAG_BLOCK_SIZE * 3]); @@ -720,7 +1012,7 @@ static int CmdLFHitagReader(const char *Cmd) { print_hitag2_blocks(data, HITAG2_MAX_BYTE_SIZE); print_hitag2_paxton(data); } else { - print_hex_break(data, HITAG2_MAX_BYTE_SIZE, HITAG_BLOCK_SIZE); + print_hex_break(data, HITAG_MAX_BYTE_SIZE, HITAG_BLOCK_SIZE); } return PM3_SUCCESS; } @@ -746,22 +1038,28 @@ static int CmdLFHitagSCheckChallenges(const char *Cmd) { CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); CLIParserFree(ctx); - clearCommandBuffer(); - uint8_t *data = NULL; size_t datalen = 0; int res = loadFile_safe(filename, ".cc", (void **)&data, &datalen); if (res == PM3_SUCCESS) { - if (datalen % 8 == 0) { - SendCommandMIX(CMD_LF_HITAGS_TEST_TRACES, datalen, 0, 0, data, datalen); - } else { - PrintAndLogEx(ERR, "Error, file length mismatch. Expected multiple of 8, got %zu", datalen); + + if (datalen % 8) { + PrintAndLogEx(ERR, "Error, file length mismatch. Expected multiple of 8, got " _RED_("%zu"), datalen); + free(data); + return PM3_EINVARG; } + if (datalen != (8 * 60)) { + PrintAndLogEx(ERR, "Error, file length mismatch. Expected 480, got " _RED_("%zu"), datalen); + free(data); + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAGS_TEST_TRACES, data, datalen); } if (data) { free(data); } - return PM3_SUCCESS; } @@ -773,14 +1071,18 @@ static int CmdLFHitag2CheckChallenges(const char *Cmd) { ); CLIParserFree(ctx); + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(lf_hitag_data_t)); + packet.cmd = RHT2F_TEST_AUTH_ATTEMPTS; + clearCommandBuffer(); - SendCommandMIX(CMD_LF_HITAG_READER, RHT2F_TEST_AUTH_ATTEMPTS, 0, 0, NULL, 0); + SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *)&packet, sizeof(packet)); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 2000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } - if (resp.oldarg[0] == false) { + if (resp.status != PM3_SUCCESS) { PrintAndLogEx(DEBUG, "DEBUG: Error - hitag failed"); return PM3_ESOFT; } @@ -792,27 +1094,22 @@ static int CmdLFHitag2CheckChallenges(const char *Cmd) { static int CmdLFHitagWriter(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag wrbl", - "Write a page in Hitag memory\n" - "Crypto mode key format: ISK high + ISK low", - "Hitag S, plain mode\n" - " lf hitag wrbl --hts -p 6 -d 01020304\n" - "Hitag S, challenge mode\n" - " lf hitag wrbl --hts --nrar 0102030411223344 -p 6 -d 01020304\n" - "Hitag S, crypto mode => use default key 4F4E4D494B52 (ONMIKR)\n" - " lf hitag wrbl --hts --crypto -p 6 -d 01020304\n" - "Hitag S, long key = crypto mode\n" - " lf hitag wrbl --hts -k 4F4E4D494B52 -p 6 -d 01020304\n\n" - - "Hitag 2, password mode => use default key 4D494B52 (MIKR)\n" - " lf hitag wrbl --ht2 --pwd -p 6 -d 01020304\n" - "Hitag 2, providing a short key = password mode\n" - " lf hitag wrbl --ht2 -k 4D494B52 -p 6 -d 01020304\n" - "Hitag 2, challenge mode\n" - " lf hitag wrbl --ht2 --nrar 0102030411223344 -p 6 -d 01020304\n" - "Hitag 2, crypto mode => use default key 4F4E4D494B52 (ONMIKR)\n" - " lf hitag wrbl --ht2 --crypto -p 6 -d 01020304\n" - "Hitag 2, providing a long key = crypto mode\n" - " lf hitag wrbl --ht2 -k 4F4E4D494B52 -p 6 -d 01020304\n" + "Write a page in Hitag memory. It support HitagS and Hitag 2\n" + " Password mode:\n" + " - default key 4D494B52 (MIKR)\n\n" + " Crypto mode: \n" + " - key format ISK high + ISK low\n" + " - default key 4F4E4D494B52 (ONMIKR)\n" + , + " lf hitag wrbl --hts -p 6 -d 01020304 -> HitagS, plain mode\n" + " lf hitag wrbl --hts -p 6 -d 01020304 --nrar 0102030411223344 -> HitagS, challenge mode\n" + " lf hitag wrbl --hts -p 6 -d 01020304 --crypto -> HitagS, crypto mode, def key\n" + " lf hitag wrbl --hts -p 6 -d 01020304 -k 4F4E4D494B52 -> HitagS, crypto mode\n\n" + " lf hitag wrbl --ht2 -p 6 -d 01020304 --pwd -> Hitag 2, pwd mode, def key\n" + " lf hitag wrbl --ht2 -p 6 -d 01020304 -k 4D494B52 -> Hitag 2, pwd mode\n" + " lf hitag wrbl --ht2 -p 6 -d 01020304 --nrar 0102030411223344 -> Hitag 2, challenge mode\n" + " lf hitag wrbl --ht2 -p 6 -d 01020304 --crypto -> Hitag 2, crypto mode, def key\n" + " lf hitag wrbl --ht2 -p 6 -d 01020304 -k 4F4E4D494B52 -> Hitag 2, crypto mode\n" ); void *argtable[] = { @@ -854,7 +1151,7 @@ static int CmdLFHitagWriter(const char *Cmd) { return PM3_EINVARG; } - uint32_t page = arg_get_u32_def(ctx, 7, 0); + int page = arg_get_int_def(ctx, 7, 0); uint8_t data[4]; int dlen = 0; @@ -931,47 +1228,83 @@ static int CmdLFHitagWriter(const char *Cmd) { return PM3_EINVARG; } - hitag_function htf; - hitag_data htd; - memset(&htd, 0, sizeof(htd)); + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); if (use_hts && use_nrar) { - htf = WHTSF_CHALLENGE; - memcpy(htd.auth.NrAr, nrar, sizeof(htd.auth.NrAr)); - memcpy(htd.auth.data, data, sizeof(data)); - PrintAndLogEx(INFO, "Authenticating to Hitag S in Challenge mode"); + packet.cmd = WHTSF_CHALLENGE; + memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); + memcpy(packet.data, data, sizeof(data)); + // iceman: No page in Hitag S ? + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag S") " in Challenge mode"); + } else if (use_hts && use_crypto) { - htf = WHTSF_KEY; - memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); - memcpy(htd.crypto.data, data, sizeof(data)); - PrintAndLogEx(INFO, "Authenticating to Hitag S in Crypto mode"); + packet.cmd = WHTSF_KEY; + memcpy(packet.key, key, sizeof(packet.key)); + memcpy(packet.data, data, sizeof(data)); + // iceman: No page in Hitag S ? + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag S") " in Crypto mode"); + } else if (use_ht2 && use_pwd) { - htf = WHT2F_PASSWORD; - memcpy(htd.pwd.password, key, sizeof(htd.pwd.password)); - memcpy(htd.crypto.data, data, sizeof(data)); - PrintAndLogEx(INFO, "Authenticating to Hitag 2 in Password mode"); + packet.cmd = WHT2F_PASSWORD; + packet.page = page; + memcpy(packet.pwd, key, sizeof(packet.pwd)); + memcpy(packet.data, data, sizeof(data)); + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag 2") " in Password mode"); + } else if (use_ht2 && use_crypto) { - htf = WHT2F_CRYPTO; - memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); - memcpy(htd.crypto.data, data, sizeof(data)); - PrintAndLogEx(INFO, "Authenticating to Hitag 2 in Crypto mode"); + packet.cmd = WHT2F_CRYPTO; + packet.page = page; + memcpy(packet.key, key, sizeof(packet.key)); + memcpy(packet.data, data, sizeof(data)); + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag 2") " in Crypto mode"); + } else { PrintAndLogEx(WARNING, "Sorry, not yet implemented"); return PM3_ENOTIMPL; } - uint16_t cmd = CMD_LF_HITAGS_WRITE; + clearCommandBuffer(); - SendCommandMIX(cmd, htf, 0, page, &htd, sizeof(htd)); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 4000) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); - return PM3_ETIMEOUT; - } - if (resp.oldarg[0] == false) { - PrintAndLogEx(DEBUG, "DEBUG: Error - hitag write failed"); - return PM3_ESOFT; + if (use_ht2) { + SendCommandNG(CMD_LF_HITAG2_WRITE, (uint8_t *)&packet, sizeof(packet)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAG2_WRITE, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_ETEAROFF) { + PrintAndLogEx(INFO, "Writing tear off triggered"); + return PM3_SUCCESS; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); + return resp.status; + } + + } else { + + SendCommandNG(CMD_LF_HITAGS_WRITE, (uint8_t *)&packet, sizeof(packet)); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_LF_HITAGS_WRITE, &resp, 4000) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ETIMEOUT; + } + + if (resp.status == PM3_ETEAROFF) { + PrintAndLogEx(INFO, "Writing tear off triggered"); + return PM3_SUCCESS; + } + + if (resp.status != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Write ( " _RED_("fail") " )"); + return resp.status; + } } + + PrintAndLogEx(SUCCESS, "Write ( " _GREEN_("ok") " )"); return PM3_SUCCESS; } @@ -979,19 +1312,15 @@ static int CmdLFHitag2Dump(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "lf hitag dump", - "Read all Hitag 2 card memory and save to file\n" - "Crypto mode key format: ISK high + ISK low", - "Password mode => use default key 4D494B52 (MIKR)\n" - " lf hitag dump --pwd\n" - "Short key = password mode\n" - " lf hitag dump -k 4D494B52\n" - "Challenge mode\n" - " lf hitag dump --nrar 0102030411223344\n" - "Crypto mode => use default key 4F4E4D494B52 (ONMIKR)\n" - " lf hitag dump --crypto\n" - "Long key = crypto mode\n" - " lf hitag dump -k 4F4E4D494B52\n" - ); + "Read all Hitag 2 card memory and save to file\n" + "Crypto mode key format: ISK high + ISK low, 4F4E4D494B52 (ONMIKR)\n" + "Password mode, default key 4D494B52 (MIKR)\n", + "lf hitag dump --pwd -> use def pwd\n" + "lf hitag dump -k 4D494B52 -> pwd mode\n" + "lf hitag dump --crypto -> use def crypto\n" + "lf hitag dump -k 4F4E4D494B52 -> crypto mode\n" + "lf hitag dump --nrar 0102030411223344\n" + ); void *argtable[] = { arg_param_begin, @@ -1022,7 +1351,7 @@ static int CmdLFHitag2Dump(const char *Cmd) { bool use_nrar = nalen > 0; bool use_crypto = arg_get_lit(ctx, 3); - uint8_t key[HITAG_CRYPTOKEY_SIZE]; + uint8_t key[HITAG_NRAR_SIZE]; int keylen = 0; res = CLIParamHexToBuf(arg_get_str(ctx, 4), key, sizeof(key), &keylen); if (res != 0) { @@ -1047,8 +1376,11 @@ static int CmdLFHitag2Dump(const char *Cmd) { return PM3_EINVARG; } - if (keylen != 0 && keylen != 4 && keylen != 6) { - PrintAndLogEx(WARNING, "Wrong KEY len expected 0, 4 or 6, got %d", keylen); + if (keylen != 0 && + keylen != HITAG_PASSWORD_SIZE && + keylen != HITAG_CRYPTOKEY_SIZE && + keylen != HITAG_NRAR_SIZE) { + PrintAndLogEx(WARNING, "Wrong KEY len expected (0,4,6,8) got %d", keylen); return PM3_EINVARG; } @@ -1059,6 +1391,10 @@ static int CmdLFHitag2Dump(const char *Cmd) { if (keylen == HITAG_CRYPTOKEY_SIZE) { use_crypto = true; } + if (keylen == HITAG_NRAR_SIZE) { + use_nrar = true; + memcpy(nrar, key, sizeof(nrar)); + } // Set default key / pwd if ((keylen == 0) && use_pwd) { @@ -1094,36 +1430,115 @@ static int CmdLFHitag2Dump(const char *Cmd) { return PM3_EINVARG; } - hitag_function htf; - hitag_data htd; - memset(&htd, 0, sizeof(htd)); + PacketResponseNG resp; + uint8_t *data = NULL; + + lf_hitag_data_t packet; + memset(&packet, 0, sizeof(packet)); + if (use_ht2 && use_pwd) { - htf = RHT2F_PASSWORD; - memcpy(htd.pwd.password, key, sizeof(htd.pwd.password)); - PrintAndLogEx(INFO, "Authenticating to Hitag2 in Password mode"); + packet.cmd = RHT2F_PASSWORD; + memcpy(packet.pwd, key, sizeof(packet.pwd)); + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag 2") " in Password mode"); + } else if (use_ht2 && use_crypto) { - htf = RHT2F_CRYPTO; - memcpy(htd.crypto.key, key, sizeof(htd.crypto.key)); - PrintAndLogEx(INFO, "Authenticating to Hitag2 in Crypto mode"); + packet.cmd = RHT2F_CRYPTO; + memcpy(packet.key, key, sizeof(packet.key)); + PrintAndLogEx(INFO, "Authenticating to " _YELLOW_("Hitag 2") " in Crypto mode"); + + } else if (use_ht2 && use_nrar) { + + + memcpy(packet.NrAr, nrar, sizeof(packet.NrAr)); + + PrintAndLogEx(INFO, _YELLOW_("Hitag 2") " - Challenge mode (NrAR)"); + + uint64_t t1 = msclock(); + + clearCommandBuffer(); + SendCommandNG(CMD_LF_HITAG2_CRACK, (uint8_t *) &packet, sizeof(packet)); + + // loop + uint8_t attempt = 30; + do { + + PrintAndLogEx(INPLACE, "Attack 1 running..." ); + fflush(stdout); + + if (WaitForResponseTimeout(CMD_LF_HITAG2_CRACK, &resp, 1000) == false) { + attempt--; + continue; + } + + lf_hitag_crack_response_t *payload = (lf_hitag_crack_response_t*)resp.data.asBytes; + + if (resp.status == PM3_SUCCESS) { + PrintAndLogEx(NORMAL, " ( %s )", _GREEN_("ok")); + data = payload->data; + + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\ntime " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + goto out; + } + + // error codes + switch (payload->status) { + case -1: { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Couldn't select tag!"); + return PM3_ESOFT; + } + case -2: { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Cannot find a valid encrypted command!"); + return PM3_ESOFT; + } + case -3: { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Cannot find encrypted 'read page0' command!"); + return PM3_ESOFT; + } + case -4: { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(FAILED, "Partial data extraction!"); + continue; + } + } + + } while (attempt); + + if (attempt == 0) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + return PM3_ESOFT; + } + + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\ntime " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + + goto out; + } else { PrintAndLogEx(WARNING, "Sorry, not yet implemented"); return PM3_ENOTIMPL; } - uint16_t cmd = CMD_LF_HITAG_READER; + clearCommandBuffer(); - SendCommandMIX(cmd, htf, 0, 0, &htd, sizeof(htd)); - PacketResponseNG resp; + SendCommandNG(CMD_LF_HITAG_READER, (uint8_t *) &packet, sizeof(packet)); - if (WaitForResponseTimeout(CMD_ACK, &resp, 2000) == false) { + if (WaitForResponseTimeout(CMD_LF_HITAG_READER, &resp, 5000) == false) { PrintAndLogEx(WARNING, "timeout while waiting for reply."); return PM3_ETIMEOUT; } - if (resp.oldarg[0] == false) { + if (resp.status != PM3_SUCCESS) { PrintAndLogEx(DEBUG, "DEBUG: Error - hitag failed"); - return PM3_ESOFT; + return resp.status; } - uint8_t *data = resp.data.asBytes; + data = resp.data.asBytes; + + out: + // block3, 1 byte uint32_t uid = bytes_to_num(data, HITAG_UID_SIZE); @@ -1384,25 +1799,544 @@ static int CmdLFHitagSniff(const char *Cmd) { return PM3_SUCCESS; } +static int CmdLFHitag2PWMDemod(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag pwmdemod", + "Demodulate the data in the GraphBuffer and output binary\n", + "lf hitag pwmdemod" + "lf hitag pwmdemod -t 65 --> specify first wave index\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_int0("t", "start", "", "first wave index"), + arg_int0(NULL, "zero", "", "Zero pulse length"), + arg_int0(NULL, "one", "", "One pulse length"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + uint32_t start_idx = (uint32_t)arg_get_int_def(ctx, 1, 0); + uint8_t fclow = (uint8_t)arg_get_int_def(ctx, 2, 20); + uint8_t fchigh = (uint8_t)arg_get_int_def(ctx, 3, 29); + CLIParserFree(ctx); + + uint8_t *bits = calloc(MAX_GRAPH_TRACE_LEN, sizeof(uint8_t)); + if (bits == NULL) { + PrintAndLogEx(INFO, "failed to allocate memory"); + return PM3_EMALLOC; + } + + size_t size = getFromGraphBuffer(bits); + + PrintAndLogEx(DEBUG, "DEBUG: (Hitag2PWM) #samples from graphbuff... %zu", size); + + if (size < 255) { + PrintAndLogEx(INFO, "too few samples in buffer"); + free(bits); + return PM3_ESOFT; + } + + // TODO autodetect + size = HitagPWMDemod(bits, size, &fchigh, &fclow, &start_idx, g_DemodBitRangeBuffer); + if (size == 0) { + PrintAndLogEx(FAILED, "No wave detected"); + free(bits); + return PM3_ESOFT; + } + + PrintAndLogEx(DEBUG, "DEBUG: start_idx... %u size... %zu", start_idx, size); + + setDemodBuffBitRange(bits, size, 0, g_DemodBitRangeBuffer); + setClockGrid(32, start_idx); + + uint32_t total = 0; + for (size_t i = 0; i < size; i++) { + total += g_DemodBitRangeBuffer[i]; + PrintAndLogEx(DEBUG, "%d", g_DemodBitRangeBuffer[i]); + } + PrintAndLogEx(DEBUG, "Total... %d", total); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("HITAG/PWM") " ---------------------------"); + printDemodBuff(0, false, false, false); + printDemodBuff(0, false, false, true); + free(bits); + return PM3_SUCCESS; +} + +static int CmdLFHitag2Chk(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag chk", + "Run dictionary key or password recovery against Hitag card.", + "lf hitag chk\n -> checks for both pwd / crypto keys" + "lf hitag chk --crypto -> use def dictionary\n" + "lf hitag chk --pwd -f my.dic -> pwd mode, custom dictionary" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "specify dictionary filename"), + arg_lit0(NULL, "pwd", "password mode"), + arg_lit0(NULL, "crypto", "crypto mode"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, true); + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool use_pwd = arg_get_lit(ctx, 2); + bool use_crypto = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + if (use_pwd + use_crypto > 1) { + PrintAndLogEx(WARNING, "Only specify one mode"); + return PM3_EINVARG; + } + + // no filename -> use default = ht2_default.dic + if (fnlen == 0) { + snprintf(filename, sizeof(filename), HITAG_DICTIONARY); + } + + uint8_t keylen = 4; + if (use_crypto) { + keylen = 6; + } + + uint64_t t1 = msclock(); + + // just loop twice at max. Starting with 4 or 6. + for (; keylen < 7; keylen += 2) { + // load keys + uint8_t *keys = NULL; + uint32_t key_count = 0; + int res = loadFileDICTIONARY_safe(filename, (void **)&keys, keylen, &key_count); + if (res != PM3_SUCCESS || key_count == 0 || keys == NULL) { + PrintAndLogEx(WARNING, "no keys found in file"); + if (keys != NULL) { + free(keys); + } + return res; + } + + // Main loop + uint32_t found_idx = 0; + int status = ht2_check_dictionary(key_count, keys, keylen, &found_idx); + + if (status == PM3_SUCCESS) { + + PrintAndLogEx(NORMAL, ""); + if (keylen == 6) { + PrintAndLogEx(SUCCESS, "found valid key [ " _GREEN_("%s") " ]", sprint_hex_inrow(keys + (found_idx * keylen), keylen)); + } else { + PrintAndLogEx(SUCCESS, "found valid password [ " _GREEN_("%s") " ]", sprint_hex_inrow(keys + (found_idx * keylen), keylen)); + } + free(keys); + break; + } + free(keys); + } + + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\ntime in check " _YELLOW_("%.0f") " seconds\n", (float)t1 / 1000.0); + return PM3_SUCCESS; +} + +static int CmdLFHitag2Lookup(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag lookup", + "This command take sniffed trace data and try to recovery a Hitag2 crypto key.\n" + " You can either\n" + " - verify that NR/AR matches a known crypto key\n" + " - verify if NR/AR matches a known 6 byte crypto key in a dictionary", + "lf hitag lookup --uid 11223344 --nr 73AA5A62 --ar EAB8529C -k 010203040506 -> check key\n" + "lf hitag lookup --uid 11223344 --nr 73AA5A62 --ar EAB8529C -> use def dictionary\n" + "lf hitag lookup --uid 11223344 --nr 73AA5A62 --ar EAB8529C -f my.dic -> use custom dictionary\n" + "lf hitag lookup --uid 11223344 --nrar 73AA5A62EAB8529C" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "specify dictionary filename"), + arg_str0("k", "key", "", "specify known cryptokey as 6 bytes"), + arg_str1("u", "uid", "", "specify UID as 4 hex bytes"), + arg_str0(NULL, "nr", "", "specify nonce as 4 hex bytes"), + arg_str0(NULL, "ar", "", "specify answer as 4 hex bytes"), + arg_str0(NULL, "nrar", "", "specify nonce / answer as 8 hex bytes"), + arg_param_end + }; + + CLIExecWithReturn(ctx, Cmd, argtable, false); + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + int inkeylen = 0; + uint8_t inkey[6] = {0}; + CLIGetHexWithReturn(ctx, 2, inkey, &inkeylen); + + int ulen = 0; + uint8_t uidarr[4] = {0}; + CLIGetHexWithReturn(ctx, 3, uidarr, &ulen); + + int nlen = 0; + uint8_t narr[4] = {0}; + CLIGetHexWithReturn(ctx, 4, narr, &nlen); + + int alen = 0; + uint8_t aarr[4] = {0}; + CLIGetHexWithReturn(ctx, 5, aarr, &alen); + + int nalen = 0; + uint8_t nrar[8] = {0}; + CLIGetHexWithReturn(ctx, 6, nrar, &nalen); + + CLIParserFree(ctx); + + // sanity checks + if (inkeylen && inkeylen != 6) { + PrintAndLogEx(INFO, "Key wrong length. expected 6, got %i", inkeylen); + return PM3_EINVARG; + } + + if (ulen && ulen != 4) { + PrintAndLogEx(INFO, "UID wrong length. expected 4, got %i", ulen); + return PM3_EINVARG; + } + + if (nlen && nlen != 4) { + PrintAndLogEx(INFO, "Nr wrong length. expected 4, got %i", nlen); + return PM3_EINVARG; + } + + if (alen && alen != 4) { + PrintAndLogEx(INFO, "Ar wrong length. expected 4, got %i", alen); + return PM3_EINVARG; + } + + if (nalen && nalen != 8) { + PrintAndLogEx(INFO, "NrAr wrong length. expected 8, got %i", nalen); + return PM3_EINVARG; + } + + // Iceman note: + // - key, uid and Nr1 is alway dentoed as LSB/LE order + // - Ar1 is NOT. It is in BE/MSB everywhere. + // - At1 is NOT. It is in BE/MSB everywhere. + // - crypto stream generated is in BE/MSB order in Pm3 code. + // - crypto state is in ? + // - lfsr state is in ? + // + // Different implementations handles internally the state either in MSB or LSB. + // Something to keep an eye for when looking at code. + // + // Termology: + // cs / hstate.shiftregister / crypto state = same + // lsfr = some implementations mixes cs and lsfr into one and only use the state. Some differentiate between them. + // usually the key recovery functions under /tools/hitag2crack + // IV / Nonce Reader 1 / Nr1 = same (clear text), always 00 00 00 00 in PM3 code when acting as reader. + // Answer Reader 1 / Ar1 = encrypted and BE/MSB, +32, the clear text is always FF FF FF FF. + // Answer Tag 1 / At1 = encrypted and BE/MSB, +32, + + /* + When initializer the crypto engine + + 1. UID: 11223344 + 2. KEY: FFFF143624FF + 3. NONCE / IV: 00 00 00 00 + 3. NONCE / IV: 3B 6F 08 4D + + now you have a CS / Shiftregister / state = crypto stream? + + Ar1 - first encrypted crypto stream ^ 0xFFFFFFFF + 4. Ar1: 96 7A 6F 2A ^ FF FF FF FF == 69 85 90 D5 + + */ + rev_msb_array(inkey, sizeof(inkey)); + rev_msb_array(uidarr, sizeof(uidarr)); + rev_msb_array(narr, sizeof(narr)); + rev_msb_array(nrar, 4); + + + // Little Endian + uint64_t knownkey = MemLeToUint6byte(inkey); + uint32_t uid = MemLeToUint4byte(uidarr); + + uint32_t nr; + // Big Endian + uint32_t ar; + + if (nlen && alen) { + nr = MemLeToUint4byte(narr); + ar = MemBeToUint4byte(aarr); + } else if (nalen) { + nr = MemLeToUint4byte(nrar); + ar = MemBeToUint4byte(nrar + 4); + } else { + PrintAndLogEx(INFO, "No nr or ar was supplied"); + return PM3_EINVARG; + } + + uint32_t iv = nr; + + + if (inkeylen) { + + PrintAndLogEx(DEBUG, "UID... %08" PRIx32, uid); + PrintAndLogEx(DEBUG, "IV.... %08" PRIx32, iv); + PrintAndLogEx(DEBUG, "Key... %012" PRIx64, knownkey); + + // initialize state + hitag_state_t hstate; + ht2_hitag2_init_ex(&hstate, knownkey, uid, iv); + + // get 32 bits of crypto stream. + uint32_t cbits = ht2_hitag2_nstep(&hstate, 32); + bool isok = (ar == (cbits ^ 0xFFFFFFFF)); + + PrintAndLogEx(DEBUG, "state.shiftreg...... %012" PRIx64, hstate.shiftreg); + PrintAndLogEx(DEBUG, "state.lfsr.......... %012" PRIx64, hstate.lfsr); + PrintAndLogEx(DEBUG, "c bits.............. %08x", cbits); + PrintAndLogEx(DEBUG, "c-bits ^ FFFFFFFF... %08x", cbits ^ 0xFFFFFFFF); + PrintAndLogEx(DEBUG, "Ar.................. %08" PRIx32 " ( %s )", ar, (isok) ? _GREEN_("ok") : _RED_("fail")); + + PrintAndLogEx(INFO, "Nr/Ar match key ( %s )", (isok) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; + } + + if (fnlen == 0) { + snprintf(filename, sizeof(filename), HITAG_DICTIONARY); + } + + // load keys + uint8_t *keys = NULL; + uint32_t key_count = 0; + int res = loadFileDICTIONARY_safe(filename, (void **)&keys, HITAG_CRYPTOKEY_SIZE, &key_count); + if (res != PM3_SUCCESS || key_count == 0 || keys == NULL) { + PrintAndLogEx(WARNING, "no keys found in file"); + if (keys != NULL) { + free(keys); + } + return res; + } + + bool found = false; + for (uint32_t i = 0; i < key_count; i++) { + + uint8_t *pkey = keys + (i * HITAG_CRYPTOKEY_SIZE); + uint64_t mykey = MemLeToUint6byte(pkey); + mykey = REV64(mykey); + + hitag_state_t hs2; + ht2_hitag2_init_ex(&hs2, mykey, uid, iv); + + uint32_t tbits = ht2_hitag2_nstep(&hs2, 32); + if ((ar ^ tbits) == 0xFFFFFFFF) { + PrintAndLogEx(SUCCESS, "Found valid key [ " _GREEN_("%s")" ]", sprint_hex_inrow(pkey, HITAG_CRYPTOKEY_SIZE)); + found = true; + break; + } + + if (g_debugMode) { + PrintAndLogEx(DEBUG, " tbits... %08" PRIx32 " Known ar... %08" PRIx32, tbits, ar); + PrintAndLogEx(DEBUG, " 0xFFFFFFFF ^ tbits... %08" PRIx32, tbits ^ 0xFFFFFFFF); + PrintAndLogEx(DEBUG, " 0xFFFFFFFF ^ ar...... %08" PRIx32, ar ^ 0xFFFFFFFF); + PrintAndLogEx(DEBUG, " tbits ^ ar........... %08" PRIx32 " ( 0xFFFFFFFF )", ar ^ tbits); + } + } + + free(keys); + + if (found == false) { + PrintAndLogEx(WARNING, "check failed"); + } + + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} + +/* Test code + + Test data and below information about it comes from + http://www.mikrocontroller.net/attachment/102194/hitag2.c + Written by "I.C. Wiener 2006-2007" + + "MIKRON" = O N M I K R + Key = 4F 4E 4D 49 4B 52 - Secret 48-bit key + Serial = 49 43 57 69 - Serial number of the tag, transmitted in clear + Random = 65 6E 45 72 - Random IV, transmitted in clear + ~28~DC~80~31 = D7 23 7F CE - Authenticator value = inverted first 4 bytes of the keystream + + The code below must print out "D7 23 7F CE 8C D0 37 A9 57 49 C1 E6 48 00 8A B6". + The inverse of the first 4 bytes is sent to the tag to authenticate. + The rest is encrypted by XORing it with the subsequent keystream. +*/ +static uint64_t hitag2_benchtest_gen32(void) { + const uint64_t key = 0x4ad292b272f2; + const uint32_t serial = 0x96eac292; + const uint32_t initvec = 0x4ea276a6; + hitag_state_t state; + + // init crypto + ht2_hitag2_init_ex(&state, key, serial, initvec); + + // benchmark: generation of 32 bit stream (excludes initialisation) + uint64_t t1 = usclock(); + + (void) ht2_hitag2_nstep(&state, 32); + + t1 = usclock() - t1; + return t1; +} + +static uint64_t hitag2_benchtest(uint32_t count) { + + const uint64_t key = 0x4ad292b272f2; + const uint32_t serial = 0x96eac292; + const uint32_t initvec = 0x4ea276a6; + + hitag_state_t state; + + // start timer + uint64_t t1 = usclock(); + + // benchmark: initialise crypto & generate 32 bit authentication + // adding i stops gcc optimizer moving init function call out of loop + for (uint32_t i = 0; i < count; i++) { + ht2_hitag2_init_ex(&state, key, serial, initvec + i); + (void) ht2_hitag2_nstep(&state, 32); + } + + t1 = usclock() - t1; + return t1; +} + +static uint64_t hitag2_verify_crypto_test(void) { + + uint8_t expected[16] = { 0xD7, 0x23, 0x7F, 0xCE, 0x8C, 0xD0, 0x37, 0xA9, 0x57, 0x49, 0xC1, 0xE6, 0x48, 0x00, 0x8A, 0xB6 }; + // key = 0x4ad292b272f2 after each byte has its bit order reversed + // uid = 0x96eac292 ditto + // initvec = 0x4ea276a6 ditto + const uint64_t key = REV64(0x524B494D4E4FUL); + const uint32_t uid = REV32(0x69574349); + const uint32_t iv = REV32(0x72456E65); + + PrintAndLogEx(DEBUG, "UID... %08" PRIx32, uid); + PrintAndLogEx(DEBUG, "IV.... %08" PRIx32, iv); + PrintAndLogEx(DEBUG, "Key... %012" PRIx64, key); + + // initialise + hitag_state_t state; + ht2_hitag2_init_ex(&state, key, uid, iv); + PrintAndLogEx(DEBUG, "hs shiftreg... %012" PRIx64, state.shiftreg); + + for (uint32_t i = 0; i < 16; i++) { + // get 8 bits of keystream + uint8_t x = (uint8_t) ht2_hitag2_nstep(&state, 8); + uint8_t y = expected[i]; + + PrintAndLogEx(DEBUG, "%02X (%02X)", x, y); + if (x != y) { + return 0; + } + } + return 1; +} + +static uint64_t hitag2_verify_crypto_test_round(void) { + + uint8_t expected[16] = { 0xD7, 0x23, 0x7F, 0xCE, 0x8C, 0xD0, 0x37, 0xA9, 0x57, 0x49, 0xC1, 0xE6, 0x48, 0x00, 0x8A, 0xB6 }; + const uint64_t key = REV64(0x524B494D4E4FUL); + const uint32_t uid = REV32(0x69574349); + const uint32_t iv = REV32(0x72456E65); + + PrintAndLogEx(DEBUG, "UID... %08" PRIx32, uid); + PrintAndLogEx(DEBUG, "IV.... %08" PRIx32, iv); + PrintAndLogEx(DEBUG, "Key... %012" PRIx64, key); + + // initialise + uint64_t cs = ht2_hitag2_init(key, uid, iv); + PrintAndLogEx(DEBUG, "hs shiftreg... %012" PRIx64, cs); + + for (uint32_t i = 0; i < 16; i++) { + // get 8 bits of keystream + uint8_t x = (uint8_t) ht2_hitag2_byte(&cs); + uint8_t y = expected[i]; + + PrintAndLogEx(DEBUG, "%02X (%02X)", x, y); + if (x != y) { + return 0; + } + } + return 1; +} + +static int CmdLFHitag2Selftest(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "lf hitag selftest", + "Perform selftest of Hitag crypto engine", + "lf hitag selftest\n" + ); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "======== " _CYAN_("Hitag2 crypto test") " ============================"); + uint64_t test = hitag2_verify_crypto_test(); + PrintAndLogEx(INFO, "Crypto self test ( %s )", test ? _GREEN_("ok") : _RED_("fail")); + + test |= hitag2_verify_crypto_test_round(); + PrintAndLogEx(INFO, "Crypto self test ROUND ( %s )", test ? _GREEN_("ok") : _RED_("fail")); + + test |= hitag2_benchtest(1); + PrintAndLogEx(INFO, "Hitag2 crypto, init + gen 32 bits ( us %" PRIu64 " )", test); + + test |= hitag2_benchtest_gen32(); + PrintAndLogEx(INFO, "Hitag2 crypto, gen new 32 bits only ( us: %" PRIu64 " )", test); + + test |= hitag2_benchtest(1000); + PrintAndLogEx(INFO, "Hitag2 crypto, init + gen 32 bits, x1000 ( us: %" PRIu64 " )", test); + + PrintAndLogEx(INFO, "--------------------------------------------------------"); + PrintAndLogEx(SUCCESS, "Tests ( %s )", (test) ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; +} static command_t CommandTable[] = { - {"-----------", CmdHelp, IfPm3Hitag, "------------------------ " _CYAN_("General") " ------------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, {"list", CmdLFHitagList, AlwaysAvailable, "List Hitag trace history"}, - {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Operations") " -----------------------"}, + {"-----------", CmdHelp, IfPm3Hitag, "------------------------ " _CYAN_("General") " ------------------------"}, {"info", CmdLFHitagInfo, IfPm3Hitag, "Hitag 2 tag information"}, + {"selftest", CmdLFHitag2Selftest, AlwaysAvailable, "Perform self test"}, + {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Operations") " -----------------------"}, + {"demod", CmdLFHitag2PWMDemod, IfPm3Hitag, "PWM Hitag 2 reader message demodulation"}, {"dump", CmdLFHitag2Dump, IfPm3Hitag, "Dump Hitag 2 tag"}, {"read", CmdLFHitagReader, IfPm3Hitag, "Read Hitag memory"}, + {"sniff", CmdLFHitagSniff, IfPm3Hitag, "Eavesdrop Hitag communication"}, {"view", CmdLFHitagView, AlwaysAvailable, "Display content from tag dump file"}, {"wrbl", CmdLFHitagWriter, IfPm3Hitag, "Write a block (page) in Hitag memory"}, - {"sniff", CmdLFHitagSniff, IfPm3Hitag, "Eavesdrop Hitag communication"}, - {"cc", CmdLFHitagSCheckChallenges, IfPm3Hitag, "Hitag S: test all provided challenges"}, - {"ta", CmdLFHitag2CheckChallenges, IfPm3Hitag, "Hitag 2: test all recorded authentications"}, {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Simulation") " -----------------------"}, {"eload", CmdLFHitagEload, IfPm3Hitag, "Upload file into emulator memory"}, // {"esave", CmdLFHitagESave, IfPm3Hitag, "Save emulator memory to file"}, {"eview", CmdLFHitagEview, IfPm3Hitag, "View emulator memory"}, {"sim", CmdLFHitagSim, IfPm3Hitag, "Simulate Hitag transponder"}, + {"-----------", CmdHelp, IfPm3Hitag, "----------------------- " _CYAN_("Recovery") " -----------------------"}, + {"cc", CmdLFHitagSCheckChallenges, IfPm3Hitag, "Hitag S: test all provided challenges"}, + {"chk", CmdLFHitag2Chk, IfPm3Hitag, "Check keys"}, + {"lookup", CmdLFHitag2Lookup, AlwaysAvailable, "Uses authentication trace to check for key in dictionary file"}, + {"ta", CmdLFHitag2CheckChallenges, IfPm3Hitag, "Hitag 2: test all recorded authentications"}, { NULL, NULL, 0, NULL } }; diff --git a/client/src/cmdlfhitag.h b/client/src/cmdlfhitag.h index c3cefc10df..049df8aa93 100644 --- a/client/src/cmdlfhitag.h +++ b/client/src/cmdlfhitag.h @@ -22,6 +22,7 @@ #include "common.h" +#define HITAG_NRAR_SIZE 8 #define HITAG_CRYPTOKEY_SIZE 6 #define HITAG_PASSWORD_SIZE 4 #define HITAG_UID_SIZE 4 @@ -37,16 +38,18 @@ #define HITAG2_CONFIG_BLOCK 3 #define HITAG2_CONFIG_OFFSET (HITAG_BLOCK_SIZE * HITAG2_CONFIG_BLOCK) +#define HITAG_DICTIONARY "ht2_default" int CmdLFHitag(const char *Cmd); int readHitagUid(void); void annotateHitag1(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response); -void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, uint8_t bits, bool is_response); +void annotateHitag2(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, uint8_t bits, bool is_response, const uint64_t *keys, uint32_t keycount, bool isdecrypted); void annotateHitagS(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, bool is_response); void annotateHitag2_init(void); - +bool hitag2_get_plain(uint8_t *plain, uint8_t *plen); +void hitag2_annotate_plain(char *exp, size_t size, const uint8_t *cmd, uint8_t cmdsize, uint8_t bits); uint8_t hitag1_CRC_check(uint8_t *d, uint32_t nbit); #endif diff --git a/client/src/cmdtrace.c b/client/src/cmdtrace.c index 8e421a7c7e..cfe139f726 100644 --- a/client/src/cmdtrace.c +++ b/client/src/cmdtrace.c @@ -501,7 +501,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr uint32_t end_of_transmission_timestamp = 0; uint8_t topaz_reader_command[9]; - char explanation[40] = {0}; + char explanation[60] = {0}; tracelog_hdr_t *first_hdr = (tracelog_hdr_t *)(trace); tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + tracepos); @@ -736,9 +736,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr // mark short bytes (less than 8 Bit + Parity) if (protocol == ISO_14443A || - protocol == PROTO_MIFARE || - protocol == PROTO_MFPLUS || - protocol == THINFILM) { + protocol == PROTO_MIFARE || + protocol == PROTO_MFPLUS || + protocol == THINFILM) { // approximated with 128 * (9 * data_len); uint16_t bitime = 1056 + 32; @@ -774,10 +774,9 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr end_of_transmission_timestamp = hdr->timestamp + duration; - if (prev_eot) + if (prev_eot) { *prev_eot = end_of_transmission_timestamp; - - + } // Always annotate these protocols both reader/tag messages switch (protocol) { @@ -793,7 +792,7 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr annotateHitag1(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); break; case PROTO_HITAG2: - annotateHitag2(explanation, sizeof(explanation), frame, data_len, parityBytes[0], hdr->isResponse); + annotateHitag2(explanation, sizeof(explanation), frame, data_len, parityBytes[0], hdr->isResponse, mfDicKeys, mfDicKeysCount, false); break; case PROTO_HITAGS: annotateHitagS(explanation, sizeof(explanation), frame, data_len, hdr->isResponse); @@ -979,6 +978,71 @@ static uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *tr } } + if (protocol == PROTO_HITAG2) { + + uint8_t ht2plain[9] = {0}; + uint8_t n = 0; + if (hitag2_get_plain(ht2plain, &n)) { + + memset(explanation, 0x00, sizeof(explanation)); + + // handle partial bytes. The parity array[0] is used to store number of left over bits from NBYTES + // This part prints the number of bits in the trace entry for hitag. + uint8_t nbits = parityBytes[0]; + + annotateHitag2(explanation, sizeof(explanation), ht2plain, n, nbits, hdr->isResponse, NULL, 0, true); + + // iceman: colorise crc bytes here will need a refactor of code from above. + for (int j = 0; j < n && (j / TRACE_MAX_HEX_BYTES) < TRACE_MAX_HEX_BYTES; j++) { + + + if (j == 0) { + + // only apply this to lesser than one byte + if (n == 1) { + + if (nbits == 5) { + snprintf(line[0], 120, "%2u: %02X ", nbits, ht2plain[0] >> (8 - nbits)); + } else { + snprintf(line[0], 120, "%2u: %02X ", nbits, ht2plain[0] >> (8 - nbits)); + } + + } else { + + if (nbits == 0) { + snprintf(line[0], 120, "%2u: %02X ", (n * 8), ht2plain[0]); + } else { + snprintf(line[0], 120, "%2u: %02X ", ((n - 1) * 8) + nbits, ht2plain[0]); + } + } + offset = 4; + + } else { + snprintf(line[j / 18] + ((j % 18) * 4) + offset, 120, "%02X ", ht2plain[j]); + } + } + + num_lines = MIN((n - 1) / TRACE_MAX_HEX_BYTES + 1, TRACE_MAX_HEX_BYTES); + + for (int j = 0; j < num_lines ; j++) { + if (hdr->isResponse) { + PrintAndLogEx(NORMAL, " | | * |%-*s | %-4s| %s", + str_padder, + line[j], + " ", + explanation); + } else { + PrintAndLogEx(NORMAL, " | | * |" _YELLOW_("%-*s")" | " _YELLOW_("%s") "| " _YELLOW_("%s"), + str_padder, + line[j], + " ", + explanation); + } + + } + } + } + if (is_last_record(tracepos, traceLen)) { return traceLen; } @@ -1416,7 +1480,7 @@ int CmdTraceList(const char *Cmd) { if (diclen > 0) { uint8_t *keyBlock = NULL; int res = loadFileDICTIONARY_safe(dictionary, (void **) &keyBlock, 6, &dicKeysCount); - if (res != PM3_SUCCESS || dicKeysCount == 0 || keyBlock == NULL) { + if (res != PM3_SUCCESS || dicKeysCount == 0 || keyBlock == NULL) { PrintAndLogEx(FAILED, "An error occurred while loading the dictionary! (we will use the default keys now)"); } else { dicKeys = calloc(dicKeysCount, sizeof(uint64_t)); @@ -1436,6 +1500,30 @@ int CmdTraceList(const char *Cmd) { } } + if ( protocol == PROTO_HITAG2) { + + if (strlen(dictionary) == 0) { + snprintf(dictionary, sizeof(dictionary), HITAG_DICTIONARY); + } + + // load keys + uint8_t *keyBlock = NULL; + int res = loadFileDICTIONARY_safe(dictionary, (void **) &keyBlock, HITAG_CRYPTOKEY_SIZE, &dicKeysCount); + if (res != PM3_SUCCESS || dicKeysCount == 0 || keyBlock == NULL) { + PrintAndLogEx(FAILED, "An error occurred while loading the dictionary!"); + } else { + dicKeys = calloc(dicKeysCount, sizeof(uint64_t)); + for (int i = 0; i < dicKeysCount; i++) { + uint64_t key = bytes_to_num(keyBlock + i * HITAG_CRYPTOKEY_SIZE, HITAG_CRYPTOKEY_SIZE); + memcpy((uint8_t *) &dicKeys[i], &key, sizeof(uint64_t)); + } + dictionaryLoad = true; + } + if (keyBlock != NULL) { + free(keyBlock); + } + } + PrintAndLogEx(NORMAL, ""); if (use_relative) { PrintAndLogEx(NORMAL, " Gap | Duration | Src | Data (! denotes parity error, ' denotes short bytes) | CRC | Annotation"); @@ -1463,16 +1551,19 @@ int CmdTraceList(const char *Cmd) { while (tracepos < gs_traceLen) { tracepos = printTraceLine(tracepos, gs_traceLen, gs_trace, protocol, show_wait_cycles, mark_crc, prev_EOT, use_us, dicKeys, dicKeysCount); - if (kbd_enter_pressed()) + if (kbd_enter_pressed()) { break; + } } - if (dictionaryLoad) + if (dictionaryLoad) { free((void *) dicKeys); + } } - if (show_hex) + if (show_hex) { PrintAndLogEx(HINT, "syntax to use: " _YELLOW_("`text2pcap -t \"%%S.\" -l 264 -n `")); + } return PM3_SUCCESS; } diff --git a/client/src/crypto/libpcrypto.c b/client/src/crypto/libpcrypto.c index 3634e9e4df..02627a8726 100644 --- a/client/src/crypto/libpcrypto.c +++ b/client/src/crypto/libpcrypto.c @@ -127,34 +127,38 @@ void des3_decrypt(void *out, const void *in, const void *key, uint8_t keycount) // NIST Special Publication 800-38A — Recommendation for block cipher modes of operation: methods and techniques, 2001. int aes_encode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length) { uint8_t iiv[16] = {0}; - if (iv) + if (iv) { memcpy(iiv, iv, 16); + } mbedtls_aes_context aes; mbedtls_aes_init(&aes); - if (mbedtls_aes_setkey_enc(&aes, key, 128)) + if (mbedtls_aes_setkey_enc(&aes, key, 128)) { return 1; - if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, length, iiv, input, output)) + } + if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_ENCRYPT, length, iiv, input, output)) { return 2; + } mbedtls_aes_free(&aes); - - return 0; + return PM3_SUCCESS; } int aes_decode(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *output, int length) { uint8_t iiv[16] = {0}; - if (iv) + if (iv) { memcpy(iiv, iv, 16); + } mbedtls_aes_context aes; mbedtls_aes_init(&aes); - if (mbedtls_aes_setkey_dec(&aes, key, 128)) + if (mbedtls_aes_setkey_dec(&aes, key, 128)) { return 1; - if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, length, iiv, input, output)) + } + if (mbedtls_aes_crypt_cbc(&aes, MBEDTLS_AES_DECRYPT, length, iiv, input, output)) { return 2; + } mbedtls_aes_free(&aes); - - return 0; + return PM3_SUCCESS; } // NIST Special Publication 800-38B — Recommendation for block cipher modes of operation: The CMAC mode for authentication. @@ -171,13 +175,14 @@ int aes_cmac8(uint8_t *iv, uint8_t *key, uint8_t *input, uint8_t *mac, int lengt memset(mac, 0x00, 8); int res = aes_cmac(iv, key, input, cmac_tmp, length); - if (res) + if (res) { return res; + } - for (int i = 0; i < 8; i++) + for (int i = 0; i < 8; i++) { mac[i] = cmac_tmp[i * 2 + 1]; - - return 0; + } + return PM3_SUCCESS; } static uint8_t fixed_rand_value[250] = {0}; @@ -188,21 +193,23 @@ static int fixed_rand(void *rng_state, unsigned char *output, size_t len) { memset(output, 0x00, len); } - return 0; + return PM3_SUCCESS; } int sha1hash(uint8_t *input, int length, uint8_t *hash) { - if (!hash || !input) + if (!hash || !input) { return 1; + } mbedtls_sha1(input, length, hash); - return 0; + return PM3_SUCCESS; } int sha256hash(uint8_t *input, int length, uint8_t *hash) { - if (!hash || !input) + if (!hash || !input) { return 1; + } mbedtls_sha256_context sctx; mbedtls_sha256_init(&sctx); @@ -211,12 +218,13 @@ int sha256hash(uint8_t *input, int length, uint8_t *hash) { mbedtls_sha256_finish(&sctx, hash); mbedtls_sha256_free(&sctx); - return 0; + return PM3_SUCCESS; } int sha512hash(uint8_t *input, int length, uint8_t *hash) { - if (!hash || !input) + if (!hash || !input) { return 1; + } mbedtls_sha512_context sctx; mbedtls_sha512_init(&sctx); @@ -225,33 +233,35 @@ int sha512hash(uint8_t *input, int length, uint8_t *hash) { mbedtls_sha512_finish(&sctx, hash); mbedtls_sha512_free(&sctx); - return 0; + return PM3_SUCCESS; } static int ecdsa_init_str(mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id curveid, const char *key_d, const char *key_x, const char *key_y) { - if (!ctx) + if (!ctx) { return 1; - - int res; + } mbedtls_ecdsa_init(ctx); - res = mbedtls_ecp_group_load(&ctx->grp, curveid); - if (res) + int res = mbedtls_ecp_group_load(&ctx->grp, curveid); + if (res) { return res; + } if (key_d) { res = mbedtls_mpi_read_string(&ctx->d, 16, key_d); - if (res) + if (res) { return res; + } } if (key_x && key_y) { res = mbedtls_ecp_point_read_string(&ctx->Q, 16, key_x, key_y); - if (res) + if (res) { return res; + } } - return 0; + return PM3_SUCCESS; } static int ecdsa_init(mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id curveid, uint8_t *key_d, uint8_t *key_xy) { @@ -278,7 +288,7 @@ static int ecdsa_init(mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id curveid, return res; } - return 0; + return PM3_SUCCESS; } int ecdsa_key_create(mbedtls_ecp_group_id curveid, uint8_t *key_d, uint8_t *key_xy) { @@ -519,8 +529,9 @@ int ecdsa_nist_test(bool verbose) { size_t siglen = 0; // NIST ecdsa test - if (verbose) - PrintAndLogEx(INFO, " ECDSA NIST test: " NOLF); + if (verbose) { + PrintAndLogEx(INFO, "ECDSA NIST test " NOLF); + } // make signature res = ecdsa_signature_create_test(curveid, T_PRIVATE_KEY, T_Q_X, T_Q_Y, T_K, input, length, signature, &siglen); // PrintAndLogEx(INFO, "res: %x signature[%x]: %s", (res < 0)? -res : res, siglen, sprint_hex(signature, siglen)); @@ -540,15 +551,16 @@ int ecdsa_nist_test(bool verbose) { uint8_t sval_s[33] = {0}; param_gethex_to_eol(T_S, 0, sval_s, sizeof(sval_s), &slen); if (strncmp((char *)rval, (char *)rval_s, 32) || strncmp((char *)sval, (char *)sval_s, 32)) { - PrintAndLogEx(INFO, "R or S check error"); + PrintAndLogEx(NORMAL, "( " _RED_("R or S check error") " )"); res = 100; goto exit; } // verify signature res = ecdsa_signature_verify_keystr(curveid, T_Q_X, T_Q_Y, input, length, signature, siglen, true); - if (res) + if (res) { goto exit; + } // verify wrong signature input[0] ^= 0xFF; @@ -559,8 +571,8 @@ int ecdsa_nist_test(bool verbose) { } if (verbose) { - PrintAndLogEx(NORMAL, _GREEN_("passed")); - PrintAndLogEx(INFO, " ECDSA binary signature create/check test: " NOLF); + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); + PrintAndLogEx(INFO, "ECDSA binary signature create/check test " NOLF); } // random ecdsa test @@ -587,12 +599,12 @@ int ecdsa_nist_test(bool verbose) { goto exit; if (verbose) - PrintAndLogEx(NORMAL, _GREEN_("passed\n")); + PrintAndLogEx(NORMAL, "( " _GREEN_("ok") " )"); return PM3_SUCCESS; exit: if (verbose) - PrintAndLogEx(NORMAL, _RED_("failed\n")); + PrintAndLogEx(NORMAL, "( " _RED_("fail") " )"); return res; } diff --git a/common/hitag2/hitag2_crypto.c b/common/hitag2/hitag2_crypto.c index 8fd596fa8b..9e94994267 100644 --- a/common/hitag2/hitag2_crypto.c +++ b/common/hitag2/hitag2_crypto.c @@ -16,10 +16,15 @@ // Hitag2 Crypto //----------------------------------------------------------------------------- #include "hitag2_crypto.h" - +#include #include "util.h" #include "string.h" #include "commonutil.h" +#include "pm3_cmd.h" + +#ifndef ON_DEVICE +#include "ui.h" +#endif /* Following is a modified version of cryptolib.com/ciphers/hitag2/ */ // Software optimized 48-bit Philips/NXP Mifare Hitag2 PCF7936/46/47/52 stream cipher algorithm by I.C. Wiener 2006-2007. @@ -27,51 +32,316 @@ // No warranties or guarantees of any kind. // This code is released into the public domain by its author. + // Single bit Hitag2 functions: #ifndef i4 -#define i4(x,a,b,c,d) ((uint32_t)((((x)>>(a))&1)+(((x)>>(b))&1)*2+(((x)>>(c))&1)*4+(((x)>>(d))&1)*8)) +#define i4(x,a,b,c,d) ((uint32_t)((((x)>>(a))&1)+(((x)>>(b))&1)*2+(((x)>>(c))&1)*4+(((x)>>(d))&1)*8)) #endif static const uint32_t ht2_f4a = 0x2C79; // 0010 1100 0111 1001 static const uint32_t ht2_f4b = 0x6671; // 0110 0110 0111 0001 static const uint32_t ht2_f5c = 0x7907287B; // 0111 1001 0000 0111 0010 1000 0111 1011 -static uint32_t ht2_f20(const uint64_t x) { - uint32_t i5; +static uint32_t ht2_f20(const uint64_t state) { - i5 = ((ht2_f4a >> i4(x, 1, 2, 4, 5)) & 1) * 1 - + ((ht2_f4b >> i4(x, 7, 11, 13, 14)) & 1) * 2 - + ((ht2_f4b >> i4(x, 16, 20, 22, 25)) & 1) * 4 - + ((ht2_f4b >> i4(x, 27, 28, 30, 32)) & 1) * 8 - + ((ht2_f4a >> i4(x, 33, 42, 43, 45)) & 1) * 16; + uint32_t i5 = ((ht2_f4a >> i4(state, 1, 2, 4, 5)) & 1) * 1 + + ((ht2_f4b >> i4(state, 7, 11, 13, 14)) & 1) * 2 + + ((ht2_f4b >> i4(state, 16, 20, 22, 25)) & 1) * 4 + + ((ht2_f4b >> i4(state, 27, 28, 30, 32)) & 1) * 8 + + ((ht2_f4a >> i4(state, 33, 42, 43, 45)) & 1) * 16; return (ht2_f5c >> i5) & 1; } -uint64_t ht2_hitag2_init(const uint64_t key, const uint32_t serial, const uint32_t IV) { - uint32_t i; +// return a single bit from a value +static int ht2_bitn(uint64_t x, int bit) { + const uint64_t bitmask = (uint64_t)(1) << bit; + return (x & bitmask) ? 1 : 0; +} + +// the sub-function R that rollback depends upon +int ht2_fnR(uint64_t state) { + // renumbered bits because my state is 0-47, not 1-48 + return ( + ht2_bitn(state, 1) ^ ht2_bitn(state, 2) ^ ht2_bitn(state, 5) ^ + ht2_bitn(state, 6) ^ ht2_bitn(state, 7) ^ ht2_bitn(state, 15) ^ + ht2_bitn(state, 21) ^ ht2_bitn(state, 22) ^ ht2_bitn(state, 25) ^ + ht2_bitn(state, 29) ^ ht2_bitn(state, 40) ^ ht2_bitn(state, 41) ^ + ht2_bitn(state, 42) ^ ht2_bitn(state, 45) ^ ht2_bitn(state, 46) ^ + ht2_bitn(state, 47) + ); +} + +/* +static void ht2_rollback(hitag_state_t *hstate, unsigned int steps) { + for (int i = 0; i < steps; i++) { + hstate->shiftreg = ((hstate->shiftreg << 1) & 0xffffffffffff) | ht2_fnR(hstate->shiftreg); + } +} +*/ +// the rollback function that lets us go backwards in time +void ht2_rollback(hitag_state_t *hstate, uint32_t steps) { + for (uint32_t i = 0; i < steps; i++) { + hstate->shiftreg = ((hstate->shiftreg << 1) & 0xffffffffffff) | ht2_fnR(hstate->shiftreg); + hstate->lfsr = LFSR_INV(hstate->lfsr); + } +} + +// the three filter sub-functions that feed fnf +#define ht2_fa(x) ht2_bitn(0x2C79, (x)) +#define ht2_fb(x) ht2_bitn(0x6671, (x)) +#define ht2_fc(x) ht2_bitn(0x7907287B, (x)) + +// the filter function that generates a bit of output from the prng state +int ht2_fnf(uint64_t state) { + + uint32_t x1 = (ht2_bitn(state, 2) << 0) | (ht2_bitn(state, 3) << 1) | (ht2_bitn(state, 5) << 2) | (ht2_bitn(state, 6) << 3); + uint32_t x2 = (ht2_bitn(state, 8) << 0) | (ht2_bitn(state, 12) << 1) | (ht2_bitn(state, 14) << 2) | (ht2_bitn(state, 15) << 3); + uint32_t x3 = (ht2_bitn(state, 17) << 0) | (ht2_bitn(state, 21) << 1) | (ht2_bitn(state, 23) << 2) | (ht2_bitn(state, 26) << 3); + uint32_t x4 = (ht2_bitn(state, 28) << 0) | (ht2_bitn(state, 29) << 1) | (ht2_bitn(state, 31) << 2) | (ht2_bitn(state, 33) << 3); + uint32_t x5 = (ht2_bitn(state, 34) << 0) | (ht2_bitn(state, 43) << 1) | (ht2_bitn(state, 44) << 2) | (ht2_bitn(state, 46) << 3); + + uint32_t x6 = (ht2_fa(x1) << 0) | (ht2_fb(x2) << 1) | (ht2_fb(x3) << 2) | (ht2_fb(x4) << 3) | (ht2_fa(x5) << 4); + return ht2_fc(x6); +} + +// builds the lfsr for the prng (quick calcs for hitag2_nstep()) +/* +static void ht2_buildlfsr(hitag_state_t *hstate) { + if (hstate == NULL) { + return; + } + + uint64_t state = hstate->shiftreg; + uint64_t temp = state ^ (state >> 1); + hstate->lfsr = state ^ (state >> 6) ^ (state >> 16) + ^ (state >> 26) ^ (state >> 30) ^ (state >> 41) + ^ (temp >> 2) ^ (temp >> 7) ^ (temp >> 22) + ^ (temp >> 42) ^ (temp >> 46); +} +*/ +#ifndef ON_DEVICE +#include +#endif + +uint64_t ht2_recoverkey(hitag_state_t *hstate, uint32_t uid, uint32_t nRenc) { + +// hstate->shiftreg = (uint64_t)(((hstate->shiftreg << 1) & 0xffffffffffff) | (uint64_t)ht2_fnR(hstate->shiftreg)); +// hstate->shiftreg = (uint64_t)(((hstate->shiftreg << 1) & 0xffffffffffff) | (uint64_t)ht2_fnR(hstate->shiftreg)); + +#ifndef ON_DEVICE + PrintAndLogEx(INFO, "shiftreg.... %" PRIx64, hstate->shiftreg); +#endif + + // key lower 16 bits are lower 16 bits of prng state + uint64_t key = hstate->shiftreg & 0xffff; + uint32_t nRxork = (hstate->shiftreg >> 16) & 0xffffffff; + + // rollback and extract bits b + uint32_t b = 0; + for (uint8_t i = 0; i < 32; i++) { + hstate->shiftreg = ((hstate->shiftreg) << 1) | ((uid >> (31 - i)) & 0x1); + b = (b << 1) | (unsigned int) ht2_fnf(hstate->shiftreg); + } + + uint32_t nR = nRenc ^ b; + uint64_t keyupper = nRxork ^ nR; + key = key | (keyupper << 16); + +#ifndef ON_DEVICE + + + + PrintAndLogEx(INFO, "b..... %08" PRIx32 " %08" PRIx32 " %012" PRIx64, b, nRenc, hstate->shiftreg); + PrintAndLogEx(INFO, "key... %012" PRIx64 " %012" PRIx64 "\n", key, REV64(key)); +#endif + return key; +} + +/* + * Parameters: + * Hitag_State* pstate - output, internal state after initialisation + * uint64_t sharedkey - 48 bit key shared between reader & tag + * uint32_t serialnum - 32 bit tag serial number + * uint32_t iv - 32 bit random IV from reader, part of tag authentication + */ +void ht2_hitag2_init_ex(hitag_state_t *hstate, uint64_t sharedkey, uint32_t serialnum, uint32_t iv) { + // init state, from serial number and lowest 16 bits of shared key + uint64_t state = ((sharedkey & 0xFFFF) << 32) | serialnum; + + // mix the initialisation vector and highest 32 bits of the shared key + iv ^= (uint32_t)(sharedkey >> 16); + + // move 16 bits from (IV xor Shared Key) to top of uint64_t state + // these will be XORed in turn with output of the crypto function + state |= (uint64_t) iv << 48; + iv >>= 16; + + // unrolled loop is faster on PIC32 (MIPS), do 32 times + // shift register, then calc new bit + state >>= 1; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + + // highest 16 bits of IV XOR Shared Key + state |= (uint64_t) iv << 47; + + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state = (state >> 1) ^ (uint64_t) ht2_f20(state) << 46; + state ^= (uint64_t) ht2_f20(state) << 47; + + // LSFR + + hstate->shiftreg = state; + /* naive version for reference, LFSR has 16 taps + pstate->lfsr = state ^ (state >> 2) ^ (state >> 3) ^ (state >> 6) + ^ (state >> 7) ^ (state >> 8) ^ (state >> 16) ^ (state >> 22) + ^ (state >> 23) ^ (state >> 26) ^ (state >> 30) ^ (state >> 41) + ^ (state >> 42) ^ (state >> 43) ^ (state >> 46) ^ (state >> 47); + */ + { + // optimise with one 64-bit intermediate + uint64_t temp = state ^ (state >> 1); + hstate->lfsr = state ^ (state >> 6) ^ (state >> 16) + ^ (state >> 26) ^ (state >> 30) ^ (state >> 41) + ^ (temp >> 2) ^ (temp >> 7) ^ (temp >> 22) + ^ (temp >> 42) ^ (temp >> 46); + } +} + +/* + * Return up to 32 crypto bits. + * Last bit is in least significant bit, earlier bits are shifted left. + * Note that the Hitag transmission protocol is least significant bit, + * so we may want to change this, or add a function, that returns the + * crypto output bits in the other order. + * + * Parameters: + * Hitag_State* pstate - in/out, internal cipher state after initialisation + * uint32_t steps - number of bits requested, (capped at 32) + */ +uint32_t ht2_hitag2_nstep(hitag_state_t *hstate, uint32_t steps) { + uint64_t state = hstate->shiftreg; + uint32_t result = 0; + uint64_t lfsr = hstate->lfsr; + + if (steps == 0) { + return 0; + } + + do { + // update shift registers + if (lfsr & 1) { + state = (state >> 1) | 0x800000000000; + lfsr = (lfsr >> 1) ^ 0xB38083220073; + // accumulate next bit of crypto + result = (result << 1) | ht2_f20(state); + } else { + state >>= 1; + lfsr >>= 1; + result = (result << 1) | ht2_f20(state); + } + } while (--steps); + + hstate->shiftreg = state; + hstate->lfsr = lfsr; + return result; +} + +uint64_t ht2_hitag2_init(const uint64_t key, const uint32_t serial, const uint32_t iv) { + uint64_t x = ((key & 0xFFFF) << 32) + serial; - for (i = 0; i < 32; i++) { + for (uint32_t i = 0; i < 32; i++) { x >>= 1; - x += (uint64_t)(ht2_f20(x) ^ (((IV >> i) ^ (key >> (i + 16))) & 1)) << 47; + x += (uint64_t)(ht2_f20(x) ^ (((iv >> i) ^ (key >> (i + 16))) & 1)) << 47; } return x; } -uint64_t ht2_hitag2_round(uint64_t *state) { - uint64_t x = *state; +int ht2_try_state(uint64_t s, uint32_t uid, uint32_t aR2, uint32_t nR1, uint32_t nR2, uint64_t *key) { - x = (x >> 1) + - ((((x >> 0) ^ (x >> 2) ^ (x >> 3) ^ (x >> 6) - ^ (x >> 7) ^ (x >> 8) ^ (x >> 16) ^ (x >> 22) - ^ (x >> 23) ^ (x >> 26) ^ (x >> 30) ^ (x >> 41) - ^ (x >> 42) ^ (x >> 43) ^ (x >> 46) ^ (x >> 47)) & 1) << 47); + hitag_state_t hstate; + hstate.shiftreg = s; + hstate.lfsr = 0; - *state = x; - return ht2_f20(x); + hstate.shiftreg = (uint64_t)(((hstate.shiftreg << 1) & 0xffffffffffff) | (uint64_t)ht2_fnR(hstate.shiftreg)); + hstate.shiftreg = (uint64_t)(((hstate.shiftreg << 1) & 0xffffffffffff) | (uint64_t)ht2_fnR(hstate.shiftreg)); + +#ifndef ON_DEVICE + hitag_state_t hs2; + hs2.shiftreg = s; + hs2.lfsr = 0; + ht2_rollback(&hs2, 2); + + PrintAndLogEx(INFO, "hstate shiftreg.... %" PRIx64 " lfsr... %" PRIx64, hstate.shiftreg, hstate.lfsr); + PrintAndLogEx(INFO, "hstate shiftreg.... %" PRIx64 " lfsr... %" PRIx64, hs2.shiftreg, hs2.lfsr); +#endif + + // recover key + uint64_t keyrev = hstate.shiftreg & 0xffff; + uint64_t nR1xk = (hstate.shiftreg >> 16) & 0xffffffff; + +#ifndef ON_DEVICE + PrintAndLogEx(INFO, "keyrev...... %012" PRIx64 " nR1xk... %08" PRIx64, keyrev, nR1xk); +#endif + + uint32_t b = 0; + for (uint8_t i = 0; i < 32; i++) { + hstate.shiftreg = ((hstate.shiftreg) << 1) | ((uid >> (31 - i)) & 0x1); + b = (b << 1) | (unsigned int) ht2_fnf(hstate.shiftreg); + } + +#ifndef ON_DEVICE + PrintAndLogEx(INFO, "b..... %08" PRIx32 " %08" PRIx32 " %012" PRIx64, b, nR1, hstate.shiftreg); +#endif + + keyrev |= (nR1xk ^ nR1 ^ b) << 16; + +#ifndef ON_DEVICE + PrintAndLogEx(INFO, "key... %012" PRIx64 " %012" PRIx64, keyrev, REV64(keyrev)); +#endif + + // test key + ht2_hitag2_init_ex(&hstate, keyrev, uid, nR2); + + if ((aR2 ^ ht2_hitag2_nstep(&hstate, 32)) == 0xFFFFFFFF) { + *key = REV64(keyrev); + return PM3_SUCCESS; + } + return PM3_ESOFT; } + // "MIKRON" = O N M I K R // Key = 4F 4E 4D 49 4B 52 - Secret 48-bit key // Serial = 49 43 57 69 - Serial number of the tag, transmitted in clear @@ -82,11 +352,48 @@ uint64_t ht2_hitag2_round(uint64_t *state) { // The inverse of the first 4 bytes is sent to the tag to authenticate. // The rest is encrypted by XORing it with the subsequent keystream. -uint32_t ht2_hitag2_byte(uint64_t *x) { - uint32_t i, c; - for (i = 0, c = 0; i < 8; i++) { - c += (uint32_t) ht2_hitag2_round(x) << (i ^ 7); - } +/* + * Return 8 crypto bits. + * Last bit is in least significant bit, earlier bits are shifted left. + * Note that the Hitag transmission protocol is least significant bit, + * so we may want to change this, or add a function, that returns the + * crypto output bits in the other order. + * + * Parameters: + * uint64_t *state - in/out, internal cipher state after initialisation + */ +uint64_t ht2_hitag2_bit(uint64_t *state) { + uint64_t x = *state; + + x = (x >> 1) + + ((((x >> 0) ^ (x >> 2) ^ (x >> 3) ^ (x >> 6) + ^ (x >> 7) ^ (x >> 8) ^ (x >> 16) ^ (x >> 22) + ^ (x >> 23) ^ (x >> 26) ^ (x >> 30) ^ (x >> 41) + ^ (x >> 42) ^ (x >> 43) ^ (x >> 46) ^ (x >> 47)) & 1) << 47); + + *state = x; + return ht2_f20(x); +} + +// Take a state and create one byte (8bits) of crypto +uint32_t ht2_hitag2_byte(uint64_t *state) { + uint32_t c = 0; + c += (uint32_t) ht2_hitag2_bit(state) << 7; // 7 + c += (uint32_t) ht2_hitag2_bit(state) << 6; // 6 + c += (uint32_t) ht2_hitag2_bit(state) << 5; // 5 + c += (uint32_t) ht2_hitag2_bit(state) << 4; + c += (uint32_t) ht2_hitag2_bit(state) << 3; + c += (uint32_t) ht2_hitag2_bit(state) << 2; + c += (uint32_t) ht2_hitag2_bit(state) << 1; + c += (uint32_t) ht2_hitag2_bit(state) << 0; + return c; +} + +uint32_t ht2_hitag2_word(uint64_t *state, uint32_t steps) { + uint32_t c = 0; + do { + c += (uint32_t) ht2_hitag2_bit(state) << (steps - 1); + } while (--steps); return c; } @@ -108,19 +415,23 @@ void ht2_hitag2_cipher_reset(hitag2_t *tag, const uint8_t *iv) { tag->cs = ht2_hitag2_init(REV64(key), REV32(uid), REV32(iv_)); } -int ht2_hitag2_cipher_authenticate(uint64_t *cs, const uint8_t *authenticator_is) { +int ht2_hitag2_cipher_authenticate(uint64_t *state, const uint8_t *authenticator_is) { uint8_t authenticator_should[4]; - authenticator_should[0] = ~ht2_hitag2_byte(cs); - authenticator_should[1] = ~ht2_hitag2_byte(cs); - authenticator_should[2] = ~ht2_hitag2_byte(cs); - authenticator_should[3] = ~ht2_hitag2_byte(cs); + authenticator_should[0] = ~ht2_hitag2_byte(state); + authenticator_should[1] = ~ht2_hitag2_byte(state); + authenticator_should[2] = ~ht2_hitag2_byte(state); + authenticator_should[3] = ~ht2_hitag2_byte(state); return (memcmp(authenticator_should, authenticator_is, 4) == 0); } -int ht2_hitag2_cipher_transcrypt(uint64_t *cs, uint8_t *data, uint16_t bytes, uint16_t bits) { +void ht2_hitag2_cipher_transcrypt(uint64_t *state, uint8_t *data, uint16_t bytes, uint16_t bits) { int i; - for (i = 0; i < bytes; i++) data[i] ^= ht2_hitag2_byte(cs); - for (i = 0; i < bits; i++) data[bytes] ^= ht2_hitag2_round(cs) << (7 - i); - return 0; + for (i = 0; i < bytes; i++) { + data[i] ^= ht2_hitag2_byte(state); + } + + for (i = 0; i < bits; i++) { + data[bytes] ^= ht2_hitag2_bit(state) << (7 - i); + } } diff --git a/common/hitag2/hitag2_crypto.h b/common/hitag2/hitag2_crypto.h index 381417621d..1dae77353a 100644 --- a/common/hitag2/hitag2_crypto.h +++ b/common/hitag2/hitag2_crypto.h @@ -17,6 +17,12 @@ #define __HITAG2_CRYPTO_H #include "common.h" +#include + +#ifndef LFSR_INV +#define LFSR_INV(state) (((state) << 1) | (__builtin_parityll((state) & ((0xce0044c101cd >> 1) | (1ull << 47))))) +#endif + typedef struct { uint32_t uid; @@ -32,11 +38,27 @@ typedef struct { uint8_t sectors[12][4]; } hitag2_t; -uint64_t ht2_hitag2_init(const uint64_t key, const uint32_t serial, const uint32_t IV); -uint64_t ht2_hitag2_round(uint64_t *state); -uint32_t ht2_hitag2_byte(uint64_t *x); +typedef struct { + uint64_t shiftreg; // naive shift register, required for nonlinear fn input + uint64_t lfsr; // fast lfsr, used to make software faster +} hitag_state_t; + +void ht2_hitag2_init_ex(hitag_state_t *hstate, uint64_t sharedkey, uint32_t serialnum, const uint32_t iv); +void ht2_rollback(hitag_state_t *hstate, uint32_t steps); +uint64_t ht2_recoverkey(hitag_state_t *hstate, uint32_t uid, uint32_t nRenc); +uint32_t ht2_hitag2_nstep(hitag_state_t *hstate, uint32_t steps); +uint32_t ht2_hitag_acid(hitag_state_t *hstate, uint32_t steps); + +int ht2_try_state(uint64_t s, uint32_t uid, uint32_t aR2, uint32_t nR1, uint32_t nR2, uint64_t *key); + +uint32_t ht2_hitag2_word(uint64_t *state, uint32_t steps); +uint64_t ht2_hitag2_init(const uint64_t key, const uint32_t serial, const uint32_t iv); +uint64_t ht2_hitag2_bit(uint64_t *state); +uint32_t ht2_hitag2_byte(uint64_t *state); void ht2_hitag2_cipher_reset(hitag2_t *tag, const uint8_t *iv); -int ht2_hitag2_cipher_authenticate(uint64_t *cs, const uint8_t *authenticator_is); -int ht2_hitag2_cipher_transcrypt(uint64_t *cs, uint8_t *data, uint16_t bytes, uint16_t bits) ; +int ht2_hitag2_cipher_authenticate(uint64_t *state, const uint8_t *authenticator_is); +void ht2_hitag2_cipher_transcrypt(uint64_t *state, uint8_t *data, uint16_t bytes, uint16_t bits) ; +int ht2_fnf(uint64_t state); +int ht2_fnR(uint64_t state); #endif diff --git a/common/parity.h b/common/parity.h index 3694c515a7..3985a1e040 100644 --- a/common/parity.h +++ b/common/parity.h @@ -42,10 +42,8 @@ static const uint8_t g_odd_byte_parity[256] = { 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 }; -//extern const uint8_t OddByteParity[256]; - -#define ODD_PARITY8(x) { g_odd_byte_parity[x] } -#define EVEN_PARITY8(x) { !g_odd_byte_parity[x] } +#define ODD_PARITY8(x) g_odd_byte_parity[x] +#define EVEN_PARITY8(x) !g_odd_byte_parity[x] static inline uint8_t oddparity8(const uint8_t x) { return g_odd_byte_parity[x]; @@ -60,7 +58,7 @@ static inline uint8_t evenparity16(uint16_t x) { x ^= x >> 8; return EVEN_PARITY8(x) ; #else - return (__builtin_parity(x) & 0xFF); + return __builtin_parity(x); #endif } @@ -77,9 +75,9 @@ static inline uint8_t evenparity32(uint32_t x) { #if !defined __GNUC__ x ^= x >> 16; x ^= x >> 8; - return EVEN_PARITY8(x); + return EVEN_PARITY8(x) ; #else - return (__builtin_parity(x) & 0xFF); + return __builtin_parity(x); #endif } diff --git a/include/common.h b/include/common.h index a2542ef389..b405b2ac70 100644 --- a/include/common.h +++ b/include/common.h @@ -127,6 +127,17 @@ extern bool g_tearoff_enabled; #endif #endif +// endian change for 48bit +#ifndef BSWAP_48 +#define BSWAP_48(x) \ + (((uint64_t)(x) << 40) & 0x0000ff0000000000ULL) | \ + (((uint64_t)(x) << 24) & 0x000000ff00000000ULL) | \ + (((uint64_t)(x) << 8) & 0x00000000ff000000ULL) | \ + (((uint64_t)(x) >> 8) & 0x000000000ff0000ULL) | \ + (((uint64_t)(x) >> 24) & 0x00000000000ff00ULL) | \ + (((uint64_t)(x) >> 40) & 0x0000000000000ffULL) +#endif + // endian change for 32bit #ifdef __GNUC__ #ifndef BSWAP_32 diff --git a/include/hitag.h b/include/hitag.h index fd0b2ddac4..06a8de9cfd 100644 --- a/include/hitag.h +++ b/include/hitag.h @@ -39,37 +39,28 @@ typedef enum { RHT2F_UID_ONLY = 26, WHT2F_PASSWORD = 27, HT2_LAST_CMD = WHT2F_PASSWORD, -} hitag_function; +} PACKED hitag_function; typedef struct { - uint8_t password[4]; -} PACKED rht2d_password; - -typedef struct { - uint8_t NrAr[8]; + hitag_function cmd; + int16_t page; uint8_t data[4]; -} PACKED rht2d_authenticate; - -typedef struct { + uint8_t NrAr[8]; uint8_t key[6]; - uint8_t data[4]; -} PACKED rht2d_crypto; + uint8_t pwd[4]; -typedef struct { + // 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]; - uint8_t key[4]; -} PACKED rht1d_authenticate; - -typedef union { - rht2d_password pwd; - rht1d_authenticate ht1auth; - rht2d_authenticate auth; - rht2d_crypto crypto; -} hitag_data; +} PACKED lf_hitag_data_t; +typedef struct { + int status; + uint8_t data[48]; +} PACKED lf_hitag_crack_response_t; //--------------------------------------------------------- // Hitag S diff --git a/tools/hitag2crack/hitag2_gen_nRaR.py b/tools/hitag2crack/hitag2_gen_nRaR.py index 232add1f3d..68256b61e6 100755 --- a/tools/hitag2crack/hitag2_gen_nRaR.py +++ b/tools/hitag2crack/hitag2_gen_nRaR.py @@ -67,9 +67,9 @@ def hitag2_init(key, uid, nonce): #print '%012x' % state #print '%012x' % (int("{0:048b}".format(state)[::-1],2)) for i in range(0, 32): - nonce_bit = (f20(state) ^ ((nonce >> (31-i)) & 1)) + nonce_bit = (f20(state) ^ ((nonce >> (31 - i)) & 1)) #print nonce_bit - state = (state >> 1) | (((nonce_bit ^ (key >> (31-i))) & 1) << 47) + state = (state >> 1) | (((nonce_bit ^ (key >> (31 - i))) & 1) << 47) #print '%012x' % state #print '%012x' % (int("{0:048b}".format(state)[::-1],2)) return state @@ -81,6 +81,7 @@ def lfsr_feedback(state): ^ (state >> 26) ^ (state >> 30) ^ (state >> 41) ^ (state >> 42) ^ (state >> 43) ^ (state >> 46) ^ (state >> 47)) & 1) + def lfsr(state): return (state >> 1) + (lfsr_feedback(state) << 47) @@ -93,15 +94,17 @@ def lfsr_feedback_inv(state): ^ (state >> 46)) & 1) def lfsr_inv(state): - return ((state << 1) + (lfsr_feedback_inv(state))) & ((1<<48)-1) + return ((state << 1) + (lfsr_feedback_inv(state))) & ((1 << 48) - 1) def hitag2(state, length=48): c = 0 for i in range(0, length): c = (c << 1) | f20(state) - #print '%012x' % state - #print '%012x' % (int("{0:048b}".format(state)[::-1],2)) + #print ('%012x' % state) state = lfsr(state) + #print ('%012x' % (int("{0:048b}".format(state)[::-1],2))) + #print('%08X %08X' % (c, state)) + #print('final: %08X %08X' % (c, state)) return c if __name__ == "__main__": @@ -111,8 +114,15 @@ def hitag2(state, length=48): uid = int(sys.argv[2], 16) n = int(sys.argv[3]) for i in range(n): - nonce = random.randrange(2**32) - state = hitag2_init(key, uid, nonce) - print('%08X %08X' % (nonce, hitag2(state, 32)^0xffffffff)) + nonceA = random.randrange(2**32) + stateA = hitag2_init(key, uid, nonceA) + csA = hitag2(stateA, 32) ^ 0xffffffff + # print('%08X %08X' % (nonceA, csA)) + + nonceB = random.randrange(2**32) + stateB = hitag2_init(key, uid, nonceB) + csB = hitag2(stateB, 32) ^ 0xffffffff + print('./ht2crack5opencl %08X %08X %08X %08X %08X' % (uid, nonceA, csA, nonceB, csB)) + print('lf hitag lookup --uid %08X --nr %08X --ar %08X --key %012X' % (uid, nonceA, csA, key)) else: print("Usage: python %s " % sys.argv[0]) diff --git a/tools/pm3_tests.sh b/tools/pm3_tests.sh index 7b28a3f09a..d2ffb74c49 100755 --- a/tools/pm3_tests.sh +++ b/tools/pm3_tests.sh @@ -414,6 +414,7 @@ while true; do if ! CheckExecute "nfc decode test - signature" "$CLIENTBIN -c 'nfc decode -d 03FF010194113870696C65742E65653A656B616172743A3266195F26063132303832325904202020205F28033233335F2701316E1B5A13333038363439303039303030323636343030355304EBF2CE704103000000AC536967010200803A2448FCA7D354A654A81BD021150D1A152D1DF4D7A55D2B771F12F094EAB6E5E10F2617A2F8DAD4FD38AFF8EA39B71C19BD42618CDA86EE7E144636C8E0E7CFC4096E19C3680E09C78A0CDBC05DA2D698E551D5D709717655E56FE3676880B897D2C70DF5F06ECE07C71435255144F8EE41AF110E7B180DA0E6C22FB8FDEF61800025687474703A2F2F70696C65742E65652F6372742F33303836343930302D303030312E637274FE'" "30864900-0001.crt"; then break; fi echo -e "\n${C_BLUE}Testing LF:${C_NC}" + if ! CheckExecute "lf hitag2 test" "$CLIENTBIN -c 'lf hitag selftest'" "Tests \( ok"; then break; fi if ! CheckExecute "lf cotag demod test" "$CLIENTBIN -c 'data load -f traces/lf_cotag_220_8331.pm3; data norm; data cthreshold -u 50 -d -20; data envelope; data raw --ar -c 272; lf cotag demod'" \ "COTAG Found: FC 220, CN: 8331 Raw: FFB841170363FFFE00001E7F00000000"; then break; fi if ! CheckExecute "lf AWID test" "$CLIENTBIN -c 'data load -f traces/lf_AWID-15-259.pm3;lf search -1'" "AWID ID found"; then break; fi