From: Martin Holst Swende Date: Tue, 13 Oct 2015 07:39:04 +0000 (+0200) Subject: Merge pull request #140 from marshmellow42/iclass X-Git-Tag: v2.3.0~22 X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/commitdiff_plain/be6250d31b5cc1ebb9ab0bece84c6691220a8e0d?hp=8c6b22980cac809f51cdb307a2043b380659fe9c Merge pull request #140 from marshmellow42/iclass iClass major updates --- diff --git a/CHANGELOG.md b/CHANGELOG.md index e2196a39..a489c981 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac ## [unreleased][unreleased] ### Added +- `hf iclass managekeys` to save, load and manage iclass keys. (adjusted most commands to accept a loaded key in memory) (marshmellow) +- `hf iclass readblk` to select, authenticate, and read 1 block from an iclass card (marshmellow) +- `hf iclass writeblk` to select, authenticate, and write 1 block to an iclass card (or picopass) (marshmellow + others) +- `hf iclass clone` to take a saved dump file and clone selected blocks to a new tag (marshmellow + others) +- `hf iclass calcnewkey` - to calculate the div_key change to change a key - (experimental) (marshmellow + others) +- `hf iclass encryptblk` - to encrypt a data block hex to prep for writing that block (marshmellow) - ISO14443a stand-alone operation with ARM CFLAG="WITH_ISO14443a_StandAlone". This code can read & emulate two banks of 14a tag UIDs and write to "magic" cards (Craig Young) - AWID26 command context added as 'lf awid' containing realtime demodulation as well as cloning/simulation based on tag numbers (Craig Young) - Added 'hw status'. This command makes the ARM print out some runtime information. (holiman) @@ -12,6 +18,8 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac - Added `data hex2bin` and `data bin2hex` for command line conversion between binary and hexadecimal (holiman) ### Changed +- changed `lf config t ` to be 0 - 128 and will trigger on + or - threshold value (marshmellow) +- `hf iclass dump` cli options - can now dump AA1 and AA2 with different keys in one run (does not go to muliple pages for the larger tags yet) - Revised workflow for StandAloneMode14a (Craig Young) - EPA functions (`hf epa`) now support both ISO 14443-A and 14443-B cards (frederikmoellers) - 'hw version' only talks to ARM at startup, after that the info is cached. (pwpiwi) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 321782da..48c9caef 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -1178,11 +1178,29 @@ 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->d.asBytes); + break; + case CMD_ICLASS_READCHECK: // auth step 1 + iClass_ReadCheck(c->arg[0], c->arg[1]); + break; + case CMD_ICLASS_READBLOCK: + iClass_ReadBlk(c->arg[0]); + break; + case CMD_ICLASS_AUTHENTICATION: //check + iClass_Authentication(c->d.asBytes); + break; + case CMD_ICLASS_DUMP: + iClass_Dump(c->arg[0], c->arg[1]); + break; + case CMD_ICLASS_CLONE: + iClass_Clone(c->arg[0], c->arg[1], c->d.asBytes); + break; #endif case CMD_BUFF_CLEAR: diff --git a/armsrc/apps.h b/armsrc/apps.h index d5c8ba0a..79c9da86 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -156,9 +156,6 @@ void OnSuccess(); void OnError(uint8_t reason); - - - /// iso15693.h void RecordRawAdcSamplesIso15693(void); void AcquireRawAdcSamplesIso15693(void); @@ -174,6 +171,13 @@ 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 *data); +void iClass_ReadBlk(uint8_t blockNo); +bool iClass_ReadBlock(uint8_t blockNo, uint8_t *readdata); +void iClass_Dump(uint8_t blockno, uint8_t numblks); +void iClass_Clone(uint8_t startblock, uint8_t endblock, uint8_t *data); +void iClass_ReadCheck(uint8_t blockNo, uint8_t keyType); // hitag2.h void SnoopHitag(uint32_t type); diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 97c62bb6..4e4854ca 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"); } } @@ -1762,8 +1769,8 @@ void ReaderIClass(uint8_t arg0) { } LED_B_OFF(); } - cmd_send(CMD_ACK,0,0,0,card_data, 0); - LED_A_OFF(); + cmd_send(CMD_ACK,0,0,0,card_data, 0); + LED_A_OFF(); } void ReaderIClass_Replay(uint8_t arg0, uint8_t *MAC) { @@ -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,129 @@ 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); - - // Reset trace buffer - memset(trace, 0x44, RECV_CMD_OFFSET); - traceLen = 0; +void iClass_ReadCheck(uint8_t blockNo, uint8_t keyType) { + uint8_t readcheck[] = { keyType, blockNo }; + uint8_t resp[] = {0,0,0,0,0,0,0,0}; + size_t isOK = 0; + isOK = sendCmdGetResponseWithRetries(readcheck, sizeof(readcheck), resp, sizeof(resp), 6); + cmd_send(CMD_ACK,isOK,0,0,0,0); +} - // 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); +void iClass_Authentication(uint8_t *MAC) { + uint8_t check[] = { ICLASS_CMD_CHECK, 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, 6); + cmd_send(CMD_ACK,isOK,0,0,0,0); +} +bool iClass_ReadBlock(uint8_t blockNo, uint8_t *readdata) { + uint8_t readcmd[] = {ICLASS_CMD_READ_OR_IDENTIFY, blockNo, 0x00, 0x00}; //0x88, 0x00 // can i use 0C? + char bl = blockNo; + uint16_t rdCrc = iclass_crc16(&bl, 1); + readcmd[2] = rdCrc >> 8; + readcmd[3] = rdCrc & 0xff; + uint8_t resp[] = {0,0,0,0,0,0,0,0,0,0}; + bool isOK = false; + + //readcmd[1] = blockNo; + isOK = sendCmdGetResponseWithRetries(readcmd, sizeof(readcmd), resp, 10, 10); + memcpy(readdata, resp, sizeof(resp)); + + return isOK; +} - SetAdcMuxFor(GPIO_MUXSEL_HIPKD); +void iClass_ReadBlk(uint8_t blockno) { + uint8_t readblockdata[] = {0,0,0,0,0,0,0,0,0,0}; + bool isOK = false; + isOK = iClass_ReadBlock(blockno, readblockdata); + 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 readblockdata[] = {0,0,0,0,0,0,0,0,0,0}; + bool isOK = false; + uint8_t blkCnt = 0; - LED_A_ON(); + BigBuf_free(); + uint8_t *dataout = BigBuf_malloc(255*8); + if (dataout == NULL){ + Dbprintf("out of memory"); + OnError(1); + return; + } + memset(dataout,0xFF,255*8); + + for (;blkCnt < numblks; blkCnt++) { + isOK = iClass_ReadBlock(blockno+blkCnt, readblockdata); + if (!isOK || (readblockdata[0] == 0xBB || readblockdata[7] == 0xBB || readblockdata[2] == 0xBB)) { //try again + isOK = iClass_ReadBlock(blockno+blkCnt, readblockdata); + if (!isOK) { + Dbprintf("Block %02X failed to read", blkCnt+blockno); + break; + } + } + memcpy(dataout+(blkCnt*8),readblockdata,8); + } + //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(); +} - for(int i=0;i<1;i++) { - - if(traceLen > TRACE_SIZE) { - DbpString("Trace full"); - break; +bool iClass_WriteBlock_ext(uint8_t blockNo, uint8_t *data) { + uint8_t write[] = { ICLASS_CMD_UPDATE, blockNo, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + //uint8_t readblockdata[10]; + //write[1] = blockNo; + memcpy(write+2, data, 12); // data + mac + uint8_t resp[] = {0,0,0,0,0,0,0,0,0,0}; + bool isOK; + isOK = sendCmdGetResponseWithRetries(write,sizeof(write),resp,sizeof(resp),10); + if (isOK) { + //Dbprintf("WriteResp: %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 (memcmp(write+2,resp,8)) { + //error try again + isOK = sendCmdGetResponseWithRetries(write,sizeof(write),resp,sizeof(resp),10); } - - 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; - } - }// + } + return isOK; +} + +void iClass_WriteBlock(uint8_t blockNo, uint8_t *data) { + bool isOK = iClass_WriteBlock_ext(blockNo, 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 *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, data+(i*12))){ + Dbprintf("Write block [%02x] successful",i + startblock); + written++; + } else { + if (iClass_WriteBlock_ext(i+startblock, data+(i*12))){ + Dbprintf("Write block [%02x] successful",i + startblock); + written++; + } else { + Dbprintf("Write block [%02x] failed",i + startblock); + } } - WDT_HIT(); } - - LED_A_OFF(); -}*/ + 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,14 +557,14 @@ int CmdHFSearch(const char *Cmd){ PrintAndLog("\nValid ISO14443A Tag Found - Quiting Search\n"); return ans; } - ans = HF14BInfo(false); + ans = HFiClassReader("", false, false); if (ans) { - PrintAndLog("\nValid ISO14443B Tag Found - Quiting Search\n"); + PrintAndLog("\nValid iClass Tag (or PicoPass Tag) Found - Quiting Search\n"); return ans; } - ans = HFiClassReader("", false, false); + ans = HF14BInfo(false); if (ans) { - PrintAndLog("\nValid iClass Tag (or PicoPass Tag) Found - Quiting Search\n"); + PrintAndLog("\nValid ISO14443B Tag Found - Quiting Search\n"); return ans; } ans = HF15Reader("", false); diff --git a/client/cmdhficlass.c b/client/cmdhficlass.c index 824aaa36..aca8ff50 100644 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@ -31,11 +31,29 @@ #include "loclass/fileutils.h" #include "protocols.h" #include "usb_cmd.h" +#include "cmdhfmfu.h" + +#define llX PRIx64 static int CmdHelp(const char *Cmd); -int xorbits_8(uint8_t val) -{ +#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 res = res ^ (res >> 1); // 2nd pass res = res ^ (res >> 2); // 3rd pass @@ -43,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