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.

This commit is contained in:
iceman1001 2024-04-22 16:20:24 +02:00
parent fc2a3dd2c5
commit c8849af5e0
20 changed files with 2703 additions and 708 deletions

@ -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)

@ -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 =

@ -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

File diff suppressed because it is too large Load Diff

@ -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

@ -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++)
resp[i] = (rx[i / 8] >> (7 - (i % 8))) & 0x1;
for (int i = 0; i < 32; i++)
for (int i = 0; i < 48; i++) {
resp[i] = (rx[i / 8] >> (7 - (i % 8))) & 0x1;
}
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;
default:
if (g_dbglevel >= DBG_EXTENDED)
Dbprintf("unknown rxlen: (%i) %02X %02X %02X %02X ...", rxlen, rx[0], rx[1], rx[2], rx[3]);
break;
}
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);
hitagS_init_clock();
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
//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;
}
}
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 @@ write_end:
* 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;
}

@ -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

@ -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

@ -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
#
# TSPL
5453504C
05040202
25293C2F
# Gone in 360 seconds
FFFF814632FF
#
# Paxton HT2
BDF5E846
#
#
# GE HT2 reader
# TSPL
5453504C
05040202
25293C2F

File diff suppressed because it is too large Load Diff

@ -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

@ -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 <input-text-file> <output-pcapng-file>`"));
}
return PM3_SUCCESS;
}

@ -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;
}

@ -16,10 +16,15 @@
// Hitag2 Crypto
//-----------------------------------------------------------------------------
#include "hitag2_crypto.h"
#include <inttypes.h>
#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,39 +32,337 @@
// 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 <stdio.h>
#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) {
int ht2_try_state(uint64_t s, uint32_t uid, uint32_t aR2, uint32_t nR1, uint32_t nR2, uint64_t *key) {
hitag_state_t hstate;
hstate.shiftreg = s;
hstate.lfsr = 0;
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
// 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.
/*
* 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) +
@ -72,21 +375,25 @@ uint64_t ht2_hitag2_round(uint64_t *state) {
return ht2_f20(x);
}
// "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
// 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;
}
// 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.
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);
}
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);
}
}

@ -17,6 +17,12 @@
#define __HITAG2_CRYPTO_H
#include "common.h"
#include <stdbool.h>
#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);
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) ;
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 *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

@ -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
}

@ -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

@ -39,37 +39,28 @@ typedef enum {
RHT2F_UID_ONLY = 26,
WHT2F_PASSWORD = 27,
HT2_LAST_CMD = WHT2F_PASSWORD,
} hitag_function;
typedef struct {
uint8_t password[4];
} PACKED rht2d_password;
} PACKED hitag_function;
typedef struct {
hitag_function cmd;
int16_t page;
uint8_t data[4];
uint8_t NrAr[8];
uint8_t data[4];
} PACKED rht2d_authenticate;
typedef struct {
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

@ -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 @@ if __name__ == "__main__":
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 <key> <uid> <nr of nRaR to generate>" % sys.argv[0])

@ -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