From ee1eadee0f50922d9d88692bbc5ea12a16e0d1fc Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 13 Mar 2015 07:36:52 +0100 Subject: [PATCH 01/47] add: start to support Topaz tags - hf 14a reader now exits gracefully in case of proprietary anticollision sequence - changed miller decoder to handle Topaz 8 data bits/no parity frames from reader - started to implement hf list topaz --- armsrc/iso14443a.c | 5 +++ armsrc/iso14443a.h | 2 +- client/cmdhf.c | 97 ++++++++++++++++++++++++++++++---------------- client/cmdhf14a.c | 14 ++++++- common/protocols.h | 17 ++++++-- 5 files changed, 96 insertions(+), 39 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index ac839cfd..f52e3eb8 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1719,6 +1719,11 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u memset(uid_ptr,0,10); } + // check for proprietary anticollision: + if ((resp[0] & 0x1F) == 0) { + return 3; + } + // OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in // which case we need to make a cascade 2 request and select - this is a long UID // While the UID is not complete, the 3nd bit (from the right) is set in the SAK. diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 1e978e88..d99236b2 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -56,7 +56,7 @@ typedef struct { // DROP_FIRST_HALF, } state; uint16_t shiftReg; - uint16_t bitCount; + int16_t bitCount; uint16_t len; uint16_t byteCntMax; uint16_t posCnt; diff --git a/client/cmdhf.c b/client/cmdhf.c index 22063bbb..03d89c0b 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -141,6 +141,23 @@ void annotateIso15693(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) } } + +void annotateTopaz(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) +{ + + switch(cmd[0]) { + case TOPAZ_REQA :snprintf(exp, size, "REQA");break; + case TOPAZ_WUPA :snprintf(exp, size, "WUPA");break; + case TOPAZ_RID :snprintf(exp, size, "RID");break; + case TOPAZ_RALL :snprintf(exp, size, "RALL");break; + case TOPAZ_READ :snprintf(exp, size, "READ");break; + case TOPAZ_WRITE_E :snprintf(exp, size, "WRITE-E");break; + case TOPAZ_WRITE_NE :snprintf(exp, size, "WRITE-NE");break; + default: snprintf(exp,size,"?"); break; + } +} + + /** 06 00 = INITIATE 0E xx = SELECT ID (xx = Chip-ID) @@ -255,11 +272,18 @@ uint8_t iclass_CRC_check(bool isResponse, uint8_t* data, uint8_t len) } } + +uint16_t merge_topaz_reader_frames(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t *topaz_reader_command, uint16_t *data_len) +{ + return tracepos; +} + + uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol, bool showWaitCycles) { bool isResponse; uint16_t duration, data_len, parity_len; - + uint8_t topaz_reader_command[9]; uint32_t timestamp, first_timestamp, EndOfTransmissionTimestamp; char explanation[30] = {0}; @@ -290,29 +314,35 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui uint8_t *parityBytes = trace + tracepos; tracepos += parity_len; + if (protocol == TOPAZ && !isResponse) { + // topaz reader commands come in 1 or 9 separate frames with 8 Bits each. + // merge them: + tracepos = merge_topaz_reader_frames(tracepos, traceLen, trace, topaz_reader_command, &data_len); + } + //Check the CRC status uint8_t crcStatus = 2; if (data_len > 2) { uint8_t b1, b2; - if(protocol == ICLASS) - { - crcStatus = iclass_CRC_check(isResponse, frame, data_len); - - }else if (protocol == ISO_14443B) - { - crcStatus = iso14443B_CRC_check(isResponse, frame, data_len); - } - else if (protocol == ISO_14443A){//Iso 14443a - - ComputeCrc14443(CRC_14443_A, frame, data_len-2, &b1, &b2); - - if (b1 != frame[data_len-2] || b2 != frame[data_len-1]) { - if(!(isResponse & (data_len < 6))) - { + switch (protocol) { + case ICLASS: + crcStatus = iclass_CRC_check(isResponse, frame, data_len); + break; + case ISO_14443B: + case TOPAZ: + crcStatus = iso14443B_CRC_check(isResponse, topaz_reader_command, data_len); + break; + case ISO_14443A: + ComputeCrc14443(CRC_14443_A, frame, data_len-2, &b1, &b2); + if (b1 != frame[data_len-2] || b2 != frame[data_len-1]) { + if(!(isResponse & (data_len < 6))) { crcStatus = 0; + } } - } + break; + default: + break; } } //0 CRC-command, CRC not ok @@ -361,12 +391,13 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui if(!isResponse) { - if(protocol == ICLASS) - annotateIclass(explanation,sizeof(explanation),frame,data_len); - else if (protocol == ISO_14443A) - annotateIso14443a(explanation,sizeof(explanation),frame,data_len); - else if(protocol == ISO_14443B) - annotateIso14443b(explanation,sizeof(explanation),frame,data_len); + switch(protocol) { + case ICLASS: annotateIclass(explanation,sizeof(explanation),frame,data_len); break; + case ISO_14443A: annotateIso14443a(explanation,sizeof(explanation),frame,data_len); break; + case ISO_14443B: annotateIso14443b(explanation,sizeof(explanation),frame,data_len); break; + case TOPAZ: annotateTopaz(explanation,sizeof(explanation),frame,data_len); break; + default: break; + } } int num_lines = MIN((data_len - 1)/16 + 1, 16); @@ -382,7 +413,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } else { PrintAndLog(" | | | %-64s| %s| %s", line[j], - (j == num_lines-1)?crc:" ", + (j == num_lines-1) ? crc : " ", (j == num_lines-1) ? explanation : ""); } } @@ -425,20 +456,17 @@ int CmdHFList(const char *Cmd) } if(!errors) { - if(strcmp(type, "iclass") == 0) - { + if(strcmp(type, "iclass") == 0) { protocol = ICLASS; - }else if(strcmp(type, "14a") == 0) - { + } else if(strcmp(type, "14a") == 0) { protocol = ISO_14443A; - } - else if(strcmp(type, "14b") == 0) - { + } else if(strcmp(type, "14b") == 0) { protocol = ISO_14443B; - }else if(strcmp(type,"raw")== 0) - { + } else if(strcmp(type,"topaz")== 0) { + protocol = TOPAZ; + } else if(strcmp(type,"raw")== 0) { protocol = -1;//No crc, no annotations - }else{ + } else { errors = true; } } @@ -452,6 +480,7 @@ int CmdHFList(const char *Cmd) PrintAndLog(" 14a - interpret data as iso14443a communications"); PrintAndLog(" 14b - interpret data as iso14443b communications"); PrintAndLog(" iclass - interpret data as iclass communications"); + PrintAndLog(" topaz - interpret data as topaz communications"); PrintAndLog(""); PrintAndLog("example: hf list 14a f"); PrintAndLog("example: hf list iclass"); diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index d36ebb8b..8978f43d 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -140,7 +140,7 @@ int CmdHF14AReader(const char *Cmd) iso14a_card_select_t card; memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); - uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS + uint64_t select_status = resp.arg[0]; // 0: couldn't read, 1: OK, with ATS, 2: OK, no ATS, 3: proprietary Anticollision if(select_status == 0) { PrintAndLog("iso14443a card select failed"); @@ -152,6 +152,18 @@ int CmdHF14AReader(const char *Cmd) return 0; } + if(select_status == 3) { + PrintAndLog("Card doesn't support standard iso14443-3 anticollision"); + PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); + // disconnect + c.arg[0] = 0; + c.arg[1] = 0; + c.arg[2] = 0; + SendCommand(&c); + return 0; + } + + PrintAndLog("ATQA : %02x %02x", card.atqa[1], card.atqa[0]); PrintAndLog(" UID : %s", sprint_hex(card.uid, card.uidlen)); PrintAndLog(" SAK : %02x [%d]", card.sak, resp.arg[0]); diff --git a/common/protocols.h b/common/protocols.h index 01b738c2..e687ca7a 100644 --- a/common/protocols.h +++ b/common/protocols.h @@ -168,9 +168,20 @@ NXP/Philips CUSTOM COMMANDS #define ISO15693_READ_MULTI_SECSTATUS 0x2C -#define ISO_14443A 0 -#define ICLASS 1 -#define ISO_14443B 2 +// Topaz command set: +#define TOPAZ_REQA 0x26 // Request +#define TOPAZ_WUPA 0x52 // WakeUp +#define TOPAZ_RID 0x78 // Read ID +#define TOPAZ_RALL 0x00 // Read All (all bytes) +#define TOPAZ_READ 0x01 // Read (a single byte) +#define TOPAZ_WRITE_E 0x53 // Write-with-erase (a single byte) +#define TOPAZ_WRITE_NE 0x1a // Write-no-erase (a single byte) + + +#define ISO_14443A 0 +#define ICLASS 1 +#define ISO_14443B 2 +#define TOPAZ 3 //-- Picopass fuses #define FUSE_FPERS 0x80 From a8904ebd46d747753c9f639f1577694c149ca5c2 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 15 Mar 2015 16:40:34 +0100 Subject: [PATCH 02/47] Change "hf list topaz" to "hf list nfc" fix: reduce length of expected unmodulated signal in Miller decoder in order to allow decoding of NFC reader communications add: hf list nfc: aggregate reader commands into one line add: hf list nfc: CRC check for NFC communications --- armsrc/iso14443a.c | 2 +- client/cmdhf.c | 72 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index f52e3eb8..a78dae6e 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -269,7 +269,7 @@ static RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) if (Uart.state == STATE_UNSYNCD) { // not yet synced - if (Uart.highCnt < 2) { // wait for a stable unmodulated signal + if (Uart.highCnt < 1) { // wait for a stable unmodulated signal if (Uart.twoBits == 0xffff) { Uart.highCnt++; } else { diff --git a/client/cmdhf.c b/client/cmdhf.c index 03d89c0b..f4d76210 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -273,16 +273,55 @@ uint8_t iclass_CRC_check(bool isResponse, uint8_t* data, uint8_t len) } -uint16_t merge_topaz_reader_frames(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t *topaz_reader_command, uint16_t *data_len) +bool is_last_record(uint16_t tracepos, uint8_t *trace, uint16_t traceLen) { - return tracepos; + return(tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) >= traceLen); +} + + +bool next_record_is_response(uint16_t tracepos, uint8_t *trace) +{ + uint16_t next_records_datalen = *((uint16_t *)(trace + tracepos + sizeof(uint32_t) + sizeof(uint16_t))); + + return(next_records_datalen & 0x8000); +} + + +void merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t *tracepos, uint16_t traceLen, uint8_t *trace, uint8_t *frame, uint8_t *topaz_reader_command, uint16_t *data_len) +{ + +#define MAX_TOPAZ_READER_CMD_LEN 9 + + uint32_t last_timestamp = timestamp + *duration; + + memcpy(topaz_reader_command, frame, MIN(*data_len, MAX_TOPAZ_READER_CMD_LEN)); + + while (!is_last_record(*tracepos, trace, traceLen) && !next_record_is_response(*tracepos, trace)) { + uint32_t next_timestamp = *((uint32_t *)(trace + *tracepos)); + *tracepos += sizeof(uint32_t); + last_timestamp = next_timestamp + *((uint16_t *)(trace + *tracepos)); + *tracepos += sizeof(uint16_t); + uint16_t next_data_len = *((uint16_t *)(trace + *tracepos)) & 0x7FFF; + *tracepos += sizeof(uint16_t); + uint8_t *next_frame = (trace + *tracepos); + *tracepos += next_data_len; + if (*data_len + next_data_len <= MAX_TOPAZ_READER_CMD_LEN) { + memcpy(topaz_reader_command + *data_len, next_frame, next_data_len); + *data_len += next_data_len; + } + uint16_t next_parity_len = (next_data_len-1)/8 + 1; + *tracepos += next_parity_len; + } + + *duration = last_timestamp - timestamp; } uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol, bool showWaitCycles) { bool isResponse; - uint16_t duration, data_len, parity_len; + uint16_t data_len, parity_len; + uint32_t duration; uint8_t topaz_reader_command[9]; uint32_t timestamp, first_timestamp, EndOfTransmissionTimestamp; char explanation[30] = {0}; @@ -317,7 +356,8 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui if (protocol == TOPAZ && !isResponse) { // topaz reader commands come in 1 or 9 separate frames with 8 Bits each. // merge them: - tracepos = merge_topaz_reader_frames(tracepos, traceLen, trace, topaz_reader_command, &data_len); + merge_topaz_reader_frames(timestamp, &duration, &tracepos, traceLen, trace, frame, topaz_reader_command, &data_len); + frame = topaz_reader_command; } //Check the CRC status @@ -331,7 +371,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui break; case ISO_14443B: case TOPAZ: - crcStatus = iso14443B_CRC_check(isResponse, topaz_reader_command, data_len); + crcStatus = iso14443B_CRC_check(isResponse, frame, data_len); break; case ISO_14443A: ComputeCrc14443(CRC_14443_A, frame, data_len-2, &b1, &b2); @@ -370,7 +410,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } } - if(crcStatus == 1) + if(crcStatus == 1 || crcStatus == 2) {//CRC-command char *pos1 = line[(data_len-2)/16]+(((data_len-2) % 16) * 4)-1; (*pos1) = '['; @@ -418,19 +458,15 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } } - if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) > traceLen) return traceLen; + if (is_last_record(tracepos, trace, traceLen)) return traceLen; - bool next_isResponse = *((uint16_t *)(trace + tracepos + 6)) & 0x8000; - - if (showWaitCycles && !isResponse && next_isResponse) { + if (showWaitCycles && !isResponse && next_record_is_response(tracepos, trace)) { uint32_t next_timestamp = *((uint32_t *)(trace + tracepos)); - if (next_timestamp != 0x44444444) { - PrintAndLog(" %9d | %9d | %s | fdt (Frame Delay Time): %d", - (EndOfTransmissionTimestamp - first_timestamp), - (next_timestamp - first_timestamp), - " ", - (next_timestamp - EndOfTransmissionTimestamp)); - } + PrintAndLog(" %9d | %9d | %s | fdt (Frame Delay Time): %d", + (EndOfTransmissionTimestamp - first_timestamp), + (next_timestamp - first_timestamp), + " ", + (next_timestamp - EndOfTransmissionTimestamp)); } return tracepos; @@ -462,7 +498,7 @@ int CmdHFList(const char *Cmd) protocol = ISO_14443A; } else if(strcmp(type, "14b") == 0) { protocol = ISO_14443B; - } else if(strcmp(type,"topaz")== 0) { + } else if(strcmp(type,"nfc")== 0) { protocol = TOPAZ; } else if(strcmp(type,"raw")== 0) { protocol = -1;//No crc, no annotations From ef00343cb1f6ab306020c4108cd414e8df6b132f Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 17 Mar 2015 07:41:08 +0100 Subject: [PATCH 03/47] revert change "hf list topaz" to "hf list nfc" refactored Startbit detection in MillerDecoding() relaxed startbit detection in MillerDecoding() fixed CRC checking and CRC bytes marking in hf list fixed topaz multi frame command listing in hf list topaz --- armsrc/iso14443a.c | 55 ++++++++++++---------------- armsrc/iso14443a.h | 3 +- client/cmdhf.c | 89 +++++++++++++++++++++++++++++++--------------- 3 files changed, 84 insertions(+), 63 deletions(-) diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index a78dae6e..06a134f6 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -248,8 +248,7 @@ void UartReset() Uart.parityLen = 0; // number of decoded parity bytes Uart.shiftReg = 0; // shiftreg to hold decoded data bits Uart.parityBits = 0; // holds 8 parity bits - Uart.twoBits = 0x0000; // buffer for 2 Bits - Uart.highCnt = 0; + Uart.fourBits = 0x00000000; // buffer for 4 Bits Uart.startTime = 0; Uart.endTime = 0; } @@ -265,40 +264,34 @@ void UartInit(uint8_t *data, uint8_t *parity) static RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) { - Uart.twoBits = (Uart.twoBits << 8) | bit; + Uart.fourBits = (Uart.fourBits << 8) | bit; if (Uart.state == STATE_UNSYNCD) { // not yet synced - if (Uart.highCnt < 1) { // wait for a stable unmodulated signal - if (Uart.twoBits == 0xffff) { - Uart.highCnt++; - } else { - Uart.highCnt = 0; - } - } else { - Uart.syncBit = 0xFFFF; // not set - // we look for a ...1111111100x11111xxxxxx pattern (the start bit) - if ((Uart.twoBits & 0xDF00) == 0x1F00) Uart.syncBit = 8; // mask is 11x11111 xxxxxxxx, - // check for 00x11111 xxxxxxxx - else if ((Uart.twoBits & 0xEF80) == 0x8F80) Uart.syncBit = 7; // both masks shifted right one bit, left padded with '1' - else if ((Uart.twoBits & 0xF7C0) == 0xC7C0) Uart.syncBit = 6; // ... - else if ((Uart.twoBits & 0xFBE0) == 0xE3E0) Uart.syncBit = 5; - else if ((Uart.twoBits & 0xFDF0) == 0xF1F0) Uart.syncBit = 4; - else if ((Uart.twoBits & 0xFEF8) == 0xF8F8) Uart.syncBit = 3; - else if ((Uart.twoBits & 0xFF7C) == 0xFC7C) Uart.syncBit = 2; - else if ((Uart.twoBits & 0xFFBE) == 0xFE3E) Uart.syncBit = 1; - if (Uart.syncBit != 0xFFFF) { // found a sync bit - Uart.startTime = non_real_time?non_real_time:(GetCountSspClk() & 0xfffffff8); - Uart.startTime -= Uart.syncBit; - Uart.endTime = Uart.startTime; - Uart.state = STATE_START_OF_COMMUNICATION; - } + Uart.syncBit = 9999; // not set + // we look for a ...xxxx1111111100x11111xxxxxx pattern + // (unmodulated, followed by the start bit = 8 '1's followed by 2 '0's, eventually followed by another '0', followed by 5 '1's) +#define ISO14443A_STARTBIT_MASK 0x007FEF80 // mask is 00000000 01111111 11101111 10000000 +#define ISO14443A_STARTBIT_PATTERN 0x007F8F80 // pattern is 00000000 01111111 10001111 10000000 + if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 0 == ISO14443A_STARTBIT_PATTERN >> 0) Uart.syncBit = 7; + else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 1 == ISO14443A_STARTBIT_PATTERN >> 1) Uart.syncBit = 6; + else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 2 == ISO14443A_STARTBIT_PATTERN >> 2) Uart.syncBit = 5; + else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 3 == ISO14443A_STARTBIT_PATTERN >> 3) Uart.syncBit = 4; + else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 4 == ISO14443A_STARTBIT_PATTERN >> 4) Uart.syncBit = 3; + else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 5 == ISO14443A_STARTBIT_PATTERN >> 5) Uart.syncBit = 2; + else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 6 == ISO14443A_STARTBIT_PATTERN >> 6) Uart.syncBit = 1; + else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 7 == ISO14443A_STARTBIT_PATTERN >> 7) Uart.syncBit = 0; + if (Uart.syncBit != 9999) { // found a sync bit + Uart.startTime = non_real_time?non_real_time:(GetCountSspClk() & 0xfffffff8); + Uart.startTime -= Uart.syncBit; + Uart.endTime = Uart.startTime; + Uart.state = STATE_START_OF_COMMUNICATION; } } else { - if (IsMillerModulationNibble1(Uart.twoBits >> Uart.syncBit)) { - if (IsMillerModulationNibble2(Uart.twoBits >> Uart.syncBit)) { // Modulation in both halves - error + if (IsMillerModulationNibble1(Uart.fourBits >> Uart.syncBit)) { + if (IsMillerModulationNibble2(Uart.fourBits >> Uart.syncBit)) { // Modulation in both halves - error UartReset(); } else { // Modulation in first half = Sequence Z = logic "0" if (Uart.state == STATE_MILLER_X) { // error - must not follow after X @@ -322,7 +315,7 @@ static RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) } } } else { - if (IsMillerModulationNibble2(Uart.twoBits >> Uart.syncBit)) { // Modulation second half = Sequence X = logic "1" + if (IsMillerModulationNibble2(Uart.fourBits >> Uart.syncBit)) { // Modulation second half = Sequence X = logic "1" Uart.bitCount++; Uart.shiftReg = (Uart.shiftReg >> 1) | 0x100; // add a 1 to the shiftreg Uart.state = STATE_MILLER_X; @@ -358,12 +351,10 @@ static RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) return TRUE; // we are finished with decoding the raw data sequence } else { UartReset(); // Nothing received - start over - Uart.highCnt = 1; } } if (Uart.state == STATE_START_OF_COMMUNICATION) { // error - must not follow directly after SOC UartReset(); - Uart.highCnt = 1; } else { // a logic "0" Uart.bitCount++; Uart.shiftReg = (Uart.shiftReg >> 1); // add a 0 to the shiftreg diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index d99236b2..ec99ab99 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -63,8 +63,7 @@ typedef struct { uint16_t syncBit; uint8_t parityBits; uint8_t parityLen; - uint16_t highCnt; - uint16_t twoBits; + uint32_t fourBits; uint32_t startTime, endTime; uint8_t *output; uint8_t *parity; diff --git a/client/cmdhf.c b/client/cmdhf.c index f4d76210..960dcf7f 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -189,7 +189,34 @@ void annotateIso14443b(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) } /** - * @brief iso14443B_CRC_Ok Checks CRC in command or response + * @brief iso14443A_CRC_check Checks CRC in command or response + * @param isResponse + * @param data + * @param len + * @return 0 : CRC-command, CRC not ok + * 1 : CRC-command, CRC ok + * 2 : Not crc-command + */ + +uint8_t iso14443A_CRC_check(bool isResponse, uint8_t* data, uint8_t len) +{ + uint8_t b1,b2; + + if(len <= 2) return 2; + + if(isResponse & (len < 6)) return 2; + + ComputeCrc14443(CRC_14443_A, data, len-2, &b1, &b2); + if (b1 != data[len-2] || b2 != data[len-1]) { + return 0; + } else { + return 1; + } +} + + +/** + * @brief iso14443B_CRC_check Checks CRC in command or response * @param isResponse * @param data * @param len @@ -206,9 +233,10 @@ uint8_t iso14443B_CRC_check(bool isResponse, uint8_t* data, uint8_t len) ComputeCrc14443(CRC_14443_B, data, len-2, &b1, &b2); if(b1 != data[len-2] || b2 != data[len-1]) { - return 0; + return 0; + } else { + return 1; } - return 1; } /** @@ -287,33 +315,42 @@ bool next_record_is_response(uint16_t tracepos, uint8_t *trace) } -void merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t *tracepos, uint16_t traceLen, uint8_t *trace, uint8_t *frame, uint8_t *topaz_reader_command, uint16_t *data_len) +bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t *tracepos, uint16_t traceLen, uint8_t *trace, uint8_t *frame, uint8_t *topaz_reader_command, uint16_t *data_len) { #define MAX_TOPAZ_READER_CMD_LEN 9 uint32_t last_timestamp = timestamp + *duration; - memcpy(topaz_reader_command, frame, MIN(*data_len, MAX_TOPAZ_READER_CMD_LEN)); + if ((*data_len != 1) || (frame[0] == TOPAZ_WUPA) || (frame[0] == TOPAZ_REQA)) return false; + + memcpy(topaz_reader_command, frame, *data_len); while (!is_last_record(*tracepos, trace, traceLen) && !next_record_is_response(*tracepos, trace)) { uint32_t next_timestamp = *((uint32_t *)(trace + *tracepos)); *tracepos += sizeof(uint32_t); - last_timestamp = next_timestamp + *((uint16_t *)(trace + *tracepos)); + uint16_t next_duration = *((uint16_t *)(trace + *tracepos)); *tracepos += sizeof(uint16_t); uint16_t next_data_len = *((uint16_t *)(trace + *tracepos)) & 0x7FFF; *tracepos += sizeof(uint16_t); uint8_t *next_frame = (trace + *tracepos); *tracepos += next_data_len; - if (*data_len + next_data_len <= MAX_TOPAZ_READER_CMD_LEN) { + if ((next_data_len == 1) && (*data_len + next_data_len <= MAX_TOPAZ_READER_CMD_LEN)) { memcpy(topaz_reader_command + *data_len, next_frame, next_data_len); *data_len += next_data_len; - } + last_timestamp = next_timestamp + next_duration; + } else { + // rewind and exit + *tracepos = *tracepos - next_data_len - sizeof(uint16_t) - sizeof(uint16_t) - sizeof(uint32_t); + break; + } uint16_t next_parity_len = (next_data_len-1)/8 + 1; *tracepos += next_parity_len; } *duration = last_timestamp - timestamp; + + return true; } @@ -354,32 +391,27 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui tracepos += parity_len; if (protocol == TOPAZ && !isResponse) { - // topaz reader commands come in 1 or 9 separate frames with 8 Bits each. + // topaz reader commands come in 1 or 9 separate frames with 7 or 8 Bits each. // merge them: - merge_topaz_reader_frames(timestamp, &duration, &tracepos, traceLen, trace, frame, topaz_reader_command, &data_len); - frame = topaz_reader_command; + if (merge_topaz_reader_frames(timestamp, &duration, &tracepos, traceLen, trace, frame, topaz_reader_command, &data_len)) { + frame = topaz_reader_command; + } } //Check the CRC status uint8_t crcStatus = 2; if (data_len > 2) { - uint8_t b1, b2; switch (protocol) { case ICLASS: crcStatus = iclass_CRC_check(isResponse, frame, data_len); break; case ISO_14443B: - case TOPAZ: + case TOPAZ: crcStatus = iso14443B_CRC_check(isResponse, frame, data_len); break; case ISO_14443A: - ComputeCrc14443(CRC_14443_A, frame, data_len-2, &b1, &b2); - if (b1 != frame[data_len-2] || b2 != frame[data_len-1]) { - if(!(isResponse & (data_len < 6))) { - crcStatus = 0; - } - } + crcStatus = iso14443A_CRC_check(isResponse, frame, data_len); break; default: break; @@ -403,19 +435,18 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } uint8_t parityBits = parityBytes[j>>3]; if (isResponse && (oddparity != ((parityBits >> (7-(j&0x0007))) & 0x01))) { - snprintf(line[j/16]+(( j % 16) * 4),110, "%02x! ", frame[j]); - + snprintf(line[j/16]+(( j % 16) * 4), 110, " %02x!", frame[j]); } else { - snprintf(line[j/16]+(( j % 16) * 4),110, "%02x ", frame[j]); + snprintf(line[j/16]+(( j % 16) * 4), 110, " %02x ", frame[j]); } } - if(crcStatus == 1 || crcStatus == 2) + if(crcStatus == 0 || crcStatus == 1) {//CRC-command - char *pos1 = line[(data_len-2)/16]+(((data_len-2) % 16) * 4)-1; + char *pos1 = line[(data_len-2)/16]+(((data_len-2) % 16) * 4); (*pos1) = '['; - char *pos2 = line[(data_len)/16]+(((data_len) % 16) * 4)-2; - (*pos2) = ']'; + char *pos2 = line[(data_len)/16]+(((data_len) % 16) * 4); + sprintf(pos2, "%c", ']'); } if(data_len == 0) { @@ -443,7 +474,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui int num_lines = MIN((data_len - 1)/16 + 1, 16); for (int j = 0; j < num_lines ; j++) { if (j == 0) { - PrintAndLog(" %9d | %9d | %s | %-64s| %s| %s", + PrintAndLog(" %9d | %9d | %s |%-64s | %s| %s", (timestamp - first_timestamp), (EndOfTransmissionTimestamp - first_timestamp), (isResponse ? "Tag" : "Rdr"), @@ -451,7 +482,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui (j == num_lines-1) ? crc : " ", (j == num_lines-1) ? explanation : ""); } else { - PrintAndLog(" | | | %-64s| %s| %s", + PrintAndLog(" | | |%-64s | %s| %s", line[j], (j == num_lines-1) ? crc : " ", (j == num_lines-1) ? explanation : ""); @@ -498,7 +529,7 @@ int CmdHFList(const char *Cmd) protocol = ISO_14443A; } else if(strcmp(type, "14b") == 0) { protocol = ISO_14443B; - } else if(strcmp(type,"nfc")== 0) { + } else if(strcmp(type,"topaz")== 0) { protocol = TOPAZ; } else if(strcmp(type,"raw")== 0) { protocol = -1;//No crc, no annotations From 05ddb52c43f932db852e18fe6836bee71e91f74e Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 18 Mar 2015 17:12:09 +0100 Subject: [PATCH 04/47] fix: introduced a stupid error when refactoring the start bit detector in MillerDecoding() chg: use -O2 instead of -Os when compiling ARM sources chg: don't clear the Miller decoders input buffer on reset chg: be more specific for the Miller decoders start bit pattern add: new option c in hf list: mark CRC bytes (default is off) --- armsrc/Makefile | 2 +- armsrc/iso14443a.c | 49 ++++++++++++++++------------ client/Makefile | 71 +++++++++++++++++++++-------------------- client/cmdhf.c | 72 +++++++++++++++++++++++++----------------- client/cmdhftopaz.c | 71 +++++++++++++++++++++++++++++++++++++++++ client/cmdhftopaz.h | 16 ++++++++++ common/Makefile.common | 2 +- 7 files changed, 197 insertions(+), 86 deletions(-) create mode 100644 client/cmdhftopaz.c create mode 100644 client/cmdhftopaz.h diff --git a/armsrc/Makefile b/armsrc/Makefile index 75ccdece..03541d61 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -10,7 +10,7 @@ APP_INCLUDES = apps.h #remove one of the following defines and comment out the relevant line #in the next section to remove that particular feature from compilation -APP_CFLAGS = -DWITH_LF -DWITH_ISO15693 -DWITH_ISO14443a -DWITH_ISO14443b -DWITH_ICLASS -DWITH_LEGICRF -DWITH_HITAG -DWITH_CRC -DON_DEVICE -fno-strict-aliasing +APP_CFLAGS = -DWITH_LF -DWITH_ISO15693 -DWITH_ISO14443a -DWITH_ISO14443b -DWITH_ICLASS -DWITH_LEGICRF -DWITH_HITAG -DWITH_CRC -DON_DEVICE -fno-strict-aliasing -O2 #-DWITH_LCD #SRC_LCD = fonts.c LCD.c diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 06a134f6..0bd681d9 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -232,13 +232,19 @@ void AppendCrc14443a(uint8_t* data, int len) static tUart Uart; // Lookup-Table to decide if 4 raw bits are a modulation. -// We accept two or three consecutive "0" in any position with the rest "1" +// We accept the following: +// 0001 - a 3 tick wide pause +// 0011 - a 2 tick wide pause, or a three tick wide pause shifted left +// 0111 - a 2 tick wide pause shifted left +// 1001 - a 2 tick wide pause shifted right const bool Mod_Miller_LUT[] = { - TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, - TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE +// TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, +// TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE + FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, + FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE }; -#define IsMillerModulationNibble1(b) (Mod_Miller_LUT[(b & 0x00F0) >> 4]) -#define IsMillerModulationNibble2(b) (Mod_Miller_LUT[(b & 0x000F)]) +#define IsMillerModulationNibble1(b) (Mod_Miller_LUT[(b & 0x000000F0) >> 4]) +#define IsMillerModulationNibble2(b) (Mod_Miller_LUT[(b & 0x0000000F)]) void UartReset() { @@ -248,7 +254,6 @@ void UartReset() Uart.parityLen = 0; // number of decoded parity bytes Uart.shiftReg = 0; // shiftreg to hold decoded data bits Uart.parityBits = 0; // holds 8 parity bits - Uart.fourBits = 0x00000000; // buffer for 4 Bits Uart.startTime = 0; Uart.endTime = 0; } @@ -257,6 +262,7 @@ void UartInit(uint8_t *data, uint8_t *parity) { Uart.output = data; Uart.parity = parity; + Uart.fourBits = 0x00000000; // clear the buffer for 4 Bits UartReset(); } @@ -269,18 +275,21 @@ static RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) if (Uart.state == STATE_UNSYNCD) { // not yet synced Uart.syncBit = 9999; // not set - // we look for a ...xxxx1111111100x11111xxxxxx pattern - // (unmodulated, followed by the start bit = 8 '1's followed by 2 '0's, eventually followed by another '0', followed by 5 '1's) -#define ISO14443A_STARTBIT_MASK 0x007FEF80 // mask is 00000000 01111111 11101111 10000000 -#define ISO14443A_STARTBIT_PATTERN 0x007F8F80 // pattern is 00000000 01111111 10001111 10000000 - if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 0 == ISO14443A_STARTBIT_PATTERN >> 0) Uart.syncBit = 7; - else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 1 == ISO14443A_STARTBIT_PATTERN >> 1) Uart.syncBit = 6; - else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 2 == ISO14443A_STARTBIT_PATTERN >> 2) Uart.syncBit = 5; - else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 3 == ISO14443A_STARTBIT_PATTERN >> 3) Uart.syncBit = 4; - else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 4 == ISO14443A_STARTBIT_PATTERN >> 4) Uart.syncBit = 3; - else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 5 == ISO14443A_STARTBIT_PATTERN >> 5) Uart.syncBit = 2; - else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 6 == ISO14443A_STARTBIT_PATTERN >> 6) Uart.syncBit = 1; - else if ((Uart.fourBits & ISO14443A_STARTBIT_MASK) >> 7 == ISO14443A_STARTBIT_PATTERN >> 7) Uart.syncBit = 0; + // The start bit is one ore more Sequence Y followed by a Sequence Z (... 11111111 00x11111). We need to distinguish from + // Sequence X followed by Sequence Y followed by Sequence Z (111100x1 11111111 00x11111) + // we therefore look for a ...xx11111111111100x11111xxxxxx... pattern + // (12 '1's followed by 2 '0's, eventually followed by another '0', followed by 5 '1's) +#define ISO14443A_STARTBIT_MASK 0x07FFEF80 // mask is 00000111 11111111 11101111 10000000 +#define ISO14443A_STARTBIT_PATTERN 0x07FF8F80 // pattern is 00000111 11111111 10001111 10000000 + if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 0)) == ISO14443A_STARTBIT_PATTERN >> 0) Uart.syncBit = 7; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 1)) == ISO14443A_STARTBIT_PATTERN >> 1) Uart.syncBit = 6; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 2)) == ISO14443A_STARTBIT_PATTERN >> 2) Uart.syncBit = 5; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 3)) == ISO14443A_STARTBIT_PATTERN >> 3) Uart.syncBit = 4; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 4)) == ISO14443A_STARTBIT_PATTERN >> 4) Uart.syncBit = 3; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 5)) == ISO14443A_STARTBIT_PATTERN >> 5) Uart.syncBit = 2; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 6)) == ISO14443A_STARTBIT_PATTERN >> 6) Uart.syncBit = 1; + else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 7)) == ISO14443A_STARTBIT_PATTERN >> 7) Uart.syncBit = 0; + if (Uart.syncBit != 9999) { // found a sync bit Uart.startTime = non_real_time?non_real_time:(GetCountSspClk() & 0xfffffff8); Uart.startTime -= Uart.syncBit; @@ -646,7 +655,7 @@ void RAMFUNC SnoopIso14443a(uint8_t param) { TRUE)) break; } /* And ready to receive another command. */ - UartReset(); + UartInit(receivedCmd, receivedCmdPar); /* And also reset the demod code, which might have been */ /* false-triggered by the commands from the reader. */ DemodReset(); @@ -2798,7 +2807,7 @@ void RAMFUNC SniffMifare(uint8_t param) { if (MfSniffLogic(receivedCmd, Uart.len, Uart.parity, Uart.bitCount, TRUE)) break; /* And ready to receive another command. */ - UartReset(); + UartInit(receivedCmd, receivedCmdPar); /* And also reset the demod code */ DemodReset(); diff --git a/client/Makefile b/client/Makefile index 6ec34469..2e1c2092 100644 --- a/client/Makefile +++ b/client/Makefile @@ -65,41 +65,42 @@ CMDSRCS = nonce2key/crapto1.c\ loclass/ikeys.c \ loclass/elite_crack.c\ loclass/fileutils.c\ - mifarehost.c\ - crc16.c \ - iso14443crc.c \ - iso15693tools.c \ - data.c \ - graph.c \ - ui.c \ - cmddata.c \ - lfdemod.c \ - cmdhf.c \ - cmdhf14a.c \ - cmdhf14b.c \ - cmdhf15.c \ - cmdhfepa.c \ - cmdhflegic.c \ - cmdhficlass.c \ - cmdhfmf.c \ - cmdhfmfu.c \ - cmdhw.c \ - cmdlf.c \ - cmdlfio.c \ - cmdlfhid.c \ - cmdlfem4x.c \ - cmdlfhitag.c \ - cmdlfti.c \ - cmdparser.c \ - cmdmain.c \ - cmdlft55xx.c \ - cmdlfpcf7931.c\ - pm3_binlib.c\ - scripting.c\ - cmdscript.c\ - pm3_bitlib.c\ - aes.c\ - protocols.c\ + mifarehost.c\ + crc16.c \ + iso14443crc.c \ + iso15693tools.c \ + data.c \ + graph.c \ + ui.c \ + cmddata.c \ + lfdemod.c \ + cmdhf.c \ + cmdhf14a.c \ + cmdhf14b.c \ + cmdhf15.c \ + cmdhfepa.c \ + cmdhflegic.c \ + cmdhficlass.c \ + cmdhfmf.c \ + cmdhfmfu.c \ + cmdhftopaz.c \ + cmdhw.c \ + cmdlf.c \ + cmdlfio.c \ + cmdlfhid.c \ + cmdlfem4x.c \ + cmdlfhitag.c \ + cmdlfti.c \ + cmdparser.c \ + cmdmain.c \ + cmdlft55xx.c \ + cmdlfpcf7931.c\ + pm3_binlib.c\ + scripting.c\ + cmdscript.c\ + pm3_bitlib.c\ + aes.c\ + protocols.c\ COREOBJS = $(CORESRCS:%.c=$(OBJDIR)/%.o) diff --git a/client/cmdhf.c b/client/cmdhf.c index 960dcf7f..0d678ab6 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -23,6 +23,7 @@ #include "cmdhficlass.h" #include "cmdhfmf.h" #include "cmdhfmfu.h" +#include "cmdhftopaz.h" #include "protocols.h" static int CmdHelp(const char *Cmd); @@ -354,7 +355,7 @@ bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t } -uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol, bool showWaitCycles) +uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, uint8_t protocol, bool showWaitCycles, bool markCRCBytes) { bool isResponse; uint16_t data_len, parity_len; @@ -441,13 +442,17 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui } } - if(crcStatus == 0 || crcStatus == 1) - {//CRC-command - char *pos1 = line[(data_len-2)/16]+(((data_len-2) % 16) * 4); - (*pos1) = '['; - char *pos2 = line[(data_len)/16]+(((data_len) % 16) * 4); - sprintf(pos2, "%c", ']'); + + if (markCRCBytes) { + if(crcStatus == 0 || crcStatus == 1) + {//CRC-command + char *pos1 = line[(data_len-2)/16]+(((data_len-2) % 16) * 4); + (*pos1) = '['; + char *pos2 = line[(data_len)/16]+(((data_len) % 16) * 4); + sprintf(pos2, "%c", ']'); + } } + if(data_len == 0) { if(data_len == 0){ @@ -507,22 +512,26 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui int CmdHFList(const char *Cmd) { bool showWaitCycles = false; + bool markCRCBytes = false; char type[40] = {0}; int tlen = param_getstr(Cmd,0,type); - char param = param_getchar(Cmd, 1); + char param1 = param_getchar(Cmd, 1); + char param2 = param_getchar(Cmd, 2); bool errors = false; uint8_t protocol = 0; //Validate params - if(tlen == 0) - { + + if(tlen == 0) { errors = true; } - if(param == 'h' || (param !=0 && param != 'f')) - { + + if(param1 == 'h' + || (param1 != 0 && param1 != 'f' && param1 != 'c') + || (param2 != 0 && param2 != 'f' && param2 != 'c')) { errors = true; } - if(!errors) - { + + if(!errors) { if(strcmp(type, "iclass") == 0) { protocol = ICLASS; } else if(strcmp(type, "14a") == 0) { @@ -540,8 +549,9 @@ int CmdHFList(const char *Cmd) if (errors) { PrintAndLog("List protocol data in trace buffer."); - PrintAndLog("Usage: hf list [f]"); + PrintAndLog("Usage: hf list [f][c]"); PrintAndLog(" f - show frame delay times as well"); + PrintAndLog(" c - mark CRC bytes"); PrintAndLog("Supported values:"); PrintAndLog(" raw - just show raw data without annotations"); PrintAndLog(" 14a - interpret data as iso14443a communications"); @@ -555,10 +565,13 @@ int CmdHFList(const char *Cmd) } - if (param == 'f') { + if (param1 == 'f' || param2 == 'f') { showWaitCycles = true; } + if (param1 == 'c' || param2 == 'c') { + markCRCBytes = true; + } uint8_t *trace; uint16_t tracepos = 0; @@ -592,7 +605,7 @@ int CmdHFList(const char *Cmd) while(tracepos < traceLen) { - tracepos = printTraceLine(tracepos, traceLen, trace, protocol, showWaitCycles); + tracepos = printTraceLine(tracepos, traceLen, trace, protocol, showWaitCycles, markCRCBytes); } free(trace); @@ -602,18 +615,19 @@ int CmdHFList(const char *Cmd) static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"14a", CmdHF14A, 1, "{ ISO14443A RFIDs... }"}, - {"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"}, - {"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"}, - {"epa", CmdHFEPA, 1, "{ German Identification Card... }"}, - {"legic", CmdHFLegic, 0, "{ LEGIC RFIDs... }"}, - {"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"}, - {"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"}, - {"mfu", CmdHFMFUltra, 1, "{ MIFARE Ultralight RFIDs... }"}, - {"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"}, - {"list", CmdHFList, 1, "List protocol data in trace buffer"}, - {NULL, NULL, 0, NULL} + {"help", CmdHelp, 1, "This help"}, + {"14a", CmdHF14A, 1, "{ ISO14443A RFIDs... }"}, + {"14b", CmdHF14B, 1, "{ ISO14443B RFIDs... }"}, + {"15", CmdHF15, 1, "{ ISO15693 RFIDs... }"}, + {"epa", CmdHFEPA, 1, "{ German Identification Card... }"}, + {"legic", CmdHFLegic, 0, "{ LEGIC RFIDs... }"}, + {"iclass", CmdHFiClass, 1, "{ ICLASS RFIDs... }"}, + {"mf", CmdHFMF, 1, "{ MIFARE RFIDs... }"}, + {"mfu", CmdHFMFUltra, 1, "{ MIFARE Ultralight RFIDs... }"}, + {"topaz", CmdHFTopaz, 1, "{ TOPAZ (NFC Type 1) RFIDs... }"}, + {"tune", CmdHFTune, 0, "Continuously measure HF antenna tuning"}, + {"list", CmdHFList, 1, "List protocol data in trace buffer"}, + {NULL, NULL, 0, NULL} }; int CmdHF(const char *Cmd) diff --git a/client/cmdhftopaz.c b/client/cmdhftopaz.c new file mode 100644 index 00000000..d747ed05 --- /dev/null +++ b/client/cmdhftopaz.c @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2015 Piwi +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency Topaz (NFC Type 1) commands +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include "cmdmain.h" +#include "cmdparser.h" +#include "cmdhftopaz.h" +#include "cmdhf14a.h" +#include "ui.h" + +int CmdHFTopazReader(const char *Cmd) +{ + PrintAndLog("not yet implemented"); + return 0; +} + + +int CmdHFTopazSim(const char *Cmd) +{ + PrintAndLog("not yet implemented"); + return 0; +} + + +int CmdHFTopazCmdRaw(const char *Cmd) +{ + PrintAndLog("not yet implemented"); + return 0; +} + + +static int CmdHelp(const char *Cmd); + + +static command_t CommandTable[] = +{ + {"help", CmdHelp, 1, "This help"}, + {"reader", CmdHFTopazReader, 0, "Act like a Topaz reader"}, + {"sim", CmdHFTopazSim, 0, " -- Simulate Topaz tag"}, + {"snoop", CmdHF14ASnoop, 0, "Eavesdrop a Topaz reader-tag communication"}, + {"raw", CmdHFTopazCmdRaw, 0, "Send raw hex data to tag"}, + {NULL, NULL, 0, NULL} +}; + + +int CmdHFTopaz(const char *Cmd) { + // flush + WaitForResponseTimeout(CMD_ACK,NULL,100); + + // parse + CmdsParse(CommandTable, Cmd); + return 0; +} + +static int CmdHelp(const char *Cmd) +{ + CmdsHelp(CommandTable); + return 0; +} + + diff --git a/client/cmdhftopaz.h b/client/cmdhftopaz.h new file mode 100644 index 00000000..8d5428dd --- /dev/null +++ b/client/cmdhftopaz.h @@ -0,0 +1,16 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2015 Piwi +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// High frequency Topaz (NFC Type 1) commands +//----------------------------------------------------------------------------- + +#ifndef CMDHFTOPAZ_H__ +#define CMDHFTOPAZ_H__ + +int CmdHFTopaz(const char *Cmd); + +#endif diff --git a/common/Makefile.common b/common/Makefile.common index 2b2bb2fb..7e264d28 100644 --- a/common/Makefile.common +++ b/common/Makefile.common @@ -66,7 +66,7 @@ VPATH = . ../common/ ../fpga/ INCLUDES = ../include/proxmark3.h ../include/at91sam7s512.h ../include/config_gpio.h ../include/usb_cmd.h $(APP_INCLUDES) -CFLAGS = -c $(INCLUDE) -Wall -Werror -pedantic -std=c99 $(APP_CFLAGS) -Os +CFLAGS = -c $(INCLUDE) -Wall -Werror -pedantic -std=c99 -Os $(APP_CFLAGS) LDFLAGS = -nostartfiles -nodefaultlibs -Wl,-gc-sections -n LIBS = -lgcc From 48ece4a750b41536ba2c56dfd9a088b192976c82 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 20 Mar 2015 21:06:51 +0100 Subject: [PATCH 05/47] add: Topaz mode for "hf 14a raw" (new option -T) chg: allow tracing without parity chg: make "hf list topaz" aware of additional commands for Dynamic Memory Model --- armsrc/BigBuf.c | 17 +++++++----- armsrc/iso14443a.c | 60 ++++++++++++++++++++++++++++++++--------- client/cmdhf.c | 11 +++++--- client/cmdhf14a.c | 58 ++++++++++++++++++++++++++-------------- client/cmdhftopaz.c | 65 +++++++++++++++++++++++++++++++++++++++++++++ common/protocols.h | 5 ++++ include/mifare.h | 17 ++++++------ 7 files changed, 182 insertions(+), 51 deletions(-) diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 703ade65..51fafdeb 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -171,18 +171,19 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ traceLen += iLen; // parity bytes - if (parity != NULL && iLen != 0) { - memcpy(trace + traceLen, parity, num_paritybytes); + if (iLen != 0) { + if (parity != NULL) { + memcpy(trace + traceLen, parity, num_paritybytes); + } else { + memset(trace + traceLen, 0x00, num_paritybytes); + } } traceLen += num_paritybytes; - if(traceLen +4 < max_traceLen) - { //If it hadn't been cleared, for whatever reason.. - memset(trace+traceLen,0x44, 4); - } - return TRUE; } + + int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwParity, int readerToTag) { /** @@ -224,6 +225,8 @@ int LogTraceHitag(const uint8_t * btBytes, int iBits, int iSamples, uint32_t dwP return TRUE; } + + // Emulator memory uint8_t emlSet(uint8_t *data, uint32_t offset, uint32_t length){ uint8_t* mem = BigBuf_get_EM_addr(); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 0bd681d9..81cb9728 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -213,6 +213,12 @@ void AppendCrc14443a(uint8_t* data, int len) ComputeCrc14443(CRC_14443_A,data,len,data+len,data+len+1); } +void AppendCrc14443b(uint8_t* data, int len) +{ + ComputeCrc14443(CRC_14443_B,data,len,data+len,data+len+1); +} + + //============================================================================= // ISO 14443 Type A - Miller decoder //============================================================================= @@ -238,8 +244,6 @@ static tUart Uart; // 0111 - a 2 tick wide pause shifted left // 1001 - a 2 tick wide pause shifted right const bool Mod_Miller_LUT[] = { -// TRUE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, -// TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE }; @@ -279,8 +283,8 @@ static RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time) // Sequence X followed by Sequence Y followed by Sequence Z (111100x1 11111111 00x11111) // we therefore look for a ...xx11111111111100x11111xxxxxx... pattern // (12 '1's followed by 2 '0's, eventually followed by another '0', followed by 5 '1's) -#define ISO14443A_STARTBIT_MASK 0x07FFEF80 // mask is 00000111 11111111 11101111 10000000 -#define ISO14443A_STARTBIT_PATTERN 0x07FF8F80 // pattern is 00000111 11111111 10001111 10000000 + #define ISO14443A_STARTBIT_MASK 0x07FFEF80 // mask is 00000111 11111111 11101111 10000000 + #define ISO14443A_STARTBIT_PATTERN 0x07FF8F80 // pattern is 00000111 11111111 10001111 10000000 if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 0)) == ISO14443A_STARTBIT_PATTERN >> 0) Uart.syncBit = 7; else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 1)) == ISO14443A_STARTBIT_PATTERN >> 1) Uart.syncBit = 6; else if ((Uart.fourBits & (ISO14443A_STARTBIT_MASK >> 2)) == ISO14443A_STARTBIT_PATTERN >> 2) Uart.syncBit = 5; @@ -655,7 +659,7 @@ void RAMFUNC SnoopIso14443a(uint8_t param) { TRUE)) break; } /* And ready to receive another command. */ - UartInit(receivedCmd, receivedCmdPar); + UartReset(); /* And also reset the demod code, which might have been */ /* false-triggered by the commands from the reader. */ DemodReset(); @@ -680,6 +684,9 @@ void RAMFUNC SnoopIso14443a(uint8_t param) { // And ready to receive another response. DemodReset(); + // And reset the Miller decoder including itS (now outdated) input buffer + UartInit(receivedCmd, receivedCmdPar); + LED_C_OFF(); } TagIsActive = (Demod.state != DEMOD_UNSYNCD); @@ -1337,7 +1344,7 @@ void CodeIso14443aBitsAsReaderPar(const uint8_t *cmd, uint16_t bits, const uint8 } // Only transmit parity bit if we transmitted a complete byte - if (j == 8) { + if (j == 8 && parity != NULL) { // Get the parity bit if (parity[i>>3] & (0x80 >> (i&0x0007))) { // Sequence X @@ -1631,6 +1638,7 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, uint8_t *receive } } + void ReaderTransmitBitsPar(uint8_t* frame, uint16_t bits, uint8_t *par, uint32_t *timing) { CodeIso14443aBitsAsReaderPar(frame, bits, par); @@ -1646,11 +1654,13 @@ void ReaderTransmitBitsPar(uint8_t* frame, uint16_t bits, uint8_t *par, uint32_t } } + void ReaderTransmitPar(uint8_t* frame, uint16_t len, uint8_t *par, uint32_t *timing) { ReaderTransmitBitsPar(frame, len*8, par, timing); } + void ReaderTransmitBits(uint8_t* frame, uint16_t len, uint32_t *timing) { // Generate parity and redirect @@ -1659,6 +1669,7 @@ void ReaderTransmitBits(uint8_t* frame, uint16_t len, uint32_t *timing) ReaderTransmitBitsPar(frame, len, par, timing); } + void ReaderTransmit(uint8_t* frame, uint16_t len, uint32_t *timing) { // Generate parity and redirect @@ -1932,15 +1943,38 @@ void ReaderIso14443a(UsbCommand *c) if(param & ISO14A_RAW) { if(param & ISO14A_APPEND_CRC) { - AppendCrc14443a(cmd,len); + if(param & ISO14A_TOPAZMODE) { + AppendCrc14443b(cmd,len); + } else { + AppendCrc14443a(cmd,len); + } len += 2; if (lenbits) lenbits += 16; } - if(lenbits>0) { - GetParity(cmd, lenbits/8, par); - ReaderTransmitBitsPar(cmd, lenbits, par, NULL); - } else { - ReaderTransmit(cmd,len, NULL); + if(lenbits>0) { // want to send a specific number of bits (e.g. short commands) + if(param & ISO14A_TOPAZMODE) { + int bits_to_send = lenbits; + uint16_t i = 0; + ReaderTransmitBitsPar(&cmd[i++], MIN(bits_to_send, 7), NULL, NULL); // first byte is always short (7bits) and no parity + bits_to_send -= 7; + while (bits_to_send > 0) { + ReaderTransmitBitsPar(&cmd[i++], MIN(bits_to_send, 8), NULL, NULL); // following bytes are 8 bit and no parity + bits_to_send -= 8; + } + } else { + GetParity(cmd, lenbits/8, par); + ReaderTransmitBitsPar(cmd, lenbits, par, NULL); // bytes are 8 bit with odd parity + } + } else { // want to send complete bytes only + if(param & ISO14A_TOPAZMODE) { + uint16_t i = 0; + ReaderTransmitBitsPar(&cmd[i++], 7, NULL, NULL); // first byte: 7 bits, no paritiy + while (i < len) { + ReaderTransmitBitsPar(&cmd[i++], 8, NULL, NULL); // following bytes: 8 bits, no paritiy + } + } else { + ReaderTransmit(cmd,len, NULL); // 8 bits, odd parity + } } arg0 = ReaderReceive(buf, par); cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); @@ -2824,6 +2858,8 @@ void RAMFUNC SniffMifare(uint8_t param) { // And ready to receive another response. DemodReset(); + // And reset the Miller decoder including its (now outdated) input buffer + UartInit(receivedCmd, receivedCmdPar); } TagIsActive = (Demod.state != DEMOD_UNSYNCD); } diff --git a/client/cmdhf.c b/client/cmdhf.c index 0d678ab6..66c8e53c 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -145,7 +145,6 @@ void annotateIso15693(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) void annotateTopaz(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) { - switch(cmd[0]) { case TOPAZ_REQA :snprintf(exp, size, "REQA");break; case TOPAZ_WUPA :snprintf(exp, size, "WUPA");break; @@ -154,6 +153,10 @@ void annotateTopaz(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize) case TOPAZ_READ :snprintf(exp, size, "READ");break; case TOPAZ_WRITE_E :snprintf(exp, size, "WRITE-E");break; case TOPAZ_WRITE_NE :snprintf(exp, size, "WRITE-NE");break; + case TOPAZ_RSEG :snprintf(exp, size, "RSEG");break; + case TOPAZ_READ8 :snprintf(exp, size, "READ8");break; + case TOPAZ_WRITE_E8 :snprintf(exp, size, "WRITE-E8");break; + case TOPAZ_WRITE_NE8 :snprintf(exp, size, "WRITE-NE8");break; default: snprintf(exp,size,"?"); break; } } @@ -319,7 +322,7 @@ bool next_record_is_response(uint16_t tracepos, uint8_t *trace) bool merge_topaz_reader_frames(uint32_t timestamp, uint32_t *duration, uint16_t *tracepos, uint16_t traceLen, uint8_t *trace, uint8_t *frame, uint8_t *topaz_reader_command, uint16_t *data_len) { -#define MAX_TOPAZ_READER_CMD_LEN 9 +#define MAX_TOPAZ_READER_CMD_LEN 16 uint32_t last_timestamp = timestamp + *duration; @@ -479,7 +482,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui int num_lines = MIN((data_len - 1)/16 + 1, 16); for (int j = 0; j < num_lines ; j++) { if (j == 0) { - PrintAndLog(" %9d | %9d | %s |%-64s | %s| %s", + PrintAndLog(" %10d | %10d | %s |%-64s | %s| %s", (timestamp - first_timestamp), (EndOfTransmissionTimestamp - first_timestamp), (isResponse ? "Tag" : "Rdr"), @@ -487,7 +490,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui (j == num_lines-1) ? crc : " ", (j == num_lines-1) ? explanation : ""); } else { - PrintAndLog(" | | |%-64s | %s| %s", + PrintAndLog(" | | |%-64s | %s| %s", line[j], (j == num_lines-1) ? crc : " ", (j == num_lines-1) ? explanation : ""); diff --git a/client/cmdhf14a.c b/client/cmdhf14a.c index 8978f43d..214ff1ec 100644 --- a/client/cmdhf14a.c +++ b/client/cmdhf14a.c @@ -509,20 +509,22 @@ int CmdHF14ASnoop(const char *Cmd) { return 0; } + int CmdHF14ACmdRaw(const char *cmd) { UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; - uint8_t reply=1; - uint8_t crc=0; - uint8_t power=0; - uint8_t active=0; - uint8_t active_select=0; - uint16_t numbits=0; - uint32_t timeout=0; - uint8_t bTimeout=0; + bool reply=1; + bool crc = FALSE; + bool power = FALSE; + bool active = FALSE; + bool active_select = FALSE; + uint16_t numbits = 0; + bool bTimeout = FALSE; + uint32_t timeout = 0; + bool topazmode = FALSE; char buf[5]=""; - int i=0; + int i = 0; uint8_t data[USB_CMD_DATA_SIZE]; - uint16_t datalen=0; + uint16_t datalen = 0; uint32_t temp; if (strlen(cmd)<2) { @@ -534,9 +536,11 @@ int CmdHF14ACmdRaw(const char *cmd) { PrintAndLog(" -s active signal field ON with select"); PrintAndLog(" -b number of bits to send. Useful for send partial byte"); PrintAndLog(" -t timeout in ms"); + PrintAndLog(" -T use Topaz protocol to send command"); return 0; } + // strip while (*cmd==' ' || *cmd=='\t') cmd++; @@ -545,19 +549,19 @@ int CmdHF14ACmdRaw(const char *cmd) { if (cmd[i]=='-') { switch (cmd[i+1]) { case 'r': - reply=0; + reply = FALSE; break; case 'c': - crc=1; + crc = TRUE; break; case 'p': - power=1; + power = TRUE; break; case 'a': - active=1; + active = TRUE; break; case 's': - active_select=1; + active_select = TRUE; break; case 'b': sscanf(cmd+i+2,"%d",&temp); @@ -567,13 +571,16 @@ int CmdHF14ACmdRaw(const char *cmd) { i-=2; break; case 't': - bTimeout=1; + bTimeout = TRUE; sscanf(cmd+i+2,"%d",&temp); timeout = temp; i+=3; while(cmd[i]!=' ' && cmd[i]!='\0') { i++; } i-=2; break; + case 'T': + topazmode = TRUE; + break; default: PrintAndLog("Invalid option"); return 0; @@ -603,10 +610,15 @@ int CmdHF14ACmdRaw(const char *cmd) { PrintAndLog("Invalid char on input"); return 0; } + if(crc && datalen>0 && datalen MAX_TIMEOUT) { timeout = MAX_TIMEOUT; @@ -627,11 +639,16 @@ int CmdHF14ACmdRaw(const char *cmd) { } c.arg[2] = 13560000 / 1000 / (8*16) * timeout; // timeout in ETUs (time to transfer 1 bit, approx. 9.4 us) } + if(power) c.arg[0] |= ISO14A_NO_DISCONNECT; - if(datalen>0) + + if(datalen > 0) c.arg[0] |= ISO14A_RAW; + if(topazmode) + c.arg[0] |= ISO14A_TOPAZMODE; + // Max buffer is USB_CMD_DATA_SIZE c.arg[1] = (datalen & 0xFFFF) | (numbits << 16); memcpy(c.d.asBytes,data,datalen); @@ -647,6 +664,7 @@ int CmdHF14ACmdRaw(const char *cmd) { return 0; } + static void waitCmd(uint8_t iSelect) { uint8_t *recv; @@ -656,7 +674,7 @@ static void waitCmd(uint8_t iSelect) if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { recv = resp.d.asBytes; uint8_t iLen = iSelect ? resp.arg[1] : resp.arg[0]; - PrintAndLog("received %i octets",iLen); + PrintAndLog("received %i octets", iLen); if(!iLen) return; hexout = (char *)malloc(iLen * 3 + 1); diff --git a/client/cmdhftopaz.c b/client/cmdhftopaz.c index d747ed05..aed5f023 100644 --- a/client/cmdhftopaz.c +++ b/client/cmdhftopaz.c @@ -17,6 +17,71 @@ #include "cmdhftopaz.h" #include "cmdhf14a.h" #include "ui.h" +#include "mifare.h" +#include "proxmark3.h" +#include "iso14443crc.h" +#include "protocols.h" + + + +static void topaz_switch_on_field(void) +{ + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE, 0, 0}}; + SendCommand(&c); + + UsbCommand resp; + WaitForResponse(CMD_ACK, &resp); +} + + +static void topaz_switch_off_field(void) +{ + UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; + SendCommand(&c); + + UsbCommand resp; + WaitForResponse(CMD_ACK, &resp); +} + + +static void topaz_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response) +{ + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE, len, 0}}; + memcpy(c.d.asBytes, cmd, len); + SendCommand(&c); + + UsbCommand resp; + WaitForResponse(CMD_ACK, &resp); + + memcpy(response, resp.d.asBytes, resp.arg[0]); +} + + +static void topaz_send_cmd(uint8_t *cmd, uint8_t len, uint8_t *response) +{ + if (len > 1) { + uint8_t first, second; + ComputeCrc14443(CRC_14443_B, cmd, len, &first, &second); + cmd[len] = first; + cmd[len+1] = second; + } + + topaz_send_cmd_raw(cmd, len+2, response); +} + + +static void topaz_select(uint8_t *atqa, uint8_t *uid) +{ + // ToDo: implement anticollision + uint8_t rid_cmd[] = {TOPAZ_RID, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t wupa_cmd[] = {TOPAZ_WUPA}; + + topaz_switch_on_field(); + topaz_send_cmd(wupa_cmd, sizeof(wupa_cmd), atqa); + topaz_send_cmd(rid_cmd, sizeof(rid_cmd) - 2, uid); + topaz_switch_off_field(); +} + int CmdHFTopazReader(const char *Cmd) { diff --git a/common/protocols.h b/common/protocols.h index e687ca7a..b0f16570 100644 --- a/common/protocols.h +++ b/common/protocols.h @@ -176,6 +176,11 @@ NXP/Philips CUSTOM COMMANDS #define TOPAZ_READ 0x01 // Read (a single byte) #define TOPAZ_WRITE_E 0x53 // Write-with-erase (a single byte) #define TOPAZ_WRITE_NE 0x1a // Write-no-erase (a single byte) +// additional commands for Dynamic Memory Model +#define TOPAZ_RSEG 0x10 // Read segment +#define TOPAZ_READ8 0x02 // Read (eight bytes) +#define TOPAZ_WRITE_E8 0x54 // Write-with-erase (eight bytes) +#define TOPAZ_WRITE_NE8 0x1B // Write-no-erase (eight bytes) #define ISO_14443A 0 diff --git a/include/mifare.h b/include/mifare.h index e2b7a7c5..ad86886d 100644 --- a/include/mifare.h +++ b/include/mifare.h @@ -26,14 +26,15 @@ typedef struct { } __attribute__((__packed__)) iso14a_card_select_t; typedef enum ISO14A_COMMAND { - ISO14A_CONNECT = 1, - ISO14A_NO_DISCONNECT = 2, - ISO14A_APDU = 4, - ISO14A_RAW = 8, - ISO14A_REQUEST_TRIGGER = 0x10, - ISO14A_APPEND_CRC = 0x20, - ISO14A_SET_TIMEOUT = 0x40, - ISO14A_NO_SELECT = 0x80 + ISO14A_CONNECT = (1 << 0), + ISO14A_NO_DISCONNECT = (1 << 1), + ISO14A_APDU = (1 << 2), + ISO14A_RAW = (1 << 3), + ISO14A_REQUEST_TRIGGER = (1 << 4), + ISO14A_APPEND_CRC = (1 << 5), + ISO14A_SET_TIMEOUT = (1 << 6), + ISO14A_NO_SELECT = (1 << 7), + ISO14A_TOPAZMODE = (1 << 8) } iso14a_command_t; #endif // _MIFARE_H_ From de15fc5fe3090a95cf100c48af340e93a738576a Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 24 Mar 2015 07:17:00 +0100 Subject: [PATCH 06/47] add: hf topaz reader (basic functionality) --- client/cmdhf.c | 4 +- client/cmdhftopaz.c | 306 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 288 insertions(+), 22 deletions(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index 66c8e53c..ad8e5369 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -603,8 +603,8 @@ int CmdHFList(const char *Cmd) PrintAndLog("iso14443a - All times are in carrier periods (1/13.56Mhz)"); PrintAndLog("iClass - Timings are not as accurate"); PrintAndLog(""); - PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |"); - PrintAndLog("-----------|-----------|-----|-----------------------------------------------------------------|-----|--------------------|"); + PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |"); + PrintAndLog("------------|------------|-----|-----------------------------------------------------------------|-----|--------------------|"); while(tracepos < traceLen) { diff --git a/client/cmdhftopaz.c b/client/cmdhftopaz.c index aed5f023..95e988ed 100644 --- a/client/cmdhftopaz.c +++ b/client/cmdhftopaz.c @@ -24,13 +24,11 @@ + static void topaz_switch_on_field(void) { UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT | ISO14A_NO_SELECT | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE, 0, 0}}; SendCommand(&c); - - UsbCommand resp; - WaitForResponse(CMD_ACK, &resp); } @@ -38,13 +36,10 @@ static void topaz_switch_off_field(void) { UsbCommand c = {CMD_READER_ISO_14443a, {0, 0, 0}}; SendCommand(&c); - - UsbCommand resp; - WaitForResponse(CMD_ACK, &resp); } -static void topaz_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response) +static int topaz_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response) { UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_RAW | ISO14A_NO_DISCONNECT | ISO14A_TOPAZMODE, len, 0}}; memcpy(c.d.asBytes, cmd, len); @@ -53,39 +48,310 @@ static void topaz_send_cmd_raw(uint8_t *cmd, uint8_t len, uint8_t *response) UsbCommand resp; WaitForResponse(CMD_ACK, &resp); - memcpy(response, resp.d.asBytes, resp.arg[0]); + if (resp.arg[0] > 0) { + memcpy(response, resp.d.asBytes, resp.arg[0]); + } + + return resp.arg[0]; } -static void topaz_send_cmd(uint8_t *cmd, uint8_t len, uint8_t *response) +static int topaz_send_cmd(uint8_t *cmd, uint8_t len, uint8_t *response) { if (len > 1) { uint8_t first, second; - ComputeCrc14443(CRC_14443_B, cmd, len, &first, &second); - cmd[len] = first; - cmd[len+1] = second; + ComputeCrc14443(CRC_14443_B, cmd, len-2, &first, &second); + cmd[len-2] = first; + cmd[len-1] = second; } - topaz_send_cmd_raw(cmd, len+2, response); + return topaz_send_cmd_raw(cmd, len, response); } -static void topaz_select(uint8_t *atqa, uint8_t *uid) +static int topaz_select(uint8_t *atqa, uint8_t *rid_response) { // ToDo: implement anticollision - uint8_t rid_cmd[] = {TOPAZ_RID, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8_t wupa_cmd[] = {TOPAZ_WUPA}; - + uint8_t rid_cmd[] = {TOPAZ_RID, 0, 0, 0, 0, 0, 0, 0, 0}; + topaz_switch_on_field(); - topaz_send_cmd(wupa_cmd, sizeof(wupa_cmd), atqa); - topaz_send_cmd(rid_cmd, sizeof(rid_cmd) - 2, uid); - topaz_switch_off_field(); + + if (!topaz_send_cmd(wupa_cmd, sizeof(wupa_cmd), atqa)) { + topaz_switch_off_field(); + return -1; // WUPA failed + } + + if (!topaz_send_cmd(rid_cmd, sizeof(rid_cmd), rid_response)) { + topaz_switch_off_field(); + return -2; // RID failed + } + + return 0; // OK } +static int topaz_rall(uint8_t *uid, uint8_t *rall_response) +{ + uint8_t rall_cmd[] = {TOPAZ_RALL, 0, 0, 0, 0, 0, 0, 0, 0}; + + memcpy(&rall_cmd[3], uid, 4); + if (!topaz_send_cmd(rall_cmd, sizeof(rall_cmd), rall_response)) { + topaz_switch_off_field(); + return -1; // RALL failed + } + + return 0; +} + + +static bool topaz_block_is_locked(uint8_t blockno, uint8_t *lockbits) +{ + if(lockbits[blockno/8] >> (blockno % 8) & 0x01) { + return true; + } else { + return false; + } +} + + +static int topaz_print_CC(uint8_t *data) +{ + if(data[0] != 0xe1) { + return -1; // no NDEF message + } + + PrintAndLog("Capability Container: %02x %02x %02x %02x", data[0], data[1], data[2], data[3]); + PrintAndLog(" %02x: NDEF Magic Number", data[0]); + PrintAndLog(" %02x: version %d.%d supported by tag", data[1], (data[1] & 0xF0) >> 4, data[1] & 0x0f); + PrintAndLog(" %02x: Physical Memory Size of this tag: %d bytes", data[2], (data[2] + 1) * 8); + PrintAndLog(" %02x: %s / %s", data[3], + (data[3] & 0xF0) ? "(RFU)" : "Read access granted without any security", + (data[3] & 0x0F)==0 ? "Write access granted without any security" : (data[3] & 0x0F)==0x0F ? "No write access granted at all" : "(RFU)"); + return 0; +} + + +static void get_TLV(uint8_t **TLV_ptr, uint8_t *tag, uint16_t *length, uint8_t **value) +{ + *length = 0; + *value = NULL; + + *tag = **TLV_ptr; + *TLV_ptr += 1; + switch (*tag) { + case 0x00: // NULL TLV. + case 0xFE: // Terminator TLV. + break; + case 0x01: // Lock Control TLV + case 0x02: // Reserved Memory TLV + case 0x03: // NDEF message TLV + case 0xFD: // proprietary TLV + *length = **TLV_ptr; + *TLV_ptr += 1; + if (*length == 0xff) { + *length = **TLV_ptr << 8; + *TLV_ptr += 1; + *length |= **TLV_ptr; + *TLV_ptr += 1; + } + *value = *TLV_ptr; + *TLV_ptr += *length; + break; + default: // RFU + break; + } +} + + +static bool topaz_print_lock_control_TLVs(uint8_t *memory) +{ + uint8_t *TLV_ptr = memory; + uint8_t tag = 0; + uint16_t length; + uint8_t *value; + bool lock_TLV_present = false; + + while(*TLV_ptr != 0x03 && *TLV_ptr != 0xFD && *TLV_ptr != 0xFE) { + // all Lock Control TLVs shall be present before the NDEF message TLV, the proprietary TLV (and the Terminator TLV) + get_TLV(&TLV_ptr, &tag, &length, &value); + if (tag == 0x01) { // the Lock Control TLV + uint8_t pages_addr = value[0] >> 4; + uint8_t byte_offset = value[0] & 0x0f; + uint8_t size_in_bits = value[1] ? value[1] : 256; + uint8_t bytes_per_page = 1 << (value[2] & 0x0f); + uint8_t bytes_locked_per_bit = 1 << (value[2] >> 4); + PrintAndLog("Lock Area of %d bits at byte offset 0x%02x. Each Lock Bit locks %d bytes.", + size_in_bits, + pages_addr * bytes_per_page + byte_offset, + bytes_locked_per_bit); + lock_TLV_present = true; + } + } + + if (!lock_TLV_present) { + PrintAndLog("(No Lock Control TLV present)"); + return -1; + } else { + return 0; + } +} + + +static int topaz_print_reserved_memory_control_TLVs(uint8_t *memory) +{ + uint8_t *TLV_ptr = memory; + uint8_t tag = 0; + uint16_t length; + uint8_t *value; + bool reserved_memory_control_TLV_present = false; + + while(*TLV_ptr != 0x03 && *TLV_ptr != 0xFD && *TLV_ptr != 0xFE) { + // all Reserved Memory Control TLVs shall be present before the NDEF message TLV, the proprietary TLV (and the Terminator TLV) + get_TLV(&TLV_ptr, &tag, &length, &value); + if (tag == 0x02) { // the Reserved Memory Control TLV + uint8_t pages_addr = value[0] >> 4; + uint8_t byte_offset = value[0] & 0x0f; + uint8_t size_in_bytes = value[1] ? value[1] : 256; + uint8_t bytes_per_page = 1 << (value[2] & 0x0f); + PrintAndLog("Reserved Memory of %d bytes at byte offset 0x%02x.", + size_in_bytes, + pages_addr * bytes_per_page + byte_offset); + reserved_memory_control_TLV_present = true; + } + } + + if (!reserved_memory_control_TLV_present) { + PrintAndLog("(No Reserved Memory Control TLV present)"); + return -1; + } else { + return 0; + } +} + + +static void topaz_print_lifecycle_state(uint8_t *data) +{ + +} + + +static void topaz_print_NDEF(uint8_t *data) +{ + +} + + int CmdHFTopazReader(const char *Cmd) { - PrintAndLog("not yet implemented"); + int status; + uint8_t atqa[2]; + uint8_t rid_response[8]; + uint8_t *uid_echo = &rid_response[2]; + union { + uint8_t raw_content[124]; + struct { + uint8_t HR[2]; + uint8_t data_block[15][8]; + uint8_t CRC[2]; + } static_memory; + } rall_response; + uint8_t *static_lock_bytes = rall_response.static_memory.data_block[0x0e]; + + status = topaz_select(atqa, rid_response); + + if (status == -1) { + PrintAndLog("Error: couldn't receive ATQA"); + return -1; + } + + PrintAndLog("ATQA : %02x %02x", atqa[1], atqa[0]); + if (atqa[1] != 0x0c && atqa[0] != 0x00) { + PrintAndLog("Tag doesn't support the Topaz protocol."); + topaz_switch_off_field(); + return -1; + } + + if (status == -2) { + PrintAndLog("Error: tag didn't answer to RID"); + topaz_switch_off_field(); + return -1; + } + + // ToDo: CRC check + PrintAndLog("HR0 : %02x (%sa Topaz tag (%scapable of carrying a NDEF message), %s memory map)", rid_response[0], + (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", + (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", + (rid_response[0] & 0x0F) == 0x10 ? "static" : "dynamic"); + PrintAndLog("HR1 : %02x", rid_response[1]); + + status = topaz_rall(uid_echo, rall_response.raw_content); + + if(status == -1) { + PrintAndLog("Error: tag didn't answer to RALL"); + topaz_switch_off_field(); + return -1; + } + + PrintAndLog("UID : %02x %02x %02x %02x %02x %02x %02x", + rall_response.static_memory.data_block[0][6], + rall_response.static_memory.data_block[0][5], + rall_response.static_memory.data_block[0][4], + rall_response.static_memory.data_block[0][3], + rall_response.static_memory.data_block[0][2], + rall_response.static_memory.data_block[0][1], + rall_response.static_memory.data_block[0][0]); + PrintAndLog(" UID[6] (Manufacturer Byte) = %02x, Manufacturer: %s", + rall_response.static_memory.data_block[0][6], + getTagInfo(rall_response.static_memory.data_block[0][6])); + + PrintAndLog(""); + PrintAndLog("Static Data blocks 00 to 0c:"); + PrintAndLog("block# | offset | Data | Locked?"); + char line[80]; + for (uint16_t i = 0; i <= 0x0c; i++) { + for (uint16_t j = 0; j < 8; j++) { + sprintf(&line[3*j], "%02x ", rall_response.static_memory.data_block[i][j] /*rall_response[2 + 8*i + j]*/); + } + PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", i, i*8, line, topaz_block_is_locked(i, static_lock_bytes) ? "yes" : "no"); + } + + PrintAndLog(""); + PrintAndLog("Static Reserved block 0d:"); + for (uint16_t j = 0; j < 8; j++) { + sprintf(&line[3*j], "%02x ", rall_response.static_memory.data_block[0x0d][j]); + } + PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", 0x0d, 0x0d*8, line, "n/a"); + + PrintAndLog(""); + PrintAndLog("Static Lockbits / OTP block 0e:"); + for (uint16_t j = 0; j < 8; j++) { + sprintf(&line[3*j], "%02x ", static_lock_bytes[j]); + } + PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", 0x0e, 0x0e*8, line, "n/a"); + + PrintAndLog(""); + + status = topaz_print_CC(&rall_response.static_memory.data_block[1][0]); + + if (status == -1) { + PrintAndLog("No NDEF message present"); + topaz_switch_off_field(); + return 0; + } + + PrintAndLog(""); + bool lock_TLV_present = topaz_print_lock_control_TLVs(&rall_response.static_memory.data_block[1][4]); + + PrintAndLog(""); + bool reserved_mem_present = topaz_print_reserved_memory_control_TLVs(&rall_response.static_memory.data_block[1][4]); + + topaz_print_lifecycle_state(&rall_response.static_memory.data_block[1][0]); + + topaz_print_NDEF(&rall_response.static_memory.data_block[1][0]); + + topaz_switch_off_field(); return 0; } From c5847ae8af8d567da6cb905bd59c886e3d0c040b Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sat, 28 Mar 2015 18:06:21 +0100 Subject: [PATCH 07/47] refactoring hf topaz reader --- client/cmdhftopaz.c | 70 ++++++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/client/cmdhftopaz.c b/client/cmdhftopaz.c index 95e988ed..e76b3fb5 100644 --- a/client/cmdhftopaz.c +++ b/client/cmdhftopaz.c @@ -22,7 +22,16 @@ #include "iso14443crc.h" #include "protocols.h" +#define TOPAZ_MAX_MEMORY 2048 +static struct { + uint8_t HR01[2]; + uint8_t uid[7]; + uint8_t size; + uint8_t data_blocks[TOPAZ_MAX_MEMORY/8][8]; + uint8_t *dynamic_lock_areas; + uint8_t *dynamic_reserved_areas; +} topaz_tag; static void topaz_switch_on_field(void) @@ -92,12 +101,12 @@ static int topaz_select(uint8_t *atqa, uint8_t *rid_response) } -static int topaz_rall(uint8_t *uid, uint8_t *rall_response) +static int topaz_rall(uint8_t *uid, uint8_t *response) { uint8_t rall_cmd[] = {TOPAZ_RALL, 0, 0, 0, 0, 0, 0, 0, 0}; memcpy(&rall_cmd[3], uid, 4); - if (!topaz_send_cmd(rall_cmd, sizeof(rall_cmd), rall_response)) { + if (!topaz_send_cmd(rall_cmd, sizeof(rall_cmd), response)) { topaz_switch_off_field(); return -1; // RALL failed } @@ -249,15 +258,7 @@ int CmdHFTopazReader(const char *Cmd) uint8_t atqa[2]; uint8_t rid_response[8]; uint8_t *uid_echo = &rid_response[2]; - union { - uint8_t raw_content[124]; - struct { - uint8_t HR[2]; - uint8_t data_block[15][8]; - uint8_t CRC[2]; - } static_memory; - } rall_response; - uint8_t *static_lock_bytes = rall_response.static_memory.data_block[0x0e]; + uint8_t rall_response[124]; status = topaz_select(atqa, rid_response); @@ -279,6 +280,9 @@ int CmdHFTopazReader(const char *Cmd) return -1; } + topaz_tag.HR01[0] = rid_response[0]; + topaz_tag.HR01[1] = rid_response[1]; + // ToDo: CRC check PrintAndLog("HR0 : %02x (%sa Topaz tag (%scapable of carrying a NDEF message), %s memory map)", rid_response[0], (rid_response[0] & 0xF0) == 0x10 ? "" : "not ", @@ -286,7 +290,7 @@ int CmdHFTopazReader(const char *Cmd) (rid_response[0] & 0x0F) == 0x10 ? "static" : "dynamic"); PrintAndLog("HR1 : %02x", rid_response[1]); - status = topaz_rall(uid_echo, rall_response.raw_content); + status = topaz_rall(uid_echo, rall_response); if(status == -1) { PrintAndLog("Error: tag didn't answer to RALL"); @@ -294,46 +298,48 @@ int CmdHFTopazReader(const char *Cmd) return -1; } + memcpy(topaz_tag.uid, rall_response+2, 7); PrintAndLog("UID : %02x %02x %02x %02x %02x %02x %02x", - rall_response.static_memory.data_block[0][6], - rall_response.static_memory.data_block[0][5], - rall_response.static_memory.data_block[0][4], - rall_response.static_memory.data_block[0][3], - rall_response.static_memory.data_block[0][2], - rall_response.static_memory.data_block[0][1], - rall_response.static_memory.data_block[0][0]); + topaz_tag.uid[6], + topaz_tag.uid[5], + topaz_tag.uid[4], + topaz_tag.uid[3], + topaz_tag.uid[2], + topaz_tag.uid[1], + topaz_tag.uid[0]); PrintAndLog(" UID[6] (Manufacturer Byte) = %02x, Manufacturer: %s", - rall_response.static_memory.data_block[0][6], - getTagInfo(rall_response.static_memory.data_block[0][6])); - + topaz_tag.uid[6], + getTagInfo(topaz_tag.uid[6])); + + memcpy(topaz_tag.data_blocks, rall_response+2, 0x10*8); PrintAndLog(""); PrintAndLog("Static Data blocks 00 to 0c:"); PrintAndLog("block# | offset | Data | Locked?"); char line[80]; for (uint16_t i = 0; i <= 0x0c; i++) { for (uint16_t j = 0; j < 8; j++) { - sprintf(&line[3*j], "%02x ", rall_response.static_memory.data_block[i][j] /*rall_response[2 + 8*i + j]*/); + sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[i][j] /*rall_response[2 + 8*i + j]*/); } - PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", i, i*8, line, topaz_block_is_locked(i, static_lock_bytes) ? "yes" : "no"); + PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", i, i*8, line, topaz_block_is_locked(i, &topaz_tag.data_blocks[0x0d][0]) ? "yes" : "no"); } PrintAndLog(""); PrintAndLog("Static Reserved block 0d:"); for (uint16_t j = 0; j < 8; j++) { - sprintf(&line[3*j], "%02x ", rall_response.static_memory.data_block[0x0d][j]); + sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[0x0d][j]); } PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", 0x0d, 0x0d*8, line, "n/a"); PrintAndLog(""); - PrintAndLog("Static Lockbits / OTP block 0e:"); + PrintAndLog("Static Lockbits and OTP Bytes:"); for (uint16_t j = 0; j < 8; j++) { - sprintf(&line[3*j], "%02x ", static_lock_bytes[j]); + sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[0x0e][j]); } PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", 0x0e, 0x0e*8, line, "n/a"); PrintAndLog(""); - status = topaz_print_CC(&rall_response.static_memory.data_block[1][0]); + status = topaz_print_CC(&topaz_tag.data_blocks[1][0]); if (status == -1) { PrintAndLog("No NDEF message present"); @@ -342,14 +348,14 @@ int CmdHFTopazReader(const char *Cmd) } PrintAndLog(""); - bool lock_TLV_present = topaz_print_lock_control_TLVs(&rall_response.static_memory.data_block[1][4]); + bool lock_TLV_present = topaz_print_lock_control_TLVs(&topaz_tag.data_blocks[1][4]); PrintAndLog(""); - bool reserved_mem_present = topaz_print_reserved_memory_control_TLVs(&rall_response.static_memory.data_block[1][4]); + bool reserved_mem_present = topaz_print_reserved_memory_control_TLVs(&topaz_tag.data_blocks[1][4]); - topaz_print_lifecycle_state(&rall_response.static_memory.data_block[1][0]); + topaz_print_lifecycle_state(&topaz_tag.data_blocks[1][0]); - topaz_print_NDEF(&rall_response.static_memory.data_block[1][0]); + topaz_print_NDEF(&topaz_tag.data_blocks[1][0]); topaz_switch_off_field(); return 0; From 6e6f1099c83989b45c494d18cf701ffcff4af5ee Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 1 Jul 2015 07:12:10 +0200 Subject: [PATCH 08/47] hf topaz reader: add support for dynamic lock areas --- client/cmdhftopaz.c | 93 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/client/cmdhftopaz.c b/client/cmdhftopaz.c index 4e515f78..ebe3a571 100644 --- a/client/cmdhftopaz.c +++ b/client/cmdhftopaz.c @@ -22,15 +22,25 @@ #include "iso14443crc.h" #include "protocols.h" -#define TOPAZ_MAX_MEMORY 2048 +#define TOPAZ_STATIC_MEMORY (0x0f * 8) + +typedef struct dynamic_lock_area { + struct dynamic_lock_area *next; + uint16_t byte_offset; + uint16_t size_in_bits; + uint16_t first_locked_byte; + uint16_t bytes_locked_per_bit; +} dynamic_lock_area_t; + static struct { uint8_t HR01[2]; uint8_t uid[7]; uint8_t size; - uint8_t data_blocks[TOPAZ_MAX_MEMORY/8][8]; - uint8_t *dynamic_lock_areas; + uint8_t data_blocks[TOPAZ_STATIC_MEMORY/8][8]; + dynamic_lock_area_t *dynamic_lock_areas; uint8_t *dynamic_reserved_areas; + uint8_t *dynamic_memory; } topaz_tag; @@ -115,8 +125,46 @@ static int topaz_rall(uint8_t *uid, uint8_t *response) } -static bool topaz_block_is_locked(uint8_t blockno, uint8_t *lockbits) +static dynamic_lock_area_t *get_dynamic_lock_area(uint16_t byteno) { + dynamic_lock_area_t *lock_area; + + lock_area = topaz_tag.dynamic_lock_areas; + + while (lock_area != NULL) { + if (byteno < lock_area->first_locked_byte) { + lock_area = lock_area->next; + } else { + return lock_area; + } + } + + return NULL; +} + + +// check if a memory block (8 Bytes) is locked. +// TODO: support other sizes of locked_bytes_per_bit (current assumption: each lock bit locks 8 Bytes) +static bool topaz_byte_is_locked(uint16_t byteno) +{ + uint8_t *lockbits; + uint16_t locked_bytes_per_bit; + dynamic_lock_area_t *lock_area; + + if (byteno < TOPAZ_STATIC_MEMORY) { + lockbits = &topaz_tag.data_blocks[0x0e][0]; + locked_bytes_per_bit = 8; + } else { + lock_area = get_dynamic_lock_area(byteno); + if (lock_area == NULL) { + return false; + } + locked_bytes_per_bit = lock_area->bytes_locked_per_bit; + byteno = byteno - lock_area->first_locked_byte; + lockbits = &topaz_tag.dynamic_memory[lock_area->byte_offset - TOPAZ_STATIC_MEMORY]; + } + + uint16_t blockno = byteno / locked_bytes_per_bit; if(lockbits[blockno/8] >> (blockno % 8) & 0x01) { return true; } else { @@ -134,7 +182,9 @@ static int topaz_print_CC(uint8_t *data) PrintAndLog("Capability Container: %02x %02x %02x %02x", data[0], data[1], data[2], data[3]); PrintAndLog(" %02x: NDEF Magic Number", data[0]); PrintAndLog(" %02x: version %d.%d supported by tag", data[1], (data[1] & 0xF0) >> 4, data[1] & 0x0f); - PrintAndLog(" %02x: Physical Memory Size of this tag: %d bytes", data[2], (data[2] + 1) * 8); + uint16_t memsize = (data[2] + 1) * 8; + topaz_tag.dynamic_memory = malloc(memsize - TOPAZ_STATIC_MEMORY); + PrintAndLog(" %02x: Physical Memory Size of this tag: %d bytes", data[2], memsize); PrintAndLog(" %02x: %s / %s", data[3], (data[3] & 0xF0) ? "(RFU)" : "Read access granted without any security", (data[3] & 0x0F)==0 ? "Write access granted without any security" : (data[3] & 0x0F)==0x0F ? "No write access granted at all" : "(RFU)"); @@ -181,6 +231,7 @@ static bool topaz_print_lock_control_TLVs(uint8_t *memory) uint16_t length; uint8_t *value; bool lock_TLV_present = false; + uint16_t first_locked_byte = 0x0f * 8; while(*TLV_ptr != 0x03 && *TLV_ptr != 0xFD && *TLV_ptr != 0xFE) { // all Lock Control TLVs shall be present before the NDEF message TLV, the proprietary TLV (and the Terminator TLV) @@ -188,14 +239,30 @@ static bool topaz_print_lock_control_TLVs(uint8_t *memory) if (tag == 0x01) { // the Lock Control TLV uint8_t pages_addr = value[0] >> 4; uint8_t byte_offset = value[0] & 0x0f; - uint8_t size_in_bits = value[1] ? value[1] : 256; - uint8_t bytes_per_page = 1 << (value[2] & 0x0f); - uint8_t bytes_locked_per_bit = 1 << (value[2] >> 4); - PrintAndLog("Lock Area of %d bits at byte offset 0x%02x. Each Lock Bit locks %d bytes.", + uint16_t size_in_bits = value[1] ? value[1] : 256; + uint16_t bytes_per_page = 1 << (value[2] & 0x0f); + uint16_t bytes_locked_per_bit = 1 << (value[2] >> 4); + PrintAndLog("Lock Area of %d bits at byte offset 0x%04x. Each Lock Bit locks %d bytes.", size_in_bits, pages_addr * bytes_per_page + byte_offset, bytes_locked_per_bit); lock_TLV_present = true; + dynamic_lock_area_t *old = topaz_tag.dynamic_lock_areas; + dynamic_lock_area_t *new = topaz_tag.dynamic_lock_areas; + if (old == NULL) { + new = topaz_tag.dynamic_lock_areas = (dynamic_lock_area_t *)malloc(sizeof(dynamic_lock_area_t)); + } else { + while(old->next != NULL) { + old = old->next; + } + new = old->next = (dynamic_lock_area_t *)malloc(sizeof(dynamic_lock_area_t)); + } + new->next = NULL; + new->first_locked_byte = first_locked_byte; + new->byte_offset = pages_addr * bytes_per_page + byte_offset; + new->size_in_bits = size_in_bits; + new->bytes_locked_per_bit = bytes_locked_per_bit; + first_locked_byte = first_locked_byte + size_in_bits*bytes_locked_per_bit; } } @@ -314,13 +381,13 @@ int CmdHFTopazReader(const char *Cmd) memcpy(topaz_tag.data_blocks, rall_response+2, 0x10*8); PrintAndLog(""); PrintAndLog("Static Data blocks 00 to 0c:"); - PrintAndLog("block# | offset | Data | Locked?"); + PrintAndLog("block# | offset | Data | Locked(y/n)"); char line[80]; for (uint16_t i = 0; i <= 0x0c; i++) { for (uint16_t j = 0; j < 8; j++) { sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[i][j] /*rall_response[2 + 8*i + j]*/); } - PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", i, i*8, line, topaz_block_is_locked(i, &topaz_tag.data_blocks[0x0e][0]) ? "yes" : "no"); + PrintAndLog(" 0x%02x | 0x%04x | %s| %-3s", i, i*8, line, topaz_byte_is_locked(i*8) ? "yyyyyyyy" : "nnnnnnnn"); } PrintAndLog(""); @@ -328,14 +395,14 @@ int CmdHFTopazReader(const char *Cmd) for (uint16_t j = 0; j < 8; j++) { sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[0x0d][j]); } - PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", 0x0d, 0x0d*8, line, "n/a"); + PrintAndLog(" 0x%02x | 0x%04x | %s| %-3s", 0x0d, 0x0d*8, line, "n/a"); PrintAndLog(""); PrintAndLog("Static Lockbits and OTP Bytes:"); for (uint16_t j = 0; j < 8; j++) { sprintf(&line[3*j], "%02x ", topaz_tag.data_blocks[0x0e][j]); } - PrintAndLog(" 0x%02x | 0x%02x | %s| %-3s", 0x0e, 0x0e*8, line, "n/a"); + PrintAndLog(" 0x%02x | 0x%04x | %s| %-3s", 0x0e, 0x0e*8, line, "n/a"); PrintAndLog(""); From aa53efc340d9f2dc382e4bb98d49bede5a18e920 Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Mon, 20 Jul 2015 13:41:40 -0400 Subject: [PATCH 09/47] iclass additions multiple contributors - thanks! --- armsrc/appmain.c | 19 +- armsrc/apps.h | 9 +- armsrc/iclass.c | 285 +++--- client/cmdhf.c | 10 +- client/cmdhficlass.c | 1590 ++++++++++++++++++++++++++++------ client/cmdhficlass.h | 6 + client/cmdlf.c | 2 +- client/hid-flasher/usb_cmd.h | 7 + client/loclass/cipher.c | 21 + client/loclass/cipher.h | 2 + client/lualibs/commands.lua | 9 +- common/protocols.c | 28 +- include/usb_cmd.h | 7 +- 13 files changed, 1580 insertions(+), 415 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 0cbfa249..906379a7 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -860,11 +860,26 @@ void UsbPacketReceived(uint8_t *packet, int len) ReaderIClass(c->arg[0]); break; case CMD_READER_ICLASS_REPLAY: - ReaderIClass_Replay(c->arg[0], c->d.asBytes); + ReaderIClass_Replay(c->arg[0], c->d.asBytes); break; - case CMD_ICLASS_EML_MEMSET: + case CMD_ICLASS_EML_MEMSET: emlSet(c->d.asBytes,c->arg[0], c->arg[1]); break; + case CMD_ICLASS_WRITEBLOCK: + iClass_WriteBlock(c->arg[0], c->arg[1], c->d.asBytes); + break; + case CMD_ICLASS_READBLOCK: + iClass_ReadBlk(c->arg[0], c->arg[1]); + break; + case CMD_ICLASS_AUTHENTICATION: + iClass_Authentication(c->d.asBytes); + break; + case CMD_ICLASS_DUMP: + iClass_Dump(c->arg[0], c->arg[1], c->arg[2]); + break; + case CMD_ICLASS_CLONE: + iClass_Clone(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; #endif case CMD_BUFF_CLEAR: diff --git a/armsrc/apps.h b/armsrc/apps.h index 42efd118..e8b43e9b 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -149,9 +149,6 @@ void OnSuccess(); void OnError(uint8_t reason); - - - /// iso15693.h void RecordRawAdcSamplesIso15693(void); void AcquireRawAdcSamplesIso15693(void); @@ -167,6 +164,12 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain void ReaderIClass(uint8_t arg0); void ReaderIClass_Replay(uint8_t arg0,uint8_t *MAC); void IClass_iso14443A_GetPublic(uint8_t arg0); +void iClass_Authentication(uint8_t *MAC); +void iClass_WriteBlock(uint8_t blockNo, uint8_t keyType, uint8_t *data); +void iClass_ReadBlk(uint8_t blockNo, uint8_t keyType); +bool iClass_ReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *readdata); +void iClass_Dump(uint8_t blockno, uint8_t numblks, uint8_t keyType); +void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t keyType, uint8_t *data); // hitag2.h void SnoopHitag(uint32_t type); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 97c62bb6..a27fb970 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1601,16 +1601,16 @@ void setupIclassReader() } -size_t sendCmdGetResponseWithRetries(uint8_t* command, size_t cmdsize, uint8_t* resp, uint8_t expected_size, uint8_t retries) +bool sendCmdGetResponseWithRetries(uint8_t* command, size_t cmdsize, uint8_t* resp, uint8_t expected_size, uint8_t retries) { while(retries-- > 0) { ReaderTransmitIClass(command, cmdsize); if(expected_size == ReaderReceiveIClass(resp)){ - return 0; + return true; } } - return 1;//Error + return false;//Error } /** @@ -1620,14 +1620,17 @@ size_t sendCmdGetResponseWithRetries(uint8_t* command, size_t cmdsize, uint8_t* * 1 = Got CSN * 2 = Got CSN and CC */ -uint8_t handshakeIclassTag(uint8_t *card_data) +uint8_t handshakeIclassTag_ext(uint8_t *card_data, bool use_credit_key) { static uint8_t act_all[] = { 0x0a }; - static uint8_t identify[] = { 0x0c }; + //static uint8_t identify[] = { 0x0c }; + static uint8_t identify[] = { 0x0c, 0x00, 0x73, 0x33 }; static uint8_t select[] = { 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - - static uint8_t readcheck_cc[]= { 0x88, 0x02,}; + static uint8_t readcheck_cc[]= { 0x88, 0x02 }; + if (use_credit_key) + readcheck_cc[0] = 0x18; + else + readcheck_cc[0] = 0x88; uint8_t resp[ICLASS_BUFFER_SIZE]; @@ -1668,6 +1671,9 @@ uint8_t handshakeIclassTag(uint8_t *card_data) return read_status; } +uint8_t handshakeIclassTag(uint8_t *card_data){ + return handshakeIclassTag_ext(card_data, false); +} // Reader iClass Anticollission @@ -1687,6 +1693,9 @@ void ReaderIClass(uint8_t arg0) { uint8_t result_status = 0; bool abort_after_read = arg0 & FLAG_ICLASS_READER_ONLY_ONCE; bool try_once = arg0 & FLAG_ICLASS_READER_ONE_TRY; + bool use_credit_key = false; + if (arg0 & FLAG_ICLASS_READER_CEDITKEY) + use_credit_key = true; set_tracing(TRUE); setupIclassReader(); @@ -1701,7 +1710,7 @@ void ReaderIClass(uint8_t arg0) { } WDT_HIT(); - read_status = handshakeIclassTag(card_data); + read_status = handshakeIclassTag_ext(card_data, use_credit_key); if(read_status == 0) continue; if(read_status == 1) result_status = FLAG_ICLASS_READER_CSN; @@ -1715,11 +1724,10 @@ void ReaderIClass(uint8_t arg0) { if(arg0 & FLAG_ICLASS_READER_CONF) { if(sendCmdGetResponseWithRetries(readConf, sizeof(readConf),card_data+8, 10, 10)) - { - Dbprintf("Failed to dump config block"); - }else { result_status |= FLAG_ICLASS_READER_CONF; + } else { + Dbprintf("Failed to dump config block"); } } @@ -1727,10 +1735,9 @@ void ReaderIClass(uint8_t arg0) { if(arg0 & FLAG_ICLASS_READER_AA){ if(sendCmdGetResponseWithRetries(readAA, sizeof(readAA),card_data+(8*4), 10, 10)) { -// Dbprintf("Failed to dump AA block"); - }else - { result_status |= FLAG_ICLASS_READER_AA; + } else { + //Dbprintf("Failed to dump AA block"); } } @@ -1814,7 +1821,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { //for now replay captured auth (as cc not updated) memcpy(check+5,MAC,4); - if(sendCmdGetResponseWithRetries(check, sizeof(check),resp, 4, 5)) + if(!sendCmdGetResponseWithRetries(check, sizeof(check),resp, 4, 5)) { Dbprintf("Error: Authentication Fail!"); continue; @@ -1826,7 +1833,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { read[2] = crc >> 8; read[3] = crc & 0xff; - if(sendCmdGetResponseWithRetries(read, sizeof(read),resp, 10, 10)) + if(!sendCmdGetResponseWithRetries(read, sizeof(read),resp, 10, 10)) { Dbprintf("Dump config (block 1) failed"); continue; @@ -1853,7 +1860,7 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { read[2] = crc >> 8; read[3] = crc & 0xff; - if(!sendCmdGetResponseWithRetries(read, sizeof(read), resp, 10, 10)) + if(sendCmdGetResponseWithRetries(read, sizeof(read), resp, 10, 10)) { Dbprintf(" %02x: %02x %02x %02x %02x %02x %02x %02x %02x", block, resp[0], resp[1], resp[2], @@ -1904,130 +1911,130 @@ void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { LED_A_OFF(); } -//2. Create Read method (cut-down from above) based off responses from 1. -// Since we have the MAC could continue to use replay function. -//3. Create Write method -/* -void IClass_iso14443A_write(uint8_t arg0, uint8_t blockNo, uint8_t *data, uint8_t *MAC) { - uint8_t act_all[] = { 0x0a }; - uint8_t identify[] = { 0x0c }; - uint8_t select[] = { 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t readcheck_cc[]= { 0x88, 0x02 }; - uint8_t check[] = { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - uint8_t read[] = { 0x0c, 0x00, 0x00, 0x00 }; - uint8_t write[] = { 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - - uint16_t crc = 0; - - uint8_t* resp = (((uint8_t *)BigBuf) + 3560); +void iClass_Authentication(uint8_t *MAC) { + uint8_t check[] = { 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t resp[ICLASS_BUFFER_SIZE]; + memcpy(check+5,MAC,4); + bool isOK; + isOK = sendCmdGetResponseWithRetries(check, sizeof(check),resp, 4, 5); + cmd_send(CMD_ACK,isOK,0,0,0,0); + //Dbprintf("isOK %d, Tag response : %02x%02x%02x%02x",isOK,resp[0],resp[1],resp[2],resp[3]); +} +bool iClass_ReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *readdata) { + uint8_t readcmd[] = {keyType, blockNo}; //0x88, 0x00 + uint8_t resp[8]; + size_t isOK = 1; - // Reset trace buffer - memset(trace, 0x44, RECV_CMD_OFFSET); - traceLen = 0; + readcmd[1] = blockNo; + isOK = sendCmdGetResponseWithRetries(readcmd, sizeof(readcmd),resp, 8, 5); + memcpy(readdata,resp,sizeof(resp)); - // Setup SSC - FpgaSetupSsc(); - // Start from off (no field generated) - // Signal field is off with the appropriate LED - LED_D_OFF(); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(200); + return isOK; +} - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); +void iClass_ReadBlk(uint8_t blockno, uint8_t keyType) { + uint8_t readblockdata[8]; + bool isOK = false; + isOK = iClass_ReadBlock(blockno, keyType, readblockdata); + //Dbprintf("read block [%02x] [%02x%02x%02x%02x%02x%02x%02x%02x]",blockNo,readblockdata[0],readblockdata[1],readblockdata[2],readblockdata[3],readblockdata[4],readblockdata[5],readblockdata[6],readblockdata[7]); + cmd_send(CMD_ACK,isOK,0,0,readblockdata,8); +} - // Now give it time to spin up. - // Signal field is on with the appropriate LED - FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); - SpinDelay(200); +void iClass_Dump(uint8_t blockno, uint8_t numblks, uint8_t keyType) { + uint8_t readblockdata[8]; + bool isOK = false; + uint8_t blkCnt = 0; - LED_A_ON(); - - for(int i=0;i<1;i++) { - - if(traceLen > TRACE_SIZE) { - DbpString("Trace full"); - break; - } - - if (BUTTON_PRESS()) break; - - // Send act_all - ReaderTransmitIClass(act_all, 1); - // Card present? - if(ReaderReceiveIClass(resp)) { - ReaderTransmitIClass(identify, 1); - if(ReaderReceiveIClass(resp) == 10) { - // Select card - memcpy(&select[1],resp,8); - ReaderTransmitIClass(select, sizeof(select)); - - if(ReaderReceiveIClass(resp) == 10) { - Dbprintf(" Selected CSN: %02x %02x %02x %02x %02x %02x %02x %02x", - resp[0], resp[1], resp[2], - resp[3], resp[4], resp[5], - resp[6], resp[7]); - } - // Card selected - Dbprintf("Readcheck on Sector 2"); - ReaderTransmitIClass(readcheck_cc, sizeof(readcheck_cc)); - if(ReaderReceiveIClass(resp) == 8) { - Dbprintf(" CC: %02x %02x %02x %02x %02x %02x %02x %02x", - resp[0], resp[1], resp[2], - resp[3], resp[4], resp[5], - resp[6], resp[7]); - }else return; - Dbprintf("Authenticate"); - //for now replay captured auth (as cc not updated) - memcpy(check+5,MAC,4); - Dbprintf(" AA: %02x %02x %02x %02x", - check[5], check[6], check[7],check[8]); - ReaderTransmitIClass(check, sizeof(check)); - if(ReaderReceiveIClass(resp) == 4) { - Dbprintf(" AR: %02x %02x %02x %02x", - resp[0], resp[1], resp[2],resp[3]); - }else { - Dbprintf("Error: Authentication Fail!"); - return; - } - Dbprintf("Write Block"); - - //read configuration for max block number - read_success=false; - read[1]=1; - uint8_t *blockno=&read[1]; - crc = iclass_crc16((char *)blockno,1); - read[2] = crc >> 8; - read[3] = crc & 0xff; - while(!read_success){ - ReaderTransmitIClass(read, sizeof(read)); - if(ReaderReceiveIClass(resp) == 10) { - read_success=true; - mem=resp[5]; - memory.k16= (mem & 0x80); - memory.book= (mem & 0x20); - memory.k2= (mem & 0x8); - memory.lockauth= (mem & 0x2); - memory.keyaccess= (mem & 0x1); - - } - } - if (memory.k16){ - cardsize=255; - }else cardsize=32; - //check card_size - - memcpy(write+1,blockNo,1); - memcpy(write+2,data,8); - memcpy(write+10,mac,4); - while(!send_success){ - ReaderTransmitIClass(write, sizeof(write)); - if(ReaderReceiveIClass(resp) == 10) { - write_success=true; - } - }// - } - WDT_HIT(); + BigBuf_free(); + uint8_t *dataout = BigBuf_malloc(255*8); + memset(dataout,0xFF,255*8); + if (dataout == NULL){ + Dbprintf("out of memory"); + OnError(1); + return; } - - LED_A_OFF(); -}*/ + + for (;blkCnt < numblks; blkCnt++) { + isOK = iClass_ReadBlock(blockno+blkCnt, keyType, readblockdata); + if (!isOK || (readblockdata[0] == 0xBB || readblockdata[7] == 0x33 || readblockdata[2] == 0xBB)) { //try again + isOK = iClass_ReadBlock(blockno+blkCnt, keyType, readblockdata); + if (!isOK) { + Dbprintf("Block %02X failed to read", blkCnt+blockno); + break; + } + } + memcpy(dataout+(blkCnt*8),readblockdata,8); + /*Dbprintf("| %02x | %02x%02x%02x%02x%02x%02x%02x%02x |", + blockno+blkCnt, readblockdata[0], readblockdata[1], readblockdata[2], + readblockdata[3], readblockdata[4], readblockdata[5], + readblockdata[6], readblockdata[7]); + */ + } + //return pointer to dump memory in arg3 + cmd_send(CMD_ACK,isOK,blkCnt,BigBuf_max_traceLen(),0,0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + BigBuf_free(); +} + +bool iClass_WriteBlock_ext(uint8_t blockNo, uint8_t keyType, uint8_t *data) { + uint8_t write[] = { 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t readblockdata[8]; + write[1] = blockNo; + memcpy(write+2, data, 12); // data + mac + uint8_t resp[10]; + bool isOK; + isOK = sendCmdGetResponseWithRetries(write,sizeof(write),resp,sizeof(resp),5); + //Dbprintf("reply [%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x]",resp[0],resp[1],resp[2],resp[3],resp[4],resp[5],resp[6],resp[7],resp[8],resp[9]); + if (isOK) { + isOK = iClass_ReadBlock(blockNo, keyType, readblockdata); + //try again + if (!isOK || (readblockdata[0] == 0xBB || readblockdata[7] == 0xBB || readblockdata[2] == 0xBB)) + isOK = iClass_ReadBlock(blockNo, keyType, readblockdata); + } + if (isOK) { + //Dbprintf("read block [%02x] [%02x%02x%02x%02x%02x%02x%02x%02x]",blockNo,readblockdata[0],readblockdata[1],readblockdata[2],readblockdata[3],readblockdata[4],readblockdata[5],readblockdata[6],readblockdata[7]); + if (memcmp(write+2,readblockdata,sizeof(readblockdata)) != 0){ + isOK=false; + } + } + return isOK; +} + +void iClass_WriteBlock(uint8_t blockNo, uint8_t keyType, uint8_t *data) { + bool isOK = iClass_WriteBlock_ext(blockNo, keyType, data); + if (isOK){ + Dbprintf("Write block [%02x] successful",blockNo); + } else { + Dbprintf("Write block [%02x] failed",blockNo); + } + cmd_send(CMD_ACK,isOK,0,0,0,0); +} + +void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t keyType, uint8_t *data) { + int i; + int written = 0; + int total_block = (endblock - startblock) + 1; + for (i = 0; i < total_block;i++){ + // block number + if (iClass_WriteBlock_ext(i+startblock, keyType, data+(i*12))){ + Dbprintf("Write block [%02x] successful",i + startblock); + written++; + } else { + if (iClass_WriteBlock_ext(i+startblock, keyType, data+(i*12))){ + Dbprintf("Write block [%02x] successful",i + startblock); + written++; + } else { + Dbprintf("Write block [%02x] failed",i + startblock); + } + } + } + if (written == total_block) + Dbprintf("Clone complete"); + else + Dbprintf("Clone incomplete"); + + cmd_send(CMD_ACK,1,0,0,0,0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +} diff --git a/client/cmdhf.c b/client/cmdhf.c index f8daff7e..8406fe76 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -557,16 +557,16 @@ int CmdHFSearch(const char *Cmd){ PrintAndLog("\nValid ISO14443A Tag Found - Quiting Search\n"); return ans; } - ans = HF14BInfo(false); - if (ans) { - PrintAndLog("\nValid ISO14443B Tag Found - Quiting Search\n"); - return ans; - } ans = HFiClassReader("", false, false); if (ans) { PrintAndLog("\nValid iClass Tag (or PicoPass Tag) Found - Quiting Search\n"); return ans; } + ans = HF14BInfo(false); + if (ans) { + PrintAndLog("\nValid ISO14443B Tag Found - Quiting Search\n"); + return ans; + } ans = HF15Reader("", false); if (ans) { PrintAndLog("\nValid ISO15693 Tag Found - Quiting Search\n"); diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 824aaa36..db3de205 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -31,9 +31,28 @@ #include "loclass/fileutils.h" #include "protocols.h" #include "usb_cmd.h" +#include "cmdhfmfu.h" + +#define llX PRIx64 static int CmdHelp(const char *Cmd); +#define ICLASS_KEYS_MAX 8 +static uint8_t iClass_Key_Table[ICLASS_KEYS_MAX][8] = { + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 } +}; + +typedef struct iclass_block { + uint8_t d[8]; +} iclass_block_t; + int xorbits_8(uint8_t val) { uint8_t res = val ^ (val >> 1); //1st pass @@ -170,10 +189,11 @@ int HFiClassReader(const char *Cmd, bool loop, bool verbose) bool tagFound = false; UsbCommand c = {CMD_READER_ICLASS, {FLAG_ICLASS_READER_CSN| FLAG_ICLASS_READER_CONF|FLAG_ICLASS_READER_AA}}; - if (!loop) c.arg[0] |= FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_ONE_TRY; - SendCommand(&c); + // loop in client not device - else on windows have a communication error + c.arg[0] |= FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_ONE_TRY; UsbCommand resp; while(!ukbhit()){ + SendCommand(&c); if (WaitForResponseTimeout(CMD_ACK,&resp, 4500)) { uint8_t readStatus = resp.arg[0] & 0xff; uint8_t *data = resp.d.asBytes; @@ -200,7 +220,6 @@ int HFiClassReader(const char *Cmd, bool loop, bool verbose) if (!loop) break; } return 0; - } int CmdHFiClassReader(const char *Cmd) @@ -231,6 +250,601 @@ int CmdHFiClassReader_Replay(const char *Cmd) return 0; } +int hf_iclass_eload_usage() +{ + PrintAndLog("Loads iclass tag-dump into emulator memory on device"); + PrintAndLog("Usage: hf iclass eload f "); + PrintAndLog(""); + PrintAndLog("Example: hf iclass eload f iclass_tagdump-aa162d30f8ff12f1.bin"); + return 0; +} + +int iclassEmlSetMem(uint8_t *data, int blockNum, int blocksCount) { + UsbCommand c = {CMD_MIFARE_EML_MEMSET, {blockNum, blocksCount, 0}}; + memcpy(c.d.asBytes, data, blocksCount * 16); + SendCommand(&c); + return 0; +} +int CmdHFiClassELoad(const char *Cmd) +{ + + char opt = param_getchar(Cmd, 0); + if (strlen(Cmd)<1 || opt == 'h') + return hf_iclass_eload_usage(); + + //File handling and reading + FILE *f; + char filename[FILE_PATH_SIZE]; + if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0) + { + f = fopen(filename, "rb"); + }else{ + return hf_iclass_eload_usage(); + } + + if(!f) { + PrintAndLog("Failed to read from file '%s'", filename); + return 1; + } + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + uint8_t *dump = malloc(fsize); + + + size_t bytes_read = fread(dump, 1, fsize, f); + fclose(f); + + printIclassDumpInfo(dump); + //Validate + + if (bytes_read < fsize) + { + prnlog("Error, could only read %d bytes (should be %d)",bytes_read, fsize ); + free(dump); + return 1; + } + //Send to device + uint32_t bytes_sent = 0; + uint32_t bytes_remaining = bytes_read; + + while(bytes_remaining > 0){ + uint32_t bytes_in_packet = MIN(USB_CMD_DATA_SIZE, bytes_remaining); + UsbCommand c = {CMD_ICLASS_EML_MEMSET, {bytes_sent,bytes_in_packet,0}}; + memcpy(c.d.asBytes, dump, bytes_in_packet); + SendCommand(&c); + bytes_remaining -= bytes_in_packet; + bytes_sent += bytes_in_packet; + } + free(dump); + PrintAndLog("Sent %d bytes of data to device emulator memory", bytes_sent); + return 0; +} + +int readKeyfile(const char *filename, size_t len, uint8_t* buffer) +{ + FILE *f = fopen(filename, "rb"); + if(!f) { + PrintAndLog("Failed to read from file '%s'", filename); + return 1; + } + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + size_t bytes_read = fread(buffer, 1, len, f); + fclose(f); + if(fsize != len) + { + PrintAndLog("Warning, file size is %d, expected %d", fsize, len); + return 1; + } + if(bytes_read != len) + { + PrintAndLog("Warning, could only read %d bytes, expected %d" ,bytes_read, len); + return 1; + } + return 0; +} + +int usage_hf_iclass_decrypt() +{ + PrintAndLog("Usage: hf iclass decrypt f o "); + PrintAndLog(""); + PrintAndLog("OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside"); + PrintAndLog("in the working directory. The file should be 16 bytes binary data"); + PrintAndLog(""); + PrintAndLog("example: hf iclass decrypt f tagdump_12312342343.bin"); + PrintAndLog(""); + PrintAndLog("OBS! This is pretty stupid implementation, it tries to decrypt every block after block 6. "); + PrintAndLog("Correct behaviour would be to decrypt only the application areas where the key is valid,"); + PrintAndLog("which is defined by the configuration block."); + return 1; +} + +int CmdHFiClassDecrypt(const char *Cmd) +{ + uint8_t key[16] = { 0 }; + if(readKeyfile("iclass_decryptionkey.bin", 16, key)) + { + usage_hf_iclass_decrypt(); + return 1; + } + PrintAndLog("Decryption file found... "); + char opt = param_getchar(Cmd, 0); + if (strlen(Cmd)<1 || opt == 'h') + return usage_hf_iclass_decrypt(); + + //Open the tagdump-file + FILE *f; + char filename[FILE_PATH_SIZE]; + if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0) + { + f = fopen(filename, "rb"); + }else{ + return usage_hf_iclass_decrypt(); + } + + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + uint8_t enc_dump[8] = {0}; + uint8_t *decrypted = malloc(fsize); + des3_context ctx = { DES_DECRYPT ,{ 0 } }; + des3_set2key_dec( &ctx, key); + size_t bytes_read = fread(enc_dump, 1, 8, f); + + //Use the first block (CSN) for filename + char outfilename[FILE_PATH_SIZE] = { 0 }; + snprintf(outfilename,FILE_PATH_SIZE,"iclass_tagdump-%02x%02x%02x%02x%02x%02x%02x%02x-decrypted", + enc_dump[0],enc_dump[1],enc_dump[2],enc_dump[3], + enc_dump[4],enc_dump[5],enc_dump[6],enc_dump[7]); + + size_t blocknum =0; + while(bytes_read == 8) + { + if(blocknum < 7) + { + memcpy(decrypted+(blocknum*8), enc_dump, 8); + }else{ + des3_crypt_ecb(&ctx, enc_dump,decrypted +(blocknum*8) ); + } + printvar("decrypted block", decrypted +(blocknum*8), 8); + bytes_read = fread(enc_dump, 1, 8, f); + blocknum++; + } + fclose(f); + + saveFile(outfilename,"bin", decrypted, blocknum*8); + + return 0; +} + +int usage_hf_iclass_encrypt(){ + PrintAndLog("Usage: hf iclass encrypt "); + PrintAndLog(""); + PrintAndLog("OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside"); + PrintAndLog("in the working directory. The file should be 16 bytes binary data"); + PrintAndLog(""); + PrintAndLog("example: hf iclass encrypt 0102030405060708"); + PrintAndLog(""); + return 0; +} + +int iClassEncryptBlkData(uint8_t *blkData) +{ + uint8_t key[16] = { 0 }; + if(readKeyfile("iclass_decryptionkey.bin", 16, key)) + { + usage_hf_iclass_encrypt(); + return 1; + } + PrintAndLog("Decryption file found... "); + + uint8_t decryptedData[16]; + uint8_t *decrypted = decryptedData; + des3_context ctx = { DES_DECRYPT ,{ 0 } }; + des3_set2key_enc( &ctx, key); + + des3_crypt_ecb(&ctx, blkData,decrypted); + //printvar("decrypted block", decrypted, 8); + memcpy(blkData,decrypted,8); + + return 1; +} + +int CmdHFiClassEncryptBlk(const char *Cmd) +{ + uint8_t blkData[8] = {0}; + char opt = param_getchar(Cmd, 0); + if (strlen(Cmd)<1 || opt == 'h') + return usage_hf_iclass_encrypt(); + + //get the bytes to encrypt + if (param_gethex(Cmd, 0, blkData, 16)) + { + PrintAndLog("BlockData must include 16 HEX symbols"); + return 0; + } + if (!iClassEncryptBlkData(blkData)) return 0; + + printvar("encrypted block", blkData, 8); + return 1; +} + +void Calc_wb_mac(uint8_t blockno,uint8_t *data,uint8_t *div_key,uint8_t MAC[4]){ + uint8_t WB[9]; + WB[0] = blockno; + memcpy(WB + 1,data,8); + doMAC_N(WB,sizeof(WB),div_key,MAC); + //printf("Cal wb mac block [%02x][%02x%02x%02x%02x%02x%02x%02x%02x] : MAC [%02x%02x%02x%02x]",WB[0],WB[1],WB[2],WB[3],WB[4],WB[5],WB[6],WB[7],WB[8],MAC[0],MAC[1],MAC[2],MAC[3]); +} + +static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool use_credit_key, bool verbose){ + UsbCommand resp; + + UsbCommand c = {CMD_READER_ICLASS, {0}}; + c.arg[0] = FLAG_ICLASS_READER_ONLY_ONCE| FLAG_ICLASS_READER_CC; + if (use_credit_key) + c.arg[0] |= FLAG_ICLASS_READER_CEDITKEY; + + clearCommandBuffer(); + SendCommand(&c); + if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) + { + PrintAndLog("Command execute timeout"); + return false; + } + + uint8_t isOK = resp.arg[0] & 0xff; + uint8_t *data = resp.d.asBytes; + + memcpy(CSN,data,8); + if (CCNR!=NULL)memcpy(CCNR,data+16,8); + //PrintAndLog("isOk:%02x", isOK); + if(isOK > 0) + { + if (verbose) PrintAndLog("CSN: %s",sprint_hex(CSN,8)); + } + if(isOK <= 1){ + PrintAndLog("Failed to obtain CC! Aborting"); + return false; + } + return true; +} + +static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool use_credit_key, bool elite, bool verbose) { + uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t keytable[128] = {0}; + uint8_t *used_key; + uint8_t key_sel[8] = {0}; + uint8_t key_sel_p[8] = { 0 }; + + if (!select_only(CSN, CCNR, use_credit_key, verbose)) + return false; + + if(elite) { + hash2(KEY, keytable); + //Get the key index (hash1) + uint8_t key_index[8] = {0}; + hash1(CSN, key_index); + //printvar("hash1", key_index,8); + for(uint8_t i = 0; i < 8 ; i++) + key_sel[i] = keytable[key_index[i]] & 0xFF; + //PrintAndLog("Pre-fortified 'permuted' HS key that would be needed by an iclass reader to talk to above CSN:"); + //printvar("k_sel", key_sel,8); + //Permute from iclass format to standard format + permutekey_rev(key_sel, key_sel_p); + used_key = key_sel_p; + } else { + used_key = KEY; + } + //PrintAndLog("Pre-fortified key that would be needed by the OmniKey reader to talk to above CSN:"); + //printvar("Used key",KEY,8); + diversifyKey(CSN, used_key, div_key); + //PrintAndLog("Hash0, a.k.a diversified key, that is computed using Ksel and stored in the card (Block 3):"); + //printvar("Div key", div_key, 8); + //printvar("CC_NR:",CCNR,8); + doMAC(CCNR, div_key, MAC); + //printvar("MAC", MAC, 4); + UsbCommand resp; + UsbCommand d = {CMD_ICLASS_AUTHENTICATION, {0}}; + memcpy(d.d.asBytes, MAC, 4); + clearCommandBuffer(); + SendCommand(&d); + if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) + { + PrintAndLog("Auth Command execute timeout"); + return false; + } + uint8_t isOK = resp.arg[0] & 0xff; + if (!isOK) { + PrintAndLog("Authentication error"); + return false; + } + return true; +} + +int usage_hf_iclass_dump(){ + PrintAndLog("Usage: hf iclass dump f k c e\n"); + PrintAndLog("Options:"); + PrintAndLog(" f : specify a filename to save dump to"); + PrintAndLog(" k : *Access Key as 16 hex symbols or 1 hex to select key from memory"); + PrintAndLog(" c : Credit Key as 16 hex symbols or 1 hex to select key from memory"); + PrintAndLog(" e : If 'e' is specified, the key is interpreted as the 16 byte"); + PrintAndLog(" Custom Key (KCus), which can be obtained via reader-attack"); + PrintAndLog(" See 'hf iclass sim 2'. This key should be on iclass-format"); + PrintAndLog(" NOTE: * = required"); + PrintAndLog("Samples:"); + PrintAndLog(" hf iclass dump k 001122334455667B"); + PrintAndLog(" hf iclass dump k AAAAAAAAAAAAAAAA c 001122334455667B"); + PrintAndLog(" hf iclass dump k AAAAAAAAAAAAAAAA e"); + return 0; +} + +int CmdHFiClassReader_Dump(const char *Cmd){ + + uint8_t MAC[4] = {0x00,0x00,0x00,0x00}; + uint8_t div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t c_div_key[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t blockno = 0; + uint8_t numblks = 0; + uint8_t maxBlk = 32; + uint8_t KEY[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CreditKEY[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t keyNbr = 0; + uint8_t dataLen = 0; + uint8_t fileNameLen = 0; + char filename[FILE_PATH_SIZE]={0}; + char tempStr[50] = {0}; + bool have_credit_key = false; + bool elite = false; + bool errors = false; + uint8_t cmdp = 0; + + while(param_getchar(Cmd, cmdp) != 0x00) + { + switch(param_getchar(Cmd, cmdp)) + { + case 'h': + case 'H': + return usage_hf_iclass_dump(); + case 'c': + case 'C': + have_credit_key = true; + dataLen = param_getstr(Cmd, cmdp+1, tempStr); + if (dataLen == 16) { + errors = param_gethex(tempStr, 0, CreditKEY, dataLen); + } else if (dataLen == 1) { + keyNbr = param_get8(Cmd, cmdp+1); + if (keyNbr <= ICLASS_KEYS_MAX) { + memcpy(CreditKEY, iClass_Key_Table[keyNbr], 8); + } else { + PrintAndLog("\nERROR: Credit KeyNbr is invalid\n"); + errors = true; + } + } else { + PrintAndLog("\nERROR: Credit Key is incorrect length\n"); + errors = true; + } + cmdp += 2; + break; + case 'e': + case 'E': + elite = true; + cmdp++; + break; + case 'f': + case 'F': + fileNameLen = param_getstr(Cmd, cmdp+1, filename); + if (fileNameLen < 1) { + PrintAndLog("No filename found after f"); + errors = true; + } + cmdp += 2; + break; + case 'k': + case 'K': + dataLen = param_getstr(Cmd, cmdp+1, tempStr); + if (dataLen == 16) { + errors = param_gethex(tempStr, 0, KEY, dataLen); + } else if (dataLen == 1) { + keyNbr = param_get8(Cmd, cmdp+1); + if (keyNbr <= ICLASS_KEYS_MAX) { + memcpy(KEY, iClass_Key_Table[keyNbr], 8); + } else { + PrintAndLog("\nERROR: Credit KeyNbr is invalid\n"); + errors = true; + } + } else { + PrintAndLog("\nERROR: Credit Key is incorrect length\n"); + errors = true; + } + cmdp += 2; + break; + default: + PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + if(errors) return usage_hf_iclass_dump(); + } + + if (cmdp < 2) return usage_hf_iclass_dump(); + + //get config and first 3 blocks + UsbCommand c = {CMD_READER_ICLASS, {FLAG_ICLASS_READER_CSN | + FLAG_ICLASS_READER_CONF | FLAG_ICLASS_READER_ONLY_ONCE | FLAG_ICLASS_READER_ONE_TRY}}; + UsbCommand resp; + uint8_t tag_data[255*8]; + clearCommandBuffer(); + SendCommand(&c); + if (WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { + uint8_t readStatus = resp.arg[0] & 0xff; + uint8_t *data = resp.d.asBytes; + + if( readStatus == 0){ + //Aborted + PrintAndLog("No tag found..."); + return 0; + } + if( readStatus & (FLAG_ICLASS_READER_CSN|FLAG_ICLASS_READER_CONF|FLAG_ICLASS_READER_CC)){ + memcpy(tag_data, data, 8*3); + /*for (; blockno < 3; blockno++) { + PrintAndLog(" | %02X | %02X%02X%02X%02X%02X%02X%02X%02X |", blockno, + data[(blockno*8)+0],data[(blockno*8)+1],data[(blockno*8)+2],data[(blockno*8)+3], + data[(blockno*8)+4],data[(blockno*8)+5],data[(blockno*8)+6],data[(blockno*8)+7]); + }*/ + blockno+=2; + numblks = data[8]; + + if (data[13] & 0x80) { + // large memory - not able to dump pages currently + maxBlk = 255; + } else { + maxBlk = 32; + } + //PrintAndLog("maxBlk: %02X",maxBlk); + } + } else { + PrintAndLog("Command execute timeout"); + return 0; + } + + if (!select_and_auth(KEY, MAC, div_key, false, elite, false)){ + //try twice - for some reason it sometimes fails the first time... + if (!select_and_auth(KEY, MAC, div_key, false, elite, false)){ + ul_switch_off_field(); + return 0; + } + } + //print debit div_key + //PrintAndLog(" | 03 | %02X%02X%02X%02X%02X%02X%02X%02X |", + // div_key[0],div_key[1],div_key[2],div_key[3], + // div_key[4],div_key[5],div_key[6],div_key[7]); + + if (have_credit_key){ + //turn off hf field + //PrintAndLog("attempt 1 to auth with credit key"); + ul_switch_off_field(); + memset(c_div_key,0,8); + memset(MAC,0,4); + if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false)){ + //try twice - for some reason it sometimes fails the first time... + memset(c_div_key,0,8); + memset(MAC,0,4); + if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false)){ + ul_switch_off_field(); + return 0; + } + } + //print credit div_key + //PrintAndLog(" | 04 | %02X%02X%02X%02X%02X%02X%02X%02X |", + // div_key[0],div_key[1],div_key[2],div_key[3], + // div_key[4],div_key[5],div_key[6],div_key[7]); + + //turn off hf field + //PrintAndLog("attempt 2 to auth with debit key"); + ul_switch_off_field(); + memset(div_key,0,8); + memset(MAC,0,4); + if (!select_and_auth(KEY, MAC, div_key, false, elite, false)){ + //try twice - for some reason it sometimes fails the first time... + if (!select_and_auth(KEY, MAC, div_key, false, elite, false)){ + ul_switch_off_field(); + return 0; + } + } + } + //PrintAndLog("have %d blocks",blockno); + + UsbCommand w = {CMD_ICLASS_DUMP, {blockno, numblks-blockno+1, 0x88}}; + clearCommandBuffer(); + SendCommand(&w); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { + PrintAndLog("Command execute time-out 1"); + return 1; + } + uint32_t blocksRead = resp.arg[1]; + uint8_t isOK = resp.arg[0] & 0xff; + if (!isOK && !blocksRead) { + PrintAndLog("Read Block Failed"); + return 0; + } + uint32_t startindex = resp.arg[2]; + if (blocksRead*8 > sizeof(tag_data)-(blockno*8)) { + PrintAndLog("Data exceeded Buffer size!"); + blocksRead = (sizeof(tag_data)/8) - blockno; + } + //PrintAndLog("blocksread: %d, blockno: %d, startindex: %x",blocksRead,blockno,startindex); + GetFromBigBuf(tag_data+(blockno*8), blocksRead*8, startindex); + WaitForResponse(CMD_ACK,NULL); + uint32_t gotBytes = blocksRead*8 + blockno*8; + + //PrintAndLog("have %d blocks",gotBytes/8); + + if (have_credit_key && maxBlk > blockno+numblks+1) { + //turn off hf field + ul_switch_off_field(); + //PrintAndLog("attempt 2 to auth with credit key"); + memset(c_div_key,0,8); + memset(MAC,0,4); + if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false)){ + //try twice - for some reason it sometimes fails the first time... + if (!select_and_auth(CreditKEY, MAC, c_div_key, true, false, false)){ + ul_switch_off_field(); + return 0; + } + } + w.arg[0] = blockno + blocksRead; + w.arg[1] = maxBlk - (blockno + blocksRead); + w.arg[2] = 0x18; + clearCommandBuffer(); + SendCommand(&w); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 4500)) { + PrintAndLog("Command execute timeout 2"); + return 0; + } + uint8_t isOK = resp.arg[0] & 0xff; + blocksRead = resp.arg[1]; + if (!isOK && !blocksRead) { + PrintAndLog("Read Block Failed 2"); + return 0; + } + + startindex = resp.arg[2]; + if (blocksRead*8 > sizeof(tag_data)-gotBytes) { + PrintAndLog("Data exceeded Buffer size!"); + blocksRead = (sizeof(tag_data) - gotBytes)/8; + } + GetFromBigBuf(tag_data+gotBytes, blocksRead*8, startindex); + WaitForResponse(CMD_ACK,NULL); + + gotBytes += blocksRead*8; + //PrintAndLog("have %d blocks",gotBytes/8); + } + + memcpy(tag_data+(3*8),div_key,8); + memcpy(tag_data+(4*8),c_div_key,8); + + for (blockno = 0; blockno < gotBytes/8; blockno++){ + PrintAndLog(" | %02X | %02X%02X%02X%02X%02X%02X%02X%02X |", blockno, + tag_data[(blockno*8)+0],tag_data[(blockno*8)+1],tag_data[(blockno*8)+2],tag_data[(blockno*8)+3], + tag_data[(blockno*8)+4],tag_data[(blockno*8)+5],tag_data[(blockno*8)+6],tag_data[(blockno*8)+7]); + } + if (filename[0] == 0){ + snprintf(filename, FILE_PATH_SIZE,"iclass_tagdump-%02x%02x%02x%02x%02x%02x%02x%02x", + tag_data[0],tag_data[1],tag_data[2],tag_data[3], + tag_data[4],tag_data[5],tag_data[6],tag_data[7]); + } + saveFile(filename,"bin",tag_data, gotBytes ); + PrintAndLog("Saving dump file - %d blocks read", gotBytes/8); + return 1; +} + +/* int CmdHFiClassReader_Dump(const char *Cmd) { uint8_t readerType = 0; @@ -238,23 +852,15 @@ int CmdHFiClassReader_Dump(const char *Cmd) uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - //uint8_t CC_temp[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; uint8_t keytable[128] = {0}; - int elite = 0; + bool elite = false; + bool use_credit_key = false; uint8_t *used_key; int i; - if (strlen(Cmd)<1) + if (strlen(Cmd) < 1) { - PrintAndLog("Usage: hf iclass dump [e]"); - PrintAndLog(" Key - A 16 byte master key"); - PrintAndLog(" e - If 'e' is specified, the key is interpreted as the 16 byte"); - PrintAndLog(" Custom Key (KCus), which can be obtained via reader-attack"); - PrintAndLog(" See 'hf iclass sim 2'. This key should be on iclass-format"); - PrintAndLog(" sample: hf iclass dump 0011223344556677"); - - - return 0; + return usage_hf_iclass_dump(); } if (param_gethex(Cmd, 0, KEY, 16)) @@ -266,12 +872,16 @@ int CmdHFiClassReader_Dump(const char *Cmd) if (param_getchar(Cmd, 1) == 'e') { PrintAndLog("Elite switch on"); - elite = 1; + elite = true; //calc h2 hash2(KEY, keytable); printarr_human_readable("keytable", keytable, 128); + if (param_getchar(Cmd, 2) == 'c') + use_credit_key = true; + } else if (param_getchar(Cmd, 1) == 'c'){ + use_credit_key = true; } UsbCommand resp; @@ -280,10 +890,11 @@ int CmdHFiClassReader_Dump(const char *Cmd) UsbCommand c = {CMD_READER_ICLASS, {0}}; c.arg[0] = FLAG_ICLASS_READER_ONLY_ONCE| FLAG_ICLASS_READER_CC; + if (use_credit_key) + c.arg[0] |= FLAG_ICLASS_READER_CEDITKEY; + clearCommandBuffer(); SendCommand(&c); - - if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) { PrintAndLog("Command execute timeout"); @@ -383,260 +994,406 @@ int CmdHFiClassReader_Dump(const char *Cmd) } } } - - return 0; } +*/ -int hf_iclass_eload_usage() -{ - PrintAndLog("Loads iclass tag-dump into emulator memory on device"); - PrintAndLog("Usage: hf iclass eload f "); - PrintAndLog(""); - PrintAndLog("Example: hf iclass eload f iclass_tagdump-aa162d30f8ff12f1.bin"); - return 0; +int WriteBlock(uint8_t blockno, uint8_t *bldata, uint8_t *KEY, bool use_credit_key, bool elite, bool verbose){ + uint8_t MAC[4]={0x00,0x00,0x00,0x00}; + uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t keyType = (use_credit_key) ? 0x18 : 0x88; + if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, verbose)) + return 0; -} + UsbCommand resp; -int iclassEmlSetMem(uint8_t *data, int blockNum, int blocksCount) { - UsbCommand c = {CMD_MIFARE_EML_MEMSET, {blockNum, blocksCount, 0}}; - memcpy(c.d.asBytes, data, blocksCount * 16); - SendCommand(&c); - return 0; -} -int CmdHFiClassELoad(const char *Cmd) -{ - - char opt = param_getchar(Cmd, 0); - if (strlen(Cmd)<1 || opt == 'h') - return hf_iclass_eload_usage(); - - //File handling and reading - FILE *f; - char filename[FILE_PATH_SIZE]; - if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0) + Calc_wb_mac(blockno,bldata,div_key,MAC); + UsbCommand w = {CMD_ICLASS_WRITEBLOCK, {blockno, keyType}}; + memcpy(w.d.asBytes, bldata, 8); + memcpy(w.d.asBytes + 8,MAC, 4); + + clearCommandBuffer(); + SendCommand(&w); + if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) { - f = fopen(filename, "rb"); - }else{ - return hf_iclass_eload_usage(); + PrintAndLog("Write Command execute timeout"); + return 0; } - - if(!f) { - PrintAndLog("Failed to read from file '%s'", filename); - return 1; + uint8_t isOK = resp.arg[0] & 0xff; + if (!isOK) { + PrintAndLog("Write Block Failed"); + return 0; } + PrintAndLog("Write Block Successful"); - fseek(f, 0, SEEK_END); - long fsize = ftell(f); - fseek(f, 0, SEEK_SET); - - uint8_t *dump = malloc(fsize); - - - size_t bytes_read = fread(dump, 1, fsize, f); - fclose(f); - - printIclassDumpInfo(dump); - //Validate - - if (bytes_read < fsize) - { - prnlog("Error, could only read %d bytes (should be %d)",bytes_read, fsize ); - free(dump); - return 1; - } - //Send to device - uint32_t bytes_sent = 0; - uint32_t bytes_remaining = bytes_read; - - while(bytes_remaining > 0){ - uint32_t bytes_in_packet = MIN(USB_CMD_DATA_SIZE, bytes_remaining); - UsbCommand c = {CMD_ICLASS_EML_MEMSET, {bytes_sent,bytes_in_packet,0}}; - memcpy(c.d.asBytes, dump, bytes_in_packet); - SendCommand(&c); - bytes_remaining -= bytes_in_packet; - bytes_sent += bytes_in_packet; - } - free(dump); - PrintAndLog("Sent %d bytes of data to device emulator memory", bytes_sent); - return 0; -} - -int usage_hf_iclass_decrypt() -{ - PrintAndLog("Usage: hf iclass decrypt f o "); - PrintAndLog(""); - PrintAndLog("OBS! In order to use this function, the file 'iclass_decryptionkey.bin' must reside"); - PrintAndLog("in the working directory. The file should be 16 bytes binary data"); - PrintAndLog(""); - PrintAndLog("example: hf iclass decrypt f tagdump_12312342343.bin"); - PrintAndLog(""); - PrintAndLog("OBS! This is pretty stupid implementation, it tries to decrypt every block after block 6. "); - PrintAndLog("Correct behaviour would be to decrypt only the application areas where the key is valid,"); - PrintAndLog("which is defined by the configuration block."); return 1; } -int readKeyfile(const char *filename, size_t len, uint8_t* buffer) -{ - FILE *f = fopen(filename, "rb"); +int usage_hf_iclass_writeblock() { + PrintAndLog("Options:"); + PrintAndLog(" b : The block number as 2 hex symbols"); + PrintAndLog(" d : Set the Data to write as 16 hex symbols"); + PrintAndLog(" k : Access Key as 16 hex symbols or 1 hex to select key from memory"); + PrintAndLog(" c : If 'c' is specified, the key set is assumed to be the credit key\n"); + PrintAndLog(" e : If 'e' is specified, elite computations applied to key"); + PrintAndLog("Samples:"); + PrintAndLog(" hf iclass writeblk b 0A d AAAAAAAAAAAAAAAA k 001122334455667B"); + PrintAndLog(" hf iclass writeblk b 1B d AAAAAAAAAAAAAAAA k 001122334455667B c"); + PrintAndLog(" hf iclass writeblk b 0A d AAAAAAAAAAAAAAAA n 0"); + return 0; +} + +int CmdHFiClass_WriteBlock(const char *Cmd) { + uint8_t blockno=0; + uint8_t bldata[8]={0}; + uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t keyNbr = 0; + uint8_t dataLen = 0; + char tempStr[50] = {0}; + bool use_credit_key = false; + bool elite = false; + bool errors = false; + uint8_t cmdp = 0; + while(param_getchar(Cmd, cmdp) != 0x00) + { + switch(param_getchar(Cmd, cmdp)) + { + case 'h': + case 'H': + return usage_hf_iclass_writeblock(); + case 'b': + case 'B': + if (param_gethex(Cmd, cmdp+1, &blockno, 2)) { + PrintAndLog("Block No must include 2 HEX symbols\n"); + errors = true; + } + cmdp += 2; + break; + case 'c': + case 'C': + use_credit_key = true; + cmdp++; + break; + case 'd': + case 'D': + if (param_gethex(Cmd, cmdp+1, bldata, 16)) + { + PrintAndLog("KEY must include 16 HEX symbols\n"); + errors = true; + } + cmdp += 2; + break; + case 'e': + case 'E': + elite = true; + cmdp++; + break; + case 'k': + case 'K': + dataLen = param_getstr(Cmd, cmdp+1, tempStr); + if (dataLen == 16) { + errors = param_gethex(tempStr, 0, KEY, dataLen); + } else if (dataLen == 1) { + keyNbr = param_get8(Cmd, cmdp+1); + if (keyNbr <= ICLASS_KEYS_MAX) { + memcpy(KEY, iClass_Key_Table[keyNbr], 8); + } else { + PrintAndLog("\nERROR: Credit KeyNbr is invalid\n"); + errors = true; + } + } else { + PrintAndLog("\nERROR: Credit Key is incorrect length\n"); + errors = true; + } + cmdp += 2; + break; + default: + PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + if(errors) return usage_hf_iclass_writeblock(); + } + + if (cmdp < 6) return usage_hf_iclass_writeblock(); + + return WriteBlock(blockno, bldata, KEY, use_credit_key, elite, true); +} + +int usage_hf_iclass_clone() { + PrintAndLog("Usage: hf iclass clone f b l k e c"); + PrintAndLog("Options:"); + PrintAndLog(" f : specify a filename to clone from"); + PrintAndLog(" b : The first block to clone as 2 hex symbols"); + PrintAndLog(" l : Set the Data to write as 16 hex symbols"); + PrintAndLog(" k : Access Key as 16 hex symbols or 1 hex to select key from memory"); + PrintAndLog(" c : If 'c' is specified, the key set is assumed to be the credit key\n"); + PrintAndLog(" e : If 'e' is specified, elite computations applied to key"); + PrintAndLog("Samples:"); + PrintAndLog(" hf iclass clone f iclass_tagdump-121345.bin b 06 l 1A k 1122334455667788 e"); + PrintAndLog(" hf iclass clone f iclass_tagdump-121345.bin b 05 l 19 k 0"); + PrintAndLog(" hf iclass clone f iclass_tagdump-121345.bin b 06 l 19 k 0 e"); + return -1; +} + +int CmdHFiClassCloneTag(const char *Cmd) { + char filename[FILE_PATH_SIZE]; + char tempStr[50]={0}; + uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t keyNbr = 0; + uint8_t fileNameLen = 0; + uint8_t startblock = 0; + uint8_t endblock = 0; + uint8_t dataLen = 0; + bool use_credit_key = false; + bool elite = false; + bool errors = false; + uint8_t cmdp = 0; + while(param_getchar(Cmd, cmdp) != 0x00) + { + switch(param_getchar(Cmd, cmdp)) + { + case 'h': + case 'H': + return usage_hf_iclass_clone(); + case 'b': + case 'B': + if (param_gethex(Cmd, cmdp+1, &startblock, 2)) { + PrintAndLog("Start Block No must include 2 HEX symbols\n"); + errors = true; + } + cmdp += 2; + break; + case 'c': + case 'C': + use_credit_key = true; + cmdp++; + break; + case 'e': + case 'E': + elite = true; + cmdp++; + break; + case 'f': + case 'F': + fileNameLen = param_getstr(Cmd, cmdp+1, filename); + if (fileNameLen < 1) { + PrintAndLog("No filename found after f"); + errors = true; + } + cmdp += 2; + break; + case 'k': + case 'K': + dataLen = param_getstr(Cmd, cmdp+1, tempStr); + if (dataLen == 16) { + errors = param_gethex(tempStr, 0, KEY, dataLen); + } else if (dataLen == 1) { + keyNbr = param_get8(Cmd, cmdp+1); + if (keyNbr <= ICLASS_KEYS_MAX) { + memcpy(KEY, iClass_Key_Table[keyNbr], 8); + } else { + PrintAndLog("\nERROR: Credit KeyNbr is invalid\n"); + errors = true; + } + } else { + PrintAndLog("\nERROR: Credit Key is incorrect length\n"); + errors = true; + } + cmdp += 2; + break; + case 'l': + case 'L': + if (param_gethex(Cmd, cmdp+1, &endblock, 2)) { + PrintAndLog("Start Block No must include 2 HEX symbols\n"); + errors = true; + } + cmdp += 2; + break; + default: + PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + if(errors) return usage_hf_iclass_clone(); + } + + if (cmdp < 8) return usage_hf_iclass_clone(); + + FILE *f; + + iclass_block_t tag_data[USB_CMD_DATA_SIZE/12]; + + if ((endblock-startblock+1)*12 > USB_CMD_DATA_SIZE) { + PrintAndLog("Trying to write too many blocks at once. Max: %d", USB_CMD_DATA_SIZE/8); + } + // file handling and reading + f = fopen(filename,"rb"); if(!f) { PrintAndLog("Failed to read from file '%s'", filename); return 1; } - fseek(f, 0, SEEK_END); - long fsize = ftell(f); - fseek(f, 0, SEEK_SET); - size_t bytes_read = fread(buffer, 1, len, f); - fclose(f); - if(fsize != len) - { - PrintAndLog("Warning, file size is %d, expected %d", fsize, len); - return 1; - } - if(bytes_read != len) - { - PrintAndLog("Warning, could only read %d bytes, expected %d" ,bytes_read, len); - return 1; - } - return 0; -} -int CmdHFiClassDecrypt(const char *Cmd) -{ - uint8_t key[16] = { 0 }; - if(readKeyfile("iclass_decryptionkey.bin", 16, key)) - { - usage_hf_iclass_decrypt(); - return 1; - } - PrintAndLog("Decryption file found... "); - char opt = param_getchar(Cmd, 0); - if (strlen(Cmd)<1 || opt == 'h') - return usage_hf_iclass_decrypt(); - - //Open the tagdump-file - FILE *f; - char filename[FILE_PATH_SIZE]; - if(opt == 'f' && param_getstr(Cmd, 1, filename) > 0) - { - f = fopen(filename, "rb"); - }else{ - return usage_hf_iclass_decrypt(); - } - - fseek(f, 0, SEEK_END); - long fsize = ftell(f); - fseek(f, 0, SEEK_SET); - uint8_t enc_dump[8] = {0}; - uint8_t *decrypted = malloc(fsize); - des3_context ctx = { DES_DECRYPT ,{ 0 } }; - des3_set2key_dec( &ctx, key); - size_t bytes_read = fread(enc_dump, 1, 8, f); - - //Use the first block (CSN) for filename - char outfilename[FILE_PATH_SIZE] = { 0 }; - snprintf(outfilename,FILE_PATH_SIZE,"iclass_tagdump-%02x%02x%02x%02x%02x%02x%02x%02x-decrypted", - enc_dump[0],enc_dump[1],enc_dump[2],enc_dump[3], - enc_dump[4],enc_dump[5],enc_dump[6],enc_dump[7]); - - size_t blocknum =0; - while(bytes_read == 8) - { - if(blocknum < 7) - { - memcpy(decrypted+(blocknum*8), enc_dump, 8); - }else{ - des3_crypt_ecb(&ctx, enc_dump,decrypted +(blocknum*8) ); - } - printvar("decrypted block", decrypted +(blocknum*8), 8); - bytes_read = fread(enc_dump, 1, 8, f); - blocknum++; - } - fclose(f); - - saveFile(outfilename,"bin", decrypted, blocknum*8); - - return 0; -} - -int CmdHFiClass_iso14443A_write(const char *Cmd) -{ - uint8_t readerType = 0; - uint8_t MAC[4]={0x00,0x00,0x00,0x00}; - uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - - uint8_t blockNo=0; - uint8_t bldata[8]={0}; - - if (strlen(Cmd)<3) - { - PrintAndLog("Usage: hf iclass write "); - PrintAndLog(" sample: hf iclass write 0011223344556677 10 AAAAAAAAAAAAAAAA"); + if (startblock<5) { + PrintAndLog("You cannot write key blocks this way. yet... make your start block > 4"); return 0; } + // now read data from the file from block 6 --- 19 + // ok we will use this struct [data 8 bytes][MAC 4 bytes] for each block calculate all mac number for each data + // then copy to usbcommand->asbytes; the max is 32 - 6 = 24 block 12 bytes each block 288 bytes then we can only accept to clone 21 blocks at the time, + // else we have to create a share memory + int i; + fseek(f,startblock*8,SEEK_SET); + fread(tag_data,sizeof(iclass_block_t),endblock - startblock + 1,f); + /* + for (i = 0; i < endblock - startblock+1; i++){ + printf("block [%02x] [%02x%02x%02x%02x%02x%02x%02x%02x]\n",i + startblock,tag_data[i].d[0],tag_data[i].d[1],tag_data[i].d[2],tag_data[i].d[3],tag_data[i].d[4],tag_data[i].d[5],tag_data[i].d[6],tag_data[i].d[7]); + }*/ - if (param_gethex(Cmd, 0, KEY, 16)) - { - PrintAndLog("KEY must include 16 HEX symbols"); - return 1; - } + uint8_t MAC[4]={0x00,0x00,0x00,0x00}; + uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - blockNo = param_get8(Cmd, 1); - if (blockNo>32) - { - PrintAndLog("Error: Maximum number of blocks is 32 for iClass 2K Cards!"); - return 1; - } - if (param_gethex(Cmd, 2, bldata, 8)) - { - PrintAndLog("Block data must include 8 HEX symbols"); - return 1; - } + if (!select_and_auth(KEY, MAC, div_key, use_credit_key, elite, true)) + return 0; - UsbCommand c = {CMD_ICLASS_ISO14443A_WRITE, {0}}; - SendCommand(&c); + UsbCommand w = {CMD_ICLASS_CLONE,{startblock,endblock,((use_credit_key) ? 0x18 : 0x88)}}; + uint8_t *ptr; + // calculate all mac for every the block we will write + for (i = startblock; i <= endblock; i++){ + Calc_wb_mac(i,tag_data[i - startblock].d,div_key,MAC); + // usb command d start pointer = d + (i - 6) * 12 + // memcpy(pointer,tag_data[i - 6],8) 8 bytes + // memcpy(pointer + 8,mac,sizoof(mac) 4 bytes; + // next one + ptr = w.d.asBytes + (i - startblock) * 12; + memcpy(ptr, &(tag_data[i - startblock].d[0]), 8); + memcpy(ptr + 8,MAC, 4); + } + uint8_t p[12]; + for (i = 0; i <= endblock - startblock;i++){ + memcpy(p,w.d.asBytes + (i * 12),12); + printf("block [%02x]",i + startblock); + printf(" [%02x%02x%02x%02x%02x%02x%02x%02x]",p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7]); + printf(" MAC [%02x%02x%02x%02x]\n",p[8],p[9],p[10],p[11]); + } UsbCommand resp; - - if (WaitForResponseTimeout(CMD_ACK,&resp,4500)) { - uint8_t isOK = resp.arg[0] & 0xff; - uint8_t * data = resp.d.asBytes; - - memcpy(CSN,data,8); - memcpy(CCNR,data+8,8); - PrintAndLog("DEBUG: %s",sprint_hex(CSN,8)); - PrintAndLog("DEBUG: %s",sprint_hex(CCNR,8)); - PrintAndLog("isOk:%02x", isOK); - } else { + SendCommand(&w); + if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) + { PrintAndLog("Command execute timeout"); + return 0; } + return 1; +} - diversifyKey(CSN,KEY, div_key); +int ReadBlock(uint8_t *KEY, uint8_t blockno, uint8_t keyType, bool elite, bool verbose){ + uint8_t MAC[4]={0x00,0x00,0x00,0x00}; + uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - PrintAndLog("Div Key: %s",sprint_hex(div_key,8)); - doMAC(CCNR, div_key, MAC); + if (!select_and_auth(KEY, MAC, div_key, (keyType==0x18), elite, verbose)) + return 0; - UsbCommand c2 = {CMD_ICLASS_ISO14443A_WRITE, {readerType,blockNo}}; - memcpy(c2.d.asBytes, bldata, 8); - memcpy(c2.d.asBytes+8, MAC, 4); - SendCommand(&c2); - - if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { - uint8_t isOK = resp.arg[0] & 0xff; - uint8_t * data = resp.d.asBytes; - - if (isOK) - PrintAndLog("isOk:%02x data:%s", isOK, sprint_hex(data, 4)); - else - PrintAndLog("isOk:%02x", isOK); - } else { + UsbCommand resp; + UsbCommand w = {CMD_ICLASS_READBLOCK, {blockno, keyType}}; + clearCommandBuffer(); + SendCommand(&w); + if (!WaitForResponseTimeout(CMD_ACK,&resp,4500)) + { PrintAndLog("Command execute timeout"); + return 0; } + uint8_t isOK = resp.arg[0] & 0xff; + if (!isOK) { + PrintAndLog("Read Block Failed"); + return 0; + } + //data read is stored in: resp.d.asBytes[0-15] + if (verbose) PrintAndLog("Block %02X: %s\n",blockno, sprint_hex(resp.d.asBytes,8)); + return 1; +} + +int usage_hf_iclass_readblock(){ + PrintAndLog("Usage: hf iclass readblk b k c e\n"); + PrintAndLog("Options:"); + PrintAndLog(" b : The block number as 2 hex symbols"); + PrintAndLog(" k : Access Key as 16 hex symbols or 1 hex to select key from memory"); + PrintAndLog(" c : If 'c' is specified, the key set is assumed to be the credit key\n"); + PrintAndLog(" e : If 'e' is specified, elite computations applied to key"); + PrintAndLog("Samples:"); + PrintAndLog(" hf iclass readblk b 06 k 0011223344556677"); + PrintAndLog(" hf iclass readblk b 1B k 0011223344556677 c"); + PrintAndLog(" hf iclass readblk b 0A k 0"); return 0; } + +int CmdHFiClass_ReadBlock(const char *Cmd) +{ + uint8_t blockno=0; + uint8_t keyType = 0x88; //debit key + uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t keyNbr = 0; + uint8_t dataLen = 0; + char tempStr[50] = {0}; + bool elite = false; + bool errors = false; + uint8_t cmdp = 0; + while(param_getchar(Cmd, cmdp) != 0x00) + { + switch(param_getchar(Cmd, cmdp)) + { + case 'h': + case 'H': + return usage_hf_iclass_readblock(); + case 'b': + case 'B': + if (param_gethex(Cmd, cmdp+1, &blockno, 2)) { + PrintAndLog("Block No must include 2 HEX symbols\n"); + errors = true; + } + cmdp += 2; + break; + case 'c': + case 'C': + keyType = 0x18; + cmdp++; + break; + case 'e': + case 'E': + elite = true; + cmdp++; + break; + case 'k': + case 'K': + dataLen = param_getstr(Cmd, cmdp+1, tempStr); + if (dataLen == 16) { + errors = param_gethex(tempStr, 0, KEY, dataLen); + } else if (dataLen == 1) { + keyNbr = param_get8(Cmd, cmdp+1); + if (keyNbr <= ICLASS_KEYS_MAX) { + memcpy(KEY, iClass_Key_Table[keyNbr], 8); + } else { + PrintAndLog("\nERROR: Credit KeyNbr is invalid\n"); + errors = true; + } + } else { + PrintAndLog("\nERROR: Credit Key is incorrect length\n"); + errors = true; + } + cmdp += 2; + break; + default: + PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + if(errors) return usage_hf_iclass_readblock(); + } + + if (cmdp < 4) return usage_hf_iclass_readblock(); + + return ReadBlock(KEY, blockno, keyType, elite, true); +} + int CmdHFiClass_loclass(const char *Cmd) { char opt = param_getchar(Cmd, 0); @@ -683,19 +1440,350 @@ int CmdHFiClass_loclass(const char *Cmd) return 0; } +int usage_hf_iclass_readtagfile(){ + PrintAndLog("Usage: hf iclass readtagfile "); + return 1; +} + +void printIclassDumpContents(uint8_t *iclass_dump, uint8_t startblock, uint8_t endblock, size_t filesize){ + uint8_t blockdata[8]; + uint8_t mem_config; + memcpy(&mem_config, iclass_dump + 13,1); + uint8_t maxmemcount; + uint8_t filemaxblock = filesize / 8; + if (mem_config == 0x80) + maxmemcount = 255; + else + maxmemcount = 32; + + if (startblock == 0) + startblock = 6; + if ((endblock > maxmemcount) || (endblock == 0)) + endblock = maxmemcount; + if (endblock > filemaxblock) + endblock = filemaxblock; + int i = startblock; + int j; + while (i < endblock){ + printf("block[%02X]: ",i); + memcpy(blockdata,iclass_dump + (i * 8),8); + for (j = 0;j < 8;j++) + printf("%02X ",blockdata[j]); + printf("\n"); + i++; + } + if ((i < filemaxblock) && (i < maxmemcount)){ + printf("block[%02X]: ",i); + memcpy(blockdata,iclass_dump + (i * 8),8); + for (j = 0;j < 8;j++) + printf("%02X",blockdata[j]); + printf("\n"); + } +} + +int CmdHFiClassReadTagFile(const char *Cmd) +{ + int startblock = 0; + int endblock = 0; + char tempnum[5]; + FILE *f; + char filename[FILE_PATH_SIZE]; + if (param_getstr(Cmd, 0, filename) < 1) + return usage_hf_iclass_readtagfile(); + if (param_getstr(Cmd,1,(char *)&tempnum) < 1) + startblock = 0; + else + sscanf(tempnum,"%d",&startblock); + + if (param_getstr(Cmd,2,(char *)&tempnum) < 1) + endblock = 0; + else + sscanf(tempnum,"%d",&endblock); + // file handling and reading + f = fopen(filename,"rb"); + if(!f) { + PrintAndLog("Failed to read from file '%s'", filename); + return 1; + } + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + uint8_t *dump = malloc(fsize); + + + size_t bytes_read = fread(dump, 1, fsize, f); + fclose(f); + uint8_t *csn = dump; + printf("CSN : %02X %02X %02X %02X %02X %02X %02X %02X\n",csn[0],csn[1],csn[2],csn[3],csn[4],csn[5],csn[6],csn[7]); + // printIclassDumpInfo(dump); + printIclassDumpContents(dump,startblock,endblock,bytes_read); + free(dump); + return 0; +} + +uint64_t xorcheck(uint64_t sdiv,uint64_t hdiv){ + uint64_t new_div = 0x00; + new_div ^= sdiv; + new_div ^= hdiv; + return new_div; +} + +uint64_t hexarray_to_uint64(uint8_t *key){ + char temp[17]; + uint64_t uint_key; + for (int i = 0;i < 8;i++) + sprintf(&temp[(i *2)],"%02X",key[i]); + temp[16] = '\0'; + if (sscanf(temp,"%016"llX,&uint_key) < 1) + return 0; + return uint_key; +} + +int usage_hf_iclass_calc_ekey(){ + PrintAndLog("Usage: hf iclass calc_ekey [c]"); + return 1; +} + +int CmdHFiClassCalcEKey(const char *Cmd){ + uint8_t STDKEY[8]; + uint8_t ELITEKEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t std_div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t elite_div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t keytable[128] = {0}; + uint8_t key_index[8] = {0}; + uint64_t new_div_key; + uint64_t i_elite_div_key; + uint64_t i_std_div_key; + bool use_credit_key = false; + //memcpy(STDKEY,GLOBAL_KEY,sizeof(STDKEY)); + int i; + if (strlen(Cmd) < 2) + return usage_hf_iclass_calc_ekey(); + + if (param_gethex(Cmd, 0, STDKEY, 16)) + return usage_hf_iclass_calc_ekey(); + + if (param_gethex(Cmd, 1, ELITEKEY, 16) != 0) + return usage_hf_iclass_calc_ekey(); + + if (param_getchar(Cmd, 2)=='c') + use_credit_key = true; + + hash2(ELITEKEY, keytable); + + uint8_t key_sel[8] = { 0 }; + uint8_t key_sel_p[8] = { 0 }; + + if (!select_only(CSN, CCNR, use_credit_key, true)) + return 0; + + diversifyKey(CSN, STDKEY, std_div_key); + + // printvar("(S)Div key", std_div_key, 8); + hash1(CSN, key_index); + for(i = 0; i < 8 ; i++) + key_sel[i] = keytable[key_index[i]] & 0xFF; + + //Permute from iclass format to standard format + permutekey_rev(key_sel, key_sel_p); + diversifyKey(CSN, key_sel_p, elite_div_key); + // printvar("(E)Div key", elite_div_key, 8); + i_elite_div_key = hexarray_to_uint64(elite_div_key); + i_std_div_key = hexarray_to_uint64(std_div_key); + new_div_key = xorcheck(i_std_div_key, i_elite_div_key); + printf("New Div Key : %016"llX"\n",new_div_key); + return 0; +} + +int loadKeys(char *filename){ + FILE *f; + f = fopen(filename,"rb"); + if(!f) { + PrintAndLog("Failed to read from file '%s'", filename); + return 0; + } + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + + uint8_t *dump = malloc(fsize); + + size_t bytes_read = fread(dump, 1, fsize, f); + fclose(f); + if (bytes_read > ICLASS_KEYS_MAX * 8){ + PrintAndLog("File is too long to load - bytes: %u", bytes_read); + free(dump); + return 0; + } + uint8_t i = 0; + for (; i < bytes_read/8; i++){ + memcpy(iClass_Key_Table[i],dump+(i*8),8); + } + free(dump); + PrintAndLog("%u keys loaded", i); + return 1; +} + +int saveKeys(char *filename){ + FILE *f; + f = fopen(filename,"wb"); + if (f == NULL) { + printf("error opening file %s\n",filename); + return 0; + } + for (uint8_t i = 0; i < ICLASS_KEYS_MAX; i++){ + if (fwrite(iClass_Key_Table[i],8,1,f) != 1){ + PrintAndLog("save key failed to write to file: %s", filename); + break; + } + } + fclose(f); + return 0; +} + +int printKeys(){ + PrintAndLog(""); + for (uint8_t i = 0; i < ICLASS_KEYS_MAX; i++){ + PrintAndLog("%u: %s",i,sprint_hex(iClass_Key_Table[i],8)); + } + PrintAndLog(""); + return 0; +} + +int usage_hf_iclass_managekeys(){ + PrintAndLog("HELP : Manage iClass Keys in client memory:\n"); + PrintAndLog("Usage: hf iclass managekeys n [keynbr] k [key] f [filename] s l p\n"); + PrintAndLog(" Options:"); + PrintAndLog(" n : specify the keyNbr to set in memory"); + PrintAndLog(" k : set a key in memory"); + PrintAndLog(" f : specify a filename to use with load or save operations"); + PrintAndLog(" s : save keys in memory to file specified by filename"); + PrintAndLog(" l : load keys to memory from file specified by filename"); + PrintAndLog(" p : print keys loaded into memory\n"); + PrintAndLog("Samples:"); + PrintAndLog(" set key : hf iclass managekeys n 0 k 1122334455667788"); + PrintAndLog(" save key file: hf iclass managekeys f mykeys.bin s"); + PrintAndLog(" load key file: hf iclass managekeys f mykeys.bin l"); + PrintAndLog(" print keys : hf iclass managekeys p\n"); + return 0; +} + +int CmdManageKeys(const char *Cmd){ + uint8_t keyNbr = 0; + uint8_t dataLen = 0; + uint8_t KEY[8] = {0}; + char filename[FILE_PATH_SIZE]; + uint8_t fileNameLen = 0; + bool errors = false; + uint8_t operation = 0; + char tempStr[20]; + uint8_t cmdp = 0; + + while(param_getchar(Cmd, cmdp) != 0x00) + { + switch(param_getchar(Cmd, cmdp)) + { + case 'h': + case 'H': + return usage_hf_iclass_managekeys(); + case 'f': + case 'F': + fileNameLen = param_getstr(Cmd, cmdp+1, filename); + if (fileNameLen < 1) { + PrintAndLog("No filename found after f"); + errors = true; + } + cmdp += 2; + break; + case 'n': + case 'N': + keyNbr = param_get8(Cmd, cmdp+1); + if (keyNbr < 0) { + PrintAndLog("Wrong block number"); + errors = true; + } + cmdp += 2; + break; + case 'k': + case 'K': + operation += 3; //set key + dataLen = param_getstr(Cmd, cmdp+1, tempStr); + if (dataLen == 16) { //ul-c or ev1/ntag key length + errors = param_gethex(tempStr, 0, KEY, dataLen); + } else { + PrintAndLog("\nERROR: Key is incorrect length\n"); + errors = true; + } + cmdp += 2; + break; + case 'p': + case 'P': + operation += 4; //print keys in memory + cmdp++; + break; + case 'l': + case 'L': + operation += 5; //load keys from file + cmdp++; + break; + case 's': + case 'S': + operation += 6; //save keys to file + cmdp++; + break; + default: + PrintAndLog("Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + if(errors) return usage_hf_iclass_managekeys(); + } + if (operation == 0){ + PrintAndLog("no operation specified (load, save, or print)\n"); + return usage_hf_iclass_managekeys(); + } + if (operation > 6){ + PrintAndLog("Too many operations specified\n"); + return usage_hf_iclass_managekeys(); + } + if (operation > 4 && fileNameLen == 0){ + PrintAndLog("You must enter a filename when loading or saving\n"); + return usage_hf_iclass_managekeys(); + } + + switch (operation){ + case 3: memcpy(iClass_Key_Table[keyNbr], KEY, 8); return 1; + case 4: return printKeys(); + case 5: return loadKeys(filename); + case 6: return saveKeys(filename); + break; + } + return 0; +} + static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"list", CmdHFiClassList, 0, "[Deprecated] List iClass history"}, - {"snoop", CmdHFiClassSnoop, 0, "Eavesdrop iClass communication"}, - {"sim", CmdHFiClassSim, 0, "Simulate iClass tag"}, - {"reader",CmdHFiClassReader, 0, "Read an iClass tag"}, - {"replay",CmdHFiClassReader_Replay, 0, "Read an iClass tag via Reply Attack"}, - {"dump", CmdHFiClassReader_Dump, 0, "Authenticate and Dump iClass tag"}, -// {"write", CmdHFiClass_iso14443A_write, 0, "Authenticate and Write iClass block"}, - {"loclass", CmdHFiClass_loclass, 1, "Use loclass to perform bruteforce of reader attack dump"}, - {"eload", CmdHFiClassELoad, 0, "[experimental] Load data into iclass emulator memory"}, - {"decrypt", CmdHFiClassDecrypt, 1, "Decrypt tagdump" }, + {"help", CmdHelp, 1, "This help"}, + {"calc_ekey", CmdHFiClassCalcEKey, 0, "Get Elite Diversified key (block 3) to write to convert std to elite"}, + {"clone", CmdHFiClassCloneTag, 0, "Authenticate and Clone from iClass bin file"}, + {"decrypt", CmdHFiClassDecrypt, 1, "Decrypt tagdump" }, + {"dump", CmdHFiClassReader_Dump, 0, "Authenticate and Dump iClass tag's AA1"}, + {"eload", CmdHFiClassELoad, 0, "[experimental] Load data into iClass emulator memory"}, + {"encryptblk", CmdHFiClassEncryptBlk, 1, "[blockData] Encrypt given block data"}, + {"list", CmdHFiClassList, 0, "[Deprecated] List iClass history"}, + //{"load", CmdHFiClass_load, 0, "Load from tagfile to iClass card"}, + {"loclass", CmdHFiClass_loclass, 1, "Use loclass to perform bruteforce of reader attack dump"}, + {"managekeys", CmdManageKeys, 1, "Manage the keys to use with iClass"}, + {"readblk", CmdHFiClass_ReadBlock, 0, "Authenticate and Read iClass block"}, + {"reader", CmdHFiClassReader, 0, "Read an iClass tag"}, + {"readtagfile", CmdHFiClassReadTagFile, 1, "Display Content from tagfile"}, + {"replay", CmdHFiClassReader_Replay, 0, "Read an iClass tag via Reply Attack"}, + {"sim", CmdHFiClassSim, 0, "Simulate iClass tag"}, + {"snoop", CmdHFiClassSnoop, 0, "Eavesdrop iClass communication"}, + {"writeblk", CmdHFiClass_WriteBlock, 0, "Authenticate and Write iClass block"}, {NULL, NULL, 0, NULL} }; diff --git a/client/cmdhficlass.h b/client/cmdhficlass.h index 30c6a8a7..88e61434 100644 --- a/client/cmdhficlass.h +++ b/client/cmdhficlass.h @@ -20,5 +20,11 @@ int CmdHFiClassList(const char *Cmd); int HFiClassReader(const char *Cmd, bool loop, bool verbose); int CmdHFiClassReader(const char *Cmd); int CmdHFiClassReader_Replay(const char *Cmd); +int CmdHFiClassReadKeyFile(const char *filename); +int CmdHFiClassWriteKeyFile(const char *Cmd); +int CmdHFiClass_ReadBlock(const char *Cmd); +int CmdHFiClass_WriteBlock(const char *Cmd); +int CmdHFiClassCalcEKey(const char *Cmd); +int CmdHFiClass_TestMac(const char *Cmd); #endif diff --git a/client/cmdlf.c b/client/cmdlf.c index 1acee39b..21b19b09 100644 --- a/client/cmdlf.c +++ b/client/cmdlf.c @@ -1136,7 +1136,7 @@ static command_t CommandTable[] = {"read", CmdLFRead, 0, "['s' silent] Read 125/134 kHz LF ID-only tag. Do 'lf read h' for help"}, {"search", CmdLFfind, 1, "[offline] ['u'] Read and Search for valid known tag (in offline mode it you can load first then search) - 'u' to search for unknown tags"}, {"sim", CmdLFSim, 0, "[GAP] -- Simulate LF tag from buffer with optional GAP (in microseconds)"}, - {"simask", CmdLFaskSim, 0, "[clock] [invert <1|0>] [manchester/raw <'m'|'r'>] [msg separator 's'] [d ] -- Simulate LF ASK tag from demodbuffer or input"}, + {"simask", CmdLFaskSim, 0, "[clock] [invert <1|0>] [biphase/manchester/raw <'b'|'m'|'r'>] [msg separator 's'] [d ] -- Simulate LF ASK tag from demodbuffer or input"}, {"simfsk", CmdLFfskSim, 0, "[c ] [i] [H ] [L ] [d ] -- Simulate LF FSK tag from demodbuffer or input"}, {"simpsk", CmdLFpskSim, 0, "[1|2|3] [c ] [i] [r ] [d ] -- Simulate LF PSK tag from demodbuffer or input"}, {"simbidir", CmdLFSimBidir, 0, "Simulate LF tag (with bidirectional data transmission between reader and tag)"}, diff --git a/client/hid-flasher/usb_cmd.h b/client/hid-flasher/usb_cmd.h index b3a7f4ec..cc415352 100644 --- a/client/hid-flasher/usb_cmd.h +++ b/client/hid-flasher/usb_cmd.h @@ -114,9 +114,16 @@ typedef struct { #define CMD_WRITER_LEGIC_RF 0x0389 #define CMD_EPA_PACE_COLLECT_NONCE 0x038A +#define CMD_ICLASS_CLONE 0x0390 +#define CMD_ICLASS_DUMP 0x0391 #define CMD_SNOOP_ICLASS 0x0392 #define CMD_SIMULATE_TAG_ICLASS 0x0393 #define CMD_READER_ICLASS 0x0394 +#define CMD_READER_ICLASS_REPLAY 0x0395 +#define CMD_ICLASS_READBLOCK 0x0396 +#define CMD_ICLASS_WRITEBLOCK 0x0397 +#define CMD_ICLASS_EML_MEMSET 0x0398 +#define CMD_ICLASS_AUTHENTICATION 0x0399 // For measurements of the antenna tuning #define CMD_MEASURE_ANTENNA_TUNING 0x0400 diff --git a/client/loclass/cipher.c b/client/loclass/cipher.c index 7c9cc873..2aae093d 100644 --- a/client/loclass/cipher.c +++ b/client/loclass/cipher.c @@ -241,6 +241,27 @@ void doMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) //free(cc_nr); return; } +void doMAC_N(uint8_t *cc_nr_p,uint8_t cc_nr_size, uint8_t *div_key_p, uint8_t mac[4]) +{ + uint8_t *cc_nr; + uint8_t div_key[8]; + cc_nr = (uint8_t*) malloc(cc_nr_size); + + memcpy(cc_nr,cc_nr_p,cc_nr_size); + memcpy(div_key,div_key_p,8); + + reverse_arraybytes(cc_nr,cc_nr_size); + BitstreamIn bitstream = {cc_nr,cc_nr_size * 8,0}; + uint8_t dest []= {0,0,0,0,0,0,0,0}; + BitstreamOut out = { dest, sizeof(dest)*8, 0 }; + MAC(div_key,bitstream, out); + //The output MAC must also be reversed + reverse_arraybytes(dest, sizeof(dest)); + memcpy(mac, dest, 4); + free(cc_nr); + return; +} + #ifndef ON_DEVICE int testMAC() { diff --git a/client/loclass/cipher.h b/client/loclass/cipher.h index bdea9432..24e86851 100644 --- a/client/loclass/cipher.h +++ b/client/loclass/cipher.h @@ -42,6 +42,8 @@ #include void doMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]); +void doMAC_N(uint8_t *cc_nr_p,uint8_t cc_nr_size, uint8_t *div_key_p, uint8_t mac[4]); + #ifndef ON_DEVICE int testMAC(); #endif diff --git a/client/lualibs/commands.lua b/client/lualibs/commands.lua index 4c7bc638..97f0b70a 100644 --- a/client/lualibs/commands.lua +++ b/client/lualibs/commands.lua @@ -86,11 +86,16 @@ local _commands = { CMD_EPA_PACE_COLLECT_NONCE = 0x038A, --//CMD_EPA_ = 0x038B, + CMD_ICLASS_CLONE = 0x0390, + CMD_ICLASS_DUMP = 0x0391, CMD_SNOOP_ICLASS = 0x0392, CMD_SIMULATE_TAG_ICLASS = 0x0393, CMD_READER_ICLASS = 0x0394, - CMD_READER_ICLASS_REPLAY = 0x0395, - CMD_ICLASS_ISO14443A_WRITE = 0x0397, + CMD_READER_ICLASS_REPLAY = 0x0395, + CMD_ICLASS_READBLOCK = 0x0396, + CMD_ICLASS_WRITEBLOCK = 0x0397, + CMD_ICLASS_EML_MEMSET = 0x0398, + CMD_ICLASS_AUTHENTICATION = 0x0399, --// For measurements of the antenna tuning CMD_MEASURE_ANTENNA_TUNING = 0x0400, diff --git a/common/protocols.c b/common/protocols.c index aa80491b..56a6924f 100644 --- a/common/protocols.c +++ b/common/protocols.c @@ -74,24 +74,30 @@ void fuse_config(const picopass_hdr *hdr) if( isset( fuses, FUSE_RA)) prnt(" RA: Read access enabled"); else prnt(" RA: Read access not enabled"); } -void mem_config(const picopass_hdr *hdr) +void mem_app_config(const picopass_hdr *hdr) { uint8_t mem = hdr->conf.mem_config; - if( isset (mem, 0x80)) prnt(" Mem: 16KBits (255 * 8 bytes)"); - else prnt(" Mem: 2 KBits ( 32 * 8 bytes)"); - -} -void applimit_config(const picopass_hdr *hdr) -{ uint8_t applimit = hdr->conf.app_limit; - prnt(" AA1: blocks 6-%d", applimit); - prnt(" AA2: blocks %d-", (applimit+1)); + if (applimit < 6) applimit = 26; + uint8_t kb=2; + uint8_t maxBlk = 32; + if( isset(mem, 0x10) && notset(mem, 0x80)){ + // 2kb default + } else if( isset(mem, 0x80) && notset(mem, 0x10)){ + kb = 16; + maxBlk = 255; //16kb + } else { + kb = 32; + maxBlk = 255; + } + prnt(" Mem: %u KBits ( %u * 8 bytes) [%02X]", kb, maxBlk, mem); + prnt(" AA1: blocks 06-%02X", applimit); + prnt(" AA2: blocks %02X-%02X", (applimit+1), (hdr->conf.mem_config)); } void print_picopass_info(const picopass_hdr *hdr) { fuse_config(hdr); - mem_config(hdr); - applimit_config(hdr); + mem_app_config(hdr); } void printIclassDumpInfo(uint8_t* iclass_dump) { diff --git a/include/usb_cmd.h b/include/usb_cmd.h index 524554e9..2618476a 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -128,12 +128,16 @@ typedef struct{ #define CMD_EPA_PACE_COLLECT_NONCE 0x038A #define CMD_EPA_PACE_REPLAY 0x038B +#define CMD_ICLASS_CLONE 0x0390 +#define CMD_ICLASS_DUMP 0x0391 #define CMD_SNOOP_ICLASS 0x0392 #define CMD_SIMULATE_TAG_ICLASS 0x0393 #define CMD_READER_ICLASS 0x0394 #define CMD_READER_ICLASS_REPLAY 0x0395 -#define CMD_ICLASS_ISO14443A_WRITE 0x0397 +#define CMD_ICLASS_READBLOCK 0x0396 +#define CMD_ICLASS_WRITEBLOCK 0x0397 #define CMD_ICLASS_EML_MEMSET 0x0398 +#define CMD_ICLASS_AUTHENTICATION 0x0399 // For measurements of the antenna tuning #define CMD_MEASURE_ANTENNA_TUNING 0x0400 @@ -204,6 +208,7 @@ typedef struct{ #define FLAG_ICLASS_READER_CONF 0x08 #define FLAG_ICLASS_READER_AA 0x10 #define FLAG_ICLASS_READER_ONE_TRY 0x20 +#define FLAG_ICLASS_READER_CEDITKEY 0x40 From 4d68ec02b281add4d6a7f6cbf5406a691f0b5f5d Mon Sep 17 00:00:00 2001 From: marshmellow42 Date: Tue, 21 Jul 2015 14:26:46 -0400 Subject: [PATCH 10/47] iclass refactor/cleanup --- armsrc/iclass.c | 9 - client/cmdhficlass.c | 754 +++++++++++++++++-------------------------- client/cmdhficlass.h | 21 +- 3 files changed, 319 insertions(+), 465 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index a27fb970..9a70c06b 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1918,7 +1918,6 @@ void iClass_Authentication(uint8_t *MAC) { bool isOK; isOK = sendCmdGetResponseWithRetries(check, sizeof(check),resp, 4, 5); cmd_send(CMD_ACK,isOK,0,0,0,0); - //Dbprintf("isOK %d, Tag response : %02x%02x%02x%02x",isOK,resp[0],resp[1],resp[2],resp[3]); } bool iClass_ReadBlock(uint8_t blockNo, uint8_t keyType, uint8_t *readdata) { uint8_t readcmd[] = {keyType, blockNo}; //0x88, 0x00 @@ -1936,7 +1935,6 @@ void iClass_ReadBlk(uint8_t blockno, uint8_t keyType) { uint8_t readblockdata[8]; bool isOK = false; isOK = iClass_ReadBlock(blockno, keyType, readblockdata); - //Dbprintf("read block [%02x] [%02x%02x%02x%02x%02x%02x%02x%02x]",blockNo,readblockdata[0],readblockdata[1],readblockdata[2],readblockdata[3],readblockdata[4],readblockdata[5],readblockdata[6],readblockdata[7]); cmd_send(CMD_ACK,isOK,0,0,readblockdata,8); } @@ -1964,11 +1962,6 @@ void iClass_Dump(uint8_t blockno, uint8_t numblks, uint8_t keyType) { } } memcpy(dataout+(blkCnt*8),readblockdata,8); - /*Dbprintf("| %02x | %02x%02x%02x%02x%02x%02x%02x%02x |", - blockno+blkCnt, readblockdata[0], readblockdata[1], readblockdata[2], - readblockdata[3], readblockdata[4], readblockdata[5], - readblockdata[6], readblockdata[7]); - */ } //return pointer to dump memory in arg3 cmd_send(CMD_ACK,isOK,blkCnt,BigBuf_max_traceLen(),0,0); @@ -1985,7 +1978,6 @@ bool iClass_WriteBlock_ext(uint8_t blockNo, uint8_t keyType, uint8_t *data) { uint8_t resp[10]; bool isOK; isOK = sendCmdGetResponseWithRetries(write,sizeof(write),resp,sizeof(resp),5); - //Dbprintf("reply [%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x]",resp[0],resp[1],resp[2],resp[3],resp[4],resp[5],resp[6],resp[7],resp[8],resp[9]); if (isOK) { isOK = iClass_ReadBlock(blockNo, keyType, readblockdata); //try again @@ -1993,7 +1985,6 @@ bool iClass_WriteBlock_ext(uint8_t blockNo, uint8_t keyType, uint8_t *data) { isOK = iClass_ReadBlock(blockNo, keyType, readblockdata); } if (isOK) { - //Dbprintf("read block [%02x] [%02x%02x%02x%02x%02x%02x%02x%02x]",blockNo,readblockdata[0],readblockdata[1],readblockdata[2],readblockdata[3],readblockdata[4],readblockdata[5],readblockdata[6],readblockdata[7]); if (memcmp(write+2,readblockdata,sizeof(readblockdata)) != 0){ isOK=false; } diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index db3de205..605793a5 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -53,8 +53,7 @@ typedef struct iclass_block { uint8_t d[8]; } iclass_block_t; -int xorbits_8(uint8_t val) -{ +int xorbits_8(uint8_t val) { uint8_t res = val ^ (val >> 1); //1st pass res = res ^ (res >> 1); // 2nd pass res = res ^ (res >> 2); // 3rd pass @@ -62,20 +61,18 @@ int xorbits_8(uint8_t val) return res & 1; } -int CmdHFiClassList(const char *Cmd) -{ +int CmdHFiClassList(const char *Cmd) { PrintAndLog("Deprecated command, use 'hf list iclass' instead"); return 0; } -int CmdHFiClassSnoop(const char *Cmd) -{ +int CmdHFiClassSnoop(const char *Cmd) { UsbCommand c = {CMD_SNOOP_ICLASS}; SendCommand(&c); return 0; } -int usage_hf_iclass_sim() -{ + +int usage_hf_iclass_sim(void) { PrintAndLog("Usage: hf iclass sim